# Copyright 2014 Google Inc. All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. """Extends BaseHTTPRequestHandler with SSL certificate generation.""" import logging import socket import certutils def _SetUpUsingDummyCert(handler): """Sets up connection providing the certificate to the client. This method handles Server Name Indication (SNI) using dummy certs. Args: handler: an instance of BaseHTTPServer.BaseHTTPRequestHandler that is used by some instance of BaseHTTPServer.HTTPServer. """ # One of: One of SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, or TLSv1_METHOD context = certutils.get_ssl_context() def handle_servername(connection): """A SNI callback that happens during do_handshake().""" try: host = connection.get_servername() if host: cert_str = ( handler.server.get_certificate(host)) new_context = certutils.get_ssl_context() cert = certutils.load_cert(cert_str) new_context.use_certificate(cert) new_context.use_privatekey_file(handler.server.ca_cert_path) connection.set_context(new_context) return new_context # else: fail with 'no shared cipher' except Exception, e: # Do not leak any exceptions or else openssl crashes. logging.error('Exception in SNI handler: %s', e) context.set_tlsext_servername_callback(handle_servername) handler.connection = certutils.get_ssl_connection(context, handler.connection) handler.connection.set_accept_state() try: handler.connection.do_handshake() except certutils.Error, v: host = handler.connection.get_servername() if not host: logging.error('Dropping request without SNI') return '' raise certutils.Error('SSL handshake error %s: %s' % (host, str(v))) # Re-wrap the read/write streams with our new connection. handler.rfile = socket._fileobject(handler.connection, 'rb', handler.rbufsize, close=False) handler.wfile = socket._fileobject(handler.connection, 'wb', handler.wbufsize, close=False) def wrap_handler(handler_class): """Wraps a BaseHTTPHandler with SSL MITM certificates.""" if certutils.openssl_import_error: # pylint: disable=raising-bad-type raise certutils.openssl_import_error class WrappedHandler(handler_class): def setup(self): handler_class.setup(self) _SetUpUsingDummyCert(self) def finish(self): handler_class.finish(self) self.connection.shutdown() self.connection.close() return WrappedHandler