chiark / gitweb /
core: add "khash" API to src/basic/ (as wrapper around kernel AF_ALG)
[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 "format-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 #if 0 /// UNNEEDED by elogind
183 int get_user_creds_clean(
184                 const char **username,
185                 uid_t *uid, gid_t *gid,
186                 const char **home,
187                 const char **shell) {
188
189         int r;
190
191         /* Like get_user_creds(), but resets home/shell to NULL if they don't contain anything relevant. */
192
193         r = get_user_creds(username, uid, gid, home, shell);
194         if (r < 0)
195                 return r;
196
197         if (shell &&
198             (isempty(*shell) || PATH_IN_SET(*shell,
199                                             "/bin/nologin",
200                                             "/sbin/nologin",
201                                             "/usr/bin/nologin",
202                                             "/usr/sbin/nologin")))
203                 *shell = NULL;
204
205         if (home &&
206             (isempty(*home) || path_equal(*home, "/")))
207                 *home = NULL;
208
209         return 0;
210 }
211
212 int get_group_creds(const char **groupname, gid_t *gid) {
213         struct group *g;
214         gid_t id;
215
216         assert(groupname);
217
218         /* We enforce some special rules for gid=0: in order to avoid
219          * NSS lookups for root we hardcode its data. */
220
221         if (streq(*groupname, "root") || streq(*groupname, "0")) {
222                 *groupname = "root";
223
224                 if (gid)
225                         *gid = 0;
226
227                 return 0;
228         }
229
230         if (parse_gid(*groupname, &id) >= 0) {
231                 errno = 0;
232                 g = getgrgid(id);
233
234                 if (g)
235                         *groupname = g->gr_name;
236         } else {
237                 errno = 0;
238                 g = getgrnam(*groupname);
239         }
240
241         if (!g)
242                 return errno > 0 ? -errno : -ESRCH;
243
244         if (gid) {
245                 if (!gid_is_valid(g->gr_gid))
246                         return -EBADMSG;
247
248                 *gid = g->gr_gid;
249         }
250
251         return 0;
252 }
253 #endif // 0
254
255 char* uid_to_name(uid_t uid) {
256         char *ret;
257         int r;
258
259         /* Shortcut things to avoid NSS lookups */
260         if (uid == 0)
261                 return strdup("root");
262
263         if (uid_is_valid(uid)) {
264                 long bufsize;
265
266                 bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
267                 if (bufsize <= 0)
268                         bufsize = 4096;
269
270                 for (;;) {
271                         struct passwd pwbuf, *pw = NULL;
272                         _cleanup_free_ char *buf = NULL;
273
274                         buf = malloc(bufsize);
275                         if (!buf)
276                                 return NULL;
277
278                         r = getpwuid_r(uid, &pwbuf, buf, (size_t) bufsize, &pw);
279                         if (r == 0 && pw)
280                                 return strdup(pw->pw_name);
281                         if (r != ERANGE)
282                                 break;
283
284                         bufsize *= 2;
285                 }
286         }
287
288         if (asprintf(&ret, UID_FMT, uid) < 0)
289                 return NULL;
290
291         return ret;
292 }
293
294 char* gid_to_name(gid_t gid) {
295         char *ret;
296         int r;
297
298         if (gid == 0)
299                 return strdup("root");
300
301         if (gid_is_valid(gid)) {
302                 long bufsize;
303
304                 bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
305                 if (bufsize <= 0)
306                         bufsize = 4096;
307
308                 for (;;) {
309                         struct group grbuf, *gr = NULL;
310                         _cleanup_free_ char *buf = NULL;
311
312                         buf = malloc(bufsize);
313                         if (!buf)
314                                 return NULL;
315
316                         r = getgrgid_r(gid, &grbuf, buf, (size_t) bufsize, &gr);
317                         if (r == 0 && gr)
318                                 return strdup(gr->gr_name);
319                         if (r != ERANGE)
320                                 break;
321
322                         bufsize *= 2;
323                 }
324         }
325
326         if (asprintf(&ret, GID_FMT, gid) < 0)
327                 return NULL;
328
329         return ret;
330 }
331
332 #if 0 /// UNNEEDED by elogind
333 int in_gid(gid_t gid) {
334         gid_t *gids;
335         int ngroups_max, r, i;
336
337         if (getgid() == gid)
338                 return 1;
339
340         if (getegid() == gid)
341                 return 1;
342
343         if (!gid_is_valid(gid))
344                 return -EINVAL;
345
346         ngroups_max = sysconf(_SC_NGROUPS_MAX);
347         assert(ngroups_max > 0);
348
349         gids = alloca(sizeof(gid_t) * ngroups_max);
350
351         r = getgroups(ngroups_max, gids);
352         if (r < 0)
353                 return -errno;
354
355         for (i = 0; i < r; i++)
356                 if (gids[i] == gid)
357                         return 1;
358
359         return 0;
360 }
361
362 int in_group(const char *name) {
363         int r;
364         gid_t gid;
365
366         r = get_group_creds(&name, &gid);
367         if (r < 0)
368                 return r;
369
370         return in_gid(gid);
371 }
372
373 int get_home_dir(char **_h) {
374         struct passwd *p;
375         const char *e;
376         char *h;
377         uid_t u;
378
379         assert(_h);
380
381         /* Take the user specified one */
382         e = secure_getenv("HOME");
383         if (e && path_is_absolute(e)) {
384                 h = strdup(e);
385                 if (!h)
386                         return -ENOMEM;
387
388                 *_h = h;
389                 return 0;
390         }
391
392         /* Hardcode home directory for root to avoid NSS */
393         u = getuid();
394         if (u == 0) {
395                 h = strdup("/root");
396                 if (!h)
397                         return -ENOMEM;
398
399                 *_h = h;
400                 return 0;
401         }
402
403         /* Check the database... */
404         errno = 0;
405         p = getpwuid(u);
406         if (!p)
407                 return errno > 0 ? -errno : -ESRCH;
408
409         if (!path_is_absolute(p->pw_dir))
410                 return -EINVAL;
411
412         h = strdup(p->pw_dir);
413         if (!h)
414                 return -ENOMEM;
415
416         *_h = h;
417         return 0;
418 }
419
420 int get_shell(char **_s) {
421         struct passwd *p;
422         const char *e;
423         char *s;
424         uid_t u;
425
426         assert(_s);
427
428         /* Take the user specified one */
429         e = getenv("SHELL");
430         if (e) {
431                 s = strdup(e);
432                 if (!s)
433                         return -ENOMEM;
434
435                 *_s = s;
436                 return 0;
437         }
438
439         /* Hardcode home directory for root to avoid NSS */
440         u = getuid();
441         if (u == 0) {
442                 s = strdup("/bin/sh");
443                 if (!s)
444                         return -ENOMEM;
445
446                 *_s = s;
447                 return 0;
448         }
449
450         /* Check the database... */
451         errno = 0;
452         p = getpwuid(u);
453         if (!p)
454                 return errno > 0 ? -errno : -ESRCH;
455
456         if (!path_is_absolute(p->pw_shell))
457                 return -EINVAL;
458
459         s = strdup(p->pw_shell);
460         if (!s)
461                 return -ENOMEM;
462
463         *_s = s;
464         return 0;
465 }
466 #endif // 0
467
468 int reset_uid_gid(void) {
469         int r;
470
471         r = maybe_setgroups(0, NULL);
472         if (r < 0)
473                 return r;
474
475         if (setresgid(0, 0, 0) < 0)
476                 return -errno;
477
478         if (setresuid(0, 0, 0) < 0)
479                 return -errno;
480
481         return 0;
482 }
483
484 #if 0 /// UNNEEDED by elogind
485 int take_etc_passwd_lock(const char *root) {
486
487         struct flock flock = {
488                 .l_type = F_WRLCK,
489                 .l_whence = SEEK_SET,
490                 .l_start = 0,
491                 .l_len = 0,
492         };
493
494         const char *path;
495         int fd, r;
496
497         /* This is roughly the same as lckpwdf(), but not as awful. We
498          * don't want to use alarm() and signals, hence we implement
499          * our own trivial version of this.
500          *
501          * Note that shadow-utils also takes per-database locks in
502          * addition to lckpwdf(). However, we don't given that they
503          * are redundant as they invoke lckpwdf() first and keep
504          * it during everything they do. The per-database locks are
505          * awfully racy, and thus we just won't do them. */
506
507         if (root)
508                 path = prefix_roota(root, "/etc/.pwd.lock");
509         else
510                 path = "/etc/.pwd.lock";
511
512         fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
513         if (fd < 0)
514                 return -errno;
515
516         r = fcntl(fd, F_SETLKW, &flock);
517         if (r < 0) {
518                 safe_close(fd);
519                 return -errno;
520         }
521
522         return fd;
523 }
524 #endif // 0
525
526 bool valid_user_group_name(const char *u) {
527         const char *i;
528         long sz;
529
530         /* Checks if the specified name is a valid user/group name. */
531
532         if (isempty(u))
533                 return false;
534
535         if (!(u[0] >= 'a' && u[0] <= 'z') &&
536             !(u[0] >= 'A' && u[0] <= 'Z') &&
537             u[0] != '_')
538                 return false;
539
540         for (i = u+1; *i; i++) {
541                 if (!(*i >= 'a' && *i <= 'z') &&
542                     !(*i >= 'A' && *i <= 'Z') &&
543                     !(*i >= '0' && *i <= '9') &&
544                     *i != '_' &&
545                     *i != '-')
546                         return false;
547         }
548
549         sz = sysconf(_SC_LOGIN_NAME_MAX);
550         assert_se(sz > 0);
551
552         if ((size_t) (i-u) > (size_t) sz)
553                 return false;
554
555         if ((size_t) (i-u) > UT_NAMESIZE - 1)
556                 return false;
557
558         return true;
559 }
560
561 bool valid_user_group_name_or_id(const char *u) {
562
563         /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the right
564          * range, and not the invalid user ids. */
565
566         if (isempty(u))
567                 return false;
568
569         if (valid_user_group_name(u))
570                 return true;
571
572         return parse_uid(u, NULL) >= 0;
573 }
574
575 bool valid_gecos(const char *d) {
576
577         if (!d)
578                 return false;
579
580         if (!utf8_is_valid(d))
581                 return false;
582
583         if (string_has_cc(d, NULL))
584                 return false;
585
586         /* Colons are used as field separators, and hence not OK */
587         if (strchr(d, ':'))
588                 return false;
589
590         return true;
591 }
592
593 bool valid_home(const char *p) {
594
595         if (isempty(p))
596                 return false;
597
598         if (!utf8_is_valid(p))
599                 return false;
600
601         if (string_has_cc(p, NULL))
602                 return false;
603
604         if (!path_is_absolute(p))
605                 return false;
606
607         if (!path_is_safe(p))
608                 return false;
609
610         /* Colons are used as field separators, and hence not OK */
611         if (strchr(p, ':'))
612                 return false;
613
614         return true;
615 }
616
617 int maybe_setgroups(size_t size, const gid_t *list) {
618         int r;
619
620         /* Check if setgroups is allowed before we try to drop all the auxiliary groups */
621         if (size == 0) { /* Dropping all aux groups? */
622                 _cleanup_free_ char *setgroups_content = NULL;
623                 bool can_setgroups;
624
625                 r = read_one_line_file("/proc/self/setgroups", &setgroups_content);
626                 if (r == -ENOENT)
627                         /* Old kernels don't have /proc/self/setgroups, so assume we can use setgroups */
628                         can_setgroups = true;
629                 else if (r < 0)
630                         return r;
631                 else
632                         can_setgroups = streq(setgroups_content, "allow");
633
634                 if (!can_setgroups) {
635                         log_debug("Skipping setgroups(), /proc/self/setgroups is set to 'deny'");
636                         return 0;
637                 }
638         }
639
640         if (setgroups(size, list) < 0)
641                 return -errno;
642
643         return 0;
644 }