70a5db5822
Fixes more or less: https://bugs.freedesktop.org/show_bug.cgi?id=67474
371 lines
14 KiB
C
371 lines
14 KiB
C
/* SPDX-License-Identifier: LGPL-2.1+ */
|
|
|
|
#include "group-record.h"
|
|
#include "homed-varlink.h"
|
|
#include "strv.h"
|
|
#include "user-record-util.h"
|
|
#include "user-record.h"
|
|
#include "user-util.h"
|
|
#include "format-util.h"
|
|
|
|
typedef struct LookupParameters {
|
|
const char *user_name;
|
|
const char *group_name;
|
|
union {
|
|
uid_t uid;
|
|
gid_t gid;
|
|
};
|
|
const char *service;
|
|
} LookupParameters;
|
|
|
|
static bool client_is_trusted(Varlink *link, Home *h) {
|
|
uid_t peer_uid;
|
|
int r;
|
|
|
|
assert(link);
|
|
assert(h);
|
|
|
|
r = varlink_get_peer_uid(link, &peer_uid);
|
|
if (r < 0) {
|
|
log_debug_errno(r, "Unable to query peer UID, ignoring: %m");
|
|
return false;
|
|
}
|
|
|
|
return peer_uid == 0 || peer_uid == h->uid;
|
|
}
|
|
|
|
static int build_user_json(Home *h, bool trusted, JsonVariant **ret) {
|
|
_cleanup_(user_record_unrefp) UserRecord *augmented = NULL;
|
|
UserRecordLoadFlags flags;
|
|
int r;
|
|
|
|
assert(h);
|
|
assert(ret);
|
|
|
|
flags = USER_RECORD_REQUIRE_REGULAR|USER_RECORD_ALLOW_PER_MACHINE|USER_RECORD_ALLOW_BINDING|USER_RECORD_STRIP_SECRET|USER_RECORD_ALLOW_STATUS|USER_RECORD_ALLOW_SIGNATURE;
|
|
if (trusted)
|
|
flags |= USER_RECORD_ALLOW_PRIVILEGED;
|
|
else
|
|
flags |= USER_RECORD_STRIP_PRIVILEGED;
|
|
|
|
r = home_augment_status(h, flags, &augmented);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return json_build(ret, JSON_BUILD_OBJECT(
|
|
JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(augmented->json)),
|
|
JSON_BUILD_PAIR("incomplete", JSON_BUILD_BOOLEAN(augmented->incomplete))));
|
|
}
|
|
|
|
static bool home_user_match_lookup_parameters(LookupParameters *p, Home *h) {
|
|
assert(p);
|
|
assert(h);
|
|
|
|
if (p->user_name && !streq(p->user_name, h->user_name))
|
|
return false;
|
|
|
|
if (uid_is_valid(p->uid) && h->uid != p->uid)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
|
|
|
|
static const JsonDispatch dispatch_table[] = {
|
|
{ "uid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, uid), 0 },
|
|
{ "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE },
|
|
{ "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
|
|
{}
|
|
};
|
|
|
|
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
|
LookupParameters p = {
|
|
.uid = UID_INVALID,
|
|
};
|
|
Manager *m = userdata;
|
|
bool trusted;
|
|
Home *h;
|
|
int r;
|
|
|
|
assert(parameters);
|
|
assert(m);
|
|
|
|
r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (!streq_ptr(p.service, "io.systemd.Home"))
|
|
return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
|
|
|
|
if (uid_is_valid(p.uid))
|
|
h = hashmap_get(m->homes_by_uid, UID_TO_PTR(p.uid));
|
|
else if (p.user_name)
|
|
h = hashmap_get(m->homes_by_name, p.user_name);
|
|
else {
|
|
Iterator i;
|
|
|
|
/* If neither UID nor name was specified, then dump all homes. Do so with varlink_notify()
|
|
* for all entries but the last, so that clients can stream the results, and easily process
|
|
* them piecemeal. */
|
|
|
|
HASHMAP_FOREACH(h, m->homes_by_name, i) {
|
|
|
|
if (!home_user_match_lookup_parameters(&p, h))
|
|
continue;
|
|
|
|
if (v) {
|
|
/* An entry set from the previous iteration? Then send it now */
|
|
r = varlink_notify(link, v);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
v = json_variant_unref(v);
|
|
}
|
|
|
|
trusted = client_is_trusted(link, h);
|
|
|
|
r = build_user_json(h, trusted, &v);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
if (!v)
|
|
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
|
|
|
|
return varlink_reply(link, v);
|
|
}
|
|
|
|
if (!h)
|
|
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
|
|
|
|
if (!home_user_match_lookup_parameters(&p, h))
|
|
return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
|
|
|
|
trusted = client_is_trusted(link, h);
|
|
|
|
r = build_user_json(h, trusted, &v);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return varlink_reply(link, v);
|
|
}
|
|
|
|
static int build_group_json(Home *h, JsonVariant **ret) {
|
|
_cleanup_(group_record_unrefp) GroupRecord *g = NULL;
|
|
int r;
|
|
|
|
assert(h);
|
|
assert(ret);
|
|
|
|
g = group_record_new();
|
|
if (!g)
|
|
return -ENOMEM;
|
|
|
|
r = group_record_synthesize(g, h->record);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
assert(!FLAGS_SET(g->mask, USER_RECORD_SECRET));
|
|
assert(!FLAGS_SET(g->mask, USER_RECORD_PRIVILEGED));
|
|
|
|
return json_build(ret,
|
|
JSON_BUILD_OBJECT(
|
|
JSON_BUILD_PAIR("record", JSON_BUILD_VARIANT(g->json))));
|
|
}
|
|
|
|
static bool home_group_match_lookup_parameters(LookupParameters *p, Home *h) {
|
|
assert(p);
|
|
assert(h);
|
|
|
|
if (p->group_name && !streq(h->user_name, p->group_name))
|
|
return false;
|
|
|
|
if (gid_is_valid(p->gid) && h->uid != (uid_t) p->gid)
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
|
|
|
|
static const JsonDispatch dispatch_table[] = {
|
|
{ "gid", JSON_VARIANT_UNSIGNED, json_dispatch_uid_gid, offsetof(LookupParameters, gid), 0 },
|
|
{ "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE },
|
|
{ "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
|
|
{}
|
|
};
|
|
|
|
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
|
LookupParameters p = {
|
|
.gid = GID_INVALID,
|
|
};
|
|
Manager *m = userdata;
|
|
Home *h;
|
|
int r;
|
|
|
|
assert(parameters);
|
|
assert(m);
|
|
|
|
r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (!streq_ptr(p.service, "io.systemd.Home"))
|
|
return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
|
|
|
|
if (gid_is_valid(p.gid))
|
|
h = hashmap_get(m->homes_by_uid, UID_TO_PTR((uid_t) p.gid));
|
|
else if (p.group_name)
|
|
h = hashmap_get(m->homes_by_name, p.group_name);
|
|
else {
|
|
Iterator i;
|
|
|
|
HASHMAP_FOREACH(h, m->homes_by_name, i) {
|
|
|
|
if (!home_group_match_lookup_parameters(&p, h))
|
|
continue;
|
|
|
|
if (v) {
|
|
r = varlink_notify(link, v);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
v = json_variant_unref(v);
|
|
}
|
|
|
|
r = build_group_json(h, &v);
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
if (!v)
|
|
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
|
|
|
|
return varlink_reply(link, v);
|
|
}
|
|
|
|
if (!h)
|
|
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
|
|
|
|
if (!home_group_match_lookup_parameters(&p, h))
|
|
return varlink_error(link, "io.systemd.UserDatabase.ConflictingRecordFound", NULL);
|
|
|
|
r = build_group_json(h, &v);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
return varlink_reply(link, v);
|
|
}
|
|
|
|
int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
|
|
|
|
static const JsonDispatch dispatch_table[] = {
|
|
{ "userName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, user_name), JSON_SAFE },
|
|
{ "groupName", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, group_name), JSON_SAFE },
|
|
{ "service", JSON_VARIANT_STRING, json_dispatch_const_string, offsetof(LookupParameters, service), 0 },
|
|
{}
|
|
};
|
|
|
|
Manager *m = userdata;
|
|
LookupParameters p = {};
|
|
Home *h;
|
|
int r;
|
|
|
|
assert(parameters);
|
|
assert(m);
|
|
|
|
r = json_dispatch(parameters, dispatch_table, NULL, 0, &p);
|
|
if (r < 0)
|
|
return r;
|
|
|
|
if (!streq_ptr(p.service, "io.systemd.Home"))
|
|
return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
|
|
|
|
if (p.user_name) {
|
|
const char *last = NULL;
|
|
char **i;
|
|
|
|
h = hashmap_get(m->homes_by_name, p.user_name);
|
|
if (!h)
|
|
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
|
|
|
|
if (p.group_name) {
|
|
if (!strv_contains(h->record->member_of, p.group_name))
|
|
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
|
|
|
|
return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h->user_name)),
|
|
JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p.group_name))));
|
|
}
|
|
|
|
STRV_FOREACH(i, h->record->member_of) {
|
|
if (last) {
|
|
r = varlink_notifyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h->user_name)),
|
|
JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last))));
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
last = *i;
|
|
}
|
|
|
|
if (last)
|
|
return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(h->user_name)),
|
|
JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last))));
|
|
|
|
} else if (p.group_name) {
|
|
const char *last = NULL;
|
|
Iterator i;
|
|
|
|
HASHMAP_FOREACH(h, m->homes_by_name, i) {
|
|
|
|
if (!strv_contains(h->record->member_of, p.group_name))
|
|
continue;
|
|
|
|
if (last) {
|
|
r = varlink_notifyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last)),
|
|
JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p.group_name))));
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
last = h->user_name;
|
|
}
|
|
|
|
if (last)
|
|
return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last)),
|
|
JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(p.group_name))));
|
|
} else {
|
|
const char *last_user_name = NULL, *last_group_name = NULL;
|
|
Iterator i;
|
|
|
|
HASHMAP_FOREACH(h, m->homes_by_name, i) {
|
|
char **j;
|
|
|
|
STRV_FOREACH(j, h->record->member_of) {
|
|
|
|
if (last_user_name) {
|
|
assert(last_group_name);
|
|
|
|
r = varlink_notifyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
|
|
JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
|
|
|
|
if (r < 0)
|
|
return r;
|
|
}
|
|
|
|
last_user_name = h->user_name;
|
|
last_group_name = *j;
|
|
}
|
|
}
|
|
|
|
if (last_user_name) {
|
|
assert(last_group_name);
|
|
return varlink_replyb(link, JSON_BUILD_OBJECT(JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last_user_name)),
|
|
JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(last_group_name))));
|
|
}
|
|
}
|
|
|
|
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
|
|
}
|