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