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.
387 lines
9.6 KiB
387 lines
9.6 KiB
/*
|
|
* $Id$
|
|
*
|
|
* Copyright (C) 2005 Voice Sistem SRL
|
|
*
|
|
* This file is part of SIP Express Router.
|
|
*
|
|
* UAC SER-module 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.
|
|
*
|
|
* UAC SER-module 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.
|
|
*
|
|
* For any questions about this software and its license, please contact
|
|
* Voice Sistem at following e-mail address:
|
|
* office@voice-sistem.ro
|
|
*
|
|
*
|
|
* History:
|
|
* ---------
|
|
* 2005-01-31 first version (ramona)
|
|
*/
|
|
|
|
|
|
#include "string.h"
|
|
#include "ctype.h"
|
|
|
|
#include "../../dprint.h"
|
|
#include "../../str.h"
|
|
#include "../../mem/mem.h"
|
|
|
|
#include "auth_hdr.h"
|
|
#include "auth.h"
|
|
|
|
|
|
#define AUTHENTICATE_MD5 (1<<0)
|
|
#define AUTHENTICATE_MD5SESS (1<<1)
|
|
#define AUTHENTICATE_STALE (1<<2)
|
|
|
|
#define AUTHENTICATE_DIGEST_S "Digest"
|
|
#define AUTHENTICATE_DIGEST_LEN (sizeof(AUTHENTICATE_DIGEST_S)-1)
|
|
|
|
#define LOWER1B(_n) \
|
|
((_n)|0x20)
|
|
#define LOWER4B(_n) \
|
|
((_n)|0x20202020)
|
|
#define GET4B(_p) \
|
|
((*(p)<<24) + (*(p+1)<<16) + (*(p+2)<<8) + *(p+3))
|
|
#define GET3B(_p) \
|
|
((*(p)<<24) + (*(p+1)<<16) + (*(p+2)<<8) + 0xff)
|
|
|
|
#define CASE_5B(_hex4,_c5, _new_state) \
|
|
case _hex4: \
|
|
if (p+5<end && LOWER1B(*(p+4))==_c5 ) \
|
|
{ \
|
|
p+=5; \
|
|
state = _new_state; \
|
|
} else { \
|
|
p+=4; \
|
|
} \
|
|
break;
|
|
|
|
#define CASE_6B(_hex4,_c5,_c6, _new_state) \
|
|
case _hex4: \
|
|
if (p+6<end && LOWER1B(*(p+4))==_c5 && LOWER1B(*(p+5))==_c6) \
|
|
{ \
|
|
p+=6; \
|
|
state = _new_state; \
|
|
} else { \
|
|
p+=4; \
|
|
} \
|
|
break;
|
|
|
|
#define OTHER_STATE 0
|
|
#define QOP_STATE 1
|
|
#define REALM_STATE 2
|
|
#define NONCE_STATE 3
|
|
#define STALE_STATE 4
|
|
#define DOMAIN_STATE 5
|
|
#define OPAQUE_STATE 6
|
|
#define ALGORITHM_STATE 7
|
|
|
|
|
|
|
|
int parse_authenticate_body( str *body, struct authenticate_body *auth)
|
|
{
|
|
char *p;
|
|
char *end;
|
|
int n;
|
|
int state;
|
|
str name;
|
|
str val;
|
|
|
|
if (body->s==0 || *body->s==0 )
|
|
{
|
|
LOG(L_ERR,"ERROR:uac:parse_authenticate_body: empty body\n");
|
|
goto error;
|
|
}
|
|
|
|
memset( auth, 0, sizeof(struct authenticate_body));
|
|
p = body->s;
|
|
end = body->s + body->len;
|
|
|
|
/* parse the "digest" */
|
|
while (p<end && isspace((int)*p)) p++;
|
|
if (p+AUTHENTICATE_DIGEST_LEN>=end )
|
|
goto parse_error;
|
|
if (strncmp(p,AUTHENTICATE_DIGEST_S,AUTHENTICATE_DIGEST_LEN)!=0)
|
|
goto parse_error;
|
|
p += AUTHENTICATE_DIGEST_LEN;
|
|
if (!isspace((int)*p))
|
|
goto parse_error;
|
|
p++;
|
|
while (p<end && isspace((int)*p)) p++;
|
|
if (p==end)
|
|
goto parse_error;
|
|
|
|
while (p<end)
|
|
{
|
|
state = OTHER_STATE;
|
|
/* get name */
|
|
name.s = p;
|
|
if (p+4<end)
|
|
{
|
|
n = LOWER4B( GET4B(p) );
|
|
switch(n)
|
|
{
|
|
CASE_5B( 0x7265616c, 'm', REALM_STATE); /*realm*/
|
|
CASE_5B( 0x6e6f6e63, 'e', NONCE_STATE); /*nonce*/
|
|
CASE_5B( 0x7374616c, 'e', STALE_STATE); /*stale*/
|
|
CASE_6B( 0x646f6d62, 'i', 'n', DOMAIN_STATE); /*domain*/
|
|
CASE_6B( 0x6f706171, 'u', 'e', OPAQUE_STATE); /*opaque*/
|
|
case 0x616c676f: /*algo*/
|
|
if (p+9<end && LOWER4B(GET4B(p+4))==0x72697468
|
|
&& LOWER1B(*(p+9))=='m' )
|
|
{
|
|
p+=9;
|
|
state = ALGORITHM_STATE;
|
|
} else {
|
|
p+=4;
|
|
}
|
|
break;
|
|
default:
|
|
if ((n|0xff)==0x716f70ff) /*qop*/
|
|
{
|
|
state = QOP_STATE;
|
|
p+=3;
|
|
}
|
|
}
|
|
} else if (p+3<end) {
|
|
n = LOWER4B( GET3B(p) );
|
|
if (n==0x716f70ff) /*qop*/
|
|
{
|
|
p+=3;
|
|
state = QOP_STATE;
|
|
}
|
|
}
|
|
|
|
/* parse to the "=" */
|
|
for( n=0 ; p<end&&!isspace((int)*p)&&*p!='=' ; n++,p++ );
|
|
if (p==end)
|
|
goto parse_error;
|
|
if (n!=0)
|
|
state = OTHER_STATE;
|
|
name.len = p-name.s;
|
|
/* get the '=' */
|
|
while (p<end && isspace((int)*p)) p++;
|
|
if (p==end || *p!='=')
|
|
goto parse_error;
|
|
p++;
|
|
/* get the value */
|
|
while (p<end && isspace((int)*p)) p++;
|
|
if (p+1>=end || *p!='\"')
|
|
goto parse_error;
|
|
val.s = ++p;
|
|
while (p<end && *p!='\"')
|
|
p++;
|
|
if (p==end)
|
|
goto error;
|
|
val.len = p - val.s;
|
|
if (val.len==0)
|
|
val.s = 0;
|
|
p++;
|
|
while (p<end && isspace((int)*p)) p++;
|
|
if (p<end && *p==',')
|
|
{
|
|
p++;
|
|
while (p<end && isspace((int)*p)) p++;
|
|
}
|
|
|
|
DBG("DEBUG:uac:parse_authenticate_body: <%.*s>=\"%.*s\" state=%d\n",
|
|
name.len,name.s,val.len,val.s,state);
|
|
|
|
/* process the AVP */
|
|
switch (state)
|
|
{
|
|
case QOP_STATE:
|
|
/* TODO - add qop support */
|
|
LOG(L_ERR,"ERROR:uac:parse_authenticate_body: no qop support "
|
|
"for the moment :-(\n");
|
|
goto error;
|
|
auth->qop = val;
|
|
break;
|
|
case REALM_STATE:
|
|
auth->realm = val;
|
|
break;
|
|
case NONCE_STATE:
|
|
auth->nonce = val;
|
|
break;
|
|
case DOMAIN_STATE:
|
|
auth->domain = val;
|
|
break;
|
|
case OPAQUE_STATE:
|
|
auth->opaque = val;
|
|
break;
|
|
case ALGORITHM_STATE:
|
|
if (val.len==3)
|
|
{
|
|
if ( LOWER4B(GET3B(val.s))==0x6d6435ff) /*MD5*/
|
|
auth->flags |= AUTHENTICATE_MD5;
|
|
} else {
|
|
LOG(L_ERR,"ERROR:uac:parse_authenticate_body: "
|
|
"unsupported algorithm \"%.*s\"\n",val.len,val.s);
|
|
goto error;
|
|
}
|
|
break;
|
|
case STALE_STATE:
|
|
if (val.len==4 && LOWER4B(GET4B(val.s))==0x74727565) /*true*/
|
|
{
|
|
auth->flags |= AUTHENTICATE_STALE;
|
|
} else if ( !(val.len==5 && val.s[4]=='e' &&
|
|
LOWER4B(GET4B(val.s))==0x66616c73) )
|
|
{
|
|
LOG(L_ERR,"ERROR:uac:parse_authenticate_body: "
|
|
"unsupported stale value \"%.*s\"\n",val.len,val.s);
|
|
goto error;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* some checkings */
|
|
if (auth->nonce.s==0 || auth->realm.s==0)
|
|
{
|
|
LOG(L_ERR,"ERROR:uac:parse_authenticate_body: realm or "
|
|
"nonce missing\n");
|
|
goto error;
|
|
}
|
|
|
|
return 0;
|
|
parse_error:
|
|
LOG(L_ERR,"ERROR:uac:parse_authenticate_body: parse error in <%.*s> "
|
|
"around %ld\n", body->len, body->s, (long)(p-body->s));
|
|
error:
|
|
return -1;
|
|
}
|
|
|
|
|
|
#define AUTHORIZATION_HDR_START "Authorization: Digest "
|
|
#define AUTHORIZATION_HDR_START_LEN (sizeof(AUTHORIZATION_HDR_START)-1)
|
|
|
|
#define PROXY_AUTHORIZATION_HDR_START "Proxy-Authorization: Digest "
|
|
#define PROXY_AUTHORIZATION_HDR_START_LEN \
|
|
(sizeof(PROXY_AUTHORIZATION_HDR_START)-1)
|
|
|
|
#define USERNAME_FIELD_S "username=\""
|
|
#define USERNAME_FIELD_LEN (sizeof(USERNAME_FIELD_S)-1)
|
|
#define REALM_FIELD_S "realm=\""
|
|
#define REALM_FIELD_LEN (sizeof(REALM_FIELD_S)-1)
|
|
#define NONCE_FIELD_S "nonce=\""
|
|
#define NONCE_FIELD_LEN (sizeof(NONCE_FIELD_S)-1)
|
|
#define URI_FIELD_S "uri=\""
|
|
#define URI_FIELD_LEN (sizeof(URI_FIELD_S)-1)
|
|
#define OPAQUE_FIELD_S "opaque=\""
|
|
#define OPAQUE_FIELD_LEN (sizeof(OPAQUE_FIELD_S)-1)
|
|
#define RESPONSE_FIELD_S "response=\""
|
|
#define RESPONSE_FIELD_LEN (sizeof(RESPONSE_FIELD_S)-1)
|
|
#define ALGORITHM_FIELD_S "algorithm=\"MD5\""
|
|
#define ALGORITHM_FIELD_LEN (sizeof(ALGORITHM_FIELD_S)-1)
|
|
#define FIELD_SEPARATOR_S "\", "
|
|
#define FIELD_SEPARATOR_LEN (sizeof(FIELD_SEPARATOR_S)-1)
|
|
|
|
#define add_string( _p, _s, _l) \
|
|
do {\
|
|
memcpy( _p, _s, _l);\
|
|
_p += _l; \
|
|
}while(0)
|
|
|
|
|
|
str* build_authorization_hdr(int code, str *uri,
|
|
struct uac_credential *crd, struct authenticate_body *auth,
|
|
char *response)
|
|
{
|
|
static str hdr;
|
|
char *p;
|
|
int len;
|
|
int response_len;
|
|
|
|
response_len = strlen(response);
|
|
|
|
/* compile then len */
|
|
len = (code==401?
|
|
AUTHORIZATION_HDR_START_LEN:PROXY_AUTHORIZATION_HDR_START_LEN) +
|
|
USERNAME_FIELD_LEN + crd->user.len + FIELD_SEPARATOR_LEN +
|
|
REALM_FIELD_LEN + crd->realm.len + FIELD_SEPARATOR_LEN +
|
|
NONCE_FIELD_LEN + auth->nonce.len + FIELD_SEPARATOR_LEN +
|
|
URI_FIELD_LEN + uri->len + FIELD_SEPARATOR_LEN +
|
|
(auth->opaque.len?
|
|
(OPAQUE_FIELD_LEN + auth->opaque.len + FIELD_SEPARATOR_LEN):0) +
|
|
RESPONSE_FIELD_LEN + response_len + FIELD_SEPARATOR_LEN +
|
|
ALGORITHM_FIELD_LEN + CRLF_LEN;
|
|
|
|
hdr.s = (char*)pkg_malloc( len + 1);
|
|
if (hdr.s==0)
|
|
{
|
|
LOG(L_ERR,"ERROR:uac:build_authorization_hdr: no more mem\n");
|
|
goto error;
|
|
}
|
|
|
|
p = hdr.s;
|
|
/* header start */
|
|
if (code==401)
|
|
{
|
|
add_string( p, AUTHORIZATION_HDR_START USERNAME_FIELD_S,
|
|
AUTHORIZATION_HDR_START_LEN+USERNAME_FIELD_LEN);
|
|
} else {
|
|
add_string( p, PROXY_AUTHORIZATION_HDR_START USERNAME_FIELD_S,
|
|
PROXY_AUTHORIZATION_HDR_START_LEN+USERNAME_FIELD_LEN);
|
|
}
|
|
/* username */
|
|
add_string( p, crd->user.s, crd->user.len);
|
|
/* REALM */
|
|
add_string( p, FIELD_SEPARATOR_S REALM_FIELD_S,
|
|
FIELD_SEPARATOR_LEN+REALM_FIELD_LEN);
|
|
add_string( p, crd->realm.s, crd->realm.len);
|
|
/* NONCE */
|
|
add_string( p, FIELD_SEPARATOR_S NONCE_FIELD_S,
|
|
FIELD_SEPARATOR_LEN+NONCE_FIELD_LEN);
|
|
add_string( p, auth->nonce.s, auth->nonce.len);
|
|
/* URI */
|
|
add_string( p, FIELD_SEPARATOR_S URI_FIELD_S,
|
|
FIELD_SEPARATOR_LEN+URI_FIELD_LEN);
|
|
add_string( p, uri->s, uri->len);
|
|
/* OPAQUE */
|
|
if (auth->opaque.len )
|
|
{
|
|
add_string( p, FIELD_SEPARATOR_S OPAQUE_FIELD_S,
|
|
FIELD_SEPARATOR_LEN+OPAQUE_FIELD_LEN);
|
|
add_string( p, auth->opaque.s, auth->opaque.len);
|
|
}
|
|
/* RESPONSE */
|
|
add_string( p, FIELD_SEPARATOR_S RESPONSE_FIELD_S,
|
|
FIELD_SEPARATOR_LEN+RESPONSE_FIELD_LEN);
|
|
add_string( p, response, response_len);
|
|
/* ALGORITHM */
|
|
add_string( p, FIELD_SEPARATOR_S ALGORITHM_FIELD_S CRLF,
|
|
FIELD_SEPARATOR_LEN+ALGORITHM_FIELD_LEN+CRLF_LEN);
|
|
|
|
hdr.len = p - hdr.s;
|
|
|
|
if (hdr.len!=len)
|
|
{
|
|
LOG(L_CRIT,"BUG:uac:build_authorization_hdr: bad buffer computation "
|
|
"(%d<>%d)\n",len,hdr.len);
|
|
pkg_free( hdr.s );
|
|
goto error;
|
|
}
|
|
|
|
DBG("DEBUG:uac:build_authorization_hdr: hdr is <%.*s>\n",
|
|
hdr.len,hdr.s);
|
|
|
|
return &hdr;
|
|
error:
|
|
return 0;
|
|
}
|
|
|