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

systemd / systemd / 26546993077

27 May 2026 08:34PM UTC coverage: 72.995% (+0.3%) from 72.667%
26546993077

push

github

bluca
test-pressure: set timeout to make not wait forever

If this runs on a slow or busy machine, then we may not get enough
pressure to trigger the event sources. In such case, the test does not
finish. It is problematic when the test is _not_ run with 'meson test',
e.g. debian/ubuntu CIs.

Let's introduce a timeout for each event loop, and skip test cases
gracefully.

8 of 12 new or added lines in 1 file covered. (66.67%)

19671 existing lines in 226 files now uncovered.

337119 of 461841 relevant lines covered (72.99%)

1326365.62 hits per line

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

85.99
/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) {
13,546,840✔
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))
13,546,840✔
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))
7,359,279✔
61
                return false;
18✔
62

63
        return true;
64
}
65

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

70
        assert(s);
251,825✔
71

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

85
        if (!uid_is_valid(uid))
13,344✔
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)
13,327✔
92
                *ret = uid;
12,533✔
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) {
779✔
150
        const char *e;
779✔
151

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

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

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

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

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

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

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

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

204
        return default_root_shell_at(rfd);
9,164✔
205
}
206

207
static int return_user_creds(
21,268✔
208
                const char *username,
209
                uid_t uid, gid_t gid,
210
                const char *home,
211
                const char *shell,
212
                char **ret_username,
213
                uid_t *ret_uid, gid_t *ret_gid,
214
                char **ret_home,
215
                char **ret_shell) {
216
        /* Helper function to help with the strdups and atomic setting of return params. */
217

218
        _cleanup_free_ char *s1 = NULL, *s2 = NULL, *s3 = NULL;
21,268✔
219
        int r;
21,268✔
220

221
        if (ret_username) {
21,268✔
222
                r = strdup_to(&s1, username);
11,375✔
223
                if (r < 0)
11,375✔
224
                        return r;
225
        }
226

227
        if (ret_home) {
21,268✔
228
                r = strdup_to(&s2, home);
11,375✔
229
                if (r < 0)
11,375✔
230
                        return r;
231
        }
232

233
        if (ret_shell) {
21,268✔
234
                r = strdup_to(&s3, shell);
11,375✔
235
                if (r < 0)
11,375✔
236
                        return r;
237
        }
238

239
        if (ret_username)
21,268✔
240
                *ret_username = TAKE_PTR(s1);
11,375✔
241
        if (ret_uid)
21,268✔
242
                *ret_uid = uid;
12,129✔
243
        if (ret_gid)
21,268✔
244
                *ret_gid = gid;
2,254✔
245
        if (ret_home)
21,268✔
246
                *ret_home = TAKE_PTR(s2);
11,375✔
247
        if (ret_shell)
21,268✔
248
                *ret_shell = TAKE_PTR(s3);
11,375✔
249
        return 0;
250
}
251

252
static int synthesize_user_creds(
20,679✔
253
                const char *username,
254
                UserCredsFlags flags,
255
                char **ret_username,
256
                uid_t *ret_uid, gid_t *ret_gid,
257
                char **ret_home,
258
                char **ret_shell) {
259
        assert(username);
20,679✔
260

261
        /* We enforce some special rules for uid=0 and uid=65534: in order to avoid nss lookups for root we
262
         * hardcode their user record data. */
263
        if (STR_IN_SET(username, "root", "0"))
20,679✔
264
                return return_user_creds("root", 0, 0,
26,140✔
265
                                         "/root",
266
                                         ret_shell ? default_root_shell(NULL) : NULL,
9,149✔
267
                                         ret_username,
268
                                         ret_uid, ret_gid,
269
                                         ret_home,
270
                                         ret_shell);
271

272
        if (STR_IN_SET(username, NOBODY_USER_NAME, "65534") &&
3,700✔
273
            synthesize_nobody())
12✔
274
                return return_user_creds(NOBODY_USER_NAME, UID_NOBODY, GID_NOBODY,
12✔
275
                                         FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : "/",
276
                                         FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : NOLOGIN,
12✔
277
                                         ret_username,
278
                                         ret_uid, ret_gid,
279
                                         ret_home,
280
                                         ret_shell);
281

282
        return -ENOMEDIUM;
3,676✔
283
}
284

285
int get_user_creds(
21,277✔
286
                const char *username,
287
                UserCredsFlags flags,
288
                char **ret_username,
289
                uid_t *ret_uid, gid_t *ret_gid,
290
                char **ret_home,
291
                char **ret_shell) {
292

293
        uid_t u = UID_INVALID;
21,277✔
294
        _cleanup_free_ struct passwd *pw = NULL;
21,277✔
295
        int r;
21,277✔
296

297
        assert(username);
21,277✔
298
        assert((ret_home || ret_shell) || !(flags & (USER_CREDS_SUPPRESS_PLACEHOLDER|USER_CREDS_CLEAN)));
21,277✔
299

300
        if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
21,277✔
301
            (!ret_home && !ret_shell)) {
302

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

312
                r = synthesize_user_creds(username, flags, ret_username, ret_uid, ret_gid, ret_home, ret_shell);
20,679✔
313
                if (r >= 0)
20,679✔
314
                        return 0;
315
                if (r != -ENOMEDIUM) /* not a username we can synthesize */
3,676✔
316
                        return r;
317
        }
318

319
        if (parse_uid(username, &u) >= 0) {
4,274✔
320
                r = getpwuid_malloc(u, &pw);
213✔
321

322
                /* If there are multiple users with the same id, make sure to leave $USER to the configured value
323
                 * instead of the first occurrence in the database. However if the uid was configured by a numeric uid,
324
                 * then let's pick the real username from /etc/passwd. */
325
                if (r >= 0)
213✔
326
                        username = pw->pw_name;
210✔
327

328
                else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING) && !ret_gid && !ret_home && !ret_shell) {
3✔
329
                        /* If the specified user is a numeric UID and it isn't in the user database, and the caller
330
                         * passed USER_CREDS_ALLOW_MISSING and was only interested in the UID, then just return that
331
                         * and don't complain. */
UNCOV
332
                        if (ret_username)
×
UNCOV
333
                                *ret_username = NULL;
×
UNCOV
334
                        if (ret_uid)
×
UNCOV
335
                                *ret_uid = u;
×
336
                        return 0;
337
                }
338
        } else
339
                r = getpwnam_malloc(username, &pw);
4,061✔
340

341
        if (r < 0) {
4,274✔
342
                /* If the user requested that we only synthesize as fallback, do so now */
343
                if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS) &&
9✔
UNCOV
344
                    synthesize_user_creds(username, flags, ret_username, ret_uid, ret_gid, ret_home, ret_shell) >= 0)
×
345
                        return 0;
346

347
                return r;
9✔
348
        }
349

350
        if (ret_uid && !uid_is_valid(pw->pw_uid))
4,265✔
351
                return -EBADMSG;
352

353
        if (ret_gid && !gid_is_valid(pw->pw_gid))
4,265✔
354
                return -EBADMSG;
355

356
        /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
357
        const char *h =
8,530✔
358
                (FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) && empty_or_root(pw->pw_dir)) ||
1✔
359
                (FLAGS_SET(flags, USER_CREDS_CLEAN) && (!path_is_valid(pw->pw_dir) || !path_is_absolute(pw->pw_dir)))
4,265✔
360
                ? NULL : pw->pw_dir;
8,530✔
361

362
        const char *s =
8,530✔
363
                (FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) && shell_is_placeholder(pw->pw_shell)) ||
1✔
364
                (FLAGS_SET(flags, USER_CREDS_CLEAN) && (!path_is_valid(pw->pw_shell) || !path_is_absolute(pw->pw_shell)))
4,265✔
365
                ? NULL : pw->pw_shell;
8,530✔
366

367
        return return_user_creds(username, pw->pw_uid, pw->pw_gid, h, s,
4,265✔
368
                                 ret_username, ret_uid, ret_gid, ret_home, ret_shell);
369
}
370

371
static int synthesize_group_creds(const char *groupname, char **ret_name, gid_t *ret_gid) {
11,242✔
372
        assert(groupname);
11,242✔
373

374
        gid_t id;
11,242✔
375
        const char *n;
11,242✔
376
        int r;
11,242✔
377

378
        if (STR_IN_SET(groupname, "root", "0")) {
11,242✔
379
                id = 0;
380
                n = "root";
381
        } else if (STR_IN_SET(groupname, NOBODY_GROUP_NAME, "65534") &&
5,334✔
382
                   synthesize_nobody()) {
6✔
383
                id = GID_NOBODY;
6✔
384
                n = NOBODY_GROUP_NAME;
6✔
385
        } else
386
                return -ENOMEDIUM;
5,322✔
387

388
        r = strdup_to_full(ret_name, n);
5,920✔
389
        if (r < 0)
5,920✔
390
                return r;
391
        if (ret_gid)
5,920✔
392
                *ret_gid = id;
5,920✔
393
        return 0;
394
}
395

396
int get_group_creds(const char *groupname, UserCredsFlags flags, char **ret_name, gid_t *ret_gid) {
11,242✔
397
        _cleanup_free_ struct group *gr = NULL;
11,242✔
398
        gid_t id;
11,242✔
399
        int r;
11,242✔
400

401
        assert(groupname);
11,242✔
402

403
        if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) {
11,242✔
404
                r = synthesize_group_creds(groupname, ret_name, ret_gid);
11,242✔
405
                if (r >= 0)
11,242✔
406
                        return 0;
407
                if (r != -ENOMEDIUM) /* not a groupname we can synthesize */
5,322✔
408
                        return r;
409
        }
410

411
        if (parse_gid(groupname, &id) >= 0) {
5,322✔
UNCOV
412
                r = getgrgid_malloc(id, &gr);
×
UNCOV
413
                if (r >= 0)
×
UNCOV
414
                        groupname = gr->gr_name;
×
UNCOV
415
                else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING)) {
×
UNCOV
416
                        if (ret_gid)
×
UNCOV
417
                                *ret_gid = id;
×
UNCOV
418
                        if (ret_name)
×
UNCOV
419
                                *ret_name = NULL;
×
420
                        return 0;
421
                }
422
        } else
423
                r = getgrnam_malloc(groupname, &gr);
5,322✔
424

425
        if (r < 0) {
5,322✔
426
                if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS) &&
1✔
UNCOV
427
                    synthesize_group_creds(groupname, ret_name, ret_gid) >= 0)
×
428
                        return 0;
429
                return r;
1✔
430
        }
431

432
        if (ret_gid && !gid_is_valid(gr->gr_gid))
5,321✔
433
                return -EBADMSG;
434

435
        r = strdup_to_full(ret_name, groupname);
5,321✔
436
        if (r < 0)
5,321✔
437
                return r;
438
        if (ret_gid)
5,321✔
439
                *ret_gid = gr->gr_gid;
5,321✔
440
        return 0;
441
}
442

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

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

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

456
                r = getpwuid_malloc(uid, &pw);
209✔
457
                if (r >= 0)
209✔
458
                        return strdup(pw->pw_name);
209✔
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) {
114✔
468
        char *ret;
114✔
469
        int r;
114✔
470

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

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

479
                r = getgrgid_malloc(gid, &gr);
48✔
480
                if (r >= 0)
48✔
481
                        return strdup(gr->gr_name);
48✔
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) {
548✔
491
        assert(list || size == 0);
548✔
492

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

497
        return false;
498
}
499

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

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

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

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

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

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

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

524
        r = get_group_creds(name, /* flags= */ 0, /* ret_name= */ NULL, &gid);
15✔
525
        if (r < 0)
15✔
526
                return r;
15✔
527

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

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

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

538
        gid_t *buf = new(gid_t, size1 + size2);
12,158✔
539
        if (!buf)
12,158✔
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++)
12,199✔
544
                if (!gid_list_has(buf, nresult, list1[i]))
41✔
545
                        buf[nresult++] = list1[i];
40✔
546
        for (size_t i = 0; i < size2; i++)
12,625✔
547
                if (!gid_list_has(buf, nresult, list2[i]))
467✔
548
                        buf[nresult++] = list2[i];
452✔
549
        *ret = buf;
12,158✔
550
        return (int)nresult;
12,158✔
551
}
552

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

556
        assert(ret);
639✔
557

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

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

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

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

598
        assert(ret);
17,284✔
599

600
        /* Take the user specified one */
601
        e = secure_getenv("HOME");
17,284✔
602
        if (e && path_is_valid(e) && path_is_absolute(e))
33,874✔
603
                goto found;
16,588✔
604

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

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

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

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

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

685
        return 0;
686
}
687

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

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

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

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

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

732
        if (FLAGS_SET(flags, VALID_USER_RELAX)) {
206,034✔
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
199,871✔
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 */
199,871✔
753
                        return false;
754

755
                if (string_has_cc(u, NULL)) /* CC characters are just dangerous (and \n in particular is the
199,871✔
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
199,869✔
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
199,859✔
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
199,851✔
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
199,849✔
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))
199,843✔
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;
6,163✔
792
                size_t l;
6,163✔
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]) &&
6,163✔
806
                    u[0] != '_')
807
                        return false;
808

809
                for (i = u+1; *i; i++)
67,561✔
810
                        if (!ascii_isalpha(*i) &&
61,499✔
811
                            !ascii_isdigit(*i) &&
9,659✔
812
                            !IN_SET(*i, '_', '-'))
4,217✔
813
                                return false;
814

815
                l = i - u;
6,062✔
816

817
                sz = sysconf(_SC_LOGIN_NAME_MAX);
6,062✔
818
                assert_se(sz > 0);
6,062✔
819

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

828
        return true;
829
}
830

831
bool valid_gecos(const char *d) {
7,752✔
832

833
        if (!d)
7,752✔
834
                return false;
835

836
        if (!utf8_is_valid(d))
7,751✔
837
                return false;
838

839
        if (string_has_cc(d, NULL))
7,751✔
840
                return false;
841

842
        /* Colons are used as field separators, and hence not OK */
843
        if (strchr(d, ':'))
7,750✔
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,209✔
881
        /* Note that this function is also called by valid_shell(), any
882
         * changes must account for that. */
883

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

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

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

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

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

899
        /* Colons are used as field separators, and hence not OK */
900
        if (strchr(p, ':'))
5,193✔
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,163✔
918
        int r;
1,163✔
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,163✔
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,418✔
927
                if (!f && errno != ENOENT)
709✔
928
                        return -errno;
×
929
                if (f) {
930
                        r = safe_fgetc(f, /* ret= */ NULL);
653✔
931
                        if (r < 0)
653✔
932
                                return r;
933
                        if (r == 0) {
653✔
934
                                log_debug("Skipping setgroups(), /proc/self/gid_map is empty");
2✔
935
                                return 0;
936
                        }
937
                }
938

939
                _cleanup_free_ char *setgroups_content = NULL;
707✔
940
                r = read_one_line_file("/proc/self/setgroups", &setgroups_content);
707✔
941
                if (r < 0 && r != -ENOENT)
707✔
942
                        return r;
943
                if (r > 0 && streq(setgroups_content, "deny")) {
707✔
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,165✔
950
}
951

952
bool synthesize_nobody(void) {
95✔
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;
95✔
961

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

965
        return cache;
95✔
966
}
967

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

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

976
        return 0;
977
}
978

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

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

987
        return 0;
988
}
989

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

994
        errno = 0;
903✔
995
        if (putgrent(gr, stream) != 0)
903✔
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) {
878✔
1003
        assert(sg);
878✔
1004
        assert(stream);
878✔
1005

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

1010
        return 0;
1011
}
1012
#endif
1013

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

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

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

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

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

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

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

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

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

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

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

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

1068
int is_this_me(const char *username) {
97✔
1069
        uid_t uid;
97✔
1070
        int r;
97✔
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, /* flags= */ USER_CREDS_ALLOW_MISSING, NULL, &uid, NULL, NULL, NULL);
97✔
1075
        if (r < 0)
97✔
1076
                return r;
97✔
1077

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

1081
const char* get_home_root(void) {
13,702✔
1082
        /* For debug purposes allow overriding where we look for home dirs */
1083
        const char *e = secure_getenv("SYSTEMD_HOME_ROOT");
13,702✔
1084
        if (e && path_is_absolute(e) && path_is_normalized(e))
13,702✔
UNCOV
1085
                return e;
×
1086

1087
        return "/home";
1088
}
1089

1090
static size_t getpw_buffer_size(void) {
8,593✔
1091
        long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
8,593✔
1092
        return bufsize <= 0 ? 4096U : (size_t) bufsize;
8,593✔
1093
}
1094

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

1101
int getpwnam_malloc(const char *name, struct passwd **ret) {
4,620✔
1102
        size_t bufsize = getpw_buffer_size();
4,620✔
1103
        int r;
4,620✔
1104

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

1108
        if (isempty(name))
9,240✔
1109
                return -EINVAL;
1110

1111
        for (;;) {
4,620✔
1112
                _cleanup_free_ void *buf = NULL;
4,620✔
1113

1114
                /* Silence static analyzers */
1115
                assert(bufsize <= SIZE_MAX - ALIGN(sizeof(struct passwd)));
4,620✔
1116
                buf = malloc0(ALIGN(sizeof(struct passwd)) + bufsize);
4,620✔
1117
                if (!buf)
4,620✔
1118
                        return -ENOMEM;
1119

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

1129
                        return -ESRCH;
1130
                }
1131

UNCOV
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. */
UNCOV
1136
                if (errno_is_user_doesnt_exist(r))
×
1137
                        return -ESRCH;
1138
                if (r != ERANGE)
×
UNCOV
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,973✔
1148
        size_t bufsize = getpw_buffer_size();
3,973✔
1149
        int r;
3,973✔
1150

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

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

1157
                /* Silence static analyzers */
1158
                assert(bufsize <= SIZE_MAX - ALIGN(sizeof(struct passwd)));
3,973✔
1159
                buf = malloc0(ALIGN(sizeof(struct passwd)) + bufsize);
3,973✔
1160
                if (!buf)
3,973✔
1161
                        return -ENOMEM;
1162

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

1172
                        return -ESRCH;
1173
                }
1174

UNCOV
1175
                assert(r > 0);
×
1176

1177
                if (errno_is_user_doesnt_exist(r))
×
1178
                        return -ESRCH;
1179
                if (r != ERANGE)
×
UNCOV
1180
                        return -r;
×
1181

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

1188
static size_t getgr_buffer_size(void) {
13,790✔
1189
        long bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
13,790✔
1190
        return bufsize <= 0 ? 4096U : (size_t) bufsize;
13,790✔
1191
}
1192

1193
int getgrnam_malloc(const char *name, struct group **ret) {
7,928✔
1194
        size_t bufsize = getgr_buffer_size();
7,928✔
1195
        int r;
7,928✔
1196

1197
        if (isempty(name))
15,856✔
1198
                return -EINVAL;
1199

1200
        for (;;) {
7,928✔
1201
                _cleanup_free_ void *buf = NULL;
7,928✔
1202

1203
                /* Silence static analyzers */
1204
                assert(bufsize <= SIZE_MAX - ALIGN(sizeof(struct group)));
7,928✔
1205
                buf = malloc0(ALIGN(sizeof(struct group)) + bufsize);
7,928✔
1206
                if (!buf)
7,928✔
1207
                        return -ENOMEM;
1208

1209
                struct group *gr = NULL;
7,928✔
1210
                r = getgrnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct group)), bufsize, &gr);
7,928✔
1211
                if (r == 0) {
7,928✔
1212
                        if (gr) {
7,927✔
1213
                                if (ret)
7,654✔
1214
                                        *ret = TAKE_PTR(buf);
7,652✔
1215
                                return 0;
1216
                        }
1217

1218
                        return -ESRCH;
1219
                }
1220

1221
                assert(r > 0);
1✔
1222

1223
                if (errno_is_user_doesnt_exist(r))
1✔
1224
                        return -ESRCH;
1225
                if (r != ERANGE)
1✔
1226
                        return -r;
1✔
1227

UNCOV
1228
                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct group)))
×
1229
                        return -ENOMEM;
1230
                bufsize *= 2;
×
1231
        }
1232
}
1233

1234
int getgrgid_malloc(gid_t gid, struct group **ret) {
5,862✔
1235
        size_t bufsize = getgr_buffer_size();
5,862✔
1236
        int r;
5,862✔
1237

1238
        if (!gid_is_valid(gid))
5,862✔
1239
                return -EINVAL;
1240

1241
        for (;;) {
5,862✔
1242
                _cleanup_free_ void *buf = NULL;
5,862✔
1243

1244
                /* Silence static analyzers */
1245
                assert(bufsize <= SIZE_MAX - ALIGN(sizeof(struct group)));
5,862✔
1246
                buf = malloc0(ALIGN(sizeof(struct group)) + bufsize);
5,862✔
1247
                if (!buf)
5,862✔
1248
                        return -ENOMEM;
1249

1250
                struct group *gr = NULL;
5,862✔
1251
                r = getgrgid_r(gid, buf, (char*) buf + ALIGN(sizeof(struct group)), bufsize, &gr);
5,862✔
1252
                if (r == 0) {
5,862✔
1253
                        if (gr) {
5,862✔
1254
                                if (ret)
2,493✔
1255
                                        *ret = TAKE_PTR(buf);
2,493✔
1256
                                return 0;
1257
                        }
1258

1259
                        return -ESRCH;
1260
                }
1261

UNCOV
1262
                assert(r > 0);
×
1263

1264
                if (errno_is_user_doesnt_exist(r))
×
1265
                        return -ESRCH;
1266
                if (r != ERANGE)
×
UNCOV
1267
                        return -r;
×
1268

1269
                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct group)))
×
1270
                        return -ENOMEM;
1271
                bufsize *= 2;
×
1272
        }
1273
}
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