Skip to content

Commit

Permalink
linux: harden chdir()
Browse files Browse the repository at this point in the history
there was recently a security vulnerability (CVE-2024-21626) that
allowed a malicious user to chdir(2) to a /proc/*/fd entry that is
outside the container rootfs.  While crun is not affected directly,
harden chdir by validating that we are still inside the container
rootfs.

Signed-off-by: Giuseppe Scrivano <gscrivan@redhat.com>
  • Loading branch information
giuseppe committed Feb 2, 2024
1 parent f157e80 commit 7790d5c
Show file tree
Hide file tree
Showing 3 changed files with 68 additions and 7 deletions.
24 changes: 17 additions & 7 deletions src/libcrun/container.c
Original file line number Diff line number Diff line change
Expand Up @@ -1172,8 +1172,13 @@ container_init_setup (void *args, pid_t own_pid, char *notify_socket,
/* Attempt to chdir immediately here, before doing the setresuid. If we fail here, let's
try again later once the process switched to the user that runs in the container. */
if (def->process && def->process->cwd)
if (LIKELY (chdir (def->process->cwd) == 0))
chdir_done = true;
{
ret = libcrun_safe_chdir (def->process->cwd, err);
if (LIKELY (ret == 0))
chdir_done = true;
else
crun_error_release (err);
}

if (def->process && def->process->args)
{
Expand Down Expand Up @@ -1290,8 +1295,11 @@ container_init_setup (void *args, pid_t own_pid, char *notify_socket,

/* The chdir was not already performed, so try again now after switching to the UID/GID in the container. */
if (! chdir_done && def->process && def->process->cwd)
if (UNLIKELY (chdir (def->process->cwd) < 0))
return crun_make_error (err, errno, "chdir `%s`", def->process->cwd);
{
ret = libcrun_safe_chdir (def->process->cwd, err);
if (UNLIKELY (ret < 0))
return ret;
}

if (notify_socket && putenv (notify_socket) < 0)
return crun_make_error (err, errno, "putenv `%s`", notify_socket);
Expand Down Expand Up @@ -3239,8 +3247,10 @@ exec_process_entrypoint (libcrun_context_t *context,
TEMP_FAILURE_RETRY (read (pipefd1, &own_pid, sizeof (own_pid)));

cwd = process->cwd ? process->cwd : "/";
if (LIKELY (chdir (cwd) == 0))
if (LIKELY (libcrun_safe_chdir (cwd, err) == 0))
chdir_done = true;
else
crun_error_release (err);

ret = unblock_signals (err);
if (UNLIKELY (ret < 0))
Expand Down Expand Up @@ -3364,8 +3374,8 @@ exec_process_entrypoint (libcrun_context_t *context,
}
}

if (! chdir_done && UNLIKELY (chdir (cwd) < 0))
libcrun_fail_with_error (errno, "chdir `%s`", cwd);
if (UNLIKELY ((! chdir_done) && libcrun_safe_chdir (cwd, err) < 0))
libcrun_fail_with_error ((*err)->status, "%s", (*err)->msg);

if (process->no_new_privileges)
{
Expand Down
49 changes: 49 additions & 0 deletions src/libcrun/linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -250,6 +250,12 @@ struct _clone3_args
__aligned_u64 cgroup;
};

static int
syscall_getcwd (char *path, size_t len)
{
return (int) syscall (__NR_getcwd, path, len);
}

static int
syscall_clone3 (struct _clone3_args *args)
{
Expand Down Expand Up @@ -5713,3 +5719,46 @@ libcrun_update_intel_rdt (const char *ctr_name, libcrun_container_t *container,

return resctl_update (name, l3_cache_schema, mem_bw_schema, err);
}

/* Change the current directory and make sure the current working
directory, once set, is accessible from the current mount
namespace. This check prevents container-escape issues like
CVE-2024-21626.
The current working directory cannot be longer than PATH_MAX.
*/
int
libcrun_safe_chdir (const char *path, libcrun_error_t *err)
{
cleanup_free char *buffer = NULL;
int ret;

ret = chdir (path);
if (UNLIKELY (ret < 0))
return crun_make_error (err, errno, "chdir to `%s`", path);

buffer = xmalloc (PATH_MAX);
ret = syscall_getcwd (buffer, PATH_MAX);
if (UNLIKELY (ret < 0))
return crun_make_error (err, errno, "getcwd");

/* Enforce that the returned path is an absolute path. */
if (ret == 0 || buffer[0] != '/')
{
(void) chdir ("/");
errno = ENOENT;

/*
The kernel prepends the string "(unreachable)" to the path
when it is not reachable from the current mount namespace.
Use it to give a better error message.
*/
#define UNREACHABLE "unreachable"
#define UNREACHABLE_LEN (sizeof (UNREACHABLE) - 1)

if ((ret >= UNREACHABLE_LEN) && (memcmp (buffer, UNREACHABLE, UNREACHABLE_LEN) == 0))
return crun_make_error (err, errno, "the working directory is not accessible from the current namespace");

return crun_make_error (err, errno, "the current working directory is not an absolute path");
}
return 0;
}
2 changes: 2 additions & 0 deletions src/libcrun/linux.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,6 @@ int libcrun_destroy_intelrdt (const char *name, libcrun_error_t *err);

int libcrun_update_intel_rdt (const char *ctr_name, libcrun_container_t *container, const char *l3_cache_schema, const char *mem_bw_schema, libcrun_error_t *err);

int libcrun_safe_chdir (const char *path, libcrun_error_t *err);

#endif

0 comments on commit 7790d5c

Please sign in to comment.