3 # swede - A tool to create DANE/TLSA records.
4 # This tool is really simple and not foolproof, it doesn't check the CN in the
5 # Subject field of the certificate. It also doesn't check if the supplied
6 # certificate is a CA certificate if usage 1 is specified (or any other
7 # checking for that matter).
9 # Usage is explained when running this program with --help
11 # This tool is loosly based on the dane tool in the sshfp package by Paul
12 # Wouters and Christopher Olah from xelerance.com.
14 # Copyright Pieter Lexis (pieter.lexis@os3.nl)
16 # License: GNU GENERAL PUBLIC LICENSE Version 2 or later
24 from M2Crypto import X509, SSL
25 from binascii import a2b_hex, b2a_hex
26 from hashlib import sha256, sha512
27 from ipaddr import IPv4Address, IPv6Address
33 def genTLSA(hostname, protocol, port, certificate, output='generic', usage=1, selector=0, mtype=1):
34 """This function generates a TLSARecord object using the data passed in the parameters,
35 it then validates the record and returns the RR as a string.
37 # check if valid vars were passed
38 if hostname[-1] != '.':
41 certificate = loadCert(certificate)
43 raise Exception('Cannot load certificate from disk')
45 # Create the record without a certificate
47 record = TLSARecord(name='%s._%s.%s'%(port,protocol,hostname), usage=usage, selector=selector, mtype=mtype, cert ='')
49 record = TLSARecord(name='_%s._%s.%s'%(port,protocol,hostname), usage=usage, selector=selector, mtype=mtype, cert ='')
50 # Check if the record is valid
52 if record.selector == 0:
53 # Hash the Full certificate
54 record.cert = getHash(certificate, record.mtype)
56 # Hash only the SubjectPublicKeyInfo
57 record.cert = getHash(certificate.get_pubkey(), record.mtype)
59 record.isValid(raiseException=True)
61 if output == 'generic':
62 return record.getRecord(generic=True)
63 return record.getRecord()
65 def getA(hostname, secure=True):
66 if not check_ipv4: return []
67 """Gets a list of A records for hostname, returns a list of ARecords"""
69 records = getRecords(hostname, rrtype='A', secure=secure)
70 except InsecureLookupException, e:
73 except DNSLookupError, e:
74 print 'Unable to resolve %s: %s' % (hostname, str(e))
77 for record in records:
78 ret.append(ARecord(hostname, str(IPv4Address(int(b2a_hex(record),16)))))
81 def getAAAA(hostname, secure=True):
82 if not check_ipv6: return []
83 """Gets a list of A records for hostname, returns a list of AAAARecords"""
85 records = getRecords(hostname, rrtype='AAAA', secure=secure)
86 except InsecureLookupException, e:
89 except DNSLookupError, e:
90 print 'Unable to resolve %s: %s' % (hostname, str(e))
93 for record in records:
94 ret.append(AAAARecord(hostname, str(IPv6Address(int(b2a_hex(record),16)))))
97 def getVerificationErrorReason(num):
98 """This function returns the name of the X509 Error based on int(num)
100 # These were taken from the M2Crypto.m2 code
102 50: "X509_V_ERR_APPLICATION_VERIFICATION",
103 22: "X509_V_ERR_CERT_CHAIN_TOO_LONG",
104 10: "X509_V_ERR_CERT_HAS_EXPIRED",
105 9: "X509_V_ERR_CERT_NOT_YET_VALID",
106 28: "X509_V_ERR_CERT_REJECTED",
107 23: "X509_V_ERR_CERT_REVOKED",
108 7: "X509_V_ERR_CERT_SIGNATURE_FAILURE",
109 27: "X509_V_ERR_CERT_UNTRUSTED",
110 12: "X509_V_ERR_CRL_HAS_EXPIRED",
111 11: "X509_V_ERR_CRL_NOT_YET_VALID",
112 8: "X509_V_ERR_CRL_SIGNATURE_FAILURE",
113 18: "X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT",
114 14: "X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD",
115 13: "X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD",
116 15: "X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD",
117 16: "X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD",
118 24: "X509_V_ERR_INVALID_CA",
119 26: "X509_V_ERR_INVALID_PURPOSE",
120 17: "X509_V_ERR_OUT_OF_MEM",
121 25: "X509_V_ERR_PATH_LENGTH_EXCEEDED",
122 19: "X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN",
123 6: "X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY",
124 4: "X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE",
125 5: "X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE",
126 3: "X509_V_ERR_UNABLE_TO_GET_CRL",
127 2: "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT",
128 20: "X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
129 21: "X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE",
130 0: "X509_V_OK"}[int(num)]
132 def getRecords(hostname, rrtype='A', secure=True):
133 """Do a lookup of a name and a rrtype, returns a list of binary coded strings. Only queries for rr_class IN."""
135 ctx = unbound.ub_ctx()
136 if os.path.exists("root.key"):
137 ctx.add_ta_file('root.key')
138 elif os.path.exists("/etc/swede/root.key"):
139 ctx.add_ta_file('/etc/swede/root.key')
141 print "Cannot find root.key, please move it to /etc/swede"
144 if os.path.exists("dlv.isc.org.key"):
145 ctx.set_option("dlv-anchor-file:", "dlv.isc.org.key")
146 elif os.path.exists("/etc/swede/dlv.isc.org.key"):
147 ctx.set_option("dlv-anchor-file:", "/etc/swede/dlv.isc.org.key")
149 print "Cannot find dlv.isc.org.key, please move it to /etc/swede"
152 # Use the local cache
153 if resolvconf and os.path.isfile(resolvconf):
154 ctx.resolvconf(resolvconf)
156 if type(rrtype) == str:
157 if 'RR_TYPE_' + rrtype in dir(unbound):
158 rrtype = getattr(unbound, 'RR_TYPE_' + rrtype)
160 raise Exception('Error: unknown RR TYPE: %s.' % rrtype)
161 elif type(rrtype) != int:
162 raise Exception('Error: rrtype in wrong format, neither int nor str.')
164 status, result = ctx.resolve(hostname, rrtype=rrtype)
165 if status == 0 and result.havedata:
166 if not result.secure:
168 # The data is insecure and a secure lookup was requested
169 raise InsecureLookupException('Error: query data not secure and secure data requested, unable to continue')
171 print >> sys.stderr, 'Warning: query data is not secure.'
172 # If we are here the data was either secure or insecure data is accepted
173 return result.data.raw
175 raise DNSLookupError('Unsuccesful lookup or no data returned for rrtype %s.' % rrtype)
177 def getHash(certificate, mtype):
178 """Hashes the certificate based on the mtype.
179 The certificate should be an M2Crypto.X509.X509 object (or the result of the get_pubkey() function on said object)
181 certificate = certificate.as_der()
183 return b2a_hex(certificate)
185 return sha256(certificate).hexdigest()
187 return sha512(certificate).hexdigest()
189 raise Exception('mtype should be 0,1,2')
191 def getTLSA(hostname, port=443, protocol='tcp', secure=True):
193 This function tries to do a secure lookup of the TLSA record.
194 At the moment it requests the TYPE52 record and parses it into a 'valid' TLSA record
195 It returns a list of TLSARecord objects
197 if hostname[-1] != '.':
200 if not protocol.lower() in ['tcp', 'udp', 'sctp']:
201 raise Exception('Error: unknown protocol: %s. Should be one of tcp, udp or sctp' % protocol)
204 records = getRecords('*._%s.%s' % (protocol.lower(), hostname), rrtype=52, secure=secure)
206 records = getRecords('_%s._%s.%s' % (port, protocol.lower(), hostname), rrtype=52, secure=secure)
207 except InsecureLookupException, e:
210 except DNSLookupError, e:
211 print 'Unable to resolve %s: %s' % (hostname, str(e))
214 for record in records:
215 hexdata = b2a_hex(record)
217 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:]))
219 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:]))
222 def loadCert(certificate):
223 """Returns an M2Crypto.X509.X509 object"""
224 if isinstance(certificate, X509.X509):
225 # nothing to be done :-)
228 # Maybe we were passed a path
229 return X509.load_cert(certificate)
231 # Can't load the cert
232 raise Exception('Unable to load certificate %s.' % certificate)
234 def verifyCertMatch(record, cert):
236 Verify the certificate with the record.
237 record should be a TLSARecord and cert should be a M2Crypto.X509.X509
239 if not isinstance(cert, X509.X509):
241 if not isinstance(record, TLSARecord):
244 if record.selector == 1:
245 certhash = getHash(cert.get_pubkey(), record.mtype)
247 certhash = getHash(cert, record.mtype)
252 if certhash == record.cert:
257 def verifyCertNameWithHostName(cert, hostname, with_msg=False):
258 """Verify the name on the certificate with a hostname, we need this because we get the cert based on IP address and thusly cannot rely on M2Crypto to verify this"""
259 if not isinstance(cert, X509.X509):
261 if not isinstance(hostname, str):
264 if hostname[-1] == '.':
265 hostname = hostname[0:-1]
267 # Ugly string comparison to see if the name on the ee-cert matches with the name provided on the commandline
269 altnames_on_cert = cert.get_ext('subjectAltName').get_value()
271 altnames_on_cert = ''
272 if hostname in (str(cert.get_subject()) + altnames_on_cert):
276 print 'WARNING: Name on the certificate (Subject: %s, SubjectAltName: %s) doesn\'t match requested hostname (%s).' % (str(cert.get_subject()), altnames_on_cert, hostname)
280 """When instanciated, this class contains all the fields of a TLSA record.
282 def __init__(self, name, usage, selector, mtype, cert):
283 """name is the name of the RR in the format: /^(_\d{1,5}|\*)\._(tcp|udp|sctp)\.([a-z0-9]*\.){2,}$/
284 usage, selector and mtype should be an integer
285 cert should be a hexidecimal string representing the certificate to be matched field
288 self.rrtype = 52 # TLSA per https://www.iana.org/assignments/dns-parameters
289 self.rrclass = 1 # IN
290 self.name = str(name)
291 self.usage = int(usage)
292 self.selector = int(selector)
293 self.mtype = int(mtype)
294 self.cert = str(cert)
296 raise Exception('Invalid value passed, unable to create a TLSARecord')
298 def getRecord(self, generic=False):
299 """Returns the RR string of this TLSARecord, either in rfc (default) or generic format"""
301 return '%s IN TYPE52 \# %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)
302 return '%s IN TLSA %s %s %s %s' % (self.name, self.usage, self.selector, self.mtype, self.cert)
304 def _toHex(self, val):
305 """Helper function to create hex strings from integers"""
308 def isValid(self, raiseException=False):
309 """Check whether all fields in the TLSA record are conforming to the spec and check if the port, protocol and name are good"""
312 if not 1 <= int(self.getPort()) <= 65535:
313 err.append('Port %s not within correct range (1 <= port <= 65535)' % self.getPort())
315 if self.getPort() != '*':
316 err.append('Port %s not a number' % self.getPort())
317 if not self.usage in [0,1,2,3]:
318 err.append('Usage: invalid (%s is not one of 0, 1, 2 or 3)' % self.usage)
319 if not self.selector in [0,1]:
320 err.append('Selector: invalid (%s is not one of 0 or 1)' % self.selector)
321 if not self.mtype in [0,1,2]:
322 err.append('Matching Type: invalid (%s is not one of 0, 1 or 2)' % self.mtype)
323 if not self.isNameValid():
324 err.append('Name (%s) is not in the correct format: _portnumber._transportprotocol.hostname.dom.' % self.name)
325 # A certificate length of 0 is accepted
326 if self.mtype in [1,2] and len(self.cert) != 0:
327 if not len(self.cert) == {1:64,2:128}[self.mtype]:
328 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])
330 if not raiseException:
333 msg = 'The TLSA record is invalid.'
335 msg += '\n\t%s' % error
336 raise RecordValidityException(msg)
340 def isNameValid(self):
341 """Check if the name if in the correct format"""
342 if not re.match('^(_\d{1,5}|\*)\._(tcp|udp|sctp)\.([-a-z0-9]*\.){2,}$', self.name):
346 def getProtocol(self):
347 """Returns the protocol based on the name"""
348 return re.split('\.', self.name)[1][1:]
351 """Returns the port based on the name"""
352 if re.split('\.', self.name)[0][0] == '*':
355 return re.split('\.', self.name)[0][1:]
358 """An object representing an A Record (IPv4 address)"""
359 def __init__(self, hostname, address):
361 self.hostname = hostname
362 self.address = address
369 IPv4Address(self.address)
375 """An object representing an AAAA Record (IPv6 address)"""
376 def __init__(self, hostname, address):
378 self.hostname = hostname
379 self.address = address
386 IPv6Address(self.address)
392 class RecordValidityException(Exception):
395 class InsecureLookupException(Exception):
398 class DNSLookupError(Exception):
401 if __name__ == '__main__':
404 parser = argparse.ArgumentParser(description='Create and verify DANE records.', epilog='This tool has a few limitations')
406 subparsers = parser.add_subparsers(title='Functions', help='Available functions, see %(prog)s function -h for function-specific help')
407 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 its local certificates.')
408 parser_verify.set_defaults(function='verify')
409 parser_create = subparsers.add_parser('create', help='Create a TLSA record')
410 parser_create.set_defaults(function='create')
412 parser.add_argument('-4', dest='ipv4', action='store_true',help='use ipv4 networking only')
413 parser.add_argument('-6', dest='ipv6', action='store_true',help='use ipv6 networking only')
414 parser.add_argument('--insecure', action='store_true', default=False, help='Allow use of non-dnssec secured answers')
415 parser.add_argument('--resolvconf', metavar='/PATH/TO/RESOLV.CONF', action='store', default='', help='Use a recursive resolver from resolv.conf')
416 parser.add_argument('-v', '--version', action='version', version='%(prog)s v0.2', help='show version and exit')
417 parser.add_argument('host', metavar="hostname")
419 parser_verify.add_argument('--port', '-p', action='store', default='443', help='The port, or \'*\' where running TLS is located (default: %(default)s).')
420 parser_verify.add_argument('--protocol', action='store', choices=['tcp','udp','sctp'], default='tcp', help='The protocol the TLS service is using (default: %(default)s).')
421 parser_verify.add_argument('--only-rr', '-o', action='store_true', help='Only verify that the TLSA resource record is correct (do not check certificate)')
422 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)')
423 parser_verify.add_argument('--quiet', '-q', action='store_true', help='Only print the result of the validation')
425 parser_create.add_argument('--port', '-p', action='store', type=int, default=443, help='The port where running TLS is located (default: %(default)s).')
426 parser_create.add_argument('--protocol', action='store', choices=['tcp','udp','sctp'], default='tcp', help='The protocol the TLS service is using (default: %(default)s).')
427 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')
428 parser_create.add_argument('--output', '-o', action='store', default='generic', choices=['generic','rfc','both'], help='The type of output. Generic (RFC 3597, TYPE52), RFC (TLSA) or both (default: %(default)s).')
430 # Usage of the certificate
431 parser_create.add_argument('--usage', '-u', action='store', type=int, default=1, choices=[0,1,2,3], help='The Usage of the Certificate for Association. \'0\' for CA, \'1\' for End Entity, \'2\' for trust-anchor, \'3\' for ONLY End-Entity match (default: %(default)s).')
432 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).')
433 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).')
435 args = parser.parse_args()
438 if args.ipv4 == True and args.ipv6 == True:
439 print "Cannot have only ipv4 and only ipv6 at the same time"
441 elif args.ipv4 == True:
443 elif args.ipv6 == True:
446 if args.host[-1] != '.':
451 if os.path.isfile(args.resolvconf):
452 resolvconf = args.resolvconf
454 print >> sys.stdout, '%s is not a file. Unable to use it as resolv.conf' % args.resolvconf
459 # not operations are fun!
460 secure = not args.insecure
462 if args.function == 'verify':
463 records = getTLSA(args.host, args.port, args.protocol, secure)
464 if len(records) == 0:
467 for record in records:
469 # First, check if the first three fields have correct values.
471 print 'Received the following record for name %s:' % record.name
472 print '\tUsage:\t\t\t\t%d (%s)' % (record.usage, {0:'CA Constraint', 1:'End-Entity Constraint + chain to CA', 2:'Trust Anchor', 3:'End-Entity'}.get(record.usage, 'INVALID'))
473 print '\tSelector:\t\t\t%d (%s)' % (record.selector, {0:'Certificate', 1:'SubjectPublicKeyInfo'}.get(record.selector, 'INVALID'))
474 print '\tMatching Type:\t\t\t%d (%s)' % (record.mtype, {0:'Full Certificate', 1:'SHA-256', 2:'SHA-512'}.get(record.mtype, 'INVALID'))
475 print '\tCertificate for Association:\t%s' % record.cert
478 record.isValid(raiseException=True)
479 except RecordValidityException, e:
480 print >> sys.stderr, 'Error: %s' % str(e)
484 print 'This record is valid (well-formed).'
487 # Go to the next record
490 # When we are here, The user also wants to verify the certificates with the record
491 if args.protocol != 'tcp':
492 print >> sys.stderr, 'Only SSL over TCP is supported (sorry)'
496 print 'Attempting to verify the record with the TLS service...'
497 if check_ipv4 and check_ipv6:
498 addresses = getA(args.host, secure=secure) + getAAAA(args.host, secure=secure)
500 addresses = getA(args.host, secure=secure)
502 addresses = getAAAA(args.host, secure=secure)
504 for address in addresses:
506 print 'Got the following IP: %s' % str(address)
507 # We do the certificate handling here, as M2Crypto keeps segfaulting when we do it in a method
509 if os.path.isfile(args.ca_cert):
510 if ctx.load_verify_locations(cafile=args.ca_cert) != 1: raise Exception('No CA cert')
511 elif os.path.exists(args.ca_cert):
512 if ctx.load_verify_locations(capath=args.ca_cert) != 1: raise Exception('No CA certs')
514 print >> sys.stderr, '%s is neither a file nor a directory, unable to continue' % args.ca_cert
516 # Don't error when the verification fails in the SSL handshake
517 ctx.set_verify(SSL.verify_none, depth=9)
518 if check_ipv6 and isinstance(address, AAAARecord):
519 sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
520 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
521 elif check_ipv4 and isinstance(address, ARecord):
522 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
523 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
526 connection = SSL.Connection(ctx, sock=sock)
528 connection.connect((str(address), int(args.port)))
529 except SSL.Checker.WrongHost, e:
530 # The name on the remote cert doesn't match the hostname because we connect on IP, not hostname (as we want secure lookup)
532 except socket.error, e:
533 print 'Cannot connect to %s: %s' % (address, str(e))
535 chain = connection.get_peer_cert_chain()
536 verify_result = connection.get_verify_result()
538 # Good, now let's verify
539 if not verifyCertNameWithHostName(cert=chain[0], hostname=str(args.host), with_msg=True):
540 # The name on the cert doesn't match the hostname... we don't verify the TLSA record
541 print 'Not checking the TLSA record.'
543 if record.usage == 1: # End-host cert
545 if verifyCertMatch(record, cert):
546 if verify_result == 0: # The cert chains to a valid CA cert according to the system-certificates
547 print 'SUCCESS (Usage 1): Certificate offered by the server matches the one mentioned in the TLSA record and chains to a valid CA certificate'
549 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)
550 if pre_exit == 0: pre_exit = 2
551 if not args.quiet: print 'The matched certificate has Subject: %s' % cert.get_subject()
553 print 'FAIL: Certificate offered by the server does not match the TLSA record'
554 if pre_exit == 0: pre_exit = 2
556 elif record.usage == 0: # CA constraint
558 # Remove the first (= End-Entity cert) from the chain
561 if verifyCertMatch(record, cert):
566 if verify_result == 0:
567 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'
569 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)
570 if pre_exit == 0: pre_exit = 2
572 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'
573 if pre_exit == 0: pre_exit = 2
574 if not args.quiet: print 'The matched certificate has Subject: %s' % cert.get_subject()
576 print 'FAIL (Usage 0): No certificate in the certificate chain offered by the server matches the TLSA record'
577 if pre_exit == 0: pre_exit = 2
579 elif record.usage == 2: # Usage 2, use the cert in the record as trust anchor
580 #FIXME: doesnt comply to the spec
582 previous_issuer = None
585 if not str(previous_issuer) == str(cert.get_subject()): # The chain cannot be valid
586 print "FAIL: Certificates don't chain"
588 previous_issuer = cert.get_issuer()
589 if verifyCertMatch(record, cert):
593 print 'SUCCESS (usage 2): A certificate in the certificate chain (including the end-entity certificate) offered by the server matches the TLSA record'
594 if not args.quiet: print 'The matched certificate has Subject: %s' % cert.get_subject()
596 print 'FAIL (usage 2): No certificate in the certificate chain (including the end-entity certificate) offered by the server matches the TLSA record'
597 if pre_exit == 0: pre_exit = 2
599 elif record.usage == 3: # EE cert MUST match
600 if verifyCertMatch(record,chain[0]):
601 print 'SUCCESS (usage 3): The certificate offered by the server matches the TLSA record'
602 if not args.quiet: print 'The matched certificate has Subject: %s' % chain[0].get_subject()
604 print 'FAIL (usage 3): The certificate offered by the server does not match the TLSA record'
605 if pre_exit == 0: pre_exit = 2
607 # Cleanup, just in case
612 # END for address in addresses
613 # END for record in records
617 else: # we want to create
619 if not args.certificate:
620 if args.protocol != 'tcp':
621 print >> sys.stderr, 'Only SSL over TCP is supported (sorry)'
624 print 'No certificate specified on the commandline, attempting to retrieve it from the server %s' % (args.host)
625 connection_port = args.port
627 sys.stdout.write('The port specified on the commandline is *, please specify the port of the TLS service on %s (443): ' % args.host)
630 user_input = raw_input()
632 connection_port = 443
635 if 1 <= int(user_input) <= 65535:
636 connection_port = user_input
639 sys.stdout.write('Port %s not numerical or within correct range (1 <= port <= 65535), try again (hit enter for default 443): ' % user_input)
640 # Get the address records for the host
642 if check_ipv4 and check_ipv6:
643 addresses = getA(args.host, secure=secure) + getAAAA(args.host, secure=secure)
645 addresses = getA(args.host, secure=secure)
647 addresses = getAAAA(args.host, secure=secure)
649 except InsecureLookupException, e:
650 print >> sys.stderr, str(e)
653 for address in addresses:
654 print 'Attempting to get certificate from %s' % str(address)
655 # We do the certificate handling here, as M2Crypto keeps segfaulting when try to do stuff with the cert if we don't
657 ctx.set_verify(SSL.verify_none, depth=9)
658 if check_ipv6 and isinstance(address, AAAARecord):
659 sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM)
660 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
661 if check_ipv4 and isinstance(address, ARecord):
662 sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
663 sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
666 connection = SSL.Connection(ctx, sock=sock)
668 connection.connect((str(address), int(connection_port)))
669 except SSL.Checker.WrongHost:
671 except socket.error, e:
672 print 'Cannot connect to %s: %s' % (address, str(e))
675 chain = connection.get_peer_cert_chain()
676 for chaincert in chain:
677 if int(args.usage) == 1 or int(args.usage) == 3:
678 # The first cert is the end-entity cert
679 print 'Got a certificate with Subject: %s' % chaincert.get_subject()
683 if (int(args.usage) == 0 and chaincert.check_ca()) or int(args.usage) == 2:
684 sys.stdout.write('Got a certificate with the following Subject:\n\t%s\nUse this as certificate to match? [y/N] ' % chaincert.get_subject())
687 user_input = raw_input()
688 if user_input in ['','n','N']:
690 elif user_input in ['y', 'Y']:
694 sys.stdout.write('Please answer Y or N')
698 if cert: # Print the requested records based on the retrieved certificates
699 if args.output == 'both':
700 print genTLSA(args.host, args.protocol, args.port, cert, 'draft', args.usage, args.selector, args.mtype)
701 print genTLSA(args.host, args.protocol, args.port, cert, 'rfc', args.usage, args.selector, args.mtype)
703 print genTLSA(args.host, args.protocol, args.port, cert, args.output, args.usage, args.selector, args.mtype)
705 # Clear the cert from memory (to stop M2Crypto from segfaulting)
706 # And cleanup the connection and context
712 else: # Pass the path to the certificate to the genTLSA function
713 if args.output == 'both':
714 print genTLSA(args.host, args.protocol, args.port, args.certificate, 'draft', args.usage, args.selector, args.mtype)
715 print genTLSA(args.host, args.protocol, args.port, args.certificate, 'rfc', args.usage, args.selector, args.mtype)
717 print genTLSA(args.host, args.protocol, args.port, args.certificate, args.output, args.usage, args.selector, args.mtype)