[Libguestfs] [PATCH] builder: allow Sigchecker to import keys from file

Pino Toscano ptoscano at redhat.com
Fri Feb 21 14:23:44 UTC 2014


Extend Sigchecker so it allows both fingerprints (to be imported from
user's keyring, as before) and keys stored in files. To simplify this
process (and have the fingerprint always around), the key is imported
on Sigchecker.create time, instead of lazily at the first verification.
---
 builder/builder.ml     |   3 +-
 builder/sigchecker.ml  | 120 ++++++++++++++++++++++++++++---------------------
 builder/sigchecker.mli |   6 ++-
 3 files changed, 76 insertions(+), 53 deletions(-)

diff --git a/builder/builder.ml b/builder/builder.ml
index 7fa3cc7..80ccef7 100644
--- a/builder/builder.ml
+++ b/builder/builder.ml
@@ -141,7 +141,8 @@ let main () =
       List.map (
         fun (source, fingerprint) ->
           let sigchecker =
-            Sigchecker.create ~debug ~gpg ~fingerprint ~check_signature in
+            Sigchecker.create ~debug ~gpg ~check_signature
+              ~gpgkey:(Sigchecker.Fingerprint fingerprint) in
           Index_parser.get_index ~prog ~debug ~downloader ~sigchecker source
       ) sources
     ) in
diff --git a/builder/sigchecker.ml b/builder/sigchecker.ml
index 10e342e..7459e4b 100644
--- a/builder/sigchecker.ml
+++ b/builder/sigchecker.ml
@@ -96,41 +96,97 @@ ZvXkQ3FVJwZoLmHw47vvlVpLD/4gi1SuHWieRvZ+UdDq00E348pm
 -----END PGP PUBLIC KEY BLOCK-----
 "
 
+type gpgkey_type =
+  | Fingerprint of string
+  | KeyFile of string
+
 type t = {
   debug : bool;
   gpg : string;
   fingerprint : string;
   check_signature : bool;
   gpghome : string;
-  mutable key_imported : bool;
 }
 
-let create ~debug ~gpg ~fingerprint ~check_signature =
-  (* Create a temporary directory for gnupg. *)
-  let tmpdir = Mkdtemp.mkdtemp (Filename.temp_dir_name // "vb.gpghome.XXXXXX") in
-  rmdir_on_exit tmpdir;
-  (* Run gpg once, so it can setup its own home directory, failing
-   * if it cannot.
-   *)
-  let cmd = sprintf "%s --homedir %s --list-keys%s"
-    gpg tmpdir (if debug then "" else " >/dev/null 2>&1") in
+(* Import the specified key file. *)
+let import_keyfile ~gpg ~gpghome ~debug keyfile =
+  let status_file = Filename.temp_file "vbstat" ".txt" in
+  unlink_on_exit status_file;
+  let cmd = sprintf "%s --homedir %s --status-file %s --import %s%s"
+    gpg gpghome (quote status_file) (quote keyfile)
+    (if debug then "" else " >/dev/null 2>&1") in
   if debug then eprintf "%s\n%!" cmd;
   let r = Sys.command cmd in
   if r <> 0 then (
-    eprintf (f_"virt-builder: error: GPG failure: could not run GPG the first time\nUse the '-v' option and look for earlier error messages.\n");
+    eprintf (f_"virt-builder: error: could not import public key\nUse the '-v' option and look for earlier error messages.\n");
     exit 1
   );
+  status_file
+
+let rec create ~debug ~gpg ~gpgkey ~check_signature =
+  (* Create a temporary directory for gnupg. *)
+  let tmpdir = Mkdtemp.mkdtemp (Filename.temp_dir_name // "vb.gpghome.XXXXXX") in
+  rmdir_on_exit tmpdir;
+  let fingerprint =
+    if check_signature then (
+      (* Run gpg so it can setup its own home directory, failing if it
+       * cannot.
+       *)
+      let cmd = sprintf "%s --homedir %s --list-keys%s"
+        gpg tmpdir (if debug then "" else " >/dev/null 2>&1") in
+      if debug then eprintf "%s\n%!" cmd;
+      let r = Sys.command cmd in
+      if r <> 0 then (
+        eprintf (f_"virt-builder: error: GPG failure: could not run GPG the first time\nUse the '-v' option and look for earlier error messages.\n");
+        exit 1
+      );
+      match gpgkey with
+      | KeyFile kf ->
+        let status_file = import_keyfile gpg tmpdir debug kf in
+        let status = read_whole_file status_file in
+        let status = string_nsplit "\n" status in
+        let fingerprint = ref "" in
+        List.iter (
+          fun line ->
+            let line = string_nsplit " " line in
+            match line with
+            | "[GNUPG:]" :: "IMPORT_OK" :: _ :: fp :: _ -> fingerprint := fp
+            | _ -> ()
+        ) status;
+        !fingerprint
+      | Fingerprint fp when equal_fingerprints default_fingerprint fp ->
+        let filename, chan = Filename.open_temp_file "vbpubkey" ".asc" in
+        unlink_on_exit filename;
+        output_string chan default_pubkey;
+        close_out chan;
+        ignore (import_keyfile gpg tmpdir debug filename);
+        fp
+      | Fingerprint fp ->
+        let filename = Filename.temp_file "vbpubkey" ".asc" in
+        unlink_on_exit filename;
+        let cmd = sprintf "%s --yes --armor --output %s --export %s%s"
+          gpg (quote filename) (quote fp)
+          (if debug then "" else " >/dev/null 2>&1") in
+        if debug then eprintf "%s\n%!" cmd;
+        let r = Sys.command cmd in
+        if r <> 0 then (
+          eprintf (f_"virt-builder: error: could not export public key\nUse the '-v' option and look for earlier error messages.\n");
+          exit 1
+        );
+        ignore (import_keyfile gpg tmpdir debug filename);
+        fp
+    ) else
+      "" in
   {
     debug = debug;
     gpg = gpg;
     fingerprint = fingerprint;
     check_signature = check_signature;
     gpghome = tmpdir;
-    key_imported = false;
   }
 
 (* Compare two strings of hex digits ignoring whitespace and case. *)
-let rec equal_fingerprints fp1 fp2 =
+and equal_fingerprints fp1 fp2 =
   let len1 = String.length fp1 and len2 = String.length fp2 in
   let rec loop i j =
     if i = len1 && j = len2 then true (* match! *)
@@ -171,8 +227,6 @@ and verify_detached t filename sigfile =
   )
 
 and do_verify t args =
-  import_key t;
-
   let status_file = Filename.temp_file "vbstat" ".txt" in
   unlink_on_exit status_file;
   let cmd =
@@ -206,42 +260,6 @@ and do_verify t args =
     exit 1
   )
 
-(* Import the requested public key. *)
-and import_key t =
-  if not t.key_imported then (
-    let keyfile = ref "" in
-    if equal_fingerprints default_fingerprint t.fingerprint then (
-      let filename, chan = Filename.open_temp_file "vbpubkey" ".asc" in
-      unlink_on_exit filename;
-      output_string chan default_pubkey;
-      close_out chan;
-      keyfile := filename
-    ) else (
-      let filename = Filename.temp_file "vbpubkey" ".asc" in
-      unlink_on_exit filename;
-      let cmd = sprintf "%s --yes --armor --output %s --export %s%s"
-        t.gpg (quote filename) (quote t.fingerprint)
-        (if t.debug then "" else " >/dev/null 2>&1") in
-      if t.debug then eprintf "%s\n%!" cmd;
-      let r = Sys.command cmd in
-      if r <> 0 then (
-        eprintf (f_"virt-builder: error: could not export public key\nUse the '-v' option and look for earlier error messages.\n");
-        exit 1
-      );
-      keyfile := filename
-    );
-
-    let cmd = sprintf "%s --homedir %s --import %s%s"
-      t.gpg t.gpghome (quote !keyfile)
-      (if t.debug then "" else " >/dev/null 2>&1") in
-    let r = Sys.command cmd in
-    if r <> 0 then (
-      eprintf (f_"virt-builder: error: could not import public key\nUse the '-v' option and look for earlier error messages.\n");
-      exit 1
-    );
-    t.key_imported <- true
-  )
-
 type csum_t = SHA512 of string
 
 let verify_checksum t (SHA512 csum) filename =
diff --git a/builder/sigchecker.mli b/builder/sigchecker.mli
index cdd800e..f4e817e 100644
--- a/builder/sigchecker.mli
+++ b/builder/sigchecker.mli
@@ -20,7 +20,11 @@ val default_fingerprint : string
 
 type t
 
-val create : debug:bool -> gpg:string -> fingerprint:string -> check_signature:bool -> t
+type gpgkey_type =
+  | Fingerprint of string
+  | KeyFile of string
+
+val create : debug:bool -> gpg:string -> gpgkey:gpgkey_type -> check_signature:bool -> t
 
 val verify : t -> string -> unit
 (** Verify the file is signed (if check_signature is true). *)
-- 
1.8.3.1




More information about the Libguestfs mailing list