comparison extensions/app_radgw/rgwx_auth.c @ 256:042af0000c0a

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