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