blob: e51f17764ade4cc4c1cfd02774dcd05563c4a3d4 [file] [log] [blame]
/*
* Copyright (C) 2012 Jeremy Kerr <jeremy.kerr@canonical.com>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 3
* of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
* USA.
*
* In addition, as a special exception, the copyright holders give
* permission to link the code of portions of this program with the OpenSSL
* library under certain conditions as described in each individual source file,
* and distribute linked combinations including the two.
*
* You must obey the GNU General Public License in all respects for all
* of the code used other than OpenSSL. If you modify file(s) with this
* exception, you may extend this exception to your version of the
* file(s), but you are not obligated to do so. If you do not wish to do
* so, delete this exception statement from your version. If you delete
* this exception statement from all source files in the program, then
* also delete it here.
*/
#define _GNU_SOURCE
#include <stdint.h>
#include <stdlib.h>
#include <string.h>
#include <dirent.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/statfs.h>
#include <sys/types.h>
#include <getopt.h>
#include <ccan/list/list.h>
#include <ccan/array_size/array_size.h>
#include <ccan/talloc/talloc.h>
#include <openssl/conf.h>
#include <openssl/x509.h>
#include <openssl/err.h>
#include "fileio.h"
#include "efivars.h"
static struct statfs statfstype;
#define EFIVARS_MOUNTPOINT "/sys/firmware/efi/efivars"
#define PSTORE_FSTYPE ((typeof(statfstype.f_type))0x6165676C)
#define EFIVARS_FSTYPE ((typeof(statfstype.f_type))0xde5e81e4)
#define EFI_IMAGE_SECURITY_DATABASE_GUID \
{ 0xd719b2cb, 0x3d3a, 0x4596, \
{ 0xa3, 0xbc, 0xda, 0xd0, 0x0e, 0x67, 0x65, 0x6f } }
static const char *toolname = "sbkeysync";
static const uint32_t sigdb_attrs = EFI_VARIABLE_NON_VOLATILE |
EFI_VARIABLE_BOOTSERVICE_ACCESS |
EFI_VARIABLE_RUNTIME_ACCESS |
EFI_VARIABLE_TIME_BASED_AUTHENTICATED_WRITE_ACCESS |
EFI_VARIABLE_APPEND_WRITE;
struct key_database_type {
const char *name;
EFI_GUID guid;
};
struct key_database_type keydb_types[] = {
{ "PK", EFI_GLOBAL_VARIABLE },
{ "KEK", EFI_GLOBAL_VARIABLE },
{ "db", EFI_IMAGE_SECURITY_DATABASE_GUID },
{ "dbx", EFI_IMAGE_SECURITY_DATABASE_GUID },
};
enum keydb_type {
KEYDB_PK = 0,
KEYDB_KEK = 1,
KEYDB_DB = 2,
KEYDB_DBX = 3,
};
static const char *default_keystore_dirs[] = {
"/etc/secureboot/keys",
"/usr/share/secureboot/keys",
};
struct key {
EFI_GUID type;
int id_len;
uint8_t *id;
char *description;
struct list_node list;
/* set for keys loaded from a filesystem keystore */
struct fs_keystore_entry *keystore_entry;
};
typedef int (*key_parse_func)(struct key *, uint8_t *, size_t);
struct cert_type {
EFI_GUID guid;
key_parse_func parse;
};
struct key_database {
const struct key_database_type *type;
struct list_head keys;
};
struct keyset {
struct key_database pk;
struct key_database kek;
struct key_database db;
struct key_database dbx;
};
struct fs_keystore_entry {
const struct key_database_type *type;
const char *root;
const char *name;
uint8_t *data;
size_t len;
struct list_node keystore_list;
struct list_node new_list;
};
struct fs_keystore {
struct list_head keys;
};
struct sync_context {
const char *efivars_dir;
struct keyset *filesystem_keys;
struct keyset *firmware_keys;
struct fs_keystore *fs_keystore;
const char **keystore_dirs;
unsigned int n_keystore_dirs;
struct list_head new_keys;
bool verbose;
bool dry_run;
bool set_pk;
};
#define GUID_STRLEN (8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12 + 1)
static void guid_to_str(const EFI_GUID *guid, char *str)
{
snprintf(str, GUID_STRLEN,
"%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
guid->Data1, guid->Data2, guid->Data3,
guid->Data4[0], guid->Data4[1],
guid->Data4[2], guid->Data4[3],
guid->Data4[4], guid->Data4[5],
guid->Data4[6], guid->Data4[7]);
}
static int sha256_key_parse(struct key *key, uint8_t *data, size_t len)
{
const unsigned int sha256_id_size = 256 / 8;
unsigned int i;
if (len != sha256_id_size)
return -1;
key->id = talloc_memdup(key, data, sha256_id_size);
key->id_len = sha256_id_size;
key->description = talloc_array(key, char, len * 2 + 1);
for (i = 0; i < len; i++)
snprintf(&key->description[i*2], 3, "%02x", data[i]);
key->description[len*2] = '\0';
return 0;
}
static int x509_key_parse(struct key *key, uint8_t *data, size_t len)
{
const int description_len = 160;
ASN1_INTEGER *serial;
const uint8_t *tmp;
X509 *x509;
int rc;
rc = -1;
tmp = data;
x509 = d2i_X509(NULL, &tmp, len);
if (!x509)
return -1;
/* we use the X509 serial number as the key ID */
serial = X509_get_serialNumber(x509);
if (!serial)
goto out;
key->id_len = ASN1_STRING_length(serial);
#if OPENSSL_VERSION_NUMBER < 0x10100000L
key->id = talloc_memdup(key, ASN1_STRING_data(serial), key->id_len);
#else
key->id = talloc_memdup(key, ASN1_STRING_get0_data(serial), key->id_len);
#endif
key->description = talloc_array(key, char, description_len);
X509_NAME_oneline(X509_get_subject_name(x509),
key->description, description_len);
rc = 0;
out:
X509_free(x509);
return rc;
}
struct cert_type cert_types[] = {
{ EFI_CERT_SHA256_GUID, sha256_key_parse },
{ EFI_CERT_X509_GUID, x509_key_parse },
};
static int guidcmp(const EFI_GUID *a, const EFI_GUID *b)
{
return memcmp(a, b, sizeof(EFI_GUID));
}
static int key_parse(struct key *key, const EFI_GUID *type,
uint8_t *data, size_t len)
{
char guid_str[GUID_STRLEN];
unsigned int i;
for (i = 0; i < ARRAY_SIZE(cert_types); i++) {
if (guidcmp(&cert_types[i].guid, type))
continue;
return cert_types[i].parse(key, data, len);
}
guid_to_str(type, guid_str);
printf("warning: unknown signature type found:\n %s\n",
guid_str);
return -1;
}
typedef int (*sigdata_fn)(EFI_SIGNATURE_DATA *, int, const EFI_GUID *, void *);
/**
* Iterates an buffer of EFI_SIGNATURE_LISTs (at db_data, of length len),
* and calls fn on each EFI_SIGNATURE_DATA item found.
*
* fn is passed the EFI_SIGNATURE_DATA pointer, and the length of the
* signature data (including GUID header), the type of the signature list,
* and a context pointer.
*/
static int sigdb_iterate(void *db_data, size_t len,
sigdata_fn fn, void *arg)
{
EFI_SIGNATURE_LIST *siglist;
EFI_SIGNATURE_DATA *sigdata;
unsigned int i, j;
int rc = 0;
if (len == 0)
return 0;
if (len < sizeof(*siglist))
return -1;
for (i = 0, siglist = db_data + i;
i + sizeof(*siglist) <= len &&
i + siglist->SignatureListSize > i &&
i + siglist->SignatureListSize <= len && !rc;
i += siglist->SignatureListSize,
siglist = db_data + i) {
/* ensure that the header & sig sizes are sensible */
if (siglist->SignatureHeaderSize > siglist->SignatureListSize)
continue;
if (siglist->SignatureSize > siglist->SignatureListSize)
continue;
if (siglist->SignatureSize < sizeof(*sigdata))
continue;
/* iterate through the (constant-sized) signature data blocks */
for (j = sizeof(*siglist) + siglist->SignatureHeaderSize;
j < siglist->SignatureListSize && !rc;
j += siglist->SignatureSize)
{
sigdata = (void *)(siglist) + j;
rc = fn(sigdata, siglist->SignatureSize,
&siglist->SignatureType, arg);
}
}
return rc;
}
struct keydb_add_ctx {
struct fs_keystore_entry *ke;
struct key_database *kdb;
struct keyset *keyset;
};
static int keydb_add_key(EFI_SIGNATURE_DATA *sigdata, int len,
const EFI_GUID *type, void *arg)
{
struct keydb_add_ctx *add_ctx = arg;
struct key *key;
int rc;
key = talloc(add_ctx->keyset, struct key);
rc = key_parse(key, type, sigdata->SignatureData,
len - sizeof(*sigdata));
if (rc) {
talloc_free(key);
return 0;
}
key->keystore_entry = add_ctx->ke;
key->type = *type;
/* add a reference to the keystore entry: we don't want it to be
* deallocated if the keystore is deallocated before the
* struct key. */
if (key->keystore_entry)
talloc_reference(key, key->keystore_entry);
list_add(&add_ctx->kdb->keys, &key->list);
return 0;
}
static int read_firmware_keydb(struct sync_context *ctx,
struct key_database *kdb)
{
struct keydb_add_ctx add_ctx;
char guid_str[GUID_STRLEN];
char *filename;
uint8_t *buf;
int rc = -1;
size_t len;
add_ctx.keyset = ctx->firmware_keys;
add_ctx.kdb = kdb;
add_ctx.ke = NULL;
guid_to_str(&kdb->type->guid, guid_str);
filename = talloc_asprintf(ctx->firmware_keys, "%s/%s-%s",
ctx->efivars_dir, kdb->type->name, guid_str);
buf = NULL;
rc = fileio_read_file_noerror(ctx->firmware_keys, filename, &buf, &len);
if (rc)
goto out;
/* efivars files start with a 32-bit attribute block */
if (len < sizeof(uint32_t))
goto out;
buf += sizeof(uint32_t);
len -= sizeof(uint32_t);
rc = 0;
sigdb_iterate(buf, len, keydb_add_key, &add_ctx);
out:
if (rc)
talloc_free(buf);
talloc_free(filename);
return rc;
}
static void __attribute__((format(printf, 2, 3))) print_keystore_key_error(
struct fs_keystore_entry *ke, const char *fmt, ...)
{
char *errstr;
va_list ap;
va_start(ap, fmt);
errstr = talloc_vasprintf(ke, fmt, ap);
fprintf(stderr, "Invalid key %s/%s\n - %s\n", ke->root, ke->name,
errstr);
talloc_free(errstr);
va_end(ap);
}
static int read_filesystem_keydb(struct sync_context *ctx,
struct key_database *kdb)
{
EFI_GUID cert_type_pkcs7 = EFI_CERT_TYPE_PKCS7_GUID;
EFI_VARIABLE_AUTHENTICATION_2 *auth;
struct keydb_add_ctx add_ctx;
struct fs_keystore_entry *ke;
int rc;
add_ctx.keyset = ctx->filesystem_keys;
add_ctx.kdb = kdb;
list_for_each(&ctx->fs_keystore->keys, ke, keystore_list) {
unsigned int len;
void *buf;
if (ke->len == 0)
continue;
if (ke->type != kdb->type)
continue;
/* parse the three data structures:
* EFI_VARIABLE_AUTHENTICATION_2 token
* EFI_SIGNATURE_LIST
* EFI_SIGNATURE_DATA
* ensuring that we have enough data for each
*/
buf = ke->data;
len = ke->len;
if (len < sizeof(*auth)) {
print_keystore_key_error(ke, "does not contain an "
"EFI_VARIABLE_AUTHENTICATION_2 descriptor");
continue;
}
auth = buf;
if (guidcmp(&auth->AuthInfo.CertType, &cert_type_pkcs7)) {
print_keystore_key_error(ke, "unknown cert type");
continue;
}
if (auth->AuthInfo.Hdr.dwLength > len) {
print_keystore_key_error(ke,
"invalid WIN_CERTIFICATE length");
continue;
}
/* the dwLength field includes the size of the WIN_CERTIFICATE,
* but not the other data in the EFI_VARIABLE_AUTHENTICATION_2
* descriptor */
buf += sizeof(*auth) - sizeof(auth->AuthInfo) +
auth->AuthInfo.Hdr.dwLength;
len -= sizeof(*auth) - sizeof(auth->AuthInfo) +
auth->AuthInfo.Hdr.dwLength;
add_ctx.ke = ke;
rc = sigdb_iterate(buf, len, keydb_add_key, &add_ctx);
if (rc) {
print_keystore_key_error(ke, "error parsing "
"EFI_SIGNATURE_LIST");
continue;
}
}
return 0;
}
static int read_keysets(struct sync_context *ctx)
{
read_firmware_keydb(ctx, &ctx->firmware_keys->pk);
read_firmware_keydb(ctx, &ctx->firmware_keys->kek);
read_firmware_keydb(ctx, &ctx->firmware_keys->db);
read_firmware_keydb(ctx, &ctx->firmware_keys->dbx);
read_filesystem_keydb(ctx, &ctx->filesystem_keys->pk);
read_filesystem_keydb(ctx, &ctx->filesystem_keys->kek);
read_filesystem_keydb(ctx, &ctx->filesystem_keys->db);
read_filesystem_keydb(ctx, &ctx->filesystem_keys->dbx);
return 0;
}
static int check_pk(struct sync_context *ctx)
{
struct key *key;
int i = 0;
list_for_each(&ctx->filesystem_keys->pk.keys, key, list)
i++;
return (i <= 1) ? 0 : 1;
}
static void print_keyset(struct keyset *keyset, const char *name)
{
struct key_database *kdbs[] =
{ &keyset->pk, &keyset->kek, &keyset->db, &keyset->dbx };
struct key *key;
unsigned int i;
printf("%s keys:\n", name);
for (i = 0; i < ARRAY_SIZE(kdbs); i++) {
printf(" %s:\n", kdbs[i]->type->name);
list_for_each(&kdbs[i]->keys, key, list) {
printf(" %s\n", key->description);
if (key->keystore_entry)
printf(" from %s/%s\n",
key->keystore_entry->root,
key->keystore_entry->name);
}
}
}
static int check_efivars_mount(const char *mountpoint)
{
struct statfs statbuf;
int rc;
rc = statfs(mountpoint, &statbuf);
if (rc)
return -1;
if (statbuf.f_type != EFIVARS_FSTYPE && statbuf.f_type != PSTORE_FSTYPE)
return -1;
return 0;
}
static int keystore_entry_read(struct fs_keystore_entry *ke)
{
const char *path;
int rc;
path = talloc_asprintf(ke, "%s/%s", ke->root, ke->name);
rc = fileio_read_file(ke, path, &ke->data, &ke->len);
talloc_free(path);
return rc;
}
static bool keystore_contains_file(struct fs_keystore *keystore,
const char *filename)
{
struct fs_keystore_entry *ke;
list_for_each(&keystore->keys, ke, keystore_list) {
if (!strcmp(ke->name, filename))
return true;
}
return false;
}
static int update_keystore(struct fs_keystore *keystore, const char *root)
{
struct fs_keystore_entry *ke;
unsigned int i;
for (i = 0; i < ARRAY_SIZE(keydb_types); i++) {
const char *filename, *dirname;
struct dirent *dirent;
DIR *dir;
dirname = talloc_asprintf(keystore, "%s/%s", root,
keydb_types[i].name);
dir = opendir(dirname);
if (!dir)
continue;
for (dirent = readdir(dir); dirent; dirent = readdir(dir)) {
if (dirent->d_name[0] == '.')
continue;
filename = talloc_asprintf(dirname, "%s/%s",
keydb_types[i].name,
dirent->d_name);
if (keystore_contains_file(keystore, filename))
continue;
ke = talloc(keystore, struct fs_keystore_entry);
ke->name = filename;
ke->root = root;
ke->type = &keydb_types[i];
talloc_steal(ke, ke->name);
if (keystore_entry_read(ke))
talloc_free(ke);
else
list_add(&keystore->keys, &ke->keystore_list);
}
closedir(dir);
talloc_free(dirname);
}
return 0;
}
static int read_keystore(struct sync_context *ctx)
{
struct fs_keystore *keystore;
unsigned int i;
keystore = talloc(ctx, struct fs_keystore);
list_head_init(&keystore->keys);
for (i = 0; i < ctx->n_keystore_dirs; i++) {
update_keystore(keystore, ctx->keystore_dirs[i]);
}
ctx->fs_keystore = keystore;
return 0;
}
static void print_keystore(struct fs_keystore *keystore)
{
struct fs_keystore_entry *ke;
printf("Filesystem keystore:\n");
list_for_each(&keystore->keys, ke, keystore_list)
printf(" %s/%s [%zd bytes]\n", ke->root, ke->name, ke->len);
}
static int key_cmp(struct key *a, struct key *b)
{
if (a->id_len != b->id_len)
return a->id_len - b->id_len;
return memcmp(a->id, b->id, a->id_len);
}
/**
* Finds the set-difference of the filesystem and firmware keys, and
* populates ctx->new_keys with the keystore_entries that should be
* inserted into firmware
*/
static int find_new_keys(struct sync_context *ctx)
{
struct {
struct key_database *fs_kdb, *fw_kdb;
} kdbs[] = {
{ &ctx->filesystem_keys->pk, &ctx->firmware_keys->pk },
{ &ctx->filesystem_keys->kek, &ctx->firmware_keys->kek },
{ &ctx->filesystem_keys->db, &ctx->firmware_keys->db },
{ &ctx->filesystem_keys->dbx, &ctx->firmware_keys->dbx },
};
unsigned int i;
int n = 0;
for (i = 0; i < ARRAY_SIZE(kdbs); i++ ) {
struct fs_keystore_entry *ke;
struct key *fs_key, *fw_key;
bool found;
list_for_each(&kdbs[i].fs_kdb->keys, fs_key, list) {
found = false;
list_for_each(&kdbs[i].fw_kdb->keys, fw_key, list) {
if (!key_cmp(fs_key, fw_key)) {
found = true;
break;
}
}
if (found)
continue;
/* add the keystore entry if it's not already present */
found = false;
list_for_each(&ctx->new_keys, ke, new_list) {
if (fs_key->keystore_entry == ke) {
found = true;
break;
}
}
if (found)
continue;
list_add(&ctx->new_keys,
&fs_key->keystore_entry->new_list);
n++;
}
}
return n;
}
static void print_new_keys(struct sync_context *ctx)
{
struct fs_keystore_entry *ke;
printf("New keys in filesystem:\n");
list_for_each(&ctx->new_keys, ke, new_list)
printf(" %s/%s\n", ke->root, ke->name);
}
static int insert_key(struct sync_context *ctx, struct fs_keystore_entry *ke)
{
char guid_str[GUID_STRLEN];
char *efivars_filename;
unsigned int buf_len;
uint8_t *buf;
int fd, rc;
fd = -1;
rc = -1;
if (ctx->verbose)
printf("Inserting key update %s/%s into %s\n",
ke->root, ke->name, ke->type->name);
/* we create a contiguous buffer of attributes & key data, so that
* we write to the efivars file in a single syscall */
buf_len = sizeof(sigdb_attrs) + ke->len;
buf = talloc_array(ke, uint8_t, buf_len);
memcpy(buf, &sigdb_attrs, sizeof(sigdb_attrs));
memcpy(buf + sizeof(sigdb_attrs), ke->data, ke->len);
guid_to_str(&ke->type->guid, guid_str);
efivars_filename = talloc_asprintf(ke, "%s/%s-%s", ctx->efivars_dir,
ke->type->name, guid_str);
fd = open(efivars_filename, O_WRONLY | O_CREAT, 0600);
if (fd < 0) {
fprintf(stderr, "Can't create key file %s: %s\n",
efivars_filename, strerror(errno));
goto out;
}
rc = write(fd, buf, buf_len);
if (rc <= 0) {
fprintf(stderr, "Error writing key update: %s\n",
strerror(errno));
goto out;
}
if (rc != (int)buf_len) {
fprintf(stderr, "Partial write during key update: "
"wrote %d bytes, expecting %d\n",
rc, buf_len);
goto out;
}
rc = 0;
out:
if (fd >= 0)
close(fd);
talloc_free(efivars_filename);
talloc_free(buf);
if (rc)
fprintf(stderr, "Error syncing keystore file %s/%s\n",
ke->root, ke->name);
return rc;
}
static int insert_new_keys(struct sync_context *ctx)
{
struct fs_keystore_entry *ke, *ke_pk;
int pks, rc;
rc = 0;
pks = 0;
ke_pk = NULL;
list_for_each(&ctx->new_keys, ke, new_list) {
/* we handle PK last */
if (ke->type == &keydb_types[KEYDB_PK]) {
ke_pk = ke;
pks++;
continue;
}
if (insert_key(ctx, ke))
rc = -1;
}
if (rc)
return rc;
if (pks == 0 || !ctx->set_pk)
return 0;
if (pks > 1) {
fprintf(stderr, "Skipping PK update due to mutiple PKs\n");
return -1;
}
rc = insert_key(ctx, ke_pk);
return rc;
}
static struct keyset *init_keyset(struct sync_context *ctx)
{
struct keyset *keyset;
keyset = talloc(ctx, struct keyset);
list_head_init(&keyset->pk.keys);
keyset->pk.type = &keydb_types[KEYDB_PK];
list_head_init(&keyset->kek.keys);
keyset->kek.type = &keydb_types[KEYDB_KEK];
list_head_init(&keyset->db.keys);
keyset->db.type = &keydb_types[KEYDB_DB];
list_head_init(&keyset->dbx.keys);
keyset->dbx.type = &keydb_types[KEYDB_DBX];
return keyset;
}
static struct option options[] = {
{ "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'V' },
{ "efivars-path", required_argument, NULL, 'e' },
{ "verbose", no_argument, NULL, 'v' },
{ "dry-run", no_argument, NULL, 'n' },
{ "pk", no_argument, NULL, 'p' },
{ "no-default-keystores", no_argument, NULL, 'd' },
{ "keystore", required_argument, NULL, 'k' },
{ NULL, 0, NULL, 0 },
};
static void usage(void)
{
printf("Usage: %s [options]\n"
"Update EFI key databases from the filesystem\n"
"\n"
"Options:\n"
"\t--efivars-path <dir> Path to efivars mountpoint\n"
"\t (or regular directory for testing)\n"
"\t--verbose Print verbose progress information\n"
"\t--dry-run Don't update firmware key databases\n"
"\t--pk Set PK\n"
"\t--keystore <dir> Read keys from <dir>/{db,dbx,KEK}/*\n"
"\t (can be specified multiple times,\n"
"\t first dir takes precedence)\n"
"\t--no-default-keystores\n"
"\t Don't read keys from the default\n"
"\t keystore dirs\n",
toolname);
}
static void version(void)
{
printf("%s %s\n", toolname, VERSION);
}
static void add_keystore_dir(struct sync_context *ctx, const char *dir)
{
ctx->keystore_dirs = talloc_realloc(ctx, ctx->keystore_dirs,
const char *, ++ctx->n_keystore_dirs);
ctx->keystore_dirs[ctx->n_keystore_dirs - 1] =
talloc_strdup(ctx->keystore_dirs, dir);
}
int main(int argc, char **argv)
{
bool use_default_keystore_dirs;
struct sync_context *ctx;
use_default_keystore_dirs = true;
ctx = talloc_zero(NULL, struct sync_context);
list_head_init(&ctx->new_keys);
for (;;) {
int idx, c;
c = getopt_long(argc, argv, "e:dpkvhV", options, &idx);
if (c == -1)
break;
switch (c) {
case 'e':
ctx->efivars_dir = optarg;
break;
case 'd':
use_default_keystore_dirs = false;
break;
case 'k':
add_keystore_dir(ctx, optarg);
break;
case 'p':
ctx->set_pk = true;
break;
case 'v':
ctx->verbose = true;
break;
case 'n':
ctx->dry_run = true;
break;
case 'V':
version();
return EXIT_SUCCESS;
case 'h':
usage();
return EXIT_SUCCESS;
}
}
if (argc != optind) {
usage();
return EXIT_FAILURE;
}
ERR_load_crypto_strings();
OpenSSL_add_all_digests();
OpenSSL_add_all_ciphers();
#if OPENSSL_VERSION_NUMBER < 0x10100000L
OPENSSL_config(NULL);
#else
OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CONFIG, NULL);
#endif
/* here we may get highly unlikely failures or we'll get a
* complaint about FIPS signatures (usually becuase the FIPS
* module isn't present). In either case ignore the errors
* (malloc will cause other failures out lower down */
ERR_clear_error();
ctx->filesystem_keys = init_keyset(ctx);
ctx->firmware_keys = init_keyset(ctx);
if (!ctx->efivars_dir) {
ctx->efivars_dir = EFIVARS_MOUNTPOINT;
if (check_efivars_mount(ctx->efivars_dir)) {
fprintf(stderr, "Can't access efivars filesystem "
"at %s, aborting\n", ctx->efivars_dir);
return EXIT_FAILURE;
}
}
if (use_default_keystore_dirs) {
unsigned int i;
for (i = 0; i < ARRAY_SIZE(default_keystore_dirs); i++)
add_keystore_dir(ctx, default_keystore_dirs[i]);
}
read_keystore(ctx);
if (ctx->verbose)
print_keystore(ctx->fs_keystore);
read_keysets(ctx);
if (ctx->verbose) {
print_keyset(ctx->firmware_keys, "firmware");
print_keyset(ctx->filesystem_keys, "filesystem");
}
if (check_pk(ctx))
fprintf(stderr, "WARNING: multiple PKs found in filesystem\n");
find_new_keys(ctx);
if (ctx->verbose)
print_new_keys(ctx);
if (!ctx->dry_run)
insert_new_keys(ctx);
talloc_free(ctx);
return EXIT_SUCCESS;
}