except (ImportError, PackagingError), e:
errors.append("%s: %s" % (e.__class__.__name__, e))
+ from allmydata.util.check_pyopenssl import check_openssl_version, OpenSSLVersionError
try:
from OpenSSL import SSL
check_openssl_version(SSL)
- except PackagingError, e:
+ except OpenSSLVersionError, e:
errors.append("%s: %s" % (e.__class__.__name__, e))
except Exception, e:
errors.append("Unable to check OpenSSL version due to %s: %s" % (e.__class__.__name__, e))
if errors:
raise PackagingError(get_error_string(errors, debug=True))
-MONTHS = {'Jan': 1, 'Feb':2, 'Mar':3, 'Apr':4, 'May':5, 'Jun':6, 'Jul':7, 'Aug':8, 'Sep':9, 'Oct':10, 'Nov':11, 'Dec':12}
-
-def parse_build_date(build_date):
- day = int(build_date[0])
- month = MONTHS[build_date[1]]
- year = int(build_date[2])
- return (year, month, day)
-
-def check_openssl_version(SSL):
- openssl_version = SSL.SSLeay_version(SSL.SSLEAY_VERSION)
- split_version = openssl_version.split(' ')
-
- if len(split_version) < 2 or split_version[0] != 'OpenSSL':
- raise PackagingError("could not understand OpenSSL version string %s" % (openssl_version,))
-
- try:
- components = split_version[1].split('.')
- numeric_components = map(int, components[:2])
- if len(components) > 2:
- m = re.match(r'[0-9]*', components[2])
- numeric_components += [int(m.group(0))]
-
- if ((numeric_components == [0, 9, 8] and components[2] >= '8y') or
- (numeric_components == [1, 0, 0] and components[2] >= '0l') or
- (numeric_components == [1, 0, 1] and components[2] >= '1g') or
- (numeric_components == [1, 0, 2] and not components[2].startswith('2-beta')) or
- (numeric_components >= [1, 0, 3])):
- return
-
- if numeric_components == [1, 0, 1] and components[2] >= '1d':
- # Unfortunately, Debian and Ubuntu patched the Heartbleed bug without bumping
- # the version number or providing any other way to detect the patch status.
- # (BAD! STOP DOING THIS!) So we allow versions 1.0.1d through 1.0.1f provided
- # they were compiled on or after 6 April 2014.
- if len(split_version) >= 5 and parse_build_date(split_version[2:5]) >= (2014, 4, 6):
- return
-
- # We also allow those versions if compiled with -DOPENSSL_NO_HEARTBEATS.
- try:
- openssl_cflags = SSL.SSLeay_version(SSL.SSLEAY_CFLAGS)
- except Exception, e:
- raise PackagingError("refusing to use %s which may be vulnerable to security bugs.\n"
- "Unable to check compilation flags due to %s: %s\n"
- "Please upgrade to OpenSSL 1.0.1g or later."
- % (openssl_version, e.__class__.__name__, e))
- else:
- if '-DOPENSSL_NO_HEARTBEATS' in openssl_cflags.split(' '):
- return
- except Exception, e:
- pass
-
- raise PackagingError("refusing to use %s which may be vulnerable to security bugs.\n"
- "Please upgrade to OpenSSL 1.0.1g or later." % (openssl_version,))
-
check_all_requirements()
--- /dev/null
+#!/usr/bin/python
+
+import re
+
+
+class OpenSSLVersionError(EnvironmentError):
+ pass
+
+
+def check_openssl_version(SSL):
+ openssl_version = SSL.SSLeay_version(SSL.SSLEAY_VERSION)
+ split_version = openssl_version.split(' ')
+
+ if len(split_version) < 2 or split_version[0] != 'OpenSSL':
+ raise OpenSSLVersionError("could not understand OpenSSL version string %s" % (openssl_version,))
+
+ try:
+ components = split_version[1].split('.')
+ numeric_components = map(int, components[:2])
+ if len(components) > 2:
+ m = re.match(r'[0-9]*', components[2])
+ numeric_components += [int(m.group(0))]
+
+ #print numeric_components
+ if ((numeric_components == [0, 9, 8] and components[2] >= '8y') or
+ (numeric_components == [1, 0, 0] and components[2] >= '0l') or
+ (numeric_components == [1, 0, 1] and components[2] >= '1g') or
+ (numeric_components == [1, 0, 2] and not components[2].startswith('2-beta')) or
+ (numeric_components >= [1, 0, 3])):
+ return
+ except Exception, e:
+ #import traceback
+ #traceback.print_exc()
+ pass
+ else:
+ if numeric_components == [1, 0, 1] and components[2] >= '1d':
+ # Unfortunately, Debian and Ubuntu patched the Heartbleed bug without bumping
+ # the version number or providing any other way to detect the patch status.
+ # (BAD! STOP DOING THIS!)
+
+ # Allow versions 1.0.1d through 1.0.1f if compiled with -DOPENSSL_NO_HEARTBEATS:
+ try:
+ openssl_cflags = SSL.SSLeay_version(SSL.SSLEAY_CFLAGS)
+ except Exception, e:
+ raise OpenSSLVersionError("refusing to use %s which may be vulnerable to security bugs.\n"
+ "Unable to check compilation flags due to %s: %s\n"
+ "Please upgrade to OpenSSL 1.0.1g or later."
+ % (openssl_version, e.__class__.__name__, e))
+ else:
+ if '-DOPENSSL_NO_HEARTBEATS' in openssl_cflags.split(' '):
+ return
+
+ # Also allow these versions if a vulnerability test passes (we do this only if
+ # the version and compiler flag checks are inconclusive, to minimize the chance
+ # for the test to break or give the wrong result somehow).
+ if not is_vulnerable(SSL):
+ return
+
+ raise OpenSSLVersionError("refusing to use %s which may be vulnerable to security bugs.\n"
+ "Please upgrade to OpenSSL 1.0.1g or later." % (openssl_version,))
+
+
+# As simple as possible, but no simpler.
+_CLIENT_HELLO = (
+ '\x16' # Handshake
+ '\x03\x02' # TLS version 1.1
+ '\x00\x34' # length of ClientHello
+ '\x01' # Handshake type (ClientHello)
+ '\x00\x00\x30' # length
+ '\x03\x02' # TLS version 1.1
+ '\x53\x43\x5b\x90' # timestamp
+ '\x9d\x9b\x72\x0b\xbc\x0c\xbc\x2b\x92\xa8\x48\x97\xcf\xbd'
+ '\x39\x04\xcc\x16\x0a\x85\x03\x90\x9f\x77\x04\x33\xd4\xde' # client random
+ '\x00' # length of session ID (not resuming session)
+ '\x00\x02' # length of ciphersuites
+ '\x00\x0a' # TLS_RSA_WITH_3DES_EDE_CBC_SHA
+ '\x01' # length of compression methods
+ '\x00' # null compression
+ '\x00\x05' # length of extensions
+ '\x00\x0f\x00\x01\x01' # heartbeat extension
+)
+
+_HEARTBEAT = (
+ '\x18' # Heartbeat
+ '\x03\x02' # TLS version 1.1
+ '\x00\x03' # length
+ '\x01' # heartbeat request
+ '\x10\x00' # payload length (4096 bytes)
+)
+_HEARTBEAT2 = (
+ '\x18' # Heartbeat
+ '\x03\x02' # TLS version 1.1
+ '\x00\x23' # length
+ '\x01' # heartbeat request
+ '\x00\x01' # payload length (0 bytes)
+) + '\x00'*33
+
+def is_vulnerable(SSL):
+ def verify_callback(connection, x509, errnum, errdepth, ok):
+ return ok
+
+ ctx = SSL.Context(SSL.TLSv1_1_METHOD)
+ ctx.set_options(SSL.OP_NO_SSLv2 | SSL.OP_NO_SSLv3)
+ ctx.use_certificate_file('test.crt')
+ ctx.use_privatekey_file('test.key')
+ ctx.set_cipher_list('DES-CBC3-SHA') # TLS_RSA_WITH_3DES_EDE_CBC_SHA
+ ctx.set_verify(SSL.VERIFY_NONE, verify_callback)
+
+ server = SSL.Connection(ctx, None)
+ server.set_accept_state()
+ server.bio_write(_CLIENT_HELLO + _HEARTBEAT + _HEARTBEAT2)
+
+ server_response = bytearray()
+ try:
+ server.do_handshake()
+ except SSL.WantReadError:
+ pass # this is expected
+
+ while True:
+ try:
+ server_response += server.bio_read(32768)
+ except SSL.WantReadError:
+ break
+ print repr(server_response)
+ print len(server_response)
+
+ # Fortunately we don't need to parse anything complicated, just the outer layer.
+ i = 0
+ while i+5 <= len(server_response):
+ record_type = server_response[i]
+ # we don't care about the record version
+ record_length = (server_response[i+3]<<8) + server_response[i+4]
+ print record_type, record_length
+ if record_length == 0:
+ # avoid infinite loop
+ return True
+ if record_type == 0x18 and record_length > 3:
+ # longer than expected heartbeat response => vulnerable
+ return True
+ i += 5 + record_length
+
+ if i < len(server_response):
+ print "hmm"
+
+ return False
+
+
+if __name__ == '__main__':
+ from OpenSSL import SSL
+ #check_openssl_version(SSL)
+ #print "Not vulnerable."
+ print is_vulnerable(SSL)
+
--- /dev/null
+-----BEGIN CERTIFICATE-----
+MIIEBTCCAu2gAwIBAgIJAKZLXxbqxONXMA0GCSqGSIb3DQEBBQUAMIGYMQswCQYD
+VQQGEwJVSzEbMBkGA1UECAwSR3JlYXRlciBNYW5jaGVzdGVyMRMwEQYDVQQHDApN
+YW5jaGVzdGVyMRswGQYDVQQKDBJKYWNhcmFuZGEgU29mdHdhcmUxFjAUBgNVBAMM
+DWphY2FyYW5kYS5vcmcxIjAgBgkqhkiG9w0BCQEWE2RhaXJhQGphY2FyYW5kYS5v
+cmcwHhcNMTQwNDE5MTkzNTUyWhcNMTQwNTE5MTkzNTUyWjCBmDELMAkGA1UEBhMC
+VUsxGzAZBgNVBAgMEkdyZWF0ZXIgTWFuY2hlc3RlcjETMBEGA1UEBwwKTWFuY2hl
+c3RlcjEbMBkGA1UECgwSSmFjYXJhbmRhIFNvZnR3YXJlMRYwFAYDVQQDDA1qYWNh
+cmFuZGEub3JnMSIwIAYJKoZIhvcNAQkBFhNkYWlyYUBqYWNhcmFuZGEub3JnMIIB
+IjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAqUUgLyuUie+lmdrLfeUmkbPW
+282f7j6WRx0I0YdMQh6Qih4WvA+AY/l1rNI2o4h+vqobs8rBVxjE9jVSZYZDUbs6
+cNSfhm9VDwX5KfZeFM9XPpL6Gc2DX9Ay6ucdE17d10JYRdnyE5iaHow137SdykBM
+QAccoL4yTB+f4kKx3qE6AEXEkATc9PUloPdZ7bNkClzqjyTAFeS2Vci5ubdkrIXZ
+YNUrdrTtakJVZiu1CL2xnPnzyCxrF2/VuX//e77CBwk8qo/nFudkajoXN66bMeW0
+1YreOuu/OBvqjwb3aW+45h7/BNo87kh6brhZpswOqRJDvlnh49lpHBnj8HgJlQID
+AQABo1AwTjAdBgNVHQ4EFgQUHbuzLZ2vYHmjjntSfHw/PstZgFwwHwYDVR0jBBgw
+FoAUHbuzLZ2vYHmjjntSfHw/PstZgFwwDAYDVR0TBAUwAwEB/zANBgkqhkiG9w0B
+AQUFAAOCAQEAUxtcB0kfWQBJDw189F/+KE43zQbD2vbGkoZBCQHk200nMPXb3IIc
+entP25YyX9xJp54ZlnMho6rJKxKKWOOL+LmQr8WwwF74x8+VpsmIa09WDfFGQGMs
+NWCJaLbKi+lv/++6zvp1b8/n8G4/JgOczHh6dptKr5o/NRBEClwwgBnlpth6Mtxh
+5TehHnZKyBUIvhY5THaNgZYQ6tZ4MPawXsdlsq0umQwn9GlZKEE0XdoRKShOxZtg
+v04vmHAZHdVB53GGBnXaRVqp005I3bsdnCvgCrCKjp/VXp2SSscYuvfCjQUlMTiv
+9VDNo9Tkie2pm154E+bLADwePQhvbAqjRA==
+-----END CERTIFICATE-----
--- /dev/null
+-----BEGIN PRIVATE KEY-----
+MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCpRSAvK5SJ76WZ
+2st95SaRs9bbzZ/uPpZHHQjRh0xCHpCKHha8D4Bj+XWs0jajiH6+qhuzysFXGMT2
+NVJlhkNRuzpw1J+Gb1UPBfkp9l4Uz1c+kvoZzYNf0DLq5x0TXt3XQlhF2fITmJoe
+jDXftJ3KQExABxygvjJMH5/iQrHeoToARcSQBNz09SWg91nts2QKXOqPJMAV5LZV
+yLm5t2Sshdlg1St2tO1qQlVmK7UIvbGc+fPILGsXb9W5f/97vsIHCTyqj+cW52Rq
+Ohc3rpsx5bTVit466784G+qPBvdpb7jmHv8E2jzuSHpuuFmmzA6pEkO+WeHj2Wkc
+GePweAmVAgMBAAECggEBAJdy1tn9hxhnR+bBoWJR/dFNdAcUEm0qqHCWJMa75Fog
+WLiLNVIsGHbawxPOvb5RqP1U3BZgad5QtiGIEeHuYBsnhqFfTCAPSq53bLjDyueZ
+n/jHrmgZvdqjQHt2L0iej6RCoxJUb0hjODnuv2EZjDhikRaUhmO36O69OONLQzPp
+3mMHhSZCh8k8CpD4uEELohAPG7CohLY5dt0jizEQw/GbhqWAlSVpzc0AlOtv0Oy7
+JzEMIfpJmEvRCLzPhGlY0SxGiDQmQ6o4GBTfuxKWpb9ZA77YyXMQ2mM3lW9/SJ5s
+FSY7DWJp1z4PR71Ph0LjT/EiAmbIZ/0K+koA/OxCQp0CgYEA2C6E+Vpg/jZtyW7/
+G0YYA4Usj21bPgfN38JHgNncLbEq+LMLMmmGbE3HweEjnREBoErdyQBsUzmrFGq+
+VjxS9GTjJ/630UrVlHXl02hhgpaBRqjB5GatcuGHHzItgEFWvKC853BYPntsIMyy
+XPu2cckqRibXl/qNhzOf2QyYXccCgYEAyHKb0ZSL1f8CN0KVuWAQeQ2kgYjzfQW4
+GDkGdkBNZ0Q26kMDM64wmVjN66leo/pakwdyxJQRwha4NamVF/dUz3myNYsIYGYJ
+V12WC5gBLU9SqBfD/ARah7agJIoAlBuall7woCDqrf2wHKXxV8BHMSrVQkAANHx0
+wQiMCNUujcMCgYB/d/aaXQtB+fAETTmI6Gyybq0WqSGa0tk2rgShWlR0cLnoyRG6
+GPTVGYvGqyznqSZvUJWztlcpP6C7ujEfAhTb3D0A0TWr59dF4bqxTCPq7zms40bo
+mQ5+5bJZ16lyrZQqRxD72od92CKquGgXYahzMW2GzdJj363h4bPINKAG8wKBgAFi
+sWsIcNMA2T4SisBmRpbfnkR68tvpXzVjp4THuwE8unhyECKaUKGuuHWpjQrrHxcn
+FQcA0wKZb5qHWo142zA38iBmnp8z9VqSPghEe7WZd8PLrkIesZWAkKjP/2MGsdHa
+RCWHNLgRKdd42is2HAfd+O1lTdsPBSuUaItjFdhLAoGBANQwRy2HIxo6tTlxPeJQ
+DelNCaLWmKfMKQhG2SFLc08Au4XwxG8/wCddFK7Ohmb1HCwRr5rq4ItO5yhfomZU
+kTtRsVb4LpiI8Fb+anG6pLQPaZK/CJiEb7Dwb/6DDMKAVcdZQnBEiIBQ5ELudq0n
+t79zujWHlJ9FXZKmNkOQpyAm
+-----END PRIVATE KEY-----