]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-systemd/nss-systemd.c
nss-systemd,user-util: add a way how synthesizing "nobody" can be turned off
[thirdparty/systemd.git] / src / nss-systemd / nss-systemd.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
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
25 #include "alloc-util.h"
26 #include "bus-common-errors.h"
27 #include "env-util.h"
28 #include "fs-util.h"
29 #include "macro.h"
30 #include "nss-util.h"
31 #include "signal-util.h"
32 #include "stdio-util.h"
33 #include "string-util.h"
34 #include "user-util.h"
35 #include "util.h"
36
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 */
50 .pw_uid = UID_NOBODY,
51 .pw_gid = GID_NOBODY,
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,
66 .gr_gid = GID_NOBODY,
67 .gr_passwd = (char*) "*", /* locked */
68 .gr_mem = (char*[]) { NULL },
69 };
70
71 NSS_GETPW_PROTOTYPES(systemd);
72 NSS_GETGR_PROTOTYPES(systemd);
73
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) {
95 char path[STRLEN("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1], *s;
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
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
118 uint32_t translated;
119 size_t l;
120 int bypass, r;
121
122 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
123
124 assert(name);
125 assert(pwd);
126
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;
131
132 /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
133 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
134 if (streq(name, root_passwd.pw_name)) {
135 *pwd = root_passwd;
136 *errnop = 0;
137 return NSS_STATUS_SUCCESS;
138 }
139 if (synthesize_nobody() &&
140 streq(name, nobody_passwd.pw_name)) {
141 *pwd = nobody_passwd;
142 *errnop = 0;
143 return NSS_STATUS_SUCCESS;
144 }
145 }
146
147 /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
148 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
149 goto not_found;
150
151 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
152 if (bypass <= 0) {
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);
158 if (r < 0) {
159 bypass = 1;
160 goto direct_lookup;
161 }
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;
182 }
183
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
194 l = strlen(name);
195 if (buflen < l+1) {
196 *errnop = ERANGE;
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;
231 _cleanup_free_ char *direct = NULL;
232 const char *translated;
233 size_t l;
234 int bypass, r;
235
236 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
237
238 if (!uid_is_valid(uid))
239 goto not_found;
240
241 /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
242 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
243 if (uid == root_passwd.pw_uid) {
244 *pwd = root_passwd;
245 *errnop = 0;
246 return NSS_STATUS_SUCCESS;
247 }
248 if (synthesize_nobody() &&
249 uid == nobody_passwd.pw_uid) {
250 *pwd = nobody_passwd;
251 *errnop = 0;
252 return NSS_STATUS_SUCCESS;
253 }
254 }
255
256 if (!uid_is_dynamic(uid))
257 goto not_found;
258
259 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
260 goto not_found;
261
262 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
263 if (bypass <= 0) {
264 r = sd_bus_open_system(&bus);
265 if (r < 0) {
266 bypass = 1;
267 goto direct_lookup;
268 }
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;
289 }
290
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
303 l = strlen(translated) + 1;
304 if (buflen < l) {
305 *errnop = ERANGE;
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
331 enum nss_status _nss_systemd_getgrnam_r(
332 const char *name,
333 struct group *gr,
334 char *buffer, size_t buflen,
335 int *errnop) {
336
337 uint32_t translated;
338 size_t l;
339 int bypass, r;
340
341 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
342
343 assert(name);
344 assert(gr);
345
346 if (!valid_user_group_name(name))
347 goto not_found;
348
349 /* Synthesize records for root and nobody, in case they are missing form /etc/group */
350 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
351 if (streq(name, root_group.gr_name)) {
352 *gr = root_group;
353 *errnop = 0;
354 return NSS_STATUS_SUCCESS;
355 }
356 if (synthesize_nobody() &&
357 streq(name, nobody_group.gr_name)) {
358 *gr = nobody_group;
359 *errnop = 0;
360 return NSS_STATUS_SUCCESS;
361 }
362 }
363
364 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
365 goto not_found;
366
367 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
368 if (bypass <= 0) {
369 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
370 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
371 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
372
373 r = sd_bus_open_system(&bus);
374 if (r < 0) {
375 bypass = 1;
376 goto direct_lookup;
377 }
378
379 r = sd_bus_call_method(bus,
380 "org.freedesktop.systemd1",
381 "/org/freedesktop/systemd1",
382 "org.freedesktop.systemd1.Manager",
383 "LookupDynamicUserByName",
384 &error,
385 &reply,
386 "s",
387 name);
388 if (r < 0) {
389 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
390 goto not_found;
391
392 goto fail;
393 }
394
395 r = sd_bus_message_read(reply, "u", &translated);
396 if (r < 0)
397 goto fail;
398 }
399
400 direct_lookup:
401 if (bypass > 0) {
402 /* Access the dynamic GID allocation directly if we are called from dbus-daemon, see above. */
403 r = direct_lookup_name(name, (uid_t*) &translated);
404 if (r == -ENOENT)
405 goto not_found;
406 if (r < 0)
407 goto fail;
408 }
409
410 l = sizeof(char*) + strlen(name) + 1;
411 if (buflen < l) {
412 *errnop = ERANGE;
413 return NSS_STATUS_TRYAGAIN;
414 }
415
416 memzero(buffer, sizeof(char*));
417 strcpy(buffer + sizeof(char*), name);
418
419 gr->gr_name = buffer + sizeof(char*);
420 gr->gr_gid = (gid_t) translated;
421 gr->gr_passwd = (char*) "*"; /* locked */
422 gr->gr_mem = (char**) buffer;
423
424 *errnop = 0;
425 return NSS_STATUS_SUCCESS;
426
427 not_found:
428 *errnop = 0;
429 return NSS_STATUS_NOTFOUND;
430
431 fail:
432 *errnop = -r;
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;
445 _cleanup_free_ char *direct = NULL;
446 const char *translated;
447 size_t l;
448 int bypass, r;
449
450 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
451
452 if (!gid_is_valid(gid))
453 goto not_found;
454
455 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
456 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
457 if (gid == root_group.gr_gid) {
458 *gr = root_group;
459 *errnop = 0;
460 return NSS_STATUS_SUCCESS;
461 }
462 if (synthesize_nobody() &&
463 gid == nobody_group.gr_gid) {
464 *gr = nobody_group;
465 *errnop = 0;
466 return NSS_STATUS_SUCCESS;
467 }
468 }
469
470 if (!gid_is_dynamic(gid))
471 goto not_found;
472
473 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
474 goto not_found;
475
476 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
477 if (bypass <= 0) {
478 r = sd_bus_open_system(&bus);
479 if (r < 0) {
480 bypass = 1;
481 goto direct_lookup;
482 }
483
484 r = sd_bus_call_method(bus,
485 "org.freedesktop.systemd1",
486 "/org/freedesktop/systemd1",
487 "org.freedesktop.systemd1.Manager",
488 "LookupDynamicUserByUID",
489 &error,
490 &reply,
491 "u",
492 (uint32_t) gid);
493 if (r < 0) {
494 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
495 goto not_found;
496
497 goto fail;
498 }
499
500 r = sd_bus_message_read(reply, "s", &translated);
501 if (r < 0)
502 goto fail;
503 }
504
505 direct_lookup:
506 if (bypass > 0) {
507 r = direct_lookup_uid(gid, &direct);
508 if (r == -ENOENT)
509 goto not_found;
510 if (r < 0)
511 goto fail;
512
513 translated = direct;
514 }
515
516 l = sizeof(char*) + strlen(translated) + 1;
517 if (buflen < l) {
518 *errnop = ERANGE;
519 return NSS_STATUS_TRYAGAIN;
520 }
521
522 memzero(buffer, sizeof(char*));
523 strcpy(buffer + sizeof(char*), translated);
524
525 gr->gr_name = buffer + sizeof(char*);
526 gr->gr_gid = gid;
527 gr->gr_passwd = (char*) "*"; /* locked */
528 gr->gr_mem = (char**) buffer;
529
530 *errnop = 0;
531 return NSS_STATUS_SUCCESS;
532
533 not_found:
534 *errnop = 0;
535 return NSS_STATUS_NOTFOUND;
536
537 fail:
538 *errnop = -r;
539 return NSS_STATUS_UNAVAIL;
540 }