Coverage Report

Created: 2026-10-27 07:01

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/boringssl/crypto/x509/v3_conf.cc
Line
Count
Source
1
// Copyright 1999-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
// extension creation utilities
16
17
#include <ctype.h>
18
#include <limits.h>
19
#include <stdio.h>
20
#include <string.h>
21
22
#include <openssl/conf.h>
23
#include <openssl/err.h>
24
#include <openssl/mem.h>
25
#include <openssl/obj.h>
26
#include <openssl/x509.h>
27
28
#include "../internal.h"
29
#include "internal.h"
30
31
32
using namespace bssl;
33
34
namespace {
35
36
X509_EXTENSION *do_ext_i2d(const X509V3_EXT_METHOD *method, int ext_nid,
37
6.01k
                           int crit, void *ext_struc) {
38
  // Convert the extension's internal representation to DER.
39
6.01k
  unsigned char *ext_der = nullptr;
40
6.01k
  int ext_len = ASN1_item_i2d(reinterpret_cast<ASN1_VALUE *>(ext_struc),
41
6.01k
                              &ext_der, ASN1_ITEM_ptr(method->it));
42
6.01k
  if (ext_len < 0) {
43
128
    return nullptr;
44
128
  }
45
46
5.88k
  ASN1_OCTET_STRING *ext_oct = ASN1_OCTET_STRING_new();
47
5.88k
  if (ext_oct == nullptr) {
48
0
    OPENSSL_free(ext_der);
49
0
    return nullptr;
50
0
  }
51
5.88k
  ASN1_STRING_set0(ext_oct, ext_der, ext_len);
52
53
5.88k
  X509_EXTENSION *ext =
54
5.88k
      X509_EXTENSION_create_by_NID(nullptr, ext_nid, crit, ext_oct);
55
5.88k
  ASN1_OCTET_STRING_free(ext_oct);
56
5.88k
  return ext;
57
5.88k
}
58
59
// CONF *conf:  Config file
60
// char *value:  Value
61
X509_EXTENSION *do_ext_nconf(const CONF *conf, const X509V3_CTX *ctx,
62
12.3k
                             int ext_nid, int crit, const char *value) {
63
12.3k
  const X509V3_EXT_METHOD *method;
64
12.3k
  X509_EXTENSION *ext;
65
12.3k
  const STACK_OF(CONF_VALUE) *nval;
66
12.3k
  STACK_OF(CONF_VALUE) *nval_owned = nullptr;
67
12.3k
  void *ext_struc;
68
12.3k
  if (ext_nid == NID_undef) {
69
1.44k
    OPENSSL_PUT_ERROR(X509V3, X509V3_R_UNKNOWN_EXTENSION_NAME);
70
1.44k
    return nullptr;
71
1.44k
  }
72
10.8k
  if (!(method = X509V3_EXT_get_nid(ext_nid))) {
73
187
    OPENSSL_PUT_ERROR(X509V3, X509V3_R_UNKNOWN_EXTENSION);
74
187
    return nullptr;
75
187
  }
76
  // Now get internal extension representation based on type
77
10.7k
  if (method->v2i) {
78
9.20k
    if (*value == '@') {
79
      // TODO(davidben): This is the only place where `X509V3_EXT_nconf`'s
80
      // `conf` parameter is used. All other codepaths use the copy inside
81
      // `ctx`. Should this be switched and then the parameter ignored?
82
5
      if (conf == nullptr) {
83
0
        OPENSSL_PUT_ERROR(X509V3, X509V3_R_NO_CONFIG_DATABASE);
84
0
        return nullptr;
85
0
      }
86
5
      nval = NCONF_get_section(conf, value + 1);
87
9.20k
    } else {
88
9.20k
      nval_owned = X509V3_parse_list(value);
89
9.20k
      nval = nval_owned;
90
9.20k
    }
91
9.20k
    if (nval == nullptr || sk_CONF_VALUE_num(nval) <= 0) {
92
221
      OPENSSL_PUT_ERROR(X509V3, X509V3_R_INVALID_EXTENSION_STRING);
93
221
      ERR_add_error_data(4, "name=", OBJ_nid2sn(ext_nid), ",section=", value);
94
221
      sk_CONF_VALUE_pop_free(nval_owned, X509V3_conf_free);
95
221
      return nullptr;
96
221
    }
97
8.98k
    ext_struc = method->v2i(method, ctx, nval);
98
8.98k
    sk_CONF_VALUE_pop_free(nval_owned, X509V3_conf_free);
99
8.98k
    if (!ext_struc) {
100
6.53k
      return nullptr;
101
6.53k
    }
102
8.98k
  } else if (method->s2i) {
103
214
    if (!(ext_struc = method->s2i(method, ctx, value))) {
104
60
      return nullptr;
105
60
    }
106
1.29k
  } else if (method->r2i) {
107
    // TODO(davidben): Should this check be removed? This matches OpenSSL, but
108
    // r2i-based extensions do not necessarily require a config database. The
109
    // two built-in extensions only use it some of the time, and already handle
110
    // `X509V3_get_section` returning NULL.
111
1.27k
    if (!ctx->db) {
112
637
      OPENSSL_PUT_ERROR(X509V3, X509V3_R_NO_CONFIG_DATABASE);
113
637
      return nullptr;
114
637
    }
115
638
    if (!(ext_struc = method->r2i(method, ctx, value))) {
116
476
      return nullptr;
117
476
    }
118
638
  } else {
119
15
    OPENSSL_PUT_ERROR(X509V3, X509V3_R_EXTENSION_SETTING_NOT_SUPPORTED);
120
15
    ERR_add_error_data(2, "name=", OBJ_nid2sn(ext_nid));
121
15
    return nullptr;
122
15
  }
123
124
2.76k
  ext = do_ext_i2d(method, ext_nid, crit, ext_struc);
125
2.76k
  ASN1_item_free(reinterpret_cast<ASN1_VALUE *>(ext_struc),
126
2.76k
                 ASN1_ITEM_ptr(method->it));
127
2.76k
  return ext;
128
10.7k
}
129
130
// Check the extension string for critical flag
131
29.0k
int v3_check_critical(const char **value) {
132
29.0k
  const char *p = *value;
133
29.0k
  if ((strlen(p) < 9) || strncmp(p, "critical,", 9)) {
134
28.7k
    return 0;
135
28.7k
  }
136
225
  p += 9;
137
637
  while (OPENSSL_isspace((unsigned char)*p)) {
138
412
    p++;
139
412
  }
140
225
  *value = p;
141
225
  return 1;
142
29.0k
}
143
144
// Check extension string for generic extension and return the type
145
29.0k
int v3_check_generic(const char **value) {
146
29.0k
  int gen_type = 0;
147
29.0k
  const char *p = *value;
148
29.0k
  if ((strlen(p) >= 4) && !strncmp(p, "DER:", 4)) {
149
4.73k
    p += 4;
150
4.73k
    gen_type = 1;
151
24.2k
  } else if ((strlen(p) >= 5) && !strncmp(p, "ASN1:", 5)) {
152
11.9k
    p += 5;
153
11.9k
    gen_type = 2;
154
12.3k
  } else {
155
12.3k
    return 0;
156
12.3k
  }
157
158
17.1k
  while (OPENSSL_isspace((unsigned char)*p)) {
159
499
    p++;
160
499
  }
161
16.6k
  *value = p;
162
16.6k
  return gen_type;
163
29.0k
}
164
165
unsigned char *generic_asn1(const char *value, const X509V3_CTX *ctx,
166
11.8k
                            size_t *ext_len) {
167
11.8k
  ASN1_TYPE *typ = ASN1_generate_v3(value, ctx);
168
11.8k
  if (typ == nullptr) {
169
6.13k
    return nullptr;
170
6.13k
  }
171
5.72k
  unsigned char *ext_der = nullptr;
172
5.72k
  int len = i2d_ASN1_TYPE(typ, &ext_der);
173
5.72k
  ASN1_TYPE_free(typ);
174
5.72k
  if (len < 0) {
175
0
    return nullptr;
176
0
  }
177
5.72k
  *ext_len = len;
178
5.72k
  return ext_der;
179
5.72k
}
180
181
// Create a generic extension: for now just handle DER type
182
X509_EXTENSION *v3_generic_extension(const char *ext, const char *value,
183
                                     int crit, int gen_type,
184
16.6k
                                     const X509V3_CTX *ctx) {
185
16.6k
  UniquePtr<ASN1_OBJECT> obj(OBJ_txt2obj(ext, 0));
186
16.6k
  if (obj == nullptr) {
187
1.26k
    OPENSSL_PUT_ERROR(X509V3, X509V3_R_EXTENSION_NAME_ERROR);
188
1.26k
    ERR_add_error_data(2, "name=", ext);
189
1.26k
    return nullptr;
190
1.26k
  }
191
192
15.4k
  UniquePtr<unsigned char> ext_der;
193
15.4k
  size_t ext_len = 0;
194
15.4k
  if (gen_type == 1) {
195
3.54k
    ext_der.reset(x509v3_hex_to_bytes(value, &ext_len));
196
11.8k
  } else if (gen_type == 2) {
197
11.8k
    ext_der.reset(generic_asn1(value, ctx, &ext_len));
198
11.8k
  }
199
200
15.4k
  if (ext_der == nullptr) {
201
6.20k
    OPENSSL_PUT_ERROR(X509V3, X509V3_R_EXTENSION_VALUE_ERROR);
202
6.20k
    ERR_add_error_data(2, "value=", value);
203
6.20k
    return nullptr;
204
6.20k
  }
205
206
9.20k
  if (ext_len > INT_MAX) {
207
0
    OPENSSL_PUT_ERROR(X509V3, ERR_R_OVERFLOW);
208
0
    return nullptr;
209
0
  }
210
211
9.20k
  UniquePtr<ASN1_OCTET_STRING> oct(ASN1_OCTET_STRING_new());
212
9.20k
  if (oct == nullptr) {
213
0
    return nullptr;
214
0
  }
215
216
9.20k
  ASN1_STRING_set0(oct.get(), ext_der.get(), (int)ext_len);
217
9.20k
  ext_der.release();  // ASN1_STRING_set0 took ownership.
218
219
9.20k
  return X509_EXTENSION_create_by_OBJ(nullptr, obj.get(), crit, oct.get());
220
9.20k
}
221
222
}  // namespace
223
224
X509_EXTENSION *X509V3_EXT_nconf(const CONF *conf, const X509V3_CTX *ctx,
225
29.0k
                                 const char *name, const char *value) {
226
  // If omitted, fill in an empty `X509V3_CTX`.
227
29.0k
  X509V3_CTX ctx_tmp;
228
29.0k
  if (ctx == nullptr) {
229
14.7k
    X509V3_set_ctx(&ctx_tmp, nullptr, nullptr, nullptr, nullptr, 0);
230
14.7k
    X509V3_set_nconf(&ctx_tmp, conf);
231
14.7k
    ctx = &ctx_tmp;
232
14.7k
  }
233
234
29.0k
  int crit = v3_check_critical(&value);
235
29.0k
  int ext_type = v3_check_generic(&value);
236
29.0k
  if (ext_type != 0) {
237
16.6k
    return v3_generic_extension(name, value, crit, ext_type, ctx);
238
16.6k
  }
239
12.3k
  X509_EXTENSION *ret = do_ext_nconf(conf, ctx, OBJ_sn2nid(name), crit, value);
240
12.3k
  if (!ret) {
241
9.69k
    OPENSSL_PUT_ERROR(X509V3, X509V3_R_ERROR_IN_EXTENSION);
242
9.69k
    ERR_add_error_data(4, "name=", name, ", value=", value);
243
9.69k
  }
244
12.3k
  return ret;
245
29.0k
}
246
247
X509_EXTENSION *X509V3_EXT_nconf_nid(const CONF *conf, const X509V3_CTX *ctx,
248
0
                                     int ext_nid, const char *value) {
249
  // If omitted, fill in an empty `X509V3_CTX`.
250
0
  X509V3_CTX ctx_tmp;
251
0
  if (ctx == nullptr) {
252
0
    X509V3_set_ctx(&ctx_tmp, nullptr, nullptr, nullptr, nullptr, 0);
253
0
    X509V3_set_nconf(&ctx_tmp, conf);
254
0
    ctx = &ctx_tmp;
255
0
  }
256
257
0
  int crit = v3_check_critical(&value);
258
0
  int ext_type = v3_check_generic(&value);
259
0
  if (ext_type != 0) {
260
0
    return v3_generic_extension(OBJ_nid2sn(ext_nid), value, crit, ext_type,
261
0
                                ctx);
262
0
  }
263
0
  return do_ext_nconf(conf, ctx, ext_nid, crit, value);
264
0
}
265
266
// Given an internal structure, nid and critical flag create an extension
267
268
3.24k
X509_EXTENSION *X509V3_EXT_i2d(int ext_nid, int crit, void *ext_struc) {
269
3.24k
  const X509V3_EXT_METHOD *method;
270
3.24k
  if (!(method = X509V3_EXT_get_nid(ext_nid))) {
271
0
    OPENSSL_PUT_ERROR(X509V3, X509V3_R_UNKNOWN_EXTENSION);
272
0
    return nullptr;
273
0
  }
274
3.24k
  return do_ext_i2d(method, ext_nid, crit, ext_struc);
275
3.24k
}
276
277
// This is the main function: add a bunch of extensions based on a config
278
// file section to an extension STACK.
279
280
int X509V3_EXT_add_nconf_sk(const CONF *conf, const X509V3_CTX *ctx,
281
                            const char *section,
282
24.4k
                            STACK_OF(X509_EXTENSION) **sk) {
283
24.4k
  const STACK_OF(CONF_VALUE) *nval = NCONF_get_section(conf, section);
284
24.4k
  if (nval == nullptr) {
285
0
    return 0;
286
0
  }
287
36.3k
  for (size_t i = 0; i < sk_CONF_VALUE_num(nval); i++) {
288
29.0k
    const CONF_VALUE *val = sk_CONF_VALUE_value(nval, i);
289
29.0k
    X509_EXTENSION *ext = X509V3_EXT_nconf(conf, ctx, val->name, val->value);
290
29.0k
    int ok = ext != nullptr &&  //
291
11.8k
             (sk == nullptr || X509v3_add_ext(sk, ext, -1) != nullptr);
292
29.0k
    X509_EXTENSION_free(ext);
293
29.0k
    if (!ok) {
294
17.1k
      return 0;
295
17.1k
    }
296
29.0k
  }
297
7.29k
  return 1;
298
24.4k
}
299
300
// Convenience functions to add extensions to a certificate, CRL and request
301
302
int X509V3_EXT_add_nconf(const CONF *conf, const X509V3_CTX *ctx,
303
24.4k
                         const char *section, X509 *cert) {
304
24.4k
  STACK_OF(X509_EXTENSION) **sk = nullptr;
305
24.4k
  if (cert) {
306
24.4k
    sk = &FromOpaque(cert)->extensions;
307
24.4k
  }
308
24.4k
  return X509V3_EXT_add_nconf_sk(conf, ctx, section, sk);
309
24.4k
}
310
311
// Same as above but for a CRL
312
313
int X509V3_EXT_CRL_add_nconf(const CONF *conf, const X509V3_CTX *ctx,
314
0
                             const char *section, X509_CRL *crl) {
315
0
  STACK_OF(X509_EXTENSION) **sk = nullptr;
316
0
  if (crl) {
317
0
    sk = &crl->crl->extensions;
318
0
  }
319
0
  return X509V3_EXT_add_nconf_sk(conf, ctx, section, sk);
320
0
}
321
322
// Add extensions to certificate request
323
324
int X509V3_EXT_REQ_add_nconf(const CONF *conf, const X509V3_CTX *ctx,
325
0
                             const char *section, X509_REQ *req) {
326
0
  STACK_OF(X509_EXTENSION) *extlist = nullptr, **sk = nullptr;
327
0
  int i;
328
0
  if (req) {
329
0
    sk = &extlist;
330
0
  }
331
0
  i = X509V3_EXT_add_nconf_sk(conf, ctx, section, sk);
332
0
  if (!i || !sk) {
333
0
    return i;
334
0
  }
335
0
  i = X509_REQ_add_extensions(req, extlist);
336
0
  sk_X509_EXTENSION_pop_free(extlist, X509_EXTENSION_free);
337
0
  return i;
338
0
}
339
340
// Config database functions
341
342
const STACK_OF(CONF_VALUE) *bssl::X509V3_get_section(const X509V3_CTX *ctx,
343
711k
                                                     const char *section) {
344
711k
  if (ctx->db == nullptr) {
345
4.22k
    OPENSSL_PUT_ERROR(X509V3, X509V3_R_OPERATION_NOT_DEFINED);
346
4.22k
    return nullptr;
347
4.22k
  }
348
707k
  return NCONF_get_section(ctx->db, section);
349
711k
}
350
351
14.7k
void X509V3_set_nconf(X509V3_CTX *ctx, const CONF *conf) { ctx->db = conf; }
352
353
void X509V3_set_ctx(X509V3_CTX *ctx, const X509 *issuer, const X509 *subj,
354
26.9k
                    const X509_REQ *req, const X509_CRL *crl, int flags) {
355
26.9k
  OPENSSL_memset(ctx, 0, sizeof(*ctx));
356
26.9k
  ctx->issuer_cert = issuer;
357
26.9k
  ctx->subject_cert = subj;
358
26.9k
  ctx->crl = crl;
359
26.9k
  ctx->subject_req = req;
360
26.9k
  ctx->flags = flags;
361
26.9k
}