]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/nspawn/nspawn-setuid.c
tree-wide: use TAKE_PTR() and TAKE_FD() macros
[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"
5018c0c9 35#include "strv.h"
b1d4f8e1 36#include "user-util.h"
07630cea 37#include "util.h"
ee645080
LP
38
39static int spawn_getent(const char *database, const char *key, pid_t *rpid) {
4c253ed1 40 int pipe_fds[2], r;
ee645080
LP
41 pid_t pid;
42
43 assert(database);
44 assert(key);
45 assert(rpid);
46
47 if (pipe2(pipe_fds, O_CLOEXEC) < 0)
48 return log_error_errno(errno, "Failed to allocate pipe: %m");
49
b6e1fff1 50 r = safe_fork("(getent)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
05a8b330
LP
51 if (r < 0) {
52 safe_close_pair(pipe_fds);
b6e1fff1 53 return r;
05a8b330 54 }
4c253ed1 55 if (r == 0) {
ee645080
LP
56 char *empty_env = NULL;
57
2b33ab09 58 safe_close(pipe_fds[0]);
ee645080 59
2b33ab09 60 if (rearrange_stdio(-1, pipe_fds[1], -1) < 0)
ee645080
LP
61 _exit(EXIT_FAILURE);
62
ee645080
LP
63 close_all_fds(NULL, 0);
64
65 execle("/usr/bin/getent", "getent", database, key, NULL, &empty_env);
66 execle("/bin/getent", "getent", database, key, NULL, &empty_env);
67 _exit(EXIT_FAILURE);
68 }
69
70 pipe_fds[1] = safe_close(pipe_fds[1]);
71
72 *rpid = pid;
73
74 return pipe_fds[0];
75}
76
77int change_uid_gid(const char *user, char **_home) {
c5b82d86 78 char *x, *u, *g, *h;
ee645080
LP
79 const char *word, *state;
80 _cleanup_free_ uid_t *uids = NULL;
c5b82d86 81 _cleanup_free_ char *home = NULL, *line = NULL;
ee645080
LP
82 _cleanup_fclose_ FILE *f = NULL;
83 _cleanup_close_ int fd = -1;
84 unsigned n_uids = 0;
85 size_t sz = 0, l;
86 uid_t uid;
87 gid_t gid;
88 pid_t pid;
89 int r;
90
91 assert(_home);
92
5018c0c9 93 if (!user || STR_IN_SET(user, "root", "0")) {
ee645080
LP
94 /* Reset everything fully to 0, just in case */
95
96 r = reset_uid_gid();
97 if (r < 0)
98 return log_error_errno(r, "Failed to become root: %m");
99
100 *_home = NULL;
101 return 0;
102 }
103
104 /* First, get user credentials */
105 fd = spawn_getent("passwd", user, &pid);
106 if (fd < 0)
107 return fd;
108
c5b82d86 109 f = fdopen(fd, "re");
ee645080
LP
110 if (!f)
111 return log_oom();
112 fd = -1;
113
c5b82d86
LP
114 r = read_line(f, LONG_LINE_MAX, &line);
115 if (r == 0) {
116 log_error("Failed to resolve user %s.", user);
117 return -ESRCH;
ee645080 118 }
c5b82d86
LP
119 if (r < 0)
120 return log_error_errno(r, "Failed to read from getent: %m");
ee645080 121
7d4904fe 122 (void) wait_for_terminate_and_check("getent passwd", pid, WAIT_LOG);
ee645080
LP
123
124 x = strchr(line, ':');
125 if (!x) {
126 log_error("/etc/passwd entry has invalid user field.");
127 return -EIO;
128 }
129
130 u = strchr(x+1, ':');
131 if (!u) {
132 log_error("/etc/passwd entry has invalid password field.");
133 return -EIO;
134 }
135
136 u++;
137 g = strchr(u, ':');
138 if (!g) {
139 log_error("/etc/passwd entry has invalid UID field.");
140 return -EIO;
141 }
142
143 *g = 0;
144 g++;
145 x = strchr(g, ':');
146 if (!x) {
147 log_error("/etc/passwd entry has invalid GID field.");
148 return -EIO;
149 }
150
151 *x = 0;
152 h = strchr(x+1, ':');
153 if (!h) {
154 log_error("/etc/passwd entry has invalid GECOS field.");
155 return -EIO;
156 }
157
158 h++;
159 x = strchr(h, ':');
160 if (!x) {
161 log_error("/etc/passwd entry has invalid home directory field.");
162 return -EIO;
163 }
164
165 *x = 0;
166
167 r = parse_uid(u, &uid);
168 if (r < 0) {
169 log_error("Failed to parse UID of user.");
170 return -EIO;
171 }
172
173 r = parse_gid(g, &gid);
174 if (r < 0) {
175 log_error("Failed to parse GID of user.");
176 return -EIO;
177 }
178
179 home = strdup(h);
180 if (!home)
181 return log_oom();
182
c5b82d86
LP
183 f = safe_fclose(f);
184 line = mfree(line);
185
ee645080
LP
186 /* Second, get group memberships */
187 fd = spawn_getent("initgroups", user, &pid);
188 if (fd < 0)
189 return fd;
190
c5b82d86 191 f = fdopen(fd, "re");
ee645080
LP
192 if (!f)
193 return log_oom();
194 fd = -1;
195
c5b82d86
LP
196 r = read_line(f, LONG_LINE_MAX, &line);
197 if (r == 0) {
198 log_error("Failed to resolve user %s.", user);
199 return -ESRCH;
ee645080 200 }
c5b82d86
LP
201 if (r < 0)
202 return log_error_errno(r, "Failed to read from getent: %m");
ee645080 203
7d4904fe 204 (void) wait_for_terminate_and_check("getent initgroups", pid, WAIT_LOG);
ee645080
LP
205
206 /* Skip over the username and subsequent separator whitespace */
207 x = line;
208 x += strcspn(x, WHITESPACE);
209 x += strspn(x, WHITESPACE);
210
211 FOREACH_WORD(word, l, x, state) {
212 char c[l+1];
213
214 memcpy(c, word, l);
215 c[l] = 0;
216
217 if (!GREEDY_REALLOC(uids, sz, n_uids+1))
218 return log_oom();
219
220 r = parse_uid(c, &uids[n_uids++]);
c7f9a8d2
LP
221 if (r < 0)
222 return log_error_errno(r, "Failed to parse group data from getent: %m");
ee645080
LP
223 }
224
225 r = mkdir_parents(home, 0775);
226 if (r < 0)
227 return log_error_errno(r, "Failed to make home root directory: %m");
228
d50b5839 229 r = mkdir_safe(home, 0755, uid, gid, 0);
37c1d5e9 230 if (r < 0 && !IN_SET(r, -EEXIST, -ENOTDIR))
ee645080
LP
231 return log_error_errno(r, "Failed to make home directory: %m");
232
233 (void) fchown(STDIN_FILENO, uid, gid);
234 (void) fchown(STDOUT_FILENO, uid, gid);
235 (void) fchown(STDERR_FILENO, uid, gid);
236
237 if (setgroups(n_uids, uids) < 0)
238 return log_error_errno(errno, "Failed to set auxiliary groups: %m");
239
240 if (setresgid(gid, gid, gid) < 0)
d2b8497d 241 return log_error_errno(errno, "setresgid() failed: %m");
ee645080
LP
242
243 if (setresuid(uid, uid, uid) < 0)
d2b8497d 244 return log_error_errno(errno, "setresuid() failed: %m");
ee645080 245
1cc6c93a
YW
246 if (_home)
247 *_home = TAKE_PTR(home);
ee645080
LP
248
249 return 0;
250}