1 /* SPDX-License-Identifier: LGPL-2.1-or-later */
7 #include "alloc-util.h"
13 #include "nspawn-setuid.h"
14 #include "process-util.h"
15 #include "signal-util.h"
16 #include "string-util.h"
18 #include "user-util.h"
20 static int spawn_getent(const char *database
, const char *key
, pid_t
*rpid
) {
28 if (pipe2(pipe_fds
, O_CLOEXEC
) < 0)
29 return log_error_errno(errno
, "Failed to allocate pipe: %m");
31 r
= safe_fork_full("(getent)",
32 (int[]) { -EBADF
, pipe_fds
[1], -EBADF
}, NULL
, 0,
33 FORK_RESET_SIGNALS
|FORK_CLOSE_ALL_FDS
|FORK_DEATHSIG_SIGTERM
|FORK_REARRANGE_STDIO
|FORK_LOG
|FORK_RLIMIT_NOFILE_SAFE
,
36 safe_close_pair(pipe_fds
);
40 execle("/usr/bin/getent", "getent", database
, key
, NULL
, &(char*[1]){});
41 execle("/bin/getent", "getent", database
, key
, NULL
, &(char*[1]){});
45 pipe_fds
[1] = safe_close(pipe_fds
[1]);
52 int change_uid_gid_raw(
55 const gid_t
*supplementary_gids
,
56 size_t n_supplementary_gids
,
59 if (!uid_is_valid(uid
))
61 if (!gid_is_valid(gid
))
65 (void) fchown(STDIN_FILENO
, uid
, gid
);
66 (void) fchown(STDOUT_FILENO
, uid
, gid
);
67 (void) fchown(STDERR_FILENO
, uid
, gid
);
70 if (setgroups(n_supplementary_gids
, supplementary_gids
) < 0)
71 return log_error_errno(errno
, "Failed to set auxiliary groups: %m");
73 if (setresgid(gid
, gid
, gid
) < 0)
74 return log_error_errno(errno
, "setresgid() failed: %m");
76 if (setresuid(uid
, uid
, uid
) < 0)
77 return log_error_errno(errno
, "setresuid() failed: %m");
82 int change_uid_gid(const char *user
, bool chown_stdio
, char **ret_home
) {
84 _cleanup_free_ gid_t
*gids
= NULL
;
85 _cleanup_free_
char *home
= NULL
, *line
= NULL
;
86 _cleanup_fclose_
FILE *f
= NULL
;
87 _cleanup_close_
int fd
= -EBADF
;
96 if (!user
|| STR_IN_SET(user
, "root", "0")) {
97 /* Reset everything fully to 0, just in case */
101 return log_error_errno(r
, "Failed to become root: %m");
107 /* First, get user credentials */
108 fd
= spawn_getent("passwd", user
, &pid
);
112 f
= take_fdopen(&fd
, "r");
116 r
= read_line(f
, LONG_LINE_MAX
, &line
);
118 return log_error_errno(SYNTHETIC_ERRNO(ESRCH
),
119 "Failed to resolve user %s.", user
);
121 return log_error_errno(r
, "Failed to read from getent: %m");
123 (void) wait_for_terminate_and_check("getent passwd", pid
, WAIT_LOG
);
125 x
= strchr(line
, ':');
127 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
128 "/etc/passwd entry has invalid user field.");
130 u
= strchr(x
+1, ':');
132 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
133 "/etc/passwd entry has invalid password field.");
138 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
139 "/etc/passwd entry has invalid UID field.");
145 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
146 "/etc/passwd entry has invalid GID field.");
149 h
= strchr(x
+1, ':');
151 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
152 "/etc/passwd entry has invalid GECOS field.");
157 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
158 "/etc/passwd entry has invalid home directory field.");
162 r
= parse_uid(u
, &uid
);
164 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
165 "Failed to parse UID of user.");
167 r
= parse_gid(g
, &gid
);
169 return log_error_errno(SYNTHETIC_ERRNO(EIO
),
170 "Failed to parse GID of user.");
179 /* Second, get group memberships */
180 fd
= spawn_getent("initgroups", user
, &pid
);
184 f
= take_fdopen(&fd
, "r");
188 r
= read_line(f
, LONG_LINE_MAX
, &line
);
190 return log_error_errno(SYNTHETIC_ERRNO(ESRCH
),
191 "Failed to resolve user %s.", user
);
193 return log_error_errno(r
, "Failed to read from getent: %m");
195 (void) wait_for_terminate_and_check("getent initgroups", pid
, WAIT_LOG
);
197 /* Skip over the username and subsequent separator whitespace */
199 x
+= strcspn(x
, WHITESPACE
);
200 x
+= strspn(x
, WHITESPACE
);
202 for (const char *p
= x
;;) {
203 _cleanup_free_
char *word
= NULL
;
205 r
= extract_first_word(&p
, &word
, NULL
, 0);
207 return log_error_errno(r
, "Failed to parse group data from getent: %m");
211 if (!GREEDY_REALLOC(gids
, n_gids
+1))
214 r
= parse_gid(word
, &gids
[n_gids
++]);
216 return log_error_errno(r
, "Failed to parse group data from getent: %m");
219 r
= mkdir_parents(home
, 0775);
221 return log_error_errno(r
, "Failed to make home root directory: %m");
223 r
= mkdir_safe(home
, 0755, uid
, gid
, 0);
224 if (r
< 0 && !IN_SET(r
, -EEXIST
, -ENOTDIR
))
225 return log_error_errno(r
, "Failed to make home directory: %m");
227 r
= change_uid_gid_raw(uid
, gid
, gids
, n_gids
, chown_stdio
);
232 *ret_home
= TAKE_PTR(home
);