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 *
parse_takey(const char * fn,const TAKey * 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
tak_parse_econtent(const char * fn,struct tak * tak,const unsigned char * d,size_t dsz)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 *
tak_parse(X509 ** x509,const char * fn,int talid,const unsigned char * der,size_t len)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
takey_free(struct takey * t)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
tak_free(struct tak * t)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