Merge pull request #30578 from bluca/polkit-varlink

varlink: add glue to allow authenticating varlink connections via polkit
This commit is contained in:
Lennart Poettering
2024-01-04 15:15:45 +01:00
committed by GitHub
33 changed files with 839 additions and 68 deletions
+268 -27
View File
@@ -4,10 +4,11 @@
#include "bus-message.h"
#include "bus-polkit.h"
#include "bus-util.h"
#include "process-util.h"
#include "strv.h"
#include "user-util.h"
static int check_good_user(sd_bus_message *m, uid_t good_user) {
static int bus_message_check_good_user(sd_bus_message *m, uid_t good_user) {
_cleanup_(sd_bus_creds_unrefp) sd_bus_creds *creds = NULL;
uid_t sender_uid;
int r;
@@ -15,7 +16,7 @@ static int check_good_user(sd_bus_message *m, uid_t good_user) {
assert(m);
if (good_user == UID_INVALID)
return 0;
return false;
r = sd_bus_query_sender_creds(m, SD_BUS_CREDS_EUID, &creds);
if (r < 0)
@@ -54,7 +55,7 @@ static int bus_message_append_strv_key_value(sd_bus_message *m, const char **l)
return r;
}
static int bus_message_new_polkit_auth_call(
static int bus_message_new_polkit_auth_call_for_bus(
sd_bus_message *m,
const char *action,
const char **details,
@@ -115,7 +116,7 @@ int bus_test_polkit(
/* Tests non-interactively! */
r = check_good_user(call, good_user);
r = bus_message_check_good_user(call, good_user);
if (r != 0)
return r;
@@ -129,7 +130,7 @@ int bus_test_polkit(
_cleanup_(sd_bus_message_unrefp) sd_bus_message *request = NULL, *reply = NULL;
int authorized = false, challenge = false;
r = bus_message_new_polkit_auth_call(call, action, details, /* interactive = */ false, &request);
r = bus_message_new_polkit_auth_call_for_bus(call, action, details, /* interactive = */ false, &request);
if (r < 0)
return r;
@@ -190,8 +191,10 @@ typedef struct AsyncPolkitQuery {
AsyncPolkitQueryAction *action;
sd_bus *bus;
sd_bus_message *request;
sd_bus_slot *slot;
Varlink *link;
Hashmap *registry;
sd_event_source *defer_event_source;
@@ -213,6 +216,9 @@ static AsyncPolkitQuery *async_polkit_query_free(AsyncPolkitQuery *q) {
sd_bus_message_unref(q->request);
sd_bus_unref(q->bus);
varlink_unref(q->link);
async_polkit_query_action_free(q->action);
sd_event_source_disable_unref(q->defer_event_source);
@@ -230,6 +236,14 @@ static AsyncPolkitQuery *async_polkit_query_free(AsyncPolkitQuery *q) {
DEFINE_PRIVATE_TRIVIAL_REF_UNREF_FUNC(AsyncPolkitQuery, async_polkit_query, async_polkit_query_free);
DEFINE_TRIVIAL_CLEANUP_FUNC(AsyncPolkitQuery*, async_polkit_query_unref);
DEFINE_HASH_OPS_WITH_VALUE_DESTRUCTOR(
async_polkit_query_hash_ops,
void,
trivial_hash_func,
trivial_compare_func,
AsyncPolkitQuery,
async_polkit_query_unref);
static int async_polkit_defer(sd_event_source *s, void *userdata) {
AsyncPolkitQuery *q = ASSERT_PTR(userdata);
@@ -316,7 +330,7 @@ static int async_polkit_process_reply(sd_bus_message *reply, AsyncPolkitQuery *q
if (!q->defer_event_source) {
r = sd_event_add_defer(
sd_bus_get_event(sd_bus_message_get_bus(reply)),
sd_bus_get_event(q->bus),
&q->defer_event_source,
async_polkit_defer,
q);
@@ -332,13 +346,21 @@ static int async_polkit_process_reply(sd_bus_message *reply, AsyncPolkitQuery *q
if (r < 0)
return r;
r = sd_bus_message_rewind(q->request, true);
if (r < 0)
return r;
if (q->request) {
r = sd_bus_message_rewind(q->request, true);
if (r < 0)
return r;
r = sd_bus_enqueue_for_read(sd_bus_message_get_bus(q->request), q->request);
if (r < 0)
return r;
r = sd_bus_enqueue_for_read(q->bus, q->request);
if (r < 0)
return r;
}
if (q->link) {
r = varlink_dispatch_again(q->link);
if (r < 0)
return r;
}
return 1;
}
@@ -352,7 +374,10 @@ static int async_polkit_callback(sd_bus_message *reply, void *userdata, sd_bus_e
r = async_polkit_process_reply(reply, q);
if (r < 0) {
log_debug_errno(r, "Processing asynchronous PolicyKit reply failed, ignoring: %m");
(void) sd_bus_reply_method_errno(q->request, r, NULL);
if (q->request)
(void) sd_bus_reply_method_errno(q->request, r, NULL);
if (q->link)
varlink_error_errno(q->link, r);
async_polkit_query_unref(q);
}
return r;
@@ -366,11 +391,10 @@ static int async_polkit_query_check_action(
assert(q);
assert(action);
assert(ret_error);
LIST_FOREACH(authorized, a, q->authorized_actions)
if (streq(a->action, action) && strv_equal(a->details, (char**) details))
return 1;
return 1; /* Allow! */
if (q->error_action && streq(q->error_action->action, action))
return sd_bus_error_copy(ret_error, &q->error);
@@ -480,7 +504,7 @@ int bus_verify_polkit_async_full(
assert(registry);
assert(ret_error);
r = check_good_user(call, good_user);
r = bus_message_check_good_user(call, good_user);
if (r != 0)
return r;
@@ -512,11 +536,7 @@ int bus_verify_polkit_async_full(
if (c > 0)
interactive = true;
r = hashmap_ensure_allocated(registry, NULL);
if (r < 0)
return r;
r = bus_message_new_polkit_auth_call(call, action, details, interactive, &pk);
r = bus_message_new_polkit_auth_call_for_bus(call, action, details, interactive, &pk);
if (r < 0)
return r;
@@ -528,6 +548,7 @@ int bus_verify_polkit_async_full(
*q = (AsyncPolkitQuery) {
.n_ref = 1,
.request = sd_bus_message_ref(call),
.bus = sd_bus_ref(sd_bus_message_get_bus(call)),
};
}
@@ -544,7 +565,7 @@ int bus_verify_polkit_async_full(
return -ENOMEM;
if (!q->registry) {
r = hashmap_put(*registry, call, q);
r = hashmap_ensure_put(registry, &async_polkit_query_hash_ops, call, q);
if (r < 0)
return r;
@@ -563,11 +584,231 @@ int bus_verify_polkit_async_full(
return -EACCES;
}
Hashmap *bus_verify_polkit_async_registry_free(Hashmap *registry) {
static int varlink_check_good_user(Varlink *link, uid_t good_user) {
int r;
assert(link);
if (good_user == UID_INVALID)
return false;
uid_t peer_uid;
r = varlink_get_peer_uid(link, &peer_uid);
if (r < 0)
return r;
return good_user == peer_uid;
}
static int varlink_check_peer_privilege(Varlink *link) {
int r;
assert(link);
uid_t peer_uid;
r = varlink_get_peer_uid(link, &peer_uid);
if (r < 0)
return r;
uid_t our_uid = getuid();
return peer_uid == our_uid ||
(our_uid != 0 && peer_uid == 0);
}
#if ENABLE_POLKIT
return hashmap_free_with_destructor(registry, async_polkit_query_unref);
#else
assert(hashmap_isempty(registry));
return hashmap_free(registry);
static int bus_message_new_polkit_auth_call_for_varlink(
sd_bus *bus,
Varlink *link,
const char *action,
const char **details,
bool interactive,
sd_bus_message **ret) {
_cleanup_(sd_bus_message_unrefp) sd_bus_message *c = NULL;
_cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
int r;
assert(bus);
assert(link);
assert(action);
assert(ret);
r = varlink_get_peer_pidref(link, &pidref);
if (r < 0)
return r;
if (r == 0) /* if we couldn't get a pidfd this returns == 0 */
return log_debug_errno(SYNTHETIC_ERRNO(EPERM), "Failed to get peer pidfd, cannot securely authenticate.");
uid_t uid;
r = varlink_get_peer_uid(link, &uid);
if (r < 0)
return r;
r = sd_bus_message_new_method_call(
bus,
&c,
"org.freedesktop.PolicyKit1",
"/org/freedesktop/PolicyKit1/Authority",
"org.freedesktop.PolicyKit1.Authority",
"CheckAuthorization");
if (r < 0)
return r;
r = sd_bus_message_append(
c,
"(sa{sv})s",
"unix-process", 2,
"pidfd", "h", (uint32_t) pidref.fd,
"uid", "i", (int32_t) uid,
action);
if (r < 0)
return r;
r = bus_message_append_strv_key_value(c, details);
if (r < 0)
return r;
r = sd_bus_message_append(c, "us", interactive, NULL);
if (r < 0)
return r;
*ret = TAKE_PTR(c);
return 0;
}
static bool varlink_allow_interactive_authentication(Varlink *link) {
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
int r;
assert(link);
/* We look for the allowInteractiveAuthentication field in the message currently being dispatched,
* always under the same name. */
r = varlink_get_current_parameters(link, &v);
if (r < 0)
return r;
JsonVariant *b;
b = json_variant_by_key(v, "allowInteractiveAuthentication");
if (b) {
if (json_variant_is_boolean(b))
return json_variant_boolean(b);
log_debug("Incoming 'allowInteractiveAuthentication' field is not a boolean, ignoring.");
}
return false;
}
#endif
int varlink_verify_polkit_async(
Varlink *link,
sd_bus *bus,
const char *action,
const char **details,
uid_t good_user,
Hashmap **registry) {
int r;
assert(link);
assert(registry);
/* This is the same as bus_verify_polkit_async_full(), but authenticates the peer of a varlink
* connection rather than the sender of a bus message. */
r = varlink_check_good_user(link, good_user);
if (r != 0)
return r;
r = varlink_check_peer_privilege(link);
if (r != 0)
return r;
#if ENABLE_POLKIT
_cleanup_(async_polkit_query_unrefp) AsyncPolkitQuery *q = NULL;
q = async_polkit_query_ref(hashmap_get(*registry, link));
/* This is a repeated invocation of this function, hence let's check if we've already got
* a response from polkit for this action */
if (q) {
_cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
r = async_polkit_query_check_action(q, action, details, &error);
if (r < 0) {
/* Reply with a nice error */
if (sd_bus_error_has_name(&error, SD_BUS_ERROR_INTERACTIVE_AUTHORIZATION_REQUIRED))
return varlink_error(link, VARLINK_ERROR_INTERACTIVE_AUTHENTICATION_REQUIRED, NULL);
if (ERRNO_IS_NEG_PRIVILEGE(r))
return varlink_error(link, VARLINK_ERROR_PERMISSION_DENIED, NULL);
return r;
}
if (r > 0)
return r;
}
_cleanup_(sd_bus_unrefp) sd_bus *mybus = NULL;
if (!bus) {
r = sd_bus_open_system(&mybus);
if (r < 0)
return r;
r = sd_bus_attach_event(mybus, varlink_get_event(link), 0);
if (r < 0)
return r;
bus = mybus;
}
bool interactive = varlink_allow_interactive_authentication(link);
_cleanup_(sd_bus_message_unrefp) sd_bus_message *pk = NULL;
r = bus_message_new_polkit_auth_call_for_varlink(bus, link, action, details, interactive, &pk);
if (r < 0)
return r;
if (!q) {
q = new(AsyncPolkitQuery, 1);
if (!q)
return -ENOMEM;
*q = (AsyncPolkitQuery) {
.n_ref = 1,
.link = varlink_ref(link),
.bus = sd_bus_ref(bus),
};
}
assert(!q->action);
q->action = new(AsyncPolkitQueryAction, 1);
if (!q->action)
return -ENOMEM;
*q->action = (AsyncPolkitQueryAction) {
.action = strdup(action),
.details = strv_copy((char**) details),
};
if (!q->action->action || !q->action->details)
return -ENOMEM;
if (!q->registry) {
r = hashmap_ensure_put(registry, &async_polkit_query_hash_ops, link, q);
if (r < 0)
return r;
q->registry = *registry;
}
r = sd_bus_call_async(bus, &q->slot, pk, async_polkit_callback, q, 0);
if (r < 0)
return r;
TAKE_PTR(q);
return 0;
#endif
return -EACCES;
}
+10 -1
View File
@@ -5,6 +5,7 @@
#include "hashmap.h"
#include "user-util.h"
#include "varlink.h"
int bus_test_polkit(sd_bus_message *call, const char *action, const char **details, uid_t good_user, bool *_challenge, sd_bus_error *e);
@@ -13,4 +14,12 @@ static inline int bus_verify_polkit_async(sd_bus_message *call, const char *acti
return bus_verify_polkit_async_full(call, action, details, false, UID_INVALID, registry, ret_error);
}
Hashmap *bus_verify_polkit_async_registry_free(Hashmap *registry);
int varlink_verify_polkit_async(Varlink *link, sd_bus *bus, const char *action, const char **details, uid_t good_user, Hashmap **registry);
/* A JsonDispatch initializer that makes sure the allowInteractiveAuthentication boolean field we want for
* polkit support in Varlink calls is ignored while regular dispatching (and does not result in errors
* regarding unexpected fields) */
#define VARLINK_DISPATCH_POLKIT_FIELD { \
.name = "allowInteractiveAuthentication", \
.type = JSON_VARIANT_BOOLEAN, \
}
+26
View File
@@ -1771,6 +1771,28 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha
return 0;
}
static bool json_variant_is_sensitive_recursive(JsonVariant *v) {
if (!v)
return false;
if (json_variant_is_sensitive(v))
return true;
if (v == JSON_VARIANT_MAGIC_EMPTY_ARRAY ||
v == JSON_VARIANT_MAGIC_EMPTY_OBJECT)
return false;
if (!json_variant_is_regular(v))
return false;
if (!IN_SET(v->type, JSON_VARIANT_ARRAY, JSON_VARIANT_OBJECT))
return false;
if (v->is_reference)
return json_variant_is_sensitive_recursive(v->reference);
for (size_t i = 0; i < json_variant_elements(v); i++)
if (json_variant_is_sensitive_recursive(json_variant_by_index(v, i)))
return true;
return false;
}
int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret) {
_cleanup_(memstream_done) MemStream m = {};
size_t sz;
@@ -1786,6 +1808,10 @@ int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret) {
if (flags & JSON_FORMAT_OFF)
return -ENOEXEC;
if ((flags & JSON_FORMAT_REFUSE_SENSITIVE))
if (json_variant_is_sensitive_recursive(v))
return -EPERM;
f = memstream_init(&m);
if (!f)
return -ENOMEM;
+12 -11
View File
@@ -185,17 +185,18 @@ struct json_variant_foreach_state {
int json_variant_get_source(JsonVariant *v, const char **ret_source, unsigned *ret_line, unsigned *ret_column);
typedef enum JsonFormatFlags {
JSON_FORMAT_NEWLINE = 1 << 0, /* suffix with newline */
JSON_FORMAT_PRETTY = 1 << 1, /* add internal whitespace to appeal to human readers */
JSON_FORMAT_PRETTY_AUTO = 1 << 2, /* same, but only if connected to a tty (and JSON_FORMAT_NEWLINE otherwise) */
JSON_FORMAT_COLOR = 1 << 3, /* insert ANSI color sequences */
JSON_FORMAT_COLOR_AUTO = 1 << 4, /* insert ANSI color sequences if colors_enabled() says so */
JSON_FORMAT_SOURCE = 1 << 5, /* prefix with source filename/line/column */
JSON_FORMAT_SSE = 1 << 6, /* prefix/suffix with W3C server-sent events */
JSON_FORMAT_SEQ = 1 << 7, /* prefix/suffix with RFC 7464 application/json-seq */
JSON_FORMAT_FLUSH = 1 << 8, /* call fflush() after dumping JSON */
JSON_FORMAT_EMPTY_ARRAY = 1 << 9, /* output "[]" for empty input */
JSON_FORMAT_OFF = 1 << 10, /* make json_variant_format() fail with -ENOEXEC */
JSON_FORMAT_NEWLINE = 1 << 0, /* suffix with newline */
JSON_FORMAT_PRETTY = 1 << 1, /* add internal whitespace to appeal to human readers */
JSON_FORMAT_PRETTY_AUTO = 1 << 2, /* same, but only if connected to a tty (and JSON_FORMAT_NEWLINE otherwise) */
JSON_FORMAT_COLOR = 1 << 3, /* insert ANSI color sequences */
JSON_FORMAT_COLOR_AUTO = 1 << 4, /* insert ANSI color sequences if colors_enabled() says so */
JSON_FORMAT_SOURCE = 1 << 5, /* prefix with source filename/line/column */
JSON_FORMAT_SSE = 1 << 6, /* prefix/suffix with W3C server-sent events */
JSON_FORMAT_SEQ = 1 << 7, /* prefix/suffix with RFC 7464 application/json-seq */
JSON_FORMAT_FLUSH = 1 << 8, /* call fflush() after dumping JSON */
JSON_FORMAT_EMPTY_ARRAY = 1 << 9, /* output "[]" for empty input */
JSON_FORMAT_OFF = 1 << 10, /* make json_variant_format() fail with -ENOEXEC */
JSON_FORMAT_REFUSE_SENSITIVE = 1 << 11, /* return EPERM if any node in the tree is marked as senstitive */
} JsonFormatFlags;
int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret);
+103 -7
View File
@@ -171,6 +171,7 @@ struct Varlink {
JsonVariant *current;
VarlinkSymbol *current_method;
int peer_pidfd;
struct ucred ucred;
bool ucred_acquired:1;
@@ -361,6 +362,8 @@ static int varlink_new(Varlink **ret) {
.timeout = VARLINK_DEFAULT_TIMEOUT_USEC,
.af = -1,
.peer_pidfd = -EBADF,
};
*ret = v;
@@ -638,6 +641,8 @@ static void varlink_clear(Varlink *v) {
sigterm_wait(v->exec_pid);
v->exec_pid = 0;
}
v->peer_pidfd = safe_close(v->peer_pidfd);
}
static Varlink* varlink_destroy(Varlink *v) {
@@ -956,10 +961,6 @@ static int varlink_parse_message(Varlink *v) {
sz = e - begin + 1;
varlink_log(v, "New incoming message: %s", begin); /* FIXME: should we output the whole message here before validation?
* This may produce a non-printable journal entry if the message
* is invalid. We may also expose privileged information. */
r = json_parse(begin, 0, &v->current, NULL, NULL);
if (r < 0) {
/* If we encounter a parse failure flush all data. We cannot possibly recover from this,
@@ -1477,6 +1478,48 @@ finish:
return r;
}
int varlink_dispatch_again(Varlink *v) {
int r;
assert_return(v, -EINVAL);
/* If a method call handler could not process the method call just yet (for example because it needed
* some Polkit authentication first), then it can leave the call unanswered, do its thing, and then
* ask to be dispatched a second time, via this call. It will then be called again, for the same
* message */
if (v->state == VARLINK_DISCONNECTED)
return varlink_log_errno(v, SYNTHETIC_ERRNO(ENOTCONN), "Not connected.");
if (v->state != VARLINK_PENDING_METHOD)
return varlink_log_errno(v, SYNTHETIC_ERRNO(EBUSY), "Connection has no pending method.");
varlink_set_state(v, VARLINK_IDLE_SERVER);
r = sd_event_source_set_enabled(v->defer_event_source, SD_EVENT_ON);
if (r < 0)
return varlink_log_errno(v, r, "Failed to enable deferred event source: %m");
return 0;
}
int varlink_get_current_parameters(Varlink *v, JsonVariant **ret) {
JsonVariant *p;
assert_return(v, -EINVAL);
if (!v->current)
return -ENODATA;
p = json_variant_by_key(v->current, "parameters");
if (!p)
return -ENODATA;
if (ret)
*ret = json_variant_ref(p);
return 0;
}
static void handle_revents(Varlink *v, int revents) {
assert(v);
@@ -1721,12 +1764,17 @@ Varlink* varlink_flush_close_unref(Varlink *v) {
static int varlink_format_json(Varlink *v, JsonVariant *m) {
_cleanup_(erase_and_freep) char *text = NULL;
bool sensitive = false;
int r;
assert(v);
assert(m);
r = json_variant_format(m, 0, &text);
r = json_variant_format(m, JSON_FORMAT_REFUSE_SENSITIVE, &text);
if (r == -EPERM) {
sensitive = true;
r = json_variant_format(m, /* flags= */ 0, &text);
}
if (r < 0)
return r;
assert(text[r] == '\0');
@@ -1734,7 +1782,7 @@ static int varlink_format_json(Varlink *v, JsonVariant *m) {
if (v->output_buffer_size + r + 1 > VARLINK_BUFFER_MAX)
return -ENOBUFS;
varlink_log(v, "Sending message: %s", text);
varlink_log(v, "Sending message: %s", sensitive ? "<sensitive data>" : text);
if (v->output_buffer_size == 0) {
@@ -1765,7 +1813,7 @@ static int varlink_format_json(Varlink *v, JsonVariant *m) {
v->output_buffer_index = 0;
}
if (json_variant_is_sensitive(m))
if (sensitive)
v->output_buffer_sensitive = true; /* Propagate sensitive flag */
else
text = mfree(text); /* No point in the erase_and_free() destructor declared above */
@@ -2591,6 +2639,54 @@ int varlink_get_peer_pid(Varlink *v, pid_t *ret) {
return 0;
}
static int varlink_acquire_pidfd(Varlink *v) {
assert(v);
if (v->peer_pidfd >= 0)
return 0;
v->peer_pidfd = getpeerpidfd(v->fd);
if (v->peer_pidfd < 0)
return v->peer_pidfd;
return 0;
}
int varlink_get_peer_pidref(Varlink *v, PidRef *ret) {
int r;
assert_return(v, -EINVAL);
assert_return(ret, -EINVAL);
/* Returns r > 0 if we acquired the pidref via SO_PEERPIDFD (i.e. if we can use it for
* authentication). Returns == 0 if we didn't, and the pidref should not be used for
* authentication. */
r = varlink_acquire_pidfd(v);
if (r < 0)
return r;
if (v->peer_pidfd < 0) {
pid_t pid;
r = varlink_get_peer_pid(v, &pid);
if (r < 0)
return r;
r = pidref_set_pid(ret, pid);
if (r < 0)
return r;
return 0; /* didn't get pidfd securely */
}
r = pidref_set_pidfd(ret, v->peer_pidfd);
if (r < 0)
return r;
return 1; /* got pidfd securely */
}
int varlink_set_relative_timeout(Varlink *v, usec_t timeout) {
assert_return(v, -EINVAL);
assert_return(timeout > 0, -EINVAL);
+11
View File
@@ -4,6 +4,7 @@
#include "sd-event.h"
#include "json.h"
#include "pidref.h"
#include "time-util.h"
#include "varlink-idl.h"
@@ -116,6 +117,12 @@ int varlink_error_errno(Varlink *v, int error);
int varlink_notify(Varlink *v, JsonVariant *parameters);
int varlink_notifyb(Varlink *v, ...);
/* Ask for the current message to be dispatched again */
int varlink_dispatch_again(Varlink *v);
/* Get the currently processed incoming message */
int varlink_get_current_parameters(Varlink *v, JsonVariant **ret);
/* Parsing incoming data via json_dispatch() and generate a nice error on parse errors */
int varlink_dispatch(Varlink *v, JsonVariant *parameters, const JsonDispatch table[], void *userdata);
@@ -139,6 +146,7 @@ void* varlink_get_userdata(Varlink *v);
int varlink_get_peer_uid(Varlink *v, uid_t *ret);
int varlink_get_peer_pid(Varlink *v, pid_t *ret);
int varlink_get_peer_pidref(Varlink *v, PidRef *ret);
int varlink_set_relative_timeout(Varlink *v, usec_t usec);
@@ -214,6 +222,9 @@ DEFINE_TRIVIAL_CLEANUP_FUNC(VarlinkServer *, varlink_server_unref);
/* This one we invented, and use for generically propagating system errors (errno) to clients */
#define VARLINK_ERROR_SYSTEM "io.systemd.System"
/* This one we invented and is a weaker version of "org.varlink.service.PermissionDenied", and indicates that if user would allow interactive auth, we might allow access */
#define VARLINK_ERROR_INTERACTIVE_AUTHENTICATION_REQUIRED "io.systemd.InteractiveAuthenticationRequired"
/* These are errors defined in the Varlink spec */
#define VARLINK_ERROR_INTERFACE_NOT_FOUND "org.varlink.service.InterfaceNotFound"
#define VARLINK_ERROR_METHOD_NOT_FOUND "org.varlink.service.MethodNotFound"