mirror of
https://github.com/containers/podman.git
synced 2025-06-28 14:29:04 +08:00
Merge pull request #16790 from giuseppe/cli-handler
rootless: add cli validator
This commit is contained in:
@ -8,6 +8,7 @@
|
|||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <limits.h>
|
#include <limits.h>
|
||||||
#include <sys/types.h>
|
#include <sys/types.h>
|
||||||
|
#include <sys/stat.h>
|
||||||
#include <signal.h>
|
#include <signal.h>
|
||||||
#include <fcntl.h>
|
#include <fcntl.h>
|
||||||
#include <sys/wait.h>
|
#include <sys/wait.h>
|
||||||
@ -19,6 +20,9 @@
|
|||||||
#include <sys/select.h>
|
#include <sys/select.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
|
|
||||||
|
#define ETC_AUTH_SCRIPTS "/etc/containers/auth-scripts"
|
||||||
|
#define LIBEXECPODMAN "/usr/libexec/podman"
|
||||||
|
|
||||||
#ifndef TEMP_FAILURE_RETRY
|
#ifndef TEMP_FAILURE_RETRY
|
||||||
#define TEMP_FAILURE_RETRY(expression) \
|
#define TEMP_FAILURE_RETRY(expression) \
|
||||||
(__extension__ \
|
(__extension__ \
|
||||||
@ -117,6 +121,155 @@ rootless_gid ()
|
|||||||
return rootless_gid_init;
|
return rootless_gid_init;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* exec the specified executable and exit if it fails. */
|
||||||
|
static void
|
||||||
|
exec_binary (const char *path, char **argv, int argc)
|
||||||
|
{
|
||||||
|
int r, status = 0;
|
||||||
|
pid_t pid;
|
||||||
|
|
||||||
|
pid = fork ();
|
||||||
|
if (pid < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "fork: %m\n");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (pid == 0)
|
||||||
|
{
|
||||||
|
size_t i;
|
||||||
|
char **newargv = malloc ((argc + 2) * sizeof(char *));
|
||||||
|
if (!newargv)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "malloc: %m\n");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
newargv[0] = (char*) path;
|
||||||
|
for (i = 0; i < argc; i++)
|
||||||
|
newargv[i+1] = argv[i];
|
||||||
|
|
||||||
|
newargv[i+1] = NULL;
|
||||||
|
errno = 0;
|
||||||
|
execv (path, newargv);
|
||||||
|
/* If the file was deleted in the meanwhile, return success. */
|
||||||
|
if (errno == ENOENT)
|
||||||
|
exit (EXIT_SUCCESS);
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
r = TEMP_FAILURE_RETRY (waitpid (pid, &status, 0));
|
||||||
|
if (r < 0)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "waitpid: %m\n");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
if (WIFEXITED(status) && WEXITSTATUS (status))
|
||||||
|
{
|
||||||
|
fprintf (stderr, "external auth script %s failed\n", path);
|
||||||
|
exit (WEXITSTATUS(status));
|
||||||
|
}
|
||||||
|
if (WIFSIGNALED (status))
|
||||||
|
{
|
||||||
|
fprintf (stderr, "external auth script %s failed\n", path);
|
||||||
|
exit (127+WTERMSIG (status));
|
||||||
|
}
|
||||||
|
if (WIFSTOPPED (status))
|
||||||
|
{
|
||||||
|
fprintf (stderr, "external auth script %s failed\n", path);
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
do_auth_scripts_dir (const char *dir, char **argv, int argc)
|
||||||
|
{
|
||||||
|
cleanup_free char *buffer = NULL;
|
||||||
|
cleanup_dir DIR *d = NULL;
|
||||||
|
size_t i, nfiles = 0;
|
||||||
|
struct dirent *de;
|
||||||
|
|
||||||
|
/* Store how many FDs were open before the Go runtime kicked in. */
|
||||||
|
d = opendir (dir);
|
||||||
|
if (!d)
|
||||||
|
{
|
||||||
|
if (errno != ENOENT)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "opendir %s: %m\n", dir);
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
|
||||||
|
for (de = readdir (d); de; de = readdir (d))
|
||||||
|
{
|
||||||
|
buffer = realloc (buffer, (nfiles + 1) * (NAME_MAX + 1));
|
||||||
|
if (buffer == NULL)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "realloc buffer: %m\n");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (de->d_type != DT_REG)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
strncpy (buffer + nfiles * (NAME_MAX + 1), de->d_name, NAME_MAX + 1);
|
||||||
|
nfiles++;
|
||||||
|
buffer[nfiles * (NAME_MAX + 1)] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
qsort (buffer, nfiles, NAME_MAX + 1, (int (*)(const void *, const void *)) strcmp);
|
||||||
|
|
||||||
|
for (i = 0; i < nfiles; i++)
|
||||||
|
{
|
||||||
|
const char *fname = buffer + i * (NAME_MAX + 1);
|
||||||
|
char path[PATH_MAX];
|
||||||
|
struct stat st;
|
||||||
|
int ret;
|
||||||
|
|
||||||
|
ret = snprintf (path, PATH_MAX, "%s/%s", dir, fname);
|
||||||
|
if (ret == PATH_MAX)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "internal error: path too long\n");
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
ret = stat (path, &st);
|
||||||
|
if (ret < 0)
|
||||||
|
{
|
||||||
|
/* Ignore the failure if the file was deleted. */
|
||||||
|
if (errno == ENOENT)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
fprintf (stderr, "stat %s: %m\n", path);
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Not an executable. */
|
||||||
|
if ((st.st_mode & (S_IXUSR | S_IXGRP | S_IXOTH)) == 0)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
exec_binary (path, argv, argc);
|
||||||
|
errno = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (errno)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "readdir %s: %m\n", dir);
|
||||||
|
exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
do_auth_scripts (char **argv, int argc)
|
||||||
|
{
|
||||||
|
char *auth_scripts = getenv ("PODMAN_AUTH_SCRIPTS_DIR");
|
||||||
|
do_auth_scripts_dir (LIBEXECPODMAN "/auth-scripts", argv, argc);
|
||||||
|
do_auth_scripts_dir (ETC_AUTH_SCRIPTS, argv, argc);
|
||||||
|
if (auth_scripts && auth_scripts[0])
|
||||||
|
do_auth_scripts_dir (auth_scripts, argv, argc);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
do_pause ()
|
do_pause ()
|
||||||
{
|
{
|
||||||
@ -134,7 +287,7 @@ do_pause ()
|
|||||||
sigaction (sig[i], &act, NULL);
|
sigaction (sig[i], &act, NULL);
|
||||||
|
|
||||||
/* Attempt to execv catatonit to keep the pause process alive. */
|
/* Attempt to execv catatonit to keep the pause process alive. */
|
||||||
execl ("/usr/libexec/podman/catatonit", "catatonit", "-P", NULL);
|
execl (LIBEXECPODMAN "catatonit", "catatonit", "-P", NULL);
|
||||||
execl ("/usr/bin/catatonit", "catatonit", "-P", NULL);
|
execl ("/usr/bin/catatonit", "catatonit", "-P", NULL);
|
||||||
/* and if the catatonit executable could not be found, fallback here... */
|
/* and if the catatonit executable could not be found, fallback here... */
|
||||||
|
|
||||||
@ -144,7 +297,7 @@ do_pause ()
|
|||||||
}
|
}
|
||||||
|
|
||||||
static char **
|
static char **
|
||||||
get_cmd_line_args ()
|
get_cmd_line_args (int *argc_out)
|
||||||
{
|
{
|
||||||
cleanup_free char *buffer = NULL;
|
cleanup_free char *buffer = NULL;
|
||||||
cleanup_close int fd = -1;
|
cleanup_close int fd = -1;
|
||||||
@ -204,13 +357,15 @@ get_cmd_line_args ()
|
|||||||
/* Move ownership. */
|
/* Move ownership. */
|
||||||
buffer = NULL;
|
buffer = NULL;
|
||||||
|
|
||||||
|
if (argc_out)
|
||||||
|
*argc_out = argc;
|
||||||
|
|
||||||
return argv;
|
return argv;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
can_use_shortcut ()
|
can_use_shortcut (char **argv)
|
||||||
{
|
{
|
||||||
cleanup_free char **argv = NULL;
|
|
||||||
cleanup_free char *argv0 = NULL;
|
cleanup_free char *argv0 = NULL;
|
||||||
bool ret = true;
|
bool ret = true;
|
||||||
int argc;
|
int argc;
|
||||||
@ -219,10 +374,6 @@ can_use_shortcut ()
|
|||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
argv = get_cmd_line_args ();
|
|
||||||
if (argv == NULL)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
argv0 = argv[0];
|
argv0 = argv[0];
|
||||||
|
|
||||||
if (strstr (argv[0], "podman") == NULL)
|
if (strstr (argv[0], "podman") == NULL)
|
||||||
@ -287,7 +438,9 @@ static void __attribute__((constructor)) init()
|
|||||||
const char *listen_pid;
|
const char *listen_pid;
|
||||||
const char *listen_fds;
|
const char *listen_fds;
|
||||||
const char *listen_fdnames;
|
const char *listen_fdnames;
|
||||||
|
cleanup_free char **argv = NULL;
|
||||||
cleanup_dir DIR *d = NULL;
|
cleanup_dir DIR *d = NULL;
|
||||||
|
int argc;
|
||||||
|
|
||||||
pause = getenv ("_PODMAN_PAUSE");
|
pause = getenv ("_PODMAN_PAUSE");
|
||||||
if (pause && pause[0])
|
if (pause && pause[0])
|
||||||
@ -337,6 +490,16 @@ static void __attribute__((constructor)) init()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
argv = get_cmd_line_args (&argc);
|
||||||
|
if (argv == NULL)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "cannot retrieve cmd line");
|
||||||
|
_exit (EXIT_FAILURE);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (geteuid () != 0 || getenv ("_CONTAINERS_USERNS_CONFIGURED") == NULL)
|
||||||
|
do_auth_scripts(argv, argc);
|
||||||
|
|
||||||
listen_pid = getenv("LISTEN_PID");
|
listen_pid = getenv("LISTEN_PID");
|
||||||
listen_fds = getenv("LISTEN_FDS");
|
listen_fds = getenv("LISTEN_FDS");
|
||||||
listen_fdnames = getenv("LISTEN_FDNAMES");
|
listen_fdnames = getenv("LISTEN_FDNAMES");
|
||||||
@ -360,7 +523,7 @@ static void __attribute__((constructor)) init()
|
|||||||
/* Shortcut. If we are able to join the pause pid file, do it now so we don't
|
/* Shortcut. If we are able to join the pause pid file, do it now so we don't
|
||||||
need to re-exec. */
|
need to re-exec. */
|
||||||
xdg_runtime_dir = getenv ("XDG_RUNTIME_DIR");
|
xdg_runtime_dir = getenv ("XDG_RUNTIME_DIR");
|
||||||
if (geteuid () != 0 && xdg_runtime_dir && xdg_runtime_dir[0] && can_use_shortcut ())
|
if (geteuid () != 0 && xdg_runtime_dir && xdg_runtime_dir[0] && can_use_shortcut (argv))
|
||||||
{
|
{
|
||||||
cleanup_free char *cwd = NULL;
|
cleanup_free char *cwd = NULL;
|
||||||
cleanup_close int userns_fd = -1;
|
cleanup_close int userns_fd = -1;
|
||||||
@ -648,7 +811,7 @@ reexec_userns_join (int pid_to_join, char *pause_pid_file_path)
|
|||||||
sprintf (uid, "%d", geteuid ());
|
sprintf (uid, "%d", geteuid ());
|
||||||
sprintf (gid, "%d", getegid ());
|
sprintf (gid, "%d", getegid ());
|
||||||
|
|
||||||
argv = get_cmd_line_args ();
|
argv = get_cmd_line_args (NULL);
|
||||||
if (argv == NULL)
|
if (argv == NULL)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "cannot read argv: %m\n");
|
fprintf (stderr, "cannot read argv: %m\n");
|
||||||
@ -898,7 +1061,7 @@ reexec_in_user_namespace (int ready, char *pause_pid_file_path, char *file_to_re
|
|||||||
_exit (EXIT_FAILURE);
|
_exit (EXIT_FAILURE);
|
||||||
}
|
}
|
||||||
|
|
||||||
argv = get_cmd_line_args ();
|
argv = get_cmd_line_args (NULL);
|
||||||
if (argv == NULL)
|
if (argv == NULL)
|
||||||
{
|
{
|
||||||
fprintf (stderr, "cannot read argv: %m\n");
|
fprintf (stderr, "cannot read argv: %m\n");
|
||||||
|
33
test/system/950-auth-scripts.bats
Normal file
33
test/system/950-auth-scripts.bats
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
#!/usr/bin/env bats
|
||||||
|
#
|
||||||
|
# Tests for podman auth scripts
|
||||||
|
#
|
||||||
|
|
||||||
|
load helpers
|
||||||
|
load helpers.network
|
||||||
|
|
||||||
|
function setup() {
|
||||||
|
basic_setup
|
||||||
|
}
|
||||||
|
|
||||||
|
function teardown() {
|
||||||
|
basic_teardown
|
||||||
|
}
|
||||||
|
|
||||||
|
@test "podman auth script" {
|
||||||
|
auth_dir=$PODMAN_TMPDIR/auth
|
||||||
|
mkdir -p $auth_dir
|
||||||
|
auth_script=$auth_dir/pull_check.sh
|
||||||
|
|
||||||
|
cat > $auth_script <<EOF
|
||||||
|
#!/bin/sh
|
||||||
|
if echo \$@ | grep "pull foobar"; then
|
||||||
|
exit 42
|
||||||
|
fi
|
||||||
|
exit 43
|
||||||
|
EOF
|
||||||
|
chmod +x $auth_script
|
||||||
|
|
||||||
|
PODMAN_AUTH_SCRIPTS_DIR=$auth_dir run_podman 42 pull foobar
|
||||||
|
PODMAN_AUTH_SCRIPTS_DIR=$auth_dir run_podman 43 pull barfoo
|
||||||
|
}
|
Reference in New Issue
Block a user