1 /* SPDX-License-Identifier: LGPL-2.1+ */
8 #include "alloc-util.h"
9 #include "bus-common-errors.h"
10 #include "dirent-util.h"
17 #include "signal-util.h"
18 #include "stdio-util.h"
19 #include "string-util.h"
20 #include "user-util.h"
23 #define DYNAMIC_USER_GECOS "Dynamic User"
24 #define DYNAMIC_USER_PASSWD "*" /* locked */
25 #define DYNAMIC_USER_DIR "/"
26 #define DYNAMIC_USER_SHELL "/sbin/nologin"
28 static const struct passwd root_passwd
= {
29 .pw_name
= (char*) "root",
30 .pw_passwd
= (char*) "x", /* see shadow file */
33 .pw_gecos
= (char*) "Super User",
34 .pw_dir
= (char*) "/root",
35 .pw_shell
= (char*) "/bin/sh",
38 static const struct passwd nobody_passwd
= {
39 .pw_name
= (char*) NOBODY_USER_NAME
,
40 .pw_passwd
= (char*) "*", /* locked */
43 .pw_gecos
= (char*) "User Nobody",
44 .pw_dir
= (char*) "/",
45 .pw_shell
= (char*) "/sbin/nologin",
48 static const struct group root_group
= {
49 .gr_name
= (char*) "root",
51 .gr_passwd
= (char*) "x", /* see shadow file */
52 .gr_mem
= (char*[]) { NULL
},
55 static const struct group nobody_group
= {
56 .gr_name
= (char*) NOBODY_GROUP_NAME
,
58 .gr_passwd
= (char*) "*", /* locked */
59 .gr_mem
= (char*[]) { NULL
},
62 typedef struct UserEntry UserEntry
;
63 typedef struct GetentData GetentData
;
70 LIST_FIELDS(UserEntry
, entries
);
74 /* As explained in NOTES section of getpwent_r(3) as 'getpwent_r() is not really
75 * reentrant since it shares the reading position in the stream with all other threads',
76 * we need to protect the data in UserEntry from multithreaded programs which may call
77 * setpwent(), getpwent_r(), or endpwent() simultaneously. So, each function locks the
78 * data by using the mutex below. */
79 pthread_mutex_t mutex
;
82 LIST_HEAD(UserEntry
, entries
);
85 static GetentData getpwent_data
= { PTHREAD_MUTEX_INITIALIZER
, NULL
, NULL
};
86 static GetentData getgrent_data
= { PTHREAD_MUTEX_INITIALIZER
, NULL
, NULL
};
88 NSS_GETPW_PROTOTYPES(systemd
);
89 NSS_GETGR_PROTOTYPES(systemd
);
90 enum nss_status
_nss_systemd_endpwent(void) _public_
;
91 enum nss_status
_nss_systemd_setpwent(int stayopen
) _public_
;
92 enum nss_status
_nss_systemd_getpwent_r(struct passwd
*result
, char *buffer
, size_t buflen
, int *errnop
) _public_
;
93 enum nss_status
_nss_systemd_endgrent(void) _public_
;
94 enum nss_status
_nss_systemd_setgrent(int stayopen
) _public_
;
95 enum nss_status
_nss_systemd_getgrent_r(struct group
*result
, char *buffer
, size_t buflen
, int *errnop
) _public_
;
97 static int direct_lookup_name(const char *name
, uid_t
*ret
) {
98 _cleanup_free_
char *s
= NULL
;
104 /* Normally, we go via the bus to resolve names. That has the benefit that it is available from any mount
105 * namespace and subject to proper authentication. However, there's one problem: if our module is called from
106 * dbus-daemon itself we really can't use D-Bus to communicate. In this case, resort to a client-side hack,
107 * and look for the dynamic names directly. This is pretty ugly, but breaks the cyclic dependency. */
109 path
= strjoina("/run/systemd/dynamic-uid/direct:", name
);
110 r
= readlink_malloc(path
, &s
);
114 return parse_uid(s
, ret
);
117 static int direct_lookup_uid(uid_t uid
, char **ret
) {
118 char path
[STRLEN("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t
) + 1], *s
;
121 xsprintf(path
, "/run/systemd/dynamic-uid/direct:" UID_FMT
, uid
);
123 r
= readlink_malloc(path
, &s
);
126 if (!valid_user_group_name(s
)) { /* extra safety check */
135 enum nss_status
_nss_systemd_getpwnam_r(
138 char *buffer
, size_t buflen
,
141 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
142 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
* reply
= NULL
;
143 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
148 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
153 /* If the username is not valid, then we don't know it. Ideally libc would filter these for us anyway. We don't
154 * generate EINVAL here, because it isn't really out business to complain about invalid user names. */
155 if (!valid_user_group_name(name
))
158 /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
159 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
160 if (streq(name
, root_passwd
.pw_name
)) {
163 return NSS_STATUS_SUCCESS
;
165 if (synthesize_nobody() &&
166 streq(name
, nobody_passwd
.pw_name
)) {
167 *pwd
= nobody_passwd
;
169 return NSS_STATUS_SUCCESS
;
173 /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
174 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
177 bypass
= getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
179 r
= sd_bus_open_system(&bus
);
185 r
= direct_lookup_name(name
, (uid_t
*) &translated
);
191 r
= sd_bus_call_method(bus
,
192 "org.freedesktop.systemd1",
193 "/org/freedesktop/systemd1",
194 "org.freedesktop.systemd1.Manager",
195 "LookupDynamicUserByName",
201 if (sd_bus_error_has_name(&error
, BUS_ERROR_NO_SUCH_DYNAMIC_USER
))
207 r
= sd_bus_message_read(reply
, "u", &translated
);
215 return NSS_STATUS_TRYAGAIN
;
218 memcpy(buffer
, name
, l
+1);
220 pwd
->pw_name
= buffer
;
221 pwd
->pw_uid
= (uid_t
) translated
;
222 pwd
->pw_gid
= (uid_t
) translated
;
223 pwd
->pw_gecos
= (char*) DYNAMIC_USER_GECOS
;
224 pwd
->pw_passwd
= (char*) DYNAMIC_USER_PASSWD
;
225 pwd
->pw_dir
= (char*) DYNAMIC_USER_DIR
;
226 pwd
->pw_shell
= (char*) DYNAMIC_USER_SHELL
;
229 return NSS_STATUS_SUCCESS
;
233 return NSS_STATUS_NOTFOUND
;
237 return NSS_STATUS_UNAVAIL
;
240 enum nss_status
_nss_systemd_getpwuid_r(
243 char *buffer
, size_t buflen
,
246 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
247 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
* reply
= NULL
;
248 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
249 _cleanup_free_
char *direct
= NULL
;
250 const char *translated
;
254 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
256 if (!uid_is_valid(uid
))
259 /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
260 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
261 if (uid
== root_passwd
.pw_uid
) {
264 return NSS_STATUS_SUCCESS
;
266 if (synthesize_nobody() &&
267 uid
== nobody_passwd
.pw_uid
) {
268 *pwd
= nobody_passwd
;
270 return NSS_STATUS_SUCCESS
;
274 if (!uid_is_dynamic(uid
))
277 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
280 bypass
= getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
282 r
= sd_bus_open_system(&bus
);
288 r
= direct_lookup_uid(uid
, &direct
);
297 r
= sd_bus_call_method(bus
,
298 "org.freedesktop.systemd1",
299 "/org/freedesktop/systemd1",
300 "org.freedesktop.systemd1.Manager",
301 "LookupDynamicUserByUID",
307 if (sd_bus_error_has_name(&error
, BUS_ERROR_NO_SUCH_DYNAMIC_USER
))
313 r
= sd_bus_message_read(reply
, "s", &translated
);
318 l
= strlen(translated
) + 1;
321 return NSS_STATUS_TRYAGAIN
;
324 memcpy(buffer
, translated
, l
);
326 pwd
->pw_name
= buffer
;
329 pwd
->pw_gecos
= (char*) DYNAMIC_USER_GECOS
;
330 pwd
->pw_passwd
= (char*) DYNAMIC_USER_PASSWD
;
331 pwd
->pw_dir
= (char*) DYNAMIC_USER_DIR
;
332 pwd
->pw_shell
= (char*) DYNAMIC_USER_SHELL
;
335 return NSS_STATUS_SUCCESS
;
339 return NSS_STATUS_NOTFOUND
;
343 return NSS_STATUS_UNAVAIL
;
346 #pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
348 enum nss_status
_nss_systemd_getgrnam_r(
351 char *buffer
, size_t buflen
,
354 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
355 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
* reply
= NULL
;
356 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
361 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
366 if (!valid_user_group_name(name
))
369 /* Synthesize records for root and nobody, in case they are missing form /etc/group */
370 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
371 if (streq(name
, root_group
.gr_name
)) {
374 return NSS_STATUS_SUCCESS
;
376 if (synthesize_nobody() &&
377 streq(name
, nobody_group
.gr_name
)) {
380 return NSS_STATUS_SUCCESS
;
384 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
387 bypass
= getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
389 r
= sd_bus_open_system(&bus
);
395 r
= direct_lookup_name(name
, (uid_t
*) &translated
);
401 r
= sd_bus_call_method(bus
,
402 "org.freedesktop.systemd1",
403 "/org/freedesktop/systemd1",
404 "org.freedesktop.systemd1.Manager",
405 "LookupDynamicUserByName",
411 if (sd_bus_error_has_name(&error
, BUS_ERROR_NO_SUCH_DYNAMIC_USER
))
417 r
= sd_bus_message_read(reply
, "u", &translated
);
422 l
= sizeof(char*) + strlen(name
) + 1;
425 return NSS_STATUS_TRYAGAIN
;
428 memzero(buffer
, sizeof(char*));
429 strcpy(buffer
+ sizeof(char*), name
);
431 gr
->gr_name
= buffer
+ sizeof(char*);
432 gr
->gr_gid
= (gid_t
) translated
;
433 gr
->gr_passwd
= (char*) DYNAMIC_USER_PASSWD
;
434 gr
->gr_mem
= (char**) buffer
;
437 return NSS_STATUS_SUCCESS
;
441 return NSS_STATUS_NOTFOUND
;
445 return NSS_STATUS_UNAVAIL
;
448 enum nss_status
_nss_systemd_getgrgid_r(
451 char *buffer
, size_t buflen
,
454 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
455 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
* reply
= NULL
;
456 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
457 _cleanup_free_
char *direct
= NULL
;
458 const char *translated
;
462 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
464 if (!gid_is_valid(gid
))
467 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
468 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
469 if (gid
== root_group
.gr_gid
) {
472 return NSS_STATUS_SUCCESS
;
474 if (synthesize_nobody() &&
475 gid
== nobody_group
.gr_gid
) {
478 return NSS_STATUS_SUCCESS
;
482 if (!gid_is_dynamic(gid
))
485 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
488 bypass
= getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
490 r
= sd_bus_open_system(&bus
);
496 r
= direct_lookup_uid(gid
, &direct
);
505 r
= sd_bus_call_method(bus
,
506 "org.freedesktop.systemd1",
507 "/org/freedesktop/systemd1",
508 "org.freedesktop.systemd1.Manager",
509 "LookupDynamicUserByUID",
515 if (sd_bus_error_has_name(&error
, BUS_ERROR_NO_SUCH_DYNAMIC_USER
))
521 r
= sd_bus_message_read(reply
, "s", &translated
);
526 l
= sizeof(char*) + strlen(translated
) + 1;
529 return NSS_STATUS_TRYAGAIN
;
532 memzero(buffer
, sizeof(char*));
533 strcpy(buffer
+ sizeof(char*), translated
);
535 gr
->gr_name
= buffer
+ sizeof(char*);
537 gr
->gr_passwd
= (char*) DYNAMIC_USER_PASSWD
;
538 gr
->gr_mem
= (char**) buffer
;
541 return NSS_STATUS_SUCCESS
;
545 return NSS_STATUS_NOTFOUND
;
549 return NSS_STATUS_UNAVAIL
;
552 static void user_entry_free(UserEntry
*p
) {
557 LIST_REMOVE(entries
, p
->data
->entries
, p
);
563 static int user_entry_add(GetentData
*data
, const char *name
, uid_t id
) {
568 /* This happens when User= or Group= already exists statically. */
569 if (!uid_is_dynamic(id
))
572 p
= new0(UserEntry
, 1);
576 p
->name
= strdup(name
);
584 LIST_PREPEND(entries
, data
->entries
, p
);
589 static void systemd_endent(GetentData
*data
) {
594 while ((p
= data
->entries
))
597 data
->position
= NULL
;
600 static enum nss_status
nss_systemd_endent(GetentData
*p
) {
601 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
603 assert_se(pthread_mutex_lock(&p
->mutex
) == 0);
605 assert_se(pthread_mutex_unlock(&p
->mutex
) == 0);
607 return NSS_STATUS_SUCCESS
;
610 enum nss_status
_nss_systemd_endpwent(void) {
611 return nss_systemd_endent(&getpwent_data
);
614 enum nss_status
_nss_systemd_endgrent(void) {
615 return nss_systemd_endent(&getgrent_data
);
618 static int direct_enumeration(GetentData
*p
) {
619 _cleanup_closedir_
DIR *d
= NULL
;
625 d
= opendir("/run/systemd/dynamic-uid/");
629 FOREACH_DIRENT(de
, d
, return -errno
) {
630 _cleanup_free_
char *name
= NULL
;
633 if (!dirent_is_file(de
))
636 r
= parse_uid(de
->d_name
, &uid
);
640 r
= direct_lookup_uid(uid
, &name
);
646 r
= direct_lookup_name(name
, &verified
);
653 r
= user_entry_add(p
, name
, uid
);
663 static enum nss_status
systemd_setent(GetentData
*p
) {
664 _cleanup_(sd_bus_error_free
) sd_bus_error error
= SD_BUS_ERROR_NULL
;
665 _cleanup_(sd_bus_message_unrefp
) sd_bus_message
* reply
= NULL
;
666 _cleanup_(sd_bus_flush_close_unrefp
) sd_bus
*bus
= NULL
;
671 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
675 assert_se(pthread_mutex_lock(&p
->mutex
) == 0);
679 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
682 bypass
= getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
685 r
= sd_bus_open_system(&bus
);
691 r
= direct_enumeration(p
);
698 r
= sd_bus_call_method(bus
,
699 "org.freedesktop.systemd1",
700 "/org/freedesktop/systemd1",
701 "org.freedesktop.systemd1.Manager",
709 r
= sd_bus_message_enter_container(reply
, 'a', "(us)");
713 while ((r
= sd_bus_message_read(reply
, "(us)", &id
, &name
)) > 0) {
714 r
= user_entry_add(p
, name
, id
);
723 r
= sd_bus_message_exit_container(reply
);
728 p
->position
= p
->entries
;
729 assert_se(pthread_mutex_unlock(&p
->mutex
) == 0);
731 return NSS_STATUS_SUCCESS
;
735 assert_se(pthread_mutex_unlock(&p
->mutex
) == 0);
737 return NSS_STATUS_UNAVAIL
;
740 enum nss_status
_nss_systemd_setpwent(int stayopen
) {
741 return systemd_setent(&getpwent_data
);
744 enum nss_status
_nss_systemd_setgrent(int stayopen
) {
745 return systemd_setent(&getgrent_data
);
748 enum nss_status
_nss_systemd_getpwent_r(struct passwd
*result
, char *buffer
, size_t buflen
, int *errnop
) {
753 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
759 assert_se(pthread_mutex_lock(&getpwent_data
.mutex
) == 0);
761 LIST_FOREACH(entries
, p
, getpwent_data
.position
) {
762 len
= strlen(p
->name
) + 1;
765 ret
= NSS_STATUS_TRYAGAIN
;
769 memcpy(buffer
, p
->name
, len
);
771 result
->pw_name
= buffer
;
772 result
->pw_uid
= p
->id
;
773 result
->pw_gid
= p
->id
;
774 result
->pw_gecos
= (char*) DYNAMIC_USER_GECOS
;
775 result
->pw_passwd
= (char*) DYNAMIC_USER_PASSWD
;
776 result
->pw_dir
= (char*) DYNAMIC_USER_DIR
;
777 result
->pw_shell
= (char*) DYNAMIC_USER_SHELL
;
782 ret
= NSS_STATUS_NOTFOUND
;
786 /* On success, step to the next entry. */
788 ret
= NSS_STATUS_SUCCESS
;
791 /* Save position for the next call. */
792 getpwent_data
.position
= p
;
794 assert_se(pthread_mutex_unlock(&getpwent_data
.mutex
) == 0);
799 enum nss_status
_nss_systemd_getgrent_r(struct group
*result
, char *buffer
, size_t buflen
, int *errnop
) {
804 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK
);
810 assert_se(pthread_mutex_lock(&getgrent_data
.mutex
) == 0);
812 LIST_FOREACH(entries
, p
, getgrent_data
.position
) {
813 len
= sizeof(char*) + strlen(p
->name
) + 1;
816 ret
= NSS_STATUS_TRYAGAIN
;
820 memzero(buffer
, sizeof(char*));
821 strcpy(buffer
+ sizeof(char*), p
->name
);
823 result
->gr_name
= buffer
+ sizeof(char*);
824 result
->gr_gid
= p
->id
;
825 result
->gr_passwd
= (char*) DYNAMIC_USER_PASSWD
;
826 result
->gr_mem
= (char**) buffer
;
831 ret
= NSS_STATUS_NOTFOUND
;
835 /* On success, step to the next entry. */
837 ret
= NSS_STATUS_SUCCESS
;
840 /* Save position for the next call. */
841 getgrent_data
.position
= p
;
843 assert_se(pthread_mutex_unlock(&getgrent_data
.mutex
) == 0);