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

systemd / systemd / 28272947092

26 Jun 2026 08:38PM UTC coverage: 72.893% (+0.2%) from 72.703%
28272947092

push

github

poettering
sysupdate: Address review feedback on CheckNew varlink scaffolding

Follow-up to #42422:

 - Rename process_image() to context_process_image(), since it now
   operates on a Context object.
 - Use IN_SET() in image_type_can_sysupdate() instead of a switch.
 - Name the return parameters of context_list_components() ret_xyz, per
   our coding style.
 - Drop a redundant "else" after a return in vl_method_check_new().

9 of 11 new or added lines in 1 file covered. (81.82%)

12567 existing lines in 144 files now uncovered.

341026 of 467845 relevant lines covered (72.89%)

1339355.33 hits per line

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

86.04
/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) {
14,590,039✔
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))
14,590,039✔
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))
8,074,861✔
61
                return false;
18✔
62

63
        return true;
64
}
65

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

70
        assert(s);
261,032✔
71

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

85
        if (!uid_is_valid(uid))
13,442✔
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,425✔
92
                *ret = uid;
12,604✔
93

94
        return 0;
95
}
96

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

207
static int return_user_creds(
22,230✔
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;
22,230✔
219
        int r;
22,230✔
220

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

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

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

239
        if (ret_username)
22,230✔
240
                *ret_username = TAKE_PTR(s1);
11,887✔
241
        if (ret_uid)
22,230✔
242
                *ret_uid = uid;
12,592✔
243
        if (ret_gid)
22,230✔
244
                *ret_gid = gid;
2,271✔
245
        if (ret_home)
22,230✔
246
                *ret_home = TAKE_PTR(s2);
11,887✔
247
        if (ret_shell)
22,230✔
248
                *ret_shell = TAKE_PTR(s3);
11,887✔
249
        return 0;
250
}
251

252
static int synthesize_user_creds(
21,652✔
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);
21,652✔
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"))
21,652✔
264
                return return_user_creds("root", 0, 0,
27,524✔
265
                                         "/root",
266
                                         ret_shell ? default_root_shell(NULL) : NULL,
9,648✔
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,788✔
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,764✔
283
}
284

285
int get_user_creds(
22,239✔
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;
22,239✔
294
        _cleanup_free_ struct passwd *pw = NULL;
22,239✔
295
        int r;
22,239✔
296

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

300
        if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
22,239✔
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);
21,652✔
313
                if (r >= 0)
21,652✔
314
                        return 0;
315
                if (r != -ENOMEDIUM) /* not a username we can synthesize */
3,764✔
316
                        return r;
317
        }
318

319
        if (parse_uid(username, &u) >= 0) {
4,351✔
320
                r = getpwuid_malloc(u, &pw);
218✔
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)
218✔
326
                        username = pw->pw_name;
215✔
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. */
332
                        if (ret_username)
×
333
                                *ret_username = NULL;
×
334
                        if (ret_uid)
×
335
                                *ret_uid = u;
×
336
                        return 0;
337
                }
338
        } else
339
                r = getpwnam_malloc(username, &pw);
4,133✔
340

341
        if (r < 0) {
4,351✔
342
                /* If the user requested that we only synthesize as fallback, do so now */
343
                if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS) &&
9✔
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,342✔
351
                return -EBADMSG;
352

353
        if (ret_gid && !gid_is_valid(pw->pw_gid))
4,342✔
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,684✔
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,342✔
360
                ? NULL : pw->pw_dir;
8,684✔
361

362
        const char *s =
8,684✔
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,342✔
365
                ? NULL : pw->pw_shell;
8,684✔
366

367
        return return_user_creds(username, pw->pw_uid, pw->pw_gid, h, s,
4,342✔
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,914✔
372
        assert(groupname);
11,914✔
373

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

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

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

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

401
        assert(groupname);
11,914✔
402

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

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

425
        if (r < 0) {
5,563✔
426
                if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS) &&
1✔
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,562✔
433
                return -EBADMSG;
434

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

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

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

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

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

493
        FOREACH_ARRAY(i, list, size)
708✔
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,685✔
532
        size_t nresult = 0;
12,685✔
533

534
        assert(size1 == 0 || list1);
12,685✔
535
        assert(size2 == 0 || list2);
12,685✔
536
        assert(ret);
12,685✔
537

538
        if (size2 > INT_MAX - size1)
12,685✔
539
                return -ENOBUFS;
540

541
        gid_t *buf = new(gid_t, size1 + size2);
12,685✔
542
        if (!buf)
12,685✔
543
                return -ENOMEM;
544

545
        /* Duplicates need to be skipped on merging, otherwise they'll be passed on and stored in the kernel. */
546
        for (size_t i = 0; i < size1; i++)
12,726✔
547
                if (!gid_list_has(buf, nresult, list1[i]))
41✔
548
                        buf[nresult++] = list1[i];
40✔
549
        for (size_t i = 0; i < size2; i++)
13,142✔
550
                if (!gid_list_has(buf, nresult, list2[i]))
457✔
551
                        buf[nresult++] = list2[i];
442✔
552
        *ret = buf;
12,685✔
553
        return (int)nresult;
12,685✔
554
}
555

556
int getgroups_alloc(gid_t **ret) {
628✔
557
        int ngroups = 8;
628✔
558

559
        assert(ret);
628✔
560

UNCOV
561
        for (unsigned attempt = 0;;) {
×
562
                _cleanup_free_ gid_t *p = NULL;
468✔
563

564
                p = new(gid_t, ngroups);
628✔
565
                if (!p)
628✔
566
                        return -ENOMEM;
567

568
                ngroups = getgroups(ngroups, p);
628✔
569
                if (ngroups > 0) {
628✔
570
                        *ret = TAKE_PTR(p);
468✔
571
                        return ngroups;
468✔
572
                }
573
                if (ngroups == 0)
160✔
574
                        break;
UNCOV
575
                if (errno != EINVAL)
×
576
                        return -errno;
×
577

578
                /* Give up eventually */
UNCOV
579
                if (attempt++ > 10)
×
580
                        return -EINVAL;
581

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

591
        *ret = NULL;
160✔
592
        return 0;
160✔
593
}
594

595
int get_home_dir(char **ret) {
17,487✔
596
        _cleanup_free_ struct passwd *p = NULL;
17,487✔
597
        const char *e;
17,487✔
598
        uid_t u;
17,487✔
599
        int r;
17,487✔
600

601
        assert(ret);
17,487✔
602

603
        /* Take the user specified one */
604
        e = secure_getenv("HOME");
17,487✔
605
        if (e && path_is_valid(e) && path_is_absolute(e))
34,280✔
606
                goto found;
16,791✔
607

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

619
        /* Check the database... */
UNCOV
620
        r = getpwuid_malloc(u, &p);
×
621
        if (r < 0)
×
622
                return r;
623

UNCOV
624
        e = p->pw_dir;
×
625
        if (!path_is_valid(e) || !path_is_absolute(e))
17,487✔
626
                return -EINVAL;
627

UNCOV
628
 found:
×
629
        return path_simplify_alloc(e, ret);
17,487✔
630
}
631

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

638
        assert(ret);
6✔
639

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

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

656
        /* Check the database... */
UNCOV
657
        r = getpwuid_malloc(u, &p);
×
658
        if (r < 0)
×
659
                return r;
660

UNCOV
661
        e = p->pw_shell;
×
662
        if (!path_is_valid(e) || !path_is_absolute(e))
6✔
663
                return -EINVAL;
664

UNCOV
665
 found:
×
666
        return path_simplify_alloc(e, ret);
6✔
667
}
668

669
int fully_set_uid_gid(uid_t uid, gid_t gid, const gid_t supplementary_gids[], size_t n_supplementary_gids) {
669✔
670
        int r;
669✔
671

672
        assert(supplementary_gids || n_supplementary_gids == 0);
669✔
673

674
        /* Sets all UIDs and all GIDs to the specified ones. Drops all auxiliary GIDs */
675

676
        r = maybe_setgroups(n_supplementary_gids, supplementary_gids);
669✔
677
        if (r < 0)
669✔
678
                return r;
679

680
        if (gid_is_valid(gid))
669✔
681
                if (setresgid(gid, gid, gid) < 0)
669✔
682
                        return -errno;
2✔
683

684
        if (uid_is_valid(uid))
667✔
685
                if (setresuid(uid, uid, uid) < 0)
667✔
UNCOV
686
                        return -errno;
×
687

688
        return 0;
689
}
690

691
int take_etc_passwd_lock(const char *root) {
317✔
692
        int r;
317✔
693

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

701
        _cleanup_free_ char *path = path_join(root, ETC_PASSWD_LOCK_PATH);
634✔
702
        if (!path)
317✔
UNCOV
703
                return log_oom_debug();
×
704

705
        (void) mkdir_parents(path, 0755);
317✔
706

707
        _cleanup_close_ int fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
634✔
708
        if (fd < 0)
317✔
UNCOV
709
                return log_debug_errno(errno, "Cannot open %s: %m", path);
×
710

711
        r = unposix_lock(fd, LOCK_EX);
317✔
712
        if (r < 0)
317✔
UNCOV
713
                return log_debug_errno(r, "Locking %s failed: %m", path);
×
714

715
        return TAKE_FD(fd);
716
}
717

718
bool valid_user_group_name(const char *u, ValidUserFlags flags) {
214,053✔
719
        const char *i;
214,053✔
720

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

728
        if (isempty(u)) /* An empty user name is never valid */
214,053✔
729
                return false;
730

731
        if (parse_uid(u, NULL) >= 0) /* Something that parses as numeric UID string is valid exactly when the
214,037✔
732
                                      * flag for it is set */
733
                return FLAGS_SET(flags, VALID_USER_ALLOW_NUMERIC);
401✔
734

735
        if (FLAGS_SET(flags, VALID_USER_RELAX)) {
213,636✔
736

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

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

755
                if (!utf8_is_valid(u)) /* We want to synthesize JSON from this, hence insist on UTF-8 */
206,964✔
756
                        return false;
757

758
                if (string_has_cc(u, NULL)) /* CC characters are just dangerous (and \n in particular is the
206,964✔
759
                                             * record separator in /etc/passwd), so we can't allow that. */
760
                        return false;
761

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

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

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

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

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

792
                /* Note that we make no restrictions on the length in relaxed mode! */
793
        } else {
794
                long sz;
6,672✔
795
                size_t l;
6,672✔
796

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

808
                if (!ascii_isalpha(u[0]) &&
6,672✔
809
                    u[0] != '_')
810
                        return false;
811

812
                for (i = u+1; *i; i++)
76,589✔
813
                        if (!ascii_isalpha(*i) &&
70,018✔
814
                            !ascii_isdigit(*i) &&
10,518✔
815
                            !IN_SET(*i, '_', '-'))
4,938✔
816
                                return false;
817

818
                l = i - u;
6,571✔
819

820
                sz = sysconf(_SC_LOGIN_NAME_MAX);
6,571✔
821
                assert_se(sz > 0);
6,571✔
822

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

831
        return true;
832
}
833

834
bool valid_gecos(const char *d) {
7,893✔
835

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

839
        if (!utf8_is_valid(d))
7,892✔
840
                return false;
841

842
        if (string_has_cc(d, NULL))
7,892✔
843
                return false;
844

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

849
        return true;
850
}
851

852
char* mangle_gecos(const char *d) {
8✔
853
        char *mangled;
8✔
854

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

859
        mangled = strdup(d);
8✔
860
        if (!mangled)
8✔
861
                return NULL;
862

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

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

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

877
                i += len - 1;
46✔
878
        }
879

880
        return mangled;
881
}
882

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

887
        if (isempty(p))
5,301✔
888
                return false;
889

890
        if (!utf8_is_valid(p))
5,297✔
891
                return false;
892

893
        if (string_has_cc(p, NULL))
5,297✔
894
                return false;
895

896
        if (!path_is_absolute(p))
5,295✔
897
                return false;
898

899
        if (!path_is_normalized(p))
5,289✔
900
                return false;
901

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

906
        return true;
907
}
908

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

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

920
int maybe_setgroups(size_t size, const gid_t *list) {
1,143✔
921
        int r;
1,143✔
922

923
        /* Check if setgroups is allowed before we try to drop all the auxiliary groups */
924
        if (size == 0) { /* Dropping all aux groups? */
1,143✔
925

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

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

952
        return RET_NERRNO(setgroups(size, list));
1,145✔
953
}
954

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

965
        if (cache < 0)
95✔
966
                cache = access("/etc/systemd/dont-synthesize-nobody", F_OK) < 0;
67✔
967

968
        return cache;
95✔
969
}
970

971
int putpwent_sane(const struct passwd *pw, FILE *stream) {
444✔
972
        assert(pw);
444✔
973
        assert(stream);
444✔
974

975
        errno = 0;
444✔
976
        if (putpwent(pw, stream) != 0)
444✔
UNCOV
977
                return errno_or_else(EIO);
×
978

979
        return 0;
980
}
981

982
int putspent_sane(const struct spwd *sp, FILE *stream) {
414✔
983
        assert(sp);
414✔
984
        assert(stream);
414✔
985

986
        errno = 0;
414✔
987
        if (putspent(sp, stream) != 0)
414✔
UNCOV
988
                return errno_or_else(EIO);
×
989

990
        return 0;
991
}
992

993
int putgrent_sane(const struct group *gr, FILE *stream) {
897✔
994
        assert(gr);
897✔
995
        assert(stream);
897✔
996

997
        errno = 0;
897✔
998
        if (putgrent(gr, stream) != 0)
897✔
UNCOV
999
                return errno_or_else(EIO);
×
1000

1001
        return 0;
1002
}
1003

1004
#if ENABLE_GSHADOW
1005
int putsgent_sane(const struct sgrp *sg, FILE *stream) {
872✔
1006
        assert(sg);
872✔
1007
        assert(stream);
872✔
1008

1009
        errno = 0;
872✔
1010
        if (putsgent(sg, stream) != 0)
872✔
UNCOV
1011
                return errno_or_else(EIO);
×
1012

1013
        return 0;
1014
}
1015
#endif
1016

1017
int fgetpwent_sane(FILE *stream, struct passwd **pw) {
1,247✔
1018
        assert(stream);
1,247✔
1019
        assert(pw);
1,247✔
1020

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

1026
        *pw = p;
1,247✔
1027
        return !!p;
1,247✔
1028
}
1029

1030
int fgetspent_sane(FILE *stream, struct spwd **sp) {
260✔
1031
        assert(stream);
260✔
1032
        assert(sp);
260✔
1033

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

1039
        *sp = s;
260✔
1040
        return !!s;
260✔
1041
}
1042

1043
int fgetgrent_sane(FILE *stream, struct group **gr) {
2,496✔
1044
        assert(stream);
2,496✔
1045
        assert(gr);
2,496✔
1046

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

1052
        *gr = g;
2,496✔
1053
        return !!g;
2,496✔
1054
}
1055

1056
#if ENABLE_GSHADOW
1057
int fgetsgent_sane(FILE *stream, struct sgrp **sg) {
359✔
1058
        assert(stream);
359✔
1059
        assert(sg);
359✔
1060

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

1066
        *sg = s;
359✔
1067
        return !!s;
359✔
1068
}
1069
#endif
1070

1071
int is_this_me(const char *username) {
99✔
1072
        uid_t uid;
99✔
1073
        int r;
99✔
1074

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

1077
        r = get_user_creds(username, /* flags= */ USER_CREDS_ALLOW_MISSING, NULL, &uid, NULL, NULL, NULL);
99✔
1078
        if (r < 0)
99✔
1079
                return r;
99✔
1080

1081
        return uid == getuid();
98✔
1082
}
1083

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

1090
        return "/home";
1091
}
1092

1093
static size_t getpw_buffer_size(void) {
8,677✔
1094
        long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
8,677✔
1095
        return bufsize <= 0 ? 4096U : (size_t) bufsize;
8,677✔
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) {
4,695✔
1105
        size_t bufsize = getpw_buffer_size();
4,695✔
1106
        int r;
4,695✔
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))
9,390✔
1112
                return -EINVAL;
1113

1114
        for (;;) {
4,695✔
1115
                _cleanup_free_ void *buf = NULL;
4,695✔
1116

1117
                /* Silence static analyzers */
1118
                assert(bufsize <= SIZE_MAX - ALIGN(sizeof(struct passwd)));
4,695✔
1119
                buf = malloc0(ALIGN(sizeof(struct passwd)) + bufsize);
4,695✔
1120
                if (!buf)
4,695✔
1121
                        return -ENOMEM;
1122

1123
                struct passwd *pw = NULL;
4,695✔
1124
                r = getpwnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct passwd)), bufsize, &pw);
4,695✔
1125
                if (r == 0) {
4,695✔
1126
                        if (pw) {
4,695✔
1127
                                if (ret)
4,416✔
1128
                                        *ret = TAKE_PTR(buf);
4,416✔
1129
                                return 0;
1130
                        }
1131

1132
                        return -ESRCH;
1133
                }
1134

UNCOV
1135
                assert(r > 0);
×
1136

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

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

1150
int getpwuid_malloc(uid_t uid, struct passwd **ret) {
3,982✔
1151
        size_t bufsize = getpw_buffer_size();
3,982✔
1152
        int r;
3,982✔
1153

1154
        if (!uid_is_valid(uid))
3,982✔
1155
                return -EINVAL;
1156

1157
        for (;;) {
3,982✔
1158
                _cleanup_free_ void *buf = NULL;
3,982✔
1159

1160
                /* Silence static analyzers */
1161
                assert(bufsize <= SIZE_MAX - ALIGN(sizeof(struct passwd)));
3,982✔
1162
                buf = malloc0(ALIGN(sizeof(struct passwd)) + bufsize);
3,982✔
1163
                if (!buf)
3,982✔
1164
                        return -ENOMEM;
1165

1166
                struct passwd *pw = NULL;
3,982✔
1167
                r = getpwuid_r(uid, buf, (char*) buf + ALIGN(sizeof(struct passwd)), bufsize, &pw);
3,982✔
1168
                if (r == 0) {
3,982✔
1169
                        if (pw) {
3,982✔
1170
                                if (ret)
666✔
1171
                                        *ret = TAKE_PTR(buf);
666✔
1172
                                return 0;
1173
                        }
1174

1175
                        return -ESRCH;
1176
                }
1177

UNCOV
1178
                assert(r > 0);
×
1179

1180
                if (errno_is_user_doesnt_exist(r))
×
1181
                        return -ESRCH;
1182
                if (r != ERANGE)
×
UNCOV
1183
                        return -r;
×
1184

UNCOV
1185
                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct passwd)))
×
1186
                        return -ENOMEM;
UNCOV
1187
                bufsize *= 2;
×
1188
        }
1189
}
1190

1191
static size_t getgr_buffer_size(void) {
14,176✔
1192
        long bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
14,176✔
1193
        return bufsize <= 0 ? 4096U : (size_t) bufsize;
14,176✔
1194
}
1195

1196
int getgrnam_malloc(const char *name, struct group **ret) {
8,234✔
1197
        size_t bufsize = getgr_buffer_size();
8,234✔
1198
        int r;
8,234✔
1199

1200
        if (isempty(name))
16,468✔
1201
                return -EINVAL;
1202

1203
        for (;;) {
8,234✔
1204
                _cleanup_free_ void *buf = NULL;
8,234✔
1205

1206
                /* Silence static analyzers */
1207
                assert(bufsize <= SIZE_MAX - ALIGN(sizeof(struct group)));
8,234✔
1208
                buf = malloc0(ALIGN(sizeof(struct group)) + bufsize);
8,234✔
1209
                if (!buf)
8,234✔
1210
                        return -ENOMEM;
1211

1212
                struct group *gr = NULL;
8,234✔
1213
                r = getgrnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct group)), bufsize, &gr);
8,234✔
1214
                if (r == 0) {
8,234✔
1215
                        if (gr) {
8,233✔
1216
                                if (ret)
7,959✔
1217
                                        *ret = TAKE_PTR(buf);
7,957✔
1218
                                return 0;
1219
                        }
1220

1221
                        return -ESRCH;
1222
                }
1223

1224
                assert(r > 0);
1✔
1225

1226
                if (errno_is_user_doesnt_exist(r))
1✔
1227
                        return -ESRCH;
1228
                if (r != ERANGE)
1✔
1229
                        return -r;
1✔
1230

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

1237
int getgrgid_malloc(gid_t gid, struct group **ret) {
5,942✔
1238
        size_t bufsize = getgr_buffer_size();
5,942✔
1239
        int r;
5,942✔
1240

1241
        if (!gid_is_valid(gid))
5,942✔
1242
                return -EINVAL;
1243

1244
        for (;;) {
5,942✔
1245
                _cleanup_free_ void *buf = NULL;
5,942✔
1246

1247
                /* Silence static analyzers */
1248
                assert(bufsize <= SIZE_MAX - ALIGN(sizeof(struct group)));
5,942✔
1249
                buf = malloc0(ALIGN(sizeof(struct group)) + bufsize);
5,942✔
1250
                if (!buf)
5,942✔
1251
                        return -ENOMEM;
1252

1253
                struct group *gr = NULL;
5,942✔
1254
                r = getgrgid_r(gid, buf, (char*) buf + ALIGN(sizeof(struct group)), bufsize, &gr);
5,942✔
1255
                if (r == 0) {
5,942✔
1256
                        if (gr) {
5,942✔
1257
                                if (ret)
2,573✔
1258
                                        *ret = TAKE_PTR(buf);
2,573✔
1259
                                return 0;
1260
                        }
1261

1262
                        return -ESRCH;
1263
                }
1264

UNCOV
1265
                assert(r > 0);
×
1266

1267
                if (errno_is_user_doesnt_exist(r))
×
1268
                        return -ESRCH;
1269
                if (r != ERANGE)
×
UNCOV
1270
                        return -r;
×
1271

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