repo: Prevent publishing summary without matching signature
authorDan Nicholson <nicholson@endlessm.com>
Tue, 21 Jan 2020 19:43:35 +0000 (12:43 -0700)
committerDan Nicholson <dbn@endlessos.org>
Tue, 7 Feb 2023 21:50:47 +0000 (14:50 -0700)
Use a temporary directory for the summary and signature file in
`ostree_repo_regenerate_metadata` so that the summary file isn't
published if signing fails. This prevents publishing a summary without a
signature file or leaving a mismatched signature file in place.

src/libostree/ostree-repo.c

index 81d3e6120b9294ef2029f104533cad7a754d6cc9..4dbec47b601bb1f369ac75b4c0ffb960fc5be9b8 100644 (file)
@@ -43,6 +43,7 @@
 #include "ostree-repo-static-delta-private.h"
 #include "ot-fs-utils.h"
 #include "ostree-autocleanups.h"
+#include "ostree-sign-private.h"
 
 #include <locale.h>
 #include <glib/gstdio.h>
@@ -6543,8 +6544,17 @@ regenerate_metadata (OstreeRepo    *self,
   if (!ostree_repo_static_delta_reindex (self, 0, NULL, cancellable, error))
     return FALSE;
 
+  /* Create the summary and signature in a temporary directory so that
+   * the summary isn't published without a matching signature.
+   */
+  g_auto(GLnxTmpDir) summary_tmpdir = { 0, };
+  if (!glnx_mkdtempat (self->tmp_dir_fd, "summary-XXXXXX", 0777,
+                       &summary_tmpdir, error))
+    return FALSE;
+  g_debug ("Using summary tmpdir %s", summary_tmpdir.path);
+
   if (!_ostree_repo_file_replace_contents (self,
-                                           self->repo_dir_fd,
+                                           summary_tmpdir.fd,
                                            "summary",
                                            g_variant_get_data (summary),
                                            g_variant_get_size (summary),
@@ -6552,18 +6562,45 @@ regenerate_metadata (OstreeRepo    *self,
                                            error))
     return FALSE;
 
-  if (!ot_ensure_unlinked_at (self->repo_dir_fd, "summary.sig", error))
-    return FALSE;
-
   if (gpg_key_ids != NULL &&
-      !ostree_repo_add_gpg_signature_summary (self, (const char **) gpg_key_ids, gpg_homedir,
-                                              cancellable, error))
+      !_ostree_repo_add_gpg_signature_summary_at (self, summary_tmpdir.fd,
+                                                  (const char **) gpg_key_ids, gpg_homedir,
+                                                  cancellable, error))
     return FALSE;
 
   if (sign_keys != NULL &&
-      !ostree_sign_summary (sign, self, sign_keys, cancellable, error))
+      !_ostree_sign_summary_at (sign, self, summary_tmpdir.fd, sign_keys,
+                                cancellable, error))
     return FALSE;
 
+  /* Rename them into place */
+  if (!glnx_renameat (summary_tmpdir.fd, "summary",
+                      self->repo_dir_fd, "summary",
+                      error))
+    return glnx_prefix_error (error, "Unable to rename summary file: ");
+
+  if (gpg_key_ids != NULL || sign_keys != NULL)
+    {
+      if (!glnx_renameat (summary_tmpdir.fd, "summary.sig",
+                          self->repo_dir_fd, "summary.sig",
+                          error))
+        {
+          /* Delete an existing signature since it no longer corresponds
+           * to the published summary.
+           */
+          g_debug ("Deleting existing unmatched summary.sig file");
+          (void) ot_ensure_unlinked_at (self->repo_dir_fd, "summary.sig", NULL);
+
+          return glnx_prefix_error (error, "Unable to rename summary signature file: ");
+        }
+    }
+  else
+    {
+      g_debug ("Deleting existing unmatched summary.sig file");
+      if (!ot_ensure_unlinked_at (self->repo_dir_fd, "summary.sig", error))
+        return glnx_prefix_error (error, "Unable to delete summary signature file: ");
+    }
+
   return TRUE;
 }