userdbd: simplify logic for generating NSS listings

So far we basically had two ways to iterate through NSS records: one via
the varlink IPC and one via the userdb.[ch] infra, with slightly
different implementations.

Let's clean this up, and always use userdb.[ch] also when resolving via
userdbd. The different codepaths for the NameServiceSwitch and the
Multiplexer varlink service now differ only in the different flags
passed to the userdb lookup.

Behaviour shouldn't change by this. This is mostly refactoring, reducing
redundant codepaths.
This commit is contained in:
Lennart Poettering
2021-05-06 16:01:44 +02:00
parent b214825433
commit 134ff8f4d1
2 changed files with 122 additions and 328 deletions

View File

@@ -24,6 +24,9 @@ typedef enum UserDBFlags {
USERDB_EXCLUDE_DYNAMIC_USER = 1 << 4, /* exclude looking up in io.systemd.DynamicUser */
USERDB_AVOID_MULTIPLEXER = 1 << 5, /* exclude looking up via io.systemd.Multiplexer */
USERDB_DONT_SYNTHESIZE = 1 << 6, /* don't synthesize root/nobody */
/* Combinations */
USERDB_NSS_ONLY = USERDB_EXCLUDE_VARLINK|USERDB_DONT_SYNTHESIZE,
} UserDBFlags;
int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret);

View File

@@ -113,6 +113,21 @@ static int build_user_json(Varlink *link, UserRecord *ur, JsonVariant **ret) {
JSON_BUILD_PAIR("incomplete", JSON_BUILD_BOOLEAN(stripped->incomplete))));
}
static int userdb_flags_from_service(Varlink *link, const char *service, UserDBFlags *ret) {
assert(link);
assert(service);
assert(ret);
if (streq_ptr(service, "io.systemd.NameServiceSwitch"))
*ret = USERDB_NSS_ONLY|USERDB_AVOID_MULTIPLEXER;
else if (streq_ptr(service, "io.systemd.Multiplexer"))
*ret = USERDB_AVOID_MULTIPLEXER;
else
return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
return 0;
}
static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, VarlinkMethodFlags flags, void *userdata) {
static const JsonDispatch dispatch_table[] = {
@@ -127,6 +142,7 @@ static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, Var
LookupParameters p = {
.uid = UID_INVALID,
};
UserDBFlags userdb_flags;
int r;
assert(parameters);
@@ -135,109 +151,49 @@ static int vl_method_get_user_record(Varlink *link, JsonVariant *parameters, Var
if (r < 0)
return r;
if (streq_ptr(p.service, "io.systemd.NameServiceSwitch")) {
if (uid_is_valid(p.uid))
r = nss_user_record_by_uid(p.uid, true, &hr);
else if (p.user_name)
r = nss_user_record_by_name(p.user_name, true, &hr);
else {
_cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
r = userdb_flags_from_service(link, p.service, &userdb_flags);
if (r < 0)
return r;
setpwent();
if (uid_is_valid(p.uid))
r = userdb_by_uid(p.uid, userdb_flags, &hr);
else if (p.user_name)
r = userdb_by_name(p.user_name, userdb_flags, &hr);
else {
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
for (;;) {
_cleanup_(user_record_unrefp) UserRecord *z = NULL;
_cleanup_free_ char *sbuf = NULL;
struct passwd *pw;
struct spwd spwd;
r = userdb_all(userdb_flags, &iterator);
if (r < 0)
return r;
errno = 0;
pw = getpwent();
if (!pw) {
if (errno != 0)
log_debug_errno(errno, "Failure while iterating through NSS user database, ignoring: %m");
for (;;) {
_cleanup_(user_record_unrefp) UserRecord *z = NULL;
break;
}
r = nss_spwd_for_passwd(pw, &spwd, &sbuf);
if (r < 0)
log_debug_errno(r, "Failed to acquire shadow entry for user %s, ignoring: %m", pw->pw_name);
r = nss_passwd_to_user_record(pw, NULL, &z);
if (r < 0) {
endpwent();
return r;
}
if (last) {
r = varlink_notify(link, last);
if (r < 0) {
endpwent();
return r;
}
last = json_variant_unref(last);
}
r = build_user_json(link, z, &last);
if (r < 0) {
endpwent();
return r;
}
}
endpwent();
if (!last)
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
return varlink_reply(link, last);
}
} else if (streq_ptr(p.service, "io.systemd.Multiplexer")) {
if (uid_is_valid(p.uid))
r = userdb_by_uid(p.uid, USERDB_AVOID_MULTIPLEXER, &hr);
else if (p.user_name)
r = userdb_by_name(p.user_name, USERDB_AVOID_MULTIPLEXER, &hr);
else {
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
r = userdb_all(USERDB_AVOID_MULTIPLEXER, &iterator);
r = userdb_iterator_get(iterator, &z);
if (r == -ESRCH)
break;
if (r < 0)
return r;
for (;;) {
_cleanup_(user_record_unrefp) UserRecord *z = NULL;
r = userdb_iterator_get(iterator, &z);
if (r == -ESRCH)
break;
if (last) {
r = varlink_notify(link, last);
if (r < 0)
return r;
if (last) {
r = varlink_notify(link, last);
if (r < 0)
return r;
last = json_variant_unref(last);
}
r = build_user_json(link, z, &last);
if (r < 0)
return r;
last = json_variant_unref(last);
}
if (!last)
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
return varlink_reply(link, last);
r = build_user_json(link, z, &last);
if (r < 0)
return r;
}
} else
return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
if (!last)
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
return varlink_reply(link, last);
}
if (r == -ESRCH)
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
if (r < 0) {
@@ -313,6 +269,7 @@ static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, Va
LookupParameters p = {
.gid = GID_INVALID,
};
UserDBFlags userdb_flags;
int r;
assert(parameters);
@@ -321,110 +278,49 @@ static int vl_method_get_group_record(Varlink *link, JsonVariant *parameters, Va
if (r < 0)
return r;
if (streq_ptr(p.service, "io.systemd.NameServiceSwitch")) {
r = userdb_flags_from_service(link, p.service, &userdb_flags);
if (r < 0)
return r;
if (gid_is_valid(p.gid))
r = nss_group_record_by_gid(p.gid, true, &g);
else if (p.group_name)
r = nss_group_record_by_name(p.group_name, true, &g);
else {
_cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
if (gid_is_valid(p.gid))
r = groupdb_by_gid(p.gid, userdb_flags, &g);
else if (p.group_name)
r = groupdb_by_name(p.group_name, userdb_flags, &g);
else {
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
setgrent();
r = groupdb_all(userdb_flags, &iterator);
if (r < 0)
return r;
for (;;) {
_cleanup_(group_record_unrefp) GroupRecord *z = NULL;
_cleanup_free_ char *sbuf = NULL;
struct group *grp;
struct sgrp sgrp;
for (;;) {
_cleanup_(group_record_unrefp) GroupRecord *z = NULL;
errno = 0;
grp = getgrent();
if (!grp) {
if (errno != 0)
log_debug_errno(errno, "Failure while iterating through NSS group database, ignoring: %m");
break;
}
r = nss_sgrp_for_group(grp, &sgrp, &sbuf);
if (r < 0)
log_debug_errno(r, "Failed to acquire shadow entry for group %s, ignoring: %m", grp->gr_name);
r = nss_group_to_group_record(grp, r >= 0 ? &sgrp : NULL, &z);
if (r < 0) {
endgrent();
return r;
}
if (last) {
r = varlink_notify(link, last);
if (r < 0) {
endgrent();
return r;
}
last = json_variant_unref(last);
}
r = build_group_json(link, z, &last);
if (r < 0) {
endgrent();
return r;
}
}
endgrent();
if (!last)
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
return varlink_reply(link, last);
}
} else if (streq_ptr(p.service, "io.systemd.Multiplexer")) {
if (gid_is_valid(p.gid))
r = groupdb_by_gid(p.gid, USERDB_AVOID_MULTIPLEXER, &g);
else if (p.group_name)
r = groupdb_by_name(p.group_name, USERDB_AVOID_MULTIPLEXER, &g);
else {
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
_cleanup_(json_variant_unrefp) JsonVariant *last = NULL;
r = groupdb_all(USERDB_AVOID_MULTIPLEXER, &iterator);
r = groupdb_iterator_get(iterator, &z);
if (r == -ESRCH)
break;
if (r < 0)
return r;
for (;;) {
_cleanup_(group_record_unrefp) GroupRecord *z = NULL;
r = groupdb_iterator_get(iterator, &z);
if (r == -ESRCH)
break;
if (last) {
r = varlink_notify(link, last);
if (r < 0)
return r;
if (last) {
r = varlink_notify(link, last);
if (r < 0)
return r;
last = json_variant_unref(last);
}
r = build_group_json(link, z, &last);
if (r < 0)
return r;
last = json_variant_unref(last);
}
if (!last)
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
return varlink_reply(link, last);
r = build_group_json(link, z, &last);
if (r < 0)
return r;
}
} else
return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
if (!last)
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
return varlink_reply(link, last);
}
if (r == -ESRCH)
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
if (r < 0) {
@@ -451,7 +347,10 @@ static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, Var
{}
};
_cleanup_free_ char *last_user_name = NULL, *last_group_name = NULL;
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
LookupParameters p = {};
UserDBFlags userdb_flags;
int r;
assert(parameters);
@@ -460,167 +359,59 @@ static int vl_method_get_memberships(Varlink *link, JsonVariant *parameters, Var
if (r < 0)
return r;
if (streq_ptr(p.service, "io.systemd.NameServiceSwitch")) {
r = userdb_flags_from_service(link, p.service, &userdb_flags);
if (r < 0)
return r;
if (p.group_name) {
_cleanup_(group_record_unrefp) GroupRecord *g = NULL;
const char *last = NULL;
char **i;
if (p.group_name)
r = membershipdb_by_group(p.group_name, userdb_flags, &iterator);
else if (p.user_name)
r = membershipdb_by_user(p.user_name, userdb_flags, &iterator);
else
r = membershipdb_all(userdb_flags, &iterator);
if (r < 0)
return r;
r = nss_group_record_by_name(p.group_name, true, &g);
if (r == -ESRCH)
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
if (r < 0)
return r;
for (;;) {
_cleanup_free_ char *user_name = NULL, *group_name = NULL;
STRV_FOREACH(i, g->members) {
if (p.user_name && !streq_ptr(p.user_name, *i))
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(g->group_name))));
if (r < 0)
return r;
}
last = *i;
}
if (!last)
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
return varlink_replyb(link, JSON_BUILD_OBJECT(
JSON_BUILD_PAIR("userName", JSON_BUILD_STRING(last)),
JSON_BUILD_PAIR("groupName", JSON_BUILD_STRING(g->group_name))));
} else {
_cleanup_free_ char *last_user_name = NULL, *last_group_name = NULL;
setgrent();
for (;;) {
struct group *grp;
const char* two[2], **users, **i;
errno = 0;
grp = getgrent();
if (!grp) {
if (errno != 0)
log_debug_errno(errno, "Failure while iterating through NSS group database, ignoring: %m");
break;
}
if (p.user_name) {
if (!strv_contains(grp->gr_mem, p.user_name))
continue;
two[0] = p.user_name;
two[1] = NULL;
users = two;
} else
users = (const char**) grp->gr_mem;
STRV_FOREACH(i, users) {
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) {
endgrent();
return r;
}
free(last_user_name);
free(last_group_name);
}
last_user_name = strdup(*i);
last_group_name = strdup(grp->gr_name);
if (!last_user_name || !last_group_name) {
endgrent();
return -ENOMEM;
}
}
}
endgrent();
if (!last_user_name) {
assert(!last_group_name);
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
}
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))));
}
} else if (streq_ptr(p.service, "io.systemd.Multiplexer")) {
_cleanup_free_ char *last_user_name = NULL, *last_group_name = NULL;
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
if (p.group_name)
r = membershipdb_by_group(p.group_name, USERDB_AVOID_MULTIPLEXER, &iterator);
else if (p.user_name)
r = membershipdb_by_user(p.user_name, USERDB_AVOID_MULTIPLEXER, &iterator);
else
r = membershipdb_all(USERDB_AVOID_MULTIPLEXER, &iterator);
r = membershipdb_iterator_get(iterator, &user_name, &group_name);
if (r == -ESRCH)
break;
if (r < 0)
return r;
for (;;) {
_cleanup_free_ char *user_name = NULL, *group_name = NULL;
/* If both group + user are specified do a-posteriori filtering */
if (p.group_name && p.user_name && !streq(group_name, p.group_name))
continue;
r = membershipdb_iterator_get(iterator, &user_name, &group_name);
if (r == -ESRCH)
break;
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;
/* If both group + user are specified do a-posteriori filtering */
if (p.group_name && p.user_name && !streq(group_name, p.group_name))
continue;
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;
free(last_user_name);
free(last_group_name);
}
last_user_name = TAKE_PTR(user_name);
last_group_name = TAKE_PTR(group_name);
free(last_user_name);
free(last_group_name);
}
if (!last_user_name) {
assert(!last_group_name);
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
}
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))));
last_user_name = TAKE_PTR(user_name);
last_group_name = TAKE_PTR(group_name);
}
return varlink_error(link, "io.systemd.UserDatabase.BadService", NULL);
if (!last_user_name) {
assert(!last_group_name);
return varlink_error(link, "io.systemd.UserDatabase.NoRecordFound", NULL);
}
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))));
}
static int process_connection(VarlinkServer *server, int fd) {