]> git.ipfire.org Git - thirdparty/systemd.git/blob - src/nspawn/nspawn-setuid.c
ethtool: add several new link modes
[thirdparty/systemd.git] / src / nspawn / nspawn-setuid.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
2
3 #include <fcntl.h>
4 #include <sys/types.h>
5 #include <unistd.h>
6
7 #include "alloc-util.h"
8 #include "def.h"
9 #include "errno.h"
10 #include "fd-util.h"
11 #include "fileio.h"
12 #include "mkdir.h"
13 #include "nspawn-setuid.h"
14 #include "process-util.h"
15 #include "rlimit-util.h"
16 #include "signal-util.h"
17 #include "string-util.h"
18 #include "strv.h"
19 #include "user-util.h"
20 #include "util.h"
21
22 static int spawn_getent(const char *database, const char *key, pid_t *rpid) {
23 int pipe_fds[2], r;
24 pid_t pid;
25
26 assert(database);
27 assert(key);
28 assert(rpid);
29
30 if (pipe2(pipe_fds, O_CLOEXEC) < 0)
31 return log_error_errno(errno, "Failed to allocate pipe: %m");
32
33 r = safe_fork("(getent)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
34 if (r < 0) {
35 safe_close_pair(pipe_fds);
36 return r;
37 }
38 if (r == 0) {
39 char *empty_env = NULL;
40
41 safe_close(pipe_fds[0]);
42
43 if (rearrange_stdio(-1, pipe_fds[1], -1) < 0)
44 _exit(EXIT_FAILURE);
45
46 (void) close_all_fds(NULL, 0);
47
48 (void) rlimit_nofile_safe();
49
50 execle("/usr/bin/getent", "getent", database, key, NULL, &empty_env);
51 execle("/bin/getent", "getent", database, key, NULL, &empty_env);
52 _exit(EXIT_FAILURE);
53 }
54
55 pipe_fds[1] = safe_close(pipe_fds[1]);
56
57 *rpid = pid;
58
59 return pipe_fds[0];
60 }
61
62 int change_uid_gid_raw(
63 uid_t uid,
64 gid_t gid,
65 const gid_t *supplementary_gids,
66 size_t n_supplementary_gids) {
67
68 if (!uid_is_valid(uid))
69 uid = 0;
70 if (!gid_is_valid(gid))
71 gid = 0;
72
73 (void) fchown(STDIN_FILENO, uid, gid);
74 (void) fchown(STDOUT_FILENO, uid, gid);
75 (void) fchown(STDERR_FILENO, uid, gid);
76
77 if (setgroups(n_supplementary_gids, supplementary_gids) < 0)
78 return log_error_errno(errno, "Failed to set auxiliary groups: %m");
79
80 if (setresgid(gid, gid, gid) < 0)
81 return log_error_errno(errno, "setresgid() failed: %m");
82
83 if (setresuid(uid, uid, uid) < 0)
84 return log_error_errno(errno, "setresuid() failed: %m");
85
86 return 0;
87 }
88
89 int change_uid_gid(const char *user, char **_home) {
90 char *x, *u, *g, *h;
91 _cleanup_free_ gid_t *gids = NULL;
92 _cleanup_free_ char *home = NULL, *line = NULL;
93 _cleanup_fclose_ FILE *f = NULL;
94 _cleanup_close_ int fd = -1;
95 unsigned n_gids = 0;
96 size_t sz = 0;
97 uid_t uid;
98 gid_t gid;
99 pid_t pid;
100 int r;
101
102 assert(_home);
103
104 if (!user || STR_IN_SET(user, "root", "0")) {
105 /* Reset everything fully to 0, just in case */
106
107 r = reset_uid_gid();
108 if (r < 0)
109 return log_error_errno(r, "Failed to become root: %m");
110
111 *_home = NULL;
112 return 0;
113 }
114
115 /* First, get user credentials */
116 fd = spawn_getent("passwd", user, &pid);
117 if (fd < 0)
118 return fd;
119
120 f = take_fdopen(&fd, "r");
121 if (!f)
122 return log_oom();
123
124 r = read_line(f, LONG_LINE_MAX, &line);
125 if (r == 0)
126 return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
127 "Failed to resolve user %s.", user);
128 if (r < 0)
129 return log_error_errno(r, "Failed to read from getent: %m");
130
131 (void) wait_for_terminate_and_check("getent passwd", pid, WAIT_LOG);
132
133 x = strchr(line, ':');
134 if (!x)
135 return log_error_errno(SYNTHETIC_ERRNO(EIO),
136 "/etc/passwd entry has invalid user field.");
137
138 u = strchr(x+1, ':');
139 if (!u)
140 return log_error_errno(SYNTHETIC_ERRNO(EIO),
141 "/etc/passwd entry has invalid password field.");
142
143 u++;
144 g = strchr(u, ':');
145 if (!g)
146 return log_error_errno(SYNTHETIC_ERRNO(EIO),
147 "/etc/passwd entry has invalid UID field.");
148
149 *g = 0;
150 g++;
151 x = strchr(g, ':');
152 if (!x)
153 return log_error_errno(SYNTHETIC_ERRNO(EIO),
154 "/etc/passwd entry has invalid GID field.");
155
156 *x = 0;
157 h = strchr(x+1, ':');
158 if (!h)
159 return log_error_errno(SYNTHETIC_ERRNO(EIO),
160 "/etc/passwd entry has invalid GECOS field.");
161
162 h++;
163 x = strchr(h, ':');
164 if (!x)
165 return log_error_errno(SYNTHETIC_ERRNO(EIO),
166 "/etc/passwd entry has invalid home directory field.");
167
168 *x = 0;
169
170 r = parse_uid(u, &uid);
171 if (r < 0)
172 return log_error_errno(SYNTHETIC_ERRNO(EIO),
173 "Failed to parse UID of user.");
174
175 r = parse_gid(g, &gid);
176 if (r < 0)
177 return log_error_errno(SYNTHETIC_ERRNO(EIO),
178 "Failed to parse GID of user.");
179
180 home = strdup(h);
181 if (!home)
182 return log_oom();
183
184 f = safe_fclose(f);
185 line = mfree(line);
186
187 /* Second, get group memberships */
188 fd = spawn_getent("initgroups", user, &pid);
189 if (fd < 0)
190 return fd;
191
192 f = take_fdopen(&fd, "r");
193 if (!f)
194 return log_oom();
195
196 r = read_line(f, LONG_LINE_MAX, &line);
197 if (r == 0)
198 return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
199 "Failed to resolve user %s.", user);
200 if (r < 0)
201 return log_error_errno(r, "Failed to read from getent: %m");
202
203 (void) wait_for_terminate_and_check("getent initgroups", pid, WAIT_LOG);
204
205 /* Skip over the username and subsequent separator whitespace */
206 x = line;
207 x += strcspn(x, WHITESPACE);
208 x += strspn(x, WHITESPACE);
209
210 for (const char *p = x;;) {
211 _cleanup_free_ char *word = NULL;
212
213 r = extract_first_word(&p, &word, NULL, 0);
214 if (r < 0)
215 return log_error_errno(r, "Failed to parse group data from getent: %m");
216 if (r == 0)
217 break;
218
219 if (!GREEDY_REALLOC(gids, sz, n_gids+1))
220 return log_oom();
221
222 r = parse_gid(word, &gids[n_gids++]);
223 if (r < 0)
224 return log_error_errno(r, "Failed to parse group data from getent: %m");
225 }
226
227 r = mkdir_parents(home, 0775);
228 if (r < 0)
229 return log_error_errno(r, "Failed to make home root directory: %m");
230
231 r = mkdir_safe(home, 0755, uid, gid, 0);
232 if (r < 0 && !IN_SET(r, -EEXIST, -ENOTDIR))
233 return log_error_errno(r, "Failed to make home directory: %m");
234
235 r = change_uid_gid_raw(uid, gid, gids, n_gids);
236 if (r < 0)
237 return r;
238
239 if (_home)
240 *_home = TAKE_PTR(home);
241
242 return 0;
243 }