mirror of https://github.com/sipwise/kamailio.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
314 lines
7.0 KiB
314 lines
7.0 KiB
/*
|
|
* $Id$
|
|
*
|
|
* Digest Authentication - Radius support
|
|
*
|
|
* Copyright (C) 2001-2003 FhG Fokus
|
|
*
|
|
* This file is part of ser, a free SIP server.
|
|
*
|
|
* ser is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version
|
|
*
|
|
* For a license to use the ser software under conditions
|
|
* other than those described here, or to purchase support for this
|
|
* software, please contact iptel.org by e-mail at the following addresses:
|
|
* info@iptel.org
|
|
*
|
|
* ser is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
* History:
|
|
* -------
|
|
* 2003-03-09: Based on authorize.c from radius_auth (janakj)
|
|
*/
|
|
|
|
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include "../../mem/mem.h"
|
|
#include "../../str.h"
|
|
#include "../../sr_module.h"
|
|
#include "../../parser/hf.h"
|
|
#include "../../parser/digest/digest.h"
|
|
#include "../../parser/parse_uri.h"
|
|
#include "../../parser/parse_from.h"
|
|
#include "../../parser/parse_to.h"
|
|
#include "../../dprint.h"
|
|
#include "../../id.h"
|
|
#include "../../ut.h"
|
|
#include "../../modules/auth/api.h"
|
|
#include "authorize.h"
|
|
#include "sterman.h"
|
|
#include "authrad_mod.h"
|
|
|
|
|
|
static void attr_name_value(str* name, str* value, VALUE_PAIR* vp)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < vp->lvalue; i++) {
|
|
if (vp->strvalue[i] == ':' || vp->strvalue[i] == '=') {
|
|
name->s = vp->strvalue;
|
|
name->len = i;
|
|
|
|
if (i == (vp->lvalue - 1)) {
|
|
value->s = (char*)0;
|
|
value->len = 0;
|
|
} else {
|
|
value->s = vp->strvalue + i + 1;
|
|
value->len = vp->lvalue - i - 1;
|
|
}
|
|
return;
|
|
}
|
|
}
|
|
|
|
name->len = value->len = 0;
|
|
name->s = value->s = (char*)0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Generate AVPs from the database result
|
|
*/
|
|
static int generate_avps(VALUE_PAIR* received)
|
|
{
|
|
int_str name, val;
|
|
VALUE_PAIR *vp;
|
|
|
|
vp = rc_avpair_get(received, ATTRID(attrs[A_SER_UID].v), VENDOR(attrs[A_SER_UID].v));
|
|
if (vp == NULL) {
|
|
WARN("RADIUS server did not send SER-UID attribute in digest authentication reply\n");
|
|
return -1;
|
|
}
|
|
val.s.len = vp->lvalue;
|
|
val.s.s = vp->strvalue;
|
|
name.s.s = "uid";
|
|
name.s.len = 3;
|
|
|
|
if (add_avp(AVP_TRACK_FROM | AVP_CLASS_USER | AVP_NAME_STR | AVP_VAL_STR, name, val) < 0) {
|
|
ERR("Unable to create UID attribute\n");
|
|
return -1;
|
|
}
|
|
|
|
vp = received;
|
|
while ((vp = rc_avpair_get(vp, ATTRID(attrs[A_SER_ATTR].v), VENDOR(attrs[A_SER_ATTR].v)))) {
|
|
attr_name_value(&name.s, &val.s, vp);
|
|
if (name.s.len == 0) {
|
|
ERR("Missing attribute name\n");
|
|
return -1;
|
|
}
|
|
|
|
if (add_avp(AVP_TRACK_FROM | AVP_CLASS_USER | AVP_NAME_STR | AVP_VAL_STR, name, val) < 0) {
|
|
LOG(L_ERR, "generate_avps: Unable to create a new AVP\n");
|
|
return -1;
|
|
} else {
|
|
DBG("generate_avps: AVP '%.*s'='%.*s' has been added\n",
|
|
name.s.len, ZSW(name.s.s),
|
|
val.s.len, ZSW(val.s.s));
|
|
}
|
|
vp = vp->next;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
* Extract URI depending on the request from To or From header
|
|
*/
|
|
static inline int get_uri(struct sip_msg* _m, str** _uri)
|
|
{
|
|
if ((REQ_LINE(_m).method.len == 8) && (memcmp(REQ_LINE(_m).method.s, "REGISTER", 8) == 0)) {
|
|
if (!_m->to && ((parse_headers(_m, HDR_TO_F, 0) == -1) || !_m->to)) {
|
|
LOG(L_ERR, "get_uri(): To header field not found or malformed\n");
|
|
return -1;
|
|
}
|
|
*_uri = &(get_to(_m)->uri);
|
|
} else {
|
|
if (parse_from_header(_m) == -1) {
|
|
LOG(L_ERR, "get_uri(): Error while parsing headers\n");
|
|
return -2;
|
|
}
|
|
*_uri = &(get_from(_m)->uri);
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
|
|
/*
|
|
* Authorize digest credentials
|
|
*/
|
|
static inline int authenticate(struct sip_msg* msg, str* realm,
|
|
hdr_types_t hftype)
|
|
{
|
|
int res;
|
|
auth_result_t ret;
|
|
struct hdr_field* h;
|
|
auth_body_t* cred;
|
|
str* uri;
|
|
struct sip_uri puri;
|
|
str user, did;
|
|
VALUE_PAIR* received;
|
|
|
|
cred = 0;
|
|
ret = -1;
|
|
user.s = 0;
|
|
received = NULL;
|
|
|
|
switch(auth_api.pre_auth(msg, realm, hftype, &h, NULL)) {
|
|
default:
|
|
BUG("unexpected reply '%d'.\n", auth_api.pre_auth(msg, realm, hftype,
|
|
&h, NULL));
|
|
#ifdef EXTRA_DEBUG
|
|
abort();
|
|
#endif
|
|
case NONCE_REUSED:
|
|
LM_DBG("nonce reused");
|
|
ret = AUTH_NONCE_REUSED;
|
|
goto end;
|
|
case STALE_NONCE:
|
|
LM_DBG("stale nonce\n");
|
|
ret = AUTH_STALE_NONCE;
|
|
goto end;
|
|
case NO_CREDENTIALS:
|
|
LM_DBG("no credentials\n");
|
|
ret = AUTH_NO_CREDENTIALS;
|
|
case ERROR:
|
|
case BAD_CREDENTIALS:
|
|
ret = -3;
|
|
goto end;
|
|
|
|
case NOT_AUTHENTICATED:
|
|
ret = -1;
|
|
goto end;
|
|
|
|
case DO_AUTHENTICATION:
|
|
break;
|
|
|
|
case AUTHENTICATED:
|
|
ret = 1;
|
|
goto end;
|
|
}
|
|
|
|
cred = (auth_body_t*)h->parsed;
|
|
|
|
if (use_did) {
|
|
if (msg->REQ_METHOD == METHOD_REGISTER) {
|
|
ret = get_to_did(&did, msg);
|
|
} else {
|
|
ret = get_from_did(&did, msg);
|
|
}
|
|
if (ret == 0) {
|
|
did.s = DEFAULT_DID;
|
|
did.len = sizeof(DEFAULT_DID) - 1;
|
|
}
|
|
} else {
|
|
did.len = 0;
|
|
did.s = 0;
|
|
}
|
|
|
|
if (get_uri(msg, &uri) < 0) {
|
|
LOG(L_ERR, "authorize(): From/To URI not found\n");
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
|
|
if (parse_uri(uri->s, uri->len, &puri) < 0) {
|
|
LOG(L_ERR, "authorize(): Error while parsing From/To URI\n");
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
|
|
user.s = (char *)pkg_malloc(puri.user.len);
|
|
if (user.s == NULL) {
|
|
LOG(L_ERR, "authorize: No memory left\n");
|
|
ret = -1;
|
|
goto end;
|
|
}
|
|
un_escape(&(puri.user), &user);
|
|
|
|
res = radius_authorize_sterman(&received, msg, &cred->digest, &msg->first_line.u.request.method, &user);
|
|
if (res == 1) {
|
|
switch(auth_api.post_auth(msg, h)) {
|
|
case ERROR:
|
|
case BAD_CREDENTIALS:
|
|
ret = -2;
|
|
break;
|
|
|
|
case NOT_AUTHENTICATED:
|
|
ret = -1;
|
|
break;
|
|
|
|
case AUTHENTICATED:
|
|
if (generate_avps(received) < 0) {
|
|
ret = -1;
|
|
break;
|
|
}
|
|
ret = 1;
|
|
break;
|
|
|
|
default:
|
|
ret = -1;
|
|
break;
|
|
}
|
|
} else {
|
|
ret = -1;
|
|
}
|
|
|
|
end:
|
|
if (received) rc_avpair_free(received);
|
|
if (user.s) pkg_free(user.s);
|
|
if (ret < 0) {
|
|
if (auth_api.build_challenge(msg, (cred ? cred->stale : 0), realm, NULL, NULL, hftype) < 0) {
|
|
ERR("Error while creating challenge\n");
|
|
ret = -2;
|
|
}
|
|
}
|
|
return ret;
|
|
}
|
|
|
|
|
|
/*
|
|
* Authorize using Proxy-Authorize header field
|
|
*/
|
|
int radius_proxy_authorize(struct sip_msg* _msg, char* p1, char* p2)
|
|
{
|
|
str realm;
|
|
|
|
if (get_str_fparam(&realm, _msg, (fparam_t*)p1) < 0) {
|
|
ERR("Cannot obtain digest realm from parameter '%s'\n", ((fparam_t*)p1)->orig);
|
|
return -1;
|
|
}
|
|
|
|
/* realm parameter is converted to str* in str_fixup */
|
|
return authenticate(_msg, &realm, HDR_PROXYAUTH_T);
|
|
}
|
|
|
|
|
|
/*
|
|
* Authorize using WWW-Authorize header field
|
|
*/
|
|
int radius_www_authorize(struct sip_msg* _msg, char* p1, char* p2)
|
|
{
|
|
str realm;
|
|
|
|
if (get_str_fparam(&realm, _msg, (fparam_t*)p1) < 0) {
|
|
ERR("Cannot obtain digest realm from parameter '%s'\n", ((fparam_t*)p1)->orig);
|
|
return -1;
|
|
}
|
|
|
|
return authenticate(_msg, &realm, HDR_AUTHORIZATION_T);
|
|
}
|
|
|