/src/openssl/crypto/x509/v3_lib.c
Line | Count | Source |
1 | | /* |
2 | | * Copyright 1999-2025 The OpenSSL Project Authors. All Rights Reserved. |
3 | | * |
4 | | * Licensed under the Apache License 2.0 (the "License"). You may not use |
5 | | * this file except in compliance with the License. You can obtain a copy |
6 | | * in the file LICENSE in the source distribution or at |
7 | | * https://www.openssl.org/source/license.html |
8 | | */ |
9 | | |
10 | | /* X509 v3 extension utilities */ |
11 | | |
12 | | #include <stdio.h> |
13 | | #include "internal/cryptlib.h" |
14 | | #include <openssl/conf.h> |
15 | | #include <openssl/x509v3.h> |
16 | | |
17 | | #include "ext_dat.h" |
18 | | #include "x509_local.h" |
19 | | |
20 | | static STACK_OF(X509V3_EXT_METHOD) *ext_list = NULL; |
21 | | |
22 | | static int ext_cmp(const X509V3_EXT_METHOD *const *a, |
23 | | const X509V3_EXT_METHOD *const *b); |
24 | | static void ext_list_free(X509V3_EXT_METHOD *ext); |
25 | | |
26 | | int X509V3_EXT_add(X509V3_EXT_METHOD *ext) |
27 | 0 | { |
28 | 0 | if (ext_list == NULL |
29 | 0 | && (ext_list = sk_X509V3_EXT_METHOD_new(ext_cmp)) == NULL) { |
30 | 0 | ERR_raise(ERR_LIB_X509V3, ERR_R_CRYPTO_LIB); |
31 | 0 | return 0; |
32 | 0 | } |
33 | 0 | if (!sk_X509V3_EXT_METHOD_push(ext_list, ext)) { |
34 | 0 | ERR_raise(ERR_LIB_X509V3, ERR_R_CRYPTO_LIB); |
35 | 0 | return 0; |
36 | 0 | } |
37 | 0 | return 1; |
38 | 0 | } |
39 | | |
40 | | static int ext_cmp(const X509V3_EXT_METHOD *const *a, |
41 | | const X509V3_EXT_METHOD *const *b) |
42 | 0 | { |
43 | 0 | return ((*a)->ext_nid - (*b)->ext_nid); |
44 | 0 | } |
45 | | |
46 | | DECLARE_OBJ_BSEARCH_CMP_FN(const X509V3_EXT_METHOD *, |
47 | | const X509V3_EXT_METHOD *, ext); |
48 | | IMPLEMENT_OBJ_BSEARCH_CMP_FN(const X509V3_EXT_METHOD *, |
49 | | const X509V3_EXT_METHOD *, ext); |
50 | | |
51 | | #include "standard_exts.h" |
52 | | |
53 | | const X509V3_EXT_METHOD *X509V3_EXT_get_nid(int nid) |
54 | 0 | { |
55 | 0 | X509V3_EXT_METHOD tmp; |
56 | 0 | const X509V3_EXT_METHOD *t = &tmp, *const * ret; |
57 | 0 | int idx; |
58 | |
|
59 | 0 | if (nid < 0) |
60 | 0 | return NULL; |
61 | 0 | tmp.ext_nid = nid; |
62 | 0 | ret = OBJ_bsearch_ext(&t, standard_exts, STANDARD_EXTENSION_COUNT); |
63 | 0 | if (ret) |
64 | 0 | return *ret; |
65 | 0 | if (!ext_list) |
66 | 0 | return NULL; |
67 | | /* Ideally, this would be done under a lock */ |
68 | 0 | sk_X509V3_EXT_METHOD_sort(ext_list); |
69 | 0 | idx = sk_X509V3_EXT_METHOD_find(ext_list, &tmp); |
70 | | /* A failure to locate the item is handled by the value method */ |
71 | 0 | return sk_X509V3_EXT_METHOD_value(ext_list, idx); |
72 | 0 | } |
73 | | |
74 | | const X509V3_EXT_METHOD *X509V3_EXT_get(const X509_EXTENSION *ext) |
75 | 0 | { |
76 | 0 | int nid; |
77 | 0 | if ((nid = OBJ_obj2nid(X509_EXTENSION_get_object(ext))) == NID_undef) |
78 | 0 | return NULL; |
79 | 0 | return X509V3_EXT_get_nid(nid); |
80 | 0 | } |
81 | | |
82 | | int X509V3_EXT_add_list(X509V3_EXT_METHOD *extlist) |
83 | 0 | { |
84 | 0 | for (; extlist->ext_nid != -1; extlist++) |
85 | 0 | if (!X509V3_EXT_add(extlist)) |
86 | 0 | return 0; |
87 | 0 | return 1; |
88 | 0 | } |
89 | | |
90 | | int X509V3_EXT_add_alias(int nid_to, int nid_from) |
91 | 0 | { |
92 | 0 | const X509V3_EXT_METHOD *ext; |
93 | 0 | X509V3_EXT_METHOD *tmpext; |
94 | |
|
95 | 0 | if ((ext = X509V3_EXT_get_nid(nid_from)) == NULL) { |
96 | 0 | ERR_raise(ERR_LIB_X509V3, X509V3_R_EXTENSION_NOT_FOUND); |
97 | 0 | return 0; |
98 | 0 | } |
99 | 0 | if ((tmpext = OPENSSL_malloc(sizeof(*tmpext))) == NULL) |
100 | 0 | return 0; |
101 | 0 | *tmpext = *ext; |
102 | 0 | tmpext->ext_nid = nid_to; |
103 | 0 | tmpext->ext_flags |= X509V3_EXT_DYNAMIC; |
104 | 0 | if (!X509V3_EXT_add(tmpext)) { |
105 | 0 | OPENSSL_free(tmpext); |
106 | 0 | return 0; |
107 | 0 | } |
108 | 0 | return 1; |
109 | 0 | } |
110 | | |
111 | | void X509V3_EXT_cleanup(void) |
112 | 0 | { |
113 | 0 | sk_X509V3_EXT_METHOD_pop_free(ext_list, ext_list_free); |
114 | 0 | ext_list = NULL; |
115 | 0 | } |
116 | | |
117 | | static void ext_list_free(X509V3_EXT_METHOD *ext) |
118 | 0 | { |
119 | 0 | if (ext->ext_flags & X509V3_EXT_DYNAMIC) |
120 | 0 | OPENSSL_free(ext); |
121 | 0 | } |
122 | | |
123 | | /* |
124 | | * Legacy function: we don't need to add standard extensions any more because |
125 | | * they are now kept in ext_dat.h. |
126 | | */ |
127 | | |
128 | | int X509V3_add_standard_extensions(void) |
129 | 0 | { |
130 | 0 | return 1; |
131 | 0 | } |
132 | | |
133 | | int ossl_ignored_x509_extension(const X509_EXTENSION *ex, int flags) |
134 | 0 | { |
135 | | /* |
136 | | * Empty OCTET STRINGs and empty SEQUENCEs encode to just two bytes of tag |
137 | | * (0x04 or 0x30) and length (0x00). We use this fact to suppress empty |
138 | | * AKID and SKID extensions that may be briefly generated when processing |
139 | | * the "= none" value or only ":nonss"-qualified AKIDs when the subject is |
140 | | * self-signed. |
141 | | * |
142 | | * The resulting extension is empty, and must not be retained, but does |
143 | | * serve to drop any previous value of the same extension, when called |
144 | | * via |
145 | | * - X509v3_add_extensions(), or |
146 | | * - either of X509V3_add1_i2d() or X509V3_EXT_add_nconf_sk(), |
147 | | * with a flags (or ctx->flags) value that allows replacement. |
148 | | */ |
149 | 0 | if (ex->value.length == 2 |
150 | 0 | && (ex->value.data[0] == 0x30 || ex->value.data[0] == 0x04)) { |
151 | 0 | ASN1_OBJECT *obj = ex->object; |
152 | 0 | ASN1_OBJECT *skid = OBJ_nid2obj(NID_subject_key_identifier); |
153 | 0 | ASN1_OBJECT *akid = OBJ_nid2obj(NID_authority_key_identifier); |
154 | |
|
155 | 0 | if (OBJ_cmp(obj, skid) == 0 || OBJ_cmp(obj, akid) == 0) { |
156 | 0 | if ((flags & X509V3_ADD_SILENT) == 0) |
157 | 0 | ERR_raise_data(ERR_LIB_X509, X509_R_INVALID_EXTENSION, |
158 | 0 | "Invalid empty X.509 %s extension", obj->sn); |
159 | 0 | return 1; |
160 | 0 | } |
161 | 0 | } |
162 | 0 | return 0; |
163 | 0 | } |
164 | | |
165 | | /* Return an extension internal structure */ |
166 | | |
167 | | void *X509V3_EXT_d2i(const X509_EXTENSION *ext) |
168 | 0 | { |
169 | 0 | const X509V3_EXT_METHOD *method; |
170 | 0 | const unsigned char *p; |
171 | 0 | const ASN1_STRING *extvalue; |
172 | 0 | int extlen; |
173 | |
|
174 | 0 | if ((method = X509V3_EXT_get(ext)) == NULL) |
175 | 0 | return NULL; |
176 | 0 | extvalue = X509_EXTENSION_get_data(ext); |
177 | 0 | p = ASN1_STRING_get0_data(extvalue); |
178 | 0 | extlen = ASN1_STRING_length(extvalue); |
179 | 0 | if (method->it) |
180 | 0 | return ASN1_item_d2i(NULL, &p, extlen, ASN1_ITEM_ptr(method->it)); |
181 | 0 | return method->d2i(NULL, &p, extlen); |
182 | 0 | } |
183 | | |
184 | | /*- |
185 | | * Get critical flag and decoded version of extension from a NID. |
186 | | * The "idx" variable returns the last found extension and can |
187 | | * be used to retrieve multiple extensions of the same NID. |
188 | | * However multiple extensions with the same NID is usually |
189 | | * due to a badly encoded certificate so if idx is NULL we |
190 | | * choke if multiple extensions exist. |
191 | | * The "crit" variable is set to the critical value. |
192 | | * The return value is the decoded extension or NULL on |
193 | | * error. The actual error can have several different causes, |
194 | | * the value of *crit reflects the cause: |
195 | | * >= 0, extension found but not decoded (reflects critical value). |
196 | | * -1 extension not found. |
197 | | * -2 extension occurs more than once. |
198 | | */ |
199 | | |
200 | | void *X509V3_get_d2i(const STACK_OF(X509_EXTENSION) *x, int nid, int *crit, |
201 | | int *idx) |
202 | 0 | { |
203 | 0 | int lastpos, i; |
204 | 0 | X509_EXTENSION *ex, *found_ex = NULL; |
205 | |
|
206 | 0 | if (!x) { |
207 | 0 | if (idx) |
208 | 0 | *idx = -1; |
209 | 0 | if (crit) |
210 | 0 | *crit = -1; |
211 | 0 | return NULL; |
212 | 0 | } |
213 | 0 | if (idx) |
214 | 0 | lastpos = *idx + 1; |
215 | 0 | else |
216 | 0 | lastpos = 0; |
217 | 0 | if (lastpos < 0) |
218 | 0 | lastpos = 0; |
219 | 0 | for (i = lastpos; i < sk_X509_EXTENSION_num(x); i++) { |
220 | 0 | ex = sk_X509_EXTENSION_value(x, i); |
221 | 0 | if (OBJ_obj2nid(X509_EXTENSION_get_object(ex)) == nid) { |
222 | 0 | if (idx) { |
223 | 0 | *idx = i; |
224 | 0 | found_ex = ex; |
225 | 0 | break; |
226 | 0 | } else if (found_ex) { |
227 | | /* Found more than one */ |
228 | 0 | if (crit) |
229 | 0 | *crit = -2; |
230 | 0 | return NULL; |
231 | 0 | } |
232 | 0 | found_ex = ex; |
233 | 0 | } |
234 | 0 | } |
235 | 0 | if (found_ex) { |
236 | | /* Found it */ |
237 | 0 | if (crit) |
238 | 0 | *crit = X509_EXTENSION_get_critical(found_ex); |
239 | 0 | return X509V3_EXT_d2i(found_ex); |
240 | 0 | } |
241 | | |
242 | | /* Extension not found */ |
243 | 0 | if (idx) |
244 | 0 | *idx = -1; |
245 | 0 | if (crit) |
246 | 0 | *crit = -1; |
247 | 0 | return NULL; |
248 | 0 | } |
249 | | |
250 | | /* |
251 | | * This function is a general extension append, replace and delete utility. |
252 | | * The precise operation is governed by the 'flags' value. The 'crit' and |
253 | | * 'value' arguments (if relevant) are the extensions internal structure. |
254 | | */ |
255 | | |
256 | | int X509V3_add1_i2d(STACK_OF(X509_EXTENSION) **x, int nid, void *value, |
257 | | int crit, unsigned long flags) |
258 | 0 | { |
259 | 0 | int errcode, extidx = -1; |
260 | 0 | X509_EXTENSION *ext = NULL, *extmp; |
261 | 0 | STACK_OF(X509_EXTENSION) *ret = NULL; |
262 | 0 | unsigned long ext_op = flags & X509V3_ADD_OP_MASK; |
263 | | |
264 | | /* |
265 | | * If appending we don't care if it exists, otherwise look for existing |
266 | | * extension. |
267 | | */ |
268 | 0 | if (ext_op != X509V3_ADD_APPEND) |
269 | 0 | extidx = X509v3_get_ext_by_NID(*x, nid, -1); |
270 | | |
271 | | /* See if extension exists */ |
272 | 0 | if (extidx >= 0) { |
273 | | /* If keep existing, nothing to do */ |
274 | 0 | if (ext_op == X509V3_ADD_KEEP_EXISTING) |
275 | 0 | return 1; |
276 | | /* If default then its an error */ |
277 | 0 | if (ext_op == X509V3_ADD_DEFAULT) { |
278 | 0 | errcode = X509V3_R_EXTENSION_EXISTS; |
279 | 0 | goto err; |
280 | 0 | } |
281 | | /* If delete, just delete it */ |
282 | 0 | if (ext_op == X509V3_ADD_DELETE) { |
283 | 0 | extmp = sk_X509_EXTENSION_delete(*x, extidx); |
284 | 0 | if (extmp == NULL) |
285 | 0 | return -1; |
286 | 0 | X509_EXTENSION_free(extmp); |
287 | 0 | return 1; |
288 | 0 | } |
289 | 0 | } else { |
290 | | /* |
291 | | * If replace existing or delete, error since extension must exist |
292 | | */ |
293 | 0 | if ((ext_op == X509V3_ADD_REPLACE_EXISTING) || (ext_op == X509V3_ADD_DELETE)) { |
294 | 0 | errcode = X509V3_R_EXTENSION_NOT_FOUND; |
295 | 0 | goto err; |
296 | 0 | } |
297 | 0 | } |
298 | | |
299 | | /* |
300 | | * If we get this far then we have to create an extension: could have |
301 | | * some flags for alternative encoding schemes... |
302 | | */ |
303 | | |
304 | 0 | ext = X509V3_EXT_i2d(nid, crit, value); |
305 | |
|
306 | 0 | if (!ext) { |
307 | 0 | ERR_raise(ERR_LIB_X509V3, X509V3_R_ERROR_CREATING_EXTENSION); |
308 | 0 | return 0; |
309 | 0 | } |
310 | | |
311 | | /* If extension exists replace it.. */ |
312 | 0 | if (extidx >= 0) { |
313 | 0 | extmp = sk_X509_EXTENSION_value(*x, extidx); |
314 | 0 | if (ossl_ignored_x509_extension(ext, X509V3_ADD_SILENT)) { |
315 | 0 | if (!sk_X509_EXTENSION_delete(*x, extidx)) |
316 | 0 | return -1; |
317 | 0 | } else if (!sk_X509_EXTENSION_set(*x, extidx, ext)) { |
318 | 0 | return -1; |
319 | 0 | } |
320 | 0 | X509_EXTENSION_free(extmp); |
321 | 0 | return 1; |
322 | 0 | } |
323 | | |
324 | 0 | ret = *x; |
325 | 0 | if (*x == NULL |
326 | 0 | && (ret = sk_X509_EXTENSION_new_null()) == NULL) |
327 | 0 | goto m_fail; |
328 | 0 | if (!sk_X509_EXTENSION_push(ret, ext)) |
329 | 0 | goto m_fail; |
330 | | |
331 | 0 | *x = ret; |
332 | 0 | return 1; |
333 | | |
334 | 0 | m_fail: |
335 | | /* ERR_raise(ERR_LIB_X509V3, ERR_R_CRYPTO_LIB); */ |
336 | 0 | if (ret != *x) |
337 | 0 | sk_X509_EXTENSION_free(ret); |
338 | 0 | X509_EXTENSION_free(ext); |
339 | 0 | return -1; |
340 | | |
341 | 0 | err: |
342 | 0 | if (!(flags & X509V3_ADD_SILENT)) |
343 | | ERR_raise(ERR_LIB_X509V3, errcode); |
344 | 0 | return 0; |
345 | 0 | } |