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

systemd / systemd / 13125386108

03 Feb 2025 10:14PM UTC coverage: 71.734% (-0.06%) from 71.79%
13125386108

push

github

web-flow
docs: Update CPE fields in package metadata spec (#36251)

Update osCPE field example to use cpe 2.3 format, as is in active use by
AmazonLinux 2023 for example.

Add appCPE field example to document the upstream application CPE for
the applicable CVEs. Often distribution source package names are
different from the upstream CPE. For example adding/removing "lib"
prefix, or adding version stream "-3" suffix. This typically leads to
guessing or fuzzy matching. Adding appCPE in such cases can help to
disambiguate (or collate) correct application CPEs; especially beyond
the lifetime of osCPE support timeframes. This also will help a lot with
packaging multiple alternative source packages of the same software
(e.g. nginx-full nginx-core); different version streams (e.g.
openssl-1.1, openssl-3); or alternative builds of upstream software with
largely the same CVEs with multiple version streams (e.g.
openjdk-{22,17,11..}, corretto-{22,17,11..}, temurin-{22,17,11..}, etc).

292625 of 407928 relevant lines covered (71.73%)

713708.1 hits per line

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

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

3
#include <errno.h>
4
#include <fcntl.h>
5
#include <stddef.h>
6
#include <stdint.h>
7
#include <stdio.h>
8
#include <stdlib.h>
9
#include <sys/file.h>
10
#include <sys/stat.h>
11
#include <unistd.h>
12
#include <utmpx.h>
13

14
#include "sd-messages.h"
15

16
#include "alloc-util.h"
17
#include "chase.h"
18
#include "errno-util.h"
19
#include "fd-util.h"
20
#include "fileio.h"
21
#include "format-util.h"
22
#include "lock-util.h"
23
#include "macro.h"
24
#include "mkdir.h"
25
#include "parse-util.h"
26
#include "path-util.h"
27
#include "random-util.h"
28
#include "string-util.h"
29
#include "strv.h"
30
#include "terminal-util.h"
31
#include "user-util.h"
32
#include "utf8.h"
33

34
bool uid_is_valid(uid_t uid) {
10,225,503✔
35

36
        /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.436. */
37

38
        /* Some libc APIs use UID_INVALID as special placeholder */
39
        if (uid == (uid_t) UINT32_C(0xFFFFFFFF))
10,225,503✔
40
                return false;
41

42
        /* A long time ago UIDs where 16 bit, hence explicitly avoid the 16-bit -1 too */
43
        if (uid == (uid_t) UINT32_C(0xFFFF))
4,905,370✔
44
                return false;
18✔
45

46
        return true;
47
}
48

49
int parse_uid(const char *s, uid_t *ret) {
234,394✔
50
        uint32_t uid = 0;
234,394✔
51
        int r;
234,394✔
52

53
        assert(s);
234,394✔
54

55
        assert_cc(sizeof(uid_t) == sizeof(uint32_t));
234,394✔
56

57
        /* We are very strict when parsing UIDs, and prohibit +/- as prefix, leading zero as prefix, and
58
         * whitespace. We do this, since this call is often used in a context where we parse things as UID
59
         * first, and if that doesn't work we fall back to NSS. Thus we really want to make sure that UIDs
60
         * are parsed as UIDs only if they really really look like UIDs. */
61
        r = safe_atou32_full(s, 10
234,394✔
62
                             | SAFE_ATO_REFUSE_PLUS_MINUS
63
                             | SAFE_ATO_REFUSE_LEADING_ZERO
64
                             | SAFE_ATO_REFUSE_LEADING_WHITESPACE, &uid);
65
        if (r < 0)
234,394✔
66
                return r;
234,394✔
67

68
        if (!uid_is_valid(uid))
7,238✔
69
                return -ENXIO; /* we return ENXIO instead of EINVAL
70
                                * here, to make it easy to distinguish
71
                                * invalid numeric uids from invalid
72
                                * strings. */
73

74
        if (ret)
7,221✔
75
                *ret = uid;
6,564✔
76

77
        return 0;
78
}
79

80
int parse_uid_range(const char *s, uid_t *ret_lower, uid_t *ret_upper) {
35✔
81
        _cleanup_free_ char *word = NULL;
35✔
82
        uid_t l, u;
35✔
83
        int r;
35✔
84

85
        assert(s);
35✔
86
        assert(ret_lower);
35✔
87
        assert(ret_upper);
35✔
88

89
        r = extract_first_word(&s, &word, "-", EXTRACT_DONT_COALESCE_SEPARATORS);
35✔
90
        if (r < 0)
35✔
91
                return r;
92
        if (r == 0)
35✔
93
                return -EINVAL;
94

95
        r = parse_uid(word, &l);
35✔
96
        if (r < 0)
35✔
97
                return r;
98

99
        /* Check for the upper bound and extract it if needed */
100
        if (!s)
22✔
101
                /* Single number with no dash. */
102
                u = l;
7✔
103
        else if (!*s)
15✔
104
                /* Trailing dash is an error. */
105
                return -EINVAL;
106
        else {
107
                r = parse_uid(s, &u);
15✔
108
                if (r < 0)
15✔
109
                        return r;
110

111
                if (l > u)
10✔
112
                        return -EINVAL;
113
        }
114

115
        *ret_lower = l;
16✔
116
        *ret_upper = u;
16✔
117
        return 0;
16✔
118
}
119

120
char* getlogname_malloc(void) {
×
121
        uid_t uid;
×
122
        struct stat st;
×
123

124
        if (isatty_safe(STDIN_FILENO) && fstat(STDIN_FILENO, &st) >= 0)
×
125
                uid = st.st_uid;
×
126
        else
127
                uid = getuid();
×
128

129
        return uid_to_name(uid);
×
130
}
131

132
char* getusername_malloc(void) {
210✔
133
        const char *e;
210✔
134

135
        e = secure_getenv("USER");
210✔
136
        if (e)
210✔
137
                return strdup(e);
199✔
138

139
        return uid_to_name(getuid());
11✔
140
}
141

142
bool is_nologin_shell(const char *shell) {
2,356✔
143
        return PATH_IN_SET(shell,
2,356✔
144
                           /* 'nologin' is the friendliest way to disable logins for a user account. It prints a nice
145
                            * message and exits. Different distributions place the binary at different places though,
146
                            * hence let's list them all. */
147
                           "/bin/nologin",
148
                           "/sbin/nologin",
149
                           "/usr/bin/nologin",
150
                           "/usr/sbin/nologin",
151
                           /* 'true' and 'false' work too for the same purpose, but are less friendly as they don't do
152
                            * any message printing. Different distributions place the binary at various places but at
153
                            * least not in the 'sbin' directory. */
154
                           "/bin/false",
155
                           "/usr/bin/false",
156
                           "/bin/true",
157
                           "/usr/bin/true");
158
}
159

160
const char* default_root_shell_at(int rfd) {
8,601✔
161
        /* We want to use the preferred shell, i.e. DEFAULT_USER_SHELL, which usually
162
         * will be /bin/bash. Fall back to /bin/sh if DEFAULT_USER_SHELL is not found,
163
         * or any access errors. */
164

165
        assert(rfd >= 0 || rfd == AT_FDCWD);
8,601✔
166

167
        int r = chaseat(rfd, DEFAULT_USER_SHELL, CHASE_AT_RESOLVE_IN_ROOT, NULL, NULL);
8,601✔
168
        if (r < 0 && r != -ENOENT)
8,601✔
169
                log_debug_errno(r, "Failed to look up shell '%s': %m", DEFAULT_USER_SHELL);
×
170
        if (r > 0)
8,601✔
171
                return DEFAULT_USER_SHELL;
8,585✔
172

173
        return "/bin/sh";
174
}
175

176
const char* default_root_shell(const char *root) {
8,595✔
177
        _cleanup_close_ int rfd = -EBADF;
8,595✔
178

179
        rfd = open(empty_to_root(root), O_CLOEXEC | O_DIRECTORY | O_PATH);
8,605✔
180
        if (rfd < 0)
8,595✔
181
                return "/bin/sh";
182

183
        return default_root_shell_at(rfd);
8,595✔
184
}
185

186
static int synthesize_user_creds(
23,608✔
187
                const char **username,
188
                uid_t *ret_uid, gid_t *ret_gid,
189
                const char **ret_home,
190
                const char **ret_shell,
191
                UserCredsFlags flags) {
192

193
        assert(username);
23,608✔
194
        assert(*username);
23,608✔
195

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

199
        if (STR_IN_SET(*username, "root", "0")) {
23,608✔
200
                *username = "root";
18,409✔
201

202
                if (ret_uid)
18,409✔
203
                        *ret_uid = 0;
9,930✔
204
                if (ret_gid)
18,409✔
205
                        *ret_gid = 0;
101✔
206
                if (ret_home)
18,409✔
207
                        *ret_home = "/root";
8,580✔
208
                if (ret_shell)
18,409✔
209
                        *ret_shell = default_root_shell(NULL);
8,580✔
210

211
                return 0;
18,409✔
212
        }
213

214
        if (STR_IN_SET(*username, NOBODY_USER_NAME, "65534") &&
5,207✔
215
            synthesize_nobody()) {
8✔
216
                *username = NOBODY_USER_NAME;
8✔
217

218
                if (ret_uid)
8✔
219
                        *ret_uid = UID_NOBODY;
8✔
220
                if (ret_gid)
8✔
221
                        *ret_gid = GID_NOBODY;
4✔
222
                if (ret_home)
8✔
223
                        *ret_home = FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : "/";
3✔
224
                if (ret_shell)
8✔
225
                        *ret_shell = FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : NOLOGIN;
3✔
226

227
                return 0;
8✔
228
        }
229

230
        return -ENOMEDIUM;
5,191✔
231
}
232

233
int get_user_creds(
23,609✔
234
                const char **username,
235
                uid_t *ret_uid, gid_t *ret_gid,
236
                const char **ret_home,
237
                const char **ret_shell,
238
                UserCredsFlags flags) {
239

240
        bool patch_username = false;
23,609✔
241
        uid_t u = UID_INVALID;
23,609✔
242
        struct passwd *p;
23,609✔
243
        int r;
23,609✔
244

245
        assert(username);
23,609✔
246
        assert(*username);
23,609✔
247
        assert((ret_home || ret_shell) || !(flags & (USER_CREDS_SUPPRESS_PLACEHOLDER|USER_CREDS_CLEAN)));
23,609✔
248

249
        if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
23,609✔
250
            (!ret_home && !ret_shell)) {
251

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

261
                r = synthesize_user_creds(username, ret_uid, ret_gid, ret_home, ret_shell, flags);
23,608✔
262
                if (r >= 0)
23,608✔
263
                        return 0;
23,609✔
264
                if (r != -ENOMEDIUM) /* not a username we can synthesize */
5,191✔
265
                        return r;
266
        }
267

268
        if (parse_uid(*username, &u) >= 0) {
5,192✔
269
                errno = 0;
110✔
270
                p = getpwuid(u);
110✔
271

272
                /* If there are multiple users with the same id, make sure to leave $USER to the configured value
273
                 * instead of the first occurrence in the database. However if the uid was configured by a numeric uid,
274
                 * then let's pick the real username from /etc/passwd. */
275
                if (p)
110✔
276
                        patch_username = true;
277
                else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING) && !ret_gid && !ret_home && !ret_shell) {
3✔
278

279
                        /* If the specified user is a numeric UID and it isn't in the user database, and the caller
280
                         * passed USER_CREDS_ALLOW_MISSING and was only interested in the UID, then just return that
281
                         * and don't complain. */
282

283
                        if (ret_uid)
×
284
                                *ret_uid = u;
×
285

286
                        return 0;
×
287
                }
288
        } else {
289
                errno = 0;
5,082✔
290
                p = getpwnam(*username);
5,082✔
291
        }
292
        if (!p) {
5,085✔
293
                /* getpwnam() may fail with ENOENT if /etc/passwd is missing.
294
                 * For us that is equivalent to the name not being defined. */
295
                r = IN_SET(errno, 0, ENOENT) ? -ESRCH : -errno;
11✔
296

297
                /* If the user requested that we only synthesize as fallback, do so now */
298
                if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS))
11✔
299
                        if (synthesize_user_creds(username, ret_uid, ret_gid, ret_home, ret_shell, flags) >= 0)
×
300
                                return 0;
301

302
                return r;
11✔
303
        }
304

305
        if (ret_uid && !uid_is_valid(p->pw_uid))
5,181✔
306
                return -EBADMSG;
307

308
        if (ret_gid && !gid_is_valid(p->pw_gid))
5,181✔
309
                return -EBADMSG;
310

311
        if (ret_uid)
5,181✔
312
                *ret_uid = p->pw_uid;
5,181✔
313

314
        if (ret_gid)
5,181✔
315
                *ret_gid = p->pw_gid;
2,469✔
316

317
        if (ret_home)
5,181✔
318
                /* Note: we don't insist on normalized paths, since there are setups that have /./ in the path */
319
                *ret_home = (FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) && empty_or_root(p->pw_dir)) ||
1✔
320
                            (FLAGS_SET(flags, USER_CREDS_CLEAN) && (!path_is_valid(p->pw_dir) || !path_is_absolute(p->pw_dir)))
4,900✔
321
                            ? NULL : p->pw_dir;
4,900✔
322

323
        if (ret_shell)
5,181✔
324
                *ret_shell = (FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) && shell_is_placeholder(p->pw_shell)) ||
1✔
325
                             (FLAGS_SET(flags, USER_CREDS_CLEAN) && (!path_is_valid(p->pw_shell) || !path_is_absolute(p->pw_shell)))
4,900✔
326
                             ? NULL : p->pw_shell;
4,900✔
327

328
        if (patch_username)
5,181✔
329
                *username = p->pw_name;
107✔
330

331
        return 0;
332
}
333

334
static int synthesize_group_creds(
16,772✔
335
                const char **groupname,
336
                gid_t *ret_gid) {
337

338
        assert(groupname);
16,772✔
339
        assert(*groupname);
16,772✔
340

341
        if (STR_IN_SET(*groupname, "root", "0")) {
16,772✔
342
                *groupname = "root";
7,436✔
343

344
                if (ret_gid)
7,436✔
345
                        *ret_gid = 0;
7,436✔
346

347
                return 0;
7,436✔
348
        }
349

350
        if (STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534") &&
9,340✔
351
            synthesize_nobody()) {
4✔
352
                *groupname = NOBODY_GROUP_NAME;
4✔
353

354
                if (ret_gid)
4✔
355
                        *ret_gid = GID_NOBODY;
4✔
356

357
                return 0;
4✔
358
        }
359

360
        return -ENOMEDIUM;
9,332✔
361
}
362

363
int get_group_creds(const char **groupname, gid_t *ret_gid, UserCredsFlags flags) {
16,772✔
364
        bool patch_groupname = false;
16,772✔
365
        struct group *g;
16,772✔
366
        gid_t id;
16,772✔
367
        int r;
16,772✔
368

369
        assert(groupname);
16,772✔
370
        assert(*groupname);
16,772✔
371

372
        if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) {
16,772✔
373
                r = synthesize_group_creds(groupname, ret_gid);
16,772✔
374
                if (r >= 0)
16,772✔
375
                        return 0;
16,772✔
376
                if (r != -ENOMEDIUM) /* not a groupname we can synthesize */
9,332✔
377
                        return r;
378
        }
379

380
        if (parse_gid(*groupname, &id) >= 0) {
9,332✔
381
                errno = 0;
×
382
                g = getgrgid(id);
×
383

384
                if (g)
×
385
                        patch_groupname = true;
386
                else if (FLAGS_SET(flags, USER_CREDS_ALLOW_MISSING)) {
×
387
                        if (ret_gid)
×
388
                                *ret_gid = id;
×
389

390
                        return 0;
×
391
                }
392
        } else {
393
                errno = 0;
9,332✔
394
                g = getgrnam(*groupname);
9,332✔
395
        }
396

397
        if (!g) {
9,332✔
398
                /* getgrnam() may fail with ENOENT if /etc/group is missing.
399
                 * For us that is equivalent to the name not being defined. */
400
                r = IN_SET(errno, 0, ENOENT) ? -ESRCH : -errno;
4✔
401

402
                if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS))
4✔
403
                        if (synthesize_group_creds(groupname, ret_gid) >= 0)
×
404
                                return 0;
405

406
                return r;
4✔
407
        }
408

409
        if (ret_gid) {
9,328✔
410
                if (!gid_is_valid(g->gr_gid))
9,328✔
411
                        return -EBADMSG;
412

413
                *ret_gid = g->gr_gid;
9,328✔
414
        }
415

416
        if (patch_groupname)
9,328✔
417
                *groupname = g->gr_name;
×
418

419
        return 0;
420
}
421

422
char* uid_to_name(uid_t uid) {
327✔
423
        char *ret;
327✔
424
        int r;
327✔
425

426
        /* Shortcut things to avoid NSS lookups */
427
        if (uid == 0)
327✔
428
                return strdup("root");
327✔
429
        if (uid == UID_NOBODY && synthesize_nobody())
179✔
430
                return strdup(NOBODY_USER_NAME);
1✔
431

432
        if (uid_is_valid(uid)) {
178✔
433
                _cleanup_free_ struct passwd *pw = NULL;
176✔
434

435
                r = getpwuid_malloc(uid, &pw);
176✔
436
                if (r >= 0)
176✔
437
                        return strdup(pw->pw_name);
176✔
438
        }
439

440
        if (asprintf(&ret, UID_FMT, uid) < 0)
2✔
441
                return NULL;
442

443
        return ret;
2✔
444
}
445

446
char* gid_to_name(gid_t gid) {
89✔
447
        char *ret;
89✔
448
        int r;
89✔
449

450
        if (gid == 0)
89✔
451
                return strdup("root");
89✔
452
        if (gid == GID_NOBODY && synthesize_nobody())
45✔
453
                return strdup(NOBODY_GROUP_NAME);
1✔
454

455
        if (gid_is_valid(gid)) {
44✔
456
                _cleanup_free_ struct group *gr = NULL;
42✔
457

458
                r = getgrgid_malloc(gid, &gr);
42✔
459
                if (r >= 0)
42✔
460
                        return strdup(gr->gr_name);
42✔
461
        }
462

463
        if (asprintf(&ret, GID_FMT, gid) < 0)
2✔
464
                return NULL;
465

466
        return ret;
2✔
467
}
468

469
static bool gid_list_has(const gid_t *list, size_t size, gid_t val) {
269✔
470
        assert(list || size == 0);
269✔
471

472
        FOREACH_ARRAY(i, list, size)
429✔
473
                if (*i == val)
173✔
474
                        return true;
475

476
        return false;
477
}
478

479
int in_gid(gid_t gid) {
41✔
480
        _cleanup_free_ gid_t *gids = NULL;
41✔
481
        int ngroups;
41✔
482

483
        if (getgid() == gid)
41✔
484
                return 1;
485

486
        if (getegid() == gid)
38✔
487
                return 1;
488

489
        if (!gid_is_valid(gid))
38✔
490
                return -EINVAL;
491

492
        ngroups = getgroups_alloc(&gids);
37✔
493
        if (ngroups < 0)
37✔
494
                return ngroups;
495

496
        return gid_list_has(gids, ngroups, gid);
37✔
497
}
498

499
int in_group(const char *name) {
14✔
500
        int r;
14✔
501
        gid_t gid;
14✔
502

503
        r = get_group_creds(&name, &gid, 0);
14✔
504
        if (r < 0)
14✔
505
                return r;
14✔
506

507
        return in_gid(gid);
13✔
508
}
509

510
int merge_gid_lists(const gid_t *list1, size_t size1, const gid_t *list2, size_t size2, gid_t **ret) {
10,993✔
511
        size_t nresult = 0;
10,993✔
512
        assert(ret);
10,993✔
513

514
        if (size2 > INT_MAX - size1)
10,993✔
515
                return -ENOBUFS;
516

517
        gid_t *buf = new(gid_t, size1 + size2);
10,993✔
518
        if (!buf)
10,993✔
519
                return -ENOMEM;
520

521
        /* Duplicates need to be skipped on merging, otherwise they'll be passed on and stored in the kernel. */
522
        for (size_t i = 0; i < size1; i++)
11,017✔
523
                if (!gid_list_has(buf, nresult, list1[i]))
24✔
524
                        buf[nresult++] = list1[i];
24✔
525
        for (size_t i = 0; i < size2; i++)
11,201✔
526
                if (!gid_list_has(buf, nresult, list2[i]))
208✔
527
                        buf[nresult++] = list2[i];
195✔
528
        *ret = buf;
10,993✔
529
        return (int)nresult;
10,993✔
530
}
531

532
int getgroups_alloc(gid_t **ret) {
319✔
533
        int ngroups = 8;
319✔
534

535
        assert(ret);
319✔
536

537
        for (unsigned attempt = 0;;) {
×
538
                _cleanup_free_ gid_t *p = NULL;
216✔
539

540
                p = new(gid_t, ngroups);
319✔
541
                if (!p)
319✔
542
                        return -ENOMEM;
543

544
                ngroups = getgroups(ngroups, p);
319✔
545
                if (ngroups > 0) {
319✔
546
                        *ret = TAKE_PTR(p);
216✔
547
                        return ngroups;
216✔
548
                }
549
                if (ngroups == 0)
103✔
550
                        break;
551
                if (errno != EINVAL)
×
552
                        return -errno;
×
553

554
                /* Give up eventually */
555
                if (attempt++ > 10)
×
556
                        return -EINVAL;
557

558
                /* Get actual size needed, and size the array explicitly. Note that this is potentially racy
559
                 * to use (in multi-threaded programs), hence let's call this in a loop. */
560
                ngroups = getgroups(0, NULL);
×
561
                if (ngroups < 0)
×
562
                        return -errno;
×
563
                if (ngroups == 0)
×
564
                        break;
565
        }
566

567
        *ret = NULL;
103✔
568
        return 0;
103✔
569
}
570

571
int get_home_dir(char **ret) {
8,158✔
572
        _cleanup_free_ struct passwd *p = NULL;
8,158✔
573
        const char *e;
8,158✔
574
        uid_t u;
8,158✔
575
        int r;
8,158✔
576

577
        assert(ret);
8,158✔
578

579
        /* Take the user specified one */
580
        e = secure_getenv("HOME");
8,158✔
581
        if (e && path_is_valid(e) && path_is_absolute(e))
15,604✔
582
                goto found;
7,444✔
583

584
        /* Hardcode home directory for root and nobody to avoid NSS */
585
        u = getuid();
714✔
586
        if (u == 0) {
714✔
587
                e = "/root";
661✔
588
                goto found;
661✔
589
        }
590
        if (u == UID_NOBODY && synthesize_nobody()) {
53✔
591
                e = "/";
×
592
                goto found;
×
593
        }
594

595
        /* Check the database... */
596
        r = getpwuid_malloc(u, &p);
53✔
597
        if (r < 0)
53✔
598
                return r;
599

600
        e = p->pw_dir;
53✔
601
        if (!path_is_valid(e) || !path_is_absolute(e))
8,264✔
602
                return -EINVAL;
603

604
 found:
53✔
605
        return path_simplify_alloc(e, ret);
8,158✔
606
}
607

608
int get_shell(char **ret) {
6✔
609
        _cleanup_free_ struct passwd *p = NULL;
6✔
610
        const char *e;
6✔
611
        uid_t u;
6✔
612
        int r;
6✔
613

614
        assert(ret);
6✔
615

616
        /* Take the user specified one */
617
        e = secure_getenv("SHELL");
6✔
618
        if (e && path_is_valid(e) && path_is_absolute(e))
9✔
619
                goto found;
3✔
620

621
        /* Hardcode shell for root and nobody to avoid NSS */
622
        u = getuid();
3✔
623
        if (u == 0) {
3✔
624
                e = default_root_shell(NULL);
3✔
625
                goto found;
3✔
626
        }
627
        if (u == UID_NOBODY && synthesize_nobody()) {
×
628
                e = NOLOGIN;
×
629
                goto found;
×
630
        }
631

632
        /* Check the database... */
633
        r = getpwuid_malloc(u, &p);
×
634
        if (r < 0)
×
635
                return r;
636

637
        e = p->pw_shell;
×
638
        if (!path_is_valid(e) || !path_is_absolute(e))
6✔
639
                return -EINVAL;
640

641
 found:
×
642
        return path_simplify_alloc(e, ret);
6✔
643
}
644

645
int fully_set_uid_gid(uid_t uid, gid_t gid, const gid_t supplementary_gids[], size_t n_supplementary_gids) {
337✔
646
        int r;
337✔
647

648
        assert(supplementary_gids || n_supplementary_gids == 0);
337✔
649

650
        /* Sets all UIDs and all GIDs to the specified ones. Drops all auxiliary GIDs */
651

652
        r = maybe_setgroups(n_supplementary_gids, supplementary_gids);
337✔
653
        if (r < 0)
337✔
654
                return r;
655

656
        if (gid_is_valid(gid))
337✔
657
                if (setresgid(gid, gid, gid) < 0)
337✔
658
                        return -errno;
×
659

660
        if (uid_is_valid(uid))
337✔
661
                if (setresuid(uid, uid, uid) < 0)
337✔
662
                        return -errno;
×
663

664
        return 0;
665
}
666

667
int take_etc_passwd_lock(const char *root) {
391✔
668
        int r;
391✔
669

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

677
        _cleanup_free_ char *path = path_join(root, ETC_PASSWD_LOCK_PATH);
782✔
678
        if (!path)
391✔
679
                return log_oom_debug();
×
680

681
        (void) mkdir_parents(path, 0755);
391✔
682

683
        _cleanup_close_ int fd = open(path, O_WRONLY|O_CREAT|O_CLOEXEC|O_NOCTTY|O_NOFOLLOW, 0600);
782✔
684
        if (fd < 0)
391✔
685
                return log_debug_errno(errno, "Cannot open %s: %m", path);
×
686

687
        r = unposix_lock(fd, LOCK_EX);
391✔
688
        if (r < 0)
391✔
689
                return log_debug_errno(r, "Locking %s failed: %m", path);
×
690

691
        return TAKE_FD(fd);
692
}
693

694
bool valid_user_group_name(const char *u, ValidUserFlags flags) {
178,181✔
695
        const char *i;
178,181✔
696

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

704
        if (isempty(u)) /* An empty user name is never valid */
178,181✔
705
                return false;
706

707
        if (parse_uid(u, NULL) >= 0) /* Something that parses as numeric UID string is valid exactly when the
178,165✔
708
                                      * flag for it is set */
709
                return FLAGS_SET(flags, VALID_USER_ALLOW_NUMERIC);
126✔
710

711
        if (FLAGS_SET(flags, VALID_USER_RELAX)) {
178,039✔
712

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

724
                if (startswith(u, " ") || endswith(u, " ")) /* At least expect whitespace padding is removed
168,876✔
725
                                                             * at front and back (accept in the middle, since
726
                                                             * that's apparently a thing on Windows). Note
727
                                                             * that this also blocks usernames consisting of
728
                                                             * whitespace only. */
729
                        return false;
730

731
                if (!utf8_is_valid(u)) /* We want to synthesize JSON from this, hence insist on UTF-8 */
168,876✔
732
                        return false;
733

734
                if (string_has_cc(u, NULL)) /* CC characters are just dangerous (and \n in particular is the
168,876✔
735
                                             * record separator in /etc/passwd), so we can't allow that. */
736
                        return false;
737

738
                if (strpbrk(u, ":/")) /* Colons are the field separator in /etc/passwd, we can't allow
168,874✔
739
                                       * that. Slashes are special to file systems paths and user names
740
                                       * typically show up in the file system as home directories, hence
741
                                       * don't allow slashes. */
742
                        return false;
743

744
                if (in_charset(u, "0123456789")) /* Don't allow fully numeric strings, they might be confused
168,865✔
745
                                                  * with UIDs (note that this test is more broad than
746
                                                  * the parse_uid() test above, as it will cover more than
747
                                                  * the 32-bit range, and it will detect 65535 (which is in
748
                                                  * invalid UID, even though in the unsigned 32 bit range) */
749
                        return false;
750

751
                if (u[0] == '-' && in_charset(u + 1, "0123456789")) /* Don't allow negative fully numeric
168,857✔
752
                                                                     * strings either. After all some people
753
                                                                     * write 65535 as -1 (even though that's
754
                                                                     * not even true on 32-bit uid_t
755
                                                                     * anyway) */
756
                        return false;
757

758
                if (dot_or_dot_dot(u)) /* User names typically become home directory names, and these two are
168,855✔
759
                                        * special in that context, don't allow that. */
760
                        return false;
761

762
                /* Compare with strict result and warn if result doesn't match */
763
                if (FLAGS_SET(flags, VALID_USER_WARN) && !valid_user_group_name(u, 0))
168,849✔
764
                        log_struct(LOG_NOTICE,
×
765
                                   LOG_MESSAGE("Accepting user/group name '%s', which does not match strict user/group name rules.", u),
766
                                   "USER_GROUP_NAME=%s", u,
767
                                   "MESSAGE_ID=" SD_MESSAGE_UNSAFE_USER_NAME_STR);
768

769
                /* Note that we make no restrictions on the length in relaxed mode! */
770
        } else {
771
                long sz;
9,163✔
772
                size_t l;
9,163✔
773

774
                /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.437. We are a bit stricter here
775
                 * however. Specifically we deviate from POSIX rules:
776
                 *
777
                 * - We don't allow empty user names (see above)
778
                 * - We require that names fit into the appropriate utmp field
779
                 * - We don't allow any dots (this conflicts with chown syntax which permits dots as user/group name separator)
780
                 * - We don't allow dashes or digit as the first character
781
                 *
782
                 * Note that other systems are even more restrictive, and don't permit underscores or uppercase characters.
783
                 */
784

785
                if (!ascii_isalpha(u[0]) &&
9,163✔
786
                    u[0] != '_')
787
                        return false;
788

789
                for (i = u+1; *i; i++)
58,192✔
790
                        if (!ascii_isalpha(*i) &&
49,070✔
791
                            !ascii_isdigit(*i) &&
2,087✔
792
                            !IN_SET(*i, '_', '-'))
1,802✔
793
                                return false;
794

795
                l = i - u;
9,122✔
796

797
                sz = sysconf(_SC_LOGIN_NAME_MAX);
9,122✔
798
                assert_se(sz > 0);
9,122✔
799

800
                if (l > (size_t) sz) /* glibc: 256 */
9,122✔
801
                        return false;
802
                if (l > NAME_MAX) /* must fit in a filename: 255 */
9,122✔
803
                        return false;
804
                if (l > sizeof_field(struct utmpx, ut_user) - 1) /* must fit in utmp: 31 */
9,122✔
805
                        return false;
×
806
        }
807

808
        return true;
809
}
810

811
bool valid_gecos(const char *d) {
3,314✔
812

813
        if (!d)
3,314✔
814
                return false;
815

816
        if (!utf8_is_valid(d))
3,313✔
817
                return false;
818

819
        if (string_has_cc(d, NULL))
3,313✔
820
                return false;
821

822
        /* Colons are used as field separators, and hence not OK */
823
        if (strchr(d, ':'))
3,312✔
824
                return false;
1✔
825

826
        return true;
827
}
828

829
char* mangle_gecos(const char *d) {
8✔
830
        char *mangled;
8✔
831

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

836
        mangled = strdup(d);
8✔
837
        if (!mangled)
8✔
838
                return NULL;
839

840
        for (char *i = mangled; *i; i++) {
65✔
841
                int len;
57✔
842

843
                if ((uint8_t) *i < (uint8_t) ' ' || *i == ':') {
57✔
844
                        *i = ' ';
8✔
845
                        continue;
8✔
846
                }
847

848
                len = utf8_encoded_valid_unichar(i, SIZE_MAX);
49✔
849
                if (len < 0) {
49✔
850
                        *i = ' ';
3✔
851
                        continue;
3✔
852
                }
853

854
                i += len - 1;
46✔
855
        }
856

857
        return mangled;
858
}
859

860
bool valid_home(const char *p) {
3,498✔
861
        /* Note that this function is also called by valid_shell(), any
862
         * changes must account for that. */
863

864
        if (isempty(p))
3,498✔
865
                return false;
866

867
        if (!utf8_is_valid(p))
3,494✔
868
                return false;
869

870
        if (string_has_cc(p, NULL))
3,494✔
871
                return false;
872

873
        if (!path_is_absolute(p))
3,492✔
874
                return false;
875

876
        if (!path_is_normalized(p))
3,486✔
877
                return false;
878

879
        /* Colons are used as field separators, and hence not OK */
880
        if (strchr(p, ':'))
3,482✔
881
                return false;
2✔
882

883
        return true;
884
}
885

886
bool valid_shell(const char *p) {
142✔
887
        /* We have the same requirements, so just piggy-back on the home check.
888
         *
889
         * Let's ignore /etc/shells because this is only applicable to real and not system users. It is also
890
         * incompatible with the idea of empty /etc/. */
891
        if (!valid_home(p))
142✔
892
                return false;
893

894
        return !endswith(p, "/"); /* one additional restriction: shells may not be dirs */
133✔
895
}
896

897
int maybe_setgroups(size_t size, const gid_t *list) {
553✔
898
        int r;
553✔
899

900
        /* Check if setgroups is allowed before we try to drop all the auxiliary groups */
901
        if (size == 0) { /* Dropping all aux groups? */
553✔
902
                _cleanup_free_ char *setgroups_content = NULL;
365✔
903
                bool can_setgroups;
365✔
904

905
                r = read_one_line_file("/proc/self/setgroups", &setgroups_content);
365✔
906
                if (r == -ENOENT)
365✔
907
                        /* Old kernels don't have /proc/self/setgroups, so assume we can use setgroups */
908
                        can_setgroups = true;
909
                else if (r < 0)
316✔
910
                        return r;
911
                else
912
                        can_setgroups = streq(setgroups_content, "allow");
316✔
913

914
                if (!can_setgroups) {
316✔
915
                        log_debug("Skipping setgroups(), /proc/self/setgroups is set to 'deny'");
×
916
                        return 0;
×
917
                }
918
        }
919

920
        return RET_NERRNO(setgroups(size, list));
554✔
921
}
922

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

933
        if (cache < 0)
83✔
934
                cache = access("/etc/systemd/dont-synthesize-nobody", F_OK) < 0;
58✔
935

936
        return cache;
83✔
937
}
938

939
int putpwent_sane(const struct passwd *pw, FILE *stream) {
416✔
940
        assert(pw);
416✔
941
        assert(stream);
416✔
942

943
        errno = 0;
416✔
944
        if (putpwent(pw, stream) != 0)
416✔
945
                return errno_or_else(EIO);
×
946

947
        return 0;
948
}
949

950
int putspent_sane(const struct spwd *sp, FILE *stream) {
386✔
951
        assert(sp);
386✔
952
        assert(stream);
386✔
953

954
        errno = 0;
386✔
955
        if (putspent(sp, stream) != 0)
386✔
956
                return errno_or_else(EIO);
×
957

958
        return 0;
959
}
960

961
int putgrent_sane(const struct group *gr, FILE *stream) {
6,472✔
962
        assert(gr);
6,472✔
963
        assert(stream);
6,472✔
964

965
        errno = 0;
6,472✔
966
        if (putgrent(gr, stream) != 0)
6,472✔
967
                return errno_or_else(EIO);
×
968

969
        return 0;
970
}
971

972
#if ENABLE_GSHADOW
973
int putsgent_sane(const struct sgrp *sg, FILE *stream) {
6,447✔
974
        assert(sg);
6,447✔
975
        assert(stream);
6,447✔
976

977
        errno = 0;
6,447✔
978
        if (putsgent(sg, stream) != 0)
6,447✔
979
                return errno_or_else(EIO);
×
980

981
        return 0;
982
}
983
#endif
984

985
int fgetpwent_sane(FILE *stream, struct passwd **pw) {
3,354✔
986
        assert(stream);
3,354✔
987
        assert(pw);
3,354✔
988

989
        errno = 0;
3,354✔
990
        struct passwd *p = fgetpwent(stream);
3,354✔
991
        if (!p && !IN_SET(errno, 0, ENOENT))
3,354✔
992
                return -errno;
×
993

994
        *pw = p;
3,354✔
995
        return !!p;
3,354✔
996
}
997

998
int fgetspent_sane(FILE *stream, struct spwd **sp) {
221✔
999
        assert(stream);
221✔
1000
        assert(sp);
221✔
1001

1002
        errno = 0;
221✔
1003
        struct spwd *s = fgetspent(stream);
221✔
1004
        if (!s && !IN_SET(errno, 0, ENOENT))
221✔
1005
                return -errno;
×
1006

1007
        *sp = s;
221✔
1008
        return !!s;
221✔
1009
}
1010

1011
int fgetgrent_sane(FILE *stream, struct group **gr) {
12,949✔
1012
        assert(stream);
12,949✔
1013
        assert(gr);
12,949✔
1014

1015
        errno = 0;
12,949✔
1016
        struct group *g = fgetgrent(stream);
12,949✔
1017
        if (!g && !IN_SET(errno, 0, ENOENT))
12,949✔
1018
                return -errno;
×
1019

1020
        *gr = g;
12,949✔
1021
        return !!g;
12,949✔
1022
}
1023

1024
#if ENABLE_GSHADOW
1025
int fgetsgent_sane(FILE *stream, struct sgrp **sg) {
6,035✔
1026
        assert(stream);
6,035✔
1027
        assert(sg);
6,035✔
1028

1029
        errno = 0;
6,035✔
1030
        struct sgrp *s = fgetsgent(stream);
6,035✔
1031
        if (!s && !IN_SET(errno, 0, ENOENT))
6,035✔
1032
                return -errno;
×
1033

1034
        *sg = s;
6,035✔
1035
        return !!s;
6,035✔
1036
}
1037
#endif
1038

1039
int is_this_me(const char *username) {
62✔
1040
        uid_t uid;
62✔
1041
        int r;
62✔
1042

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

1045
        r = get_user_creds(&username, &uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
62✔
1046
        if (r < 0)
62✔
1047
                return r;
62✔
1048

1049
        return uid == getuid();
62✔
1050
}
1051

1052
const char* get_home_root(void) {
824✔
1053
        const char *e;
824✔
1054

1055
        /* For debug purposes allow overriding where we look for home dirs */
1056
        e = secure_getenv("SYSTEMD_HOME_ROOT");
824✔
1057
        if (e && path_is_absolute(e) && path_is_normalized(e))
824✔
1058
                return e;
×
1059

1060
        return "/home";
1061
}
1062

1063
static size_t getpw_buffer_size(void) {
675✔
1064
        long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
675✔
1065
        return bufsize <= 0 ? 4096U : (size_t) bufsize;
675✔
1066
}
1067

1068
static bool errno_is_user_doesnt_exist(int error) {
1✔
1069
        /* See getpwnam(3) and getgrnam(3): those codes and others can be returned if the user or group are
1070
         * not found. */
1071
        return IN_SET(abs(error), ENOENT, ESRCH, EBADF, EPERM);
1✔
1072
}
1073

1074
int getpwnam_malloc(const char *name, struct passwd **ret) {
174✔
1075
        size_t bufsize = getpw_buffer_size();
174✔
1076
        int r;
174✔
1077

1078
        /* A wrapper around getpwnam_r() that allocates the necessary buffer on the heap. The caller must
1079
         * free() the returned structures! */
1080

1081
        if (isempty(name))
348✔
1082
                return -EINVAL;
1083

1084
        for (;;) {
174✔
1085
                _cleanup_free_ void *buf = NULL;
174✔
1086

1087
                buf = malloc(ALIGN(sizeof(struct passwd)) + bufsize);
174✔
1088
                if (!buf)
174✔
1089
                        return -ENOMEM;
1090

1091
                struct passwd *pw = NULL;
174✔
1092
                r = getpwnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct passwd)), (size_t) bufsize, &pw);
174✔
1093
                if (r == 0) {
174✔
1094
                        if (pw) {
174✔
1095
                                if (ret)
103✔
1096
                                        *ret = TAKE_PTR(buf);
103✔
1097
                                return 0;
103✔
1098
                        }
1099

1100
                        return -ESRCH;
1101
                }
1102

1103
                assert(r > 0);
×
1104

1105
                /* getpwnam() may fail with ENOENT if /etc/passwd is missing.  For us that is equivalent to
1106
                 * the name not being defined. */
1107
                if (errno_is_user_doesnt_exist(r))
×
1108
                        return -ESRCH;
1109
                if (r != ERANGE)
×
1110
                        return -r;
×
1111

1112
                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct passwd)))
×
1113
                        return -ENOMEM;
1114
                bufsize *= 2;
×
1115
        }
1116
}
1117

1118
int getpwuid_malloc(uid_t uid, struct passwd **ret) {
501✔
1119
        size_t bufsize = getpw_buffer_size();
501✔
1120
        int r;
501✔
1121

1122
        if (!uid_is_valid(uid))
501✔
1123
                return -EINVAL;
1124

1125
        for (;;) {
501✔
1126
                _cleanup_free_ void *buf = NULL;
501✔
1127

1128
                buf = malloc(ALIGN(sizeof(struct passwd)) + bufsize);
501✔
1129
                if (!buf)
501✔
1130
                        return -ENOMEM;
1131

1132
                struct passwd *pw = NULL;
501✔
1133
                r = getpwuid_r(uid, buf, (char*) buf + ALIGN(sizeof(struct passwd)), (size_t) bufsize, &pw);
501✔
1134
                if (r == 0) {
501✔
1135
                        if (pw) {
501✔
1136
                                if (ret)
351✔
1137
                                        *ret = TAKE_PTR(buf);
351✔
1138
                                return 0;
351✔
1139
                        }
1140

1141
                        return -ESRCH;
1142
                }
1143

1144
                assert(r > 0);
×
1145

1146
                if (errno_is_user_doesnt_exist(r))
×
1147
                        return -ESRCH;
1148
                if (r != ERANGE)
×
1149
                        return -r;
×
1150

1151
                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct passwd)))
×
1152
                        return -ENOMEM;
1153
                bufsize *= 2;
×
1154
        }
1155
}
1156

1157
static size_t getgr_buffer_size(void) {
3,297✔
1158
        long bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
3,297✔
1159
        return bufsize <= 0 ? 4096U : (size_t) bufsize;
3,297✔
1160
}
1161

1162
int getgrnam_malloc(const char *name, struct group **ret) {
78✔
1163
        size_t bufsize = getgr_buffer_size();
78✔
1164
        int r;
78✔
1165

1166
        if (isempty(name))
156✔
1167
                return -EINVAL;
1168

1169
        for (;;) {
78✔
1170
                _cleanup_free_ void *buf = NULL;
78✔
1171

1172
                buf = malloc(ALIGN(sizeof(struct group)) + bufsize);
78✔
1173
                if (!buf)
78✔
1174
                        return -ENOMEM;
1175

1176
                struct group *gr = NULL;
78✔
1177
                r = getgrnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct group)), (size_t) bufsize, &gr);
78✔
1178
                if (r == 0) {
78✔
1179
                        if (gr) {
77✔
1180
                                if (ret)
11✔
1181
                                        *ret = TAKE_PTR(buf);
11✔
1182
                                return 0;
11✔
1183
                        }
1184

1185
                        return -ESRCH;
1186
                }
1187

1188
                assert(r > 0);
1✔
1189

1190
                if (errno_is_user_doesnt_exist(r))
1✔
1191
                        return -ESRCH;
1192
                if (r != ERANGE)
1✔
1193
                        return -r;
1✔
1194

1195
                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct group)))
×
1196
                        return -ENOMEM;
1197
                bufsize *= 2;
×
1198
        }
1199
}
1200

1201
int getgrgid_malloc(gid_t gid, struct group **ret) {
3,219✔
1202
        size_t bufsize = getgr_buffer_size();
3,219✔
1203
        int r;
3,219✔
1204

1205
        if (!gid_is_valid(gid))
3,219✔
1206
                return -EINVAL;
1207

1208
        for (;;) {
3,219✔
1209
                _cleanup_free_ void *buf = NULL;
3,219✔
1210

1211
                buf = malloc(ALIGN(sizeof(struct group)) + bufsize);
3,219✔
1212
                if (!buf)
3,219✔
1213
                        return -ENOMEM;
1214

1215
                struct group *gr = NULL;
3,219✔
1216
                r = getgrgid_r(gid, buf, (char*) buf + ALIGN(sizeof(struct group)), (size_t) bufsize, &gr);
3,219✔
1217
                if (r == 0) {
3,219✔
1218
                        if (gr) {
3,219✔
1219
                                if (ret)
2,964✔
1220
                                        *ret = TAKE_PTR(buf);
2,964✔
1221
                                return 0;
2,964✔
1222
                        }
1223

1224
                        return -ESRCH;
1225
                }
1226

1227
                assert(r > 0);
×
1228

1229
                if (errno_is_user_doesnt_exist(r))
×
1230
                        return -ESRCH;
1231
                if (r != ERANGE)
×
1232
                        return -r;
×
1233

1234
                if (bufsize > SIZE_MAX/2 - ALIGN(sizeof(struct group)))
×
1235
                        return -ENOMEM;
1236
                bufsize *= 2;
×
1237
        }
1238
}
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