138032Speter/*- 2261363Sgshapiro * Copyright (c) 2017-2018, Juniper Networks, Inc. 364562Sgshapiro * 438032Speter * Redistribution and use in source and binary forms, with or without 538032Speter * modification, are permitted provided that the following conditions 638032Speter * are met: 738032Speter * 1. Redistributions of source code must retain the above copyright 838032Speter * notice, this list of conditions and the following disclaimer. 938032Speter * 2. Redistributions in binary form must reproduce the above copyright 1038032Speter * notice, this list of conditions and the following disclaimer in the 1138032Speter * documentation and/or other materials provided with the distribution. 1238032Speter * 1338032Speter * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 1464562Sgshapiro * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 1538032Speter * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 1638032Speter * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 1764562Sgshapiro * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 1890792Sgshapiro * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 1964562Sgshapiro * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 20132943Sgshapiro * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 21132943Sgshapiro * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 22132943Sgshapiro * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23132943Sgshapiro * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 2464562Sgshapiro */ 2538032Speter#include <sys/cdefs.h> 2664562Sgshapiro#include <sys/queue.h> 2738032Speter 2864562Sgshapiro#include "libsecureboot-priv.h" 2938032Speter 3064562Sgshapiro 3164562Sgshapirostruct fingerprint_info { 3264562Sgshapiro char *fi_prefix; /**< manifest entries relative to */ 3364562Sgshapiro char *fi_skip; /**< manifest entries prefixed with */ 3464562Sgshapiro const char *fi_data; /**< manifest data */ 3590792Sgshapiro size_t fi_prefix_len; /**< length of prefix */ 3664562Sgshapiro size_t fi_skip_len; /**< length of skip */ 3764562Sgshapiro dev_t fi_dev; /**< device id */ 3864562Sgshapiro LIST_ENTRY(fingerprint_info) entries; 3964562Sgshapiro}; 4038032Speter 4138032Speterstatic LIST_HEAD(, fingerprint_info) fi_list; 4264562Sgshapiro 4338032Speterstatic void 4464562Sgshapirofingerprint_info_init(void) 4590792Sgshapiro{ 4690792Sgshapiro static int once; 4790792Sgshapiro 4890792Sgshapiro if (once) 4990792Sgshapiro return; 5090792Sgshapiro LIST_INIT(&fi_list); 5190792Sgshapiro once = 1; 5290792Sgshapiro} 5390792Sgshapiro 5490792Sgshapiro/** 55266692Sgshapiro * @brief 5690792Sgshapiro * add manifest data to list 5790792Sgshapiro * 5890792Sgshapiro * list is kept sorted by longest prefix. 5964562Sgshapiro * 6064562Sgshapiro * @param[in] prefix 6190792Sgshapiro * path that all manifest entries are resolved via 6290792Sgshapiro * 6390792Sgshapiro * @param[in] skip 6490792Sgshapiro * optional prefix within manifest entries which should be skipped 6590792Sgshapiro * 6690792Sgshapiro * @param[in] data 6790792Sgshapiro * manifest data 6890792Sgshapiro */ 6990792Sgshapirovoid 7090792Sgshapirofingerprint_info_add(const char *filename, const char *prefix, 7190792Sgshapiro const char *skip, const char *data, struct stat *stp) 7290792Sgshapiro{ 73168515Sgshapiro struct fingerprint_info *fip, *nfip, *lfip; 7438032Speter char *cp; 7564562Sgshapiro int n; 7638032Speter 7764562Sgshapiro fingerprint_info_init(); 7838032Speter nfip = malloc(sizeof(struct fingerprint_info)); 7964562Sgshapiro if (nfip == NULL) { 8064562Sgshapiro#ifdef _STANDALONE 8164562Sgshapiro printf("%s: out of memory! %lu\n", __func__, 8264562Sgshapiro (unsigned long)sizeof(struct fingerprint_info)); 8364562Sgshapiro#endif 8438032Speter return; 8564562Sgshapiro } 8664562Sgshapiro if (prefix) { 8764562Sgshapiro nfip->fi_prefix = strdup(prefix); 8864562Sgshapiro } else { 8964562Sgshapiro if (!filename) { 9064562Sgshapiro free(nfip); 9164562Sgshapiro return; 9264562Sgshapiro } 9364562Sgshapiro nfip->fi_prefix = strdup(filename); 9464562Sgshapiro cp = strrchr(nfip->fi_prefix, '/'); 9564562Sgshapiro if (cp == nfip->fi_prefix) { 9638032Speter cp[1] = '\0'; 9764562Sgshapiro } else if (cp) { 9864562Sgshapiro *cp = '\0'; 9938032Speter } else { 10064562Sgshapiro free(nfip->fi_prefix); 10164562Sgshapiro free(nfip); 10238032Speter return; 10364562Sgshapiro } 10464562Sgshapiro } 10538032Speter /* collapse any trailing ..[/] */ 10664562Sgshapiro n = 0; 10764562Sgshapiro while ((cp = strrchr(nfip->fi_prefix, '/')) > nfip->fi_prefix) { 10864562Sgshapiro if (cp[1] == '\0') { /* trailing "/" */ 10964562Sgshapiro *cp = '\0'; 11064562Sgshapiro continue; 11164562Sgshapiro } 11290792Sgshapiro if (strcmp(&cp[1], "..") == 0) { 11390792Sgshapiro n++; 11490792Sgshapiro *cp = '\0'; 11564562Sgshapiro continue; 11638032Speter } 11790792Sgshapiro if (n > 0) { 11864562Sgshapiro n--; 11964562Sgshapiro *cp = '\0'; 12064562Sgshapiro } 12164562Sgshapiro if (n == 0) 12264562Sgshapiro break; 12338032Speter } 12464562Sgshapiro nfip->fi_dev = stp->st_dev; 12564562Sgshapiro#ifdef UNIT_TEST 12690792Sgshapiro nfip->fi_dev = 0; 127249729Sgshapiro#endif 128249729Sgshapiro nfip->fi_data = data; 129249729Sgshapiro nfip->fi_prefix_len = strlen(nfip->fi_prefix); 130249729Sgshapiro if (skip) { 131249729Sgshapiro nfip->fi_skip_len = strlen(skip); 13290792Sgshapiro if (nfip->fi_skip_len) 13364562Sgshapiro nfip->fi_skip = strdup(skip); 13464562Sgshapiro else 13564562Sgshapiro nfip->fi_skip = NULL; 13698121Sgshapiro } else { 13798121Sgshapiro nfip->fi_skip = NULL; 13898121Sgshapiro nfip->fi_skip_len = 0; 13998121Sgshapiro } 140225906Sume 14198121Sgshapiro if (LIST_EMPTY(&fi_list)) { 142225906Sume LIST_INSERT_HEAD(&fi_list, nfip, entries); 143225906Sume DEBUG_PRINTF(4, ("inserted %zu %s at head\n", 144261363Sgshapiro nfip->fi_prefix_len, nfip->fi_prefix)); 14598121Sgshapiro return; 14698121Sgshapiro } 14798121Sgshapiro LIST_FOREACH(fip, &fi_list, entries) { 148225906Sume if (nfip->fi_prefix_len >= fip->fi_prefix_len) { 14998121Sgshapiro LIST_INSERT_BEFORE(fip, nfip, entries); 15064562Sgshapiro DEBUG_PRINTF(4, ("inserted %zu %s before %zu %s\n", 15164562Sgshapiro nfip->fi_prefix_len, nfip->fi_prefix, 15298121Sgshapiro fip->fi_prefix_len, fip->fi_prefix)); 15364562Sgshapiro return; 15464562Sgshapiro } 15598121Sgshapiro lfip = fip; 15664562Sgshapiro } 15764562Sgshapiro LIST_INSERT_AFTER(lfip, nfip, entries); 15864562Sgshapiro DEBUG_PRINTF(4, ("inserted %zu %s after %zu %s\n", 15964562Sgshapiro nfip->fi_prefix_len, nfip->fi_prefix, 16098121Sgshapiro lfip->fi_prefix_len, lfip->fi_prefix)); 16164562Sgshapiro} 16264562Sgshapiro 16364562Sgshapiro#ifdef MANIFEST_SKIP_MAYBE 16464562Sgshapiro/* 16564562Sgshapiro * Deal with old incompatible boot/manifest 16664562Sgshapiro * if fp[-1] is '/' and start of entry matches 16764562Sgshapiro * MANIFEST_SKIP_MAYBE, we want it. 16864562Sgshapiro */ 16938032Speterstatic char * 17038032Spetermaybe_skip(char *fp, struct fingerprint_info *fip, size_t *nplenp) 17138032Speter{ 17238032Speter char *tp; 17338032Speter 17438032Speter tp = fp - sizeof(MANIFEST_SKIP_MAYBE); 17538032Speter 17638032Speter if (tp >= fip->fi_data) { 17738032Speter DEBUG_PRINTF(3, ("maybe: %.48s\n", tp)); 17838032Speter if ((tp == fip->fi_data || tp[-1] == '\n') && 17938032Speter strncmp(tp, MANIFEST_SKIP_MAYBE, 18064562Sgshapiro sizeof(MANIFEST_SKIP_MAYBE) - 1) == 0) { 18164562Sgshapiro fp = tp; 18264562Sgshapiro *nplenp += sizeof(MANIFEST_SKIP_MAYBE); 18364562Sgshapiro } 18438032Speter } 18538032Speter return (fp); 18664562Sgshapiro} 18738032Speter#endif 18838032Speter 18964562Sgshapirochar * 19073188Sgshapirofingerprint_info_lookup(int fd, const char *path) 19173188Sgshapiro{ 19273188Sgshapiro char pbuf[MAXPATHLEN+1]; 19338032Speter char nbuf[MAXPATHLEN+1]; 19464562Sgshapiro struct stat st; 19564562Sgshapiro struct fingerprint_info *fip; 19664562Sgshapiro char *cp, *ep, *fp, *np; 19738032Speter const char *prefix; 19864562Sgshapiro size_t n, plen, nlen, nplen; 19964562Sgshapiro dev_t dev = 0; 20064562Sgshapiro 20138032Speter fingerprint_info_init(); 20264562Sgshapiro 203120256Sgshapiro n = strlcpy(pbuf, path, sizeof(pbuf)); 204120256Sgshapiro if (n >= sizeof(pbuf)) 205120256Sgshapiro return (NULL); 206120256Sgshapiro if (fstat(fd, &st) == 0) 20790792Sgshapiro dev = st.st_dev; 20890792Sgshapiro#ifdef UNIT_TEST 20990792Sgshapiro dev = 0; 21090792Sgshapiro#endif 21190792Sgshapiro /* 21290792Sgshapiro * get the first entry - it will have longest prefix 21390792Sgshapiro * so we can can work out how to initially split path 21490792Sgshapiro */ 21590792Sgshapiro fip = LIST_FIRST(&fi_list); 21690792Sgshapiro if (!fip) 21790792Sgshapiro return (NULL); 21890792Sgshapiro prefix = pbuf; 21990792Sgshapiro ep = NULL; 22090792Sgshapiro cp = &pbuf[fip->fi_prefix_len]; 22190792Sgshapiro do { 22290792Sgshapiro if (ep) { 22338032Speter *ep = '/'; 22438032Speter cp -= 2; 22538032Speter if (cp < pbuf) 22690792Sgshapiro break; 22738032Speter } 22890792Sgshapiro nlen = plen = 0; /* keep gcc quiet */ 22938032Speter if (cp > pbuf) { 23038032Speter for ( ; cp >= pbuf && *cp != '/'; cp--) 23138032Speter ; /* nothing */ 23238032Speter if (cp > pbuf) { 23338032Speter ep = cp++; 23438032Speter *ep = '\0'; 23538032Speter } else { 23638032Speter cp = pbuf; 23738032Speter } 23838032Speter if (ep) { 23938032Speter plen = ep - pbuf; 24090792Sgshapiro nlen = n - plen - 1; 24138032Speter } 24238032Speter } 24338032Speter if (cp == pbuf) { 24438032Speter prefix = "/"; 24538032Speter plen = 1; 24638032Speter if (*cp == '/') { 24738032Speter nlen = n - 1; 24838032Speter cp++; 24990792Sgshapiro } else 25090792Sgshapiro nlen = n; 25190792Sgshapiro ep = NULL; 25290792Sgshapiro } 25338032Speter 25438032Speter DEBUG_PRINTF(2, ("looking for %s %zu %s\n", prefix, plen, cp)); 25538032Speter 25638032Speter LIST_FOREACH(fip, &fi_list, entries) { 25738032Speter DEBUG_PRINTF(4, ("at %zu %s\n", 25864562Sgshapiro fip->fi_prefix_len, fip->fi_prefix)); 25990792Sgshapiro 26090792Sgshapiro if (fip->fi_prefix_len < plen) { 26190792Sgshapiro DEBUG_PRINTF(3, ("skipping prefix=%s %zu %zu\n", 26290792Sgshapiro fip->fi_prefix, fip->fi_prefix_len, 26338032Speter plen)); 26438032Speter break; 26538032Speter } 26638032Speter if (fip->fi_prefix_len == plen) { 26764562Sgshapiro if (fip->fi_dev != 0 && fip->fi_dev != dev) { 26864562Sgshapiro DEBUG_PRINTF(3, ( 26964562Sgshapiro "skipping dev=%ld != %ld\n", 27064562Sgshapiro (long)fip->fi_dev, 27164562Sgshapiro (long)dev)); 27264562Sgshapiro continue; 27364562Sgshapiro } 27464562Sgshapiro if (strcmp(prefix, fip->fi_prefix)) { 27564562Sgshapiro DEBUG_PRINTF(3, ( 27664562Sgshapiro "skipping prefix=%s\n", 27790792Sgshapiro fip->fi_prefix)); 27864562Sgshapiro continue; 27964562Sgshapiro } 28064562Sgshapiro DEBUG_PRINTF(3, ("checking prefix=%s\n", 28164562Sgshapiro fip->fi_prefix)); 28264562Sgshapiro if (fip->fi_skip_len) { 28390792Sgshapiro np = nbuf; 28490792Sgshapiro nplen = snprintf(nbuf, sizeof(nbuf), 28590792Sgshapiro "%s/%s", 28664562Sgshapiro fip->fi_skip, cp); 28764562Sgshapiro nplen = MIN(nplen, sizeof(nbuf) - 1); 28838032Speter } else { 28964562Sgshapiro np = cp; 29038032Speter nplen = nlen; 29164562Sgshapiro } 29264562Sgshapiro DEBUG_PRINTF(3, ("lookup: '%s'\n", np)); 29364562Sgshapiro if (!(fp = strstr(fip->fi_data, np))) 29464562Sgshapiro continue; 29564562Sgshapiro#ifdef MANIFEST_SKIP_MAYBE 29690792Sgshapiro if (fip->fi_skip_len == 0 && 29790792Sgshapiro fp > fip->fi_data && fp[-1] == '/') { 29838032Speter fp = maybe_skip(fp, fip, &nplen); 29990792Sgshapiro } 30090792Sgshapiro#endif 30190792Sgshapiro /* 30290792Sgshapiro * when we find a match: 30390792Sgshapiro * fp[nplen] will be space and 30490792Sgshapiro * fp will be fip->fi_data or 30590792Sgshapiro * fp[-1] will be \n 30690792Sgshapiro */ 30790792Sgshapiro if (!((fp == fip->fi_data || fp[-1] == '\n') && 30890792Sgshapiro fp[nplen] == ' ')) { 30990792Sgshapiro do { 31090792Sgshapiro fp++; 31190792Sgshapiro fp = strstr(fp, np); 31290792Sgshapiro if (fp) { 31390792Sgshapiro#ifdef MANIFEST_SKIP_MAYBE 314102528Sgshapiro if (fip->fi_skip_len == 0 && 31590792Sgshapiro fp > fip->fi_data && 31664562Sgshapiro fp[-1] == '/') { 31764562Sgshapiro fp = maybe_skip(fp, fip, &nplen); 31864562Sgshapiro } 31964562Sgshapiro#endif 32064562Sgshapiro DEBUG_PRINTF(3, 32190792Sgshapiro ("fp[-1]=%#x fp[%zu]=%#x fp=%.78s\n", 32264562Sgshapiro fp[-1], nplen, 32364562Sgshapiro fp[nplen], 32464562Sgshapiro fp)); 32564562Sgshapiro } 32664562Sgshapiro } while (fp != NULL && 32790792Sgshapiro !(fp[-1] == '\n' && 32864562Sgshapiro fp[nplen] == ' ')); 32990792Sgshapiro if (!fp) 33090792Sgshapiro continue; 33164562Sgshapiro } 33290792Sgshapiro DEBUG_PRINTF(2, ("found %.78s\n", fp)); 33390792Sgshapiro /* we have a match! */ 33464562Sgshapiro for (cp = &fp[nplen]; *cp == ' '; cp++) 33590792Sgshapiro ; /* nothing */ 336173340Sgshapiro return (cp); 337173340Sgshapiro } else { 33890792Sgshapiro DEBUG_PRINTF(3, 339223067Sgshapiro ("Ignoring prefix=%s\n", fip->fi_prefix)); 34064562Sgshapiro } 34164562Sgshapiro } 34264562Sgshapiro } while (cp > &pbuf[1]); 34364562Sgshapiro 34464562Sgshapiro return (NULL); 34538032Speter} 346168515Sgshapiro 347111823Sgshapirostatic int 34864562Sgshapiroverify_fingerprint(int fd, const char *path, const char *cp, off_t off) 34964562Sgshapiro{ 35064562Sgshapiro unsigned char buf[PAGE_SIZE]; 35190792Sgshapiro const br_hash_class *md; 35290792Sgshapiro br_hash_compat_context mctx; 35390792Sgshapiro size_t hlen; 354132943Sgshapiro int n; 355132943Sgshapiro 35638032Speter if (strncmp(cp, "no_hash", 7) == 0) { 35764562Sgshapiro return (VE_FINGERPRINT_IGNORE); 35890792Sgshapiro } else if (strncmp(cp, "sha256=", 7) == 0) { 35938032Speter md = &br_sha256_vtable; 36038032Speter hlen = br_sha256_SIZE; 36190792Sgshapiro cp += 7; 36264562Sgshapiro#ifdef VE_SHA1_SUPPORT 36390792Sgshapiro } else if (strncmp(cp, "sha1=", 5) == 0) { 36464562Sgshapiro md = &br_sha1_vtable; 365168515Sgshapiro hlen = br_sha1_SIZE; 366168515Sgshapiro cp += 5; 367168515Sgshapiro#endif 368168515Sgshapiro#ifdef VE_SHA384_SUPPORT 369168515Sgshapiro } else if (strncmp(cp, "sha384=", 7) == 0) { 370168515Sgshapiro md = &br_sha384_vtable; 37164562Sgshapiro hlen = br_sha384_SIZE; 37290792Sgshapiro cp += 7; 37390792Sgshapiro#endif 37490792Sgshapiro#ifdef VE_SHA512_SUPPORT 37590792Sgshapiro } else if (strncmp(cp, "sha512=", 7) == 0) { 376168515Sgshapiro md = &br_sha512_vtable; 377168515Sgshapiro hlen = br_sha512_SIZE; 378168515Sgshapiro cp += 7; 379168515Sgshapiro#endif 380168515Sgshapiro } else { 381168515Sgshapiro ve_error_set("%s: no supported fingerprint", path); 382168515Sgshapiro return (VE_FINGERPRINT_UNKNOWN); 383168515Sgshapiro } 38438032Speter 38538032Speter md->init(&mctx.vtable); 38638032Speter if (off) 38738032Speter lseek(fd, 0, SEEK_SET); 38838032Speter do { 38938032Speter n = read(fd, buf, sizeof(buf)); 39038032Speter if (n < 0) 39138032Speter return (n); 39238032Speter if (n > 0) 39338032Speter md->update(&mctx.vtable, buf, n); 39438032Speter } while (n > 0); 39538032Speter lseek(fd, off, SEEK_SET); 39638032Speter return (ve_check_hash(&mctx, md, path, cp, hlen)); 39738032Speter} 39838032Speter 39938032Speter 40038032Speter/** 40138032Speter * @brief 40264562Sgshapiro * verify an open file 40338032Speter * 40438032Speter * @param[in] fd 40538032Speter * open descriptor 40638032Speter * 40738032Speter * @param[in] path 40838032Speter * pathname to open 40938032Speter * 41038032Speter * @param[in] off 41138032Speter * current offset 41238032Speter * 41364562Sgshapiro * @return 0, VE_FINGERPRINT_OK or VE_FINGERPRINT_NONE, VE_FINGERPRINT_WRONG 41438032Speter */ 41564562Sgshapiroint 41638032Speterverify_fd(int fd, const char *path, off_t off, struct stat *stp) 41738032Speter{ 41838032Speter struct stat st; 41964562Sgshapiro char *cp; 42064562Sgshapiro int rc; 42190792Sgshapiro 42238032Speter if (!stp) { 42338032Speter if (fstat(fd, &st) == 0) 42438032Speter stp = &st; 42590792Sgshapiro } 42664562Sgshapiro if (stp && !S_ISREG(stp->st_mode)) 42764562Sgshapiro return (0); /* not relevant */ 428141858Sgshapiro cp = fingerprint_info_lookup(fd, path); 42964562Sgshapiro if (!cp) { 43064562Sgshapiro ve_error_set("%s: no entry", path); 43164562Sgshapiro return (VE_FINGERPRINT_NONE); 43238032Speter } 43364562Sgshapiro rc = verify_fingerprint(fd, path, cp, off); 43464562Sgshapiro switch (rc) { 43564562Sgshapiro case VE_FINGERPRINT_OK: 43638032Speter case VE_FINGERPRINT_IGNORE: 43764562Sgshapiro case VE_FINGERPRINT_UNKNOWN: 43864562Sgshapiro return (rc); 43964562Sgshapiro default: 44064562Sgshapiro return (VE_FINGERPRINT_WRONG); 44164562Sgshapiro } 44264562Sgshapiro} 44364562Sgshapiro 44464562Sgshapiro/** 44564562Sgshapiro * @brief 44664562Sgshapiro * open a file if it can be verified 44764562Sgshapiro * 44838032Speter * @param[in] path 44964562Sgshapiro * pathname to open 45064562Sgshapiro * 45164562Sgshapiro * @param[in] flags 45264562Sgshapiro * flags for open 45338032Speter * 45464562Sgshapiro * @return fd or VE_FINGERPRINT_NONE, VE_FINGERPRINT_WRONG 45564562Sgshapiro */ 45664562Sgshapiroint 45764562Sgshapiroverify_open(const char *path, int flags) 45864562Sgshapiro{ 45964562Sgshapiro int fd; 46064562Sgshapiro int rc; 46164562Sgshapiro 46238032Speter if ((fd = open(path, flags)) >= 0) { 46364562Sgshapiro if ((rc = verify_fd(fd, path, 0, NULL)) < 0) { 464132943Sgshapiro close(fd); 46538032Speter fd = rc; 46664562Sgshapiro } 46764562Sgshapiro } 46890792Sgshapiro return (fd); 46964562Sgshapiro} 47064562Sgshapiro