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>
* jdnssec-verifyzone: Complete refactored the verification code to

View File

@ -50,7 +50,8 @@ public class VerifyZone
{
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
{
@Override
@ -58,7 +59,7 @@ public class VerifyZone
{
StringBuilder out = new StringBuilder();
String lvl = arg0.getLevel().getName();
out.append(lvl);
out.append(": ");
out.append(arg0.getMessage());
@ -75,10 +76,11 @@ public class VerifyZone
private static class CLIState
{
private Options opts;
// public boolean strict = false;
// public File keydir = null;
public String zonefile = null;
public String[] keyfiles = null;
public String zonefile = null;
public String[] keyfiles = null;
public int startfudge = 0;
public int expirefudge = 0;
public boolean ignoreTime = false;
public CLIState()
{
@ -102,7 +104,7 @@ public class VerifyZone
OptionBuilder.withLongOpt("verbose");
OptionBuilder.withArgName("level");
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'));
OptionBuilder.hasArg();
@ -110,6 +112,22 @@ public class VerifyZone
OptionBuilder.withLongOpt("alg-alias");
OptionBuilder.withDescription("Define an alias for an algorithm");
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)
@ -153,6 +171,22 @@ public class VerifyZone
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;
if ((optstrs = cli.getOptionValues('A')) != null)
{
@ -207,7 +241,8 @@ public class VerifyZone
// print our own usage statement:
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);
out.flush();
@ -242,6 +277,9 @@ public class VerifyZone
public static void execute(CLIState state) throws Exception
{
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);

View File

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

View File

@ -110,6 +110,11 @@ public class ZoneVerifier
mBAcmp = new ByteArrayComparator();
}
public DnsSecVerifier getVerifier()
{
return mVerifier;
}
private static String key(Name n, int type)
{
return n.toString() + ':' + type;
@ -231,7 +236,8 @@ public class ZoneVerifier
// All RRs at the zone apex are 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))
{
return NodeType.GLUE;
@ -284,7 +290,8 @@ public class ZoneVerifier
for (int type : typeset)
{
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;
// otherwise, verify the RRset.
String k = key(n, type);
@ -370,22 +377,6 @@ public class ZoneVerifier
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)
{
StringBuilder sb = new StringBuilder();
@ -400,9 +391,23 @@ public class ZoneVerifier
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)
{
// 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;
Set compareTypeset = new HashSet();
@ -446,7 +451,8 @@ public class ZoneVerifier
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;
// 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 (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))
{
return false;
@ -492,7 +499,7 @@ public class ZoneVerifier
{
log.warning("Typemap for NSEC3 RR " + hashname + " for " + n
+ " did not match what was expected. Expected '" + typesetToString(typeset)
+ "', got '" + typesToString(nsec3.getTypes()));
+ "', got '" + typesToString(nsec3.getTypes()) + "'");
errors++;
}
@ -520,7 +527,8 @@ public class ZoneVerifier
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.
if (lastNSEC != null)
{
@ -536,7 +544,8 @@ public class ZoneVerifier
Name n = entry.getKey();
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())
{
log.warning("NSEC RR for " + n + " appears to be extra.");
@ -545,7 +554,8 @@ public class ZoneVerifier
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.
if (!n.equals(nsec.getName()))
{
@ -553,7 +563,8 @@ public class ZoneVerifier
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))
{
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();)
{
// 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.
if (lastNSEC3 != null)
{
@ -627,7 +639,8 @@ public class ZoneVerifier
Name n = entry.getKey();
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())
{
log.warning("NSEC3 RR for " + n + " appears to be extra.");
@ -636,7 +649,8 @@ public class ZoneVerifier
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.
if (!n.equals(nsec3.getName()))
{
@ -650,7 +664,8 @@ public class ZoneVerifier
firstNSEC3 = nsec3;
}
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)
{