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