mirror of
https://github.com/systemd/systemd-stable.git
synced 2024-12-23 17:34:00 +03:00
userdb: optionally read user/group/membership "dropins", too
This commit is contained in:
parent
2d0b71b6f6
commit
85f088abe8
@ -262,6 +262,8 @@ shared_sources = files('''
|
||||
user-record.h
|
||||
userdb.c
|
||||
userdb.h
|
||||
userdb-dropin.c
|
||||
userdb-dropin.h
|
||||
utmp-wtmp.h
|
||||
varlink.c
|
||||
varlink.h
|
||||
|
302
src/shared/userdb-dropin.c
Normal file
302
src/shared/userdb-dropin.c
Normal file
@ -0,0 +1,302 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "fileio.h"
|
||||
#include "format-util.h"
|
||||
#include "path-util.h"
|
||||
#include "stdio-util.h"
|
||||
#include "user-util.h"
|
||||
#include "userdb-dropin.h"
|
||||
|
||||
static int load_user(
|
||||
FILE *f,
|
||||
const char *path,
|
||||
const char *name,
|
||||
uid_t uid,
|
||||
UserDBFlags flags,
|
||||
UserRecord **ret) {
|
||||
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
||||
_cleanup_(user_record_unrefp) UserRecord *u = NULL;
|
||||
bool have_privileged;
|
||||
int r;
|
||||
|
||||
assert(f);
|
||||
|
||||
r = json_parse_file(f, path, 0, &v, NULL, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (FLAGS_SET(flags, USERDB_SUPPRESS_SHADOW) || !path || !(name || uid_is_valid(uid)))
|
||||
have_privileged = false;
|
||||
else {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *privileged_v = NULL;
|
||||
_cleanup_free_ char *d = NULL, *j = NULL;
|
||||
|
||||
/* Let's load the "privileged" section from a companion file. But only if USERDB_AVOID_SHADOW
|
||||
* is not set. After all, the privileged section kinda takes the role of the data from the
|
||||
* shadow file, hence it makes sense to use the same flag here.
|
||||
*
|
||||
* The general assumption is that whoever provides these records makes the .user file
|
||||
* world-readable, but the .privilege file readable to root and the assigned UID only. But we
|
||||
* won't verify that here, as it would be too late. */
|
||||
|
||||
r = path_extract_directory(path, &d);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (name) {
|
||||
j = strjoin(d, "/", name, ".user-privileged");
|
||||
if (!j)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
assert(uid_is_valid(uid));
|
||||
if (asprintf(&j, "%s/" UID_FMT ".user-privileged", d, uid) < 0)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = json_parse_file(NULL, j, JSON_PARSE_SENSITIVE, &privileged_v, NULL, NULL);
|
||||
if (ERRNO_IS_PRIVILEGE(r))
|
||||
have_privileged = false;
|
||||
else if (r == -ENOENT)
|
||||
have_privileged = true; /* if the privileged file doesn't exist, we are complete */
|
||||
else if (r < 0)
|
||||
return r;
|
||||
else {
|
||||
r = json_variant_merge(&v, privileged_v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
have_privileged = true;
|
||||
}
|
||||
}
|
||||
|
||||
u = user_record_new();
|
||||
if (!u)
|
||||
return -ENOMEM;
|
||||
|
||||
r = user_record_load(
|
||||
u, v,
|
||||
USER_RECORD_REQUIRE_REGULAR|
|
||||
USER_RECORD_ALLOW_PER_MACHINE|
|
||||
USER_RECORD_ALLOW_BINDING|
|
||||
USER_RECORD_ALLOW_SIGNATURE|
|
||||
(have_privileged ? USER_RECORD_ALLOW_PRIVILEGED : 0));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (name && !streq_ptr(name, u->user_name))
|
||||
return -EINVAL;
|
||||
|
||||
if (uid_is_valid(uid) && uid != u->uid)
|
||||
return -EINVAL;
|
||||
|
||||
u->incomplete = !have_privileged;
|
||||
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(u);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dropin_user_record_by_name(const char *name, const char *path, UserDBFlags flags, UserRecord **ret) {
|
||||
_cleanup_free_ char *found_path = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
|
||||
if (path) {
|
||||
f = fopen(path, "re");
|
||||
if (!f)
|
||||
return errno == ENOENT ? -ESRCH : -errno; /* We generally want ESRCH to indicate no such user */
|
||||
} else {
|
||||
const char *j;
|
||||
|
||||
j = strjoina(name, ".user");
|
||||
if (!filename_is_valid(j)) /* Doesn't qualify as valid filename? Then it's definitely not provided as a drop-in */
|
||||
return -ESRCH;
|
||||
|
||||
r = search_and_fopen_nulstr(j, "re", NULL, USERDB_DROPIN_DIR_NULSTR("userdb"), &f, &found_path);
|
||||
if (r == -ENOENT)
|
||||
return -ESRCH;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
path = found_path;
|
||||
}
|
||||
|
||||
return load_user(f, path, name, UID_INVALID, flags, ret);
|
||||
}
|
||||
|
||||
int dropin_user_record_by_uid(uid_t uid, const char *path, UserDBFlags flags, UserRecord **ret) {
|
||||
_cleanup_free_ char *found_path = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
int r;
|
||||
|
||||
assert(uid_is_valid(uid));
|
||||
|
||||
if (path) {
|
||||
f = fopen(path, "re");
|
||||
if (!f)
|
||||
return errno == ENOENT ? -ESRCH : -errno;
|
||||
} else {
|
||||
char buf[DECIMAL_STR_MAX(uid_t) + STRLEN(".user") + 1];
|
||||
|
||||
xsprintf(buf, UID_FMT ".user", uid);
|
||||
/* Note that we don't bother to validate this as a filename, as this is generated from a decimal
|
||||
* integer, i.e. is definitely OK as a filename */
|
||||
|
||||
r = search_and_fopen_nulstr(buf, "re", NULL, USERDB_DROPIN_DIR_NULSTR("userdb"), &f, &found_path);
|
||||
if (r == -ENOENT)
|
||||
return -ESRCH;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
path = found_path;
|
||||
}
|
||||
|
||||
return load_user(f, path, NULL, uid, flags, ret);
|
||||
}
|
||||
|
||||
static int load_group(
|
||||
FILE *f,
|
||||
const char *path,
|
||||
const char *name,
|
||||
gid_t gid,
|
||||
UserDBFlags flags,
|
||||
GroupRecord **ret) {
|
||||
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *v = NULL;
|
||||
_cleanup_(group_record_unrefp) GroupRecord *g = NULL;
|
||||
bool have_privileged;
|
||||
int r;
|
||||
|
||||
assert(f);
|
||||
|
||||
r = json_parse_file(f, path, 0, &v, NULL, NULL);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (FLAGS_SET(flags, USERDB_SUPPRESS_SHADOW) || !path || !(name || gid_is_valid(gid)))
|
||||
have_privileged = false;
|
||||
else {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *privileged_v = NULL;
|
||||
_cleanup_free_ char *d = NULL, *j = NULL;
|
||||
|
||||
r = path_extract_directory(path, &d);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (name) {
|
||||
j = strjoin(d, "/", name, ".group-privileged");
|
||||
if (!j)
|
||||
return -ENOMEM;
|
||||
} else {
|
||||
assert(gid_is_valid(gid));
|
||||
if (asprintf(&j, "%s/" GID_FMT ".group-privileged", d, gid) < 0)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
||||
r = json_parse_file(NULL, j, JSON_PARSE_SENSITIVE, &privileged_v, NULL, NULL);
|
||||
if (ERRNO_IS_PRIVILEGE(r))
|
||||
have_privileged = false;
|
||||
else if (r == -ENOENT)
|
||||
have_privileged = true; /* if the privileged file doesn't exist, we are complete */
|
||||
else if (r < 0)
|
||||
return r;
|
||||
else {
|
||||
r = json_variant_merge(&v, privileged_v);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
have_privileged = true;
|
||||
}
|
||||
}
|
||||
|
||||
g = group_record_new();
|
||||
if (!g)
|
||||
return -ENOMEM;
|
||||
|
||||
r = group_record_load(
|
||||
g, v,
|
||||
USER_RECORD_REQUIRE_REGULAR|
|
||||
USER_RECORD_ALLOW_PER_MACHINE|
|
||||
USER_RECORD_ALLOW_BINDING|
|
||||
USER_RECORD_ALLOW_SIGNATURE|
|
||||
(have_privileged ? USER_RECORD_ALLOW_PRIVILEGED : 0));
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
if (name && !streq_ptr(name, g->group_name))
|
||||
return -EINVAL;
|
||||
|
||||
if (gid_is_valid(gid) && gid != g->gid)
|
||||
return -EINVAL;
|
||||
|
||||
g->incomplete = !have_privileged;
|
||||
|
||||
if (ret)
|
||||
*ret = TAKE_PTR(g);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int dropin_group_record_by_name(const char *name, const char *path, UserDBFlags flags, GroupRecord **ret) {
|
||||
_cleanup_free_ char *found_path = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
int r;
|
||||
|
||||
assert(name);
|
||||
|
||||
if (path) {
|
||||
f = fopen(path, "re");
|
||||
if (!f)
|
||||
return errno == ENOENT ? -ESRCH : -errno;
|
||||
} else {
|
||||
const char *j;
|
||||
|
||||
j = strjoina(name, ".group");
|
||||
if (!filename_is_valid(j)) /* Doesn't qualify as valid filename? Then it's definitely not provided as a drop-in */
|
||||
return -ESRCH;
|
||||
|
||||
r = search_and_fopen_nulstr(j, "re", NULL, USERDB_DROPIN_DIR_NULSTR("userdb"), &f, &found_path);
|
||||
if (r == -ENOENT)
|
||||
return -ESRCH;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
path = found_path;
|
||||
}
|
||||
|
||||
return load_group(f, path, name, GID_INVALID, flags, ret);
|
||||
}
|
||||
|
||||
int dropin_group_record_by_gid(gid_t gid, const char *path, UserDBFlags flags, GroupRecord **ret) {
|
||||
_cleanup_free_ char *found_path = NULL;
|
||||
_cleanup_fclose_ FILE *f = NULL;
|
||||
int r;
|
||||
|
||||
assert(gid_is_valid(gid));
|
||||
|
||||
if (path) {
|
||||
f = fopen(path, "re");
|
||||
if (!f)
|
||||
return errno == ENOENT ? -ESRCH : -errno;
|
||||
} else {
|
||||
char buf[DECIMAL_STR_MAX(gid_t) + STRLEN(".group") + 1];
|
||||
|
||||
xsprintf(buf, GID_FMT ".group", gid);
|
||||
|
||||
r = search_and_fopen_nulstr(buf, "re", NULL, USERDB_DROPIN_DIR_NULSTR("userdb"), &f, &found_path);
|
||||
if (r == -ENOENT)
|
||||
return -ESRCH;
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
path = found_path;
|
||||
}
|
||||
|
||||
return load_group(f, path, NULL, gid, flags, ret);
|
||||
}
|
23
src/shared/userdb-dropin.h
Normal file
23
src/shared/userdb-dropin.h
Normal file
@ -0,0 +1,23 @@
|
||||
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
||||
#pragma once
|
||||
|
||||
#include "def.h"
|
||||
#include "group-record.h"
|
||||
#include "user-record.h"
|
||||
#include "userdb.h"
|
||||
|
||||
/* This could be put together with CONF_PATHS_NULSTR, with the exception of the /run/host/ part in the
|
||||
* middle, which we use here, but not otherwise. */
|
||||
#define USERDB_DROPIN_DIR_NULSTR(n) \
|
||||
"/etc/" n "\0" \
|
||||
"/run/" n "\0" \
|
||||
"/run/host/" n "\0" \
|
||||
"/usr/local/lib/" n "\0" \
|
||||
"/usr/lib/" n "\0" \
|
||||
_CONF_PATHS_SPLIT_USR_NULSTR(n)
|
||||
|
||||
int dropin_user_record_by_name(const char *name, const char *path, UserDBFlags flags, UserRecord **ret);
|
||||
int dropin_user_record_by_uid(uid_t uid, const char *path, UserDBFlags flags, UserRecord **ret);
|
||||
|
||||
int dropin_group_record_by_name(const char *name, const char *path, UserDBFlags flags, GroupRecord **ret);
|
||||
int dropin_group_record_by_gid(gid_t gid, const char *path, UserDBFlags flags, GroupRecord **ret);
|
@ -2,10 +2,12 @@
|
||||
|
||||
#include <sys/auxv.h>
|
||||
|
||||
#include "conf-files.h"
|
||||
#include "dirent-util.h"
|
||||
#include "dlfcn-util.h"
|
||||
#include "errno-util.h"
|
||||
#include "fd-util.h"
|
||||
#include "format-util.h"
|
||||
#include "missing_syscall.h"
|
||||
#include "parse-util.h"
|
||||
#include "set.h"
|
||||
@ -13,6 +15,7 @@
|
||||
#include "strv.h"
|
||||
#include "user-record-nss.h"
|
||||
#include "user-util.h"
|
||||
#include "userdb-dropin.h"
|
||||
#include "userdb.h"
|
||||
#include "varlink.h"
|
||||
|
||||
@ -31,9 +34,12 @@ struct UserDBIterator {
|
||||
Set *links;
|
||||
bool nss_covered:1;
|
||||
bool nss_iterating:1;
|
||||
bool dropin_covered:1;
|
||||
bool synthesize_root:1;
|
||||
bool synthesize_nobody:1;
|
||||
bool nss_systemd_blocked:1;
|
||||
char **dropins;
|
||||
size_t current_dropin;
|
||||
int error;
|
||||
unsigned n_found;
|
||||
sd_event *event;
|
||||
@ -43,7 +49,7 @@ struct UserDBIterator {
|
||||
char *found_user_name, *found_group_name; /* when .what == LOOKUP_MEMBERSHIP */
|
||||
char **members_of_group;
|
||||
size_t index_members_of_group;
|
||||
char *filter_user_name;
|
||||
char *filter_user_name, *filter_group_name;
|
||||
};
|
||||
|
||||
UserDBIterator* userdb_iterator_free(UserDBIterator *iterator) {
|
||||
@ -51,6 +57,7 @@ UserDBIterator* userdb_iterator_free(UserDBIterator *iterator) {
|
||||
return NULL;
|
||||
|
||||
set_free(iterator->links);
|
||||
strv_free(iterator->dropins);
|
||||
|
||||
switch (iterator->what) {
|
||||
|
||||
@ -75,6 +82,7 @@ UserDBIterator* userdb_iterator_free(UserDBIterator *iterator) {
|
||||
free(iterator->found_group_name);
|
||||
strv_free(iterator->members_of_group);
|
||||
free(iterator->filter_user_name);
|
||||
free(iterator->filter_group_name);
|
||||
|
||||
if (iterator->nss_iterating)
|
||||
endgrent();
|
||||
@ -425,7 +433,7 @@ static int userdb_start_query(
|
||||
}
|
||||
|
||||
/* First, let's talk to the multiplexer, if we can */
|
||||
if ((flags & (USERDB_AVOID_MULTIPLEXER|USERDB_EXCLUDE_DYNAMIC_USER|USERDB_EXCLUDE_NSS|USERDB_DONT_SYNTHESIZE)) == 0 &&
|
||||
if ((flags & (USERDB_AVOID_MULTIPLEXER|USERDB_EXCLUDE_DYNAMIC_USER|USERDB_EXCLUDE_NSS|USERDB_EXCLUDE_DROPIN|USERDB_DONT_SYNTHESIZE)) == 0 &&
|
||||
!strv_contains(except, "io.systemd.Multiplexer") &&
|
||||
(!only || strv_contains(only, "io.systemd.Multiplexer"))) {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *patched_query = json_variant_ref(query);
|
||||
@ -437,6 +445,7 @@ static int userdb_start_query(
|
||||
r = userdb_connect(iterator, "/run/systemd/userdb/io.systemd.Multiplexer", method, more, patched_query);
|
||||
if (r >= 0) {
|
||||
iterator->nss_covered = true; /* The multiplexer does NSS */
|
||||
iterator->dropin_covered = true; /* It also handles drop-in stuff */
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@ -452,7 +461,7 @@ static int userdb_start_query(
|
||||
FOREACH_DIRENT(de, d, return -errno) {
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *patched_query = NULL;
|
||||
_cleanup_free_ char *p = NULL;
|
||||
bool is_nss;
|
||||
bool is_nss, is_dropin;
|
||||
|
||||
if (streq(de->d_name, "io.systemd.Multiplexer")) /* We already tried this above, don't try this again */
|
||||
continue;
|
||||
@ -469,6 +478,11 @@ static int userdb_start_query(
|
||||
if ((flags & (USERDB_EXCLUDE_NSS|USERDB_AVOID_MULTIPLEXER)) && is_nss)
|
||||
continue;
|
||||
|
||||
/* Similar for the drop-in service */
|
||||
is_dropin = streq(de->d_name, "io.systemd.DropIn");
|
||||
if ((flags & (USERDB_EXCLUDE_DROPIN|USERDB_AVOID_MULTIPLEXER)) && is_dropin)
|
||||
continue;
|
||||
|
||||
if (strv_contains(except, de->d_name))
|
||||
continue;
|
||||
|
||||
@ -485,9 +499,11 @@ static int userdb_start_query(
|
||||
return log_debug_errno(r, "Unable to set service JSON field: %m");
|
||||
|
||||
r = userdb_connect(iterator, p, method, more, patched_query);
|
||||
if (is_nss && r >= 0) /* Turn off fallback NSS if we found the NSS service and could connect
|
||||
* to it */
|
||||
if (is_nss && r >= 0) /* Turn off fallback NSS + dropin if we found the NSS/dropin service
|
||||
* and could connect to it */
|
||||
iterator->nss_covered = true;
|
||||
if (is_dropin && r >= 0)
|
||||
iterator->dropin_covered = true;
|
||||
|
||||
if (ret == 0 && r < 0)
|
||||
ret = r;
|
||||
@ -624,6 +640,12 @@ int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && !iterator->dropin_covered) {
|
||||
r = dropin_user_record_by_name(name, NULL, flags, ret);
|
||||
if (r >= 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && !iterator->nss_covered) {
|
||||
/* Make sure the NSS lookup doesn't recurse back to us. */
|
||||
|
||||
@ -671,6 +693,12 @@ int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && !iterator->dropin_covered) {
|
||||
r = dropin_user_record_by_uid(uid, NULL, flags, ret);
|
||||
if (r >= 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && !iterator->nss_covered) {
|
||||
r = userdb_iterator_block_nss_systemd(iterator);
|
||||
if (r >= 0) {
|
||||
@ -694,7 +722,7 @@ int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret) {
|
||||
|
||||
int userdb_all(UserDBFlags flags, UserDBIterator **ret) {
|
||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||
int r;
|
||||
int r, qr;
|
||||
|
||||
assert(ret);
|
||||
|
||||
@ -704,17 +732,33 @@ int userdb_all(UserDBFlags flags, UserDBIterator **ret) {
|
||||
|
||||
iterator->synthesize_root = iterator->synthesize_nobody = !FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE);
|
||||
|
||||
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", true, NULL, flags);
|
||||
qr = userdb_start_query(iterator, "io.systemd.UserDatabase.GetUserRecord", true, NULL, flags);
|
||||
|
||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (r < 0 || !iterator->nss_covered)) {
|
||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (qr < 0 || !iterator->nss_covered)) {
|
||||
r = userdb_iterator_block_nss_systemd(iterator);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
setpwent();
|
||||
iterator->nss_iterating = true;
|
||||
} else if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && (qr < 0 || !iterator->dropin_covered)) {
|
||||
r = conf_files_list_nulstr(
|
||||
&iterator->dropins,
|
||||
".user",
|
||||
NULL,
|
||||
CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED,
|
||||
USERDB_DROPIN_DIR_NULSTR("userdb"));
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to find user drop-ins, ignoring: %m");
|
||||
}
|
||||
|
||||
/* propagate IPC error, but only if there are no drop-ins */
|
||||
if (qr < 0 &&
|
||||
!iterator->nss_iterating &&
|
||||
strv_isempty(iterator->dropins))
|
||||
return qr;
|
||||
|
||||
*ret = TAKE_PTR(iterator);
|
||||
return 0;
|
||||
@ -772,9 +816,45 @@ int userdb_iterator_get(UserDBIterator *iterator, UserRecord **ret) {
|
||||
endpwent();
|
||||
}
|
||||
|
||||
r = userdb_process(iterator, ret, NULL, NULL, NULL);
|
||||
for (; iterator->dropins && iterator->dropins[iterator->current_dropin]; iterator->current_dropin++) {
|
||||
const char *i = iterator->dropins[iterator->current_dropin];
|
||||
_cleanup_free_ char *fn = NULL;
|
||||
uid_t uid;
|
||||
char *e;
|
||||
|
||||
/* Next, let's add in the static drop-ins, which are quick to retrieve */
|
||||
|
||||
r = path_extract_filename(i, &fn);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
e = endswith(fn, ".user"); /* not actually a .user file? Then skip to next */
|
||||
if (!e)
|
||||
continue;
|
||||
|
||||
*e = 0; /* Chop off suffix */
|
||||
|
||||
if (parse_uid(fn, &uid) < 0) /* not a UID .user file? Then skip to next */
|
||||
continue;
|
||||
|
||||
r = dropin_user_record_by_uid(uid, i, iterator->flags, ret);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to parse user record for UID " UID_FMT ", ignoring: %m", uid);
|
||||
continue; /* If we failed to parse this record, let's suppress it from enumeration,
|
||||
* and continue with the next record. Maybe someone is dropping it files
|
||||
* and only partially wrote this one. */
|
||||
}
|
||||
|
||||
iterator->current_dropin++; /* make sure on the next call of userdb_iterator_get() we continue with the next dropin */
|
||||
iterator->n_found++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* Then, let's return the users provided by varlink IPC */
|
||||
r = userdb_process(iterator, ret, NULL, NULL, NULL);
|
||||
if (r < 0) {
|
||||
|
||||
/* Finally, synthesize root + nobody if not done yet */
|
||||
if (iterator->synthesize_root) {
|
||||
iterator->synthesize_root = false;
|
||||
iterator->n_found++;
|
||||
@ -835,6 +915,13 @@ int groupdb_by_name(const char *name, UserDBFlags flags, GroupRecord **ret) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && !(iterator && iterator->dropin_covered)) {
|
||||
r = dropin_group_record_by_name(name, NULL, flags, ret);
|
||||
if (r >= 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && !(iterator && iterator->nss_covered)) {
|
||||
r = userdb_iterator_block_nss_systemd(iterator);
|
||||
if (r >= 0) {
|
||||
@ -879,6 +966,12 @@ int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret) {
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && !(iterator && iterator->dropin_covered)) {
|
||||
r = dropin_group_record_by_gid(gid, NULL, flags, ret);
|
||||
if (r >= 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && !(iterator && iterator->nss_covered)) {
|
||||
r = userdb_iterator_block_nss_systemd(iterator);
|
||||
if (r >= 0) {
|
||||
@ -901,7 +994,7 @@ int groupdb_by_gid(gid_t gid, UserDBFlags flags, GroupRecord **ret) {
|
||||
|
||||
int groupdb_all(UserDBFlags flags, UserDBIterator **ret) {
|
||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||
int r;
|
||||
int r, qr;
|
||||
|
||||
assert(ret);
|
||||
|
||||
@ -911,17 +1004,32 @@ int groupdb_all(UserDBFlags flags, UserDBIterator **ret) {
|
||||
|
||||
iterator->synthesize_root = iterator->synthesize_nobody = !FLAGS_SET(flags, USERDB_DONT_SYNTHESIZE);
|
||||
|
||||
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", true, NULL, flags);
|
||||
qr = userdb_start_query(iterator, "io.systemd.UserDatabase.GetGroupRecord", true, NULL, flags);
|
||||
|
||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (r < 0 || !iterator->nss_covered)) {
|
||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (qr < 0 || !iterator->nss_covered)) {
|
||||
r = userdb_iterator_block_nss_systemd(iterator);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
setgrent();
|
||||
iterator->nss_iterating = true;
|
||||
} else if (r < 0)
|
||||
return r;
|
||||
}
|
||||
|
||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && (qr < 0 || !iterator->dropin_covered)) {
|
||||
r = conf_files_list_nulstr(
|
||||
&iterator->dropins,
|
||||
".group",
|
||||
NULL,
|
||||
CONF_FILES_REGULAR|CONF_FILES_FILTER_MASKED,
|
||||
USERDB_DROPIN_DIR_NULSTR("userdb"));
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to find group drop-ins, ignoring: %m");
|
||||
}
|
||||
|
||||
if (qr < 0 &&
|
||||
!iterator->nss_iterating &&
|
||||
strv_isempty(iterator->dropins))
|
||||
return qr;
|
||||
|
||||
*ret = TAKE_PTR(iterator);
|
||||
return 0;
|
||||
@ -977,6 +1085,36 @@ int groupdb_iterator_get(UserDBIterator *iterator, GroupRecord **ret) {
|
||||
endgrent();
|
||||
}
|
||||
|
||||
for (; iterator->dropins && iterator->dropins[iterator->current_dropin]; iterator->current_dropin++) {
|
||||
const char *i = iterator->dropins[iterator->current_dropin];
|
||||
_cleanup_free_ char *fn = NULL;
|
||||
gid_t gid;
|
||||
char *e;
|
||||
|
||||
r = path_extract_filename(i, &fn);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
e = endswith(fn, ".group");
|
||||
if (!e)
|
||||
continue;
|
||||
|
||||
*e = 0; /* Chop off suffix */
|
||||
|
||||
if (parse_gid(fn, &gid) < 0)
|
||||
continue;
|
||||
|
||||
r = dropin_group_record_by_gid(gid, i, iterator->flags, ret);
|
||||
if (r < 0) {
|
||||
log_debug_errno(r, "Failed to parse group record for GID " GID_FMT ", ignoring: %m", gid);
|
||||
continue;
|
||||
}
|
||||
|
||||
iterator->current_dropin++;
|
||||
iterator->n_found++;
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = userdb_process(iterator, NULL, ret, NULL, NULL);
|
||||
if (r < 0) {
|
||||
if (iterator->synthesize_root) {
|
||||
@ -999,10 +1137,23 @@ int groupdb_iterator_get(UserDBIterator *iterator, GroupRecord **ret) {
|
||||
return r;
|
||||
}
|
||||
|
||||
static void discover_membership_dropins(UserDBIterator *i, UserDBFlags flags) {
|
||||
int r;
|
||||
|
||||
r = conf_files_list_nulstr(
|
||||
&i->dropins,
|
||||
".membership",
|
||||
NULL,
|
||||
CONF_FILES_REGULAR|CONF_FILES_BASENAME|CONF_FILES_FILTER_MASKED,
|
||||
USERDB_DROPIN_DIR_NULSTR("userdb"));
|
||||
if (r < 0)
|
||||
log_debug_errno(r, "Failed to find membership drop-ins, ignoring: %m");
|
||||
}
|
||||
|
||||
int membershipdb_by_user(const char *name, UserDBFlags flags, UserDBIterator **ret) {
|
||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
|
||||
int r;
|
||||
int r, qr;
|
||||
|
||||
assert(ret);
|
||||
|
||||
@ -1018,34 +1169,37 @@ int membershipdb_by_user(const char *name, UserDBFlags flags, UserDBIterator **r
|
||||
if (!iterator)
|
||||
return -ENOMEM;
|
||||
|
||||
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, query, flags);
|
||||
if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_EXCLUDE_NSS))
|
||||
goto finish;
|
||||
|
||||
r = userdb_iterator_block_nss_systemd(iterator);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
iterator->filter_user_name = strdup(name);
|
||||
if (!iterator->filter_user_name)
|
||||
return -ENOMEM;
|
||||
|
||||
setgrent();
|
||||
iterator->nss_iterating = true;
|
||||
qr = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, query, flags);
|
||||
|
||||
r = 0;
|
||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (qr < 0 || !iterator->nss_covered)) {
|
||||
r = userdb_iterator_block_nss_systemd(iterator);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
finish:
|
||||
if (r >= 0)
|
||||
*ret = TAKE_PTR(iterator);
|
||||
return r;
|
||||
setgrent();
|
||||
iterator->nss_iterating = true;
|
||||
}
|
||||
|
||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && (qr < 0 || !iterator->dropin_covered))
|
||||
discover_membership_dropins(iterator, flags);
|
||||
|
||||
if (qr < 0 &&
|
||||
!iterator->nss_iterating &&
|
||||
strv_isempty(iterator->dropins))
|
||||
return qr;
|
||||
|
||||
*ret = TAKE_PTR(iterator);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int membershipdb_by_group(const char *name, UserDBFlags flags, UserDBIterator **ret) {
|
||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||
_cleanup_(json_variant_unrefp) JsonVariant *query = NULL;
|
||||
_cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
|
||||
int r;
|
||||
int r, qr;
|
||||
|
||||
assert(ret);
|
||||
|
||||
@ -1061,40 +1215,49 @@ int membershipdb_by_group(const char *name, UserDBFlags flags, UserDBIterator **
|
||||
if (!iterator)
|
||||
return -ENOMEM;
|
||||
|
||||
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, query, flags);
|
||||
if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_EXCLUDE_NSS))
|
||||
goto finish;
|
||||
iterator->filter_group_name = strdup(name);
|
||||
if (!iterator->filter_group_name)
|
||||
return -ENOMEM;
|
||||
|
||||
r = userdb_iterator_block_nss_systemd(iterator);
|
||||
if (r < 0)
|
||||
return r;
|
||||
qr = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, query, flags);
|
||||
|
||||
/* We ignore all errors here, since the group might be defined by a userdb native service, and we queried them already above. */
|
||||
(void) nss_group_record_by_name(name, false, &gr);
|
||||
if (gr) {
|
||||
iterator->members_of_group = strv_copy(gr->members);
|
||||
if (!iterator->members_of_group)
|
||||
return -ENOMEM;
|
||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (qr < 0 || !iterator->nss_covered)) {
|
||||
_cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
|
||||
|
||||
iterator->index_members_of_group = 0;
|
||||
r = userdb_iterator_block_nss_systemd(iterator);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
iterator->found_group_name = strdup(name);
|
||||
if (!iterator->found_group_name)
|
||||
return -ENOMEM;
|
||||
/* We ignore all errors here, since the group might be defined by a userdb native service, and we queried them already above. */
|
||||
(void) nss_group_record_by_name(name, false, &gr);
|
||||
if (gr) {
|
||||
iterator->members_of_group = strv_copy(gr->members);
|
||||
if (!iterator->members_of_group)
|
||||
return -ENOMEM;
|
||||
|
||||
iterator->index_members_of_group = 0;
|
||||
|
||||
iterator->found_group_name = strdup(name);
|
||||
if (!iterator->found_group_name)
|
||||
return -ENOMEM;
|
||||
}
|
||||
}
|
||||
|
||||
r = 0;
|
||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && (qr < 0 || !iterator->dropin_covered))
|
||||
discover_membership_dropins(iterator, flags);
|
||||
|
||||
finish:
|
||||
if (r >= 0)
|
||||
*ret = TAKE_PTR(iterator);
|
||||
if (qr < 0 &&
|
||||
strv_isempty(iterator->members_of_group) &&
|
||||
strv_isempty(iterator->dropins))
|
||||
return qr;
|
||||
|
||||
return r;
|
||||
*ret = TAKE_PTR(iterator);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int membershipdb_all(UserDBFlags flags, UserDBIterator **ret) {
|
||||
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
||||
int r;
|
||||
int r, qr;
|
||||
|
||||
assert(ret);
|
||||
|
||||
@ -1102,24 +1265,27 @@ int membershipdb_all(UserDBFlags flags, UserDBIterator **ret) {
|
||||
if (!iterator)
|
||||
return -ENOMEM;
|
||||
|
||||
r = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, NULL, flags);
|
||||
if ((r >= 0 && iterator->nss_covered) || FLAGS_SET(flags, USERDB_EXCLUDE_NSS))
|
||||
goto finish;
|
||||
qr = userdb_start_query(iterator, "io.systemd.UserDatabase.GetMemberships", true, NULL, flags);
|
||||
|
||||
r = userdb_iterator_block_nss_systemd(iterator);
|
||||
if (r < 0)
|
||||
return r;
|
||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_NSS) && (qr < 0 || !iterator->nss_covered)) {
|
||||
r = userdb_iterator_block_nss_systemd(iterator);
|
||||
if (r < 0)
|
||||
return r;
|
||||
|
||||
setgrent();
|
||||
iterator->nss_iterating = true;
|
||||
setgrent();
|
||||
iterator->nss_iterating = true;
|
||||
}
|
||||
|
||||
r = 0;
|
||||
if (!FLAGS_SET(flags, USERDB_EXCLUDE_DROPIN) && (qr < 0 || !iterator->dropin_covered))
|
||||
discover_membership_dropins(iterator, flags);
|
||||
|
||||
finish:
|
||||
if (r >= 0)
|
||||
*ret = TAKE_PTR(iterator);
|
||||
if (qr < 0 &&
|
||||
!iterator->nss_iterating &&
|
||||
strv_isempty(iterator->dropins))
|
||||
return qr;
|
||||
|
||||
return r;
|
||||
*ret = TAKE_PTR(iterator);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int membershipdb_iterator_get(
|
||||
@ -1205,6 +1371,48 @@ int membershipdb_iterator_get(
|
||||
iterator->found_group_name = mfree(iterator->found_group_name);
|
||||
}
|
||||
|
||||
for (; iterator->dropins && iterator->dropins[iterator->current_dropin]; iterator->current_dropin++) {
|
||||
const char *i = iterator->dropins[iterator->current_dropin], *e, *c;
|
||||
_cleanup_free_ char *un = NULL, *gn = NULL;
|
||||
|
||||
e = endswith(i, ".membership");
|
||||
if (!e)
|
||||
continue;
|
||||
|
||||
c = memchr(i, ':', e - i);
|
||||
if (!c)
|
||||
continue;
|
||||
|
||||
un = strndup(i, c - i);
|
||||
if (!un)
|
||||
return -ENOMEM;
|
||||
if (iterator->filter_user_name) {
|
||||
if (!streq(un, iterator->filter_user_name))
|
||||
continue;
|
||||
} else if (!valid_user_group_name(un, VALID_USER_RELAX))
|
||||
continue;
|
||||
|
||||
c++; /* skip over ':' */
|
||||
gn = strndup(c, e - c);
|
||||
if (!gn)
|
||||
return -ENOMEM;
|
||||
if (iterator->filter_group_name) {
|
||||
if (!streq(gn, iterator->filter_group_name))
|
||||
continue;
|
||||
} else if (!valid_user_group_name(gn, VALID_USER_RELAX))
|
||||
continue;
|
||||
|
||||
iterator->current_dropin++;
|
||||
iterator->n_found++;
|
||||
|
||||
if (ret_user)
|
||||
*ret_user = TAKE_PTR(un);
|
||||
if (ret_group)
|
||||
*ret_group = TAKE_PTR(gn);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
r = userdb_process(iterator, NULL, NULL, ret_user, ret_group);
|
||||
if (r < 0 && iterator->n_found > 0)
|
||||
return -ESRCH;
|
||||
|
@ -18,6 +18,7 @@ typedef enum UserDBFlags {
|
||||
/* The main sources */
|
||||
USERDB_EXCLUDE_NSS = 1 << 0, /* don't do client-side nor server-side NSS */
|
||||
USERDB_EXCLUDE_VARLINK = 1 << 1, /* don't talk to any varlink services */
|
||||
USERDB_EXCLUDE_DROPIN = 1 << 2, /* don't load drop-in user/group definitions */
|
||||
|
||||
/* Modifications */
|
||||
USERDB_SUPPRESS_SHADOW = 1 << 3, /* don't do client-side shadow calls (server side might happen though) */
|
||||
@ -26,9 +27,17 @@ typedef enum UserDBFlags {
|
||||
USERDB_DONT_SYNTHESIZE = 1 << 6, /* don't synthesize root/nobody */
|
||||
|
||||
/* Combinations */
|
||||
USERDB_NSS_ONLY = USERDB_EXCLUDE_VARLINK|USERDB_DONT_SYNTHESIZE,
|
||||
USERDB_NSS_ONLY = USERDB_EXCLUDE_VARLINK|USERDB_EXCLUDE_DROPIN|USERDB_DONT_SYNTHESIZE,
|
||||
} UserDBFlags;
|
||||
|
||||
/* Well-known errors we'll return here:
|
||||
*
|
||||
* -ESRCH: No such user/group
|
||||
* -ELINK: Varlink logic turned off (and no other source available)
|
||||
* -EOPNOTSUPP: Enumeration not supported
|
||||
* -ETIMEDOUT: Time-out
|
||||
*/
|
||||
|
||||
int userdb_by_name(const char *name, UserDBFlags flags, UserRecord **ret);
|
||||
int userdb_by_uid(uid_t uid, UserDBFlags flags, UserRecord **ret);
|
||||
int userdb_all(UserDBFlags flags, UserDBIterator **ret);
|
||||
|
Loading…
Reference in New Issue
Block a user