def _ssl_handshake(self): """ Perform an SSL handshake w/ the server. Precondition: a successful STARTTLS exchange has taken place with Riak returns True upon success, otherwise an exception is raised """ credentials = self._client._credentials if credentials: try: ssl_ctx = configure_ssl_context(credentials) host = self._address[0] ssl_socket = ssl.SSLSocket( sock=self._socket, keyfile=credentials.pkey_file, certfile=credentials.cert_file, cert_reqs=ssl.CERT_REQUIRED, ca_certs=credentials.cacert_file, ciphers=credentials.ciphers, server_hostname=host) ssl_socket.context = ssl_ctx # ssl handshake successful ssl_socket.do_handshake() self._socket = ssl_socket return True except ssl.SSLError as e: raise SecurityError(e) except Exception as e: # fail if *any* exceptions are thrown during SSL handshake raise SecurityError(e)
def _init_security(self): """ Initialize a secure connection to the server. """ if not self._starttls(): raise SecurityError("Could not start TLS connection") # _ssh_handshake() will throw an exception upon failure self._ssl_handshake() if not self._auth(): raise SecurityError("Could not authorize connection")
def configure_ssl_context(credentials): """ Set various options on the SSL context for Python >= 2.7.9 and 3.x. N.B. versions earlier than 3.4 may not support all security measures, e.g., hostname check. :param credentials: Riak Security Credentials :type credentials: :class:`~riak.security.SecurityCreds` :rtype :class:`~ssl.SSLContext` """ ssl_ctx = ssl.SSLContext(credentials.ssl_version) ssl_ctx.verify_mode = ssl.CERT_REQUIRED if hasattr(ssl_ctx, 'check_hostname'): ssl_ctx.check_hostname = True if credentials.cacert_file is None: raise SecurityError("cacert_file is required in SecurityCreds") if credentials.ciphers is not None: ssl_ctx.set_ciphers(credentials.ciphers) ssl_ctx.load_verify_locations(credentials.cacert_file) if credentials.ciphers is not None: ssl_ctx.set_ciphers(credentials.ciphers) pkeyfile = credentials.pkey_file certfile = credentials.cert_file if pkeyfile and not certfile: raise SecurityError("cert_file must be specified with pkey_file") if certfile and not pkeyfile: pkeyfile = certfile if certfile: ssl_ctx.load_cert_chain(certfile, pkeyfile) # TODO https://bugs.python.org/issue8813 if credentials.crl_file is not None: ssl_ctx.load_verify_locations(credentials.crl_file) ssl_ctx.verify_flags = ssl.VERIFY_CRL_CHECK_LEAF # SSLv2 considered harmful. ssl_ctx.options |= ssl.OP_NO_SSLv2 # SSLv3 has problematic security and is only required for really old # clients such as IE6 on Windows XP ssl_ctx.options |= ssl.OP_NO_SSLv3 # disable compression to prevent CRIME attacks (OpenSSL 1.0+) ssl_ctx.options |= ssl.OP_NO_COMPRESSION return ssl_ctx
def connect(self): """ Connect to a host on a given (SSL) port using PyOpenSSL. """ sock = socket.create_connection((self.host, self.port), self.timeout) if PY2: ssl_ctx = configure_pyopenssl_context(self.credentials) # attempt to upgrade the socket to TLS cxn = OpenSSL.SSL.Connection(ssl_ctx, sock) cxn.set_connect_state() while True: try: cxn.do_handshake() except OpenSSL.SSL.WantReadError: select.select([sock], [], []) continue except OpenSSL.SSL.Error as e: raise SecurityError('bad handshake - ' + str(e)) break self.sock = RiakWrappedSocket(cxn, sock) self.credentials._check_revoked_cert(self.sock) else: ssl_ctx = configure_ssl_context(self.credentials) host = "riak@" + self.host self.sock = ssl.SSLSocket(sock=sock, keyfile=self.credentials.pkey_file, certfile=self.credentials.cert_file, cert_reqs=ssl.CERT_REQUIRED, ca_certs=self.credentials.cacert_file, ciphers=self.credentials.ciphers, server_hostname=host) self.sock.context = ssl_ctx
def _ssl_handshake(self): """ Perform an SSL handshake w/ the server. Precondition: a successful STARTTLS exchange has taken place with Riak returns True upon success, otherwise an exception is raised """ if self._client._credentials: ssl_ctx = \ Context(self._client._credentials.ssl_version) try: configure_context(ssl_ctx, self._client._credentials) # attempt to upgrade the socket to SSL ssl_socket = Connection(ssl_ctx, self._socket) ssl_socket.set_connect_state() ssl_socket.do_handshake() # ssl handshake successful self._socket = ssl_socket self._client._credentials._check_revoked_cert(ssl_socket) return True except Exception as e: # fail if *any* exceptions are thrown during SSL handshake raise SecurityError(e.message)
def configure_pyopenssl_context(credentials): """ Set various options on the SSL context for Python <= 2.7.8. :param credentials: Riak Security Credentials :type credentials: :class:`~riak.security.SecurityCreds` :rtype ssl_ctx: :class:`~OpenSSL.SSL.Context` """ ssl_ctx = OpenSSL.SSL.Context(credentials.ssl_version) if credentials._has_credential('pkey'): ssl_ctx.use_privatekey(credentials.pkey) if credentials._has_credential('cert'): ssl_ctx.use_certificate(credentials.cert) if credentials._has_credential('cacert'): store = ssl_ctx.get_cert_store() cacerts = credentials.cacert if not isinstance(cacerts, list): cacerts = [cacerts] for cacert in cacerts: store.add_cert(cacert) else: raise SecurityError("cacert_file is required in SecurityCreds") ciphers = credentials.ciphers if ciphers is not None: ssl_ctx.set_cipher_list(ciphers) # Demand a certificate ssl_ctx.set_verify( OpenSSL.SSL.VERIFY_PEER | OpenSSL.SSL.VERIFY_FAIL_IF_NO_PEER_CERT, verify_cb) return ssl_ctx
def verify_cb(conn, cert, errnum, depth, ok): """ The default OpenSSL certificate verification callback. """ if not ok: raise SecurityError("Could not verify CA certificate {0}".format( cert.get_subject())) return ok
def set_bucket_props(self, bucket, props): """ Set the properties on the bucket object given """ bucket_type = self._get_bucket_type(bucket.bucket_type) url = self.bucket_properties_path(bucket.name, bucket_type=bucket_type) headers = {'Content-Type': 'application/json'} content = json.dumps({'props': props}) # Run the request... status, _, body = self._request('PUT', url, headers, content) if status == 401: raise SecurityError('Not authorized to set bucket properties.') elif status != 204: raise RiakError('Error setting bucket properties.') return True