# HG changeset patch # User Sebastien Decugis # Date 1271759699 -32400 # Node ID 411314907b437ac6331958b48596146debc9cba9 # Parent 47fff7a7af06ba74effe46abae56fbe617a4e7d3 Added translation of Accounting messages diff -r 47fff7a7af06 -r 411314907b43 doc/app_radgw.conf.sample --- a/doc/app_radgw.conf.sample Mon Apr 19 17:00:24 2010 +0900 +++ b/doc/app_radgw.conf.sample Tue Apr 20 19:34:59 2010 +0900 @@ -39,6 +39,10 @@ # Once the list of extensions for an incoming message has been called (or if the list is empty), # an error is logged if some RADIUS attributes of the message have not been handled. +RGWX = "extensions/echodrop.rgwx" : "echodrop.rgwx.conf"; # See echodrop.rgwx.conf.sample file +RGWX = "extensions/auth.rgwx" : auth; +RGWX = "extensions/acct.rgwx" : acct; +# RGWX = "extensions/debug.rgwx"; # Uncomment to see the result of the translation plugins. ################## # RADIUS Clients # diff -r 47fff7a7af06 -r 411314907b43 extensions/app_radgw/rgw.h --- a/extensions/app_radgw/rgw.h Mon Apr 19 17:00:24 2010 +0900 +++ b/extensions/app_radgw/rgw.h Tue Apr 20 19:34:59 2010 +0900 @@ -102,6 +102,7 @@ int rgw_clients_check_dup(struct rgw_radius_msg_meta **msg, struct rgw_client *cli); int rgw_clients_check_origin(struct rgw_radius_msg_meta *msg, struct rgw_client *cli); int rgw_clients_get_origin(struct rgw_client *cli, char **fqdn, char **realm); +char * rgw_clients_id(struct rgw_client *cli); int rgw_client_finish_send(struct radius_msg ** msg, struct rgw_radius_msg_meta * req, struct rgw_client * cli); void rgw_clients_dispose(struct rgw_client ** ref); void rgw_clients_dump(void); diff -r 47fff7a7af06 -r 411314907b43 extensions/app_radgw/rgw_clients.c --- a/extensions/app_radgw/rgw_clients.c Mon Apr 19 17:00:24 2010 +0900 +++ b/extensions/app_radgw/rgw_clients.c Tue Apr 20 19:34:59 2010 +0900 @@ -443,6 +443,11 @@ return 0; } +char * rgw_clients_id(struct rgw_client *cli) +{ + return cli->fqdn; +} + void rgw_clients_dispose(struct rgw_client ** ref) { diff -r 47fff7a7af06 -r 411314907b43 extensions/app_radgw/rgw_common.h --- a/extensions/app_radgw/rgw_common.h Mon Apr 19 17:00:24 2010 +0900 +++ b/extensions/app_radgw/rgw_common.h Tue Apr 20 19:34:59 2010 +0900 @@ -129,7 +129,7 @@ RADIUS_ATTR_PASSWORD_RETRY = 75, RADIUS_ATTR_PROMPT = 76, RADIUS_ATTR_CONFIGURATION_TOKEN = 78, - RADIUS_ATTR_TUNNEL_ASSIGNEMENT_ID = 82, + RADIUS_ATTR_TUNNEL_ASSIGNMENT_ID = 82, RADIUS_ATTR_TUNNEL_PREFERENCE = 83, RADIUS_ATTR_ARAP_CHALLENGE_RESPONSE = 84, RADIUS_ATTR_NAS_PORT_ID = 87, @@ -148,6 +148,8 @@ enum { DIAM_ATTR_USER_NAME = 1, DIAM_ATTR_USER_PASSWORD = 2, + DIAM_ATTR_NAS_IP_ADDRESS = 4, + DIAM_ATTR_NAS_PORT = 5, DIAM_ATTR_SERVICE_TYPE = 6, DIAM_ATTR_FRAMED_PROTOCOL = 7, DIAM_ATTR_FRAMED_IP_ADDRESS = 8, @@ -167,12 +169,17 @@ DIAM_ATTR_STATE = 24, DIAM_ATTR_CLASS = 25, DIAM_ATTR_IDLE_TIMEOUT = 28, + DIAM_ATTR_NAS_IDENTIFIER = 32, DIAM_ATTR_LOGIN_LAT_SERVICE = 34, DIAM_ATTR_LOGIN_LAT_NODE = 35, DIAM_ATTR_LOGIN_LAT_GROUP = 36, DIAM_ATTR_FRAMED_APPLETALK_LINK = 37, DIAM_ATTR_FRAMED_APPLETALK_NETWORK = 38, DIAM_ATTR_FRAMED_APPLETALK_ZONE = 39, + DIAM_ATTR_ACCT_SESSION_ID = 44, + DIAM_ATTR_ACCT_MULTI_SESSION_ID = 50, + DIAM_ATTR_EVENT_TIMESTAMP = 55, + DIAM_ATTR_NAS_PORT_TYPE = 61, DIAM_ATTR_PORT_LIMIT = 62, DIAM_ATTR_LOGIN_LAT_PORT = 63, DIAM_ATTR_TUNNEL_TYPE = 64, @@ -188,13 +195,15 @@ DIAM_ATTR_PROMPT = 76, DIAM_ATTR_CONFIGURATION_TOKEN = 78, DIAM_ATTR_TUNNEL_PRIVATE_GROUP_ID = 81, - DIAM_ATTR_TUNNEL_ASSIGNEMENT_ID = 82, + DIAM_ATTR_TUNNEL_ASSIGNMENT_ID = 82, DIAM_ATTR_TUNNEL_PREFERENCE = 83, DIAM_ATTR_ARAP_CHALLENGE_RESPONSE = 84, DIAM_ATTR_ACCT_INTERIM_INTERVAL = 85, + DIAM_ATTR_NAS_PORT_ID = 87, DIAM_ATTR_FRAMED_POOL = 88, DIAM_ATTR_TUNNEL_CLIENT_AUTH_ID = 90, DIAM_ATTR_TUNNEL_SERVER_AUTH_ID = 91, + DIAM_ATTR_NAS_IPV6_ADDRESS = 95, DIAM_ATTR_FRAMED_INTERFACE_ID = 96, DIAM_ATTR_FRAMED_IPV6_PREFIX = 97, DIAM_ATTR_LOGIN_IPV6_HOST = 98, @@ -202,6 +211,10 @@ DIAM_ATTR_FRAMED_IPV6_POOL = 100, DIAM_ATTR_EAP_KEY_NAME = 102, DIAM_ATTR_AUTH_APPLICATION_ID = 258, + DIAM_ATTR_ACCT_APPLICATION_ID = 259, + DIAM_ATTR_VENDOR_SPECIFIC_APPLICATION_ID = 260, + DIAM_ATTR_SESSION_ID = 263, + DIAM_ATTR_ORIGIN_HOST = 264, DIAM_ATTR_MULTI_ROUND_TIMEOUT = 272, DIAM_ATTR_AUTH_REQUEST_TYPE = 274, DIAM_ATTR_AUTH_GRACE_PERIOD = 276, @@ -211,7 +224,10 @@ DIAM_ATTR_ERROR_MESSAGE = 281, DIAM_ATTR_ROUTE_RECORD = 282, DIAM_ATTR_PROXY_INFO = 284, + DIAM_ATTR_ACCOUNTING_SUB_SESSION_ID = 287, DIAM_ATTR_ERROR_REPORTING_HOST = 294, + DIAM_ATTR_TERMINATION_CAUSE = 295, + DIAM_ATTR_ORIGIN_REALM = 296, DIAM_ATTR_NAS_FILTER_RULE = 400, DIAM_ATTR_TUNNELING = 401, DIAM_ATTR_QOS_FILTER_RULE = 407, @@ -219,7 +235,10 @@ DIAM_ATTR_EAP_PAYLOAD = 462, DIAM_ATTR_EAP_REISSUED_PAYLOAD = 463, DIAM_ATTR_EAP_MASTER_SESSION_KEY = 464, - DIAM_ATTR_ACCOUNTING_EAP_AUTH_METHOD = 465 + DIAM_ATTR_ACCOUNTING_EAP_AUTH_METHOD = 465, + DIAM_ATTR_ACCOUNTING_RECORD_TYPE = 480, + DIAM_ATTR_ACCOUNTING_REALTIME_REQUIRED = 483, + DIAM_ATTR_ACCOUNTING_RECORD_NUMBER = 485 }; const char * rgw_msg_attrtype_str(unsigned char c); diff -r 47fff7a7af06 -r 411314907b43 extensions/app_radgw/rgw_plugins.c --- a/extensions/app_radgw/rgw_plugins.c Mon Apr 19 17:00:24 2010 +0900 +++ b/extensions/app_radgw/rgw_plugins.c Tue Apr 20 19:34:59 2010 +0900 @@ -380,7 +380,7 @@ if (ret > 0) { /* Critical error, log and exit */ - fd_log_debug("An error occurred while handling a RADIUS message, turn on DEBUG for details: %s\n", strerror(ret)); + TRACE_DEBUG(NONE, "An error occurred while handling a RADIUS message from '%s': %s\n", rgw_clients_id(cli), strerror(ret)); return ret; } @@ -438,6 +438,8 @@ /* We might define other return values with special meaning here (ret == -1, ...) for example create a new Diameter request */ + /* -1: just abord the translation with no more processing. */ + return 0; } diff -r 47fff7a7af06 -r 411314907b43 extensions/app_radgw/rgw_worker.c --- a/extensions/app_radgw/rgw_worker.c Mon Apr 19 17:00:24 2010 +0900 +++ b/extensions/app_radgw/rgw_worker.c Tue Apr 20 19:34:59 2010 +0900 @@ -247,36 +247,39 @@ /* Pass the Diameter answer to the same extensions as the request */ CHECK_FCT_DO( rgw_plg_loop_ans(pa->rad, pa->sess, ans, &rad_ans, pa->cli), goto out ); - - /* Now check what AVPs remain in the diameter answer. If AVPs with the 'M' flag are here, we have a problem... */ - CHECK_FCT_DO( fd_msg_browse(*ans, MSG_BRW_FIRST_CHILD, &avp, NULL), { avp = NULL; pb++; } ); - while (avp) { - CHECK_FCT_DO( fd_msg_avp_hdr ( avp, &ahdr ), { pb++; continue; } ); - if (ahdr->avp_flags & AVP_FLAG_MANDATORY) { - if (ahdr->avp_flags & AVP_FLAG_VENDOR) { - TRACE_DEBUG(FULL, "Remaining Mandatory Vendor AVP, code %d", ahdr->avp_code); - pb++; - } else { - switch (ahdr->avp_code) { - /* A few AVPs can be safely ignored here: */ - case DIAM_ATTR_ROUTE_RECORD: - case DIAM_ATTR_PROXY_INFO: - - - /* just ignore */ - break; - - default: - TRACE_DEBUG(FULL, "Remaining Mandatory AVP, code %d", ahdr->avp_code); - pb++; + + if (*ans != NULL) { + + /* Now check what AVPs remain in the diameter answer. If AVPs with the 'M' flag are here, we have a problem... */ + CHECK_FCT_DO( fd_msg_browse(*ans, MSG_BRW_FIRST_CHILD, &avp, NULL), { avp = NULL; pb++; } ); + while (avp) { + CHECK_FCT_DO( fd_msg_avp_hdr ( avp, &ahdr ), { pb++; continue; } ); + if (ahdr->avp_flags & AVP_FLAG_MANDATORY) { + if (ahdr->avp_flags & AVP_FLAG_VENDOR) { + TRACE_DEBUG(FULL, "Remaining Mandatory Vendor AVP, code %d", ahdr->avp_code); + pb++; + } else { + switch (ahdr->avp_code) { + /* A few AVPs can be safely ignored here: */ + case DIAM_ATTR_ROUTE_RECORD: + case DIAM_ATTR_PROXY_INFO: + + + /* just ignore */ + break; + + default: + TRACE_DEBUG(FULL, "Remaining Mandatory AVP, code %d", ahdr->avp_code); + pb++; + } } } + CHECK_FCT_DO( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL), { pb++; break; } ); } - CHECK_FCT_DO( fd_msg_browse(avp, MSG_BRW_NEXT, &avp, NULL), { pb++; break; } ); - } - - if (pb) { - TRACE_DEBUG(INFO, "[radgw] WARNING: %d mandatory AVP in the Diameter answer have not been translated to RADIUS!\n Please use plg_debug.rgwx for more information.", pb); + + if (pb) { + TRACE_DEBUG(INFO, "[radgw] WARNING: %d mandatory AVP in the Diameter answer have not been translated to RADIUS!\n Please use plg_debug.rgwx for more information.", pb); + } } /* Now try and send the RADIUS answer */ diff -r 47fff7a7af06 -r 411314907b43 extensions/app_radgw/rgwx_acct.c --- a/extensions/app_radgw/rgwx_acct.c Mon Apr 19 17:00:24 2010 +0900 +++ b/extensions/app_radgw/rgwx_acct.c Tue Apr 20 19:34:59 2010 +0900 @@ -37,26 +37,126 @@ #include "rgw_common.h" + +/* Other constants we use */ +#define AI_ACCT 3 /* Diameter Base Accounting application */ +#define CC_AC 271 /* ACR/ACA */ +#define ACV_ART_START_RECORD 2 /* START_RECORD */ +#define ACV_ART_INTERIM_RECORD 3 /* INTERIM_RECORD */ +#define ACV_ART_STOP_RECORD 4 /* STOP_RECORD */ +#define ACV_ART_AUTHORIZE_AUTHENTICATE 3 /* AUTHORIZE_AUTHENTICATE */ +#define ER_DIAMETER_LIMITED_SUCCESS 2002 + + /* The state we keep for this plugin */ struct rgwp_config { struct { + struct dict_object * Accounting_Record_Type; /* Accounting-Record-Type */ + struct dict_object * Acct_Application_Id; /* Acct-Application-Id */ + struct dict_object * Acct_Delay_Time; /* Acct-Delay-Time */ + struct dict_object * Accounting_Input_Octets; /* Accounting-Input-Octets */ + struct dict_object * Accounting_Output_Octets; /* Accounting-Output-Octets */ + struct dict_object * Accounting_Input_Packets; /* Accounting-Input-Packets */ + struct dict_object * Accounting_Output_Packets; /* Accounting-Output-Packets */ + struct dict_object * Acct_Link_Count; /* Acct-Link-Count */ + struct dict_object * Acct_Authentic; /* Acct-Authentic */ + struct dict_object * Acct_Multi_Session_Id; /* Acct-Multi-Session-Id */ + struct dict_object * Acct_Session_Id; /* Acct-Session-Id */ + struct dict_object * Acct_Session_Time; /* Acct-Session-Time */ + + struct dict_object * ARAP_Password; /* ARAP-Password */ + struct dict_object * ARAP_Security; /* ARAP-Security */ + struct dict_object * ARAP_Security_Data; /* ARAP-Security-Data */ + struct dict_object * Auth_Application_Id; /* Auth-Application-Id */ + struct dict_object * Auth_Request_Type; /* Auth-Request-Type */ + struct dict_object * Authorization_Lifetime; /* Authorization-Lifetime */ + struct dict_object * Callback_Number; /* Callback-Number */ + struct dict_object * Callback_Id; /* Callback-Id */ + struct dict_object * Called_Station_Id; /* Called-Station-Id */ + struct dict_object * Calling_Station_Id; /* Calling-Station-Id */ + struct dict_object * Class; /* Class */ + struct dict_object * CHAP_Algorithm; /* CHAP-Algorithm */ + struct dict_object * CHAP_Auth; /* CHAP-Auth */ + struct dict_object * CHAP_Challenge; /* CHAP-Challenge */ + struct dict_object * CHAP_Ident; /* CHAP-Ident */ + struct dict_object * CHAP_Response; /* CHAP-Response */ + struct dict_object * Connect_Info; /* Connect-Info */ + struct dict_object * EAP_Payload; /* EAP-Payload */ struct dict_object * Error_Message; /* Error-Message */ struct dict_object * Error_Reporting_Host; /* Error-Reporting-Host */ + struct dict_object * Event_Timestamp; /* Event-Timestamp */ struct dict_object * Failed_AVP; /* Failed-AVP */ + struct dict_object * Framed_AppleTalk_Link; /* Framed-AppleTalk-Link */ + struct dict_object * Framed_AppleTalk_Network; /* Framed-AppleTalk-Network */ + struct dict_object * Framed_AppleTalk_Zone; /* Framed-AppleTalk-Zone */ + struct dict_object * Framed_Compression; /* Framed-Compression */ + struct dict_object * Framed_IP_Address; /* Framed-IP-Address */ + struct dict_object * Framed_IP_Netmask; /* Framed-IP-Netmask */ + struct dict_object * Framed_Interface_Id; /* Framed-Interface-Id */ + struct dict_object * Framed_IPv6_Prefix; /* Framed-IPv6-Prefix */ + struct dict_object * Framed_IPX_Network; /* Framed-IPX-Network */ + struct dict_object * Framed_MTU; /* Framed-MTU */ + struct dict_object * Framed_Protocol; /* Framed-Protocol */ + struct dict_object * Framed_Pool; /* Framed-Pool */ + struct dict_object * Framed_IPv6_Route; /* Framed-IPv6-Route */ + struct dict_object * Framed_IPv6_Pool; /* Framed-IPv6-Pool */ + struct dict_object * Framed_Route; /* Framed-Route */ + struct dict_object * Framed_Routing; /* Framed-Routing */ + struct dict_object * Filter_Id; /* Filter-Id */ + struct dict_object * Idle_Timeout; /* Idle-Timeout */ + struct dict_object * Login_IP_Host; /* Login-IP-Host */ + struct dict_object * Login_IPv6_Host; /* Login-IPv6-Host */ + struct dict_object * Login_LAT_Group; /* Login-LAT-Group */ + struct dict_object * Login_LAT_Node; /* Login-LAT-Node */ + struct dict_object * Login_LAT_Port; /* Login-LAT-Port */ + struct dict_object * Login_LAT_Service; /* Login-LAT-Service */ + struct dict_object * Login_Service; /* Login-Service */ + struct dict_object * Login_TCP_Port; /* Login-TCP-Port */ + struct dict_object * NAS_Identifier; /* NAS-Identifier */ + struct dict_object * NAS_IP_Address; /* NAS-IP-Address */ + struct dict_object * NAS_IPv6_Address; /* NAS-IPv6-Address */ + struct dict_object * NAS_Port; /* NAS-Port */ + struct dict_object * NAS_Port_Id; /* NAS-Port-Id */ + struct dict_object * NAS_Port_Type; /* NAS-Port-Type */ + struct dict_object * Origin_AAA_Protocol; /* Origin-AAA-Protocol */ struct dict_object * Origin_Host; /* Origin-Host */ struct dict_object * Origin_Realm; /* Origin-Realm */ + struct dict_object * Originating_Line_Info; /* Originating-Line-Info */ + struct dict_object * Port_Limit; /* Port-Limit */ + struct dict_object * Re_Auth_Request_Type; /* Re-Auth-Request-Type */ struct dict_object * Result_Code; /* Result-Code */ + struct dict_object * Service_Type; /* Service-Type */ struct dict_object * Session_Id; /* Session-Id */ + struct dict_object * Session_Timeout; /* Session-Timeout */ struct dict_object * State; /* State */ + struct dict_object * Tunneling; /* Tunneling */ + struct dict_object * Tunnel_Type; /* Tunnel-Type */ + struct dict_object * Tunnel_Assignment_Id; /* Tunnel-Assignment-Id */ + struct dict_object * Tunnel_Medium_Type; /* Tunnel-Medium-Type */ + struct dict_object * Tunnel_Client_Endpoint; /* Tunnel-Client-Endpoint */ + struct dict_object * Tunnel_Server_Endpoint; /* Tunnel-Server-Endpoint */ + struct dict_object * Tunnel_Private_Group_Id; /* Tunnel-Private-Group-Id */ + struct dict_object * Tunnel_Preference; /* Tunnel-Preference */ + struct dict_object * Tunnel_Client_Auth_Id; /* Tunnel-Client-Auth-Id */ + struct dict_object * Tunnel_Server_Auth_Id; /* Tunnel-Server-Auth-Id */ struct dict_object * User_Name; /* User-Name */ } dict; /* cache of the dictionary objects we use */ + struct session_handler * sess_hdl; /* We store RADIUS request authenticator information in the session */ char * confstr; }; +/* The state we store in the session */ +struct sess_state { + uint8_t req_auth[16]; /* The request authenticator */ + int send_str; /* If not 0, we must send a STR when the ACA is received. */ + uint32_t term_cause; /* If not 0, the Termination-Cause to put in the STR. */ +}; + /* Initialize the plugin */ static int acct_conf_parse(char * conffile, struct rgwp_config ** state) { struct rgwp_config * new; + struct dict_object * app; TRACE_ENTRY("%p %p", conffile, state); CHECK_PARAMS( state ); @@ -67,17 +167,94 @@ new->confstr = conffile; /* Resolve all dictionary objects we use */ + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Record-Type", &new->dict.Accounting_Record_Type, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Application-Id", &new->dict.Acct_Application_Id, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Delay-Time", &new->dict.Acct_Delay_Time, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Input-Octets", &new->dict.Accounting_Input_Octets, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Output-Octets", &new->dict.Accounting_Output_Octets, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Input-Packets", &new->dict.Accounting_Input_Packets, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Output-Packets", &new->dict.Accounting_Output_Packets, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Authentic", &new->dict.Acct_Authentic, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Link-Count", &new->dict.Acct_Link_Count, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Multi-Session-Id", &new->dict.Acct_Multi_Session_Id, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Session-Id", &new->dict.Acct_Session_Id, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Session-Time", &new->dict.Acct_Session_Time, ENOENT) ); + + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "ARAP-Password", &new->dict.ARAP_Password, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "ARAP-Security", &new->dict.ARAP_Security, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "ARAP-Security-Data", &new->dict.ARAP_Security_Data, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Application-Id", &new->dict.Auth_Application_Id, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Request-Type", &new->dict.Auth_Request_Type, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Authorization-Lifetime", &new->dict.Authorization_Lifetime, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Callback-Number", &new->dict.Callback_Number, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Callback-Id", &new->dict.Callback_Id, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Called-Station-Id", &new->dict.Called_Station_Id, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Calling-Station-Id", &new->dict.Calling_Station_Id, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Class", &new->dict.Class, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Connect-Info", &new->dict.Connect_Info, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "EAP-Payload", &new->dict.EAP_Payload, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Message", &new->dict.Error_Message, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Reporting-Host", &new->dict.Error_Reporting_Host, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Event-Timestamp", &new->dict.Event_Timestamp, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Failed-AVP", &new->dict.Failed_AVP, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-AppleTalk-Link", &new->dict.Framed_AppleTalk_Link, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-AppleTalk-Network", &new->dict.Framed_AppleTalk_Network, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-AppleTalk-Zone", &new->dict.Framed_AppleTalk_Zone, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Compression", &new->dict.Framed_Compression, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IP-Address", &new->dict.Framed_IP_Address, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IP-Netmask", &new->dict.Framed_IP_Netmask, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Interface-Id", &new->dict.Framed_Interface_Id, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IPv6-Prefix", &new->dict.Framed_IPv6_Prefix, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IPX-Network", &new->dict.Framed_IPX_Network, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-MTU", &new->dict.Framed_MTU, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Protocol", &new->dict.Framed_Protocol, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Pool", &new->dict.Framed_Pool, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Route", &new->dict.Framed_Route, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IPv6-Route", &new->dict.Framed_IPv6_Route, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IPv6-Pool", &new->dict.Framed_IPv6_Pool, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Routing", &new->dict.Framed_Routing, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Filter-Id", &new->dict.Filter_Id, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Idle-Timeout", &new->dict.Idle_Timeout, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-IP-Host", &new->dict.Login_IP_Host, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-IPv6-Host", &new->dict.Login_IPv6_Host, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Group", &new->dict.Login_LAT_Group, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Node", &new->dict.Login_LAT_Node, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Port", &new->dict.Login_LAT_Port, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Service", &new->dict.Login_LAT_Service, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-Service", &new->dict.Login_Service, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-TCP-Port", &new->dict.Login_TCP_Port, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Identifier", &new->dict.NAS_Identifier, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-IP-Address", &new->dict.NAS_IP_Address, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-IPv6-Address", &new->dict.NAS_IPv6_Address, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Port", &new->dict.NAS_Port, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Port-Id", &new->dict.NAS_Port_Id, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Port-Type", &new->dict.NAS_Port_Type, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-AAA-Protocol", &new->dict.Origin_AAA_Protocol, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &new->dict.Origin_Host, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &new->dict.Origin_Realm, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Originating-Line-Info", &new->dict.Originating_Line_Info, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Port-Limit", &new->dict.Port_Limit, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Re-Auth-Request-Type", &new->dict.Re_Auth_Request_Type, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Result-Code", &new->dict.Result_Code, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Service-Type", &new->dict.Service_Type, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &new->dict.Session_Id, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Timeout", &new->dict.Session_Timeout, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "State", &new->dict.State, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunneling", &new->dict.Tunneling, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Assignment-Id", &new->dict.Tunnel_Assignment_Id, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Type", &new->dict.Tunnel_Type, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Medium-Type", &new->dict.Tunnel_Medium_Type, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Client-Endpoint", &new->dict.Tunnel_Client_Endpoint, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Server-Endpoint", &new->dict.Tunnel_Server_Endpoint, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Private-Group-Id", &new->dict.Tunnel_Private_Group_Id, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Preference", &new->dict.Tunnel_Preference, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Client-Auth-Id", &new->dict.Tunnel_Client_Auth_Id, ENOENT) ); + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Server-Auth-Id", &new->dict.Tunnel_Server_Auth_Id, ENOENT) ); CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "User-Name", &new->dict.User_Name, ENOENT) ); - TODO("Advertize application support..."); + /* This plugin provides the following Diameter authentication applications support: */ + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Diameter Base Accounting", &app, ENOENT) ); + CHECK_FCT( fd_disp_app_support ( app, NULL, 0, 1 ) ); *state = new; return 0; @@ -96,54 +273,444 @@ static int acct_rad_req( struct rgwp_config * cs, struct session * session, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli ) { int idx; + int send_str=0; + uint32_t str_cause=0; int got_id = 0; uint32_t status_type; + uint32_t termination_action = 0; + uint32_t gigawords_in=0, gigawords_out=0; + size_t nattr_used = 0; + union avp_value value; + struct avp ** avp_tun = NULL, *avp = 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 the message contains the NAS identification */ + /* + Either NAS-IP-Address or NAS-Identifier MUST be present in a + RADIUS Accounting-Request. It SHOULD contain a NAS-Port or NAS- + Port-Type attribute or both unless the service does not involve a + port or the NAS does not distinguish among its ports. + */ 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]); + uint8_t * v = (uint8_t *)(attr + 1); 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_TERMINATION_ACTION: + CHECK_PARAMS( attr->length == 6 ); + termination_action = (v[0] << 24) + | (v[1] << 16) + | (v[2] << 8) + | v[3] ; + break; + + case RADIUS_ATTR_ACCT_INPUT_GIGAWORDS: + CHECK_PARAMS( attr->length == 6 ); + gigawords_in = (v[0] << 24) + | (v[1] << 16) + | (v[2] << 8) + | v[3] ; + break; + + case RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS: + CHECK_PARAMS( attr->length == 6 ); + gigawords_out = (v[0] << 24) + | (v[1] << 16) + | (v[2] << 8) + | v[3] ; + break; } } /* Check basic information is there */ if (!got_id || radius_msg_get_attr_int32(rad_req, RADIUS_ATTR_ACCT_STATUS_TYPE, &status_type)) { - TRACE_DEBUG(INFO, "[acct.rgwx] RADIUS Account-Request did not contain a NAS ip/identifier or Acct-Status-Type attribute, reject."); + TRACE_DEBUG(INFO, "[acct.rgwx] RADIUS Account-Request from %s did not contain a NAS ip/identifier or Acct-Status-Type attribute, reject.", rgw_clients_id(cli)); return EINVAL; } - /* Handle the Accounting-On/Off case: nothing to do, just reply OK, since Diameter does not support this */ - if ((status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON) || (status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF)) { + /* + -- RFC2866: + In Accounting-Request Packets, the Authenticator value is a 16 + octet MD5 [5] checksum, called the Request Authenticator. + + The NAS and RADIUS accounting server share a secret. The Request + Authenticator field in Accounting-Request packets contains a one- + way MD5 hash calculated over a stream of octets consisting of the + Code + Identifier + Length + 16 zero octets + request attributes + + shared secret (where + indicates concatenation). The 16 octet MD5 + hash value is stored in the Authenticator field of the + Accounting-Request packet. + + Note that the Request Authenticator of an Accounting-Request can + not be done the same way as the Request Authenticator of a RADIUS + Access-Request, because there is no User-Password attribute in an + Accounting-Request. + + + -- RFC5080: + The Request Authenticator field MUST contain the correct data, as + given by the above calculation. Invalid packets are silently + discarded. Note that some early implementations always set the + Request Authenticator to all zeros. New implementations of RADIUS + clients MUST use the above algorithm to calculate the Request + Authenticator field. New RADIUS server implementations MUST + silently discard invalid packets. + + */ + { + uint8_t save[MD5_MAC_LEN]; + uint8_t * secret; + size_t secret_len; + + /* Get the shared secret */ + CHECK_FCT(rgw_clients_getkey(cli, &secret, &secret_len)); + + /* Copy the received Request Authenticator */ + memcpy(&save[0], &rad_req->hdr->authenticator[0], MD5_MAC_LEN); + + /* Compute the same authenticator */ + radius_msg_finish_acct(rad_req, secret, secret_len); + + /* And now compare with the received value */ + if (memcmp(&save[0], &rad_req->hdr->authenticator[0], MD5_MAC_LEN)) { + /* Invalid authenticator */ + TRACE_DEBUG_BUFFER(FULL+1, "Received ReqAuth: ", &save[0], MD5_MAC_LEN, "" ); + TRACE_DEBUG_BUFFER(FULL+1, "Expected ReqAuth: ", &rad_req->hdr->authenticator[0], MD5_MAC_LEN, "" ); + TRACE_DEBUG(INFO, "[acct.rgwx] Invalid Request Authenticator in Account-Request from %s, discarding the message.", rgw_clients_id(cli)); + return EINVAL; + } + } + + + /* Handle the Accounting-On case: nothing to do, just reply OK */ + if (status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON) { TRACE_DEBUG(FULL, "[acct.rgwx] Received Accounting-On Acct-Status-Type attribute, replying without translation to Diameter."); CHECK_MALLOC( *rad_ans = radius_msg_new(RADIUS_CODE_ACCOUNTING_RESPONSE, rad_req->hdr->identifier) ); return -2; } - /* Other cases */ - return ENOTSUP; - /* - - 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 (status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF) { + TRACE_DEBUG(FULL, "[acct.rgwx] Received Accounting-Off Acct-Status-Type attribute, we must terminate all active sessions."); + TODO("RADIUS side is rebooting, send STR on all sessions?"); + return ENOTSUP; + } + + /* Add the command code */ + { + struct msg_hdr * header = NULL; + CHECK_FCT( fd_msg_hdr ( *diam_fw, &header ) ); + header->msg_flags = CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE; + header->msg_code = CC_AC; + header->msg_appl = AI_ACCT; + + /* Add the Acct-Application-Id */ + CHECK_FCT( fd_msg_avp_new ( cs->dict.Acct_Application_Id, 0, &avp ) ); + value.i32 = header->msg_appl; + CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); + CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); + } + + /* Convert the RADIUS attributes, as they appear in the message */ + 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) { + /* + Any attribute valid in a RADIUS Access-Request or Access-Accept + packet is valid in a RADIUS Accounting-Request packet, except that + the following attributes MUST NOT be present in an Accounting- + Request: User-Password, CHAP-Password, Reply-Message, State. */ - - /* - - If the Accounting message contains an Acct-Termination-Cause - attribute, it should be translated to the equivalent - Termination-Cause AVP value. (see below) - */ - + case RADIUS_ATTR_USER_PASSWORD: + case RADIUS_ATTR_CHAP_PASSWORD: + case RADIUS_ATTR_REPLY_MESSAGE: + case RADIUS_ATTR_STATE: + case RADIUS_ATTR_MESSAGE_AUTHENTICATOR: + case RADIUS_ATTR_EAP_MESSAGE: + TRACE_DEBUG(INFO, "[acct.rgwx] RADIUS Account-Request contains a forbidden attribute (%hhd), reject.", attr->type); + return EINVAL; + + + /* This macro converts a RADIUS attribute to a Diameter AVP of type OctetString */ + #define CONV2DIAM_STR( _dictobj_ ) \ + CHECK_PARAMS( attr->length >= 2 ); \ + /* Create the AVP with the specified dictionary model */ \ + CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \ + value.os.len = attr->length - 2; \ + value.os.data = (unsigned char *)(attr + 1); \ + CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \ + /* Add the AVP in the Diameter message. */ \ + CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); \ + + /* Same thing, for scalar AVPs of 32 bits */ + #define CONV2DIAM_32B( _dictobj_ ) \ + CHECK_PARAMS( attr->length == 6 ); \ + CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \ + { \ + uint8_t * v = (uint8_t *)(attr + 1); \ + value.u32 = (v[0] << 24) \ + | (v[1] << 16) \ + | (v[2] << 8) \ + | v[3] ; \ + } \ + CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \ + CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); \ + + /* And the 64b version */ + #define CONV2DIAM_64B( _dictobj_ ) \ + CHECK_PARAMS( attr->length == 10); \ + CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 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( fd_msg_avp_setvalue ( avp, &value ) ); \ + CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); \ + + + /* Attributes as listed in RFC2866, section 5.13 and RFC4005, section 10.2.1 */ + case RADIUS_ATTR_USER_NAME: + CONV2DIAM_STR( User_Name ); + break; + + case RADIUS_ATTR_NAS_IP_ADDRESS: + CONV2DIAM_STR( NAS_IP_Address ); + break; + + case RADIUS_ATTR_NAS_PORT: + CONV2DIAM_32B( NAS_Port ); + break; + + case RADIUS_ATTR_SERVICE_TYPE: + CONV2DIAM_32B( Service_Type ); + break; + + case RADIUS_ATTR_FRAMED_PROTOCOL: + CONV2DIAM_32B( Framed_Protocol ); + break; + + case RADIUS_ATTR_FRAMED_IP_ADDRESS: + CONV2DIAM_STR( Framed_IP_Address ); + break; + + case RADIUS_ATTR_FRAMED_IP_NETMASK: + CONV2DIAM_STR( Framed_IP_Netmask ); + break; + + case RADIUS_ATTR_FRAMED_ROUTING: + CONV2DIAM_32B( Framed_Routing ); + break; + + case RADIUS_ATTR_FILTER_ID: + CONV2DIAM_STR( Filter_Id ); + break; + + case RADIUS_ATTR_FRAMED_MTU: + CONV2DIAM_32B( Framed_MTU ); + break; + + case RADIUS_ATTR_FRAMED_COMPRESSION: + CONV2DIAM_32B( Framed_Compression ); + break; + + case RADIUS_ATTR_LOGIN_IP_HOST: + CONV2DIAM_STR( Login_IP_Host ); + break; + + case RADIUS_ATTR_LOGIN_SERVICE: + CONV2DIAM_32B( Login_Service ); + break; + + case RADIUS_ATTR_LOGIN_TCP_PORT: + CONV2DIAM_32B( Login_TCP_Port ); + break; + + case RADIUS_ATTR_CALLBACK_NUMBER: + CONV2DIAM_STR( Callback_Number ); + break; + + case RADIUS_ATTR_CALLBACK_ID: + CONV2DIAM_STR( Callback_Id ); + break; + + case RADIUS_ATTR_FRAMED_ROUTE: + CONV2DIAM_STR( Framed_Route ); + break; + + case RADIUS_ATTR_FRAMED_IPX_NETWORK: + CONV2DIAM_32B( Framed_IPX_Network ); + break; + + case RADIUS_ATTR_CLASS: + CONV2DIAM_STR( Class ); + break; + + case RADIUS_ATTR_VENDOR_SPECIFIC: + 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; + + left = attr->length - 6; + vtlv = (struct radius_attr_vendor *)c; + + while ((left >= 2) && (vtlv->vendor_length <= left)) { + /* Search our dictionary for corresponding Vendor's AVP */ + struct dict_avp_request req; + struct dict_object * avp_model = NULL; + memset(&req, 0, sizeof(struct dict_avp_request)); + req.avp_vendor = vendor_id; + req.avp_code = vtlv->vendor_type; + + CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE_AND_VENDOR, &req, &avp_model, 0) ); + if (!avp_model) { + TRACE_DEBUG(FULL, "Unknown attribute (vendor 0x%x, code 0x%x) ignored.", req.avp_vendor, req.avp_code); + } else { + CHECK_FCT( fd_msg_avp_new ( avp_model, 0, &avp ) ); + value.os.len = vtlv->vendor_length - 2; + value.os.data = (unsigned char *)(vtlv + 1); + CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); + CHECK_FCT( fd_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; + + case RADIUS_ATTR_SESSION_TIMEOUT: + /* Translation depends on Termination-Action : rfc4005#section-9.2.1 */ + if (termination_action != RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) { + CONV2DIAM_32B( Session_Timeout ); + } else { + CONV2DIAM_32B( Authorization_Lifetime ); + /* And add this additional AVP */ + CHECK_FCT( fd_msg_avp_new ( cs->dict.Re_Auth_Request_Type, 0, &avp ) ); + value.u32 = ACV_ART_AUTHORIZE_AUTHENTICATE; + CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); + CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); + } + break; + + case RADIUS_ATTR_IDLE_TIMEOUT: + CONV2DIAM_32B( Idle_Timeout ); + break; + + case RADIUS_ATTR_TERMINATION_ACTION: + /* Just remove */ + break; + + case RADIUS_ATTR_CALLED_STATION_ID: + CONV2DIAM_STR( Called_Station_Id ); + break; + + case RADIUS_ATTR_CALLING_STATION_ID: + CONV2DIAM_STR( Calling_Station_Id ); + break; + + case RADIUS_ATTR_NAS_IDENTIFIER: + CONV2DIAM_STR( NAS_Identifier ); + break; + + /* Proxy-State is handled by echo_drop.rgwx plugin, we ignore it here */ + + case RADIUS_ATTR_LOGIN_LAT_SERVICE: + CONV2DIAM_STR( Login_LAT_Service ); + break; + + case RADIUS_ATTR_LOGIN_LAT_NODE: + CONV2DIAM_STR( Login_LAT_Node ); + break; + + case RADIUS_ATTR_LOGIN_LAT_GROUP: + CONV2DIAM_STR( Login_LAT_Group ); + break; + + case RADIUS_ATTR_FRAMED_APPLETALK_LINK: + CONV2DIAM_32B( Framed_AppleTalk_Link ); + break; + + case RADIUS_ATTR_FRAMED_APPLETALK_NETWORK: + CONV2DIAM_32B( Framed_AppleTalk_Network ); + break; + + case RADIUS_ATTR_FRAMED_APPLETALK_ZONE: + CONV2DIAM_STR( Framed_AppleTalk_Zone ); + break; + + case RADIUS_ATTR_ACCT_STATUS_TYPE: + /* + - 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. + */ + switch (status_type) { + case RADIUS_ACCT_STATUS_TYPE_START: + value.u32 = ACV_ART_START_RECORD; + break; + case RADIUS_ACCT_STATUS_TYPE_STOP: + value.u32 = ACV_ART_STOP_RECORD; + send_str = 1; /* Register this info in the session */ + break; + case RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE: + value.u32 = ACV_ART_INTERIM_RECORD; + break; + default: + TRACE_DEBUG(INFO, "Unknown RADIUS_ATTR_ACCT_STATUS_TYPE value %d, abording...", status_type); + return ENOTSUP; + } + CHECK_FCT( fd_msg_avp_new ( cs->dict.Accounting_Record_Type, 0, &avp ) ); + CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); + CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); + break; + + case RADIUS_ATTR_ACCT_DELAY_TIME: + CONV2DIAM_32B( Acct_Delay_Time ); + break; + /* - If the RADIUS message contains the Accounting-Input-Octets, Accounting-Input-Packets, Accounting-Output-Octets, or @@ -153,14 +720,486 @@ these must be used to properly compute the Diameter accounting AVPs. */ + case RADIUS_ATTR_ACCT_INPUT_OCTETS: + memset(&value, 0, sizeof(value)); + { + uint8_t * v = (uint8_t *)(attr + 1); + value.u64 = (v[0] << 24) + | (v[1] << 16) + | (v[2] << 8) + | v[3] ; + } + value.u64 += ((uint64_t)gigawords_in << 32); + CHECK_FCT( fd_msg_avp_new ( cs->dict.Accounting_Input_Octets, 0, &avp ) ); + CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); + CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); + break; + + case RADIUS_ATTR_ACCT_OUTPUT_OCTETS: + memset(&value, 0, sizeof(value)); + { + uint8_t * v = (uint8_t *)(attr + 1); + value.u64 = (v[0] << 24) + | (v[1] << 16) + | (v[2] << 8) + | v[3] ; + } + value.u64 += ((uint64_t)gigawords_out << 32); + CHECK_FCT( fd_msg_avp_new ( cs->dict.Accounting_Output_Octets, 0, &avp ) ); + CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); + CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); + break; + + case RADIUS_ATTR_ACCT_SESSION_ID: + CONV2DIAM_STR( Acct_Session_Id ); + break; + + case RADIUS_ATTR_ACCT_AUTHENTIC: + CONV2DIAM_32B( Acct_Authentic ); + break; + + case RADIUS_ATTR_ACCT_SESSION_TIME: + CONV2DIAM_32B( Acct_Session_Time ); + break; + + case RADIUS_ATTR_ACCT_INPUT_PACKETS: + memset(&value, 0, sizeof(value)); + { + uint8_t * v = (uint8_t *)(attr + 1); + value.u64 = (v[0] << 24) + | (v[1] << 16) + | (v[2] << 8) + | v[3] ; + } + /* value.u64 += (gigawords_in << 32); */ + CHECK_FCT( fd_msg_avp_new ( cs->dict.Accounting_Input_Packets, 0, &avp ) ); + CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); + CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); + break; + + case RADIUS_ATTR_ACCT_OUTPUT_PACKETS: + memset(&value, 0, sizeof(value)); + { + uint8_t * v = (uint8_t *)(attr + 1); + value.u64 = (v[0] << 24) + | (v[1] << 16) + | (v[2] << 8) + | v[3] ; + } + /* value.u64 += (gigawords_out << 32); */ + CHECK_FCT( fd_msg_avp_new ( cs->dict.Accounting_Output_Packets, 0, &avp ) ); + CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); + CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); + break; + + /* + - If the Accounting message contains an Acct-Termination-Cause + attribute, it should be translated to the equivalent + Termination-Cause AVP value. + */ + case RADIUS_ATTR_ACCT_TERMINATE_CAUSE: + /* rfc4005#section-9.3.5 */ + { + uint8_t * v = (uint8_t *)(attr + 1); + str_cause = (v[0] << 24) + | (v[1] << 16) + | (v[2] << 8) + | v[3] ; + } + str_cause += 10; /* This seems to be the rule, we can modify later if needed */ + break; + + case RADIUS_ATTR_ACCT_MULTI_SESSION_ID: + CONV2DIAM_STR( Acct_Multi_Session_Id ); + break; + + case RADIUS_ATTR_ACCT_LINK_COUNT: + CONV2DIAM_32B( Acct_Link_Count ); + break; + + /* CHAP-Challenge is not present in Accounting-Request */ + + case RADIUS_ATTR_NAS_PORT_TYPE: + CONV2DIAM_32B( NAS_Port_Type ); + break; + + case RADIUS_ATTR_PORT_LIMIT: + CONV2DIAM_32B( Port_Limit ); + break; + + case RADIUS_ATTR_LOGIN_LAT_PORT: + CONV2DIAM_STR( Login_LAT_Port ); + break; + + /* RFC 3162 */ + case RADIUS_ATTR_NAS_IPV6_ADDRESS: + CONV2DIAM_STR( NAS_IPv6_Address ); + break; + + case RADIUS_ATTR_FRAMED_INTERFACE_ID: + CONV2DIAM_64B( Framed_Interface_Id ); + break; + + case RADIUS_ATTR_FRAMED_IPV6_PREFIX: + CONV2DIAM_STR( Framed_IPv6_Prefix ); + break; + + case RADIUS_ATTR_LOGIN_IPV6_HOST: + CONV2DIAM_STR( Login_IPv6_Host ); + break; + + case RADIUS_ATTR_FRAMED_IPV6_ROUTE: + CONV2DIAM_STR( Framed_IPv6_Route ); + break; + + case RADIUS_ATTR_FRAMED_IPV6_POOL: + CONV2DIAM_STR( Framed_IPv6_Pool ); + break; + + /* RFC 2868 */ + /* Prepare the top-level Tunneling AVP for each tag values, as needed, and add to the Diameter message. + This macro is called when an AVP is added inside the group, so we will not have empty grouped AVPs */ + #define AVP_TUN_PREPARE() { \ + if (avp_tun == NULL) { \ + CHECK_MALLOC( avp_tun = calloc(sizeof(struct avp *), 32 ) ); \ + } \ + tag = *(uint8_t *)(attr + 1); \ + if (tag > 0x1F) tag = 0; \ + if (avp_tun[tag] == NULL) { \ + CHECK_FCT( fd_msg_avp_new ( cs->dict.Tunneling, 0, &avp_tun[tag] ) ); \ + CHECK_FCT( fd_msg_avp_add (*diam_fw, MSG_BRW_LAST_CHILD, avp_tun[tag]));\ + } \ + } + + /* Convert an attribute to an OctetString AVP and add inside the Tunneling AVP corresponding to the tag */ + #define CONV2DIAM_TUN_STR( _dictobj_ ) { \ + uint8_t tag; \ + CHECK_PARAMS( attr->length >= 3); \ + AVP_TUN_PREPARE(); \ + CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \ + value.os.len = attr->length - (tag ? 3 : 2); \ + value.os.data = ((unsigned char *)(attr + 1)) + (tag ? 1 : 0); \ + CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \ + CHECK_FCT( fd_msg_avp_add ( avp_tun[tag], MSG_BRW_LAST_CHILD, avp) ); \ + } + + /* Convert an attribute to a scalar AVP and add inside the Tunneling AVP corresponding to the tag */ + #define CONV2DIAM_TUN_24B( _dictobj_ ) { \ + uint8_t tag; \ + CHECK_PARAMS( attr->length == 6); \ + AVP_TUN_PREPARE(); \ + CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \ + { \ + uint8_t * v = (uint8_t *)(attr + 1); \ + value.u32 = (v[1] << 16) | (v[2] <<8) | v[3] ; \ + } \ + CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \ + CHECK_FCT( fd_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 unpredictable here.. + */ + case RADIUS_ATTR_TUNNEL_TYPE: + CONV2DIAM_TUN_24B( Tunnel_Type ); + break; + + case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE: + CONV2DIAM_TUN_24B( Tunnel_Medium_Type ); + break; + + case RADIUS_ATTR_TUNNEL_CLIENT_ENDPOINT: + CONV2DIAM_TUN_STR( Tunnel_Client_Endpoint ); + break; + + case RADIUS_ATTR_TUNNEL_SERVER_ENDPOINT: + CONV2DIAM_TUN_STR( Tunnel_Server_Endpoint ); + break; + + /* Tunnel-Password never present in an Accounting-Request */ + + case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID: + CONV2DIAM_TUN_STR( Tunnel_Private_Group_Id ); + break; + + case RADIUS_ATTR_TUNNEL_ASSIGNMENT_ID: + CONV2DIAM_TUN_STR( Tunnel_Assignment_Id ); + break; + + /* Tunnel-Reference never present in an Accounting-Request */ + + case RADIUS_ATTR_TUNNEL_CLIENT_AUTH_ID: + CONV2DIAM_TUN_STR( Tunnel_Client_Auth_Id ); + break; + + case RADIUS_ATTR_TUNNEL_SERVER_AUTH_ID: + CONV2DIAM_TUN_STR( Tunnel_Server_Auth_Id ); + break; + + /* RFC 2869 */ + /* + Acct-Input-Gigawords, Acct-Output- + Gigawords, Event-Timestamp, and NAS-Port-Id may have 0-1 instances in + an Accounting-Request packet. Connect-Info may have 0+ instances in + an Accounting-Request packet. The other attributes added in this + document must not be present in an Accounting-Request. + */ + case RADIUS_ATTR_ACCT_INPUT_GIGAWORDS: + break; /* we already saved the value in gigawords_in */ + + case RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS: + break; /* we already saved the value in gigawords_out */ + + case RADIUS_ATTR_EVENT_TIMESTAMP: + /* RADIUS: + The Value field is four octets encoding an unsigned integer with + the number of seconds since January 1, 1970 00:00 UTC. + Diameter: + The Time format is derived from the OctetString AVP Base Format. + The string MUST contain four octets, in the same format as the + first four bytes are in the NTP timestamp format. The NTP + Timestamp format is defined in Chapter 3 of [RFC4330]. + + This represents the number of seconds since 0h on 1 January 1900 + with respect to the Coordinated Universal Time (UTC). + + -- RFC4330: + NTP timestamps are represented as a 64-bit unsigned + fixed-point number, in seconds relative to 0h on 1 January 1900. The + integer part is in the first 32 bits, and the fraction part in the + last 32 bits. In the fraction part, the non-significant low-order + bits are not specified and are ordinarily set to 0. + */ + { + uint32_t ts; + + uint8_t * v = (uint8_t *)(attr + 1); + /* Read the RADIUS attribute value */ + ts = (v[0] << 24) + | (v[1] << 16) + | (v[2] << 8) + | v[3] ; + + /* Add the 70 missing years */ + ts += 2208988800U; /* 60 * 60 * 24 * ( 365 * 70 + 17 ) */ + + /* Convert to network byte order */ + ts = htonl(ts); + + /* Diameter Time datatype is derived from OctetString */ + value.os.data = (void *) &ts; + value.os.len = sizeof(uint32_t); + + CHECK_FCT( fd_msg_avp_new ( cs->dict.Event_Timestamp, 0, &avp ) ); + CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); + CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); + } + break; + + case RADIUS_ATTR_NAS_PORT_ID: + CONV2DIAM_STR( NAS_Port_Id ); + break; + + case RADIUS_ATTR_CONNECT_INFO: + CONV2DIAM_STR( Connect_Info ); + break; + + case RADIUS_ATTR_FRAMED_POOL: /* To follow the IPv6 version */ + CONV2DIAM_STR( Framed_Pool ); + break; + + + /* RFC 3579 */ + /* + The EAP-Message and Message-Authenticator attributes specified in + this document MUST NOT be present in an Accounting-Request. + */ + case RADIUS_ATTR_ORIGINATING_LINE_INFO: + CONV2DIAM_STR( Originating_Line_Info ); + break; + + /* Default */ + default: /* unknown attribute */ + /* We just keep the attribute in the RADIUS message */ + rad_req->attr_pos[nattr_used++] = rad_req->attr_pos[idx]; + } + } + + /* Update the radius message to remove all handled attributes */ + rad_req->attr_used = nattr_used; + + /* Store useful information in the session */ + { + struct sess_state * st; + CHECK_PARAMS(session); + + CHECK_MALLOC( st = malloc(sizeof(struct sess_state)) ); + memset(st, 0, sizeof(struct sess_state)); + memcpy(&st->req_auth, &rad_req->hdr->authenticator[0], 16); + st->send_str = send_str; + st->term_cause = str_cause; + CHECK_FCT( fd_sess_state_store( cs->sess_hdl, session, &st ) ); + } + + return 0; } static int acct_diam_ans( struct rgwp_config * cs, struct session * session, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli ) { + struct sess_state * st = NULL; + struct avp *avp, *next; + struct avp_hdr *ahdr, *sid, *oh, *or; + TRACE_ENTRY("%p %p %p %p %p", cs, session, diam_ans, rad_fw, cli); CHECK_PARAMS(cs); + + if (session) { + CHECK_FCT( fd_sess_state_retrieve( cs->sess_hdl, session, &st ) ); + } + + if (!st) { + TRACE_DEBUG(INFO, "Received an ACA without corresponding session information, cannot translate to RADIUS"); + return EINVAL; + } + + /* Search these AVPs first */ + CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Session_Id, &avp) ); + CHECK_FCT( fd_msg_avp_hdr ( avp, &sid ) ); + + CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Origin_Host, &avp) ); + CHECK_FCT( fd_msg_avp_hdr ( avp, &oh ) ); - return ENOTSUP; + CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Origin_Realm, &avp) ); + CHECK_FCT( fd_msg_avp_hdr ( avp, &or ) ); + + /* Check the Diameter error code */ + CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Result_Code, &avp) ); + ASSERT( avp ); /* otherwise the message should have been discarded a lot earlier because of ABNF */ + CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) ); + switch (ahdr->avp_value->u32) { + case ER_DIAMETER_SUCCESS: + case ER_DIAMETER_LIMITED_SUCCESS: + (*rad_fw)->hdr->code = RADIUS_CODE_ACCOUNTING_RESPONSE; + break; + + default: + fd_log_debug("[auth.rgwx] Received Diameter answer with error code '%d' from server '%.*s', session %.*s, not translating into Accounting-Response\n", + ahdr->avp_value->u32, + oh->avp_value->os.len, oh->avp_value->os.data, + sid->avp_value->os.len, sid->avp_value->os.data); + CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Error_Message, &avp) ); + if (avp) { + CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) ); + fd_log_debug("[auth.rgwx] Error-Message content: '%.*s'\n", + ahdr->avp_value->os.len, ahdr->avp_value->os.data); + } + CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Error_Reporting_Host, &avp) ); + if (avp) { + CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) ); + fd_log_debug("[auth.rgwx] Error-Reporting-Host: '%.*s'\n", + ahdr->avp_value->os.len, ahdr->avp_value->os.data); + } + CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Failed_AVP, &avp) ); + if (avp) { + fd_log_debug("[auth.rgwx] Failed-AVP was included in the message.\n"); + /* Dump its content ? */ + } + return -1; + } + /* Remove this Result-Code avp */ + CHECK_FCT( fd_msg_free( avp ) ); + + /* If it was a response to a STOP record, we must send an STR for this session */ + if (st->send_str) { + TODO("Send STR, including sid, [Dest-Host=oh,] Dest-Realm=or, Term-Cause=st->term_cause... Register to receive the answer."); + } + + + /* + No attributes should be found in + Accounting-Response packets except Proxy-State and possibly Vendor- + Specific. + */ + + /* Now loop in the list of AVPs and convert those that we know how */ + CHECK_FCT( fd_msg_browse(*diam_ans, MSG_BRW_FIRST_CHILD, &next, NULL) ); + + while (next) { + int handled = 1; + avp = next; + CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &next, NULL) ); + + CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) ); + + if (!(ahdr->avp_flags & AVP_FLAG_VENDOR)) { + switch (ahdr->avp_code) { + /* Based on RFC4005, sec 3.10 */ + case DIAM_ATTR_SESSION_ID: + case DIAM_ATTR_ORIGIN_HOST: + case DIAM_ATTR_ORIGIN_REALM: + case DIAM_ATTR_ACCOUNTING_RECORD_TYPE: + case DIAM_ATTR_ACCOUNTING_RECORD_NUMBER: + case DIAM_ATTR_ACCT_APPLICATION_ID: + case DIAM_ATTR_VENDOR_SPECIFIC_APPLICATION_ID: + case DIAM_ATTR_USER_NAME: + case DIAM_ATTR_ACCOUNTING_SUB_SESSION_ID: + case DIAM_ATTR_ACCT_SESSION_ID: + case DIAM_ATTR_ACCT_MULTI_SESSION_ID: + case DIAM_ATTR_EVENT_TIMESTAMP: + case DIAM_ATTR_ORIGIN_AAA_PROTOCOL: + case DIAM_ATTR_ORIGIN_STATE_ID: + case DIAM_ATTR_NAS_IDENTIFIER: + case DIAM_ATTR_NAS_IP_ADDRESS: + case DIAM_ATTR_NAS_IPV6_ADDRESS: + case DIAM_ATTR_NAS_PORT: + case DIAM_ATTR_NAS_PORT_ID: + case DIAM_ATTR_NAS_PORT_TYPE: + case DIAM_ATTR_SERVICE_TYPE: + case DIAM_ATTR_TERMINATION_CAUSE: + case DIAM_ATTR_ACCOUNTING_REALTIME_REQUIRED: + case DIAM_ATTR_ACCT_INTERIM_INTERVAL: + case DIAM_ATTR_CLASS: + /* We just remove these AVP, they are not expected in RADIUS client */ + break; + + default: + /* Leave the AVP in the message for further treatment */ + handled = 0; + } + } else { + /* Vendor-specific AVPs */ + switch (ahdr->avp_vendor) { + + default: /* unknown vendor */ + handled = 0; + } + } + + if (handled) { + CHECK_FCT( fd_msg_free( avp ) ); + } + } + + /* + The Authenticator field in an Accounting-Response packet is called + the Response Authenticator, and contains a one-way MD5 hash + calculated over a stream of octets consisting of the Accounting- + Response Code, Identifier, Length, the Request Authenticator field + from the Accounting-Request packet being replied to, and the + response attributes if any, followed by the shared secret. The + resulting 16 octet MD5 hash value is stored in the Authenticator + field of the Accounting-Response packet. + + -- done in radius_msg_finish_srv + */ + + return 0; } /* The exported symbol */ diff -r 47fff7a7af06 -r 411314907b43 extensions/app_radgw/rgwx_auth.c --- a/extensions/app_radgw/rgwx_auth.c Mon Apr 19 17:00:24 2010 +0900 +++ b/extensions/app_radgw/rgwx_auth.c Tue Apr 20 19:34:59 2010 +0900 @@ -46,7 +46,6 @@ #define AI_EAP 5 /* Diameter EAP application */ #define CC_AA 265 /* AAR */ #define CC_DIAMETER_EAP 268 /* DER */ -#define CC_DIAMETER_EAP 268 /* DER */ #define ACV_ART_AUTHORIZE_AUTHENTICATE 3 /* AUTHORIZE_AUTHENTICATE */ #define ACV_OAP_RADIUS 1 /* RADIUS */ #define ACV_ASS_STATE_MAINTAINED 0 /* STATE_MAINTAINED */ @@ -55,7 +54,6 @@ /* The state we keep for this plugin */ struct rgwp_config { - struct session_handler * sess_hdl; /* We store RADIUS request authenticator information in the session */ struct { struct dict_object * ARAP_Password; /* ARAP-Password */ struct dict_object * ARAP_Security; /* ARAP-Security */ @@ -119,6 +117,7 @@ struct dict_object * User_Password; /* User-Password */ } dict; /* cache of the dictionary objects we use */ + struct session_handler * sess_hdl; /* We store RADIUS request authenticator information in the session */ char * confstr; }; @@ -767,13 +766,13 @@ tag = *(uint8_t *)(attr + 1); \ if (tag > 0x1F) tag = 0; \ if (avp_tun[tag] == NULL) { \ - CHECK_FCT( fd_msg_avp_new ( cs->dict.Tunneling, 0, &avp_tun[tag] ) ); \ + CHECK_FCT( fd_msg_avp_new ( cs->dict.Tunneling, 0, &avp_tun[tag] ) ); \ CHECK_FCT( fd_msg_avp_add (*diam_fw, MSG_BRW_LAST_CHILD, avp_tun[tag]));\ } \ } /* Convert an attribute to an OctetString AVP and add inside the Tunneling AVP corresponding to the tag */ - #define CONV2DIAM_TUN_STR( _dictobj_ ) { \ + #define CONV2DIAM_TUN_STR( _dictobj_ ) { \ uint8_t tag; \ CHECK_PARAMS( attr->length >= 3); \ AVP_TUN_PREPARE(); \ @@ -785,7 +784,7 @@ } /* Convert an attribute to a scalar AVP and add inside the Tunneling AVP corresponding to the tag */ - #define CONV2DIAM_TUN_24B( _dictobj_ ) { \ + #define CONV2DIAM_TUN_24B( _dictobj_ ) { \ uint8_t tag; \ CHECK_PARAMS( attr->length == 6); \ AVP_TUN_PREPARE(); \ @@ -915,7 +914,6 @@ static int auth_diam_ans( struct rgwp_config * cs, struct session * session, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli ) { - struct msg_hdr *mhdr; struct avp *avp, *next, *avp_x, *avp_y, *asid, *aoh; struct avp_hdr *ahdr, *sid, *oh; char buf[254]; /* to store some attributes values (with final '\0') */ @@ -931,8 +929,6 @@ CHECK_FCT( fd_sess_state_retrieve( cs->sess_hdl, session, &req_auth ) ); } - CHECK_FCT( fd_msg_hdr( *diam_ans, &mhdr ) ); - /* - If the Diameter Command-Code is set to AA-Answer and the Result-Code AVP is set to DIAMETER_MULTI_ROUND_AUTH, the @@ -1465,8 +1461,8 @@ CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_SERVER_AUTH_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1); break; - case DIAM_ATTR_TUNNEL_ASSIGNEMENT_ID: - CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_ASSIGNEMENT_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1); + case DIAM_ATTR_TUNNEL_ASSIGNMENT_ID: + CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_ASSIGNMENT_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1); break; case DIAM_ATTR_TUNNEL_PASSWORD: