Import from initial devel-repo
authorPieter Lexis <pieter.lexis@os3.nl>
Tue, 24 Jan 2012 11:55:38 +0000 (12:55 +0100)
committerPieter Lexis <pieter.lexis@os3.nl>
Tue, 24 Jan 2012 11:55:38 +0000 (12:55 +0100)
README [new file with mode: 0644]
root.key [new file with mode: 0644]
swede [new file with mode: 0755]

diff --git a/README b/README
new file mode 100644 (file)
index 0000000..d64c829
--- /dev/null
+++ b/README
@@ -0,0 +1,57 @@
+                          SWEDE - tools to create and verify TLSA (DANE) records
+================================================================================
+Swede aims to provide a one-stop solutions to create and test TLSA records.
+
+                                                                         LICENSE
+--------------------------------------------------------------------------------
+swede is copyright Pieter Lexis <pieter@os3.nl> and is licensed under the terms
+of the GNU General Public Licence version 2 or higher.
+
+                                                                    DEPENDENCIES
+--------------------------------------------------------------------------------
+- Python (>= 2.6)
+- python-{unbound, argparse, ipaddr, m2crypto}
+
+swede has been tested on Debian 6 (Squeeze) using the python-unbound package
+from squeeze-backports.
+
+                                                                        FEATURES
+--------------------------------------------------------------------------------
+- Creation of all 18 permutations of TLSA records
+- Output in draft and RFC format
+- Ability to load certificates from disk to create records from
+- Verify TLSA records 'in the field' with the certificates offered by the TLS
+  service running on the server
+
+                                                                           USAGE
+--------------------------------------------------------------------------------
+See EXAMPLES below and try the following:
+swede --help
+swede create --help
+swede verify --help
+
+                                                                        EXAMPLES
+--------------------------------------------------------------------------------
+swede create --usage 1 --output rfc www.os3.nl
+swede --insecure create --usage 0 mail.google.com
+
+swede verify -p 1516 dane.kiev.practicum.os3.nl
+swede verify ulthar.us
+                                                                            TODO
+--------------------------------------------------------------------------------
+- Creation tool that checks the CN in the Subject of the certificate
+- IPv6 support (M2Crypto doesnt support it at the moment)
+- Creation tool that does an AXFR for a full zone, collects all hostnames, gets
+  the certificates (or the CA certificate from the commandline) and creates all
+  TLSA records.
+- Test certificates (other than using the functions in M2Crypto) when no chain
+  is presented during the TLS session
+- Manpage
+
+                                                                      KNOWN BUGS
+--------------------------------------------------------------------------------
+- swede is mostly untested.
+- Not everything that can raise an exception is in a try/except block
+- No support for SRV record indirection (see Issue 28 of the DANE-WG)
+- No support for TLS/SSL over UDP or SCTP
+- No support for STARTTLS type protocols (only 'straight' SSL/TLS conections)
diff --git a/root.key b/root.key
new file mode 100644 (file)
index 0000000..eda1c35
--- /dev/null
+++ b/root.key
@@ -0,0 +1,9 @@
+; autotrust trust anchor file
+;;id: . 1
+;;last_queried: 1326888589 ;;Wed Jan 18 13:09:49 2012
+;;last_success: 1326888589 ;;Wed Jan 18 13:09:49 2012
+;;next_probe_time: 1326929661 ;;Thu Jan 19 00:34:21 2012
+;;query_failed: 0
+;;query_interval: 43200
+;;retry_time: 8640
+.      172800  IN      DNSKEY  257 3 8 AwEAAagAIKlVZrpC6Ia7gEzahOR+9W29euxhJhVVLOyQbSEW0O8gcCjFFVQUTf6v58fLjwBd0YI0EzrAcQqBGCzh/RStIoO8g0NfnfL2MTJRkxoXbfDaUeVPQuYEhg37NZWAJQ9VnMVDxP/VHL496M/QZxkjf5/Efucp2gaDX6RS6CXpoY68LsvPVjR0ZSwzz1apAzvN9dlzEheX7ICJBBtuA6G3LQpzW5hOA2hzCTMjJPJ8LbqF6dsV6DoBQzgul0sGIcGOYl7OyQdXfZ57relSQageu+ipAdTTJ25AsRTAoub8ONGcLmqrAmRLKBP1dfwhYB4N7knNnulqQxA+Uk1ihz0= ;{id = 19036 (ksk), size = 2048b} ;;state=2 [  VALID  ] ;;count=0 ;;lastchange=1326804159 ;;Tue Jan 17 13:42:39 2012
diff --git a/swede b/swede
new file mode 100755 (executable)
index 0000000..643c079
--- /dev/null
+++ b/swede
@@ -0,0 +1,572 @@
+#!/usr/bin/python
+
+# swede - A tool to create DANE/TLSA (draft 14) records.
+# This tool is really simple and not foolproof, it doesn't check the CN in the
+# Subject field of the certificate. It also doesn't check if the supplied
+# certificate is a CA certificate if usage 1 is specified (or any other
+# checking for that matter).
+#
+# Usage is explained when running this program with --help
+#
+# This tool is loosly based on the dane tool in the sshfp package by Paul
+# Wouters and Christopher Olah from xelerance.com.
+#
+# Copyright Pieter Lexis (pieter.lexis@os3.nl)
+#
+# License: GNU GENERAL PUBLIC LICENSE Version 2 or later
+
+import sys
+import os
+import unbound
+import re
+from M2Crypto import X509, SSL
+from binascii import a2b_hex, b2a_hex
+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.
+       """
+       # check if valid vars were passed
+       if hostname[-1] != '.':
+               hostname += '.'
+
+       certificate = loadCert(certificate)
+       if not certificate:
+               raise Exception('Cannot load certificate from disk')
+
+       # Create the record without a certificate
+       if port == '*':
+               record = TLSARecord(name='%s._%s.%s'%(port,protocol,hostname), usage=usage, selector=selector, mtype=mtype, cert ='')
+       else:
+               record = TLSARecord(name='_%s._%s.%s'%(port,protocol,hostname), usage=usage, selector=selector, mtype=mtype, cert ='')
+       # Check if the record is valid
+       if record.isValid:
+               if record.selector == 0:
+                       # Hash the Full certificate
+                       record.cert = getHash(certificate, record.mtype)
+               else:
+                       # Hash only the SubjectPublicKeyInfo
+                       record.cert = getHash(certificate.get_pubkey(), record.mtype)
+
+       record.isValid(raiseException=True)
+
+       if output == 'draft':
+               return record.getRecord(draft=True)
+       return record.getRecord()
+
+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)
+       ret = []
+       for record in records:
+               ret.append(ARecord(hostname, str(IPv4Address(int(b2a_hex(record),16)))))
+       return ret
+
+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)
+       ret = []
+       for record in records:
+               ret.append(AAAARecord(hostname, str(IPv6Address(int(b2a_hex(record),16)))))
+       return ret
+
+def getVerificationErrorReason(num):
+       """This function returns the name of the X509 Error based on int(num)
+       """
+       # These were taken from the M2Crypto.m2 code
+       return {
+50: "X509_V_ERR_APPLICATION_VERIFICATION",
+22: "X509_V_ERR_CERT_CHAIN_TOO_LONG",
+10: "X509_V_ERR_CERT_HAS_EXPIRED",
+9:  "X509_V_ERR_CERT_NOT_YET_VALID",
+28: "X509_V_ERR_CERT_REJECTED",
+23: "X509_V_ERR_CERT_REVOKED",
+7:  "X509_V_ERR_CERT_SIGNATURE_FAILURE",
+27: "X509_V_ERR_CERT_UNTRUSTED",
+12: "X509_V_ERR_CRL_HAS_EXPIRED",
+11: "X509_V_ERR_CRL_NOT_YET_VALID",
+8:  "X509_V_ERR_CRL_SIGNATURE_FAILURE",
+18: "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT",
+14: "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD",
+13: "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD",
+15: "X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD",
+16: "X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD",
+24: "X509_V_ERR_INVALID_CA",
+26: "X509_V_ERR_INVALID_PURPOSE",
+17: "X509_V_ERR_OUT_OF_MEM",
+25: "X509_V_ERR_PATH_LENGTH_EXCEEDED",
+19: "X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN",
+6:  "X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY",
+4:  "X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE",
+5:  "X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE",
+3:  "X509_V_ERR_UNABLE_TO_GET_CRL",
+2:  "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT",
+20: "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
+21: "X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE",
+0:  "X509_V_OK"}[int(num)]
+
+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."""
+       ctx = unbound.ub_ctx()
+       ctx.add_ta_file('root.key')
+
+       if type(rrtype) == str:
+               if 'RR_TYPE_' + rrtype in dir(unbound):
+                       rrtype = getattr(unbound, 'RR_TYPE_' + rrtype)
+               else:
+                       raise Exception('Error: unknown RR TYPE: %s.' % rrtype)
+       elif type(rrtype) != int:
+               raise Exception('Error: rrtype in wrong format, neither int nor str.')
+
+       status, result = ctx.resolve(hostname, rrtype=rrtype)
+       if status == 0 and result.havedata:
+               if not result.secure:
+                       if secure:
+                               # The data is insecure and a secure lookup was requested
+                               raise InsecureLookupException('Error: query data not secure and secure data requested, unable to continue')
+                       else:
+                               print >> sys.stderr, 'Warning: query data is not secure.'
+               # 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.')
+
+def getHash(certificate, mtype):
+       """Hashes the certificate based on the mtype.
+       The certificate should be an M2Crypto.X509.X509 object (or the result of the get_pubkey() function on said object)
+       """
+       certificate = certificate.as_der()
+       if mtype == 0:
+               return b2a_hex(certificate)
+       elif mtype == 1:
+               return sha256(certificate).hexdigest()
+       elif mtype == 2:
+               return sha512(certificate).hexdigest()
+       else:
+               raise Exception('mtype should be 0,1,2')
+
+def getTLSA(hostname, port=443, protocol='tcp', secure=True):
+       """
+       This function tries to do a secure lookup of the TLSA record.
+       At the moment it requests the TYPE65468 record and parses it into a 'valid' TLSA record
+       It returns a list of TLSARecord objects
+       """
+       if hostname[-1] != '.':
+               hostname += '.'
+
+       if not protocol.lower() in ['tcp', 'udp', 'sctp']:
+               raise Exception('Error: unknown protocol: %s. Should be one of tcp, udp or sctp' % protocol)
+       try:
+               if port == '*':
+                       records = getRecords('*._%s.%s' % (protocol.lower(), hostname), rrtype=65468, secure=secure)
+               else:
+                       records = getRecords('_%s._%s.%s' % (port, protocol.lower(), hostname), rrtype=65468, secure=secure)
+       except InsecureLookupException, e:
+               print str(e)
+               sys.exit(1)
+       ret = []
+       for record in records:
+               hexdata = b2a_hex(record)
+               if port == '*':
+                       ret.append(TLSARecord('*._%s.%s' % (protocol.lower(), hostname), int(hexdata[0:2],16), int(hexdata[2:4],16), int(hexdata[4:6],16), hexdata[6:]))
+               else:
+                       ret.append(TLSARecord('_%s._%s.%s' % (port, protocol.lower(), hostname), int(hexdata[0:2],16), int(hexdata[2:4],16), int(hexdata[4:6],16), hexdata[6:]))
+       return ret
+
+def loadCert(certificate):
+       """Returns an M2Crypto.X509.X509 object"""
+       if isinstance(certificate, X509.X509):
+               # nothing to be done :-)
+               return certificate
+       try:
+               # Maybe we were passed a path
+               return X509.load_cert(certificate)
+       except:
+               # Can't load the cert
+               raise Exception('Unable to load certificate %s.' % certificate)
+
+def verifyCertMatch(record, cert):
+       """
+       Verify the certificate with the record.
+       record should be a TLSARecord and cert should be a M2Crypto.X509.X509
+       """
+       if not isinstance(cert, X509.X509):
+               return
+       if not isinstance(record, TLSARecord):
+               return
+
+       if record.selector == 1:
+               certhash = getHash(cert.get_pubkey(), record.mtype)
+       else:
+               certhash = getHash(cert, record.mtype)
+
+       if not certhash:
+               return
+
+       if certhash == record.cert:
+               return True
+       else:
+               return False
+
+class TLSARecord:
+       """When instanciated, this class contains all the fields of a TLSA record.
+       """
+       def __init__(self, name, usage, selector, mtype, cert):
+               """name is the name of the RR in the format: /^(_\d{1,5}|\*)\._(tcp|udp|sctp)\.([a-z0-9]*\.){2,}$/
+               usage, selector and mtype should be an integer
+               cert should be a hexidecimal string representing the certificate to be matched field
+               """
+               try:
+                       self.rrtype = 65468 # TLSA provisional
+                       self.rrclass = 1    # IN
+                       self.name = str(name)
+                       self.usage = int(usage)
+                       self.selector = int(selector)
+                       self.mtype = int(mtype)
+                       self.cert = str(cert)
+               except:
+                       raise Exception('Invalid value passed, unable to create a TLSARecord')
+
+       def getRecord(self, draft=False):
+               """Returns the RR string of this TLSARecord, either in rfc (default) or draft format"""
+               if draft:
+                       return '%s IN TYPE65468 \# %s %s%s%s%s' % (self.name, (len(self.cert)/2)+3 , self._toHex(self.usage), self._toHex(self.selector), self._toHex(self.mtype), self.cert)
+               return '%s IN TLSA %s %s %s %s' % (self.name, self.usage, self.selector, self.mtype, self.cert)
+
+       def _toHex(self, val):
+               """Helper function to create hex strings from integers"""
+               return "%0.2x" % val
+
+       def isValid(self, raiseException=False):
+               """Check whether all fields in the TLSA record are conforming to the spec and check if the port, protocol and name are good"""
+               err =[]
+               try:
+                       if not 1 <= int(self.getPort()) <= 65535:
+                               err.append('Port %s not within correct range (1 <= port <= 65535)' % self.getPort())
+               except:
+                       if self.getPort() != '*':
+                               err.append('Port %s not a number' % self.getPort())
+               if not self.usage in [0,1,2]:
+                       err.append('Usage: invalid (%s is not one of 0, 1 or 2)' % self.usage)
+               if not self.selector in [0,1]:
+                       err.append('Selector: invalid (%s is not one of 0 or 1)' % self.selector)
+               if not self.mtype in [0,1,2]:
+                       err.append('Matching Type: invalid (%s is not one of 0, 1 or 2)' % self.mtype)
+               if not self.isNameValid():
+                       err.append('Name (%s) is not in the correct format: _portnumber._transportprotocol.hostname.dom.' % self.name)
+               # A certificate length of 0 is accepted
+               if self.mtype in [1,2] and len(self.cert) != 0:
+                       if not len(self.cert) == {1:64,2:128}[self.mtype]:
+                               err.append('Certificate for Association: invalid (Hash length does not match hash-type in Matching Type(%s))' % {1:'SHA-256',2:'SHA-512'}[self.mtype])
+               if len(err) != 0:
+                       if not raiseException:
+                               return False
+                       else:
+                               msg = 'The TLSA record is invalid.'
+                               for error in err:
+                                       msg += '\n\t%s' % error
+                               raise RecordValidityException(msg)
+               else:
+                       return True
+
+       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):
+                       return False
+               return True
+
+       def getProtocol(self):
+               """Returns the protocol based on the name"""
+               return re.split('\.', self.name)[1][1:]
+
+       def getPort(self):
+               """Returns the port based on the name"""
+               if re.split('\.', self.name)[0][0] == '*':
+                       return '*'
+               else:
+                       return re.split('\.', self.name)[0][1:]
+
+class ARecord:
+       """An object representing an A Record (IPv4 address)"""
+       def __init__(self, hostname, address):
+               self.rrtype = 1
+               self.hostname = hostname
+               self.address = address
+
+       def __str__(self):
+               return self.address
+
+       def isValid(self):
+               try:
+                       IPv4Address(self.address)
+                       return True
+               except:
+                       return False
+
+class AAAARecord:
+       """An object representing an AAAA Record (IPv6 address)"""
+       def __init__(self, hostname, address):
+               self.rrtype = 28
+               self.address = address
+
+       def __str__(self):
+               return self.address
+
+       def isValid(self):
+               try:
+                       IPv6Address(self.address)
+                       return True
+               except:
+                       return False
+
+# Exceptions
+class RecordValidityException(Exception):
+       def __init__(self, value):
+               self.value = value
+       def __str__(self):
+               return self.value
+
+class InsecureLookupException(Exception):
+       def __init__(self, value):
+               self.value = value
+       def __str__(self):
+               return self.value
+
+if __name__ == '__main__':
+       import argparse
+       # create the parser
+       parser = argparse.ArgumentParser(description='Create and verify DANE records.', epilog='This tool has a few limitations: it only IPv4 for SSL connections.')
+
+       subparsers = parser.add_subparsers(title='Functions', help='Available functions, see %(prog)s function -h for function-specific help')
+       parser_verify = subparsers.add_parser('verify', help='Verify a TLSA record, exit 0 when all TLSA records are matched, exit 2 when a record does not match the received certificate, exit 1 on error.', epilog='Caveat: For TLSA validation, this program chases through the certificate chain offered by the server, not it\'s local certificates.')
+       parser_verify.set_defaults(function='verify')
+       parser_create = subparsers.add_parser('create', help='Create a TLSA record')
+       parser_create.set_defaults(function='create')
+
+       #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('-v', '--version', action='version', version='%(prog)s v0.1', help='show version and exit')
+       parser.add_argument('host', metavar="hostname")
+
+       parser_verify.add_argument('--port', '-p', action='store', default='443', help='The port, or \'*\' where running TLS is located (default: %(default)s).')
+       parser_verify.add_argument('--protocol', action='store', choices=['tcp','udp','sctp'], default='tcp', help='The protocol the TLS service is using (default: %(default)s).')
+       parser_verify.add_argument('--only-rr', '-o', action='store_true', help='Only verify that the TLSA resource record is correct (do not check certificate)')
+       parser_verify.add_argument('--ca-cert', metavar='/PATH/TO/CERTSTORE', action='store', default = '/etc/ssl/certs/', help='Path to a CA certificate or a directory containing the certificates (default: %(default)s)')
+       parser_verify.add_argument('--quiet', '-q', action='store_true', help='Only print the result of the validation')
+
+       parser_create.add_argument('--port', '-p', action='store', type=int, default=443, help='The port where running TLS is located (default: %(default)s).')
+       parser_create.add_argument('--protocol', action='store', choices=['tcp','udp','sctp'], default='tcp', help='The protocol the TLS service is using (default: %(default)s).')
+       parser_create.add_argument('--certificate', '-c', help='The certificate used for the host. If certificate is empty, the certificate will be downloaded from the server')
+       parser_create.add_argument('--output', '-o', action='store', default='draft', choices=['draft','rfc','both'], help='The type of output. Draft (private RRtype, 65468), RFC (TLSA) or both (default: %(default)s).')
+
+       # Usage of the certificate
+       parser_create.add_argument('--usage', '-u', action='store', type=int, default=1, choices=[0,1,2], help='The Usage of the Certificate for Association. \'0\' for CA, \'1\' for End Entity, \'2\' for trust-anchor (default: %(default)s).')
+       parser_create.add_argument('--selector', '-s', action='store', type=int, default=0, choices=[0,1], help='The Selector for the Certificate for Association. \'0\' for Full Certificate, \'1\' for SubjectPublicKeyInfo (default: %(default)s).')
+       parser_create.add_argument('--mtype', '-m', action='store', type=int, default=1, choices=[0,1,2], help='The Matching Type of the Certificate for Association. \'0\' for Exact match, \'1\' for SHA-256 hash, \'2\' for SHA-512 (default: %(default)s).')
+
+       args = parser.parse_args()
+
+       if args.host[-1] != '.':
+               args.host += '.'
+
+       # not operations are fun!
+       secure = not args.insecure
+
+       if args.function == 'verify':
+               records = getTLSA(args.host, args.port, args.protocol, secure)
+               if len(records) == 0:
+                       sys.exit(1)
+
+               for record in records:
+                       pre_exit = 0
+                       # 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 '\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)
+                       else:
+                               if not args.quiet:
+                                       print 'This record is valid (well-formed).'
+
+                       if args.only_rr:
+                               # Go to the next record
+                               continue
+
+                       # When we are here, The user also wants to verify the certificates with the record
+                       if args.protocol != 'tcp':
+                               print >> sys.stderr, 'Only SSL over TCP is supported (sorry)'
+                               sys.exit(0)
+
+                       if not args.quiet:
+                               print 'Attempting to verify the record with the TLS service...'
+                       addresses = getA(args.host, secure=secure)
+                       for address in addresses:
+                               if not args.quiet:
+                                       print 'Got the following IP: %s' % str(address)
+                               # We do the certificate handling here, as M2Crypto keeps segfaulting when we do it in a method
+                               ctx = SSL.Context()
+                               if os.path.isfile(args.ca_cert):
+                                       if ctx.load_verify_locations(cafile=args.ca_cert) != 1: raise Exception('No CA cert')
+                               elif os.path.exists(args.ca_cert):
+                                       if ctx.load_verify_locations(capath=args.ca_cert) != 1: raise Exception('No CA certs')
+                               else:
+                                       print >> sys.stderr, '%s is neither a file nor a directory, unable to continue' % args.ca_cert
+                                       sys.exit(1)
+                               # Don't error when the verification fails in the SSL handshake
+                               ctx.set_verify(SSL.verify_none, depth=9)
+                               connection = SSL.Connection(ctx)
+                               try:
+                                       connection.connect((str(address), int(args.port)))
+                               except SSL.Checker.WrongHost, e:
+                                       # The name on the remote cert doesn't match the hostname because we connect on IP, not hostname (as we want secure lookup)
+                                       pass
+                               chain = connection.get_peer_cert_chain()
+                               verify_result = connection.get_verify_result()
+
+                               # Good, now let's verify
+                               if record.usage == 1: # End-host cert
+                                       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'
+                                               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 not args.quiet: print 'The matched certificate has Subject: %s' % cert.get_subject()
+                                       else:
+                                               print 'FAIL: Certificate offered by the server does not match the TLSA record'
+                                               if pre_exit == 0: pre_exit = 2
+
+                               elif record.usage == 0: # CA constraint
+                                       matched = False
+                                       # Remove the first (= End-Entity cert) from the chain
+                                       for cert in chain:
+                                               if verifyCertMatch(record, cert):
+                                                       matched = True
+                                                       continue
+                                       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'
+                                                       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
+                                               else:
+                                                       print 'FAIL (Usage 0): A certificate in the certificate chain offered by the server matches the one mentioned in the TLSA record but is not a CA certificate'
+                                                       if pre_exit == 0: pre_exit = 2
+                                               if not args.quiet: print 'The matched certificate has Subject: %s' % cert.get_subject()
+                                       else:
+                                               print 'FAIL (Usage 0): No certificate in the certificate chain offered by the server matches the TLSA record'
+                                               if pre_exit == 0: pre_exit = 2
+
+                               elif record.usage == 2: # Usage 2, ANY cert in the chain must match (aka 'pick any')
+                                       matched = False
+                                       for cert in chain:
+                                               if verifyCertMatch(record, cert):
+                                                       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'
+                                               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'
+                                               if pre_exit == 0: pre_exit = 2
+
+                               # Cleanup, just in case
+                               connection.clear()
+                               connection.close()
+                               ctx.close()
+
+                       # END for address in addresses
+               # END for record in records
+               sys.exit(pre_exit)
+       # END if args.verify
+
+       else: # we want to create
+               cert = None
+               if not args.certificate:
+                       if args.protocol != 'tcp':
+                               print >> sys.stderr, 'Only SSL over TCP is supported (sorry)'
+                               sys.exit(1)
+
+                       print 'No certificate specified on the commandline, attempting to retrieve it from the server %s' % (args.host)
+                       connection_port = args.port
+                       if args.port == '*':
+                               sys.stdout.write('The port specified on the commandline is *, please specify the port of the TLS service on %s (443): ' % args.host)
+                               input_ok = False
+                               while not input_ok:
+                                       user_input = raw_input()
+                                       if user_input == '':
+                                               connection_port = 443
+                                               break
+                                       try:
+                                               if 1 <= int(user_input) <= 65535:
+                                                       connection_port = user_input
+                                                       input_ok = True
+                                       except:
+                                               sys.stdout.write('Port %s not numerical or within correct range (1 <= port <= 65535), try again (hit enter for default 443): ' % user_input)
+                       # Get the A records for the host
+                       try:
+                               addresses = getA(args.host, secure=secure)
+                       except InsecureLookupException, e:
+                               print >> sys.stderr, str(e)
+                               sys.exit(1)
+
+                       for address in addresses:
+                               print 'Attempting to get certificate from %s' % str(address)
+                               # We do the certificate handling here, as M2Crypto keeps segfaulting when try to do stuff with the cert if we don't
+                               ctx = SSL.Context()
+                               ctx.set_verify(SSL.verify_none, depth=9)
+                               connection = SSL.Connection(ctx)
+                               try:
+                                       connection.connect((str(address), int(connection_port)))
+                               except SSL.Checker.WrongHost:
+                                       pass
+
+                               chain = connection.get_peer_cert_chain()
+                               for chaincert in chain:
+                                       if int(args.usage) == 1:
+                                               # The first cert is the end-entity cert
+                                               print 'Got a certificate with Subject: %s' % chaincert.get_subject()
+                                               cert = chaincert
+                                               break
+                                       else:
+                                               if (int(args.usage) == 0 and chaincert.check_ca()) or int(args.usage) == 2:
+                                                       sys.stdout.write('Got a certificate with the following Subject:\n\t%s.\nUse this as certificate to match? [y/N] ' % chaincert.get_subject())
+                                                       input_ok = False
+                                                       while not input_ok:
+                                                               user_input = raw_input()
+                                                               if user_input in ['','n','N']:
+                                                                       input_ok=True
+                                                               elif user_input in ['y', 'Y']:
+                                                                       input_ok = True
+                                                                       cert = chaincert
+                                                               else:
+                                                                       sys.stdout.write('Please answer Y or N')
+                                               if cert:
+                                                       break
+
+                               if cert: # Print the requested records based on the retrieved certificates
+                                       if args.output == 'b':
+                                               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':
+                               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:
+                               print genTLSA(args.host, args.protocol, args.port, args.certificate, args.output, args.usage, args.selector, args.mtype)