1 /* $OpenBSD: tak.c,v 1.21 2024/11/13 12:51:04 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->num_comments = sk_ASN1_UTF8STRING_num(takey->comments); 98 if (res->num_comments > 0) { 99 res->comments = calloc(res->num_comments, sizeof(char *)); 100 if (res->comments == NULL) 101 err(1, NULL); 102 103 for (i = 0; i < res->num_comments; i++) { 104 comment = sk_ASN1_UTF8STRING_value(takey->comments, i); 105 res->comments[i] = strndup(comment->data, 106 comment->length); 107 if (res->comments[i] == NULL) 108 err(1, NULL); 109 } 110 } 111 112 res->num_uris = sk_ASN1_IA5STRING_num(takey->certificateURIs); 113 if (res->num_uris == 0) { 114 warnx("%s: Signed TAL requires at least 1 CertificateURI", fn); 115 goto err; 116 } 117 if ((res->uris = calloc(res->num_uris, sizeof(char *))) == NULL) 118 err(1, NULL); 119 120 for (i = 0; i < res->num_uris; i++) { 121 certURI = sk_ASN1_IA5STRING_value(takey->certificateURIs, i); 122 if (!valid_uri(certURI->data, certURI->length, NULL)) { 123 warnx("%s: invalid TA URI", fn); 124 goto err; 125 } 126 127 /* XXX: enforce that protocol is rsync or https. */ 128 129 res->uris[i] = strndup(certURI->data, certURI->length); 130 if (res->uris[i] == NULL) 131 err(1, NULL); 132 } 133 134 pubkey = takey->subjectPublicKeyInfo; 135 if ((res->ski = x509_pubkey_get_ski(pubkey, fn)) == NULL) 136 goto err; 137 138 if ((der_len = i2d_X509_PUBKEY(pubkey, &der)) <= 0) { 139 warnx("%s: i2d_X509_PUBKEY failed", fn); 140 goto err; 141 } 142 res->pubkey = der; 143 res->pubkeysz = der_len; 144 145 return res; 146 147 err: 148 takey_free(res); 149 return NULL; 150 } 151 152 /* 153 * Parses the eContent segment of an TAK file 154 * Returns zero on failure, non-zero on success. 155 */ 156 static int 157 tak_parse_econtent(const char *fn, struct tak *tak, const unsigned char *d, 158 size_t dsz) 159 { 160 const unsigned char *oder; 161 TAK *tak_asn1; 162 int rc = 0; 163 164 oder = d; 165 if ((tak_asn1 = d2i_TAK(NULL, &d, dsz)) == NULL) { 166 warnx("%s: failed to parse Trust Anchor Key", fn); 167 goto out; 168 } 169 if (d != oder + dsz) { 170 warnx("%s: %td bytes trailing garbage in eContent", fn, 171 oder + dsz - d); 172 goto out; 173 } 174 175 if (!valid_econtent_version(fn, tak_asn1->version, 0)) 176 goto out; 177 178 tak->current = parse_takey(fn, tak_asn1->current); 179 if (tak->current == NULL) 180 goto out; 181 182 if (tak_asn1->predecessor != NULL) { 183 tak->predecessor = parse_takey(fn, tak_asn1->predecessor); 184 if (tak->predecessor == NULL) 185 goto out; 186 } 187 188 if (tak_asn1->successor != NULL) { 189 tak->successor = parse_takey(fn, tak_asn1->successor); 190 if (tak->successor == NULL) 191 goto out; 192 } 193 194 rc = 1; 195 out: 196 TAK_free(tak_asn1); 197 return rc; 198 } 199 200 /* 201 * Parse a full draft-ietf-sidrops-signed-tal file. 202 * Returns the TAK or NULL if the object was malformed. 203 */ 204 struct tak * 205 tak_parse(X509 **x509, const char *fn, int talid, const unsigned char *der, 206 size_t len) 207 { 208 struct tak *tak; 209 struct cert *cert = NULL; 210 unsigned char *cms; 211 size_t cmsz; 212 time_t signtime = 0; 213 int rc = 0; 214 215 cms = cms_parse_validate(x509, fn, der, len, tak_oid, &cmsz, &signtime); 216 if (cms == NULL) 217 return NULL; 218 219 if ((tak = calloc(1, sizeof(struct tak))) == NULL) 220 err(1, NULL); 221 tak->signtime = signtime; 222 223 if (!x509_get_aia(*x509, fn, &tak->aia)) 224 goto out; 225 if (!x509_get_aki(*x509, fn, &tak->aki)) 226 goto out; 227 if (!x509_get_sia(*x509, fn, &tak->sia)) 228 goto out; 229 if (!x509_get_ski(*x509, fn, &tak->ski)) 230 goto out; 231 if (tak->aia == NULL || tak->aki == NULL || tak->sia == NULL || 232 tak->ski == NULL) { 233 warnx("%s: RFC 6487 section 4.8: " 234 "missing AIA, AKI, SIA, or SKI X509 extension", fn); 235 goto out; 236 } 237 238 if (!x509_get_notbefore(*x509, fn, &tak->notbefore)) 239 goto out; 240 if (!x509_get_notafter(*x509, fn, &tak->notafter)) 241 goto out; 242 243 if (!x509_inherits(*x509)) { 244 warnx("%s: RFC 3779 extension not set to inherit", fn); 245 goto out; 246 } 247 248 if (!tak_parse_econtent(fn, tak, cms, cmsz)) 249 goto out; 250 251 if ((cert = cert_parse_ee_cert(fn, talid, *x509)) == NULL) 252 goto out; 253 254 if (strcmp(tak->aki, tak->current->ski) != 0) { 255 warnx("%s: current TAKey's SKI does not match EE AKI", fn); 256 goto out; 257 } 258 259 rc = 1; 260 out: 261 if (rc == 0) { 262 tak_free(tak); 263 tak = NULL; 264 X509_free(*x509); 265 *x509 = NULL; 266 } 267 cert_free(cert); 268 free(cms); 269 return tak; 270 } 271 272 /* 273 * Free TAKey pointer. 274 */ 275 void 276 takey_free(struct takey *t) 277 { 278 size_t i; 279 280 if (t == NULL) 281 return; 282 283 for (i = 0; i < t->num_comments; i++) 284 free(t->comments[i]); 285 286 for (i = 0; i < t->num_uris; i++) 287 free(t->uris[i]); 288 289 free(t->comments); 290 free(t->uris); 291 free(t->ski); 292 free(t->pubkey); 293 free(t); 294 } 295 296 /* 297 * Free an TAK pointer. 298 * Safe to call with NULL. 299 */ 300 void 301 tak_free(struct tak *t) 302 { 303 if (t == NULL) 304 return; 305 306 takey_free(t->current); 307 takey_free(t->predecessor); 308 takey_free(t->successor); 309 310 free(t->aia); 311 free(t->aki); 312 free(t->sia); 313 free(t->ski); 314 free(t); 315 } 316