]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/nspawn/nspawn-setuid.c
1 /* SPDX-License-Identifier: LGPL-2.1+ */
3 This file is part of systemd.
5 Copyright 2015 Lennart Poettering
12 #include "alloc-util.h"
18 #include "nspawn-setuid.h"
19 #include "process-util.h"
20 #include "signal-util.h"
21 #include "string-util.h"
23 #include "user-util.h"
26 static int spawn_getent(const char *database
, const char *key
, pid_t
*rpid
) {
34 if (pipe2(pipe_fds
, O_CLOEXEC
) < 0)
35 return log_error_errno(errno
, "Failed to allocate pipe: %m");
37 r
= safe_fork("(getent)", FORK_RESET_SIGNALS
|FORK_DEATHSIG
|FORK_LOG
, &pid
);
39 safe_close_pair(pipe_fds
);
43 char *empty_env
= NULL
;
45 safe_close(pipe_fds
[0]);
47 if (rearrange_stdio(-1, pipe_fds
[1], -1) < 0)
50 close_all_fds(NULL
, 0);
52 execle("/usr/bin/getent", "getent", database
, key
, NULL
, &empty_env
);
53 execle("/bin/getent", "getent", database
, key
, NULL
, &empty_env
);
57 pipe_fds
[1] = safe_close(pipe_fds
[1]);
64 int change_uid_gid(const char *user
, char **_home
) {
66 const char *word
, *state
;
67 _cleanup_free_ uid_t
*uids
= NULL
;
68 _cleanup_free_
char *home
= NULL
, *line
= NULL
;
69 _cleanup_fclose_
FILE *f
= NULL
;
70 _cleanup_close_
int fd
= -1;
80 if (!user
|| STR_IN_SET(user
, "root", "0")) {
81 /* Reset everything fully to 0, just in case */
85 return log_error_errno(r
, "Failed to become root: %m");
91 /* First, get user credentials */
92 fd
= spawn_getent("passwd", user
, &pid
);
101 r
= read_line(f
, LONG_LINE_MAX
, &line
);
103 log_error("Failed to resolve user %s.", user
);
107 return log_error_errno(r
, "Failed to read from getent: %m");
109 (void) wait_for_terminate_and_check("getent passwd", pid
, WAIT_LOG
);
111 x
= strchr(line
, ':');
113 log_error("/etc/passwd entry has invalid user field.");
117 u
= strchr(x
+1, ':');
119 log_error("/etc/passwd entry has invalid password field.");
126 log_error("/etc/passwd entry has invalid UID field.");
134 log_error("/etc/passwd entry has invalid GID field.");
139 h
= strchr(x
+1, ':');
141 log_error("/etc/passwd entry has invalid GECOS field.");
148 log_error("/etc/passwd entry has invalid home directory field.");
154 r
= parse_uid(u
, &uid
);
156 log_error("Failed to parse UID of user.");
160 r
= parse_gid(g
, &gid
);
162 log_error("Failed to parse GID of user.");
173 /* Second, get group memberships */
174 fd
= spawn_getent("initgroups", user
, &pid
);
178 f
= fdopen(fd
, "re");
183 r
= read_line(f
, LONG_LINE_MAX
, &line
);
185 log_error("Failed to resolve user %s.", user
);
189 return log_error_errno(r
, "Failed to read from getent: %m");
191 (void) wait_for_terminate_and_check("getent initgroups", pid
, WAIT_LOG
);
193 /* Skip over the username and subsequent separator whitespace */
195 x
+= strcspn(x
, WHITESPACE
);
196 x
+= strspn(x
, WHITESPACE
);
198 FOREACH_WORD(word
, l
, x
, state
) {
204 if (!GREEDY_REALLOC(uids
, sz
, n_uids
+1))
207 r
= parse_uid(c
, &uids
[n_uids
++]);
209 return log_error_errno(r
, "Failed to parse group data from getent: %m");
212 r
= mkdir_parents(home
, 0775);
214 return log_error_errno(r
, "Failed to make home root directory: %m");
216 r
= mkdir_safe(home
, 0755, uid
, gid
, 0);
217 if (r
< 0 && !IN_SET(r
, -EEXIST
, -ENOTDIR
))
218 return log_error_errno(r
, "Failed to make home directory: %m");
220 (void) fchown(STDIN_FILENO
, uid
, gid
);
221 (void) fchown(STDOUT_FILENO
, uid
, gid
);
222 (void) fchown(STDERR_FILENO
, uid
, gid
);
224 if (setgroups(n_uids
, uids
) < 0)
225 return log_error_errno(errno
, "Failed to set auxiliary groups: %m");
227 if (setresgid(gid
, gid
, gid
) < 0)
228 return log_error_errno(errno
, "setresgid() failed: %m");
230 if (setresuid(uid
, uid
, uid
) < 0)
231 return log_error_errno(errno
, "setresuid() failed: %m");
234 *_home
= TAKE_PTR(home
);