]> git.decadent.org.uk Git - dak.git/commitdiff
Add (not enabled by default) support for auto-fetching keys from a keyserver. Origin...
authorJames Troup <james@nocrew.org>
Mon, 22 May 2006 00:07:27 +0000 (19:07 -0500)
committerJames Troup <james@nocrew.org>
Mon, 22 May 2006 00:07:27 +0000 (19:07 -0500)
ChangeLog
daklib/utils.py
docs/README.config

index 556e156b856c6079434d255dd7754c85aaf3f49c..23566093a0a5c8bea288929d4c75419519507cdb 100644 (file)
--- a/ChangeLog
+++ b/ChangeLog
@@ -1,3 +1,17 @@
+2006-05-21  James Troup  <james@nocrew.org>
+
+       * daklib/utils.py (process_gpgv_output): new function, split out
+       of check_signature().
+       (check_signature): adapt accordingly.
+       (retrieve_key): new function that will try to retrieve the key
+       that signed a given file from a keyserver.
+       (check_signature): add 'autofetch' argument that if not set
+       defaults to the value of Dinstall::KeyAutoFetch (if that exists).
+       If 'autofetch' is true, invoke retrieve_key().  
+
+       * docs/README.config: document Dinstall::KeyAutoFetch and
+       Dinstall:KeyServer.
+
 2006-05-20  James Troup  <james@nocrew.org>
 
        * dak/find_null_maintainers.py (main):
index c4c13669d92ca3a2aca761c2124e800ecf55e95a..47978228663aa5ee42033a9b95556ddf671305a0 100644 (file)
@@ -865,10 +865,80 @@ def gpgv_get_status_output(cmd, status_read, status_write):
 
     return output, status, exit_status
 
-############################################################
+################################################################################
 
+def process_gpgv_output(status):
+    # Process the status-fd output
+    keywords = {}
+    internal_error = ""
+    for line in status.split('\n'):
+        line = line.strip()
+        if line == "":
+            continue
+        split = line.split()
+        if len(split) < 2:
+            internal_error += "gpgv status line is malformed (< 2 atoms) ['%s'].\n" % (line)
+            continue
+        (gnupg, keyword) = split[:2]
+        if gnupg != "[GNUPG:]":
+            internal_error += "gpgv status line is malformed (incorrect prefix '%s').\n" % (gnupg)
+            continue
+        args = split[2:]
+        if keywords.has_key(keyword) and (keyword != "NODATA" and keyword != "SIGEXPIRED"):
+            internal_error += "found duplicate status token ('%s').\n" % (keyword)
+            continue
+        else:
+            keywords[keyword] = args
+
+    return (keywords, internal_error)
+
+################################################################################
+
+def retrieve_key (filename, keyserver=None, keyring=None):
+    """Retrieve the key that signed 'filename' from 'keyserver' and
+add it to 'keyring'.  Returns nothing on success, or an error message
+on error."""
+
+    # Defaults for keyserver and keyring
+    if not keyserver:
+        keyserver = Cnf["Dinstall::KeyServer"]
+    if not keyring:
+        keyring = Cnf["Dinstall::GPGKeyring"]
+
+    # Ensure the filename contains no shell meta-characters or other badness
+    if not re_taint_free.match(filename):
+        return "%s: tainted filename" % (filename)
+
+    # Invoke gpgv on the file
+    status_read, status_write = os.pipe(); 
+    cmd = "gpgv --status-fd %s --keyring /dev/null %s" % (status_write, filename)
+    (_, status, _) = gpgv_get_status_output(cmd, status_read, status_write)
 
-def check_signature (sig_filename, reject, data_filename="", keyrings=None):
+    # Process the status-fd output
+    (keywords, internal_error) = process_gpgv_output(status)
+    if internal_error:
+        return internal_error
+
+    if not keywords.has_key("NO_PUBKEY"):
+        return "didn't find expected NO_PUBKEY in gpgv status-fd output"
+
+    fingerprint = keywords["NO_PUBKEY"][0]
+    # XXX - gpg sucks.  You can't use --secret-keyring=/dev/null as
+    # it'll try to create a lockfile in /dev.  A better solution might
+    # be a tempfile or something.
+    cmd = "gpg --no-default-keyring --secret-keyring=%s --no-options" \
+          % (Cnf["Dinstall::SigningKeyring"])
+    cmd += " --keyring %s --keyserver %s --recv-key %s" \
+           % (keyring, keyserver, fingerprint)
+    (result, output) = commands.getstatusoutput(cmd)
+    if (result != 0):
+        return "'%s' failed with exit code %s" % (cmd, result)
+
+    return ""
+
+################################################################################
+
+def check_signature (sig_filename, reject, data_filename="", keyrings=None, autofetch=None):
     """Check the signature of a file and return the fingerprint if the
 signature is valid or 'None' if it's not.  The first argument is the
 filename whose signature should be checked.  The second argument is a
@@ -878,8 +948,9 @@ the second is an optional prefix string.  It's possible for reject()
 to be called more than once during an invocation of check_signature().
 The third argument is optional and is the name of the files the
 detached signature applies to.  The fourth argument is optional and is
-a *list* of keyrings to use.
-"""
+a *list* of keyrings to use.  'autofetch' can either be None, True or
+False.  If None, the default behaviour specified in the config will be
+used."""
 
     # Ensure the filename contains no shell meta-characters or other badness
     if not re_taint_free.match(sig_filename):
@@ -893,6 +964,15 @@ a *list* of keyrings to use.
     if not keyrings:
         keyrings = (Cnf["Dinstall::PGPKeyring"], Cnf["Dinstall::GPGKeyring"])
 
+    # Autofetch the signing key if that's enabled
+    if autofetch == None:
+        autofetch = Cnf.get("Dinstall::KeyAutoFetch")
+    if autofetch:
+        error_msg = retrieve_key(sig_filename)
+        if error_msg:
+            reject(error_msg)
+            return None
+
     # Build the command line
     status_read, status_write = os.pipe(); 
     cmd = "gpgv --status-fd %s" % (status_write)
@@ -903,26 +983,7 @@ a *list* of keyrings to use.
     (output, status, exit_status) = gpgv_get_status_output(cmd, status_read, status_write)
 
     # Process the status-fd output
-    keywords = {}
-    bad = internal_error = ""
-    for line in status.split('\n'):
-        line = line.strip()
-        if line == "":
-            continue
-        split = line.split()
-        if len(split) < 2:
-            internal_error += "gpgv status line is malformed (< 2 atoms) ['%s'].\n" % (line)
-            continue
-        (gnupg, keyword) = split[:2]
-        if gnupg != "[GNUPG:]":
-            internal_error += "gpgv status line is malformed (incorrect prefix '%s').\n" % (gnupg)
-            continue
-        args = split[2:]
-        if keywords.has_key(keyword) and (keyword != "NODATA" and keyword != "SIGEXPIRED"):
-            internal_error += "found duplicate status token ('%s').\n" % (keyword)
-            continue
-        else:
-            keywords[keyword] = args
+    (keywords, internal_error) = process_gpgv_output(status)
 
     # If we failed to parse the status-fd output, let's just whine and bail now
     if internal_error:
@@ -931,6 +992,7 @@ a *list* of keyrings to use.
         reject("Please report the above errors to the Archive maintainers by replying to this mail.", "")
         return None
 
+    bad = ""
     # Now check for obviously bad things in the processed output
     if keywords.has_key("SIGEXPIRED"):
         reject("The key used to sign %s has expired." % (sig_filename))
index 645c22ad57be63a926ae4a98e597f72d1e9cb0e5..52588bdc80ad2651416e79f1bca8e4df625a0b7a 100644 (file)
@@ -291,6 +291,15 @@ SkipTime (required): an integer value which is the number of seconds that a
 file must be older than (via it's last modified timestamp) before dak process-unchecked
 will REJECT rather than SKIP the package.
 
+KeyAutoFetch (optional): boolean (default: false), which if set (and
+not overriden by explicit argument to check_signature()) will enable
+auto key retrieval.  Requires KeyServer and SigningKeyIds variables be
+set.  NB: you should only enable this variable on production systems
+if you have strict control of your upload queue.
+
+KeyServer (optional): keyserver used for key auto-retrieval
+(c.f. KeyAutoFetch).
+
 ================================================================================
 
 Archive