web.web.wsgiserver.ssl_builtin

Package web :: Package web :: Package wsgiserver :: Module ssl_builtin |
[hide private]
[frames] | no frames]

# Source Code for Module web.web.wsgiserver.ssl_builtin

      1  """A library for integrating pyOpenSSL with CherryPy.
      2
      3  The ssl module must be importable for SSL functionality.
      4
      5  To use this module, set CherryPyWSGIServer.ssl_adapter to an instance of
      6  BuiltinSSLAdapter.
      7
      8      ssl_adapter.certificate: the filename of the server SSL certificate.
      9      ssl_adapter.private_key: the filename of the server's private key file.
     10  """
     11
     12  try:
     13      import ssl
     14  except ImportError:
     15      ssl = None
     16
     17
     18  from web import wsgiserver
     19
     20 -def decode_cert( prefix, cert ):
     21
     22      if not cert:
     23          return None
     24
     25      key_map = { 'countryName':'C',
     26                  'stateOrProvinceName':'ST',
     27                  'localityName':'L',
     28                  'organizationName':'O',
     29                  'organizationalUnitName':'OU',
     30                  'commonName':'CN',
     31                  # Don't know by what key names python's ssl
     32                  # implementation uses for these fields.
     33                  #'???':'T',
     34                  #'???':'I',
     35                  #'???':'G',
     36                  #'???':'S',
     37                  #'???':'D',
     38                  #'???':'UID',
     39                  'emailAddress':'Email',
     40                  }
     41
     42      DN_string = ["subject=",]
     43      cert_dict = {}
     44
     45      for rdn in cert:
     46          for key, item in rdn:
     47              if key in key_map:
     48                  cert_dict["%s_%s" % ( prefix, key_map[key] ) ] = item
     49                  DN_string.append( "%s=%s" % ( key_map[key], item ))
     50
     51      cert_dict[prefix] = "/".join( DN_string )
     52
     53      return cert_dict
     54
     55
     56 -class BuiltinSSLAdapter(wsgiserver.SSLAdapter):
     57      """A wrapper for integrating Python's builtin ssl module with CherryPy."""
     58
     59 -    def __init__(self, certificate, private_key, certificate_chain=None, client_CA=None):
     60          if ssl is None:
     61              raise ImportError("You must install the ssl module to use HTTPS.")
     62          self.certificate = certificate
     63          self.private_key = private_key
     64          self.certificate_chain = certificate_chain
     65          self.client_CA = client_CA
     66
     67 -    def bind(self, sock):
     68          """Wrap and return the given socket."""
     69          return sock
     70
     71 -    def wrap(self, sock):
     72          """Wrap and return the given socket, plus WSGI environ entries."""
     73          try:
     74              if self.client_CA:
     75                  s = ssl.wrap_socket(sock, do_handshake_on_connect=True,
     76                                      server_side=True, certfile=self.certificate,
     77                                      keyfile=self.private_key, ssl_version=ssl.PROTOCOL_SSLv23,
     78                                      ca_certs=self.client_CA,
     79                                      cert_reqs=ssl.CERT_REQUIRED)
     80              else:
     81                  s = ssl.wrap_socket(sock, do_handshake_on_connect=True,
     82                                      server_side=True, certfile=self.certificate,
     83                                      keyfile=self.private_key, ssl_version=ssl.PROTOCOL_SSLv23)
     84          except ssl.SSLError, e:
     85              if e.errno == ssl.SSL_ERROR_EOF:
     86                  # This is almost certainly due to the cherrypy engine
     87                  # 'pinging' the socket to assert it's connectable;
     88                  # the 'ping' isn't SSL.
     89                  return None, {}
     90              elif e.errno == ssl.SSL_ERROR_SSL:
     91                  if e.args[1].endswith('http request'):
     92                      # The client is speaking HTTP to an HTTPS server.
     93                      raise wsgiserver.NoSSLError
     94              raise
     95          return s, self.get_environ(s)
     96
     97      # TODO: fill this out more with mod ssl env
     98 -    def get_environ(self, sock):
     99          """Create WSGI environ entries to be merged into each request."""
    100          cipher = sock.cipher()
    101          ssl_environ = {
    102              "wsgi.url_scheme": "https",
    103              "HTTPS": "on",
    104              'SSL_PROTOCOL': cipher[1],
    105              'SSL_CIPHER': cipher[0]
    106  ##            SSL_VERSION_INTERFACE     string  The mod_ssl program version
    107  ##            SSL_VERSION_LIBRARY       string  The OpenSSL program version
    108              }
    109
    110
    111          client_cert = sock.getpeercert()
    112
    113          client_cert_subject = decode_cert( "SSL_CLIENT_S_DN", client_cert["subject"] )
    114
    115
    116          # Update for client environment variables
    117          ssl_environ.update( {
    118              #"SSL_CLIENT_M_VERSION":              "",
    119              #"SSL_CLIENT_M_SERIAL":               "",
    120              #"SSL_CLIENT_V_START":               "",
    121              "SSL_CLIENT_V_END":                  client_cert["notAfter"],
    122              #"SSL_CLIENT_A_SIG":                         "",
    123              #"SSL_CLIENT_A_KEY":                         "",
    124              #"SSL_CLIENT_CERT":                  "",
    125              #"SSL_CLIENT_CERT_CHAIN":            "",
    126              #"SSL_CLIENT_VERIFY":                "",
    127              } )
    128
    129          ssl_environ.update( client_cert_subject )
    130
    131          # Update for server environment variables
    132          ssl_environ.update( {
    133              #"SSL_SERVER_M_VERSION":              "",
    134              #"SSL_SERVER_M_SERIAL":               "",
    135              #"SSL_SERVER_V_START":               "",
    136              #"SSL_SERVER_V_END":                         "",
    137              #"SSL_SERVER_A_SIG":                         "",
    138              #"SSL_SERVER_A_KEY":                         "",
    139              #"SSL_SERVER_CERT":                  "",
    140              } )
    141
    142
    143          return ssl_environ
    144
    145 -    def makefile(self, sock, mode='r', bufsize=-1):
    146          return wsgiserver.CP_fileobject(sock, mode, bufsize)
    147