Coverage Report

Created: 2026-08-08 07:14

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/boringssl/crypto/conf/conf.cc
Line
Count
Source
1
// Copyright 1995-2016 The OpenSSL Project Authors. All Rights Reserved.
2
//
3
// Licensed under the Apache License, Version 2.0 (the "License");
4
// you may not use this file except in compliance with the License.
5
// You may obtain a copy of the License at
6
//
7
//     https://www.apache.org/licenses/LICENSE-2.0
8
//
9
// Unless required by applicable law or agreed to in writing, software
10
// distributed under the License is distributed on an "AS IS" BASIS,
11
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
// See the License for the specific language governing permissions and
13
// limitations under the License.
14
15
#include <openssl/conf.h>
16
17
#include <assert.h>
18
#include <ctype.h>
19
#include <string.h>
20
21
#include <openssl/bio.h>
22
#include <openssl/buf.h>
23
#include <openssl/err.h>
24
#include <openssl/mem.h>
25
26
#include "../internal.h"
27
#include "../mem_internal.h"
28
#include "internal.h"
29
30
31
using namespace bssl;
32
33
BSSL_NAMESPACE_BEGIN
34
35
struct conf_section_st {
36
  char *name;
37
  // values contains non-owning pointers to the values in the section.
38
  STACK_OF(CONF_VALUE) *values;
39
};
40
41
BSSL_NAMESPACE_END
42
43
static const char kDefaultSectionName[] = "default";
44
45
0
static uint32_t conf_section_hash(const CONF_SECTION *s) {
46
0
  return OPENSSL_strhash(s->name);
47
0
}
48
49
0
static int conf_section_cmp(const CONF_SECTION *a, const CONF_SECTION *b) {
50
0
  return strcmp(a->name, b->name);
51
0
}
52
53
0
static uint32_t conf_value_hash(const CONF_VALUE *v) {
54
0
  const uint32_t section_hash = OPENSSL_strhash(v->section);
55
0
  const uint32_t name_hash = OPENSSL_strhash(v->name);
56
0
  return (section_hash << 2) ^ name_hash;
57
0
}
58
59
0
static int conf_value_cmp(const CONF_VALUE *a, const CONF_VALUE *b) {
60
0
  int cmp = strcmp(a->section, b->section);
61
0
  if (cmp != 0) {
62
0
    return cmp;
63
0
  }
64
65
0
  return strcmp(a->name, b->name);
66
0
}
67
68
0
CONF *NCONF_new(void *method) {
69
0
  if (method != nullptr) {
70
0
    return nullptr;
71
0
  }
72
73
0
  CONF *conf = New<CONF>();
74
0
  if (conf == nullptr) {
75
0
    return nullptr;
76
0
  }
77
78
0
  conf->sections = lh_CONF_SECTION_new(conf_section_hash, conf_section_cmp);
79
0
  conf->values = lh_CONF_VALUE_new(conf_value_hash, conf_value_cmp);
80
0
  if (conf->sections == nullptr || conf->values == nullptr) {
81
0
    NCONF_free(conf);
82
0
    return nullptr;
83
0
  }
84
85
0
  return conf;
86
0
}
87
88
18.4k
CONF_VALUE *bssl::CONF_VALUE_new() { return New<CONF_VALUE>(); }
89
90
0
static void value_free(CONF_VALUE *value) {
91
0
  if (value == nullptr) {
92
0
    return;
93
0
  }
94
0
  OPENSSL_free(value->section);
95
0
  OPENSSL_free(value->name);
96
0
  OPENSSL_free(value->value);
97
0
  Delete(value);
98
0
}
99
100
0
static void section_free(CONF_SECTION *section) {
101
0
  if (section == nullptr) {
102
0
    return;
103
0
  }
104
0
  OPENSSL_free(section->name);
105
0
  sk_CONF_VALUE_free(section->values);
106
0
  Delete(section);
107
0
}
108
109
0
static void value_free_arg(CONF_VALUE *value, void *arg) { value_free(value); }
110
111
0
static void section_free_arg(CONF_SECTION *section, void *arg) {
112
0
  section_free(section);
113
0
}
114
115
0
void NCONF_free(CONF *conf) {
116
0
  if (conf == nullptr) {
117
0
    return;
118
0
  }
119
120
0
  lh_CONF_SECTION_doall_arg(conf->sections, section_free_arg, nullptr);
121
0
  lh_CONF_SECTION_free(conf->sections);
122
0
  lh_CONF_VALUE_doall_arg(conf->values, value_free_arg, nullptr);
123
0
  lh_CONF_VALUE_free(conf->values);
124
0
  Delete(conf);
125
0
}
126
127
0
static CONF_SECTION *NCONF_new_section(const CONF *conf, const char *section) {
128
0
  CONF_SECTION *s = New<CONF_SECTION>();
129
0
  if (!s) {
130
0
    return nullptr;
131
0
  }
132
0
  s->name = OPENSSL_strdup(section);
133
0
  s->values = sk_CONF_VALUE_new_null();
134
0
  if (s->name == nullptr || s->values == nullptr) {
135
0
    goto err;
136
0
  }
137
138
0
  CONF_SECTION *old_section;
139
0
  if (!lh_CONF_SECTION_insert(conf->sections, &old_section, s)) {
140
0
    goto err;
141
0
  }
142
0
  section_free(old_section);
143
0
  return s;
144
145
0
err:
146
0
  section_free(s);
147
0
  return nullptr;
148
0
}
149
150
0
static int is_comment(char c) { return c == '#'; }
151
152
0
static int is_quote(char c) { return c == '"' || c == '\'' || c == '`'; }
153
154
0
static int is_esc(char c) { return c == '\\'; }
155
156
0
static int is_conf_ws(char c) {
157
  // This differs from `OPENSSL_isspace` in that CONF does not accept '\v' and
158
  // '\f' as whitespace.
159
0
  return c == ' ' || c == '\t' || c == '\r' || c == '\n';
160
0
}
161
162
0
static int is_name_char(char c) {
163
  // Alphanumeric characters, and a handful of symbols, may appear in value and
164
  // section names without escaping.
165
0
  return OPENSSL_isalnum(c) || c == '_' || c == '!' || c == '.' || c == '%' ||
166
0
         c == '&' || c == '*' || c == '+' || c == ',' || c == '/' || c == ';' ||
167
0
         c == '?' || c == '@' || c == '^' || c == '~' || c == '|' || c == '-';
168
0
}
169
170
0
static int str_copy(CONF *conf, char *section, char **pto, char *from) {
171
0
  int q, to = 0, len = 0;
172
0
  char v;
173
0
  BUF_MEM *buf;
174
175
0
  buf = BUF_MEM_new();
176
0
  if (buf == nullptr) {
177
0
    return 0;
178
0
  }
179
180
0
  len = strlen(from) + 1;
181
0
  if (!BUF_MEM_grow(buf, len)) {
182
0
    goto err;
183
0
  }
184
185
0
  for (;;) {
186
0
    if (is_quote(*from)) {
187
0
      q = *from;
188
0
      from++;
189
0
      while (*from != '\0' && *from != q) {
190
0
        if (is_esc(*from)) {
191
0
          from++;
192
0
          if (*from == '\0') {
193
0
            break;
194
0
          }
195
0
        }
196
0
        buf->data[to++] = *(from++);
197
0
      }
198
0
      if (*from == q) {
199
0
        from++;
200
0
      }
201
0
    } else if (is_esc(*from)) {
202
0
      from++;
203
0
      v = *(from++);
204
0
      if (v == '\0') {
205
0
        break;
206
0
      } else if (v == 'r') {
207
0
        v = '\r';
208
0
      } else if (v == 'n') {
209
0
        v = '\n';
210
0
      } else if (v == 'b') {
211
0
        v = '\b';
212
0
      } else if (v == 't') {
213
0
        v = '\t';
214
0
      }
215
0
      buf->data[to++] = v;
216
0
    } else if (*from == '\0') {
217
0
      break;
218
0
    } else if (*from == '$') {
219
      // Historically, $foo would expand to a previously-parsed value. This
220
      // feature has been removed as it was unused and is a DoS vector. If
221
      // trying to embed '$' in a line, either escape it or wrap the value in
222
      // quotes.
223
0
      OPENSSL_PUT_ERROR(CONF, CONF_R_VARIABLE_EXPANSION_NOT_SUPPORTED);
224
0
      goto err;
225
0
    } else {
226
0
      buf->data[to++] = *(from++);
227
0
    }
228
0
  }
229
230
0
  buf->data[to] = '\0';
231
0
  OPENSSL_free(*pto);
232
0
  *pto = buf->data;
233
0
  OPENSSL_free(buf);
234
0
  return 1;
235
236
0
err:
237
0
  BUF_MEM_free(buf);
238
0
  return 0;
239
0
}
240
241
0
static CONF_SECTION *get_section(const CONF *conf, const char *section) {
242
0
  CONF_SECTION templ;
243
0
  OPENSSL_memset(&templ, 0, sizeof(templ));
244
0
  templ.name = (char *)section;
245
0
  return lh_CONF_SECTION_retrieve(conf->sections, &templ);
246
0
}
247
248
const STACK_OF(CONF_VALUE) *NCONF_get_section(const CONF *conf,
249
0
                                              const char *section) {
250
0
  const CONF_SECTION *section_obj = get_section(conf, section);
251
0
  if (section_obj == nullptr) {
252
0
    return nullptr;
253
0
  }
254
0
  return section_obj->values;
255
0
}
256
257
const char *NCONF_get_string(const CONF *conf, const char *section,
258
0
                             const char *name) {
259
0
  CONF_VALUE templ, *value;
260
261
0
  if (section == nullptr) {
262
0
    section = kDefaultSectionName;
263
0
  }
264
265
0
  OPENSSL_memset(&templ, 0, sizeof(templ));
266
0
  templ.section = (char *)section;
267
0
  templ.name = (char *)name;
268
0
  value = lh_CONF_VALUE_retrieve(conf->values, &templ);
269
0
  if (value == nullptr) {
270
0
    return nullptr;
271
0
  }
272
0
  return value->value;
273
0
}
274
275
static int add_string(const CONF *conf, CONF_SECTION *section,
276
0
                      CONF_VALUE *value) {
277
0
  value->section = OPENSSL_strdup(section->name);
278
0
  if (value->section == nullptr) {
279
0
    return 0;
280
0
  }
281
282
0
  if (!sk_CONF_VALUE_push(section->values, value)) {
283
0
    return 0;
284
0
  }
285
286
0
  CONF_VALUE *old_value;
287
0
  if (!lh_CONF_VALUE_insert(conf->values, &old_value, value)) {
288
    // Remove `value` from `section->values`, so we do not leave a dangling
289
    // pointer.
290
0
    sk_CONF_VALUE_pop(section->values);
291
0
    return 0;
292
0
  }
293
0
  if (old_value != nullptr) {
294
0
    (void)sk_CONF_VALUE_delete_ptr(section->values, old_value);
295
0
    value_free(old_value);
296
0
  }
297
298
0
  return 1;
299
0
}
300
301
0
static char *eat_ws(char *p) {
302
0
  while (*p != '\0' && is_conf_ws(*p)) {
303
0
    p++;
304
0
  }
305
0
  return p;
306
0
}
307
308
0
static char *scan_esc(char *p) {
309
0
  assert(p[0] == '\\');
310
0
  return p[1] == '\0' ? p + 1 : p + 2;
311
0
}
312
313
0
static char *eat_name(char *p) {
314
0
  for (;;) {
315
0
    if (is_esc(*p)) {
316
0
      p = scan_esc(p);
317
0
      continue;
318
0
    }
319
0
    if (!is_name_char(*p)) {
320
0
      return p;
321
0
    }
322
0
    p++;
323
0
  }
324
0
}
325
326
0
static char *scan_quote(char *p) {
327
0
  int q = *p;
328
329
0
  p++;
330
0
  while (*p != '\0' && *p != q) {
331
0
    if (is_esc(*p)) {
332
0
      p++;
333
0
      if (*p == '\0') {
334
0
        return p;
335
0
      }
336
0
    }
337
0
    p++;
338
0
  }
339
0
  if (*p == q) {
340
0
    p++;
341
0
  }
342
0
  return p;
343
0
}
344
345
0
static void clear_comments(char *p) {
346
0
  for (;;) {
347
0
    if (!is_conf_ws(*p)) {
348
0
      break;
349
0
    }
350
0
    p++;
351
0
  }
352
353
0
  for (;;) {
354
0
    if (is_comment(*p)) {
355
0
      *p = '\0';
356
0
      return;
357
0
    }
358
0
    if (is_quote(*p)) {
359
0
      p = scan_quote(p);
360
0
      continue;
361
0
    }
362
0
    if (is_esc(*p)) {
363
0
      p = scan_esc(p);
364
0
      continue;
365
0
    }
366
0
    if (*p == '\0') {
367
0
      return;
368
0
    } else {
369
0
      p++;
370
0
    }
371
0
  }
372
0
}
373
374
0
int NCONF_load_bio(CONF *conf, BIO *in, long *out_error_line) {
375
0
  static const size_t CONFBUFSIZE = 512;
376
0
  int bufnum = 0, i, ii;
377
0
  BUF_MEM *buff = nullptr;
378
0
  char *s, *p, *end;
379
0
  int again;
380
0
  long eline = 0;
381
0
  CONF_VALUE *v = nullptr;
382
0
  CONF_SECTION *sv = nullptr;
383
0
  char *section = nullptr, *buf;
384
0
  char *start, *psection, *pname;
385
386
0
  if ((buff = BUF_MEM_new()) == nullptr) {
387
0
    OPENSSL_PUT_ERROR(CONF, ERR_R_BUF_LIB);
388
0
    goto err;
389
0
  }
390
391
0
  section = OPENSSL_strdup(kDefaultSectionName);
392
0
  if (section == nullptr) {
393
0
    goto err;
394
0
  }
395
396
0
  sv = NCONF_new_section(conf, section);
397
0
  if (sv == nullptr) {
398
0
    OPENSSL_PUT_ERROR(CONF, CONF_R_UNABLE_TO_CREATE_NEW_SECTION);
399
0
    goto err;
400
0
  }
401
402
0
  bufnum = 0;
403
0
  again = 0;
404
0
  for (;;) {
405
0
    if (!BUF_MEM_grow(buff, bufnum + CONFBUFSIZE)) {
406
0
      OPENSSL_PUT_ERROR(CONF, ERR_R_BUF_LIB);
407
0
      goto err;
408
0
    }
409
0
    p = &(buff->data[bufnum]);
410
0
    *p = '\0';
411
0
    BIO_gets(in, p, CONFBUFSIZE - 1);
412
0
    p[CONFBUFSIZE - 1] = '\0';
413
0
    ii = i = strlen(p);
414
0
    if (i == 0 && !again) {
415
0
      break;
416
0
    }
417
0
    again = 0;
418
0
    while (i > 0) {
419
0
      if ((p[i - 1] != '\r') && (p[i - 1] != '\n')) {
420
0
        break;
421
0
      } else {
422
0
        i--;
423
0
      }
424
0
    }
425
    // we removed some trailing stuff so there is a new
426
    // line on the end.
427
0
    if (ii && i == ii) {
428
0
      again = 1;  // long line
429
0
    } else {
430
0
      p[i] = '\0';
431
0
      eline++;  // another input line
432
0
    }
433
434
    // we now have a line with trailing \r\n removed
435
436
    // i is the number of bytes
437
0
    bufnum += i;
438
439
0
    v = nullptr;
440
    // check for line continuation
441
0
    if (bufnum >= 1) {
442
      // If we have bytes and the last char '\\' and
443
      // second last char is not '\\'
444
0
      p = &(buff->data[bufnum - 1]);
445
0
      if (is_esc(p[0]) && ((bufnum <= 1) || !is_esc(p[-1]))) {
446
0
        bufnum--;
447
0
        again = 1;
448
0
      }
449
0
    }
450
0
    if (again) {
451
0
      continue;
452
0
    }
453
0
    bufnum = 0;
454
0
    buf = buff->data;
455
456
0
    clear_comments(buf);
457
0
    s = eat_ws(buf);
458
0
    if (*s == '\0') {
459
0
      continue;  // blank line
460
0
    }
461
0
    if (*s == '[') {
462
0
      char *ss;
463
464
0
      s++;
465
0
      start = eat_ws(s);
466
0
      ss = start;
467
0
    again:
468
0
      end = eat_name(ss);
469
0
      p = eat_ws(end);
470
0
      if (*p != ']') {
471
0
        if (*p != '\0' && ss != p) {
472
0
          ss = p;
473
0
          goto again;
474
0
        }
475
0
        OPENSSL_PUT_ERROR(CONF, CONF_R_MISSING_CLOSE_SQUARE_BRACKET);
476
0
        goto err;
477
0
      }
478
0
      *end = '\0';
479
0
      if (!str_copy(conf, nullptr, &section, start)) {
480
0
        goto err;
481
0
      }
482
0
      if ((sv = get_section(conf, section)) == nullptr) {
483
0
        sv = NCONF_new_section(conf, section);
484
0
      }
485
0
      if (sv == nullptr) {
486
0
        OPENSSL_PUT_ERROR(CONF, CONF_R_UNABLE_TO_CREATE_NEW_SECTION);
487
0
        goto err;
488
0
      }
489
0
      continue;
490
0
    } else {
491
0
      pname = s;
492
0
      psection = nullptr;
493
0
      end = eat_name(s);
494
0
      if ((end[0] == ':') && (end[1] == ':')) {
495
0
        *end = '\0';
496
0
        end += 2;
497
0
        psection = pname;
498
0
        pname = end;
499
0
        end = eat_name(end);
500
0
      }
501
0
      p = eat_ws(end);
502
0
      if (*p != '=') {
503
0
        OPENSSL_PUT_ERROR(CONF, CONF_R_MISSING_EQUAL_SIGN);
504
0
        goto err;
505
0
      }
506
0
      *end = '\0';
507
0
      p++;
508
0
      start = eat_ws(p);
509
0
      while (*p != '\0') {
510
0
        p++;
511
0
      }
512
0
      p--;
513
0
      while (p != start && is_conf_ws(*p)) {
514
0
        p--;
515
0
      }
516
0
      p++;
517
0
      *p = '\0';
518
519
0
      if (!(v = CONF_VALUE_new())) {
520
0
        goto err;
521
0
      }
522
0
      if (psection == nullptr) {
523
0
        psection = section;
524
0
      }
525
0
      v->name = OPENSSL_strdup(pname);
526
0
      if (v->name == nullptr) {
527
0
        goto err;
528
0
      }
529
0
      if (!str_copy(conf, psection, &(v->value), start)) {
530
0
        goto err;
531
0
      }
532
533
0
      CONF_SECTION *tv;
534
0
      if (strcmp(psection, section) != 0) {
535
0
        if ((tv = get_section(conf, psection)) == nullptr) {
536
0
          tv = NCONF_new_section(conf, psection);
537
0
        }
538
0
        if (tv == nullptr) {
539
0
          OPENSSL_PUT_ERROR(CONF, CONF_R_UNABLE_TO_CREATE_NEW_SECTION);
540
0
          goto err;
541
0
        }
542
0
      } else {
543
0
        tv = sv;
544
0
      }
545
0
      if (add_string(conf, tv, v) == 0) {
546
0
        goto err;
547
0
      }
548
0
      v = nullptr;
549
0
    }
550
0
  }
551
0
  BUF_MEM_free(buff);
552
0
  Delete(section);
553
0
  return 1;
554
555
0
err:
556
0
  BUF_MEM_free(buff);
557
0
  Delete(section);
558
0
  if (out_error_line != nullptr) {
559
0
    *out_error_line = eline;
560
0
  }
561
0
  ERR_add_error_dataf("line %ld", eline);
562
0
  value_free(v);
563
0
  return 0;
564
0
}
565
566
0
int NCONF_load(CONF *conf, const char *filename, long *out_error_line) {
567
0
  BIO *in = BIO_new_file(filename, "rb");
568
0
  int ret;
569
570
0
  if (in == nullptr) {
571
0
    OPENSSL_PUT_ERROR(CONF, ERR_R_SYS_LIB);
572
0
    return 0;
573
0
  }
574
575
0
  ret = NCONF_load_bio(conf, in, out_error_line);
576
0
  BIO_free(in);
577
578
0
  return ret;
579
0
}
580
581
int bssl::CONF_parse_list(const char *list, char sep, int remove_whitespace,
582
                          int (*list_cb)(const char *elem, size_t len,
583
                                         void *usr),
584
0
                          void *arg) {
585
0
  int ret;
586
0
  const char *lstart, *tmpend, *p;
587
588
0
  if (list == nullptr) {
589
0
    OPENSSL_PUT_ERROR(CONF, CONF_R_LIST_CANNOT_BE_NULL);
590
0
    return 0;
591
0
  }
592
593
0
  lstart = list;
594
0
  for (;;) {
595
0
    if (remove_whitespace) {
596
0
      while (*lstart && OPENSSL_isspace((unsigned char)*lstart)) {
597
0
        lstart++;
598
0
      }
599
0
    }
600
0
    p = strchr(lstart, sep);
601
0
    if (p == lstart || !*lstart) {
602
0
      ret = list_cb(nullptr, 0, arg);
603
0
    } else {
604
0
      if (p) {
605
0
        tmpend = p - 1;
606
0
      } else {
607
0
        tmpend = lstart + strlen(lstart) - 1;
608
0
      }
609
0
      if (remove_whitespace) {
610
0
        while (OPENSSL_isspace((unsigned char)*tmpend)) {
611
0
          tmpend--;
612
0
        }
613
0
      }
614
0
      ret = list_cb(lstart, tmpend - lstart + 1, arg);
615
0
    }
616
0
    if (ret <= 0) {
617
0
      return ret;
618
0
    }
619
0
    if (p == nullptr) {
620
0
      return 1;
621
0
    }
622
0
    lstart = p + 1;
623
0
  }
624
0
}
625
626
int CONF_modules_load_file(const char *filename, const char *appname,
627
0
                           unsigned long flags) {
628
0
  return 1;
629
0
}
630
631
0
void CONF_modules_unload(int all) {}
632
633
0
void CONF_modules_free() {}
634
635
0
void OPENSSL_config(const char *config_name) {}
636
637
0
void OPENSSL_no_config() {}