예제 #1
0
파일: plain.py 프로젝트: sgricci/digsby
    def response(self,response):
        """Process a client reponse.

        :Parameters:
            - `response`: the response from the client.
        :Types:
            - `response`: `str`

        :return: a challenge, a success indicator or a failure indicator.
        :returntype: `sasl.Challenge`, `sasl.Success` or `sasl.Failure`"""
        s=response.split("\000")
        if len(s)!=3:
            self.__logger.debug("Bad response: %r" % (response,))
            return Failure("not-authorized")
        authzid,username,password=s
        authzid=from_utf8(authzid)
        username=from_utf8(username)
        password=from_utf8(password)
        if not self.password_manager.check_password(username,password):
            self.__logger.debug("Bad password. Response was: %r" % (response,))
            return Failure("not-authorized")
        info={"mechanism":"PLAIN","username":username}
        if self.password_manager.check_authzid(authzid,info):
            return Success(username,None,authzid)
        else:
            self.__logger.debug("Authzid verification failed.")
            return Failure("invalid-authzid")
예제 #2
0
    def _get_realm(self, realms, charset):
        """Choose a realm from the list specified by the server.

        :Parameters:
            - `realms`: the realm list.
            - `charset`: encoding of realms on the list.
        :Types:
            - `realms`: `list` of `str`
            - `charset`: `str`

        :return: the realm chosen or a failure indicator.
        :returntype: `str` or `Failure`"""
        if realms:
            realms = [unicode(r, charset) for r in realms]
            realm = self.password_manager.choose_realm(realms)
        else:
            realm = self.password_manager.choose_realm([])
        if realm:
            if type(realm) is unicode:
                try:
                    realm = realm.encode(charset)
                except UnicodeError:
                    self.__logger.debug("Couldn't encode realm to %r" %
                                        (charset, ))
                    return Failure("incompatible-charset")
            elif charset != "utf-8":
                try:
                    realm = unicode(realm, "utf-8").encode(charset)
                except UnicodeError:
                    self.__logger.debug(
                        "Couldn't encode realm from utf-8 to %r" % (charset, ))
                    return Failure("incompatible-charset")
            self.realm = realm
        return realm
예제 #3
0
    def _parse_response(self, response):
        """Parse a client reponse and pass to further processing.

        :Parameters:
            - `response`: the response from the client.
        :Types:
            - `response`: `str`

        :return: a challenge, a success indicator or a failure indicator.
        :returntype: `sasl.Challenge`, `sasl.Success` or `sasl.Failure`"""
        response = response.split('\x00')[
            0]  # workaround for some SASL implementations
        if self.realm:
            realm = to_utf8(self.realm)
            realm = _quote(realm)
        else:
            realm = None
        username = None
        cnonce = None
        digest_uri = None
        response_val = None
        authzid = None
        nonce_count = None
        while response:
            m = _param_re.match(response)
            if not m:
                self.__logger.debug("Response syntax error: %r" % (response, ))
                return Failure("not-authorized")
            response = m.group("rest")
            var = m.group("var")
            val = m.group("val")
            self.__logger.debug("%r: %r" % (var, val))
            if var == "realm":
                realm = val[1:-1]
            elif var == "cnonce":
                if cnonce:
                    self.__logger.debug("Duplicate cnonce")
                    return Failure("not-authorized")
                cnonce = val[1:-1]
            elif var == "qop":
                if val != 'auth':
                    self.__logger.debug("qop other then 'auth'")
                    return Failure("not-authorized")
            elif var == "digest-uri":
                digest_uri = val[1:-1]
            elif var == "authzid":
                authzid = val[1:-1]
            elif var == "username":
                username = val[1:-1]
            elif var == "response":
                response_val = val
            elif var == "nc":
                nonce_count = val
                self.last_nonce_count += 1
                if int(nonce_count) != self.last_nonce_count:
                    self.__logger.debug("bad nonce: %r != %r" %
                                        (nonce_count, self.last_nonce_count))
                    return Failure("not-authorized")
        return self._check_params(username, realm, cnonce, digest_uri,
                                  response_val, authzid, nonce_count)
예제 #4
0
    def finish(self, data):
        """Process success indicator from the server.

        Process any addiitional data passed with the success.
        Fail if the server was not authenticated.

        :Parameters:
            - `data`: an optional additional data with success.
        :Types:
            - `data`: `str`

        :return: success or failure indicator.
        :returntype: `sasl.Success` or `sasl.Failure`"""
        if not self.response_auth:
            self.__logger.debug("Got success too early")
            return Failure("bad-success")
        if self.rspauth_checked:
            return Success(self.username, self.realm, self.authzid)
        else:
            r = self._final_challenge(data)
            if isinstance(r, Failure):
                return r
            if self.rspauth_checked:
                return Success(self.username, self.realm, self.authzid)
            else:
                self.__logger.debug(
                    "Something went wrong when processing additional data with success?"
                )
                return Failure("bad-success")
예제 #5
0
    def challenge(self, challenge):
        """Process a challenge and return the response.

        :Parameters:
            - `challenge`: the challenge from server.
        :Types:
            - `challenge`: `str`

        :return: the response or a failure indicator.
        :returntype: `sasl.Response` or `sasl.Failure`"""
        if not challenge:
            self.__logger.debug("Empty challenge")
            return Failure("bad-challenge")
        challenge = challenge.split('\x00')[
            0]  # workaround for some buggy implementations
        if self.response_auth:
            return self._final_challenge(challenge)
        realms = []
        nonce = None
        charset = "iso-8859-1"
        while challenge:
            m = _param_re.match(challenge)
            if not m:
                self.__logger.debug("Challenge syntax error: %r" %
                                    (challenge, ))
                return Failure("bad-challenge")
            challenge = m.group("rest")
            var = m.group("var")
            val = m.group("val")
            self.__logger.debug("%r: %r" % (var, val))
            if var == "realm":
                realms.append(_unquote(val))
            elif var == "nonce":
                if nonce:
                    self.__logger.debug("Duplicate nonce")
                    return Failure("bad-challenge")
                nonce = _unquote(val)
            elif var == "qop":
                qopl = _unquote(val).split(",")
                if "auth" not in qopl:
                    self.__logger.debug("auth not supported")
                    return Failure("not-implemented")
            elif var == "charset":
                val = _unquote(val)
                if val != "utf-8":
                    self.__logger.debug("charset given and not utf-8")
                    return Failure("bad-challenge")
                charset = "utf-8"
            elif var == "algorithm":
                val = _unquote(val)
                if val != "md5-sess":
                    self.__logger.debug("algorithm given and not md5-sess")
                    return Failure("bad-challenge")
        if not nonce:
            self.__logger.debug("nonce not given")
            return Failure("bad-challenge")
        self._get_password()
        return self._make_response(charset, realms, nonce)
예제 #6
0
    def _check_params(self, username, realm, cnonce, digest_uri, response_val,
                      authzid, nonce_count):
        """Check parameters of a client reponse and pass them to further
        processing.

        :Parameters:
            - `username`: user name.
            - `realm`: realm.
            - `cnonce`: cnonce value.
            - `digest_uri`: digest-uri value.
            - `response_val`: response value computed by the client.
            - `authzid`: authorization id.
            - `nonce_count`: nonce count value.
        :Types:
            - `username`: `str`
            - `realm`: `str`
            - `cnonce`: `str`
            - `digest_uri`: `str`
            - `response_val`: `str`
            - `authzid`: `str`
            - `nonce_count`: `int`

        :return: a challenge, a success indicator or a failure indicator.
        :returntype: `sasl.Challenge`, `sasl.Success` or `sasl.Failure`"""
        if not cnonce:
            self.__logger.debug("Required 'cnonce' parameter not given")
            return Failure("not-authorized")
        if not response_val:
            self.__logger.debug("Required 'response' parameter not given")
            return Failure("not-authorized")
        if not username:
            self.__logger.debug("Required 'username' parameter not given")
            return Failure("not-authorized")
        if not digest_uri:
            self.__logger.debug("Required 'digest_uri' parameter not given")
            return Failure("not-authorized")
        if not nonce_count:
            self.__logger.debug("Required 'nc' parameter not given")
            return Failure("not-authorized")
        return self._make_final_challenge(username, realm, cnonce, digest_uri,
                                          response_val, authzid, nonce_count)
예제 #7
0
파일: plain.py 프로젝트: sgricci/digsby
    def challenge(self, challenge):
        """Process the challenge and return the response.

        :Parameters:
            - `challenge`: the challenge.
        :Types:
            - `challenge`: `str`

        :return: the response or a failure indicator.
        :returntype: `sasl.Response` or `sasl.Failure`"""
        _unused = challenge
        if self.finished:
            self.__logger.debug("Already authenticated")
            return Failure("extra-challenge")
        self.finished=1
        if self.password is None:
            self.password,pformat=self.password_manager.get_password(self.username)
        if not self.password or pformat!="plain":
            self.__logger.debug("Couldn't retrieve plain password")
            return Failure("password-unavailable")
        return Response("%s\000%s\000%s" % (    to_utf8(self.authzid),
                            to_utf8(self.username),
                            to_utf8(self.password)))
예제 #8
0
    def _get_password(self):
        """Retrieve user's password from the password manager.

        Set `self.password` to the password and `self.pformat`
        to its format name ('plain' or 'md5:user:realm:pass')."""
        if self.password is None:
            self.password, self.pformat = self.password_manager.get_password(
                self.username, ["plain", "md5:user:realm:pass"])
        if not self.password or self.pformat not in ("plain",
                                                     "md5:user:realm:pass"):
            self.__logger.debug(
                "Couldn't get plain password. Password: %r Format: %r" %
                (self.password, self.pformat))
            return Failure("password-unavailable")
예제 #9
0
    def response(self, response):
        """Process a client reponse.

        :Parameters:
            - `response`: the response from the client.
        :Types:
            - `response`: `str`

        :return: a challenge, a success indicator or a failure indicator.
        :returntype: `sasl.Challenge`, `sasl.Success` or `sasl.Failure`"""
        if self.done:
            return Success(self.username, self.realm, self.authzid)
        if not response:
            return Failure("not-authorized")
        return self._parse_response(response)
예제 #10
0
    def _final_challenge(self, challenge):
        """Process the second challenge from the server and return the response.

        :Parameters:
            - `challenge`: the challenge from server.
        :Types:
            - `challenge`: `str`

        :return: the response or a failure indicator.
        :returntype: `sasl.Response` or `sasl.Failure`"""
        if self.rspauth_checked:
            return Failure("extra-challenge")
        challenge = challenge.split('\x00')[0]
        rspauth = None
        while challenge:
            m = _param_re.match(challenge)
            if not m:
                self.__logger.debug("Challenge syntax error: %r" %
                                    (challenge, ))
                return Failure("bad-challenge")
            challenge = m.group("rest")
            var = m.group("var")
            val = m.group("val")
            self.__logger.debug("%r: %r" % (var, val))
            if var == "rspauth":
                rspauth = val
        if not rspauth:
            self.__logger.debug("Final challenge without rspauth")
            return Failure("bad-success")
        if rspauth == self.response_auth:
            self.rspauth_checked = 1
            return Response("")
        else:
            self.__logger.debug("Wrong rspauth value - peer is cheating?")
            self.__logger.debug("my rspauth: %r" % (self.response_auth, ))
            return Failure("bad-success")
예제 #11
0
    def _make_final_challenge(self, username, realm, cnonce, digest_uri,
                              response_val, authzid, nonce_count):
        """Send the second challenge in reply to the client response.

        :Parameters:
            - `username`: user name.
            - `realm`: realm.
            - `cnonce`: cnonce value.
            - `digest_uri`: digest-uri value.
            - `response_val`: response value computed by the client.
            - `authzid`: authorization id.
            - `nonce_count`: nonce count value.
        :Types:
            - `username`: `str`
            - `realm`: `str`
            - `cnonce`: `str`
            - `digest_uri`: `str`
            - `response_val`: `str`
            - `authzid`: `str`
            - `nonce_count`: `int`

        :return: a challenge, a success indicator or a failure indicator.
        :returntype: `sasl.Challenge`, `sasl.Success` or `sasl.Failure`"""
        username_uq = from_utf8(username.replace('\\', ''))
        if authzid:
            authzid_uq = from_utf8(authzid.replace('\\', ''))
        else:
            authzid_uq = None
        if realm:
            realm_uq = from_utf8(realm.replace('\\', ''))
        else:
            realm_uq = None
        digest_uri_uq = digest_uri.replace('\\', '')
        self.username = username_uq
        self.realm = realm_uq
        password, pformat = self.password_manager.get_password(
            username_uq, realm_uq, ("plain", "md5:user:realm:pass"))
        if pformat == "md5:user:realm:pass":
            urp_hash = password
        elif pformat == "plain":
            urp_hash = _make_urp_hash(username, realm, password)
        else:
            self.__logger.debug("Couldn't get password.")
            return Failure("not-authorized")
        valid_response = _compute_response(urp_hash, self.nonce, cnonce,
                                           nonce_count, authzid, digest_uri)
        if response_val != valid_response:
            self.__logger.debug("Response mismatch: %r != %r" %
                                (response_val, valid_response))
            return Failure("not-authorized")
        s = digest_uri_uq.split("/")
        if len(s) == 3:
            serv_type, host, serv_name = s
        elif len(s) == 2:
            serv_type, host = s
            serv_name = None
        else:
            self.__logger.debug("Bad digest_uri: %r" % (digest_uri_uq, ))
            return Failure("not-authorized")
        info = {}
        info["mechanism"] = "DIGEST-MD5"
        info["username"] = username_uq
        info["serv-type"] = serv_type
        info["host"] = host
        info["serv-name"] = serv_name
        if self.password_manager.check_authzid(authzid_uq, info):
            rspauth = _compute_response_auth(urp_hash, self.nonce, cnonce,
                                             nonce_count, authzid, digest_uri)
            self.authzid = authzid
            self.done = 1
            return Challenge("rspauth=" + rspauth)
        else:
            self.__logger.debug("Authzid check failed")
            return Failure("invalid_authzid")
예제 #12
0
    def _make_response(self, charset, realms, nonce):
        """Make a response for the first challenge from the server.

        :Parameters:
            - `charset`: charset name from the challenge.
            - `realms`: realms list from the challenge.
            - `nonce`: nonce value from the challenge.
        :Types:
            - `charset`: `str`
            - `realms`: `str`
            - `nonce`: `str`

        :return: the response or a failure indicator.
        :returntype: `sasl.Response` or `sasl.Failure`"""
        params = []
        realm = self._get_realm(realms, charset)
        if isinstance(realm, Failure):
            return realm
        elif realm:
            realm = _quote(realm)
            params.append('realm="%s"' % (realm, ))

        try:
            username = self.username.encode(charset)
        except UnicodeError:
            self.__logger.debug("Couldn't encode username to %r" % (charset, ))
            return Failure("incompatible-charset")

        username = _quote(username)
        params.append('username="******"' % (username, ))

        cnonce = self.password_manager.generate_nonce()
        cnonce = _quote(cnonce)
        params.append('cnonce="%s"' % (cnonce, ))

        params.append('nonce="%s"' % (_quote(nonce), ))

        self.nonce_count += 1
        nonce_count = "%08x" % (self.nonce_count, )
        params.append('nc=%s' % (nonce_count, ))

        params.append('qop=auth')

        serv_type = self.password_manager.get_serv_type().encode("us-ascii")
        host = self.password_manager.get_serv_host().encode("idna")
        serv_name = self.password_manager.get_serv_name().encode("utf-8")

        if serv_name and serv_name != host:
            digest_uri = "%s/%s/%s" % (serv_type, host, serv_name)
        else:
            digest_uri = "%s/%s" % (serv_type, host)

        digest_uri = _quote(digest_uri)
        params.append('digest-uri="%s"' % (digest_uri, ))

        if self.authzid:
            try:
                authzid = self.authzid.encode(charset)
            except UnicodeError:
                self.__logger.debug("Couldn't encode authzid to %r" %
                                    (charset, ))
                return Failure("incompatible-charset")
            authzid = _quote(authzid)
        else:
            authzid = ""

        if self.pformat == "md5:user:realm:pass":
            urp_hash = self.password
        else:
            urp_hash = _make_urp_hash(username, realm, self.password)

        response = _compute_response(urp_hash, nonce, cnonce, nonce_count,
                                     authzid, digest_uri)
        self.response_auth = _compute_response_auth(urp_hash, nonce, cnonce,
                                                    nonce_count, authzid,
                                                    digest_uri)
        params.append('response=%s' % (response, ))
        if authzid:
            params.append('authzid="%s"' % (authzid, ))
        return Response(",".join(params))
예제 #13
0
    def challenge(self, challenge):
        if self.password is None:
            self.password, pformat = self.password_manager.get_password(
                self.username)
            if not self.password or pformat != "plain":
                self.__logger.debug("Couldn't retrieve plain password")
                return Failure("password-unavailable")
        if self.step == 0:
            self.step = 1
            return Response(''.join(
                pstrIlist([to_utf8(self.authzid),
                           to_utf8(self.username)])))

        elif self.step == 1:
            self.step = 2
            srv_rsa_key = None
            self.__logger.critical("loading server certificate")
            try:
                srv_cert = M2Crypto.X509.load_cert_string(challenge)
                if srv_cert is not None:
                    self.__logger.critical("retrieving server pubkey")
                    srv_key = srv_cert.get_pubkey()
                    if srv_key is not None:
                        self.__logger.critical("retrieving server RSA pubkey")
                        srv_rsa_key = srv_key.get_rsa()
            except Exception:
                traceback.print_exc()

            if srv_rsa_key is None:
                return Failure("bad-server-cert")

            if not srv_cert.verify(ROOT_CERT.get_pubkey()):
                return Failure("bad-server-cert")

            self.srv_rsa_key = srv_rsa_key
            self.__logger.critical("generating Nonce")
            from M2Crypto import m2
            nonce = m2.rand_bytes(16)
            self.__logger.critical("encrypting Nonce")
            enonce = digsbyrsa.DIGSBY_RSA_public_encrypt(
                nonce, srv_rsa_key, M2Crypto.RSA.pkcs1_oaep_padding)

            self.__logger.critical("loading key")
            try:
                self.key = _get_client_key_pem(self.username, self.password)
            except Exception:
                self.key = None

            if self.key is None:
                self.__logger.critical("generating new key")
                self.key = M2Crypto.RSA.gen_key(2048, 0x10001)
                self.__logger.critical("saving new key")
                if not self.key.check_key():
                    raise ValueError("failed to generate key")
                try:
                    _save_client_key_pem(self.key, self.username,
                                         self.password)
                except Exception:
                    traceback.print_exc()
            self.__logger.critical("creating buffer")
            buff = M2Crypto.BIO.MemoryBuffer()
            self.__logger.critical("serializing client public key to buffer")
            self.key.save_pub_key_bio(buff)

            self.__logger.critical_s("Nonce: %r", nonce)
            genkey = buff.getvalue()
            self.__logger.critical_s("Key: %r", genkey)
            eKey = pstrAES(nonce, genkey)

            self.__logger.critical("returning response")

            return Response(''.join(pstrIlist([enonce, eKey])))
        elif self.step == 2:
            self.step = 3

            package_nonce_C_userpub, package_C_AES_C_serverpriv = unpackpstrlist(
                challenge)
            package_nonce = digsbyrsa.DIGSBY_RSA_private_decrypt(
                package_nonce_C_userpub, self.key,
                M2Crypto.RSA.pkcs1_oaep_padding)
            package_C_serverpriv = util.cryptography.decrypt(
                package_nonce,
                package_C_AES_C_serverpriv,
                padchar=None,
                mode=util.cryptography.Mode.CBC)

            nonce = digsbyrsa.DIGSBY_RSA_public_decrypt(
                package_C_serverpriv, self.srv_rsa_key,
                M2Crypto.RSA.pkcs1_padding)

            return Response(pstrAES(nonce, self.password))
        else:
            return Failure("extra-challenge")