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

systemd / systemd / 20401947236

20 Dec 2025 09:56PM UTC coverage: 72.701% (+0.1%) from 72.578%
20401947236

push

github

DaanDeMeyer
core/socket: modernize listen/accept_in_cgroup

4 of 9 new or added lines in 1 file covered. (44.44%)

7723 existing lines in 114 files now uncovered.

309972 of 426363 relevant lines covered (72.7%)

1133403.64 hits per line

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

78.79
/src/nspawn/nspawn-setuid.c
1
/* SPDX-License-Identifier: LGPL-2.1-or-later */
2

3
#include <fcntl.h>
4
#include <unistd.h>
5

6
#include "alloc-util.h"
7
#include "errno.h"
8
#include "extract-word.h"
9
#include "fd-util.h"
10
#include "fileio.h"
11
#include "log.h"
12
#include "mkdir.h"
13
#include "nspawn-setuid.h"
14
#include "pidref.h"
15
#include "process-util.h"
16
#include "string-util.h"
17
#include "strv.h"
18
#include "user-util.h"
19

20
static int spawn_getent(const char *database, const char *key, PidRef *ret) {
6✔
21
        int pipe_fds[2], r;
6✔
22

23
        assert(database);
6✔
24
        assert(key);
6✔
25
        assert(ret);
6✔
26

27
        if (pipe2(pipe_fds, O_CLOEXEC) < 0)
6✔
28
                return log_error_errno(errno, "Failed to allocate pipe: %m");
6✔
29

30
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
6✔
31
        r = pidref_safe_fork_full(
18✔
32
                        "(getent)",
33
                        (int[]) { -EBADF, pipe_fds[1], -EBADF }, NULL, 0,
6✔
34
                        FORK_RESET_SIGNALS|FORK_CLOSE_ALL_FDS|FORK_DEATHSIG_SIGTERM|FORK_REARRANGE_STDIO|FORK_LOG|FORK_RLIMIT_NOFILE_SAFE,
35
                        &pidref);
36
        if (r < 0) {
12✔
UNCOV
37
                safe_close_pair(pipe_fds);
×
38
                return r;
39
        }
40
        if (r == 0) {
12✔
41
                execle("/usr/bin/getent", "getent", database, key, NULL, &(char*[1]){});
6✔
42
                execle("/bin/getent", "getent", database, key, NULL, &(char*[1]){});
6✔
43
                _exit(EXIT_FAILURE);
6✔
44
        }
45

46
        pipe_fds[1] = safe_close(pipe_fds[1]);
6✔
47

48
        *ret = TAKE_PIDREF(pidref);
6✔
49

50
        return pipe_fds[0];
6✔
51
}
52

53
int change_uid_gid_raw(
3✔
54
                uid_t uid,
55
                gid_t gid,
56
                const gid_t *supplementary_gids,
57
                size_t n_supplementary_gids,
58
                bool chown_stdio) {
59

60
        int r;
3✔
61

62
        if (!uid_is_valid(uid))
3✔
63
                uid = 0;
×
64
        if (!gid_is_valid(gid))
3✔
UNCOV
65
                gid = 0;
×
66

67
        if (chown_stdio) {
3✔
68
                (void) fchown(STDIN_FILENO, uid, gid);
3✔
69
                (void) fchown(STDOUT_FILENO, uid, gid);
3✔
70
                (void) fchown(STDERR_FILENO, uid, gid);
3✔
71
        }
72

73
        r = fully_set_uid_gid(uid, gid, supplementary_gids, n_supplementary_gids);
3✔
74
        if (r < 0)
3✔
UNCOV
75
                return log_error_errno(r, "Changing privileges failed: %m");
×
76

77
        return 0;
78
}
79

80
int change_uid_gid(const char *user, bool chown_stdio, char **ret_home) {
119✔
81
        char *x, *u, *g, *h;
119✔
82
        _cleanup_free_ gid_t *gids = NULL;
238✔
83
        _cleanup_free_ char *home = NULL, *line = NULL;
119✔
84
        _cleanup_fclose_ FILE *f = NULL;
119✔
85
        _cleanup_close_ int fd = -EBADF;
119✔
86
        unsigned n_gids = 0;
119✔
87
        uid_t uid;
119✔
88
        gid_t gid;
119✔
89
        int r;
119✔
90

91
        assert(ret_home);
119✔
92

93
        if (!user || STR_IN_SET(user, "root", "0")) {
119✔
94
                /* Reset everything fully to 0, just in case */
95

96
                r = reset_uid_gid();
116✔
97
                if (r < 0)
116✔
98
                        return log_error_errno(r, "Failed to become root: %m");
116✔
99

100
                *ret_home = NULL;
116✔
101
                return 0;
116✔
102
        }
103

104
        /* First, get user credentials */
105
        _cleanup_(pidref_done) PidRef pidref = PIDREF_NULL;
3✔
106
        fd = spawn_getent("passwd", user, &pidref);
3✔
107
        if (fd < 0)
3✔
108
                return fd;
109

110
        f = take_fdopen(&fd, "r");
3✔
111
        if (!f)
3✔
UNCOV
112
                return log_oom();
×
113

114
        r = read_line(f, LONG_LINE_MAX, &line);
3✔
115
        if (r == 0)
3✔
UNCOV
116
                return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
×
117
                                       "Failed to resolve user %s.", user);
118
        if (r < 0)
3✔
UNCOV
119
                return log_error_errno(r, "Failed to read from getent: %m");
×
120

121
        (void) pidref_wait_for_terminate_and_check("getent passwd", &pidref, WAIT_LOG);
3✔
122

123
        x = strchr(line, ':');
3✔
124
        if (!x)
3✔
UNCOV
125
                return log_error_errno(SYNTHETIC_ERRNO(EIO),
×
126
                                       "/etc/passwd entry has invalid user field.");
127

128
        u = strchr(x+1, ':');
3✔
129
        if (!u)
3✔
UNCOV
130
                return log_error_errno(SYNTHETIC_ERRNO(EIO),
×
131
                                       "/etc/passwd entry has invalid password field.");
132

133
        u++;
3✔
134
        g = strchr(u, ':');
3✔
135
        if (!g)
3✔
UNCOV
136
                return log_error_errno(SYNTHETIC_ERRNO(EIO),
×
137
                                       "/etc/passwd entry has invalid UID field.");
138

139
        *g = 0;
3✔
140
        g++;
3✔
141
        x = strchr(g, ':');
3✔
142
        if (!x)
3✔
UNCOV
143
                return log_error_errno(SYNTHETIC_ERRNO(EIO),
×
144
                                       "/etc/passwd entry has invalid GID field.");
145

146
        *x = 0;
3✔
147
        h = strchr(x+1, ':');
3✔
148
        if (!h)
3✔
UNCOV
149
                return log_error_errno(SYNTHETIC_ERRNO(EIO),
×
150
                                       "/etc/passwd entry has invalid GECOS field.");
151

152
        h++;
3✔
153
        x = strchr(h, ':');
3✔
154
        if (!x)
3✔
UNCOV
155
                return log_error_errno(SYNTHETIC_ERRNO(EIO),
×
156
                                       "/etc/passwd entry has invalid home directory field.");
157

158
        *x = 0;
3✔
159

160
        r = parse_uid(u, &uid);
3✔
161
        if (r < 0)
3✔
UNCOV
162
                return log_error_errno(SYNTHETIC_ERRNO(EIO),
×
163
                                       "Failed to parse UID of user.");
164

165
        r = parse_gid(g, &gid);
3✔
166
        if (r < 0)
3✔
UNCOV
167
                return log_error_errno(SYNTHETIC_ERRNO(EIO),
×
168
                                       "Failed to parse GID of user.");
169

170
        home = strdup(h);
3✔
171
        if (!home)
3✔
UNCOV
172
                return log_oom();
×
173

174
        f = safe_fclose(f);
3✔
175
        line = mfree(line);
3✔
176

177
        /* Second, get group memberships */
178
        pidref_done(&pidref);
3✔
179
        fd = spawn_getent("initgroups", user, &pidref);
3✔
180
        if (fd < 0)
3✔
181
                return fd;
182

183
        f = take_fdopen(&fd, "r");
3✔
184
        if (!f)
3✔
UNCOV
185
                return log_oom();
×
186

187
        r = read_line(f, LONG_LINE_MAX, &line);
3✔
188
        if (r == 0)
3✔
189
                return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
×
190
                                       "Failed to resolve user %s.", user);
191
        if (r < 0)
3✔
UNCOV
192
                return log_error_errno(r, "Failed to read from getent: %m");
×
193

194
        (void) pidref_wait_for_terminate_and_check("getent initgroups", &pidref, WAIT_LOG);
3✔
195

196
        /* Skip over the username and subsequent separator whitespace */
197
        x = line;
3✔
198
        x += strcspn(x, WHITESPACE);
3✔
199
        x += strspn(x, WHITESPACE);
3✔
200

201
        for (const char *p = x;;) {
3✔
UNCOV
202
               _cleanup_free_ char *word = NULL;
×
203

204
                r = extract_first_word(&p, &word, NULL, 0);
3✔
205
                if (r < 0)
3✔
UNCOV
206
                        return log_error_errno(r, "Failed to parse group data from getent: %m");
×
207
                if (r == 0)
3✔
208
                        break;
209

210
                if (!GREEDY_REALLOC(gids, n_gids+1))
×
211
                        return log_oom();
×
212

UNCOV
213
                r = parse_gid(word, &gids[n_gids++]);
×
UNCOV
214
                if (r < 0)
×
UNCOV
215
                        return log_error_errno(r, "Failed to parse group data from getent: %m");
×
216
        }
217

218
        r = mkdir_parents(home, 0775);
3✔
219
        if (r < 0)
3✔
UNCOV
220
                return log_error_errno(r, "Failed to make home root directory: %m");
×
221

222
        r = mkdir_safe(home, 0755, uid, gid, 0);
3✔
223
        if (r < 0 && !IN_SET(r, -EEXIST, -ENOTDIR))
3✔
UNCOV
224
                return log_error_errno(r, "Failed to make home directory: %m");
×
225

226
        r = change_uid_gid_raw(uid, gid, gids, n_gids, chown_stdio);
3✔
227
        if (r < 0)
3✔
228
                return r;
229

230
        if (ret_home)
3✔
231
                *ret_home = TAKE_PTR(home);
3✔
232

233
        return 0;
234
}
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