]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/basic/user-util.c
user-util: simplify uid parsing a bit
[thirdparty/systemd.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 <pwd.h>
23 #include <grp.h>
24
25 #include "user-util.h"
26 #include "macro.h"
27 #include "util.h"
28 #include "string-util.h"
29 #include "path-util.h"
30
31 bool uid_is_valid(uid_t uid) {
32
33 /* Some libc APIs use UID_INVALID as special placeholder */
34 if (uid == (uid_t) UINT32_C(0xFFFFFFFF))
35 return false;
36
37 /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */
38 if (uid == (uid_t) UINT32_C(0xFFFF))
39 return false;
40
41 return true;
42 }
43
44 int parse_uid(const char *s, uid_t *ret) {
45 uint32_t uid = 0;
46 int r;
47
48 assert(s);
49
50 assert_cc(sizeof(uid_t) == sizeof(uint32_t));
51 r = safe_atou32(s, &uid);
52 if (r < 0)
53 return r;
54
55 if (!uid_is_valid(uid))
56 return -ENXIO; /* we return ENXIO instead of EINVAL
57 * here, to make it easy to distuingish
58 * invalid numeric uids invalid
59 * strings. */
60
61 if (ret)
62 *ret = uid;
63
64 return 0;
65 }
66
67 char *lookup_uid(uid_t uid) {
68 long bufsize;
69 char *name;
70 _cleanup_free_ char *buf = NULL;
71 struct passwd pwbuf, *pw = NULL;
72
73 /* Shortcut things to avoid NSS lookups */
74 if (uid == 0)
75 return strdup("root");
76
77 bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
78 if (bufsize <= 0)
79 bufsize = 4096;
80
81 buf = malloc(bufsize);
82 if (!buf)
83 return NULL;
84
85 if (getpwuid_r(uid, &pwbuf, buf, bufsize, &pw) == 0 && pw)
86 return strdup(pw->pw_name);
87
88 if (asprintf(&name, UID_FMT, uid) < 0)
89 return NULL;
90
91 return name;
92 }
93
94 char* getlogname_malloc(void) {
95 uid_t uid;
96 struct stat st;
97
98 if (isatty(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
99 uid = st.st_uid;
100 else
101 uid = getuid();
102
103 return lookup_uid(uid);
104 }
105
106 char *getusername_malloc(void) {
107 const char *e;
108
109 e = getenv("USER");
110 if (e)
111 return strdup(e);
112
113 return lookup_uid(getuid());
114 }
115
116 int get_user_creds(
117 const char **username,
118 uid_t *uid, gid_t *gid,
119 const char **home,
120 const char **shell) {
121
122 struct passwd *p;
123 uid_t u;
124
125 assert(username);
126 assert(*username);
127
128 /* We enforce some special rules for uid=0: in order to avoid
129 * NSS lookups for root we hardcode its data. */
130
131 if (streq(*username, "root") || streq(*username, "0")) {
132 *username = "root";
133
134 if (uid)
135 *uid = 0;
136
137 if (gid)
138 *gid = 0;
139
140 if (home)
141 *home = "/root";
142
143 if (shell)
144 *shell = "/bin/sh";
145
146 return 0;
147 }
148
149 if (parse_uid(*username, &u) >= 0) {
150 errno = 0;
151 p = getpwuid(u);
152
153 /* If there are multiple users with the same id, make
154 * sure to leave $USER to the configured value instead
155 * of the first occurrence in the database. However if
156 * the uid was configured by a numeric uid, then let's
157 * pick the real username from /etc/passwd. */
158 if (p)
159 *username = p->pw_name;
160 } else {
161 errno = 0;
162 p = getpwnam(*username);
163 }
164
165 if (!p)
166 return errno > 0 ? -errno : -ESRCH;
167
168 if (uid)
169 *uid = p->pw_uid;
170
171 if (gid)
172 *gid = p->pw_gid;
173
174 if (home)
175 *home = p->pw_dir;
176
177 if (shell)
178 *shell = p->pw_shell;
179
180 return 0;
181 }
182
183 int get_group_creds(const char **groupname, gid_t *gid) {
184 struct group *g;
185 gid_t id;
186
187 assert(groupname);
188
189 /* We enforce some special rules for gid=0: in order to avoid
190 * NSS lookups for root we hardcode its data. */
191
192 if (streq(*groupname, "root") || streq(*groupname, "0")) {
193 *groupname = "root";
194
195 if (gid)
196 *gid = 0;
197
198 return 0;
199 }
200
201 if (parse_gid(*groupname, &id) >= 0) {
202 errno = 0;
203 g = getgrgid(id);
204
205 if (g)
206 *groupname = g->gr_name;
207 } else {
208 errno = 0;
209 g = getgrnam(*groupname);
210 }
211
212 if (!g)
213 return errno > 0 ? -errno : -ESRCH;
214
215 if (gid)
216 *gid = g->gr_gid;
217
218 return 0;
219 }
220
221 char* uid_to_name(uid_t uid) {
222 struct passwd *p;
223 char *r;
224
225 if (uid == 0)
226 return strdup("root");
227
228 p = getpwuid(uid);
229 if (p)
230 return strdup(p->pw_name);
231
232 if (asprintf(&r, UID_FMT, uid) < 0)
233 return NULL;
234
235 return r;
236 }
237
238 char* gid_to_name(gid_t gid) {
239 struct group *p;
240 char *r;
241
242 if (gid == 0)
243 return strdup("root");
244
245 p = getgrgid(gid);
246 if (p)
247 return strdup(p->gr_name);
248
249 if (asprintf(&r, GID_FMT, gid) < 0)
250 return NULL;
251
252 return r;
253 }
254
255 int in_gid(gid_t gid) {
256 gid_t *gids;
257 int ngroups_max, r, i;
258
259 if (getgid() == gid)
260 return 1;
261
262 if (getegid() == gid)
263 return 1;
264
265 ngroups_max = sysconf(_SC_NGROUPS_MAX);
266 assert(ngroups_max > 0);
267
268 gids = alloca(sizeof(gid_t) * ngroups_max);
269
270 r = getgroups(ngroups_max, gids);
271 if (r < 0)
272 return -errno;
273
274 for (i = 0; i < r; i++)
275 if (gids[i] == gid)
276 return 1;
277
278 return 0;
279 }
280
281 int in_group(const char *name) {
282 int r;
283 gid_t gid;
284
285 r = get_group_creds(&name, &gid);
286 if (r < 0)
287 return r;
288
289 return in_gid(gid);
290 }
291
292 int get_home_dir(char **_h) {
293 struct passwd *p;
294 const char *e;
295 char *h;
296 uid_t u;
297
298 assert(_h);
299
300 /* Take the user specified one */
301 e = secure_getenv("HOME");
302 if (e && path_is_absolute(e)) {
303 h = strdup(e);
304 if (!h)
305 return -ENOMEM;
306
307 *_h = h;
308 return 0;
309 }
310
311 /* Hardcode home directory for root to avoid NSS */
312 u = getuid();
313 if (u == 0) {
314 h = strdup("/root");
315 if (!h)
316 return -ENOMEM;
317
318 *_h = h;
319 return 0;
320 }
321
322 /* Check the database... */
323 errno = 0;
324 p = getpwuid(u);
325 if (!p)
326 return errno > 0 ? -errno : -ESRCH;
327
328 if (!path_is_absolute(p->pw_dir))
329 return -EINVAL;
330
331 h = strdup(p->pw_dir);
332 if (!h)
333 return -ENOMEM;
334
335 *_h = h;
336 return 0;
337 }
338
339 int get_shell(char **_s) {
340 struct passwd *p;
341 const char *e;
342 char *s;
343 uid_t u;
344
345 assert(_s);
346
347 /* Take the user specified one */
348 e = getenv("SHELL");
349 if (e) {
350 s = strdup(e);
351 if (!s)
352 return -ENOMEM;
353
354 *_s = s;
355 return 0;
356 }
357
358 /* Hardcode home directory for root to avoid NSS */
359 u = getuid();
360 if (u == 0) {
361 s = strdup("/bin/sh");
362 if (!s)
363 return -ENOMEM;
364
365 *_s = s;
366 return 0;
367 }
368
369 /* Check the database... */
370 errno = 0;
371 p = getpwuid(u);
372 if (!p)
373 return errno > 0 ? -errno : -ESRCH;
374
375 if (!path_is_absolute(p->pw_shell))
376 return -EINVAL;
377
378 s = strdup(p->pw_shell);
379 if (!s)
380 return -ENOMEM;
381
382 *_s = s;
383 return 0;
384 }
385
386 int reset_uid_gid(void) {
387
388 if (setgroups(0, NULL) < 0)
389 return -errno;
390
391 if (setresgid(0, 0, 0) < 0)
392 return -errno;
393
394 if (setresuid(0, 0, 0) < 0)
395 return -errno;
396
397 return 0;
398 }