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