changeset 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 e90a9d081c98
children 7b3d4431610a
files extensions/radius_gw/rg_common.h extensions/radius_gw/sub_acct.c extensions/radius_gw/sub_auth.c
diffstat 3 files changed, 766 insertions(+), 7 deletions(-) [+]
line wrap: on
line diff
--- a/extensions/radius_gw/rg_common.h	Thu Jun 11 16:59:23 2009 +0900
+++ b/extensions/radius_gw/rg_common.h	Thu Jun 11 16:59:45 2009 +0900
@@ -77,6 +77,60 @@
 #define RGW_EXT_TYPE_ACCT	2
 
 
+/**************************************************************/
+/*              Additional RADIUS definitions                 */
+/**************************************************************/
+/* Attributes missing from radius.h (not used in EAP) */
+enum { RADIUS_ATTR_CHAP_PASSWORD = 3,
+       RADIUS_ATTR_SERVICE_TYPE = 6,
+       RADIUS_ATTR_FRAMED_PROTOCOL = 7,
+       RADIUS_ATTR_FRAMED_IP_ADDRESS = 8,
+       RADIUS_ATTR_FRAMED_IP_NETMASK = 9,
+       RADIUS_ATTR_FRAMED_ROUTING = 10,
+       RADIUS_ATTR_FILTER_ID = 11,
+       RADIUS_ATTR_FRAMED_COMPRESSION = 13,
+       RADIUS_ATTR_LOGIN_IP_HOST = 14,
+       RADIUS_ATTR_LOGIN_SERVICE = 15,
+       RADIUS_ATTR_LOGIN_TCP_PORT = 16,
+       RADIUS_ATTR_CALLBACK_NUMBER = 19,
+       RADIUS_ATTR_CALLBACK_ID = 20,
+       RADIUS_ATTR_FRAMED_ROUTE = 22,
+       RADIUS_ATTR_FRAMED_IPX_NETWORK = 23,
+       RADIUS_ATTR_LOGIN_LAT_SERVICE = 34,
+       RADIUS_ATTR_LOGIN_LAT_NODE = 35,
+       RADIUS_ATTR_LOGIN_LAT_GROUP = 36,
+       RADIUS_ATTR_FRAMED_APPLETALK_LINK = 37,
+       RADIUS_ATTR_FRAMED_APPLETALK_NETWORK = 38,
+       RADIUS_ATTR_FRAMED_APPLETALK_ZONE = 39,
+       RADIUS_ATTR_CHAP_CHALLENGE = 60,
+       RADIUS_ATTR_PORT_LIMIT = 62,
+       RADIUS_ATTR_LOGIN_LAT_PORT = 63,
+       RADIUS_ATTR_TUNNEL_CLIENT_ENDPOINT = 66,
+       RADIUS_ATTR_TUNNEL_SERVER_ENDPOINT = 67,
+       RADIUS_ATTR_TUNNEL_PASSWORD = 69,
+       RADIUS_ATTR_ARAP_PASSWORD = 70,
+       RADIUS_ATTR_ARAP_FEATURES = 71,
+       RADIUS_ATTR_ARAP_ZONE_ACCESS = 72,
+       RADIUS_ATTR_ARAP_SECURITY = 73,
+       RADIUS_ATTR_ARAP_SECURITY_DATA = 74,
+       RADIUS_ATTR_PASSWORD_RETRY = 75,
+       RADIUS_ATTR_PROMPT = 76,
+       RADIUS_ATTR_CONFIGURATION_TOKEN = 78,
+       RADIUS_ATTR_TUNNEL_ASSIGNEMENT_ID = 82,
+       RADIUS_ATTR_TUNNEL_PREFERENCE = 83,
+       RADIUS_ATTR_ARAP_CHALLENGE_RESPONSE = 84,
+       RADIUS_ATTR_NAS_PORT_ID = 87,
+       RADIUS_ATTR_FRAMED_POOL = 88,
+       RADIUS_ATTR_TUNNEL_CLIENT_AUTH_ID = 90,
+       RADIUS_ATTR_TUNNEL_SERVER_AUTH_ID = 91,
+       RADIUS_ATTR_ORIGINATING_LINE_INFO = 94,
+       RADIUS_ATTR_FRAMED_INTERFACE_ID = 96,
+       RADIUS_ATTR_FRAMED_IPV6_PREFIX = 97,
+       RADIUS_ATTR_LOGIN_IPV6_HOST = 98,
+       RADIUS_ATTR_FRAMED_IPV6_ROUTE = 99,
+       RADIUS_ATTR_FRAMED_IPV6_POOL = 100
+};
+
 
 /**************************************************************/
 /*      Functions exported by the common library extension    */
--- a/extensions/radius_gw/sub_acct.c	Thu Jun 11 16:59:23 2009 +0900
+++ b/extensions/radius_gw/sub_acct.c	Thu Jun 11 16:59:45 2009 +0900
@@ -128,6 +128,30 @@
 		return -3;
 	}
 	
+			/*
+			      -  If the RADIUS message received is an Accounting-Request, the
+        			 Acct-Status-Type attribute value must be converted to a
+        			 Accounting-Record-Type AVP value.  If the Acct-Status-Type
+        			 attribute value is STOP, the local server MUST issue a
+        			 Session-Termination-Request message once the Diameter
+        			 Accounting-Answer message has been received.
+			*/
+
+			/*
+			      -  If the Accounting message contains an Acct-Termination-Cause
+        			 attribute, it should be translated to the equivalent
+        			 Termination-Cause AVP value.  (see below)
+			*/
+
+			/*
+			      -  If the RADIUS message contains the Accounting-Input-Octets,
+        			 Accounting-Input-Packets, Accounting-Output-Octets, or
+        			 Accounting-Output-Packets, these attributes must be converted
+        			 to the Diameter equivalents.  Further, if the Acct-Input-
+        			 Gigawords or Acct-Output-Gigawords attributes are present,
+        			 these must be used to properly compute the Diameter accounting
+        			 AVPs.
+			*/
 	return ENOTSUP;
 }
 
--- a/extensions/radius_gw/sub_auth.c	Thu Jun 11 16:59:23 2009 +0900
+++ b/extensions/radius_gw/sub_auth.c	Thu Jun 11 16:59:45 2009 +0900
@@ -55,6 +55,8 @@
 
 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)
@@ -74,6 +76,9 @@
 		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;
 }
 
@@ -81,6 +86,7 @@
 {
 	TRACE_ENTRY("%p", cs);
 	CHECK_PARAMS_DO( cs, );
+	rg_pointers_fini(&cs->dl_handle);
 	free(cs);
 	return;
 }
@@ -92,12 +98,84 @@
 	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_ACCOUNTING_REQUEST) && rad_ans && diam_fw && *diam_fw);
+	CHECK_PARAMS(rad_req && (rad_req->hdr->code == RADIUS_CODE_ACCESS_REQUEST) && rad_ans && diam_fw && *diam_fw);
 	
-	/* Check the message contains the NAS identification */
+	/*
+	   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) {
@@ -111,6 +189,8 @@
 				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:
@@ -119,10 +199,8 @@
 				break;
 		}
 	}
-			
-	/* Check basic information is there */
 	if (!got_id) {
-		TRACE_DEBUG(INFO, "RADIUS Account-Request did not contain a NAS IP or Identifier attribute, reject.");
+		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
@@ -135,11 +213,614 @@
 	   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 Account-Request not conform to RFC3579 sec 3.3 note 1, discard.");
+		TRACE_DEBUG(INFO, "RADIUS Access-Request not conform to RFC3579 sec 3.3 note 1, discard.");
 		return EINVAL;
 	}
 	
-	return ENOTSUP;
+	/* 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 )
"Welcome to our mercurial repository"