fcb4b3b
Index: pywbem-20131121/cim_http.py
fcb4b3b
===================================================================
fcb4b3b
--- pywbem-20131121.orig/cim_http.py
fcb4b3b
+++ pywbem-20131121/cim_http.py
fcb4b3b
@@ -28,6 +28,7 @@ being transferred is XML.  It is up to t
fcb4b3b
 data and interpret the result.
fcb4b3b
 '''
fcb4b3b
 
fcb4b3b
+from M2Crypto import SSL, Err
fcb4b3b
 import sys, string, re, os, socket, getpass
fcb4b3b
 from stat import S_ISSOCK
fcb4b3b
 import cim_obj
fcb4b3b
@@ -74,8 +75,26 @@ def parse_url(url):
fcb4b3b
 
fcb4b3b
     return host, port, ssl
fcb4b3b
 
fcb4b3b
+def get_default_ca_certs():
fcb4b3b
+    """
fcb4b3b
+    Try to find out system path with ca certificates. This path is cached and
fcb4b3b
+    returned. If no path is found out, None is returned.
fcb4b3b
+    """
fcb4b3b
+    if not hasattr(get_default_ca_certs, '_path'):
fcb4b3b
+        for path in (
fcb4b3b
+                '/etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt',
fcb4b3b
+                '/etc/ssl/certs',
fcb4b3b
+                '/etc/ssl/certificates'):
fcb4b3b
+            if os.path.exists(path):
fcb4b3b
+                get_default_ca_certs._path = path
fcb4b3b
+                break
fcb4b3b
+        else:
fcb4b3b
+            get_default_ca_certs._path = None
fcb4b3b
+    return get_default_ca_certs._path
fcb4b3b
+
fcb4b3b
 def wbem_request(url, data, creds, headers = [], debug = 0, x509 = None,
fcb4b3b
-                 verify_callback = None):
fcb4b3b
+                 verify_callback = None, ca_certs = None,
fcb4b3b
+                 no_verification = False):
fcb4b3b
     """Send XML data over HTTP to the specified url. Return the
fcb4b3b
     response in XML.  Uses Python's build-in httplib.  x509 may be a
fcb4b3b
     dictionary containing the location of the SSL certificate and key
5675d62
@@ -105,10 +124,47 @@ def wbem_request(url, data, creds, heade
fcb4b3b
     
fcb4b3b
     class HTTPSConnection(HTTPBaseConnection, httplib.HTTPSConnection):
fcb4b3b
         def __init__(self, host, port=None, key_file=None, cert_file=None, 
fcb4b3b
-                     strict=None):
fcb4b3b
+                     strict=None, ca_certs=None, verify_callback=None):
fcb4b3b
             httplib.HTTPSConnection.__init__(self, host, port, key_file, 
fcb4b3b
                                              cert_file, strict)
fcb4b3b
-    
fcb4b3b
+            self.ca_certs = ca_certs
fcb4b3b
+            self.verify_callback = verify_callback
fcb4b3b
+
fcb4b3b
+        def connect(self):
fcb4b3b
+            "Connect to a host on a given (SSL) port."
5675d62
+            self.sock = socket.create_connection((self.host, self.port),
fcb4b3b
+                                            self.timeout, self.source_address)
fcb4b3b
+            if self._tunnel_host:
fcb4b3b
+                self.sock = sock
fcb4b3b
+                self._tunnel()
fcb4b3b
+            ctx = SSL.Context('sslv23')
fcb4b3b
+            if self.cert_file:
fcb4b3b
+                ctx.load_cert(self.cert_file, keyfile=self.key_file)
fcb4b3b
+            if self.ca_certs:
fcb4b3b
+                ctx.set_verify(SSL.verify_peer | SSL.verify_fail_if_no_peer_cert,
fcb4b3b
+                    depth=9, callback=verify_callback)
fcb4b3b
+                if os.path.isdir(self.ca_certs):
fcb4b3b
+                    ctx.load_verify_locations(capath=self.ca_certs)
fcb4b3b
+                else:
fcb4b3b
+                    ctx.load_verify_locations(cafile=self.ca_certs)
fcb4b3b
+            try:
5675d62
+                self.sock = SSL.Connection(ctx, self.sock)
5675d62
+                # Below is a body of SSL.Connection.connect() method
5675d62
+                # except for the first line (socket connection). We want to preserve
5675d62
+                # tunneling ability.
5675d62
+                self.sock.addr = (self.host, self.port)
5675d62
+                self.sock.setup_ssl()
5675d62
+                self.sock.set_connect_state()
5675d62
+                ret = self.sock.connect_ssl()
5675d62
+                check = getattr(self.sock, 'postConnectionCheck',
5675d62
+                         self.sock.clientPostConnectionCheck)
5675d62
+                if check is not None:
5675d62
+                    if not check(self.sock.get_peer_cert(), self.host):
5675d62
+                        raise Error('SSL error: post connection check failed')
5675d62
+                return ret
fcb4b3b
+            except (Err.SSLError, SSL.SSLError, SSL.SSLTimeoutError), arg:
fcb4b3b
+                raise Error("SSL error: %s" % arg)
fcb4b3b
+
fcb4b3b
     class FileHTTPConnection(HTTPBaseConnection, httplib.HTTPConnection):
fcb4b3b
         def __init__(self, uds_path):
fcb4b3b
             httplib.HTTPConnection.__init__(self, 'localhost')
5675d62
@@ -117,64 +173,36 @@ def wbem_request(url, data, creds, heade
fcb4b3b
             self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
fcb4b3b
             self.sock.connect(self.uds_path)
fcb4b3b
 
fcb4b3b
-    host, port, ssl = parse_url(url)
fcb4b3b
+    host, port, use_ssl = parse_url(url)
fcb4b3b
 
fcb4b3b
     key_file = None
fcb4b3b
     cert_file = None
fcb4b3b
 
fcb4b3b
-    if ssl:
fcb4b3b
-
fcb4b3b
-        if x509 is not None:
fcb4b3b
-            cert_file = x509.get('cert_file')
fcb4b3b
-            key_file = x509.get('key_file')
fcb4b3b
-
fcb4b3b
-        if verify_callback is not None:
fcb4b3b
-            addr_ind = 0
fcb4b3b
-            # Temporary exception store
fcb4b3b
-            addr_exc = None
fcb4b3b
-            # Get a list of arguments for socket().
fcb4b3b
-            addr_list = socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM)
fcb4b3b
-            for addr_ind in xrange(len(addr_list)):
fcb4b3b
-                family, socktype, proto, canonname, sockaddr = addr_list[addr_ind]
fcb4b3b
-                try:
fcb4b3b
-                    from OpenSSL import SSL
fcb4b3b
-                    ctx = SSL.Context(SSL.SSLv3_METHOD)
fcb4b3b
-                    ctx.set_verify(SSL.VERIFY_PEER, verify_callback)
fcb4b3b
-                    ctx.set_default_verify_paths()
fcb4b3b
-                    # Add the key and certificate to the session
fcb4b3b
-                    if cert_file is not None and key_file is not None:
fcb4b3b
-                      ctx.use_certificate_file(cert_file)
fcb4b3b
-                      ctx.use_privatekey_file(key_file)
fcb4b3b
-                    s = SSL.Connection(ctx, socket.socket(family, socktype, proto))
fcb4b3b
-                    s.connect((host, port))
fcb4b3b
-                    s.do_handshake()
fcb4b3b
-                    s.shutdown()
fcb4b3b
-                    s.close()
fcb4b3b
-                    addr_exc = None
fcb4b3b
-                    break
fcb4b3b
-                except (socket.gaierror, socket.error), arg:
fcb4b3b
-                    # Could not perform connect() call, store the exception object for
fcb4b3b
-                    # later use.
fcb4b3b
-                    addr_exc = arg
fcb4b3b
-                    continue
fcb4b3b
-                except socket.sslerror, arg:
fcb4b3b
-                    raise Error("SSL error: %s" % (arg,))
fcb4b3b
-
fcb4b3b
-            # Did we try all the addresses from getaddrinfo() and no successful
fcb4b3b
-            # connection performed?
fcb4b3b
-            if addr_exc:
fcb4b3b
-                raise Error("Socket error: %s" % (addr_exc),)
fcb4b3b
+    if use_ssl and x509 is not None:
fcb4b3b
+        cert_file = x509.get('cert_file')
fcb4b3b
+        key_file = x509.get('key_file')
fcb4b3b
 
fcb4b3b
     numTries = 0
fcb4b3b
     localAuthHeader = None
5675d62
     tryLimit = 5
fcb4b3b
 
5675d62
+    if isinstance(data, unicode):
5675d62
+        data = data.encode('utf-8')
fcb4b3b
     data = '\n' + data
fcb4b3b
 
fcb4b3b
+    if not no_verification and ca_certs is None:
fcb4b3b
+        ca_certs = get_default_ca_certs()
fcb4b3b
+    elif no_verification:
fcb4b3b
+        ca_certs = None
fcb4b3b
+
fcb4b3b
     local = False
fcb4b3b
-    if ssl:
fcb4b3b
-        h = HTTPSConnection(host, port = port, key_file = key_file,
fcb4b3b
-                                            cert_file = cert_file)
fcb4b3b
+    if use_ssl:
fcb4b3b
+        h = HTTPSConnection(host,
fcb4b3b
+                port = port,
fcb4b3b
+                key_file = key_file,
fcb4b3b
+                cert_file = cert_file,
fcb4b3b
+                ca_certs = ca_certs,
fcb4b3b
+                verify_callback = verify_callback)
fcb4b3b
     else:
fcb4b3b
         if url.startswith('http'):
fcb4b3b
             h = HTTPConnection(host, port = port)
5675d62
@@ -216,6 +244,8 @@ def wbem_request(url, data, creds, heade
5675d62
             h.putheader('PegasusAuthorization', 'Local "%s"' % locallogin)
5675d62
 
5675d62
         for hdr in headers:
5675d62
+            if isinstance(hdr, unicode):
5675d62
+                hdr = hdr.encode('utf-8')
5675d62
             s = map(lambda x: string.strip(x), string.split(hdr, ":", 1))
5675d62
             h.putheader(urllib.quote(s[0]), urllib.quote(s[1]))
5675d62
 
fcb4b3b
Index: pywbem-20131121/cim_operations.py
fcb4b3b
===================================================================
fcb4b3b
--- pywbem-20131121.orig/cim_operations.py
fcb4b3b
+++ pywbem-20131121/cim_operations.py
fcb4b3b
@@ -78,12 +78,12 @@ class WBEMConnection(object):
fcb4b3b
     the request before it is sent, and the reply before it is
fcb4b3b
     unpacked.
fcb4b3b
 
fcb4b3b
-    verify_callback is used to verify the server certificate.  
fcb4b3b
-    It is passed to OpenSSL.SSL.set_verify, and is called during the SSL
fcb4b3b
-    handshake.  verify_callback should take five arguments: A Connection 
fcb4b3b
-    object, an X509 object, and three integer variables, which are in turn 
fcb4b3b
-    potential error number, error depth and return code. verify_callback 
fcb4b3b
-    should return True if verification passes and False otherwise.
fcb4b3b
+    verify_callback is used to verify the server certificate.  It is passed to
fcb4b3b
+    M2Crypto.SSL.Context.set_verify, and is called during the SSL handshake.
fcb4b3b
+    verify_callback should take five arguments: An SSL Context object, an X509
fcb4b3b
+    object, and three integer variables, which are in turn potential error
fcb4b3b
+    number, error depth and return code. verify_callback should return True if
fcb4b3b
+    verification passes and False otherwise.
fcb4b3b
 
fcb4b3b
     The value of the x509 argument is used only when the url contains
fcb4b3b
     'https'. x509 must be a dictionary containing the keys 'cert_file' 
fcb4b3b
@@ -91,14 +91,27 @@ class WBEMConnection(object):
fcb4b3b
     filename of an certificate and the value of 'key_file' must consist 
fcb4b3b
     of a filename containing the private key belonging to the public key 
fcb4b3b
     that is part of the certificate in cert_file. 
fcb4b3b
+
fcb4b3b
+    ca_certs specifies where CA certificates for verification purposes are
fcb4b3b
+    located. These are trusted certificates. Note that the certificates have to
fcb4b3b
+    be in PEM format. Either it is a directory prepared using the c_rehash tool
fcb4b3b
+    included with OpenSSL or an pemfile. If None, default system path will be
fcb4b3b
+    used.
fcb4b3b
+
fcb4b3b
+    no_verification allows to disable peer's verification. This is insecure and
fcb4b3b
+    should be avoided. If True, peer's certificate is not verified and ca_certs
fcb4b3b
+    argument is ignored.
fcb4b3b
     """
fcb4b3b
     
fcb4b3b
     def __init__(self, url, creds = None, default_namespace = DEFAULT_NAMESPACE,
fcb4b3b
-                 x509 = None, verify_callback = None):
fcb4b3b
+                 x509 = None, verify_callback = None, ca_certs = None,
fcb4b3b
+                 no_verification = False):
fcb4b3b
         self.url = url
fcb4b3b
         self.creds = creds
fcb4b3b
         self.x509 = x509
fcb4b3b
         self.verify_callback = verify_callback
fcb4b3b
+        self.ca_certs = ca_certs
fcb4b3b
+        self.no_verification = no_verification
fcb4b3b
         self.last_request = self.last_reply = ''
fcb4b3b
         self.default_namespace = default_namespace
fcb4b3b
         self.debug = False
fcb4b3b
@@ -164,7 +177,9 @@ class WBEMConnection(object):
fcb4b3b
             resp_xml = cim_http.wbem_request(self.url, req_xml.toxml(),
fcb4b3b
                                              self.creds, headers,
fcb4b3b
                                              x509 = self.x509,
fcb4b3b
-                                             verify_callback = self.verify_callback)
fcb4b3b
+                                             verify_callback = self.verify_callback,
fcb4b3b
+                                             ca_certs = self.ca_certs,
fcb4b3b
+                                             no_verification = self.no_verification)
fcb4b3b
         except cim_http.AuthError:
fcb4b3b
             raise
fcb4b3b
         except cim_http.Error, arg:
fcb4b3b
@@ -321,7 +336,9 @@ class WBEMConnection(object):
fcb4b3b
             resp_xml = cim_http.wbem_request(self.url, req_xml.toxml(),
fcb4b3b
                                              self.creds, headers,
fcb4b3b
                                              x509 = self.x509,
fcb4b3b
-                                             verify_callback = self.verify_callback)
fcb4b3b
+                                             verify_callback = self.verify_callback,
fcb4b3b
+                                             ca_certs = self.ca_certs,
fcb4b3b
+                                             no_verification = self.no_verification)
fcb4b3b
         except cim_http.Error, arg:
fcb4b3b
             # Convert cim_http exceptions to CIMError exceptions
fcb4b3b
             raise CIMError(0, str(arg))
fcb4b3b
Index: pywbem-20131121/setup.py
fcb4b3b
===================================================================
fcb4b3b
--- pywbem-20131121.orig/setup.py
fcb4b3b
+++ pywbem-20131121/setup.py
fcb4b3b
@@ -37,6 +37,7 @@ args = {'name': 'pywbem',
fcb4b3b
         'version': '0.7.0',
fcb4b3b
         'license': 'LGPLv2',
fcb4b3b
         'packages': ['pywbem'],
fcb4b3b
+        'install_requires': ['M2Crypto'],
fcb4b3b
         # Make packages in root dir appear in pywbem module
fcb4b3b
         'package_dir': {'pywbem': ''}, 
fcb4b3b
         # Make extensions in root dir appear in pywbem module