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

systemd / systemd / 14895667988

07 May 2025 08:57PM UTC coverage: 72.225% (-0.007%) from 72.232%
14895667988

push

github

yuwata
network: log_link_message_debug_errno() automatically append %m if necessary

Follow-up for d28746ef5.
Fixes CID#1609753.

0 of 1 new or added line in 1 file covered. (0.0%)

20297 existing lines in 338 files now uncovered.

297407 of 411780 relevant lines covered (72.22%)

695716.85 hits per line

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

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

3
#include <fcntl.h>
4
#include <sys/types.h>
5
#include <unistd.h>
6

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

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

26
        assert(database);
6✔
27
        assert(key);
6✔
28
        assert(rpid);
6✔
29

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

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

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

49
        *rpid = pid;
6✔
50

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

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

61
        int r;
3✔
62

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

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

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

78
        return 0;
79
}
80

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

93
        assert(ret_home);
106✔
94

95
        if (!user || STR_IN_SET(user, "root", "0")) {
106✔
96
                /* Reset everything fully to 0, just in case */
97

98
                r = reset_uid_gid();
103✔
99
                if (r < 0)
103✔
100
                        return log_error_errno(r, "Failed to become root: %m");
103✔
101

102
                *ret_home = NULL;
103✔
103
                return 0;
103✔
104
        }
105

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

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

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

122
        (void) wait_for_terminate_and_check("getent passwd", pid, WAIT_LOG);
3✔
123

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

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

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

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

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

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

159
        *x = 0;
3✔
160

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

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

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

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

178
        /* Second, get group memberships */
179
        fd = spawn_getent("initgroups", user, &pid);
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✔
UNCOV
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) wait_for_terminate_and_check("getent initgroups", pid, 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))
×
UNCOV
211
                        return log_oom();
×
212

213
                r = parse_gid(word, &gids[n_gids++]);
×
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;
3✔
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