Ticket #254: ssl_options.patch

File ssl_options.patch, 31.0 KB (added by s0undt3ch, 4 years ago)
  • paste/httpserver.py

     
    2727from itertools import count 
    2828from BaseHTTPServer import BaseHTTPRequestHandler, HTTPServer 
    2929from SocketServer import ThreadingMixIn 
    30 from paste.util import converters 
     30from paste.util import converters, import_string 
    3131import logging 
    3232try: 
    3333    from paste.util import killthread 
     
    3535    # Not available, probably no ctypes 
    3636    killthread = None 
    3737 
     38 
     39from pprint import pprint 
     40 
    3841__all__ = ['WSGIHandlerMixin', 'WSGIServer', 'WSGIHandler', 'serve'] 
    3942__version__ = "0.5" 
    4043 
     
    190193                '100-continue' == self.headers.get('Expect','').lower(): 
    191194            rfile = ContinueHook(rfile, self.wfile.write) 
    192195        else: 
    193             # We can put in the protection to keep from over-reading the 
    194             # file 
     196            # We can put in the protection to keep from over-reading the file 
    195197            try: 
    196198                content_length = int(self.headers.get('Content-Length', '0')) 
    197199            except ValueError: 
     
    224226               # CGI not required by PEP-333 
    225227               ,'REMOTE_ADDR': remote_address 
    226228               } 
     229        if SSL: 
     230            self.wsgi_environ['SSL_VERSION_LIBRARY'] = __openssl_version__ 
     231            self.wsgi_environ['SSL_CLIENT_VERIFY'] = 'NONE' 
    227232        if scheme: 
    228233            self.wsgi_environ['paste.httpserver.proxy.scheme'] = scheme 
    229234        if netloc: 
     
    254259                continue 
    255260            self.wsgi_environ[key] = ','.join(self.headers.getheaders(k)) 
    256261 
     262 
    257263        if hasattr(self.connection,'get_context'): 
    258             self.wsgi_environ['wsgi.url_scheme'] = 'https' 
     264            env = self.wsgi_environ 
     265            env['wsgi.url_scheme'] = 'https' 
    259266            # @@: extract other SSL parameters from pyOpenSSL at... 
    260267            # http://www.modssl.org/docs/2.8/ssl_reference.html#ToC25 
     268            client_cert = self.connection.get_peer_certificate() 
     269            if client_cert: 
     270                env['SSL_CLIENT_CERT'] = crypto.dump_certificate( 
     271                    crypto.FILETYPE_PEM, client_cert 
     272                ) 
     273                env['SSL_CLIENT_M_VERSION'] = client_cert.get_version() 
     274                env['SSL_CLIENT_M_SERIAL'] = client_cert.get_serial_number() 
     275                try: 
     276                    env['SSL_CLIENT_V_START'] = client_cert.get_notBefore() 
     277                    env['SSL_CLIENT_V_END'] = client_cert.get_notAfter() 
     278                except AttributeError: 
     279                    # This is PyOpenSSL < 0.7 
     280                    pass 
     281                if self.connection.state_string().endswith('successfully'): 
     282                    env['SSL_CLIENT_VERIFY'] = 'SUCCESS' 
     283                else: 
     284                    env['SSL_CLIENT_VERIFY'] = 'FAILED' 
    261285 
    262286        if environ: 
    263287            assert isinstance(environ, dict) 
     
    314338# http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/442473 
    315339# 
    316340try: 
    317     from OpenSSL import SSL, tsafe 
     341    from OpenSSL import SSL, tsafe, __version__ as __openssl_version__, crypto 
    318342    SocketErrors = (socket.error, SSL.ZeroReturnError, SSL.SysCallError) 
    319343except ImportError: 
    320344    # Do not require pyOpenSSL to be installed, but disable SSL 
     
    327351            assert not ssl_context, "pyOpenSSL not installed" 
    328352            HTTPServer.__init__(self, server_address, RequestHandlerClass) 
    329353else: 
     354    import itertools, md5 
     355 
     356    # Private - shared between all OpenSSLCertificateOptions, counts up to provide 
     357    # a unique session id for each context 
     358    _sessionCounter = itertools.count().next 
     359 
     360    class _SSLApplicationData(object): 
     361        def __init__(self): 
     362            self.problems = [] 
     363 
     364 
     365    class OpenSSLVerifyError(Exception): 
     366 
     367        _errorCodes = {0: ('X509_V_OK', 
     368                           'ok', 
     369                           'the operation was successful. >'), 
     370 
     371                       2: ('X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT', 
     372                           'unable to get issuer certificate', 
     373                           "The issuer certificate could not be found.  This " 
     374                           "occurs if the issuer certificate of an untrusted " 
     375                           "certificate cannot be found."), 
     376 
     377                       3: ('X509_V_ERR_UNABLE_TO_GET_CRL', 
     378                           'unable to get certificate CRL', 
     379                           "The CRL of a certificate could not be found. " 
     380                           "Unused."), 
     381 
     382                       4: ('X509_V_ERR_UNABLE_TO_DECRYPT_CERT_SIGNATURE', 
     383                           "unable to decrypt certificate's signature", 
     384                           "The certificate signature could not be decrypted.  " 
     385                           "This means that the actual signature value could not " 
     386                           "be determined rather than it not matching the " 
     387                           "expected value, this is only meaningful for RSA " 
     388                           "keys."), 
     389 
     390                       5: ('X509_V_ERR_UNABLE_TO_DECRYPT_CRL_SIGNATURE', 
     391                           "unable to decrypt CRL's signature", 
     392                           "The CRL signature could not be decrypted.  This " 
     393                           "means that the actual signature value could not be " 
     394                           "determined rather than it not matching the expected " 
     395                           "value. Unused."), 
     396 
     397                       6: ('X509_V_ERR_UNABLE_TO_DECODE_ISSUER_PUBLIC_KEY', 
     398                           'unable to decode issuer', 
     399                           "Public key the public key in the certificate " 
     400                           "SubjectPublicKeyInfo could not be read."), 
     401 
     402                       7: ('X509_V_ERR_CERT_SIGNATURE_FAILURE', 
     403                           'certificate signature failure', 
     404                           'The signature of the certificate is invalid.'), 
     405 
     406                       8: ('X509_V_ERR_CRL_SIGNATURE_FAILURE', 
     407                           'CRL signature failure', 
     408                           'The signature of the certificate is invalid. Unused.'), 
     409 
     410                       9: ('X509_V_ERR_CERT_NOT_YET_VALID', 
     411                           'certificate is not yet valid', 
     412                           "The certificate is not yet valid.  The notBefore " 
     413                           "date is after the current time."), 
     414 
     415                       10: ('X509_V_ERR_CERT_HAS_EXPIRED', 
     416                            'certificate has expired', 
     417                            "The certificate has expired.  The notAfter date " 
     418                            "is before the current time."), 
     419 
     420                       11: ('X509_V_ERR_CRL_NOT_YET_VALID', 
     421                            'CRL is not yet valid', 
     422                            'The CRL is not yet valid. Unused.'), 
     423 
     424                       12: ('X509_V_ERR_CRL_HAS_EXPIRED', 
     425                            'CRL has expired', 
     426                            'The CRL has expired. Unused.'), 
     427 
     428                       13: ('X509_V_ERR_ERROR_IN_CERT_NOT_BEFORE_FIELD', 
     429                            "format error in certificate's notBefore field", 
     430                            "The certificate's notBefore field contains an " 
     431                            "invalid time."), 
     432 
     433                       14: ('X509_V_ERR_ERROR_IN_CERT_NOT_AFTER_FIELD', 
     434                            "format error in certificate's notAfter field.", 
     435                            "The certificate's notAfter field contains an " 
     436                            "invalid time."), 
     437 
     438                       15: ('X509_V_ERR_ERROR_IN_CRL_LAST_UPDATE_FIELD', 
     439                            "format error in CRL's lastUpdate field", 
     440                            "The CRL lastUpdate field contains an invalid " 
     441                            "time. Unused."), 
     442 
     443                       16: ('X509_V_ERR_ERROR_IN_CRL_NEXT_UPDATE_FIELD', 
     444                            "format error in CRL's nextUpdate field", 
     445                            "The CRL nextUpdate field contains an invalid " 
     446                            "time. Unused."), 
     447 
     448                       17: ('X509_V_ERR_OUT_OF_MEM', 
     449                            'out of memory', 
     450                            'An error occurred trying to allocate memory. ' 
     451                            'This should never happen.'), 
     452 
     453                       18: ('X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT', 
     454                            'self signed certificate', 
     455                            'The passed certificate is self signed and the same ' 
     456                            'certificate cannot be found in the list of trusted ' 
     457                            'certificates.'), 
     458 
     459                       19: ('X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN', 
     460                            'self signed certificate in certificate chain', 
     461                            'The certificate chain could be built up using the ' 
     462                            'untrusted certificates but the root could not be ' 
     463                            'found locally.'), 
     464 
     465                       20: ('X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY', 
     466                            'unable to get local issuer certificate', 
     467                            'The issuer certificate of a locally looked up ' 
     468                            'certificate could not be found. This normally ' 
     469                            'means the list of trusted certificates is not ' 
     470                            'complete.'), 
     471 
     472                       21: ('X509_V_ERR_UNABLE_TO_VERIFY_LEAF_SIGNATURE', 
     473                            'unable to verify the first certificate', 
     474                            'No signatures could be verified because the chain ' 
     475                            'contains only one certificate and it is not self ' 
     476                            'signed.'), 
     477 
     478                       22: ('X509_V_ERR_CERT_CHAIN_TOO_LONG', 
     479                            'certificate chain too long', 
     480                            'The certificate chain length is greater than the ' 
     481                            'supplied maximum depth. Unused.'), 
     482 
     483                       23: ('X509_V_ERR_CERT_REVOKED', 
     484                            'certificate revoked', 
     485                            'The certificate has been revoked. Unused.'), 
     486 
     487                       24: ('X509_V_ERR_INVALID_CA', 
     488                            'invalid CA certificate', 
     489                            'A CA certificate is invalid. Either it is not a CA ' 
     490                            'or its extensions are not consistent with the ' 
     491                            'supplied purpose.'), 
     492 
     493                       25: ('X509_V_ERR_PATH_LENGTH_EXCEEDED', 
     494                            'path length constraint exceeded', 
     495                            'The basicConstraints pathlength parameter has been ' 
     496                            'exceeded.'), 
     497 
     498                       26: ('X509_V_ERR_INVALID_PURPOSE', 
     499                            'unsupported certificate purpose', 
     500                            'The supplied certificate cannot be used for the ' 
     501                            'specified purpose.'), 
     502 
     503                       27: ('X509_V_ERR_CERT_UNTRUSTED', 
     504                            'certificate not trusted', 
     505                            'The root CA is not marked as trusted for the ' 
     506                            'specified purpose.'), 
     507 
     508                       28: ('X509_V_ERR_CERT_REJECTED', 
     509                            'certificate rejected', 
     510                            'The root CA is marked to reject the specified ' 
     511                            'purpose.'), 
     512 
     513                       29: ('X509_V_ERR_SUBJECT_ISSUER_MISMATCH', 
     514                            'subject issuer mismatch', 
     515                            'The current candidate issuer certificate was ' 
     516                            'rejected because its subject name did not match ' 
     517                            'the issuer name of the current certificate. Only ' 
     518                            'displayed when the issuer_checks option is set.'), 
     519 
     520                       30: ('X509_V_ERR_AKID_SKID_MISMATCH', 
     521                            'authority and subject key identifier mismatch', 
     522                            'The current candidate issuer certificate was ' 
     523                            'rejected because its subject key identifier was ' 
     524                            'present and did not match the authority key ' 
     525                            'identifier current certificate. Only displayed ' 
     526                            'when the issuer_checks option is set.'), 
     527 
     528                       31: ('X509_V_ERR_AKID_ISSUER_SERIAL_MISMATCH', 
     529                            'authority and issuer serial number mismatch', 
     530                            'The current candidate issuer certificate was ' 
     531                            'rejected because its issuer name and serial ' 
     532                            'number was present and did not match the ' 
     533                            'authority key identifier of the current ' 
     534                            'certificate. Only displayed when the issuer_checks ' 
     535                            'option is set.'), 
     536 
     537                       32: ('X509_V_ERR_KEYUSAGE_NO_CERTSIGN', 
     538                            'key usage does not include certificate', 
     539                            'Signing the current candidate issuer certificate was ' 
     540                            'rejected because its keyUsage extension does not ' 
     541                            'permit certificate signing.'), 
     542 
     543                       50: ('X509_V_ERR_APPLICATION_VERIFICATION', 
     544                            'application verification failure', 
     545                            'an application specific error. Unused.')} 
     546 
     547 
     548        def __init__(self, cert, errno, depth): 
     549            Exception.__init__(self, cert, errno, depth) 
     550            self.cert = cert 
     551            self.errno = errno 
     552            self.depth = depth 
     553 
     554        def __repr__(self): 
     555            x = self._errorCodes.get(self.errno) 
     556            if x is not None: 
     557                name, short, long = x 
     558                return 'Peer Certificate Verification Failed: %s (error code: %d)' \ 
     559                    % ( long, self.errno ) 
     560            return "Peer Certificate Verification Failed for Unknown Reason" 
     561 
     562        __str__ = __repr__ 
     563 
     564 
     565    class OpenSSLCertificateOptions(object): 
     566        """ 
     567        A factory for SSL context objects for both SSL servers and clients. 
     568        """ 
     569 
     570        _context = None 
     571        # Older versions of PyOpenSSL didn't provide OP_ALL. 
     572        # Fudge it here, just in case. 
     573        _OP_ALL = getattr(SSL, 'OP_ALL', 0x0000FFFF) 
     574 
     575        method = SSL.SSLv23_METHOD 
     576 
     577        def __init__(self, 
     578                     privateKey=None, 
     579                     privateKeyPasswd=None, 
     580                     certificate=None, 
     581                     method=None, 
     582                     verify=False, 
     583                     caCerts=None, 
     584                     verifyDepth=9, 
     585                     requireCertificate=True, 
     586                     verifyOnce=True, 
     587                     enableSingleUseKeys=True, 
     588                     enableSessions=True, 
     589                     fixBrokenPeers=False): 
     590            """ 
     591            Create an OpenSSL context SSL connection context factory. 
     592 
     593            ``ssl_privateKey`` 
     594 
     595                private key file path or PKey object. 
     596 
     597            ``ssl_privateKeyPasswd`` 
     598 
     599                A string holding the ssl_privateKey's password. 
     600 
     601            ``ssl_certificate`` 
     602 
     603                This an optional SSL certificate file path (via OpenSSL) or 
     604                X509 object. You can supply ``*`` and a development-only 
     605                certificate will be created for you, or you can generate a 
     606                self-signed test PEM certificate file as follows:: 
     607 
     608                    $ openssl genrsa 1024 > host.key 
     609                    $ chmod 400 host.key 
     610                    $ openssl req -new -x509 -nodes -sha1 -days 365  \\ 
     611                                  -key host.key > host.cert 
     612                    $ cat host.cert host.key > host.pem 
     613                    $ chmod 400 host.pem 
     614 
     615            ``ssl_method`` 
     616 
     617                The SSL protocol to use, one of SSLv23_METHOD, SSLv2_METHOD, 
     618                SSLv3_METHOD, TLSv1_METHOD.  Defaults to SSLv23_METHOD. 
     619 
     620            ``ssl_verify`` 
     621 
     622                If True, verify certificates received from the peer and fail the 
     623                handshake if verification fails.  Otherwise, allow anonymous 
     624                sessions and sessions with certificates which fail validation. 
     625                By default this is False. 
     626 
     627            ``ssl_caCerts`` 
     628 
     629                List of certificate authority certificates to send to the client 
     630                when requesting a certificate. Only used if verify is True. 
     631                Since verify is False by default, this is None by default. 
     632 
     633            ``ssl_verifyDepth`` 
     634 
     635                Depth in certificate chain down to which to verify. 
     636                If unspecified, use the underlying default (9). 
     637 
     638            ``ssl_requireCertificate`` 
     639 
     640                If True, do not allow anonymous sessions. 
     641 
     642            ``ssl_verifyOnce`` 
     643 
     644                If True, do not re-verify the certificate on session resumption. 
     645 
     646            ``ssl_enableSingleUseKeys`` 
     647 
     648                If True, generate a new key whenever ephemeral DH parameters are 
     649                used to prevent small subgroup attacks. 
     650 
     651            ``ssl_enableSessions`` 
     652 
     653                If True, set a session ID on each context.  This allows a 
     654                shortened handshake to be used when a known client reconnects. 
     655 
     656            ``fixBrokenPeers`` 
     657 
     658                If True, enable various non-spec protocol fixes for broken SSL 
     659                implementations.  This should be entirely safe, according to the 
     660                OpenSSL documentation, but YMMV. This option is now off by 
     661                default, because it causes problems with connections between 
     662                peers using OpenSSL 0.9.8a. 
     663            """ 
     664 
     665            assert (privateKey is None) == (certificate is None), \ 
     666                "Specify neither or both of privateKey and certificate" 
     667            self.privateKey = privateKey 
     668            self.privateKeyPasswd = privateKeyPasswd 
     669            self.certificate = certificate 
     670            if method is not None: 
     671                if method.lower().startswith('sslv23'): 
     672                    self.method = SSL.SSLv23_METHOD 
     673                if method.lower().startswith('sslv2'): 
     674                    self.method = SSL.SSLv2_METHOD 
     675                elif method.lower().startswith('sslv3'): 
     676                    self.method = SSL.SSLv3_METHOD 
     677                elif method.lower().startswith('tlsv1'): 
     678                    self.method = SSL.TLSv1_METHOD 
     679            else: 
     680                self.method = SSL.SSLv23_METHOD 
     681 
     682            self.verify = verify 
     683            assert ((verify and caCerts) or 
     684                    (not verify)), "Specify client CA certificate information" \ 
     685                                   " if and only if enabling certificate " \ 
     686                                   "verification" 
     687 
     688            if caCerts: 
     689                certs = [] 
     690                for caCert in caCerts: 
     691                    if isinstance(caCert, basestring): 
     692                        certs.append( 
     693                            crypto.load_certificate(crypto.FILETYPE_PEM, 
     694                                                    open(caCert).read()) 
     695                        ) 
     696                self.caCerts = certs 
     697            else: 
     698                self.caCerts = caCerts 
     699            self.verifyDepth = verifyDepth 
     700            self.requireCertificate = requireCertificate 
     701            self.verifyOnce = verifyOnce 
     702            self.enableSingleUseKeys = enableSingleUseKeys 
     703            self.enableSessions = enableSessions 
     704            self.fixBrokenPeers = fixBrokenPeers 
     705 
     706 
     707        def __getstate__(self): 
     708            d = self.__dict__.copy() 
     709            try: 
     710                del d['_context'] 
     711            except KeyError: 
     712                pass 
     713            return d 
     714 
     715 
     716        def __setstate__(self, state): 
     717            self.__dict__ = state 
     718 
     719 
     720        def getContext(self): 
     721            """Return a SSL.Context object. 
     722            """ 
     723            if self._context is None: 
     724                self._context = self._makeContext() 
     725            return self._context 
     726 
     727 
     728        def _makeContext(self): 
     729            ctx = SSL.Context(self.method) 
     730            ctx.set_app_data(_SSLApplicationData()) 
     731 
     732            if self.privateKeyPasswd: 
     733                ctx.set_passwd_cb(lambda a, b, c: self.privateKeyPasswd) 
     734 
     735            if self.certificate is not None and self.privateKey is not None: 
     736                try: 
     737                    ctx.use_certificate(self.certificate) 
     738                except: 
     739                    # Passed a filepath? 
     740                    ctx.use_certificate_file(self.certificate) 
     741                try: 
     742                    ctx.use_privatekey(self.privateKey) 
     743                except: 
     744                    # Passed a filepath? 
     745                    ctx.use_privatekey_file(self.privateKey) 
     746                # Sanity check 
     747                ctx.check_privatekey() 
     748 
     749            verifyFlags = SSL.VERIFY_NONE 
     750            if self.verify: 
     751                verifyFlags = SSL.VERIFY_PEER 
     752                if self.requireCertificate: 
     753                    verifyFlags |= SSL.VERIFY_FAIL_IF_NO_PEER_CERT 
     754                if self.verifyOnce: 
     755                    verifyFlags |= SSL.VERIFY_CLIENT_ONCE 
     756                if self.caCerts: 
     757                    store = ctx.get_cert_store() 
     758                    for cert in self.caCerts: 
     759                        store.add_cert(cert) 
     760                self.verifyFlags = verifyFlags 
     761 
     762            def _trackVerificationProblems(conn,cert,errno,depth,preverify_ok): 
     763                # retcode is the answer OpenSSL's default verifier would have 
     764                # given, had we allowed it to run. 
     765                if not preverify_ok: 
     766                    error = OpenSSLVerifyError(cert, errno, depth) 
     767                    ctx.get_app_data().problems.append(error) 
     768                return preverify_ok 
     769            ctx.set_verify(verifyFlags, _trackVerificationProblems) 
     770 
     771            if self.verifyDepth is not None: 
     772                ctx.set_verify_depth(self.verifyDepth) 
     773 
     774            if self.enableSingleUseKeys: 
     775                ctx.set_options(SSL.OP_SINGLE_DH_USE) 
     776 
     777            if self.fixBrokenPeers: 
     778                ctx.set_options(self._OP_ALL) 
     779 
     780            if self.enableSessions: 
     781                sessionName = md5.md5("%s-%d" % ( 
     782                    self.__class__.__module__ + '.' + self.__class__.__name__, 
     783                    _sessionCounter())).hexdigest() 
     784                ctx.set_session_id(sessionName) 
     785 
     786            return ctx 
    330787 
    331788    class _ConnFixer(object): 
    332789        """ wraps a socket connection so it implements makefile """ 
     
    380837                conn = _ConnFixer(conn) 
    381838            return (conn, info) 
    382839 
    383     def _auto_ssl_context(): 
    384         import OpenSSL, time, random 
    385         pkey = OpenSSL.crypto.PKey() 
    386         pkey.generate_key(OpenSSL.crypto.TYPE_RSA, 768) 
    387  
    388         cert = OpenSSL.crypto.X509() 
    389  
    390         cert.set_serial_number(random.randint(0, sys.maxint)) 
    391         cert.gmtime_adj_notBefore(0) 
    392         cert.gmtime_adj_notAfter(60 * 60 * 24 * 365) 
    393         cert.get_subject().CN = '*' 
    394         cert.get_subject().O = 'Dummy Certificate' 
    395         cert.get_issuer().CN = 'Untrusted Authority' 
    396         cert.get_issuer().O = 'Self-Signed' 
    397         cert.set_pubkey(pkey) 
    398         cert.sign(pkey, 'md5') 
    399  
    400         ctx = SSL.Context(SSL.SSLv23_METHOD) 
    401         ctx.use_privatekey(pkey) 
    402         ctx.use_certificate(cert) 
    403  
    404         return ctx 
    405840 
    406841class WSGIHandler(WSGIHandlerMixin, BaseHTTPRequestHandler): 
    407842    """ 
     
    424859            return 
    425860        if not self.parse_request(): # An error code has been sent, just exit 
    426861            return 
     862        if self.headers.get('x-renegotiate-ssl', None): 
     863            # If the response to the client contains X-Renegotiate-SSL header 
     864            # the SSL connection is re-negotiated. 
     865            self.connection.renegotiate() 
    427866        self.wsgi_execute() 
    428867 
    429868    def handle(self): 
    430869        # don't bother logging disconnects while handling a request 
    431870        try: 
    432871            BaseHTTPRequestHandler.handle(self) 
     872        except (SSL.WantReadError, SSL.WantWriteError, SSL.WantX509LookupError): 
     873            pass 
     874        except SSL.Error, error: 
     875            self.wsgi_connection_drop(error) 
    433876        except SocketErrors, exce: 
    434877            self.wsgi_connection_drop(exce) 
    435878 
     
    456899            length = left 
    457900        else: 
    458901            length = min(length, left) 
    459         # next two lines are hnecessary only if read(0) blocks 
     902        # next two lines are necessary only if read(0) blocks 
    460903        if not left: 
    461904            return '' 
    462905        data = self.file.read(length) 
     
    11261569    caught) 
    11271570    """ 
    11281571 
    1129 def serve(application, host=None, port=None, handler=None, ssl_pem=None, 
    1130           ssl_context=None, server_version=None, protocol_version=None, 
    1131           start_loop=True, daemon_threads=None, socket_timeout=None, 
    1132           use_threadpool=None, threadpool_workers=10, 
    1133           threadpool_options=None): 
     1572 
     1573def serve(application, host=None, port=None, handler=None, ssl_options=None, 
     1574          server_version=None, protocol_version=None, start_loop=True, 
     1575          daemon_threads=None, socket_timeout=None, use_threadpool=None, 
     1576          threadpool_workers=10, threadpool_options=None): 
    11341577    """ 
    11351578    Serves your ``application`` over HTTP(S) via WSGI interface 
    11361579 
     
    11641607            $ cat host.cert host.key > host.pem 
    11651608            $ chmod 400 host.pem 
    11661609 
    1167     ``ssl_context`` 
    11681610 
    1169         This an optional SSL context object for the server.  A SSL 
    1170         context will be automatically constructed for you if you supply 
    1171         ``ssl_pem``.  Supply this to use a context of your own 
    1172         construction. 
    11731611 
    11741612    ``server_version`` 
    11751613 
     
    12261664        threadpool.  See paste.httpserver.ThreadPool for specific 
    12271665        options (``threadpool_workers`` is a specific option that can 
    12281666        also go here). 
     1667 
     1668    ``ssl_privateKey`` 
     1669 
     1670        private key file path. 
     1671 
     1672    ``ssl_privateKeyPasswd`` 
     1673 
     1674        A string holding the ssl_privateKey's password. 
     1675 
     1676    ``ssl_certificate`` 
     1677 
     1678        This an optional SSL certificate file (via OpenSSL). You can 
     1679        supply ``*`` and a development-only certificate will be 
     1680        created for you, or you can generate a self-signed test PEM 
     1681        certificate file as follows:: 
     1682 
     1683            $ openssl genrsa 1024 > host.key 
     1684            $ chmod 400 host.key 
     1685            $ openssl req -new -x509 -nodes -sha1 -days 365  \\ 
     1686                          -key host.key > host.cert 
     1687            $ cat host.cert host.key > host.pem 
     1688            $ chmod 400 host.pem 
     1689 
     1690    ``ssl_method`` 
     1691 
     1692        The SSL protocol to use, one of SSLv23_METHOD, SSLv2_METHOD, 
     1693        SSLv3_METHOD, TLSv1_METHOD.  Defaults to SSLv23_METHOD. 
     1694 
     1695    ``ssl_verify`` 
     1696 
     1697        If True, verify certificates received from the peer and fail the 
     1698        handshake if verification fails.  Otherwise, allow anonymous sessions 
     1699        and sessions with certificates which fail validation.  By default this 
     1700        is False. 
     1701 
     1702    ``ssl_caCerts`` 
     1703 
     1704        List of certificate authority certificates to send to the client when 
     1705        requesting a certificate.  Only used if verify is True.  Since verify 
     1706        is False by default, this is None by default. 
     1707 
     1708    ``ssl_verifyDepth`` 
     1709 
     1710        Depth in certificate chain down to which to verify. 
     1711        If unspecified, use the underlying default (9). 
     1712 
     1713    ``ssl_requireCertificate`` 
     1714 
     1715        If True, do not allow anonymous sessions. 
     1716 
     1717    ``ssl_verifyOnce`` 
     1718 
     1719        If True, do not re-verify the certificate on session resumption. 
     1720 
     1721    ``ssl_enableSingleUseKeys`` 
     1722 
     1723        If True, generate a new key whenever ephemeral DH parameters are used 
     1724        to prevent small subgroup attacks. 
     1725 
     1726    ``ssl_enableSessions`` 
     1727 
     1728        If True, set a session ID on each context.  This allows a shortened 
     1729        handshake to be used when a known client reconnects. 
     1730 
     1731    ``fixBrokenPeers`` 
     1732 
     1733        If True, enable various non-spec protocol fixes for broken SSL 
     1734        implementations.  This should be entirely safe, according to the OpenSSL 
     1735        documentation, but YMMV.  This option is now off by default, because it 
     1736        causes problems with connections between peers using OpenSSL 0.9.8a. 
     1737 
    12291738    """ 
    12301739    is_ssl = False 
    1231     if ssl_pem or ssl_context: 
     1740    if ssl_options: 
    12321741        assert SSL, "pyOpenSSL is not installed" 
    12331742        is_ssl = True 
    12341743        port = int(port or 4443) 
    1235         if not ssl_context: 
    1236             if ssl_pem == '*': 
    1237                 ssl_context = _auto_ssl_context() 
    1238             else: 
    1239                 ssl_context = SSL.Context(SSL.SSLv23_METHOD) 
    1240                 ssl_context.use_privatekey_file(ssl_pem) 
    1241                 ssl_context.use_certificate_chain_file(ssl_pem) 
     1744        if ssl_options.get('certificate') == '*': 
     1745            from OpenSSL import crypto 
     1746            import random 
     1747            pkey = crypto.PKey() 
     1748            pkey.generate_key(crypto.TYPE_RSA, 768) 
     1749 
     1750            cert = crypto.X509() 
     1751 
     1752            cert.set_serial_number(random.randint(0, sys.maxint)) 
     1753            cert.gmtime_adj_notBefore(0) 
     1754            cert.gmtime_adj_notAfter(60 * 60 * 24 * 365) 
     1755            cert.get_subject().CN = '*' 
     1756            cert.get_subject().O = 'Dummy Certificate' 
     1757            cert.get_issuer().CN = 'Untrusted Authority' 
     1758            cert.get_issuer().O = 'Self-Signed' 
     1759            cert.set_pubkey(pkey) 
     1760            cert.sign(pkey, 'md5') 
     1761            ssl_options['privateKey'] = pkey 
     1762            ssl_options['certificate'] = cert 
     1763 
     1764        ctxopts = OpenSSLCertificateOptions(**ssl_options) 
     1765        ssl_context = ctxopts.getContext() 
    12421766 
    12431767    host = host or '127.0.0.1' 
    12441768    if port is None: 
     
    13051829    for name in ['use_threadpool', 'daemon_threads']: 
    13061830        if name in kwargs: 
    13071831            kwargs[name] = asbool(kwargs[name]) 
     1832    ssl_options = {} 
    13081833    threadpool_options = {} 
    13091834    for name, value in kwargs.items(): 
    13101835        if name.startswith('threadpool_') and name != 'threadpool_workers': 
    13111836            threadpool_options[name[len('threadpool_'):]] = value 
    13121837            del kwargs[name] 
     1838        elif name.startswith('ssl_'): 
     1839            ctxopt = name[len('ssl_'):] 
     1840            if ctxopt in ('verify', 'requireCertificate', 'verifyOnce', 
     1841                          'enableSingleUseKeys', 'enableSessions', 
     1842                          'fixBrokenPeers'): 
     1843                ssl_options[ctxopt] = asbool(value) 
     1844            elif ctxopt == 'verifyDepth': 
     1845                ssl_options[ctxopt] = int(value) 
     1846            elif ctxopt == 'caCerts': 
     1847                ssl_options[ctxopt] = converters.aslist(value) 
     1848            else: 
     1849                ssl_options[ctxopt] = value 
     1850            del kwargs[name] 
    13131851    if ('error_email' not in threadpool_options 
    13141852        and 'error_email' in global_conf): 
    13151853        threadpool_options['error_email'] = global_conf['error_email'] 
    13161854    kwargs['threadpool_options'] = threadpool_options 
     1855    kwargs['ssl_options'] = ssl_options 
    13171856    serve(wsgi_app, **kwargs) 
    13181857 
    13191858server_runner.__doc__ = (serve.__doc__ or '') + """