diff -r 629042c81883 -r bb35a9811599 components/python/pywbem/patches/01-CVE-2013-6418-CVE-2013-6444.patch --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/components/python/pywbem/patches/01-CVE-2013-6418-CVE-2013-6444.patch Tue Jan 19 13:53:42 2016 -0800 @@ -0,0 +1,334 @@ +http://sourceforge.net/p/pywbem/code/622/ +http://sourceforge.net/p/pywbem/code/627/ + +fixed TOCTOU error when validating peer's certificate + +By TOCTOU it's meant time-of-check-time-of-use. Up to now, pywbem made +two connections for one request (applies just to ssl). The first one +made the verification (without the hostname check) and the second one +was used for request. No verification was done for the latter, which +could be abused. + +Peer's certificate is now validated when connecting over ssl. To +prevent man-in-the-middle attack, verification of hostname is also +added. Peer's hostname must match the commonName of its certificate. +Or it must be contained in subjectAltName (list of aliases). M2Crypto +package is used for that purpose. Thanks to it both security +enhancements could be implemented quiete easily. Downside is a new +dependency added to pywbem. Verification can be skipped if +no_verification is set to False. + +Certificate trust store can now be specified by user. Some default +paths, valid for several distributions, were added. + +Authored by: miminar 2014-01-17 + +NOTE: The code and patches are littered with whitespace issues. +Generation of patches needs to be done carefully. + +--- pywbem-0.7.0/cim_http.py.orig 2008-11-05 17:01:51.000000000 -0800 ++++ pywbem-0.7.0/cim_http.py 2014-01-17 06:11:05.000000000 -0800 +@@ -4,8 +4,7 @@ + # + # This program is free software; you can redistribute it and/or modify + # it under the terms of the GNU Lesser General Public License as +-# published by the Free Software Foundation; either version 2 of the +-# License. ++# published by the Free Software Foundation; version 2 of the License. + # + # This program is distributed in the hope that it will be useful, but + # WITHOUT ANY WARRANTY; without even the implied warranty of +@@ -29,7 +28,8 @@ being transferred is XML. It is up to t + data and interpret the result. + ''' + +-import sys, string, re, os, socket, pwd ++from M2Crypto import SSL, Err ++import sys, string, re, os, socket, getpass + from stat import S_ISSOCK + import cim_obj + from types import StringTypes +@@ -59,6 +59,15 @@ def parse_url(url): + if m: + host = url[len(m.group(0)):] + ++ # IPv6 with/without port ++ m = re.match("^\[?([0-9A-Fa-f:]*)\]?(:([0-9]*))?$", host) ++ if m: ++ host = m.group(1) ++ port_tmp = m.group(3) ++ if port_tmp: ++ port = int(port_tmp) ++ return host, port, ssl ++ + s = string.split(host, ":") # Set port number + if len(s) != 1: + host = s[0] +@@ -66,8 +75,26 @@ def parse_url(url): + + return host, port, ssl + ++def get_default_ca_certs(): ++ """ ++ Try to find out system path with ca certificates. This path is cached and ++ returned. If no path is found out, None is returned. ++ """ ++ if not hasattr(get_default_ca_certs, '_path'): ++ for path in ( ++ '/etc/pki/ca-trust/extracted/openssl/ca-bundle.trust.crt', ++ '/etc/ssl/certs', ++ '/etc/ssl/certificates'): ++ if os.path.exists(path): ++ get_default_ca_certs._path = path ++ break ++ else: ++ get_default_ca_certs._path = None ++ return get_default_ca_certs._path ++ + def wbem_request(url, data, creds, headers = [], debug = 0, x509 = None, +- verify_callback = None): ++ verify_callback = None, ca_certs = None, ++ no_verification = False): + """Send XML data over HTTP to the specified url. Return the + response in XML. Uses Python's build-in httplib. x509 may be a + dictionary containing the location of the SSL certificate and key +@@ -97,9 +124,48 @@ def wbem_request(url, data, creds, heade + + class HTTPSConnection(HTTPBaseConnection, httplib.HTTPSConnection): + def __init__(self, host, port=None, key_file=None, cert_file=None, +- strict=None): ++ strict=None, ca_certs=None, verify_callback=None): + httplib.HTTPSConnection.__init__(self, host, port, key_file, + cert_file, strict) ++ self.ca_certs = ca_certs ++ self.verify_callback = verify_callback ++ ++ def connect(self): ++ "Connect to a host on a given (SSL) port." ++ self.sock = socket.create_connection((self.host, self.port), ++ self.timeout, self.source_address) ++ if self._tunnel_host: ++ self.sock = sock ++ self._tunnel() ++ ctx = SSL.Context('sslv23') ++ if self.cert_file: ++ ctx.load_cert(self.cert_file, keyfile=self.key_file) ++ if self.ca_certs: ++ ctx.set_verify(SSL.verify_peer | SSL.verify_fail_if_no_peer_cert, ++ depth=9, callback=verify_callback) ++ if os.path.isdir(self.ca_certs): ++ ctx.load_verify_locations(capath=self.ca_certs) ++ else: ++ ctx.load_verify_locations(cafile=self.ca_certs) ++ try: ++ self.sock = SSL.Connection(ctx, self.sock) ++ # Below is a body of SSL.Connection.connect() method ++ # except for the first line (socket connection). We want to preserve ++ # tunneling ability. ++ self.sock.addr = (self.host, self.port) ++ self.sock.setup_ssl() ++ self.sock.set_connect_state() ++ ret = self.sock.connect_ssl() ++ if self.ca_certs: ++ check = getattr(self.sock, 'postConnectionCheck', ++ self.sock.clientPostConnectionCheck) ++ if check is not None: ++ if not check(self.sock.get_peer_cert(), self.host): ++ raise Error('SSL error: post connection check failed') ++ return ret ++ except ( Err.SSLError, SSL.SSLError, SSL.SSLTimeoutError ++ , SSL.Checker.WrongHost), arg: ++ raise Error("SSL error: %s" % arg) + + class FileHTTPConnection(HTTPBaseConnection, httplib.HTTPConnection): + def __init__(self, uds_path): +@@ -109,47 +175,36 @@ def wbem_request(url, data, creds, heade + self.sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) + self.sock.connect(self.uds_path) + +- host, port, ssl = parse_url(url) ++ host, port, use_ssl = parse_url(url) + + key_file = None + cert_file = None + +- if ssl: +- +- if x509 is not None: ++ if use_ssl and x509 is not None: + cert_file = x509.get('cert_file') + key_file = x509.get('key_file') + +- if verify_callback is not None: +- try: +- from OpenSSL import SSL +- ctx = SSL.Context(SSL.SSLv3_METHOD) +- ctx.set_verify(SSL.VERIFY_PEER, verify_callback) +- # Add the key and certificate to the session +- if cert_file is not None and key_file is not None: +- ctx.use_certificate_file(cert_file) +- ctx.use_privatekey_file(key_file) +- s = SSL.Connection(ctx, socket.socket(socket.AF_INET, +- socket.SOCK_STREAM)) +- s.connect((host, port)) +- s.do_handshake() +- s.shutdown() +- s.close() +- except socket.error, arg: +- raise Error("Socket error: %s" % (arg,)) +- except socket.sslerror, arg: +- raise Error("SSL error: %s" % (arg,)) +- + numTries = 0 + localAuthHeader = None + tryLimit = 5 + ++ if isinstance(data, unicode): ++ data = data.encode('utf-8') + data = '\n' + data + ++ if not no_verification and ca_certs is None: ++ ca_certs = get_default_ca_certs() ++ elif no_verification: ++ ca_certs = None ++ + local = False +- if ssl: +- h = HTTPSConnection(host, port = port, key_file = key_file, +- cert_file = cert_file) ++ if use_ssl: ++ h = HTTPSConnection(host, ++ port = port, ++ key_file = key_file, ++ cert_file = cert_file, ++ ca_certs = ca_certs, ++ verify_callback = verify_callback) + else: + if url.startswith('http'): + h = HTTPConnection(host, port = port) +@@ -167,12 +222,12 @@ def wbem_request(url, data, creds, heade + raise Error('Invalid URL') + + locallogin = None +- if host in ('localhost', '127.0.0.1'): ++ if host in ('localhost', 'localhost6', '127.0.0.1', '::1'): + local = True + if local: + uid = os.getuid() + try: +- locallogin = pwd.getpwuid(uid)[0] ++ locallogin = getpass.getuser() + except KeyError: + locallogin = None + while numTries < tryLimit: +@@ -191,6 +246,8 @@ def wbem_request(url, data, creds, heade + h.putheader('PegasusAuthorization', 'Local "%s"' % locallogin) + + for hdr in headers: ++ if isinstance(hdr, unicode): ++ hdr = hdr.encode('utf-8') + s = map(lambda x: string.strip(x), string.split(hdr, ":", 1)) + h.putheader(urllib.quote(s[0]), urllib.quote(s[1])) + + +--- pywbem-0.7.0/cim_operations.py.orig 2008-12-12 09:40:22.000000000 -0800 ++++ pywbem-0.7.0/cim_operations.py 2014-01-17 06:11:05.000000000 -0800 +@@ -4,8 +4,7 @@ + # + # This program is free software; you can redistribute it and/or modify + # it under the terms of the GNU Lesser General Public License as +-# published by the Free Software Foundation; either version 2 of the +-# License. ++# published by the Free Software Foundation; version 2 of the License. + # + # This program is distributed in the hope that it will be useful, but + # WITHOUT ANY WARRANTY; without even the implied warranty of +@@ -28,7 +27,7 @@ import sys, string + from types import StringTypes + from xml.dom import minidom + import cim_obj, cim_xml, cim_http, cim_types +-from cim_obj import CIMClassName, CIMInstanceName, CIMInstance, CIMClass ++from cim_obj import CIMClassName, CIMInstanceName, CIMInstance, CIMClass, NocaseDict + from datetime import datetime, timedelta + from tupletree import dom_to_tupletree, xml_to_tupletree + from tupleparse import parse_cim +@@ -79,12 +78,12 @@ class WBEMConnection(object): + the request before it is sent, and the reply before it is + unpacked. + +- verify_callback is used to verify the server certificate. +- It is passed to OpenSSL.SSL.set_verify, and is called during the SSL +- handshake. verify_callback should take five arguments: A Connection +- object, an X509 object, and three integer variables, which are in turn +- potential error number, error depth and return code. verify_callback +- should return True if verification passes and False otherwise. ++ verify_callback is used to verify the server certificate. It is passed to ++ M2Crypto.SSL.Context.set_verify, and is called during the SSL handshake. ++ verify_callback should take five arguments: An SSL Context object, an X509 ++ object, and three integer variables, which are in turn potential error ++ number, error depth and return code. verify_callback should return True if ++ verification passes and False otherwise. + + The value of the x509 argument is used only when the url contains + 'https'. x509 must be a dictionary containing the keys 'cert_file' +@@ -92,14 +91,27 @@ class WBEMConnection(object): + filename of an certificate and the value of 'key_file' must consist + of a filename containing the private key belonging to the public key + that is part of the certificate in cert_file. ++ ++ ca_certs specifies where CA certificates for verification purposes are ++ located. These are trusted certificates. Note that the certificates have to ++ be in PEM format. Either it is a directory prepared using the c_rehash tool ++ included with OpenSSL or an pemfile. If None, default system path will be ++ used. ++ ++ no_verification allows to disable peer's verification. This is insecure and ++ should be avoided. If True, peer's certificate is not verified and ca_certs ++ argument is ignored. + """ + + def __init__(self, url, creds = None, default_namespace = DEFAULT_NAMESPACE, +- x509 = None, verify_callback = None): ++ x509 = None, verify_callback = None, ca_certs = None, ++ no_verification = False): + self.url = url + self.creds = creds + self.x509 = x509 + self.verify_callback = verify_callback ++ self.ca_certs = ca_certs ++ self.no_verification = no_verification + self.last_request = self.last_reply = '' + self.default_namespace = default_namespace + self.debug = False +@@ -165,7 +177,9 @@ class WBEMConnection(object): + resp_xml = cim_http.wbem_request(self.url, req_xml.toxml(), + self.creds, headers, + x509 = self.x509, +- verify_callback = self.verify_callback) ++ verify_callback = self.verify_callback, ++ ca_certs = self.ca_certs, ++ no_verification = self.no_verification) + except cim_http.AuthError: + raise + except cim_http.Error, arg: +@@ -322,7 +336,9 @@ class WBEMConnection(object): + resp_xml = cim_http.wbem_request(self.url, req_xml.toxml(), + self.creds, headers, + x509 = self.x509, +- verify_callback = self.verify_callback) ++ verify_callback = self.verify_callback, ++ ca_certs = self.ca_certs, ++ no_verification = self.no_verification) + except cim_http.Error, arg: + # Convert cim_http exceptions to CIMError exceptions + raise CIMError(0, str(arg)) +@@ -812,7 +828,7 @@ class WBEMConnection(object): + + # Convert zero or more PARAMVALUE elements into dictionary + +- output_params = {} ++ output_params = NocaseDict() + + for p in result: + if p[1] == 'reference': +