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