Line data Source code
1 : /* SPDX-License-Identifier: LGPL-2.1+ */
2 :
3 : #include <alloca.h>
4 : #include <errno.h>
5 : #include <fcntl.h>
6 : #include <grp.h>
7 : #include <pwd.h>
8 : #include <stddef.h>
9 : #include <stdint.h>
10 : #include <stdio.h>
11 : #include <stdlib.h>
12 : #include <string.h>
13 : #include <sys/stat.h>
14 : #include <unistd.h>
15 : #include <utmp.h>
16 :
17 : #include "alloc-util.h"
18 : #include "errno-util.h"
19 : #include "fd-util.h"
20 : #include "fileio.h"
21 : #include "format-util.h"
22 : #include "macro.h"
23 : #include "missing.h"
24 : #include "parse-util.h"
25 : #include "path-util.h"
26 : #include "random-util.h"
27 : #include "string-util.h"
28 : #include "strv.h"
29 : #include "user-util.h"
30 : #include "utf8.h"
31 :
32 5055 : bool uid_is_valid(uid_t uid) {
33 :
34 : /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.436. */
35 :
36 : /* Some libc APIs use UID_INVALID as special placeholder */
37 5055 : if (uid == (uid_t) UINT32_C(0xFFFFFFFF))
38 4350 : return false;
39 :
40 : /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */
41 705 : if (uid == (uid_t) UINT32_C(0xFFFF))
42 8 : return false;
43 :
44 697 : return true;
45 : }
46 :
47 248 : int parse_uid(const char *s, uid_t *ret) {
48 248 : uint32_t uid = 0;
49 : int r;
50 :
51 248 : assert(s);
52 :
53 : assert_cc(sizeof(uid_t) == sizeof(uint32_t));
54 248 : r = safe_atou32(s, &uid);
55 248 : if (r < 0)
56 37 : return r;
57 :
58 211 : if (!uid_is_valid(uid))
59 7 : return -ENXIO; /* we return ENXIO instead of EINVAL
60 : * here, to make it easy to distinguish
61 : * invalid numeric uids from invalid
62 : * strings. */
63 :
64 204 : if (ret)
65 154 : *ret = uid;
66 :
67 204 : return 0;
68 : }
69 :
70 0 : char* getlogname_malloc(void) {
71 : uid_t uid;
72 : struct stat st;
73 :
74 0 : if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
75 0 : uid = st.st_uid;
76 : else
77 0 : uid = getuid();
78 :
79 0 : return uid_to_name(uid);
80 : }
81 :
82 5 : char *getusername_malloc(void) {
83 : const char *e;
84 :
85 5 : e = secure_getenv("USER");
86 5 : if (e)
87 5 : return strdup(e);
88 :
89 0 : return uid_to_name(getuid());
90 : }
91 :
92 0 : static bool is_nologin_shell(const char *shell) {
93 :
94 0 : return PATH_IN_SET(shell,
95 : /* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice
96 : * message and exits. Different distributions place the binary at different places though,
97 : * hence let's list them all. */
98 : "/bin/nologin",
99 : "/sbin/nologin",
100 : "/usr/bin/nologin",
101 : "/usr/sbin/nologin",
102 : /* 'true' and 'false' work too for the same purpose, but are less friendly as they don't do
103 : * any message printing. Different distributions place the binary at various places but at
104 : * least not in the 'sbin' directory. */
105 : "/bin/false",
106 : "/usr/bin/false",
107 : "/bin/true",
108 : "/usr/bin/true");
109 : }
110 :
111 7 : static int synthesize_user_creds(
112 : const char **username,
113 : uid_t *uid, gid_t *gid,
114 : const char **home,
115 : const char **shell,
116 : UserCredsFlags flags) {
117 :
118 : /* We enforce some special rules for uid=0 and uid=65534: in order to avoid NSS lookups for root we hardcode
119 : * their user record data. */
120 :
121 7 : if (STR_IN_SET(*username, "root", "0")) {
122 3 : *username = "root";
123 :
124 3 : if (uid)
125 3 : *uid = 0;
126 3 : if (gid)
127 2 : *gid = 0;
128 :
129 3 : if (home)
130 2 : *home = "/root";
131 :
132 3 : if (shell)
133 2 : *shell = "/bin/sh";
134 :
135 3 : return 0;
136 : }
137 :
138 4 : if (synthesize_nobody() &&
139 0 : STR_IN_SET(*username, NOBODY_USER_NAME, "65534")) {
140 0 : *username = NOBODY_USER_NAME;
141 :
142 0 : if (uid)
143 0 : *uid = UID_NOBODY;
144 0 : if (gid)
145 0 : *gid = GID_NOBODY;
146 :
147 0 : if (home)
148 0 : *home = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/";
149 :
150 0 : if (shell)
151 0 : *shell = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : NOLOGIN;
152 :
153 0 : return 0;
154 : }
155 :
156 4 : return -ENOMEDIUM;
157 : }
158 :
159 7 : int get_user_creds(
160 : const char **username,
161 : uid_t *uid, gid_t *gid,
162 : const char **home,
163 : const char **shell,
164 : UserCredsFlags flags) {
165 :
166 7 : uid_t u = UID_INVALID;
167 : struct passwd *p;
168 : int r;
169 :
170 7 : assert(username);
171 7 : assert(*username);
172 :
173 7 : if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
174 0 : (!home && !shell)) {
175 :
176 : /* So here's the deal: normally, we'll try to synthesize all records we can synthesize, and override
177 : * the user database with that. However, if the user specifies USER_CREDS_PREFER_NSS then the
178 : * user database will override the synthetic records instead — except if the user is only interested in
179 : * the UID and/or GID (but not the home directory, or the shell), in which case we'll always override
180 : * the user database (i.e. the USER_CREDS_PREFER_NSS flag has no effect in this case). Why?
181 : * Simply because there are valid usecase where the user might change the home directory or the shell
182 : * of the relevant users, but changing the UID/GID mappings for them is something we explicitly don't
183 : * support. */
184 :
185 7 : r = synthesize_user_creds(username, uid, gid, home, shell, flags);
186 7 : if (r >= 0)
187 3 : return 0;
188 4 : if (r != -ENOMEDIUM) /* not a username we can synthesize */
189 0 : return r;
190 : }
191 :
192 4 : if (parse_uid(*username, &u) >= 0) {
193 1 : errno = 0;
194 1 : p = getpwuid(u);
195 :
196 : /* If there are multiple users with the same id, make sure to leave $USER to the configured value
197 : * instead of the first occurrence in the database. However if the uid was configured by a numeric uid,
198 : * then let's pick the real username from /etc/passwd. */
199 1 : if (p)
200 1 : *username = p->pw_name;
201 0 : else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING) && !gid && !home && !shell) {
202 :
203 : /* If the specified user is a numeric UID and it isn't in the user database, and the caller
204 : * passed USER_CREDS_ALLOW_MISSING and was only interested in the UID, then juts return that
205 : * and don't complain. */
206 :
207 0 : if (uid)
208 0 : *uid = u;
209 :
210 0 : return 0;
211 : }
212 : } else {
213 3 : errno = 0;
214 3 : p = getpwnam(*username);
215 : }
216 4 : if (!p) {
217 2 : r = errno_or_else(ESRCH);
218 :
219 : /* If the user requested that we only synthesize as fallback, do so now */
220 2 : if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) {
221 0 : if (synthesize_user_creds(username, uid, gid, home, shell, flags) >= 0)
222 0 : return 0;
223 : }
224 :
225 2 : return r;
226 : }
227 :
228 2 : if (uid) {
229 2 : if (!uid_is_valid(p->pw_uid))
230 0 : return -EBADMSG;
231 :
232 2 : *uid = p->pw_uid;
233 : }
234 :
235 2 : if (gid) {
236 2 : if (!gid_is_valid(p->pw_gid))
237 0 : return -EBADMSG;
238 :
239 2 : *gid = p->pw_gid;
240 : }
241 :
242 2 : if (home) {
243 2 : if (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
244 0 : (empty_or_root(p->pw_dir) ||
245 0 : !path_is_valid(p->pw_dir) ||
246 0 : !path_is_absolute(p->pw_dir)))
247 0 : *home = NULL; /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
248 : else
249 2 : *home = p->pw_dir;
250 : }
251 :
252 2 : if (shell) {
253 2 : if (FLAGS_SET(flags, USER_CREDS_CLEAN) &&
254 0 : (isempty(p->pw_shell) ||
255 0 : !path_is_valid(p->pw_dir) ||
256 0 : !path_is_absolute(p->pw_shell) ||
257 0 : is_nologin_shell(p->pw_shell)))
258 0 : *shell = NULL;
259 : else
260 2 : *shell = p->pw_shell;
261 : }
262 :
263 2 : return 0;
264 : }
265 :
266 15 : int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags) {
267 : struct group *g;
268 : gid_t id;
269 :
270 15 : assert(groupname);
271 :
272 : /* We enforce some special rules for gid=0: in order to avoid NSS lookups for root we hardcode its data. */
273 :
274 15 : if (STR_IN_SET(*groupname, "root", "0")) {
275 3 : *groupname = "root";
276 :
277 3 : if (gid)
278 3 : *gid = 0;
279 :
280 3 : return 0;
281 : }
282 :
283 12 : if (synthesize_nobody() &&
284 0 : STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534")) {
285 0 : *groupname = NOBODY_GROUP_NAME;
286 :
287 0 : if (gid)
288 0 : *gid = GID_NOBODY;
289 :
290 0 : return 0;
291 : }
292 :
293 12 : if (parse_gid(*groupname, &id) >= 0) {
294 1 : errno = 0;
295 1 : g = getgrgid(id);
296 :
297 1 : if (g)
298 1 : *groupname = g->gr_name;
299 0 : else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING)) {
300 0 : if (gid)
301 0 : *gid = id;
302 :
303 0 : return 0;
304 : }
305 : } else {
306 11 : errno = 0;
307 11 : g = getgrnam(*groupname);
308 : }
309 :
310 12 : if (!g)
311 1 : return errno_or_else(ESRCH);
312 :
313 11 : if (gid) {
314 11 : if (!gid_is_valid(g->gr_gid))
315 0 : return -EBADMSG;
316 :
317 11 : *gid = g->gr_gid;
318 : }
319 :
320 11 : return 0;
321 : }
322 :
323 14 : char* uid_to_name(uid_t uid) {
324 : char *ret;
325 : int r;
326 :
327 : /* Shortcut things to avoid NSS lookups */
328 14 : if (uid == 0)
329 1 : return strdup("root");
330 13 : if (synthesize_nobody() &&
331 : uid == UID_NOBODY)
332 0 : return strdup(NOBODY_USER_NAME);
333 :
334 13 : if (uid_is_valid(uid)) {
335 : long bufsize;
336 :
337 11 : bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
338 11 : if (bufsize <= 0)
339 0 : bufsize = 4096;
340 :
341 0 : for (;;) {
342 11 : struct passwd pwbuf, *pw = NULL;
343 11 : _cleanup_free_ char *buf = NULL;
344 :
345 11 : buf = malloc(bufsize);
346 11 : if (!buf)
347 0 : return NULL;
348 :
349 11 : r = getpwuid_r(uid, &pwbuf, buf, (size_t) bufsize, &pw);
350 11 : if (r == 0 && pw)
351 11 : return strdup(pw->pw_name);
352 0 : if (r != ERANGE)
353 0 : break;
354 :
355 0 : if (bufsize > LONG_MAX/2) /* overflow check */
356 0 : return NULL;
357 :
358 0 : bufsize *= 2;
359 : }
360 : }
361 :
362 2 : if (asprintf(&ret, UID_FMT, uid) < 0)
363 0 : return NULL;
364 :
365 2 : return ret;
366 : }
367 :
368 24 : char* gid_to_name(gid_t gid) {
369 : char *ret;
370 : int r;
371 :
372 24 : if (gid == 0)
373 1 : return strdup("root");
374 23 : if (synthesize_nobody() &&
375 : gid == GID_NOBODY)
376 0 : return strdup(NOBODY_GROUP_NAME);
377 :
378 23 : if (gid_is_valid(gid)) {
379 : long bufsize;
380 :
381 21 : bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
382 21 : if (bufsize <= 0)
383 0 : bufsize = 4096;
384 :
385 0 : for (;;) {
386 21 : struct group grbuf, *gr = NULL;
387 21 : _cleanup_free_ char *buf = NULL;
388 :
389 21 : buf = malloc(bufsize);
390 21 : if (!buf)
391 0 : return NULL;
392 :
393 21 : r = getgrgid_r(gid, &grbuf, buf, (size_t) bufsize, &gr);
394 21 : if (r == 0 && gr)
395 21 : return strdup(gr->gr_name);
396 0 : if (r != ERANGE)
397 0 : break;
398 :
399 0 : if (bufsize > LONG_MAX/2) /* overflow check */
400 0 : return NULL;
401 :
402 0 : bufsize *= 2;
403 : }
404 : }
405 :
406 2 : if (asprintf(&ret, GID_FMT, gid) < 0)
407 0 : return NULL;
408 :
409 2 : return ret;
410 : }
411 :
412 21 : int in_gid(gid_t gid) {
413 : long ngroups_max;
414 : gid_t *gids;
415 : int r, i;
416 :
417 21 : if (getgid() == gid)
418 3 : return 1;
419 :
420 18 : if (getegid() == gid)
421 0 : return 1;
422 :
423 18 : if (!gid_is_valid(gid))
424 0 : return -EINVAL;
425 :
426 18 : ngroups_max = sysconf(_SC_NGROUPS_MAX);
427 18 : assert(ngroups_max > 0);
428 :
429 18 : gids = newa(gid_t, ngroups_max);
430 :
431 18 : r = getgroups(ngroups_max, gids);
432 18 : if (r < 0)
433 0 : return -errno;
434 :
435 96 : for (i = 0; i < r; i++)
436 94 : if (gids[i] == gid)
437 16 : return 1;
438 :
439 2 : return 0;
440 : }
441 :
442 11 : int in_group(const char *name) {
443 : int r;
444 : gid_t gid;
445 :
446 11 : r = get_group_creds(&name, &gid, 0);
447 11 : if (r < 0)
448 1 : return r;
449 :
450 10 : return in_gid(gid);
451 : }
452 :
453 441 : int get_home_dir(char **_h) {
454 : struct passwd *p;
455 : const char *e;
456 : char *h;
457 : uid_t u;
458 :
459 441 : assert(_h);
460 :
461 : /* Take the user specified one */
462 441 : e = secure_getenv("HOME");
463 441 : if (e && path_is_valid(e) && path_is_absolute(e)) {
464 439 : h = strdup(e);
465 439 : if (!h)
466 0 : return -ENOMEM;
467 :
468 439 : *_h = path_simplify(h, true);
469 439 : return 0;
470 : }
471 :
472 : /* Hardcode home directory for root and nobody to avoid NSS */
473 2 : u = getuid();
474 2 : if (u == 0) {
475 0 : h = strdup("/root");
476 0 : if (!h)
477 0 : return -ENOMEM;
478 :
479 0 : *_h = h;
480 0 : return 0;
481 : }
482 2 : if (synthesize_nobody() &&
483 : u == UID_NOBODY) {
484 0 : h = strdup("/");
485 0 : if (!h)
486 0 : return -ENOMEM;
487 :
488 0 : *_h = h;
489 0 : return 0;
490 : }
491 :
492 : /* Check the database... */
493 2 : errno = 0;
494 2 : p = getpwuid(u);
495 2 : if (!p)
496 0 : return errno_or_else(ESRCH);
497 :
498 2 : if (!path_is_valid(p->pw_dir) ||
499 2 : !path_is_absolute(p->pw_dir))
500 0 : return -EINVAL;
501 :
502 2 : h = strdup(p->pw_dir);
503 2 : if (!h)
504 0 : return -ENOMEM;
505 :
506 2 : *_h = path_simplify(h, true);
507 2 : return 0;
508 : }
509 :
510 1 : int get_shell(char **_s) {
511 : struct passwd *p;
512 : const char *e;
513 : char *s;
514 : uid_t u;
515 :
516 1 : assert(_s);
517 :
518 : /* Take the user specified one */
519 1 : e = secure_getenv("SHELL");
520 1 : if (e && path_is_valid(e) && path_is_absolute(e)) {
521 1 : s = strdup(e);
522 1 : if (!s)
523 0 : return -ENOMEM;
524 :
525 1 : *_s = path_simplify(s, true);
526 1 : return 0;
527 : }
528 :
529 : /* Hardcode shell for root and nobody to avoid NSS */
530 0 : u = getuid();
531 0 : if (u == 0) {
532 0 : s = strdup("/bin/sh");
533 0 : if (!s)
534 0 : return -ENOMEM;
535 :
536 0 : *_s = s;
537 0 : return 0;
538 : }
539 0 : if (synthesize_nobody() &&
540 : u == UID_NOBODY) {
541 0 : s = strdup(NOLOGIN);
542 0 : if (!s)
543 0 : return -ENOMEM;
544 :
545 0 : *_s = s;
546 0 : return 0;
547 : }
548 :
549 : /* Check the database... */
550 0 : errno = 0;
551 0 : p = getpwuid(u);
552 0 : if (!p)
553 0 : return errno_or_else(ESRCH);
554 :
555 0 : if (!path_is_valid(p->pw_shell) ||
556 0 : !path_is_absolute(p->pw_shell))
557 0 : return -EINVAL;
558 :
559 0 : s = strdup(p->pw_shell);
560 0 : if (!s)
561 0 : return -ENOMEM;
562 :
563 0 : *_s = path_simplify(s, true);
564 0 : return 0;
565 : }
566 :
567 0 : int reset_uid_gid(void) {
568 : int r;
569 :
570 0 : r = maybe_setgroups(0, NULL);
571 0 : if (r < 0)
572 0 : return r;
573 :
574 0 : if (setresgid(0, 0, 0) < 0)
575 0 : return -errno;
576 :
577 0 : if (setresuid(0, 0, 0) < 0)
578 0 : return -errno;
579 :
580 0 : return 0;
581 : }
582 :
583 0 : int take_etc_passwd_lock(const char *root) {
584 :
585 0 : struct flock flock = {
586 : .l_type = F_WRLCK,
587 : .l_whence = SEEK_SET,
588 : .l_start = 0,
589 : .l_len = 0,
590 : };
591 :
592 : const char *path;
593 : int fd, r;
594 :
595 : /* This is roughly the same as lckpwdf(), but not as awful. We
596 : * don't want to use alarm() and signals, hence we implement
597 : * our own trivial version of this.
598 : *
599 : * Note that shadow-utils also takes per-database locks in
600 : * addition to lckpwdf(). However, we don't given that they
601 : * are redundant as they invoke lckpwdf() first and keep
602 : * it during everything they do. The per-database locks are
603 : * awfully racy, and thus we just won't do them. */
604 :
605 0 : if (root)
606 0 : path = prefix_roota(root, ETC_PASSWD_LOCK_PATH);
607 : else
608 0 : path = ETC_PASSWD_LOCK_PATH;
609 :
610 0 : fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
611 0 : if (fd < 0)
612 0 : return log_debug_errno(errno, "Cannot open %s: %m", path);
613 :
614 0 : r = fcntl(fd, F_SETLKW, &flock);
615 0 : if (r < 0) {
616 0 : safe_close(fd);
617 0 : return log_debug_errno(errno, "Locking %s failed: %m", path);
618 : }
619 :
620 0 : return fd;
621 : }
622 :
623 43 : bool valid_user_group_name(const char *u) {
624 : const char *i;
625 : long sz;
626 :
627 : /* Checks if the specified name is a valid user/group name. Also see POSIX IEEE Std 1003.1-2008, 2016 Edition,
628 : * 3.437. We are a bit stricter here however. Specifically we deviate from POSIX rules:
629 : *
630 : * - We don't allow any dots (this would break chown syntax which permits dots as user/group name separator)
631 : * - We require that names fit into the appropriate utmp field
632 : * - We don't allow empty user names
633 : *
634 : * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters.
635 : */
636 :
637 43 : if (isempty(u))
638 2 : return false;
639 :
640 41 : if (!(u[0] >= 'a' && u[0] <= 'z') &&
641 23 : !(u[0] >= 'A' && u[0] <= 'Z') &&
642 19 : u[0] != '_')
643 17 : return false;
644 :
645 114 : for (i = u+1; *i; i++) {
646 98 : if (!(*i >= 'a' && *i <= 'z') &&
647 48 : !(*i >= 'A' && *i <= 'Z') &&
648 16 : !(*i >= '0' && *i <= '9') &&
649 12 : !IN_SET(*i, '_', '-'))
650 8 : return false;
651 : }
652 :
653 16 : sz = sysconf(_SC_LOGIN_NAME_MAX);
654 16 : assert_se(sz > 0);
655 :
656 16 : if ((size_t) (i-u) > (size_t) sz)
657 0 : return false;
658 :
659 16 : if ((size_t) (i-u) > UT_NAMESIZE - 1)
660 0 : return false;
661 :
662 16 : return true;
663 : }
664 :
665 25 : bool valid_user_group_name_or_id(const char *u) {
666 :
667 : /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the right
668 : * range, and not the invalid user ids. */
669 :
670 25 : if (isempty(u))
671 2 : return false;
672 :
673 23 : if (valid_user_group_name(u))
674 8 : return true;
675 :
676 15 : return parse_uid(u, NULL) >= 0;
677 : }
678 :
679 6 : bool valid_gecos(const char *d) {
680 :
681 6 : if (!d)
682 1 : return false;
683 :
684 5 : if (!utf8_is_valid(d))
685 0 : return false;
686 :
687 5 : if (string_has_cc(d, NULL))
688 1 : return false;
689 :
690 : /* Colons are used as field separators, and hence not OK */
691 4 : if (strchr(d, ':'))
692 1 : return false;
693 :
694 3 : return true;
695 : }
696 :
697 12 : bool valid_home(const char *p) {
698 : /* Note that this function is also called by valid_shell(), any
699 : * changes must account for that. */
700 :
701 12 : if (isempty(p))
702 2 : return false;
703 :
704 10 : if (!utf8_is_valid(p))
705 0 : return false;
706 :
707 10 : if (string_has_cc(p, NULL))
708 1 : return false;
709 :
710 9 : if (!path_is_absolute(p))
711 3 : return false;
712 :
713 6 : if (!path_is_normalized(p))
714 2 : return false;
715 :
716 : /* Colons are used as field separators, and hence not OK */
717 4 : if (strchr(p, ':'))
718 1 : return false;
719 :
720 3 : return true;
721 : }
722 :
723 0 : int maybe_setgroups(size_t size, const gid_t *list) {
724 : int r;
725 :
726 : /* Check if setgroups is allowed before we try to drop all the auxiliary groups */
727 0 : if (size == 0) { /* Dropping all aux groups? */
728 0 : _cleanup_free_ char *setgroups_content = NULL;
729 : bool can_setgroups;
730 :
731 0 : r = read_one_line_file("/proc/self/setgroups", &setgroups_content);
732 0 : if (r == -ENOENT)
733 : /* Old kernels don't have /proc/self/setgroups, so assume we can use setgroups */
734 0 : can_setgroups = true;
735 0 : else if (r < 0)
736 0 : return r;
737 : else
738 0 : can_setgroups = streq(setgroups_content, "allow");
739 :
740 0 : if (!can_setgroups) {
741 0 : log_debug("Skipping setgroups(), /proc/self/setgroups is set to 'deny'");
742 0 : return 0;
743 : }
744 : }
745 :
746 0 : if (setgroups(size, list) < 0)
747 0 : return -errno;
748 :
749 0 : return 0;
750 : }
751 :
752 71 : bool synthesize_nobody(void) {
753 : /* Returns true when we shall synthesize the "nobody" user (which we do by default). This can be turned off by
754 : * touching /etc/systemd/dont-synthesize-nobody in order to provide upgrade compatibility with legacy systems
755 : * that used the "nobody" user name and group name for other UIDs/GIDs than 65534.
756 : *
757 : * Note that we do not employ any kind of synchronization on the following caching variable. If the variable is
758 : * accessed in multi-threaded programs in the worst case it might happen that we initialize twice, but that
759 : * shouldn't matter as each initialization should come to the same result. */
760 : static int cache = -1;
761 :
762 71 : if (cache < 0)
763 9 : cache = access("/etc/systemd/dont-synthesize-nobody", F_OK) < 0;
764 :
765 71 : return cache;
766 : }
767 :
768 0 : int putpwent_sane(const struct passwd *pw, FILE *stream) {
769 0 : assert(pw);
770 0 : assert(stream);
771 :
772 0 : errno = 0;
773 0 : if (putpwent(pw, stream) != 0)
774 0 : return errno_or_else(EIO);
775 :
776 0 : return 0;
777 : }
778 :
779 0 : int putspent_sane(const struct spwd *sp, FILE *stream) {
780 0 : assert(sp);
781 0 : assert(stream);
782 :
783 0 : errno = 0;
784 0 : if (putspent(sp, stream) != 0)
785 0 : return errno_or_else(EIO);
786 :
787 0 : return 0;
788 : }
789 :
790 0 : int putgrent_sane(const struct group *gr, FILE *stream) {
791 0 : assert(gr);
792 0 : assert(stream);
793 :
794 0 : errno = 0;
795 0 : if (putgrent(gr, stream) != 0)
796 0 : return errno_or_else(EIO);
797 :
798 0 : return 0;
799 : }
800 :
801 : #if ENABLE_GSHADOW
802 0 : int putsgent_sane(const struct sgrp *sg, FILE *stream) {
803 0 : assert(sg);
804 0 : assert(stream);
805 :
806 0 : errno = 0;
807 0 : if (putsgent(sg, stream) != 0)
808 0 : return errno_or_else(EIO);
809 :
810 0 : return 0;
811 : }
812 : #endif
813 :
814 0 : int fgetpwent_sane(FILE *stream, struct passwd **pw) {
815 : struct passwd *p;
816 :
817 0 : assert(pw);
818 0 : assert(stream);
819 :
820 0 : errno = 0;
821 0 : p = fgetpwent(stream);
822 0 : if (!p && errno != ENOENT)
823 0 : return errno_or_else(EIO);
824 :
825 0 : *pw = p;
826 0 : return !!p;
827 : }
828 :
829 0 : int fgetspent_sane(FILE *stream, struct spwd **sp) {
830 : struct spwd *s;
831 :
832 0 : assert(sp);
833 0 : assert(stream);
834 :
835 0 : errno = 0;
836 0 : s = fgetspent(stream);
837 0 : if (!s && errno != ENOENT)
838 0 : return errno_or_else(EIO);
839 :
840 0 : *sp = s;
841 0 : return !!s;
842 : }
843 :
844 0 : int fgetgrent_sane(FILE *stream, struct group **gr) {
845 : struct group *g;
846 :
847 0 : assert(gr);
848 0 : assert(stream);
849 :
850 0 : errno = 0;
851 0 : g = fgetgrent(stream);
852 0 : if (!g && errno != ENOENT)
853 0 : return errno_or_else(EIO);
854 :
855 0 : *gr = g;
856 0 : return !!g;
857 : }
858 :
859 : #if ENABLE_GSHADOW
860 0 : int fgetsgent_sane(FILE *stream, struct sgrp **sg) {
861 : struct sgrp *s;
862 :
863 0 : assert(sg);
864 0 : assert(stream);
865 :
866 0 : errno = 0;
867 0 : s = fgetsgent(stream);
868 0 : if (!s && errno != ENOENT)
869 0 : return errno_or_else(EIO);
870 :
871 0 : *sg = s;
872 0 : return !!s;
873 : }
874 : #endif
875 :
876 2 : int make_salt(char **ret) {
877 : static const char table[] =
878 : "abcdefghijklmnopqrstuvwxyz"
879 : "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
880 : "0123456789"
881 : "./";
882 :
883 : uint8_t raw[16];
884 : char *salt, *j;
885 : size_t i;
886 : int r;
887 :
888 : /* This is a bit like crypt_gensalt_ra(), but doesn't require libcrypt, and doesn't do anything but
889 : * SHA512, i.e. is legacy-free and minimizes our deps. */
890 :
891 : assert_cc(sizeof(table) == 64U + 1U);
892 :
893 : /* Insist on the best randomness by setting RANDOM_BLOCK, this is about keeping passwords secret after all. */
894 2 : r = genuine_random_bytes(raw, sizeof(raw), RANDOM_BLOCK);
895 2 : if (r < 0)
896 0 : return r;
897 :
898 2 : salt = new(char, 3+sizeof(raw)+1+1);
899 2 : if (!salt)
900 0 : return -ENOMEM;
901 :
902 : /* We only bother with SHA512 hashed passwords, the rest is legacy, and we don't do legacy. */
903 2 : j = stpcpy(salt, "$6$");
904 34 : for (i = 0; i < sizeof(raw); i++)
905 32 : j[i] = table[raw[i] & 63];
906 2 : j[i++] = '$';
907 2 : j[i] = 0;
908 :
909 2 : *ret = salt;
910 2 : return 0;
911 : }
|