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