blob: f78b97e7c5066f0dbd4dee7b5d57e9068b02a496 [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 Access-Request messages translation plugin */
37
38#include "rgw_common.h"
39
40/* Attributes missing from radius.h */
41#define RADIUS_ATTR_CHAP_PASSWORD 3
42#define RADIUS_ATTR_ARAP_PASSWORD 70
43
44/* Other constants we use */
45#define AI_NASREQ 1 /* Diameter NASREQ */
46#define AI_EAP 5 /* Diameter EAP application */
47#define CC_AA 265 /* AAR */
48#define CC_DIAMETER_EAP 268 /* DER */
49#define ACV_ART_AUTHORIZE_AUTHENTICATE 3 /* AUTHORIZE_AUTHENTICATE */
50#define ACV_OAP_RADIUS 1 /* RADIUS */
51#define ACV_ASS_STATE_MAINTAINED 0 /* STATE_MAINTAINED */
52#define ACV_ASS_NO_STATE_MAINTAINED 1 /* NO_STATE_MAINTAINED */
53
54/* The state we keep for this plugin */
55struct rgwp_config {
56 struct {
57 struct dict_object * ARAP_Password; /* ARAP-Password */
58 struct dict_object * ARAP_Security; /* ARAP-Security */
59 struct dict_object * ARAP_Security_Data; /* ARAP-Security-Data */
60 struct dict_object * Auth_Application_Id; /* Auth-Application-Id */
61 struct dict_object * Auth_Request_Type; /* Auth-Request-Type */
62 struct dict_object * Authorization_Lifetime; /* Authorization-Lifetime */
63 struct dict_object * Callback_Number; /* Callback-Number */
64 struct dict_object * Called_Station_Id; /* Called-Station-Id */
65 struct dict_object * Calling_Station_Id; /* Calling-Station-Id */
66 struct dict_object * CHAP_Algorithm; /* CHAP-Algorithm */
67 struct dict_object * CHAP_Auth; /* CHAP-Auth */
68 struct dict_object * CHAP_Challenge; /* CHAP-Challenge */
69 struct dict_object * CHAP_Ident; /* CHAP-Ident */
70 struct dict_object * CHAP_Response; /* CHAP-Response */
71 struct dict_object * Destination_Host; /* Destination-Host */
72 struct dict_object * Destination_Realm; /* Destination-Realm */
73 struct dict_object * Connect_Info; /* Connect-Info */
74 struct dict_object * EAP_Payload; /* EAP-Payload */
75 struct dict_object * Error_Message; /* Error-Message */
76 struct dict_object * Error_Reporting_Host; /* Error-Reporting-Host */
77 struct dict_object * Failed_AVP; /* Failed-AVP */
78 struct dict_object * Framed_Compression; /* Framed-Compression */
79 struct dict_object * Framed_IP_Address; /* Framed-IP-Address */
80 struct dict_object * Framed_IP_Netmask; /* Framed-IP-Netmask */
81 struct dict_object * Framed_Interface_Id; /* Framed-Interface-Id */
82 struct dict_object * Framed_IPv6_Prefix; /* Framed-IPv6-Prefix */
83 struct dict_object * Framed_MTU; /* Framed-MTU */
84 struct dict_object * Framed_Protocol; /* Framed-Protocol */
85 struct dict_object * Login_IP_Host; /* Login-IP-Host */
86 struct dict_object * Login_IPv6_Host; /* Login-IPv6-Host */
87 struct dict_object * Login_LAT_Group; /* Login-LAT-Group */
88 struct dict_object * Login_LAT_Node; /* Login-LAT-Node */
89 struct dict_object * Login_LAT_Port; /* Login-LAT-Port */
90 struct dict_object * Login_LAT_Service; /* Login-LAT-Service */
91 struct dict_object * NAS_Identifier; /* NAS-Identifier */
92 struct dict_object * NAS_IP_Address; /* NAS-IP-Address */
93 struct dict_object * NAS_IPv6_Address; /* NAS-IPv6-Address */
94 struct dict_object * NAS_Port; /* NAS-Port */
95 struct dict_object * NAS_Port_Id; /* NAS-Port-Id */
96 struct dict_object * NAS_Port_Type; /* NAS-Port-Type */
97 struct dict_object * Origin_AAA_Protocol; /* Origin-AAA-Protocol */
98 struct dict_object * Origin_Host; /* Origin-Host */
99 struct dict_object * Origin_Realm; /* Origin-Realm */
100 struct dict_object * Originating_Line_Info; /* Originating-Line-Info */
101 struct dict_object * Port_Limit; /* Port-Limit */
102 struct dict_object * Re_Auth_Request_Type; /* Re-Auth-Request-Type */
103 struct dict_object * Result_Code; /* Result-Code */
104 struct dict_object * Service_Type; /* Service-Type */
105 struct dict_object * Session_Id; /* Session-Id */
106 struct dict_object * Session_Timeout; /* Session-Timeout */
107 struct dict_object * State; /* State */
108 struct dict_object * Tunneling; /* Tunneling */
109 struct dict_object * Tunnel_Type; /* Tunnel-Type */
110 struct dict_object * Tunnel_Medium_Type; /* Tunnel-Medium-Type */
111 struct dict_object * Tunnel_Client_Endpoint; /* Tunnel-Client-Endpoint */
112 struct dict_object * Tunnel_Server_Endpoint; /* Tunnel-Server-Endpoint */
113 struct dict_object * Tunnel_Private_Group_Id; /* Tunnel-Private-Group-Id */
114 struct dict_object * Tunnel_Preference; /* Tunnel-Preference */
115 struct dict_object * Tunnel_Client_Auth_Id; /* Tunnel-Client-Auth-Id */
116 struct dict_object * Tunnel_Server_Auth_Id; /* Tunnel-Server-Auth-Id */
117 struct dict_object * User_Name; /* User-Name */
118 struct dict_object * User_Password; /* User-Password */
119
120 } dict; /* cache of the dictionary objects we use */
121 struct session_handler * sess_hdl; /* We store RADIUS request authenticator information in the session */
122 char * confstr;
123
124 int ignore_nai;
125};
126
127struct sess_state {
128 uint8_t req_auth[16];
129};
130
131
132/* Initialize the plugin */
133static int auth_conf_parse(char * confstr, struct rgwp_config ** state)
134{
135 struct rgwp_config * new;
136 struct dict_object * app;
137
138 TRACE_ENTRY("%p %p", confstr, state);
139 CHECK_PARAMS( state );
140
141 CHECK_MALLOC( new = malloc(sizeof(struct rgwp_config)) );
142 memset(new, 0, sizeof(struct rgwp_config));
143
144 CHECK_FCT( fd_sess_handler_create( &new->sess_hdl, (void *)free, NULL, NULL ) );
145 new->confstr = confstr;
146
147 if (confstr && strstr(confstr, "nonai"))
148 new->ignore_nai = 1;
149
150 /* Resolve all dictionary objects we use */
151 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "ARAP-Password", &new->dict.ARAP_Password, ENOENT) );
152 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "ARAP-Security", &new->dict.ARAP_Security, ENOENT) );
153 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "ARAP-Security-Data", &new->dict.ARAP_Security_Data, ENOENT) );
154 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Application-Id", &new->dict.Auth_Application_Id, ENOENT) );
155 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Request-Type", &new->dict.Auth_Request_Type, ENOENT) );
156 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Authorization-Lifetime", &new->dict.Authorization_Lifetime, ENOENT) );
157 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Callback-Number", &new->dict.Callback_Number, ENOENT) );
158 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Called-Station-Id", &new->dict.Called_Station_Id, ENOENT) );
159 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Calling-Station-Id", &new->dict.Calling_Station_Id, ENOENT) );
160 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "CHAP-Algorithm", &new->dict.CHAP_Algorithm, ENOENT) );
161 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "CHAP-Auth", &new->dict.CHAP_Auth, ENOENT) );
162 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "CHAP-Challenge", &new->dict.CHAP_Challenge, ENOENT) );
163 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "CHAP-Ident", &new->dict.CHAP_Ident, ENOENT) );
164 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "CHAP-Response", &new->dict.CHAP_Response, ENOENT) );
165 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Connect-Info", &new->dict.Connect_Info, ENOENT) );
166 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Host", &new->dict.Destination_Host, ENOENT) );
167 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Realm", &new->dict.Destination_Realm, ENOENT) );
168 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "EAP-Payload", &new->dict.EAP_Payload, ENOENT) );
169 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Message", &new->dict.Error_Message, ENOENT) );
170 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Error-Reporting-Host", &new->dict.Error_Reporting_Host, ENOENT) );
171 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Failed-AVP", &new->dict.Failed_AVP, ENOENT) );
172 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Compression", &new->dict.Framed_Compression, ENOENT) );
173 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IP-Address", &new->dict.Framed_IP_Address, ENOENT) );
174 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IP-Netmask", &new->dict.Framed_IP_Netmask, ENOENT) );
175 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Interface-Id", &new->dict.Framed_Interface_Id, ENOENT) );
176 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-IPv6-Prefix", &new->dict.Framed_IPv6_Prefix, ENOENT) );
177 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-MTU", &new->dict.Framed_MTU, ENOENT) );
178 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Framed-Protocol", &new->dict.Framed_Protocol, ENOENT) );
179 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-IP-Host", &new->dict.Login_IP_Host, ENOENT) );
180 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-IPv6-Host", &new->dict.Login_IPv6_Host, ENOENT) );
181 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Group", &new->dict.Login_LAT_Group, ENOENT) );
182 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Node", &new->dict.Login_LAT_Node, ENOENT) );
183 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Port", &new->dict.Login_LAT_Port, ENOENT) );
184 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Login-LAT-Service", &new->dict.Login_LAT_Service, ENOENT) );
185 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Identifier", &new->dict.NAS_Identifier, ENOENT) );
186 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-IP-Address", &new->dict.NAS_IP_Address, ENOENT) );
187 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-IPv6-Address", &new->dict.NAS_IPv6_Address, ENOENT) );
188 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Port", &new->dict.NAS_Port, ENOENT) );
189 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Port-Id", &new->dict.NAS_Port_Id, ENOENT) );
190 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "NAS-Port-Type", &new->dict.NAS_Port_Type, ENOENT) );
191 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-AAA-Protocol", &new->dict.Origin_AAA_Protocol, ENOENT) );
192 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &new->dict.Origin_Host, ENOENT) );
193 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &new->dict.Origin_Realm, ENOENT) );
194 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Originating-Line-Info", &new->dict.Originating_Line_Info, ENOENT) );
195 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Port-Limit", &new->dict.Port_Limit, ENOENT) );
196 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) );
197 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Result-Code", &new->dict.Result_Code, ENOENT) );
198 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Service-Type", &new->dict.Service_Type, ENOENT) );
199 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &new->dict.Session_Id, ENOENT) );
200 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Timeout", &new->dict.Session_Timeout, ENOENT) );
201 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "State", &new->dict.State, ENOENT) );
202 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunneling", &new->dict.Tunneling, ENOENT) );
203 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Type", &new->dict.Tunnel_Type, ENOENT) );
204 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Medium-Type", &new->dict.Tunnel_Medium_Type, ENOENT) );
205 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Client-Endpoint", &new->dict.Tunnel_Client_Endpoint, ENOENT) );
206 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Server-Endpoint", &new->dict.Tunnel_Server_Endpoint, ENOENT) );
207 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) );
208 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Tunnel-Preference", &new->dict.Tunnel_Preference, ENOENT) );
209 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) );
210 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) );
211 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "User-Name", &new->dict.User_Name, ENOENT) );
212 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "User-Password", &new->dict.User_Password, ENOENT) );
213
214 /* This plugin provides the following Diameter authentication applications support: */
215 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Diameter Network Access Server Application", &app, ENOENT) );
216 CHECK_FCT( fd_disp_app_support ( app, NULL, 1, 0 ) );
217
218 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Diameter Extensible Authentication Protocol (EAP) Application", &app, ENOENT) );
219 CHECK_FCT( fd_disp_app_support ( app, NULL, 1, 0 ) );
220
221 *state = new;
222 return 0;
223}
224
225/* deinitialize */
226static void auth_conf_free(struct rgwp_config * state)
227{
228 TRACE_ENTRY("%p", state);
229 CHECK_PARAMS_DO( state, return );
230 CHECK_FCT_DO( fd_sess_handler_destroy( &state->sess_hdl, NULL ), );
231 free(state);
232 return;
233}
234
235/* Handle an incoming RADIUS request */
236static int auth_rad_req( struct rgwp_config * cs, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
237{
238 int idx;
239 int got_id = 0;
240 int got_mac = 0;
241 int got_passwd = 0;
242 int got_eap = 0;
243 int got_empty_eap = 0;
244 const char * prefix = "Diameter/";
245 size_t pref_len;
246 os0_t dh = NULL;
247 size_t dh_len = 0;
248 os0_t dr = NULL;
249 size_t dr_len = 0;
250 os0_t si = NULL;
251 size_t si_len = 0;
252 os0_t un = NULL;
253 size_t un_len = 0;
254 size_t nattr_used = 0;
255 struct avp ** avp_tun = NULL, *avp = NULL;
256 union avp_value value;
257 struct session * sess;
258
259 TRACE_ENTRY("%p %p %p %p %p", cs, rad_req, rad_ans, diam_fw, cli);
260 CHECK_PARAMS(cs && rad_req && (rad_req->hdr->code == RADIUS_CODE_ACCESS_REQUEST) && rad_ans && diam_fw && *diam_fw);
261
262 pref_len = strlen(prefix);
263
264 /*
265 Guidelines:
266 http://tools.ietf.org/html/rfc4005#section-9.1
267 http://tools.ietf.org/html/rfc4072#section-6.1
268
269 When a Translation Agent receives a RADIUS message, the following
270 steps should be taken:
271
272 - If a Message-Authenticator attribute is present, the value MUST
273 be checked but not included in the Diameter message. If it is
274 incorrect, the RADIUS message should be silently discarded.
275 The gateway system SHOULD generate and include a Message-
276 Authenticator in returned RADIUS responses.
277 -> done in rgw_msg_auth_check
278
279 - The transport address of the sender MUST be checked against the
280 NAS identifying attributes. See the description of NAS-
281 Identifier and NAS-IP-Address below.
282 -> done in rgw_clients_check_origin
283
284 - The Translation Agent must maintain transaction state
285 information relevant to the RADIUS request, such as the
286 Identifier field in the RADIUS header, any existing RADIUS
287 Proxy-State attribute, and the source IP address and port
288 number of the UDP packet. These may be maintained locally in a
289 state table or saved in a Proxy-Info AVP group. A Diameter
290 Session-Id AVP value must be created using a session state
291 mapping mechanism.
292 -> Identifier, source and port are saved along with the request,
293 and associated with the session state.
294 -> sub_echo_drop should handle the Proxy-State attribute (conf issue)
295
296 - The Diameter Origin-Host and Origin-Realm AVPs MUST be created
297 and added by using the information from an FQDN corresponding
298 to the NAS-IP-Address attribute (preferred if available),
299 and/or to the NAS-Identifier attribute. (Note that the RADIUS
300 NAS-Identifier is not required to be an FQDN.)
301 -> done in rgw_clients_create_origin.
302
303 - The response MUST have an Origin-AAA-Protocol AVP added,
304 indicating the protocol of origin of the message.
305 -> what "response" ??? Added to the AAR / DER in this function.
306
307 - The Proxy-Info group SHOULD be added, with the local server's
308 identity specified in the Proxy-Host AVP. This should ensure
309 that the response is returned to this system.
310 -> We don't need this, answer is always routed here anyway.
311
312 For EAP:
313
314 o RADIUS EAP-Message attribute(s) are translated to a Diameter
315 EAP-Payload AVP. If multiple RADIUS EAP-Message attributes are
316 present, they are concatenated and translated to a single Diameter
317 EAP-Payload AVP.
318 -> concatenation done by radius_msg_get_eap
319
320 -> the remaining is specific conversion rules
321 */
322
323 /* Check basic information is there, and also retrieve some attribute information */
324 for (idx = 0; idx < rad_req->attr_used; idx++) {
325 struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(rad_req->buf + rad_req->attr_pos[idx]);
326 uint8_t * attr_val = (uint8_t *)(attr + 1);
327 size_t attr_len = attr->length - sizeof(struct radius_attr_hdr);
328
329 switch (attr->type) {
330 case RADIUS_ATTR_NAS_IP_ADDRESS:
331 case RADIUS_ATTR_NAS_IDENTIFIER:
332 case RADIUS_ATTR_NAS_IPV6_ADDRESS:
333 got_id = 1;
334 break;
335 case RADIUS_ATTR_MESSAGE_AUTHENTICATOR:
336 got_mac = 1;
337 break;
338 case RADIUS_ATTR_EAP_MESSAGE:
339 got_eap = 1;
340 if (attr->length == 2)
341 got_empty_eap = 1;
342 break;
343 case RADIUS_ATTR_USER_PASSWORD:
344 case RADIUS_ATTR_CHAP_PASSWORD:
345 case RADIUS_ATTR_ARAP_PASSWORD:
346 got_passwd += 1;
347 break;
348
349 /* Is there a State attribute with prefix "Diameter/" in the message? (in that case: Diameter/Destination-Host/Destination-Realm/Session-Id) */
350 /* NOTE: RFC4005 says "Origin-Host" here, but it's not coherent with the rules for answers. Destination-Host makes more sense */
351 case RADIUS_ATTR_STATE:
352 if ((attr_len > pref_len + 5 /* for the '/'s and non empty strings */ )
353 && ! memcmp(attr_val, prefix, pref_len)) {
354 int i, start;
355
356 TRACE_DEBUG(ANNOYING, "Found a State attribute with '%s' prefix (attr #%d).", prefix, idx);
357
358 /* Now parse the value and check its content is valid. Unfortunately we cannot use strchr here since strings are not \0-terminated */
359
360 i = start = pref_len;
361 dh = attr_val + i;
362 for (; (i < attr_len - 2) && (attr_val[i] != '/'); i++) /* loop */;
363 if ( i >= attr_len - 2 ) continue; /* the attribute format is not good */
364 dh_len = i - start;
365
366 start = ++i;
367 dr = attr_val + i;
368 for (; (i < attr_len - 1) && (attr_val[i] != '/'); i++) /* loop */;
369 if ( i >= attr_len - 1 ) continue; /* the attribute format is not good */
370 dr_len = i - start;
371
372 i++;
373 si = attr_val + i;
374 si_len = attr_len - i;
375
376 TRACE_DEBUG(ANNOYING, "Attribute parsed successfully: DH:'%.*s' DR:'%.*s' SI:'%.*s'.", (int)dh_len, dh, (int)dr_len, dr, (int)si_len, si);
377 /* Remove from the message */
378 for (i = idx + 1; i < rad_req->attr_used; i++)
379 rad_req->attr_pos[i - 1] = rad_req->attr_pos[i];
380 rad_req->attr_used -= 1;
381 idx--;
382 }
383 break;
384
385 case RADIUS_ATTR_USER_NAME:
386 TRACE_DEBUG(ANNOYING, "Found a User-Name attribute: '%.*s'", (int)attr_len, attr_len ? (char *)attr_val : "");
387 un = attr_val;
388 un_len = attr_len;
389 break;
390
391 }
392 }
393 if (!got_id) {
394 TRACE_DEBUG(INFO, "RADIUS Access-Request did not contain a NAS IP or Identifier attribute, reject.");
395 return EINVAL;
396 }
397 /* [Note 1] An Access-Request that contains either a User-Password or
398 CHAP-Password or ARAP-Password or one or more EAP-Message attributes
399 MUST NOT contain more than one type of those four attributes. If it
400 does not contain any of those four attributes, it SHOULD contain a
401 Message-Authenticator. If any packet type contains an EAP-Message
402 attribute it MUST also contain a Message-Authenticator. A RADIUS
403 server receiving an Access-Request not containing any of those four
404 attributes and also not containing a Message-Authenticator attribute
405 SHOULD silently discard it. */
406 if (((got_eap + got_passwd) > 1) || (got_eap && !got_mac) || (!got_eap && !got_passwd && !got_mac)) {
407 TRACE_DEBUG(INFO, "RADIUS Access-Request not conform to RFC3579 sec 3.3 note 1, discard.");
408 return EINVAL;
409 }
410
411
412
413 /*
414 - If the RADIUS request contained a State attribute and the
415 prefix of the data is "Diameter/", the data following the
416 prefix contains the Diameter Origin-Host/Origin-Realm/Session-
417 Id. If no such attributes are present and the RADIUS command
418 is an Access-Request, a new Session-Id is created. The
419 Session-Id is included in the Session-Id AVP.
420 */
421
422 /* Add the Destination-Realm AVP */
423 CHECK_FCT( fd_msg_avp_new ( cs->dict.Destination_Realm, 0, &avp ) );
424 if (dr) {
425 value.os.data = (unsigned char *)dr;
426 value.os.len = dr_len;
427 } else {
428 int i = 0;
429 if (un && ! cs->ignore_nai) {
430 /* Is there an '@' in the user name? We don't care for decorated NAI here */
431 for (i = un_len - 2; i > 0; i--) {
432 if (un[i] == '@') {
433 i++;
434 break;
435 }
436 }
437 }
438 if (i <= 0) {
439 /* Not found in the User-Name => we use the local domain of this gateway */
440 value.os.data = (uint8_t *)fd_g_config->cnf_diamrlm;
441 value.os.len = fd_g_config->cnf_diamrlm_len;
442 } else {
443 value.os.data = un + i;
444 value.os.len = un_len - i;
445 }
446 }
447 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
448 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) );
449
450 /* Add the Destination-Host if found */
451 if (dh) {
452 CHECK_FCT( fd_msg_avp_new ( cs->dict.Destination_Host, 0, &avp ) );
453 value.os.data = (unsigned char *)dh;
454 value.os.len = dh_len;
455 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
456 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) );
457 }
458
459 /* Create the session if it is not already done */
460 {
461 os0_t sess_str = NULL;
462 size_t sess_strlen;
463
464 if (si_len) {
465 /* We already have the Session-Id, just use it */
466 CHECK_FCT( fd_sess_fromsid_msg ( si, si_len, &sess, NULL) );
467 } else {
468 /* Create a new Session-Id string */
469
470 DiamId_t fqdn;
471 size_t fqdnlen;
472 DiamId_t realm;
473 size_t realmlen;
474
475 /* Get information on the RADIUS client */
476 CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &fqdnlen, &realm, &realmlen) );
477
478 /* If we have a user name, create the new session with it */
479 if (un) {
480 int len;
481 /* If not found, create a new Session-Id. Our format is: {fqdn;hi32;lo32;username;diamid} */
482 CHECK_MALLOC( sess_str = malloc(un_len + 1 /* ';' */ + fd_g_config->cnf_diamid_len + 1 /* '\0' */) );
483 len = sprintf((char *)sess_str, "%.*s;%s", (int)un_len, un, fd_g_config->cnf_diamid);
484 CHECK_FCT( fd_sess_new(&sess, fqdn, fqdnlen, sess_str, len) );
485 free(sess_str);
486 } else {
487 /* We don't have enough information to create the Session-Id, the RADIUS message is probably invalid */
488 TRACE_DEBUG(INFO, "RADIUS Access-Request does not contain a User-Name attribute, rejecting.");
489 return EINVAL;
490 }
491 }
492
493 /* Now, add the Session-Id AVP at beginning of Diameter message */
494 CHECK_FCT( fd_sess_getsid(sess, &sess_str, &sess_strlen) );
495 TRACE_DEBUG(FULL, "[auth.rgwx] Translating new message for session '%s'...", sess_str);
496
497 /* Now add this session in the message */
498 CHECK_FCT( fd_msg_avp_new ( cs->dict.Session_Id, 0, &avp ) );
499 value.os.data = sess_str;
500 value.os.len = sess_strlen;
501 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
502 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) );
503 CHECK_FCT( fd_msg_sess_set(*diam_fw, sess) );
504 }
505
506
507 /* Add the appropriate command code & Auth-Application-Id */
508 {
509 struct msg_hdr * header = NULL;
510 CHECK_FCT( fd_msg_hdr ( *diam_fw, &header ) );
511 header->msg_flags = CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE;
512 if (got_eap) {
513 header->msg_code = CC_DIAMETER_EAP;
514 header->msg_appl = AI_EAP;
515 } else {
516 header->msg_code = CC_AA;
517 header->msg_appl = AI_NASREQ;
518 }
519
520 /* Add the Auth-Application-Id */
521 {
522 CHECK_FCT( fd_msg_avp_new ( cs->dict.Auth_Application_Id, 0, &avp ) );
523 value.i32 = header->msg_appl;
524 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
525 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
526 }
527 }
528
529 /* The type of request is identified through the Auth-Request-Type AVP
530 [BASE]. The recommended value for most RADIUS interoperabily
531 situations is AUTHORIZE_AUTHENTICATE. */
532
533 /* Add Auth-Request-Type AVP */
534 {
535 CHECK_FCT( fd_msg_avp_new ( cs->dict.Auth_Request_Type, 0, &avp ) );
536 value.i32 = ACV_ART_AUTHORIZE_AUTHENTICATE;
537 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
538 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
539 }
540
541 /* Add Origin-AAA-Protocol AVP */
542 {
543 CHECK_FCT( fd_msg_avp_new ( cs->dict.Origin_AAA_Protocol, 0, &avp ) );
544 value.i32 = ACV_OAP_RADIUS;
545 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
546 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
547 }
548
549 /* Convert the EAP payload (concat RADIUS attributes) */
550 if (got_eap) {
551 CHECK_FCT( fd_msg_avp_new ( cs->dict.EAP_Payload, 0, &avp ) );
552
553 /* o An empty RADIUS EAP-Message attribute (with length 2) signifies
554 EAP-Start, and it is translated to an empty EAP-Payload AVP. */
555 if (got_empty_eap) {
556 value.os.len = 0;
557 value.os.data = (uint8_t *)"";
558 } else {
559 CHECK_MALLOC( value.os.data = radius_msg_get_eap(rad_req, &value.os.len) );
560 }
561
562 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
563 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
564 }
565
566 /* Tunnel AVPs need some preparation */
567 /* Convert the attributes one by one */
568 for (idx = 0; idx < rad_req->attr_used; idx++) {
569 struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(rad_req->buf + rad_req->attr_pos[idx]);
570
571 switch (attr->type) {
572
573 /* This macro converts a RADIUS attribute to a Diameter AVP of type OctetString */
574 #define CONV2DIAM_STR( _dictobj_ ) \
575 CHECK_PARAMS( attr->length >= sizeof(struct radius_attr_hdr) ); \
576 /* Create the AVP with the specified dictionary model */ \
577 CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \
578 value.os.len = attr->length - sizeof(struct radius_attr_hdr); \
579 value.os.data = (os0_t)(attr + 1); \
580 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \
581 /* Add the AVP in the Diameter message. */ \
582 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); \
583
584 /* Same thing, for scalar AVPs of 32 bits */
585 #define CONV2DIAM_32B( _dictobj_ ) \
586 CHECK_PARAMS( attr->length == sizeof(struct radius_attr_hdr)+sizeof(uint32_t) );\
587 CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \
588 { \
589 uint8_t * v = (uint8_t *)(attr + 1); \
590 value.u32 = (v[0] << 24) \
591 | (v[1] << 16) \
592 | (v[2] << 8) \
593 | v[3] ; \
594 } \
595 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \
596 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); \
597
598 /* And the 64b version */
599 #define CONV2DIAM_64B( _dictobj_ ) \
600 CHECK_PARAMS( attr->length == sizeof(struct radius_attr_hdr)+sizeof(uint64_t) );\
601 CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \
602 { \
603 uint8_t * v = (uint8_t *)(attr + 1); \
604 value.u64 = ((uint64_t)(v[0]) << 56) \
605 | ((uint64_t)(v[1]) << 48) \
606 | ((uint64_t)(v[2]) << 40) \
607 | ((uint64_t)(v[3]) << 32) \
608 | ((uint64_t)(v[4]) << 24) \
609 | ((uint64_t)(v[5]) << 16) \
610 | ((uint64_t)(v[6]) << 8) \
611 | (uint64_t)(v[7]) ; \
612 } \
613 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \
614 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); \
615
616 /* RFC 2865 */
617 /*
618 - The Destination-Realm AVP is created from the information found
619 in the RADIUS User-Name attribute.
620 -> done in rgw_clients_create_origin
621 */
622 case RADIUS_ATTR_USER_NAME:
623 CONV2DIAM_STR( User_Name );
624 break;
625
626 /*
627 - If the RADIUS User-Password attribute is present, the password
628 must be unencrypted by using the link's RADIUS shared secret.
629 The unencrypted value must be forwarded in a User-Password AVP
630 using Diameter security.
631 */
632 case RADIUS_ATTR_USER_PASSWORD:
633 if ((attr->length - 2) % 16) {
634 TRACE_DEBUG(INFO, "Invalid length of User-Password attribute: %hhd", attr->length);
635 return EINVAL;
636 }
637 {
638 /* Decipher following this logic (refers to rfc2865#section-5.2 )
639 b1 = MD5(S + RA) p1 = c(1) xor b1
640 b2 = MD5(S + c(1)) p2 = c(2) xor b2
641 ...
642 */
643
644 uint8_t *ciph = (uint8_t *)(attr+1); /* c(i) */
645 size_t ciph_len = attr->length - 2;
646 uint8_t deciph[128]; /* pi */
647 size_t deciph_len = 0;
648 uint8_t * secret; /* S */
649 size_t secret_len;
650 uint8_t hash[16]; /* b(i) */
651 const uint8_t *addr[2];
652 size_t len[2];
653
654 /* Retrieve the shared secret */
655 CHECK_FCT(rgw_clients_getkey(cli, &secret, &secret_len));
656
657 /* Initial b1 = MD5(S + RA) */
658 addr[0] = secret;
659 len[0] = secret_len;
660 addr[1] = rad_req->hdr->authenticator;
661 len[1] = 16;
662 md5_vector(2, addr, len, hash);
663
664 /* loop */
665 while (deciph_len < ciph_len) {
666 int i;
667 /* pi = c(i) xor bi */
668 for (i = 0; i < 16; i++)
669 deciph[deciph_len + i] = ciph[deciph_len + i] ^ hash[i];
670 /* do we have to remove the padding '\0's ? */
671
672 /* b(i+1) = MD5(S + c(i) */
673 addr[1] = ciph + deciph_len;
674 md5_vector(2, addr, len, hash);
675
676 deciph_len += 16;
677 }
678
679 /* Now save this value in the AVP */
680 CHECK_FCT( fd_msg_avp_new ( cs->dict.User_Password, 0, &avp ) );
681 value.os.data = &deciph[0];
682 value.os.len = deciph_len;
683 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
684 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
685 }
686 break;
687
688
689 /*
690 - If the RADIUS CHAP-Password attribute is present, the Ident and
691 Data portion of the attribute are used to create the CHAP-Auth
692 grouped AVP.
693 */
694 case RADIUS_ATTR_CHAP_PASSWORD:
695 CHECK_PARAMS( attr->length == 19 /* RFC 2865 */);
696 {
697 uint8_t * c = (uint8_t *)(attr + 1);
698 struct avp * chap_auth;
699 CHECK_FCT( fd_msg_avp_new ( cs->dict.CHAP_Auth, 0, &chap_auth ) );
700 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, chap_auth) );
701
702 CHECK_FCT( fd_msg_avp_new ( cs->dict.CHAP_Algorithm, 0, &avp ) );
703 value.u32 = 5; /* The only value defined currently... */
704 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
705 CHECK_FCT( fd_msg_avp_add ( chap_auth, MSG_BRW_LAST_CHILD, avp) );
706
707 CHECK_FCT( fd_msg_avp_new ( cs->dict.CHAP_Ident, 0, &avp ) );
708 value.os.data = c;
709 value.os.len = 1;
710 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
711 CHECK_FCT( fd_msg_avp_add ( chap_auth, MSG_BRW_LAST_CHILD, avp) );
712
713 c++;
714
715 CHECK_FCT( fd_msg_avp_new ( cs->dict.CHAP_Response, 0, &avp ) );
716 value.os.data = c;
717 value.os.len = attr->length - 3;
718 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
719 CHECK_FCT( fd_msg_avp_add ( chap_auth, MSG_BRW_LAST_CHILD, avp) );
720 }
721 break;
722
723 case RADIUS_ATTR_NAS_IP_ADDRESS:
724 CONV2DIAM_STR( NAS_IP_Address );
725 break;
726
727 case RADIUS_ATTR_NAS_PORT:
728 CONV2DIAM_32B( NAS_Port );
729 break;
730
731 case RADIUS_ATTR_SERVICE_TYPE:
732 CONV2DIAM_32B( Service_Type );
733 break;
734
735 case RADIUS_ATTR_FRAMED_PROTOCOL:
736 CONV2DIAM_32B( Framed_Protocol );
737 break;
738
739 case RADIUS_ATTR_FRAMED_IP_ADDRESS:
740 CONV2DIAM_STR( Framed_IP_Address );
741 break;
742
743 case RADIUS_ATTR_FRAMED_IP_NETMASK:
744 CONV2DIAM_STR( Framed_IP_Netmask );
745 break;
746
747 /* Framed-Routing never present in an Access-Request */
748 /* Filter-Id never present in an Access-Request */
749
750 case RADIUS_ATTR_FRAMED_MTU:
751 CONV2DIAM_32B( Framed_MTU );
752 break;
753
754 case RADIUS_ATTR_FRAMED_COMPRESSION:
755 CONV2DIAM_32B( Framed_Compression );
756 break;
757
758 case RADIUS_ATTR_LOGIN_IP_HOST:
759 CONV2DIAM_STR( Login_IP_Host );
760 break;
761
762 /* Login-Service never present in an Access-Request */
763 /* Login-TCP-Port never present in an Access-Request */
764 /* Reply-Message never present in an Access-Request */
765
766 case RADIUS_ATTR_CALLBACK_NUMBER:
767 CONV2DIAM_STR( Callback_Number );
768 break;
769
770 /* Callback-Id never present in an Access-Request */
771 /* Framed-Route never present in an Access-Request */
772 /* Framed-IPX-Network never present in an Access-Request */
773
774 case RADIUS_ATTR_STATE:
775 CONV2DIAM_STR( State );
776 break;
777
778 /* Class never present in an Access-Request */
779
780 case RADIUS_ATTR_VENDOR_SPECIFIC:
781 /* RFC 4005, Section 9.6 :
782 Systems that don't have vendor format knowledge MAY discard such
783 attributes without knowing a suitable translation.
784
785 [conversion rule in 9.6.2]
786 */
787 if (attr->length >= 6) {
788 uint32_t vendor_id;
789 uint8_t * c = (uint8_t *)(attr + 1);
790
791 vendor_id = c[0] << 24 | c[1] << 16 | c[2] << 8 | c[3];
792 c += 4;
793
794 switch (vendor_id) {
795
796 /* For the vendors we KNOW they follow the VSA recommended format, we convert following the rules of RFC4005 (9.6.2) */
797 case RADIUS_VENDOR_ID_MICROSOFT : /* RFC 2548 */
798 /* other vendors ? */
799 {
800 size_t left;
801 struct radius_attr_vendor *vtlv;
802
803 left = attr->length - 6;
804 vtlv = (struct radius_attr_vendor *)c;
805
806 while ((left >= 2) && (vtlv->vendor_length <= left)) {
807 /* Search our dictionary for corresponding Vendor's AVP */
808 struct dict_avp_request req;
809 struct dict_object * avp_model = NULL;
810 memset(&req, 0, sizeof(struct dict_avp_request));
811 req.avp_vendor = vendor_id;
812 req.avp_code = vtlv->vendor_type;
813
814 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_CODE_AND_VENDOR, &req, &avp_model, 0) );
815 if (!avp_model) {
816 TRACE_DEBUG(FULL, "Unknown attribute (vendor 0x%x, code 0x%x) ignored.", req.avp_vendor, req.avp_code);
817 } else {
818 CHECK_FCT( fd_msg_avp_new ( avp_model, 0, &avp ) );
819 value.os.len = vtlv->vendor_length - 2;
820 value.os.data = (unsigned char *)(vtlv + 1);
821 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
822 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
823 }
824 c += vtlv->vendor_length;
825 left -= vtlv->vendor_length;
826 vtlv = (struct radius_attr_vendor *)c;
827 }
828 }
829 break;
830
831 /* Other vendors we KNOw how to convert the attributes would be added here... */
832 /* case RADIUS_VENDOR_ID_CISCO :
833 break; */
834 /* case RADIUS_VENDOR_ID_IETF : (extended RADIUS attributes)
835 break; */
836
837 /* When we don't know, just discard the attribute... VSA are optional with regards to RADIUS anyway */
838 default:
839 /* do nothing */
840 TRACE_DEBUG(FULL, "VSA attribute from vendor %d discarded", vendor_id);
841
842 }
843 }
844 break;
845
846 /* Session-Timeout never present in an Access-Request */
847 /* Idle-Timeout never present in an Access-Request */
848 /* Termination-Action never present in an Access-Request */
849
850 case RADIUS_ATTR_CALLED_STATION_ID:
851 CONV2DIAM_STR( Called_Station_Id );
852 break;
853
854 case RADIUS_ATTR_CALLING_STATION_ID:
855 CONV2DIAM_STR( Calling_Station_Id );
856 break;
857
858 case RADIUS_ATTR_NAS_IDENTIFIER:
859 CONV2DIAM_STR( NAS_Identifier );
860 break;
861
862 /* Proxy-State is handled by echo_drop.rgwx plugin, we ignore it here */
863
864 case RADIUS_ATTR_LOGIN_LAT_SERVICE:
865 CONV2DIAM_STR( Login_LAT_Service );
866 break;
867
868 case RADIUS_ATTR_LOGIN_LAT_NODE:
869 CONV2DIAM_STR( Login_LAT_Node );
870 break;
871
872 case RADIUS_ATTR_LOGIN_LAT_GROUP:
873 CONV2DIAM_STR( Login_LAT_Group );
874 break;
875
876 /* Framed-AppleTalk-Link never present in an Access-Request */
877 /* Framed-AppleTalk-Network never present in an Access-Request */
878 /* Framed-AppleTalk-Zone never present in an Access-Request */
879
880 case RADIUS_ATTR_CHAP_CHALLENGE:
881 CONV2DIAM_STR( CHAP_Challenge );
882 break;
883
884 case RADIUS_ATTR_NAS_PORT_TYPE:
885 CONV2DIAM_32B( NAS_Port_Type );
886 break;
887
888 case RADIUS_ATTR_PORT_LIMIT:
889 CONV2DIAM_32B( Port_Limit );
890 break;
891
892 case RADIUS_ATTR_LOGIN_LAT_PORT:
893 CONV2DIAM_STR( Login_LAT_Port );
894 break;
895
896
897 /* RFC 3162 */
898 case RADIUS_ATTR_NAS_IPV6_ADDRESS:
899 CONV2DIAM_STR( NAS_IPv6_Address );
900 break;
901
902 case RADIUS_ATTR_FRAMED_INTERFACE_ID:
903 CONV2DIAM_64B( Framed_Interface_Id );
904 break;
905
906 case RADIUS_ATTR_FRAMED_IPV6_PREFIX:
907 CONV2DIAM_STR( Framed_IPv6_Prefix );
908 break;
909
910 case RADIUS_ATTR_LOGIN_IPV6_HOST:
911 CONV2DIAM_STR( Login_IPv6_Host );
912 break;
913
914 /* Framed-IPv6-Route never present in an Access-Request */
915 /* Framed-IPv6-Pool never present in an Access-Request */
916
917
918 /* RFC 2868 */
919 /* Prepare the top-level Tunneling AVP for each tag values, as needed, and add to the Diameter message.
920 This macro is called when an AVP is added inside the group, so we will not have empty grouped AVPs */
921 #define AVP_TUN_PREPARE() { \
922 if (avp_tun == NULL) { \
923 CHECK_MALLOC( avp_tun = calloc(sizeof(struct avp *), 32 ) ); \
924 } \
925 tag = *(uint8_t *)(attr + 1); \
926 if (tag > 0x1F) tag = 0; \
927 if (avp_tun[tag] == NULL) { \
928 CHECK_FCT( fd_msg_avp_new ( cs->dict.Tunneling, 0, &avp_tun[tag] ) ); \
929 CHECK_FCT( fd_msg_avp_add (*diam_fw, MSG_BRW_LAST_CHILD, avp_tun[tag]));\
930 } \
931 }
932
933 /* Convert an attribute to an OctetString AVP and add inside the Tunneling AVP corresponding to the tag */
934 #define CONV2DIAM_TUN_STR( _dictobj_ ) { \
935 uint8_t tag; \
936 CHECK_PARAMS( attr->length >= 3); \
937 AVP_TUN_PREPARE(); \
938 CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \
939 value.os.len = attr->length - (tag ? 3 : 2); \
940 value.os.data = ((unsigned char *)(attr + 1)) + (tag ? 1 : 0); \
941 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \
942 CHECK_FCT( fd_msg_avp_add ( avp_tun[tag], MSG_BRW_LAST_CHILD, avp) ); \
943 }
944
945 /* Convert an attribute to a scalar AVP and add inside the Tunneling AVP corresponding to the tag */
946 #define CONV2DIAM_TUN_24B( _dictobj_ ) { \
947 uint8_t tag; \
948 CHECK_PARAMS( attr->length == 6); \
949 AVP_TUN_PREPARE(); \
950 CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \
951 { \
952 uint8_t * v = (uint8_t *)(attr + 1); \
953 value.u32 = (v[1] << 16) | (v[2] <<8) | v[3] ; \
954 } \
955 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \
956 CHECK_FCT( fd_msg_avp_add ( avp_tun[tag], MSG_BRW_LAST_CHILD, avp) ); \
957 }
958
959 /*
960 - If the RADIUS message contains Tunnel information [RADTunnels],
961 the attributes or tagged groups should each be converted to a
962 Diameter Tunneling Grouped AVP set. If the tunnel information
963 contains a Tunnel-Password attribute, the RADIUS encryption
964 must be resolved, and the password forwarded, by using Diameter
965 security methods.
966 -> If the RADIUS message does not use properly the Tag info, result is unpredictable here..
967 */
968 case RADIUS_ATTR_TUNNEL_TYPE:
969 CONV2DIAM_TUN_24B( Tunnel_Type );
970 break;
971
972 case RADIUS_ATTR_TUNNEL_MEDIUM_TYPE:
973 CONV2DIAM_TUN_24B( Tunnel_Medium_Type );
974 break;
975
976 case RADIUS_ATTR_TUNNEL_CLIENT_ENDPOINT:
977 CONV2DIAM_TUN_STR( Tunnel_Client_Endpoint );
978 break;
979
980 case RADIUS_ATTR_TUNNEL_SERVER_ENDPOINT:
981 CONV2DIAM_TUN_STR( Tunnel_Server_Endpoint );
982 break;
983
984 /* Tunnel-Password never present in an Access-Request */
985
986 case RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID:
987 CONV2DIAM_TUN_STR( Tunnel_Private_Group_Id );
988 break;
989
990 /* Tunnel-Assignment-ID never present in an Access-Request */
991
992 case RADIUS_ATTR_TUNNEL_PREFERENCE:
993 CONV2DIAM_TUN_24B( Tunnel_Preference );
994 break;
995
996 case RADIUS_ATTR_TUNNEL_CLIENT_AUTH_ID:
997 CONV2DIAM_TUN_STR( Tunnel_Client_Auth_Id );
998 break;
999
1000 case RADIUS_ATTR_TUNNEL_SERVER_AUTH_ID:
1001 CONV2DIAM_TUN_STR( Tunnel_Server_Auth_Id );
1002 break;
1003
1004
1005 /* RFC 2869 */
1006 case RADIUS_ATTR_ARAP_PASSWORD:
1007 CONV2DIAM_STR( ARAP_Password );
1008 break;
1009
1010 /* ARAP-Features never present in an Access-Request */
1011 /* ARAP-Zone-Access never present in an Access-Request */
1012
1013 case RADIUS_ATTR_ARAP_SECURITY:
1014 CONV2DIAM_32B( ARAP_Security );
1015 break;
1016
1017 case RADIUS_ATTR_ARAP_SECURITY_DATA:
1018 CONV2DIAM_STR( ARAP_Security_Data );
1019 break;
1020
1021 /* Password-Retry never present in an Access-Request */
1022 /* Prompt never present in an Access-Request */
1023
1024 case RADIUS_ATTR_CONNECT_INFO:
1025 CONV2DIAM_STR( Connect_Info );
1026 break;
1027
1028 /* Configuration-Token never present in an Access-Request */
1029 /* ARAP-Challenge-Response never present in an Access-Request */
1030 /* Acct-Interim-Interval never present in an Access-Request */
1031
1032 case RADIUS_ATTR_NAS_PORT_ID:
1033 CONV2DIAM_STR( NAS_Port_Id );
1034 break;
1035
1036 /* Framed-Pool never present in an Access-Request */
1037
1038
1039 /* RFC 2869 / 3579 */
1040 case RADIUS_ATTR_ORIGINATING_LINE_INFO:
1041 CONV2DIAM_STR( Originating_Line_Info );
1042 break;
1043
1044 case RADIUS_ATTR_MESSAGE_AUTHENTICATOR:
1045 case RADIUS_ATTR_EAP_MESSAGE:
1046 /* It was already handled, just remove the attribute */
1047 break;
1048
1049 /* Default */
1050 default: /* unknown attribute */
1051 /* We just keep the attribute in the RADIUS message */
1052 rad_req->attr_pos[nattr_used++] = rad_req->attr_pos[idx];
1053 }
1054 }
1055
1056 /* Destroy tunnel pointers (if we used it) */
1057 free(avp_tun);
1058
1059 /* Update the radius message to remove all handled attributes */
1060 rad_req->attr_used = nattr_used;
1061
1062 /* Store the request identifier in the session (if provided) */
1063 {
1064 struct sess_state *st;
1065 CHECK_MALLOC(st = malloc(sizeof(struct sess_state)));
1066 memcpy(st->req_auth, &rad_req->hdr->authenticator[0], 16);
1067
1068 CHECK_FCT( fd_sess_state_store( cs->sess_hdl, sess, &st ) );
1069 }
1070
1071 return 0;
1072}
1073
1074static int auth_diam_ans( struct rgwp_config * cs, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli )
1075{
1076 struct msg_hdr * hdr;
1077 struct avp *avp, *next, *avp_x, *avp_y, *aoh;
1078 struct avp_hdr *ahdr, *oh;
1079 uint8_t buf[254]; /* to store some attributes values (with final '\0') */
1080 size_t sz;
1081 int ta_set = 0;
1082 int no_str = 0; /* indicate if an STR is required for this server */
1083 uint8_t tuntag = 0;
1084 struct sess_state *st;
1085 int error_cause = 0;
1086 struct session * sess;
1087 os0_t sid = NULL;
1088 size_t sidlen;
1089
1090 TRACE_ENTRY("%p %p %p %p", cs, diam_ans, rad_fw, cli);
1091 CHECK_PARAMS(cs && diam_ans && *diam_ans && rad_fw && *rad_fw);
1092
1093 /* Retrieve the request identified which was stored in the session */
1094 CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, *diam_ans, &sess, NULL) );
1095 if (sess) {
1096 CHECK_FCT( fd_sess_state_retrieve( cs->sess_hdl, sess, &st ) );
1097 CHECK_FCT( fd_sess_getsid(sess, &sid, &sidlen) );
1098 } /* else ? */
1099
1100 /*
1101 - If the Diameter Command-Code is set to AA-Answer and the
1102 Result-Code AVP is set to DIAMETER_MULTI_ROUND_AUTH, the
1103 gateway must send a RADIUS Access-Challenge. This must have
1104 the Origin-Host, Origin-Realm, and Diameter Session-Id AVPs
1105 encapsulated in the RADIUS State attribute, with the prefix
1106 "Diameter/", concatenated in the above order separated with "/"
1107 characters, in UTF-8 [UTF-8]. This is necessary to ensure that
1108 the Translation Agent receiving the subsequent RADIUS Access-
1109 Request will have access to the Session Identifier and be able
1110 to set the Destination-Host to the correct value.
1111 -> done here below
1112
1113 - If the Command-Code is set to AA-Answer, the Diameter Session-
1114 Id AVP is saved in a new RADIUS Class attribute whose format
1115 consists of the string "Diameter/" followed by the Diameter
1116 Session Identifier. This will ensure that the subsequent
1117 Accounting messages, which could be received by any Translation
1118 Agent, would have access to the original Diameter Session
1119 Identifier.
1120 -> done here but only for Access-Accept messages (Result-Code = success)
1121 */
1122
1123 /* MACROS to help in the process: convert AVP data to RADIUS attributes. */
1124 /* Control large attributes: _trunc_ = 0 => error; _trunc_ = 1 => truncate; _trunc = 2 => create several attributes */
1125 #define CONV2RAD_STR( _attr_, _data_, _len_, _trunc_) { \
1126 size_t __l = (size_t)(_len_); \
1127 size_t __off = 0; \
1128 TRACE_DEBUG(FULL, "Converting AVP to "#_attr_); \
1129 if ((_trunc_) == 0) { \
1130 CHECK_PARAMS( __l <= 253 ); \
1131 } \
1132 if ((__l > 253) && (_trunc_ == 1)) { \
1133 TRACE_DEBUG(INFO, "[auth.rgwx] AVP truncated in "#_attr_); \
1134 __l = 253; \
1135 } \
1136 do { \
1137 size_t __w = (__l > 253) ? 253 : __l; \
1138 CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (_data_) + __off, __w)); \
1139 __off += __w; \
1140 __l -= __w; \
1141 } while (__l); \
1142 }
1143
1144 #define CONV2RAD_32B( _attr_, _data_) { \
1145 uint32_t __v = htonl((uint32_t)(_data_)); \
1146 TRACE_DEBUG(FULL, "Converting AVP to "#_attr_); \
1147 CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (uint8_t *)&__v, sizeof(__v))); \
1148 }
1149
1150 #define CONV2RAD_64B( _attr_, _data_) { \
1151 uint64_t __v = htonll((uint64_t)(_data_)); \
1152 TRACE_DEBUG(FULL, "Converting AVP to "#_attr_); \
1153 CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (uint8_t *)&__v, sizeof(__v))); \
1154 }
1155
1156 /* Search the different AVPs we handle here */
1157 CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Origin_Host, &aoh) );
1158 CHECK_FCT( fd_msg_avp_hdr ( aoh, &oh ) );
1159
1160 /* Check the Diameter error code */
1161 CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Result_Code, &avp) );
1162 ASSERT( avp ); /* otherwise the message should have been discarded a lot earlier because of ABNF */
1163 CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
1164 switch (ahdr->avp_value->u32) {
1165 case ER_DIAMETER_MULTI_ROUND_AUTH:
1166 (*rad_fw)->hdr->code = RADIUS_CODE_ACCESS_CHALLENGE;
1167 break;
1168 case ER_DIAMETER_SUCCESS:
1169 case ER_DIAMETER_LIMITED_SUCCESS:
1170 (*rad_fw)->hdr->code = RADIUS_CODE_ACCESS_ACCEPT;
1171 break;
1172
1173 default:
1174 /* Can we convert the value to a natural Error-Cause ? */
1175 switch (ahdr->avp_value->u32) {
1176 case ER_DIAMETER_AVP_UNSUPPORTED:
1177 error_cause = 401; /* Unsupported Attribute */
1178 break;
1179
1180 case ER_DIAMETER_MISSING_AVP:
1181 error_cause = 402; /* Missing Attribute */
1182 break;
1183
1184 case ER_DIAMETER_UNABLE_TO_COMPLY:
1185 error_cause = 404; /* Invalid Request */
1186 break;
1187
1188 case ER_DIAMETER_APPLICATION_UNSUPPORTED:
1189 error_cause = 405; /* Unsupported Service */
1190 break;
1191
1192 case ER_DIAMETER_COMMAND_UNSUPPORTED:
1193 error_cause = 406; /* Unsupported Extension */
1194 break;
1195
1196 case ER_DIAMETER_INVALID_AVP_VALUE:
1197 error_cause = 407; /* Invalid Attribute Value */
1198 break;
1199
1200 case ER_DIAMETER_AVP_NOT_ALLOWED:
1201 error_cause = 501; /* Administratively Prohibited */
1202 break;
1203
1204 case ER_DIAMETER_REALM_NOT_SERVED:
1205 case ER_DIAMETER_LOOP_DETECTED:
1206 case ER_DIAMETER_UNKNOWN_PEER:
1207 case ER_DIAMETER_UNABLE_TO_DELIVER:
1208 error_cause = 502; /* Request Not Routable (Proxy) */
1209 break;
1210
1211 case ER_DIAMETER_UNKNOWN_SESSION_ID:
1212 error_cause = 503; /* Session Context Not Found */
1213 break;
1214
1215 case ER_DIAMETER_TOO_BUSY:
1216 case ER_DIAMETER_OUT_OF_SPACE:
1217 error_cause = 506; /* Resources Unavailable */
1218 break;
1219
1220#if 0
1221 /* remaining Diameter Result-Code & RADIUS Error-Cause */
1222 case ER_DIAMETER_REDIRECT_INDICATION:
1223 case ER_DIAMETER_INVALID_HDR_BITS:
1224 case ER_DIAMETER_INVALID_AVP_BITS:
1225 case ER_DIAMETER_AUTHENTICATION_REJECTED:
1226 case ER_ELECTION_LOST:
1227 case ER_DIAMETER_AUTHORIZATION_REJECTED:
1228 case ER_DIAMETER_RESOURCES_EXCEEDED:
1229 case ER_DIAMETER_CONTRADICTING_AVPS:
1230 case ER_DIAMETER_AVP_OCCURS_TOO_MANY_TIMES
1231 case ER_DIAMETER_NO_COMMON_APPLICATION:
1232 case ER_DIAMETER_UNSUPPORTED_VERSION:
1233 case ER_DIAMETER_INVALID_BIT_IN_HEADER:
1234 case ER_DIAMETER_INVALID_AVP_LENGTH:
1235 case ER_DIAMETER_INVALID_MESSAGE_LENGTH:
1236 case ER_DIAMETER_INVALID_AVP_BIT_COMBO:
1237 case ER_DIAMETER_NO_COMMON_SECURITY:
1238 error_cause = 403; /* NAS Identification Mismatch */
1239 error_cause = 504; /* Session Context Not Removable */
1240 error_cause = 505; /* Other Proxy Processing Error */
1241 error_cause = 507; /* Request Initiated */
1242 error_cause = 508; /* Multiple Session Selection Unsupported */
1243#endif /* 0 */
1244 }
1245 /* In any case, the following is processed: */
1246 (*rad_fw)->hdr->code = RADIUS_CODE_ACCESS_REJECT;
1247 fd_log_debug("[auth.rgwx] Received Diameter answer with error code '%d' from server '%.*s', session %.*s, translating into Access-Reject",
1248 ahdr->avp_value->u32,
1249 (int)oh->avp_value->os.len, oh->avp_value->os.data,
1250 (int)sidlen, sid);
1251 CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Error_Message, &avp_x) );
1252 if (avp_x) {
1253 CHECK_FCT( fd_msg_avp_hdr ( avp_x, &ahdr ) );
1254 fd_log_debug("[auth.rgwx] Error-Message content: '%.*s'",
1255 (int)ahdr->avp_value->os.len, ahdr->avp_value->os.data);
1256 }
1257 CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Error_Reporting_Host, &avp_x) );
1258 if (avp_x) {
1259 CHECK_FCT( fd_msg_avp_hdr ( avp_x, &ahdr ) );
1260 fd_log_debug("[auth.rgwx] Error-Reporting-Host: '%.*s'",
1261 (int)ahdr->avp_value->os.len, ahdr->avp_value->os.data);
1262 }
1263 CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Failed_AVP, &avp_x) );
1264 if (avp_x) {
1265 fd_log_debug("[auth.rgwx] Failed-AVP was included in the message.");
1266 /* Dump its content ? */
1267 }
1268 }
1269 /* Remove this Result-Code avp */
1270 CHECK_FCT( fd_msg_free( avp ) );
1271
1272 /* Creation of the State or Class attribute with session information */
1273 CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Origin_Realm, &avp) );
1274 CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
1275
1276 /* Now, save the session-id and eventually server info in a STATE or CLASS attribute */
1277 if ((*rad_fw)->hdr->code == RADIUS_CODE_ACCESS_CHALLENGE) {
1278 if (sizeof(buf) < (sz = snprintf((char *)buf, sizeof(buf), "Diameter/%.*s/%.*s/%.*s",
1279 (int)oh->avp_value->os.len, (char *)oh->avp_value->os.data,
1280 (int)ahdr->avp_value->os.len, (char *)ahdr->avp_value->os.data,
1281 (int)sidlen, (char *)sid))) {
1282 TRACE_DEBUG(INFO, "Data truncated in State attribute: %s", buf);
1283 }
1284 CONV2RAD_STR(RADIUS_ATTR_STATE, buf, sz, 0);
1285 }
1286
1287 if ((*rad_fw)->hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
1288 /* Add the Session-Id */
1289 if (sizeof(buf) < (sz = snprintf((char *)buf, sizeof(buf), "Diameter/%.*s",
1290 (int)sidlen, sid))) {
1291 TRACE_DEBUG(INFO, "Data truncated in Class attribute: %s", buf);
1292 }
1293 CONV2RAD_STR(RADIUS_ATTR_CLASS, buf, sz, 0);
1294 }
1295
1296 /* Unlink the Origin-Realm now; the others are unlinked at the end of this function */
1297 CHECK_FCT( fd_msg_free( avp ) );
1298
1299 CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Session_Timeout, &avp) );
1300 CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Authorization_Lifetime, &avp_x) );
1301 CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Re_Auth_Request_Type, &avp_y) );
1302 /*
1303 When translating a Diameter AA-Answer (with successful result code)
1304 to RADIUS Access-Accept that contains a Session-Timeout or
1305 Authorization-Lifetime AVP, take the following steps:
1306
1307 - If the Diameter message contains a Session-Timeout AVP but no
1308 Authorization-Lifetime AVP, translate it to a Session-Timeout
1309 attribute (not a Termination-Action).
1310 */
1311 if ((avp != NULL) && (avp_x == NULL)) {
1312 CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
1313 CONV2RAD_32B( RADIUS_ATTR_SESSION_TIMEOUT, ahdr->avp_value->u32 );
1314 }
1315
1316 /*
1317 - If the Diameter message contains an Authorization-Lifetime AVP
1318 but no Session-Timeout AVP, translate it to a Session-Timeout
1319 attribute and a Termination-Action set to AA-REQUEST. (Remove
1320 Authorization-Lifetime and Re-Auth-Request-Type.)
1321 */
1322 if ((avp == NULL) && (avp_x != NULL)) {
1323 CHECK_FCT( fd_msg_avp_hdr ( avp_x, &ahdr ) );
1324 CONV2RAD_32B( RADIUS_ATTR_SESSION_TIMEOUT, ahdr->avp_value->u32 );
1325 CONV2RAD_32B( RADIUS_ATTR_TERMINATION_ACTION, RADIUS_TERMINATION_ACTION_RADIUS_REQUEST );
1326 ta_set = 1;
1327 }
1328
1329 /*
1330 - If the Diameter message has both, the Session-Timeout must be
1331 greater than or equal to the Authorization-Lifetime (required
1332 by [BASE]). Translate it to a Session-Timeout value (with
1333 value from Authorization-Lifetime AVP, the smaller one) and
1334 with the Termination-Action set to AA-REQUEST. (Remove the
1335 Authorization-Lifetime and Re-Auth-Request-Type.)
1336 */
1337 if ((avp != NULL) && (avp_x != NULL)) {
1338 CHECK_FCT( fd_msg_avp_hdr ( avp_x, &ahdr ) );
1339 CONV2RAD_32B( RADIUS_ATTR_SESSION_TIMEOUT, ahdr->avp_value->u32 );
1340 CONV2RAD_32B( RADIUS_ATTR_TERMINATION_ACTION, RADIUS_TERMINATION_ACTION_RADIUS_REQUEST );
1341 ta_set = 1;
1342 }
1343
1344 /* -> Not too sure about Auth-Grace-Period... we'll just discard it for now */
1345
1346 if (avp) {
1347 CHECK_FCT( fd_msg_free( avp ) );
1348 }
1349 if (avp_x) {
1350 CHECK_FCT( fd_msg_free( avp_x ) );
1351 }
1352 if (avp_y) {
1353 CHECK_FCT( fd_msg_free( avp_y ) );
1354 }
1355
1356
1357 /*
1358 - If a Proxy-State attribute was present in the RADIUS request,
1359 the same attribute is added in the response. This information
1360 may be found in the Proxy-Info AVP group, or in a local state
1361 table.
1362 -> handled by sub_echo_drop
1363
1364 - If state information regarding the RADIUS request was saved in
1365 a Proxy-Info AVP or local state table, the RADIUS Identifier
1366 and UDP IP Address and port number are extracted and used in
1367 issuing the RADIUS reply.
1368 -> was saved with the full request
1369 */
1370
1371
1372 /* Now loop in the list of AVPs and convert those that we know how */
1373 CHECK_FCT( fd_msg_browse(*diam_ans, MSG_BRW_FIRST_CHILD, &next, NULL) );
1374
1375 while (next) {
1376 int handled = 1;
1377 avp = next;
1378 CHECK_FCT( fd_msg_browse(avp, MSG_BRW_NEXT, &next, NULL) );
1379
1380 CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
1381
1382 if (!(ahdr->avp_flags & AVP_FLAG_VENDOR)) {
1383 switch (ahdr->avp_code) {
1384 /* In case of Diameter error, include the Reply-Message attribute */
1385 case DIAM_ATTR_ERROR_MESSAGE:
1386 CONV2RAD_STR(RADIUS_ATTR_REPLY_MESSAGE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
1387 break;
1388
1389 case DIAM_ATTR_ERROR_REPORTING_HOST:
1390 {
1391 char buf[254];
1392 int bsz = snprintf(buf, sizeof(buf), "Error-Reporting-Host: %*s", (int)(ahdr->avp_value->os.len), ahdr->avp_value->os.data);
1393 CONV2RAD_STR(RADIUS_ATTR_REPLY_MESSAGE, (uint8_t *)buf, bsz, 2);
1394 }
1395 break;
1396
1397 case DIAM_ATTR_FAILED_AVP:
1398 {
1399 struct avp * favp;
1400 CHECK_FCT( fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &favp, NULL) );
1401 if (favp) {
1402 char buf[254];
1403 int bsz;
1404 struct dict_object * favp_model;
1405
1406 CHECK_FCT( fd_msg_model(favp, &favp_model) );
1407 if (favp_model) {
1408 struct dict_avp_data fadata;
1409 CHECK_FCT( fd_dict_getval(favp_model, &fadata) );
1410 bsz = snprintf(buf, sizeof(buf), "Failed-AVP: %s", fadata.avp_name);
1411 } else {
1412 struct avp_hdr * favp_hdr;
1413 CHECK_FCT( fd_msg_avp_hdr ( favp, &favp_hdr ) );
1414 bsz = snprintf(buf, sizeof(buf), "Failed-AVP: code %u, vendor %u", favp_hdr->avp_code, favp_hdr->avp_vendor);
1415 }
1416 CONV2RAD_STR(RADIUS_ATTR_REPLY_MESSAGE, (uint8_t *)buf, bsz, 2);
1417 }
1418 }
1419 break;
1420
1421 /* RFC 4005 (AVP in the order of the AA-Request/Answer AVP Table) */
1422 case DIAM_ATTR_ACCT_INTERIM_INTERVAL:
1423 CONV2RAD_32B(RADIUS_ATTR_ACCT_INTERIM_INTERVAL, ahdr->avp_value->u32);
1424 break;
1425
1426 case DIAM_ATTR_ARAP_CHALLENGE_RESPONSE:
1427 CONV2RAD_STR(RADIUS_ATTR_ARAP_CHALLENGE_RESPONSE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
1428 break;
1429
1430 case DIAM_ATTR_ARAP_FEATURES:
1431 CONV2RAD_STR(RADIUS_ATTR_ARAP_FEATURES, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
1432 break;
1433
1434 /* ARAP-Password is not present in answers */
1435
1436 case DIAM_ATTR_ARAP_SECURITY:
1437 CONV2RAD_32B(RADIUS_ATTR_ARAP_SECURITY, ahdr->avp_value->u32);
1438 break;
1439
1440 case DIAM_ATTR_ARAP_SECURITY_DATA:
1441 CONV2RAD_STR(RADIUS_ATTR_ARAP_SECURITY_DATA, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
1442 break;
1443
1444 case DIAM_ATTR_ARAP_ZONE_ACCESS:
1445 CONV2RAD_32B(RADIUS_ATTR_ARAP_ZONE_ACCESS, ahdr->avp_value->u32);
1446 break;
1447
1448 case DIAM_ATTR_AUTH_APPLICATION_ID:
1449 /* We just remove this AVP */
1450 break;
1451
1452 case DIAM_ATTR_AUTH_GRACE_PERIOD:
1453 /* We just remove this AVP (?) */
1454 break;
1455
1456 case DIAM_ATTR_AUTH_REQUEST_TYPE:
1457 /* We only check the value */
1458 if (ahdr->avp_value->u32 != 3) {
1459 fd_log_debug("[auth.rgwx] Received Diameter answer with Auth-Request-Type set to %d (%s) from server %.*s, session %.*s."
1460 " This may cause interoperability problems with RADIUS.",
1461 ahdr->avp_value->u32,
1462 (ahdr->avp_value->u32 == 1) ? "AUTHENTICATE_ONLY" :
1463 ((ahdr->avp_value->u32 == 2) ? "AUTHORIZE_ONLY" : "???"),
1464 (int)oh->avp_value->os.len, oh->avp_value->os.data,
1465 (int)sidlen, sid);
1466 }
1467 break;
1468
1469 case DIAM_ATTR_AUTH_SESSION_STATE:
1470 if ((!ta_set) && (ahdr->avp_value->u32 == ACV_ASS_STATE_MAINTAINED)) {
1471 CONV2RAD_32B( RADIUS_ATTR_TERMINATION_ACTION, RADIUS_TERMINATION_ACTION_RADIUS_REQUEST );
1472 }
1473
1474 if (ahdr->avp_value->u32 == ACV_ASS_NO_STATE_MAINTAINED) {
1475 no_str = 1;
1476 }
1477 break;
1478
1479 /* Authorization-Lifetime already handled */
1480
1481 case DIAM_ATTR_CALLBACK_ID:
1482 CONV2RAD_STR(RADIUS_ATTR_CALLBACK_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
1483 break;
1484
1485 case DIAM_ATTR_CALLBACK_NUMBER:
1486 CONV2RAD_STR(RADIUS_ATTR_CALLBACK_NUMBER, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
1487 break;
1488
1489 /* Called-Station-Id is not present in answers */
1490 /* Calling-Station-Id is not present in answers */
1491 /* CHAP-Auth is not present in answers */
1492 /* CHAP-Challenge is not present in answers */
1493
1494 case DIAM_ATTR_CLASS:
1495 CONV2RAD_STR(RADIUS_ATTR_CLASS, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
1496 break;
1497
1498 case DIAM_ATTR_CONFIGURATION_TOKEN:
1499 /* We might as well remove it since it's not supposed to be sent to the NAS... */
1500 CONV2RAD_STR(RADIUS_ATTR_CONFIGURATION_TOKEN, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
1501 break;
1502
1503 /* Connect-Info is not present in answers */
1504
1505 case DIAM_ATTR_FILTER_ID:
1506 CONV2RAD_STR(RADIUS_ATTR_FILTER_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
1507 break;
1508
1509 case DIAM_ATTR_FRAMED_APPLETALK_LINK:
1510 CONV2RAD_32B(RADIUS_ATTR_FRAMED_APPLETALK_LINK, ahdr->avp_value->u32);
1511 break;
1512
1513 case DIAM_ATTR_FRAMED_APPLETALK_NETWORK:
1514 CONV2RAD_32B(RADIUS_ATTR_FRAMED_APPLETALK_NETWORK, ahdr->avp_value->u32);
1515 break;
1516
1517 case DIAM_ATTR_FRAMED_APPLETALK_ZONE:
1518 CONV2RAD_STR(RADIUS_ATTR_FRAMED_APPLETALK_ZONE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
1519 break;
1520
1521 case DIAM_ATTR_FRAMED_COMPRESSION:
1522 CONV2RAD_32B(RADIUS_ATTR_FRAMED_COMPRESSION, ahdr->avp_value->u32);
1523 break;
1524
1525 case DIAM_ATTR_FRAMED_INTERFACE_ID:
1526 CONV2RAD_64B(RADIUS_ATTR_FRAMED_INTERFACE_ID, ahdr->avp_value->u64);
1527 break;
1528
1529 case DIAM_ATTR_FRAMED_IP_ADDRESS:
1530 CONV2RAD_STR(RADIUS_ATTR_FRAMED_IP_ADDRESS, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
1531 break;
1532
1533 case DIAM_ATTR_FRAMED_IP_NETMASK:
1534 CONV2RAD_STR(RADIUS_ATTR_FRAMED_IP_NETMASK, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
1535 break;
1536
1537 case DIAM_ATTR_FRAMED_IPV6_PREFIX:
1538 CONV2RAD_STR(RADIUS_ATTR_FRAMED_IPV6_PREFIX, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
1539 break;
1540
1541 case DIAM_ATTR_FRAMED_IPV6_POOL:
1542 CONV2RAD_STR(RADIUS_ATTR_FRAMED_IPV6_POOL, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
1543 break;
1544
1545 case DIAM_ATTR_FRAMED_IPV6_ROUTE:
1546 CONV2RAD_STR(RADIUS_ATTR_FRAMED_IPV6_ROUTE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
1547 break;
1548
1549 case DIAM_ATTR_FRAMED_IPX_NETWORK:
1550 CONV2RAD_32B(RADIUS_ATTR_FRAMED_IPX_NETWORK, ahdr->avp_value->u32);
1551 break;
1552
1553 case DIAM_ATTR_FRAMED_MTU:
1554 CONV2RAD_32B(RADIUS_ATTR_FRAMED_MTU, ahdr->avp_value->u32);
1555 break;
1556
1557 case DIAM_ATTR_FRAMED_POOL:
1558 CONV2RAD_STR(RADIUS_ATTR_FRAMED_POOL, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
1559 break;
1560
1561 case DIAM_ATTR_FRAMED_PROTOCOL:
1562 CONV2RAD_32B(RADIUS_ATTR_FRAMED_PROTOCOL, ahdr->avp_value->u32);
1563 break;
1564
1565 case DIAM_ATTR_FRAMED_ROUTE:
1566 CONV2RAD_STR(RADIUS_ATTR_FRAMED_ROUTE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
1567 break;
1568
1569 case DIAM_ATTR_FRAMED_ROUTING:
1570 CONV2RAD_32B(RADIUS_ATTR_FRAMED_ROUTING, ahdr->avp_value->u32);
1571 break;
1572
1573 case DIAM_ATTR_IDLE_TIMEOUT:
1574 CONV2RAD_32B(RADIUS_ATTR_IDLE_TIMEOUT, ahdr->avp_value->u32);
1575 break;
1576
1577 case DIAM_ATTR_LOGIN_IP_HOST:
1578 CONV2RAD_STR(RADIUS_ATTR_LOGIN_IP_HOST, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
1579 break;
1580
1581 case DIAM_ATTR_LOGIN_IPV6_HOST:
1582 CONV2RAD_STR(RADIUS_ATTR_LOGIN_IPV6_HOST, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
1583 break;
1584
1585 case DIAM_ATTR_LOGIN_LAT_GROUP:
1586 CONV2RAD_STR(RADIUS_ATTR_LOGIN_LAT_GROUP, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
1587 break;
1588
1589 case DIAM_ATTR_LOGIN_LAT_NODE:
1590 CONV2RAD_STR(RADIUS_ATTR_LOGIN_LAT_NODE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
1591 break;
1592
1593 case DIAM_ATTR_LOGIN_LAT_PORT:
1594 CONV2RAD_STR(RADIUS_ATTR_LOGIN_LAT_PORT, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
1595 break;
1596
1597 case DIAM_ATTR_LOGIN_LAT_SERVICE:
1598 CONV2RAD_STR(RADIUS_ATTR_LOGIN_LAT_SERVICE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
1599 break;
1600
1601 case DIAM_ATTR_LOGIN_SERVICE:
1602 CONV2RAD_32B(RADIUS_ATTR_LOGIN_SERVICE, ahdr->avp_value->u32);
1603 break;
1604
1605 case DIAM_ATTR_LOGIN_TCP_PORT:
1606 CONV2RAD_32B(RADIUS_ATTR_LOGIN_TCP_PORT, ahdr->avp_value->u32);
1607 break;
1608
1609 /*
1610 - If the
1611 Multi-Round-Time-Out AVP is present, the value of the AVP MUST
1612 be inserted in the RADIUS Session-Timeout AVP.
1613
1614 o As described in [NASREQ], if the Result-Code AVP set to
1615 DIAMETER_MULTI_ROUND_AUTH and the Multi-Round-Time-Out AVP is
1616 present, it is translated to the RADIUS Session-Timeout attribute.
1617 */
1618 case DIAM_ATTR_MULTI_ROUND_TIMEOUT:
1619 CONV2RAD_32B(RADIUS_ATTR_SESSION_TIMEOUT, ahdr->avp_value->u32);
1620 break;
1621
1622 case DIAM_ATTR_NAS_FILTER_RULE:
1623 /* This is not translatable to RADIUS */
1624 fd_log_debug("[auth.rgwx] Received Diameter answer with non-translatable NAS-Filter-Rule AVP from '%.*s' (session: '%.*s'), ignoring.",
1625 (int)oh->avp_value->os.len, oh->avp_value->os.data,
1626 (int)sidlen, sid);
1627 handled = 0;
1628 break;
1629
1630 /* NAS-Identifier is not present in answers */
1631 /* NAS-IP-Address is not present in answers */
1632 /* NAS-IPv6-Address is not present in answers */
1633 /* NAS-Port is not present in answers */
1634 /* NAS-Port-Id is not present in answers */
1635 /* NAS-Port-Type is not present in answers */
1636
1637 case DIAM_ATTR_ORIGIN_AAA_PROTOCOL:
1638 /* We just remove this AVP */
1639 break;
1640
1641 /* Originating-Line-Info is not present in answers */
1642
1643 case DIAM_ATTR_PASSWORD_RETRY:
1644 CONV2RAD_32B(RADIUS_ATTR_PASSWORD_RETRY, ahdr->avp_value->u32);
1645 break;
1646
1647 case DIAM_ATTR_PORT_LIMIT:
1648 CONV2RAD_32B(RADIUS_ATTR_PORT_LIMIT, ahdr->avp_value->u32);
1649 break;
1650
1651 case DIAM_ATTR_PROMPT:
1652 CONV2RAD_32B(RADIUS_ATTR_PROMPT, ahdr->avp_value->u32);
1653 break;
1654
1655 case DIAM_ATTR_QOS_FILTER_RULE:
1656 /* This is not translatable to RADIUS */
1657 fd_log_debug("[auth.rgwx] Received Diameter answer with non-translatable QoS-Filter-Rule AVP from '%.*s' (session: '%.*s'), ignoring.",
1658 (int)oh->avp_value->os.len, oh->avp_value->os.data,
1659 (int)sidlen, sid);
1660 handled = 0;
1661 break;
1662
1663 /* Re-Auth-Request-Type already handled */
1664
1665 case DIAM_ATTR_REPLY_MESSAGE:
1666 CONV2RAD_STR(RADIUS_ATTR_REPLY_MESSAGE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
1667 break;
1668
1669 case DIAM_ATTR_SERVICE_TYPE:
1670 CONV2RAD_32B(RADIUS_ATTR_SERVICE_TYPE, ahdr->avp_value->u32);
1671 break;
1672
1673 case DIAM_ATTR_STATE:
1674 CONV2RAD_STR(RADIUS_ATTR_STATE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 2);
1675 break;
1676
1677 case DIAM_ATTR_TUNNELING:
1678 {
1679#define CONV2RAD_TUN_STR( _attr_, _data_, _len_, _trunc_) { \
1680 size_t __l = (size_t)(_len_); \
1681 size_t __w = (__l > 252) ? 252 : __l; \
1682 size_t __off = 0; \
1683 if ((_trunc_) == 0) { \
1684 CHECK_PARAMS( __l <= 252 ); \
1685 } \
1686 if ((__l > 252) && (_trunc_ == 1)) { \
1687 TRACE_DEBUG(FULL, "Attribute truncated!"); \
1688 __l = 252; \
1689 } \
1690 buf[0] = tuntag; \
1691 memcpy(&buf[1], (_data_), __w); \
1692 CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), &buf[0], __w + 1)); \
1693 while (__l -= __w) { \
1694 __off += __w; \
1695 __w = (__l > 253) ? 253 : __l; \
1696 CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (_data_) + __off, __w)); \
1697 } \
1698}
1699
1700#define CONV2RAD_TUN_32B( _attr_, _data_) { \
1701 uint32_t __v = htonl((uint32_t)(_data_) | (tuntag << 24)); \
1702 CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (uint8_t *)&__v, sizeof(__v))); \
1703}
1704 struct avp *inavp, *innext;
1705 tuntag++;
1706 CHECK_FCT( fd_msg_browse(avp, MSG_BRW_FIRST_CHILD, &innext, NULL) );
1707 while (innext) {
1708 inavp = innext;
1709 CHECK_FCT( fd_msg_browse(inavp, MSG_BRW_NEXT, &innext, NULL) );
1710 CHECK_FCT( fd_msg_avp_hdr ( inavp, &ahdr ) );
1711
1712 if ( ! (ahdr->avp_flags & AVP_FLAG_VENDOR)) {
1713 switch (ahdr->avp_code) {
1714 case DIAM_ATTR_TUNNEL_TYPE:
1715 CONV2RAD_TUN_32B( RADIUS_ATTR_TUNNEL_TYPE, ahdr->avp_value->u32);
1716 break;
1717
1718 case DIAM_ATTR_TUNNEL_MEDIUM_TYPE:
1719 CONV2RAD_TUN_32B( RADIUS_ATTR_TUNNEL_MEDIUM_TYPE, ahdr->avp_value->u32);
1720 break;
1721
1722 case DIAM_ATTR_TUNNEL_CLIENT_ENDPOINT:
1723 CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_CLIENT_ENDPOINT, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
1724 break;
1725
1726 case DIAM_ATTR_TUNNEL_SERVER_ENDPOINT:
1727 CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_SERVER_ENDPOINT, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
1728 break;
1729
1730 case DIAM_ATTR_TUNNEL_PREFERENCE:
1731 CONV2RAD_TUN_32B( RADIUS_ATTR_TUNNEL_PREFERENCE, ahdr->avp_value->u32);
1732 break;
1733
1734 case DIAM_ATTR_TUNNEL_CLIENT_AUTH_ID:
1735 CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_CLIENT_AUTH_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
1736 break;
1737
1738 case DIAM_ATTR_TUNNEL_SERVER_AUTH_ID:
1739 CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_SERVER_AUTH_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
1740 break;
1741
1742 case DIAM_ATTR_TUNNEL_ASSIGNMENT_ID:
1743 CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_ASSIGNMENT_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
1744 break;
1745
1746 case DIAM_ATTR_TUNNEL_PASSWORD:
1747 {
1748 /* This AVP must be encoded for RADIUS (similar to radius_msg_add_attr_user_password)
1749 0 1 2 3
1750 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
1751 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1752 | Type | Length | Tag | Salt
1753 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1754 Salt (cont) | String ...
1755 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1756 */
1757 size_t pos;
1758 int i;
1759 uint8_t * secret; /* S */
1760 size_t secret_len;
1761 uint8_t hash[16]; /* b(i) */
1762 const uint8_t *addr[3];
1763 size_t len[3];
1764
1765 /* We need the request authenticator */
1766 CHECK_PARAMS(st);
1767
1768 /* Retrieve the shared secret */
1769 CHECK_FCT(rgw_clients_getkey(cli, &secret, &secret_len));
1770
1771 /* Beginning of the buffer */
1772 buf[0] = tuntag;
1773 buf[1] = (uint8_t)(lrand48()); /* A (hi bits) */
1774 buf[2] = (uint8_t)(lrand48()); /* A (low bits) */
1775
1776 /* The plain text string P */
1777 CHECK_PARAMS(ahdr->avp_value->os.len < 240);
1778 buf[3] = ahdr->avp_value->os.len;
1779 memcpy(&buf[4], ahdr->avp_value->os.data, ahdr->avp_value->os.len);
1780 memset(&buf[4 + ahdr->avp_value->os.len], 0, sizeof(buf) - 4 - ahdr->avp_value->os.len);
1781
1782 /* Initial b1 = MD5(S + R + A) */
1783 addr[0] = secret;
1784 len[0] = secret_len;
1785 addr[1] = st->req_auth;
1786 len[1] = 16;
1787 addr[2] = &buf[1];
1788 len[2] = 2;
1789 md5_vector(3, addr, len, hash);
1790
1791 /* Initial c(1) = p(1) xor b(1) */
1792 for (i = 0; i < 16; i++) {
1793 buf[i + 3] ^= hash[i];
1794 }
1795 pos = 16;
1796
1797 /* loop */
1798 while (pos < ahdr->avp_value->os.len + 1) {
1799 addr[0] = secret;
1800 len[0] = secret_len;
1801 addr[1] = &buf[pos - 13];
1802 len[1] = 16;
1803 /* b(i) = MD5( S + c(i-1) */
1804 md5_vector(2, addr, len, hash);
1805
1806 /* c(i) = p(i) xor b(i) */
1807 for (i = 0; i < 16; i++)
1808 buf[pos + i + 3] ^= hash[i];
1809
1810 pos += 16;
1811 }
1812
1813 CONV2RAD_STR(RADIUS_ATTR_TUNNEL_PASSWORD, &buf[0], pos + 3, 0);
1814 }
1815 break;
1816
1817 case DIAM_ATTR_TUNNEL_PRIVATE_GROUP_ID:
1818 CONV2RAD_TUN_STR(RADIUS_ATTR_TUNNEL_PRIVATE_GROUP_ID, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
1819 break;
1820
1821 default:
1822 TRACE_DEBUG(FULL, "Ignored unknown AVP inside Tunneling AVP (%d)", ahdr->avp_code);
1823 }
1824 } else {
1825 TRACE_DEBUG(FULL, "Ignored unknown Vendor AVP inside Tunneling AVP (%d, %d)", ahdr->avp_vendor, ahdr->avp_code);
1826 }
1827 }
1828 }
1829 break;
1830
1831 case DIAM_ATTR_USER_NAME:
1832 CONV2RAD_STR(RADIUS_ATTR_USER_NAME, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
1833 break;
1834
1835 /* User-Password never present in answers */
1836
1837 /* RFC 4072 (AVP in the order of the EAP Command AVP Table) */
1838 /*
1839 o Diameter Accounting-EAP-Auth-Method AVPs, if present, are
1840 discarded.
1841 */
1842 case DIAM_ATTR_ACCOUNTING_EAP_AUTH_METHOD:
1843 break;
1844
1845 /*
1846 o Diameter EAP-Master-Session-Key AVP can be translated to the
1847 vendor-specific RADIUS MS-MPPE-Recv-Key and MS-MPPE-Send-Key
1848 attributes [RFC2548]. The first up to 32 octets of the key is
1849 stored into MS-MPPE-Recv-Key, and the next up to 32 octets (if
1850 present) are stored into MS-MPPE-Send-Key. The encryption of this
1851 attribute is described in [RFC2548].
1852 */
1853 case DIAM_ATTR_EAP_MASTER_SESSION_KEY:
1854 {
1855 uint8_t * secret; /* S */
1856 size_t secret_len;
1857 size_t recv_len, send_len;
1858
1859 /* We need the request authenticator */
1860 CHECK_PARAMS(st);
1861
1862 /* Retrieve the shared secret */
1863 CHECK_FCT(rgw_clients_getkey(cli, &secret, &secret_len));
1864
1865 if (ahdr->avp_value->os.len != 64) {
1866 TRACE_DEBUG(INFO, "Received EAP-Master-Session-Key attribute with length %zd != 64.", ahdr->avp_value->os.len)
1867 }
1868
1869 CHECK_PARAMS(ahdr->avp_value->os.len <= 64);
1870 recv_len = ahdr->avp_value->os.len >= 32 ? 32 : ahdr->avp_value->os.len;
1871 send_len = ahdr->avp_value->os.len - recv_len;
1872
1873 if ( ! radius_msg_add_mppe_keys(*rad_fw, st->req_auth, secret, secret_len,
1874 ahdr->avp_value->os.data + recv_len, send_len,
1875 ahdr->avp_value->os.data, recv_len) ) {
1876 TRACE_DEBUG(INFO, "Error while converting EAP-Master-Session-Key to RADIUS message");
1877 return ENOMEM;
1878 }
1879 }
1880 break;
1881
1882 case DIAM_ATTR_EAP_KEY_NAME:
1883 CONV2RAD_STR(RADIUS_ATTR_EAP_KEY_NAME, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
1884 break;
1885
1886 /*
1887 o Diameter EAP-Payload AVP is translated to RADIUS EAP-Message
1888 attribute(s). If necessary, the value is split into multiple
1889 RADIUS EAP-Message attributes.
1890 */
1891 case DIAM_ATTR_EAP_PAYLOAD:
1892 if ( ! radius_msg_add_eap(*rad_fw, ahdr->avp_value->os.data, ahdr->avp_value->os.len) ) {
1893 TRACE_DEBUG(INFO, "Error while converting EAP payload to RADIUS message");
1894 return ENOMEM;
1895 }
1896 break;
1897
1898 /*
1899 o Diameter EAP-Reissued-Payload AVP is translated to a message that
1900 contains RADIUS EAP-Message attribute(s), and a RADIUS Error-Cause
1901 attribute [RFC3576] with value 202 (decimal), "Invalid EAP Packet
1902 (Ignored)" [RFC3579].
1903 */
1904 case DIAM_ATTR_EAP_REISSUED_PAYLOAD:
1905 if ( ! radius_msg_add_eap(*rad_fw, ahdr->avp_value->os.data, ahdr->avp_value->os.len) ) {
1906 TRACE_DEBUG(INFO, "Error while converting EAP reissued payload to RADIUS message");
1907 return ENOMEM;
1908 }
1909
1910 error_cause = 202; /* Invalid EAP Packet */
1911 break;
1912
1913 default:
1914 /* Leave the AVP in the message for further treatment */
1915 handled = 0;
1916 }
1917 } else {
1918 /* Vendor-specific AVPs */
1919 switch (ahdr->avp_vendor) {
1920
1921 default: /* unknown vendor */
1922 handled = 0;
1923 }
1924 }
1925
1926 if (handled) {
1927 CHECK_FCT( fd_msg_free( avp ) );
1928 }
1929 }
1930
1931 CHECK_FCT( fd_msg_free( aoh ) );
1932 free(st);
1933
1934 if (error_cause) {
1935 if ( ! radius_msg_add_attr_int32(*rad_fw, RADIUS_ATTR_ERROR_CAUSE, error_cause) ) {
1936 TRACE_DEBUG(INFO, "Error while adding Error-Cause attribute in RADIUS message");
1937 return ENOMEM;
1938 }
1939 }
1940
1941 if ((*rad_fw)->hdr->code == RADIUS_CODE_ACCESS_ACCEPT) {
1942 /* Add the auth-application-id required for STR, or 0 if no STR is required */
1943 CHECK_FCT( fd_msg_hdr( *diam_ans, &hdr ) );
1944 if (sizeof(buf) < (sz = snprintf((char *)buf, sizeof(buf), CLASS_AAI_PREFIX "%u",
1945 no_str ? 0 : hdr->msg_appl))) {
1946 TRACE_DEBUG(INFO, "Data truncated in Class attribute: %s", buf);
1947 }
1948 CONV2RAD_STR(RADIUS_ATTR_CLASS, buf, sz, 0);
1949 }
1950
1951 return 0;
1952}
1953
1954/* The exported symbol */
1955struct rgw_api rgwp_descriptor = {
1956 .rgwp_name = "auth",
1957 .rgwp_conf_parse = auth_conf_parse,
1958 .rgwp_conf_free = auth_conf_free,
1959 .rgwp_rad_req = auth_rad_req,
1960 .rgwp_diam_ans = auth_diam_ans
1961};