Branch data 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 : 20408 : 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 [ + + ]: 20408 : if (uid == (uid_t) UINT32_C(0xFFFFFFFF))
38 : 17576 : return false;
39 : :
40 : : /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */
41 [ + + ]: 2832 : if (uid == (uid_t) UINT32_C(0xFFFF))
42 : 32 : return false;
43 : :
44 : 2800 : return true;
45 : : }
46 : :
47 : 1004 : int parse_uid(const char *s, uid_t *ret) {
48 : 1004 : uint32_t uid = 0;
49 : : int r;
50 : :
51 [ - + ]: 1004 : assert(s);
52 : :
53 : : assert_cc(sizeof(uid_t) == sizeof(uint32_t));
54 : 1004 : r = safe_atou32(s, &uid);
55 [ + + ]: 1004 : if (r < 0)
56 : 148 : return r;
57 : :
58 [ + + ]: 856 : if (!uid_is_valid(uid))
59 : 28 : 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 [ + + ]: 828 : if (ret)
65 : 628 : *ret = uid;
66 : :
67 : 828 : 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 : 20 : char *getusername_malloc(void) {
83 : : const char *e;
84 : :
85 : 20 : e = secure_getenv("USER");
86 [ + - ]: 20 : if (e)
87 : 20 : 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 : 28 : 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 [ + + ]: 28 : if (STR_IN_SET(*username, "root", "0")) {
122 : 12 : *username = "root";
123 : :
124 [ + - ]: 12 : if (uid)
125 : 12 : *uid = 0;
126 [ + + ]: 12 : if (gid)
127 : 8 : *gid = 0;
128 : :
129 [ + + ]: 12 : if (home)
130 : 8 : *home = "/root";
131 : :
132 [ + + ]: 12 : if (shell)
133 : 8 : *shell = "/bin/sh";
134 : :
135 : 12 : return 0;
136 : : }
137 : :
138 [ - + ]: 16 : 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 : 16 : return -ENOMEDIUM;
157 : : }
158 : :
159 : 28 : 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 : 28 : uid_t u = UID_INVALID;
167 : : struct passwd *p;
168 : : int r;
169 : :
170 [ - + ]: 28 : assert(username);
171 [ - + ]: 28 : assert(*username);
172 : :
173 [ - + # # ]: 28 : 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 : 28 : r = synthesize_user_creds(username, uid, gid, home, shell, flags);
186 [ + + ]: 28 : if (r >= 0)
187 : 12 : return 0;
188 [ - + ]: 16 : if (r != -ENOMEDIUM) /* not a username we can synthesize */
189 : 0 : return r;
190 : : }
191 : :
192 [ + + ]: 16 : if (parse_uid(*username, &u) >= 0) {
193 : 4 : errno = 0;
194 : 4 : 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 [ + - ]: 4 : if (p)
200 : 4 : *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 : 12 : errno = 0;
214 : 12 : p = getpwnam(*username);
215 : : }
216 [ + + ]: 16 : if (!p) {
217 : 8 : r = errno_or_else(ESRCH);
218 : :
219 : : /* If the user requested that we only synthesize as fallback, do so now */
220 [ - + ]: 8 : 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 : 8 : return r;
226 : : }
227 : :
228 [ + - ]: 8 : if (uid) {
229 [ - + ]: 8 : if (!uid_is_valid(p->pw_uid))
230 : 0 : return -EBADMSG;
231 : :
232 : 8 : *uid = p->pw_uid;
233 : : }
234 : :
235 [ + - ]: 8 : if (gid) {
236 [ - + ]: 8 : if (!gid_is_valid(p->pw_gid))
237 : 0 : return -EBADMSG;
238 : :
239 : 8 : *gid = p->pw_gid;
240 : : }
241 : :
242 [ + - ]: 8 : if (home) {
243 [ - + # # ]: 8 : 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 : 8 : *home = p->pw_dir;
250 : : }
251 : :
252 [ + - ]: 8 : if (shell) {
253 [ - + # # ]: 8 : 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 : 8 : *shell = p->pw_shell;
261 : : }
262 : :
263 : 8 : return 0;
264 : : }
265 : :
266 : 60 : int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags) {
267 : : struct group *g;
268 : : gid_t id;
269 : :
270 [ - + ]: 60 : 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 [ + + ]: 60 : if (STR_IN_SET(*groupname, "root", "0")) {
275 : 12 : *groupname = "root";
276 : :
277 [ + - ]: 12 : if (gid)
278 : 12 : *gid = 0;
279 : :
280 : 12 : return 0;
281 : : }
282 : :
283 [ - + ]: 48 : 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 [ + + ]: 48 : if (parse_gid(*groupname, &id) >= 0) {
294 : 4 : errno = 0;
295 : 4 : g = getgrgid(id);
296 : :
297 [ + - ]: 4 : if (g)
298 : 4 : *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 : 44 : errno = 0;
307 : 44 : g = getgrnam(*groupname);
308 : : }
309 : :
310 [ + + ]: 48 : if (!g)
311 : 4 : return errno_or_else(ESRCH);
312 : :
313 [ + - ]: 44 : if (gid) {
314 [ - + ]: 44 : if (!gid_is_valid(g->gr_gid))
315 : 0 : return -EBADMSG;
316 : :
317 : 44 : *gid = g->gr_gid;
318 : : }
319 : :
320 : 44 : return 0;
321 : : }
322 : :
323 : 56 : char* uid_to_name(uid_t uid) {
324 : : char *ret;
325 : : int r;
326 : :
327 : : /* Shortcut things to avoid NSS lookups */
328 [ + + ]: 56 : if (uid == 0)
329 : 4 : return strdup("root");
330 [ - + # # ]: 52 : if (synthesize_nobody() &&
331 : : uid == UID_NOBODY)
332 : 0 : return strdup(NOBODY_USER_NAME);
333 : :
334 [ + + ]: 52 : if (uid_is_valid(uid)) {
335 : : long bufsize;
336 : :
337 : 44 : bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
338 [ - + ]: 44 : if (bufsize <= 0)
339 : 0 : bufsize = 4096;
340 : :
341 : 0 : for (;;) {
342 : 44 : struct passwd pwbuf, *pw = NULL;
343 [ - + - ]: 44 : _cleanup_free_ char *buf = NULL;
344 : :
345 : 44 : buf = malloc(bufsize);
346 [ - + ]: 44 : if (!buf)
347 : 0 : return NULL;
348 : :
349 : 44 : r = getpwuid_r(uid, &pwbuf, buf, (size_t) bufsize, &pw);
350 [ + - + - ]: 44 : if (r == 0 && pw)
351 : 44 : 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 [ - + ]: 8 : if (asprintf(&ret, UID_FMT, uid) < 0)
363 : 0 : return NULL;
364 : :
365 : 8 : return ret;
366 : : }
367 : :
368 : 96 : char* gid_to_name(gid_t gid) {
369 : : char *ret;
370 : : int r;
371 : :
372 [ + + ]: 96 : if (gid == 0)
373 : 4 : return strdup("root");
374 [ - + # # ]: 92 : if (synthesize_nobody() &&
375 : : gid == GID_NOBODY)
376 : 0 : return strdup(NOBODY_GROUP_NAME);
377 : :
378 [ + + ]: 92 : if (gid_is_valid(gid)) {
379 : : long bufsize;
380 : :
381 : 84 : bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
382 [ - + ]: 84 : if (bufsize <= 0)
383 : 0 : bufsize = 4096;
384 : :
385 : 0 : for (;;) {
386 : 84 : struct group grbuf, *gr = NULL;
387 [ - + - ]: 84 : _cleanup_free_ char *buf = NULL;
388 : :
389 : 84 : buf = malloc(bufsize);
390 [ - + ]: 84 : if (!buf)
391 : 0 : return NULL;
392 : :
393 : 84 : r = getgrgid_r(gid, &grbuf, buf, (size_t) bufsize, &gr);
394 [ + - + - ]: 84 : if (r == 0 && gr)
395 : 84 : 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 [ - + ]: 8 : if (asprintf(&ret, GID_FMT, gid) < 0)
407 : 0 : return NULL;
408 : :
409 : 8 : return ret;
410 : : }
411 : :
412 : 84 : int in_gid(gid_t gid) {
413 : : long ngroups_max;
414 : : gid_t *gids;
415 : : int r, i;
416 : :
417 [ + + ]: 84 : if (getgid() == gid)
418 : 12 : return 1;
419 : :
420 [ - + ]: 72 : if (getegid() == gid)
421 : 0 : return 1;
422 : :
423 [ - + ]: 72 : if (!gid_is_valid(gid))
424 : 0 : return -EINVAL;
425 : :
426 : 72 : ngroups_max = sysconf(_SC_NGROUPS_MAX);
427 [ - + ]: 72 : assert(ngroups_max > 0);
428 : :
429 [ - + - + ]: 72 : gids = newa(gid_t, ngroups_max);
430 : :
431 : 72 : r = getgroups(ngroups_max, gids);
432 [ - + ]: 72 : if (r < 0)
433 : 0 : return -errno;
434 : :
435 [ + + ]: 384 : for (i = 0; i < r; i++)
436 [ + + ]: 376 : if (gids[i] == gid)
437 : 64 : return 1;
438 : :
439 : 8 : return 0;
440 : : }
441 : :
442 : 44 : int in_group(const char *name) {
443 : : int r;
444 : : gid_t gid;
445 : :
446 : 44 : r = get_group_creds(&name, &gid, 0);
447 [ + + ]: 44 : if (r < 0)
448 : 4 : return r;
449 : :
450 : 40 : return in_gid(gid);
451 : : }
452 : :
453 : 1764 : int get_home_dir(char **_h) {
454 : : struct passwd *p;
455 : : const char *e;
456 : : char *h;
457 : : uid_t u;
458 : :
459 [ - + ]: 1764 : assert(_h);
460 : :
461 : : /* Take the user specified one */
462 : 1764 : e = secure_getenv("HOME");
463 [ + + + - : 1764 : if (e && path_is_valid(e) && path_is_absolute(e)) {
+ - ]
464 : 1756 : h = strdup(e);
465 [ - + ]: 1756 : if (!h)
466 : 0 : return -ENOMEM;
467 : :
468 : 1756 : *_h = path_simplify(h, true);
469 : 1756 : return 0;
470 : : }
471 : :
472 : : /* Hardcode home directory for root and nobody to avoid NSS */
473 : 8 : u = getuid();
474 [ - + ]: 8 : 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 [ - + # # ]: 8 : 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 : 8 : errno = 0;
494 : 8 : p = getpwuid(u);
495 [ - + ]: 8 : if (!p)
496 : 0 : return errno_or_else(ESRCH);
497 : :
498 [ + - ]: 8 : if (!path_is_valid(p->pw_dir) ||
499 [ - + ]: 8 : !path_is_absolute(p->pw_dir))
500 : 0 : return -EINVAL;
501 : :
502 : 8 : h = strdup(p->pw_dir);
503 [ - + ]: 8 : if (!h)
504 : 0 : return -ENOMEM;
505 : :
506 : 8 : *_h = path_simplify(h, true);
507 : 8 : return 0;
508 : : }
509 : :
510 : 4 : int get_shell(char **_s) {
511 : : struct passwd *p;
512 : : const char *e;
513 : : char *s;
514 : : uid_t u;
515 : :
516 [ - + ]: 4 : assert(_s);
517 : :
518 : : /* Take the user specified one */
519 : 4 : e = secure_getenv("SHELL");
520 [ + - + - : 4 : if (e && path_is_valid(e) && path_is_absolute(e)) {
+ - ]
521 : 4 : s = strdup(e);
522 [ - + ]: 4 : if (!s)
523 : 0 : return -ENOMEM;
524 : :
525 : 4 : *_s = path_simplify(s, true);
526 : 4 : 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 : 172 : 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 [ + + ]: 172 : if (isempty(u))
638 : 8 : return false;
639 : :
640 [ + + - + ]: 164 : if (!(u[0] >= 'a' && u[0] <= 'z') &&
641 [ + + + + ]: 92 : !(u[0] >= 'A' && u[0] <= 'Z') &&
642 [ + + ]: 76 : u[0] != '_')
643 : 68 : return false;
644 : :
645 [ + + ]: 456 : for (i = u+1; *i; i++) {
646 [ + + - + ]: 392 : if (!(*i >= 'a' && *i <= 'z') &&
647 [ + + - + ]: 192 : !(*i >= 'A' && *i <= 'Z') &&
648 [ + + + + ]: 64 : !(*i >= '0' && *i <= '9') &&
649 [ + + + + ]: 48 : !IN_SET(*i, '_', '-'))
650 : 32 : return false;
651 : : }
652 : :
653 : 64 : sz = sysconf(_SC_LOGIN_NAME_MAX);
654 [ - + ]: 64 : assert_se(sz > 0);
655 : :
656 [ - + ]: 64 : if ((size_t) (i-u) > (size_t) sz)
657 : 0 : return false;
658 : :
659 [ - + ]: 64 : if ((size_t) (i-u) > UT_NAMESIZE - 1)
660 : 0 : return false;
661 : :
662 : 64 : return true;
663 : : }
664 : :
665 : 100 : 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 [ + + ]: 100 : if (isempty(u))
671 : 8 : return false;
672 : :
673 [ + + ]: 92 : if (valid_user_group_name(u))
674 : 32 : return true;
675 : :
676 : 60 : return parse_uid(u, NULL) >= 0;
677 : : }
678 : :
679 : 24 : bool valid_gecos(const char *d) {
680 : :
681 [ + + ]: 24 : if (!d)
682 : 4 : return false;
683 : :
684 [ - + ]: 20 : if (!utf8_is_valid(d))
685 : 0 : return false;
686 : :
687 [ + + ]: 20 : if (string_has_cc(d, NULL))
688 : 4 : return false;
689 : :
690 : : /* Colons are used as field separators, and hence not OK */
691 [ + + ]: 16 : if (strchr(d, ':'))
692 : 4 : return false;
693 : :
694 : 12 : return true;
695 : : }
696 : :
697 : 48 : 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 [ + + ]: 48 : if (isempty(p))
702 : 8 : return false;
703 : :
704 [ - + ]: 40 : if (!utf8_is_valid(p))
705 : 0 : return false;
706 : :
707 [ + + ]: 40 : if (string_has_cc(p, NULL))
708 : 4 : return false;
709 : :
710 [ + + ]: 36 : if (!path_is_absolute(p))
711 : 12 : return false;
712 : :
713 [ + + ]: 24 : if (!path_is_normalized(p))
714 : 8 : return false;
715 : :
716 : : /* Colons are used as field separators, and hence not OK */
717 [ + + ]: 16 : if (strchr(p, ':'))
718 : 4 : return false;
719 : :
720 : 12 : 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 : 284 : 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 [ + + ]: 284 : if (cache < 0)
763 : 36 : cache = access("/etc/systemd/dont-synthesize-nobody", F_OK) < 0;
764 : :
765 : 284 : 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 : 8 : 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 : 8 : r = genuine_random_bytes(raw, sizeof(raw), RANDOM_BLOCK);
895 [ - + ]: 8 : if (r < 0)
896 : 0 : return r;
897 : :
898 : 8 : salt = new(char, 3+sizeof(raw)+1+1);
899 [ - + ]: 8 : 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 : 8 : j = stpcpy(salt, "$6$");
904 [ + + ]: 136 : for (i = 0; i < sizeof(raw); i++)
905 : 128 : j[i] = table[raw[i] & 63];
906 : 8 : j[i++] = '$';
907 : 8 : j[i] = 0;
908 : :
909 : 8 : *ret = salt;
910 : 8 : return 0;
911 : : }
|