]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/nspawn/nspawn-setuid.c
nspawn: port some code to use read_line()
[thirdparty/systemd.git] / src / nspawn / nspawn-setuid.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
ee645080
LP
2/***
3 This file is part of systemd.
4
5 Copyright 2015 Lennart Poettering
6
7 systemd is free software; you can redistribute it and/or modify it
8 under the terms of the GNU Lesser General Public License as published by
9 the Free Software Foundation; either version 2.1 of the License, or
10 (at your option) any later version.
11
12 systemd is distributed in the hope that it will be useful, but
13 WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public License
18 along with systemd; If not, see <http://www.gnu.org/licenses/>.
19***/
20
07630cea 21#include <grp.h>
ee645080
LP
22#include <sys/types.h>
23#include <unistd.h>
ee645080 24
b5efdb8a 25#include "alloc-util.h"
c5b82d86 26#include "def.h"
dccca82b 27#include "errno.h"
3ffd4af2 28#include "fd-util.h"
c5b82d86 29#include "fileio.h"
ee645080 30#include "mkdir.h"
3ffd4af2 31#include "nspawn-setuid.h"
ee645080 32#include "process-util.h"
07630cea
LP
33#include "signal-util.h"
34#include "string-util.h"
b1d4f8e1 35#include "user-util.h"
07630cea 36#include "util.h"
ee645080
LP
37
38static int spawn_getent(const char *database, const char *key, pid_t *rpid) {
4c253ed1 39 int pipe_fds[2], r;
ee645080
LP
40 pid_t pid;
41
42 assert(database);
43 assert(key);
44 assert(rpid);
45
46 if (pipe2(pipe_fds, O_CLOEXEC) < 0)
47 return log_error_errno(errno, "Failed to allocate pipe: %m");
48
b6e1fff1 49 r = safe_fork("(getent)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
4c253ed1 50 if (r < 0)
b6e1fff1 51 return r;
4c253ed1 52 if (r == 0) {
ee645080
LP
53 int nullfd;
54 char *empty_env = NULL;
55
56 if (dup3(pipe_fds[1], STDOUT_FILENO, 0) < 0)
57 _exit(EXIT_FAILURE);
58
59 if (pipe_fds[0] > 2)
60 safe_close(pipe_fds[0]);
61 if (pipe_fds[1] > 2)
62 safe_close(pipe_fds[1]);
63
64 nullfd = open("/dev/null", O_RDWR);
65 if (nullfd < 0)
66 _exit(EXIT_FAILURE);
67
68 if (dup3(nullfd, STDIN_FILENO, 0) < 0)
69 _exit(EXIT_FAILURE);
70
71 if (dup3(nullfd, STDERR_FILENO, 0) < 0)
72 _exit(EXIT_FAILURE);
73
74 if (nullfd > 2)
75 safe_close(nullfd);
76
ee645080
LP
77 close_all_fds(NULL, 0);
78
79 execle("/usr/bin/getent", "getent", database, key, NULL, &empty_env);
80 execle("/bin/getent", "getent", database, key, NULL, &empty_env);
81 _exit(EXIT_FAILURE);
82 }
83
84 pipe_fds[1] = safe_close(pipe_fds[1]);
85
86 *rpid = pid;
87
88 return pipe_fds[0];
89}
90
91int change_uid_gid(const char *user, char **_home) {
c5b82d86 92 char *x, *u, *g, *h;
ee645080
LP
93 const char *word, *state;
94 _cleanup_free_ uid_t *uids = NULL;
c5b82d86 95 _cleanup_free_ char *home = NULL, *line = NULL;
ee645080
LP
96 _cleanup_fclose_ FILE *f = NULL;
97 _cleanup_close_ int fd = -1;
98 unsigned n_uids = 0;
99 size_t sz = 0, l;
100 uid_t uid;
101 gid_t gid;
102 pid_t pid;
103 int r;
104
105 assert(_home);
106
107 if (!user || streq(user, "root") || streq(user, "0")) {
108 /* Reset everything fully to 0, just in case */
109
110 r = reset_uid_gid();
111 if (r < 0)
112 return log_error_errno(r, "Failed to become root: %m");
113
114 *_home = NULL;
115 return 0;
116 }
117
118 /* First, get user credentials */
119 fd = spawn_getent("passwd", user, &pid);
120 if (fd < 0)
121 return fd;
122
c5b82d86 123 f = fdopen(fd, "re");
ee645080
LP
124 if (!f)
125 return log_oom();
126 fd = -1;
127
c5b82d86
LP
128 r = read_line(f, LONG_LINE_MAX, &line);
129 if (r == 0) {
130 log_error("Failed to resolve user %s.", user);
131 return -ESRCH;
ee645080 132 }
c5b82d86
LP
133 if (r < 0)
134 return log_error_errno(r, "Failed to read from getent: %m");
ee645080 135
7d4904fe 136 (void) wait_for_terminate_and_check("getent passwd", pid, WAIT_LOG);
ee645080
LP
137
138 x = strchr(line, ':');
139 if (!x) {
140 log_error("/etc/passwd entry has invalid user field.");
141 return -EIO;
142 }
143
144 u = strchr(x+1, ':');
145 if (!u) {
146 log_error("/etc/passwd entry has invalid password field.");
147 return -EIO;
148 }
149
150 u++;
151 g = strchr(u, ':');
152 if (!g) {
153 log_error("/etc/passwd entry has invalid UID field.");
154 return -EIO;
155 }
156
157 *g = 0;
158 g++;
159 x = strchr(g, ':');
160 if (!x) {
161 log_error("/etc/passwd entry has invalid GID field.");
162 return -EIO;
163 }
164
165 *x = 0;
166 h = strchr(x+1, ':');
167 if (!h) {
168 log_error("/etc/passwd entry has invalid GECOS field.");
169 return -EIO;
170 }
171
172 h++;
173 x = strchr(h, ':');
174 if (!x) {
175 log_error("/etc/passwd entry has invalid home directory field.");
176 return -EIO;
177 }
178
179 *x = 0;
180
181 r = parse_uid(u, &uid);
182 if (r < 0) {
183 log_error("Failed to parse UID of user.");
184 return -EIO;
185 }
186
187 r = parse_gid(g, &gid);
188 if (r < 0) {
189 log_error("Failed to parse GID of user.");
190 return -EIO;
191 }
192
193 home = strdup(h);
194 if (!home)
195 return log_oom();
196
c5b82d86
LP
197 f = safe_fclose(f);
198 line = mfree(line);
199
ee645080
LP
200 /* Second, get group memberships */
201 fd = spawn_getent("initgroups", user, &pid);
202 if (fd < 0)
203 return fd;
204
c5b82d86 205 f = fdopen(fd, "re");
ee645080
LP
206 if (!f)
207 return log_oom();
208 fd = -1;
209
c5b82d86
LP
210 r = read_line(f, LONG_LINE_MAX, &line);
211 if (r == 0) {
212 log_error("Failed to resolve user %s.", user);
213 return -ESRCH;
ee645080 214 }
c5b82d86
LP
215 if (r < 0)
216 return log_error_errno(r, "Failed to read from getent: %m");
ee645080 217
7d4904fe 218 (void) wait_for_terminate_and_check("getent initgroups", pid, WAIT_LOG);
ee645080
LP
219
220 /* Skip over the username and subsequent separator whitespace */
221 x = line;
222 x += strcspn(x, WHITESPACE);
223 x += strspn(x, WHITESPACE);
224
225 FOREACH_WORD(word, l, x, state) {
226 char c[l+1];
227
228 memcpy(c, word, l);
229 c[l] = 0;
230
231 if (!GREEDY_REALLOC(uids, sz, n_uids+1))
232 return log_oom();
233
234 r = parse_uid(c, &uids[n_uids++]);
235 if (r < 0) {
236 log_error("Failed to parse group data from getent.");
237 return -EIO;
238 }
239 }
240
241 r = mkdir_parents(home, 0775);
242 if (r < 0)
243 return log_error_errno(r, "Failed to make home root directory: %m");
244
c31ad024 245 r = mkdir_safe(home, 0755, uid, gid, false);
ee645080
LP
246 if (r < 0 && r != -EEXIST)
247 return log_error_errno(r, "Failed to make home directory: %m");
248
249 (void) fchown(STDIN_FILENO, uid, gid);
250 (void) fchown(STDOUT_FILENO, uid, gid);
251 (void) fchown(STDERR_FILENO, uid, gid);
252
253 if (setgroups(n_uids, uids) < 0)
254 return log_error_errno(errno, "Failed to set auxiliary groups: %m");
255
256 if (setresgid(gid, gid, gid) < 0)
d2b8497d 257 return log_error_errno(errno, "setresgid() failed: %m");
ee645080
LP
258
259 if (setresuid(uid, uid, uid) < 0)
d2b8497d 260 return log_error_errno(errno, "setresuid() failed: %m");
ee645080
LP
261
262 if (_home) {
263 *_home = home;
264 home = NULL;
265 }
266
267 return 0;
268}