• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In
Build has been canceled!

systemd / systemd / 22081376732

16 Feb 2026 09:47PM UTC coverage: 72.633% (+0.2%) from 72.384%
22081376732

push

github

web-flow
report: enforce metric prefix (#40647)

Addressing
> we should enforce that metrics ids begin with the varlink service name

from #40633

48 of 59 new or added lines in 1 file covered. (81.36%)

3079 existing lines in 84 files now uncovered.

312905 of 430802 relevant lines covered (72.63%)

1136024.03 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) {
11,588,610✔
52

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

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

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

63
        return true;
64
}
65

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

70
        assert(s);
224,779✔
71

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

85
        if (!uid_is_valid(uid))
11,364✔
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,346✔
92
                *ret = uid;
10,816✔
93

94
        return 0;
95
}
96

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

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

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

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

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

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

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

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

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

146
        return uid_to_name(uid);
×
147
}
148

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

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

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

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

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

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

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

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

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

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

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

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

232
                return 0;
14,471✔
233
        }
234

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

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

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

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

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

289
        if (parse_uid(*username, &u) >= 0) {
4,204✔
290
                errno = 0;
200✔
291
                p = getpwuid(u);
200✔
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)
200✔
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,004✔
311
                p = getpwnam(*username);
4,004✔
312
        }
313
        if (!p) {
4,007✔
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,195✔
327
                return -EBADMSG;
328

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

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

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

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

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

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

352
        return 0;
353
}
354

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

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

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

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

368
                return 0;
5,480✔
369
        }
370

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

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

378
                return 0;
4✔
379
        }
380

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

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

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

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

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

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

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

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

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

427
                return r;
1✔
428
        }
429

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

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

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

440
        return 0;
441
}
442

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

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

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

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

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

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

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

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

556
        assert(ret);
556✔
557

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

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

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

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

598
        assert(ret);
14,755✔
599

600
        /* Take the user specified one */
601
        e = secure_getenv("HOME");
14,755✔
602
        if (e && path_is_valid(e) && path_is_absolute(e))
28,885✔
603
                goto found;
14,128✔
604

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

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

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

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

681
        if (uid_is_valid(uid))
582✔
682
                if (setresuid(uid, uid, uid) < 0)
582✔
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) {
182,935✔
716
        const char *i;
182,935✔
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 */
182,935✔
726
                return false;
727

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

732
        if (FLAGS_SET(flags, VALID_USER_RELAX)) {
182,757✔
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
178,933✔
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 */
178,933✔
753
                        return false;
754

755
                if (string_has_cc(u, NULL)) /* CC characters are just dangerous (and \n in particular is the
178,933✔
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
178,931✔
760
                                       * that. Slashes are special to file systems paths and user names
761
                                       * typically show up in the file system as home directories, hence
762
                                       * don't allow slashes. */
763
                        return false;
764

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

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

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

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

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

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

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

810
                for (i = u+1; *i; i++)
37,261✔
811
                        if (!ascii_isalpha(*i) &&
33,501✔
812
                            !ascii_isdigit(*i) &&
6,147✔
813
                            !IN_SET(*i, '_', '-'))
2,107✔
814
                                return false;
815

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

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

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

829
        return true;
830
}
831

832
bool valid_gecos(const char *d) {
7,880✔
833

834
        if (!d)
7,880✔
835
                return false;
836

837
        if (!utf8_is_valid(d))
7,879✔
838
                return false;
839

840
        if (string_has_cc(d, NULL))
7,879✔
841
                return false;
842

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

847
        return true;
848
}
849

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

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

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

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

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

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

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

878
        return mangled;
879
}
880

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

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

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

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

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

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

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

904
        return true;
905
}
906

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

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

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

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

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

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

950
        return RET_NERRNO(setgroups(size, list));
987✔
951
}
952

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

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

966
        return cache;
89✔
967
}
968

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

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

977
        return 0;
978
}
979

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

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

988
        return 0;
989
}
990

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

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

999
        return 0;
1000
}
1001

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

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

1011
        return 0;
1012
}
1013
#endif
1014

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

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

1024
        *pw = p;
1,202✔
1025
        return !!p;
1,202✔
1026
}
1027

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

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

1037
        *sp = s;
248✔
1038
        return !!s;
248✔
1039
}
1040

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

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

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

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

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

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

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

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

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

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

1082
const char* get_home_root(void) {
11,233✔
1083
        const char *e;
11,233✔
1084

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

1090
        return "/home";
1091
}
1092

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

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

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

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

1111
        if (isempty(name))
1,066✔
1112
                return -EINVAL;
1113

1114
        for (;;) {
533✔
1115
                _cleanup_free_ void *buf = NULL;
533✔
1116

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

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

1130
                        return -ESRCH;
1131
                }
1132

1133
                assert(r > 0);
×
1134

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

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

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

1152
        if (!uid_is_valid(uid))
3,720✔
1153
                return -EINVAL;
1154

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

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

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

1171
                        return -ESRCH;
1172
                }
1173

1174
                assert(r > 0);
×
1175

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

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

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

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

1196
        if (isempty(name))
4,696✔
1197
                return -EINVAL;
1198

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

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

1206
                struct group *gr = NULL;
2,348✔
1207
                r = getgrnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct group)), bufsize, &gr);
2,348✔
1208
                if (r == 0) {
2,348✔
1209
                        if (gr) {
2,347✔
1210
                                if (ret)
2,097✔
1211
                                        *ret = TAKE_PTR(buf);
2,095✔
1212
                                return 0;
2,097✔
1213
                        }
1214

1215
                        return -ESRCH;
1216
                }
1217

1218
                assert(r > 0);
1✔
1219

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

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

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

1235
        if (!gid_is_valid(gid))
5,744✔
1236
                return -EINVAL;
1237

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

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

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

1254
                        return -ESRCH;
1255
                }
1256

1257
                assert(r > 0);
×
1258

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

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