chiark / gitweb /
user-util: minor updates of in_gid()
[elogind.git] / src / basic / user-util.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2 /***
3   This file is part of systemd.
4
5   Copyright 2010 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 <alloca.h>
22 #include <errno.h>
23 #include <fcntl.h>
24 #include <grp.h>
25 #include <pwd.h>
26 #include <stddef.h>
27 #include <stdint.h>
28 #include <stdio.h>
29 #include <stdlib.h>
30 #include <string.h>
31 #include <sys/stat.h>
32 #include <unistd.h>
33 #include <utmp.h>
34
35 #include "alloc-util.h"
36 #include "fd-util.h"
37 #include "fileio.h"
38 #include "format-util.h"
39 #include "macro.h"
40 #include "missing.h"
41 #include "parse-util.h"
42 #include "path-util.h"
43 #include "string-util.h"
44 #include "strv.h"
45 #include "user-util.h"
46 #include "utf8.h"
47
48 bool uid_is_valid(uid_t uid) {
49
50         /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.436. */
51
52         /* Some libc APIs use UID_INVALID as special placeholder */
53         if (uid == (uid_t) UINT32_C(0xFFFFFFFF))
54                 return false;
55
56         /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */
57         if (uid == (uid_t) UINT32_C(0xFFFF))
58                 return false;
59
60         return true;
61 }
62
63 int parse_uid(const char *s, uid_t *ret) {
64         uint32_t uid = 0;
65         int r;
66
67         assert(s);
68
69         assert_cc(sizeof(uid_t) == sizeof(uint32_t));
70         r = safe_atou32(s, &uid);
71         if (r < 0)
72                 return r;
73
74         if (!uid_is_valid(uid))
75                 return -ENXIO; /* we return ENXIO instead of EINVAL
76                                 * here, to make it easy to distuingish
77                                 * invalid numeric uids from invalid
78                                 * strings. */
79
80         if (ret)
81                 *ret = uid;
82
83         return 0;
84 }
85
86 char* getlogname_malloc(void) {
87         uid_t uid;
88         struct stat st;
89
90         if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
91                 uid = st.st_uid;
92         else
93                 uid = getuid();
94
95         return uid_to_name(uid);
96 }
97
98 #if 0 /// UNNEEDED by elogind
99 char *getusername_malloc(void) {
100         const char *e;
101
102         e = getenv("USER");
103         if (e)
104                 return strdup(e);
105
106         return uid_to_name(getuid());
107 }
108 #endif // 0
109
110 int get_user_creds(
111                 const char **username,
112                 uid_t *uid, gid_t *gid,
113                 const char **home,
114                 const char **shell) {
115
116         struct passwd *p;
117         uid_t u;
118
119         assert(username);
120         assert(*username);
121
122         /* We enforce some special rules for uid=0 and uid=65534: in order to avoid NSS lookups for root we hardcode
123          * their user record data. */
124
125         if (STR_IN_SET(*username, "root", "0")) {
126                 *username = "root";
127
128                 if (uid)
129                         *uid = 0;
130                 if (gid)
131                         *gid = 0;
132
133                 if (home)
134                         *home = "/root";
135
136                 if (shell)
137                         *shell = "/bin/sh";
138
139                 return 0;
140         }
141
142         if (STR_IN_SET(*username, NOBODY_USER_NAME, "65534")) {
143                 *username = NOBODY_USER_NAME;
144
145                 if (uid)
146                         *uid = UID_NOBODY;
147                 if (gid)
148                         *gid = GID_NOBODY;
149
150                 if (home)
151                         *home = "/";
152
153                 if (shell)
154                         *shell = "/sbin/nologin";
155
156                 return 0;
157         }
158
159         if (parse_uid(*username, &u) >= 0) {
160                 errno = 0;
161                 p = getpwuid(u);
162
163                 /* If there are multiple users with the same id, make
164                  * sure to leave $USER to the configured value instead
165                  * of the first occurrence in the database. However if
166                  * the uid was configured by a numeric uid, then let's
167                  * pick the real username from /etc/passwd. */
168                 if (p)
169                         *username = p->pw_name;
170         } else {
171                 errno = 0;
172                 p = getpwnam(*username);
173         }
174
175         if (!p)
176                 return errno > 0 ? -errno : -ESRCH;
177
178         if (uid) {
179                 if (!uid_is_valid(p->pw_uid))
180                         return -EBADMSG;
181
182                 *uid = p->pw_uid;
183         }
184
185         if (gid) {
186                 if (!gid_is_valid(p->pw_gid))
187                         return -EBADMSG;
188
189                 *gid = p->pw_gid;
190         }
191
192         if (home)
193                 *home = p->pw_dir;
194
195         if (shell)
196                 *shell = p->pw_shell;
197
198         return 0;
199 }
200
201 #if 0 /// UNNEEDED by elogind
202 int get_user_creds_clean(
203                 const char **username,
204                 uid_t *uid, gid_t *gid,
205                 const char **home,
206                 const char **shell) {
207
208         int r;
209
210         /* Like get_user_creds(), but resets home/shell to NULL if they don't contain anything relevant. */
211
212         r = get_user_creds(username, uid, gid, home, shell);
213         if (r < 0)
214                 return r;
215
216         if (shell &&
217             (isempty(*shell) || PATH_IN_SET(*shell,
218                                             "/bin/nologin",
219                                             "/sbin/nologin",
220                                             "/usr/bin/nologin",
221                                             "/usr/sbin/nologin")))
222                 *shell = NULL;
223
224         if (home &&
225             (isempty(*home) || path_equal(*home, "/")))
226                 *home = NULL;
227
228         return 0;
229 }
230
231 int get_group_creds(const char **groupname, gid_t *gid) {
232         struct group *g;
233         gid_t id;
234
235         assert(groupname);
236
237         /* We enforce some special rules for gid=0: in order to avoid
238          * NSS lookups for root we hardcode its data. */
239
240         if (STR_IN_SET(*groupname, "root", "0")) {
241                 *groupname = "root";
242
243                 if (gid)
244                         *gid = 0;
245
246                 return 0;
247         }
248
249         if (STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534")) {
250                 *groupname = NOBODY_GROUP_NAME;
251
252                 if (gid)
253                         *gid = GID_NOBODY;
254
255                 return 0;
256         }
257
258         if (parse_gid(*groupname, &id) >= 0) {
259                 errno = 0;
260                 g = getgrgid(id);
261
262                 if (g)
263                         *groupname = g->gr_name;
264         } else {
265                 errno = 0;
266                 g = getgrnam(*groupname);
267         }
268
269         if (!g)
270                 return errno > 0 ? -errno : -ESRCH;
271
272         if (gid) {
273                 if (!gid_is_valid(g->gr_gid))
274                         return -EBADMSG;
275
276                 *gid = g->gr_gid;
277         }
278
279         return 0;
280 }
281 #endif // 0
282
283 char* uid_to_name(uid_t uid) {
284         char *ret;
285         int r;
286
287         /* Shortcut things to avoid NSS lookups */
288         if (uid == 0)
289                 return strdup("root");
290         if (uid == UID_NOBODY)
291                 return strdup(NOBODY_USER_NAME);
292
293         if (uid_is_valid(uid)) {
294                 long bufsize;
295
296                 bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
297                 if (bufsize <= 0)
298                         bufsize = 4096;
299
300                 for (;;) {
301                         struct passwd pwbuf, *pw = NULL;
302                         _cleanup_free_ char *buf = NULL;
303
304                         buf = malloc(bufsize);
305                         if (!buf)
306                                 return NULL;
307
308                         r = getpwuid_r(uid, &pwbuf, buf, (size_t) bufsize, &pw);
309                         if (r == 0 && pw)
310                                 return strdup(pw->pw_name);
311                         if (r != ERANGE)
312                                 break;
313
314                         bufsize *= 2;
315                 }
316         }
317
318         if (asprintf(&ret, UID_FMT, uid) < 0)
319                 return NULL;
320
321         return ret;
322 }
323
324 char* gid_to_name(gid_t gid) {
325         char *ret;
326         int r;
327
328         if (gid == 0)
329                 return strdup("root");
330         if (gid == GID_NOBODY)
331                 return strdup(NOBODY_GROUP_NAME);
332
333         if (gid_is_valid(gid)) {
334                 long bufsize;
335
336                 bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
337                 if (bufsize <= 0)
338                         bufsize = 4096;
339
340                 for (;;) {
341                         struct group grbuf, *gr = NULL;
342                         _cleanup_free_ char *buf = NULL;
343
344                         buf = malloc(bufsize);
345                         if (!buf)
346                                 return NULL;
347
348                         r = getgrgid_r(gid, &grbuf, buf, (size_t) bufsize, &gr);
349                         if (r == 0 && gr)
350                                 return strdup(gr->gr_name);
351                         if (r != ERANGE)
352                                 break;
353
354                         bufsize *= 2;
355                 }
356         }
357
358         if (asprintf(&ret, GID_FMT, gid) < 0)
359                 return NULL;
360
361         return ret;
362 }
363
364 #if 0 /// UNNEEDED by elogind
365 int in_gid(gid_t gid) {
366         long ngroups_max;
367         gid_t *gids;
368         int r, i;
369
370         if (getgid() == gid)
371                 return 1;
372
373         if (getegid() == gid)
374                 return 1;
375
376         if (!gid_is_valid(gid))
377                 return -EINVAL;
378
379         ngroups_max = sysconf(_SC_NGROUPS_MAX);
380         assert(ngroups_max > 0);
381
382         gids = newa(gid_t, ngroups_max);
383
384         r = getgroups(ngroups_max, gids);
385         if (r < 0)
386                 return -errno;
387
388         for (i = 0; i < r; i++)
389                 if (gids[i] == gid)
390                         return 1;
391
392         return 0;
393 }
394
395 int in_group(const char *name) {
396         int r;
397         gid_t gid;
398
399         r = get_group_creds(&name, &gid);
400         if (r < 0)
401                 return r;
402
403         return in_gid(gid);
404 }
405
406 int get_home_dir(char **_h) {
407         struct passwd *p;
408         const char *e;
409         char *h;
410         uid_t u;
411
412         assert(_h);
413
414         /* Take the user specified one */
415         e = secure_getenv("HOME");
416         if (e && path_is_absolute(e)) {
417                 h = strdup(e);
418                 if (!h)
419                         return -ENOMEM;
420
421                 *_h = h;
422                 return 0;
423         }
424
425         /* Hardcode home directory for root and nobody to avoid NSS */
426         u = getuid();
427         if (u == 0) {
428                 h = strdup("/root");
429                 if (!h)
430                         return -ENOMEM;
431
432                 *_h = h;
433                 return 0;
434         }
435         if (u == UID_NOBODY) {
436                 h = strdup("/");
437                 if (!h)
438                         return -ENOMEM;
439
440                 *_h = h;
441                 return 0;
442         }
443
444         /* Check the database... */
445         errno = 0;
446         p = getpwuid(u);
447         if (!p)
448                 return errno > 0 ? -errno : -ESRCH;
449
450         if (!path_is_absolute(p->pw_dir))
451                 return -EINVAL;
452
453         h = strdup(p->pw_dir);
454         if (!h)
455                 return -ENOMEM;
456
457         *_h = h;
458         return 0;
459 }
460
461 int get_shell(char **_s) {
462         struct passwd *p;
463         const char *e;
464         char *s;
465         uid_t u;
466
467         assert(_s);
468
469         /* Take the user specified one */
470         e = getenv("SHELL");
471         if (e) {
472                 s = strdup(e);
473                 if (!s)
474                         return -ENOMEM;
475
476                 *_s = s;
477                 return 0;
478         }
479
480         /* Hardcode shell for root and nobody to avoid NSS */
481         u = getuid();
482         if (u == 0) {
483                 s = strdup("/bin/sh");
484                 if (!s)
485                         return -ENOMEM;
486
487                 *_s = s;
488                 return 0;
489         }
490         if (u == UID_NOBODY) {
491                 s = strdup("/sbin/nologin");
492                 if (!s)
493                         return -ENOMEM;
494
495                 *_s = s;
496                 return 0;
497         }
498
499         /* Check the database... */
500         errno = 0;
501         p = getpwuid(u);
502         if (!p)
503                 return errno > 0 ? -errno : -ESRCH;
504
505         if (!path_is_absolute(p->pw_shell))
506                 return -EINVAL;
507
508         s = strdup(p->pw_shell);
509         if (!s)
510                 return -ENOMEM;
511
512         *_s = s;
513         return 0;
514 }
515 #endif // 0
516
517 int reset_uid_gid(void) {
518         int r;
519
520         r = maybe_setgroups(0, NULL);
521         if (r < 0)
522                 return r;
523
524         if (setresgid(0, 0, 0) < 0)
525                 return -errno;
526
527         if (setresuid(0, 0, 0) < 0)
528                 return -errno;
529
530         return 0;
531 }
532
533 #if 0 /// UNNEEDED by elogind
534 int take_etc_passwd_lock(const char *root) {
535
536         struct flock flock = {
537                 .l_type = F_WRLCK,
538                 .l_whence = SEEK_SET,
539                 .l_start = 0,
540                 .l_len = 0,
541         };
542
543         const char *path;
544         int fd, r;
545
546         /* This is roughly the same as lckpwdf(), but not as awful. We
547          * don't want to use alarm() and signals, hence we implement
548          * our own trivial version of this.
549          *
550          * Note that shadow-utils also takes per-database locks in
551          * addition to lckpwdf(). However, we don't given that they
552          * are redundant as they invoke lckpwdf() first and keep
553          * it during everything they do. The per-database locks are
554          * awfully racy, and thus we just won't do them. */
555
556         if (root)
557                 path = prefix_roota(root, "/etc/.pwd.lock");
558         else
559                 path = "/etc/.pwd.lock";
560
561         fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
562         if (fd < 0)
563                 return -errno;
564
565         r = fcntl(fd, F_SETLKW, &flock);
566         if (r < 0) {
567                 safe_close(fd);
568                 return -errno;
569         }
570
571         return fd;
572 }
573 #endif // 0
574
575 bool valid_user_group_name(const char *u) {
576         const char *i;
577         long sz;
578
579         /* Checks if the specified name is a valid user/group name. Also see POSIX IEEE Std 1003.1-2008, 2016 Edition,
580          * 3.437. We are a bit stricter here however. Specifically we deviate from POSIX rules:
581          *
582          * - We don't allow any dots (this would break chown syntax which permits dots as user/group name separator)
583          * - We require that names fit into the appropriate utmp field
584          * - We don't allow empty user names
585          *
586          * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters.
587          */
588
589         if (isempty(u))
590                 return false;
591
592         if (!(u[0] >= 'a' && u[0] <= 'z') &&
593             !(u[0] >= 'A' && u[0] <= 'Z') &&
594             u[0] != '_')
595                 return false;
596
597         for (i = u+1; *i; i++) {
598                 if (!(*i >= 'a' && *i <= 'z') &&
599                     !(*i >= 'A' && *i <= 'Z') &&
600                     !(*i >= '0' && *i <= '9') &&
601                     !IN_SET(*i, '_', '-'))
602                         return false;
603         }
604
605         sz = sysconf(_SC_LOGIN_NAME_MAX);
606         assert_se(sz > 0);
607
608         if ((size_t) (i-u) > (size_t) sz)
609                 return false;
610
611         if ((size_t) (i-u) > UT_NAMESIZE - 1)
612                 return false;
613
614         return true;
615 }
616
617 bool valid_user_group_name_or_id(const char *u) {
618
619         /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the right
620          * range, and not the invalid user ids. */
621
622         if (isempty(u))
623                 return false;
624
625         if (valid_user_group_name(u))
626                 return true;
627
628         return parse_uid(u, NULL) >= 0;
629 }
630
631 bool valid_gecos(const char *d) {
632
633         if (!d)
634                 return false;
635
636         if (!utf8_is_valid(d))
637                 return false;
638
639         if (string_has_cc(d, NULL))
640                 return false;
641
642         /* Colons are used as field separators, and hence not OK */
643         if (strchr(d, ':'))
644                 return false;
645
646         return true;
647 }
648
649 bool valid_home(const char *p) {
650
651         if (isempty(p))
652                 return false;
653
654         if (!utf8_is_valid(p))
655                 return false;
656
657         if (string_has_cc(p, NULL))
658                 return false;
659
660         if (!path_is_absolute(p))
661                 return false;
662
663         if (!path_is_normalized(p))
664                 return false;
665
666         /* Colons are used as field separators, and hence not OK */
667         if (strchr(p, ':'))
668                 return false;
669
670         return true;
671 }
672
673 int maybe_setgroups(size_t size, const gid_t *list) {
674         int r;
675
676         /* Check if setgroups is allowed before we try to drop all the auxiliary groups */
677         if (size == 0) { /* Dropping all aux groups? */
678                 _cleanup_free_ char *setgroups_content = NULL;
679                 bool can_setgroups;
680
681                 r = read_one_line_file("/proc/self/setgroups", &setgroups_content);
682                 if (r == -ENOENT)
683                         /* Old kernels don't have /proc/self/setgroups, so assume we can use setgroups */
684                         can_setgroups = true;
685                 else if (r < 0)
686                         return r;
687                 else
688                         can_setgroups = streq(setgroups_content, "allow");
689
690                 if (!can_setgroups) {
691                         log_debug("Skipping setgroups(), /proc/self/setgroups is set to 'deny'");
692                         return 0;
693                 }
694         }
695
696         if (setgroups(size, list) < 0)
697                 return -errno;
698
699         return 0;
700 }