]> git.ipfire.org Git - thirdparty/systemd.git/blame - src/nspawn/nspawn-setuid.c
Merge pull request #11827 from keszybz/pkgconfig-variables
[thirdparty/systemd.git] / src / nspawn / nspawn-setuid.c
CommitLineData
53e1b683 1/* SPDX-License-Identifier: LGPL-2.1+ */
ee645080 2
07630cea 3#include <grp.h>
ee645080
LP
4#include <sys/types.h>
5#include <unistd.h>
ee645080 6
b5efdb8a 7#include "alloc-util.h"
c5b82d86 8#include "def.h"
dccca82b 9#include "errno.h"
3ffd4af2 10#include "fd-util.h"
c5b82d86 11#include "fileio.h"
ee645080 12#include "mkdir.h"
3ffd4af2 13#include "nspawn-setuid.h"
ee645080 14#include "process-util.h"
595225af 15#include "rlimit-util.h"
07630cea
LP
16#include "signal-util.h"
17#include "string-util.h"
5018c0c9 18#include "strv.h"
b1d4f8e1 19#include "user-util.h"
07630cea 20#include "util.h"
ee645080
LP
21
22static int spawn_getent(const char *database, const char *key, pid_t *rpid) {
4c253ed1 23 int pipe_fds[2], r;
ee645080
LP
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
b6e1fff1 33 r = safe_fork("(getent)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
05a8b330
LP
34 if (r < 0) {
35 safe_close_pair(pipe_fds);
b6e1fff1 36 return r;
05a8b330 37 }
4c253ed1 38 if (r == 0) {
ee645080
LP
39 char *empty_env = NULL;
40
2b33ab09 41 safe_close(pipe_fds[0]);
ee645080 42
2b33ab09 43 if (rearrange_stdio(-1, pipe_fds[1], -1) < 0)
ee645080
LP
44 _exit(EXIT_FAILURE);
45
ee645080
LP
46 close_all_fds(NULL, 0);
47
595225af
LP
48 (void) rlimit_nofile_safe();
49
ee645080
LP
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
62int change_uid_gid(const char *user, char **_home) {
c5b82d86 63 char *x, *u, *g, *h;
ee645080
LP
64 const char *word, *state;
65 _cleanup_free_ uid_t *uids = NULL;
c5b82d86 66 _cleanup_free_ char *home = NULL, *line = NULL;
ee645080
LP
67 _cleanup_fclose_ FILE *f = NULL;
68 _cleanup_close_ int fd = -1;
69 unsigned n_uids = 0;
70 size_t sz = 0, l;
71 uid_t uid;
72 gid_t gid;
73 pid_t pid;
74 int r;
75
76 assert(_home);
77
5018c0c9 78 if (!user || STR_IN_SET(user, "root", "0")) {
ee645080
LP
79 /* Reset everything fully to 0, just in case */
80
81 r = reset_uid_gid();
82 if (r < 0)
83 return log_error_errno(r, "Failed to become root: %m");
84
85 *_home = NULL;
86 return 0;
87 }
88
89 /* First, get user credentials */
90 fd = spawn_getent("passwd", user, &pid);
91 if (fd < 0)
92 return fd;
93
e92aaed3 94 f = fdopen(fd, "r");
ee645080
LP
95 if (!f)
96 return log_oom();
97 fd = -1;
98
c5b82d86 99 r = read_line(f, LONG_LINE_MAX, &line);
baaa35ad
ZJS
100 if (r == 0)
101 return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
102 "Failed to resolve user %s.", user);
c5b82d86
LP
103 if (r < 0)
104 return log_error_errno(r, "Failed to read from getent: %m");
ee645080 105
7d4904fe 106 (void) wait_for_terminate_and_check("getent passwd", pid, WAIT_LOG);
ee645080
LP
107
108 x = strchr(line, ':');
baaa35ad
ZJS
109 if (!x)
110 return log_error_errno(SYNTHETIC_ERRNO(EIO),
111 "/etc/passwd entry has invalid user field.");
ee645080
LP
112
113 u = strchr(x+1, ':');
baaa35ad
ZJS
114 if (!u)
115 return log_error_errno(SYNTHETIC_ERRNO(EIO),
116 "/etc/passwd entry has invalid password field.");
ee645080
LP
117
118 u++;
119 g = strchr(u, ':');
baaa35ad
ZJS
120 if (!g)
121 return log_error_errno(SYNTHETIC_ERRNO(EIO),
122 "/etc/passwd entry has invalid UID field.");
ee645080
LP
123
124 *g = 0;
125 g++;
126 x = strchr(g, ':');
baaa35ad
ZJS
127 if (!x)
128 return log_error_errno(SYNTHETIC_ERRNO(EIO),
129 "/etc/passwd entry has invalid GID field.");
ee645080
LP
130
131 *x = 0;
132 h = strchr(x+1, ':');
baaa35ad
ZJS
133 if (!h)
134 return log_error_errno(SYNTHETIC_ERRNO(EIO),
135 "/etc/passwd entry has invalid GECOS field.");
ee645080
LP
136
137 h++;
138 x = strchr(h, ':');
baaa35ad
ZJS
139 if (!x)
140 return log_error_errno(SYNTHETIC_ERRNO(EIO),
141 "/etc/passwd entry has invalid home directory field.");
ee645080
LP
142
143 *x = 0;
144
145 r = parse_uid(u, &uid);
baaa35ad
ZJS
146 if (r < 0)
147 return log_error_errno(SYNTHETIC_ERRNO(EIO),
148 "Failed to parse UID of user.");
ee645080
LP
149
150 r = parse_gid(g, &gid);
baaa35ad
ZJS
151 if (r < 0)
152 return log_error_errno(SYNTHETIC_ERRNO(EIO),
153 "Failed to parse GID of user.");
ee645080
LP
154
155 home = strdup(h);
156 if (!home)
157 return log_oom();
158
c5b82d86
LP
159 f = safe_fclose(f);
160 line = mfree(line);
161
ee645080
LP
162 /* Second, get group memberships */
163 fd = spawn_getent("initgroups", user, &pid);
164 if (fd < 0)
165 return fd;
166
e92aaed3 167 f = fdopen(fd, "r");
ee645080
LP
168 if (!f)
169 return log_oom();
170 fd = -1;
171
c5b82d86 172 r = read_line(f, LONG_LINE_MAX, &line);
baaa35ad
ZJS
173 if (r == 0)
174 return log_error_errno(SYNTHETIC_ERRNO(ESRCH),
175 "Failed to resolve user %s.", user);
c5b82d86
LP
176 if (r < 0)
177 return log_error_errno(r, "Failed to read from getent: %m");
ee645080 178
7d4904fe 179 (void) wait_for_terminate_and_check("getent initgroups", pid, WAIT_LOG);
ee645080
LP
180
181 /* Skip over the username and subsequent separator whitespace */
182 x = line;
183 x += strcspn(x, WHITESPACE);
184 x += strspn(x, WHITESPACE);
185
186 FOREACH_WORD(word, l, x, state) {
187 char c[l+1];
188
189 memcpy(c, word, l);
190 c[l] = 0;
191
192 if (!GREEDY_REALLOC(uids, sz, n_uids+1))
193 return log_oom();
194
195 r = parse_uid(c, &uids[n_uids++]);
c7f9a8d2
LP
196 if (r < 0)
197 return log_error_errno(r, "Failed to parse group data from getent: %m");
ee645080
LP
198 }
199
200 r = mkdir_parents(home, 0775);
201 if (r < 0)
202 return log_error_errno(r, "Failed to make home root directory: %m");
203
d50b5839 204 r = mkdir_safe(home, 0755, uid, gid, 0);
37c1d5e9 205 if (r < 0 && !IN_SET(r, -EEXIST, -ENOTDIR))
ee645080
LP
206 return log_error_errno(r, "Failed to make home directory: %m");
207
208 (void) fchown(STDIN_FILENO, uid, gid);
209 (void) fchown(STDOUT_FILENO, uid, gid);
210 (void) fchown(STDERR_FILENO, uid, gid);
211
212 if (setgroups(n_uids, uids) < 0)
213 return log_error_errno(errno, "Failed to set auxiliary groups: %m");
214
215 if (setresgid(gid, gid, gid) < 0)
d2b8497d 216 return log_error_errno(errno, "setresgid() failed: %m");
ee645080
LP
217
218 if (setresuid(uid, uid, uid) < 0)
d2b8497d 219 return log_error_errno(errno, "setresuid() failed: %m");
ee645080 220
1cc6c93a
YW
221 if (_home)
222 *_home = TAKE_PTR(home);
ee645080
LP
223
224 return 0;
225}