示例#1
0
    def connect(self,
                loop,
                metadata,
                domain: str,
                host,
                port,
                negotiation_timeout,
                base_logger=None):
        """
        .. seealso::

           :meth:`BaseConnector.connect`
              For general information on the :meth:`connect` method.

        Connect to `host` at TCP port number `port`. The
        :class:`aioxmpp.security_layer.SecurityLayer` object `metadata` is used
        to determine the parameters of the TLS connection.

        First, a normal TCP connection is opened and the stream header is sent.
        The stream features are waited for, and then STARTTLS is negotiated if
        possible.

        :attr:`~.security_layer.SecurityLayer.tls_required` is honoured: if it
        is true and TLS negotiation fails, :class:`~.errors.TLSUnavailable` is
        raised. TLS negotiation is always attempted if
        :attr:`~.security_layer.SecurityLayer.tls_required` is true, even if
        the server does not advertise a STARTTLS stream feature. This might
        help to prevent trivial downgrade attacks, and we don’t have anything
        to lose at this point anymore anyways.

        :attr:`~.security_layer.SecurityLayer.ssl_context_factory` and
        :attr:`~.security_layer.SecurityLayer.certificate_verifier_factory` are
        used to configure the TLS connection.

        .. versionchanged:: 0.10

            The `negotiation_timeout` is set as
            :attr:`~.XMLStream.deadtime_hard_limit` on the returned XML stream.
        """

        features_future = asyncio.Future(loop=loop)

        stream = protocol.XMLStream(
            to=domain,
            features_future=features_future,
            base_logger=base_logger,
        )
        if base_logger is not None:
            logger = base_logger.getChild(type(self).__name__)
        else:
            logger = logging.getLogger(".".join([
                __name__,
                type(self).__qualname__,
            ]))

        try:
            transport, _ = yield from ssl_transport.create_starttls_connection(
                loop,
                lambda: stream,
                host=host,
                port=port,
                peer_hostname=host,
                server_hostname=domain,
                use_starttls=True,
            )
        except:  # NOQA
            stream.abort()
            raise

        stream.deadtime_hard_limit = timedelta(seconds=negotiation_timeout)

        features = yield from features_future

        try:
            features[nonza.StartTLSFeature]
        except KeyError:
            if not metadata.tls_required:
                return transport, stream, (yield from features_future)
            logger.debug(
                "attempting STARTTLS despite not announced since it is"
                " required")

        try:
            response = yield from protocol.send_and_wait_for(
                stream, [
                    nonza.StartTLS(),
                ], [
                    nonza.StartTLSFailure,
                    nonza.StartTLSProceed,
                ])
        except errors.StreamError as exc:
            raise errors.TLSUnavailable(
                "STARTTLS not supported by server, but required by client")

        if not isinstance(response, nonza.StartTLSProceed):
            if metadata.tls_required:
                message = ("server failed to STARTTLS")

                protocol.send_stream_error_and_close(
                    stream,
                    condition=errors.StreamErrorCondition.POLICY_VIOLATION,
                    text=message,
                )

                raise errors.TLSUnavailable(message)
            return transport, stream, (yield from features_future)

        verifier = metadata.certificate_verifier_factory()
        yield from verifier.pre_handshake(
            domain,
            host,
            port,
            metadata,
        )

        ssl_context = metadata.ssl_context_factory()
        verifier.setup_context(ssl_context, transport)

        yield from stream.starttls(
            ssl_context=ssl_context,
            post_handshake_callback=verifier.post_handshake,
        )

        features_future = yield from protocol.reset_stream_and_get_features(
            stream,
            timeout=negotiation_timeout,
        )

        return transport, stream, features_future
示例#2
0
    def connect(self, loop, metadata, domain, host, port, negotiation_timeout):
        """
        .. seealso::

           :meth:`BaseConnector.connect`
              For general information on the :meth:`connect` method.

        Connect to `host` at TCP port number `port`. The
        :class:`aioxmpp.security_layer.SecurityLayer` object `metadata` is used
        to determine the parameters of the TLS connection.

        First, a normal TCP connection is opened and the stream header is sent.
        The stream features are waited for, and then STARTTLS is negotiated if
        possible.

        :attr:`~.security_layer.SecurityLayer.tls_required` is honoured: if it
        is true and the server does not offer TLS or TLS negotiation fails,
        :class:`~.errors.TLSUnavailable` is raised.

        :attr:`~.security_layer.SecurityLayer.ssl_context_factory` and
        :attr:`~.security_layer.SecurityLayer.certificate_verifier_factory` are
        used to configure the TLS connection.
        """

        features_future = asyncio.Future(loop=loop)

        stream = protocol.XMLStream(
            to=domain,
            features_future=features_future,
        )

        transport, _ = yield from ssl_transport.create_starttls_connection(
            loop,
            lambda: stream,
            host=host,
            port=port,
            peer_hostname=host,
            server_hostname=domain,
            use_starttls=True,
        )

        features = yield from features_future

        try:
            features[nonza.StartTLSFeature]
        except KeyError:
            if metadata.tls_required:
                message = (
                    "STARTTLS not supported by server, but required by client")

                protocol.send_stream_error_and_close(
                    stream,
                    condition=(namespaces.streams, "policy-violation"),
                    text=message,
                )

                raise errors.TLSUnavailable(message)
            else:
                return transport, stream, (yield from features_future)

        response = yield from protocol.send_and_wait_for(
            stream, [
                nonza.StartTLS(),
            ], [
                nonza.StartTLSFailure,
                nonza.StartTLSProceed,
            ])

        if not isinstance(response, nonza.StartTLSProceed):
            if metadata.tls_required:
                message = ("server failed to STARTTLS")

                protocol.send_stream_error_and_close(
                    stream,
                    condition=(namespaces.streams, "policy-violation"),
                    text=message,
                )

                raise errors.TLSUnavailable(message)
            return transport, stream, (yield from features_future)

        verifier = metadata.certificate_verifier_factory()
        yield from verifier.pre_handshake(
            domain,
            host,
            port,
            metadata,
        )

        ssl_context = metadata.ssl_context_factory()
        verifier.setup_context(ssl_context, transport)

        yield from stream.starttls(
            ssl_context=ssl_context,
            post_handshake_callback=verifier.post_handshake,
        )

        features_future = yield from protocol.reset_stream_and_get_features(
            stream,
            timeout=negotiation_timeout,
        )

        return transport, stream, features_future