ssl_pyopenssl.py (5603B)
1 #Embedded file name: C:\BuildAgent\work\20d73f1b24eed06e\eve\release\V24.01\carbon\common\lib\cherrypy\wsgiserver\ssl_pyopenssl.py 2 import socket 3 import threading 4 import time 5 from cherrypy import wsgiserver 6 try: 7 from OpenSSL import SSL 8 from OpenSSL import crypto 9 except ImportError: 10 SSL = None 11 12 class SSL_fileobject(wsgiserver.CP_fileobject): 13 ssl_timeout = 3 14 ssl_retry = 0.01 15 16 def _safe_call(self, is_reader, call, *args, **kwargs): 17 start = time.time() 18 while True: 19 try: 20 return call(*args, **kwargs) 21 except SSL.WantReadError: 22 time.sleep(self.ssl_retry) 23 except SSL.WantWriteError: 24 time.sleep(self.ssl_retry) 25 except SSL.SysCallError as e: 26 if is_reader and e.args == (-1, 'Unexpected EOF'): 27 return '' 28 errnum = e.args[0] 29 if is_reader and errnum in wsgiserver.socket_errors_to_ignore: 30 return '' 31 raise socket.error(errnum) 32 except SSL.Error as e: 33 if is_reader and e.args == (-1, 'Unexpected EOF'): 34 return '' 35 thirdarg = None 36 try: 37 thirdarg = e.args[0][0][2] 38 except IndexError: 39 pass 40 41 if thirdarg == 'http request': 42 raise wsgiserver.NoSSLError() 43 raise wsgiserver.FatalSSLAlert(*e.args) 44 except: 45 raise 46 47 if time.time() - start > self.ssl_timeout: 48 raise socket.timeout('timed out') 49 50 def recv(self, *args, **kwargs): 51 buf = [] 52 r = super(SSL_fileobject, self).recv 53 while True: 54 data = self._safe_call(True, r, *args, **kwargs) 55 buf.append(data) 56 p = self._sock.pending() 57 if not p: 58 return ''.join(buf) 59 60 def sendall(self, *args, **kwargs): 61 return self._safe_call(False, super(SSL_fileobject, self).sendall, *args, **kwargs) 62 63 def send(self, *args, **kwargs): 64 return self._safe_call(False, super(SSL_fileobject, self).send, *args, **kwargs) 65 66 67 class SSLConnection: 68 69 def __init__(self, *args): 70 self._ssl_conn = SSL.Connection(*args) 71 self._lock = threading.RLock() 72 73 for f in ('get_context', 'pending', 'send', 'write', 'recv', 'read', 'renegotiate', 'bind', 'listen', 'connect', 'accept', 'setblocking', 'fileno', 'close', 'get_cipher_list', 'getpeername', 'getsockname', 'getsockopt', 'setsockopt', 'makefile', 'get_app_data', 'set_app_data', 'state_string', 'sock_shutdown', 'get_peer_certificate', 'want_read', 'want_write', 'set_connect_state', 'set_accept_state', 'connect_ex', 'sendall', 'settimeout', 'gettimeout'): 74 exec 'def %s(self, *args):\n self._lock.acquire()\n try:\n return self._ssl_conn.%s(*args)\n finally:\n self._lock.release()\n' % (f, f) 75 76 def shutdown(self, *args): 77 self._lock.acquire() 78 try: 79 return self._ssl_conn.shutdown() 80 finally: 81 self._lock.release() 82 83 84 class pyOpenSSLAdapter(wsgiserver.SSLAdapter): 85 context = None 86 certificate = None 87 private_key = None 88 certificate_chain = None 89 90 def __init__(self, certificate, private_key, certificate_chain = None): 91 if SSL is None: 92 raise ImportError('You must install pyOpenSSL to use HTTPS.') 93 self.context = None 94 self.certificate = certificate 95 self.private_key = private_key 96 self.certificate_chain = certificate_chain 97 self._environ = None 98 99 def bind(self, sock): 100 if self.context is None: 101 self.context = self.get_context() 102 conn = SSLConnection(self.context, sock) 103 self._environ = self.get_environ() 104 return conn 105 106 def wrap(self, sock): 107 return (sock, self._environ.copy()) 108 109 def get_context(self): 110 c = SSL.Context(SSL.SSLv23_METHOD) 111 c.use_privatekey_file(self.private_key) 112 if self.certificate_chain: 113 c.load_verify_locations(self.certificate_chain) 114 c.use_certificate_file(self.certificate) 115 return c 116 117 def get_environ(self): 118 ssl_environ = {'HTTPS': 'on'} 119 if self.certificate: 120 cert = open(self.certificate, 'rb').read() 121 cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert) 122 ssl_environ.update({'SSL_SERVER_M_VERSION': cert.get_version(), 123 'SSL_SERVER_M_SERIAL': cert.get_serial_number()}) 124 for prefix, dn in [('I', cert.get_issuer()), ('S', cert.get_subject())]: 125 dnstr = str(dn)[18:-2] 126 wsgikey = 'SSL_SERVER_%s_DN' % prefix 127 ssl_environ[wsgikey] = dnstr 128 while dnstr: 129 pos = dnstr.rfind('=') 130 dnstr, value = dnstr[:pos], dnstr[pos + 1:] 131 pos = dnstr.rfind('/') 132 dnstr, key = dnstr[:pos], dnstr[pos + 1:] 133 if key and value: 134 wsgikey = 'SSL_SERVER_%s_DN_%s' % (prefix, key) 135 ssl_environ[wsgikey] = value 136 137 return ssl_environ 138 139 def makefile(self, sock, mode = 'r', bufsize = -1): 140 if SSL and isinstance(sock, SSL.ConnectionType): 141 timeout = sock.gettimeout() 142 f = SSL_fileobject(sock, mode, bufsize) 143 f.ssl_timeout = timeout 144 return f 145 else: 146 return wsgiserver.CP_fileobject(sock, mode, bufsize)