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