| 1450 | | ssl_certificate: the filename of the server SSL certificate. |
|---|
| 1451 | | ssl_privatekey: the filename of the server's private key file. |
|---|
| 1452 | | |
|---|
| 1453 | | If either of these is None (both are None by default), this server |
|---|
| 1454 | | will not use SSL. If both are given and are valid, they will be read |
|---|
| 1455 | | on server start and used in the SSL context for the listening socket. |
|---|
| | 1450 | There are two ways to use SSL: |
|---|
| | 1451 | |
|---|
| | 1452 | Method One: |
|---|
| | 1453 | ssl_context: an instance of SSL.Context. |
|---|
| | 1454 | |
|---|
| | 1455 | If this is not None, it is assumed to be an SSL.Context instance, |
|---|
| | 1456 | and will be passed to SSL.Connection on bind(). The developer is |
|---|
| | 1457 | responsible for forming a valid Context object. This approach is |
|---|
| | 1458 | to be preferred for more flexibility, e.g. if the cert and key are |
|---|
| | 1459 | streams instead of files, or need decryption, or SSL.SSLv3_METHOD |
|---|
| | 1460 | is desired instead of the default SSL.SSLv23_METHOD, etc. Consult |
|---|
| | 1461 | the pyOpenSSL documentation for complete options. |
|---|
| | 1462 | |
|---|
| | 1463 | Method Two (shortcut): |
|---|
| | 1464 | ssl_certificate: the filename of the server SSL certificate. |
|---|
| | 1465 | ssl_privatekey: the filename of the server's private key file. |
|---|
| | 1466 | |
|---|
| | 1467 | Both are None by default. If ssl_context is None, but ssl_privatekey |
|---|
| | 1468 | and ssl_certificate are both given and valid, they will be read on |
|---|
| | 1469 | server start, and self.ssl_context will be automatically created |
|---|
| | 1470 | from them. |
|---|
| 1616 | | ctx = SSL.Context(SSL.SSLv23_METHOD) |
|---|
| 1617 | | ctx.use_privatekey_file(self.ssl_private_key) |
|---|
| 1618 | | ctx.use_certificate_file(self.ssl_certificate) |
|---|
| 1619 | | self.socket = SSLConnection(ctx, self.socket) |
|---|
| | 1636 | self.ssl_context = SSL.Context(SSL.SSLv23_METHOD) |
|---|
| | 1637 | self.ssl_context.use_privatekey_file(self.ssl_private_key) |
|---|
| | 1638 | self.ssl_context.use_certificate_file(self.ssl_certificate) |
|---|
| | 1639 | |
|---|
| | 1640 | if self.ssl_context is not None: |
|---|
| | 1641 | self.socket = SSLConnection(self.ssl_context, self.socket) |
|---|
| 1621 | | |
|---|
| 1622 | | # If listening on the IPV6 any address ('::' = IN6ADDR_ANY), |
|---|
| 1623 | | # activate dual-stack. See http://www.cherrypy.org/ticket/871. |
|---|
| 1624 | | if (not isinstance(self.bind_addr, basestring) |
|---|
| 1625 | | and self.bind_addr[0] == '::' and family == socket.AF_INET6): |
|---|
| 1626 | | try: |
|---|
| 1627 | | self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) |
|---|
| 1628 | | except (AttributeError, socket.error): |
|---|
| 1629 | | # Apparently, the socket option is not available in |
|---|
| 1630 | | # this machine's TCP stack |
|---|
| 1631 | | pass |
|---|
| | 1643 | |
|---|
| | 1644 | # If listening on the IPV6 any address ('::' = IN6ADDR_ANY), |
|---|
| | 1645 | # activate dual-stack. See http://www.cherrypy.org/ticket/871. |
|---|
| | 1646 | if (not isinstance(self.bind_addr, basestring) |
|---|
| | 1647 | and self.bind_addr[0] == '::' and family == socket.AF_INET6): |
|---|
| | 1648 | try: |
|---|
| | 1649 | self.socket.setsockopt(socket.IPPROTO_IPV6, socket.IPV6_V6ONLY, 0) |
|---|
| | 1650 | except (AttributeError, socket.error): |
|---|
| | 1651 | # Apparently, the socket option is not available in |
|---|
| | 1652 | # this machine's TCP stack |
|---|
| | 1653 | pass |
|---|
| 1753 | | # Server certificate attributes |
|---|
| 1754 | | ssl_environ.update({ |
|---|
| 1755 | | 'SSL_SERVER_M_VERSION': cert.get_version(), |
|---|
| 1756 | | 'SSL_SERVER_M_SERIAL': cert.get_serial_number(), |
|---|
| 1757 | | ## 'SSL_SERVER_V_START': Validity of server's certificate (start time), |
|---|
| 1758 | | ## 'SSL_SERVER_V_END': Validity of server's certificate (end time), |
|---|
| 1759 | | }) |
|---|
| 1760 | | |
|---|
| 1761 | | for prefix, dn in [("I", cert.get_issuer()), |
|---|
| 1762 | | ("S", cert.get_subject())]: |
|---|
| 1763 | | # X509Name objects don't seem to have a way to get the |
|---|
| 1764 | | # complete DN string. Use str() and slice it instead, |
|---|
| 1765 | | # because str(dn) == "<X509Name object '/C=US/ST=...'>" |
|---|
| 1766 | | dnstr = str(dn)[18:-2] |
|---|
| | 1773 | if self.ssl_certificate: |
|---|
| | 1774 | # Server certificate attributes |
|---|
| | 1775 | cert = open(self.ssl_certificate, 'rb').read() |
|---|
| | 1776 | cert = crypto.load_certificate(crypto.FILETYPE_PEM, cert) |
|---|
| | 1777 | ssl_environ.update({ |
|---|
| | 1778 | 'SSL_SERVER_M_VERSION': cert.get_version(), |
|---|
| | 1779 | 'SSL_SERVER_M_SERIAL': cert.get_serial_number(), |
|---|
| | 1780 | ## 'SSL_SERVER_V_START': Validity of server's certificate (start time), |
|---|
| | 1781 | ## 'SSL_SERVER_V_END': Validity of server's certificate (end time), |
|---|
| | 1782 | }) |
|---|
| 1768 | | wsgikey = 'SSL_SERVER_%s_DN' % prefix |
|---|
| 1769 | | ssl_environ[wsgikey] = dnstr |
|---|
| 1770 | | |
|---|
| 1771 | | # The DN should be of the form: /k1=v1/k2=v2, but we must allow |
|---|
| 1772 | | # for any value to contain slashes itself (in a URL). |
|---|
| 1773 | | while dnstr: |
|---|
| 1774 | | pos = dnstr.rfind("=") |
|---|
| 1775 | | dnstr, value = dnstr[:pos], dnstr[pos + 1:] |
|---|
| 1776 | | pos = dnstr.rfind("/") |
|---|
| 1777 | | dnstr, key = dnstr[:pos], dnstr[pos + 1:] |
|---|
| 1778 | | if key and value: |
|---|
| 1779 | | wsgikey = 'SSL_SERVER_%s_DN_%s' % (prefix, key) |
|---|
| 1780 | | ssl_environ[wsgikey] = value |
|---|
| | 1784 | for prefix, dn in [("I", cert.get_issuer()), |
|---|
| | 1785 | ("S", cert.get_subject())]: |
|---|
| | 1786 | # X509Name objects don't seem to have a way to get the |
|---|
| | 1787 | # complete DN string. Use str() and slice it instead, |
|---|
| | 1788 | # because str(dn) == "<X509Name object '/C=US/ST=...'>" |
|---|
| | 1789 | dnstr = str(dn)[18:-2] |
|---|
| | 1790 | |
|---|
| | 1791 | wsgikey = 'SSL_SERVER_%s_DN' % prefix |
|---|
| | 1792 | ssl_environ[wsgikey] = dnstr |
|---|
| | 1793 | |
|---|
| | 1794 | # The DN should be of the form: /k1=v1/k2=v2, but we must allow |
|---|
| | 1795 | # for any value to contain slashes itself (in a URL). |
|---|
| | 1796 | while dnstr: |
|---|
| | 1797 | pos = dnstr.rfind("=") |
|---|
| | 1798 | dnstr, value = dnstr[:pos], dnstr[pos + 1:] |
|---|
| | 1799 | pos = dnstr.rfind("/") |
|---|
| | 1800 | dnstr, key = dnstr[:pos], dnstr[pos + 1:] |
|---|
| | 1801 | if key and value: |
|---|
| | 1802 | wsgikey = 'SSL_SERVER_%s_DN_%s' % (prefix, key) |
|---|
| | 1803 | ssl_environ[wsgikey] = value |
|---|