# HG changeset patch # User Sebastien Decugis # Date 1244707185 -32400 # Node ID 9cb1799c40d16a0fd23bdff670f9500f9d29ae10 # Parent e90a9d081c988ad8bcea7c105bd1bd39e59e376b Added code to convert Access-Request attributes to Diameter AVPs diff -r e90a9d081c98 -r 9cb1799c40d1 extensions/radius_gw/rg_common.h --- 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 */ diff -r e90a9d081c98 -r 9cb1799c40d1 extensions/radius_gw/sub_acct.c --- 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; } diff -r e90a9d081c98 -r 9cb1799c40d1 extensions/radius_gw/sub_auth.c --- 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 )