blob: f9f1480e4ad847f016909aee0485aaee0513be34 [file] [log] [blame]
Brian Waters13d96012017-12-08 16:53:31 -06001/*********************************************************************************************************
2* Software License Agreement (BSD License) *
3* Author: Sebastien Decugis <sdecugis@freediameter.net> *
4* *
5* Copyright (c) 2013, WIDE Project and NICT *
6* All rights reserved. *
7* *
8* Redistribution and use of this software in source and binary forms, with or without modification, are *
9* permitted provided that the following conditions are met: *
10* *
11* * Redistributions of source code must retain the above *
12* copyright notice, this list of conditions and the *
13* following disclaimer. *
14* *
15* * Redistributions in binary form must reproduce the above *
16* copyright notice, this list of conditions and the *
17* following disclaimer in the documentation and/or other *
18* materials provided with the distribution. *
19* *
20* * Neither the name of the WIDE Project or NICT nor the *
21* names of its contributors may be used to endorse or *
22* promote products derived from this software without *
23* specific prior written permission of WIDE Project and *
24* NICT. *
25* *
26* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
27* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
28* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
29* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
30* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS *
31* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
32* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF *
33* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
34*********************************************************************************************************/
35
36/* RADIUS Accounting-Request messages translation plugin */
37
38#include "rgw_common.h"
39
40
41/* Other constants we use */
42#define AI_ACCT 3 /* Diameter Base Accounting application */
43#define CC_AC 271 /* ACR/ACA */
44#define ACV_ART_START_RECORD 2 /* START_RECORD */
45#define ACV_ART_INTERIM_RECORD 3 /* INTERIM_RECORD */
46#define ACV_ART_STOP_RECORD 4 /* STOP_RECORD */
47#define ACV_ART_AUTHORIZE_AUTHENTICATE 3 /* AUTHORIZE_AUTHENTICATE */
48
49
50/* The state we keep for this plugin */
51struct rgwp_config {
52 struct {
53 struct dict_object * Accounting_Record_Number; /* Accounting-Record-Number */
54 struct dict_object * Accounting_Record_Type; /* Accounting-Record-Type */
55 struct dict_object * Acct_Application_Id; /* Acct-Application-Id */
56 struct dict_object * Acct_Delay_Time; /* Acct-Delay-Time */
57 struct dict_object * Accounting_Input_Octets; /* Accounting-Input-Octets */
58 struct dict_object * Accounting_Output_Octets; /* Accounting-Output-Octets */
59 struct dict_object * Accounting_Input_Packets; /* Accounting-Input-Packets */
60 struct dict_object * Accounting_Output_Packets; /* Accounting-Output-Packets */
61 struct dict_object * Acct_Link_Count; /* Acct-Link-Count */
62 struct dict_object * Acct_Authentic; /* Acct-Authentic */
63 struct dict_object * Acct_Multi_Session_Id; /* Acct-Multi-Session-Id */
64 struct dict_object * Acct_Session_Id; /* Acct-Session-Id */
65 struct dict_object * Acct_Session_Time; /* Acct-Session-Time */
66
67 struct dict_object * ARAP_Password; /* ARAP-Password */
68 struct dict_object * ARAP_Security; /* ARAP-Security */
69 struct dict_object * ARAP_Security_Data; /* ARAP-Security-Data */
70 struct dict_object * Auth_Application_Id; /* Auth-Application-Id */
71 struct dict_object * Auth_Request_Type; /* Auth-Request-Type */
72 struct dict_object * Authorization_Lifetime; /* Authorization-Lifetime */
73 struct dict_object * Callback_Number; /* Callback-Number */
74 struct dict_object * Callback_Id; /* Callback-Id */
75 struct dict_object * Called_Station_Id; /* Called-Station-Id */
76 struct dict_object * Calling_Station_Id; /* Calling-Station-Id */
77 struct dict_object * Class; /* Class */
78 struct dict_object * CHAP_Algorithm; /* CHAP-Algorithm */
79 struct dict_object * CHAP_Auth; /* CHAP-Auth */
80 struct dict_object * CHAP_Challenge; /* CHAP-Challenge */
81 struct dict_object * CHAP_Ident; /* CHAP-Ident */
82 struct dict_object * CHAP_Response; /* CHAP-Response */
83 struct dict_object * Connect_Info; /* Connect-Info */
84 struct dict_object * Destination_Host; /* Destination-Host */
85 struct dict_object * Destination_Realm; /* Destination-Realm */
86 struct dict_object * EAP_Payload; /* EAP-Payload */
87 struct dict_object * Error_Message; /* Error-Message */
88 struct dict_object * Error_Reporting_Host; /* Error-Reporting-Host */
89 struct dict_object * Event_Timestamp; /* Event-Timestamp */
90 struct dict_object * Failed_AVP; /* Failed-AVP */
91 struct dict_object * Framed_AppleTalk_Link; /* Framed-AppleTalk-Link */
92 struct dict_object * Framed_AppleTalk_Network; /* Framed-AppleTalk-Network */
93 struct dict_object * Framed_AppleTalk_Zone; /* Framed-AppleTalk-Zone */
94 struct dict_object * Framed_Compression; /* Framed-Compression */
95 struct dict_object * Framed_IP_Address; /* Framed-IP-Address */
96 struct dict_object * Framed_IP_Netmask; /* Framed-IP-Netmask */
97 struct dict_object * Framed_Interface_Id; /* Framed-Interface-Id */
98 struct dict_object * Framed_IPv6_Prefix; /* Framed-IPv6-Prefix */
99 struct dict_object * Framed_IPX_Network; /* Framed-IPX-Network */
100 struct dict_object * Framed_MTU; /* Framed-MTU */
101 struct dict_object * Framed_Protocol; /* Framed-Protocol */
102 struct dict_object * Framed_Pool; /* Framed-Pool */
103 struct dict_object * Framed_IPv6_Route; /* Framed-IPv6-Route */
104 struct dict_object * Framed_IPv6_Pool; /* Framed-IPv6-Pool */
105 struct dict_object * Framed_Route; /* Framed-Route */
106 struct dict_object * Framed_Routing; /* Framed-Routing */
107 struct dict_object * Filter_Id; /* Filter-Id */
108 struct dict_object * Idle_Timeout; /* Idle-Timeout */
109 struct dict_object * Login_IP_Host; /* Login-IP-Host */
110 struct dict_object * Login_IPv6_Host; /* Login-IPv6-Host */
111 struct dict_object * Login_LAT_Group; /* Login-LAT-Group */
112 struct dict_object * Login_LAT_Node; /* Login-LAT-Node */
113 struct dict_object * Login_LAT_Port; /* Login-LAT-Port */
114 struct dict_object * Login_LAT_Service; /* Login-LAT-Service */
115 struct dict_object * Login_Service; /* Login-Service */
116 struct dict_object * Login_TCP_Port; /* Login-TCP-Port */
117 struct dict_object * NAS_Identifier; /* NAS-Identifier */
118 struct dict_object * NAS_IP_Address; /* NAS-IP-Address */
119 struct dict_object * NAS_IPv6_Address; /* NAS-IPv6-Address */
120 struct dict_object * NAS_Port; /* NAS-Port */
121 struct dict_object * NAS_Port_Id; /* NAS-Port-Id */
122 struct dict_object * NAS_Port_Type; /* NAS-Port-Type */
123 struct dict_object * Origin_AAA_Protocol; /* Origin-AAA-Protocol */
124 struct dict_object * Origin_Host; /* Origin-Host */
125 struct dict_object * Origin_Realm; /* Origin-Realm */
126 struct dict_object * Originating_Line_Info; /* Originating-Line-Info */
127 struct dict_object * Port_Limit; /* Port-Limit */
128 struct dict_object * Re_Auth_Request_Type; /* Re-Auth-Request-Type */
129 struct dict_object * Result_Code; /* Result-Code */
130 struct dict_object * Service_Type; /* Service-Type */
131 struct dict_object * Session_Id; /* Session-Id */
132 struct dict_object * Session_Timeout; /* Session-Timeout */
133 struct dict_object * State; /* State */
134 struct dict_object * Termination_Cause; /* Termination-Cause */
135 struct dict_object * Tunneling; /* Tunneling */
136 struct dict_object * Tunnel_Type; /* Tunnel-Type */
137 struct dict_object * Tunnel_Assignment_Id; /* Tunnel-Assignment-Id */
138 struct dict_object * Tunnel_Medium_Type; /* Tunnel-Medium-Type */
139 struct dict_object * Tunnel_Client_Endpoint; /* Tunnel-Client-Endpoint */
140 struct dict_object * Tunnel_Server_Endpoint; /* Tunnel-Server-Endpoint */
141 struct dict_object * Tunnel_Private_Group_Id; /* Tunnel-Private-Group-Id */
142 struct dict_object * Tunnel_Preference; /* Tunnel-Preference */
143 struct dict_object * Tunnel_Client_Auth_Id; /* Tunnel-Client-Auth-Id */
144 struct dict_object * Tunnel_Server_Auth_Id; /* Tunnel-Server-Auth-Id */
145 struct dict_object * User_Name; /* User-Name */
146
147 struct dict_object * Session_Termination_Request;/* STR */
148 } dict; /* cache of the dictionary objects we use */
149 struct session_handler * sess_hdl; /* We store RADIUS request authenticator information in the session */
150 char * confstr;
151
152 int ignore_nai;
153};
154
155/* The state we store in the session */
156struct sess_state {
157 application_id_t auth_appl; /* Auth-Application-Id used for this session, if available (stored in a Class attribute) */
158 int send_str; /* If not 0, we must send a STR when the ACA is received. */
159 uint32_t term_cause; /* If not 0, the Termination-Cause to put in the STR. */
160};
161
162static DECLARE_FD_DUMP_PROTOTYPE(acct_conf_session_state_dump, struct sess_state * st)
163{
164 return fd_dump_extend( FD_DUMP_STD_PARAMS, "[rgwx sess_state](@%p): aai:%x str:%d TC:%u", st, st->auth_appl, st->send_str, st->term_cause);
165}
166
167/* Initialize the plugin */
168static int acct_conf_parse(char * conffile, struct rgwp_config ** state)
169{
170 struct rgwp_config * new;
171 struct dict_object * app;
172
173 TRACE_ENTRY("%p %p", conffile, state);
174 CHECK_PARAMS( state );
175
176 CHECK_MALLOC( new = malloc(sizeof(struct rgwp_config)) );
177 memset(new, 0, sizeof(struct rgwp_config));
178
179 CHECK_FCT( fd_sess_handler_create( &new->sess_hdl, (void *)free, acct_conf_session_state_dump, NULL ) );
180 new->confstr = conffile;
181
182 if (conffile && strstr(conffile, "nonai"))
183 new->ignore_nai = 1;
184
185 /* Resolve all dictionary objects we use */
186 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Record-Number", &new->dict.Accounting_Record_Number, ENOENT) );
187 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Record-Type", &new->dict.Accounting_Record_Type, ENOENT) );
188 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Application-Id", &new->dict.Acct_Application_Id, ENOENT) );
189 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Delay-Time", &new->dict.Acct_Delay_Time, ENOENT) );
190 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Input-Octets", &new->dict.Accounting_Input_Octets, ENOENT) );
191 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Output-Octets", &new->dict.Accounting_Output_Octets, ENOENT) );
192 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Input-Packets", &new->dict.Accounting_Input_Packets, ENOENT) );
193 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Accounting-Output-Packets", &new->dict.Accounting_Output_Packets, ENOENT) );
194 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Authentic", &new->dict.Acct_Authentic, ENOENT) );
195 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Link-Count", &new->dict.Acct_Link_Count, ENOENT) );
196 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) );
197 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Session-Id", &new->dict.Acct_Session_Id, ENOENT) );
198 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Acct-Session-Time", &new->dict.Acct_Session_Time, ENOENT) );
199
200 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "ARAP-Password", &new->dict.ARAP_Password, ENOENT) );
201 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "ARAP-Security", &new->dict.ARAP_Security, ENOENT) );
202 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "ARAP-Security-Data", &new->dict.ARAP_Security_Data, ENOENT) );
203 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Application-Id", &new->dict.Auth_Application_Id, ENOENT) );
204 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Request-Type", &new->dict.Auth_Request_Type, ENOENT) );
205 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Authorization-Lifetime", &new->dict.Authorization_Lifetime, ENOENT) );
206 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Callback-Number", &new->dict.Callback_Number, ENOENT) );
207 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Callback-Id", &new->dict.Callback_Id, ENOENT) );
208 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Called-Station-Id", &new->dict.Called_Station_Id, ENOENT) );
209 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Calling-Station-Id", &new->dict.Calling_Station_Id, ENOENT) );
210 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Class", &new->dict.Class, ENOENT) );
211 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Connect-Info", &new->dict.Connect_Info, ENOENT) );
212 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Host", &new->dict.Destination_Host, ENOENT) );
213 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Realm", &new->dict.Destination_Realm, ENOENT) );
214 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "EAP-Payload", &new->dict.EAP_Payload, ENOENT) );
215 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Message", &new->dict.Error_Message, ENOENT) );
216 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Reporting-Host", &new->dict.Error_Reporting_Host, ENOENT) );
217 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Event-Timestamp", &new->dict.Event_Timestamp, ENOENT) );
218 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Failed-AVP", &new->dict.Failed_AVP, ENOENT) );
219 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-AppleTalk-Link", &new->dict.Framed_AppleTalk_Link, ENOENT) );
220 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-AppleTalk-Network", &new->dict.Framed_AppleTalk_Network, ENOENT) );
221 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-AppleTalk-Zone", &new->dict.Framed_AppleTalk_Zone, ENOENT) );
222 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Compression", &new->dict.Framed_Compression, ENOENT) );
223 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IP-Address", &new->dict.Framed_IP_Address, ENOENT) );
224 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IP-Netmask", &new->dict.Framed_IP_Netmask, ENOENT) );
225 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Interface-Id", &new->dict.Framed_Interface_Id, ENOENT) );
226 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IPv6-Prefix", &new->dict.Framed_IPv6_Prefix, ENOENT) );
227 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IPX-Network", &new->dict.Framed_IPX_Network, ENOENT) );
228 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-MTU", &new->dict.Framed_MTU, ENOENT) );
229 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Protocol", &new->dict.Framed_Protocol, ENOENT) );
230 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Pool", &new->dict.Framed_Pool, ENOENT) );
231 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Route", &new->dict.Framed_Route, ENOENT) );
232 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IPv6-Route", &new->dict.Framed_IPv6_Route, ENOENT) );
233 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IPv6-Pool", &new->dict.Framed_IPv6_Pool, ENOENT) );
234 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Routing", &new->dict.Framed_Routing, ENOENT) );
235 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Filter-Id", &new->dict.Filter_Id, ENOENT) );
236 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Idle-Timeout", &new->dict.Idle_Timeout, ENOENT) );
237 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-IP-Host", &new->dict.Login_IP_Host, ENOENT) );
238 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-IPv6-Host", &new->dict.Login_IPv6_Host, ENOENT) );
239 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Group", &new->dict.Login_LAT_Group, ENOENT) );
240 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Node", &new->dict.Login_LAT_Node, ENOENT) );
241 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Port", &new->dict.Login_LAT_Port, ENOENT) );
242 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Service", &new->dict.Login_LAT_Service, ENOENT) );
243 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-Service", &new->dict.Login_Service, ENOENT) );
244 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-TCP-Port", &new->dict.Login_TCP_Port, ENOENT) );
245 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Identifier", &new->dict.NAS_Identifier, ENOENT) );
246 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-IP-Address", &new->dict.NAS_IP_Address, ENOENT) );
247 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-IPv6-Address", &new->dict.NAS_IPv6_Address, ENOENT) );
248 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Port", &new->dict.NAS_Port, ENOENT) );
249 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Port-Id", &new->dict.NAS_Port_Id, ENOENT) );
250 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Port-Type", &new->dict.NAS_Port_Type, ENOENT) );
251 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-AAA-Protocol", &new->dict.Origin_AAA_Protocol, ENOENT) );
252 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &new->dict.Origin_Host, ENOENT) );
253 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &new->dict.Origin_Realm, ENOENT) );
254 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Originating-Line-Info", &new->dict.Originating_Line_Info, ENOENT) );
255 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Port-Limit", &new->dict.Port_Limit, ENOENT) );
256 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) );
257 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Result-Code", &new->dict.Result_Code, ENOENT) );
258 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Service-Type", &new->dict.Service_Type, ENOENT) );
259 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &new->dict.Session_Id, ENOENT) );
260 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Timeout", &new->dict.Session_Timeout, ENOENT) );
261 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "State", &new->dict.State, ENOENT) );
262 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Termination-Cause", &new->dict.Termination_Cause, ENOENT) );
263 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunneling", &new->dict.Tunneling, ENOENT) );
264 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Assignment-Id", &new->dict.Tunnel_Assignment_Id, ENOENT) );
265 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Type", &new->dict.Tunnel_Type, ENOENT) );
266 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Medium-Type", &new->dict.Tunnel_Medium_Type, ENOENT) );
267 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Client-Endpoint", &new->dict.Tunnel_Client_Endpoint, ENOENT) );
268 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Server-Endpoint", &new->dict.Tunnel_Server_Endpoint, ENOENT) );
269 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) );
270 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Preference", &new->dict.Tunnel_Preference, ENOENT) );
271 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) );
272 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) );
273 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "User-Name", &new->dict.User_Name, ENOENT) );
274
275 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_COMMAND, CMD_BY_NAME, "Session-Termination-Request", &new->dict.Session_Termination_Request, ENOENT) );
276
277 /* This plugin provides the following Diameter authentication applications support: */
278 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Diameter Base Accounting", &app, ENOENT) );
279 CHECK_FCT( fd_disp_app_support ( app, NULL, 0, 1 ) );
280
281 *state = new;
282 return 0;
283}
284
285/* deinitialize */
286static void acct_conf_free(struct rgwp_config * state)
287{
288 TRACE_ENTRY("%p", state);
289 CHECK_PARAMS_DO( state, return );
290 CHECK_FCT_DO( fd_sess_handler_destroy( &state->sess_hdl, NULL ), );
291 free(state);
292 return;
293}
294
295/* Incoming RADIUS request */
296static int acct_rad_req( struct rgwp_config * cs, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
297{
298 int idx;
299 int send_str=0;
300 uint32_t str_cause=0;
301 uint32_t e2eid = 0;
302 application_id_t auth_appl=0;
303 int got_id = 0;
304 uint32_t status_type;
305 uint32_t termination_action = 0;
306 uint32_t gigawords_in=0, gigawords_out=0;
307 size_t nattr_used = 0;
308 union avp_value value;
309 struct avp ** avp_tun = NULL, *avp = NULL;
310 struct session * sess;
311
312 const char * prefix = "Diameter/";
313 size_t pref_len;
314 os0_t si = NULL;
315 size_t si_len = 0;
316 os0_t un = NULL;
317 size_t un_len = 0;
318
319 TRACE_ENTRY("%p %p %p %p %p", cs, rad_req, rad_ans, diam_fw, cli);
320 CHECK_PARAMS(rad_req && (rad_req->hdr->code == RADIUS_CODE_ACCOUNTING_REQUEST) && rad_ans && diam_fw && *diam_fw);
321
322 pref_len = strlen(prefix);
323
324 /*
325 Either NAS-IP-Address or NAS-Identifier MUST be present in a
326 RADIUS Accounting-Request. It SHOULD contain a NAS-Port or NAS-
327 Port-Type attribute or both unless the service does not involve a
328 port or the NAS does not distinguish among its ports.
329 */
330 /* We also enforce that the message contains a CLASS attribute with Diameter/ prefix containing the Session-Id. */
331 for (idx = 0; idx < rad_req->attr_used; idx++) {
332 struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(rad_req->buf + rad_req->attr_pos[idx]);
333 uint8_t * v = (uint8_t *)(attr + 1);
334 size_t attr_len = attr->length - sizeof(struct radius_attr_hdr);
335
336 switch (attr->type) {
337 case RADIUS_ATTR_NAS_IP_ADDRESS:
338 case RADIUS_ATTR_NAS_IDENTIFIER:
339 case RADIUS_ATTR_NAS_IPV6_ADDRESS:
340 got_id = 1;
341 break;
342
343 case RADIUS_ATTR_TERMINATION_ACTION:
344 CHECK_PARAMS( attr->length == 6 );
345 termination_action = (v[0] << 24)
346 | (v[1] << 16)
347 | (v[2] << 8)
348 | v[3] ;
349 break;
350
351 case RADIUS_ATTR_ACCT_INPUT_GIGAWORDS:
352 CHECK_PARAMS( attr->length == 6 );
353 gigawords_in = (v[0] << 24)
354 | (v[1] << 16)
355 | (v[2] << 8)
356 | v[3] ;
357 break;
358
359 case RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS:
360 CHECK_PARAMS( attr->length == 6 );
361 gigawords_out = (v[0] << 24)
362 | (v[1] << 16)
363 | (v[2] << 8)
364 | v[3] ;
365 break;
366
367 case RADIUS_ATTR_CLASS:
368 if ((attr_len > pref_len ) && ! strncmp((char *)v, prefix, pref_len)) {
369 int i;
370 si = v + pref_len;
371 si_len = attr_len - pref_len;
372 TRACE_DEBUG(ANNOYING, "Found Class attribute with '%s' prefix (attr #%d), SI:'%.*s'.", prefix, idx, (int)si_len, si);
373 /* Remove from the message */
374 for (i = idx + 1; i < rad_req->attr_used; i++)
375 rad_req->attr_pos[i - 1] = rad_req->attr_pos[i];
376 rad_req->attr_used -= 1;
377 }
378 break;
379
380 case RADIUS_ATTR_USER_NAME:
381 if (attr_len) {
382 un = v;
383 un_len = attr_len;
384 TRACE_DEBUG(ANNOYING, "Found a User-Name attribute: '%.*s'", (int)un_len, un);
385 }
386 break;
387
388 }
389 }
390
391 /* Check basic information is there */
392 if (!got_id || radius_msg_get_attr_int32(rad_req, RADIUS_ATTR_ACCT_STATUS_TYPE, &status_type)) {
393 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));
394 return EINVAL;
395 }
396
397 /*
398 -- RFC2866:
399 In Accounting-Request Packets, the Authenticator value is a 16
400 octet MD5 [5] checksum, called the Request Authenticator.
401
402 The NAS and RADIUS accounting server share a secret. The Request
403 Authenticator field in Accounting-Request packets contains a one-
404 way MD5 hash calculated over a stream of octets consisting of the
405 Code + Identifier + Length + 16 zero octets + request attributes +
406 shared secret (where + indicates concatenation). The 16 octet MD5
407 hash value is stored in the Authenticator field of the
408 Accounting-Request packet.
409
410 Note that the Request Authenticator of an Accounting-Request can
411 not be done the same way as the Request Authenticator of a RADIUS
412 Access-Request, because there is no User-Password attribute in an
413 Accounting-Request.
414
415
416 -- RFC5080:
417 The Request Authenticator field MUST contain the correct data, as
418 given by the above calculation. Invalid packets are silently
419 discarded. Note that some early implementations always set the
420 Request Authenticator to all zeros. New implementations of RADIUS
421 clients MUST use the above algorithm to calculate the Request
422 Authenticator field. New RADIUS server implementations MUST
423 silently discard invalid packets.
424
425 */
426 {
427 uint8_t save[MD5_MAC_LEN];
428 uint8_t * secret;
429 size_t secret_len;
430
431 /* Get the shared secret */
432 CHECK_FCT(rgw_clients_getkey(cli, &secret, &secret_len));
433
434 /* Copy the received Request Authenticator */
435 memcpy(&save[0], &rad_req->hdr->authenticator[0], MD5_MAC_LEN);
436
437 /* Compute the same authenticator */
438 radius_msg_finish_acct(rad_req, secret, secret_len);
439
440 /* And now compare with the received value */
441 if (memcmp(&save[0], &rad_req->hdr->authenticator[0], MD5_MAC_LEN)) {
442 /* Invalid authenticator */
443 TRACE_BUFFER(FD_LOG_DEBUG, FULL+1, "Received ReqAuth: ", &save[0], MD5_MAC_LEN, "" );
444 TRACE_BUFFER(FD_LOG_DEBUG, FULL+1, "Expected ReqAuth: ", &rad_req->hdr->authenticator[0], MD5_MAC_LEN, "" );
445 TRACE_DEBUG(INFO, "[acct.rgwx] Invalid Request Authenticator in Account-Request from %s, discarding the message.", rgw_clients_id(cli));
446 return EINVAL;
447 }
448 }
449
450
451 /* Handle the Accounting-On case: nothing to do, just reply OK */
452 if (status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_ON) {
453 TRACE_DEBUG(FULL, "[acct.rgwx] Received Accounting-On Acct-Status-Type attribute, replying without translation to Diameter.");
454 CHECK_MALLOC( *rad_ans = radius_msg_new(RADIUS_CODE_ACCOUNTING_RESPONSE, rad_req->hdr->identifier) );
455 return -2;
456 }
457
458 if (status_type == RADIUS_ACCT_STATUS_TYPE_ACCOUNTING_OFF) {
459 TRACE_DEBUG(FULL, "[acct.rgwx] Received Accounting-Off Acct-Status-Type attribute, we must terminate all active sessions.");
460 TODO("RADIUS side is rebooting, send STR on all sessions???");
461 return ENOTSUP;
462 }
463
464 /* Check if we got a valid session information, otherwise the server will not be able to handle the data... */
465 if (!si) {
466 TRACE_DEBUG(INFO, "[acct.rgwx] RADIUS Account-Request from %s did not contain a CLASS attribute with Diameter session information, reject.", rgw_clients_id(cli));
467 return EINVAL;
468 }
469
470 /* Add the Destination-Realm */
471 CHECK_FCT( fd_msg_avp_new ( cs->dict.Destination_Realm, 0, &avp ) );
472 idx = 0;
473 if (un && ! cs->ignore_nai) {
474 /* Is there an '@' in the user name? We don't care for decorated NAI here */
475 for (idx = un_len - 2; idx > 0; idx--) {
476 if (un[idx] == '@') {
477 idx++;
478 break;
479 }
480 }
481 }
482 if (idx == 0) {
483 /* Not found in the User-Name => we use the local domain of this gateway */
484 value.os.data = (uint8_t *)fd_g_config->cnf_diamrlm;
485 value.os.len = fd_g_config->cnf_diamrlm_len;
486 } else {
487 value.os.data = un + idx;
488 value.os.len = un_len - idx;
489 }
490 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
491 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) );
492
493 /* Create the Session-Id AVP */
494 {
495 CHECK_FCT( fd_sess_fromsid_msg ( si, si_len, &sess, NULL) );
496
497 TRACE_DEBUG(FULL, "[acct.rgwx] Translating new accounting message for session '%.*s'...", (int)si_len, si);
498
499 /* Add the Session-Id AVP as first AVP */
500 CHECK_FCT( fd_msg_avp_new ( cs->dict.Session_Id, 0, &avp ) );
501 value.os.data = (unsigned char *)si;
502 value.os.len = si_len;
503 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
504 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) );
505 CHECK_FCT( fd_msg_sess_set(*diam_fw, sess) );
506 }
507
508
509 /* Add the command code */
510 {
511 struct msg_hdr * header = NULL;
512 CHECK_FCT( fd_msg_hdr ( *diam_fw, &header ) );
513 header->msg_flags = CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE;
514 header->msg_code = CC_AC;
515 header->msg_appl = AI_ACCT;
516
517 /* Add the Acct-Application-Id */
518 CHECK_FCT( fd_msg_avp_new ( cs->dict.Acct_Application_Id, 0, &avp ) );
519 value.i32 = header->msg_appl;
520 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
521 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
522
523 /* save the end to end id */
524 e2eid = header->msg_eteid;
525 }
526
527 /* Convert the RADIUS attributes, as they appear in the message */
528 for (idx = 0; idx < rad_req->attr_used; idx++) {
529 struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(rad_req->buf + rad_req->attr_pos[idx]);
530
531 switch (attr->type) {
532 /*
533 Any attribute valid in a RADIUS Access-Request or Access-Accept
534 packet is valid in a RADIUS Accounting-Request packet, except that
535 the following attributes MUST NOT be present in an Accounting-
536 Request: User-Password, CHAP-Password, Reply-Message, State.
537 */
538 case RADIUS_ATTR_USER_PASSWORD:
539 case RADIUS_ATTR_CHAP_PASSWORD:
540 case RADIUS_ATTR_REPLY_MESSAGE:
541 case RADIUS_ATTR_STATE:
542 case RADIUS_ATTR_MESSAGE_AUTHENTICATOR:
543 case RADIUS_ATTR_EAP_MESSAGE:
544 TRACE_DEBUG(INFO, "[acct.rgwx] RADIUS Account-Request contains a forbidden attribute (%hhd), reject.", attr->type);
545 return EINVAL;
546
547
548 /* This macro converts a RADIUS attribute to a Diameter AVP of type OctetString */
549 #define CONV2DIAM_STR( _dictobj_ ) \
550 CHECK_PARAMS( attr->length >= 2 ); \
551 /* Create the AVP with the specified dictionary model */ \
552 CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \
553 value.os.len = attr->length - 2; \
554 value.os.data = (unsigned char *)(attr + 1); \
555 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \
556 /* Add the AVP in the Diameter message. */ \
557 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); \
558
559 /* Same thing, for scalar AVPs of 32 bits */
560 #define CONV2DIAM_32B( _dictobj_ ) \
561 CHECK_PARAMS( attr->length == 6 ); \
562 CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \
563 { \
564 uint8_t * v = (uint8_t *)(attr + 1); \
565 value.u32 = (v[0] << 24) \
566 | (v[1] << 16) \
567 | (v[2] << 8) \
568 | v[3] ; \
569 } \
570 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \
571 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); \
572
573 /* And the 64b version */
574 #define CONV2DIAM_64B( _dictobj_ ) \
575 CHECK_PARAMS( attr->length == 10); \
576 CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \
577 { \
578 uint8_t * v = (uint8_t *)(attr + 1); \
579 value.u64 = ((uint64_t)(v[0]) << 56) \
580 | ((uint64_t)(v[1]) << 48) \
581 | ((uint64_t)(v[2]) << 40) \
582 | ((uint64_t)(v[3]) << 32) \
583 | ((uint64_t)(v[4]) << 24) \
584 | ((uint64_t)(v[5]) << 16) \
585 | ((uint64_t)(v[6]) << 8) \
586 | (uint64_t)(v[7]) ; \
587 } \
588 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \
589 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); \
590
591
592 /* Attributes as listed in RFC2866, section 5.13 and RFC4005, section 10.2.1 */
593 case RADIUS_ATTR_USER_NAME:
594 CONV2DIAM_STR( User_Name );
595 break;
596
597 case RADIUS_ATTR_NAS_IP_ADDRESS:
598 CONV2DIAM_STR( NAS_IP_Address );
599 break;
600
601 case RADIUS_ATTR_NAS_PORT:
602 CONV2DIAM_32B( NAS_Port );
603 break;
604
605 case RADIUS_ATTR_SERVICE_TYPE:
606 CONV2DIAM_32B( Service_Type );
607 break;
608
609 case RADIUS_ATTR_FRAMED_PROTOCOL:
610 CONV2DIAM_32B( Framed_Protocol );
611 break;
612
613 case RADIUS_ATTR_FRAMED_IP_ADDRESS:
614 CONV2DIAM_STR( Framed_IP_Address );
615 break;
616
617 case RADIUS_ATTR_FRAMED_IP_NETMASK:
618 CONV2DIAM_STR( Framed_IP_Netmask );
619 break;
620
621 case RADIUS_ATTR_FRAMED_ROUTING:
622 CONV2DIAM_32B( Framed_Routing );
623 break;
624
625 case RADIUS_ATTR_FILTER_ID:
626 CONV2DIAM_STR( Filter_Id );
627 break;
628
629 case RADIUS_ATTR_FRAMED_MTU:
630 CONV2DIAM_32B( Framed_MTU );
631 break;
632
633 case RADIUS_ATTR_FRAMED_COMPRESSION:
634 CONV2DIAM_32B( Framed_Compression );
635 break;
636
637 case RADIUS_ATTR_LOGIN_IP_HOST:
638 CONV2DIAM_STR( Login_IP_Host );
639 break;
640
641 case RADIUS_ATTR_LOGIN_SERVICE:
642 CONV2DIAM_32B( Login_Service );
643 break;
644
645 case RADIUS_ATTR_LOGIN_TCP_PORT:
646 CONV2DIAM_32B( Login_TCP_Port );
647 break;
648
649 case RADIUS_ATTR_CALLBACK_NUMBER:
650 CONV2DIAM_STR( Callback_Number );
651 break;
652
653 case RADIUS_ATTR_CALLBACK_ID:
654 CONV2DIAM_STR( Callback_Id );
655 break;
656
657 case RADIUS_ATTR_FRAMED_ROUTE:
658 CONV2DIAM_STR( Framed_Route );
659 break;
660
661 case RADIUS_ATTR_FRAMED_IPX_NETWORK:
662 CONV2DIAM_32B( Framed_IPX_Network );
663 break;
664
665 case RADIUS_ATTR_CLASS:
666 CONV2DIAM_STR( Class );
667 /* In addition, save the data in the session if it is "our" CLASS_AAI_PREFIX Class attribute */
668 {
669 char buf[32];
670 char * attr_val, *auth_val;
671 attr_val = (char *)(attr + 1);
672 auth_val = attr_val + CONSTSTRLEN(CLASS_AAI_PREFIX);
673 if ( (attr->length > sizeof(struct radius_attr_hdr) + CONSTSTRLEN(CLASS_AAI_PREFIX) )
674 && (attr->length < sizeof(struct radius_attr_hdr) + CONSTSTRLEN(CLASS_AAI_PREFIX) + sizeof(buf))
675 && ! strncmp(attr_val, CLASS_AAI_PREFIX, CONSTSTRLEN(CLASS_AAI_PREFIX))) {
676
677 memset(buf, 0, sizeof(buf));
678 memcpy(buf, auth_val, attr->length - sizeof(struct radius_attr_hdr) - CONSTSTRLEN(CLASS_AAI_PREFIX));
679 if (sscanf(buf, "%u", &auth_appl) == 1) {
680 TRACE_DEBUG(ANNOYING, "Found Class attribute with '%s' prefix (attr #%d), AAI:%u.", CLASS_AAI_PREFIX, idx, auth_appl);
681 }
682 }
683 }
684 break;
685
686 case RADIUS_ATTR_VENDOR_SPECIFIC:
687 if (attr->length >= 6) {
688 uint32_t vendor_id;
689 uint8_t * c = (uint8_t *)(attr + 1);
690
691 vendor_id = c[0] << 24 | c[1] << 16 | c[2] << 8 | c[3];
692 c += 4;
693
694 switch (vendor_id) {
695
696 /* For the vendors we KNOW they follow the VSA recommended format, we convert following the rules of RFC4005 (9.6.2) */
697 case RADIUS_VENDOR_ID_MICROSOFT : /* RFC 2548 */
698 /* other vendors ? */
699 {
700 size_t left;
701 struct radius_attr_vendor *vtlv;
702
703 left = attr->length - 6;
704 vtlv = (struct radius_attr_vendor *)c;
705
706 while ((left >= 2) && (vtlv->vendor_length <= left)) {
707 /* Search our dictionary for corresponding Vendor's AVP */
708 struct dict_avp_request req;
709 struct dict_object * avp_model = NULL;
710 memset(&req, 0, sizeof(struct dict_avp_request));
711 req.avp_vendor = vendor_id;
712 req.avp_code = vtlv->vendor_type;
713
714 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE_AND_VENDOR, &req, &avp_model, 0) );
715 if (!avp_model) {
716 TRACE_DEBUG(FULL, "Unknown attribute (vendor 0x%x, code 0x%x) ignored.", req.avp_vendor, req.avp_code);
717 } else {
718 CHECK_FCT( fd_msg_avp_new ( avp_model, 0, &avp ) );
719 value.os.len = vtlv->vendor_length - 2;
720 value.os.data = (unsigned char *)(vtlv + 1);
721 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
722 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
723 }
724 c += vtlv->vendor_length;
725 left -= vtlv->vendor_length;
726 vtlv = (struct radius_attr_vendor *)c;
727 }
728 }
729 break;
730
731 /* Other vendors we KNOw how to convert the attributes would be added here... */
732 /* case RADIUS_VENDOR_ID_CISCO :
733 break; */
734 /* case RADIUS_VENDOR_ID_IETF : (extended RADIUS attributes)
735 break; */
736
737 /* When we don't know, just discard the attribute... VSA are optional with regards to RADIUS anyway */
738 default:
739 /* do nothing */
740 TRACE_DEBUG(FULL, "VSA attribute from vendor %d discarded", vendor_id);
741
742 }
743 }
744 break;
745
746 case RADIUS_ATTR_SESSION_TIMEOUT:
747 /* Translation depends on Termination-Action : rfc4005#section-9.2.1 */
748 if (termination_action != RADIUS_TERMINATION_ACTION_RADIUS_REQUEST) {
749 CONV2DIAM_32B( Session_Timeout );
750 } else {
751 CONV2DIAM_32B( Authorization_Lifetime );
752 /* And add this additional AVP */
753 CHECK_FCT( fd_msg_avp_new ( cs->dict.Re_Auth_Request_Type, 0, &avp ) );
754 value.u32 = ACV_ART_AUTHORIZE_AUTHENTICATE;
755 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
756 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
757 }
758 break;
759
760 case RADIUS_ATTR_IDLE_TIMEOUT:
761 CONV2DIAM_32B( Idle_Timeout );
762 break;
763
764 case RADIUS_ATTR_TERMINATION_ACTION:
765 /* Just remove */
766 break;
767
768 case RADIUS_ATTR_CALLED_STATION_ID:
769 CONV2DIAM_STR( Called_Station_Id );
770 break;
771
772 case RADIUS_ATTR_CALLING_STATION_ID:
773 CONV2DIAM_STR( Calling_Station_Id );
774 break;
775
776 case RADIUS_ATTR_NAS_IDENTIFIER:
777 CONV2DIAM_STR( NAS_Identifier );
778 break;
779
780 /* Proxy-State is handled by echo_drop.rgwx plugin, we ignore it here */
781
782 case RADIUS_ATTR_LOGIN_LAT_SERVICE:
783 CONV2DIAM_STR( Login_LAT_Service );
784 break;
785
786 case RADIUS_ATTR_LOGIN_LAT_NODE:
787 CONV2DIAM_STR( Login_LAT_Node );
788 break;
789
790 case RADIUS_ATTR_LOGIN_LAT_GROUP:
791 CONV2DIAM_STR( Login_LAT_Group );
792 break;
793
794 case RADIUS_ATTR_FRAMED_APPLETALK_LINK:
795 CONV2DIAM_32B( Framed_AppleTalk_Link );
796 break;
797
798 case RADIUS_ATTR_FRAMED_APPLETALK_NETWORK:
799 CONV2DIAM_32B( Framed_AppleTalk_Network );
800 break;
801
802 case RADIUS_ATTR_FRAMED_APPLETALK_ZONE:
803 CONV2DIAM_STR( Framed_AppleTalk_Zone );
804 break;
805
806 case RADIUS_ATTR_ACCT_STATUS_TYPE:
807 /*
808 - If the RADIUS message received is an Accounting-Request, the
809 Acct-Status-Type attribute value must be converted to a
810 Accounting-Record-Type AVP value. If the Acct-Status-Type
811 attribute value is STOP, the local server MUST issue a
812 Session-Termination-Request message once the Diameter
813 Accounting-Answer message has been received.
814 */
815 switch (status_type) {
816 case RADIUS_ACCT_STATUS_TYPE_START:
817 value.u32 = ACV_ART_START_RECORD;
818 break;
819 case RADIUS_ACCT_STATUS_TYPE_STOP:
820 value.u32 = ACV_ART_STOP_RECORD;
821 send_str = 1; /* Register this info in the session */
822 break;
823 case RADIUS_ACCT_STATUS_TYPE_INTERIM_UPDATE:
824 value.u32 = ACV_ART_INTERIM_RECORD;
825 break;
826 default:
827 TRACE_DEBUG(INFO, "Unknown RADIUS_ATTR_ACCT_STATUS_TYPE value %d, aborting...", status_type);
828 return ENOTSUP;
829 }
830 CHECK_FCT( fd_msg_avp_new ( cs->dict.Accounting_Record_Type, 0, &avp ) );
831 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
832 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
833
834 /* While here, we also add the Accouting-Record-Number AVP.
835 The Accounting-Record-Number AVP (AVP Code 485) is of type Unsigned32
836 and identifies this record within one session. As Session-Id AVPs
837 are globally unique, the combination of Session-Id and Accounting-
838 Record-Number AVPs is also globally unique, and can be used in
839 matching accounting records with confirmations. An easy way to
840 produce unique numbers is to set the value to 0 for records of type
841 EVENT_RECORD and START_RECORD, and set the value to 1 for the first
842 INTERIM_RECORD, 2 for the second, and so on until the value for
843 STOP_RECORD is one more than for the last INTERIM_RECORD.
844
845 -- we actually use the end-to-end id of the message here, which remains constant
846 if we send a duplicate, so it has the same properties as the suggested algorithm.
847 Anyway, it assumes that we are not converting twice the same RADIUS message.
848 . */
849 CHECK_FCT( fd_msg_avp_new ( cs->dict.Accounting_Record_Number, 0, &avp ) );
850 value.u32 = e2eid;
851 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
852 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
853
854 break;
855
856 case RADIUS_ATTR_ACCT_DELAY_TIME:
857 CONV2DIAM_32B( Acct_Delay_Time );
858 break;
859
860 /*
861 - If the RADIUS message contains the Accounting-Input-Octets,
862 Accounting-Input-Packets, Accounting-Output-Octets, or
863 Accounting-Output-Packets, these attributes must be converted
864 to the Diameter equivalents. Further, if the Acct-Input-
865 Gigawords or Acct-Output-Gigawords attributes are present,
866 these must be used to properly compute the Diameter accounting
867 AVPs.
868 */
869 case RADIUS_ATTR_ACCT_INPUT_OCTETS:
870 memset(&value, 0, sizeof(value));
871 {
872 uint8_t * v = (uint8_t *)(attr + 1);
873 value.u64 = (v[0] << 24)
874 | (v[1] << 16)
875 | (v[2] << 8)
876 | v[3] ;
877 }
878 value.u64 += ((uint64_t)gigawords_in << 32);
879 CHECK_FCT( fd_msg_avp_new ( cs->dict.Accounting_Input_Octets, 0, &avp ) );
880 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
881 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
882 break;
883
884 case RADIUS_ATTR_ACCT_OUTPUT_OCTETS:
885 memset(&value, 0, sizeof(value));
886 {
887 uint8_t * v = (uint8_t *)(attr + 1);
888 value.u64 = (v[0] << 24)
889 | (v[1] << 16)
890 | (v[2] << 8)
891 | v[3] ;
892 }
893 value.u64 += ((uint64_t)gigawords_out << 32);
894 CHECK_FCT( fd_msg_avp_new ( cs->dict.Accounting_Output_Octets, 0, &avp ) );
895 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
896 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
897 break;
898
899 case RADIUS_ATTR_ACCT_SESSION_ID:
900 CONV2DIAM_STR( Acct_Session_Id );
901 break;
902
903 case RADIUS_ATTR_ACCT_AUTHENTIC:
904 CONV2DIAM_32B( Acct_Authentic );
905 break;
906
907 case RADIUS_ATTR_ACCT_SESSION_TIME:
908 CONV2DIAM_32B( Acct_Session_Time );
909 break;
910
911 case RADIUS_ATTR_ACCT_INPUT_PACKETS:
912 memset(&value, 0, sizeof(value));
913 {
914 uint8_t * v = (uint8_t *)(attr + 1);
915 value.u64 = (v[0] << 24)
916 | (v[1] << 16)
917 | (v[2] << 8)
918 | v[3] ;
919 }
920 /* value.u64 += (gigawords_in << 32); */
921 CHECK_FCT( fd_msg_avp_new ( cs->dict.Accounting_Input_Packets, 0, &avp ) );
922 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
923 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
924 break;
925
926 case RADIUS_ATTR_ACCT_OUTPUT_PACKETS:
927 memset(&value, 0, sizeof(value));
928 {
929 uint8_t * v = (uint8_t *)(attr + 1);
930 value.u64 = (v[0] << 24)
931 | (v[1] << 16)
932 | (v[2] << 8)
933 | v[3] ;
934 }
935 /* value.u64 += (gigawords_out << 32); */
936 CHECK_FCT( fd_msg_avp_new ( cs->dict.Accounting_Output_Packets, 0, &avp ) );
937 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
938 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
939 break;
940
941 /*
942 - If the Accounting message contains an Acct-Termination-Cause
943 attribute, it should be translated to the equivalent
944 Termination-Cause AVP value.
945 */
946 case RADIUS_ATTR_ACCT_TERMINATE_CAUSE:
947 /* rfc4005#section-9.3.5 */
948 {
949 uint8_t * v = (uint8_t *)(attr + 1);
950 str_cause = (v[0] << 24)
951 | (v[1] << 16)
952 | (v[2] << 8)
953 | v[3] ;
954 }
955 str_cause += 10; /* This seems to be the rule, we can modify later if needed */
956 break;
957
958 case RADIUS_ATTR_ACCT_MULTI_SESSION_ID:
959 CONV2DIAM_STR( Acct_Multi_Session_Id );
960 break;
961
962 case RADIUS_ATTR_ACCT_LINK_COUNT:
963 CONV2DIAM_32B( Acct_Link_Count );
964 break;
965
966 /* CHAP-Challenge is not present in Accounting-Request */
967
968 case RADIUS_ATTR_NAS_PORT_TYPE:
969 CONV2DIAM_32B( NAS_Port_Type );
970 break;
971
972 case RADIUS_ATTR_PORT_LIMIT:
973 CONV2DIAM_32B( Port_Limit );
974 break;
975
976 case RADIUS_ATTR_LOGIN_LAT_PORT:
977 CONV2DIAM_STR( Login_LAT_Port );
978 break;
979
980 /* RFC 3162 */
981 case RADIUS_ATTR_NAS_IPV6_ADDRESS:
982 CONV2DIAM_STR( NAS_IPv6_Address );
983 break;
984
985 case RADIUS_ATTR_FRAMED_INTERFACE_ID:
986 CONV2DIAM_64B( Framed_Interface_Id );
987 break;
988
989 case RADIUS_ATTR_FRAMED_IPV6_PREFIX:
990 CONV2DIAM_STR( Framed_IPv6_Prefix );
991 break;
992
993 case RADIUS_ATTR_LOGIN_IPV6_HOST:
994 CONV2DIAM_STR( Login_IPv6_Host );
995 break;
996
997 case RADIUS_ATTR_FRAMED_IPV6_ROUTE:
998 CONV2DIAM_STR( Framed_IPv6_Route );
999 break;
1000
1001 case RADIUS_ATTR_FRAMED_IPV6_POOL:
1002 CONV2DIAM_STR( Framed_IPv6_Pool );
1003 break;
1004
1005 /* RFC 2868 */
1006 /* Prepare the top-level Tunneling AVP for each tag values, as needed, and add to the Diameter message.
1007 This macro is called when an AVP is added inside the group, so we will not have empty grouped AVPs */
1008 #define AVP_TUN_PREPARE() { \
1009 if (avp_tun == NULL) { \
1010 CHECK_MALLOC( avp_tun = calloc(sizeof(struct avp *), 32 ) ); \
1011 } \
1012 tag = *(uint8_t *)(attr + 1); \
1013 if (tag > 0x1F) tag = 0; \
1014 if (avp_tun[tag] == NULL) { \
1015 CHECK_FCT( fd_msg_avp_new ( cs->dict.Tunneling, 0, &avp_tun[tag] ) ); \
1016 CHECK_FCT( fd_msg_avp_add (*diam_fw, MSG_BRW_LAST_CHILD, avp_tun[tag]));\
1017 } \
1018 }
1019
1020 /* Convert an attribute to an OctetString AVP and add inside the Tunneling AVP corresponding to the tag */
1021 #define CONV2DIAM_TUN_STR( _dictobj_ ) { \
1022 uint8_t tag; \
1023 CHECK_PARAMS( attr->length >= 3); \
1024 AVP_TUN_PREPARE(); \
1025 CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \
1026 value.os.len = attr->length - (tag ? 3 : 2); \
1027 value.os.data = ((unsigned char *)(attr + 1)) + (tag ? 1 : 0); \
1028 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \
1029 CHECK_FCT( fd_msg_avp_add ( avp_tun[tag], MSG_BRW_LAST_CHILD, avp) ); \
1030 }
1031
1032 /* Convert an attribute to a scalar AVP and add inside the Tunneling AVP corresponding to the tag */
1033 #define CONV2DIAM_TUN_24B( _dictobj_ ) { \
1034 uint8_t tag; \
1035 CHECK_PARAMS( attr->length == 6); \
1036 AVP_TUN_PREPARE(); \
1037 CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \
1038 { \
1039 uint8_t * v = (uint8_t *)(attr + 1); \
1040 value.u32 = (v[1] << 16) | (v[2] <<8) | v[3] ; \
1041 } \
1042 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \
1043 CHECK_FCT( fd_msg_avp_add ( avp_tun[tag], MSG_BRW_LAST_CHILD, avp) ); \
1044 }
1045
1046 /*
1047 - If the RADIUS message contains Tunnel information [RADTunnels],
1048 the attributes or tagged groups should each be converted to a
1049 Diameter Tunneling Grouped AVP set. If the tunnel information
1050 contains a Tunnel-Password attribute, the RADIUS encryption
1051 must be resolved, and the password forwarded, by using Diameter
1052 security methods.
1053 -> If the RADIUS message does not use properly the Tag info, result is unpredictable here..
1054 */
1055 case RADIUS_ATTR_TUNNEL_TYPE:
1056 CONV2DIAM_TUN_24B( Tunnel_Type );
1057 break;
1058
1059 case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE:
1060 CONV2DIAM_TUN_24B( Tunnel_Medium_Type );
1061 break;
1062
1063 case RADIUS_ATTR_TUNNEL_CLIENT_ENDPOINT:
1064 CONV2DIAM_TUN_STR( Tunnel_Client_Endpoint );
1065 break;
1066
1067 case RADIUS_ATTR_TUNNEL_SERVER_ENDPOINT:
1068 CONV2DIAM_TUN_STR( Tunnel_Server_Endpoint );
1069 break;
1070
1071 /* Tunnel-Password never present in an Accounting-Request */
1072
1073 case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID:
1074 CONV2DIAM_TUN_STR( Tunnel_Private_Group_Id );
1075 break;
1076
1077 case RADIUS_ATTR_TUNNEL_ASSIGNMENT_ID:
1078 CONV2DIAM_TUN_STR( Tunnel_Assignment_Id );
1079 break;
1080
1081 /* Tunnel-Reference never present in an Accounting-Request */
1082
1083 case RADIUS_ATTR_TUNNEL_CLIENT_AUTH_ID:
1084 CONV2DIAM_TUN_STR( Tunnel_Client_Auth_Id );
1085 break;
1086
1087 case RADIUS_ATTR_TUNNEL_SERVER_AUTH_ID:
1088 CONV2DIAM_TUN_STR( Tunnel_Server_Auth_Id );
1089 break;
1090
1091 /* RFC 2869 */
1092 /*
1093 Acct-Input-Gigawords, Acct-Output-
1094 Gigawords, Event-Timestamp, and NAS-Port-Id may have 0-1 instances in
1095 an Accounting-Request packet. Connect-Info may have 0+ instances in
1096 an Accounting-Request packet. The other attributes added in this
1097 document must not be present in an Accounting-Request.
1098 */
1099 case RADIUS_ATTR_ACCT_INPUT_GIGAWORDS:
1100 break; /* we already saved the value in gigawords_in */
1101
1102 case RADIUS_ATTR_ACCT_OUTPUT_GIGAWORDS:
1103 break; /* we already saved the value in gigawords_out */
1104
1105 case RADIUS_ATTR_EVENT_TIMESTAMP:
1106 /* RADIUS:
1107 The Value field is four octets encoding an unsigned integer with
1108 the number of seconds since January 1, 1970 00:00 UTC.
1109 Diameter:
1110 The Time format is derived from the OctetString AVP Base Format.
1111 The string MUST contain four octets, in the same format as the
1112 first four bytes are in the NTP timestamp format. The NTP
1113 Timestamp format is defined in Chapter 3 of [RFC4330].
1114
1115 This represents the number of seconds since 0h on 1 January 1900
1116 with respect to the Coordinated Universal Time (UTC).
1117
1118 -- RFC4330:
1119 NTP timestamps are represented as a 64-bit unsigned
1120 fixed-point number, in seconds relative to 0h on 1 January 1900. The
1121 integer part is in the first 32 bits, and the fraction part in the
1122 last 32 bits. In the fraction part, the non-significant low-order
1123 bits are not specified and are ordinarily set to 0.
1124 */
1125 {
1126 uint32_t ts;
1127
1128 uint8_t * v = (uint8_t *)(attr + 1);
1129 /* Read the RADIUS attribute value */
1130 ts = (v[0] << 24)
1131 | (v[1] << 16)
1132 | (v[2] << 8)
1133 | v[3] ;
1134
1135 /* Add the 70 missing years */
1136 ts += 2208988800U; /* 60 * 60 * 24 * ( 365 * 70 + 17 ) */
1137
1138 /* Convert to network byte order */
1139 ts = htonl(ts);
1140
1141 /* Diameter Time datatype is derived from OctetString */
1142 value.os.data = (void *) &ts;
1143 value.os.len = sizeof(uint32_t);
1144
1145 CHECK_FCT( fd_msg_avp_new ( cs->dict.Event_Timestamp, 0, &avp ) );
1146 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
1147 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
1148 }
1149 break;
1150
1151 case RADIUS_ATTR_NAS_PORT_ID:
1152 CONV2DIAM_STR( NAS_Port_Id );
1153 break;
1154
1155 case RADIUS_ATTR_CONNECT_INFO:
1156 CONV2DIAM_STR( Connect_Info );
1157 break;
1158
1159 case RADIUS_ATTR_FRAMED_POOL: /* To follow the IPv6 version */
1160 CONV2DIAM_STR( Framed_Pool );
1161 break;
1162
1163
1164 /* RFC 3579 */
1165 /*
1166 The EAP-Message and Message-Authenticator attributes specified in
1167 this document MUST NOT be present in an Accounting-Request.
1168 */
1169 case RADIUS_ATTR_ORIGINATING_LINE_INFO:
1170 CONV2DIAM_STR( Originating_Line_Info );
1171 break;
1172
1173 /* Default */
1174 default: /* unknown attribute */
1175 /* We just keep the attribute in the RADIUS message */
1176 rad_req->attr_pos[nattr_used++] = rad_req->attr_pos[idx];
1177 }
1178 }
1179
1180 /* Update the radius message to remove all handled attributes */
1181 rad_req->attr_used = nattr_used;
1182
1183 /* Store useful information in the session */
1184 {
1185 struct sess_state * st;
1186
1187 CHECK_MALLOC( st = malloc(sizeof(struct sess_state)) );
1188 memset(st, 0, sizeof(struct sess_state));
1189 st->auth_appl = auth_appl;
1190 if (auth_appl) { /* We use the value 0 for servers which indicated NO STATE MAINTAINED, hence have no need for STR */
1191 st->send_str = send_str;
1192 }
1193 st->term_cause = str_cause;
1194 CHECK_FCT( fd_sess_state_store( cs->sess_hdl, sess, &st ) );
1195 }
1196
1197 return 0;
1198}
1199
1200/* Callback when an STA is received after having sent an STR. */
1201static void handle_sta(void * data, struct msg ** answer)
1202{
1203 struct rgwp_config * cs = data;
1204 struct avp *avp;
1205 struct avp_hdr *ahdr;
1206
1207 CHECK_PARAMS_DO( data && answer && *answer, goto out );
1208
1209 /* Check the Diameter error code */
1210 CHECK_FCT_DO( fd_msg_search_avp (*answer, cs->dict.Result_Code, &avp), goto out );
1211 CHECK_PARAMS_DO( avp, goto out );
1212 CHECK_FCT_DO( fd_msg_avp_hdr ( avp, &ahdr ), goto out );
1213 if (ahdr->avp_value->u32 != ER_DIAMETER_SUCCESS)
1214 goto out;
1215
1216 /* OK, discard the message without complaining */
1217 fd_msg_free(*answer);
1218 *answer = NULL;
1219
1220out:
1221 if (answer && *answer) {
1222 char * buf = NULL; size_t buflen;
1223 CHECK_MALLOC_DO( fd_msg_dump_treeview(&buf, &buflen, NULL, *answer, NULL, 0, 1), );
1224 TRACE_DEBUG(INFO, "Received the following problematic STA message, discarding anyway: %s", buf ?: "<error>");
1225 free(buf);
1226
1227 fd_msg_free(*answer);
1228 *answer = NULL;
1229 }
1230 return;
1231}
1232
1233static int acct_diam_ans( struct rgwp_config * cs, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli )
1234{
1235 struct session * sess;
1236 struct sess_state * st = NULL, stloc;
1237 struct avp *avp, *next;
1238 struct avp_hdr *ahdr, *oh, *or;
1239 os0_t sid = NULL;
1240 size_t sidlen;
1241
1242 TRACE_ENTRY("%p %p %p %p", cs, diam_ans, rad_fw, cli);
1243 CHECK_PARAMS(cs);
1244
1245 CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, *diam_ans, &sess, NULL) );
1246 if (sess) {
1247 CHECK_FCT( fd_sess_state_retrieve( cs->sess_hdl, sess, &st ) );
1248 CHECK_FCT( fd_sess_getsid(sess, &sid, &sidlen) );
1249 }
1250
1251 if (!st) {
1252 TRACE_DEBUG(INFO, "Received an ACA without corresponding session information, cannot translate to RADIUS");
1253 return EINVAL;
1254 }
1255
1256 /* Free the state */
1257 memcpy(&stloc, st, sizeof(struct sess_state));
1258 free(st);
1259 st = &stloc;
1260
1261 /* Search these AVPs first */
1262 CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Origin_Host, &avp) );
1263 CHECK_FCT( fd_msg_avp_hdr ( avp, &oh ) );
1264
1265 CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Origin_Realm, &avp) );
1266 CHECK_FCT( fd_msg_avp_hdr ( avp, &or ) );
1267
1268 /* Check the Diameter error code */
1269 CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Result_Code, &avp) );
1270 ASSERT( avp ); /* otherwise the message should have been discarded a lot earlier because of ABNF */
1271 CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
1272 switch (ahdr->avp_value->u32) {
1273 case ER_DIAMETER_SUCCESS:
1274 case ER_DIAMETER_LIMITED_SUCCESS:
1275 (*rad_fw)->hdr->code = RADIUS_CODE_ACCOUNTING_RESPONSE;
1276 break;
1277
1278 default:
1279 fd_log_debug("[acct.rgwx] Received Diameter answer with error code '%d' from server '%.*s', session %.*s, not translating into Accounting-Response",
1280 ahdr->avp_value->u32,
1281 (int)oh->avp_value->os.len, oh->avp_value->os.data,
1282 (int)sidlen, sid);
1283 CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Error_Message, &avp) );
1284 if (avp) {
1285 CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
1286 fd_log_debug("[acct.rgwx] Error-Message content: '%.*s'",
1287 (int)ahdr->avp_value->os.len, ahdr->avp_value->os.data);
1288 }
1289 CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Error_Reporting_Host, &avp) );
1290 if (avp) {
1291 CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
1292 fd_log_debug("[acct.rgwx] Error-Reporting-Host: '%.*s'",
1293 (int)ahdr->avp_value->os.len, ahdr->avp_value->os.data);
1294 }
1295 CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Failed_AVP, &avp) );
1296 if (avp) {
1297 fd_log_debug("[acct.rgwx] Failed-AVP was included in the message.");
1298 /* Dump its content ? */
1299 }
1300
1301 /* Now, destroy the Diameter message, since we know it is not converted to RADIUS */
1302 CHECK_FCT( fd_msg_free(*diam_ans) );
1303 *diam_ans = NULL;
1304
1305 return -1;
1306 }
1307 /* Remove this Result-Code avp */
1308 CHECK_FCT( fd_msg_free( avp ) );
1309
1310 /* If it was a response to a STOP record, we must send an STR for this session */
1311 if (st->send_str) {
1312 struct msg * str = NULL;
1313 struct msg_hdr * hdr = NULL;
1314 DiamId_t fqdn;
1315 size_t fqdn_len;
1316 DiamId_t realm;
1317 size_t realm_len;
1318 union avp_value avp_val;
1319
1320 /* Create a new STR message */
1321 CHECK_FCT( fd_msg_new ( cs->dict.Session_Termination_Request, MSGFL_ALLOC_ETEID, &str ) );
1322
1323 /* Set the application-id to the auth application if available, accounting otherwise (not sure what is actually expected...) */
1324 CHECK_FCT( fd_msg_hdr ( str, &hdr ) );
1325 hdr->msg_appl = st->auth_appl ?: AI_ACCT;
1326
1327 /* Add the Session-Id AVP as first AVP */
1328 CHECK_FCT( fd_msg_avp_new ( cs->dict.Session_Id, 0, &avp ) );
1329 avp_val.os.data = sid;
1330 avp_val.os.len = sidlen;
1331 CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
1332 CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_FIRST_CHILD, avp) );
1333 CHECK_FCT( fd_sess_ref_msg(sess) );
1334
1335 /* Add the Destination-Realm as next AVP */
1336 CHECK_FCT( fd_msg_avp_new ( cs->dict.Destination_Realm, 0, &avp ) );
1337 CHECK_FCT( fd_msg_avp_setvalue ( avp, or->avp_value ) );
1338 CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
1339
1340 /* Get information on the NAS */
1341 CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &fqdn_len, &realm, &realm_len) );
1342
1343 /* Add the Origin-Host as next AVP */
1344 CHECK_FCT( fd_msg_avp_new ( cs->dict.Origin_Host, 0, &avp ) );
1345 memset(&avp_val, 0, sizeof(avp_val));
1346 avp_val.os.data = (unsigned char *)fqdn;
1347 avp_val.os.len = fqdn_len;
1348 CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
1349 CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
1350
1351 /* Add the Origin-Realm as next AVP */
1352 CHECK_FCT( fd_msg_avp_new ( cs->dict.Origin_Realm, 0, &avp ) );
1353 memset(&avp_val, 0, sizeof(avp_val));
1354 avp_val.os.data = (unsigned char *)realm;
1355 avp_val.os.len = realm_len;
1356 CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
1357 CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
1358
1359 /* Auth-Application-Id -- if we did not get it from our Class attribute, we just set "0" */
1360 CHECK_FCT( fd_msg_avp_new ( cs->dict.Auth_Application_Id, 0, &avp ) );
1361 avp_val.u32 = st->auth_appl;
1362 CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
1363 CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
1364
1365 /* Termination-Cause */
1366 CHECK_FCT( fd_msg_avp_new ( cs->dict.Termination_Cause, 0, &avp ) );
1367 avp_val.u32 = st->term_cause;
1368 CHECK_FCT( fd_msg_avp_setvalue ( avp, &avp_val ) );
1369 CHECK_FCT( fd_msg_avp_add ( str, MSG_BRW_LAST_CHILD, avp) );
1370
1371 /* Send this message */
1372 CHECK_FCT( fd_msg_send ( &str, handle_sta, cs ) );
1373 }
1374
1375 /*
1376 No attributes should be found in
1377 Accounting-Response packets except Proxy-State and possibly Vendor-
1378 Specific.
1379 */
1380
1381 /* Now loop in the list of AVPs and convert those that we know how */
1382 CHECK_FCT( fd_msg_browse(*diam_ans, MSG_BRW_FIRST_CHILD, &next, NULL) );
1383
1384 while (next) {
1385 int handled = 1;
1386 avp = next;
1387 CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &next, NULL) );
1388
1389 CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
1390
1391 if (!(ahdr->avp_flags & AVP_FLAG_VENDOR)) {
1392 switch (ahdr->avp_code) {
1393 /* Based on RFC4005, sec 3.10 */
1394 case DIAM_ATTR_SESSION_ID:
1395 case DIAM_ATTR_ORIGIN_HOST:
1396 case DIAM_ATTR_ORIGIN_REALM:
1397 case DIAM_ATTR_ACCOUNTING_RECORD_TYPE:
1398 case DIAM_ATTR_ACCOUNTING_RECORD_NUMBER:
1399 case DIAM_ATTR_ACCT_APPLICATION_ID:
1400 case DIAM_ATTR_VENDOR_SPECIFIC_APPLICATION_ID:
1401 case DIAM_ATTR_USER_NAME:
1402 case DIAM_ATTR_ACCOUNTING_SUB_SESSION_ID:
1403 case DIAM_ATTR_ACCT_SESSION_ID:
1404 case DIAM_ATTR_ACCT_MULTI_SESSION_ID:
1405 case DIAM_ATTR_EVENT_TIMESTAMP:
1406 case DIAM_ATTR_ORIGIN_AAA_PROTOCOL:
1407 case DIAM_ATTR_ORIGIN_STATE_ID:
1408 case DIAM_ATTR_NAS_IDENTIFIER:
1409 case DIAM_ATTR_NAS_IP_ADDRESS:
1410 case DIAM_ATTR_NAS_IPV6_ADDRESS:
1411 case DIAM_ATTR_NAS_PORT:
1412 case DIAM_ATTR_NAS_PORT_ID:
1413 case DIAM_ATTR_NAS_PORT_TYPE:
1414 case DIAM_ATTR_SERVICE_TYPE:
1415 case DIAM_ATTR_TERMINATION_CAUSE:
1416 case DIAM_ATTR_ACCOUNTING_REALTIME_REQUIRED:
1417 case DIAM_ATTR_ACCT_INTERIM_INTERVAL:
1418 case DIAM_ATTR_CLASS:
1419 /* We just remove these AVP, they are not expected in RADIUS client */
1420 break;
1421
1422 default:
1423 /* Leave the AVP in the message for further treatment */
1424 handled = 0;
1425 }
1426 } else {
1427 /* Vendor-specific AVPs */
1428 switch (ahdr->avp_vendor) {
1429
1430 default: /* unknown vendor */
1431 handled = 0;
1432 }
1433 }
1434
1435 if (handled) {
1436 CHECK_FCT( fd_msg_free( avp ) );
1437 }
1438 }
1439
1440 /*
1441 The Authenticator field in an Accounting-Response packet is called
1442 the Response Authenticator, and contains a one-way MD5 hash
1443 calculated over a stream of octets consisting of the Accounting-
1444 Response Code, Identifier, Length, the Request Authenticator field
1445 from the Accounting-Request packet being replied to, and the
1446 response attributes if any, followed by the shared secret. The
1447 resulting 16 octet MD5 hash value is stored in the Authenticator
1448 field of the Accounting-Response packet.
1449
1450 -- done in radius_msg_finish_srv
1451 */
1452
1453 return 0;
1454}
1455
1456/* The exported symbol */
1457struct rgw_api rgwp_descriptor = {
1458 .rgwp_name = "acct",
1459 .rgwp_conf_parse = acct_conf_parse,
1460 .rgwp_conf_free = acct_conf_free,
1461 .rgwp_rad_req = acct_rad_req,
1462 .rgwp_diam_ans = acct_diam_ans
1463};