提交 df1a2290 编写于 作者: J James Troup

2004-08-04 James Troup <james@nocrew.org> * jennifer (check_files): check...

2004-08-04  James Troup  <james@nocrew.org>	* jennifer (check_files): check for unknown component before	checking for NEWness.2004-06-17  James Troup  <james@nocrew.org>	* jennifer (check_dsc): s/dsc_whitespace_rules/signing_rules/.	* tea (check_dscs): likewise.	* utils.py (parse_changes): s/dsc_whitespace_rules/signing_rules/,	change from boolean to a variable with 3 possible values, 0 and 1	as before, -1 means don't require a signature.  Makes	parse_changes() useful for parsing arbitary RFC822-style files,	e.g. 'Release' files.	(check_signature): add support for detached signatures by passing	the files the signature is for as an optional third argument.	s/filename/sig_filename/g.  Add a fourth optional argument to	choose the keyring(s) to use.  Don't os.path.basename() the	sig_filename before checking it for taint.	(re_taint_free): allow '/'.
上级 0f6538cf
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# Checks Debian packages from Incoming # Checks Debian packages from Incoming
# Copyright (C) 2000, 2001, 2002, 2003, 2004 James Troup <james@nocrew.org> # Copyright (C) 2000, 2001, 2002, 2003, 2004 James Troup <james@nocrew.org>
# $Id: jennifer,v 1.51 2004-06-17 15:01:18 troup Exp $ # $Id: jennifer,v 1.52 2004-11-27 13:32:16 troup Exp $
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
...@@ -45,7 +45,7 @@ re_strip_revision = re.compile(r"-([^-]+)$"); ...@@ -45,7 +45,7 @@ re_strip_revision = re.compile(r"-([^-]+)$");
################################################################################ ################################################################################
# Globals # Globals
jennifer_version = "$Revision: 1.51 $"; jennifer_version = "$Revision: 1.52 $";
Cnf = None; Cnf = None;
Options = None; Options = None;
...@@ -533,16 +533,13 @@ def check_files(): ...@@ -533,16 +533,13 @@ def check_files():
if files[file]["component"] == source: if files[file]["component"] == source:
files[file]["original component"] = source; files[file]["original component"] = source;
files[file]["component"] = dest; files[file]["component"] = dest;
# Ensure the component is valid for the target suite # Ensure the component is valid for the target suite
if Cnf.has_key("Suite:%s::Components" % (suite)) and \ if Cnf.has_key("Suite:%s::Components" % (suite)) and \
files[file]["component"] not in Cnf.ValueList("Suite::%s::Components" % (suite)): files[file]["component"] not in Cnf.ValueList("Suite::%s::Components" % (suite)):
reject("unknown component `%s' for suite `%s'." % (files[file]["component"], suite)); reject("unknown component `%s' for suite `%s'." % (files[file]["component"], suite));
continue; continue;
# See if the package is NEW
if not Katie.in_override_p(files[file]["package"], files[file]["component"], suite, files[file].get("dbtype",""), file):
files[file]["new"] = 1;
# Validate the component # Validate the component
component = files[file]["component"]; component = files[file]["component"];
component_id = db_access.get_component_id(component); component_id = db_access.get_component_id(component);
...@@ -550,6 +547,10 @@ def check_files(): ...@@ -550,6 +547,10 @@ def check_files():
reject("file '%s' has unknown component '%s'." % (file, component)); reject("file '%s' has unknown component '%s'." % (file, component));
continue; continue;
# See if the package is NEW
if not Katie.in_override_p(files[file]["package"], files[file]["component"], suite, files[file].get("dbtype",""), file):
files[file]["new"] = 1;
# Validate the priority # Validate the priority
if files[file]["priority"].find('/') != -1: if files[file]["priority"].find('/') != -1:
reject("file '%s' has invalid priority '%s' [contains '/']." % (file, files[file]["priority"])); reject("file '%s' has invalid priority '%s' [contains '/']." % (file, files[file]["priority"]));
...@@ -617,7 +618,7 @@ def check_dsc(): ...@@ -617,7 +618,7 @@ def check_dsc():
# Parse the .dsc file # Parse the .dsc file
try: try:
dsc.update(utils.parse_changes(dsc_filename, dsc_whitespace_rules=1)); dsc.update(utils.parse_changes(dsc_filename, signing_rules=1));
except utils.cant_open_exc: except utils.cant_open_exc:
# if not -n copy_to_holding() will have done this for us... # if not -n copy_to_holding() will have done this for us...
if Options["No-Action"]: if Options["No-Action"]:
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# Various different sanity checks # Various different sanity checks
# Copyright (C) 2000, 2001, 2002, 2003, 2004 James Troup <james@nocrew.org> # Copyright (C) 2000, 2001, 2002, 2003, 2004 James Troup <james@nocrew.org>
# $Id: tea,v 1.29 2004-09-01 07:52:15 rmurray Exp $ # $Id: tea,v 1.30 2004-11-27 13:32:16 troup Exp $
# This program is free software; you can redistribute it and/or modify # This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by # it under the terms of the GNU General Public License as published by
...@@ -126,7 +126,7 @@ def check_dscs(): ...@@ -126,7 +126,7 @@ def check_dscs():
for line in list_file.readlines(): for line in list_file.readlines():
file = line[:-1]; file = line[:-1];
try: try:
utils.parse_changes(file, dsc_whitespace_rules=1); utils.parse_changes(file, signing_rules=1);
except utils.invalid_dsc_format_exc, line: except utils.invalid_dsc_format_exc, line:
utils.warn("syntax error in .dsc file '%s', line %s." % (file, line)); utils.warn("syntax error in .dsc file '%s', line %s." % (file, line));
count += 1; count += 1;
......
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
# Utility functions # Utility functions
# Copyright (C) 2000, 2001, 2002, 2003, 2004 James Troup <james@nocrew.org> # Copyright (C) 2000, 2001, 2002, 2003, 2004 James Troup <james@nocrew.org>
# $Id: utils.py,v 1.69 2004-06-24 00:41:39 troup Exp $ # $Id: utils.py,v 1.70 2004-11-27 13:32:16 troup Exp $
################################################################################ ################################################################################
...@@ -40,7 +40,7 @@ re_issource = re.compile (r"(.+)_(.+?)\.(orig\.tar\.gz|diff\.gz|tar\.gz|dsc)$"); ...@@ -40,7 +40,7 @@ re_issource = re.compile (r"(.+)_(.+?)\.(orig\.tar\.gz|diff\.gz|tar\.gz|dsc)$");
re_single_line_field = re.compile(r"^(\S*)\s*:\s*(.*)"); re_single_line_field = re.compile(r"^(\S*)\s*:\s*(.*)");
re_multi_line_field = re.compile(r"^\s(.*)"); re_multi_line_field = re.compile(r"^\s(.*)");
re_taint_free = re.compile(r"^[-+~\.\w]+$"); re_taint_free = re.compile(r"^[-+~/\.\w]+$");
re_parse_maintainer = re.compile(r"^\s*(\S.*\S)\s*\<([^\>]+)\>"); re_parse_maintainer = re.compile(r"^\s*(\S.*\S)\s*\<([^\>]+)\>");
...@@ -137,24 +137,27 @@ def extract_component_from_section(section): ...@@ -137,24 +137,27 @@ def extract_component_from_section(section):
################################################################################ ################################################################################
# Parses a changes file and returns a dictionary where each field is a def parse_changes(filename, signing_rules=0):
# key. The mandatory first argument is the filename of the .changes """Parses a changes file and returns a dictionary where each field is a
# file. key. The mandatory first argument is the filename of the .changes
file.
# dsc_whitespace_rules is an optional boolean argument which defaults
# to off. If true, it turns on strict format checking to avoid signing_rules is an optional argument:
# allowing in source packages which are unextracable by the
# inappropriately fragile dpkg-source. o If signing_rules == -1, no signature is required.
# o If signing_rules == 0 (the default), a signature is required.
# The rules are: o If signing_rules == 1, it turns on the same strict format checking
# as dpkg-source.
# o The PGP header consists of "-----BEGIN PGP SIGNED MESSAGE-----"
# followed by any PGP header data and must end with a blank line. The rules for (signing_rules == 1)-mode are:
#
# o The data section must end with a blank line and must be followed by o The PGP header consists of "-----BEGIN PGP SIGNED MESSAGE-----"
# "-----BEGIN PGP SIGNATURE-----". followed by any PGP header data and must end with a blank line.
def parse_changes(filename, dsc_whitespace_rules=0): o The data section must end with a blank line and must be followed by
"-----BEGIN PGP SIGNATURE-----".
"""
error = ""; error = "";
changes = {}; changes = {};
...@@ -181,7 +184,7 @@ def parse_changes(filename, dsc_whitespace_rules=0): ...@@ -181,7 +184,7 @@ def parse_changes(filename, dsc_whitespace_rules=0):
index += 1; index += 1;
line = indexed_lines[index]; line = indexed_lines[index];
if line == "": if line == "":
if dsc_whitespace_rules: if signing_rules == 1:
index += 1; index += 1;
if index > num_of_lines: if index > num_of_lines:
raise invalid_dsc_format_exc, index; raise invalid_dsc_format_exc, index;
...@@ -196,13 +199,13 @@ def parse_changes(filename, dsc_whitespace_rules=0): ...@@ -196,13 +199,13 @@ def parse_changes(filename, dsc_whitespace_rules=0):
break; break;
if line.startswith("-----BEGIN PGP SIGNED MESSAGE"): if line.startswith("-----BEGIN PGP SIGNED MESSAGE"):
inside_signature = 1; inside_signature = 1;
if dsc_whitespace_rules: if signing_rules == 1:
while index < num_of_lines and line != "": while index < num_of_lines and line != "":
index += 1; index += 1;
line = indexed_lines[index]; line = indexed_lines[index];
continue; continue;
# If we're not inside the signed data, don't process anything # If we're not inside the signed data, don't process anything
if not inside_signature: if signing_rules >= 0 and not inside_signature:
continue; continue;
slf = re_single_line_field.match(line); slf = re_single_line_field.match(line);
if slf: if slf:
...@@ -224,7 +227,7 @@ def parse_changes(filename, dsc_whitespace_rules=0): ...@@ -224,7 +227,7 @@ def parse_changes(filename, dsc_whitespace_rules=0):
continue; continue;
error += line; error += line;
if dsc_whitespace_rules and inside_signature: if signing_rules == 1 and inside_signature:
raise invalid_dsc_format_exc, index; raise invalid_dsc_format_exc, index;
changes_in.close(); changes_in.close();
...@@ -852,24 +855,38 @@ def gpgv_get_status_output(cmd, status_read, status_write): ...@@ -852,24 +855,38 @@ def gpgv_get_status_output(cmd, status_read, status_write):
############################################################ ############################################################
def check_signature (filename, reject): def check_signature (sig_filename, reject, data_filename="", keyrings=None):
"""Check the signature of a file and return the fingerprint if the """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 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 filename whose signature should be checked. The second argument is a
reject function and is called when an error is found. The reject() reject function and is called when an error is found. The reject()
function must allow for two arguments: the first is the error message, function must allow for two arguments: the first is the error message,
the second is an optional prefix string. It's possible for reject() the second is an optional prefix string. It's possible for reject()
to be called more than once during an invocation of check_signature().""" 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.
"""
# Ensure the filename contains no shell meta-characters or other badness # Ensure the filename contains no shell meta-characters or other badness
if not re_taint_free.match(os.path.basename(filename)): if not re_taint_free.match(sig_filename):
reject("!!WARNING!! tainted filename: '%s'." % (filename)); reject("!!WARNING!! tainted signature filename: '%s'." % (sig_filename));
return 0; return None;
if data_filename and not re_taint_free.match(data_filename):
reject("!!WARNING!! tainted data filename: '%s'." % (data_filename));
return None;
if not keyrings:
keyrings = (Cnf["Dinstall::PGPKeyring"], Cnf["Dinstall::GPGKeyring"])
# Build the command line
status_read, status_write = os.pipe();
cmd = "gpgv --status-fd %s" % (status_write);
for keyring in keyrings:
cmd += " --keyring %s" % (keyring);
cmd += " %s %s" % (sig_filename, data_filename);
# Invoke gpgv on the file # Invoke gpgv on the file
status_read, status_write = os.pipe();
cmd = "gpgv --status-fd %s --keyring %s --keyring %s %s" \
% (status_write, Cnf["Dinstall::PGPKeyring"], Cnf["Dinstall::GPGKeyring"], filename);
(output, status, exit_status) = gpgv_get_status_output(cmd, status_read, status_write); (output, status, exit_status) = gpgv_get_status_output(cmd, status_read, status_write);
# Process the status-fd output # Process the status-fd output
...@@ -896,35 +913,35 @@ to be called more than once during an invocation of check_signature().""" ...@@ -896,35 +913,35 @@ to be called more than once during an invocation of check_signature()."""
# If we failed to parse the status-fd output, let's just whine and bail now # If we failed to parse the status-fd output, let's just whine and bail now
if internal_error: if internal_error:
reject("internal error while performing signature check on %s." % (filename)); reject("internal error while performing signature check on %s." % (sig_filename));
reject(internal_error, ""); reject(internal_error, "");
reject("Please report the above errors to the Archive maintainers by replying to this mail.", ""); reject("Please report the above errors to the Archive maintainers by replying to this mail.", "");
return None; return None;
# Now check for obviously bad things in the processed output # Now check for obviously bad things in the processed output
if keywords.has_key("SIGEXPIRED"): if keywords.has_key("SIGEXPIRED"):
reject("The key used to sign %s has expired." % (filename)); reject("The key used to sign %s has expired." % (sig_filename));
bad = 1; bad = 1;
if keywords.has_key("KEYREVOKED"): if keywords.has_key("KEYREVOKED"):
reject("The key used to sign %s has been revoked." % (filename)); reject("The key used to sign %s has been revoked." % (sig_filename));
bad = 1; bad = 1;
if keywords.has_key("BADSIG"): if keywords.has_key("BADSIG"):
reject("bad signature on %s." % (filename)); reject("bad signature on %s." % (sig_filename));
bad = 1; bad = 1;
if keywords.has_key("ERRSIG") and not keywords.has_key("NO_PUBKEY"): if keywords.has_key("ERRSIG") and not keywords.has_key("NO_PUBKEY"):
reject("failed to check signature on %s." % (filename)); reject("failed to check signature on %s." % (sig_filename));
bad = 1; bad = 1;
if keywords.has_key("NO_PUBKEY"): if keywords.has_key("NO_PUBKEY"):
args = keywords["NO_PUBKEY"]; args = keywords["NO_PUBKEY"];
if len(args) >= 1: if len(args) >= 1:
key = args[0]; key = args[0];
reject("The key (0x%s) used to sign %s wasn't found in the keyring(s)." % (key, filename)); reject("The key (0x%s) used to sign %s wasn't found in the keyring(s)." % (key, sig_filename));
bad = 1; bad = 1;
if keywords.has_key("BADARMOR"): if keywords.has_key("BADARMOR"):
reject("ASCII armour of signature was corrupt in %s." % (filename)); reject("ASCII armour of signature was corrupt in %s." % (sig_filename));
bad = 1; bad = 1;
if keywords.has_key("NODATA"): if keywords.has_key("NODATA"):
reject("no signature found in %s." % (filename)); reject("no signature found in %s." % (sig_filename));
bad = 1; bad = 1;
if bad: if bad:
...@@ -932,7 +949,7 @@ to be called more than once during an invocation of check_signature().""" ...@@ -932,7 +949,7 @@ to be called more than once during an invocation of check_signature()."""
# Next check gpgv exited with a zero return code # Next check gpgv exited with a zero return code
if exit_status: if exit_status:
reject("gpgv failed while checking %s." % (filename)); reject("gpgv failed while checking %s." % (sig_filename));
if status.strip(): if status.strip():
reject(prefix_multi_line_string(status, " [GPG status-fd output:] "), ""); reject(prefix_multi_line_string(status, " [GPG status-fd output:] "), "");
else: else:
...@@ -941,20 +958,20 @@ to be called more than once during an invocation of check_signature().""" ...@@ -941,20 +958,20 @@ to be called more than once during an invocation of check_signature()."""
# Sanity check the good stuff we expect # Sanity check the good stuff we expect
if not keywords.has_key("VALIDSIG"): if not keywords.has_key("VALIDSIG"):
reject("signature on %s does not appear to be valid [No VALIDSIG]." % (filename)); reject("signature on %s does not appear to be valid [No VALIDSIG]." % (sig_filename));
bad = 1; bad = 1;
else: else:
args = keywords["VALIDSIG"]; args = keywords["VALIDSIG"];
if len(args) < 1: if len(args) < 1:
reject("internal error while checking signature on %s." % (filename)); reject("internal error while checking signature on %s." % (sig_filename));
bad = 1; bad = 1;
else: else:
fingerprint = args[0]; fingerprint = args[0];
if not keywords.has_key("GOODSIG"): if not keywords.has_key("GOODSIG"):
reject("signature on %s does not appear to be valid [No GOODSIG]." % (filename)); reject("signature on %s does not appear to be valid [No GOODSIG]." % (sig_filename));
bad = 1; bad = 1;
if not keywords.has_key("SIG_ID"): if not keywords.has_key("SIG_ID"):
reject("signature on %s does not appear to be valid [No SIG_ID]." % (filename)); reject("signature on %s does not appear to be valid [No SIG_ID]." % (sig_filename));
bad = 1; bad = 1;
# Finally ensure there's not something we don't recognise # Finally ensure there's not something we don't recognise
...@@ -964,7 +981,7 @@ to be called more than once during an invocation of check_signature().""" ...@@ -964,7 +981,7 @@ to be called more than once during an invocation of check_signature()."""
for keyword in keywords.keys(): for keyword in keywords.keys():
if not known_keywords.has_key(keyword): if not known_keywords.has_key(keyword):
reject("found unknown status token '%s' from gpgv with args '%r' in %s." % (keyword, keywords[keyword], filename)); reject("found unknown status token '%s' from gpgv with args '%r' in %s." % (keyword, keywords[keyword], sig_filename));
bad = 1; bad = 1;
if bad: if bad:
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册