]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/user-util.c
Merge pull request #3005 from keszybz/kill-user-proceses
[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 "missing.h"
34 #include "alloc-util.h"
35 #include "fd-util.h"
36 #include "formats-util.h"
37 #include "macro.h"
38 #include "parse-util.h"
39 #include "path-util.h"
40 #include "string-util.h"
41 #include "user-util.h"
42
43 bool uid_is_valid(uid_t uid) {
44
45 /* Some libc APIs use UID_INVALID as special placeholder */
46 if (uid == (uid_t) UINT32_C(0xFFFFFFFF))
47 return false;
48
49 /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */
50 if (uid == (uid_t) UINT32_C(0xFFFF))
51 return false;
52
53 return true;
54 }
55
56 int parse_uid(const char *s, uid_t *ret) {
57 uint32_t uid = 0;
58 int r;
59
60 assert(s);
61
62 assert_cc(sizeof(uid_t) == sizeof(uint32_t));
63 r = safe_atou32(s, &uid);
64 if (r < 0)
65 return r;
66
67 if (!uid_is_valid(uid))
68 return -ENXIO; /* we return ENXIO instead of EINVAL
69 * here, to make it easy to distuingish
70 * invalid numeric uids from invalid
71 * strings. */
72
73 if (ret)
74 *ret = uid;
75
76 return 0;
77 }
78
79 char* 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
88 return uid_to_name(uid);
89 }
90
91 char *getusername_malloc(void) {
92 const char *e;
93
94 e = getenv("USER");
95 if (e)
96 return strdup(e);
97
98 return uid_to_name(getuid());
99 }
100
101 int 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
153 if (uid) {
154 if (!uid_is_valid(p->pw_uid))
155 return -EBADMSG;
156
157 *uid = p->pw_uid;
158 }
159
160 if (gid) {
161 if (!gid_is_valid(p->pw_gid))
162 return -EBADMSG;
163
164 *gid = p->pw_gid;
165 }
166
167 if (home)
168 *home = p->pw_dir;
169
170 if (shell)
171 *shell = p->pw_shell;
172
173 return 0;
174 }
175
176 int 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
208 if (gid) {
209 if (!gid_is_valid(g->gr_gid))
210 return -EBADMSG;
211
212 *gid = g->gr_gid;
213 }
214
215 return 0;
216 }
217
218 char* uid_to_name(uid_t uid) {
219 char *ret;
220 int r;
221
222 /* Shortcut things to avoid NSS lookups */
223 if (uid == 0)
224 return strdup("root");
225
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 }
250
251 if (asprintf(&ret, UID_FMT, uid) < 0)
252 return NULL;
253
254 return ret;
255 }
256
257 char* gid_to_name(gid_t gid) {
258 char *ret;
259 int r;
260
261 if (gid == 0)
262 return strdup("root");
263
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 }
288
289 if (asprintf(&ret, GID_FMT, gid) < 0)
290 return NULL;
291
292 return ret;
293 }
294
295 int 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
305 if (!gid_is_valid(gid))
306 return -EINVAL;
307
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
324 int 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
335 int 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
382 int 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
429 int 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 }
442
443 int 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
461 * are redundant as they they invoke lckpwdf() first and keep
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 }