/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 | 8.95k | #define AUTHENTICATE_DIGEST_S "Digest" |
38 | 8.95k | #define AUTHENTICATE_DIGEST_LEN (sizeof(AUTHENTICATE_DIGEST_S)-1) |
39 | | |
40 | | #define LOWER1B(_n) \ |
41 | 228k | ((_n < 'A' ||_n > 'Z') ? _n : _n |0x20) |
42 | | #define LOWER4B(_n) \ |
43 | 389k | ((_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 | 27.3k | case _hex4: \ |
49 | 27.3k | if (body.len > 5 && LOWER1B(*(body.s+4))==_c5 ) \ |
50 | 27.3k | { \ |
51 | 24.9k | STR_ADVANCE_BY(&body, 5); \ |
52 | 24.9k | state = _new_state; \ |
53 | 24.9k | quoted_val = _quoted; \ |
54 | 24.9k | } else { \ |
55 | 2.38k | STR_ADVANCE_BY(&body, 4); \ |
56 | 2.38k | } \ |
57 | 27.3k | break; |
58 | | |
59 | | #define CASE_6B(_hex4,_c5,_c6, _new_state, _quoted) \ |
60 | 118k | case _hex4: \ |
61 | 118k | if (body.len > 6 && LOWER1B(*(body.s+4))==_c5 && LOWER1B(*(body.s+5))==_c6) \ |
62 | 118k | { \ |
63 | 4.54k | STR_ADVANCE_BY(&body, 6); \ |
64 | 4.54k | state = _new_state; \ |
65 | 4.54k | quoted_val = _quoted; \ |
66 | 114k | } else { \ |
67 | 114k | STR_ADVANCE_BY(&body, 4); \ |
68 | 114k | } \ |
69 | 118k | break; |
70 | | |
71 | 660k | #define OTHER_STATE 0 |
72 | 122k | #define QOP_STATE 1 |
73 | 11.6k | #define REALM_STATE 2 |
74 | 11.0k | #define NONCE_STATE 3 |
75 | 418 | #define STALE_STATE 4 |
76 | 3.56k | #define DOMAIN_STATE 5 |
77 | 220 | #define OPAQUE_STATE 6 |
78 | 10.0k | #define ALGORITHM_STATE 7 |
79 | 515 | #define IK_STATE 8 |
80 | 636 | #define CK_STATE 9 |
81 | | |
82 | 17.6k | #define TRB_SCASEMATCH(cp, S) (turbo_casematch(cp, (S), (sizeof(S) - 1))) |
83 | 3.37k | #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 | 24.4M | #define STR_ADVANCE_BY(sptr, incr) {int _t = (incr); (sptr)->s += _t; (sptr)->len -= _t;} |
88 | 23.3M | #define STR_ADVANCE(sptr) STR_ADVANCE_BY(sptr, 1) |
89 | 89.7k | #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 | 89.7k | { |
93 | 89.7k | if (val->len < slen || !turbo_casematch(val->s, sval, slen)) |
94 | 40.3k | return 0; |
95 | 49.4k | STR_ADVANCE_BY(val, slen); |
96 | 49.4k | return 1; |
97 | 89.7k | } |
98 | | |
99 | | int parse_qop_value(str val, struct authenticate_body *auth) |
100 | 60.6k | { |
101 | | |
102 | | /* parse first token */ |
103 | 60.6k | if (!STR_ADVANCE_IF_STARTS(&val, "auth")) |
104 | 24.2k | return -1; |
105 | 36.4k | if (val.len == 0) { |
106 | 1.02k | auth->flags |= QOP_AUTH; |
107 | 1.02k | return 0; |
108 | 1.02k | } |
109 | 35.4k | switch (*val.s) { |
110 | 953 | case ' ': |
111 | 15.2k | case '\t': |
112 | 15.2k | STR_ADVANCE(&val); |
113 | 15.2k | auth->flags |= QOP_AUTH; |
114 | 15.2k | break; |
115 | 11.0k | case '-': |
116 | 11.0k | STR_ADVANCE(&val); |
117 | 11.0k | if (STR_ADVANCE_IF_STARTS(&val, "int")) { |
118 | 9.92k | auth->flags |= QOP_AUTH_INT; |
119 | 9.92k | } else |
120 | 1.08k | return -1; |
121 | 9.92k | break; |
122 | 9.92k | case ',': |
123 | 3.72k | auth->flags |= QOP_AUTH; |
124 | 3.72k | goto postcomma; |
125 | 5.38k | default: |
126 | 5.38k | return -1; |
127 | 35.4k | } |
128 | | |
129 | 25.2k | if (val.len == 0) |
130 | 2.43k | return 0; |
131 | | |
132 | 22.7k | trim_leading(&val); |
133 | | |
134 | 22.7k | if (val.len == 0) |
135 | 260 | return 0; |
136 | 22.5k | if (*val.s != ',') |
137 | 8.17k | return -1; |
138 | 18.0k | postcomma: |
139 | 18.0k | STR_ADVANCE(&val); |
140 | 18.0k | trim_leading(&val); |
141 | | |
142 | | /* parse second token */ |
143 | 18.0k | if (!STR_ADVANCE_IF_STARTS(&val, "auth")) |
144 | 15.0k | return -1; |
145 | 3.06k | if (val.len == 0) { |
146 | 300 | auth->flags |= QOP_AUTH; |
147 | 300 | return 0; |
148 | 300 | } |
149 | 2.76k | if (TRB_STRCASEMATCH(&val, "-int")) { |
150 | 741 | auth->flags |= QOP_AUTH_INT; |
151 | 741 | return 0; |
152 | 741 | } else |
153 | 2.02k | return -1; |
154 | 2.76k | } |
155 | | |
156 | | int parse_authenticate_body( str body, struct authenticate_body *auth) |
157 | 8.97k | { |
158 | 8.97k | int n, ret = 0; |
159 | 8.97k | int state; |
160 | 8.97k | str name; |
161 | 8.97k | str val; |
162 | 8.97k | int quoted_val; |
163 | | |
164 | 8.97k | if (body.len == 0) |
165 | 15 | { |
166 | 15 | LM_ERR("empty body\n"); |
167 | 15 | goto error; |
168 | 15 | } |
169 | | |
170 | 8.95k | memset( auth, 0, sizeof(struct authenticate_body)); |
171 | | |
172 | | /* parse the "digest" */ |
173 | 8.95k | trim_leading(&body); |
174 | 8.95k | if (body.len <= AUTHENTICATE_DIGEST_LEN) |
175 | 72 | goto parse_error; |
176 | 8.88k | if (!TRB_SCASEMATCH(body.s, "digest")) |
177 | 316 | goto parse_error; |
178 | 8.56k | STR_ADVANCE_BY(&body, AUTHENTICATE_DIGEST_LEN); |
179 | 8.56k | if (!is_ws(*body.s)) |
180 | 17 | goto parse_error; |
181 | 8.55k | STR_ADVANCE(&body); |
182 | 8.55k | trim_leading(&body); |
183 | 8.55k | if (body.len == 0) |
184 | 38 | goto parse_error; |
185 | | |
186 | 394k | while (body.len > 0) |
187 | 391k | { |
188 | 391k | state = OTHER_STATE; |
189 | 391k | quoted_val = 0; |
190 | | /* get name */ |
191 | 391k | name.s = body.s; |
192 | 391k | if (body.len > 4) |
193 | 389k | { |
194 | 389k | n = LOWER4B( GET4B(body.s) ); |
195 | 389k | switch(n) |
196 | 389k | { |
197 | 13.8k | CASE_5B( 0x7265616c, 'm', REALM_STATE, 1); /*realm*/ |
198 | 12.1k | CASE_5B( 0x6e6f6e63, 'e', NONCE_STATE, 1); /*nonce*/ |
199 | 1.28k | CASE_5B( 0x7374616c, 'e', STALE_STATE, 0); /*stale*/ |
200 | 6.87k | CASE_6B( 0x646f6d62, 'i', 'n', DOMAIN_STATE, 1); /*domain*/ |
201 | 112k | CASE_6B( 0x6f706171, 'u', 'e', OPAQUE_STATE, 1); /*opaque*/ |
202 | 7.26k | case 0x616c676f: /*algo*/ |
203 | 7.26k | if (body.len > 9 && TRB_SCASEMATCH(body.s+4, "rithm")) |
204 | 5.07k | { |
205 | 5.07k | STR_ADVANCE_BY(&body, 9); |
206 | 5.07k | state = ALGORITHM_STATE; |
207 | 5.07k | } else { |
208 | 2.18k | STR_ADVANCE_BY(&body, 4); |
209 | 2.18k | } |
210 | 7.26k | break; |
211 | 236k | default: |
212 | 236k | if ((n|0xff)==0x716f70ff) /*qop*/ |
213 | 61.7k | { |
214 | 61.7k | state = QOP_STATE; |
215 | 61.7k | STR_ADVANCE_BY(&body, 3); |
216 | 174k | } else if ((n|0xffff) == 0x696bffff) { /*ik*/ |
217 | 255 | state = IK_STATE; |
218 | 255 | STR_ADVANCE_BY(&body, 2); |
219 | 174k | } else if ((n|0xffff) == 0x636bffff) { /*ck*/ |
220 | 402 | state = CK_STATE; |
221 | 402 | STR_ADVANCE_BY(&body, 2); |
222 | 402 | } |
223 | 389k | } |
224 | 389k | } else if (body.len > 2) { |
225 | 1.10k | if (body.len > 3) { |
226 | 402 | if (TRB_SCASEMATCH(body.s, "qop")) |
227 | 15 | { |
228 | 15 | STR_ADVANCE_BY(&body, 3); |
229 | 15 | state = QOP_STATE; |
230 | 15 | } |
231 | 701 | } else if (TRB_SCASEMATCH(body.s, "ik")) |
232 | 15 | { |
233 | 15 | STR_ADVANCE_BY(&body, 2); |
234 | 15 | state = IK_STATE; |
235 | 686 | } else if (TRB_SCASEMATCH(body.s, "ck")) |
236 | 8 | { |
237 | 8 | STR_ADVANCE_BY(&body, 2); |
238 | 8 | state = CK_STATE; |
239 | 8 | } |
240 | 1.10k | } |
241 | | |
242 | | /* parse to the "=" */ |
243 | 11.5M | for(n=0 ; body.len > 0 && !is_ws(*body.s) && *body.s != '=' ; n++) |
244 | 11.1M | STR_ADVANCE(&body); |
245 | 391k | if (body.len == 0) |
246 | 2.77k | goto parse_error; |
247 | 388k | if (n!=0) |
248 | 268k | state = OTHER_STATE; |
249 | 388k | name.len = body.s - name.s; |
250 | | /* get the '=' */ |
251 | 388k | trim_leading(&body); |
252 | 388k | if (body.len == 0 || *body.s != '=') |
253 | 1.20k | goto parse_error; |
254 | 387k | STR_ADVANCE(&body); |
255 | | /* get the value (quoted or not) */ |
256 | 387k | trim_leading(&body); |
257 | 387k | if (body.len <= 1 || (quoted_val && *body.s != '\"')) |
258 | 502 | goto parse_error; |
259 | 386k | if (!quoted_val && *body.s == '\"') |
260 | 78.1k | quoted_val = 1; |
261 | 386k | if (quoted_val) |
262 | 106k | { |
263 | 106k | STR_ADVANCE(&body); |
264 | 106k | char *cp = memchr(body.s, '\"', body.len); |
265 | 106k | if (cp == NULL) |
266 | 155 | goto error; |
267 | 106k | val.s = body.s; |
268 | 106k | STR_ADVANCE_BY(&body, cp - body.s); |
269 | 280k | } else { |
270 | 280k | val.s = body.s; |
271 | 11.6M | while (body.len > 0 && !is_ws(*body.s) && *body.s != ',') |
272 | 11.3M | STR_ADVANCE(&body); |
273 | 280k | } |
274 | 386k | val.len = body.s - val.s; |
275 | 386k | if (val.len==0) |
276 | 25.7k | val.s = 0; |
277 | | /* consume the closing '"' if quoted */ |
278 | 386k | STR_ADVANCE_BY(&body, quoted_val); |
279 | 386k | trim_leading(&body); |
280 | 386k | if (body.len > 0 && *body.s == ',') |
281 | 255k | { |
282 | 255k | STR_ADVANCE(&body); |
283 | 255k | trim_leading(&body); |
284 | 255k | } |
285 | | |
286 | 386k | LM_DBG("<%.*s>=\"%.*s\" state=%d\n", |
287 | 386k | name.len,name.s,val.len,val.s,state); |
288 | | |
289 | | /* process the AVP */ |
290 | 386k | switch (state) |
291 | 386k | { |
292 | 60.6k | case QOP_STATE: |
293 | 60.6k | auth->qop = val; |
294 | 60.6k | if (parse_qop_value(val, auth) < 0) |
295 | 55.9k | LM_DBG("Unknown token in qop value '%.*s'\n", |
296 | 60.6k | val.len, val.s); |
297 | 60.6k | break; |
298 | 11.6k | case REALM_STATE: |
299 | 11.6k | auth->realm = val; |
300 | 11.6k | break; |
301 | 11.0k | case NONCE_STATE: |
302 | 11.0k | auth->nonce = val; |
303 | 11.0k | break; |
304 | 3.56k | case DOMAIN_STATE: |
305 | 3.56k | auth->domain = val; |
306 | 3.56k | break; |
307 | 220 | case OPAQUE_STATE: |
308 | 220 | auth->opaque = val; |
309 | 220 | break; |
310 | 245 | case IK_STATE: |
311 | 245 | auth->ik = val; |
312 | 245 | break; |
313 | 226 | case CK_STATE: |
314 | 226 | auth->ck = val; |
315 | 226 | break; |
316 | 5.00k | case ALGORITHM_STATE: |
317 | 5.00k | auth->algorithm = parse_digest_algorithm(&val); |
318 | 5.00k | if (auth->algorithm == ALG_OTHER) { |
319 | 470 | LM_INFO("bad algorithm \"%.*s\"\n", val.len, val.s); |
320 | 470 | goto error; |
321 | 470 | } |
322 | 4.53k | break; |
323 | 4.53k | case STALE_STATE: |
324 | 418 | if (TRB_STRCASEMATCH(&val, "true")) |
325 | 219 | { |
326 | 219 | auth->flags |= AUTHENTICATE_STALE; |
327 | 219 | } else if (!(TRB_STRCASEMATCH(&val, "false"))) |
328 | 199 | { |
329 | 199 | LM_ERR("unsupported stale value \"%.*s\"\n",val.len,val.s); |
330 | 199 | goto error; |
331 | 199 | } |
332 | 219 | break; |
333 | 293k | default: |
334 | 293k | break; |
335 | 386k | } |
336 | 386k | } |
337 | | |
338 | | /* some checkings */ |
339 | 3.20k | if (auth->nonce.s==0 || auth->realm.s==0) |
340 | 1.78k | { |
341 | 1.78k | LM_ERR("realm or nonce missing\n"); |
342 | 1.78k | goto error; |
343 | 1.78k | } |
344 | | |
345 | 1.42k | return ret; |
346 | 4.92k | parse_error: |
347 | 4.92k | LM_ERR("parse error in <%.*s> around %ld\n", body.len, body.s, (long)(body.len)); |
348 | 7.55k | error: |
349 | 7.55k | return -1; |
350 | 4.92k | } |
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 | 9.66k | { |
356 | 9.66k | void **parsed; |
357 | 9.66k | struct authenticate_body *auth_body, *ret_auth; |
358 | 9.66k | int rc, prev_parsed; |
359 | | |
360 | 9.66k | parsed = &(authenticate->parsed); |
361 | 9.66k | prev_parsed = (*parsed != NULL); |
362 | 9.66k | ret_auth = NULL; |
363 | | |
364 | 10.8k | while(*parsed == NULL) |
365 | 8.97k | { |
366 | 8.97k | auth_body = pkg_malloc(sizeof(struct authenticate_body)); |
367 | 8.97k | 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 | 8.97k | rc = parse_authenticate_body(authenticate->body, auth_body); |
375 | 8.97k | if (rc < 0) { |
376 | 7.55k | pkg_free(auth_body); |
377 | 7.55k | *picked_auth = ret_auth; |
378 | 7.55k | return -1; |
379 | 7.55k | } |
380 | | |
381 | 1.42k | if (rc == 0 && !ret_auth && |
382 | 1.42k | (md == NULL || md->matchf(auth_body, md))) |
383 | 312 | ret_auth = auth_body; |
384 | | |
385 | 1.42k | *parsed = auth_body; |
386 | | |
387 | 1.42k | authenticate = authenticate->sibling; |
388 | 1.42k | if (authenticate) |
389 | 1.18k | parsed = &(authenticate->parsed); |
390 | 238 | else |
391 | 238 | break; |
392 | 1.42k | } |
393 | 2.11k | if (prev_parsed) { |
394 | 3.74k | while (!ret_auth && authenticate) { |
395 | 1.87k | if (authenticate->parsed && |
396 | 1.87k | (md == NULL || md->matchf(authenticate->parsed, md))) |
397 | 1.87k | ret_auth = authenticate->parsed; |
398 | 1.87k | authenticate = authenticate->sibling; |
399 | 1.87k | } |
400 | 1.87k | } |
401 | 2.11k | *picked_auth = ret_auth; |
402 | | |
403 | 2.11k | return ret_auth ? 0 : -1; |
404 | 9.66k | } |
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 | 448 | { |
416 | 448 | if ( !msg->www_authenticate && |
417 | 448 | (parse_headers(msg, HDR_WWW_AUTHENTICATE_F,0)==-1 || !msg->www_authenticate)) { |
418 | 7 | return -1; |
419 | 7 | } |
420 | | |
421 | 441 | return parse_authenticate_header(msg->www_authenticate, md, |
422 | 441 | picked_auth); |
423 | 448 | } |
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 | 9.38k | { |
436 | 9.38k | if ( !msg->proxy_authenticate && |
437 | 9.38k | (parse_headers(msg, HDR_PROXY_AUTHENTICATE_F,0)==-1 || !msg->proxy_authenticate)) { |
438 | 161 | return -1; |
439 | 161 | } |
440 | | |
441 | 9.21k | return parse_authenticate_header(msg->proxy_authenticate, md, |
442 | 9.21k | picked_auth); |
443 | 9.38k | } |
444 | | |
445 | | |
446 | | void free_authenticate(struct authenticate_body *authenticate_b) |
447 | 1.42k | { |
448 | 1.42k | if (authenticate_b) { |
449 | 1.42k | pkg_free(authenticate_b); |
450 | 1.42k | } |
451 | | |
452 | 1.42k | return; |
453 | 1.42k | } |