auth/api.c

Go to the documentation of this file.
00001 /*
00002  * $Id: api.c 5258 2008-11-25 09:53:17Z henningw $
00003  *
00004  * Copyright (C) 2001-2003 FhG Fokus
00005  *
00006  * This file is part of Kamailio, a free SIP server.
00007  *
00008  * Kamailio is free software; you can redistribute it and/or modify
00009  * it under the terms of the GNU General Public License as published by
00010  * the Free Software Foundation; either version 2 of the License, or
00011  * (at your option) any later version
00012  *
00013  * Kamailio 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 /*!
00024  * \file
00025  * \brief Digest Authentication Module, API exports
00026  * \ingroup auth
00027  * - Module: \ref auth
00028  */
00029 
00030 #include <string.h>
00031 #include "../../dprint.h"
00032 #include "../../parser/digest/digest.h"
00033 #include "../../sr_module.h"
00034 #include "../../str.h"
00035 #include "../../ut.h"
00036 #include "auth_mod.h"
00037 #include "nonce.h"
00038 #include "common.h"
00039 #include "api.h"
00040 #include "rpid.h"
00041 #include "index.h"
00042 
00043 static str auth_400_err = str_init(MESSAGE_400);
00044 static str auth_500_err = str_init(MESSAGE_500);
00045 
00046 
00047 /*!
00048  * \brief Strip the beginning of a realm string
00049  *
00050  * Strip the beginning of a realm string, depending on the length of
00051  * the realm_prefix.
00052  * \param _realm realm string
00053  */
00054 void strip_realm(str* _realm)
00055 {
00056    /* no param defined -- return */
00057    if (!realm_prefix.len) return;
00058 
00059    /* prefix longer than realm -- return */
00060    if (realm_prefix.len > _realm->len) return;
00061 
00062    /* match ? -- if so, shorten realm -*/
00063    if (memcmp(realm_prefix.s, _realm->s, realm_prefix.len) == 0) {
00064       _realm->s += realm_prefix.len;
00065       _realm->len -= realm_prefix.len;
00066    }
00067    return;
00068 }
00069 
00070 
00071 /*!
00072  * \brief Find credentials with given realm in a SIP message header
00073  * \param _m SIP message
00074  * \param _realm authentification realm
00075  * \param _hftype header field type
00076  * \param _h header field
00077  */
00078 static inline int find_credentials(struct sip_msg* _m, str* _realm,
00079                         hdr_types_t _hftype, struct hdr_field** _h)
00080 {
00081    struct hdr_field** hook, *ptr, *prev;
00082    hdr_flags_t hdr_flags;
00083    int res;
00084    str* r;
00085 
00086    /*
00087     * Determine if we should use WWW-Authorization or
00088     * Proxy-Authorization header fields, this parameter
00089     * is set in www_authorize and proxy_authorize
00090     */
00091    switch(_hftype) {
00092    case HDR_AUTHORIZATION_T:
00093       hook = &(_m->authorization);
00094       hdr_flags=HDR_AUTHORIZATION_F;
00095       break;
00096    case HDR_PROXYAUTH_T:
00097       hook = &(_m->proxy_auth);
00098       hdr_flags=HDR_PROXYAUTH_F;
00099       break;
00100    default:
00101       hook = &(_m->authorization);
00102       hdr_flags=HDR_T2F(_hftype);
00103       break;
00104    }
00105 
00106    /*
00107     * If the credentials haven't been parsed yet, do it now
00108     */
00109    if (*hook == 0) {
00110       /* No credentials parsed yet */
00111       if (parse_headers(_m, hdr_flags, 0) == -1) {
00112          LM_ERR("failed to parse headers\n");
00113          return -1;
00114       }
00115    }
00116 
00117    ptr = *hook;
00118 
00119    /*
00120     * Iterate through the credentials in the message and
00121     * find credentials with given realm
00122     */
00123    while(ptr) {
00124       res = parse_credentials(ptr);
00125       if (res < 0) {
00126          LM_ERR("failed to parse credentials\n");
00127          return (res == -1) ? -2 : -3;
00128       } else if (res == 0) {
00129          r = &(((auth_body_t*)(ptr->parsed))->digest.realm);
00130          if (r->len == _realm->len) {
00131             if (!strncasecmp(_realm->s, r->s, r->len)) {
00132                *_h = ptr;
00133                return 0;
00134             }
00135          }
00136       }
00137 
00138       prev = ptr;
00139       if (parse_headers(_m, hdr_flags, 1) == -1) {
00140          LM_ERR("failed to parse headers\n");
00141          return -4;
00142       } else {
00143          if (prev != _m->last_header) {
00144             if (_m->last_header->type == _hftype) ptr = _m->last_header;
00145             else break;
00146          } else break;
00147       }
00148    }
00149    
00150    /*
00151     * Credentials with given realm not found
00152     */
00153 
00154     return 1;
00155 }
00156 
00157 
00158 /*!
00159  * \brief Find credentials with given realm, check if we need to authenticate
00160  *
00161  * The purpose of this function is to find credentials with given realm,
00162  * do sanity check, validate credential correctness and determine if
00163  * we should really authenticate (there must be no authentication for
00164  * ACK and CANCEL.
00165  * \param _m SIP message
00166  * \param _realm authentification realm
00167  * \param _hftype header field type
00168  * \param _h header field
00169  * \return authentification result
00170  */
00171 auth_result_t pre_auth(struct sip_msg* _m, str* _realm, hdr_types_t _hftype,
00172                                        struct hdr_field** _h)
00173 {
00174    int ret;
00175    auth_body_t* c;
00176    struct sip_uri *uri;
00177 
00178    /* ACK and CANCEL must be always authorized, there is
00179     * no way how to challenge ACK and CANCEL cannot be
00180     * challenged because it must have the same CSeq as
00181     * the request to be canceled
00182     */
00183 
00184    if ((_m->REQ_METHOD == METHOD_ACK) ||  (_m->REQ_METHOD == METHOD_CANCEL))
00185       return AUTHORIZED;
00186 
00187    if (_realm->len == 0) {
00188       if (get_realm(_m, _hftype, &uri) < 0) {
00189          LM_ERR("failed to extract realm\n");
00190          if (send_resp(_m, 400, &auth_400_err, 0, 0) == -1) {
00191             LM_ERR("failed to send 400 reply\n");
00192          }
00193          return ERROR;
00194       }
00195       
00196       *_realm = uri->host;
00197       strip_realm(_realm);
00198    }
00199 
00200    /* Try to find credentials with corresponding realm
00201     * in the message, parse them and return pointer to
00202     * parsed structure
00203     */
00204    ret = find_credentials(_m, _realm, _hftype, _h);
00205    if (ret < 0) {
00206       LM_ERR("failed to find credentials\n");
00207       if (send_resp(_m, (ret == -2) ? 500 : 400, 
00208                (ret == -2) ? &auth_500_err : &auth_400_err, 0, 0) == -1) {
00209          LM_ERR("failed to send 400 reply\n");
00210       }
00211       return ERROR;
00212    } else if (ret > 0) {
00213       LM_DBG("credentials with given realm not found\n");
00214       return NO_CREDENTIALS;
00215    }
00216 
00217    /* Pointer to the parsed credentials */
00218    c = (auth_body_t*)((*_h)->parsed);
00219 
00220    /* Check credentials correctness here */
00221    if (check_dig_cred(&(c->digest)) != E_DIG_OK) {
00222       LM_ERR("received credentials are not filled properly\n");
00223       if (send_resp(_m, 400, &auth_400_err, 0, 0) == -1) {
00224          LM_ERR("failed to send 400 reply\n");
00225       }
00226       return ERROR;
00227    }
00228 
00229    if (mark_authorized_cred(_m, *_h) < 0) {
00230       LM_ERR("failed to mark parsed credentials\n");
00231       if (send_resp(_m, 500, &auth_400_err, 0, 0) == -1) {
00232          LM_ERR("failed to send 400 reply\n");
00233       }
00234       return ERROR;
00235    }
00236 
00237    if (check_nonce(&c->digest.nonce, &secret) != 0) {
00238       LM_DBG("invalid nonce value received\n");
00239       c->stale = 1;
00240       return STALE_NONCE;
00241    }
00242 
00243    return DO_AUTHORIZATION;
00244 }
00245 
00246 
00247 /*!
00248  * \brief Do post authentification steps
00249  *
00250  * The purpose of this function is to do post authentication steps like
00251  * marking authorized credentials and so on.
00252  * \param _m SIP message
00253  * \param _h header field
00254  * \return authentification result
00255  */
00256 auth_result_t post_auth(struct sip_msg* _m, struct hdr_field* _h)
00257 {
00258    auth_body_t* c;
00259    int index = 0;
00260 
00261    c = (auth_body_t*)((_h)->parsed);
00262 
00263    if ((_m->REQ_METHOD == METHOD_ACK) ||
00264       (_m->REQ_METHOD == METHOD_CANCEL))
00265       return AUTHORIZED;
00266 
00267    if (is_nonce_stale(&c->digest.nonce)) {
00268       LM_DBG("response is OK, but nonce is stale\n");
00269       c->stale = 1;
00270       return STALE_NONCE;
00271    } else {
00272       if(nonce_reuse==0)
00273       {
00274          /* Verify if it is the first time this nonce is received */
00275          index= get_nonce_index(&c->digest.nonce);
00276          if(index== -1)
00277          {
00278             LM_ERR("failed to extract nonce index\n");
00279             return ERROR;
00280          }
00281          LM_DBG("nonce index= %d\n", index);
00282 
00283          if(!is_nonce_index_valid(index))
00284          {
00285             LM_DBG("nonce index not valid\n");
00286             return NONCE_REUSED;
00287          }
00288       }
00289    }
00290    return AUTHORIZED;
00291 }
00292 
00293 
00294 /*!
00295  * \brief Calculate the response and compare with given response
00296  *
00297  * Calculate the response and compare with the given response string.
00298  * Authorization is successful if this two strings are same.
00299  * \param _cred digest credentials
00300  * \param _method method from the request
00301  * \param _ha1 HA1 value
00302  * \return 0 if comparison was ok, 1 when length not match, 2 when comparison not ok
00303  */
00304 int check_response(dig_cred_t* _cred, str* _method, char* _ha1)
00305 {
00306    HASHHEX resp, hent;
00307 
00308    /*
00309     * First, we have to verify that the response received has
00310     * the same length as responses created by us
00311     */
00312    if (_cred->response.len != 32) {
00313       LM_DBG("receive response len != 32\n");
00314       return 1;
00315    }
00316 
00317    /*
00318     * Now, calculate our response from parameters received
00319     * from the user agent
00320     */
00321    calc_response(_ha1, &(_cred->nonce),
00322       &(_cred->nc), &(_cred->cnonce),
00323       &(_cred->qop.qop_str), _cred->qop.qop_parsed == QOP_AUTHINT,
00324       _method, &(_cred->uri), hent, resp);
00325    
00326    LM_DBG("our result = \'%s\'\n", resp);
00327    
00328    /*
00329     * And simply compare the strings, the user is
00330     * authorized if they match
00331     */
00332    if (!memcmp(resp, _cred->response.s, 32)) {
00333       LM_DBG("authorization is OK\n");
00334       return 0;
00335    } else {
00336       LM_DBG("authorization failed\n");
00337       return 2;
00338    }
00339 }
00340 
00341 
00342 /*!
00343  * \brief Bind function for the auth API
00344  * \param api binded API
00345  * \return 0 on success, -1 on failure
00346  */
00347 int bind_auth(auth_api_t* api)
00348 {
00349    if (!api) {
00350       LM_ERR("invalid parameter value\n");
00351       return -1;
00352    }
00353 
00354    api->pre_auth = pre_auth;
00355    api->post_auth = post_auth;
00356    api->calc_HA1 = calc_HA1;
00357    api->check_response = check_response;
00358 
00359    get_rpid_avp( &api->rpid_avp, &api->rpid_avp_type );
00360 
00361    return 0;
00362 }

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