Branch data Line data Source code
1 : : /* SPDX-License-Identifier: LGPL-2.1+ */
2 : :
3 : : #include <nss.h>
4 : : #include <pthread.h>
5 : :
6 : : #include "sd-bus.h"
7 : :
8 : : #include "alloc-util.h"
9 : : #include "bus-common-errors.h"
10 : : #include "dirent-util.h"
11 : : #include "env-util.h"
12 : : #include "fd-util.h"
13 : : #include "format-util.h"
14 : : #include "fs-util.h"
15 : : #include "list.h"
16 : : #include "macro.h"
17 : : #include "nss-util.h"
18 : : #include "signal-util.h"
19 : : #include "stdio-util.h"
20 : : #include "string-util.h"
21 : : #include "user-util.h"
22 : : #include "util.h"
23 : :
24 : : #define DYNAMIC_USER_GECOS "Dynamic User"
25 : : #define DYNAMIC_USER_PASSWD "*" /* locked */
26 : : #define DYNAMIC_USER_DIR "/"
27 : : #define DYNAMIC_USER_SHELL NOLOGIN
28 : :
29 : : static const struct passwd root_passwd = {
30 : : .pw_name = (char*) "root",
31 : : .pw_passwd = (char*) "x", /* see shadow file */
32 : : .pw_uid = 0,
33 : : .pw_gid = 0,
34 : : .pw_gecos = (char*) "Super User",
35 : : .pw_dir = (char*) "/root",
36 : : .pw_shell = (char*) "/bin/sh",
37 : : };
38 : :
39 : : static const struct passwd nobody_passwd = {
40 : : .pw_name = (char*) NOBODY_USER_NAME,
41 : : .pw_passwd = (char*) "*", /* locked */
42 : : .pw_uid = UID_NOBODY,
43 : : .pw_gid = GID_NOBODY,
44 : : .pw_gecos = (char*) "User Nobody",
45 : : .pw_dir = (char*) "/",
46 : : .pw_shell = (char*) NOLOGIN,
47 : : };
48 : :
49 : : static const struct group root_group = {
50 : : .gr_name = (char*) "root",
51 : : .gr_gid = 0,
52 : : .gr_passwd = (char*) "x", /* see shadow file */
53 : : .gr_mem = (char*[]) { NULL },
54 : : };
55 : :
56 : : static const struct group nobody_group = {
57 : : .gr_name = (char*) NOBODY_GROUP_NAME,
58 : : .gr_gid = GID_NOBODY,
59 : : .gr_passwd = (char*) "*", /* locked */
60 : : .gr_mem = (char*[]) { NULL },
61 : : };
62 : :
63 : : typedef struct UserEntry UserEntry;
64 : : typedef struct GetentData GetentData;
65 : :
66 : : struct UserEntry {
67 : : uid_t id;
68 : : char *name;
69 : :
70 : : GetentData *data;
71 : : LIST_FIELDS(UserEntry, entries);
72 : : };
73 : :
74 : : struct GetentData {
75 : : /* As explained in NOTES section of getpwent_r(3) as 'getpwent_r() is not really
76 : : * reentrant since it shares the reading position in the stream with all other threads',
77 : : * we need to protect the data in UserEntry from multithreaded programs which may call
78 : : * setpwent(), getpwent_r(), or endpwent() simultaneously. So, each function locks the
79 : : * data by using the mutex below. */
80 : : pthread_mutex_t mutex;
81 : :
82 : : UserEntry *position;
83 : : LIST_HEAD(UserEntry, entries);
84 : : };
85 : :
86 : : static GetentData getpwent_data = { PTHREAD_MUTEX_INITIALIZER, NULL, NULL };
87 : : static GetentData getgrent_data = { PTHREAD_MUTEX_INITIALIZER, NULL, NULL };
88 : :
89 : : NSS_GETPW_PROTOTYPES(systemd);
90 : : NSS_GETGR_PROTOTYPES(systemd);
91 : : enum nss_status _nss_systemd_endpwent(void) _public_;
92 : : enum nss_status _nss_systemd_setpwent(int stayopen) _public_;
93 : : enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) _public_;
94 : : enum nss_status _nss_systemd_endgrent(void) _public_;
95 : : enum nss_status _nss_systemd_setgrent(int stayopen) _public_;
96 : : enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size_t buflen, int *errnop) _public_;
97 : :
98 : 0 : static int direct_lookup_name(const char *name, uid_t *ret) {
99 : 0 : _cleanup_free_ char *s = NULL;
100 : : const char *path;
101 : : int r;
102 : :
103 [ # # ]: 0 : assert(name);
104 : :
105 : : /* Normally, we go via the bus to resolve names. That has the benefit that it is available from any mount
106 : : * namespace and subject to proper authentication. However, there's one problem: if our module is called from
107 : : * dbus-daemon itself we really can't use D-Bus to communicate. In this case, resort to a client-side hack,
108 : : * and look for the dynamic names directly. This is pretty ugly, but breaks the cyclic dependency. */
109 : :
110 [ # # # # : 0 : path = strjoina("/run/systemd/dynamic-uid/direct:", name);
# # # # #
# # # ]
111 : 0 : r = readlink_malloc(path, &s);
112 [ # # ]: 0 : if (r < 0)
113 : 0 : return r;
114 : :
115 : 0 : return parse_uid(s, ret);
116 : : }
117 : :
118 : 0 : static int direct_lookup_uid(uid_t uid, char **ret) {
119 : : char path[STRLEN("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1], *s;
120 : : int r;
121 : :
122 [ # # ]: 0 : xsprintf(path, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid);
123 : :
124 : 0 : r = readlink_malloc(path, &s);
125 [ # # ]: 0 : if (r < 0)
126 : 0 : return r;
127 [ # # ]: 0 : if (!valid_user_group_name(s)) { /* extra safety check */
128 : 0 : free(s);
129 : 0 : return -EINVAL;
130 : : }
131 : :
132 : 0 : *ret = s;
133 : 0 : return 0;
134 : : }
135 : :
136 : 0 : enum nss_status _nss_systemd_getpwnam_r(
137 : : const char *name,
138 : : struct passwd *pwd,
139 : : char *buffer, size_t buflen,
140 : : int *errnop) {
141 : :
142 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
143 : 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
144 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
145 : : uint32_t translated;
146 : : size_t l;
147 : : int bypass, r;
148 : :
149 : 0 : PROTECT_ERRNO;
150 [ # # ]: 0 : BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
151 : :
152 [ # # ]: 0 : assert(name);
153 [ # # ]: 0 : assert(pwd);
154 : :
155 : : /* If the username is not valid, then we don't know it. Ideally libc would filter these for us anyway. We don't
156 : : * generate EINVAL here, because it isn't really out business to complain about invalid user names. */
157 [ # # ]: 0 : if (!valid_user_group_name(name))
158 : 0 : return NSS_STATUS_NOTFOUND;
159 : :
160 : : /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
161 [ # # ]: 0 : if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
162 [ # # ]: 0 : if (streq(name, root_passwd.pw_name)) {
163 : 0 : *pwd = root_passwd;
164 : 0 : return NSS_STATUS_SUCCESS;
165 : : }
166 [ # # ]: 0 : if (synthesize_nobody() &&
167 [ # # ]: 0 : streq(name, nobody_passwd.pw_name)) {
168 : 0 : *pwd = nobody_passwd;
169 : 0 : return NSS_STATUS_SUCCESS;
170 : : }
171 : : }
172 : :
173 : : /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
174 [ # # ]: 0 : if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
175 : 0 : return NSS_STATUS_NOTFOUND;
176 : :
177 : 0 : bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
178 [ # # ]: 0 : if (bypass <= 0) {
179 : 0 : r = sd_bus_open_system(&bus);
180 [ # # ]: 0 : if (r < 0)
181 : 0 : bypass = 1;
182 : : }
183 : :
184 [ # # ]: 0 : if (bypass > 0) {
185 : 0 : r = direct_lookup_name(name, (uid_t*) &translated);
186 [ # # ]: 0 : if (r == -ENOENT)
187 : 0 : return NSS_STATUS_NOTFOUND;
188 [ # # ]: 0 : if (r < 0)
189 : 0 : goto fail;
190 : : } else {
191 : 0 : r = sd_bus_call_method(bus,
192 : : "org.freedesktop.systemd1",
193 : : "/org/freedesktop/systemd1",
194 : : "org.freedesktop.systemd1.Manager",
195 : : "LookupDynamicUserByName",
196 : : &error,
197 : : &reply,
198 : : "s",
199 : : name);
200 [ # # ]: 0 : if (r < 0) {
201 [ # # ]: 0 : if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
202 : 0 : return NSS_STATUS_NOTFOUND;
203 : :
204 : 0 : goto fail;
205 : : }
206 : :
207 : 0 : r = sd_bus_message_read(reply, "u", &translated);
208 [ # # ]: 0 : if (r < 0)
209 : 0 : goto fail;
210 : : }
211 : :
212 : 0 : l = strlen(name);
213 [ # # ]: 0 : if (buflen < l+1) {
214 : 0 : UNPROTECT_ERRNO;
215 : 0 : *errnop = ERANGE;
216 : 0 : return NSS_STATUS_TRYAGAIN;
217 : : }
218 : :
219 : 0 : memcpy(buffer, name, l+1);
220 : :
221 : 0 : pwd->pw_name = buffer;
222 : 0 : pwd->pw_uid = (uid_t) translated;
223 : 0 : pwd->pw_gid = (uid_t) translated;
224 : 0 : pwd->pw_gecos = (char*) DYNAMIC_USER_GECOS;
225 : 0 : pwd->pw_passwd = (char*) DYNAMIC_USER_PASSWD;
226 : 0 : pwd->pw_dir = (char*) DYNAMIC_USER_DIR;
227 : 0 : pwd->pw_shell = (char*) DYNAMIC_USER_SHELL;
228 : :
229 : 0 : return NSS_STATUS_SUCCESS;
230 : :
231 : 0 : fail:
232 : 0 : UNPROTECT_ERRNO;
233 : 0 : *errnop = -r;
234 : 0 : return NSS_STATUS_UNAVAIL;
235 : : }
236 : :
237 : 0 : enum nss_status _nss_systemd_getpwuid_r(
238 : : uid_t uid,
239 : : struct passwd *pwd,
240 : : char *buffer, size_t buflen,
241 : : int *errnop) {
242 : :
243 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
244 : 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
245 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
246 : 0 : _cleanup_free_ char *direct = NULL;
247 : : const char *translated;
248 : : size_t l;
249 : : int bypass, r;
250 : :
251 : 0 : PROTECT_ERRNO;
252 [ # # ]: 0 : BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
253 : :
254 [ # # ]: 0 : if (!uid_is_valid(uid))
255 : 0 : return NSS_STATUS_NOTFOUND;
256 : :
257 : : /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
258 [ # # ]: 0 : if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
259 [ # # ]: 0 : if (uid == root_passwd.pw_uid) {
260 : 0 : *pwd = root_passwd;
261 : 0 : return NSS_STATUS_SUCCESS;
262 : : }
263 [ # # ]: 0 : if (synthesize_nobody() &&
264 [ # # ]: 0 : uid == nobody_passwd.pw_uid) {
265 : 0 : *pwd = nobody_passwd;
266 : 0 : return NSS_STATUS_SUCCESS;
267 : : }
268 : : }
269 : :
270 [ # # ]: 0 : if (!uid_is_dynamic(uid))
271 : 0 : return NSS_STATUS_NOTFOUND;
272 : :
273 [ # # ]: 0 : if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
274 : 0 : return NSS_STATUS_NOTFOUND;
275 : :
276 : 0 : bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
277 [ # # ]: 0 : if (bypass <= 0) {
278 : 0 : r = sd_bus_open_system(&bus);
279 [ # # ]: 0 : if (r < 0)
280 : 0 : bypass = 1;
281 : : }
282 : :
283 [ # # ]: 0 : if (bypass > 0) {
284 : 0 : r = direct_lookup_uid(uid, &direct);
285 [ # # ]: 0 : if (r == -ENOENT)
286 : 0 : return NSS_STATUS_NOTFOUND;
287 [ # # ]: 0 : if (r < 0)
288 : 0 : goto fail;
289 : :
290 : 0 : translated = direct;
291 : :
292 : : } else {
293 : 0 : r = sd_bus_call_method(bus,
294 : : "org.freedesktop.systemd1",
295 : : "/org/freedesktop/systemd1",
296 : : "org.freedesktop.systemd1.Manager",
297 : : "LookupDynamicUserByUID",
298 : : &error,
299 : : &reply,
300 : : "u",
301 : : (uint32_t) uid);
302 [ # # ]: 0 : if (r < 0) {
303 [ # # ]: 0 : if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
304 : 0 : return NSS_STATUS_NOTFOUND;
305 : :
306 : 0 : goto fail;
307 : : }
308 : :
309 : 0 : r = sd_bus_message_read(reply, "s", &translated);
310 [ # # ]: 0 : if (r < 0)
311 : 0 : goto fail;
312 : : }
313 : :
314 : 0 : l = strlen(translated) + 1;
315 [ # # ]: 0 : if (buflen < l) {
316 : 0 : UNPROTECT_ERRNO;
317 : 0 : *errnop = ERANGE;
318 : 0 : return NSS_STATUS_TRYAGAIN;
319 : : }
320 : :
321 : 0 : memcpy(buffer, translated, l);
322 : :
323 : 0 : pwd->pw_name = buffer;
324 : 0 : pwd->pw_uid = uid;
325 : 0 : pwd->pw_gid = uid;
326 : 0 : pwd->pw_gecos = (char*) DYNAMIC_USER_GECOS;
327 : 0 : pwd->pw_passwd = (char*) DYNAMIC_USER_PASSWD;
328 : 0 : pwd->pw_dir = (char*) DYNAMIC_USER_DIR;
329 : 0 : pwd->pw_shell = (char*) DYNAMIC_USER_SHELL;
330 : :
331 : 0 : return NSS_STATUS_SUCCESS;
332 : :
333 : 0 : fail:
334 : 0 : UNPROTECT_ERRNO;
335 : 0 : *errnop = -r;
336 : 0 : return NSS_STATUS_UNAVAIL;
337 : : }
338 : :
339 : : #pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
340 : :
341 : 0 : enum nss_status _nss_systemd_getgrnam_r(
342 : : const char *name,
343 : : struct group *gr,
344 : : char *buffer, size_t buflen,
345 : : int *errnop) {
346 : :
347 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
348 : 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
349 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
350 : : uint32_t translated;
351 : : size_t l;
352 : : int bypass, r;
353 : :
354 : 0 : PROTECT_ERRNO;
355 [ # # ]: 0 : BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
356 : :
357 [ # # ]: 0 : assert(name);
358 [ # # ]: 0 : assert(gr);
359 : :
360 [ # # ]: 0 : if (!valid_user_group_name(name))
361 : 0 : return NSS_STATUS_NOTFOUND;
362 : :
363 : : /* Synthesize records for root and nobody, in case they are missing form /etc/group */
364 [ # # ]: 0 : if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
365 [ # # ]: 0 : if (streq(name, root_group.gr_name)) {
366 : 0 : *gr = root_group;
367 : 0 : return NSS_STATUS_SUCCESS;
368 : : }
369 [ # # ]: 0 : if (synthesize_nobody() &&
370 [ # # ]: 0 : streq(name, nobody_group.gr_name)) {
371 : 0 : *gr = nobody_group;
372 : 0 : return NSS_STATUS_SUCCESS;
373 : : }
374 : : }
375 : :
376 [ # # ]: 0 : if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
377 : 0 : return NSS_STATUS_NOTFOUND;
378 : :
379 : 0 : bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
380 [ # # ]: 0 : if (bypass <= 0) {
381 : 0 : r = sd_bus_open_system(&bus);
382 [ # # ]: 0 : if (r < 0)
383 : 0 : bypass = 1;
384 : : }
385 : :
386 [ # # ]: 0 : if (bypass > 0) {
387 : 0 : r = direct_lookup_name(name, (uid_t*) &translated);
388 [ # # ]: 0 : if (r == -ENOENT)
389 : 0 : return NSS_STATUS_NOTFOUND;
390 [ # # ]: 0 : if (r < 0)
391 : 0 : goto fail;
392 : : } else {
393 : 0 : r = sd_bus_call_method(bus,
394 : : "org.freedesktop.systemd1",
395 : : "/org/freedesktop/systemd1",
396 : : "org.freedesktop.systemd1.Manager",
397 : : "LookupDynamicUserByName",
398 : : &error,
399 : : &reply,
400 : : "s",
401 : : name);
402 [ # # ]: 0 : if (r < 0) {
403 [ # # ]: 0 : if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
404 : 0 : return NSS_STATUS_NOTFOUND;
405 : :
406 : 0 : goto fail;
407 : : }
408 : :
409 : 0 : r = sd_bus_message_read(reply, "u", &translated);
410 [ # # ]: 0 : if (r < 0)
411 : 0 : goto fail;
412 : : }
413 : :
414 : 0 : l = sizeof(char*) + strlen(name) + 1;
415 [ # # ]: 0 : if (buflen < l) {
416 : 0 : UNPROTECT_ERRNO;
417 : 0 : *errnop = ERANGE;
418 : 0 : return NSS_STATUS_TRYAGAIN;
419 : : }
420 : :
421 [ # # ]: 0 : memzero(buffer, sizeof(char*));
422 : 0 : strcpy(buffer + sizeof(char*), name);
423 : :
424 : 0 : gr->gr_name = buffer + sizeof(char*);
425 : 0 : gr->gr_gid = (gid_t) translated;
426 : 0 : gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
427 : 0 : gr->gr_mem = (char**) buffer;
428 : :
429 : 0 : return NSS_STATUS_SUCCESS;
430 : :
431 : 0 : fail:
432 : 0 : UNPROTECT_ERRNO;
433 : 0 : *errnop = -r;
434 : 0 : return NSS_STATUS_UNAVAIL;
435 : : }
436 : :
437 : 0 : enum nss_status _nss_systemd_getgrgid_r(
438 : : gid_t gid,
439 : : struct group *gr,
440 : : char *buffer, size_t buflen,
441 : : int *errnop) {
442 : :
443 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
444 : 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
445 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
446 : 0 : _cleanup_free_ char *direct = NULL;
447 : : const char *translated;
448 : : size_t l;
449 : : int bypass, r;
450 : :
451 : 0 : PROTECT_ERRNO;
452 [ # # ]: 0 : BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
453 : :
454 [ # # ]: 0 : if (!gid_is_valid(gid))
455 : 0 : return NSS_STATUS_NOTFOUND;
456 : :
457 : : /* Synthesize records for root and nobody, in case they are missing from /etc/group */
458 [ # # ]: 0 : if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
459 [ # # ]: 0 : if (gid == root_group.gr_gid) {
460 : 0 : *gr = root_group;
461 : 0 : return NSS_STATUS_SUCCESS;
462 : : }
463 [ # # ]: 0 : if (synthesize_nobody() &&
464 [ # # ]: 0 : gid == nobody_group.gr_gid) {
465 : 0 : *gr = nobody_group;
466 : 0 : return NSS_STATUS_SUCCESS;
467 : : }
468 : : }
469 : :
470 [ # # ]: 0 : if (!gid_is_dynamic(gid))
471 : 0 : return NSS_STATUS_NOTFOUND;
472 : :
473 [ # # ]: 0 : if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
474 : 0 : return NSS_STATUS_NOTFOUND;
475 : :
476 : 0 : bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
477 [ # # ]: 0 : if (bypass <= 0) {
478 : 0 : r = sd_bus_open_system(&bus);
479 [ # # ]: 0 : if (r < 0)
480 : 0 : bypass = 1;
481 : : }
482 : :
483 [ # # ]: 0 : if (bypass > 0) {
484 : 0 : r = direct_lookup_uid(gid, &direct);
485 [ # # ]: 0 : if (r == -ENOENT)
486 : 0 : return NSS_STATUS_NOTFOUND;
487 [ # # ]: 0 : if (r < 0)
488 : 0 : goto fail;
489 : :
490 : 0 : translated = direct;
491 : :
492 : : } else {
493 : 0 : r = sd_bus_call_method(bus,
494 : : "org.freedesktop.systemd1",
495 : : "/org/freedesktop/systemd1",
496 : : "org.freedesktop.systemd1.Manager",
497 : : "LookupDynamicUserByUID",
498 : : &error,
499 : : &reply,
500 : : "u",
501 : : (uint32_t) gid);
502 [ # # ]: 0 : if (r < 0) {
503 [ # # ]: 0 : if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
504 : 0 : return NSS_STATUS_NOTFOUND;
505 : :
506 : 0 : goto fail;
507 : : }
508 : :
509 : 0 : r = sd_bus_message_read(reply, "s", &translated);
510 [ # # ]: 0 : if (r < 0)
511 : 0 : goto fail;
512 : : }
513 : :
514 : 0 : l = sizeof(char*) + strlen(translated) + 1;
515 [ # # ]: 0 : if (buflen < l) {
516 : 0 : UNPROTECT_ERRNO;
517 : 0 : *errnop = ERANGE;
518 : 0 : return NSS_STATUS_TRYAGAIN;
519 : : }
520 : :
521 [ # # ]: 0 : memzero(buffer, sizeof(char*));
522 : 0 : strcpy(buffer + sizeof(char*), translated);
523 : :
524 : 0 : gr->gr_name = buffer + sizeof(char*);
525 : 0 : gr->gr_gid = gid;
526 : 0 : gr->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
527 : 0 : gr->gr_mem = (char**) buffer;
528 : :
529 : 0 : return NSS_STATUS_SUCCESS;
530 : :
531 : 0 : fail:
532 : 0 : UNPROTECT_ERRNO;
533 : 0 : *errnop = -r;
534 : 0 : return NSS_STATUS_UNAVAIL;
535 : : }
536 : :
537 : 0 : static void user_entry_free(UserEntry *p) {
538 [ # # ]: 0 : if (!p)
539 : 0 : return;
540 : :
541 [ # # ]: 0 : if (p->data)
542 [ # # # # : 0 : LIST_REMOVE(entries, p->data->entries, p);
# # # # ]
543 : :
544 : 0 : free(p->name);
545 : 0 : free(p);
546 : : }
547 : :
548 : 0 : static int user_entry_add(GetentData *data, const char *name, uid_t id) {
549 : : UserEntry *p;
550 : :
551 [ # # ]: 0 : assert(data);
552 : :
553 : : /* This happens when User= or Group= already exists statically. */
554 [ # # ]: 0 : if (!uid_is_dynamic(id))
555 : 0 : return -EINVAL;
556 : :
557 : 0 : p = new0(UserEntry, 1);
558 [ # # ]: 0 : if (!p)
559 : 0 : return -ENOMEM;
560 : :
561 : 0 : p->name = strdup(name);
562 [ # # ]: 0 : if (!p->name) {
563 : 0 : free(p);
564 : 0 : return -ENOMEM;
565 : : }
566 : 0 : p->id = id;
567 : 0 : p->data = data;
568 : :
569 [ # # # # ]: 0 : LIST_PREPEND(entries, data->entries, p);
570 : :
571 : 0 : return 0;
572 : : }
573 : :
574 : 0 : static void systemd_endent(GetentData *data) {
575 : : UserEntry *p;
576 : :
577 [ # # ]: 0 : assert(data);
578 : :
579 [ # # ]: 0 : while ((p = data->entries))
580 : 0 : user_entry_free(p);
581 : :
582 : 0 : data->position = NULL;
583 : 0 : }
584 : :
585 : 0 : static enum nss_status nss_systemd_endent(GetentData *p) {
586 : 0 : PROTECT_ERRNO;
587 [ # # ]: 0 : BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
588 : :
589 [ # # ]: 0 : assert_se(pthread_mutex_lock(&p->mutex) == 0);
590 : 0 : systemd_endent(p);
591 [ # # ]: 0 : assert_se(pthread_mutex_unlock(&p->mutex) == 0);
592 : :
593 : 0 : return NSS_STATUS_SUCCESS;
594 : : }
595 : :
596 : 0 : enum nss_status _nss_systemd_endpwent(void) {
597 : 0 : return nss_systemd_endent(&getpwent_data);
598 : : }
599 : :
600 : 0 : enum nss_status _nss_systemd_endgrent(void) {
601 : 0 : return nss_systemd_endent(&getgrent_data);
602 : : }
603 : :
604 : 0 : static int direct_enumeration(GetentData *p) {
605 : 0 : _cleanup_closedir_ DIR *d = NULL;
606 : : struct dirent *de;
607 : : int r;
608 : :
609 [ # # ]: 0 : assert(p);
610 : :
611 : 0 : d = opendir("/run/systemd/dynamic-uid/");
612 [ # # ]: 0 : if (!d)
613 : 0 : return -errno;
614 : :
615 [ # # # # : 0 : FOREACH_DIRENT(de, d, return -errno) {
# # ]
616 [ # # # ]: 0 : _cleanup_free_ char *name = NULL;
617 : : uid_t uid, verified;
618 : :
619 [ # # ]: 0 : if (!dirent_is_file(de))
620 : 0 : continue;
621 : :
622 : 0 : r = parse_uid(de->d_name, &uid);
623 [ # # ]: 0 : if (r < 0)
624 : 0 : continue;
625 : :
626 : 0 : r = direct_lookup_uid(uid, &name);
627 [ # # ]: 0 : if (r == -ENOMEM)
628 : 0 : return r;
629 [ # # ]: 0 : if (r < 0)
630 : 0 : continue;
631 : :
632 : 0 : r = direct_lookup_name(name, &verified);
633 [ # # ]: 0 : if (r < 0)
634 : 0 : continue;
635 : :
636 [ # # ]: 0 : if (uid != verified)
637 : 0 : continue;
638 : :
639 : 0 : r = user_entry_add(p, name, uid);
640 [ # # ]: 0 : if (r == -ENOMEM)
641 : 0 : return r;
642 [ # # ]: 0 : if (r < 0)
643 : 0 : continue;
644 : : }
645 : :
646 : 0 : return 0;
647 : : }
648 : :
649 : 0 : static enum nss_status systemd_setent(GetentData *p) {
650 : 0 : _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
651 : 0 : _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
652 : 0 : _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
653 : : const char *name;
654 : : uid_t id;
655 : : int bypass, r;
656 : :
657 : 0 : PROTECT_ERRNO;
658 [ # # ]: 0 : BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
659 : :
660 [ # # ]: 0 : assert(p);
661 : :
662 [ # # ]: 0 : assert_se(pthread_mutex_lock(&p->mutex) == 0);
663 : :
664 : 0 : systemd_endent(p);
665 : :
666 [ # # ]: 0 : if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
667 : 0 : goto finish;
668 : :
669 : 0 : bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
670 : :
671 [ # # ]: 0 : if (bypass <= 0) {
672 : 0 : r = sd_bus_open_system(&bus);
673 [ # # ]: 0 : if (r < 0)
674 : 0 : bypass = 1;
675 : : }
676 : :
677 [ # # ]: 0 : if (bypass > 0) {
678 : 0 : r = direct_enumeration(p);
679 [ # # ]: 0 : if (r < 0)
680 : 0 : goto fail;
681 : :
682 : 0 : goto finish;
683 : : }
684 : :
685 : 0 : r = sd_bus_call_method(bus,
686 : : "org.freedesktop.systemd1",
687 : : "/org/freedesktop/systemd1",
688 : : "org.freedesktop.systemd1.Manager",
689 : : "GetDynamicUsers",
690 : : &error,
691 : : &reply,
692 : : NULL);
693 [ # # ]: 0 : if (r < 0)
694 : 0 : goto fail;
695 : :
696 : 0 : r = sd_bus_message_enter_container(reply, 'a', "(us)");
697 [ # # ]: 0 : if (r < 0)
698 : 0 : goto fail;
699 : :
700 [ # # ]: 0 : while ((r = sd_bus_message_read(reply, "(us)", &id, &name)) > 0) {
701 : 0 : r = user_entry_add(p, name, id);
702 [ # # ]: 0 : if (r == -ENOMEM)
703 : 0 : goto fail;
704 [ # # ]: 0 : if (r < 0)
705 : 0 : continue;
706 : : }
707 [ # # ]: 0 : if (r < 0)
708 : 0 : goto fail;
709 : :
710 : 0 : r = sd_bus_message_exit_container(reply);
711 [ # # ]: 0 : if (r < 0)
712 : 0 : goto fail;
713 : :
714 : 0 : finish:
715 : 0 : p->position = p->entries;
716 [ # # ]: 0 : assert_se(pthread_mutex_unlock(&p->mutex) == 0);
717 : :
718 : 0 : return NSS_STATUS_SUCCESS;
719 : :
720 : 0 : fail:
721 : 0 : systemd_endent(p);
722 [ # # ]: 0 : assert_se(pthread_mutex_unlock(&p->mutex) == 0);
723 : :
724 : 0 : return NSS_STATUS_UNAVAIL;
725 : : }
726 : :
727 : 0 : enum nss_status _nss_systemd_setpwent(int stayopen) {
728 : 0 : return systemd_setent(&getpwent_data);
729 : : }
730 : :
731 : 0 : enum nss_status _nss_systemd_setgrent(int stayopen) {
732 : 0 : return systemd_setent(&getgrent_data);
733 : : }
734 : :
735 : 0 : enum nss_status _nss_systemd_getpwent_r(struct passwd *result, char *buffer, size_t buflen, int *errnop) {
736 : : enum nss_status ret;
737 : : UserEntry *p;
738 : : size_t len;
739 : :
740 : 0 : PROTECT_ERRNO;
741 [ # # ]: 0 : BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
742 : :
743 [ # # ]: 0 : assert(result);
744 [ # # ]: 0 : assert(buffer);
745 [ # # ]: 0 : assert(errnop);
746 : :
747 [ # # ]: 0 : assert_se(pthread_mutex_lock(&getpwent_data.mutex) == 0);
748 : :
749 [ # # ]: 0 : LIST_FOREACH(entries, p, getpwent_data.position) {
750 : 0 : len = strlen(p->name) + 1;
751 [ # # ]: 0 : if (buflen < len) {
752 : 0 : UNPROTECT_ERRNO;
753 : 0 : *errnop = ERANGE;
754 : 0 : ret = NSS_STATUS_TRYAGAIN;
755 : 0 : goto finalize;
756 : : }
757 : :
758 : 0 : memcpy(buffer, p->name, len);
759 : :
760 : 0 : result->pw_name = buffer;
761 : 0 : result->pw_uid = p->id;
762 : 0 : result->pw_gid = p->id;
763 : 0 : result->pw_gecos = (char*) DYNAMIC_USER_GECOS;
764 : 0 : result->pw_passwd = (char*) DYNAMIC_USER_PASSWD;
765 : 0 : result->pw_dir = (char*) DYNAMIC_USER_DIR;
766 : 0 : result->pw_shell = (char*) DYNAMIC_USER_SHELL;
767 : 0 : break;
768 : : }
769 [ # # ]: 0 : if (!p) {
770 : 0 : ret = NSS_STATUS_NOTFOUND;
771 : 0 : goto finalize;
772 : : }
773 : :
774 : : /* On success, step to the next entry. */
775 : 0 : p = p->entries_next;
776 : 0 : ret = NSS_STATUS_SUCCESS;
777 : :
778 : 0 : finalize:
779 : : /* Save position for the next call. */
780 : 0 : getpwent_data.position = p;
781 : :
782 [ # # ]: 0 : assert_se(pthread_mutex_unlock(&getpwent_data.mutex) == 0);
783 : :
784 : 0 : return ret;
785 : : }
786 : :
787 : 0 : enum nss_status _nss_systemd_getgrent_r(struct group *result, char *buffer, size_t buflen, int *errnop) {
788 : : enum nss_status ret;
789 : : UserEntry *p;
790 : : size_t len;
791 : :
792 : 0 : PROTECT_ERRNO;
793 [ # # ]: 0 : BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
794 : :
795 [ # # ]: 0 : assert(result);
796 [ # # ]: 0 : assert(buffer);
797 [ # # ]: 0 : assert(errnop);
798 : :
799 [ # # ]: 0 : assert_se(pthread_mutex_lock(&getgrent_data.mutex) == 0);
800 : :
801 [ # # ]: 0 : LIST_FOREACH(entries, p, getgrent_data.position) {
802 : 0 : len = sizeof(char*) + strlen(p->name) + 1;
803 [ # # ]: 0 : if (buflen < len) {
804 : 0 : UNPROTECT_ERRNO;
805 : 0 : *errnop = ERANGE;
806 : 0 : ret = NSS_STATUS_TRYAGAIN;
807 : 0 : goto finalize;
808 : : }
809 : :
810 [ # # ]: 0 : memzero(buffer, sizeof(char*));
811 : 0 : strcpy(buffer + sizeof(char*), p->name);
812 : :
813 : 0 : result->gr_name = buffer + sizeof(char*);
814 : 0 : result->gr_gid = p->id;
815 : 0 : result->gr_passwd = (char*) DYNAMIC_USER_PASSWD;
816 : 0 : result->gr_mem = (char**) buffer;
817 : 0 : break;
818 : : }
819 [ # # ]: 0 : if (!p) {
820 : 0 : ret = NSS_STATUS_NOTFOUND;
821 : 0 : goto finalize;
822 : : }
823 : :
824 : : /* On success, step to the next entry. */
825 : 0 : p = p->entries_next;
826 : 0 : ret = NSS_STATUS_SUCCESS;
827 : :
828 : 0 : finalize:
829 : : /* Save position for the next call. */
830 : 0 : getgrent_data.position = p;
831 : :
832 [ # # ]: 0 : assert_se(pthread_mutex_unlock(&getgrent_data.mutex) == 0);
833 : :
834 : 0 : return ret;
835 : : }
|