1/*	$OpenBSD: rsc.c,v 1.35 2024/06/11 10:38:40 tb Exp $ */
2/*
3 * Copyright (c) 2022 Theo Buehler <tb@openbsd.org>
4 * Copyright (c) 2022 Job Snijders <job@fastly.com>
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
34extern ASN1_OBJECT	*rsc_oid;
35
36/*
37 * Types and templates for RSC eContent - RFC 9323
38 */
39
40ASN1_ITEM_EXP ConstrainedASIdentifiers_it;
41ASN1_ITEM_EXP ConstrainedIPAddressFamily_it;
42ASN1_ITEM_EXP ConstrainedIPAddrBlocks_it;
43ASN1_ITEM_EXP FileNameAndHash_it;
44ASN1_ITEM_EXP ResourceBlock_it;
45ASN1_ITEM_EXP RpkiSignedChecklist_it;
46
47typedef struct {
48	ASIdOrRanges		*asnum;
49} ConstrainedASIdentifiers;
50
51ASN1_SEQUENCE(ConstrainedASIdentifiers) = {
52	ASN1_EXP_SEQUENCE_OF(ConstrainedASIdentifiers, asnum, ASIdOrRange, 0),
53} ASN1_SEQUENCE_END(ConstrainedASIdentifiers);
54
55typedef struct {
56	ASN1_OCTET_STRING		*addressFamily;
57	STACK_OF(IPAddressOrRange)	*addressesOrRanges;
58} ConstrainedIPAddressFamily;
59
60ASN1_SEQUENCE(ConstrainedIPAddressFamily) = {
61	ASN1_SIMPLE(ConstrainedIPAddressFamily, addressFamily,
62	    ASN1_OCTET_STRING),
63	ASN1_SEQUENCE_OF(ConstrainedIPAddressFamily, addressesOrRanges,
64	    IPAddressOrRange),
65} ASN1_SEQUENCE_END(ConstrainedIPAddressFamily);
66
67typedef STACK_OF(ConstrainedIPAddressFamily) ConstrainedIPAddrBlocks;
68DECLARE_STACK_OF(ConstrainedIPAddressFamily);
69
70ASN1_ITEM_TEMPLATE(ConstrainedIPAddrBlocks) =
71	ASN1_EX_TEMPLATE_TYPE(ASN1_TFLG_SEQUENCE_OF, 0, ConstrainedIPAddrBlocks,
72	    ConstrainedIPAddressFamily)
73ASN1_ITEM_TEMPLATE_END(ConstrainedIPAddrBlocks);
74
75typedef struct {
76	ConstrainedASIdentifiers	*asID;
77	ConstrainedIPAddrBlocks		*ipAddrBlocks;
78} ResourceBlock;
79
80ASN1_SEQUENCE(ResourceBlock) = {
81	ASN1_EXP_OPT(ResourceBlock, asID, ConstrainedASIdentifiers, 0),
82	ASN1_EXP_SEQUENCE_OF_OPT(ResourceBlock, ipAddrBlocks,
83	    ConstrainedIPAddressFamily, 1)
84} ASN1_SEQUENCE_END(ResourceBlock);
85
86typedef struct {
87	ASN1_IA5STRING		*fileName;
88	ASN1_OCTET_STRING	*hash;
89} FileNameAndHash;
90
91DECLARE_STACK_OF(FileNameAndHash);
92
93#ifndef DEFINE_STACK_OF
94#define sk_ConstrainedIPAddressFamily_num(sk) \
95    SKM_sk_num(ConstrainedIPAddressFamily, (sk))
96#define sk_ConstrainedIPAddressFamily_value(sk, i) \
97    SKM_sk_value(ConstrainedIPAddressFamily, (sk), (i))
98
99#define sk_FileNameAndHash_num(sk)	SKM_sk_num(FileNameAndHash, (sk))
100#define sk_FileNameAndHash_value(sk, i)	SKM_sk_value(FileNameAndHash, (sk), (i))
101#endif
102
103ASN1_SEQUENCE(FileNameAndHash) = {
104	ASN1_OPT(FileNameAndHash, fileName, ASN1_IA5STRING),
105	ASN1_SIMPLE(FileNameAndHash, hash, ASN1_OCTET_STRING),
106} ASN1_SEQUENCE_END(FileNameAndHash);
107
108typedef struct {
109	ASN1_INTEGER			*version;
110	ResourceBlock			*resources;
111	X509_ALGOR			*digestAlgorithm;
112	STACK_OF(FileNameAndHash)	*checkList;
113} RpkiSignedChecklist;
114
115ASN1_SEQUENCE(RpkiSignedChecklist) = {
116	ASN1_EXP_OPT(RpkiSignedChecklist, version, ASN1_INTEGER, 0),
117	ASN1_SIMPLE(RpkiSignedChecklist, resources, ResourceBlock),
118	ASN1_SIMPLE(RpkiSignedChecklist, digestAlgorithm, X509_ALGOR),
119	ASN1_SEQUENCE_OF(RpkiSignedChecklist, checkList, FileNameAndHash),
120} ASN1_SEQUENCE_END(RpkiSignedChecklist);
121
122DECLARE_ASN1_FUNCTIONS(RpkiSignedChecklist);
123IMPLEMENT_ASN1_FUNCTIONS(RpkiSignedChecklist);
124
125/*
126 * Parse asID (inside ResourceBlock)
127 * Return 0 on failure.
128 */
129static int
130rsc_parse_aslist(const char *fn, struct rsc *rsc,
131    const ConstrainedASIdentifiers *asids)
132{
133	int	 i, asz;
134
135	if (asids == NULL)
136		return 1;
137
138	if ((asz = sk_ASIdOrRange_num(asids->asnum)) == 0) {
139		warnx("%s: RSC asID empty", fn);
140		return 0;
141	}
142
143	if (asz >= MAX_AS_SIZE) {
144		warnx("%s: too many AS number entries: limit %d",
145		    fn, MAX_AS_SIZE);
146		return 0;
147	}
148
149	rsc->as = calloc(asz, sizeof(struct cert_as));
150	if (rsc->as == NULL)
151		err(1, NULL);
152
153	for (i = 0; i < asz; i++) {
154		const ASIdOrRange *aor;
155
156		aor = sk_ASIdOrRange_value(asids->asnum, i);
157
158		switch (aor->type) {
159		case ASIdOrRange_id:
160			if (!sbgp_as_id(fn, rsc->as, &rsc->asz, aor->u.id))
161				return 0;
162			break;
163		case ASIdOrRange_range:
164			if (!sbgp_as_range(fn, rsc->as, &rsc->asz,
165			    aor->u.range))
166				return 0;
167			break;
168		default:
169			warnx("%s: RSC AsList: unknown type %d", fn, aor->type);
170			return 0;
171		}
172	}
173
174	return 1;
175}
176
177static int
178rsc_parse_iplist(const char *fn, struct rsc *rsc,
179    const ConstrainedIPAddrBlocks *ipAddrBlocks)
180{
181	const ConstrainedIPAddressFamily	*af;
182	const IPAddressOrRanges			*aors;
183	const IPAddressOrRange			*aor;
184	size_t					 ipsz;
185	enum afi				 afi;
186	int					 i, j;
187
188	if (ipAddrBlocks == NULL)
189		return 1;
190
191	if (sk_ConstrainedIPAddressFamily_num(ipAddrBlocks) == 0) {
192		warnx("%s: RSC ipAddrBlocks empty", fn);
193		return 0;
194	}
195
196	for (i = 0; i < sk_ConstrainedIPAddressFamily_num(ipAddrBlocks); i++) {
197		af = sk_ConstrainedIPAddressFamily_value(ipAddrBlocks, i);
198		aors = af->addressesOrRanges;
199
200		ipsz = rsc->ipsz + sk_IPAddressOrRange_num(aors);
201		if (ipsz >= MAX_IP_SIZE) {
202			warnx("%s: too many IP address entries: limit %d",
203			    fn, MAX_IP_SIZE);
204			return 0;
205		}
206
207		rsc->ips = recallocarray(rsc->ips, rsc->ipsz, ipsz,
208		    sizeof(struct cert_ip));
209		if (rsc->ips == NULL)
210			err(1, NULL);
211
212		if (!ip_addr_afi_parse(fn, af->addressFamily, &afi)) {
213			warnx("%s: RSC: invalid AFI", fn);
214			return 0;
215		}
216
217		for (j = 0; j < sk_IPAddressOrRange_num(aors); j++) {
218			aor = sk_IPAddressOrRange_value(aors, j);
219			switch (aor->type) {
220			case IPAddressOrRange_addressPrefix:
221				if (!sbgp_addr(fn, rsc->ips,
222				    &rsc->ipsz, afi, aor->u.addressPrefix))
223					return 0;
224				break;
225			case IPAddressOrRange_addressRange:
226				if (!sbgp_addr_range(fn, rsc->ips,
227				    &rsc->ipsz, afi, aor->u.addressRange))
228					return 0;
229				break;
230			default:
231				warnx("%s: RFC 3779: IPAddressOrRange: "
232				    "unknown type %d", fn, aor->type);
233				return 0;
234			}
235		}
236	}
237
238	return 1;
239}
240
241static int
242rsc_check_digesttype(const char *fn, struct rsc *rsc, const X509_ALGOR *alg)
243{
244	const ASN1_OBJECT	*obj;
245	int			 type, nid;
246
247	X509_ALGOR_get0(&obj, &type, NULL, alg);
248
249	if (type != V_ASN1_UNDEF) {
250		warnx("%s: RSC DigestAlgorithmIdentifier unexpected parameters:"
251		    " %d", fn, type);
252		return 0;
253	}
254
255	if ((nid = OBJ_obj2nid(obj)) != NID_sha256) {
256		warnx("%s: RSC DigestAlgorithmIdentifier: want SHA256, have %s",
257		    fn, nid2str(nid));
258		return 0;
259	}
260
261	return 1;
262}
263
264/*
265 * Parse the FileNameAndHash sequence, RFC 9323, section 4.4.
266 * Return zero on failure, non-zero on success.
267 */
268static int
269rsc_parse_checklist(const char *fn, struct rsc *rsc,
270    const STACK_OF(FileNameAndHash) *checkList)
271{
272	FileNameAndHash		*fh;
273	ASN1_IA5STRING		*fileName;
274	struct rscfile		*file;
275	size_t			 sz, i;
276
277	if ((sz = sk_FileNameAndHash_num(checkList)) == 0) {
278		warnx("%s: RSC checkList needs at least one entry", fn);
279		return 0;
280	}
281
282	if (sz >= MAX_CHECKLIST_ENTRIES) {
283		warnx("%s: %zu exceeds checklist entry limit (%d)", fn, sz,
284		    MAX_CHECKLIST_ENTRIES);
285		return 0;
286	}
287
288	rsc->files = calloc(sz, sizeof(struct rscfile));
289	if (rsc->files == NULL)
290		err(1, NULL);
291	rsc->filesz = sz;
292
293	for (i = 0; i < sz; i++) {
294		fh = sk_FileNameAndHash_value(checkList, i);
295
296		file = &rsc->files[i];
297
298		if (fh->hash->length != SHA256_DIGEST_LENGTH) {
299			warnx("%s: RSC Digest: invalid SHA256 length", fn);
300			return 0;
301		}
302		memcpy(file->hash, fh->hash->data, SHA256_DIGEST_LENGTH);
303
304		if ((fileName = fh->fileName) == NULL)
305			continue;
306
307		if (!valid_filename(fileName->data, fileName->length)) {
308			warnx("%s: RSC FileNameAndHash: bad filename", fn);
309			return 0;
310		}
311
312		file->filename = strndup(fileName->data, fileName->length);
313		if (file->filename == NULL)
314			err(1, NULL);
315	}
316
317	return 1;
318}
319
320/*
321 * Parses the eContent segment of an RSC file
322 * RFC 9323, section 4
323 * Returns zero on failure, non-zero on success.
324 */
325static int
326rsc_parse_econtent(const char *fn, struct rsc *rsc, const unsigned char *d,
327    size_t dsz)
328{
329	const unsigned char	*oder;
330	RpkiSignedChecklist	*rsc_asn1;
331	ResourceBlock		*resources;
332	int			 rc = 0;
333
334	/*
335	 * RFC 9323 section 4
336	 */
337
338	oder = d;
339	if ((rsc_asn1 = d2i_RpkiSignedChecklist(NULL, &d, dsz)) == NULL) {
340		warnx("%s: RSC: failed to parse RpkiSignedChecklist", fn);
341		goto out;
342	}
343	if (d != oder + dsz) {
344		warnx("%s: %td bytes trailing garbage in eContent", fn,
345		    oder + dsz - d);
346		goto out;
347	}
348
349	if (!valid_econtent_version(fn, rsc_asn1->version, 0))
350		goto out;
351
352	resources = rsc_asn1->resources;
353	if (resources->asID == NULL && resources->ipAddrBlocks == NULL) {
354		warnx("%s: RSC: one of asID or ipAddrBlocks must be present",
355		    fn);
356		goto out;
357	}
358
359	if (!rsc_parse_aslist(fn, rsc, resources->asID))
360		goto out;
361
362	if (!rsc_parse_iplist(fn, rsc, resources->ipAddrBlocks))
363		goto out;
364
365	if (!rsc_check_digesttype(fn, rsc, rsc_asn1->digestAlgorithm))
366		goto out;
367
368	if (!rsc_parse_checklist(fn, rsc, rsc_asn1->checkList))
369		goto out;
370
371	rc = 1;
372 out:
373	RpkiSignedChecklist_free(rsc_asn1);
374	return rc;
375}
376
377/*
378 * Parse a full RFC 9323 file.
379 * Returns the RSC or NULL if the object was malformed.
380 */
381struct rsc *
382rsc_parse(X509 **x509, const char *fn, int talid, const unsigned char *der,
383    size_t len)
384{
385	struct rsc		*rsc;
386	unsigned char		*cms;
387	size_t			 cmsz;
388	struct cert		*cert = NULL;
389	time_t			 signtime = 0;
390	int			 rc = 0;
391
392	cms = cms_parse_validate(x509, fn, der, len, rsc_oid, &cmsz,
393	    &signtime);
394	if (cms == NULL)
395		return NULL;
396
397	if ((rsc = calloc(1, sizeof(struct rsc))) == NULL)
398		err(1, NULL);
399	rsc->signtime = signtime;
400
401	if (!x509_get_aia(*x509, fn, &rsc->aia))
402		goto out;
403	if (!x509_get_aki(*x509, fn, &rsc->aki))
404		goto out;
405	if (!x509_get_ski(*x509, fn, &rsc->ski))
406		goto out;
407	if (rsc->aia == NULL || rsc->aki == NULL || rsc->ski == NULL) {
408		warnx("%s: RFC 6487 section 4.8: "
409		    "missing AIA, AKI or SKI X509 extension", fn);
410		goto out;
411	}
412
413	if (!x509_get_notbefore(*x509, fn, &rsc->notbefore))
414		goto out;
415	if (!x509_get_notafter(*x509, fn, &rsc->notafter))
416		goto out;
417
418	if (X509_get_ext_by_NID(*x509, NID_sinfo_access, -1) != -1) {
419		warnx("%s: RSC: EE cert must not have an SIA extension", fn);
420		goto out;
421	}
422
423	if (x509_any_inherits(*x509)) {
424		warnx("%s: inherit elements not allowed in EE cert", fn);
425		goto out;
426	}
427
428	if (!rsc_parse_econtent(fn, rsc, cms, cmsz))
429		goto out;
430
431	if ((cert = cert_parse_ee_cert(fn, talid, *x509)) == NULL)
432		goto out;
433
434	rsc->valid = valid_rsc(fn, cert, rsc);
435
436	rc = 1;
437 out:
438	if (rc == 0) {
439		rsc_free(rsc);
440		rsc = NULL;
441		X509_free(*x509);
442		*x509 = NULL;
443	}
444	cert_free(cert);
445	free(cms);
446	return rsc;
447}
448
449/*
450 * Free an RSC pointer.
451 * Safe to call with NULL.
452 */
453void
454rsc_free(struct rsc *p)
455{
456	size_t	i;
457
458	if (p == NULL)
459		return;
460
461	for (i = 0; i < p->filesz; i++)
462		free(p->files[i].filename);
463
464	free(p->aia);
465	free(p->aki);
466	free(p->ski);
467	free(p->ips);
468	free(p->as);
469	free(p->files);
470	free(p);
471}
472