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