1 /* $OpenBSD: x509.c,v 1.21 2021/04/01 06:43:23 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 <stdlib.h> 24 #include <string.h> 25 #include <unistd.h> 26 27 #include <openssl/x509v3.h> 28 29 #include "extern.h" 30 31 /* 32 * Parse X509v3 authority key identifier (AKI), RFC 6487 sec. 4.8.3. 33 * Returns the AKI or NULL if it could not be parsed. 34 * The AKI is formatted as a hex string. 35 */ 36 char * 37 x509_get_aki(X509 *x, int ta, const char *fn) 38 { 39 const unsigned char *d; 40 AUTHORITY_KEYID *akid; 41 ASN1_OCTET_STRING *os; 42 int dsz, crit; 43 char *res = NULL; 44 45 akid = X509_get_ext_d2i(x, NID_authority_key_identifier, &crit, NULL); 46 if (akid == NULL) { 47 if (!ta) 48 warnx("%s: RFC 6487 section 4.8.3: AKI: " 49 "extension missing", fn); 50 return NULL; 51 } 52 if (crit != 0) { 53 warnx("%s: RFC 6487 section 4.8.3: " 54 "AKI: extension not non-critical", fn); 55 goto out; 56 } 57 if (akid->issuer != NULL || akid->serial != NULL) { 58 warnx("%s: RFC 6487 section 4.8.3: AKI: " 59 "authorityCertIssuer or authorityCertSerialNumber present", 60 fn); 61 goto out; 62 } 63 64 os = akid->keyid; 65 if (os == NULL) { 66 warnx("%s: RFC 6487 section 4.8.3: AKI: " 67 "Key Identifier missing", fn); 68 goto out; 69 } 70 71 d = os->data; 72 dsz = os->length; 73 74 if (dsz != SHA_DIGEST_LENGTH) { 75 warnx("%s: RFC 6487 section 4.8.2: AKI: " 76 "want %d bytes SHA1 hash, have %d bytes", 77 fn, SHA_DIGEST_LENGTH, dsz); 78 goto out; 79 } 80 81 res = hex_encode(d, dsz); 82 out: 83 AUTHORITY_KEYID_free(akid); 84 return res; 85 } 86 87 /* 88 * Parse X509v3 subject key identifier (SKI), RFC 6487 sec. 4.8.2. 89 * Returns the SKI or NULL if it could not be parsed. 90 * The SKI is formatted as a hex string. 91 */ 92 char * 93 x509_get_ski(X509 *x, const char *fn) 94 { 95 const unsigned char *d; 96 ASN1_OCTET_STRING *os; 97 int dsz, crit; 98 char *res = NULL; 99 100 os = X509_get_ext_d2i(x, NID_subject_key_identifier, &crit, NULL); 101 if (os == NULL) { 102 warnx("%s: RFC 6487 section 4.8.2: SKI: extension missing", fn); 103 return NULL; 104 } 105 if (crit != 0) { 106 warnx("%s: RFC 6487 section 4.8.2: " 107 "SKI: extension not non-critical", fn); 108 goto out; 109 } 110 111 d = os->data; 112 dsz = os->length; 113 114 if (dsz != SHA_DIGEST_LENGTH) { 115 warnx("%s: RFC 6487 section 4.8.2: SKI: " 116 "want %d bytes SHA1 hash, have %d bytes", 117 fn, SHA_DIGEST_LENGTH, dsz); 118 goto out; 119 } 120 121 res = hex_encode(d, dsz); 122 out: 123 ASN1_OCTET_STRING_free(os); 124 return res; 125 } 126 127 /* 128 * Parse the Authority Information Access (AIA) extension 129 * See RFC 6487, section 4.8.7 for details. 130 * Returns NULL on failure, on success returns the AIA URI 131 * (which has to be freed after use). 132 */ 133 char * 134 x509_get_aia(X509 *x, const char *fn) 135 { 136 ACCESS_DESCRIPTION *ad; 137 AUTHORITY_INFO_ACCESS *info; 138 char *aia = NULL; 139 int crit; 140 141 info = X509_get_ext_d2i(x, NID_info_access, &crit, NULL); 142 if (info == NULL) { 143 warnx("%s: RFC 6487 section 4.8.7: AIA: extension missing", fn); 144 return NULL; 145 } 146 if (crit != 0) { 147 warnx("%s: RFC 6487 section 4.8.7: " 148 "AIA: extension not non-critical", fn); 149 goto out; 150 } 151 if (sk_ACCESS_DESCRIPTION_num(info) != 1) { 152 warnx("%s: RFC 6487 section 4.8.7: AIA: " 153 "want 1 element, have %d", fn, 154 sk_ACCESS_DESCRIPTION_num(info)); 155 goto out; 156 } 157 158 ad = sk_ACCESS_DESCRIPTION_value(info, 0); 159 if (OBJ_obj2nid(ad->method) != NID_ad_ca_issuers) { 160 warnx("%s: RFC 6487 section 4.8.7: AIA: " 161 "expected caIssuers, have %d", fn, OBJ_obj2nid(ad->method)); 162 goto out; 163 } 164 if (ad->location->type != GEN_URI) { 165 warnx("%s: RFC 6487 section 4.8.7: AIA: " 166 "want GEN_URI type, have %d", fn, ad->location->type); 167 goto out; 168 } 169 170 aia = strndup( 171 ASN1_STRING_get0_data(ad->location->d.uniformResourceIdentifier), 172 ASN1_STRING_length(ad->location->d.uniformResourceIdentifier)); 173 if (aia == NULL) 174 err(1, NULL); 175 176 out: 177 AUTHORITY_INFO_ACCESS_free(info); 178 return aia; 179 } 180 181 /* 182 * Parse the very specific subset of information in the CRL distribution 183 * point extension. 184 * See RFC 6487, sectoin 4.8.6 for details. 185 * Returns NULL on failure, the crl URI on success which has to be freed 186 * after use. 187 */ 188 char * 189 x509_get_crl(X509 *x, const char *fn) 190 { 191 CRL_DIST_POINTS *crldp; 192 DIST_POINT *dp; 193 GENERAL_NAME *name; 194 char *crl = NULL; 195 int crit; 196 197 crldp = X509_get_ext_d2i(x, NID_crl_distribution_points, &crit, NULL); 198 if (crldp == NULL) { 199 warnx("%s: RFC 6487 section 4.8.6: CRL: " 200 "no CRL distribution point extension", fn); 201 return NULL; 202 } 203 if (crit != 0) { 204 warnx("%s: RFC 6487 section 4.8.6: " 205 "CRL distribution point: extension not non-critical", fn); 206 goto out; 207 } 208 209 if (sk_DIST_POINT_num(crldp) != 1) { 210 warnx("%s: RFC 6487 section 4.8.6: CRL: " 211 "want 1 element, have %d", fn, 212 sk_DIST_POINT_num(crldp)); 213 goto out; 214 } 215 216 dp = sk_DIST_POINT_value(crldp, 0); 217 if (dp->distpoint == NULL) { 218 warnx("%s: RFC 6487 section 4.8.6: CRL: " 219 "no distribution point name", fn); 220 goto out; 221 } 222 if (dp->distpoint->type != 0) { 223 warnx("%s: RFC 6487 section 4.8.6: CRL: " 224 "expected GEN_OTHERNAME, have %d", fn, dp->distpoint->type); 225 goto out; 226 } 227 228 if (sk_GENERAL_NAME_num(dp->distpoint->name.fullname) != 1) { 229 warnx("%s: RFC 6487 section 4.8.6: CRL: " 230 "want 1 full name, have %d", fn, 231 sk_GENERAL_NAME_num(dp->distpoint->name.fullname)); 232 goto out; 233 } 234 235 name = sk_GENERAL_NAME_value(dp->distpoint->name.fullname, 0); 236 if (name->type != GEN_URI) { 237 warnx("%s: RFC 6487 section 4.8.6: CRL: " 238 "want URI type, have %d", fn, name->type); 239 goto out; 240 } 241 242 crl = strndup(ASN1_STRING_get0_data(name->d.uniformResourceIdentifier), 243 ASN1_STRING_length(name->d.uniformResourceIdentifier)); 244 if (crl == NULL) 245 err(1, NULL); 246 247 out: 248 CRL_DIST_POINTS_free(crldp); 249 return crl; 250 } 251 252 /* 253 * Parse X509v3 authority key identifier (AKI) from the CRL. 254 * This is matched against the string from x509_get_ski() above. 255 * Returns the AKI or NULL if it could not be parsed. 256 * The AKI is formatted as a hex string. 257 */ 258 char * 259 x509_crl_get_aki(X509_CRL *crl, const char *fn) 260 { 261 const unsigned char *d; 262 AUTHORITY_KEYID *akid; 263 ASN1_OCTET_STRING *os; 264 int dsz, crit; 265 char *res = NULL; 266 267 akid = X509_CRL_get_ext_d2i(crl, NID_authority_key_identifier, &crit, 268 NULL); 269 if (akid == NULL) { 270 warnx("%s: RFC 6487 section 4.8.3: AKI: extension missing", fn); 271 return NULL; 272 } 273 if (crit != 0) { 274 warnx("%s: RFC 6487 section 4.8.3: " 275 "AKI: extension not non-critical", fn); 276 goto out; 277 } 278 if (akid->issuer != NULL || akid->serial != NULL) { 279 warnx("%s: RFC 6487 section 4.8.3: AKI: " 280 "authorityCertIssuer or authorityCertSerialNumber present", 281 fn); 282 goto out; 283 } 284 285 os = akid->keyid; 286 if (os == NULL) { 287 warnx("%s: RFC 6487 section 4.8.3: AKI: " 288 "Key Identifier missing", fn); 289 goto out; 290 } 291 292 d = os->data; 293 dsz = os->length; 294 295 if (dsz != SHA_DIGEST_LENGTH) { 296 warnx("%s: RFC 6487 section 4.8.2: AKI: " 297 "want %d bytes SHA1 hash, have %d bytes", 298 fn, SHA_DIGEST_LENGTH, dsz); 299 goto out; 300 } 301 302 res = hex_encode(d, dsz); 303 out: 304 AUTHORITY_KEYID_free(akid); 305 return res; 306 } 307