blob: b4fd91e6f3b5cb17af66e80ca7f07fe2f47a3cf5 [file] [log] [blame]
Brian Waters13d96012017-12-08 16:53:31 -06001/*********************************************************************************************************
2* Software License Agreement (BSD License) *
3* Author: Alexandre Westfahl <awestfahl@freediameter.net> *
4* * *
5* Copyright (c) 2013, WIDE Project and NICT *
6* Copyright (c) 2010, Alexandre Westfahl, Teraoka Laboratory (Keio University), and the WIDE Project. *
7* *
8* All rights reserved. *
9* Based on rgwx_auth plugin (Sebastien Decugis <sdecugis@freediameter.net>) *
10* *
11* Redistribution and use of this software in source and binary forms, with or without modification, are *
12* permitted provided that the following conditions are met: *
13* *
14* * Redistributions of source code must retain the above *
15* copyright notice, this list of conditions and the *
16* following disclaimer. *
17* *
18* * Redistributions in binary form must reproduce the above *
19* copyright notice, this list of conditions and the *
20* following disclaimer in the documentation and/or other *
21* materials provided with the distribution. *
22* *
23* * Neither the name of the Teraoka Laboratory nor the *
24* names of its contributors may be used to endorse or *
25* promote products derived from this software without *
26* specific prior written permission of Teraoka Laboratory *
27* *
28* *
29* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED *
30* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A *
31* PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR *
32* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT *
33* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS *
34* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR *
35* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF *
36* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. *
37*********************************************************************************************************/
38
39
40/* RADIUS Access-Request messages translation plugin */
41
42#include "rgw_common.h"
43#include <string.h>
44#include <stdio.h>
45#include <string.h>
46#include <stdlib.h>
47
48/* Other constants we use */
49#define AI_SIP 6 /* Diameter SIP application */
50#define CC_MULTIMEDIA_AUTH_REQUEST 286 /* MAR */
51#define CC_MULTIMEDIA_AUTH_ANSWER 286 /* MAA */
52#define ACV_ASS_STATE_MAINTAINED 0 /* STATE_MAINTAINED */
53#define ACV_ASS_NO_STATE_MAINTAINED 1 /* NO_STATE_MAINTAINED */
54#define ER_DIAMETER_SUCCESS_AUTH_SENT_SERVER_NOT_STORED 2008
55#define ER_DIAMETER_SUCCESS_SERVER_NAME_NOT_STORED 2006
56
57
58
59/* This macro converts a RADIUS attribute to a Diameter AVP of type OctetString */
60#define CONV2DIAM_STR( _dictobj_ ) \
61 CHECK_PARAMS( attr->length >= sizeof(struct radius_attr_hdr) ); \
62 /* Create the AVP with the specified dictionary model */ \
63 CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \
64 value.os.len = attr->length - sizeof(struct radius_attr_hdr); \
65 value.os.data = (os0_t)(attr + 1); \
66 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \
67 /* Add the AVP in the Diameter message. */ \
68 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); \
69
70#define CONV2DIAM_STR_AUTH( _dictobj_ ) \
71 CHECK_PARAMS( attr->length >= sizeof(struct radius_attr_hdr) ); \
72 /* Create the AVP with the specified dictionary model */ \
73 CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \
74 value.os.len = attr->length - sizeof(struct radius_attr_hdr); \
75 value.os.data = (os0_t)(attr + 1); \
76 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \
77 /* Add the AVP in the Diameter message. */ \
78 CHECK_FCT( fd_msg_avp_add ( auth, MSG_BRW_LAST_CHILD, avp) ); \
79
80/* Same thing, for scalar AVPs of 32 bits */
81#define CONV2DIAM_32B( _dictobj_ ) \
82 CHECK_PARAMS( attr->length == sizeof(struct radius_attr_hdr)+sizeof(uint32_t) );\
83 CHECK_FCT( fd_msg_avp_new ( cs->dict._dictobj_, 0, &avp ) ); \
84 { \
85 uint8_t * v = (uint8_t *)(attr + 1); \
86 value.u32 = (v[0] << 24) \
87 | (v[1] << 16) \
88 | (v[2] << 8) \
89 | v[3] ; \
90 } \
91 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) ); \
92 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) ); \
93
94
95
96
97
98/* The state we keep for this plugin */
99struct rgwp_config {
100 struct {
101 struct dict_object * Session_Id;
102 struct dict_object * Auth_Application_Id;
103 struct dict_object * Auth_Session_State;
104 struct dict_object * Origin_Host;
105 struct dict_object * Origin_Realm;
106 struct dict_object * Destination_Realm;
107 struct dict_object * SIP_AOR;
108 struct dict_object * SIP_Method;
109 struct dict_object * Destination_Host;
110 struct dict_object * User_Name;
111 struct dict_object * SIP_Server_URI;
112 struct dict_object * SIP_Number_Auth_Items;
113 struct dict_object * SIP_Authorization;
114 struct dict_object * SIP_Authentication_Scheme;
115 struct dict_object * SIP_Authentication_Info;
116 struct dict_object * SIP_Auth_Data_Item;
117 struct dict_object * Proxy_Info;
118 struct dict_object * Route_Record;
119 struct dict_object * Service_Type;
120 struct dict_object * Result_Code;
121 struct dict_object * Digest_URI;
122 struct dict_object * Digest_Nonce;
123 struct dict_object * Digest_CNonce;
124 struct dict_object * Digest_Nonce_Count;
125 struct dict_object * Digest_Realm;
126 struct dict_object * Digest_Response;
127 struct dict_object * Digest_Method;
128 struct dict_object * Digest_Response_Auth;
129 struct dict_object * Digest_Username;
130 struct dict_object * Digest_Algorithm;
131 struct dict_object * Digest_QOP;
132
133
134
135 } dict; /* cache of the dictionary objects we use */
136 char * confstr;
137 //Chained list of nonce
138 struct fd_list listnonce;
139 //This will be used to lock access to chained list
140 pthread_mutex_t nonce_mutex;
141};
142
143typedef struct noncechain noncechain;
144struct noncechain
145{
146 struct fd_list chain;
147 os0_t sid;
148 size_t sidlen;
149 os0_t nonce;
150 size_t noncelen;
151
152};
153
154static int nonce_add_element(os0_t nonce, size_t noncelen, os0_t sid, size_t sidlen, struct rgwp_config * state)
155{
156 CHECK_PARAMS(nonce && state && sid && sidlen && noncelen);
157
158 noncechain *newelt;
159 CHECK_MALLOC(newelt=malloc(sizeof(noncechain)));
160
161 CHECK_MALLOC(newelt->nonce= os0dup(nonce, noncelen));
162 newelt->noncelen=noncelen;
163
164 CHECK_MALLOC(newelt->sid=os0dup(sid, sidlen));
165 newelt->sidlen=sidlen;
166
167 fd_list_init(&newelt->chain,NULL);
168
169 CHECK_POSIX(pthread_mutex_lock(&state->nonce_mutex));
170 fd_list_insert_before(&state->listnonce,&newelt->chain);
171 CHECK_POSIX(pthread_mutex_unlock(&state->nonce_mutex));
172
173 return 0;
174}
175/*
176static void nonce_del_element(char * nonce, struct rgwp_config *state)
177{
178 struct fd_list * li;
179
180 CHECK_PARAMS_DO(nonce && state, return);
181
182 for(li=state->listnonce.next;li!=&state->listnonce;li=li->next)
183 {
184 noncechain *temp=(noncechain *)li;
185
186 if(strcmp(temp->nonce,nonce)==0)
187 {
188 fd_list_unlink (li);
189 free(temp->sid);
190 free(temp->nonce);
191 free(temp);
192 break;
193 }
194 }
195}
196*/
197//Retrieve sid from nonce
198static os0_t nonce_get_sid(os0_t nonce, size_t noncelen, size_t * sidlen, struct rgwp_config *state)
199{
200 struct fd_list * li;
201 os0_t sid=NULL;
202
203 CHECK_PARAMS_DO(nonce && state && noncelen && sidlen, return NULL);
204 *sidlen=0;
205
206 // **Start mutex
207 CHECK_POSIX_DO(pthread_mutex_lock(&state->nonce_mutex),);
208 for(li=state->listnonce.next;li!=&state->listnonce;li=li->next)
209 {
210 noncechain *temp=(noncechain *)li;
211
212 if (!fd_os_cmp(temp->nonce, temp->noncelen, nonce, noncelen))
213 {
214 fd_list_unlink (li);
215 sid=temp->sid;
216 *sidlen=temp->sidlen;
217 free(temp->nonce);
218 free(temp);
219 break;
220 }
221
222 }
223 CHECK_POSIX_DO(pthread_mutex_unlock(&state->nonce_mutex),);
224 // ***Stop mutex
225 return sid;
226}
227
228static void nonce_deletelistnonce(struct rgwp_config *state)
229{
230 // **Start mutex
231 CHECK_POSIX_DO(pthread_mutex_lock(&state->nonce_mutex),);
232 while(!(FD_IS_LIST_EMPTY(&state->listnonce)) )
233 {
234 noncechain *temp=(noncechain *)state->listnonce.next;
235
236 fd_list_unlink (&temp->chain);
237 free(temp->sid);
238 free(temp->nonce);
239 free(temp);
240
241 }
242 CHECK_POSIX_DO(pthread_mutex_unlock(&state->nonce_mutex),);
243 // ***Stop mutex
244}
245
246/* Initialize the plugin */
247static int sip_conf_parse(char * conffile, struct rgwp_config ** state)
248{
249 struct rgwp_config * new;
250 struct dict_object * app;
251
252
253 TRACE_ENTRY("%p %p", conffile, state);
254 CHECK_PARAMS( state );
255
256 CHECK_MALLOC( new = malloc(sizeof(struct rgwp_config)) );
257 memset(new, 0, sizeof(struct rgwp_config));
258
259 new->confstr = conffile;
260
261 /* Resolve all dictionary objects we use */
262 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Session-Id", &new->dict.Session_Id, ENOENT) );
263 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Application-Id", &new->dict.Auth_Application_Id, ENOENT) );
264 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Auth-Session-State", &new->dict.Auth_Session_State, ENOENT) );
265 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Host", &new->dict.Origin_Host, ENOENT) );
266 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Origin-Realm", &new->dict.Origin_Realm, ENOENT) );
267 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Realm", &new->dict.Destination_Realm, ENOENT) );
268 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-AOR", &new->dict.SIP_AOR, ENOENT) );
269 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Method", &new->dict.SIP_Method, ENOENT) );
270 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Destination-Host", &new->dict.Destination_Host, ENOENT) );
271 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "User-Name", &new->dict.User_Name, ENOENT) );
272 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Server-URI", &new->dict.SIP_Server_URI, ENOENT) );
273 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Number-Auth-Items", &new->dict.SIP_Number_Auth_Items, ENOENT) );
274 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Authorization", &new->dict.SIP_Authorization, ENOENT) );
275 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Auth-Data-Item", &new->dict.SIP_Auth_Data_Item, ENOENT) );
276 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Authentication-Scheme", &new->dict.SIP_Authentication_Scheme, ENOENT) );
277 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "SIP-Authentication-Info", &new->dict.SIP_Authentication_Info, ENOENT) );
278 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Proxy-Info", &new->dict.Proxy_Info, ENOENT) );
279 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Route-Record", &new->dict.Route_Record, ENOENT) );
280 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Result-Code", &new->dict.Result_Code, ENOENT) );
281 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-URI", &new->dict.Digest_URI, ENOENT) );
282 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Nonce", &new->dict.Digest_Nonce, ENOENT) );
283 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Method", &new->dict.Digest_Method, ENOENT) );
284 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-CNonce", &new->dict.Digest_CNonce, ENOENT) );
285 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Nonce-Count", &new->dict.Digest_Nonce_Count, ENOENT) );
286 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Realm", &new->dict.Digest_Realm, ENOENT) );
287 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Response", &new->dict.Digest_Response, ENOENT) );
288 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Response-Auth", &new->dict.Digest_Response_Auth, ENOENT) );
289 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Username", &new->dict.Digest_Username, ENOENT) );
290 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-Algorithm", &new->dict.Digest_Algorithm, ENOENT) );
291 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_AVP, AVP_BY_NAME, "Digest-QoP", &new->dict.Digest_QOP, ENOENT) );
292
293
294
295 CHECK_FCT( fd_dict_search( fd_g_config->cnf_dict, DICT_APPLICATION, APPLICATION_BY_NAME, "Diameter Session Initiation Protocol (SIP) Application", &app, ENOENT) );
296 CHECK_FCT( fd_disp_app_support ( app, NULL, 1, 0 ) );
297
298 //chained list
299 fd_list_init(&new->listnonce,NULL);
300 CHECK_POSIX(pthread_mutex_init(&new->nonce_mutex,NULL));
301
302 *state = new;
303 return 0;
304}
305
306/* deinitialize */
307static void sip_conf_free(struct rgwp_config * state)
308{
309 TRACE_ENTRY("%p", state);
310 CHECK_PARAMS_DO( state, return );
311
312 nonce_deletelistnonce(state);
313 CHECK_POSIX_DO(pthread_mutex_destroy(&state->nonce_mutex), /*continue*/);
314
315 free(state);
316 return;
317}
318
319/* Handle an incoming RADIUS request */
320static int sip_rad_req( struct rgwp_config * cs, struct radius_msg * rad_req, struct radius_msg ** rad_ans, struct msg ** diam_fw, struct rgw_client * cli )
321{
322 int idx;
323 int got_AOR = 0;
324 int got_Dusername = 0;
325 int got_Drealm = 0;
326 int got_Duri = 0;
327 int got_Dmethod = 0;
328 int got_Dnonce = 0;
329 int got_Dresponse = 0;
330 int got_Dalgorithm = 0;
331 os0_t sid = NULL;
332 size_t sidlen;
333 os0_t un=NULL;
334 size_t un_len;
335 size_t nattr_used = 0;
336 struct avp *auth_data=NULL, *auth=NULL, *avp = NULL;
337 union avp_value value;
338 struct session * sess;
339
340 TRACE_ENTRY("%p %p %p %p %p", cs, rad_req, rad_ans, diam_fw, cli);
341
342 CHECK_PARAMS(rad_req && (rad_req->hdr->code == RADIUS_CODE_ACCESS_REQUEST) && rad_ans && diam_fw && *diam_fw);
343
344 /*
345 RFC5090 RADIUS Extension Digest Application
346 */
347 CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, *diam_fw, &sess, NULL) );
348 if (sess != NULL) {
349 TRACE_DEBUG(INFO,"INTERNAL ERROR: We are not supposed to receive a session in radSIP plugin.");
350 return EINVAL;
351 }
352
353 /* Check basic information is there */
354 for (idx = 0; idx < rad_req->attr_used; idx++) {
355 struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(rad_req->buf + rad_req->attr_pos[idx]);
356
357
358 switch (attr->type) {
359
360 case RADIUS_ATTR_USER_NAME:
361 if (attr->length>sizeof(struct radius_attr_hdr))
362 {
363 TRACE_DEBUG(ANNOYING, "Found a User-Name attribute: '%.*s'", (int)(attr->length- sizeof(struct radius_attr_hdr)), (char *)(attr+1));
364 un = (os0_t)(attr + 1);
365 un_len =attr->length - sizeof(struct radius_attr_hdr);
366 }
367 break;
368 case RADIUS_ATTR_DIGEST_USERNAME:
369 got_Dusername = 1;
370 break;
371 case RADIUS_ATTR_DIGEST_REALM:
372 got_Drealm = 1;
373 break;
374 case RADIUS_ATTR_DIGEST_URI:
375 got_Duri = 1;
376 break;
377 case RADIUS_ATTR_DIGEST_METHOD:
378 got_Dmethod = 1;
379 break;
380 // case RADIUS_ATTR_DIGEST_QOP:
381 // got_Dqop = 1;
382 // break;
383 // case RADIUS_ATTR_DIGEST_NONCE_COUNT:
384 // got_Dnonce_count = 1;
385 // break;
386 case RADIUS_ATTR_DIGEST_NONCE:
387 got_Dnonce = 1;
388
389 sid=nonce_get_sid((os0_t)(attr+1), attr->length - sizeof(struct radius_attr_hdr), &sidlen, cs);
390 if(!sid)
391 {
392 TRACE_DEBUG(INFO,"We haven't found the session.'");
393 return EINVAL;
394 }
395 CHECK_FCT(fd_sess_fromsid_msg (sid, sidlen, &sess, NULL));
396 free(sid);
397
398
399 break;
400 // case RADIUS_ATTR_DIGEST_CNONCE:
401 // got_Dcnonce = 1;
402 // break;
403 case RADIUS_ATTR_DIGEST_RESPONSE:
404 got_Dresponse = 1;
405 break;
406 case RADIUS_ATTR_DIGEST_ALGORITHM:
407 got_Dalgorithm = 1;
408 break;
409 case RADIUS_ATTR_SIP_AOR:
410 got_AOR = 1;
411 break;
412 }
413 }
414 if(!un)
415 {
416 TRACE_DEBUG(INFO,"No Username in request");
417 return EINVAL;
418 }
419
420 /* Create the session if it is not already done */
421 if (!sess) {
422
423 DiamId_t fqdn;
424 size_t fqdn_len;
425 DiamId_t realm;
426 size_t realm_len;
427
428 /* Get information on the RADIUS client */
429 CHECK_FCT( rgw_clients_get_origin(cli, &fqdn, &fqdn_len, &realm, &realm_len) );
430
431 /* Create a new Session-Id. The format is: {fqdn;hi32;lo32;username;diamid} */
432 CHECK_MALLOC( sid = malloc(un_len + 1 /* ';' */ + fd_g_config->cnf_diamid_len + 1 /* '\0' */) );
433 sidlen = sprintf((char *)sid, "%.*s;%s", (int)un_len, un, fd_g_config->cnf_diamid);
434 CHECK_FCT( fd_sess_new(&sess, fqdn, fqdn_len, sid, sidlen) );
435 free(sid);
436 }
437
438 /* Now, add the Session-Id AVP at beginning of Diameter message */
439 CHECK_FCT( fd_msg_avp_new ( cs->dict.Session_Id, 0, &avp ) );
440 value.os.data = sid;
441 value.os.len = sidlen;
442 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
443 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_FIRST_CHILD, avp) );
444
445 TRACE_DEBUG(FULL, "[sip.rgwx] Translating new message for session '%s'...", sid);
446
447 /* Now add this session in the message */
448 CHECK_FCT( fd_msg_sess_set(*diam_fw, sess) );
449
450 /* Add the Destination-Realm AVP */
451 CHECK_FCT( fd_msg_avp_new ( cs->dict.Destination_Realm, 0, &avp ) );
452
453 int i = 0;
454
455 /* Is there an '@' in the user name? We don't care for decorated NAI here */
456 for (i = un_len - 2; i > 0; i--) {
457 if (un[i] == '@') {
458 i++;
459 break;
460 }
461 }
462
463 if (i == 0) {
464 /* Not found in the User-Name => we use the local domain of this gateway */
465 value.os.data = (os0_t)fd_g_config->cnf_diamrlm;
466 value.os.len = fd_g_config->cnf_diamrlm_len;
467 } else {
468 value.os.data = un + i;
469 value.os.len = un_len - i;
470 }
471
472 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
473 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
474
475 /*
476 If the RADIUS Access-Request message does not
477 contain any Digest-* attribute, then the RADIUS client does not want
478 to apply HTTP Digest authentication, in which case, actions at the
479 gateway are outside the scope of this document.
480 */
481
482 if(!(got_Dmethod && got_Duri))
483 {
484 TRACE_DEBUG(INFO,"No Digest attributes in request, we drop it...");
485 return 1;
486 }
487
488 /* Add the appropriate command code & Auth-Application-Id */
489 {
490 struct msg_hdr * header = NULL;
491 CHECK_FCT( fd_msg_hdr ( *diam_fw, &header ) );
492 header->msg_flags = CMD_FLAG_REQUEST | CMD_FLAG_PROXIABLE;
493 header->msg_code = CC_MULTIMEDIA_AUTH_REQUEST;
494 header->msg_appl = AI_SIP;
495
496
497 /* Add the Auth-Application-Id */
498 {
499 CHECK_FCT( fd_msg_avp_new ( cs->dict.Auth_Application_Id, 0, &avp ) );
500 value.i32 = header->msg_appl;
501 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
502 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
503 }
504 }
505 /*Add Auth_Session_State AVP */
506 {
507 CHECK_FCT( fd_msg_avp_new ( cs->dict.Auth_Session_State, 0, &avp ) );
508 value.i32 = ACV_ASS_NO_STATE_MAINTAINED;
509 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
510 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
511 }
512
513
514 /*Add SIP_Number_Auth_Items AVP */
515 {
516 CHECK_FCT( fd_msg_avp_new ( cs->dict.SIP_Number_Auth_Items, 0, &avp ) );
517 value.i32 = 1; //We just treat one auth per request in gateway
518 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
519 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
520 }
521
522 /* Add SIP_Auth_Data_Item AVP */
523 {
524 CHECK_FCT( fd_msg_avp_new ( cs->dict.SIP_Auth_Data_Item, 0, &auth_data ) );
525 }
526 /* Add SIP_Authentication_Scheme AVP */
527 {
528 CHECK_FCT( fd_msg_avp_new ( cs->dict.SIP_Authentication_Scheme, 0, &avp ) );
529 value.i32=0; //There is only Digest Auth in RFC for now
530 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
531 CHECK_FCT( fd_msg_avp_add ( auth_data, MSG_BRW_LAST_CHILD, avp) );
532
533 }
534
535
536 /* Add SIP_Authorization AVP */
537 {
538 CHECK_FCT( fd_msg_avp_new ( cs->dict.SIP_Authorization, 0, &auth ) );
539 CHECK_FCT( fd_msg_avp_add ( auth_data, MSG_BRW_LAST_CHILD, auth) );
540 }
541
542 for (idx = 0; idx < rad_req->attr_used; idx++)
543 {
544 struct radius_attr_hdr * attr = (struct radius_attr_hdr *)(rad_req->buf + rad_req->attr_pos[idx]);
545
546 switch (attr->type) {
547
548 case RADIUS_ATTR_USER_NAME:
549 CONV2DIAM_STR( User_Name );
550
551 if(!got_Dusername)
552 {
553 CONV2DIAM_STR_AUTH(Digest_Username);
554 got_Dusername=1;
555 }
556
557 break;
558
559 case RADIUS_ATTR_DIGEST_URI:
560
561 CONV2DIAM_STR_AUTH(Digest_URI);
562
563 //All of these attributes are required by Diameter but not defined in RFC5090 so we provide FAKE values (only in first exchange)
564 if(!got_AOR)
565 {
566 CONV2DIAM_STR( SIP_AOR );
567 got_AOR=1;
568 }
569 /*
570 We must provide a fake nonce because of RFC4740 problem
571 TODO: remove when RFC is updated
572 ==START of FAKE
573 */
574 if(!got_Dresponse)
575 {
576 CONV2DIAM_STR_AUTH(Digest_Response);
577 got_Dresponse=1;
578 }
579 /*
580 ==END of FAKE
581 */
582 if(!got_Drealm)
583 {
584 //We extract Realm from Digest_URI
585 DiamId_t realm=NULL;
586 size_t realm_len = 0;
587 os0_t temp;
588
589 temp = (os0_t)(attr + 1);
590
591 for (i=attr->length - sizeof(struct radius_attr_hdr) - 1; i>=0; i--) {
592 if (temp[i] == '@') {
593 realm = (DiamId_t)temp + i + 1;
594 CHECK_FCT_DO( fd_os_validate_DiameterIdentity(&realm, &realm_len, 1),
595 realm = NULL );
596 break;
597 }
598 }
599
600 if(realm!=NULL)
601 {
602 CHECK_FCT( fd_msg_avp_new ( cs->dict.Digest_Realm, 0, &avp ) );
603 value.os.data=(os0_t)realm;
604 value.os.len=realm_len;
605 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
606 CHECK_FCT( fd_msg_avp_add ( auth, MSG_BRW_LAST_CHILD, avp) );
607
608 //We add SIP-Server-URI AVP because SIP server is registrar (through gateway)
609 CHECK_FCT( fd_msg_avp_new ( cs->dict.SIP_Server_URI, 0, &avp ) );
610 value.os.data=(os0_t)realm;
611 value.os.len=realm_len;
612 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
613 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
614
615 free(realm);
616 }
617 else
618 {
619 TRACE_DEBUG(INFO, "Can't extract domain from URI, droping request...");
620 return 1;
621 }
622 got_Drealm=1;
623 }
624 break;
625
626 case RADIUS_ATTR_DIGEST_METHOD:
627 CONV2DIAM_STR(SIP_Method);
628 CONV2DIAM_STR_AUTH(Digest_Method);
629 break;
630 case RADIUS_ATTR_DIGEST_REALM:
631 CONV2DIAM_STR_AUTH(Digest_Realm);
632
633 //We add SIP-Server-URI AVP because SIP server is registrar (through gateway)
634 CHECK_FCT( fd_msg_avp_new ( cs->dict.SIP_Server_URI, 0, &avp ) );
635 os0_t temp;
636 #define SIP_PREFIX "sip:"
637 size_t temp_len = attr->length - sizeof(struct radius_attr_hdr) + CONSTSTRLEN(SIP_PREFIX) + 1;
638 CHECK_MALLOC( temp = malloc(temp_len) );
639 temp_len = snprintf((char *)temp, temp_len, SIP_PREFIX "%.*s", (int)(attr->length - sizeof(struct radius_attr_hdr)), (char *)(attr + 1));
640
641 value.os.data=temp;
642 value.os.len=temp_len;
643
644 free(temp);
645
646 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
647 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, avp) );
648 break;
649
650 case RADIUS_ATTR_DIGEST_USERNAME:
651 CONV2DIAM_STR_AUTH(Digest_Username);
652 break;
653
654 case RADIUS_ATTR_DIGEST_QOP:
655 CONV2DIAM_STR_AUTH( Digest_QOP );
656 break;
657 case RADIUS_ATTR_DIGEST_ALGORITHM:
658 CONV2DIAM_STR_AUTH( Digest_Algorithm );
659 break;
660 case RADIUS_ATTR_DIGEST_CNONCE:
661 CONV2DIAM_STR_AUTH( Digest_CNonce );
662 break;
663 case RADIUS_ATTR_DIGEST_NONCE:
664 CONV2DIAM_STR_AUTH( Digest_Nonce );
665 break;
666 case RADIUS_ATTR_DIGEST_NONCE_COUNT:
667 CONV2DIAM_STR_AUTH( Digest_Nonce_Count );
668 break;
669 case RADIUS_ATTR_DIGEST_RESPONSE:
670 CONV2DIAM_STR_AUTH( Digest_Response );
671 break;
672 case RADIUS_ATTR_SIP_AOR:
673 CONV2DIAM_STR( SIP_AOR );
674 break;
675
676 default:
677 if(!got_Dalgorithm)
678 {
679 //[Note 3] If Digest-Algorithm is missing, 'MD5' is assumed.
680 #define DIGEST_ALGO_MD5 "MD5"
681
682 CHECK_FCT( fd_msg_avp_new ( cs->dict.Digest_Algorithm, 0, &avp ) );
683
684 value.os.data = (os0_t)DIGEST_ALGO_MD5;
685 value.os.len = CONSTSTRLEN(DIGEST_ALGO_MD5) - 1;
686 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
687 CHECK_FCT( fd_msg_avp_add ( auth, MSG_BRW_LAST_CHILD, avp) );
688 got_Dalgorithm=1;
689 }
690
691 if(!got_Dnonce)
692 {
693 //We give a fake nonce because it will be calculated at the server.
694 CHECK_FCT( fd_msg_avp_new ( cs->dict.Digest_Nonce, 0, &avp ) );
695 value.os.data=(unsigned char *)"nonce";
696 value.os.len=strlen((const char *)value.os.data);
697 CHECK_FCT( fd_msg_avp_setvalue ( avp, &value ) );
698 CHECK_FCT( fd_msg_avp_add ( auth, MSG_BRW_LAST_CHILD, avp) );
699 got_Dnonce=1;
700 }
701 break;
702
703 }
704 }
705
706
707 CHECK_FCT( fd_msg_avp_add ( *diam_fw, MSG_BRW_LAST_CHILD, auth_data) );
708
709 /* Update the radius message to remove all handled attributes */
710 rad_req->attr_used = nattr_used;
711
712 //fd_msg_dump_walk(1,*diam_fw);
713
714
715 return 0;
716}
717
718static int sip_diam_ans( struct rgwp_config * cs, struct msg ** diam_ans, struct radius_msg ** rad_fw, struct rgw_client * cli )
719{
720
721
722 struct avp *avp, *next;
723 struct avp_hdr *ahdr;
724 //char buf[254]; /* to store some attributes values (with final '\0') */
725 struct session * sess;
726 os0_t sid = NULL;
727 size_t sidlen;
728
729 TRACE_ENTRY("%p %p %p %p", cs, diam_ans, rad_fw, cli);
730 CHECK_PARAMS(cs && diam_ans && *diam_ans && rad_fw && *rad_fw);
731
732
733 /* MACROS to help in the process: convert AVP data to RADIUS attributes. */
734 /* Control large attributes: _trunc_ = 0 => error; _trunc_ = 1 => truncate; _trunc = 2 => create several attributes */
735 #define CONV2RAD_STR( _attr_, _data_, _len_, _trunc_) { \
736 size_t __l = (size_t)(_len_); \
737 size_t __off = 0; \
738 TRACE_DEBUG(FULL, "Converting AVP to "#_attr_); \
739 if ((_trunc_) == 0) { \
740 CHECK_PARAMS( __l <= 253 ); \
741 } \
742 if ((__l > 253) && (_trunc_ == 1)) { \
743 TRACE_DEBUG(INFO, "[authSIP.rgwx] AVP truncated in "#_attr_); \
744 __l = 253; \
745 } \
746 do { \
747 size_t __w = (__l > 253) ? 253 : __l; \
748 CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (_data_) + __off, __w)); \
749 __off += __w; \
750 __l -= __w; \
751 } while (__l); \
752 }
753
754 #define CONV2RAD_32B( _attr_, _data_) { \
755 uint32_t __v = htonl((uint32_t)(_data_)); \
756 TRACE_DEBUG(FULL, "Converting AVP to "#_attr_); \
757 CHECK_MALLOC(radius_msg_add_attr(*rad_fw, (_attr_), (uint8_t *)&__v, sizeof(__v))); \
758 }
759
760 /* Search the different AVPs we handle here */
761 CHECK_FCT( fd_msg_sess_get(fd_g_config->cnf_dict, *diam_ans, &sess, NULL) );
762 if (sess) {
763 CHECK_FCT( fd_sess_getsid(sess, &sid, &sidlen) );
764 }
765
766
767 /* Check the Diameter error code */
768 CHECK_FCT( fd_msg_search_avp (*diam_ans, cs->dict.Result_Code, &avp) );
769 ASSERT( avp ); /* otherwise the message should have been discarded a lot earlier because of ABNF */
770 CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
771 switch (ahdr->avp_value->u32) {
772 case ER_DIAMETER_MULTI_ROUND_AUTH:
773 case ER_DIAMETER_SUCCESS_AUTH_SENT_SERVER_NOT_STORED:
774 (*rad_fw)->hdr->code = RADIUS_CODE_ACCESS_CHALLENGE;
775 //struct timespec nowts;
776 //CHECK_SYS(clock_gettime(CLOCK_REALTIME, &nowts));
777 //nowts.tv_sec+=600;
778 //CHECK_FCT(fd_sess_settimeout(session, &nowts ));
779 break;
780 case ER_DIAMETER_SUCCESS_SERVER_NAME_NOT_STORED:
781 case ER_DIAMETER_SUCCESS:
782 (*rad_fw)->hdr->code = RADIUS_CODE_ACCESS_ACCEPT;
783 // in_success=1;
784 break;
785
786 default:
787 (*rad_fw)->hdr->code = RADIUS_CODE_ACCESS_REJECT;
788 fd_log_debug("[sip.rgwx] Received Diameter answer with error code '%d', session %.*s, translating into Access-Reject",
789 ahdr->avp_value->u32, (int)sidlen, sid);
790 return 0;
791 }
792 /* Remove this Result-Code avp */
793 CHECK_FCT( fd_msg_free( avp ) );
794
795 /* Now loop in the list of AVPs and convert those that we know how */
796 CHECK_FCT( fd_msg_browse(*diam_ans, MSG_BRW_FIRST_CHILD, &next, NULL) );
797
798 while (next) {
799 int handled = 1;
800 avp = next;
801 CHECK_FCT( fd_msg_browse(avp, MSG_BRW_WALK, &next, NULL) );
802
803 CHECK_FCT( fd_msg_avp_hdr ( avp, &ahdr ) );
804
805 if (!(ahdr->avp_flags & AVP_FLAG_VENDOR)) {
806 switch (ahdr->avp_code) {
807
808
809 case DIAM_ATTR_DIGEST_NONCE:
810 CONV2RAD_STR(DIAM_ATTR_DIGEST_NONCE, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
811 nonce_add_element(ahdr->avp_value->os.data, ahdr->avp_value->os.len, sid, sidlen, cs);
812 break;
813 case DIAM_ATTR_DIGEST_REALM:
814 CONV2RAD_STR(DIAM_ATTR_DIGEST_REALM, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
815 break;
816 case DIAM_ATTR_DIGEST_QOP:
817 CONV2RAD_STR(DIAM_ATTR_DIGEST_QOP, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
818 break;
819 case DIAM_ATTR_DIGEST_ALGORITHM:
820 CONV2RAD_STR(DIAM_ATTR_DIGEST_ALGORITHM, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 1);
821 break;
822 case DIAM_ATTR_DIGEST_RESPONSE_AUTH:
823 CONV2RAD_STR(DIAM_ATTR_DIGEST_RESPONSE_AUTH, ahdr->avp_value->os.data, ahdr->avp_value->os.len, 0);
824 break;
825 default:
826 handled=0;
827 break;
828 }
829 }
830 else
831 {
832 /* Vendor-specific AVPs */
833 switch (ahdr->avp_vendor) {
834
835 default: /* unknown vendor */
836 handled = 0;
837 }
838 }
839 if (handled) {
840 CHECK_FCT( fd_msg_free( avp ) );
841 }
842 }
843
844 return 0;
845}
846
847/* The exported symbol */
848struct rgw_api rgwp_descriptor = {
849 .rgwp_name = "sip",
850 .rgwp_conf_parse = sip_conf_parse,
851 .rgwp_conf_free = sip_conf_free,
852 .rgwp_rad_req = sip_rad_req,
853 .rgwp_diam_ans = sip_diam_ans
854};
855