Skip to content

Commit

Permalink
composefs: Relabel /usr/etc as /etc in the composefs image
Browse files Browse the repository at this point in the history
This allows using /usr/etc as /etc (vie e.g. a bind-mount or an
overlayfs mount) in the booted system.
  • Loading branch information
alexlarsson committed Oct 2, 2023
1 parent 86767ac commit 5e3ee4c
Showing 1 changed file with 62 additions and 17 deletions.
79 changes: 62 additions & 17 deletions src/libostree/ostree-repo-composefs.c
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,12 @@
#include <gio/gunixoutputstream.h>
#include <sys/ioctl.h>

#include "ostree-autocleanups.h"
#include "ostree-core-private.h"
#include "ostree-repo-file.h"
#include "ostree-repo-private.h"
#include "ostree-sepolicy-private.h"
#include "ostree-sepolicy.h"

#ifdef HAVE_COMPOSEFS
#include <libcomposefs/lcfs-writer.h>
Expand Down Expand Up @@ -268,7 +271,7 @@ _ostree_composefs_set_xattrs (struct lcfs_node_s *node, GVariant *xattrs, GCance
return TRUE;
}

static gboolean
static struct lcfs_node_s *
checkout_one_composefs_file_at (OstreeRepo *repo, const char *checksum, struct lcfs_node_s *parent,
const char *destination_name, GCancellable *cancellable,
GError **error)
Expand All @@ -279,15 +282,15 @@ checkout_one_composefs_file_at (OstreeRepo *repo, const char *checksum, struct l

/* Validate this up front to prevent path traversal attacks */
if (!ot_util_filename_validate (destination_name, error))
return FALSE;
return NULL;

existing = lcfs_node_lookup_child (parent, destination_name);
if (existing != NULL)
return glnx_throw (error, "Target checkout file already exist");
return glnx_null_throw (error, "Target checkout file already exist");

g_autoptr (GFileInfo) source_info = NULL;
if (!ostree_repo_load_file (repo, checksum, &input, &source_info, &xattrs, cancellable, error))
return FALSE;
return NULL;

const guint32 source_mode = g_file_info_get_attribute_uint32 (source_info, "unix::mode");
const guint32 source_uid = g_file_info_get_attribute_uint32 (source_info, "unix::uid");
Expand All @@ -298,13 +301,13 @@ checkout_one_composefs_file_at (OstreeRepo *repo, const char *checksum, struct l

struct lcfs_node_s *node = lcfs_node_new ();
if (node == NULL)
return glnx_throw (error, "Out of memory");
return glnx_null_throw (error, "Out of memory");

/* Takes ownership on success */
if (lcfs_node_add_child (parent, node, destination_name) != 0)
{
lcfs_node_unref (node);
return glnx_throw_errno (error);
return glnx_null_throw_errno (error);
}

lcfs_node_set_mode (node, source_mode);
Expand All @@ -315,14 +318,14 @@ checkout_one_composefs_file_at (OstreeRepo *repo, const char *checksum, struct l
{
const char *source_symlink_target = g_file_info_get_symlink_target (source_info);
if (lcfs_node_set_payload (node, source_symlink_target) != 0)
return glnx_throw_errno (error);
return glnx_null_throw_errno (error);
}
else if (source_size != 0)
{
char loose_path_buf[_OSTREE_LOOSE_PATH_MAX];
_ostree_loose_path (loose_path_buf, checksum, OSTREE_OBJECT_TYPE_FILE, OSTREE_REPO_MODE_BARE);
if (lcfs_node_set_payload (node, loose_path_buf) != 0)
return glnx_throw_errno (error);
return glnx_null_throw_errno (error);

guchar *known_digest = NULL;

Expand All @@ -349,24 +352,41 @@ checkout_one_composefs_file_at (OstreeRepo *repo, const char *checksum, struct l
if (known_digest)
lcfs_node_set_fsverity_digest (node, known_digest);
else if (lcfs_node_set_fsverity_from_content (node, input, _composefs_read_cb) != 0)
return glnx_throw_errno (error);
return glnx_null_throw_errno (error);
}

if (xattrs)
{
if (!_ostree_composefs_set_xattrs (node, xattrs, cancellable, error))
return FALSE;
return NULL;
}

g_clear_object (&input);

return node;
}

/* Relabel node as if it had path for_path. Used to relabel /usr/etc as /etc */
static gboolean
composefs_node_relabel_for (struct lcfs_node_s *node, const char *for_path, guint32 mode,
OstreeSePolicy *sepolicy, GCancellable *cancellable, GError **error)
{
g_autofree char *label = NULL;

if (!ostree_sepolicy_get_label (sepolicy, for_path, mode, &label, cancellable, error))
return FALSE;

if (label && lcfs_node_set_xattr (node, "security.selinux", label, strlen (label) + 1) != 0)
return glnx_throw_errno_prefix (error, "relabeling for %s", for_path);

return TRUE;
}

static gboolean
checkout_composefs_recurse (OstreeRepo *self, const char *dirtree_checksum,
const char *dirmeta_checksum, struct lcfs_node_s *parent,
const char *name, GCancellable *cancellable, GError **error)
const char *name, const char *path, OstreeSePolicy *sepolicy,
GCancellable *cancellable, GError **error)
{
g_autoptr (GVariant) dirtree = NULL;
g_autoptr (GVariant) dirmeta = NULL;
Expand Down Expand Up @@ -414,6 +434,13 @@ checkout_composefs_recurse (OstreeRepo *self, const char *dirtree_checksum,
if (xattrs && !_ostree_composefs_set_xattrs (directory, xattrs, cancellable, error))
return FALSE;

gboolean in_usr_etc = strcmp (path, "/usr/etc") == 0 || g_str_has_prefix (path, "/usr/etc/");

if (in_usr_etc
&& !composefs_node_relabel_for (directory, path + strlen ("/usr"), mode, sepolicy,
cancellable, error))
return FALSE;

/* Process files in this subdir */
{
g_autoptr (GVariant) dir_file_contents = g_variant_get_child_value (dirtree, 0);
Expand All @@ -426,9 +453,18 @@ checkout_composefs_recurse (OstreeRepo *self, const char *dirtree_checksum,
char tmp_checksum[OSTREE_SHA256_STRING_LEN + 1];
_ostree_checksum_inplace_from_bytes_v (contents_csum_v, tmp_checksum);

if (!checkout_one_composefs_file_at (self, tmp_checksum, directory, fname, cancellable,
error))
struct lcfs_node_s *node = checkout_one_composefs_file_at (self, tmp_checksum, directory,
fname, cancellable, error);
if (node == NULL)
return FALSE;

if (in_usr_etc)
{
g_autofree char *child_path = g_build_filename (path + strlen ("/usr"), fname, NULL);
if (!composefs_node_relabel_for (node, child_path, lcfs_node_get_mode (node), sepolicy,
cancellable, error))
return FALSE;
}
}
contents_csum_v = NULL; /* iter_loop freed it */
}
Expand Down Expand Up @@ -457,8 +493,10 @@ checkout_composefs_recurse (OstreeRepo *self, const char *dirtree_checksum,
_ostree_checksum_inplace_from_bytes_v (subdirtree_csum_v, subdirtree_checksum);
char subdirmeta_checksum[OSTREE_SHA256_STRING_LEN + 1];
_ostree_checksum_inplace_from_bytes_v (subdirmeta_csum_v, subdirmeta_checksum);

g_autofree char *child_path = g_build_filename (path, dname, NULL);
if (!checkout_composefs_recurse (self, subdirtree_checksum, subdirmeta_checksum, directory,
dname, cancellable, error))
dname, child_path, sepolicy, cancellable, error))
return FALSE;
}
/* Freed by iter-loop */
Expand All @@ -472,7 +510,8 @@ checkout_composefs_recurse (OstreeRepo *self, const char *dirtree_checksum,
/* Begin a checkout process */
static gboolean
checkout_composefs_tree (OstreeRepo *self, OstreeComposefsTarget *target, OstreeRepoFile *source,
GFileInfo *source_info, GCancellable *cancellable, GError **error)
GFileInfo *source_info, OstreeSePolicy *sepolicy,
GCancellable *cancellable, GError **error)
{
if (g_file_info_get_file_type (source_info) != G_FILE_TYPE_DIRECTORY)
return glnx_throw (error, "Root checkout of composefs must be directory");
Expand All @@ -488,7 +527,7 @@ checkout_composefs_tree (OstreeRepo *self, OstreeComposefsTarget *target, Ostree
const char *dirtree_checksum = ostree_repo_file_tree_get_contents_checksum (source);
const char *dirmeta_checksum = ostree_repo_file_tree_get_metadata_checksum (source);
return checkout_composefs_recurse (self, dirtree_checksum, dirmeta_checksum, target->dest, "root",
cancellable, error);
"/", sepolicy, cancellable, error);
}

static struct lcfs_node_s *
Expand Down Expand Up @@ -541,13 +580,19 @@ ostree_repo_checkout_composefs (OstreeRepo *self, OstreeComposefsTarget *target,
int i;
struct lcfs_node_s *root, *dir;

g_autoptr (GError) my_error = NULL;
g_autoptr (OstreeSePolicy) sepolicy
= ostree_sepolicy_new_from_root (self, source, cancellable, &my_error);
if (sepolicy == NULL)
return FALSE;

g_autoptr (GFileInfo) target_info
= g_file_query_info (G_FILE (source), OSTREE_GIO_FAST_QUERYINFO,
G_FILE_QUERY_INFO_NOFOLLOW_SYMLINKS, cancellable, error);
if (!target_info)
return FALSE;

if (!checkout_composefs_tree (self, target, source, target_info, cancellable, error))
if (!checkout_composefs_tree (self, target, source, target_info, sepolicy, cancellable, error))
return FALSE;

/* We need a root dir */
Expand Down

0 comments on commit 5e3ee4c

Please sign in to comment.