Coverage Report

Created: 2025-09-05 06:13

/src/boringssl/crypto/obj/obj.cc
Line
Count
Source (jump to first uncovered line)
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/obj.h>
16
17
#include <inttypes.h>
18
#include <limits.h>
19
#include <string.h>
20
21
#include <iterator>
22
23
#include <openssl/asn1.h>
24
#include <openssl/bytestring.h>
25
#include <openssl/err.h>
26
#include <openssl/mem.h>
27
28
#include "../asn1/internal.h"
29
#include "../internal.h"
30
#include "../lhash/internal.h"
31
32
// obj_data.h must be included after the definition of |ASN1_OBJECT|.
33
#include "obj_dat.h"
34
35
36
DEFINE_LHASH_OF(ASN1_OBJECT)
37
38
static CRYPTO_MUTEX global_added_lock = CRYPTO_MUTEX_INIT;
39
// These globals are protected by |global_added_lock|.
40
static LHASH_OF(ASN1_OBJECT) *global_added_by_data = NULL;
41
static LHASH_OF(ASN1_OBJECT) *global_added_by_nid = NULL;
42
static LHASH_OF(ASN1_OBJECT) *global_added_by_short_name = NULL;
43
static LHASH_OF(ASN1_OBJECT) *global_added_by_long_name = NULL;
44
45
static CRYPTO_MUTEX global_next_nid_lock = CRYPTO_MUTEX_INIT;
46
static unsigned global_next_nid = NUM_NID;
47
48
0
static int obj_next_nid(void) {
49
0
  CRYPTO_MUTEX_lock_write(&global_next_nid_lock);
50
0
  int ret = global_next_nid++;
51
0
  CRYPTO_MUTEX_unlock_write(&global_next_nid_lock);
52
0
  return ret;
53
0
}
54
55
2.83M
ASN1_OBJECT *OBJ_dup(const ASN1_OBJECT *o) {
56
2.83M
  ASN1_OBJECT *r;
57
2.83M
  unsigned char *data = NULL;
58
2.83M
  char *sn = NULL, *ln = NULL;
59
60
2.83M
  if (o == NULL) {
61
0
    return NULL;
62
0
  }
63
64
2.83M
  if (!(o->flags & ASN1_OBJECT_FLAG_DYNAMIC)) {
65
    // TODO(fork): this is a little dangerous.
66
18.6k
    return (ASN1_OBJECT *)o;
67
18.6k
  }
68
69
2.81M
  r = ASN1_OBJECT_new();
70
2.81M
  if (r == NULL) {
71
0
    OPENSSL_PUT_ERROR(OBJ, ERR_R_ASN1_LIB);
72
0
    return NULL;
73
0
  }
74
2.81M
  r->ln = r->sn = NULL;
75
76
  // once data is attached to an object, it remains const
77
2.81M
  r->data = reinterpret_cast<uint8_t *>(OPENSSL_memdup(o->data, o->length));
78
2.81M
  if (o->length != 0 && r->data == NULL) {
79
0
    goto err;
80
0
  }
81
82
2.81M
  r->length = o->length;
83
2.81M
  r->nid = o->nid;
84
85
2.81M
  if (o->ln != NULL) {
86
0
    ln = OPENSSL_strdup(o->ln);
87
0
    if (ln == NULL) {
88
0
      goto err;
89
0
    }
90
0
  }
91
92
2.81M
  if (o->sn != NULL) {
93
0
    sn = OPENSSL_strdup(o->sn);
94
0
    if (sn == NULL) {
95
0
      goto err;
96
0
    }
97
0
  }
98
99
2.81M
  r->sn = sn;
100
2.81M
  r->ln = ln;
101
102
2.81M
  r->flags =
103
2.81M
      o->flags | (ASN1_OBJECT_FLAG_DYNAMIC | ASN1_OBJECT_FLAG_DYNAMIC_STRINGS |
104
2.81M
                  ASN1_OBJECT_FLAG_DYNAMIC_DATA);
105
2.81M
  return r;
106
107
0
err:
108
0
  OPENSSL_free(ln);
109
0
  OPENSSL_free(sn);
110
0
  OPENSSL_free(data);
111
0
  OPENSSL_free(r);
112
0
  return NULL;
113
2.81M
}
114
115
2.26M
int OBJ_cmp(const ASN1_OBJECT *a, const ASN1_OBJECT *b) {
116
2.26M
  if (a->length < b->length) {
117
793k
    return -1;
118
1.47M
  } else if (a->length > b->length) {
119
115k
    return 1;
120
115k
  }
121
1.35M
  return OPENSSL_memcmp(a->data, b->data, a->length);
122
2.26M
}
123
124
0
const uint8_t *OBJ_get0_data(const ASN1_OBJECT *obj) {
125
0
  if (obj == NULL) {
126
0
    return NULL;
127
0
  }
128
129
0
  return obj->data;
130
0
}
131
132
0
size_t OBJ_length(const ASN1_OBJECT *obj) {
133
0
  if (obj == NULL || obj->length < 0) {
134
0
    return 0;
135
0
  }
136
137
0
  return (size_t)obj->length;
138
0
}
139
140
3.90M
static const ASN1_OBJECT *get_builtin_object(int nid) {
141
  // |NID_undef| is stored separately, so all the indices are off by one. The
142
  // caller of this function must have a valid built-in, non-undef NID.
143
3.90M
  BSSL_CHECK(nid > 0 && nid < NUM_NID);
144
3.90M
  return &kObjects[nid - 1];
145
3.90M
}
146
147
// obj_cmp is called to search the kNIDsInOIDOrder array. The |key| argument is
148
// an |ASN1_OBJECT|* that we're looking for and |element| is a pointer to an
149
// unsigned int in the array.
150
2.26M
static int obj_cmp(const void *key, const void *element) {
151
2.26M
  uint16_t nid = *((const uint16_t *)element);
152
2.26M
  return OBJ_cmp(reinterpret_cast<const ASN1_OBJECT *>(key),
153
2.26M
                 get_builtin_object(nid));
154
2.26M
}
155
156
266k
int OBJ_obj2nid(const ASN1_OBJECT *obj) {
157
266k
  if (obj == NULL) {
158
0
    return NID_undef;
159
0
  }
160
161
266k
  if (obj->nid != 0) {
162
13.7k
    return obj->nid;
163
13.7k
  }
164
165
252k
  CRYPTO_MUTEX_lock_read(&global_added_lock);
166
252k
  if (global_added_by_data != NULL) {
167
0
    ASN1_OBJECT *match;
168
169
0
    match = lh_ASN1_OBJECT_retrieve(global_added_by_data, obj);
170
0
    if (match != NULL) {
171
0
      CRYPTO_MUTEX_unlock_read(&global_added_lock);
172
0
      return match->nid;
173
0
    }
174
0
  }
175
252k
  CRYPTO_MUTEX_unlock_read(&global_added_lock);
176
177
252k
  const uint16_t *nid_ptr = reinterpret_cast<const uint16_t *>(
178
252k
      bsearch(obj, kNIDsInOIDOrder, std::size(kNIDsInOIDOrder),
179
252k
              sizeof(kNIDsInOIDOrder[0]), obj_cmp));
180
252k
  if (nid_ptr == NULL) {
181
115k
    return NID_undef;
182
115k
  }
183
184
137k
  return get_builtin_object(*nid_ptr)->nid;
185
252k
}
186
187
0
int OBJ_cbs2nid(const CBS *cbs) {
188
0
  if (CBS_len(cbs) > INT_MAX) {
189
0
    return NID_undef;
190
0
  }
191
192
0
  ASN1_OBJECT obj;
193
0
  OPENSSL_memset(&obj, 0, sizeof(obj));
194
0
  obj.data = CBS_data(cbs);
195
0
  obj.length = (int)CBS_len(cbs);
196
197
0
  return OBJ_obj2nid(&obj);
198
0
}
199
200
// short_name_cmp is called to search the kNIDsInShortNameOrder array. The
201
// |key| argument is name that we're looking for and |element| is a pointer to
202
// an unsigned int in the array.
203
1.15M
static int short_name_cmp(const void *key, const void *element) {
204
1.15M
  const char *name = (const char *)key;
205
1.15M
  uint16_t nid = *((const uint16_t *)element);
206
207
1.15M
  return strcmp(name, get_builtin_object(nid)->sn);
208
1.15M
}
209
210
128k
int OBJ_sn2nid(const char *short_name) {
211
128k
  CRYPTO_MUTEX_lock_read(&global_added_lock);
212
128k
  if (global_added_by_short_name != NULL) {
213
0
    ASN1_OBJECT *match, templ;
214
215
0
    templ.sn = short_name;
216
0
    match = lh_ASN1_OBJECT_retrieve(global_added_by_short_name, &templ);
217
0
    if (match != NULL) {
218
0
      CRYPTO_MUTEX_unlock_read(&global_added_lock);
219
0
      return match->nid;
220
0
    }
221
0
  }
222
128k
  CRYPTO_MUTEX_unlock_read(&global_added_lock);
223
224
128k
  const uint16_t *nid_ptr = reinterpret_cast<const uint16_t *>(bsearch(
225
128k
      short_name, kNIDsInShortNameOrder, std::size(kNIDsInShortNameOrder),
226
128k
      sizeof(kNIDsInShortNameOrder[0]), short_name_cmp));
227
128k
  if (nid_ptr == NULL) {
228
10.7k
    return NID_undef;
229
10.7k
  }
230
231
117k
  return get_builtin_object(*nid_ptr)->nid;
232
128k
}
233
234
// long_name_cmp is called to search the kNIDsInLongNameOrder array. The
235
// |key| argument is name that we're looking for and |element| is a pointer to
236
// an unsigned int in the array.
237
90.6k
static int long_name_cmp(const void *key, const void *element) {
238
90.6k
  const char *name = (const char *)key;
239
90.6k
  uint16_t nid = *((const uint16_t *)element);
240
241
90.6k
  return strcmp(name, get_builtin_object(nid)->ln);
242
90.6k
}
243
244
9.23k
int OBJ_ln2nid(const char *long_name) {
245
9.23k
  CRYPTO_MUTEX_lock_read(&global_added_lock);
246
9.23k
  if (global_added_by_long_name != NULL) {
247
0
    ASN1_OBJECT *match, templ;
248
249
0
    templ.ln = long_name;
250
0
    match = lh_ASN1_OBJECT_retrieve(global_added_by_long_name, &templ);
251
0
    if (match != NULL) {
252
0
      CRYPTO_MUTEX_unlock_read(&global_added_lock);
253
0
      return match->nid;
254
0
    }
255
0
  }
256
9.23k
  CRYPTO_MUTEX_unlock_read(&global_added_lock);
257
258
9.23k
  const uint16_t *nid_ptr = reinterpret_cast<const uint16_t *>(
259
9.23k
      bsearch(long_name, kNIDsInLongNameOrder, std::size(kNIDsInLongNameOrder),
260
9.23k
              sizeof(kNIDsInLongNameOrder[0]), long_name_cmp));
261
9.23k
  if (nid_ptr == NULL) {
262
8.45k
    return NID_undef;
263
8.45k
  }
264
265
778
  return get_builtin_object(*nid_ptr)->nid;
266
9.23k
}
267
268
0
int OBJ_txt2nid(const char *s) {
269
0
  ASN1_OBJECT *obj;
270
0
  int nid;
271
272
0
  obj = OBJ_txt2obj(s, 0 /* search names */);
273
0
  nid = OBJ_obj2nid(obj);
274
0
  ASN1_OBJECT_free(obj);
275
0
  return nid;
276
0
}
277
278
0
OPENSSL_EXPORT int OBJ_nid2cbb(CBB *out, int nid) {
279
0
  const ASN1_OBJECT *obj = OBJ_nid2obj(nid);
280
0
  return obj != NULL &&
281
0
         CBB_add_asn1_element(out, CBS_ASN1_OBJECT, obj->data, obj->length);
282
0
}
283
284
2.92M
const ASN1_OBJECT *OBJ_get_undef(void) {
285
2.92M
  static const ASN1_OBJECT kUndef = {
286
2.92M
      /*sn=*/SN_undef,
287
2.92M
      /*ln=*/LN_undef,
288
2.92M
      /*nid=*/NID_undef,
289
2.92M
      /*length=*/0,
290
      /*data=*/NULL,
291
2.92M
      /*flags=*/0,
292
2.92M
  };
293
2.92M
  return &kUndef;
294
2.92M
}
295
296
140k
ASN1_OBJECT *OBJ_nid2obj(int nid) {
297
140k
  if (nid == NID_undef) {
298
0
    return (ASN1_OBJECT *)OBJ_get_undef();
299
0
  }
300
301
140k
  if (nid > 0 && nid < NUM_NID) {
302
140k
    const ASN1_OBJECT *obj = get_builtin_object(nid);
303
140k
    if (nid != NID_undef && obj->nid == NID_undef) {
304
0
      goto err;
305
0
    }
306
140k
    return (ASN1_OBJECT *)obj;
307
140k
  }
308
309
0
  CRYPTO_MUTEX_lock_read(&global_added_lock);
310
0
  if (global_added_by_nid != NULL) {
311
0
    ASN1_OBJECT *match, templ;
312
313
0
    templ.nid = nid;
314
0
    match = lh_ASN1_OBJECT_retrieve(global_added_by_nid, &templ);
315
0
    if (match != NULL) {
316
0
      CRYPTO_MUTEX_unlock_read(&global_added_lock);
317
0
      return match;
318
0
    }
319
0
  }
320
0
  CRYPTO_MUTEX_unlock_read(&global_added_lock);
321
322
0
err:
323
0
  OPENSSL_PUT_ERROR(OBJ, OBJ_R_UNKNOWN_NID);
324
0
  return NULL;
325
0
}
326
327
4.80k
const char *OBJ_nid2sn(int nid) {
328
4.80k
  const ASN1_OBJECT *obj = OBJ_nid2obj(nid);
329
4.80k
  if (obj == NULL) {
330
0
    return NULL;
331
0
  }
332
333
4.80k
  return obj->sn;
334
4.80k
}
335
336
14.0k
const char *OBJ_nid2ln(int nid) {
337
14.0k
  const ASN1_OBJECT *obj = OBJ_nid2obj(nid);
338
14.0k
  if (obj == NULL) {
339
0
    return NULL;
340
0
  }
341
342
14.0k
  return obj->ln;
343
14.0k
}
344
345
static ASN1_OBJECT *create_object_with_text_oid(int (*get_nid)(void),
346
                                                const char *oid,
347
                                                const char *short_name,
348
8.45k
                                                const char *long_name) {
349
8.45k
  uint8_t *buf;
350
8.45k
  size_t len;
351
8.45k
  CBB cbb;
352
8.45k
  if (!CBB_init(&cbb, 32) ||
353
8.45k
      !CBB_add_asn1_oid_from_text(&cbb, oid, strlen(oid)) ||
354
8.45k
      !CBB_finish(&cbb, &buf, &len)) {
355
1.60k
    OPENSSL_PUT_ERROR(OBJ, OBJ_R_INVALID_OID_STRING);
356
1.60k
    CBB_cleanup(&cbb);
357
1.60k
    return NULL;
358
1.60k
  }
359
360
6.84k
  ASN1_OBJECT *ret = ASN1_OBJECT_create(get_nid ? get_nid() : NID_undef, buf,
361
6.84k
                                        len, short_name, long_name);
362
6.84k
  OPENSSL_free(buf);
363
6.84k
  return ret;
364
8.45k
}
365
366
116k
ASN1_OBJECT *OBJ_txt2obj(const char *s, int dont_search_names) {
367
116k
  if (!dont_search_names) {
368
116k
    int nid = OBJ_sn2nid(s);
369
116k
    if (nid == NID_undef) {
370
9.23k
      nid = OBJ_ln2nid(s);
371
9.23k
    }
372
373
116k
    if (nid != NID_undef) {
374
108k
      return OBJ_nid2obj(nid);
375
108k
    }
376
116k
  }
377
378
8.45k
  return create_object_with_text_oid(NULL, s, NULL, NULL);
379
116k
}
380
381
37.0k
static int strlcpy_int(char *dst, const char *src, int dst_size) {
382
37.0k
  size_t ret = OPENSSL_strlcpy(dst, src, dst_size < 0 ? 0 : (size_t)dst_size);
383
37.0k
  if (ret > INT_MAX) {
384
0
    OPENSSL_PUT_ERROR(OBJ, ERR_R_OVERFLOW);
385
0
    return -1;
386
0
  }
387
37.0k
  return (int)ret;
388
37.0k
}
389
390
int OBJ_obj2txt(char *out, int out_len, const ASN1_OBJECT *obj,
391
37.6k
                int always_return_oid) {
392
  // Python depends on the empty OID successfully encoding as the empty
393
  // string.
394
37.6k
  if (obj == NULL || obj->length == 0) {
395
0
    return strlcpy_int(out, "", out_len);
396
0
  }
397
398
37.6k
  if (!always_return_oid) {
399
36.8k
    int nid = OBJ_obj2nid(obj);
400
36.8k
    if (nid != NID_undef) {
401
14.0k
      const char *name = OBJ_nid2ln(nid);
402
14.0k
      if (name == NULL) {
403
0
        name = OBJ_nid2sn(nid);
404
0
      }
405
14.0k
      if (name != NULL) {
406
14.0k
        return strlcpy_int(out, name, out_len);
407
14.0k
      }
408
14.0k
    }
409
36.8k
  }
410
411
23.6k
  CBS cbs;
412
23.6k
  CBS_init(&cbs, obj->data, obj->length);
413
23.6k
  char *txt = CBS_asn1_oid_to_text(&cbs);
414
23.6k
  if (txt == NULL) {
415
607
    if (out_len > 0) {
416
607
      out[0] = '\0';
417
607
    }
418
607
    return -1;
419
607
  }
420
421
22.9k
  int ret = strlcpy_int(out, txt, out_len);
422
22.9k
  OPENSSL_free(txt);
423
22.9k
  return ret;
424
23.6k
}
425
426
0
static uint32_t hash_nid(const ASN1_OBJECT *obj) { return obj->nid; }
427
428
0
static int cmp_nid(const ASN1_OBJECT *a, const ASN1_OBJECT *b) {
429
0
  return a->nid - b->nid;
430
0
}
431
432
0
static uint32_t hash_data(const ASN1_OBJECT *obj) {
433
0
  return OPENSSL_hash32(obj->data, obj->length);
434
0
}
435
436
0
static uint32_t hash_short_name(const ASN1_OBJECT *obj) {
437
0
  return OPENSSL_strhash(obj->sn);
438
0
}
439
440
0
static int cmp_short_name(const ASN1_OBJECT *a, const ASN1_OBJECT *b) {
441
0
  return strcmp(a->sn, b->sn);
442
0
}
443
444
0
static uint32_t hash_long_name(const ASN1_OBJECT *obj) {
445
0
  return OPENSSL_strhash(obj->ln);
446
0
}
447
448
0
static int cmp_long_name(const ASN1_OBJECT *a, const ASN1_OBJECT *b) {
449
0
  return strcmp(a->ln, b->ln);
450
0
}
451
452
// obj_add_object inserts |obj| into the various global hashes for run-time
453
// added objects. It returns one on success or zero otherwise.
454
0
static int obj_add_object(ASN1_OBJECT *obj) {
455
0
  obj->flags &= ~(ASN1_OBJECT_FLAG_DYNAMIC | ASN1_OBJECT_FLAG_DYNAMIC_STRINGS |
456
0
                  ASN1_OBJECT_FLAG_DYNAMIC_DATA);
457
458
0
  CRYPTO_MUTEX_lock_write(&global_added_lock);
459
0
  if (global_added_by_nid == NULL) {
460
0
    global_added_by_nid = lh_ASN1_OBJECT_new(hash_nid, cmp_nid);
461
0
  }
462
0
  if (global_added_by_data == NULL) {
463
0
    global_added_by_data = lh_ASN1_OBJECT_new(hash_data, OBJ_cmp);
464
0
  }
465
0
  if (global_added_by_short_name == NULL) {
466
0
    global_added_by_short_name =
467
0
        lh_ASN1_OBJECT_new(hash_short_name, cmp_short_name);
468
0
  }
469
0
  if (global_added_by_long_name == NULL) {
470
0
    global_added_by_long_name =
471
0
        lh_ASN1_OBJECT_new(hash_long_name, cmp_long_name);
472
0
  }
473
474
0
  int ok = 0;
475
0
  if (global_added_by_nid == NULL ||         //
476
0
      global_added_by_data == NULL ||        //
477
0
      global_added_by_short_name == NULL ||  //
478
0
      global_added_by_long_name == NULL) {
479
0
    goto err;
480
0
  }
481
482
  // We don't pay attention to |old_object| (which contains any previous object
483
  // that was evicted from the hashes) because we don't have a reference count
484
  // on ASN1_OBJECT values. Also, we should never have duplicates nids and so
485
  // should always have objects in |global_added_by_nid|.
486
0
  ASN1_OBJECT *old_object;
487
0
  ok = lh_ASN1_OBJECT_insert(global_added_by_nid, &old_object, obj);
488
0
  if (obj->length != 0 && obj->data != NULL) {
489
0
    ok &= lh_ASN1_OBJECT_insert(global_added_by_data, &old_object, obj);
490
0
  }
491
0
  if (obj->sn != NULL) {
492
0
    ok &= lh_ASN1_OBJECT_insert(global_added_by_short_name, &old_object, obj);
493
0
  }
494
0
  if (obj->ln != NULL) {
495
0
    ok &= lh_ASN1_OBJECT_insert(global_added_by_long_name, &old_object, obj);
496
0
  }
497
498
0
err:
499
0
  CRYPTO_MUTEX_unlock_write(&global_added_lock);
500
0
  return ok;
501
0
}
502
503
0
int OBJ_create(const char *oid, const char *short_name, const char *long_name) {
504
0
  ASN1_OBJECT *op =
505
0
      create_object_with_text_oid(obj_next_nid, oid, short_name, long_name);
506
0
  if (op == NULL || !obj_add_object(op)) {
507
0
    return NID_undef;
508
0
  }
509
0
  return op->nid;
510
0
}
511
512
0
void OBJ_cleanup(void) {}