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