Coverage Report

Created: 2025-07-18 06:32

/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
}