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