コード例 #1
0
void QXmppIncomingServer::handleStanza(const QDomElement &stanza)
{
    const QString ns = stanza.namespaceURI();

    if (ns == ns_tls && stanza.tagName() == QLatin1String("starttls"))
    {
        sendData("<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
        socket()->flush();
        socket()->startServerEncryption();
        return;
    }
    else if (QXmppDialback::isDialback(stanza))
    {
        QXmppDialback request;
        request.parse(stanza);
        // check the request is valid
        if (!request.type().isEmpty() ||
            request.from().isEmpty() ||
            request.to() != d->domain ||
            request.key().isEmpty())
        {
            warning(QString("Invalid dialback received on %1").arg(d->origin()));
            return;
        }

        const QString domain = request.from();
        if (request.command() == QXmppDialback::Result)
        {
            debug(QString("Received a dialback result from '%1' on %2").arg(domain, d->origin()));

            // establish dialback connection
            QXmppOutgoingServer *stream = new QXmppOutgoingServer(d->domain, this);
            bool check = connect(stream, SIGNAL(dialbackResponseReceived(QXmppDialback)),
                                 this, SLOT(slotDialbackResponseReceived(QXmppDialback)));
            Q_ASSERT(check);
            Q_UNUSED(check);
            stream->setVerify(d->localStreamId, request.key());
            stream->connectToHost(domain);
        }
        else if (request.command() == QXmppDialback::Verify)
        {
            debug(QString("Received a dialback verify from '%1' on %2").arg(domain, d->origin()));
            emit dialbackRequestReceived(request);
        }

    }
    else if (d->authenticated.contains(QXmppUtils::jidToDomain(stanza.attribute("from"))))
    {
        // relay stanza if the remote party is authenticated
        emit elementReceived(stanza);
    } else {
        warning(QString("Received an element from unverified domain '%1' on %2").arg(QXmppUtils::jidToDomain(stanza.attribute("from")), d->origin()));
        disconnectFromHost();
    }
}
コード例 #2
0
void QXmppIncomingClient::handleStanza(const QDomElement &nodeRecv)
{
    const QString ns = nodeRecv.namespaceURI();

    if (d->idleTimer->interval())
        d->idleTimer->start();

    if (ns == ns_tls && nodeRecv.tagName() == "starttls")
    {
        sendData("<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
        socket()->flush();
        socket()->startServerEncryption();
        return;
    }
    else if (ns == ns_sasl)
    {
        if (nodeRecv.tagName() == "auth")
        {
            const QString mechanism = nodeRecv.attribute("mechanism");
            if (mechanism == "PLAIN")
            {
                QList<QByteArray> auth = QByteArray::fromBase64(nodeRecv.text().toAscii()).split('\0');
                if (auth.size() != 3)
                {
                    sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'><incorrect-encoding/></failure>");
                    disconnectFromHost();
                    return;
                }

                QXmppPasswordRequest request;
                request.setDomain(d->domain);
                request.setUsername(QString::fromUtf8(auth[1]));
                request.setPassword(QString::fromUtf8(auth[2]));
                if (!d->passwordChecker) {
                    // FIXME: what type of failure?
                    warning(QString("Cannot authenticate '%1', no password checker").arg(request.username()));
                    sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>");
                    disconnectFromHost();
                    return;
                }

                QXmppPasswordReply *reply = d->passwordChecker->checkPassword(request);
                reply->setParent(this);
                reply->setProperty("__sasl_username", request.username());
                connect(reply, SIGNAL(finished()), this, SLOT(onPasswordReply()));
            }
            else if (mechanism == "DIGEST-MD5")
            {
                // generate nonce
                d->saslDigest.setNonce(QXmppSaslDigestMd5::generateNonce());
                d->saslDigest.setQop("auth");
                d->saslStep = 1;

                QMap<QByteArray, QByteArray> challenge;
                challenge["nonce"] = d->saslDigest.nonce();
                challenge["realm"] = d->domain.toUtf8();
                challenge["qop"] = d->saslDigest.qop();
                challenge["charset"] = "utf-8";
                challenge["algorithm"] = "md5-sess";

                const QByteArray data = QXmppSaslDigestMd5::serializeMessage(challenge).toBase64();
                sendData("<challenge xmlns='urn:ietf:params:xml:ns:xmpp-sasl'>" + data +"</challenge>");
            }
            else
            {
                // unsupported method
                sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'></failure>");
                disconnectFromHost();
                return;
            }
        }
        else if (nodeRecv.tagName() == "response")
        {
            if (d->saslStep == 1)
            {
                const QByteArray raw = QByteArray::fromBase64(nodeRecv.text().toAscii());
                QMap<QByteArray, QByteArray> saslResponse = QXmppSaslDigestMd5::parseMessage(raw);

                // check credentials
                const QString username = QString::fromUtf8(saslResponse.value("username"));
                if (!d->passwordChecker) {
                    // FIXME: what type of failure?
                    warning(QString("Cannot authenticate '%1', no password checker").arg(username));
                    sendData("<failure xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>");
                    disconnectFromHost();
                    return;
                }

                QXmppPasswordRequest request;
                request.setUsername(username);
                request.setDomain(d->domain);

                QXmppPasswordReply *reply = d->passwordChecker->getDigest(request);
                reply->setParent(this);
                reply->setProperty("__sasl_raw", raw);
                connect(reply, SIGNAL(finished()), this, SLOT(onDigestReply()));
            }
            else if (d->saslStep == 2)
            {
                // authentication succeeded
                d->saslStep = 3;
                info(QString("Authentication succeeded for '%1'").arg(d->username));
                sendData("<success xmlns='urn:ietf:params:xml:ns:xmpp-sasl'/>");
            }
        }
    }
    else if (ns == ns_client)
    {
        if (nodeRecv.tagName() == "iq")
        {
            const QString type = nodeRecv.attribute("type");
            if (QXmppBindIq::isBindIq(nodeRecv) && type == "set")
            {
                QXmppBindIq bindSet;
                bindSet.parse(nodeRecv);
                d->resource = bindSet.resource().trimmed();
                if (d->resource.isEmpty())
                    d->resource = generateStanzaHash();

                QXmppBindIq bindResult;
                bindResult.setType(QXmppIq::Result);
                bindResult.setId(bindSet.id());
                bindResult.setJid(jid());
                sendPacket(bindResult);

                // bound
                emit connected();
                return;
            }
            else if (QXmppSessionIq::isSessionIq(nodeRecv) && type == "set")
            {
                QXmppSessionIq sessionSet;
                sessionSet.parse(nodeRecv);

                QXmppIq sessionResult;
                sessionResult.setType(QXmppIq::Result);
                sessionResult.setId(sessionSet.id());
                sessionResult.setTo(jid());
                sendPacket(sessionResult);
                return;
            }
        }

        // check the sender is legitimate
        const QString from = nodeRecv.attribute("from");
        if (!from.isEmpty() && from != jid() && from != jidToBareJid(jid()))
        {
            warning(QString("Received a stanza from unexpected JID %1").arg(from));
            return;
        }

        // process unhandled stanzas
        if (nodeRecv.tagName() == "iq" ||
            nodeRecv.tagName() == "message" ||
            nodeRecv.tagName() == "presence")
        {
            QDomElement nodeFull(nodeRecv);

            // if the sender is empty, set it to the appropriate JID
            if (nodeFull.attribute("from").isEmpty())
            {
                if (nodeFull.tagName() == "presence" &&
                    (nodeFull.attribute("type") == "subscribe" ||
                    nodeFull.attribute("type") == "subscribed"))
                    nodeFull.setAttribute("from", jidToBareJid(jid()));
                else
                    nodeFull.setAttribute("from", jid());
            }

            // if the recipient is empty, set it to the local domain
            if (nodeFull.attribute("to").isEmpty())
                nodeFull.setAttribute("to", d->domain);

            // emit stanza for processing by server
            emit elementReceived(nodeFull);
        }
    }
}
コード例 #3
0
void QXmppIncomingClient::handleStanza (const QDomElement &nodeRecv)
{
    const QString ns = nodeRecv.namespaceURI();

    if (d->idleTimer->interval())
        d->idleTimer->start();

    if (ns == ns_tls && nodeRecv.tagName() == QLatin1String ("starttls"))
    {
        sendData ("<proceed xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
        socket()->flush();
        socket()->startServerEncryption();
        return;
    }

    else if (ns == ns_sasl)
    {
        if (!d->passwordChecker)
        {
            warning ("Cannot perform authentication, no password checker");
            sendPacket (QXmppSaslFailure ("temporary-auth-failure"));
            disconnectFromHost();
            return;
        }

        if (nodeRecv.tagName() == QLatin1String ("auth"))
        {
            QXmppSaslAuth auth;
            auth.parse (nodeRecv);

            d->saslServer = QXmppSaslServer::create (auth.mechanism(), this);

            if (!d->saslServer)
            {
                sendPacket (QXmppSaslFailure ("invalid-mechanism"));
                disconnectFromHost();
                return;
            }

            d->saslServer->setRealm (d->domain.toUtf8());

            QByteArray challenge;
            QXmppSaslServer::Response result = d->saslServer->respond (auth.value(), challenge);

            if (result == QXmppSaslServer::InputNeeded)
            {
                // check credentials
                d->checkCredentials (auth.value());
            }

            else if (result == QXmppSaslServer::Challenge)
                sendPacket (QXmppSaslChallenge (challenge));

            else
            {
                // FIXME: what condition?
                sendPacket (QXmppSaslFailure());
                disconnectFromHost();
                return;
            }
        }

        else if (nodeRecv.tagName() == QLatin1String ("response"))
        {
            QXmppSaslResponse response;
            response.parse (nodeRecv);

            if (!d->saslServer)
            {
                warning ("SASL response received, but no mechanism selected");
                sendPacket (QXmppSaslFailure());
                disconnectFromHost();
                return;
            }

            QByteArray challenge;
            QXmppSaslServer::Response result = d->saslServer->respond (response.value(), challenge);

            if (result == QXmppSaslServer::InputNeeded)
            {
                // check credentials
                d->checkCredentials (response.value());
            }

            else if (result == QXmppSaslServer::Succeeded)
            {
                // authentication succeeded
                d->jid = QString ("%1@%2").arg (d->saslServer->username(), d->domain);
                info (QString ("Authentication succeeded for '%1' from %2").arg (d->jid, d->origin()));
                updateCounter ("incoming-client.auth.success");
                sendPacket (QXmppSaslSuccess());
                handleStart();
            }

            else
            {
                // FIXME: what condition?
                sendPacket (QXmppSaslFailure());
                disconnectFromHost();
            }
        }
    }

    else if (ns == ns_client)
    {
        if (nodeRecv.tagName() == QLatin1String ("iq"))
        {
            const QString type = nodeRecv.attribute ("type");

            if (QXmppBindIq::isBindIq (nodeRecv) && type == QLatin1String ("set"))
            {
                QXmppBindIq bindSet;
                bindSet.parse (nodeRecv);
                d->resource = bindSet.resource().trimmed();

                if (d->resource.isEmpty())
                    d->resource = QXmppUtils::generateStanzaHash();

                d->jid = QString ("%1/%2").arg (QXmppUtils::jidToBareJid (d->jid), d->resource);

                QXmppBindIq bindResult;
                bindResult.setType (QXmppIq::Result);
                bindResult.setId (bindSet.id());
                bindResult.setJid (d->jid);
                sendPacket (bindResult);

                // bound
                emit connected();
                return;
            }

            else if (QXmppSessionIq::isSessionIq (nodeRecv) && type == QLatin1String ("set"))
            {
                QXmppSessionIq sessionSet;
                sessionSet.parse (nodeRecv);

                QXmppIq sessionResult;
                sessionResult.setType (QXmppIq::Result);
                sessionResult.setId (sessionSet.id());
                sessionResult.setTo (d->jid);
                sendPacket (sessionResult);
                return;
            }
        }

        // check the sender is legitimate
        const QString from = nodeRecv.attribute ("from");

        if (!from.isEmpty() && from != d->jid && from != QXmppUtils::jidToBareJid (d->jid))
        {
            warning (QString ("Received a stanza from unexpected JID %1").arg (from));
            return;
        }

        // process unhandled stanzas
        if (nodeRecv.tagName() == QLatin1String ("iq") ||
                nodeRecv.tagName() == QLatin1String ("message") ||
                nodeRecv.tagName() == QLatin1String ("presence"))
        {
            QDomElement nodeFull (nodeRecv);

            // if the sender is empty, set it to the appropriate JID
            if (nodeFull.attribute ("from").isEmpty())
            {
                if (nodeFull.tagName() == QLatin1String ("presence") &&
                        (nodeFull.attribute ("type") == QLatin1String ("subscribe") ||
                         nodeFull.attribute ("type") == QLatin1String ("subscribed")))
                    nodeFull.setAttribute ("from", QXmppUtils::jidToBareJid (d->jid));

                else
                    nodeFull.setAttribute ("from", d->jid);
            }

            // if the recipient is empty, set it to the local domain
            if (nodeFull.attribute ("to").isEmpty())
                nodeFull.setAttribute ("to", d->domain);

            // emit stanza for processing by server
            emit elementReceived (nodeFull);
        }
    }
}
コード例 #4
0
void QXmppOutgoingClient::handleStanza(const QDomElement &nodeRecv)
{
    // if we receive any kind of data, stop the timeout timer
    d->timeoutTimer->stop();

    const QString ns = nodeRecv.namespaceURI();

    // give client opportunity to handle stanza
    bool handled = false;
    emit elementReceived(nodeRecv, handled);
    if (handled)
        return;

    if(QXmppStreamFeatures::isStreamFeatures(nodeRecv))
    {
        QXmppStreamFeatures features;
        features.parse(nodeRecv);

        if (!socket()->isEncrypted())
        {
            // determine TLS mode to use
            const QXmppConfiguration::StreamSecurityMode localSecurity = configuration().streamSecurityMode();
            const QXmppStreamFeatures::Mode remoteSecurity = features.tlsMode();
            if (!socket()->supportsSsl() &&
                (localSecurity == QXmppConfiguration::TLSRequired ||
                 remoteSecurity == QXmppStreamFeatures::Required))
            {
                warning("Disconnecting as TLS is required, but SSL support is not available");
                disconnectFromHost();
                return;
            }
            if (localSecurity == QXmppConfiguration::TLSRequired &&
                remoteSecurity == QXmppStreamFeatures::Disabled)
            {
                warning("Disconnecting as TLS is required, but not supported by the server");
                disconnectFromHost();
                return;
            }

            if (socket()->supportsSsl() &&
                localSecurity != QXmppConfiguration::TLSDisabled &&
                remoteSecurity != QXmppStreamFeatures::Disabled)
            {
                // enable TLS as it is support by both parties
                sendData("<starttls xmlns='urn:ietf:params:xml:ns:xmpp-tls'/>");
                return;
            }
        }

        // handle authentication
        const bool nonSaslAvailable = features.nonSaslAuthMode() != QXmppStreamFeatures::Disabled;
        const bool saslAvailable = !features.authMechanisms().isEmpty();
        if (saslAvailable && configuration().useSASLAuthentication())
        {
            // supported and preferred SASL auth mechanisms
            QStringList supportedMechanisms = QXmppSaslClient::availableMechanisms();
            const QString preferredMechanism = configuration().saslAuthMechanism();
            if (configuration().facebookAppId().isEmpty() || configuration().facebookAccessToken().isEmpty())
                supportedMechanisms.removeAll("X-FACEBOOK-PLATFORM");
            if (configuration().windowsLiveAccessToken().isEmpty())
                supportedMechanisms.removeAll("X-MESSENGER-OAUTH2");
            if (configuration().googleAccessToken().isEmpty())
                supportedMechanisms.removeAll("X-OAUTH2");

            // determine SASL Authentication mechanism to use
            QStringList commonMechanisms;
            QString usedMechanism;
            foreach (const QString &mechanism, features.authMechanisms()) {
                if (supportedMechanisms.contains(mechanism))
                    commonMechanisms << mechanism;
            }
            if (commonMechanisms.isEmpty()) {
                warning("No supported SASL Authentication mechanism available");
                disconnectFromHost();
                return;
            } else if (!commonMechanisms.contains(preferredMechanism)) {
                info(QString("Desired SASL Auth mechanism '%1' is not available, selecting first available one").arg(preferredMechanism));
                usedMechanism = commonMechanisms.first();
            } else {
                usedMechanism = preferredMechanism;
            }

            d->saslClient = QXmppSaslClient::create(usedMechanism, this);
            if (!d->saslClient) {
                warning("SASL mechanism negotiation failed");
                disconnectFromHost();
                return;
            }
            info(QString("SASL mechanism '%1' selected").arg(d->saslClient->mechanism()));
            d->saslClient->setHost(d->config.domain());
            d->saslClient->setServiceType("xmpp");
            if (d->saslClient->mechanism() == "X-FACEBOOK-PLATFORM") {
                d->saslClient->setUsername(configuration().facebookAppId());
                d->saslClient->setPassword(configuration().facebookAccessToken());
            } else if (d->saslClient->mechanism() == "X-MESSENGER-OAUTH2") {
                d->saslClient->setPassword(configuration().windowsLiveAccessToken());
            } else if (d->saslClient->mechanism() == "X-OAUTH2") {
                d->saslClient->setUsername(configuration().user());
                d->saslClient->setPassword(configuration().googleAccessToken());
            } else {
                d->saslClient->setUsername(configuration().user());
                d->saslClient->setPassword(configuration().password());
            }

            // send SASL auth request
            QByteArray response;
            if (!d->saslClient->respond(QByteArray(), response)) {
                warning("SASL initial response failed");
                disconnectFromHost();
                return;
            }
            sendPacket(QXmppSaslAuth(d->saslClient->mechanism(), response));
            return;
        } else if(nonSaslAvailable && configuration().useNonSASLAuthentication()) {