Add libsecureboot

Used by loader and veriexec
Depends on libbearssl

Reviewed by:	emaste
Sponsored by:	Juniper Networks
Differential Revision:	D16335
This commit is contained in:
sjg 2019-02-26 06:09:10 +00:00
parent 1769b514d0
commit 114933b262
29 changed files with 4922 additions and 0 deletions

View File

@ -0,0 +1,11 @@
# $FreeBSD$
.include <src.opts.mk>
LIB= secureboot
.include "Makefile.inc"
INCS= h/libsecureboot.h
.include <bsd.lib.mk>

View File

@ -0,0 +1,17 @@
# $FreeBSD$
# Autogenerated - do NOT edit!
DIRDEPS = \
gnu/lib/csu \
include \
include/xlocale \
lib/${CSU_DIR} \
lib/libc \
lib/libcompiler_rt \
.include <dirdeps.mk>
.if ${DEP_RELDIR} == ${_DEP_RELDIR}
# local dependencies - needed for -jN in clean tree
.endif

View File

@ -0,0 +1,12 @@
# $FreeBSD$
# Autogenerated - do NOT edit!
DIRDEPS = \
lib/libstand \
.include <dirdeps.mk>
.if ${DEP_RELDIR} == ${_DEP_RELDIR}
# local dependencies - needed for -jN in clean tree
.endif

View File

@ -0,0 +1,133 @@
# $FreeBSD$
.if empty(BEARSSL)
.include "../libbearssl/Makefile.inc"
.endif
.if !target(_${__this}_)
_${__this}_:
libsecureboot_src:= ${.PARSEDIR}
CFLAGS+= -I${libsecureboot_src}/h
.PATH: ${.PARSEDIR}
SRCS+= \
readfile.c \
brf.c \
vesigned.c \
vets.c
.if ${.CURDIR:M*libsecureboot*} != ""
SRCS+= veta.c
.endif
CFLAGS+= ${XCFLAGS.${.TARGET:T:R}:U}
# we use a couple of files from ${BEARSSL}/tools
BRSSL_CFLAGS+= -I${BEARSSL}/tools
BRSSL_SRCS+= \
${BEARSSL}/tools/xmem.c \
${BEARSSL}/tools/vector.c
# we do not need/want nested objdirs
OBJS_SRCS_FILTER = T R
SRCS+= ${BRSSL_SRCS}
# extract the last cert from a chain (should be rootCA)
_LAST_PEM_USE: .USE
sed "1,`grep -n .-END ${.ALLSRC:M*.pem} | tail -2 | head -1 | sed 's,:.*,,'`d" ${.ALLSRC:M*.pem} > ${.TARGET}
# extract 2nd last cert from chain - we use this for self-test
_2ndLAST_PEM_USE: .USE
sed -n "`grep -n .-BEGIN ${.ALLSRC:M*.pem} | tail -2 | \
sed 's,:.*,,' | xargs | (read a b; echo $$a,$$(($$b - 1)))`p" ${.ALLSRC:M*.pem} > ${.TARGET}
# list of hashes we support
VE_HASH_LIST?= SHA256
# list of signatures we support
# some people don't trust ECDSA
VE_SIGNATURE_LIST?= RSA
# this list controls our search for signatures so will not be sorted
# note: for X509 signatures we assume we can replace the trailing
# "sig" with "certs" to find the certificate chain
# eg. for manifest.esig we use manifest.ecerts
VE_SIGNATURE_EXT_LIST?= sig
# needs to be yes for FIPS 140-2 compliance
VE_SELF_TESTS?= no
# rules to populate the [tv]*.pem files we use to generate ta.h
# and can add/alter VE_*_LIST as desired.
.-include "local.trust.mk"
# this is what we use as our trust anchor
CFLAGS+= -I. -DTRUST_ANCHOR_STR=ta_PEM
.if ${VE_SELF_TESTS} != "no"
XCFLAGS.vets+= -DVERIFY_CERTS_STR=vc_PEM
.endif
# clean these up
VE_HASH_LIST:= ${VE_HASH_LIST:tu:O:u}
VE_SIGNATURE_LIST:= ${VE_SIGNATURE_LIST:tu:O:u}
# define what we are supporting
CFLAGS+= ${VE_HASH_LIST:@H@-DVE_$H_SUPPORT@} \
${VE_SIGNATURE_LIST:@S@-DVE_$S_SUPPORT@}
.if ${VE_SIGNATURE_LIST:MOPENPGP} != ""
.include "openpgp/Makefile.inc"
.endif
.if ${VE_SELF_TESTS} != "no"
# The input used for hash KATs
VE_HASH_KAT_STR?= vc_PEM
XCFLAGS.vets+= -DVE_HASH_KAT_STR=${VE_HASH_KAT_STR}
.endif
# Generate ta.h containing one or more PEM encoded trust anchors in ta_PEM.
#
# If we are doing self-tests, we define another arrary vc_PEM
# containing certificates that we can verify for each trust anchor.
# This is typically a subordinate CA cert.
# Finally we generate a hash of vc_PEM using each supported hash method
# to use as a Known Answer Test (needed for FIPS 140-2)
#
vets.o vets.po vets.pico: ta.h
ta.h: ${.ALLTARGETS:M[tv]*pem:O:u}
@( echo '/* Autogenerated - DO NOT EDIT!!! */'; echo; \
cat ${.ALLSRC:N*crl*:Mt*.pem} /dev/null | \
file2c -sx 'static const char ta_PEM[] = {' '};'; \
echo "${.newline}${VE_HASH_LIST:@H@static char vh_$H[] = \"`cat ${.ALLSRC:N*crl*:Mv*.pem} | ${$H:U${H:tl}}`\";${.newline}@}"; ) > ${.TARGET}
.if ${VE_SELF_TESTS} != "no"
( cat ${.ALLSRC:N*crl*:Mv*.pem} /dev/null | \
file2c -sx 'static const char vc_PEM[] = {' '};'; echo ) >> ${.TARGET}
.endif
.if !empty(BUILD_UTC_FILE)
echo '#define BUILD_UTC ${${STAT:Ustat} -f %m ${BUILD_UTC_FILE}:L:sh}' >> ${.TARGET} ${.OODATE:MNOMETA_CMP}
.endif
# This header records our preference for signature extensions.
vesigned.o vesigned.po vesigned.pico: vse.h
vse.h:
@( echo '/* Autogenerated - DO NOT EDIT!!! */'; echo; \
echo "static const char *signature_exts[] = {"; \
echo '${VE_SIGNATURE_EXT_LIST:@e@"$e",${.newline}@}'; \
echo 'NULL };' ) > ${.TARGET}
.for s in ${BRSSL_SRCS} brf.c vets.c veta.c
.ifdef BRSSL_SED
$s: brssl.h
.endif
XCFLAGS.${s:R}+= ${BRSSL_CFLAGS}
.endfor
.endif

View File

@ -0,0 +1,40 @@
# $FreeBSD$
BRSSL_CFLAGS+= -DNO_STDIO
.include "Makefile.inc"
# for "measured boot"
# loader puts the equivalent of TPM's PCR register into kenv
# this is not as good but *way* simpler than talking to TPM
CFLAGS+= -DVE_PCR_SUPPORT
# sources that only apply to libsa
SRCS+= \
vectx.c \
veopen.c \
vepcr.c \
verify_file.c \
# this is the list of paths (relative to a file
# that we need to verify) used to find a signed manifest.
# the signature extensions in VE_SIGNATURE_EXT_LIST
# will be applied to each.
VE_MANIFEST_LIST?= manifest ../manifest
verify_file.o: manifests.h
manifests.h:
@( echo '/* Autogenerated - DO NOT EDIT!!! */'; echo; \
echo "static const char *manifest_names[] = {"; \
echo '${VE_MANIFEST_LIST:@m@"$m",${.newline}@}'; \
echo 'NULL };' ) > ${.TARGET}
XCFLAGS.verify_file+= \
-DVE_DEBUG_LEVEL=${VE_DEBUG_LEVEL:U0} \
-DVE_VERBOSE_DEFAULT=${VE_VERBOSE_DEFAULT:U0} \
.if !empty(MANIFEST_SKIP_ALWAYS)
XCFLAGS.verify_file+= -DMANIFEST_SKIP_ALWAYS=\"${MANIFEST_SKIP_ALWAYS}\"
.elif !empty(MANIFEST_SKIP)
XCFLAGS.verify_file+= -DMANIFEST_SKIP=\"${MANIFEST_SKIP}\"
.endif

View File

@ -0,0 +1,134 @@
libsecureboot
*************
This library depends one way or another on verifying digital signatures.
To do that, the necessary trust anchors need to be available.
The simplest (and most attractive for an embedded system) is to
capture them in this library.
The makefile ``local.trust.mk`` is responsible for doing that.
The file provided is just an example and depends on the environment
here at Juniper.
Within Juniper we use signing servers, which apart from signing things
provide access to the necessary trust anchors.
That signing server is freely available - see
http://www.crufty.net/sjg/docs/signing-server.htm
X.509 certificates chains offer a lot of flexibility over time and are
a great solution for an embedded vendor like Juniper or even
FreeBSD.org, but are probably overkill for personal or small site use.
Setting up a CA for this is rather involved so I'll just provide a
link below to suitable tutorial below.
Using OpenPGP is much simpler.
OpenPGP
========
This is very simple to setup and use.
An RSA key pair can be generated with::
GNUPGHOME=$PWD/.gnupg gpg --openpgp \
--quick-generate-key --batch --passphrase '' "keyname" RSA
The use of ``GNUPGHOME=$PWD/.gnupg`` just avoids messing with personal
keyrings.
We can list the resulting key::
GNUPGHOME=$PWD/.gnupg gpg --openpgp --list-keys
gpg: WARNING: unsafe permissions on homedir
'/h/sjg/openpgp/.gnupg'
gpg: Warning: using insecure memory!
/h/sjg/openpgp/.gnupg/pubring.kbx
---------------------------------
pub rsa2048 2018-03-26 [SC] [expires: 2020-03-25]
AB39B111E40DD019E0E7C171ACA72B4719FD2523
uid [ultimate] OpenPGPtest
The ``keyID`` we want later will be the last 8 octets
(``ACA72B4719FD2523``)
This is what we will use for looking up the key.
We can then export the private and public keys::
GNUPGHOME=$PWD/.gnupg gpg --openpgp \
--export --armor > ACA72B4719FD2523.pub.asc
GNUPGHOME=$PWD/.gnupg gpg --openpgp \
--export-secret-keys --armor > ACA72B4719FD2523.sec.asc
The public key ``ACA72B4719FD2523.pub.asc`` is what we want to
embed in this library.
If you look at the ``ta_asc.h`` target in ``openpgp/Makefile.inc``
we want the trust anchor in a file named ``t*.asc``
eg. ``ta_openpgp.asc``.
The ``ta_asc.h`` target will capture all such ``t*.asc`` into that
header.
Signatures
----------
We expect ascii armored (``.asc``) detached signatures.
Eg. signature for ``manifest`` would be in ``manifest.asc``
We only support version 4 signatures using RSA (the default for ``gpg``).
OpenSSL
========
The basic idea here is to setup a private CA.
There are lots of good tutorials on available on this topic;
just google *setup openssl ca*.
A good example is https://jamielinux.com/docs/openssl-certificate-authority/
All we need for this library is a copy of the PEM encoded root CA
certificate (trust anchor). This is expected to be in a file named
``t*.pem`` eg. ``ta_rsa.pem``.
The ``ta.h`` target in ``Makefile.inc`` will combine all such
``t*.pem`` files into that header.
Signatures
----------
For Junos we currently use EC DSA signatures with file extension
``.esig`` so the signature for ``manifest`` would be ``manifest.esig``
This was the first signature method we used with the remote signing
servers and it ends up being a signature of a hash.
Ie. client sends a hash which during signing gets hashed again.
So for Junos we define VE_ECDSA_HASH_AGAIN which causes ``verify_ec``
to hash again.
Otherwise our EC DSA and RSA signatures are the default used by
OpenSSL - an original design goal was that a customer could verify our
signatures using nothing but an ``openssl`` binary.
Self tests
==========
If you want the ``loader`` to perform self-test of a given signature
verification method on startup (a must for FIPS 140-2 certification)
you need to provide a suitable file signed by each supported trust
anchor.
These should be stored in files with names that start with ``v`` and
have the same extension as the corresponding trust anchor.
Eg. for ``ta_openpgp.asc`` we use ``vc_openpgp.asc``
and for ``ta_rsa.pem`` we use ``vc_rsa.pem``.
Note for the X.509 case we simply extract the 2nd last certificate
from the relevant chain - which is sure to be a valid certificate
signed by the corresponding trust anchor.
--------------------
$FreeBSD$

403
lib/libsecureboot/brf.c Normal file
View File

@ -0,0 +1,403 @@
// The functions here are derrived from BearSSL/tools/*.c
// When that is refactored suitably we can use them directly.
/*
* Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
* NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
* BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
* ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
* CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#define NEED_BRSSL_H
#include "libsecureboot-priv.h"
#include <brssl.h>
static int
is_ign(int c)
{
if (c == 0) {
return (0);
}
if (c <= 32 || c == '-' || c == '_' || c == '.'
|| c == '/' || c == '+' || c == ':')
{
return (1);
}
return (0);
}
/*
* Get next non-ignored character, normalised:
* ASCII letters are converted to lowercase
* control characters, space, '-', '_', '.', '/', '+' and ':' are ignored
* A terminating zero is returned as 0.
*/
static int
next_char(const char **ps, const char *limit)
{
for (;;) {
int c;
if (*ps == limit) {
return (0);
}
c = *(*ps) ++;
if (c == 0) {
return (0);
}
if (c >= 'A' && c <= 'Z') {
c += 'a' - 'A';
}
if (!is_ign(c)) {
return (c);
}
}
}
/*
* Partial string equality comparison, with normalisation.
*/
static int
eqstr_chunk(const char *s1, size_t s1_len, const char *s2, size_t s2_len)
{
const char *lim1, *lim2;
lim1 = s1 + s1_len;
lim2 = s2 + s2_len;
for (;;) {
int c1, c2;
c1 = next_char(&s1, lim1);
c2 = next_char(&s2, lim2);
if (c1 != c2) {
return (0);
}
if (c1 == 0) {
return (1);
}
}
}
/* see brssl.h */
int
eqstr(const char *s1, const char *s2)
{
return (eqstr_chunk(s1, strlen(s1), s2, strlen(s2)));
}
int
looks_like_DER(const unsigned char *buf, size_t len)
{
int fb;
size_t dlen;
if (len < 2) {
return (0);
}
if (*buf ++ != 0x30) {
return (0);
}
fb = *buf ++;
len -= 2;
if (fb < 0x80) {
return ((size_t)fb == len);
} else if (fb == 0x80) {
return (0);
} else {
fb -= 0x80;
if (len < (size_t)fb + 2) {
return (0);
}
len -= (size_t)fb;
dlen = 0;
while (fb -- > 0) {
if (dlen > (len >> 8)) {
return (0);
}
dlen = (dlen << 8) + (size_t)*buf ++;
}
return (dlen == len);
}
}
static void
vblob_append(void *cc, const void *data, size_t len)
{
bvector *bv;
bv = cc;
VEC_ADDMANY(*bv, data, len);
}
void
free_pem_object_contents(pem_object *po)
{
if (po != NULL) {
xfree(po->name);
xfree(po->data);
}
}
pem_object *
decode_pem(const void *src, size_t len, size_t *num)
{
VECTOR(pem_object) pem_list = VEC_INIT;
br_pem_decoder_context pc;
pem_object po, *pos;
const unsigned char *buf;
bvector bv = VEC_INIT;
int inobj;
int extra_nl;
*num = 0;
br_pem_decoder_init(&pc);
buf = src;
inobj = 0;
po.name = NULL;
po.data = NULL;
po.data_len = 0;
extra_nl = 1;
while (len > 0) {
size_t tlen;
tlen = br_pem_decoder_push(&pc, buf, len);
buf += tlen;
len -= tlen;
switch (br_pem_decoder_event(&pc)) {
case BR_PEM_BEGIN_OBJ:
po.name = xstrdup(br_pem_decoder_name(&pc));
br_pem_decoder_setdest(&pc, vblob_append, &bv);
inobj = 1;
break;
case BR_PEM_END_OBJ:
if (inobj) {
po.data = VEC_TOARRAY(bv);
po.data_len = VEC_LEN(bv);
VEC_ADD(pem_list, po);
VEC_CLEAR(bv);
po.name = NULL;
po.data = NULL;
po.data_len = 0;
inobj = 0;
}
break;
case BR_PEM_ERROR:
xfree(po.name);
VEC_CLEAR(bv);
ve_error_set("ERROR: invalid PEM encoding");
VEC_CLEAREXT(pem_list, &free_pem_object_contents);
return (NULL);
}
/*
* We add an extra newline at the end, in order to
* support PEM files that lack the newline on their last
* line (this is somwehat invalid, but PEM format is not
* standardised and such files do exist in the wild, so
* we'd better accept them).
*/
if (len == 0 && extra_nl) {
extra_nl = 0;
buf = (const unsigned char *)"\n";
len = 1;
}
}
if (inobj) {
ve_error_set("ERROR: unfinished PEM object");
xfree(po.name);
VEC_CLEAR(bv);
VEC_CLEAREXT(pem_list, &free_pem_object_contents);
return (NULL);
}
*num = VEC_LEN(pem_list);
VEC_ADD(pem_list, po);
pos = VEC_TOARRAY(pem_list);
VEC_CLEAR(pem_list);
return (pos);
}
br_x509_certificate *
parse_certificates(unsigned char *buf, size_t len, size_t *num)
{
VECTOR(br_x509_certificate) cert_list = VEC_INIT;
pem_object *pos;
size_t u, num_pos;
br_x509_certificate *xcs;
br_x509_certificate dummy;
*num = 0;
/*
* Check for a DER-encoded certificate.
*/
if (looks_like_DER(buf, len)) {
xcs = xmalloc(2 * sizeof *xcs);
xcs[0].data = buf;
xcs[0].data_len = len;
xcs[1].data = NULL;
xcs[1].data_len = 0;
*num = 1;
return (xcs);
}
pos = decode_pem(buf, len, &num_pos);
if (pos == NULL) {
return (NULL);
}
for (u = 0; u < num_pos; u ++) {
if (eqstr(pos[u].name, "CERTIFICATE")
|| eqstr(pos[u].name, "X509 CERTIFICATE"))
{
br_x509_certificate xc;
xc.data = pos[u].data;
xc.data_len = pos[u].data_len;
pos[u].data = NULL;
VEC_ADD(cert_list, xc);
}
}
for (u = 0; u < num_pos; u ++) {
free_pem_object_contents(&pos[u]);
}
xfree(pos);
if (VEC_LEN(cert_list) == 0) {
return (NULL);
}
*num = VEC_LEN(cert_list);
dummy.data = NULL;
dummy.data_len = 0;
VEC_ADD(cert_list, dummy);
xcs = VEC_TOARRAY(cert_list);
VEC_CLEAR(cert_list);
return (xcs);
}
br_x509_certificate *
read_certificates(const char *fname, size_t *num)
{
br_x509_certificate *xcs;
unsigned char *buf;
size_t len;
*num = 0;
/*
* TODO: reading the whole file is crude; we could parse them
* in a streamed fashion. But it does not matter much in practice.
*/
buf = read_file(fname, &len);
if (buf == NULL) {
return (NULL);
}
xcs = parse_certificates(buf, len, num);
if (xcs == NULL) {
ve_error_set("ERROR: no certificate in file '%s'\n", fname);
}
xfree(buf);
return (xcs);
}
/* see brssl.h */
void
free_certificates(br_x509_certificate *certs, size_t num)
{
size_t u;
for (u = 0; u < num; u ++) {
xfree(certs[u].data);
}
xfree(certs);
}
static void
dn_append(void *ctx, const void *buf, size_t len)
{
VEC_ADDMANY(*(bvector *)ctx, buf, len);
}
int
certificate_to_trust_anchor_inner(br_x509_trust_anchor *ta,
br_x509_certificate *xc)
{
br_x509_decoder_context dc;
bvector vdn = VEC_INIT;
br_x509_pkey *pk;
br_x509_decoder_init(&dc, dn_append, &vdn);
br_x509_decoder_push(&dc, xc->data, xc->data_len);
pk = br_x509_decoder_get_pkey(&dc);
if (pk == NULL) {
ve_error_set("ERROR: CA decoding failed with error %d\n",
br_x509_decoder_last_error(&dc));
VEC_CLEAR(vdn);
return (-1);
}
ta->dn.data = VEC_TOARRAY(vdn);
ta->dn.len = VEC_LEN(vdn);
VEC_CLEAR(vdn);
ta->flags = 0;
if (br_x509_decoder_isCA(&dc)) {
ta->flags |= BR_X509_TA_CA;
}
switch (pk->key_type) {
case BR_KEYTYPE_RSA:
ta->pkey.key_type = BR_KEYTYPE_RSA;
ta->pkey.key.rsa.n = xblobdup(pk->key.rsa.n, pk->key.rsa.nlen);
ta->pkey.key.rsa.nlen = pk->key.rsa.nlen;
ta->pkey.key.rsa.e = xblobdup(pk->key.rsa.e, pk->key.rsa.elen);
ta->pkey.key.rsa.elen = pk->key.rsa.elen;
break;
case BR_KEYTYPE_EC:
ta->pkey.key_type = BR_KEYTYPE_EC;
ta->pkey.key.ec.curve = pk->key.ec.curve;
ta->pkey.key.ec.q = xblobdup(pk->key.ec.q, pk->key.ec.qlen);
ta->pkey.key.ec.qlen = pk->key.ec.qlen;
break;
default:
ve_error_set("ERROR: unsupported public key type in CA\n");
xfree(ta->dn.data);
return (-1);
}
return (0);
}
/* see brssl.h */
void
free_ta_contents(br_x509_trust_anchor *ta)
{
xfree(ta->dn.data);
switch (ta->pkey.key_type) {
case BR_KEYTYPE_RSA:
xfree(ta->pkey.key.rsa.n);
xfree(ta->pkey.key.rsa.e);
break;
case BR_KEYTYPE_EC:
xfree(ta->pkey.key.ec.q);
break;
}
}

View File

@ -0,0 +1,94 @@
/*-
* 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.
*/
/*
* $FreeBSD$
*/
#ifndef _LIBSECUREBOOT_H_
#define _LIBSECUREBOOT_H_
#include <sys/param.h>
#ifdef _STANDALONE
#include <stand.h>
#else
#include <sys/types.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#endif
#include <bearssl.h>
#ifndef NEED_BRSSL_H
unsigned char * read_file(const char *, size_t *);
#endif
extern int DebugVe;
#define DEBUG_PRINTF(n, x) if (DebugVe >= n) printf x
int ve_trust_init(void);
int ve_trust_add(const char *);
void ve_debug_set(int);
void ve_utc_set(time_t utc);
char *ve_error_get(void);
int ve_error_set(const char *, ...) __printflike(1,2);
int ve_self_tests(void);
void fingerprint_info_add(const char *, const char *, const char *,
const char *, struct stat *);
int ve_check_hash(br_hash_compat_context *, const br_hash_class *,
const char *, const char *, size_t);
struct vectx;
struct vectx* vectx_open(int, const char *, off_t, struct stat *, int *);
ssize_t vectx_read(struct vectx *, void *, size_t);
off_t vectx_lseek(struct vectx *, off_t, int);
int vectx_close(struct vectx *);
char * hexdigest(char *, size_t, unsigned char *, size_t);
int verify_fd(int, const char *, off_t, struct stat *);
int verify_open(const char *, int);
unsigned char *verify_signed(const char *, int);
unsigned char *verify_sig(const char *, int);
unsigned char *verify_asc(const char *, int); /* OpenPGP */
void ve_pcr_init(void);
void ve_pcr_update(unsigned char *, size_t);
ssize_t ve_pcr_get(unsigned char *, size_t);
/* flags for verify_{asc,sig,signed} */
#define VEF_VERBOSE 1
#define VE_FINGERPRINT_OK 1
/* errors from verify_fd */
#define VE_FINGERPRINT_NONE -2
#define VE_FINGERPRINT_WRONG -3
#define VE_FINGERPRINT_UNKNOWN -4 /* may not be an error */
#endif /* _LIBSECUREBOOT_H_ */

View File

@ -0,0 +1,47 @@
/*-
* 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.
*
* $FreeBSD$
*/
#ifndef _VERIFY_FILE_H_
#define _VERIFY_FILE_H_
#define VE_GUESS -1 /* let verify_file work it out */
#define VE_TRY 0 /* we don't mind if unverified */
#define VE_WANT 1 /* we want this verified */
#define VE_MUST 2 /* this must be verified */
#define VE_VERIFIED 1 /* all good */
#define VE_UNVERIFIED_OK 0 /* not verified but that's ok */
#define VE_NOT_VERIFYING 2 /* we are not verifying */
struct stat;
void ve_debug_set(int);
int ve_status_get(int);
int load_manifest(const char *, const char *, const char *, struct stat *);
int verify_file(int, const char *, off_t, int);
void verify_pcr_export(void);
#endif /* _VERIFY_FILE_H_ */

View File

@ -0,0 +1,48 @@
/*-
* Copyright (c) 2017, 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.
*/
/*
* $FreeBSD$
*/
#ifndef _LIBSECUREBOOT_PRIV_H_
#define _LIBSECUREBOOT_PRIV_H_
/* public api */
#include "libsecureboot.h"
size_t ve_trust_anchors_add(br_x509_certificate *, size_t);
char *fingerprint_info_lookup(int, const char *);
br_x509_certificate * parse_certificates(unsigned char *, size_t, size_t *);
int certificate_to_trust_anchor_inner(br_x509_trust_anchor *,
br_x509_certificate *);
int verify_rsa_digest(br_rsa_public_key *pkey,
const unsigned char *hash_oid,
unsigned char *mdata, size_t mlen,
unsigned char *sdata, size_t slen);
int openpgp_self_tests(void);
#endif /* _LIBSECUREBOOT_PRIV_H_ */

View File

@ -0,0 +1,114 @@
# $FreeBSD$
# Consider this file an example.
#
# For Junos this is how we obtain trust anchor .pems
# the signing server (http://www.crufty.net/sjg/blog/signing-server.htm)
# for each key will provide the appropriate certificate chain on request
# force these for Junos
MANIFEST_SKIP_ALWAYS= boot
VE_HASH_LIST= \
SHA1 \
SHA256 \
SHA384
VE_SIGNATURE_LIST= \
ECDSA
VE_SIGNATURE_EXT_LIST= \
esig
VE_SELF_TESTS= yes
.if ${MACHINE} == "host" && ${.CURDIR:T} == "tests"
# for testing
VE_HASH_LIST+= \
SHA512
VE_SIGNATURE_LIST+= \
RSA \
DEPRECATED_RSA_SHA1
VE_SIGNATURE_EXT_LIST+= \
sig
.endif
SIGNER ?= ${SB_TOOLS_PATH:U/volume/buildtools/bin}/sign.py
.if exists(${SIGNER})
SIGN_HOST ?= ${SB_SITE:Usvl}-junos-signer.juniper.net
ECDSA_PORT:= ${133%y:L:gmtime}
SIGN_ECDSA= ${PYTHON} ${SIGNER} -u ${SIGN_HOST}:${ECDSA_PORT} -h sha256
RSA2_PORT:= ${163%y:L:gmtime}
SIGN_RSA2= ${PYTHON} ${SIGNER} -u ${SIGN_HOST}:${RSA2_PORT} -h sha256
.if !empty(OPENPGP_SIGN_URL)
VE_SIGNATURE_LIST+= OPENPGP
VE_SIGNATURE_EXT_LIST+= asc
SIGN_OPENPGP= ${PYTHON} ${SIGNER:H}/openpgp-sign.py -a -u ${OPENPGP_SIGN_URL}
ta_openpgp.asc:
${SIGN_OPENPGP} -C ${.TARGET}
ta.h: ta_openpgp.asc
.if ${VE_SELF_TESTS} != "no"
# for self test
vc_openpgp.asc: ta_openpgp.asc
${SIGN_OPENPGP} ${.ALLSRC:M*.asc}
mv ta_openpgp.asc.asc ${.TARGET}
ta.h: vc_openpgp.asc
.endif
.endif
rcerts.pem:
${SIGN_RSA2} -C ${.TARGET}
ecerts.pem:
${SIGN_ECDSA} -C ${.TARGET}
.if ${VE_SIGNATURE_LIST:tu:MECDSA} != ""
# the last cert in the chain is the one we want
ta_ec.pem: ecerts.pem _LAST_PEM_USE
.if ${VE_SELF_TESTS} != "no"
# these are for verification self test
vc_ec.pem: ecerts.pem _2ndLAST_PEM_USE
.endif
.endif
.if ${VE_SIGNATURE_LIST:tu:MRSA} != ""
ta_rsa.pem: rcerts.pem _LAST_PEM_USE
.if ${VE_SELF_TESTS} != "no"
vc_rsa.pem: rcerts.pem _2ndLAST_PEM_USE
.endif
.endif
# we take the mtime of this as our baseline time
BUILD_UTC_FILE= ecerts.pem
#VE_DEBUG_LEVEL=3
#VE_VERBOSE_DEFAULT=1
.else
# you need to provide t*.pem or t*.asc files for each trust anchor
.if empty(TRUST_ANCHORS)
TRUST_ANCHORS!= cd ${.CURDIR} && 'ls' -1 *.pem t*.asc 2> /dev/null
.endif
.if empty(TRUST_ANCHORS)
.error Need TRUST_ANCHORS see ${.CURDIR}/README.rst
.endif
.if ${TRUST_ANCHORS:T:Mt*.pem} != ""
ta.h: ${TRUST_ANCHORS:M*.pem}
.endif
.if ${TRUST_ANCHORS:T:Mt*.asc} != ""
VE_SIGNATURE_LIST+= OPENPGP
VE_SIGNATURE_EXT_LIST+= asc
ta_asc.h: ${TRUST_ANCHORS:M*.asc}
.endif
# we take the mtime of this as our baseline time
BUILD_UTC_FILE?= ${TRUST_ANCHORS:[1]}
.endif

View File

@ -0,0 +1,48 @@
# $FreeBSD$
# decode OpenPGP signatures per rfc4880
.PATH: ${.PARSEDIR}
CFLAGS+= -DUSE_BEARSSL
BRSSL_SRCS+= dearmor.c
SRCS+= \
decode.c \
opgp_key.c \
opgp_sig.c
opgp_key.o opgp_key.po opgp_key.pico: ta_asc.h
# Generate ta_asc.h containing one or more OpenPGP trust anchors.
#
# Since each trust anchor must be processed individually,
# we create ta_ASC as a list of pointers to them.
#
# If we are doing self-tests, we define another arrary vc_ASC
# containing pointers to a signature of each trust anchor.
# It is assumed that these v*.asc files are named similarly to
# the appropriate t*.asc so that the relative order of vc_ASC
# entries matches ta_ASC.
#
ta_asc.h: ${.ALLTARGETS:M[tv]*.asc:O:u}
.if ${VE_SIGNATURE_LIST:MOPENPGP} != ""
@( echo '/* Autogenerated - DO NOT EDIT!!! */'; echo; \
echo "#define HAVE_TA_ASC 1"; \
set -- ${.ALLSRC:Mt*.asc:@f@$f ${f:T:R}@}; \
while test $$# -ge 2; do \
file2c -sx "static const char $$2[] = {" ', 0x00 };' < $$1; \
shift 2; \
done; \
echo 'static const char *ta_ASC[] = { ${.ALLSRC:Mt*.asc:T:R:ts,}, NULL };'; \
echo; ) > ${.TARGET}
.if ${VE_SELF_TESTS} != "no"
@( echo "#define HAVE_VC_ASC 1"; \
set -- ${.ALLSRC:Mv*.asc:@f@$f ${f:T:R}@}; \
while test $$# -ge 2; do \
file2c -sx "static const char $$2[] = {" ', 0x00 };' < $$1; \
shift 2; \
done; \
echo 'static const char *vc_ASC[] = { ${.ALLSRC:Mv*.asc:T:R:ts,}, NULL };'; \
echo; ) >> ${.TARGET}
.endif
.endif

View File

@ -0,0 +1,144 @@
/*-
* Copyright (c) 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$");
#define NEED_BRSSL_H
#include <libsecureboot.h>
#include <brssl.h>
#include "decode.h"
/**
* @brief decode ascii armor
*
* once we get rid of the trailing checksum
* we can treat as PEM.
*
* @sa rfc4880:6.2
*/
unsigned char *
dearmor(char *pem, size_t nbytes, size_t *len)
{
#ifdef USE_BEARSSL
pem_object *po;
size_t npo;
#else
BIO *bp;
char *name = NULL;
char *header = NULL;
#endif
unsigned char *data = NULL;
char *cp;
char *ep;
/* we need to remove the Armor tail */
if ((cp = strstr((char *)pem, "\n=")) &&
(ep = strstr(cp, "\n---"))) {
memmove(cp, ep, nbytes - (size_t)(ep - pem));
nbytes -= (size_t)(ep - cp);
pem[nbytes] = '\0';
}
#ifdef USE_BEARSSL
/* we also need to remove any headers */
if ((cp = strstr((char *)pem, "---\n")) &&
(ep = strstr(cp, "\n\n"))) {
cp += 4;
ep += 2;
memmove(cp, ep, nbytes - (size_t)(ep - pem));
nbytes -= (size_t)(ep - cp);
pem[nbytes] = '\0';
}
if ((po = decode_pem(pem, nbytes, &npo))) {
data = po->data;
*len = po->data_len;
}
#else
if ((bp = BIO_new_mem_buf(pem, (int)nbytes))) {
long llen = (long)nbytes;
if (!PEM_read_bio(bp, &name, &header, &data, &llen))
data = NULL;
BIO_free(bp);
*len = (size_t)llen;
}
#endif
return (data);
}
#ifdef MAIN_DEARMOR
#include <unistd.h>
#include <fcntl.h>
#include <err.h>
/*
* Mostly a unit test.
*/
int
main(int argc, char *argv[])
{
const char *infile, *outfile;
unsigned char *data;
size_t n, x;
int fd;
int o;
infile = outfile = NULL;
while ((o = getopt(argc, argv, "i:o:")) != -1) {
switch (o) {
case 'i':
infile = optarg;
break;
case 'o':
outfile = optarg;
break;
default:
errx(1, "unknown option: -%c", o);
}
}
if (!infile)
errx(1, "need -i infile");
if (outfile) {
if ((fd = open(outfile, O_WRONLY|O_CREAT|O_TRUNC)) < 0)
err(1, "cannot open %s", outfile);
} else {
fd = 1; /* stdout */
}
data = read_file(infile, &n);
if (!(data[0] & OPENPGP_TAG_ISTAG))
data = dearmor(data, n, &n);
for (x = 0; x < n; ) {
o = write(fd, &data[x], (n - x));
if (o < 0)
err(1, "cannot write");
x += o;
}
if (fd != 1)
close(fd);
free(data);
return (0);
}
#endif

View File

@ -0,0 +1,304 @@
/*-
* Copyright (c) 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 <libsecureboot.h>
#include "decode.h"
char *
octets2hex(unsigned char *ptr, size_t n)
{
char *hex;
char *cp;
size_t i;
hex = malloc(2 * n + 1);
if (hex != NULL) {
for (i = 0, cp = hex; i < n; i++) {
snprintf(&cp[i*2], 3, "%02X", ptr[i]);
}
}
return (hex);
}
unsigned char *
i2octets(int n, size_t i)
{
static unsigned char o[16];
int x, j;
if (n > 15)
return (NULL);
for (j = 0, x = n - 1; x >= 0; x--, j++) {
o[j] = (unsigned char)((i & (0xff << x * 8)) >> x * 8);
}
return (o);
}
int
octets2i(unsigned char *ptr, size_t n)
{
size_t i;
int val;
for (val = i = 0; i < n; i++) {
val |= (*ptr++ << ((n - i - 1) * 8));
}
return (val);
}
/**
* @brief decode packet tag
*
* Also indicate if new/old and in the later case
* the length type
*
* @sa rfc4880:4.2
*/
int
decode_tag(unsigned char *ptr, int *isnew, int *ltype)
{
int tag;
if (!ptr || !isnew || !ltype)
return (-1);
tag = *ptr;
if (!(tag & OPENPGP_TAG_ISTAG))
return (-1); /* we are lost! */
*isnew = tag & OPENPGP_TAG_ISNEW;
if (*isnew) {
*ltype = -1; /* irrelevant */
tag &= OPENPGP_TAG_NEW_MASK;
} else {
*ltype = tag & OPENPGP_TAG_OLD_TYPE;
tag = (tag & OPENPGP_TAG_OLD_MASK) >> 2;
}
return (tag);
}
/**
* @brief return packet length
*
* @sa rfc4880:4.2.2
*/
static int
decode_new_len(unsigned char **pptr)
{
unsigned char *ptr;
int len = -1;
if (pptr == NULL)
return (-1);
ptr = *pptr;
if (!(*ptr < 224 || *ptr == 255))
return (-1); /* not supported */
if (*ptr < 192)
len = *ptr++;
else if (*ptr < 224) {
len = ((*ptr - 192) << 8) + *(ptr+1) + 192;
ptr++;
} else if (*ptr == 255) {
len = (*ptr++ << 24);
len |= (*ptr++ << 16);
len |= (*ptr++ < 8);
len |= *ptr++;
}
*pptr = ptr;
return (len);
}
/**
* @brief return packet length
*
* @sa rfc4880:4.2.1
*/
static int
decode_len(unsigned char **pptr, int ltype)
{
unsigned char *ptr;
int len;
if (ltype < 0)
return (decode_new_len(pptr));
if (pptr == NULL)
return (-1);
ptr = *pptr;
switch (ltype) {
case 0:
len = *ptr++;
break;
case 1:
len = (*ptr++ << 8);
len |= *ptr++;
break;
case 2:
len = *ptr++ << 24;
len |= *ptr++ << 16;
len |= *ptr++ << 8;
len |= *ptr++;
break;
case 3:
default:
/* Not supported */
len = -1;
}
*pptr = ptr;
return (len);
}
/**
* @brief return pointer and length of an mpi
*
* @sa rfc4880:3.2
*/
unsigned char *
decode_mpi(unsigned char **pptr, size_t *sz)
{
unsigned char *data;
unsigned char *ptr;
size_t mlen;
if (pptr == NULL || sz == NULL)
return (NULL);
ptr = *pptr;
mlen = (size_t)(*ptr++ << 8);
mlen |= (size_t)*ptr++; /* number of bits */
mlen = (mlen + 7) / 8; /* number of bytes */
*sz = mlen;
data = ptr;
ptr += mlen;
*pptr = ptr;
return (data);
}
/**
* @brief return an OpenSSL BIGNUM from mpi
*
* @sa rfc4880:3.2
*/
#ifdef USE_BEARSSL
unsigned char *
mpi2bn(unsigned char **pptr, size_t *sz)
{
return (decode_mpi(pptr, sz));
}
#else
BIGNUM *
mpi2bn(unsigned char **pptr)
{
BIGNUM *bn = NULL;
unsigned char *ptr;
int mlen;
if (pptr == NULL)
return (NULL);
ptr = *pptr;
mlen = (*ptr++ << 8);
mlen |= *ptr++; /* number of bits */
mlen = (mlen + 7) / 8; /* number of bytes */
bn = BN_bin2bn(ptr, mlen, NULL);
ptr += mlen;
*pptr = ptr;
return (bn);
}
#endif
/**
* @brief decode a packet
*
* If want is set, check that the packet tag matches
* if all good, call the provided decoder with its arg
*
* @return count of unconsumed data
*
* @sa rfc4880:4.2
*/
int
decode_packet(int want, unsigned char **pptr, size_t nbytes,
decoder_t decoder, void *decoder_arg)
{
int tag;
unsigned char *ptr;
unsigned char *nptr;
int isnew, ltype;
int len;
int hlen;
int rc = 0;
nptr = ptr = *pptr;
tag = decode_tag(ptr, &isnew, &ltype);
if (want > 0 && tag != want)
return (-1);
ptr++;
len = rc = decode_len(&ptr, ltype);
hlen = (int)(ptr - nptr);
nptr = ptr + len; /* consume it */
if (decoder)
rc = decoder(tag, &ptr, len, decoder_arg);
*pptr = nptr;
nbytes -= (size_t)(hlen + len);
if (rc < 0)
return (rc); /* error */
return ((int)nbytes); /* unconsumed data */
}
/**
* @brief decode a sub packet
*
* @sa rfc4880:5.2.3.1
*/
unsigned char *
decode_subpacket(unsigned char **pptr, int *stag, int *sz)
{
unsigned char *ptr;
int len;
ptr = *pptr;
len = decode_len(&ptr, -1);
*sz = (int)(len + ptr - *pptr);
*pptr = ptr + len;
*stag = *ptr++;
return (ptr);
}

View File

@ -0,0 +1,57 @@
/*-
* Copyright (c) 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.
*/
/*
* $FreeBSD$
*/
#ifdef USE_BEARSSL
unsigned char * mpi2bn(unsigned char **pptr, size_t *sz);
#else
# include <openssl/bn.h>
# include <openssl/rsa.h>
# include <openssl/evp.h>
BIGNUM * mpi2bn(unsigned char **pptr);
#endif
#define NEW(x) calloc(1,sizeof(x))
#define OPENPGP_TAG_ISTAG 0200
#define OPENPGP_TAG_ISNEW 0100
#define OPENPGP_TAG_NEW_MASK 0077
#define OPENPGP_TAG_OLD_MASK 0074
#define OPENPGP_TAG_OLD_TYPE 0003
typedef int (*decoder_t)(int, unsigned char **, int, void *);
unsigned char * i2octets(int n, size_t i);
int octets2i(unsigned char *ptr, size_t n);
char * octets2hex(unsigned char *ptr, size_t n);
int decode_tag(unsigned char *ptr, int *isnew, int *ltype);
unsigned char * decode_mpi(unsigned char **pptr, size_t *sz);
unsigned char * dearmor(char *pem, size_t nbytes, size_t *len);
int decode_packet(int want, unsigned char **pptr, size_t nbytes,
decoder_t decoder, void *decoder_arg);
unsigned char * decode_subpacket(unsigned char **pptr, int *stag, int *sz);

View File

@ -0,0 +1,352 @@
/*-
* Copyright (c) 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 "../libsecureboot-priv.h"
#include "decode.h"
#include "packet.h"
/**
* @brief decode user-id packet
*
* This is trivial
*
* @sa rfc4880:5.11
*/
ssize_t
decode_user(int tag, unsigned char **pptr, size_t len, OpenPGP_user *user)
{
char *cp;
if (tag == 13) {
user->id = malloc(len + 1);
strncpy(user->id, (char *)*pptr, len);
user->id[len] = '\0';
user->name = user->id;
cp = strchr(user->id, '<');
if (cp > user->id) {
user->id = strdup(user->id);
cp[-1] = '\0';
}
}
*pptr += len;
return ((ssize_t)len);
}
/**
* @brief decode a key packet
*
* We only really support v4 and RSA
*
* @sa rfc4880:5.5.1.1
*/
ssize_t
decode_key(int tag, unsigned char **pptr, size_t len, OpenPGP_key *key)
{
unsigned char *ptr;
int version;
#ifdef USE_BEARSSL
br_sha1_context mctx;
unsigned char mdata[br_sha512_SIZE];
size_t mlen;
#else
RSA *rsa = NULL;
const EVP_MD *md = NULL;
EVP_MD_CTX mctx;
unsigned char mdata[EVP_MAX_MD_SIZE];
unsigned int mlen;
#endif
if (tag != 6)
return (-1);
key->key = NULL;
ptr = *pptr;
version = *ptr;
if (version == 4) { /* all we support really */
/* comput key fingerprint and id @sa rfc4880:12.2 */
mdata[0] = 0x99; /* rfc4880: 12.2.a.1 */
mdata[1] = (len >> 8) & 0xff;
mdata[2] = len & 0xff;
#ifdef USE_BEARSSL
br_sha1_init(&mctx);
br_sha1_update(&mctx, mdata, 3);
br_sha1_update(&mctx, ptr, len);
br_sha1_out(&mctx, mdata);
mlen = br_sha1_SIZE;
#else
md = EVP_get_digestbyname("sha1");
EVP_DigestInit(&mctx, md);
EVP_DigestUpdate(&mctx, mdata, 3);
EVP_DigestUpdate(&mctx, ptr, len);
mlen = (unsigned int)sizeof(mdata);
EVP_DigestFinal(&mctx, mdata, &mlen);
#endif
key->id = octets2hex(&mdata[mlen - 8], 8);
}
ptr += 1; /* done with version */
ptr += 4; /* skip ctime */
if (version == 3)
ptr += 2; /* valid days */
key->sig_alg = *ptr++;
if (key->sig_alg == 1) { /* RSA */
#ifdef USE_BEARSSL
key->key = NEW(br_rsa_public_key);
if (!key->key)
goto oops;
key->key->n = mpi2bn(&ptr, &key->key->nlen);
key->key->e = mpi2bn(&ptr, &key->key->elen);
#else
rsa = RSA_new();
if (!rsa)
goto oops;
rsa->n = mpi2bn(&ptr);
rsa->e = mpi2bn(&ptr);
key->key = EVP_PKEY_new();
if (!key->key || !rsa->n || !rsa->e) {
goto oops;
}
if (!EVP_PKEY_set1_RSA(key->key, rsa))
goto oops;
#endif
}
/* we are done */
return ((ssize_t)len);
oops:
#ifdef USE_BEARSSL
free(key->key);
key->key = NULL;
#else
if (rsa)
RSA_free(rsa);
if (key->key) {
EVP_PKEY_free(key->key);
key->key = NULL;
}
#endif
return (-1);
}
static OpenPGP_key *
load_key_buf(unsigned char *buf, size_t nbytes)
{
unsigned char *data = NULL;
unsigned char *ptr;
ssize_t rc;
int tag;
OpenPGP_key *key;
if (!buf)
return (NULL);
initialize();
if (!(buf[0] & OPENPGP_TAG_ISTAG)) {
data = dearmor((char *)buf, nbytes, &nbytes);
ptr = data;
} else
ptr = buf;
key = NEW(OpenPGP_key);
if (key) {
rc = decode_packet(0, &ptr, nbytes, (decoder_t)decode_key,
key);
if (rc < 0) {
free(key);
key = NULL;
} else if (rc > 8) {
int isnew, ltype;
tag = decode_tag(ptr, &isnew, &ltype);
if (tag == 13) {
key->user = NEW(OpenPGP_user);
rc = decode_packet(0, &ptr, (size_t)rc,
(decoder_t)decode_user, key->user);
}
}
}
free(data);
return (key);
}
static LIST_HEAD(, OpenPGP_key_) trust_list;
/**
* @brief add a key to our list
*/
void
openpgp_trust_add(OpenPGP_key *key)
{
static int once = 0;
if (!once) {
once = 1;
LIST_INIT(&trust_list);
}
if (key)
LIST_INSERT_HEAD(&trust_list, key, entries);
}
/**
* @brief if keyID is in our list return the key
*
* @return key or NULL
*/
OpenPGP_key *
openpgp_trust_get(const char *keyID)
{
OpenPGP_key *key;
openpgp_trust_add(NULL); /* initialize if needed */
LIST_FOREACH(key, &trust_list, entries) {
if (strcmp(key->id, keyID) == 0)
return (key);
}
return (NULL);
}
/**
* @brief load a key from file
*/
OpenPGP_key *
load_key_file(const char *kfile)
{
unsigned char *data = NULL;
size_t n;
OpenPGP_key *key;
data = read_file(kfile, &n);
key = load_key_buf(data, n);
free(data);
openpgp_trust_add(key);
return (key);
}
#include <ta_asc.h>
#ifndef _STANDALONE
/* we can lookup keyID in filesystem */
static const char *trust_store[] = {
"/var/db/trust",
"/etc/db/trust",
NULL,
};
/**
* @brief lookup key id in trust store
*
*/
static OpenPGP_key *
load_trusted_key_id(const char *keyID)
{
char kfile[MAXPATHLEN];
const char **tp;
size_t n;
for (tp = trust_store; *tp; tp++) {
n = (size_t)snprintf(kfile, sizeof(kfile), "%s/%s", *tp, keyID);
if (n >= sizeof(kfile))
return (NULL);
if (access(kfile, R_OK) == 0) {
return (load_key_file(kfile));
}
}
return (NULL);
}
#endif
/**
* @brief return key if trusted
*/
OpenPGP_key *
load_key_id(const char *keyID)
{
static int once = 0;
OpenPGP_key *key;
if (!once) {
#ifdef HAVE_TA_ASC
const char **tp;
char *cp;
size_t n;
for (tp = ta_ASC; *tp; tp++) {
if ((cp = strdup(*tp))) {
n = strlen(cp);
key = load_key_buf((unsigned char *)cp, n);
free(cp);
openpgp_trust_add(key);
}
}
#endif
once = 1;
}
key = openpgp_trust_get(keyID);
#ifndef _STANDALONE
if (!key)
key = load_trusted_key_id(keyID);
#endif
return (key);
}
/**
* @brief test that we can verify a signature
*
* Unlike X.509 certificates, we only support RSA keys
* so we stop after first successful signature verification
* (which should also be the first attempt ;-)
*/
int
openpgp_self_tests(void)
{
static int rc = -1; /* remember result */
#ifdef HAVE_VC_ASC
const char **vp, **tp;
char *fdata, *sdata = NULL;
size_t fbytes, sbytes;
for (tp = ta_ASC, vp = vc_ASC; *tp && *vp && rc; tp++, vp++) {
if ((fdata = strdup(*tp)) &&
(sdata = strdup(*vp))) {
fbytes = strlen(fdata);
sbytes = strlen(sdata);
rc = openpgp_verify("ta_ASC",
(unsigned char *)fdata, fbytes,
(unsigned char *)sdata, sbytes, 0);
printf("Testing verify OpenPGP signature:\t\t%s\n",
rc ? "Failed" : "Passed");
}
free(fdata);
free(sdata);
}
#endif
return (rc);
}

View File

@ -0,0 +1,484 @@
/*-
* Copyright (c) 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.
*/
/*
* RCSid:
* from: signer.c,v 1.10 2018/03/23 01:14:30 sjg
*
* @(#) Copyright (c) 2012 Simon J. Gerraty
*
* This file is provided in the hope that it will
* be of use. There is absolutely NO WARRANTY.
* Permission to copy, redistribute or otherwise
* use this file is hereby granted provided that
* the above copyright notice and this notice are
* left intact.
*
* Please send copies of changes and bug-fixes to:
* sjg@crufty.net
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include "../libsecureboot-priv.h"
#ifdef _STANDALONE
#define warnx printf
#else
#include <sys/param.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <fcntl.h>
#include <stdlib.h>
#include <string.h>
#include <err.h>
#endif
#include "decode.h"
#include "packet.h"
#ifdef USE_BEARSSL
#define get_error_string ve_error_get
void
initialize (void)
{
#ifdef _STANDALONE
ve_trust_init();
#endif
}
#else
#include <openssl/err.h>
/**
* @brief intialize OpenSSL
*/
void
initialize(void)
{
static int once;
if (once)
return);
once = 1;
//CRYPTO_malloc_init();
ERR_load_crypto_strings();
OpenSSL_add_all_algorithms();
}
/**
* @brief
* last error from OpenSSL as a string
*/
char *
get_error_string(void)
{
initialize();
return (ERR_error_string(ERR_get_error(), NULL));
}
#endif
/**
* @brief decode a signature packet
*
* We only support RSA
*
* @sa rfc4880:5.2
*/
ssize_t
decode_sig(int tag, unsigned char **pptr, size_t len, OpenPGP_sig *sig)
{
unsigned char *ptr;
unsigned char *pgpbytes;
unsigned char *sp;
int version;
int hcount = 0;
int ucount = 0;
int stag = 0;
int n;
n = tag; /* avoid unused */
/*
* We need to keep a reference to the packet bytes
* as these form part of the signature data.
*
* @sa rfc4880:5.2.4
*/
pgpbytes = ptr = *pptr;
version = *ptr++;
if (version == 3) {
ptr++;
sig->pgpbytes = malloc(5);
if (!sig->pgpbytes)
return (-1);
memcpy(sig->pgpbytes, ptr, 5);
sig->pgpbytes_len = 5;
sig->sig_type = *ptr++;
ptr += 4;
sig->key_id = octets2hex(ptr, 8);
ptr += 8;
sig->sig_alg = *ptr++;
sig->hash_alg = *ptr++;
} else if (version == 4) {
sig->sig_type = *ptr++;
sig->sig_alg = *ptr++;
sig->hash_alg = *ptr++;
hcount = octets2i(ptr, 2);
ptr += 2;
sig->pgpbytes_len = (size_t)hcount + 6;
sig->pgpbytes = malloc(sig->pgpbytes_len + 6);
if (!sig->pgpbytes)
return (-1);
memcpy(sig->pgpbytes, pgpbytes, sig->pgpbytes_len);
sp = &sig->pgpbytes[sig->pgpbytes_len];
*sp++ = 4;
*sp++ = 255;
memcpy(sp, i2octets(4, (int)sig->pgpbytes_len), 4);
sig->pgpbytes_len += 6;
while (hcount > 0) {
sp = decode_subpacket(&ptr, &stag, &n);
hcount -= n;
/* can check stag to see if we care */
}
ucount = octets2i(ptr, 2);
ptr += 2;
while (ucount > 0) {
sp = decode_subpacket(&ptr, &stag, &n);
ucount -= n;
/* can check stag to see if we care */
if (stag == 16) {
free(sig->key_id);
sig->key_id = octets2hex(sp, 8);
}
}
} else
return (-1);
ptr += 2; /* skip hash16 */
if (sig->sig_alg == 1) { /* RSA */
sig->sig = decode_mpi(&ptr, &sig->sig_len);
}
/* we are done */
return ((ssize_t)len);
}
/**
* @brief map OpenPGP hash algorithm id's to name
*
* @sa rfc4880:9.4
*/
static struct hash_alg_map {
int halg;
const char *hname;
} hash_algs[] = {
{1, "md5"},
{2, "sha1"},
{8, "sha256"},
{9, "sha384"},
{10, "sha512"},
{11, "sha224"},
{0, NULL},
};
static const char *
get_hname(int hash_alg)
{
struct hash_alg_map *hmp;
for (hmp = hash_algs; hmp->halg > 0; hmp++) {
if (hmp->halg == hash_alg)
return (hmp->hname);
}
return (NULL);
}
/* lifted from signer.c */
/**
* @brief verify a digest
*
* The public key, digest name, file and signature data.
*
* @return 1 on success 0 on failure, -1 on error
*/
#ifndef USE_BEARSSL
static int
verify_digest (EVP_PKEY *pkey,
const char *digest,
unsigned char *mdata, size_t mlen,
unsigned char *sdata, size_t slen)
{
EVP_MD_CTX ctx;
const EVP_MD *md = NULL;
EVP_PKEY_CTX *pctx = NULL;
int rc = 0;
int i = -1;
initialize();
md = EVP_get_digestbyname(digest);
EVP_DigestInit(&ctx, md);
pctx = EVP_PKEY_CTX_new(pkey, NULL);
if (!pctx)
goto fail;
if (EVP_PKEY_verify_init(pctx) <= 0)
goto fail;
if (EVP_PKEY_CTX_set_signature_md(pctx, ctx.digest) <= 0)
goto fail;
i = EVP_PKEY_verify(pctx, sdata, slen, mdata, mlen);
if (i >= 0)
rc = i;
fail:
EVP_PKEY_CTX_free(pctx);
return (rc);
}
#endif
/**
* @brief verify OpenPGP signed file
*
*
* @param[in] filename
* used to determine the signature name
*
* @param[in] fdata
* content of filename
*
* @param[in] fbytes
* of fdata
*
* @param[in] sdata
* content of signature
*
* @param[in] sbytes
* of sdata
*
* @param[in] flags
*
* @return 0 on success
*/
int
openpgp_verify(const char *filename,
unsigned char *fdata, size_t fbytes,
unsigned char *sdata, size_t sbytes,
int flags)
{
OpenPGP_key *key;
OpenPGP_sig *sig;
#ifdef USE_BEARSSL
const br_hash_class *md;
br_hash_compat_context mctx;
const unsigned char *hash_oid;
#else
const EVP_MD *md = NULL;
EVP_MD_CTX mctx;
#endif
unsigned char mdata[64];
unsigned char *ptr;
unsigned char *ddata = NULL;
const char *hname;
size_t mlen;
int rc = -1;
initialize();
sig = NEW(OpenPGP_sig);
if (!sdata || !sig) {
warnx("cannot verify %s", filename);
goto oops;
}
if (!(sdata[0] & OPENPGP_TAG_ISTAG))
sdata = ddata = dearmor((char *)sdata, sbytes, &sbytes);
ptr = sdata;
rc = decode_packet(2, &ptr, sbytes, (decoder_t)decode_sig, sig);
if (rc == 0 && sig->key_id) {
key = load_key_id(sig->key_id);
if (!key) {
warnx("cannot find key-id: %s", sig->key_id);
rc = -1;
} else if (!(hname = get_hname(sig->hash_alg))) {
warnx("unsupported hash algorithm: %d", sig->hash_alg);
rc = -1;
} else {
/*
* Hash fdata according to the OpenPGP recipe
*
* @sa rfc4880:5.2.4
*/
#ifdef USE_BEARSSL
switch (sig->hash_alg) { /* see hash_algs above */
case 2: /* sha1 */
md = &br_sha1_vtable;
mlen = br_sha1_SIZE;
hash_oid = BR_HASH_OID_SHA1;
break;
case 8: /* sha256 */
md = &br_sha256_vtable;
mlen = br_sha256_SIZE;
hash_oid = BR_HASH_OID_SHA256;
break;
default:
warnx("unsupported hash algorithm: %s", hname);
goto oops;
}
md->init(&mctx.vtable);
md->update(&mctx.vtable, fdata, fbytes);
md->update(&mctx.vtable, sig->pgpbytes,
sig->pgpbytes_len);
md->out(&mctx.vtable, mdata);
rc = verify_rsa_digest(key->key, hash_oid,
mdata, mlen, sig->sig, sig->sig_len);
#else
md = EVP_get_digestbyname(hname);
EVP_DigestInit(&mctx, md);
EVP_DigestUpdate(&mctx, fdata, fbytes);
EVP_DigestUpdate(&mctx, sig->pgpbytes,
sig->pgpbytes_len);
mlen = sizeof(mdata);
EVP_DigestFinal(&mctx,mdata,(unsigned int *)&mlen);
rc = verify_digest(key->key, hname, mdata, mlen,
sig->sig, sig->sig_len);
#endif
if (rc > 0) {
if ((flags & 1))
printf("Verified %s signed by %s\n",
filename,
key->user ? key->user->name : "someone");
rc = 0; /* success */
} else if (rc == 0) {
printf("Unverified %s: %s\n",
filename, get_error_string());
rc = 1;
} else {
printf("Unverified %s\n", filename);
}
}
} else {
warnx("cannot decode signature for %s", filename);
rc = -1;
}
oops:
free(ddata);
free(sig);
return (rc);
}
#ifndef _STANDALONE
/**
* @brief list of extensions we handle
*
* ".asc" is preferred as it works seamlessly with openpgp
*/
static const char *sig_exts[] = {
".asc",
".pgp",
".psig",
NULL,
};
/**
* @brief verify OpenPGP signed file
*
*
* @param[in] filename
* used to determine the signature name
*
* @param[in] fdata
* content of filename
*
* @param[in] nbytes
* of fdata
*
* @return
*/
int
openpgp_verify_file(const char *filename, unsigned char *fdata, size_t nbytes)
{
char pbuf[MAXPATHLEN];
unsigned char *sdata;
const char *sname = NULL;
const char **ep;
size_t sz;
int n;
for (ep = sig_exts; *ep; ep++) {
n = snprintf(pbuf, sizeof(pbuf), "%s%s", filename, *ep);
if (n >= (int)sizeof(pbuf)) {
warnx("cannot form signature name for %s", filename);
return (-1);
}
if (access(pbuf, R_OK) == 0) {
sname = pbuf;
break;
}
}
if (!sname) {
warnx("cannot find signature for %s", filename);
return (-1);
}
sdata = read_file(sname, &sz);
return (openpgp_verify(filename, fdata, nbytes, sdata, sz, 1));
}
#endif
/**
* @brief verify OpenPGP signature
*
* @return content of signed file
*/
unsigned char *
verify_asc(const char *sigfile, int flags)
{
char pbuf[MAXPATHLEN];
char *cp;
size_t n;
unsigned char *fdata, *sdata;
size_t fbytes, sbytes;
if ((sdata = read_file(sigfile, &sbytes))) {
n = strlcpy(pbuf, sigfile, sizeof(pbuf));
if ((cp = strrchr(pbuf, '.')))
*cp = '\0';
if ((fdata = read_file(pbuf, &fbytes))) {
if (openpgp_verify(pbuf, fdata, fbytes, sdata,
sbytes, flags)) {
free(fdata);
fdata = NULL;
}
}
} else
fdata = NULL;
free(sdata);
return (fdata);
}

View File

@ -0,0 +1,82 @@
/*-
* Copyright (c) 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.
*/
/*
* $FreeBSD$
*/
#include <sys/queue.h>
/*
* Structs to represent what we need
*/
typedef struct OpenPGP_user {
char *id;
char *name;
} OpenPGP_user;
struct OpenPGP_key_ {
char *id;
int sig_alg;
OpenPGP_user *user;
#ifdef USE_BEARSSL
br_rsa_public_key *key;
#else
EVP_PKEY *key;
#endif
LIST_ENTRY(OpenPGP_key_) entries;
};
typedef struct OpenPGP_key_ OpenPGP_key;
typedef struct OpenPGP_sig {
char *key_id;
int sig_type;
int sig_alg;
int hash_alg;
unsigned char *pgpbytes;
size_t pgpbytes_len;
unsigned char *sig;
size_t sig_len;
} OpenPGP_sig;
void openpgp_trust_add(OpenPGP_key *key);
OpenPGP_key * openpgp_trust_get(const char *keyID);
OpenPGP_key * load_key_file(const char *kfile);
OpenPGP_key * load_key_id(const char *keyID);
void initialize(void);
char * get_error_string(void);
int openpgp_verify(const char *filename, unsigned char *fdata, size_t fbytes,
unsigned char *sdata, size_t sbytes, int flags);
int openpgp_verify_file(const char *filename, unsigned char *fdata,
size_t nbytes);
/* packet decoders */
#define DECODER_DECL(x) \
ssize_t decode_##x(int, unsigned char **, size_t, OpenPGP_##x *)
DECODER_DECL(user);
DECODER_DECL(key);
DECODER_DECL(sig);

View File

@ -0,0 +1,61 @@
/*-
* 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 <libsecureboot.h>
unsigned char *
read_file(const char *path, size_t *len)
{
int fd, m, n, x;
struct stat st;
unsigned char *buf;
if (len)
*len = 0;
if ((fd = open(path, O_RDONLY)) < 0)
return (NULL);
fstat(fd, &st);
if (len)
*len = st.st_size;
buf = malloc(st.st_size + 1);
for (x = 0, m = st.st_size; m > 0; ) {
n = read(fd, &buf[x], m);
if (n < 0)
break;
if (n > 0) {
m -= n;
x += n;
}
}
close(fd);
if (m == 0) {
buf[st.st_size] = '\0';
return (buf);
}
free(buf);
return (NULL);
}

View File

@ -0,0 +1,18 @@
# $FreeBSD$
PROG= tvo
SRCS+= tvo.c
CFLAGS+= -DUNIT_TEST -g -O0
LIBADD+= bearssl
MAN=
NO_SHARED=
# we want to test verify_file api too
# which requires a kludge or two
.include "../Makefile.libsa.inc"
BRSSL_CFLAGS := ${BRSSL_CFLAGS:N-DNO_STDIO}
XCFLAGS.verify_file += -DSOPEN_MAX=64
.include <bsd.prog.mk>

View File

@ -0,0 +1,12 @@
# $FreeBSD$
# Autogenerated - do NOT edit!
DIRDEPS = \
lib/libbearssl \
.include <dirdeps.mk>
.if ${DEP_RELDIR} == ${_DEP_RELDIR}
# local dependencies - needed for -jN in clean tree
.endif

View File

@ -0,0 +1,181 @@
/*
* 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 "../libsecureboot-priv.h"
#include <unistd.h>
#include <err.h>
#include <verify_file.h>
char *Skip;
int
main(int argc, char *argv[])
{
int n;
int fd;
int c;
int Vflag;
char *cp;
char *prefix;
prefix = NULL;
Skip = NULL;
n = ve_trust_init();
printf("Trust %d\n", n);
Vflag = 0;
while ((c = getopt(argc, argv, "dp:s:T:V")) != -1) {
switch (c) {
case 'd':
DebugVe++;
break;
case 'p':
prefix = optarg;
break;
case 's':
Skip = optarg;
break;
case 'T':
n = ve_trust_add(optarg);
printf("Local trust %s: %d\n", optarg, n);
break;
case 'V':
Vflag = 1;
break;
default:
errx(1, "unknown option: -%c", c);
break;
}
}
ve_self_tests();
for ( ; optind < argc; optind++) {
if (Vflag) {
/*
* Simulate what loader does.
* verify_file should "just work"
*/
fd = open(argv[optind], O_RDONLY);
if (fd > 0) {
/*
* See if verify_file is happy
*/
int x;
x = verify_file(fd, argv[optind], 0, VE_GUESS);
printf("verify_file(%s) = %d\n", argv[optind], x);
close(fd);
}
continue;
}
#ifdef VE_OPENPGP_SUPPORT
if (strstr(argv[optind], "asc")) {
cp = (char *)verify_asc(argv[optind], 1);
if (cp) {
printf("Verified: %s: %.28s...\n",
argv[optind], cp);
fingerprint_info_add(argv[optind],
prefix, Skip, cp, NULL);
} else {
fprintf(stderr, "%s: %s\n",
argv[optind], ve_error_get());
}
} else
#endif
if (strstr(argv[optind], "sig")) {
cp = (char *)verify_sig(argv[optind], 1);
if (cp) {
printf("Verified: %s: %.28s...\n",
argv[optind], cp);
fingerprint_info_add(argv[optind],
prefix, Skip, cp, NULL);
} else {
fprintf(stderr, "%s: %s\n",
argv[optind], ve_error_get());
}
} else if (strstr(argv[optind], "manifest")) {
cp = (char *)read_file(argv[optind], NULL);
if (cp) {
fingerprint_info_add(argv[optind],
prefix, Skip, cp, NULL);
}
} else {
fd = verify_open(argv[optind], O_RDONLY);
printf("verify_open(%s) = %d %s\n", argv[optind], fd,
(fd < 0) ? ve_error_get() : "");
if (fd > 0) {
/*
* Check that vectx_* can also verify the file.
*/
void *vp;
char buf[BUFSIZ];
struct stat st;
int error;
size_t off, n;
fstat(fd, &st);
lseek(fd, 0, SEEK_SET);
off = st.st_size % 512;
vp = vectx_open(fd, argv[optind], off,
&st, &error);
if (!vp) {
printf("vectx_open(%s) failed: %d %s\n",
argv[optind], error,
ve_error_get());
} else {
off = vectx_lseek(vp,
(st.st_size % 1024), SEEK_SET);
if (off < st.st_size) {
n = vectx_read(vp, buf,
sizeof(buf));
if (n > 0)
off += n;
}
off = vectx_lseek(vp, 0, SEEK_END);
/* repeating that should be harmless */
off = vectx_lseek(vp, 0, SEEK_END);
error = vectx_close(vp);
if (error) {
printf("vectx_close(%s) == %d %s\n",
argv[optind], error,
ve_error_get());
} else {
printf("vectx_close: Verified: %s\n",
argv[optind]);
}
}
close(fd);
}
}
}
return (0);
}

291
lib/libsecureboot/vectx.c Normal file
View File

@ -0,0 +1,291 @@
/*-
* Copyright (c) 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$");
#ifndef _STANDALONE
/* Avoid unwanted userlandish components */
#define _KERNEL
#include <sys/errno.h>
#undef _KERNEL
#endif
#include "libsecureboot-priv.h"
/**
* @file vectx.c
* @brief api to verify file while reading
*
* This API allows the hash of a file to be computed as it is read.
* Key to this is seeking by reading.
*
* On close an indication of the verification result is returned.
*/
struct vectx {
br_hash_compat_context vec_ctx; /* hash ctx */
const br_hash_class *vec_md; /* hash method */
const char *vec_path; /* path we are verifying */
const char *vec_want; /* hash value we want */
off_t vec_off; /* current offset */
size_t vec_size; /* size of path */
size_t vec_hashsz; /* size of hash */
int vec_fd; /* file descriptor */
int vec_status; /* verification status */
};
/**
* @brief
* verify an open file as we read it
*
* If the file has no fingerprint to match, we will still return a
* verification context containing little more than the file
* descriptor, and an error code in @c error.
*
* @param[in] fd
* open descriptor
*
* @param[in] path
* pathname to open
*
* @param[in] off
* current offset
*
* @param[in] stp
* pointer to struct stat
*
* @param[out] error
* @li 0 all is good
* @li ENOMEM out of memory
* @li VE_FINGERPRINT_NONE no entry found
* @li VE_FINGERPRINT_UNKNOWN no fingerprint in entry
*
* @return ctx or NULL on error.
* NULL is only returned for non-files or out-of-memory.
*/
struct vectx *
vectx_open(int fd, const char *path, off_t off, struct stat *stp, int *error)
{
struct vectx *ctx;
struct stat st;
size_t hashsz;
char *cp;
if (!stp) {
if (fstat(fd, &st) == 0)
stp = &st;
}
/* we *should* only get called for files */
if (stp && !S_ISREG(stp->st_mode)) {
*error = 0;
return (NULL);
}
ctx = malloc(sizeof(struct vectx));
if (!ctx)
goto enomem;
ctx->vec_fd = fd;
ctx->vec_path = path;
ctx->vec_size = stp->st_size;
ctx->vec_off = 0;
ctx->vec_want = NULL;
ctx->vec_status = 0;
hashsz = 0;
cp = fingerprint_info_lookup(fd, path);
if (!cp) {
ctx->vec_status = VE_FINGERPRINT_NONE;
ve_error_set("%s: no entry", path);
} else {
if (strncmp(cp, "sha256=", 7) == 0) {
ctx->vec_md = &br_sha256_vtable;
hashsz = br_sha256_SIZE;
cp += 7;
#ifdef VE_SHA1_SUPPORT
} else if (strncmp(cp, "sha1=", 5) == 0) {
ctx->vec_md = &br_sha1_vtable;
hashsz = br_sha1_SIZE;
cp += 5;
#endif
#ifdef VE_SHA384_SUPPORT
} else if (strncmp(cp, "sha384=", 7) == 0) {
ctx->vec_md = &br_sha384_vtable;
hashsz = br_sha384_SIZE;
cp += 7;
#endif
#ifdef VE_SHA512_SUPPORT
} else if (strncmp(cp, "sha512=", 7) == 0) {
ctx->vec_md = &br_sha512_vtable;
hashsz = br_sha512_SIZE;
cp += 7;
#endif
} else {
ctx->vec_status = VE_FINGERPRINT_UNKNOWN;
ve_error_set("%s: no supported fingerprint", path);
}
}
*error = ctx->vec_status;
ctx->vec_hashsz = hashsz;
ctx->vec_want = cp;
ctx->vec_md->init(&ctx->vec_ctx.vtable);
if (hashsz > 0 && off > 0) {
lseek(fd, 0, SEEK_SET);
vectx_lseek(ctx, off, SEEK_SET);
}
return (ctx);
enomem: /* unlikely */
*error = ENOMEM;
free(ctx);
return (NULL);
}
/**
* @brief
* read bytes from file and update hash
*
* It is critical that all file I/O comes through here.
* We keep track of current offset.
*
* @param[in] pctx
* pointer to ctx
*
* @param[in] buf
*
* @param[in] nbytes
*
* @return bytes read or error.
*/
ssize_t
vectx_read(struct vectx *ctx, void *buf, size_t nbytes)
{
unsigned char *bp = buf;
int n;
size_t off;
if (ctx->vec_hashsz == 0) /* nothing to do */
return (read(ctx->vec_fd, buf, nbytes));
off = 0;
do {
n = read(ctx->vec_fd, &bp[off], nbytes - off);
if (n < 0)
return (n);
if (n > 0) {
ctx->vec_md->update(&ctx->vec_ctx.vtable, &bp[off], n);
off += n;
ctx->vec_off += n;
}
} while (n > 0 && off < nbytes);
return (off);
}
/**
* @brief
* vectx equivalent of lseek
*
* We do not actually, seek, but call vectx_read
* to reach the desired offset.
*
* We do not support seeking backwards.
*
* @param[in] pctx
* pointer to ctx
*
* @param[in] off
* desired offset
*
* @param[in] whence
*
* @return offset or error.
*/
off_t
vectx_lseek(struct vectx *ctx, off_t off, int whence)
{
unsigned char buf[PAGE_SIZE];
size_t delta;
ssize_t n;
if (ctx->vec_hashsz == 0) /* nothing to do */
return (lseek(ctx->vec_fd, off, whence));
/*
* Try to convert whence to SEEK_SET
* but we cannot support seeking backwards!
* Nor beyond end of file.
*/
if (whence == SEEK_END && off <= 0) {
whence = SEEK_SET;
off += ctx->vec_size;
} else if (whence == SEEK_CUR && off >= 0) {
whence = SEEK_SET;
off += ctx->vec_off;
}
if (whence != SEEK_SET || off < ctx->vec_off ||
(size_t)off > ctx->vec_size) {
printf("ERROR: %s: unsupported operation\n", __func__);
return (-1);
}
n = 0;
do {
delta = off - ctx->vec_off;
if (delta > 0) {
delta = MIN(PAGE_SIZE, delta);
n = vectx_read(ctx, buf, delta);
if (n < 0)
return (n);
}
} while (ctx->vec_off < off && n > 0);
return (ctx->vec_off);
}
/**
* @brief
* check that hashes match and cleanup
*
* We have finished reading file, compare the hash with what
* we wanted.
*
* @param[in] pctx
* pointer to ctx
*
* @return 0 or an error.
*/
int
vectx_close(struct vectx *ctx)
{
int rc;
if (ctx->vec_hashsz == 0) {
rc = ctx->vec_status;
} else {
rc = ve_check_hash(&ctx->vec_ctx, ctx->vec_md,
ctx->vec_path, ctx->vec_want, ctx->vec_hashsz);
}
free(ctx);
return ((rc < 0) ? rc : 0);
}

458
lib/libsecureboot/veopen.c Normal file
View File

@ -0,0 +1,458 @@
/*-
* 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 "libsecureboot-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;
int n;
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;
}
}
/* collapse any trailing ..[/] */
n = 0;
while ((cp = strrchr(nfip->fi_prefix, '/')) != NULL) {
if (cp[1] == '\0') { /* trailing "/" */
*cp = '\0';
continue;
}
if (strcmp(&cp[1], "..") == 0) {
n++;
*cp = '\0';
continue;
}
if (n > 0) {
n--;
*cp = '\0';
}
if (n == 0)
break;
}
#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));
}
#ifdef MANIFEST_SKIP_MAYBE
/*
* Deal with old incompatible boot/manifest
* if fp[-1] is '/' and start of entry matches
* MANIFEST_SKIP_MAYBE, we want it.
*/
static char *
maybe_skip(char *fp, struct fingerprint_info *fip, size_t *nplenp)
{
char *tp;
tp = fp - sizeof(MANIFEST_SKIP_MAYBE);
if (tp >= fip->fi_data) {
DEBUG_PRINTF(3, ("maybe: %.48s\n", tp));
if ((tp == fip->fi_data || tp[-1] == '\n') &&
strncmp(tp, MANIFEST_SKIP_MAYBE,
sizeof(MANIFEST_SKIP_MAYBE) - 1) == 0) {
fp = tp;
*nplenp += sizeof(MANIFEST_SKIP_MAYBE);
}
}
return (fp);
}
#endif
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;
#ifdef MANIFEST_SKIP_MAYBE
if (fip->fi_skip_len == 0 &&
fp > fip->fi_data && fp[-1] == '/') {
fp = maybe_skip(fp, fip, &nplen);
}
#endif
/*
* 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) {
#ifdef MANIFEST_SKIP_MAYBE
if (fip->fi_skip_len == 0 &&
fp > fip->fi_data &&
fp[-1] == '/') {
fp = maybe_skip(fp, fip, &nplen);
}
#endif
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);
}

84
lib/libsecureboot/vepcr.c Normal file
View File

@ -0,0 +1,84 @@
/*-
* Copyright (c) 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 "libsecureboot-priv.h"
/*
* To support measured boot without putting a ton
* of extra code in the loader, we just maintain
* a hash of all the hashes we (attempt to) verify.
* The loader can export this for kernel or rc script
* to feed to a TPM pcr register - hence the name ve_pcr.
*
* NOTE: in the current standard the TPM pcr register size is for SHA1,
* the fact that we provide a SHA256 hash should not matter
* as long as we are consistent - it can be truncated or hashed
* before feeding to TPM.
*/
static const br_hash_class *pcr_md = NULL;
static br_hash_compat_context pcr_ctx;
static size_t pcr_hlen = 0;
/**
* @brief initialize pcr context
*
* Real TPM registers only hold a SHA1 hash
* but we use SHA256
*/
void
ve_pcr_init(void)
{
pcr_hlen = br_sha256_SIZE;
pcr_md = &br_sha256_vtable;
pcr_md->init(&pcr_ctx.vtable);
}
/**
* @brief update pcr context
*/
void
ve_pcr_update(unsigned char *data, size_t dlen)
{
if (pcr_md)
pcr_md->update(&pcr_ctx.vtable, data, dlen);
}
/**
* @brief get pcr result
*/
ssize_t
ve_pcr_get(unsigned char *buf, size_t sz)
{
if (!pcr_md)
return (-1);
if (sz < pcr_hlen)
return (-1);
pcr_md->out(&pcr_ctx.vtable, buf);
return (pcr_hlen);
}

View File

@ -0,0 +1,421 @@
/*-
* 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.
*/
/*
* Routines to verify files loaded.
*/
#include <sys/cdefs.h>
__FBSDID("$FreeBSD$");
#include <sys/param.h>
#include <string.h>
#include <sys/queue.h>
#include "libsecureboot.h"
#include <verify_file.h>
#include <manifests.h>
#define VE_NOT_CHECKED -42
#ifdef UNIT_TEST
# include <err.h>
# define panic warn
/*
* define MANIFEST_SKIP to Skip - in tests/tvo.c so that
* tvo can control the value we use in find_manifest()
*/
extern char *Skip;
# undef MANIFEST_SKIP
# define MANIFEST_SKIP Skip
# undef VE_DEBUG_LEVEL
#endif
/*
* We sometimes need to know if input is verified or not.
* The extra slot is for tracking most recently opened.
*/
static int ve_status[SOPEN_MAX+1];
static int ve_status_state;
struct verify_status;
struct verify_status *verified_files = NULL;
static int loaded_manifests = 0; /* have we loaded anything? */
#define VE_STATUS_NONE 1
#define VE_STATUS_VALID 2
/**
* @brief set ve status for fd
*/
static void
ve_status_set(int fd, int ves)
{
if (fd >= 0 && fd < SOPEN_MAX) {
ve_status[fd] = ves;
ve_status_state = VE_STATUS_VALID;
}
ve_status[SOPEN_MAX] = ves;
}
/**
* @brief get ve status of fd
*
* What we return depends on ve_status_state.
*
* @return
* @li ve_status[fd] if ve_status_state is valid
* @li ve_status[SOPEN_MAX] if ve_status_state is none
* @li VE_NOT_CHECKED if ve_status_state uninitialized
*/
int
ve_status_get(int fd)
{
if (!ve_status_state) {
return (VE_NOT_CHECKED);
}
if (ve_status_state == VE_STATUS_VALID &&
fd >= 0 && fd < SOPEN_MAX)
return (ve_status[fd]);
return (ve_status[SOPEN_MAX]); /* most recent */
}
/**
* @brief track verify status
*
* occasionally loader will make multiple calls
* for the same file, we need only check it once.
*/
struct verify_status {
dev_t vs_dev;
ino_t vs_ino;
int vs_status;
struct verify_status *vs_next;
};
static int
is_verified(struct stat *stp)
{
struct verify_status *vsp;
for (vsp = verified_files; vsp != NULL; vsp = vsp->vs_next) {
if (stp->st_dev == vsp->vs_dev &&
stp->st_ino == vsp->vs_ino)
return (vsp->vs_status);
}
return (VE_NOT_CHECKED);
}
/* most recent first, since most likely to see repeated calls. */
static void
add_verify_status(struct stat *stp, int status)
{
struct verify_status *vsp;
vsp = malloc(sizeof(struct verify_status));
vsp->vs_next = verified_files;
vsp->vs_dev = stp->st_dev;
vsp->vs_ino = stp->st_ino;
vsp->vs_status = status;
verified_files = vsp;
}
/**
* @brief
* load specified manifest if verified
*/
int
load_manifest(const char *name, const char *prefix,
const char *skip, struct stat *stp)
{
struct stat st;
size_t n;
int rc;
char *content;
rc = VE_FINGERPRINT_NONE;
n = strlen(name);
if (n > 4) {
if (!stp) {
stp = &st;
if (stat(name, &st) < 0 || !S_ISREG(st.st_mode))
return (rc);
}
rc = is_verified(stp);
if (rc != VE_NOT_CHECKED) {
return (rc);
}
/* loader has no sense of time */
ve_utc_set(stp->st_mtime);
content = (char *)verify_signed(name, VEF_VERBOSE);
if (content) {
fingerprint_info_add(name, prefix, skip, content, stp);
add_verify_status(stp, VE_VERIFIED);
loaded_manifests = 1; /* we are verifying! */
DEBUG_PRINTF(3, ("loaded: %s %s %s\n",
name, prefix, skip));
rc = 0;
} else {
rc = VE_FINGERPRINT_WRONG;
add_verify_status(stp, rc); /* remember */
}
}
return (rc);
}
static int
find_manifest(const char *name)
{
struct stat st;
char buf[MAXPATHLEN];
char *prefix;
char *skip;
const char **tp;
int rc;
strncpy(buf, name, MAXPATHLEN - 1);
if (!(prefix = strrchr(buf, '/')))
return (-1);
*prefix = '\0';
prefix = strdup(buf);
rc = VE_FINGERPRINT_NONE;
for (tp = manifest_names; *tp; tp++) {
snprintf(buf, sizeof(buf), "%s/%s", prefix, *tp);
DEBUG_PRINTF(5, ("looking for %s\n", buf));
if (stat(buf, &st) == 0 && st.st_size > 0) {
#ifdef MANIFEST_SKIP_ALWAYS /* very unlikely */
skip = MANIFEST_SKIP_ALWAYS;
#else
#ifdef MANIFEST_SKIP /* rare */
if (*tp[0] == '.') {
skip = MANIFEST_SKIP;
} else
#endif
skip = NULL;
#endif
rc = load_manifest(buf, skip ? prefix : NULL,
skip, &st);
break;
}
}
free(prefix);
return (rc);
}
#ifdef LOADER_VERIEXEC_TESTING
# define ACCEPT_NO_FP_DEFAULT VE_MUST + 1
#else
# define ACCEPT_NO_FP_DEFAULT VE_MUST
#endif
#ifndef VE_VERBOSE_DEFAULT
# define VE_VERBOSE_DEFAULT 0
#endif
static int
severity_guess(const char *filename)
{
const char *cp;
/* Some files like *.conf and *.hints may be unsigned */
if ((cp = strrchr(filename, '.'))) {
if (strcmp(cp, ".conf") == 0 ||
strcmp(cp, ".cookie") == 0 ||
strcmp(cp, ".hints") == 0)
return (VE_TRY);
}
return (VE_WANT);
}
static void
verify_tweak(char *tweak, int *accept_no_fp, int *verbose, int *verifying)
{
if (strcmp(tweak, "off") == 0) {
*verifying = 0;
} else if (strcmp(tweak, "strict") == 0) {
/* anything caller wants verified must be */
*accept_no_fp = VE_WANT;
*verbose = 1; /* warn of anything unverified */
/* treat self test failure as fatal */
if (!ve_self_tests()) {
panic("verify self tests failed");
}
} else if (strcmp(tweak, "modules") == 0) {
/* modules/kernel must be verified */
*accept_no_fp = VE_MUST;
} else if (strcmp(tweak, "try") == 0) {
/* best effort: always accept no fp */
*accept_no_fp = VE_MUST + 1;
} else if (strcmp(tweak, "verbose") == 0) {
*verbose = 1;
} else if (strcmp(tweak, "quiet") == 0) {
*verbose = 0;
}
}
/**
* @brief verify an open file
*
* @param[in] fd
* open descriptor
*
* @param[in] filename
* path we opened and will use to lookup fingerprint
*
* @param[in] off
* current offset in fd, must be restored on return
*
* @param[in] severity
* indicator of how to handle case of missing fingerprint
*
* We look for a signed manifest relative to the filename
* just opened and verify/load it if needed.
*
* We then use verify_fd() in libve to actually verify that hash for
* open file. If it returns < 0 we look at the severity arg to decide
* what to do about it.
*
* If verify_fd() returns VE_FINGERPRINT_NONE we accept it if severity
* is < accept_no_fp.
*
* @return >= 0 on success < 0 on failure
*/
int
verify_file(int fd, const char *filename, off_t off, int severity)
{
static int verifying = -1;
static int accept_no_fp = ACCEPT_NO_FP_DEFAULT;
static int verbose = VE_VERBOSE_DEFAULT;
struct stat st;
char *cp;
int rc;
if (verifying < 0) {
verifying = ve_trust_init();
#ifdef VE_DEBUG_LEVEL
ve_debug_set(VE_DEBUG_LEVEL);
#endif
/* initialize ve_status with default result */
rc = verifying ? VE_NOT_CHECKED : VE_NOT_VERIFYING;
ve_status_set(0, rc);
ve_status_state = VE_STATUS_NONE;
if (verifying)
ve_self_tests();
}
if (!verifying)
return (0);
if (fd < 0 || fstat(fd, &st) < 0 || !S_ISREG(st.st_mode))
return (0);
DEBUG_PRINTF(3, ("fd=%d,name='%s',off=%lld,dev=%lld,ino=%lld\n",
fd, filename, (long long)off, (long long)st.st_dev,
(long long)st.st_ino));
rc = is_verified(&st);
if (rc != VE_NOT_CHECKED) {
ve_status_set(fd, rc);
return (rc);
}
rc = find_manifest(filename);
if (rc != VE_FINGERPRINT_WRONG && loaded_manifests) {
if (severity <= VE_GUESS)
severity = severity_guess(filename);
if ((rc = verify_fd(fd, filename, off, &st)) >= 0) {
if (verbose || severity > VE_WANT) {
#if defined(VE_DEBUG_LEVEL) && VE_DEBUG_LEVEL > 0
printf("Verified %s %llu,%llu\n", filename,
st.st_dev, st.st_ino);
#else
printf("Verified %s\n", filename);
#endif
}
if (severity < VE_MUST) { /* not a kernel or module */
if ((cp = strrchr(filename, '/'))) {
cp++;
if (strncmp(cp, "loader.ve.", 10) == 0) {
cp += 10;
verify_tweak(cp,
&accept_no_fp, &verbose,
&verifying);
}
}
}
add_verify_status(&st, rc);
ve_status_set(fd, rc);
return (rc);
}
if (severity || verbose)
printf("Unverified: %s\n", ve_error_get());
if (rc == VE_FINGERPRINT_UNKNOWN && severity < VE_MUST)
rc = VE_UNVERIFIED_OK;
else if (rc == VE_FINGERPRINT_NONE && severity < accept_no_fp)
rc = VE_UNVERIFIED_OK;
add_verify_status(&st, rc);
}
#ifdef LOADER_VERIEXEC_TESTING
else if (rc != VE_FINGERPRINT_WRONG) {
/*
* We have not loaded any manifest and
* not because of verication failure.
* Most likely reason is we have none.
* Allow boot to proceed if we are just testing.
*/
return (VE_UNVERIFIED_OK);
}
#endif
if (rc == VE_FINGERPRINT_WRONG && severity > accept_no_fp)
panic("cannot continue");
ve_status_set(fd, rc);
return (rc);
}
/**
* @brief get hex string for pcr value and export
*
* In case we are doing measured boot, provide
* value of the "pcr" data we have accumulated.
*/
void
verify_pcr_export(void)
{
#ifdef VE_PCR_SUPPORT
char hexbuf[br_sha256_SIZE * 2 + 2];
unsigned char hbuf[br_sha256_SIZE];
char *hex;
ssize_t hlen;
hlen = ve_pcr_get(hbuf, sizeof(hbuf));
if (hlen > 0) {
hex = hexdigest(hexbuf, sizeof(hexbuf), hbuf, hlen);
if (hex) {
hex[hlen*2] = '\0'; /* clobber newline */
setenv("loader.ve.pcr", hex, 1);
}
}
#endif
}

View File

@ -0,0 +1,61 @@
/*-
* Copyright (c) 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 <libsecureboot.h>
#include <vse.h>
/**
* @brief
* verify signed file
*
* We look for a signature using the extensions
* recorded in signature_exts.
* If we find a match we pass it to a suitable verify method.
*
* @return content of verified file or NULL on error.
*/
unsigned char *
verify_signed(const char *filename, int flags)
{
struct stat st;
char buf[MAXPATHLEN];
const char **se;
for (se = signature_exts; *se; se++) {
snprintf(buf, sizeof(buf), "%s.%s", filename, *se);
if (stat(buf, &st) < 0 || !S_ISREG(st.st_mode))
continue;
DEBUG_PRINTF(5, ("verify_signed: %s\n", buf));
#ifdef VE_OPENPGP_SUPPORT
if (strncmp(*se, "asc", 3) == 0)
return (verify_asc(buf, flags));
#endif
return (verify_sig(buf, flags));
}
return (NULL);
}

111
lib/libsecureboot/veta.c Normal file
View File

@ -0,0 +1,111 @@
/*-
* Copyright (c) 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$");
/**
* @file veta.c - add to trust anchors
*
*/
#define NEED_BRSSL_H
#include "libsecureboot-priv.h"
#include <brssl.h>
#include <sys/stat.h>
#include <dirent.h>
#ifdef VE_OPENPGP_SUPPORT
#include "openpgp/packet.h"
#endif
/**
* @brief add trust anchors from a file
*
* The file might contain X.509 certs
* or OpenPGP public key
*/
static size_t
trust_file_add(const char *trust)
{
br_x509_certificate *xcs;
size_t num;
xcs = read_certificates(trust, &num);
if (xcs) {
num = ve_trust_anchors_add(xcs, num);
}
#ifdef VE_OPENPGP_SUPPORT
else if (load_key_file(trust)) {
num = 1;
}
#endif
return (num);
}
/**
* @brief add trust anchors from a directory
*
* Pass each file in directory to trust_file_add
*/
static size_t
trust_dir_add(const char *trust)
{
char fbuf[MAXPATHLEN];
DIR *dh;
struct dirent *de;
struct stat st;
ssize_t sz;
size_t num;
if (!(dh = opendir(trust)))
return (0);
for (num = 0, de = readdir(dh); de; de = readdir(dh)) {
if (de->d_name[0] == '.')
continue;
sz = snprintf(fbuf, sizeof(fbuf), "%s/%s", trust, de->d_name);
if (sz >= (ssize_t)sizeof(fbuf))
continue;
if (stat(fbuf, &st) < 0 || S_ISDIR(st.st_mode))
continue;
num += trust_file_add(fbuf);
}
closedir(dh);
return (num);
}
/**
* @brief add trust anchors
*/
int
ve_trust_add(const char *trust)
{
struct stat st;
if (stat(trust, &st) < 0)
return (-1);
if (S_ISDIR(st.st_mode))
return (trust_dir_add(trust));
return (trust_file_add(trust));
}

700
lib/libsecureboot/vets.c Normal file
View File

@ -0,0 +1,700 @@
/*-
* 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$");
/**
* @file vets.c - trust store
* @brief verify signatures
*
* We leverage code from BearSSL www.bearssl.org
*/
#include <sys/time.h>
#include <stdarg.h>
#define NEED_BRSSL_H
#include "libsecureboot-priv.h"
#include <brssl.h>
#include <ta.h>
#ifndef TRUST_ANCHOR_STR
# define TRUST_ANCHOR_STR ta_PEM
#endif
#define SECONDS_PER_DAY 86400
#define X509_DAYS_TO_UTC0 719528
int DebugVe = 0;
typedef VECTOR(br_x509_certificate) cert_list;
static anchor_list trust_anchors = VEC_INIT;
void
ve_debug_set(int n)
{
DebugVe = n;
}
static char ebuf[512];
char *
ve_error_get(void)
{
return (ebuf);
}
int
ve_error_set(const char *fmt, ...)
{
int rc;
va_list ap;
va_start(ap, fmt);
ebuf[0] = '\0';
rc = 0;
if (fmt) {
#ifdef STAND_H
vsprintf(ebuf, fmt, ap); /* no vsnprintf in libstand */
ebuf[sizeof(ebuf) - 1] = '\0';
rc = strlen(ebuf);
#else
rc = vsnprintf(ebuf, sizeof(ebuf), fmt, ap);
#endif
}
va_end(ap);
return (rc);
}
/* this is the time we use for verifying certs */
static time_t ve_utc = 0;
/**
* @brief
* set ve_utc used for certificate verification
*
* @param[in] utc
* time - ignored unless greater than current value.
*/
void
ve_utc_set(time_t utc)
{
if (utc > ve_utc) {
DEBUG_PRINTF(2, ("Set ve_utc=%jd\n", (intmax_t)utc));
ve_utc = utc;
}
}
static void
free_cert_contents(br_x509_certificate *xc)
{
xfree(xc->data);
}
/**
* @brief
* add certs to our trust store
*/
size_t
ve_trust_anchors_add(br_x509_certificate *xcs, size_t num)
{
br_x509_trust_anchor ta;
size_t u;
for (u = 0; u < num; u++) {
if (certificate_to_trust_anchor_inner(&ta, &xcs[u]) < 0) {
break;
}
VEC_ADD(trust_anchors, ta);
}
return (u);
}
/**
* @brief
* initialize our trust_anchors from ta_PEM
*/
int
ve_trust_init(void)
{
br_x509_certificate *xcs;
static int once = -1;
size_t num;
if (once >= 0)
return (once);
once = 0;
ve_utc_set(time(NULL));
#ifdef BUILD_UTC
ve_utc_set(BUILD_UTC); /* just in case */
#endif
ve_error_set(NULL); /* make sure it is empty */
#ifdef VE_PCR_SUPPORT
ve_pcr_init();
#endif
#ifdef TRUST_ANCHOR_STR
xcs = parse_certificates(__DECONST(unsigned char *, TRUST_ANCHOR_STR),
sizeof(TRUST_ANCHOR_STR), &num);
if (xcs == NULL)
return (0);
num = ve_trust_anchors_add(xcs, num);
once = (int) num;
#else
num = 0;
#endif
return (num);
}
/**
* if we can verify the certificate chain in "certs",
* return the public key and if "xcp" is !NULL the associated
* certificate
*/
static br_x509_pkey *
verify_signer_xcs(br_x509_certificate *xcs,
size_t num,
br_name_element *elts, size_t num_elts)
{
br_x509_minimal_context mc;
br_x509_certificate *xc;
size_t u;
cert_list chain = VEC_INIT;
const br_x509_pkey *tpk;
br_x509_pkey *pk;
unsigned int usages;
int err;
DEBUG_PRINTF(5, ("verify_signer: %zu certs in chain\n", num));
VEC_ADDMANY(chain, xcs, num);
if (VEC_LEN(chain) == 0) {
ve_error_set("ERROR: no/invalid certificate chain\n");
return (NULL);
}
DEBUG_PRINTF(5, ("verify_signer: %zu trust anchors\n",
VEC_LEN(trust_anchors)));
br_x509_minimal_init(&mc, &br_sha256_vtable,
&VEC_ELT(trust_anchors, 0),
VEC_LEN(trust_anchors));
#ifdef VE_ECDSA_SUPPORT
br_x509_minimal_set_ecdsa(&mc,
&br_ec_prime_i31, &br_ecdsa_i31_vrfy_asn1);
#endif
#ifdef VE_RSA_SUPPORT
br_x509_minimal_set_rsa(&mc, &br_rsa_i31_pkcs1_vrfy);
#endif
#if defined(UNIT_TEST) && defined(VE_DEPRECATED_RSA_SHA1_SUPPORT)
/* This is deprecated! do not enable unless you absoultely have to */
br_x509_minimal_set_hash(&mc, br_sha1_ID, &br_sha1_vtable);
#endif
br_x509_minimal_set_hash(&mc, br_sha256_ID, &br_sha256_vtable);
#ifdef VE_SHA384_SUPPORT
br_x509_minimal_set_hash(&mc, br_sha384_ID, &br_sha384_vtable);
#endif
#ifdef VE_SHA512_SUPPORT
br_x509_minimal_set_hash(&mc, br_sha512_ID, &br_sha512_vtable);
#endif
br_x509_minimal_set_name_elements(&mc, elts, num_elts);
#ifdef _STANDALONE
/*
* Clock is probably bogus so we use ve_utc.
*/
mc.days = (ve_utc / SECONDS_PER_DAY) + X509_DAYS_TO_UTC0;
mc.seconds = (ve_utc % SECONDS_PER_DAY);
#endif
mc.vtable->start_chain(&mc.vtable, NULL);
for (u = 0; u < VEC_LEN(chain); u ++) {
xc = &VEC_ELT(chain, u);
mc.vtable->start_cert(&mc.vtable, xc->data_len);
mc.vtable->append(&mc.vtable, xc->data, xc->data_len);
mc.vtable->end_cert(&mc.vtable);
switch (mc.err) {
case 0:
case BR_ERR_X509_OK:
case BR_ERR_X509_EXPIRED:
break;
default:
printf("u=%zu mc.err=%d\n", u, mc.err);
break;
}
}
err = mc.vtable->end_chain(&mc.vtable);
pk = NULL;
if (err) {
ve_error_set("Validation failed, err = %d", err);
} else {
tpk = mc.vtable->get_pkey(&mc.vtable, &usages);
if (tpk != NULL) {
pk = xpkeydup(tpk);
}
}
VEC_CLEAREXT(chain, &free_cert_contents);
return (pk);
}
static br_x509_pkey *
verify_signer(const char *certs,
br_name_element *elts, size_t num_elts)
{
br_x509_certificate *xcs;
br_x509_pkey *pk;
size_t num;
ve_trust_init();
xcs = read_certificates(certs, &num);
if (xcs == NULL) {
ve_error_set("cannot read certificates\n");
return (NULL);
}
pk = verify_signer_xcs(xcs, num, elts, num_elts);
xfree(xcs);
return (pk);
}
/**
* we need a hex digest including trailing newline below
*/
char *
hexdigest(char *buf, size_t bufsz, unsigned char *foo, size_t foo_len)
{
char const hex2ascii[] = "0123456789abcdef";
size_t i;
/* every binary byte is 2 chars in hex + newline + null */
if (bufsz < (2 * foo_len) + 2)
return (NULL);
for (i = 0; i < foo_len; i++) {
buf[i * 2] = hex2ascii[foo[i] >> 4];
buf[i * 2 + 1] = hex2ascii[foo[i] & 0x0f];
}
buf[i * 2] = 0x0A; /* we also want a newline */
buf[i * 2 + 1] = '\0';
return (buf);
}
/**
* @brief
* verify file against sigfile using pk
*
* When we generated the signature in sigfile,
* we hashed (sha256) file, and sent that to signing server
* which hashed (sha256) that hash.
*
* To verify we need to replicate that result.
*
* @param[in] pk
* br_x509_pkey
*
* @paramp[in] file
* file to be verified
*
* @param[in] sigfile
* signature (PEM encoded)
*
* @return NULL on error, otherwise content of file.
*/
#ifdef VE_ECDSA_SUPPORT
static unsigned char *
verify_ec(br_x509_pkey *pk, const char *file, const char *sigfile)
{
char hexbuf[br_sha512_SIZE * 2 + 2];
unsigned char rhbuf[br_sha512_SIZE];
char *hex;
br_sha256_context ctx;
unsigned char *fcp, *scp;
size_t flen, slen, plen;
pem_object *po;
const br_ec_impl *ec;
br_ecdsa_vrfy vrfy;
if ((fcp = read_file(file, &flen)) == NULL)
return (NULL);
if ((scp = read_file(sigfile, &slen)) == NULL) {
free(fcp);
return (NULL);
}
if ((po = decode_pem(scp, slen, &plen)) == NULL) {
free(fcp);
free(scp);
return (NULL);
}
br_sha256_init(&ctx);
br_sha256_update(&ctx, fcp, flen);
br_sha256_out(&ctx, rhbuf);
hex = hexdigest(hexbuf, sizeof(hexbuf), rhbuf, br_sha256_SIZE);
/* now hash that */
if (hex) {
br_sha256_init(&ctx);
br_sha256_update(&ctx, hex, strlen(hex));
br_sha256_out(&ctx, rhbuf);
}
ec = br_ec_get_default();
vrfy = br_ecdsa_vrfy_asn1_get_default();
if (!vrfy(ec, rhbuf, br_sha256_SIZE, &pk->key.ec, po->data,
po->data_len)) {
free(fcp);
fcp = NULL;
}
free(scp);
return (fcp);
}
#endif
#if defined(VE_RSA_SUPPORT) || defined(VE_OPENPGP_SUPPORT)
/**
* @brief verify an rsa digest
*
* @return 0 on failure
*/
int
verify_rsa_digest (br_rsa_public_key *pkey,
const unsigned char *hash_oid,
unsigned char *mdata, size_t mlen,
unsigned char *sdata, size_t slen)
{
br_rsa_pkcs1_vrfy vrfy;
unsigned char vhbuf[br_sha512_SIZE];
vrfy = br_rsa_pkcs1_vrfy_get_default();
if (!vrfy(sdata, slen, hash_oid, mlen, pkey, vhbuf) ||
memcmp(vhbuf, mdata, mlen) != 0) {
return (0); /* fail */
}
return (1); /* ok */
}
#endif
/**
* @brief
* verify file against sigfile using pk
*
* When we generated the signature in sigfile,
* we hashed (sha256) file, and sent that to signing server
* which hashed (sha256) that hash.
*
* Or (deprecated) we simply used sha1 hash directly.
*
* To verify we need to replicate that result.
*
* @param[in] pk
* br_x509_pkey
*
* @paramp[in] file
* file to be verified
*
* @param[in] sigfile
* signature (PEM encoded)
*
* @return NULL on error, otherwise content of file.
*/
#ifdef VE_RSA_SUPPORT
static unsigned char *
verify_rsa(br_x509_pkey *pk, const char *file, const char *sigfile)
{
unsigned char rhbuf[br_sha512_SIZE];
const unsigned char *hash_oid;
const br_hash_class *md;
br_hash_compat_context mctx;
unsigned char *fcp, *scp;
size_t flen, slen, plen, hlen;
pem_object *po;
if ((fcp = read_file(file, &flen)) == NULL)
return (NULL);
if ((scp = read_file(sigfile, &slen)) == NULL) {
free(fcp);
return (NULL);
}
if ((po = decode_pem(scp, slen, &plen)) == NULL) {
free(fcp);
free(scp);
return (NULL);
}
switch (po->data_len) {
#if defined(UNIT_TEST) && defined(VE_DEPRECATED_RSA_SHA1_SUPPORT)
case 256:
// this is our old deprecated sig method
md = &br_sha1_vtable;
hlen = br_sha1_SIZE;
hash_oid = BR_HASH_OID_SHA1;
break;
#endif
default:
md = &br_sha256_vtable;
hlen = br_sha256_SIZE;
hash_oid = BR_HASH_OID_SHA256;
break;
}
md->init(&mctx.vtable);
md->update(&mctx.vtable, fcp, flen);
md->out(&mctx.vtable, rhbuf);
if (!verify_rsa_digest(&pk->key.rsa, hash_oid,
rhbuf, hlen, po->data, po->data_len)) {
free(fcp);
fcp = NULL;
}
free(scp);
return (fcp);
}
#endif
/**
* @brief
* verify a signature and return content of signed file
*
* @param[in] sigfile
* file containing signature
* we derrive path of signed file and certificate change from
* this.
*
* @param[in] flags
* only bit 1 significant so far
*
* @return NULL on error otherwise content of signed file
*/
unsigned char *
verify_sig(const char *sigfile, int flags)
{
br_x509_pkey *pk;
br_name_element cn;
char cn_buf[80];
unsigned char cn_oid[4];
char pbuf[MAXPATHLEN];
char *cp;
unsigned char *ucp;
size_t n;
DEBUG_PRINTF(5, ("verify_sig: %s\n", sigfile));
n = strlcpy(pbuf, sigfile, sizeof(pbuf));
if (n > (sizeof(pbuf) - 5) || strcmp(&sigfile[n - 3], "sig") != 0)
return (NULL);
cp = strcpy(&pbuf[n - 3], "certs");
/*
* We want the commonName field
* the OID we want is 2,5,4,3 - but DER encoded
*/
cn_oid[0] = 3;
cn_oid[1] = 0x55;
cn_oid[2] = 4;
cn_oid[3] = 3;
cn.oid = cn_oid;
cn.buf = cn_buf;
cn.len = sizeof(cn_buf);
pk = verify_signer(pbuf, &cn, 1);
if (!pk) {
printf("cannot verify: %s: %s\n", pbuf, ve_error_get());
return (NULL);
}
for (; cp > pbuf; cp--) {
if (*cp == '.') {
*cp = '\0';
break;
}
}
switch (pk->key_type) {
#ifdef VE_ECDSA_SUPPORT
case BR_KEYTYPE_EC:
ucp = verify_ec(pk, pbuf, sigfile);
break;
#endif
#ifdef VE_RSA_SUPPORT
case BR_KEYTYPE_RSA:
ucp = verify_rsa(pk, pbuf, sigfile);
break;
#endif
default:
ucp = NULL; /* not supported */
}
xfreepkey(pk);
if (!ucp) {
printf("Unverified %s (%s)\n", pbuf,
cn.status ? cn_buf : "unknown");
} else if ((flags & 1) != 0) {
printf("Verified %s signed by %s\n", pbuf,
cn.status ? cn_buf : "someone we trust");
}
return (ucp);
}
/**
* @brief verify hash matches
*
* We have finished hashing a file,
* see if we got the desired result.
*
* @param[in] ctx
* pointer to hash context
*
* @param[in] md
* pointer to hash class
*
* @param[in] path
* name of the file we are checking
*
* @param[in] want
* the expected result
*
* @param[in] hlen
* size of hash output
*
* @return 0 on success
*/
int
ve_check_hash(br_hash_compat_context *ctx, const br_hash_class *md,
const char *path, const char *want, size_t hlen)
{
char hexbuf[br_sha512_SIZE * 2 + 2];
unsigned char hbuf[br_sha512_SIZE];
char *hex;
int rc;
int n;
md->out(&ctx->vtable, hbuf);
#ifdef VE_PCR_SUPPORT
ve_pcr_update(hbuf, hlen);
#endif
hex = hexdigest(hexbuf, sizeof(hexbuf), hbuf, hlen);
if (!hex)
return (VE_FINGERPRINT_WRONG);
n = 2*hlen;
if ((rc = strncmp(hex, want, n))) {
ve_error_set("%s: %.*s != %.*s", path, n, hex, n, want);
rc = VE_FINGERPRINT_WRONG;
}
return (rc ? rc : VE_FINGERPRINT_OK);
}
#ifdef VE_HASH_KAT_STR
static int
test_hash(const br_hash_class *md, size_t hlen,
const char *hname, const char *s, size_t slen, const char *want)
{
br_hash_compat_context mctx;
md->init(&mctx.vtable);
md->update(&mctx.vtable, s, slen);
return (ve_check_hash(&mctx, md, hname, want, hlen) != VE_FINGERPRINT_OK);
}
#endif
#define ve_test_hash(n, N) \
printf("Testing hash: " #n "\t\t\t\t%s\n", \
test_hash(&br_ ## n ## _vtable, br_ ## n ## _SIZE, #n, \
VE_HASH_KAT_STR, sizeof(VE_HASH_KAT_STR), \
vh_ ## N) ? "Failed" : "Passed")
/**
* @brief
* run self tests on hash and signature verification
*
* Test that the hash methods (SHA1 and SHA256) work.
* Test that we can verify a certificate for each supported
* Root CA.
*
* @return cached result.
*/
int
ve_self_tests(void)
{
static int once = -1;
#ifdef VERIFY_CERTS_STR
br_x509_certificate *xcs;
br_x509_pkey *pk;
br_name_element cn;
char cn_buf[80];
unsigned char cn_oid[4];
size_t num;
size_t u;
#endif
if (once >= 0)
return (once);
once = 0;
DEBUG_PRINTF(5, ("Self tests...\n"));
#ifdef VE_HASH_KAT_STR
#ifdef VE_SHA1_SUPPORT
ve_test_hash(sha1, SHA1);
#endif
#ifdef VE_SHA256_SUPPORT
ve_test_hash(sha256, SHA256);
#endif
#ifdef VE_SHA384_SUPPORT
ve_test_hash(sha384, SHA384);
#endif
#ifdef VE_SHA512_SUPPORT
ve_test_hash(sha512, SHA512);
#endif
#endif
#ifdef VERIFY_CERTS_STR
xcs = parse_certificates(__DECONST(unsigned char *, VERIFY_CERTS_STR),
sizeof(VERIFY_CERTS_STR), &num);
if (xcs == NULL)
return (0);
/*
* We want the commonName field
* the OID we want is 2,5,4,3 - but DER encoded
*/
cn_oid[0] = 3;
cn_oid[1] = 0x55;
cn_oid[2] = 4;
cn_oid[3] = 3;
cn.oid = cn_oid;
cn.buf = cn_buf;
for (u = 0; u < num; u ++) {
cn.len = sizeof(cn_buf);
if ((pk = verify_signer_xcs(&xcs[u], 1, &cn, 1)) != NULL) {
once++;
printf("Testing verify certificate: %s\tPassed\n",
cn.status ? cn_buf : "");
xfreepkey(pk);
}
}
if (!once)
printf("Testing verify certificate:\t\t\tFailed\n");
xfree(xcs);
#else
printf("No X.509 self tests\n");
#endif /* VERIFY_CERTS_STR */
#ifdef VE_OPENPGP_SUPPORT
if (!openpgp_self_tests())
once++;
#endif
return (once);
}