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