Merge pull request #12325 from giuseppe/rootless-use-auto-cleanup

rootless: use auto cleanup functions
This commit is contained in:
OpenShift Merge Robot 2021-11-17 20:20:29 +01:00 committed by GitHub
commit 878d22e831
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 147 additions and 157 deletions

View File

@ -19,6 +19,33 @@
#include <sys/select.h>
#include <stdio.h>
#define cleanup_free __attribute__ ((cleanup (cleanup_freep)))
#define cleanup_close __attribute__ ((cleanup (cleanup_closep)))
#define cleanup_dir __attribute__ ((cleanup (cleanup_dirp)))
static inline void
cleanup_freep (void *p)
{
void **pp = (void **) p;
free (*pp);
}
static inline void
cleanup_closep (void *p)
{
int *pp = p;
if (*pp >= 0)
TEMP_FAILURE_RETRY (close (*pp));
}
static inline void
cleanup_dirp (DIR **p)
{
DIR *dir = *p;
if (dir)
closedir (dir);
}
int rename_noreplace (int olddirfd, const char *oldpath, int newdirfd, const char *newpath)
{
int ret;
@ -119,8 +146,8 @@ do_pause ()
static char **
get_cmd_line_args ()
{
int fd;
char *buffer;
cleanup_free char *buffer = NULL;
cleanup_close int fd = -1;
size_t allocated;
size_t used = 0;
int ret;
@ -139,10 +166,7 @@ get_cmd_line_args ()
{
ret = TEMP_FAILURE_RETRY (read (fd, buffer + used, allocated - used));
if (ret < 0)
{
free (buffer);
return NULL;
}
return NULL;
if (ret == 0)
break;
@ -153,30 +177,21 @@ get_cmd_line_args ()
allocated += 512;
char *tmp = realloc (buffer, allocated);
if (tmp == NULL)
{
free (buffer);
return NULL;
}
return NULL;
buffer = tmp;
}
}
close (fd);
for (i = 0; i < used; i++)
if (buffer[i] == '\0')
argc++;
if (argc == 0)
{
free (buffer);
return NULL;
}
return NULL;
argv = malloc (sizeof (char *) * (argc + 1));
if (argv == NULL)
{
free (buffer);
return NULL;
}
return NULL;
argc = 0;
argv[argc++] = buffer;
@ -186,15 +201,19 @@ get_cmd_line_args ()
argv[argc] = NULL;
/* Move ownership. */
buffer = NULL;
return argv;
}
static bool
can_use_shortcut ()
{
int argc;
char **argv;
cleanup_free char **argv = NULL;
cleanup_free char *argv0 = NULL;
bool ret = true;
int argc;
#ifdef DISABLE_JOIN_SHORTCUT
return false;
@ -204,12 +223,10 @@ can_use_shortcut ()
if (argv == NULL)
return false;
argv0 = argv[0];
if (strstr (argv[0], "podman") == NULL)
{
free (argv[0]);
free (argv);
return false;
}
return false;
for (argc = 0; argv[argc]; argc++)
{
@ -234,11 +251,25 @@ can_use_shortcut ()
}
}
free (argv[0]);
free (argv);
return ret;
}
static int
open_namespace (int pid_to_join, const char *ns_file)
{
char ns_path[PATH_MAX];
int ret;
ret = snprintf (ns_path, PATH_MAX, "/proc/%d/ns/%s", pid_to_join, ns_file);
if (ret == PATH_MAX)
{
fprintf (stderr, "internal error: namespace path too long\n");
return -1;
}
return open (ns_path, O_CLOEXEC | O_RDONLY);
}
int
is_fd_inherited(int fd)
{
@ -255,8 +286,7 @@ static void __attribute__((constructor)) init()
const char *listen_pid;
const char *listen_fds;
const char *listen_fdnames;
DIR *d;
cleanup_dir DIR *d = NULL;
pause = getenv ("_PODMAN_PAUSE");
if (pause && pause[0])
@ -304,7 +334,6 @@ static void __attribute__((constructor)) init()
FD_SET (fd % FD_SETSIZE, &(open_files_set[fd / FD_SETSIZE]));
}
closedir (d);
}
listen_pid = getenv("LISTEN_PID");
@ -322,7 +351,7 @@ static void __attribute__((constructor)) init()
if (saved_systemd_listen_pid == NULL
|| saved_systemd_listen_fds == NULL)
{
fprintf (stderr, "save socket listen environments error: %s\n", strerror (errno));
fprintf (stderr, "save socket listen environments error: %m\n");
_exit (EXIT_FAILURE);
}
}
@ -332,73 +361,70 @@ static void __attribute__((constructor)) init()
xdg_runtime_dir = getenv ("XDG_RUNTIME_DIR");
if (geteuid () != 0 && xdg_runtime_dir && xdg_runtime_dir[0] && can_use_shortcut ())
{
int r;
int fd;
cleanup_free char *cwd = NULL;
cleanup_close int userns_fd = -1;
cleanup_close int mntns_fd = -1;
cleanup_close int fd = -1;
long pid;
char buf[12];
uid_t uid;
gid_t gid;
char path[PATH_MAX];
const char *const suffix = "/libpod/tmp/pause.pid";
char *cwd = getcwd (NULL, 0);
char uid_fmt[16];
char gid_fmt[16];
size_t len;
int r;
cwd = getcwd (NULL, 0);
if (cwd == NULL)
{
fprintf (stderr, "error getting current working directory: %s\n", strerror (errno));
fprintf (stderr, "error getting current working directory: %m\n");
_exit (EXIT_FAILURE);
}
len = snprintf (path, PATH_MAX, "%s%s", xdg_runtime_dir, suffix);
if (len >= PATH_MAX)
{
fprintf (stderr, "invalid value for XDG_RUNTIME_DIR: %s", strerror (ENAMETOOLONG));
errno = ENAMETOOLONG;
fprintf (stderr, "invalid value for XDG_RUNTIME_DIR: %m");
exit (EXIT_FAILURE);
}
fd = open (path, O_RDONLY);
if (fd < 0)
{
free (cwd);
return;
}
return;
r = TEMP_FAILURE_RETRY (read (fd, buf, sizeof (buf) - 1));
close (fd);
if (r < 0)
{
free (cwd);
return;
}
return;
buf[r] = '\0';
pid = strtol (buf, NULL, 10);
if (pid == LONG_MAX)
{
free (cwd);
return;
}
return;
uid = geteuid ();
gid = getegid ();
sprintf (path, "/proc/%ld/ns/user", pid);
fd = open (path, O_RDONLY);
if (fd < 0 || setns (fd, 0) < 0)
{
free (cwd);
return;
}
close (fd);
userns_fd = open_namespace (pid, "user");
if (userns_fd < 0)
return;
/* Errors here cannot be ignored as we already joined a ns. */
sprintf (path, "/proc/%ld/ns/mnt", pid);
fd = open (path, O_RDONLY);
if (fd < 0)
mntns_fd = open_namespace (pid, "mnt");
if (mntns_fd < 0)
return;
if (setns (userns_fd, 0) < 0)
return;
/* The user namespace was joined, after this point errors are
not recoverable anymore. */
if (setns (mntns_fd, 0) < 0)
{
fprintf (stderr, "cannot open %s: %s", path, strerror (errno));
fprintf (stderr, "cannot join mount namespace for %ld: %m", pid);
exit (EXIT_FAILURE);
}
@ -409,33 +435,24 @@ static void __attribute__((constructor)) init()
setenv ("_CONTAINERS_ROOTLESS_UID", uid_fmt, 1);
setenv ("_CONTAINERS_ROOTLESS_GID", gid_fmt, 1);
r = setns (fd, 0);
if (r < 0)
{
fprintf (stderr, "cannot join mount namespace for %ld: %s", pid, strerror (errno));
exit (EXIT_FAILURE);
}
close (fd);
if (syscall_setresgid (0, 0, 0) < 0)
{
fprintf (stderr, "cannot setresgid: %s\n", strerror (errno));
fprintf (stderr, "cannot setresgid: %m\n");
_exit (EXIT_FAILURE);
}
if (syscall_setresuid (0, 0, 0) < 0)
{
fprintf (stderr, "cannot setresuid: %s\n", strerror (errno));
fprintf (stderr, "cannot setresuid: %m\n");
_exit (EXIT_FAILURE);
}
if (chdir (cwd) < 0)
{
fprintf (stderr, "cannot chdir to %s: %s\n", cwd, strerror (errno));
fprintf (stderr, "cannot chdir to %s: %m\n", cwd);
_exit (EXIT_FAILURE);
}
free (cwd);
rootless_uid_init = uid;
rootless_gid_init = gid;
}
@ -534,7 +551,7 @@ create_pause_process (const char *pause_pid_file_path, char **argv)
fd = mkstemp (tmp_file_path);
if (fd < 0)
{
fprintf (stderr, "error creating temporary file: %s\n", strerror (errno));
fprintf (stderr, "error creating temporary file: %m\n");
kill (pid, SIGKILL);
_exit (EXIT_FAILURE);
}
@ -542,7 +559,7 @@ create_pause_process (const char *pause_pid_file_path, char **argv)
r = TEMP_FAILURE_RETRY (write (fd, pid_str, strlen (pid_str)));
if (r < 0)
{
fprintf (stderr, "cannot write to file descriptor: %s\n", strerror (errno));
fprintf (stderr, "cannot write to file descriptor: %m\n");
kill (pid, SIGKILL);
_exit (EXIT_FAILURE);
}
@ -560,7 +577,7 @@ create_pause_process (const char *pause_pid_file_path, char **argv)
r = TEMP_FAILURE_RETRY (write (p[1], "0", 1));
if (r < 0)
{
fprintf (stderr, "cannot write to pipe: %s\n", strerror (errno));
fprintf (stderr, "cannot write to pipe: %m\n");
_exit (EXIT_FAILURE);
}
close (p[1]);
@ -595,22 +612,6 @@ create_pause_process (const char *pause_pid_file_path, char **argv)
}
}
static int
open_namespace (int pid_to_join, const char *ns_file)
{
char ns_path[PATH_MAX];
int ret;
ret = snprintf (ns_path, PATH_MAX, "/proc/%d/ns/%s", pid_to_join, ns_file);
if (ret == PATH_MAX)
{
fprintf (stderr, "internal error: namespace path too long\n");
return -1;
}
return open (ns_path, O_CLOEXEC | O_RDONLY);
}
static void
join_namespace_or_die (const char *name, int ns_fd)
{
@ -624,18 +625,20 @@ join_namespace_or_die (const char *name, int ns_fd)
int
reexec_userns_join (int pid_to_join, char *pause_pid_file_path)
{
cleanup_close int userns_fd = -1;
cleanup_close int mntns_fd = -1;
cleanup_free char *cwd = NULL;
char uid[16];
char gid[16];
char **argv;
cleanup_free char *argv0 = NULL;
cleanup_free char **argv = NULL;
int pid;
int mnt_ns = -1;
int user_ns = -1;
char *cwd = getcwd (NULL, 0);
sigset_t sigset, oldsigset;
cwd = getcwd (NULL, 0);
if (cwd == NULL)
{
fprintf (stderr, "error getting current working directory: %s\n", strerror (errno));
fprintf (stderr, "error getting current working directory: %m\n");
_exit (EXIT_FAILURE);
}
@ -645,32 +648,27 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path)
argv = get_cmd_line_args ();
if (argv == NULL)
{
fprintf (stderr, "cannot read argv: %s\n", strerror (errno));
fprintf (stderr, "cannot read argv: %m\n");
_exit (EXIT_FAILURE);
}
user_ns = open_namespace (pid_to_join, "user");
if (user_ns < 0)
return user_ns;
mnt_ns = open_namespace (pid_to_join, "mnt");
if (mnt_ns < 0)
{
close (user_ns);
return mnt_ns;
}
argv0 = argv[0];
userns_fd = open_namespace (pid_to_join, "user");
if (userns_fd < 0)
return userns_fd;
mntns_fd = open_namespace (pid_to_join, "mnt");
if (mntns_fd < 0)
return mntns_fd;
pid = fork ();
if (pid < 0)
fprintf (stderr, "cannot fork: %s\n", strerror (errno));
fprintf (stderr, "cannot fork: %m\n");
if (pid)
{
int f;
/* We passed down these fds, close them. */
close (user_ns);
close (mnt_ns);
for (f = 3; f <= open_files_max_fd; f++)
if (is_fd_inherited (f))
close (f);
@ -686,22 +684,22 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path)
if (sigfillset (&sigset) < 0)
{
fprintf (stderr, "cannot fill sigset: %s\n", strerror (errno));
fprintf (stderr, "cannot fill sigset: %m\n");
_exit (EXIT_FAILURE);
}
if (sigdelset (&sigset, SIGCHLD) < 0)
{
fprintf (stderr, "cannot sigdelset(SIGCHLD): %s\n", strerror (errno));
fprintf (stderr, "cannot sigdelset(SIGCHLD): %m\n");
_exit (EXIT_FAILURE);
}
if (sigdelset (&sigset, SIGTERM) < 0)
{
fprintf (stderr, "cannot sigdelset(SIGTERM): %s\n", strerror (errno));
fprintf (stderr, "cannot sigdelset(SIGTERM): %m\n");
_exit (EXIT_FAILURE);
}
if (sigprocmask (SIG_BLOCK, &sigset, &oldsigset) < 0)
{
fprintf (stderr, "cannot block signals: %s\n", strerror (errno));
fprintf (stderr, "cannot block signals: %m\n");
_exit (EXIT_FAILURE);
}
@ -722,33 +720,30 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path)
if (prctl (PR_SET_PDEATHSIG, SIGTERM, 0, 0, 0) < 0)
{
fprintf (stderr, "cannot prctl(PR_SET_PDEATHSIG): %s\n", strerror (errno));
fprintf (stderr, "cannot prctl(PR_SET_PDEATHSIG): %m\n");
_exit (EXIT_FAILURE);
}
join_namespace_or_die ("user", user_ns);
join_namespace_or_die ("mnt", mnt_ns);
close (user_ns);
close (mnt_ns);
join_namespace_or_die ("user", userns_fd);
join_namespace_or_die ("mnt", mntns_fd);
if (syscall_setresgid (0, 0, 0) < 0)
{
fprintf (stderr, "cannot setresgid: %s\n", strerror (errno));
fprintf (stderr, "cannot setresgid: %m\n");
_exit (EXIT_FAILURE);
}
if (syscall_setresuid (0, 0, 0) < 0)
{
fprintf (stderr, "cannot setresuid: %s\n", strerror (errno));
fprintf (stderr, "cannot setresuid: %m\n");
_exit (EXIT_FAILURE);
}
if (chdir (cwd) < 0)
{
fprintf (stderr, "cannot chdir to %s: %s\n", cwd, strerror (errno));
fprintf (stderr, "cannot chdir to %s: %m\n", cwd);
_exit (EXIT_FAILURE);
}
free (cwd);
if (pause_pid_file_path && pause_pid_file_path[0] != '\0')
{
@ -757,7 +752,7 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path)
}
if (sigprocmask (SIG_SETMASK, &oldsigset, NULL) < 0)
{
fprintf (stderr, "cannot block signals: %s\n", strerror (errno));
fprintf (stderr, "cannot block signals: %m\n");
_exit (EXIT_FAILURE);
}
@ -789,7 +784,7 @@ static int
copy_file_to_fd (const char *file_to_read, int outfd)
{
char buf[512];
int fd;
cleanup_close int fd = -1;
fd = open (file_to_read, O_RDONLY);
if (fd < 0)
@ -801,10 +796,7 @@ copy_file_to_fd (const char *file_to_read, int outfd)
r = TEMP_FAILURE_RETRY (read (fd, buf, sizeof buf));
if (r < 0)
{
close (fd);
return r;
}
return r;
if (r == 0)
break;
@ -813,43 +805,40 @@ copy_file_to_fd (const char *file_to_read, int outfd)
{
w = TEMP_FAILURE_RETRY (write (outfd, &buf[t], r - t));
if (w < 0)
{
close (fd);
return w;
}
return w;
t += w;
}
}
close (fd);
return 0;
}
int
reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_read, int outputfd)
{
cleanup_free char **argv = NULL;
cleanup_free char *argv0 = NULL;
cleanup_free char *cwd = NULL;
sigset_t sigset, oldsigset;
int ret;
pid_t pid;
char b;
char **argv;
char uid[16];
char gid[16];
char *cwd = getcwd (NULL, 0);
sigset_t sigset, oldsigset;
cwd = getcwd (NULL, 0);
if (cwd == NULL)
{
fprintf (stderr, "error getting current working directory: %s\n", strerror (errno));
fprintf (stderr, "error getting current working directory: %m\n");
_exit (EXIT_FAILURE);
}
sprintf (uid, "%d", geteuid ());
sprintf (gid, "%d", getegid ());
pid = syscall_clone (CLONE_NEWUSER|CLONE_NEWNS|SIGCHLD, NULL);
if (pid < 0)
{
fprintf (stderr, "cannot clone: %s\n", strerror (errno));
fprintf (stderr, "cannot clone: %m\n");
check_proc_sys_userns_file (_max_user_namespaces);
check_proc_sys_userns_file (_unprivileged_user_namespaces);
}
@ -877,32 +866,34 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
if (sigfillset (&sigset) < 0)
{
fprintf (stderr, "cannot fill sigset: %s\n", strerror (errno));
fprintf (stderr, "cannot fill sigset: %m\n");
_exit (EXIT_FAILURE);
}
if (sigdelset (&sigset, SIGCHLD) < 0)
{
fprintf (stderr, "cannot sigdelset(SIGCHLD): %s\n", strerror (errno));
fprintf (stderr, "cannot sigdelset(SIGCHLD): %m\n");
_exit (EXIT_FAILURE);
}
if (sigdelset (&sigset, SIGTERM) < 0)
{
fprintf (stderr, "cannot sigdelset(SIGTERM): %s\n", strerror (errno));
fprintf (stderr, "cannot sigdelset(SIGTERM): %m\n");
_exit (EXIT_FAILURE);
}
if (sigprocmask (SIG_BLOCK, &sigset, &oldsigset) < 0)
{
fprintf (stderr, "cannot block signals: %s\n", strerror (errno));
fprintf (stderr, "cannot block signals: %m\n");
_exit (EXIT_FAILURE);
}
argv = get_cmd_line_args ();
if (argv == NULL)
{
fprintf (stderr, "cannot read argv: %s\n", strerror (errno));
fprintf (stderr, "cannot read argv: %m\n");
_exit (EXIT_FAILURE);
}
argv0 = argv[0];
if (do_socket_activation)
{
char s[32];
@ -921,7 +912,7 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
ret = TEMP_FAILURE_RETRY (read (ready, &b, 1));
if (ret < 0)
{
fprintf (stderr, "cannot read from sync pipe: %s\n", strerror (errno));
fprintf (stderr, "cannot read from sync pipe: %m\n");
_exit (EXIT_FAILURE);
}
if (ret != 1 || b != '0')
@ -929,25 +920,24 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
if (syscall_setresgid (0, 0, 0) < 0)
{
fprintf (stderr, "cannot setresgid: %s\n", strerror (errno));
fprintf (stderr, "cannot setresgid: %m\n");
TEMP_FAILURE_RETRY (write (ready, "1", 1));
_exit (EXIT_FAILURE);
}
if (syscall_setresuid (0, 0, 0) < 0)
{
fprintf (stderr, "cannot setresuid: %s\n", strerror (errno));
fprintf (stderr, "cannot setresuid: %m\n");
TEMP_FAILURE_RETRY (write (ready, "1", 1));
_exit (EXIT_FAILURE);
}
if (chdir (cwd) < 0)
{
fprintf (stderr, "cannot chdir to %s: %s\n", cwd, strerror (errno));
fprintf (stderr, "cannot chdir to %s: %m\n", cwd);
TEMP_FAILURE_RETRY (write (ready, "1", 1));
_exit (EXIT_FAILURE);
}
free (cwd);
if (pause_pid_file_path && pause_pid_file_path[0] != '\0')
{
@ -961,14 +951,14 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
ret = TEMP_FAILURE_RETRY (write (ready, "0", 1));
if (ret < 0)
{
fprintf (stderr, "cannot write to ready pipe: %s\n", strerror (errno));
_exit (EXIT_FAILURE);
fprintf (stderr, "cannot write to ready pipe: %m\n");
_exit (EXIT_FAILURE);
}
close (ready);
if (sigprocmask (SIG_SETMASK, &oldsigset, NULL) < 0)
{
fprintf (stderr, "cannot block signals: %s\n", strerror (errno));
fprintf (stderr, "cannot block signals: %m\n");
_exit (EXIT_FAILURE);
}