1*30a08502Stb /* $OpenBSD: tak.c,v 1.21 2024/11/13 12:51:04 tb Exp $ */ 2ee2a33daSjob /* 3ee2a33daSjob * Copyright (c) 2022 Job Snijders <job@fastly.com> 4ee2a33daSjob * Copyright (c) 2022 Theo Buehler <tb@openbsd.org> 5ee2a33daSjob * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv> 6ee2a33daSjob * 7ee2a33daSjob * Permission to use, copy, modify, and distribute this software for any 8ee2a33daSjob * purpose with or without fee is hereby granted, provided that the above 9ee2a33daSjob * copyright notice and this permission notice appear in all copies. 10ee2a33daSjob * 11ee2a33daSjob * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12ee2a33daSjob * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13ee2a33daSjob * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14ee2a33daSjob * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15ee2a33daSjob * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16ee2a33daSjob * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17ee2a33daSjob * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18ee2a33daSjob */ 19ee2a33daSjob 20ee2a33daSjob #include <err.h> 21ee2a33daSjob #include <stdlib.h> 22ee2a33daSjob #include <string.h> 23ee2a33daSjob #include <unistd.h> 24ee2a33daSjob 25ee2a33daSjob #include <openssl/asn1.h> 26ee2a33daSjob #include <openssl/asn1t.h> 27ee2a33daSjob #include <openssl/safestack.h> 28ee2a33daSjob #include <openssl/stack.h> 29ee2a33daSjob #include <openssl/x509.h> 30ee2a33daSjob #include <openssl/x509v3.h> 31ee2a33daSjob 32ee2a33daSjob #include "extern.h" 33ee2a33daSjob 34ee2a33daSjob extern ASN1_OBJECT *tak_oid; 35ee2a33daSjob 36ee2a33daSjob /* 37ee2a33daSjob * ASN.1 templates for Trust Anchor Keys (draft-ietf-sidrops-signed-tal-12) 38ee2a33daSjob */ 39ee2a33daSjob 40d3d26873Sjob ASN1_ITEM_EXP TAKey_it; 41d3d26873Sjob ASN1_ITEM_EXP TAK_it; 42d3d26873Sjob 43ee2a33daSjob DECLARE_STACK_OF(ASN1_IA5STRING); 44ee2a33daSjob 45ee2a33daSjob #ifndef DEFINE_STACK_OF 46ee2a33daSjob #define sk_ASN1_IA5STRING_num(st) SKM_sk_num(ASN1_IA5STRING, (st)) 47ee2a33daSjob #define sk_ASN1_IA5STRING_value(st, i) SKM_sk_value(ASN1_IA5STRING, (st), (i)) 48ee2a33daSjob #endif 49ee2a33daSjob 50ee2a33daSjob typedef struct { 51ee2a33daSjob STACK_OF(ASN1_UTF8STRING) *comments; 52ee2a33daSjob STACK_OF(ASN1_IA5STRING) *certificateURIs; 53ee2a33daSjob X509_PUBKEY *subjectPublicKeyInfo; 54ee2a33daSjob } TAKey; 55ee2a33daSjob 56ee2a33daSjob typedef struct { 57ee2a33daSjob ASN1_INTEGER *version; 58ee2a33daSjob TAKey *current; 59ee2a33daSjob TAKey *predecessor; 60ee2a33daSjob TAKey *successor; 61ee2a33daSjob } TAK; 62ee2a33daSjob 63ee2a33daSjob ASN1_SEQUENCE(TAKey) = { 64ee2a33daSjob ASN1_SEQUENCE_OF(TAKey, comments, ASN1_UTF8STRING), 65ee2a33daSjob ASN1_SEQUENCE_OF(TAKey, certificateURIs, ASN1_IA5STRING), 66ee2a33daSjob ASN1_SIMPLE(TAKey, subjectPublicKeyInfo, X509_PUBKEY), 67ee2a33daSjob } ASN1_SEQUENCE_END(TAKey); 68ee2a33daSjob 69ee2a33daSjob ASN1_SEQUENCE(TAK) = { 70ee2a33daSjob ASN1_EXP_OPT(TAK, version, ASN1_INTEGER, 0), 71ee2a33daSjob ASN1_SIMPLE(TAK, current, TAKey), 72ee2a33daSjob ASN1_EXP_OPT(TAK, predecessor, TAKey, 0), 73ee2a33daSjob ASN1_EXP_OPT(TAK, successor, TAKey, 1), 74ee2a33daSjob } ASN1_SEQUENCE_END(TAK); 75ee2a33daSjob 76ee2a33daSjob DECLARE_ASN1_FUNCTIONS(TAK); 77ee2a33daSjob IMPLEMENT_ASN1_FUNCTIONS(TAK); 78ee2a33daSjob 79ee2a33daSjob /* 80ee2a33daSjob * On success return pointer to allocated & valid takey structure, 81ee2a33daSjob * on failure return NULL. 82ee2a33daSjob */ 83ee2a33daSjob static struct takey * 84ee2a33daSjob parse_takey(const char *fn, const TAKey *takey) 85ee2a33daSjob { 86ee2a33daSjob const ASN1_UTF8STRING *comment; 87ee2a33daSjob const ASN1_IA5STRING *certURI; 8823c6f3a2Stb X509_PUBKEY *pubkey; 89ee2a33daSjob struct takey *res = NULL; 9023c6f3a2Stb unsigned char *der = NULL; 91ee2a33daSjob size_t i; 9223c6f3a2Stb int der_len; 93ee2a33daSjob 94ee2a33daSjob if ((res = calloc(1, sizeof(struct takey))) == NULL) 95ee2a33daSjob err(1, NULL); 96ee2a33daSjob 97*30a08502Stb res->num_comments = sk_ASN1_UTF8STRING_num(takey->comments); 98*30a08502Stb if (res->num_comments > 0) { 99*30a08502Stb res->comments = calloc(res->num_comments, sizeof(char *)); 100ee2a33daSjob if (res->comments == NULL) 101ee2a33daSjob err(1, NULL); 102ee2a33daSjob 103*30a08502Stb for (i = 0; i < res->num_comments; i++) { 104ee2a33daSjob comment = sk_ASN1_UTF8STRING_value(takey->comments, i); 105*30a08502Stb res->comments[i] = strndup(comment->data, 106*30a08502Stb comment->length); 107ee2a33daSjob if (res->comments[i] == NULL) 108ee2a33daSjob err(1, NULL); 109ee2a33daSjob } 110ee2a33daSjob } 111ee2a33daSjob 112*30a08502Stb res->num_uris = sk_ASN1_IA5STRING_num(takey->certificateURIs); 113*30a08502Stb if (res->num_uris == 0) { 114ee2a33daSjob warnx("%s: Signed TAL requires at least 1 CertificateURI", fn); 115142eb7c6Sjob goto err; 116ee2a33daSjob } 117*30a08502Stb if ((res->uris = calloc(res->num_uris, sizeof(char *))) == NULL) 118ee2a33daSjob err(1, NULL); 119ee2a33daSjob 120*30a08502Stb for (i = 0; i < res->num_uris; i++) { 121ee2a33daSjob certURI = sk_ASN1_IA5STRING_value(takey->certificateURIs, i); 122ee2a33daSjob if (!valid_uri(certURI->data, certURI->length, NULL)) { 123ee2a33daSjob warnx("%s: invalid TA URI", fn); 124142eb7c6Sjob goto err; 125ee2a33daSjob } 126ee2a33daSjob 127ee2a33daSjob /* XXX: enforce that protocol is rsync or https. */ 128ee2a33daSjob 129ee2a33daSjob res->uris[i] = strndup(certURI->data, certURI->length); 130ee2a33daSjob if (res->uris[i] == NULL) 131ee2a33daSjob err(1, NULL); 132ee2a33daSjob } 133ee2a33daSjob 13423c6f3a2Stb pubkey = takey->subjectPublicKeyInfo; 13523c6f3a2Stb if ((res->ski = x509_pubkey_get_ski(pubkey, fn)) == NULL) 136142eb7c6Sjob goto err; 137ee2a33daSjob 13823c6f3a2Stb if ((der_len = i2d_X509_PUBKEY(pubkey, &der)) <= 0) { 139142eb7c6Sjob warnx("%s: i2d_X509_PUBKEY failed", fn); 140142eb7c6Sjob goto err; 141ee2a33daSjob } 14223c6f3a2Stb res->pubkey = der; 14323c6f3a2Stb res->pubkeysz = der_len; 144ee2a33daSjob 145ee2a33daSjob return res; 146142eb7c6Sjob 147142eb7c6Sjob err: 148142eb7c6Sjob takey_free(res); 149142eb7c6Sjob return NULL; 150ee2a33daSjob } 151ee2a33daSjob 152ee2a33daSjob /* 153ee2a33daSjob * Parses the eContent segment of an TAK file 154ee2a33daSjob * Returns zero on failure, non-zero on success. 155ee2a33daSjob */ 156ee2a33daSjob static int 157be6e5ad5Stb tak_parse_econtent(const char *fn, struct tak *tak, const unsigned char *d, 158be6e5ad5Stb size_t dsz) 159ee2a33daSjob { 160d115f50dSjob const unsigned char *oder; 161cd9dc441Stb TAK *tak_asn1; 162ee2a33daSjob int rc = 0; 163ee2a33daSjob 164d115f50dSjob oder = d; 165cd9dc441Stb if ((tak_asn1 = d2i_TAK(NULL, &d, dsz)) == NULL) { 166c0528901Stb warnx("%s: failed to parse Trust Anchor Key", fn); 167ee2a33daSjob goto out; 168ee2a33daSjob } 169d115f50dSjob if (d != oder + dsz) { 170be6e5ad5Stb warnx("%s: %td bytes trailing garbage in eContent", fn, 171d115f50dSjob oder + dsz - d); 172d115f50dSjob goto out; 173d115f50dSjob } 174ee2a33daSjob 175cd9dc441Stb if (!valid_econtent_version(fn, tak_asn1->version, 0)) 176ee2a33daSjob goto out; 177ee2a33daSjob 178be6e5ad5Stb tak->current = parse_takey(fn, tak_asn1->current); 179be6e5ad5Stb if (tak->current == NULL) 180ee2a33daSjob goto out; 181ee2a33daSjob 182cd9dc441Stb if (tak_asn1->predecessor != NULL) { 183be6e5ad5Stb tak->predecessor = parse_takey(fn, tak_asn1->predecessor); 184be6e5ad5Stb if (tak->predecessor == NULL) 185ee2a33daSjob goto out; 186ee2a33daSjob } 187ee2a33daSjob 188cd9dc441Stb if (tak_asn1->successor != NULL) { 189be6e5ad5Stb tak->successor = parse_takey(fn, tak_asn1->successor); 190be6e5ad5Stb if (tak->successor == NULL) 191ee2a33daSjob goto out; 192ee2a33daSjob } 193ee2a33daSjob 194ee2a33daSjob rc = 1; 195ee2a33daSjob out: 196cd9dc441Stb TAK_free(tak_asn1); 197ee2a33daSjob return rc; 198ee2a33daSjob } 199ee2a33daSjob 200ee2a33daSjob /* 201ee2a33daSjob * Parse a full draft-ietf-sidrops-signed-tal file. 202ee2a33daSjob * Returns the TAK or NULL if the object was malformed. 203ee2a33daSjob */ 204ee2a33daSjob struct tak * 2050636c4d0Stb tak_parse(X509 **x509, const char *fn, int talid, const unsigned char *der, 2060636c4d0Stb size_t len) 207ee2a33daSjob { 208be6e5ad5Stb struct tak *tak; 209b454bae8Sjob struct cert *cert = NULL; 210ee2a33daSjob unsigned char *cms; 211ee2a33daSjob size_t cmsz; 212beccb378Stb time_t signtime = 0; 213ee2a33daSjob int rc = 0; 214ee2a33daSjob 2151330c972Stb cms = cms_parse_validate(x509, fn, der, len, tak_oid, &cmsz, &signtime); 216ee2a33daSjob if (cms == NULL) 217ee2a33daSjob return NULL; 218ee2a33daSjob 219be6e5ad5Stb if ((tak = calloc(1, sizeof(struct tak))) == NULL) 220ee2a33daSjob err(1, NULL); 221be6e5ad5Stb tak->signtime = signtime; 222ee2a33daSjob 223be6e5ad5Stb if (!x509_get_aia(*x509, fn, &tak->aia)) 224ee2a33daSjob goto out; 225be6e5ad5Stb if (!x509_get_aki(*x509, fn, &tak->aki)) 226ee2a33daSjob goto out; 227be6e5ad5Stb if (!x509_get_sia(*x509, fn, &tak->sia)) 2282cf0e122Sjob goto out; 229be6e5ad5Stb if (!x509_get_ski(*x509, fn, &tak->ski)) 230ee2a33daSjob goto out; 231be6e5ad5Stb if (tak->aia == NULL || tak->aki == NULL || tak->sia == NULL || 232be6e5ad5Stb tak->ski == NULL) { 233ee2a33daSjob warnx("%s: RFC 6487 section 4.8: " 2342cf0e122Sjob "missing AIA, AKI, SIA, or SKI X509 extension", fn); 235ee2a33daSjob goto out; 236ee2a33daSjob } 237ee2a33daSjob 238be6e5ad5Stb if (!x509_get_notbefore(*x509, fn, &tak->notbefore)) 239ee2a33daSjob goto out; 240be6e5ad5Stb if (!x509_get_notafter(*x509, fn, &tak->notafter)) 241ee2a33daSjob goto out; 242ee2a33daSjob 243ee2a33daSjob if (!x509_inherits(*x509)) { 244ee2a33daSjob warnx("%s: RFC 3779 extension not set to inherit", fn); 245ee2a33daSjob goto out; 246ee2a33daSjob } 247ee2a33daSjob 248be6e5ad5Stb if (!tak_parse_econtent(fn, tak, cms, cmsz)) 249ee2a33daSjob goto out; 250ee2a33daSjob 251891d6bceSjob if ((cert = cert_parse_ee_cert(fn, talid, *x509)) == NULL) 252b454bae8Sjob goto out; 253b454bae8Sjob 254be6e5ad5Stb if (strcmp(tak->aki, tak->current->ski) != 0) { 255ee2a33daSjob warnx("%s: current TAKey's SKI does not match EE AKI", fn); 256ee2a33daSjob goto out; 257ee2a33daSjob } 258ee2a33daSjob 259ee2a33daSjob rc = 1; 260ee2a33daSjob out: 261ee2a33daSjob if (rc == 0) { 262be6e5ad5Stb tak_free(tak); 263be6e5ad5Stb tak = NULL; 264ee2a33daSjob X509_free(*x509); 265ee2a33daSjob *x509 = NULL; 266ee2a33daSjob } 267b454bae8Sjob cert_free(cert); 268ee2a33daSjob free(cms); 269be6e5ad5Stb return tak; 270ee2a33daSjob } 271ee2a33daSjob 272ee2a33daSjob /* 273ee2a33daSjob * Free TAKey pointer. 274ee2a33daSjob */ 275ee2a33daSjob void 276ee2a33daSjob takey_free(struct takey *t) 277ee2a33daSjob { 278ee2a33daSjob size_t i; 279ee2a33daSjob 280ee2a33daSjob if (t == NULL) 281ee2a33daSjob return; 282ee2a33daSjob 283*30a08502Stb for (i = 0; i < t->num_comments; i++) 284ee2a33daSjob free(t->comments[i]); 285ee2a33daSjob 286*30a08502Stb for (i = 0; i < t->num_uris; i++) 287ee2a33daSjob free(t->uris[i]); 288ee2a33daSjob 289ee2a33daSjob free(t->comments); 290ee2a33daSjob free(t->uris); 291ee2a33daSjob free(t->ski); 292ee2a33daSjob free(t->pubkey); 293ee2a33daSjob free(t); 294ee2a33daSjob } 295ee2a33daSjob 296ee2a33daSjob /* 297ee2a33daSjob * Free an TAK pointer. 298ee2a33daSjob * Safe to call with NULL. 299ee2a33daSjob */ 300ee2a33daSjob void 301ee2a33daSjob tak_free(struct tak *t) 302ee2a33daSjob { 303ee2a33daSjob if (t == NULL) 304ee2a33daSjob return; 305ee2a33daSjob 306ee2a33daSjob takey_free(t->current); 307ee2a33daSjob takey_free(t->predecessor); 308ee2a33daSjob takey_free(t->successor); 309ee2a33daSjob 310ee2a33daSjob free(t->aia); 311ee2a33daSjob free(t->aki); 3129d4541e5Stb free(t->sia); 313ee2a33daSjob free(t->ski); 314ee2a33daSjob free(t); 315ee2a33daSjob } 316