view extensions/radius_gw/sub_auth.c @ 410:9cb1799c40d1

Added code to convert Access-Request attributes to Diameter AVPs
author Sebastien Decugis <sdecugis@nict.go.jp>
date Thu, 11 Jun 2009 16:59:45 +0900
parents bcc13af0825a
children 2d1f1c6ae1cd
line wrap: on
line source

/*********************************************************************************************************
* Software License Agreement (BSD License)                                                               *
* Author: Sebastien Decugis <sdecugis@nict.go.jp>							 *
*													 *
* Copyright (c) 2009, WIDE Project and NICT								 *
* All rights reserved.											 *
* 													 *
* Redistribution and use of this software in source and binary forms, with or without modification, are  *
* permitted provided that the following conditions are met:						 *
* 													 *
* * Redistributions of source code must retain the above 						 *
*   copyright notice, this list of conditions and the 							 *
*   following disclaimer.										 *
*    													 *
* * Redistributions in binary form must reproduce the above 						 *
*   copyright notice, this list of conditions and the 							 *
*   following disclaimer in the documentation and/or other						 *
*   materials provided with the distribution.								 *
* 													 *
* * Neither the name of the WIDE Project or NICT nor the 						 *
*   names of its contributors may be used to endorse or 						 *
*   promote products derived from this software without 						 *
*   specific prior written permission of WIDE Project and 						 *
*   NICT.												 *
* 													 *
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 	 *
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 	 *
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF   *
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.								 *
*********************************************************************************************************/

/* Sub extension for handling RADIUS Accounting-Request messages */

#define IN_EXTENSION
#define DEFINE_DEBUG_MACRO	sub_auth
#define DECLARE_API_POINTERS
#include <waaad/waaad.h>

#include "rg_common.h"

#ifndef SUB_AUTH_VERBO
#define SUB_AUTH_VERBO 0
#endif /* SUB_AUTH_VERBO */


int sub_auth_verbosity = SUB_AUTH_VERBO;

/* Attributes missing from radius.h */
#define RADIUS_ATTR_CHAP_PASSWORD	3
#define RADIUS_ATTR_ARAP_PASSWORD	70

struct rga_conf_state {
	char * conffile;
	void * dl_handle;
	int (*rgw_clients_getkey)(void * cli, unsigned char **key, size_t *key_len);
};

static struct rga_conf_state * auth_conf_parse(char * conffile)
{
	struct rga_conf_state * cs;
	
	TRACE_ENTRY("%p", conffile);
	
	CHECK_MALLOC_DO( cs = malloc(sizeof(struct rga_conf_state)), return NULL );
	memset(cs, 0, sizeof(struct rga_conf_state));
	
	cs->conffile = conffile;
	
	if (conffile) {
		TRACE_DEBUG(INFO, "Sub extension Authentication (RFC2865, RFC3579) initialized with configuration: '%s'", cs->conffile);
	} else {
		TRACE_DEBUG(INFO, "Sub extension Authentication (RFC2865, RFC3579) initialized with default configuration");
	}
	
	CHECK_FCT_DO( rg_pointers_init(&cs->dl_handle), return NULL );
	rg_pointers_resolve(cs->rgw_clients_getkey, cs->dl_handle, "rgw_clients_getkey", NULL);
	
	return cs;
}

static void auth_conf_free(struct rga_conf_state * cs)
{
	TRACE_ENTRY("%p", cs);
	CHECK_PARAMS_DO( cs, );
	rg_pointers_fini(&cs->dl_handle);
	free(cs);
	return;
}

static int auth_rad_req(struct rga_conf_state * cs, sess_id_t ** session, struct radius_msg * rad_req, struct radius_msg ** rad_ans, msg_t ** diam_fw, void * cli )
{
	int idx;
	int got_id = 0;
	int got_mac = 0;
	int got_passwd = 0;
	int got_eap = 0;
	int got_empty_eap = 0;
	uint32_t status_type;
	size_t *nattr_pos;
	size_t nattr_used = 0;
	dict_object_t * avp_tun_dict;
	msg_avp_t ** avp_tun = NULL;
	
	TRACE_ENTRY("%p %p %p %p %p %p", cs, session, rad_req, rad_ans, diam_fw, cli);
	CHECK_PARAMS(rad_req && (rad_req->hdr->code == RADIUS_CODE_ACCESS_REQUEST) && rad_ans && diam_fw && *diam_fw);
	
	/*
	   Guidelines:
	     http://tools.ietf.org/html/rfc4005#section-9.1
	     http://tools.ietf.org/html/rfc4072#section-6.1

	   When a Translation Agent receives a RADIUS message, the following
	   steps should be taken:

	      -  If a Message-Authenticator attribute is present, the value MUST
        	 be checked but not included in the Diameter message.  If it is
        	 incorrect, the RADIUS message should be silently discarded.
        	 The gateway system SHOULD generate and include a Message-
        	 Authenticator in returned RADIUS responses.
	             -> done in rgw_msg_auth_check

	      -  The transport address of the sender MUST be checked against the
        	 NAS identifying attributes.  See the description of NAS-
        	 Identifier and NAS-IP-Address below.
		     -> done in rgw_clients_check_origin

	      -  The Translation Agent must maintain transaction state
        	 information relevant to the RADIUS request, such as the
        	 Identifier field in the RADIUS header, any existing RADIUS
        	 Proxy-State attribute, and the source IP address and port
        	 number of the UDP packet.  These may be maintained locally in a
        	 state table or saved in a Proxy-Info AVP group.  A Diameter
        	 Session-Id AVP value must be created using a session state
        	 mapping mechanism.
		     -> Identifier, source and port are saved along with the request,
		        and associated with the session state.
		     -> sub_echo_drop should handle the Proxy-State attribute (conf issue)

	      -  If the RADIUS request contained a State attribute and the
        	 prefix of the data is "Diameter/", the data following the
        	 prefix contains the Diameter Origin-Host/Origin-Realm/Session-
        	 Id.  If no such attributes are present and the RADIUS command
        	 is an Access-Request, a new Session-Id is created.  The
        	 Session-Id is included in the Session-Id AVP.
		     -> done in rgw_msg_create_base.

	      -  The Diameter Origin-Host and Origin-Realm AVPs MUST be created
        	 and added by using the information from an FQDN corresponding
        	 to the NAS-IP-Address attribute (preferred if available),
        	 and/or to the NAS-Identifier attribute.  (Note that the RADIUS
        	 NAS-Identifier is not required to be an FQDN.)
		     -> done in rgw_msg_create_base.

	      -  The response MUST have an Origin-AAA-Protocol AVP added,
        	 indicating the protocol of origin of the message.
		     -> what "response" ??? Added to the AAR / DER in this function.

	      -  The Proxy-Info group SHOULD be added, with the local server's
        	 identity specified in the Proxy-Host AVP.  This should ensure
        	 that the response is returned to this system.
		     -> We don't need this, answer is always routed here anyway.
		     
	      For EAP:
	      
	      o  RADIUS EAP-Message attribute(s) are translated to a Diameter
		 EAP-Payload AVP.  If multiple RADIUS EAP-Message attributes are
		 present, they are concatenated and translated to a single Diameter
		 EAP-Payload AVP.
		     -> concatenation done by radius_msg_get_eap

	      -> the remaining is specific conversion rules
	*/
	
	/* Check basic information is there */
	for (idx = 0; idx < rad_req->attr_used; idx++) {
		struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(rad_req->buf + rad_req->attr_pos[idx]);
		switch (attr->type) {
			case RADIUS_ATTR_NAS_IP_ADDRESS:
			case RADIUS_ATTR_NAS_IDENTIFIER:
			case RADIUS_ATTR_NAS_IPV6_ADDRESS:
				got_id = 1;
				break;
			case RADIUS_ATTR_MESSAGE_AUTHENTICATOR:
				got_mac = 1;
				break;
			case RADIUS_ATTR_EAP_MESSAGE:
				got_eap = 1;
				if (attr->length == 2)
					got_empty_eap = 1;
				break;
			case RADIUS_ATTR_USER_PASSWORD:
			case RADIUS_ATTR_CHAP_PASSWORD:
			case RADIUS_ATTR_ARAP_PASSWORD:
				got_passwd += 1;
				break;
		}
	}
	if (!got_id) {
		TRACE_DEBUG(INFO, "RADIUS Access-Request did not contain a NAS IP or Identifier attribute, reject.");
		return EINVAL;
	}
	/* [Note 1] An Access-Request that contains either a User-Password or
	   CHAP-Password or ARAP-Password or one or more EAP-Message attributes
	   MUST NOT contain more than one type of those four attributes.  If it
	   does not contain any of those four attributes, it SHOULD contain a
	   Message-Authenticator.  If any packet type contains an EAP-Message
	   attribute it MUST also contain a Message-Authenticator.  A RADIUS
	   server receiving an Access-Request not containing any of those four
	   attributes and also not containing a Message-Authenticator attribute
	   SHOULD silently discard it.  */
	if (((got_eap + got_passwd) > 1) || (got_eap && !got_mac) || (!got_eap && !got_passwd && !got_mac)) {
		TRACE_DEBUG(INFO, "RADIUS Access-Request not conform to RFC3579 sec 3.3 note 1, discard.");
		return EINVAL;
	}
	
	/* Add the appropriate command code & Auth-Application-Id */
	{
		msg_data_t * header = NULL;
		CHECK_FCT( msg_data ( *diam_fw, &header ) );
		header->msg_flags = CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE;
		if (got_eap) {
			header->msg_code = 268; /* DER */
			header->msg_appl = 5;   /* Diameter EAP application */
		} else {
			header->msg_code = 265; /* AAR */
			header->msg_appl = 1;   /* Diameter NASREQ */
		}
		
		/* Add the Auth-Application-Id */
		{
			dict_object_t * avp_dict;
			avp_value_t value;
			msg_avp_t * avp;
			CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Auth-Application-Id", &avp_dict, ENOENT));
			CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );
			value.i32 = header->msg_appl;
			CHECK_FCT( msg_avp_setvalue ( avp, &value ) );
			CHECK_FCT( msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
		}
	}
	
	/*  	The type of request is identified through the Auth-Request-Type AVP
		[BASE].  The recommended value for most RADIUS interoperabily
		situations is AUTHORIZE_AUTHENTICATE. */
	
	/* Add Auth-Request-Type AVP */
	{
		dict_object_t * avp_dict;
		avp_value_t value;
		msg_avp_t * avp;
		CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Auth-Request-Type", &avp_dict, ENOENT));
		CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );
		value.i32 = 3; /* AUTHORIZE_AUTHENTICATE */
		CHECK_FCT( msg_avp_setvalue ( avp, &value ) );
		CHECK_FCT( msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
	}
	
	/* Add Origin-AAA-Protocol AVP */
	{
		dict_object_t * avp_dict;
		avp_value_t value;
		msg_avp_t * avp;
		CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Origin-AAA-Protocol", &avp_dict, ENOENT));
		CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );
		value.i32 = 1; /* RADIUS */
		CHECK_FCT( msg_avp_setvalue ( avp, &value ) );
		CHECK_FCT( msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
	}
	
	/* Convert the EAP payload (concat) */
	if (got_eap) {
		dict_object_t * avp_dict;
		avp_value_t value;
		msg_avp_t * avp;
		CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "EAP-Payload", &avp_dict, ENOENT));
		CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );
		
		/*    o  An empty RADIUS EAP-Message attribute (with length 2) signifies
			 EAP-Start, and it is translated to an empty EAP-Payload AVP. */
		if (got_empty_eap) {
			value.os.len = 0;
			value.os.data = "";
		} else {
			CHECK_MALLOC( value.os.data = radius_msg_get_eap(rad_req, &value.os.len) );
		}
		
		CHECK_FCT( msg_avp_setvalue ( avp, &value ) );
		CHECK_FCT( msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
	}
	
	/* Tunnel AVPs need some preparation */
	CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Tunneling", &avp_tun_dict, ENOENT));
	
	/* Convert the attributes one by one */
	CHECK_MALLOC( nattr_pos = malloc(rad_req->attr_size * sizeof(size_t)) );
	for (idx = 0; idx < rad_req->attr_used; idx++) {
		struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(rad_req->buf + rad_req->attr_pos[idx]);
		dict_object_t * avp_dict;
		avp_value_t value;
		msg_avp_t * avp;
		
		switch (attr->type) {
			
			#define CONV_STR( _avp_name_ )	\
				CHECK_PARAMS( attr->length >= 2 );						\
				CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, _avp_name_, &avp_dict, ENOENT));	\
				CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );					\
				value.os.len = attr->length - 2;						\
				value.os.data = (unsigned char *)(attr + 1);					\
				CHECK_FCT( msg_avp_setvalue ( avp, &value ) );					\
				CHECK_FCT( msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );			\
				
			#define CONV_32B( _avp_name_ )	\
				CHECK_PARAMS( attr->length == 6 );						\
				CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, _avp_name_, &avp_dict, ENOENT));	\
				CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );					\
				{										\
					uint8_t * v = (uint8_t *)(attr + 1);					\
					value.u32  = (v[0] << 24)						\
					           | (v[1] << 16)						\
					           | (v[2] <<  8)						\
					           |  v[3] ;							\
				}										\
				CHECK_FCT( msg_avp_setvalue ( avp, &value ) );					\
				CHECK_FCT( msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );			\
				
			#define CONV_64B( _avp_name_ )	\
				CHECK_PARAMS( attr->length == 10);						\
				CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, _avp_name_, &avp_dict, ENOENT));	\
				CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );					\
				{										\
					uint8_t * v = (uint8_t *)(attr + 1);					\
					value.u64  = ((uint64_t)(v[0]) << 56)					\
					           | ((uint64_t)(v[1]) << 48)					\
					           | ((uint64_t)(v[2]) << 40)					\
					           | ((uint64_t)(v[3]) << 32)					\
					           | ((uint64_t)(v[4]) << 24)					\
					           | ((uint64_t)(v[5]) << 16)					\
					           | ((uint64_t)(v[6]) <<  8)					\
					           |  (uint64_t)(v[7]) ;					\
				}										\
				CHECK_FCT( msg_avp_setvalue ( avp, &value ) );					\
				CHECK_FCT( msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );			\
				
		/* RFC 2865 */	
			/*
			      -  The Destination-Realm AVP is created from the information found
        			 in the RADIUS User-Name attribute.
			*/
			case RADIUS_ATTR_USER_NAME:
				CONV_STR( "User-Name" );
				{
					/* In addition, extract the destination-realm, if any */
					/* We suppose the format is anything@dest-realm */
					/* We don't care about decorated NAI here */
					int i;
					for (i = value.os.len - 2; i > 0; i--) {
						if (value.os.data[i] == '@') {
							break;
						}
					}
					CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "Destination-Realm", &avp_dict, ENOENT));
					CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );
					if (i == 0) {
						/* Not found in the User-Name => local domain */
						value.os.data = g_pconf->diameter_realm;
						value.os.len  = strlen(g_pconf->diameter_realm);
					} else {
						value.os.data += i + 1;
						value.os.len  -= i + 1;
					}
					CHECK_FCT( msg_avp_setvalue ( avp, &value ) );
					CHECK_FCT( msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
				}
				break;
				
			/*
			      -  If the RADIUS User-Password attribute is present, the password
        			 must be unencrypted by using the link's RADIUS shared secret.
        			 The unencrypted value must be forwarded in a User-Password AVP
        			 using Diameter security.
			*/
			case RADIUS_ATTR_USER_PASSWORD:
				if ((attr->length - 2) % 16) {
					TRACE_DEBUG(INFO, "Invalid length of User-Password attribute: %hhd", attr->length);
					return EINVAL;
				}
				{
					/* Decipher following this logic (refers to rfc2865#section-5.2 )
					   b1 = MD5(S + RA)	p1 = c(1) xor b1
					   b2 = MD5(S + c(1))   p2 = c(2) xor b2
					   ...
					*/
					
					uint8_t *ciph = (uint8_t *)(attr+1); 	/* c(i) */
					size_t ciph_len = attr->length - 2;
					uint8_t deciph[128];			/* pi */
					size_t deciph_len = 0;
					uint8_t * secret;			/* S */
					size_t secret_len;
					uint8_t hash[16];			/* b(i) */
					const uint8_t *addr[2];
					size_t len[2];
					
					/* Retrieve the shared secret */
					CHECK_FCT((*cs->rgw_clients_getkey)(cli, &secret, &secret_len));
					
					/* Initial b1 = MD5(S + RA) */
					addr[0] = secret;
					len[0] = secret_len;
					addr[1] = rad_req->hdr->authenticator;
					len[1] = 16;
					md5_vector(2, addr, len, hash);
					
					/* loop */
					while (deciph_len < ciph_len) {
						int i;
						/* pi = c(i) xor bi */
						for (i = 0; i < 16; i++)
							deciph[deciph_len + i] = ciph[deciph_len + i] ^ hash[i];
							/* do we have to remove the padding '\0's ? */
						
						/* b(i+1) = MD5(S + c(i) */
						addr[1] = ciph + deciph_len;
						md5_vector(2, addr, len, hash);
						
						deciph_len += 16;
					}
					
					/* Now save this value in the AVP */
					CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "User-Password", &avp_dict, ENOENT));
					CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );
					value.os.data = &deciph[0];
					value.os.len  = deciph_len;
					CHECK_FCT( msg_avp_setvalue ( avp, &value ) );
					CHECK_FCT( msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
				}
				break;
				

			/*
			      -  If the RADIUS CHAP-Password attribute is present, the Ident and
        			 Data portion of the attribute are used to create the CHAP-Auth
        			 grouped AVP.
			*/
			case RADIUS_ATTR_CHAP_PASSWORD:
				CHECK_PARAMS( attr->length == 19 /* RFC 2865 */);
				{
					uint8_t * c = (uint8_t *)(attr + 1);
					msg_avp_t * chap_auth;
					CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "CHAP-Auth", &avp_dict, ENOENT));
					CHECK_FCT( msg_avp_new ( avp_dict, 0, &chap_auth ) );
					CHECK_FCT( msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, chap_auth) );
					
					CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "CHAP-Algorithm", &avp_dict, ENOENT));
					CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );
					value.u32 = 5; /* The only value defined currently... */
					CHECK_FCT( msg_avp_setvalue ( avp, &value ) );
					CHECK_FCT( msg_avp_add ( chap_auth, MSG_BRW_LAST_CHILD, avp) );
					
					CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "CHAP-Ident", &avp_dict, ENOENT));
					CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );
					value.os.data = c;
					value.os.len = 1;
					CHECK_FCT( msg_avp_setvalue ( avp, &value ) );
					CHECK_FCT( msg_avp_add ( chap_auth, MSG_BRW_LAST_CHILD, avp) );
					
					c++;
					
					CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "CHAP-Response", &avp_dict, ENOENT));
					CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );
					value.os.data = c;
					value.os.len = attr->length - 3;
					CHECK_FCT( msg_avp_setvalue ( avp, &value ) );
					CHECK_FCT( msg_avp_add ( chap_auth, MSG_BRW_LAST_CHILD, avp) );
				}
				break;
				
			case RADIUS_ATTR_NAS_IP_ADDRESS:
				CONV_STR( "NAS-IP-Address" );
				break;
				
			case RADIUS_ATTR_NAS_PORT:
				CONV_32B( "NAS-Port" );
				break;
				
			case RADIUS_ATTR_SERVICE_TYPE:
				CONV_32B( "Service-Type" );
				break;
				
			case RADIUS_ATTR_FRAMED_PROTOCOL:
				CONV_32B( "Framed-Protocol" );
				break;
				
			case RADIUS_ATTR_FRAMED_IP_ADDRESS:
				CONV_STR( "Framed-IP-Address" );
				break;
				
			case RADIUS_ATTR_FRAMED_IP_NETMASK:
				CONV_STR( "Framed-IP-Netmask" );
				break;
				
			/* Framed-Routing never present in an Access-Request */
			/* Filter-Id never present in an Access-Request */
				
			case RADIUS_ATTR_FRAMED_MTU:
				CONV_32B( "Framed-MTU" );
				break;
			
			case RADIUS_ATTR_FRAMED_COMPRESSION:
				CONV_32B( "Framed-Compression" );
				break;
			
			case RADIUS_ATTR_LOGIN_IP_HOST:
				CONV_STR( "Login-IP-Host" );
				break;
				
			/* Login-Service never present in an Access-Request */
			/* Login-TCP-Port never present in an Access-Request */
			/* Reply-Message never present in an Access-Request */
			
			case RADIUS_ATTR_CALLBACK_NUMBER:
				CONV_STR( "Callback-Number" );
				break;
				
			/* Callback-Id never present in an Access-Request */
			/* Framed-Route never present in an Access-Request */
			/* Framed-IPX-Network never present in an Access-Request */
				
			case RADIUS_ATTR_STATE:
				CONV_STR( "State" );
				break;
			
			/* Class never present in an Access-Request */
				
			case RADIUS_ATTR_VENDOR_SPECIFIC:
				/* RFC 4005, Section 9.6 : 
					   Systems that don't have vendor format knowledge MAY discard such
					   attributes without knowing a suitable translation.
					   
					   [conversion rule in 9.6.2]
				 */
				if (attr->length >= 6) {
					uint32_t vendor_id;
					uint8_t * c = (uint8_t *)(attr + 1);
					
					vendor_id = c[0] << 24 | c[1] << 16 | c[2] << 8 | c[3];
					c += 4;
					
					switch (vendor_id) {
						
						/* For the vendors we KNOW they follow the VSA recommended format, we convert following the rules of RFC4005 (9.6.2) */
						case RADIUS_VENDOR_ID_MICROSOFT : /* RFC 2548 */
						/* other vendors ? */
						{
							size_t left;
							struct radius_attr_vendor *vtlv;
							
							CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, "waaad_generic_os", &avp_dict, ENOENT));
							
							left = attr->length - 6;
							vtlv = (struct radius_attr_vendor *)c;
						
							while ((left >= 2) && (vtlv->vendor_length <= left)) {
								msg_avp_data_t * avp_hdr;
								
								CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );
								value.os.len = vtlv->vendor_length - 2;
								value.os.data = (unsigned char *)(vtlv + 1);
								CHECK_FCT( msg_avp_setvalue ( avp, &value ) );
								
								CHECK_FCT( msg_avp_data( avp, &avp_hdr ) );
								avp_hdr->avp_code = vtlv->vendor_type;
								avp_hdr->avp_flags = AVP_FLAG_VENDOR;
								/* avp_hdr->avp_len = ; */
								avp_hdr->avp_vendor = vendor_id;
								
								CHECK_FCT( msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
								
								c += vtlv->vendor_length;
								left -= vtlv->vendor_length;
								vtlv = (struct radius_attr_vendor *)c;
							}
						}
						break;
						
						/* Other vendors we KNOw how to convert the attributes would be added here... */
						/* case RADIUS_VENDOR_ID_CISCO :
							break; */
						/* case RADIUS_VENDOR_ID_IETF : (extended RADIUS attributes)
							break; */
						
						/* When we don't know, just discard the attribute... VSA are optional with regards to RADIUS anyway */
						default:
							/* do nothing */
							TRACE_DEBUG(FULL, "VSA attribute from vendor %d discarded", vendor_id);
							
					}
				}
				break;
				
			/* Session-Timeout never present in an Access-Request */
			/* Idle-Timeout never present in an Access-Request */
			/* Termination-Action never present in an Access-Request */
				
			case RADIUS_ATTR_CALLED_STATION_ID:
				CONV_STR( "Called-Station-Id" );
				break;
			
			case RADIUS_ATTR_CALLING_STATION_ID:
				CONV_STR( "Calling-Station-Id" );
				break;
			
			case RADIUS_ATTR_NAS_IDENTIFIER:
				CONV_STR( "NAS-Identifier" );
				break;
			
			/* Proxy-State is supposed to be handled by sub_echo_drop, we don't touch it here */
			
			case RADIUS_ATTR_LOGIN_LAT_SERVICE:
				CONV_STR( "Login-LAT-Service" );
				break;
			
			case RADIUS_ATTR_LOGIN_LAT_NODE:
				CONV_STR( "Login-LAT-Node" );
				break;
			
			case RADIUS_ATTR_LOGIN_LAT_GROUP:
				CONV_STR( "Login-LAT-Group" );
				break;
			
			/* Framed-AppleTalk-Link never present in an Access-Request */
			/* Framed-AppleTalk-Network never present in an Access-Request */
			/* Framed-AppleTalk-Zone never present in an Access-Request */
			
			case RADIUS_ATTR_CHAP_CHALLENGE:
				CONV_STR( "CHAP-Challenge" );
				break;
			
			case RADIUS_ATTR_NAS_PORT_TYPE:
				CONV_32B( "NAS-Port-Type" );
				break;
			
			case RADIUS_ATTR_PORT_LIMIT:
				CONV_32B( "Port-Limit" );
				break;
			
			case RADIUS_ATTR_LOGIN_LAT_PORT:
				CONV_STR( "Login-LAT-Port" );
				break;
			
			
		/* RFC 3162 */	
			case RADIUS_ATTR_NAS_IPV6_ADDRESS:
				CONV_STR( "NAS-IPv6-Address" );
				break;
				
			case RADIUS_ATTR_FRAMED_INTERFACE_ID:
				CONV_64B( "Framed-Interface-Id" );
				break;
				
			case RADIUS_ATTR_FRAMED_IPV6_PREFIX:
				CONV_STR( "Framed-IPv6-Prefix" );
				break;
				
			case RADIUS_ATTR_LOGIN_IPV6_HOST:
				CONV_STR( "Login-IPv6-Host" );
				break;
				
			/* Framed-IPv6-Route never present in an Access-Request */
			/* Framed-IPv6-Pool never present in an Access-Request */


		/* RFC 2868 */	
			#define AVP_TUN_PREPARE() {										\
						if (avp_tun == NULL) {								\
							CHECK_MALLOC( avp_tun = malloc(sizeof( msg_avp_t * ) * 32 ) );		\
							memset(avp_tun, 0, sizeof( msg_avp_t * )* 32);				\
						}										\
						tag = *(uint8_t *)(attr + 1);							\
						if (tag > 0x1F) tag = 0;							\
						if (avp_tun[tag] == NULL) {							\
							CHECK_FCT( msg_avp_new ( avp_tun_dict, 0, &avp_tun[tag] ) );		\
							CHECK_FCT( msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp_tun[tag]) );	\
						}										\
					}
					
			#define CONV_TUN_STR( _avp_name_ ) {							\
				uint8_t tag;									\
				AVP_TUN_PREPARE();								\
				CHECK_PARAMS( attr->length >= 3);						\
				CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, _avp_name_, &avp_dict, ENOENT));	\
				CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );					\
				value.os.len = attr->length - (tag ? 3 : 2);					\
				value.os.data = ((unsigned char *)(attr + 1)) + (tag ? 1 : 0);			\
				CHECK_FCT( msg_avp_setvalue ( avp, &value ) );					\
				CHECK_FCT( msg_avp_add ( avp_tun[tag], MSG_BRW_LAST_CHILD, avp) );		\
				}
				
			#define CONV_TUN_24B( _avp_name_ ) {							\
				uint8_t tag;									\
				AVP_TUN_PREPARE();								\
				CHECK_PARAMS( attr->length == 6);						\
				CHECK_FCT( dict_search( DICT_AVP, AVP_BY_NAME, _avp_name_, &avp_dict, ENOENT));	\
				CHECK_FCT( msg_avp_new ( avp_dict, 0, &avp ) );					\
				{										\
					uint8_t * v = (uint8_t *)(attr + 1);					\
					value.u32 = (v[1] << 16) | (v[2] <<8) | v[3] ;				\
				}										\
				CHECK_FCT( msg_avp_setvalue ( avp, &value ) );					\
				CHECK_FCT( msg_avp_add ( avp_tun[tag], MSG_BRW_LAST_CHILD, avp) );		\
				}

			/*
			      -  If the RADIUS message contains Tunnel information [RADTunnels],
        			 the attributes or tagged groups should each be converted to a
        			 Diameter Tunneling Grouped AVP set.  If the tunnel information
        			 contains a Tunnel-Password attribute, the RADIUS encryption
        			 must be resolved, and the password forwarded, by using Diameter
        			 security methods.
				    -> If the RADIUS message does not use properly the Tag info, result is unpredictible here.. 
			*/
			case RADIUS_ATTR_TUNNEL_TYPE:
				CONV_TUN_24B( "Tunnel-Type" );
				break;
			
			case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE:
				CONV_TUN_24B( "Tunnel-Medium-Type" );
				break;
			
			case RADIUS_ATTR_TUNNEL_CLIENT_ENDPOINT:
				CONV_TUN_STR( "Tunnel-Client-Endpoint" );
				break;
			
			case RADIUS_ATTR_TUNNEL_SERVER_ENDPOINT:
				CONV_TUN_STR( "Tunnel-Server-Endpoint" );
				break;
			
			/* Tunnel-Password never present in an Access-Request */

			case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID:
				CONV_TUN_STR( "Tunnel-Private-Group-Id" );
				break;
			
			/* Tunnel-Assignment-ID never present in an Access-Request */
			
			case RADIUS_ATTR_TUNNEL_PREFERENCE:
				CONV_TUN_24B( "Tunnel-Preference" );
				break;
			
			case RADIUS_ATTR_TUNNEL_CLIENT_AUTH_ID:
				CONV_TUN_STR( "Tunnel-Client-Auth-Id" );
				break;
			
			case RADIUS_ATTR_TUNNEL_SERVER_AUTH_ID:
				CONV_TUN_STR( "Tunnel-Server-Auth-Id" );
				break;
			
			
		/* RFC 2869 */	
			case RADIUS_ATTR_ARAP_PASSWORD:
				CONV_STR( "ARAP-Password" );
				break;
				
			/* ARAP-Features never present in an Access-Request */
			/* ARAP-Zone-Access never present in an Access-Request */
			
			case RADIUS_ATTR_ARAP_SECURITY:
				CONV_32B( "ARAP-Security" );
				break;
			
			case RADIUS_ATTR_ARAP_SECURITY_DATA:
				CONV_STR( "ARAP-Security-Data" );
				break;
			
			/* Password-Retry never present in an Access-Request */
			/* Prompt never present in an Access-Request */
			
			case RADIUS_ATTR_CONNECT_INFO:
				CONV_STR( "Connect-Info" );
				break;
			
			/* Configuration-Token never present in an Access-Request */
			/* ARAP-Challenge-Response never present in an Access-Request */
			/* Acct-Interim-Interval never present in an Access-Request */
			
			case RADIUS_ATTR_NAS_PORT_ID:
				CONV_STR( "NAS-Port-Id" );
				break;
			
			/* Framed-Pool never present in an Access-Request */
			
				
		/* RFC 2869 / 3579 */	
			case RADIUS_ATTR_ORIGINATING_LINE_INFO:
				CONV_STR( "Originating-Line-Info" );
				break;
				
			case RADIUS_ATTR_MESSAGE_AUTHENTICATOR:
			case RADIUS_ATTR_EAP_MESSAGE:
				/* It was already handled, just remove the attribute */
				break;
				
		/* Default */		
			default: /* unknown attribute */
				/* We just keep the attribute in the RADIUS message */
				nattr_pos[nattr_used++] = rad_req->attr_pos[idx];
		}
	}
	
	/* Destroy tunnel pointers in case we used it */
	free(avp_tun);
	
	/* Update the radius message to remove all handled attributes */
	free(rad_req->attr_pos);
	rad_req->attr_pos = nattr_pos;
	rad_req->attr_used = nattr_used;

	return 0;
}

static int auth_diam_ans(struct rga_conf_state * cs, sess_id_t ** session, msg_t ** diam_ans, struct radius_msg ** rad_fw, void * cli )
{
	TRACE_ENTRY("%p %p %p %p %p", cs, session, diam_ans, rad_fw, cli);
	CHECK_PARAMS(cs);

	return ENOTSUP;
}

int rga_register(int version, waaad_api_t * waaad_api, struct radius_gw_api * api)
{
	TRACE_ENTRY("%d %p %p", version, waaad_api, api);
	CHECK_PARAMS( waaad_api && api );
	
	if (version != RADIUS_GW_API_VER) {
		log_error("ABI version mismatch, please recompile this extension (%s)\n", __FILE__);
		return EINVAL;
	}
	
	/* Required to use the waaad api from this sub-extension: */
	EXTENSION_API_INIT_INTERN( API_MODULE_ALL, "sub_auth", waaad_api );
	
	/* Initialize the radius_gw api callbacks */
	api->rga_conf_parse_cb = auth_conf_parse;
	api->rga_conf_free_cb  = auth_conf_free;
	api->rga_rad_req_cb    = auth_rad_req;
	api->rga_diam_ans_cb   = auth_diam_ans;
	
	/* We're done, we must not initialize any state here since the extension must be re-entrant, but in sample_conf_parse */
	return 0;
}
"Welcome to our mercurial repository"