sbkeysync: Refactor signature database data structures
Rather than having three sets of (firmware, filesystem) key databases, refactor into two sets of (kdk, db, dbx) databases. This allows us to add the PK later. Signed-off-by: Jeremy Kerr <jeremy.kerr@canonical.com>
This commit is contained in:
parent
b4773c902a
commit
74153741c4
1 changed files with 138 additions and 168 deletions
306
src/sbkeysync.c
306
src/sbkeysync.c
|
@ -109,8 +109,13 @@ struct cert_type {
|
|||
|
||||
struct key_database {
|
||||
const struct key_database_type *type;
|
||||
struct list_head firmware_keys;
|
||||
struct list_head filesystem_keys;
|
||||
struct list_head keys;
|
||||
};
|
||||
|
||||
struct keyset {
|
||||
struct key_database kek;
|
||||
struct key_database db;
|
||||
struct key_database dbx;
|
||||
};
|
||||
|
||||
struct fs_keystore_entry {
|
||||
|
@ -129,9 +134,8 @@ struct fs_keystore {
|
|||
|
||||
struct sync_context {
|
||||
const char *efivars_dir;
|
||||
struct key_database *kek;
|
||||
struct key_database *db;
|
||||
struct key_database *dbx;
|
||||
struct keyset *filesystem_keys;
|
||||
struct keyset *firmware_keys;
|
||||
struct fs_keystore *fs_keystore;
|
||||
const char **keystore_dirs;
|
||||
unsigned int n_keystore_dirs;
|
||||
|
@ -140,6 +144,7 @@ struct sync_context {
|
|||
bool dry_run;
|
||||
};
|
||||
|
||||
|
||||
#define GUID_STRLEN (8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12 + 1)
|
||||
static void guid_to_str(const EFI_GUID *guid, char *str)
|
||||
{
|
||||
|
@ -295,42 +300,63 @@ static int sigdb_iterate(void *db_data, size_t len,
|
|||
return rc;
|
||||
}
|
||||
|
||||
static int sigdb_add_key(EFI_SIGNATURE_DATA *sigdata, int len,
|
||||
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 key_database *kdb = arg;
|
||||
struct keydb_add_ctx *add_ctx = arg;
|
||||
struct key *key;
|
||||
int rc;
|
||||
|
||||
key = talloc(kdb, struct key);
|
||||
key = talloc(add_ctx->keyset, struct key);
|
||||
|
||||
rc = key_parse(key, type, sigdata->SignatureData,
|
||||
len - sizeof(*sigdata));
|
||||
|
||||
if (rc)
|
||||
if (rc) {
|
||||
talloc_free(key);
|
||||
else
|
||||
list_add(&kdb->firmware_keys, &key->list);
|
||||
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_key_database(struct key_database *kdb,
|
||||
const char *dir)
|
||||
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(kdb, "%s/%s-%s", dir,
|
||||
kdb->type->name, 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(kdb, filename, &buf, &len);
|
||||
rc = fileio_read_file_noerror(ctx->firmware_keys, filename, &buf, &len);
|
||||
if (rc)
|
||||
goto out;
|
||||
|
||||
|
@ -342,7 +368,7 @@ static int read_firmware_key_database(struct key_database *kdb,
|
|||
len -= sizeof(uint32_t);
|
||||
|
||||
rc = 0;
|
||||
sigdb_iterate(buf, len, sigdb_add_key, kdb);
|
||||
sigdb_iterate(buf, len, keydb_add_key, &add_ctx);
|
||||
|
||||
out:
|
||||
if (rc)
|
||||
|
@ -352,40 +378,6 @@ out:
|
|||
return rc;
|
||||
}
|
||||
|
||||
struct keystore_add_ctx {
|
||||
struct fs_keystore *keystore;
|
||||
struct fs_keystore_entry *ke;
|
||||
struct key_database *kdb;
|
||||
};
|
||||
|
||||
static int keystore_add_key(EFI_SIGNATURE_DATA *sigdata, int len,
|
||||
const EFI_GUID *type, void *arg)
|
||||
{
|
||||
struct keystore_add_ctx *add_ctx = arg;
|
||||
struct key *key;
|
||||
int rc;
|
||||
|
||||
key = talloc(add_ctx->kdb, struct key);
|
||||
key->keystore_entry = add_ctx->ke;
|
||||
key->type = *type;
|
||||
|
||||
rc = key_parse(key, type, sigdata->SignatureData,
|
||||
len - sizeof(*sigdata));
|
||||
|
||||
if (rc) {
|
||||
talloc_free(key);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* add a reference to the data: we don't want it to be
|
||||
* deallocated if the keystore is deallocated before the
|
||||
* struct key. */
|
||||
talloc_reference(key, add_ctx->ke->data);
|
||||
list_add(&add_ctx->kdb->filesystem_keys, &key->list);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void __attribute__((format(printf, 2, 3))) print_keystore_key_error(
|
||||
struct fs_keystore_entry *ke, const char *fmt, ...)
|
||||
{
|
||||
|
@ -402,19 +394,19 @@ static void __attribute__((format(printf, 2, 3))) print_keystore_key_error(
|
|||
va_end(ap);
|
||||
}
|
||||
|
||||
static int read_keystore_key_database(struct key_database *kdb,
|
||||
struct fs_keystore *keystore)
|
||||
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 keystore_add_ctx add_ctx;
|
||||
struct keydb_add_ctx add_ctx;
|
||||
struct fs_keystore_entry *ke;
|
||||
int rc;
|
||||
|
||||
add_ctx.keystore = keystore;
|
||||
add_ctx.keyset = ctx->filesystem_keys;
|
||||
add_ctx.kdb = kdb;
|
||||
|
||||
list_for_each(&keystore->keys, ke, keystore_list) {
|
||||
list_for_each(&ctx->fs_keystore->keys, ke, keystore_list) {
|
||||
unsigned int len;
|
||||
void *buf;
|
||||
|
||||
|
@ -462,7 +454,7 @@ static int read_keystore_key_database(struct key_database *kdb,
|
|||
auth->AuthInfo.Hdr.dwLength;
|
||||
|
||||
add_ctx.ke = ke;
|
||||
rc = sigdb_iterate(buf, len, keystore_add_key, &add_ctx);
|
||||
rc = sigdb_iterate(buf, len, keydb_add_key, &add_ctx);
|
||||
if (rc) {
|
||||
print_keystore_key_error(ke, "error parsing "
|
||||
"EFI_SIGNATURE_LIST");
|
||||
|
@ -474,51 +466,41 @@ static int read_keystore_key_database(struct key_database *kdb,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int read_key_databases(struct sync_context *ctx)
|
||||
static int read_keysets(struct sync_context *ctx)
|
||||
{
|
||||
struct key_database *kdbs[] = {
|
||||
ctx->kek,
|
||||
ctx->db,
|
||||
ctx->dbx,
|
||||
};
|
||||
unsigned int i;
|
||||
read_firmware_keydb(ctx, &ctx->firmware_keys->kek);
|
||||
read_firmware_keydb(ctx, &ctx->firmware_keys->db);
|
||||
read_firmware_keydb(ctx, &ctx->firmware_keys->dbx);
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(kdbs); i++) {
|
||||
struct key_database *kdb = kdbs[i];
|
||||
read_firmware_key_database(kdb, ctx->efivars_dir);
|
||||
read_keystore_key_database(kdb, ctx->fs_keystore);
|
||||
}
|
||||
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 void print_key_database(struct key_database *kdb)
|
||||
static void print_keyset(struct keyset *keyset, const char *name)
|
||||
{
|
||||
struct key_database *kdbs[] =
|
||||
{ &keyset->kek, &keyset->db, &keyset->dbx };
|
||||
struct key *key;
|
||||
unsigned int i;
|
||||
|
||||
printf(" %s (firmware)\n", kdb->type->name);
|
||||
printf("%s keys:\n", name);
|
||||
|
||||
list_for_each(&kdb->firmware_keys, key, list)
|
||||
printf(" %s\n", key->description);
|
||||
for (i = 0; i < ARRAY_SIZE(kdbs); i++) {
|
||||
printf(" %s:\n", kdbs[i]->type->name);
|
||||
|
||||
printf(" %s (filesystem)\n", kdb->type->name);
|
||||
|
||||
list_for_each(&kdb->filesystem_keys, key, list) {
|
||||
printf(" %s\n", key->description);
|
||||
printf(" from %s/%s\n",
|
||||
key->keystore_entry->root,
|
||||
key->keystore_entry->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 void print_key_databases(struct sync_context *ctx)
|
||||
{
|
||||
printf("EFI key databases:\n");
|
||||
print_key_database(ctx->kek);
|
||||
print_key_database(ctx->db);
|
||||
print_key_database(ctx->dbx);
|
||||
}
|
||||
|
||||
static int check_efivars_mount(const char *mountpoint)
|
||||
{
|
||||
struct statfs statbuf;
|
||||
|
@ -534,13 +516,6 @@ static int check_efivars_mount(const char *mountpoint)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/* for each root directory, top-level first:
|
||||
* for each db/dbx/KEK:
|
||||
* for each file:
|
||||
* if file exists in keystore, skip
|
||||
* add file to keystore
|
||||
*/
|
||||
|
||||
static int keystore_entry_read(struct fs_keystore_entry *ke)
|
||||
{
|
||||
const char *path;
|
||||
|
@ -570,10 +545,11 @@ static bool keystore_contains_file(struct fs_keystore *keystore,
|
|||
|
||||
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 *dirname, *filename;
|
||||
const char *filename, *dirname;
|
||||
struct dirent *dirent;
|
||||
DIR *dir;
|
||||
|
||||
|
@ -585,7 +561,6 @@ static int update_keystore(struct fs_keystore *keystore, const char *root)
|
|||
continue;
|
||||
|
||||
for (dirent = readdir(dir); dirent; dirent = readdir(dir)) {
|
||||
struct fs_keystore_entry *ke;
|
||||
|
||||
if (dirent->d_name[0] == '.')
|
||||
continue;
|
||||
|
@ -612,6 +587,7 @@ static int update_keystore(struct fs_keystore *keystore, const char *root)
|
|||
closedir(dir);
|
||||
talloc_free(dirname);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -650,67 +626,55 @@ static int key_cmp(struct key *a, struct key *b)
|
|||
return memcmp(a->id, b->id, a->id_len);
|
||||
}
|
||||
|
||||
static int find_new_keys_in_kdb(struct sync_context *ctx,
|
||||
struct key_database *kdb)
|
||||
{
|
||||
struct fs_keystore_entry *ke;
|
||||
struct key *fs_key, *fw_key;
|
||||
bool found;
|
||||
int n = 0;
|
||||
|
||||
list_for_each(&kdb->filesystem_keys, fs_key, list) {
|
||||
found = false;
|
||||
list_for_each(&kdb->firmware_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;
|
||||
}
|
||||
|
||||
/* Find the keys that are present in the filesystem, but not the firmware.
|
||||
* Returns:
|
||||
* 0 if there are no new keys to add
|
||||
* >0 if there are keys to add
|
||||
* -1 on error
|
||||
/**
|
||||
* 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 key_database *kdbs[] = {
|
||||
ctx->kek,
|
||||
ctx->db,
|
||||
ctx->dbx,
|
||||
struct {
|
||||
struct key_database *fs_kdb, *fw_kdb;
|
||||
} kdbs[] = {
|
||||
{ &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 n, i;
|
||||
int rc;
|
||||
unsigned int i;
|
||||
int n = 0;
|
||||
|
||||
n = 0;
|
||||
for (i = 0; i < ARRAY_SIZE(kdbs); i++ ) {
|
||||
struct fs_keystore_entry *ke;
|
||||
struct key *fs_key, *fw_key;
|
||||
bool found;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(kdbs); i++) {
|
||||
rc = find_new_keys_in_kdb(ctx, kdbs[i]);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
n += rc;
|
||||
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;
|
||||
|
@ -800,19 +764,22 @@ static int insert_new_keys(struct sync_context *ctx)
|
|||
return rc;
|
||||
}
|
||||
|
||||
static void init_key_database(struct sync_context *ctx,
|
||||
struct key_database **kdb_p,
|
||||
const struct key_database_type *type)
|
||||
static struct keyset *init_keyset(struct sync_context *ctx)
|
||||
{
|
||||
struct key_database *kdb;
|
||||
struct keyset *keyset;
|
||||
|
||||
kdb = talloc(ctx, struct key_database);
|
||||
keyset = talloc(ctx, struct keyset);
|
||||
|
||||
list_head_init(&kdb->firmware_keys);
|
||||
list_head_init(&kdb->filesystem_keys);
|
||||
kdb->type = type;
|
||||
list_head_init(&keyset->kek.keys);
|
||||
keyset->kek.type = &keydb_types[0];
|
||||
|
||||
*kdb_p = kdb;
|
||||
list_head_init(&keyset->db.keys);
|
||||
keyset->db.type = &keydb_types[1];
|
||||
|
||||
list_head_init(&keyset->dbx.keys);
|
||||
keyset->dbx.type = &keydb_types[2];
|
||||
|
||||
return keyset;
|
||||
}
|
||||
|
||||
static struct option options[] = {
|
||||
|
@ -904,14 +871,13 @@ int main(int argc, char **argv)
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
init_key_database(ctx, &ctx->kek, &keydb_types[0]);
|
||||
init_key_database(ctx, &ctx->db, &keydb_types[1]);
|
||||
init_key_database(ctx, &ctx->dbx, &keydb_types[2]);
|
||||
|
||||
ERR_load_crypto_strings();
|
||||
OpenSSL_add_all_digests();
|
||||
OpenSSL_add_all_ciphers();
|
||||
|
||||
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)) {
|
||||
|
@ -933,9 +899,11 @@ int main(int argc, char **argv)
|
|||
if (ctx->verbose)
|
||||
print_keystore(ctx->fs_keystore);
|
||||
|
||||
read_key_databases(ctx);
|
||||
if (ctx->verbose)
|
||||
print_key_databases(ctx);
|
||||
read_keysets(ctx);
|
||||
if (ctx->verbose) {
|
||||
print_keyset(ctx->firmware_keys, "firmware");
|
||||
print_keyset(ctx->filesystem_keys, "filesystem");
|
||||
}
|
||||
|
||||
find_new_keys(ctx);
|
||||
|
||||
|
@ -945,5 +913,7 @@ int main(int argc, char **argv)
|
|||
if (!ctx->dry_run)
|
||||
insert_new_keys(ctx);
|
||||
|
||||
talloc_free(ctx);
|
||||
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue