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