Add options for fudging or ignoring times in verifyzone.

git-svn-id: https://svn.verisignlabs.com/jdnssec/tools/trunk@224 4cbd57fe-54e5-0310-bd9a-f30fe5ea5e6e
This commit is contained in:
David Blacka 2010-12-14 18:01:12 +00:00
parent 3d6b21b0fc
commit 86072cbcc8
4 changed files with 116 additions and 60 deletions

View File

@ -1,3 +1,8 @@
2010-12-14 Blacka <davidb@verisignlabs.com>
* jdnssec-verifyzone: Add options to either fudge or ignore RRSIG
inception and expiration times.
2010-12-06 David Blacka <davidb@verisignlabs.com> 2010-12-06 David Blacka <davidb@verisignlabs.com>
* jdnssec-verifyzone: Complete refactored the verification code to * jdnssec-verifyzone: Complete refactored the verification code to

View File

@ -50,7 +50,8 @@ public class VerifyZone
{ {
private static Logger log; private static Logger log;
// A log formatter that strips away all of the noise that the default SimpleFormatter has // A log formatter that strips away all of the noise that the default
// SimpleFormatter has
private static class MyLogFormatter extends java.util.logging.Formatter private static class MyLogFormatter extends java.util.logging.Formatter
{ {
@Override @Override
@ -75,10 +76,11 @@ public class VerifyZone
private static class CLIState private static class CLIState
{ {
private Options opts; private Options opts;
// public boolean strict = false;
// public File keydir = null;
public String zonefile = null; public String zonefile = null;
public String[] keyfiles = null; public String[] keyfiles = null;
public int startfudge = 0;
public int expirefudge = 0;
public boolean ignoreTime = false;
public CLIState() public CLIState()
{ {
@ -102,7 +104,7 @@ public class VerifyZone
OptionBuilder.withLongOpt("verbose"); OptionBuilder.withLongOpt("verbose");
OptionBuilder.withArgName("level"); OptionBuilder.withArgName("level");
OptionBuilder.withDescription("verbosity level -- 0 is silence, " OptionBuilder.withDescription("verbosity level -- 0 is silence, "
+ "5 is debug information, 6 is trace information.\n" + "default is level 5."); + "5 is debug information, 6 is trace information. default is level 5.");
opts.addOption(OptionBuilder.create('v')); opts.addOption(OptionBuilder.create('v'));
OptionBuilder.hasArg(); OptionBuilder.hasArg();
@ -110,6 +112,22 @@ public class VerifyZone
OptionBuilder.withLongOpt("alg-alias"); OptionBuilder.withLongOpt("alg-alias");
OptionBuilder.withDescription("Define an alias for an algorithm"); OptionBuilder.withDescription("Define an alias for an algorithm");
opts.addOption(OptionBuilder.create('A')); opts.addOption(OptionBuilder.create('A'));
OptionBuilder.hasOptionalArg();
OptionBuilder.withLongOpt("sig-start-fudge");
OptionBuilder.withArgName("seconds");
OptionBuilder.withDescription("'fudge' RRSIG inception times by 'seconds' seconds.");
opts.addOption(OptionBuilder.create('S'));
OptionBuilder.hasOptionalArg();
OptionBuilder.withLongOpt("sig-expire-fudge");
OptionBuilder.withArgName("seconds");
OptionBuilder.withDescription("'fudge' RRSIG expiration times by 'seconds' seconds.");
opts.addOption(OptionBuilder.create('E'));
OptionBuilder.withLongOpt("ignore-time");
OptionBuilder.withDescription("Ignore RRSIG inception and expiration time errors.");
opts.addOption(OptionBuilder.create());
} }
public void parseCommandLine(String[] args) public void parseCommandLine(String[] args)
@ -153,6 +171,22 @@ public class VerifyZone
org.xbill.DNS.Options.set("multiline"); org.xbill.DNS.Options.set("multiline");
} }
if (cli.hasOption("ignore-time"))
{
ignoreTime = true;
}
String optstr = null;
if ((optstr = cli.getOptionValue('S')) != null)
{
startfudge = parseInt(optstr, 0);
}
if ((optstr = cli.getOptionValue('E')) != null)
{
expirefudge = parseInt(optstr, 0);
}
String[] optstrs = null; String[] optstrs = null;
if ((optstrs = cli.getOptionValues('A')) != null) if ((optstrs = cli.getOptionValues('A')) != null)
{ {
@ -207,7 +241,8 @@ public class VerifyZone
// print our own usage statement: // print our own usage statement:
f.printHelp(out, 75, "jdnssec-verifyzone [..options..] zonefile " f.printHelp(out, 75, "jdnssec-verifyzone [..options..] zonefile "
+ "[keyfile [keyfile...]]", null, opts, HelpFormatter.DEFAULT_LEFT_PAD, + "[keyfile [keyfile...]]", null, opts,
HelpFormatter.DEFAULT_LEFT_PAD,
HelpFormatter.DEFAULT_DESC_PAD, null); HelpFormatter.DEFAULT_DESC_PAD, null);
out.flush(); out.flush();
@ -242,6 +277,9 @@ public class VerifyZone
public static void execute(CLIState state) throws Exception public static void execute(CLIState state) throws Exception
{ {
ZoneVerifier zoneverifier = new ZoneVerifier(); ZoneVerifier zoneverifier = new ZoneVerifier();
zoneverifier.getVerifier().setStartFudge(state.startfudge);
zoneverifier.getVerifier().setExpireFudge(state.expirefudge);
zoneverifier.getVerifier().setIgnoreTime(state.ignoreTime);
List records = ZoneUtils.readZoneFile(state.zonefile, null); List records = ZoneUtils.readZoneFile(state.zonefile, null);

View File

@ -94,8 +94,7 @@ public class DnsSecVerifier implements Verifier
for (Iterator i = l.iterator(); i.hasNext();) for (Iterator i = l.iterator(); i.hasNext();)
{ {
DnsKeyPair p = (DnsKeyPair) i.next(); DnsKeyPair p = (DnsKeyPair) i.next();
if (p.getDNSKEYAlgorithm() == algorithm if (p.getDNSKEYAlgorithm() == algorithm && p.getDNSKEYFootprint() == keyid)
&& p.getDNSKEYFootprint() == keyid)
{ {
return p; return p;
} }
@ -108,6 +107,7 @@ public class DnsSecVerifier implements Verifier
private int mStartFudge = 0; private int mStartFudge = 0;
private int mExpireFudge = 0; private int mExpireFudge = 0;
private boolean mVerifyAllSigs = false; private boolean mVerifyAllSigs = false;
private boolean mIgnoreTime = false;
private Logger log; private Logger log;
@ -153,8 +153,12 @@ public class DnsSecVerifier implements Verifier
mVerifyAllSigs = v; mVerifyAllSigs = v;
} }
private DnsKeyPair findCachedKey(Cache cache, Name name, int algorithm, public void setIgnoreTime(boolean v)
int footprint) {
mIgnoreTime = v;
}
private DnsKeyPair findCachedKey(Cache cache, Name name, int algorithm, int footprint)
{ {
RRset[] keysets = cache.findAnyRecords(name, Type.KEY); RRset[] keysets = cache.findAnyRecords(name, Type.KEY);
if (keysets == null) return null; if (keysets == null) return null;
@ -166,8 +170,7 @@ public class DnsSecVerifier implements Verifier
Object o = i.next(); Object o = i.next();
if (!(o instanceof DNSKEYRecord)) continue; if (!(o instanceof DNSKEYRecord)) continue;
DNSKEYRecord keyrec = (DNSKEYRecord) o; DNSKEYRecord keyrec = (DNSKEYRecord) o;
if (keyrec.getAlgorithm() == algorithm if (keyrec.getAlgorithm() == algorithm && keyrec.getFootprint() == footprint)
&& keyrec.getFootprint() == footprint)
{ {
return new DnsKeyPair(keyrec, (PrivateKey) null); return new DnsKeyPair(keyrec, (PrivateKey) null);
} }
@ -176,8 +179,7 @@ public class DnsSecVerifier implements Verifier
return null; return null;
} }
private DnsKeyPair findKey(Cache cache, Name name, int algorithm, private DnsKeyPair findKey(Cache cache, Name name, int algorithm, int footprint)
int footprint)
{ {
DnsKeyPair pair = mKeyStore.find(name, algorithm, footprint); DnsKeyPair pair = mKeyStore.find(name, algorithm, footprint);
if (pair == null && cache != null) if (pair == null && cache != null)
@ -194,17 +196,17 @@ public class DnsSecVerifier implements Verifier
if (!rrset.getName().equals(sigrec.getName())) if (!rrset.getName().equals(sigrec.getName()))
{ {
log.fine("Signature name does not match RRset name"); log.fine("Signature name does not match RRset name");
if (reasons != null) if (reasons != null) reasons.add("Signature name does not match RRset name");
reasons.add("Signature name does not match RRset name");
return DNSSEC.Failed; return DNSSEC.Failed;
} }
if (rrset.getType() != sigrec.getTypeCovered()) if (rrset.getType() != sigrec.getTypeCovered())
{ {
log.fine("Signature type does not match RRset type"); log.fine("Signature type does not match RRset type");
if (reasons != null) if (reasons != null) reasons.add("Signature type does not match RRset type");
reasons.add("Signature type does not match RRset type");
} }
if (mIgnoreTime) return DNSSEC.Secure;
Date now = new Date(); Date now = new Date();
Date start = sigrec.getTimeSigned(); Date start = sigrec.getTimeSigned();
Date expire = sigrec.getExpire(); Date expire = sigrec.getExpire();
@ -231,8 +233,7 @@ public class DnsSecVerifier implements Verifier
} }
if (now.after(expire)) if (now.after(expire))
{ {
log.fine("Signature has expired (now = " + now + ", sig expires = " log.fine("Signature has expired (now = " + now + ", sig expires = " + expire);
+ expire);
if (reasons != null) reasons.add("Signature has expired."); if (reasons != null) reasons.add("Signature has expired.");
return DNSSEC.Failed; return DNSSEC.Failed;
} }
@ -254,14 +255,13 @@ public class DnsSecVerifier implements Verifier
* could not be completed (usually because the public key was not * could not be completed (usually because the public key was not
* available). * available).
*/ */
public byte verifySignature(RRset rrset, RRSIGRecord sigrec, Cache cache, public byte verifySignature(RRset rrset, RRSIGRecord sigrec, Cache cache, List reasons)
List reasons)
{ {
byte result = validateSignature(rrset, sigrec, reasons); byte result = validateSignature(rrset, sigrec, reasons);
if (result != DNSSEC.Secure) return result; if (result != DNSSEC.Secure) return result;
DnsKeyPair keypair = findKey(cache, sigrec.getSigner(), sigrec DnsKeyPair keypair = findKey(cache, sigrec.getSigner(), sigrec.getAlgorithm(),
.getAlgorithm(), sigrec.getFootprint()); sigrec.getFootprint());
if (keypair == null) if (keypair == null)
{ {
@ -288,8 +288,7 @@ public class DnsSecVerifier implements Verifier
if (!signer.verify(sig)) if (!signer.verify(sig))
{ {
if (reasons != null) if (reasons != null) reasons.add("Signature failed to verify cryptographically");
reasons.add("Signature failed to verify cryptographically");
log.fine("Signature failed to verify cryptographically"); log.fine("Signature failed to verify cryptographically");
return DNSSEC.Failed; return DNSSEC.Failed;
} }
@ -304,8 +303,7 @@ public class DnsSecVerifier implements Verifier
{ {
log.severe("Security error: " + e); log.severe("Security error: " + e);
} }
if (reasons != null) if (reasons != null) reasons.add("Signature failed to verify due to exception");
reasons.add("Signature failed to verify due to exception");
log.fine("Signature failed to verify due to exception"); log.fine("Signature failed to verify due to exception");
return DNSSEC.Insecure; return DNSSEC.Insecure;
} }

View File

@ -110,6 +110,11 @@ public class ZoneVerifier
mBAcmp = new ByteArrayComparator(); mBAcmp = new ByteArrayComparator();
} }
public DnsSecVerifier getVerifier()
{
return mVerifier;
}
private static String key(Name n, int type) private static String key(Name n, int type)
{ {
return n.toString() + ':' + type; return n.toString() + ':' + type;
@ -231,7 +236,8 @@ public class ZoneVerifier
// All RRs at the zone apex are normal // All RRs at the zone apex are normal
if (n.equals(mZoneName)) return NodeType.NORMAL; if (n.equals(mZoneName)) return NodeType.NORMAL;
// If the node is below a zone cut (either a delegation or DNAME), it is glue. // If the node is below a zone cut (either a delegation or DNAME), it is
// glue.
if (last_cut != null && n.subdomain(last_cut) && !n.equals(last_cut)) if (last_cut != null && n.subdomain(last_cut) && !n.equals(last_cut))
{ {
return NodeType.GLUE; return NodeType.GLUE;
@ -284,7 +290,8 @@ public class ZoneVerifier
for (int type : typeset) for (int type : typeset)
{ {
if (type == Type.RRSIG) continue; if (type == Type.RRSIG) continue;
// at delegation points, only DS RRs are signed (and NSEC, but those are checked separately) // at delegation points, only DS RRs are signed (and NSEC, but those are
// checked separately)
if (ntype == NodeType.DELEGATION && type != Type.DS) continue; if (ntype == NodeType.DELEGATION && type != Type.DS) continue;
// otherwise, verify the RRset. // otherwise, verify the RRset.
String k = key(n, type); String k = key(n, type);
@ -370,22 +377,6 @@ public class ZoneVerifier
return result == DNSSEC.Secure ? 0 : 1; return result == DNSSEC.Secure ? 0 : 1;
} }
private String typesetToString(Set<Integer> typeset)
{
if (typeset == null) return "";
StringBuilder sb = new StringBuilder();
boolean first = true;
for (int type : typeset)
{
if (!first) sb.append(' ');
sb.append(Type.string(type));
first = false;
}
return sb.toString();
}
private String typesToString(int[] types) private String typesToString(int[] types)
{ {
StringBuilder sb = new StringBuilder(); StringBuilder sb = new StringBuilder();
@ -400,9 +391,23 @@ public class ZoneVerifier
return sb.toString(); return sb.toString();
} }
private String typesetToString(Set<Integer> typeset)
{
if (typeset == null) return "";
int[] types = new int[typeset.size()];
int i = 0;
for (int type : typeset)
{
types[i++] = type;
}
return typesToString(types);
}
private boolean checkTypeMap(Set<Integer> typeset, int[] types) private boolean checkTypeMap(Set<Integer> typeset, int[] types)
{ {
// a null typeset means that we are expecting the typemap of an ENT, which should be empty. // a null typeset means that we are expecting the typemap of an ENT, which
// should be empty.
if (typeset == null) return types.length == 0; if (typeset == null) return types.length == 0;
Set compareTypeset = new HashSet(); Set compareTypeset = new HashSet();
@ -446,7 +451,8 @@ public class ZoneVerifier
private boolean shouldCheckENTs(Name n, Set<Integer> typeset, NodeType ntype) private boolean shouldCheckENTs(Name n, Set<Integer> typeset, NodeType ntype)
{ {
// if we are just one (or zero) labels longer than the zonename, the node can't create a ENT // if we are just one (or zero) labels longer than the zonename, the node
// can't create a ENT
if (n.labels() <= mZoneName.labels() + 1) return false; if (n.labels() <= mZoneName.labels() + 1) return false;
// we probably won't ever get called for a GLUE node // we probably won't ever get called for a GLUE node
@ -455,7 +461,8 @@ public class ZoneVerifier
// if we aren't doing opt-out, then all possible ENTs must be checked. // if we aren't doing opt-out, then all possible ENTs must be checked.
if (mDNSSECType == DNSSECType.NSEC3) return true; if (mDNSSECType == DNSSECType.NSEC3) return true;
// if we are opt-out, and the node is an insecure delegation, don't check ENTs. // if we are opt-out, and the node is an insecure delegation, don't check
// ENTs.
if (ntype == NodeType.DELEGATION && !typeset.contains(Type.DS)) if (ntype == NodeType.DELEGATION && !typeset.contains(Type.DS))
{ {
return false; return false;
@ -492,7 +499,7 @@ public class ZoneVerifier
{ {
log.warning("Typemap for NSEC3 RR " + hashname + " for " + n log.warning("Typemap for NSEC3 RR " + hashname + " for " + n
+ " did not match what was expected. Expected '" + typesetToString(typeset) + " did not match what was expected. Expected '" + typesetToString(typeset)
+ "', got '" + typesToString(nsec3.getTypes())); + "', got '" + typesToString(nsec3.getTypes()) + "'");
errors++; errors++;
} }
@ -520,7 +527,8 @@ public class ZoneVerifier
for (Iterator<Map.Entry<Name, MarkRRset>> i = mNSECMap.entrySet().iterator(); i.hasNext();) for (Iterator<Map.Entry<Name, MarkRRset>> i = mNSECMap.entrySet().iterator(); i.hasNext();)
{ {
// check the internal ordering of the previous NSEC record. This avoids looking at the last one, // check the internal ordering of the previous NSEC record. This avoids
// looking at the last one,
// which is different. // which is different.
if (lastNSEC != null) if (lastNSEC != null)
{ {
@ -536,7 +544,8 @@ public class ZoneVerifier
Name n = entry.getKey(); Name n = entry.getKey();
MarkRRset rrset = entry.getValue(); MarkRRset rrset = entry.getValue();
// check to see if the NSEC is marked. If not, it was not correlated to a signed node. // check to see if the NSEC is marked. If not, it was not correlated to a
// signed node.
if (!rrset.getMark()) if (!rrset.getMark())
{ {
log.warning("NSEC RR for " + n + " appears to be extra."); log.warning("NSEC RR for " + n + " appears to be extra.");
@ -545,7 +554,8 @@ public class ZoneVerifier
NSECRecord nsec = (NSECRecord) rrset.first(); NSECRecord nsec = (NSECRecord) rrset.first();
// This is just a sanity check. If this isn't true, we are constructing the // This is just a sanity check. If this isn't true, we are constructing
// the
// nsec map incorrectly. // nsec map incorrectly.
if (!n.equals(nsec.getName())) if (!n.equals(nsec.getName()))
{ {
@ -553,7 +563,8 @@ public class ZoneVerifier
errors++; errors++;
} }
// If this is the first row, ensure that the owner name equals the zone name // If this is the first row, ensure that the owner name equals the zone
// name
if (lastNSEC == null && !n.equals(mZoneName)) if (lastNSEC == null && !n.equals(mZoneName))
{ {
log.warning("The first NSEC in the chain does not match the zone name: name = " log.warning("The first NSEC in the chain does not match the zone name: name = "
@ -611,7 +622,8 @@ public class ZoneVerifier
for (Iterator<Map.Entry<Name, MarkRRset>> i = mNSEC3Map.entrySet().iterator(); i.hasNext();) for (Iterator<Map.Entry<Name, MarkRRset>> i = mNSEC3Map.entrySet().iterator(); i.hasNext();)
{ {
// check the internal ordering of the previous NSEC3 record. This avoids looking at the last one, // check the internal ordering of the previous NSEC3 record. This avoids
// looking at the last one,
// which is different. // which is different.
if (lastNSEC3 != null) if (lastNSEC3 != null)
{ {
@ -627,7 +639,8 @@ public class ZoneVerifier
Name n = entry.getKey(); Name n = entry.getKey();
MarkRRset rrset = entry.getValue(); MarkRRset rrset = entry.getValue();
// check to see if the NSEC is marked. If not, it was not correlated to a signed node. // check to see if the NSEC is marked. If not, it was not correlated to a
// signed node.
if (!rrset.getMark()) if (!rrset.getMark())
{ {
log.warning("NSEC3 RR for " + n + " appears to be extra."); log.warning("NSEC3 RR for " + n + " appears to be extra.");
@ -636,7 +649,8 @@ public class ZoneVerifier
NSEC3Record nsec3 = (NSEC3Record) rrset.first(); NSEC3Record nsec3 = (NSEC3Record) rrset.first();
// This is just a sanity check. If this isn't true, we are constructing the // This is just a sanity check. If this isn't true, we are constructing
// the
// nsec3 map incorrectly. // nsec3 map incorrectly.
if (!n.equals(nsec3.getName())) if (!n.equals(nsec3.getName()))
{ {
@ -650,7 +664,8 @@ public class ZoneVerifier
firstNSEC3 = nsec3; firstNSEC3 = nsec3;
} }
else else
// Check that the prior NSEC3's next hashed name equals this row's hashed owner name. // Check that the prior NSEC3's next hashed name equals this row's hashed
// owner name.
{ {
if (compareNSEC3Hashes(nsec3.getName(), lastNSEC3.getNext()) != 0) if (compareNSEC3Hashes(nsec3.getName(), lastNSEC3.getNext()) != 0)
{ {