/src/opensips/parser/parse_authenticate.c
Line | Count | Source |
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.13k | #define AUTHENTICATE_DIGEST_S "Digest" |
38 | 8.13k | #define AUTHENTICATE_DIGEST_LEN (sizeof(AUTHENTICATE_DIGEST_S)-1) |
39 | | |
40 | | #define LOWER1B(_n) \ |
41 | 52.0k | ((_n < 'A' ||_n > 'Z') ? _n : _n |0x20) |
42 | | #define LOWER4B(_n) \ |
43 | 153k | ((_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 | 32.5k | case _hex4: \ |
49 | 32.5k | if (body.len > 5 && LOWER1B(*(body.s+4))==_c5 ) \ |
50 | 32.5k | { \ |
51 | 25.8k | STR_ADVANCE_BY(&body, 5); \ |
52 | 25.8k | state = _new_state; \ |
53 | 25.8k | quoted_val = _quoted; \ |
54 | 25.8k | } else { \ |
55 | 6.76k | STR_ADVANCE_BY(&body, 4); \ |
56 | 6.76k | } \ |
57 | 32.5k | break; |
58 | | |
59 | | #define CASE_6B(_hex4,_c5,_c6, _new_state, _quoted) \ |
60 | 13.5k | case _hex4: \ |
61 | 13.5k | if (body.len > 6 && LOWER1B(*(body.s+4))==_c5 && LOWER1B(*(body.s+5))==_c6) \ |
62 | 13.5k | { \ |
63 | 2.24k | STR_ADVANCE_BY(&body, 6); \ |
64 | 2.24k | state = _new_state; \ |
65 | 2.24k | quoted_val = _quoted; \ |
66 | 11.2k | } else { \ |
67 | 11.2k | STR_ADVANCE_BY(&body, 4); \ |
68 | 11.2k | } \ |
69 | 13.5k | break; |
70 | | |
71 | 245k | #define OTHER_STATE 0 |
72 | 46.2k | #define QOP_STATE 1 |
73 | 8.33k | #define REALM_STATE 2 |
74 | 10.5k | #define NONCE_STATE 3 |
75 | 500 | #define STALE_STATE 4 |
76 | 375 | #define DOMAIN_STATE 5 |
77 | 237 | #define OPAQUE_STATE 6 |
78 | 13.9k | #define ALGORITHM_STATE 7 |
79 | 743 | #define IK_STATE 8 |
80 | 1.97k | #define CK_STATE 9 |
81 | | |
82 | 20.8k | #define TRB_SCASEMATCH(cp, S) (turbo_casematch(cp, (S), (sizeof(S) - 1))) |
83 | 5.07k | #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 | 7.41M | #define STR_ADVANCE_BY(sptr, incr) {int _t = (incr); (sptr)->s += _t; (sptr)->len -= _t;} |
88 | 6.91M | #define STR_ADVANCE(sptr) STR_ADVANCE_BY(sptr, 1) |
89 | 32.9k | #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 | 32.9k | { |
93 | 32.9k | if (val->len < slen || !turbo_casematch(val->s, sval, slen)) |
94 | 11.7k | return 0; |
95 | 21.1k | STR_ADVANCE_BY(val, slen); |
96 | 21.1k | return 1; |
97 | 32.9k | } |
98 | | |
99 | | int parse_qop_value(str val, struct authenticate_body *auth) |
100 | 22.4k | { |
101 | | |
102 | | /* parse first token */ |
103 | 22.4k | if (!STR_ADVANCE_IF_STARTS(&val, "auth")) |
104 | 6.42k | return -1; |
105 | 16.0k | if (val.len == 0) { |
106 | 384 | auth->flags |= QOP_AUTH; |
107 | 384 | return 0; |
108 | 384 | } |
109 | 15.6k | switch (*val.s) { |
110 | 1.49k | case ' ': |
111 | 3.25k | case '\t': |
112 | 3.25k | STR_ADVANCE(&val); |
113 | 3.25k | auth->flags |= QOP_AUTH; |
114 | 3.25k | break; |
115 | 2.54k | case '-': |
116 | 2.54k | STR_ADVANCE(&val); |
117 | 2.54k | if (STR_ADVANCE_IF_STARTS(&val, "int")) { |
118 | 389 | auth->flags |= QOP_AUTH_INT; |
119 | 389 | } else |
120 | 2.15k | return -1; |
121 | 389 | break; |
122 | 7.39k | case ',': |
123 | 7.39k | auth->flags |= QOP_AUTH; |
124 | 7.39k | goto postcomma; |
125 | 2.50k | default: |
126 | 2.50k | return -1; |
127 | 15.6k | } |
128 | | |
129 | 3.63k | if (val.len == 0) |
130 | 231 | return 0; |
131 | | |
132 | 3.40k | trim_leading(&val); |
133 | | |
134 | 3.40k | if (val.len == 0) |
135 | 410 | return 0; |
136 | 2.99k | if (*val.s != ',') |
137 | 2.47k | return -1; |
138 | 7.91k | postcomma: |
139 | 7.91k | STR_ADVANCE(&val); |
140 | 7.91k | trim_leading(&val); |
141 | | |
142 | | /* parse second token */ |
143 | 7.91k | if (!STR_ADVANCE_IF_STARTS(&val, "auth")) |
144 | 3.19k | return -1; |
145 | 4.72k | if (val.len == 0) { |
146 | 237 | auth->flags |= QOP_AUTH; |
147 | 237 | return 0; |
148 | 237 | } |
149 | 4.48k | if (TRB_STRCASEMATCH(&val, "-int")) { |
150 | 1.57k | auth->flags |= QOP_AUTH_INT; |
151 | 1.57k | return 0; |
152 | 1.57k | } else |
153 | 2.91k | return -1; |
154 | 4.48k | } |
155 | | |
156 | | int parse_authenticate_body( str body, struct authenticate_body *auth) |
157 | 8.15k | { |
158 | 8.15k | int n, ret = 0; |
159 | 8.15k | int state; |
160 | 8.15k | str name; |
161 | 8.15k | str val; |
162 | 8.15k | int quoted_val; |
163 | | |
164 | 8.15k | if (body.len == 0) |
165 | 15 | { |
166 | 15 | LM_ERR("empty body\n"); |
167 | 15 | goto error; |
168 | 15 | } |
169 | | |
170 | 8.13k | memset( auth, 0, sizeof(struct authenticate_body)); |
171 | | |
172 | | /* parse the "digest" */ |
173 | 8.13k | trim_leading(&body); |
174 | 8.13k | if (body.len <= AUTHENTICATE_DIGEST_LEN) |
175 | 81 | goto parse_error; |
176 | 8.05k | if (!TRB_SCASEMATCH(body.s, "digest")) |
177 | 174 | goto parse_error; |
178 | 7.88k | STR_ADVANCE_BY(&body, AUTHENTICATE_DIGEST_LEN); |
179 | 7.88k | if (!is_ws(*body.s)) |
180 | 8 | goto parse_error; |
181 | 7.87k | STR_ADVANCE(&body); |
182 | 7.87k | trim_leading(&body); |
183 | 7.87k | if (body.len == 0) |
184 | 45 | goto parse_error; |
185 | | |
186 | 158k | while (body.len > 0) |
187 | 155k | { |
188 | 155k | state = OTHER_STATE; |
189 | 155k | quoted_val = 0; |
190 | | /* get name */ |
191 | 155k | name.s = body.s; |
192 | 155k | if (body.len > 4) |
193 | 153k | { |
194 | 153k | n = LOWER4B( GET4B(body.s) ); |
195 | 153k | switch(n) |
196 | 153k | { |
197 | 15.4k | CASE_5B( 0x7265616c, 'm', REALM_STATE, 1); /*realm*/ |
198 | 12.8k | CASE_5B( 0x6e6f6e63, 'e', NONCE_STATE, 1); /*nonce*/ |
199 | 4.27k | CASE_5B( 0x7374616c, 'e', STALE_STATE, 0); /*stale*/ |
200 | 9.34k | CASE_6B( 0x646f6d62, 'i', 'n', DOMAIN_STATE, 1); /*domain*/ |
201 | 4.17k | CASE_6B( 0x6f706171, 'u', 'e', OPAQUE_STATE, 1); /*opaque*/ |
202 | 11.1k | case 0x616c676f: /*algo*/ |
203 | 11.1k | if (body.len > 9 && TRB_SCASEMATCH(body.s+4, "rithm")) |
204 | 7.65k | { |
205 | 7.65k | STR_ADVANCE_BY(&body, 9); |
206 | 7.65k | state = ALGORITHM_STATE; |
207 | 7.65k | } else { |
208 | 3.53k | STR_ADVANCE_BY(&body, 4); |
209 | 3.53k | } |
210 | 11.1k | break; |
211 | 96.4k | default: |
212 | 96.4k | if ((n|0xff)==0x716f70ff) /*qop*/ |
213 | 23.7k | { |
214 | 23.7k | state = QOP_STATE; |
215 | 23.7k | STR_ADVANCE_BY(&body, 3); |
216 | 72.7k | } else if ((n|0xffff) == 0x696bffff) { /*ik*/ |
217 | 371 | state = IK_STATE; |
218 | 371 | STR_ADVANCE_BY(&body, 2); |
219 | 72.3k | } else if ((n|0xffff) == 0x636bffff) { /*ck*/ |
220 | 1.74k | state = CK_STATE; |
221 | 1.74k | STR_ADVANCE_BY(&body, 2); |
222 | 1.74k | } |
223 | 153k | } |
224 | 153k | } else if (body.len > 2) { |
225 | 1.24k | if (body.len > 3) { |
226 | 549 | if (TRB_SCASEMATCH(body.s, "qop")) |
227 | 8 | { |
228 | 8 | STR_ADVANCE_BY(&body, 3); |
229 | 8 | state = QOP_STATE; |
230 | 8 | } |
231 | 693 | } else if (TRB_SCASEMATCH(body.s, "ik")) |
232 | 15 | { |
233 | 15 | STR_ADVANCE_BY(&body, 2); |
234 | 15 | state = IK_STATE; |
235 | 678 | } 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.24k | } |
241 | | |
242 | | /* parse to the "=" */ |
243 | 2.92M | for(n=0 ; body.len > 0 && !is_ws(*body.s) && *body.s != '=' ; n++) |
244 | 2.77M | STR_ADVANCE(&body); |
245 | 155k | if (body.len == 0) |
246 | 2.71k | goto parse_error; |
247 | 152k | if (n!=0) |
248 | 90.1k | state = OTHER_STATE; |
249 | 152k | name.len = body.s - name.s; |
250 | | /* get the '=' */ |
251 | 152k | trim_leading(&body); |
252 | 152k | if (body.len == 0 || *body.s != '=') |
253 | 961 | goto parse_error; |
254 | 151k | STR_ADVANCE(&body); |
255 | | /* get the value (quoted or not) */ |
256 | 151k | trim_leading(&body); |
257 | 151k | if (body.len <= 1 || (quoted_val && *body.s != '\"')) |
258 | 363 | goto parse_error; |
259 | 151k | if (!quoted_val && *body.s == '\"') |
260 | 54.6k | quoted_val = 1; |
261 | 151k | if (quoted_val) |
262 | 80.6k | { |
263 | 80.6k | STR_ADVANCE(&body); |
264 | 80.6k | char *cp = memchr(body.s, '\"', body.len); |
265 | 80.6k | if (cp == NULL) |
266 | 185 | goto error; |
267 | 80.4k | val.s = body.s; |
268 | 80.4k | STR_ADVANCE_BY(&body, cp - body.s); |
269 | 80.4k | } else { |
270 | 70.7k | val.s = body.s; |
271 | 3.92M | while (body.len > 0 && !is_ws(*body.s) && *body.s != ',') |
272 | 3.84M | STR_ADVANCE(&body); |
273 | 70.7k | } |
274 | 151k | val.len = body.s - val.s; |
275 | 151k | if (val.len==0) |
276 | 9.84k | val.s = 0; |
277 | | /* consume the closing '"' if quoted */ |
278 | 151k | STR_ADVANCE_BY(&body, quoted_val); |
279 | 151k | trim_leading(&body); |
280 | 151k | if (body.len > 0 && *body.s == ',') |
281 | 44.0k | { |
282 | 44.0k | STR_ADVANCE(&body); |
283 | 44.0k | trim_leading(&body); |
284 | 44.0k | } |
285 | | |
286 | 151k | LM_DBG("<%.*s>=\"%.*s\" state=%d\n", |
287 | 151k | name.len,name.s,val.len,val.s,state); |
288 | | |
289 | | /* process the AVP */ |
290 | 151k | switch (state) |
291 | 151k | { |
292 | 22.4k | case QOP_STATE: |
293 | 22.4k | auth->qop = val; |
294 | 22.4k | if (parse_qop_value(val, auth) < 0) |
295 | 19.6k | LM_DBG("Unknown token in qop value '%.*s'\n", |
296 | 22.4k | val.len, val.s); |
297 | 22.4k | break; |
298 | 8.33k | case REALM_STATE: |
299 | 8.33k | auth->realm = val; |
300 | 8.33k | break; |
301 | 10.5k | case NONCE_STATE: |
302 | 10.5k | auth->nonce = val; |
303 | 10.5k | break; |
304 | 375 | case DOMAIN_STATE: |
305 | 375 | auth->domain = val; |
306 | 375 | break; |
307 | 237 | case OPAQUE_STATE: |
308 | 237 | auth->opaque = val; |
309 | 237 | break; |
310 | 357 | case IK_STATE: |
311 | 357 | auth->ik = val; |
312 | 357 | break; |
313 | 224 | case CK_STATE: |
314 | 224 | auth->ck = val; |
315 | 224 | break; |
316 | 6.27k | case ALGORITHM_STATE: |
317 | 6.27k | auth->algorithm = parse_digest_algorithm(&val); |
318 | 6.27k | if (auth->algorithm == ALG_OTHER) { |
319 | 462 | LM_INFO("bad algorithm \"%.*s\"\n", val.len, val.s); |
320 | 462 | goto error; |
321 | 462 | } |
322 | 5.81k | break; |
323 | 5.81k | case STALE_STATE: |
324 | 500 | if (TRB_STRCASEMATCH(&val, "true")) |
325 | 406 | { |
326 | 406 | auth->flags |= AUTHENTICATE_STALE; |
327 | 406 | } else if (!(TRB_STRCASEMATCH(&val, "false"))) |
328 | 94 | { |
329 | 94 | LM_ERR("unsupported stale value \"%.*s\"\n",val.len,val.s); |
330 | 94 | goto error; |
331 | 94 | } |
332 | 406 | break; |
333 | 101k | default: |
334 | 101k | break; |
335 | 151k | } |
336 | 151k | } |
337 | | |
338 | | /* some checkings */ |
339 | 3.05k | if (auth->nonce.s==0 || auth->realm.s==0) |
340 | 1.55k | { |
341 | 1.55k | LM_ERR("realm or nonce missing\n"); |
342 | 1.55k | goto error; |
343 | 1.55k | } |
344 | | |
345 | 1.49k | return ret; |
346 | 4.34k | parse_error: |
347 | 4.34k | LM_ERR("parse error in <%.*s> around %ld\n", body.len, body.s, (long)(body.len)); |
348 | 6.66k | error: |
349 | 6.66k | return -1; |
350 | 4.34k | } |
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 | 8.67k | { |
356 | 8.67k | void **parsed; |
357 | 8.67k | struct authenticate_body *auth_body, *ret_auth; |
358 | 8.67k | int rc, prev_parsed; |
359 | | |
360 | 8.67k | parsed = &(authenticate->parsed); |
361 | 8.67k | prev_parsed = (*parsed != NULL); |
362 | 8.67k | ret_auth = NULL; |
363 | | |
364 | 9.94k | while(*parsed == NULL) |
365 | 8.15k | { |
366 | 8.15k | auth_body = pkg_malloc(sizeof(struct authenticate_body)); |
367 | 8.15k | 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.15k | rc = parse_authenticate_body(authenticate->body, auth_body); |
375 | 8.15k | if (rc < 0) { |
376 | 6.66k | pkg_free(auth_body); |
377 | 6.66k | *picked_auth = ret_auth; |
378 | 6.66k | return -1; |
379 | 6.66k | } |
380 | | |
381 | 1.49k | if (rc == 0 && !ret_auth && |
382 | 299 | (md == NULL || md->matchf(auth_body, md))) |
383 | 299 | ret_auth = auth_body; |
384 | | |
385 | 1.49k | *parsed = auth_body; |
386 | | |
387 | 1.49k | authenticate = authenticate->sibling; |
388 | 1.49k | if (authenticate) |
389 | 1.27k | parsed = &(authenticate->parsed); |
390 | 219 | else |
391 | 219 | break; |
392 | 1.49k | } |
393 | 2.01k | if (prev_parsed) { |
394 | 3.58k | while (!ret_auth && authenticate) { |
395 | 1.79k | if (authenticate->parsed && |
396 | 1.79k | (md == NULL || md->matchf(authenticate->parsed, md))) |
397 | 1.79k | ret_auth = authenticate->parsed; |
398 | 1.79k | authenticate = authenticate->sibling; |
399 | 1.79k | } |
400 | 1.79k | } |
401 | 2.01k | *picked_auth = ret_auth; |
402 | | |
403 | 2.01k | return ret_auth ? 0 : -1; |
404 | 8.67k | } |
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 | 1.23k | { |
416 | 1.23k | if ( !msg->www_authenticate && |
417 | 7 | (parse_headers(msg, HDR_WWW_AUTHENTICATE_F,0)==-1 || !msg->www_authenticate)) { |
418 | 7 | return -1; |
419 | 7 | } |
420 | | |
421 | 1.23k | return parse_authenticate_header(msg->www_authenticate, md, |
422 | 1.23k | picked_auth); |
423 | 1.23k | } |
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 | 7.56k | { |
436 | 7.56k | if ( !msg->proxy_authenticate && |
437 | 126 | (parse_headers(msg, HDR_PROXY_AUTHENTICATE_F,0)==-1 || !msg->proxy_authenticate)) { |
438 | 126 | return -1; |
439 | 126 | } |
440 | | |
441 | 7.44k | return parse_authenticate_header(msg->proxy_authenticate, md, |
442 | 7.44k | picked_auth); |
443 | 7.56k | } |
444 | | |
445 | | |
446 | | void free_authenticate(struct authenticate_body *authenticate_b) |
447 | 1.49k | { |
448 | 1.49k | if (authenticate_b) { |
449 | 1.49k | pkg_free(authenticate_b); |
450 | 1.49k | } |
451 | | |
452 | 1.49k | return; |
453 | 1.49k | } |