예제 #1
0
파일: iq.py 프로젝트: linusyang/pjabberd
    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
예제 #2
0
파일: iq.py 프로젝트: Kiesco08/pjabberd
    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
예제 #3
0
    def handle(self, tree, msg, lastRetVal=None):

        # The spec is silent on the case when a reinitialized <stream> is different
        # from the initial <stream>. In theory, there is never a need to change
        # any attributes in the new stream other than to change the ns prefix.
        # That seems like a dubious use case, so for now we just assume the stream
        # is the same as when it was first sent. This can be changed if it doesn't
        # play well with some clients.

        ns = msg.conn.parser.ns
        id = generateId()

        msg.conn.data['stream']['id'] = id

        msg.addTextOutput(u"<stream:stream from='%s' id='%s' xmlns='%s' "  \
                              % (msg.conn.server.hostname, id, ns) + \
                        "xmlns:stream='http://etherx.jabber.org/streams' " + \
                        "version='1.0'>")

        if msg.conn.data['tls']['complete']:
            # TODO: go to features-auth
            return lastRetVal

        if msg.conn.data['sasl']['complete']:
            msg.setNextHandler('write')
            msg.setNextHandler('features-postauth')
            return lastRetVal
예제 #4
0
파일: iq.py 프로젝트: stack-coder/pjabberd
    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
예제 #5
0
    def handle(self, tree, msg, lastRetVal=None):
        # Expat removes the xmlns attributes, so we save them in the parser
        # class and check them here.
        ns = msg.conn.parser.ns

        if ns == 'jabber:client':
            streamType = 'client'
        elif ns == 'jabber:server':
            streamType = 'server'
        else:
            # TODO: send <bad-namespace-prefix/>
            logging.warning("[%s] Unknown stream namespace: %s",
                            self.__class__, ns)
            return lastRetVal

        # TODO: version check

        id = generateId()

        msg.conn.data['stream']['in-stream'] = True
        msg.conn.data['stream']['type'] = streamType
        msg.conn.data['stream']['id'] = id

        # no one should need to modify this, so we don't pass it along
        # to the next handler, but just add it to the socket write queue
        # commenting this out for now as it causes expat problems
        msg.addTextOutput(u"<?xml version='1.0'?>" + \
        #msg.addTextOutput(

                "<stream:stream from='%s' id='%s' xmlns='%s' "  \
                    % (msg.conn.server.hostname, id, ns) + \
                "xmlns:stream='http://etherx.jabber.org/streams' " + \
                "version='1.0'>")
예제 #6
0
파일: stream.py 프로젝트: Kiesco08/pjabberd
    def handle(self, tree, msg, lastRetVal=None):

        # The spec is silent on the case when a reinitialized <stream> is different
        # from the initial <stream>. In theory, there is never a need to change
        # any attributes in the new stream other than to change the ns prefix.
        # That seems like a dubious use case, so for now we just assume the stream
        # is the same as when it was first sent. This can be changed if it doesn't
        # play well with some clients.

        ns = msg.conn.parser.ns
        id = generateId()

        msg.conn.data["stream"]["id"] = id

        msg.addTextOutput(
            u"<stream:stream from='%s' id='%s' xmlns='%s' " % (msg.conn.server.hostname, id, ns)
            + "xmlns:stream='http://etherx.jabber.org/streams' "
            + "version='1.0'>"
        )

        if msg.conn.data["tls"]["complete"]:
            # TODO: go to features-auth
            return lastRetVal

        if msg.conn.data["sasl"]["complete"]:
            msg.setNextHandler("write")
            msg.setNextHandler("features-postauth")
            return lastRetVal
예제 #7
0
파일: stream.py 프로젝트: Kiesco08/pjabberd
    def handle(self, tree, msg, lastRetVal=None):
        # Expat removes the xmlns attributes, so we save them in the parser
        # class and check them here.
        ns = msg.conn.parser.ns

        if ns == "jabber:client":
            streamType = "client"
        elif ns == "jabber:server":
            streamType = "server"
        else:
            # TODO: send <bad-namespace-prefix/>
            logging.warning("[%s] Unknown stream namespace: %s", self.__class__, ns)
            return lastRetVal

        # TODO: version check

        id = generateId()

        msg.conn.data["stream"]["in-stream"] = True
        msg.conn.data["stream"]["type"] = streamType
        msg.conn.data["stream"]["id"] = id

        # no one should need to modify this, so we don't pass it along
        # to the next handler, but just add it to the socket write queue
        # commenting this out for now as it causes expat problems
        msg.addTextOutput(
            u"<?xml version='1.0'?>"
            +
            # msg.addTextOutput(
            "<stream:stream from='%s' id='%s' xmlns='%s' " % (msg.conn.server.hostname, id, ns)
            + "xmlns:stream='http://etherx.jabber.org/streams' "
            + "version='1.0'>"
        )
예제 #8
0
파일: iq.py 프로젝트: Kiesco08/pjabberd
def bindResource(msg, resource):
    """Records the resource binding. Returns the bare JID.
    This should only be called from the C2Sserver.
    """
    data = msg.conn.data
    server = msg.conn.server
    jid = data["user"]["jid"]

    # check if we have this resource already
    if server.data["resources"].has_key(jid) and server.data["resources"][jid].has_key(resource):
        # create our own
        resource = resource + generateId()[:6]
    data["user"]["resource"] = resource

    # record the resource in the JID object of the (JID, Connection) pair
    # this is for local delivery lookups
    server.conns[msg.conn.id][0].resource = resource

    # save the jid/resource in the server's global storage
    if not server.data["resources"].has_key(jid):
        server.data["resources"][jid] = {}
    server.data["resources"][jid][resource] = msg.conn
예제 #9
0
파일: iq.py 프로젝트: stack-coder/pjabberd
def bindResource(msg, resource):
    """Records the resource binding. Returns the bare JID.
    This should only be called from the C2Sserver.
    """
    data = msg.conn.data
    server = msg.conn.server
    jid = data['user']['jid']

    # check if we have this resource already
    if server.data['resources'].has_key(jid) and \
    server.data['resources'][jid].has_key(resource):
        # create our own
        resource = resource + generateId()[:6]
    data['user']['resource'] = resource

    # record the resource in the JID object of the (JID, Connection) pair
    # this is for local delivery lookups
    server.conns[msg.conn.id][0].resource = resource

    # save the jid/resource in the server's global storage
    if not server.data['resources'].has_key(jid):
        server.data['resources'][jid] = {}
    server.data['resources'][jid][resource] = msg.conn
예제 #10
0
파일: iq.py 프로젝트: linusyang/pjabberd
        def act():
            # we have to be passed a tree to work
            # or a tuple with routingData and a tree
            if not isinstance(lastRetVal, list):
                logging.warning('[%s] lastRetVal is not a list', self.__class__)
                return
            if isinstance(lastRetVal[-1], Element):
                if lastRetVal[-1].tag.find('query') == -1:
                    logging.warning('[%s] Got a non-query Element last return value' +\
                                '. Last return value: %s',
                                self.__class__, lastRetVal)
            elif isinstance(lastRetVal[-1], tuple):
                if not isinstance(lastRetVal[-1][0], dict) \
                or not isinstance(lastRetVal[-1][1], Element):
                    logging.warning('[%s] Got a non-query Element last return value' +\
                                '. Last return value: %s',
                                self.__class__, lastRetVal)
                    return
            else:
                logging.warning('[%s] Roster push needs either a <query> Element ' +\
                                'as the last item in lastRetVal or a tuple ' + \
                                'with (routeData, query Element)', self.__class__)
                return

            # this is the roster <query> that we'll send
            # it could be a tuple if we got routing data as well
            query = lastRetVal.pop(-1)
            routeData = None

            # did we get routing data (from S2S)
            if isinstance(query, tuple):
                routeData = query[0]
                query = query[1]

            if routeData:
                jid = routeData['jid']
                resources = routeData['resources']
            else:
                jid = msg.conn.data['user']['jid']
                resource = msg.conn.data['user']['resource']
                resources = msg.conn.server.data['resources'][jid]

            for res, con in resources.items():
                # don't send the roster to clients that didn't request it
                if con.data['user']['requestedRoster']:
                    iq = Element('iq', {
                                        'to' : '%s/%s' % (jid, res),
                                        'type' : 'set',
                                        'id' : generateId()[:10]
                                        })
                    iq.append(query)

                    # TODO: remove this. debug.
                    logging.debug("Sending " + tostring(iq))
                    con.send(tostring(iq))

            if tree.tag == '{jabber:client}iq' and tree.get('id'):
                # send an ack to client if this is in reply to a roster get/set
                id = tree.get('id')
                d = {
                     'to' : '%s/%s' % (jid, resource),
                     'type' : 'result',
                     'id' : id
                     }
                iq = Element('iq', d)
                return chainOutput(lastRetVal, iq)
예제 #11
0
파일: iq.py 프로젝트: Kiesco08/pjabberd
        def act():
            # we have to be passed a tree to work
            # or a tuple with routingData and a tree
            if not isinstance(lastRetVal, list):
                logging.warning("[%s] lastRetVal is not a list", self.__class__)
                return
            if isinstance(lastRetVal[-1], Element):
                if lastRetVal[-1].tag.find("query") == -1:
                    logging.warning(
                        "[%s] Got a non-query Element last return value" + ". Last return value: %s",
                        self.__class__,
                        lastRetVal,
                    )
            elif isinstance(lastRetVal[-1], tuple):
                if not isinstance(lastRetVal[-1][0], dict) or not isinstance(lastRetVal[-1][1], Element):
                    logging.warning(
                        "[%s] Got a non-query Element last return value" + ". Last return value: %s",
                        self.__class__,
                        lastRetVal,
                    )
                    return
            else:
                logging.warning(
                    "[%s] Roster push needs either a <query> Element "
                    + "as the last item in lastRetVal or a tuple "
                    + "with (routeData, query Element)",
                    self.__class__,
                )
                return

            # this is the roster <query> that we'll send
            # it could be a tuple if we got routing data as well
            query = lastRetVal.pop(-1)
            routeData = None

            # did we get routing data (from S2S)
            if isinstance(query, tuple):
                routeData = query[0]
                query = query[1]

            if routeData:
                jid = routeData["jid"]
                resources = routeData["resources"]
            else:
                jid = msg.conn.data["user"]["jid"]
                resource = msg.conn.data["user"]["resource"]
                resources = msg.conn.server.data["resources"][jid]

            for res, con in resources.items():
                # don't send the roster to clients that didn't request it
                if con.data["user"]["requestedRoster"]:
                    iq = Element("iq", {"to": "%s/%s" % (jid, res), "type": "set", "id": generateId()[:10]})
                    iq.append(query)

                    # TODO: remove this. debug.
                    logging.debug("Sending " + tostring(iq))
                    con.send(tostring(iq))

            if tree.tag == "{jabber:client}iq" and tree.get("id"):
                # send an ack to client if this is in reply to a roster get/set
                id = tree.get("id")
                d = {"to": "%s/%s" % (jid, resource), "type": "result", "id": id}
                iq = Element("iq", d)
                return chainOutput(lastRetVal, iq)
예제 #12
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
예제 #13
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
예제 #14
0
파일: iq.py 프로젝트: stack-coder/pjabberd
        def act():
            # we have to be passed a tree to work
            # or a tuple with routingData and a tree
            if not isinstance(lastRetVal, list):
                logging.warning('[%s] lastRetVal is not a list',
                                self.__class__)
                return
            if isinstance(lastRetVal[-1], Element):
                if lastRetVal[-1].tag.find('query') == -1:
                    logging.warning('[%s] Got a non-query Element last return value' +\
                                '. Last return value: %s',
                                self.__class__, lastRetVal)
            elif isinstance(lastRetVal[-1], tuple):
                if not isinstance(lastRetVal[-1][0], dict) \
                or not isinstance(lastRetVal[-1][1], Element):
                    logging.warning('[%s] Got a non-query Element last return value' +\
                                '. Last return value: %s',
                                self.__class__, lastRetVal)
                    return
            else:
                logging.warning('[%s] Roster push needs either a <query> Element ' +\
                                'as the last item in lastRetVal or a tuple ' + \
                                'with (routeData, query Element)', self.__class__)
                return

            # this is the roster <query> that we'll send
            # it could be a tuple if we got routing data as well
            query = lastRetVal.pop(-1)
            routeData = None

            # did we get routing data (from S2S)
            if isinstance(query, tuple):
                routeData = query[0]
                query = query[1]

            if routeData:
                jid = routeData['jid']
                resources = routeData['resources']
            else:
                jid = msg.conn.data['user']['jid']
                resource = msg.conn.data['user']['resource']
                resources = msg.conn.server.data['resources'][jid]

            for res, con in resources.items():
                # don't send the roster to clients that didn't request it
                if con.data['user']['requestedRoster']:
                    iq = Element(
                        'iq', {
                            'to': '%s/%s' % (jid, res),
                            'type': 'set',
                            'id': generateId()[:10]
                        })
                    iq.append(query)

                    # TODO: remove this. debug.
                    logging.debug("Sending " + tostring(iq))
                    con.send(tostring(iq))

            if tree.tag == '{jabber:client}iq' and tree.get('id'):
                # send an ack to client if this is in reply to a roster get/set
                id = tree.get('id')
                d = {
                    'to': '%s/%s' % (jid, resource),
                    'type': 'result',
                    'id': id
                }
                iq = Element('iq', d)
                return chainOutput(lastRetVal, iq)