don't hard-code the path to the python interpreter
[tls-check.git] / tls-check
index f757bdbf535acf718e466315228aa0bc90193d7b..04c911f7d71055f91610abaffd3b2793b39ecb0b 100755 (executable)
--- a/tls-check
+++ b/tls-check
@@ -1,4 +1,4 @@
-#!/usr/bin/python3
+#!/usr/bin/env python3
 import subprocess, sys, argparse, time, re
 from collections import OrderedDict, namedtuple
 from enum import Enum
 import subprocess, sys, argparse, time, re
 from collections import OrderedDict, namedtuple
 from enum import Enum
@@ -59,7 +59,7 @@ def test_cipher(host, port, protocol, cipher = None, wait_time=0, options=[]):
     try:
         if cipher is not None:
             options = ["-cipher", cipher]+options
     try:
         if cipher is not None:
             options = ["-cipher", cipher]+options
-        subprocess.check_call(["openssl", "s_client", "-"+protocol, "-connect", host+":"+str(port)]+options,
+        subprocess.check_call(["openssl", "s_client", "-"+protocol, "-connect", host+":"+str(port), "-servername", host]+options,
                               stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
     except subprocess.CalledProcessError:
         return False
                               stdin=subprocess.DEVNULL, stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
     except subprocess.CalledProcessError:
         return False
@@ -118,10 +118,14 @@ class CipherPropsProvider:
         self.high = set(list_ciphers("HIGH"))
         self.props = {}
     
         self.high = set(list_ciphers("HIGH"))
         self.props = {}
     
-    def __getProps(self, cipher):
+    def getProps(self, protocol, cipher):
+        # strip the sub-version-number from the protocol
+        pos = protocol.find('_')
+        if pos >= 0:
+            protocol = protocol[:pos]
         # as OpenSSL about this cipher
         # as OpenSSL about this cipher
-        cipherInfo = subprocess.check_output(["openssl", "ciphers", "-v", cipher]).decode('UTF-8').strip()
-        assert '\n' not in cipherInfo
+        cipherInfo = subprocess.check_output(["openssl", "ciphers", "-v", "-"+protocol, cipher]).decode('UTF-8').strip()
+        assert '\n' not in cipherInfo, "Cipher "+cipher+" produced unexpected output:\n"+cipherInfo
         cipherInfoFields = cipherInfo.split()
         # get # of bits
         encMatch = re.match(r'^Enc=([0-9A-Za-z]+)\(([0-9]+)\)$', cipherInfoFields[4])
         cipherInfoFields = cipherInfo.split()
         # get # of bits
         encMatch = re.match(r'^Enc=([0-9A-Za-z]+)\(([0-9]+)\)$', cipherInfoFields[4])
@@ -143,7 +147,7 @@ class CipherPropsProvider:
         isLow = cipher in self.low
         isMedium = cipher in self.medium
         isHigh = cipher in self.high
         isLow = cipher in self.low
         isMedium = cipher in self.medium
         isHigh = cipher in self.high
-        assert isExp+isLow+isMedium+isHigh <= 1, "Cipher is more than one from EXP, LOW, MEDIUM, HIGH"
+        assert isExp+isLow+isMedium+isHigh <= 1, "Cipher "+cipher+" is more than one from EXP, LOW, MEDIUM, HIGH"
         if isExp:
             strength = CipherStrength.exp
         elif isLow:
         if isExp:
             strength = CipherStrength.exp
         elif isLow:
@@ -156,19 +160,12 @@ class CipherPropsProvider:
             strength = CipherStrength.unknown
         # done!
         return CipherProps(bits=bits, strength=strength, isPfs=isPfs)
             strength = CipherStrength.unknown
         # done!
         return CipherProps(bits=bits, strength=strength, isPfs=isPfs)
-    
-    def getProps(self, cipher):
-        if cipher in self.props:
-            return self.props[cipher]
-        props = self.__getProps(cipher)
-        self.props[cipher] = props
-        return props
 
 # main program
 if __name__ == "__main__":
     parser = argparse.ArgumentParser(description='Check TLS ciphers supported by a host')
     parser.add_argument("--starttls", dest="starttls",
 
 # main program
 if __name__ == "__main__":
     parser = argparse.ArgumentParser(description='Check TLS ciphers supported by a host')
     parser.add_argument("--starttls", dest="starttls",
-                        help="Use a STARTTLS variant to establish the TLS connection. Possible values include smpt, imap, xmpp.")
+                        help="Use a STARTTLS variant to establish the TLS connection. Possible values include smtp, imap.")
     parser.add_argument("--wait-time", "-t", dest="wait_time", default="10",
                         help="Time (in ms) to wait between two connections to the server. Default is 10ms.")
     parser.add_argument("host", metavar='HOST[:PORT]',
     parser.add_argument("--wait-time", "-t", dest="wait_time", default="10",
                         help="Time (in ms) to wait between two connections to the server. Default is 10ms.")
     parser.add_argument("host", metavar='HOST[:PORT]',
@@ -200,7 +197,7 @@ if __name__ == "__main__":
         else:
             for cipher, supported in ciphers.items():
                 if supported:
         else:
             for cipher, supported in ciphers.items():
                 if supported:
-                    cipherProps = propsProvider.getProps(cipher)
+                    cipherProps = propsProvider.getProps(protocol, cipher)
                     fsText = ConsoleFormat.color("FS", ConsoleFormat.GREEN) if cipherProps.isPfs else ConsoleFormat.color("no FS", ConsoleFormat.RED)
                     bitColor = ConsoleFormat.GREEN if cipherProps.bits >= 128 else (ConsoleFormat.YELLOW if cipherProps.bits >= 100 else ConsoleFormat.RED)
                     print("    {0} ({1}, {2}, {3})".format(cipher.ljust(STATE_WIDTH), cipherProps.strength.colorName(), ConsoleFormat.color(str(cipherProps.bits)+" bits", bitColor), fsText))
                     fsText = ConsoleFormat.color("FS", ConsoleFormat.GREEN) if cipherProps.isPfs else ConsoleFormat.color("no FS", ConsoleFormat.RED)
                     bitColor = ConsoleFormat.GREEN if cipherProps.bits >= 128 else (ConsoleFormat.YELLOW if cipherProps.bits >= 100 else ConsoleFormat.RED)
                     print("    {0} ({1}, {2}, {3})".format(cipher.ljust(STATE_WIDTH), cipherProps.strength.colorName(), ConsoleFormat.color(str(cipherProps.bits)+" bits", bitColor), fsText))