JabChapter 7

From WikiContent

(Difference between revisions)
Jump to: navigation, search
Current revision (14:22, 18 September 2009) (edit) (undo)
(content navi added)
 
Line 1: Line 1:
 +
{{Content Programming Jabber}}
=User Registration and Authorization=
=User Registration and Authorization=
<br>With all of Jabber's building blocks at
<br>With all of Jabber's building blocks at

Current revision

Programming Jabber
Preface
1 - Getting started with Jabber
2 - Inside Jabber
3 - Installing the Jabber server
4 - Server Architecture and Configuration
5 - Jabber Technology Basics
6 - Jabber Namespaces
7 - User Registration and Authorization
8 - Using Messages and Presence
9 - Groupchat, : Components, and Event Models
10 - Pointers for Further Development
Appendix A
Appendix B
Colophon

Contents

User Registration and Authorization


With all of Jabber's building blocks at our fingertips, we can now take a look at two fundamental processes that revolve around Jabber users:

  • The registration, or creation, of Jabber user accounts
  • The authentication of a Jabber user account, including the different

authentication methods These processes involve some of the things we learned about in Chapter 5 and Chapter 6, so we'll take a look at them again along the way:

  • The XML stream header
  • The jabber:iq:register namespace
  • The jabber:iq:auth namespace At the end of this chapter, we

will create a small utility program with which we can register users on a server. We can then use this utility program to create users as required for further recipes in this book.


XML Stream Flow


User registration and user authorization are two processes that take place at the start of an XML stream, immediately following the XML stream header exchange. The Jabber server is designed to check for and process any user registration request or authentication request before anything else.

This makes sense, as outside of a session (a session is created for a user when the authentication completes successfully), any other element—a <message/> element, a <presence/> element, or an <iq/> element that doesn't contain an extension qualified by the jabber:iq:register or jabber:iq:auth namespaces—is invalid. So if any of these other elements are sent before authentication has taken place (that is, before a session has been created for that connection), they are queued and processed after authentication.

Figure 7-1 shows the general flow within an XML stream, with regard to where the registration and/or authentication steps happen.


XML stream flow showing registration and authentication


-</code>


User Registration


User registration with a Jabber server specifically means the creation (or modification) of a user account for the JSM component—the component that provides the basic IM services and has a notion of users and user sessions. It takes place at the start of a connection to a Jabber server, as does user authentication, as shown in Figure 7-1.

Let's take a look at the XML fragments involved in a typical user registration process. Example 7-1 shows the XML stream header exchange and the IQ packets in the jabber:iq:register namespace.


A typical user registration process First is the 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='3B2DB1A7' xmlns='jabber:client' from='yak'>


Then the client sends a request to discover what information must be passed to the Jabber server to register a new user:


SEND: <iq type='get'> <query xmlns='jabber:iq:register'/> </iq>

RECV: <iq type='result'> <query xmlns='jabber:iq:register'> <instructions> Choose a username and password to register with this server. </instructions> <name/> <email/> <username/> <password/> </query> </iq>


The client does as asked and sends the required information, which results in a successful new user registration:


SEND: <iq type='set'> <query xmlns='jabber:iq:register'> <username>leslie</username> <password>secret</password> <email>lel@plevna.com</email> <name>Leslie Hawke</name> </query> </iq>

RECV: <iq type='result'/>



Configuration and Module Load Directives


Lets start by reviewing the relevant configuration in jabber.xml. User registration is a JSM feature, and we find two places of interest in that component instance definition. The first is in that instance's configuration, in the section qualified by the jabber:config:jsm namespace:


<register notify="yes"> <instructions> Choose a username and password to register with this server. </instructions> <name/> <email/> </register>


The second is in that instance's connection method, showing the mod_register module being loaded. The module plays a major part in handling the registration process, but there are others, too, as we will see:


<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>


The <register/> configuration section looks familiar; the contents are very similar to the IQ-result returned in response to the IQ-get in the jabber:iq:register namespace in Example 7-1. Indeed, to respond to an IQ-get, the mod_register module looks for this <register/> section and formulates the contents into a reply, making a simple modification as it goes: it appends tags for the two fields <username/> and <password/> as these two are always required, regardless of configuration.

Removing the <register/> section from the configuration effectively blocks the registration process and returns an appropriate error to the user:


SEND: <iq type='get'> <query xmlns='jabber:iq:register'/> </iq>

RECV: <iq type='error'> <query xmlns='jabber:iq:register'/> <error code='501'>Not Implemented</error> </iq>


Removing the reference to the mod_register module from the component instance's connection method:


<!-- <mod_register>./jsm/jsm.so</mod_register> -->


has the same effect.

More information on the configuration discussed here can be found in Section 4.4.


Step by Step


Taking the stream contents in Example 7-1 step by step, what do we see?


The XML stream header exchange


Although the XML declaration that is sent immediately preceding the opening <stream:stream/> root tag is optional, it's not a good idea to leave it out. While the Jabber server currently does not enforce its presence, future implementations may do so.[1] The Jabber server will always send an XML declaration in response. In both cases—in both streamed XML documents—the encoding is assumed to be UTF-8.

For the most part, the rest of the <stream:stream/> root tag is static. The namespace qualifying the stream content is jabber:client (which is the only namespace acceptable when making such an XML stream connection to the c2s—Client-to-Server—component listening on port 5222), and the namespace qualifying the stream itself is fixed at http://etherx.jabber.org/streams.[2]

The only thing that is going to be dynamic is the to attribute, which is used to specify the Jabber server name. Note that this is the logical name, the internal name, of the Jabber server. In our example, we've already resolved the physical hostname, yak, and connected to port 5222; the to attribute is to specify the virtual Jabber host, which in many cases—including an out-of-the-box jabber.xml configuration—has the same name as the physical host.

If our Jabber server has just a single virtual host, we can use an <alias/> configuration tag in the c2s component instance configuration, as described in Section 4.6.3, to remove the requirement of specifying the to attribute.

Using:


<alias to='yak'/>


will set the default alias to yak and indeed override any value specified in the to attribute.


"Required" fields


The list of fields returned in the response to the IQ-get in the jabber:iq:register namespace is actually supposed to be a list of mandatory fields:

  • <name/> * <email/> * <username/> * <password/>

However, with the current version of the Jabber server (1.4.1, which is our reference version for this book), this is not the case.

The <username/> and <password/> fields are mandatory; not supplying them or supplying an invalid username or password will result in an error:


SEND: <iq type='set'> <query xmlns='jabber:iq:register'> <username>leslie</username> <email>lel@plevna.com</email> <name>Leslie Hawke</name> </query> </iq>

RECV: <iq type='error'> <query xmlns='jabber:iq:register'> <username>leslie</username> <email>lel@plevna.com</email> <name>Leslie Hawke</name> </query> <error code='406'>Not Acceptable</error> </iq>


However, currently, failing to supply any of the other fields—the fields that are specified in the <register/> section of the JSM instance's configuration—will not result in an error. This may be fixed in a later release. In any case, it's no great loss; the details are simply stored in the user's spool file on the server, and right now there's only one situation where this information is subsequently used: in answering a browse request made to a user's JID, the server looks up the <name/> tag from the registration data stored and uses the value there in the browse response:


SEND: <iq type='get' to='dj@yak'> <query xmlns='jabber:iq:browse'/> </iq>

RECV: <iq type='result' from='dj@yak' to='sabine@yak/Work'> <user name='DJ Adams' xmlns='jabber:iq:browse' jid='dj@yak'/> </iq>



Set without get


In many of the examples of IQ throughout the book, we've seen a standard pattern: IQ-get -> IQ-result -> IQ-set -> IQ-result. This pattern isn't any different here, but it's not essential. Bearing in mind that registration field requirements aren't going to change that often, and even if they do, the only ones that are enforced are <username/> and <password/>, you can get away with forgoing the IQ-get and cutting straight to the chase with an IQ-set. This isn't a recommendation to do that, merely an observation, as it's always good practice to ask first.


Still no connection


After registering a new user, note that there's still no session. Only after successful authorization (see later in this chapter) is a session created. Although used in authentication (and so implicitly in session creation), the value of the id attribute in the XML stream header returned by the server (which has the value 3B2DB1A7 in Example 7-1) is a connection ID, not a session ID.

So, what can we do at this stage? Well, one of two things: register another user (yes!) or proceed to the authentication stage. Basically, reaching the end of the registration process, we're back where we started; a "raw" connection where only one of two sequences is valid: the jabber:iq:register or jabber:iq:auth sequence.


Passwords


You may be wondering about the plaintext nature of the password sent in the registration process. Although the Jabber server offers different types of password-based authentication, there's a "bootstrap" process required to get the password to the server in the first place. There's currently no way around the fact that the server must at one time receive the password in all its plaintext glory. After receiving it, there are authentication processes that don't use the plaintext password again.

So if you're concerned about the security of this registration phase, consider doing it over a secure (SSL) connection to the server.

We will look at the detail of the different authentication mechanisms later in this chapter; however, it is worth noting here, in the context of the registration process, that the JSM modules that implement the mechanism are responsible for storing the password when it's received. The mod_register module actually registers the user, but it is the mod_auth_plain and mod_auth_0k modules that actually store the password when received.[3]

Passwords are stored on another occasion—when a user wishes to change his password. This procedure is also covered by the jabber:iq:register namespace, albeit in a different context—the context of a session. While a jabber:iq:register-based IQ conversation outside the context of a session is for registering a user, a similar conversation within the context of a session, that is, after a user has authenticated, is used to change the user's password and other registration details. Among other reasons, this is for security: a session context implies the user has identified and authenticated himself, and so has the authority to change the password.

Example 7-2 shows a typical IQ-set to change a password.


Changing a password with jabber:iq:register

SEND: <iq type='set' id='pass_4' to='yak'> <query xmlns='jabber:iq:register'> <password>newsecret</password> </query> </iq>

RECV: <iq type='result' id='pass_4' from='yak' to='dj@yak/Work'/>


The to attribute is required here, to make sure the query is handled by the server itself. We can also see evidence that the context of this exchange is within a session in the value of the to attribute on the IQ-result packet—the JID dj@yak/Work includes a resource suffix, which implies a session (a resource must be specified in the authentication process—see later in this chapter). And the specification of a <username/> is not necessary, as the server will stamp the incoming IQ-set anyway with the JID associated with the user's session.

If you had made an IQ-get, as recommended earlier, before doing the IQ-set to change the password, the result would have looked like this:


RECV: <iq type='result' to='dj@yak/Work' id='pass_2' from='yak'> <query xmlns='jabber:iq:register'> <password/> <instructions> Choose a username and password to register with this server. </instructions> <name/> <email/> <key>9a6957b7f69535274afa5c134fb4d916c5d5c20b</key> <registered/> </query> </iq>


We see that, as in the registration IQ-get outside the session context, the contents of the <register/> section of the JSM instance's configuration have been inserted (the <instructions/>, <name/>, and <email/> tags). Additionally, we have a <key/> tag, as a simple security token (described in Section 6.2.11) and a <registered/> tag. The <key/> is not actually checked in the current implementation and therefore does not have to be supplied in the return IQ-set packet. And the <register/> tag is merely a flag telling us that the user is already registered.

Use of the jabber:iq:register namespace in conversation with the JSM in a session context is not limited to changing passwords; you can modify the rest of the registration details supplied when the user was created—in this case, the <name/> and <email/> information. In fact, with the current implementation, because of the lack of checks, you can specify your own fields in the jabber:iq:register IQ-set, in both contexts. But don't do it; it's a habit that will probably be impossible to keep up in later releases of the server.[4]

It almost goes without saying that because IQs in the jabber:iq:register namespace are handled differently in a session context, you can't register a new user once your session has started; you must end it. To end it, the XML stream must be closed with a </stream:stream>, and a new connection and stream must then be created.


Reversing a user registration


The opposite of registering a user is unregistering a user. This is not the same as removing that user altogether. When the <remove/> tag, described in Section 6.2.11, is used in a jabber:iq:register-qualified IQ-set during a user session, the user is unregistered. That is, all the information held in the user's spool file is removed. But the spool file itself is not removed until the Jabber server is shut down. This means that even if you <remove/> a user, the username will still exist until the server is cycled, causing an error if the same username is used in a new registration attempt:


RECV: <iq type='error'> <query xmlns='jabber:iq:register'> <username>dj</username> <password>secret</password> </query> <error code='409'>Username Not Available</error> </iq>


This may well be fixed in a later release of the Jabber server.


A note on error messages


Various errors can occur during user registration. They are, on the whole, fairly plain and easy to understand. But because of the way the server has been written, you might be surprised at what error message you receive in certain circumstances.

Because the required fields <username/> and <password/> are checked before looking to see whether or not there is a <register/> section in the JSM instance configuration, you will always receive a 406 "Not Acceptable" instead of a 501 "Not Implemented" if you don't supply those fields, even if the registration mechanism has been turned off.

Likewise, if you specify a username that already exists, you will receive a 409 "Username Not Available" instead of a 501 "Not Implemented."

Of course, if you do an IQ-get with the jabber:iq:register namespace beforehand, you will receive the "correct" error—good practice pays!


User Authentication


There are similarities between user registration and user authentication:

  • Authentication must take place outside of a session context (it

doesn't really makes sense inside a session context, anyway). It is perfectly possible to perform a user registration step followed by a user authentication step (for any user) in the same XML stream. * Any packets sent before the authentication step (apart from user registration packets) are queued until after the authentication step has been completed. * The IQ-get in the jabber:iq:auth namespace is not mandatory but is recommended (even more strongly than the recommendation for the IQ-get in the jabber:iq:register namespace). Example 7-3 shows a typical authentication process, including the XML stream header exchange.


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.[5]


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 Section 4.6.3 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) and 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 that 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 Section 4.4.4, they provide different authentication methods, these methods being reflected in their names:

  • mod_auth_plain: plaintext * mod_auth_digest: digest
  • mod_auth_0k: zero-knowledge You have a certain amount of flexibility

as administrator to determine what methods are made available on your Jabber server. If you want to offer all three, do nothing. If you want to offer only the zero-knowledge method, comment out or otherwise remove the other two definitions from the list of module load directives:


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


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 former.

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 to avoid 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 making the comparison. : The algorithm used is
the NIST SHA-1 message digest algorithm.[6]: This algorithm
takes arbitrary input and produces a fingerprint or "message digest"
of it.[7]: 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 connection ID—the value of the id
attribute in the server's XML stream header response that we saw in
Example 7-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 the case where the password is "secret," 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 must be rehashed 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—a single SHA-1 hash
process—is required.
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, actually, 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 server
stores a value that is the password hashed with an arbitrary string
token multiple (N) times, recursively. It doesn't store the
password itself. It also 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 server sends the client the
information it needs in two tags:
<sequence>
The value of N is sent in the <sequence/> tag.
<token>
The arbitrary string token to be hashed with the password is sent in
the <token/> tag. : The N-1 hash value is passed by
the client to the server in the <hash/> tag. : By way
of illustration, Example 7-4 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.

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; }

  1. Initial hash of password

my $hash = sha1_hex($opt_p);

  1. Sequence 0: hash of hashed-password and token

$hash = sha1_hex($hash.$opt_t);

  1. Repeat N-1 times

$hash = sha1_hex($hash) while $opt_s--;

print "$hash\n";

To use the script, pass the data via the parameters, using the values
obtained from the response to the initial IQ-get. The value that the
script produces for the value to send in the <hash/>
tag, like this: :

$ ./0k Usage: 0k -p <password> -t <token> -s <sequence>

$ ./0k -p secret -t ABCDE -s 400 01945b9b2c3207c4cce5dc99e50605779f570077 $

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 reset, as it must be passed in plaintext. A reset
is required when N reaches 0, which is another disadvantage—a
password can be used only N times before a plaintext reset.


Choosing the Authentication Method


Now that we know a little bit about the authentication methods, let's jump back to the initial IQ-get query in Example 7-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 does the same with the <digest/> flag, and mod_auth_0k inserts the <sequence/> and <token/> tags and values, which is what the client will need if it 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 without the <password/> or <digest/> tags:


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


{{Note|At the beginning of this section, we strongly recommended using the IQ-get in the jabber:iq:auth namespace, 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.

</code>


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.


User Registration Script


Currently, the Jabber technology has no concept of anonymous users—users who can connect to the server with no previous registration requirement. Until this is possible, we have to make do with creating specific users for specific scenarios.

To this end, it would be useful to be able to run a quick script to register a new user, rather than grab an existing client, start it up, and go through the process of registering a new user with the Jabber server, with whatever window navigation and mouse clicking that might entail. All the script must do is interact with the Jabber server in the context of the jabber:iq:register namespace, specifically, pre-session. It must be able to make a registration inquiry by sending an IQ-get and returning the fields listed in the result and to make a registration attempt, when supplied with values for the registration fields.

We should be able to invoke our script, reguser, in one of two ways. The first, specifying merely a hostname (and optional port, which will default to 5222 if not specified), implies a registration inquiry, that we wish to make an inquiry of the Jabber server as to (a) whether registration is possible and (b) if so, what fields are required. The second way, specifying not only the host and optional port but also a list of field name and value pairs, implies a registration attempt. Example 7-5 shows both these ways in action.


Uses of the reguser script

$ ./reguser yak:5222 [Enquiry] Fields: username password email name

$ ./reguser yak username=joseph password=spinach 'name=Joseph Adams' email=joseph@yak [Attempt] (joseph) Successful registration

$ ./reguser yak username=dj password=secret 'name=DJ Adams' email=dj@yak [Attempt] (dj) Error: 409 (Username Not Available) As it's our first substantial script, let's take it step by step. It's written in Perl and uses the Net::Jabber module:


#!/usr/bin/perl

use strict; use Net::Jabber 1.0022 qw(Client);

use constant NS_REGISTER => 'jabber:iq:register';

unless (@ARGV) { print usage(); exit; }


We start out with some basic start-of-script housekeeping: delaring our usage of Net::Jabber, setting a constant for the jabber:iq:register namespace, and handling the case of being "wrongly" invoked by giving some help text from the usage() subroutine. The specification of Client in the line:


use Net::Jabber 1.0022 qw(Client);


means that the connection is going to be client-based; in other words, the namespace that will be used to qualify the XML stream header that Net::Jabber will produce is jabber:client.

The Net::Jabber module has changed as it has matured over the recent versions (1.0020, 1.0021, and 1.0022), and these changes do sometimes affect how the scripts that use Net::Jabber should be written. So we explicitly specify in the use statement which version of Net::Jabber we require, to avoid confusion.


my ($host, $port) = split(":", shift @ARGV); $port ||= 5222;

my $c = Net::Jabber::Client->new();

defined($c->Connect( hostname => $host, port => $port, )) or die "Cannot reach Jabber server at $host:$port\n";

my ($iq, $query, $result);


We parse the hostname and port, defaulting the latter to 5222 if it wasn't specified. Then we create a new instance of the Net::Jabber::Client object. The Net::Jabber family of modules presents its collective functions in an object-oriented way. The scalar $c represents the "client" mechanism with which we connect to the Jabber server.

With the Connect() method, we make a connection to the Jabber server; the namespace of the XML stream for this connection, sent to the Jabber server in the stream header, is jabber:client.


# Registration attempt or inquiry?

if (scalar @ARGV) {

 # Attempt: Send <iq type='set'> <query
 # xmlns='jabber:iq:register'> <username>...</username>
 # <password>...</password> ... </query> </iq>
 print "[Attempt] ";
 $iq = Net::Jabber::IQ->new(); $iq->SetType('set'); $query =
 $iq->NewQuery(NS_REGISTER);


We work out what we have to do by looking to see if any extra parameters beyond the hostname and port were specified. If there were, we need to build an IQ-set in the jabber:iq:register namespace to make a registration attempt.

The Net::Jabber::IQ module represents the IQ model and provides methods to manage IQ packets. With the new() constructor, we create a new, empty IQ packet in $iq, and set its type attribute to set.

As we know, the <query/> part of an IQ packet is contained within the <iq/> tag. The NewQuery() method, called on an IQ packet, creates a <query/> tag as a child of that IQ packet and delivers us a handle on that <query/> tag—which we store in $query—so that we can manipulate it independently of the IQ packet that wraps around it. The jabber:iq:register namespace value is passed as a parameter to the NewQuery() call to set the correct xmlns namespace attribute.

Figure 7-2 shows what the packet looks like at this stage and how the scalar object references $iq and $query relate to it.


An IQ packet under construction by Net::Jabber::IQ


</code> In our foreach loop, we run through the list of parameters, in the form fieldname=value, and call a Set method on the $query object (the <query/> packet) to add child tags:


foreach my $arg (@ARGV) { my ($field, $value) = split('=", $arg); print "($value) " if $field eq 'username'; eval '$query->Set'.ucfirst($field).'($value)';

 }
 $result = $c->SendAndReceiveWithID($iq);


Net::Jabber::Query provides a number of SetXXXX methods that are available according to namespace. These "set" methods available for the jabber:iq:register namespace are plentiful and include SetName, SetEmail, SetPhone, and so on. Each method will create a child tag named after the method (e.g., SetName will create a <name/> tag, and SetPhone will create a <phone/> tag) and insert the value passed to the method into the tag.

For example:


$query->SetName('DJ Adams');


will insert (or amend) a tag in the <query/> thus:


<iq type='set'> <query xmlns='jabber:iq:register'> <name>DJ Adams</name> </query> </iq>


We use eval to allow us to make our SetXXXX method calls dynamically, according to each field name specified. The ucfirst() function is used to change the first character of the field name to uppercase, to suit the SetXXXX method naming conventions.

Once we've added all the fields, we send the complete packet ($iq) to the server using the SendAndReceiveWithID() method on the connection ($c) object. This method is extremely powerful and does many things for us. It keeps the process of writing small scripts like this very simple. It adds a unique id attribute to the <iq/> packet, transmits the packet over the XML stream, and waits for a response.

"Hey, what about the event model that we read about?" you might ask. Of course, Net::Jabber supports an event-model programming style, but for now we can get away with keeping our code "procedural" (and short) using this high-level method that does everything we want. After all, in any one execution of the script, we wish to send only one packet to the Jabber server and receive one back. Nothing more complicated than that.

Recipes in later chapters will demonstrate the event model.

The response is stored in $result and is itself an IQ packet, as we expect. So $result is a handle on a Net::Jabber::IQ object that we can now manipulate:


# Success if ($result->GetType() eq 'result') { print "Successful registration\n";

 }
 # Failure
 else { print "Error: ", $result->GetErrorCode(), " (",
 $result->GetError(), ")\n";
 }

}


We check the type of the IQ returned from the server. If it's a result:


RECV: <iq type='result' id='1'> <query xmlns='jabber:iq:register'/> </iq>


then great—the registration was successful. Otherwise, we can grab the error code and description from the <error/> element:


RECV: <iq type='error'> <query xmlns='jabber:iq:register'> <username>dj</username> <password>secret</password> </query> <error code='409'>Username Not Available</error> </iq>


using the GetError() and GetErrorCode() methods on the IQ object.

We go through a similar process if there are no further parameters following the host[:port] specification:


else {

 # Inquiry: Send <iq type='get'><query
 # xmlns='jabber:iq:register'/></iq>
 print "[Inquiry] ";
 $iq = Net::Jabber::IQ->new(); $iq->SetType('get'); $query =
 $iq->NewQuery(NS_REGISTER);
 $result = $c->SendAndReceiveWithID($iq);


The only difference here is that we set the IQ type to get, not set, and we don't insert any tags into the $query object, before sending the packet off and waiting for a response:

If we receive a result type, like this:


RECV: <iq type='result'> <query xmlns='jabber:iq:register'> <instructions> Choose a username and password to register with this server. </instructions> <name/> <email/> <username/> <password/> </query> </iq>


then we need to extract the fields listed in the <query/> tag and return them to the user.


# Success if ($result->GetType() eq 'result') { $query = $result->GetQuery();

   my %contents = $query->GetRegister(); delete
   $contents{'instructions'}; print "Fields: ", join(', ', keys
   %contents), "\n";
 }


While the NewQuery() method creates a new <query/> tag inside an IQ object, the GetQuery() method retrieves an existing one, in the form of a Net::Jabber::Query object whose handle we store in $query. We can call the GetRegister() method on this query object, which returns a hash of the contents:


( 'instructions' => 'Choose a username and password ...', 'name' => undef, 'email' => undef, 'username' => undef, 'password' => undef )


And, after removing the "instructions," we can display them as the result.

When an error is returned in response to the IQ-get (perhaps no registrations are allowed), we display the error in the same way as before:


# Failure else { $query = $result->GetQuery(); print "Error: ", $result->GetErrorCode(), " (", $result->GetError(), ")\n";

 }

}


When we've finished, we close the connection and exit. Here we also have the usage() subroutine defined:


$c->Disconnect;

exit;


sub usage {

<<EOF Usage: Enquiry: reguser host[:port] Attempt: reguser host[:port] field1=value1 [fieldN = valueN] ... EOF

}



Using the Script


The script is very basic, but it gets the job done. It is suitable for calling from another script, for mass user generation, although you may wish to modify it so that a connection is not created and destroyed for every username that needs to be registered.[8]

It also illustrates how simple a Jabber client can be. In this case, the Net::Jabber libraries mask the bulk of the effort (socket connection, XML stream negotiation, XML fragment traffic management, and so on). We'll be making use of this script to create users for our recipes later on in the book.

Personal tools