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