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