1# Copyright 2014 Google Inc. All Rights Reserved. 2# 3# Licensed under the Apache License, Version 2.0 (the "License"); 4# you may not use this file except in compliance with the License. 5# You may obtain a copy of the License at 6# 7# http://www.apache.org/licenses/LICENSE-2.0 8# 9# Unless required by applicable law or agreed to in writing, software 10# distributed under the License is distributed on an "AS IS" BASIS, 11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12# See the License for the specific language governing permissions and 13# limitations under the License. 14 15"""Extends BaseHTTPRequestHandler with SSL certificate generation.""" 16 17import logging 18import socket 19 20import certutils 21 22 23 24def _SetUpUsingDummyCert(handler): 25 """Sets up connection providing the certificate to the client. 26 27 This method handles Server Name Indication (SNI) using dummy certs. 28 29 Args: 30 handler: an instance of BaseHTTPServer.BaseHTTPRequestHandler that is used 31 by some instance of BaseHTTPServer.HTTPServer. 32 """ 33 # One of: One of SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, or TLSv1_METHOD 34 context = certutils.get_ssl_context() 35 def handle_servername(connection): 36 """A SNI callback that happens during do_handshake().""" 37 try: 38 host = connection.get_servername() 39 if host: 40 cert_str = ( 41 handler.server.get_certificate(host)) 42 new_context = certutils.get_ssl_context() 43 cert = certutils.load_cert(cert_str) 44 new_context.use_certificate(cert) 45 new_context.use_privatekey_file(handler.server.ca_cert_path) 46 connection.set_context(new_context) 47 return new_context 48 # else: fail with 'no shared cipher' 49 except Exception, e: 50 # Do not leak any exceptions or else openssl crashes. 51 logging.error('Exception in SNI handler: %s', e) 52 53 context.set_tlsext_servername_callback(handle_servername) 54 handler.connection = certutils.get_ssl_connection(context, handler.connection) 55 handler.connection.set_accept_state() 56 try: 57 handler.connection.do_handshake() 58 except certutils.Error, v: 59 host = handler.connection.get_servername() 60 if not host: 61 logging.error('Dropping request without SNI') 62 return '' 63 raise certutils.Error('SSL handshake error %s: %s' % (host, str(v))) 64 65 # Re-wrap the read/write streams with our new connection. 66 handler.rfile = socket._fileobject(handler.connection, 'rb', handler.rbufsize, 67 close=False) 68 handler.wfile = socket._fileobject(handler.connection, 'wb', handler.wbufsize, 69 close=False) 70 71 72def wrap_handler(handler_class): 73 """Wraps a BaseHTTPHandler with SSL MITM certificates.""" 74 if certutils.openssl_import_error: 75 # pylint: disable=raising-bad-type 76 raise certutils.openssl_import_error 77 78 class WrappedHandler(handler_class): 79 80 def setup(self): 81 handler_class.setup(self) 82 _SetUpUsingDummyCert(self) 83 84 def finish(self): 85 handler_class.finish(self) 86 self.connection.shutdown() 87 self.connection.close() 88 89 return WrappedHandler 90