diff options
Diffstat (limited to 'common/image-fit.c')
-rw-r--r-- | common/image-fit.c | 326 |
1 files changed, 231 insertions, 95 deletions
diff --git a/common/image-fit.c b/common/image-fit.c index 2681d62a9a..251fda97b3 100644 --- a/common/image-fit.c +++ b/common/image-fit.c @@ -1,20 +1,8 @@ +// SPDX-License-Identifier: GPL-2.0-or-later /* * Copyright (C) Jan Lübbe, 2014 * * This code is inspired by the U-Boot FIT image code. - * - * 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, see <http://www.gnu.org/licenses/>. */ #define pr_fmt(fmt) "FIT: " fmt @@ -33,6 +21,7 @@ #include <linux/err.h> #include <stringlist.h> #include <rsa.h> +#include <uncompress.h> #include <image-fit.h> #define FDT_MAX_DEPTH 32 @@ -242,11 +231,9 @@ static struct digest *fit_alloc_digest(struct device_node *sig_node, return ERR_PTR(-EINVAL); } - if (strcmp(algo_name, "sha1,rsa2048") == 0) { + if (strncmp(algo_name, "sha1,", 5) == 0) { algo = HASH_ALGO_SHA1; - } else if (strcmp(algo_name, "sha256,rsa2048") == 0) { - algo = HASH_ALGO_SHA256; - } else if (strcmp(algo_name, "sha256,rsa4096") == 0) { + } else if (strncmp(algo_name, "sha256,", 7) == 0) { algo = HASH_ALGO_SHA256; } else { pr_err("unknown algo %s\n", algo_name); @@ -269,53 +256,44 @@ static struct digest *fit_alloc_digest(struct device_node *sig_node, static int fit_check_rsa_signature(struct device_node *sig_node, enum hash_algo algo, void *hash) { - struct rsa_public_key *key; - const char *key_name; - char *key_path; - struct device_node *key_node; + const struct rsa_public_key *key; + const char *key_name = NULL; int sig_len; const char *sig_value; int ret; sig_value = of_get_property(sig_node, "value", &sig_len); if (!sig_value) { - pr_err("signature value not found in %s\n", sig_node->full_name); - return -EINVAL; - } - - if (of_property_read_string(sig_node, "key-name-hint", &key_name)) { - pr_err("key name not found in %s\n", sig_node->full_name); + pr_err("signature value not found in %pOF\n", sig_node); return -EINVAL; } - key = rsa_get_key(key_name); - if (IS_ERR(key)) { - key_path = xasprintf("/signature/key-%s", key_name); - key_node = of_find_node_by_path(key_path); - if (!key_node) { - pr_info("failed to find key node %s\n", key_path); - free(key_path); - return -ENOENT; + of_property_read_string(sig_node, "key-name-hint", &key_name); + if (key_name) { + key = rsa_get_key(key_name); + if (key) { + ret = rsa_verify(key, sig_value, sig_len, hash, algo); + if (!ret) + goto ok; } - free(key_path); + } - key = rsa_of_read_key(key_node); + for_each_rsa_key(key) { + if (key_name && !strcmp(key->key_name_hint, key_name)) + continue; - if (IS_ERR(key)) { - pr_info("failed to read key in %s\n", key_node->full_name); - return -ENOENT; - } + ret = rsa_verify(key, sig_value, sig_len, hash, algo); + if (!ret) + goto ok; } - ret = rsa_verify(key, sig_value, sig_len, hash, algo); - if (ret) - pr_err("image signature BAD\n"); - else - pr_info("image signature OK\n"); + pr_err("image signature BAD\n"); - rsa_key_free(key); + return -EBADMSG; +ok: + pr_info("image signature OK\n"); - return ret; + return 0; } /* @@ -332,12 +310,12 @@ static int fit_verify_signature(struct device_node *sig_node, const void *fit) if (of_property_read_u32_index(sig_node, "hashed-strings", 0, &hashed_strings_start)) { - pr_err("hashed-strings start not found in %s\n", sig_node->full_name); + pr_err("hashed-strings start not found in %pOF\n", sig_node); return -EINVAL; } if (of_property_read_u32_index(sig_node, "hashed-strings", 1, &hashed_strings_size)) { - pr_err("hashed-strings size not found in %s\n", sig_node->full_name); + pr_err("hashed-strings size not found in %pOF\n", sig_node); return -EINVAL; } @@ -345,7 +323,7 @@ static int fit_verify_signature(struct device_node *sig_node, const void *fit) string_list_init(&exc_props); if (of_read_string_list(sig_node, "hashed-nodes", &inc_nodes)) { - pr_err("hashed-nodes property not found in %s\n", sig_node->full_name); + pr_err("hashed-nodes property not found in %pOF\n", sig_node); ret = -EINVAL; goto out_sl; } @@ -385,7 +363,6 @@ static int fit_verify_hash(struct fit_handle *handle, struct device_node *image, struct digest *d; const char *algo; const char *value_read; - char *value_calc; int hash_len, ret; struct device_node *hash; @@ -404,50 +381,44 @@ static int fit_verify_hash(struct fit_handle *handle, struct device_node *image, hash = of_get_child_by_name(image, "hash@1"); if (!hash) { if (ret) - pr_err("image %s does not have hashes\n", - image->full_name); + pr_err("image %pOF does not have hashes\n", image); return ret; } value_read = of_get_property(hash, "value", &hash_len); if (!value_read) { - pr_err("%s: \"value\" property not found\n", hash->full_name); + pr_err("%pOF: \"value\" property not found\n", hash); return -EINVAL; } if (of_property_read_string(hash, "algo", &algo)) { - pr_err("%s: \"algo\" property not found\n", hash->full_name); + pr_err("%pOF: \"algo\" property not found\n", hash); return -EINVAL; } d = digest_alloc(algo); if (!d) { - pr_err("%s: unsupported algo %s\n", hash->full_name, algo); + pr_err("%pOF: unsupported algo %s\n", hash, algo); return -EINVAL; } if (hash_len != digest_length(d)) { - pr_err("%s: invalid hash length %d\n", hash->full_name, hash_len); + pr_err("%pOF: invalid hash length %d\n", hash, hash_len); ret = -EINVAL; goto err_digest_free; } - value_calc = xmalloc(hash_len); - digest_init(d); digest_update(d, data, data_len); - digest_final(d, value_calc); - if (memcmp(value_read, value_calc, hash_len)) { - pr_info("%s: hash BAD\n", hash->full_name); + if (digest_verify(d, value_read)) { + pr_info("%pOF: hash BAD\n", hash); ret = -EBADMSG; } else { - pr_info("%s: hash OK\n", hash->full_name); + pr_info("%pOF: hash OK\n", hash); ret = 0; } - free(value_calc); - err_digest_free: digest_free(d); @@ -481,7 +452,7 @@ static int fit_image_verify_signature(struct fit_handle *handle, if (!sig_node) sig_node = of_get_child_by_name(image, "signature@1"); if (!sig_node) { - pr_err("Image %s has no signature\n", image->full_name); + pr_err("Image %pOF has no signature\n", image); return ret; } @@ -517,6 +488,129 @@ int fit_has_image(struct fit_handle *handle, void *configuration, return 1; } +static int fit_get_address(struct device_node *image, const char *property, + unsigned long *addr) +{ + const __be32 *cell; + int len = 0; + + cell = of_get_property(image, property, &len); + if (!cell) + return -EINVAL; + if (len > sizeof(*addr)) + return -ENOTSUPP; + + *addr = (unsigned long)of_read_number(cell, len / sizeof(*cell)); + return 0; +} + +static int +fit_get_image(struct fit_handle *handle, void *configuration, + const char **unit, struct device_node **image) +{ + struct device_node *conf_node = configuration; + + if (conf_node) { + if (of_property_read_string(conf_node, *unit, unit)) { + pr_err("No image named '%s'\n", *unit); + return -ENOENT; + } + } + + *image = of_get_child_by_name(handle->images, *unit); + if (!*image) + return -ENOENT; + + return 0; +} + +/** + * fit_get_image_address - Get an address from an image in a FIT image + * @handle: The FIT image handle + * @name: The name of the image to open + * @property: The name of the address to get (for example "load" or "entry") + * @address: The address given by the image + * + * Try to parse the @property in the image @name as an address. @configuration + * holds the cookie returned from fit_open_configuration() if the image is + * opened as part of a configuration, or NULL if the image is opened without a + * configuration. If it exists the value will be returned in @address. Otherwise + * @address won't be changed. + * + * Return: 0 for success, negative error code otherwise + */ +int fit_get_image_address(struct fit_handle *handle, void *configuration, + const char *name, const char *property, + unsigned long *address) +{ + struct device_node *image; + const char *unit = name; + const char *type; + int ret; + + if (!address || !property || !name) + return -EINVAL; + + ret = fit_get_image(handle, configuration, &unit, &image); + if (ret) + return ret; + + /* Treat type = "kernel_noload" as if entry/load address is missing */ + ret = of_property_read_string(image, "type", &type); + if (!ret && !strcmp(type, "kernel_noload")) + return -ENOENT; + + ret = fit_get_address(image, property, address); + + return ret; +} + +static void fit_uncompress_error_fn(char *x) +{ + pr_err("%s\n", x); +} + +static int fit_handle_decompression(struct device_node *image, + const char *type, + const void **data, + int *data_len) +{ + const char *compression = NULL; + void *uc_data; + int ret; + + of_property_read_string(image, "compression", &compression); + if (!compression || !strcmp(compression, "none")) + return 0; + + if (!strcmp(type, "ramdisk")) { + pr_warn("compression != \"none\" for ramdisks is deprecated," + " please fix your .its file!\n"); + return 0; + } + + if (!IS_ENABLED(CONFIG_UNCOMPRESS)) { + pr_err("image has compression = \"%s\", but support not compiled in\n", + compression); + return -ENOSYS; + } + + ret = uncompress_buf_to_buf(*data, *data_len, &uc_data, + fit_uncompress_error_fn); + if (ret < 0) { + pr_err("data couldn't be decompressed\n"); + return ret; + } + + *data = uc_data; + *data_len = ret; + + /* associate buffer with FIT, so it's not leaked */ + __of_new_property(image, "uncompressed-data", uc_data, *data_len); + + return 0; +} + /** * fit_open_image - Open an image in a FIT image * @handle: The FIT image handle @@ -539,31 +633,21 @@ int fit_open_image(struct fit_handle *handle, void *configuration, unsigned long *outsize) { struct device_node *image; - const char *unit, *type = NULL, *desc= "(no description)"; + const char *unit = name, *type = NULL, *desc= "(no description)"; const void *data; int data_len; int ret = 0; - struct device_node *conf_node = configuration; - if (conf_node) { - if (of_property_read_string(conf_node, name, &unit)) { - pr_err("No image named '%s'\n", name); - return -ENOENT; - } - } else { - unit = name; - } - - image = of_get_child_by_name(handle->images, unit); - if (!image) - return -ENOENT; + ret = fit_get_image(handle, configuration, &unit, &image); + if (ret) + return ret; of_property_read_string(image, "description", &desc); pr_info("image '%s': '%s'\n", unit, desc); of_property_read_string(image, "type", &type); if (!type) { - pr_err("No \"type\" property found in %s\n", image->full_name); + pr_err("No \"type\" property found in %pOF\n", image); return -EINVAL; } @@ -573,7 +657,7 @@ int fit_open_image(struct fit_handle *handle, void *configuration, return -EINVAL; } - if (conf_node) + if (configuration) ret = fit_verify_hash(handle, image, data, data_len); else ret = fit_image_verify_signature(handle, image, data, data_len); @@ -581,6 +665,10 @@ int fit_open_image(struct fit_handle *handle, void *configuration, if (ret < 0) return ret; + ret = fit_handle_decompression(image, type, &data, &data_len); + if (ret) + return ret; + *outdata = data; *outsize = data_len; @@ -608,27 +696,32 @@ static int fit_config_verify_signature(struct fit_handle *handle, struct device_ } for_each_child_of_node(conf_node, sig_node) { + if (!of_node_has_prefix(sig_node, "signature")) + continue; + if (handle->verbose) - of_print_nodes(sig_node, 0); + of_print_nodes(sig_node, 0, ~0); + ret = fit_verify_signature(sig_node, handle->fit); if (ret < 0) return ret; } if (ret < 0) { - pr_err("configuration '%s' does not have a signature\n", - conf_node->full_name); + pr_err("configuration '%pOF' does not have a signature\n", conf_node); return ret; } return ret; } -static int fit_find_compatible_unit(struct device_node *conf_node, +static int fit_find_compatible_unit(struct fit_handle *handle, + struct device_node *conf_node, const char **unit) { struct device_node *child = NULL; struct device_node *barebox_root; + int best_score = 0; const char *machine; int ret; @@ -641,13 +734,55 @@ static int fit_find_compatible_unit(struct device_node *conf_node, return -ENOENT; for_each_child_of_node(conf_node, child) { - if (of_device_is_compatible(child, machine)) { + int score = of_device_is_compatible(child, machine); + + if (!score && !of_property_present(child, "compatible") && + of_property_present(child, "fdt")) { + struct device_node *image; + const char *unit = "fdt"; + int data_len; + const void *data; + int ret; + + ret = fit_get_image(handle, child, &unit, &image); + if (ret) + goto next; + + data = of_get_property(image, "data", &data_len); + if (!data) { + ret = -EINVAL; + goto next; + } + + ret = fit_handle_decompression(image, "fdt", &data, &data_len); + if (ret) { + ret = -EILSEQ; + goto next; + } + + score = fdt_machine_is_compatible(data, data_len, machine); + + of_delete_property_by_name(image, "uncompressed-data"); +next: + if (ret) + pr_warn("skipping malformed configuration: %pOF (%pe)\n", + child, ERR_PTR(ret)); + } + + if (score > best_score) { + best_score = score; *unit = child->name; - pr_info("matching unit '%s' found\n", *unit); - return 0; + + if (score == OF_DEVICE_COMPATIBLE_MAX_SCORE) + break; } } + if (best_score) { + pr_info("matching unit '%s' found\n", *unit); + return 0; + } + default_unit: pr_info("No match found. Trying default.\n"); if (of_property_read_string(conf_node, "default", unit) == 0) @@ -679,7 +814,7 @@ void *fit_open_configuration(struct fit_handle *handle, const char *name) if (name) { unit = name; } else { - ret = fit_find_compatible_unit(conf_node, &unit); + ret = fit_find_compatible_unit(handle, conf_node, &unit); if (ret) { pr_info("Couldn't get a valid configuration. Aborting.\n"); return ERR_PTR(ret); @@ -707,7 +842,7 @@ static int fit_do_open(struct fit_handle *handle) const char *desc = "(no description)"; struct device_node *root; - root = of_unflatten_dtb_const(handle->fit); + root = of_unflatten_dtb_const(handle->fit, handle->size); if (IS_ERR(root)) return PTR_ERR(root); @@ -765,6 +900,7 @@ struct fit_handle *fit_open_buf(const void *buf, size_t size, bool verbose, * @filename: The filename of the FIT image * @verbose: If true, be more verbose * @verify: The verify mode + * @max_size: maximum length to read from file * * This opens a FIT image found in @filename. The returned handle is used as * context for the other FIT functions. @@ -772,7 +908,7 @@ struct fit_handle *fit_open_buf(const void *buf, size_t size, bool verbose, * Return: A handle to a FIT image or a ERR_PTR */ struct fit_handle *fit_open(const char *filename, bool verbose, - enum bootm_verify verify) + enum bootm_verify verify, loff_t max_size) { struct fit_handle *handle; int ret; @@ -783,8 +919,8 @@ struct fit_handle *fit_open(const char *filename, bool verbose, handle->verify = verify; ret = read_file_2(filename, &handle->size, &handle->fit_alloc, - FILESIZE_MAX); - if (ret) { + max_size); + if (ret && ret != -EFBIG) { pr_err("unable to read %s: %s\n", filename, strerror(-ret)); return ERR_PTR(ret); } |