diff --git a/src/libostree/libostree.sym b/src/libostree/libostree.sym index 47015e14..ca3c6fd0 100644 --- a/src/libostree/libostree.sym +++ b/src/libostree/libostree.sym @@ -331,4 +331,5 @@ global: LIBOSTREE_2016.5 { global: ostree_repo_import_object_from_with_trust; + ostree_sepolicy_get_csum; } LIBOSTREE_2016.4; diff --git a/src/libostree/ostree-sepolicy.c b/src/libostree/ostree-sepolicy.c index b8e3572d..3b1a391b 100644 --- a/src/libostree/ostree-sepolicy.c +++ b/src/libostree/ostree-sepolicy.c @@ -50,6 +50,7 @@ struct OstreeSePolicy { GFile *selinux_policy_root; struct selabel_handle *selinux_hnd; char *selinux_policy_name; + char *selinux_policy_csum; #endif }; @@ -77,6 +78,7 @@ ostree_sepolicy_finalize (GObject *object) #ifdef HAVE_SELINUX g_clear_object (&self->selinux_policy_root); g_clear_pointer (&self->selinux_policy_name, g_free); + g_clear_pointer (&self->selinux_policy_csum, g_free); if (self->selinux_hnd) { selabel_close (self->selinux_hnd); @@ -155,6 +157,93 @@ ostree_sepolicy_class_init (OstreeSePolicyClass *klass) G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY)); } +#ifdef HAVE_SELINUX + +/* Find the latest policy file in our root and return its checksum. */ +static gboolean +get_policy_checksum (char **out_csum, + GCancellable *cancellable, + GError **error) +{ + gboolean ret = FALSE; + + const char *binary_policy_path = selinux_binary_policy_path (); + const char *binfile_prefix = glnx_basename (binary_policy_path); + g_autofree char *bindir_path = g_path_get_dirname (binary_policy_path); + + glnx_fd_close int bindir_dfd = -1; + + g_autofree char *best_policy = NULL; + int best_version = 0; + + g_auto(GLnxDirFdIterator) dfd_iter = { 0,}; + + if (!glnx_opendirat (AT_FDCWD, bindir_path, TRUE, &bindir_dfd, error)) + goto out; + + if (!glnx_dirfd_iterator_init_at (bindir_dfd, ".", FALSE, &dfd_iter, error)) + goto out; + + while (TRUE) + { + struct dirent *dent = NULL; + + if (!glnx_dirfd_iterator_next_dent_ensure_dtype (&dfd_iter, &dent, + cancellable, error)) + goto out; + + if (dent == NULL) + break; + + if (dent->d_type == DT_REG) + { + /* We could probably save a few hundred nanoseconds if we accept that + * the prefix will always be "policy" and hardcode that in a static + * compile-once GRegex... But picture how exciting it'd be if it *did* + * somehow change; there would be cheers & slow-mo high-fives at the + * sight of our code not breaking. Is that hope not worth a fraction + * of a millisecond? I believe it is... or maybe I'm just lazy. */ + g_autofree char *regex = g_strdup_printf ("^\\Q%s\\E\\.[0-9]+$", + binfile_prefix); + + /* we could use match groups to extract the version, but mehhh, we + * already have the prefix on hand */ + if (g_regex_match_simple (regex, dent->d_name, 0, 0)) + { + int version = /* do +1 for the period */ + (int)g_ascii_strtoll (dent->d_name + strlen (binfile_prefix)+1, + NULL, 10); + g_assert (version > 0); + + if (version > best_version) + { + best_version = version; + g_free (best_policy); + best_policy = g_strdup (dent->d_name); + } + } + } + } + + if (!best_policy) + { + g_set_error (error, G_IO_ERROR, G_IO_ERROR_FAILED, + "Could not find binary policy file"); + goto out; + } + + *out_csum = ot_checksum_file_at (bindir_dfd, best_policy, G_CHECKSUM_SHA256, + cancellable, error); + if (*out_csum == NULL) + goto out; + + ret = TRUE; +out: + return ret; +} + +#endif + static gboolean initable_init (GInitable *initable, GCancellable *cancellable, @@ -257,6 +346,12 @@ initable_init (GInitable *initable, freecon (con); } + if (!get_policy_checksum (&self->selinux_policy_csum, cancellable, error)) + { + g_prefix_error (error, "While calculating SELinux checksum: "); + goto out; + } + self->selinux_policy_name = g_strdup (policytype); self->selinux_policy_root = g_object_ref (etc_selinux_dir); } @@ -306,6 +401,12 @@ ostree_sepolicy_get_path (OstreeSePolicy *self) return self->path; } +/** + * ostree_sepolicy_get_name: + * @self: + * + * Returns: (transfer none): Type of current policy + */ const char * ostree_sepolicy_get_name (OstreeSePolicy *self) { @@ -316,6 +417,22 @@ ostree_sepolicy_get_name (OstreeSePolicy *self) #endif } +/** + * ostree_sepolicy_get_csum: + * @self: + * + * Returns: (transfer none): Checksum of current policy + */ +const char * +ostree_sepolicy_get_csum (OstreeSePolicy *self) +{ +#ifdef HAVE_SELINUX + return self->selinux_policy_csum; +#else + return NULL; +#endif +} + /** * ostree_sepolicy_get_label: * @self: Self diff --git a/src/libostree/ostree-sepolicy.h b/src/libostree/ostree-sepolicy.h index 83e3b379..d204953e 100644 --- a/src/libostree/ostree-sepolicy.h +++ b/src/libostree/ostree-sepolicy.h @@ -44,6 +44,9 @@ GFile * ostree_sepolicy_get_path (OstreeSePolicy *self); _OSTREE_PUBLIC const char *ostree_sepolicy_get_name (OstreeSePolicy *self); +_OSTREE_PUBLIC +const char *ostree_sepolicy_get_csum (OstreeSePolicy *self); + _OSTREE_PUBLIC gboolean ostree_sepolicy_get_label (OstreeSePolicy *self, const char *relpath, diff --git a/src/libotutil/ot-checksum-utils.c b/src/libotutil/ot-checksum-utils.c index b2aaf329..8d30bdc3 100644 --- a/src/libotutil/ot-checksum-utils.c +++ b/src/libotutil/ot-checksum-utils.c @@ -140,17 +140,17 @@ ot_gio_checksum_stream (GInputStream *in, } char * -ot_checksum_file (GFile *file, - GChecksumType checksum_type, - GCancellable *cancellable, - GError **error) +ot_checksum_file_at (int dfd, + const char *path, + GChecksumType checksum_type, + GCancellable *cancellable, + GError **error) { GChecksum *checksum = NULL; char *ret = NULL; g_autoptr(GInputStream) in = NULL; - in = (GInputStream*)g_file_read (file, cancellable, error); - if (!in) + if (!ot_openat_read_stream (dfd, path, TRUE, &in, cancellable, error)) goto out; checksum = g_checksum_new (checksum_type); diff --git a/src/libotutil/ot-checksum-utils.h b/src/libotutil/ot-checksum-utils.h index eb8bbc04..8b3a394e 100644 --- a/src/libotutil/ot-checksum-utils.h +++ b/src/libotutil/ot-checksum-utils.h @@ -53,10 +53,11 @@ gboolean ot_gio_checksum_stream (GInputStream *in, GCancellable *cancellable, GError **error); -char * ot_checksum_file (GFile *file, - GChecksumType checksum_type, - GCancellable *cancellable, - GError **error); +char * ot_checksum_file_at (int dfd, + const char *path, + GChecksumType checksum_type, + GCancellable *cancellable, + GError **error); void ot_gio_checksum_stream_async (GInputStream *in, int io_priority,