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.
kamailio/parser/digest/digest_parser.c

395 lines
8.4 KiB

/*
* $Id$
*
* Digest credentials parser
*
* 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-02: Added parse_domain function (janakj)
*/
#include "digest_parser.h"
#include "../../trim.h" /* trim_leading */
#include <string.h> /* strncasecmp */
#include "param_parser.h" /* Digest parameter name parser */
#include "../../ut.h" /* q_memchr */
#define DIGEST_SCHEME "digest"
#define DIG_LEN 6
#define QOP_AUTH_STR "auth"
#define QOP_AUTH_STR_LEN 4
#define QOP_AUTHINT_STR "auth-int"
#define QOP_AUTHINT_STR_LEN 8
#define ALG_MD5_STR "MD5"
#define ALG_MD5_STR_LEN 3
#define ALG_MD5SESS_STR "MD5-sess"
#define ALG_MD5SESS_STR_LEN 8
/*
* Parse quoted string in a parameter body
* return the string without quotes in _r
* parameter and update _s to point behind the
* closing quote
*/
static inline int parse_quoted(str* _s, str* _r)
{
char* end_quote;
/* The string must have at least
* surrounding quotes
*/
if (_s->len < 2) {
return -1;
}
/* Skip opening quote */
_s->s++;
_s->len--;
/* Find closing quote */
end_quote = q_memchr(_s->s, '\"', _s->len);
/* Not found, return error */
if (!end_quote) {
return -2;
}
/* Let _r point to the string without
* surrounding quotes
*/
_r->s = _s->s;
_r->len = end_quote - _s->s;
/* Update _s parameter to point
* behind the closing quote
*/
_s->len -= (end_quote - _s->s + 1);
_s->s = end_quote + 1;
/* Everything went OK */
return 0;
}
/*
* Parse unquoted token in a parameter body
* let _r point to the token and update _s
* to point right behind the token
*/
static inline int parse_token(str* _s, str* _r)
{
int i;
/* Save the begining of the
* token in _r->s
*/
_r->s = _s->s;
/* Iterate through the
* token body
*/
for(i = 0; i < _s->len; i++) {
/* All these characters
* mark end of the token
*/
switch(_s->s[i]) {
case ' ':
case '\t':
case '\r':
case '\n':
case ',':
/* So if you find
* any of them
* stop iterating
*/
goto out;
}
}
out:
/* Empty token is error */
if (i == 0) {
return -2;
}
/* Save length of the token */
_r->len = i;
/* Update _s parameter so it points
* right behind the end of the token
*/
_s->s = _s->s + i;
_s->len -= i;
/* Everything went OK */
return 0;
}
/*
* Parse a digest parameter
*/
static inline int parse_digest_param(str* _s, dig_cred_t* _c)
{
dig_par_t t;
str* ptr;
str dummy;
/* Get type of the parameter */
if (parse_param_name(_s, &t) < 0) {
return -1;
}
_s->s++; /* skip = */
_s->len--;
/* Find the begining of body */
trim_leading(_s);
if (_s->len == 0) {
return -2;
}
/* Decide in which attribute the
* body content will be stored
*/
switch(t) {
case PAR_USERNAME: ptr = &_c->username.whole; break;
case PAR_REALM: ptr = &_c->realm; break;
case PAR_NONCE: ptr = &_c->nonce; break;
case PAR_URI: ptr = &_c->uri; break;
case PAR_RESPONSE: ptr = &_c->response; break;
case PAR_CNONCE: ptr = &_c->cnonce; break;
case PAR_OPAQUE: ptr = &_c->opaque; break;
case PAR_QOP: ptr = &_c->qop.qop_str; break;
case PAR_NC: ptr = &_c->nc; break;
case PAR_ALGORITHM: ptr = &_c->alg.alg_str; break;
case PAR_OTHER: ptr = &dummy; break;
default: ptr = &dummy; break;
}
/* If the first character is quote, it is
* a quoted string, otherwise it is a token
*/
if (_s->s[0] == '\"') {
if (parse_quoted(_s, ptr) < 0) {
return -3;
}
} else {
if (parse_token(_s, ptr) < 0) {
return -4;
}
}
return 0;
}
/*
* Parse qop parameter body
*/
void parse_qop(struct qp* _q)
{
str s;
s.s = _q->qop_str.s;
s.len = _q->qop_str.len;
trim(&s);
if (s.len == 0) {
_q->qop_parsed = QOP_UNSPEC;
} else if ((s.len == QOP_AUTH_STR_LEN) &&
!strncasecmp(s.s, QOP_AUTH_STR, QOP_AUTH_STR_LEN)) {
_q->qop_parsed = QOP_AUTH;
} else if ((s.len == QOP_AUTHINT_STR_LEN) &&
!strncasecmp(s.s, QOP_AUTHINT_STR, QOP_AUTHINT_STR_LEN)) {
_q->qop_parsed = QOP_AUTHINT;
} else {
_q->qop_parsed = QOP_OTHER;
}
}
/*
* Parse algorithm parameter body
*/
static inline void parse_algorithm(struct algorithm* _a)
{
str s;
s.s = _a->alg_str.s;
s.len = _a->alg_str.len;
trim(&s);
if ((s.len == ALG_MD5_STR_LEN) &&
!strncasecmp(s.s, ALG_MD5_STR, ALG_MD5_STR_LEN)) {
_a->alg_parsed = ALG_MD5;
} else if ((s.len == ALG_MD5SESS_STR_LEN) &&
!strncasecmp(s.s, ALG_MD5SESS_STR, ALG_MD5SESS_STR_LEN)) {
_a->alg_parsed = ALG_MD5SESS;
} else {
_a->alg_parsed = ALG_OTHER;
}
}
/*
* Parse username for user and domain parts
*/
static inline void parse_username(struct username* _u)
{
char* d;
_u->user = _u->whole;
if (_u->whole.len <= 2) return;
d = q_memchr(_u->whole.s, '@', _u->whole.len);
if (d) {
_u->domain.s = d + 1;
_u->domain.len = _u->whole.len - (d - _u->whole.s) - 1;
_u->user.len = d - _u->user.s;
}
}
/*
* Parse Digest credentials parameter, one by one
*/
static inline int parse_digest_params(str* _s, dig_cred_t* _c)
{
char* comma;
do {
/* Parse the first parameter */
if (parse_digest_param(_s, _c) < 0) {
return -1;
}
/* Try to find the next parameter */
comma = q_memchr(_s->s, ',', _s->len);
if (comma) {
/* Yes, there is another,
* remove any leading white-spaces
* and let _s point to the next
* parameter name
*/
_s->len -= comma - _s->s + 1;
_s->s = comma + 1;
trim_leading(_s);
}
} while(comma); /* Repeat while there are next parameters */
/* Parse QOP body if the parameter was present */
if (_c->qop.qop_str.s != 0) {
parse_qop(&_c->qop);
}
/* Parse algorithm body if the parameter was present */
if (_c->alg.alg_str.s != 0) {
parse_algorithm(&_c->alg);
}
if (_c->username.whole.s != 0) {
parse_username(&_c->username);
}
return 0;
}
/*
* We support Digest authentication only
*
* Returns:
* 0 - if everything is OK
* -1 - Error while parsing
* 1 - Unknown scheme
*/
int parse_digest_cred(str* _s, dig_cred_t* _c)
{
str tmp;
/* Make a temporary copy, we are
* going to modify it
*/
tmp.s = _s->s;
tmp.len = _s->len;
/* Remove any leading spaces, tabs, \r and \n */
trim_leading(&tmp);
/* Check the string length */
if (tmp.len < (DIG_LEN + 1)) return 1; /* Too short, unknown scheme */
/* Now test, if it is digest scheme, since it is the only
* scheme we are able to parse here
*/
if (!strncasecmp(tmp.s, DIGEST_SCHEME, DIG_LEN) &&
((tmp.s[DIG_LEN] == ' ') || /* Test for one of LWS chars */
(tmp.s[DIG_LEN] == '\r') ||
(tmp.s[DIG_LEN] == '\n') ||
(tmp.s[DIG_LEN] == '\t') ||
(tmp.s[DIG_LEN] == ','))) {
/* Scheme is Digest */
tmp.s += DIG_LEN + 1;
tmp.len -= DIG_LEN + 1;
/* Again, skip all white-spaces */
trim_leading(&tmp);
/* And parse digest parameters */
if (parse_digest_params(&tmp, _c) < 0) {
return -2; /* We must not return -1 in this function ! */
} else {
return 0;
}
} else {
return 1; /* Unknown scheme */
}
}
/*
* Initialize a digest credentials structure
*/
void init_dig_cred(dig_cred_t* _c)
{
memset(_c, 0, sizeof(dig_cred_t));
}