digest_parser.c

Go to the documentation of this file.
00001 /*
00002  * $Id: digest_parser.c 4892 2008-09-11 17:44:28Z henningw $
00003  *
00004  * Digest credentials parser
00005  *
00006  * Copyright (C) 2001-2003 FhG Fokus
00007  *
00008  * This file is part of Kamailio, a free SIP server.
00009  *
00010  * Kamailio is free software; you can redistribute it and/or modify
00011  * it under the terms of the GNU General Public License as published by
00012  * the Free Software Foundation; either version 2 of the License, or
00013  * (at your option) any later version
00014  *
00015  * Kamailio is distributed in the hope that it will be useful,
00016  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00017  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00018  * GNU General Public License for more details.
00019  *
00020  * You should have received a copy of the GNU General Public License 
00021  * along with this program; if not, write to the Free Software 
00022  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
00023  *
00024  * History:
00025  * --------
00026  * 2003-03-02: Added parse_domain function (janakj)
00027  */
00028 
00029 
00030 
00031 #include "digest_parser.h"
00032 #include "../../trim.h"    /* trim_leading */
00033 #include <string.h>        /* strncasecmp */
00034 #include "param_parser.h"  /* Digest parameter name parser */
00035 
00036 
00037 #define DIGEST_SCHEME "digest"
00038 #define DIG_LEN 6
00039 
00040 #define QOP_AUTH_STR "auth"
00041 #define QOP_AUTH_STR_LEN 4
00042 
00043 #define QOP_AUTHINT_STR "auth-int"
00044 #define QOP_AUTHINT_STR_LEN 8
00045 
00046 #define ALG_MD5_STR "MD5"
00047 #define ALG_MD5_STR_LEN 3
00048 
00049 #define ALG_MD5SESS_STR "MD5-sess"
00050 #define ALG_MD5SESS_STR_LEN 8
00051 
00052 
00053 /*
00054  * Parse quoted string in a parameter body
00055  * return the string without quotes in _r
00056  * parameter and update _s to point behind the
00057  * closing quote
00058  */
00059 static inline int parse_quoted(str* _s, str* _r)
00060 {
00061    char* end_quote;
00062 
00063         /* The string must have at least
00064          * surrounding quotes
00065          */
00066    if (_s->len < 2) {
00067       return -1;
00068    }
00069 
00070         /* Skip opening quote */
00071    _s->s++;
00072    _s->len--;
00073 
00074 
00075         /* Find closing quote */
00076    end_quote = memchr(_s->s, '\"', _s->len);
00077 
00078         /* Not found, return error */
00079    if (!end_quote) {
00080       return -2;
00081    }
00082 
00083         /* Let _r point to the string without
00084          * surrounding quotes
00085          */
00086    _r->s = _s->s;
00087    _r->len = end_quote - _s->s;
00088 
00089         /* Update _s parameter to point
00090          * behind the closing quote
00091          */
00092    _s->len -= (end_quote - _s->s + 1);
00093    _s->s = end_quote + 1;
00094 
00095         /* Everything went OK */
00096    return 0;
00097 }
00098 
00099 
00100 /*
00101  * Parse unquoted token in a parameter body
00102  * let _r point to the token and update _s
00103  * to point right behind the token
00104  */
00105 static inline int parse_token(str* _s, str* _r)
00106 {
00107    int i;
00108 
00109         /* Save the begining of the
00110          * token in _r->s
00111          */
00112    _r->s = _s->s;
00113 
00114         /* Iterate through the
00115          * token body
00116          */
00117    for(i = 0; i < _s->len; i++) {
00118 
00119            /* All these characters
00120             * mark end of the token
00121             */
00122       switch(_s->s[i]) {
00123       case ' ':
00124       case '\t':
00125       case '\r':
00126       case '\n':
00127       case ',':
00128               /* So if you find
00129                * any of them
00130                * stop iterating
00131                */
00132          goto out;
00133       }
00134    }
00135  out:
00136         /* Empty token is error */
00137    if (i == 0) {
00138            return -2;
00139    }
00140 
00141         /* Save length of the token */
00142         _r->len = i;
00143 
00144         /* Update _s parameter so it points
00145          * right behind the end of the token
00146          */
00147    _s->s = _s->s + i;
00148    _s->len -= i;
00149 
00150         /* Everything went OK */
00151    return 0;
00152 }
00153 
00154 
00155 /*
00156  * Parse a digest parameter
00157  */
00158 static inline int parse_digest_param(str* _s, dig_cred_t* _c)
00159 {
00160    dig_par_t t;
00161    str* ptr;
00162    str dummy;
00163 
00164         /* Get type of the parameter */
00165    if (parse_param_name(_s, &t) < 0) {
00166       return -1;
00167    }
00168 
00169    _s->s++;  /* skip = */
00170    _s->len--;
00171 
00172         /* Find the begining of body */
00173    trim_leading(_s);
00174 
00175    if (_s->len == 0) {
00176       return -2;
00177    }
00178 
00179         /* Decide in which attribute the
00180          * body content will be stored
00181          */
00182    switch(t) {
00183    case PAR_USERNAME:  ptr = &_c->username.whole;  break;
00184    case PAR_REALM:     ptr = &_c->realm;           break;
00185    case PAR_NONCE:     ptr = &_c->nonce;           break;
00186    case PAR_URI:       ptr = &_c->uri;             break;
00187    case PAR_RESPONSE:  ptr = &_c->response;        break;
00188    case PAR_CNONCE:    ptr = &_c->cnonce;          break;
00189    case PAR_OPAQUE:    ptr = &_c->opaque;          break;
00190    case PAR_QOP:       ptr = &_c->qop.qop_str;     break;
00191    case PAR_NC:        ptr = &_c->nc;              break;
00192    case PAR_ALGORITHM: ptr = &_c->alg.alg_str;     break;
00193    case PAR_OTHER:     ptr = &dummy;               break;
00194    default:            ptr = &dummy;               break;
00195    }
00196 
00197         /* If the first character is quote, it is
00198          * a quoted string, otherwise it is a token
00199          */
00200    if (_s->s[0] == '\"') {
00201       if (parse_quoted(_s, ptr) < 0) {
00202          return -3;
00203       }
00204    } else {
00205       if (parse_token(_s, ptr) < 0) {
00206          return -4;
00207       }
00208    }
00209    
00210    return 0;
00211 }
00212 
00213 
00214 /*
00215  * Parse qop parameter body
00216  */
00217 static inline void parse_qop(struct qp* _q)
00218 {
00219    str s;
00220 
00221    s.s = _q->qop_str.s;
00222    s.len = _q->qop_str.len;
00223 
00224    trim(&s);
00225 
00226    if ((s.len == QOP_AUTH_STR_LEN) &&
00227        !strncasecmp(s.s, QOP_AUTH_STR, QOP_AUTH_STR_LEN)) {
00228       _q->qop_parsed = QOP_AUTH;
00229    } else if ((s.len == QOP_AUTHINT_STR_LEN) &&
00230          !strncasecmp(s.s, QOP_AUTHINT_STR, QOP_AUTHINT_STR_LEN)) {
00231       _q->qop_parsed = QOP_AUTHINT;
00232    } else {
00233       _q->qop_parsed = QOP_OTHER;
00234    }
00235 }
00236 
00237 
00238 /*
00239  * Parse algorithm parameter body
00240  */
00241 static inline void parse_algorithm(struct algorithm* _a)
00242 {
00243    str s;
00244 
00245    s.s = _a->alg_str.s;
00246    s.len = _a->alg_str.len;
00247 
00248    trim(&s);
00249 
00250    if ((s.len == ALG_MD5_STR_LEN) &&
00251        !strncasecmp(s.s, ALG_MD5_STR, ALG_MD5_STR_LEN)) {
00252       _a->alg_parsed = ALG_MD5;
00253    } else if ((s.len == ALG_MD5SESS_STR_LEN) &&
00254          !strncasecmp(s.s, ALG_MD5SESS_STR, ALG_MD5SESS_STR_LEN)) {
00255       _a->alg_parsed = ALG_MD5SESS;
00256    } else {
00257       _a->alg_parsed = ALG_OTHER;
00258    }  
00259 }
00260 
00261 
00262 /*
00263  * Parse username for user and domain parts
00264  */
00265 static inline void parse_username(struct username* _u)
00266 {
00267    char* d;
00268 
00269    _u->user = _u->whole;
00270    if (_u->whole.len <= 2) return;
00271 
00272    d = memchr(_u->whole.s, '@', _u->whole.len);
00273 
00274    if (d) {
00275       _u->domain.s = d + 1;
00276       _u->domain.len = _u->whole.len - (d - _u->whole.s) - 1;
00277       _u->user.len = d - _u->user.s;
00278    }
00279 }
00280 
00281 
00282 /*
00283  * Parse Digest credentials parameter, one by one
00284  */
00285 static inline int parse_digest_params(str* _s, dig_cred_t* _c)
00286 {
00287    char* comma;
00288 
00289    do {
00290            /* Parse the first parameter */
00291       if (parse_digest_param(_s, _c) < 0) {
00292          return -1;
00293       }
00294       
00295            /* Try to find the next parameter */
00296       comma = memchr(_s->s, ',', _s->len);
00297       if (comma) {
00298               /* Yes, there is another, 
00299                * remove any leading white-spaces
00300                * and let _s point to the next
00301                * parameter name
00302                */
00303          _s->len -= comma - _s->s + 1;
00304          _s->s = comma + 1;
00305          trim_leading(_s);
00306       }
00307    } while(comma); /* Repeat while there are next parameters */
00308 
00309         /* Parse QOP body if the parameter was present */
00310    if (_c->qop.qop_str.s != 0) {
00311       parse_qop(&_c->qop);
00312    }
00313 
00314         /* Parse algorithm body if the parameter was present */
00315    if (_c->alg.alg_str.s != 0) {
00316       parse_algorithm(&_c->alg);
00317    }
00318 
00319    if (_c->username.whole.s != 0) {
00320       parse_username(&_c->username);
00321    }
00322 
00323    return 0;
00324 }
00325 
00326 
00327 /*
00328  * We support Digest authentication only
00329  *
00330  * Returns:
00331  *  0 - if everything is OK
00332  * -1 - Error while parsing
00333  *  1 - Unknown scheme
00334  */
00335 int parse_digest_cred(str* _s, dig_cred_t* _c)
00336 {
00337    str tmp;
00338 
00339         /* Make a temporary copy, we are
00340          * going to modify it 
00341          */
00342    tmp.s = _s->s;
00343    tmp.len = _s->len;
00344 
00345         /* Remove any leading spaces, tabs, \r and \n */
00346    trim_leading(&tmp);
00347 
00348         /* Check the string length */
00349    if (tmp.len < (DIG_LEN + 1)) return 1; /* Too short, unknown scheme */
00350 
00351         /* Now test, if it is digest scheme, since it is the only
00352          * scheme we are able to parse here
00353          */
00354    if (!strncasecmp(tmp.s, DIGEST_SCHEME, DIG_LEN) &&
00355        ((tmp.s[DIG_LEN] == ' ') ||     /* Test for one of LWS chars */
00356         (tmp.s[DIG_LEN] == '\r') || 
00357         (tmp.s[DIG_LEN] == 'n') || 
00358         (tmp.s[DIG_LEN] == '\t') ||
00359         (tmp.s[DIG_LEN] == ','))) {
00360            /* Scheme is Digest */
00361       tmp.s += DIG_LEN + 1;
00362       tmp.len -= DIG_LEN + 1;
00363       
00364            /* Again, skip all white-spaces */
00365       trim_leading(&tmp);
00366 
00367            /* And parse digest parameters */
00368       if (parse_digest_params(&tmp, _c) < 0) {
00369          return -2; /* We must not return -1 in this function ! */
00370       } else {
00371          return 0;
00372       }
00373    } else {
00374       return 1; /* Unknown scheme */
00375    }
00376 }
00377 
00378 
00379 /*
00380  * Initialize a digest credentials structure
00381  */
00382 void init_dig_cred(dig_cred_t* _c)
00383 {
00384    memset(_c, 0, sizeof(dig_cred_t));
00385 }
00386 

Generated on Tue May 22 16:00:26 2012 for Kamailio - The Open Source SIP Server by  doxygen 1.5.6