xref: /openbsd-src/usr.sbin/rpki-client/validate.c (revision 5a38ef86d0b61900239c7913d24a05e7b88a58f0)
1 /*	$OpenBSD: validate.c,v 1.22 2021/11/04 11:32:55 claudio 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 <ctype.h>
23 #include <err.h>
24 #include <fcntl.h>
25 #include <inttypes.h>
26 #include <stdarg.h>
27 #include <stdlib.h>
28 #include <string.h>
29 #include <unistd.h>
30 
31 #include "extern.h"
32 
33 /*
34  * Walk up the chain of certificates trying to match our AS number to
35  * one of the allocations in that chain.
36  * Returns 1 if covered or 0 if not.
37  */
38 static int
39 valid_as(struct auth *a, uint32_t min, uint32_t max)
40 {
41 	int	 c;
42 
43 	if (a == NULL)
44 		return 0;
45 
46 	/* Does this certificate cover our AS number? */
47 	if (a->cert->asz) {
48 		c = as_check_covered(min, max, a->cert->as, a->cert->asz);
49 		if (c > 0)
50 			return 1;
51 		else if (c < 0)
52 			return 0;
53 	}
54 
55 	/* If it doesn't, walk up the chain. */
56 	return valid_as(a->parent, min, max);
57 }
58 
59 /*
60  * Walk up the chain of certificates (really just the last one, but in
61  * the case of inheritence, the ones before) making sure that our IP
62  * prefix is covered in the first non-inheriting specification.
63  * Returns 1 if covered or 0 if not.
64  */
65 static int
66 valid_ip(struct auth *a, enum afi afi,
67     const unsigned char *min, const unsigned char *max)
68 {
69 	int	 c;
70 
71 	if (a == NULL)
72 		return 0;
73 
74 	/* Does this certificate cover our IP prefix? */
75 	c = ip_addr_check_covered(afi, min, max, a->cert->ips, a->cert->ipsz);
76 	if (c > 0)
77 		return 1;
78 	else if (c < 0)
79 		return 0;
80 
81 	/* If it doesn't, walk up the chain. */
82 	return valid_ip(a->parent, afi, min, max);
83 }
84 
85 /*
86  * Make sure that the SKI doesn't already exist and return the parent by
87  * its AKI.
88  * Returns the parent auth or NULL on failure.
89  */
90 struct auth *
91 valid_ski_aki(const char *fn, struct auth_tree *auths,
92     const char *ski, const char *aki)
93 {
94 	struct auth *a;
95 
96 	if (auth_find(auths, ski) != NULL) {
97 		warnx("%s: RFC 6487: duplicate SKI", fn);
98 		return NULL;
99 	}
100 
101 	a = auth_find(auths, aki);
102 	if (a == NULL)
103 		warnx("%s: RFC 6487: unknown AKI", fn);
104 
105 	return a;
106 }
107 
108 /*
109  * Authenticate a trust anchor by making sure its resources are not
110  * inheriting and that the SKI is unique.
111  * Returns 1 if valid, 0 otherwise.
112  */
113 int
114 valid_ta(const char *fn, struct auth_tree *auths, const struct cert *cert)
115 {
116 	size_t	 i;
117 
118 	/* AS and IP resources must not inherit. */
119 	if (cert->asz && cert->as[0].type == CERT_AS_INHERIT) {
120 		warnx("%s: RFC 6487 (trust anchor): "
121 		    "inheriting AS resources", fn);
122 		return 0;
123 	}
124 	for (i = 0; i < cert->ipsz; i++)
125 		if (cert->ips[i].type == CERT_IP_INHERIT) {
126 			warnx("%s: RFC 6487 (trust anchor): "
127 			    "inheriting IP resources", fn);
128 			return 0;
129 		}
130 
131 	/* SKI must not be a dupe. */
132 	if (auth_find(auths, cert->ski) != NULL) {
133 		warnx("%s: RFC 6487: duplicate SKI", fn);
134 		return 0;
135 	}
136 
137 	return 1;
138 }
139 
140 /*
141  * Validate a non-TA certificate: make sure its IP and AS resources are
142  * fully covered by those in the authority key (which must exist).
143  * Returns 1 if valid, 0 otherwise.
144  */
145 int
146 valid_cert(const char *fn, struct auth_tree *auths, const struct cert *cert)
147 {
148 	struct auth	*a;
149 	size_t		 i;
150 	uint32_t	 min, max;
151 	char		 buf1[64], buf2[64];
152 
153 	a = valid_ski_aki(fn, auths, cert->ski, cert->aki);
154 	if (a == NULL)
155 		return 0;
156 
157 	for (i = 0; i < cert->asz; i++) {
158 		if (cert->as[i].type == CERT_AS_INHERIT) {
159 			if (cert->purpose == CERT_PURPOSE_BGPSEC_ROUTER)
160 				return 0; /* BGPsec doesn't permit inheriting */
161 			continue;
162 		}
163 		min = cert->as[i].type == CERT_AS_ID ?
164 		    cert->as[i].id : cert->as[i].range.min;
165 		max = cert->as[i].type == CERT_AS_ID ?
166 		    cert->as[i].id : cert->as[i].range.max;
167 		if (valid_as(a, min, max))
168 			continue;
169 		warnx("%s: RFC 6487: uncovered AS: "
170 		    "%u--%u", fn, min, max);
171 		return 0;
172 	}
173 
174 	for (i = 0; i < cert->ipsz; i++) {
175 		if (valid_ip(a, cert->ips[i].afi, cert->ips[i].min,
176 		    cert->ips[i].max))
177 			continue;
178 		switch (cert->ips[i].type) {
179 		case CERT_IP_RANGE:
180 			ip_addr_print(&cert->ips[i].range.min,
181 			    cert->ips[i].afi, buf1, sizeof(buf1));
182 			ip_addr_print(&cert->ips[i].range.max,
183 			    cert->ips[i].afi, buf2, sizeof(buf2));
184 			warnx("%s: RFC 6487: uncovered IP: "
185 			    "%s--%s", fn, buf1, buf2);
186 			break;
187 		case CERT_IP_ADDR:
188 			ip_addr_print(&cert->ips[i].ip,
189 			    cert->ips[i].afi, buf1, sizeof(buf1));
190 			warnx("%s: RFC 6487: uncovered IP: "
191 			    "%s", fn, buf1);
192 			break;
193 		case CERT_IP_INHERIT:
194 			warnx("%s: RFC 6487: uncovered IP: "
195 			    "(inherit)", fn);
196 			break;
197 		}
198 		return 0;
199 	}
200 
201 	return 1;
202 }
203 
204 /*
205  * Validate our ROA: check that the SKI is unique, the AKI exists, and
206  * the IP prefix is also contained.
207  * Returns 1 if valid, 0 otherwise.
208  */
209 int
210 valid_roa(const char *fn, struct auth_tree *auths, struct roa *roa)
211 {
212 	struct auth	*a;
213 	size_t	 i;
214 	char	 buf[64];
215 
216 	a = valid_ski_aki(fn, auths, roa->ski, roa->aki);
217 	if (a == NULL)
218 		return 0;
219 
220 	roa->talid = a->cert->talid;
221 
222 	for (i = 0; i < roa->ipsz; i++) {
223 		if (valid_ip(a, roa->ips[i].afi, roa->ips[i].min,
224 		    roa->ips[i].max))
225 			continue;
226 		ip_addr_print(&roa->ips[i].addr,
227 		    roa->ips[i].afi, buf, sizeof(buf));
228 		warnx("%s: RFC 6482: uncovered IP: "
229 		    "%s", fn, buf);
230 		return 0;
231 	}
232 
233 	return 1;
234 }
235 
236 /*
237  * Validate a filename listed on a Manifest.
238  * draft-ietf-sidrops-6486bis section 4.2.2
239  * Returns 1 if filename is valid, otherwise 0.
240  */
241 int
242 valid_filename(const char *fn)
243 {
244 	size_t			 sz;
245 	const unsigned char	*c;
246 
247 	sz = strlen(fn);
248 	if (sz < 5)
249 		return 0;
250 
251 	for (c = fn; *c != '\0'; ++c)
252 		if (!isalnum(*c) && *c != '-' && *c != '_' && *c != '.')
253 			return 0;
254 
255 	if (strchr(fn, '.') != strrchr(fn, '.'))
256 		return 0;
257 
258 	if (strcasecmp(fn + sz - 4, ".cer") == 0)
259 		return 1;
260 	if (strcasecmp(fn + sz - 4, ".crl") == 0)
261 		return 1;
262 	if (strcasecmp(fn + sz - 4, ".gbr") == 0)
263 		return 1;
264 	if (strcasecmp(fn + sz - 4, ".roa") == 0)
265 		return 1;
266 
267 	return 0;
268 }
269 
270 /*
271  * Validate a file by verifying the SHA256 hash of that file.
272  * Returns 1 if valid, 0 otherwise.
273  */
274 int
275 valid_filehash(const char *fn, const char *hash, size_t hlen)
276 {
277 	SHA256_CTX ctx;
278 	char	filehash[SHA256_DIGEST_LENGTH];
279 	char	buffer[8192];
280 	ssize_t	nr;
281 	int	fd;
282 
283 	if (hlen != sizeof(filehash))
284 		errx(1, "bad hash size");
285 
286 	if ((fd = open(fn, O_RDONLY)) == -1)
287 		return 0;
288 
289 	SHA256_Init(&ctx);
290 	while ((nr = read(fd, buffer, sizeof(buffer))) > 0)
291 		SHA256_Update(&ctx, buffer, nr);
292 	close(fd);
293 
294 	SHA256_Final(filehash, &ctx);
295 	if (memcmp(hash, filehash, sizeof(filehash)) != 0)
296 		return 0;
297 
298 	return 1;
299 }
300 
301 /*
302  * Validate a URI to make sure it is pure ASCII and does not point backwards
303  * or doing some other silly tricks. To enforce the protocol pass either
304  * https:// or rsync:// as proto, if NULL is passed no protocol is enforced.
305  * Returns 1 if valid, 0 otherwise.
306  */
307 int
308 valid_uri(const char *uri, size_t usz, const char *proto)
309 {
310 	size_t s;
311 
312 	if (usz > MAX_URI_LENGTH)
313 		return 0;
314 
315 	for (s = 0; s < usz; s++)
316 		if (!isalnum((unsigned char)uri[s]) &&
317 		    !ispunct((unsigned char)uri[s]))
318 			return 0;
319 
320 	if (proto != NULL) {
321 		s = strlen(proto);
322 		if (strncasecmp(uri, proto, s) != 0)
323 			return 0;
324 	}
325 
326 	/* do not allow files or directories to start with a '.' */
327 	if (strstr(uri, "/.") != NULL)
328 		return 0;
329 
330 	return 1;
331 }
332 
333 /*
334  * Validate that a URI has the same host as the URI passed in proto.
335  * Returns 1 if valid, 0 otherwise.
336  */
337 int
338 valid_origin(const char *uri, const char *proto)
339 {
340 	const char *to;
341 
342 	/* extract end of host from proto URI */
343 	to = strstr(proto, "://");
344 	if (to == NULL)
345 		return 0;
346 	to += strlen("://");
347 	if ((to = strchr(to, '/')) == NULL)
348 		return 0;
349 
350 	/* compare hosts including the / for the start of the path section */
351 	if (strncasecmp(uri, proto, to - proto + 1) != 0)
352 		return 0;
353 
354 	return 1;
355 }
356