]> git.ipfire.org Git - thirdparty/systemd.git/blame - 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
CommitLineData
b1d4f8e1
LP
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
b1d4f8e1 25#include "macro.h"
6bedfcbb 26#include "parse-util.h"
b1d4f8e1 27#include "path-util.h"
6bedfcbb
LP
28#include "string-util.h"
29#include "user-util.h"
30#include "util.h"
b1d4f8e1
LP
31
32bool uid_is_valid(uid_t uid) {
33
34 /* Some libc APIs use UID_INVALID as special placeholder */
b1d52773 35 if (uid == (uid_t) UINT32_C(0xFFFFFFFF))
b1d4f8e1
LP
36 return false;
37
38 /* A long time ago UIDs where 16bit, hence explicitly avoid the 16bit -1 too */
b1d52773 39 if (uid == (uid_t) UINT32_C(0xFFFF))
b1d4f8e1
LP
40 return false;
41
42 return true;
43}
44
b1d52773
LP
45int parse_uid(const char *s, uid_t *ret) {
46 uint32_t uid = 0;
b1d4f8e1
LP
47 int r;
48
49 assert(s);
50
b1d52773
LP
51 assert_cc(sizeof(uid_t) == sizeof(uint32_t));
52 r = safe_atou32(s, &uid);
b1d4f8e1
LP
53 if (r < 0)
54 return r;
55
b1d4f8e1
LP
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
b1d52773
LP
62 if (ret)
63 *ret = uid;
b1d4f8e1
LP
64
65 return 0;
66}
67
b1d4f8e1
LP
68char* 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
d0260817 77 return uid_to_name(uid);
b1d4f8e1
LP
78}
79
80char *getusername_malloc(void) {
81 const char *e;
82
83 e = getenv("USER");
84 if (e)
85 return strdup(e);
86
d0260817 87 return uid_to_name(getuid());
b1d4f8e1
LP
88}
89
90int 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
67c7c892
LP
142 if (uid) {
143 if (!uid_is_valid(p->pw_uid))
144 return -EBADMSG;
145
b1d4f8e1 146 *uid = p->pw_uid;
67c7c892
LP
147 }
148
149 if (gid) {
150 if (!gid_is_valid(p->pw_gid))
151 return -EBADMSG;
b1d4f8e1 152
b1d4f8e1 153 *gid = p->pw_gid;
67c7c892 154 }
b1d4f8e1
LP
155
156 if (home)
157 *home = p->pw_dir;
158
159 if (shell)
160 *shell = p->pw_shell;
161
162 return 0;
163}
164
165int 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
67c7c892
LP
197 if (gid) {
198 if (!gid_is_valid(g->gr_gid))
199 return -EBADMSG;
200
b1d4f8e1 201 *gid = g->gr_gid;
67c7c892 202 }
b1d4f8e1
LP
203
204 return 0;
205}
206
207char* uid_to_name(uid_t uid) {
d0260817
LP
208 char *ret;
209 int r;
b1d4f8e1 210
d0260817 211 /* Shortcut things to avoid NSS lookups */
b1d4f8e1
LP
212 if (uid == 0)
213 return strdup("root");
214
d0260817
LP
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 }
b1d4f8e1 239
d0260817 240 if (asprintf(&ret, UID_FMT, uid) < 0)
b1d4f8e1
LP
241 return NULL;
242
d0260817 243 return ret;
b1d4f8e1
LP
244}
245
246char* gid_to_name(gid_t gid) {
d0260817
LP
247 char *ret;
248 int r;
b1d4f8e1
LP
249
250 if (gid == 0)
251 return strdup("root");
252
d0260817
LP
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 }
b1d4f8e1 277
d0260817 278 if (asprintf(&ret, GID_FMT, gid) < 0)
b1d4f8e1
LP
279 return NULL;
280
d0260817 281 return ret;
b1d4f8e1
LP
282}
283
284int 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
67c7c892
LP
294 if (!gid_is_valid(gid))
295 return -EINVAL;
296
b1d4f8e1
LP
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
313int 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
324int 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
371int 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
418int 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}