Ejemplo n.º 1
0
    def handle(self, tree, msg, lastRetVal=None):
        if len(tree) > 0:
            # get the original iq msg
            origIQ = tree
        else:
            logging.warning("[%s] Original <iq> missing:\n%s", self.__class__,
                            tostring(tree))
            return

        id = origIQ.get('id')
        if id:
            res = Element('iq', {'type': 'error', 'id': id})
            res.append(origIQ)

            err = Element('error', {'type': 'cancel'})
            SubElement(err, 'service-unavailable',
                       {'xmlns': 'urn:ietf:params:xml:ns:xmpp-stanzas'})

            res.append(err)

            return chainOutput(lastRetVal, res)
        else:
            logging.warning("[%s] No id in <iq>:\n%s", self.__class__,
                            tostring(origIQ))

        return lastRetVal
Ejemplo n.º 2
0
    def createRosterQuery(cjid,
                          subName,
                          name=None,
                          groups=None,
                          itemArgs=None):
        """Creates and returns a <query> item for sending in an <iq> in a
        roster push.
        cjid -- jid as a str for the contact in a roster item.
        subName -- name of the subscription as a str.
        name -- name for the contact. Can be None.
        groups -- list of group names as strings.
        """
        itemArgs = itemArgs or {}
        query = Element('query', {'xmlns': 'jabber:iq:roster'})

        d = {
            'jid': cjid,
            'subscription': subName,
        }
        if name:
            d['name'] = name

        d.update(itemArgs)

        item = SubElement(query, 'item', d)

        for groupName in groups:
            if groupName:  # don't want empty groups
                group = Element('group')
                group.text = groupName
                item.append(group)

        return query
Ejemplo n.º 3
0
    def handle(self, tree, msg, lastRetVal=None):
        iq = tree
        id = iq.get('id')
        if id:
            bind = iq[0]
            if len(bind) > 0:
                resource = bind[0].text
            else:
                # generate an id
                resource = generateId()[:6]

            # TODO: check that we don't already have such a resource
            jid = msg.conn.data['user']['jid']
            bindResource(msg, resource)

            res = Element('iq', {'type': 'result', 'id': id})
            bind = Element('bind',
                           {'xmlns': 'urn:ietf:params:xml:ns:xmpp-bind'})
            jidEl = Element('jid')
            jidEl.text = '%s/%s' % (jid, resource)
            bind.append(jidEl)
            res.append(bind)

            return chainOutput(lastRetVal, res)
        else:
            logging.warning("[%s] No id in <iq>:\n%s", self.__class__,
                            tostring(iq))

        return lastRetVal
Ejemplo n.º 4
0
 def makeServiceUnavailableError():
     fromJID = cjid.__str__()
     reply = Element('message', {'type': 'error', 'to': fromJID})
     reply.append(copy(tree))
     error = Element('error', {'type': 'cancel'})
     SubElement(error, 'service-unavailable',
                {'xmlns': 'urn:ietf:params:xml:ns:xmpp-stanzas'})
     routeData = {'to': fromJID, 'data': reply}
     msg.setNextHandler('route-client')
     return chainOutput(lastRetVal, routeData)
Ejemplo n.º 5
0
    def handle_close(self):
        # we want to be able to do stuff in handlers when the connection is
        # closed, so we dispatch into the 'stream-end' phase.

        # we set the tree to a dummy element so that the handlers could modify
        # it.
        if self.data['stream']['closing']:
            # already closing
            return
        wrapper = Element('wrapper')
        tree = Element('tag')
        wrapper.append(tree)
        Dispatcher().dispatch(wrapper, self, 'stream-end')
Ejemplo n.º 6
0
    def handle(self, tree, msg, lastRetVal=None):
        res = Element('stream:features')
        SubElement(res, 'bind', {'xmlns': 'urn:ietf:params:xml:ns:xmpp-bind'})
        SubElement(res, 'session',
                   {'xmlns': 'urn:ietf:params:xml:ns:xmpp-session'})

        return chainOutput(lastRetVal, res)
Ejemplo n.º 7
0
def makeSuccess(id):
    """Creates the auth successful iq stanza"""
    d = {'type': 'result'}
    if id:
        d['id'] = id
    iq = Element('iq', d)
    return iq
Ejemplo n.º 8
0
            def handle(self, tree, msg, lastRetVal=None):
                res = Element('stream:features')
                mechs = SubElement(res, 'mechanisms',
                                   {'xmlns' : 'urn:ietf:params:xml:ns:xmpp-sasl'})
                SubElement(mechs, 'mechanism').text = 'PLAIN'

                return chainOutput(lastRetVal, res)
Ejemplo n.º 9
0
    def getAsTree(self):
        """Returns the roster Element tree starting from <query>. Call
        loadRoster() before this.
        """
        query = Element('query', {'xmlns': 'jabber:iq:roster'})
        for item in self.items:
            query.append(self.items[item].getAsTree())

        return query
Ejemplo n.º 10
0
def checkPolicyViolation(msg):
    """Checks if a SASL auth is being or has been attempted"""
    data = msg.conn.data['sasl']
    if data['in-progress'] or data['complete'] or data['mechObj']:
        # policy violation!
        se = Element('stream:error')
        SubElement(se, 'policy-violation',
                   {'xmlns': 'urn:ietf:params:xml:ns:xmpp-streams'})
        return se
Ejemplo n.º 11
0
def makeConflict(id):
    """Creates the conflict iq error stanza"""
    d = {'type': 'error'}
    if id:
        d['id'] = id
    iq = Element('iq', d)
    error = SubElement(iq, 'error', {'code': '409', 'type': 'cancel'})
    SubElement(error, 'conflict',
               {'xmlns': 'urn:ietf:params:xml:ns:xmpp-stanzas'})
    return iq
Ejemplo n.º 12
0
def makeNotAcceptable(id):
    """Creates the not-acceptable iq error stanza"""
    d = {'type': 'error'}
    if id:
        d['id'] = id
    iq = Element('iq', d)
    error = SubElement(iq, 'error', {'code': '406', 'type': 'modify'})
    SubElement(error, 'not-acceptable',
               {'xmlns': 'urn:ietf:params:xml:ns:xmpp-stanzas'})
    return iq
Ejemplo n.º 13
0
def makeNotAuthorized(id):
    """Creates the not-authorized iq error stanza"""
    d = {'type': 'error'}
    if id:
        d['id'] = id
    iq = Element('iq', d)
    error = SubElement(iq, 'error', {'code': '401', 'type': 'cancel'})

    SubElement(error, 'not-authorized',
               {'xmlns': 'urn:ietf:params:xml:ns:xmpp-stanzas'})
    return iq
Ejemplo n.º 14
0
    def handle(self, tree, msg, lastRetVal=None):
        res = Element(
            'iq', {
                'from': msg.conn.server.hostname,
                'type': 'result',
                'id': tree.get('id')
            })

        msg.conn.data['user']['in-session'] = True

        return chainOutput(lastRetVal, res)
Ejemplo n.º 15
0
    def testNamespacedXPath(self):
        """Should be able to refer to elements using namespaces in XPath"""
        self.p.feed(streamStart)
        self.p.feed('<stream:features></stream:features>')

        stream = Element('dummy')
        stream.insert(0, self.p.tree)

        self.assert_(
            stream.find('{http://etherx.jabber.org/streams}features')
            is not None)
Ejemplo n.º 16
0
    def handle(self, tree, msg, lastRetVal=None):
        if isinstance(lastRetVal, SASLError):
            el = Element('failure',
                         {'xmlns': 'urn:ietf:params:xml:ns:xmpp-sasl'})
            el.append(lastRetVal.errorElement())

            return chainOutput(lastRetVal, el)
        else:
            logging.warning("[%s] SASLErrorHandler was passed a non-SASL " +\
                            "exception. Exception: %s",
                            self.__class__, lastRetVal)
            raise Exception, "can't handle a non-SASL error"
Ejemplo n.º 17
0
    def handle(self, tree, msg, lastRetVal=None):
        res = Element('stream:features')
        mechs = SubElement(res, 'mechanisms',
                           {'xmlns': 'urn:ietf:params:xml:ns:xmpp-sasl'})
        SubElement(mechs, 'mechanism').text = 'DIGEST-MD5'
        SubElement(mechs, 'mechanism').text = 'PLAIN'

        # we also support the old style jabber:iq:auth
        SubElement(res, 'auth',
                   {'xmlns': 'http://jabber.org/features/iq-auth'})

        return chainOutput(lastRetVal, res)
Ejemplo n.º 18
0
    def getAsTree(self):
        """Return the roster item as an Element tree starting from <item>"""
        item = Element(
            'item', {
                'jid':
                self.jid,
                'subscription':
                Subscription.getPrimaryNameFromState(self.subscription)
            })
        if self.name:
            item.set('name', self.name)
        for group in self.groups:
            SubElement(item, 'group').text = group

        return item
Ejemplo n.º 19
0
    def handle(self, b64text):
        """Verify the username/password in response"""
        authtext = ''
        if b64text:
            try:
                authtext = fromBase64(b64text)
            except:
                raise SASLIncorrectEncodingError

            auth = authtext.split('\x00')

            if len(auth) != 3:
                raise SASLIncorrectEncodingError

            con = DBautocommit()
            c = con.cursor()
            c.execute("SELECT * FROM jids WHERE \
                jid = ? AND password = ?"                                         , (auth[1] + \
                                            '@' + self.msg.conn.server.hostname, auth[2]))
            res = c.fetchall()
            if len(res) == 0:
                c.close()
                con.close()
                raise SASLAuthError
            c.close()
            con.close()

        self.msg.conn.data['sasl']['complete'] = True
        self.msg.conn.data['sasl']['in-progress'] = False
        self.msg.conn.data['user']['jid'] = '%s@%s' % (
            auth[1], self.msg.conn.server.hostname)

        # record the JID for local delivery
        self.msg.conn.server.conns[self.msg.conn.id] = (JID(
            self.msg.conn.data['user']['jid']), self.msg.conn)

        self.msg.conn.parser.resetParser()

        return Element('success',
                       {'xmlns': 'urn:ietf:params:xml:ns:xmpp-sasl'})
Ejemplo n.º 20
0
        def act():
            # TODO: verify that it's coming from a known user
            jid = msg.conn.data['user']['jid']
            resource = msg.conn.data['user']['resource']
            id = tree.get('id')
            if id is None:
                logging.warning('[%s] No id in roster get query. Tree: %s',
                                self.__class__, tree)
                # TODO: throw exception here
                return

            roster = Roster(jid)

            roster.loadRoster()

            res = Element('iq', {
                'to': '/'.join([jid, resource]),
                'type': 'result',
                'id': id
            })

            res.append(roster.getAsTree())
            return chainOutput(lastRetVal, res)
Ejemplo n.º 21
0
    def __init__(self, sock, addr, server):
        ServerConnection.__init__(self, sock, addr, server)

        self.id = 'locsin%s' % id(self)

        self.data['server']['direction'] = 'from'
        self.data['server']['hostname'] = 'localhost'

        # send the <stream> to prime the parser for the ns it'll deal with,
        # but tell it not to process the xml during priming, since we don't
        # care for auth/encr on a loopback connection
        self.parser.disable()
        data = "<?xml version='1.0' ?>" +\
                "<stream:stream xmlns='jabber:server' " +\
                "xmlns:stream='http://etherx.jabber.org/streams' " +\
                "version='1.0'>"
        self.parser.feed(data)
        self.parser.depth = 1
        self.parser.stream = Element('{http://etherx.jabber.org/streams}stream',
                                     {'version' : '1.0'})
        self.parser.ns = 'jabber:server'
        self.parser.enable()

        logging.info("New LocalServerInConnection created with %s", addr)
Ejemplo n.º 22
0
        def act():
            d = msg.conn.data

            retVal = lastRetVal

            jid = d['user']['jid']
            resource = d['user']['resource']

            roster = Roster(jid)

            presTree = deepcopy(tree)
            presTree.set('from', '%s/%s' % (jid, resource))

            probes = []
            if tree.get('to') is None and not d['user']['active']:
                # initial presence
                # TODO: we don't need to do it every time. we can cache the
                # data after the first resource is active and just resend
                # that to all new resources
                d['user']['active'] = True

                # get jids of the contacts whose status we're interested in
                cjids = roster.getPresenceSubscriptions()

                probeTree = Element('presence', {
                                                 'type': 'probe',
                                                 'from' : '%s/%s' \
                                                    % (jid, resource)
                                                 })

                # TODO: replace this with a more efficient router handler
                for cjid in cjids:
                    probeTree.set('to', cjid)
                    probeRouteData = {'to': cjid, 'data': deepcopy(probeTree)}
                    probes.append(probeRouteData)
                    # they're sent first. see below

                # broadcast to other resources of this user
                retVal = self.broadcastToOtherResources(
                    presTree, msg, retVal, jid, resource)

            elif tree.get('to') is not None:
                # TODO: directed presence
                return
            elif tree.get('type') == 'unavailable':
                # broadcast to other resources of this user
                d['user']['active'] = False
                retVal = self.broadcastToOtherResources(
                    presTree, msg, retVal, jid, resource)

            # record this stanza as the last presence sent from this client
            lastPresence = deepcopy(tree)
            lastPresence.set('from', '%s/%s' % (jid, resource))
            d['user']['lastPresence'] = lastPresence

            # lookup contacts interested in presence
            cjids = roster.getPresenceSubscribers()

            # TODO: replace this with another router handler that would send
            # it out to all cjids in a batch instead of queuing a handler
            # for each
            for cjid in cjids:
                presTree.set('to', cjid)
                presRouteData = {'to': cjid, 'data': deepcopy(presTree)}
                retVal = chainOutput(retVal, presRouteData)
                msg.setNextHandler('route-server')

            # send the probes first
            for probe in probes:
                msg.setNextHandler('route-server')
                retVal = chainOutput(retVal, probe)

            return retVal
Ejemplo n.º 23
0
"""XMPP SASL and iq auth stuff"""
# Some parts are borrowed from twisted. See TWISTED-LICENSE for details on its
# license.

import pjs.auth_mechanisms as mechs
import pjs.threadpool as threadpool
import logging

from pjs.handlers.base import Handler, ThreadedHandler, poll, chainOutput
from pjs.auth_mechanisms import SASLError, IQAuthError
from pjs.handlers.iq import bindResource
from pjs.utils import FunctionCall
from pjs.elementtree.ElementTree import Element, SubElement

iqAuthEl = Element('iq', {'type': 'result'})
iqAuthQueryEl = SubElement(iqAuthEl, 'query', {'xmlns': 'jabber:iq:auth'})
SubElement(iqAuthQueryEl, 'username')
SubElement(iqAuthQueryEl, 'digest')
SubElement(iqAuthQueryEl, 'resource')


def checkPolicyViolation(msg):
    """Checks if a SASL auth is being or has been attempted"""
    data = msg.conn.data['sasl']
    if data['in-progress'] or data['complete'] or data['mechObj']:
        # policy violation!
        se = Element('stream:error')
        SubElement(se, 'policy-violation',
                   {'xmlns': 'urn:ietf:params:xml:ns:xmpp-streams'})
        return se
Ejemplo n.º 24
0
 def errorElement(self):
     return Element('not-authorized')
Ejemplo n.º 25
0
 def errorElement(self):
     return Element('temporary-auth-failure')
Ejemplo n.º 26
0
 def errorElement(self):
     return Element('mechanism-too-weak')
Ejemplo n.º 27
0
 def errorElement(self):
     return Element('invalid-mechanism')
Ejemplo n.º 28
0
 def errorElement(self):
     return Element('invalid-authzid')
Ejemplo n.º 29
0
 def errorElement(self):
     return Element('incorrect-encoding')
Ejemplo n.º 30
0
    def handle(self, data=None):
        """Performs DIGEST-MD5 auth based on current state.

        data -- either None for initial challenge, base64-encoded text when
                the client responds to challenge 1, or the tree when the client
                responds to challenge 2.
        """

        # TODO: authz
        # TODO: subsequent auth

        qop = 'qop="auth"'
        charset = 'charset=utf-8'
        algo = 'algorithm=md5-sess'

        if self.state == SASLDigestMD5.INIT:  # initial challenge
            self.nonce = generateId()
            self.state = SASLDigestMD5.SENT_CHALLENGE1

            nonce = 'nonce="%s"' % self.nonce
            realm = 'realm="%s"' % self.realm

            res = Element('challenge',
                          {'xmlns': 'urn:ietf:params:xml:ns:xmpp-sasl'})
            res.text = base64.b64encode(','.join(
                [realm, qop, nonce, charset, algo]))

            return res
        elif self.state == SASLDigestMD5.SENT_CHALLENGE1 and data:
            # response to client's reponse (ie. challenge 2)
            try:
                text = fromBase64(data)
            except:
                raise SASLIncorrectEncodingError

            pairs = self._parse(text)
            try:
                username = pairs['username']
                nonce = pairs['nonce']
                realm = pairs['realm']
                cnonce = pairs['cnonce']
                nc = pairs['nc']
                qop = pairs['qop']
                response = pairs['response']
                digest_uri = pairs['digest-uri']
            except KeyError:
                self._handleFailure()
                raise SASLAuthError

            self.username = username

            # authz is ignored for now
            if nonce != self.nonce or realm != self.realm \
                or int(nc, 16) != 1 or qop[0] != 'auth' or not response\
                or not digest_uri:
                self._handleFailure()
                raise SASLAuthError

            # fetch the password now
            con = DBautocommit()
            c = con.cursor()
            c.execute(
                "SELECT password FROM jids WHERE \
                jid = ?", (username + '@%s' % self.msg.conn.server.hostname, ))
            for row in c:
                password = row['password']
                break
            else:
                self._handleFailure()
                c.close()
                con.close()
                raise SASLAuthError
            c.close()
            con.close()

            # compute the digest as per RFC 2831
            a1 = "%s:%s:%s" % (H("%s:%s:%s" %
                                 (username, realm, password)), nonce, cnonce)
            a2 = ":%s" % digest_uri
            a2client = "AUTHENTICATE:%s" % digest_uri

            digest = HEX(
                KD(
                    HEX(H(a1)), "%s:%s:%s:%s:%s" %
                    (nonce, nc, cnonce, "auth", HEX(H(a2client)))))

            if digest == response:
                rspauth = HEX(
                    KD(
                        HEX(H(a1)), "%s:%s:%s:%s:%s" %
                        (nonce, nc, cnonce, "auth", HEX(H(a2)))))

                self.state = SASLDigestMD5.SENT_CHALLENGE2

                res = Element('challenge',
                              {'xmlns': 'urn:ietf:params:xml:ns:xmpp-sasl'})
                res.text = base64.b64encode(u"rspauth=%s" % rspauth)

                return res

            else:
                self._handleFailure()
                raise SASLAuthError
        elif self.state == SASLDigestMD5.SENT_CHALLENGE2 and isinstance(
                data, Element):
            # expect to get <response xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>
            respInd = data.tag.find(
                '{urn:ietf:params:xml:ns:xmpp-sasl}response')
            d = self.msg.conn.data
            if respInd != -1 and len(data) == 0:
                self.state = SASLDigestMD5.INIT
                d['sasl']['complete'] = True
                d['sasl']['in-progress'] = False
                d['user']['jid'] = '%s@%s' % (self.username,
                                              self.msg.conn.server.hostname)

                # record the JID for local delivery
                self.msg.conn.server.conns[self.msg.conn.id] = (JID(
                    d['user']['jid']), self.msg.conn)

                self.msg.conn.parser.resetParser()

                res = Element('success',
                              {'xmlns': 'urn:ietf:params:xml:ns:xmpp-sasl'})
                return res
            else:
                self._handleFailure()
                raise SASLAuthError
        else:
            self._handleFailure()
            raise SASLAuthError