]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-systemd/nss-systemd.c
Add SPDX license identifiers to source files under the LGPL
[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 = 65534,
51 .pw_gid = 65534,
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 = 65534,
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 (streq(name, nobody_passwd.pw_name)) {
140 *pwd = nobody_passwd;
141 *errnop = 0;
142 return NSS_STATUS_SUCCESS;
143 }
144 }
145
146 /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
147 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
148 goto not_found;
149
150 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
151 if (bypass <= 0) {
152 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
153 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
154 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
155
156 r = sd_bus_open_system(&bus);
157 if (r < 0) {
158 bypass = 1;
159 goto direct_lookup;
160 }
161
162 r = sd_bus_call_method(bus,
163 "org.freedesktop.systemd1",
164 "/org/freedesktop/systemd1",
165 "org.freedesktop.systemd1.Manager",
166 "LookupDynamicUserByName",
167 &error,
168 &reply,
169 "s",
170 name);
171 if (r < 0) {
172 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
173 goto not_found;
174
175 goto fail;
176 }
177
178 r = sd_bus_message_read(reply, "u", &translated);
179 if (r < 0)
180 goto fail;
181 }
182
183 direct_lookup:
184 if (bypass > 0) {
185 /* Access the dynamic UID allocation directly if we are called from dbus-daemon, see above. */
186 r = direct_lookup_name(name, (uid_t*) &translated);
187 if (r == -ENOENT)
188 goto not_found;
189 if (r < 0)
190 goto fail;
191 }
192
193 l = strlen(name);
194 if (buflen < l+1) {
195 *errnop = ERANGE;
196 return NSS_STATUS_TRYAGAIN;
197 }
198
199 memcpy(buffer, name, l+1);
200
201 pwd->pw_name = buffer;
202 pwd->pw_uid = (uid_t) translated;
203 pwd->pw_gid = (uid_t) translated;
204 pwd->pw_gecos = (char*) "Dynamic User";
205 pwd->pw_passwd = (char*) "*"; /* locked */
206 pwd->pw_dir = (char*) "/";
207 pwd->pw_shell = (char*) "/sbin/nologin";
208
209 *errnop = 0;
210 return NSS_STATUS_SUCCESS;
211
212 not_found:
213 *errnop = 0;
214 return NSS_STATUS_NOTFOUND;
215
216 fail:
217 *errnop = -r;
218 return NSS_STATUS_UNAVAIL;
219 }
220
221 enum nss_status _nss_systemd_getpwuid_r(
222 uid_t uid,
223 struct passwd *pwd,
224 char *buffer, size_t buflen,
225 int *errnop) {
226
227 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
228 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
229 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
230 _cleanup_free_ char *direct = NULL;
231 const char *translated;
232 size_t l;
233 int bypass, r;
234
235 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
236
237 if (!uid_is_valid(uid))
238 goto not_found;
239
240 /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
241 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
242 if (uid == root_passwd.pw_uid) {
243 *pwd = root_passwd;
244 *errnop = 0;
245 return NSS_STATUS_SUCCESS;
246 }
247 if (uid == nobody_passwd.pw_uid) {
248 *pwd = nobody_passwd;
249 *errnop = 0;
250 return NSS_STATUS_SUCCESS;
251 }
252 }
253
254 if (uid <= SYSTEM_UID_MAX)
255 goto not_found;
256
257 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
258 goto not_found;
259
260 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
261 if (bypass <= 0) {
262 r = sd_bus_open_system(&bus);
263 if (r < 0) {
264 bypass = 1;
265 goto direct_lookup;
266 }
267
268 r = sd_bus_call_method(bus,
269 "org.freedesktop.systemd1",
270 "/org/freedesktop/systemd1",
271 "org.freedesktop.systemd1.Manager",
272 "LookupDynamicUserByUID",
273 &error,
274 &reply,
275 "u",
276 (uint32_t) uid);
277 if (r < 0) {
278 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
279 goto not_found;
280
281 goto fail;
282 }
283
284 r = sd_bus_message_read(reply, "s", &translated);
285 if (r < 0)
286 goto fail;
287 }
288
289 direct_lookup:
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;
298
299 }
300
301 l = strlen(translated) + 1;
302 if (buflen < l) {
303 *errnop = ERANGE;
304 return NSS_STATUS_TRYAGAIN;
305 }
306
307 memcpy(buffer, translated, l);
308
309 pwd->pw_name = buffer;
310 pwd->pw_uid = uid;
311 pwd->pw_gid = uid;
312 pwd->pw_gecos = (char*) "Dynamic User";
313 pwd->pw_passwd = (char*) "*"; /* locked */
314 pwd->pw_dir = (char*) "/";
315 pwd->pw_shell = (char*) "/sbin/nologin";
316
317 *errnop = 0;
318 return NSS_STATUS_SUCCESS;
319
320 not_found:
321 *errnop = 0;
322 return NSS_STATUS_NOTFOUND;
323
324 fail:
325 *errnop = -r;
326 return NSS_STATUS_UNAVAIL;
327 }
328
329 enum nss_status _nss_systemd_getgrnam_r(
330 const char *name,
331 struct group *gr,
332 char *buffer, size_t buflen,
333 int *errnop) {
334
335 uint32_t translated;
336 size_t l;
337 int bypass, r;
338
339 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
340
341 assert(name);
342 assert(gr);
343
344 if (!valid_user_group_name(name))
345 goto not_found;
346
347 /* Synthesize records for root and nobody, in case they are missing form /etc/group */
348 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
349 if (streq(name, root_group.gr_name)) {
350 *gr = root_group;
351 *errnop = 0;
352 return NSS_STATUS_SUCCESS;
353 }
354 if (streq(name, nobody_group.gr_name)) {
355 *gr = nobody_group;
356 *errnop = 0;
357 return NSS_STATUS_SUCCESS;
358 }
359 }
360
361 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
362 goto not_found;
363
364 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
365 if (bypass <= 0) {
366 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
367 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
368 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
369
370 r = sd_bus_open_system(&bus);
371 if (r < 0) {
372 bypass = 1;
373 goto direct_lookup;
374 }
375
376 r = sd_bus_call_method(bus,
377 "org.freedesktop.systemd1",
378 "/org/freedesktop/systemd1",
379 "org.freedesktop.systemd1.Manager",
380 "LookupDynamicUserByName",
381 &error,
382 &reply,
383 "s",
384 name);
385 if (r < 0) {
386 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
387 goto not_found;
388
389 goto fail;
390 }
391
392 r = sd_bus_message_read(reply, "u", &translated);
393 if (r < 0)
394 goto fail;
395 }
396
397 direct_lookup:
398 if (bypass > 0) {
399 /* Access the dynamic GID allocation directly if we are called from dbus-daemon, see above. */
400 r = direct_lookup_name(name, (uid_t*) &translated);
401 if (r == -ENOENT)
402 goto not_found;
403 if (r < 0)
404 goto fail;
405 }
406
407 l = sizeof(char*) + strlen(name) + 1;
408 if (buflen < l) {
409 *errnop = ERANGE;
410 return NSS_STATUS_TRYAGAIN;
411 }
412
413 memzero(buffer, sizeof(char*));
414 strcpy(buffer + sizeof(char*), name);
415
416 gr->gr_name = buffer + sizeof(char*);
417 gr->gr_gid = (gid_t) translated;
418 gr->gr_passwd = (char*) "*"; /* locked */
419 gr->gr_mem = (char**) buffer;
420
421 *errnop = 0;
422 return NSS_STATUS_SUCCESS;
423
424 not_found:
425 *errnop = 0;
426 return NSS_STATUS_NOTFOUND;
427
428 fail:
429 *errnop = -r;
430 return NSS_STATUS_UNAVAIL;
431 }
432
433 enum nss_status _nss_systemd_getgrgid_r(
434 gid_t gid,
435 struct group *gr,
436 char *buffer, size_t buflen,
437 int *errnop) {
438
439 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
440 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
441 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
442 _cleanup_free_ char *direct = NULL;
443 const char *translated;
444 size_t l;
445 int bypass, r;
446
447 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
448
449 if (!gid_is_valid(gid))
450 goto not_found;
451
452 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
453 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
454 if (gid == root_group.gr_gid) {
455 *gr = root_group;
456 *errnop = 0;
457 return NSS_STATUS_SUCCESS;
458 }
459 if (gid == nobody_group.gr_gid) {
460 *gr = nobody_group;
461 *errnop = 0;
462 return NSS_STATUS_SUCCESS;
463 }
464 }
465
466 if (gid <= SYSTEM_GID_MAX)
467 goto not_found;
468
469 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
470 goto not_found;
471
472 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
473 if (bypass <= 0) {
474 r = sd_bus_open_system(&bus);
475 if (r < 0) {
476 bypass = 1;
477 goto direct_lookup;
478 }
479
480 r = sd_bus_call_method(bus,
481 "org.freedesktop.systemd1",
482 "/org/freedesktop/systemd1",
483 "org.freedesktop.systemd1.Manager",
484 "LookupDynamicUserByUID",
485 &error,
486 &reply,
487 "u",
488 (uint32_t) gid);
489 if (r < 0) {
490 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
491 goto not_found;
492
493 goto fail;
494 }
495
496 r = sd_bus_message_read(reply, "s", &translated);
497 if (r < 0)
498 goto fail;
499 }
500
501 direct_lookup:
502 if (bypass > 0) {
503
504 r = direct_lookup_uid(gid, &direct);
505 if (r == -ENOENT)
506 goto not_found;
507 if (r < 0)
508 goto fail;
509
510 translated = direct;
511 }
512
513 l = sizeof(char*) + strlen(translated) + 1;
514 if (buflen < l) {
515 *errnop = ERANGE;
516 return NSS_STATUS_TRYAGAIN;
517 }
518
519 memzero(buffer, sizeof(char*));
520 strcpy(buffer + sizeof(char*), translated);
521
522 gr->gr_name = buffer + sizeof(char*);
523 gr->gr_gid = gid;
524 gr->gr_passwd = (char*) "*"; /* locked */
525 gr->gr_mem = (char**) buffer;
526
527 *errnop = 0;
528 return NSS_STATUS_SUCCESS;
529
530 not_found:
531 *errnop = 0;
532 return NSS_STATUS_NOTFOUND;
533
534 fail:
535 *errnop = -r;
536 return NSS_STATUS_UNAVAIL;
537 }