auth.c

Go to the documentation of this file.
00001 /*
00002  * $Id: auth.c 5401 2008-12-30 18:21:37Z miconda $
00003  *
00004  * Copyright (C) 2005 Voice Sistem SRL
00005  *
00006  * This file is part of Kamailio, a free SIP server.
00007  *
00008  * UAC Kamailio-module is free software; you can redistribute it and/or
00009  * modify it under the terms of the GNU General Public License
00010  * as published by the Free Software Foundation; either version 2
00011  * of the License, or (at your option) any later version.
00012  *
00013  * UAC Kamailio-module is distributed in the hope that it will be useful,
00014  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  * GNU General Public License for more details.
00017  *
00018  * You should have received a copy of the GNU General Public License
00019  * along with this program; if not, write to the Free Software
00020  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
00021  *
00022  *
00023  * History:
00024  * ---------
00025  *  2005-01-31  first version (ramona)
00026  *  2006-03-02  UAC authentication looks first in AVPs for credential (bogdan)
00027  */
00028 
00029 
00030 #include <ctype.h>
00031 #include <string.h>
00032 
00033 #include "../../cmpapi.h"
00034 #include "../../dprint.h"
00035 #include "../../pvar.h"
00036 #include "../../data_lump.h"
00037 #include "../../mem/mem.h"
00038 #include "../tm/tm_load.h"
00039 
00040 #include "auth.h"
00041 #include "auth_alg.h"
00042 #include "auth_hdr.h"
00043 
00044 
00045 extern struct tm_binds uac_tmb;
00046 extern pv_spec_t auth_username_spec;
00047 extern pv_spec_t auth_realm_spec;
00048 extern pv_spec_t auth_password_spec;
00049 
00050 
00051 static struct uac_credential *crd_list = 0;
00052 
00053 
00054 #define  duplicate_str(_strd, _strs, _error) \
00055    do { \
00056       _strd.s = (char*)pkg_malloc(_strs.len); \
00057       if (_strd.s==0) \
00058       { \
00059          LM_ERR("no more pkg memory\n");\
00060          goto _error; \
00061       } \
00062       memcpy( _strd.s, _strs.s, _strs.len); \
00063       _strd.len = _strs.len; \
00064    }while(0)
00065 
00066 
00067 #define WWW_AUTH_CODE       401
00068 #define WWW_AUTH_HDR        "WWW-Authenticate"
00069 #define WWW_AUTH_HDR_LEN    (sizeof(WWW_AUTH_HDR)-1)
00070 #define PROXY_AUTH_CODE     407
00071 #define PROXY_AUTH_HDR      "Proxy-Authenticate"
00072 #define PROXY_AUTH_HDR_LEN  (sizeof(PROXY_AUTH_HDR)-1)
00073 
00074 static str nc = {"00000001", 8};
00075 static str cnonce = {"o", 1};
00076 
00077 int has_credentials(void)
00078 {
00079    return (crd_list!=0)?1:0;
00080 }
00081 
00082 void free_credential(struct uac_credential *crd)
00083 {
00084    if (crd)
00085    {
00086       if (crd->realm.s)
00087          pkg_free(crd->realm.s);
00088       if (crd->user.s)
00089          pkg_free(crd->user.s);
00090       if (crd->passwd.s)
00091          pkg_free(crd->passwd.s);
00092       pkg_free(crd);
00093    }
00094 }
00095 
00096 
00097 int add_credential( unsigned int type, void *val)
00098 {
00099    struct uac_credential *crd;
00100    char *p;
00101    str foo;
00102 
00103    p = (char*)val;
00104    crd = 0;
00105 
00106    if (p==0 || *p==0)
00107       goto error;
00108 
00109    crd = (struct uac_credential*)pkg_malloc(sizeof(struct uac_credential));
00110    if (crd==0)
00111    {
00112       LM_ERR("no more pkg mem\n");
00113       goto error;
00114    }
00115    memset( crd, 0, sizeof(struct uac_credential));
00116 
00117    /*parse the user */
00118    while (*p && isspace((int)*p)) p++;
00119    foo.s = p;
00120    while (*p && *p!=':' && !isspace((int)*p)) p++;
00121    if (foo.s==p || *p==0)
00122       /* missing or empty user */
00123       goto parse_error;
00124    foo.len = p - foo.s;
00125    /* dulicate it */
00126    duplicate_str( crd->user, foo, error);
00127 
00128    /* parse the ':' separator */
00129    while (*p && isspace((int)*p)) p++;
00130    if (*p!=':')
00131       goto parse_error;
00132    p++;
00133    while (*p && isspace((int)*p)) p++;
00134    if (*p==0)
00135       goto parse_error;
00136 
00137    /*parse the realm */
00138    while (*p && isspace((int)*p)) p++;
00139    foo.s = p;
00140    while (*p && *p!=':' && !isspace((int)*p)) p++;
00141    if (foo.s==p || *p==0)
00142       /* missing or empty realm */
00143       goto parse_error;
00144    foo.len = p - foo.s;
00145    /* dulicate it */
00146    duplicate_str( crd->realm, foo, error);
00147 
00148    /* parse the ':' separator */
00149    while (*p && isspace((int)*p)) p++;
00150    if (*p!=':')
00151       goto parse_error;
00152    p++;
00153    while (*p && isspace((int)*p)) p++;
00154    if (*p==0)
00155       goto parse_error;
00156 
00157    /*parse the passwd */
00158    while (*p && isspace((int)*p)) p++;
00159    foo.s = p;
00160    while (*p && !isspace((int)*p)) p++;
00161    if (foo.s==p)
00162       /* missing or empty passwd */
00163       goto parse_error;
00164    foo.len = p - foo.s;
00165    /* dulicate it */
00166    duplicate_str( crd->passwd, foo, error);
00167 
00168    /* end of string */
00169    while (*p && isspace((int)*p)) p++;
00170    if (*p!=0)
00171       goto parse_error;
00172 
00173    /* link the new cred struct */
00174    crd->next = crd_list;
00175    crd_list = crd;
00176 
00177    pkg_free(val);
00178    return 0;
00179 parse_error:
00180       LM_ERR("parse error in <%s> "
00181       "around %ld\n", (char*)val, (long)(p-(char*)val));
00182 error:
00183    if (crd)
00184       free_credential(crd);
00185    return -1;
00186 }
00187 
00188 
00189 void destroy_credentials(void)
00190 {
00191    struct uac_credential *foo;
00192 
00193    while (crd_list)
00194    {
00195       foo = crd_list;
00196       crd_list = crd_list->next;
00197       free_credential(foo);
00198    }
00199    crd_list = 0;
00200 }
00201 
00202 
00203 static inline struct hdr_field *get_autenticate_hdr(struct sip_msg *rpl,
00204                                                 int rpl_code)
00205 {
00206    struct hdr_field *hdr;
00207    str hdr_name;
00208 
00209    /* what hdr should we look for */
00210    if (rpl_code==WWW_AUTH_CODE)
00211    {
00212       hdr_name.s = WWW_AUTH_HDR;
00213       hdr_name.len = WWW_AUTH_HDR_LEN;
00214    } else if (rpl_code==PROXY_AUTH_CODE) {
00215       hdr_name.s = PROXY_AUTH_HDR;
00216       hdr_name.len = PROXY_AUTH_HDR_LEN;
00217    } else {
00218       LM_ERR("reply is not an "
00219          "auth request\n");
00220       goto error;
00221    }
00222 
00223    LM_DBG("looking for header \"%.*s\"\n",
00224       hdr_name.len, hdr_name.s);
00225 
00226    /* search the auth hdr, but first parse them all */
00227    if (parse_headers( rpl, HDR_EOH_F, 0)<0)
00228    {
00229       LM_ERR("failed to parse reply\n");
00230       goto error;
00231    }
00232    for( hdr=rpl->headers ; hdr ; hdr=hdr->next )
00233    {
00234       if ( hdr->type!=HDR_OTHER_T )
00235          continue;
00236       if (cmp_hdrname_str(&hdr->name, &hdr_name)==0)
00237          return hdr;
00238    }
00239 
00240    LM_ERR("reply has no "
00241       "auth hdr (%.*s)\n", hdr_name.len, hdr_name.s);
00242 error:
00243    return 0;
00244 }
00245 
00246 
00247 static inline struct uac_credential *lookup_realm( str *realm)
00248 {
00249    struct uac_credential *crd;
00250 
00251    for( crd=crd_list ; crd ; crd=crd->next )
00252       if (realm->len==crd->realm.len &&
00253       strncmp( realm->s, crd->realm.s, realm->len)==0 )
00254          return crd;
00255    return 0;
00256 }
00257 
00258 
00259 static inline struct uac_credential *get_avp_credential(struct sip_msg *msg,
00260                                                 str *realm)
00261 {
00262    static struct uac_credential crd;
00263    pv_value_t pv_val;
00264 
00265    if(pv_get_spec_value( msg, &auth_realm_spec, &pv_val)!=0
00266    || pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0)
00267       return 0;
00268    
00269    crd.realm = pv_val.rs;
00270    /* is it the domain we are looking for? */
00271    if (realm->len!=crd.realm.len ||
00272    strncmp( realm->s, crd.realm.s, realm->len)!=0 )
00273       return 0;
00274 
00275    /* get username and password */
00276    if(pv_get_spec_value( msg, &auth_username_spec, &pv_val)!=0
00277    || pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0)
00278       return 0;
00279    crd.user = pv_val.rs;
00280 
00281    if(pv_get_spec_value( msg, &auth_password_spec, &pv_val)!=0
00282    || pv_val.flags&PV_VAL_NULL || pv_val.rs.len<=0)
00283       return 0;
00284    crd.passwd = pv_val.rs;
00285 
00286    return &crd;
00287 }
00288 
00289 
00290 static inline void do_uac_auth(struct sip_msg *req, str *uri,
00291       struct uac_credential *crd, struct authenticate_body *auth,
00292       HASHHEX response)
00293 {
00294    HASHHEX ha1;
00295    HASHHEX ha2;
00296 
00297    if((auth->flags&QOP_AUTH) || (auth->flags&QOP_AUTH_INT))
00298    {
00299       /* if qop generate nonce-count and cnonce */
00300       cnonce.s = int2str(core_hash(&auth->nonce, 0, 0),&cnonce.len);
00301 
00302       /* do authentication */
00303       uac_calc_HA1( crd, auth, &cnonce, ha1);
00304       uac_calc_HA2( &req->first_line.u.request.method, uri,
00305          auth, 0/*hentity*/, ha2 );
00306 
00307       uac_calc_response( ha1, ha2, auth, &nc, &cnonce, response);
00308       auth->nc = &nc;
00309       auth->cnonce = &cnonce;
00310    } else {
00311       /* do authentication */
00312       uac_calc_HA1( crd, auth, 0/*cnonce*/, ha1);
00313       uac_calc_HA2( &req->first_line.u.request.method, uri,
00314          auth, 0/*hentity*/, ha2 );
00315 
00316       uac_calc_response( ha1, ha2, auth, 0/*nc*/, 0/*cnonce*/, response);
00317    }
00318 }
00319 
00320 
00321 static inline int apply_urihdr_changes( struct sip_msg *req,
00322                                        str *uri, str *hdr)
00323 {
00324    struct lump* anchor;
00325 
00326    /* add the uri - move it to branch directly FIXME (bogdan)*/
00327    if (req->new_uri.s)
00328    {
00329       pkg_free(req->new_uri.s);
00330       req->new_uri.len=0;
00331    }
00332    req->parsed_uri_ok=0;
00333    req->new_uri.s = (char*)pkg_malloc(uri->len+1);
00334    if (req->new_uri.s==0)
00335    {
00336       LM_ERR("no more pkg\n");
00337       goto error;
00338    }
00339    memcpy( req->new_uri.s, uri->s, uri->len);
00340    req->new_uri.s[uri->len]=0;
00341    req->new_uri.len=uri->len;
00342 
00343    /* add the header */
00344    if (parse_headers(req, HDR_EOH_F, 0) == -1)
00345    {
00346       LM_ERR("failed to parse message\n");
00347       goto error;
00348    }
00349 
00350    anchor = anchor_lump(req, req->unparsed - req->buf, 0, 0);
00351    if (anchor==0)
00352    {
00353       LM_ERR("failed to get anchor\n");
00354       goto error;
00355    }
00356 
00357    if (insert_new_lump_before(anchor, hdr->s, hdr->len, 0) == 0)
00358    {
00359       LM_ERR("faield to insert lump\n");
00360       goto error;
00361    }
00362 
00363    return 0;
00364 error:
00365    pkg_free( hdr->s );
00366    return -1;
00367 }
00368 
00369 
00370 
00371 int uac_auth( struct sip_msg *msg)
00372 {
00373    static struct authenticate_body auth;
00374    struct uac_credential *crd;
00375    int code, branch;
00376    struct sip_msg *rpl;
00377    struct cell *t;
00378    struct hdr_field *hdr;
00379    HASHHEX response;
00380    str *new_hdr;
00381 
00382    /* get transaction */
00383    t = uac_tmb.t_gett();
00384    if (t==T_UNDEFINED || t==T_NULL_CELL)
00385    {
00386       LM_CRIT("no current transaction found\n");
00387       goto error;
00388    }
00389 
00390    /* get the selected branch */
00391    branch = uac_tmb.t_get_picked();
00392    if (branch<0) {
00393       LM_CRIT("no picked branch (%d)\n",branch);
00394       goto error;
00395    }
00396 
00397    rpl = t->uac[branch].reply;
00398    code = t->uac[branch].last_received;
00399    LM_DBG("picked reply is %p, code %d\n",rpl,code);
00400 
00401    if (rpl==0)
00402    {
00403       LM_CRIT("empty reply on picked branch\n");
00404       goto error;
00405    }
00406    if (rpl==FAKED_REPLY)
00407    {
00408       LM_ERR("cannot process a FAKED reply\n");
00409       goto error;
00410    }
00411 
00412    hdr = get_autenticate_hdr( rpl, code);
00413    if (hdr==0)
00414    {
00415       LM_ERR("failed to extract authenticate hdr\n");
00416       goto error;
00417    }
00418 
00419    LM_DBG("header found; body=<%.*s>\n",
00420       hdr->body.len, hdr->body.s);
00421 
00422    if (parse_authenticate_body( &hdr->body, &auth)<0)
00423    {
00424       LM_ERR("failed to parse auth hdr body\n");
00425       goto error;
00426    }
00427 
00428    /* can we authenticate this realm? */
00429    crd = 0;
00430    /* first look into AVP, if set */
00431    if ( auth_realm_spec.type==PVT_AVP )
00432       crd = get_avp_credential( msg, &auth.realm );
00433    /* if not found, look into predefined credentials */
00434    if (crd==0)
00435       crd = lookup_realm( &auth.realm );
00436    /* found? */
00437    if (crd==0)
00438    {
00439       LM_DBG("no credential for realm \"%.*s\"\n",
00440          auth.realm.len, auth.realm.s);
00441       goto error;
00442    }
00443 
00444    /* do authentication */
00445    do_uac_auth( msg, &t->uac[branch].uri, crd, &auth, response);
00446 
00447    /* build the authorization header */
00448    new_hdr = build_authorization_hdr( code, &t->uac[branch].uri,
00449       crd, &auth, response);
00450    if (new_hdr==0)
00451    {
00452       LM_ERR("failed to build authorization hdr\n");
00453       goto error;
00454    }
00455 
00456    /* so far, so good -> add the header and set the proper RURI */
00457    if ( apply_urihdr_changes( msg, &t->uac[branch].uri, new_hdr)<0 )
00458    {
00459       LM_ERR("failed to apply changes\n");
00460       goto error;
00461    }
00462 
00463    /* increas the Cseq nr */
00464 
00465 
00466    return 0;
00467 error:
00468    return -1;
00469 }
00470 
00471 
00472 

Generated on Thu May 17 12:00:24 2012 for Kamailio - The Open Source SIP Server by  doxygen 1.5.6