X-Git-Url: https://git.sur5r.net/?a=blobdiff_plain;f=tools%2Fimage-host.c;h=8e43671714b8936f572c47bd2971d492c7cbad71;hb=ac378bb05fa3cac3197085431f577e0dbddd4b4a;hp=7aebc2903b3b7ad6e482f22fb451e7d137256d90;hpb=56518e71041fafdfd7af3a24f263b0a22efbeda9;p=u-boot diff --git a/tools/image-host.c b/tools/image-host.c index 7aebc2903b..8e43671714 100644 --- a/tools/image-host.c +++ b/tools/image-host.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Copyright (c) 2013, Google Inc. * @@ -5,27 +6,10 @@ * * (C) Copyright 2000-2006 * Wolfgang Denk, DENX Software Engineering, wd@denx.de. - * - * See file CREDITS for list of people who contributed to this - * project. - * - * 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 2 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., 59 Temple Place, Suite 330, Boston, - * MA 02111-1307 USA */ #include "mkimage.h" +#include #include #include @@ -53,7 +37,7 @@ static int fit_set_hash_value(void *fit, int noffset, uint8_t *value, printf("Can't set hash '%s' property for '%s' node(%s)\n", FIT_VALUE_PROP, fit_get_name(fit, noffset, NULL), fdt_strerror(ret)); - return -1; + return ret == -FDT_ERR_NOSPACE ? -ENOSPC : -EIO; } return 0; @@ -79,25 +63,27 @@ static int fit_image_process_hash(void *fit, const char *image_name, const char *node_name; int value_len; char *algo; + int ret; node_name = fit_get_name(fit, noffset, NULL); if (fit_image_hash_get_algo(fit, noffset, &algo)) { printf("Can't get hash algo property for '%s' hash node in '%s' image node\n", node_name, image_name); - return -1; + return -ENOENT; } if (calculate_hash(data, size, algo, value, &value_len)) { printf("Unsupported hash algorithm (%s) for '%s' hash node in '%s' image node\n", algo, node_name, image_name); - return -1; + return -EPROTONOSUPPORT; } - if (fit_set_hash_value(fit, noffset, value, value_len)) { + ret = fit_set_hash_value(fit, noffset, value, value_len); + if (ret) { printf("Can't set hash value for '%s' hash node in '%s' image node\n", node_name, image_name); - return -1; + return ret; } return 0; @@ -162,7 +148,7 @@ static int fit_image_write_sig(void *fit, int noffset, uint8_t *value, static int fit_image_setup_sig(struct image_sign_info *info, const char *keydir, void *fit, const char *image_name, - int noffset, const char *require_keys) + int noffset, const char *require_keys, const char *engine_id) { const char *node_name; char *algo_name; @@ -179,9 +165,12 @@ static int fit_image_setup_sig(struct image_sign_info *info, info->keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); info->fit = fit; info->node_offset = noffset; - info->algo = image_get_sig_algo(algo_name); + info->name = strdup(algo_name); + info->checksum = image_get_checksum_algo(algo_name); + info->crypto = image_get_crypto_algo(algo_name); info->require_keys = require_keys; - if (!info->algo) { + info->engine_id = engine_id; + if (!info->checksum || !info->crypto) { printf("Unsupported signature algorithm (%s) for '%s' signature node in '%s' image node\n", algo_name, node_name, image_name); return -1; @@ -205,12 +194,13 @@ static int fit_image_setup_sig(struct image_sign_info *info, * @size: size of data in bytes * @comment: Comment to add to signature nodes * @require_keys: Mark all keys as 'required' + * @engine_id: Engine to use for signing * @return 0 if ok, -1 on error */ static int fit_image_process_sig(const char *keydir, void *keydest, void *fit, const char *image_name, int noffset, const void *data, size_t size, - const char *comment, int require_keys) + const char *comment, int require_keys, const char *engine_id) { struct image_sign_info info; struct image_region region; @@ -220,13 +210,13 @@ static int fit_image_process_sig(const char *keydir, void *keydest, int ret; if (fit_image_setup_sig(&info, keydir, fit, image_name, noffset, - require_keys ? "image" : NULL)) + require_keys ? "image" : NULL, engine_id)) return -1; node_name = fit_get_name(fit, noffset, NULL); region.data = data; region.size = size; - ret = info.algo->sign(&info, ®ion, 1, &value, &value_len); + ret = info.crypto->sign(&info, ®ion, 1, &value, &value_len); if (ret) { printf("Failed to sign '%s' signature node in '%s' image node: %d\n", node_name, image_name, ret); @@ -240,7 +230,9 @@ static int fit_image_process_sig(const char *keydir, void *keydest, ret = fit_image_write_sig(fit, noffset, value, value_len, comment, NULL, 0); if (ret) { - printf("Can't write signature for '%s' signature node in '%s' image node: %s\n", + if (ret == -FDT_ERR_NOSPACE) + return -ENOSPC; + printf("Can't write signature for '%s' signature node in '%s' conf node: %s\n", node_name, image_name, fdt_strerror(ret)); return -1; } @@ -249,11 +241,18 @@ static int fit_image_process_sig(const char *keydir, void *keydest, /* Get keyname again, as FDT has changed and invalidated our pointer */ info.keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); - /* Write the public key into the supplied FDT file */ - if (keydest && info.algo->add_verify_data(&info, keydest)) { - printf("Failed to add verification data for '%s' signature node in '%s' image node\n", - node_name, image_name); - return -1; + /* + * Write the public key into the supplied FDT file; this might fail + * several times, since we try signing with successively increasing + * size values + */ + if (keydest) { + ret = info.crypto->add_verify_data(&info, keydest); + if (ret) { + printf("Failed to add verification data for '%s' signature node in '%s' image node\n", + node_name, image_name); + return ret; + } } return 0; @@ -270,16 +269,16 @@ static int fit_image_process_sig(const char *keydir, void *keydest, * * Input component image node structure: * - * o image@1 (at image_noffset) + * o image-1 (at image_noffset) * | - data = [binary data] - * o hash@1 + * o hash-1 * |- algo = "sha1" * * Output component image node structure: * - * o image@1 (at image_noffset) + * o image-1 (at image_noffset) * | - data = [binary data] - * o hash@1 + * o hash-1 * |- algo = "sha1" * |- value = sha1(data) * @@ -291,11 +290,12 @@ static int fit_image_process_sig(const char *keydir, void *keydest, * @image_noffset: Requested component image node * @comment: Comment to add to signature nodes * @require_keys: Mark all keys as 'required' + * @engine_id: Engine to use for signing * @return: 0 on success, <0 on failure */ int fit_image_add_verification_data(const char *keydir, void *keydest, void *fit, int image_noffset, const char *comment, - int require_keys) + int require_keys, const char *engine_id) { const char *image_name; const void *data; @@ -320,7 +320,7 @@ int fit_image_add_verification_data(const char *keydir, void *keydest, /* * Check subnode name, must be equal to "hash" or "signature". * Multiple hash nodes require unique unit node - * names, e.g. hash@1, hash@2, signature@1, etc. + * names, e.g. hash-1, hash-2, signature-1, etc. */ node_name = fit_get_name(fit, noffset, NULL); if (!strncmp(node_name, FIT_HASH_NODENAME, @@ -332,19 +332,344 @@ int fit_image_add_verification_data(const char *keydir, void *keydest, strlen(FIT_SIG_NODENAME))) { ret = fit_image_process_sig(keydir, keydest, fit, image_name, noffset, data, size, - comment, require_keys); + comment, require_keys, engine_id); + } + if (ret) + return ret; + } + + return 0; +} + +struct strlist { + int count; + char **strings; +}; + +static void strlist_init(struct strlist *list) +{ + memset(list, '\0', sizeof(*list)); +} + +static void strlist_free(struct strlist *list) +{ + int i; + + for (i = 0; i < list->count; i++) + free(list->strings[i]); + free(list->strings); +} + +static int strlist_add(struct strlist *list, const char *str) +{ + char *dup; + + dup = strdup(str); + list->strings = realloc(list->strings, + (list->count + 1) * sizeof(char *)); + if (!list || !str) + return -1; + list->strings[list->count++] = dup; + + return 0; +} + +static const char *fit_config_get_image_list(void *fit, int noffset, + int *lenp, int *allow_missingp) +{ + static const char default_list[] = FIT_KERNEL_PROP "\0" + FIT_FDT_PROP; + const char *prop; + + /* If there is an "image" property, use that */ + prop = fdt_getprop(fit, noffset, "sign-images", lenp); + if (prop) { + *allow_missingp = 0; + return *lenp ? prop : NULL; + } + + /* Default image list */ + *allow_missingp = 1; + *lenp = sizeof(default_list); + + return default_list; +} + +static int fit_config_get_hash_list(void *fit, int conf_noffset, + int sig_offset, struct strlist *node_inc) +{ + int allow_missing; + const char *prop, *iname, *end; + const char *conf_name, *sig_name; + char name[200], path[200]; + int image_count; + int ret, len; + + conf_name = fit_get_name(fit, conf_noffset, NULL); + sig_name = fit_get_name(fit, sig_offset, NULL); + + /* + * Build a list of nodes we need to hash. We always need the root + * node and the configuration. + */ + strlist_init(node_inc); + snprintf(name, sizeof(name), "%s/%s", FIT_CONFS_PATH, conf_name); + if (strlist_add(node_inc, "/") || + strlist_add(node_inc, name)) + goto err_mem; + + /* Get a list of images that we intend to sign */ + prop = fit_config_get_image_list(fit, sig_offset, &len, + &allow_missing); + if (!prop) + return 0; + + /* Locate the images */ + end = prop + len; + image_count = 0; + for (iname = prop; iname < end; iname += strlen(iname) + 1) { + int noffset; + int image_noffset; + int hash_count; + + image_noffset = fit_conf_get_prop_node(fit, conf_noffset, + iname); + if (image_noffset < 0) { + printf("Failed to find image '%s' in configuration '%s/%s'\n", + iname, conf_name, sig_name); + if (allow_missing) + continue; + + return -ENOENT; + } + + ret = fdt_get_path(fit, image_noffset, path, sizeof(path)); + if (ret < 0) + goto err_path; + if (strlist_add(node_inc, path)) + goto err_mem; + + snprintf(name, sizeof(name), "%s/%s", FIT_CONFS_PATH, + conf_name); + + /* Add all this image's hashes */ + hash_count = 0; + for (noffset = fdt_first_subnode(fit, image_noffset); + noffset >= 0; + noffset = fdt_next_subnode(fit, noffset)) { + const char *name = fit_get_name(fit, noffset, NULL); + + if (strncmp(name, FIT_HASH_NODENAME, + strlen(FIT_HASH_NODENAME))) + continue; + ret = fdt_get_path(fit, noffset, path, sizeof(path)); + if (ret < 0) + goto err_path; + if (strlist_add(node_inc, path)) + goto err_mem; + hash_count++; + } + + if (!hash_count) { + printf("Failed to find any hash nodes in configuration '%s/%s' image '%s' - without these it is not possible to verify this image\n", + conf_name, sig_name, iname); + return -ENOMSG; + } + + image_count++; + } + + if (!image_count) { + printf("Failed to find any images for configuration '%s/%s'\n", + conf_name, sig_name); + return -ENOMSG; + } + + return 0; + +err_mem: + printf("Out of memory processing configuration '%s/%s'\n", conf_name, + sig_name); + return -ENOMEM; + +err_path: + printf("Failed to get path for image '%s' in configuration '%s/%s': %s\n", + iname, conf_name, sig_name, fdt_strerror(ret)); + return -ENOENT; +} + +static int fit_config_get_data(void *fit, int conf_noffset, int noffset, + struct image_region **regionp, int *region_countp, + char **region_propp, int *region_proplen) +{ + char * const exc_prop[] = {"data"}; + struct strlist node_inc; + struct image_region *region; + struct fdt_region fdt_regions[100]; + const char *conf_name, *sig_name; + char path[200]; + int count, i; + char *region_prop; + int ret, len; + + conf_name = fit_get_name(fit, conf_noffset, NULL); + sig_name = fit_get_name(fit, noffset, NULL); + debug("%s: conf='%s', sig='%s'\n", __func__, conf_name, sig_name); + + /* Get a list of nodes we want to hash */ + ret = fit_config_get_hash_list(fit, conf_noffset, noffset, &node_inc); + if (ret) + return ret; + + /* Get a list of regions to hash */ + count = fdt_find_regions(fit, node_inc.strings, node_inc.count, + exc_prop, ARRAY_SIZE(exc_prop), + fdt_regions, ARRAY_SIZE(fdt_regions), + path, sizeof(path), 1); + if (count < 0) { + printf("Failed to hash configuration '%s/%s': %s\n", conf_name, + sig_name, fdt_strerror(ret)); + return -EIO; + } + if (count == 0) { + printf("No data to hash for configuration '%s/%s': %s\n", + conf_name, sig_name, fdt_strerror(ret)); + return -EINVAL; + } + + /* Build our list of data blocks */ + region = fit_region_make_list(fit, fdt_regions, count, NULL); + if (!region) { + printf("Out of memory hashing configuration '%s/%s'\n", + conf_name, sig_name); + return -ENOMEM; + } + + /* Create a list of all hashed properties */ + debug("Hash nodes:\n"); + for (i = len = 0; i < node_inc.count; i++) { + debug(" %s\n", node_inc.strings[i]); + len += strlen(node_inc.strings[i]) + 1; + } + region_prop = malloc(len); + if (!region_prop) { + printf("Out of memory setting up regions for configuration '%s/%s'\n", + conf_name, sig_name); + return -ENOMEM; + } + for (i = len = 0; i < node_inc.count; + len += strlen(node_inc.strings[i]) + 1, i++) + strcpy(region_prop + len, node_inc.strings[i]); + strlist_free(&node_inc); + + *region_countp = count; + *regionp = region; + *region_propp = region_prop; + *region_proplen = len; + + return 0; +} + +static int fit_config_process_sig(const char *keydir, void *keydest, + void *fit, const char *conf_name, int conf_noffset, + int noffset, const char *comment, int require_keys, + const char *engine_id) +{ + struct image_sign_info info; + const char *node_name; + struct image_region *region; + char *region_prop; + int region_proplen; + int region_count; + uint8_t *value; + uint value_len; + int ret; + + node_name = fit_get_name(fit, noffset, NULL); + if (fit_config_get_data(fit, conf_noffset, noffset, ®ion, + ®ion_count, ®ion_prop, ®ion_proplen)) + return -1; + + if (fit_image_setup_sig(&info, keydir, fit, conf_name, noffset, + require_keys ? "conf" : NULL, engine_id)) + return -1; + + ret = info.crypto->sign(&info, region, region_count, &value, + &value_len); + free(region); + if (ret) { + printf("Failed to sign '%s' signature node in '%s' conf node\n", + node_name, conf_name); + + /* We allow keys to be missing */ + if (ret == -ENOENT) + return 0; + return -1; + } + + ret = fit_image_write_sig(fit, noffset, value, value_len, comment, + region_prop, region_proplen); + if (ret) { + if (ret == -FDT_ERR_NOSPACE) + return -ENOSPC; + printf("Can't write signature for '%s' signature node in '%s' conf node: %s\n", + node_name, conf_name, fdt_strerror(ret)); + return -1; + } + free(value); + free(region_prop); + + /* Get keyname again, as FDT has changed and invalidated our pointer */ + info.keyname = fdt_getprop(fit, noffset, "key-name-hint", NULL); + + /* Write the public key into the supplied FDT file */ + if (keydest) { + ret = info.crypto->add_verify_data(&info, keydest); + if (ret) { + printf("Failed to add verification data for '%s' signature node in '%s' configuration node\n", + node_name, conf_name); + } + return ret; + } + + return 0; +} + +static int fit_config_add_verification_data(const char *keydir, void *keydest, + void *fit, int conf_noffset, const char *comment, + int require_keys, const char *engine_id) +{ + const char *conf_name; + int noffset; + + conf_name = fit_get_name(fit, conf_noffset, NULL); + + /* Process all hash subnodes of the configuration node */ + for (noffset = fdt_first_subnode(fit, conf_noffset); + noffset >= 0; + noffset = fdt_next_subnode(fit, noffset)) { + const char *node_name; + int ret = 0; + + node_name = fit_get_name(fit, noffset, NULL); + if (!strncmp(node_name, FIT_SIG_NODENAME, + strlen(FIT_SIG_NODENAME))) { + ret = fit_config_process_sig(keydir, keydest, + fit, conf_name, conf_noffset, noffset, comment, + require_keys, engine_id); } if (ret) - return -1; + return ret; } return 0; } int fit_add_verification_data(const char *keydir, void *keydest, void *fit, - const char *comment, int require_keys) + const char *comment, int require_keys, + const char *engine_id) { - int images_noffset; + int images_noffset, confs_noffset; int noffset; int ret; @@ -365,10 +690,54 @@ int fit_add_verification_data(const char *keydir, void *keydest, void *fit, * i.e. component image node. */ ret = fit_image_add_verification_data(keydir, keydest, - fit, noffset, comment, require_keys); + fit, noffset, comment, require_keys, engine_id); + if (ret) + return ret; + } + + /* If there are no keys, we can't sign configurations */ + if (!IMAGE_ENABLE_SIGN || !keydir) + return 0; + + /* Find configurations parent node offset */ + confs_noffset = fdt_path_offset(fit, FIT_CONFS_PATH); + if (confs_noffset < 0) { + printf("Can't find images parent node '%s' (%s)\n", + FIT_CONFS_PATH, fdt_strerror(confs_noffset)); + return -ENOENT; + } + + /* Process its subnodes, print out component images details */ + for (noffset = fdt_first_subnode(fit, confs_noffset); + noffset >= 0; + noffset = fdt_next_subnode(fit, noffset)) { + ret = fit_config_add_verification_data(keydir, keydest, + fit, noffset, comment, + require_keys, + engine_id); if (ret) return ret; } return 0; } + +#ifdef CONFIG_FIT_SIGNATURE +int fit_check_sign(const void *fit, const void *key) +{ + int cfg_noffset; + int ret; + + cfg_noffset = fit_conf_get_node(fit, NULL); + if (!cfg_noffset) + return -1; + + printf("Verifying Hash Integrity ... "); + ret = fit_config_verify(fit, cfg_noffset); + if (ret) + return ret; + ret = bootm_host_load_images(fit, cfg_noffset); + + return ret; +} +#endif