chiark / gitweb /
Prep v228: Add remaining updates from upstream (3/3)
[elogind.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 <grp.h>
23 #include <pwd.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 }