1 /* $OpenBSD: validate.c,v 1.14 2021/04/19 17:04:35 deraadt 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 static void 34 tracewarn(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 */ 46 static int 47 valid_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, a->cert->as, a->cert->asz); 57 if (c > 0) 58 return 1; 59 else if (c < 0) 60 return 0; 61 } 62 63 /* If it doesn't, walk up the chain. */ 64 return valid_as(a->parent, min, max); 65 } 66 67 /* 68 * Walk up the chain of certificates (really just the last one, but in 69 * the case of inheritence, the ones before) making sure that our IP 70 * prefix is covered in the first non-inheriting specification. 71 * Returns 1 if covered or 0 if not. 72 */ 73 static int 74 valid_ip(struct auth *a, enum afi afi, 75 const unsigned char *min, const unsigned char *max) 76 { 77 int c; 78 79 if (a == NULL) 80 return 0; 81 82 /* Does this certificate cover our IP prefix? */ 83 c = ip_addr_check_covered(afi, min, max, a->cert->ips, a->cert->ipsz); 84 if (c > 0) 85 return 1; 86 else if (c < 0) 87 return 0; 88 89 /* If it doesn't, walk up the chain. */ 90 return valid_ip(a->parent, afi, min, max); 91 } 92 93 /* 94 * Make sure that the SKI doesn't already exist and return the parent by 95 * its AKI. 96 * Returns the parent auth or NULL on failure. 97 */ 98 struct auth * 99 valid_ski_aki(const char *fn, struct auth_tree *auths, 100 const char *ski, const char *aki) 101 { 102 struct auth *a; 103 104 if (auth_find(auths, ski) != NULL) { 105 warnx("%s: RFC 6487: duplicate SKI", fn); 106 return NULL; 107 } 108 109 a = auth_find(auths, aki); 110 if (a == NULL) 111 warnx("%s: RFC 6487: unknown AKI", fn); 112 113 return a; 114 } 115 116 /* 117 * Authenticate a trust anchor by making sure its resources are not 118 * inheriting and that the SKI is unique. 119 * Returns 1 if valid, 0 otherwise. 120 */ 121 int 122 valid_ta(const char *fn, struct auth_tree *auths, const struct cert *cert) 123 { 124 size_t i; 125 126 /* AS and IP resources must not inherit. */ 127 if (cert->asz && cert->as[0].type == CERT_AS_INHERIT) { 128 warnx("%s: RFC 6487 (trust anchor): " 129 "inheriting AS resources", fn); 130 return 0; 131 } 132 for (i = 0; i < cert->ipsz; i++) 133 if (cert->ips[i].type == CERT_IP_INHERIT) { 134 warnx("%s: RFC 6487 (trust anchor): " 135 "inheriting IP resources", fn); 136 return 0; 137 } 138 139 /* SKI must not be a dupe. */ 140 if (auth_find(auths, cert->ski) != NULL) { 141 warnx("%s: RFC 6487: duplicate SKI", fn); 142 return 0; 143 } 144 145 return 1; 146 } 147 148 /* 149 * Validate a non-TA certificate: make sure its IP and AS resources are 150 * fully covered by those in the authority key (which must exist). 151 * Returns 1 if valid, 0 otherwise. 152 */ 153 int 154 valid_cert(const char *fn, struct auth_tree *auths, const struct cert *cert) 155 { 156 struct auth *a; 157 size_t i; 158 uint32_t min, max; 159 char buf1[64], buf2[64]; 160 161 a = valid_ski_aki(fn, auths, cert->ski, cert->aki); 162 if (a == NULL) 163 return 0; 164 165 for (i = 0; i < cert->asz; i++) { 166 if (cert->as[i].type == CERT_AS_INHERIT) 167 continue; 168 min = cert->as[i].type == CERT_AS_ID ? 169 cert->as[i].id : cert->as[i].range.min; 170 max = cert->as[i].type == CERT_AS_ID ? 171 cert->as[i].id : cert->as[i].range.max; 172 if (valid_as(a, min, max)) 173 continue; 174 warnx("%s: RFC 6487: uncovered AS: " 175 "%u--%u", fn, min, max); 176 tracewarn(a); 177 return 0; 178 } 179 180 for (i = 0; i < cert->ipsz; i++) { 181 if (valid_ip(a, cert->ips[i].afi, cert->ips[i].min, 182 cert->ips[i].max)) 183 continue; 184 switch (cert->ips[i].type) { 185 case CERT_IP_RANGE: 186 ip_addr_print(&cert->ips[i].range.min, 187 cert->ips[i].afi, buf1, sizeof(buf1)); 188 ip_addr_print(&cert->ips[i].range.max, 189 cert->ips[i].afi, buf2, sizeof(buf2)); 190 warnx("%s: RFC 6487: uncovered IP: " 191 "%s--%s", fn, buf1, buf2); 192 break; 193 case CERT_IP_ADDR: 194 ip_addr_print(&cert->ips[i].ip, 195 cert->ips[i].afi, buf1, sizeof(buf1)); 196 warnx("%s: RFC 6487: uncovered IP: " 197 "%s", fn, buf1); 198 case CERT_IP_INHERIT: 199 warnx("%s: RFC 6487: uncovered IP: " 200 "(inherit)", fn); 201 break; 202 } 203 tracewarn(a); 204 return 0; 205 } 206 207 return 1; 208 } 209 210 /* 211 * Validate our ROA: check that the SKI is unique, the AKI exists, and 212 * the IP prefix is also contained. 213 * Returns 1 if valid, 0 otherwise. 214 */ 215 int 216 valid_roa(const char *fn, struct auth_tree *auths, struct roa *roa) 217 { 218 struct auth *a; 219 size_t i; 220 char buf[64]; 221 222 a = valid_ski_aki(fn, auths, roa->ski, roa->aki); 223 if (a == NULL) 224 return 0; 225 226 if ((roa->tal = strdup(a->tal)) == NULL) 227 err(1, NULL); 228 229 for (i = 0; i < roa->ipsz; i++) { 230 if (valid_ip(a, roa->ips[i].afi, roa->ips[i].min, 231 roa->ips[i].max)) 232 continue; 233 ip_addr_print(&roa->ips[i].addr, 234 roa->ips[i].afi, buf, sizeof(buf)); 235 warnx("%s: RFC 6482: uncovered IP: " 236 "%s", fn, buf); 237 tracewarn(a); 238 return 0; 239 } 240 241 return 1; 242 } 243 244 /* 245 * Validate a file by verifying the SHA256 hash of that file. 246 * Returns 1 if valid, 0 otherwise. 247 */ 248 int 249 valid_filehash(const char *fn, const char *hash, size_t hlen) 250 { 251 SHA256_CTX ctx; 252 char filehash[SHA256_DIGEST_LENGTH]; 253 char buffer[8192]; 254 ssize_t nr; 255 int fd; 256 257 if (hlen != sizeof(filehash)) 258 errx(1, "bad hash size"); 259 260 if ((fd = open(fn, O_RDONLY)) == -1) 261 return 0; 262 263 SHA256_Init(&ctx); 264 while ((nr = read(fd, buffer, sizeof(buffer))) > 0) 265 SHA256_Update(&ctx, buffer, nr); 266 close(fd); 267 268 SHA256_Final(filehash, &ctx); 269 if (memcmp(hash, filehash, sizeof(filehash)) != 0) 270 return 0; 271 272 return 1; 273 } 274 275 /* 276 * Validate a URI to make sure it is pure ASCII and does not point backwards 277 * or doing some other silly tricks. To enforce the protocol pass either 278 * https:// or rsync:// as proto, if NULL is passed no protocol is enforced. 279 * Returns 1 if valid, 0 otherwise. 280 */ 281 int 282 valid_uri(const char *uri, size_t usz, const char *proto) 283 { 284 size_t s; 285 286 for (s = 0; s < usz; s++) 287 if (!isalnum((unsigned char)uri[s]) && 288 !ispunct((unsigned char)uri[s])) 289 return 0; 290 291 if (proto != NULL) { 292 s = strlen(proto); 293 if (strncasecmp(uri, proto, s) != 0) 294 return 0; 295 } 296 297 /* do not allow files or directories to start with a '.' */ 298 if (strstr(uri, "/.") != NULL) 299 return 0; 300 301 return 1; 302 } 303