from hashlib import sha256, sha512
from ipaddr import IPv4Address, IPv6Address
+
def genTLSA(hostname, protocol, port, certificate, output='draft', usage=1, selector=0, mtype=1):
"""This function generates a TLSARecord object using the data passed in the parameters,
it then validates the record and returns the RR as a string.
def getA(hostname, secure=True):
"""Gets a list of A records for hostname, returns a list of ARecords"""
- records = getRecords(hostname, rrtype='A', secure=secure)
+ try:
+ records = getRecords(hostname, rrtype='A', secure=secure)
+ except InsecureLookupException, e:
+ print str(e)
+ sys.exit(1)
+ except DNSLookupError, e:
+ print 'Unable to resolve %s: %s' % (hostname, str(e))
+ sys.exit(1)
ret = []
for record in records:
ret.append(ARecord(hostname, str(IPv4Address(int(b2a_hex(record),16)))))
def getAAAA(hostname, secure=True):
"""Gets a list of A records for hostname, returns a list of AAAARecords"""
- records = getRecords(hostname, rrtype='AAAA', secure=secure)
+ try:
+ records = getRecords(hostname, rrtype='AAAA', secure=secure)
+ except InsecureLookupException, e:
+ print str(e)
+ sys.exit(1)
+ except DNSLookupError, e:
+ print 'Unable to resolve %s: %s' % (hostname, str(e))
+ sys.exit(1)
ret = []
for record in records:
ret.append(AAAARecord(hostname, str(IPv6Address(int(b2a_hex(record),16)))))
def getRecords(hostname, rrtype='A', secure=True):
"""Do a lookup of a name and a rrtype, returns a list of binary coded strings. Only queries for rr_class IN."""
+ global resolvconf
ctx = unbound.ub_ctx()
ctx.add_ta_file('root.key')
+ # Use the local cache
+ if resolvconf and os.path.isfile(resolvconf):
+ ctx.resolvconf(resolvconf)
if type(rrtype) == str:
if 'RR_TYPE_' + rrtype in dir(unbound):
# If we are here the data was either secure or insecure data is accepted
return result.data.raw
else:
- raise Exception('Error: Unsuccesful lookup or no data returned.')
+ raise DNSLookupError('Unsuccesful lookup or no data returned for rrtype %s.' % rrtype)
def getHash(certificate, mtype):
"""Hashes the certificate based on the mtype.
except InsecureLookupException, e:
print str(e)
sys.exit(1)
+ except DNSLookupError, e:
+ print 'Unable to resolve %s: %s' % (hostname, str(e))
+ sys.exit(1)
ret = []
for record in records:
hexdata = b2a_hex(record)
def isNameValid(self):
"""Check if the name if in the correct format"""
- if not re.match('^(_\d{1,5}|\*)\._(tcp|udp|sctp)\.([a-z0-9]*\.){2,}$', self.name):
+ if not re.match('^(_\d{1,5}|\*)\._(tcp|udp|sctp)\.([-a-z0-9]*\.){2,}$', self.name):
return False
return True
# Exceptions
class RecordValidityException(Exception):
- def __init__(self, value):
- self.value = value
- def __str__(self):
- return self.value
+ pass
class InsecureLookupException(Exception):
- def __init__(self, value):
- self.value = value
- def __str__(self):
- return self.value
+ pass
+
+class DNSLookupError(Exception):
+ pass
if __name__ == '__main__':
import argparse
#parser.add_argument('-4', dest='ipv4', action='store_true',help='use ipv4 networking only')
#parser.add_argument('-6', dest='ipv6', action='store_true',help='use ipv6 networking only')
parser.add_argument('--insecure', action='store_true', default=False, help='Allow use of non-dnssec secured answers')
+ parser.add_argument('--resolvconf', metavar='/PATH/TO/RESOLV.CONF', action='store', default='', help='Use a recursive resolver from resolv.conf')
parser.add_argument('-v', '--version', action='version', version='%(prog)s v0.1', help='show version and exit')
parser.add_argument('host', metavar="hostname")
if args.host[-1] != '.':
args.host += '.'
+ global resolvconf
+ if args.resolvconf:
+ if os.path.isfile(args.resolvconf):
+ resolvconf = args.resolvconf
+ else:
+ print >> sys.stdout, '%s is not a file. Unable to use it as resolv.conf' % args.resolvconf
+ sys.exit(1)
+ else:
+ resolvconf = None
+
# not operations are fun!
secure = not args.insecure
# First, check if the first three fields have correct values.
if not args.quiet:
print 'Received the following record for name %s:' % record.name
- print '\tUsage:\t\t\t\t%d (%s)' % (record.usage, {0:'CA Constraint', 1:'End-Entity Constraint', 2:'Trust Anchor'}[record.usage])
- print '\tSelector:\t\t\t%d (%s)' % (record.selector, {0:'Certificate', 1:'SubjectPublicKeyInfo'}[record.selector])
- print '\tMatching Type:\t\t\t%d (%s)' % (record.mtype, {0:'Full Certificate', 1:'SHA-256', 2:'SHA-512'}[record.mtype])
+ print '\tUsage:\t\t\t\t%d (%s)' % (record.usage, {0:'CA Constraint', 1:'End-Entity Constraint', 2:'Trust Anchor'}.get(record.usage, 'INVALID'))
+ print '\tSelector:\t\t\t%d (%s)' % (record.selector, {0:'Certificate', 1:'SubjectPublicKeyInfo'}.get(record.usage, 'INVALID'))
+ print '\tMatching Type:\t\t\t%d (%s)' % (record.mtype, {0:'Full Certificate', 1:'SHA-256', 2:'SHA-512'}.get(record.usage, 'INVALID'))
print '\tCertificate for Association:\t%s' % record.cert
try:
record.isValid(raiseException=True)
except RecordValidityException, e:
- print sys.stderr, 'Error: %s' % str(e)
- sys.exit(1)
+ print >> sys.stderr, 'Error: %s' % str(e)
+ continue
else:
if not args.quiet:
print 'This record is valid (well-formed).'
cert = chain[0]
if verifyCertMatch(record, cert):
if verify_result == 0: # The cert chains to a valid CA cert according to the system-certificates
- print 'SUCCES (Usage 1): Certificate offered by the server matches the one mentioned in the TLSA record and chains to a valid CA certificate'
+ print 'SUCCESS (Usage 1): Certificate offered by the server matches the one mentioned in the TLSA record and chains to a valid CA certificate'
else:
print 'FAIL (Usage 1): Certificate offered by the server matches the one mentioned in the TLSA record but the following error was raised during PKIX validation: %s' % getVerificationErrorReason(verify_result)
if pre_exit == 0: pre_exit = 2
if matched:
if cert.check_ca():
if verify_result == 0:
- print 'SUCCES (Usage 0): A certificate in the certificate chain offered by the server matches the one mentioned in the TLSA record and is a CA certificate'
+ print 'SUCCESS (Usage 0): A certificate in the certificate chain offered by the server matches the one mentioned in the TLSA record and is a CA certificate'
else:
print 'FAIL (Usage 0): A certificate in the certificate chain offered by the server matches the one mentioned in the TLSA record and is a CA certificate, but the following error was raised during PKIX validation:' % getVerificationErrorReason(verify_result)
if pre_exit == 0: pre_exit = 2
matched = True
continue
if matched:
- print 'SUCCES (usage 2): A certificate in the certificate chain (including the end-entity certificate) offered by the server matches the TLSA record'
+ print 'SUCCESS (usage 2): A certificate in the certificate chain (including the end-entity certificate) offered by the server matches the TLSA record'
if not args.quiet: print 'The matched certificate has Subject: %s' % cert.get_subject()
else:
print 'FAIL (usage 2): No certificate in the certificate chain (including the end-entity certificate) offered by the server matches the TLSA record'
break
if cert: # Print the requested records based on the retrieved certificates
- if args.output == 'b':
+ if args.output == 'both':
print genTLSA(args.host, args.protocol, args.port, cert, 'draft', args.usage, args.selector, args.mtype)
print genTLSA(args.host, args.protocol, args.port, cert, 'rfc', args.usage, args.selector, args.mtype)
else:
print genTLSA(args.host, args.protocol, args.port, cert, args.output, args.usage, args.selector, args.mtype)
else: # Pass the path to the certificate to the genTLSA function
- if args.output == 'b':
+ if args.output == 'both':
print genTLSA(args.host, args.protocol, args.port, args.certificate, 'draft', args.usage, args.selector, args.mtype)
print genTLSA(args.host, args.protocol, args.port, args.certificate, 'rfc', args.usage, args.selector, args.mtype)
else: