1 /*	$OpenBSD: validate.c,v 1.29 2022/02/04 13:50:32 job 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
valid_as(struct auth * a,uint32_t min,uint32_t max)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 inheritance, 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
valid_ip(struct auth * a,enum afi afi,const unsigned char * min,const unsigned char * max)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 *
valid_ski_aki(const char * fn,struct auth_tree * auths,const char * ski,const char * aki)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
valid_ta(const char * fn,struct auth_tree * auths,const struct cert * cert)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
valid_cert(const char * fn,struct auth * a,const struct cert * cert)146 valid_cert(const char *fn, struct auth *a, const struct cert *cert)
147 {
148 	size_t		 i;
149 	uint32_t	 min, max;
150 	char		 buf1[64], buf2[64];
151 
152 	for (i = 0; i < cert->asz; i++) {
153 		if (cert->as[i].type == CERT_AS_INHERIT) {
154 			if (cert->purpose == CERT_PURPOSE_BGPSEC_ROUTER)
155 				return 0; /* BGPsec doesn't permit inheriting */
156 			continue;
157 		}
158 		min = cert->as[i].type == CERT_AS_ID ?
159 		    cert->as[i].id : cert->as[i].range.min;
160 		max = cert->as[i].type == CERT_AS_ID ?
161 		    cert->as[i].id : cert->as[i].range.max;
162 		if (valid_as(a, min, max))
163 			continue;
164 		warnx("%s: RFC 6487: uncovered AS: "
165 		    "%u--%u", fn, min, max);
166 		return 0;
167 	}
168 
169 	for (i = 0; i < cert->ipsz; i++) {
170 		if (valid_ip(a, cert->ips[i].afi, cert->ips[i].min,
171 		    cert->ips[i].max))
172 			continue;
173 		switch (cert->ips[i].type) {
174 		case CERT_IP_RANGE:
175 			ip_addr_print(&cert->ips[i].range.min,
176 			    cert->ips[i].afi, buf1, sizeof(buf1));
177 			ip_addr_print(&cert->ips[i].range.max,
178 			    cert->ips[i].afi, buf2, sizeof(buf2));
179 			warnx("%s: RFC 6487: uncovered IP: "
180 			    "%s--%s", fn, buf1, buf2);
181 			break;
182 		case CERT_IP_ADDR:
183 			ip_addr_print(&cert->ips[i].ip,
184 			    cert->ips[i].afi, buf1, sizeof(buf1));
185 			warnx("%s: RFC 6487: uncovered IP: "
186 			    "%s", fn, buf1);
187 			break;
188 		case CERT_IP_INHERIT:
189 			warnx("%s: RFC 6487: uncovered IP: "
190 			    "(inherit)", fn);
191 			break;
192 		}
193 		return 0;
194 	}
195 
196 	return 1;
197 }
198 
199 /*
200  * Validate our ROA: check that the prefixes (ipAddrBlocks) are contained.
201  * Returns 1 if valid, 0 otherwise.
202  */
203 int
valid_roa(const char * fn,struct auth * a,struct roa * roa)204 valid_roa(const char *fn, struct auth *a, struct roa *roa)
205 {
206 	size_t	 i;
207 	char	 buf[64];
208 
209 	for (i = 0; i < roa->ipsz; i++) {
210 		if (valid_ip(a, roa->ips[i].afi, roa->ips[i].min,
211 		    roa->ips[i].max))
212 			continue;
213 		ip_addr_print(&roa->ips[i].addr,
214 		    roa->ips[i].afi, buf, sizeof(buf));
215 		warnx("%s: RFC 6482: uncovered IP: "
216 		    "%s", fn, buf);
217 		return 0;
218 	}
219 
220 	return 1;
221 }
222 
223 /*
224  * Validate a file by verifying the SHA256 hash of that file.
225  * The file to check is passed as a file descriptor.
226  * Returns 1 if hash matched, 0 otherwise. Closes fd when done.
227  */
228 int
valid_filehash(int fd,const char * hash,size_t hlen)229 valid_filehash(int fd, const char *hash, size_t hlen)
230 {
231 	SHA256_CTX ctx;
232 	char	filehash[SHA256_DIGEST_LENGTH];
233 	char	buffer[8192];
234 	ssize_t	nr;
235 
236 	if (hlen != sizeof(filehash))
237 		errx(1, "bad hash size");
238 
239 	if (fd == -1)
240 		return 0;
241 
242 	SHA256_Init(&ctx);
243 	while ((nr = read(fd, buffer, sizeof(buffer))) > 0)
244 		SHA256_Update(&ctx, buffer, nr);
245 	close(fd);
246 	SHA256_Final(filehash, &ctx);
247 
248 	if (memcmp(hash, filehash, sizeof(filehash)) != 0)
249 		return 0;
250 
251 	return 1;
252 }
253 
254 /*
255  * Validate a URI to make sure it is pure ASCII and does not point backwards
256  * or doing some other silly tricks. To enforce the protocol pass either
257  * https:// or rsync:// as proto, if NULL is passed no protocol is enforced.
258  * Returns 1 if valid, 0 otherwise.
259  */
260 int
valid_uri(const char * uri,size_t usz,const char * proto)261 valid_uri(const char *uri, size_t usz, const char *proto)
262 {
263 	size_t s;
264 
265 	if (usz > MAX_URI_LENGTH)
266 		return 0;
267 
268 	for (s = 0; s < usz; s++)
269 		if (!isalnum((unsigned char)uri[s]) &&
270 		    !ispunct((unsigned char)uri[s]))
271 			return 0;
272 
273 	if (proto != NULL) {
274 		s = strlen(proto);
275 		if (strncasecmp(uri, proto, s) != 0)
276 			return 0;
277 	}
278 
279 	/* do not allow files or directories to start with a '.' */
280 	if (strstr(uri, "/.") != NULL)
281 		return 0;
282 
283 	return 1;
284 }
285 
286 /*
287  * Validate that a URI has the same host as the URI passed in proto.
288  * Returns 1 if valid, 0 otherwise.
289  */
290 int
valid_origin(const char * uri,const char * proto)291 valid_origin(const char *uri, const char *proto)
292 {
293 	const char *to;
294 
295 	/* extract end of host from proto URI */
296 	to = strstr(proto, "://");
297 	if (to == NULL)
298 		return 0;
299 	to += strlen("://");
300 	if ((to = strchr(to, '/')) == NULL)
301 		return 0;
302 
303 	/* compare hosts including the / for the start of the path section */
304 	if (strncasecmp(uri, proto, to - proto + 1) != 0)
305 		return 0;
306 
307 	return 1;
308 }
309