Skip to content

Commit

Permalink
apparmor: stack apparmor profiles if nnp and confined
Browse files Browse the repository at this point in the history
In case crun is running under apparmor profile and
no_new_privileges flag set for containers
the only way apparmor allows a change of profile
is when a profile is stacked on top of current profile
to ensure no new permissions are gained

Closes: containers#1385
Signed-off-by: 😎Mostafa Emami <mustafaemami@gmail.com>
  • Loading branch information
idleroamer committed Jan 16, 2024
1 parent d1a8dc0 commit eb9c717
Show file tree
Hide file tree
Showing 3 changed files with 86 additions and 37 deletions.
2 changes: 1 addition & 1 deletion src/libcrun/linux.c
Original file line number Diff line number Diff line change
Expand Up @@ -3232,7 +3232,7 @@ int
libcrun_set_apparmor_profile (runtime_spec_schema_config_schema_process *proc, bool now, libcrun_error_t *err)
{
if (proc->apparmor_profile)
return set_apparmor_profile (proc->apparmor_profile, now, err);
return set_apparmor_profile (proc->apparmor_profile, proc->no_new_privileges, now, err);
return 0;
}

Expand Down
119 changes: 84 additions & 35 deletions src/libcrun/utils.c
Original file line number Diff line number Diff line change
Expand Up @@ -823,56 +823,78 @@ add_selinux_mount_label (char **retlabel, const char *data, const char *label, c
return 0;
}

static int
set_security_attr (const char *lsm, const char *fname, const char *data, libcrun_error_t *err)
static const char *
lsm_attr_path (const char *lsm, const char *fname, libcrun_error_t *err)
{
int ret;
struct statfs sfs;

cleanup_close int attr_dirfd = -1;
cleanup_close int lsm_dirfd = -1;
cleanup_close int fd = -1;
char *attr_path = NULL;

attr_dirfd = open ("/proc/thread-self/attr", O_DIRECTORY | O_RDONLY | O_CLOEXEC);
if (UNLIKELY (attr_dirfd < 0))
return crun_make_error (err, errno, "open `/proc/thread-self/attr`");
{
crun_make_error (err, errno, "open `/proc/thread-self/attr`");
return NULL;
}

// Check for newer scoped interface in /proc/thread-self/attr/<lsm>
if (lsm != NULL)
{
lsm_dirfd = openat (attr_dirfd, lsm, O_DIRECTORY | O_RDONLY | O_CLOEXEC);

if (UNLIKELY (lsm_dirfd < 0 && errno != ENOENT))
return crun_make_error (err, errno, "open `/proc/thread-self/attr/%s`", lsm);
{
crun_make_error (err, errno, "open `/proc/thread-self/attr/%s`", lsm);
return NULL;
}
}

// Use scoped interface if available, fall back to unscoped
if (lsm_dirfd >= 0)
fd = openat (lsm_dirfd, fname, O_WRONLY | O_CLOEXEC);
else
fd = openat (attr_dirfd, fname, O_WRONLY | O_CLOEXEC);
xasprintf (&attr_path, "/proc/thread-self/attr/%s%s%s" + lsm_dirfd >= 0 ? lsm : "", lsm_dirfd >= 0 ? "/" : "", fname);

if (UNLIKELY (fd < 0))
return crun_make_error (err, errno, "open `/proc/thread-self/attr/%s%s%s`",
lsm_dirfd >= 0 ? lsm : "", lsm_dirfd >= 0 ? "/" : "", fname);
return attr_path;
}

// Check that the file system type is indeed procfs
ret = fstatfs (fd, &sfs);
static int
is_proc_super_magic (int fd, const char *path, libcrun_error_t *err)
{
struct statfs sfs;

int ret = fstatfs (fd, &sfs);
if (UNLIKELY (ret < 0))
return crun_make_error (err, errno, "statfs `/proc/thread-self/attr/%s%s%s`",
lsm_dirfd >= 0 ? lsm : "", lsm_dirfd >= 0 ? "/" : "", fname);
return crun_make_error (err, errno, "statfs `%s`", path);

if (sfs.f_type != PROC_SUPER_MAGIC)
return crun_make_error (err, 0, "the file `/proc/thread-self/attr/%s%s%s` is not on a `procfs` file system",
lsm_dirfd >= 0 ? lsm : "", lsm_dirfd >= 0 ? "/" : "", fname);
return crun_make_error (err, 0, "the file `%s` is not on a `procfs` file system", path);

return 0;
}

static int
set_security_attr (const char *lsm, const char *fname, const char *data, libcrun_error_t *err)
{
int ret;

cleanup_free const char *attr_path = lsm_attr_path (lsm, fname, err);
cleanup_close int fd = -1;

if (UNLIKELY (attr_path == NULL))
return -1;

fd = open (attr_path, O_WRONLY | O_CLOEXEC);

if (UNLIKELY (fd < 0))
return crun_make_error (err, errno, "open `%s`", attr_path);

ret = is_proc_super_magic (fd, attr_path, err);
if (UNLIKELY (ret < 0))
return ret;

// Write out data
ret = TEMP_FAILURE_RETRY (write (fd, data, strlen (data)));
if (UNLIKELY (ret < 0))
return crun_make_error (err, errno, "write file `/proc/thread-self/attr/%s%s%s`",
lsm_dirfd >= 0 ? lsm : "", lsm_dirfd >= 0 ? "/" : "", fname);
return crun_make_error (err, errno, "write file `%s`", attr_path);

return 0;
return ret;
}

int
Expand All @@ -897,8 +919,33 @@ libcrun_is_apparmor_enabled (libcrun_error_t *err)
return apparmor_enabled;
}

static int
is_current_process_confined (libcrun_error_t *err)
{
cleanup_free const char *attr_path = lsm_attr_path ("apparmor", "current", err);
cleanup_close int fd = -1;
char buf[256];

if (UNLIKELY (attr_path == NULL))
return -1;

fd = open (attr_path, O_RDONLY | O_CLOEXEC);

if (UNLIKELY (fd < 0))
return crun_make_error (err, errno, "open `%s`", attr_path);

if (UNLIKELY (is_proc_super_magic (fd, attr_path, err)))
return -1;

ssize_t bytes_read = read (fd, buf, sizeof (buf) - 1);
if (UNLIKELY (bytes_read == -1))
return crun_make_error (err, errno, "error reading file `%s`", attr_path);

return (strncmp (buf, "unconfined", bytes_read) != 0 && buf[0] != '\0');
}

int
set_apparmor_profile (const char *profile, bool now, libcrun_error_t *err)
set_apparmor_profile (const char *profile, bool no_new_privileges, bool now, libcrun_error_t *err)
{
int ret;

Expand All @@ -909,8 +956,13 @@ set_apparmor_profile (const char *profile, bool now, libcrun_error_t *err)
if (ret)
{
cleanup_free char *buf = NULL;

xasprintf (&buf, "%s %s", now ? "changeprofile" : "exec", profile);
ret = is_current_process_confined (err);
if (UNLIKELY (ret < 0))
return ret;
// if confined only way for apparmor to allow change of profile with NNP is with stacking
xasprintf (&buf, "%s %s", no_new_privileges && ret ? "stack" : now ? "changeprofile"
: "exec",
profile);

return set_security_attr ("apparmor", now ? "current" : "exec", buf, err);
}
Expand Down Expand Up @@ -1262,7 +1314,8 @@ copy_from_fd_to_fd (int src, int dst, int consume, libcrun_error_t *err)
return crun_make_error (err, errno, "write");
remaining -= ret;
}
} while (consume && nread);
}
while (consume && nread);

return 0;
}
Expand Down Expand Up @@ -1695,7 +1748,6 @@ mark_or_close_fds_ge_than (int n, bool close_now, libcrun_error_t *err)
cleanup_dir DIR *dir = NULL;
int ret;
int fd;
struct statfs sfs;
struct dirent *next;

ret = syscall_close_range (n, UINT_MAX, close_now ? 0 : CLOSE_RANGE_CLOEXEC);
Expand All @@ -1708,12 +1760,9 @@ mark_or_close_fds_ge_than (int n, bool close_now, libcrun_error_t *err)
if (UNLIKELY (cfd < 0))
return crun_make_error (err, errno, "open `/proc/self/fd`");

ret = fstatfs (cfd, &sfs);
ret = is_proc_super_magic (cfd, "/proc/self/fd", err);
if (UNLIKELY (ret < 0))
return crun_make_error (err, errno, "statfs `/proc/self/fd`");

if (sfs.f_type != PROC_SUPER_MAGIC)
return crun_make_error (err, 0, "the path `/proc/self/fd` is not on file system type `procfs`");
return ret;

dir = fdopendir (cfd);
if (UNLIKELY (dir == NULL))
Expand Down
2 changes: 1 addition & 1 deletion src/libcrun/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,7 @@ int set_selinux_label (const char *label, bool now, libcrun_error_t *err);

int add_selinux_mount_label (char **ret, const char *data, const char *label, const char *context_type, libcrun_error_t *err);

int set_apparmor_profile (const char *profile, bool now, libcrun_error_t *err);
int set_apparmor_profile (const char *profile, bool no_new_privileges, bool now, libcrun_error_t *err);

int read_all_fd_with_size_hint (int fd, const char *description, char **out, size_t *len, size_t hint, libcrun_error_t *err);

Expand Down

0 comments on commit eb9c717

Please sign in to comment.