Coverage Report

Created: 2025-07-18 06:32

/src/opensips/parser/parse_security.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * Security-{Client,Server,Verify} header field body parser
3
 *
4
 * Copyright (c) 2024 OpenSIPS Solutions
5
 *
6
 * This file is part of opensips, a free SIP server.
7
 *
8
 * opensips is free software; you can redistribute it and/or modify
9
 * it under the terms of the GNU General Public License as published by
10
 * the Free Software Foundation; either version 2 of the License, or
11
 * (at your option) any later version
12
 *
13
 * opensips is distributed in the hope that it will be useful,
14
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16
 * GNU General Public License for more details.
17
 *
18
 * You should have received a copy of the GNU General Public License
19
 * along with this program; if not, write to the Free Software
20
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21
 */
22
23
#include "../ut.h"
24
#include "../mem/mem.h"
25
#include "parse_security.h"
26
27
static void free_sec_agree_body(sec_agree_body_t *s)
28
0
{
29
0
  sec_agree_param_t *it, *next;
30
0
  for (it = s->params; it; it = next) {
31
0
    next = it->next;
32
0
    pkg_free(it);
33
0
  }
34
0
  s->params = NULL;
35
0
  pkg_free(s);
36
0
}
37
38
39
enum sec_agree_state {
40
  SEC_AGREE_STATE_INVALID,
41
  SEC_AGREE_STATE_START,
42
  SEC_AGREE_STATE_MECHANISM,
43
  SEC_AGREE_STATE_PARAM_NAME_START,
44
  SEC_AGREE_STATE_PARAM_NAME,
45
  SEC_AGREE_STATE_PARAM_START_VALUE,
46
  SEC_AGREE_STATE_PARAM_VALUE,
47
  SEC_AGREE_STATE_PARAM_VALUE_ENC,
48
  SEC_AGREE_STATE_PARAM_VALUE_END,
49
};
50
51
52
void free_sec_agree(sec_agree_body_t **_s)
53
0
{
54
0
  sec_agree_body_t *bit;
55
0
  bit = *_s;
56
0
  while (bit) {
57
0
    *_s = (*_s)->next;
58
0
    free_sec_agree_body(bit);
59
0
    bit = *_s;
60
0
  }
61
0
}
62
63
sec_agree_mechanism_t parse_sec_agree_mechanism(str *mech)
64
0
{
65
0
  LM_DBG("new mechanism %.*s\n", mech->len, mech->s);
66
  /* 3GPP TS 33.203 */
67
0
  if (str_match(mech, _str("ipsec-3gpp")))
68
0
    return SEC_AGREE_MECHANISM_IPSEC_3GPP;
69
  /* RFC 3329 */
70
0
  if (str_match(mech, _str("tls")))
71
0
    return SEC_AGREE_MECHANISM_TLS;
72
0
  if (str_match(mech, _str("digest")))
73
0
    return SEC_AGREE_MECHANISM_DIGEST;
74
0
  if (str_match(mech, _str("ipsec-ike")))
75
0
    return SEC_AGREE_MECHANISM_IPSEC_IKE;
76
0
  if (str_match(mech, _str("ipsec-man")))
77
0
    return SEC_AGREE_MECHANISM_IPSEC_IKE;
78
0
  return SEC_AGREE_MECHANISM_OTHER;
79
0
}
80
81
int parse_sec_agree_parameter_pref(str *value)
82
0
{
83
0
  int i, val = 0;
84
0
  trim(value);
85
0
  if (value->len < 1)
86
0
    goto error;
87
0
  if (value->s[0] == '1') {
88
0
    if (value->len > 1) {
89
0
      if (value->s[1] != '.')
90
0
        goto error;
91
0
      for (i = 2; i < value->len; i++)
92
0
        if (value->s[i] != '0')
93
0
          goto error;
94
0
    }
95
0
    return 1000;
96
0
  } else if (value->s[0] != '0') {
97
0
    goto error;
98
0
  }
99
0
  if (value->len > 1 && value->s[1] != '.')
100
0
    goto error;
101
0
  for (i = 2; i < value->len; i++) {
102
0
    if (value->s[i] < '0' || value->s[i] > '9')
103
0
      goto error;
104
0
    val = 10 * val + (value->s[i] - '0');
105
0
  }
106
0
  val = val * 100;
107
0
  return val;
108
0
error:
109
0
  LM_ERR("invalid preference value [%.*s]\n", value->len, value->s);
110
0
  return -1;
111
0
}
112
113
int parse_sec_agree_parameter(sec_agree_body_t *sa, str *name, str *value)
114
0
{
115
0
  char *err;
116
0
  sec_agree_param_t *sp = NULL;
117
0
  static sec_agree_param_t *last_sp = NULL;
118
0
  unsigned int tmp;
119
120
0
  LM_DBG("parsing %p[%.*s] [%.*s]=[%.*s]\n", sa, sa->mechanism_str.len,
121
0
      sa->mechanism_str.s, name->len, name->s, value->len, value->s);
122
123
0
  switch (sa->mechanism) {
124
0
  case SEC_AGREE_MECHANISM_IPSEC_3GPP:
125
0
    if (str_match(name, _str("q"))) {
126
0
      sa->ts3gpp.pref_str = *value;
127
0
      sa->ts3gpp.pref = parse_sec_agree_parameter_pref(value);
128
0
      if (sa->ts3gpp.pref < 0) {
129
0
        err = "invalid preference value";
130
0
        goto error;
131
0
      }
132
0
      return 0;
133
0
    }
134
    /* TODO: do we need to parse these fields? */
135
0
    if (str_match(name, _str("alg"))) {
136
0
      sa->ts3gpp.alg_str = *value;
137
0
      return 0;
138
0
    }
139
0
    if (str_match(name, _str("mod"))) {
140
0
      sa->ts3gpp.mod_str = *value;
141
0
      return 0;
142
0
    }
143
0
    if (str_match(name, _str("ealg"))) {
144
0
      sa->ts3gpp.ealg_str = *value;
145
0
      return 0;
146
0
    }
147
0
    if (str_match(name, _str("spi-c"))) {
148
0
      sa->ts3gpp.spi_c_str = *value;
149
0
      if (str2int(value, &sa->ts3gpp.spi_c) < 0) {
150
0
        err = "invalid spi-c value";
151
0
        goto error;
152
0
      }
153
0
      return 0;
154
0
    }
155
0
    if (str_match(name, _str("spi-s"))) {
156
0
      sa->ts3gpp.spi_s_str = *value;
157
0
      if (str2int(value, &sa->ts3gpp.spi_s) < 0) {
158
0
        err = "invalid spi-s value";
159
0
        goto error;
160
0
      }
161
0
      return 0;
162
0
    }
163
0
    if (str_match(name, _str("port-c"))) {
164
0
      sa->ts3gpp.port_c_str = *value;
165
0
      if (str2int(value, &tmp) < 0 || tmp == 0 || tmp > 65535) {
166
0
        err = "invalid port-c value";
167
0
        goto error;
168
0
      }
169
0
      sa->ts3gpp.port_c = (unsigned short)tmp;
170
0
      return 0;
171
0
    }
172
0
    if (str_match(name, _str("port-s"))) {
173
0
      sa->ts3gpp.port_s_str = *value;
174
0
      if (str2int(value, &tmp) < 0 || tmp == 0 || tmp > 65535) {
175
0
        err = "invalid port-s value";
176
0
        goto error;
177
0
      }
178
0
      sa->ts3gpp.port_s = (unsigned short)tmp;
179
0
      return 0;
180
0
    }
181
0
    break;
182
0
  default:
183
    /* treat all the other the same */
184
0
    if (str_match(name, _str("q"))) {
185
0
      sa->def.preference_str = *value;
186
0
      sa->def.preference = parse_sec_agree_parameter_pref(value);
187
0
      if (sa->def.preference < 0) {
188
0
        err = "invalid preference value";
189
0
        goto error;
190
0
      }
191
0
      return 0;
192
0
    }
193
0
    if (str_match(name, _str("d-alg"))) {
194
0
      sa->def.algorithm_str = *value;
195
0
      return 0;
196
0
    }
197
0
    if (str_match(name, _str("d-qop"))) {
198
0
      sa->def.qop_str = *value;
199
0
      return 0;
200
0
    }
201
0
    if (str_match(name, _str("d-ver"))) {
202
0
      sa->def.verify_str = *value;
203
0
      return 0;
204
0
    }
205
0
    break;
206
0
  }
207
  
208
0
  sp = pkg_malloc(sizeof *sp);
209
0
  if (!sp) {
210
0
    err = "oom for extra sec-agree param";
211
0
    goto error;
212
0
  }
213
0
  memset(sp, 0, sizeof *sp);
214
0
  sp->name = *name;
215
0
  sp->value = *value;
216
0
  if (sa->params)
217
0
    last_sp->next = sp;
218
0
  else
219
0
    sa->params = sp;
220
0
  last_sp = sp;
221
0
    LM_DBG("adding extra param %p [%.*s]=[%.*s]\n", sa, sp->name.len, sp->name.s,
222
0
      sp->value.len, sp->value.s);
223
0
  return 0;
224
0
error:
225
0
  LM_ERR("%s %.*s=%.*s\n", err, name->len, name->s, value->len, value->s);
226
0
  return -1;
227
0
}
228
229
#define SEC_AGREE_FILL_MECHANISM() \
230
0
  do { \
231
0
    sa->mechanism_str.s = s; \
232
0
    sa->mechanism_str.len = p - s; \
233
0
    trim(&sa->mechanism_str); \
234
0
    if (sa->mechanism_str.len <= 0) { \
235
0
      LM_ERR("invalid sec-agree mechanism\n"); \
236
0
      goto free_sa; \
237
0
    } \
238
0
    sa->mechanism = parse_sec_agree_mechanism(&sa->mechanism_str); \
239
0
    if (!last) \
240
0
      first = sa; \
241
0
    else \
242
0
      last->next = sa; \
243
0
    last = sa; \
244
0
    if (*p == ',') { \
245
0
      state = SEC_AGREE_STATE_START; \
246
0
      sa = NULL; \
247
0
    } else { \
248
0
      state = SEC_AGREE_STATE_PARAM_NAME_START; \
249
0
      s = p + 1; \
250
0
    } \
251
0
  } while (0)
252
253
254
sec_agree_body_t *parse_sec_agree_body(str *body)
255
0
{
256
0
  sec_agree_body_t *sa = NULL, *last = NULL, *first = NULL;
257
0
  char *p, *s = NULL, *end = body->s + body->len;
258
0
  enum sec_agree_state state = SEC_AGREE_STATE_START;
259
0
  str name, value;
260
261
0
  LM_DBG("parsing %.*s\n", body->len, body->s);
262
0
  for (p = body->s; p < end; p++) {
263
0
    switch (state) {
264
0
    case SEC_AGREE_STATE_START:
265
266
0
      if (sa) {
267
0
        sec_agree_param_t *sp;
268
0
        for (sp = sa->params; sp; sp = sp->next) {
269
0
          LM_DBG("extra param %p [%.*s]=[%.*s]\n", sa, sp->name.len, sp->name.s,
270
0
              sp->value.len, sp->value.s);
271
0
        }
272
0
      }
273
0
      sa = pkg_malloc(sizeof *sa);
274
0
      if (!sa) {
275
0
        LM_ERR("oom for new sec-agree field\n");
276
0
        goto out;
277
0
      }
278
0
      memset(sa, 0, sizeof *sa);
279
0
      s = p;
280
0
      state = SEC_AGREE_STATE_MECHANISM;
281
0
      break;
282
283
0
    case SEC_AGREE_STATE_MECHANISM:
284
0
      switch(*p) {
285
0
      case ',':
286
0
      case ';':
287
0
        SEC_AGREE_FILL_MECHANISM();
288
0
        break;
289
0
      default: /* valid character */
290
0
        break;
291
0
      }
292
0
      break;
293
0
    case SEC_AGREE_STATE_PARAM_NAME_START:
294
0
      switch (*p) {
295
0
      case ' ':
296
0
      case '\r':
297
0
      case '\n':
298
0
      case '\t':
299
0
        s = p;
300
0
        break;
301
0
      case ',':
302
0
        state = SEC_AGREE_STATE_START;
303
0
        break;
304
0
      default:
305
0
        s = p;
306
0
        state = SEC_AGREE_STATE_PARAM_NAME;
307
0
        break;
308
0
      }
309
0
      break;
310
0
    case SEC_AGREE_STATE_PARAM_NAME:
311
0
      switch (*p) {
312
0
      case '=':
313
0
        name.s = s;
314
0
        name.len = p - s;
315
0
        trim(&name);
316
0
        if (name.len <= 0) {
317
0
          LM_ERR("invalid sec-agree param name!\n");
318
0
          state = SEC_AGREE_STATE_INVALID;
319
0
        } else {
320
0
          state = SEC_AGREE_STATE_PARAM_START_VALUE;
321
0
        }
322
0
      }
323
0
      break;
324
0
    case SEC_AGREE_STATE_PARAM_START_VALUE:
325
0
      switch (*p) {
326
0
      case ' ':
327
0
      case '\r':
328
0
      case '\n':
329
0
      case '\t':
330
0
        s = p;
331
0
        break;
332
0
      case ',':
333
0
        state = SEC_AGREE_STATE_START;
334
0
        break;
335
0
      case '"':
336
0
        state = SEC_AGREE_STATE_PARAM_VALUE_ENC;
337
0
        s = p + 1;
338
0
      default:
339
0
        state = SEC_AGREE_STATE_PARAM_VALUE;
340
0
        s = p;
341
0
        break;
342
0
      }
343
0
      break;
344
0
    case SEC_AGREE_STATE_PARAM_VALUE_ENC:
345
0
      switch (*p) {
346
0
      case '"':
347
0
        if (*(p - 1) == '/')
348
0
          break;
349
        /* got to the end of parameter */
350
0
        value.s = s;
351
0
        value.len = p - s - 1;
352
0
        if (value.len <= 0) {
353
0
          LM_ERR("invalid parameter %.*s value len (enc)\n", name.len, name.s);
354
0
          state = SEC_AGREE_STATE_INVALID;
355
0
        } else {
356
0
          state = SEC_AGREE_STATE_PARAM_VALUE_END;
357
0
        }
358
0
        break;
359
0
      }
360
0
      break;
361
0
    case SEC_AGREE_STATE_PARAM_VALUE_END:
362
0
      switch (*p) {
363
0
      case ' ':
364
0
      case '\r':
365
0
      case '\n':
366
0
      case '\t':
367
0
        break;
368
0
      case ',':
369
0
        if (!parse_sec_agree_parameter(sa, &name, &value)) {
370
0
          state = SEC_AGREE_STATE_START;
371
0
          sa = NULL;
372
0
        } else {
373
0
          state = SEC_AGREE_STATE_INVALID;
374
0
        }
375
0
        break;
376
0
      case ';':
377
0
        if (!parse_sec_agree_parameter(sa, &name, &value)) {
378
0
          state = SEC_AGREE_STATE_PARAM_NAME_START;
379
0
          s = p + 1;
380
0
        } else {
381
0
          state = SEC_AGREE_STATE_INVALID;
382
0
        }
383
0
        break;
384
0
      default:
385
0
        LM_ERR("invalid parameter %.*s value extra [%.*s]\n",
386
0
            name.len, name.s, (int)(p - s), s);
387
0
        state = SEC_AGREE_STATE_INVALID;
388
0
        break;
389
0
      }
390
0
      break;
391
0
    case SEC_AGREE_STATE_PARAM_VALUE:
392
0
      switch (*p) {
393
0
      case ',':
394
0
      case ';':
395
        /* got to the end of parameter */
396
0
        value.s = s;
397
0
        value.len = p - s;
398
0
        if (value.len <= 0) {
399
0
          LM_ERR("invalid parameter %.*s value len\n", name.len, name.s);
400
0
          state = SEC_AGREE_STATE_INVALID;
401
0
        } else {
402
0
          if (!parse_sec_agree_parameter(sa, &name, &value))
403
0
            if (*p == ',')
404
0
              state = SEC_AGREE_STATE_START;
405
0
            else
406
0
              state = SEC_AGREE_STATE_PARAM_NAME_START;
407
0
          else
408
0
            state = SEC_AGREE_STATE_INVALID;
409
0
        }
410
0
        break;
411
0
      }
412
0
      break;
413
0
    case SEC_AGREE_STATE_INVALID:
414
0
      sa->invalid = 1;
415
      /* best effort - search for next param */
416
0
      switch (*p) {
417
0
      case ',':
418
0
        sa = NULL;
419
0
        state = SEC_AGREE_STATE_START;
420
0
        break;
421
0
      case ';':
422
0
        state = SEC_AGREE_STATE_PARAM_NAME_START;
423
0
        break;
424
0
      }
425
0
      break;
426
0
    }
427
0
  }
428
429
  /* final states */
430
0
  switch (state) {
431
0
    case SEC_AGREE_STATE_START:
432
0
      goto free_sa;
433
0
    case SEC_AGREE_STATE_MECHANISM:
434
0
      SEC_AGREE_FILL_MECHANISM();
435
0
      break;
436
0
    case SEC_AGREE_STATE_PARAM_NAME_START:
437
0
      break;
438
0
    case SEC_AGREE_STATE_PARAM_NAME:
439
0
      LM_ERR("invalid parameter %.*s without name\n", (int)(p - s), s);
440
0
      goto invalid;
441
0
    case SEC_AGREE_STATE_PARAM_START_VALUE:
442
0
      LM_ERR("invalid parameter %.*s without value\n", (int)(p - s), s);
443
0
      goto invalid;
444
0
    case SEC_AGREE_STATE_INVALID:
445
0
      goto invalid;
446
0
    case SEC_AGREE_STATE_PARAM_VALUE:
447
0
      value.s = s;
448
0
      value.len = p - s;
449
0
    case SEC_AGREE_STATE_PARAM_VALUE_END:
450
0
      if (value.len <= 0 || parse_sec_agree_parameter(sa, &name, &value) < 0) {
451
0
        LM_ERR("invalid parameter %.*s value len\n", name.len, name.s);
452
0
        goto invalid;
453
0
      }
454
0
      break;
455
0
    case SEC_AGREE_STATE_PARAM_VALUE_ENC:
456
0
      LM_ERR("invalid parameter %.*s value (not enclosed)\n", (int)(p - s), s);
457
0
      goto invalid;
458
0
  }
459
0
out:
460
0
  if (!first) {
461
0
    if (sa) /* should not be reached */
462
0
      free_sec_agree_body(sa);
463
0
    return NULL;
464
0
  }
465
0
  return first;
466
0
invalid:
467
0
  if (sa)
468
0
    sa->invalid = 1;
469
0
  return first;
470
0
free_sa:
471
0
  if (sa)
472
0
    free_sec_agree_body(sa);
473
0
  return first;
474
0
}
475
#undef SEC_AGREE_FILL_MECHANISM
476
477
int parse_sec_agree(struct hdr_field* _h)
478
0
{
479
0
  sec_agree_body_t *sa = NULL;
480
0
  if (_h->parsed)
481
0
    return 0;
482
0
  sa = parse_sec_agree_body(&_h->body);
483
0
  if (!sa) {
484
0
    LM_ERR("could not parse header [%.*s]!\n",
485
0
        _h->body.len, _h->body.s);
486
0
    return -1;
487
0
  }
488
0
  _h->parsed = sa;
489
0
  return 0;
490
0
}