diff options
Diffstat (limited to 'xmpp/auth.py')
-rw-r--r-- | xmpp/auth.py | 828 |
1 files changed, 414 insertions, 414 deletions
diff --git a/xmpp/auth.py b/xmpp/auth.py index 869ead4..7149dff 100644 --- a/xmpp/auth.py +++ b/xmpp/auth.py @@ -1,414 +1,414 @@ -## auth.py
-##
-## Copyright (C) 2003-2005 Alexey "Snake" Nezhdanov
-##
-## This program is free software; you can redistribute it and/or modify
-## it under the terms of the GNU General Public License as published by
-## the Free Software Foundation; either version 2, or (at your option)
-## any later version.
-##
-## This program is distributed in the hope that it will be useful,
-## but WITHOUT ANY WARRANTY; without even the implied warranty of
-## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-## GNU General Public License for more details.
-
-# $Id: auth.py, v1.42 2013/10/21 alkorgun Exp $
-
-"""
-Provides library with all Non-SASL and SASL authentication mechanisms.
-Can be used both for client and transport authentication.
-"""
-
-import hashlib
-from . import dispatcher
-
-from base64 import encodestring, decodestring
-from .plugin import PlugIn
-from .protocol import *
-from random import random as _random
-from re import findall as re_findall
-
-def HH(some):
- return hashlib.md5(some).hexdigest()
-
-def H(some):
- return hashlib.md5(some).digest()
-
-def C(some):
- return ":".join(some)
-
-class NonSASL(PlugIn):
- """
- Implements old Non-SASL (JEP-0078) authentication used in jabberd1.4 and transport authentication.
- """
- def __init__(self, user, password, resource):
- """
- Caches username, password and resource for auth.
- """
- PlugIn.__init__(self)
- self.DBG_LINE = "gen_auth"
- self.user = user
- self.password = password
- self.resource = resource
-
- def plugin(self, owner):
- """
- Determine the best auth method (digest/0k/plain) and use it for auth.
- Returns used method name on success. Used internally.
- """
- if not self.resource:
- return self.authComponent(owner)
- self.DEBUG("Querying server about possible auth methods", "start")
- resp = owner.Dispatcher.SendAndWaitForResponse(Iq("get", NS_AUTH, payload=[Node("username", payload=[self.user])]))
- if not isResultNode(resp):
- self.DEBUG("No result node arrived! Aborting...", "error")
- return None
- iq = Iq(typ="set", node=resp)
- query = iq.getTag("query")
- query.setTagData("username", self.user)
- query.setTagData("resource", self.resource)
- if query.getTag("digest"):
- self.DEBUG("Performing digest authentication", "ok")
- hash = hashlib.sha1(owner.Dispatcher.Stream._document_attrs["id"] + self.password).hexdigest()
- query.setTagData("digest", hash)
- if query.getTag("password"):
- query.delChild("password")
- method = "digest"
- elif query.getTag("token"):
- token = query.getTagData("token")
- seq = query.getTagData("sequence")
- self.DEBUG("Performing zero-k authentication", "ok")
- hash = hashlib.sha1(hashlib.sha1(self.password).hexdigest() + token).hexdigest()
- for i in xrange(int(seq)):
- hash = hashlib.sha1(hash).hexdigest()
- query.setTagData("hash", hash)
- method = "0k"
- else:
- self.DEBUG("Sequre methods unsupported, performing plain text authentication", "warn")
- query.setTagData("password", self.password)
- method = "plain"
- resp = owner.Dispatcher.SendAndWaitForResponse(iq)
- if isResultNode(resp):
- self.DEBUG("Sucessfully authenticated with remove host.", "ok")
- owner.User = self.user
- owner.Resource = self.resource
- owner._registered_name = owner.User + "@" + owner.Server + "/" + owner.Resource
- return method
- self.DEBUG("Authentication failed!", "error")
-
- def authComponent(self, owner):
- """
- Authenticate component. Send handshake stanza and wait for result. Returns "ok" on success.
- """
- self.handshake = 0
- hash = hashlib.sha1(owner.Dispatcher.Stream._document_attrs["id"] + self.password).hexdigest()
- owner.send(Node(NS_COMPONENT_ACCEPT + " handshake", payload=[hash]))
- owner.RegisterHandler("handshake", self.handshakeHandler, xmlns=NS_COMPONENT_ACCEPT)
- while not self.handshake:
- self.DEBUG("waiting on handshake", "notify")
- owner.Process(1)
- owner._registered_name = self.user
- if self.handshake + 1:
- return "ok"
-
- def handshakeHandler(self, disp, stanza):
- """
- Handler for registering in dispatcher for accepting transport authentication.
- """
- if stanza.getName() == "handshake":
- self.handshake = 1
- else:
- self.handshake = -1
-
-class SASL(PlugIn):
- """
- Implements SASL authentication.
- """
- def __init__(self, username, password):
- PlugIn.__init__(self)
- self.username = username
- self.password = password
-
- def plugin(self, owner):
- if "version" not in self._owner.Dispatcher.Stream._document_attrs:
- self.startsasl = "not-supported"
- elif self._owner.Dispatcher.Stream.features:
- try:
- self.FeaturesHandler(self._owner.Dispatcher, self._owner.Dispatcher.Stream.features)
- except NodeProcessed:
- pass
- else:
- self.startsasl = None
-
- def auth(self):
- """
- Start authentication. Result can be obtained via "SASL.startsasl" attribute
- and will beeither "success" or "failure". Note that successfull
- auth will take at least two Dispatcher.Process() calls.
- """
- if self.startsasl:
- pass
- elif self._owner.Dispatcher.Stream.features:
- try:
- self.FeaturesHandler(self._owner.Dispatcher, self._owner.Dispatcher.Stream.features)
- except NodeProcessed:
- pass
- else:
- self._owner.RegisterHandler("features", self.FeaturesHandler, xmlns=NS_STREAMS)
-
- def plugout(self):
- """
- Remove SASL handlers from owner's dispatcher. Used internally.
- """
- if hasattr(self._owner, "features"):
- self._owner.UnregisterHandler("features", self.FeaturesHandler, xmlns=NS_STREAMS)
- if hasattr(self._owner, "challenge"):
- self._owner.UnregisterHandler("challenge", self.SASLHandler, xmlns=NS_SASL)
- if hasattr(self._owner, "failure"):
- self._owner.UnregisterHandler("failure", self.SASLHandler, xmlns=NS_SASL)
- if hasattr(self._owner, "success"):
- self._owner.UnregisterHandler("success", self.SASLHandler, xmlns=NS_SASL)
-
- def FeaturesHandler(self, conn, feats):
- """
- Used to determine if server supports SASL auth. Used internally.
- """
- if not feats.getTag("mechanisms", namespace=NS_SASL):
- self.startsasl = "not-supported"
- self.DEBUG("SASL not supported by server", "error")
- return None
- mecs = []
- for mec in feats.getTag("mechanisms", namespace=NS_SASL).getTags("mechanism"):
- mecs.append(mec.getData())
- self._owner.RegisterHandler("challenge", self.SASLHandler, xmlns=NS_SASL)
- self._owner.RegisterHandler("failure", self.SASLHandler, xmlns=NS_SASL)
- self._owner.RegisterHandler("success", self.SASLHandler, xmlns=NS_SASL)
- if "ANONYMOUS" in mecs and self.username == None:
- node = Node("auth", attrs={"xmlns": NS_SASL, "mechanism": "ANONYMOUS"})
- elif "DIGEST-MD5" in mecs:
- node = Node("auth", attrs={"xmlns": NS_SASL, "mechanism": "DIGEST-MD5"})
- elif "PLAIN" in mecs:
- sasl_data = "%s\x00%s\x00%s" % ("@".join((self.username, self._owner.Server)), self.username, self.password)
- node = Node("auth", attrs={"xmlns": NS_SASL, "mechanism": "PLAIN"}, payload=[encodestring(sasl_data).replace("\r", "").replace("\n", "")])
- else:
- self.startsasl = "failure"
- self.DEBUG("I can only use DIGEST-MD5 and PLAIN mecanisms.", "error")
- return
- self.startsasl = "in-process"
- self._owner.send(node.__str__())
- raise NodeProcessed()
-
- def SASLHandler(self, conn, challenge):
- """
- Perform next SASL auth step. Used internally.
- """
- if challenge.getNamespace() != NS_SASL:
- return None
- if challenge.getName() == "failure":
- self.startsasl = "failure"
- try:
- reason = challenge.getChildren()[0]
- except Exception:
- reason = challenge
- self.DEBUG("Failed SASL authentification: %s" % reason, "error")
- raise NodeProcessed()
- elif challenge.getName() == "success":
- self.startsasl = "success"
- self.DEBUG("Successfully authenticated with remote server.", "ok")
- handlers = self._owner.Dispatcher.dumpHandlers()
- self._owner.Dispatcher.PlugOut()
- dispatcher.Dispatcher().PlugIn(self._owner)
- self._owner.Dispatcher.restoreHandlers(handlers)
- self._owner.User = self.username
- raise NodeProcessed()
- incoming_data = challenge.getData()
- chal = {}
- data = decodestring(incoming_data)
- self.DEBUG("Got challenge:" + data, "ok")
- for pair in re_findall('(\w+\s*=\s*(?:(?:"[^"]+")|(?:[^,]+)))', data):
- key, value = [x.strip() for x in pair.split("=", 1)]
- if value[:1] == '"' and value[-1:] == '"':
- value = value[1:-1]
- chal[key] = value
- if "qop" in chal and "auth" in [x.strip() for x in chal["qop"].split(",")]:
- resp = {}
- resp["username"] = self.username
- resp["realm"] = self._owner.Server
- resp["nonce"] = chal["nonce"]
- cnonce = ""
- for i in xrange(7):
- cnonce += hex(int(_random() * 65536 * 4096))[2:]
- resp["cnonce"] = cnonce
- resp["nc"] = ("00000001")
- resp["qop"] = "auth"
- resp["digest-uri"] = "xmpp/" + self._owner.Server
- A1 = C([H(C([resp["username"], resp["realm"], self.password])), resp["nonce"], resp["cnonce"]])
- A2 = C(["AUTHENTICATE", resp["digest-uri"]])
- response = HH(C([HH(A1), resp["nonce"], resp["nc"], resp["cnonce"], resp["qop"], HH(A2)]))
- resp["response"] = response
- resp["charset"] = "utf-8"
- sasl_data = ""
- for key in ("charset", "username", "realm", "nonce", "nc", "cnonce", "digest-uri", "response", "qop"):
- if key in ("nc", "qop", "response", "charset"):
- sasl_data += "%s=%s," % (key, resp[key])
- else:
- sasl_data += "%s=\"%s\"," % (key, resp[key])
- node = Node("response", attrs={"xmlns": NS_SASL}, payload=[encodestring(sasl_data[:-1]).replace("\r", "").replace("\n", "")])
- self._owner.send(node.__str__())
- elif "rspauth" in chal:
- self._owner.send(Node("response", attrs={"xmlns": NS_SASL}).__str__())
- else:
- self.startsasl = "failure"
- self.DEBUG("Failed SASL authentification: unknown challenge", "error")
- raise NodeProcessed()
-
-class Bind(PlugIn):
- """
- Bind some JID to the current connection to allow router know of our location.
- """
- def __init__(self):
- PlugIn.__init__(self)
- self.DBG_LINE = "bind"
- self.bound = None
-
- def plugin(self, owner):
- """
- Start resource binding, if allowed at this time. Used internally.
- """
- if self._owner.Dispatcher.Stream.features:
- try:
- self.FeaturesHandler(self._owner.Dispatcher, self._owner.Dispatcher.Stream.features)
- except NodeProcessed:
- pass
- else:
- self._owner.RegisterHandler("features", self.FeaturesHandler, xmlns=NS_STREAMS)
-
- def plugout(self):
- """
- Remove Bind handler from owner's dispatcher. Used internally.
- """
- self._owner.UnregisterHandler("features", self.FeaturesHandler, xmlns=NS_STREAMS)
-
- def FeaturesHandler(self, conn, feats):
- """
- Determine if server supports resource binding and set some internal attributes accordingly.
- """
- if not feats.getTag("bind", namespace=NS_BIND):
- self.bound = "failure"
- self.DEBUG("Server does not requested binding.", "error")
- return None
- if feats.getTag("session", namespace=NS_SESSION):
- self.session = 1
- else:
- self.session = -1
- self.bound = []
-
- def Bind(self, resource=None):
- """
- Perform binding. Use provided resource name or random (if not provided).
- """
- while self.bound is None and self._owner.Process(1):
- pass
- if resource:
- resource = [Node("resource", payload=[resource])]
- else:
- resource = []
- resp = self._owner.SendAndWaitForResponse(Protocol("iq", typ="set", payload=[Node("bind", attrs={"xmlns": NS_BIND}, payload=resource)]))
- if isResultNode(resp):
- self.bound.append(resp.getTag("bind").getTagData("jid"))
- self.DEBUG("Successfully bound %s." % self.bound[-1], "ok")
- jid = JID(resp.getTag("bind").getTagData("jid"))
- self._owner.User = jid.getNode()
- self._owner.Resource = jid.getResource()
- resp = self._owner.SendAndWaitForResponse(Protocol("iq", typ="set", payload=[Node("session", attrs={"xmlns": NS_SESSION})]))
- if isResultNode(resp):
- self.DEBUG("Successfully opened session.", "ok")
- self.session = 1
- return "ok"
- else:
- self.DEBUG("Session open failed.", "error")
- self.session = 0
- elif resp:
- self.DEBUG("Binding failed: %s." % resp.getTag("error"), "error")
- else:
- self.DEBUG("Binding failed: timeout expired.", "error")
- return ""
-
-class ComponentBind(PlugIn):
- """
- ComponentBind some JID to the current connection to allow router know of our location.
- """
- def __init__(self, sasl):
- PlugIn.__init__(self)
- self.DBG_LINE = "bind"
- self.bound = None
- self.needsUnregister = None
- self.sasl = sasl
-
- def plugin(self, owner):
- """
- Start resource binding, if allowed at this time. Used internally.
- """
- if not self.sasl:
- self.bound = []
- return None
- if self._owner.Dispatcher.Stream.features:
- try:
- self.FeaturesHandler(self._owner.Dispatcher, self._owner.Dispatcher.Stream.features)
- except NodeProcessed:
- pass
- else:
- self._owner.RegisterHandler("features", self.FeaturesHandler, xmlns=NS_STREAMS)
- self.needsUnregister = 1
-
- def plugout(self):
- """
- Remove ComponentBind handler from owner's dispatcher. Used internally.
- """
- if self.needsUnregister:
- self._owner.UnregisterHandler("features", self.FeaturesHandler, xmlns=NS_STREAMS)
-
- def FeaturesHandler(self, conn, feats):
- """
- Determine if server supports resource binding and set some internal attributes accordingly.
- """
- if not feats.getTag("bind", namespace=NS_BIND):
- self.bound = "failure"
- self.DEBUG("Server does not requested binding.", "error")
- return None
- if feats.getTag("session", namespace=NS_SESSION):
- self.session = 1
- else:
- self.session = -1
- self.bound = []
-
- def Bind(self, domain=None):
- """
- Perform binding. Use provided domain name (if not provided).
- """
- while self.bound is None and self._owner.Process(1):
- pass
- if self.sasl:
- xmlns = NS_COMPONENT_1
- else:
- xmlns = None
- self.bindresponse = None
- ttl = dispatcher.DefaultTimeout
- self._owner.RegisterHandler("bind", self.BindHandler, xmlns=xmlns)
- self._owner.send(Protocol("bind", attrs={"name": domain}, xmlns=NS_COMPONENT_1))
- while self.bindresponse is None and self._owner.Process(1) and ttl > 0:
- ttl -= 1
- self._owner.UnregisterHandler("bind", self.BindHandler, xmlns=xmlns)
- resp = self.bindresponse
- if resp and resp.getAttr("error"):
- self.DEBUG("Binding failed: %s." % resp.getAttr("error"), "error")
- elif resp:
- self.DEBUG("Successfully bound.", "ok")
- return "ok"
- else:
- self.DEBUG("Binding failed: timeout expired.", "error")
- return ""
-
- def BindHandler(self, conn, bind):
- self.bindresponse = bind
- pass
+## auth.py +## +## Copyright (C) 2003-2005 Alexey "Snake" Nezhdanov +## +## This program is free software; you can redistribute it and/or modify +## it under the terms of the GNU General Public License as published by +## the Free Software Foundation; either version 2, or (at your option) +## any later version. +## +## This program is distributed in the hope that it will be useful, +## but WITHOUT ANY WARRANTY; without even the implied warranty of +## MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +## GNU General Public License for more details. + +# $Id: auth.py, v1.42 2013/10/21 alkorgun Exp $ + +""" +Provides library with all Non-SASL and SASL authentication mechanisms. +Can be used both for client and transport authentication. +""" + +import hashlib +from . import dispatcher + +from base64 import encodestring, decodestring +from .plugin import PlugIn +from .protocol import * +from random import random as _random +from re import findall as re_findall + +def HH(some): + return hashlib.md5(some).hexdigest() + +def H(some): + return hashlib.md5(some).digest() + +def C(some): + return ":".join(some) + +class NonSASL(PlugIn): + """ + Implements old Non-SASL (JEP-0078) authentication used in jabberd1.4 and transport authentication. + """ + def __init__(self, user, password, resource): + """ + Caches username, password and resource for auth. + """ + PlugIn.__init__(self) + self.DBG_LINE = "gen_auth" + self.user = user + self.password = password + self.resource = resource + + def plugin(self, owner): + """ + Determine the best auth method (digest/0k/plain) and use it for auth. + Returns used method name on success. Used internally. + """ + if not self.resource: + return self.authComponent(owner) + self.DEBUG("Querying server about possible auth methods", "start") + resp = owner.Dispatcher.SendAndWaitForResponse(Iq("get", NS_AUTH, payload=[Node("username", payload=[self.user])])) + if not isResultNode(resp): + self.DEBUG("No result node arrived! Aborting...", "error") + return None + iq = Iq(typ="set", node=resp) + query = iq.getTag("query") + query.setTagData("username", self.user) + query.setTagData("resource", self.resource) + if query.getTag("digest"): + self.DEBUG("Performing digest authentication", "ok") + hash = hashlib.sha1(owner.Dispatcher.Stream._document_attrs["id"] + self.password).hexdigest() + query.setTagData("digest", hash) + if query.getTag("password"): + query.delChild("password") + method = "digest" + elif query.getTag("token"): + token = query.getTagData("token") + seq = query.getTagData("sequence") + self.DEBUG("Performing zero-k authentication", "ok") + hash = hashlib.sha1(hashlib.sha1(self.password).hexdigest() + token).hexdigest() + for i in xrange(int(seq)): + hash = hashlib.sha1(hash).hexdigest() + query.setTagData("hash", hash) + method = "0k" + else: + self.DEBUG("Sequre methods unsupported, performing plain text authentication", "warn") + query.setTagData("password", self.password) + method = "plain" + resp = owner.Dispatcher.SendAndWaitForResponse(iq) + if isResultNode(resp): + self.DEBUG("Sucessfully authenticated with remove host.", "ok") + owner.User = self.user + owner.Resource = self.resource + owner._registered_name = owner.User + "@" + owner.Server + "/" + owner.Resource + return method + self.DEBUG("Authentication failed!", "error") + + def authComponent(self, owner): + """ + Authenticate component. Send handshake stanza and wait for result. Returns "ok" on success. + """ + self.handshake = 0 + hash = hashlib.sha1(owner.Dispatcher.Stream._document_attrs["id"] + self.password).hexdigest() + owner.send(Node(NS_COMPONENT_ACCEPT + " handshake", payload=[hash])) + owner.RegisterHandler("handshake", self.handshakeHandler, xmlns=NS_COMPONENT_ACCEPT) + while not self.handshake: + self.DEBUG("waiting on handshake", "notify") + owner.Process(1) + owner._registered_name = self.user + if self.handshake + 1: + return "ok" + + def handshakeHandler(self, disp, stanza): + """ + Handler for registering in dispatcher for accepting transport authentication. + """ + if stanza.getName() == "handshake": + self.handshake = 1 + else: + self.handshake = -1 + +class SASL(PlugIn): + """ + Implements SASL authentication. + """ + def __init__(self, username, password): + PlugIn.__init__(self) + self.username = username + self.password = password + + def plugin(self, owner): + if "version" not in self._owner.Dispatcher.Stream._document_attrs: + self.startsasl = "not-supported" + elif self._owner.Dispatcher.Stream.features: + try: + self.FeaturesHandler(self._owner.Dispatcher, self._owner.Dispatcher.Stream.features) + except NodeProcessed: + pass + else: + self.startsasl = None + + def auth(self): + """ + Start authentication. Result can be obtained via "SASL.startsasl" attribute + and will beeither "success" or "failure". Note that successfull + auth will take at least two Dispatcher.Process() calls. + """ + if self.startsasl: + pass + elif self._owner.Dispatcher.Stream.features: + try: + self.FeaturesHandler(self._owner.Dispatcher, self._owner.Dispatcher.Stream.features) + except NodeProcessed: + pass + else: + self._owner.RegisterHandler("features", self.FeaturesHandler, xmlns=NS_STREAMS) + + def plugout(self): + """ + Remove SASL handlers from owner's dispatcher. Used internally. + """ + if hasattr(self._owner, "features"): + self._owner.UnregisterHandler("features", self.FeaturesHandler, xmlns=NS_STREAMS) + if hasattr(self._owner, "challenge"): + self._owner.UnregisterHandler("challenge", self.SASLHandler, xmlns=NS_SASL) + if hasattr(self._owner, "failure"): + self._owner.UnregisterHandler("failure", self.SASLHandler, xmlns=NS_SASL) + if hasattr(self._owner, "success"): + self._owner.UnregisterHandler("success", self.SASLHandler, xmlns=NS_SASL) + + def FeaturesHandler(self, conn, feats): + """ + Used to determine if server supports SASL auth. Used internally. + """ + if not feats.getTag("mechanisms", namespace=NS_SASL): + self.startsasl = "not-supported" + self.DEBUG("SASL not supported by server", "error") + return None + mecs = [] + for mec in feats.getTag("mechanisms", namespace=NS_SASL).getTags("mechanism"): + mecs.append(mec.getData()) + self._owner.RegisterHandler("challenge", self.SASLHandler, xmlns=NS_SASL) + self._owner.RegisterHandler("failure", self.SASLHandler, xmlns=NS_SASL) + self._owner.RegisterHandler("success", self.SASLHandler, xmlns=NS_SASL) + if "ANONYMOUS" in mecs and self.username == None: + node = Node("auth", attrs={"xmlns": NS_SASL, "mechanism": "ANONYMOUS"}) + elif "DIGEST-MD5" in mecs: + node = Node("auth", attrs={"xmlns": NS_SASL, "mechanism": "DIGEST-MD5"}) + elif "PLAIN" in mecs: + sasl_data = "%s\x00%s\x00%s" % ("@".join((self.username, self._owner.Server)), self.username, self.password) + node = Node("auth", attrs={"xmlns": NS_SASL, "mechanism": "PLAIN"}, payload=[encodestring(sasl_data).replace("\r", "").replace("\n", "")]) + else: + self.startsasl = "failure" + self.DEBUG("I can only use DIGEST-MD5 and PLAIN mecanisms.", "error") + return + self.startsasl = "in-process" + self._owner.send(node.__str__()) + raise NodeProcessed() + + def SASLHandler(self, conn, challenge): + """ + Perform next SASL auth step. Used internally. + """ + if challenge.getNamespace() != NS_SASL: + return None + if challenge.getName() == "failure": + self.startsasl = "failure" + try: + reason = challenge.getChildren()[0] + except Exception: + reason = challenge + self.DEBUG("Failed SASL authentification: %s" % reason, "error") + raise NodeProcessed() + elif challenge.getName() == "success": + self.startsasl = "success" + self.DEBUG("Successfully authenticated with remote server.", "ok") + handlers = self._owner.Dispatcher.dumpHandlers() + self._owner.Dispatcher.PlugOut() + dispatcher.Dispatcher().PlugIn(self._owner) + self._owner.Dispatcher.restoreHandlers(handlers) + self._owner.User = self.username + raise NodeProcessed() + incoming_data = challenge.getData() + chal = {} + data = decodestring(incoming_data) + self.DEBUG("Got challenge:" + data, "ok") + for pair in re_findall('(\w+\s*=\s*(?:(?:"[^"]+")|(?:[^,]+)))', data): + key, value = [x.strip() for x in pair.split("=", 1)] + if value[:1] == '"' and value[-1:] == '"': + value = value[1:-1] + chal[key] = value + if "qop" in chal and "auth" in [x.strip() for x in chal["qop"].split(",")]: + resp = {} + resp["username"] = self.username + resp["realm"] = self._owner.Server + resp["nonce"] = chal["nonce"] + cnonce = "" + for i in xrange(7): + cnonce += hex(int(_random() * 65536 * 4096))[2:] + resp["cnonce"] = cnonce + resp["nc"] = ("00000001") + resp["qop"] = "auth" + resp["digest-uri"] = "xmpp/" + self._owner.Server + A1 = C([H(C([resp["username"], resp["realm"], self.password])), resp["nonce"], resp["cnonce"]]) + A2 = C(["AUTHENTICATE", resp["digest-uri"]]) + response = HH(C([HH(A1), resp["nonce"], resp["nc"], resp["cnonce"], resp["qop"], HH(A2)])) + resp["response"] = response + resp["charset"] = "utf-8" + sasl_data = "" + for key in ("charset", "username", "realm", "nonce", "nc", "cnonce", "digest-uri", "response", "qop"): + if key in ("nc", "qop", "response", "charset"): + sasl_data += "%s=%s," % (key, resp[key]) + else: + sasl_data += "%s=\"%s\"," % (key, resp[key]) + node = Node("response", attrs={"xmlns": NS_SASL}, payload=[encodestring(sasl_data[:-1]).replace("\r", "").replace("\n", "")]) + self._owner.send(node.__str__()) + elif "rspauth" in chal: + self._owner.send(Node("response", attrs={"xmlns": NS_SASL}).__str__()) + else: + self.startsasl = "failure" + self.DEBUG("Failed SASL authentification: unknown challenge", "error") + raise NodeProcessed() + +class Bind(PlugIn): + """ + Bind some JID to the current connection to allow router know of our location. + """ + def __init__(self): + PlugIn.__init__(self) + self.DBG_LINE = "bind" + self.bound = None + + def plugin(self, owner): + """ + Start resource binding, if allowed at this time. Used internally. + """ + if self._owner.Dispatcher.Stream.features: + try: + self.FeaturesHandler(self._owner.Dispatcher, self._owner.Dispatcher.Stream.features) + except NodeProcessed: + pass + else: + self._owner.RegisterHandler("features", self.FeaturesHandler, xmlns=NS_STREAMS) + + def plugout(self): + """ + Remove Bind handler from owner's dispatcher. Used internally. + """ + self._owner.UnregisterHandler("features", self.FeaturesHandler, xmlns=NS_STREAMS) + + def FeaturesHandler(self, conn, feats): + """ + Determine if server supports resource binding and set some internal attributes accordingly. + """ + if not feats.getTag("bind", namespace=NS_BIND): + self.bound = "failure" + self.DEBUG("Server does not requested binding.", "error") + return None + if feats.getTag("session", namespace=NS_SESSION): + self.session = 1 + else: + self.session = -1 + self.bound = [] + + def Bind(self, resource=None): + """ + Perform binding. Use provided resource name or random (if not provided). + """ + while self.bound is None and self._owner.Process(1): + pass + if resource: + resource = [Node("resource", payload=[resource])] + else: + resource = [] + resp = self._owner.SendAndWaitForResponse(Protocol("iq", typ="set", payload=[Node("bind", attrs={"xmlns": NS_BIND}, payload=resource)])) + if isResultNode(resp): + self.bound.append(resp.getTag("bind").getTagData("jid")) + self.DEBUG("Successfully bound %s." % self.bound[-1], "ok") + jid = JID(resp.getTag("bind").getTagData("jid")) + self._owner.User = jid.getNode() + self._owner.Resource = jid.getResource() + resp = self._owner.SendAndWaitForResponse(Protocol("iq", typ="set", payload=[Node("session", attrs={"xmlns": NS_SESSION})])) + if isResultNode(resp): + self.DEBUG("Successfully opened session.", "ok") + self.session = 1 + return "ok" + else: + self.DEBUG("Session open failed.", "error") + self.session = 0 + elif resp: + self.DEBUG("Binding failed: %s." % resp.getTag("error"), "error") + else: + self.DEBUG("Binding failed: timeout expired.", "error") + return "" + +class ComponentBind(PlugIn): + """ + ComponentBind some JID to the current connection to allow router know of our location. + """ + def __init__(self, sasl): + PlugIn.__init__(self) + self.DBG_LINE = "bind" + self.bound = None + self.needsUnregister = None + self.sasl = sasl + + def plugin(self, owner): + """ + Start resource binding, if allowed at this time. Used internally. + """ + if not self.sasl: + self.bound = [] + return None + if self._owner.Dispatcher.Stream.features: + try: + self.FeaturesHandler(self._owner.Dispatcher, self._owner.Dispatcher.Stream.features) + except NodeProcessed: + pass + else: + self._owner.RegisterHandler("features", self.FeaturesHandler, xmlns=NS_STREAMS) + self.needsUnregister = 1 + + def plugout(self): + """ + Remove ComponentBind handler from owner's dispatcher. Used internally. + """ + if self.needsUnregister: + self._owner.UnregisterHandler("features", self.FeaturesHandler, xmlns=NS_STREAMS) + + def FeaturesHandler(self, conn, feats): + """ + Determine if server supports resource binding and set some internal attributes accordingly. + """ + if not feats.getTag("bind", namespace=NS_BIND): + self.bound = "failure" + self.DEBUG("Server does not requested binding.", "error") + return None + if feats.getTag("session", namespace=NS_SESSION): + self.session = 1 + else: + self.session = -1 + self.bound = [] + + def Bind(self, domain=None): + """ + Perform binding. Use provided domain name (if not provided). + """ + while self.bound is None and self._owner.Process(1): + pass + if self.sasl: + xmlns = NS_COMPONENT_1 + else: + xmlns = None + self.bindresponse = None + ttl = dispatcher.DefaultTimeout + self._owner.RegisterHandler("bind", self.BindHandler, xmlns=xmlns) + self._owner.send(Protocol("bind", attrs={"name": domain}, xmlns=NS_COMPONENT_1)) + while self.bindresponse is None and self._owner.Process(1) and ttl > 0: + ttl -= 1 + self._owner.UnregisterHandler("bind", self.BindHandler, xmlns=xmlns) + resp = self.bindresponse + if resp and resp.getAttr("error"): + self.DEBUG("Binding failed: %s." % resp.getAttr("error"), "error") + elif resp: + self.DEBUG("Successfully bound.", "ok") + return "ok" + else: + self.DEBUG("Binding failed: timeout expired.", "error") + return "" + + def BindHandler(self, conn, bind): + self.bindresponse = bind + pass |