validate.c revision 1.10
1/*	$OpenBSD: validate.c,v 1.10 2019/11/29 05:16:54 benno Exp $ */
2/*
3 * Copyright (c) 2019 Kristaps Dzonsons <kristaps@bsd.lv>
4 *
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18#include <sys/socket.h>
19
20#include <arpa/inet.h>
21#include <assert.h>
22#include <err.h>
23#include <inttypes.h>
24#include <stdarg.h>
25#include <stdlib.h>
26#include <string.h>
27#include <unistd.h>
28
29#include <openssl/ssl.h>
30
31#include "extern.h"
32
33static void
34tracewarn(const struct auth *a)
35{
36
37	for (; a != NULL; a = a->parent)
38		warnx(" ...inheriting from: %s", a->fn);
39}
40
41/*
42 * Walk up the chain of certificates trying to match our AS number to
43 * one of the allocations in that chain.
44 * Returns 1 if covered or 0 if not.
45 */
46static int
47valid_as(struct auth *a, uint32_t min, uint32_t max)
48{
49	int	 c;
50
51	if (a == NULL)
52		return 0;
53
54	/* Does this certificate cover our AS number? */
55	if (a->cert->asz) {
56		c = as_check_covered(min, max,
57		    a->cert->as, a->cert->asz);
58		if (c > 0)
59			return 1;
60		else if (c < 0)
61			return 0;
62	}
63
64	/* If it doesn't, walk up the chain. */
65	return valid_as(a->parent, min, max);
66}
67
68/*
69 * Walk up the chain of certificates (really just the last one, but in
70 * the case of inheritence, the ones before) making sure that our IP
71 * prefix is covered in the first non-inheriting specification.
72 * Returns 1 if covered or 0 if not.
73 */
74static int
75valid_ip(struct auth *a, enum afi afi,
76    const unsigned char *min, const unsigned char *max)
77{
78	int	 c;
79
80	if (a == NULL)
81		return 0;
82
83	/* Does this certificate cover our IP prefix? */
84	c = ip_addr_check_covered(afi, min, max,
85	    a->cert->ips, a->cert->ipsz);
86	if (c > 0)
87		return 1;
88	else if (c < 0)
89		return 0;
90
91	/* If it doesn't, walk up the chain. */
92	return valid_ip(a->parent, afi, min, max);
93}
94
95/*
96 * Make sure that the SKI doesn't already exist and return the parent by
97 * its AKI.
98 * Returns the parent auth or NULL on failure.
99 */
100struct auth *
101valid_ski_aki(const char *fn, struct auth_tree *auths,
102    const char *ski, const char *aki)
103{
104	struct auth *a;
105
106	if (auth_find(auths, ski) != NULL) {
107		warnx("%s: RFC 6487: duplicate SKI", fn);
108		return NULL;
109	}
110
111	a = auth_find(auths, aki);
112	if (a == NULL)
113		warnx("%s: RFC 6487: unknown AKI", fn);
114
115	return a;
116}
117
118/*
119 * Authenticate a trust anchor by making sure its resources are not
120 * inheriting and that the SKI is unique.
121 * Returns 1 if valid, 0 otherwise.
122 */
123int
124valid_ta(const char *fn, struct auth_tree *auths, const struct cert *cert)
125{
126	size_t	 i;
127
128	/* AS and IP resources must not inherit. */
129	if (cert->asz && cert->as[0].type == CERT_AS_INHERIT) {
130		warnx("%s: RFC 6487 (trust anchor): "
131		    "inheriting AS resources", fn);
132		return 0;
133	}
134	for (i = 0; i < cert->ipsz; i++)
135		if (cert->ips[i].type == CERT_IP_INHERIT) {
136			warnx("%s: RFC 6487 (trust anchor): "
137			    "inheriting IP resources", fn);
138			return 0;
139		}
140
141	/* SKI must not be a dupe. */
142	if (auth_find(auths, cert->ski) != NULL) {
143		warnx("%s: RFC 6487: duplicate SKI", fn);
144		return 0;
145	}
146
147	return 1;
148}
149
150/*
151 * Validate a non-TA certificate: make sure its IP and AS resources are
152 * fully covered by those in the authority key (which must exist).
153 * Returns 1 if valid, 0 otherwise.
154 */
155int
156valid_cert(const char *fn, struct auth_tree *auths, const struct cert *cert)
157{
158	struct auth	*a;
159	size_t		 i;
160	uint32_t	 min, max;
161	char		 buf1[64], buf2[64];
162
163	a = valid_ski_aki(fn, auths, cert->ski, cert->aki);
164	if (a == NULL)
165		return 0;
166
167	for (i = 0; i < cert->asz; i++) {
168		if (cert->as[i].type == CERT_AS_INHERIT)
169			continue;
170		min = cert->as[i].type == CERT_AS_ID ?
171		    cert->as[i].id : cert->as[i].range.min;
172		max = cert->as[i].type == CERT_AS_ID ?
173		    cert->as[i].id : cert->as[i].range.max;
174		if (valid_as(a, min, max))
175			continue;
176		warnx("%s: RFC 6487: uncovered AS: "
177		    "%u--%u", fn, min, max);
178		tracewarn(a);
179		return 0;
180	}
181
182	for (i = 0; i < cert->ipsz; i++) {
183		if (valid_ip(a, cert->ips[i].afi, cert->ips[i].min,
184		    cert->ips[i].max))
185			continue;
186		switch (cert->ips[i].type) {
187		case CERT_IP_RANGE:
188			ip_addr_print(&cert->ips[i].range.min,
189			    cert->ips[i].afi, buf1, sizeof(buf1));
190			ip_addr_print(&cert->ips[i].range.max,
191			    cert->ips[i].afi, buf2, sizeof(buf2));
192			warnx("%s: RFC 6487: uncovered IP: "
193			    "%s--%s", fn, buf1, buf2);
194			break;
195		case CERT_IP_ADDR:
196			ip_addr_print(&cert->ips[i].ip,
197			    cert->ips[i].afi, buf1, sizeof(buf1));
198			warnx("%s: RFC 6487: uncovered IP: "
199			    "%s", fn, buf1);
200		case CERT_IP_INHERIT:
201			warnx("%s: RFC 6487: uncovered IP: "
202			    "(inherit)", fn);
203			break;
204		}
205		tracewarn(a);
206		return 0;
207	}
208
209	return 1;
210}
211
212/*
213 * Validate our ROA: check that the SKI is unique, the AKI exists, and
214 * the IP prefix is also contained.
215 * Returns 1 if valid, 0 otherwise.
216 */
217int
218valid_roa(const char *fn, struct auth_tree *auths, struct roa *roa)
219{
220	struct auth	*a;
221	size_t	 i;
222	char	 buf[64];
223
224	a = valid_ski_aki(fn, auths, roa->ski, roa->aki);
225	if (a == NULL)
226		return 0;
227
228	if ((roa->tal = strdup(a->tal)) == NULL)
229		err(1, NULL);
230
231	for (i = 0; i < roa->ipsz; i++) {
232		if (valid_ip(a, roa->ips[i].afi, roa->ips[i].min,
233		    roa->ips[i].max))
234			continue;
235		ip_addr_print(&roa->ips[i].addr,
236		    roa->ips[i].afi, buf, sizeof(buf));
237		warnx("%s: RFC 6482: uncovered IP: "
238		    "%s", fn, buf);
239		tracewarn(a);
240		return 0;
241	}
242
243	return 1;
244}
245