chiark / gitweb /
97ff2af4a121d6da72eb43d2d81b6631cbb7817b
[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 /// UNNEEDED by elogind
71 #if 0
72 char* getlogname_malloc(void) {
73         uid_t uid;
74         struct stat st;
75
76         if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
77                 uid = st.st_uid;
78         else
79                 uid = getuid();
80
81         return uid_to_name(uid);
82 }
83
84 char *getusername_malloc(void) {
85         const char *e;
86
87         e = getenv("USER");
88         if (e)
89                 return strdup(e);
90
91         return uid_to_name(getuid());
92 }
93 #endif // 0
94
95 int get_user_creds(
96                 const char **username,
97                 uid_t *uid, gid_t *gid,
98                 const char **home,
99                 const char **shell) {
100
101         struct passwd *p;
102         uid_t u;
103
104         assert(username);
105         assert(*username);
106
107         /* We enforce some special rules for uid=0: in order to avoid
108          * NSS lookups for root we hardcode its data. */
109
110         if (streq(*username, "root") || streq(*username, "0")) {
111                 *username = "root";
112
113                 if (uid)
114                         *uid = 0;
115
116                 if (gid)
117                         *gid = 0;
118
119                 if (home)
120                         *home = "/root";
121
122                 if (shell)
123                         *shell = "/bin/sh";
124
125                 return 0;
126         }
127
128         if (parse_uid(*username, &u) >= 0) {
129                 errno = 0;
130                 p = getpwuid(u);
131
132                 /* If there are multiple users with the same id, make
133                  * sure to leave $USER to the configured value instead
134                  * of the first occurrence in the database. However if
135                  * the uid was configured by a numeric uid, then let's
136                  * pick the real username from /etc/passwd. */
137                 if (p)
138                         *username = p->pw_name;
139         } else {
140                 errno = 0;
141                 p = getpwnam(*username);
142         }
143
144         if (!p)
145                 return errno > 0 ? -errno : -ESRCH;
146
147         if (uid) {
148                 if (!uid_is_valid(p->pw_uid))
149                         return -EBADMSG;
150
151                 *uid = p->pw_uid;
152         }
153
154         if (gid) {
155                 if (!gid_is_valid(p->pw_gid))
156                         return -EBADMSG;
157
158                 *gid = p->pw_gid;
159         }
160
161         if (home)
162                 *home = p->pw_dir;
163
164         if (shell)
165                 *shell = p->pw_shell;
166
167         return 0;
168 }
169
170 int get_group_creds(const char **groupname, gid_t *gid) {
171         struct group *g;
172         gid_t id;
173
174         assert(groupname);
175
176         /* We enforce some special rules for gid=0: in order to avoid
177          * NSS lookups for root we hardcode its data. */
178
179         if (streq(*groupname, "root") || streq(*groupname, "0")) {
180                 *groupname = "root";
181
182                 if (gid)
183                         *gid = 0;
184
185                 return 0;
186         }
187
188         if (parse_gid(*groupname, &id) >= 0) {
189                 errno = 0;
190                 g = getgrgid(id);
191
192                 if (g)
193                         *groupname = g->gr_name;
194         } else {
195                 errno = 0;
196                 g = getgrnam(*groupname);
197         }
198
199         if (!g)
200                 return errno > 0 ? -errno : -ESRCH;
201
202         if (gid) {
203                 if (!gid_is_valid(g->gr_gid))
204                         return -EBADMSG;
205
206                 *gid = g->gr_gid;
207         }
208
209         return 0;
210 }
211
212 char* uid_to_name(uid_t uid) {
213         char *ret;
214         int r;
215
216         /* Shortcut things to avoid NSS lookups */
217         if (uid == 0)
218                 return strdup("root");
219
220         if (uid_is_valid(uid)) {
221                 long bufsize;
222
223                 bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
224                 if (bufsize <= 0)
225                         bufsize = 4096;
226
227                 for (;;) {
228                         struct passwd pwbuf, *pw = NULL;
229                         _cleanup_free_ char *buf = NULL;
230
231                         buf = malloc(bufsize);
232                         if (!buf)
233                                 return NULL;
234
235                         r = getpwuid_r(uid, &pwbuf, buf, (size_t) bufsize, &pw);
236                         if (r == 0 && pw)
237                                 return strdup(pw->pw_name);
238                         if (r != ERANGE)
239                                 break;
240
241                         bufsize *= 2;
242                 }
243         }
244
245         if (asprintf(&ret, UID_FMT, uid) < 0)
246                 return NULL;
247
248         return ret;
249 }
250
251 char* gid_to_name(gid_t gid) {
252         char *ret;
253         int r;
254
255         if (gid == 0)
256                 return strdup("root");
257
258         if (gid_is_valid(gid)) {
259                 long bufsize;
260
261                 bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
262                 if (bufsize <= 0)
263                         bufsize = 4096;
264
265                 for (;;) {
266                         struct group grbuf, *gr = NULL;
267                         _cleanup_free_ char *buf = NULL;
268
269                         buf = malloc(bufsize);
270                         if (!buf)
271                                 return NULL;
272
273                         r = getgrgid_r(gid, &grbuf, buf, (size_t) bufsize, &gr);
274                         if (r == 0 && gr)
275                                 return strdup(gr->gr_name);
276                         if (r != ERANGE)
277                                 break;
278
279                         bufsize *= 2;
280                 }
281         }
282
283         if (asprintf(&ret, GID_FMT, gid) < 0)
284                 return NULL;
285
286         return ret;
287 }
288
289 /// UNNEEDED by elogind
290 #if 0
291 int in_gid(gid_t gid) {
292         gid_t *gids;
293         int ngroups_max, r, i;
294
295         if (getgid() == gid)
296                 return 1;
297
298         if (getegid() == gid)
299                 return 1;
300
301         if (!gid_is_valid(gid))
302                 return -EINVAL;
303
304         ngroups_max = sysconf(_SC_NGROUPS_MAX);
305         assert(ngroups_max > 0);
306
307         gids = alloca(sizeof(gid_t) * ngroups_max);
308
309         r = getgroups(ngroups_max, gids);
310         if (r < 0)
311                 return -errno;
312
313         for (i = 0; i < r; i++)
314                 if (gids[i] == gid)
315                         return 1;
316
317         return 0;
318 }
319
320 int in_group(const char *name) {
321         int r;
322         gid_t gid;
323
324         r = get_group_creds(&name, &gid);
325         if (r < 0)
326                 return r;
327
328         return in_gid(gid);
329 }
330
331 int get_home_dir(char **_h) {
332         struct passwd *p;
333         const char *e;
334         char *h;
335         uid_t u;
336
337         assert(_h);
338
339         /* Take the user specified one */
340         e = secure_getenv("HOME");
341         if (e && path_is_absolute(e)) {
342                 h = strdup(e);
343                 if (!h)
344                         return -ENOMEM;
345
346                 *_h = h;
347                 return 0;
348         }
349
350         /* Hardcode home directory for root to avoid NSS */
351         u = getuid();
352         if (u == 0) {
353                 h = strdup("/root");
354                 if (!h)
355                         return -ENOMEM;
356
357                 *_h = h;
358                 return 0;
359         }
360
361         /* Check the database... */
362         errno = 0;
363         p = getpwuid(u);
364         if (!p)
365                 return errno > 0 ? -errno : -ESRCH;
366
367         if (!path_is_absolute(p->pw_dir))
368                 return -EINVAL;
369
370         h = strdup(p->pw_dir);
371         if (!h)
372                 return -ENOMEM;
373
374         *_h = h;
375         return 0;
376 }
377
378 int get_shell(char **_s) {
379         struct passwd *p;
380         const char *e;
381         char *s;
382         uid_t u;
383
384         assert(_s);
385
386         /* Take the user specified one */
387         e = getenv("SHELL");
388         if (e) {
389                 s = strdup(e);
390                 if (!s)
391                         return -ENOMEM;
392
393                 *_s = s;
394                 return 0;
395         }
396
397         /* Hardcode home directory for root to avoid NSS */
398         u = getuid();
399         if (u == 0) {
400                 s = strdup("/bin/sh");
401                 if (!s)
402                         return -ENOMEM;
403
404                 *_s = s;
405                 return 0;
406         }
407
408         /* Check the database... */
409         errno = 0;
410         p = getpwuid(u);
411         if (!p)
412                 return errno > 0 ? -errno : -ESRCH;
413
414         if (!path_is_absolute(p->pw_shell))
415                 return -EINVAL;
416
417         s = strdup(p->pw_shell);
418         if (!s)
419                 return -ENOMEM;
420
421         *_s = s;
422         return 0;
423 }
424 #endif // 0
425
426 int reset_uid_gid(void) {
427
428         if (setgroups(0, NULL) < 0)
429                 return -errno;
430
431         if (setresgid(0, 0, 0) < 0)
432                 return -errno;
433
434         if (setresuid(0, 0, 0) < 0)
435                 return -errno;
436
437         return 0;
438 }
439
440 /// UNNEEDED by elogind
441 #if 0
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 }
481 #endif // 0