| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  | #!./python | 
					
						
							|  |  |  | """Run Python tests against multiple installations of OpenSSL and LibreSSL
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The script | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   (1) downloads OpenSSL / LibreSSL tar bundle | 
					
						
							|  |  |  |   (2) extracts it to ./src | 
					
						
							|  |  |  |   (3) compiles OpenSSL / LibreSSL | 
					
						
							|  |  |  |   (4) installs OpenSSL / LibreSSL into ../multissl/$LIB/$VERSION/ | 
					
						
							|  |  |  |   (5) forces a recompilation of Python modules using the | 
					
						
							|  |  |  |       header and library files from ../multissl/$LIB/$VERSION/ | 
					
						
							|  |  |  |   (6) runs Python's test suite | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The script must be run with Python's build directory as current working | 
					
						
							|  |  |  | directory. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | The script uses LD_RUN_PATH, LD_LIBRARY_PATH, CPPFLAGS and LDFLAGS to bend | 
					
						
							|  |  |  | search paths for header files and shared libraries. It's known to work on | 
					
						
							|  |  |  | Linux with GCC and clang. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Please keep this script compatible with Python 2.7, and 3.4 to 3.7. | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | (c) 2013-2017 Christian Heimes <christian@python.org> | 
					
						
							|  |  |  | """
 | 
					
						
							|  |  |  | from __future__ import print_function | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import argparse | 
					
						
							|  |  |  | from datetime import datetime | 
					
						
							|  |  |  | import logging | 
					
						
							|  |  |  | import os | 
					
						
							|  |  |  | try: | 
					
						
							|  |  |  |     from urllib.request import urlopen | 
					
						
							| 
									
										
										
										
											2020-05-15 22:32:25 +02:00
										 |  |  |     from urllib.error import HTTPError | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  | except ImportError: | 
					
						
							| 
									
										
										
										
											2020-05-15 22:32:25 +02:00
										 |  |  |     from urllib2 import urlopen, HTTPError | 
					
						
							| 
									
										
										
										
											2021-04-26 10:54:12 +02:00
										 |  |  | import re | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  | import shutil | 
					
						
							| 
									
										
										
										
											2020-05-15 22:32:25 +02:00
										 |  |  | import subprocess | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  | import sys | 
					
						
							|  |  |  | import tarfile | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | log = logging.getLogger("multissl") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | OPENSSL_OLD_VERSIONS = [ | 
					
						
							| 
									
										
										
										
											2024-09-04 14:29:41 -05:00
										 |  |  |     "1.1.1w", | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  | ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | OPENSSL_RECENT_VERSIONS = [ | 
					
						
							| 
									
										
										
										
											2024-09-04 13:16:53 -05:00
										 |  |  |     "3.0.15", | 
					
						
							|  |  |  |     "3.1.7", | 
					
						
							|  |  |  |     "3.2.3", | 
					
						
							| 
									
										
										
										
											2024-09-04 14:29:41 -05:00
										 |  |  |     "3.3.2", | 
					
						
							| 
									
										
										
										
											2024-11-28 13:29:27 +01:00
										 |  |  |     "3.4.0", | 
					
						
							|  |  |  |     # See make_ssl_data.py for notes on adding a new version. | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  | ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | LIBRESSL_OLD_VERSIONS = [ | 
					
						
							|  |  |  | ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | LIBRESSL_RECENT_VERSIONS = [ | 
					
						
							|  |  |  | ] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | # store files in ../multissl | 
					
						
							| 
									
										
										
										
											2018-01-16 21:02:26 +01:00
										 |  |  | HERE = os.path.dirname(os.path.abspath(__file__)) | 
					
						
							|  |  |  | PYTHONROOT = os.path.abspath(os.path.join(HERE, '..', '..')) | 
					
						
							|  |  |  | MULTISSL_DIR = os.path.abspath(os.path.join(PYTHONROOT, '..', 'multissl')) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | parser = argparse.ArgumentParser( | 
					
						
							|  |  |  |     prog='multissl', | 
					
						
							|  |  |  |     description=( | 
					
						
							|  |  |  |         "Run CPython tests with multiple OpenSSL and LibreSSL " | 
					
						
							|  |  |  |         "versions." | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | parser.add_argument( | 
					
						
							|  |  |  |     '--debug', | 
					
						
							|  |  |  |     action='store_true', | 
					
						
							| 
									
										
										
										
											2018-05-23 22:24:45 +02:00
										 |  |  |     help="Enable debug logging", | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  | ) | 
					
						
							|  |  |  | parser.add_argument( | 
					
						
							|  |  |  |     '--disable-ancient', | 
					
						
							|  |  |  |     action='store_true', | 
					
						
							| 
									
										
										
										
											2020-05-16 03:33:05 +02:00
										 |  |  |     help="Don't test OpenSSL and LibreSSL versions without upstream support", | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  | ) | 
					
						
							|  |  |  | parser.add_argument( | 
					
						
							|  |  |  |     '--openssl', | 
					
						
							|  |  |  |     nargs='+', | 
					
						
							|  |  |  |     default=(), | 
					
						
							|  |  |  |     help=( | 
					
						
							|  |  |  |         "OpenSSL versions, defaults to '{}' (ancient: '{}') if no " | 
					
						
							|  |  |  |         "OpenSSL and LibreSSL versions are given." | 
					
						
							|  |  |  |     ).format(OPENSSL_RECENT_VERSIONS, OPENSSL_OLD_VERSIONS) | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | parser.add_argument( | 
					
						
							|  |  |  |     '--libressl', | 
					
						
							|  |  |  |     nargs='+', | 
					
						
							|  |  |  |     default=(), | 
					
						
							|  |  |  |     help=( | 
					
						
							|  |  |  |         "LibreSSL versions, defaults to '{}' (ancient: '{}') if no " | 
					
						
							|  |  |  |         "OpenSSL and LibreSSL versions are given." | 
					
						
							|  |  |  |     ).format(LIBRESSL_RECENT_VERSIONS, LIBRESSL_OLD_VERSIONS) | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | parser.add_argument( | 
					
						
							|  |  |  |     '--tests', | 
					
						
							|  |  |  |     nargs='*', | 
					
						
							|  |  |  |     default=(), | 
					
						
							|  |  |  |     help="Python tests to run, defaults to all SSL related tests.", | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | parser.add_argument( | 
					
						
							|  |  |  |     '--base-directory', | 
					
						
							|  |  |  |     default=MULTISSL_DIR, | 
					
						
							|  |  |  |     help="Base directory for OpenSSL / LibreSSL sources and builds." | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | parser.add_argument( | 
					
						
							|  |  |  |     '--no-network', | 
					
						
							|  |  |  |     action='store_false', | 
					
						
							|  |  |  |     dest='network', | 
					
						
							|  |  |  |     help="Disable network tests." | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | parser.add_argument( | 
					
						
							| 
									
										
										
										
											2018-01-16 21:02:26 +01:00
										 |  |  |     '--steps', | 
					
						
							|  |  |  |     choices=['library', 'modules', 'tests'], | 
					
						
							|  |  |  |     default='tests', | 
					
						
							|  |  |  |     help=( | 
					
						
							|  |  |  |         "Which steps to perform. 'library' downloads and compiles OpenSSL " | 
					
						
							|  |  |  |         "or LibreSSL. 'module' also compiles Python modules. 'tests' builds " | 
					
						
							|  |  |  |         "all and runs the test suite." | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  | ) | 
					
						
							| 
									
										
										
										
											2018-05-16 17:50:29 -04:00
										 |  |  | parser.add_argument( | 
					
						
							|  |  |  |     '--system', | 
					
						
							|  |  |  |     default='', | 
					
						
							|  |  |  |     help="Override the automatic system type detection." | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2018-05-23 22:24:45 +02:00
										 |  |  | parser.add_argument( | 
					
						
							|  |  |  |     '--force', | 
					
						
							|  |  |  |     action='store_true', | 
					
						
							|  |  |  |     dest='force', | 
					
						
							|  |  |  |     help="Force build and installation." | 
					
						
							|  |  |  | ) | 
					
						
							|  |  |  | parser.add_argument( | 
					
						
							|  |  |  |     '--keep-sources', | 
					
						
							|  |  |  |     action='store_true', | 
					
						
							|  |  |  |     dest='keep_sources', | 
					
						
							|  |  |  |     help="Keep original sources for debugging." | 
					
						
							|  |  |  | ) | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class AbstractBuilder(object): | 
					
						
							|  |  |  |     library = None | 
					
						
							| 
									
										
										
										
											2020-05-15 22:32:25 +02:00
										 |  |  |     url_templates = None | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |     src_template = None | 
					
						
							|  |  |  |     build_template = None | 
					
						
							| 
									
										
										
										
											2021-03-19 10:29:25 +01:00
										 |  |  |     depend_target = None | 
					
						
							| 
									
										
										
										
											2018-01-16 21:02:26 +01:00
										 |  |  |     install_target = 'install' | 
					
						
							| 
									
										
										
										
											2023-10-01 03:14:57 +02:00
										 |  |  |     if hasattr(os, 'process_cpu_count'): | 
					
						
							|  |  |  |         jobs = os.process_cpu_count() | 
					
						
							|  |  |  |     else: | 
					
						
							|  |  |  |         jobs = os.cpu_count() | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-11-22 11:50:53 +02:00
										 |  |  |     module_files = ( | 
					
						
							|  |  |  |         os.path.join(PYTHONROOT, "Modules/_ssl.c"), | 
					
						
							|  |  |  |         os.path.join(PYTHONROOT, "Modules/_hashopenssl.c"), | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |     module_libs = ("_ssl", "_hashlib") | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-16 21:02:26 +01:00
										 |  |  |     def __init__(self, version, args): | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |         self.version = version | 
					
						
							| 
									
										
										
										
											2018-01-16 21:02:26 +01:00
										 |  |  |         self.args = args | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |         # installation directory | 
					
						
							|  |  |  |         self.install_dir = os.path.join( | 
					
						
							| 
									
										
										
										
											2018-01-16 21:02:26 +01:00
										 |  |  |             os.path.join(args.base_directory, self.library.lower()), version | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |         ) | 
					
						
							|  |  |  |         # source file | 
					
						
							| 
									
										
										
										
											2018-01-16 21:02:26 +01:00
										 |  |  |         self.src_dir = os.path.join(args.base_directory, 'src') | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |         self.src_file = os.path.join( | 
					
						
							|  |  |  |             self.src_dir, self.src_template.format(version)) | 
					
						
							|  |  |  |         # build directory (removed after install) | 
					
						
							|  |  |  |         self.build_dir = os.path.join( | 
					
						
							|  |  |  |             self.src_dir, self.build_template.format(version)) | 
					
						
							| 
									
										
										
										
											2018-05-16 17:50:29 -04:00
										 |  |  |         self.system = args.system | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     def __str__(self): | 
					
						
							|  |  |  |         return "<{0.__class__.__name__} for {0.version}>".format(self) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __eq__(self, other): | 
					
						
							|  |  |  |         if not isinstance(other, AbstractBuilder): | 
					
						
							|  |  |  |             return NotImplemented | 
					
						
							|  |  |  |         return ( | 
					
						
							|  |  |  |             self.library == other.library | 
					
						
							|  |  |  |             and self.version == other.version | 
					
						
							|  |  |  |         ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def __hash__(self): | 
					
						
							|  |  |  |         return hash((self.library, self.version)) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-15 22:32:25 +02:00
										 |  |  |     @property | 
					
						
							|  |  |  |     def short_version(self): | 
					
						
							|  |  |  |         """Short version for OpenSSL download URL""" | 
					
						
							|  |  |  |         return None | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |     @property | 
					
						
							|  |  |  |     def openssl_cli(self): | 
					
						
							|  |  |  |         """openssl CLI binary""" | 
					
						
							|  |  |  |         return os.path.join(self.install_dir, "bin", "openssl") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def openssl_version(self): | 
					
						
							|  |  |  |         """output of 'bin/openssl version'""" | 
					
						
							|  |  |  |         cmd = [self.openssl_cli, "version"] | 
					
						
							|  |  |  |         return self._subprocess_output(cmd) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def pyssl_version(self): | 
					
						
							|  |  |  |         """Value of ssl.OPENSSL_VERSION""" | 
					
						
							|  |  |  |         cmd = [ | 
					
						
							|  |  |  |             sys.executable, | 
					
						
							|  |  |  |             '-c', 'import ssl; print(ssl.OPENSSL_VERSION)' | 
					
						
							|  |  |  |         ] | 
					
						
							|  |  |  |         return self._subprocess_output(cmd) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def include_dir(self): | 
					
						
							|  |  |  |         return os.path.join(self.install_dir, "include") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def lib_dir(self): | 
					
						
							|  |  |  |         return os.path.join(self.install_dir, "lib") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def has_openssl(self): | 
					
						
							|  |  |  |         return os.path.isfile(self.openssl_cli) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     @property | 
					
						
							|  |  |  |     def has_src(self): | 
					
						
							|  |  |  |         return os.path.isfile(self.src_file) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _subprocess_call(self, cmd, env=None, **kwargs): | 
					
						
							|  |  |  |         log.debug("Call '{}'".format(" ".join(cmd))) | 
					
						
							|  |  |  |         return subprocess.check_call(cmd, env=env, **kwargs) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _subprocess_output(self, cmd, env=None, **kwargs): | 
					
						
							|  |  |  |         log.debug("Call '{}'".format(" ".join(cmd))) | 
					
						
							|  |  |  |         if env is None: | 
					
						
							|  |  |  |             env = os.environ.copy() | 
					
						
							|  |  |  |             env["LD_LIBRARY_PATH"] = self.lib_dir | 
					
						
							|  |  |  |         out = subprocess.check_output(cmd, env=env, **kwargs) | 
					
						
							|  |  |  |         return out.strip().decode("utf-8") | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _download_src(self): | 
					
						
							|  |  |  |         """Download sources""" | 
					
						
							|  |  |  |         src_dir = os.path.dirname(self.src_file) | 
					
						
							|  |  |  |         if not os.path.isdir(src_dir): | 
					
						
							|  |  |  |             os.makedirs(src_dir) | 
					
						
							| 
									
										
										
										
											2020-05-15 22:32:25 +02:00
										 |  |  |         data = None | 
					
						
							|  |  |  |         for url_template in self.url_templates: | 
					
						
							|  |  |  |             url = url_template.format(v=self.version, s=self.short_version) | 
					
						
							|  |  |  |             log.info("Downloading from {}".format(url)) | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 req = urlopen(url) | 
					
						
							|  |  |  |                 # KISS, read all, write all | 
					
						
							|  |  |  |                 data = req.read() | 
					
						
							|  |  |  |             except HTTPError as e: | 
					
						
							|  |  |  |                 log.error( | 
					
						
							|  |  |  |                     "Download from {} has from failed: {}".format(url, e) | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 log.info("Successfully downloaded from {}".format(url)) | 
					
						
							|  |  |  |                 break | 
					
						
							|  |  |  |         if data is None: | 
					
						
							|  |  |  |             raise ValueError("All download URLs have failed") | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |         log.info("Storing {}".format(self.src_file)) | 
					
						
							|  |  |  |         with open(self.src_file, "wb") as f: | 
					
						
							|  |  |  |             f.write(data) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def _unpack_src(self): | 
					
						
							|  |  |  |         """Unpack tar.gz bundle""" | 
					
						
							|  |  |  |         # cleanup | 
					
						
							|  |  |  |         if os.path.isdir(self.build_dir): | 
					
						
							|  |  |  |             shutil.rmtree(self.build_dir) | 
					
						
							|  |  |  |         os.makedirs(self.build_dir) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         tf = tarfile.open(self.src_file) | 
					
						
							|  |  |  |         name = self.build_template.format(self.version) | 
					
						
							|  |  |  |         base = name + '/' | 
					
						
							|  |  |  |         # force extraction into build dir | 
					
						
							|  |  |  |         members = tf.getmembers() | 
					
						
							|  |  |  |         for member in list(members): | 
					
						
							|  |  |  |             if member.name == name: | 
					
						
							|  |  |  |                 members.remove(member) | 
					
						
							|  |  |  |             elif not member.name.startswith(base): | 
					
						
							|  |  |  |                 raise ValueError(member.name, base) | 
					
						
							|  |  |  |             member.name = member.name[len(base):].lstrip('/') | 
					
						
							|  |  |  |         log.info("Unpacking files to {}".format(self.build_dir)) | 
					
						
							|  |  |  |         tf.extractall(self.build_dir, members) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 16:30:12 +02:00
										 |  |  |     def _build_src(self, config_args=()): | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |         """Now build openssl""" | 
					
						
							|  |  |  |         log.info("Running build in {}".format(self.build_dir)) | 
					
						
							|  |  |  |         cwd = self.build_dir | 
					
						
							| 
									
										
										
										
											2018-05-23 22:24:45 +02:00
										 |  |  |         cmd = [ | 
					
						
							| 
									
										
										
										
											2021-05-06 16:30:12 +02:00
										 |  |  |             "./config", *config_args, | 
					
						
							| 
									
										
										
										
											2018-05-23 22:24:45 +02:00
										 |  |  |             "shared", "--debug", | 
					
						
							|  |  |  |             "--prefix={}".format(self.install_dir) | 
					
						
							|  |  |  |         ] | 
					
						
							| 
									
										
										
										
											2020-06-01 08:58:14 +02:00
										 |  |  |         # cmd.extend(["no-deprecated", "--api=1.1.0"]) | 
					
						
							| 
									
										
										
										
											2018-05-23 22:24:45 +02:00
										 |  |  |         env = os.environ.copy() | 
					
						
							|  |  |  |         # set rpath | 
					
						
							|  |  |  |         env["LD_RUN_PATH"] = self.lib_dir | 
					
						
							| 
									
										
										
										
											2018-05-16 17:50:29 -04:00
										 |  |  |         if self.system: | 
					
						
							|  |  |  |             env['SYSTEM'] = self.system | 
					
						
							|  |  |  |         self._subprocess_call(cmd, cwd=cwd, env=env) | 
					
						
							| 
									
										
										
										
											2021-03-19 10:29:25 +01:00
										 |  |  |         if self.depend_target: | 
					
						
							|  |  |  |             self._subprocess_call( | 
					
						
							|  |  |  |                 ["make", "-j1", self.depend_target], cwd=cwd, env=env | 
					
						
							|  |  |  |             ) | 
					
						
							|  |  |  |         self._subprocess_call(["make", f"-j{self.jobs}"], cwd=cwd, env=env) | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-05-23 22:24:45 +02:00
										 |  |  |     def _make_install(self): | 
					
						
							| 
									
										
										
										
											2018-01-16 21:02:26 +01:00
										 |  |  |         self._subprocess_call( | 
					
						
							|  |  |  |             ["make", "-j1", self.install_target], | 
					
						
							|  |  |  |             cwd=self.build_dir | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2020-05-15 18:48:25 +02:00
										 |  |  |         self._post_install() | 
					
						
							| 
									
										
										
										
											2018-05-23 22:24:45 +02:00
										 |  |  |         if not self.args.keep_sources: | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |             shutil.rmtree(self.build_dir) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-15 18:48:25 +02:00
										 |  |  |     def _post_install(self): | 
					
						
							|  |  |  |         pass | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |     def install(self): | 
					
						
							|  |  |  |         log.info(self.openssl_cli) | 
					
						
							| 
									
										
										
										
											2018-05-23 22:24:45 +02:00
										 |  |  |         if not self.has_openssl or self.args.force: | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |             if not self.has_src: | 
					
						
							|  |  |  |                 self._download_src() | 
					
						
							|  |  |  |             else: | 
					
						
							|  |  |  |                 log.debug("Already has src {}".format(self.src_file)) | 
					
						
							|  |  |  |             self._unpack_src() | 
					
						
							|  |  |  |             self._build_src() | 
					
						
							|  |  |  |             self._make_install() | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             log.info("Already has installation {}".format(self.install_dir)) | 
					
						
							|  |  |  |         # validate installation | 
					
						
							|  |  |  |         version = self.openssl_version | 
					
						
							|  |  |  |         if self.version not in version: | 
					
						
							|  |  |  |             raise ValueError(version) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def recompile_pymods(self): | 
					
						
							|  |  |  |         log.warning("Using build from {}".format(self.build_dir)) | 
					
						
							|  |  |  |         # force a rebuild of all modules that use OpenSSL APIs | 
					
						
							|  |  |  |         for fname in self.module_files: | 
					
						
							|  |  |  |             os.utime(fname, None) | 
					
						
							|  |  |  |         # remove all build artefacts | 
					
						
							|  |  |  |         for root, dirs, files in os.walk('build'): | 
					
						
							|  |  |  |             for filename in files: | 
					
						
							|  |  |  |                 if filename.startswith(self.module_libs): | 
					
						
							|  |  |  |                     os.unlink(os.path.join(root, filename)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         # overwrite header and library search paths | 
					
						
							|  |  |  |         env = os.environ.copy() | 
					
						
							|  |  |  |         env["CPPFLAGS"] = "-I{}".format(self.include_dir) | 
					
						
							|  |  |  |         env["LDFLAGS"] = "-L{}".format(self.lib_dir) | 
					
						
							|  |  |  |         # set rpath | 
					
						
							|  |  |  |         env["LD_RUN_PATH"] = self.lib_dir | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |         log.info("Rebuilding Python modules") | 
					
						
							| 
									
										
										
										
											2022-07-14 09:51:49 +02:00
										 |  |  |         cmd = ["make", "sharedmods", "checksharedmods"] | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |         self._subprocess_call(cmd, env=env) | 
					
						
							|  |  |  |         self.check_imports() | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def check_imports(self): | 
					
						
							|  |  |  |         cmd = [sys.executable, "-c", "import _ssl; import _hashlib"] | 
					
						
							|  |  |  |         self._subprocess_call(cmd) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def check_pyssl(self): | 
					
						
							|  |  |  |         version = self.pyssl_version | 
					
						
							|  |  |  |         if self.version not in version: | 
					
						
							|  |  |  |             raise ValueError(version) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     def run_python_tests(self, tests, network=True): | 
					
						
							|  |  |  |         if not tests: | 
					
						
							| 
									
										
										
										
											2021-11-22 11:50:53 +02:00
										 |  |  |             cmd = [ | 
					
						
							|  |  |  |                 sys.executable, | 
					
						
							|  |  |  |                 os.path.join(PYTHONROOT, 'Lib/test/ssltests.py'), | 
					
						
							|  |  |  |                 '-j0' | 
					
						
							|  |  |  |             ] | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |         elif sys.version_info < (3, 3): | 
					
						
							|  |  |  |             cmd = [sys.executable, '-m', 'test.regrtest'] | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             cmd = [sys.executable, '-m', 'test', '-j0'] | 
					
						
							|  |  |  |         if network: | 
					
						
							|  |  |  |             cmd.extend(['-u', 'network', '-u', 'urlfetch']) | 
					
						
							|  |  |  |         cmd.extend(['-w', '-r']) | 
					
						
							|  |  |  |         cmd.extend(tests) | 
					
						
							|  |  |  |         self._subprocess_call(cmd, stdout=None) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | class BuildOpenSSL(AbstractBuilder): | 
					
						
							|  |  |  |     library = "OpenSSL" | 
					
						
							| 
									
										
										
										
											2020-05-15 22:32:25 +02:00
										 |  |  |     url_templates = ( | 
					
						
							| 
									
										
										
										
											2024-09-04 13:16:53 -05:00
										 |  |  |         "https://github.com/openssl/openssl/releases/download/openssl-{v}/openssl-{v}.tar.gz", | 
					
						
							| 
									
										
										
										
											2020-05-15 22:32:25 +02:00
										 |  |  |         "https://www.openssl.org/source/openssl-{v}.tar.gz", | 
					
						
							|  |  |  |         "https://www.openssl.org/source/old/{s}/openssl-{v}.tar.gz" | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |     src_template = "openssl-{}.tar.gz" | 
					
						
							|  |  |  |     build_template = "openssl-{}" | 
					
						
							| 
									
										
										
										
											2018-01-16 21:02:26 +01:00
										 |  |  |     # only install software, skip docs | 
					
						
							|  |  |  |     install_target = 'install_sw' | 
					
						
							| 
									
										
										
										
											2021-03-19 10:29:25 +01:00
										 |  |  |     depend_target = 'depend' | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-15 18:48:25 +02:00
										 |  |  |     def _post_install(self): | 
					
						
							| 
									
										
										
										
											2022-12-24 04:04:20 +02:00
										 |  |  |         if self.version.startswith("3."): | 
					
						
							|  |  |  |             self._post_install_3xx() | 
					
						
							| 
									
										
										
										
											2020-05-15 18:48:25 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2021-05-06 16:30:12 +02:00
										 |  |  |     def _build_src(self, config_args=()): | 
					
						
							| 
									
										
										
										
											2022-12-24 04:04:20 +02:00
										 |  |  |         if self.version.startswith("3."): | 
					
						
							| 
									
										
										
										
											2021-05-06 16:30:12 +02:00
										 |  |  |             config_args += ("enable-fips",) | 
					
						
							|  |  |  |         super()._build_src(config_args) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2022-12-24 04:04:20 +02:00
										 |  |  |     def _post_install_3xx(self): | 
					
						
							| 
									
										
										
										
											2020-05-15 18:48:25 +02:00
										 |  |  |         # create ssl/ subdir with example configs | 
					
						
							|  |  |  |         # Install FIPS module | 
					
						
							|  |  |  |         self._subprocess_call( | 
					
						
							| 
									
										
										
										
											2021-05-06 16:30:12 +02:00
										 |  |  |             ["make", "-j1", "install_ssldirs", "install_fips"], | 
					
						
							|  |  |  |             cwd=self.build_dir | 
					
						
							| 
									
										
										
										
											2020-05-15 18:48:25 +02:00
										 |  |  |         ) | 
					
						
							| 
									
										
										
										
											2021-09-07 20:04:55 +03:00
										 |  |  |         if not os.path.isdir(self.lib_dir): | 
					
						
							|  |  |  |             # 3.0.0-beta2 uses lib64 on 64 bit platforms | 
					
						
							|  |  |  |             lib64 = self.lib_dir + "64" | 
					
						
							|  |  |  |             os.symlink(lib64, self.lib_dir) | 
					
						
							| 
									
										
										
										
											2021-05-06 16:30:12 +02:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-05-15 22:32:25 +02:00
										 |  |  |     @property | 
					
						
							|  |  |  |     def short_version(self): | 
					
						
							|  |  |  |         """Short version for OpenSSL download URL""" | 
					
						
							| 
									
										
										
										
											2021-04-26 10:54:12 +02:00
										 |  |  |         mo = re.search(r"^(\d+)\.(\d+)\.(\d+)", self.version) | 
					
						
							|  |  |  |         parsed = tuple(int(m) for m in mo.groups()) | 
					
						
							|  |  |  |         if parsed < (1, 0, 0): | 
					
						
							|  |  |  |             return "0.9.x" | 
					
						
							|  |  |  |         if parsed >= (3, 0, 0): | 
					
						
							|  |  |  |             # OpenSSL 3.0.0 -> /old/3.0/ | 
					
						
							|  |  |  |             parsed = parsed[:2] | 
					
						
							|  |  |  |         return ".".join(str(i) for i in parsed) | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2024-09-04 13:16:53 -05:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  | class BuildLibreSSL(AbstractBuilder): | 
					
						
							|  |  |  |     library = "LibreSSL" | 
					
						
							| 
									
										
										
										
											2020-05-15 22:32:25 +02:00
										 |  |  |     url_templates = ( | 
					
						
							|  |  |  |         "https://ftp.openbsd.org/pub/OpenBSD/LibreSSL/libressl-{v}.tar.gz", | 
					
						
							|  |  |  |     ) | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |     src_template = "libressl-{}.tar.gz" | 
					
						
							|  |  |  |     build_template = "libressl-{}" | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def configure_make(): | 
					
						
							|  |  |  |     if not os.path.isfile('Makefile'): | 
					
						
							|  |  |  |         log.info('Running ./configure') | 
					
						
							|  |  |  |         subprocess.check_call([ | 
					
						
							|  |  |  |             './configure', '--config-cache', '--quiet', | 
					
						
							|  |  |  |             '--with-pydebug' | 
					
						
							|  |  |  |         ]) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     log.info('Running make') | 
					
						
							|  |  |  |     subprocess.check_call(['make', '--quiet']) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | def main(): | 
					
						
							|  |  |  |     args = parser.parse_args() | 
					
						
							|  |  |  |     if not args.openssl and not args.libressl: | 
					
						
							|  |  |  |         args.openssl = list(OPENSSL_RECENT_VERSIONS) | 
					
						
							|  |  |  |         args.libressl = list(LIBRESSL_RECENT_VERSIONS) | 
					
						
							|  |  |  |         if not args.disable_ancient: | 
					
						
							|  |  |  |             args.openssl.extend(OPENSSL_OLD_VERSIONS) | 
					
						
							|  |  |  |             args.libressl.extend(LIBRESSL_OLD_VERSIONS) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     logging.basicConfig( | 
					
						
							|  |  |  |         level=logging.DEBUG if args.debug else logging.INFO, | 
					
						
							|  |  |  |         format="*** %(levelname)s %(message)s" | 
					
						
							|  |  |  |     ) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     start = datetime.now() | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-16 21:02:26 +01:00
										 |  |  |     if args.steps in {'modules', 'tests'}: | 
					
						
							| 
									
										
										
										
											2022-07-14 09:51:49 +02:00
										 |  |  |         for name in ['Makefile.pre.in', 'Modules/_ssl.c']: | 
					
						
							| 
									
										
										
										
											2018-01-16 21:02:26 +01:00
										 |  |  |             if not os.path.isfile(os.path.join(PYTHONROOT, name)): | 
					
						
							|  |  |  |                 parser.error( | 
					
						
							|  |  |  |                     "Must be executed from CPython build dir" | 
					
						
							|  |  |  |                 ) | 
					
						
							|  |  |  |         if not os.path.samefile('python', sys.executable): | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |             parser.error( | 
					
						
							| 
									
										
										
										
											2018-01-16 21:02:26 +01:00
										 |  |  |                 "Must be executed with ./python from CPython build dir" | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |             ) | 
					
						
							| 
									
										
										
										
											2018-01-16 21:02:26 +01:00
										 |  |  |         # check for configure and run make | 
					
						
							|  |  |  |         configure_make() | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     # download and register builder | 
					
						
							|  |  |  |     builds = [] | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for version in args.openssl: | 
					
						
							| 
									
										
										
										
											2018-01-16 21:02:26 +01:00
										 |  |  |         build = BuildOpenSSL( | 
					
						
							|  |  |  |             version, | 
					
						
							|  |  |  |             args | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |         build.install() | 
					
						
							|  |  |  |         builds.append(build) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     for version in args.libressl: | 
					
						
							| 
									
										
										
										
											2018-01-16 21:02:26 +01:00
										 |  |  |         build = BuildLibreSSL( | 
					
						
							|  |  |  |             version, | 
					
						
							|  |  |  |             args | 
					
						
							|  |  |  |         ) | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |         build.install() | 
					
						
							|  |  |  |         builds.append(build) | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2018-01-16 21:02:26 +01:00
										 |  |  |     if args.steps in {'modules', 'tests'}: | 
					
						
							|  |  |  |         for build in builds: | 
					
						
							|  |  |  |             try: | 
					
						
							|  |  |  |                 build.recompile_pymods() | 
					
						
							|  |  |  |                 build.check_pyssl() | 
					
						
							|  |  |  |                 if args.steps == 'tests': | 
					
						
							|  |  |  |                     build.run_python_tests( | 
					
						
							|  |  |  |                         tests=args.tests, | 
					
						
							|  |  |  |                         network=args.network, | 
					
						
							|  |  |  |                     ) | 
					
						
							|  |  |  |             except Exception as e: | 
					
						
							|  |  |  |                 log.exception("%s failed", build) | 
					
						
							|  |  |  |                 print("{} failed: {}".format(build, e), file=sys.stderr) | 
					
						
							|  |  |  |                 sys.exit(2) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |     log.info("\n{} finished in {}".format( | 
					
						
							|  |  |  |             args.steps.capitalize(), | 
					
						
							|  |  |  |             datetime.now() - start | 
					
						
							|  |  |  |         )) | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  |     print('Python: ', sys.version) | 
					
						
							| 
									
										
										
										
											2018-01-16 21:02:26 +01:00
										 |  |  |     if args.steps == 'tests': | 
					
						
							|  |  |  |         if args.tests: | 
					
						
							|  |  |  |             print('Executed Tests:', ' '.join(args.tests)) | 
					
						
							|  |  |  |         else: | 
					
						
							|  |  |  |             print('Executed all SSL tests.') | 
					
						
							| 
									
										
										
										
											2017-09-06 18:59:22 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  |     print('OpenSSL / LibreSSL versions:') | 
					
						
							|  |  |  |     for build in builds: | 
					
						
							|  |  |  |         print("    * {0.library} {0.version}".format(build)) | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | if __name__ == "__main__": | 
					
						
							|  |  |  |     main() |