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

systemd / systemd / 22648815823

03 Mar 2026 11:47PM UTC coverage: 72.603% (+0.3%) from 72.342%
22648815823

push

github

yuwata
udevadm: fix --help text for udevadm test-builtin

316021 of 435275 relevant lines covered (72.6%)

1244520.48 hits per line

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

86.25
/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);
11✔
49
DEFINE_STRERROR_ACCOUNT(group);
9✔
50

51
bool uid_is_valid(uid_t uid) {
12,138,336✔
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))
12,138,336✔
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))
6,260,972✔
61
                return false;
19✔
62

63
        return true;
64
}
65

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

70
        assert(s);
234,254✔
71

72
        assert_cc(sizeof(uid_t) == sizeof(uint32_t));
234,254✔
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
234,254✔
79
                             | SAFE_ATO_REFUSE_PLUS_MINUS
80
                             | SAFE_ATO_REFUSE_LEADING_ZERO
81
                             | SAFE_ATO_REFUSE_LEADING_WHITESPACE, &uid);
82
        if (r < 0)
234,254✔
83
                return r;
234,254✔
84

85
        if (!uid_is_valid(uid))
11,784✔
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,767✔
92
                *ret = uid;
11,196✔
93

94
        return 0;
95
}
96

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

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

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

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

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

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

132
        *ret_lower = l;
46✔
133
        *ret_upper = u;
46✔
134
        return 0;
46✔
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) {
722✔
150
        const char *e;
722✔
151

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

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

159
bool is_nologin_shell(const char *shell) {
2,703✔
160
        return PATH_IN_SET(shell,
2,703✔
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,154✔
178
        return isempty(shell) || is_nologin_shell(shell);
4,308✔
179
}
180

181
const char* default_root_shell_at(int rfd) {
7,512✔
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,512✔
187

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

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

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

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

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

207
static int synthesize_user_creds(
18,573✔
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);
18,573✔
215
        assert(*username);
18,573✔
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")) {
18,573✔
221
                *username = "root";
14,830✔
222

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

232
                return 0;
14,830✔
233
        }
234

235
        if (STR_IN_SET(*username, NOBODY_USER_NAME, "65534") &&
3,754✔
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,732✔
252
}
253

254
int get_user_creds(
19,119✔
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;
19,119✔
262
        uid_t u = UID_INVALID;
19,119✔
263
        struct passwd *p;
19,119✔
264
        int r;
19,119✔
265

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

270
        if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
19,119✔
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);
18,573✔
283
                if (r >= 0)
18,573✔
284
                        return 0;
19,119✔
285
                if (r != -ENOMEDIUM) /* not a username we can synthesize */
3,732✔
286
                        return r;
287
        }
288

289
        if (parse_uid(*username, &u) >= 0) {
4,278✔
290
                errno = 0;
205✔
291
                p = getpwuid(u);
205✔
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)
205✔
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;
4,073✔
311
                p = getpwnam(*username);
4,073✔
312
        }
313
        if (!p) {
4,076✔
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,269✔
327
                return -EBADMSG;
328

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

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

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

338
        if (ret_home)
4,269✔
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,256✔
342
                            ? NULL : p->pw_dir;
4,256✔
343

344
        if (ret_shell)
4,269✔
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,256✔
347
                             ? NULL : p->pw_shell;
4,256✔
348

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

352
        return 0;
353
}
354

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

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

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

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

368
                return 0;
5,695✔
369
        }
370

371
        if (STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534") &&
5,120✔
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;
5,112✔
382
}
383

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

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

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

401
        if (parse_gid(*groupname, &id) >= 0) {
5,112✔
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;
5,112✔
415
                g = getgrnam(*groupname);
5,112✔
416
        }
417

418
        if (!g) {
5,112✔
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) {
5,111✔
431
                if (!gid_is_valid(g->gr_gid))
5,111✔
432
                        return -EBADMSG;
433

434
                *ret_gid = g->gr_gid;
5,111✔
435
        }
436

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

440
        return 0;
441
}
442

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

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

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

456
                r = getpwuid_malloc(uid, &pw);
203✔
457
                if (r >= 0)
203✔
458
                        return strdup(pw->pw_name);
203✔
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) {
494✔
491
        assert(list || size == 0);
494✔
492

493
        FOREACH_ARRAY(i, list, size)
661✔
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) {
10,368✔
532
        size_t nresult = 0;
10,368✔
533
        assert(ret);
10,368✔
534

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

538
        gid_t *buf = new(gid_t, size1 + size2);
10,368✔
539
        if (!buf)
10,368✔
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++)
10,407✔
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,786✔
547
                if (!gid_list_has(buf, nresult, list2[i]))
418✔
548
                        buf[nresult++] = list2[i];
403✔
549
        *ret = buf;
10,368✔
550
        return (int)nresult;
10,368✔
551
}
552

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

556
        assert(ret);
584✔
557

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

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

565
                ngroups = getgroups(ngroups, p);
584✔
566
                if (ngroups > 0) {
584✔
567
                        *ret = TAKE_PTR(p);
426✔
568
                        return ngroups;
426✔
569
                }
570
                if (ngroups == 0)
158✔
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;
158✔
589
        return 0;
158✔
590
}
591

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

598
        assert(ret);
16,214✔
599

600
        /* Take the user specified one */
601
        e = secure_getenv("HOME");
16,214✔
602
        if (e && path_is_valid(e) && path_is_absolute(e))
31,748✔
603
                goto found;
15,532✔
604

605
        /* Hardcode home directory for root and nobody to avoid NSS */
606
        u = getuid();
682✔
607
        if (u == 0) {
682✔
608
                e = "/root";
682✔
609
                goto found;
682✔
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))
16,214✔
623
                return -EINVAL;
624

625
 found:
×
626
        return path_simplify_alloc(e, ret);
16,214✔
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) {
625✔
667
        int r;
625✔
668

669
        assert(supplementary_gids || n_supplementary_gids == 0);
625✔
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);
625✔
674
        if (r < 0)
625✔
675
                return r;
676

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

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

685
        return 0;
686
}
687

688
int take_etc_passwd_lock(const char *root) {
299✔
689
        int r;
299✔
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);
598✔
699
        if (!path)
299✔
700
                return log_oom_debug();
×
701

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

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

708
        r = unposix_lock(fd, LOCK_EX);
299✔
709
        if (r < 0)
299✔
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) {
191,295✔
716
        const char *i;
191,295✔
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 */
191,295✔
726
                return false;
727

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

732
        if (FLAGS_SET(flags, VALID_USER_RELAX)) {
191,103✔
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
186,586✔
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 */
186,586✔
753
                        return false;
754

755
                if (string_has_cc(u, NULL)) /* CC characters are just dangerous (and \n in particular is the
186,586✔
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
186,584✔
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, DIGITS)) /* Don't allow fully numeric strings, they might be confused with
186,575✔
766
                                            * UIDs (note that this test is more broad than the parse_uid()
767
                                            * test above, as it will cover more than the 32-bit range, and it
768
                                            * will detect 65535 (which is in invalid UID, even though in the
769
                                            * unsigned 32 bit range) */
770
                        return false;
771

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

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

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

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

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

805
                if (!ascii_isalpha(u[0]) &&
4,517✔
806
                    u[0] != '_')
807
                        return false;
808

809
                for (i = u+1; *i; i++)
47,369✔
810
                        if (!ascii_isalpha(*i) &&
42,932✔
811
                            !ascii_isdigit(*i) &&
7,969✔
812
                            !IN_SET(*i, '_', '-'))
2,893✔
813
                                return false;
814

815
                l = i - u;
4,437✔
816

817
                sz = sysconf(_SC_LOGIN_NAME_MAX);
4,437✔
818
                assert_se(sz > 0);
4,437✔
819

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

828
        return true;
829
}
830

831
bool valid_gecos(const char *d) {
8,102✔
832

833
        if (!d)
8,102✔
834
                return false;
835

836
        if (!utf8_is_valid(d))
8,101✔
837
                return false;
838

839
        if (string_has_cc(d, NULL))
8,101✔
840
                return false;
841

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

846
        return true;
847
}
848

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

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

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

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

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

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

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

877
        return mangled;
878
}
879

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

884
        if (isempty(p))
5,313✔
885
                return false;
886

887
        if (!utf8_is_valid(p))
5,309✔
888
                return false;
889

890
        if (string_has_cc(p, NULL))
5,309✔
891
                return false;
892

893
        if (!path_is_absolute(p))
5,307✔
894
                return false;
895

896
        if (!path_is_normalized(p))
5,301✔
897
                return false;
898

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

903
        return true;
904
}
905

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

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

917
int maybe_setgroups(size_t size, const gid_t *list) {
1,057✔
918
        int r;
1,057✔
919

920
        /* Check if setgroups is allowed before we try to drop all the auxiliary groups */
921
        if (size == 0) { /* Dropping all aux groups? */
1,057✔
922

923
                /* The kernel refuses setgroups() if there are no GID mappings in the current
924
                 * user namespace, so check that beforehand and don't try to setgroups() if
925
                 * there are no GID mappings. */
926
                _cleanup_fclose_ FILE *f = fopen("/proc/self/gid_map", "re");
1,306✔
927
                if (!f && errno != ENOENT)
653✔
928
                        return -errno;
×
929
                if (f) {
930
                        r = safe_fgetc(f, /* ret= */ NULL);
600✔
931
                        if (r < 0)
600✔
932
                                return r;
933
                        if (r == 0) {
600✔
934
                                log_debug("Skipping setgroups(), /proc/self/gid_map is empty");
2✔
935
                                return 0;
2✔
936
                        }
937
                }
938

939
                _cleanup_free_ char *setgroups_content = NULL;
651✔
940
                r = read_one_line_file("/proc/self/setgroups", &setgroups_content);
651✔
941
                if (r < 0 && r != -ENOENT)
651✔
942
                        return r;
943
                if (r > 0 && streq(setgroups_content, "deny")) {
651✔
944
                        log_debug("Skipping setgroups(), /proc/self/setgroups is set to 'deny'");
×
945
                        return 0;
×
946
                }
947
        }
948

949
        return RET_NERRNO(setgroups(size, list));
1,059✔
950
}
951

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

962
        if (cache < 0)
89✔
963
                cache = access("/etc/systemd/dont-synthesize-nobody", F_OK) < 0;
63✔
964

965
        return cache;
89✔
966
}
967

968
int putpwent_sane(const struct passwd *pw, FILE *stream) {
439✔
969
        assert(pw);
439✔
970
        assert(stream);
439✔
971

972
        errno = 0;
439✔
973
        if (putpwent(pw, stream) != 0)
439✔
974
                return errno_or_else(EIO);
×
975

976
        return 0;
977
}
978

979
int putspent_sane(const struct spwd *sp, FILE *stream) {
409✔
980
        assert(sp);
409✔
981
        assert(stream);
409✔
982

983
        errno = 0;
409✔
984
        if (putspent(sp, stream) != 0)
409✔
985
                return errno_or_else(EIO);
×
986

987
        return 0;
988
}
989

990
int putgrent_sane(const struct group *gr, FILE *stream) {
832✔
991
        assert(gr);
832✔
992
        assert(stream);
832✔
993

994
        errno = 0;
832✔
995
        if (putgrent(gr, stream) != 0)
832✔
996
                return errno_or_else(EIO);
×
997

998
        return 0;
999
}
1000

1001
#if ENABLE_GSHADOW
1002
int putsgent_sane(const struct sgrp *sg, FILE *stream) {
807✔
1003
        assert(sg);
807✔
1004
        assert(stream);
807✔
1005

1006
        errno = 0;
807✔
1007
        if (putsgent(sg, stream) != 0)
807✔
1008
                return errno_or_else(EIO);
×
1009

1010
        return 0;
1011
}
1012
#endif
1013

1014
int fgetpwent_sane(FILE *stream, struct passwd **pw) {
1,203✔
1015
        assert(stream);
1,203✔
1016
        assert(pw);
1,203✔
1017

1018
        errno = 0;
1,203✔
1019
        struct passwd *p = fgetpwent(stream);
1,203✔
1020
        if (!p && !IN_SET(errno, 0, ENOENT))
1,203✔
1021
                return -errno;
×
1022

1023
        *pw = p;
1,203✔
1024
        return !!p;
1,203✔
1025
}
1026

1027
int fgetspent_sane(FILE *stream, struct spwd **sp) {
249✔
1028
        assert(stream);
249✔
1029
        assert(sp);
249✔
1030

1031
        errno = 0;
249✔
1032
        struct spwd *s = fgetspent(stream);
249✔
1033
        if (!s && !IN_SET(errno, 0, ENOENT))
249✔
1034
                return -errno;
×
1035

1036
        *sp = s;
249✔
1037
        return !!s;
249✔
1038
}
1039

1040
int fgetgrent_sane(FILE *stream, struct group **gr) {
2,429✔
1041
        assert(stream);
2,429✔
1042
        assert(gr);
2,429✔
1043

1044
        errno = 0;
2,429✔
1045
        struct group *g = fgetgrent(stream);
2,429✔
1046
        if (!g && !IN_SET(errno, 0, ENOENT))
2,429✔
1047
                return -errno;
×
1048

1049
        *gr = g;
2,429✔
1050
        return !!g;
2,429✔
1051
}
1052

1053
#if ENABLE_GSHADOW
1054
int fgetsgent_sane(FILE *stream, struct sgrp **sg) {
295✔
1055
        assert(stream);
295✔
1056
        assert(sg);
295✔
1057

1058
        errno = 0;
295✔
1059
        struct sgrp *s = fgetsgent(stream);
295✔
1060
        if (!s && !IN_SET(errno, 0, ENOENT))
295✔
1061
                return -errno;
×
1062

1063
        *sg = s;
295✔
1064
        return !!s;
295✔
1065
}
1066
#endif
1067

1068
int is_this_me(const char *username) {
87✔
1069
        uid_t uid;
87✔
1070
        int r;
87✔
1071

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

1074
        r = get_user_creds(&username, &uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
87✔
1075
        if (r < 0)
87✔
1076
                return r;
87✔
1077

1078
        return uid == getuid();
86✔
1079
}
1080

1081
const char* get_home_root(void) {
12,227✔
1082
        const char *e;
12,227✔
1083

1084
        /* For debug purposes allow overriding where we look for home dirs */
1085
        e = secure_getenv("SYSTEMD_HOME_ROOT");
12,227✔
1086
        if (e && path_is_absolute(e) && path_is_normalized(e))
12,227✔
1087
                return e;
×
1088

1089
        return "/home";
1090
}
1091

1092
static size_t getpw_buffer_size(void) {
4,293✔
1093
        long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
4,293✔
1094
        return bufsize <= 0 ? 4096U : (size_t) bufsize;
4,293✔
1095
}
1096

1097
static bool errno_is_user_doesnt_exist(int error) {
1✔
1098
        /* See getpwnam(3) and getgrnam(3): those codes and others can be returned if the user or group are
1099
         * not found. */
1100
        return IN_SET(abs(error), ENOENT, ESRCH, EBADF, EPERM);
1✔
1101
}
1102

1103
int getpwnam_malloc(const char *name, struct passwd **ret) {
560✔
1104
        size_t bufsize = getpw_buffer_size();
560✔
1105
        int r;
560✔
1106

1107
        /* A wrapper around getpwnam_r() that allocates the necessary buffer on the heap. The caller must
1108
         * free() the returned structures! */
1109

1110
        if (isempty(name))
1,120✔
1111
                return -EINVAL;
1112

1113
        for (;;) {
560✔
1114
                _cleanup_free_ void *buf = NULL;
560✔
1115

1116
                buf = malloc0(ALIGN(sizeof(struct passwd)) + bufsize);
560✔
1117
                if (!buf)
560✔
1118
                        return -ENOMEM;
1119

1120
                struct passwd *pw = NULL;
560✔
1121
                r = getpwnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct passwd)), bufsize, &pw);
560✔
1122
                if (r == 0) {
560✔
1123
                        if (pw) {
560✔
1124
                                if (ret)
289✔
1125
                                        *ret = TAKE_PTR(buf);
289✔
1126
                                return 0;
289✔
1127
                        }
1128

1129
                        return -ESRCH;
1130
                }
1131

1132
                assert(r > 0);
×
1133

1134
                /* getpwnam() may fail with ENOENT if /etc/passwd is missing.  For us that is equivalent to
1135
                 * the name not being defined. */
1136
                if (errno_is_user_doesnt_exist(r))
×
1137
                        return -ESRCH;
1138
                if (r != ERANGE)
×
1139
                        return -r;
×
1140

1141
                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct passwd)))
×
1142
                        return -ENOMEM;
1143
                bufsize *= 2;
×
1144
        }
1145
}
1146

1147
int getpwuid_malloc(uid_t uid, struct passwd **ret) {
3,733✔
1148
        size_t bufsize = getpw_buffer_size();
3,733✔
1149
        int r;
3,733✔
1150

1151
        if (!uid_is_valid(uid))
3,733✔
1152
                return -EINVAL;
1153

1154
        for (;;) {
3,733✔
1155
                _cleanup_free_ void *buf = NULL;
3,733✔
1156

1157
                buf = malloc0(ALIGN(sizeof(struct passwd)) + bufsize);
3,733✔
1158
                if (!buf)
3,733✔
1159
                        return -ENOMEM;
1160

1161
                struct passwd *pw = NULL;
3,733✔
1162
                r = getpwuid_r(uid, buf, (char*) buf + ALIGN(sizeof(struct passwd)), bufsize, &pw);
3,733✔
1163
                if (r == 0) {
3,733✔
1164
                        if (pw) {
3,733✔
1165
                                if (ret)
428✔
1166
                                        *ret = TAKE_PTR(buf);
428✔
1167
                                return 0;
428✔
1168
                        }
1169

1170
                        return -ESRCH;
1171
                }
1172

1173
                assert(r > 0);
×
1174

1175
                if (errno_is_user_doesnt_exist(r))
×
1176
                        return -ESRCH;
1177
                if (r != ERANGE)
×
1178
                        return -r;
×
1179

1180
                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct passwd)))
×
1181
                        return -ENOMEM;
1182
                bufsize *= 2;
×
1183
        }
1184
}
1185

1186
static size_t getgr_buffer_size(void) {
8,290✔
1187
        long bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
8,290✔
1188
        return bufsize <= 0 ? 4096U : (size_t) bufsize;
8,290✔
1189
}
1190

1191
int getgrnam_malloc(const char *name, struct group **ret) {
2,461✔
1192
        size_t bufsize = getgr_buffer_size();
2,461✔
1193
        int r;
2,461✔
1194

1195
        if (isempty(name))
4,922✔
1196
                return -EINVAL;
1197

1198
        for (;;) {
2,461✔
1199
                _cleanup_free_ void *buf = NULL;
2,461✔
1200

1201
                buf = malloc0(ALIGN(sizeof(struct group)) + bufsize);
2,461✔
1202
                if (!buf)
2,461✔
1203
                        return -ENOMEM;
1204

1205
                struct group *gr = NULL;
2,461✔
1206
                r = getgrnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct group)), bufsize, &gr);
2,461✔
1207
                if (r == 0) {
2,461✔
1208
                        if (gr) {
2,460✔
1209
                                if (ret)
2,194✔
1210
                                        *ret = TAKE_PTR(buf);
2,192✔
1211
                                return 0;
2,194✔
1212
                        }
1213

1214
                        return -ESRCH;
1215
                }
1216

1217
                assert(r > 0);
1✔
1218

1219
                if (errno_is_user_doesnt_exist(r))
1✔
1220
                        return -ESRCH;
1221
                if (r != ERANGE)
1✔
1222
                        return -r;
1✔
1223

1224
                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct group)))
×
1225
                        return -ENOMEM;
1226
                bufsize *= 2;
×
1227
        }
1228
}
1229

1230
int getgrgid_malloc(gid_t gid, struct group **ret) {
5,829✔
1231
        size_t bufsize = getgr_buffer_size();
5,829✔
1232
        int r;
5,829✔
1233

1234
        if (!gid_is_valid(gid))
5,829✔
1235
                return -EINVAL;
1236

1237
        for (;;) {
5,829✔
1238
                _cleanup_free_ void *buf = NULL;
5,829✔
1239

1240
                buf = malloc0(ALIGN(sizeof(struct group)) + bufsize);
5,829✔
1241
                if (!buf)
5,829✔
1242
                        return -ENOMEM;
1243

1244
                struct group *gr = NULL;
5,829✔
1245
                r = getgrgid_r(gid, buf, (char*) buf + ALIGN(sizeof(struct group)), bufsize, &gr);
5,829✔
1246
                if (r == 0) {
5,829✔
1247
                        if (gr) {
5,829✔
1248
                                if (ret)
2,473✔
1249
                                        *ret = TAKE_PTR(buf);
2,473✔
1250
                                return 0;
2,473✔
1251
                        }
1252

1253
                        return -ESRCH;
1254
                }
1255

1256
                assert(r > 0);
×
1257

1258
                if (errno_is_user_doesnt_exist(r))
×
1259
                        return -ESRCH;
1260
                if (r != ERANGE)
×
1261
                        return -r;
×
1262

1263
                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct group)))
×
1264
                        return -ENOMEM;
1265
                bufsize *= 2;
×
1266
        }
1267
}
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