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 (prefix) { 81 nfip->fi_prefix = strdup(prefix); 82 } else { 83 if (!filename) { 84 free(nfip); 85 return; 86 } 87 nfip->fi_prefix = strdup(filename); 88 cp = strrchr(nfip->fi_prefix, '/'); 89 if (cp) 90 *cp = '\0'; 91 else { 92 free(nfip->fi_prefix); 93 free(nfip); 94 return; 95 } 96 } 97 /* collapse any trailing ..[/] */ 98 n = 0; 99 while ((cp = strrchr(nfip->fi_prefix, '/')) != NULL) { 100 if (cp[1] == '\0') { /* trailing "/" */ 101 *cp = '\0'; 102 continue; 103 } 104 if (strcmp(&cp[1], "..") == 0) { 105 n++; 106 *cp = '\0'; 107 continue; 108 } 109 if (n > 0) { 110 n--; 111 *cp = '\0'; 112 } 113 if (n == 0) 114 break; 115 } 116 #ifdef UNIT_TEST 117 nfip->fi_dev = 0; 118 #else 119 nfip->fi_dev = stp->st_dev; 120 #endif 121 nfip->fi_data = data; 122 nfip->fi_prefix_len = strlen(nfip->fi_prefix); 123 if (skip) { 124 nfip->fi_skip_len = strlen(skip); 125 if (nfip->fi_skip_len) 126 nfip->fi_skip = strdup(skip); 127 else 128 nfip->fi_skip = NULL; 129 } else { 130 nfip->fi_skip = NULL; 131 nfip->fi_skip_len = 0; 132 } 133 134 if (LIST_EMPTY(&fi_list)) { 135 LIST_INSERT_HEAD(&fi_list, nfip, entries); 136 DEBUG_PRINTF(4, ("inserted %zu %s at head\n", 137 nfip->fi_prefix_len, nfip->fi_prefix)); 138 return; 139 } 140 LIST_FOREACH(fip, &fi_list, entries) { 141 if (nfip->fi_prefix_len >= fip->fi_prefix_len) { 142 LIST_INSERT_BEFORE(fip, nfip, entries); 143 DEBUG_PRINTF(4, ("inserted %zu %s before %zu %s\n", 144 nfip->fi_prefix_len, nfip->fi_prefix, 145 fip->fi_prefix_len, fip->fi_prefix)); 146 return; 147 } 148 lfip = fip; 149 } 150 LIST_INSERT_AFTER(lfip, nfip, entries); 151 DEBUG_PRINTF(4, ("inserted %zu %s after %zu %s\n", 152 nfip->fi_prefix_len, nfip->fi_prefix, 153 lfip->fi_prefix_len, lfip->fi_prefix)); 154 } 155 156 #ifdef MANIFEST_SKIP_MAYBE 157 /* 158 * Deal with old incompatible boot/manifest 159 * if fp[-1] is '/' and start of entry matches 160 * MANIFEST_SKIP_MAYBE, we want it. 161 */ 162 static char * 163 maybe_skip(char *fp, struct fingerprint_info *fip, size_t *nplenp) 164 { 165 char *tp; 166 167 tp = fp - sizeof(MANIFEST_SKIP_MAYBE); 168 169 if (tp >= fip->fi_data) { 170 DEBUG_PRINTF(3, ("maybe: %.48s\n", tp)); 171 if ((tp == fip->fi_data || tp[-1] == '\n') && 172 strncmp(tp, MANIFEST_SKIP_MAYBE, 173 sizeof(MANIFEST_SKIP_MAYBE) - 1) == 0) { 174 fp = tp; 175 *nplenp += sizeof(MANIFEST_SKIP_MAYBE); 176 } 177 } 178 return (fp); 179 } 180 #endif 181 182 char * 183 fingerprint_info_lookup(int fd, const char *path) 184 { 185 char pbuf[MAXPATHLEN+1]; 186 char nbuf[MAXPATHLEN+1]; 187 struct stat st; 188 struct fingerprint_info *fip; 189 char *cp, *ep, *fp, *np; 190 const char *prefix; 191 size_t n, plen, nlen, nplen; 192 dev_t dev = 0; 193 194 fingerprint_info_init(); 195 196 n = strlcpy(pbuf, path, sizeof(pbuf)); 197 if (n >= sizeof(pbuf)) 198 return (NULL); 199 #ifndef UNIT_TEST 200 if (fstat(fd, &st) == 0) 201 dev = st.st_dev; 202 #endif 203 /* 204 * get the first entry - it will have longest prefix 205 * so we can can work out how to initially split path 206 */ 207 fip = LIST_FIRST(&fi_list); 208 if (!fip) 209 return (NULL); 210 prefix = pbuf; 211 ep = NULL; 212 cp = &pbuf[fip->fi_prefix_len]; 213 do { 214 if (ep) { 215 *ep = '/'; 216 cp -= 2; 217 if (cp < pbuf) 218 break; 219 } 220 nlen = plen = 0; /* keep gcc quiet */ 221 if (cp > pbuf) { 222 for ( ; cp >= pbuf && *cp != '/'; cp--) 223 ; /* nothing */ 224 if (cp > pbuf) { 225 ep = cp++; 226 *ep = '\0'; 227 } else { 228 cp = pbuf; 229 } 230 if (ep) { 231 plen = ep - pbuf; 232 nlen = n - plen - 1; 233 } 234 } 235 if (cp == pbuf) { 236 prefix = "/"; 237 plen = 1; 238 if (*cp == '/') { 239 nlen = n - 1; 240 cp++; 241 } else 242 nlen = n; 243 ep = NULL; 244 } 245 246 DEBUG_PRINTF(2, ("looking for %s %zu %s\n", prefix, plen, cp)); 247 248 LIST_FOREACH(fip, &fi_list, entries) { 249 DEBUG_PRINTF(4, ("at %zu %s\n", 250 fip->fi_prefix_len, fip->fi_prefix)); 251 252 if (fip->fi_prefix_len < plen) { 253 DEBUG_PRINTF(3, ("skipping prefix=%s %zu %zu\n", 254 fip->fi_prefix, fip->fi_prefix_len, 255 plen)); 256 break; 257 } 258 if (fip->fi_prefix_len == plen) { 259 if (fip->fi_dev != 0 && fip->fi_dev != dev) { 260 DEBUG_PRINTF(3, ( 261 "skipping dev=%ld != %ld\n", 262 (long)fip->fi_dev, 263 (long)dev)); 264 continue; 265 } 266 if (strcmp(prefix, fip->fi_prefix)) { 267 DEBUG_PRINTF(3, ( 268 "skipping prefix=%s\n", 269 fip->fi_prefix)); 270 continue; 271 } 272 DEBUG_PRINTF(3, ("checking prefix=%s\n", 273 fip->fi_prefix)); 274 if (fip->fi_skip_len) { 275 np = nbuf; 276 nplen = snprintf(nbuf, sizeof(nbuf), 277 "%s/%s", 278 fip->fi_skip, cp); 279 nplen = MIN(nplen, sizeof(nbuf) - 1); 280 } else { 281 np = cp; 282 nplen = nlen; 283 } 284 DEBUG_PRINTF(3, ("lookup: '%s'\n", np)); 285 if (!(fp = strstr(fip->fi_data, np))) 286 continue; 287 #ifdef MANIFEST_SKIP_MAYBE 288 if (fip->fi_skip_len == 0 && 289 fp > fip->fi_data && fp[-1] == '/') { 290 fp = maybe_skip(fp, fip, &nplen); 291 } 292 #endif 293 /* 294 * when we find a match: 295 * fp[nplen] will be space and 296 * fp will be fip->fi_data or 297 * fp[-1] will be \n 298 */ 299 if (!((fp == fip->fi_data || fp[-1] == '\n') && 300 fp[nplen] == ' ')) { 301 do { 302 fp++; 303 fp = strstr(fp, np); 304 if (fp) { 305 #ifdef MANIFEST_SKIP_MAYBE 306 if (fip->fi_skip_len == 0 && 307 fp > fip->fi_data && 308 fp[-1] == '/') { 309 fp = maybe_skip(fp, fip, &nplen); 310 } 311 #endif 312 DEBUG_PRINTF(3, 313 ("fp[-1]=%#x fp[%zu]=%#x fp=%.78s\n", 314 fp[-1], nplen, 315 fp[nplen], 316 fp)); 317 } 318 } while (fp != NULL && 319 !(fp[-1] == '\n' && 320 fp[nplen] == ' ')); 321 if (!fp) 322 continue; 323 } 324 DEBUG_PRINTF(2, ("found %.78s\n", fp)); 325 /* we have a match! */ 326 for (cp = &fp[nplen]; *cp == ' '; cp++) 327 ; /* nothing */ 328 return (cp); 329 } else { 330 DEBUG_PRINTF(3, 331 ("Ignoring prefix=%s\n", fip->fi_prefix)); 332 } 333 } 334 } while (cp > &pbuf[1]); 335 336 return (NULL); 337 } 338 339 static int 340 verify_fingerprint(int fd, const char *path, const char *cp, off_t off) 341 { 342 unsigned char buf[PAGE_SIZE]; 343 const br_hash_class *md; 344 br_hash_compat_context mctx; 345 size_t hlen; 346 int n; 347 348 if (strncmp(cp, "no_hash", 7) == 0) { 349 return (VE_FINGERPRINT_IGNORE); 350 } else if (strncmp(cp, "sha256=", 7) == 0) { 351 md = &br_sha256_vtable; 352 hlen = br_sha256_SIZE; 353 cp += 7; 354 #ifdef VE_SHA1_SUPPORT 355 } else if (strncmp(cp, "sha1=", 5) == 0) { 356 md = &br_sha1_vtable; 357 hlen = br_sha1_SIZE; 358 cp += 5; 359 #endif 360 #ifdef VE_SHA384_SUPPORT 361 } else if (strncmp(cp, "sha384=", 7) == 0) { 362 md = &br_sha384_vtable; 363 hlen = br_sha384_SIZE; 364 cp += 7; 365 #endif 366 #ifdef VE_SHA512_SUPPORT 367 } else if (strncmp(cp, "sha512=", 7) == 0) { 368 md = &br_sha512_vtable; 369 hlen = br_sha512_SIZE; 370 cp += 7; 371 #endif 372 } else { 373 ve_error_set("%s: no supported fingerprint", path); 374 return (VE_FINGERPRINT_UNKNOWN); 375 } 376 377 md->init(&mctx.vtable); 378 if (off) 379 lseek(fd, 0, SEEK_SET); 380 do { 381 n = read(fd, buf, sizeof(buf)); 382 if (n < 0) 383 return (n); 384 if (n > 0) 385 md->update(&mctx.vtable, buf, n); 386 } while (n > 0); 387 lseek(fd, off, SEEK_SET); 388 return (ve_check_hash(&mctx, md, path, cp, hlen)); 389 } 390 391 392 /** 393 * @brief 394 * verify an open file 395 * 396 * @param[in] fd 397 * open descriptor 398 * 399 * @param[in] path 400 * pathname to open 401 * 402 * @param[in] off 403 * current offset 404 * 405 * @return 0, VE_FINGERPRINT_OK or VE_FINGERPRINT_NONE, VE_FINGERPRINT_WRONG 406 */ 407 int 408 verify_fd(int fd, const char *path, off_t off, struct stat *stp) 409 { 410 struct stat st; 411 char *cp; 412 int rc; 413 414 if (!stp) { 415 if (fstat(fd, &st) == 0) 416 stp = &st; 417 } 418 if (stp && !S_ISREG(stp->st_mode)) 419 return (0); /* not relevant */ 420 cp = fingerprint_info_lookup(fd, path); 421 if (!cp) { 422 ve_error_set("%s: no entry", path); 423 return (VE_FINGERPRINT_NONE); 424 } 425 rc = verify_fingerprint(fd, path, cp, off); 426 switch (rc) { 427 case VE_FINGERPRINT_OK: 428 case VE_FINGERPRINT_IGNORE: 429 case VE_FINGERPRINT_UNKNOWN: 430 return (rc); 431 default: 432 return (VE_FINGERPRINT_WRONG); 433 } 434 } 435 436 /** 437 * @brief 438 * open a file if it can be verified 439 * 440 * @param[in] path 441 * pathname to open 442 * 443 * @param[in] flags 444 * flags for open 445 * 446 * @return fd or VE_FINGERPRINT_NONE, VE_FINGERPRINT_WRONG 447 */ 448 int 449 verify_open(const char *path, int flags) 450 { 451 int fd; 452 int rc; 453 454 if ((fd = open(path, flags)) >= 0) { 455 if ((rc = verify_fd(fd, path, 0, NULL)) < 0) { 456 close(fd); 457 fd = rc; 458 } 459 } 460 return (fd); 461 } 462