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