mirror of
https://github.com/troglobit/finit.git
synced 2026-11-07 16:01:08 +00:00
libink/dbus: identify the caller behind a broker
On the local bus SO_PEERCRED says who is calling and the kernel is the one saying it. Behind a broker one connection carries every caller, so that credential describes dbus-daemon and nothing else, and every privileged method was refused there, root included. Ask the bus driver instead. libink parks the call and hands us the sender; we ask GetConnectionUnixUser and answer when the reply lands, through the same event loop as everything else. Nothing blocks: blocking in PID 1 is why libuEv exists. That needs calls libink can make on a connection it already has, so it gained those too. Answers are cached, since a bus never reuses a unique name while it runs. Not across a restart though: a new dbus-daemon numbers from scratch and :1.7 becomes somebody else, so the cache goes when the broker does. A sender name too long to key on is refused rather than truncated, two callers sharing a truncated key would share an identity. Privilege is no longer uid 0 alone. The socket is already owned by the --with-group group, so refusing its members every method that changes anything left a wheel user able to open the bus and unable to reboot. Both gates now say the same thing. Group membership needs NSS, which the C library loads with dlopen(), so the lookup is compiled out where Finit is built to link statically. That leaves such a build root-only, which is worth saying out loud rather than leaving to be discovered. Signed-off-by: Joachim Wiberg <troglobit@gmail.com>
This commit is contained in:
1 parent
d710a23513
commit
63aabaa6df
17 files changed
+774
-90
No files matched your search
@@ -88,6 +88,67 @@ typedef struct {
|
||||
size_t body_len;
|
||||
} link_reply_t;
|
||||
|
||||
/* ---------- caller identity on a broker connection ---------- */
|
||||
|
||||
/* Handle for a call parked while its caller is identified. Opaque,
|
||||
* and safe to hold: it encodes a slot and a generation, so resolving
|
||||
* a stale handle is a no-op rather than a use-after-free. */
|
||||
typedef uint64_t link_authz_t;
|
||||
|
||||
/* "Nobody asked yet", distinct from any real uid. link_call_uid()
|
||||
* returns this on a broker connection until something forces the
|
||||
* question, which today only a LINK_METHOD_PRIVILEGED method does. */
|
||||
#define LINK_UID_UNKNOWN ((uid_t)-1)
|
||||
|
||||
/* A broker sets SENDER to a unique name, ":1.<u32>", so this is very
|
||||
* generous. Callers that key anything on a sender must reject longer
|
||||
* names rather than truncate: two senders sharing a truncated key
|
||||
* would share an identity. */
|
||||
#define LINK_SENDER_MAX 64
|
||||
|
||||
/* Answer "which uid is `sender`?" for a privileged call arriving on a
|
||||
* broker connection, where SO_PEERCRED describes the broker and not
|
||||
* the caller.
|
||||
*
|
||||
* Return 0 with *uid set when the answer is already known, 1 to answer
|
||||
* later by calling link_uid_resolved() with `tok`, or -1 when it
|
||||
* cannot be determined, which fails the call closed. Returning 1
|
||||
* without ever calling link_uid_resolved() leaks the slot and leaves
|
||||
* the caller without a reply, so always answer. */
|
||||
typedef int (*link_uid_resolver_t)(link_connection_t *conn, const char *sender,
|
||||
link_authz_t tok, uid_t *uid, void *userdata);
|
||||
|
||||
void link_server_set_uid_resolver(link_server_t *server, link_uid_resolver_t cb,
|
||||
void *userdata);
|
||||
|
||||
/* May `uid` invoke a LINK_METHOD_PRIVILEGED method? Return non-zero
|
||||
* to allow. Who counts as privileged is the embedder's policy, not
|
||||
* the library's; with no authorizer installed only uid 0 may. */
|
||||
typedef int (*link_authorizer_t)(uid_t uid, void *userdata);
|
||||
|
||||
void link_server_set_authorizer(link_server_t *server, link_authorizer_t cb,
|
||||
void *userdata);
|
||||
|
||||
/* Complete a deferred resolve and resume the parked call. Pass
|
||||
* (uid_t)-1 to say the caller could not be identified, which denies
|
||||
* it. Resolving a handle twice, or one whose connection has since
|
||||
* closed, does nothing. */
|
||||
void link_uid_resolved(link_server_t *server, link_authz_t tok, uid_t uid);
|
||||
|
||||
/* Called with the reply to an outbound link_connection_call(). `reply`
|
||||
* is NULL if the connection dropped before one arrived. */
|
||||
typedef void (*link_reply_cb_t)(link_connection_t *conn, const link_reply_t *reply,
|
||||
void *userdata);
|
||||
|
||||
/* Issue a method call on an established connection and invoke `cb`
|
||||
* when the reply lands. Unlike link_client_call() this never blocks:
|
||||
* the reply is picked up by the normal read loop. Argument marshalling
|
||||
* matches link_client_call_v(). */
|
||||
int link_connection_call(link_connection_t *conn, const char *destination,
|
||||
const char *path, const char *interface, const char *member,
|
||||
link_reply_cb_t cb, void *userdata,
|
||||
const char *signature, ...);
|
||||
|
||||
/* ---------- server / connection lifecycle ---------- */
|
||||
|
||||
/* Bind a listening socket at `path` with file mode `mode`, e.g. 0660
|
||||
|
||||
Reference in new issue
Block a user