1 /* $OpenBSD: x509.c,v 1.14 2020/09/12 15:46:48 claudio Exp $ */ 2 /* 3 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 4 * 5 * Permission to use, copy, modify, and distribute this software for any 6 * purpose with or without fee is hereby granted, provided that the above 7 * copyright notice and this permission notice appear in all copies. 8 * 9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 */ 17 18 #include <sys/socket.h> 19 20 #include <assert.h> 21 #include <err.h> 22 #include <stdarg.h> 23 #include <stdint.h> 24 #include <stdlib.h> 25 #include <string.h> 26 #include <unistd.h> 27 28 #include <openssl/x509v3.h> 29 30 #include "extern.h" 31 32 /* 33 * Wrapper around ASN1_get_object() that preserves the current start 34 * state and returns a more meaningful value. 35 * Return zero on failure, non-zero on success. 36 */ 37 static int 38 ASN1_frame(const char *fn, size_t sz, 39 const unsigned char **cnt, long *cntsz, int *tag) 40 { 41 int ret, pcls; 42 43 assert(cnt != NULL && *cnt != NULL); 44 assert(sz > 0); 45 ret = ASN1_get_object(cnt, cntsz, tag, &pcls, sz); 46 if ((ret & 0x80)) { 47 cryptowarnx("%s: ASN1_get_object", fn); 48 return 0; 49 } 50 return ASN1_object_size((ret & 0x01) ? 2 : 0, *cntsz, *tag); 51 } 52 53 /* 54 * Parse X509v3 authority key identifier (AKI), RFC 6487 sec. 4.8.3. 55 * Returns the AKI or NULL if it could not be parsed. 56 * The AKI is formatted as aa:bb:cc:dd, with each being a hex value. 57 */ 58 char * 59 x509_get_aki_ext(X509_EXTENSION *ext, const char *fn) 60 { 61 const unsigned char *d; 62 const ASN1_TYPE *t; 63 const ASN1_OCTET_STRING *os = NULL; 64 ASN1_SEQUENCE_ANY *seq = NULL; 65 int dsz, ptag; 66 long i, plen; 67 char buf[4]; 68 char *res = NULL; 69 70 assert(NID_authority_key_identifier == 71 OBJ_obj2nid(X509_EXTENSION_get_object(ext))); 72 os = X509_EXTENSION_get_data(ext); 73 assert(os != NULL); 74 75 d = os->data; 76 dsz = os->length; 77 78 if ((seq = d2i_ASN1_SEQUENCE_ANY(NULL, &d, dsz)) == NULL) { 79 cryptowarnx("%s: RFC 6487 section 4.8.3: AKI: " 80 "failed ASN.1 sub-sequence parse", fn); 81 goto out; 82 } 83 if (sk_ASN1_TYPE_num(seq) != 1) { 84 warnx("%s: RFC 6487 section 4.8.3: AKI: " 85 "want 1 element, have %d", fn, sk_ASN1_TYPE_num(seq)); 86 goto out; 87 } 88 89 t = sk_ASN1_TYPE_value(seq, 0); 90 if (t->type != V_ASN1_OTHER) { 91 warnx("%s: RFC 6487 section 4.8.3: AKI: " 92 "want ASN.1 external, have %s (NID %d)", 93 fn, ASN1_tag2str(t->type), t->type); 94 goto out; 95 } 96 97 d = t->value.asn1_string->data; 98 dsz = t->value.asn1_string->length; 99 100 if (!ASN1_frame(fn, dsz, &d, &plen, &ptag)) 101 goto out; 102 103 /* Make room for [hex1, hex2, ":"]*, NUL. */ 104 105 if ((res = calloc(plen * 3 + 1, 1)) == NULL) 106 err(1, NULL); 107 108 for (i = 0; i < plen; i++) { 109 snprintf(buf, sizeof(buf), "%02X:", d[i]); 110 strlcat(res, buf, plen * 3 + 1); 111 } 112 res[plen * 3 - 1] = '\0'; 113 out: 114 sk_ASN1_TYPE_pop_free(seq, ASN1_TYPE_free); 115 return res; 116 } 117 118 /* 119 * Parse X509v3 subject key identifier (SKI), RFC 6487 sec. 4.8.2. 120 * Returns the SKI or NULL if it could not be parsed. 121 * The SKI is formatted as aa:bb:cc:dd, with each being a hex value. 122 */ 123 char * 124 x509_get_ski_ext(X509_EXTENSION *ext, const char *fn) 125 { 126 const unsigned char *d; 127 const ASN1_OCTET_STRING *os; 128 ASN1_OCTET_STRING *oss = NULL; 129 int i, dsz; 130 char buf[4]; 131 char *res = NULL; 132 133 assert(NID_subject_key_identifier == 134 OBJ_obj2nid(X509_EXTENSION_get_object(ext))); 135 136 os = X509_EXTENSION_get_data(ext); 137 assert(os != NULL); 138 d = os->data; 139 dsz = os->length; 140 141 if ((oss = d2i_ASN1_OCTET_STRING(NULL, &d, dsz)) == NULL) { 142 cryptowarnx("%s: RFC 6487 section 4.8.2: SKI: " 143 "failed ASN.1 octet string parse", fn); 144 goto out; 145 } 146 147 d = oss->data; 148 dsz = oss->length; 149 150 if (dsz != 20) { 151 warnx("%s: RFC 6487 section 4.8.2: SKI: " 152 "want 20 B SHA1 hash, have %d B", fn, dsz); 153 goto out; 154 } 155 156 /* Make room for [hex1, hex2, ":"]*, NUL. */ 157 158 if ((res = calloc(dsz * 3 + 1, 1)) == NULL) 159 err(1, NULL); 160 161 for (i = 0; i < dsz; i++) { 162 snprintf(buf, sizeof(buf), "%02X:", d[i]); 163 strlcat(res, buf, dsz * 3 + 1); 164 } 165 res[dsz * 3 - 1] = '\0'; 166 out: 167 ASN1_OCTET_STRING_free(oss); 168 return res; 169 } 170 171 /* 172 * Wraps around x509_get_ski_ext and x509_get_aki_ext. 173 * Returns zero on failure (out pointers are NULL) or non-zero on 174 * success (out pointers must be freed). 175 */ 176 int 177 x509_get_ski_aki(X509 *x, const char *fn, char **ski, char **aki) 178 { 179 X509_EXTENSION *ext = NULL; 180 const ASN1_OBJECT *obj; 181 int extsz, i; 182 183 *ski = *aki = NULL; 184 185 if ((extsz = X509_get_ext_count(x)) < 0) 186 cryptoerrx("X509_get_ext_count"); 187 188 for (i = 0; i < extsz; i++) { 189 ext = X509_get_ext(x, i); 190 assert(ext != NULL); 191 obj = X509_EXTENSION_get_object(ext); 192 assert(obj != NULL); 193 switch (OBJ_obj2nid(obj)) { 194 case NID_subject_key_identifier: 195 free(*ski); 196 *ski = x509_get_ski_ext(ext, fn); 197 break; 198 case NID_authority_key_identifier: 199 free(*aki); 200 *aki = x509_get_aki_ext(ext, fn); 201 break; 202 } 203 } 204 205 if (*aki == NULL) { 206 cryptowarnx("%s: RFC 6487 section 4.8.3: AKI: " 207 "missing AKI X509 extension", fn); 208 free(*ski); 209 *ski = NULL; 210 return 0; 211 } 212 if (*ski == NULL) { 213 cryptowarnx("%s: RFC 6487 section 4.8.2: AKI: " 214 "missing SKI X509 extension", fn); 215 free(*aki); 216 *aki = NULL; 217 return 0; 218 } 219 220 return 1; 221 } 222 223 /* 224 * Parse the very specific subset of information in the CRL distribution 225 * point extension. 226 * See RFC 6487, sectoin 4.8.6 for details. 227 * Returns NULL on failure, the crl URI on success which has to be freed 228 * after use. 229 */ 230 char * 231 x509_get_crl(X509 *x, const char *fn) 232 { 233 STACK_OF(DIST_POINT) *crldp; 234 DIST_POINT *dp; 235 GENERAL_NAME *name; 236 char *crl; 237 238 crldp = X509_get_ext_d2i(x, NID_crl_distribution_points, NULL, NULL); 239 if (crldp == NULL) { 240 warnx("%s: RFC 6487 section 4.8.6: CRL: " 241 "no CRL distribution point extension", fn); 242 return NULL; 243 } 244 245 if (sk_DIST_POINT_num(crldp) != 1) { 246 warnx("%s: RFC 6487 section 4.8.6: CRL: " 247 "want 1 element, have %d", fn, 248 sk_DIST_POINT_num(crldp)); 249 return NULL; 250 } 251 252 dp = sk_DIST_POINT_value(crldp, 0); 253 if (dp->distpoint == NULL) { 254 warnx("%s: RFC 6487 section 4.8.6: CRL: " 255 "no distribution point name", fn); 256 return NULL; 257 } 258 if (dp->distpoint->type != 0) { 259 warnx("%s: RFC 6487 section 4.8.6: CRL: " 260 "expected GEN_OTHERNAME, have %d", fn, dp->distpoint->type); 261 return NULL; 262 } 263 264 if (sk_GENERAL_NAME_num(dp->distpoint->name.fullname) != 1) { 265 warnx("%s: RFC 6487 section 4.8.6: CRL: " 266 "want 1 full name, have %d", fn, 267 sk_GENERAL_NAME_num(dp->distpoint->name.fullname)); 268 return NULL; 269 } 270 271 name = sk_GENERAL_NAME_value(dp->distpoint->name.fullname, 0); 272 if (name->type != GEN_URI) { 273 warnx("%s: RFC 6487 section 4.8.6: CRL: " 274 "want URI type, have %d", fn, name->type); 275 return NULL; 276 } 277 278 crl = strndup(ASN1_STRING_get0_data(name->d.uniformResourceIdentifier), 279 ASN1_STRING_length(name->d.uniformResourceIdentifier)); 280 if (crl == NULL) 281 err(1, NULL); 282 283 return crl; 284 } 285 286 char * 287 x509_crl_get_aki(X509_CRL *crl) 288 { 289 X509_EXTENSION *ext; 290 int loc; 291 292 loc = X509_CRL_get_ext_by_NID(crl, NID_authority_key_identifier, -1); 293 if (loc == -1) { 294 warnx("%s: CRL without AKI extension", __func__); 295 return NULL; 296 } 297 ext = X509_CRL_get_ext(crl, loc); 298 299 return x509_get_aki_ext(ext, "x509_crl_get_aki"); 300 } 301