]> git.ipfire.org Git - thirdparty/systemd.git/blame - 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
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 */
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
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,
66 .gr_gid = 65534,
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) {
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
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 }
139 if (streq(name, nobody_passwd.pw_name)) {
140 *pwd = nobody_passwd;
141 *errnop = 0;
142 return NSS_STATUS_SUCCESS;
143 }
2129011e
LP
144 }
145
409093fe 146 /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
71e0accc 147 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
409093fe
LP
148 goto not_found;
149
689ca202
YW
150 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
151 if (bypass <= 0) {
fd63e712
LP
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);
689ca202
YW
157 if (r < 0) {
158 bypass = 1;
159 goto direct_lookup;
160 }
fd63e712
LP
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;
409093fe
LP
181 }
182
689ca202
YW
183direct_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
409093fe
LP
193 l = strlen(name);
194 if (buflen < l+1) {
cda458a5 195 *errnop = ERANGE;
409093fe
LP
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
212not_found:
213 *errnop = 0;
214 return NSS_STATUS_NOTFOUND;
215
216fail:
217 *errnop = -r;
218 return NSS_STATUS_UNAVAIL;
219}
220
221enum 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;
fd63e712 230 _cleanup_free_ char *direct = NULL;
409093fe
LP
231 const char *translated;
232 size_t l;
689ca202 233 int bypass, r;
409093fe
LP
234
235 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
236
d6c575e3
LP
237 if (!uid_is_valid(uid))
238 goto not_found;
409093fe 239
2129011e 240 /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
71e0accc 241 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
fe102d6a
LP
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 }
2129011e
LP
252 }
253
409093fe
LP
254 if (uid <= SYSTEM_UID_MAX)
255 goto not_found;
256
71e0accc 257 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
409093fe
LP
258 goto not_found;
259
689ca202
YW
260 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
261 if (bypass <= 0) {
fd63e712 262 r = sd_bus_open_system(&bus);
689ca202
YW
263 if (r < 0) {
264 bypass = 1;
265 goto direct_lookup;
266 }
fd63e712
LP
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;
409093fe
LP
287 }
288
689ca202
YW
289direct_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
409093fe
LP
301 l = strlen(translated) + 1;
302 if (buflen < l) {
cda458a5 303 *errnop = ERANGE;
409093fe
LP
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
320not_found:
321 *errnop = 0;
322 return NSS_STATUS_NOTFOUND;
323
324fail:
325 *errnop = -r;
326 return NSS_STATUS_UNAVAIL;
327}
328
329enum nss_status _nss_systemd_getgrnam_r(
330 const char *name,
331 struct group *gr,
332 char *buffer, size_t buflen,
333 int *errnop) {
334
409093fe
LP
335 uint32_t translated;
336 size_t l;
689ca202 337 int bypass, r;
409093fe
LP
338
339 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
340
341 assert(name);
342 assert(gr);
343
d6c575e3
LP
344 if (!valid_user_group_name(name))
345 goto not_found;
2129011e
LP
346
347 /* Synthesize records for root and nobody, in case they are missing form /etc/group */
71e0accc 348 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
fe102d6a
LP
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 }
2129011e
LP
359 }
360
71e0accc 361 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
409093fe
LP
362 goto not_found;
363
689ca202
YW
364 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
365 if (bypass <= 0) {
fd63e712
LP
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);
689ca202
YW
371 if (r < 0) {
372 bypass = 1;
373 goto direct_lookup;
374 }
fd63e712
LP
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;
409093fe
LP
395 }
396
689ca202
YW
397direct_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
409093fe
LP
407 l = sizeof(char*) + strlen(name) + 1;
408 if (buflen < l) {
cda458a5 409 *errnop = ERANGE;
409093fe
LP
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
424not_found:
425 *errnop = 0;
426 return NSS_STATUS_NOTFOUND;
427
428fail:
429 *errnop = -r;
430 return NSS_STATUS_UNAVAIL;
431}
432
433enum 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;
fd63e712 442 _cleanup_free_ char *direct = NULL;
409093fe
LP
443 const char *translated;
444 size_t l;
689ca202 445 int bypass, r;
409093fe
LP
446
447 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
448
d6c575e3
LP
449 if (!gid_is_valid(gid))
450 goto not_found;
409093fe 451
2129011e 452 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
71e0accc 453 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
fe102d6a
LP
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 }
2129011e
LP
464 }
465
409093fe
LP
466 if (gid <= SYSTEM_GID_MAX)
467 goto not_found;
468
71e0accc 469 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
409093fe
LP
470 goto not_found;
471
689ca202
YW
472 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
473 if (bypass <= 0) {
fd63e712 474 r = sd_bus_open_system(&bus);
689ca202
YW
475 if (r < 0) {
476 bypass = 1;
477 goto direct_lookup;
478 }
fd63e712
LP
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;
409093fe
LP
499 }
500
689ca202
YW
501direct_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
409093fe
LP
513 l = sizeof(char*) + strlen(translated) + 1;
514 if (buflen < l) {
cda458a5 515 *errnop = ERANGE;
409093fe
LP
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
530not_found:
531 *errnop = 0;
532 return NSS_STATUS_NOTFOUND;
533
534fail:
535 *errnop = -r;
536 return NSS_STATUS_UNAVAIL;
537}