]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nss-systemd/nss-systemd.c
Merge pull request #5370 from evverx/fix-test-journal-importer
[thirdparty/systemd.git] / src / nss-systemd / nss-systemd.c
1 /***
2 This file is part of systemd.
3
4 Copyright 2016 Lennart Poettering
5
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
10
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
15
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
18 ***/
19
20 #include <nss.h>
21
22 #include "sd-bus.h"
23
24 #include "alloc-util.h"
25 #include "bus-common-errors.h"
26 #include "env-util.h"
27 #include "fs-util.h"
28 #include "macro.h"
29 #include "nss-util.h"
30 #include "signal-util.h"
31 #include "stdio-util.h"
32 #include "string-util.h"
33 #include "user-util.h"
34 #include "util.h"
35
36 static const struct passwd root_passwd = {
37 .pw_name = (char*) "root",
38 .pw_passwd = (char*) "x", /* see shadow file */
39 .pw_uid = 0,
40 .pw_gid = 0,
41 .pw_gecos = (char*) "Super User",
42 .pw_dir = (char*) "/root",
43 .pw_shell = (char*) "/bin/sh",
44 };
45
46 static const struct passwd nobody_passwd = {
47 .pw_name = (char*) NOBODY_USER_NAME,
48 .pw_passwd = (char*) "*", /* locked */
49 .pw_uid = 65534,
50 .pw_gid = 65534,
51 .pw_gecos = (char*) "User Nobody",
52 .pw_dir = (char*) "/",
53 .pw_shell = (char*) "/sbin/nologin",
54 };
55
56 static const struct group root_group = {
57 .gr_name = (char*) "root",
58 .gr_gid = 0,
59 .gr_passwd = (char*) "x", /* see shadow file */
60 .gr_mem = (char*[]) { NULL },
61 };
62
63 static const struct group nobody_group = {
64 .gr_name = (char*) NOBODY_GROUP_NAME,
65 .gr_gid = 65534,
66 .gr_passwd = (char*) "*", /* locked */
67 .gr_mem = (char*[]) { NULL },
68 };
69
70 NSS_GETPW_PROTOTYPES(systemd);
71 NSS_GETGR_PROTOTYPES(systemd);
72
73 static int direct_lookup_name(const char *name, uid_t *ret) {
74 _cleanup_free_ char *s = NULL;
75 const char *path;
76 int r;
77
78 assert(name);
79
80 /* Normally, we go via the bus to resolve names. That has the benefit that it is available from any mount
81 * namespace and subject to proper authentication. However, there's one problem: if our module is called from
82 * dbus-daemon itself we really can't use D-Bus to communicate. In this case, resort to a client-side hack,
83 * and look for the dynamic names directly. This is pretty ugly, but breaks the cyclic dependency. */
84
85 path = strjoina("/run/systemd/dynamic-uid/direct:", name);
86 r = readlink_malloc(path, &s);
87 if (r < 0)
88 return r;
89
90 return parse_uid(s, ret);
91 }
92
93 static int direct_lookup_uid(uid_t uid, char **ret) {
94 char path[strlen("/run/systemd/dynamic-uid/direct:") + DECIMAL_STR_MAX(uid_t) + 1], *s;
95 int r;
96
97 xsprintf(path, "/run/systemd/dynamic-uid/direct:" UID_FMT, uid);
98
99 r = readlink_malloc(path, &s);
100 if (r < 0)
101 return r;
102 if (!valid_user_group_name(s)) { /* extra safety check */
103 free(s);
104 return -EINVAL;
105 }
106
107 *ret = s;
108 return 0;
109 }
110
111 enum nss_status _nss_systemd_getpwnam_r(
112 const char *name,
113 struct passwd *pwd,
114 char *buffer, size_t buflen,
115 int *errnop) {
116
117 uint32_t translated;
118 size_t l;
119 int r;
120
121 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
122
123 assert(name);
124 assert(pwd);
125
126 /* If the username is not valid, then we don't know it. Ideally libc would filter these for us anyway. We don't
127 * generate EINVAL here, because it isn't really out business to complain about invalid user names. */
128 if (!valid_user_group_name(name))
129 goto not_found;
130
131 /* Synthesize entries for the root and nobody users, in case they are missing in /etc/passwd */
132 if (streq(name, root_passwd.pw_name)) {
133 *pwd = root_passwd;
134 *errnop = 0;
135 return NSS_STATUS_SUCCESS;
136 }
137 if (streq(name, nobody_passwd.pw_name)) {
138 *pwd = nobody_passwd;
139 *errnop = 0;
140 return NSS_STATUS_SUCCESS;
141 }
142
143 /* Make sure that we don't go in circles when allocating a dynamic UID by checking our own database */
144 if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
145 goto not_found;
146
147 if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
148
149 /* Access the dynamic UID allocation directly if we are called from dbus-daemon, see above. */
150 r = direct_lookup_name(name, (uid_t*) &translated);
151 if (r == -ENOENT)
152 goto not_found;
153 if (r < 0)
154 goto fail;
155
156 } else {
157 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
158 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
159 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
160
161 r = sd_bus_open_system(&bus);
162 if (r < 0)
163 goto fail;
164
165 r = sd_bus_call_method(bus,
166 "org.freedesktop.systemd1",
167 "/org/freedesktop/systemd1",
168 "org.freedesktop.systemd1.Manager",
169 "LookupDynamicUserByName",
170 &error,
171 &reply,
172 "s",
173 name);
174 if (r < 0) {
175 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
176 goto not_found;
177
178 goto fail;
179 }
180
181 r = sd_bus_message_read(reply, "u", &translated);
182 if (r < 0)
183 goto fail;
184 }
185
186 l = strlen(name);
187 if (buflen < l+1) {
188 *errnop = ERANGE;
189 return NSS_STATUS_TRYAGAIN;
190 }
191
192 memcpy(buffer, name, l+1);
193
194 pwd->pw_name = buffer;
195 pwd->pw_uid = (uid_t) translated;
196 pwd->pw_gid = (uid_t) translated;
197 pwd->pw_gecos = (char*) "Dynamic User";
198 pwd->pw_passwd = (char*) "*"; /* locked */
199 pwd->pw_dir = (char*) "/";
200 pwd->pw_shell = (char*) "/sbin/nologin";
201
202 *errnop = 0;
203 return NSS_STATUS_SUCCESS;
204
205 not_found:
206 *errnop = 0;
207 return NSS_STATUS_NOTFOUND;
208
209 fail:
210 *errnop = -r;
211 return NSS_STATUS_UNAVAIL;
212 }
213
214 enum nss_status _nss_systemd_getpwuid_r(
215 uid_t uid,
216 struct passwd *pwd,
217 char *buffer, size_t buflen,
218 int *errnop) {
219
220 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
221 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
222 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
223 _cleanup_free_ char *direct = NULL;
224 const char *translated;
225 size_t l;
226 int r;
227
228 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
229
230 if (!uid_is_valid(uid))
231 goto not_found;
232
233 /* Synthesize data for the root user and for nobody in case they are missing from /etc/passwd */
234 if (uid == root_passwd.pw_uid) {
235 *pwd = root_passwd;
236 *errnop = 0;
237 return NSS_STATUS_SUCCESS;
238 }
239 if (uid == nobody_passwd.pw_uid) {
240 *pwd = nobody_passwd;
241 *errnop = 0;
242 return NSS_STATUS_SUCCESS;
243 }
244
245 if (uid <= SYSTEM_UID_MAX)
246 goto not_found;
247
248 if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
249 goto not_found;
250
251 if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
252
253 r = direct_lookup_uid(uid, &direct);
254 if (r == -ENOENT)
255 goto not_found;
256 if (r < 0)
257 goto fail;
258
259 translated = direct;
260
261 } else {
262 r = sd_bus_open_system(&bus);
263 if (r < 0)
264 goto fail;
265
266 r = sd_bus_call_method(bus,
267 "org.freedesktop.systemd1",
268 "/org/freedesktop/systemd1",
269 "org.freedesktop.systemd1.Manager",
270 "LookupDynamicUserByUID",
271 &error,
272 &reply,
273 "u",
274 (uint32_t) uid);
275 if (r < 0) {
276 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
277 goto not_found;
278
279 goto fail;
280 }
281
282 r = sd_bus_message_read(reply, "s", &translated);
283 if (r < 0)
284 goto fail;
285 }
286
287 l = strlen(translated) + 1;
288 if (buflen < l) {
289 *errnop = ERANGE;
290 return NSS_STATUS_TRYAGAIN;
291 }
292
293 memcpy(buffer, translated, l);
294
295 pwd->pw_name = buffer;
296 pwd->pw_uid = uid;
297 pwd->pw_gid = uid;
298 pwd->pw_gecos = (char*) "Dynamic User";
299 pwd->pw_passwd = (char*) "*"; /* locked */
300 pwd->pw_dir = (char*) "/";
301 pwd->pw_shell = (char*) "/sbin/nologin";
302
303 *errnop = 0;
304 return NSS_STATUS_SUCCESS;
305
306 not_found:
307 *errnop = 0;
308 return NSS_STATUS_NOTFOUND;
309
310 fail:
311 *errnop = -r;
312 return NSS_STATUS_UNAVAIL;
313 }
314
315 enum nss_status _nss_systemd_getgrnam_r(
316 const char *name,
317 struct group *gr,
318 char *buffer, size_t buflen,
319 int *errnop) {
320
321 uint32_t translated;
322 size_t l;
323 int r;
324
325 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
326
327 assert(name);
328 assert(gr);
329
330 if (!valid_user_group_name(name))
331 goto not_found;
332
333 /* Synthesize records for root and nobody, in case they are missing form /etc/group */
334 if (streq(name, root_group.gr_name)) {
335 *gr = root_group;
336 *errnop = 0;
337 return NSS_STATUS_SUCCESS;
338 }
339 if (streq(name, nobody_group.gr_name)) {
340 *gr = nobody_group;
341 *errnop = 0;
342 return NSS_STATUS_SUCCESS;
343 }
344
345 if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
346 goto not_found;
347
348 if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
349
350 /* Access the dynamic GID allocation directly if we are called from dbus-daemon, see above. */
351 r = direct_lookup_name(name, (uid_t*) &translated);
352 if (r == -ENOENT)
353 goto not_found;
354 if (r < 0)
355 goto fail;
356 } else {
357
358 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
359 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
360 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
361
362 r = sd_bus_open_system(&bus);
363 if (r < 0)
364 goto fail;
365
366 r = sd_bus_call_method(bus,
367 "org.freedesktop.systemd1",
368 "/org/freedesktop/systemd1",
369 "org.freedesktop.systemd1.Manager",
370 "LookupDynamicUserByName",
371 &error,
372 &reply,
373 "s",
374 name);
375 if (r < 0) {
376 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
377 goto not_found;
378
379 goto fail;
380 }
381
382 r = sd_bus_message_read(reply, "u", &translated);
383 if (r < 0)
384 goto fail;
385 }
386
387 l = sizeof(char*) + strlen(name) + 1;
388 if (buflen < l) {
389 *errnop = ERANGE;
390 return NSS_STATUS_TRYAGAIN;
391 }
392
393 memzero(buffer, sizeof(char*));
394 strcpy(buffer + sizeof(char*), name);
395
396 gr->gr_name = buffer + sizeof(char*);
397 gr->gr_gid = (gid_t) translated;
398 gr->gr_passwd = (char*) "*"; /* locked */
399 gr->gr_mem = (char**) buffer;
400
401 *errnop = 0;
402 return NSS_STATUS_SUCCESS;
403
404 not_found:
405 *errnop = 0;
406 return NSS_STATUS_NOTFOUND;
407
408 fail:
409 *errnop = -r;
410 return NSS_STATUS_UNAVAIL;
411 }
412
413 enum nss_status _nss_systemd_getgrgid_r(
414 gid_t gid,
415 struct group *gr,
416 char *buffer, size_t buflen,
417 int *errnop) {
418
419 _cleanup_(sd_bus_error_free) sd_bus_error error = SD_BUS_ERROR_NULL;
420 _cleanup_(sd_bus_message_unrefp) sd_bus_message* reply = NULL;
421 _cleanup_(sd_bus_flush_close_unrefp) sd_bus *bus = NULL;
422 _cleanup_free_ char *direct = NULL;
423 const char *translated;
424 size_t l;
425 int r;
426
427 BLOCK_SIGNALS(NSS_SIGNALS_BLOCK);
428
429 if (!gid_is_valid(gid))
430 goto not_found;
431
432 /* Synthesize records for root and nobody, in case they are missing from /etc/group */
433 if (gid == root_group.gr_gid) {
434 *gr = root_group;
435 *errnop = 0;
436 return NSS_STATUS_SUCCESS;
437 }
438 if (gid == nobody_group.gr_gid) {
439 *gr = nobody_group;
440 *errnop = 0;
441 return NSS_STATUS_SUCCESS;
442 }
443
444 if (gid <= SYSTEM_GID_MAX)
445 goto not_found;
446
447 if (getenv_bool("SYSTEMD_NSS_DYNAMIC_BYPASS") > 0)
448 goto not_found;
449
450 if (getenv_bool("SYSTEMD_NSS_BYPASS_BUS") > 0) {
451
452 r = direct_lookup_uid(gid, &direct);
453 if (r == -ENOENT)
454 goto not_found;
455 if (r < 0)
456 goto fail;
457
458 translated = direct;
459 } else {
460 r = sd_bus_open_system(&bus);
461 if (r < 0)
462 goto fail;
463
464 r = sd_bus_call_method(bus,
465 "org.freedesktop.systemd1",
466 "/org/freedesktop/systemd1",
467 "org.freedesktop.systemd1.Manager",
468 "LookupDynamicUserByUID",
469 &error,
470 &reply,
471 "u",
472 (uint32_t) gid);
473 if (r < 0) {
474 if (sd_bus_error_has_name(&error, BUS_ERROR_NO_SUCH_DYNAMIC_USER))
475 goto not_found;
476
477 goto fail;
478 }
479
480 r = sd_bus_message_read(reply, "s", &translated);
481 if (r < 0)
482 goto fail;
483 }
484
485 l = sizeof(char*) + strlen(translated) + 1;
486 if (buflen < l) {
487 *errnop = ERANGE;
488 return NSS_STATUS_TRYAGAIN;
489 }
490
491 memzero(buffer, sizeof(char*));
492 strcpy(buffer + sizeof(char*), translated);
493
494 gr->gr_name = buffer + sizeof(char*);
495 gr->gr_gid = gid;
496 gr->gr_passwd = (char*) "*"; /* locked */
497 gr->gr_mem = (char**) buffer;
498
499 *errnop = 0;
500 return NSS_STATUS_SUCCESS;
501
502 not_found:
503 *errnop = 0;
504 return NSS_STATUS_NOTFOUND;
505
506 fail:
507 *errnop = -r;
508 return NSS_STATUS_UNAVAIL;
509 }