|
|
862c849 |
From 96541c0e3f55b233ac9eaf8710235fa40057f977 Mon Sep 17 00:00:00 2001
|
|
|
862c849 |
From: Patrick Uiterwijk <puiterwijk@redhat.com>
|
|
|
862c849 |
Date: Wed, 2 Nov 2016 01:59:25 +0000
|
|
|
862c849 |
Subject: [PATCH] Fix adding the same trusted certificates multiple times
|
|
|
862c849 |
|
|
|
862c849 |
If there is an intermittent network failure, we might be trying to reach
|
|
|
862c849 |
an URL multiple times. Without this patch, we would be re-adding the same
|
|
|
862c849 |
certificate to the OpenSSL default context multiple times.
|
|
|
862c849 |
Normally, this is no big issue, as OpenSSL will just silently ignore them,
|
|
|
862c849 |
after registering the error in its own error stack.
|
|
|
862c849 |
However, when python-cryptography initializes, it verifies that the current
|
|
|
862c849 |
error stack of the default OpenSSL context is empty, which it no longer is
|
|
|
862c849 |
due to us adding the certificates multiple times.
|
|
|
862c849 |
This results in cryptography throwing an Unknown OpenSSL Error with details:
|
|
|
862c849 |
|
|
|
862c849 |
OpenSSLErrorWithText(code=185057381L, lib=11, func=124, reason=101,
|
|
|
862c849 |
reason_text='error:0B07C065:x509 certificate routines:X509_STORE_add_cert:cert already in hash table'),
|
|
|
862c849 |
|
|
|
862c849 |
Signed-off-by: Patrick Uiterwijk <puiterwijk@redhat.com>
|
|
|
862c849 |
---
|
|
|
862c849 |
lib/ansible/module_utils/urls.py | 35 ++++++++++++++++++++++++++++-------
|
|
|
862c849 |
1 file changed, 28 insertions(+), 7 deletions(-)
|
|
|
862c849 |
|
|
|
862c849 |
diff --git a/lib/ansible/module_utils/urls.py b/lib/ansible/module_utils/urls.py
|
|
|
862c849 |
index bef950f..c4a13bf 100644
|
|
|
862c849 |
--- a/lib/ansible/module_utils/urls.py
|
|
|
862c849 |
+++ b/lib/ansible/module_utils/urls.py
|
|
|
862c849 |
@@ -182,6 +182,8 @@
|
|
|
862c849 |
del libssl
|
|
|
862c849 |
|
|
|
862c849 |
|
|
|
862c849 |
+LOADED_VERIFY_LOCATIONS = set()
|
|
|
862c849 |
+
|
|
|
862c849 |
HAS_MATCH_HOSTNAME = True
|
|
|
862c849 |
try:
|
|
|
862c849 |
from ssl import match_hostname, CertificateError
|
|
|
862c849 |
@@ -590,6 +592,8 @@ def get_ca_certs(self):
|
|
|
862c849 |
paths_checked.append('/etc/ansible')
|
|
|
862c849 |
|
|
|
862c849 |
tmp_fd, tmp_path = tempfile.mkstemp()
|
|
|
862c849 |
+ to_add_fd, to_add_path = tempfile.mkstemp()
|
|
|
862c849 |
+ to_add = False
|
|
|
862c849 |
|
|
|
862c849 |
# Write the dummy ca cert if we are running on Mac OS X
|
|
|
862c849 |
if system == 'Darwin':
|
|
|
862c849 |
@@ -608,13 +612,21 @@ def get_ca_certs(self):
|
|
|
862c849 |
if os.path.isfile(full_path) and os.path.splitext(f)[1] in ('.crt','.pem'):
|
|
|
862c849 |
try:
|
|
|
862c849 |
cert_file = open(full_path, 'rb')
|
|
|
862c849 |
- os.write(tmp_fd, cert_file.read())
|
|
|
862c849 |
- os.write(tmp_fd, b('\n'))
|
|
|
862c849 |
+ cert = cert_file.read()
|
|
|
862c849 |
cert_file.close()
|
|
|
862c849 |
+ os.write(tmp_fd, cert)
|
|
|
862c849 |
+ os.write(tmp_fd, b('\n'))
|
|
|
862c849 |
+ if full_path not in LOADED_VERIFY_LOCATIONS:
|
|
|
862c849 |
+ to_add = True
|
|
|
862c849 |
+ os.write(to_add_fd, cert)
|
|
|
862c849 |
+ os.write(to_add_fd, b('\n'))
|
|
|
862c849 |
+ LOADED_VERIFY_LOCATIONS.add(full_path)
|
|
|
862c849 |
except (OSError, IOError):
|
|
|
862c849 |
pass
|
|
|
862c849 |
|
|
|
862c849 |
- return (tmp_path, paths_checked)
|
|
|
862c849 |
+ if not to_add:
|
|
|
862c849 |
+ to_add_path = None
|
|
|
862c849 |
+ return (tmp_path, to_add_path, paths_checked)
|
|
|
862c849 |
|
|
|
862c849 |
def validate_proxy_response(self, response, valid_codes=[200]):
|
|
|
862c849 |
'''
|
|
|
862c849 |
@@ -643,17 +655,18 @@ def detect_no_proxy(self, url):
|
|
|
862c849 |
return False
|
|
|
862c849 |
return True
|
|
|
862c849 |
|
|
|
862c849 |
- def _make_context(self, tmp_ca_cert_path):
|
|
|
862c849 |
+ def _make_context(self, to_add_ca_cert_path):
|
|
|
862c849 |
context = create_default_context()
|
|
|
862c849 |
- context.load_verify_locations(tmp_ca_cert_path)
|
|
|
862c849 |
+ if to_add_ca_cert_path:
|
|
|
862c849 |
+ context.load_verify_locations(to_add_ca_cert_path)
|
|
|
862c849 |
return context
|
|
|
862c849 |
|
|
|
862c849 |
def http_request(self, req):
|
|
|
862c849 |
- tmp_ca_cert_path, paths_checked = self.get_ca_certs()
|
|
|
862c849 |
+ tmp_ca_cert_path, to_add_ca_cert_path, paths_checked = self.get_ca_certs()
|
|
|
862c849 |
https_proxy = os.environ.get('https_proxy')
|
|
|
862c849 |
context = None
|
|
|
862c849 |
if HAS_SSLCONTEXT:
|
|
|
862c849 |
- context = self._make_context(tmp_ca_cert_path)
|
|
|
862c849 |
+ context = self._make_context(to_add_ca_cert_path)
|
|
|
862c849 |
|
|
|
862c849 |
# Detect if 'no_proxy' environment variable is set and if our URL is included
|
|
|
862c849 |
use_proxy = self.detect_no_proxy(req.get_full_url())
|
|
|
862c849 |
@@ -719,6 +732,14 @@ def http_request(self, req):
|
|
|
862c849 |
except:
|
|
|
862c849 |
pass
|
|
|
862c849 |
|
|
|
862c849 |
+ try:
|
|
|
862c849 |
+ # cleanup the temp file created, don't worry
|
|
|
862c849 |
+ # if it fails for some reason
|
|
|
862c849 |
+ if to_add_ca_cert_path:
|
|
|
862c849 |
+ os.remove(to_add_ca_cert_path)
|
|
|
862c849 |
+ except:
|
|
|
862c849 |
+ pass
|
|
|
862c849 |
+
|
|
|
862c849 |
return req
|
|
|
862c849 |
|
|
|
862c849 |
https_request = http_request
|