]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-systemd/nss-systemd.c
nss-systemd: add work-around to silence gcc warning
[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 #pragma GCC diagnostic ignored "-Wsizeof-pointer-memaccess"
332
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
339 uint32_t translated;
340 size_t l;
341 int bypass, r;
342
343 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
344
345 assert(name);
346 assert(gr);
347
348 if (!valid_user_group_name(name))
349 goto not_found;
350
351 /* Synthesize records for root and nobody, in case they are missing form /etc/group */
352 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
353 if (streq(name, root_group.gr_name)) {
354 *gr = root_group;
355 *errnop = 0;
356 return NSS_STATUS_SUCCESS;
357 }
358 if (synthesize_nobody() &&
359 streq(name, nobody_group.gr_name)) {
360 *gr = nobody_group;
361 *errnop = 0;
362 return NSS_STATUS_SUCCESS;
363 }
364 }
365
366 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
367 goto not_found;
368
369 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
370 if (bypass <= 0) {
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);
376 if (r < 0) {
377 bypass = 1;
378 goto direct_lookup;
379 }
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;
400 }
401
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
412 l = sizeof(char*) + strlen(name) + 1;
413 if (buflen < l) {
414 *errnop = ERANGE;
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;
447 _cleanup_free_ char *direct = NULL;
448 const char *translated;
449 size_t l;
450 int bypass, r;
451
452 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
453
454 if (!gid_is_valid(gid))
455 goto not_found;
456
457 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
458 if (getenv_bool_secure("SYSTEMD_NSS_BYPASS_SYNTHETIC") <= 0) {
459 if (gid == root_group.gr_gid) {
460 *gr = root_group;
461 *errnop = 0;
462 return NSS_STATUS_SUCCESS;
463 }
464 if (synthesize_nobody() &&
465 gid == nobody_group.gr_gid) {
466 *gr = nobody_group;
467 *errnop = 0;
468 return NSS_STATUS_SUCCESS;
469 }
470 }
471
472 if (!gid_is_dynamic(gid))
473 goto not_found;
474
475 if (getenv_bool_secure("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
476 goto not_found;
477
478 bypass = getenv_bool_secure("SYSTEMD_NSS_BYPASS_BUS");
479 if (bypass <= 0) {
480 r = sd_bus_open_system(&bus);
481 if (r < 0) {
482 bypass = 1;
483 goto direct_lookup;
484 }
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;
505 }
506
507 direct_lookup:
508 if (bypass > 0) {
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
518 l = sizeof(char*) + strlen(translated) + 1;
519 if (buflen < l) {
520 *errnop = ERANGE;
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 }