]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/basic/user-util.c
Remove 'inline' attributes from static functions in .c files (#11426)
[thirdparty/systemd.git] / src / basic / user-util.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
b1d4f8e1 2
11c3a366
TA
3#include <alloca.h>
4#include <errno.h>
5#include <fcntl.h>
b1d4f8e1 6#include <grp.h>
cf0fbc49 7#include <pwd.h>
11c3a366
TA
8#include <stddef.h>
9#include <stdint.h>
10#include <stdio.h>
11#include <stdlib.h>
12#include <string.h>
13#include <sys/stat.h>
14#include <unistd.h>
e4631b48 15#include <utmp.h>
b1d4f8e1 16
b5efdb8a 17#include "alloc-util.h"
e929bee0 18#include "fd-util.h"
36d85478 19#include "fileio.h"
f97b34a6 20#include "format-util.h"
b1d4f8e1 21#include "macro.h"
be39ccf3 22#include "missing.h"
6bedfcbb 23#include "parse-util.h"
b1d4f8e1 24#include "path-util.h"
6bedfcbb 25#include "string-util.h"
be39ccf3 26#include "strv.h"
6bedfcbb 27#include "user-util.h"
e4631b48 28#include "utf8.h"
b1d4f8e1
LP
29
30bool uid_is_valid(uid_t uid) {
31
1429dfe5
LP
32 /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.436. */
33
b1d4f8e1 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
ba60af86 59 * invalid numeric uids from invalid
b1d4f8e1
LP
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
a1e92eee 90static bool is_nologin_shell(const char *shell) {
fafff8f1
LP
91
92 return PATH_IN_SET(shell,
93 /* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice
94 * message and exits. Different distributions place the binary at different places though,
95 * hence let's list them all. */
96 "/bin/nologin",
97 "/sbin/nologin",
98 "/usr/bin/nologin",
99 "/usr/sbin/nologin",
100 /* 'true' and 'false' work too for the same purpose, but are less friendly as they don't do
101 * any message printing. Different distributions place the binary at various places but at
102 * least not in the 'sbin' directory. */
103 "/bin/false",
104 "/usr/bin/false",
105 "/bin/true",
106 "/usr/bin/true");
107}
108
109static int synthesize_user_creds(
b1d4f8e1
LP
110 const char **username,
111 uid_t *uid, gid_t *gid,
112 const char **home,
fafff8f1
LP
113 const char **shell,
114 UserCredsFlags flags) {
b1d4f8e1 115
7e61fd02
LP
116 /* We enforce some special rules for uid=0 and uid=65534: in order to avoid NSS lookups for root we hardcode
117 * their user record data. */
b1d4f8e1 118
7e61fd02 119 if (STR_IN_SET(*username, "root", "0")) {
b1d4f8e1
LP
120 *username = "root";
121
122 if (uid)
123 *uid = 0;
b1d4f8e1
LP
124 if (gid)
125 *gid = 0;
126
127 if (home)
128 *home = "/root";
129
130 if (shell)
131 *shell = "/bin/sh";
132
133 return 0;
134 }
135
24eccc34
LP
136 if (synthesize_nobody() &&
137 STR_IN_SET(*username, NOBODY_USER_NAME, "65534")) {
7e61fd02
LP
138 *username = NOBODY_USER_NAME;
139
140 if (uid)
141 *uid = UID_NOBODY;
142 if (gid)
143 *gid = GID_NOBODY;
144
145 if (home)
fafff8f1 146 *home = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/";
7e61fd02
LP
147
148 if (shell)
fafff8f1 149 *shell = FLAGS_SET(flags, USER_CREDS_CLEAN) ? NULL : "/sbin/nologin";
7e61fd02
LP
150
151 return 0;
152 }
153
fafff8f1
LP
154 return -ENOMEDIUM;
155}
156
157int get_user_creds(
158 const char **username,
159 uid_t *uid, gid_t *gid,
160 const char **home,
161 const char **shell,
162 UserCredsFlags flags) {
163
164 uid_t u = UID_INVALID;
165 struct passwd *p;
166 int r;
167
168 assert(username);
169 assert(*username);
170
43ad3ad7 171 if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
fafff8f1
LP
172 (!home && !shell)) {
173
174 /* So here's the deal: normally, we'll try to synthesize all records we can synthesize, and override
43ad3ad7 175 * the user database with that. However, if the user specifies USER_CREDS_PREFER_NSS then the
fafff8f1
LP
176 * user database will override the synthetic records instead — except if the user is only interested in
177 * the UID and/or GID (but not the home directory, or the shell), in which case we'll always override
43ad3ad7 178 * the user database (i.e. the USER_CREDS_PREFER_NSS flag has no effect in this case). Why?
fafff8f1
LP
179 * Simply because there are valid usecase where the user might change the home directory or the shell
180 * of the relevant users, but changing the UID/GID mappings for them is something we explicitly don't
181 * support. */
182
183 r = synthesize_user_creds(username, uid, gid, home, shell, flags);
184 if (r >= 0)
185 return 0;
186 if (r != -ENOMEDIUM) /* not a username we can synthesize */
187 return r;
188 }
189
b1d4f8e1
LP
190 if (parse_uid(*username, &u) >= 0) {
191 errno = 0;
192 p = getpwuid(u);
193
fafff8f1
LP
194 /* If there are multiple users with the same id, make sure to leave $USER to the configured value
195 * instead of the first occurrence in the database. However if the uid was configured by a numeric uid,
196 * then let's pick the real username from /etc/passwd. */
b1d4f8e1
LP
197 if (p)
198 *username = p->pw_name;
fafff8f1
LP
199 else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING) && !gid && !home && !shell) {
200
201 /* If the specified user is a numeric UID and it isn't in the user database, and the caller
202 * passed USER_CREDS_ALLOW_MISSING and was only interested in the UID, then juts return that
203 * and don't complain. */
204
205 if (uid)
206 *uid = u;
207
208 return 0;
209 }
b1d4f8e1
LP
210 } else {
211 errno = 0;
212 p = getpwnam(*username);
213 }
fafff8f1
LP
214 if (!p) {
215 r = errno > 0 ? -errno : -ESRCH;
b1d4f8e1 216
fafff8f1 217 /* If the user requested that we only synthesize as fallback, do so now */
43ad3ad7 218 if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) {
fafff8f1
LP
219 if (synthesize_user_creds(username, uid, gid, home, shell, flags) >= 0)
220 return 0;
221 }
222
223 return r;
224 }
b1d4f8e1 225
67c7c892
LP
226 if (uid) {
227 if (!uid_is_valid(p->pw_uid))
228 return -EBADMSG;
229
b1d4f8e1 230 *uid = p->pw_uid;
67c7c892
LP
231 }
232
233 if (gid) {
234 if (!gid_is_valid(p->pw_gid))
235 return -EBADMSG;
b1d4f8e1 236
b1d4f8e1 237 *gid = p->pw_gid;
67c7c892 238 }
b1d4f8e1 239
fafff8f1
LP
240 if (home) {
241 if (FLAGS_SET(flags, USER_CREDS_CLEAN) && empty_or_root(p->pw_dir))
242 *home = NULL;
243 else
244 *home = p->pw_dir;
245 }
be39ccf3 246
fafff8f1
LP
247 if (shell) {
248 if (FLAGS_SET(flags, USER_CREDS_CLEAN) && (isempty(p->pw_shell) || is_nologin_shell(p->pw_shell)))
249 *shell = NULL;
250 else
251 *shell = p->pw_shell;
252 }
be39ccf3
LP
253
254 return 0;
255}
256
fafff8f1 257int get_group_creds(const char **groupname, gid_t *gid, UserCredsFlags flags) {
b1d4f8e1
LP
258 struct group *g;
259 gid_t id;
260
261 assert(groupname);
262
fafff8f1 263 /* We enforce some special rules for gid=0: in order to avoid NSS lookups for root we hardcode its data. */
b1d4f8e1 264
7e61fd02 265 if (STR_IN_SET(*groupname, "root", "0")) {
b1d4f8e1
LP
266 *groupname = "root";
267
268 if (gid)
269 *gid = 0;
270
271 return 0;
272 }
273
24eccc34
LP
274 if (synthesize_nobody() &&
275 STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534")) {
7e61fd02
LP
276 *groupname = NOBODY_GROUP_NAME;
277
278 if (gid)
279 *gid = GID_NOBODY;
280
281 return 0;
282 }
283
b1d4f8e1
LP
284 if (parse_gid(*groupname, &id) >= 0) {
285 errno = 0;
286 g = getgrgid(id);
287
288 if (g)
289 *groupname = g->gr_name;
fafff8f1
LP
290 else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING)) {
291 if (gid)
292 *gid = id;
293
294 return 0;
295 }
b1d4f8e1
LP
296 } else {
297 errno = 0;
298 g = getgrnam(*groupname);
299 }
300
301 if (!g)
302 return errno > 0 ? -errno : -ESRCH;
303
67c7c892
LP
304 if (gid) {
305 if (!gid_is_valid(g->gr_gid))
306 return -EBADMSG;
307
b1d4f8e1 308 *gid = g->gr_gid;
67c7c892 309 }
b1d4f8e1
LP
310
311 return 0;
312}
313
314char* uid_to_name(uid_t uid) {
d0260817
LP
315 char *ret;
316 int r;
b1d4f8e1 317
d0260817 318 /* Shortcut things to avoid NSS lookups */
b1d4f8e1
LP
319 if (uid == 0)
320 return strdup("root");
24eccc34
LP
321 if (synthesize_nobody() &&
322 uid == UID_NOBODY)
7e61fd02 323 return strdup(NOBODY_USER_NAME);
b1d4f8e1 324
d0260817
LP
325 if (uid_is_valid(uid)) {
326 long bufsize;
327
328 bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
329 if (bufsize <= 0)
330 bufsize = 4096;
331
332 for (;;) {
333 struct passwd pwbuf, *pw = NULL;
334 _cleanup_free_ char *buf = NULL;
335
336 buf = malloc(bufsize);
337 if (!buf)
338 return NULL;
339
340 r = getpwuid_r(uid, &pwbuf, buf, (size_t) bufsize, &pw);
341 if (r == 0 && pw)
342 return strdup(pw->pw_name);
343 if (r != ERANGE)
344 break;
345
346 bufsize *= 2;
347 }
348 }
b1d4f8e1 349
d0260817 350 if (asprintf(&ret, UID_FMT, uid) < 0)
b1d4f8e1
LP
351 return NULL;
352
d0260817 353 return ret;
b1d4f8e1
LP
354}
355
356char* gid_to_name(gid_t gid) {
d0260817
LP
357 char *ret;
358 int r;
b1d4f8e1
LP
359
360 if (gid == 0)
361 return strdup("root");
24eccc34
LP
362 if (synthesize_nobody() &&
363 gid == GID_NOBODY)
7e61fd02 364 return strdup(NOBODY_GROUP_NAME);
b1d4f8e1 365
d0260817
LP
366 if (gid_is_valid(gid)) {
367 long bufsize;
368
369 bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
370 if (bufsize <= 0)
371 bufsize = 4096;
372
373 for (;;) {
374 struct group grbuf, *gr = NULL;
375 _cleanup_free_ char *buf = NULL;
376
377 buf = malloc(bufsize);
378 if (!buf)
379 return NULL;
380
381 r = getgrgid_r(gid, &grbuf, buf, (size_t) bufsize, &gr);
382 if (r == 0 && gr)
383 return strdup(gr->gr_name);
384 if (r != ERANGE)
385 break;
386
387 bufsize *= 2;
388 }
389 }
b1d4f8e1 390
d0260817 391 if (asprintf(&ret, GID_FMT, gid) < 0)
b1d4f8e1
LP
392 return NULL;
393
d0260817 394 return ret;
b1d4f8e1
LP
395}
396
397int in_gid(gid_t gid) {
2dc89454 398 long ngroups_max;
b1d4f8e1 399 gid_t *gids;
2dc89454 400 int r, i;
b1d4f8e1
LP
401
402 if (getgid() == gid)
403 return 1;
404
405 if (getegid() == gid)
406 return 1;
407
67c7c892
LP
408 if (!gid_is_valid(gid))
409 return -EINVAL;
410
b1d4f8e1
LP
411 ngroups_max = sysconf(_SC_NGROUPS_MAX);
412 assert(ngroups_max > 0);
413
2dc89454 414 gids = newa(gid_t, ngroups_max);
b1d4f8e1
LP
415
416 r = getgroups(ngroups_max, gids);
417 if (r < 0)
418 return -errno;
419
420 for (i = 0; i < r; i++)
421 if (gids[i] == gid)
422 return 1;
423
424 return 0;
425}
426
427int in_group(const char *name) {
428 int r;
429 gid_t gid;
430
fafff8f1 431 r = get_group_creds(&name, &gid, 0);
b1d4f8e1
LP
432 if (r < 0)
433 return r;
434
435 return in_gid(gid);
436}
437
438int get_home_dir(char **_h) {
439 struct passwd *p;
440 const char *e;
441 char *h;
442 uid_t u;
443
444 assert(_h);
445
446 /* Take the user specified one */
447 e = secure_getenv("HOME");
448 if (e && path_is_absolute(e)) {
449 h = strdup(e);
450 if (!h)
451 return -ENOMEM;
452
453 *_h = h;
454 return 0;
455 }
456
7e61fd02 457 /* Hardcode home directory for root and nobody to avoid NSS */
b1d4f8e1
LP
458 u = getuid();
459 if (u == 0) {
460 h = strdup("/root");
461 if (!h)
462 return -ENOMEM;
463
464 *_h = h;
465 return 0;
466 }
24eccc34
LP
467 if (synthesize_nobody() &&
468 u == UID_NOBODY) {
7e61fd02
LP
469 h = strdup("/");
470 if (!h)
471 return -ENOMEM;
472
473 *_h = h;
474 return 0;
475 }
b1d4f8e1
LP
476
477 /* Check the database... */
478 errno = 0;
479 p = getpwuid(u);
480 if (!p)
481 return errno > 0 ? -errno : -ESRCH;
482
483 if (!path_is_absolute(p->pw_dir))
484 return -EINVAL;
485
486 h = strdup(p->pw_dir);
487 if (!h)
488 return -ENOMEM;
489
490 *_h = h;
491 return 0;
492}
493
494int get_shell(char **_s) {
495 struct passwd *p;
496 const char *e;
497 char *s;
498 uid_t u;
499
500 assert(_s);
501
502 /* Take the user specified one */
503 e = getenv("SHELL");
504 if (e) {
505 s = strdup(e);
506 if (!s)
507 return -ENOMEM;
508
509 *_s = s;
510 return 0;
511 }
512
7e61fd02 513 /* Hardcode shell for root and nobody to avoid NSS */
b1d4f8e1
LP
514 u = getuid();
515 if (u == 0) {
516 s = strdup("/bin/sh");
517 if (!s)
518 return -ENOMEM;
519
520 *_s = s;
521 return 0;
522 }
24eccc34
LP
523 if (synthesize_nobody() &&
524 u == UID_NOBODY) {
7e61fd02
LP
525 s = strdup("/sbin/nologin");
526 if (!s)
527 return -ENOMEM;
528
529 *_s = s;
530 return 0;
531 }
b1d4f8e1
LP
532
533 /* Check the database... */
534 errno = 0;
535 p = getpwuid(u);
536 if (!p)
537 return errno > 0 ? -errno : -ESRCH;
538
539 if (!path_is_absolute(p->pw_shell))
540 return -EINVAL;
541
542 s = strdup(p->pw_shell);
543 if (!s)
544 return -ENOMEM;
545
546 *_s = s;
547 return 0;
548}
549
550int reset_uid_gid(void) {
97f0e76f 551 int r;
b1d4f8e1 552
97f0e76f
LP
553 r = maybe_setgroups(0, NULL);
554 if (r < 0)
555 return r;
b1d4f8e1
LP
556
557 if (setresgid(0, 0, 0) < 0)
558 return -errno;
559
560 if (setresuid(0, 0, 0) < 0)
561 return -errno;
562
563 return 0;
564}
e929bee0
LP
565
566int take_etc_passwd_lock(const char *root) {
567
568 struct flock flock = {
569 .l_type = F_WRLCK,
570 .l_whence = SEEK_SET,
571 .l_start = 0,
572 .l_len = 0,
573 };
574
575 const char *path;
576 int fd, r;
577
578 /* This is roughly the same as lckpwdf(), but not as awful. We
579 * don't want to use alarm() and signals, hence we implement
580 * our own trivial version of this.
581 *
582 * Note that shadow-utils also takes per-database locks in
583 * addition to lckpwdf(). However, we don't given that they
61233823 584 * are redundant as they invoke lckpwdf() first and keep
e929bee0
LP
585 * it during everything they do. The per-database locks are
586 * awfully racy, and thus we just won't do them. */
587
588 if (root)
d1e4b8fd 589 path = prefix_roota(root, ETC_PASSWD_LOCK_PATH);
e929bee0 590 else
d1e4b8fd 591 path = ETC_PASSWD_LOCK_PATH;
e929bee0
LP
592
593 fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
594 if (fd < 0)
d1e4b8fd 595 return log_debug_errno(errno, "Cannot open %s: %m", path);
e929bee0
LP
596
597 r = fcntl(fd, F_SETLKW, &flock);
598 if (r < 0) {
599 safe_close(fd);
d1e4b8fd 600 return log_debug_errno(errno, "Locking %s failed: %m", path);
e929bee0
LP
601 }
602
603 return fd;
604}
e4631b48
LP
605
606bool valid_user_group_name(const char *u) {
607 const char *i;
608 long sz;
609
1429dfe5
LP
610 /* Checks if the specified name is a valid user/group name. Also see POSIX IEEE Std 1003.1-2008, 2016 Edition,
611 * 3.437. We are a bit stricter here however. Specifically we deviate from POSIX rules:
612 *
613 * - We don't allow any dots (this would break chown syntax which permits dots as user/group name separator)
614 * - We require that names fit into the appropriate utmp field
615 * - We don't allow empty user names
616 *
617 * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters.
618 */
e4631b48
LP
619
620 if (isempty(u))
621 return false;
622
623 if (!(u[0] >= 'a' && u[0] <= 'z') &&
624 !(u[0] >= 'A' && u[0] <= 'Z') &&
625 u[0] != '_')
626 return false;
627
628 for (i = u+1; *i; i++) {
629 if (!(*i >= 'a' && *i <= 'z') &&
630 !(*i >= 'A' && *i <= 'Z') &&
631 !(*i >= '0' && *i <= '9') &&
4c701096 632 !IN_SET(*i, '_', '-'))
e4631b48
LP
633 return false;
634 }
635
636 sz = sysconf(_SC_LOGIN_NAME_MAX);
637 assert_se(sz > 0);
638
639 if ((size_t) (i-u) > (size_t) sz)
640 return false;
641
642 if ((size_t) (i-u) > UT_NAMESIZE - 1)
643 return false;
644
645 return true;
646}
647
648bool valid_user_group_name_or_id(const char *u) {
649
650 /* Similar as above, but is also fine with numeric UID/GID specifications, as long as they are in the right
651 * range, and not the invalid user ids. */
652
653 if (isempty(u))
654 return false;
655
656 if (valid_user_group_name(u))
657 return true;
658
659 return parse_uid(u, NULL) >= 0;
660}
661
662bool valid_gecos(const char *d) {
663
664 if (!d)
665 return false;
666
667 if (!utf8_is_valid(d))
668 return false;
669
670 if (string_has_cc(d, NULL))
671 return false;
672
673 /* Colons are used as field separators, and hence not OK */
674 if (strchr(d, ':'))
675 return false;
676
677 return true;
678}
679
680bool valid_home(const char *p) {
7b1aaf66
ZJS
681 /* Note that this function is also called by valid_shell(), any
682 * changes must account for that. */
e4631b48
LP
683
684 if (isempty(p))
685 return false;
686
687 if (!utf8_is_valid(p))
688 return false;
689
690 if (string_has_cc(p, NULL))
691 return false;
692
693 if (!path_is_absolute(p))
694 return false;
695
99be45a4 696 if (!path_is_normalized(p))
e4631b48
LP
697 return false;
698
699 /* Colons are used as field separators, and hence not OK */
700 if (strchr(p, ':'))
701 return false;
702
703 return true;
704}
36d85478
GS
705
706int maybe_setgroups(size_t size, const gid_t *list) {
97f0e76f
LP
707 int r;
708
709 /* Check if setgroups is allowed before we try to drop all the auxiliary groups */
710 if (size == 0) { /* Dropping all aux groups? */
711 _cleanup_free_ char *setgroups_content = NULL;
712 bool can_setgroups;
713
714 r = read_one_line_file("/proc/self/setgroups", &setgroups_content);
715 if (r == -ENOENT)
716 /* Old kernels don't have /proc/self/setgroups, so assume we can use setgroups */
717 can_setgroups = true;
718 else if (r < 0)
719 return r;
720 else
721 can_setgroups = streq(setgroups_content, "allow");
722
723 if (!can_setgroups) {
724 log_debug("Skipping setgroups(), /proc/self/setgroups is set to 'deny'");
36d85478 725 return 0;
97f0e76f 726 }
36d85478 727 }
97f0e76f
LP
728
729 if (setgroups(size, list) < 0)
730 return -errno;
731
732 return 0;
36d85478 733}
24eccc34
LP
734
735bool synthesize_nobody(void) {
736
737#ifdef NOLEGACY
738 return true;
739#else
740 /* Returns true when we shall synthesize the "nobody" user (which we do by default). This can be turned off by
741 * touching /etc/systemd/dont-synthesize-nobody in order to provide upgrade compatibility with legacy systems
742 * that used the "nobody" user name and group name for other UIDs/GIDs than 65534.
743 *
744 * Note that we do not employ any kind of synchronization on the following caching variable. If the variable is
745 * accessed in multi-threaded programs in the worst case it might happen that we initialize twice, but that
746 * shouldn't matter as each initialization should come to the same result. */
747 static int cache = -1;
748
749 if (cache < 0)
750 cache = access("/etc/systemd/dont-synthesize-nobody", F_OK) < 0;
751
752 return cache;
753#endif
754}
100d5f6e
FB
755
756int putpwent_sane(const struct passwd *pw, FILE *stream) {
757 assert(pw);
758 assert(stream);
759
760 errno = 0;
761 if (putpwent(pw, stream) != 0)
762 return errno > 0 ? -errno : -EIO;
763
764 return 0;
765}
766
767int putspent_sane(const struct spwd *sp, FILE *stream) {
768 assert(sp);
769 assert(stream);
770
771 errno = 0;
772 if (putspent(sp, stream) != 0)
773 return errno > 0 ? -errno : -EIO;
774
775 return 0;
776}
777
778int putgrent_sane(const struct group *gr, FILE *stream) {
779 assert(gr);
780 assert(stream);
781
782 errno = 0;
783 if (putgrent(gr, stream) != 0)
784 return errno > 0 ? -errno : -EIO;
785
786 return 0;
787}
788
789#if ENABLE_GSHADOW
790int putsgent_sane(const struct sgrp *sg, FILE *stream) {
791 assert(sg);
792 assert(stream);
793
794 errno = 0;
795 if (putsgent(sg, stream) != 0)
796 return errno > 0 ? -errno : -EIO;
797
798 return 0;
799}
800#endif
801
802int fgetpwent_sane(FILE *stream, struct passwd **pw) {
803 struct passwd *p;
804
805 assert(pw);
806 assert(stream);
807
808 errno = 0;
809 p = fgetpwent(stream);
ad80c6a6 810 if (!p && errno != ENOENT)
100d5f6e 811 return errno > 0 ? -errno : -EIO;
100d5f6e
FB
812
813 *pw = p;
ad80c6a6 814 return !!p;
100d5f6e
FB
815}
816
817int fgetspent_sane(FILE *stream, struct spwd **sp) {
818 struct spwd *s;
819
820 assert(sp);
821 assert(stream);
822
823 errno = 0;
824 s = fgetspent(stream);
ad80c6a6 825 if (!s && errno != ENOENT)
100d5f6e 826 return errno > 0 ? -errno : -EIO;
100d5f6e
FB
827
828 *sp = s;
ad80c6a6 829 return !!s;
100d5f6e
FB
830}
831
832int fgetgrent_sane(FILE *stream, struct group **gr) {
833 struct group *g;
834
835 assert(gr);
836 assert(stream);
837
838 errno = 0;
839 g = fgetgrent(stream);
ad80c6a6 840 if (!g && errno != ENOENT)
100d5f6e 841 return errno > 0 ? -errno : -EIO;
100d5f6e
FB
842
843 *gr = g;
ad80c6a6 844 return !!g;
100d5f6e
FB
845}
846
847#if ENABLE_GSHADOW
848int fgetsgent_sane(FILE *stream, struct sgrp **sg) {
849 struct sgrp *s;
850
851 assert(sg);
852 assert(stream);
853
854 errno = 0;
855 s = fgetsgent(stream);
ad80c6a6 856 if (!s && errno != ENOENT)
100d5f6e 857 return errno > 0 ? -errno : -EIO;
100d5f6e
FB
858
859 *sg = s;
ad80c6a6 860 return !!s;
100d5f6e
FB
861}
862#endif