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:
parent
1769b514d0
commit
114933b262
11
lib/libsecureboot/Makefile
Normal file
11
lib/libsecureboot/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
# $FreeBSD$
|
||||
|
||||
.include <src.opts.mk>
|
||||
|
||||
LIB= secureboot
|
||||
|
||||
.include "Makefile.inc"
|
||||
|
||||
INCS= h/libsecureboot.h
|
||||
|
||||
.include <bsd.lib.mk>
|
17
lib/libsecureboot/Makefile.depend
Normal file
17
lib/libsecureboot/Makefile.depend
Normal 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
|
12
lib/libsecureboot/Makefile.depend.host
Normal file
12
lib/libsecureboot/Makefile.depend.host
Normal 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
|
133
lib/libsecureboot/Makefile.inc
Normal file
133
lib/libsecureboot/Makefile.inc
Normal 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
|
40
lib/libsecureboot/Makefile.libsa.inc
Normal file
40
lib/libsecureboot/Makefile.libsa.inc
Normal 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
|
134
lib/libsecureboot/README.rst
Normal file
134
lib/libsecureboot/README.rst
Normal 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
403
lib/libsecureboot/brf.c
Normal 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;
|
||||
}
|
||||
}
|
94
lib/libsecureboot/h/libsecureboot.h
Normal file
94
lib/libsecureboot/h/libsecureboot.h
Normal 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_ */
|
47
lib/libsecureboot/h/verify_file.h
Normal file
47
lib/libsecureboot/h/verify_file.h
Normal 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_ */
|
48
lib/libsecureboot/libsecureboot-priv.h
Normal file
48
lib/libsecureboot/libsecureboot-priv.h
Normal 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_ */
|
114
lib/libsecureboot/local.trust.mk
Normal file
114
lib/libsecureboot/local.trust.mk
Normal 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
|
||||
|
48
lib/libsecureboot/openpgp/Makefile.inc
Normal file
48
lib/libsecureboot/openpgp/Makefile.inc
Normal 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
|
144
lib/libsecureboot/openpgp/dearmor.c
Normal file
144
lib/libsecureboot/openpgp/dearmor.c
Normal 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
|
304
lib/libsecureboot/openpgp/decode.c
Normal file
304
lib/libsecureboot/openpgp/decode.c
Normal 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, <ype);
|
||||
|
||||
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);
|
||||
}
|
57
lib/libsecureboot/openpgp/decode.h
Normal file
57
lib/libsecureboot/openpgp/decode.h
Normal 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);
|
352
lib/libsecureboot/openpgp/opgp_key.c
Normal file
352
lib/libsecureboot/openpgp/opgp_key.c
Normal 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, <ype);
|
||||
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);
|
||||
}
|
484
lib/libsecureboot/openpgp/opgp_sig.c
Normal file
484
lib/libsecureboot/openpgp/opgp_sig.c
Normal 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);
|
||||
}
|
82
lib/libsecureboot/openpgp/packet.h
Normal file
82
lib/libsecureboot/openpgp/packet.h
Normal 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);
|
61
lib/libsecureboot/readfile.c
Normal file
61
lib/libsecureboot/readfile.c
Normal 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);
|
||||
}
|
18
lib/libsecureboot/tests/Makefile
Normal file
18
lib/libsecureboot/tests/Makefile
Normal 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>
|
12
lib/libsecureboot/tests/Makefile.depend.host
Normal file
12
lib/libsecureboot/tests/Makefile.depend.host
Normal 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
|
181
lib/libsecureboot/tests/tvo.c
Normal file
181
lib/libsecureboot/tests/tvo.c
Normal 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
291
lib/libsecureboot/vectx.c
Normal 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
458
lib/libsecureboot/veopen.c
Normal 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
84
lib/libsecureboot/vepcr.c
Normal 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);
|
||||
}
|
||||
|
421
lib/libsecureboot/verify_file.c
Normal file
421
lib/libsecureboot/verify_file.c
Normal 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
|
||||
}
|
61
lib/libsecureboot/vesigned.c
Normal file
61
lib/libsecureboot/vesigned.c
Normal 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
111
lib/libsecureboot/veta.c
Normal 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
700
lib/libsecureboot/vets.c
Normal 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);
|
||||
}
|
Loading…
Reference in New Issue
Block a user