]>
git.ipfire.org Git - thirdparty/systemd.git/blob - src/nspawn/nspawn-setuid.c
2 This file is part of systemd.
4 Copyright 2015 Lennart Poettering
6 systemd is free software; you can redistribute it and/or modify it
7 under the terms of the GNU Lesser General Public License as published by
8 the Free Software Foundation; either version 2.1 of the License, or
9 (at your option) any later version.
11 systemd is distributed in the hope that it will be useful, but
12 WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Lesser General Public License for more details.
16 You should have received a copy of the GNU Lesser General Public License
17 along with systemd; If not, see <http://www.gnu.org/licenses/>.
21 #include <sys/types.h>
24 #include "alloc-util.h"
27 #include "nspawn-setuid.h"
28 #include "process-util.h"
29 #include "signal-util.h"
30 #include "string-util.h"
31 #include "user-util.h"
34 static int spawn_getent(const char *database
, const char *key
, pid_t
*rpid
) {
42 if (pipe2(pipe_fds
, O_CLOEXEC
) < 0)
43 return log_error_errno(errno
, "Failed to allocate pipe: %m");
47 return log_error_errno(errno
, "Failed to fork getent child: %m");
50 char *empty_env
= NULL
;
52 if (dup3(pipe_fds
[1], STDOUT_FILENO
, 0) < 0)
56 safe_close(pipe_fds
[0]);
58 safe_close(pipe_fds
[1]);
60 nullfd
= open("/dev/null", O_RDWR
);
64 if (dup3(nullfd
, STDIN_FILENO
, 0) < 0)
67 if (dup3(nullfd
, STDERR_FILENO
, 0) < 0)
73 (void) reset_all_signal_handlers();
74 (void) reset_signal_mask();
75 close_all_fds(NULL
, 0);
77 execle("/usr/bin/getent", "getent", database
, key
, NULL
, &empty_env
);
78 execle("/bin/getent", "getent", database
, key
, NULL
, &empty_env
);
82 pipe_fds
[1] = safe_close(pipe_fds
[1]);
89 int change_uid_gid(const char *user
, char **_home
) {
90 char line
[LINE_MAX
], *x
, *u
, *g
, *h
;
91 const char *word
, *state
;
92 _cleanup_free_ uid_t
*uids
= NULL
;
93 _cleanup_free_
char *home
= NULL
;
94 _cleanup_fclose_
FILE *f
= NULL
;
95 _cleanup_close_
int fd
= -1;
105 if (!user
|| streq(user
, "root") || streq(user
, "0")) {
106 /* Reset everything fully to 0, just in case */
110 return log_error_errno(r
, "Failed to become root: %m");
116 /* First, get user credentials */
117 fd
= spawn_getent("passwd", user
, &pid
);
126 if (!fgets(line
, sizeof(line
), f
)) {
128 log_error("Failed to resolve user %s.", user
);
132 return log_error_errno(errno
, "Failed to read from getent: %m");
137 wait_for_terminate_and_warn("getent passwd", pid
, true);
139 x
= strchr(line
, ':');
141 log_error("/etc/passwd entry has invalid user field.");
145 u
= strchr(x
+1, ':');
147 log_error("/etc/passwd entry has invalid password field.");
154 log_error("/etc/passwd entry has invalid UID field.");
162 log_error("/etc/passwd entry has invalid GID field.");
167 h
= strchr(x
+1, ':');
169 log_error("/etc/passwd entry has invalid GECOS field.");
176 log_error("/etc/passwd entry has invalid home directory field.");
182 r
= parse_uid(u
, &uid
);
184 log_error("Failed to parse UID of user.");
188 r
= parse_gid(g
, &gid
);
190 log_error("Failed to parse GID of user.");
198 /* Second, get group memberships */
199 fd
= spawn_getent("initgroups", user
, &pid
);
209 if (!fgets(line
, sizeof(line
), f
)) {
211 log_error("Failed to resolve user %s.", user
);
215 return log_error_errno(errno
, "Failed to read from getent: %m");
220 wait_for_terminate_and_warn("getent initgroups", pid
, true);
222 /* Skip over the username and subsequent separator whitespace */
224 x
+= strcspn(x
, WHITESPACE
);
225 x
+= strspn(x
, WHITESPACE
);
227 FOREACH_WORD(word
, l
, x
, state
) {
233 if (!GREEDY_REALLOC(uids
, sz
, n_uids
+1))
236 r
= parse_uid(c
, &uids
[n_uids
++]);
238 log_error("Failed to parse group data from getent.");
243 r
= mkdir_parents(home
, 0775);
245 return log_error_errno(r
, "Failed to make home root directory: %m");
247 r
= mkdir_safe(home
, 0755, uid
, gid
);
248 if (r
< 0 && r
!= -EEXIST
)
249 return log_error_errno(r
, "Failed to make home directory: %m");
251 (void) fchown(STDIN_FILENO
, uid
, gid
);
252 (void) fchown(STDOUT_FILENO
, uid
, gid
);
253 (void) fchown(STDERR_FILENO
, uid
, gid
);
255 if (setgroups(n_uids
, uids
) < 0)
256 return log_error_errno(errno
, "Failed to set auxiliary groups: %m");
258 if (setresgid(gid
, gid
, gid
) < 0)
259 return log_error_errno(errno
, "setresgid() failed: %m");
261 if (setresuid(uid
, uid
, uid
) < 0)
262 return log_error_errno(errno
, "setresuid() failed: %m");