diff --git a/Doc/library/ssl.rst b/Doc/library/ssl.rst index c115976..84dd332 100644 --- a/Doc/library/ssl.rst +++ b/Doc/library/ssl.rst @@ -28,19 +28,14 @@ probably additional platforms, as long as OpenSSL is installed on that platform. Some behavior may be platform dependent, since calls are made to the operating system socket APIs. The installed version of OpenSSL may also - cause variations in behavior. + cause variations in behavior. For example, TLSv1.1 and TLSv1.2 come with + openssl version 1.0.1. .. warning:: - The ssl module won't validate certificates by default. When used in - client mode, this means you are vulnerable to man-in-the-middle attacks. + Don't use this module without reading the :ref:`ssl-security`. Doing so + may lead to a false sense of security, as the default settings of the + ssl module are not necessarily appropriate for your application. -.. warning:: - - OpenSSL's internal random number generator does not properly handle fork. - Applications must change the PRNG state of the parent process if they use - any SSL feature with :func:`os.fork`. Any successful call of - :func:`~ssl.RAND_add`, :func:`~ssl.RAND_bytes` or - :func:`~ssl.RAND_pseudo_bytes` is sufficient. This section documents the objects and functions in the ``ssl`` module; for more general information about TLS, SSL, and certificates, the reader is referred to @@ -49,23 +44,101 @@ the documents in the "See Also" section at the bottom. This module provides a class, :class:`ssl.SSLSocket`, which is derived from the :class:`socket.socket` type, and provides a socket-like wrapper that also encrypts and decrypts the data going over the socket with SSL. It supports -additional :meth:`read` and :meth:`write` methods, along with a method, -:meth:`getpeercert`, to retrieve the certificate of the other side of the -connection, and a method, :meth:`cipher`, to retrieve the cipher being used for -the secure connection. +additional methods such as :meth:`getpeercert`, which retrieves the +certificate of the other side of the connection, and :meth:`cipher`,which +retrieves the cipher being used for the secure connection. + +For more sophisticated applications, the :class:`ssl.SSLContext` class +helps manage settings and certificates, which can then be inherited +by SSL sockets created through the :meth:`SSLContext.wrap_socket` method. + Functions, Constants, and Exceptions ------------------------------------ .. exception:: SSLError - Raised to signal an error from the underlying SSL implementation. This - signifies some problem in the higher-level encryption and authentication - layer that's superimposed on the underlying network connection. This error - is a subtype of :exc:`socket.error`, which in turn is a subtype of - :exc:`IOError`. + Raised to signal an error from the underlying SSL implementation (currently + provided by the OpenSSL library). This signifies some problem in the + higher-level encryption and authentication layer that's superimposed on the + underlying network connection. This error is a subtype of + :exc:`socket.error`, which in turn is a subtype of :exc:`IOError`. The + error code and message of :exc:`SSLError` instances are provided by the + OpenSSL library. + + .. attribute:: library + + A string mnemonic designating the OpenSSL submodule in which the error + occurred, such as ``SSL``, ``PEM`` or ``X509``. The range of possible + values depends on the OpenSSL version. + + .. versionadded:: 2.7.9 + + .. attribute:: reason + + A string mnemonic designating the reason this error occurred, for + example ``CERTIFICATE_VERIFY_FAILED``. The range of possible + values depends on the OpenSSL version. + + .. versionadded:: 2.7.9 + +.. exception:: SSLZeroReturnError + + A subclass of :exc:`SSLError` raised when trying to read or write and + the SSL connection has been closed cleanly. Note that this doesn't + mean that the underlying transport (read TCP) has been closed. + + .. versionadded:: 2.7.9 + +.. exception:: SSLWantReadError + + A subclass of :exc:`SSLError` raised by a :ref:`non-blocking SSL socket + ` when trying to read or write data, but more data needs + to be received on the underlying TCP transport before the request can be + fulfilled. + + .. versionadded:: 2.7.9 + +.. exception:: SSLWantWriteError + + A subclass of :exc:`SSLError` raised by a :ref:`non-blocking SSL socket + ` when trying to read or write data, but more data needs + to be sent on the underlying TCP transport before the request can be + fulfilled. -.. function:: wrap_socket (sock, keyfile=None, certfile=None, server_side=False, cert_reqs=CERT_NONE, ssl_version={see docs}, ca_certs=None, do_handshake_on_connect=True, suppress_ragged_eofs=True, ciphers=None) + .. versionadded:: 2.7.9 + +.. exception:: SSLSyscallError + + A subclass of :exc:`SSLError` raised when a system error was encountered + while trying to fulfill an operation on a SSL socket. Unfortunately, + there is no easy way to inspect the original errno number. + + .. versionadded:: 2.7.9 + +.. exception:: SSLEOFError + + A subclass of :exc:`SSLError` raised when the SSL connection has been + terminated abruptly. Generally, you shouldn't try to reuse the underlying + transport when this error is encountered. + + .. versionadded:: 2.7.9 + +.. exception:: CertificateError + + Raised to signal an error with a certificate (such as mismatching + hostname). Certificate errors detected by OpenSSL, though, raise + an :exc:`SSLError`. + + +Socket creation +^^^^^^^^^^^^^^^ + +The following function allows for standalone socket creation. Starting from +Python 2.7.9, it can be more flexible to use :meth:`SSLContext.wrap_socket` +instead. + +.. function:: wrap_socket(sock, keyfile=None, certfile=None, server_side=False, cert_reqs=CERT_NONE, ssl_version={see docs}, ca_certs=None, do_handshake_on_connect=True, suppress_ragged_eofs=True, ciphers=None) Takes an instance ``sock`` of :class:`socket.socket`, and returns an instance of :class:`ssl.SSLSocket`, a subtype of :class:`socket.socket`, which wraps @@ -85,19 +158,6 @@ Functions, Constants, and Exceptions connection. See the discussion of :ref:`ssl-certificates` for more information on how the certificate is stored in the ``certfile``. - Often the private key is stored in the same file as the certificate; in this - case, only the ``certfile`` parameter need be passed. If the private key is - stored in a separate file, both parameters must be used. If the private key - is stored in the ``certfile``, it should come before the first certificate in - the certificate chain:: - - -----BEGIN RSA PRIVATE KEY----- - ... (private key in base64 encoding) ... - -----END RSA PRIVATE KEY----- - -----BEGIN CERTIFICATE----- - ... (certificate in base64 PEM encoding) ... - -----END CERTIFICATE----- - The parameter ``server_side`` is a boolean which identifies whether server-side or client-side behavior is desired from this socket. @@ -127,14 +187,16 @@ Functions, Constants, and Exceptions .. table:: - ======================== ========= ========= ========== ========= - *client* / **server** **SSLv2** **SSLv3** **SSLv23** **TLSv1** - ------------------------ --------- --------- ---------- --------- - *SSLv2* yes no yes no - *SSLv3* no yes yes no - *SSLv23* yes no yes no - *TLSv1* no no yes yes - ======================== ========= ========= ========== ========= + ======================== ========= ========= ========== ========= =========== =========== + *client* / **server** **SSLv2** **SSLv3** **SSLv23** **TLSv1** **TLSv1.1** **TLSv1.2** + ------------------------ --------- --------- ---------- --------- ----------- ----------- + *SSLv2* yes no yes no no no + *SSLv3* no yes yes no no no + *SSLv23* yes no yes no no no + *TLSv1* no no yes yes no no + *TLSv1.1* no no yes no yes no + *TLSv1.2* no no yes no no yes + ======================== ========= ========= ========== ========= =========== =========== .. note:: @@ -161,22 +223,79 @@ Functions, Constants, and Exceptions The parameter ``suppress_ragged_eofs`` specifies how the :meth:`SSLSocket.read` method should signal unexpected EOF from the other end of the connection. If specified as :const:`True` (the default), it returns a - normal EOF in response to unexpected EOF errors raised from the underlying - socket; if :const:`False`, it will raise the exceptions back to the caller. + normal EOF (an empty bytes object) in response to unexpected EOF errors + raised from the underlying socket; if :const:`False`, it will raise the + exceptions back to the caller. .. versionchanged:: 2.7 New optional argument *ciphers*. + +Context creation +^^^^^^^^^^^^^^^^ + +A convenience function helps create :class:`SSLContext` objects for common +purposes. + +.. function:: create_default_context(purpose=Purpose.SERVER_AUTH, cafile=None, capath=None, cadata=None) + + Return a new :class:`SSLContext` object with default settings for + the given *purpose*. The settings are chosen by the :mod:`ssl` module, + and usually represent a higher security level than when calling the + :class:`SSLContext` constructor directly. + + *cafile*, *capath*, *cadata* represent optional CA certificates to + trust for certificate verification, as in + :meth:`SSLContext.load_verify_locations`. If all three are + :const:`None`, this function can choose to trust the system's default + CA certificates instead. + + The settings in Python 2.7.9 are: :data:`PROTOCOL_SSLv23`, + :data:`OP_NO_SSLv2`, and :data:`OP_NO_SSLv3` with high encryption cipher + suites without RC4 and without unauthenticated cipher suites. Passing + :data:`~Purpose.SERVER_AUTH` as *purpose* sets + :data:`~SSLContext.verify_mode` to :data:`CERT_REQUIRED` and either loads CA + certificates (when at least one of *cafile*, *capath* or *cadata* is given) + or uses :meth:`SSLContext.load_default_certs` to load default CA + certificates. + + .. note:: + The protocol, options, cipher and other settings may change to more + restrictive values anytime without prior deprecation. The values + represent a fair balance between compatibility and security. + + If your application needs specific settings, you should create a + :class:`SSLContext` and apply the settings yourself. + + .. note:: + If you find that when certain older clients or servers attempt to connect + with a :class:`SSLContext` created by this function that they get an + error stating "Protocol or cipher suite mismatch", it may be that they + only support SSL3.0 which this function excludes using the + :data:`OP_NO_SSLv3`. SSL3.0 has problematic security due to a number of + poor implementations and it's reliance on MD5 within the protocol. If you + wish to continue to use this function but still allow SSL 3.0 connections + you can re-enable them using:: + + ctx = ssl.create_default_context(Purpose.CLIENT_AUTH) + ctx.options &= ~ssl.OP_NO_SSLv3 + + .. versionadded:: 2.7.9 + + +Random generation +^^^^^^^^^^^^^^^^^ + .. function:: RAND_status() Returns ``True`` if the SSL pseudo-random number generator has been seeded with - 'enough' randomness, and False otherwise. You can use :func:`ssl.RAND_egd` + 'enough' randomness, and ``False`` otherwise. You can use :func:`ssl.RAND_egd` and :func:`ssl.RAND_add` to increase the randomness of the pseudo-random number generator. .. function:: RAND_egd(path) - If you are running an entropy-gathering daemon (EGD) somewhere, and ``path`` + If you are running an entropy-gathering daemon (EGD) somewhere, and *path* is the pathname of a socket connection open to it, this will read 256 bytes of randomness from the socket, and add it to the SSL pseudo-random number generator to increase the security of generated secret keys. This is @@ -187,28 +306,66 @@ Functions, Constants, and Exceptions .. function:: RAND_add(bytes, entropy) - Mixes the given ``bytes`` into the SSL pseudo-random number generator. The - parameter ``entropy`` (a float) is a lower bound on the entropy contained in + Mixes the given *bytes* into the SSL pseudo-random number generator. The + parameter *entropy* (a float) is a lower bound on the entropy contained in string (so you can always use :const:`0.0`). See :rfc:`1750` for more information on sources of entropy. -.. function:: cert_time_to_seconds(timestring) +Certificate handling +^^^^^^^^^^^^^^^^^^^^ + +.. function:: match_hostname(cert, hostname) + + Verify that *cert* (in decoded format as returned by + :meth:`SSLSocket.getpeercert`) matches the given *hostname*. The rules + applied are those for checking the identity of HTTPS servers as outlined + in :rfc:`2818` and :rfc:`6125`, except that IP addresses are not currently + supported. In addition to HTTPS, this function should be suitable for + checking the identity of servers in various SSL-based protocols such as + FTPS, IMAPS, POPS and others. + + :exc:`CertificateError` is raised on failure. On success, the function + returns nothing:: - Returns a floating-point value containing a normal seconds-after-the-epoch - time value, given the time-string representing the "notBefore" or "notAfter" - date from a certificate. + >>> cert = {'subject': ((('commonName', 'example.com'),),)} + >>> ssl.match_hostname(cert, "example.com") + >>> ssl.match_hostname(cert, "example.org") + Traceback (most recent call last): + File "", line 1, in + File "/home/py3k/Lib/ssl.py", line 130, in match_hostname + ssl.CertificateError: hostname 'example.org' doesn't match 'example.com' - Here's an example:: + .. versionadded:: 2.7.9 - >>> import ssl - >>> ssl.cert_time_to_seconds("May 9 00:00:00 2007 GMT") - 1178694000.0 - >>> import time - >>> time.ctime(ssl.cert_time_to_seconds("May 9 00:00:00 2007 GMT")) - 'Wed May 9 00:00:00 2007' - >>> -.. function:: get_server_certificate (addr, ssl_version=PROTOCOL_SSLv3, ca_certs=None) +.. function:: cert_time_to_seconds(cert_time) + + Return the time in seconds since the Epoch, given the ``cert_time`` + string representing the "notBefore" or "notAfter" date from a + certificate in ``"%b %d %H:%M:%S %Y %Z"`` strptime format (C + locale). + + Here's an example: + + .. doctest:: newcontext + + >>> import ssl + >>> timestamp = ssl.cert_time_to_seconds("Jan 5 09:34:43 2018 GMT") + >>> timestamp + 1515144883 + >>> from datetime import datetime + >>> print(datetime.utcfromtimestamp(timestamp)) + 2018-01-05 09:34:43 + + "notBefore" or "notAfter" dates must use GMT (:rfc:`5280`). + + .. versionchanged:: 2.7.9 + Interpret the input time as a time in UTC as specified by 'GMT' + timezone in the input string. Local timezone was used + previously. Return an integer (no fractions of a second in the + input format) + +.. function:: get_server_certificate(addr, ssl_version=PROTOCOL_SSLv23, ca_certs=None) Given the address ``addr`` of an SSL-protected server, as a (*hostname*, *port-number*) pair, fetches the server's certificate, and returns it as a @@ -219,36 +376,144 @@ Functions, Constants, and Exceptions will attempt to validate the server certificate against that set of root certificates, and will fail if the validation attempt fails. -.. function:: DER_cert_to_PEM_cert (DER_cert_bytes) + .. versionchanged:: 2.7.9 + + This function is now IPv6-compatible, and the default *ssl_version* is + changed from :data:`PROTOCOL_SSLv3` to :data:`PROTOCOL_SSLv23` for + maximum compatibility with modern servers. + +.. function:: DER_cert_to_PEM_cert(DER_cert_bytes) Given a certificate as a DER-encoded blob of bytes, returns a PEM-encoded string version of the same certificate. -.. function:: PEM_cert_to_DER_cert (PEM_cert_string) +.. function:: PEM_cert_to_DER_cert(PEM_cert_string) Given a certificate as an ASCII PEM string, returns a DER-encoded sequence of bytes for that same certificate. +.. function:: get_default_verify_paths() + + Returns a named tuple with paths to OpenSSL's default cafile and capath. + The paths are the same as used by + :meth:`SSLContext.set_default_verify_paths`. The return value is a + :term:`named tuple` ``DefaultVerifyPaths``: + + * :attr:`cafile` - resolved path to cafile or None if the file doesn't exist, + * :attr:`capath` - resolved path to capath or None if the directory doesn't exist, + * :attr:`openssl_cafile_env` - OpenSSL's environment key that points to a cafile, + * :attr:`openssl_cafile` - hard coded path to a cafile, + * :attr:`openssl_capath_env` - OpenSSL's environment key that points to a capath, + * :attr:`openssl_capath` - hard coded path to a capath directory + + .. versionadded:: 2.7.9 + +.. function:: enum_certificates(store_name) + + Retrieve certificates from Windows' system cert store. *store_name* may be + one of ``CA``, ``ROOT`` or ``MY``. Windows may provide additional cert + stores, too. + + The function returns a list of (cert_bytes, encoding_type, trust) tuples. + The encoding_type specifies the encoding of cert_bytes. It is either + :const:`x509_asn` for X.509 ASN.1 data or :const:`pkcs_7_asn` for + PKCS#7 ASN.1 data. Trust specifies the purpose of the certificate as a set + of OIDS or exactly ``True`` if the certificate is trustworthy for all + purposes. + + Example:: + + >>> ssl.enum_certificates("CA") + [(b'data...', 'x509_asn', {'1.3.6.1.5.5.7.3.1', '1.3.6.1.5.5.7.3.2'}), + (b'data...', 'x509_asn', True)] + + Availability: Windows. + + .. versionadded:: 2.7.9 + +.. function:: enum_crls(store_name) + + Retrieve CRLs from Windows' system cert store. *store_name* may be + one of ``CA``, ``ROOT`` or ``MY``. Windows may provide additional cert + stores, too. + + The function returns a list of (cert_bytes, encoding_type, trust) tuples. + The encoding_type specifies the encoding of cert_bytes. It is either + :const:`x509_asn` for X.509 ASN.1 data or :const:`pkcs_7_asn` for + PKCS#7 ASN.1 data. + + Availability: Windows. + + .. versionadded:: 2.7.9 + + +Constants +^^^^^^^^^ + .. data:: CERT_NONE - Value to pass to the ``cert_reqs`` parameter to :func:`sslobject` when no - certificates will be required or validated from the other side of the socket - connection. + Possible value for :attr:`SSLContext.verify_mode`, or the ``cert_reqs`` + parameter to :func:`wrap_socket`. In this mode (the default), no + certificates will be required from the other side of the socket connection. + If a certificate is received from the other end, no attempt to validate it + is made. + + See the discussion of :ref:`ssl-security` below. .. data:: CERT_OPTIONAL - Value to pass to the ``cert_reqs`` parameter to :func:`sslobject` when no - certificates will be required from the other side of the socket connection, - but if they are provided, will be validated. Note that use of this setting - requires a valid certificate validation file also be passed as a value of the - ``ca_certs`` parameter. + Possible value for :attr:`SSLContext.verify_mode`, or the ``cert_reqs`` + parameter to :func:`wrap_socket`. In this mode no certificates will be + required from the other side of the socket connection; but if they + are provided, validation will be attempted and an :class:`SSLError` + will be raised on failure. + + Use of this setting requires a valid set of CA certificates to + be passed, either to :meth:`SSLContext.load_verify_locations` or as a + value of the ``ca_certs`` parameter to :func:`wrap_socket`. .. data:: CERT_REQUIRED - Value to pass to the ``cert_reqs`` parameter to :func:`sslobject` when - certificates will be required from the other side of the socket connection. - Note that use of this setting requires a valid certificate validation file - also be passed as a value of the ``ca_certs`` parameter. + Possible value for :attr:`SSLContext.verify_mode`, or the ``cert_reqs`` + parameter to :func:`wrap_socket`. In this mode, certificates are + required from the other side of the socket connection; an :class:`SSLError` + will be raised if no certificate is provided, or if its validation fails. + + Use of this setting requires a valid set of CA certificates to + be passed, either to :meth:`SSLContext.load_verify_locations` or as a + value of the ``ca_certs`` parameter to :func:`wrap_socket`. + +.. data:: VERIFY_DEFAULT + + Possible value for :attr:`SSLContext.verify_flags`. In this mode, + certificate revocation lists (CRLs) are not checked. By default OpenSSL + does neither require nor verify CRLs. + + .. versionadded:: 2.7.9 + +.. data:: VERIFY_CRL_CHECK_LEAF + + Possible value for :attr:`SSLContext.verify_flags`. In this mode, only the + peer cert is check but non of the intermediate CA certificates. The mode + requires a valid CRL that is signed by the peer cert's issuer (its direct + ancestor CA). If no proper has been loaded + :attr:`SSLContext.load_verify_locations`, validation will fail. + + .. versionadded:: 2.7.9 + +.. data:: VERIFY_CRL_CHECK_CHAIN + + Possible value for :attr:`SSLContext.verify_flags`. In this mode, CRLs of + all certificates in the peer cert chain are checked. + + .. versionadded:: 2.7.9 + +.. data:: VERIFY_X509_STRICT + + Possible value for :attr:`SSLContext.verify_flags` to disable workarounds + for broken X.509 certificates. + + .. versionadded:: 2.7.9 .. data:: PROTOCOL_SSLv2 @@ -275,9 +540,136 @@ Functions, Constants, and Exceptions .. data:: PROTOCOL_TLSv1 - Selects TLS version 1 as the channel encryption protocol. This is the most + Selects TLS version 1.0 as the channel encryption protocol. + +.. data:: PROTOCOL_TLSv1_1 + + Selects TLS version 1.1 as the channel encryption protocol. + Available only with openssl version 1.0.1+. + + .. versionadded:: 2.7.9 + +.. data:: PROTOCOL_TLSv1_2 + + Selects TLS version 1.2 as the channel encryption protocol. This is the most modern version, and probably the best choice for maximum protection, if both - sides can speak it. + sides can speak it. Available only with openssl version 1.0.1+. + + .. versionadded:: 2.7.9 + +.. data:: OP_ALL + + Enables workarounds for various bugs present in other SSL implementations. + This option is set by default. It does not necessarily set the same + flags as OpenSSL's ``SSL_OP_ALL`` constant. + + .. versionadded:: 2.7.9 + +.. data:: OP_NO_SSLv2 + + Prevents an SSLv2 connection. This option is only applicable in + conjunction with :const:`PROTOCOL_SSLv23`. It prevents the peers from + choosing SSLv2 as the protocol version. + + .. versionadded:: 2.7.9 + +.. data:: OP_NO_SSLv3 + + Prevents an SSLv3 connection. This option is only applicable in + conjunction with :const:`PROTOCOL_SSLv23`. It prevents the peers from + choosing SSLv3 as the protocol version. + + .. versionadded:: 2.7.9 + +.. data:: OP_NO_TLSv1 + + Prevents a TLSv1 connection. This option is only applicable in + conjunction with :const:`PROTOCOL_SSLv23`. It prevents the peers from + choosing TLSv1 as the protocol version. + + .. versionadded:: 2.7.9 + +.. data:: OP_NO_TLSv1_1 + + Prevents a TLSv1.1 connection. This option is only applicable in conjunction + with :const:`PROTOCOL_SSLv23`. It prevents the peers from choosing TLSv1.1 as + the protocol version. Available only with openssl version 1.0.1+. + + .. versionadded:: 2.7.9 + +.. data:: OP_NO_TLSv1_2 + + Prevents a TLSv1.2 connection. This option is only applicable in conjunction + with :const:`PROTOCOL_SSLv23`. It prevents the peers from choosing TLSv1.2 as + the protocol version. Available only with openssl version 1.0.1+. + + .. versionadded:: 2.7.9 + +.. data:: OP_CIPHER_SERVER_PREFERENCE + + Use the server's cipher ordering preference, rather than the client's. + This option has no effect on client sockets and SSLv2 server sockets. + + .. versionadded:: 2.7.9 + +.. data:: OP_SINGLE_DH_USE + + Prevents re-use of the same DH key for distinct SSL sessions. This + improves forward secrecy but requires more computational resources. + This option only applies to server sockets. + + .. versionadded:: 2.7.9 + +.. data:: OP_SINGLE_ECDH_USE + + Prevents re-use of the same ECDH key for distinct SSL sessions. This + improves forward secrecy but requires more computational resources. + This option only applies to server sockets. + + .. versionadded:: 2.7.9 + +.. data:: OP_NO_COMPRESSION + + Disable compression on the SSL channel. This is useful if the application + protocol supports its own compression scheme. + + This option is only available with OpenSSL 1.0.0 and later. + + .. versionadded:: 2.7.9 + +.. data:: HAS_ECDH + + Whether the OpenSSL library has built-in support for Elliptic Curve-based + Diffie-Hellman key exchange. This should be true unless the feature was + explicitly disabled by the distributor. + + .. versionadded:: 2.7.9 + +.. data:: HAS_SNI + + Whether the OpenSSL library has built-in support for the *Server Name + Indication* extension to the SSLv3 and TLSv1 protocols (as defined in + :rfc:`4366`). When true, you can use the *server_hostname* argument to + :meth:`SSLContext.wrap_socket`. + + .. versionadded:: 2.7.9 + +.. data:: HAS_NPN + + Whether the OpenSSL library has built-in support for *Next Protocol + Negotiation* as described in the `NPN draft specification + `_. When true, + you can use the :meth:`SSLContext.set_npn_protocols` method to advertise + which protocols you want to support. + + .. versionadded:: 2.7.9 + +.. data:: CHANNEL_BINDING_TYPES + + List of supported TLS channel binding types. Strings in this list + can be used as arguments to :meth:`SSLSocket.get_channel_binding`. + + .. versionadded:: 2.7.9 .. data:: OPENSSL_VERSION @@ -309,9 +701,40 @@ Functions, Constants, and Exceptions .. versionadded:: 2.7 +.. data:: ALERT_DESCRIPTION_HANDSHAKE_FAILURE + ALERT_DESCRIPTION_INTERNAL_ERROR + ALERT_DESCRIPTION_* + + Alert Descriptions from :rfc:`5246` and others. The `IANA TLS Alert Registry + `_ + contains this list and references to the RFCs where their meaning is defined. + + Used as the return value of the callback function in + :meth:`SSLContext.set_servername_callback`. + + .. versionadded:: 2.7.9 + +.. data:: Purpose.SERVER_AUTH + + Option for :func:`create_default_context` and + :meth:`SSLContext.load_default_certs`. This value indicates that the + context may be used to authenticate Web servers (therefore, it will + be used to create client-side sockets). + + .. versionadded:: 2.7.9 -SSLSocket Objects ------------------ +.. data:: Purpose.CLIENT_AUTH + + Option for :func:`create_default_context` and + :meth:`SSLContext.load_default_certs`. This value indicates that the + context may be used to authenticate Web clients (therefore, it will + be used to create server-side sockets). + + .. versionadded:: 2.7.9 + + +SSL Sockets +----------- SSL sockets provide the following methods of :ref:`socket-objects`: @@ -334,37 +757,64 @@ SSL sockets provide the following methods of :ref:`socket-objects`: However, since the SSL (and TLS) protocol has its own framing atop of TCP, the SSL sockets abstraction can, in certain respects, diverge from -the specification of normal, OS-level sockets. +the specification of normal, OS-level sockets. See especially the +:ref:`notes on non-blocking sockets `. SSL sockets also have the following additional methods and attributes: +.. method:: SSLSocket.do_handshake() + + Perform the SSL setup handshake. + + .. versionchanged:: 2.7.9 + + The handshake method also performs :func:`match_hostname` when the + :attr:`~SSLContext.check_hostname` attribute of the socket's + :attr:`~SSLSocket.context` is true. + .. method:: SSLSocket.getpeercert(binary_form=False) If there is no certificate for the peer on the other end of the connection, - returns ``None``. + return ``None``. If the SSL handshake hasn't been done yet, raise + :exc:`ValueError`. If the ``binary_form`` parameter is :const:`False`, and a certificate was received from the peer, this method returns a :class:`dict` instance. If the certificate was not validated, the dict is empty. If the certificate was - validated, it returns a dict with the keys ``subject`` (the principal for - which the certificate was issued), and ``notAfter`` (the time after which the - certificate should not be trusted). The certificate was already validated, - so the ``notBefore`` and ``issuer`` fields are not returned. If a - certificate contains an instance of the *Subject Alternative Name* extension - (see :rfc:`3280`), there will also be a ``subjectAltName`` key in the - dictionary. - - The "subject" field is a tuple containing the sequence of relative - distinguished names (RDNs) given in the certificate's data structure for the - principal, and each RDN is a sequence of name-value pairs:: - - {'notAfter': 'Feb 16 16:54:50 2013 GMT', - 'subject': ((('countryName', u'US'),), - (('stateOrProvinceName', u'Delaware'),), - (('localityName', u'Wilmington'),), - (('organizationName', u'Python Software Foundation'),), - (('organizationalUnitName', u'SSL'),), - (('commonName', u'somemachine.python.org'),))} + validated, it returns a dict with several keys, amongst them ``subject`` + (the principal for which the certificate was issued) and ``issuer`` + (the principal issuing the certificate). If a certificate contains an + instance of the *Subject Alternative Name* extension (see :rfc:`3280`), + there will also be a ``subjectAltName`` key in the dictionary. + + The ``subject`` and ``issuer`` fields are tuples containing the sequence + of relative distinguished names (RDNs) given in the certificate's data + structure for the respective fields, and each RDN is a sequence of + name-value pairs. Here is a real-world example:: + + {'issuer': ((('countryName', 'IL'),), + (('organizationName', 'StartCom Ltd.'),), + (('organizationalUnitName', + 'Secure Digital Certificate Signing'),), + (('commonName', + 'StartCom Class 2 Primary Intermediate Server CA'),)), + 'notAfter': 'Nov 22 08:15:19 2013 GMT', + 'notBefore': 'Nov 21 03:09:52 2011 GMT', + 'serialNumber': '95F0', + 'subject': ((('description', '571208-SLe257oHY9fVQ07Z'),), + (('countryName', 'US'),), + (('stateOrProvinceName', 'California'),), + (('localityName', 'San Francisco'),), + (('organizationName', 'Electronic Frontier Foundation, Inc.'),), + (('commonName', '*.eff.org'),), + (('emailAddress', 'hostmaster@eff.org'),)), + 'subjectAltName': (('DNS', '*.eff.org'), ('DNS', 'eff.org')), + 'version': 3} + + .. note:: + + To validate a certificate for a particular service, you can use the + :func:`match_hostname` function. If the ``binary_form`` parameter is :const:`True`, and a certificate was provided, this method returns the DER-encoded form of the entire certificate @@ -380,40 +830,388 @@ SSL sockets also have the following additional methods and attributes: :const:`None` if you used :const:`CERT_NONE` (rather than :const:`CERT_OPTIONAL` or :const:`CERT_REQUIRED`). + .. versionchanged:: 2.7.9 + The returned dictionary includes additional items such as ``issuer`` and + ``notBefore``. Additionall :exc:`ValueError` is raised when the handshake + isn't done. The returned dictionary includes additional X509v3 extension + items such as ``crlDistributionPoints``, ``caIssuers`` and ``OCSP`` URIs. + .. method:: SSLSocket.cipher() Returns a three-value tuple containing the name of the cipher being used, the version of the SSL protocol that defines its use, and the number of secret bits being used. If no connection has been established, returns ``None``. -.. method:: SSLSocket.do_handshake() +.. method:: SSLSocket.compression() + + Return the compression algorithm being used as a string, or ``None`` + if the connection isn't compressed. + + If the higher-level protocol supports its own compression mechanism, + you can use :data:`OP_NO_COMPRESSION` to disable SSL-level compression. + + .. versionadded:: 2.7.9 + +.. method:: SSLSocket.get_channel_binding(cb_type="tls-unique") - Perform a TLS/SSL handshake. If this is used with a non-blocking socket, it - may raise :exc:`SSLError` with an ``arg[0]`` of :const:`SSL_ERROR_WANT_READ` - or :const:`SSL_ERROR_WANT_WRITE`, in which case it must be called again until - it completes successfully. For example, to simulate the behavior of a - blocking socket, one might write:: - - while True: - try: - s.do_handshake() - break - except ssl.SSLError as err: - if err.args[0] == ssl.SSL_ERROR_WANT_READ: - select.select([s], [], []) - elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: - select.select([], [s], []) - else: - raise + Get channel binding data for current connection, as a bytes object. Returns + ``None`` if not connected or the handshake has not been completed. + + The *cb_type* parameter allow selection of the desired channel binding + type. Valid channel binding types are listed in the + :data:`CHANNEL_BINDING_TYPES` list. Currently only the 'tls-unique' channel + binding, defined by :rfc:`5929`, is supported. :exc:`ValueError` will be + raised if an unsupported channel binding type is requested. + + .. versionadded:: 2.7.9 + +.. method:: SSLSocket.selected_npn_protocol() + + Returns the protocol that was selected during the TLS/SSL handshake. If + :meth:`SSLContext.set_npn_protocols` was not called, or if the other party + does not support NPN, or if the handshake has not yet happened, this will + return ``None``. + + .. versionadded:: 2.7.9 .. method:: SSLSocket.unwrap() Performs the SSL shutdown handshake, which removes the TLS layer from the underlying socket, and returns the underlying socket object. This can be used to go from encrypted operation over a connection to unencrypted. The - socket instance returned should always be used for further communication with - the other side of the connection, rather than the original socket instance - (which may not function properly after the unwrap). + returned socket should always be used for further communication with the + other side of the connection, rather than the original socket. + +.. attribute:: SSLSocket.context + + The :class:`SSLContext` object this SSL socket is tied to. If the SSL + socket was created using the top-level :func:`wrap_socket` function + (rather than :meth:`SSLContext.wrap_socket`), this is a custom context + object created for this SSL socket. + + .. versionadded:: 2.7.9 + + +SSL Contexts +------------ + +.. versionadded:: 2.7.9 + +An SSL context holds various data longer-lived than single SSL connections, +such as SSL configuration options, certificate(s) and private key(s). +It also manages a cache of SSL sessions for server-side sockets, in order +to speed up repeated connections from the same clients. + +.. class:: SSLContext(protocol) + + Create a new SSL context. You must pass *protocol* which must be one + of the ``PROTOCOL_*`` constants defined in this module. + :data:`PROTOCOL_SSLv23` is currently recommended for maximum + interoperability. + + .. seealso:: + :func:`create_default_context` lets the :mod:`ssl` module choose + security settings for a given purpose. + + +:class:`SSLContext` objects have the following methods and attributes: + +.. method:: SSLContext.cert_store_stats() + + Get statistics about quantities of loaded X.509 certificates, count of + X.509 certificates flagged as CA certificates and certificate revocation + lists as dictionary. + + Example for a context with one CA cert and one other cert:: + + >>> context.cert_store_stats() + {'crl': 0, 'x509_ca': 1, 'x509': 2} + + +.. method:: SSLContext.load_cert_chain(certfile, keyfile=None, password=None) + + Load a private key and the corresponding certificate. The *certfile* + string must be the path to a single file in PEM format containing the + certificate as well as any number of CA certificates needed to establish + the certificate's authenticity. The *keyfile* string, if present, must + point to a file containing the private key in. Otherwise the private + key will be taken from *certfile* as well. See the discussion of + :ref:`ssl-certificates` for more information on how the certificate + is stored in the *certfile*. + + The *password* argument may be a function to call to get the password for + decrypting the private key. It will only be called if the private key is + encrypted and a password is necessary. It will be called with no arguments, + and it should return a string, bytes, or bytearray. If the return value is + a string it will be encoded as UTF-8 before using it to decrypt the key. + Alternatively a string, bytes, or bytearray value may be supplied directly + as the *password* argument. It will be ignored if the private key is not + encrypted and no password is needed. + + If the *password* argument is not specified and a password is required, + OpenSSL's built-in password prompting mechanism will be used to + interactively prompt the user for a password. + + An :class:`SSLError` is raised if the private key doesn't + match with the certificate. + +.. method:: SSLContext.load_default_certs(purpose=Purpose.SERVER_AUTH) + + Load a set of default "certification authority" (CA) certificates from + default locations. On Windows it loads CA certs from the ``CA`` and + ``ROOT`` system stores. On other systems it calls + :meth:`SSLContext.set_default_verify_paths`. In the future the method may + load CA certificates from other locations, too. + + The *purpose* flag specifies what kind of CA certificates are loaded. The + default settings :data:`Purpose.SERVER_AUTH` loads certificates, that are + flagged and trusted for TLS web server authentication (client side + sockets). :data:`Purpose.CLIENT_AUTH` loads CA certificates for client + certificate verification on the server side. + +.. method:: SSLContext.load_verify_locations(cafile=None, capath=None, cadata=None) + + Load a set of "certification authority" (CA) certificates used to validate + other peers' certificates when :data:`verify_mode` is other than + :data:`CERT_NONE`. At least one of *cafile* or *capath* must be specified. + + This method can also load certification revocation lists (CRLs) in PEM or + DER format. In order to make use of CRLs, :attr:`SSLContext.verify_flags` + must be configured properly. + + The *cafile* string, if present, is the path to a file of concatenated + CA certificates in PEM format. See the discussion of + :ref:`ssl-certificates` for more information about how to arrange the + certificates in this file. + + The *capath* string, if present, is + the path to a directory containing several CA certificates in PEM format, + following an `OpenSSL specific layout + `_. + + The *cadata* object, if present, is either an ASCII string of one or more + PEM-encoded certificates or a bytes-like object of DER-encoded + certificates. Like with *capath* extra lines around PEM-encoded + certificates are ignored but at least one certificate must be present. + +.. method:: SSLContext.get_ca_certs(binary_form=False) + + Get a list of loaded "certification authority" (CA) certificates. If the + ``binary_form`` parameter is :const:`False` each list + entry is a dict like the output of :meth:`SSLSocket.getpeercert`. Otherwise + the method returns a list of DER-encoded certificates. The returned list + does not contain certificates from *capath* unless a certificate was + requested and loaded by a SSL connection. + +.. method:: SSLContext.set_default_verify_paths() + + Load a set of default "certification authority" (CA) certificates from + a filesystem path defined when building the OpenSSL library. Unfortunately, + there's no easy way to know whether this method succeeds: no error is + returned if no certificates are to be found. When the OpenSSL library is + provided as part of the operating system, though, it is likely to be + configured properly. + +.. method:: SSLContext.set_ciphers(ciphers) + + Set the available ciphers for sockets created with this context. + It should be a string in the `OpenSSL cipher list format + `_. + If no cipher can be selected (because compile-time options or other + configuration forbids use of all the specified ciphers), an + :class:`SSLError` will be raised. + + .. note:: + when connected, the :meth:`SSLSocket.cipher` method of SSL sockets will + give the currently selected cipher. + +.. method:: SSLContext.set_npn_protocols(protocols) + + Specify which protocols the socket should advertise during the SSL/TLS + handshake. It should be a list of strings, like ``['http/1.1', 'spdy/2']``, + ordered by preference. The selection of a protocol will happen during the + handshake, and will play out according to the `NPN draft specification + `_. After a + successful handshake, the :meth:`SSLSocket.selected_npn_protocol` method will + return the agreed-upon protocol. + + This method will raise :exc:`NotImplementedError` if :data:`HAS_NPN` is + False. + +.. method:: SSLContext.set_servername_callback(server_name_callback) + + Register a callback function that will be called after the TLS Client Hello + handshake message has been received by the SSL/TLS server when the TLS client + specifies a server name indication. The server name indication mechanism + is specified in :rfc:`6066` section 3 - Server Name Indication. + + Only one callback can be set per ``SSLContext``. If *server_name_callback* + is ``None`` then the callback is disabled. Calling this function a + subsequent time will disable the previously registered callback. + + The callback function, *server_name_callback*, will be called with three + arguments; the first being the :class:`ssl.SSLSocket`, the second is a string + that represents the server name that the client is intending to communicate + (or :const:`None` if the TLS Client Hello does not contain a server name) + and the third argument is the original :class:`SSLContext`. The server name + argument is the IDNA decoded server name. + + A typical use of this callback is to change the :class:`ssl.SSLSocket`'s + :attr:`SSLSocket.context` attribute to a new object of type + :class:`SSLContext` representing a certificate chain that matches the server + name. + + Due to the early negotiation phase of the TLS connection, only limited + methods and attributes are usable like + :meth:`SSLSocket.selected_npn_protocol` and :attr:`SSLSocket.context`. + :meth:`SSLSocket.getpeercert`, :meth:`SSLSocket.getpeercert`, + :meth:`SSLSocket.cipher` and :meth:`SSLSocket.compress` methods require that + the TLS connection has progressed beyond the TLS Client Hello and therefore + will not contain return meaningful values nor can they be called safely. + + The *server_name_callback* function must return ``None`` to allow the + TLS negotiation to continue. If a TLS failure is required, a constant + :const:`ALERT_DESCRIPTION_* ` can be + returned. Other return values will result in a TLS fatal error with + :const:`ALERT_DESCRIPTION_INTERNAL_ERROR`. + + If there is an IDNA decoding error on the server name, the TLS connection + will terminate with an :const:`ALERT_DESCRIPTION_INTERNAL_ERROR` fatal TLS + alert message to the client. + + If an exception is raised from the *server_name_callback* function the TLS + connection will terminate with a fatal TLS alert message + :const:`ALERT_DESCRIPTION_HANDSHAKE_FAILURE`. + + This method will raise :exc:`NotImplementedError` if the OpenSSL library + had OPENSSL_NO_TLSEXT defined when it was built. + +.. method:: SSLContext.load_dh_params(dhfile) + + Load the key generation parameters for Diffie-Helman (DH) key exchange. + Using DH key exchange improves forward secrecy at the expense of + computational resources (both on the server and on the client). + The *dhfile* parameter should be the path to a file containing DH + parameters in PEM format. + + This setting doesn't apply to client sockets. You can also use the + :data:`OP_SINGLE_DH_USE` option to further improve security. + +.. method:: SSLContext.set_ecdh_curve(curve_name) + + Set the curve name for Elliptic Curve-based Diffie-Hellman (ECDH) key + exchange. ECDH is significantly faster than regular DH while arguably + as secure. The *curve_name* parameter should be a string describing + a well-known elliptic curve, for example ``prime256v1`` for a widely + supported curve. + + This setting doesn't apply to client sockets. You can also use the + :data:`OP_SINGLE_ECDH_USE` option to further improve security. + + This method is not available if :data:`HAS_ECDH` is False. + + .. seealso:: + `SSL/TLS & Perfect Forward Secrecy `_ + Vincent Bernat. + +.. method:: SSLContext.wrap_socket(sock, server_side=False, \ + do_handshake_on_connect=True, suppress_ragged_eofs=True, \ + server_hostname=None) + + Wrap an existing Python socket *sock* and return an :class:`SSLSocket` + object. *sock* must be a :data:`~socket.SOCK_STREAM` socket; other socket + types are unsupported. + + The returned SSL socket is tied to the context, its settings and + certificates. The parameters *server_side*, *do_handshake_on_connect* + and *suppress_ragged_eofs* have the same meaning as in the top-level + :func:`wrap_socket` function. + + On client connections, the optional parameter *server_hostname* specifies + the hostname of the service which we are connecting to. This allows a + single server to host multiple SSL-based services with distinct certificates, + quite similarly to HTTP virtual hosts. Specifying *server_hostname* + will raise a :exc:`ValueError` if the OpenSSL library doesn't have support + for it (that is, if :data:`HAS_SNI` is :const:`False`). Specifying + *server_hostname* will also raise a :exc:`ValueError` if *server_side* + is true. + +.. method:: SSLContext.session_stats() + + Get statistics about the SSL sessions created or managed by this context. + A dictionary is returned which maps the names of each `piece of information + `_ to their + numeric values. For example, here is the total number of hits and misses + in the session cache since the context was created:: + + >>> stats = context.session_stats() + >>> stats['hits'], stats['misses'] + (0, 0) + +.. method:: SSLContext.get_ca_certs(binary_form=False) + + Returns a list of dicts with information of loaded CA certs. If the + optional argument is true, returns a DER-encoded copy of the CA + certificate. + + .. note:: + Certificates in a capath directory aren't loaded unless they have + been used at least once. + +.. attribute:: SSLContext.check_hostname + + Wether to match the peer cert's hostname with :func:`match_hostname` in + :meth:`SSLSocket.do_handshake`. The context's + :attr:`~SSLContext.verify_mode` must be set to :data:`CERT_OPTIONAL` or + :data:`CERT_REQUIRED`, and you must pass *server_hostname* to + :meth:`~SSLContext.wrap_socket` in order to match the hostname. + + Example:: + + import socket, ssl + + context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + context.verify_mode = ssl.CERT_REQUIRED + context.check_hostname = True + context.load_default_certs() + + s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + ssl_sock = context.wrap_socket(s, server_hostname='www.verisign.com') + ssl_sock.connect(('www.verisign.com', 443)) + + .. note:: + + This features requires OpenSSL 0.9.8f or newer. + +.. attribute:: SSLContext.options + + An integer representing the set of SSL options enabled on this context. + The default value is :data:`OP_ALL`, but you can specify other options + such as :data:`OP_NO_SSLv2` by ORing them together. + + .. note:: + With versions of OpenSSL older than 0.9.8m, it is only possible + to set options, not to clear them. Attempting to clear an option + (by resetting the corresponding bits) will raise a ``ValueError``. + +.. attribute:: SSLContext.protocol + + The protocol version chosen when constructing the context. This attribute + is read-only. + +.. attribute:: SSLContext.verify_flags + + The flags for certificate verification operations. You can set flags like + :data:`VERIFY_CRL_CHECK_LEAF` by ORing them together. By default OpenSSL + does neither require nor verify certificate revocation lists (CRLs). + Available only with openssl version 0.9.8+. + +.. attribute:: SSLContext.verify_mode + + Whether to try to verify other peers' certificates and how to behave + if verification fails. This attribute must be one of + :data:`CERT_NONE`, :data:`CERT_OPTIONAL` or :data:`CERT_REQUIRED`. + .. index:: single: certificates @@ -460,6 +1258,9 @@ and a footer line:: ... (certificate in base64 PEM encoding) ... -----END CERTIFICATE----- +Certificate chains +^^^^^^^^^^^^^^^^^^ + The Python files which contain certificates can contain a sequence of certificates, sometimes called a *certificate chain*. This chain should start with the specific certificate for the principal who "is" the client or server, @@ -483,24 +1284,35 @@ certification authority's certificate:: ... (the root certificate for the CA's issuer)... -----END CERTIFICATE----- +CA certificates +^^^^^^^^^^^^^^^ + If you are going to require validation of the other side of the connection's certificate, you need to provide a "CA certs" file, filled with the certificate chains for each issuer you are willing to trust. Again, this file just contains these chains concatenated together. For validation, Python will use the first -chain it finds in the file which matches. +chain it finds in the file which matches. The platform's certificates file can +be used by calling :meth:`SSLContext.load_default_certs`, this is done +automatically with :func:`.create_default_context`. + +Combined key and certificate +^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Often the private key is stored in the same file as the certificate; in this +case, only the ``certfile`` parameter to :meth:`SSLContext.load_cert_chain` +and :func:`wrap_socket` needs to be passed. If the private key is stored +with the certificate, it should come before the first certificate in +the certificate chain:: -Some "standard" root certificates are available from various certification -authorities: `Thawte `_, `Verisign -`_, `Positive SSL -`_ -(used by python.org), `Equifax and GeoTrust -`_. + -----BEGIN RSA PRIVATE KEY----- + ... (private key in base64 encoding) ... + -----END RSA PRIVATE KEY----- + -----BEGIN CERTIFICATE----- + ... (certificate in base64 PEM encoding) ... + -----END CERTIFICATE----- -In general, if you are using SSL3 or TLS1, you don't need to put the full chain -in your "CA certs" file; you only need the root certificates, and the remote -peer is supposed to furnish the other certificates necessary to chain from its -certificate to a root certificate. See :rfc:`4158` for more discussion of the -way in which certification chains can be built. +Self-signed certificates +^^^^^^^^^^^^^^^^^^^^^^^^ If you are going to create a server that provides SSL-encrypted connection services, you will need to acquire a certificate for that service. There are @@ -555,87 +1367,156 @@ should use the following idiom:: Client-side operation ^^^^^^^^^^^^^^^^^^^^^ -This example connects to an SSL server, prints the server's address and -certificate, sends some bytes, and reads part of the response:: +This example connects to an SSL server and prints the server's certificate:: import socket, ssl, pprint s = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - # require a certificate from the server ssl_sock = ssl.wrap_socket(s, ca_certs="/etc/ca_certs_file", cert_reqs=ssl.CERT_REQUIRED) - ssl_sock.connect(('www.verisign.com', 443)) - print repr(ssl_sock.getpeername()) - print ssl_sock.cipher() - print pprint.pformat(ssl_sock.getpeercert()) - - # Set a simple HTTP request -- use httplib in actual code. - ssl_sock.write("""GET / HTTP/1.0\r - Host: www.verisign.com\r\n\r\n""") - - # Read a chunk of data. Will not necessarily - # read all the data returned by the server. - data = ssl_sock.read() - + pprint.pprint(ssl_sock.getpeercert()) # note that closing the SSLSocket will also close the underlying socket ssl_sock.close() -As of September 6, 2007, the certificate printed by this program looked like +As of January 6, 2012, the certificate printed by this program looks like this:: - {'notAfter': 'May 8 23:59:59 2009 GMT', - 'subject': ((('serialNumber', u'2497886'),), - (('1.3.6.1.4.1.311.60.2.1.3', u'US'),), - (('1.3.6.1.4.1.311.60.2.1.2', u'Delaware'),), - (('countryName', u'US'),), - (('postalCode', u'94043'),), - (('stateOrProvinceName', u'California'),), - (('localityName', u'Mountain View'),), - (('streetAddress', u'487 East Middlefield Road'),), - (('organizationName', u'VeriSign, Inc.'),), - (('organizationalUnitName', - u'Production Security Services'),), - (('organizationalUnitName', - u'Terms of use at www.verisign.com/rpa (c)06'),), - (('commonName', u'www.verisign.com'),))} - -which is a fairly poorly-formed ``subject`` field. + {'issuer': ((('countryName', 'US'),), + (('organizationName', 'VeriSign, Inc.'),), + (('organizationalUnitName', 'VeriSign Trust Network'),), + (('organizationalUnitName', + 'Terms of use at https://www.verisign.com/rpa (c)06'),), + (('commonName', + 'VeriSign Class 3 Extended Validation SSL SGC CA'),)), + 'notAfter': 'May 25 23:59:59 2012 GMT', + 'notBefore': 'May 26 00:00:00 2010 GMT', + 'serialNumber': '53D2BEF924A7245E83CA01E46CAA2477', + 'subject': ((('1.3.6.1.4.1.311.60.2.1.3', 'US'),), + (('1.3.6.1.4.1.311.60.2.1.2', 'Delaware'),), + (('businessCategory', 'V1.0, Clause 5.(b)'),), + (('serialNumber', '2497886'),), + (('countryName', 'US'),), + (('postalCode', '94043'),), + (('stateOrProvinceName', 'California'),), + (('localityName', 'Mountain View'),), + (('streetAddress', '487 East Middlefield Road'),), + (('organizationName', 'VeriSign, Inc.'),), + (('organizationalUnitName', ' Production Security Services'),), + (('commonName', 'www.verisign.com'),)), + 'subjectAltName': (('DNS', 'www.verisign.com'), + ('DNS', 'verisign.com'), + ('DNS', 'www.verisign.net'), + ('DNS', 'verisign.net'), + ('DNS', 'www.verisign.mobi'), + ('DNS', 'verisign.mobi'), + ('DNS', 'www.verisign.eu'), + ('DNS', 'verisign.eu')), + 'version': 3} + +This other example first creates an SSL context, instructs it to verify +certificates sent by peers, and feeds it a set of recognized certificate +authorities (CA):: + + >>> context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + >>> context.verify_mode = ssl.CERT_REQUIRED + >>> context.load_verify_locations("/etc/ssl/certs/ca-bundle.crt") + +(it is assumed your operating system places a bundle of all CA certificates +in ``/etc/ssl/certs/ca-bundle.crt``; if not, you'll get an error and have +to adjust the location) + +When you use the context to connect to a server, :const:`CERT_REQUIRED` +validates the server certificate: it ensures that the server certificate +was signed with one of the CA certificates, and checks the signature for +correctness:: + + >>> conn = context.wrap_socket(socket.socket(socket.AF_INET)) + >>> conn.connect(("linuxfr.org", 443)) + +You should then fetch the certificate and check its fields for conformity:: + + >>> cert = conn.getpeercert() + >>> ssl.match_hostname(cert, "linuxfr.org") + +Visual inspection shows that the certificate does identify the desired service +(that is, the HTTPS host ``linuxfr.org``):: + + >>> pprint.pprint(cert) + {'issuer': ((('organizationName', 'CAcert Inc.'),), + (('organizationalUnitName', 'http://www.CAcert.org'),), + (('commonName', 'CAcert Class 3 Root'),)), + 'notAfter': 'Jun 7 21:02:24 2013 GMT', + 'notBefore': 'Jun 8 21:02:24 2011 GMT', + 'serialNumber': 'D3E9', + 'subject': ((('commonName', 'linuxfr.org'),),), + 'subjectAltName': (('DNS', 'linuxfr.org'), + ('othername', ''), + ('DNS', 'linuxfr.org'), + ('othername', ''), + ('DNS', 'dev.linuxfr.org'), + ('othername', ''), + ('DNS', 'prod.linuxfr.org'), + ('othername', ''), + ('DNS', 'alpha.linuxfr.org'), + ('othername', ''), + ('DNS', '*.linuxfr.org'), + ('othername', '')), + 'version': 3} + +Now that you are assured of its authenticity, you can proceed to talk with +the server:: + + >>> conn.sendall(b"HEAD / HTTP/1.0\r\nHost: linuxfr.org\r\n\r\n") + >>> pprint.pprint(conn.recv(1024).split(b"\r\n")) + [b'HTTP/1.1 302 Found', + b'Date: Sun, 16 May 2010 13:43:28 GMT', + b'Server: Apache/2.2', + b'Location: https://linuxfr.org/pub/', + b'Vary: Accept-Encoding', + b'Connection: close', + b'Content-Type: text/html; charset=iso-8859-1', + b'', + b''] + +See the discussion of :ref:`ssl-security` below. + Server-side operation ^^^^^^^^^^^^^^^^^^^^^ -For server operation, typically you'd need to have a server certificate, and -private key, each in a file. You'd open a socket, bind it to a port, call -:meth:`listen` on it, then start waiting for clients to connect:: +For server operation, typically you'll need to have a server certificate, and +private key, each in a file. You'll first create a context holding the key +and the certificate, so that clients can check your authenticity. Then +you'll open a socket, bind it to a port, call :meth:`listen` on it, and start +waiting for clients to connect:: import socket, ssl + context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + context.load_cert_chain(certfile="mycertfile", keyfile="mykeyfile") + bindsocket = socket.socket() bindsocket.bind(('myaddr.mydomain.com', 10023)) bindsocket.listen(5) -When one did, you'd call :meth:`accept` on the socket to get the new socket from -the other end, and use :func:`wrap_socket` to create a server-side SSL context -for it:: +When a client connects, you'll call :meth:`accept` on the socket to get the +new socket from the other end, and use the context's :meth:`SSLContext.wrap_socket` +method to create a server-side SSL socket for the connection:: while True: newsocket, fromaddr = bindsocket.accept() - connstream = ssl.wrap_socket(newsocket, - server_side=True, - certfile="mycertfile", - keyfile="mykeyfile", - ssl_version=ssl.PROTOCOL_TLSv1) + connstream = context.wrap_socket(newsocket, server_side=True) try: deal_with_client(connstream) finally: connstream.shutdown(socket.SHUT_RDWR) connstream.close() -Then you'd read data from the ``connstream`` and do something with it till you +Then you'll read data from the ``connstream`` and do something with it till you are finished with the client (or the client is finished with you):: def deal_with_client(connstream): @@ -649,7 +1530,138 @@ are finished with the client (or the client is finished with you):: data = connstream.read() # finished with client -And go back to listening for new client connections. +And go back to listening for new client connections (of course, a real server +would probably handle each client connection in a separate thread, or put +the sockets in non-blocking mode and use an event loop). + + +.. _ssl-nonblocking: + +Notes on non-blocking sockets +----------------------------- + +When working with non-blocking sockets, there are several things you need +to be aware of: + +- Calling :func:`~select.select` tells you that the OS-level socket can be + read from (or written to), but it does not imply that there is sufficient + data at the upper SSL layer. For example, only part of an SSL frame might + have arrived. Therefore, you must be ready to handle :meth:`SSLSocket.recv` + and :meth:`SSLSocket.send` failures, and retry after another call to + :func:`~select.select`. + +- Conversely, since the SSL layer has its own framing, a SSL socket may + still have data available for reading without :func:`~select.select` + being aware of it. Therefore, you should first call + :meth:`SSLSocket.recv` to drain any potentially available data, and then + only block on a :func:`~select.select` call if still necessary. + + (of course, similar provisions apply when using other primitives such as + :func:`~select.poll`, or those in the :mod:`selectors` module) + +- The SSL handshake itself will be non-blocking: the + :meth:`SSLSocket.do_handshake` method has to be retried until it returns + successfully. Here is a synopsis using :func:`~select.select` to wait for + the socket's readiness:: + + while True: + try: + sock.do_handshake() + break + except ssl.SSLWantReadError: + select.select([sock], [], []) + except ssl.SSLWantWriteError: + select.select([], [sock], []) + + +.. _ssl-security: + +Security considerations +----------------------- + +Best defaults +^^^^^^^^^^^^^ + +For **client use**, if you don't have any special requirements for your +security policy, it is highly recommended that you use the +:func:`create_default_context` function to create your SSL context. +It will load the system's trusted CA certificates, enable certificate +validation and hostname checking, and try to choose reasonably secure +protocol and cipher settings. + +If a client certificate is needed for the connection, it can be added with +:meth:`SSLContext.load_cert_chain`. + +By contrast, if you create the SSL context by calling the :class:`SSLContext` +constructor yourself, it will not have certificate validation nor hostname +checking enabled by default. If you do so, please read the paragraphs below +to achieve a good security level. + +Manual settings +^^^^^^^^^^^^^^^ + +Verifying certificates +'''''''''''''''''''''' + +When calling the :class:`SSLContext` constructor directly, +:const:`CERT_NONE` is the default. Since it does not authenticate the other +peer, it can be insecure, especially in client mode where most of time you +would like to ensure the authenticity of the server you're talking to. +Therefore, when in client mode, it is highly recommended to use +:const:`CERT_REQUIRED`. However, it is in itself not sufficient; you also +have to check that the server certificate, which can be obtained by calling +:meth:`SSLSocket.getpeercert`, matches the desired service. For many +protocols and applications, the service can be identified by the hostname; +in this case, the :func:`match_hostname` function can be used. This common +check is automatically performed when :attr:`SSLContext.check_hostname` is +enabled. + +In server mode, if you want to authenticate your clients using the SSL layer +(rather than using a higher-level authentication mechanism), you'll also have +to specify :const:`CERT_REQUIRED` and similarly check the client certificate. + + .. note:: + + In client mode, :const:`CERT_OPTIONAL` and :const:`CERT_REQUIRED` are + equivalent unless anonymous ciphers are enabled (they are disabled + by default). + +Protocol versions +''''''''''''''''' + +SSL version 2 is considered insecure and is therefore dangerous to use. If +you want maximum compatibility between clients and servers, it is recommended +to use :const:`PROTOCOL_SSLv23` as the protocol version and then disable +SSLv2 explicitly using the :data:`SSLContext.options` attribute:: + + context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + context.options |= ssl.OP_NO_SSLv2 + +The SSL context created above will allow SSLv3 and TLSv1 (and later, if +supported by your system) connections, but not SSLv2. + +Cipher selection +'''''''''''''''' + +If you have advanced security requirements, fine-tuning of the ciphers +enabled when negotiating a SSL session is possible through the +:meth:`SSLContext.set_ciphers` method. Starting from Python 2.7.9, the +ssl module disables certain weak ciphers by default, but you may want +to further restrict the cipher choice. Be sure to read OpenSSL's documentation +about the `cipher list format `_. +If you want to check which ciphers are enabled by a given cipher list, use the +``openssl ciphers`` command on your system. + +Multi-processing +^^^^^^^^^^^^^^^^ + +If using this module as part of a multi-processed application (using, +for example the :mod:`multiprocessing` or :mod:`concurrent.futures` modules), +be aware that OpenSSL's internal random number generator does not properly +handle forked processes. Applications must change the PRNG state of the +parent process if they use any SSL feature with :func:`os.fork`. Any +successful call of :func:`~ssl.RAND_add`, :func:`~ssl.RAND_bytes` or +:func:`~ssl.RAND_pseudo_bytes` is sufficient. .. seealso:: @@ -668,3 +1680,15 @@ And go back to listening for new client connections. `RFC 3280: Internet X.509 Public Key Infrastructure Certificate and CRL Profile `_ Housley et. al. + + `RFC 4366: Transport Layer Security (TLS) Extensions `_ + Blake-Wilson et. al. + + `RFC 5246: The Transport Layer Security (TLS) Protocol Version 1.2 `_ + T. Dierks et. al. + + `RFC 6066: Transport Layer Security (TLS) Extensions `_ + D. Eastlake + + `IANA TLS: Transport Layer Security (TLS) Parameters `_ + IANA diff --git a/Lib/ssl.py b/Lib/ssl.py index 666cea3..882b60e 100644 --- a/Lib/ssl.py +++ b/Lib/ssl.py @@ -1,8 +1,7 @@ # Wrapper module for _ssl, providing some additional facilities # implemented in Python. Written by Bill Janssen. -"""\ -This module provides some more Pythonic support for SSL. +"""This module provides some more Pythonic support for SSL. Object types: @@ -53,62 +52,461 @@ PROTOCOL_SSLv2 PROTOCOL_SSLv3 PROTOCOL_SSLv23 PROTOCOL_TLSv1 +PROTOCOL_TLSv1_1 +PROTOCOL_TLSv1_2 + +The following constants identify various SSL alert message descriptions as per +http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-6 + +ALERT_DESCRIPTION_CLOSE_NOTIFY +ALERT_DESCRIPTION_UNEXPECTED_MESSAGE +ALERT_DESCRIPTION_BAD_RECORD_MAC +ALERT_DESCRIPTION_RECORD_OVERFLOW +ALERT_DESCRIPTION_DECOMPRESSION_FAILURE +ALERT_DESCRIPTION_HANDSHAKE_FAILURE +ALERT_DESCRIPTION_BAD_CERTIFICATE +ALERT_DESCRIPTION_UNSUPPORTED_CERTIFICATE +ALERT_DESCRIPTION_CERTIFICATE_REVOKED +ALERT_DESCRIPTION_CERTIFICATE_EXPIRED +ALERT_DESCRIPTION_CERTIFICATE_UNKNOWN +ALERT_DESCRIPTION_ILLEGAL_PARAMETER +ALERT_DESCRIPTION_UNKNOWN_CA +ALERT_DESCRIPTION_ACCESS_DENIED +ALERT_DESCRIPTION_DECODE_ERROR +ALERT_DESCRIPTION_DECRYPT_ERROR +ALERT_DESCRIPTION_PROTOCOL_VERSION +ALERT_DESCRIPTION_INSUFFICIENT_SECURITY +ALERT_DESCRIPTION_INTERNAL_ERROR +ALERT_DESCRIPTION_USER_CANCELLED +ALERT_DESCRIPTION_NO_RENEGOTIATION +ALERT_DESCRIPTION_UNSUPPORTED_EXTENSION +ALERT_DESCRIPTION_CERTIFICATE_UNOBTAINABLE +ALERT_DESCRIPTION_UNRECOGNIZED_NAME +ALERT_DESCRIPTION_BAD_CERTIFICATE_STATUS_RESPONSE +ALERT_DESCRIPTION_BAD_CERTIFICATE_HASH_VALUE +ALERT_DESCRIPTION_UNKNOWN_PSK_IDENTITY """ import textwrap +import re +import sys +import os +from collections import namedtuple +from contextlib import closing import _ssl # if we can't import it, let the error propagate from _ssl import OPENSSL_VERSION_NUMBER, OPENSSL_VERSION_INFO, OPENSSL_VERSION -from _ssl import SSLError +from _ssl import _SSLContext +from _ssl import ( + SSLError, SSLZeroReturnError, SSLWantReadError, SSLWantWriteError, + SSLSyscallError, SSLEOFError, + ) from _ssl import CERT_NONE, CERT_OPTIONAL, CERT_REQUIRED +from _ssl import (VERIFY_DEFAULT, VERIFY_CRL_CHECK_LEAF, VERIFY_CRL_CHECK_CHAIN, + VERIFY_X509_STRICT) +from _ssl import txt2obj as _txt2obj, nid2obj as _nid2obj from _ssl import RAND_status, RAND_egd, RAND_add -from _ssl import \ - SSL_ERROR_ZERO_RETURN, \ - SSL_ERROR_WANT_READ, \ - SSL_ERROR_WANT_WRITE, \ - SSL_ERROR_WANT_X509_LOOKUP, \ - SSL_ERROR_SYSCALL, \ - SSL_ERROR_SSL, \ - SSL_ERROR_WANT_CONNECT, \ - SSL_ERROR_EOF, \ - SSL_ERROR_INVALID_ERROR_CODE -from _ssl import PROTOCOL_SSLv3, PROTOCOL_SSLv23, PROTOCOL_TLSv1 -_PROTOCOL_NAMES = { - PROTOCOL_TLSv1: "TLSv1", - PROTOCOL_SSLv23: "SSLv23", - PROTOCOL_SSLv3: "SSLv3", -} + +def _import_symbols(prefix): + for n in dir(_ssl): + if n.startswith(prefix): + globals()[n] = getattr(_ssl, n) + +_import_symbols('OP_') +_import_symbols('ALERT_DESCRIPTION_') +_import_symbols('SSL_ERROR_') +_import_symbols('PROTOCOL_') + +from _ssl import HAS_SNI, HAS_ECDH, HAS_NPN + +from _ssl import _OPENSSL_API_VERSION + +_PROTOCOL_NAMES = {value: name for name, value in globals().items() if name.startswith('PROTOCOL_')} + try: - from _ssl import PROTOCOL_SSLv2 _SSLv2_IF_EXISTS = PROTOCOL_SSLv2 -except ImportError: +except NameError: _SSLv2_IF_EXISTS = None -else: - _PROTOCOL_NAMES[PROTOCOL_SSLv2] = "SSLv2" from socket import socket, _fileobject, _delegate_methods, error as socket_error -from socket import getnameinfo as _getnameinfo -from socket import SOL_SOCKET, SO_TYPE, SOCK_STREAM +if sys.platform == "win32": + from _ssl import enum_certificates, enum_crls + +from socket import socket, AF_INET, SOCK_STREAM, create_connection +from socket import SOL_SOCKET, SO_TYPE import base64 # for DER-to-PEM translation import errno +if _ssl.HAS_TLS_UNIQUE: + CHANNEL_BINDING_TYPES = ['tls-unique'] +else: + CHANNEL_BINDING_TYPES = [] + # Disable weak or insecure ciphers by default # (OpenSSL's default setting is 'DEFAULT:!aNULL:!eNULL') -_DEFAULT_CIPHERS = 'DEFAULT:!aNULL:!eNULL:!LOW:!EXPORT:!SSLv2' - +# Enable a better set of ciphers by default +# This list has been explicitly chosen to: +# * Prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE) +# * Prefer ECDHE over DHE for better performance +# * Prefer any AES-GCM over any AES-CBC for better performance and security +# * Then Use HIGH cipher suites as a fallback +# * Then Use 3DES as fallback which is secure but slow +# * Finally use RC4 as a fallback which is problematic but needed for +# compatibility some times. +# * Disable NULL authentication, NULL encryption, and MD5 MACs for security +# reasons +_DEFAULT_CIPHERS = ( + 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:' + 'DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:ECDH+RC4:' + 'DH+RC4:RSA+RC4:!aNULL:!eNULL:!MD5' +) + +# Restricted and more secure ciphers for the server side +# This list has been explicitly chosen to: +# * Prefer cipher suites that offer perfect forward secrecy (DHE/ECDHE) +# * Prefer ECDHE over DHE for better performance +# * Prefer any AES-GCM over any AES-CBC for better performance and security +# * Then Use HIGH cipher suites as a fallback +# * Then Use 3DES as fallback which is secure but slow +# * Disable NULL authentication, NULL encryption, MD5 MACs, DSS, and RC4 for +# security reasons +_RESTRICTED_SERVER_CIPHERS = ( + 'ECDH+AESGCM:DH+AESGCM:ECDH+AES256:DH+AES256:ECDH+AES128:DH+AES:ECDH+HIGH:' + 'DH+HIGH:ECDH+3DES:DH+3DES:RSA+AESGCM:RSA+AES:RSA+HIGH:RSA+3DES:!aNULL:' + '!eNULL:!MD5:!DSS:!RC4' +) + + +class CertificateError(ValueError): + pass + + +def _dnsname_match(dn, hostname, max_wildcards=1): + """Matching according to RFC 6125, section 6.4.3 + + http://tools.ietf.org/html/rfc6125#section-6.4.3 + """ + pats = [] + if not dn: + return False + + pieces = dn.split(r'.') + leftmost = pieces[0] + remainder = pieces[1:] + + wildcards = leftmost.count('*') + if wildcards > max_wildcards: + # Issue #17980: avoid denials of service by refusing more + # than one wildcard per fragment. A survery of established + # policy among SSL implementations showed it to be a + # reasonable choice. + raise CertificateError( + "too many wildcards in certificate DNS name: " + repr(dn)) + + # speed up common case w/o wildcards + if not wildcards: + return dn.lower() == hostname.lower() + + # RFC 6125, section 6.4.3, subitem 1. + # The client SHOULD NOT attempt to match a presented identifier in which + # the wildcard character comprises a label other than the left-most label. + if leftmost == '*': + # When '*' is a fragment by itself, it matches a non-empty dotless + # fragment. + pats.append('[^.]+') + elif leftmost.startswith('xn--') or hostname.startswith('xn--'): + # RFC 6125, section 6.4.3, subitem 3. + # The client SHOULD NOT attempt to match a presented identifier + # where the wildcard character is embedded within an A-label or + # U-label of an internationalized domain name. + pats.append(re.escape(leftmost)) + else: + # Otherwise, '*' matches any dotless string, e.g. www* + pats.append(re.escape(leftmost).replace(r'\*', '[^.]*')) + + # add the remaining fragments, ignore any wildcards + for frag in remainder: + pats.append(re.escape(frag)) + + pat = re.compile(r'\A' + r'\.'.join(pats) + r'\Z', re.IGNORECASE) + return pat.match(hostname) + + +def match_hostname(cert, hostname): + """Verify that *cert* (in decoded format as returned by + SSLSocket.getpeercert()) matches the *hostname*. RFC 2818 and RFC 6125 + rules are followed, but IP addresses are not accepted for *hostname*. + + CertificateError is raised on failure. On success, the function + returns nothing. + """ + if not cert: + raise ValueError("empty or no certificate, match_hostname needs a " + "SSL socket or SSL context with either " + "CERT_OPTIONAL or CERT_REQUIRED") + dnsnames = [] + san = cert.get('subjectAltName', ()) + for key, value in san: + if key == 'DNS': + if _dnsname_match(value, hostname): + return + dnsnames.append(value) + if not dnsnames: + # The subject is only checked when there is no dNSName entry + # in subjectAltName + for sub in cert.get('subject', ()): + for key, value in sub: + # XXX according to RFC 2818, the most specific Common Name + # must be used. + if key == 'commonName': + if _dnsname_match(value, hostname): + return + dnsnames.append(value) + if len(dnsnames) > 1: + raise CertificateError("hostname %r " + "doesn't match either of %s" + % (hostname, ', '.join(map(repr, dnsnames)))) + elif len(dnsnames) == 1: + raise CertificateError("hostname %r " + "doesn't match %r" + % (hostname, dnsnames[0])) + else: + raise CertificateError("no appropriate commonName or " + "subjectAltName fields were found") + + +DefaultVerifyPaths = namedtuple("DefaultVerifyPaths", + "cafile capath openssl_cafile_env openssl_cafile openssl_capath_env " + "openssl_capath") + +def get_default_verify_paths(): + """Return paths to default cafile and capath. + """ + parts = _ssl.get_default_verify_paths() + + # environment vars shadow paths + cafile = os.environ.get(parts[0], parts[1]) + capath = os.environ.get(parts[2], parts[3]) + + return DefaultVerifyPaths(cafile if os.path.isfile(cafile) else None, + capath if os.path.isdir(capath) else None, + *parts) + + +class _ASN1Object(namedtuple("_ASN1Object", "nid shortname longname oid")): + """ASN.1 object identifier lookup + """ + __slots__ = () + + def __new__(cls, oid): + return super(_ASN1Object, cls).__new__(cls, *_txt2obj(oid, name=False)) + + @classmethod + def fromnid(cls, nid): + """Create _ASN1Object from OpenSSL numeric ID + """ + return super(_ASN1Object, cls).__new__(cls, *_nid2obj(nid)) + + @classmethod + def fromname(cls, name): + """Create _ASN1Object from short name, long name or OID + """ + return super(_ASN1Object, cls).__new__(cls, *_txt2obj(name, name=True)) + + +class Purpose(_ASN1Object): + """SSLContext purpose flags with X509v3 Extended Key Usage objects + """ + +Purpose.SERVER_AUTH = Purpose('1.3.6.1.5.5.7.3.1') +Purpose.CLIENT_AUTH = Purpose('1.3.6.1.5.5.7.3.2') + + +class SSLContext(_SSLContext): + """An SSLContext holds various SSL-related configuration options and + data, such as certificates and possibly a private key.""" + + __slots__ = ('protocol', '__weakref__') + _windows_cert_stores = ("CA", "ROOT") + + def __new__(cls, protocol, *args, **kwargs): + self = _SSLContext.__new__(cls, protocol) + if protocol != _SSLv2_IF_EXISTS: + self.set_ciphers(_DEFAULT_CIPHERS) + return self + + def __init__(self, protocol): + self.protocol = protocol + + def wrap_socket(self, sock, server_side=False, + do_handshake_on_connect=True, + suppress_ragged_eofs=True, + server_hostname=None): + return SSLSocket(sock=sock, server_side=server_side, + do_handshake_on_connect=do_handshake_on_connect, + suppress_ragged_eofs=suppress_ragged_eofs, + server_hostname=server_hostname, + _context=self) + + def set_npn_protocols(self, npn_protocols): + protos = bytearray() + for protocol in npn_protocols: + b = protocol.encode('ascii') + if len(b) == 0 or len(b) > 255: + raise SSLError('NPN protocols must be 1 to 255 in length') + protos.append(len(b)) + protos.extend(b) + + self._set_npn_protocols(protos) + + def _load_windows_store_certs(self, storename, purpose): + certs = bytearray() + for cert, encoding, trust in enum_certificates(storename): + # CA certs are never PKCS#7 encoded + if encoding == "x509_asn": + if trust is True or purpose.oid in trust: + certs.extend(cert) + self.load_verify_locations(cadata=certs) + return certs + + def load_default_certs(self, purpose=Purpose.SERVER_AUTH): + if not isinstance(purpose, _ASN1Object): + raise TypeError(purpose) + if sys.platform == "win32": + for storename in self._windows_cert_stores: + self._load_windows_store_certs(storename, purpose) + else: + self.set_default_verify_paths() + + +def create_default_context(purpose=Purpose.SERVER_AUTH, cafile=None, + capath=None, cadata=None): + """Create a SSLContext object with default settings. + + NOTE: The protocol and settings may change anytime without prior + deprecation. The values represent a fair balance between maximum + compatibility and security. + """ + if not isinstance(purpose, _ASN1Object): + raise TypeError(purpose) + + context = SSLContext(PROTOCOL_SSLv23) + + # SSLv2 considered harmful. + context.options |= OP_NO_SSLv2 + + # SSLv3 has problematic security and is only required for really old + # clients such as IE6 on Windows XP + context.options |= OP_NO_SSLv3 + + # disable compression to prevent CRIME attacks (OpenSSL 1.0+) + context.options |= getattr(_ssl, "OP_NO_COMPRESSION", 0) + + if purpose == Purpose.SERVER_AUTH: + # verify certs and host name in client mode + context.verify_mode = CERT_REQUIRED + context.check_hostname = True + elif purpose == Purpose.CLIENT_AUTH: + # Prefer the server's ciphers by default so that we get stronger + # encryption + context.options |= getattr(_ssl, "OP_CIPHER_SERVER_PREFERENCE", 0) + + # Use single use keys in order to improve forward secrecy + context.options |= getattr(_ssl, "OP_SINGLE_DH_USE", 0) + context.options |= getattr(_ssl, "OP_SINGLE_ECDH_USE", 0) + + # disallow ciphers with known vulnerabilities + context.set_ciphers(_RESTRICTED_SERVER_CIPHERS) + + if cafile or capath or cadata: + context.load_verify_locations(cafile, capath, cadata) + elif context.verify_mode != CERT_NONE: + # no explicit cafile, capath or cadata but the verify mode is + # CERT_OPTIONAL or CERT_REQUIRED. Let's try to load default system + # root CA certificates for the given purpose. This may fail silently. + context.load_default_certs(purpose) + return context + + +def _create_stdlib_context(protocol=PROTOCOL_SSLv23, cert_reqs=None, + check_hostname=False, purpose=Purpose.SERVER_AUTH, + certfile=None, keyfile=None, + cafile=None, capath=None, cadata=None): + """Create a SSLContext object for Python stdlib modules + + All Python stdlib modules shall use this function to create SSLContext + objects in order to keep common settings in one place. The configuration + is less restrict than create_default_context()'s to increase backward + compatibility. + """ + if not isinstance(purpose, _ASN1Object): + raise TypeError(purpose) + + context = SSLContext(protocol) + # SSLv2 considered harmful. + context.options |= OP_NO_SSLv2 + + if cert_reqs is not None: + context.verify_mode = cert_reqs + context.check_hostname = check_hostname + + if keyfile and not certfile: + raise ValueError("certfile must be specified") + if certfile or keyfile: + context.load_cert_chain(certfile, keyfile) + + # load CA root certs + if cafile or capath or cadata: + context.load_verify_locations(cafile, capath, cadata) + elif context.verify_mode != CERT_NONE: + # no explicit cafile, capath or cadata but the verify mode is + # CERT_OPTIONAL or CERT_REQUIRED. Let's try to load default system + # root CA certificates for the given purpose. This may fail silently. + context.load_default_certs(purpose) + + return context class SSLSocket(socket): - """This class implements a subtype of socket.socket that wraps the underlying OS socket in an SSL context when necessary, and provides read and write methods over that channel.""" - def __init__(self, sock, keyfile=None, certfile=None, + def __init__(self, sock=None, keyfile=None, certfile=None, server_side=False, cert_reqs=CERT_NONE, ssl_version=PROTOCOL_SSLv23, ca_certs=None, do_handshake_on_connect=True, - suppress_ragged_eofs=True, ciphers=None): + family=AF_INET, type=SOCK_STREAM, proto=0, fileno=None, + suppress_ragged_eofs=True, npn_protocols=None, ciphers=None, + server_hostname=None, + _context=None): + + if _context: + self._context = _context + else: + if server_side and not certfile: + raise ValueError("certfile must be specified for server-side " + "operations") + if keyfile and not certfile: + raise ValueError("certfile must be specified") + if certfile and not keyfile: + keyfile = certfile + self._context = SSLContext(ssl_version) + self._context.verify_mode = cert_reqs + if ca_certs: + self._context.load_verify_locations(ca_certs) + if certfile: + self._context.load_cert_chain(certfile, keyfile) + if npn_protocols: + self._context.set_npn_protocols(npn_protocols) + if ciphers: + self._context.set_ciphers(ciphers) + self.keyfile = keyfile + self.certfile = certfile + self.cert_reqs = cert_reqs + self.ssl_version = ssl_version + self.ca_certs = ca_certs + self.ciphers = ciphers # Can't use sock.type as other flags (such as SOCK_NONBLOCK) get # mixed in. if sock.getsockopt(SOL_SOCKET, SO_TYPE) != SOCK_STREAM: @@ -122,98 +520,161 @@ class SSLSocket(socket): delattr(self, attr) except AttributeError: pass + if server_side and server_hostname: + raise ValueError("server_hostname can only be specified " + "in client mode") + if self._context.check_hostname and not server_hostname: + if HAS_SNI: + raise ValueError("check_hostname requires server_hostname") + else: + raise ValueError("check_hostname requires server_hostname, " + "but it's not supported by your OpenSSL " + "library") + self.server_side = server_side + self.server_hostname = server_hostname + self.do_handshake_on_connect = do_handshake_on_connect + self.suppress_ragged_eofs = suppress_ragged_eofs - if ciphers is None and ssl_version != _SSLv2_IF_EXISTS: - ciphers = _DEFAULT_CIPHERS - - if certfile and not keyfile: - keyfile = certfile - # see if it's connected + # See if we are connected try: - socket.getpeername(self) - except socket_error, e: + self.getpeername() + except socket_error as e: if e.errno != errno.ENOTCONN: raise - # no, no connection yet - self._connected = False - self._sslobj = None + connected = False else: - # yes, create the SSL object - self._connected = True - self._sslobj = _ssl.sslwrap(self._sock, server_side, - keyfile, certfile, - cert_reqs, ssl_version, ca_certs, - ciphers) - if do_handshake_on_connect: - self.do_handshake() - self.keyfile = keyfile - self.certfile = certfile - self.cert_reqs = cert_reqs - self.ssl_version = ssl_version - self.ca_certs = ca_certs - self.ciphers = ciphers - self.do_handshake_on_connect = do_handshake_on_connect - self.suppress_ragged_eofs = suppress_ragged_eofs + connected = True + + self._closed = False + self._sslobj = None + self._connected = connected + if connected: + # create the SSL object + try: + self._sslobj = self._context._wrap_socket(self._sock, server_side, + server_hostname, ssl_sock=self) + if do_handshake_on_connect: + timeout = self.gettimeout() + if timeout == 0.0: + # non-blocking + raise ValueError("do_handshake_on_connect should not be specified for non-blocking sockets") + self.do_handshake() + + except (OSError, ValueError): + self.close() + raise self._makefile_refs = 0 - def read(self, len=1024): + @property + def context(self): + return self._context + + @context.setter + def context(self, ctx): + self._context = ctx + self._sslobj.context = ctx + + def dup(self): + raise NotImplemented("Can't dup() %s instances" % + self.__class__.__name__) + + def _checkClosed(self, msg=None): + # raise an exception here if you wish to check for spurious closes + pass + def _check_connected(self): + if not self._connected: + # getpeername() will raise ENOTCONN if the socket is really + # not connected; note that we can be connected even without + # _connected being set, e.g. if connect() first returned + # EAGAIN. + self.getpeername() + + def read(self, len=0, buffer=None): """Read up to LEN bytes and return them. Return zero-length string on EOF.""" + self._checkClosed() + if not self._sslobj: + raise ValueError("Read on closed or unwrapped SSL socket.") try: - return self._sslobj.read(len) - except SSLError, x: + if buffer is not None: + v = self._sslobj.read(len, buffer) + else: + v = self._sslobj.read(len or 1024) + return v + except SSLError as x: if x.args[0] == SSL_ERROR_EOF and self.suppress_ragged_eofs: - return '' + if buffer is not None: + return 0 + else: + return b'' else: raise def write(self, data): - """Write DATA to the underlying SSL channel. Returns number of bytes of DATA actually transmitted.""" + self._checkClosed() + if not self._sslobj: + raise ValueError("Write on closed or unwrapped SSL socket.") return self._sslobj.write(data) def getpeercert(self, binary_form=False): - """Returns a formatted version of the data in the certificate provided by the other end of the SSL channel. Return None if no certificate was provided, {} if a certificate was provided, but not validated.""" + self._checkClosed() + self._check_connected() return self._sslobj.peer_certificate(binary_form) - def cipher(self): + def selected_npn_protocol(self): + self._checkClosed() + if not self._sslobj or not _ssl.HAS_NPN: + return None + else: + return self._sslobj.selected_npn_protocol() + def cipher(self): + self._checkClosed() if not self._sslobj: return None else: return self._sslobj.cipher() + def compression(self): + self._checkClosed() + if not self._sslobj: + return None + else: + return self._sslobj.compression() + def send(self, data, flags=0): + self._checkClosed() if self._sslobj: if flags != 0: raise ValueError( "non-zero flags not allowed in calls to send() on %s" % self.__class__) - while True: - try: - v = self._sslobj.write(data) - except SSLError, x: - if x.args[0] == SSL_ERROR_WANT_READ: - return 0 - elif x.args[0] == SSL_ERROR_WANT_WRITE: - return 0 - else: - raise + try: + v = self._sslobj.write(data) + except SSLError as x: + if x.args[0] == SSL_ERROR_WANT_READ: + return 0 + elif x.args[0] == SSL_ERROR_WANT_WRITE: + return 0 else: - return v + raise + else: + return v else: return self._sock.send(data, flags) def sendto(self, data, flags_or_addr, addr=None): + self._checkClosed() if self._sslobj: raise ValueError("sendto not allowed on instances of %s" % self.__class__) @@ -222,7 +683,9 @@ class SSLSocket(socket): else: return self._sock.sendto(data, flags_or_addr, addr) + def sendall(self, data, flags=0): + self._checkClosed() if self._sslobj: if flags != 0: raise ValueError( @@ -238,6 +701,7 @@ class SSLSocket(socket): return socket.sendall(self, data, flags) def recv(self, buflen=1024, flags=0): + self._checkClosed() if self._sslobj: if flags != 0: raise ValueError( @@ -248,6 +712,7 @@ class SSLSocket(socket): return self._sock.recv(buflen, flags) def recv_into(self, buffer, nbytes=None, flags=0): + self._checkClosed() if buffer and (nbytes is None): nbytes = len(buffer) elif nbytes is None: @@ -257,14 +722,12 @@ class SSLSocket(socket): raise ValueError( "non-zero flags not allowed in calls to recv_into() on %s" % self.__class__) - tmp_buffer = self.read(nbytes) - v = len(tmp_buffer) - buffer[:v] = tmp_buffer - return v + return self.read(nbytes, buffer) else: return self._sock.recv_into(buffer, nbytes, flags) def recvfrom(self, buflen=1024, flags=0): + self._checkClosed() if self._sslobj: raise ValueError("recvfrom not allowed on instances of %s" % self.__class__) @@ -272,27 +735,23 @@ class SSLSocket(socket): return self._sock.recvfrom(buflen, flags) def recvfrom_into(self, buffer, nbytes=None, flags=0): + self._checkClosed() if self._sslobj: raise ValueError("recvfrom_into not allowed on instances of %s" % self.__class__) else: return self._sock.recvfrom_into(buffer, nbytes, flags) + def pending(self): + self._checkClosed() if self._sslobj: return self._sslobj.pending() else: return 0 - def unwrap(self): - if self._sslobj: - s = self._sslobj.shutdown() - self._sslobj = None - return s - else: - raise ValueError("No SSL wrapper around " + str(self)) - def shutdown(self, how): + self._checkClosed() self._sslobj = None socket.shutdown(self, how) @@ -303,32 +762,55 @@ class SSLSocket(socket): else: self._makefile_refs -= 1 - def do_handshake(self): - - """Perform a TLS/SSL handshake.""" + def unwrap(self): + if self._sslobj: + s = self._sslobj.shutdown() + self._sslobj = None + return s + else: + raise ValueError("No SSL wrapper around " + str(self)) - self._sslobj.do_handshake() + def _real_close(self): + self._sslobj = None + socket._real_close(self) - def _real_connect(self, addr, return_errno): + def do_handshake(self, block=False): + """Perform a TLS/SSL handshake.""" + self._check_connected() + timeout = self.gettimeout() + try: + if timeout == 0.0 and block: + self.settimeout(None) + self._sslobj.do_handshake() + finally: + self.settimeout(timeout) + + if self.context.check_hostname: + if not self.server_hostname: + raise ValueError("check_hostname needs server_hostname " + "argument") + match_hostname(self.getpeercert(), self.server_hostname) + + def _real_connect(self, addr, connect_ex): + if self.server_side: + raise ValueError("can't connect in server-side mode") # Here we assume that the socket is client-side, and not # connected at the time of the call. We connect it, then wrap it. if self._connected: raise ValueError("attempt to connect already-connected SSLSocket!") - self._sslobj = _ssl.sslwrap(self._sock, False, self.keyfile, self.certfile, - self.cert_reqs, self.ssl_version, - self.ca_certs, self.ciphers) + self._sslobj = self.context._wrap_socket(self._sock, False, self.server_hostname, ssl_sock=self) try: - if return_errno: + if connect_ex: rc = socket.connect_ex(self, addr) else: rc = None socket.connect(self, addr) if not rc: + self._connected = True if self.do_handshake_on_connect: self.do_handshake() - self._connected = True return rc - except socket_error: + except (OSError, ValueError): self._sslobj = None raise @@ -343,27 +825,16 @@ class SSLSocket(socket): return self._real_connect(addr, True) def accept(self): - """Accepts a new connection from a remote client, and returns a tuple containing that new connection wrapped with a server-side SSL channel, and the address of the remote client.""" newsock, addr = socket.accept(self) - try: - return (SSLSocket(newsock, - keyfile=self.keyfile, - certfile=self.certfile, - server_side=True, - cert_reqs=self.cert_reqs, - ssl_version=self.ssl_version, - ca_certs=self.ca_certs, - ciphers=self.ciphers, - do_handshake_on_connect=self.do_handshake_on_connect, - suppress_ragged_eofs=self.suppress_ragged_eofs), - addr) - except socket_error as e: - newsock.close() - raise e + newsock = self.context.wrap_socket(newsock, + do_handshake_on_connect=self.do_handshake_on_connect, + suppress_ragged_eofs=self.suppress_ragged_eofs, + server_side=True) + return newsock, addr def makefile(self, mode='r', bufsize=-1): @@ -376,54 +847,81 @@ class SSLSocket(socket): # the file-like object. return _fileobject(self, mode, bufsize, close=True) + def get_channel_binding(self, cb_type="tls-unique"): + """Get channel binding data for current connection. Raise ValueError + if the requested `cb_type` is not supported. Return bytes of the data + or None if the data is not available (e.g. before the handshake). + """ + if cb_type not in CHANNEL_BINDING_TYPES: + raise ValueError("Unsupported channel binding type") + if cb_type != "tls-unique": + raise NotImplementedError( + "{0} channel binding type not implemented" + .format(cb_type)) + if self._sslobj is None: + return None + return self._sslobj.tls_unique_cb() def wrap_socket(sock, keyfile=None, certfile=None, server_side=False, cert_reqs=CERT_NONE, ssl_version=PROTOCOL_SSLv23, ca_certs=None, do_handshake_on_connect=True, - suppress_ragged_eofs=True, ciphers=None): + suppress_ragged_eofs=True, + ciphers=None): - return SSLSocket(sock, keyfile=keyfile, certfile=certfile, + return SSLSocket(sock=sock, keyfile=keyfile, certfile=certfile, server_side=server_side, cert_reqs=cert_reqs, ssl_version=ssl_version, ca_certs=ca_certs, do_handshake_on_connect=do_handshake_on_connect, suppress_ragged_eofs=suppress_ragged_eofs, ciphers=ciphers) - # some utility functions def cert_time_to_seconds(cert_time): - - """Takes a date-time string in standard ASN1_print form - ("MON DAY 24HOUR:MINUTE:SEC YEAR TIMEZONE") and return - a Python time value in seconds past the epoch.""" - - import time - return time.mktime(time.strptime(cert_time, "%b %d %H:%M:%S %Y GMT")) + """Return the time in seconds since the Epoch, given the timestring + representing the "notBefore" or "notAfter" date from a certificate + in ``"%b %d %H:%M:%S %Y %Z"`` strptime format (C locale). + + "notBefore" or "notAfter" dates must use UTC (RFC 5280). + + Month is one of: Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec + UTC should be specified as GMT (see ASN1_TIME_print()) + """ + from time import strptime + from calendar import timegm + + months = ( + "Jan","Feb","Mar","Apr","May","Jun", + "Jul","Aug","Sep","Oct","Nov","Dec" + ) + time_format = ' %d %H:%M:%S %Y GMT' # NOTE: no month, fixed GMT + try: + month_number = months.index(cert_time[:3].title()) + 1 + except ValueError: + raise ValueError('time data %r does not match ' + 'format "%%b%s"' % (cert_time, time_format)) + else: + # found valid month + tt = strptime(cert_time[3:], time_format) + # return an integer, the previous mktime()-based implementation + # returned a float (fractional seconds are always zero here). + return timegm((tt[0], month_number) + tt[2:6]) PEM_HEADER = "-----BEGIN CERTIFICATE-----" PEM_FOOTER = "-----END CERTIFICATE-----" def DER_cert_to_PEM_cert(der_cert_bytes): - """Takes a certificate in binary DER format and returns the PEM version of it as a string.""" - if hasattr(base64, 'standard_b64encode'): - # preferred because older API gets line-length wrong - f = base64.standard_b64encode(der_cert_bytes) - return (PEM_HEADER + '\n' + - textwrap.fill(f, 64) + '\n' + - PEM_FOOTER + '\n') - else: - return (PEM_HEADER + '\n' + - base64.encodestring(der_cert_bytes) + - PEM_FOOTER + '\n') + f = base64.standard_b64encode(der_cert_bytes).decode('ascii') + return (PEM_HEADER + '\n' + + textwrap.fill(f, 64) + '\n' + + PEM_FOOTER + '\n') def PEM_cert_to_DER_cert(pem_cert_string): - """Takes a certificate in ASCII PEM format and returns the DER-encoded version of it as a byte sequence""" @@ -434,25 +932,25 @@ def PEM_cert_to_DER_cert(pem_cert_string): raise ValueError("Invalid PEM encoding; must end with %s" % PEM_FOOTER) d = pem_cert_string.strip()[len(PEM_HEADER):-len(PEM_FOOTER)] - return base64.decodestring(d) - -def get_server_certificate(addr, ssl_version=PROTOCOL_SSLv3, ca_certs=None): + return base64.decodestring(d.encode('ASCII', 'strict')) +def get_server_certificate(addr, ssl_version=PROTOCOL_SSLv23, ca_certs=None): """Retrieve the certificate from the server at the specified address, and return it as a PEM-encoded string. If 'ca_certs' is specified, validate the server cert against it. If 'ssl_version' is specified, use it in the connection attempt.""" host, port = addr - if (ca_certs is not None): + if ca_certs is not None: cert_reqs = CERT_REQUIRED else: cert_reqs = CERT_NONE - s = wrap_socket(socket(), ssl_version=ssl_version, - cert_reqs=cert_reqs, ca_certs=ca_certs) - s.connect(addr) - dercert = s.getpeercert(True) - s.close() + context = _create_stdlib_context(ssl_version, + cert_reqs=cert_reqs, + cafile=ca_certs) + with closing(create_connection(addr)) as sock: + with closing(context.wrap_socket(sock)) as sslsock: + dercert = sslsock.getpeercert(True) return DER_cert_to_PEM_cert(dercert) def get_protocol_name(protocol_code): diff --git a/Lib/test/capath/4e1295a3.0 b/Lib/test/capath/4e1295a3.0 new file mode 100644 index 0000000..9d7ac23 --- /dev/null +++ b/Lib/test/capath/4e1295a3.0 @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICLDCCAdYCAQAwDQYJKoZIhvcNAQEEBQAwgaAxCzAJBgNVBAYTAlBUMRMwEQYD +VQQIEwpRdWVlbnNsYW5kMQ8wDQYDVQQHEwZMaXNib2ExFzAVBgNVBAoTDk5ldXJv +bmlvLCBMZGEuMRgwFgYDVQQLEw9EZXNlbnZvbHZpbWVudG8xGzAZBgNVBAMTEmJy +dXR1cy5uZXVyb25pby5wdDEbMBkGCSqGSIb3DQEJARYMc2FtcG9AaWtpLmZpMB4X +DTk2MDkwNTAzNDI0M1oXDTk2MTAwNTAzNDI0M1owgaAxCzAJBgNVBAYTAlBUMRMw +EQYDVQQIEwpRdWVlbnNsYW5kMQ8wDQYDVQQHEwZMaXNib2ExFzAVBgNVBAoTDk5l +dXJvbmlvLCBMZGEuMRgwFgYDVQQLEw9EZXNlbnZvbHZpbWVudG8xGzAZBgNVBAMT +EmJydXR1cy5uZXVyb25pby5wdDEbMBkGCSqGSIb3DQEJARYMc2FtcG9AaWtpLmZp +MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL7+aty3S1iBA/+yxjxv4q1MUTd1kjNw +L4lYKbpzzlmC5beaQXeQ2RmGMTXU+mDvuqItjVHOK3DvPK7lTcSGftUCAwEAATAN +BgkqhkiG9w0BAQQFAANBAFqPEKFjk6T6CKTHvaQeEAsX0/8YHPHqH/9AnhSjrwuX +9EBc0n6bVGhN7XaXd6sJ7dym9sbsWxb+pJdurnkxjx4= +-----END CERTIFICATE----- diff --git a/Lib/test/capath/5ed36f99.0 b/Lib/test/capath/5ed36f99.0 new file mode 100644 index 0000000..e7dfc82 --- /dev/null +++ b/Lib/test/capath/5ed36f99.0 @@ -0,0 +1,41 @@ +-----BEGIN CERTIFICATE----- +MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290 +IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB +IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA +Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO +BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi +MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ +ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ +8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6 +zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y +fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7 +w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc +G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k +epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q +laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ +QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU +fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826 +YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w +ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY +gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe +MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0 +IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy +dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw +czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0 +dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl +aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC +AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg +b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB +ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc +nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg +18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c +gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl +Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY +sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T +SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF +CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum +GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk +zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW +omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD +-----END CERTIFICATE----- diff --git a/Lib/test/capath/6e88d7b8.0 b/Lib/test/capath/6e88d7b8.0 new file mode 100644 index 0000000..9d7ac23 --- /dev/null +++ b/Lib/test/capath/6e88d7b8.0 @@ -0,0 +1,14 @@ +-----BEGIN CERTIFICATE----- +MIICLDCCAdYCAQAwDQYJKoZIhvcNAQEEBQAwgaAxCzAJBgNVBAYTAlBUMRMwEQYD +VQQIEwpRdWVlbnNsYW5kMQ8wDQYDVQQHEwZMaXNib2ExFzAVBgNVBAoTDk5ldXJv +bmlvLCBMZGEuMRgwFgYDVQQLEw9EZXNlbnZvbHZpbWVudG8xGzAZBgNVBAMTEmJy +dXR1cy5uZXVyb25pby5wdDEbMBkGCSqGSIb3DQEJARYMc2FtcG9AaWtpLmZpMB4X +DTk2MDkwNTAzNDI0M1oXDTk2MTAwNTAzNDI0M1owgaAxCzAJBgNVBAYTAlBUMRMw +EQYDVQQIEwpRdWVlbnNsYW5kMQ8wDQYDVQQHEwZMaXNib2ExFzAVBgNVBAoTDk5l +dXJvbmlvLCBMZGEuMRgwFgYDVQQLEw9EZXNlbnZvbHZpbWVudG8xGzAZBgNVBAMT +EmJydXR1cy5uZXVyb25pby5wdDEbMBkGCSqGSIb3DQEJARYMc2FtcG9AaWtpLmZp +MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAL7+aty3S1iBA/+yxjxv4q1MUTd1kjNw +L4lYKbpzzlmC5beaQXeQ2RmGMTXU+mDvuqItjVHOK3DvPK7lTcSGftUCAwEAATAN +BgkqhkiG9w0BAQQFAANBAFqPEKFjk6T6CKTHvaQeEAsX0/8YHPHqH/9AnhSjrwuX +9EBc0n6bVGhN7XaXd6sJ7dym9sbsWxb+pJdurnkxjx4= +-----END CERTIFICATE----- diff --git a/Lib/test/capath/99d0fa06.0 b/Lib/test/capath/99d0fa06.0 new file mode 100644 index 0000000..e7dfc82 --- /dev/null +++ b/Lib/test/capath/99d0fa06.0 @@ -0,0 +1,41 @@ +-----BEGIN CERTIFICATE----- +MIIHPTCCBSWgAwIBAgIBADANBgkqhkiG9w0BAQQFADB5MRAwDgYDVQQKEwdSb290 +IENBMR4wHAYDVQQLExVodHRwOi8vd3d3LmNhY2VydC5vcmcxIjAgBgNVBAMTGUNB +IENlcnQgU2lnbmluZyBBdXRob3JpdHkxITAfBgkqhkiG9w0BCQEWEnN1cHBvcnRA +Y2FjZXJ0Lm9yZzAeFw0wMzAzMzAxMjI5NDlaFw0zMzAzMjkxMjI5NDlaMHkxEDAO +BgNVBAoTB1Jvb3QgQ0ExHjAcBgNVBAsTFWh0dHA6Ly93d3cuY2FjZXJ0Lm9yZzEi +MCAGA1UEAxMZQ0EgQ2VydCBTaWduaW5nIEF1dGhvcml0eTEhMB8GCSqGSIb3DQEJ +ARYSc3VwcG9ydEBjYWNlcnQub3JnMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIIC +CgKCAgEAziLA4kZ97DYoB1CW8qAzQIxL8TtmPzHlawI229Z89vGIj053NgVBlfkJ +8BLPRoZzYLdufujAWGSuzbCtRRcMY/pnCujW0r8+55jE8Ez64AO7NV1sId6eINm6 +zWYyN3L69wj1x81YyY7nDl7qPv4coRQKFWyGhFtkZip6qUtTefWIonvuLwphK42y +fk1WpRPs6tqSnqxEQR5YYGUFZvjARL3LlPdCfgv3ZWiYUQXw8wWRBB0bF4LsyFe7 +w2t6iPGwcswlWyCR7BYCEo8y6RcYSNDHBS4CMEK4JZwFaz+qOqfrU0j36NK2B5jc +G8Y0f3/JHIJ6BVgrCFvzOKKrF11myZjXnhCLotLddJr3cQxyYN/Nb5gznZY0dj4k +epKwDpUeb+agRThHqtdB7Uq3EvbXG4OKDy7YCbZZ16oE/9KTfWgu3YtLq1i6L43q +laegw1SJpfvbi1EinbLDvhG+LJGGi5Z4rSDTii8aP8bQUWWHIbEZAWV/RRyH9XzQ +QUxPKZgh/TMfdQwEUfoZd9vUFBzugcMd9Zi3aQaRIt0AUMyBMawSB3s42mhb5ivU +fslfrejrckzzAeVLIL+aplfKkQABi6F1ITe1Yw1nPkZPcCBnzsXWWdsC4PDSy826 +YreQQejdIOQpvGQpQsgi3Hia/0PsmBsJUUtaWsJx8cTLc6nloQsCAwEAAaOCAc4w +ggHKMB0GA1UdDgQWBBQWtTIb1Mfz4OaO873SsDrusjkY0TCBowYDVR0jBIGbMIGY +gBQWtTIb1Mfz4OaO873SsDrusjkY0aF9pHsweTEQMA4GA1UEChMHUm9vdCBDQTEe +MBwGA1UECxMVaHR0cDovL3d3dy5jYWNlcnQub3JnMSIwIAYDVQQDExlDQSBDZXJ0 +IFNpZ25pbmcgQXV0aG9yaXR5MSEwHwYJKoZIhvcNAQkBFhJzdXBwb3J0QGNhY2Vy +dC5vcmeCAQAwDwYDVR0TAQH/BAUwAwEB/zAyBgNVHR8EKzApMCegJaAjhiFodHRw +czovL3d3dy5jYWNlcnQub3JnL3Jldm9rZS5jcmwwMAYJYIZIAYb4QgEEBCMWIWh0 +dHBzOi8vd3d3LmNhY2VydC5vcmcvcmV2b2tlLmNybDA0BglghkgBhvhCAQgEJxYl +aHR0cDovL3d3dy5jYWNlcnQub3JnL2luZGV4LnBocD9pZD0xMDBWBglghkgBhvhC +AQ0ESRZHVG8gZ2V0IHlvdXIgb3duIGNlcnRpZmljYXRlIGZvciBGUkVFIGhlYWQg +b3ZlciB0byBodHRwOi8vd3d3LmNhY2VydC5vcmcwDQYJKoZIhvcNAQEEBQADggIB +ACjH7pyCArpcgBLKNQodgW+JapnM8mgPf6fhjViVPr3yBsOQWqy1YPaZQwGjiHCc +nWKdpIevZ1gNMDY75q1I08t0AoZxPuIrA2jxNGJARjtT6ij0rPtmlVOKTV39O9lg +18p5aTuxZZKmxoGCXJzN600BiqXfEVWqFcofN8CCmHBh22p8lqOOLlQ+TyGpkO/c +gr/c6EWtTZBzCDyUZbAEmXZ/4rzCahWqlwQ3JNgelE5tDlG+1sSPypZt90Pf6DBl +Jzt7u0NDY8RD97LsaMzhGY4i+5jhe1o+ATc7iwiwovOVThrLm82asduycPAtStvY +sONvRUgzEv/+PDIqVPfE94rwiCPCR/5kenHA0R6mY7AHfqQv0wGP3J8rtsYIqQ+T +SCX8Ev2fQtzzxD72V7DX3WnRBnc0CkvSyqD/HMaMyRa+xMwyN2hzXwj7UfdJUzYF +CpUCTPJ5GhD22Dp1nPMd8aINcGeGG7MW9S/lpOt5hvk9C8JzC6WZrG/8Z7jlLwum +GCSNe9FINSkYQKyTYOGWhlC0elnYjyELn8+CkcY7v2vcB5G5l1YjqrZslMZIBjzk +zk6q5PYvCdxTby78dOs6Y5nCpqyJvKeyRKANihDjbPIky/qbn3BHLt4Ui9SyIAmW +omTxJBzcoTWcFbLUvFUufQb1nA5V9FrWk9p2rSVzTMVD +-----END CERTIFICATE----- diff --git a/Lib/test/dh512.pem b/Lib/test/dh512.pem new file mode 100644 index 0000000..200d16c --- /dev/null +++ b/Lib/test/dh512.pem @@ -0,0 +1,9 @@ +-----BEGIN DH PARAMETERS----- +MEYCQQD1Kv884bEpQBgRjXyEpwpy1obEAxnIByl6ypUM2Zafq9AKUJsCRtMIPWak +XUGfnHy9iUsiGSa6q6Jew1XpKgVfAgEC +-----END DH PARAMETERS----- + +These are the 512 bit DH parameters from "Assigned Number for SKIP Protocols" +(http://www.skip-vpn.org/spec/numbers.html). +See there for how they were generated. +Note that g is not a generator, but this is not a problem since p is a safe prime. diff --git a/Lib/test/keycert.passwd.pem b/Lib/test/keycert.passwd.pem new file mode 100644 index 0000000..e905748 --- /dev/null +++ b/Lib/test/keycert.passwd.pem @@ -0,0 +1,33 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,1A8D9D2A02EC698A + +kJYbfZ8L0sfe9Oty3gw0aloNnY5E8fegRfQLZlNoxTl6jNt0nIwI8kDJ36CZgR9c +u3FDJm/KqrfUoz8vW+qEnWhSG7QPX2wWGPHd4K94Yz/FgrRzZ0DoK7XxXq9gOtVA +AVGQhnz32p+6WhfGsCr9ArXEwRZrTk/FvzEPaU5fHcoSkrNVAGX8IpSVkSDwEDQr +Gv17+cfk99UV1OCza6yKHoFkTtrC+PZU71LomBabivS2Oc4B9hYuSR2hF01wTHP+ +YlWNagZOOVtNz4oKK9x9eNQpmfQXQvPPTfusexKIbKfZrMvJoxcm1gfcZ0H/wK6P +6wmXSG35qMOOztCZNtperjs1wzEBXznyK8QmLcAJBjkfarABJX9vBEzZV0OUKhy+ +noORFwHTllphbmydLhu6ehLUZMHPhzAS5UN7srtpSN81eerDMy0RMUAwA7/PofX1 +94Me85Q8jP0PC9ETdsJcPqLzAPETEYu0ELewKRcrdyWi+tlLFrpE5KT/s5ecbl9l +7B61U4Kfd1PIXc/siINhU3A3bYK+845YyUArUOnKf1kEox7p1RpD7yFqVT04lRTo +cibNKATBusXSuBrp2G6GNuhWEOSafWCKJQAzgCYIp6ZTV2khhMUGppc/2H3CF6cO +zX0KtlPVZC7hLkB6HT8SxYUwF1zqWY7+/XPPdc37MeEZ87Q3UuZwqORLY+Z0hpgt +L5JXBCoklZhCAaN2GqwFLXtGiRSRFGY7xXIhbDTlE65Wv1WGGgDLMKGE1gOz3yAo +2jjG1+yAHJUdE69XTFHSqSkvaloA1W03LdMXZ9VuQJ/ySXCie6ABAQ== +-----END RSA PRIVATE KEY----- +-----BEGIN CERTIFICATE----- +MIICVDCCAb2gAwIBAgIJANfHOBkZr8JOMA0GCSqGSIb3DQEBBQUAMF8xCzAJBgNV +BAYTAlhZMRcwFQYDVQQHEw5DYXN0bGUgQW50aHJheDEjMCEGA1UEChMaUHl0aG9u +IFNvZnR3YXJlIEZvdW5kYXRpb24xEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xMDEw +MDgyMzAxNTZaFw0yMDEwMDUyMzAxNTZaMF8xCzAJBgNVBAYTAlhZMRcwFQYDVQQH +Ew5DYXN0bGUgQW50aHJheDEjMCEGA1UEChMaUHl0aG9uIFNvZnR3YXJlIEZvdW5k +YXRpb24xEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw +gYkCgYEA21vT5isq7F68amYuuNpSFlKDPrMUCa4YWYqZRt2OZ+/3NKaZ2xAiSwr7 +6MrQF70t5nLbSPpqE5+5VrS58SY+g/sXLiFd6AplH1wJZwh78DofbFYXUggktFMt +pTyiX8jtP66bkcPkDADA089RI1TQR6Ca+n7HFa7c1fabVV6i3zkCAwEAAaMYMBYw +FAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3DQEBBQUAA4GBAHPctQBEQ4wd +BJ6+JcpIraopLn8BGhbjNWj40mmRqWB/NAWF6M5ne7KpGAu7tLeG4hb1zLaldK8G +lxy2GPSRF6LFS48dpEj2HbMv2nvv6xxalDMJ9+DicWgAKTQ6bcX2j3GUkCR0g/T1 +CRlNBAAlvhKzO7Clpf9l0YKBEfraJByX +-----END CERTIFICATE----- diff --git a/Lib/test/keycert3.pem b/Lib/test/keycert3.pem new file mode 100644 index 0000000..5bfa62c --- /dev/null +++ b/Lib/test/keycert3.pem @@ -0,0 +1,73 @@ +-----BEGIN PRIVATE KEY----- +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAMLgD0kAKDb5cFyP +jbwNfR5CtewdXC+kMXAWD8DLxiTTvhMW7qVnlwOm36mZlszHKvsRf05lT4pegiFM +9z2j1OlaN+ci/X7NU22TNN6crYSiN77FjYJP464j876ndSxyD+rzys386T+1r1aZ +aggEdkj1TsSsv1zWIYKlPIjlvhuxAgMBAAECgYA0aH+T2Vf3WOPv8KdkcJg6gCRe +yJKXOWgWRcicx/CUzOEsTxmFIDPLxqAWA3k7v0B+3vjGw5Y9lycV/5XqXNoQI14j +y09iNsumds13u5AKkGdTJnZhQ7UKdoVHfuP44ZdOv/rJ5/VD6F4zWywpe90pcbK+ +AWDVtusgGQBSieEl1QJBAOyVrUG5l2yoUBtd2zr/kiGm/DYyXlIthQO/A3/LngDW +5/ydGxVsT7lAVOgCsoT+0L4efTh90PjzW8LPQrPBWVMCQQDS3h/FtYYd5lfz+FNL +9CEe1F1w9l8P749uNUD0g317zv1tatIqVCsQWHfVHNdVvfQ+vSFw38OORO00Xqs9 +1GJrAkBkoXXEkxCZoy4PteheO/8IWWLGGr6L7di6MzFl1lIqwT6D8L9oaV2vynFT +DnKop0pa09Unhjyw57KMNmSE2SUJAkEArloTEzpgRmCq4IK2/NpCeGdHS5uqRlbh +1VIa/xGps7EWQl5Mn8swQDel/YP3WGHTjfx7pgSegQfkyaRtGpZ9OQJAa9Vumj8m +JAAtI0Bnga8hgQx7BhTQY4CadDxyiRGOGYhwUzYVCqkb2sbVRH9HnwUaJT7cWBY3 +RnJdHOMXWem7/w== +-----END PRIVATE KEY----- +Certificate: + Data: + Version: 1 (0x0) + Serial Number: 12723342612721443281 (0xb09264b1f2da21d1) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=XY, O=Python Software Foundation CA, CN=our-ca-server + Validity + Not Before: Jan 4 19:47:07 2013 GMT + Not After : Nov 13 19:47:07 2022 GMT + Subject: C=XY, L=Castle Anthrax, O=Python Software Foundation, CN=localhost + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:c2:e0:0f:49:00:28:36:f9:70:5c:8f:8d:bc:0d: + 7d:1e:42:b5:ec:1d:5c:2f:a4:31:70:16:0f:c0:cb: + c6:24:d3:be:13:16:ee:a5:67:97:03:a6:df:a9:99: + 96:cc:c7:2a:fb:11:7f:4e:65:4f:8a:5e:82:21:4c: + f7:3d:a3:d4:e9:5a:37:e7:22:fd:7e:cd:53:6d:93: + 34:de:9c:ad:84:a2:37:be:c5:8d:82:4f:e3:ae:23: + f3:be:a7:75:2c:72:0f:ea:f3:ca:cd:fc:e9:3f:b5: + af:56:99:6a:08:04:76:48:f5:4e:c4:ac:bf:5c:d6: + 21:82:a5:3c:88:e5:be:1b:b1 + Exponent: 65537 (0x10001) + Signature Algorithm: sha1WithRSAEncryption + 2f:42:5f:a3:09:2c:fa:51:88:c7:37:7f:ea:0e:63:f0:a2:9a: + e5:5a:e2:c8:20:f0:3f:60:bc:c8:0f:b6:c6:76:ce:db:83:93: + f5:a3:33:67:01:8e:04:cd:00:9a:73:fd:f3:35:86:fa:d7:13: + e2:46:c6:9d:c0:29:53:d4:a9:90:b8:77:4b:e6:83:76:e4:92: + d6:9c:50:cf:43:d0:c6:01:77:61:9a:de:9b:70:f7:72:cd:59: + 00:31:69:d9:b4:ca:06:9c:6d:c3:c7:80:8c:68:e6:b5:a2:f8: + ef:1d:bb:16:9f:77:77:ef:87:62:22:9b:4d:69:a4:3a:1a:f1: + 21:5e:8c:32:ac:92:fd:15:6b:18:c2:7f:15:0d:98:30:ca:75: + 8f:1a:71:df:da:1d:b2:ef:9a:e8:2d:2e:02:fd:4a:3c:aa:96: + 0b:06:5d:35:b3:3d:24:87:4b:e0:b0:58:60:2f:45:ac:2e:48: + 8a:b0:99:10:65:27:ff:cc:b1:d8:fd:bd:26:6b:b9:0c:05:2a: + f4:45:63:35:51:07:ed:83:85:fe:6f:69:cb:bb:40:a8:ae:b6: + 3b:56:4a:2d:a4:ed:6d:11:2c:4d:ed:17:24:fd:47:bc:d3:41: + a2:d3:06:fe:0c:90:d8:d8:94:26:c4:ff:cc:a1:d8:42:77:eb: + fc:a9:94:71 +-----BEGIN CERTIFICATE----- +MIICpDCCAYwCCQCwkmSx8toh0TANBgkqhkiG9w0BAQUFADBNMQswCQYDVQQGEwJY +WTEmMCQGA1UECgwdUHl0aG9uIFNvZnR3YXJlIEZvdW5kYXRpb24gQ0ExFjAUBgNV +BAMMDW91ci1jYS1zZXJ2ZXIwHhcNMTMwMTA0MTk0NzA3WhcNMjIxMTEzMTk0NzA3 +WjBfMQswCQYDVQQGEwJYWTEXMBUGA1UEBxMOQ2FzdGxlIEFudGhyYXgxIzAhBgNV +BAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMRIwEAYDVQQDEwlsb2NhbGhv +c3QwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAMLgD0kAKDb5cFyPjbwNfR5C +tewdXC+kMXAWD8DLxiTTvhMW7qVnlwOm36mZlszHKvsRf05lT4pegiFM9z2j1Ola +N+ci/X7NU22TNN6crYSiN77FjYJP464j876ndSxyD+rzys386T+1r1aZaggEdkj1 +TsSsv1zWIYKlPIjlvhuxAgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAC9CX6MJLPpR +iMc3f+oOY/CimuVa4sgg8D9gvMgPtsZ2ztuDk/WjM2cBjgTNAJpz/fM1hvrXE+JG +xp3AKVPUqZC4d0vmg3bkktacUM9D0MYBd2Ga3ptw93LNWQAxadm0ygacbcPHgIxo +5rWi+O8duxafd3fvh2Iim01ppDoa8SFejDKskv0VaxjCfxUNmDDKdY8acd/aHbLv +mugtLgL9SjyqlgsGXTWzPSSHS+CwWGAvRawuSIqwmRBlJ//Msdj9vSZruQwFKvRF +YzVRB+2Dhf5vacu7QKiutjtWSi2k7W0RLE3tFyT9R7zTQaLTBv4MkNjYlCbE/8yh +2EJ36/yplHE= +-----END CERTIFICATE----- diff --git a/Lib/test/keycert4.pem b/Lib/test/keycert4.pem new file mode 100644 index 0000000..53355c8 --- /dev/null +++ b/Lib/test/keycert4.pem @@ -0,0 +1,73 @@ +-----BEGIN PRIVATE KEY----- +MIICdgIBADANBgkqhkiG9w0BAQEFAASCAmAwggJcAgEAAoGBAK5UQiMI5VkNs2Qv +L7gUaiDdFevNUXRjU4DHAe3ZzzYLZNE69h9gO9VCSS16tJ5fT5VEu0EZyGr0e3V2 +NkX0ZoU0Hc/UaY4qx7LHmn5SYZpIxhJnkf7SyHJK1zUaGlU0/LxYqIuGCtF5dqx1 +L2OQhEx1GM6RydHdgX69G64LXcY5AgMBAAECgYAhsRMfJkb9ERLMl/oG/5sLQu9L +pWDKt6+ZwdxzlZbggQ85CMYshjLKIod2DLL/sLf2x1PRXyRG131M1E3k8zkkz6de +R1uDrIN/x91iuYzfLQZGh8bMY7Yjd2eoroa6R/7DjpElGejLxOAaDWO0ST2IFQy9 +myTGS2jSM97wcXfsSQJBANP3jelJoS5X6BRjTSneY21wcocxVuQh8pXpErALVNsT +drrFTeaBuZp7KvbtnIM5g2WRNvaxLZlAY/hXPJvi6ncCQQDSix1cebml6EmPlEZS +Mm8gwI2F9ufUunwJmBJcz826Do0ZNGByWDAM/JQZH4FX4GfAFNuj8PUb+GQfadkx +i1DPAkEA0lVsNHojvuDsIo8HGuzarNZQT2beWjJ1jdxh9t7HrTx7LIps6rb/fhOK +Zs0R6gVAJaEbcWAPZ2tFyECInAdnsQJAUjaeXXjuxFkjOFym5PvqpvhpivEx78Bu +JPTr3rAKXmfGMxxfuOa0xK1wSyshP6ZR/RBn/+lcXPKubhHQDOegwwJAJF1DBQnN ++/tLmOPULtDwfP4Zixn+/8GmGOahFoRcu6VIGHmRilJTn6MOButw7Glv2YdeC6l/ +e83Gq6ffLVfKNQ== +-----END PRIVATE KEY----- +Certificate: + Data: + Version: 1 (0x0) + Serial Number: 12723342612721443282 (0xb09264b1f2da21d2) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=XY, O=Python Software Foundation CA, CN=our-ca-server + Validity + Not Before: Jan 4 19:47:07 2013 GMT + Not After : Nov 13 19:47:07 2022 GMT + Subject: C=XY, L=Castle Anthrax, O=Python Software Foundation, CN=fakehostname + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (1024 bit) + Modulus: + 00:ae:54:42:23:08:e5:59:0d:b3:64:2f:2f:b8:14: + 6a:20:dd:15:eb:cd:51:74:63:53:80:c7:01:ed:d9: + cf:36:0b:64:d1:3a:f6:1f:60:3b:d5:42:49:2d:7a: + b4:9e:5f:4f:95:44:bb:41:19:c8:6a:f4:7b:75:76: + 36:45:f4:66:85:34:1d:cf:d4:69:8e:2a:c7:b2:c7: + 9a:7e:52:61:9a:48:c6:12:67:91:fe:d2:c8:72:4a: + d7:35:1a:1a:55:34:fc:bc:58:a8:8b:86:0a:d1:79: + 76:ac:75:2f:63:90:84:4c:75:18:ce:91:c9:d1:dd: + 81:7e:bd:1b:ae:0b:5d:c6:39 + Exponent: 65537 (0x10001) + Signature Algorithm: sha1WithRSAEncryption + ad:45:8a:8e:ef:c6:ef:04:41:5c:2c:4a:84:dc:02:76:0c:d0: + 66:0f:f0:16:04:58:4d:fd:68:b7:b8:d3:a8:41:a5:5c:3c:6f: + 65:3c:d1:f8:ce:43:35:e7:41:5f:53:3d:c9:2c:c3:7d:fc:56: + 4a:fa:47:77:38:9d:bb:97:28:0a:3b:91:19:7f:bc:74:ae:15: + 6b:bd:20:36:67:45:a5:1e:79:d7:75:e6:89:5c:6d:54:84:d1: + 95:d7:a7:b4:33:3c:af:37:c4:79:8f:5e:75:dc:75:c2:18:fb: + 61:6f:2d:dc:38:65:5b:ba:67:28:d0:88:d7:8d:b9:23:5a:8e: + e8:c6:bb:db:ce:d5:b8:41:2a:ce:93:08:b6:95:ad:34:20:18: + d5:3b:37:52:74:50:0b:07:2c:b0:6d:a4:4c:7b:f4:e0:fd:d1: + af:17:aa:20:cd:62:e3:f0:9d:37:69:db:41:bd:d4:1c:fb:53: + 20:da:88:9d:76:26:67:ce:01:90:a7:80:1d:a9:5b:39:73:68: + 54:0a:d1:2a:03:1b:8f:3c:43:5d:5d:c4:51:f1:a7:e7:11:da: + 31:2c:49:06:af:04:f4:b8:3c:99:c4:20:b9:06:36:a2:00:92: + 61:1d:0c:6d:24:05:e2:82:e1:47:db:a0:5f:ba:b9:fb:ba:fa: + 49:12:1e:ce +-----BEGIN CERTIFICATE----- +MIICpzCCAY8CCQCwkmSx8toh0jANBgkqhkiG9w0BAQUFADBNMQswCQYDVQQGEwJY +WTEmMCQGA1UECgwdUHl0aG9uIFNvZnR3YXJlIEZvdW5kYXRpb24gQ0ExFjAUBgNV +BAMMDW91ci1jYS1zZXJ2ZXIwHhcNMTMwMTA0MTk0NzA3WhcNMjIxMTEzMTk0NzA3 +WjBiMQswCQYDVQQGEwJYWTEXMBUGA1UEBxMOQ2FzdGxlIEFudGhyYXgxIzAhBgNV +BAoTGlB5dGhvbiBTb2Z0d2FyZSBGb3VuZGF0aW9uMRUwEwYDVQQDEwxmYWtlaG9z +dG5hbWUwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAK5UQiMI5VkNs2QvL7gU +aiDdFevNUXRjU4DHAe3ZzzYLZNE69h9gO9VCSS16tJ5fT5VEu0EZyGr0e3V2NkX0 +ZoU0Hc/UaY4qx7LHmn5SYZpIxhJnkf7SyHJK1zUaGlU0/LxYqIuGCtF5dqx1L2OQ +hEx1GM6RydHdgX69G64LXcY5AgMBAAEwDQYJKoZIhvcNAQEFBQADggEBAK1Fio7v +xu8EQVwsSoTcAnYM0GYP8BYEWE39aLe406hBpVw8b2U80fjOQzXnQV9TPcksw338 +Vkr6R3c4nbuXKAo7kRl/vHSuFWu9IDZnRaUeedd15olcbVSE0ZXXp7QzPK83xHmP +XnXcdcIY+2FvLdw4ZVu6ZyjQiNeNuSNajujGu9vO1bhBKs6TCLaVrTQgGNU7N1J0 +UAsHLLBtpEx79OD90a8XqiDNYuPwnTdp20G91Bz7UyDaiJ12JmfOAZCngB2pWzlz +aFQK0SoDG488Q11dxFHxp+cR2jEsSQavBPS4PJnEILkGNqIAkmEdDG0kBeKC4Ufb +oF+6ufu6+kkSHs4= +-----END CERTIFICATE----- diff --git a/Lib/test/make_ssl_certs.py b/Lib/test/make_ssl_certs.py new file mode 100644 index 0000000..81d04f8 --- /dev/null +++ b/Lib/test/make_ssl_certs.py @@ -0,0 +1,176 @@ +"""Make the custom certificate and private key files used by test_ssl +and friends.""" + +import os +import shutil +import sys +import tempfile +from subprocess import * + +req_template = """ + [req] + distinguished_name = req_distinguished_name + x509_extensions = req_x509_extensions + prompt = no + + [req_distinguished_name] + C = XY + L = Castle Anthrax + O = Python Software Foundation + CN = {hostname} + + [req_x509_extensions] + subjectAltName = DNS:{hostname} + + [ ca ] + default_ca = CA_default + + [ CA_default ] + dir = cadir + database = $dir/index.txt + crlnumber = $dir/crl.txt + default_md = sha1 + default_days = 3600 + default_crl_days = 3600 + certificate = pycacert.pem + private_key = pycakey.pem + serial = $dir/serial + RANDFILE = $dir/.rand + + policy = policy_match + + [ policy_match ] + countryName = match + stateOrProvinceName = optional + organizationName = match + organizationalUnitName = optional + commonName = supplied + emailAddress = optional + + [ policy_anything ] + countryName = optional + stateOrProvinceName = optional + localityName = optional + organizationName = optional + organizationalUnitName = optional + commonName = supplied + emailAddress = optional + + + [ v3_ca ] + + subjectKeyIdentifier=hash + authorityKeyIdentifier=keyid:always,issuer + basicConstraints = CA:true + + """ + +here = os.path.abspath(os.path.dirname(__file__)) + +def make_cert_key(hostname, sign=False): + print("creating cert for " + hostname) + tempnames = [] + for i in range(3): + with tempfile.NamedTemporaryFile(delete=False) as f: + tempnames.append(f.name) + req_file, cert_file, key_file = tempnames + try: + with open(req_file, 'w') as f: + f.write(req_template.format(hostname=hostname)) + args = ['req', '-new', '-days', '3650', '-nodes', + '-newkey', 'rsa:1024', '-keyout', key_file, + '-config', req_file] + if sign: + with tempfile.NamedTemporaryFile(delete=False) as f: + tempnames.append(f.name) + reqfile = f.name + args += ['-out', reqfile ] + + else: + args += ['-x509', '-out', cert_file ] + check_call(['openssl'] + args) + + if sign: + args = ['ca', '-config', req_file, '-out', cert_file, '-outdir', 'cadir', + '-policy', 'policy_anything', '-batch', '-infiles', reqfile ] + check_call(['openssl'] + args) + + + with open(cert_file, 'r') as f: + cert = f.read() + with open(key_file, 'r') as f: + key = f.read() + return cert, key + finally: + for name in tempnames: + os.remove(name) + +TMP_CADIR = 'cadir' + +def unmake_ca(): + shutil.rmtree(TMP_CADIR) + +def make_ca(): + os.mkdir(TMP_CADIR) + with open(os.path.join('cadir','index.txt'),'a+') as f: + pass # empty file + with open(os.path.join('cadir','crl.txt'),'a+') as f: + f.write("00") + with open(os.path.join('cadir','index.txt.attr'),'w+') as f: + f.write('unique_subject = no') + + with tempfile.NamedTemporaryFile("w") as t: + t.write(req_template.format(hostname='our-ca-server')) + t.flush() + with tempfile.NamedTemporaryFile() as f: + args = ['req', '-new', '-days', '3650', '-extensions', 'v3_ca', '-nodes', + '-newkey', 'rsa:2048', '-keyout', 'pycakey.pem', + '-out', f.name, + '-subj', '/C=XY/L=Castle Anthrax/O=Python Software Foundation CA/CN=our-ca-server'] + check_call(['openssl'] + args) + args = ['ca', '-config', t.name, '-create_serial', + '-out', 'pycacert.pem', '-batch', '-outdir', TMP_CADIR, + '-keyfile', 'pycakey.pem', '-days', '3650', + '-selfsign', '-extensions', 'v3_ca', '-infiles', f.name ] + check_call(['openssl'] + args) + args = ['ca', '-config', t.name, '-gencrl', '-out', 'revocation.crl'] + check_call(['openssl'] + args) + +if __name__ == '__main__': + os.chdir(here) + cert, key = make_cert_key('localhost') + with open('ssl_cert.pem', 'w') as f: + f.write(cert) + with open('ssl_key.pem', 'w') as f: + f.write(key) + print("password protecting ssl_key.pem in ssl_key.passwd.pem") + check_call(['openssl','rsa','-in','ssl_key.pem','-out','ssl_key.passwd.pem','-des3','-passout','pass:somepass']) + check_call(['openssl','rsa','-in','ssl_key.pem','-out','keycert.passwd.pem','-des3','-passout','pass:somepass']) + + with open('keycert.pem', 'w') as f: + f.write(key) + f.write(cert) + + with open('keycert.passwd.pem', 'a+') as f: + f.write(cert) + + # For certificate matching tests + make_ca() + cert, key = make_cert_key('fakehostname') + with open('keycert2.pem', 'w') as f: + f.write(key) + f.write(cert) + + cert, key = make_cert_key('localhost', True) + with open('keycert3.pem', 'w') as f: + f.write(key) + f.write(cert) + + cert, key = make_cert_key('fakehostname', True) + with open('keycert4.pem', 'w') as f: + f.write(key) + f.write(cert) + + unmake_ca() + print("\n\nPlease change the values in test_ssl.py, test_parse_cert function related to notAfter,notBefore and serialNumber") + check_call(['openssl','x509','-in','keycert.pem','-dates','-serial','-noout']) diff --git a/Lib/test/pycacert.pem b/Lib/test/pycacert.pem new file mode 100644 index 0000000..09b1f3e --- /dev/null +++ b/Lib/test/pycacert.pem @@ -0,0 +1,78 @@ +Certificate: + Data: + Version: 3 (0x2) + Serial Number: 12723342612721443280 (0xb09264b1f2da21d0) + Signature Algorithm: sha1WithRSAEncryption + Issuer: C=XY, O=Python Software Foundation CA, CN=our-ca-server + Validity + Not Before: Jan 4 19:47:07 2013 GMT + Not After : Jan 2 19:47:07 2023 GMT + Subject: C=XY, O=Python Software Foundation CA, CN=our-ca-server + Subject Public Key Info: + Public Key Algorithm: rsaEncryption + Public-Key: (2048 bit) + Modulus: + 00:e7:de:e9:e3:0c:9f:00:b6:a1:fd:2b:5b:96:d2: + 6f:cc:e0:be:86:b9:20:5e:ec:03:7a:55:ab:ea:a4: + e9:f9:49:85:d2:66:d5:ed:c7:7a:ea:56:8e:2d:8f: + e7:42:e2:62:28:a9:9f:d6:1b:8e:eb:b5:b4:9c:9f: + 14:ab:df:e6:94:8b:76:1d:3e:6d:24:61:ed:0c:bf: + 00:8a:61:0c:df:5c:c8:36:73:16:00:cd:47:ba:6d: + a4:a4:74:88:83:23:0a:19:fc:09:a7:3c:4a:4b:d3: + e7:1d:2d:e4:ea:4c:54:21:f3:26:db:89:37:18:d4: + 02:bb:40:32:5f:a4:ff:2d:1c:f7:d4:bb:ec:8e:cf: + 5c:82:ac:e6:7c:08:6c:48:85:61:07:7f:25:e0:5c: + e0:bc:34:5f:e0:b9:04:47:75:c8:47:0b:8d:bc:d6: + c8:68:5f:33:83:62:d2:20:44:35:b1:ad:81:1a:8a: + cd:bc:35:b0:5c:8b:47:d6:18:e9:9c:18:97:cc:01: + 3c:29:cc:e8:1e:e4:e4:c1:b8:de:e7:c2:11:18:87: + 5a:93:34:d8:a6:25:f7:14:71:eb:e4:21:a2:d2:0f: + 2e:2e:d4:62:00:35:d3:d6:ef:5c:60:4b:4c:a9:14: + e2:dd:15:58:46:37:33:26:b7:e7:2e:5d:ed:42:e4: + c5:4d + Exponent: 65537 (0x10001) + X509v3 extensions: + X509v3 Subject Key Identifier: + BC:DD:62:D9:76:DA:1B:D2:54:6B:CF:E0:66:9B:1E:1E:7B:56:0C:0B + X509v3 Authority Key Identifier: + keyid:BC:DD:62:D9:76:DA:1B:D2:54:6B:CF:E0:66:9B:1E:1E:7B:56:0C:0B + + X509v3 Basic Constraints: + CA:TRUE + Signature Algorithm: sha1WithRSAEncryption + 7d:0a:f5:cb:8d:d3:5d:bd:99:8e:f8:2b:0f:ba:eb:c2:d9:a6: + 27:4f:2e:7b:2f:0e:64:d8:1c:35:50:4e:ee:fc:90:b9:8d:6d: + a8:c5:c6:06:b0:af:f3:2d:bf:3b:b8:42:07:dd:18:7d:6d:95: + 54:57:85:18:60:47:2f:eb:78:1b:f9:e8:17:fd:5a:0d:87:17: + 28:ac:4c:6a:e6:bc:29:f4:f4:55:70:29:42:de:85:ea:ab:6c: + 23:06:64:30:75:02:8e:53:bc:5e:01:33:37:cc:1e:cd:b8:a4: + fd:ca:e4:5f:65:3b:83:1c:86:f1:55:02:a0:3a:8f:db:91:b7: + 40:14:b4:e7:8d:d2:ee:73:ba:e3:e5:34:2d:bc:94:6f:4e:24: + 06:f7:5f:8b:0e:a7:8e:6b:de:5e:75:f4:32:9a:50:b1:44:33: + 9a:d0:05:e2:78:82:ff:db:da:8a:63:eb:a9:dd:d1:bf:a0:61: + ad:e3:9e:8a:24:5d:62:0e:e7:4c:91:7f:ef:df:34:36:3b:2f: + 5d:f5:84:b2:2f:c4:6d:93:96:1a:6f:30:28:f1:da:12:9a:64: + b4:40:33:1d:bd:de:2b:53:a8:ea:be:d6:bc:4e:96:f5:44:fb: + 32:18:ae:d5:1f:f6:69:af:b6:4e:7b:1d:58:ec:3b:a9:53:a3: + 5e:58:c8:9e +-----BEGIN CERTIFICATE----- +MIIDbTCCAlWgAwIBAgIJALCSZLHy2iHQMA0GCSqGSIb3DQEBBQUAME0xCzAJBgNV +BAYTAlhZMSYwJAYDVQQKDB1QeXRob24gU29mdHdhcmUgRm91bmRhdGlvbiBDQTEW +MBQGA1UEAwwNb3VyLWNhLXNlcnZlcjAeFw0xMzAxMDQxOTQ3MDdaFw0yMzAxMDIx +OTQ3MDdaME0xCzAJBgNVBAYTAlhZMSYwJAYDVQQKDB1QeXRob24gU29mdHdhcmUg +Rm91bmRhdGlvbiBDQTEWMBQGA1UEAwwNb3VyLWNhLXNlcnZlcjCCASIwDQYJKoZI +hvcNAQEBBQADggEPADCCAQoCggEBAOfe6eMMnwC2of0rW5bSb8zgvoa5IF7sA3pV +q+qk6flJhdJm1e3HeupWji2P50LiYiipn9Ybjuu1tJyfFKvf5pSLdh0+bSRh7Qy/ +AIphDN9cyDZzFgDNR7ptpKR0iIMjChn8Cac8SkvT5x0t5OpMVCHzJtuJNxjUArtA +Ml+k/y0c99S77I7PXIKs5nwIbEiFYQd/JeBc4Lw0X+C5BEd1yEcLjbzWyGhfM4Ni +0iBENbGtgRqKzbw1sFyLR9YY6ZwYl8wBPCnM6B7k5MG43ufCERiHWpM02KYl9xRx +6+QhotIPLi7UYgA109bvXGBLTKkU4t0VWEY3Mya35y5d7ULkxU0CAwEAAaNQME4w +HQYDVR0OBBYEFLzdYtl22hvSVGvP4GabHh57VgwLMB8GA1UdIwQYMBaAFLzdYtl2 +2hvSVGvP4GabHh57VgwLMAwGA1UdEwQFMAMBAf8wDQYJKoZIhvcNAQEFBQADggEB +AH0K9cuN0129mY74Kw+668LZpidPLnsvDmTYHDVQTu78kLmNbajFxgawr/Mtvzu4 +QgfdGH1tlVRXhRhgRy/reBv56Bf9Wg2HFyisTGrmvCn09FVwKULeheqrbCMGZDB1 +Ao5TvF4BMzfMHs24pP3K5F9lO4MchvFVAqA6j9uRt0AUtOeN0u5zuuPlNC28lG9O +JAb3X4sOp45r3l519DKaULFEM5rQBeJ4gv/b2opj66nd0b+gYa3jnookXWIO50yR +f+/fNDY7L131hLIvxG2TlhpvMCjx2hKaZLRAMx293itTqOq+1rxOlvVE+zIYrtUf +9mmvtk57HVjsO6lTo15YyJ4= +-----END CERTIFICATE----- diff --git a/Lib/test/revocation.crl b/Lib/test/revocation.crl new file mode 100644 index 0000000..6d89b08 --- /dev/null +++ b/Lib/test/revocation.crl @@ -0,0 +1,11 @@ +-----BEGIN X509 CRL----- +MIIBpjCBjwIBATANBgkqhkiG9w0BAQUFADBNMQswCQYDVQQGEwJYWTEmMCQGA1UE +CgwdUHl0aG9uIFNvZnR3YXJlIEZvdW5kYXRpb24gQ0ExFjAUBgNVBAMMDW91ci1j +YS1zZXJ2ZXIXDTEzMTEyMTE3MDg0N1oXDTIzMDkzMDE3MDg0N1qgDjAMMAoGA1Ud +FAQDAgEAMA0GCSqGSIb3DQEBBQUAA4IBAQCNJXC2mVKauEeN3LlQ3ZtM5gkH3ExH ++i4bmJjtJn497WwvvoIeUdrmVXgJQR93RtV37hZwN0SXMLlNmUZPH4rHhihayw4m +unCzVj/OhCCY7/TPjKuJ1O/0XhaLBpBVjQN7R/1ujoRKbSia/CD3vcn7Fqxzw7LK +fSRCKRGTj1CZiuxrphtFchwALXSiFDy9mr2ZKhImcyq1PydfgEzU78APpOkMQsIC +UNJ/cf3c9emzf+dUtcMEcejQ3mynBo4eIGg1EW42bz4q4hSjzQlKcBV0muw5qXhc +HOxH2iTFhQ7SrvVuK/dM14rYM4B5mSX3nRC1kNmXpS9j3wJDhuwmjHed +-----END X509 CRL----- diff --git a/Lib/test/sha256.pem b/Lib/test/sha256.pem index 9475576..d3db4b8 100644 --- a/Lib/test/sha256.pem +++ b/Lib/test/sha256.pem @@ -1,128 +1,128 @@ # Certificate chain for https://sha256.tbs-internet.com - 0 s:/C=FR/postalCode=14000/ST=Calvados/L=CAEN/street=22 rue de Bretagne/O=TBS INTERNET/OU=0002 440443810/OU=Certificats TBS X509/CN=ecom.tbs-x509.com - i:/C=FR/ST=Calvados/L=Caen/O=TBS INTERNET/OU=Terms and Conditions: http://www.tbs-internet.com/CA/repository/OU=TBS INTERNET CA/CN=TBS X509 CA business + 0 s:/C=FR/postalCode=14000/ST=Calvados/L=CAEN/street=22 rue de Bretagne/O=TBS INTERNET/OU=0002 440443810/OU=sha-256 production/CN=sha256.tbs-internet.com + i:/C=FR/ST=Calvados/L=Caen/O=TBS INTERNET/OU=Terms and Conditions: http://www.tbs-internet.com/CA/repository/OU=TBS INTERNET CA/CN=TBS X509 CA SGC -----BEGIN CERTIFICATE----- -MIIGTjCCBTagAwIBAgIQOh3d9dNDPq1cSdJmEiMpqDANBgkqhkiG9w0BAQUFADCB -yTELMAkGA1UEBhMCRlIxETAPBgNVBAgTCENhbHZhZG9zMQ0wCwYDVQQHEwRDYWVu -MRUwEwYDVQQKEwxUQlMgSU5URVJORVQxSDBGBgNVBAsTP1Rlcm1zIGFuZCBDb25k -aXRpb25zOiBodHRwOi8vd3d3LnRicy1pbnRlcm5ldC5jb20vQ0EvcmVwb3NpdG9y -eTEYMBYGA1UECxMPVEJTIElOVEVSTkVUIENBMR0wGwYDVQQDExRUQlMgWDUwOSBD -QSBidXNpbmVzczAeFw0xMTAxMjUwMDAwMDBaFw0xMzAyMDUyMzU5NTlaMIHHMQsw -CQYDVQQGEwJGUjEOMAwGA1UEERMFMTQwMDAxETAPBgNVBAgTCENhbHZhZG9zMQ0w -CwYDVQQHEwRDQUVOMRswGQYDVQQJExIyMiBydWUgZGUgQnJldGFnbmUxFTATBgNV -BAoTDFRCUyBJTlRFUk5FVDEXMBUGA1UECxMOMDAwMiA0NDA0NDM4MTAxHTAbBgNV -BAsTFENlcnRpZmljYXRzIFRCUyBYNTA5MRowGAYDVQQDExFlY29tLnRicy14NTA5 -LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKRrlHUnJ++1lpcg -jtYco7cdmRe+EEfTmwPfCdfV3G1QfsTSvY6FfMpm/83pqHfT+4ANwr18wD9ZrAEN -G16mf9VdCGK12+TP7DmqeZyGIqlFFoahQnmb8EarvE43/1UeQ2CV9XmzwZvpqeli -LfXsFonawrY3H6ZnMwS64St61Z+9gdyuZ/RbsoZBbT5KUjDEG844QRU4OT1IGeEI -eY5NM5RNIh6ZNhVtqeeCxMS7afONkHQrOco73RdSTRck/Hj96Ofl3MHNHryr+AMK -DGFk1kLCZGpPdXtkxXvaDeQoiYDlil26CWc+YK6xyDPMdsWvoG14ZLyCpzMXA7/7 -4YAQRH0CAwEAAaOCAjAwggIsMB8GA1UdIwQYMBaAFBoJBMz5CY+7HqDO1KQUf0vV -I1jNMB0GA1UdDgQWBBQgOU8HsWzbmD4WZP5Wtdw7jca2WDAOBgNVHQ8BAf8EBAMC -BaAwDAYDVR0TAQH/BAIwADAdBgNVHSUEFjAUBggrBgEFBQcDAQYIKwYBBQUHAwIw -TAYDVR0gBEUwQzBBBgsrBgEEAYDlNwIBATAyMDAGCCsGAQUFBwIBFiRodHRwczov -L3d3dy50YnMtaW50ZXJuZXQuY29tL0NBL0NQUzEwdwYDVR0fBHAwbjA3oDWgM4Yx -aHR0cDovL2NybC50YnMtaW50ZXJuZXQuY29tL1RCU1g1MDlDQWJ1c2luZXNzLmNy -bDAzoDGgL4YtaHR0cDovL2NybC50YnMteDUwOS5jb20vVEJTWDUwOUNBYnVzaW5l -c3MuY3JsMIGwBggrBgEFBQcBAQSBozCBoDA9BggrBgEFBQcwAoYxaHR0cDovL2Ny -dC50YnMtaW50ZXJuZXQuY29tL1RCU1g1MDlDQWJ1c2luZXNzLmNydDA5BggrBgEF -BQcwAoYtaHR0cDovL2NydC50YnMteDUwOS5jb20vVEJTWDUwOUNBYnVzaW5lc3Mu -Y3J0MCQGCCsGAQUFBzABhhhodHRwOi8vb2NzcC50YnMteDUwOS5jb20wMwYDVR0R -BCwwKoIRZWNvbS50YnMteDUwOS5jb22CFXd3dy5lY29tLnRicy14NTA5LmNvbTAN -BgkqhkiG9w0BAQUFAAOCAQEArT4NHfbY87bGAw8lPV4DmHlmuDuVp/y7ltO3Ynse -3Rz8RxW2AzuO0Oy2F0Cu4yWKtMyEyMXyHqWtae7ElRbdTu5w5GwVBLJHClCzC8S9 -SpgMMQTx3Rgn8vjkHuU9VZQlulZyiPK7yunjc7c310S9FRZ7XxOwf8Nnx4WnB+No -WrfApzhhQl31w+RyrNxZe58hCfDDHmevRvwLjQ785ZoQXJDj2j3qAD4aI2yB8lB5 -oaE1jlCJzC7Kmz/Y9jzfmv/zAs1LQTm9ktevv4BTUFaGjv9jxnQ1xnS862ZiouLW -zZYIlYPf4F6JjXGiIQgQRglILUfq3ftJd9/ok9W9ZF8h8w== +MIIGXDCCBUSgAwIBAgIRAKpVmHgg9nfCodAVwcP4siwwDQYJKoZIhvcNAQELBQAw +gcQxCzAJBgNVBAYTAkZSMREwDwYDVQQIEwhDYWx2YWRvczENMAsGA1UEBxMEQ2Fl +bjEVMBMGA1UEChMMVEJTIElOVEVSTkVUMUgwRgYDVQQLEz9UZXJtcyBhbmQgQ29u +ZGl0aW9uczogaHR0cDovL3d3dy50YnMtaW50ZXJuZXQuY29tL0NBL3JlcG9zaXRv +cnkxGDAWBgNVBAsTD1RCUyBJTlRFUk5FVCBDQTEYMBYGA1UEAxMPVEJTIFg1MDkg +Q0EgU0dDMB4XDTEyMDEwNDAwMDAwMFoXDTE0MDIxNzIzNTk1OVowgcsxCzAJBgNV +BAYTAkZSMQ4wDAYDVQQREwUxNDAwMDERMA8GA1UECBMIQ2FsdmFkb3MxDTALBgNV +BAcTBENBRU4xGzAZBgNVBAkTEjIyIHJ1ZSBkZSBCcmV0YWduZTEVMBMGA1UEChMM +VEJTIElOVEVSTkVUMRcwFQYDVQQLEw4wMDAyIDQ0MDQ0MzgxMDEbMBkGA1UECxMS +c2hhLTI1NiBwcm9kdWN0aW9uMSAwHgYDVQQDExdzaGEyNTYudGJzLWludGVybmV0 +LmNvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAKQIX/zdJcyxty0m +PM1XQSoSSifueS3AVcgqMsaIKS/u+rYzsv4hQ/qA6vLn5m5/ewUcZDj7zdi6rBVf +PaVNXJ6YinLX0tkaW8TEjeVuZG5yksGZlhCt1CJ1Ho9XLiLaP4uJ7MCoNUntpJ+E +LfrOdgsIj91kPmwjDJeztVcQCvKzhjVJA/KxdInc0JvOATn7rpaSmQI5bvIjufgo +qVsTPwVFzuUYULXBk7KxRT7MiEqnd5HvviNh0285QC478zl3v0I0Fb5El4yD3p49 +IthcRnxzMKc0UhU5ogi0SbONyBfm/mzONVfSxpM+MlyvZmJqrbuuLoEDzJD+t8PU +xSuzgbcCAwEAAaOCAj4wggI6MB8GA1UdIwQYMBaAFAdEdoWTKLx/bXjSCuv6TEvf +2YIfMB0GA1UdDgQWBBT/qTGYdaj+f61c2IRFL/B1eEsM8DAOBgNVHQ8BAf8EBAMC +BaAwDAYDVR0TAQH/BAIwADA0BgNVHSUELTArBggrBgEFBQcDAQYIKwYBBQUHAwIG +CisGAQQBgjcKAwMGCWCGSAGG+EIEATBLBgNVHSAERDBCMEAGCisGAQQB5TcCBAEw +MjAwBggrBgEFBQcCARYkaHR0cHM6Ly93d3cudGJzLWludGVybmV0LmNvbS9DQS9D +UFM0MG0GA1UdHwRmMGQwMqAwoC6GLGh0dHA6Ly9jcmwudGJzLWludGVybmV0LmNv +bS9UQlNYNTA5Q0FTR0MuY3JsMC6gLKAqhihodHRwOi8vY3JsLnRicy14NTA5LmNv +bS9UQlNYNTA5Q0FTR0MuY3JsMIGmBggrBgEFBQcBAQSBmTCBljA4BggrBgEFBQcw +AoYsaHR0cDovL2NydC50YnMtaW50ZXJuZXQuY29tL1RCU1g1MDlDQVNHQy5jcnQw +NAYIKwYBBQUHMAKGKGh0dHA6Ly9jcnQudGJzLXg1MDkuY29tL1RCU1g1MDlDQVNH +Qy5jcnQwJAYIKwYBBQUHMAGGGGh0dHA6Ly9vY3NwLnRicy14NTA5LmNvbTA/BgNV +HREEODA2ghdzaGEyNTYudGJzLWludGVybmV0LmNvbYIbd3d3LnNoYTI1Ni50YnMt +aW50ZXJuZXQuY29tMA0GCSqGSIb3DQEBCwUAA4IBAQA0pOuL8QvAa5yksTbGShzX +ABApagunUGoEydv4YJT1MXy9tTp7DrWaozZSlsqBxrYAXP1d9r2fuKbEniYHxaQ0 +UYaf1VSIlDo1yuC8wE7wxbHDIpQ/E5KAyxiaJ8obtDhFstWAPAH+UoGXq0kj2teN +21sFQ5dXgA95nldvVFsFhrRUNB6xXAcaj0VZFhttI0ZfQZmQwEI/P+N9Jr40OGun +aa+Dn0TMeUH4U20YntfLbu2nDcJcYfyurm+8/0Tr4HznLnedXu9pCPYj0TaddrgT +XO0oFiyy7qGaY6+qKh71yD64Y3ycCJ/HR9Wm39mjZYc9ezYwT4noP6r7Lk8YO7/q -----END CERTIFICATE----- - 1 s:/C=FR/ST=Calvados/L=Caen/O=TBS INTERNET/OU=Terms and Conditions: http://www.tbs-internet.com/CA/repository/OU=TBS INTERNET CA/CN=TBS X509 CA business + 1 s:/C=FR/ST=Calvados/L=Caen/O=TBS INTERNET/OU=Terms and Conditions: http://www.tbs-internet.com/CA/repository/OU=TBS INTERNET CA/CN=TBS X509 CA SGC i:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root -----BEGIN CERTIFICATE----- -MIIFPzCCBCegAwIBAgIQDlBz/++iRSmLDeVRHT/hADANBgkqhkiG9w0BAQUFADBv +MIIFVjCCBD6gAwIBAgIQXpDZ0ETJMV02WTx3GTnhhTANBgkqhkiG9w0BAQUFADBv MQswCQYDVQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFk ZFRydXN0IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBF -eHRlcm5hbCBDQSBSb290MB4XDTA1MTIwMTAwMDAwMFoXDTE5MDcwOTE4MTkyMlow -gckxCzAJBgNVBAYTAkZSMREwDwYDVQQIEwhDYWx2YWRvczENMAsGA1UEBxMEQ2Fl +eHRlcm5hbCBDQSBSb290MB4XDTA1MTIwMTAwMDAwMFoXDTE5MDYyNDE5MDYzMFow +gcQxCzAJBgNVBAYTAkZSMREwDwYDVQQIEwhDYWx2YWRvczENMAsGA1UEBxMEQ2Fl bjEVMBMGA1UEChMMVEJTIElOVEVSTkVUMUgwRgYDVQQLEz9UZXJtcyBhbmQgQ29u ZGl0aW9uczogaHR0cDovL3d3dy50YnMtaW50ZXJuZXQuY29tL0NBL3JlcG9zaXRv -cnkxGDAWBgNVBAsTD1RCUyBJTlRFUk5FVCBDQTEdMBsGA1UEAxMUVEJTIFg1MDkg -Q0EgYnVzaW5lc3MwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQDB1PAU -qudCcz3tmyGcf+u6EkZqonKKHrV4gZYbvVkIRojmmlhfi/jwvpHvo8bqSt/9Rj5S -jhCDW0pcbI+IPPtD1Jy+CHNSfnMqVDy6CKQ3p5maTzCMG6ZT+XjnvcND5v+FtaiB -xk1iCX6uvt0jeUtdZvYbyytsSDE6c3Y5//wRxOF8tM1JxibwO3pyER26jbbN2gQz -m/EkdGjLdJ4svPk23WDAvQ6G0/z2LcAaJB+XLfqRwfQpHQvfKa1uTi8PivC8qtip -rmNQMMPMjxSK2azX8cKjjTDJiUKaCb4VHlJDWKEsCFRpgJAoAuX8f7Yfs1M4esGo -sWb3PGspK3O22uIlAgMBAAGjggF6MIIBdjAdBgNVHQ4EFgQUGgkEzPkJj7seoM7U -pBR/S9UjWM0wDgYDVR0PAQH/BAQDAgEGMBIGA1UdEwEB/wQIMAYBAf8CAQAwGAYD -VR0gBBEwDzANBgsrBgEEAYDlNwIBATB7BgNVHR8EdDByMDigNqA0hjJodHRwOi8v -Y3JsLmNvbW9kb2NhLmNvbS9BZGRUcnVzdEV4dGVybmFsQ0FSb290LmNybDA2oDSg -MoYwaHR0cDovL2NybC5jb21vZG8ubmV0L0FkZFRydXN0RXh0ZXJuYWxDQVJvb3Qu -Y3JsMIGGBggrBgEFBQcBAQR6MHgwOwYIKwYBBQUHMAKGL2h0dHA6Ly9jcnQuY29t -b2RvY2EuY29tL0FkZFRydXN0VVROU2VydmVyQ0EuY3J0MDkGCCsGAQUFBzAChi1o -dHRwOi8vY3J0LmNvbW9kby5uZXQvQWRkVHJ1c3RVVE5TZXJ2ZXJDQS5jcnQwEQYJ -YIZIAYb4QgEBBAQDAgIEMA0GCSqGSIb3DQEBBQUAA4IBAQA7mqrMgk/MrE6QnbNA -h4nRCn2ti4bg4w2C3lB6bSvRPnYwuNw9Jb8vuKkNFzRDxNJXqVDZdfFW5CVQJuyd -nfAx83+wk+spzvFaE1KhFYfN9G9pQfXUfvDRoIcJgPEKUXL1wRiOG+IjU3VVI8pg -IgqHkr7ylln5i5zCiFAPuIJmYUSFg/gxH5xkCNcjJqqrHrHatJr6Qrrke93joupw -oU1njfAcZtYp6fbiK6u2b1pJqwkVBE8RsfLnPhRj+SFbpvjv8Od7o/ieJhFIYQNU -k2jX2u8qZnAiNw93LZW9lpYjtuvMXq8QQppENNja5b53q7UwI+lU7ZGjZ7quuESp -J6/5 +cnkxGDAWBgNVBAsTD1RCUyBJTlRFUk5FVCBDQTEYMBYGA1UEAxMPVEJTIFg1MDkg +Q0EgU0dDMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAsgOkO3f7wzN6 +rOjg45tR5vjBfzK7qmV9IBxb/QW9EEXxG+E7FNhZqQLtwGBKoSsHTnQqV75wWMk0 +9tinWvftBkSpj5sTi/8cbzJfUvTSVYh3Qxv6AVVjMMH/ruLjE6y+4PoaPs8WoYAQ +ts5R4Z1g8c/WnTepLst2x0/Wv7GmuoQi+gXvHU6YrBiu7XkeYhzc95QdviWSJRDk +owhb5K43qhcvjRmBfO/paGlCliDGZp8mHwrI21mwobWpVjTxZRwYO3bd4+TGcI4G +Ie5wmHwE8F7SK1tgSqbBacKjDa93j7txKkfz/Yd2n7TGqOXiHPsJpG655vrKtnXk +9vs1zoDeJQIDAQABo4IBljCCAZIwHQYDVR0OBBYEFAdEdoWTKLx/bXjSCuv6TEvf +2YIfMA4GA1UdDwEB/wQEAwIBBjASBgNVHRMBAf8ECDAGAQH/AgEAMCAGA1UdJQQZ +MBcGCisGAQQBgjcKAwMGCWCGSAGG+EIEATAYBgNVHSAEETAPMA0GCysGAQQBgOU3 +AgQBMHsGA1UdHwR0MHIwOKA2oDSGMmh0dHA6Ly9jcmwuY29tb2RvY2EuY29tL0Fk +ZFRydXN0RXh0ZXJuYWxDQVJvb3QuY3JsMDagNKAyhjBodHRwOi8vY3JsLmNvbW9k +by5uZXQvQWRkVHJ1c3RFeHRlcm5hbENBUm9vdC5jcmwwgYAGCCsGAQUFBwEBBHQw +cjA4BggrBgEFBQcwAoYsaHR0cDovL2NydC5jb21vZG9jYS5jb20vQWRkVHJ1c3RV +VE5TR0NDQS5jcnQwNgYIKwYBBQUHMAKGKmh0dHA6Ly9jcnQuY29tb2RvLm5ldC9B +ZGRUcnVzdFVUTlNHQ0NBLmNydDARBglghkgBhvhCAQEEBAMCAgQwDQYJKoZIhvcN +AQEFBQADggEBAK2zEzs+jcIrVK9oDkdDZNvhuBYTdCfpxfFs+OAujW0bIfJAy232 +euVsnJm6u/+OrqKudD2tad2BbejLLXhMZViaCmK7D9nrXHx4te5EP8rL19SUVqLY +1pTnv5dhNgEgvA7n5lIzDSYs7yRLsr7HJsYPr6SeYSuZizyX1SNz7ooJ32/F3X98 +RB0Mlc/E0OyOrkQ9/y5IrnpnaSora8CnUrV5XNOg+kyCz9edCyx4D5wXYcwZPVWz +8aDqquESrezPyjtfi4WRO4s/VD3HLZvOxzMrWAVYCDG9FxaOhF0QGuuG1F7F3GKV +v6prNyCl016kRl2j1UT+a7gLd8fA25A4C9E= -----END CERTIFICATE----- 2 s:/C=SE/O=AddTrust AB/OU=AddTrust External TTP Network/CN=AddTrust External CA Root - i:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN-USERFirst-Hardware + i:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN - DATACorp SGC -----BEGIN CERTIFICATE----- -MIIETzCCAzegAwIBAgIQHM5EYpUZep1jUvnyI6m2mDANBgkqhkiG9w0BAQUFADCB -lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +MIIEZjCCA06gAwIBAgIQUSYKkxzif5zDpV954HKugjANBgkqhkiG9w0BAQUFADCB +kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho -dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt -SGFyZHdhcmUwHhcNMDUwNjA3MDgwOTEwWhcNMTkwNzA5MTgxOTIyWjBvMQswCQYD -VQQGEwJTRTEUMBIGA1UEChMLQWRkVHJ1c3QgQUIxJjAkBgNVBAsTHUFkZFRydXN0 -IEV4dGVybmFsIFRUUCBOZXR3b3JrMSIwIAYDVQQDExlBZGRUcnVzdCBFeHRlcm5h -bCBDQSBSb290MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAt/caM+by -AAQtOeBOW+0fvGwPzbX6I7bO3psRM5ekKUx9k5+9SryT7QMa44/P5W1QWtaXKZRa -gLBJetsulf24yr83OC0ePpFBrXBWx/BPP+gynnTKyJBU6cZfD3idmkA8Dqxhql4U -j56HoWpQ3NeaTq8Fs6ZxlJxxs1BgCscTnTgHhgKo6ahpJhiQq0ywTyOrOk+E2N/O -n+Fpb7vXQtdrROTHre5tQV9yWnEIN7N5ZaRZoJQ39wAvDcKSctrQOHLbFKhFxF0q -fbe01sTurM0TRLfJK91DACX6YblpalgjEbenM49WdVn1zSnXRrcKK2W200JvFbK4 -e/vv6V1T1TRaJwIDAQABo4G9MIG6MB8GA1UdIwQYMBaAFKFyXyYbKJhDlV0HN9WF -lp1L0sNFMB0GA1UdDgQWBBStvZh6NLQm9/rEJlTvA73gJMtUGjAOBgNVHQ8BAf8E -BAMCAQYwDwYDVR0TAQH/BAUwAwEB/zARBglghkgBhvhCAQEEBAMCAQIwRAYDVR0f -BD0wOzA5oDegNYYzaHR0cDovL2NybC51c2VydHJ1c3QuY29tL1VUTi1VU0VSRmly -c3QtSGFyZHdhcmUuY3JsMA0GCSqGSIb3DQEBBQUAA4IBAQByQhANOs4kClrwF8BW -onvUOGCSjRK52zYZgDXYNjDtmr5rJ6NyPFDNn+JxkLpjYetIFMTbSRe679Bt8m7a -gIAoQYFQtxMuyLnJegB2aEbQiIxh/tC21UcFF7ktdnDoTlA6w3pLuvunaI84Of3o -2YBrhzkTbCfaYk5JRlTpudW9DkUkHBsyx3nknPKnplkIGaK0jgn8E0n+SFabYaHk -I9LroYT/+JtLefh9lgBdAgVv0UPbzoGfuDsrk/Zh+UrgbLFpHoVnElhzbkh64Z0X -OGaJunQc68cCZu5HTn/aK7fBGMcVflRCXLVEQpU9PIAdGA8Ynvg684t8GMaKsRl1 -jIGZ +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw +IFNHQzAeFw0wNTA2MDcwODA5MTBaFw0xOTA2MjQxOTA2MzBaMG8xCzAJBgNVBAYT +AlNFMRQwEgYDVQQKEwtBZGRUcnVzdCBBQjEmMCQGA1UECxMdQWRkVHJ1c3QgRXh0 +ZXJuYWwgVFRQIE5ldHdvcmsxIjAgBgNVBAMTGUFkZFRydXN0IEV4dGVybmFsIENB +IFJvb3QwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC39xoz5vIABC05 +4E5b7R+8bA/Ntfojts7emxEzl6QpTH2Tn71KvJPtAxrjj8/lbVBa1pcplFqAsEl6 +2y6V/bjKvzc4LR4+kUGtcFbH8E8/6DKedMrIkFTpxl8PeJ2aQDwOrGGqXhSPnoeh +alDc15pOrwWzpnGUnHGzUGAKxxOdOAeGAqjpqGkmGJCrTLBPI6s6T4TY386f4Wlv +u9dC12tE5Met7m1BX3JacQg3s3llpFmglDf3AC8NwpJy2tA4ctsUqEXEXSp9t7TW +xO6szRNEt8kr3UMAJfphuWlqWCMRt6czj1Z1WfXNKddGtworZbbTQm8Vsrh7++/p +XVPVNFonAgMBAAGjgdgwgdUwHwYDVR0jBBgwFoAUUzLRs89/+uDxoF2FTpLSnkUd +tE8wHQYDVR0OBBYEFK29mHo0tCb3+sQmVO8DveAky1QaMA4GA1UdDwEB/wQEAwIB +BjAPBgNVHRMBAf8EBTADAQH/MBEGCWCGSAGG+EIBAQQEAwIBAjAgBgNVHSUEGTAX +BgorBgEEAYI3CgMDBglghkgBhvhCBAEwPQYDVR0fBDYwNDAyoDCgLoYsaHR0cDov +L2NybC51c2VydHJ1c3QuY29tL1VUTi1EQVRBQ29ycFNHQy5jcmwwDQYJKoZIhvcN +AQEFBQADggEBAMbuUxdoFLJRIh6QWA2U/b3xcOWGLcM2MY9USEbnLQg3vGwKYOEO +rVE04BKT6b64q7gmtOmWPSiPrmQH/uAB7MXjkesYoPF1ftsK5p+R26+udd8jkWjd +FwBaS/9kbHDrARrQkNnHptZt9hPk/7XJ0h4qy7ElQyZ42TCbTg0evmnv3+r+LbPM ++bDdtRTKkdSytaX7ARmjR3mfnYyVhzT4HziS2jamEfpr62vp3EV4FTkG101B5CHI +3C+H0be/SGB1pWLLJN47YaApIKa+xWycxOkKaSLvkTr6Jq/RW0GnOuL4OAdCq8Fb ++M5tug8EPzI0rNwEKNdwMBQmBsTkm5jVz3g= -----END CERTIFICATE----- - 3 s:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN-USERFirst-Hardware - i:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN-USERFirst-Hardware + 3 s:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN - DATACorp SGC + i:/C=US/ST=UT/L=Salt Lake City/O=The USERTRUST Network/OU=http://www.usertrust.com/CN=UTN - DATACorp SGC -----BEGIN CERTIFICATE----- -MIIEdDCCA1ygAwIBAgIQRL4Mi1AAJLQR0zYq/mUK/TANBgkqhkiG9w0BAQUFADCB -lzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug +MIIEXjCCA0agAwIBAgIQRL4Mi1AAIbQR0ypoBqmtaTANBgkqhkiG9w0BAQUFADCB +kzELMAkGA1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2Ug Q2l0eTEeMBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExho -dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3Qt -SGFyZHdhcmUwHhcNOTkwNzA5MTgxMDQyWhcNMTkwNzA5MTgxOTIyWjCBlzELMAkG -A1UEBhMCVVMxCzAJBgNVBAgTAlVUMRcwFQYDVQQHEw5TYWx0IExha2UgQ2l0eTEe -MBwGA1UEChMVVGhlIFVTRVJUUlVTVCBOZXR3b3JrMSEwHwYDVQQLExhodHRwOi8v -d3d3LnVzZXJ0cnVzdC5jb20xHzAdBgNVBAMTFlVUTi1VU0VSRmlyc3QtSGFyZHdh -cmUwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCx98M4P7Sof885glFn -0G2f0v9Y8+efK+wNiVSZuTiZFvfgIXlIwrthdBKWHTxqctU8EGc6Oe0rE81m65UJ -M6Rsl7HoxuzBdXmcRl6Nq9Bq/bkqVRcQVLMZ8Jr28bFdtqdt++BxF2uiiPsA3/4a -MXcMmgF6sTLjKwEHOG7DpV4jvEWbe1DByTCP2+UretNb+zNAHqDVmBe8i4fDidNd -oI6yqqr2jmmIBsX6iSHzCJ1pLgkzmykNRg+MzEk0sGlRvfkGzWitZky8PqxhvQqI -DsjfPe58BEydCl5rkdbux+0ojatNh4lz0G6k0B4WixThdkQDf2Os5M1JnMWS9Ksy -oUhbAgMBAAGjgbkwgbYwCwYDVR0PBAQDAgHGMA8GA1UdEwEB/wQFMAMBAf8wHQYD -VR0OBBYEFKFyXyYbKJhDlV0HN9WFlp1L0sNFMEQGA1UdHwQ9MDswOaA3oDWGM2h0 -dHA6Ly9jcmwudXNlcnRydXN0LmNvbS9VVE4tVVNFUkZpcnN0LUhhcmR3YXJlLmNy -bDAxBgNVHSUEKjAoBggrBgEFBQcDAQYIKwYBBQUHAwUGCCsGAQUFBwMGBggrBgEF -BQcDBzANBgkqhkiG9w0BAQUFAAOCAQEARxkP3nTGmZev/K0oXnWO6y1n7k57K9cM -//bey1WiCuFMVGWTYGufEpytXoMs61quwOQt9ABjHbjAbPLPSbtNk28Gpgoiskli -CE7/yMgUsogWXecB5BKV5UU0s4tpvc+0hY91UZ59Ojg6FEgSxvunOxqNDYJAB+gE -CJChicsZUN/KHAG8HQQZexB2lzvukJDKxA4fFm517zP4029bHpbj4HR3dHuKom4t -3XbWOTCC8KucUvIqx69JXn7HaOWCgchqJ/kniCrVWFCVH/A7HFe7fRQ5YiuayZSS -KqMiDP+JJn1fIytH1xUdqWqeUQ0qUZ6B+dQ7XnASfxAynB67nfhmqA== +dHRwOi8vd3d3LnVzZXJ0cnVzdC5jb20xGzAZBgNVBAMTElVUTiAtIERBVEFDb3Jw +IFNHQzAeFw05OTA2MjQxODU3MjFaFw0xOTA2MjQxOTA2MzBaMIGTMQswCQYDVQQG +EwJVUzELMAkGA1UECBMCVVQxFzAVBgNVBAcTDlNhbHQgTGFrZSBDaXR5MR4wHAYD +VQQKExVUaGUgVVNFUlRSVVNUIE5ldHdvcmsxITAfBgNVBAsTGGh0dHA6Ly93d3cu +dXNlcnRydXN0LmNvbTEbMBkGA1UEAxMSVVROIC0gREFUQUNvcnAgU0dDMIIBIjAN +BgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEA3+5YEKIrblXEjr8uRgnn4AgPLit6 +E5Qbvfa2gI5lBZMAHryv4g+OGQ0SR+ysraP6LnD43m77VkIVni5c7yPeIbkFdicZ +D0/Ww5y0vpQZY/KmEQrrU0icvvIpOxboGqBMpsn0GFlowHDyUwDAXlCCpVZvNvlK +4ESGoE1O1kduSUrLZ9emxAW5jh70/P/N5zbgnAVssjMiFdC04MwXwLLA9P4yPykq +lXvY8qdOD1R8oQ2AswkDwf9c3V6aPryuvEeKaq5xyh+xKrhfQgUL7EYw0XILyulW +bfXv33i+Ybqypa4ETLyorGkVl73v67SMvzX41MPRKA5cOp9wGDMgd8SirwIDAQAB +o4GrMIGoMAsGA1UdDwQEAwIBxjAPBgNVHRMBAf8EBTADAQH/MB0GA1UdDgQWBBRT +MtGzz3/64PGgXYVOktKeRR20TzA9BgNVHR8ENjA0MDKgMKAuhixodHRwOi8vY3Js +LnVzZXJ0cnVzdC5jb20vVVROLURBVEFDb3JwU0dDLmNybDAqBgNVHSUEIzAhBggr +BgEFBQcDAQYKKwYBBAGCNwoDAwYJYIZIAYb4QgQBMA0GCSqGSIb3DQEBBQUAA4IB +AQAnNZcAiosovcYzMB4p/OL31ZjUQLtgyr+rFywJNn9Q+kHcrpY6CiM+iVnJowft +Gzet/Hy+UUla3joKVAgWRcKZsYfNjGjgaQPpxE6YsjuMFrMOoAyYUJuTqXAJyCyj +j98C5OBxOvG0I3KgqgHf35g+FFCgMSa9KOlaMCZ1+XtgHI3zzVAmbQQnmt/VDUVH +KWss5nbZqSl9Mt3JNjy9rjXxEZ4du5A/EkdOjtd+D2JzHVImOBwYSf0wdJrE5SIv +2MCN7ZF6TACPcn9d2t0bi0Vr591pl6jFVkwPDPafepE39peC4N1xaf92P2BNPM/3 +mfnGV/TJVTl4uix5yaaIK/QI -----END CERTIFICATE----- diff --git a/Lib/test/ssl_cert.pem b/Lib/test/ssl_cert.pem new file mode 100644 index 0000000..47a7d7e --- /dev/null +++ b/Lib/test/ssl_cert.pem @@ -0,0 +1,15 @@ +-----BEGIN CERTIFICATE----- +MIICVDCCAb2gAwIBAgIJANfHOBkZr8JOMA0GCSqGSIb3DQEBBQUAMF8xCzAJBgNV +BAYTAlhZMRcwFQYDVQQHEw5DYXN0bGUgQW50aHJheDEjMCEGA1UEChMaUHl0aG9u +IFNvZnR3YXJlIEZvdW5kYXRpb24xEjAQBgNVBAMTCWxvY2FsaG9zdDAeFw0xMDEw +MDgyMzAxNTZaFw0yMDEwMDUyMzAxNTZaMF8xCzAJBgNVBAYTAlhZMRcwFQYDVQQH +Ew5DYXN0bGUgQW50aHJheDEjMCEGA1UEChMaUHl0aG9uIFNvZnR3YXJlIEZvdW5k +YXRpb24xEjAQBgNVBAMTCWxvY2FsaG9zdDCBnzANBgkqhkiG9w0BAQEFAAOBjQAw +gYkCgYEA21vT5isq7F68amYuuNpSFlKDPrMUCa4YWYqZRt2OZ+/3NKaZ2xAiSwr7 +6MrQF70t5nLbSPpqE5+5VrS58SY+g/sXLiFd6AplH1wJZwh78DofbFYXUggktFMt +pTyiX8jtP66bkcPkDADA089RI1TQR6Ca+n7HFa7c1fabVV6i3zkCAwEAAaMYMBYw +FAYDVR0RBA0wC4IJbG9jYWxob3N0MA0GCSqGSIb3DQEBBQUAA4GBAHPctQBEQ4wd +BJ6+JcpIraopLn8BGhbjNWj40mmRqWB/NAWF6M5ne7KpGAu7tLeG4hb1zLaldK8G +lxy2GPSRF6LFS48dpEj2HbMv2nvv6xxalDMJ9+DicWgAKTQ6bcX2j3GUkCR0g/T1 +CRlNBAAlvhKzO7Clpf9l0YKBEfraJByX +-----END CERTIFICATE----- diff --git a/Lib/test/ssl_key.passwd.pem b/Lib/test/ssl_key.passwd.pem new file mode 100644 index 0000000..2524672 --- /dev/null +++ b/Lib/test/ssl_key.passwd.pem @@ -0,0 +1,18 @@ +-----BEGIN RSA PRIVATE KEY----- +Proc-Type: 4,ENCRYPTED +DEK-Info: DES-EDE3-CBC,1A8D9D2A02EC698A + +kJYbfZ8L0sfe9Oty3gw0aloNnY5E8fegRfQLZlNoxTl6jNt0nIwI8kDJ36CZgR9c +u3FDJm/KqrfUoz8vW+qEnWhSG7QPX2wWGPHd4K94Yz/FgrRzZ0DoK7XxXq9gOtVA +AVGQhnz32p+6WhfGsCr9ArXEwRZrTk/FvzEPaU5fHcoSkrNVAGX8IpSVkSDwEDQr +Gv17+cfk99UV1OCza6yKHoFkTtrC+PZU71LomBabivS2Oc4B9hYuSR2hF01wTHP+ +YlWNagZOOVtNz4oKK9x9eNQpmfQXQvPPTfusexKIbKfZrMvJoxcm1gfcZ0H/wK6P +6wmXSG35qMOOztCZNtperjs1wzEBXznyK8QmLcAJBjkfarABJX9vBEzZV0OUKhy+ +noORFwHTllphbmydLhu6ehLUZMHPhzAS5UN7srtpSN81eerDMy0RMUAwA7/PofX1 +94Me85Q8jP0PC9ETdsJcPqLzAPETEYu0ELewKRcrdyWi+tlLFrpE5KT/s5ecbl9l +7B61U4Kfd1PIXc/siINhU3A3bYK+845YyUArUOnKf1kEox7p1RpD7yFqVT04lRTo +cibNKATBusXSuBrp2G6GNuhWEOSafWCKJQAzgCYIp6ZTV2khhMUGppc/2H3CF6cO +zX0KtlPVZC7hLkB6HT8SxYUwF1zqWY7+/XPPdc37MeEZ87Q3UuZwqORLY+Z0hpgt +L5JXBCoklZhCAaN2GqwFLXtGiRSRFGY7xXIhbDTlE65Wv1WGGgDLMKGE1gOz3yAo +2jjG1+yAHJUdE69XTFHSqSkvaloA1W03LdMXZ9VuQJ/ySXCie6ABAQ== +-----END RSA PRIVATE KEY----- diff --git a/Lib/test/ssl_key.pem b/Lib/test/ssl_key.pem new file mode 100644 index 0000000..3fd3bbd --- /dev/null +++ b/Lib/test/ssl_key.pem @@ -0,0 +1,16 @@ +-----BEGIN PRIVATE KEY----- +MIICdwIBADANBgkqhkiG9w0BAQEFAASCAmEwggJdAgEAAoGBANtb0+YrKuxevGpm +LrjaUhZSgz6zFAmuGFmKmUbdjmfv9zSmmdsQIksK++jK0Be9LeZy20j6ahOfuVa0 +ufEmPoP7Fy4hXegKZR9cCWcIe/A6H2xWF1IIJLRTLaU8ol/I7T+um5HD5AwAwNPP +USNU0Eegmvp+xxWu3NX2m1Veot85AgMBAAECgYA3ZdZ673X0oexFlq7AAmrutkHt +CL7LvwrpOiaBjhyTxTeSNWzvtQBkIU8DOI0bIazA4UreAFffwtvEuPmonDb3F+Iq +SMAu42XcGyVZEl+gHlTPU9XRX7nTOXVt+MlRRRxL6t9GkGfUAXI3XxJDXW3c0vBK +UL9xqD8cORXOfE06rQJBAP8mEX1ERkR64Ptsoe4281vjTlNfIbs7NMPkUnrn9N/Y +BLhjNIfQ3HFZG8BTMLfX7kCS9D593DW5tV4Z9BP/c6cCQQDcFzCcVArNh2JSywOQ +ZfTfRbJg/Z5Lt9Fkngv1meeGNPgIMLN8Sg679pAOOWmzdMO3V706rNPzSVMME7E5 +oPIfAkEA8pDddarP5tCvTTgUpmTFbakm0KoTZm2+FzHcnA4jRh+XNTjTOv98Y6Ik +eO5d1ZnKXseWvkZncQgxfdnMqqpj5wJAcNq/RVne1DbYlwWchT2Si65MYmmJ8t+F +0mcsULqjOnEMwf5e+ptq5LzwbyrHZYq5FNk7ocufPv/ZQrcSSC+cFwJBAKvOJByS +x56qyGeZLOQlWS2JS3KJo59XuLFGqcbgN9Om9xFa41Yb4N9NvplFivsvZdw3m1Q/ +SPIXQuT8RMPDVNQ= +-----END PRIVATE KEY----- diff --git a/Lib/test/ssl_servers.py b/Lib/test/ssl_servers.py new file mode 100644 index 0000000..a312e28 --- /dev/null +++ b/Lib/test/ssl_servers.py @@ -0,0 +1,209 @@ +import os +import sys +import ssl +import pprint +import urllib +import urlparse +# Rename HTTPServer to _HTTPServer so as to avoid confusion with HTTPSServer. +from BaseHTTPServer import HTTPServer as _HTTPServer, BaseHTTPRequestHandler +from SimpleHTTPServer import SimpleHTTPRequestHandler + +from test import test_support as support +threading = support.import_module("threading") + +here = os.path.dirname(__file__) + +HOST = support.HOST +CERTFILE = os.path.join(here, 'keycert.pem') + +# This one's based on HTTPServer, which is based on SocketServer + +class HTTPSServer(_HTTPServer): + + def __init__(self, server_address, handler_class, context): + _HTTPServer.__init__(self, server_address, handler_class) + self.context = context + + def __str__(self): + return ('<%s %s:%s>' % + (self.__class__.__name__, + self.server_name, + self.server_port)) + + def get_request(self): + # override this to wrap socket with SSL + try: + sock, addr = self.socket.accept() + sslconn = self.context.wrap_socket(sock, server_side=True) + except OSError as e: + # socket errors are silenced by the caller, print them here + if support.verbose: + sys.stderr.write("Got an error:\n%s\n" % e) + raise + return sslconn, addr + +class RootedHTTPRequestHandler(SimpleHTTPRequestHandler): + # need to override translate_path to get a known root, + # instead of using os.curdir, since the test could be + # run from anywhere + + server_version = "TestHTTPS/1.0" + root = here + # Avoid hanging when a request gets interrupted by the client + timeout = 5 + + def translate_path(self, path): + """Translate a /-separated PATH to the local filename syntax. + + Components that mean special things to the local file system + (e.g. drive or directory names) are ignored. (XXX They should + probably be diagnosed.) + + """ + # abandon query parameters + path = urlparse.urlparse(path)[2] + path = os.path.normpath(urllib.unquote(path)) + words = path.split('/') + words = filter(None, words) + path = self.root + for word in words: + drive, word = os.path.splitdrive(word) + head, word = os.path.split(word) + path = os.path.join(path, word) + return path + + def log_message(self, format, *args): + # we override this to suppress logging unless "verbose" + if support.verbose: + sys.stdout.write(" server (%s:%d %s):\n [%s] %s\n" % + (self.server.server_address, + self.server.server_port, + self.request.cipher(), + self.log_date_time_string(), + format%args)) + + +class StatsRequestHandler(BaseHTTPRequestHandler): + """Example HTTP request handler which returns SSL statistics on GET + requests. + """ + + server_version = "StatsHTTPS/1.0" + + def do_GET(self, send_body=True): + """Serve a GET request.""" + sock = self.rfile.raw._sock + context = sock.context + stats = { + 'session_cache': context.session_stats(), + 'cipher': sock.cipher(), + 'compression': sock.compression(), + } + body = pprint.pformat(stats) + body = body.encode('utf-8') + self.send_response(200) + self.send_header("Content-type", "text/plain; charset=utf-8") + self.send_header("Content-Length", str(len(body))) + self.end_headers() + if send_body: + self.wfile.write(body) + + def do_HEAD(self): + """Serve a HEAD request.""" + self.do_GET(send_body=False) + + def log_request(self, format, *args): + if support.verbose: + BaseHTTPRequestHandler.log_request(self, format, *args) + + +class HTTPSServerThread(threading.Thread): + + def __init__(self, context, host=HOST, handler_class=None): + self.flag = None + self.server = HTTPSServer((host, 0), + handler_class or RootedHTTPRequestHandler, + context) + self.port = self.server.server_port + threading.Thread.__init__(self) + self.daemon = True + + def __str__(self): + return "<%s %s>" % (self.__class__.__name__, self.server) + + def start(self, flag=None): + self.flag = flag + threading.Thread.start(self) + + def run(self): + if self.flag: + self.flag.set() + try: + self.server.serve_forever(0.05) + finally: + self.server.server_close() + + def stop(self): + self.server.shutdown() + + +def make_https_server(case, context=None, certfile=CERTFILE, + host=HOST, handler_class=None): + if context is None: + context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + # We assume the certfile contains both private key and certificate + context.load_cert_chain(certfile) + server = HTTPSServerThread(context, host, handler_class) + flag = threading.Event() + server.start(flag) + flag.wait() + def cleanup(): + if support.verbose: + sys.stdout.write('stopping HTTPS server\n') + server.stop() + if support.verbose: + sys.stdout.write('joining HTTPS thread\n') + server.join() + case.addCleanup(cleanup) + return server + + +if __name__ == "__main__": + import argparse + parser = argparse.ArgumentParser( + description='Run a test HTTPS server. ' + 'By default, the current directory is served.') + parser.add_argument('-p', '--port', type=int, default=4433, + help='port to listen on (default: %(default)s)') + parser.add_argument('-q', '--quiet', dest='verbose', default=True, + action='store_false', help='be less verbose') + parser.add_argument('-s', '--stats', dest='use_stats_handler', default=False, + action='store_true', help='always return stats page') + parser.add_argument('--curve-name', dest='curve_name', type=str, + action='store', + help='curve name for EC-based Diffie-Hellman') + parser.add_argument('--ciphers', dest='ciphers', type=str, + help='allowed cipher list') + parser.add_argument('--dh', dest='dh_file', type=str, action='store', + help='PEM file containing DH parameters') + args = parser.parse_args() + + support.verbose = args.verbose + if args.use_stats_handler: + handler_class = StatsRequestHandler + else: + handler_class = RootedHTTPRequestHandler + handler_class.root = os.getcwd() + context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + context.load_cert_chain(CERTFILE) + if args.curve_name: + context.set_ecdh_curve(args.curve_name) + if args.dh_file: + context.load_dh_params(args.dh_file) + if args.ciphers: + context.set_ciphers(args.ciphers) + + server = HTTPSServer(("", args.port), handler_class, context) + if args.verbose: + print("Listening on https://localhost:{0.port}".format(args)) + server.serve_forever(0.1) diff --git a/Lib/test/test_ssl.py b/Lib/test/test_ssl.py index 91b8029..a629e1b 100644 --- a/Lib/test/test_ssl.py +++ b/Lib/test/test_ssl.py @@ -1,35 +1,78 @@ +# -*- encoding: utf-8 -*- # Test the support for SSL and sockets import sys import unittest -from test import test_support +from test import test_support as support import asyncore import socket import select import time +import datetime import gc import os import errno import pprint -import urllib, urlparse +import tempfile +import urllib import traceback import weakref -import functools import platform +import functools +from contextlib import closing + +ssl = support.import_module("ssl") + +PROTOCOLS = sorted(ssl._PROTOCOL_NAMES) +HOST = support.HOST + +def data_file(*name): + return os.path.join(os.path.dirname(__file__), *name) + +# The custom key and certificate files used in test_ssl are generated +# using Lib/test/make_ssl_certs.py. +# Other certificates are simply fetched from the Internet servers they +# are meant to authenticate. + +CERTFILE = data_file("keycert.pem") +BYTES_CERTFILE = CERTFILE.encode(sys.getfilesystemencoding()) +ONLYCERT = data_file("ssl_cert.pem") +ONLYKEY = data_file("ssl_key.pem") +BYTES_ONLYCERT = ONLYCERT.encode(sys.getfilesystemencoding()) +BYTES_ONLYKEY = ONLYKEY.encode(sys.getfilesystemencoding()) +CERTFILE_PROTECTED = data_file("keycert.passwd.pem") +ONLYKEY_PROTECTED = data_file("ssl_key.passwd.pem") +KEY_PASSWORD = "somepass" +CAPATH = data_file("capath") +BYTES_CAPATH = CAPATH.encode(sys.getfilesystemencoding()) +CAFILE_NEURONIO = data_file("capath", "4e1295a3.0") +CAFILE_CACERT = data_file("capath", "5ed36f99.0") + + +# empty CRL +CRLFILE = data_file("revocation.crl") + +# Two keys and certs signed by the same CA (for SNI tests) +SIGNED_CERTFILE = data_file("keycert3.pem") +SIGNED_CERTFILE2 = data_file("keycert4.pem") +SIGNING_CA = data_file("pycacert.pem") + +SVN_PYTHON_ORG_ROOT_CERT = data_file("https_svn_python_org_root.pem") -from BaseHTTPServer import HTTPServer -from SimpleHTTPServer import SimpleHTTPRequestHandler +EMPTYCERT = data_file("nullcert.pem") +BADCERT = data_file("badcert.pem") +WRONGCERT = data_file("XXXnonexisting.pem") +BADKEY = data_file("badkey.pem") +NOKIACERT = data_file("nokia.pem") +NULLBYTECERT = data_file("nullbytecert.pem") -ssl = test_support.import_module("ssl") +DHFILE = data_file("dh512.pem") +BYTES_DHFILE = DHFILE.encode(sys.getfilesystemencoding()) -HOST = test_support.HOST -CERTFILE = None -SVN_PYTHON_ORG_ROOT_CERT = None -NULLBYTECERT = None def handle_error(prefix): exc_format = ' '.join(traceback.format_exception(*sys.exc_info())) - if test_support.verbose: + if support.verbose: sys.stdout.write(prefix + exc_format) @@ -51,48 +94,76 @@ class BasicTests(unittest.TestCase): pass else: raise +def can_clear_options(): + # 0.9.8m or higher + return ssl._OPENSSL_API_VERSION >= (0, 9, 8, 13, 15) + +def no_sslv2_implies_sslv3_hello(): + # 0.9.7h or higher + return ssl.OPENSSL_VERSION_INFO >= (0, 9, 7, 8, 15) + +def have_verify_flags(): + # 0.9.8 or higher + return ssl.OPENSSL_VERSION_INFO >= (0, 9, 8, 0, 15) + +def utc_offset(): #NOTE: ignore issues like #1647654 + # local time = utc time + utc offset + if time.daylight and time.localtime().tm_isdst > 0: + return -time.altzone # seconds + return -time.timezone + +def asn1time(cert_time): + # Some versions of OpenSSL ignore seconds, see #18207 + # 0.9.8.i + if ssl._OPENSSL_API_VERSION == (0, 9, 8, 9, 15): + fmt = "%b %d %H:%M:%S %Y GMT" + dt = datetime.datetime.strptime(cert_time, fmt) + dt = dt.replace(second=0) + cert_time = dt.strftime(fmt) + # %d adds leading zero but ASN1_TIME_print() uses leading space + if cert_time[4] == "0": + cert_time = cert_time[:4] + " " + cert_time[5:] + + return cert_time # Issue #9415: Ubuntu hijacks their OpenSSL and forcefully disables SSLv2 def skip_if_broken_ubuntu_ssl(func): if hasattr(ssl, 'PROTOCOL_SSLv2'): - # We need to access the lower-level wrapper in order to create an - # implicit SSL context without trying to connect or listen. - try: - import _ssl - except ImportError: - # The returned function won't get executed, just ignore the error - pass @functools.wraps(func) def f(*args, **kwargs): try: - s = socket.socket(socket.AF_INET) - _ssl.sslwrap(s._sock, 0, None, None, - ssl.CERT_NONE, ssl.PROTOCOL_SSLv2, None, None) - except ssl.SSLError as e: + ssl.SSLContext(ssl.PROTOCOL_SSLv2) + except ssl.SSLError: if (ssl.OPENSSL_VERSION_INFO == (0, 9, 8, 15, 15) and - platform.linux_distribution() == ('debian', 'squeeze/sid', '') - and 'Invalid SSL protocol variant specified' in str(e)): + platform.linux_distribution() == ('debian', 'squeeze/sid', '')): raise unittest.SkipTest("Patched Ubuntu OpenSSL breaks behaviour") return func(*args, **kwargs) return f else: return func +needs_sni = unittest.skipUnless(ssl.HAS_SNI, "SNI support needed for this test") + class BasicSocketTests(unittest.TestCase): def test_constants(self): - #ssl.PROTOCOL_SSLv2 - ssl.PROTOCOL_SSLv23 - ssl.PROTOCOL_SSLv3 - ssl.PROTOCOL_TLSv1 ssl.CERT_NONE ssl.CERT_OPTIONAL ssl.CERT_REQUIRED + ssl.OP_CIPHER_SERVER_PREFERENCE + ssl.OP_SINGLE_DH_USE + if ssl.HAS_ECDH: + ssl.OP_SINGLE_ECDH_USE + if ssl.OPENSSL_VERSION_INFO >= (1, 0): + ssl.OP_NO_COMPRESSION + self.assertIn(ssl.HAS_SNI, {True, False}) + self.assertIn(ssl.HAS_ECDH, {True, False}) + def test_random(self): v = ssl.RAND_status() - if test_support.verbose: + if support.verbose: sys.stdout.write("\n RAND_status is %d (%s)\n" % (v, (v and "sufficient randomness") or "insufficient randomness")) @@ -104,9 +175,19 @@ class BasicSocketTests(unittest.TestCase): # note that this uses an 'unofficial' function in _ssl.c, # provided solely for this test, to exercise the certificate # parsing code - p = ssl._ssl._test_decode_cert(CERTFILE, False) - if test_support.verbose: + p = ssl._ssl._test_decode_cert(CERTFILE) + if support.verbose: sys.stdout.write("\n" + pprint.pformat(p) + "\n") + self.assertEqual(p['issuer'], + ((('countryName', 'XY'),), + (('localityName', 'Castle Anthrax'),), + (('organizationName', 'Python Software Foundation'),), + (('commonName', 'localhost'),)) + ) + # Note the next three asserts will fail if the keys are regenerated + self.assertEqual(p['notAfter'], asn1time('Oct 5 23:01:56 2020 GMT')) + self.assertEqual(p['notBefore'], asn1time('Oct 8 23:01:56 2010 GMT')) + self.assertEqual(p['serialNumber'], 'D7C7381919AFC24E') self.assertEqual(p['subject'], ((('countryName', 'XY'),), (('localityName', 'Castle Anthrax'),), @@ -117,16 +198,22 @@ class BasicSocketTests(unittest.TestCase): # Issue #13034: the subjectAltName in some certificates # (notably projects.developer.nokia.com:443) wasn't parsed p = ssl._ssl._test_decode_cert(NOKIACERT) - if test_support.verbose: + if support.verbose: sys.stdout.write("\n" + pprint.pformat(p) + "\n") self.assertEqual(p['subjectAltName'], (('DNS', 'projects.developer.nokia.com'), ('DNS', 'projects.forum.nokia.com')) ) + # extra OCSP and AIA fields + self.assertEqual(p['OCSP'], ('http://ocsp.verisign.com',)) + self.assertEqual(p['caIssuers'], + ('http://SVRIntl-G3-aia.verisign.com/SVRIntlG3.cer',)) + self.assertEqual(p['crlDistributionPoints'], + ('http://SVRIntl-G3-crl.verisign.com/SVRIntlG3.crl',)) def test_parse_cert_CVE_2013_4238(self): p = ssl._ssl._test_decode_cert(NULLBYTECERT) - if test_support.verbose: + if support.verbose: sys.stdout.write("\n" + pprint.pformat(p) + "\n") subject = ((('countryName', 'US'),), (('stateOrProvinceName', 'Oregon'),), @@ -137,7 +224,7 @@ class BasicSocketTests(unittest.TestCase): (('emailAddress', 'python-dev@python.org'),)) self.assertEqual(p['subject'], subject) self.assertEqual(p['issuer'], subject) - if ssl.OPENSSL_VERSION_INFO >= (0, 9, 8): + if ssl._OPENSSL_API_VERSION >= (0, 9, 8): san = (('DNS', 'altnull.python.org\x00example.com'), ('email', 'null@python.org\x00user@example.org'), ('URI', 'http://null.python.org\x00http://example.org'), @@ -196,24 +283,7 @@ class BasicSocketTests(unittest.TestCase): self.assertTrue(s.startswith("OpenSSL {:d}.{:d}.{:d}".format(major, minor, fix)), (s, t)) - @test_support.requires_resource('network') - def test_ciphers(self): - remote = ("svn.python.org", 443) - with test_support.transient_internet(remote[0]): - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_NONE, ciphers="ALL") - s.connect(remote) - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_NONE, ciphers="DEFAULT") - s.connect(remote) - # Error checking occurs when connecting, because the SSL context - # isn't created before. - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_NONE, ciphers="^$:,;?*'dorothyx") - with self.assertRaisesRegexp(ssl.SSLError, "No cipher can be selected"): - s.connect(remote) - - @test_support.cpython_only + @support.cpython_only def test_refcycle(self): # Issue #7943: an SSL object doesn't create reference cycles with # itself. @@ -224,17 +294,319 @@ class BasicSocketTests(unittest.TestCase): self.assertEqual(wr(), None) def test_wrapped_unconnected(self): - # The _delegate_methods in socket.py are correctly delegated to by an - # unconnected SSLSocket, so they will raise a socket.error rather than - # something unexpected like TypeError. + # Methods on an unconnected SSLSocket propagate the original + # socket.error raise by the underlying socket object. s = socket.socket(socket.AF_INET) - ss = ssl.wrap_socket(s) - self.assertRaises(socket.error, ss.recv, 1) - self.assertRaises(socket.error, ss.recv_into, bytearray(b'x')) - self.assertRaises(socket.error, ss.recvfrom, 1) - self.assertRaises(socket.error, ss.recvfrom_into, bytearray(b'x'), 1) - self.assertRaises(socket.error, ss.send, b'x') - self.assertRaises(socket.error, ss.sendto, b'x', ('0.0.0.0', 0)) + with closing(ssl.wrap_socket(s)) as ss: + self.assertRaises(socket.error, ss.recv, 1) + self.assertRaises(socket.error, ss.recv_into, bytearray(b'x')) + self.assertRaises(socket.error, ss.recvfrom, 1) + self.assertRaises(socket.error, ss.recvfrom_into, bytearray(b'x'), 1) + self.assertRaises(socket.error, ss.send, b'x') + self.assertRaises(socket.error, ss.sendto, b'x', ('0.0.0.0', 0)) + + def test_timeout(self): + # Issue #8524: when creating an SSL socket, the timeout of the + # original socket should be retained. + for timeout in (None, 0.0, 5.0): + s = socket.socket(socket.AF_INET) + s.settimeout(timeout) + with closing(ssl.wrap_socket(s)) as ss: + self.assertEqual(timeout, ss.gettimeout()) + + def test_errors(self): + sock = socket.socket() + self.assertRaisesRegexp(ValueError, + "certfile must be specified", + ssl.wrap_socket, sock, keyfile=CERTFILE) + self.assertRaisesRegexp(ValueError, + "certfile must be specified for server-side operations", + ssl.wrap_socket, sock, server_side=True) + self.assertRaisesRegexp(ValueError, + "certfile must be specified for server-side operations", + ssl.wrap_socket, sock, server_side=True, certfile="") + with closing(ssl.wrap_socket(sock, server_side=True, certfile=CERTFILE)) as s: + self.assertRaisesRegexp(ValueError, "can't connect in server-side mode", + s.connect, (HOST, 8080)) + with self.assertRaises(IOError) as cm: + with closing(socket.socket()) as sock: + ssl.wrap_socket(sock, certfile=WRONGCERT) + self.assertEqual(cm.exception.errno, errno.ENOENT) + with self.assertRaises(IOError) as cm: + with closing(socket.socket()) as sock: + ssl.wrap_socket(sock, certfile=CERTFILE, keyfile=WRONGCERT) + self.assertEqual(cm.exception.errno, errno.ENOENT) + with self.assertRaises(IOError) as cm: + with closing(socket.socket()) as sock: + ssl.wrap_socket(sock, certfile=WRONGCERT, keyfile=WRONGCERT) + self.assertEqual(cm.exception.errno, errno.ENOENT) + + def test_match_hostname(self): + def ok(cert, hostname): + ssl.match_hostname(cert, hostname) + def fail(cert, hostname): + self.assertRaises(ssl.CertificateError, + ssl.match_hostname, cert, hostname) + + cert = {'subject': ((('commonName', 'example.com'),),)} + ok(cert, 'example.com') + ok(cert, 'ExAmple.cOm') + fail(cert, 'www.example.com') + fail(cert, '.example.com') + fail(cert, 'example.org') + fail(cert, 'exampleXcom') + + cert = {'subject': ((('commonName', '*.a.com'),),)} + ok(cert, 'foo.a.com') + fail(cert, 'bar.foo.a.com') + fail(cert, 'a.com') + fail(cert, 'Xa.com') + fail(cert, '.a.com') + + # only match one left-most wildcard + cert = {'subject': ((('commonName', 'f*.com'),),)} + ok(cert, 'foo.com') + ok(cert, 'f.com') + fail(cert, 'bar.com') + fail(cert, 'foo.a.com') + fail(cert, 'bar.foo.com') + + # NULL bytes are bad, CVE-2013-4073 + cert = {'subject': ((('commonName', + 'null.python.org\x00example.org'),),)} + ok(cert, 'null.python.org\x00example.org') # or raise an error? + fail(cert, 'example.org') + fail(cert, 'null.python.org') + + # error cases with wildcards + cert = {'subject': ((('commonName', '*.*.a.com'),),)} + fail(cert, 'bar.foo.a.com') + fail(cert, 'a.com') + fail(cert, 'Xa.com') + fail(cert, '.a.com') + + cert = {'subject': ((('commonName', 'a.*.com'),),)} + fail(cert, 'a.foo.com') + fail(cert, 'a..com') + fail(cert, 'a.com') + + # wildcard doesn't match IDNA prefix 'xn--' + idna = u'püthon.python.org'.encode("idna").decode("ascii") + cert = {'subject': ((('commonName', idna),),)} + ok(cert, idna) + cert = {'subject': ((('commonName', 'x*.python.org'),),)} + fail(cert, idna) + cert = {'subject': ((('commonName', 'xn--p*.python.org'),),)} + fail(cert, idna) + + # wildcard in first fragment and IDNA A-labels in sequent fragments + # are supported. + idna = u'www*.pythön.org'.encode("idna").decode("ascii") + cert = {'subject': ((('commonName', idna),),)} + ok(cert, u'www.pythön.org'.encode("idna").decode("ascii")) + ok(cert, u'www1.pythön.org'.encode("idna").decode("ascii")) + fail(cert, u'ftp.pythön.org'.encode("idna").decode("ascii")) + fail(cert, u'pythön.org'.encode("idna").decode("ascii")) + + # Slightly fake real-world example + cert = {'notAfter': 'Jun 26 21:41:46 2011 GMT', + 'subject': ((('commonName', 'linuxfrz.org'),),), + 'subjectAltName': (('DNS', 'linuxfr.org'), + ('DNS', 'linuxfr.com'), + ('othername', ''))} + ok(cert, 'linuxfr.org') + ok(cert, 'linuxfr.com') + # Not a "DNS" entry + fail(cert, '') + # When there is a subjectAltName, commonName isn't used + fail(cert, 'linuxfrz.org') + + # A pristine real-world example + cert = {'notAfter': 'Dec 18 23:59:59 2011 GMT', + 'subject': ((('countryName', 'US'),), + (('stateOrProvinceName', 'California'),), + (('localityName', 'Mountain View'),), + (('organizationName', 'Google Inc'),), + (('commonName', 'mail.google.com'),))} + ok(cert, 'mail.google.com') + fail(cert, 'gmail.com') + # Only commonName is considered + fail(cert, 'California') + + # Neither commonName nor subjectAltName + cert = {'notAfter': 'Dec 18 23:59:59 2011 GMT', + 'subject': ((('countryName', 'US'),), + (('stateOrProvinceName', 'California'),), + (('localityName', 'Mountain View'),), + (('organizationName', 'Google Inc'),))} + fail(cert, 'mail.google.com') + + # No DNS entry in subjectAltName but a commonName + cert = {'notAfter': 'Dec 18 23:59:59 2099 GMT', + 'subject': ((('countryName', 'US'),), + (('stateOrProvinceName', 'California'),), + (('localityName', 'Mountain View'),), + (('commonName', 'mail.google.com'),)), + 'subjectAltName': (('othername', 'blabla'), )} + ok(cert, 'mail.google.com') + + # No DNS entry subjectAltName and no commonName + cert = {'notAfter': 'Dec 18 23:59:59 2099 GMT', + 'subject': ((('countryName', 'US'),), + (('stateOrProvinceName', 'California'),), + (('localityName', 'Mountain View'),), + (('organizationName', 'Google Inc'),)), + 'subjectAltName': (('othername', 'blabla'),)} + fail(cert, 'google.com') + + # Empty cert / no cert + self.assertRaises(ValueError, ssl.match_hostname, None, 'example.com') + self.assertRaises(ValueError, ssl.match_hostname, {}, 'example.com') + + # Issue #17980: avoid denials of service by refusing more than one + # wildcard per fragment. + cert = {'subject': ((('commonName', 'a*b.com'),),)} + ok(cert, 'axxb.com') + cert = {'subject': ((('commonName', 'a*b.co*'),),)} + fail(cert, 'axxb.com') + cert = {'subject': ((('commonName', 'a*b*.com'),),)} + with self.assertRaises(ssl.CertificateError) as cm: + ssl.match_hostname(cert, 'axxbxxc.com') + self.assertIn("too many wildcards", str(cm.exception)) + + def test_server_side(self): + # server_hostname doesn't work for server sockets + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + with closing(socket.socket()) as sock: + self.assertRaises(ValueError, ctx.wrap_socket, sock, True, + server_hostname="some.hostname") + + def test_unknown_channel_binding(self): + # should raise ValueError for unknown type + s = socket.socket(socket.AF_INET) + with closing(ssl.wrap_socket(s)) as ss: + with self.assertRaises(ValueError): + ss.get_channel_binding("unknown-type") + + @unittest.skipUnless("tls-unique" in ssl.CHANNEL_BINDING_TYPES, + "'tls-unique' channel binding not available") + def test_tls_unique_channel_binding(self): + # unconnected should return None for known type + s = socket.socket(socket.AF_INET) + with closing(ssl.wrap_socket(s)) as ss: + self.assertIsNone(ss.get_channel_binding("tls-unique")) + # the same for server-side + s = socket.socket(socket.AF_INET) + with closing(ssl.wrap_socket(s, server_side=True, certfile=CERTFILE)) as ss: + self.assertIsNone(ss.get_channel_binding("tls-unique")) + + def test_get_default_verify_paths(self): + paths = ssl.get_default_verify_paths() + self.assertEqual(len(paths), 6) + self.assertIsInstance(paths, ssl.DefaultVerifyPaths) + + with support.EnvironmentVarGuard() as env: + env["SSL_CERT_DIR"] = CAPATH + env["SSL_CERT_FILE"] = CERTFILE + paths = ssl.get_default_verify_paths() + self.assertEqual(paths.cafile, CERTFILE) + self.assertEqual(paths.capath, CAPATH) + + @unittest.skipUnless(sys.platform == "win32", "Windows specific") + def test_enum_certificates(self): + self.assertTrue(ssl.enum_certificates("CA")) + self.assertTrue(ssl.enum_certificates("ROOT")) + + self.assertRaises(TypeError, ssl.enum_certificates) + self.assertRaises(WindowsError, ssl.enum_certificates, "") + + trust_oids = set() + for storename in ("CA", "ROOT"): + store = ssl.enum_certificates(storename) + self.assertIsInstance(store, list) + for element in store: + self.assertIsInstance(element, tuple) + self.assertEqual(len(element), 3) + cert, enc, trust = element + self.assertIsInstance(cert, bytes) + self.assertIn(enc, {"x509_asn", "pkcs_7_asn"}) + self.assertIsInstance(trust, (set, bool)) + if isinstance(trust, set): + trust_oids.update(trust) + + serverAuth = "1.3.6.1.5.5.7.3.1" + self.assertIn(serverAuth, trust_oids) + + @unittest.skipUnless(sys.platform == "win32", "Windows specific") + def test_enum_crls(self): + self.assertTrue(ssl.enum_crls("CA")) + self.assertRaises(TypeError, ssl.enum_crls) + self.assertRaises(WindowsError, ssl.enum_crls, "") + + crls = ssl.enum_crls("CA") + self.assertIsInstance(crls, list) + for element in crls: + self.assertIsInstance(element, tuple) + self.assertEqual(len(element), 2) + self.assertIsInstance(element[0], bytes) + self.assertIn(element[1], {"x509_asn", "pkcs_7_asn"}) + + + def test_asn1object(self): + expected = (129, 'serverAuth', 'TLS Web Server Authentication', + '1.3.6.1.5.5.7.3.1') + + val = ssl._ASN1Object('1.3.6.1.5.5.7.3.1') + self.assertEqual(val, expected) + self.assertEqual(val.nid, 129) + self.assertEqual(val.shortname, 'serverAuth') + self.assertEqual(val.longname, 'TLS Web Server Authentication') + self.assertEqual(val.oid, '1.3.6.1.5.5.7.3.1') + self.assertIsInstance(val, ssl._ASN1Object) + self.assertRaises(ValueError, ssl._ASN1Object, 'serverAuth') + + val = ssl._ASN1Object.fromnid(129) + self.assertEqual(val, expected) + self.assertIsInstance(val, ssl._ASN1Object) + self.assertRaises(ValueError, ssl._ASN1Object.fromnid, -1) + with self.assertRaisesRegexp(ValueError, "unknown NID 100000"): + ssl._ASN1Object.fromnid(100000) + for i in range(1000): + try: + obj = ssl._ASN1Object.fromnid(i) + except ValueError: + pass + else: + self.assertIsInstance(obj.nid, int) + self.assertIsInstance(obj.shortname, str) + self.assertIsInstance(obj.longname, str) + self.assertIsInstance(obj.oid, (str, type(None))) + + val = ssl._ASN1Object.fromname('TLS Web Server Authentication') + self.assertEqual(val, expected) + self.assertIsInstance(val, ssl._ASN1Object) + self.assertEqual(ssl._ASN1Object.fromname('serverAuth'), expected) + self.assertEqual(ssl._ASN1Object.fromname('1.3.6.1.5.5.7.3.1'), + expected) + with self.assertRaisesRegexp(ValueError, "unknown object 'serverauth'"): + ssl._ASN1Object.fromname('serverauth') + + def test_purpose_enum(self): + val = ssl._ASN1Object('1.3.6.1.5.5.7.3.1') + self.assertIsInstance(ssl.Purpose.SERVER_AUTH, ssl._ASN1Object) + self.assertEqual(ssl.Purpose.SERVER_AUTH, val) + self.assertEqual(ssl.Purpose.SERVER_AUTH.nid, 129) + self.assertEqual(ssl.Purpose.SERVER_AUTH.shortname, 'serverAuth') + self.assertEqual(ssl.Purpose.SERVER_AUTH.oid, + '1.3.6.1.5.5.7.3.1') + + val = ssl._ASN1Object('1.3.6.1.5.5.7.3.2') + self.assertIsInstance(ssl.Purpose.CLIENT_AUTH, ssl._ASN1Object) + self.assertEqual(ssl.Purpose.CLIENT_AUTH, val) + self.assertEqual(ssl.Purpose.CLIENT_AUTH.nid, 130) + self.assertEqual(ssl.Purpose.CLIENT_AUTH.shortname, 'clientAuth') + self.assertEqual(ssl.Purpose.CLIENT_AUTH.oid, + '1.3.6.1.5.5.7.3.2') def test_unsupported_dtls(self): s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) @@ -242,42 +614,606 @@ class BasicSocketTests(unittest.TestCase): with self.assertRaises(NotImplementedError) as cx: ssl.wrap_socket(s, cert_reqs=ssl.CERT_NONE) self.assertEqual(str(cx.exception), "only stream sockets are supported") + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + with self.assertRaises(NotImplementedError) as cx: + ctx.wrap_socket(s) + self.assertEqual(str(cx.exception), "only stream sockets are supported") + + def cert_time_ok(self, timestring, timestamp): + self.assertEqual(ssl.cert_time_to_seconds(timestring), timestamp) + + def cert_time_fail(self, timestring): + with self.assertRaises(ValueError): + ssl.cert_time_to_seconds(timestring) + + @unittest.skipUnless(utc_offset(), + 'local time needs to be different from UTC') + def test_cert_time_to_seconds_timezone(self): + # Issue #19940: ssl.cert_time_to_seconds() returns wrong + # results if local timezone is not UTC + self.cert_time_ok("May 9 00:00:00 2007 GMT", 1178668800.0) + self.cert_time_ok("Jan 5 09:34:43 2018 GMT", 1515144883.0) + + def test_cert_time_to_seconds(self): + timestring = "Jan 5 09:34:43 2018 GMT" + ts = 1515144883.0 + self.cert_time_ok(timestring, ts) + # accept keyword parameter, assert its name + self.assertEqual(ssl.cert_time_to_seconds(cert_time=timestring), ts) + # accept both %e and %d (space or zero generated by strftime) + self.cert_time_ok("Jan 05 09:34:43 2018 GMT", ts) + # case-insensitive + self.cert_time_ok("JaN 5 09:34:43 2018 GmT", ts) + self.cert_time_fail("Jan 5 09:34 2018 GMT") # no seconds + self.cert_time_fail("Jan 5 09:34:43 2018") # no GMT + self.cert_time_fail("Jan 5 09:34:43 2018 UTC") # not GMT timezone + self.cert_time_fail("Jan 35 09:34:43 2018 GMT") # invalid day + self.cert_time_fail("Jon 5 09:34:43 2018 GMT") # invalid month + self.cert_time_fail("Jan 5 24:00:00 2018 GMT") # invalid hour + self.cert_time_fail("Jan 5 09:60:43 2018 GMT") # invalid minute + + newyear_ts = 1230768000.0 + # leap seconds + self.cert_time_ok("Dec 31 23:59:60 2008 GMT", newyear_ts) + # same timestamp + self.cert_time_ok("Jan 1 00:00:00 2009 GMT", newyear_ts) + + self.cert_time_ok("Jan 5 09:34:59 2018 GMT", 1515144899) + # allow 60th second (even if it is not a leap second) + self.cert_time_ok("Jan 5 09:34:60 2018 GMT", 1515144900) + # allow 2nd leap second for compatibility with time.strptime() + self.cert_time_ok("Jan 5 09:34:61 2018 GMT", 1515144901) + self.cert_time_fail("Jan 5 09:34:62 2018 GMT") # invalid seconds + + # no special treatement for the special value: + # 99991231235959Z (rfc 5280) + self.cert_time_ok("Dec 31 23:59:59 9999 GMT", 253402300799.0) + + @support.run_with_locale('LC_ALL', '') + def test_cert_time_to_seconds_locale(self): + # `cert_time_to_seconds()` should be locale independent + + def local_february_name(): + return time.strftime('%b', (1, 2, 3, 4, 5, 6, 0, 0, 0)) + + if local_february_name().lower() == 'feb': + self.skipTest("locale-specific month name needs to be " + "different from C locale") + + # locale-independent + self.cert_time_ok("Feb 9 00:00:00 2007 GMT", 1170979200.0) + self.cert_time_fail(local_february_name() + " 9 00:00:00 2007 GMT") + + +class ContextTests(unittest.TestCase): + + @skip_if_broken_ubuntu_ssl + def test_constructor(self): + for protocol in PROTOCOLS: + ssl.SSLContext(protocol) + self.assertRaises(TypeError, ssl.SSLContext) + self.assertRaises(ValueError, ssl.SSLContext, -1) + self.assertRaises(ValueError, ssl.SSLContext, 42) + + @skip_if_broken_ubuntu_ssl + def test_protocol(self): + for proto in PROTOCOLS: + ctx = ssl.SSLContext(proto) + self.assertEqual(ctx.protocol, proto) + + def test_ciphers(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + ctx.set_ciphers("ALL") + ctx.set_ciphers("DEFAULT") + with self.assertRaisesRegexp(ssl.SSLError, "No cipher can be selected"): + ctx.set_ciphers("^$:,;?*'dorothyx") + + @skip_if_broken_ubuntu_ssl + def test_options(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + # OP_ALL | OP_NO_SSLv2 is the default value + self.assertEqual(ssl.OP_ALL | ssl.OP_NO_SSLv2, + ctx.options) + ctx.options |= ssl.OP_NO_SSLv3 + self.assertEqual(ssl.OP_ALL | ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3, + ctx.options) + if can_clear_options(): + ctx.options = (ctx.options & ~ssl.OP_NO_SSLv2) | ssl.OP_NO_TLSv1 + self.assertEqual(ssl.OP_ALL | ssl.OP_NO_TLSv1 | ssl.OP_NO_SSLv3, + ctx.options) + ctx.options = 0 + self.assertEqual(0, ctx.options) + else: + with self.assertRaises(ValueError): + ctx.options = 0 + + def test_verify_mode(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + # Default value + self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) + ctx.verify_mode = ssl.CERT_OPTIONAL + self.assertEqual(ctx.verify_mode, ssl.CERT_OPTIONAL) + ctx.verify_mode = ssl.CERT_REQUIRED + self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED) + ctx.verify_mode = ssl.CERT_NONE + self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) + with self.assertRaises(TypeError): + ctx.verify_mode = None + with self.assertRaises(ValueError): + ctx.verify_mode = 42 + + @unittest.skipUnless(have_verify_flags(), + "verify_flags need OpenSSL > 0.9.8") + def test_verify_flags(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + # default value by OpenSSL + self.assertEqual(ctx.verify_flags, ssl.VERIFY_DEFAULT) + ctx.verify_flags = ssl.VERIFY_CRL_CHECK_LEAF + self.assertEqual(ctx.verify_flags, ssl.VERIFY_CRL_CHECK_LEAF) + ctx.verify_flags = ssl.VERIFY_CRL_CHECK_CHAIN + self.assertEqual(ctx.verify_flags, ssl.VERIFY_CRL_CHECK_CHAIN) + ctx.verify_flags = ssl.VERIFY_DEFAULT + self.assertEqual(ctx.verify_flags, ssl.VERIFY_DEFAULT) + # supports any value + ctx.verify_flags = ssl.VERIFY_CRL_CHECK_LEAF | ssl.VERIFY_X509_STRICT + self.assertEqual(ctx.verify_flags, + ssl.VERIFY_CRL_CHECK_LEAF | ssl.VERIFY_X509_STRICT) + with self.assertRaises(TypeError): + ctx.verify_flags = None + + def test_load_cert_chain(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + # Combined key and cert in a single file + ctx.load_cert_chain(CERTFILE) + ctx.load_cert_chain(CERTFILE, keyfile=CERTFILE) + self.assertRaises(TypeError, ctx.load_cert_chain, keyfile=CERTFILE) + with self.assertRaises(IOError) as cm: + ctx.load_cert_chain(WRONGCERT) + self.assertEqual(cm.exception.errno, errno.ENOENT) + with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"): + ctx.load_cert_chain(BADCERT) + with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"): + ctx.load_cert_chain(EMPTYCERT) + # Separate key and cert + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + ctx.load_cert_chain(ONLYCERT, ONLYKEY) + ctx.load_cert_chain(certfile=ONLYCERT, keyfile=ONLYKEY) + ctx.load_cert_chain(certfile=BYTES_ONLYCERT, keyfile=BYTES_ONLYKEY) + with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"): + ctx.load_cert_chain(ONLYCERT) + with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"): + ctx.load_cert_chain(ONLYKEY) + with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"): + ctx.load_cert_chain(certfile=ONLYKEY, keyfile=ONLYCERT) + # Mismatching key and cert + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + with self.assertRaisesRegexp(ssl.SSLError, "key values mismatch"): + ctx.load_cert_chain(SVN_PYTHON_ORG_ROOT_CERT, ONLYKEY) + # Password protected key and cert + ctx.load_cert_chain(CERTFILE_PROTECTED, password=KEY_PASSWORD) + ctx.load_cert_chain(CERTFILE_PROTECTED, password=KEY_PASSWORD.encode()) + ctx.load_cert_chain(CERTFILE_PROTECTED, + password=bytearray(KEY_PASSWORD.encode())) + ctx.load_cert_chain(ONLYCERT, ONLYKEY_PROTECTED, KEY_PASSWORD) + ctx.load_cert_chain(ONLYCERT, ONLYKEY_PROTECTED, KEY_PASSWORD.encode()) + ctx.load_cert_chain(ONLYCERT, ONLYKEY_PROTECTED, + bytearray(KEY_PASSWORD.encode())) + with self.assertRaisesRegexp(TypeError, "should be a string"): + ctx.load_cert_chain(CERTFILE_PROTECTED, password=True) + with self.assertRaises(ssl.SSLError): + ctx.load_cert_chain(CERTFILE_PROTECTED, password="badpass") + with self.assertRaisesRegexp(ValueError, "cannot be longer"): + # openssl has a fixed limit on the password buffer. + # PEM_BUFSIZE is generally set to 1kb. + # Return a string larger than this. + ctx.load_cert_chain(CERTFILE_PROTECTED, password=b'a' * 102400) + # Password callback + def getpass_unicode(): + return KEY_PASSWORD + def getpass_bytes(): + return KEY_PASSWORD.encode() + def getpass_bytearray(): + return bytearray(KEY_PASSWORD.encode()) + def getpass_badpass(): + return "badpass" + def getpass_huge(): + return b'a' * (1024 * 1024) + def getpass_bad_type(): + return 9 + def getpass_exception(): + raise Exception('getpass error') + class GetPassCallable: + def __call__(self): + return KEY_PASSWORD + def getpass(self): + return KEY_PASSWORD + ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_unicode) + ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_bytes) + ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_bytearray) + ctx.load_cert_chain(CERTFILE_PROTECTED, password=GetPassCallable()) + ctx.load_cert_chain(CERTFILE_PROTECTED, + password=GetPassCallable().getpass) + with self.assertRaises(ssl.SSLError): + ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_badpass) + with self.assertRaisesRegexp(ValueError, "cannot be longer"): + ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_huge) + with self.assertRaisesRegexp(TypeError, "must return a string"): + ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_bad_type) + with self.assertRaisesRegexp(Exception, "getpass error"): + ctx.load_cert_chain(CERTFILE_PROTECTED, password=getpass_exception) + # Make sure the password function isn't called if it isn't needed + ctx.load_cert_chain(CERTFILE, password=getpass_exception) + + def test_load_verify_locations(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + ctx.load_verify_locations(CERTFILE) + ctx.load_verify_locations(cafile=CERTFILE, capath=None) + ctx.load_verify_locations(BYTES_CERTFILE) + ctx.load_verify_locations(cafile=BYTES_CERTFILE, capath=None) + self.assertRaises(TypeError, ctx.load_verify_locations) + self.assertRaises(TypeError, ctx.load_verify_locations, None, None, None) + with self.assertRaises(IOError) as cm: + ctx.load_verify_locations(WRONGCERT) + self.assertEqual(cm.exception.errno, errno.ENOENT) + with self.assertRaisesRegexp(ssl.SSLError, "PEM lib"): + ctx.load_verify_locations(BADCERT) + ctx.load_verify_locations(CERTFILE, CAPATH) + ctx.load_verify_locations(CERTFILE, capath=BYTES_CAPATH) + + # Issue #10989: crash if the second argument type is invalid + self.assertRaises(TypeError, ctx.load_verify_locations, None, True) + + def test_load_verify_cadata(self): + # test cadata + with open(CAFILE_CACERT) as f: + cacert_pem = f.read().decode("ascii") + cacert_der = ssl.PEM_cert_to_DER_cert(cacert_pem) + with open(CAFILE_NEURONIO) as f: + neuronio_pem = f.read().decode("ascii") + neuronio_der = ssl.PEM_cert_to_DER_cert(neuronio_pem) + + # test PEM + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + self.assertEqual(ctx.cert_store_stats()["x509_ca"], 0) + ctx.load_verify_locations(cadata=cacert_pem) + self.assertEqual(ctx.cert_store_stats()["x509_ca"], 1) + ctx.load_verify_locations(cadata=neuronio_pem) + self.assertEqual(ctx.cert_store_stats()["x509_ca"], 2) + # cert already in hash table + ctx.load_verify_locations(cadata=neuronio_pem) + self.assertEqual(ctx.cert_store_stats()["x509_ca"], 2) + + # combined + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + combined = "\n".join((cacert_pem, neuronio_pem)) + ctx.load_verify_locations(cadata=combined) + self.assertEqual(ctx.cert_store_stats()["x509_ca"], 2) + + # with junk around the certs + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + combined = ["head", cacert_pem, "other", neuronio_pem, "again", + neuronio_pem, "tail"] + ctx.load_verify_locations(cadata="\n".join(combined)) + self.assertEqual(ctx.cert_store_stats()["x509_ca"], 2) + + # test DER + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + ctx.load_verify_locations(cadata=cacert_der) + ctx.load_verify_locations(cadata=neuronio_der) + self.assertEqual(ctx.cert_store_stats()["x509_ca"], 2) + # cert already in hash table + ctx.load_verify_locations(cadata=cacert_der) + self.assertEqual(ctx.cert_store_stats()["x509_ca"], 2) + + # combined + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + combined = b"".join((cacert_der, neuronio_der)) + ctx.load_verify_locations(cadata=combined) + self.assertEqual(ctx.cert_store_stats()["x509_ca"], 2) + + # error cases + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + self.assertRaises(TypeError, ctx.load_verify_locations, cadata=object) + + with self.assertRaisesRegexp(ssl.SSLError, "no start line"): + ctx.load_verify_locations(cadata=u"broken") + with self.assertRaisesRegexp(ssl.SSLError, "not enough data"): + ctx.load_verify_locations(cadata=b"broken") + + + def test_load_dh_params(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + ctx.load_dh_params(DHFILE) + if os.name != 'nt': + ctx.load_dh_params(BYTES_DHFILE) + self.assertRaises(TypeError, ctx.load_dh_params) + self.assertRaises(TypeError, ctx.load_dh_params, None) + with self.assertRaises(IOError) as cm: + ctx.load_dh_params(WRONGCERT) + self.assertEqual(cm.exception.errno, errno.ENOENT) + with self.assertRaises(ssl.SSLError) as cm: + ctx.load_dh_params(CERTFILE) + + @skip_if_broken_ubuntu_ssl + def test_session_stats(self): + for proto in PROTOCOLS: + ctx = ssl.SSLContext(proto) + self.assertEqual(ctx.session_stats(), { + 'number': 0, + 'connect': 0, + 'connect_good': 0, + 'connect_renegotiate': 0, + 'accept': 0, + 'accept_good': 0, + 'accept_renegotiate': 0, + 'hits': 0, + 'misses': 0, + 'timeouts': 0, + 'cache_full': 0, + }) + + def test_set_default_verify_paths(self): + # There's not much we can do to test that it acts as expected, + # so just check it doesn't crash or raise an exception. + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + ctx.set_default_verify_paths() + + @unittest.skipUnless(ssl.HAS_ECDH, "ECDH disabled on this OpenSSL build") + def test_set_ecdh_curve(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + ctx.set_ecdh_curve("prime256v1") + ctx.set_ecdh_curve(b"prime256v1") + self.assertRaises(TypeError, ctx.set_ecdh_curve) + self.assertRaises(TypeError, ctx.set_ecdh_curve, None) + self.assertRaises(ValueError, ctx.set_ecdh_curve, "foo") + self.assertRaises(ValueError, ctx.set_ecdh_curve, b"foo") + + @needs_sni + def test_sni_callback(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + + # set_servername_callback expects a callable, or None + self.assertRaises(TypeError, ctx.set_servername_callback) + self.assertRaises(TypeError, ctx.set_servername_callback, 4) + self.assertRaises(TypeError, ctx.set_servername_callback, "") + self.assertRaises(TypeError, ctx.set_servername_callback, ctx) + + def dummycallback(sock, servername, ctx): + pass + ctx.set_servername_callback(None) + ctx.set_servername_callback(dummycallback) + + @needs_sni + def test_sni_callback_refcycle(self): + # Reference cycles through the servername callback are detected + # and cleared. + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + def dummycallback(sock, servername, ctx, cycle=ctx): + pass + ctx.set_servername_callback(dummycallback) + wr = weakref.ref(ctx) + del ctx, dummycallback + gc.collect() + self.assertIs(wr(), None) + + def test_cert_store_stats(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + self.assertEqual(ctx.cert_store_stats(), + {'x509_ca': 0, 'crl': 0, 'x509': 0}) + ctx.load_cert_chain(CERTFILE) + self.assertEqual(ctx.cert_store_stats(), + {'x509_ca': 0, 'crl': 0, 'x509': 0}) + ctx.load_verify_locations(CERTFILE) + self.assertEqual(ctx.cert_store_stats(), + {'x509_ca': 0, 'crl': 0, 'x509': 1}) + ctx.load_verify_locations(SVN_PYTHON_ORG_ROOT_CERT) + self.assertEqual(ctx.cert_store_stats(), + {'x509_ca': 1, 'crl': 0, 'x509': 2}) + + def test_get_ca_certs(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + self.assertEqual(ctx.get_ca_certs(), []) + # CERTFILE is not flagged as X509v3 Basic Constraints: CA:TRUE + ctx.load_verify_locations(CERTFILE) + self.assertEqual(ctx.get_ca_certs(), []) + # but SVN_PYTHON_ORG_ROOT_CERT is a CA cert + ctx.load_verify_locations(SVN_PYTHON_ORG_ROOT_CERT) + self.assertEqual(ctx.get_ca_certs(), + [{'issuer': ((('organizationName', 'Root CA'),), + (('organizationalUnitName', 'http://www.cacert.org'),), + (('commonName', 'CA Cert Signing Authority'),), + (('emailAddress', 'support@cacert.org'),)), + 'notAfter': asn1time('Mar 29 12:29:49 2033 GMT'), + 'notBefore': asn1time('Mar 30 12:29:49 2003 GMT'), + 'serialNumber': '00', + 'crlDistributionPoints': ('https://www.cacert.org/revoke.crl',), + 'subject': ((('organizationName', 'Root CA'),), + (('organizationalUnitName', 'http://www.cacert.org'),), + (('commonName', 'CA Cert Signing Authority'),), + (('emailAddress', 'support@cacert.org'),)), + 'version': 3}]) + + with open(SVN_PYTHON_ORG_ROOT_CERT) as f: + pem = f.read() + der = ssl.PEM_cert_to_DER_cert(pem) + self.assertEqual(ctx.get_ca_certs(True), [der]) + + def test_load_default_certs(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + ctx.load_default_certs() + + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + ctx.load_default_certs(ssl.Purpose.SERVER_AUTH) + ctx.load_default_certs() + + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + ctx.load_default_certs(ssl.Purpose.CLIENT_AUTH) + + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + self.assertRaises(TypeError, ctx.load_default_certs, None) + self.assertRaises(TypeError, ctx.load_default_certs, 'SERVER_AUTH') + + def test_create_default_context(self): + ctx = ssl.create_default_context() + self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23) + self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED) + self.assertTrue(ctx.check_hostname) + self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + self.assertEqual( + ctx.options & getattr(ssl, "OP_NO_COMPRESSION", 0), + getattr(ssl, "OP_NO_COMPRESSION", 0), + ) + + with open(SIGNING_CA) as f: + cadata = f.read().decode("ascii") + ctx = ssl.create_default_context(cafile=SIGNING_CA, capath=CAPATH, + cadata=cadata) + self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23) + self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED) + self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + self.assertEqual( + ctx.options & getattr(ssl, "OP_NO_COMPRESSION", 0), + getattr(ssl, "OP_NO_COMPRESSION", 0), + ) + + ctx = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH) + self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23) + self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) + self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + self.assertEqual( + ctx.options & getattr(ssl, "OP_NO_COMPRESSION", 0), + getattr(ssl, "OP_NO_COMPRESSION", 0), + ) + self.assertEqual( + ctx.options & getattr(ssl, "OP_SINGLE_DH_USE", 0), + getattr(ssl, "OP_SINGLE_DH_USE", 0), + ) + self.assertEqual( + ctx.options & getattr(ssl, "OP_SINGLE_ECDH_USE", 0), + getattr(ssl, "OP_SINGLE_ECDH_USE", 0), + ) + + def test__create_stdlib_context(self): + ctx = ssl._create_stdlib_context() + self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23) + self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) + self.assertFalse(ctx.check_hostname) + self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + + ctx = ssl._create_stdlib_context(ssl.PROTOCOL_TLSv1) + self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1) + self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) + self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + + ctx = ssl._create_stdlib_context(ssl.PROTOCOL_TLSv1, + cert_reqs=ssl.CERT_REQUIRED, + check_hostname=True) + self.assertEqual(ctx.protocol, ssl.PROTOCOL_TLSv1) + self.assertEqual(ctx.verify_mode, ssl.CERT_REQUIRED) + self.assertTrue(ctx.check_hostname) + self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + + ctx = ssl._create_stdlib_context(purpose=ssl.Purpose.CLIENT_AUTH) + self.assertEqual(ctx.protocol, ssl.PROTOCOL_SSLv23) + self.assertEqual(ctx.verify_mode, ssl.CERT_NONE) + self.assertEqual(ctx.options & ssl.OP_NO_SSLv2, ssl.OP_NO_SSLv2) + + def test_check_hostname(self): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + self.assertFalse(ctx.check_hostname) + + # Requires CERT_REQUIRED or CERT_OPTIONAL + with self.assertRaises(ValueError): + ctx.check_hostname = True + ctx.verify_mode = ssl.CERT_REQUIRED + self.assertFalse(ctx.check_hostname) + ctx.check_hostname = True + self.assertTrue(ctx.check_hostname) + + ctx.verify_mode = ssl.CERT_OPTIONAL + ctx.check_hostname = True + self.assertTrue(ctx.check_hostname) + + # Cannot set CERT_NONE with check_hostname enabled + with self.assertRaises(ValueError): + ctx.verify_mode = ssl.CERT_NONE + ctx.check_hostname = False + self.assertFalse(ctx.check_hostname) + + +class SSLErrorTests(unittest.TestCase): + + def test_str(self): + # The str() of a SSLError doesn't include the errno + e = ssl.SSLError(1, "foo") + self.assertEqual(str(e), "foo") + self.assertEqual(e.errno, 1) + # Same for a subclass + e = ssl.SSLZeroReturnError(1, "foo") + self.assertEqual(str(e), "foo") + self.assertEqual(e.errno, 1) + + def test_lib_reason(self): + # Test the library and reason attributes + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + with self.assertRaises(ssl.SSLError) as cm: + ctx.load_dh_params(CERTFILE) + self.assertEqual(cm.exception.library, 'PEM') + self.assertEqual(cm.exception.reason, 'NO_START_LINE') + s = str(cm.exception) + self.assertTrue(s.startswith("[PEM: NO_START_LINE] no start line"), s) + + def test_subclass(self): + # Check that the appropriate SSLError subclass is raised + # (this only tests one of them) + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + with closing(socket.socket()) as s: + s.bind(("127.0.0.1", 0)) + s.listen(5) + c = socket.socket() + c.connect(s.getsockname()) + c.setblocking(False) + with closing(ctx.wrap_socket(c, False, do_handshake_on_connect=False)) as c: + with self.assertRaises(ssl.SSLWantReadError) as cm: + c.do_handshake() + s = str(cm.exception) + self.assertTrue(s.startswith("The operation did not complete (read)"), s) + # For compatibility + self.assertEqual(cm.exception.errno, ssl.SSL_ERROR_WANT_READ) class NetworkedTests(unittest.TestCase): def test_connect(self): - with test_support.transient_internet("svn.python.org"): + with support.transient_internet("svn.python.org"): s = ssl.wrap_socket(socket.socket(socket.AF_INET), cert_reqs=ssl.CERT_NONE) - s.connect(("svn.python.org", 443)) - c = s.getpeercert() - if c: - self.fail("Peer cert %s shouldn't be here!") - s.close() - - # this should fail because we have no verification certs - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_REQUIRED) try: s.connect(("svn.python.org", 443)) - except ssl.SSLError: - pass + self.assertEqual({}, s.getpeercert()) finally: s.close() + # this should fail because we have no verification certs + s = ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_REQUIRED) + self.assertRaisesRegexp(ssl.SSLError, "certificate verify failed", + s.connect, ("svn.python.org", 443)) + s.close() + # this should succeed because we specify the root cert s = ssl.wrap_socket(socket.socket(socket.AF_INET), cert_reqs=ssl.CERT_REQUIRED, ca_certs=SVN_PYTHON_ORG_ROOT_CERT) try: s.connect(("svn.python.org", 443)) + self.assertTrue(s.getpeercert()) finally: s.close() def test_connect_ex(self): # Issue #11326: check connect_ex() implementation - with test_support.transient_internet("svn.python.org"): + with support.transient_internet("svn.python.org"): s = ssl.wrap_socket(socket.socket(socket.AF_INET), cert_reqs=ssl.CERT_REQUIRED, ca_certs=SVN_PYTHON_ORG_ROOT_CERT) @@ -290,7 +1226,7 @@ class NetworkedTests(unittest.TestCase): def test_non_blocking_connect_ex(self): # Issue #11326: non-blocking connect_ex() should allow handshake # to proceed after the socket gets ready. - with test_support.transient_internet("svn.python.org"): + with support.transient_internet("svn.python.org"): s = ssl.wrap_socket(socket.socket(socket.AF_INET), cert_reqs=ssl.CERT_REQUIRED, ca_certs=SVN_PYTHON_ORG_ROOT_CERT, @@ -307,13 +1243,10 @@ class NetworkedTests(unittest.TestCase): try: s.do_handshake() break - except ssl.SSLError as err: - if err.args[0] == ssl.SSL_ERROR_WANT_READ: - select.select([s], [], [], 5.0) - elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: - select.select([], [s], [], 5.0) - else: - raise + except ssl.SSLWantReadError: + select.select([s], [], [], 5.0) + except ssl.SSLWantWriteError: + select.select([], [s], [], 5.0) # SSL established self.assertTrue(s.getpeercert()) finally: @@ -322,7 +1255,7 @@ class NetworkedTests(unittest.TestCase): def test_timeout_connect_ex(self): # Issue #12065: on a timeout, connect_ex() should return the original # errno (mimicking the behaviour of non-SSL sockets). - with test_support.transient_internet("svn.python.org"): + with support.transient_internet("svn.python.org"): s = ssl.wrap_socket(socket.socket(socket.AF_INET), cert_reqs=ssl.CERT_REQUIRED, ca_certs=SVN_PYTHON_ORG_ROOT_CERT, @@ -337,22 +1270,109 @@ class NetworkedTests(unittest.TestCase): s.close() def test_connect_ex_error(self): - with test_support.transient_internet("svn.python.org"): + with support.transient_internet("svn.python.org"): s = ssl.wrap_socket(socket.socket(socket.AF_INET), cert_reqs=ssl.CERT_REQUIRED, ca_certs=SVN_PYTHON_ORG_ROOT_CERT) try: - self.assertEqual(errno.ECONNREFUSED, - s.connect_ex(("svn.python.org", 444))) + rc = s.connect_ex(("svn.python.org", 444)) + # Issue #19919: Windows machines or VMs hosted on Windows + # machines sometimes return EWOULDBLOCK. + self.assertIn(rc, (errno.ECONNREFUSED, errno.EWOULDBLOCK)) + finally: + s.close() + + def test_connect_with_context(self): + with support.transient_internet("svn.python.org"): + # Same as test_connect, but with a separately created context + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + s = ctx.wrap_socket(socket.socket(socket.AF_INET)) + s.connect(("svn.python.org", 443)) + try: + self.assertEqual({}, s.getpeercert()) + finally: + s.close() + # Same with a server hostname + s = ctx.wrap_socket(socket.socket(socket.AF_INET), + server_hostname="svn.python.org") + if ssl.HAS_SNI: + s.connect(("svn.python.org", 443)) + s.close() + else: + self.assertRaises(ValueError, s.connect, ("svn.python.org", 443)) + # This should fail because we have no verification certs + ctx.verify_mode = ssl.CERT_REQUIRED + s = ctx.wrap_socket(socket.socket(socket.AF_INET)) + self.assertRaisesRegexp(ssl.SSLError, "certificate verify failed", + s.connect, ("svn.python.org", 443)) + s.close() + # This should succeed because we specify the root cert + ctx.load_verify_locations(SVN_PYTHON_ORG_ROOT_CERT) + s = ctx.wrap_socket(socket.socket(socket.AF_INET)) + s.connect(("svn.python.org", 443)) + try: + cert = s.getpeercert() + self.assertTrue(cert) + finally: + s.close() + + def test_connect_capath(self): + # Verify server certificates using the `capath` argument + # NOTE: the subject hashing algorithm has been changed between + # OpenSSL 0.9.8n and 1.0.0, as a result the capath directory must + # contain both versions of each certificate (same content, different + # filename) for this test to be portable across OpenSSL releases. + with support.transient_internet("svn.python.org"): + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.verify_mode = ssl.CERT_REQUIRED + ctx.load_verify_locations(capath=CAPATH) + s = ctx.wrap_socket(socket.socket(socket.AF_INET)) + s.connect(("svn.python.org", 443)) + try: + cert = s.getpeercert() + self.assertTrue(cert) + finally: + s.close() + # Same with a bytes `capath` argument + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.verify_mode = ssl.CERT_REQUIRED + ctx.load_verify_locations(capath=BYTES_CAPATH) + s = ctx.wrap_socket(socket.socket(socket.AF_INET)) + s.connect(("svn.python.org", 443)) + try: + cert = s.getpeercert() + self.assertTrue(cert) finally: s.close() + def test_connect_cadata(self): + with open(CAFILE_CACERT) as f: + pem = f.read().decode('ascii') + der = ssl.PEM_cert_to_DER_cert(pem) + with support.transient_internet("svn.python.org"): + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.verify_mode = ssl.CERT_REQUIRED + ctx.load_verify_locations(cadata=pem) + with closing(ctx.wrap_socket(socket.socket(socket.AF_INET))) as s: + s.connect(("svn.python.org", 443)) + cert = s.getpeercert() + self.assertTrue(cert) + + # same with DER + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.verify_mode = ssl.CERT_REQUIRED + ctx.load_verify_locations(cadata=der) + with closing(ctx.wrap_socket(socket.socket(socket.AF_INET))) as s: + s.connect(("svn.python.org", 443)) + cert = s.getpeercert() + self.assertTrue(cert) + @unittest.skipIf(os.name == "nt", "Can't use a socket as a file under Windows") def test_makefile_close(self): # Issue #5238: creating a file-like object with makefile() shouldn't # delay closing the underlying "real socket" (here tested with its # file descriptor, hence skipping the test under Windows). - with test_support.transient_internet("svn.python.org"): + with support.transient_internet("svn.python.org"): ss = ssl.wrap_socket(socket.socket(socket.AF_INET)) ss.connect(("svn.python.org", 443)) fd = ss.fileno() @@ -368,7 +1388,7 @@ class NetworkedTests(unittest.TestCase): self.assertEqual(e.exception.errno, errno.EBADF) def test_non_blocking_handshake(self): - with test_support.transient_internet("svn.python.org"): + with support.transient_internet("svn.python.org"): s = socket.socket(socket.AF_INET) s.connect(("svn.python.org", 443)) s.setblocking(False) @@ -381,41 +1401,57 @@ class NetworkedTests(unittest.TestCase): count += 1 s.do_handshake() break - except ssl.SSLError, err: - if err.args[0] == ssl.SSL_ERROR_WANT_READ: - select.select([s], [], []) - elif err.args[0] == ssl.SSL_ERROR_WANT_WRITE: - select.select([], [s], []) - else: - raise + except ssl.SSLWantReadError: + select.select([s], [], []) + except ssl.SSLWantWriteError: + select.select([], [s], []) s.close() - if test_support.verbose: + if support.verbose: sys.stdout.write("\nNeeded %d calls to do_handshake() to establish session.\n" % count) def test_get_server_certificate(self): - with test_support.transient_internet("svn.python.org"): - pem = ssl.get_server_certificate(("svn.python.org", 443), - ssl.PROTOCOL_SSLv23) - if not pem: - self.fail("No server certificate on svn.python.org:443!") + def _test_get_server_certificate(host, port, cert=None): + with support.transient_internet(host): + pem = ssl.get_server_certificate((host, port)) + if not pem: + self.fail("No server certificate on %s:%s!" % (host, port)) - try: - pem = ssl.get_server_certificate(("svn.python.org", 443), - ssl.PROTOCOL_SSLv23, - ca_certs=CERTFILE) - except ssl.SSLError: - #should fail - pass - else: - self.fail("Got server certificate %s for svn.python.org!" % pem) + try: + pem = ssl.get_server_certificate((host, port), + ca_certs=CERTFILE) + except ssl.SSLError as x: + #should fail + if support.verbose: + sys.stdout.write("%s\n" % x) + else: + self.fail("Got server certificate %s for %s:%s!" % (pem, host, port)) + + pem = ssl.get_server_certificate((host, port), + ca_certs=cert) + if not pem: + self.fail("No server certificate on %s:%s!" % (host, port)) + if support.verbose: + sys.stdout.write("\nVerified certificate for %s:%s is\n%s\n" % (host, port ,pem)) + + _test_get_server_certificate('svn.python.org', 443, SVN_PYTHON_ORG_ROOT_CERT) + if support.IPV6_ENABLED: + _test_get_server_certificate('ipv6.google.com', 443) - pem = ssl.get_server_certificate(("svn.python.org", 443), - ssl.PROTOCOL_SSLv23, - ca_certs=SVN_PYTHON_ORG_ROOT_CERT) - if not pem: - self.fail("No server certificate on svn.python.org:443!") - if test_support.verbose: - sys.stdout.write("\nVerified certificate for svn.python.org:443 is\n%s\n" % pem) + def test_ciphers(self): + remote = ("svn.python.org", 443) + with support.transient_internet(remote[0]): + with closing(ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_NONE, ciphers="ALL")) as s: + s.connect(remote) + with closing(ssl.wrap_socket(socket.socket(socket.AF_INET), + cert_reqs=ssl.CERT_NONE, ciphers="DEFAULT")) as s: + s.connect(remote) + # Error checking can happen at instantiation or when connecting + with self.assertRaisesRegexp(ssl.SSLError, "No cipher can be selected"): + with closing(socket.socket(socket.AF_INET)) as sock: + s = ssl.wrap_socket(sock, + cert_reqs=ssl.CERT_NONE, ciphers="^$:,;?*'dorothyx") + s.connect(remote) def test_algorithms(self): # Issue #8484: all algorithms should be available when verifying a @@ -423,17 +1459,21 @@ class NetworkedTests(unittest.TestCase): # SHA256 was added in OpenSSL 0.9.8 if ssl.OPENSSL_VERSION_INFO < (0, 9, 8, 0, 15): self.skipTest("SHA256 not available on %r" % ssl.OPENSSL_VERSION) - self.skipTest("remote host needs SNI, only available on Python 3.2+") - # NOTE: https://sha2.hboeck.de is another possible test host + # sha256.tbs-internet.com needs SNI to use the correct certificate + if not ssl.HAS_SNI: + self.skipTest("SNI needed for this test") + # https://sha2.hboeck.de/ was used until 2011-01-08 (no route to host) remote = ("sha256.tbs-internet.com", 443) sha256_cert = os.path.join(os.path.dirname(__file__), "sha256.pem") - with test_support.transient_internet("sha256.tbs-internet.com"): - s = ssl.wrap_socket(socket.socket(socket.AF_INET), - cert_reqs=ssl.CERT_REQUIRED, - ca_certs=sha256_cert,) + with support.transient_internet("sha256.tbs-internet.com"): + ctx = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + ctx.verify_mode = ssl.CERT_REQUIRED + ctx.load_verify_locations(sha256_cert) + s = ctx.wrap_socket(socket.socket(socket.AF_INET), + server_hostname="sha256.tbs-internet.com") try: s.connect(remote) - if test_support.verbose: + if support.verbose: sys.stdout.write("\nCipher with %r is %r\n" % (remote, s.cipher())) sys.stdout.write("Certificate is:\n%s\n" % @@ -441,6 +1481,36 @@ class NetworkedTests(unittest.TestCase): finally: s.close() + def test_get_ca_certs_capath(self): + # capath certs are loaded on request + with support.transient_internet("svn.python.org"): + ctx = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + ctx.verify_mode = ssl.CERT_REQUIRED + ctx.load_verify_locations(capath=CAPATH) + self.assertEqual(ctx.get_ca_certs(), []) + s = ctx.wrap_socket(socket.socket(socket.AF_INET)) + s.connect(("svn.python.org", 443)) + try: + cert = s.getpeercert() + self.assertTrue(cert) + finally: + s.close() + self.assertEqual(len(ctx.get_ca_certs()), 1) + + @needs_sni + def test_context_setget(self): + # Check that the context of a connected socket can be replaced. + with support.transient_internet("svn.python.org"): + ctx1 = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + ctx2 = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + s = socket.socket(socket.AF_INET) + with closing(ctx1.wrap_socket(s)) as ss: + ss.connect(("svn.python.org", 443)) + self.assertIs(ss.context, ctx1) + self.assertIs(ss._sslobj.context, ctx1) + ss.context = ctx2 + self.assertIs(ss.context, ctx2) + self.assertIs(ss._sslobj.context, ctx2) try: import threading @@ -449,6 +1519,8 @@ except ImportError: else: _have_threads = True + from test.ssl_servers import make_https_server + class ThreadedEchoServer(threading.Thread): class ConnectionHandler(threading.Thread): @@ -457,48 +1529,51 @@ else: with and without the SSL wrapper around the socket connection, so that we can test the STARTTLS functionality.""" - def __init__(self, server, connsock): + def __init__(self, server, connsock, addr): self.server = server self.running = False self.sock = connsock + self.addr = addr self.sock.setblocking(1) self.sslconn = None threading.Thread.__init__(self) self.daemon = True - def show_conn_details(self): - if self.server.certreqs == ssl.CERT_REQUIRED: - cert = self.sslconn.getpeercert() - if test_support.verbose and self.server.chatty: - sys.stdout.write(" client cert is " + pprint.pformat(cert) + "\n") - cert_binary = self.sslconn.getpeercert(True) - if test_support.verbose and self.server.chatty: - sys.stdout.write(" cert binary is " + str(len(cert_binary)) + " bytes\n") - cipher = self.sslconn.cipher() - if test_support.verbose and self.server.chatty: - sys.stdout.write(" server: connection cipher is now " + str(cipher) + "\n") - def wrap_conn(self): try: - self.sslconn = ssl.wrap_socket(self.sock, server_side=True, - certfile=self.server.certificate, - ssl_version=self.server.protocol, - ca_certs=self.server.cacerts, - cert_reqs=self.server.certreqs, - ciphers=self.server.ciphers) - except ssl.SSLError as e: + self.sslconn = self.server.context.wrap_socket( + self.sock, server_side=True) + self.server.selected_protocols.append(self.sslconn.selected_npn_protocol()) + except socket.error as e: + # We treat ConnectionResetError as though it were an + # SSLError - OpenSSL on Ubuntu abruptly closes the + # connection when asked to use an unsupported protocol. + # # XXX Various errors can have happened here, for example # a mismatching protocol version, an invalid certificate, # or a low-level bug. This should be made more discriminating. + if not isinstance(e, ssl.SSLError) and e.errno != errno.ECONNRESET: + raise self.server.conn_errors.append(e) if self.server.chatty: - handle_error("\n server: bad connection attempt from " + - str(self.sock.getpeername()) + ":\n") - self.close() + handle_error("\n server: bad connection attempt from " + repr(self.addr) + ":\n") self.running = False self.server.stop() + self.close() return False else: + if self.server.context.verify_mode == ssl.CERT_REQUIRED: + cert = self.sslconn.getpeercert() + if support.verbose and self.server.chatty: + sys.stdout.write(" client cert is " + pprint.pformat(cert) + "\n") + cert_binary = self.sslconn.getpeercert(True) + if support.verbose and self.server.chatty: + sys.stdout.write(" cert binary is " + str(len(cert_binary)) + " bytes\n") + cipher = self.sslconn.cipher() + if support.verbose and self.server.chatty: + sys.stdout.write(" server: connection cipher is now " + str(cipher) + "\n") + sys.stdout.write(" server: selected protocol is now " + + str(self.sslconn.selected_npn_protocol()) + "\n") return True def read(self): @@ -517,48 +1592,53 @@ else: if self.sslconn: self.sslconn.close() else: - self.sock._sock.close() + self.sock.close() def run(self): self.running = True if not self.server.starttls_server: - if isinstance(self.sock, ssl.SSLSocket): - self.sslconn = self.sock - elif not self.wrap_conn(): + if not self.wrap_conn(): return - self.show_conn_details() while self.running: try: msg = self.read() - if not msg: + stripped = msg.strip() + if not stripped: # eof, so quit this handler self.running = False self.close() - elif msg.strip() == 'over': - if test_support.verbose and self.server.connectionchatty: + elif stripped == b'over': + if support.verbose and self.server.connectionchatty: sys.stdout.write(" server: client closed connection\n") self.close() return - elif self.server.starttls_server and msg.strip() == 'STARTTLS': - if test_support.verbose and self.server.connectionchatty: + elif (self.server.starttls_server and + stripped == b'STARTTLS'): + if support.verbose and self.server.connectionchatty: sys.stdout.write(" server: read STARTTLS from client, sending OK...\n") - self.write("OK\n") + self.write(b"OK\n") if not self.wrap_conn(): return - elif self.server.starttls_server and self.sslconn and msg.strip() == 'ENDTLS': - if test_support.verbose and self.server.connectionchatty: + elif (self.server.starttls_server and self.sslconn + and stripped == b'ENDTLS'): + if support.verbose and self.server.connectionchatty: sys.stdout.write(" server: read ENDTLS from client, sending OK...\n") - self.write("OK\n") - self.sslconn.unwrap() + self.write(b"OK\n") + self.sock = self.sslconn.unwrap() self.sslconn = None - if test_support.verbose and self.server.connectionchatty: + if support.verbose and self.server.connectionchatty: sys.stdout.write(" server: connection is now unencrypted...\n") + elif stripped == b'CB tls-unique': + if support.verbose and self.server.connectionchatty: + sys.stdout.write(" server: read CB tls-unique from client, sending our CB data...\n") + data = self.sslconn.get_channel_binding("tls-unique") + self.write(repr(data).encode("us-ascii") + b"\n") else: - if (test_support.verbose and + if (support.verbose and self.server.connectionchatty): ctype = (self.sslconn and "encrypted") or "unencrypted" - sys.stdout.write(" server: read %s (%s), sending back %s (%s)...\n" - % (repr(msg), ctype, repr(msg.lower()), ctype)) + sys.stdout.write(" server: read %r (%s), sending back %r (%s)...\n" + % (msg, ctype, msg.lower(), ctype)) self.write(msg.lower()) except ssl.SSLError: if self.server.chatty: @@ -569,36 +1649,34 @@ else: # harness, we want to stop the server self.server.stop() - def __init__(self, certificate, ssl_version=None, + def __init__(self, certificate=None, ssl_version=None, certreqs=None, cacerts=None, chatty=True, connectionchatty=False, starttls_server=False, - wrap_accepting_socket=False, ciphers=None): - - if ssl_version is None: - ssl_version = ssl.PROTOCOL_TLSv1 - if certreqs is None: - certreqs = ssl.CERT_NONE - self.certificate = certificate - self.protocol = ssl_version - self.certreqs = certreqs - self.cacerts = cacerts - self.ciphers = ciphers + npn_protocols=None, ciphers=None, context=None): + if context: + self.context = context + else: + self.context = ssl.SSLContext(ssl_version + if ssl_version is not None + else ssl.PROTOCOL_TLSv1) + self.context.verify_mode = (certreqs if certreqs is not None + else ssl.CERT_NONE) + if cacerts: + self.context.load_verify_locations(cacerts) + if certificate: + self.context.load_cert_chain(certificate) + if npn_protocols: + self.context.set_npn_protocols(npn_protocols) + if ciphers: + self.context.set_ciphers(ciphers) self.chatty = chatty self.connectionchatty = connectionchatty self.starttls_server = starttls_server self.sock = socket.socket() + self.port = support.bind_port(self.sock) self.flag = None - if wrap_accepting_socket: - self.sock = ssl.wrap_socket(self.sock, server_side=True, - certfile=self.certificate, - cert_reqs = self.certreqs, - ca_certs = self.cacerts, - ssl_version = self.protocol, - ciphers = self.ciphers) - if test_support.verbose and self.chatty: - sys.stdout.write(' server: wrapped server socket as %s\n' % str(self.sock)) - self.port = test_support.bind_port(self.sock) self.active = False + self.selected_protocols = [] self.conn_errors = [] threading.Thread.__init__(self) self.daemon = True @@ -626,10 +1704,10 @@ else: while self.active: try: newconn, connaddr = self.sock.accept() - if test_support.verbose and self.chatty: + if support.verbose and self.chatty: sys.stdout.write(' server: new connection from ' - + str(connaddr) + '\n') - handler = self.ConnectionHandler(self, newconn) + + repr(connaddr) + '\n') + handler = self.ConnectionHandler(self, newconn, connaddr) handler.start() handler.join() except socket.timeout: @@ -648,11 +1726,12 @@ else: class ConnectionHandler(asyncore.dispatcher_with_send): def __init__(self, conn, certfile): - asyncore.dispatcher_with_send.__init__(self, conn) self.socket = ssl.wrap_socket(conn, server_side=True, certfile=certfile, do_handshake_on_connect=False) + asyncore.dispatcher_with_send.__init__(self, self.socket) self._ssl_accepting = True + self._do_ssl_handshake() def readable(self): if isinstance(self.socket, ssl.SSLSocket): @@ -663,12 +1742,11 @@ else: def _do_ssl_handshake(self): try: self.socket.do_handshake() - except ssl.SSLError, err: - if err.args[0] in (ssl.SSL_ERROR_WANT_READ, - ssl.SSL_ERROR_WANT_WRITE): - return - elif err.args[0] == ssl.SSL_ERROR_EOF: - return self.handle_close() + except (ssl.SSLWantReadError, ssl.SSLWantWriteError): + return + except ssl.SSLEOFError: + return self.handle_close() + except ssl.SSLError: raise except socket.error, err: if err.args[0] == errno.ECONNABORTED: @@ -681,12 +1759,16 @@ else: self._do_ssl_handshake() else: data = self.recv(1024) - if data and data.strip() != 'over': + if support.verbose: + sys.stdout.write(" server: read %s from client\n" % repr(data)) + if not data: + self.close() + else: self.send(data.lower()) def handle_close(self): self.close() - if test_support.verbose: + if support.verbose: sys.stdout.write(" server: closed connection %s\n" % self.socket) def handle_error(self): @@ -694,14 +1776,14 @@ else: def __init__(self, certfile): self.certfile = certfile - asyncore.dispatcher.__init__(self) - self.create_socket(socket.AF_INET, socket.SOCK_STREAM) - self.port = test_support.bind_port(self.socket) + sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + self.port = support.bind_port(sock, '') + asyncore.dispatcher.__init__(self, sock) self.listen(5) def handle_accept(self): sock_obj, addr = self.accept() - if test_support.verbose: + if support.verbose: sys.stdout.write(" server: new connection from %s:%s\n" %addr) self.ConnectionHandler(sock_obj, self.certfile) @@ -725,13 +1807,13 @@ else: return self def __exit__(self, *args): - if test_support.verbose: + if support.verbose: sys.stdout.write(" cleanup: stopping server.\n") self.stop() - if test_support.verbose: + if support.verbose: sys.stdout.write(" cleanup: joining server thread.\n") self.join() - if test_support.verbose: + if support.verbose: sys.stdout.write(" cleanup: successfully joined.\n") def start(self, flag=None): @@ -743,103 +1825,15 @@ else: if self.flag: self.flag.set() while self.active: - asyncore.loop(0.05) + try: + asyncore.loop(1) + except: + pass def stop(self): self.active = False self.server.close() - class SocketServerHTTPSServer(threading.Thread): - - class HTTPSServer(HTTPServer): - - def __init__(self, server_address, RequestHandlerClass, certfile): - HTTPServer.__init__(self, server_address, RequestHandlerClass) - # we assume the certfile contains both private key and certificate - self.certfile = certfile - self.allow_reuse_address = True - - def __str__(self): - return ('<%s %s:%s>' % - (self.__class__.__name__, - self.server_name, - self.server_port)) - - def get_request(self): - # override this to wrap socket with SSL - sock, addr = self.socket.accept() - sslconn = ssl.wrap_socket(sock, server_side=True, - certfile=self.certfile) - return sslconn, addr - - class RootedHTTPRequestHandler(SimpleHTTPRequestHandler): - # need to override translate_path to get a known root, - # instead of using os.curdir, since the test could be - # run from anywhere - - server_version = "TestHTTPS/1.0" - - root = None - - def translate_path(self, path): - """Translate a /-separated PATH to the local filename syntax. - - Components that mean special things to the local file system - (e.g. drive or directory names) are ignored. (XXX They should - probably be diagnosed.) - - """ - # abandon query parameters - path = urlparse.urlparse(path)[2] - path = os.path.normpath(urllib.unquote(path)) - words = path.split('/') - words = filter(None, words) - path = self.root - for word in words: - drive, word = os.path.splitdrive(word) - head, word = os.path.split(word) - if word in self.root: continue - path = os.path.join(path, word) - return path - - def log_message(self, format, *args): - - # we override this to suppress logging unless "verbose" - - if test_support.verbose: - sys.stdout.write(" server (%s:%d %s):\n [%s] %s\n" % - (self.server.server_address, - self.server.server_port, - self.request.cipher(), - self.log_date_time_string(), - format%args)) - - - def __init__(self, certfile): - self.flag = None - self.RootedHTTPRequestHandler.root = os.path.split(CERTFILE)[0] - self.server = self.HTTPSServer( - (HOST, 0), self.RootedHTTPRequestHandler, certfile) - self.port = self.server.server_port - threading.Thread.__init__(self) - self.daemon = True - - def __str__(self): - return "<%s %s>" % (self.__class__.__name__, self.server) - - def start(self, flag=None): - self.flag = flag - threading.Thread.start(self) - - def run(self): - if self.flag: - self.flag.set() - self.server.serve_forever(0.05) - - def stop(self): - self.server.shutdown() - - def bad_cert_test(certfile): """ Launch a server with CERT_REQUIRED, and check that trying to @@ -847,74 +1841,74 @@ else: """ server = ThreadedEchoServer(CERTFILE, certreqs=ssl.CERT_REQUIRED, - cacerts=CERTFILE, chatty=False) + cacerts=CERTFILE, chatty=False, + connectionchatty=False) with server: try: - s = ssl.wrap_socket(socket.socket(), - certfile=certfile, - ssl_version=ssl.PROTOCOL_TLSv1) - s.connect((HOST, server.port)) - except ssl.SSLError, x: - if test_support.verbose: - sys.stdout.write("\nSSLError is %s\n" % x[1]) - except socket.error, x: - if test_support.verbose: - sys.stdout.write("\nsocket.error is %s\n" % x[1]) + with closing(socket.socket()) as sock: + s = ssl.wrap_socket(sock, + certfile=certfile, + ssl_version=ssl.PROTOCOL_TLSv1) + s.connect((HOST, server.port)) + except ssl.SSLError as x: + if support.verbose: + sys.stdout.write("\nSSLError is %s\n" % x.args[1]) + except OSError as x: + if support.verbose: + sys.stdout.write("\nOSError is %s\n" % x.args[1]) + except OSError as x: + if x.errno != errno.ENOENT: + raise + if support.verbose: + sys.stdout.write("\OSError is %s\n" % str(x)) else: raise AssertionError("Use of invalid cert should have failed!") - def server_params_test(certfile, protocol, certreqs, cacertsfile, - client_certfile, client_protocol=None, indata="FOO\n", - ciphers=None, chatty=True, connectionchatty=False, - wrap_accepting_socket=False): + def server_params_test(client_context, server_context, indata=b"FOO\n", + chatty=True, connectionchatty=False, sni_name=None): """ Launch a server, connect a client to it and try various reads and writes. """ - server = ThreadedEchoServer(certfile, - certreqs=certreqs, - ssl_version=protocol, - cacerts=cacertsfile, - ciphers=ciphers, + stats = {} + server = ThreadedEchoServer(context=server_context, chatty=chatty, - connectionchatty=connectionchatty, - wrap_accepting_socket=wrap_accepting_socket) + connectionchatty=False) with server: - # try to connect - if client_protocol is None: - client_protocol = protocol - s = ssl.wrap_socket(socket.socket(), - certfile=client_certfile, - ca_certs=cacertsfile, - ciphers=ciphers, - cert_reqs=certreqs, - ssl_version=client_protocol) - s.connect((HOST, server.port)) - for arg in [indata, bytearray(indata), memoryview(indata)]: - if connectionchatty: - if test_support.verbose: - sys.stdout.write( - " client: sending %s...\n" % (repr(arg))) - s.write(arg) - outdata = s.read() + with closing(client_context.wrap_socket(socket.socket(), + server_hostname=sni_name)) as s: + s.connect((HOST, server.port)) + for arg in [indata, bytearray(indata), memoryview(indata)]: + if connectionchatty: + if support.verbose: + sys.stdout.write( + " client: sending %r...\n" % indata) + s.write(arg) + outdata = s.read() + if connectionchatty: + if support.verbose: + sys.stdout.write(" client: read %r\n" % outdata) + if outdata != indata.lower(): + raise AssertionError( + "bad data <<%r>> (%d) received; expected <<%r>> (%d)\n" + % (outdata[:20], len(outdata), + indata[:20].lower(), len(indata))) + s.write(b"over\n") if connectionchatty: - if test_support.verbose: - sys.stdout.write(" client: read %s\n" % repr(outdata)) - if outdata != indata.lower(): - raise AssertionError( - "bad data <<%s>> (%d) received; expected <<%s>> (%d)\n" - % (outdata[:min(len(outdata),20)], len(outdata), - indata[:min(len(indata),20)].lower(), len(indata))) - s.write("over\n") - if connectionchatty: - if test_support.verbose: - sys.stdout.write(" client: closing connection.\n") - s.close() + if support.verbose: + sys.stdout.write(" client: closing connection.\n") + stats.update({ + 'compression': s.compression(), + 'cipher': s.cipher(), + 'peercert': s.getpeercert(), + 'client_npn_protocol': s.selected_npn_protocol() + }) + s.close() + stats['server_npn_protocols'] = server.selected_protocols + return stats - def try_protocol_combo(server_protocol, - client_protocol, - expect_success, - certsreqs=None): + def try_protocol_combo(server_protocol, client_protocol, expect_success, + certsreqs=None, server_options=0, client_options=0): if certsreqs is None: certsreqs = ssl.CERT_NONE certtype = { @@ -922,19 +1916,30 @@ else: ssl.CERT_OPTIONAL: "CERT_OPTIONAL", ssl.CERT_REQUIRED: "CERT_REQUIRED", }[certsreqs] - if test_support.verbose: + if support.verbose: formatstr = (expect_success and " %s->%s %s\n") or " {%s->%s} %s\n" sys.stdout.write(formatstr % (ssl.get_protocol_name(client_protocol), ssl.get_protocol_name(server_protocol), certtype)) + client_context = ssl.SSLContext(client_protocol) + client_context.options |= client_options + server_context = ssl.SSLContext(server_protocol) + server_context.options |= server_options + + # NOTE: we must enable "ALL" ciphers on the client, otherwise an + # SSLv23 client will send an SSLv3 hello (rather than SSLv2) + # starting from OpenSSL 1.0.0 (see issue #8322). + if client_context.protocol == ssl.PROTOCOL_SSLv23: + client_context.set_ciphers("ALL") + + for ctx in (client_context, server_context): + ctx.verify_mode = certsreqs + ctx.load_cert_chain(CERTFILE) + ctx.load_verify_locations(CERTFILE) try: - # NOTE: we must enable "ALL" ciphers, otherwise an SSLv23 client - # will send an SSLv3 hello (rather than SSLv2) starting from - # OpenSSL 1.0.0 (see issue #8322). - server_params_test(CERTFILE, server_protocol, certsreqs, - CERTFILE, CERTFILE, client_protocol, - ciphers="ALL", chatty=False) + server_params_test(client_context, server_context, + chatty=False, connectionchatty=False) # Protocol mismatch can result in either an SSLError, or a # "Connection reset by peer" error. except ssl.SSLError: @@ -953,75 +1958,38 @@ else: class ThreadedTests(unittest.TestCase): - def test_rude_shutdown(self): - """A brutal shutdown of an SSL server should raise an IOError - in the client when attempting handshake. - """ - listener_ready = threading.Event() - listener_gone = threading.Event() - - s = socket.socket() - port = test_support.bind_port(s, HOST) - - # `listener` runs in a thread. It sits in an accept() until - # the main thread connects. Then it rudely closes the socket, - # and sets Event `listener_gone` to let the main thread know - # the socket is gone. - def listener(): - s.listen(5) - listener_ready.set() - s.accept() - s.close() - listener_gone.set() - - def connector(): - listener_ready.wait() - c = socket.socket() - c.connect((HOST, port)) - listener_gone.wait() - try: - ssl_sock = ssl.wrap_socket(c) - except IOError: - pass - else: - self.fail('connecting to closed SSL socket should have failed') - - t = threading.Thread(target=listener) - t.start() - try: - connector() - finally: - t.join() - @skip_if_broken_ubuntu_ssl def test_echo(self): """Basic test of an SSL client connecting to a server""" - if test_support.verbose: + if support.verbose: sys.stdout.write("\n") - server_params_test(CERTFILE, ssl.PROTOCOL_TLSv1, ssl.CERT_NONE, - CERTFILE, CERTFILE, ssl.PROTOCOL_TLSv1, - chatty=True, connectionchatty=True) + for protocol in PROTOCOLS: + context = ssl.SSLContext(protocol) + context.load_cert_chain(CERTFILE) + server_params_test(context, context, + chatty=True, connectionchatty=True) def test_getpeercert(self): - if test_support.verbose: + if support.verbose: sys.stdout.write("\n") - s2 = socket.socket() - server = ThreadedEchoServer(CERTFILE, - certreqs=ssl.CERT_NONE, - ssl_version=ssl.PROTOCOL_SSLv23, - cacerts=CERTFILE, - chatty=False) + context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + context.verify_mode = ssl.CERT_REQUIRED + context.load_verify_locations(CERTFILE) + context.load_cert_chain(CERTFILE) + server = ThreadedEchoServer(context=context, chatty=False) with server: - s = ssl.wrap_socket(socket.socket(), - certfile=CERTFILE, - ca_certs=CERTFILE, - cert_reqs=ssl.CERT_REQUIRED, - ssl_version=ssl.PROTOCOL_SSLv23) + s = context.wrap_socket(socket.socket(), + do_handshake_on_connect=False) s.connect((HOST, server.port)) + # getpeercert() raise ValueError while the handshake isn't + # done. + with self.assertRaises(ValueError): + s.getpeercert() + s.do_handshake() cert = s.getpeercert() self.assertTrue(cert, "Can't get peer certificate.") cipher = s.cipher() - if test_support.verbose: + if support.verbose: sys.stdout.write(pprint.pformat(cert) + '\n') sys.stdout.write("Connection cipher is " + str(cipher) + '.\n') if 'subject' not in cert: @@ -1032,8 +2000,94 @@ else: self.fail( "Missing or invalid 'organizationName' field in certificate subject; " "should be 'Python Software Foundation'.") + self.assertIn('notBefore', cert) + self.assertIn('notAfter', cert) + before = ssl.cert_time_to_seconds(cert['notBefore']) + after = ssl.cert_time_to_seconds(cert['notAfter']) + self.assertLess(before, after) s.close() + @unittest.skipUnless(have_verify_flags(), + "verify_flags need OpenSSL > 0.9.8") + def test_crl_check(self): + if support.verbose: + sys.stdout.write("\n") + + server_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + server_context.load_cert_chain(SIGNED_CERTFILE) + + context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + context.verify_mode = ssl.CERT_REQUIRED + context.load_verify_locations(SIGNING_CA) + self.assertEqual(context.verify_flags, ssl.VERIFY_DEFAULT) + + # VERIFY_DEFAULT should pass + server = ThreadedEchoServer(context=server_context, chatty=True) + with server: + with closing(context.wrap_socket(socket.socket())) as s: + s.connect((HOST, server.port)) + cert = s.getpeercert() + self.assertTrue(cert, "Can't get peer certificate.") + + # VERIFY_CRL_CHECK_LEAF without a loaded CRL file fails + context.verify_flags |= ssl.VERIFY_CRL_CHECK_LEAF + + server = ThreadedEchoServer(context=server_context, chatty=True) + with server: + with closing(context.wrap_socket(socket.socket())) as s: + with self.assertRaisesRegexp(ssl.SSLError, + "certificate verify failed"): + s.connect((HOST, server.port)) + + # now load a CRL file. The CRL file is signed by the CA. + context.load_verify_locations(CRLFILE) + + server = ThreadedEchoServer(context=server_context, chatty=True) + with server: + with closing(context.wrap_socket(socket.socket())) as s: + s.connect((HOST, server.port)) + cert = s.getpeercert() + self.assertTrue(cert, "Can't get peer certificate.") + + @needs_sni + def test_check_hostname(self): + if support.verbose: + sys.stdout.write("\n") + + server_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + server_context.load_cert_chain(SIGNED_CERTFILE) + + context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + context.verify_mode = ssl.CERT_REQUIRED + context.check_hostname = True + context.load_verify_locations(SIGNING_CA) + + # correct hostname should verify + server = ThreadedEchoServer(context=server_context, chatty=True) + with server: + with closing(context.wrap_socket(socket.socket(), + server_hostname="localhost")) as s: + s.connect((HOST, server.port)) + cert = s.getpeercert() + self.assertTrue(cert, "Can't get peer certificate.") + + # incorrect hostname should raise an exception + server = ThreadedEchoServer(context=server_context, chatty=True) + with server: + with closing(context.wrap_socket(socket.socket(), + server_hostname="invalid")) as s: + with self.assertRaisesRegexp(ssl.CertificateError, + "hostname 'invalid' doesn't match u?'localhost'"): + s.connect((HOST, server.port)) + + # missing server_hostname arg should cause an exception, too + server = ThreadedEchoServer(context=server_context, chatty=True) + with server: + with closing(socket.socket()) as s: + with self.assertRaisesRegexp(ValueError, + "check_hostname requires server_hostname"): + context.wrap_socket(s) + def test_empty_cert(self): """Connecting with an empty cert file""" bad_cert_test(os.path.join(os.path.dirname(__file__) or os.curdir, @@ -1051,25 +2105,84 @@ else: bad_cert_test(os.path.join(os.path.dirname(__file__) or os.curdir, "badkey.pem")) + def test_rude_shutdown(self): + """A brutal shutdown of an SSL server should raise an OSError + in the client when attempting handshake. + """ + listener_ready = threading.Event() + listener_gone = threading.Event() + + s = socket.socket() + port = support.bind_port(s, HOST) + + # `listener` runs in a thread. It sits in an accept() until + # the main thread connects. Then it rudely closes the socket, + # and sets Event `listener_gone` to let the main thread know + # the socket is gone. + def listener(): + s.listen(5) + listener_ready.set() + newsock, addr = s.accept() + newsock.close() + s.close() + listener_gone.set() + + def connector(): + listener_ready.wait() + with closing(socket.socket()) as c: + c.connect((HOST, port)) + listener_gone.wait() + try: + ssl_sock = ssl.wrap_socket(c) + except ssl.SSLError: + pass + else: + self.fail('connecting to closed SSL socket should have failed') + + t = threading.Thread(target=listener) + t.start() + try: + connector() + finally: + t.join() + @skip_if_broken_ubuntu_ssl + @unittest.skipUnless(hasattr(ssl, 'PROTOCOL_SSLv2'), + "OpenSSL is compiled without SSLv2 support") def test_protocol_sslv2(self): """Connecting to an SSLv2 server with various client options""" - if test_support.verbose: + if support.verbose: sys.stdout.write("\n") - if not hasattr(ssl, 'PROTOCOL_SSLv2'): - self.skipTest("PROTOCOL_SSLv2 needed") try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True) try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True, ssl.CERT_OPTIONAL) try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv2, True, ssl.CERT_REQUIRED) try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, False) try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv3, False) try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_TLSv1, False) + # SSLv23 client with specific SSL options + if no_sslv2_implies_sslv3_hello(): + # No SSLv2 => client will use an SSLv3 hello on recent OpenSSLs + try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, False, + client_options=ssl.OP_NO_SSLv2) + try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, False, + client_options=ssl.OP_NO_SSLv3) + try_protocol_combo(ssl.PROTOCOL_SSLv2, ssl.PROTOCOL_SSLv23, False, + client_options=ssl.OP_NO_TLSv1) @skip_if_broken_ubuntu_ssl def test_protocol_sslv23(self): """Connecting to an SSLv23 server with various client options""" - if test_support.verbose: + if support.verbose: sys.stdout.write("\n") + if hasattr(ssl, 'PROTOCOL_SSLv2'): + try: + try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv2, True) + except socket.error as x: + # this fails on some older versions of OpenSSL (0.9.7l, for instance) + if support.verbose: + sys.stdout.write( + " SSL2 client to SSL23 server test unexpectedly failed:\n %s\n" + % str(x)) try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, True) try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True) try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True) @@ -1082,22 +2195,38 @@ else: try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, ssl.CERT_REQUIRED) try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, True, ssl.CERT_REQUIRED) + # Server with specific SSL options + try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv3, False, + server_options=ssl.OP_NO_SSLv3) + # Will choose TLSv1 + try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_SSLv23, True, + server_options=ssl.OP_NO_SSLv2 | ssl.OP_NO_SSLv3) + try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1, False, + server_options=ssl.OP_NO_TLSv1) + + @skip_if_broken_ubuntu_ssl def test_protocol_sslv3(self): """Connecting to an SSLv3 server with various client options""" - if test_support.verbose: + if support.verbose: sys.stdout.write("\n") try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True) try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_OPTIONAL) try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv3, True, ssl.CERT_REQUIRED) if hasattr(ssl, 'PROTOCOL_SSLv2'): try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv2, False) + try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, False, + client_options=ssl.OP_NO_SSLv3) try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_TLSv1, False) + if no_sslv2_implies_sslv3_hello(): + # No SSLv2 => client will use an SSLv3 hello on recent OpenSSLs + try_protocol_combo(ssl.PROTOCOL_SSLv3, ssl.PROTOCOL_SSLv23, True, + client_options=ssl.OP_NO_SSLv2) @skip_if_broken_ubuntu_ssl def test_protocol_tlsv1(self): """Connecting to a TLSv1 server with various client options""" - if test_support.verbose: + if support.verbose: sys.stdout.write("\n") try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True) try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1, True, ssl.CERT_OPTIONAL) @@ -1105,10 +2234,55 @@ else: if hasattr(ssl, 'PROTOCOL_SSLv2'): try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv2, False) try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv3, False) + try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_SSLv23, False, + client_options=ssl.OP_NO_TLSv1) + + @skip_if_broken_ubuntu_ssl + @unittest.skipUnless(hasattr(ssl, "PROTOCOL_TLSv1_1"), + "TLS version 1.1 not supported.") + def test_protocol_tlsv1_1(self): + """Connecting to a TLSv1.1 server with various client options. + Testing against older TLS versions.""" + if support.verbose: + sys.stdout.write("\n") + try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1_1, True) + if hasattr(ssl, 'PROTOCOL_SSLv2'): + try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_SSLv2, False) + try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_SSLv3, False) + try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_SSLv23, False, + client_options=ssl.OP_NO_TLSv1_1) + + try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1_1, True) + try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1, False) + try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1_1, False) + + + @skip_if_broken_ubuntu_ssl + @unittest.skipUnless(hasattr(ssl, "PROTOCOL_TLSv1_2"), + "TLS version 1.2 not supported.") + def test_protocol_tlsv1_2(self): + """Connecting to a TLSv1.2 server with various client options. + Testing against older TLS versions.""" + if support.verbose: + sys.stdout.write("\n") + try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1_2, True, + server_options=ssl.OP_NO_SSLv3|ssl.OP_NO_SSLv2, + client_options=ssl.OP_NO_SSLv3|ssl.OP_NO_SSLv2,) + if hasattr(ssl, 'PROTOCOL_SSLv2'): + try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_SSLv2, False) + try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_SSLv3, False) + try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_SSLv23, False, + client_options=ssl.OP_NO_TLSv1_2) + + try_protocol_combo(ssl.PROTOCOL_SSLv23, ssl.PROTOCOL_TLSv1_2, True) + try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1, False) + try_protocol_combo(ssl.PROTOCOL_TLSv1, ssl.PROTOCOL_TLSv1_2, False) + try_protocol_combo(ssl.PROTOCOL_TLSv1_2, ssl.PROTOCOL_TLSv1_1, False) + try_protocol_combo(ssl.PROTOCOL_TLSv1_1, ssl.PROTOCOL_TLSv1_2, False) def test_starttls(self): """Switching from clear text to encrypted and back again.""" - msgs = ("msg 1", "MSG 2", "STARTTLS", "MSG 3", "msg 4", "ENDTLS", "msg 5", "msg 6") + msgs = (b"msg 1", b"MSG 2", b"STARTTLS", b"MSG 3", b"msg 4", b"ENDTLS", b"msg 5", b"msg 6") server = ThreadedEchoServer(CERTFILE, ssl_version=ssl.PROTOCOL_TLSv1, @@ -1120,119 +2294,109 @@ else: s = socket.socket() s.setblocking(1) s.connect((HOST, server.port)) - if test_support.verbose: + if support.verbose: sys.stdout.write("\n") for indata in msgs: - if test_support.verbose: + if support.verbose: sys.stdout.write( - " client: sending %s...\n" % repr(indata)) + " client: sending %r...\n" % indata) if wrapped: conn.write(indata) outdata = conn.read() else: s.send(indata) outdata = s.recv(1024) - if (indata == "STARTTLS" and - outdata.strip().lower().startswith("ok")): + msg = outdata.strip().lower() + if indata == b"STARTTLS" and msg.startswith(b"ok"): # STARTTLS ok, switch to secure mode - if test_support.verbose: + if support.verbose: sys.stdout.write( - " client: read %s from server, starting TLS...\n" - % repr(outdata)) + " client: read %r from server, starting TLS...\n" + % msg) conn = ssl.wrap_socket(s, ssl_version=ssl.PROTOCOL_TLSv1) wrapped = True - elif (indata == "ENDTLS" and - outdata.strip().lower().startswith("ok")): + elif indata == b"ENDTLS" and msg.startswith(b"ok"): # ENDTLS ok, switch back to clear text - if test_support.verbose: + if support.verbose: sys.stdout.write( - " client: read %s from server, ending TLS...\n" - % repr(outdata)) + " client: read %r from server, ending TLS...\n" + % msg) s = conn.unwrap() wrapped = False else: - if test_support.verbose: + if support.verbose: sys.stdout.write( - " client: read %s from server\n" % repr(outdata)) - if test_support.verbose: + " client: read %r from server\n" % msg) + if support.verbose: sys.stdout.write(" client: closing connection.\n") if wrapped: - conn.write("over\n") + conn.write(b"over\n") else: - s.send("over\n") - s.close() + s.send(b"over\n") + if wrapped: + conn.close() + else: + s.close() def test_socketserver(self): """Using a SocketServer to create and manage SSL connections.""" - server = SocketServerHTTPSServer(CERTFILE) - flag = threading.Event() - server.start(flag) - # wait for it to start - flag.wait() + server = make_https_server(self, certfile=CERTFILE) # try to connect + if support.verbose: + sys.stdout.write('\n') + with open(CERTFILE, 'rb') as f: + d1 = f.read() + d2 = '' + # now fetch the same data from the HTTPS server + url = 'https://%s:%d/%s' % ( + HOST, server.port, os.path.split(CERTFILE)[1]) + f = urllib.urlopen(url) try: - if test_support.verbose: - sys.stdout.write('\n') - with open(CERTFILE, 'rb') as f: - d1 = f.read() - d2 = '' - # now fetch the same data from the HTTPS server - url = 'https://127.0.0.1:%d/%s' % ( - server.port, os.path.split(CERTFILE)[1]) - with test_support.check_py3k_warnings(): - f = urllib.urlopen(url) dlen = f.info().getheader("content-length") if dlen and (int(dlen) > 0): d2 = f.read(int(dlen)) - if test_support.verbose: + if support.verbose: sys.stdout.write( " client: read %d bytes from remote server '%s'\n" % (len(d2), server)) - f.close() - self.assertEqual(d1, d2) finally: - server.stop() - server.join() - - def test_wrapped_accept(self): - """Check the accept() method on SSL sockets.""" - if test_support.verbose: - sys.stdout.write("\n") - server_params_test(CERTFILE, ssl.PROTOCOL_SSLv23, ssl.CERT_REQUIRED, - CERTFILE, CERTFILE, ssl.PROTOCOL_SSLv23, - chatty=True, connectionchatty=True, - wrap_accepting_socket=True) + f.close() + self.assertEqual(d1, d2) def test_asyncore_server(self): """Check the example asyncore integration.""" indata = "TEST MESSAGE of mixed case\n" - if test_support.verbose: + if support.verbose: sys.stdout.write("\n") + + indata = b"FOO\n" server = AsyncoreEchoServer(CERTFILE) with server: s = ssl.wrap_socket(socket.socket()) s.connect(('127.0.0.1', server.port)) - if test_support.verbose: + if support.verbose: sys.stdout.write( - " client: sending %s...\n" % (repr(indata))) + " client: sending %r...\n" % indata) s.write(indata) outdata = s.read() - if test_support.verbose: - sys.stdout.write(" client: read %s\n" % repr(outdata)) + if support.verbose: + sys.stdout.write(" client: read %r\n" % outdata) if outdata != indata.lower(): self.fail( - "bad data <<%s>> (%d) received; expected <<%s>> (%d)\n" - % (outdata[:min(len(outdata),20)], len(outdata), - indata[:min(len(indata),20)].lower(), len(indata))) - s.write("over\n") - if test_support.verbose: + "bad data <<%r>> (%d) received; expected <<%r>> (%d)\n" + % (outdata[:20], len(outdata), + indata[:20].lower(), len(indata))) + s.write(b"over\n") + if support.verbose: sys.stdout.write(" client: closing connection.\n") s.close() + if support.verbose: + sys.stdout.write(" client: connection closed.\n") def test_recv_send(self): """Test recv(), send() and friends.""" - if test_support.verbose: + if support.verbose: sys.stdout.write("\n") server = ThreadedEchoServer(CERTFILE, @@ -1251,12 +2415,12 @@ else: s.connect((HOST, server.port)) # helper methods for standardising recv* method signatures def _recv_into(): - b = bytearray("\0"*100) + b = bytearray(b"\0"*100) count = s.recv_into(b) return b[:count] def _recvfrom_into(): - b = bytearray("\0"*100) + b = bytearray(b"\0"*100) count, addr = s.recvfrom_into(b) return b[:count] @@ -1275,73 +2439,73 @@ else: data_prefix = u"PREFIX_" for meth_name, send_meth, expect_success, args in send_methods: - indata = data_prefix + meth_name + indata = (data_prefix + meth_name).encode('ascii') try: - send_meth(indata.encode('ASCII', 'strict'), *args) + send_meth(indata, *args) outdata = s.read() - outdata = outdata.decode('ASCII', 'strict') if outdata != indata.lower(): self.fail( - "While sending with <<%s>> bad data " - "<<%r>> (%d) received; " - "expected <<%r>> (%d)\n" % ( - meth_name, outdata[:20], len(outdata), - indata[:20], len(indata) + "While sending with <<{name:s}>> bad data " + "<<{outdata:r}>> ({nout:d}) received; " + "expected <<{indata:r}>> ({nin:d})\n".format( + name=meth_name, outdata=outdata[:20], + nout=len(outdata), + indata=indata[:20], nin=len(indata) ) ) except ValueError as e: if expect_success: self.fail( - "Failed to send with method <<%s>>; " - "expected to succeed.\n" % (meth_name,) + "Failed to send with method <<{name:s}>>; " + "expected to succeed.\n".format(name=meth_name) ) if not str(e).startswith(meth_name): self.fail( - "Method <<%s>> failed with unexpected " - "exception message: %s\n" % ( - meth_name, e + "Method <<{name:s}>> failed with unexpected " + "exception message: {exp:s}\n".format( + name=meth_name, exp=e ) ) for meth_name, recv_meth, expect_success, args in recv_methods: - indata = data_prefix + meth_name + indata = (data_prefix + meth_name).encode('ascii') try: - s.send(indata.encode('ASCII', 'strict')) + s.send(indata) outdata = recv_meth(*args) - outdata = outdata.decode('ASCII', 'strict') if outdata != indata.lower(): self.fail( - "While receiving with <<%s>> bad data " - "<<%r>> (%d) received; " - "expected <<%r>> (%d)\n" % ( - meth_name, outdata[:20], len(outdata), - indata[:20], len(indata) + "While receiving with <<{name:s}>> bad data " + "<<{outdata:r}>> ({nout:d}) received; " + "expected <<{indata:r}>> ({nin:d})\n".format( + name=meth_name, outdata=outdata[:20], + nout=len(outdata), + indata=indata[:20], nin=len(indata) ) ) except ValueError as e: if expect_success: self.fail( - "Failed to receive with method <<%s>>; " - "expected to succeed.\n" % (meth_name,) + "Failed to receive with method <<{name:s}>>; " + "expected to succeed.\n".format(name=meth_name) ) if not str(e).startswith(meth_name): self.fail( - "Method <<%s>> failed with unexpected " - "exception message: %s\n" % ( - meth_name, e + "Method <<{name:s}>> failed with unexpected " + "exception message: {exp:s}\n".format( + name=meth_name, exp=e ) ) # consume data s.read() - s.write("over\n".encode("ASCII", "strict")) + s.write(b"over\n") s.close() def test_handshake_timeout(self): # Issue #5103: SSL handshake must respect the socket timeout server = socket.socket(socket.AF_INET) host = "127.0.0.1" - port = test_support.bind_port(server) + port = support.bind_port(server) started = threading.Event() finish = False @@ -1355,6 +2519,8 @@ else: # Let the socket hang around rather than having # it closed by garbage collection. conns.append(server.accept()[0]) + for sock in conns: + sock.close() t = threading.Thread(target=serve) t.start() @@ -1372,8 +2538,8 @@ else: c.close() try: c = socket.socket(socket.AF_INET) - c.settimeout(0.2) c = ssl.wrap_socket(c) + c.settimeout(0.2) # Will attempt handshake and time out self.assertRaisesRegexp(ssl.SSLError, "timed out", c.connect, (host, port)) @@ -1384,59 +2550,384 @@ else: t.join() server.close() + def test_server_accept(self): + # Issue #16357: accept() on a SSLSocket created through + # SSLContext.wrap_socket(). + context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + context.verify_mode = ssl.CERT_REQUIRED + context.load_verify_locations(CERTFILE) + context.load_cert_chain(CERTFILE) + server = socket.socket(socket.AF_INET) + host = "127.0.0.1" + port = support.bind_port(server) + server = context.wrap_socket(server, server_side=True) + + evt = threading.Event() + remote = [None] + peer = [None] + def serve(): + server.listen(5) + # Block on the accept and wait on the connection to close. + evt.set() + remote[0], peer[0] = server.accept() + remote[0].recv(1) + + t = threading.Thread(target=serve) + t.start() + # Client wait until server setup and perform a connect. + evt.wait() + client = context.wrap_socket(socket.socket()) + client.connect((host, port)) + client_addr = client.getsockname() + client.close() + t.join() + remote[0].close() + server.close() + # Sanity checks. + self.assertIsInstance(remote[0], ssl.SSLSocket) + self.assertEqual(peer[0], client_addr) + + def test_getpeercert_enotconn(self): + context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + with closing(context.wrap_socket(socket.socket())) as sock: + with self.assertRaises(socket.error) as cm: + sock.getpeercert() + self.assertEqual(cm.exception.errno, errno.ENOTCONN) + + def test_do_handshake_enotconn(self): + context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + with closing(context.wrap_socket(socket.socket())) as sock: + with self.assertRaises(socket.error) as cm: + sock.do_handshake() + self.assertEqual(cm.exception.errno, errno.ENOTCONN) + def test_default_ciphers(self): + context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + try: + # Force a set of weak ciphers on our client context + context.set_ciphers("DES") + except ssl.SSLError: + self.skipTest("no DES cipher available") with ThreadedEchoServer(CERTFILE, ssl_version=ssl.PROTOCOL_SSLv23, chatty=False) as server: - sock = socket.socket() - try: - # Force a set of weak ciphers on our client socket - try: - s = ssl.wrap_socket(sock, - ssl_version=ssl.PROTOCOL_SSLv23, - ciphers="DES") - except ssl.SSLError: - self.skipTest("no DES cipher available") - with self.assertRaises((OSError, ssl.SSLError)): + with closing(context.wrap_socket(socket.socket())) as s: + with self.assertRaises(ssl.SSLError): s.connect((HOST, server.port)) - finally: - sock.close() self.assertIn("no shared cipher", str(server.conn_errors[0])) + @unittest.skipUnless(ssl.HAS_ECDH, "test requires ECDH-enabled OpenSSL") + def test_default_ecdh_curve(self): + # Issue #21015: elliptic curve-based Diffie Hellman key exchange + # should be enabled by default on SSL contexts. + context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + context.load_cert_chain(CERTFILE) + # Prior to OpenSSL 1.0.0, ECDH ciphers have to be enabled + # explicitly using the 'ECCdraft' cipher alias. Otherwise, + # our default cipher list should prefer ECDH-based ciphers + # automatically. + if ssl.OPENSSL_VERSION_INFO < (1, 0, 0): + context.set_ciphers("ECCdraft:ECDH") + with ThreadedEchoServer(context=context) as server: + with closing(context.wrap_socket(socket.socket())) as s: + s.connect((HOST, server.port)) + self.assertIn("ECDH", s.cipher()[0]) + + @unittest.skipUnless("tls-unique" in ssl.CHANNEL_BINDING_TYPES, + "'tls-unique' channel binding not available") + def test_tls_unique_channel_binding(self): + """Test tls-unique channel binding.""" + if support.verbose: + sys.stdout.write("\n") + + server = ThreadedEchoServer(CERTFILE, + certreqs=ssl.CERT_NONE, + ssl_version=ssl.PROTOCOL_TLSv1, + cacerts=CERTFILE, + chatty=True, + connectionchatty=False) + with server: + s = ssl.wrap_socket(socket.socket(), + server_side=False, + certfile=CERTFILE, + ca_certs=CERTFILE, + cert_reqs=ssl.CERT_NONE, + ssl_version=ssl.PROTOCOL_TLSv1) + s.connect((HOST, server.port)) + # get the data + cb_data = s.get_channel_binding("tls-unique") + if support.verbose: + sys.stdout.write(" got channel binding data: {0!r}\n" + .format(cb_data)) + + # check if it is sane + self.assertIsNotNone(cb_data) + self.assertEqual(len(cb_data), 12) # True for TLSv1 + + # and compare with the peers version + s.write(b"CB tls-unique\n") + peer_data_repr = s.read().strip() + self.assertEqual(peer_data_repr, + repr(cb_data).encode("us-ascii")) + s.close() + + # now, again + s = ssl.wrap_socket(socket.socket(), + server_side=False, + certfile=CERTFILE, + ca_certs=CERTFILE, + cert_reqs=ssl.CERT_NONE, + ssl_version=ssl.PROTOCOL_TLSv1) + s.connect((HOST, server.port)) + new_cb_data = s.get_channel_binding("tls-unique") + if support.verbose: + sys.stdout.write(" got another channel binding data: {0!r}\n" + .format(new_cb_data)) + # is it really unique + self.assertNotEqual(cb_data, new_cb_data) + self.assertIsNotNone(cb_data) + self.assertEqual(len(cb_data), 12) # True for TLSv1 + s.write(b"CB tls-unique\n") + peer_data_repr = s.read().strip() + self.assertEqual(peer_data_repr, + repr(new_cb_data).encode("us-ascii")) + s.close() + + def test_compression(self): + context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + context.load_cert_chain(CERTFILE) + stats = server_params_test(context, context, + chatty=True, connectionchatty=True) + if support.verbose: + sys.stdout.write(" got compression: {!r}\n".format(stats['compression'])) + self.assertIn(stats['compression'], { None, 'ZLIB', 'RLE' }) + + @unittest.skipUnless(hasattr(ssl, 'OP_NO_COMPRESSION'), + "ssl.OP_NO_COMPRESSION needed for this test") + def test_compression_disabled(self): + context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + context.load_cert_chain(CERTFILE) + context.options |= ssl.OP_NO_COMPRESSION + stats = server_params_test(context, context, + chatty=True, connectionchatty=True) + self.assertIs(stats['compression'], None) + + def test_dh_params(self): + # Check we can get a connection with ephemeral Diffie-Hellman + context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + context.load_cert_chain(CERTFILE) + context.load_dh_params(DHFILE) + context.set_ciphers("kEDH") + stats = server_params_test(context, context, + chatty=True, connectionchatty=True) + cipher = stats["cipher"][0] + parts = cipher.split("-") + if "ADH" not in parts and "EDH" not in parts and "DHE" not in parts: + self.fail("Non-DH cipher: " + cipher[0]) + + def test_selected_npn_protocol(self): + # selected_npn_protocol() is None unless NPN is used + context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + context.load_cert_chain(CERTFILE) + stats = server_params_test(context, context, + chatty=True, connectionchatty=True) + self.assertIs(stats['client_npn_protocol'], None) + + @unittest.skipUnless(ssl.HAS_NPN, "NPN support needed for this test") + def test_npn_protocols(self): + server_protocols = ['http/1.1', 'spdy/2'] + protocol_tests = [ + (['http/1.1', 'spdy/2'], 'http/1.1'), + (['spdy/2', 'http/1.1'], 'http/1.1'), + (['spdy/2', 'test'], 'spdy/2'), + (['abc', 'def'], 'abc') + ] + for client_protocols, expected in protocol_tests: + server_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + server_context.load_cert_chain(CERTFILE) + server_context.set_npn_protocols(server_protocols) + client_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + client_context.load_cert_chain(CERTFILE) + client_context.set_npn_protocols(client_protocols) + stats = server_params_test(client_context, server_context, + chatty=True, connectionchatty=True) + + msg = "failed trying %s (s) and %s (c).\n" \ + "was expecting %s, but got %%s from the %%s" \ + % (str(server_protocols), str(client_protocols), + str(expected)) + client_result = stats['client_npn_protocol'] + self.assertEqual(client_result, expected, msg % (client_result, "client")) + server_result = stats['server_npn_protocols'][-1] \ + if len(stats['server_npn_protocols']) else 'nothing' + self.assertEqual(server_result, expected, msg % (server_result, "server")) + + def sni_contexts(self): + server_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + server_context.load_cert_chain(SIGNED_CERTFILE) + other_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + other_context.load_cert_chain(SIGNED_CERTFILE2) + client_context = ssl.SSLContext(ssl.PROTOCOL_TLSv1) + client_context.verify_mode = ssl.CERT_REQUIRED + client_context.load_verify_locations(SIGNING_CA) + return server_context, other_context, client_context + + def check_common_name(self, stats, name): + cert = stats['peercert'] + self.assertIn((('commonName', name),), cert['subject']) + + @needs_sni + def test_sni_callback(self): + calls = [] + server_context, other_context, client_context = self.sni_contexts() + + def servername_cb(ssl_sock, server_name, initial_context): + calls.append((server_name, initial_context)) + if server_name is not None: + ssl_sock.context = other_context + server_context.set_servername_callback(servername_cb) + + stats = server_params_test(client_context, server_context, + chatty=True, + sni_name='supermessage') + # The hostname was fetched properly, and the certificate was + # changed for the connection. + self.assertEqual(calls, [("supermessage", server_context)]) + # CERTFILE4 was selected + self.check_common_name(stats, 'fakehostname') + + calls = [] + # The callback is called with server_name=None + stats = server_params_test(client_context, server_context, + chatty=True, + sni_name=None) + self.assertEqual(calls, [(None, server_context)]) + self.check_common_name(stats, 'localhost') + + # Check disabling the callback + calls = [] + server_context.set_servername_callback(None) + + stats = server_params_test(client_context, server_context, + chatty=True, + sni_name='notfunny') + # Certificate didn't change + self.check_common_name(stats, 'localhost') + self.assertEqual(calls, []) + + @needs_sni + def test_sni_callback_alert(self): + # Returning a TLS alert is reflected to the connecting client + server_context, other_context, client_context = self.sni_contexts() + + def cb_returning_alert(ssl_sock, server_name, initial_context): + return ssl.ALERT_DESCRIPTION_ACCESS_DENIED + server_context.set_servername_callback(cb_returning_alert) + + with self.assertRaises(ssl.SSLError) as cm: + stats = server_params_test(client_context, server_context, + chatty=False, + sni_name='supermessage') + self.assertEqual(cm.exception.reason, 'TLSV1_ALERT_ACCESS_DENIED') + + @needs_sni + def test_sni_callback_raising(self): + # Raising fails the connection with a TLS handshake failure alert. + server_context, other_context, client_context = self.sni_contexts() + + def cb_raising(ssl_sock, server_name, initial_context): + 1/0 + server_context.set_servername_callback(cb_raising) + + with self.assertRaises(ssl.SSLError) as cm, \ + support.captured_stderr() as stderr: + stats = server_params_test(client_context, server_context, + chatty=False, + sni_name='supermessage') + self.assertEqual(cm.exception.reason, 'SSLV3_ALERT_HANDSHAKE_FAILURE') + self.assertIn("ZeroDivisionError", stderr.getvalue()) + + @needs_sni + def test_sni_callback_wrong_return_type(self): + # Returning the wrong return type terminates the TLS connection + # with an internal error alert. + server_context, other_context, client_context = self.sni_contexts() + + def cb_wrong_return_type(ssl_sock, server_name, initial_context): + return "foo" + server_context.set_servername_callback(cb_wrong_return_type) + + with self.assertRaises(ssl.SSLError) as cm, \ + support.captured_stderr() as stderr: + stats = server_params_test(client_context, server_context, + chatty=False, + sni_name='supermessage') + self.assertEqual(cm.exception.reason, 'TLSV1_ALERT_INTERNAL_ERROR') + self.assertIn("TypeError", stderr.getvalue()) + + def test_read_write_after_close_raises_valuerror(self): + context = ssl.SSLContext(ssl.PROTOCOL_SSLv23) + context.verify_mode = ssl.CERT_REQUIRED + context.load_verify_locations(CERTFILE) + context.load_cert_chain(CERTFILE) + server = ThreadedEchoServer(context=context, chatty=False) + + with server: + s = context.wrap_socket(socket.socket()) + s.connect((HOST, server.port)) + s.close() + + self.assertRaises(ValueError, s.read, 1024) + self.assertRaises(ValueError, s.write, b'hello') + def test_main(verbose=False): - global CERTFILE, SVN_PYTHON_ORG_ROOT_CERT, NOKIACERT, NULLBYTECERT - CERTFILE = os.path.join(os.path.dirname(__file__) or os.curdir, - "keycert.pem") - SVN_PYTHON_ORG_ROOT_CERT = os.path.join( - os.path.dirname(__file__) or os.curdir, - "https_svn_python_org_root.pem") - NOKIACERT = os.path.join(os.path.dirname(__file__) or os.curdir, - "nokia.pem") - NULLBYTECERT = os.path.join(os.path.dirname(__file__) or os.curdir, - "nullbytecert.pem") - - if (not os.path.exists(CERTFILE) or - not os.path.exists(SVN_PYTHON_ORG_ROOT_CERT) or - not os.path.exists(NOKIACERT) or - not os.path.exists(NULLBYTECERT)): - raise test_support.TestFailed("Can't read certificate files!") - - tests = [BasicTests, BasicSocketTests] - - if test_support.is_resource_enabled('network'): + if support.verbose: + plats = { + 'Linux': platform.linux_distribution, + 'Mac': platform.mac_ver, + 'Windows': platform.win32_ver, + } + for name, func in plats.items(): + plat = func() + if plat and plat[0]: + plat = '%s %r' % (name, plat) + break + else: + plat = repr(platform.platform()) + print("test_ssl: testing with %r %r" % + (ssl.OPENSSL_VERSION, ssl.OPENSSL_VERSION_INFO)) + print(" under %s" % plat) + print(" HAS_SNI = %r" % ssl.HAS_SNI) + print(" OP_ALL = 0x%8x" % ssl.OP_ALL) + try: + print(" OP_NO_TLSv1_1 = 0x%8x" % ssl.OP_NO_TLSv1_1) + except AttributeError: + pass + + for filename in [ + CERTFILE, SVN_PYTHON_ORG_ROOT_CERT, BYTES_CERTFILE, + ONLYCERT, ONLYKEY, BYTES_ONLYCERT, BYTES_ONLYKEY, + SIGNED_CERTFILE, SIGNED_CERTFILE2, SIGNING_CA, + BADCERT, BADKEY, EMPTYCERT]: + if not os.path.exists(filename): + raise support.TestFailed("Can't read certificate file %r" % filename) + + tests = [ContextTests, BasicSocketTests, SSLErrorTests] + + if support.is_resource_enabled('network'): tests.append(NetworkedTests) if _have_threads: - thread_info = test_support.threading_setup() - if thread_info and test_support.is_resource_enabled('network'): + thread_info = support.threading_setup() + if thread_info: tests.append(ThreadedTests) try: - test_support.run_unittest(*tests) + support.run_unittest(*tests) finally: if _have_threads: - test_support.threading_cleanup(*thread_info) + support.threading_cleanup(*thread_info) if __name__ == "__main__": test_main() diff --git a/Lib/test/test_support.py b/Lib/test/test_support.py index 695ac95..c1c8799 100644 --- a/Lib/test/test_support.py +++ b/Lib/test/test_support.py @@ -39,7 +39,7 @@ __all__ = ["Error", "TestFailed", "ResourceDenied", "import_module", "threading_cleanup", "reap_children", "cpython_only", "check_impl_detail", "get_attribute", "py3k_bytes", "import_fresh_module", "threading_cleanup", "reap_children", - "strip_python_stderr"] + "strip_python_stderr", "IPV6_ENABLED"] class Error(Exception): """Base class for regression test exceptions.""" @@ -465,6 +465,23 @@ def bind_port(sock, host=HOST): port = sock.getsockname()[1] return port +def _is_ipv6_enabled(): + """Check whether IPv6 is enabled on this host.""" + if socket.has_ipv6: + sock = None + try: + sock = socket.socket(socket.AF_INET6, socket.SOCK_STREAM) + sock.bind((HOSTv6, 0)) + return True + except OSError: + pass + finally: + if sock: + sock.close() + return False + +IPV6_ENABLED = _is_ipv6_enabled() + FUZZ = 1e-6 def fcmp(x, y): # fuzzy comparison function diff --git a/Makefile.pre.in b/Makefile.pre.in index bcd83bf..80a0926 100644 --- a/Makefile.pre.in +++ b/Makefile.pre.in @@ -930,8 +930,8 @@ PLATMACDIRS= plat-mac plat-mac/Carbon plat-mac/lib-scriptpackages \ plat-mac/lib-scriptpackages/Terminal PLATMACPATH=:plat-mac:plat-mac/lib-scriptpackages LIBSUBDIRS= lib-tk lib-tk/test lib-tk/test/test_tkinter \ - lib-tk/test/test_ttk site-packages test test/audiodata test/data \ - test/cjkencodings test/decimaltestdata test/xmltestdata \ + lib-tk/test/test_ttk site-packages test test/audiodata test/capath \ + test/data test/cjkencodings test/decimaltestdata test/xmltestdata \ test/imghdrdata \ test/subprocessdata \ test/tracedmodules \ diff --git a/Modules/_ssl.c b/Modules/_ssl.c index 752b033..8f4062b 100644 --- a/Modules/_ssl.c +++ b/Modules/_ssl.c @@ -14,22 +14,28 @@ http://bugs.python.org/issue8108#msg102867 ? */ +#define PY_SSIZE_T_CLEAN #include "Python.h" #ifdef WITH_THREAD #include "pythread.h" +#define PySSL_BEGIN_ALLOW_THREADS_S(save) \ + do { if (_ssl_locks_count>0) { (save) = PyEval_SaveThread(); } } while (0) +#define PySSL_END_ALLOW_THREADS_S(save) \ + do { if (_ssl_locks_count>0) { PyEval_RestoreThread(save); } } while (0) #define PySSL_BEGIN_ALLOW_THREADS { \ PyThreadState *_save = NULL; \ - if (_ssl_locks_count>0) {_save = PyEval_SaveThread();} -#define PySSL_BLOCK_THREADS if (_ssl_locks_count>0){PyEval_RestoreThread(_save)}; -#define PySSL_UNBLOCK_THREADS if (_ssl_locks_count>0){_save = PyEval_SaveThread()}; -#define PySSL_END_ALLOW_THREADS if (_ssl_locks_count>0){PyEval_RestoreThread(_save);} \ - } + PySSL_BEGIN_ALLOW_THREADS_S(_save); +#define PySSL_BLOCK_THREADS PySSL_END_ALLOW_THREADS_S(_save); +#define PySSL_UNBLOCK_THREADS PySSL_BEGIN_ALLOW_THREADS_S(_save); +#define PySSL_END_ALLOW_THREADS PySSL_END_ALLOW_THREADS_S(_save); } #else /* no WITH_THREAD */ +#define PySSL_BEGIN_ALLOW_THREADS_S(save) +#define PySSL_END_ALLOW_THREADS_S(save) #define PySSL_BEGIN_ALLOW_THREADS #define PySSL_BLOCK_THREADS #define PySSL_UNBLOCK_THREADS @@ -37,6 +43,68 @@ #endif +/* Include symbols from _socket module */ +#include "socketmodule.h" + +#if defined(HAVE_POLL_H) +#include +#elif defined(HAVE_SYS_POLL_H) +#include +#endif + +/* Include OpenSSL header files */ +#include "openssl/rsa.h" +#include "openssl/crypto.h" +#include "openssl/x509.h" +#include "openssl/x509v3.h" +#include "openssl/pem.h" +#include "openssl/ssl.h" +#include "openssl/err.h" +#include "openssl/rand.h" + +/* SSL error object */ +static PyObject *PySSLErrorObject; +static PyObject *PySSLZeroReturnErrorObject; +static PyObject *PySSLWantReadErrorObject; +static PyObject *PySSLWantWriteErrorObject; +static PyObject *PySSLSyscallErrorObject; +static PyObject *PySSLEOFErrorObject; + +/* Error mappings */ +static PyObject *err_codes_to_names; +static PyObject *err_names_to_codes; +static PyObject *lib_codes_to_names; + +struct py_ssl_error_code { + const char *mnemonic; + int library, reason; +}; +struct py_ssl_library_code { + const char *library; + int code; +}; + +/* Include generated data (error codes) */ +#include "_ssl_data.h" + +/* Openssl comes with TLSv1.1 and TLSv1.2 between 1.0.0h and 1.0.1 + http://www.openssl.org/news/changelog.html + */ +#if OPENSSL_VERSION_NUMBER >= 0x10001000L +# define HAVE_TLSv1_2 1 +#else +# define HAVE_TLSv1_2 0 +#endif + +/* SNI support (client- and server-side) appeared in OpenSSL 1.0.0 and 0.9.8f + * This includes the SSL_set_SSL_CTX() function. + */ +#ifdef SSL_CTRL_SET_TLSEXT_HOSTNAME +# define HAVE_SNI 1 +#else +# define HAVE_SNI 0 +#endif + enum py_ssl_error { /* these mirror ssl.h */ PY_SSL_ERROR_NONE, @@ -49,6 +117,7 @@ enum py_ssl_error { PY_SSL_ERROR_WANT_CONNECT, /* start of non ssl.h errorcodes */ PY_SSL_ERROR_EOF, /* special case of SSL_ERROR_SYSCALL */ + PY_SSL_ERROR_NO_SOCKET, /* socket has been GC'd */ PY_SSL_ERROR_INVALID_ERROR_CODE }; @@ -64,35 +133,17 @@ enum py_ssl_cert_requirements { }; enum py_ssl_version { -#ifndef OPENSSL_NO_SSL2 PY_SSL_VERSION_SSL2, -#endif PY_SSL_VERSION_SSL3=1, PY_SSL_VERSION_SSL23, +#if HAVE_TLSv1_2 + PY_SSL_VERSION_TLS1, + PY_SSL_VERSION_TLS1_1, + PY_SSL_VERSION_TLS1_2 +#else PY_SSL_VERSION_TLS1 -}; - -/* Include symbols from _socket module */ -#include "socketmodule.h" - -#if defined(HAVE_POLL_H) -#include -#elif defined(HAVE_SYS_POLL_H) -#include #endif - -/* Include OpenSSL header files */ -#include "openssl/rsa.h" -#include "openssl/crypto.h" -#include "openssl/x509.h" -#include "openssl/x509v3.h" -#include "openssl/pem.h" -#include "openssl/ssl.h" -#include "openssl/err.h" -#include "openssl/rand.h" - -/* SSL error object */ -static PyObject *PySSLErrorObject; +}; #ifdef WITH_THREAD @@ -114,27 +165,79 @@ static unsigned int _ssl_locks_count = 0; # undef HAVE_OPENSSL_RAND #endif +/* SSL_CTX_clear_options() and SSL_clear_options() were first added in + * OpenSSL 0.9.8m but do not appear in some 0.9.9-dev versions such the + * 0.9.9 from "May 2008" that NetBSD 5.0 uses. */ +#if OPENSSL_VERSION_NUMBER >= 0x009080dfL && OPENSSL_VERSION_NUMBER != 0x00909000L +# define HAVE_SSL_CTX_CLEAR_OPTIONS +#else +# undef HAVE_SSL_CTX_CLEAR_OPTIONS +#endif + +/* In case of 'tls-unique' it will be 12 bytes for TLS, 36 bytes for + * older SSL, but let's be safe */ +#define PySSL_CB_MAXLEN 128 + +/* SSL_get_finished got added to OpenSSL in 0.9.5 */ +#if OPENSSL_VERSION_NUMBER >= 0x0090500fL +# define HAVE_OPENSSL_FINISHED 1 +#else +# define HAVE_OPENSSL_FINISHED 0 +#endif + +/* ECDH support got added to OpenSSL in 0.9.8 */ +#if OPENSSL_VERSION_NUMBER < 0x0090800fL && !defined(OPENSSL_NO_ECDH) +# define OPENSSL_NO_ECDH +#endif + +/* compression support got added to OpenSSL in 0.9.8 */ +#if OPENSSL_VERSION_NUMBER < 0x0090800fL && !defined(OPENSSL_NO_COMP) +# define OPENSSL_NO_COMP +#endif + +/* X509_VERIFY_PARAM got added to OpenSSL in 0.9.8 */ +#if OPENSSL_VERSION_NUMBER >= 0x0090800fL +# define HAVE_OPENSSL_VERIFY_PARAM +#endif + + +typedef struct { + PyObject_HEAD + SSL_CTX *ctx; +#ifdef OPENSSL_NPN_NEGOTIATED + char *npn_protocols; + int npn_protocols_len; +#endif +#ifndef OPENSSL_NO_TLSEXT + PyObject *set_hostname; +#endif + int check_hostname; +} PySSLContext; + typedef struct { PyObject_HEAD - PySocketSockObject *Socket; /* Socket on which we're layered */ - SSL_CTX* ctx; - SSL* ssl; - X509* peer_cert; - char server[X509_NAME_MAXLEN]; - char issuer[X509_NAME_MAXLEN]; - int shutdown_seen_zero; - -} PySSLObject; - -static PyTypeObject PySSL_Type; -static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args); -static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args); + PySocketSockObject *Socket; + PyObject *ssl_sock; + SSL *ssl; + PySSLContext *ctx; /* weakref to SSL context */ + X509 *peer_cert; + char shutdown_seen_zero; + char handshake_done; + enum py_ssl_server_or_client socket_type; +} PySSLSocket; + +static PyTypeObject PySSLContext_Type; +static PyTypeObject PySSLSocket_Type; + +static PyObject *PySSL_SSLwrite(PySSLSocket *self, PyObject *args); +static PyObject *PySSL_SSLread(PySSLSocket *self, PyObject *args); static int check_socket_and_wait_for_timeout(PySocketSockObject *s, int writing); -static PyObject *PySSL_peercert(PySSLObject *self, PyObject *args); -static PyObject *PySSL_cipher(PySSLObject *self); +static PyObject *PySSL_peercert(PySSLSocket *self, PyObject *args); +static PyObject *PySSL_cipher(PySSLSocket *self); -#define PySSLObject_Check(v) (Py_TYPE(v) == &PySSL_Type) +#define PySSLContext_Check(v) (Py_TYPE(v) == &PySSLContext_Type) +#define PySSLSocket_Check(v) (Py_TYPE(v) == &PySSLSocket_Type) typedef enum { SOCKET_IS_NONBLOCKING, @@ -151,36 +254,140 @@ typedef enum { #define ERRSTR1(x,y,z) (x ":" y ": " z) #define ERRSTR(x) ERRSTR1("_ssl.c", STRINGIFY2(__LINE__), x) -/* XXX It might be helpful to augment the error message generated - below with the name of the SSL function that generated the error. - I expect it's obvious most of the time. -*/ + +/* + * SSL errors. + */ + +PyDoc_STRVAR(SSLError_doc, +"An error occurred in the SSL implementation."); + +PyDoc_STRVAR(SSLZeroReturnError_doc, +"SSL/TLS session closed cleanly."); + +PyDoc_STRVAR(SSLWantReadError_doc, +"Non-blocking SSL socket needs to read more data\n" +"before the requested operation can be completed."); + +PyDoc_STRVAR(SSLWantWriteError_doc, +"Non-blocking SSL socket needs to write more data\n" +"before the requested operation can be completed."); + +PyDoc_STRVAR(SSLSyscallError_doc, +"System error when attempting SSL operation."); + +PyDoc_STRVAR(SSLEOFError_doc, +"SSL/TLS connection terminated abruptly."); + static PyObject * -PySSL_SetError(PySSLObject *obj, int ret, char *filename, int lineno) +SSLError_str(PyEnvironmentErrorObject *self) { - PyObject *v; - char buf[2048]; - char *errstr; + if (self->strerror != NULL) { + Py_INCREF(self->strerror); + return self->strerror; + } + else + return PyObject_Str(self->args); +} + +static void +fill_and_set_sslerror(PyObject *type, int ssl_errno, const char *errstr, + int lineno, unsigned long errcode) +{ + PyObject *err_value = NULL, *reason_obj = NULL, *lib_obj = NULL; + PyObject *init_value, *msg, *key; + + if (errcode != 0) { + int lib, reason; + + lib = ERR_GET_LIB(errcode); + reason = ERR_GET_REASON(errcode); + key = Py_BuildValue("ii", lib, reason); + if (key == NULL) + goto fail; + reason_obj = PyDict_GetItem(err_codes_to_names, key); + Py_DECREF(key); + if (reason_obj == NULL) { + /* XXX if reason < 100, it might reflect a library number (!!) */ + PyErr_Clear(); + } + key = PyLong_FromLong(lib); + if (key == NULL) + goto fail; + lib_obj = PyDict_GetItem(lib_codes_to_names, key); + Py_DECREF(key); + if (lib_obj == NULL) { + PyErr_Clear(); + } + if (errstr == NULL) + errstr = ERR_reason_error_string(errcode); + } + if (errstr == NULL) + errstr = "unknown error"; + + if (reason_obj && lib_obj) + msg = PyUnicode_FromFormat("[%S: %S] %s (_ssl.c:%d)", + lib_obj, reason_obj, errstr, lineno); + else if (lib_obj) + msg = PyUnicode_FromFormat("[%S] %s (_ssl.c:%d)", + lib_obj, errstr, lineno); + else + msg = PyUnicode_FromFormat("%s (_ssl.c:%d)", errstr, lineno); + if (msg == NULL) + goto fail; + + init_value = Py_BuildValue("iN", ssl_errno, msg); + if (init_value == NULL) + goto fail; + + err_value = PyObject_CallObject(type, init_value); + Py_DECREF(init_value); + if (err_value == NULL) + goto fail; + + if (reason_obj == NULL) + reason_obj = Py_None; + if (PyObject_SetAttrString(err_value, "reason", reason_obj)) + goto fail; + if (lib_obj == NULL) + lib_obj = Py_None; + if (PyObject_SetAttrString(err_value, "library", lib_obj)) + goto fail; + PyErr_SetObject(type, err_value); +fail: + Py_XDECREF(err_value); +} + +static PyObject * +PySSL_SetError(PySSLSocket *obj, int ret, char *filename, int lineno) +{ + PyObject *type = PySSLErrorObject; + char *errstr = NULL; int err; enum py_ssl_error p = PY_SSL_ERROR_NONE; + unsigned long e = 0; assert(ret <= 0); + e = ERR_peek_last_error(); if (obj->ssl != NULL) { err = SSL_get_error(obj->ssl, ret); switch (err) { case SSL_ERROR_ZERO_RETURN: - errstr = "TLS/SSL connection has been closed"; + errstr = "TLS/SSL connection has been closed (EOF)"; + type = PySSLZeroReturnErrorObject; p = PY_SSL_ERROR_ZERO_RETURN; break; case SSL_ERROR_WANT_READ: errstr = "The operation did not complete (read)"; + type = PySSLWantReadErrorObject; p = PY_SSL_ERROR_WANT_READ; break; case SSL_ERROR_WANT_WRITE: p = PY_SSL_ERROR_WANT_WRITE; + type = PySSLWantWriteErrorObject; errstr = "The operation did not complete (write)"; break; case SSL_ERROR_WANT_X509_LOOKUP: @@ -193,213 +400,109 @@ PySSL_SetError(PySSLObject *obj, int ret, char *filename, int lineno) break; case SSL_ERROR_SYSCALL: { - unsigned long e = ERR_get_error(); if (e == 0) { - if (ret == 0 || !obj->Socket) { + PySocketSockObject *s = obj->Socket; + if (ret == 0) { p = PY_SSL_ERROR_EOF; + type = PySSLEOFErrorObject; errstr = "EOF occurred in violation of protocol"; } else if (ret == -1) { /* underlying BIO reported an I/O error */ + Py_INCREF(s); ERR_clear_error(); - return obj->Socket->errorhandler(); + s->errorhandler(); + Py_DECREF(s); + return NULL; } else { /* possible? */ p = PY_SSL_ERROR_SYSCALL; + type = PySSLSyscallErrorObject; errstr = "Some I/O error occurred"; } } else { p = PY_SSL_ERROR_SYSCALL; - /* XXX Protected by global interpreter lock */ - errstr = ERR_error_string(e, NULL); } break; } case SSL_ERROR_SSL: { - unsigned long e = ERR_get_error(); p = PY_SSL_ERROR_SSL; - if (e != 0) - /* XXX Protected by global interpreter lock */ - errstr = ERR_error_string(e, NULL); - else { /* possible? */ + if (e == 0) + /* possible? */ errstr = "A failure in the SSL library occurred"; - } break; } default: p = PY_SSL_ERROR_INVALID_ERROR_CODE; errstr = "Invalid error code"; } - } else { - errstr = ERR_error_string(ERR_peek_last_error(), NULL); } - PyOS_snprintf(buf, sizeof(buf), "_ssl.c:%d: %s", lineno, errstr); + fill_and_set_sslerror(type, p, errstr, lineno, e); ERR_clear_error(); - v = Py_BuildValue("(is)", p, buf); - if (v != NULL) { - PyErr_SetObject(PySSLErrorObject, v); - Py_DECREF(v); - } return NULL; } static PyObject * _setSSLError (char *errstr, int errcode, char *filename, int lineno) { - char buf[2048]; - PyObject *v; - - if (errstr == NULL) { + if (errstr == NULL) errcode = ERR_peek_last_error(); - errstr = ERR_error_string(errcode, NULL); - } - PyOS_snprintf(buf, sizeof(buf), "_ssl.c:%d: %s", lineno, errstr); + else + errcode = 0; + fill_and_set_sslerror(PySSLErrorObject, errcode, errstr, lineno, errcode); ERR_clear_error(); - v = Py_BuildValue("(is)", errcode, buf); - if (v != NULL) { - PyErr_SetObject(PySSLErrorObject, v); - Py_DECREF(v); - } return NULL; } -static PySSLObject * -newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file, +/* + * SSL objects + */ + +static PySSLSocket * +newPySSLSocket(PySSLContext *sslctx, PySocketSockObject *sock, enum py_ssl_server_or_client socket_type, - enum py_ssl_cert_requirements certreq, - enum py_ssl_version proto_version, - char *cacerts_file, char *ciphers) + char *server_hostname, PyObject *ssl_sock) { - PySSLObject *self; - char *errstr = NULL; - int ret; - int verification_mode; - long options; + PySSLSocket *self; + SSL_CTX *ctx = sslctx->ctx; + long mode; - self = PyObject_New(PySSLObject, &PySSL_Type); /* Create new object */ + self = PyObject_New(PySSLSocket, &PySSLSocket_Type); if (self == NULL) return NULL; - memset(self->server, '\0', sizeof(char) * X509_NAME_MAXLEN); - memset(self->issuer, '\0', sizeof(char) * X509_NAME_MAXLEN); + self->peer_cert = NULL; self->ssl = NULL; - self->ctx = NULL; self->Socket = NULL; + self->ssl_sock = NULL; + self->ctx = sslctx; self->shutdown_seen_zero = 0; + self->handshake_done = 0; + Py_INCREF(sslctx); /* Make sure the SSL error state is initialized */ (void) ERR_get_state(); ERR_clear_error(); - if ((key_file && !cert_file) || (!key_file && cert_file)) { - errstr = ERRSTR("Both the key & certificate files " - "must be specified"); - goto fail; - } - - if ((socket_type == PY_SSL_SERVER) && - ((key_file == NULL) || (cert_file == NULL))) { - errstr = ERRSTR("Both the key & certificate files " - "must be specified for server-side operation"); - goto fail; - } - - PySSL_BEGIN_ALLOW_THREADS - if (proto_version == PY_SSL_VERSION_TLS1) - self->ctx = SSL_CTX_new(TLSv1_method()); /* Set up context */ - else if (proto_version == PY_SSL_VERSION_SSL3) - self->ctx = SSL_CTX_new(SSLv3_method()); /* Set up context */ -#ifndef OPENSSL_NO_SSL2 - else if (proto_version == PY_SSL_VERSION_SSL2) - self->ctx = SSL_CTX_new(SSLv2_method()); /* Set up context */ -#endif - else if (proto_version == PY_SSL_VERSION_SSL23) - self->ctx = SSL_CTX_new(SSLv23_method()); /* Set up context */ - PySSL_END_ALLOW_THREADS - - if (self->ctx == NULL) { - errstr = ERRSTR("Invalid SSL protocol variant specified."); - goto fail; - } - - if (ciphers != NULL) { - ret = SSL_CTX_set_cipher_list(self->ctx, ciphers); - if (ret == 0) { - errstr = ERRSTR("No cipher can be selected."); - goto fail; - } - } - - if (certreq != PY_SSL_CERT_NONE) { - if (cacerts_file == NULL) { - errstr = ERRSTR("No root certificates specified for " - "verification of other-side certificates."); - goto fail; - } else { - PySSL_BEGIN_ALLOW_THREADS - ret = SSL_CTX_load_verify_locations(self->ctx, - cacerts_file, - NULL); - PySSL_END_ALLOW_THREADS - if (ret != 1) { - _setSSLError(NULL, 0, __FILE__, __LINE__); - goto fail; - } - } - } - if (key_file) { - PySSL_BEGIN_ALLOW_THREADS - ret = SSL_CTX_use_PrivateKey_file(self->ctx, key_file, - SSL_FILETYPE_PEM); - PySSL_END_ALLOW_THREADS - if (ret != 1) { - _setSSLError(NULL, ret, __FILE__, __LINE__); - goto fail; - } - - PySSL_BEGIN_ALLOW_THREADS - ret = SSL_CTX_use_certificate_chain_file(self->ctx, - cert_file); - PySSL_END_ALLOW_THREADS - if (ret != 1) { - /* - fprintf(stderr, "ret is %d, errcode is %lu, %lu, with file \"%s\"\n", - ret, ERR_peek_error(), ERR_peek_last_error(), cert_file); - */ - if (ERR_peek_last_error() != 0) { - _setSSLError(NULL, ret, __FILE__, __LINE__); - goto fail; - } - } - } - - /* ssl compatibility */ - options = SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; - if (proto_version != PY_SSL_VERSION_SSL2) - options |= SSL_OP_NO_SSLv2; - SSL_CTX_set_options(self->ctx, options); - - verification_mode = SSL_VERIFY_NONE; - if (certreq == PY_SSL_CERT_OPTIONAL) - verification_mode = SSL_VERIFY_PEER; - else if (certreq == PY_SSL_CERT_REQUIRED) - verification_mode = (SSL_VERIFY_PEER | - SSL_VERIFY_FAIL_IF_NO_PEER_CERT); - SSL_CTX_set_verify(self->ctx, verification_mode, - NULL); /* set verify lvl */ - PySSL_BEGIN_ALLOW_THREADS - self->ssl = SSL_new(self->ctx); /* New ssl struct */ + self->ssl = SSL_new(ctx); PySSL_END_ALLOW_THREADS - SSL_set_fd(self->ssl, Sock->sock_fd); /* Set the socket for SSL */ + SSL_set_app_data(self->ssl,self); + SSL_set_fd(self->ssl, Py_SAFE_DOWNCAST(sock->sock_fd, SOCKET_T, int)); + mode = SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER; #ifdef SSL_MODE_AUTO_RETRY - SSL_set_mode(self->ssl, SSL_MODE_AUTO_RETRY); + mode |= SSL_MODE_AUTO_RETRY; +#endif + SSL_set_mode(self->ssl, mode); + +#if HAVE_SNI + if (server_hostname != NULL) + SSL_set_tlsext_host_name(self->ssl, server_hostname); #endif /* If the socket is in non-blocking mode or timeout mode, set the BIO * to non-blocking mode (blocking is the default) */ - if (Sock->sock_timeout >= 0.0) { - /* Set both the read and write BIO's to non-blocking mode */ + if (sock->sock_timeout >= 0.0) { BIO_set_nbio(SSL_get_rbio(self->ssl), 1); BIO_set_nbio(SSL_get_wbio(self->ssl), 1); } @@ -411,65 +514,31 @@ newPySSLObject(PySocketSockObject *Sock, char *key_file, char *cert_file, SSL_set_accept_state(self->ssl); PySSL_END_ALLOW_THREADS - self->Socket = Sock; + self->socket_type = socket_type; + self->Socket = sock; Py_INCREF(self->Socket); - return self; - fail: - if (errstr) - PyErr_SetString(PySSLErrorObject, errstr); - Py_DECREF(self); - return NULL; -} - -static PyObject * -PySSL_sslwrap(PyObject *self, PyObject *args) -{ - PySocketSockObject *Sock; - int server_side = 0; - int verification_mode = PY_SSL_CERT_NONE; - int protocol = PY_SSL_VERSION_SSL23; - char *key_file = NULL; - char *cert_file = NULL; - char *cacerts_file = NULL; - char *ciphers = NULL; - - if (!PyArg_ParseTuple(args, "O!i|zziizz:sslwrap", - PySocketModule.Sock_Type, - &Sock, - &server_side, - &key_file, &cert_file, - &verification_mode, &protocol, - &cacerts_file, &ciphers)) + self->ssl_sock = PyWeakref_NewRef(ssl_sock, NULL); + if (self->ssl_sock == NULL) { + Py_DECREF(self); return NULL; - - /* - fprintf(stderr, - "server_side is %d, keyfile %p, certfile %p, verify_mode %d, " - "protocol %d, certs %p\n", - server_side, key_file, cert_file, verification_mode, - protocol, cacerts_file); - */ - - return (PyObject *) newPySSLObject(Sock, key_file, cert_file, - server_side, verification_mode, - protocol, cacerts_file, - ciphers); + } + return self; } -PyDoc_STRVAR(ssl_doc, -"sslwrap(socket, server_side, [keyfile, certfile, certs_mode, protocol,\n" -" cacertsfile, ciphers]) -> sslobject"); /* SSL object methods */ -static PyObject *PySSL_SSLdo_handshake(PySSLObject *self) +static PyObject *PySSL_SSLdo_handshake(PySSLSocket *self) { int ret; int err; int sockstate, nonblocking; + PySocketSockObject *sock = self->Socket; + + Py_INCREF(sock); /* just in case the blocking state of the socket has been changed */ - nonblocking = (self->Socket->sock_timeout >= 0.0); + nonblocking = (sock->sock_timeout >= 0.0); BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); @@ -480,60 +549,48 @@ static PyObject *PySSL_SSLdo_handshake(PySSLObject *self) ret = SSL_do_handshake(self->ssl); err = SSL_get_error(self->ssl, ret); PySSL_END_ALLOW_THREADS - if(PyErr_CheckSignals()) { - return NULL; - } + if (PyErr_CheckSignals()) + goto error; if (err == SSL_ERROR_WANT_READ) { - sockstate = check_socket_and_wait_for_timeout(self->Socket, 0); + sockstate = check_socket_and_wait_for_timeout(sock, 0); } else if (err == SSL_ERROR_WANT_WRITE) { - sockstate = check_socket_and_wait_for_timeout(self->Socket, 1); + sockstate = check_socket_and_wait_for_timeout(sock, 1); } else { sockstate = SOCKET_OPERATION_OK; } if (sockstate == SOCKET_HAS_TIMED_OUT) { PyErr_SetString(PySSLErrorObject, ERRSTR("The handshake operation timed out")); - return NULL; + goto error; } else if (sockstate == SOCKET_HAS_BEEN_CLOSED) { PyErr_SetString(PySSLErrorObject, ERRSTR("Underlying socket has been closed.")); - return NULL; + goto error; } else if (sockstate == SOCKET_TOO_LARGE_FOR_SELECT) { PyErr_SetString(PySSLErrorObject, ERRSTR("Underlying socket too large for select().")); - return NULL; + goto error; } else if (sockstate == SOCKET_IS_NONBLOCKING) { break; } } while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); + Py_DECREF(sock); if (ret < 1) return PySSL_SetError(self, ret, __FILE__, __LINE__); if (self->peer_cert) X509_free (self->peer_cert); PySSL_BEGIN_ALLOW_THREADS - if ((self->peer_cert = SSL_get_peer_certificate(self->ssl))) { - X509_NAME_oneline(X509_get_subject_name(self->peer_cert), - self->server, X509_NAME_MAXLEN); - X509_NAME_oneline(X509_get_issuer_name(self->peer_cert), - self->issuer, X509_NAME_MAXLEN); - } + self->peer_cert = SSL_get_peer_certificate(self->ssl); PySSL_END_ALLOW_THREADS + self->handshake_done = 1; Py_INCREF(Py_None); return Py_None; -} - -static PyObject * -PySSL_server(PySSLObject *self) -{ - return PyString_FromString(self->server); -} -static PyObject * -PySSL_issuer(PySSLObject *self) -{ - return PyString_FromString(self->issuer); +error: + Py_DECREF(sock); + return NULL; } static PyObject * @@ -639,8 +696,8 @@ _create_tuple_for_X509_NAME (X509_NAME *xname) /* fprintf(stderr, "RDN level %d, attribute %s: %s\n", entry->set, - PyString_AS_STRING(PyTuple_GET_ITEM(attr, 0)), - PyString_AS_STRING(PyTuple_GET_ITEM(attr, 1))); + PyBytes_AS_STRING(PyTuple_GET_ITEM(attr, 0)), + PyBytes_AS_STRING(PyTuple_GET_ITEM(attr, 1))); */ if (attr == NULL) goto fail1; @@ -727,21 +784,24 @@ _get_peer_alt_names (X509 *certificate) { /* now decode the altName */ ext = X509_get_ext(certificate, i); if(!(method = X509V3_EXT_get(ext))) { - PyErr_SetString(PySSLErrorObject, - ERRSTR("No method for internalizing subjectAltName!")); + PyErr_SetString + (PySSLErrorObject, + ERRSTR("No method for internalizing subjectAltName!")); goto fail; } p = ext->value->data; if (method->it) - names = (GENERAL_NAMES*) (ASN1_item_d2i(NULL, - &p, - ext->value->length, - ASN1_ITEM_ptr(method->it))); + names = (GENERAL_NAMES*) + (ASN1_item_d2i(NULL, + &p, + ext->value->length, + ASN1_ITEM_ptr(method->it))); else - names = (GENERAL_NAMES*) (method->d2i(NULL, - &p, - ext->value->length)); + names = (GENERAL_NAMES*) + (method->d2i(NULL, + &p, + ext->value->length)); for(j = 0; j < sk_GENERAL_NAME_num(names); j++) { /* get a rendering of each name in the set of names */ @@ -888,94 +948,212 @@ _get_peer_alt_names (X509 *certificate) { } static PyObject * -_decode_certificate (X509 *certificate, int verbose) { - - PyObject *retval = NULL; - BIO *biobuf = NULL; - PyObject *peer; - PyObject *peer_alt_names = NULL; - PyObject *issuer; - PyObject *version; - PyObject *sn_obj; - ASN1_INTEGER *serialNumber; - char buf[2048]; - int len; - ASN1_TIME *notBefore, *notAfter; - PyObject *pnotBefore, *pnotAfter; - - retval = PyDict_New(); - if (retval == NULL) - return NULL; +_get_aia_uri(X509 *certificate, int nid) { + PyObject *lst = NULL, *ostr = NULL; + int i, result; + AUTHORITY_INFO_ACCESS *info; + + info = X509_get_ext_d2i(certificate, NID_info_access, NULL, NULL); + if ((info == NULL) || (sk_ACCESS_DESCRIPTION_num(info) == 0)) { + return Py_None; + } - peer = _create_tuple_for_X509_NAME( - X509_get_subject_name(certificate)); - if (peer == NULL) - goto fail0; - if (PyDict_SetItemString(retval, (const char *) "subject", peer) < 0) { - Py_DECREF(peer); - goto fail0; + if ((lst = PyList_New(0)) == NULL) { + goto fail; } - Py_DECREF(peer); - if (verbose) { - issuer = _create_tuple_for_X509_NAME( - X509_get_issuer_name(certificate)); - if (issuer == NULL) - goto fail0; - if (PyDict_SetItemString(retval, (const char *)"issuer", issuer) < 0) { - Py_DECREF(issuer); - goto fail0; - } - Py_DECREF(issuer); + for (i = 0; i < sk_ACCESS_DESCRIPTION_num(info); i++) { + ACCESS_DESCRIPTION *ad = sk_ACCESS_DESCRIPTION_value(info, i); + ASN1_IA5STRING *uri; - version = PyInt_FromLong(X509_get_version(certificate) + 1); - if (PyDict_SetItemString(retval, "version", version) < 0) { - Py_DECREF(version); - goto fail0; + if ((OBJ_obj2nid(ad->method) != nid) || + (ad->location->type != GEN_URI)) { + continue; + } + uri = ad->location->d.uniformResourceIdentifier; + ostr = PyUnicode_FromStringAndSize((char *)uri->data, + uri->length); + if (ostr == NULL) { + goto fail; + } + result = PyList_Append(lst, ostr); + Py_DECREF(ostr); + if (result < 0) { + goto fail; } - Py_DECREF(version); } + AUTHORITY_INFO_ACCESS_free(info); - /* get a memory buffer */ - biobuf = BIO_new(BIO_s_mem()); - - if (verbose) { + /* convert to tuple or None */ + if (PyList_Size(lst) == 0) { + Py_DECREF(lst); + return Py_None; + } else { + PyObject *tup; + tup = PyList_AsTuple(lst); + Py_DECREF(lst); + return tup; + } - (void) BIO_reset(biobuf); - serialNumber = X509_get_serialNumber(certificate); - /* should not exceed 20 octets, 160 bits, so buf is big enough */ - i2a_ASN1_INTEGER(biobuf, serialNumber); - len = BIO_gets(biobuf, buf, sizeof(buf)-1); - if (len < 0) { - _setSSLError(NULL, 0, __FILE__, __LINE__); - goto fail1; - } - sn_obj = PyString_FromStringAndSize(buf, len); - if (sn_obj == NULL) - goto fail1; - if (PyDict_SetItemString(retval, "serialNumber", sn_obj) < 0) { - Py_DECREF(sn_obj); - goto fail1; + fail: + AUTHORITY_INFO_ACCESS_free(info); + Py_XDECREF(lst); + return NULL; +} + +static PyObject * +_get_crl_dp(X509 *certificate) { + STACK_OF(DIST_POINT) *dps; + int i, j, result; + PyObject *lst; + +#if OPENSSL_VERSION_NUMBER < 0x10001000L + dps = X509_get_ext_d2i(certificate, NID_crl_distribution_points, + NULL, NULL); +#else + /* Calls x509v3_cache_extensions and sets up crldp */ + X509_check_ca(certificate); + dps = certificate->crldp; +#endif + + if (dps == NULL) { + return Py_None; + } + + if ((lst = PyList_New(0)) == NULL) { + return NULL; + } + + for (i=0; i < sk_DIST_POINT_num(dps); i++) { + DIST_POINT *dp; + STACK_OF(GENERAL_NAME) *gns; + + dp = sk_DIST_POINT_value(dps, i); + gns = dp->distpoint->name.fullname; + + for (j=0; j < sk_GENERAL_NAME_num(gns); j++) { + GENERAL_NAME *gn; + ASN1_IA5STRING *uri; + PyObject *ouri; + + gn = sk_GENERAL_NAME_value(gns, j); + if (gn->type != GEN_URI) { + continue; + } + uri = gn->d.uniformResourceIdentifier; + ouri = PyUnicode_FromStringAndSize((char *)uri->data, + uri->length); + if (ouri == NULL) { + Py_DECREF(lst); + return NULL; + } + result = PyList_Append(lst, ouri); + Py_DECREF(ouri); + if (result < 0) { + Py_DECREF(lst); + return NULL; + } } + } + /* convert to tuple or None */ + if (PyList_Size(lst) == 0) { + Py_DECREF(lst); + return Py_None; + } else { + PyObject *tup; + tup = PyList_AsTuple(lst); + Py_DECREF(lst); + return tup; + } +} + +static PyObject * +_decode_certificate(X509 *certificate) { + + PyObject *retval = NULL; + BIO *biobuf = NULL; + PyObject *peer; + PyObject *peer_alt_names = NULL; + PyObject *issuer; + PyObject *version; + PyObject *sn_obj; + PyObject *obj; + ASN1_INTEGER *serialNumber; + char buf[2048]; + int len, result; + ASN1_TIME *notBefore, *notAfter; + PyObject *pnotBefore, *pnotAfter; + + retval = PyDict_New(); + if (retval == NULL) + return NULL; + + peer = _create_tuple_for_X509_NAME( + X509_get_subject_name(certificate)); + if (peer == NULL) + goto fail0; + if (PyDict_SetItemString(retval, (const char *) "subject", peer) < 0) { + Py_DECREF(peer); + goto fail0; + } + Py_DECREF(peer); + + issuer = _create_tuple_for_X509_NAME( + X509_get_issuer_name(certificate)); + if (issuer == NULL) + goto fail0; + if (PyDict_SetItemString(retval, (const char *)"issuer", issuer) < 0) { + Py_DECREF(issuer); + goto fail0; + } + Py_DECREF(issuer); + + version = PyLong_FromLong(X509_get_version(certificate) + 1); + if (version == NULL) + goto fail0; + if (PyDict_SetItemString(retval, "version", version) < 0) { + Py_DECREF(version); + goto fail0; + } + Py_DECREF(version); + + /* get a memory buffer */ + biobuf = BIO_new(BIO_s_mem()); + + (void) BIO_reset(biobuf); + serialNumber = X509_get_serialNumber(certificate); + /* should not exceed 20 octets, 160 bits, so buf is big enough */ + i2a_ASN1_INTEGER(biobuf, serialNumber); + len = BIO_gets(biobuf, buf, sizeof(buf)-1); + if (len < 0) { + _setSSLError(NULL, 0, __FILE__, __LINE__); + goto fail1; + } + sn_obj = PyUnicode_FromStringAndSize(buf, len); + if (sn_obj == NULL) + goto fail1; + if (PyDict_SetItemString(retval, "serialNumber", sn_obj) < 0) { Py_DECREF(sn_obj); + goto fail1; + } + Py_DECREF(sn_obj); - (void) BIO_reset(biobuf); - notBefore = X509_get_notBefore(certificate); - ASN1_TIME_print(biobuf, notBefore); - len = BIO_gets(biobuf, buf, sizeof(buf)-1); - if (len < 0) { - _setSSLError(NULL, 0, __FILE__, __LINE__); - goto fail1; - } - pnotBefore = PyString_FromStringAndSize(buf, len); - if (pnotBefore == NULL) - goto fail1; - if (PyDict_SetItemString(retval, "notBefore", pnotBefore) < 0) { - Py_DECREF(pnotBefore); - goto fail1; - } + (void) BIO_reset(biobuf); + notBefore = X509_get_notBefore(certificate); + ASN1_TIME_print(biobuf, notBefore); + len = BIO_gets(biobuf, buf, sizeof(buf)-1); + if (len < 0) { + _setSSLError(NULL, 0, __FILE__, __LINE__); + goto fail1; + } + pnotBefore = PyUnicode_FromStringAndSize(buf, len); + if (pnotBefore == NULL) + goto fail1; + if (PyDict_SetItemString(retval, "notBefore", pnotBefore) < 0) { Py_DECREF(pnotBefore); + goto fail1; } + Py_DECREF(pnotBefore); (void) BIO_reset(biobuf); notAfter = X509_get_notAfter(certificate); @@ -1008,6 +1186,41 @@ _decode_certificate (X509 *certificate, int verbose) { Py_DECREF(peer_alt_names); } + /* Authority Information Access: OCSP URIs */ + obj = _get_aia_uri(certificate, NID_ad_OCSP); + if (obj == NULL) { + goto fail1; + } else if (obj != Py_None) { + result = PyDict_SetItemString(retval, "OCSP", obj); + Py_DECREF(obj); + if (result < 0) { + goto fail1; + } + } + + obj = _get_aia_uri(certificate, NID_ad_ca_issuers); + if (obj == NULL) { + goto fail1; + } else if (obj != Py_None) { + result = PyDict_SetItemString(retval, "caIssuers", obj); + Py_DECREF(obj); + if (result < 0) { + goto fail1; + } + } + + /* CDP (CRL distribution points) */ + obj = _get_crl_dp(certificate); + if (obj == NULL) { + goto fail1; + } else if (obj != Py_None) { + result = PyDict_SetItemString(retval, "crlDistributionPoints", obj); + Py_DECREF(obj); + if (result < 0) { + goto fail1; + } + } + BIO_free(biobuf); return retval; @@ -1019,6 +1232,24 @@ _decode_certificate (X509 *certificate, int verbose) { return NULL; } +static PyObject * +_certificate_to_der(X509 *certificate) +{ + unsigned char *bytes_buf = NULL; + int len; + PyObject *retval; + + bytes_buf = NULL; + len = i2d_X509(certificate, &bytes_buf); + if (len < 0) { + _setSSLError(NULL, 0, __FILE__, __LINE__); + return NULL; + } + /* this is actually an immutable bytes sequence */ + retval = PyBytes_FromStringAndSize((const char *) bytes_buf, len); + OPENSSL_free(bytes_buf); + return retval; +} static PyObject * PySSL_test_decode_certificate (PyObject *mod, PyObject *args) { @@ -1027,28 +1258,30 @@ PySSL_test_decode_certificate (PyObject *mod, PyObject *args) { char *filename = NULL; X509 *x=NULL; BIO *cert; - int verbose = 1; - if (!PyArg_ParseTuple(args, "s|i:test_decode_certificate", &filename, &verbose)) + if (!PyArg_ParseTuple(args, "s:test_decode_certificate", &filename)) return NULL; if ((cert=BIO_new(BIO_s_file())) == NULL) { - PyErr_SetString(PySSLErrorObject, "Can't malloc memory to read file"); + PyErr_SetString(PySSLErrorObject, + "Can't malloc memory to read file"); goto fail0; } if (BIO_read_filename(cert,filename) <= 0) { - PyErr_SetString(PySSLErrorObject, "Can't open file"); + PyErr_SetString(PySSLErrorObject, + "Can't open file"); goto fail0; } x = PEM_read_bio_X509_AUX(cert,NULL, NULL, NULL); if (x == NULL) { - PyErr_SetString(PySSLErrorObject, "Error decoding PEM-encoded file"); + PyErr_SetString(PySSLErrorObject, + "Error decoding PEM-encoded file"); goto fail0; } - retval = _decode_certificate(x, verbose); + retval = _decode_certificate(x); X509_free(x); fail0: @@ -1059,10 +1292,8 @@ PySSL_test_decode_certificate (PyObject *mod, PyObject *args) { static PyObject * -PySSL_peercert(PySSLObject *self, PyObject *args) +PySSL_peercert(PySSLSocket *self, PyObject *args) { - PyObject *retval = NULL; - int len; int verification; PyObject *binary_mode = Py_None; int b; @@ -1070,6 +1301,11 @@ PySSL_peercert(PySSLObject *self, PyObject *args) if (!PyArg_ParseTuple(args, "|O:peer_certificate", &binary_mode)) return NULL; + if (!self->handshake_done) { + PyErr_SetString(PyExc_ValueError, + "handshake not done yet"); + return NULL; + } if (!self->peer_cert) Py_RETURN_NONE; @@ -1078,26 +1314,13 @@ PySSL_peercert(PySSLObject *self, PyObject *args) return NULL; if (b) { /* return cert in DER-encoded format */ - - unsigned char *bytes_buf = NULL; - - bytes_buf = NULL; - len = i2d_X509(self->peer_cert, &bytes_buf); - if (len < 0) { - PySSL_SetError(self, len, __FILE__, __LINE__); - return NULL; - } - retval = PyString_FromStringAndSize((const char *) bytes_buf, len); - OPENSSL_free(bytes_buf); - return retval; - + return _certificate_to_der(self->peer_cert); } else { - - verification = SSL_CTX_get_verify_mode(self->ctx); + verification = SSL_CTX_get_verify_mode(SSL_get_SSL_CTX(self->ssl)); if ((verification & SSL_VERIFY_PEER) == 0) return PyDict_New(); else - return _decode_certificate (self->peer_cert, 0); + return _decode_certificate(self->peer_cert); } } @@ -1113,7 +1336,7 @@ If the optional argument is True, returns a DER-encoded copy of the\n\ peer certificate, or None if no certificate was provided. This will\n\ return the certificate even if it wasn't validated."); -static PyObject *PySSL_cipher (PySSLObject *self) { +static PyObject *PySSL_cipher (PySSLSocket *self) { PyObject *retval, *v; const SSL_CIPHER *current; @@ -1140,7 +1363,7 @@ static PyObject *PySSL_cipher (PySSLObject *self) { goto fail0; PyTuple_SET_ITEM(retval, 0, v); } - cipher_protocol = SSL_CIPHER_get_version(current); + cipher_protocol = (char *) SSL_CIPHER_get_version(current); if (cipher_protocol == NULL) { Py_INCREF(Py_None); PyTuple_SET_ITEM(retval, 1, Py_None); @@ -1161,15 +1384,85 @@ static PyObject *PySSL_cipher (PySSLObject *self) { return NULL; } -static void PySSL_dealloc(PySSLObject *self) +#ifdef OPENSSL_NPN_NEGOTIATED +static PyObject *PySSL_selected_npn_protocol(PySSLSocket *self) { + const unsigned char *out; + unsigned int outlen; + + SSL_get0_next_proto_negotiated(self->ssl, + &out, &outlen); + + if (out == NULL) + Py_RETURN_NONE; + return PyUnicode_FromStringAndSize((char *) out, outlen); +} +#endif + +static PyObject *PySSL_compression(PySSLSocket *self) { +#ifdef OPENSSL_NO_COMP + Py_RETURN_NONE; +#else + const COMP_METHOD *comp_method; + const char *short_name; + + if (self->ssl == NULL) + Py_RETURN_NONE; + comp_method = SSL_get_current_compression(self->ssl); + if (comp_method == NULL || comp_method->type == NID_undef) + Py_RETURN_NONE; + short_name = OBJ_nid2sn(comp_method->type); + if (short_name == NULL) + Py_RETURN_NONE; + return PyBytes_FromString(short_name); +#endif +} + +static PySSLContext *PySSL_get_context(PySSLSocket *self, void *closure) { + Py_INCREF(self->ctx); + return self->ctx; +} + +static int PySSL_set_context(PySSLSocket *self, PyObject *value, + void *closure) { + + if (PyObject_TypeCheck(value, &PySSLContext_Type)) { +#if !HAVE_SNI + PyErr_SetString(PyExc_NotImplementedError, "setting a socket's " + "context is not supported by your OpenSSL library"); + return -1; +#else + Py_INCREF(value); + Py_DECREF(self->ctx); + self->ctx = (PySSLContext *) value; + SSL_set_SSL_CTX(self->ssl, self->ctx->ctx); +#endif + } else { + PyErr_SetString(PyExc_TypeError, "The value must be a SSLContext"); + return -1; + } + + return 0; +} + +PyDoc_STRVAR(PySSL_set_context_doc, +"_setter_context(ctx)\n\ +\ +This changes the context associated with the SSLSocket. This is typically\n\ +used from within a callback function set by the set_servername_callback\n\ +on the SSLContext to change the certificate information associated with the\n\ +SSLSocket before the cryptographic exchange handshake messages\n"); + + + +static void PySSL_dealloc(PySSLSocket *self) { if (self->peer_cert) /* Possible not to have one? */ X509_free (self->peer_cert); if (self->ssl) SSL_free(self->ssl); - if (self->ctx) - SSL_CTX_free(self->ctx); Py_XDECREF(self->Socket); + Py_XDECREF(self->ssl_sock); + Py_XDECREF(self->ctx); PyObject_Del(self); } @@ -1241,16 +1534,21 @@ normal_return: return rc == 0 ? SOCKET_HAS_TIMED_OUT : SOCKET_OPERATION_OK; } -static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args) +static PyObject *PySSL_SSLwrite(PySSLSocket *self, PyObject *args) { Py_buffer buf; int len; int sockstate; int err; int nonblocking; + PySocketSockObject *sock = self->Socket; + + Py_INCREF(sock); - if (!PyArg_ParseTuple(args, "s*:write", &buf)) + if (!PyArg_ParseTuple(args, "s*:write", &buf)) { + Py_DECREF(sock); return NULL; + } if (buf.len > INT_MAX) { PyErr_Format(PyExc_OverflowError, @@ -1259,11 +1557,11 @@ static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args) } /* just in case the blocking state of the socket has been changed */ - nonblocking = (self->Socket->sock_timeout >= 0.0); + nonblocking = (sock->sock_timeout >= 0.0); BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); - sockstate = check_socket_and_wait_for_timeout(self->Socket, 1); + sockstate = check_socket_and_wait_for_timeout(sock, 1); if (sockstate == SOCKET_HAS_TIMED_OUT) { PyErr_SetString(PySSLErrorObject, "The write operation timed out"); @@ -1286,9 +1584,9 @@ static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args) goto error; } if (err == SSL_ERROR_WANT_READ) { - sockstate = check_socket_and_wait_for_timeout(self->Socket, 0); + sockstate = check_socket_and_wait_for_timeout(sock, 0); } else if (err == SSL_ERROR_WANT_WRITE) { - sockstate = check_socket_and_wait_for_timeout(self->Socket, 1); + sockstate = check_socket_and_wait_for_timeout(sock, 1); } else { sockstate = SOCKET_OPERATION_OK; } @@ -1305,6 +1603,7 @@ static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args) } } while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); + Py_DECREF(sock); PyBuffer_Release(&buf); if (len > 0) return PyInt_FromLong(len); @@ -1312,6 +1611,7 @@ static PyObject *PySSL_SSLwrite(PySSLObject *self, PyObject *args) return PySSL_SetError(self, len, __FILE__, __LINE__); error: + Py_DECREF(sock); PyBuffer_Release(&buf); return NULL; } @@ -1322,7 +1622,7 @@ PyDoc_STRVAR(PySSL_SSLwrite_doc, Writes the string s into the SSL object. Returns the number\n\ of bytes written."); -static PyObject *PySSL_SSLpending(PySSLObject *self) +static PyObject *PySSL_SSLpending(PySSLSocket *self) { int count = 0; @@ -1341,23 +1641,46 @@ PyDoc_STRVAR(PySSL_SSLpending_doc, Returns the number of already decrypted bytes available for read,\n\ pending on the connection.\n"); -static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args) +static PyObject *PySSL_SSLread(PySSLSocket *self, PyObject *args) { - PyObject *buf; - int count = 0; - int len = 1024; + PyObject *dest = NULL; + Py_buffer buf; + char *mem; + int len, count; + int buf_passed = 0; int sockstate; int err; int nonblocking; + PySocketSockObject *sock = self->Socket; - if (!PyArg_ParseTuple(args, "|i:read", &len)) - return NULL; + Py_INCREF(sock); - if (!(buf = PyString_FromStringAndSize((char *) 0, len))) - return NULL; + buf.obj = NULL; + buf.buf = NULL; + if (!PyArg_ParseTuple(args, "i|w*:read", &len, &buf)) + goto error; + + if ((buf.buf == NULL) && (buf.obj == NULL)) { + dest = PyBytes_FromStringAndSize(NULL, len); + if (dest == NULL) + goto error; + mem = PyBytes_AS_STRING(dest); + } + else { + buf_passed = 1; + mem = buf.buf; + if (len <= 0 || len > buf.len) { + len = (int) buf.len; + if (buf.len != len) { + PyErr_SetString(PyExc_OverflowError, + "maximum length can't fit in a C 'int'"); + goto error; + } + } + } /* just in case the blocking state of the socket has been changed */ - nonblocking = (self->Socket->sock_timeout >= 0.0); + nonblocking = (sock->sock_timeout >= 0.0); BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); @@ -1367,70 +1690,71 @@ static PyObject *PySSL_SSLread(PySSLObject *self, PyObject *args) PySSL_END_ALLOW_THREADS if (!count) { - sockstate = check_socket_and_wait_for_timeout(self->Socket, 0); + sockstate = check_socket_and_wait_for_timeout(sock, 0); if (sockstate == SOCKET_HAS_TIMED_OUT) { PyErr_SetString(PySSLErrorObject, "The read operation timed out"); - Py_DECREF(buf); - return NULL; + goto error; } else if (sockstate == SOCKET_TOO_LARGE_FOR_SELECT) { PyErr_SetString(PySSLErrorObject, "Underlying socket too large for select()."); - Py_DECREF(buf); - return NULL; + goto error; } else if (sockstate == SOCKET_HAS_BEEN_CLOSED) { - if (SSL_get_shutdown(self->ssl) != - SSL_RECEIVED_SHUTDOWN) - { - Py_DECREF(buf); - PyErr_SetString(PySSLErrorObject, - "Socket closed without SSL shutdown handshake"); - return NULL; - } else { - /* should contain a zero-length string */ - _PyString_Resize(&buf, 0); - return buf; - } + count = 0; + goto done; } } do { PySSL_BEGIN_ALLOW_THREADS - count = SSL_read(self->ssl, PyString_AsString(buf), len); + count = SSL_read(self->ssl, mem, len); err = SSL_get_error(self->ssl, count); PySSL_END_ALLOW_THREADS - if(PyErr_CheckSignals()) { - Py_DECREF(buf); - return NULL; - } + if (PyErr_CheckSignals()) + goto error; if (err == SSL_ERROR_WANT_READ) { - sockstate = check_socket_and_wait_for_timeout(self->Socket, 0); + sockstate = check_socket_and_wait_for_timeout(sock, 0); } else if (err == SSL_ERROR_WANT_WRITE) { - sockstate = check_socket_and_wait_for_timeout(self->Socket, 1); + sockstate = check_socket_and_wait_for_timeout(sock, 1); } else if ((err == SSL_ERROR_ZERO_RETURN) && (SSL_get_shutdown(self->ssl) == SSL_RECEIVED_SHUTDOWN)) { - _PyString_Resize(&buf, 0); - return buf; + count = 0; + goto done; } else { sockstate = SOCKET_OPERATION_OK; } if (sockstate == SOCKET_HAS_TIMED_OUT) { PyErr_SetString(PySSLErrorObject, "The read operation timed out"); - Py_DECREF(buf); - return NULL; + goto error; } else if (sockstate == SOCKET_IS_NONBLOCKING) { break; } } while (err == SSL_ERROR_WANT_READ || err == SSL_ERROR_WANT_WRITE); if (count <= 0) { - Py_DECREF(buf); - return PySSL_SetError(self, count, __FILE__, __LINE__); + PySSL_SetError(self, count, __FILE__, __LINE__); + goto error; + } + +done: + Py_DECREF(sock); + if (!buf_passed) { + _PyBytes_Resize(&dest, count); + return dest; } - if (count != len) - _PyString_Resize(&buf, count); - return buf; + else { + PyBuffer_Release(&buf); + return PyLong_FromLong(count); + } + +error: + Py_DECREF(sock); + if (!buf_passed) + Py_XDECREF(dest); + else + PyBuffer_Release(&buf); + return NULL; } PyDoc_STRVAR(PySSL_SSLread_doc, @@ -1438,20 +1762,22 @@ PyDoc_STRVAR(PySSL_SSLread_doc, \n\ Read up to len bytes from the SSL socket."); -static PyObject *PySSL_SSLshutdown(PySSLObject *self) +static PyObject *PySSL_SSLshutdown(PySSLSocket *self) { int err, ssl_err, sockstate, nonblocking; int zeros = 0; + PySocketSockObject *sock = self->Socket; /* Guard against closed socket */ - if (self->Socket->sock_fd < 0) { - PyErr_SetString(PySSLErrorObject, - "Underlying socket has been closed."); + if (sock->sock_fd < 0) { + _setSSLError("Underlying socket connection gone", + PY_SSL_ERROR_NO_SOCKET, __FILE__, __LINE__); return NULL; } + Py_INCREF(sock); /* Just in case the blocking state of the socket has been changed */ - nonblocking = (self->Socket->sock_timeout >= 0.0); + nonblocking = (sock->sock_timeout >= 0.0); BIO_set_nbio(SSL_get_rbio(self->ssl), nonblocking); BIO_set_nbio(SSL_get_wbio(self->ssl), nonblocking); @@ -1486,9 +1812,9 @@ static PyObject *PySSL_SSLshutdown(PySSLObject *self) /* Possibly retry shutdown until timeout or failure */ ssl_err = SSL_get_error(self->ssl, err); if (ssl_err == SSL_ERROR_WANT_READ) - sockstate = check_socket_and_wait_for_timeout(self->Socket, 0); + sockstate = check_socket_and_wait_for_timeout(sock, 0); else if (ssl_err == SSL_ERROR_WANT_WRITE) - sockstate = check_socket_and_wait_for_timeout(self->Socket, 1); + sockstate = check_socket_and_wait_for_timeout(sock, 1); else break; if (sockstate == SOCKET_HAS_TIMED_OUT) { @@ -1498,24 +1824,29 @@ static PyObject *PySSL_SSLshutdown(PySSLObject *self) else PyErr_SetString(PySSLErrorObject, "The write operation timed out"); - return NULL; + goto error; } else if (sockstate == SOCKET_TOO_LARGE_FOR_SELECT) { PyErr_SetString(PySSLErrorObject, "Underlying socket too large for select()."); - return NULL; + goto error; } else if (sockstate != SOCKET_OPERATION_OK) /* Retain the SSL error code */ break; } - if (err < 0) + if (err < 0) { + Py_DECREF(sock); return PySSL_SetError(self, err, __FILE__, __LINE__); - else { - Py_INCREF(self->Socket); - return (PyObject *) (self->Socket); } + else + /* It's already INCREF'ed */ + return (PyObject *) sock; + +error: + Py_DECREF(sock); + return NULL; } PyDoc_STRVAR(PySSL_SSLshutdown_doc, @@ -1524,6 +1855,47 @@ PyDoc_STRVAR(PySSL_SSLshutdown_doc, Does the SSL shutdown handshake with the remote end, and returns\n\ the underlying socket object."); +#if HAVE_OPENSSL_FINISHED +static PyObject * +PySSL_tls_unique_cb(PySSLSocket *self) +{ + PyObject *retval = NULL; + char buf[PySSL_CB_MAXLEN]; + size_t len; + + if (SSL_session_reused(self->ssl) ^ !self->socket_type) { + /* if session is resumed XOR we are the client */ + len = SSL_get_finished(self->ssl, buf, PySSL_CB_MAXLEN); + } + else { + /* if a new session XOR we are the server */ + len = SSL_get_peer_finished(self->ssl, buf, PySSL_CB_MAXLEN); + } + + /* It cannot be negative in current OpenSSL version as of July 2011 */ + if (len == 0) + Py_RETURN_NONE; + + retval = PyBytes_FromStringAndSize(buf, len); + + return retval; +} + +PyDoc_STRVAR(PySSL_tls_unique_cb_doc, +"tls_unique_cb() -> bytes\n\ +\n\ +Returns the 'tls-unique' channel binding data, as defined by RFC 5929.\n\ +\n\ +If the TLS handshake is not yet complete, None is returned"); + +#endif /* HAVE_OPENSSL_FINISHED */ + +static PyGetSetDef ssl_getsetlist[] = { + {"context", (getter) PySSL_get_context, + (setter) PySSL_set_context, PySSL_set_context_doc}, + {NULL}, /* sentinel */ +}; + static PyMethodDef PySSLMethods[] = { {"do_handshake", (PyCFunction)PySSL_SSLdo_handshake, METH_NOARGS}, {"write", (PyCFunction)PySSL_SSLwrite, METH_VARARGS, @@ -1532,66 +1904,1343 @@ static PyMethodDef PySSLMethods[] = { PySSL_SSLread_doc}, {"pending", (PyCFunction)PySSL_SSLpending, METH_NOARGS, PySSL_SSLpending_doc}, - {"server", (PyCFunction)PySSL_server, METH_NOARGS}, - {"issuer", (PyCFunction)PySSL_issuer, METH_NOARGS}, {"peer_certificate", (PyCFunction)PySSL_peercert, METH_VARARGS, PySSL_peercert_doc}, {"cipher", (PyCFunction)PySSL_cipher, METH_NOARGS}, +#ifdef OPENSSL_NPN_NEGOTIATED + {"selected_npn_protocol", (PyCFunction)PySSL_selected_npn_protocol, METH_NOARGS}, +#endif + {"compression", (PyCFunction)PySSL_compression, METH_NOARGS}, {"shutdown", (PyCFunction)PySSL_SSLshutdown, METH_NOARGS, PySSL_SSLshutdown_doc}, +#if HAVE_OPENSSL_FINISHED + {"tls_unique_cb", (PyCFunction)PySSL_tls_unique_cb, METH_NOARGS, + PySSL_tls_unique_cb_doc}, +#endif {NULL, NULL} }; -static PyObject *PySSL_getattr(PySSLObject *self, char *name) -{ - return Py_FindMethod(PySSLMethods, (PyObject *)self, name); -} - -static PyTypeObject PySSL_Type = { +static PyTypeObject PySSLSocket_Type = { PyVarObject_HEAD_INIT(NULL, 0) - "ssl.SSLContext", /*tp_name*/ - sizeof(PySSLObject), /*tp_basicsize*/ + "_ssl._SSLSocket", /*tp_name*/ + sizeof(PySSLSocket), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ (destructor)PySSL_dealloc, /*tp_dealloc*/ 0, /*tp_print*/ - (getattrfunc)PySSL_getattr, /*tp_getattr*/ + 0, /*tp_getattr*/ 0, /*tp_setattr*/ - 0, /*tp_compare*/ + 0, /*tp_reserved*/ 0, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT, /*tp_flags*/ + 0, /*tp_doc*/ + 0, /*tp_traverse*/ + 0, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + PySSLMethods, /*tp_methods*/ + 0, /*tp_members*/ + ssl_getsetlist, /*tp_getset*/ }; -#ifdef HAVE_OPENSSL_RAND -/* helper routines for seeding the SSL PRNG */ +/* + * _SSLContext objects + */ + static PyObject * -PySSL_RAND_add(PyObject *self, PyObject *args) +context_new(PyTypeObject *type, PyObject *args, PyObject *kwds) { - char *buf; - int len; - double entropy; + char *kwlist[] = {"protocol", NULL}; + PySSLContext *self; + int proto_version = PY_SSL_VERSION_SSL23; + long options; + SSL_CTX *ctx = NULL; - if (!PyArg_ParseTuple(args, "s#d:RAND_add", &buf, &len, &entropy)) + if (!PyArg_ParseTupleAndKeywords( + args, kwds, "i:_SSLContext", kwlist, + &proto_version)) return NULL; - RAND_add(buf, len, entropy); - Py_INCREF(Py_None); - return Py_None; -} - -PyDoc_STRVAR(PySSL_RAND_add_doc, -"RAND_add(string, entropy)\n\ -\n\ -Mix string into the OpenSSL PRNG state. entropy (a float) is a lower\n\ -bound on the entropy contained in string. See RFC 1750."); -static PyObject * -PySSL_RAND_status(PyObject *self) -{ - return PyInt_FromLong(RAND_status()); + PySSL_BEGIN_ALLOW_THREADS + if (proto_version == PY_SSL_VERSION_TLS1) + ctx = SSL_CTX_new(TLSv1_method()); +#if HAVE_TLSv1_2 + else if (proto_version == PY_SSL_VERSION_TLS1_1) + ctx = SSL_CTX_new(TLSv1_1_method()); + else if (proto_version == PY_SSL_VERSION_TLS1_2) + ctx = SSL_CTX_new(TLSv1_2_method()); +#endif + else if (proto_version == PY_SSL_VERSION_SSL3) + ctx = SSL_CTX_new(SSLv3_method()); +#ifndef OPENSSL_NO_SSL2 + else if (proto_version == PY_SSL_VERSION_SSL2) + ctx = SSL_CTX_new(SSLv2_method()); +#endif + else if (proto_version == PY_SSL_VERSION_SSL23) + ctx = SSL_CTX_new(SSLv23_method()); + else + proto_version = -1; + PySSL_END_ALLOW_THREADS + + if (proto_version == -1) { + PyErr_SetString(PyExc_ValueError, + "invalid protocol version"); + return NULL; + } + if (ctx == NULL) { + PyErr_SetString(PySSLErrorObject, + "failed to allocate SSL context"); + return NULL; + } + + assert(type != NULL && type->tp_alloc != NULL); + self = (PySSLContext *) type->tp_alloc(type, 0); + if (self == NULL) { + SSL_CTX_free(ctx); + return NULL; + } + self->ctx = ctx; +#ifdef OPENSSL_NPN_NEGOTIATED + self->npn_protocols = NULL; +#endif +#ifndef OPENSSL_NO_TLSEXT + self->set_hostname = NULL; +#endif + /* Don't check host name by default */ + self->check_hostname = 0; + /* Defaults */ + SSL_CTX_set_verify(self->ctx, SSL_VERIFY_NONE, NULL); + options = SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; + if (proto_version != PY_SSL_VERSION_SSL2) + options |= SSL_OP_NO_SSLv2; + SSL_CTX_set_options(self->ctx, options); + +#ifndef OPENSSL_NO_ECDH + /* Allow automatic ECDH curve selection (on OpenSSL 1.0.2+), or use + prime256v1 by default. This is Apache mod_ssl's initialization + policy, so we should be safe. */ +#if defined(SSL_CTX_set_ecdh_auto) + SSL_CTX_set_ecdh_auto(self->ctx, 1); +#else + { + EC_KEY *key = EC_KEY_new_by_curve_name(NID_X9_62_prime256v1); + SSL_CTX_set_tmp_ecdh(self->ctx, key); + EC_KEY_free(key); + } +#endif +#endif + +#define SID_CTX "Python" + SSL_CTX_set_session_id_context(self->ctx, (const unsigned char *) SID_CTX, + sizeof(SID_CTX)); +#undef SID_CTX + + return (PyObject *)self; +} + +static int +context_traverse(PySSLContext *self, visitproc visit, void *arg) +{ +#ifndef OPENSSL_NO_TLSEXT + Py_VISIT(self->set_hostname); +#endif + return 0; +} + +static int +context_clear(PySSLContext *self) +{ +#ifndef OPENSSL_NO_TLSEXT + Py_CLEAR(self->set_hostname); +#endif + return 0; +} + +static void +context_dealloc(PySSLContext *self) +{ + context_clear(self); + SSL_CTX_free(self->ctx); +#ifdef OPENSSL_NPN_NEGOTIATED + PyMem_Free(self->npn_protocols); +#endif + Py_TYPE(self)->tp_free(self); +} + +static PyObject * +set_ciphers(PySSLContext *self, PyObject *args) +{ + int ret; + const char *cipherlist; + + if (!PyArg_ParseTuple(args, "s:set_ciphers", &cipherlist)) + return NULL; + ret = SSL_CTX_set_cipher_list(self->ctx, cipherlist); + if (ret == 0) { + /* Clearing the error queue is necessary on some OpenSSL versions, + otherwise the error will be reported again when another SSL call + is done. */ + ERR_clear_error(); + PyErr_SetString(PySSLErrorObject, + "No cipher can be selected."); + return NULL; + } + Py_RETURN_NONE; +} + +#ifdef OPENSSL_NPN_NEGOTIATED +/* this callback gets passed to SSL_CTX_set_next_protos_advertise_cb */ +static int +_advertiseNPN_cb(SSL *s, + const unsigned char **data, unsigned int *len, + void *args) +{ + PySSLContext *ssl_ctx = (PySSLContext *) args; + + if (ssl_ctx->npn_protocols == NULL) { + *data = (unsigned char *) ""; + *len = 0; + } else { + *data = (unsigned char *) ssl_ctx->npn_protocols; + *len = ssl_ctx->npn_protocols_len; + } + + return SSL_TLSEXT_ERR_OK; +} +/* this callback gets passed to SSL_CTX_set_next_proto_select_cb */ +static int +_selectNPN_cb(SSL *s, + unsigned char **out, unsigned char *outlen, + const unsigned char *server, unsigned int server_len, + void *args) +{ + PySSLContext *ssl_ctx = (PySSLContext *) args; + + unsigned char *client = (unsigned char *) ssl_ctx->npn_protocols; + int client_len; + + if (client == NULL) { + client = (unsigned char *) ""; + client_len = 0; + } else { + client_len = ssl_ctx->npn_protocols_len; + } + + SSL_select_next_proto(out, outlen, + server, server_len, + client, client_len); + + return SSL_TLSEXT_ERR_OK; +} +#endif + +static PyObject * +_set_npn_protocols(PySSLContext *self, PyObject *args) +{ +#ifdef OPENSSL_NPN_NEGOTIATED + Py_buffer protos; + + if (!PyArg_ParseTuple(args, "s*:set_npn_protocols", &protos)) + return NULL; + + if (self->npn_protocols != NULL) { + PyMem_Free(self->npn_protocols); + } + + self->npn_protocols = PyMem_Malloc(protos.len); + if (self->npn_protocols == NULL) { + PyBuffer_Release(&protos); + return PyErr_NoMemory(); + } + memcpy(self->npn_protocols, protos.buf, protos.len); + self->npn_protocols_len = (int) protos.len; + + /* set both server and client callbacks, because the context can + * be used to create both types of sockets */ + SSL_CTX_set_next_protos_advertised_cb(self->ctx, + _advertiseNPN_cb, + self); + SSL_CTX_set_next_proto_select_cb(self->ctx, + _selectNPN_cb, + self); + + PyBuffer_Release(&protos); + Py_RETURN_NONE; +#else + PyErr_SetString(PyExc_NotImplementedError, + "The NPN extension requires OpenSSL 1.0.1 or later."); + return NULL; +#endif +} + +static PyObject * +get_verify_mode(PySSLContext *self, void *c) +{ + switch (SSL_CTX_get_verify_mode(self->ctx)) { + case SSL_VERIFY_NONE: + return PyLong_FromLong(PY_SSL_CERT_NONE); + case SSL_VERIFY_PEER: + return PyLong_FromLong(PY_SSL_CERT_OPTIONAL); + case SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT: + return PyLong_FromLong(PY_SSL_CERT_REQUIRED); + } + PyErr_SetString(PySSLErrorObject, + "invalid return value from SSL_CTX_get_verify_mode"); + return NULL; +} + +static int +set_verify_mode(PySSLContext *self, PyObject *arg, void *c) +{ + int n, mode; + if (!PyArg_Parse(arg, "i", &n)) + return -1; + if (n == PY_SSL_CERT_NONE) + mode = SSL_VERIFY_NONE; + else if (n == PY_SSL_CERT_OPTIONAL) + mode = SSL_VERIFY_PEER; + else if (n == PY_SSL_CERT_REQUIRED) + mode = SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT; + else { + PyErr_SetString(PyExc_ValueError, + "invalid value for verify_mode"); + return -1; + } + if (mode == SSL_VERIFY_NONE && self->check_hostname) { + PyErr_SetString(PyExc_ValueError, + "Cannot set verify_mode to CERT_NONE when " + "check_hostname is enabled."); + return -1; + } + SSL_CTX_set_verify(self->ctx, mode, NULL); + return 0; +} + +#ifdef HAVE_OPENSSL_VERIFY_PARAM +static PyObject * +get_verify_flags(PySSLContext *self, void *c) +{ + X509_STORE *store; + unsigned long flags; + + store = SSL_CTX_get_cert_store(self->ctx); + flags = X509_VERIFY_PARAM_get_flags(store->param); + return PyLong_FromUnsignedLong(flags); +} + +static int +set_verify_flags(PySSLContext *self, PyObject *arg, void *c) +{ + X509_STORE *store; + unsigned long new_flags, flags, set, clear; + + if (!PyArg_Parse(arg, "k", &new_flags)) + return -1; + store = SSL_CTX_get_cert_store(self->ctx); + flags = X509_VERIFY_PARAM_get_flags(store->param); + clear = flags & ~new_flags; + set = ~flags & new_flags; + if (clear) { + if (!X509_VERIFY_PARAM_clear_flags(store->param, clear)) { + _setSSLError(NULL, 0, __FILE__, __LINE__); + return -1; + } + } + if (set) { + if (!X509_VERIFY_PARAM_set_flags(store->param, set)) { + _setSSLError(NULL, 0, __FILE__, __LINE__); + return -1; + } + } + return 0; +} +#endif + +static PyObject * +get_options(PySSLContext *self, void *c) +{ + return PyLong_FromLong(SSL_CTX_get_options(self->ctx)); +} + +static int +set_options(PySSLContext *self, PyObject *arg, void *c) +{ + long new_opts, opts, set, clear; + if (!PyArg_Parse(arg, "l", &new_opts)) + return -1; + opts = SSL_CTX_get_options(self->ctx); + clear = opts & ~new_opts; + set = ~opts & new_opts; + if (clear) { +#ifdef HAVE_SSL_CTX_CLEAR_OPTIONS + SSL_CTX_clear_options(self->ctx, clear); +#else + PyErr_SetString(PyExc_ValueError, + "can't clear options before OpenSSL 0.9.8m"); + return -1; +#endif + } + if (set) + SSL_CTX_set_options(self->ctx, set); + return 0; +} + +static PyObject * +get_check_hostname(PySSLContext *self, void *c) +{ + return PyBool_FromLong(self->check_hostname); +} + +static int +set_check_hostname(PySSLContext *self, PyObject *arg, void *c) +{ + PyObject *py_check_hostname; + int check_hostname; + if (!PyArg_Parse(arg, "O", &py_check_hostname)) + return -1; + + check_hostname = PyObject_IsTrue(py_check_hostname); + if (check_hostname < 0) + return -1; + if (check_hostname && + SSL_CTX_get_verify_mode(self->ctx) == SSL_VERIFY_NONE) { + PyErr_SetString(PyExc_ValueError, + "check_hostname needs a SSL context with either " + "CERT_OPTIONAL or CERT_REQUIRED"); + return -1; + } + self->check_hostname = check_hostname; + return 0; +} + + +typedef struct { + PyThreadState *thread_state; + PyObject *callable; + char *password; + int size; + int error; +} _PySSLPasswordInfo; + +static int +_pwinfo_set(_PySSLPasswordInfo *pw_info, PyObject* password, + const char *bad_type_error) +{ + /* Set the password and size fields of a _PySSLPasswordInfo struct + from a unicode, bytes, or byte array object. + The password field will be dynamically allocated and must be freed + by the caller */ + PyObject *password_bytes = NULL; + const char *data = NULL; + Py_ssize_t size; + + if (PyUnicode_Check(password)) { + password_bytes = PyUnicode_AsEncodedString(password, NULL, NULL); + if (!password_bytes) { + goto error; + } + data = PyBytes_AS_STRING(password_bytes); + size = PyBytes_GET_SIZE(password_bytes); + } else if (PyBytes_Check(password)) { + data = PyBytes_AS_STRING(password); + size = PyBytes_GET_SIZE(password); + } else if (PyByteArray_Check(password)) { + data = PyByteArray_AS_STRING(password); + size = PyByteArray_GET_SIZE(password); + } else { + PyErr_SetString(PyExc_TypeError, bad_type_error); + goto error; + } + + if (size > (Py_ssize_t)INT_MAX) { + PyErr_Format(PyExc_ValueError, + "password cannot be longer than %d bytes", INT_MAX); + goto error; + } + + PyMem_Free(pw_info->password); + pw_info->password = PyMem_Malloc(size); + if (!pw_info->password) { + PyErr_SetString(PyExc_MemoryError, + "unable to allocate password buffer"); + goto error; + } + memcpy(pw_info->password, data, size); + pw_info->size = (int)size; + + Py_XDECREF(password_bytes); + return 1; + +error: + Py_XDECREF(password_bytes); + return 0; +} + +static int +_password_callback(char *buf, int size, int rwflag, void *userdata) +{ + _PySSLPasswordInfo *pw_info = (_PySSLPasswordInfo*) userdata; + PyObject *fn_ret = NULL; + + PySSL_END_ALLOW_THREADS_S(pw_info->thread_state); + + if (pw_info->callable) { + fn_ret = PyObject_CallFunctionObjArgs(pw_info->callable, NULL); + if (!fn_ret) { + /* TODO: It would be nice to move _ctypes_add_traceback() into the + core python API, so we could use it to add a frame here */ + goto error; + } + + if (!_pwinfo_set(pw_info, fn_ret, + "password callback must return a string")) { + goto error; + } + Py_CLEAR(fn_ret); + } + + if (pw_info->size > size) { + PyErr_Format(PyExc_ValueError, + "password cannot be longer than %d bytes", size); + goto error; + } + + PySSL_BEGIN_ALLOW_THREADS_S(pw_info->thread_state); + memcpy(buf, pw_info->password, pw_info->size); + return pw_info->size; + +error: + Py_XDECREF(fn_ret); + PySSL_BEGIN_ALLOW_THREADS_S(pw_info->thread_state); + pw_info->error = 1; + return -1; +} + +static PyObject * +load_cert_chain(PySSLContext *self, PyObject *args, PyObject *kwds) +{ + char *kwlist[] = {"certfile", "keyfile", "password", NULL}; + PyObject *password = NULL; + char *certfile_bytes = NULL, *keyfile_bytes = NULL; + pem_password_cb *orig_passwd_cb = self->ctx->default_passwd_callback; + void *orig_passwd_userdata = self->ctx->default_passwd_callback_userdata; + _PySSLPasswordInfo pw_info = { NULL, NULL, NULL, 0, 0 }; + int r; + + errno = 0; + ERR_clear_error(); + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "et|etO:load_cert_chain", kwlist, + Py_FileSystemDefaultEncoding, &certfile_bytes, + Py_FileSystemDefaultEncoding, &keyfile_bytes, + &password)) + return NULL; + if (password && password != Py_None) { + if (PyCallable_Check(password)) { + pw_info.callable = password; + } else if (!_pwinfo_set(&pw_info, password, + "password should be a string or callable")) { + goto error; + } + SSL_CTX_set_default_passwd_cb(self->ctx, _password_callback); + SSL_CTX_set_default_passwd_cb_userdata(self->ctx, &pw_info); + } + PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state); + r = SSL_CTX_use_certificate_chain_file(self->ctx, certfile_bytes); + PySSL_END_ALLOW_THREADS_S(pw_info.thread_state); + if (r != 1) { + if (pw_info.error) { + ERR_clear_error(); + /* the password callback has already set the error information */ + } + else if (errno != 0) { + ERR_clear_error(); + PyErr_SetFromErrno(PyExc_IOError); + } + else { + _setSSLError(NULL, 0, __FILE__, __LINE__); + } + goto error; + } + PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state); + r = SSL_CTX_use_PrivateKey_file(self->ctx, + keyfile_bytes ? keyfile_bytes : certfile_bytes, + SSL_FILETYPE_PEM); + PySSL_END_ALLOW_THREADS_S(pw_info.thread_state); + if (r != 1) { + if (pw_info.error) { + ERR_clear_error(); + /* the password callback has already set the error information */ + } + else if (errno != 0) { + ERR_clear_error(); + PyErr_SetFromErrno(PyExc_IOError); + } + else { + _setSSLError(NULL, 0, __FILE__, __LINE__); + } + goto error; + } + PySSL_BEGIN_ALLOW_THREADS_S(pw_info.thread_state); + r = SSL_CTX_check_private_key(self->ctx); + PySSL_END_ALLOW_THREADS_S(pw_info.thread_state); + if (r != 1) { + _setSSLError(NULL, 0, __FILE__, __LINE__); + goto error; + } + SSL_CTX_set_default_passwd_cb(self->ctx, orig_passwd_cb); + SSL_CTX_set_default_passwd_cb_userdata(self->ctx, orig_passwd_userdata); + PyMem_Free(pw_info.password); + Py_RETURN_NONE; + +error: + SSL_CTX_set_default_passwd_cb(self->ctx, orig_passwd_cb); + SSL_CTX_set_default_passwd_cb_userdata(self->ctx, orig_passwd_userdata); + PyMem_Free(pw_info.password); + PyMem_Free(keyfile_bytes); + PyMem_Free(certfile_bytes); + return NULL; +} + +/* internal helper function, returns -1 on error + */ +static int +_add_ca_certs(PySSLContext *self, void *data, Py_ssize_t len, + int filetype) +{ + BIO *biobuf = NULL; + X509_STORE *store; + int retval = 0, err, loaded = 0; + + assert(filetype == SSL_FILETYPE_ASN1 || filetype == SSL_FILETYPE_PEM); + + if (len <= 0) { + PyErr_SetString(PyExc_ValueError, + "Empty certificate data"); + return -1; + } else if (len > INT_MAX) { + PyErr_SetString(PyExc_OverflowError, + "Certificate data is too long."); + return -1; + } + + biobuf = BIO_new_mem_buf(data, (int)len); + if (biobuf == NULL) { + _setSSLError("Can't allocate buffer", 0, __FILE__, __LINE__); + return -1; + } + + store = SSL_CTX_get_cert_store(self->ctx); + assert(store != NULL); + + while (1) { + X509 *cert = NULL; + int r; + + if (filetype == SSL_FILETYPE_ASN1) { + cert = d2i_X509_bio(biobuf, NULL); + } else { + cert = PEM_read_bio_X509(biobuf, NULL, + self->ctx->default_passwd_callback, + self->ctx->default_passwd_callback_userdata); + } + if (cert == NULL) { + break; + } + r = X509_STORE_add_cert(store, cert); + X509_free(cert); + if (!r) { + err = ERR_peek_last_error(); + if ((ERR_GET_LIB(err) == ERR_LIB_X509) && + (ERR_GET_REASON(err) == X509_R_CERT_ALREADY_IN_HASH_TABLE)) { + /* cert already in hash table, not an error */ + ERR_clear_error(); + } else { + break; + } + } + loaded++; + } + + err = ERR_peek_last_error(); + if ((filetype == SSL_FILETYPE_ASN1) && + (loaded > 0) && + (ERR_GET_LIB(err) == ERR_LIB_ASN1) && + (ERR_GET_REASON(err) == ASN1_R_HEADER_TOO_LONG)) { + /* EOF ASN1 file, not an error */ + ERR_clear_error(); + retval = 0; + } else if ((filetype == SSL_FILETYPE_PEM) && + (loaded > 0) && + (ERR_GET_LIB(err) == ERR_LIB_PEM) && + (ERR_GET_REASON(err) == PEM_R_NO_START_LINE)) { + /* EOF PEM file, not an error */ + ERR_clear_error(); + retval = 0; + } else { + _setSSLError(NULL, 0, __FILE__, __LINE__); + retval = -1; + } + + BIO_free(biobuf); + return retval; +} + + +static PyObject * +load_verify_locations(PySSLContext *self, PyObject *args, PyObject *kwds) +{ + char *kwlist[] = {"cafile", "capath", "cadata", NULL}; + PyObject *cadata = NULL, *cafile = NULL, *capath = NULL; + PyObject *cafile_bytes = NULL, *capath_bytes = NULL; + const char *cafile_buf = NULL, *capath_buf = NULL; + int r = 0, ok = 1; + + errno = 0; + if (!PyArg_ParseTupleAndKeywords(args, kwds, + "|OOO:load_verify_locations", kwlist, + &cafile, &capath, &cadata)) + return NULL; + + if (cafile == Py_None) + cafile = NULL; + if (capath == Py_None) + capath = NULL; + if (cadata == Py_None) + cadata = NULL; + + if (cafile == NULL && capath == NULL && cadata == NULL) { + PyErr_SetString(PyExc_TypeError, + "cafile, capath and cadata cannot be all omitted"); + goto error; + } + + if (cafile) { + cafile_bytes = PyString_AsEncodedObject( + cafile, Py_FileSystemDefaultEncoding, "strict"); + if (!cafile_bytes) { + goto error; + } + } + if (capath) { + capath_bytes = PyString_AsEncodedObject( + capath, Py_FileSystemDefaultEncoding, "strict"); + if (!capath_bytes) { + goto error; + } + } + + /* validata cadata type and load cadata */ + if (cadata) { + Py_buffer buf; + PyObject *cadata_ascii = NULL; + + if (!PyUnicode_Check(cadata) && PyObject_GetBuffer(cadata, &buf, PyBUF_SIMPLE) == 0) { + if (!PyBuffer_IsContiguous(&buf, 'C') || buf.ndim > 1) { + PyBuffer_Release(&buf); + PyErr_SetString(PyExc_TypeError, + "cadata should be a contiguous buffer with " + "a single dimension"); + goto error; + } + r = _add_ca_certs(self, buf.buf, buf.len, SSL_FILETYPE_ASN1); + PyBuffer_Release(&buf); + if (r == -1) { + goto error; + } + } else { + PyErr_Clear(); + cadata_ascii = PyUnicode_AsASCIIString(cadata); + if (cadata_ascii == NULL) { + PyErr_SetString(PyExc_TypeError, + "cadata should be a ASCII string or a " + "bytes-like object"); + goto error; + } + r = _add_ca_certs(self, + PyBytes_AS_STRING(cadata_ascii), + PyBytes_GET_SIZE(cadata_ascii), + SSL_FILETYPE_PEM); + Py_DECREF(cadata_ascii); + if (r == -1) { + goto error; + } + } + } + + /* load cafile or capath */ + if (cafile_bytes || capath_bytes) { + if (cafile) + cafile_buf = PyBytes_AS_STRING(cafile_bytes); + if (capath) + capath_buf = PyBytes_AS_STRING(capath_bytes); + PySSL_BEGIN_ALLOW_THREADS + r = SSL_CTX_load_verify_locations( + self->ctx, + cafile_buf, + capath_buf); + PySSL_END_ALLOW_THREADS + if (r != 1) { + ok = 0; + if (errno != 0) { + ERR_clear_error(); + PyErr_SetFromErrno(PyExc_IOError); + } + else { + _setSSLError(NULL, 0, __FILE__, __LINE__); + } + goto error; + } + } + goto end; + + error: + ok = 0; + end: + Py_XDECREF(cafile_bytes); + Py_XDECREF(capath_bytes); + if (ok) { + Py_RETURN_NONE; + } else { + return NULL; + } +} + +static PyObject * +load_dh_params(PySSLContext *self, PyObject *filepath) +{ + BIO *bio; + DH *dh; + char *path = PyBytes_AsString(filepath); + if (!path) { + return NULL; + } + + bio = BIO_new_file(path, "r"); + if (bio == NULL) { + ERR_clear_error(); + PyErr_SetFromErrnoWithFilenameObject(PyExc_IOError, filepath); + return NULL; + } + errno = 0; + PySSL_BEGIN_ALLOW_THREADS + dh = PEM_read_bio_DHparams(bio, NULL, NULL, NULL); + BIO_free(bio); + PySSL_END_ALLOW_THREADS + if (dh == NULL) { + if (errno != 0) { + ERR_clear_error(); + PyErr_SetFromErrnoWithFilenameObject(PyExc_OSError, filepath); + } + else { + _setSSLError(NULL, 0, __FILE__, __LINE__); + } + return NULL; + } + if (SSL_CTX_set_tmp_dh(self->ctx, dh) == 0) + _setSSLError(NULL, 0, __FILE__, __LINE__); + DH_free(dh); + Py_RETURN_NONE; +} + +static PyObject * +context_wrap_socket(PySSLContext *self, PyObject *args, PyObject *kwds) +{ + char *kwlist[] = {"sock", "server_side", "server_hostname", "ssl_sock", NULL}; + PySocketSockObject *sock; + int server_side = 0; + char *hostname = NULL; + PyObject *hostname_obj, *ssl_sock = Py_None, *res; + + /* server_hostname is either None (or absent), or to be encoded + using the idna encoding. */ + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!i|O!O:_wrap_socket", kwlist, + PySocketModule.Sock_Type, + &sock, &server_side, + Py_TYPE(Py_None), &hostname_obj, + &ssl_sock)) { + PyErr_Clear(); + if (!PyArg_ParseTupleAndKeywords(args, kwds, "O!iet|O:_wrap_socket", kwlist, + PySocketModule.Sock_Type, + &sock, &server_side, + "idna", &hostname, &ssl_sock)) + return NULL; +#if !HAVE_SNI + PyMem_Free(hostname); + PyErr_SetString(PyExc_ValueError, "server_hostname is not supported " + "by your OpenSSL library"); + return NULL; +#endif + } + + res = (PyObject *) newPySSLSocket(self, sock, server_side, + hostname, ssl_sock); + if (hostname != NULL) + PyMem_Free(hostname); + return res; +} + +static PyObject * +session_stats(PySSLContext *self, PyObject *unused) +{ + int r; + PyObject *value, *stats = PyDict_New(); + if (!stats) + return NULL; + +#define ADD_STATS(SSL_NAME, KEY_NAME) \ + value = PyLong_FromLong(SSL_CTX_sess_ ## SSL_NAME (self->ctx)); \ + if (value == NULL) \ + goto error; \ + r = PyDict_SetItemString(stats, KEY_NAME, value); \ + Py_DECREF(value); \ + if (r < 0) \ + goto error; + + ADD_STATS(number, "number"); + ADD_STATS(connect, "connect"); + ADD_STATS(connect_good, "connect_good"); + ADD_STATS(connect_renegotiate, "connect_renegotiate"); + ADD_STATS(accept, "accept"); + ADD_STATS(accept_good, "accept_good"); + ADD_STATS(accept_renegotiate, "accept_renegotiate"); + ADD_STATS(accept, "accept"); + ADD_STATS(hits, "hits"); + ADD_STATS(misses, "misses"); + ADD_STATS(timeouts, "timeouts"); + ADD_STATS(cache_full, "cache_full"); + +#undef ADD_STATS + + return stats; + +error: + Py_DECREF(stats); + return NULL; +} + +static PyObject * +set_default_verify_paths(PySSLContext *self, PyObject *unused) +{ + if (!SSL_CTX_set_default_verify_paths(self->ctx)) { + _setSSLError(NULL, 0, __FILE__, __LINE__); + return NULL; + } + Py_RETURN_NONE; +} + +#ifndef OPENSSL_NO_ECDH +static PyObject * +set_ecdh_curve(PySSLContext *self, PyObject *name) +{ + char *name_bytes; + int nid; + EC_KEY *key; + + name_bytes = PyBytes_AsString(name); + if (!name_bytes) { + return NULL; + } + nid = OBJ_sn2nid(name_bytes); + if (nid == 0) { + PyErr_Format(PyExc_ValueError, + "unknown elliptic curve name %R", name); + return NULL; + } + key = EC_KEY_new_by_curve_name(nid); + if (key == NULL) { + _setSSLError(NULL, 0, __FILE__, __LINE__); + return NULL; + } + SSL_CTX_set_tmp_ecdh(self->ctx, key); + EC_KEY_free(key); + Py_RETURN_NONE; +} +#endif + +#if HAVE_SNI && !defined(OPENSSL_NO_TLSEXT) +static int +_servername_callback(SSL *s, int *al, void *args) +{ + int ret; + PySSLContext *ssl_ctx = (PySSLContext *) args; + PySSLSocket *ssl; + PyObject *servername_o; + PyObject *servername_idna; + PyObject *result; + /* The high-level ssl.SSLSocket object */ + PyObject *ssl_socket; + const char *servername = SSL_get_servername(s, TLSEXT_NAMETYPE_host_name); +#ifdef WITH_THREAD + PyGILState_STATE gstate = PyGILState_Ensure(); +#endif + + if (ssl_ctx->set_hostname == NULL) { + /* remove race condition in this the call back while if removing the + * callback is in progress */ +#ifdef WITH_THREAD + PyGILState_Release(gstate); +#endif + return SSL_TLSEXT_ERR_OK; + } + + ssl = SSL_get_app_data(s); + assert(PySSLSocket_Check(ssl)); + ssl_socket = PyWeakref_GetObject(ssl->ssl_sock); + Py_INCREF(ssl_socket); + if (ssl_socket == Py_None) { + goto error; + } + + if (servername == NULL) { + result = PyObject_CallFunctionObjArgs(ssl_ctx->set_hostname, ssl_socket, + Py_None, ssl_ctx, NULL); + } + else { + servername_o = PyBytes_FromString(servername); + if (servername_o == NULL) { + PyErr_WriteUnraisable((PyObject *) ssl_ctx); + goto error; + } + servername_idna = PyUnicode_FromEncodedObject(servername_o, "idna", NULL); + if (servername_idna == NULL) { + PyErr_WriteUnraisable(servername_o); + Py_DECREF(servername_o); + goto error; + } + Py_DECREF(servername_o); + result = PyObject_CallFunctionObjArgs(ssl_ctx->set_hostname, ssl_socket, + servername_idna, ssl_ctx, NULL); + Py_DECREF(servername_idna); + } + Py_DECREF(ssl_socket); + + if (result == NULL) { + PyErr_WriteUnraisable(ssl_ctx->set_hostname); + *al = SSL_AD_HANDSHAKE_FAILURE; + ret = SSL_TLSEXT_ERR_ALERT_FATAL; + } + else { + if (result != Py_None) { + *al = (int) PyLong_AsLong(result); + if (PyErr_Occurred()) { + PyErr_WriteUnraisable(result); + *al = SSL_AD_INTERNAL_ERROR; + } + ret = SSL_TLSEXT_ERR_ALERT_FATAL; + } + else { + ret = SSL_TLSEXT_ERR_OK; + } + Py_DECREF(result); + } + +#ifdef WITH_THREAD + PyGILState_Release(gstate); +#endif + return ret; + +error: + Py_DECREF(ssl_socket); + *al = SSL_AD_INTERNAL_ERROR; + ret = SSL_TLSEXT_ERR_ALERT_FATAL; +#ifdef WITH_THREAD + PyGILState_Release(gstate); +#endif + return ret; +} +#endif + +PyDoc_STRVAR(PySSL_set_servername_callback_doc, +"set_servername_callback(method)\n\ +\n\ +This sets a callback that will be called when a server name is provided by\n\ +the SSL/TLS client in the SNI extension.\n\ +\n\ +If the argument is None then the callback is disabled. The method is called\n\ +with the SSLSocket, the server name as a string, and the SSLContext object.\n\ +See RFC 6066 for details of the SNI extension."); + +static PyObject * +set_servername_callback(PySSLContext *self, PyObject *args) +{ +#if HAVE_SNI && !defined(OPENSSL_NO_TLSEXT) + PyObject *cb; + + if (!PyArg_ParseTuple(args, "O", &cb)) + return NULL; + + Py_CLEAR(self->set_hostname); + if (cb == Py_None) { + SSL_CTX_set_tlsext_servername_callback(self->ctx, NULL); + } + else { + if (!PyCallable_Check(cb)) { + SSL_CTX_set_tlsext_servername_callback(self->ctx, NULL); + PyErr_SetString(PyExc_TypeError, + "not a callable object"); + return NULL; + } + Py_INCREF(cb); + self->set_hostname = cb; + SSL_CTX_set_tlsext_servername_callback(self->ctx, _servername_callback); + SSL_CTX_set_tlsext_servername_arg(self->ctx, self); + } + Py_RETURN_NONE; +#else + PyErr_SetString(PyExc_NotImplementedError, + "The TLS extension servername callback, " + "SSL_CTX_set_tlsext_servername_callback, " + "is not in the current OpenSSL library."); + return NULL; +#endif +} + +PyDoc_STRVAR(PySSL_get_stats_doc, +"cert_store_stats() -> {'crl': int, 'x509_ca': int, 'x509': int}\n\ +\n\ +Returns quantities of loaded X.509 certificates. X.509 certificates with a\n\ +CA extension and certificate revocation lists inside the context's cert\n\ +store.\n\ +NOTE: Certificates in a capath directory aren't loaded unless they have\n\ +been used at least once."); + +static PyObject * +cert_store_stats(PySSLContext *self) +{ + X509_STORE *store; + X509_OBJECT *obj; + int x509 = 0, crl = 0, pkey = 0, ca = 0, i; + + store = SSL_CTX_get_cert_store(self->ctx); + for (i = 0; i < sk_X509_OBJECT_num(store->objs); i++) { + obj = sk_X509_OBJECT_value(store->objs, i); + switch (obj->type) { + case X509_LU_X509: + x509++; + if (X509_check_ca(obj->data.x509)) { + ca++; + } + break; + case X509_LU_CRL: + crl++; + break; + case X509_LU_PKEY: + pkey++; + break; + default: + /* Ignore X509_LU_FAIL, X509_LU_RETRY, X509_LU_PKEY. + * As far as I can tell they are internal states and never + * stored in a cert store */ + break; + } + } + return Py_BuildValue("{sisisi}", "x509", x509, "crl", crl, + "x509_ca", ca); +} + +PyDoc_STRVAR(PySSL_get_ca_certs_doc, +"get_ca_certs(binary_form=False) -> list of loaded certificate\n\ +\n\ +Returns a list of dicts with information of loaded CA certs. If the\n\ +optional argument is True, returns a DER-encoded copy of the CA certificate.\n\ +NOTE: Certificates in a capath directory aren't loaded unless they have\n\ +been used at least once."); + +static PyObject * +get_ca_certs(PySSLContext *self, PyObject *args, PyObject *kwds) +{ + char *kwlist[] = {"binary_form", NULL}; + X509_STORE *store; + PyObject *ci = NULL, *rlist = NULL, *py_binary_mode = Py_False; + int i; + int binary_mode = 0; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "|O:get_ca_certs", + kwlist, &py_binary_mode)) { + return NULL; + } + binary_mode = PyObject_IsTrue(py_binary_mode); + if (binary_mode < 0) { + return NULL; + } + + if ((rlist = PyList_New(0)) == NULL) { + return NULL; + } + + store = SSL_CTX_get_cert_store(self->ctx); + for (i = 0; i < sk_X509_OBJECT_num(store->objs); i++) { + X509_OBJECT *obj; + X509 *cert; + + obj = sk_X509_OBJECT_value(store->objs, i); + if (obj->type != X509_LU_X509) { + /* not a x509 cert */ + continue; + } + /* CA for any purpose */ + cert = obj->data.x509; + if (!X509_check_ca(cert)) { + continue; + } + if (binary_mode) { + ci = _certificate_to_der(cert); + } else { + ci = _decode_certificate(cert); + } + if (ci == NULL) { + goto error; + } + if (PyList_Append(rlist, ci) == -1) { + goto error; + } + Py_CLEAR(ci); + } + return rlist; + + error: + Py_XDECREF(ci); + Py_XDECREF(rlist); + return NULL; +} + + +static PyGetSetDef context_getsetlist[] = { + {"check_hostname", (getter) get_check_hostname, + (setter) set_check_hostname, NULL}, + {"options", (getter) get_options, + (setter) set_options, NULL}, +#ifdef HAVE_OPENSSL_VERIFY_PARAM + {"verify_flags", (getter) get_verify_flags, + (setter) set_verify_flags, NULL}, +#endif + {"verify_mode", (getter) get_verify_mode, + (setter) set_verify_mode, NULL}, + {NULL}, /* sentinel */ +}; + +static struct PyMethodDef context_methods[] = { + {"_wrap_socket", (PyCFunction) context_wrap_socket, + METH_VARARGS | METH_KEYWORDS, NULL}, + {"set_ciphers", (PyCFunction) set_ciphers, + METH_VARARGS, NULL}, + {"_set_npn_protocols", (PyCFunction) _set_npn_protocols, + METH_VARARGS, NULL}, + {"load_cert_chain", (PyCFunction) load_cert_chain, + METH_VARARGS | METH_KEYWORDS, NULL}, + {"load_dh_params", (PyCFunction) load_dh_params, + METH_O, NULL}, + {"load_verify_locations", (PyCFunction) load_verify_locations, + METH_VARARGS | METH_KEYWORDS, NULL}, + {"session_stats", (PyCFunction) session_stats, + METH_NOARGS, NULL}, + {"set_default_verify_paths", (PyCFunction) set_default_verify_paths, + METH_NOARGS, NULL}, +#ifndef OPENSSL_NO_ECDH + {"set_ecdh_curve", (PyCFunction) set_ecdh_curve, + METH_O, NULL}, +#endif + {"set_servername_callback", (PyCFunction) set_servername_callback, + METH_VARARGS, PySSL_set_servername_callback_doc}, + {"cert_store_stats", (PyCFunction) cert_store_stats, + METH_NOARGS, PySSL_get_stats_doc}, + {"get_ca_certs", (PyCFunction) get_ca_certs, + METH_VARARGS | METH_KEYWORDS, PySSL_get_ca_certs_doc}, + {NULL, NULL} /* sentinel */ +}; + +static PyTypeObject PySSLContext_Type = { + PyVarObject_HEAD_INIT(NULL, 0) + "_ssl._SSLContext", /*tp_name*/ + sizeof(PySSLContext), /*tp_basicsize*/ + 0, /*tp_itemsize*/ + (destructor)context_dealloc, /*tp_dealloc*/ + 0, /*tp_print*/ + 0, /*tp_getattr*/ + 0, /*tp_setattr*/ + 0, /*tp_reserved*/ + 0, /*tp_repr*/ + 0, /*tp_as_number*/ + 0, /*tp_as_sequence*/ + 0, /*tp_as_mapping*/ + 0, /*tp_hash*/ + 0, /*tp_call*/ + 0, /*tp_str*/ + 0, /*tp_getattro*/ + 0, /*tp_setattro*/ + 0, /*tp_as_buffer*/ + Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE | Py_TPFLAGS_HAVE_GC, /*tp_flags*/ + 0, /*tp_doc*/ + (traverseproc) context_traverse, /*tp_traverse*/ + (inquiry) context_clear, /*tp_clear*/ + 0, /*tp_richcompare*/ + 0, /*tp_weaklistoffset*/ + 0, /*tp_iter*/ + 0, /*tp_iternext*/ + context_methods, /*tp_methods*/ + 0, /*tp_members*/ + context_getsetlist, /*tp_getset*/ + 0, /*tp_base*/ + 0, /*tp_dict*/ + 0, /*tp_descr_get*/ + 0, /*tp_descr_set*/ + 0, /*tp_dictoffset*/ + 0, /*tp_init*/ + 0, /*tp_alloc*/ + context_new, /*tp_new*/ +}; + + + +#ifdef HAVE_OPENSSL_RAND + +/* helper routines for seeding the SSL PRNG */ +static PyObject * +PySSL_RAND_add(PyObject *self, PyObject *args) +{ + char *buf; + Py_ssize_t len, written; + double entropy; + + if (!PyArg_ParseTuple(args, "s#d:RAND_add", &buf, &len, &entropy)) + return NULL; + do { + if (len >= INT_MAX) { + written = INT_MAX; + } else { + written = len; + } + RAND_add(buf, (int)written, entropy); + buf += written; + len -= written; + } while (len); + Py_INCREF(Py_None); + return Py_None; +} + +PyDoc_STRVAR(PySSL_RAND_add_doc, +"RAND_add(string, entropy)\n\ +\n\ +Mix string into the OpenSSL PRNG state. entropy (a float) is a lower\n\ +bound on the entropy contained in string. See RFC 1750."); + +static PyObject * +PySSL_RAND_status(PyObject *self) +{ + return PyLong_FromLong(RAND_status()); } PyDoc_STRVAR(PySSL_RAND_status_doc, @@ -1630,21 +3279,413 @@ fails or if it does not provide enough data to seed PRNG."); #endif /* HAVE_OPENSSL_RAND */ +PyDoc_STRVAR(PySSL_get_default_verify_paths_doc, +"get_default_verify_paths() -> tuple\n\ +\n\ +Return search paths and environment vars that are used by SSLContext's\n\ +set_default_verify_paths() to load default CAs. The values are\n\ +'cert_file_env', 'cert_file', 'cert_dir_env', 'cert_dir'."); + +static PyObject * +PySSL_get_default_verify_paths(PyObject *self) +{ + PyObject *ofile_env = NULL; + PyObject *ofile = NULL; + PyObject *odir_env = NULL; + PyObject *odir = NULL; + +#define convert(info, target) { \ + const char *tmp = (info); \ + target = NULL; \ + if (!tmp) { Py_INCREF(Py_None); target = Py_None; } \ + else { target = PyBytes_FromString(tmp); } \ + if (!target) goto error; \ + } while(0) + + convert(X509_get_default_cert_file_env(), ofile_env); + convert(X509_get_default_cert_file(), ofile); + convert(X509_get_default_cert_dir_env(), odir_env); + convert(X509_get_default_cert_dir(), odir); +#undef convert + + return Py_BuildValue("NNNN", ofile_env, ofile, odir_env, odir); + + error: + Py_XDECREF(ofile_env); + Py_XDECREF(ofile); + Py_XDECREF(odir_env); + Py_XDECREF(odir); + return NULL; +} + +static PyObject* +asn1obj2py(ASN1_OBJECT *obj) +{ + int nid; + const char *ln, *sn; + char buf[100]; + Py_ssize_t buflen; + + nid = OBJ_obj2nid(obj); + if (nid == NID_undef) { + PyErr_Format(PyExc_ValueError, "Unknown object"); + return NULL; + } + sn = OBJ_nid2sn(nid); + ln = OBJ_nid2ln(nid); + buflen = OBJ_obj2txt(buf, sizeof(buf), obj, 1); + if (buflen < 0) { + _setSSLError(NULL, 0, __FILE__, __LINE__); + return NULL; + } + if (buflen) { + return Py_BuildValue("isss#", nid, sn, ln, buf, buflen); + } else { + return Py_BuildValue("issO", nid, sn, ln, Py_None); + } +} + +PyDoc_STRVAR(PySSL_txt2obj_doc, +"txt2obj(txt, name=False) -> (nid, shortname, longname, oid)\n\ +\n\ +Lookup NID, short name, long name and OID of an ASN1_OBJECT. By default\n\ +objects are looked up by OID. With name=True short and long name are also\n\ +matched."); + +static PyObject* +PySSL_txt2obj(PyObject *self, PyObject *args, PyObject *kwds) +{ + char *kwlist[] = {"txt", "name", NULL}; + PyObject *result = NULL; + char *txt; + PyObject *pyname = Py_None; + int name = 0; + ASN1_OBJECT *obj; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|O:txt2obj", + kwlist, &txt, &pyname)) { + return NULL; + } + name = PyObject_IsTrue(pyname); + if (name < 0) + return NULL; + obj = OBJ_txt2obj(txt, name ? 0 : 1); + if (obj == NULL) { + PyErr_Format(PyExc_ValueError, "unknown object '%.100s'", txt); + return NULL; + } + result = asn1obj2py(obj); + ASN1_OBJECT_free(obj); + return result; +} + +PyDoc_STRVAR(PySSL_nid2obj_doc, +"nid2obj(nid) -> (nid, shortname, longname, oid)\n\ +\n\ +Lookup NID, short name, long name and OID of an ASN1_OBJECT by NID."); + +static PyObject* +PySSL_nid2obj(PyObject *self, PyObject *args) +{ + PyObject *result = NULL; + int nid; + ASN1_OBJECT *obj; + + if (!PyArg_ParseTuple(args, "i:nid2obj", &nid)) { + return NULL; + } + if (nid < NID_undef) { + PyErr_SetString(PyExc_ValueError, "NID must be positive."); + return NULL; + } + obj = OBJ_nid2obj(nid); + if (obj == NULL) { + PyErr_Format(PyExc_ValueError, "unknown NID %i", nid); + return NULL; + } + result = asn1obj2py(obj); + ASN1_OBJECT_free(obj); + return result; +} + +#ifdef _MSC_VER + +static PyObject* +certEncodingType(DWORD encodingType) +{ + static PyObject *x509_asn = NULL; + static PyObject *pkcs_7_asn = NULL; + + if (x509_asn == NULL) { + x509_asn = PyUnicode_InternFromString("x509_asn"); + if (x509_asn == NULL) + return NULL; + } + if (pkcs_7_asn == NULL) { + pkcs_7_asn = PyUnicode_InternFromString("pkcs_7_asn"); + if (pkcs_7_asn == NULL) + return NULL; + } + switch(encodingType) { + case X509_ASN_ENCODING: + Py_INCREF(x509_asn); + return x509_asn; + case PKCS_7_ASN_ENCODING: + Py_INCREF(pkcs_7_asn); + return pkcs_7_asn; + default: + return PyLong_FromLong(encodingType); + } +} + +static PyObject* +parseKeyUsage(PCCERT_CONTEXT pCertCtx, DWORD flags) +{ + CERT_ENHKEY_USAGE *usage; + DWORD size, error, i; + PyObject *retval; + + if (!CertGetEnhancedKeyUsage(pCertCtx, flags, NULL, &size)) { + error = GetLastError(); + if (error == CRYPT_E_NOT_FOUND) { + Py_RETURN_TRUE; + } + return PyErr_SetFromWindowsErr(error); + } + + usage = (CERT_ENHKEY_USAGE*)PyMem_Malloc(size); + if (usage == NULL) { + return PyErr_NoMemory(); + } + + /* Now get the actual enhanced usage property */ + if (!CertGetEnhancedKeyUsage(pCertCtx, flags, usage, &size)) { + PyMem_Free(usage); + error = GetLastError(); + if (error == CRYPT_E_NOT_FOUND) { + Py_RETURN_TRUE; + } + return PyErr_SetFromWindowsErr(error); + } + retval = PySet_New(NULL); + if (retval == NULL) { + goto error; + } + for (i = 0; i < usage->cUsageIdentifier; ++i) { + if (usage->rgpszUsageIdentifier[i]) { + PyObject *oid; + int err; + oid = PyUnicode_FromString(usage->rgpszUsageIdentifier[i]); + if (oid == NULL) { + Py_CLEAR(retval); + goto error; + } + err = PySet_Add(retval, oid); + Py_DECREF(oid); + if (err == -1) { + Py_CLEAR(retval); + goto error; + } + } + } + error: + PyMem_Free(usage); + return retval; +} + +PyDoc_STRVAR(PySSL_enum_certificates_doc, +"enum_certificates(store_name) -> []\n\ +\n\ +Retrieve certificates from Windows' cert store. store_name may be one of\n\ +'CA', 'ROOT' or 'MY'. The system may provide more cert storages, too.\n\ +The function returns a list of (bytes, encoding_type, trust) tuples. The\n\ +encoding_type flag can be interpreted with X509_ASN_ENCODING or\n\ +PKCS_7_ASN_ENCODING. The trust setting is either a set of OIDs or the\n\ +boolean True."); + +static PyObject * +PySSL_enum_certificates(PyObject *self, PyObject *args, PyObject *kwds) +{ + char *kwlist[] = {"store_name", NULL}; + char *store_name; + HCERTSTORE hStore = NULL; + PCCERT_CONTEXT pCertCtx = NULL; + PyObject *keyusage = NULL, *cert = NULL, *enc = NULL, *tup = NULL; + PyObject *result = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s:enum_certificates", + kwlist, &store_name)) { + return NULL; + } + result = PyList_New(0); + if (result == NULL) { + return NULL; + } + hStore = CertOpenSystemStore((HCRYPTPROV)NULL, store_name); + if (hStore == NULL) { + Py_DECREF(result); + return PyErr_SetFromWindowsErr(GetLastError()); + } + + while (pCertCtx = CertEnumCertificatesInStore(hStore, pCertCtx)) { + cert = PyBytes_FromStringAndSize((const char*)pCertCtx->pbCertEncoded, + pCertCtx->cbCertEncoded); + if (!cert) { + Py_CLEAR(result); + break; + } + if ((enc = certEncodingType(pCertCtx->dwCertEncodingType)) == NULL) { + Py_CLEAR(result); + break; + } + keyusage = parseKeyUsage(pCertCtx, CERT_FIND_PROP_ONLY_ENHKEY_USAGE_FLAG); + if (keyusage == Py_True) { + Py_DECREF(keyusage); + keyusage = parseKeyUsage(pCertCtx, CERT_FIND_EXT_ONLY_ENHKEY_USAGE_FLAG); + } + if (keyusage == NULL) { + Py_CLEAR(result); + break; + } + if ((tup = PyTuple_New(3)) == NULL) { + Py_CLEAR(result); + break; + } + PyTuple_SET_ITEM(tup, 0, cert); + cert = NULL; + PyTuple_SET_ITEM(tup, 1, enc); + enc = NULL; + PyTuple_SET_ITEM(tup, 2, keyusage); + keyusage = NULL; + if (PyList_Append(result, tup) < 0) { + Py_CLEAR(result); + break; + } + Py_CLEAR(tup); + } + if (pCertCtx) { + /* loop ended with an error, need to clean up context manually */ + CertFreeCertificateContext(pCertCtx); + } + + /* In error cases cert, enc and tup may not be NULL */ + Py_XDECREF(cert); + Py_XDECREF(enc); + Py_XDECREF(keyusage); + Py_XDECREF(tup); + + if (!CertCloseStore(hStore, 0)) { + /* This error case might shadow another exception.*/ + Py_XDECREF(result); + return PyErr_SetFromWindowsErr(GetLastError()); + } + return result; +} + +PyDoc_STRVAR(PySSL_enum_crls_doc, +"enum_crls(store_name) -> []\n\ +\n\ +Retrieve CRLs from Windows' cert store. store_name may be one of\n\ +'CA', 'ROOT' or 'MY'. The system may provide more cert storages, too.\n\ +The function returns a list of (bytes, encoding_type) tuples. The\n\ +encoding_type flag can be interpreted with X509_ASN_ENCODING or\n\ +PKCS_7_ASN_ENCODING."); + +static PyObject * +PySSL_enum_crls(PyObject *self, PyObject *args, PyObject *kwds) +{ + char *kwlist[] = {"store_name", NULL}; + char *store_name; + HCERTSTORE hStore = NULL; + PCCRL_CONTEXT pCrlCtx = NULL; + PyObject *crl = NULL, *enc = NULL, *tup = NULL; + PyObject *result = NULL; + + if (!PyArg_ParseTupleAndKeywords(args, kwds, "s|s:enum_crls", + kwlist, &store_name)) { + return NULL; + } + result = PyList_New(0); + if (result == NULL) { + return NULL; + } + hStore = CertOpenSystemStore((HCRYPTPROV)NULL, store_name); + if (hStore == NULL) { + Py_DECREF(result); + return PyErr_SetFromWindowsErr(GetLastError()); + } + + while (pCrlCtx = CertEnumCRLsInStore(hStore, pCrlCtx)) { + crl = PyBytes_FromStringAndSize((const char*)pCrlCtx->pbCrlEncoded, + pCrlCtx->cbCrlEncoded); + if (!crl) { + Py_CLEAR(result); + break; + } + if ((enc = certEncodingType(pCrlCtx->dwCertEncodingType)) == NULL) { + Py_CLEAR(result); + break; + } + if ((tup = PyTuple_New(2)) == NULL) { + Py_CLEAR(result); + break; + } + PyTuple_SET_ITEM(tup, 0, crl); + crl = NULL; + PyTuple_SET_ITEM(tup, 1, enc); + enc = NULL; + + if (PyList_Append(result, tup) < 0) { + Py_CLEAR(result); + break; + } + Py_CLEAR(tup); + } + if (pCrlCtx) { + /* loop ended with an error, need to clean up context manually */ + CertFreeCRLContext(pCrlCtx); + } + + /* In error cases cert, enc and tup may not be NULL */ + Py_XDECREF(crl); + Py_XDECREF(enc); + Py_XDECREF(tup); + + if (!CertCloseStore(hStore, 0)) { + /* This error case might shadow another exception.*/ + Py_XDECREF(result); + return PyErr_SetFromWindowsErr(GetLastError()); + } + return result; +} + +#endif /* _MSC_VER */ + /* List of functions exported by this module. */ static PyMethodDef PySSL_methods[] = { - {"sslwrap", PySSL_sslwrap, - METH_VARARGS, ssl_doc}, {"_test_decode_cert", PySSL_test_decode_certificate, METH_VARARGS}, #ifdef HAVE_OPENSSL_RAND {"RAND_add", PySSL_RAND_add, METH_VARARGS, PySSL_RAND_add_doc}, - {"RAND_egd", PySSL_RAND_egd, METH_O, + {"RAND_egd", PySSL_RAND_egd, METH_VARARGS, PySSL_RAND_egd_doc}, {"RAND_status", (PyCFunction)PySSL_RAND_status, METH_NOARGS, PySSL_RAND_status_doc}, #endif + {"get_default_verify_paths", (PyCFunction)PySSL_get_default_verify_paths, + METH_NOARGS, PySSL_get_default_verify_paths_doc}, +#ifdef _MSC_VER + {"enum_certificates", (PyCFunction)PySSL_enum_certificates, + METH_VARARGS | METH_KEYWORDS, PySSL_enum_certificates_doc}, + {"enum_crls", (PyCFunction)PySSL_enum_crls, + METH_VARARGS | METH_KEYWORDS, PySSL_enum_crls_doc}, +#endif + {"txt2obj", (PyCFunction)PySSL_txt2obj, + METH_VARARGS | METH_KEYWORDS, PySSL_txt2obj_doc}, + {"nid2obj", (PyCFunction)PySSL_nid2obj, + METH_VARARGS, PySSL_nid2obj_doc}, {NULL, NULL} /* Sentinel */ }; @@ -1672,16 +3713,17 @@ _ssl_thread_id_function (void) { } #endif -static void _ssl_thread_locking_function (int mode, int n, const char *file, int line) { +static void _ssl_thread_locking_function + (int mode, int n, const char *file, int line) { /* this function is needed to perform locking on shared data structures. (Note that OpenSSL uses a number of global data - structures that will be implicitly shared whenever multiple threads - use OpenSSL.) Multi-threaded applications will crash at random if - it is not set. + structures that will be implicitly shared whenever multiple + threads use OpenSSL.) Multi-threaded applications will + crash at random if it is not set. - locking_function() must be able to handle up to CRYPTO_num_locks() - different mutex locks. It sets the n-th lock if mode & CRYPTO_LOCK, and - releases it otherwise. + locking_function() must be able to handle up to + CRYPTO_num_locks() different mutex locks. It sets the n-th + lock if mode & CRYPTO_LOCK, and releases it otherwise. file and line are the file number of the function setting the lock. They can be useful for debugging. @@ -1705,10 +3747,11 @@ static int _setup_ssl_threads(void) { if (_ssl_locks == NULL) { _ssl_locks_count = CRYPTO_num_locks(); _ssl_locks = (PyThread_type_lock *) - malloc(sizeof(PyThread_type_lock) * _ssl_locks_count); + PyMem_Malloc(sizeof(PyThread_type_lock) * _ssl_locks_count); if (_ssl_locks == NULL) return 0; - memset(_ssl_locks, 0, sizeof(PyThread_type_lock) * _ssl_locks_count); + memset(_ssl_locks, 0, + sizeof(PyThread_type_lock) * _ssl_locks_count); for (i = 0; i < _ssl_locks_count; i++) { _ssl_locks[i] = PyThread_allocate_lock(); if (_ssl_locks[i] == NULL) { @@ -1716,7 +3759,7 @@ static int _setup_ssl_threads(void) { for (j = 0; j < i; j++) { PyThread_free_lock(_ssl_locks[j]); } - free(_ssl_locks); + PyMem_Free(_ssl_locks); return 0; } } @@ -1736,14 +3779,39 @@ PyDoc_STRVAR(module_doc, "Implementation module for SSL socket operations. See the socket module\n\ for documentation."); + + + +static void +parse_openssl_version(unsigned long libver, + unsigned int *major, unsigned int *minor, + unsigned int *fix, unsigned int *patch, + unsigned int *status) +{ + *status = libver & 0xF; + libver >>= 4; + *patch = libver & 0xFF; + libver >>= 8; + *fix = libver & 0xFF; + libver >>= 8; + *minor = libver & 0xFF; + libver >>= 8; + *major = libver & 0xFF; +} + PyMODINIT_FUNC init_ssl(void) { PyObject *m, *d, *r; unsigned long libver; unsigned int major, minor, fix, patch, status; + struct py_ssl_error_code *errcode; + struct py_ssl_library_code *libcode; - Py_TYPE(&PySSL_Type) = &PyType_Type; + if (PyType_Ready(&PySSLContext_Type) < 0) + return; + if (PyType_Ready(&PySSLSocket_Type) < 0) + return; m = Py_InitModule3("_ssl", PySSL_methods, module_doc); if (m == NULL) @@ -1766,15 +3834,53 @@ init_ssl(void) OpenSSL_add_all_algorithms(); /* Add symbols to module dict */ - PySSLErrorObject = PyErr_NewException("ssl.SSLError", - PySocketModule.error, - NULL); + PySSLErrorObject = PyErr_NewExceptionWithDoc( + "ssl.SSLError", SSLError_doc, + PySocketModule.error, NULL); if (PySSLErrorObject == NULL) return; - if (PyDict_SetItemString(d, "SSLError", PySSLErrorObject) != 0) + ((PyTypeObject *)PySSLErrorObject)->tp_str = (reprfunc)SSLError_str; + + PySSLZeroReturnErrorObject = PyErr_NewExceptionWithDoc( + "ssl.SSLZeroReturnError", SSLZeroReturnError_doc, + PySSLErrorObject, NULL); + PySSLWantReadErrorObject = PyErr_NewExceptionWithDoc( + "ssl.SSLWantReadError", SSLWantReadError_doc, + PySSLErrorObject, NULL); + PySSLWantWriteErrorObject = PyErr_NewExceptionWithDoc( + "ssl.SSLWantWriteError", SSLWantWriteError_doc, + PySSLErrorObject, NULL); + PySSLSyscallErrorObject = PyErr_NewExceptionWithDoc( + "ssl.SSLSyscallError", SSLSyscallError_doc, + PySSLErrorObject, NULL); + PySSLEOFErrorObject = PyErr_NewExceptionWithDoc( + "ssl.SSLEOFError", SSLEOFError_doc, + PySSLErrorObject, NULL); + if (PySSLZeroReturnErrorObject == NULL + || PySSLWantReadErrorObject == NULL + || PySSLWantWriteErrorObject == NULL + || PySSLSyscallErrorObject == NULL + || PySSLEOFErrorObject == NULL) + return; + + ((PyTypeObject *)PySSLZeroReturnErrorObject)->tp_str = (reprfunc)SSLError_str; + ((PyTypeObject *)PySSLWantReadErrorObject)->tp_str = (reprfunc)SSLError_str; + ((PyTypeObject *)PySSLWantWriteErrorObject)->tp_str = (reprfunc)SSLError_str; + ((PyTypeObject *)PySSLSyscallErrorObject)->tp_str = (reprfunc)SSLError_str; + ((PyTypeObject *)PySSLEOFErrorObject)->tp_str = (reprfunc)SSLError_str; + + if (PyDict_SetItemString(d, "SSLError", PySSLErrorObject) != 0 + || PyDict_SetItemString(d, "SSLZeroReturnError", PySSLZeroReturnErrorObject) != 0 + || PyDict_SetItemString(d, "SSLWantReadError", PySSLWantReadErrorObject) != 0 + || PyDict_SetItemString(d, "SSLWantWriteError", PySSLWantWriteErrorObject) != 0 + || PyDict_SetItemString(d, "SSLSyscallError", PySSLSyscallErrorObject) != 0 + || PyDict_SetItemString(d, "SSLEOFError", PySSLEOFErrorObject) != 0) + return; + if (PyDict_SetItemString(d, "_SSLContext", + (PyObject *)&PySSLContext_Type) != 0) return; - if (PyDict_SetItemString(d, "SSLType", - (PyObject *)&PySSL_Type) != 0) + if (PyDict_SetItemString(d, "_SSLSocket", + (PyObject *)&PySSLSocket_Type) != 0) return; PyModule_AddIntConstant(m, "SSL_ERROR_ZERO_RETURN", PY_SSL_ERROR_ZERO_RETURN); @@ -1802,6 +3908,66 @@ init_ssl(void) PY_SSL_CERT_OPTIONAL); PyModule_AddIntConstant(m, "CERT_REQUIRED", PY_SSL_CERT_REQUIRED); + /* CRL verification for verification_flags */ + PyModule_AddIntConstant(m, "VERIFY_DEFAULT", + 0); + PyModule_AddIntConstant(m, "VERIFY_CRL_CHECK_LEAF", + X509_V_FLAG_CRL_CHECK); + PyModule_AddIntConstant(m, "VERIFY_CRL_CHECK_CHAIN", + X509_V_FLAG_CRL_CHECK|X509_V_FLAG_CRL_CHECK_ALL); + PyModule_AddIntConstant(m, "VERIFY_X509_STRICT", + X509_V_FLAG_X509_STRICT); + + /* Alert Descriptions from ssl.h */ + /* note RESERVED constants no longer intended for use have been removed */ + /* http://www.iana.org/assignments/tls-parameters/tls-parameters.xml#tls-parameters-6 */ + +#define ADD_AD_CONSTANT(s) \ + PyModule_AddIntConstant(m, "ALERT_DESCRIPTION_"#s, \ + SSL_AD_##s) + + ADD_AD_CONSTANT(CLOSE_NOTIFY); + ADD_AD_CONSTANT(UNEXPECTED_MESSAGE); + ADD_AD_CONSTANT(BAD_RECORD_MAC); + ADD_AD_CONSTANT(RECORD_OVERFLOW); + ADD_AD_CONSTANT(DECOMPRESSION_FAILURE); + ADD_AD_CONSTANT(HANDSHAKE_FAILURE); + ADD_AD_CONSTANT(BAD_CERTIFICATE); + ADD_AD_CONSTANT(UNSUPPORTED_CERTIFICATE); + ADD_AD_CONSTANT(CERTIFICATE_REVOKED); + ADD_AD_CONSTANT(CERTIFICATE_EXPIRED); + ADD_AD_CONSTANT(CERTIFICATE_UNKNOWN); + ADD_AD_CONSTANT(ILLEGAL_PARAMETER); + ADD_AD_CONSTANT(UNKNOWN_CA); + ADD_AD_CONSTANT(ACCESS_DENIED); + ADD_AD_CONSTANT(DECODE_ERROR); + ADD_AD_CONSTANT(DECRYPT_ERROR); + ADD_AD_CONSTANT(PROTOCOL_VERSION); + ADD_AD_CONSTANT(INSUFFICIENT_SECURITY); + ADD_AD_CONSTANT(INTERNAL_ERROR); + ADD_AD_CONSTANT(USER_CANCELLED); + ADD_AD_CONSTANT(NO_RENEGOTIATION); + /* Not all constants are in old OpenSSL versions */ +#ifdef SSL_AD_UNSUPPORTED_EXTENSION + ADD_AD_CONSTANT(UNSUPPORTED_EXTENSION); +#endif +#ifdef SSL_AD_CERTIFICATE_UNOBTAINABLE + ADD_AD_CONSTANT(CERTIFICATE_UNOBTAINABLE); +#endif +#ifdef SSL_AD_UNRECOGNIZED_NAME + ADD_AD_CONSTANT(UNRECOGNIZED_NAME); +#endif +#ifdef SSL_AD_BAD_CERTIFICATE_STATUS_RESPONSE + ADD_AD_CONSTANT(BAD_CERTIFICATE_STATUS_RESPONSE); +#endif +#ifdef SSL_AD_BAD_CERTIFICATE_HASH_VALUE + ADD_AD_CONSTANT(BAD_CERTIFICATE_HASH_VALUE); +#endif +#ifdef SSL_AD_UNKNOWN_PSK_IDENTITY + ADD_AD_CONSTANT(UNKNOWN_PSK_IDENTITY); +#endif + +#undef ADD_AD_CONSTANT /* protocol versions */ #ifndef OPENSSL_NO_SSL2 @@ -1814,6 +3980,109 @@ init_ssl(void) PY_SSL_VERSION_SSL23); PyModule_AddIntConstant(m, "PROTOCOL_TLSv1", PY_SSL_VERSION_TLS1); +#if HAVE_TLSv1_2 + PyModule_AddIntConstant(m, "PROTOCOL_TLSv1_1", + PY_SSL_VERSION_TLS1_1); + PyModule_AddIntConstant(m, "PROTOCOL_TLSv1_2", + PY_SSL_VERSION_TLS1_2); +#endif + + /* protocol options */ + PyModule_AddIntConstant(m, "OP_ALL", + SSL_OP_ALL & ~SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS); + PyModule_AddIntConstant(m, "OP_NO_SSLv2", SSL_OP_NO_SSLv2); + PyModule_AddIntConstant(m, "OP_NO_SSLv3", SSL_OP_NO_SSLv3); + PyModule_AddIntConstant(m, "OP_NO_TLSv1", SSL_OP_NO_TLSv1); +#if HAVE_TLSv1_2 + PyModule_AddIntConstant(m, "OP_NO_TLSv1_1", SSL_OP_NO_TLSv1_1); + PyModule_AddIntConstant(m, "OP_NO_TLSv1_2", SSL_OP_NO_TLSv1_2); +#endif + PyModule_AddIntConstant(m, "OP_CIPHER_SERVER_PREFERENCE", + SSL_OP_CIPHER_SERVER_PREFERENCE); + PyModule_AddIntConstant(m, "OP_SINGLE_DH_USE", SSL_OP_SINGLE_DH_USE); +#ifdef SSL_OP_SINGLE_ECDH_USE + PyModule_AddIntConstant(m, "OP_SINGLE_ECDH_USE", SSL_OP_SINGLE_ECDH_USE); +#endif +#ifdef SSL_OP_NO_COMPRESSION + PyModule_AddIntConstant(m, "OP_NO_COMPRESSION", + SSL_OP_NO_COMPRESSION); +#endif + +#if HAVE_SNI + r = Py_True; +#else + r = Py_False; +#endif + Py_INCREF(r); + PyModule_AddObject(m, "HAS_SNI", r); + +#if HAVE_OPENSSL_FINISHED + r = Py_True; +#else + r = Py_False; +#endif + Py_INCREF(r); + PyModule_AddObject(m, "HAS_TLS_UNIQUE", r); + +#ifdef OPENSSL_NO_ECDH + r = Py_False; +#else + r = Py_True; +#endif + Py_INCREF(r); + PyModule_AddObject(m, "HAS_ECDH", r); + +#ifdef OPENSSL_NPN_NEGOTIATED + r = Py_True; +#else + r = Py_False; +#endif + Py_INCREF(r); + PyModule_AddObject(m, "HAS_NPN", r); + + /* Mappings for error codes */ + err_codes_to_names = PyDict_New(); + err_names_to_codes = PyDict_New(); + if (err_codes_to_names == NULL || err_names_to_codes == NULL) + return; + errcode = error_codes; + while (errcode->mnemonic != NULL) { + PyObject *mnemo, *key; + mnemo = PyUnicode_FromString(errcode->mnemonic); + key = Py_BuildValue("ii", errcode->library, errcode->reason); + if (mnemo == NULL || key == NULL) + return; + if (PyDict_SetItem(err_codes_to_names, key, mnemo)) + return; + if (PyDict_SetItem(err_names_to_codes, mnemo, key)) + return; + Py_DECREF(key); + Py_DECREF(mnemo); + errcode++; + } + if (PyModule_AddObject(m, "err_codes_to_names", err_codes_to_names)) + return; + if (PyModule_AddObject(m, "err_names_to_codes", err_names_to_codes)) + return; + + lib_codes_to_names = PyDict_New(); + if (lib_codes_to_names == NULL) + return; + libcode = library_codes; + while (libcode->library != NULL) { + PyObject *mnemo, *key; + key = PyLong_FromLong(libcode->code); + mnemo = PyUnicode_FromString(libcode->library); + if (key == NULL || mnemo == NULL) + return; + if (PyDict_SetItem(lib_codes_to_names, key, mnemo)) + return; + Py_DECREF(key); + Py_DECREF(mnemo); + libcode++; + } + if (PyModule_AddObject(m, "lib_codes_to_names", lib_codes_to_names)) + return; /* OpenSSL version */ /* SSLeay() gives us the version of the library linked against, @@ -1825,15 +4094,7 @@ init_ssl(void) return; if (PyModule_AddObject(m, "OPENSSL_VERSION_NUMBER", r)) return; - status = libver & 0xF; - libver >>= 4; - patch = libver & 0xFF; - libver >>= 8; - fix = libver & 0xFF; - libver >>= 8; - minor = libver & 0xFF; - libver >>= 8; - major = libver & 0xFF; + parse_openssl_version(libver, &major, &minor, &fix, &patch, &status); r = Py_BuildValue("IIIII", major, minor, fix, patch, status); if (r == NULL || PyModule_AddObject(m, "OPENSSL_VERSION_INFO", r)) return; @@ -1841,4 +4102,9 @@ init_ssl(void) if (r == NULL || PyModule_AddObject(m, "OPENSSL_VERSION", r)) return; + libver = OPENSSL_VERSION_NUMBER; + parse_openssl_version(libver, &major, &minor, &fix, &patch, &status); + r = Py_BuildValue("IIIII", major, minor, fix, patch, status); + if (r == NULL || PyModule_AddObject(m, "_OPENSSL_API_VERSION", r)) + return; } diff --git a/Modules/_ssl_data.h b/Modules/_ssl_data.h new file mode 100644 index 0000000..81a8d7b --- /dev/null +++ b/Modules/_ssl_data.h @@ -0,0 +1,1653 @@ +/* File generated by Tools/ssl/make_ssl_data.py */ +/* Generated on 2012-05-16T23:56:40.981382 */ + +static struct py_ssl_library_code library_codes[] = { + {"PEM", ERR_LIB_PEM}, + {"SSL", ERR_LIB_SSL}, + {"X509", ERR_LIB_X509}, + { NULL } +}; + +static struct py_ssl_error_code error_codes[] = { + #ifdef PEM_R_BAD_BASE64_DECODE + {"BAD_BASE64_DECODE", ERR_LIB_PEM, PEM_R_BAD_BASE64_DECODE}, + #else + {"BAD_BASE64_DECODE", ERR_LIB_PEM, 100}, + #endif + #ifdef PEM_R_BAD_DECRYPT + {"BAD_DECRYPT", ERR_LIB_PEM, PEM_R_BAD_DECRYPT}, + #else + {"BAD_DECRYPT", ERR_LIB_PEM, 101}, + #endif + #ifdef PEM_R_BAD_END_LINE + {"BAD_END_LINE", ERR_LIB_PEM, PEM_R_BAD_END_LINE}, + #else + {"BAD_END_LINE", ERR_LIB_PEM, 102}, + #endif + #ifdef PEM_R_BAD_IV_CHARS + {"BAD_IV_CHARS", ERR_LIB_PEM, PEM_R_BAD_IV_CHARS}, + #else + {"BAD_IV_CHARS", ERR_LIB_PEM, 103}, + #endif + #ifdef PEM_R_BAD_MAGIC_NUMBER + {"BAD_MAGIC_NUMBER", ERR_LIB_PEM, PEM_R_BAD_MAGIC_NUMBER}, + #else + {"BAD_MAGIC_NUMBER", ERR_LIB_PEM, 116}, + #endif + #ifdef PEM_R_BAD_PASSWORD_READ + {"BAD_PASSWORD_READ", ERR_LIB_PEM, PEM_R_BAD_PASSWORD_READ}, + #else + {"BAD_PASSWORD_READ", ERR_LIB_PEM, 104}, + #endif + #ifdef PEM_R_BAD_VERSION_NUMBER + {"BAD_VERSION_NUMBER", ERR_LIB_PEM, PEM_R_BAD_VERSION_NUMBER}, + #else + {"BAD_VERSION_NUMBER", ERR_LIB_PEM, 117}, + #endif + #ifdef PEM_R_BIO_WRITE_FAILURE + {"BIO_WRITE_FAILURE", ERR_LIB_PEM, PEM_R_BIO_WRITE_FAILURE}, + #else + {"BIO_WRITE_FAILURE", ERR_LIB_PEM, 118}, + #endif + #ifdef PEM_R_CIPHER_IS_NULL + {"CIPHER_IS_NULL", ERR_LIB_PEM, PEM_R_CIPHER_IS_NULL}, + #else + {"CIPHER_IS_NULL", ERR_LIB_PEM, 127}, + #endif + #ifdef PEM_R_ERROR_CONVERTING_PRIVATE_KEY + {"ERROR_CONVERTING_PRIVATE_KEY", ERR_LIB_PEM, PEM_R_ERROR_CONVERTING_PRIVATE_KEY}, + #else + {"ERROR_CONVERTING_PRIVATE_KEY", ERR_LIB_PEM, 115}, + #endif + #ifdef PEM_R_EXPECTING_PRIVATE_KEY_BLOB + {"EXPECTING_PRIVATE_KEY_BLOB", ERR_LIB_PEM, PEM_R_EXPECTING_PRIVATE_KEY_BLOB}, + #else + {"EXPECTING_PRIVATE_KEY_BLOB", ERR_LIB_PEM, 119}, + #endif + #ifdef PEM_R_EXPECTING_PUBLIC_KEY_BLOB + {"EXPECTING_PUBLIC_KEY_BLOB", ERR_LIB_PEM, PEM_R_EXPECTING_PUBLIC_KEY_BLOB}, + #else + {"EXPECTING_PUBLIC_KEY_BLOB", ERR_LIB_PEM, 120}, + #endif + #ifdef PEM_R_INCONSISTENT_HEADER + {"INCONSISTENT_HEADER", ERR_LIB_PEM, PEM_R_INCONSISTENT_HEADER}, + #else + {"INCONSISTENT_HEADER", ERR_LIB_PEM, 121}, + #endif + #ifdef PEM_R_KEYBLOB_HEADER_PARSE_ERROR + {"KEYBLOB_HEADER_PARSE_ERROR", ERR_LIB_PEM, PEM_R_KEYBLOB_HEADER_PARSE_ERROR}, + #else + {"KEYBLOB_HEADER_PARSE_ERROR", ERR_LIB_PEM, 122}, + #endif + #ifdef PEM_R_KEYBLOB_TOO_SHORT + {"KEYBLOB_TOO_SHORT", ERR_LIB_PEM, PEM_R_KEYBLOB_TOO_SHORT}, + #else + {"KEYBLOB_TOO_SHORT", ERR_LIB_PEM, 123}, + #endif + #ifdef PEM_R_NOT_DEK_INFO + {"NOT_DEK_INFO", ERR_LIB_PEM, PEM_R_NOT_DEK_INFO}, + #else + {"NOT_DEK_INFO", ERR_LIB_PEM, 105}, + #endif + #ifdef PEM_R_NOT_ENCRYPTED + {"NOT_ENCRYPTED", ERR_LIB_PEM, PEM_R_NOT_ENCRYPTED}, + #else + {"NOT_ENCRYPTED", ERR_LIB_PEM, 106}, + #endif + #ifdef PEM_R_NOT_PROC_TYPE + {"NOT_PROC_TYPE", ERR_LIB_PEM, PEM_R_NOT_PROC_TYPE}, + #else + {"NOT_PROC_TYPE", ERR_LIB_PEM, 107}, + #endif + #ifdef PEM_R_NO_START_LINE + {"NO_START_LINE", ERR_LIB_PEM, PEM_R_NO_START_LINE}, + #else + {"NO_START_LINE", ERR_LIB_PEM, 108}, + #endif + #ifdef PEM_R_PROBLEMS_GETTING_PASSWORD + {"PROBLEMS_GETTING_PASSWORD", ERR_LIB_PEM, PEM_R_PROBLEMS_GETTING_PASSWORD}, + #else + {"PROBLEMS_GETTING_PASSWORD", ERR_LIB_PEM, 109}, + #endif + #ifdef PEM_R_PUBLIC_KEY_NO_RSA + {"PUBLIC_KEY_NO_RSA", ERR_LIB_PEM, PEM_R_PUBLIC_KEY_NO_RSA}, + #else + {"PUBLIC_KEY_NO_RSA", ERR_LIB_PEM, 110}, + #endif + #ifdef PEM_R_PVK_DATA_TOO_SHORT + {"PVK_DATA_TOO_SHORT", ERR_LIB_PEM, PEM_R_PVK_DATA_TOO_SHORT}, + #else + {"PVK_DATA_TOO_SHORT", ERR_LIB_PEM, 124}, + #endif + #ifdef PEM_R_PVK_TOO_SHORT + {"PVK_TOO_SHORT", ERR_LIB_PEM, PEM_R_PVK_TOO_SHORT}, + #else + {"PVK_TOO_SHORT", ERR_LIB_PEM, 125}, + #endif + #ifdef PEM_R_READ_KEY + {"READ_KEY", ERR_LIB_PEM, PEM_R_READ_KEY}, + #else + {"READ_KEY", ERR_LIB_PEM, 111}, + #endif + #ifdef PEM_R_SHORT_HEADER + {"SHORT_HEADER", ERR_LIB_PEM, PEM_R_SHORT_HEADER}, + #else + {"SHORT_HEADER", ERR_LIB_PEM, 112}, + #endif + #ifdef PEM_R_UNSUPPORTED_CIPHER + {"UNSUPPORTED_CIPHER", ERR_LIB_PEM, PEM_R_UNSUPPORTED_CIPHER}, + #else + {"UNSUPPORTED_CIPHER", ERR_LIB_PEM, 113}, + #endif + #ifdef PEM_R_UNSUPPORTED_ENCRYPTION + {"UNSUPPORTED_ENCRYPTION", ERR_LIB_PEM, PEM_R_UNSUPPORTED_ENCRYPTION}, + #else + {"UNSUPPORTED_ENCRYPTION", ERR_LIB_PEM, 114}, + #endif + #ifdef PEM_R_UNSUPPORTED_KEY_COMPONENTS + {"UNSUPPORTED_KEY_COMPONENTS", ERR_LIB_PEM, PEM_R_UNSUPPORTED_KEY_COMPONENTS}, + #else + {"UNSUPPORTED_KEY_COMPONENTS", ERR_LIB_PEM, 126}, + #endif + #ifdef SSL_R_APP_DATA_IN_HANDSHAKE + {"APP_DATA_IN_HANDSHAKE", ERR_LIB_SSL, SSL_R_APP_DATA_IN_HANDSHAKE}, + #else + {"APP_DATA_IN_HANDSHAKE", ERR_LIB_SSL, 100}, + #endif + #ifdef SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT + {"ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT", ERR_LIB_SSL, SSL_R_ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT}, + #else + {"ATTEMPT_TO_REUSE_SESSION_IN_DIFFERENT_CONTEXT", ERR_LIB_SSL, 272}, + #endif + #ifdef SSL_R_BAD_ALERT_RECORD + {"BAD_ALERT_RECORD", ERR_LIB_SSL, SSL_R_BAD_ALERT_RECORD}, + #else + {"BAD_ALERT_RECORD", ERR_LIB_SSL, 101}, + #endif + #ifdef SSL_R_BAD_AUTHENTICATION_TYPE + {"BAD_AUTHENTICATION_TYPE", ERR_LIB_SSL, SSL_R_BAD_AUTHENTICATION_TYPE}, + #else + {"BAD_AUTHENTICATION_TYPE", ERR_LIB_SSL, 102}, + #endif + #ifdef SSL_R_BAD_CHANGE_CIPHER_SPEC + {"BAD_CHANGE_CIPHER_SPEC", ERR_LIB_SSL, SSL_R_BAD_CHANGE_CIPHER_SPEC}, + #else + {"BAD_CHANGE_CIPHER_SPEC", ERR_LIB_SSL, 103}, + #endif + #ifdef SSL_R_BAD_CHECKSUM + {"BAD_CHECKSUM", ERR_LIB_SSL, SSL_R_BAD_CHECKSUM}, + #else + {"BAD_CHECKSUM", ERR_LIB_SSL, 104}, + #endif + #ifdef SSL_R_BAD_DATA_RETURNED_BY_CALLBACK + {"BAD_DATA_RETURNED_BY_CALLBACK", ERR_LIB_SSL, SSL_R_BAD_DATA_RETURNED_BY_CALLBACK}, + #else + {"BAD_DATA_RETURNED_BY_CALLBACK", ERR_LIB_SSL, 106}, + #endif + #ifdef SSL_R_BAD_DECOMPRESSION + {"BAD_DECOMPRESSION", ERR_LIB_SSL, SSL_R_BAD_DECOMPRESSION}, + #else + {"BAD_DECOMPRESSION", ERR_LIB_SSL, 107}, + #endif + #ifdef SSL_R_BAD_DH_G_LENGTH + {"BAD_DH_G_LENGTH", ERR_LIB_SSL, SSL_R_BAD_DH_G_LENGTH}, + #else + {"BAD_DH_G_LENGTH", ERR_LIB_SSL, 108}, + #endif + #ifdef SSL_R_BAD_DH_PUB_KEY_LENGTH + {"BAD_DH_PUB_KEY_LENGTH", ERR_LIB_SSL, SSL_R_BAD_DH_PUB_KEY_LENGTH}, + #else + {"BAD_DH_PUB_KEY_LENGTH", ERR_LIB_SSL, 109}, + #endif + #ifdef SSL_R_BAD_DH_P_LENGTH + {"BAD_DH_P_LENGTH", ERR_LIB_SSL, SSL_R_BAD_DH_P_LENGTH}, + #else + {"BAD_DH_P_LENGTH", ERR_LIB_SSL, 110}, + #endif + #ifdef SSL_R_BAD_DIGEST_LENGTH + {"BAD_DIGEST_LENGTH", ERR_LIB_SSL, SSL_R_BAD_DIGEST_LENGTH}, + #else + {"BAD_DIGEST_LENGTH", ERR_LIB_SSL, 111}, + #endif + #ifdef SSL_R_BAD_DSA_SIGNATURE + {"BAD_DSA_SIGNATURE", ERR_LIB_SSL, SSL_R_BAD_DSA_SIGNATURE}, + #else + {"BAD_DSA_SIGNATURE", ERR_LIB_SSL, 112}, + #endif + #ifdef SSL_R_BAD_ECC_CERT + {"BAD_ECC_CERT", ERR_LIB_SSL, SSL_R_BAD_ECC_CERT}, + #else + {"BAD_ECC_CERT", ERR_LIB_SSL, 304}, + #endif + #ifdef SSL_R_BAD_ECDSA_SIGNATURE + {"BAD_ECDSA_SIGNATURE", ERR_LIB_SSL, SSL_R_BAD_ECDSA_SIGNATURE}, + #else + {"BAD_ECDSA_SIGNATURE", ERR_LIB_SSL, 305}, + #endif + #ifdef SSL_R_BAD_ECPOINT + {"BAD_ECPOINT", ERR_LIB_SSL, SSL_R_BAD_ECPOINT}, + #else + {"BAD_ECPOINT", ERR_LIB_SSL, 306}, + #endif + #ifdef SSL_R_BAD_HANDSHAKE_LENGTH + {"BAD_HANDSHAKE_LENGTH", ERR_LIB_SSL, SSL_R_BAD_HANDSHAKE_LENGTH}, + #else + {"BAD_HANDSHAKE_LENGTH", ERR_LIB_SSL, 332}, + #endif + #ifdef SSL_R_BAD_HELLO_REQUEST + {"BAD_HELLO_REQUEST", ERR_LIB_SSL, SSL_R_BAD_HELLO_REQUEST}, + #else + {"BAD_HELLO_REQUEST", ERR_LIB_SSL, 105}, + #endif + #ifdef SSL_R_BAD_LENGTH + {"BAD_LENGTH", ERR_LIB_SSL, SSL_R_BAD_LENGTH}, + #else + {"BAD_LENGTH", ERR_LIB_SSL, 271}, + #endif + #ifdef SSL_R_BAD_MAC_DECODE + {"BAD_MAC_DECODE", ERR_LIB_SSL, SSL_R_BAD_MAC_DECODE}, + #else + {"BAD_MAC_DECODE", ERR_LIB_SSL, 113}, + #endif + #ifdef SSL_R_BAD_MAC_LENGTH + {"BAD_MAC_LENGTH", ERR_LIB_SSL, SSL_R_BAD_MAC_LENGTH}, + #else + {"BAD_MAC_LENGTH", ERR_LIB_SSL, 333}, + #endif + #ifdef SSL_R_BAD_MESSAGE_TYPE + {"BAD_MESSAGE_TYPE", ERR_LIB_SSL, SSL_R_BAD_MESSAGE_TYPE}, + #else + {"BAD_MESSAGE_TYPE", ERR_LIB_SSL, 114}, + #endif + #ifdef SSL_R_BAD_PACKET_LENGTH + {"BAD_PACKET_LENGTH", ERR_LIB_SSL, SSL_R_BAD_PACKET_LENGTH}, + #else + {"BAD_PACKET_LENGTH", ERR_LIB_SSL, 115}, + #endif + #ifdef SSL_R_BAD_PROTOCOL_VERSION_NUMBER + {"BAD_PROTOCOL_VERSION_NUMBER", ERR_LIB_SSL, SSL_R_BAD_PROTOCOL_VERSION_NUMBER}, + #else + {"BAD_PROTOCOL_VERSION_NUMBER", ERR_LIB_SSL, 116}, + #endif + #ifdef SSL_R_BAD_PSK_IDENTITY_HINT_LENGTH + {"BAD_PSK_IDENTITY_HINT_LENGTH", ERR_LIB_SSL, SSL_R_BAD_PSK_IDENTITY_HINT_LENGTH}, + #else + {"BAD_PSK_IDENTITY_HINT_LENGTH", ERR_LIB_SSL, 316}, + #endif + #ifdef SSL_R_BAD_RESPONSE_ARGUMENT + {"BAD_RESPONSE_ARGUMENT", ERR_LIB_SSL, SSL_R_BAD_RESPONSE_ARGUMENT}, + #else + {"BAD_RESPONSE_ARGUMENT", ERR_LIB_SSL, 117}, + #endif + #ifdef SSL_R_BAD_RSA_DECRYPT + {"BAD_RSA_DECRYPT", ERR_LIB_SSL, SSL_R_BAD_RSA_DECRYPT}, + #else + {"BAD_RSA_DECRYPT", ERR_LIB_SSL, 118}, + #endif + #ifdef SSL_R_BAD_RSA_ENCRYPT + {"BAD_RSA_ENCRYPT", ERR_LIB_SSL, SSL_R_BAD_RSA_ENCRYPT}, + #else + {"BAD_RSA_ENCRYPT", ERR_LIB_SSL, 119}, + #endif + #ifdef SSL_R_BAD_RSA_E_LENGTH + {"BAD_RSA_E_LENGTH", ERR_LIB_SSL, SSL_R_BAD_RSA_E_LENGTH}, + #else + {"BAD_RSA_E_LENGTH", ERR_LIB_SSL, 120}, + #endif + #ifdef SSL_R_BAD_RSA_MODULUS_LENGTH + {"BAD_RSA_MODULUS_LENGTH", ERR_LIB_SSL, SSL_R_BAD_RSA_MODULUS_LENGTH}, + #else + {"BAD_RSA_MODULUS_LENGTH", ERR_LIB_SSL, 121}, + #endif + #ifdef SSL_R_BAD_RSA_SIGNATURE + {"BAD_RSA_SIGNATURE", ERR_LIB_SSL, SSL_R_BAD_RSA_SIGNATURE}, + #else + {"BAD_RSA_SIGNATURE", ERR_LIB_SSL, 122}, + #endif + #ifdef SSL_R_BAD_SIGNATURE + {"BAD_SIGNATURE", ERR_LIB_SSL, SSL_R_BAD_SIGNATURE}, + #else + {"BAD_SIGNATURE", ERR_LIB_SSL, 123}, + #endif + #ifdef SSL_R_BAD_SSL_FILETYPE + {"BAD_SSL_FILETYPE", ERR_LIB_SSL, SSL_R_BAD_SSL_FILETYPE}, + #else + {"BAD_SSL_FILETYPE", ERR_LIB_SSL, 124}, + #endif + #ifdef SSL_R_BAD_SSL_SESSION_ID_LENGTH + {"BAD_SSL_SESSION_ID_LENGTH", ERR_LIB_SSL, SSL_R_BAD_SSL_SESSION_ID_LENGTH}, + #else + {"BAD_SSL_SESSION_ID_LENGTH", ERR_LIB_SSL, 125}, + #endif + #ifdef SSL_R_BAD_STATE + {"BAD_STATE", ERR_LIB_SSL, SSL_R_BAD_STATE}, + #else + {"BAD_STATE", ERR_LIB_SSL, 126}, + #endif + #ifdef SSL_R_BAD_WRITE_RETRY + {"BAD_WRITE_RETRY", ERR_LIB_SSL, SSL_R_BAD_WRITE_RETRY}, + #else + {"BAD_WRITE_RETRY", ERR_LIB_SSL, 127}, + #endif + #ifdef SSL_R_BIO_NOT_SET + {"BIO_NOT_SET", ERR_LIB_SSL, SSL_R_BIO_NOT_SET}, + #else + {"BIO_NOT_SET", ERR_LIB_SSL, 128}, + #endif + #ifdef SSL_R_BLOCK_CIPHER_PAD_IS_WRONG + {"BLOCK_CIPHER_PAD_IS_WRONG", ERR_LIB_SSL, SSL_R_BLOCK_CIPHER_PAD_IS_WRONG}, + #else + {"BLOCK_CIPHER_PAD_IS_WRONG", ERR_LIB_SSL, 129}, + #endif + #ifdef SSL_R_BN_LIB + {"BN_LIB", ERR_LIB_SSL, SSL_R_BN_LIB}, + #else + {"BN_LIB", ERR_LIB_SSL, 130}, + #endif + #ifdef SSL_R_CA_DN_LENGTH_MISMATCH + {"CA_DN_LENGTH_MISMATCH", ERR_LIB_SSL, SSL_R_CA_DN_LENGTH_MISMATCH}, + #else + {"CA_DN_LENGTH_MISMATCH", ERR_LIB_SSL, 131}, + #endif + #ifdef SSL_R_CA_DN_TOO_LONG + {"CA_DN_TOO_LONG", ERR_LIB_SSL, SSL_R_CA_DN_TOO_LONG}, + #else + {"CA_DN_TOO_LONG", ERR_LIB_SSL, 132}, + #endif + #ifdef SSL_R_CCS_RECEIVED_EARLY + {"CCS_RECEIVED_EARLY", ERR_LIB_SSL, SSL_R_CCS_RECEIVED_EARLY}, + #else + {"CCS_RECEIVED_EARLY", ERR_LIB_SSL, 133}, + #endif + #ifdef SSL_R_CERTIFICATE_VERIFY_FAILED + {"CERTIFICATE_VERIFY_FAILED", ERR_LIB_SSL, SSL_R_CERTIFICATE_VERIFY_FAILED}, + #else + {"CERTIFICATE_VERIFY_FAILED", ERR_LIB_SSL, 134}, + #endif + #ifdef SSL_R_CERT_LENGTH_MISMATCH + {"CERT_LENGTH_MISMATCH", ERR_LIB_SSL, SSL_R_CERT_LENGTH_MISMATCH}, + #else + {"CERT_LENGTH_MISMATCH", ERR_LIB_SSL, 135}, + #endif + #ifdef SSL_R_CHALLENGE_IS_DIFFERENT + {"CHALLENGE_IS_DIFFERENT", ERR_LIB_SSL, SSL_R_CHALLENGE_IS_DIFFERENT}, + #else + {"CHALLENGE_IS_DIFFERENT", ERR_LIB_SSL, 136}, + #endif + #ifdef SSL_R_CIPHER_CODE_WRONG_LENGTH + {"CIPHER_CODE_WRONG_LENGTH", ERR_LIB_SSL, SSL_R_CIPHER_CODE_WRONG_LENGTH}, + #else + {"CIPHER_CODE_WRONG_LENGTH", ERR_LIB_SSL, 137}, + #endif + #ifdef SSL_R_CIPHER_OR_HASH_UNAVAILABLE + {"CIPHER_OR_HASH_UNAVAILABLE", ERR_LIB_SSL, SSL_R_CIPHER_OR_HASH_UNAVAILABLE}, + #else + {"CIPHER_OR_HASH_UNAVAILABLE", ERR_LIB_SSL, 138}, + #endif + #ifdef SSL_R_CIPHER_TABLE_SRC_ERROR + {"CIPHER_TABLE_SRC_ERROR", ERR_LIB_SSL, SSL_R_CIPHER_TABLE_SRC_ERROR}, + #else + {"CIPHER_TABLE_SRC_ERROR", ERR_LIB_SSL, 139}, + #endif + #ifdef SSL_R_CLIENTHELLO_TLSEXT + {"CLIENTHELLO_TLSEXT", ERR_LIB_SSL, SSL_R_CLIENTHELLO_TLSEXT}, + #else + {"CLIENTHELLO_TLSEXT", ERR_LIB_SSL, 226}, + #endif + #ifdef SSL_R_COMPRESSED_LENGTH_TOO_LONG + {"COMPRESSED_LENGTH_TOO_LONG", ERR_LIB_SSL, SSL_R_COMPRESSED_LENGTH_TOO_LONG}, + #else + {"COMPRESSED_LENGTH_TOO_LONG", ERR_LIB_SSL, 140}, + #endif + #ifdef SSL_R_COMPRESSION_DISABLED + {"COMPRESSION_DISABLED", ERR_LIB_SSL, SSL_R_COMPRESSION_DISABLED}, + #else + {"COMPRESSION_DISABLED", ERR_LIB_SSL, 343}, + #endif + #ifdef SSL_R_COMPRESSION_FAILURE + {"COMPRESSION_FAILURE", ERR_LIB_SSL, SSL_R_COMPRESSION_FAILURE}, + #else + {"COMPRESSION_FAILURE", ERR_LIB_SSL, 141}, + #endif + #ifdef SSL_R_COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE + {"COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE", ERR_LIB_SSL, SSL_R_COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE}, + #else + {"COMPRESSION_ID_NOT_WITHIN_PRIVATE_RANGE", ERR_LIB_SSL, 307}, + #endif + #ifdef SSL_R_COMPRESSION_LIBRARY_ERROR + {"COMPRESSION_LIBRARY_ERROR", ERR_LIB_SSL, SSL_R_COMPRESSION_LIBRARY_ERROR}, + #else + {"COMPRESSION_LIBRARY_ERROR", ERR_LIB_SSL, 142}, + #endif + #ifdef SSL_R_CONNECTION_ID_IS_DIFFERENT + {"CONNECTION_ID_IS_DIFFERENT", ERR_LIB_SSL, SSL_R_CONNECTION_ID_IS_DIFFERENT}, + #else + {"CONNECTION_ID_IS_DIFFERENT", ERR_LIB_SSL, 143}, + #endif + #ifdef SSL_R_CONNECTION_TYPE_NOT_SET + {"CONNECTION_TYPE_NOT_SET", ERR_LIB_SSL, SSL_R_CONNECTION_TYPE_NOT_SET}, + #else + {"CONNECTION_TYPE_NOT_SET", ERR_LIB_SSL, 144}, + #endif + #ifdef SSL_R_COOKIE_MISMATCH + {"COOKIE_MISMATCH", ERR_LIB_SSL, SSL_R_COOKIE_MISMATCH}, + #else + {"COOKIE_MISMATCH", ERR_LIB_SSL, 308}, + #endif + #ifdef SSL_R_DATA_BETWEEN_CCS_AND_FINISHED + {"DATA_BETWEEN_CCS_AND_FINISHED", ERR_LIB_SSL, SSL_R_DATA_BETWEEN_CCS_AND_FINISHED}, + #else + {"DATA_BETWEEN_CCS_AND_FINISHED", ERR_LIB_SSL, 145}, + #endif + #ifdef SSL_R_DATA_LENGTH_TOO_LONG + {"DATA_LENGTH_TOO_LONG", ERR_LIB_SSL, SSL_R_DATA_LENGTH_TOO_LONG}, + #else + {"DATA_LENGTH_TOO_LONG", ERR_LIB_SSL, 146}, + #endif + #ifdef SSL_R_DECRYPTION_FAILED + {"DECRYPTION_FAILED", ERR_LIB_SSL, SSL_R_DECRYPTION_FAILED}, + #else + {"DECRYPTION_FAILED", ERR_LIB_SSL, 147}, + #endif + #ifdef SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC + {"DECRYPTION_FAILED_OR_BAD_RECORD_MAC", ERR_LIB_SSL, SSL_R_DECRYPTION_FAILED_OR_BAD_RECORD_MAC}, + #else + {"DECRYPTION_FAILED_OR_BAD_RECORD_MAC", ERR_LIB_SSL, 281}, + #endif + #ifdef SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG + {"DH_PUBLIC_VALUE_LENGTH_IS_WRONG", ERR_LIB_SSL, SSL_R_DH_PUBLIC_VALUE_LENGTH_IS_WRONG}, + #else + {"DH_PUBLIC_VALUE_LENGTH_IS_WRONG", ERR_LIB_SSL, 148}, + #endif + #ifdef SSL_R_DIGEST_CHECK_FAILED + {"DIGEST_CHECK_FAILED", ERR_LIB_SSL, SSL_R_DIGEST_CHECK_FAILED}, + #else + {"DIGEST_CHECK_FAILED", ERR_LIB_SSL, 149}, + #endif + #ifdef SSL_R_DTLS_MESSAGE_TOO_BIG + {"DTLS_MESSAGE_TOO_BIG", ERR_LIB_SSL, SSL_R_DTLS_MESSAGE_TOO_BIG}, + #else + {"DTLS_MESSAGE_TOO_BIG", ERR_LIB_SSL, 334}, + #endif + #ifdef SSL_R_DUPLICATE_COMPRESSION_ID + {"DUPLICATE_COMPRESSION_ID", ERR_LIB_SSL, SSL_R_DUPLICATE_COMPRESSION_ID}, + #else + {"DUPLICATE_COMPRESSION_ID", ERR_LIB_SSL, 309}, + #endif + #ifdef SSL_R_ECC_CERT_NOT_FOR_KEY_AGREEMENT + {"ECC_CERT_NOT_FOR_KEY_AGREEMENT", ERR_LIB_SSL, SSL_R_ECC_CERT_NOT_FOR_KEY_AGREEMENT}, + #else + {"ECC_CERT_NOT_FOR_KEY_AGREEMENT", ERR_LIB_SSL, 317}, + #endif + #ifdef SSL_R_ECC_CERT_NOT_FOR_SIGNING + {"ECC_CERT_NOT_FOR_SIGNING", ERR_LIB_SSL, SSL_R_ECC_CERT_NOT_FOR_SIGNING}, + #else + {"ECC_CERT_NOT_FOR_SIGNING", ERR_LIB_SSL, 318}, + #endif + #ifdef SSL_R_ECC_CERT_SHOULD_HAVE_RSA_SIGNATURE + {"ECC_CERT_SHOULD_HAVE_RSA_SIGNATURE", ERR_LIB_SSL, SSL_R_ECC_CERT_SHOULD_HAVE_RSA_SIGNATURE}, + #else + {"ECC_CERT_SHOULD_HAVE_RSA_SIGNATURE", ERR_LIB_SSL, 322}, + #endif + #ifdef SSL_R_ECC_CERT_SHOULD_HAVE_SHA1_SIGNATURE + {"ECC_CERT_SHOULD_HAVE_SHA1_SIGNATURE", ERR_LIB_SSL, SSL_R_ECC_CERT_SHOULD_HAVE_SHA1_SIGNATURE}, + #else + {"ECC_CERT_SHOULD_HAVE_SHA1_SIGNATURE", ERR_LIB_SSL, 323}, + #endif + #ifdef SSL_R_ECGROUP_TOO_LARGE_FOR_CIPHER + {"ECGROUP_TOO_LARGE_FOR_CIPHER", ERR_LIB_SSL, SSL_R_ECGROUP_TOO_LARGE_FOR_CIPHER}, + #else + {"ECGROUP_TOO_LARGE_FOR_CIPHER", ERR_LIB_SSL, 310}, + #endif + #ifdef SSL_R_ENCRYPTED_LENGTH_TOO_LONG + {"ENCRYPTED_LENGTH_TOO_LONG", ERR_LIB_SSL, SSL_R_ENCRYPTED_LENGTH_TOO_LONG}, + #else + {"ENCRYPTED_LENGTH_TOO_LONG", ERR_LIB_SSL, 150}, + #endif + #ifdef SSL_R_ERROR_GENERATING_TMP_RSA_KEY + {"ERROR_GENERATING_TMP_RSA_KEY", ERR_LIB_SSL, SSL_R_ERROR_GENERATING_TMP_RSA_KEY}, + #else + {"ERROR_GENERATING_TMP_RSA_KEY", ERR_LIB_SSL, 282}, + #endif + #ifdef SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST + {"ERROR_IN_RECEIVED_CIPHER_LIST", ERR_LIB_SSL, SSL_R_ERROR_IN_RECEIVED_CIPHER_LIST}, + #else + {"ERROR_IN_RECEIVED_CIPHER_LIST", ERR_LIB_SSL, 151}, + #endif + #ifdef SSL_R_EXCESSIVE_MESSAGE_SIZE + {"EXCESSIVE_MESSAGE_SIZE", ERR_LIB_SSL, SSL_R_EXCESSIVE_MESSAGE_SIZE}, + #else + {"EXCESSIVE_MESSAGE_SIZE", ERR_LIB_SSL, 152}, + #endif + #ifdef SSL_R_EXTRA_DATA_IN_MESSAGE + {"EXTRA_DATA_IN_MESSAGE", ERR_LIB_SSL, SSL_R_EXTRA_DATA_IN_MESSAGE}, + #else + {"EXTRA_DATA_IN_MESSAGE", ERR_LIB_SSL, 153}, + #endif + #ifdef SSL_R_GOT_A_FIN_BEFORE_A_CCS + {"GOT_A_FIN_BEFORE_A_CCS", ERR_LIB_SSL, SSL_R_GOT_A_FIN_BEFORE_A_CCS}, + #else + {"GOT_A_FIN_BEFORE_A_CCS", ERR_LIB_SSL, 154}, + #endif + #ifdef SSL_R_HTTPS_PROXY_REQUEST + {"HTTPS_PROXY_REQUEST", ERR_LIB_SSL, SSL_R_HTTPS_PROXY_REQUEST}, + #else + {"HTTPS_PROXY_REQUEST", ERR_LIB_SSL, 155}, + #endif + #ifdef SSL_R_HTTP_REQUEST + {"HTTP_REQUEST", ERR_LIB_SSL, SSL_R_HTTP_REQUEST}, + #else + {"HTTP_REQUEST", ERR_LIB_SSL, 156}, + #endif + #ifdef SSL_R_ILLEGAL_PADDING + {"ILLEGAL_PADDING", ERR_LIB_SSL, SSL_R_ILLEGAL_PADDING}, + #else + {"ILLEGAL_PADDING", ERR_LIB_SSL, 283}, + #endif + #ifdef SSL_R_INCONSISTENT_COMPRESSION + {"INCONSISTENT_COMPRESSION", ERR_LIB_SSL, SSL_R_INCONSISTENT_COMPRESSION}, + #else + {"INCONSISTENT_COMPRESSION", ERR_LIB_SSL, 340}, + #endif + #ifdef SSL_R_INVALID_CHALLENGE_LENGTH + {"INVALID_CHALLENGE_LENGTH", ERR_LIB_SSL, SSL_R_INVALID_CHALLENGE_LENGTH}, + #else + {"INVALID_CHALLENGE_LENGTH", ERR_LIB_SSL, 158}, + #endif + #ifdef SSL_R_INVALID_COMMAND + {"INVALID_COMMAND", ERR_LIB_SSL, SSL_R_INVALID_COMMAND}, + #else + {"INVALID_COMMAND", ERR_LIB_SSL, 280}, + #endif + #ifdef SSL_R_INVALID_COMPRESSION_ALGORITHM + {"INVALID_COMPRESSION_ALGORITHM", ERR_LIB_SSL, SSL_R_INVALID_COMPRESSION_ALGORITHM}, + #else + {"INVALID_COMPRESSION_ALGORITHM", ERR_LIB_SSL, 341}, + #endif + #ifdef SSL_R_INVALID_PURPOSE + {"INVALID_PURPOSE", ERR_LIB_SSL, SSL_R_INVALID_PURPOSE}, + #else + {"INVALID_PURPOSE", ERR_LIB_SSL, 278}, + #endif + #ifdef SSL_R_INVALID_STATUS_RESPONSE + {"INVALID_STATUS_RESPONSE", ERR_LIB_SSL, SSL_R_INVALID_STATUS_RESPONSE}, + #else + {"INVALID_STATUS_RESPONSE", ERR_LIB_SSL, 328}, + #endif + #ifdef SSL_R_INVALID_TICKET_KEYS_LENGTH + {"INVALID_TICKET_KEYS_LENGTH", ERR_LIB_SSL, SSL_R_INVALID_TICKET_KEYS_LENGTH}, + #else + {"INVALID_TICKET_KEYS_LENGTH", ERR_LIB_SSL, 325}, + #endif + #ifdef SSL_R_INVALID_TRUST + {"INVALID_TRUST", ERR_LIB_SSL, SSL_R_INVALID_TRUST}, + #else + {"INVALID_TRUST", ERR_LIB_SSL, 279}, + #endif + #ifdef SSL_R_KEY_ARG_TOO_LONG + {"KEY_ARG_TOO_LONG", ERR_LIB_SSL, SSL_R_KEY_ARG_TOO_LONG}, + #else + {"KEY_ARG_TOO_LONG", ERR_LIB_SSL, 284}, + #endif + #ifdef SSL_R_KRB5 + {"KRB5", ERR_LIB_SSL, SSL_R_KRB5}, + #else + {"KRB5", ERR_LIB_SSL, 285}, + #endif + #ifdef SSL_R_KRB5_C_CC_PRINC + {"KRB5_C_CC_PRINC", ERR_LIB_SSL, SSL_R_KRB5_C_CC_PRINC}, + #else + {"KRB5_C_CC_PRINC", ERR_LIB_SSL, 286}, + #endif + #ifdef SSL_R_KRB5_C_GET_CRED + {"KRB5_C_GET_CRED", ERR_LIB_SSL, SSL_R_KRB5_C_GET_CRED}, + #else + {"KRB5_C_GET_CRED", ERR_LIB_SSL, 287}, + #endif + #ifdef SSL_R_KRB5_C_INIT + {"KRB5_C_INIT", ERR_LIB_SSL, SSL_R_KRB5_C_INIT}, + #else + {"KRB5_C_INIT", ERR_LIB_SSL, 288}, + #endif + #ifdef SSL_R_KRB5_C_MK_REQ + {"KRB5_C_MK_REQ", ERR_LIB_SSL, SSL_R_KRB5_C_MK_REQ}, + #else + {"KRB5_C_MK_REQ", ERR_LIB_SSL, 289}, + #endif + #ifdef SSL_R_KRB5_S_BAD_TICKET + {"KRB5_S_BAD_TICKET", ERR_LIB_SSL, SSL_R_KRB5_S_BAD_TICKET}, + #else + {"KRB5_S_BAD_TICKET", ERR_LIB_SSL, 290}, + #endif + #ifdef SSL_R_KRB5_S_INIT + {"KRB5_S_INIT", ERR_LIB_SSL, SSL_R_KRB5_S_INIT}, + #else + {"KRB5_S_INIT", ERR_LIB_SSL, 291}, + #endif + #ifdef SSL_R_KRB5_S_RD_REQ + {"KRB5_S_RD_REQ", ERR_LIB_SSL, SSL_R_KRB5_S_RD_REQ}, + #else + {"KRB5_S_RD_REQ", ERR_LIB_SSL, 292}, + #endif + #ifdef SSL_R_KRB5_S_TKT_EXPIRED + {"KRB5_S_TKT_EXPIRED", ERR_LIB_SSL, SSL_R_KRB5_S_TKT_EXPIRED}, + #else + {"KRB5_S_TKT_EXPIRED", ERR_LIB_SSL, 293}, + #endif + #ifdef SSL_R_KRB5_S_TKT_NYV + {"KRB5_S_TKT_NYV", ERR_LIB_SSL, SSL_R_KRB5_S_TKT_NYV}, + #else + {"KRB5_S_TKT_NYV", ERR_LIB_SSL, 294}, + #endif + #ifdef SSL_R_KRB5_S_TKT_SKEW + {"KRB5_S_TKT_SKEW", ERR_LIB_SSL, SSL_R_KRB5_S_TKT_SKEW}, + #else + {"KRB5_S_TKT_SKEW", ERR_LIB_SSL, 295}, + #endif + #ifdef SSL_R_LENGTH_MISMATCH + {"LENGTH_MISMATCH", ERR_LIB_SSL, SSL_R_LENGTH_MISMATCH}, + #else + {"LENGTH_MISMATCH", ERR_LIB_SSL, 159}, + #endif + #ifdef SSL_R_LENGTH_TOO_SHORT + {"LENGTH_TOO_SHORT", ERR_LIB_SSL, SSL_R_LENGTH_TOO_SHORT}, + #else + {"LENGTH_TOO_SHORT", ERR_LIB_SSL, 160}, + #endif + #ifdef SSL_R_LIBRARY_BUG + {"LIBRARY_BUG", ERR_LIB_SSL, SSL_R_LIBRARY_BUG}, + #else + {"LIBRARY_BUG", ERR_LIB_SSL, 274}, + #endif + #ifdef SSL_R_LIBRARY_HAS_NO_CIPHERS + {"LIBRARY_HAS_NO_CIPHERS", ERR_LIB_SSL, SSL_R_LIBRARY_HAS_NO_CIPHERS}, + #else + {"LIBRARY_HAS_NO_CIPHERS", ERR_LIB_SSL, 161}, + #endif + #ifdef SSL_R_MESSAGE_TOO_LONG + {"MESSAGE_TOO_LONG", ERR_LIB_SSL, SSL_R_MESSAGE_TOO_LONG}, + #else + {"MESSAGE_TOO_LONG", ERR_LIB_SSL, 296}, + #endif + #ifdef SSL_R_MISSING_DH_DSA_CERT + {"MISSING_DH_DSA_CERT", ERR_LIB_SSL, SSL_R_MISSING_DH_DSA_CERT}, + #else + {"MISSING_DH_DSA_CERT", ERR_LIB_SSL, 162}, + #endif + #ifdef SSL_R_MISSING_DH_KEY + {"MISSING_DH_KEY", ERR_LIB_SSL, SSL_R_MISSING_DH_KEY}, + #else + {"MISSING_DH_KEY", ERR_LIB_SSL, 163}, + #endif + #ifdef SSL_R_MISSING_DH_RSA_CERT + {"MISSING_DH_RSA_CERT", ERR_LIB_SSL, SSL_R_MISSING_DH_RSA_CERT}, + #else + {"MISSING_DH_RSA_CERT", ERR_LIB_SSL, 164}, + #endif + #ifdef SSL_R_MISSING_DSA_SIGNING_CERT + {"MISSING_DSA_SIGNING_CERT", ERR_LIB_SSL, SSL_R_MISSING_DSA_SIGNING_CERT}, + #else + {"MISSING_DSA_SIGNING_CERT", ERR_LIB_SSL, 165}, + #endif + #ifdef SSL_R_MISSING_EXPORT_TMP_DH_KEY + {"MISSING_EXPORT_TMP_DH_KEY", ERR_LIB_SSL, SSL_R_MISSING_EXPORT_TMP_DH_KEY}, + #else + {"MISSING_EXPORT_TMP_DH_KEY", ERR_LIB_SSL, 166}, + #endif + #ifdef SSL_R_MISSING_EXPORT_TMP_RSA_KEY + {"MISSING_EXPORT_TMP_RSA_KEY", ERR_LIB_SSL, SSL_R_MISSING_EXPORT_TMP_RSA_KEY}, + #else + {"MISSING_EXPORT_TMP_RSA_KEY", ERR_LIB_SSL, 167}, + #endif + #ifdef SSL_R_MISSING_RSA_CERTIFICATE + {"MISSING_RSA_CERTIFICATE", ERR_LIB_SSL, SSL_R_MISSING_RSA_CERTIFICATE}, + #else + {"MISSING_RSA_CERTIFICATE", ERR_LIB_SSL, 168}, + #endif + #ifdef SSL_R_MISSING_RSA_ENCRYPTING_CERT + {"MISSING_RSA_ENCRYPTING_CERT", ERR_LIB_SSL, SSL_R_MISSING_RSA_ENCRYPTING_CERT}, + #else + {"MISSING_RSA_ENCRYPTING_CERT", ERR_LIB_SSL, 169}, + #endif + #ifdef SSL_R_MISSING_RSA_SIGNING_CERT + {"MISSING_RSA_SIGNING_CERT", ERR_LIB_SSL, SSL_R_MISSING_RSA_SIGNING_CERT}, + #else + {"MISSING_RSA_SIGNING_CERT", ERR_LIB_SSL, 170}, + #endif + #ifdef SSL_R_MISSING_TMP_DH_KEY + {"MISSING_TMP_DH_KEY", ERR_LIB_SSL, SSL_R_MISSING_TMP_DH_KEY}, + #else + {"MISSING_TMP_DH_KEY", ERR_LIB_SSL, 171}, + #endif + #ifdef SSL_R_MISSING_TMP_ECDH_KEY + {"MISSING_TMP_ECDH_KEY", ERR_LIB_SSL, SSL_R_MISSING_TMP_ECDH_KEY}, + #else + {"MISSING_TMP_ECDH_KEY", ERR_LIB_SSL, 311}, + #endif + #ifdef SSL_R_MISSING_TMP_RSA_KEY + {"MISSING_TMP_RSA_KEY", ERR_LIB_SSL, SSL_R_MISSING_TMP_RSA_KEY}, + #else + {"MISSING_TMP_RSA_KEY", ERR_LIB_SSL, 172}, + #endif + #ifdef SSL_R_MISSING_TMP_RSA_PKEY + {"MISSING_TMP_RSA_PKEY", ERR_LIB_SSL, SSL_R_MISSING_TMP_RSA_PKEY}, + #else + {"MISSING_TMP_RSA_PKEY", ERR_LIB_SSL, 173}, + #endif + #ifdef SSL_R_MISSING_VERIFY_MESSAGE + {"MISSING_VERIFY_MESSAGE", ERR_LIB_SSL, SSL_R_MISSING_VERIFY_MESSAGE}, + #else + {"MISSING_VERIFY_MESSAGE", ERR_LIB_SSL, 174}, + #endif + #ifdef SSL_R_NON_SSLV2_INITIAL_PACKET + {"NON_SSLV2_INITIAL_PACKET", ERR_LIB_SSL, SSL_R_NON_SSLV2_INITIAL_PACKET}, + #else + {"NON_SSLV2_INITIAL_PACKET", ERR_LIB_SSL, 175}, + #endif + #ifdef SSL_R_NO_CERTIFICATES_RETURNED + {"NO_CERTIFICATES_RETURNED", ERR_LIB_SSL, SSL_R_NO_CERTIFICATES_RETURNED}, + #else + {"NO_CERTIFICATES_RETURNED", ERR_LIB_SSL, 176}, + #endif + #ifdef SSL_R_NO_CERTIFICATE_ASSIGNED + {"NO_CERTIFICATE_ASSIGNED", ERR_LIB_SSL, SSL_R_NO_CERTIFICATE_ASSIGNED}, + #else + {"NO_CERTIFICATE_ASSIGNED", ERR_LIB_SSL, 177}, + #endif + #ifdef SSL_R_NO_CERTIFICATE_RETURNED + {"NO_CERTIFICATE_RETURNED", ERR_LIB_SSL, SSL_R_NO_CERTIFICATE_RETURNED}, + #else + {"NO_CERTIFICATE_RETURNED", ERR_LIB_SSL, 178}, + #endif + #ifdef SSL_R_NO_CERTIFICATE_SET + {"NO_CERTIFICATE_SET", ERR_LIB_SSL, SSL_R_NO_CERTIFICATE_SET}, + #else + {"NO_CERTIFICATE_SET", ERR_LIB_SSL, 179}, + #endif + #ifdef SSL_R_NO_CERTIFICATE_SPECIFIED + {"NO_CERTIFICATE_SPECIFIED", ERR_LIB_SSL, SSL_R_NO_CERTIFICATE_SPECIFIED}, + #else + {"NO_CERTIFICATE_SPECIFIED", ERR_LIB_SSL, 180}, + #endif + #ifdef SSL_R_NO_CIPHERS_AVAILABLE + {"NO_CIPHERS_AVAILABLE", ERR_LIB_SSL, SSL_R_NO_CIPHERS_AVAILABLE}, + #else + {"NO_CIPHERS_AVAILABLE", ERR_LIB_SSL, 181}, + #endif + #ifdef SSL_R_NO_CIPHERS_PASSED + {"NO_CIPHERS_PASSED", ERR_LIB_SSL, SSL_R_NO_CIPHERS_PASSED}, + #else + {"NO_CIPHERS_PASSED", ERR_LIB_SSL, 182}, + #endif + #ifdef SSL_R_NO_CIPHERS_SPECIFIED + {"NO_CIPHERS_SPECIFIED", ERR_LIB_SSL, SSL_R_NO_CIPHERS_SPECIFIED}, + #else + {"NO_CIPHERS_SPECIFIED", ERR_LIB_SSL, 183}, + #endif + #ifdef SSL_R_NO_CIPHER_LIST + {"NO_CIPHER_LIST", ERR_LIB_SSL, SSL_R_NO_CIPHER_LIST}, + #else + {"NO_CIPHER_LIST", ERR_LIB_SSL, 184}, + #endif + #ifdef SSL_R_NO_CIPHER_MATCH + {"NO_CIPHER_MATCH", ERR_LIB_SSL, SSL_R_NO_CIPHER_MATCH}, + #else + {"NO_CIPHER_MATCH", ERR_LIB_SSL, 185}, + #endif + #ifdef SSL_R_NO_CLIENT_CERT_METHOD + {"NO_CLIENT_CERT_METHOD", ERR_LIB_SSL, SSL_R_NO_CLIENT_CERT_METHOD}, + #else + {"NO_CLIENT_CERT_METHOD", ERR_LIB_SSL, 331}, + #endif + #ifdef SSL_R_NO_CLIENT_CERT_RECEIVED + {"NO_CLIENT_CERT_RECEIVED", ERR_LIB_SSL, SSL_R_NO_CLIENT_CERT_RECEIVED}, + #else + {"NO_CLIENT_CERT_RECEIVED", ERR_LIB_SSL, 186}, + #endif + #ifdef SSL_R_NO_COMPRESSION_SPECIFIED + {"NO_COMPRESSION_SPECIFIED", ERR_LIB_SSL, SSL_R_NO_COMPRESSION_SPECIFIED}, + #else + {"NO_COMPRESSION_SPECIFIED", ERR_LIB_SSL, 187}, + #endif + #ifdef SSL_R_NO_GOST_CERTIFICATE_SENT_BY_PEER + {"NO_GOST_CERTIFICATE_SENT_BY_PEER", ERR_LIB_SSL, SSL_R_NO_GOST_CERTIFICATE_SENT_BY_PEER}, + #else + {"NO_GOST_CERTIFICATE_SENT_BY_PEER", ERR_LIB_SSL, 330}, + #endif + #ifdef SSL_R_NO_METHOD_SPECIFIED + {"NO_METHOD_SPECIFIED", ERR_LIB_SSL, SSL_R_NO_METHOD_SPECIFIED}, + #else + {"NO_METHOD_SPECIFIED", ERR_LIB_SSL, 188}, + #endif + #ifdef SSL_R_NO_PRIVATEKEY + {"NO_PRIVATEKEY", ERR_LIB_SSL, SSL_R_NO_PRIVATEKEY}, + #else + {"NO_PRIVATEKEY", ERR_LIB_SSL, 189}, + #endif + #ifdef SSL_R_NO_PRIVATE_KEY_ASSIGNED + {"NO_PRIVATE_KEY_ASSIGNED", ERR_LIB_SSL, SSL_R_NO_PRIVATE_KEY_ASSIGNED}, + #else + {"NO_PRIVATE_KEY_ASSIGNED", ERR_LIB_SSL, 190}, + #endif + #ifdef SSL_R_NO_PROTOCOLS_AVAILABLE + {"NO_PROTOCOLS_AVAILABLE", ERR_LIB_SSL, SSL_R_NO_PROTOCOLS_AVAILABLE}, + #else + {"NO_PROTOCOLS_AVAILABLE", ERR_LIB_SSL, 191}, + #endif + #ifdef SSL_R_NO_PUBLICKEY + {"NO_PUBLICKEY", ERR_LIB_SSL, SSL_R_NO_PUBLICKEY}, + #else + {"NO_PUBLICKEY", ERR_LIB_SSL, 192}, + #endif + #ifdef SSL_R_NO_RENEGOTIATION + {"NO_RENEGOTIATION", ERR_LIB_SSL, SSL_R_NO_RENEGOTIATION}, + #else + {"NO_RENEGOTIATION", ERR_LIB_SSL, 339}, + #endif + #ifdef SSL_R_NO_REQUIRED_DIGEST + {"NO_REQUIRED_DIGEST", ERR_LIB_SSL, SSL_R_NO_REQUIRED_DIGEST}, + #else + {"NO_REQUIRED_DIGEST", ERR_LIB_SSL, 324}, + #endif + #ifdef SSL_R_NO_SHARED_CIPHER + {"NO_SHARED_CIPHER", ERR_LIB_SSL, SSL_R_NO_SHARED_CIPHER}, + #else + {"NO_SHARED_CIPHER", ERR_LIB_SSL, 193}, + #endif + #ifdef SSL_R_NO_VERIFY_CALLBACK + {"NO_VERIFY_CALLBACK", ERR_LIB_SSL, SSL_R_NO_VERIFY_CALLBACK}, + #else + {"NO_VERIFY_CALLBACK", ERR_LIB_SSL, 194}, + #endif + #ifdef SSL_R_NULL_SSL_CTX + {"NULL_SSL_CTX", ERR_LIB_SSL, SSL_R_NULL_SSL_CTX}, + #else + {"NULL_SSL_CTX", ERR_LIB_SSL, 195}, + #endif + #ifdef SSL_R_NULL_SSL_METHOD_PASSED + {"NULL_SSL_METHOD_PASSED", ERR_LIB_SSL, SSL_R_NULL_SSL_METHOD_PASSED}, + #else + {"NULL_SSL_METHOD_PASSED", ERR_LIB_SSL, 196}, + #endif + #ifdef SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED + {"OLD_SESSION_CIPHER_NOT_RETURNED", ERR_LIB_SSL, SSL_R_OLD_SESSION_CIPHER_NOT_RETURNED}, + #else + {"OLD_SESSION_CIPHER_NOT_RETURNED", ERR_LIB_SSL, 197}, + #endif + #ifdef SSL_R_OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED + {"OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED", ERR_LIB_SSL, SSL_R_OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED}, + #else + {"OLD_SESSION_COMPRESSION_ALGORITHM_NOT_RETURNED", ERR_LIB_SSL, 344}, + #endif + #ifdef SSL_R_ONLY_TLS_ALLOWED_IN_FIPS_MODE + {"ONLY_TLS_ALLOWED_IN_FIPS_MODE", ERR_LIB_SSL, SSL_R_ONLY_TLS_ALLOWED_IN_FIPS_MODE}, + #else + {"ONLY_TLS_ALLOWED_IN_FIPS_MODE", ERR_LIB_SSL, 297}, + #endif + #ifdef SSL_R_OPAQUE_PRF_INPUT_TOO_LONG + {"OPAQUE_PRF_INPUT_TOO_LONG", ERR_LIB_SSL, SSL_R_OPAQUE_PRF_INPUT_TOO_LONG}, + #else + {"OPAQUE_PRF_INPUT_TOO_LONG", ERR_LIB_SSL, 327}, + #endif + #ifdef SSL_R_PACKET_LENGTH_TOO_LONG + {"PACKET_LENGTH_TOO_LONG", ERR_LIB_SSL, SSL_R_PACKET_LENGTH_TOO_LONG}, + #else + {"PACKET_LENGTH_TOO_LONG", ERR_LIB_SSL, 198}, + #endif + #ifdef SSL_R_PARSE_TLSEXT + {"PARSE_TLSEXT", ERR_LIB_SSL, SSL_R_PARSE_TLSEXT}, + #else + {"PARSE_TLSEXT", ERR_LIB_SSL, 227}, + #endif + #ifdef SSL_R_PATH_TOO_LONG + {"PATH_TOO_LONG", ERR_LIB_SSL, SSL_R_PATH_TOO_LONG}, + #else + {"PATH_TOO_LONG", ERR_LIB_SSL, 270}, + #endif + #ifdef SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE + {"PEER_DID_NOT_RETURN_A_CERTIFICATE", ERR_LIB_SSL, SSL_R_PEER_DID_NOT_RETURN_A_CERTIFICATE}, + #else + {"PEER_DID_NOT_RETURN_A_CERTIFICATE", ERR_LIB_SSL, 199}, + #endif + #ifdef SSL_R_PEER_ERROR + {"PEER_ERROR", ERR_LIB_SSL, SSL_R_PEER_ERROR}, + #else + {"PEER_ERROR", ERR_LIB_SSL, 200}, + #endif + #ifdef SSL_R_PEER_ERROR_CERTIFICATE + {"PEER_ERROR_CERTIFICATE", ERR_LIB_SSL, SSL_R_PEER_ERROR_CERTIFICATE}, + #else + {"PEER_ERROR_CERTIFICATE", ERR_LIB_SSL, 201}, + #endif + #ifdef SSL_R_PEER_ERROR_NO_CERTIFICATE + {"PEER_ERROR_NO_CERTIFICATE", ERR_LIB_SSL, SSL_R_PEER_ERROR_NO_CERTIFICATE}, + #else + {"PEER_ERROR_NO_CERTIFICATE", ERR_LIB_SSL, 202}, + #endif + #ifdef SSL_R_PEER_ERROR_NO_CIPHER + {"PEER_ERROR_NO_CIPHER", ERR_LIB_SSL, SSL_R_PEER_ERROR_NO_CIPHER}, + #else + {"PEER_ERROR_NO_CIPHER", ERR_LIB_SSL, 203}, + #endif + #ifdef SSL_R_PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE + {"PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE", ERR_LIB_SSL, SSL_R_PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE}, + #else + {"PEER_ERROR_UNSUPPORTED_CERTIFICATE_TYPE", ERR_LIB_SSL, 204}, + #endif + #ifdef SSL_R_PRE_MAC_LENGTH_TOO_LONG + {"PRE_MAC_LENGTH_TOO_LONG", ERR_LIB_SSL, SSL_R_PRE_MAC_LENGTH_TOO_LONG}, + #else + {"PRE_MAC_LENGTH_TOO_LONG", ERR_LIB_SSL, 205}, + #endif + #ifdef SSL_R_PROBLEMS_MAPPING_CIPHER_FUNCTIONS + {"PROBLEMS_MAPPING_CIPHER_FUNCTIONS", ERR_LIB_SSL, SSL_R_PROBLEMS_MAPPING_CIPHER_FUNCTIONS}, + #else + {"PROBLEMS_MAPPING_CIPHER_FUNCTIONS", ERR_LIB_SSL, 206}, + #endif + #ifdef SSL_R_PROTOCOL_IS_SHUTDOWN + {"PROTOCOL_IS_SHUTDOWN", ERR_LIB_SSL, SSL_R_PROTOCOL_IS_SHUTDOWN}, + #else + {"PROTOCOL_IS_SHUTDOWN", ERR_LIB_SSL, 207}, + #endif + #ifdef SSL_R_PSK_IDENTITY_NOT_FOUND + {"PSK_IDENTITY_NOT_FOUND", ERR_LIB_SSL, SSL_R_PSK_IDENTITY_NOT_FOUND}, + #else + {"PSK_IDENTITY_NOT_FOUND", ERR_LIB_SSL, 223}, + #endif + #ifdef SSL_R_PSK_NO_CLIENT_CB + {"PSK_NO_CLIENT_CB", ERR_LIB_SSL, SSL_R_PSK_NO_CLIENT_CB}, + #else + {"PSK_NO_CLIENT_CB", ERR_LIB_SSL, 224}, + #endif + #ifdef SSL_R_PSK_NO_SERVER_CB + {"PSK_NO_SERVER_CB", ERR_LIB_SSL, SSL_R_PSK_NO_SERVER_CB}, + #else + {"PSK_NO_SERVER_CB", ERR_LIB_SSL, 225}, + #endif + #ifdef SSL_R_PUBLIC_KEY_ENCRYPT_ERROR + {"PUBLIC_KEY_ENCRYPT_ERROR", ERR_LIB_SSL, SSL_R_PUBLIC_KEY_ENCRYPT_ERROR}, + #else + {"PUBLIC_KEY_ENCRYPT_ERROR", ERR_LIB_SSL, 208}, + #endif + #ifdef SSL_R_PUBLIC_KEY_IS_NOT_RSA + {"PUBLIC_KEY_IS_NOT_RSA", ERR_LIB_SSL, SSL_R_PUBLIC_KEY_IS_NOT_RSA}, + #else + {"PUBLIC_KEY_IS_NOT_RSA", ERR_LIB_SSL, 209}, + #endif + #ifdef SSL_R_PUBLIC_KEY_NOT_RSA + {"PUBLIC_KEY_NOT_RSA", ERR_LIB_SSL, SSL_R_PUBLIC_KEY_NOT_RSA}, + #else + {"PUBLIC_KEY_NOT_RSA", ERR_LIB_SSL, 210}, + #endif + #ifdef SSL_R_READ_BIO_NOT_SET + {"READ_BIO_NOT_SET", ERR_LIB_SSL, SSL_R_READ_BIO_NOT_SET}, + #else + {"READ_BIO_NOT_SET", ERR_LIB_SSL, 211}, + #endif + #ifdef SSL_R_READ_TIMEOUT_EXPIRED + {"READ_TIMEOUT_EXPIRED", ERR_LIB_SSL, SSL_R_READ_TIMEOUT_EXPIRED}, + #else + {"READ_TIMEOUT_EXPIRED", ERR_LIB_SSL, 312}, + #endif + #ifdef SSL_R_READ_WRONG_PACKET_TYPE + {"READ_WRONG_PACKET_TYPE", ERR_LIB_SSL, SSL_R_READ_WRONG_PACKET_TYPE}, + #else + {"READ_WRONG_PACKET_TYPE", ERR_LIB_SSL, 212}, + #endif + #ifdef SSL_R_RECORD_LENGTH_MISMATCH + {"RECORD_LENGTH_MISMATCH", ERR_LIB_SSL, SSL_R_RECORD_LENGTH_MISMATCH}, + #else + {"RECORD_LENGTH_MISMATCH", ERR_LIB_SSL, 213}, + #endif + #ifdef SSL_R_RECORD_TOO_LARGE + {"RECORD_TOO_LARGE", ERR_LIB_SSL, SSL_R_RECORD_TOO_LARGE}, + #else + {"RECORD_TOO_LARGE", ERR_LIB_SSL, 214}, + #endif + #ifdef SSL_R_RECORD_TOO_SMALL + {"RECORD_TOO_SMALL", ERR_LIB_SSL, SSL_R_RECORD_TOO_SMALL}, + #else + {"RECORD_TOO_SMALL", ERR_LIB_SSL, 298}, + #endif + #ifdef SSL_R_RENEGOTIATE_EXT_TOO_LONG + {"RENEGOTIATE_EXT_TOO_LONG", ERR_LIB_SSL, SSL_R_RENEGOTIATE_EXT_TOO_LONG}, + #else + {"RENEGOTIATE_EXT_TOO_LONG", ERR_LIB_SSL, 335}, + #endif + #ifdef SSL_R_RENEGOTIATION_ENCODING_ERR + {"RENEGOTIATION_ENCODING_ERR", ERR_LIB_SSL, SSL_R_RENEGOTIATION_ENCODING_ERR}, + #else + {"RENEGOTIATION_ENCODING_ERR", ERR_LIB_SSL, 336}, + #endif + #ifdef SSL_R_RENEGOTIATION_MISMATCH + {"RENEGOTIATION_MISMATCH", ERR_LIB_SSL, SSL_R_RENEGOTIATION_MISMATCH}, + #else + {"RENEGOTIATION_MISMATCH", ERR_LIB_SSL, 337}, + #endif + #ifdef SSL_R_REQUIRED_CIPHER_MISSING + {"REQUIRED_CIPHER_MISSING", ERR_LIB_SSL, SSL_R_REQUIRED_CIPHER_MISSING}, + #else + {"REQUIRED_CIPHER_MISSING", ERR_LIB_SSL, 215}, + #endif + #ifdef SSL_R_REQUIRED_COMPRESSSION_ALGORITHM_MISSING + {"REQUIRED_COMPRESSSION_ALGORITHM_MISSING", ERR_LIB_SSL, SSL_R_REQUIRED_COMPRESSSION_ALGORITHM_MISSING}, + #else + {"REQUIRED_COMPRESSSION_ALGORITHM_MISSING", ERR_LIB_SSL, 342}, + #endif + #ifdef SSL_R_REUSE_CERT_LENGTH_NOT_ZERO + {"REUSE_CERT_LENGTH_NOT_ZERO", ERR_LIB_SSL, SSL_R_REUSE_CERT_LENGTH_NOT_ZERO}, + #else + {"REUSE_CERT_LENGTH_NOT_ZERO", ERR_LIB_SSL, 216}, + #endif + #ifdef SSL_R_REUSE_CERT_TYPE_NOT_ZERO + {"REUSE_CERT_TYPE_NOT_ZERO", ERR_LIB_SSL, SSL_R_REUSE_CERT_TYPE_NOT_ZERO}, + #else + {"REUSE_CERT_TYPE_NOT_ZERO", ERR_LIB_SSL, 217}, + #endif + #ifdef SSL_R_REUSE_CIPHER_LIST_NOT_ZERO + {"REUSE_CIPHER_LIST_NOT_ZERO", ERR_LIB_SSL, SSL_R_REUSE_CIPHER_LIST_NOT_ZERO}, + #else + {"REUSE_CIPHER_LIST_NOT_ZERO", ERR_LIB_SSL, 218}, + #endif + #ifdef SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING + {"SCSV_RECEIVED_WHEN_RENEGOTIATING", ERR_LIB_SSL, SSL_R_SCSV_RECEIVED_WHEN_RENEGOTIATING}, + #else + {"SCSV_RECEIVED_WHEN_RENEGOTIATING", ERR_LIB_SSL, 345}, + #endif + #ifdef SSL_R_SERVERHELLO_TLSEXT + {"SERVERHELLO_TLSEXT", ERR_LIB_SSL, SSL_R_SERVERHELLO_TLSEXT}, + #else + {"SERVERHELLO_TLSEXT", ERR_LIB_SSL, 275}, + #endif + #ifdef SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED + {"SESSION_ID_CONTEXT_UNINITIALIZED", ERR_LIB_SSL, SSL_R_SESSION_ID_CONTEXT_UNINITIALIZED}, + #else + {"SESSION_ID_CONTEXT_UNINITIALIZED", ERR_LIB_SSL, 277}, + #endif + #ifdef SSL_R_SHORT_READ + {"SHORT_READ", ERR_LIB_SSL, SSL_R_SHORT_READ}, + #else + {"SHORT_READ", ERR_LIB_SSL, 219}, + #endif + #ifdef SSL_R_SIGNATURE_FOR_NON_SIGNING_CERTIFICATE + {"SIGNATURE_FOR_NON_SIGNING_CERTIFICATE", ERR_LIB_SSL, SSL_R_SIGNATURE_FOR_NON_SIGNING_CERTIFICATE}, + #else + {"SIGNATURE_FOR_NON_SIGNING_CERTIFICATE", ERR_LIB_SSL, 220}, + #endif + #ifdef SSL_R_SSL23_DOING_SESSION_ID_REUSE + {"SSL23_DOING_SESSION_ID_REUSE", ERR_LIB_SSL, SSL_R_SSL23_DOING_SESSION_ID_REUSE}, + #else + {"SSL23_DOING_SESSION_ID_REUSE", ERR_LIB_SSL, 221}, + #endif + #ifdef SSL_R_SSL2_CONNECTION_ID_TOO_LONG + {"SSL2_CONNECTION_ID_TOO_LONG", ERR_LIB_SSL, SSL_R_SSL2_CONNECTION_ID_TOO_LONG}, + #else + {"SSL2_CONNECTION_ID_TOO_LONG", ERR_LIB_SSL, 299}, + #endif + #ifdef SSL_R_SSL3_EXT_INVALID_ECPOINTFORMAT + {"SSL3_EXT_INVALID_ECPOINTFORMAT", ERR_LIB_SSL, SSL_R_SSL3_EXT_INVALID_ECPOINTFORMAT}, + #else + {"SSL3_EXT_INVALID_ECPOINTFORMAT", ERR_LIB_SSL, 321}, + #endif + #ifdef SSL_R_SSL3_EXT_INVALID_SERVERNAME + {"SSL3_EXT_INVALID_SERVERNAME", ERR_LIB_SSL, SSL_R_SSL3_EXT_INVALID_SERVERNAME}, + #else + {"SSL3_EXT_INVALID_SERVERNAME", ERR_LIB_SSL, 319}, + #endif + #ifdef SSL_R_SSL3_EXT_INVALID_SERVERNAME_TYPE + {"SSL3_EXT_INVALID_SERVERNAME_TYPE", ERR_LIB_SSL, SSL_R_SSL3_EXT_INVALID_SERVERNAME_TYPE}, + #else + {"SSL3_EXT_INVALID_SERVERNAME_TYPE", ERR_LIB_SSL, 320}, + #endif + #ifdef SSL_R_SSL3_SESSION_ID_TOO_LONG + {"SSL3_SESSION_ID_TOO_LONG", ERR_LIB_SSL, SSL_R_SSL3_SESSION_ID_TOO_LONG}, + #else + {"SSL3_SESSION_ID_TOO_LONG", ERR_LIB_SSL, 300}, + #endif + #ifdef SSL_R_SSL3_SESSION_ID_TOO_SHORT + {"SSL3_SESSION_ID_TOO_SHORT", ERR_LIB_SSL, SSL_R_SSL3_SESSION_ID_TOO_SHORT}, + #else + {"SSL3_SESSION_ID_TOO_SHORT", ERR_LIB_SSL, 222}, + #endif + #ifdef SSL_R_SSLV3_ALERT_BAD_CERTIFICATE + {"SSLV3_ALERT_BAD_CERTIFICATE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_BAD_CERTIFICATE}, + #else + {"SSLV3_ALERT_BAD_CERTIFICATE", ERR_LIB_SSL, 1042}, + #endif + #ifdef SSL_R_SSLV3_ALERT_BAD_RECORD_MAC + {"SSLV3_ALERT_BAD_RECORD_MAC", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_BAD_RECORD_MAC}, + #else + {"SSLV3_ALERT_BAD_RECORD_MAC", ERR_LIB_SSL, 1020}, + #endif + #ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED + {"SSLV3_ALERT_CERTIFICATE_EXPIRED", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_CERTIFICATE_EXPIRED}, + #else + {"SSLV3_ALERT_CERTIFICATE_EXPIRED", ERR_LIB_SSL, 1045}, + #endif + #ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED + {"SSLV3_ALERT_CERTIFICATE_REVOKED", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_CERTIFICATE_REVOKED}, + #else + {"SSLV3_ALERT_CERTIFICATE_REVOKED", ERR_LIB_SSL, 1044}, + #endif + #ifdef SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN + {"SSLV3_ALERT_CERTIFICATE_UNKNOWN", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_CERTIFICATE_UNKNOWN}, + #else + {"SSLV3_ALERT_CERTIFICATE_UNKNOWN", ERR_LIB_SSL, 1046}, + #endif + #ifdef SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE + {"SSLV3_ALERT_DECOMPRESSION_FAILURE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_DECOMPRESSION_FAILURE}, + #else + {"SSLV3_ALERT_DECOMPRESSION_FAILURE", ERR_LIB_SSL, 1030}, + #endif + #ifdef SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE + {"SSLV3_ALERT_HANDSHAKE_FAILURE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_HANDSHAKE_FAILURE}, + #else + {"SSLV3_ALERT_HANDSHAKE_FAILURE", ERR_LIB_SSL, 1040}, + #endif + #ifdef SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER + {"SSLV3_ALERT_ILLEGAL_PARAMETER", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_ILLEGAL_PARAMETER}, + #else + {"SSLV3_ALERT_ILLEGAL_PARAMETER", ERR_LIB_SSL, 1047}, + #endif + #ifdef SSL_R_SSLV3_ALERT_NO_CERTIFICATE + {"SSLV3_ALERT_NO_CERTIFICATE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_NO_CERTIFICATE}, + #else + {"SSLV3_ALERT_NO_CERTIFICATE", ERR_LIB_SSL, 1041}, + #endif + #ifdef SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE + {"SSLV3_ALERT_UNEXPECTED_MESSAGE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_UNEXPECTED_MESSAGE}, + #else + {"SSLV3_ALERT_UNEXPECTED_MESSAGE", ERR_LIB_SSL, 1010}, + #endif + #ifdef SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE + {"SSLV3_ALERT_UNSUPPORTED_CERTIFICATE", ERR_LIB_SSL, SSL_R_SSLV3_ALERT_UNSUPPORTED_CERTIFICATE}, + #else + {"SSLV3_ALERT_UNSUPPORTED_CERTIFICATE", ERR_LIB_SSL, 1043}, + #endif + #ifdef SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION + {"SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION", ERR_LIB_SSL, SSL_R_SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION}, + #else + {"SSL_CTX_HAS_NO_DEFAULT_SSL_VERSION", ERR_LIB_SSL, 228}, + #endif + #ifdef SSL_R_SSL_HANDSHAKE_FAILURE + {"SSL_HANDSHAKE_FAILURE", ERR_LIB_SSL, SSL_R_SSL_HANDSHAKE_FAILURE}, + #else + {"SSL_HANDSHAKE_FAILURE", ERR_LIB_SSL, 229}, + #endif + #ifdef SSL_R_SSL_LIBRARY_HAS_NO_CIPHERS + {"SSL_LIBRARY_HAS_NO_CIPHERS", ERR_LIB_SSL, SSL_R_SSL_LIBRARY_HAS_NO_CIPHERS}, + #else + {"SSL_LIBRARY_HAS_NO_CIPHERS", ERR_LIB_SSL, 230}, + #endif + #ifdef SSL_R_SSL_SESSION_ID_CALLBACK_FAILED + {"SSL_SESSION_ID_CALLBACK_FAILED", ERR_LIB_SSL, SSL_R_SSL_SESSION_ID_CALLBACK_FAILED}, + #else + {"SSL_SESSION_ID_CALLBACK_FAILED", ERR_LIB_SSL, 301}, + #endif + #ifdef SSL_R_SSL_SESSION_ID_CONFLICT + {"SSL_SESSION_ID_CONFLICT", ERR_LIB_SSL, SSL_R_SSL_SESSION_ID_CONFLICT}, + #else + {"SSL_SESSION_ID_CONFLICT", ERR_LIB_SSL, 302}, + #endif + #ifdef SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG + {"SSL_SESSION_ID_CONTEXT_TOO_LONG", ERR_LIB_SSL, SSL_R_SSL_SESSION_ID_CONTEXT_TOO_LONG}, + #else + {"SSL_SESSION_ID_CONTEXT_TOO_LONG", ERR_LIB_SSL, 273}, + #endif + #ifdef SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH + {"SSL_SESSION_ID_HAS_BAD_LENGTH", ERR_LIB_SSL, SSL_R_SSL_SESSION_ID_HAS_BAD_LENGTH}, + #else + {"SSL_SESSION_ID_HAS_BAD_LENGTH", ERR_LIB_SSL, 303}, + #endif + #ifdef SSL_R_SSL_SESSION_ID_IS_DIFFERENT + {"SSL_SESSION_ID_IS_DIFFERENT", ERR_LIB_SSL, SSL_R_SSL_SESSION_ID_IS_DIFFERENT}, + #else + {"SSL_SESSION_ID_IS_DIFFERENT", ERR_LIB_SSL, 231}, + #endif + #ifdef SSL_R_TLSV1_ALERT_ACCESS_DENIED + {"TLSV1_ALERT_ACCESS_DENIED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_ACCESS_DENIED}, + #else + {"TLSV1_ALERT_ACCESS_DENIED", ERR_LIB_SSL, 1049}, + #endif + #ifdef SSL_R_TLSV1_ALERT_DECODE_ERROR + {"TLSV1_ALERT_DECODE_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECODE_ERROR}, + #else + {"TLSV1_ALERT_DECODE_ERROR", ERR_LIB_SSL, 1050}, + #endif + #ifdef SSL_R_TLSV1_ALERT_DECRYPTION_FAILED + {"TLSV1_ALERT_DECRYPTION_FAILED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECRYPTION_FAILED}, + #else + {"TLSV1_ALERT_DECRYPTION_FAILED", ERR_LIB_SSL, 1021}, + #endif + #ifdef SSL_R_TLSV1_ALERT_DECRYPT_ERROR + {"TLSV1_ALERT_DECRYPT_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_DECRYPT_ERROR}, + #else + {"TLSV1_ALERT_DECRYPT_ERROR", ERR_LIB_SSL, 1051}, + #endif + #ifdef SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION + {"TLSV1_ALERT_EXPORT_RESTRICTION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_EXPORT_RESTRICTION}, + #else + {"TLSV1_ALERT_EXPORT_RESTRICTION", ERR_LIB_SSL, 1060}, + #endif + #ifdef SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY + {"TLSV1_ALERT_INSUFFICIENT_SECURITY", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_INSUFFICIENT_SECURITY}, + #else + {"TLSV1_ALERT_INSUFFICIENT_SECURITY", ERR_LIB_SSL, 1071}, + #endif + #ifdef SSL_R_TLSV1_ALERT_INTERNAL_ERROR + {"TLSV1_ALERT_INTERNAL_ERROR", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_INTERNAL_ERROR}, + #else + {"TLSV1_ALERT_INTERNAL_ERROR", ERR_LIB_SSL, 1080}, + #endif + #ifdef SSL_R_TLSV1_ALERT_NO_RENEGOTIATION + {"TLSV1_ALERT_NO_RENEGOTIATION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_NO_RENEGOTIATION}, + #else + {"TLSV1_ALERT_NO_RENEGOTIATION", ERR_LIB_SSL, 1100}, + #endif + #ifdef SSL_R_TLSV1_ALERT_PROTOCOL_VERSION + {"TLSV1_ALERT_PROTOCOL_VERSION", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_PROTOCOL_VERSION}, + #else + {"TLSV1_ALERT_PROTOCOL_VERSION", ERR_LIB_SSL, 1070}, + #endif + #ifdef SSL_R_TLSV1_ALERT_RECORD_OVERFLOW + {"TLSV1_ALERT_RECORD_OVERFLOW", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_RECORD_OVERFLOW}, + #else + {"TLSV1_ALERT_RECORD_OVERFLOW", ERR_LIB_SSL, 1022}, + #endif + #ifdef SSL_R_TLSV1_ALERT_UNKNOWN_CA + {"TLSV1_ALERT_UNKNOWN_CA", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_UNKNOWN_CA}, + #else + {"TLSV1_ALERT_UNKNOWN_CA", ERR_LIB_SSL, 1048}, + #endif + #ifdef SSL_R_TLSV1_ALERT_USER_CANCELLED + {"TLSV1_ALERT_USER_CANCELLED", ERR_LIB_SSL, SSL_R_TLSV1_ALERT_USER_CANCELLED}, + #else + {"TLSV1_ALERT_USER_CANCELLED", ERR_LIB_SSL, 1090}, + #endif + #ifdef SSL_R_TLSV1_BAD_CERTIFICATE_HASH_VALUE + {"TLSV1_BAD_CERTIFICATE_HASH_VALUE", ERR_LIB_SSL, SSL_R_TLSV1_BAD_CERTIFICATE_HASH_VALUE}, + #else + {"TLSV1_BAD_CERTIFICATE_HASH_VALUE", ERR_LIB_SSL, 1114}, + #endif + #ifdef SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE + {"TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE", ERR_LIB_SSL, SSL_R_TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE}, + #else + {"TLSV1_BAD_CERTIFICATE_STATUS_RESPONSE", ERR_LIB_SSL, 1113}, + #endif + #ifdef SSL_R_TLSV1_CERTIFICATE_UNOBTAINABLE + {"TLSV1_CERTIFICATE_UNOBTAINABLE", ERR_LIB_SSL, SSL_R_TLSV1_CERTIFICATE_UNOBTAINABLE}, + #else + {"TLSV1_CERTIFICATE_UNOBTAINABLE", ERR_LIB_SSL, 1111}, + #endif + #ifdef SSL_R_TLSV1_UNRECOGNIZED_NAME + {"TLSV1_UNRECOGNIZED_NAME", ERR_LIB_SSL, SSL_R_TLSV1_UNRECOGNIZED_NAME}, + #else + {"TLSV1_UNRECOGNIZED_NAME", ERR_LIB_SSL, 1112}, + #endif + #ifdef SSL_R_TLSV1_UNSUPPORTED_EXTENSION + {"TLSV1_UNSUPPORTED_EXTENSION", ERR_LIB_SSL, SSL_R_TLSV1_UNSUPPORTED_EXTENSION}, + #else + {"TLSV1_UNSUPPORTED_EXTENSION", ERR_LIB_SSL, 1110}, + #endif + #ifdef SSL_R_TLS_CLIENT_CERT_REQ_WITH_ANON_CIPHER + {"TLS_CLIENT_CERT_REQ_WITH_ANON_CIPHER", ERR_LIB_SSL, SSL_R_TLS_CLIENT_CERT_REQ_WITH_ANON_CIPHER}, + #else + {"TLS_CLIENT_CERT_REQ_WITH_ANON_CIPHER", ERR_LIB_SSL, 232}, + #endif + #ifdef SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST + {"TLS_INVALID_ECPOINTFORMAT_LIST", ERR_LIB_SSL, SSL_R_TLS_INVALID_ECPOINTFORMAT_LIST}, + #else + {"TLS_INVALID_ECPOINTFORMAT_LIST", ERR_LIB_SSL, 157}, + #endif + #ifdef SSL_R_TLS_PEER_DID_NOT_RESPOND_WITH_CERTIFICATE_LIST + {"TLS_PEER_DID_NOT_RESPOND_WITH_CERTIFICATE_LIST", ERR_LIB_SSL, SSL_R_TLS_PEER_DID_NOT_RESPOND_WITH_CERTIFICATE_LIST}, + #else + {"TLS_PEER_DID_NOT_RESPOND_WITH_CERTIFICATE_LIST", ERR_LIB_SSL, 233}, + #endif + #ifdef SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG + {"TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG", ERR_LIB_SSL, SSL_R_TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG}, + #else + {"TLS_RSA_ENCRYPTED_VALUE_LENGTH_IS_WRONG", ERR_LIB_SSL, 234}, + #endif + #ifdef SSL_R_TRIED_TO_USE_UNSUPPORTED_CIPHER + {"TRIED_TO_USE_UNSUPPORTED_CIPHER", ERR_LIB_SSL, SSL_R_TRIED_TO_USE_UNSUPPORTED_CIPHER}, + #else + {"TRIED_TO_USE_UNSUPPORTED_CIPHER", ERR_LIB_SSL, 235}, + #endif + #ifdef SSL_R_UNABLE_TO_DECODE_DH_CERTS + {"UNABLE_TO_DECODE_DH_CERTS", ERR_LIB_SSL, SSL_R_UNABLE_TO_DECODE_DH_CERTS}, + #else + {"UNABLE_TO_DECODE_DH_CERTS", ERR_LIB_SSL, 236}, + #endif + #ifdef SSL_R_UNABLE_TO_DECODE_ECDH_CERTS + {"UNABLE_TO_DECODE_ECDH_CERTS", ERR_LIB_SSL, SSL_R_UNABLE_TO_DECODE_ECDH_CERTS}, + #else + {"UNABLE_TO_DECODE_ECDH_CERTS", ERR_LIB_SSL, 313}, + #endif + #ifdef SSL_R_UNABLE_TO_EXTRACT_PUBLIC_KEY + {"UNABLE_TO_EXTRACT_PUBLIC_KEY", ERR_LIB_SSL, SSL_R_UNABLE_TO_EXTRACT_PUBLIC_KEY}, + #else + {"UNABLE_TO_EXTRACT_PUBLIC_KEY", ERR_LIB_SSL, 237}, + #endif + #ifdef SSL_R_UNABLE_TO_FIND_DH_PARAMETERS + {"UNABLE_TO_FIND_DH_PARAMETERS", ERR_LIB_SSL, SSL_R_UNABLE_TO_FIND_DH_PARAMETERS}, + #else + {"UNABLE_TO_FIND_DH_PARAMETERS", ERR_LIB_SSL, 238}, + #endif + #ifdef SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS + {"UNABLE_TO_FIND_ECDH_PARAMETERS", ERR_LIB_SSL, SSL_R_UNABLE_TO_FIND_ECDH_PARAMETERS}, + #else + {"UNABLE_TO_FIND_ECDH_PARAMETERS", ERR_LIB_SSL, 314}, + #endif + #ifdef SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS + {"UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS", ERR_LIB_SSL, SSL_R_UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS}, + #else + {"UNABLE_TO_FIND_PUBLIC_KEY_PARAMETERS", ERR_LIB_SSL, 239}, + #endif + #ifdef SSL_R_UNABLE_TO_FIND_SSL_METHOD + {"UNABLE_TO_FIND_SSL_METHOD", ERR_LIB_SSL, SSL_R_UNABLE_TO_FIND_SSL_METHOD}, + #else + {"UNABLE_TO_FIND_SSL_METHOD", ERR_LIB_SSL, 240}, + #endif + #ifdef SSL_R_UNABLE_TO_LOAD_SSL2_MD5_ROUTINES + {"UNABLE_TO_LOAD_SSL2_MD5_ROUTINES", ERR_LIB_SSL, SSL_R_UNABLE_TO_LOAD_SSL2_MD5_ROUTINES}, + #else + {"UNABLE_TO_LOAD_SSL2_MD5_ROUTINES", ERR_LIB_SSL, 241}, + #endif + #ifdef SSL_R_UNABLE_TO_LOAD_SSL3_MD5_ROUTINES + {"UNABLE_TO_LOAD_SSL3_MD5_ROUTINES", ERR_LIB_SSL, SSL_R_UNABLE_TO_LOAD_SSL3_MD5_ROUTINES}, + #else + {"UNABLE_TO_LOAD_SSL3_MD5_ROUTINES", ERR_LIB_SSL, 242}, + #endif + #ifdef SSL_R_UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES + {"UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES", ERR_LIB_SSL, SSL_R_UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES}, + #else + {"UNABLE_TO_LOAD_SSL3_SHA1_ROUTINES", ERR_LIB_SSL, 243}, + #endif + #ifdef SSL_R_UNEXPECTED_MESSAGE + {"UNEXPECTED_MESSAGE", ERR_LIB_SSL, SSL_R_UNEXPECTED_MESSAGE}, + #else + {"UNEXPECTED_MESSAGE", ERR_LIB_SSL, 244}, + #endif + #ifdef SSL_R_UNEXPECTED_RECORD + {"UNEXPECTED_RECORD", ERR_LIB_SSL, SSL_R_UNEXPECTED_RECORD}, + #else + {"UNEXPECTED_RECORD", ERR_LIB_SSL, 245}, + #endif + #ifdef SSL_R_UNINITIALIZED + {"UNINITIALIZED", ERR_LIB_SSL, SSL_R_UNINITIALIZED}, + #else + {"UNINITIALIZED", ERR_LIB_SSL, 276}, + #endif + #ifdef SSL_R_UNKNOWN_ALERT_TYPE + {"UNKNOWN_ALERT_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_ALERT_TYPE}, + #else + {"UNKNOWN_ALERT_TYPE", ERR_LIB_SSL, 246}, + #endif + #ifdef SSL_R_UNKNOWN_CERTIFICATE_TYPE + {"UNKNOWN_CERTIFICATE_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_CERTIFICATE_TYPE}, + #else + {"UNKNOWN_CERTIFICATE_TYPE", ERR_LIB_SSL, 247}, + #endif + #ifdef SSL_R_UNKNOWN_CIPHER_RETURNED + {"UNKNOWN_CIPHER_RETURNED", ERR_LIB_SSL, SSL_R_UNKNOWN_CIPHER_RETURNED}, + #else + {"UNKNOWN_CIPHER_RETURNED", ERR_LIB_SSL, 248}, + #endif + #ifdef SSL_R_UNKNOWN_CIPHER_TYPE + {"UNKNOWN_CIPHER_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_CIPHER_TYPE}, + #else + {"UNKNOWN_CIPHER_TYPE", ERR_LIB_SSL, 249}, + #endif + #ifdef SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE + {"UNKNOWN_KEY_EXCHANGE_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_KEY_EXCHANGE_TYPE}, + #else + {"UNKNOWN_KEY_EXCHANGE_TYPE", ERR_LIB_SSL, 250}, + #endif + #ifdef SSL_R_UNKNOWN_PKEY_TYPE + {"UNKNOWN_PKEY_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_PKEY_TYPE}, + #else + {"UNKNOWN_PKEY_TYPE", ERR_LIB_SSL, 251}, + #endif + #ifdef SSL_R_UNKNOWN_PROTOCOL + {"UNKNOWN_PROTOCOL", ERR_LIB_SSL, SSL_R_UNKNOWN_PROTOCOL}, + #else + {"UNKNOWN_PROTOCOL", ERR_LIB_SSL, 252}, + #endif + #ifdef SSL_R_UNKNOWN_REMOTE_ERROR_TYPE + {"UNKNOWN_REMOTE_ERROR_TYPE", ERR_LIB_SSL, SSL_R_UNKNOWN_REMOTE_ERROR_TYPE}, + #else + {"UNKNOWN_REMOTE_ERROR_TYPE", ERR_LIB_SSL, 253}, + #endif + #ifdef SSL_R_UNKNOWN_SSL_VERSION + {"UNKNOWN_SSL_VERSION", ERR_LIB_SSL, SSL_R_UNKNOWN_SSL_VERSION}, + #else + {"UNKNOWN_SSL_VERSION", ERR_LIB_SSL, 254}, + #endif + #ifdef SSL_R_UNKNOWN_STATE + {"UNKNOWN_STATE", ERR_LIB_SSL, SSL_R_UNKNOWN_STATE}, + #else + {"UNKNOWN_STATE", ERR_LIB_SSL, 255}, + #endif + #ifdef SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED + {"UNSAFE_LEGACY_RENEGOTIATION_DISABLED", ERR_LIB_SSL, SSL_R_UNSAFE_LEGACY_RENEGOTIATION_DISABLED}, + #else + {"UNSAFE_LEGACY_RENEGOTIATION_DISABLED", ERR_LIB_SSL, 338}, + #endif + #ifdef SSL_R_UNSUPPORTED_CIPHER + {"UNSUPPORTED_CIPHER", ERR_LIB_SSL, SSL_R_UNSUPPORTED_CIPHER}, + #else + {"UNSUPPORTED_CIPHER", ERR_LIB_SSL, 256}, + #endif + #ifdef SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM + {"UNSUPPORTED_COMPRESSION_ALGORITHM", ERR_LIB_SSL, SSL_R_UNSUPPORTED_COMPRESSION_ALGORITHM}, + #else + {"UNSUPPORTED_COMPRESSION_ALGORITHM", ERR_LIB_SSL, 257}, + #endif + #ifdef SSL_R_UNSUPPORTED_DIGEST_TYPE + {"UNSUPPORTED_DIGEST_TYPE", ERR_LIB_SSL, SSL_R_UNSUPPORTED_DIGEST_TYPE}, + #else + {"UNSUPPORTED_DIGEST_TYPE", ERR_LIB_SSL, 326}, + #endif + #ifdef SSL_R_UNSUPPORTED_ELLIPTIC_CURVE + {"UNSUPPORTED_ELLIPTIC_CURVE", ERR_LIB_SSL, SSL_R_UNSUPPORTED_ELLIPTIC_CURVE}, + #else + {"UNSUPPORTED_ELLIPTIC_CURVE", ERR_LIB_SSL, 315}, + #endif + #ifdef SSL_R_UNSUPPORTED_PROTOCOL + {"UNSUPPORTED_PROTOCOL", ERR_LIB_SSL, SSL_R_UNSUPPORTED_PROTOCOL}, + #else + {"UNSUPPORTED_PROTOCOL", ERR_LIB_SSL, 258}, + #endif + #ifdef SSL_R_UNSUPPORTED_SSL_VERSION + {"UNSUPPORTED_SSL_VERSION", ERR_LIB_SSL, SSL_R_UNSUPPORTED_SSL_VERSION}, + #else + {"UNSUPPORTED_SSL_VERSION", ERR_LIB_SSL, 259}, + #endif + #ifdef SSL_R_UNSUPPORTED_STATUS_TYPE + {"UNSUPPORTED_STATUS_TYPE", ERR_LIB_SSL, SSL_R_UNSUPPORTED_STATUS_TYPE}, + #else + {"UNSUPPORTED_STATUS_TYPE", ERR_LIB_SSL, 329}, + #endif + #ifdef SSL_R_WRITE_BIO_NOT_SET + {"WRITE_BIO_NOT_SET", ERR_LIB_SSL, SSL_R_WRITE_BIO_NOT_SET}, + #else + {"WRITE_BIO_NOT_SET", ERR_LIB_SSL, 260}, + #endif + #ifdef SSL_R_WRONG_CIPHER_RETURNED + {"WRONG_CIPHER_RETURNED", ERR_LIB_SSL, SSL_R_WRONG_CIPHER_RETURNED}, + #else + {"WRONG_CIPHER_RETURNED", ERR_LIB_SSL, 261}, + #endif + #ifdef SSL_R_WRONG_MESSAGE_TYPE + {"WRONG_MESSAGE_TYPE", ERR_LIB_SSL, SSL_R_WRONG_MESSAGE_TYPE}, + #else + {"WRONG_MESSAGE_TYPE", ERR_LIB_SSL, 262}, + #endif + #ifdef SSL_R_WRONG_NUMBER_OF_KEY_BITS + {"WRONG_NUMBER_OF_KEY_BITS", ERR_LIB_SSL, SSL_R_WRONG_NUMBER_OF_KEY_BITS}, + #else + {"WRONG_NUMBER_OF_KEY_BITS", ERR_LIB_SSL, 263}, + #endif + #ifdef SSL_R_WRONG_SIGNATURE_LENGTH + {"WRONG_SIGNATURE_LENGTH", ERR_LIB_SSL, SSL_R_WRONG_SIGNATURE_LENGTH}, + #else + {"WRONG_SIGNATURE_LENGTH", ERR_LIB_SSL, 264}, + #endif + #ifdef SSL_R_WRONG_SIGNATURE_SIZE + {"WRONG_SIGNATURE_SIZE", ERR_LIB_SSL, SSL_R_WRONG_SIGNATURE_SIZE}, + #else + {"WRONG_SIGNATURE_SIZE", ERR_LIB_SSL, 265}, + #endif + #ifdef SSL_R_WRONG_SSL_VERSION + {"WRONG_SSL_VERSION", ERR_LIB_SSL, SSL_R_WRONG_SSL_VERSION}, + #else + {"WRONG_SSL_VERSION", ERR_LIB_SSL, 266}, + #endif + #ifdef SSL_R_WRONG_VERSION_NUMBER + {"WRONG_VERSION_NUMBER", ERR_LIB_SSL, SSL_R_WRONG_VERSION_NUMBER}, + #else + {"WRONG_VERSION_NUMBER", ERR_LIB_SSL, 267}, + #endif + #ifdef SSL_R_X509_LIB + {"X509_LIB", ERR_LIB_SSL, SSL_R_X509_LIB}, + #else + {"X509_LIB", ERR_LIB_SSL, 268}, + #endif + #ifdef SSL_R_X509_VERIFICATION_SETUP_PROBLEMS + {"X509_VERIFICATION_SETUP_PROBLEMS", ERR_LIB_SSL, SSL_R_X509_VERIFICATION_SETUP_PROBLEMS}, + #else + {"X509_VERIFICATION_SETUP_PROBLEMS", ERR_LIB_SSL, 269}, + #endif + #ifdef X509_R_BAD_X509_FILETYPE + {"BAD_X509_FILETYPE", ERR_LIB_X509, X509_R_BAD_X509_FILETYPE}, + #else + {"BAD_X509_FILETYPE", ERR_LIB_X509, 100}, + #endif + #ifdef X509_R_BASE64_DECODE_ERROR + {"BASE64_DECODE_ERROR", ERR_LIB_X509, X509_R_BASE64_DECODE_ERROR}, + #else + {"BASE64_DECODE_ERROR", ERR_LIB_X509, 118}, + #endif + #ifdef X509_R_CANT_CHECK_DH_KEY + {"CANT_CHECK_DH_KEY", ERR_LIB_X509, X509_R_CANT_CHECK_DH_KEY}, + #else + {"CANT_CHECK_DH_KEY", ERR_LIB_X509, 114}, + #endif + #ifdef X509_R_CERT_ALREADY_IN_HASH_TABLE + {"CERT_ALREADY_IN_HASH_TABLE", ERR_LIB_X509, X509_R_CERT_ALREADY_IN_HASH_TABLE}, + #else + {"CERT_ALREADY_IN_HASH_TABLE", ERR_LIB_X509, 101}, + #endif + #ifdef X509_R_ERR_ASN1_LIB + {"ERR_ASN1_LIB", ERR_LIB_X509, X509_R_ERR_ASN1_LIB}, + #else + {"ERR_ASN1_LIB", ERR_LIB_X509, 102}, + #endif + #ifdef X509_R_INVALID_DIRECTORY + {"INVALID_DIRECTORY", ERR_LIB_X509, X509_R_INVALID_DIRECTORY}, + #else + {"INVALID_DIRECTORY", ERR_LIB_X509, 113}, + #endif + #ifdef X509_R_INVALID_FIELD_NAME + {"INVALID_FIELD_NAME", ERR_LIB_X509, X509_R_INVALID_FIELD_NAME}, + #else + {"INVALID_FIELD_NAME", ERR_LIB_X509, 119}, + #endif + #ifdef X509_R_INVALID_TRUST + {"INVALID_TRUST", ERR_LIB_X509, X509_R_INVALID_TRUST}, + #else + {"INVALID_TRUST", ERR_LIB_X509, 123}, + #endif + #ifdef X509_R_KEY_TYPE_MISMATCH + {"KEY_TYPE_MISMATCH", ERR_LIB_X509, X509_R_KEY_TYPE_MISMATCH}, + #else + {"KEY_TYPE_MISMATCH", ERR_LIB_X509, 115}, + #endif + #ifdef X509_R_KEY_VALUES_MISMATCH + {"KEY_VALUES_MISMATCH", ERR_LIB_X509, X509_R_KEY_VALUES_MISMATCH}, + #else + {"KEY_VALUES_MISMATCH", ERR_LIB_X509, 116}, + #endif + #ifdef X509_R_LOADING_CERT_DIR + {"LOADING_CERT_DIR", ERR_LIB_X509, X509_R_LOADING_CERT_DIR}, + #else + {"LOADING_CERT_DIR", ERR_LIB_X509, 103}, + #endif + #ifdef X509_R_LOADING_DEFAULTS + {"LOADING_DEFAULTS", ERR_LIB_X509, X509_R_LOADING_DEFAULTS}, + #else + {"LOADING_DEFAULTS", ERR_LIB_X509, 104}, + #endif + #ifdef X509_R_METHOD_NOT_SUPPORTED + {"METHOD_NOT_SUPPORTED", ERR_LIB_X509, X509_R_METHOD_NOT_SUPPORTED}, + #else + {"METHOD_NOT_SUPPORTED", ERR_LIB_X509, 124}, + #endif + #ifdef X509_R_NO_CERT_SET_FOR_US_TO_VERIFY + {"NO_CERT_SET_FOR_US_TO_VERIFY", ERR_LIB_X509, X509_R_NO_CERT_SET_FOR_US_TO_VERIFY}, + #else + {"NO_CERT_SET_FOR_US_TO_VERIFY", ERR_LIB_X509, 105}, + #endif + #ifdef X509_R_PUBLIC_KEY_DECODE_ERROR + {"PUBLIC_KEY_DECODE_ERROR", ERR_LIB_X509, X509_R_PUBLIC_KEY_DECODE_ERROR}, + #else + {"PUBLIC_KEY_DECODE_ERROR", ERR_LIB_X509, 125}, + #endif + #ifdef X509_R_PUBLIC_KEY_ENCODE_ERROR + {"PUBLIC_KEY_ENCODE_ERROR", ERR_LIB_X509, X509_R_PUBLIC_KEY_ENCODE_ERROR}, + #else + {"PUBLIC_KEY_ENCODE_ERROR", ERR_LIB_X509, 126}, + #endif + #ifdef X509_R_SHOULD_RETRY + {"SHOULD_RETRY", ERR_LIB_X509, X509_R_SHOULD_RETRY}, + #else + {"SHOULD_RETRY", ERR_LIB_X509, 106}, + #endif + #ifdef X509_R_UNABLE_TO_FIND_PARAMETERS_IN_CHAIN + {"UNABLE_TO_FIND_PARAMETERS_IN_CHAIN", ERR_LIB_X509, X509_R_UNABLE_TO_FIND_PARAMETERS_IN_CHAIN}, + #else + {"UNABLE_TO_FIND_PARAMETERS_IN_CHAIN", ERR_LIB_X509, 107}, + #endif + #ifdef X509_R_UNABLE_TO_GET_CERTS_PUBLIC_KEY + {"UNABLE_TO_GET_CERTS_PUBLIC_KEY", ERR_LIB_X509, X509_R_UNABLE_TO_GET_CERTS_PUBLIC_KEY}, + #else + {"UNABLE_TO_GET_CERTS_PUBLIC_KEY", ERR_LIB_X509, 108}, + #endif + #ifdef X509_R_UNKNOWN_KEY_TYPE + {"UNKNOWN_KEY_TYPE", ERR_LIB_X509, X509_R_UNKNOWN_KEY_TYPE}, + #else + {"UNKNOWN_KEY_TYPE", ERR_LIB_X509, 117}, + #endif + #ifdef X509_R_UNKNOWN_NID + {"UNKNOWN_NID", ERR_LIB_X509, X509_R_UNKNOWN_NID}, + #else + {"UNKNOWN_NID", ERR_LIB_X509, 109}, + #endif + #ifdef X509_R_UNKNOWN_PURPOSE_ID + {"UNKNOWN_PURPOSE_ID", ERR_LIB_X509, X509_R_UNKNOWN_PURPOSE_ID}, + #else + {"UNKNOWN_PURPOSE_ID", ERR_LIB_X509, 121}, + #endif + #ifdef X509_R_UNKNOWN_TRUST_ID + {"UNKNOWN_TRUST_ID", ERR_LIB_X509, X509_R_UNKNOWN_TRUST_ID}, + #else + {"UNKNOWN_TRUST_ID", ERR_LIB_X509, 120}, + #endif + #ifdef X509_R_UNSUPPORTED_ALGORITHM + {"UNSUPPORTED_ALGORITHM", ERR_LIB_X509, X509_R_UNSUPPORTED_ALGORITHM}, + #else + {"UNSUPPORTED_ALGORITHM", ERR_LIB_X509, 111}, + #endif + #ifdef X509_R_WRONG_LOOKUP_TYPE + {"WRONG_LOOKUP_TYPE", ERR_LIB_X509, X509_R_WRONG_LOOKUP_TYPE}, + #else + {"WRONG_LOOKUP_TYPE", ERR_LIB_X509, 112}, + #endif + #ifdef X509_R_WRONG_TYPE + {"WRONG_TYPE", ERR_LIB_X509, X509_R_WRONG_TYPE}, + #else + {"WRONG_TYPE", ERR_LIB_X509, 122}, + #endif + { NULL } +}; diff --git a/Tools/ssl/make_ssl_data.py b/Tools/ssl/make_ssl_data.py new file mode 100755 index 0000000..10244d1 --- /dev/null +++ b/Tools/ssl/make_ssl_data.py @@ -0,0 +1,68 @@ +#! /usr/bin/env python3 + +""" +This script should be called *manually* when we want to upgrade SSLError +`library` and `reason` mnemnonics to a more recent OpenSSL version. + +It takes two arguments: +- the path to the OpenSSL include files' directory + (e.g. openssl-1.0.1-beta3/include/openssl/) +- the path to the C file to be generated + (probably Modules/_ssl_data.h) +""" + +import datetime +import os +import re +import sys + + +def parse_error_codes(h_file, prefix): + pat = re.compile(r"#define\W+(%s([\w]+))\W+(\d+)\b" % re.escape(prefix)) + codes = [] + with open(h_file, "r", encoding="latin1") as f: + for line in f: + match = pat.search(line) + if match: + code, name, num = match.groups() + num = int(num) + codes.append((code, name, num)) + return codes + +if __name__ == "__main__": + openssl_inc = sys.argv[1] + outfile = sys.argv[2] + use_stdout = outfile == '-' + f = sys.stdout if use_stdout else open(outfile, "w") + error_libraries = ( + # (library code, mnemonic, error prefix, header file) + ('ERR_LIB_PEM', 'PEM', 'PEM_R_', 'pem.h'), + ('ERR_LIB_SSL', 'SSL', 'SSL_R_', 'ssl.h'), + ('ERR_LIB_X509', 'X509', 'X509_R_', 'x509.h'), + ) + def w(l): + f.write(l + "\n") + w("/* File generated by Tools/ssl/make_ssl_data.py */") + w("/* Generated on %s */" % datetime.datetime.now().isoformat()) + w("") + + w("static struct py_ssl_library_code library_codes[] = {") + for libcode, mnemo, _, _ in error_libraries: + w(' {"%s", %s},' % (mnemo, libcode)) + w(' { NULL }') + w('};') + w("") + + w("static struct py_ssl_error_code error_codes[] = {") + for libcode, _, prefix, h_file in error_libraries: + codes = parse_error_codes(os.path.join(openssl_inc, h_file), prefix) + for code, name, num in sorted(codes): + w(' #ifdef %s' % (code)) + w(' {"%s", %s, %s},' % (name, libcode, code)) + w(' #else') + w(' {"%s", %s, %d},' % (name, libcode, num)) + w(' #endif') + w(' { NULL }') + w('};') + if not use_stdout: + f.close() diff --git a/Tools/ssl/test_multiple_versions.py b/Tools/ssl/test_multiple_versions.py new file mode 100644 index 0000000..fc7a967 --- /dev/null +++ b/Tools/ssl/test_multiple_versions.py @@ -0,0 +1,241 @@ +#./python +"""Run Python tests with multiple installations of OpenSSL + +The script + + (1) downloads OpenSSL tar bundle + (2) extracts it to ../openssl/src/openssl-VERSION/ + (3) compiles OpenSSL + (4) installs OpenSSL into ../openssl/VERSION/ + (5) forces a recompilation of Python modules using the + header and library files from ../openssl/VERSION/ + (6) runs Python's test suite + +The script must be run with Python's build directory as current working +directory: + + ./python Tools/ssl/test_multiple_versions.py + +The script uses LD_RUN_PATH, LD_LIBRARY_PATH, CPPFLAGS and LDFLAGS to bend +search paths for header files and shared libraries. It's known to work on +Linux with GCC 4.x. + +(c) 2013 Christian Heimes +""" +import logging +import os +import tarfile +import shutil +import subprocess +import sys +from urllib import urlopen + +log = logging.getLogger("multissl") + +OPENSSL_VERSIONS = [ + "0.9.7m", "0.9.8i", "0.9.8l", "0.9.8m", "0.9.8y", "1.0.0k", "1.0.1e" +] +FULL_TESTS = [ + "test_asyncio", "test_ftplib", "test_hashlib", "test_httplib", + "test_imaplib", "test_nntplib", "test_poplib", "test_smtplib", + "test_smtpnet", "test_urllib2_localnet", "test_venv" +] +MINIMAL_TESTS = ["test_ssl", "test_hashlib"] +CADEFAULT = True +HERE = os.path.abspath(os.getcwd()) +DEST_DIR = os.path.abspath(os.path.join(HERE, os.pardir, "openssl")) + + +class BuildSSL(object): + url_template = "https://www.openssl.org/source/openssl-{}.tar.gz" + + module_files = ["Modules/_ssl.c", + "Modules/socketmodule.c", + "Modules/_hashopenssl.c"] + + def __init__(self, version, openssl_compile_args=(), destdir=DEST_DIR): + self._check_python_builddir() + self.version = version + self.openssl_compile_args = openssl_compile_args + # installation directory + self.install_dir = os.path.join(destdir, version) + # source file + self.src_file = os.path.join(destdir, "src", + "openssl-{}.tar.gz".format(version)) + # build directory (removed after install) + self.build_dir = os.path.join(destdir, "src", + "openssl-{}".format(version)) + + @property + def openssl_cli(self): + """openssl CLI binary""" + return os.path.join(self.install_dir, "bin", "openssl") + + @property + def openssl_version(self): + """output of 'bin/openssl version'""" + env = os.environ.copy() + env["LD_LIBRARY_PATH"] = self.lib_dir + cmd = [self.openssl_cli, "version"] + return self._subprocess_output(cmd, env=env) + + @property + def pyssl_version(self): + """Value of ssl.OPENSSL_VERSION""" + env = os.environ.copy() + env["LD_LIBRARY_PATH"] = self.lib_dir + cmd = ["./python", "-c", "import ssl; print(ssl.OPENSSL_VERSION)"] + return self._subprocess_output(cmd, env=env) + + @property + def include_dir(self): + return os.path.join(self.install_dir, "include") + + @property + def lib_dir(self): + return os.path.join(self.install_dir, "lib") + + @property + def has_openssl(self): + return os.path.isfile(self.openssl_cli) + + @property + def has_src(self): + return os.path.isfile(self.src_file) + + def _subprocess_call(self, cmd, stdout=subprocess.DEVNULL, env=None, + **kwargs): + log.debug("Call '{}'".format(" ".join(cmd))) + return subprocess.check_call(cmd, stdout=stdout, env=env, **kwargs) + + def _subprocess_output(self, cmd, env=None, **kwargs): + log.debug("Call '{}'".format(" ".join(cmd))) + out = subprocess.check_output(cmd, env=env) + return out.strip().decode("utf-8") + + def _check_python_builddir(self): + if not os.path.isfile("python") or not os.path.isfile("setup.py"): + raise ValueError("Script must be run in Python build directory") + + def _download_openssl(self): + """Download OpenSSL source dist""" + src_dir = os.path.dirname(self.src_file) + if not os.path.isdir(src_dir): + os.makedirs(src_dir) + url = self.url_template.format(self.version) + log.info("Downloading OpenSSL from {}".format(url)) + req = urlopen(url, cadefault=CADEFAULT) + # KISS, read all, write all + data = req.read() + log.info("Storing {}".format(self.src_file)) + with open(self.src_file, "wb") as f: + f.write(data) + + def _unpack_openssl(self): + """Unpack tar.gz bundle""" + # cleanup + if os.path.isdir(self.build_dir): + shutil.rmtree(self.build_dir) + os.makedirs(self.build_dir) + + tf = tarfile.open(self.src_file) + base = "openssl-{}/".format(self.version) + # force extraction into build dir + members = tf.getmembers() + for member in members: + if not member.name.startswith(base): + raise ValueError(member.name) + member.name = member.name[len(base):] + log.info("Unpacking files to {}".format(self.build_dir)) + tf.extractall(self.build_dir, members) + + def _build_openssl(self): + """Now build openssl""" + log.info("Running build in {}".format(self.install_dir)) + cwd = self.build_dir + cmd = ["./config", "shared", "--prefix={}".format(self.install_dir)] + cmd.extend(self.openssl_compile_args) + self._subprocess_call(cmd, cwd=cwd) + self._subprocess_call(["make"], cwd=cwd) + + def _install_openssl(self, remove=True): + self._subprocess_call(["make", "install"], cwd=self.build_dir) + if remove: + shutil.rmtree(self.build_dir) + + def install_openssl(self): + if not self.has_openssl: + if not self.has_src: + self._download_openssl() + else: + log.debug("Already has src {}".format(self.src_file)) + self._unpack_openssl() + self._build_openssl() + self._install_openssl() + else: + log.info("Already has installation {}".format(self.install_dir)) + # validate installation + version = self.openssl_version + if self.version not in version: + raise ValueError(version) + + def touch_pymods(self): + # force a rebuild of all modules that use OpenSSL APIs + for fname in self.module_files: + os.utime(fname) + + def recompile_pymods(self): + log.info("Using OpenSSL build from {}".format(self.build_dir)) + # overwrite header and library search paths + env = os.environ.copy() + env["CPPFLAGS"] = "-I{}".format(self.include_dir) + env["LDFLAGS"] = "-L{}".format(self.lib_dir) + # set rpath + env["LD_RUN_PATH"] = self.lib_dir + + log.info("Rebuilding Python modules") + self.touch_pymods() + cmd = ["./python", "setup.py", "build"] + self._subprocess_call(cmd, env=env) + + def check_pyssl(self): + version = self.pyssl_version + if self.version not in version: + raise ValueError(version) + + def run_pytests(self, *args): + cmd = ["./python", "-m", "test"] + cmd.extend(args) + self._subprocess_call(cmd, stdout=None) + + def run_python_tests(self, *args): + self.recompile_pymods() + self.check_pyssl() + self.run_pytests(*args) + + +def main(*args): + builders = [] + for version in OPENSSL_VERSIONS: + if version in ("0.9.8i", "0.9.8l"): + openssl_compile_args = ("no-asm",) + else: + openssl_compile_args = () + builder = BuildSSL(version, openssl_compile_args) + builder.install_openssl() + builders.append(builder) + + for builder in builders: + builder.run_python_tests(*args) + # final touch + builder.touch_pymods() + + +if __name__ == "__main__": + logging.basicConfig(level=logging.INFO, + format="*** %(levelname)s %(message)s") + args = sys.argv[1:] + if not args: + args = ["-unetwork", "-v"] + args.extend(FULL_TESTS) + main(*args)