/src/opensips/parser/parse_authenticate.c
Line | Count | Source (jump to first uncovered line) |
1 | | /* |
2 | | * Copyright (C) 2011 VoIP Embedded Inc. <http://www.voipembedded.com/> |
3 | | * |
4 | | * |
5 | | * This file is part of opensips, a free SIP server. |
6 | | * |
7 | | * opensips is free software; you can redistribute it and/or modify |
8 | | * it under the terms of the GNU General Public License as published by |
9 | | * the Free Software Foundation; either version 2 of the License, or |
10 | | * (at your option) any later version |
11 | | * |
12 | | * opensips is distributed in the hope that it will be useful, |
13 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
14 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
15 | | * GNU General Public License for more details. |
16 | | * |
17 | | * You should have received a copy of the GNU General Public License |
18 | | * along with this program; if not, write to the Free Software |
19 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
20 | | * |
21 | | * History: |
22 | | * -------- |
23 | | * 2005-01-31 first version (ramona) |
24 | | * 2011-03-07 Initial revision (Ovidiu Sas) |
25 | | */ |
26 | | |
27 | | #include <stdlib.h> |
28 | | #include <string.h> |
29 | | #include "../dprint.h" |
30 | | #include "../ut.h" |
31 | | #include "../lib/turbocompare.h" |
32 | | #include "../mem/mem.h" |
33 | | #include "msg_parser.h" |
34 | | #include "parse_authenticate.h" |
35 | | |
36 | | |
37 | 0 | #define AUTHENTICATE_DIGEST_S "Digest" |
38 | 0 | #define AUTHENTICATE_DIGEST_LEN (sizeof(AUTHENTICATE_DIGEST_S)-1) |
39 | | |
40 | | #define LOWER1B(_n) \ |
41 | 0 | ((_n < 'A' ||_n > 'Z') ? _n : _n |0x20) |
42 | | #define LOWER4B(_n) \ |
43 | 0 | ((_n)|TURBO_LCMASK((unsigned int)_n)) |
44 | | #define GET4B(_p) \ |
45 | | ((*(_p)<<24) + (*(_p+1)<<16) + (*(_p+2)<<8) + *(_p+3)) |
46 | | |
47 | | #define CASE_5B(_hex4,_c5, _new_state, _quoted) \ |
48 | 0 | case _hex4: \ |
49 | 0 | if (body.len > 5 && LOWER1B(*(body.s+4))==_c5 ) \ |
50 | 0 | { \ |
51 | 0 | STR_ADVANCE_BY(&body, 5); \ |
52 | 0 | state = _new_state; \ |
53 | 0 | quoted_val = _quoted; \ |
54 | 0 | } else { \ |
55 | 0 | STR_ADVANCE_BY(&body, 4); \ |
56 | 0 | } \ |
57 | 0 | break; |
58 | | |
59 | | #define CASE_6B(_hex4,_c5,_c6, _new_state, _quoted) \ |
60 | 0 | case _hex4: \ |
61 | 0 | if (body.len > 6 && LOWER1B(*(body.s+4))==_c5 && LOWER1B(*(body.s+5))==_c6) \ |
62 | 0 | { \ |
63 | 0 | STR_ADVANCE_BY(&body, 6); \ |
64 | 0 | state = _new_state; \ |
65 | 0 | quoted_val = _quoted; \ |
66 | 0 | } else { \ |
67 | 0 | STR_ADVANCE_BY(&body, 4); \ |
68 | 0 | } \ |
69 | 0 | break; |
70 | | |
71 | 0 | #define OTHER_STATE 0 |
72 | 0 | #define QOP_STATE 1 |
73 | 0 | #define REALM_STATE 2 |
74 | 0 | #define NONCE_STATE 3 |
75 | 0 | #define STALE_STATE 4 |
76 | 0 | #define DOMAIN_STATE 5 |
77 | 0 | #define OPAQUE_STATE 6 |
78 | 0 | #define ALGORITHM_STATE 7 |
79 | 0 | #define IK_STATE 8 |
80 | 0 | #define CK_STATE 9 |
81 | | |
82 | 0 | #define TRB_SCASEMATCH(cp, S) (turbo_casematch(cp, (S), (sizeof(S) - 1))) |
83 | 0 | #define TRB_STRCASEMATCH(sarg, S) (turbo_strcasematch(sarg, (S), (sizeof(S) - 1))) |
84 | | #define TRB_STRCASESTARTS(sarg, S) ((sarg)->len >= (sizeof(S) - 1) && \ |
85 | | turbo_casematch((sarg)->s, (S), (sizeof(S) - 1))) |
86 | | |
87 | 0 | #define STR_ADVANCE_BY(sptr, incr) {int _t = (incr); (sptr)->s += _t; (sptr)->len -= _t;} |
88 | 0 | #define STR_ADVANCE(sptr) STR_ADVANCE_BY(sptr, 1) |
89 | 0 | #define STR_ADVANCE_IF_STARTS(sarg, S) (str_advance_if_starts((sarg), (S), (sizeof(S) - 1))) |
90 | | |
91 | | static int str_advance_if_starts(str *val, const char *sval, size_t slen) |
92 | 0 | { |
93 | 0 | if (val->len < slen || !turbo_casematch(val->s, sval, slen)) |
94 | 0 | return 0; |
95 | 0 | STR_ADVANCE_BY(val, slen); |
96 | 0 | return 1; |
97 | 0 | } |
98 | | |
99 | | int parse_qop_value(str val, struct authenticate_body *auth) |
100 | 0 | { |
101 | | |
102 | | /* parse first token */ |
103 | 0 | if (!STR_ADVANCE_IF_STARTS(&val, "auth")) |
104 | 0 | return -1; |
105 | 0 | if (val.len == 0) { |
106 | 0 | auth->flags |= QOP_AUTH; |
107 | 0 | return 0; |
108 | 0 | } |
109 | 0 | switch (*val.s) { |
110 | 0 | case ' ': |
111 | 0 | case '\t': |
112 | 0 | STR_ADVANCE(&val); |
113 | 0 | auth->flags |= QOP_AUTH; |
114 | 0 | break; |
115 | 0 | case '-': |
116 | 0 | STR_ADVANCE(&val); |
117 | 0 | if (STR_ADVANCE_IF_STARTS(&val, "int")) { |
118 | 0 | auth->flags |= QOP_AUTH_INT; |
119 | 0 | } else |
120 | 0 | return -1; |
121 | 0 | break; |
122 | 0 | case ',': |
123 | 0 | auth->flags |= QOP_AUTH; |
124 | 0 | goto postcomma; |
125 | 0 | default: |
126 | 0 | return -1; |
127 | 0 | } |
128 | | |
129 | 0 | if (val.len == 0) |
130 | 0 | return 0; |
131 | | |
132 | 0 | trim_leading(&val); |
133 | |
|
134 | 0 | if (val.len == 0) |
135 | 0 | return 0; |
136 | 0 | if (*val.s != ',') |
137 | 0 | return -1; |
138 | 0 | postcomma: |
139 | 0 | STR_ADVANCE(&val); |
140 | 0 | trim_leading(&val); |
141 | | |
142 | | /* parse second token */ |
143 | 0 | if (!STR_ADVANCE_IF_STARTS(&val, "auth")) |
144 | 0 | return -1; |
145 | 0 | if (val.len == 0) { |
146 | 0 | auth->flags |= QOP_AUTH; |
147 | 0 | return 0; |
148 | 0 | } |
149 | 0 | if (TRB_STRCASEMATCH(&val, "-int")) { |
150 | 0 | auth->flags |= QOP_AUTH_INT; |
151 | 0 | return 0; |
152 | 0 | } else |
153 | 0 | return -1; |
154 | 0 | } |
155 | | |
156 | | int parse_authenticate_body( str body, struct authenticate_body *auth) |
157 | 0 | { |
158 | 0 | int n, ret = 0; |
159 | 0 | int state; |
160 | 0 | str name; |
161 | 0 | str val; |
162 | 0 | int quoted_val; |
163 | |
|
164 | 0 | if (body.len == 0) |
165 | 0 | { |
166 | 0 | LM_ERR("empty body\n"); |
167 | 0 | goto error; |
168 | 0 | } |
169 | | |
170 | 0 | memset( auth, 0, sizeof(struct authenticate_body)); |
171 | | |
172 | | /* parse the "digest" */ |
173 | 0 | trim_leading(&body); |
174 | 0 | if (body.len <= AUTHENTICATE_DIGEST_LEN) |
175 | 0 | goto parse_error; |
176 | 0 | if (!TRB_SCASEMATCH(body.s, "digest")) |
177 | 0 | goto parse_error; |
178 | 0 | STR_ADVANCE_BY(&body, AUTHENTICATE_DIGEST_LEN); |
179 | 0 | if (!is_ws(*body.s)) |
180 | 0 | goto parse_error; |
181 | 0 | STR_ADVANCE(&body); |
182 | 0 | trim_leading(&body); |
183 | 0 | if (body.len == 0) |
184 | 0 | goto parse_error; |
185 | | |
186 | 0 | while (body.len > 0) |
187 | 0 | { |
188 | 0 | state = OTHER_STATE; |
189 | 0 | quoted_val = 0; |
190 | | /* get name */ |
191 | 0 | name.s = body.s; |
192 | 0 | if (body.len > 4) |
193 | 0 | { |
194 | 0 | n = LOWER4B( GET4B(body.s) ); |
195 | 0 | switch(n) |
196 | 0 | { |
197 | 0 | CASE_5B( 0x7265616c, 'm', REALM_STATE, 1); /*realm*/ |
198 | 0 | CASE_5B( 0x6e6f6e63, 'e', NONCE_STATE, 1); /*nonce*/ |
199 | 0 | CASE_5B( 0x7374616c, 'e', STALE_STATE, 0); /*stale*/ |
200 | 0 | CASE_6B( 0x646f6d62, 'i', 'n', DOMAIN_STATE, 1); /*domain*/ |
201 | 0 | CASE_6B( 0x6f706171, 'u', 'e', OPAQUE_STATE, 1); /*opaque*/ |
202 | 0 | case 0x616c676f: /*algo*/ |
203 | 0 | if (body.len > 9 && TRB_SCASEMATCH(body.s+4, "rithm")) |
204 | 0 | { |
205 | 0 | STR_ADVANCE_BY(&body, 9); |
206 | 0 | state = ALGORITHM_STATE; |
207 | 0 | } else { |
208 | 0 | STR_ADVANCE_BY(&body, 4); |
209 | 0 | } |
210 | 0 | break; |
211 | 0 | default: |
212 | 0 | if ((n|0xff)==0x716f70ff) /*qop*/ |
213 | 0 | { |
214 | 0 | state = QOP_STATE; |
215 | 0 | STR_ADVANCE_BY(&body, 3); |
216 | 0 | } else if ((n|0xffff) == 0x696bffff) { /*ik*/ |
217 | 0 | state = IK_STATE; |
218 | 0 | STR_ADVANCE_BY(&body, 2); |
219 | 0 | } else if ((n|0xffff) == 0x636bffff) { /*ck*/ |
220 | 0 | state = CK_STATE; |
221 | 0 | STR_ADVANCE_BY(&body, 2); |
222 | 0 | } |
223 | 0 | } |
224 | 0 | } else if (body.len > 2) { |
225 | 0 | if (body.len > 3) { |
226 | 0 | if (TRB_SCASEMATCH(body.s, "qop")) |
227 | 0 | { |
228 | 0 | STR_ADVANCE_BY(&body, 3); |
229 | 0 | state = QOP_STATE; |
230 | 0 | } |
231 | 0 | } else if (TRB_SCASEMATCH(body.s, "ik")) |
232 | 0 | { |
233 | 0 | STR_ADVANCE_BY(&body, 2); |
234 | 0 | state = IK_STATE; |
235 | 0 | } else if (TRB_SCASEMATCH(body.s, "ck")) |
236 | 0 | { |
237 | 0 | STR_ADVANCE_BY(&body, 2); |
238 | 0 | state = CK_STATE; |
239 | 0 | } |
240 | 0 | } |
241 | | |
242 | | /* parse to the "=" */ |
243 | 0 | for(n=0 ; body.len > 0 && !is_ws(*body.s) && *body.s != '=' ; n++) |
244 | 0 | STR_ADVANCE(&body); |
245 | 0 | if (body.len == 0) |
246 | 0 | goto parse_error; |
247 | 0 | if (n!=0) |
248 | 0 | state = OTHER_STATE; |
249 | 0 | name.len = body.s - name.s; |
250 | | /* get the '=' */ |
251 | 0 | trim_leading(&body); |
252 | 0 | if (body.len == 0 || *body.s != '=') |
253 | 0 | goto parse_error; |
254 | 0 | STR_ADVANCE(&body); |
255 | | /* get the value (quoted or not) */ |
256 | 0 | trim_leading(&body); |
257 | 0 | if (body.len <= 1 || (quoted_val && *body.s != '\"')) |
258 | 0 | goto parse_error; |
259 | 0 | if (!quoted_val && *body.s == '\"') |
260 | 0 | quoted_val = 1; |
261 | 0 | if (quoted_val) |
262 | 0 | { |
263 | 0 | STR_ADVANCE(&body); |
264 | 0 | char *cp = memchr(body.s, '\"', body.len); |
265 | 0 | if (cp == NULL) |
266 | 0 | goto error; |
267 | 0 | val.s = body.s; |
268 | 0 | STR_ADVANCE_BY(&body, cp - body.s); |
269 | 0 | } else { |
270 | 0 | val.s = body.s; |
271 | 0 | while (body.len > 0 && !is_ws(*body.s) && *body.s != ',') |
272 | 0 | STR_ADVANCE(&body); |
273 | 0 | } |
274 | 0 | val.len = body.s - val.s; |
275 | 0 | if (val.len==0) |
276 | 0 | val.s = 0; |
277 | | /* consume the closing '"' if quoted */ |
278 | 0 | STR_ADVANCE_BY(&body, quoted_val); |
279 | 0 | trim_leading(&body); |
280 | 0 | if (body.len > 0 && *body.s == ',') |
281 | 0 | { |
282 | 0 | STR_ADVANCE(&body); |
283 | 0 | trim_leading(&body); |
284 | 0 | } |
285 | |
|
286 | 0 | LM_DBG("<%.*s>=\"%.*s\" state=%d\n", |
287 | 0 | name.len,name.s,val.len,val.s,state); |
288 | | |
289 | | /* process the AVP */ |
290 | 0 | switch (state) |
291 | 0 | { |
292 | 0 | case QOP_STATE: |
293 | 0 | auth->qop = val; |
294 | 0 | if (parse_qop_value(val, auth) < 0) |
295 | 0 | LM_DBG("Unknown token in qop value '%.*s'\n", |
296 | 0 | val.len, val.s); |
297 | 0 | break; |
298 | 0 | case REALM_STATE: |
299 | 0 | auth->realm = val; |
300 | 0 | break; |
301 | 0 | case NONCE_STATE: |
302 | 0 | auth->nonce = val; |
303 | 0 | break; |
304 | 0 | case DOMAIN_STATE: |
305 | 0 | auth->domain = val; |
306 | 0 | break; |
307 | 0 | case OPAQUE_STATE: |
308 | 0 | auth->opaque = val; |
309 | 0 | break; |
310 | 0 | case IK_STATE: |
311 | 0 | auth->ik = val; |
312 | 0 | break; |
313 | 0 | case CK_STATE: |
314 | 0 | auth->ck = val; |
315 | 0 | break; |
316 | 0 | case ALGORITHM_STATE: |
317 | 0 | auth->algorithm = parse_digest_algorithm(&val); |
318 | 0 | if (auth->algorithm == ALG_OTHER) { |
319 | 0 | LM_INFO("bad algorithm \"%.*s\"\n", val.len, val.s); |
320 | 0 | goto error; |
321 | 0 | } |
322 | 0 | break; |
323 | 0 | case STALE_STATE: |
324 | 0 | if (TRB_STRCASEMATCH(&val, "true")) |
325 | 0 | { |
326 | 0 | auth->flags |= AUTHENTICATE_STALE; |
327 | 0 | } else if (!(TRB_STRCASEMATCH(&val, "false"))) |
328 | 0 | { |
329 | 0 | LM_ERR("unsupported stale value \"%.*s\"\n",val.len,val.s); |
330 | 0 | goto error; |
331 | 0 | } |
332 | 0 | break; |
333 | 0 | default: |
334 | 0 | break; |
335 | 0 | } |
336 | 0 | } |
337 | | |
338 | | /* some checkings */ |
339 | 0 | if (auth->nonce.s==0 || auth->realm.s==0) |
340 | 0 | { |
341 | 0 | LM_ERR("realm or nonce missing\n"); |
342 | 0 | goto error; |
343 | 0 | } |
344 | | |
345 | 0 | return ret; |
346 | 0 | parse_error: |
347 | 0 | LM_ERR("parse error in <%.*s> around %ld\n", body.len, body.s, (long)(body.len)); |
348 | 0 | error: |
349 | 0 | return -1; |
350 | 0 | } |
351 | | |
352 | | |
353 | | int parse_authenticate_header(struct hdr_field *authenticate, |
354 | | const struct match_auth_hf_desc *md, struct authenticate_body **picked_auth) |
355 | 0 | { |
356 | 0 | void **parsed; |
357 | 0 | struct authenticate_body *auth_body, *ret_auth; |
358 | 0 | int rc, prev_parsed; |
359 | |
|
360 | 0 | parsed = &(authenticate->parsed); |
361 | 0 | prev_parsed = (*parsed != NULL); |
362 | 0 | ret_auth = NULL; |
363 | |
|
364 | 0 | while(*parsed == NULL) |
365 | 0 | { |
366 | 0 | auth_body = pkg_malloc(sizeof(struct authenticate_body)); |
367 | 0 | if (auth_body == NULL) |
368 | 0 | { |
369 | 0 | LM_ERR("oom\n"); |
370 | 0 | *picked_auth = ret_auth; |
371 | 0 | return -1; |
372 | 0 | } |
373 | | |
374 | 0 | rc = parse_authenticate_body(authenticate->body, auth_body); |
375 | 0 | if (rc < 0) { |
376 | 0 | pkg_free(auth_body); |
377 | 0 | *picked_auth = ret_auth; |
378 | 0 | return -1; |
379 | 0 | } |
380 | | |
381 | 0 | if (rc == 0 && !ret_auth && |
382 | 0 | (md == NULL || md->matchf(auth_body, md))) |
383 | 0 | ret_auth = auth_body; |
384 | |
|
385 | 0 | *parsed = auth_body; |
386 | |
|
387 | 0 | authenticate = authenticate->sibling; |
388 | 0 | if (authenticate) |
389 | 0 | parsed = &(authenticate->parsed); |
390 | 0 | else |
391 | 0 | break; |
392 | 0 | } |
393 | 0 | if (prev_parsed) { |
394 | 0 | while (!ret_auth && authenticate) { |
395 | 0 | if (authenticate->parsed && |
396 | 0 | (md == NULL || md->matchf(authenticate->parsed, md))) |
397 | 0 | ret_auth = authenticate->parsed; |
398 | 0 | authenticate = authenticate->sibling; |
399 | 0 | } |
400 | 0 | } |
401 | 0 | *picked_auth = ret_auth; |
402 | |
|
403 | 0 | return ret_auth ? 0 : -1; |
404 | 0 | } |
405 | | |
406 | | /* |
407 | | * This method is used to parse WWW-Authenticate header. |
408 | | * |
409 | | * params: msg : sip msg |
410 | | * returns 0 on success, |
411 | | * -1 on failure. |
412 | | */ |
413 | | int parse_www_authenticate_header(struct sip_msg *msg, |
414 | | const struct match_auth_hf_desc *md, struct authenticate_body **picked_auth) |
415 | 0 | { |
416 | 0 | if ( !msg->www_authenticate && |
417 | 0 | (parse_headers(msg, HDR_WWW_AUTHENTICATE_F,0)==-1 || !msg->www_authenticate)) { |
418 | 0 | return -1; |
419 | 0 | } |
420 | | |
421 | 0 | return parse_authenticate_header(msg->www_authenticate, md, |
422 | 0 | picked_auth); |
423 | 0 | } |
424 | | |
425 | | |
426 | | /* |
427 | | * This method is used to parse Proxy-Authenticate header. |
428 | | * |
429 | | * params: msg : sip msg |
430 | | * returns 0 on success, |
431 | | * -1 on failure. |
432 | | */ |
433 | | int parse_proxy_authenticate_header(struct sip_msg *msg, |
434 | | const struct match_auth_hf_desc *md, struct authenticate_body **picked_auth) |
435 | 0 | { |
436 | 0 | if ( !msg->proxy_authenticate && |
437 | 0 | (parse_headers(msg, HDR_PROXY_AUTHENTICATE_F,0)==-1 || !msg->proxy_authenticate)) { |
438 | 0 | return -1; |
439 | 0 | } |
440 | | |
441 | 0 | return parse_authenticate_header(msg->proxy_authenticate, md, |
442 | 0 | picked_auth); |
443 | 0 | } |
444 | | |
445 | | |
446 | | void free_authenticate(struct authenticate_body *authenticate_b) |
447 | 0 | { |
448 | 0 | if (authenticate_b) { |
449 | 0 | pkg_free(authenticate_b); |
450 | 0 | } |
451 | |
|
452 | 0 | return; |
453 | 0 | } |