-/*-*- Mode: C; c-basic-offset: 8; indent-tabs-mode: nil -*-*/
-
+/* SPDX-License-Identifier: LGPL-2.1+ */
/***
This file is part of systemd.
#include <sys/types.h>
#include <unistd.h>
+#include "alloc-util.h"
+#include "def.h"
+#include "errno.h"
#include "fd-util.h"
+#include "fileio.h"
#include "mkdir.h"
#include "nspawn-setuid.h"
#include "process-util.h"
#include "signal-util.h"
#include "string-util.h"
+#include "strv.h"
+#include "user-util.h"
#include "util.h"
static int spawn_getent(const char *database, const char *key, pid_t *rpid) {
- int pipe_fds[2];
+ int pipe_fds[2], r;
pid_t pid;
assert(database);
if (pipe2(pipe_fds, O_CLOEXEC) < 0)
return log_error_errno(errno, "Failed to allocate pipe: %m");
- pid = fork();
- if (pid < 0)
- return log_error_errno(errno, "Failed to fork getent child: %m");
- else if (pid == 0) {
- int nullfd;
+ r = safe_fork("(getent)", FORK_RESET_SIGNALS|FORK_DEATHSIG|FORK_LOG, &pid);
+ if (r < 0) {
+ safe_close_pair(pipe_fds);
+ return r;
+ }
+ if (r == 0) {
char *empty_env = NULL;
- if (dup3(pipe_fds[1], STDOUT_FILENO, 0) < 0)
- _exit(EXIT_FAILURE);
-
- if (pipe_fds[0] > 2)
- safe_close(pipe_fds[0]);
- if (pipe_fds[1] > 2)
- safe_close(pipe_fds[1]);
+ safe_close(pipe_fds[0]);
- nullfd = open("/dev/null", O_RDWR);
- if (nullfd < 0)
+ if (rearrange_stdio(-1, pipe_fds[1], -1) < 0)
_exit(EXIT_FAILURE);
- if (dup3(nullfd, STDIN_FILENO, 0) < 0)
- _exit(EXIT_FAILURE);
-
- if (dup3(nullfd, STDERR_FILENO, 0) < 0)
- _exit(EXIT_FAILURE);
-
- if (nullfd > 2)
- safe_close(nullfd);
-
- (void) reset_all_signal_handlers();
- (void) reset_signal_mask();
close_all_fds(NULL, 0);
execle("/usr/bin/getent", "getent", database, key, NULL, &empty_env);
}
int change_uid_gid(const char *user, char **_home) {
- char line[LINE_MAX], *x, *u, *g, *h;
+ char *x, *u, *g, *h;
const char *word, *state;
_cleanup_free_ uid_t *uids = NULL;
- _cleanup_free_ char *home = NULL;
+ _cleanup_free_ char *home = NULL, *line = NULL;
_cleanup_fclose_ FILE *f = NULL;
_cleanup_close_ int fd = -1;
unsigned n_uids = 0;
assert(_home);
- if (!user || streq(user, "root") || streq(user, "0")) {
+ if (!user || STR_IN_SET(user, "root", "0")) {
/* Reset everything fully to 0, just in case */
r = reset_uid_gid();
if (fd < 0)
return fd;
- f = fdopen(fd, "r");
+ f = fdopen(fd, "re");
if (!f)
return log_oom();
fd = -1;
- if (!fgets(line, sizeof(line), f)) {
-
- if (!ferror(f)) {
- log_error("Failed to resolve user %s.", user);
- return -ESRCH;
- }
-
- log_error_errno(errno, "Failed to read from getent: %m");
- return -errno;
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r == 0) {
+ log_error("Failed to resolve user %s.", user);
+ return -ESRCH;
}
+ if (r < 0)
+ return log_error_errno(r, "Failed to read from getent: %m");
- truncate_nl(line);
-
- wait_for_terminate_and_warn("getent passwd", pid, true);
+ (void) wait_for_terminate_and_check("getent passwd", pid, WAIT_LOG);
x = strchr(line, ':');
if (!x) {
if (!home)
return log_oom();
+ f = safe_fclose(f);
+ line = mfree(line);
+
/* Second, get group memberships */
fd = spawn_getent("initgroups", user, &pid);
if (fd < 0)
return fd;
- fclose(f);
- f = fdopen(fd, "r");
+ f = fdopen(fd, "re");
if (!f)
return log_oom();
fd = -1;
- if (!fgets(line, sizeof(line), f)) {
- if (!ferror(f)) {
- log_error("Failed to resolve user %s.", user);
- return -ESRCH;
- }
-
- log_error_errno(errno, "Failed to read from getent: %m");
- return -errno;
+ r = read_line(f, LONG_LINE_MAX, &line);
+ if (r == 0) {
+ log_error("Failed to resolve user %s.", user);
+ return -ESRCH;
}
+ if (r < 0)
+ return log_error_errno(r, "Failed to read from getent: %m");
- truncate_nl(line);
-
- wait_for_terminate_and_warn("getent initgroups", pid, true);
+ (void) wait_for_terminate_and_check("getent initgroups", pid, WAIT_LOG);
/* Skip over the username and subsequent separator whitespace */
x = line;
return log_oom();
r = parse_uid(c, &uids[n_uids++]);
- if (r < 0) {
- log_error("Failed to parse group data from getent.");
- return -EIO;
- }
+ if (r < 0)
+ return log_error_errno(r, "Failed to parse group data from getent: %m");
}
r = mkdir_parents(home, 0775);
if (r < 0)
return log_error_errno(r, "Failed to make home root directory: %m");
- r = mkdir_safe(home, 0755, uid, gid);
+ r = mkdir_safe(home, 0755, uid, gid, false);
if (r < 0 && r != -EEXIST)
return log_error_errno(r, "Failed to make home directory: %m");
return log_error_errno(errno, "Failed to set auxiliary groups: %m");
if (setresgid(gid, gid, gid) < 0)
- return log_error_errno(errno, "setregid() failed: %m");
+ return log_error_errno(errno, "setresgid() failed: %m");
if (setresuid(uid, uid, uid) < 0)
- return log_error_errno(errno, "setreuid() failed: %m");
+ return log_error_errno(errno, "setresuid() failed: %m");
if (_home) {
*_home = home;