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