Changeset View
Changeset View
Standalone View
Standalone View
lib/libve/veopen.c
- This file was added.
Property | Old Value | New Value |
---|---|---|
svn:eol-style | null | native \ No newline at end of property |
svn:keywords | null | FreeBSD=%H \ No newline at end of property |
svn:mime-type | null | text/plain \ No newline at end of property |
/*- | |||||
* Copyright (c) 2017-2018, Juniper Networks, Inc. | |||||
* | |||||
* Redistribution and use in source and binary forms, with or without | |||||
* modification, are permitted provided that the following conditions | |||||
* are met: | |||||
* 1. Redistributions of source code must retain the above copyright | |||||
* notice, this list of conditions and the following disclaimer. | |||||
* 2. Redistributions in binary form must reproduce the above copyright | |||||
* notice, this list of conditions and the following disclaimer in the | |||||
* documentation and/or other materials provided with the distribution. | |||||
* | |||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS | |||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT | |||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR | |||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT | |||||
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, | |||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT | |||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, | |||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY | |||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT | |||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE | |||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. | |||||
*/ | |||||
#include <sys/cdefs.h> | |||||
__FBSDID("$FreeBSD$"); | |||||
#include <sys/queue.h> | |||||
#include "libve-priv.h" | |||||
struct fingerprint_info { | |||||
char *fi_prefix; /**< manifest entries relative to */ | |||||
char *fi_skip; /**< manifest entries prefixed with */ | |||||
const char *fi_data; /**< manifest data */ | |||||
size_t fi_prefix_len; /**< length of prefix */ | |||||
size_t fi_skip_len; /**< length of skip */ | |||||
dev_t fi_dev; /**< device id */ | |||||
LIST_ENTRY(fingerprint_info) entries; | |||||
}; | |||||
static LIST_HEAD(, fingerprint_info) fi_list; | |||||
static void | |||||
fingerprint_info_init(void) | |||||
{ | |||||
static int once; | |||||
if (once) | |||||
return; | |||||
LIST_INIT(&fi_list); | |||||
once = 1; | |||||
} | |||||
/** | |||||
* @brief | |||||
* add manifest data to list | |||||
* | |||||
* list is kept sorted by longest prefix. | |||||
* | |||||
* @param[in] prefix | |||||
* path that all manifest entries are resolved via | |||||
* | |||||
* @param[in] skip | |||||
* optional prefix within manifest entries which should be skipped | |||||
* | |||||
* @param[in] data | |||||
* manifest data | |||||
*/ | |||||
void | |||||
fingerprint_info_add(const char *filename, const char *prefix, | |||||
const char *skip, const char *data, struct stat *stp) | |||||
{ | |||||
struct fingerprint_info *fip, *nfip, *lfip; | |||||
char *cp; | |||||
fingerprint_info_init(); | |||||
nfip = malloc(sizeof(struct fingerprint_info)); | |||||
if (prefix) { | |||||
nfip->fi_prefix = strdup(prefix); | |||||
} else { | |||||
if (!filename) { | |||||
free(nfip); | |||||
return; | |||||
} | |||||
nfip->fi_prefix = strdup(filename); | |||||
cp = strrchr(nfip->fi_prefix, '/'); | |||||
if (cp) | |||||
*cp = '\0'; | |||||
else { | |||||
free(nfip->fi_prefix); | |||||
free(nfip); | |||||
return; | |||||
} | |||||
} | |||||
#ifdef UNIT_TEST | |||||
nfip->fi_dev = 0; | |||||
#else | |||||
nfip->fi_dev = stp->st_dev; | |||||
#endif | |||||
nfip->fi_data = data; | |||||
nfip->fi_prefix_len = strlen(nfip->fi_prefix); | |||||
if (skip) { | |||||
nfip->fi_skip_len = strlen(skip); | |||||
if (nfip->fi_skip_len) | |||||
nfip->fi_skip = strdup(skip); | |||||
else | |||||
nfip->fi_skip = NULL; | |||||
} else { | |||||
nfip->fi_skip = NULL; | |||||
nfip->fi_skip_len = 0; | |||||
} | |||||
if (LIST_EMPTY(&fi_list)) { | |||||
LIST_INSERT_HEAD(&fi_list, nfip, entries); | |||||
DEBUG_PRINTF(4, ("inserted %zu %s at head\n", | |||||
nfip->fi_prefix_len, nfip->fi_prefix)); | |||||
return; | |||||
} | |||||
LIST_FOREACH(fip, &fi_list, entries) { | |||||
if (nfip->fi_prefix_len >= fip->fi_prefix_len) { | |||||
LIST_INSERT_BEFORE(fip, nfip, entries); | |||||
DEBUG_PRINTF(4, ("inserted %zu %s before %zu %s\n", | |||||
nfip->fi_prefix_len, nfip->fi_prefix, | |||||
fip->fi_prefix_len, fip->fi_prefix)); | |||||
return; | |||||
} | |||||
lfip = fip; | |||||
} | |||||
LIST_INSERT_AFTER(lfip, nfip, entries); | |||||
DEBUG_PRINTF(4, ("inserted %zu %s after %zu %s\n", | |||||
nfip->fi_prefix_len, nfip->fi_prefix, | |||||
lfip->fi_prefix_len, lfip->fi_prefix)); | |||||
} | |||||
char * | |||||
fingerprint_info_lookup(int fd, const char *path) | |||||
{ | |||||
char pbuf[MAXPATHLEN+1]; | |||||
char nbuf[MAXPATHLEN+1]; | |||||
struct stat st; | |||||
struct fingerprint_info *fip; | |||||
char *cp, *ep, *fp, *np; | |||||
const char *prefix; | |||||
size_t n, plen, nlen, nplen; | |||||
dev_t dev = 0; | |||||
fingerprint_info_init(); | |||||
n = strlcpy(pbuf, path, sizeof(pbuf)); | |||||
if (n >= sizeof(pbuf)) | |||||
return (NULL); | |||||
#ifndef UNIT_TEST | |||||
if (fstat(fd, &st) == 0) | |||||
dev = st.st_dev; | |||||
#endif | |||||
/* | |||||
* get the first entry - it will have longest prefix | |||||
* so we can can work out how to initially split path | |||||
*/ | |||||
fip = LIST_FIRST(&fi_list); | |||||
if (!fip) | |||||
return (NULL); | |||||
prefix = pbuf; | |||||
ep = NULL; | |||||
cp = &pbuf[fip->fi_prefix_len]; | |||||
do { | |||||
if (ep) { | |||||
*ep = '/'; | |||||
cp -= 2; | |||||
if (cp < pbuf) | |||||
break; | |||||
} | |||||
nlen = plen = 0; /* keep gcc quiet */ | |||||
if (cp > pbuf) { | |||||
for ( ; cp >= pbuf && *cp != '/'; cp--) | |||||
; /* nothing */ | |||||
if (cp > pbuf) { | |||||
ep = cp++; | |||||
*ep = '\0'; | |||||
} else { | |||||
cp = pbuf; | |||||
} | |||||
if (ep) { | |||||
plen = ep - pbuf; | |||||
nlen = n - plen - 1; | |||||
} | |||||
} | |||||
if (cp == pbuf) { | |||||
prefix = "/"; | |||||
plen = 1; | |||||
if (*cp == '/') { | |||||
nlen = n - 1; | |||||
cp++; | |||||
} else | |||||
nlen = n; | |||||
ep = NULL; | |||||
} | |||||
DEBUG_PRINTF(2, ("looking for %s %zu %s\n", prefix, plen, cp)); | |||||
LIST_FOREACH(fip, &fi_list, entries) { | |||||
DEBUG_PRINTF(4, ("at %zu %s\n", | |||||
fip->fi_prefix_len, fip->fi_prefix)); | |||||
if (fip->fi_prefix_len < plen) { | |||||
DEBUG_PRINTF(3, ("skipping prefix=%s %zu %zu\n", | |||||
fip->fi_prefix, fip->fi_prefix_len, | |||||
plen)); | |||||
break; | |||||
} | |||||
if (fip->fi_prefix_len == plen) { | |||||
if (fip->fi_dev != 0 && fip->fi_dev != dev) { | |||||
DEBUG_PRINTF(3, ( | |||||
"skipping dev=%ld != %ld\n", | |||||
(long)fip->fi_dev, | |||||
(long)dev)); | |||||
continue; | |||||
} | |||||
if (strcmp(prefix, fip->fi_prefix)) { | |||||
DEBUG_PRINTF(3, ( | |||||
"skipping prefix=%s\n", | |||||
fip->fi_prefix)); | |||||
continue; | |||||
} | |||||
DEBUG_PRINTF(3, ("checking prefix=%s\n", | |||||
fip->fi_prefix)); | |||||
if (fip->fi_skip_len) { | |||||
np = nbuf; | |||||
nplen = snprintf(nbuf, sizeof(nbuf), | |||||
"%s/%s", | |||||
fip->fi_skip, cp); | |||||
nplen = MIN(nplen, sizeof(nbuf) - 1); | |||||
} else { | |||||
np = cp; | |||||
nplen = nlen; | |||||
} | |||||
DEBUG_PRINTF(3, ("lookup: '%s'\n", np)); | |||||
if (!(fp = strstr(fip->fi_data, np))) | |||||
continue; | |||||
/* | |||||
* when we find a match: | |||||
* fp[nplen] will be space and | |||||
* fp will be fip->fi_data or | |||||
* fp[-1] will be \n | |||||
*/ | |||||
if (!((fp == fip->fi_data || fp[-1] == '\n') && | |||||
fp[nplen] == ' ')) { | |||||
do { | |||||
fp++; | |||||
fp = strstr(fp, np); | |||||
if (fp) { | |||||
DEBUG_PRINTF(3, | |||||
("fp[-1]=%#x fp[%zu]=%#x fp=%.78s\n", | |||||
fp[-1], nplen, | |||||
fp[nplen], | |||||
fp)); | |||||
} | |||||
} while (fp != NULL && | |||||
!(fp[-1] == '\n' && | |||||
fp[nplen] == ' ')); | |||||
if (!fp) | |||||
continue; | |||||
} | |||||
DEBUG_PRINTF(2, ("found %.78s\n", fp)); | |||||
/* we have a match! */ | |||||
for (cp = &fp[nplen]; *cp == ' '; cp++) | |||||
; /* nothing */ | |||||
return (cp); | |||||
} else { | |||||
DEBUG_PRINTF(3, | |||||
("Ignoring prefix=%s\n", fip->fi_prefix)); | |||||
} | |||||
} | |||||
} while (cp > &pbuf[1]); | |||||
return (NULL); | |||||
} | |||||
static int | |||||
verify_fingerprint(int fd, const char *path, const char *cp, off_t off) | |||||
{ | |||||
unsigned char buf[PAGE_SIZE]; | |||||
const br_hash_class *md; | |||||
br_hash_compat_context mctx; | |||||
size_t hlen; | |||||
int n; | |||||
if (strncmp(cp, "sha256=", 7) == 0) { | |||||
md = &br_sha256_vtable; | |||||
hlen = br_sha256_SIZE; | |||||
cp += 7; | |||||
#ifdef VE_SHA1_SUPPORT | |||||
} else if (strncmp(cp, "sha1=", 5) == 0) { | |||||
md = &br_sha1_vtable; | |||||
hlen = br_sha1_SIZE; | |||||
cp += 5; | |||||
#endif | |||||
#ifdef VE_SHA384_SUPPORT | |||||
} else if (strncmp(cp, "sha384=", 7) == 0) { | |||||
md = &br_sha384_vtable; | |||||
hlen = br_sha384_SIZE; | |||||
cp += 7; | |||||
#endif | |||||
#ifdef VE_SHA512_SUPPORT | |||||
} else if (strncmp(cp, "sha512=", 7) == 0) { | |||||
md = &br_sha512_vtable; | |||||
hlen = br_sha512_SIZE; | |||||
cp += 7; | |||||
#endif | |||||
} else { | |||||
ve_error_set("%s: no supported fingerprint", path); | |||||
return (VE_FINGERPRINT_UNKNOWN); | |||||
} | |||||
md->init(&mctx.vtable); | |||||
if (off) | |||||
lseek(fd, 0, SEEK_SET); | |||||
do { | |||||
n = read(fd, buf, sizeof(buf)); | |||||
if (n < 0) | |||||
return (n); | |||||
if (n > 0) | |||||
md->update(&mctx.vtable, buf, n); | |||||
} while (n > 0); | |||||
lseek(fd, off, SEEK_SET); | |||||
return (ve_check_hash(&mctx, md, path, cp, hlen)); | |||||
} | |||||
/** | |||||
* @brief | |||||
* verify an open file | |||||
* | |||||
* @param[in] fd | |||||
* open descriptor | |||||
* | |||||
* @param[in] path | |||||
* pathname to open | |||||
* | |||||
* @param[in] off | |||||
* current offset | |||||
* | |||||
* @return 0, VE_FINGERPRINT_OK or VE_FINGERPRINT_NONE, VE_FINGERPRINT_WRONG | |||||
*/ | |||||
int | |||||
verify_fd(int fd, const char *path, off_t off, struct stat *stp) | |||||
{ | |||||
struct stat st; | |||||
char *cp; | |||||
int rc; | |||||
if (!stp) { | |||||
if (fstat(fd, &st) == 0) | |||||
stp = &st; | |||||
} | |||||
if (stp && !S_ISREG(stp->st_mode)) | |||||
return (0); /* not relevant */ | |||||
cp = fingerprint_info_lookup(fd, path); | |||||
if (!cp) { | |||||
ve_error_set("%s: no entry", path); | |||||
return (VE_FINGERPRINT_NONE); | |||||
} | |||||
rc = verify_fingerprint(fd, path, cp, off); | |||||
switch (rc) { | |||||
case VE_FINGERPRINT_OK: | |||||
case VE_FINGERPRINT_UNKNOWN: | |||||
return (rc); | |||||
default: | |||||
return (VE_FINGERPRINT_WRONG); | |||||
} | |||||
} | |||||
/** | |||||
* @brief | |||||
* open a file if it can be verified | |||||
* | |||||
* @param[in] path | |||||
* pathname to open | |||||
* | |||||
* @param[in] flags | |||||
* flags for open | |||||
* | |||||
* @return fd or VE_FINGERPRINT_NONE, VE_FINGERPRINT_WRONG | |||||
*/ | |||||
int | |||||
verify_open(const char *path, int flags) | |||||
{ | |||||
int fd; | |||||
int rc; | |||||
if ((fd = open(path, flags)) >= 0) { | |||||
if ((rc = verify_fd(fd, path, 0, NULL)) < 0) { | |||||
close(fd); | |||||
fd = rc; | |||||
} | |||||
} | |||||
return (fd); | |||||
} |