SMACK bug that prevents Spark to authenticate(SPARK-751)

Hi,

In cases when XMPP server’s xmpp.domain value is different that xmpp.fqdn, Spark cannot login

Here is the comment that I posted for resolution of SPARK-751

The core problem lies in the SMACK API.
I took a look over the latest sources and I found that SASLAuthentication.java class (that plays the role of a SASL Authentication Mechanisms register) tries to authenticate using connection.getServiceName() instead of using connection.getHost()

As per XMPPConnection API doc, getServiceName returns the xmpp.domain value, which may be different than xmpp.fqdn when SRV records are used.
SASLAuthentication.authenticate(String username, String password, String resource) method contains the following:

// Trigger SASL authentication with the selected mechanism. We use
// connection.getHost() since GSAPI requires the FQDN of the server, which
// may not match the XMPP domain.
currentMechanism.authenticate(username, connection.getServiceName(), password);

The comment says that it should use conection.getHost() - but connection.getServiceName() is passed instead

I changed that into connection.getHost() on a local build, tested and works just fine

IMO, this should be fixed, because it prevents SPARK to authenticate against servers where SRV records are used

I also noticed that other “authenticate” methods use connection.getHost(). Here should be done the same. Someone probably forgot to update this method too

Thanks,

Mircea

Logged as SMACK-344. Can you supply your fix as a .patch file please. You can attach it this thread and I will attach it to the Jira task.

Sure

Please find attached - its a very simple patch

Thanks

Mircea
SMACK-344.patch.zip (479 Bytes)

Looking at the commit log, it seems that the call was explicitly changed from getHost() to getServiceName(): http://fisheye.igniterealtime.org/changelog/smack?cs=10844

Maybe some SASL Methods need the FQDN host and some the service name. Just make sure that you don’t break something else by applying the patch.

Flow

Looks like afix for a fundamental Spark issue Spark-751. What would be the standard conformat behaviour that we expect from Smack?

I am posting a new patch. I used DNSUtil.resolveXMPPDomain(domain) to retrieve the true FQDN given the domain name.

This method performs DNS SRV queries. The previous patch didn’t work when you use the IP. Please consider this new patch when fixing SMACK-344 ticket instead of the previous one.
SMACK-344-GOOD.patch.zip (767 Bytes)

added patch to SMACK-344

Mircea, your patch breaks login at igniterealtime.org. We have compiled an experimental Smack to get IBB working and included 344-good in it. This gives an SASL authentication error when I try to logon igniterealtime.org. Sorry for the bad news.

no pb - thanks for trying it out

I made a test program where I called DNSUtil.resolveXMPPDomain(“igniterealtime.org”) and it returned to me

xmpp.igniterealtime.org - which is correct, I think. This is the true location of XMPP.

So basically it should get sent to SASL the value xmpp.igniterealtime.org which I think it is correct - so I don’t understand why is not working…

I would love to debug the problem…I am wondering if there is any test account available just for testing…

AFAIK you should set the SASL domain to ingiterealtime.org (or the domain part of the JID to be excact), because the DNS SRV lookup just returns the host on which the XMPP Server runs. This could be anything like xmpp-serv.otherprovider.com and has nothing to do with the SASL realm.

See also my previous post.

Flow

I think that the trick is how to create the SASL client. I noticed that SMACK uses Java JDK impmenentation for creating SASL client:

in Smack’s SASLMechanism.java:

sc = Sasl.createSaslClient(mechanisms, username, “xmpp”, host, props, this);

Reading on the JDK documentation about sasl:

http://download.oracle.com/javase/6/docs/api/javax/security/sasl/Sasl.html

they say that for “host” parameter we should send:

“The non-null fully-qualified host name of the server to authenticate to”

Smack sends connection.getServiceName() which basically represents the XMPP domain name - but the doc says to send FQDN - and the only thing that the patch does is to retrieve the true XMPP location using DNSUtil.resolveXMPPDomain(domainName) to retrieve it.

Hi

I’ll do a double check on the patch and report the debug log.

Walter

Your can create an account via the spark button “Account” in the login screen :-).

nice - thanks

I created account: mirceac with password: 123

I made a fresh install of spark-2.6.3 and I copied jars from http://community.igniterealtime.org/blogs/ignite/2011/09/02/smack-322-beta-fixes -ibb-transfer

I succesfully logged in using mirceac/123 and server: igniterealtime.org and also with: xmpp.igniterealtime.org. Can you also please try with this account and let me know if this is working for you

Thanks,

Mircea

Hi Mircea,

the current Smack alpha does not include 344-good. :slight_smile:

Walter

Walter,

I made a debug on smack code using igniterealtime.org as server and I think I found the culprit.

Flow is right, the fqdn has nothing to do with SASL realm, but smack is using as SASL realm the hostname - and here is the problem:

When the client sends back the response, given the server’s chalenge, the client has to use the realm that the server sent in the challenge. Smack puts there the hostname (actually the domain name, they send connection.getServiceName in the original jar) instead - so when the realm is different than the “hostname”, authentication will allways be broken.

My patch broke the authentication because was sending xmpp.igniterealtime.org, but the REALM is igniterealtime.org.

I will prepare a new patch tomorrow

Nice SASL work-flow explanation here:

http://web.archive.org/web/20050224191820/http://cataclysm.cx/wip/digest-md5-cra sh.html

And here is what is interesting for us:

"realm (zero or one occurence)

The authentication realm that this user’s account is in. This isrequired if the server specified realms in the first challenge,and should be set to one of those realms. If this is missing,it will be set to the empty string."

Wow, how did you find the SASL explanation and many thanks for the revised patch.

Well, I was using Google and probably I was lucky

Anyway, I am almost ready with the revised patch and I found another issue: “digest-uri” which is part of the authentication response that the client sends to the server.

In order for the authentication to be succesfull, the digest-uri sent by the client should match with the digest-uri that is created at server level

From RFC-2831:

  • digest-uri = “digest-uri” “=” digest-uri-value

  • digest-uri-value = serv-type “/” host [ “/” serv-name ]

  • digest-uri:

  • Indicates the principal name of the service with which the client

  • wishes to connect, formed from the serv-type, host, and serv-name.

  • For example, the FTP service

  • on “ftp.example.com” would have a “digest-uri” value of “ftp/ftp.example.com”; the SMTP

  • server from the example above would have a “digest-uri” value of

  • “smtp/mail3.example.com/example.com”.

  • host:

  • The DNS host name or IP address for the service requested. The DNS host name

  • must be the fully-qualified canonical name of the host. The DNS host name is the

  • preferred form; see notes on server processing of the digest-uri.

  • serv-name:

  • Indicates the name of the service if it is replicated. The service is

  • considered to be replicated if the client’s service-location process involves resolution

  • using standard DNS lookup operations, and if these operations involve DNS records (such

  • as SRV, or MX) which resolve one DNS name into a set of other DNS names. In this case,

  • the initial name used by the client is the “serv-name”, and the final name is the “host”

  • component. For example, the incoming mail service for “example.com” may be replicated

  • through the use of MX records stored in the DNS, one of which points at an SMTP server

  • called “mail3.example.com”; it’s “serv-name” would be “example.com”, it’s “host” would be

  • mail3.example.com”. If the service is not replicated, or the serv-name is identical to

  • the host, then the serv-name component MUST be omitted

So basically when we have serv-name (domain) different than the true host (the SRV/MX record value for xmpp) the

digest-uri has to contain both

For example in case of igniterealtime.org we have serv-name = igniterealtime.org and host=xmpp.igniterealtime.org

digest-uri has to be = xmpp/xmpp.igniterealtime.org/igniterealtime.org

I looked over Openfire code and looks like it does not respect RFC-2831 gudelines on digest-uri

Openfire does not perform DNS SRV lookup. With current smack/spark code when OF server name is different than OF domain name, authentication will never work, no matter what user introduces for host in the login dialog (spark always sends connection.getServiceName to authenticate, instead of serverName)

here is how the SASL Server instance is created:

SaslServer ss = Sasl.createSaslServer(mechanism, “xmpp”,

JiveGlobals.getProperty(“xmpp.fqdn”, session.getServerName()), props,

new XMPPCallbackHandler());

That inside JDK code will become digest-uri=xmpp/JiveGlobals.getProperty(“xmpp.fqdn”, session.getServerName())

I don’t know what other XMPP servers are doing, maybe others do follow exact RFC-2831 guidelines, but we have two options:

  1. simply use in spark whatever user introduces for host in the login dialog (the user is must put the xmpp.fqdn value (OF server name for cases when spark authenticates against openfire))

2.Patch openfire with true digest-uri vaulue by using DNS SRV lookup to retrieve true hostname and to compute correct server digest-uri. In Spark do the same thing

String trueHostName = DNSUtil.resolveXMPPDomain(connection.getServiceName()).getHost()

make sure to provide value xmpp/trueHostname/connection.getServiceName() for digest-uri)

The user can use domain name in the login dialog or server name as well

I would vote for solution 1, but servers that implement true digest-uri values won’t be able to authenticate with spark (they cannot authenticate with the current spark/smack code anyway)

Opinions are welcomed

Thanks,
Mircea

Hi Mircea,

I would go for 1). Patching Openfire is one part, releasing OF another one. I do not see any release of 3.7.1 soon and we have to keep the “legacy” supported, even if it is wrong. We just had the same in Smack with respect to file transfer. The “standard” tells use “list-single” for transfer options, but it breaks Spark 2.5.8…

Kind regards

Walter

Actually, the problem with SPARK-751 does not resides in SMACK only (the problem with realm), but in OPENFIRE as well.

SMACK use for digest-uri whatever receives from OPENFIRE:

PacketReader.java

else if (parser.getAttributeName(i).equals(“from”)) {

// Use the server name that the server says that it is.

connection.config.setServiceName(parser.getAttributeValue(i));

}

Here is how Openfire calculates the “from” tag:

StanzaHandler.java

private String geStreamHeader() {

StringBuilder sb = new StringBuilder(200);

sb.append("<?xml version=‘1.0’ encoding=’");

sb.append(CHARSET);

sb.append("’?>");

if (connection.isFlashClient()) {

        sb.append("<flash:stream xmlns:flash=\"[http://www.jabber.com/streams/flash](http://www.jabber.com/streams/flash)\" ");

}

else {

sb.append("<stream:stream ");

}

    sb.append("xmlns:stream=\"[http://etherx.jabber.org/streams](http://etherx.jabber.org/streams)\" xmlns=\"");

sb.append(getNamespace());

sb.append("" from="");

sb.append(serverName);

sb.append("" id="");

sb.append(session.getStreamID());

sb.append("" xml:lang="");

sb.append(connection.getLanguage());

sb.append("" version="");

sb.append(Session.MAJOR_VERSION).append(".").append(Session.MINOR_VERSION);

sb.append("">");

return sb.toString();

}

Looking at:

sb.append("" from="");

sb.append(serverName); - serverName represents session.getServerName() - which may be different than xmpp.fqdn

On the other hand, Openfire creates the SASL Server like this:

Openfire’s SASLAuthentication.java

SaslServer ss = Sasl.createSaslServer(mechanism, “xmpp”,

JiveGlobals.getProperty(“xmpp.fqdn”, session.getServerName()), props,

new XMPPCallbackHandler());

The value JiveGlobals.getProperty(“xmpp.fqdn”, session.getServerName()) is used for creating the digest-uri at server level, but it does not match with the value sent to the client (smack) for creating the client digest-uri.

There is no reason to use JiveGlobals.getProperty(“xmpp.fqdn”, session.getServerName()) , we can use only session.getServerName(), even if it does not have the exact form of RFC-2831 - but server’s digest-uri must be the same with what is sent to the client in the “form” tag

So solution 1) looks more like a hack just to workaround Openfire bug and limits the power of host discovery provided in Spark/Smack. With solution 1 for instance, we cannot never login using IP in login Dialog, we will be restricted to use xmpp.fqdn value

I will post two patches:

1 Patch for smack that fixes the realm issue and separates the meaning of hostname from serviceName - hostname is what smack discovered and serviceName is what openfire sent to the client as the true location of xmpp server to connect to

2.A patch for openfire that synchronizes what is used for creating the SASL server in order to create the server’s digest-uri, and what is sent to the client in the “form” tag

there are to ways to synchronize:

a) one way is to use only session.getServerName() instead of JiveGlobals.getProperty(“xmpp.fqdn”, session.getServerName()) when SASL server is created (the Patch: SPARK-751-SMACK-344-OPENFIRE.patch)

b) other way is vice-versa, to use JiveGlobals.getProperty(“xmpp.fqdn”, serverName) instead of serverName in the StanzaHandler for the “from” tag value creation (the Patch: SPARK-751-SMACK-344-OPENFIRE-2.patch)

Whenever it is decided to update openfire - at least there is a patch for this issue and can be used

And as a second step - lets think a bit more if solution 1) is what we need…

Mircea
SPARK-751-SMACK-344-OPENFIRE-2.patch.zip (462 Bytes)
SPARK-751-SMACK-344.patch.zip (3112 Bytes)
SPARK-751-SMACK-344-OPENFIRE.patch.zip (553 Bytes)