8fac1055a50d781f57b182ea326cf24de05cd3f5
[sip-router] / parser / digest / digest_parser.c
1 /*
2  * $Id$
3  *
4  * Digest credentials parser
5  */
6
7
8 #include "digest_parser.h"
9 #include "../../trim.h"    /* trim_leading */
10 #include <string.h>        /* strncasecmp */
11 #include "param_parser.h"  /* Digest parameter name parser */
12 #include "../../ut.h"      /* q_memchr */
13
14
15 #define DIGEST_SCHEME "digest"
16 #define DIG_LEN 6
17
18 #define QOP_AUTH_STR "auth"
19 #define QOP_AUTH_STR_LEN 4
20
21 #define QOP_AUTHINT_STR "auth-int"
22 #define QOP_AUTHINT_STR_LEN 8
23
24 #define ALG_MD5_STR "MD5"
25 #define ALG_MD5_STR_LEN 3
26
27 #define ALG_MD5SESS_STR "MD5-sess"
28 #define ALG_MD5SESS_STR_LEN 8
29
30
31 /*
32  * Parse quoted string in a parameter body
33  * return the string without quotes in _r
34  * parameter and update _s to point behind the
35  * closing quote
36  */
37 static inline int parse_quoted(str* _s, str* _r)
38 {
39         char* end_quote;
40
41              /* The string must have at least
42               * surrounding quotes
43               */
44         if (_s->len < 2) {
45                 return -1;
46         }
47
48              /* Skip opening quote */
49         _s->s++;
50         _s->len--;
51
52
53              /* Find closing quote */
54         end_quote = q_memchr(_s->s, '\"', _s->len);
55
56              /* Not found, return error */
57         if (!end_quote) {
58                 return -2;
59         }
60
61              /* Let _r point to the string without
62               * surrounding quotes
63               */
64         _r->s = _s->s;
65         _r->len = end_quote - _s->s;
66
67              /* Update _s parameter to point
68               * behind the closing quote
69               */
70         _s->len -= (end_quote - _s->s + 1);
71         _s->s = end_quote + 1;
72
73              /* Everything went OK */
74         return 0;
75 }
76
77
78 /*
79  * Parse unquoted token in a parameter body
80  * let _r point to the token and update _s
81  * to point right behind the token
82  */
83 static inline int parse_token(str* _s, str* _r)
84 {
85         int i;
86
87              /* Save the begining of the
88               * token in _r->s
89               */
90         _r->s = _s->s;
91
92              /* Iterate throught the
93               * token body
94               */
95         for(i = 0; i < _s->len; i++) {
96
97                      /* All these characters
98                       * mark end of the token
99                       */
100                 switch(_s->s[i]) {
101                 case ' ':
102                 case '\t':
103                 case '\r':
104                 case '\n':
105                 case ',':
106                              /* So if you find
107                               * any of them
108                               * stop iterating
109                               */
110                         goto out;
111                 }
112         }
113  out:
114              /* Empty token is error */
115         if (i == 0) {
116                 return -2;
117         }
118
119              /* Save length of the token */
120         _r->len = i;
121
122              /* Update _s parameter so it points
123               * right behind the end of the token
124               */
125         _s->s = _s->s + i;
126         _s->len -= i;
127
128              /* Everything went OK */
129         return 0;
130 }
131
132
133 /*
134  * Parse a digest parameter
135  */
136 static inline int parse_digest_param(str* _s, dig_cred_t* _c)
137 {
138         dig_par_t t;
139         str* ptr;
140         str dummy;
141
142              /* Get type of the parameter */
143         if (parse_param_name(_s, &t) < 0) {
144                 return -1;
145         }
146
147         _s->s++;  /* skip = */
148         _s->len--;
149
150              /* Find the begining of body */
151         trim_leading(_s);
152
153         if (_s->len == 0) {
154                 return -2;
155         }
156
157              /* Decide in which attribute the
158               * body content will be stored
159               */
160         switch(t) {
161         case PAR_USERNAME:  ptr = &(_c->username);    break;
162         case PAR_REALM:     ptr = &(_c->realm);       break;
163         case PAR_NONCE:     ptr = &(_c->nonce);       break;
164         case PAR_URI:       ptr = &(_c->uri);         break;
165         case PAR_RESPONSE:  ptr = &(_c->response);    break;
166         case PAR_CNONCE:    ptr = &(_c->cnonce);      break;
167         case PAR_OPAQUE:    ptr = &(_c->opaque);      break;
168         case PAR_QOP:       ptr = &(_c->qop.qop_str); break;
169         case PAR_NC:        ptr = &(_c->nc);          break;
170         case PAR_ALGORITHM: ptr = &(_c->alg.alg_str); break;
171         case PAR_OTHER:     ptr = &dummy;             break;
172         default:            ptr = &dummy;             break;
173         }
174
175              /* If the first character is qoute, it is
176               * a quoted string, otherwise it is a token
177               */
178         if (_s->s[0] == '\"') {
179                 if (parse_quoted(_s, ptr) < 0) {
180                         return -3;
181                 }
182         } else {
183                 if (parse_token(_s, ptr) < 0) {
184                         return -4;
185                 }
186         }
187         
188         return 0;
189 }
190
191
192 /*
193  * Parse qop parameter body
194  */
195 static inline void parse_qop(struct qp* _q)
196 {
197         str s;
198
199         s.s = _q->qop_str.s;
200         s.len = _q->qop_str.len;
201
202         trim(&s);
203
204         if ((s.len == QOP_AUTH_STR_LEN) &&
205             !strncasecmp(s.s, QOP_AUTH_STR, QOP_AUTH_STR_LEN)) {
206                 _q->qop_parsed = QOP_AUTH;
207         } else if ((s.len == QOP_AUTHINT_STR_LEN) &&
208                    !strncasecmp(s.s, QOP_AUTHINT_STR, QOP_AUTHINT_STR_LEN)) {
209                 _q->qop_parsed = QOP_AUTHINT;
210         } else {
211                 _q->qop_parsed = QOP_OTHER;
212         }
213 }
214
215
216 /*
217  * Parse algorithm parameter body
218  */
219 static inline void parse_algorithm(struct algorithm* _a)
220 {
221         str s;
222
223         s.s = _a->alg_str.s;
224         s.len = _a->alg_str.len;
225
226         trim(&s);
227
228         if ((s.len == ALG_MD5_STR_LEN) &&
229             !strncasecmp(s.s, ALG_MD5_STR, ALG_MD5_STR_LEN)) {
230                 _a->alg_parsed = ALG_MD5;
231         } else if ((s.len == ALG_MD5SESS_STR_LEN) &&
232                    !strncasecmp(s.s, ALG_MD5SESS_STR, ALG_MD5SESS_STR_LEN)) {
233                 _a->alg_parsed = ALG_MD5SESS;
234         } else {
235                 _a->alg_parsed = ALG_OTHER;
236         }       
237 }
238
239
240 /*
241  * Parse Digest credentials parameter, one by one
242  */
243 static inline int parse_digest_params(str* _s, dig_cred_t* _c)
244 {
245         char* comma;
246
247         do {
248                      /* Parse the first parameter */
249                 if (parse_digest_param(_s, _c) < 0) {
250                         return -1;
251                 }
252                 
253                      /* Try to find the next parameter */
254                 comma = q_memchr(_s->s, ',', _s->len);
255                 if (comma) {
256                              /* Yes, there is another, 
257                               * remove any leading whitespaces
258                               * and let _s point to the next
259                               * parameter name
260                               */
261                         _s->len -= comma - _s->s + 1;
262                         _s->s = comma + 1;
263                         trim_leading(_s);
264                 }
265         } while(comma); /* Repeat while there are next parameters */
266
267              /* Parse QOP body if the parameter was present */
268         if (_c->qop.qop_str.s != 0) {
269                 parse_qop(&(_c->qop));
270         }
271
272              /* Parse algorithm body if the parameter was present */
273         if (_c->alg.alg_str.s != 0) {
274                 parse_algorithm(&(_c->alg));
275         }
276
277         return 0;
278 }
279
280
281 /*
282  * We support Digest authentication only
283  *
284  * Returns:
285  *  0 - if everything is OK
286  * -1 - Error while parsing
287  *  1 - Unknown scheme
288  */
289 int parse_digest_cred(str* _s, dig_cred_t* _c)
290 {
291         str tmp;
292
293              /* Make a temporary copy, we are
294               * going to modify it 
295               */
296         tmp.s = _s->s;
297         tmp.len = _s->len;
298
299              /* Remove any leading spaces, tabs, \r and \n */
300         trim_leading(&tmp);
301
302              /* Check the string length */
303         if (tmp.len < (DIG_LEN + 1)) return 1; /* Too short, unknown scheme */
304
305              /* Now test, if it is digest scheme, since it is the only
306               * scheme we are able to parse here
307               */
308         if (!strncasecmp(tmp.s, DIGEST_SCHEME, DIG_LEN) &&
309             ((tmp.s[DIG_LEN] == ' ') ||     /* Test for one of LWS chars */
310              (tmp.s[DIG_LEN] == '\r') || 
311              (tmp.s[DIG_LEN] == 'n') || 
312              (tmp.s[DIG_LEN] == '\t') ||
313              (tmp.s[DIG_LEN] == ','))) {
314                      /* Scheme is Digest */
315                 tmp.s += DIG_LEN + 1;
316                 tmp.len -= DIG_LEN + 1;
317                 
318                      /* Again, skip all whitespaces */
319                 trim_leading(&tmp);
320
321                      /* And parse digest parameters */
322                 if (parse_digest_params(&tmp, _c) < 0) {
323                         return -1;
324                 } else {
325                         return 0;
326                 }
327         } else {
328                 return 1; /* Unknown scheme */
329         }
330 }
331
332
333 /*
334  * Initialize a digest credentials structure
335  */
336 void init_dig_cred(dig_cred_t* _c)
337 {
338         memset(_c, 0, sizeof(dig_cred_t));
339 }
340
341
342 /*
343  * Initialize digest_parser
344  */
345  
346 void init_digest_parser(void)
347 {
348         init_digest_htable();
349 }