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

systemd / systemd / 15150396955

20 May 2025 10:32PM UTC coverage: 72.047% (-0.2%) from 72.25%
15150396955

push

github

web-flow
resolved: add new "DNS Delegate" concepts (#34368)

Various long standing issues (at least: #5573 #14159 #20485 #21260
#24532 #32022 #18056) have been asking for a way to delegate DNS
resolution of specific domains to very specific DNS servers.

This PR goes a major step towards that goal by adding a new concept "DNS
Delegate" which allows to configure just that. Basically, this adds a
third kind of DNS scope to resolved's logic: besides the per-link and
global DNS scopes there are now also "delegate" scopes, which can be
created by dropping in a new file /etc/systemd/dns-delegate/*.conf. They
carry DNS= and Domains= lines just like the global setting or what the
per-link configuration can carry.

And they are consulted the same way as link DNS scopes are considered,
following the same routing rules.

This allows to configure these DNS delegates statically via drop-in
files as mentioned, and only adds the most basic functionality. Later on
we might want to extend this:

1. Allow dynamic creation of DNS delegates via IPC with lifecycle bound
to IPC client (usecase: installing a DNS delegate that routes traffic to
some DNS-over-TLS server once basic setup is complete).
2. Allow configuration of protocol details per delegate the same way
this is currently allowed per-link.
3. Instead of strictly using DNS as delegation protocol, support an
alternative varlink based protocol (without retransmission problems and
so on) that systemd-machined and similar can implement.

This PR is not complete yet. Lacks docs and tests. Seems to work fine in
my local tests however.

Fixes: #5573
Fixes: #18056
Fixes: #20485

470 of 586 new or added lines in 14 files covered. (80.2%)

3358 existing lines in 54 files now uncovered.

299091 of 415134 relevant lines covered (72.05%)

703065.7 hits per line

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

85.86
/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 "extract-word.h"
20
#include "fd-util.h"
21
#include "fileio.h"
22
#include "format-util.h"
23
#include "lock-util.h"
24
#include "log.h"
25
#include "macro.h"
26
#include "mkdir.h"
27
#include "parse-util.h"
28
#include "path-util.h"
29
#include "random-util.h"
30
#include "string-util.h"
31
#include "strv.h"
32
#include "terminal-util.h"
33
#include "user-util.h"
34
#include "utf8.h"
35

36
bool uid_is_valid(uid_t uid) {
10,589,059✔
37

38
        /* Also see POSIX IEEE Std 1003.1-2008, 2016 Edition, 3.436. */
39

40
        /* Some libc APIs use UID_INVALID as special placeholder */
41
        if (uid == (uid_t) UINT32_C(0xFFFFFFFF))
10,589,059✔
42
                return false;
43

44
        /* A long time ago UIDs where 16 bit, hence explicitly avoid the 16-bit -1 too */
45
        if (uid == (uid_t) UINT32_C(0xFFFF))
5,219,688✔
46
                return false;
18✔
47

48
        return true;
49
}
50

51
int parse_uid(const char *s, uid_t *ret) {
206,763✔
52
        uint32_t uid = 0;
206,763✔
53
        int r;
206,763✔
54

55
        assert(s);
206,763✔
56

57
        assert_cc(sizeof(uid_t) == sizeof(uint32_t));
206,763✔
58

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

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

76
        if (ret)
10,963✔
77
                *ret = uid;
10,202✔
78

79
        return 0;
80
}
81

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

87
        assert(s);
35✔
88
        assert(ret_lower);
35✔
89
        assert(ret_upper);
35✔
90

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

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

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

113
                if (l > u)
10✔
114
                        return -EINVAL;
115
        }
116

117
        *ret_lower = l;
16✔
118
        *ret_upper = u;
16✔
119
        return 0;
16✔
120
}
121

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

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

131
        return uid_to_name(uid);
×
132
}
133

134
char* getusername_malloc(void) {
519✔
135
        const char *e;
519✔
136

137
        e = secure_getenv("USER");
519✔
138
        if (e)
519✔
139
                return strdup(e);
418✔
140

141
        return uid_to_name(getuid());
101✔
142
}
143

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

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

167
        assert(rfd >= 0 || rfd == AT_FDCWD);
6,819✔
168

169
        int r = chaseat(rfd, DEFAULT_USER_SHELL, CHASE_AT_RESOLVE_IN_ROOT, NULL, NULL);
6,819✔
170
        if (r < 0 && r != -ENOENT)
6,819✔
171
                log_debug_errno(r, "Failed to look up shell '%s': %m", DEFAULT_USER_SHELL);
×
172
        if (r > 0)
6,819✔
173
                return DEFAULT_USER_SHELL;
6,803✔
174

175
        return "/bin/sh";
176
}
177

178
const char* default_root_shell(const char *root) {
6,813✔
179
        _cleanup_close_ int rfd = -EBADF;
6,813✔
180

181
        rfd = open(empty_to_root(root), O_CLOEXEC | O_DIRECTORY | O_PATH);
6,813✔
182
        if (rfd < 0)
6,813✔
183
                return "/bin/sh";
184

185
        return default_root_shell_at(rfd);
6,813✔
186
}
187

188
static int synthesize_user_creds(
18,038✔
189
                const char **username,
190
                uid_t *ret_uid, gid_t *ret_gid,
191
                const char **ret_home,
192
                const char **ret_shell,
193
                UserCredsFlags flags) {
194

195
        assert(username);
18,038✔
196
        assert(*username);
18,038✔
197

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

201
        if (STR_IN_SET(*username, "root", "0")) {
18,038✔
202
                *username = "root";
13,813✔
203

204
                if (ret_uid)
13,813✔
205
                        *ret_uid = 0;
7,023✔
206
                if (ret_gid)
13,813✔
207
                        *ret_gid = 0;
8✔
208
                if (ret_home)
13,813✔
209
                        *ret_home = "/root";
6,798✔
210
                if (ret_shell)
13,813✔
211
                        *ret_shell = default_root_shell(NULL);
6,798✔
212

213
                return 0;
13,813✔
214
        }
215

216
        if (STR_IN_SET(*username, NOBODY_USER_NAME, "65534") &&
4,236✔
217
            synthesize_nobody()) {
11✔
218
                *username = NOBODY_USER_NAME;
11✔
219

220
                if (ret_uid)
11✔
221
                        *ret_uid = UID_NOBODY;
11✔
222
                if (ret_gid)
11✔
223
                        *ret_gid = GID_NOBODY;
7✔
224
                if (ret_home)
11✔
225
                        *ret_home = FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : "/";
6✔
226
                if (ret_shell)
11✔
227
                        *ret_shell = FLAGS_SET(flags, USER_CREDS_SUPPRESS_PLACEHOLDER) ? NULL : NOLOGIN;
6✔
228

229
                return 0;
11✔
230
        }
231

232
        return -ENOMEDIUM;
4,214✔
233
}
234

235
int get_user_creds(
18,489✔
236
                const char **username,
237
                uid_t *ret_uid, gid_t *ret_gid,
238
                const char **ret_home,
239
                const char **ret_shell,
240
                UserCredsFlags flags) {
241

242
        bool patch_username = false;
18,489✔
243
        uid_t u = UID_INVALID;
18,489✔
244
        struct passwd *p;
18,489✔
245
        int r;
18,489✔
246

247
        assert(username);
18,489✔
248
        assert(*username);
18,489✔
249
        assert((ret_home || ret_shell) || !(flags & (USER_CREDS_SUPPRESS_PLACEHOLDER|USER_CREDS_CLEAN)));
18,489✔
250

251
        if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS) ||
18,489✔
252
            (!ret_home && !ret_shell)) {
253

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

263
                r = synthesize_user_creds(username, ret_uid, ret_gid, ret_home, ret_shell, flags);
18,038✔
264
                if (r >= 0)
18,038✔
265
                        return 0;
18,489✔
266
                if (r != -ENOMEDIUM) /* not a username we can synthesize */
4,214✔
267
                        return r;
268
        }
269

270
        if (parse_uid(*username, &u) >= 0) {
4,665✔
271
                errno = 0;
187✔
272
                p = getpwuid(u);
187✔
273

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

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

285
                        if (ret_uid)
×
286
                                *ret_uid = u;
×
287

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

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

304
                return r;
9✔
305
        }
306

307
        if (ret_uid && !uid_is_valid(p->pw_uid))
4,656✔
308
                return -EBADMSG;
309

310
        if (ret_gid && !gid_is_valid(p->pw_gid))
4,656✔
311
                return -EBADMSG;
312

313
        if (ret_uid)
4,656✔
314
                *ret_uid = p->pw_uid;
4,656✔
315

316
        if (ret_gid)
4,656✔
317
                *ret_gid = p->pw_gid;
2,616✔
318

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

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

330
        if (patch_username)
4,656✔
331
                *username = p->pw_name;
184✔
332

333
        return 0;
334
}
335

336
static int synthesize_group_creds(
10,292✔
337
                const char **groupname,
338
                gid_t *ret_gid) {
339

340
        assert(groupname);
10,292✔
341
        assert(*groupname);
10,292✔
342

343
        if (STR_IN_SET(*groupname, "root", "0")) {
10,292✔
344
                *groupname = "root";
5,270✔
345

346
                if (ret_gid)
5,270✔
347
                        *ret_gid = 0;
5,270✔
348

349
                return 0;
5,270✔
350
        }
351

352
        if (STR_IN_SET(*groupname, NOBODY_GROUP_NAME, "65534") &&
5,026✔
353
            synthesize_nobody()) {
4✔
354
                *groupname = NOBODY_GROUP_NAME;
4✔
355

356
                if (ret_gid)
4✔
357
                        *ret_gid = GID_NOBODY;
4✔
358

359
                return 0;
4✔
360
        }
361

362
        return -ENOMEDIUM;
5,018✔
363
}
364

365
int get_group_creds(const char **groupname, gid_t *ret_gid, UserCredsFlags flags) {
10,292✔
366
        bool patch_groupname = false;
10,292✔
367
        struct group *g;
10,292✔
368
        gid_t id;
10,292✔
369
        int r;
10,292✔
370

371
        assert(groupname);
10,292✔
372
        assert(*groupname);
10,292✔
373

374
        if (!FLAGS_SET(flags, USER_CREDS_PREFER_NSS)) {
10,292✔
375
                r = synthesize_group_creds(groupname, ret_gid);
10,292✔
376
                if (r >= 0)
10,292✔
377
                        return 0;
10,292✔
378
                if (r != -ENOMEDIUM) /* not a groupname we can synthesize */
5,018✔
379
                        return r;
380
        }
381

382
        if (parse_gid(*groupname, &id) >= 0) {
5,018✔
383
                errno = 0;
×
384
                g = getgrgid(id);
×
385

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

392
                        return 0;
×
393
                }
394
        } else {
395
                errno = 0;
5,018✔
396
                g = getgrnam(*groupname);
5,018✔
397
        }
398

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

404
                if (FLAGS_SET(flags, USER_CREDS_PREFER_NSS))
1✔
405
                        if (synthesize_group_creds(groupname, ret_gid) >= 0)
×
406
                                return 0;
407

408
                return r;
1✔
409
        }
410

411
        if (ret_gid) {
5,017✔
412
                if (!gid_is_valid(g->gr_gid))
5,017✔
413
                        return -EBADMSG;
414

415
                *ret_gid = g->gr_gid;
5,017✔
416
        }
417

418
        if (patch_groupname)
5,017✔
419
                *groupname = g->gr_name;
×
420

421
        return 0;
422
}
423

424
char* uid_to_name(uid_t uid) {
459✔
425
        char *ret;
459✔
426
        int r;
459✔
427

428
        /* Shortcut things to avoid NSS lookups */
429
        if (uid == 0)
459✔
430
                return strdup("root");
459✔
431
        if (uid == UID_NOBODY && synthesize_nobody())
186✔
432
                return strdup(NOBODY_USER_NAME);
1✔
433

434
        if (uid_is_valid(uid)) {
185✔
435
                _cleanup_free_ struct passwd *pw = NULL;
183✔
436

437
                r = getpwuid_malloc(uid, &pw);
183✔
438
                if (r >= 0)
183✔
439
                        return strdup(pw->pw_name);
183✔
440
        }
441

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

445
        return ret;
2✔
446
}
447

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

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

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

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

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

468
        return ret;
2✔
469
}
470

471
static bool gid_list_has(const gid_t *list, size_t size, gid_t val) {
353✔
472
        assert(list || size == 0);
353✔
473

474
        FOREACH_ARRAY(i, list, size)
518✔
475
                if (*i == val)
178✔
476
                        return true;
477

478
        return false;
479
}
480

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

485
        if (getgid() == gid)
41✔
486
                return 1;
487

488
        if (getegid() == gid)
38✔
489
                return 1;
490

491
        if (!gid_is_valid(gid))
38✔
492
                return -EINVAL;
493

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

498
        return gid_list_has(gids, ngroups, gid);
37✔
499
}
500

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

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

509
        return in_gid(gid);
13✔
510
}
511

512
int merge_gid_lists(const gid_t *list1, size_t size1, const gid_t *list2, size_t size2, gid_t **ret) {
9,594✔
513
        size_t nresult = 0;
9,594✔
514
        assert(ret);
9,594✔
515

516
        if (size2 > INT_MAX - size1)
9,594✔
517
                return -ENOBUFS;
518

519
        gid_t *buf = new(gid_t, size1 + size2);
9,594✔
520
        if (!buf)
9,594✔
521
                return -ENOMEM;
522

523
        /* Duplicates need to be skipped on merging, otherwise they'll be passed on and stored in the kernel. */
524
        for (size_t i = 0; i < size1; i++)
9,628✔
525
                if (!gid_list_has(buf, nresult, list1[i]))
34✔
526
                        buf[nresult++] = list1[i];
34✔
527
        for (size_t i = 0; i < size2; i++)
9,876✔
528
                if (!gid_list_has(buf, nresult, list2[i]))
282✔
529
                        buf[nresult++] = list2[i];
269✔
530
        *ret = buf;
9,594✔
531
        return (int)nresult;
9,594✔
532
}
533

534
int getgroups_alloc(gid_t **ret) {
441✔
535
        int ngroups = 8;
441✔
536

537
        assert(ret);
441✔
538

539
        for (unsigned attempt = 0;;) {
×
540
                _cleanup_free_ gid_t *p = NULL;
290✔
541

542
                p = new(gid_t, ngroups);
441✔
543
                if (!p)
441✔
544
                        return -ENOMEM;
545

546
                ngroups = getgroups(ngroups, p);
441✔
547
                if (ngroups > 0) {
441✔
548
                        *ret = TAKE_PTR(p);
290✔
549
                        return ngroups;
290✔
550
                }
551
                if (ngroups == 0)
151✔
552
                        break;
553
                if (errno != EINVAL)
×
554
                        return -errno;
×
555

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

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

569
        *ret = NULL;
151✔
570
        return 0;
151✔
571
}
572

573
int get_home_dir(char **ret) {
10,692✔
574
        _cleanup_free_ struct passwd *p = NULL;
10,692✔
575
        const char *e;
10,692✔
576
        uid_t u;
10,692✔
577
        int r;
10,692✔
578

579
        assert(ret);
10,692✔
580

581
        /* Take the user specified one */
582
        e = secure_getenv("HOME");
10,692✔
583
        if (e && path_is_valid(e) && path_is_absolute(e))
20,725✔
584
                goto found;
10,031✔
585

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

597
        /* Check the database... */
598
        r = getpwuid_malloc(u, &p);
×
599
        if (r < 0)
×
600
                return r;
601

602
        e = p->pw_dir;
×
603
        if (!path_is_valid(e) || !path_is_absolute(e))
10,692✔
604
                return -EINVAL;
605

606
 found:
×
607
        return path_simplify_alloc(e, ret);
10,692✔
608
}
609

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

616
        assert(ret);
6✔
617

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

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

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

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

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

647
int fully_set_uid_gid(uid_t uid, gid_t gid, const gid_t supplementary_gids[], size_t n_supplementary_gids) {
382✔
648
        int r;
382✔
649

650
        assert(supplementary_gids || n_supplementary_gids == 0);
382✔
651

652
        /* Sets all UIDs and all GIDs to the specified ones. Drops all auxiliary GIDs */
653

654
        r = maybe_setgroups(n_supplementary_gids, supplementary_gids);
382✔
655
        if (r < 0)
382✔
656
                return r;
657

658
        if (gid_is_valid(gid))
382✔
659
                if (setresgid(gid, gid, gid) < 0)
382✔
660
                        return -errno;
×
661

662
        if (uid_is_valid(uid))
382✔
663
                if (setresuid(uid, uid, uid) < 0)
382✔
664
                        return -errno;
×
665

666
        return 0;
667
}
668

669
int take_etc_passwd_lock(const char *root) {
281✔
670
        int r;
281✔
671

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

679
        _cleanup_free_ char *path = path_join(root, ETC_PASSWD_LOCK_PATH);
562✔
680
        if (!path)
281✔
681
                return log_oom_debug();
×
682

683
        (void) mkdir_parents(path, 0755);
281✔
684

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

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

693
        return TAKE_FD(fd);
694
}
695

696
bool valid_user_group_name(const char *u, ValidUserFlags flags) {
165,116✔
697
        const char *i;
165,116✔
698

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

706
        if (isempty(u)) /* An empty user name is never valid */
165,116✔
707
                return false;
708

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

713
        if (FLAGS_SET(flags, VALID_USER_RELAX)) {
164,951✔
714

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

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

733
                if (!utf8_is_valid(u)) /* We want to synthesize JSON from this, hence insist on UTF-8 */
162,459✔
734
                        return false;
735

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

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

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

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

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

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

771
                /* Note that we make no restrictions on the length in relaxed mode! */
772
        } else {
773
                long sz;
2,492✔
774
                size_t l;
2,492✔
775

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

787
                if (!ascii_isalpha(u[0]) &&
2,492✔
788
                    u[0] != '_')
789
                        return false;
790

791
                for (i = u+1; *i; i++)
21,423✔
792
                        if (!ascii_isalpha(*i) &&
18,974✔
793
                            !ascii_isdigit(*i) &&
1,339✔
794
                            !IN_SET(*i, '_', '-'))
909✔
795
                                return false;
796

797
                l = i - u;
2,449✔
798

799
                sz = sysconf(_SC_LOGIN_NAME_MAX);
2,449✔
800
                assert_se(sz > 0);
2,449✔
801

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

810
        return true;
811
}
812

813
bool valid_gecos(const char *d) {
7,754✔
814

815
        if (!d)
7,754✔
816
                return false;
817

818
        if (!utf8_is_valid(d))
7,753✔
819
                return false;
820

821
        if (string_has_cc(d, NULL))
7,753✔
822
                return false;
823

824
        /* Colons are used as field separators, and hence not OK */
825
        if (strchr(d, ':'))
7,752✔
826
                return false;
1✔
827

828
        return true;
829
}
830

831
char* mangle_gecos(const char *d) {
8✔
832
        char *mangled;
8✔
833

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

838
        mangled = strdup(d);
8✔
839
        if (!mangled)
8✔
840
                return NULL;
841

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

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

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

856
                i += len - 1;
46✔
857
        }
858

859
        return mangled;
860
}
861

862
bool valid_home(const char *p) {
4,916✔
863
        /* Note that this function is also called by valid_shell(), any
864
         * changes must account for that. */
865

866
        if (isempty(p))
4,916✔
867
                return false;
868

869
        if (!utf8_is_valid(p))
4,912✔
870
                return false;
871

872
        if (string_has_cc(p, NULL))
4,912✔
873
                return false;
874

875
        if (!path_is_absolute(p))
4,910✔
876
                return false;
877

878
        if (!path_is_normalized(p))
4,904✔
879
                return false;
880

881
        /* Colons are used as field separators, and hence not OK */
882
        if (strchr(p, ':'))
4,900✔
883
                return false;
2✔
884

885
        return true;
886
}
887

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

896
        return !endswith(p, "/"); /* one additional restriction: shells may not be dirs */
33✔
897
}
898

899
int maybe_setgroups(size_t size, const gid_t *list) {
673✔
900
        int r;
673✔
901

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

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

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

922
        return RET_NERRNO(setgroups(size, list));
674✔
923
}
924

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

935
        if (cache < 0)
88✔
936
                cache = access("/etc/systemd/dont-synthesize-nobody", F_OK) < 0;
63✔
937

938
        return cache;
88✔
939
}
940

941
int putpwent_sane(const struct passwd *pw, FILE *stream) {
412✔
942
        assert(pw);
412✔
943
        assert(stream);
412✔
944

945
        errno = 0;
412✔
946
        if (putpwent(pw, stream) != 0)
412✔
947
                return errno_or_else(EIO);
×
948

949
        return 0;
950
}
951

952
int putspent_sane(const struct spwd *sp, FILE *stream) {
382✔
953
        assert(sp);
382✔
954
        assert(stream);
382✔
955

956
        errno = 0;
382✔
957
        if (putspent(sp, stream) != 0)
382✔
958
                return errno_or_else(EIO);
×
959

960
        return 0;
961
}
962

963
int putgrent_sane(const struct group *gr, FILE *stream) {
768✔
964
        assert(gr);
768✔
965
        assert(stream);
768✔
966

967
        errno = 0;
768✔
968
        if (putgrent(gr, stream) != 0)
768✔
969
                return errno_or_else(EIO);
×
970

971
        return 0;
972
}
973

974
#if ENABLE_GSHADOW
975
int putsgent_sane(const struct sgrp *sg, FILE *stream) {
743✔
976
        assert(sg);
743✔
977
        assert(stream);
743✔
978

979
        errno = 0;
743✔
980
        if (putsgent(sg, stream) != 0)
743✔
981
                return errno_or_else(EIO);
×
982

983
        return 0;
984
}
985
#endif
986

987
int fgetpwent_sane(FILE *stream, struct passwd **pw) {
743✔
988
        assert(stream);
743✔
989
        assert(pw);
743✔
990

991
        errno = 0;
743✔
992
        struct passwd *p = fgetpwent(stream);
743✔
993
        if (!p && !IN_SET(errno, 0, ENOENT))
743✔
994
                return -errno;
×
995

996
        *pw = p;
743✔
997
        return !!p;
743✔
998
}
999

1000
int fgetspent_sane(FILE *stream, struct spwd **sp) {
216✔
1001
        assert(stream);
216✔
1002
        assert(sp);
216✔
1003

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

1009
        *sp = s;
216✔
1010
        return !!s;
216✔
1011
}
1012

1013
int fgetgrent_sane(FILE *stream, struct group **gr) {
1,339✔
1014
        assert(stream);
1,339✔
1015
        assert(gr);
1,339✔
1016

1017
        errno = 0;
1,339✔
1018
        struct group *g = fgetgrent(stream);
1,339✔
1019
        if (!g && !IN_SET(errno, 0, ENOENT))
1,339✔
1020
                return -errno;
×
1021

1022
        *gr = g;
1,339✔
1023
        return !!g;
1,339✔
1024
}
1025

1026
#if ENABLE_GSHADOW
1027
int fgetsgent_sane(FILE *stream, struct sgrp **sg) {
231✔
1028
        assert(stream);
231✔
1029
        assert(sg);
231✔
1030

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

1036
        *sg = s;
231✔
1037
        return !!s;
231✔
1038
}
1039
#endif
1040

1041
int is_this_me(const char *username) {
83✔
1042
        uid_t uid;
83✔
1043
        int r;
83✔
1044

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

1047
        r = get_user_creds(&username, &uid, NULL, NULL, NULL, USER_CREDS_ALLOW_MISSING);
83✔
1048
        if (r < 0)
83✔
1049
                return r;
83✔
1050

1051
        return uid == getuid();
82✔
1052
}
1053

1054
const char* get_home_root(void) {
5,801✔
1055
        const char *e;
5,801✔
1056

1057
        /* For debug purposes allow overriding where we look for home dirs */
1058
        e = secure_getenv("SYSTEMD_HOME_ROOT");
5,801✔
1059
        if (e && path_is_absolute(e) && path_is_normalized(e))
5,801✔
1060
                return e;
×
1061

1062
        return "/home";
1063
}
1064

1065
static size_t getpw_buffer_size(void) {
4,009✔
1066
        long bufsize = sysconf(_SC_GETPW_R_SIZE_MAX);
4,009✔
1067
        return bufsize <= 0 ? 4096U : (size_t) bufsize;
4,009✔
1068
}
1069

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

1076
int getpwnam_malloc(const char *name, struct passwd **ret) {
430✔
1077
        size_t bufsize = getpw_buffer_size();
430✔
1078
        int r;
430✔
1079

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

1083
        if (isempty(name))
860✔
1084
                return -EINVAL;
1085

1086
        for (;;) {
430✔
1087
                _cleanup_free_ void *buf = NULL;
430✔
1088

1089
                buf = malloc0(ALIGN(sizeof(struct passwd)) + bufsize);
430✔
1090
                if (!buf)
430✔
1091
                        return -ENOMEM;
1092

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

1102
                        return -ESRCH;
1103
                }
1104

1105
                assert(r > 0);
×
1106

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

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

1120
int getpwuid_malloc(uid_t uid, struct passwd **ret) {
3,579✔
1121
        size_t bufsize = getpw_buffer_size();
3,579✔
1122
        int r;
3,579✔
1123

1124
        if (!uid_is_valid(uid))
3,579✔
1125
                return -EINVAL;
1126

1127
        for (;;) {
3,579✔
1128
                _cleanup_free_ void *buf = NULL;
3,579✔
1129

1130
                buf = malloc0(ALIGN(sizeof(struct passwd)) + bufsize);
3,579✔
1131
                if (!buf)
3,579✔
1132
                        return -ENOMEM;
1133

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

1143
                        return -ESRCH;
1144
                }
1145

1146
                assert(r > 0);
×
1147

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

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

1159
static size_t getgr_buffer_size(void) {
7,484✔
1160
        long bufsize = sysconf(_SC_GETGR_R_SIZE_MAX);
7,484✔
1161
        return bufsize <= 0 ? 4096U : (size_t) bufsize;
7,484✔
1162
}
1163

1164
int getgrnam_malloc(const char *name, struct group **ret) {
2,005✔
1165
        size_t bufsize = getgr_buffer_size();
2,005✔
1166
        int r;
2,005✔
1167

1168
        if (isempty(name))
4,010✔
1169
                return -EINVAL;
1170

1171
        for (;;) {
2,005✔
1172
                _cleanup_free_ void *buf = NULL;
2,005✔
1173

1174
                buf = malloc0(ALIGN(sizeof(struct group)) + bufsize);
2,005✔
1175
                if (!buf)
2,005✔
1176
                        return -ENOMEM;
1177

1178
                struct group *gr = NULL;
2,005✔
1179
                r = getgrnam_r(name, buf, (char*) buf + ALIGN(sizeof(struct group)), (size_t) bufsize, &gr);
2,005✔
1180
                if (r == 0) {
2,005✔
1181
                        if (gr) {
2,004✔
1182
                                if (ret)
1,804✔
1183
                                        *ret = TAKE_PTR(buf);
1,804✔
1184
                                return 0;
1,804✔
1185
                        }
1186

1187
                        return -ESRCH;
1188
                }
1189

1190
                assert(r > 0);
1✔
1191

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

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

1203
int getgrgid_malloc(gid_t gid, struct group **ret) {
5,479✔
1204
        size_t bufsize = getgr_buffer_size();
5,479✔
1205
        int r;
5,479✔
1206

1207
        if (!gid_is_valid(gid))
5,479✔
1208
                return -EINVAL;
1209

1210
        for (;;) {
5,479✔
1211
                _cleanup_free_ void *buf = NULL;
5,479✔
1212

1213
                buf = malloc0(ALIGN(sizeof(struct group)) + bufsize);
5,479✔
1214
                if (!buf)
5,479✔
1215
                        return -ENOMEM;
1216

1217
                struct group *gr = NULL;
5,479✔
1218
                r = getgrgid_r(gid, buf, (char*) buf + ALIGN(sizeof(struct group)), (size_t) bufsize, &gr);
5,479✔
1219
                if (r == 0) {
5,479✔
1220
                        if (gr) {
5,479✔
1221
                                if (ret)
2,270✔
1222
                                        *ret = TAKE_PTR(buf);
2,270✔
1223
                                return 0;
2,270✔
1224
                        }
1225

1226
                        return -ESRCH;
1227
                }
1228

1229
                assert(r > 0);
×
1230

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

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