Exemplo n.º 1
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:
            exceptions.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 exceptions.TcpException(repr(e))
        elif isinstance(self.o, SSL.Connection):
            try:
                return self.o.recv(length, socket.MSG_PEEK)
            except SSL.Error as e:
                six.reraise(exceptions.TlsException,
                            exceptions.TlsException(str(e)),
                            sys.exc_info()[2])
        else:
            raise NotImplementedError("Can only peek into (pyOpenSSL) sockets")
Exemplo n.º 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:
            exceptions.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 exceptions.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(exceptions.TlsException, exceptions.TlsException(str(e)), sys.exc_info()[2])
        else:
            raise NotImplementedError("Can only peek into (pyOpenSSL) sockets")
Exemplo n.º 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 exceptions.TcpTimeout()
         except socket.timeout:
             raise exceptions.TcpTimeout()
         except socket.error as e:
             raise exceptions.TcpDisconnect(str(e))
         except SSL.SysCallError as e:
             if e.args == (-1, 'Unexpected EOF'):
                 break
             raise exceptions.TlsException(str(e))
         except SSL.Error as e:
             raise exceptions.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
Exemplo n.º 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 exceptions.TlsException(
                 "SSL client certificate error: %s" % str(v))
     return context
Exemplo n.º 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 exceptions.TlsException("SSL handshake error: %s" % repr(v))
        self.ssl_established = True
        self.rfile.set_descriptor(self.connection)
        self.wfile.set_descriptor(self.connection)
Exemplo n.º 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
        """
        verification_mode = sslctx_kwargs.get('verify_options', None)
        if verification_mode == SSL.VERIFY_PEER and not sni:
            raise exceptions.TlsException(
                "Cannot validate certificate hostname without SNI")

        context = self.create_ssl_context(alpn_protos=alpn_protos,
                                          sni=sni,
                                          **sslctx_kwargs)
        self.connection = SSL.Connection(context, self.connection)
        if sni:
            self.sni = sni
            self.connection.set_tlsext_host_name(sni.encode("idna"))
        self.connection.set_connect_state()
        try:
            self.connection.do_handshake()
        except SSL.Error as v:
            if self.ssl_verification_error:
                raise self.ssl_verification_error
            else:
                raise exceptions.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:
                raise self.ssl_verification_error

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

        # Keep all server certificates in a list
        for i in self.connection.get_peer_cert_chain():
            self.server_certs.append(certutils.SSLCert(i))

        # 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
            else:
                hostname = "no-hostname"
            ssl_match_hostname.match_hostname(crt, hostname)
        except (ValueError, ssl_match_hostname.CertificateError) as e:
            self.ssl_verification_error = exceptions.InvalidCertificateException(
                "Certificate Verification Error for {}: {}".format(
                    sni or repr(self.address), str(e)))
            if verification_mode == SSL.VERIFY_PEER:
                raise self.ssl_verification_error

        self.ssl_established = True
        self.rfile.set_descriptor(self.connection)
        self.wfile.set_descriptor(self.connection)
Exemplo n.º 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,
        sni=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 = exceptions.InvalidCertificateException(
                        "Certificate Verification Error for {}: {} (errno: {}, depth: {})"
                        .format(
                            sni,
                            strutils.native(
                                SSL._ffi.string(
                                    SSL._lib.X509_verify_cert_error_string(
                                        errno)), "utf8"), errno, 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 exceptions.TlsException(
                    "SSL cipher specification error: %s" % str(v))

        # SSLKEYLOGFILE
        if log_ssl_key:
            context.set_info_callback(log_ssl_key)

        if 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 exceptions.TlsException(
                    "ALPN error: only define alpn_select (string) OR alpn_select_callback (method)."
                )

        return context