From 09001dbdc88f447656d79822bbff9c6d7ed7e5cf Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 5 May 2021 15:53:07 +0200 Subject: [PATCH 1/2] nss-systemd: set USERDB_SUPPRESS_SHADOW flag when looking up user records Setting the flags means we won#t try to read the data from /etc/shadow when reading a user record, thus slightly making conversion quicker and reducing the chance of generating MAC faults, because we needlessly access a privileged resource. Previously, passing the flag didn't matter, when converting our JSON records to NSS since the flag only had an effect on whether to use NSS getspnam() and related calls or not. But given that we turn off NSS anyway as backend for this conversion (since we want to avoid NSS loops, where we turn NSS data to our JSON user records, and then to NSS forever and ever) it was unnecessary to pass it. This changed in one of the previous commits however, where we added support for reading user definitions from drop-in files, with separate drop-in files for the shadow data. --- src/nss-systemd/userdb-glue.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/nss-systemd/userdb-glue.c b/src/nss-systemd/userdb-glue.c index 73941b2ba5..4b04d03c95 100644 --- a/src/nss-systemd/userdb-glue.c +++ b/src/nss-systemd/userdb-glue.c @@ -79,7 +79,7 @@ enum nss_status userdb_getpwnam( if (_nss_systemd_is_blocked()) return NSS_STATUS_NOTFOUND; - r = userdb_by_name(name, nss_glue_userdb_flags(), &hr); + r = userdb_by_name(name, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW, &hr); if (r == -ESRCH) return NSS_STATUS_NOTFOUND; if (r < 0) { @@ -112,7 +112,7 @@ enum nss_status userdb_getpwuid( if (_nss_systemd_is_blocked()) return NSS_STATUS_NOTFOUND; - r = userdb_by_uid(uid, nss_glue_userdb_flags(), &hr); + r = userdb_by_uid(uid, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW, &hr); if (r == -ESRCH) return NSS_STATUS_NOTFOUND; if (r < 0) { @@ -209,13 +209,13 @@ enum nss_status userdb_getgrnam( if (_nss_systemd_is_blocked()) return NSS_STATUS_NOTFOUND; - r = groupdb_by_name(name, nss_glue_userdb_flags(), &g); + r = groupdb_by_name(name, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW, &g); if (r < 0 && r != -ESRCH) { *errnop = -r; return NSS_STATUS_UNAVAIL; } - r = membershipdb_by_group_strv(name, nss_glue_userdb_flags(), &members); + r = membershipdb_by_group_strv(name, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW, &members); if (r < 0 && r != -ESRCH) { *errnop = -r; return NSS_STATUS_UNAVAIL; @@ -277,7 +277,7 @@ enum nss_status userdb_getgrgid( if (_nss_systemd_is_blocked()) return NSS_STATUS_NOTFOUND; - r = groupdb_by_gid(gid, nss_glue_userdb_flags(), &g); + r = groupdb_by_gid(gid, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW, &g); if (r < 0 && r != -ESRCH) { *errnop = -r; return NSS_STATUS_UNAVAIL; @@ -308,7 +308,7 @@ enum nss_status userdb_getgrgid( } else from_nss = false; - r = membershipdb_by_group_strv(g->group_name, nss_glue_userdb_flags(), &members); + r = membershipdb_by_group_strv(g->group_name, nss_glue_userdb_flags()|USERDB_SUPPRESS_SHADOW, &members); if (r < 0 && r != -ESRCH) { *errnop = -r; return NSS_STATUS_UNAVAIL; From f43a19ecd6e3415ec223f245204b4eaaff739af4 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 5 May 2021 16:11:26 +0200 Subject: [PATCH 2/2] nss-systemd: synthesize NSS shadow/gshadow records from userdb, as well This ensures we not only synthesize regular paswd/group records of userdb records, but shadow records as well. This should make sure that userdb can be used as comprehensive superset of the classic passwd/group/shadow/gshadow functionality. --- factory/etc/nsswitch.conf | 3 +- man/nss-myhostname.xml | 4 +- man/nss-mymachines.xml | 3 +- man/nss-resolve.xml | 3 +- man/nss-systemd.xml | 9 +- src/basic/nss-util.h | 36 ++++ src/nss-systemd/nss-systemd.c | 298 +++++++++++++++++++++++++++++++- src/nss-systemd/nss-systemd.sym | 8 + src/nss-systemd/userdb-glue.c | 151 +++++++++++++++- src/nss-systemd/userdb-glue.h | 7 + 10 files changed, 511 insertions(+), 11 deletions(-) diff --git a/factory/etc/nsswitch.conf b/factory/etc/nsswitch.conf index d87f8811ec..acfa8474d2 100644 --- a/factory/etc/nsswitch.conf +++ b/factory/etc/nsswitch.conf @@ -2,7 +2,8 @@ passwd: compat systemd group: compat [SUCCESS=merge] systemd -shadow: compat +shadow: compat systemd +gshadow: files systemd hosts: mymachines resolve [!UNAVAIL=return] files myhostname dns networks: files diff --git a/man/nss-myhostname.xml b/man/nss-myhostname.xml index 8a603fb9ce..98eb0ec77e 100644 --- a/man/nss-myhostname.xml +++ b/man/nss-myhostname.xml @@ -91,7 +91,9 @@ passwd: compat systemd group: compat [SUCCESS=merge] systemd -shadow: compat +shadow: compat systemd +gshadow: files systemd + # Either (untrusted network, see above): hosts: mymachines resolve [!UNAVAIL=return] files myhostname dns diff --git a/man/nss-mymachines.xml b/man/nss-mymachines.xml index b2785df410..03fcd4308e 100644 --- a/man/nss-mymachines.xml +++ b/man/nss-mymachines.xml @@ -57,7 +57,8 @@ passwd: compat systemd group: compat [SUCCESS=merge] systemd -shadow: compat +shadow: compat systemd +gshadow: files systemd hosts: mymachines resolve [!UNAVAIL=return] files myhostname dns networks: files diff --git a/man/nss-resolve.xml b/man/nss-resolve.xml index 78c92030ac..97c3768100 100644 --- a/man/nss-resolve.xml +++ b/man/nss-resolve.xml @@ -63,7 +63,8 @@ passwd: compat systemd group: compat [SUCCESS=merge] systemd -shadow: compat +shadow: compat systemd +gshadow: files systemd hosts: mymachines resolve [!UNAVAIL=return] files myhostname dns networks: files diff --git a/man/nss-systemd.xml b/man/nss-systemd.xml index 1fee8cc8ba..d42a866b0f 100644 --- a/man/nss-systemd.xml +++ b/man/nss-systemd.xml @@ -47,11 +47,13 @@ for resolving users and groups, but also works without the service running. To activate the NSS module, add systemd to the lines starting with - passwd: and group: in /etc/nsswitch.conf. + passwd:, group:, shadow: and + gshadow: in /etc/nsswitch.conf. It is recommended to place systemd after the files or compat entry of the /etc/nsswitch.conf lines so that - /etc/passwd and /etc/group based mappings take precedence. + /etc/passwd, /etc/group, /etc/shadow and + /etc/gshadow based mappings take precedence. @@ -63,7 +65,8 @@ passwd: compat systemd group: compat [SUCCESS=merge] systemd -shadow: compat +shadow: compat systemd +gshadow: files systemd hosts: mymachines resolve [!UNAVAIL=return] files myhostname dns networks: files diff --git a/src/basic/nss-util.h b/src/basic/nss-util.h index 3c59dcc03c..579e2c0bde 100644 --- a/src/basic/nss-util.h +++ b/src/basic/nss-util.h @@ -127,6 +127,20 @@ enum nss_status _nss_##module##_getpwuid_r( \ char *buffer, size_t buflen, \ int *errnop) _public_ +#define NSS_GETSP_PROTOTYPES(module) \ +enum nss_status _nss_##module##_getspnam_r( \ + const char *name, \ + struct spwd *spwd, \ + char *buffer, size_t buflen, \ + int *errnop) _public_ + +#define NSS_GETSG_PROTOTYPES(module) \ +enum nss_status _nss_##module##_getsgnam_r( \ + const char *name, \ + struct sgrp *sgrp, \ + char *buffer, size_t buflen, \ + int *errnop) _public_ + #define NSS_GETGR_PROTOTYPES(module) \ enum nss_status _nss_##module##_getgrnam_r( \ const char *name, \ @@ -150,6 +164,17 @@ enum nss_status _nss_##module##_getpwent_r( \ size_t buflen, \ int *errnop) _public_; +#define NSS_SPENT_PROTOTYPES(module) \ +enum nss_status _nss_##module##_endspent( \ + void) _public_; \ +enum nss_status _nss_##module##_setspent( \ + int stayopen) _public_; \ +enum nss_status _nss_##module##_getspent_r( \ + struct spwd *spwd, \ + char *buffer, \ + size_t buflen, \ + int *errnop) _public_; + #define NSS_GRENT_PROTOTYPES(module) \ enum nss_status _nss_##module##_endgrent( \ void) _public_; \ @@ -161,6 +186,17 @@ enum nss_status _nss_##module##_getgrent_r( \ size_t buflen, \ int *errnop) _public_; +#define NSS_SGENT_PROTOTYPES(module) \ +enum nss_status _nss_##module##_endsgent( \ + void) _public_; \ +enum nss_status _nss_##module##_setsgent( \ + int stayopen) _public_; \ +enum nss_status _nss_##module##_getsgent_r( \ + struct sgrp *sgrp, \ + char *buffer, \ + size_t buflen, \ + int *errnop) _public_; + #define NSS_INITGROUPS_PROTOTYPE(module) \ enum nss_status _nss_##module##_initgroups_dyn( \ const char *user, \ diff --git a/src/nss-systemd/nss-systemd.c b/src/nss-systemd/nss-systemd.c index 14712f8735..1b0866109a 100644 --- a/src/nss-systemd/nss-systemd.c +++ b/src/nss-systemd/nss-systemd.c @@ -28,6 +28,18 @@ static const struct passwd root_passwd = { .pw_shell = (char*) "/bin/sh", }; +static const struct spwd root_spwd = { + .sp_namp = (char*) "root", + .sp_pwdp = (char*) PASSWORD_LOCKED_AND_INVALID, + .sp_lstchg = -1, + .sp_min = -1, + .sp_max = -1, + .sp_warn = -1, + .sp_inact = -1, + .sp_expire = -1, + .sp_flag = ULONG_MAX, /* this appears to be what everybody does ... */ +}; + static const struct passwd nobody_passwd = { .pw_name = (char*) NOBODY_USER_NAME, .pw_passwd = (char*) PASSWORD_LOCKED_AND_INVALID, @@ -38,6 +50,18 @@ static const struct passwd nobody_passwd = { .pw_shell = (char*) NOLOGIN, }; +static const struct spwd nobody_spwd = { + .sp_namp = (char*) NOBODY_USER_NAME, + .sp_pwdp = (char*) PASSWORD_LOCKED_AND_INVALID, + .sp_lstchg = -1, + .sp_min = -1, + .sp_max = -1, + .sp_warn = -1, + .sp_inact = -1, + .sp_expire = -1, + .sp_flag = ULONG_MAX, /* this appears to be what everybody does ... */ +}; + static const struct group root_group = { .gr_name = (char*) "root", .gr_gid = 0, @@ -45,6 +69,11 @@ static const struct group root_group = { .gr_mem = (char*[]) { NULL }, }; +static const struct sgrp root_sgrp = { + .sg_namp = (char*) "root", + .sg_passwd = (char*) PASSWORD_LOCKED_AND_INVALID, +}; + static const struct group nobody_group = { .gr_name = (char*) NOBODY_GROUP_NAME, .gr_gid = GID_NOBODY, @@ -52,6 +81,11 @@ static const struct group nobody_group = { .gr_mem = (char*[]) { NULL }, }; +static const struct sgrp nobody_sgrp = { + .sg_namp = (char*) NOBODY_GROUP_NAME, + .sg_passwd = (char*) PASSWORD_LOCKED_AND_INVALID, +}; + 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 @@ -66,11 +100,19 @@ typedef struct GetentData { } GetentData; static GetentData getpwent_data = { - .mutex = PTHREAD_MUTEX_INITIALIZER + .mutex = PTHREAD_MUTEX_INITIALIZER, }; static GetentData getgrent_data = { - .mutex = PTHREAD_MUTEX_INITIALIZER + .mutex = PTHREAD_MUTEX_INITIALIZER, +}; + +static GetentData getspent_data = { + .mutex = PTHREAD_MUTEX_INITIALIZER, +}; + +static GetentData getsgent_data = { + .mutex = PTHREAD_MUTEX_INITIALIZER, }; static void setup_logging(void) { @@ -88,9 +130,13 @@ static void setup_logging_once(void) { setup_logging_once() NSS_GETPW_PROTOTYPES(systemd); +NSS_GETSP_PROTOTYPES(systemd); NSS_GETGR_PROTOTYPES(systemd); +NSS_GETSG_PROTOTYPES(systemd); NSS_PWENT_PROTOTYPES(systemd); +NSS_SPENT_PROTOTYPES(systemd); NSS_GRENT_PROTOTYPES(systemd); +NSS_SGENT_PROTOTYPES(systemd); NSS_INITGROUPS_PROTOTYPE(systemd); enum nss_status _nss_systemd_getpwnam_r( @@ -191,6 +237,54 @@ enum nss_status _nss_systemd_getpwuid_r( return status; } +enum nss_status _nss_systemd_getspnam_r( + const char *name, + struct spwd *spwd, + char *buffer, size_t buflen, + int *errnop) { + + enum nss_status status; + int e; + + PROTECT_ERRNO; + NSS_ENTRYPOINT_BEGIN; + + assert(name); + assert(spwd); + assert(errnop); + + if (!valid_user_group_name(name, VALID_USER_RELAX)) + 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_spwd.sp_namp)) { + *spwd = root_spwd; + return NSS_STATUS_SUCCESS; + } + + if (streq(name, nobody_spwd.sp_namp)) { + if (!synthesize_nobody()) + return NSS_STATUS_NOTFOUND; + + *spwd = nobody_spwd; + return NSS_STATUS_SUCCESS; + } + + } else if (STR_IN_SET(name, root_spwd.sp_namp, nobody_spwd.sp_namp)) + return NSS_STATUS_NOTFOUND; + + status = userdb_getspnam(name, spwd, buffer, buflen, &e); + if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) { + UNPROTECT_ERRNO; + *errnop = e; + return status; + } + + return status; +} + #pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess" enum nss_status _nss_systemd_getgrnam_r( @@ -288,6 +382,54 @@ enum nss_status _nss_systemd_getgrgid_r( return status; } +enum nss_status _nss_systemd_getsgnam_r( + const char *name, + struct sgrp *sgrp, + char *buffer, size_t buflen, + int *errnop) { + + enum nss_status status; + int e; + + PROTECT_ERRNO; + NSS_ENTRYPOINT_BEGIN; + + assert(name); + assert(sgrp); + assert(errnop); + + if (!valid_user_group_name(name, VALID_USER_RELAX)) + 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 (streq(name, root_sgrp.sg_namp)) { + *sgrp = root_sgrp; + return NSS_STATUS_SUCCESS; + } + + if (streq(name, nobody_sgrp.sg_namp)) { + if (!synthesize_nobody()) + return NSS_STATUS_NOTFOUND; + + *sgrp = nobody_sgrp; + return NSS_STATUS_SUCCESS; + } + + } else if (STR_IN_SET(name, root_sgrp.sg_namp, nobody_sgrp.sg_namp)) + return NSS_STATUS_NOTFOUND; + + status = userdb_getsgnam(name, sgrp, buffer, buflen, &e); + if (IN_SET(status, NSS_STATUS_UNAVAIL, NSS_STATUS_TRYAGAIN)) { + UNPROTECT_ERRNO; + *errnop = e; + return status; + } + + return status; +} + static enum nss_status nss_systemd_endent(GetentData *p) { PROTECT_ERRNO; NSS_ENTRYPOINT_BEGIN; @@ -307,10 +449,18 @@ enum nss_status _nss_systemd_endpwent(void) { return nss_systemd_endent(&getpwent_data); } +enum nss_status _nss_systemd_endspent(void) { + return nss_systemd_endent(&getspent_data); +} + enum nss_status _nss_systemd_endgrent(void) { return nss_systemd_endent(&getgrent_data); } +enum nss_status _nss_systemd_endsgent(void) { + return nss_systemd_endent(&getsgent_data); +} + enum nss_status _nss_systemd_setpwent(int stayopen) { int r; @@ -355,6 +505,46 @@ enum nss_status _nss_systemd_setgrent(int stayopen) { return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS; } +enum nss_status _nss_systemd_setspent(int stayopen) { + int r; + + PROTECT_ERRNO; + NSS_ENTRYPOINT_BEGIN; + + if (_nss_systemd_is_blocked()) + return NSS_STATUS_NOTFOUND; + + _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = pthread_mutex_lock_assert(&getspent_data.mutex); + (void) _l; /* make llvm shut up about _l not being used. */ + + getspent_data.iterator = userdb_iterator_free(getspent_data.iterator); + getspent_data.by_membership = false; + + /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE here */ + r = userdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE, &getspent_data.iterator); + return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS; +} + +enum nss_status _nss_systemd_setsgent(int stayopen) { + int r; + + PROTECT_ERRNO; + NSS_ENTRYPOINT_BEGIN; + + if (_nss_systemd_is_blocked()) + return NSS_STATUS_NOTFOUND; + + _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = pthread_mutex_lock_assert(&getsgent_data.mutex); + (void) _l; /* make llvm shut up about _l not being used. */ + + getsgent_data.iterator = userdb_iterator_free(getsgent_data.iterator); + getsgent_data.by_membership = false; + + /* See _nss_systemd_setpwent() for an explanation why we use USERDB_DONT_SYNTHESIZE here */ + r = groupdb_all(nss_glue_userdb_flags() | USERDB_DONT_SYNTHESIZE, &getsgent_data.iterator); + return r < 0 ? NSS_STATUS_UNAVAIL : NSS_STATUS_SUCCESS; +} + enum nss_status _nss_systemd_getpwent_r( struct passwd *result, char *buffer, size_t buflen, @@ -527,6 +717,110 @@ enum nss_status _nss_systemd_getgrent_r( return NSS_STATUS_SUCCESS; } +enum nss_status _nss_systemd_getspent_r( + struct spwd *result, + char *buffer, size_t buflen, + int *errnop) { + + _cleanup_(user_record_unrefp) UserRecord *ur = NULL; + int r; + + PROTECT_ERRNO; + NSS_ENTRYPOINT_BEGIN; + + assert(result); + assert(errnop); + + if (_nss_systemd_is_blocked()) + return NSS_STATUS_NOTFOUND; + + _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = pthread_mutex_lock_assert(&getspent_data.mutex); + (void) _l; /* make llvm shut up about _l not being used. */ + + if (!getspent_data.iterator) { + UNPROTECT_ERRNO; + *errnop = EHOSTDOWN; + return NSS_STATUS_UNAVAIL; + } + + for (;;) { + r = userdb_iterator_get(getspent_data.iterator, &ur); + if (r == -ESRCH) + return NSS_STATUS_NOTFOUND; + if (r < 0) { + UNPROTECT_ERRNO; + *errnop = -r; + return NSS_STATUS_UNAVAIL; + } + + if (!ur->incomplete) /* don't synthesize shadow records for records where we couldn't read shadow data */ + break; + + ur = user_record_unref(ur); + } + + r = nss_pack_user_record_shadow(ur, result, buffer, buflen); + if (r < 0) { + UNPROTECT_ERRNO; + *errnop = -r; + return NSS_STATUS_TRYAGAIN; + } + + return NSS_STATUS_SUCCESS; +} + +enum nss_status _nss_systemd_getsgent_r( + struct sgrp *result, + char *buffer, size_t buflen, + int *errnop) { + + _cleanup_(group_record_unrefp) GroupRecord *gr = NULL; + int r; + + PROTECT_ERRNO; + NSS_ENTRYPOINT_BEGIN; + + assert(result); + assert(errnop); + + if (_nss_systemd_is_blocked()) + return NSS_STATUS_NOTFOUND; + + _cleanup_(pthread_mutex_unlock_assertp) pthread_mutex_t *_l = pthread_mutex_lock_assert(&getsgent_data.mutex); + (void) _l; /* make llvm shut up about _l not being used. */ + + if (!getsgent_data.iterator) { + UNPROTECT_ERRNO; + *errnop = EHOSTDOWN; + return NSS_STATUS_UNAVAIL; + } + + for (;;) { + r = groupdb_iterator_get(getsgent_data.iterator, &gr); + if (r == -ESRCH) + return NSS_STATUS_NOTFOUND; + if (r < 0) { + UNPROTECT_ERRNO; + *errnop = -r; + return NSS_STATUS_UNAVAIL; + } + + if (!gr->incomplete) /* don't synthesize shadow records for records where we couldn't read shadow data */ + break; + + gr = group_record_unref(gr); + } + + r = nss_pack_group_record_shadow(gr, result, buffer, buflen); + if (r < 0) { + UNPROTECT_ERRNO; + *errnop = -r; + return NSS_STATUS_TRYAGAIN; + } + + return NSS_STATUS_SUCCESS; +} + enum nss_status _nss_systemd_initgroups_dyn( const char *user_name, gid_t gid, diff --git a/src/nss-systemd/nss-systemd.sym b/src/nss-systemd/nss-systemd.sym index 7caf217144..5602f0064c 100644 --- a/src/nss-systemd/nss-systemd.sym +++ b/src/nss-systemd/nss-systemd.sym @@ -10,15 +10,23 @@ { global: _nss_systemd_getpwnam_r; + _nss_systemd_getspnam_r; _nss_systemd_getpwuid_r; _nss_systemd_endpwent; _nss_systemd_setpwent; _nss_systemd_getpwent_r; + _nss_systemd_endspent; + _nss_systemd_setspent; + _nss_systemd_getspent_r; _nss_systemd_getgrnam_r; + _nss_systemd_getsgnam_r; _nss_systemd_getgrgid_r; _nss_systemd_endgrent; _nss_systemd_setgrent; _nss_systemd_getgrent_r; + _nss_systemd_endsgent; + _nss_systemd_setsgent; + _nss_systemd_getsgent_r; _nss_systemd_initgroups_dyn; /* These two are not used by glibc, but can be used by apps to explicitly disable nss-systemd for the calling thread. */ diff --git a/src/nss-systemd/userdb-glue.c b/src/nss-systemd/userdb-glue.c index 4b04d03c95..f7df30f906 100644 --- a/src/nss-systemd/userdb-glue.c +++ b/src/nss-systemd/userdb-glue.c @@ -32,7 +32,7 @@ int nss_pack_user_record( assert(hr); assert(pwd); - assert_se(hr->user_name); + assert(hr->user_name); required = strlen(hr->user_name) + 1; assert_se(rn = user_record_real_name(hr)); @@ -129,6 +129,85 @@ enum nss_status userdb_getpwuid( return NSS_STATUS_SUCCESS; } +int nss_pack_user_record_shadow( + UserRecord *hr, + struct spwd *spwd, + char *buffer, + size_t buflen) { + + const char *hashed; + size_t required; + + assert(hr); + assert(spwd); + + assert(hr->user_name); + required = strlen(hr->user_name) + 1; + + assert_se(hashed = strv_isempty(hr->hashed_password) ? PASSWORD_LOCKED_AND_INVALID : hr->hashed_password[0]); + required += strlen(hashed) + 1; + + if (buflen < required) + return -ERANGE; + + *spwd = (struct spwd) { + .sp_namp = buffer, + .sp_lstchg = hr->last_password_change_usec == 0 ? 1 : /* map 0 to 1, since 0 means please change pasword on next login */ + hr->last_password_change_usec == UINT64_MAX ? -1 : + (long int) (hr->last_password_change_usec / USEC_PER_DAY), + .sp_min = hr->password_change_min_usec != UINT64_MAX ? (long int) (hr->password_change_min_usec / USEC_PER_DAY) : -1, + .sp_max = hr->password_change_max_usec != UINT64_MAX ? (long int) (hr->password_change_max_usec / USEC_PER_DAY) : -1, + .sp_warn = hr->password_change_warn_usec != UINT64_MAX ? (long int) (hr->password_change_warn_usec / USEC_PER_DAY) : -1, + .sp_inact = hr->password_change_inactive_usec != UINT64_MAX ? (long int) (hr->password_change_inactive_usec / USEC_PER_DAY) : -1, + .sp_expire = hr->locked > 0 || hr->not_after_usec == 0 ? 1 : /* already expired/locked */ + hr->not_after_usec == UINT64_MAX ? -1 : + (long int) (hr->not_after_usec / USEC_PER_DAY), + .sp_flag = ULONG_MAX, + }; + + assert(buffer); + + spwd->sp_pwdp = stpcpy(spwd->sp_namp, hr->user_name) + 1; + strcpy(spwd->sp_pwdp, hashed); + + return 0; +} + +enum nss_status userdb_getspnam( + const char *name, + struct spwd *spwd, + char *buffer, size_t buflen, + int *errnop) { + + _cleanup_(user_record_unrefp) UserRecord *hr = NULL; + int r; + + assert(spwd); + assert(errnop); + + if (_nss_systemd_is_blocked()) + return NSS_STATUS_NOTFOUND; + + r = userdb_by_name(name, nss_glue_userdb_flags(), &hr); + if (r == -ESRCH) + return NSS_STATUS_NOTFOUND; + if (r < 0) { + *errnop = -r; + return NSS_STATUS_UNAVAIL; + } + + if (hr->incomplete) /* protected records missing? */ + return NSS_STATUS_NOTFOUND; + + r = nss_pack_user_record_shadow(hr, spwd, buffer, buflen); + if (r < 0) { + *errnop = -r; + return NSS_STATUS_TRYAGAIN; + } + + return NSS_STATUS_SUCCESS; +} + int nss_pack_group_record( GroupRecord *g, char **extra_members, @@ -142,7 +221,7 @@ int nss_pack_group_record( assert(g); assert(gr); - assert_se(g->group_name); + assert(g->group_name); required = strlen(g->group_name) + 1; STRV_FOREACH(m, g->members) { @@ -327,3 +406,71 @@ enum nss_status userdb_getgrgid( return NSS_STATUS_SUCCESS; } + +int nss_pack_group_record_shadow( + GroupRecord *hr, + struct sgrp *sgrp, + char *buffer, + size_t buflen) { + + const char *hashed; + size_t required; + + assert(hr); + assert(sgrp); + + assert(hr->group_name); + required = strlen(hr->group_name) + 1; + + assert_se(hashed = strv_isempty(hr->hashed_password) ? PASSWORD_LOCKED_AND_INVALID : hr->hashed_password[0]); + required += strlen(hashed) + 1; + + if (buflen < required) + return -ERANGE; + + *sgrp = (struct sgrp) { + .sg_namp = buffer, + }; + + assert(buffer); + + sgrp->sg_passwd = stpcpy(sgrp->sg_namp, hr->group_name) + 1; + strcpy(sgrp->sg_passwd, hashed); + + return 0; +} + +enum nss_status userdb_getsgnam( + const char *name, + struct sgrp *sgrp, + char *buffer, size_t buflen, + int *errnop) { + + _cleanup_(group_record_unrefp) GroupRecord *hr = NULL; + int r; + + assert(sgrp); + assert(errnop); + + if (_nss_systemd_is_blocked()) + return NSS_STATUS_NOTFOUND; + + r = groupdb_by_name(name, nss_glue_userdb_flags(), &hr); + if (r == -ESRCH) + return NSS_STATUS_NOTFOUND; + if (r < 0) { + *errnop = -r; + return NSS_STATUS_UNAVAIL; + } + + if (hr->incomplete) /* protected records missing? */ + return NSS_STATUS_NOTFOUND; + + r = nss_pack_group_record_shadow(hr, sgrp, buffer, buflen); + if (r < 0) { + *errnop = -r; + return NSS_STATUS_TRYAGAIN; + } + + return NSS_STATUS_SUCCESS; +} diff --git a/src/nss-systemd/userdb-glue.h b/src/nss-systemd/userdb-glue.h index cb0dcb9302..386cc88955 100644 --- a/src/nss-systemd/userdb-glue.h +++ b/src/nss-systemd/userdb-glue.h @@ -13,8 +13,15 @@ UserDBFlags nss_glue_userdb_flags(void); int nss_pack_user_record(UserRecord *hr, struct passwd *pwd, char *buffer, size_t buflen); int nss_pack_group_record(GroupRecord *g, char **extra_members, struct group *gr, char *buffer, size_t buflen); +int nss_pack_user_record_shadow(UserRecord *hr, struct spwd *spwd, char *buffer, size_t buflen); +int nss_pack_group_record_shadow(GroupRecord *hr, struct sgrp *sgrp, char *buffer,size_t buflen); + enum nss_status userdb_getpwnam(const char *name, struct passwd *pwd, char *buffer, size_t buflen, int *errnop); enum nss_status userdb_getpwuid(uid_t uid, struct passwd *pwd, char *buffer, size_t buflen, int *errnop); +enum nss_status userdb_getspnam(const char *name, struct spwd *spwd, char *buffer, size_t buflen, int *errnop); + enum nss_status userdb_getgrnam(const char *name, struct group *gr, char *buffer, size_t buflen, int *errnop); enum nss_status userdb_getgrgid(gid_t gid, struct group *gr, char *buffer, size_t buflen, int *errnop); + +enum nss_status userdb_getsgnam(const char *name, struct sgrp *sgrp, char *buffer, size_t buflen, int *errnop);