mvadkert / rpms / ansible

Forked from rpms/ansible 6 years ago
Clone
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