]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/nss-systemd/nss-systemd.c
Merge pull request #8086 from hdante/sdboot-setmode-v2
[thirdparty/systemd.git] / src / nss-systemd / nss-systemd.c
CommitLineData
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
37static 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
47static 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
57static 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
64static 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
71NSS_GETPW_PROTOTYPES(systemd);
72NSS_GETGR_PROTOTYPES(systemd);
73
fd63e712
LP
74static 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
94static 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
112enum 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
184direct_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
213not_found:
214 *errnop = 0;
215 return NSS_STATUS_NOTFOUND;
216
217fail:
218 *errnop = -r;
219 return NSS_STATUS_UNAVAIL;
220}
221
222enum 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
291direct_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
322not_found:
323 *errnop = 0;
324 return NSS_STATUS_NOTFOUND;
325
326fail:
327 *errnop = -r;
328 return NSS_STATUS_UNAVAIL;
329}
330
3c3d384a
ZJS
331#pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
332
409093fe
LP
333enum 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
402direct_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
429not_found:
430 *errnop = 0;
431 return NSS_STATUS_NOTFOUND;
432
433fail:
434 *errnop = -r;
435 return NSS_STATUS_UNAVAIL;
436}
437
438enum 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
507direct_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
535not_found:
536 *errnop = 0;
537 return NSS_STATUS_NOTFOUND;
538
539fail:
540 *errnop = -r;
541 return NSS_STATUS_UNAVAIL;
542}