Exemple #1
0
    def convert_to_ssl(self, sni=None, alpn_protos=None, **sslctx_kwargs):
        """
            cert: Path to a file containing both client cert and private key.

            options: A bit field consisting of OpenSSL.SSL.OP_* values
            verify_options: A bit field consisting of OpenSSL.SSL.VERIFY_* values
            ca_path: Path to a directory of trusted CA certificates prepared using the c_rehash tool
            ca_pemfile: Path to a PEM formatted trusted CA certificate
        """
        verification_mode = sslctx_kwargs.get('verify_options', None)
        if verification_mode == SSL.VERIFY_PEER and not sni:
            raise TlsException("Cannot validate certificate hostname without SNI")

        context = self.create_ssl_context(
            alpn_protos=alpn_protos,
            **sslctx_kwargs
        )
        self.connection = SSL.Connection(context, self.connection)
        if sni:
            self.sni = sni
            self.connection.set_tlsext_host_name(sni)
        self.connection.set_connect_state()
        try:
            self.connection.do_handshake()
        except SSL.Error as v:
            if self.ssl_verification_error:
                raise InvalidCertificateException("SSL handshake error: %s" % repr(v))
            else:
                raise TlsException("SSL handshake error: %s" % repr(v))
        else:
            # Fix for pre v1.0 OpenSSL, which doesn't throw an exception on
            # certificate validation failure
            if verification_mode == SSL.VERIFY_PEER and self.ssl_verification_error is not None:
                raise InvalidCertificateException("SSL handshake error: certificate verify failed")

        self.cert = certutils.SSLCert(self.connection.get_peer_certificate())

        # Validate TLS Hostname
        try:
            crt = dict(
                subjectAltName=[("DNS", x.decode("ascii", "strict")) for x in self.cert.altnames]
            )
            if self.cert.cn:
                crt["subject"] = [[["commonName", self.cert.cn.decode("ascii", "strict")]]]
            if sni:
                hostname = sni.decode("ascii", "strict")
            else:
                hostname = "no-hostname"
            ssl_match_hostname.match_hostname(crt, hostname)
        except (ValueError, ssl_match_hostname.CertificateError) as e:
            self.ssl_verification_error = dict(depth=0, errno="Invalid Hostname")
            if verification_mode == SSL.VERIFY_PEER:
                raise InvalidCertificateException("Presented certificate for {} is not valid: {}".format(sni, str(e)))

        self.ssl_established = True
        self.rfile.set_descriptor(self.connection)
        self.wfile.set_descriptor(self.connection)
Exemple #2
0
    def peek(self, length):
        """
        Tries to peek into the underlying file object.

        Returns:
            Up to the next N bytes if peeking is successful.

        Raises:
            TcpException if there was an error with the socket
            TlsException if there was an error with pyOpenSSL.
            NotImplementedError if the underlying file object is not a [pyOpenSSL] socket
        """
        if isinstance(self.o, socket_fileobject):
            try:
                return self.o._sock.recv(length, socket.MSG_PEEK)
            except socket.error as e:
                raise TcpException(repr(e))
        elif isinstance(self.o, SSL.Connection):
            try:
                if tuple(int(x) for x in OpenSSL.__version__.split(".")[:2]) > (0, 15):
                    return self.o.recv(length, socket.MSG_PEEK)
                else:
                    # TODO: remove once a new version is released
                    # Polyfill for pyOpenSSL <= 0.15.1
                    # Taken from https://github.com/pyca/pyopenssl/commit/1d95dea7fea03c7c0df345a5ea30c12d8a0378d2
                    buf = SSL._ffi.new("char[]", length)
                    result = SSL._lib.SSL_peek(self.o._ssl, buf, length)
                    self.o._raise_ssl_error(self.o._ssl, result)
                    return SSL._ffi.buffer(buf, result)[:]
            except SSL.Error as e:
                six.reraise(TlsException, TlsException(str(e)), sys.exc_info()[2])
        else:
            raise NotImplementedError("Can only peek into (pyOpenSSL) sockets")
Exemple #3
0
 def read(self, length):
     """
         If length is -1, we read until connection closes.
     """
     result = b''
     start = time.time()
     while length == -1 or length > 0:
         if length == -1 or length > self.BLOCKSIZE:
             rlen = self.BLOCKSIZE
         else:
             rlen = length
         try:
             data = self.o.read(rlen)
         except SSL.ZeroReturnError:
             # TLS connection was shut down cleanly
             break
         except (SSL.WantWriteError, SSL.WantReadError):
             # From the OpenSSL docs:
             # If the underlying BIO is non-blocking, SSL_read() will also return when the
             # underlying BIO could not satisfy the needs of SSL_read() to continue the
             # operation. In this case a call to SSL_get_error with the return value of
             # SSL_read() will yield SSL_ERROR_WANT_READ or SSL_ERROR_WANT_WRITE.
             if (time.time() - start) < self.o.gettimeout():
                 time.sleep(0.1)
                 continue
             else:
                 raise TcpTimeout()
         except socket.timeout:
             raise TcpTimeout()
         except socket.error as e:
             raise TcpDisconnect(str(e))
         except SSL.SysCallError as e:
             if e.args == (-1, 'Unexpected EOF'):
                 break
             raise TlsException(str(e))
         except SSL.Error as e:
             raise TlsException(str(e))
         self.first_byte_timestamp = self.first_byte_timestamp or time.time(
         )
         if not data:
             break
         result += data
         if length != -1:
             length -= len(data)
     self.add_log(result)
     return result
Exemple #4
0
 def create_ssl_context(self, cert=None, alpn_protos=None, **sslctx_kwargs):
     context = self._create_ssl_context(alpn_protos=alpn_protos,
                                        **sslctx_kwargs)
     # Client Certs
     if cert:
         try:
             context.use_privatekey_file(cert)
             context.use_certificate_file(cert)
         except SSL.Error as v:
             raise TlsException("SSL client certificate error: %s" % str(v))
     return context
Exemple #5
0
    def convert_to_ssl(self, cert, key, **sslctx_kwargs):
        """
        Convert connection to SSL.
        For a list of parameters, see BaseHandler._create_ssl_context(...)
        """

        context = self.create_ssl_context(cert, key, **sslctx_kwargs)
        self.connection = SSL.Connection(context, self.connection)
        self.connection.set_accept_state()
        try:
            self.connection.do_handshake()
        except SSL.Error as v:
            raise TlsException("SSL handshake error: %s" % repr(v))
        self.ssl_established = True
        self.rfile.set_descriptor(self.connection)
        self.wfile.set_descriptor(self.connection)
Exemple #6
0
    def convert_to_ssl(self, sni=None, alpn_protos=None, **sslctx_kwargs):
        """
            cert: Path to a file containing both client cert and private key.

            options: A bit field consisting of OpenSSL.SSL.OP_* values
            verify_options: A bit field consisting of OpenSSL.SSL.VERIFY_* values
            ca_path: Path to a directory of trusted CA certificates prepared using the c_rehash tool
            ca_pemfile: Path to a PEM formatted trusted CA certificate
        """
        context = self.create_ssl_context(alpn_protos=alpn_protos,
                                          **sslctx_kwargs)
        self.connection = SSL.Connection(context, self.connection)
        if sni:
            self.sni = sni
            self.connection.set_tlsext_host_name(sni)
        self.connection.set_connect_state()
        try:
            self.connection.do_handshake()
        except SSL.Error as v:
            if self.ssl_verification_error:
                raise InvalidCertificateException("SSL handshake error: %s" %
                                                  repr(v))
            else:
                raise TlsException("SSL handshake error: %s" % repr(v))

        # Fix for pre v1.0 OpenSSL, which doesn't throw an exception on
        # certificate validation failure
        verification_mode = sslctx_kwargs.get('verify_options', None)
        if self.ssl_verification_error is not None and verification_mode == SSL.VERIFY_PEER:
            raise InvalidCertificateException(
                "SSL handshake error: certificate verify failed")

        self.ssl_established = True
        self.cert = certutils.SSLCert(self.connection.get_peer_certificate())
        self.rfile.set_descriptor(self.connection)
        self.wfile.set_descriptor(self.connection)
Exemple #7
0
    def _create_ssl_context(
        self,
        method=SSL_DEFAULT_METHOD,
        options=SSL_DEFAULT_OPTIONS,
        verify_options=SSL.VERIFY_NONE,
        ca_path=None,
        ca_pemfile=None,
        cipher_list=None,
        alpn_protos=None,
        alpn_select=None,
        alpn_select_callback=None,
    ):
        """
        Creates an SSL Context.

        :param method: One of SSLv2_METHOD, SSLv3_METHOD, SSLv23_METHOD, TLSv1_METHOD, TLSv1_1_METHOD, or TLSv1_2_METHOD
        :param options: A bit field consisting of OpenSSL.SSL.OP_* values
        :param verify_options: A bit field consisting of OpenSSL.SSL.VERIFY_* values
        :param ca_path: Path to a directory of trusted CA certificates prepared using the c_rehash tool
        :param ca_pemfile: Path to a PEM formatted trusted CA certificate
        :param cipher_list: A textual OpenSSL cipher list, see https://www.openssl.org/docs/apps/ciphers.html
        :rtype : SSL.Context
        """
        context = SSL.Context(method)
        # Options (NO_SSLv2/3)
        if options is not None:
            context.set_options(options)

        # Verify Options (NONE/PEER and trusted CAs)
        if verify_options is not None:

            def verify_cert(conn, x509, errno, err_depth, is_cert_verified):
                if not is_cert_verified:
                    self.ssl_verification_error = dict(errno=errno,
                                                       depth=err_depth)
                return is_cert_verified

            context.set_verify(verify_options, verify_cert)
            if ca_path is None and ca_pemfile is None:
                ca_pemfile = certifi.where()
            context.load_verify_locations(ca_pemfile, ca_path)

        # Workaround for
        # https://github.com/pyca/pyopenssl/issues/190
        # https://github.com/mitmproxy/mitmproxy/issues/472
        # Options already set before are not cleared.
        context.set_mode(SSL._lib.SSL_MODE_AUTO_RETRY)

        # Cipher List
        if cipher_list:
            try:
                context.set_cipher_list(cipher_list)

                # TODO: maybe change this to with newer pyOpenSSL APIs
                context.set_tmp_ecdh(
                    OpenSSL.crypto.get_elliptic_curve('prime256v1'))
            except SSL.Error as v:
                raise TlsException("SSL cipher specification error: %s" %
                                   str(v))

        # SSLKEYLOGFILE
        if log_ssl_key:
            context.set_info_callback(log_ssl_key)

        if OpenSSL._util.lib.Cryptography_HAS_ALPN:
            if alpn_protos is not None:
                # advertise application layer protocols
                context.set_alpn_protos(alpn_protos)
            elif alpn_select is not None and alpn_select_callback is None:
                # select application layer protocol
                def alpn_select_callback(conn_, options):
                    if alpn_select in options:
                        return bytes(alpn_select)
                    else:  # pragma no cover
                        return options[0]

                context.set_alpn_select_callback(alpn_select_callback)
            elif alpn_select_callback is not None and alpn_select is None:
                context.set_alpn_select_callback(alpn_select_callback)
            elif alpn_select_callback is not None and alpn_select is not None:
                raise TlsException(
                    "ALPN error: only define alpn_select (string) OR alpn_select_callback (method)."
                )

        return context