]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/user-util.c
basic: add missing word in comment
[thirdparty/systemd.git] / src / basic / user-util.c
1 /*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
2
3 /***
4 This file is part of systemd.
5
6 Copyright 2010 Lennart Poettering
7
8 systemd is free software; you can redistribute it and/or modify it
9 under the terms of the GNU Lesser General Public License as published by
10 the Free Software Foundation; either version 2.1 of the License, or
11 (at your option) any later version.
12
13 systemd is distributed in the hope that it will be useful, but
14 WITHOUT ANY WARRANTY; without even the implied warranty of
15 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 Lesser General Public License for more details.
17
18 You should have received a copy of the GNU Lesser General Public License
19 along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <alloca.h>
23 #include <errno.h>
24 #include <fcntl.h>
25 #include <grp.h>
26 #include <pwd.h>
27 #include <stddef.h>
28 #include <stdint.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/stat.h>
33 #include <unistd.h>
34
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
44 bool uid_is_valid(uid_t uid) {
45
46 /* Some libc APIs use UID_INVALID as special placeholder */
47 if (uid == (uid_t) UINT32_C(0xFFFFFFFF))
48 return false;
49
50 /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */
51 if (uid == (uid_t) UINT32_C(0xFFFF))
52 return false;
53
54 return true;
55 }
56
57 int parse_uid(const char *s, uid_t *ret) {
58 uint32_t uid = 0;
59 int r;
60
61 assert(s);
62
63 assert_cc(sizeof(uid_t) == sizeof(uint32_t));
64 r = safe_atou32(s, &uid);
65 if (r < 0)
66 return r;
67
68 if (!uid_is_valid(uid))
69 return -ENXIO; /* we return ENXIO instead of EINVAL
70 * here, to make it easy to distuingish
71 * invalid numeric uids from invalid
72 * strings. */
73
74 if (ret)
75 *ret = uid;
76
77 return 0;
78 }
79
80 char* getlogname_malloc(void) {
81 uid_t uid;
82 struct stat st;
83
84 if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
85 uid = st.st_uid;
86 else
87 uid = getuid();
88
89 return uid_to_name(uid);
90 }
91
92 char *getusername_malloc(void) {
93 const char *e;
94
95 e = getenv("USER");
96 if (e)
97 return strdup(e);
98
99 return uid_to_name(getuid());
100 }
101
102 int get_user_creds(
103 const char **username,
104 uid_t *uid, gid_t *gid,
105 const char **home,
106 const char **shell) {
107
108 struct passwd *p;
109 uid_t u;
110
111 assert(username);
112 assert(*username);
113
114 /* We enforce some special rules for uid=0: in order to avoid
115 * NSS lookups for root we hardcode its data. */
116
117 if (streq(*username, "root") || streq(*username, "0")) {
118 *username = "root";
119
120 if (uid)
121 *uid = 0;
122
123 if (gid)
124 *gid = 0;
125
126 if (home)
127 *home = "/root";
128
129 if (shell)
130 *shell = "/bin/sh";
131
132 return 0;
133 }
134
135 if (parse_uid(*username, &u) >= 0) {
136 errno = 0;
137 p = getpwuid(u);
138
139 /* If there are multiple users with the same id, make
140 * sure to leave $USER to the configured value instead
141 * of the first occurrence in the database. However if
142 * the uid was configured by a numeric uid, then let's
143 * pick the real username from /etc/passwd. */
144 if (p)
145 *username = p->pw_name;
146 } else {
147 errno = 0;
148 p = getpwnam(*username);
149 }
150
151 if (!p)
152 return errno > 0 ? -errno : -ESRCH;
153
154 if (uid) {
155 if (!uid_is_valid(p->pw_uid))
156 return -EBADMSG;
157
158 *uid = p->pw_uid;
159 }
160
161 if (gid) {
162 if (!gid_is_valid(p->pw_gid))
163 return -EBADMSG;
164
165 *gid = p->pw_gid;
166 }
167
168 if (home)
169 *home = p->pw_dir;
170
171 if (shell)
172 *shell = p->pw_shell;
173
174 return 0;
175 }
176
177 int get_group_creds(const char **groupname, gid_t *gid) {
178 struct group *g;
179 gid_t id;
180
181 assert(groupname);
182
183 /* We enforce some special rules for gid=0: in order to avoid
184 * NSS lookups for root we hardcode its data. */
185
186 if (streq(*groupname, "root") || streq(*groupname, "0")) {
187 *groupname = "root";
188
189 if (gid)
190 *gid = 0;
191
192 return 0;
193 }
194
195 if (parse_gid(*groupname, &id) >= 0) {
196 errno = 0;
197 g = getgrgid(id);
198
199 if (g)
200 *groupname = g->gr_name;
201 } else {
202 errno = 0;
203 g = getgrnam(*groupname);
204 }
205
206 if (!g)
207 return errno > 0 ? -errno : -ESRCH;
208
209 if (gid) {
210 if (!gid_is_valid(g->gr_gid))
211 return -EBADMSG;
212
213 *gid = g->gr_gid;
214 }
215
216 return 0;
217 }
218
219 char* uid_to_name(uid_t uid) {
220 char *ret;
221 int r;
222
223 /* Shortcut things to avoid NSS lookups */
224 if (uid == 0)
225 return strdup("root");
226
227 if (uid_is_valid(uid)) {
228 long bufsize;
229
230 bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
231 if (bufsize <= 0)
232 bufsize = 4096;
233
234 for (;;) {
235 struct passwd pwbuf, *pw = NULL;
236 _cleanup_free_ char *buf = NULL;
237
238 buf = malloc(bufsize);
239 if (!buf)
240 return NULL;
241
242 r = getpwuid_r(uid, &pwbuf, buf, (size_t) bufsize, &pw);
243 if (r == 0 && pw)
244 return strdup(pw->pw_name);
245 if (r != ERANGE)
246 break;
247
248 bufsize *= 2;
249 }
250 }
251
252 if (asprintf(&ret, UID_FMT, uid) < 0)
253 return NULL;
254
255 return ret;
256 }
257
258 char* gid_to_name(gid_t gid) {
259 char *ret;
260 int r;
261
262 if (gid == 0)
263 return strdup("root");
264
265 if (gid_is_valid(gid)) {
266 long bufsize;
267
268 bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
269 if (bufsize <= 0)
270 bufsize = 4096;
271
272 for (;;) {
273 struct group grbuf, *gr = NULL;
274 _cleanup_free_ char *buf = NULL;
275
276 buf = malloc(bufsize);
277 if (!buf)
278 return NULL;
279
280 r = getgrgid_r(gid, &grbuf, buf, (size_t) bufsize, &gr);
281 if (r == 0 && gr)
282 return strdup(gr->gr_name);
283 if (r != ERANGE)
284 break;
285
286 bufsize *= 2;
287 }
288 }
289
290 if (asprintf(&ret, GID_FMT, gid) < 0)
291 return NULL;
292
293 return ret;
294 }
295
296 int in_gid(gid_t gid) {
297 gid_t *gids;
298 int ngroups_max, r, i;
299
300 if (getgid() == gid)
301 return 1;
302
303 if (getegid() == gid)
304 return 1;
305
306 if (!gid_is_valid(gid))
307 return -EINVAL;
308
309 ngroups_max = sysconf(_SC_NGROUPS_MAX);
310 assert(ngroups_max > 0);
311
312 gids = alloca(sizeof(gid_t) * ngroups_max);
313
314 r = getgroups(ngroups_max, gids);
315 if (r < 0)
316 return -errno;
317
318 for (i = 0; i < r; i++)
319 if (gids[i] == gid)
320 return 1;
321
322 return 0;
323 }
324
325 int in_group(const char *name) {
326 int r;
327 gid_t gid;
328
329 r = get_group_creds(&name, &gid);
330 if (r < 0)
331 return r;
332
333 return in_gid(gid);
334 }
335
336 int get_home_dir(char **_h) {
337 struct passwd *p;
338 const char *e;
339 char *h;
340 uid_t u;
341
342 assert(_h);
343
344 /* Take the user specified one */
345 e = secure_getenv("HOME");
346 if (e && path_is_absolute(e)) {
347 h = strdup(e);
348 if (!h)
349 return -ENOMEM;
350
351 *_h = h;
352 return 0;
353 }
354
355 /* Hardcode home directory for root to avoid NSS */
356 u = getuid();
357 if (u == 0) {
358 h = strdup("/root");
359 if (!h)
360 return -ENOMEM;
361
362 *_h = h;
363 return 0;
364 }
365
366 /* Check the database... */
367 errno = 0;
368 p = getpwuid(u);
369 if (!p)
370 return errno > 0 ? -errno : -ESRCH;
371
372 if (!path_is_absolute(p->pw_dir))
373 return -EINVAL;
374
375 h = strdup(p->pw_dir);
376 if (!h)
377 return -ENOMEM;
378
379 *_h = h;
380 return 0;
381 }
382
383 int get_shell(char **_s) {
384 struct passwd *p;
385 const char *e;
386 char *s;
387 uid_t u;
388
389 assert(_s);
390
391 /* Take the user specified one */
392 e = getenv("SHELL");
393 if (e) {
394 s = strdup(e);
395 if (!s)
396 return -ENOMEM;
397
398 *_s = s;
399 return 0;
400 }
401
402 /* Hardcode home directory for root to avoid NSS */
403 u = getuid();
404 if (u == 0) {
405 s = strdup("/bin/sh");
406 if (!s)
407 return -ENOMEM;
408
409 *_s = s;
410 return 0;
411 }
412
413 /* Check the database... */
414 errno = 0;
415 p = getpwuid(u);
416 if (!p)
417 return errno > 0 ? -errno : -ESRCH;
418
419 if (!path_is_absolute(p->pw_shell))
420 return -EINVAL;
421
422 s = strdup(p->pw_shell);
423 if (!s)
424 return -ENOMEM;
425
426 *_s = s;
427 return 0;
428 }
429
430 int reset_uid_gid(void) {
431
432 if (setgroups(0, NULL) < 0)
433 return -errno;
434
435 if (setresgid(0, 0, 0) < 0)
436 return -errno;
437
438 if (setresuid(0, 0, 0) < 0)
439 return -errno;
440
441 return 0;
442 }
443
444 int take_etc_passwd_lock(const char *root) {
445
446 struct flock flock = {
447 .l_type = F_WRLCK,
448 .l_whence = SEEK_SET,
449 .l_start = 0,
450 .l_len = 0,
451 };
452
453 const char *path;
454 int fd, r;
455
456 /* This is roughly the same as lckpwdf(), but not as awful. We
457 * don't want to use alarm() and signals, hence we implement
458 * our own trivial version of this.
459 *
460 * Note that shadow-utils also takes per-database locks in
461 * addition to lckpwdf(). However, we don't given that they
462 * are redundant as they they invoke lckpwdf() first and keep
463 * it during everything they do. The per-database locks are
464 * awfully racy, and thus we just won't do them. */
465
466 if (root)
467 path = prefix_roota(root, "/etc/.pwd.lock");
468 else
469 path = "/etc/.pwd.lock";
470
471 fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
472 if (fd < 0)
473 return -errno;
474
475 r = fcntl(fd, F_SETLKW, &flock);
476 if (r < 0) {
477 safe_close(fd);
478 return -errno;
479 }
480
481 return fd;
482 }