User Authentication

There are similarities between user registration and user authentication:

Example 6-3 shows a typical authentication process, including the XML stream header exchange.

Example 6-3. A typical user authentication process

Here the authentication process immediately follows the initial XML stream header exchange:

SEND: <?xml version='1.0'?>
      <stream:stream to='yak' xmlns='jabber:client'
                     xmlns:stream='http://etherx.jabber.org/streams'>

RECV: <?xml version='1.0'?>
      <stream:stream xmlns:stream='http://etherx.jabber.org/streams'
                     id='1ED34A55' xmlns='jabber:client' from='yak'>

We ask the server about the authentication methods available for our specific user:

SEND: <iq type='get'>
        <query xmlns='jabber:iq:auth'>
          <username>dj</username>
        </query>
      </iq>

RECV: <iq type='result'>
        <query xmlns='jabber:iq:auth'>
          <username>dj</username>
          <password/>
          <digest/>
          <sequence>496</sequence>
          <token>3B2DEEC0</token>
          <resource/>
        </query>
      </iq>

Because we're connecting here to the server with telnet and don't have any digest utilities handy, we decide to use the simplest authentication method, and send our password in plaintext. The server checks the credentials, and gives us the thumbs up.

SEND: <iq type='set'>
        <query xmlns='jabber:iq:auth'>
          <username>dj</username>
          <password>secret</password>
          <resource>laptop</resource>
        </query>
      </iq>

RECV: <iq type='result' id='pthsock_client_auth_ID'/>

At this stage, we have a session. [1]

Configuration and Module Load Directives

The c2s component contains a configuration directive related to the authorization process:

<service id="c2s">
  <load>
    <pthsock_client>./pthsock/pthsock_client.so</pthsock_client>
  </load>
  <pthcsock xmlns='jabber:config:pth-csock'>
    <alias to='yak'/>
    <authtime/>
    ...
  </pthcsock>
</service>

This <authtime/> tag is used to set the time limit, in seconds, within which authentication should be completed, starting to measure at the time the connection was made. See the section called Custom Configuration in Chapter 4 for more details.

There is also an undocumented tag <auth/> which can be specified in the JSM instance configuration (for example after the <register/> section) with which you can specify an external component that is to handle authentication in place of the standard JSM modules (mod_auth_*).

The JSM module load directives specify the modules which are to handle authentication:

<load main="jsm">
  <jsm>./jsm/jsm.so</jsm>
  <mod_echo>./jsm/jsm.so</mod_echo>
  <mod_roster>./jsm/jsm.so</mod_roster>
  <mod_time>./jsm/jsm.so</mod_time>
  <mod_vcard>./jsm/jsm.so</mod_vcard>
  <mod_last>./jsm/jsm.so</mod_last>
  <mod_version>./jsm/jsm.so</mod_version>
  <mod_announce>./jsm/jsm.so</mod_announce>
  <mod_agents>./jsm/jsm.so</mod_agents>
  <mod_browse>./jsm/jsm.so</mod_browse>
  <mod_admin>./jsm/jsm.so</mod_admin>
  <mod_filter>./jsm/jsm.so</mod_filter>
  <mod_offline>./jsm/jsm.so</mod_offline>
  <mod_presence>./jsm/jsm.so</mod_presence>
  <mod_auth_plain>./jsm/jsm.so</mod_auth_plain>
  <mod_auth_digest>./jsm/jsm.so</mod_auth_digest>
  <mod_auth_0k>./jsm/jsm.so</mod_auth_0k>
  <mod_log>./jsm/jsm.so</mod_log>
  <mod_register>./jsm/jsm.so</mod_register>
  <mod_xml>./jsm/jsm.so</mod_xml>
</load>

Each of these modules - mod_auth_plain, mod_auth_digest and mod_auth_0k - can play a role in the authentication process. As mentioned in the section called Component Connection Method in Chapter 4, they provide different authentication methods, these methods being reflected in their names:

There's a certain amount of flexibility you have as administrator to determine what methods are made available on your Jabber server. If you want to offer all three, do nothing. If you only want to offer the zero-knowledge method, comment out or otherwise remove the other two definitions

<!--
  <mod_auth_plain>./jsm/jsm.so</mod_auth_plain>
  <mod_auth_digest>./jsm/jsm.so</mod_auth_digest>
-->

from the list of module load directives. If you want to offer the digest method, you must include the module load directives for both mod_auth_plain and mod_auth_digest, as the latter is merely an extension - a 'parasite' - upon the other.

Let's look at each of these authentication methods in turn.

Plaintext Authentication Method

The plaintext authentication method works as you would expect, and is the default 'lowest common denominator' method supplied with the Jabber server. It is provided by the mod_auth_plain module.

Method

The password is transmitted in the XML stream, inside the <password/> tag in the jabber:iq:auth IQ set packet, from the client to the server in plaintext, where it is compared to the password stored, also in plaintext, on the server.

When a password is changed, using a jabber:iq:register IQ set as described earlier in this Chapter, mod_auth_plain stores the password, as received, in the user's spool file.

Advantages

This method is by far the simplest to implement on the client side. It is also useful for debugging and testing purposes as it can be used in a connection 'by hand' via telnet, not requiring any extra computation such as the digest and zero-knowledge methods do.

Disadvantages

It's insecure, on two levels. First, the password is transmitted in plaintext across the wire from client to server. The risk can be minimized by encrypting the whole connection using SSL. Second, the password is stored in plaintext on the server, which may be compromised.

Digest Authentication Method

The module that provides the digest authentication method, mod_auth_digest, works in conjunction with the plaintext module mod_auth_plain. It provides a way of avoiding having to send the plaintext password across the wire.

Method

The digest method is similar to the plaintext method, in that the password sent by the client is compared to the password stored on the server. However in this case, the password is first encoded using a hashing algorithm. It is encoded by the client before being sent across the wire, and it is encoded by the server (having retrieved it in plaintext) before being making the comparison.

The algorithm is the NIST SHA-1 message digest algorithm. [2] This algorithm takes arbitrary input and produces a 'fingerprint' or message digest of it. [3] A random string, shared between the client and the server, is appended to the password before being passed to the hashing algorithm. This random string is the value of the id attribute in the server's XML stream header response that we saw in Example 6-3:

RECV: <?xml version='1.0'?>
<stream:stream xmlns:stream='http://etherx.jabber.org/streams'
  id='1ED34A55' xmlns='jabber:client' from='yak'>

which means, in this case, the string that will be hashed is

secret1ED34A55

which is

03ea09f012493415908d63dcb1f6dbdb9bfc09ba

The digested password is transmitted to the server inside the <digest/> tag.

mod_auth_digest is unlike the other two modules in that it doesn't take any responsibility for storing passwords; it leaves that to mod_auth_plain, as the plaintext password is needed to be re-hashed with a new random suffix each time.

Advantages

No plaintext password is transmitted across the wire from client to server, and only a small amount of computation is required - a single SHA1 hash process.

Disadvantages

The password is still stored in plaintext on the server.

Zero-Knowledge Authentication Method

The zero-knowledge authentication method is so called as the server requires no knowledge of the password in order to check the credentials. It makes use of the same hashing algorithm used in the digest authentication method.

Method

Just as mod_auth_plain is responsible for storing the password (in plaintext) when a user is created or when the password is changed, so mod_auth_0k is responsible for storing its version of the password, or in fact the information it needs (originally based on the password) to check the client's credentials in a zero-knowledge authentication process.

As we know from the user registration and password change processes, any new password is supplied to the server in plaintext. This is where a secure (SSL) connection is critical for complete security. While the mod_auth_plain module just stores that password as-is, the mod_auth_0k module stores a sequenced hash of the password instead.

What does this mean?

The idea is this: the server stores a value which is the password hashed with an arbitrary string token multiple (N) times, recursively. It doesn't store the password itself. It remembers how many times it has been hashed (N).

Whenever a client wants to authenticate, the server sends the client the string token, and the value of N. The client, having obtained the password from the user, performs the same iterative hashing sequence that the server performed when it was originally given the password, but performs the sequence N-1 times. It passes the result of this sequence to the server, which does one more hash to go from N-1 to N, and compares what it gets with what it has stored. If they match, authentication is successful, and the server stores the N-1 hash passed from the client and decrements N, ready for next time.

The N-1 hash value is passed by the client to the server in the <hash/> tag.

By way of illustration, Figure 6-2 shows a small Perl script that a telnet user can use to make the necessary hashing computations when wishing to authenticate with a Jabber server using the zero-knowledge method.

Figure 6-2. A script implementing the client-side zero-knowledge process

#!/usr/bin/perl -w

use strict;
use Digest::SHA1 qw(sha1_hex);
use Getopt::Std;

our($opt_p, $opt_t, $opt_s);
getopt('pts');

unless ($opt_p and $opt_t and $opt_s)
{
  print "Usage: $0 -p <password> -t <token> -s <sequence>\n";
  exit;
}

# Initial hash of password
my $hash = sha1_hex($opt_p);

# Sequence 0: hash of hashed-password and token
$hash = sha1_hex($hash.$opt_t);

# Repeat N-1 times
$hash = sha1_hex($hash) while $opt_s--;

print "$hash\n";

Pass the data to the script via the parameters, using the values obtained from the response to the initial IQ get. Use the value that the script produces for the value to send in the <hash/> tag.

Advantages

No password is stored on the server. No (plaintext) password is transmitted from client to server.

Disadvantages

This method is slightly more compute-intensive than the other methods. There is still a security weak spot in the procedure, when the password is set or re-set (required when the counter N reaches zero), as it must be passed in plaintext.

Choosing the Authentication Method

Now we know a little bit about the authentication methods, let's jump back to the initial IQ get query in Example 6-3:

SEND: <iq type='get'>
        <query xmlns='jabber:iq:auth'>
          <username>dj</username>
        </query>
      </iq>

RECV: <iq type='result'>
        <query xmlns='jabber:iq:auth'>
          <username>dj</username>
          <password/>
          <digest/>
          <sequence>496</sequence>
          <token>3B2DEEC0</token>
          <resource/>
        </query>
      </iq>

What we're actually seeing here is the result of the authentication modules announcing their readiness to authenticate the user dj. The query is passed to each of the modules. mod_auth_plain announces its readiness by inserting the <password/> flag, mod_auth_digest with the <digest/> flag, and mod_auth_0k inserts the <sequence/> and <token/> tags and values, which is what the client will need it if wishes to authenticate using the zero-knowledge method. The <resource/> tag, which is required in any authentication, is finally added before the result is returned.

This way, the IQ result can convey which authentication methods are available - if the mod_auth_plain and mod_auth_digest modules were to be commented out in the module load directive list, as we saw earlier, then the IQ result would look like this:

RECV: <iq type='result'>
        <query xmlns='jabber:iq:auth'>
          <username>dj</username>
          <sequence>496</sequence>
          <token>3B2DEEC0</token>
          <resource/>
        </query>
      </iq>

that is, without the <password/> or <digest/> tags.

At the beginning of this section on User Authorization, we noted that the IQ get in the jabber:iq:auth namespace was strongly recommended, before proceeding with the IQ set. This was primarily to be able to check what authentication methods were supported. However, as you can see here, it is in fact essential in the case of the zero-knowledge authentication method, because without the <sequence/> and <token/> information, we cannot begin our hashing sequence.

Password Errors and Retries

If you don't supply all the required parameters, you are notified with a 406 "Not Acceptable" error:

SEND: <iq type='set'>
        <query xmlns='jabber:iq:auth'>
          <username>dj</username>
          <digest>03ea09f012493415908d63dcb1f6dbdb9bfc09ba</digest>
        </query>
      </iq>

RECV: <iq type='result' id='pthsock_client_auth_ID'>
        <query xmlns='jabber:iq:auth'>
          <username>dj</username>
          <digest>03ea09f012493415908d63dcb1f6dbdb9bfc09ba</digest>
        </query>
        <error code='406'>Not Acceptable</error>
      </iq>

In this case, no <resource/> value was supplied. The <resource/> value is required, as it is the key part of the JID that is used to distinguish between multiple connections as the same user on the same Jabber server.

If you get the password wrong, you receive a 401 "Not Authorized" error. There is currently no limit on the number of times an authorization attempt can be made, but the <karma/> limits for the c2s connections (see Chapter 5) may slow the attempts down.

Notes

[1]

The 'pthsock_client_auth_ID' value for the id attribute is placed by the JSM as it processes the IQ set request, in cases where a value has not been specified.

[2]

More information on SHA-1 - Secure Hash Algorithm 1, can be obtained at http://www.itl.nist.gov/fipspubs/fip180-1.htm.

[3]

There are different types of message digest that can be produced with this algorithm - binary, hexadecimal and base64. The hexadecimal format is used here and elsewhere in Jabber.