Bug Summary

File:src/usr.sbin/rpki-client/x509.c
Warning:line 930, column 9
Although the value stored to 'as' is used in the enclosing expression, the value is never actually read from 'as'

Annotated Source Code

Press '?' to see keyboard shortcuts

clang -cc1 -cc1 -triple amd64-unknown-openbsd7.4 -analyze -disable-free -clear-ast-before-backend -disable-llvm-verifier -discard-value-names -main-file-name x509.c -analyzer-checker=core -analyzer-checker=apiModeling -analyzer-checker=unix -analyzer-checker=deadcode -analyzer-checker=security.insecureAPI.UncheckedReturn -analyzer-checker=security.insecureAPI.getpw -analyzer-checker=security.insecureAPI.gets -analyzer-checker=security.insecureAPI.mktemp -analyzer-checker=security.insecureAPI.mkstemp -analyzer-checker=security.insecureAPI.vfork -analyzer-checker=nullability.NullPassedToNonnull -analyzer-checker=nullability.NullReturnedFromNonnull -analyzer-output plist -w -setup-static-analyzer -mrelocation-model pic -pic-level 1 -pic-is-pie -mframe-pointer=all -relaxed-aliasing -ffp-contract=on -fno-rounding-math -mconstructor-aliases -funwind-tables=2 -target-cpu x86-64 -target-feature +retpoline-indirect-calls -target-feature +retpoline-indirect-branches -tune-cpu generic -debugger-tuning=gdb -fcoverage-compilation-dir=/usr/src/usr.sbin/rpki-client/obj -resource-dir /usr/local/llvm16/lib/clang/16 -I /usr/src/usr.sbin/rpki-client -internal-isystem /usr/local/llvm16/lib/clang/16/include -internal-externc-isystem /usr/include -O2 -fdebug-compilation-dir=/usr/src/usr.sbin/rpki-client/obj -ferror-limit 19 -fwrapv -D_RET_PROTECTOR -ret-protector -fcf-protection=branch -fno-jump-tables -fgnuc-version=4.2.1 -vectorize-loops -vectorize-slp -fno-builtin-malloc -fno-builtin-calloc -fno-builtin-realloc -fno-builtin-valloc -fno-builtin-free -fno-builtin-strdup -fno-builtin-strndup -analyzer-output=html -faddrsig -D__GCC_HAVE_DWARF2_CFI_ASM=1 -o /home/ben/Projects/scan/2024-01-11-140451-98009-1 -x c /usr/src/usr.sbin/rpki-client/x509.c
1/* $OpenBSD: x509.c,v 1.75 2023/11/16 11:10:59 tb Exp $ */
2/*
3 * Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
4 * Copyright (c) 2021 Claudio Jeker <claudio@openbsd.org>
5 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20#include <err.h>
21#include <stdlib.h>
22#include <string.h>
23#include <unistd.h>
24
25#include <openssl/evp.h>
26#include <openssl/x509v3.h>
27
28#include "extern.h"
29
30ASN1_OBJECT *certpol_oid; /* id-cp-ipAddr-asNumber cert policy */
31ASN1_OBJECT *carepo_oid; /* 1.3.6.1.5.5.7.48.5 (caRepository) */
32ASN1_OBJECT *manifest_oid; /* 1.3.6.1.5.5.7.48.10 (rpkiManifest) */
33ASN1_OBJECT *signedobj_oid; /* 1.3.6.1.5.5.7.48.11 (signedObject) */
34ASN1_OBJECT *notify_oid; /* 1.3.6.1.5.5.7.48.13 (rpkiNotify) */
35ASN1_OBJECT *roa_oid; /* id-ct-routeOriginAuthz CMS content type */
36ASN1_OBJECT *mft_oid; /* id-ct-rpkiManifest CMS content type */
37ASN1_OBJECT *gbr_oid; /* id-ct-rpkiGhostbusters CMS content type */
38ASN1_OBJECT *bgpsec_oid; /* id-kp-bgpsec-router Key Purpose */
39ASN1_OBJECT *cnt_type_oid; /* pkcs-9 id-contentType */
40ASN1_OBJECT *msg_dgst_oid; /* pkcs-9 id-messageDigest */
41ASN1_OBJECT *sign_time_oid; /* pkcs-9 id-signingTime */
42ASN1_OBJECT *bin_sign_time_oid; /* pkcs-9 id-aa-binarySigningTime */
43ASN1_OBJECT *rsc_oid; /* id-ct-signedChecklist */
44ASN1_OBJECT *aspa_oid; /* id-ct-ASPA */
45ASN1_OBJECT *tak_oid; /* id-ct-SignedTAL */
46ASN1_OBJECT *geofeed_oid; /* id-ct-geofeedCSVwithCRLF */
47
48static const struct {
49 const char *oid;
50 ASN1_OBJECT **ptr;
51} oid_table[] = {
52 {
53 .oid = "1.3.6.1.5.5.7.14.2",
54 .ptr = &certpol_oid,
55 },
56 {
57 .oid = "1.3.6.1.5.5.7.48.5",
58 .ptr = &carepo_oid,
59 },
60 {
61 .oid = "1.3.6.1.5.5.7.48.10",
62 .ptr = &manifest_oid,
63 },
64 {
65 .oid = "1.3.6.1.5.5.7.48.11",
66 .ptr = &signedobj_oid,
67 },
68 {
69 .oid = "1.3.6.1.5.5.7.48.13",
70 .ptr = &notify_oid,
71 },
72 {
73 .oid = "1.2.840.113549.1.9.16.1.24",
74 .ptr = &roa_oid,
75 },
76 {
77 .oid = "1.2.840.113549.1.9.16.1.26",
78 .ptr = &mft_oid,
79 },
80 {
81 .oid = "1.2.840.113549.1.9.16.1.35",
82 .ptr = &gbr_oid,
83 },
84 {
85 .oid = "1.3.6.1.5.5.7.3.30",
86 .ptr = &bgpsec_oid,
87 },
88 {
89 .oid = "1.2.840.113549.1.9.3",
90 .ptr = &cnt_type_oid,
91 },
92 {
93 .oid = "1.2.840.113549.1.9.4",
94 .ptr = &msg_dgst_oid,
95 },
96 {
97 .oid = "1.2.840.113549.1.9.5",
98 .ptr = &sign_time_oid,
99 },
100 {
101 .oid = "1.2.840.113549.1.9.16.2.46",
102 .ptr = &bin_sign_time_oid,
103 },
104 {
105 .oid = "1.2.840.113549.1.9.16.1.47",
106 .ptr = &geofeed_oid,
107 },
108 {
109 .oid = "1.2.840.113549.1.9.16.1.48",
110 .ptr = &rsc_oid,
111 },
112 {
113 .oid = "1.2.840.113549.1.9.16.1.49",
114 .ptr = &aspa_oid,
115 },
116 {
117 .oid = "1.2.840.113549.1.9.16.1.50",
118 .ptr = &tak_oid,
119 },
120};
121
122void
123x509_init_oid(void)
124{
125 size_t i;
126
127 for (i = 0; i < sizeof(oid_table) / sizeof(oid_table[0]); i++) {
128 *oid_table[i].ptr = OBJ_txt2obj(oid_table[i].oid, 1);
129 if (*oid_table[i].ptr == NULL((void *)0))
130 errx(1, "OBJ_txt2obj for %s failed", oid_table[i].oid);
131 }
132}
133
134/*
135 * Parse X509v3 authority key identifier (AKI), RFC 6487 sec. 4.8.3.
136 * Returns the AKI or NULL if it could not be parsed.
137 * The AKI is formatted as a hex string.
138 */
139int
140x509_get_aki(X509 *x, const char *fn, char **aki)
141{
142 const unsigned char *d;
143 AUTHORITY_KEYID *akid;
144 ASN1_OCTET_STRING *os;
145 int dsz, crit, rc = 0;
146
147 *aki = NULL((void *)0);
148 akid = X509_get_ext_d2i(x, NID_authority_key_identifier90, &crit, NULL((void *)0));
149 if (akid == NULL((void *)0)) {
150 if (crit != -1) {
151 warnx("%s: RFC 6487 section 4.8.3: error parsing AKI",
152 fn);
153 return 0;
154 }
155 return 1;
156 }
157 if (crit != 0) {
158 warnx("%s: RFC 6487 section 4.8.3: "
159 "AKI: extension not non-critical", fn);
160 goto out;
161 }
162 if (akid->issuer != NULL((void *)0) || akid->serial != NULL((void *)0)) {
163 warnx("%s: RFC 6487 section 4.8.3: AKI: "
164 "authorityCertIssuer or authorityCertSerialNumber present",
165 fn);
166 goto out;
167 }
168
169 os = akid->keyid;
170 if (os == NULL((void *)0)) {
171 warnx("%s: RFC 6487 section 4.8.3: AKI: "
172 "Key Identifier missing", fn);
173 goto out;
174 }
175
176 d = os->data;
177 dsz = os->length;
178
179 if (dsz != SHA_DIGEST_LENGTH20) {
180 warnx("%s: RFC 6487 section 4.8.2: AKI: "
181 "want %d bytes SHA1 hash, have %d bytes",
182 fn, SHA_DIGEST_LENGTH20, dsz);
183 goto out;
184 }
185
186 *aki = hex_encode(d, dsz);
187 rc = 1;
188out:
189 AUTHORITY_KEYID_free(akid);
190 return rc;
191}
192
193/*
194 * Parse X509v3 subject key identifier (SKI), RFC 6487 sec. 4.8.2.
195 * The SKI must be the SHA1 hash of the Subject Public Key.
196 * Returns the SKI formatted as hex string, or NULL if it couldn't be parsed.
197 */
198int
199x509_get_ski(X509 *x, const char *fn, char **ski)
200{
201 const unsigned char *d, *spk;
202 ASN1_OCTET_STRING *os;
203 X509_PUBKEY *pubkey;
204 unsigned char spkd[SHA_DIGEST_LENGTH20];
205 int crit, dsz, spkz, rc = 0;
206
207 *ski = NULL((void *)0);
208 os = X509_get_ext_d2i(x, NID_subject_key_identifier82, &crit, NULL((void *)0));
209 if (os == NULL((void *)0)) {
210 if (crit != -1) {
211 warnx("%s: RFC 6487 section 4.8.2: error parsing SKI",
212 fn);
213 return 0;
214 }
215 return 1;
216 }
217 if (crit != 0) {
218 warnx("%s: RFC 6487 section 4.8.2: "
219 "SKI: extension not non-critical", fn);
220 goto out;
221 }
222
223 d = os->data;
224 dsz = os->length;
225
226 if (dsz != SHA_DIGEST_LENGTH20) {
227 warnx("%s: RFC 6487 section 4.8.2: SKI: "
228 "want %d bytes SHA1 hash, have %d bytes",
229 fn, SHA_DIGEST_LENGTH20, dsz);
230 goto out;
231 }
232
233 if ((pubkey = X509_get_X509_PUBKEY(x)) == NULL((void *)0)) {
234 warnx("%s: X509_get_X509_PUBKEY", fn);
235 goto out;
236 }
237 if (!X509_PUBKEY_get0_param(NULL((void *)0), &spk, &spkz, NULL((void *)0), pubkey)) {
238 warnx("%s: X509_PUBKEY_get0_param", fn);
239 goto out;
240 }
241
242 if (!EVP_Digest(spk, spkz, spkd, NULL((void *)0), EVP_sha1(), NULL((void *)0))) {
243 warnx("%s: EVP_Digest failed", fn);
244 goto out;
245 }
246
247 if (memcmp(spkd, d, dsz) != 0) {
248 warnx("%s: SKI does not match SHA1 hash of SPK", fn);
249 goto out;
250 }
251
252 *ski = hex_encode(d, dsz);
253 rc = 1;
254 out:
255 ASN1_OCTET_STRING_free(os);
256 return rc;
257}
258
259/*
260 * Check the certificate's purpose: CA or BGPsec Router.
261 * Return a member of enum cert_purpose.
262 */
263enum cert_purpose
264x509_get_purpose(X509 *x, const char *fn)
265{
266 BASIC_CONSTRAINTS *bc = NULL((void *)0);
267 EXTENDED_KEY_USAGE *eku = NULL((void *)0);
268 int crit;
269 enum cert_purpose purpose = CERT_PURPOSE_INVALID;
270
271 if (X509_check_ca(x) == 1) {
272 bc = X509_get_ext_d2i(x, NID_basic_constraints87, &crit, NULL((void *)0));
273 if (bc == NULL((void *)0)) {
274 if (crit != -1)
275 warnx("%s: RFC 6487 section 4.8.1: "
276 "error parsing basic constraints", fn);
277 else
278 warnx("%s: RFC 6487 section 4.8.1: "
279 "missing basic constraints", fn);
280 goto out;
281 }
282 if (crit != 1) {
283 warnx("%s: RFC 6487 section 4.8.1: Basic Constraints "
284 "must be marked critical", fn);
285 goto out;
286 }
287 if (bc->pathlen != NULL((void *)0)) {
288 warnx("%s: RFC 6487 section 4.8.1: Path Length "
289 "Constraint must be absent", fn);
290 goto out;
291 }
292 purpose = CERT_PURPOSE_CA;
293 goto out;
294 }
295
296 if (X509_get_extension_flags(x) & EXFLAG_BCONS0x0001) {
297 warnx("%s: Basic Constraints ext in non-CA cert", fn);
298 goto out;
299 }
300
301 eku = X509_get_ext_d2i(x, NID_ext_key_usage126, &crit, NULL((void *)0));
302 if (eku == NULL((void *)0)) {
303 if (crit != -1)
304 warnx("%s: error parsing EKU", fn);
305 else
306 warnx("%s: EKU: extension missing", fn);
307 goto out;
308 }
309 if (crit != 0) {
310 warnx("%s: EKU: extension must not be marked critical", fn);
311 goto out;
312 }
313 if (sk_ASN1_OBJECT_num(eku)sk_num(((_STACK*) (1 ? (eku) : (struct stack_st_ASN1_OBJECT*)
0)))
!= 1) {
314 warnx("%s: EKU: expected 1 purpose, have %d", fn,
315 sk_ASN1_OBJECT_num(eku)sk_num(((_STACK*) (1 ? (eku) : (struct stack_st_ASN1_OBJECT*)
0)))
);
316 goto out;
317 }
318
319 if (OBJ_cmp(bgpsec_oid, sk_ASN1_OBJECT_value(eku, 0)((ASN1_OBJECT *)sk_value(((_STACK*) (1 ? (eku) : (struct stack_st_ASN1_OBJECT
*)0)), (0)))
) == 0) {
320 purpose = CERT_PURPOSE_BGPSEC_ROUTER;
321 goto out;
322 }
323
324 out:
325 BASIC_CONSTRAINTS_free(bc);
326 EXTENDED_KEY_USAGE_free(eku);
327 return purpose;
328}
329
330/*
331 * Extract Subject Public Key Info (SPKI) from BGPsec X.509 Certificate.
332 * Returns NULL on failure, on success return the SPKI as base64 encoded pubkey
333 */
334char *
335x509_get_pubkey(X509 *x, const char *fn)
336{
337 EVP_PKEY *pkey;
338 EC_KEY *eckey;
339 int nid;
340 const char *cname;
341 uint8_t *pubkey = NULL((void *)0);
342 char *res = NULL((void *)0);
343 int len;
344
345 pkey = X509_get0_pubkey(x);
346 if (pkey == NULL((void *)0)) {
347 warnx("%s: X509_get0_pubkey failed in %s", fn, __func__);
348 goto out;
349 }
350 if (EVP_PKEY_base_id(pkey) != EVP_PKEY_EC408) {
351 warnx("%s: Expected EVP_PKEY_EC, got %d", fn,
352 EVP_PKEY_base_id(pkey));
353 goto out;
354 }
355
356 eckey = EVP_PKEY_get0_EC_KEY(pkey);
357 if (eckey == NULL((void *)0)) {
358 warnx("%s: Incorrect key type", fn);
359 goto out;
360 }
361
362 nid = EC_GROUP_get_curve_name(EC_KEY_get0_group(eckey));
363 if (nid != NID_X9_62_prime256v1415) {
364 if ((cname = EC_curve_nid2nist(nid)) == NULL((void *)0))
365 cname = OBJ_nid2sn(nid);
366 warnx("%s: Expected P-256, got %s", fn, cname);
367 goto out;
368 }
369
370 if (!EC_KEY_check_key(eckey)) {
371 warnx("%s: EC_KEY_check_key failed in %s", fn, __func__);
372 goto out;
373 }
374
375 len = i2d_PUBKEY(pkey, &pubkey);
376 if (len <= 0) {
377 warnx("%s: i2d_PUBKEY failed in %s", fn, __func__);
378 goto out;
379 }
380
381 if (base64_encode(pubkey, len, &res) == -1)
382 errx(1, "base64_encode failed in %s", __func__);
383
384 out:
385 free(pubkey);
386 return res;
387}
388
389/*
390 * Parse the Authority Information Access (AIA) extension
391 * See RFC 6487, section 4.8.7 for details.
392 * Returns NULL on failure, on success returns the AIA URI
393 * (which has to be freed after use).
394 */
395int
396x509_get_aia(X509 *x, const char *fn, char **aia)
397{
398 ACCESS_DESCRIPTION *ad;
399 AUTHORITY_INFO_ACCESS *info;
400 int crit, rc = 0;
401
402 *aia = NULL((void *)0);
403 info = X509_get_ext_d2i(x, NID_info_access177, &crit, NULL((void *)0));
404 if (info == NULL((void *)0)) {
405 if (crit != -1) {
406 warnx("%s: RFC 6487 section 4.8.7: error parsing AIA",
407 fn);
408 return 0;
409 }
410 return 1;
411 }
412
413 if (crit != 0) {
414 warnx("%s: RFC 6487 section 4.8.7: "
415 "AIA: extension not non-critical", fn);
416 goto out;
417 }
418
419 if ((X509_get_extension_flags(x) & EXFLAG_SS0x2000) != 0) {
420 warnx("%s: RFC 6487 section 4.8.7: AIA must be absent from "
421 "a self-signed certificate", fn);
422 goto out;
423 }
424
425 if (sk_ACCESS_DESCRIPTION_num(info)sk_num(((_STACK*) (1 ? (info) : (struct stack_st_ACCESS_DESCRIPTION
*)0)))
!= 1) {
426 warnx("%s: RFC 6487 section 4.8.7: AIA: "
427 "want 1 element, have %d", fn,
428 sk_ACCESS_DESCRIPTION_num(info)sk_num(((_STACK*) (1 ? (info) : (struct stack_st_ACCESS_DESCRIPTION
*)0)))
);
429 goto out;
430 }
431
432 ad = sk_ACCESS_DESCRIPTION_value(info, 0)((ACCESS_DESCRIPTION *)sk_value(((_STACK*) (1 ? (info) : (struct
stack_st_ACCESS_DESCRIPTION*)0)), (0)))
;
433 if (OBJ_obj2nid(ad->method) != NID_ad_ca_issuers179) {
434 warnx("%s: RFC 6487 section 4.8.7: AIA: "
435 "expected caIssuers, have %d", fn, OBJ_obj2nid(ad->method));
436 goto out;
437 }
438
439 if (!x509_location(fn, "AIA: caIssuers", NULL((void *)0), ad->location, aia))
440 goto out;
441
442 rc = 1;
443
444out:
445 AUTHORITY_INFO_ACCESS_free(info);
446 return rc;
447}
448
449/*
450 * Parse the Subject Information Access (SIA) extension
451 * See RFC 6487, section 4.8.8 for details.
452 * Returns NULL on failure, on success returns the SIA signedObject URI
453 * (which has to be freed after use).
454 */
455int
456x509_get_sia(X509 *x, const char *fn, char **sia)
457{
458 ACCESS_DESCRIPTION *ad;
459 AUTHORITY_INFO_ACCESS *info;
460 ASN1_OBJECT *oid;
461 int i, crit, rsync_found = 0;
462
463 *sia = NULL((void *)0);
464
465 info = X509_get_ext_d2i(x, NID_sinfo_access398, &crit, NULL((void *)0));
466 if (info == NULL((void *)0)) {
467 if (crit != -1) {
468 warnx("%s: error parsing SIA", fn);
469 return 0;
470 }
471 return 1;
472 }
473
474 if (crit != 0) {
475 warnx("%s: RFC 6487 section 4.8.8: "
476 "SIA: extension not non-critical", fn);
477 goto out;
478 }
479
480 for (i = 0; i < sk_ACCESS_DESCRIPTION_num(info)sk_num(((_STACK*) (1 ? (info) : (struct stack_st_ACCESS_DESCRIPTION
*)0)))
; i++) {
481 ad = sk_ACCESS_DESCRIPTION_value(info, i)((ACCESS_DESCRIPTION *)sk_value(((_STACK*) (1 ? (info) : (struct
stack_st_ACCESS_DESCRIPTION*)0)), (i)))
;
482 oid = ad->method;
483
484 /*
485 * XXX: RFC 6487 4.8.8.2 states that the accessMethod MUST be
486 * signedObject. However, rpkiNotify accessMethods currently
487 * exist in the wild. Consider removing this special case.
488 * See also https://www.rfc-editor.org/errata/eid7239.
489 */
490 if (OBJ_cmp(oid, notify_oid) == 0) {
491 if (verbose > 1)
492 warnx("%s: RFC 6487 section 4.8.8.2: SIA should"
493 " not contain rpkiNotify accessMethod", fn);
494 continue;
495 }
496 if (OBJ_cmp(oid, signedobj_oid) != 0) {
497 char buf[128];
498
499 OBJ_obj2txt(buf, sizeof(buf), oid, 0);
500 warnx("%s: RFC 6487 section 4.8.8.2: unexpected"
501 " accessMethod: %s", fn, buf);
502 goto out;
503 }
504
505 /* Don't fail on non-rsync URI, so check this afterward. */
506 if (!x509_location(fn, "SIA: signedObject", NULL((void *)0), ad->location,
507 sia))
508 goto out;
509
510 if (rsync_found)
511 continue;
512
513 if (strncasecmp(*sia, "rsync://", 8) == 0) {
514 rsync_found = 1;
515 continue;
516 }
517
518 free(*sia);
519 *sia = NULL((void *)0);
520 }
521
522 if (!rsync_found) {
523 warnx("%s: RFC 6487 section 4.8.8.2: "
524 "SIA without rsync accessLocation", fn);
525 goto out;
526 }
527
528 AUTHORITY_INFO_ACCESS_free(info);
529 return 1;
530
531 out:
532 free(*sia);
533 *sia = NULL((void *)0);
534 AUTHORITY_INFO_ACCESS_free(info);
535 return 0;
536}
537
538/*
539 * Extract the notBefore of a certificate.
540 */
541int
542x509_get_notbefore(X509 *x, const char *fn, time_t *tt)
543{
544 const ASN1_TIME *at;
545
546 at = X509_get0_notBefore(x);
547 if (at == NULL((void *)0)) {
548 warnx("%s: X509_get0_notBefore failed", fn);
549 return 0;
550 }
551 if (!x509_get_time(at, tt)) {
552 warnx("%s: ASN1_TIME_to_tm failed", fn);
553 return 0;
554 }
555 return 1;
556}
557
558/*
559 * Extract the notAfter from a certificate.
560 */
561int
562x509_get_notafter(X509 *x, const char *fn, time_t *tt)
563{
564 const ASN1_TIME *at;
565
566 at = X509_get0_notAfter(x);
567 if (at == NULL((void *)0)) {
568 warnx("%s: X509_get0_notafter failed", fn);
569 return 0;
570 }
571 if (!x509_get_time(at, tt)) {
572 warnx("%s: ASN1_TIME_to_tm failed", fn);
573 return 0;
574 }
575 return 1;
576}
577
578/*
579 * Check whether all RFC 3779 extensions are set to inherit.
580 * Return 1 if both AS & IP are set to inherit.
581 * Return 0 on failure (such as missing extensions or no inheritance).
582 */
583int
584x509_inherits(X509 *x)
585{
586 STACK_OF(IPAddressFamily)struct stack_st_IPAddressFamily *addrblk = NULL((void *)0);
587 ASIdentifiers *asidentifiers = NULL((void *)0);
588 const IPAddressFamily *af;
589 int crit, i, rc = 0;
590
591 addrblk = X509_get_ext_d2i(x, NID_sbgp_ipAddrBlock290, &crit, NULL((void *)0));
592 if (addrblk == NULL((void *)0)) {
593 if (crit != -1)
594 warnx("error parsing ipAddrBlock");
595 goto out;
596 }
597
598 /*
599 * Check by hand, since X509v3_addr_inherits() success only means that
600 * at least one address family inherits, not all of them.
601 */
602 for (i = 0; i < sk_IPAddressFamily_num(addrblk)sk_num(((_STACK*) (1 ? (addrblk) : (struct stack_st_IPAddressFamily
*)0)))
; i++) {
603 af = sk_IPAddressFamily_value(addrblk, i)((IPAddressFamily *)sk_value(((_STACK*) (1 ? (addrblk) : (struct
stack_st_IPAddressFamily*)0)), (i)))
;
604 if (af->ipAddressChoice->type != IPAddressChoice_inherit0)
605 goto out;
606 }
607
608 asidentifiers = X509_get_ext_d2i(x, NID_sbgp_autonomousSysNum291, NULL((void *)0),
609 NULL((void *)0));
610 if (asidentifiers == NULL((void *)0)) {
611 if (crit != -1)
612 warnx("error parsing asIdentifiers");
613 goto out;
614 }
615
616 /* We need to have AS numbers and don't want RDIs. */
617 if (asidentifiers->asnum == NULL((void *)0) || asidentifiers->rdi != NULL((void *)0))
618 goto out;
619 if (!X509v3_asid_inherits(asidentifiers))
620 goto out;
621
622 rc = 1;
623 out:
624 ASIdentifiers_free(asidentifiers);
625 sk_IPAddressFamily_pop_free(addrblk, IPAddressFamily_free)sk_pop_free(((_STACK*) (1 ? (addrblk) : (struct stack_st_IPAddressFamily
*)0)), ((void (*)(void *)) ((1 ? (IPAddressFamily_free) : (void
(*)(IPAddressFamily *))0))))
;
626 return rc;
627}
628
629/*
630 * Check whether at least one RFC 3779 extension is set to inherit.
631 * Return 1 if an inherit element is encountered in AS or IP.
632 * Return 0 otherwise.
633 */
634int
635x509_any_inherits(X509 *x)
636{
637 STACK_OF(IPAddressFamily)struct stack_st_IPAddressFamily *addrblk = NULL((void *)0);
638 ASIdentifiers *asidentifiers = NULL((void *)0);
639 int crit, rc = 0;
640
641 addrblk = X509_get_ext_d2i(x, NID_sbgp_ipAddrBlock290, &crit, NULL((void *)0));
642 if (addrblk == NULL((void *)0) && crit != -1)
643 warnx("error parsing ipAddrBlock");
644 if (X509v3_addr_inherits(addrblk))
645 rc = 1;
646
647 asidentifiers = X509_get_ext_d2i(x, NID_sbgp_autonomousSysNum291, &crit,
648 NULL((void *)0));
649 if (asidentifiers == NULL((void *)0) && crit != -1)
650 warnx("error parsing asIdentifiers");
651 if (X509v3_asid_inherits(asidentifiers))
652 rc = 1;
653
654 ASIdentifiers_free(asidentifiers);
655 sk_IPAddressFamily_pop_free(addrblk, IPAddressFamily_free)sk_pop_free(((_STACK*) (1 ? (addrblk) : (struct stack_st_IPAddressFamily
*)0)), ((void (*)(void *)) ((1 ? (IPAddressFamily_free) : (void
(*)(IPAddressFamily *))0))))
;
656 return rc;
657}
658
659/*
660 * Parse the very specific subset of information in the CRL distribution
661 * point extension.
662 * See RFC 6487, section 4.8.6 for details.
663 * Returns NULL on failure, the crl URI on success which has to be freed
664 * after use.
665 */
666int
667x509_get_crl(X509 *x, const char *fn, char **crl)
668{
669 CRL_DIST_POINTS *crldp;
670 DIST_POINT *dp;
671 GENERAL_NAMES *names;
672 GENERAL_NAME *name;
673 int i, crit, rsync_found = 0;
674
675 *crl = NULL((void *)0);
676 crldp = X509_get_ext_d2i(x, NID_crl_distribution_points103, &crit, NULL((void *)0));
677 if (crldp == NULL((void *)0)) {
678 if (crit != -1) {
679 warnx("%s: RFC 6487 section 4.8.6: failed to parse "
680 "CRL distribution points", fn);
681 return 0;
682 }
683 return 1;
684 }
685
686 if (crit != 0) {
687 warnx("%s: RFC 6487 section 4.8.6: "
688 "CRL distribution point: extension not non-critical", fn);
689 goto out;
690 }
691
692 if (sk_DIST_POINT_num(crldp)sk_num(((_STACK*) (1 ? (crldp) : (struct stack_st_DIST_POINT*
)0)))
!= 1) {
693 warnx("%s: RFC 6487 section 4.8.6: CRL: "
694 "want 1 element, have %d", fn,
695 sk_DIST_POINT_num(crldp)sk_num(((_STACK*) (1 ? (crldp) : (struct stack_st_DIST_POINT*
)0)))
);
696 goto out;
697 }
698
699 dp = sk_DIST_POINT_value(crldp, 0)((DIST_POINT *)sk_value(((_STACK*) (1 ? (crldp) : (struct stack_st_DIST_POINT
*)0)), (0)))
;
700 if (dp->CRLissuer != NULL((void *)0)) {
701 warnx("%s: RFC 6487 section 4.8.6: CRL CRLIssuer field"
702 " disallowed", fn);
703 goto out;
704 }
705 if (dp->reasons != NULL((void *)0)) {
706 warnx("%s: RFC 6487 section 4.8.6: CRL Reasons field"
707 " disallowed", fn);
708 goto out;
709 }
710 if (dp->distpoint == NULL((void *)0)) {
711 warnx("%s: RFC 6487 section 4.8.6: CRL: "
712 "no distribution point name", fn);
713 goto out;
714 }
715 if (dp->distpoint->dpname != NULL((void *)0)) {
716 warnx("%s: RFC 6487 section 4.8.6: nameRelativeToCRLIssuer"
717 " disallowed", fn);
718 goto out;
719 }
720 if (dp->distpoint->type != 0) {
721 warnx("%s: RFC 6487 section 4.8.6: CRL: "
722 "expected GEN_OTHERNAME, have %d", fn, dp->distpoint->type);
723 goto out;
724 }
725
726 names = dp->distpoint->name.fullname;
727 for (i = 0; i < sk_GENERAL_NAME_num(names)sk_num(((_STACK*) (1 ? (names) : (struct stack_st_GENERAL_NAME
*)0)))
; i++) {
728 name = sk_GENERAL_NAME_value(names, i)((GENERAL_NAME *)sk_value(((_STACK*) (1 ? (names) : (struct stack_st_GENERAL_NAME
*)0)), (i)))
;
729
730 /* Don't fail on non-rsync URI, so check this afterward. */
731 if (!x509_location(fn, "CRL distribution point", NULL((void *)0), name,
732 crl))
733 goto out;
734
735 if (strncasecmp(*crl, "rsync://", 8) == 0) {
736 rsync_found = 1;
737 goto out;
738 }
739
740 free(*crl);
741 *crl = NULL((void *)0);
742 }
743
744 warnx("%s: RFC 6487 section 4.8.6: no rsync URI "
745 "in CRL distributionPoint", fn);
746
747 out:
748 CRL_DIST_POINTS_free(crldp);
749 return rsync_found;
750}
751
752/*
753 * Parse X509v3 authority key identifier (AKI) from the CRL.
754 * This is matched against the string from x509_get_ski() above.
755 * Returns the AKI or NULL if it could not be parsed.
756 * The AKI is formatted as a hex string.
757 */
758char *
759x509_crl_get_aki(X509_CRL *crl, const char *fn)
760{
761 const unsigned char *d;
762 AUTHORITY_KEYID *akid;
763 ASN1_OCTET_STRING *os;
764 int dsz, crit;
765 char *res = NULL((void *)0);
766
767 akid = X509_CRL_get_ext_d2i(crl, NID_authority_key_identifier90, &crit,
768 NULL((void *)0));
769 if (akid == NULL((void *)0)) {
770 warnx("%s: RFC 6487 section 4.8.3: AKI: extension missing", fn);
771 return NULL((void *)0);
772 }
773 if (crit != 0) {
774 warnx("%s: RFC 6487 section 4.8.3: "
775 "AKI: extension not non-critical", fn);
776 goto out;
777 }
778 if (akid->issuer != NULL((void *)0) || akid->serial != NULL((void *)0)) {
779 warnx("%s: RFC 6487 section 4.8.3: AKI: "
780 "authorityCertIssuer or authorityCertSerialNumber present",
781 fn);
782 goto out;
783 }
784
785 os = akid->keyid;
786 if (os == NULL((void *)0)) {
787 warnx("%s: RFC 6487 section 4.8.3: AKI: "
788 "Key Identifier missing", fn);
789 goto out;
790 }
791
792 d = os->data;
793 dsz = os->length;
794
795 if (dsz != SHA_DIGEST_LENGTH20) {
796 warnx("%s: RFC 6487 section 4.8.2: AKI: "
797 "want %d bytes SHA1 hash, have %d bytes",
798 fn, SHA_DIGEST_LENGTH20, dsz);
799 goto out;
800 }
801
802 res = hex_encode(d, dsz);
803out:
804 AUTHORITY_KEYID_free(akid);
805 return res;
806}
807
808/*
809 * Retrieve CRL Number extension. Returns a printable hexadecimal representation
810 * of the number which has to be freed after use.
811 */
812char *
813x509_crl_get_number(X509_CRL *crl, const char *fn)
814{
815 ASN1_INTEGER *aint;
816 int crit;
817 char *res = NULL((void *)0);
818
819 aint = X509_CRL_get_ext_d2i(crl, NID_crl_number88, &crit, NULL((void *)0));
820 if (aint == NULL((void *)0)) {
821 warnx("%s: RFC 6487 section 5: CRL Number missing", fn);
822 return NULL((void *)0);
823 }
824 if (crit != 0) {
825 warnx("%s: RFC 5280, section 5.2.3: "
826 "CRL Number not non-critical", fn);
827 goto out;
828 }
829
830 /* This checks that the number is non-negative and <= 20 bytes. */
831 res = x509_convert_seqnum(fn, aint);
832
833 out:
834 ASN1_INTEGER_free(aint);
835 return res;
836}
837
838/*
839 * Convert passed ASN1_TIME to time_t *t.
840 * Returns 1 on success and 0 on failure.
841 */
842int
843x509_get_time(const ASN1_TIME *at, time_t *t)
844{
845 struct tm tm;
846
847 *t = 0;
848 memset(&tm, 0, sizeof(tm));
849 /* Fail instead of silently falling back to the current time. */
850 if (at == NULL((void *)0))
851 return 0;
852 if (!ASN1_TIME_to_tm(at, &tm))
853 return 0;
854 if ((*t = timegm(&tm)) == -1)
855 errx(1, "timegm failed");
856 return 1;
857}
858
859/*
860 * Extract and validate an accessLocation, RFC 6487, 4.8 and RFC 8182, 3.2.
861 * Returns 0 on failure and 1 on success.
862 */
863int
864x509_location(const char *fn, const char *descr, const char *proto,
865 GENERAL_NAME *location, char **out)
866{
867 ASN1_IA5STRING *uri;
868
869 if (location->type != GEN_URI6) {
870 warnx("%s: RFC 6487 section 4.8: %s not URI", fn, descr);
871 return 0;
872 }
873
874 uri = location->d.uniformResourceIdentifier;
875
876 if (!valid_uri(uri->data, uri->length, proto)) {
877 warnx("%s: RFC 6487 section 4.8: %s bad location", fn, descr);
878 return 0;
879 }
880
881 if (*out != NULL((void *)0)) {
882 warnx("%s: RFC 6487 section 4.8: multiple %s specified, "
883 "using the first one", fn, descr);
884 return 1;
885 }
886
887 if ((*out = strndup(uri->data, uri->length)) == NULL((void *)0))
888 err(1, NULL((void *)0));
889
890 return 1;
891}
892
893/*
894 * Check that the subject only contains commonName and serialNumber.
895 * Return 0 on failure.
896 */
897int
898x509_valid_subject(const char *fn, const X509 *x)
899{
900 const X509_NAME *xn;
901 const X509_NAME_ENTRY *ne;
902 const ASN1_OBJECT *ao;
903 const ASN1_STRING *as;
904 int cn = 0, sn = 0;
905 int i, nid;
906
907 if ((xn = X509_get_subject_name(x)) == NULL((void *)0)) {
908 warnx("%s: X509_get_subject_name", fn);
909 return 0;
910 }
911
912 for (i = 0; i < X509_NAME_entry_count(xn); i++) {
913 if ((ne = X509_NAME_get_entry(xn, i)) == NULL((void *)0)) {
914 warnx("%s: X509_NAME_get_entry", fn);
915 return 0;
916 }
917 if ((ao = X509_NAME_ENTRY_get_object(ne)) == NULL((void *)0)) {
918 warnx("%s: X509_NAME_ENTRY_get_object", fn);
919 return 0;
920 }
921
922 nid = OBJ_obj2nid(ao);
923 switch (nid) {
924 case NID_commonName13:
925 if (cn++ > 0) {
926 warnx("%s: duplicate commonName in subject",
927 fn);
928 return 0;
929 }
930 if ((as = X509_NAME_ENTRY_get_data(ne)) == NULL((void *)0)) {
Although the value stored to 'as' is used in the enclosing expression, the value is never actually read from 'as'
931 warnx("%s: X509_NAME_ENTRY_get_data failed",
932 fn);
933 return 0;
934 }
935/*
936 * The following check can be enabled after AFRINIC re-issues CA certs.
937 * https://lists.afrinic.net/pipermail/dbwg/2023-March/000436.html
938 */
939#if 0
940 if (ASN1_STRING_type(as) != V_ASN1_PRINTABLESTRING19) {
941 warnx("%s: RFC 6487 section 4.5: commonName is"
942 " not PrintableString", fn);
943 return 0;
944 }
945#endif
946 break;
947 case NID_serialNumber105:
948 if (sn++ > 0) {
949 warnx("%s: duplicate serialNumber in subject",
950 fn);
951 return 0;
952 }
953 break;
954 case NID_undef0:
955 warnx("%s: OBJ_obj2nid failed", fn);
956 return 0;
957 default:
958 warnx("%s: RFC 6487 section 4.5: unexpected attribute "
959 "%s", fn, OBJ_nid2sn(nid));
960 return 0;
961 }
962 }
963
964 if (cn == 0) {
965 warnx("%s: RFC 6487 section 4.5: subject missing commonName",
966 fn);
967 return 0;
968 }
969
970 return 1;
971}
972
973/*
974 * Convert an ASN1_INTEGER into a hexstring.
975 * Returned string needs to be freed by the caller.
976 */
977char *
978x509_convert_seqnum(const char *fn, const ASN1_INTEGER *i)
979{
980 BIGNUM *seqnum = NULL((void *)0);
981 char *s = NULL((void *)0);
982
983 if (i == NULL((void *)0))
984 goto out;
985
986 seqnum = ASN1_INTEGER_to_BN(i, NULL((void *)0));
987 if (seqnum == NULL((void *)0)) {
988 warnx("%s: ASN1_INTEGER_to_BN error", fn);
989 goto out;
990 }
991
992 if (BN_is_negative(seqnum)) {
993 warnx("%s: %s: want positive integer, have negative.",
994 __func__, fn);
995 goto out;
996 }
997
998 if (BN_num_bytes(seqnum)((BN_num_bits(seqnum)+7)/8) > 20) {
999 warnx("%s: %s: want 20 octets or fewer, have more.",
1000 __func__, fn);
1001 goto out;
1002 }
1003
1004 s = BN_bn2hex(seqnum);
1005 if (s == NULL((void *)0))
1006 warnx("%s: BN_bn2hex error", fn);
1007
1008 out:
1009 BN_free(seqnum);
1010 return s;
1011}
1012
1013/*
1014 * Find the closest expiry moment by walking the chain of authorities.
1015 */
1016time_t
1017x509_find_expires(time_t notafter, struct auth *a, struct crl_tree *crlt)
1018{
1019 struct crl *crl;
1020 time_t expires;
1021
1022 expires = notafter;
1023
1024 for (; a != NULL((void *)0); a = a->parent) {
1025 if (expires > a->cert->notafter)
1026 expires = a->cert->notafter;
1027 crl = crl_get(crlt, a);
1028 if (crl != NULL((void *)0) && expires > crl->nextupdate)
1029 expires = crl->nextupdate;
1030 }
1031
1032 return expires;
1033}