Navigation


source: freeDiameter/extensions/app_radgw/rgwx_sip.c @ 639:95a784729cac

Last change on this file since 639:95a784729cac was 639:95a784729cac, checked in by Sebastien Decugis <sdecugis@nict.go.jp>, 2 years ago

Added new opaque pointer to fd_sess_handler_create and fd_disp_register for usability. Bumped API version number.

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