256
|
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 }; |