]> git.decadent.org.uk Git - dak.git/commitdiff
Merge key-auto-fetch branch.
authorJames Troup <james@nocrew.org>
Mon, 22 May 2006 00:09:21 +0000 (19:09 -0500)
committerJames Troup <james@nocrew.org>
Mon, 22 May 2006 00:09:21 +0000 (19:09 -0500)
1  2 
ChangeLog
daklib/utils.py

diff --combined ChangeLog
index 0fc220ba8fab5313b7d4f108a57445f7cc9427db,23566093a0a5c8bea288929d4c75419519507cdb..905a4162a3580b49b081d9f1ec8a6b068377f1b1
+++ b/ChangeLog
@@@ -1,22 -1,17 +1,36 @@@
 +2006-05-21  James Troup  <james@nocrew.org>
 +
 +      * dak/check_archive.py (check_indices_files_exist): use list
 +      comprehension instead of map().  No longer need to import
 +      deprecated string module as a side-effect.
 +      * dak/check_overrides.py (process): likewise.
 +      (main): likewise.
 +      * dak/cruft_report.py (do_obsolete_source): likewise.
 +      (main): likewise.
 +      * dak/ls.py (main): likewise.
 +      * dak/make_suite_file_list.py (write_filelists): likewise.
 +      * dak/process_accepted.py (stable_install): likewise.
 +      * dak/rm.py (main): likewise.
 +      * dak/stats.py (number_of_packages): likewise.
 +      * daklib/logging.py (Logger.log): likewise.
 +      * daklib/queue.py (Upload.source_exists): likewise.
 +      (Upload.cross_suite_version_check): likewise.
 +      * daklib/utils.py (parse_args): likewise.
 +
+ 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):
diff --combined daklib/utils.py
index a32b0c468d023f74e48d841e92c8f18b897a2178,47978228663aa5ee42033a9b95556ddf671305a0..230391a192a2e046b1ead07b38a722182270a279
@@@ -683,7 -683,7 +683,7 @@@ def parse_args(Options)
              else:
                  suite_ids_list.append(suite_id)
          if suite_ids_list:
 -            con_suites = "AND su.id IN (%s)" % ", ".join(map(str, suite_ids_list))
 +            con_suites = "AND su.id IN (%s)" % ", ".join([ str(i) for i in suite_ids_list ])
          else:
              fubar("No valid suite given.")
      else:
              else:
                  component_ids_list.append(component_id)
          if component_ids_list:
 -            con_components = "AND c.id IN (%s)" % ", ".join(map(str, component_ids_list))
 +            con_components = "AND c.id IN (%s)" % ", ".join([ str(i) for i in component_ids_list ])
          else:
              fubar("No valid component given.")
      else:
                  else:
                      arch_ids_list.append(architecture_id)
          if arch_ids_list:
 -            con_architectures = "AND a.id IN (%s)" % ", ".join(map(str, arch_ids_list))
 +            con_architectures = "AND a.id IN (%s)" % ", ".join([ str(i) for i in arch_ids_list ])
          else:
              if not check_source:
                  fubar("No valid architecture given.")
@@@ -865,10 -865,80 +865,80 @@@ def gpgv_get_status_output(cmd, status_
  
      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 +948,9 @@@ the second is an optional prefix string
  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):
      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)
      (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:
          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))