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