2020-11-09 05:23:58 +01:00
|
|
|
/* SPDX-License-Identifier: LGPL-2.1-or-later */
|
2016-07-14 19:19:49 +02:00
|
|
|
|
|
|
|
#include <nss.h>
|
2018-03-21 05:39:03 +01:00
|
|
|
#include <pthread.h>
|
2016-07-14 19:19:49 +02:00
|
|
|
|
|
|
|
#include "env-util.h"
|
2019-07-04 18:31:11 +02:00
|
|
|
#include "errno-util.h"
|
2018-03-21 05:39:03 +01:00
|
|
|
#include "fd-util.h"
|
2020-12-10 12:45:48 +01:00
|
|
|
#include "log.h"
|
2016-07-14 19:19:49 +02:00
|
|
|
#include "macro.h"
|
2020-06-04 11:46:36 +02:00
|
|
|
#include "nss-systemd.h"
|
2016-07-14 19:19:49 +02:00
|
|
|
#include "nss-util.h"
|
2020-03-28 13:26:25 +01:00
|
|
|
#include "pthread-util.h"
|
2016-07-14 19:19:49 +02:00
|
|
|
#include "signal-util.h"
|
2019-07-04 18:31:11 +02:00
|
|
|
#include "strv.h"
|
2020-08-30 21:25:12 +02:00
|
|
|
#include "user-record-nss.h"
|
2016-07-14 19:19:49 +02:00
|
|
|
#include "user-util.h"
|
2019-07-04 18:31:11 +02:00
|
|
|
#include "userdb-glue.h"
|
|
|
|
#include "userdb.h"
|
2018-03-06 07:39:26 +01:00
|
|
|
|
2016-07-27 13:14:01 +02:00
|
|
|
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 */
|
2017-12-04 17:06:56 +01:00
|
|
|
.pw_uid = UID_NOBODY,
|
|
|
|
.pw_gid = GID_NOBODY,
|
2016-07-27 13:14:01 +02:00
|
|
|
.pw_gecos = (char*) "User Nobody",
|
|
|
|
.pw_dir = (char*) "/",
|
2019-07-18 01:24:00 +02:00
|
|
|
.pw_shell = (char*) NOLOGIN,
|
2016-07-27 13:14:01 +02:00
|
|
|
};
|
|
|
|
|
|
|
|
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,
|
2017-12-04 17:06:56 +01:00
|
|
|
.gr_gid = GID_NOBODY,
|
2016-07-27 13:14:01 +02:00
|
|
|
.gr_passwd = (char*) "*", /* locked */
|
|
|
|
.gr_mem = (char*[]) { NULL },
|
|
|
|
};
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
typedef 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
|
|
|
|
* UserDBIterator 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;
|
|
|
|
UserDBIterator *iterator;
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
/* Applies to group iterations only: true while we iterate over groups defined through NSS, false
|
|
|
|
* otherwise. */
|
|
|
|
bool by_membership;
|
|
|
|
} GetentData;
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
static GetentData getpwent_data = {
|
|
|
|
.mutex = PTHREAD_MUTEX_INITIALIZER
|
2018-03-21 05:39:03 +01:00
|
|
|
};
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
static GetentData getgrent_data = {
|
|
|
|
.mutex = PTHREAD_MUTEX_INITIALIZER
|
2018-03-21 05:39:03 +01:00
|
|
|
};
|
|
|
|
|
2020-12-10 12:45:48 +01:00
|
|
|
static void setup_logging(void) {
|
|
|
|
/* We need a dummy function because log_parse_environment is a macro. */
|
|
|
|
log_parse_environment();
|
|
|
|
}
|
|
|
|
|
|
|
|
static void setup_logging_once(void) {
|
|
|
|
static pthread_once_t once = PTHREAD_ONCE_INIT;
|
|
|
|
assert_se(pthread_once(&once, setup_logging) == 0);
|
|
|
|
}
|
|
|
|
|
|
|
|
#define NSS_ENTRYPOINT_BEGIN \
|
|
|
|
BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); \
|
|
|
|
setup_logging_once()
|
|
|
|
|
2016-07-14 19:19:49 +02:00
|
|
|
NSS_GETPW_PROTOTYPES(systemd);
|
|
|
|
NSS_GETGR_PROTOTYPES(systemd);
|
2019-07-04 18:31:11 +02:00
|
|
|
NSS_PWENT_PROTOTYPES(systemd);
|
|
|
|
NSS_GRENT_PROTOTYPES(systemd);
|
|
|
|
NSS_INITGROUPS_PROTOTYPE(systemd);
|
2016-08-02 12:28:51 +02:00
|
|
|
|
2016-07-14 19:19:49 +02:00
|
|
|
enum nss_status _nss_systemd_getpwnam_r(
|
|
|
|
const char *name,
|
|
|
|
struct passwd *pwd,
|
|
|
|
char *buffer, size_t buflen,
|
|
|
|
int *errnop) {
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
enum nss_status status;
|
|
|
|
int e;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
2018-07-15 16:00:00 +02:00
|
|
|
PROTECT_ERRNO;
|
2020-12-10 12:45:48 +01:00
|
|
|
NSS_ENTRYPOINT_BEGIN;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
|
|
|
assert(name);
|
|
|
|
assert(pwd);
|
2019-07-04 18:31:11 +02:00
|
|
|
assert(errnop);
|
2016-07-14 19:19:49 +02:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
/* 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. */
|
2020-04-04 12:23:02 +02:00
|
|
|
if (!valid_user_group_name(name, VALID_USER_RELAX))
|
2018-07-15 16:00:00 +02:00
|
|
|
return NSS_STATUS_NOTFOUND;
|
2016-07-27 13:14:01 +02:00
|
|
|
|
|
|
|
/* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
|
2017-09-14 09:20:27 +02:00
|
|
|
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
|
2019-07-04 18:31:11 +02:00
|
|
|
|
2017-09-14 06:20:39 +02:00
|
|
|
if (streq(name, root_passwd.pw_name)) {
|
|
|
|
*pwd = root_passwd;
|
|
|
|
return NSS_STATUS_SUCCESS;
|
|
|
|
}
|
2016-07-14 19:19:49 +02:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
if (streq(name, nobody_passwd.pw_name)) {
|
|
|
|
if (!synthesize_nobody())
|
2018-07-15 16:00:00 +02:00
|
|
|
return NSS_STATUS_NOTFOUND;
|
2016-08-02 12:28:51 +02:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
*pwd = nobody_passwd;
|
|
|
|
return NSS_STATUS_SUCCESS;
|
2016-08-02 12:28:51 +02:00
|
|
|
}
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
} else if (STR_IN_SET(name, root_passwd.pw_name, nobody_passwd.pw_name))
|
|
|
|
return NSS_STATUS_NOTFOUND;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
status = userdb_getpwnam(name, pwd, buffer, buflen, &e);
|
|
|
|
if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
|
2019-01-18 20:13:55 +01:00
|
|
|
UNPROTECT_ERRNO;
|
2019-07-04 18:31:11 +02:00
|
|
|
*errnop = e;
|
|
|
|
return status;
|
2016-07-14 19:19:49 +02:00
|
|
|
}
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
return status;
|
2016-07-14 19:19:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
enum nss_status _nss_systemd_getpwuid_r(
|
|
|
|
uid_t uid,
|
|
|
|
struct passwd *pwd,
|
|
|
|
char *buffer, size_t buflen,
|
|
|
|
int *errnop) {
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
enum nss_status status;
|
|
|
|
int e;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
2018-07-15 16:00:00 +02:00
|
|
|
PROTECT_ERRNO;
|
2020-12-10 12:45:48 +01:00
|
|
|
NSS_ENTRYPOINT_BEGIN;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
assert(pwd);
|
|
|
|
assert(errnop);
|
|
|
|
|
nss: when we encounter an invalid user/group name or UID/GID, don't return EINVAL
It's not our business to validate invalid user/group names or UID/GID.
Ideally, libc would filter these out, but they don't, hence we have to
filter, but let's not propagate this as error, but simply as "not found"
to the caller.
User name rules are pretty vaguely defined, and the rules defined by
POSIX clash with reality quite heavily (for example, utmp doesn't offer
enough room for user name length, and /usr/bin/chown permits separating
user/group names by a single dot, even though POSIX allows dots being
used in user/group names themselves.) We enforce stricter rules than
POSIX for good reason, and hence in doing so we should not categorically
return EINVAL on stuff we don't consider valid, but other components
might.
Fixes: #4983
2016-12-27 17:59:38 +01:00
|
|
|
if (!uid_is_valid(uid))
|
2018-07-15 16:00:00 +02:00
|
|
|
return NSS_STATUS_NOTFOUND;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
2016-07-27 13:14:01 +02:00
|
|
|
/* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
|
2017-09-14 09:20:27 +02:00
|
|
|
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
|
2019-07-04 18:31:11 +02:00
|
|
|
|
2017-09-14 06:20:39 +02:00
|
|
|
if (uid == root_passwd.pw_uid) {
|
|
|
|
*pwd = root_passwd;
|
|
|
|
return NSS_STATUS_SUCCESS;
|
|
|
|
}
|
2016-07-27 13:14:01 +02:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
if (uid == nobody_passwd.pw_uid) {
|
|
|
|
if (!synthesize_nobody())
|
2018-07-15 16:00:00 +02:00
|
|
|
return NSS_STATUS_NOTFOUND;
|
2016-08-02 12:28:51 +02:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
*pwd = nobody_passwd;
|
|
|
|
return NSS_STATUS_SUCCESS;
|
2016-08-02 12:28:51 +02:00
|
|
|
}
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
} else if (uid == root_passwd.pw_uid || uid == nobody_passwd.pw_uid)
|
|
|
|
return NSS_STATUS_NOTFOUND;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
status = userdb_getpwuid(uid, pwd, buffer, buflen, &e);
|
|
|
|
if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
|
2019-01-18 20:13:55 +01:00
|
|
|
UNPROTECT_ERRNO;
|
2019-07-04 18:31:11 +02:00
|
|
|
*errnop = e;
|
|
|
|
return status;
|
2016-07-14 19:19:49 +02:00
|
|
|
}
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
return status;
|
2016-07-14 19:19:49 +02:00
|
|
|
}
|
|
|
|
|
2018-02-05 11:07:40 +01:00
|
|
|
#pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
|
|
|
|
|
2016-07-14 19:19:49 +02:00
|
|
|
enum nss_status _nss_systemd_getgrnam_r(
|
|
|
|
const char *name,
|
|
|
|
struct group *gr,
|
|
|
|
char *buffer, size_t buflen,
|
|
|
|
int *errnop) {
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
enum nss_status status;
|
|
|
|
int e;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
2018-07-15 16:00:00 +02:00
|
|
|
PROTECT_ERRNO;
|
2020-12-10 12:45:48 +01:00
|
|
|
NSS_ENTRYPOINT_BEGIN;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
|
|
|
assert(name);
|
|
|
|
assert(gr);
|
2019-07-04 18:31:11 +02:00
|
|
|
assert(errnop);
|
2016-07-14 19:19:49 +02:00
|
|
|
|
2020-04-04 12:23:02 +02:00
|
|
|
if (!valid_user_group_name(name, VALID_USER_RELAX))
|
2018-07-15 16:00:00 +02:00
|
|
|
return NSS_STATUS_NOTFOUND;
|
2016-07-27 13:14:01 +02:00
|
|
|
|
2020-03-01 12:24:25 +01:00
|
|
|
/* Synthesize records for root and nobody, in case they are missing from /etc/group */
|
2017-09-14 09:20:27 +02:00
|
|
|
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
|
2019-07-04 18:31:11 +02:00
|
|
|
|
2017-09-14 06:20:39 +02:00
|
|
|
if (streq(name, root_group.gr_name)) {
|
|
|
|
*gr = root_group;
|
|
|
|
return NSS_STATUS_SUCCESS;
|
|
|
|
}
|
2016-07-14 19:19:49 +02:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
if (streq(name, nobody_group.gr_name)) {
|
|
|
|
if (!synthesize_nobody())
|
2018-07-15 16:00:00 +02:00
|
|
|
return NSS_STATUS_NOTFOUND;
|
2016-08-02 12:28:51 +02:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
*gr = nobody_group;
|
|
|
|
return NSS_STATUS_SUCCESS;
|
2016-08-02 12:28:51 +02:00
|
|
|
}
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
} else if (STR_IN_SET(name, root_group.gr_name, nobody_group.gr_name))
|
|
|
|
return NSS_STATUS_NOTFOUND;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
status = userdb_getgrnam(name, gr, buffer, buflen, &e);
|
|
|
|
if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
|
2019-01-18 20:13:55 +01:00
|
|
|
UNPROTECT_ERRNO;
|
2019-07-04 18:31:11 +02:00
|
|
|
*errnop = e;
|
|
|
|
return status;
|
2016-07-14 19:19:49 +02:00
|
|
|
}
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
return status;
|
2016-07-14 19:19:49 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
enum nss_status _nss_systemd_getgrgid_r(
|
|
|
|
gid_t gid,
|
|
|
|
struct group *gr,
|
|
|
|
char *buffer, size_t buflen,
|
|
|
|
int *errnop) {
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
enum nss_status status;
|
|
|
|
int e;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
2018-07-15 16:00:00 +02:00
|
|
|
PROTECT_ERRNO;
|
2020-12-10 12:45:48 +01:00
|
|
|
NSS_ENTRYPOINT_BEGIN;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
assert(gr);
|
|
|
|
assert(errnop);
|
|
|
|
|
nss: when we encounter an invalid user/group name or UID/GID, don't return EINVAL
It's not our business to validate invalid user/group names or UID/GID.
Ideally, libc would filter these out, but they don't, hence we have to
filter, but let's not propagate this as error, but simply as "not found"
to the caller.
User name rules are pretty vaguely defined, and the rules defined by
POSIX clash with reality quite heavily (for example, utmp doesn't offer
enough room for user name length, and /usr/bin/chown permits separating
user/group names by a single dot, even though POSIX allows dots being
used in user/group names themselves.) We enforce stricter rules than
POSIX for good reason, and hence in doing so we should not categorically
return EINVAL on stuff we don't consider valid, but other components
might.
Fixes: #4983
2016-12-27 17:59:38 +01:00
|
|
|
if (!gid_is_valid(gid))
|
2018-07-15 16:00:00 +02:00
|
|
|
return NSS_STATUS_NOTFOUND;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
2016-07-27 13:14:01 +02:00
|
|
|
/* Synthesize records for root and nobody, in case they are missing from /etc/group */
|
2017-09-14 09:20:27 +02:00
|
|
|
if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
|
2019-07-04 18:31:11 +02:00
|
|
|
|
2017-09-14 06:20:39 +02:00
|
|
|
if (gid == root_group.gr_gid) {
|
|
|
|
*gr = root_group;
|
|
|
|
return NSS_STATUS_SUCCESS;
|
|
|
|
}
|
2016-07-14 19:19:49 +02:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
if (gid == nobody_group.gr_gid) {
|
|
|
|
if (!synthesize_nobody())
|
2018-07-15 16:00:00 +02:00
|
|
|
return NSS_STATUS_NOTFOUND;
|
2016-08-02 12:28:51 +02:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
*gr = nobody_group;
|
|
|
|
return NSS_STATUS_SUCCESS;
|
2016-08-02 12:28:51 +02:00
|
|
|
}
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
} else if (gid == root_group.gr_gid || gid == nobody_group.gr_gid)
|
|
|
|
return NSS_STATUS_NOTFOUND;
|
2016-07-14 19:19:49 +02:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
status = userdb_getgrgid(gid, gr, buffer, buflen, &e);
|
|
|
|
if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) {
|
2019-01-18 20:13:55 +01:00
|
|
|
UNPROTECT_ERRNO;
|
2019-07-04 18:31:11 +02:00
|
|
|
*errnop = e;
|
|
|
|
return status;
|
2016-07-14 19:19:49 +02:00
|
|
|
}
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
return status;
|
2018-03-21 05:39:03 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
static enum nss_status nss_systemd_endent(GetentData *p) {
|
2018-07-15 16:00:00 +02:00
|
|
|
PROTECT_ERRNO;
|
2020-12-10 12:45:48 +01:00
|
|
|
NSS_ENTRYPOINT_BEGIN;
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
assert(p);
|
|
|
|
|
2020-03-28 13:26:25 +01:00
|
|
|
_cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL;
|
|
|
|
_l = pthread_mutex_lock_assert(&p->mutex);
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
p->iterator = userdb_iterator_free(p->iterator);
|
|
|
|
p->by_membership = false;
|
2018-03-21 05:39:03 +01:00
|
|
|
|
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
enum nss_status _nss_systemd_setpwent(int stayopen) {
|
|
|
|
PROTECT_ERRNO;
|
2020-12-10 12:45:48 +01:00
|
|
|
NSS_ENTRYPOINT_BEGIN;
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2020-06-04 11:46:36 +02:00
|
|
|
if (_nss_systemd_is_blocked())
|
2019-07-04 18:31:11 +02:00
|
|
|
return NSS_STATUS_NOTFOUND;
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2020-03-28 13:26:25 +01:00
|
|
|
_cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
_l = pthread_mutex_lock_assert(&getpwent_data.mutex);
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
getpwent_data.iterator = userdb_iterator_free(getpwent_data.iterator);
|
|
|
|
getpwent_data.by_membership = false;
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2020-04-23 09:55:06 +02:00
|
|
|
/* Don't synthesize root/nobody when iterating. Let nss-files take care of that. If the two records
|
|
|
|
* are missing there, then that's fine, after all getpwent() is known to be possibly incomplete
|
|
|
|
* (think: LDAP/NIS type situations), and our synthesizing of root/nobody is a robustness fallback
|
|
|
|
* only, which matters for getpwnam()/getpwuid() primarily, which are the main NSS entrypoints to the
|
|
|
|
* user database. */
|
|
|
|
r = userdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE, &getpwent_data.iterator);
|
2020-03-28 13:26:25 +01:00
|
|
|
return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
|
2019-07-04 18:31:11 +02:00
|
|
|
}
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
enum nss_status _nss_systemd_setgrent(int stayopen) {
|
|
|
|
PROTECT_ERRNO;
|
2020-12-10 12:45:48 +01:00
|
|
|
NSS_ENTRYPOINT_BEGIN;
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2020-06-04 11:46:36 +02:00
|
|
|
if (_nss_systemd_is_blocked())
|
2019-07-04 18:31:11 +02:00
|
|
|
return NSS_STATUS_NOTFOUND;
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2020-03-28 13:26:25 +01:00
|
|
|
_cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL;
|
|
|
|
int r;
|
|
|
|
|
|
|
|
_l = pthread_mutex_lock_assert(&getgrent_data.mutex);
|
2019-07-04 18:31:11 +02:00
|
|
|
|
|
|
|
getgrent_data.iterator = userdb_iterator_free(getgrent_data.iterator);
|
|
|
|
getpwent_data.by_membership = false;
|
|
|
|
|
2020-04-23 09:55:06 +02:00
|
|
|
/* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE here */
|
|
|
|
r = groupdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE, &getgrent_data.iterator);
|
2020-03-28 13:26:25 +01:00
|
|
|
return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS;
|
2018-03-21 05:39:03 +01:00
|
|
|
}
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
enum nss_status _nss_systemd_getpwent_r(
|
|
|
|
struct passwd *result,
|
|
|
|
char *buffer, size_t buflen,
|
|
|
|
int *errnop) {
|
|
|
|
|
|
|
|
_cleanup_(user_record_unrefp) UserRecord *ur = NULL;
|
|
|
|
int r;
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2018-07-15 16:00:00 +02:00
|
|
|
PROTECT_ERRNO;
|
2020-12-10 12:45:48 +01:00
|
|
|
NSS_ENTRYPOINT_BEGIN;
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
assert(result);
|
|
|
|
assert(errnop);
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2020-06-04 11:46:36 +02:00
|
|
|
if (_nss_systemd_is_blocked())
|
2019-07-04 18:31:11 +02:00
|
|
|
return NSS_STATUS_NOTFOUND;
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2020-03-28 13:26:25 +01:00
|
|
|
_cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL;
|
|
|
|
|
|
|
|
_l = pthread_mutex_lock_assert(&getpwent_data.mutex);
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
if (!getpwent_data.iterator) {
|
|
|
|
UNPROTECT_ERRNO;
|
|
|
|
*errnop = EHOSTDOWN;
|
2020-03-28 13:26:25 +01:00
|
|
|
return NSS_STATUS_UNAVAIL;
|
2018-03-21 05:39:03 +01:00
|
|
|
}
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
r = userdb_iterator_get(getpwent_data.iterator, &ur);
|
2020-03-28 13:26:25 +01:00
|
|
|
if (r == -ESRCH)
|
|
|
|
return NSS_STATUS_NOTFOUND;
|
2019-07-04 18:31:11 +02:00
|
|
|
if (r < 0) {
|
|
|
|
UNPROTECT_ERRNO;
|
|
|
|
*errnop = -r;
|
2020-03-28 13:26:25 +01:00
|
|
|
return NSS_STATUS_UNAVAIL;
|
2018-03-21 05:39:03 +01:00
|
|
|
}
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
r = nss_pack_user_record(ur, result, buffer, buflen);
|
|
|
|
if (r < 0) {
|
|
|
|
UNPROTECT_ERRNO;
|
|
|
|
*errnop = -r;
|
2020-03-28 13:26:25 +01:00
|
|
|
return NSS_STATUS_TRYAGAIN;
|
2018-03-21 05:39:03 +01:00
|
|
|
}
|
|
|
|
|
2020-03-28 13:26:25 +01:00
|
|
|
return NSS_STATUS_SUCCESS;
|
2018-03-21 05:39:03 +01:00
|
|
|
}
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
enum nss_status _nss_systemd_getgrent_r(
|
|
|
|
struct group *result,
|
|
|
|
char *buffer, size_t buflen,
|
|
|
|
int *errnop) {
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
_cleanup_(group_record_unrefp) GroupRecord *gr = NULL;
|
|
|
|
_cleanup_free_ char **members = NULL;
|
|
|
|
int r;
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2018-07-15 16:00:00 +02:00
|
|
|
PROTECT_ERRNO;
|
2020-12-10 12:45:48 +01:00
|
|
|
NSS_ENTRYPOINT_BEGIN;
|
2018-03-21 05:39:03 +01:00
|
|
|
|
|
|
|
assert(result);
|
|
|
|
assert(errnop);
|
|
|
|
|
2020-06-04 11:46:36 +02:00
|
|
|
if (_nss_systemd_is_blocked())
|
|
|
|
return NSS_STATUS_NOTFOUND;
|
2019-07-04 18:31:11 +02:00
|
|
|
|
2020-03-28 13:26:25 +01:00
|
|
|
_cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = NULL;
|
|
|
|
|
|
|
|
_l = pthread_mutex_lock_assert(&getgrent_data.mutex);
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
if (!getgrent_data.iterator) {
|
|
|
|
UNPROTECT_ERRNO;
|
|
|
|
*errnop = EHOSTDOWN;
|
2020-03-28 13:26:25 +01:00
|
|
|
return NSS_STATUS_UNAVAIL;
|
2019-07-04 18:31:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!getgrent_data.by_membership) {
|
|
|
|
r = groupdb_iterator_get(getgrent_data.iterator, &gr);
|
|
|
|
if (r == -ESRCH) {
|
2020-03-28 13:03:06 +01:00
|
|
|
/* So we finished iterating native groups now. Let's now continue with iterating
|
2019-07-04 18:31:11 +02:00
|
|
|
* native memberships, and generate additional group entries for any groups
|
|
|
|
* referenced there that are defined in NSS only. This means for those groups there
|
|
|
|
* will be two or more entries generated during iteration, but this is apparently how
|
|
|
|
* this is supposed to work, and what other implementations do too. Clients are
|
|
|
|
* supposed to merge the group records found during iteration automatically. */
|
|
|
|
getgrent_data.iterator = userdb_iterator_free(getgrent_data.iterator);
|
|
|
|
|
|
|
|
r = membershipdb_all(nss_glue_userdb_flags(), &getgrent_data.iterator);
|
|
|
|
if (r < 0) {
|
|
|
|
UNPROTECT_ERRNO;
|
|
|
|
*errnop = -r;
|
2020-03-28 13:26:25 +01:00
|
|
|
return NSS_STATUS_UNAVAIL;
|
2019-07-04 18:31:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
getgrent_data.by_membership = true;
|
|
|
|
} else if (r < 0) {
|
2019-01-18 20:13:55 +01:00
|
|
|
UNPROTECT_ERRNO;
|
2019-07-04 18:31:11 +02:00
|
|
|
*errnop = -r;
|
2020-03-28 13:26:25 +01:00
|
|
|
return NSS_STATUS_UNAVAIL;
|
2019-07-04 18:31:11 +02:00
|
|
|
} else if (!STR_IN_SET(gr->group_name, root_group.gr_name, nobody_group.gr_name)) {
|
|
|
|
r = membershipdb_by_group_strv(gr->group_name, nss_glue_userdb_flags(), &members);
|
|
|
|
if (r < 0) {
|
|
|
|
UNPROTECT_ERRNO;
|
|
|
|
*errnop = -r;
|
2020-03-28 13:26:25 +01:00
|
|
|
return NSS_STATUS_UNAVAIL;
|
2019-07-04 18:31:11 +02:00
|
|
|
}
|
2018-03-21 05:39:03 +01:00
|
|
|
}
|
2019-07-04 18:31:11 +02:00
|
|
|
}
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
if (getgrent_data.by_membership) {
|
2020-06-04 11:46:36 +02:00
|
|
|
_cleanup_(_nss_systemd_unblockp) bool blocked = false;
|
2019-07-04 18:31:11 +02:00
|
|
|
|
|
|
|
for (;;) {
|
|
|
|
_cleanup_free_ char *user_name = NULL, *group_name = NULL;
|
|
|
|
|
|
|
|
r = membershipdb_iterator_get(getgrent_data.iterator, &user_name, &group_name);
|
2020-03-28 13:26:25 +01:00
|
|
|
if (r == -ESRCH)
|
|
|
|
return NSS_STATUS_NOTFOUND;
|
2019-07-04 18:31:11 +02:00
|
|
|
if (r < 0) {
|
|
|
|
UNPROTECT_ERRNO;
|
|
|
|
*errnop = -r;
|
2020-03-28 13:26:25 +01:00
|
|
|
return NSS_STATUS_UNAVAIL;
|
2019-07-04 18:31:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
if (STR_IN_SET(user_name, root_passwd.pw_name, nobody_passwd.pw_name))
|
|
|
|
continue;
|
|
|
|
if (STR_IN_SET(group_name, root_group.gr_name, nobody_group.gr_name))
|
|
|
|
continue;
|
|
|
|
|
|
|
|
/* We are about to recursively call into NSS, let's make sure we disable recursion into our own code. */
|
2020-06-04 11:46:36 +02:00
|
|
|
if (!blocked) {
|
|
|
|
r = _nss_systemd_block(true);
|
|
|
|
if (r < 0) {
|
2019-07-04 18:31:11 +02:00
|
|
|
UNPROTECT_ERRNO;
|
2020-06-04 11:46:36 +02:00
|
|
|
*errnop = -r;
|
2020-03-28 13:26:25 +01:00
|
|
|
return NSS_STATUS_UNAVAIL;
|
2019-07-04 18:31:11 +02:00
|
|
|
}
|
2020-06-04 11:46:36 +02:00
|
|
|
|
|
|
|
blocked = true;
|
2019-07-04 18:31:11 +02:00
|
|
|
}
|
|
|
|
|
2020-04-09 14:28:34 +02:00
|
|
|
r = nss_group_record_by_name(group_name, false, &gr);
|
2019-07-04 18:31:11 +02:00
|
|
|
if (r == -ESRCH)
|
|
|
|
continue;
|
|
|
|
if (r < 0) {
|
|
|
|
log_debug_errno(r, "Failed to do NSS check for group '%s', ignoring: %m", group_name);
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
members = strv_new(user_name);
|
|
|
|
if (!members) {
|
|
|
|
UNPROTECT_ERRNO;
|
|
|
|
*errnop = ENOMEM;
|
2020-03-28 13:26:25 +01:00
|
|
|
return NSS_STATUS_TRYAGAIN;
|
2019-07-04 18:31:11 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/* Note that we currently generate one group entry per user that is part of a
|
|
|
|
* group. It's a bit ugly, but equivalent to generating a single entry with a set of
|
|
|
|
* members in them. */
|
|
|
|
break;
|
|
|
|
}
|
2018-03-21 05:39:03 +01:00
|
|
|
}
|
2019-07-04 18:31:11 +02:00
|
|
|
|
|
|
|
r = nss_pack_group_record(gr, members, result, buffer, buflen);
|
|
|
|
if (r < 0) {
|
|
|
|
UNPROTECT_ERRNO;
|
|
|
|
*errnop = -r;
|
2020-03-28 13:26:25 +01:00
|
|
|
return NSS_STATUS_TRYAGAIN;
|
2018-03-21 05:39:03 +01:00
|
|
|
}
|
|
|
|
|
2020-03-28 13:26:25 +01:00
|
|
|
return NSS_STATUS_SUCCESS;
|
2018-03-21 05:39:03 +01:00
|
|
|
}
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
enum nss_status _nss_systemd_initgroups_dyn(
|
|
|
|
const char *user_name,
|
|
|
|
gid_t gid,
|
|
|
|
long *start,
|
|
|
|
long *size,
|
|
|
|
gid_t **groupsp,
|
|
|
|
long int limit,
|
|
|
|
int *errnop) {
|
|
|
|
|
|
|
|
_cleanup_(userdb_iterator_freep) UserDBIterator *iterator = NULL;
|
|
|
|
bool any = false;
|
|
|
|
int r;
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2018-07-15 16:00:00 +02:00
|
|
|
PROTECT_ERRNO;
|
2020-12-10 12:45:48 +01:00
|
|
|
NSS_ENTRYPOINT_BEGIN;
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
assert(user_name);
|
|
|
|
assert(start);
|
|
|
|
assert(size);
|
|
|
|
assert(groupsp);
|
2018-03-21 05:39:03 +01:00
|
|
|
assert(errnop);
|
|
|
|
|
2020-04-04 12:23:02 +02:00
|
|
|
if (!valid_user_group_name(user_name, VALID_USER_RELAX))
|
2019-07-04 18:31:11 +02:00
|
|
|
return NSS_STATUS_NOTFOUND;
|
|
|
|
|
|
|
|
/* Don't allow extending these two special users, the same as we won't resolve them via getpwnam() */
|
|
|
|
if (STR_IN_SET(user_name, root_passwd.pw_name, nobody_passwd.pw_name))
|
|
|
|
return NSS_STATUS_NOTFOUND;
|
|
|
|
|
2020-06-04 11:46:36 +02:00
|
|
|
if (_nss_systemd_is_blocked())
|
2019-07-04 18:31:11 +02:00
|
|
|
return NSS_STATUS_NOTFOUND;
|
|
|
|
|
|
|
|
r = membershipdb_by_user(user_name, nss_glue_userdb_flags(), &iterator);
|
|
|
|
if (r < 0) {
|
|
|
|
UNPROTECT_ERRNO;
|
|
|
|
*errnop = -r;
|
|
|
|
return NSS_STATUS_UNAVAIL;
|
|
|
|
}
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
for (;;) {
|
|
|
|
_cleanup_(group_record_unrefp) GroupRecord *g = NULL;
|
|
|
|
_cleanup_free_ char *group_name = NULL;
|
|
|
|
|
|
|
|
r = membershipdb_iterator_get(iterator, NULL, &group_name);
|
|
|
|
if (r == -ESRCH)
|
|
|
|
break;
|
|
|
|
if (r < 0) {
|
2019-01-18 20:13:55 +01:00
|
|
|
UNPROTECT_ERRNO;
|
2019-07-04 18:31:11 +02:00
|
|
|
*errnop = -r;
|
|
|
|
return NSS_STATUS_UNAVAIL;
|
2018-03-21 05:39:03 +01:00
|
|
|
}
|
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
/* The group might be defined via traditional NSS only, hence let's do a full look-up without
|
|
|
|
* disabling NSS. This means we are operating recursively here. */
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2020-06-09 10:47:43 +02:00
|
|
|
r = groupdb_by_name(group_name, (nss_glue_userdb_flags() & ~USERDB_AVOID_NSS) | USERDB_AVOID_SHADOW, &g);
|
2019-07-04 18:31:11 +02:00
|
|
|
if (r == -ESRCH)
|
|
|
|
continue;
|
|
|
|
if (r < 0) {
|
|
|
|
log_debug_errno(r, "Failed to resolve group '%s', ignoring: %m", group_name);
|
|
|
|
continue;
|
|
|
|
}
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
if (g->gid == gid)
|
|
|
|
continue;
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
if (*start >= *size) {
|
|
|
|
gid_t *new_groups;
|
|
|
|
long new_size;
|
|
|
|
|
|
|
|
if (limit > 0 && *size >= limit) /* Reached the limit.? */
|
|
|
|
break;
|
|
|
|
|
|
|
|
if (*size > LONG_MAX/2) { /* Check for overflow */
|
|
|
|
UNPROTECT_ERRNO;
|
|
|
|
*errnop = ENOMEM;
|
|
|
|
return NSS_STATUS_TRYAGAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
new_size = *start * 2;
|
|
|
|
if (limit > 0 && new_size > limit)
|
|
|
|
new_size = limit;
|
|
|
|
|
|
|
|
/* Enlarge buffer */
|
2020-10-09 14:59:44 +02:00
|
|
|
new_groups = reallocarray(*groupsp, new_size, sizeof(**groupsp));
|
2019-07-04 18:31:11 +02:00
|
|
|
if (!new_groups) {
|
|
|
|
UNPROTECT_ERRNO;
|
|
|
|
*errnop = ENOMEM;
|
|
|
|
return NSS_STATUS_TRYAGAIN;
|
|
|
|
}
|
|
|
|
|
|
|
|
*groupsp = new_groups;
|
|
|
|
*size = new_size;
|
|
|
|
}
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
(*groupsp)[(*start)++] = g->gid;
|
|
|
|
any = true;
|
|
|
|
}
|
2018-03-21 05:39:03 +01:00
|
|
|
|
2019-07-04 18:31:11 +02:00
|
|
|
return any ? NSS_STATUS_SUCCESS : NSS_STATUS_NOTFOUND;
|
2018-03-21 05:39:03 +01:00
|
|
|
}
|
2020-06-04 11:46:36 +02:00
|
|
|
|
|
|
|
static thread_local unsigned _blocked = 0;
|
|
|
|
|
|
|
|
_public_ int _nss_systemd_block(bool b) {
|
|
|
|
|
|
|
|
/* This blocks recursively: it's blocked for as many times this function is called with `true` until
|
|
|
|
* it is called an equal time with `false`. */
|
|
|
|
|
|
|
|
if (b) {
|
|
|
|
if (_blocked >= UINT_MAX)
|
|
|
|
return -EOVERFLOW;
|
|
|
|
|
|
|
|
_blocked++;
|
|
|
|
} else {
|
|
|
|
if (_blocked <= 0)
|
|
|
|
return -EOVERFLOW;
|
|
|
|
|
|
|
|
_blocked--;
|
|
|
|
}
|
|
|
|
|
|
|
|
return b; /* Return what is passed in, i.e. the new state from the PoV of the caller */
|
|
|
|
}
|
|
|
|
|
|
|
|
_public_ bool _nss_systemd_is_blocked(void) {
|
|
|
|
return _blocked > 0;
|
|
|
|
}
|