Ejemplo n.º 1
0
    def handle_LDAPModifyDNRequest(self, request, controls, reply):
        self.checkControls(controls)
        dn = distinguishedname.DistinguishedName(request.entry)
        newrdn = distinguishedname.RelativeDistinguishedName(request.newrdn)
        deleteoldrdn = bool(request.deleteoldrdn)
        if not deleteoldrdn:
            raise ldaperrors.LDAPUnwillingToPerform(
                "Cannot handle preserving old RDN yet.")
        newSuperior = request.newSuperior
        if newSuperior is None:
            newSuperior = dn.up()
        else:
            newSuperior = distinguishedname.DistinguishedName(newSuperior)
        newdn = distinguishedname.DistinguishedName(
            listOfRDNs=(newrdn,)+newSuperior.split())
        root = interfaces.IConnectedLDAPEntry(self.factory)
        d = root.lookup(dn)

        def _gotEntry(entry):
            d = entry.move(newdn)
            return d

        def _report(entry):
            return pureldap.LDAPModifyDNResponse(resultCode=0)

        d.addCallback(_gotEntry)
        d.addCallback(_report)
        return d
Ejemplo n.º 2
0
    def handle_LDAPExtendedRequest(
        self,
        request: LDAPExtendedRequest,
        controls: pureldap.LDAPControl,
        reply_fn: Callable[..., None],
    ) -> Any:
        # Handle STARTTLS locally; proxy everything else.
        if request.requestName != pureldap.LDAPStartTLSRequest.oid:
            return self.handleUnknown(request, controls, reply_fn)

        if not self.sslctx:
            raise ldaperrors.LDAPProtocolError(
                STARTTLS_NOT_SUPPORTED_ERROR_MESSAGE)

        self.checkControls(controls)  # assert can ignore controls, then do so

        try:
            # Send reply indicating TLS negotiation should start.
            msg = pureldap.LDAPExtendedResponse(
                resultCode=ldaperrors.Success.resultCode,
                responseName=pureldap.LDAPStartTLSRequest.oid,
            )
            reply_fn(msg)
            # Start TLS negotiation!
            self.transport.startTLS(self.sslctx, self.factory)
        except ldaperrors.LDAPException:
            raise
        except Exception:
            raise ldaperrors.LDAPUnwillingToPerform()
Ejemplo n.º 3
0
 def handle_LDAPExtendedRequest(self, request, controls, reply):
     raise ldaperrors.LDAPUnwillingToPerform()
Ejemplo n.º 4
0
 def handle_LDAPModifyDNRequest(self, request, controls, reply):
     raise ldaperrors.LDAPUnwillingToPerform()
Ejemplo n.º 5
0
    def perform_bind_sspi(self,
                          dn: str,
                          username: str,
                          password: str,
                          domain: str,
                          permit_implicit: bool,
                          targetspn=None):
        """Perform bind using native windows SSPI mechanism. If no
        username, password, or domain is provided, then we'll attempt
        to use the authproxy's existing process credentials to perform
        the bind.

        If targetspn is provided (and valid), then theoretically we
        might use kerberos instead of NTLM

        Returns:
            No return value from this function. Finishing without raising an exception means the
            bind succeeded
        Raises:
            LDAPUnwillingToPerform: If SSPI auth is not supported
            SSPIError: If the SSPI negotiation fails
        """

        if sspi is None:
            msg = 'The SSPI bind type is only supported on Windows.'
            log.err(msg)
            raise ldaperrors.LDAPUnwillingToPerform(msg)

        auth_info: Optional[Tuple[str, str,
                                  str]] = (username, domain, password)
        # omitting 'domain' from this if statement; that way we can
        # specify ntlm_domain in config and have it apply to user
        # auth, but not automatically trip us into using configured,
        # rather than implicit, service account creds
        if not (username or password):
            if permit_implicit:
                auth_info = None
            else:
                # even passing a tuple of empty values still appears
                # to trigger the implicit-auth mechanism, so we need
                # to be explicit
                msg = 'Implicit authentication forbidden for this request.'
                log.err(msg)
                raise ldaperrors.LDAPUnwillingToPerform(msg)

        # SSPI negotiation can request some application-level
        # encryption/authentication, but AD apparently throws a fit if
        # you leave this on when using SSL/TLS (and expects you to
        # actually somehow sign all your LDAP requests otherwise)
        scflags = ISC_REQ_NO_INTEGRITY

        # these return codes mean we need to continue the handshake
        sspi_continue = set([
            sspicon.SEC_I_CONTINUE_NEEDED, sspicon.SEC_I_COMPLETE_AND_CONTINUE
        ])
        # any return code not in this set should be considered an error
        sspi_ok = sspi_continue.union(
            [sspicon.SEC_E_OK, sspicon.SEC_I_COMPLETE_NEEDED])

        ca = self._create_sspi_authenticator(auth_info, targetspn, scflags)
        data = None
        # This negotiation is made up of challenge and response messages.
        # We will continue responding to challenges until a success or
        # failure case is hit
        while True:
            # get the next step in the handshake
            err, out_buf = ca.authorize(data)
            if err not in sspi_ok:
                raise SSPIError('SSPI negotiation failed', err)

            response = yield self._send_sspi_bind(dn, out_buf)

            if response.resultCode == ldaperrors.LDAPSaslBindInProgress.resultCode:
                data = self._recalculate_buffer_data(ca, response)
            elif response.resultCode == ldaperrors.Success.resultCode:
                break
            else:
                raise ldaperrors.get(response.resultCode,
                                     response.errorMessage)

            # if SSPI said we're done, but ldap response doesn't
            # agree, that's weird
            if err not in sspi_continue:
                raise SSPIError('SSPI negotiation should\'ve finished by now',
                                err)
Ejemplo n.º 6
0
    def perform_bind_sspi(
        self,
        username: str,
        password: str,
        domain: str,
        permit_implicit: bool,
        targetspn: str = None,
    ):
        """Perform bind using native windows SSPI mechanism. If no
        username, password, or domain is provided, then we'll attempt
        to use the authproxy's existing process credentials to perform
        the bind.

        This method uses the Negotiate authentication mechanism (GSS-SPNEGO).
        If a valid targetspn is provided, then SSPI will use Kerberos. Otherwise,
        NTLM will be used.

        Returns:
            No return value from this function. Finishing without raising an exception means the
            bind succeeded
        Raises:
            LDAPUnwillingToPerform: If SSPI auth is not supported
            SSPIError: If the SSPI negotiation fails
        """

        if sspi is None:
            msg = "The SSPI bind type is only supported on Windows."
            log.error(msg)
            raise ldaperrors.LDAPUnwillingToPerform(msg)

        auth_info: Optional[Tuple[str, str,
                                  str]] = (username, domain, password)
        # omitting 'domain' from this if statement; that way we can
        # specify ntlm_domain in config and have it apply to user
        # auth, but not automatically trip us into using configured,
        # rather than implicit, service account creds
        if not (username or password):
            if permit_implicit:
                auth_info = None
            else:
                # even passing a tuple of empty values still appears
                # to trigger the implicit-auth mechanism, so we need
                # to be explicit
                msg = "Implicit authentication forbidden for this request."
                log.error(msg)
                raise ldaperrors.LDAPUnwillingToPerform(msg)

        # TLS and Sign and Seal are mutually eclusive security measures
        # so we only request integrity checking if we don't already have
        # it from the transport layer
        if self.factory.transport_type in const.AD_TRANSPORTS_WITH_SSL:
            scflags = ISC_REQ_NO_INTEGRITY
        else:
            scflags = sspicon.ISC_REQ_INTEGRITY | sspicon.ISC_REQ_CONFIDENTIALITY

        # When doing an SSPI bind, we interact with two services:
        #
        #   1) SSPI
        #   2) LDAP
        #
        # SSPI creates a security context and provides the credentials to do
        # a SASL bind. The authentication is complete when both SSPI and LDAP
        # don't require any more steps.
        #
        # There is no guarantee that there will be the same number of interactions
        # with SSPI and LDAP for a single authentication. For example, when
        # doing a Kerberos authentication the entire flow is
        # authorize->bind->authorize due to there being no LDAP challenge. If
        # NTLM is used, though, the flow is authorize->bind(neg)->authorize->bind(auth).
        # Because of this, we must check whether we need to do another authorize
        # and/or bind on each iteration of the loop.
        #
        # More details on how to interact with SSPI and authorize(), particularly
        # the 'Remarks' section:
        # https://docs.microsoft.com/en-us/previous-versions/windows/desktop/legacy/aa375509(v=vs.85)
        response = None
        result_code = None
        out_buf = None
        err = None
        ca = self._create_sspi_authenticator(auth_info, targetspn, scflags)

        while err != sspicon.SEC_E_OK or result_code != ldaperrors.Success.resultCode:
            # If it's either our first iteration or SSPI says more steps are
            # necessary, authorize to get the data buffer for the next step.
            # Intentionally checking for err being None instead of falsey since
            # sspicon.SEC_E_OK is 0, which is also falsey!
            if err is None or err in SSPI_CONTINUE:
                err, out_buf = yield self._authorize(ca, response)

            # Send a bind request if this is our first iteration or if the
            # LDAP response from the previous iteration was a challenge.
            if (not response or response.resultCode
                    == ldaperrors.LDAPSaslBindInProgress.resultCode):
                response = yield self._send_sspi_bind(out_buf)
                result_code = response.resultCode

            # if SSPI said we're done, but ldap response doesn't agree, we've
            # reached an unexpected state. Raise an error.
            if err == sspicon.SEC_E_OK and result_code != ldaperrors.Success.resultCode:
                raise SSPIError("SSPI negotiation should've finished by now",
                                err)

        defer.returnValue(ca)