1 /* $OpenBSD: tak.c,v 1.8 2023/03/12 11:46:35 tb Exp $ */ 2 /* 3 * Copyright (c) 2022 Job Snijders <job@fastly.com> 4 * Copyright (c) 2022 Theo Buehler <tb@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/asn1.h> 26 #include <openssl/asn1t.h> 27 #include <openssl/safestack.h> 28 #include <openssl/stack.h> 29 #include <openssl/x509.h> 30 #include <openssl/x509v3.h> 31 32 #include "extern.h" 33 34 /* 35 * Parse results and data of the Trust Anchor Key file. 36 */ 37 struct parse { 38 const char *fn; /* TAK file name */ 39 struct tak *res; /* results */ 40 }; 41 42 extern ASN1_OBJECT *tak_oid; 43 44 /* 45 * ASN.1 templates for Trust Anchor Keys (draft-ietf-sidrops-signed-tal-12) 46 */ 47 48 DECLARE_STACK_OF(ASN1_IA5STRING); 49 50 #ifndef DEFINE_STACK_OF 51 #define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st)) 52 #define sk_ASN1_IA5STRING_value(st, i) SKM_sk_value(ASN1_IA5STRING, (st), (i)) 53 #endif 54 55 typedef struct { 56 STACK_OF(ASN1_UTF8STRING) *comments; 57 STACK_OF(ASN1_IA5STRING) *certificateURIs; 58 X509_PUBKEY *subjectPublicKeyInfo; 59 } TAKey; 60 61 typedef struct { 62 ASN1_INTEGER *version; 63 TAKey *current; 64 TAKey *predecessor; 65 TAKey *successor; 66 } TAK; 67 68 ASN1_SEQUENCE(TAKey) = { 69 ASN1_SEQUENCE_OF(TAKey, comments, ASN1_UTF8STRING), 70 ASN1_SEQUENCE_OF(TAKey, certificateURIs, ASN1_IA5STRING), 71 ASN1_SIMPLE(TAKey, subjectPublicKeyInfo, X509_PUBKEY), 72 } ASN1_SEQUENCE_END(TAKey); 73 74 ASN1_SEQUENCE(TAK) = { 75 ASN1_EXP_OPT(TAK, version, ASN1_INTEGER, 0), 76 ASN1_SIMPLE(TAK, current, TAKey), 77 ASN1_EXP_OPT(TAK, predecessor, TAKey, 0), 78 ASN1_EXP_OPT(TAK, successor, TAKey, 1), 79 } ASN1_SEQUENCE_END(TAK); 80 81 DECLARE_ASN1_FUNCTIONS(TAK); 82 IMPLEMENT_ASN1_FUNCTIONS(TAK); 83 84 /* 85 * On success return pointer to allocated & valid takey structure, 86 * on failure return NULL. 87 */ 88 static struct takey * 89 parse_takey(const char *fn, const TAKey *takey) 90 { 91 const ASN1_UTF8STRING *comment; 92 const ASN1_IA5STRING *certURI; 93 EVP_PKEY *pkey; 94 RSA *r; 95 struct takey *res = NULL; 96 unsigned char *der = NULL, *rder = NULL; 97 unsigned char md[SHA_DIGEST_LENGTH]; 98 size_t i; 99 int rdersz, rc = 0; 100 101 if ((res = calloc(1, sizeof(struct takey))) == NULL) 102 err(1, NULL); 103 104 res->commentsz = sk_ASN1_UTF8STRING_num(takey->comments); 105 if (res->commentsz > 0) { 106 res->comments = calloc(res->commentsz, sizeof(char *)); 107 if (res->comments == NULL) 108 err(1, NULL); 109 110 for (i = 0; i < res->commentsz; i++) { 111 comment = sk_ASN1_UTF8STRING_value(takey->comments, i); 112 res->comments[i] = strndup(comment->data, comment->length); 113 if (res->comments[i] == NULL) 114 err(1, NULL); 115 } 116 } 117 118 res->urisz = sk_ASN1_IA5STRING_num(takey->certificateURIs); 119 if (res->urisz == 0) { 120 warnx("%s: Signed TAL requires at least 1 CertificateURI", fn); 121 goto out; 122 } 123 if ((res->uris = calloc(res->urisz, sizeof(char *))) == NULL) 124 err(1, NULL); 125 126 for (i = 0; i < res->urisz; i++) { 127 certURI = sk_ASN1_IA5STRING_value(takey->certificateURIs, i); 128 if (!valid_uri(certURI->data, certURI->length, NULL)) { 129 warnx("%s: invalid TA URI", fn); 130 goto out; 131 } 132 133 /* XXX: enforce that protocol is rsync or https. */ 134 135 res->uris[i] = strndup(certURI->data, certURI->length); 136 if (res->uris[i] == NULL) 137 err(1, NULL); 138 } 139 140 if ((pkey = X509_PUBKEY_get0(takey->subjectPublicKeyInfo)) == NULL) { 141 warnx("%s: X509_PUBKEY_get0 failed", fn); 142 goto out; 143 } 144 145 if ((r = EVP_PKEY_get0_RSA(pkey)) == NULL) { 146 warnx("%s: EVP_PKEY_get0_RSA failed", fn); 147 goto out; 148 } 149 150 if ((rdersz = i2d_RSAPublicKey(r, &rder)) <= 0) { 151 warnx("%s: i2d_RSAPublicKey failed", fn); 152 goto out; 153 } 154 155 if (!EVP_Digest(rder, rdersz, md, NULL, EVP_sha1(), NULL)) { 156 warnx("%s: EVP_Digest failed", fn); 157 goto out; 158 } 159 res->ski = hex_encode(md, SHA_DIGEST_LENGTH); 160 161 if ((res->pubkeysz = i2d_PUBKEY(pkey, &der)) <= 0) { 162 warnx("%s: i2d_PUBKEY failed", fn); 163 goto out; 164 } 165 166 res->pubkey = der; 167 der = NULL; 168 169 rc = 1; 170 out: 171 if (rc == 0) { 172 takey_free(res); 173 res = NULL; 174 } 175 free(der); 176 free(rder); 177 return res; 178 } 179 180 /* 181 * Parses the eContent segment of an TAK file 182 * Returns zero on failure, non-zero on success. 183 */ 184 static int 185 tak_parse_econtent(const unsigned char *d, size_t dsz, struct parse *p) 186 { 187 TAK *tak; 188 const char *fn; 189 int rc = 0; 190 191 fn = p->fn; 192 193 if ((tak = d2i_TAK(NULL, &d, dsz)) == NULL) { 194 cryptowarnx("%s: failed to parse Trust Anchor Key", fn); 195 goto out; 196 } 197 198 if (!valid_econtent_version(fn, tak->version)) 199 goto out; 200 201 p->res->current = parse_takey(fn, tak->current); 202 if (p->res->current == NULL) 203 goto out; 204 205 if (tak->predecessor != NULL) { 206 p->res->predecessor = parse_takey(fn, tak->predecessor); 207 if (p->res->predecessor == NULL) 208 goto out; 209 } 210 211 if (tak->successor != NULL) { 212 p->res->successor = parse_takey(fn, tak->successor); 213 if (p->res->successor == NULL) 214 goto out; 215 } 216 217 rc = 1; 218 out: 219 TAK_free(tak); 220 return rc; 221 } 222 223 /* 224 * Parse a full draft-ietf-sidrops-signed-tal file. 225 * Returns the TAK or NULL if the object was malformed. 226 */ 227 struct tak * 228 tak_parse(X509 **x509, const char *fn, const unsigned char *der, size_t len) 229 { 230 struct parse p; 231 unsigned char *cms; 232 size_t cmsz; 233 time_t signtime = 0; 234 int rc = 0; 235 236 memset(&p, 0, sizeof(struct parse)); 237 p.fn = fn; 238 239 cms = cms_parse_validate(x509, fn, der, len, tak_oid, &cmsz, &signtime); 240 if (cms == NULL) 241 return NULL; 242 243 if ((p.res = calloc(1, sizeof(struct tak))) == NULL) 244 err(1, NULL); 245 p.res->signtime = signtime; 246 247 if (!x509_get_aia(*x509, fn, &p.res->aia)) 248 goto out; 249 if (!x509_get_aki(*x509, fn, &p.res->aki)) 250 goto out; 251 if (!x509_get_sia(*x509, fn, &p.res->sia)) 252 goto out; 253 if (!x509_get_ski(*x509, fn, &p.res->ski)) 254 goto out; 255 if (p.res->aia == NULL || p.res->aki == NULL || p.res->sia == NULL || 256 p.res->ski == NULL) { 257 warnx("%s: RFC 6487 section 4.8: " 258 "missing AIA, AKI, SIA, or SKI X509 extension", fn); 259 goto out; 260 } 261 262 if (!x509_get_notbefore(*x509, fn, &p.res->notbefore)) 263 goto out; 264 if (!x509_get_notafter(*x509, fn, &p.res->notafter)) 265 goto out; 266 267 if (!x509_inherits(*x509)) { 268 warnx("%s: RFC 3779 extension not set to inherit", fn); 269 goto out; 270 } 271 272 if (!tak_parse_econtent(cms, cmsz, &p)) 273 goto out; 274 275 if (strcmp(p.res->aki, p.res->current->ski) != 0) { 276 warnx("%s: current TAKey's SKI does not match EE AKI", fn); 277 goto out; 278 } 279 280 rc = 1; 281 out: 282 if (rc == 0) { 283 tak_free(p.res); 284 p.res = NULL; 285 X509_free(*x509); 286 *x509 = NULL; 287 } 288 free(cms); 289 return p.res; 290 } 291 292 /* 293 * Free TAKey pointer. 294 */ 295 void 296 takey_free(struct takey *t) 297 { 298 size_t i; 299 300 if (t == NULL) 301 return; 302 303 for (i = 0; i < t->commentsz; i++) 304 free(t->comments[i]); 305 306 for (i = 0; i < t->urisz; i++) 307 free(t->uris[i]); 308 309 free(t->comments); 310 free(t->uris); 311 free(t->ski); 312 free(t->pubkey); 313 free(t); 314 } 315 316 /* 317 * Free an TAK pointer. 318 * Safe to call with NULL. 319 */ 320 void 321 tak_free(struct tak *t) 322 { 323 if (t == NULL) 324 return; 325 326 takey_free(t->current); 327 takey_free(t->predecessor); 328 takey_free(t->successor); 329 330 free(t->aia); 331 free(t->aki); 332 free(t->ski); 333 free(t); 334 } 335