SIP peers external authentication in Asterisk / OpenPBX

Philippe Sultan

Revision History
Revision $Revision: 1.10 $$Date: 2006/03/22 18:42:57 $

Table of Contents
1. Copyright and license
2. Introduction
3. Simple registration process
4. Using RADIUS as an external authentication source
4.1. Configuring FreeRADIUS for digest authentication
4.1.1. Storing passwords in an encrypted form in FreeRADIUS
4.2. Chosing a RADIUS client API
4.2.1. pam_radius module
4.2.2. radiusclient-ng
4.3. Asterisk
4.4. OpenPBX
5. LDAP authentication through RADIUS
5.1. LDAP Alone
5.2. LDAP through RADIUS
6. GNU Free Documentation License

1. Copyright and license

Copyright (c) 2005 Philippe Sultan, INRIA. Permission is granted to copy, distribute and/or modify this document under the terms of the GNU Free Documentation License, Version 1.2 or any later version published by the Free Software Foundation; with no Invariant Sections, no Front-Cover Texts, and no Back-Cover Texts. A copy of the license is included in the section entitled "GNU Free Documentation License".


2. Introduction

We set up a telephony service for remote users using software SIP clients for users, Asterisk as an IPBX, a SIP enabled Cisco router as a gateway. Note that as of Nov 15 2005, Asterisk has been replaced by OpenPBX, which is actually a forked project of Asterisk. As both rely on the same configuration files, other switch might occur in the future without any noticeable change for our end users.

After having set up an IPsec VPN tunnel, every user willing to place calls using our gateway must register to our Asterisk / OpenPBX server, and thus authenticate himself. SIP authentication process as described in RFC 3310, relies on a challenge / response procedure similar to HTTP digest authentication. On a REGISTER request issued by a UAC, the Registrar challenges the UAC, which registers again providing the challenge response.

We have a real interest in integrating an external existing authentication source to a SIP Registrar such as Asterisk / OpenPBX, because we want our remote access telephony service to take advantage of our existing AAA system. However, we found it not trivial to achieve. We describe in this document our experience with RADIUS and LDAP used by Asterisk / OpenPBX as such external sources.

In particular, we describe why it is currently quite tricky to achieve an acceptable LDAP authentication scheme for SIP authentication, simply because no LDAP server can handle the challenging procedure needed by SIP. One acceptable way of using LDAP as an authentication source is to store the passwords either in clear text (which unacceptably alters the security level of the global authentication solution), or encrypted in the form of a hash of the username:realm:password string, and have a RADIUS server between the SIP Registrar and the LDAP server that pulls the password during the Authorization process.


3. Simple registration process

We describe in this section the general case, where the user provided credentials are checked against a local database by Asterisk / OpenPBX, e.g. the configuration file sip.conf, in which each user has a matching entry. The user password can be stored either in clear text or in an encrypted form, which is actually a hash of the username:realm:password chain. Hereafter is an example of an entry for a user taken in the sip.conf file :


[username]
type=friend
context=from-sip-remote-clients
fromdomain=inria.fr
md5secret=f66538457ea6d5af832fcac4c3bcebe2
host=dynamic

Figure 1 shows a simple registration procedure in the case where the authentication source resides in the SIP registrar itself.

Figure 1. Simple SIP registration

Here is an example that details the previous registration procedure (taken from an Asterisk log). We can see the first refusal sent by the SIP registrar, along with the WWW-Authenticate attribute containing both realm and nonce values needed by the User Agent in order to compute the response value sent in the Authorization attribute contained in the second registration attempt.



REGISTER sip:192.168.0.1:5060 SIP/2.0
Content-Length: 0
Contact: ;events="message-summary"
Call-ID: 1DBDA84B-37E9-4F05-BE8B-E3A0F6BBEE91@192.168.0.2
Max-Forwards: 70
From: ;tag=220587183498
CSeq: 3 REGISTER
To: 
Via: SIP/2.0/UDP 192.168.0.2;rport;branch=z9hG4bK805d2fa50131c9b1434671010000391200000013

SIP/2.0 401 Unauthorized
Via: SIP/2.0/UDP 192.168.0.2;rport;branch=z9hG4bK805d2fa50131c9b1434671010000391200000013;received=192.168.0.2
From: ;tag=220587183498
To: ;tag=as6d76ba1b
Call-ID: 1DBDA84B-37E9-4F05-BE8B-E3A0F6BBEE91@192.168.0.2
CSeq: 3 REGISTER
User-Agent: Asterisk PBX
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY
Contact: 
WWW-Authenticate: Digest realm="asterisk", nonce="4f87b95d" 
Content-Length: 0

REGISTER sip:192.168.0.1:5060 SIP/2.0
Content-Length: 0
Contact: ;events="message-summary"
Call-ID: 1DBDA84B-37E9-4F05-BE8B-E3A0F6BBEE91@192.168.0.2
Max-Forwards: 70
From: ;tag=2205872822811
CSeq: 4 REGISTER
To: 
Via: SIP/2.0/UDP 192.168.0.2;rport;branch=z9hG4bK805d2fa50131c9b14346710100004e6d00000016
Authorization: Digest username="sultan",realm="asterisk",nonce="4f87b95d",uri="sip:192.168.0.1:5060",response="fed6890f44712fbaef17c704e6e30eac"

SIP/2.0 200 OK
Via: SIP/2.0/UDP 192.168.0.2;rport;branch=z9hG4bK805d2fa50131c9b14346710100004e6d00000016;received=192.168.0.2
From: ;tag=2205872822811
To: ;tag=as6d76ba1b
Call-ID: 1DBDA84B-37E9-4F05-BE8B-E3A0F6BBEE91@192.168.0.2
CSeq: 4 REGISTER
User-Agent: Asterisk PBX
Allow: INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY
Expires: 120
Contact: ;expires=120
Date: Fri, 07 Oct 2005 12:57:55 GMT
Content-Length: 0


4. Using RADIUS as an external authentication source

SIP peers authentication relies on the Digest Authentication method defined in RFC 2617. The SIP server challenges the peer, and compares the peer given value with its own computation result.

The following expired draft proposes a method for integrating RADIUS into the SIP digest authentication mechanism : http://www1.cs.columbia.edu/sip/drafts/sip/draft-sterman-aaa-sip-04.txt

The IETF RADEXT Working Group now handles this task : http://www.ietf.org/internet-drafts/draft-ietf-radext-digest-auth-06.txt

The RADIUS protocol offers the necessary means to achieve SIP digest authentication and thus use an external user authentication database. We describe in this section how we set up a RADIUS server, and a RADIUS client running on the same computer that runs the SIP Registrar (eg. Asterisk / OpenPBX).

Figure 2 shows a SIP registration procedure in the case where the authentication source resides in an external RADIUS server. The Access-Request must contains the necessary material to compute the challenge on the RADIUS server, e.g. at least the following attributes :

Digest-Response
Digest-Realm
Digest-Nonce
Digest-Method
Digest-URI
Digest-User-Name

Figure 2. SIP + RADIUS registration process


4.1. Configuring FreeRADIUS for digest authentication

In order to set up FreeRADIUS to handle digest authentication requests, we just need to uncomment the digest lines in both "authenticate" and "authorize" sections of the radiusd.conf file.

In the 'authenticate' section :


#
#  The 'digest' module currently has no configuration.
#
#  "Digest" authentication against a Cisco SIP server.
#  See 'doc/rfc/draft-sterman-aaa-sip-00.txt' for details
#  on performing digest authentication for Cisco SIP servers.
#
	digest {
	}

In the 'authorize' section :


#
#  If you have a Cisco SIP server authenticating against
#  FreeRADIUS, uncomment the following line, and the 'digest'
#  line in the 'authenticate' section.
digest

See the next paragraph for configuring the users file.


4.1.1. Storing passwords in an encrypted form in FreeRADIUS

User passwords can be stored in clear or encrypted form in the users file of the FreeRADIUS server.

Here is an example of a user record in the users FreeRADIUS file :


bob		User-Password := "zanzibar"

The password is stored in clear text, which might not be acceptable.

It is possible to store the hash value of a specific string instead of the clear text password : H(username:realm:password).Where H() is a hashing function (ex. MD5).

Note that the encrypted password storage is supported in FreeRADIUS for versions 1.1.0 and above. The H(username:realm:password) chain can simply be stored in a Digest-HA1 attribute.

Example :

Suppose user "bob", in realm "biloxi.com" with password "zanzibar" is registered in our RADIUS server. The matching entry in the FreeRADIUS users file would be :


bob		Digest-HA1 := "12af60467a33e8518da5c68bbff12b11"

the MD5 hash is given by issuing the following command :


$echo -n 'bob:biloxi.com:zanzibar' | md5sum
12af60467a33e8518da5c68bbff12b11  -

Storing the user credentials this way does not prevent from a password replay attack in the case the authentication database is compromised, because the hash can be reused by some program in order to authenticate to the SIP server. But, by encrypting the passwords, other applications that rely on the same authentication database are protected.


4.2. Chosing a RADIUS client API

The next step towards setting up RADIUS authentication is to provide the RADIUS client functionnality to the SIP registrar. We found out two APIs that fit this need :

RADIUS clients APIs

pam_radius

A PAM module that provides RADIUS client functionnality. Taken from the FreeRADIUS project, it needs to be patched if you want to use it in digest Authentication mode.

radiusclient-ng

A RADIUS client API. Only tested along with OpenPBX, no need to patch it if you want to use it in digest Authentication mode.

We patched Asterisk in order to have it work with the PAM module, and at the same time, we patched OpenPBX so that it can handle both the pam_module and radiusclient-ng API. More information regarding the settings and configurations are given in the next paragraphs.


4.2.1. pam_radius module

Although FreeRADIUS handles digest authentication correctly, pam\_radius does not. The following patch corrects this : http://bugs.freeradius.org/show_bug.cgi?id=259

You need to download the CVS version avalaible at this time (version 1.3.17) :


$cvs -d :pserver:anoncvs@cvs.freeradius.org:/source login
CVS password: anoncvs
$cvs -d :pserver:anoncvs@cvs.freeradius.org:/source checkout pam_radius

Then apply the patch (name it pam_radius-digest_auth-0.1.patch for example):


$patch -p0 -b < pam_radius-digest_auth-0.1.patch
patching file pam_radius/pam_radius_auth.c
patching file pam_radius/pam_radius_auth.h
patching file pam_radius/radius.h

And follow the instructions to install the pam_radius client.

Finally, we must tell the PAM module how to deal with RADIUS. Assuming we want to authenticate against server at address 12.34.56.78 and using secret testing123, we will edit the /etc/radd/server file :


12.34.56.78	testing123	1

4.2.2. radiusclient-ng

radiusclient-ng (v0.5.2) has been used, without any modification to the source code.

Once radiusclient-ng is installed (based on a /usr/local prefix), you need to append the RADIUS SIP attributes to the dictionary file :


$cat /usr/local/etc/radiusclient-ng/dictionary.sip >> /usr/local/etc/radiusclient-ng/dictionary

Do not forget to tell your RADIUS client where it can reach the server by filling the /usr/local/etc/radiusclient-ng/servers file.


4.3. Asterisk

Asterisk has been patched along with the previously decribed PAM radius module.

A discussion on how to provide RADIUS functions to Asterisk can be found here, along with the patch : http://bugs.digium.com/view.php?id=5424

User configuration resides in sip.conf file. Now, we don't want the user password stored in this file, so we just tell Asterisk that authentication is achieved through PAM. Therefore, an example of user entry would be :


[username]
type=friend
context=from-sip-remote-clients
fromdomain=inria.fr
auth_type=pam
host=dynamic

Along with sip.conf, we need to edit the /etc/pam.d/asterisk file as well :


auth    sufficient      pam_radius_auth.so      digest

4.4. OpenPBX

OpenPBX has been patched along with both the previously decribed RADIUS APIs (eg pam_radius and radiusclient-ng)

A discussion on how to provide RADIUS functions to OpenPBX can be found at the OpenPBX's bugtracker system : http://trac.openpbx.org/cgi-bin/trac.cgi/ticket/97

Current work regarding this topic is available at this subversion location : svn://svn.openpbx.org/openpbx/branches/psultan/experimental/trunk

To download and test it :


$svn co svn://svn.openpbx.org/openpbx/branches/psultan/experimental/trunk
$cd trunk
$./bootstrap.sh
$./configure --enable-extauth --with-res_user_auth
$make
$make install

User configuration resides in the sip.conf file. Now, we don't want the user password stored in this file, so we just tell OpenPBX that authentication is achieved through PAM or directly using radiusclient-ng.

An example of user entry would be :

pam_radius

[username]
type=friend
context=from-sip-remote-clients
fromdomain=inria.fr
auth_type=pam
host=dynamic
radiusclient-ng

[username]
type=friend
context=from-sip-remote-clients
fromdomain=inria.fr
auth_type=radius
host=dynamic

Along with sip.conf, we need to edit the /etc/pam.d/asterisk file as well, in case we chose to authenticate users with pam_radius :


auth    sufficient      pam_radius_auth.so      digest

5. LDAP authentication through RADIUS

5.1. LDAP Alone

Using LDAP directly as an authentication server cannot be done, because authentication in LDAP is simply a binding procedure with the user provided password, it cannot handle the HTTP-SIP digest authentication mechanism. We might achieve user authentication against an LDAP server by putting RADIUS entities (e.g. client and server) in between the SIP registrar (eg Asterisk or OpenPBX) and the LDAP server. See next section.


5.2. LDAP through RADIUS

Our goal here is to retrieve the user password stored in the LDAP server during the Authorization process in FreeRADIUS, that takes place before Authentication.

We need to :

  1. Configure FreeRADIUS to handle digest authentication (see previous sections)

  2. Uncomment the pap line in the "authorize" section of the radiusd.conf file, as this module handles some uuencode functions necessary in case your LDAP server stores MD5 hashes in base64 for example

  3. Uncomment the ldap line in the "authorize" section of the radiusd.conf file

  4. Configure the ldap module

Here is an example of a configuration for the ldap module :


ldap {
	server = "ldap.your.domain"
	identity = "cn=admin,o=My Org,c=UA"
	password = mypass
	basedn = "o=My Org,c=UA"
	filter = "(uid=%{Stripped-User-Name:-%{User-Name}})"
	# base_filter = "(objectclass=radiusprofile)"
	[....]
	dictionary_mapping = ${raddbdir}/ldap.attrmap
		ldap_connections_number = 5
	[....]
	password_attribute = userPassword
	[....]
}

During the Authorization process, the RADIUS server will connect to the configured LDAP server as an administrator, pull the userPassword LDAP value for the specified user and transfer it to the User-Password RADIUS value.

As we've seen it in the previous sections, the userPassword LDAP value can be either a clear text password, or a hashed value of username:realm:password.

Figure 3 shows a SIP registration procedure in the case where the authentication source resides in an external LDAP server. The userPassword value is pulled out by the RADIUS server during the Authorization process.

Figure 3. SIP + RADIUS + LDAP registration process


6. GNU Free Documentation License

A copy of the GNU FDL is available here : GNU Free Documentation License