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