• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

systemd / systemd / 19555332179

20 Nov 2025 07:10PM UTC coverage: 72.661% (+0.1%) from 72.548%
19555332179

push

github

web-flow
apparmor: move dlopen() into mac_apparmor_use() check (#39826)

This mirrors what we do for mac_selinux_use(), which also loads
libselinux.

309032 of 425304 relevant lines covered (72.66%)

1136602.94 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

86.14
/src/basic/user-util.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fcntl.h>
4
#include <stdio.h>
5
#include <stdlib.h>
6
#include <sys/file.h>
7
#include <sys/stat.h>
8
#include <unistd.h>
9
#include <utmpx.h>
10

11
#include "sd-messages.h"
12

13
#include "alloc-util.h"
14
#include "chase.h"
15
#include "errno-util.h"
16
#include "extract-word.h"
17
#include "fd-util.h"
18
#include "fileio.h"
19
#include "format-util.h"
20
#include "lock-util.h"
21
#include "log.h"
22
#include "mkdir.h"
23
#include "parse-util.h"
24
#include "path-util.h"
25
#include "string-util.h"
26
#include "strv.h"
27
#include "terminal-util.h"
28
#include "user-util.h"
29
#include "utf8.h"
30

31
#define DEFINE_STRERROR_ACCOUNT(type)                                   \
32
        const char* strerror_##type(                                    \
33
                        int errnum,                                     \
34
                        char *buf,                                      \
35
                        size_t buflen) {                                \
36
                                                                        \
37
                errnum = ABS(errnum);                                   \
38
                switch (errnum) {                                       \
39
                case ESRCH:                                             \
40
                        return "Unknown " STRINGIFY(type);              \
41
                case ENOEXEC:                                           \
42
                        return "Not a system " STRINGIFY(type);         \
43
                default:                                                \
44
                        return strerror_r(errnum, buf, buflen);         \
45
                }                                                       \
46
        }
47

48
DEFINE_STRERROR_ACCOUNT(user);
17✔
49
DEFINE_STRERROR_ACCOUNT(group);
15✔
50

51
bool uid_is_valid(uid_t uid) {
11,210,881✔
52

53
        /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.436. */
54

55
        /* Some libc APIs use UID_INVALID as special placeholder */
56
        if (uid == (uid_t) UINT32_C(0xFFFFFFFF))
11,210,881✔
57
                return false;
58

59
        /* A long time ago UIDs where 16 bit, hence explicitly avoid the 16-bit -1 too */
60
        if (uid == (uid_t) UINT32_C(0xFFFF))
5,711,152✔
61
                return false;
18✔
62

63
        return true;
64
}
65

66
int parse_uid(const char *s, uid_t *ret) {
214,452✔
67
        uint32_t uid = 0;
214,452✔
68
        int r;
214,452✔
69

70
        assert(s);
214,452✔
71

72
        assert_cc(sizeof(uid_t) == sizeof(uint32_t));
214,452✔
73

74
        /* We are very strict when parsing UIDs, and prohibit +/- as prefix, leading zero as prefix, and
75
         * whitespace. We do this, since this call is often used in a context where we parse things as UID
76
         * first, and if that doesn't work we fall back to NSS. Thus we really want to make sure that UIDs
77
         * are parsed as UIDs only if they really really look like UIDs. */
78
        r = safe_atou32_full(s, 10
214,452✔
79
                             | SAFE_ATO_REFUSE_PLUS_MINUS
80
                             | SAFE_ATO_REFUSE_LEADING_ZERO
81
                             | SAFE_ATO_REFUSE_LEADING_WHITESPACE, &uid);
82
        if (r < 0)
214,452✔
83
                return r;
214,452✔
84

85
        if (!uid_is_valid(uid))
11,049✔
86
                return -ENXIO; /* we return ENXIO instead of EINVAL
87
                                * here, to make it easy to distinguish
88
                                * invalid numeric uids from invalid
89
                                * strings. */
90

91
        if (ret)
11,032✔
92
                *ret = uid;
10,504✔
93

94
        return 0;
95
}
96

97
int parse_uid_range(const char *s, uid_t *ret_lower, uid_t *ret_upper) {
35✔
98
        _cleanup_free_ char *word = NULL;
35✔
99
        uid_t l, u;
35✔
100
        int r;
35✔
101

102
        assert(s);
35✔
103
        assert(ret_lower);
35✔
104
        assert(ret_upper);
35✔
105

106
        r = extract_first_word(&s, &word, "-", EXTRACT_DONT_COALESCE_SEPARATORS);
35✔
107
        if (r < 0)
35✔
108
                return r;
109
        if (r == 0)
35✔
110
                return -EINVAL;
111

112
        r = parse_uid(word, &l);
35✔
113
        if (r < 0)
35✔
114
                return r;
115

116
        /* Check for the upper bound and extract it if needed */
117
        if (!s)
22✔
118
                /* Single number with no dash. */
119
                u = l;
7✔
120
        else if (!*s)
15✔
121
                /* Trailing dash is an error. */
122
                return -EINVAL;
123
        else {
124
                r = parse_uid(s, &u);
15✔
125
                if (r < 0)
15✔
126
                        return r;
127

128
                if (l > u)
10✔
129
                        return -EINVAL;
130
        }
131

132
        *ret_lower = l;
16✔
133
        *ret_upper = u;
16✔
134
        return 0;
16✔
135
}
136

137
char* getlogname_malloc(void) {
×
138
        uid_t uid;
×
139
        struct stat st;
×
140

141
        if (isatty_safe(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
×
142
                uid = st.st_uid;
×
143
        else
144
                uid = getuid();
×
145

146
        return uid_to_name(uid);
×
147
}
148

149
char* getusername_malloc(void) {
677✔
150
        const char *e;
677✔
151

152
        e = secure_getenv("USER");
677✔
153
        if (e)
677✔
154
                return strdup(e);
560✔
155

156
        return uid_to_name(getuid());
117✔
157
}
158

159
bool is_nologin_shell(const char *shell) {
2,637✔
160
        return PATH_IN_SET(shell,
2,637✔
161
                           /* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice
162
                            * message and exits. Different distributions place the binary at different places though,
163
                            * hence let's list them all. */
164
                           "/bin/nologin",
165
                           "/sbin/nologin",
166
                           "/usr/bin/nologin",
167
                           "/usr/sbin/nologin",
168
                           /* 'true' and 'false' work too for the same purpose, but are less friendly as they don't do
169
                            * any message printing. Different distributions place the binary at various places but at
170
                            * least not in the 'sbin' directory. */
171
                           "/bin/false",
172
                           "/usr/bin/false",
173
                           "/bin/true",
174
                           "/usr/bin/true");
175
}
176

177
bool shell_is_placeholder(const char *shell) {
2,085✔
178
        return isempty(shell) || is_nologin_shell(shell);
4,170✔
179
}
180

181
const char* default_root_shell_at(int rfd) {
7,208✔
182
        /* We want to use the preferred shell, i.e. DEFAULT_USER_SHELL, which usually
183
         * will be /bin/bash. Fall back to /bin/sh if DEFAULT_USER_SHELL is not found,
184
         * or any access errors. */
185

186
        assert(rfd >= 0 || rfd == AT_FDCWD);
7,208✔
187

188
        int r = chaseat(rfd, DEFAULT_USER_SHELL, CHASE_AT_RESOLVE_IN_ROOT, NULL, NULL);
7,208✔
189
        if (r < 0 && r != -ENOENT)
7,208✔
190
                log_debug_errno(r, "Failed to look up shell '%s': %m", DEFAULT_USER_SHELL);
×
191
        if (r > 0)
7,208✔
192
                return DEFAULT_USER_SHELL;
7,192✔
193

194
        return "/bin/sh";
195
}
196

197
const char* default_root_shell(const char *root) {
7,202✔
198
        _cleanup_close_ int rfd = -EBADF;
7,202✔
199

200
        rfd = open(empty_to_root(root), O_CLOEXEC | O_DIRECTORY | O_PATH);
7,202✔
201
        if (rfd < 0)
7,202✔
202
                return "/bin/sh";
203

204
        return default_root_shell_at(rfd);
7,202✔
205
}
206

207
static int synthesize_user_creds(
17,752✔
208
                const char **username,
209
                uid_t *ret_uid, gid_t *ret_gid,
210
                const char **ret_home,
211
                const char **ret_shell,
212
                UserCredsFlags flags) {
213

214
        assert(username);
17,752✔
215
        assert(*username);
17,752✔
216

217
        /* We enforce some special rules for uid=0 and uid=65534: in order to avoid NSS lookups for root we hardcode
218
         * their user record data. */
219

220
        if (STR_IN_SET(*username, "root", "0")) {
17,752✔
221
                *username = "root";
14,114✔
222

223
                if (ret_uid)
14,114✔
224
                        *ret_uid = 0;
6,937✔
225
                if (ret_gid)
14,114✔
226
                        *ret_gid = 0;
10✔
227
                if (ret_home)
14,114✔
228
                        *ret_home = "/root";
7,187✔
229
                if (ret_shell)
14,114✔
230
                        *ret_shell = default_root_shell(NULL);
7,187✔
231

232
                return 0;
14,114✔
233
        }
234

235
        if (STR_IN_SET(*username, NOBODY_USER_NAME, "65534") &&
3,649✔
236
            synthesize_nobody()) {
11✔
237
                *username = NOBODY_USER_NAME;
11✔
238

239
                if (ret_uid)
11✔
240
                        *ret_uid = UID_NOBODY;
11✔
241
                if (ret_gid)
11✔
242
                        *ret_gid = GID_NOBODY;
7✔
243
                if (ret_home)
11✔
244
                        *ret_home = FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : "/";
6✔
245
                if (ret_shell)
11✔
246
                        *ret_shell = FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : NOLOGIN;
6✔
247

248
                return 0;
11✔
249
        }
250

251
        return -ENOMEDIUM;
3,627✔
252
}
253

254
int get_user_creds(
18,254✔
255
                const char **username,
256
                uid_t *ret_uid, gid_t *ret_gid,
257
                const char **ret_home,
258
                const char **ret_shell,
259
                UserCredsFlags flags) {
260

261
        bool patch_username = false;
18,254✔
262
        uid_t u = UID_INVALID;
18,254✔
263
        struct passwd *p;
18,254✔
264
        int r;
18,254✔
265

266
        assert(username);
18,254✔
267
        assert(*username);
18,254✔
268
        assert((ret_home || ret_shell) || !(flags & (USER_CREDS_SUPPRESS_PLACEHOLDER|USER_CREDS_CLEAN)));
18,254✔
269

270
        if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
18,254✔
271
            (!ret_home && !ret_shell)) {
272

273
                /* So here's the deal: normally, we'll try to synthesize all records we can synthesize, and override
274
                 * the user database with that. However, if the user specifies USER_CREDS_PREFER_NSS then the
275
                 * user database will override the synthetic records instead — except if the user is only interested in
276
                 * the UID and/or GID (but not the home directory, or the shell), in which case we'll always override
277
                 * the user database (i.e. the USER_CREDS_PREFER_NSS flag has no effect in this case). Why?
278
                 * Simply because there are valid usecase where the user might change the home directory or the shell
279
                 * of the relevant users, but changing the UID/GID mappings for them is something we explicitly don't
280
                 * support. */
281

282
                r = synthesize_user_creds(username, ret_uid, ret_gid, ret_home, ret_shell, flags);
17,752✔
283
                if (r >= 0)
17,752✔
284
                        return 0;
18,254✔
285
                if (r != -ENOMEDIUM) /* not a username we can synthesize */
3,627✔
286
                        return r;
287
        }
288

289
        if (parse_uid(*username, &u) >= 0) {
4,129✔
290
                errno = 0;
195✔
291
                p = getpwuid(u);
195✔
292

293
                /* If there are multiple users with the same id, make sure to leave $USER to the configured value
294
                 * instead of the first occurrence in the database. However if the uid was configured by a numeric uid,
295
                 * then let's pick the real username from /etc/passwd. */
296
                if (p)
195✔
297
                        patch_username = true;
298
                else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING) && !ret_gid && !ret_home && !ret_shell) {
3✔
299

300
                        /* If the specified user is a numeric UID and it isn't in the user database, and the caller
301
                         * passed USER_CREDS_ALLOW_MISSING and was only interested in the UID, then just return that
302
                         * and don't complain. */
303

304
                        if (ret_uid)
×
305
                                *ret_uid = u;
×
306

307
                        return 0;
×
308
                }
309
        } else {
310
                errno = 0;
3,934✔
311
                p = getpwnam(*username);
3,934✔
312
        }
313
        if (!p) {
3,937✔
314
                /* getpwnam() may fail with ENOENT if /etc/passwd is missing.
315
                 * For us that is equivalent to the name not being defined. */
316
                r = IN_SET(errno, 0, ENOENT) ? -ESRCH : -errno;
9✔
317

318
                /* If the user requested that we only synthesize as fallback, do so now */
319
                if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS))
9✔
320
                        if (synthesize_user_creds(username, ret_uid, ret_gid, ret_home, ret_shell, flags) >= 0)
×
321
                                return 0;
322

323
                return r;
9✔
324
        }
325

326
        if (ret_uid && !uid_is_valid(p->pw_uid))
4,120✔
327
                return -EBADMSG;
328

329
        if (ret_gid && !gid_is_valid(p->pw_gid))
4,120✔
330
                return -EBADMSG;
331

332
        if (ret_uid)
4,120✔
333
                *ret_uid = p->pw_uid;
4,120✔
334

335
        if (ret_gid)
4,120✔
336
                *ret_gid = p->pw_gid;
2,079✔
337

338
        if (ret_home)
4,120✔
339
                /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
340
                *ret_home = (FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) && empty_or_root(p->pw_dir)) ||
1✔
341
                            (FLAGS_SET(flags, USER_CREDS_CLEAN) && (!path_is_valid(p->pw_dir) || !path_is_absolute(p->pw_dir)))
4,120✔
342
                            ? NULL : p->pw_dir;
4,120✔
343

344
        if (ret_shell)
4,120✔
345
                *ret_shell = (FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) && shell_is_placeholder(p->pw_shell)) ||
1✔
346
                             (FLAGS_SET(flags, USER_CREDS_CLEAN) && (!path_is_valid(p->pw_shell) || !path_is_absolute(p->pw_shell)))
4,120✔
347
                             ? NULL : p->pw_shell;
4,120✔
348

349
        if (patch_username)
4,120✔
350
                *username = p->pw_name;
192✔
351

352
        return 0;
353
}
354

355
static int synthesize_group_creds(
10,147✔
356
                const char **groupname,
357
                gid_t *ret_gid) {
358

359
        assert(groupname);
10,147✔
360
        assert(*groupname);
10,147✔
361

362
        if (STR_IN_SET(*groupname, "root", "0")) {
10,147✔
363
                *groupname = "root";
5,226✔
364

365
                if (ret_gid)
5,226✔
366
                        *ret_gid = 0;
5,226✔
367

368
                return 0;
5,226✔
369
        }
370

371
        if (STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534") &&
4,925✔
372
            synthesize_nobody()) {
4✔
373
                *groupname = NOBODY_GROUP_NAME;
4✔
374

375
                if (ret_gid)
4✔
376
                        *ret_gid = GID_NOBODY;
4✔
377

378
                return 0;
4✔
379
        }
380

381
        return -ENOMEDIUM;
4,917✔
382
}
383

384
int get_group_creds(const char **groupname, gid_t *ret_gid, UserCredsFlags flags) {
10,147✔
385
        bool patch_groupname = false;
10,147✔
386
        struct group *g;
10,147✔
387
        gid_t id;
10,147✔
388
        int r;
10,147✔
389

390
        assert(groupname);
10,147✔
391
        assert(*groupname);
10,147✔
392

393
        if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) {
10,147✔
394
                r = synthesize_group_creds(groupname, ret_gid);
10,147✔
395
                if (r >= 0)
10,147✔
396
                        return 0;
10,147✔
397
                if (r != -ENOMEDIUM) /* not a groupname we can synthesize */
4,917✔
398
                        return r;
399
        }
400

401
        if (parse_gid(*groupname, &id) >= 0) {
4,917✔
402
                errno = 0;
×
403
                g = getgrgid(id);
×
404

405
                if (g)
×
406
                        patch_groupname = true;
407
                else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING)) {
×
408
                        if (ret_gid)
×
409
                                *ret_gid = id;
×
410

411
                        return 0;
×
412
                }
413
        } else {
414
                errno = 0;
4,917✔
415
                g = getgrnam(*groupname);
4,917✔
416
        }
417

418
        if (!g) {
4,917✔
419
                /* getgrnam() may fail with ENOENT if /etc/group is missing.
420
                 * For us that is equivalent to the name not being defined. */
421
                r = IN_SET(errno, 0, ENOENT) ? -ESRCH : -errno;
1✔
422

423
                if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS))
1✔
424
                        if (synthesize_group_creds(groupname, ret_gid) >= 0)
×
425
                                return 0;
426

427
                return r;
1✔
428
        }
429

430
        if (ret_gid) {
4,916✔
431
                if (!gid_is_valid(g->gr_gid))
4,916✔
432
                        return -EBADMSG;
433

434
                *ret_gid = g->gr_gid;
4,916✔
435
        }
436

437
        if (patch_groupname)
4,916✔
438
                *groupname = g->gr_name;
×
439

440
        return 0;
441
}
442

443
char* uid_to_name(uid_t uid) {
504✔
444
        char *ret;
504✔
445
        int r;
504✔
446

447
        /* Shortcut things to avoid NSS lookups */
448
        if (uid == 0)
504✔
449
                return strdup("root");
504✔
450
        if (uid == UID_NOBODY && synthesize_nobody())
199✔
451
                return strdup(NOBODY_USER_NAME);
1✔
452

453
        if (uid_is_valid(uid)) {
198✔
454
                _cleanup_free_ struct passwd *pw = NULL;
196✔
455

456
                r = getpwuid_malloc(uid, &pw);
196✔
457
                if (r >= 0)
196✔
458
                        return strdup(pw->pw_name);
196✔
459
        }
460

461
        if (asprintf(&ret, UID_FMT, uid) < 0)
2✔
462
                return NULL;
463

464
        return ret;
2✔
465
}
466

467
char* gid_to_name(gid_t gid) {
104✔
468
        char *ret;
104✔
469
        int r;
104✔
470

471
        if (gid == 0)
104✔
472
                return strdup("root");
104✔
473
        if (gid == GID_NOBODY && synthesize_nobody())
49✔
474
                return strdup(NOBODY_GROUP_NAME);
1✔
475

476
        if (gid_is_valid(gid)) {
48✔
477
                _cleanup_free_ struct group *gr = NULL;
46✔
478

479
                r = getgrgid_malloc(gid, &gr);
46✔
480
                if (r >= 0)
46✔
481
                        return strdup(gr->gr_name);
46✔
482
        }
483

484
        if (asprintf(&ret, GID_FMT, gid) < 0)
2✔
485
                return NULL;
486

487
        return ret;
2✔
488
}
489

490
static bool gid_list_has(const gid_t *list, size_t size, gid_t val) {
454✔
491
        assert(list || size == 0);
454✔
492

493
        FOREACH_ARRAY(i, list, size)
621✔
494
                if (*i == val)
182✔
495
                        return true;
496

497
        return false;
498
}
499

500
int in_gid(gid_t gid) {
41✔
501
        _cleanup_free_ gid_t *gids = NULL;
41✔
502
        int ngroups;
41✔
503

504
        if (getgid() == gid)
41✔
505
                return 1;
506

507
        if (getegid() == gid)
38✔
508
                return 1;
509

510
        if (!gid_is_valid(gid))
38✔
511
                return -EINVAL;
512

513
        ngroups = getgroups_alloc(&gids);
37✔
514
        if (ngroups < 0)
37✔
515
                return ngroups;
516

517
        return gid_list_has(gids, ngroups, gid);
37✔
518
}
519

520
int in_group(const char *name) {
14✔
521
        int r;
14✔
522
        gid_t gid;
14✔
523

524
        r = get_group_creds(&name, &gid, 0);
14✔
525
        if (r < 0)
14✔
526
                return r;
14✔
527

528
        return in_gid(gid);
13✔
529
}
530

531
int merge_gid_lists(const gid_t *list1, size_t size1, const gid_t *list2, size_t size2, gid_t **ret) {
9,957✔
532
        size_t nresult = 0;
9,957✔
533
        assert(ret);
9,957✔
534

535
        if (size2 > INT_MAX - size1)
9,957✔
536
                return -ENOBUFS;
537

538
        gid_t *buf = new(gid_t, size1 + size2);
9,957✔
539
        if (!buf)
9,957✔
540
                return -ENOMEM;
541

542
        /* Duplicates need to be skipped on merging, otherwise they'll be passed on and stored in the kernel. */
543
        for (size_t i = 0; i < size1; i++)
9,996✔
544
                if (!gid_list_has(buf, nresult, list1[i]))
39✔
545
                        buf[nresult++] = list1[i];
39✔
546
        for (size_t i = 0; i < size2; i++)
10,335✔
547
                if (!gid_list_has(buf, nresult, list2[i]))
378✔
548
                        buf[nresult++] = list2[i];
363✔
549
        *ret = buf;
9,957✔
550
        return (int)nresult;
9,957✔
551
}
552

553
int getgroups_alloc(gid_t **ret) {
540✔
554
        int ngroups = 8;
540✔
555

556
        assert(ret);
540✔
557

558
        for (unsigned attempt = 0;;) {
×
559
                _cleanup_free_ gid_t *p = NULL;
386✔
560

561
                p = new(gid_t, ngroups);
540✔
562
                if (!p)
540✔
563
                        return -ENOMEM;
564

565
                ngroups = getgroups(ngroups, p);
540✔
566
                if (ngroups > 0) {
540✔
567
                        *ret = TAKE_PTR(p);
386✔
568
                        return ngroups;
386✔
569
                }
570
                if (ngroups == 0)
154✔
571
                        break;
572
                if (errno != EINVAL)
×
573
                        return -errno;
×
574

575
                /* Give up eventually */
576
                if (attempt++ > 10)
×
577
                        return -EINVAL;
578

579
                /* Get actual size needed, and size the array explicitly. Note that this is potentially racy
580
                 * to use (in multi-threaded programs), hence let's call this in a loop. */
581
                ngroups = getgroups(0, NULL);
×
582
                if (ngroups < 0)
×
583
                        return -errno;
×
584
                if (ngroups == 0)
×
585
                        break;
586
        }
587

588
        *ret = NULL;
154✔
589
        return 0;
154✔
590
}
591

592
int get_home_dir(char **ret) {
12,422✔
593
        _cleanup_free_ struct passwd *p = NULL;
12,422✔
594
        const char *e;
12,422✔
595
        uid_t u;
12,422✔
596
        int r;
12,422✔
597

598
        assert(ret);
12,422✔
599

600
        /* Take the user specified one */
601
        e = secure_getenv("HOME");
12,422✔
602
        if (e && path_is_valid(e) && path_is_absolute(e))
24,229✔
603
                goto found;
11,805✔
604

605
        /* Hardcode home directory for root and nobody to avoid NSS */
606
        u = getuid();
617✔
607
        if (u == 0) {
617✔
608
                e = "/root";
617✔
609
                goto found;
617✔
610
        }
611
        if (u == UID_NOBODY && synthesize_nobody()) {
×
612
                e = "/";
×
613
                goto found;
×
614
        }
615

616
        /* Check the database... */
617
        r = getpwuid_malloc(u, &p);
×
618
        if (r < 0)
×
619
                return r;
620

621
        e = p->pw_dir;
×
622
        if (!path_is_valid(e) || !path_is_absolute(e))
12,422✔
623
                return -EINVAL;
624

625
 found:
×
626
        return path_simplify_alloc(e, ret);
12,422✔
627
}
628

629
int get_shell(char **ret) {
6✔
630
        _cleanup_free_ struct passwd *p = NULL;
6✔
631
        const char *e;
6✔
632
        uid_t u;
6✔
633
        int r;
6✔
634

635
        assert(ret);
6✔
636

637
        /* Take the user specified one */
638
        e = secure_getenv("SHELL");
6✔
639
        if (e && path_is_valid(e) && path_is_absolute(e))
9✔
640
                goto found;
3✔
641

642
        /* Hardcode shell for root and nobody to avoid NSS */
643
        u = getuid();
3✔
644
        if (u == 0) {
3✔
645
                e = default_root_shell(NULL);
3✔
646
                goto found;
3✔
647
        }
648
        if (u == UID_NOBODY && synthesize_nobody()) {
×
649
                e = NOLOGIN;
×
650
                goto found;
×
651
        }
652

653
        /* Check the database... */
654
        r = getpwuid_malloc(u, &p);
×
655
        if (r < 0)
×
656
                return r;
657

658
        e = p->pw_shell;
×
659
        if (!path_is_valid(e) || !path_is_absolute(e))
6✔
660
                return -EINVAL;
661

662
 found:
×
663
        return path_simplify_alloc(e, ret);
6✔
664
}
665

666
int fully_set_uid_gid(uid_t uid, gid_t gid, const gid_t supplementary_gids[], size_t n_supplementary_gids) {
522✔
667
        int r;
522✔
668

669
        assert(supplementary_gids || n_supplementary_gids == 0);
522✔
670

671
        /* Sets all UIDs and all GIDs to the specified ones. Drops all auxiliary GIDs */
672

673
        r = maybe_setgroups(n_supplementary_gids, supplementary_gids);
522✔
674
        if (r < 0)
522✔
675
                return r;
676

677
        if (gid_is_valid(gid))
522✔
678
                if (setresgid(gid, gid, gid) < 0)
522✔
679
                        return -errno;
×
680

681
        if (uid_is_valid(uid))
522✔
682
                if (setresuid(uid, uid, uid) < 0)
522✔
683
                        return -errno;
×
684

685
        return 0;
686
}
687

688
int take_etc_passwd_lock(const char *root) {
307✔
689
        int r;
307✔
690

691
        /* This is roughly the same as lckpwdf(), but not as awful. We don't want to use alarm() and signals,
692
         * hence we implement our own trivial version of this.
693
         *
694
         * Note that shadow-utils also takes per-database locks in addition to lckpwdf(). However, we don't,
695
         * given that they are redundant: they invoke lckpwdf() first and keep it during everything they do.
696
         * The per-database locks are awfully racy, and thus we just won't do them. */
697

698
        _cleanup_free_ char *path = path_join(root, ETC_PASSWD_LOCK_PATH);
614✔
699
        if (!path)
307✔
700
                return log_oom_debug();
×
701

702
        (void) mkdir_parents(path, 0755);
307✔
703

704
        _cleanup_close_ int fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
614✔
705
        if (fd < 0)
307✔
706
                return log_debug_errno(errno, "Cannot open %s: %m", path);
×
707

708
        r = unposix_lock(fd, LOCK_EX);
307✔
709
        if (r < 0)
307✔
710
                return log_debug_errno(r, "Locking %s failed: %m", path);
×
711

712
        return TAKE_FD(fd);
713
}
714

715
bool valid_user_group_name(const char *u, ValidUserFlags flags) {
173,602✔
716
        const char *i;
173,602✔
717

718
        /* Checks if the specified name is a valid user/group name. There are two flavours of this call:
719
         * strict mode is the default which is POSIX plus some extra rules; and relaxed mode where we accept
720
         * pretty much everything except the really worst offending names.
721
         *
722
         * Whenever we synthesize users ourselves we should use the strict mode. But when we process users
723
         * created by other stuff, let's be more liberal. */
724

725
        if (isempty(u)) /* An empty user name is never valid */
173,602✔
726
                return false;
727

728
        if (parse_uid(u, NULL) >= 0) /* Something that parses as numeric UID string is valid exactly when the
173,585✔
729
                                      * flag for it is set */
730
                return FLAGS_SET(flags, VALID_USER_ALLOW_NUMERIC);
156✔
731

732
        if (FLAGS_SET(flags, VALID_USER_RELAX)) {
173,429✔
733

734
                /* In relaxed mode we just check very superficially. Apparently SSSD and other stuff is
735
                 * extremely liberal (way too liberal if you ask me, even inserting "@" in user names, which
736
                 * is bound to cause problems for example when used with an MTA), hence only filter the most
737
                 * obvious cases, or where things would result in an invalid entry if such a user name would
738
                 * show up in /etc/passwd (or equivalent getent output).
739
                 *
740
                 * Note that we stepped far out of POSIX territory here. It's not our fault though, but
741
                 * SSSD's, Samba's and everybody else who ignored POSIX on this. (I mean, I am happy to step
742
                 * outside of POSIX' bounds any day, but I must say in this case I probably wouldn't
743
                 * have...) */
744

745
                if (startswith(u, " ") || endswith(u, " ")) /* At least expect whitespace padding is removed
169,852✔
746
                                                             * at front and back (accept in the middle, since
747
                                                             * that's apparently a thing on Windows). Note
748
                                                             * that this also blocks usernames consisting of
749
                                                             * whitespace only. */
750
                        return false;
751

752
                if (!utf8_is_valid(u)) /* We want to synthesize JSON from this, hence insist on UTF-8 */
169,852✔
753
                        return false;
754

755
                if (string_has_cc(u, NULL)) /* CC characters are just dangerous (and \n in particular is the
169,852✔
756
                                             * record separator in /etc/passwd), so we can't allow that. */
757
                        return false;
758

759
                if (strpbrk(u, ":/")) /* Colons are the field separator in /etc/passwd, we can't allow
169,850✔
760
                                       * that. Slashes are special to file systems paths and user names
761
                                       * typically show up in the file system as home directories, hence
762
                                       * don't allow slashes. */
763
                        return false;
764

765
                if (in_charset(u, "0123456789")) /* Don't allow fully numeric strings, they might be confused
169,841✔
766
                                                  * with UIDs (note that this test is more broad than
767
                                                  * the parse_uid() test above, as it will cover more than
768
                                                  * the 32-bit range, and it will detect 65535 (which is in
769
                                                  * invalid UID, even though in the unsigned 32 bit range) */
770
                        return false;
771

772
                if (u[0] == '-' && in_charset(u + 1, "0123456789")) /* Don't allow negative fully numeric
169,833✔
773
                                                                     * strings either. After all some people
774
                                                                     * write 65535 as -1 (even though that's
775
                                                                     * not even true on 32-bit uid_t
776
                                                                     * anyway) */
777
                        return false;
778

779
                if (dot_or_dot_dot(u)) /* User names typically become home directory names, and these two are
169,831✔
780
                                        * special in that context, don't allow that. */
781
                        return false;
782

783
                /* Compare with strict result and warn if result doesn't match */
784
                if (FLAGS_SET(flags, VALID_USER_WARN) && !valid_user_group_name(u, 0))
169,825✔
785
                        log_struct(LOG_NOTICE,
×
786
                                   LOG_MESSAGE("Accepting user/group name '%s', which does not match strict user/group name rules.", u),
787
                                   LOG_ITEM("USER_GROUP_NAME=%s", u),
788
                                   LOG_MESSAGE_ID(SD_MESSAGE_UNSAFE_USER_NAME_STR));
789

790
                /* Note that we make no restrictions on the length in relaxed mode! */
791
        } else {
792
                long sz;
3,577✔
793
                size_t l;
3,577✔
794

795
                /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.437. We are a bit stricter here
796
                 * however. Specifically we deviate from POSIX rules:
797
                 *
798
                 * - We don't allow empty user names (see above)
799
                 * - We require that names fit into the appropriate utmp field
800
                 * - We don't allow any dots (this conflicts with chown syntax which permits dots as user/group name separator)
801
                 * - We don't allow dashes or digit as the first character
802
                 *
803
                 * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters.
804
                 */
805

806
                if (!ascii_isalpha(u[0]) &&
3,577✔
807
                    u[0] != '_')
808
                        return false;
809

810
                for (i = u+1; *i; i++)
31,709✔
811
                        if (!ascii_isalpha(*i) &&
28,192✔
812
                            !ascii_isdigit(*i) &&
3,054✔
813
                            !IN_SET(*i, '_', '-'))
1,417✔
814
                                return false;
815

816
                l = i - u;
3,517✔
817

818
                sz = sysconf(_SC_LOGIN_NAME_MAX);
3,517✔
819
                assert_se(sz > 0);
3,517✔
820

821
                if (l > (size_t) sz) /* glibc: 256 */
3,517✔
822
                        return false;
823
                if (l > NAME_MAX) /* must fit in a filename: 255 */
3,517✔
824
                        return false;
825
                if (l > sizeof_field(struct utmpx, ut_user) - 1) /* must fit in utmp: 31 */
3,517✔
826
                        return false;
6✔
827
        }
828

829
        return true;
830
}
831

832
bool valid_gecos(const char *d) {
8,106✔
833

834
        if (!d)
8,106✔
835
                return false;
836

837
        if (!utf8_is_valid(d))
8,105✔
838
                return false;
839

840
        if (string_has_cc(d, NULL))
8,105✔
841
                return false;
842

843
        /* Colons are used as field separators, and hence not OK */
844
        if (strchr(d, ':'))
8,104✔
845
                return false;
1✔
846

847
        return true;
848
}
849

850
char* mangle_gecos(const char *d) {
8✔
851
        char *mangled;
8✔
852

853
        /* Makes sure the provided string becomes valid as a GEGOS field, by dropping bad chars. glibc's
854
         * putpwent() only changes \n and : to spaces. We do more: replace all CC too, and remove invalid
855
         * UTF-8 */
856

857
        mangled = strdup(d);
8✔
858
        if (!mangled)
8✔
859
                return NULL;
860

861
        for (char *i = mangled; *i; i++) {
65✔
862
                int len;
57✔
863

864
                if ((uint8_t) *i < (uint8_t) ' ' || *i == ':') {
57✔
865
                        *i = ' ';
8✔
866
                        continue;
8✔
867
                }
868

869
                len = utf8_encoded_valid_unichar(i, SIZE_MAX);
49✔
870
                if (len < 0) {
49✔
871
                        *i = ' ';
3✔
872
                        continue;
3✔
873
                }
874

875
                i += len - 1;
46✔
876
        }
877

878
        return mangled;
879
}
880

881
bool valid_home(const char *p) {
5,269✔
882
        /* Note that this function is also called by valid_shell(), any
883
         * changes must account for that. */
884

885
        if (isempty(p))
5,269✔
886
                return false;
887

888
        if (!utf8_is_valid(p))
5,265✔
889
                return false;
890

891
        if (string_has_cc(p, NULL))
5,265✔
892
                return false;
893

894
        if (!path_is_absolute(p))
5,263✔
895
                return false;
896

897
        if (!path_is_normalized(p))
5,257✔
898
                return false;
899

900
        /* Colons are used as field separators, and hence not OK */
901
        if (strchr(p, ':'))
5,253✔
902
                return false;
2✔
903

904
        return true;
905
}
906

907
bool valid_shell(const char *p) {
42✔
908
        /* We have the same requirements, so just piggy-back on the home check.
909
         *
910
         * Let's ignore /etc/shells because this is only applicable to real and not system users. It is also
911
         * incompatible with the idea of empty /etc/. */
912
        if (!valid_home(p))
42✔
913
                return false;
914

915
        return !endswith(p, "/"); /* one additional restriction: shells may not be dirs */
33✔
916
}
917

918
int maybe_setgroups(size_t size, const gid_t *list) {
914✔
919
        int r;
914✔
920

921
        /* Check if setgroups is allowed before we try to drop all the auxiliary groups */
922
        if (size == 0) { /* Dropping all aux groups? */
914✔
923
                _cleanup_free_ char *setgroups_content = NULL;
550✔
924
                bool can_setgroups;
550✔
925

926
                r = read_one_line_file("/proc/self/setgroups", &setgroups_content);
550✔
927
                if (r == -ENOENT)
550✔
928
                        /* Old kernels don't have /proc/self/setgroups, so assume we can use setgroups */
929
                        can_setgroups = true;
930
                else if (r < 0)
494✔
931
                        return r;
932
                else
933
                        can_setgroups = streq(setgroups_content, "allow");
494✔
934

935
                if (!can_setgroups) {
494✔
936
                        log_debug("Skipping setgroups(), /proc/self/setgroups is set to 'deny'");
×
937
                        return 0;
×
938
                }
939
        }
940

941
        return RET_NERRNO(setgroups(size, list));
916✔
942
}
943

944
bool synthesize_nobody(void) {
89✔
945
        /* Returns true when we shall synthesize the "nobody" user (which we do by default). This can be turned off by
946
         * touching /etc/systemd/dont-synthesize-nobody in order to provide upgrade compatibility with legacy systems
947
         * that used the "nobody" user name and group name for other UIDs/GIDs than 65534.
948
         *
949
         * Note that we do not employ any kind of synchronization on the following caching variable. If the variable is
950
         * accessed in multi-threaded programs in the worst case it might happen that we initialize twice, but that
951
         * shouldn't matter as each initialization should come to the same result. */
952
        static int cache = -1;
89✔
953

954
        if (cache < 0)
89✔
955
                cache = access("/etc/systemd/dont-synthesize-nobody", F_OK) < 0;
63✔
956

957
        return cache;
89✔
958
}
959

960
int putpwent_sane(const struct passwd *pw, FILE *stream) {
416✔
961
        assert(pw);
416✔
962
        assert(stream);
416✔
963

964
        errno = 0;
416✔
965
        if (putpwent(pw, stream) != 0)
416✔
966
                return errno_or_else(EIO);
×
967

968
        return 0;
969
}
970

971
int putspent_sane(const struct spwd *sp, FILE *stream) {
386✔
972
        assert(sp);
386✔
973
        assert(stream);
386✔
974

975
        errno = 0;
386✔
976
        if (putspent(sp, stream) != 0)
386✔
977
                return errno_or_else(EIO);
×
978

979
        return 0;
980
}
981

982
int putgrent_sane(const struct group *gr, FILE *stream) {
776✔
983
        assert(gr);
776✔
984
        assert(stream);
776✔
985

986
        errno = 0;
776✔
987
        if (putgrent(gr, stream) != 0)
776✔
988
                return errno_or_else(EIO);
×
989

990
        return 0;
991
}
992

993
#if ENABLE_GSHADOW
994
int putsgent_sane(const struct sgrp *sg, FILE *stream) {
751✔
995
        assert(sg);
751✔
996
        assert(stream);
751✔
997

998
        errno = 0;
751✔
999
        if (putsgent(sg, stream) != 0)
751✔
1000
                return errno_or_else(EIO);
×
1001

1002
        return 0;
1003
}
1004
#endif
1005

1006
int fgetpwent_sane(FILE *stream, struct passwd **pw) {
1,156✔
1007
        assert(stream);
1,156✔
1008
        assert(pw);
1,156✔
1009

1010
        errno = 0;
1,156✔
1011
        struct passwd *p = fgetpwent(stream);
1,156✔
1012
        if (!p && !IN_SET(errno, 0, ENOENT))
1,156✔
1013
                return -errno;
×
1014

1015
        *pw = p;
1,156✔
1016
        return !!p;
1,156✔
1017
}
1018

1019
int fgetspent_sane(FILE *stream, struct spwd **sp) {
223✔
1020
        assert(stream);
223✔
1021
        assert(sp);
223✔
1022

1023
        errno = 0;
223✔
1024
        struct spwd *s = fgetspent(stream);
223✔
1025
        if (!s && !IN_SET(errno, 0, ENOENT))
223✔
1026
                return -errno;
×
1027

1028
        *sp = s;
223✔
1029
        return !!s;
223✔
1030
}
1031

1032
int fgetgrent_sane(FILE *stream, struct group **gr) {
2,319✔
1033
        assert(stream);
2,319✔
1034
        assert(gr);
2,319✔
1035

1036
        errno = 0;
2,319✔
1037
        struct group *g = fgetgrent(stream);
2,319✔
1038
        if (!g && !IN_SET(errno, 0, ENOENT))
2,319✔
1039
                return -errno;
×
1040

1041
        *gr = g;
2,319✔
1042
        return !!g;
2,319✔
1043
}
1044

1045
#if ENABLE_GSHADOW
1046
int fgetsgent_sane(FILE *stream, struct sgrp **sg) {
239✔
1047
        assert(stream);
239✔
1048
        assert(sg);
239✔
1049

1050
        errno = 0;
239✔
1051
        struct sgrp *s = fgetsgent(stream);
239✔
1052
        if (!s && !IN_SET(errno, 0, ENOENT))
239✔
1053
                return -errno;
×
1054

1055
        *sg = s;
239✔
1056
        return !!s;
239✔
1057
}
1058
#endif
1059

1060
int is_this_me(const char *username) {
83✔
1061
        uid_t uid;
83✔
1062
        int r;
83✔
1063

1064
        /* Checks if the specified username is our current one. Passed string might be a UID or a user name. */
1065

1066
        r = get_user_creds(&username, &uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
83✔
1067
        if (r < 0)
83✔
1068
                return r;
83✔
1069

1070
        return uid == getuid();
82✔
1071
}
1072

1073
const char* get_home_root(void) {
8,789✔
1074
        const char *e;
8,789✔
1075

1076
        /* For debug purposes allow overriding where we look for home dirs */
1077
        e = secure_getenv("SYSTEMD_HOME_ROOT");
8,789✔
1078
        if (e && path_is_absolute(e) && path_is_normalized(e))
8,789✔
1079
                return e;
×
1080

1081
        return "/home";
1082
}
1083

1084
static size_t getpw_buffer_size(void) {
4,101✔
1085
        long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
4,101✔
1086
        return bufsize <= 0 ? 4096U : (size_t) bufsize;
4,101✔
1087
}
1088

1089
static bool errno_is_user_doesnt_exist(int error) {
1✔
1090
        /* See getpwnam(3) and getgrnam(3): those codes and others can be returned if the user or group are
1091
         * not found. */
1092
        return IN_SET(abs(error), ENOENT, ESRCH, EBADF, EPERM);
1✔
1093
}
1094

1095
int getpwnam_malloc(const char *name, struct passwd **ret) {
456✔
1096
        size_t bufsize = getpw_buffer_size();
456✔
1097
        int r;
456✔
1098

1099
        /* A wrapper around getpwnam_r() that allocates the necessary buffer on the heap. The caller must
1100
         * free() the returned structures! */
1101

1102
        if (isempty(name))
912✔
1103
                return -EINVAL;
1104

1105
        for (;;) {
456✔
1106
                _cleanup_free_ void *buf = NULL;
456✔
1107

1108
                buf = malloc0(ALIGN(sizeof(struct passwd)) + bufsize);
456✔
1109
                if (!buf)
456✔
1110
                        return -ENOMEM;
1111

1112
                struct passwd *pw = NULL;
456✔
1113
                r = getpwnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct passwd)), (size_t) bufsize, &pw);
456✔
1114
                if (r == 0) {
456✔
1115
                        if (pw) {
456✔
1116
                                if (ret)
262✔
1117
                                        *ret = TAKE_PTR(buf);
262✔
1118
                                return 0;
262✔
1119
                        }
1120

1121
                        return -ESRCH;
1122
                }
1123

1124
                assert(r > 0);
×
1125

1126
                /* getpwnam() may fail with ENOENT if /etc/passwd is missing.  For us that is equivalent to
1127
                 * the name not being defined. */
1128
                if (errno_is_user_doesnt_exist(r))
×
1129
                        return -ESRCH;
1130
                if (r != ERANGE)
×
1131
                        return -r;
×
1132

1133
                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct passwd)))
×
1134
                        return -ENOMEM;
1135
                bufsize *= 2;
×
1136
        }
1137
}
1138

1139
int getpwuid_malloc(uid_t uid, struct passwd **ret) {
3,645✔
1140
        size_t bufsize = getpw_buffer_size();
3,645✔
1141
        int r;
3,645✔
1142

1143
        if (!uid_is_valid(uid))
3,645✔
1144
                return -EINVAL;
1145

1146
        for (;;) {
3,645✔
1147
                _cleanup_free_ void *buf = NULL;
3,645✔
1148

1149
                buf = malloc0(ALIGN(sizeof(struct passwd)) + bufsize);
3,645✔
1150
                if (!buf)
3,645✔
1151
                        return -ENOMEM;
1152

1153
                struct passwd *pw = NULL;
3,645✔
1154
                r = getpwuid_r(uid, buf, (char*) buf + ALIGN(sizeof(struct passwd)), (size_t) bufsize, &pw);
3,645✔
1155
                if (r == 0) {
3,645✔
1156
                        if (pw) {
3,645✔
1157
                                if (ret)
422✔
1158
                                        *ret = TAKE_PTR(buf);
422✔
1159
                                return 0;
422✔
1160
                        }
1161

1162
                        return -ESRCH;
1163
                }
1164

1165
                assert(r > 0);
×
1166

1167
                if (errno_is_user_doesnt_exist(r))
×
1168
                        return -ESRCH;
1169
                if (r != ERANGE)
×
1170
                        return -r;
×
1171

1172
                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct passwd)))
×
1173
                        return -ENOMEM;
1174
                bufsize *= 2;
×
1175
        }
1176
}
1177

1178
static size_t getgr_buffer_size(void) {
7,698✔
1179
        long bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
7,698✔
1180
        return bufsize <= 0 ? 4096U : (size_t) bufsize;
7,698✔
1181
}
1182

1183
int getgrnam_malloc(const char *name, struct group **ret) {
2,087✔
1184
        size_t bufsize = getgr_buffer_size();
2,087✔
1185
        int r;
2,087✔
1186

1187
        if (isempty(name))
4,174✔
1188
                return -EINVAL;
1189

1190
        for (;;) {
2,087✔
1191
                _cleanup_free_ void *buf = NULL;
2,087✔
1192

1193
                buf = malloc0(ALIGN(sizeof(struct group)) + bufsize);
2,087✔
1194
                if (!buf)
2,087✔
1195
                        return -ENOMEM;
1196

1197
                struct group *gr = NULL;
2,087✔
1198
                r = getgrnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct group)), (size_t) bufsize, &gr);
2,087✔
1199
                if (r == 0) {
2,087✔
1200
                        if (gr) {
2,086✔
1201
                                if (ret)
1,884✔
1202
                                        *ret = TAKE_PTR(buf);
1,882✔
1203
                                return 0;
1,884✔
1204
                        }
1205

1206
                        return -ESRCH;
1207
                }
1208

1209
                assert(r > 0);
1✔
1210

1211
                if (errno_is_user_doesnt_exist(r))
1✔
1212
                        return -ESRCH;
1213
                if (r != ERANGE)
1✔
1214
                        return -r;
1✔
1215

1216
                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct group)))
×
1217
                        return -ENOMEM;
1218
                bufsize *= 2;
×
1219
        }
1220
}
1221

1222
int getgrgid_malloc(gid_t gid, struct group **ret) {
5,611✔
1223
        size_t bufsize = getgr_buffer_size();
5,611✔
1224
        int r;
5,611✔
1225

1226
        if (!gid_is_valid(gid))
5,611✔
1227
                return -EINVAL;
1228

1229
        for (;;) {
5,611✔
1230
                _cleanup_free_ void *buf = NULL;
5,611✔
1231

1232
                buf = malloc0(ALIGN(sizeof(struct group)) + bufsize);
5,611✔
1233
                if (!buf)
5,611✔
1234
                        return -ENOMEM;
1235

1236
                struct group *gr = NULL;
5,611✔
1237
                r = getgrgid_r(gid, buf, (char*) buf + ALIGN(sizeof(struct group)), (size_t) bufsize, &gr);
5,611✔
1238
                if (r == 0) {
5,611✔
1239
                        if (gr) {
5,611✔
1240
                                if (ret)
2,347✔
1241
                                        *ret = TAKE_PTR(buf);
2,347✔
1242
                                return 0;
2,347✔
1243
                        }
1244

1245
                        return -ESRCH;
1246
                }
1247

1248
                assert(r > 0);
×
1249

1250
                if (errno_is_user_doesnt_exist(r))
×
1251
                        return -ESRCH;
1252
                if (r != ERANGE)
×
1253
                        return -r;
×
1254

1255
                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct group)))
×
1256
                        return -ENOMEM;
1257
                bufsize *= 2;
×
1258
        }
1259
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc