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

systemd / systemd / 15175033720

21 May 2025 10:22PM UTC coverage: 72.079% (+0.03%) from 72.047%
15175033720

push

github

web-flow
Several minor follow-ups for #33995 (#37558)

3 of 3 new or added lines in 1 file covered. (100.0%)

34302 existing lines in 652 files now uncovered.

299232 of 415142 relevant lines covered (72.08%)

700018.81 hits per line

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

77.27
/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 "process-util.h"
15
#include "string-util.h"
16
#include "strv.h"
17
#include "user-util.h"
18

19
static int spawn_getent(const char *database, const char *key, pid_t *rpid) {
6✔
20
        int pipe_fds[2], r;
6✔
21
        pid_t pid;
6✔
22

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

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

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

44
        pipe_fds[1] = safe_close(pipe_fds[1]);
6✔
45

46
        *rpid = pid;
6✔
47

48
        return pipe_fds[0];
6✔
49
}
50

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

58
        int r;
3✔
59

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

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

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

75
        return 0;
76
}
77

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

90
        assert(ret_home);
107✔
91

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

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

99
                *ret_home = NULL;
104✔
100
                return 0;
104✔
101
        }
102

103
        /* First, get user credentials */
104
        fd = spawn_getent("passwd", user, &pid);
3✔
105
        if (fd < 0)
3✔
106
                return fd;
107

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

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

119
        (void) wait_for_terminate_and_check("getent passwd", pid, WAIT_LOG);
3✔
120

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

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

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

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

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

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

156
        *x = 0;
3✔
157

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

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

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

172
        f = safe_fclose(f);
3✔
173
        line = mfree(line);
3✔
174

175
        /* Second, get group memberships */
176
        fd = spawn_getent("initgroups", user, &pid);
3✔
177
        if (fd < 0)
3✔
178
                return fd;
179

180
        f = take_fdopen(&fd, "r");
3✔
181
        if (!f)
3✔
UNCOV
182
                return log_oom();
×
183

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

191
        (void) wait_for_terminate_and_check("getent initgroups", pid, WAIT_LOG);
3✔
192

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

198
        for (const char *p = x;;) {
3✔
UNCOV
199
               _cleanup_free_ char *word = NULL;
×
200

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

UNCOV
207
                if (!GREEDY_REALLOC(gids, n_gids+1))
×
UNCOV
208
                        return log_oom();
×
209

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

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

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

223
        r = change_uid_gid_raw(uid, gid, gids, n_gids, chown_stdio);
3✔
224
        if (r < 0)
3✔
225
                return r;
226

227
        if (ret_home)
3✔
228
                *ret_home = TAKE_PTR(home);
3✔
229

230
        return 0;
231
}
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