]>
Commit | Line | Data |
---|---|---|
53e1b683 | 1 | /* SPDX-License-Identifier: LGPL-2.1+ */ |
409093fe LP |
2 | /*** |
3 | This file is part of systemd. | |
4 | ||
5 | Copyright 2016 Lennart Poettering | |
6 | ||
7 | systemd is free software; you can redistribute it and/or modify it | |
8 | under the terms of the GNU Lesser General Public License as published by | |
9 | the Free Software Foundation; either version 2.1 of the License, or | |
10 | (at your option) any later version. | |
11 | ||
12 | systemd is distributed in the hope that it will be useful, but | |
13 | WITHOUT ANY WARRANTY; without even the implied warranty of | |
14 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
15 | Lesser General Public License for more details. | |
16 | ||
17 | You should have received a copy of the GNU Lesser General Public License | |
18 | along with systemd; If not, see <http://www.gnu.org/licenses/>. | |
19 | ***/ | |
20 | ||
21 | #include <nss.h> | |
22 | ||
23 | #include "sd-bus.h" | |
24 | ||
fd63e712 | 25 | #include "alloc-util.h" |
409093fe LP |
26 | #include "bus-common-errors.h" |
27 | #include "env-util.h" | |
fd63e712 | 28 | #include "fs-util.h" |
409093fe LP |
29 | #include "macro.h" |
30 | #include "nss-util.h" | |
31 | #include "signal-util.h" | |
fd63e712 | 32 | #include "stdio-util.h" |
2129011e | 33 | #include "string-util.h" |
409093fe LP |
34 | #include "user-util.h" |
35 | #include "util.h" | |
36 | ||
2129011e LP |
37 | static const struct passwd root_passwd = { |
38 | .pw_name = (char*) "root", | |
39 | .pw_passwd = (char*) "x", /* see shadow file */ | |
40 | .pw_uid = 0, | |
41 | .pw_gid = 0, | |
42 | .pw_gecos = (char*) "Super User", | |
43 | .pw_dir = (char*) "/root", | |
44 | .pw_shell = (char*) "/bin/sh", | |
45 | }; | |
46 | ||
47 | static const struct passwd nobody_passwd = { | |
48 | .pw_name = (char*) NOBODY_USER_NAME, | |
49 | .pw_passwd = (char*) "*", /* locked */ | |
3a664727 LP |
50 | .pw_uid = UID_NOBODY, |
51 | .pw_gid = GID_NOBODY, | |
2129011e LP |
52 | .pw_gecos = (char*) "User Nobody", |
53 | .pw_dir = (char*) "/", | |
54 | .pw_shell = (char*) "/sbin/nologin", | |
55 | }; | |
56 | ||
57 | static const struct group root_group = { | |
58 | .gr_name = (char*) "root", | |
59 | .gr_gid = 0, | |
60 | .gr_passwd = (char*) "x", /* see shadow file */ | |
61 | .gr_mem = (char*[]) { NULL }, | |
62 | }; | |
63 | ||
64 | static const struct group nobody_group = { | |
65 | .gr_name = (char*) NOBODY_GROUP_NAME, | |
3a664727 | 66 | .gr_gid = GID_NOBODY, |
2129011e LP |
67 | .gr_passwd = (char*) "*", /* locked */ |
68 | .gr_mem = (char*[]) { NULL }, | |
69 | }; | |
70 | ||
409093fe LP |
71 | NSS_GETPW_PROTOTYPES(systemd); |
72 | NSS_GETGR_PROTOTYPES(systemd); | |
73 | ||
fd63e712 LP |
74 | static int direct_lookup_name(const char *name, uid_t *ret) { |
75 | _cleanup_free_ char *s = NULL; | |
76 | const char *path; | |
77 | int r; | |
78 | ||
79 | assert(name); | |
80 | ||
81 | /* Normally, we go via the bus to resolve names. That has the benefit that it is available from any mount | |
82 | * namespace and subject to proper authentication. However, there's one problem: if our module is called from | |
83 | * dbus-daemon itself we really can't use D-Bus to communicate. In this case, resort to a client-side hack, | |
84 | * and look for the dynamic names directly. This is pretty ugly, but breaks the cyclic dependency. */ | |
85 | ||
86 | path = strjoina("/run/systemd/dynamic-uid/direct:", name); | |
87 | r = readlink_malloc(path, &s); | |
88 | if (r < 0) | |
89 | return r; | |
90 | ||
91 | return parse_uid(s, ret); | |
92 | } | |
93 | ||
94 | static int direct_lookup_uid(uid_t uid, char **ret) { | |
fbd0b64f | 95 | char path[STRLEN("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1], *s; |
fd63e712 LP |
96 | int r; |
97 | ||
98 | xsprintf(path, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid); | |
99 | ||
100 | r = readlink_malloc(path, &s); | |
101 | if (r < 0) | |
102 | return r; | |
103 | if (!valid_user_group_name(s)) { /* extra safety check */ | |
104 | free(s); | |
105 | return -EINVAL; | |
106 | } | |
107 | ||
108 | *ret = s; | |
109 | return 0; | |
110 | } | |
111 | ||
409093fe LP |
112 | enum nss_status _nss_systemd_getpwnam_r( |
113 | const char *name, | |
114 | struct passwd *pwd, | |
115 | char *buffer, size_t buflen, | |
116 | int *errnop) { | |
117 | ||
409093fe LP |
118 | uint32_t translated; |
119 | size_t l; | |
689ca202 | 120 | int bypass, r; |
409093fe LP |
121 | |
122 | BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); | |
123 | ||
124 | assert(name); | |
125 | assert(pwd); | |
126 | ||
d6c575e3 LP |
127 | /* If the username is not valid, then we don't know it. Ideally libc would filter these for us anyway. We don't |
128 | * generate EINVAL here, because it isn't really out business to complain about invalid user names. */ | |
129 | if (!valid_user_group_name(name)) | |
130 | goto not_found; | |
2129011e LP |
131 | |
132 | /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */ | |
71e0accc | 133 | if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) { |
fe102d6a LP |
134 | if (streq(name, root_passwd.pw_name)) { |
135 | *pwd = root_passwd; | |
136 | *errnop = 0; | |
137 | return NSS_STATUS_SUCCESS; | |
138 | } | |
24eccc34 LP |
139 | if (synthesize_nobody() && |
140 | streq(name, nobody_passwd.pw_name)) { | |
fe102d6a LP |
141 | *pwd = nobody_passwd; |
142 | *errnop = 0; | |
143 | return NSS_STATUS_SUCCESS; | |
144 | } | |
2129011e LP |
145 | } |
146 | ||
409093fe | 147 | /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */ |
71e0accc | 148 | if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) |
409093fe LP |
149 | goto not_found; |
150 | ||
689ca202 YW |
151 | bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS"); |
152 | if (bypass <= 0) { | |
fd63e712 LP |
153 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; |
154 | _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL; | |
155 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; | |
156 | ||
157 | r = sd_bus_open_system(&bus); | |
689ca202 YW |
158 | if (r < 0) { |
159 | bypass = 1; | |
160 | goto direct_lookup; | |
161 | } | |
fd63e712 LP |
162 | |
163 | r = sd_bus_call_method(bus, | |
164 | "org.freedesktop.systemd1", | |
165 | "/org/freedesktop/systemd1", | |
166 | "org.freedesktop.systemd1.Manager", | |
167 | "LookupDynamicUserByName", | |
168 | &error, | |
169 | &reply, | |
170 | "s", | |
171 | name); | |
172 | if (r < 0) { | |
173 | if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER)) | |
174 | goto not_found; | |
175 | ||
176 | goto fail; | |
177 | } | |
178 | ||
179 | r = sd_bus_message_read(reply, "u", &translated); | |
180 | if (r < 0) | |
181 | goto fail; | |
409093fe LP |
182 | } |
183 | ||
689ca202 YW |
184 | direct_lookup: |
185 | if (bypass > 0) { | |
186 | /* Access the dynamic UID allocation directly if we are called from dbus-daemon, see above. */ | |
187 | r = direct_lookup_name(name, (uid_t*) &translated); | |
188 | if (r == -ENOENT) | |
189 | goto not_found; | |
190 | if (r < 0) | |
191 | goto fail; | |
192 | } | |
193 | ||
409093fe LP |
194 | l = strlen(name); |
195 | if (buflen < l+1) { | |
cda458a5 | 196 | *errnop = ERANGE; |
409093fe LP |
197 | return NSS_STATUS_TRYAGAIN; |
198 | } | |
199 | ||
200 | memcpy(buffer, name, l+1); | |
201 | ||
202 | pwd->pw_name = buffer; | |
203 | pwd->pw_uid = (uid_t) translated; | |
204 | pwd->pw_gid = (uid_t) translated; | |
205 | pwd->pw_gecos = (char*) "Dynamic User"; | |
206 | pwd->pw_passwd = (char*) "*"; /* locked */ | |
207 | pwd->pw_dir = (char*) "/"; | |
208 | pwd->pw_shell = (char*) "/sbin/nologin"; | |
209 | ||
210 | *errnop = 0; | |
211 | return NSS_STATUS_SUCCESS; | |
212 | ||
213 | not_found: | |
214 | *errnop = 0; | |
215 | return NSS_STATUS_NOTFOUND; | |
216 | ||
217 | fail: | |
218 | *errnop = -r; | |
219 | return NSS_STATUS_UNAVAIL; | |
220 | } | |
221 | ||
222 | enum nss_status _nss_systemd_getpwuid_r( | |
223 | uid_t uid, | |
224 | struct passwd *pwd, | |
225 | char *buffer, size_t buflen, | |
226 | int *errnop) { | |
227 | ||
228 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
229 | _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL; | |
230 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; | |
fd63e712 | 231 | _cleanup_free_ char *direct = NULL; |
409093fe LP |
232 | const char *translated; |
233 | size_t l; | |
689ca202 | 234 | int bypass, r; |
409093fe LP |
235 | |
236 | BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); | |
237 | ||
d6c575e3 LP |
238 | if (!uid_is_valid(uid)) |
239 | goto not_found; | |
409093fe | 240 | |
2129011e | 241 | /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */ |
71e0accc | 242 | if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) { |
fe102d6a LP |
243 | if (uid == root_passwd.pw_uid) { |
244 | *pwd = root_passwd; | |
245 | *errnop = 0; | |
246 | return NSS_STATUS_SUCCESS; | |
247 | } | |
24eccc34 LP |
248 | if (synthesize_nobody() && |
249 | uid == nobody_passwd.pw_uid) { | |
fe102d6a LP |
250 | *pwd = nobody_passwd; |
251 | *errnop = 0; | |
252 | return NSS_STATUS_SUCCESS; | |
253 | } | |
2129011e LP |
254 | } |
255 | ||
83438277 | 256 | if (!uid_is_dynamic(uid)) |
409093fe LP |
257 | goto not_found; |
258 | ||
71e0accc | 259 | if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) |
409093fe LP |
260 | goto not_found; |
261 | ||
689ca202 YW |
262 | bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS"); |
263 | if (bypass <= 0) { | |
fd63e712 | 264 | r = sd_bus_open_system(&bus); |
689ca202 YW |
265 | if (r < 0) { |
266 | bypass = 1; | |
267 | goto direct_lookup; | |
268 | } | |
fd63e712 LP |
269 | |
270 | r = sd_bus_call_method(bus, | |
271 | "org.freedesktop.systemd1", | |
272 | "/org/freedesktop/systemd1", | |
273 | "org.freedesktop.systemd1.Manager", | |
274 | "LookupDynamicUserByUID", | |
275 | &error, | |
276 | &reply, | |
277 | "u", | |
278 | (uint32_t) uid); | |
279 | if (r < 0) { | |
280 | if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER)) | |
281 | goto not_found; | |
282 | ||
283 | goto fail; | |
284 | } | |
285 | ||
286 | r = sd_bus_message_read(reply, "s", &translated); | |
287 | if (r < 0) | |
288 | goto fail; | |
409093fe LP |
289 | } |
290 | ||
689ca202 YW |
291 | direct_lookup: |
292 | if (bypass > 0) { | |
293 | r = direct_lookup_uid(uid, &direct); | |
294 | if (r == -ENOENT) | |
295 | goto not_found; | |
296 | if (r < 0) | |
297 | goto fail; | |
298 | ||
299 | translated = direct; | |
300 | ||
301 | } | |
302 | ||
409093fe LP |
303 | l = strlen(translated) + 1; |
304 | if (buflen < l) { | |
cda458a5 | 305 | *errnop = ERANGE; |
409093fe LP |
306 | return NSS_STATUS_TRYAGAIN; |
307 | } | |
308 | ||
309 | memcpy(buffer, translated, l); | |
310 | ||
311 | pwd->pw_name = buffer; | |
312 | pwd->pw_uid = uid; | |
313 | pwd->pw_gid = uid; | |
314 | pwd->pw_gecos = (char*) "Dynamic User"; | |
315 | pwd->pw_passwd = (char*) "*"; /* locked */ | |
316 | pwd->pw_dir = (char*) "/"; | |
317 | pwd->pw_shell = (char*) "/sbin/nologin"; | |
318 | ||
319 | *errnop = 0; | |
320 | return NSS_STATUS_SUCCESS; | |
321 | ||
322 | not_found: | |
323 | *errnop = 0; | |
324 | return NSS_STATUS_NOTFOUND; | |
325 | ||
326 | fail: | |
327 | *errnop = -r; | |
328 | return NSS_STATUS_UNAVAIL; | |
329 | } | |
330 | ||
3c3d384a ZJS |
331 | #pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess" |
332 | ||
409093fe LP |
333 | enum nss_status _nss_systemd_getgrnam_r( |
334 | const char *name, | |
335 | struct group *gr, | |
336 | char *buffer, size_t buflen, | |
337 | int *errnop) { | |
338 | ||
409093fe LP |
339 | uint32_t translated; |
340 | size_t l; | |
689ca202 | 341 | int bypass, r; |
409093fe LP |
342 | |
343 | BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); | |
344 | ||
345 | assert(name); | |
346 | assert(gr); | |
347 | ||
d6c575e3 LP |
348 | if (!valid_user_group_name(name)) |
349 | goto not_found; | |
2129011e LP |
350 | |
351 | /* Synthesize records for root and nobody, in case they are missing form /etc/group */ | |
71e0accc | 352 | if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) { |
fe102d6a LP |
353 | if (streq(name, root_group.gr_name)) { |
354 | *gr = root_group; | |
355 | *errnop = 0; | |
356 | return NSS_STATUS_SUCCESS; | |
357 | } | |
24eccc34 LP |
358 | if (synthesize_nobody() && |
359 | streq(name, nobody_group.gr_name)) { | |
fe102d6a LP |
360 | *gr = nobody_group; |
361 | *errnop = 0; | |
362 | return NSS_STATUS_SUCCESS; | |
363 | } | |
2129011e LP |
364 | } |
365 | ||
71e0accc | 366 | if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) |
409093fe LP |
367 | goto not_found; |
368 | ||
689ca202 YW |
369 | bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS"); |
370 | if (bypass <= 0) { | |
fd63e712 LP |
371 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; |
372 | _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL; | |
373 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; | |
374 | ||
375 | r = sd_bus_open_system(&bus); | |
689ca202 YW |
376 | if (r < 0) { |
377 | bypass = 1; | |
378 | goto direct_lookup; | |
379 | } | |
fd63e712 LP |
380 | |
381 | r = sd_bus_call_method(bus, | |
382 | "org.freedesktop.systemd1", | |
383 | "/org/freedesktop/systemd1", | |
384 | "org.freedesktop.systemd1.Manager", | |
385 | "LookupDynamicUserByName", | |
386 | &error, | |
387 | &reply, | |
388 | "s", | |
389 | name); | |
390 | if (r < 0) { | |
391 | if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER)) | |
392 | goto not_found; | |
393 | ||
394 | goto fail; | |
395 | } | |
396 | ||
397 | r = sd_bus_message_read(reply, "u", &translated); | |
398 | if (r < 0) | |
399 | goto fail; | |
409093fe LP |
400 | } |
401 | ||
689ca202 YW |
402 | direct_lookup: |
403 | if (bypass > 0) { | |
404 | /* Access the dynamic GID allocation directly if we are called from dbus-daemon, see above. */ | |
405 | r = direct_lookup_name(name, (uid_t*) &translated); | |
406 | if (r == -ENOENT) | |
407 | goto not_found; | |
408 | if (r < 0) | |
409 | goto fail; | |
410 | } | |
411 | ||
409093fe LP |
412 | l = sizeof(char*) + strlen(name) + 1; |
413 | if (buflen < l) { | |
cda458a5 | 414 | *errnop = ERANGE; |
409093fe LP |
415 | return NSS_STATUS_TRYAGAIN; |
416 | } | |
417 | ||
418 | memzero(buffer, sizeof(char*)); | |
419 | strcpy(buffer + sizeof(char*), name); | |
420 | ||
421 | gr->gr_name = buffer + sizeof(char*); | |
422 | gr->gr_gid = (gid_t) translated; | |
423 | gr->gr_passwd = (char*) "*"; /* locked */ | |
424 | gr->gr_mem = (char**) buffer; | |
425 | ||
426 | *errnop = 0; | |
427 | return NSS_STATUS_SUCCESS; | |
428 | ||
429 | not_found: | |
430 | *errnop = 0; | |
431 | return NSS_STATUS_NOTFOUND; | |
432 | ||
433 | fail: | |
434 | *errnop = -r; | |
435 | return NSS_STATUS_UNAVAIL; | |
436 | } | |
437 | ||
438 | enum nss_status _nss_systemd_getgrgid_r( | |
439 | gid_t gid, | |
440 | struct group *gr, | |
441 | char *buffer, size_t buflen, | |
442 | int *errnop) { | |
443 | ||
444 | _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL; | |
445 | _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL; | |
446 | _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL; | |
fd63e712 | 447 | _cleanup_free_ char *direct = NULL; |
409093fe LP |
448 | const char *translated; |
449 | size_t l; | |
689ca202 | 450 | int bypass, r; |
409093fe LP |
451 | |
452 | BLOCK_SIGNALS(NSS_SIGNALS_BLOCK); | |
453 | ||
d6c575e3 LP |
454 | if (!gid_is_valid(gid)) |
455 | goto not_found; | |
409093fe | 456 | |
2129011e | 457 | /* Synthesize records for root and nobody, in case they are missing from /etc/group */ |
71e0accc | 458 | if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) { |
fe102d6a LP |
459 | if (gid == root_group.gr_gid) { |
460 | *gr = root_group; | |
461 | *errnop = 0; | |
462 | return NSS_STATUS_SUCCESS; | |
463 | } | |
24eccc34 LP |
464 | if (synthesize_nobody() && |
465 | gid == nobody_group.gr_gid) { | |
fe102d6a LP |
466 | *gr = nobody_group; |
467 | *errnop = 0; | |
468 | return NSS_STATUS_SUCCESS; | |
469 | } | |
2129011e LP |
470 | } |
471 | ||
83438277 | 472 | if (!gid_is_dynamic(gid)) |
409093fe LP |
473 | goto not_found; |
474 | ||
71e0accc | 475 | if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0) |
409093fe LP |
476 | goto not_found; |
477 | ||
689ca202 YW |
478 | bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS"); |
479 | if (bypass <= 0) { | |
fd63e712 | 480 | r = sd_bus_open_system(&bus); |
689ca202 YW |
481 | if (r < 0) { |
482 | bypass = 1; | |
483 | goto direct_lookup; | |
484 | } | |
fd63e712 LP |
485 | |
486 | r = sd_bus_call_method(bus, | |
487 | "org.freedesktop.systemd1", | |
488 | "/org/freedesktop/systemd1", | |
489 | "org.freedesktop.systemd1.Manager", | |
490 | "LookupDynamicUserByUID", | |
491 | &error, | |
492 | &reply, | |
493 | "u", | |
494 | (uint32_t) gid); | |
495 | if (r < 0) { | |
496 | if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER)) | |
497 | goto not_found; | |
498 | ||
499 | goto fail; | |
500 | } | |
501 | ||
502 | r = sd_bus_message_read(reply, "s", &translated); | |
503 | if (r < 0) | |
504 | goto fail; | |
409093fe LP |
505 | } |
506 | ||
689ca202 YW |
507 | direct_lookup: |
508 | if (bypass > 0) { | |
689ca202 YW |
509 | r = direct_lookup_uid(gid, &direct); |
510 | if (r == -ENOENT) | |
511 | goto not_found; | |
512 | if (r < 0) | |
513 | goto fail; | |
514 | ||
515 | translated = direct; | |
516 | } | |
517 | ||
409093fe LP |
518 | l = sizeof(char*) + strlen(translated) + 1; |
519 | if (buflen < l) { | |
cda458a5 | 520 | *errnop = ERANGE; |
409093fe LP |
521 | return NSS_STATUS_TRYAGAIN; |
522 | } | |
523 | ||
524 | memzero(buffer, sizeof(char*)); | |
525 | strcpy(buffer + sizeof(char*), translated); | |
526 | ||
527 | gr->gr_name = buffer + sizeof(char*); | |
528 | gr->gr_gid = gid; | |
529 | gr->gr_passwd = (char*) "*"; /* locked */ | |
530 | gr->gr_mem = (char**) buffer; | |
531 | ||
532 | *errnop = 0; | |
533 | return NSS_STATUS_SUCCESS; | |
534 | ||
535 | not_found: | |
536 | *errnop = 0; | |
537 | return NSS_STATUS_NOTFOUND; | |
538 | ||
539 | fail: | |
540 | *errnop = -r; | |
541 | return NSS_STATUS_UNAVAIL; | |
542 | } |