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