1 /*- 2 * Copyright (c) 2017-2018, Juniper Networks, Inc. 3 * 4 * Redistribution and use in source and binary forms, with or without 5 * modification, are permitted provided that the following conditions 6 * are met: 7 * 1. Redistributions of source code must retain the above copyright 8 * notice, this list of conditions and the following disclaimer. 9 * 2. Redistributions in binary form must reproduce the above copyright 10 * notice, this list of conditions and the following disclaimer in the 11 * documentation and/or other materials provided with the distribution. 12 * 13 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 14 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 15 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 16 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 17 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 18 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 19 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 */ 25 #include <sys/cdefs.h> 26 __FBSDID("$FreeBSD$"); 27 #include <sys/queue.h> 28 29 #include "libsecureboot-priv.h" 30 31 32 struct fingerprint_info { 33 char *fi_prefix; /**< manifest entries relative to */ 34 char *fi_skip; /**< manifest entries prefixed with */ 35 const char *fi_data; /**< manifest data */ 36 size_t fi_prefix_len; /**< length of prefix */ 37 size_t fi_skip_len; /**< length of skip */ 38 dev_t fi_dev; /**< device id */ 39 LIST_ENTRY(fingerprint_info) entries; 40 }; 41 42 static LIST_HEAD(, fingerprint_info) fi_list; 43 44 static void 45 fingerprint_info_init(void) 46 { 47 static int once; 48 49 if (once) 50 return; 51 LIST_INIT(&fi_list); 52 once = 1; 53 } 54 55 /** 56 * @brief 57 * add manifest data to list 58 * 59 * list is kept sorted by longest prefix. 60 * 61 * @param[in] prefix 62 * path that all manifest entries are resolved via 63 * 64 * @param[in] skip 65 * optional prefix within manifest entries which should be skipped 66 * 67 * @param[in] data 68 * manifest data 69 */ 70 void 71 fingerprint_info_add(const char *filename, const char *prefix, 72 const char *skip, const char *data, struct stat *stp) 73 { 74 struct fingerprint_info *fip, *nfip, *lfip; 75 char *cp; 76 int n; 77 78 fingerprint_info_init(); 79 nfip = malloc(sizeof(struct fingerprint_info)); 80 if (nfip == NULL) { 81 #ifdef _STANDALONE 82 printf("%s: out of memory! %lu\n", __func__, 83 (unsigned long)sizeof(struct fingerprint_info)); 84 #endif 85 return; 86 } 87 if (prefix) { 88 nfip->fi_prefix = strdup(prefix); 89 } else { 90 if (!filename) { 91 free(nfip); 92 return; 93 } 94 nfip->fi_prefix = strdup(filename); 95 cp = strrchr(nfip->fi_prefix, '/'); 96 if (cp == nfip->fi_prefix) { 97 cp[1] = '\0'; 98 } else if (cp) { 99 *cp = '\0'; 100 } else { 101 free(nfip->fi_prefix); 102 free(nfip); 103 return; 104 } 105 } 106 /* collapse any trailing ..[/] */ 107 n = 0; 108 while ((cp = strrchr(nfip->fi_prefix, '/')) > nfip->fi_prefix) { 109 if (cp[1] == '\0') { /* trailing "/" */ 110 *cp = '\0'; 111 continue; 112 } 113 if (strcmp(&cp[1], "..") == 0) { 114 n++; 115 *cp = '\0'; 116 continue; 117 } 118 if (n > 0) { 119 n--; 120 *cp = '\0'; 121 } 122 if (n == 0) 123 break; 124 } 125 nfip->fi_dev = stp->st_dev; 126 #ifdef UNIT_TEST 127 nfip->fi_dev = 0; 128 #endif 129 nfip->fi_data = data; 130 nfip->fi_prefix_len = strlen(nfip->fi_prefix); 131 if (skip) { 132 nfip->fi_skip_len = strlen(skip); 133 if (nfip->fi_skip_len) 134 nfip->fi_skip = strdup(skip); 135 else 136 nfip->fi_skip = NULL; 137 } else { 138 nfip->fi_skip = NULL; 139 nfip->fi_skip_len = 0; 140 } 141 142 if (LIST_EMPTY(&fi_list)) { 143 LIST_INSERT_HEAD(&fi_list, nfip, entries); 144 DEBUG_PRINTF(4, ("inserted %zu %s at head\n", 145 nfip->fi_prefix_len, nfip->fi_prefix)); 146 return; 147 } 148 LIST_FOREACH(fip, &fi_list, entries) { 149 if (nfip->fi_prefix_len >= fip->fi_prefix_len) { 150 LIST_INSERT_BEFORE(fip, nfip, entries); 151 DEBUG_PRINTF(4, ("inserted %zu %s before %zu %s\n", 152 nfip->fi_prefix_len, nfip->fi_prefix, 153 fip->fi_prefix_len, fip->fi_prefix)); 154 return; 155 } 156 lfip = fip; 157 } 158 LIST_INSERT_AFTER(lfip, nfip, entries); 159 DEBUG_PRINTF(4, ("inserted %zu %s after %zu %s\n", 160 nfip->fi_prefix_len, nfip->fi_prefix, 161 lfip->fi_prefix_len, lfip->fi_prefix)); 162 } 163 164 #ifdef MANIFEST_SKIP_MAYBE 165 /* 166 * Deal with old incompatible boot/manifest 167 * if fp[-1] is '/' and start of entry matches 168 * MANIFEST_SKIP_MAYBE, we want it. 169 */ 170 static char * 171 maybe_skip(char *fp, struct fingerprint_info *fip, size_t *nplenp) 172 { 173 char *tp; 174 175 tp = fp - sizeof(MANIFEST_SKIP_MAYBE); 176 177 if (tp >= fip->fi_data) { 178 DEBUG_PRINTF(3, ("maybe: %.48s\n", tp)); 179 if ((tp == fip->fi_data || tp[-1] == '\n') && 180 strncmp(tp, MANIFEST_SKIP_MAYBE, 181 sizeof(MANIFEST_SKIP_MAYBE) - 1) == 0) { 182 fp = tp; 183 *nplenp += sizeof(MANIFEST_SKIP_MAYBE); 184 } 185 } 186 return (fp); 187 } 188 #endif 189 190 char * 191 fingerprint_info_lookup(int fd, const char *path) 192 { 193 char pbuf[MAXPATHLEN+1]; 194 char nbuf[MAXPATHLEN+1]; 195 struct stat st; 196 struct fingerprint_info *fip; 197 char *cp, *ep, *fp, *np; 198 const char *prefix; 199 size_t n, plen, nlen, nplen; 200 dev_t dev = 0; 201 202 fingerprint_info_init(); 203 204 n = strlcpy(pbuf, path, sizeof(pbuf)); 205 if (n >= sizeof(pbuf)) 206 return (NULL); 207 if (fstat(fd, &st) == 0) 208 dev = st.st_dev; 209 #ifdef UNIT_TEST 210 dev = 0; 211 #endif 212 /* 213 * get the first entry - it will have longest prefix 214 * so we can can work out how to initially split path 215 */ 216 fip = LIST_FIRST(&fi_list); 217 if (!fip) 218 return (NULL); 219 prefix = pbuf; 220 ep = NULL; 221 cp = &pbuf[fip->fi_prefix_len]; 222 do { 223 if (ep) { 224 *ep = '/'; 225 cp -= 2; 226 if (cp < pbuf) 227 break; 228 } 229 nlen = plen = 0; /* keep gcc quiet */ 230 if (cp > pbuf) { 231 for ( ; cp >= pbuf && *cp != '/'; cp--) 232 ; /* nothing */ 233 if (cp > pbuf) { 234 ep = cp++; 235 *ep = '\0'; 236 } else { 237 cp = pbuf; 238 } 239 if (ep) { 240 plen = ep - pbuf; 241 nlen = n - plen - 1; 242 } 243 } 244 if (cp == pbuf) { 245 prefix = "/"; 246 plen = 1; 247 if (*cp == '/') { 248 nlen = n - 1; 249 cp++; 250 } else 251 nlen = n; 252 ep = NULL; 253 } 254 255 DEBUG_PRINTF(2, ("looking for %s %zu %s\n", prefix, plen, cp)); 256 257 LIST_FOREACH(fip, &fi_list, entries) { 258 DEBUG_PRINTF(4, ("at %zu %s\n", 259 fip->fi_prefix_len, fip->fi_prefix)); 260 261 if (fip->fi_prefix_len < plen) { 262 DEBUG_PRINTF(3, ("skipping prefix=%s %zu %zu\n", 263 fip->fi_prefix, fip->fi_prefix_len, 264 plen)); 265 break; 266 } 267 if (fip->fi_prefix_len == plen) { 268 if (fip->fi_dev != 0 && fip->fi_dev != dev) { 269 DEBUG_PRINTF(3, ( 270 "skipping dev=%ld != %ld\n", 271 (long)fip->fi_dev, 272 (long)dev)); 273 continue; 274 } 275 if (strcmp(prefix, fip->fi_prefix)) { 276 DEBUG_PRINTF(3, ( 277 "skipping prefix=%s\n", 278 fip->fi_prefix)); 279 continue; 280 } 281 DEBUG_PRINTF(3, ("checking prefix=%s\n", 282 fip->fi_prefix)); 283 if (fip->fi_skip_len) { 284 np = nbuf; 285 nplen = snprintf(nbuf, sizeof(nbuf), 286 "%s/%s", 287 fip->fi_skip, cp); 288 nplen = MIN(nplen, sizeof(nbuf) - 1); 289 } else { 290 np = cp; 291 nplen = nlen; 292 } 293 DEBUG_PRINTF(3, ("lookup: '%s'\n", np)); 294 if (!(fp = strstr(fip->fi_data, np))) 295 continue; 296 #ifdef MANIFEST_SKIP_MAYBE 297 if (fip->fi_skip_len == 0 && 298 fp > fip->fi_data && fp[-1] == '/') { 299 fp = maybe_skip(fp, fip, &nplen); 300 } 301 #endif 302 /* 303 * when we find a match: 304 * fp[nplen] will be space and 305 * fp will be fip->fi_data or 306 * fp[-1] will be \n 307 */ 308 if (!((fp == fip->fi_data || fp[-1] == '\n') && 309 fp[nplen] == ' ')) { 310 do { 311 fp++; 312 fp = strstr(fp, np); 313 if (fp) { 314 #ifdef MANIFEST_SKIP_MAYBE 315 if (fip->fi_skip_len == 0 && 316 fp > fip->fi_data && 317 fp[-1] == '/') { 318 fp = maybe_skip(fp, fip, &nplen); 319 } 320 #endif 321 DEBUG_PRINTF(3, 322 ("fp[-1]=%#x fp[%zu]=%#x fp=%.78s\n", 323 fp[-1], nplen, 324 fp[nplen], 325 fp)); 326 } 327 } while (fp != NULL && 328 !(fp[-1] == '\n' && 329 fp[nplen] == ' ')); 330 if (!fp) 331 continue; 332 } 333 DEBUG_PRINTF(2, ("found %.78s\n", fp)); 334 /* we have a match! */ 335 for (cp = &fp[nplen]; *cp == ' '; cp++) 336 ; /* nothing */ 337 return (cp); 338 } else { 339 DEBUG_PRINTF(3, 340 ("Ignoring prefix=%s\n", fip->fi_prefix)); 341 } 342 } 343 } while (cp > &pbuf[1]); 344 345 return (NULL); 346 } 347 348 static int 349 verify_fingerprint(int fd, const char *path, const char *cp, off_t off) 350 { 351 unsigned char buf[PAGE_SIZE]; 352 const br_hash_class *md; 353 br_hash_compat_context mctx; 354 size_t hlen; 355 int n; 356 357 if (strncmp(cp, "no_hash", 7) == 0) { 358 return (VE_FINGERPRINT_IGNORE); 359 } else if (strncmp(cp, "sha256=", 7) == 0) { 360 md = &br_sha256_vtable; 361 hlen = br_sha256_SIZE; 362 cp += 7; 363 #ifdef VE_SHA1_SUPPORT 364 } else if (strncmp(cp, "sha1=", 5) == 0) { 365 md = &br_sha1_vtable; 366 hlen = br_sha1_SIZE; 367 cp += 5; 368 #endif 369 #ifdef VE_SHA384_SUPPORT 370 } else if (strncmp(cp, "sha384=", 7) == 0) { 371 md = &br_sha384_vtable; 372 hlen = br_sha384_SIZE; 373 cp += 7; 374 #endif 375 #ifdef VE_SHA512_SUPPORT 376 } else if (strncmp(cp, "sha512=", 7) == 0) { 377 md = &br_sha512_vtable; 378 hlen = br_sha512_SIZE; 379 cp += 7; 380 #endif 381 } else { 382 ve_error_set("%s: no supported fingerprint", path); 383 return (VE_FINGERPRINT_UNKNOWN); 384 } 385 386 md->init(&mctx.vtable); 387 if (off) 388 lseek(fd, 0, SEEK_SET); 389 do { 390 n = read(fd, buf, sizeof(buf)); 391 if (n < 0) 392 return (n); 393 if (n > 0) 394 md->update(&mctx.vtable, buf, n); 395 } while (n > 0); 396 lseek(fd, off, SEEK_SET); 397 return (ve_check_hash(&mctx, md, path, cp, hlen)); 398 } 399 400 401 /** 402 * @brief 403 * verify an open file 404 * 405 * @param[in] fd 406 * open descriptor 407 * 408 * @param[in] path 409 * pathname to open 410 * 411 * @param[in] off 412 * current offset 413 * 414 * @return 0, VE_FINGERPRINT_OK or VE_FINGERPRINT_NONE, VE_FINGERPRINT_WRONG 415 */ 416 int 417 verify_fd(int fd, const char *path, off_t off, struct stat *stp) 418 { 419 struct stat st; 420 char *cp; 421 int rc; 422 423 if (!stp) { 424 if (fstat(fd, &st) == 0) 425 stp = &st; 426 } 427 if (stp && !S_ISREG(stp->st_mode)) 428 return (0); /* not relevant */ 429 cp = fingerprint_info_lookup(fd, path); 430 if (!cp) { 431 ve_error_set("%s: no entry", path); 432 return (VE_FINGERPRINT_NONE); 433 } 434 rc = verify_fingerprint(fd, path, cp, off); 435 switch (rc) { 436 case VE_FINGERPRINT_OK: 437 case VE_FINGERPRINT_IGNORE: 438 case VE_FINGERPRINT_UNKNOWN: 439 return (rc); 440 default: 441 return (VE_FINGERPRINT_WRONG); 442 } 443 } 444 445 /** 446 * @brief 447 * open a file if it can be verified 448 * 449 * @param[in] path 450 * pathname to open 451 * 452 * @param[in] flags 453 * flags for open 454 * 455 * @return fd or VE_FINGERPRINT_NONE, VE_FINGERPRINT_WRONG 456 */ 457 int 458 verify_open(const char *path, int flags) 459 { 460 int fd; 461 int rc; 462 463 if ((fd = open(path, flags)) >= 0) { 464 if ((rc = verify_fd(fd, path, 0, NULL)) < 0) { 465 close(fd); 466 fd = rc; 467 } 468 } 469 return (fd); 470 } 471