diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 02:25:50 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-05-06 02:25:50 +0000 |
commit | 19f4f86bfed21c5326ed2acebe1163f3a83e832b (patch) | |
tree | d59b9989ce55ed23693e80974d94c856f1c2c8b1 /src/nss-systemd/nss-systemd.c | |
parent | Initial commit. (diff) | |
download | systemd-19f4f86bfed21c5326ed2acebe1163f3a83e832b.tar.xz systemd-19f4f86bfed21c5326ed2acebe1163f3a83e832b.zip |
Adding upstream version 241.upstream/241upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'src/nss-systemd/nss-systemd.c')
-rw-r--r-- | src/nss-systemd/nss-systemd.c | 834 |
1 files changed, 834 insertions, 0 deletions
diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c new file mode 100644 index 0000000..f8db27a --- /dev/null +++ b/src/nss-systemd/nss-systemd.c @@ -0,0 +1,834 @@ +/* SPDX-License-Identifier: LGPL-2.1+ */ + +#include <nss.h> +#include <pthread.h> + +#include "sd-bus.h" + +#include "alloc-util.h" +#include "bus-common-errors.h" +#include "dirent-util.h" +#include "env-util.h" +#include "fd-util.h" +#include "fs-util.h" +#include "list.h" +#include "macro.h" +#include "nss-util.h" +#include "signal-util.h" +#include "stdio-util.h" +#include "string-util.h" +#include "user-util.h" +#include "util.h" + +#define DYNAMIC_USER_GECOS "Dynamic User" +#define DYNAMIC_USER_PASSWD "*" /* locked */ +#define DYNAMIC_USER_DIR "/" +#define DYNAMIC_USER_SHELL "/sbin/nologin" + +static const struct passwd root_passwd = { + .pw_name = (char*) "root", + .pw_passwd = (char*) "x", /* see shadow file */ + .pw_uid = 0, + .pw_gid = 0, + .pw_gecos = (char*) "Super User", + .pw_dir = (char*) "/root", + .pw_shell = (char*) "/bin/sh", +}; + +static const struct passwd nobody_passwd = { + .pw_name = (char*) NOBODY_USER_NAME, + .pw_passwd = (char*) "*", /* locked */ + .pw_uid = UID_NOBODY, + .pw_gid = GID_NOBODY, + .pw_gecos = (char*) "User Nobody", + .pw_dir = (char*) "/", + .pw_shell = (char*) "/sbin/nologin", +}; + +static const struct group root_group = { + .gr_name = (char*) "root", + .gr_gid = 0, + .gr_passwd = (char*) "x", /* see shadow file */ + .gr_mem = (char*[]) { NULL }, +}; + +static const struct group nobody_group = { + .gr_name = (char*) NOBODY_GROUP_NAME, + .gr_gid = GID_NOBODY, + .gr_passwd = (char*) "*", /* locked */ + .gr_mem = (char*[]) { NULL }, +}; + +typedef struct UserEntry UserEntry; +typedef struct GetentData GetentData; + +struct UserEntry { + uid_t id; + char *name; + + GetentData *data; + LIST_FIELDS(UserEntry, entries); +}; + +struct GetentData { + /* As explained in NOTES section of getpwent_r(3) as 'getpwent_r() is not really + * reentrant since it shares the reading position in the stream with all other threads', + * we need to protect the data in UserEntry from multithreaded programs which may call + * setpwent(), getpwent_r(), or endpwent() simultaneously. So, each function locks the + * data by using the mutex below. */ + pthread_mutex_t mutex; + + UserEntry *position; + LIST_HEAD(UserEntry, entries); +}; + +static GetentData getpwent_data = { PTHREAD_MUTEX_INITIALIZER, NULL, NULL }; +static GetentData getgrent_data = { PTHREAD_MUTEX_INITIALIZER, NULL, NULL }; + +NSS_GETPW_PROTOTYPES(systemd); +NSS_GETGR_PROTOTYPES(systemd); +enum nss_status _nss_systemd_endpwent(void) _public_; +enum nss_status _nss_systemd_setpwent(int stayopen) _public_; +enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) _public_; +enum nss_status _nss_systemd_endgrent(void) _public_; +enum nss_status _nss_systemd_setgrent(int stayopen) _public_; +enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size_t buflen, int *errnop) _public_; + +static int direct_lookup_name(const char *name, uid_t *ret) { + _cleanup_free_ char *s = NULL; + const char *path; + int r; + + assert(name); + + /* Normally, we go via the bus to resolve names. That has the benefit that it is available from any mount + * namespace and subject to proper authentication. However, there's one problem: if our module is called from + * dbus-daemon itself we really can't use D-Bus to communicate. In this case, resort to a client-side hack, + * and look for the dynamic names directly. This is pretty ugly, but breaks the cyclic dependency. */ + + path = strjoina("/run/systemd/dynamic-uid/direct:", name); + r = readlink_malloc(path, &s); + if (r < 0) + return r; + + return parse_uid(s, ret); +} + +static int direct_lookup_uid(uid_t uid, char **ret) { + char path[STRLEN("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1], *s; + int r; + + xsprintf(path, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid); + + r = readlink_malloc(path, &s); + if (r < 0) + return r; + if (!valid_user_group_name(s)) { /* extra safety check */ + free(s); + return -EINVAL; + } + + *ret = s; + return 0; +} + +enum nss_status _nss_systemd_getpwnam_r( + const char *name, + struct passwd *pwd, + char *buffer, size_t buflen, + int *errnop) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + uint32_t translated; + size_t l; + int bypass, r; + + PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(name); + assert(pwd); + + /* If the username is not valid, then we don't know it. Ideally libc would filter these for us anyway. We don't + * generate EINVAL here, because it isn't really out business to complain about invalid user names. */ + if (!valid_user_group_name(name)) + return NSS_STATUS_NOTFOUND; + + /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */ + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) { + if (streq(name, root_passwd.pw_name)) { + *pwd = root_passwd; + return NSS_STATUS_SUCCESS; + } + if (synthesize_nobody() && + streq(name, nobody_passwd.pw_name)) { + *pwd = nobody_passwd; + return NSS_STATUS_SUCCESS; + } + } + + /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */ + if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) + return NSS_STATUS_NOTFOUND; + + bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS"); + if (bypass <= 0) { + r = sd_bus_open_system(&bus); + if (r < 0) + bypass = 1; + } + + if (bypass > 0) { + r = direct_lookup_name(name, (uid_t*) &translated); + if (r == -ENOENT) + return NSS_STATUS_NOTFOUND; + if (r < 0) + goto fail; + } else { + r = sd_bus_call_method(bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "LookupDynamicUserByName", + &error, + &reply, + "s", + name); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER)) + return NSS_STATUS_NOTFOUND; + + goto fail; + } + + r = sd_bus_message_read(reply, "u", &translated); + if (r < 0) + goto fail; + } + + l = strlen(name); + if (buflen < l+1) { + UNPROTECT_ERRNO; + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } + + memcpy(buffer, name, l+1); + + pwd->pw_name = buffer; + pwd->pw_uid = (uid_t) translated; + pwd->pw_gid = (uid_t) translated; + pwd->pw_gecos = (char*) DYNAMIC_USER_GECOS; + pwd->pw_passwd = (char*) DYNAMIC_USER_PASSWD; + pwd->pw_dir = (char*) DYNAMIC_USER_DIR; + pwd->pw_shell = (char*) DYNAMIC_USER_SHELL; + + return NSS_STATUS_SUCCESS; + +fail: + UNPROTECT_ERRNO; + *errnop = -r; + return NSS_STATUS_UNAVAIL; +} + +enum nss_status _nss_systemd_getpwuid_r( + uid_t uid, + struct passwd *pwd, + char *buffer, size_t buflen, + int *errnop) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_free_ char *direct = NULL; + const char *translated; + size_t l; + int bypass, r; + + PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + if (!uid_is_valid(uid)) + return NSS_STATUS_NOTFOUND; + + /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */ + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) { + if (uid == root_passwd.pw_uid) { + *pwd = root_passwd; + return NSS_STATUS_SUCCESS; + } + if (synthesize_nobody() && + uid == nobody_passwd.pw_uid) { + *pwd = nobody_passwd; + return NSS_STATUS_SUCCESS; + } + } + + if (!uid_is_dynamic(uid)) + return NSS_STATUS_NOTFOUND; + + if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) + return NSS_STATUS_NOTFOUND; + + bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS"); + if (bypass <= 0) { + r = sd_bus_open_system(&bus); + if (r < 0) + bypass = 1; + } + + if (bypass > 0) { + r = direct_lookup_uid(uid, &direct); + if (r == -ENOENT) + return NSS_STATUS_NOTFOUND; + if (r < 0) + goto fail; + + translated = direct; + + } else { + r = sd_bus_call_method(bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "LookupDynamicUserByUID", + &error, + &reply, + "u", + (uint32_t) uid); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER)) + return NSS_STATUS_NOTFOUND; + + goto fail; + } + + r = sd_bus_message_read(reply, "s", &translated); + if (r < 0) + goto fail; + } + + l = strlen(translated) + 1; + if (buflen < l) { + UNPROTECT_ERRNO; + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } + + memcpy(buffer, translated, l); + + pwd->pw_name = buffer; + pwd->pw_uid = uid; + pwd->pw_gid = uid; + pwd->pw_gecos = (char*) DYNAMIC_USER_GECOS; + pwd->pw_passwd = (char*) DYNAMIC_USER_PASSWD; + pwd->pw_dir = (char*) DYNAMIC_USER_DIR; + pwd->pw_shell = (char*) DYNAMIC_USER_SHELL; + + return NSS_STATUS_SUCCESS; + +fail: + UNPROTECT_ERRNO; + *errnop = -r; + return NSS_STATUS_UNAVAIL; +} + +#pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess" + +enum nss_status _nss_systemd_getgrnam_r( + const char *name, + struct group *gr, + char *buffer, size_t buflen, + int *errnop) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + uint32_t translated; + size_t l; + int bypass, r; + + PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(name); + assert(gr); + + if (!valid_user_group_name(name)) + return NSS_STATUS_NOTFOUND; + + /* Synthesize records for root and nobody, in case they are missing form /etc/group */ + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) { + if (streq(name, root_group.gr_name)) { + *gr = root_group; + return NSS_STATUS_SUCCESS; + } + if (synthesize_nobody() && + streq(name, nobody_group.gr_name)) { + *gr = nobody_group; + return NSS_STATUS_SUCCESS; + } + } + + if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) + return NSS_STATUS_NOTFOUND; + + bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS"); + if (bypass <= 0) { + r = sd_bus_open_system(&bus); + if (r < 0) + bypass = 1; + } + + if (bypass > 0) { + r = direct_lookup_name(name, (uid_t*) &translated); + if (r == -ENOENT) + return NSS_STATUS_NOTFOUND; + if (r < 0) + goto fail; + } else { + r = sd_bus_call_method(bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "LookupDynamicUserByName", + &error, + &reply, + "s", + name); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER)) + return NSS_STATUS_NOTFOUND; + + goto fail; + } + + r = sd_bus_message_read(reply, "u", &translated); + if (r < 0) + goto fail; + } + + l = sizeof(char*) + strlen(name) + 1; + if (buflen < l) { + UNPROTECT_ERRNO; + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } + + memzero(buffer, sizeof(char*)); + strcpy(buffer + sizeof(char*), name); + + gr->gr_name = buffer + sizeof(char*); + gr->gr_gid = (gid_t) translated; + gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD; + gr->gr_mem = (char**) buffer; + + return NSS_STATUS_SUCCESS; + +fail: + UNPROTECT_ERRNO; + *errnop = -r; + return NSS_STATUS_UNAVAIL; +} + +enum nss_status _nss_systemd_getgrgid_r( + gid_t gid, + struct group *gr, + char *buffer, size_t buflen, + int *errnop) { + + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + _cleanup_free_ char *direct = NULL; + const char *translated; + size_t l; + int bypass, r; + + PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + if (!gid_is_valid(gid)) + return NSS_STATUS_NOTFOUND; + + /* Synthesize records for root and nobody, in case they are missing from /etc/group */ + if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) { + if (gid == root_group.gr_gid) { + *gr = root_group; + return NSS_STATUS_SUCCESS; + } + if (synthesize_nobody() && + gid == nobody_group.gr_gid) { + *gr = nobody_group; + return NSS_STATUS_SUCCESS; + } + } + + if (!gid_is_dynamic(gid)) + return NSS_STATUS_NOTFOUND; + + if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) + return NSS_STATUS_NOTFOUND; + + bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS"); + if (bypass <= 0) { + r = sd_bus_open_system(&bus); + if (r < 0) + bypass = 1; + } + + if (bypass > 0) { + r = direct_lookup_uid(gid, &direct); + if (r == -ENOENT) + return NSS_STATUS_NOTFOUND; + if (r < 0) + goto fail; + + translated = direct; + + } else { + r = sd_bus_call_method(bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "LookupDynamicUserByUID", + &error, + &reply, + "u", + (uint32_t) gid); + if (r < 0) { + if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER)) + return NSS_STATUS_NOTFOUND; + + goto fail; + } + + r = sd_bus_message_read(reply, "s", &translated); + if (r < 0) + goto fail; + } + + l = sizeof(char*) + strlen(translated) + 1; + if (buflen < l) { + UNPROTECT_ERRNO; + *errnop = ERANGE; + return NSS_STATUS_TRYAGAIN; + } + + memzero(buffer, sizeof(char*)); + strcpy(buffer + sizeof(char*), translated); + + gr->gr_name = buffer + sizeof(char*); + gr->gr_gid = gid; + gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD; + gr->gr_mem = (char**) buffer; + + return NSS_STATUS_SUCCESS; + +fail: + UNPROTECT_ERRNO; + *errnop = -r; + return NSS_STATUS_UNAVAIL; +} + +static void user_entry_free(UserEntry *p) { + if (!p) + return; + + if (p->data) + LIST_REMOVE(entries, p->data->entries, p); + + free(p->name); + free(p); +} + +static int user_entry_add(GetentData *data, const char *name, uid_t id) { + UserEntry *p; + + assert(data); + + /* This happens when User= or Group= already exists statically. */ + if (!uid_is_dynamic(id)) + return -EINVAL; + + p = new0(UserEntry, 1); + if (!p) + return -ENOMEM; + + p->name = strdup(name); + if (!p->name) { + free(p); + return -ENOMEM; + } + p->id = id; + p->data = data; + + LIST_PREPEND(entries, data->entries, p); + + return 0; +} + +static void systemd_endent(GetentData *data) { + UserEntry *p; + + assert(data); + + while ((p = data->entries)) + user_entry_free(p); + + data->position = NULL; +} + +static enum nss_status nss_systemd_endent(GetentData *p) { + PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert_se(pthread_mutex_lock(&p->mutex) == 0); + systemd_endent(p); + assert_se(pthread_mutex_unlock(&p->mutex) == 0); + + return NSS_STATUS_SUCCESS; +} + +enum nss_status _nss_systemd_endpwent(void) { + return nss_systemd_endent(&getpwent_data); +} + +enum nss_status _nss_systemd_endgrent(void) { + return nss_systemd_endent(&getgrent_data); +} + +static int direct_enumeration(GetentData *p) { + _cleanup_closedir_ DIR *d = NULL; + struct dirent *de; + int r; + + assert(p); + + d = opendir("/run/systemd/dynamic-uid/"); + if (!d) + return -errno; + + FOREACH_DIRENT(de, d, return -errno) { + _cleanup_free_ char *name = NULL; + uid_t uid, verified; + + if (!dirent_is_file(de)) + continue; + + r = parse_uid(de->d_name, &uid); + if (r < 0) + continue; + + r = direct_lookup_uid(uid, &name); + if (r == -ENOMEM) + return r; + if (r < 0) + continue; + + r = direct_lookup_name(name, &verified); + if (r < 0) + continue; + + if (uid != verified) + continue; + + r = user_entry_add(p, name, uid); + if (r == -ENOMEM) + return r; + if (r < 0) + continue; + } + + return 0; +} + +static enum nss_status systemd_setent(GetentData *p) { + _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; + _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL; + _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; + const char *name; + uid_t id; + int bypass, r; + + PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(p); + + assert_se(pthread_mutex_lock(&p->mutex) == 0); + + systemd_endent(p); + + if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) + goto finish; + + bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS"); + + if (bypass <= 0) { + r = sd_bus_open_system(&bus); + if (r < 0) + bypass = 1; + } + + if (bypass > 0) { + r = direct_enumeration(p); + if (r < 0) + goto fail; + + goto finish; + } + + r = sd_bus_call_method(bus, + "org.freedesktop.systemd1", + "/org/freedesktop/systemd1", + "org.freedesktop.systemd1.Manager", + "GetDynamicUsers", + &error, + &reply, + NULL); + if (r < 0) + goto fail; + + r = sd_bus_message_enter_container(reply, 'a', "(us)"); + if (r < 0) + goto fail; + + while ((r = sd_bus_message_read(reply, "(us)", &id, &name)) > 0) { + r = user_entry_add(p, name, id); + if (r == -ENOMEM) + goto fail; + if (r < 0) + continue; + } + if (r < 0) + goto fail; + + r = sd_bus_message_exit_container(reply); + if (r < 0) + goto fail; + +finish: + p->position = p->entries; + assert_se(pthread_mutex_unlock(&p->mutex) == 0); + + return NSS_STATUS_SUCCESS; + +fail: + systemd_endent(p); + assert_se(pthread_mutex_unlock(&p->mutex) == 0); + + return NSS_STATUS_UNAVAIL; +} + +enum nss_status _nss_systemd_setpwent(int stayopen) { + return systemd_setent(&getpwent_data); +} + +enum nss_status _nss_systemd_setgrent(int stayopen) { + return systemd_setent(&getgrent_data); +} + +enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) { + enum nss_status ret; + UserEntry *p; + size_t len; + + PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(result); + assert(buffer); + assert(errnop); + + assert_se(pthread_mutex_lock(&getpwent_data.mutex) == 0); + + LIST_FOREACH(entries, p, getpwent_data.position) { + len = strlen(p->name) + 1; + if (buflen < len) { + UNPROTECT_ERRNO; + *errnop = ERANGE; + ret = NSS_STATUS_TRYAGAIN; + goto finalize; + } + + memcpy(buffer, p->name, len); + + result->pw_name = buffer; + result->pw_uid = p->id; + result->pw_gid = p->id; + result->pw_gecos = (char*) DYNAMIC_USER_GECOS; + result->pw_passwd = (char*) DYNAMIC_USER_PASSWD; + result->pw_dir = (char*) DYNAMIC_USER_DIR; + result->pw_shell = (char*) DYNAMIC_USER_SHELL; + break; + } + if (!p) { + ret = NSS_STATUS_NOTFOUND; + goto finalize; + } + + /* On success, step to the next entry. */ + p = p->entries_next; + ret = NSS_STATUS_SUCCESS; + +finalize: + /* Save position for the next call. */ + getpwent_data.position = p; + + assert_se(pthread_mutex_unlock(&getpwent_data.mutex) == 0); + + return ret; +} + +enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size_t buflen, int *errnop) { + enum nss_status ret; + UserEntry *p; + size_t len; + + PROTECT_ERRNO; + BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); + + assert(result); + assert(buffer); + assert(errnop); + + assert_se(pthread_mutex_lock(&getgrent_data.mutex) == 0); + + LIST_FOREACH(entries, p, getgrent_data.position) { + len = sizeof(char*) + strlen(p->name) + 1; + if (buflen < len) { + UNPROTECT_ERRNO; + *errnop = ERANGE; + ret = NSS_STATUS_TRYAGAIN; + goto finalize; + } + + memzero(buffer, sizeof(char*)); + strcpy(buffer + sizeof(char*), p->name); + + result->gr_name = buffer + sizeof(char*); + result->gr_gid = p->id; + result->gr_passwd = (char*) DYNAMIC_USER_PASSWD; + result->gr_mem = (char**) buffer; + break; + } + if (!p) { + ret = NSS_STATUS_NOTFOUND; + goto finalize; + } + + /* On success, step to the next entry. */ + p = p->entries_next; + ret = NSS_STATUS_SUCCESS; + +finalize: + /* Save position for the next call. */ + getgrent_data.position = p; + + assert_se(pthread_mutex_unlock(&getgrent_data.mutex) == 0); + + return ret; +} |