From 8792007bc10ebb74ab009059e2edc69a1f9cdd61 Mon Sep 17 00:00:00 2001 From: Colin Walters Date: Tue, 3 Apr 2012 08:52:58 -0400 Subject: [PATCH] core: Validate structure of objects in fsck --- src/libostree/ostree-core.c | 205 +++++++++++++++++++++++++++++++---- src/libostree/ostree-core.h | 21 +++- src/ostree/ot-builtin-fsck.c | 23 ++++ 3 files changed, 222 insertions(+), 27 deletions(-) diff --git a/src/libostree/ostree-core.c b/src/libostree/ostree-core.c index 06897a2c..947bc6dc 100644 --- a/src/libostree/ostree-core.c +++ b/src/libostree/ostree-core.c @@ -35,30 +35,7 @@ gboolean ostree_validate_checksum_string (const char *sha256, GError **error) { - int i = 0; - size_t len = strlen (sha256); - - if (len != 64) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Invalid rev '%s'", sha256); - return FALSE; - } - - for (i = 0; i < len; i++) - { - guint8 c = ((guint8*) sha256)[i]; - - if (!((c >= 48 && c <= 57) - || (c >= 97 && c <= 102))) - { - g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, - "Invalid character '%d' in rev '%s'", - c, sha256); - return FALSE; - } - } - return TRUE; + return ostree_validate_structureof_checksum_string (sha256, error); } gboolean @@ -1477,6 +1454,36 @@ ostree_validate_structureof_checksum (GVariant *checksum, return TRUE; } +gboolean +ostree_validate_structureof_checksum_string (const char *checksum, + GError **error) +{ + int i = 0; + size_t len = strlen (checksum); + + if (len != 64) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Invalid rev '%s'", checksum); + return FALSE; + } + + for (i = 0; i < len; i++) + { + guint8 c = ((guint8*) checksum)[i]; + + if (!((c >= 48 && c <= 57) + || (c >= 97 && c <= 102))) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Invalid character '%d' in rev '%s'", + c, checksum); + return FALSE; + } + } + return TRUE; +} + static gboolean validate_variant (GVariant *variant, const GVariantType *variant_type, @@ -1498,6 +1505,156 @@ validate_variant (GVariant *variant, return TRUE; } +gboolean +ostree_validate_structureof_commit (GVariant *commit, + GError **error) +{ + gboolean ret = FALSE; + const char *parent; + const char *contents; + const char *metadata; + + if (!validate_variant (commit, OSTREE_COMMIT_GVARIANT_FORMAT, error)) + goto out; + + g_variant_get_child (commit, 2, "&s", &parent); + + if (*parent) + { + if (!ostree_validate_structureof_checksum_string (parent, error)) + goto out; + } + + g_variant_get_child (commit, 6, "&s", &contents); + if (!ostree_validate_structureof_checksum_string (contents, error)) + goto out; + + g_variant_get_child (commit, 7, "&s", &metadata); + if (!ostree_validate_structureof_checksum_string (metadata, error)) + goto out; + + ret = TRUE; + out: + return ret; +} + +gboolean +ostree_validate_structureof_dirtree (GVariant *dirtree, + GError **error) +{ + gboolean ret = FALSE; + GVariantIter *contents_iter = NULL; + const char *filename; + const char *meta_checksum; + const char *content_checksum; + + if (!validate_variant (dirtree, OSTREE_TREE_GVARIANT_FORMAT, error)) + goto out; + + g_variant_get_child (dirtree, 2, "a(ss)", &contents_iter); + + while (g_variant_iter_loop (contents_iter, "(&s&s)", + &filename, &content_checksum)) + { + if (!ot_util_filename_validate (filename, error)) + goto out; + if (!ostree_validate_structureof_checksum_string (content_checksum, error)) + goto out; + } + + g_variant_iter_free (contents_iter); + g_variant_get_child (dirtree, 3, "a(sss)", &contents_iter); + + while (g_variant_iter_loop (contents_iter, "(&s&s&s)", + &filename, &content_checksum, &meta_checksum)) + { + if (!ot_util_filename_validate (filename, error)) + goto out; + if (!ostree_validate_structureof_checksum_string (content_checksum, error)) + goto out; + if (!ostree_validate_structureof_checksum_string (meta_checksum, error)) + goto out; + } + + ret = TRUE; + out: + if (contents_iter) + g_variant_iter_free (contents_iter); + return ret; +} + +static gboolean +validate_stat_mode_perms (guint32 mode, + GError **error) +{ + gboolean ret = FALSE; + guint32 otherbits = (~S_IFMT & ~S_IRWXU & ~S_IRWXG & ~S_IRWXO); + + if (mode & otherbits) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Invalid mode %u; invalid bits in mode", mode); + goto out; + } + + ret = TRUE; + out: + return ret; +} + +gboolean +ostree_validate_structureof_file_mode (guint32 mode, + GError **error) +{ + gboolean ret = FALSE; + + if (!(S_ISREG (mode) + || S_ISLNK (mode) + || S_ISCHR (mode) + || S_ISBLK (mode) + || S_ISFIFO (mode))) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Invalid file metadata mode %u; not a valid file type", mode); + goto out; + } + + if (!validate_stat_mode_perms (mode, error)) + goto out; + + ret = TRUE; + out: + return ret; +} + +gboolean +ostree_validate_structureof_dirmeta (GVariant *dirmeta, + GError **error) +{ + gboolean ret = FALSE; + guint32 mode; + + if (!validate_variant (dirmeta, OSTREE_DIRMETA_GVARIANT_FORMAT, error)) + goto out; + + g_variant_get_child (dirmeta, 3, "u", &mode); + mode = GUINT32_FROM_BE (mode); + + if (!S_ISDIR (mode)) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Invalid directory metadata mode %u; not a directory", mode); + goto out; + } + + if (!validate_stat_mode_perms (mode, error)) + goto out; + + ret = TRUE; + out: + return ret; +} + gboolean ostree_validate_structureof_pack_index (GVariant *index, GError **error) diff --git a/src/libostree/ostree-core.h b/src/libostree/ostree-core.h index e733f512..1df8a6ae 100644 --- a/src/libostree/ostree-core.h +++ b/src/libostree/ostree-core.h @@ -60,7 +60,7 @@ typedef enum { * u - mode * a(ayay) - xattrs */ -#define OSTREE_DIRMETA_GVARIANT_FORMAT "(uuuua(ayay))" +#define OSTREE_DIRMETA_GVARIANT_FORMAT G_VARIANT_TYPE ("(uuuua(ayay))") #define OSTREE_TREE_VERSION 0 /* @@ -70,7 +70,7 @@ typedef enum { * a(ss) - array of (filename, checksum) for files * a(sss) - array of (dirname, tree_checksum, meta_checksum) for directories */ -#define OSTREE_TREE_GVARIANT_FORMAT "(ua{sv}a(ss)a(sss)" +#define OSTREE_TREE_GVARIANT_FORMAT G_VARIANT_TYPE ("(ua{sv}a(ss)a(sss))") #define OSTREE_COMMIT_VERSION 0 /* @@ -84,7 +84,7 @@ typedef enum { * s - Root tree contents * s - Root tree metadata */ -#define OSTREE_COMMIT_GVARIANT_FORMAT "(ua{sv}ssstss)" +#define OSTREE_COMMIT_GVARIANT_FORMAT G_VARIANT_TYPE ("(ua{sv}ssstss)") /* Archive file objects: * u - Version @@ -300,6 +300,21 @@ gboolean ostree_validate_structureof_objtype (guint32 objtype, gboolean ostree_validate_structureof_checksum (GVariant *checksum, GError **error); +gboolean ostree_validate_structureof_checksum_string (const char *checksum, + GError **error); + +gboolean ostree_validate_structureof_file_mode (guint32 mode, + GError **error); + +gboolean ostree_validate_structureof_commit (GVariant *index, + GError **error); + +gboolean ostree_validate_structureof_dirtree (GVariant *index, + GError **error); + +gboolean ostree_validate_structureof_dirmeta (GVariant *index, + GError **error); + gboolean ostree_validate_structureof_pack_index (GVariant *index, GError **error); diff --git a/src/ostree/ot-builtin-fsck.c b/src/ostree/ot-builtin-fsck.c index 5b9b9646..ee45dc38 100644 --- a/src/ostree/ot-builtin-fsck.c +++ b/src/ostree/ot-builtin-fsck.c @@ -200,6 +200,24 @@ fsck_reachable_objects_from_commits (OtFsckData *data, if (!ostree_repo_load_variant (data->repo, objtype, checksum, &metadata, error)) goto out; + + if (objtype == OSTREE_OBJECT_TYPE_COMMIT) + { + if (!ostree_validate_structureof_commit (metadata, error)) + goto out; + } + else if (objtype == OSTREE_OBJECT_TYPE_DIR_TREE) + { + if (!ostree_validate_structureof_dirtree (metadata, error)) + goto out; + } + else if (objtype == OSTREE_OBJECT_TYPE_DIR_META) + { + if (!ostree_validate_structureof_dirmeta (metadata, error)) + goto out; + } + else + g_assert_not_reached (); ot_clear_gvariant (&metadata_wrapped); metadata_wrapped = ostree_wrap_metadata_variant (objtype, metadata); @@ -216,10 +234,15 @@ fsck_reachable_objects_from_commits (OtFsckData *data, else if (objtype == OSTREE_OBJECT_TYPE_RAW_FILE || objtype == OSTREE_OBJECT_TYPE_ARCHIVED_FILE_META) { + guint32 mode; if (!ostree_repo_load_file (data->repo, checksum, &input, &file_info, &xattrs, cancellable, error)) goto out; checksum_objtype = OSTREE_OBJECT_TYPE_RAW_FILE; /* Override */ + + mode = g_file_info_get_attribute_uint32 (file_info, "unix::mode"); + if (!ostree_validate_structureof_file_mode (mode, error)) + goto out; } else {