]> git.rkrishnan.org Git - tahoe-lafs/tahoe-lafs.git/commitdiff
Add OpenSSL version check and tests.
authorDaira Hopwood <daira@jacaranda.org>
Mon, 1 Jun 2015 13:52:03 +0000 (14:52 +0100)
committerDaira Hopwood <daira@jacaranda.org>
Mon, 1 Jun 2015 13:52:03 +0000 (14:52 +0100)
Signed-off-by: Daira Hopwood <daira@jacaranda.org>
src/allmydata/__init__.py
src/allmydata/_auto_deps.py
src/allmydata/test/test_version.py

index 44f971f8634fd86b8f9bf53678acdb29875d3be4..9883931de7b4034633426b78e7bfce32d1289973 100644 (file)
@@ -400,9 +400,71 @@ def check_all_requirements():
         except (ImportError, PackagingError), e:
             errors.append("%s: %s" % (e.__class__.__name__, e))
 
+    try:
+        from OpenSSL import SSL
+        check_openssl_version(SSL)
+    except PackagingError, 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])):
+            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()
 
 
index 69a6081586960e71207de1e96297bfbe4d6e8a1f..eb469f6f448c88e74fdfd36bade1a8cc4c6529d9 100644 (file)
@@ -169,7 +169,8 @@ else:
 #   not *directly* depend on pyOpenSSL.
 #
 # * pyOpenSSL >= 0.13 is needed in order to avoid
-#   <https://tahoe-lafs.org/trac/tahoe-lafs/ticket/2005>.
+#   <https://tahoe-lafs.org/trac/tahoe-lafs/ticket/2005>, and also to check the
+#   version of OpenSSL that pyOpenSSL is using.
 #
 # * pyOpenSSL >= 0.14 is built on the 'cryptography' package which depends
 #   on 'cffi' (and indirectly several other packages). Unfortunately cffi
index fc64277724c0d3ae258feddfd3149169c9dcdb9e..9471016ef874a0e676fe4158185e51a1c49721b8 100644 (file)
@@ -3,12 +3,27 @@ from pkg_resources import Requirement
 
 from twisted.trial import unittest
 
-from allmydata import check_requirement, cross_check, PackagingError
+from allmydata import check_requirement, cross_check, check_openssl_version, parse_build_date, PackagingError
 from allmydata.util.verlib import NormalizedVersion as V, \
                                   IrrationalVersionError, \
                                   suggest_normalized_version as suggest
 
 
+class MockSSL(object):
+    SSLEAY_VERSION = 0
+    SSLEAY_CFLAGS = 2
+
+    def __init__(self, version, compiled_without_heartbeats=False):
+        self.opts = {
+            self.SSLEAY_VERSION: version,
+            self.SSLEAY_CFLAGS: compiled_without_heartbeats and 'compiler: gcc -DOPENSSL_NO_HEARTBEATS'
+                                                             or 'compiler: gcc',
+        }
+
+    def SSLeay_version(self, which):
+        return self.opts[which]
+
+
 class CheckRequirement(unittest.TestCase):
     def test_check_requirement(self):
         self._check_success("setuptools >= 0.6c6", {"setuptools": ("0.6", "", None)})
@@ -118,6 +133,59 @@ class CheckRequirement(unittest.TestCase):
         self.failUnlessEqual(len(res), 1)
         self.failUnlessIn("but version '2.0'", res[0])
 
+    def test_check_openssl_version(self):
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL(""))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("NotOpenSSL"))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("OpenSSL a.b.c"))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("OpenSSL 1.1.x"))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("OpenSSL 0.9"))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("OpenSSL 0.9.0"))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("OpenSSL 0.9.8"))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("OpenSSL 0.9.8", True))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("OpenSSL 0.9.8x"))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("OpenSSL 1.0.0"))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("OpenSSL 1.0.0", True))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("OpenSSL 1.0.0k"))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("OpenSSL 1.0.1"))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("OpenSSL 1.0.1e 11 Feb 2013"))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("OpenSSL 1.0.1e 5 Apr 2014"))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("OpenSSL 1.0.1e 7 Abc 2014"))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("OpenSSL 1.0.1e invalid_date"))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("OpenSSL 1.0.1e 7 Apr"))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("OpenSSL 0.10"))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("OpenSSL 0.10.0"))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("OpenSSL 1.0.0"))
+        self.failUnlessRaises(PackagingError, check_openssl_version, MockSSL("OpenSSL 1.0.0 1 Jan 2000"))
+
+        check_openssl_version(MockSSL("OpenSSL 0.9.8y"))
+        check_openssl_version(MockSSL("OpenSSL 0.9.8z"))
+        check_openssl_version(MockSSL("OpenSSL 1.0.0l"))
+        check_openssl_version(MockSSL("OpenSSL 1.0.0m"))
+        check_openssl_version(MockSSL("OpenSSL 1.0.1", True))
+        check_openssl_version(MockSSL("OpenSSL 1.0.1e 11 Feb 2013", True))
+        check_openssl_version(MockSSL("OpenSSL 1.0.1 7 Apr 2014"))
+        check_openssl_version(MockSSL("OpenSSL 1.0.1e 7 Apr 2014"))
+        check_openssl_version(MockSSL("OpenSSL 1.0.1g 1 Mar 2014"))
+        check_openssl_version(MockSSL("OpenSSL 1.0.1h 1 Jan 2015"))
+        check_openssl_version(MockSSL("OpenSSL 1.0.1zzz"))
+        check_openssl_version(MockSSL("OpenSSL 1.0.2"))
+        check_openssl_version(MockSSL("OpenSSL 1.0.2a"))
+        check_openssl_version(MockSSL("OpenSSL 1.0.10a"))
+        check_openssl_version(MockSSL("OpenSSL 1.1"))
+        check_openssl_version(MockSSL("OpenSSL 1.1.0"))
+        check_openssl_version(MockSSL("OpenSSL 1.1.0a"))
+        check_openssl_version(MockSSL("OpenSSL 1.10"))
+        check_openssl_version(MockSSL("OpenSSL 1.10.10a"))
+        check_openssl_version(MockSSL("OpenSSL 2"))
+        check_openssl_version(MockSSL("OpenSSL 2.0.0 31 Dec 2020"))
+        check_openssl_version(MockSSL("OpenSSL 10.0.0 31 Dec 2099"))
+
+        self.failUnlessEqual(parse_build_date(['1', 'Jan', '2000']), (2000, 1, 1))
+        self.failUnlessEqual(parse_build_date(['5', 'Apr', '2014']), (2014, 4, 5))
+        self.failUnlessEqual(parse_build_date(['7', 'Apr', '2014']), (2014, 4, 7))
+        self.failUnlessRaises(Exception, parse_build_date, [])
+        self.failUnlessRaises(Exception, parse_build_date, ['1', 'Abc' '2000'])
+
 
 # based on https://bitbucket.org/tarek/distutilsversion/src/17df9a7d96ef/test_verlib.py