image: Check for unit addresses in FITs
Using unit addresses in a FIT is a security risk. Add a check for this and disallow it. CVE-2021-27138 Signed-off-by: Simon Glass <sjg@chromium.org> Reported-by: Bruce Monroe <bruce.monroe@intel.com> Reported-by: Arie Haenel <arie.haenel@intel.com> Reported-by: Julien Lenoir <julien.lenoir@intel.com>
This commit is contained in:
		
							parent
							
								
									124c255731
								
							
						
					
					
						commit
						3f04db891a
					
				| 
						 | 
					@ -1568,6 +1568,34 @@ int fit_image_check_comp(const void *fit, int noffset, uint8_t comp)
 | 
				
			||||||
	return (comp == image_comp);
 | 
						return (comp == image_comp);
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/**
 | 
				
			||||||
 | 
					 * fdt_check_no_at() - Check for nodes whose names contain '@'
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * This checks the parent node and all subnodes recursively
 | 
				
			||||||
 | 
					 *
 | 
				
			||||||
 | 
					 * @fit: FIT to check
 | 
				
			||||||
 | 
					 * @parent: Parent node to check
 | 
				
			||||||
 | 
					 * @return 0 if OK, -EADDRNOTAVAIL is a node has a name containing '@'
 | 
				
			||||||
 | 
					 */
 | 
				
			||||||
 | 
					static int fdt_check_no_at(const void *fit, int parent)
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
						const char *name;
 | 
				
			||||||
 | 
						int node;
 | 
				
			||||||
 | 
						int ret;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						name = fdt_get_name(fit, parent, NULL);
 | 
				
			||||||
 | 
						if (!name || strchr(name, '@'))
 | 
				
			||||||
 | 
							return -EADDRNOTAVAIL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fdt_for_each_subnode(node, fit, parent) {
 | 
				
			||||||
 | 
							ret = fdt_check_no_at(fit, node);
 | 
				
			||||||
 | 
							if (ret)
 | 
				
			||||||
 | 
								return ret;
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						return 0;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
int fit_check_format(const void *fit, ulong size)
 | 
					int fit_check_format(const void *fit, ulong size)
 | 
				
			||||||
{
 | 
					{
 | 
				
			||||||
	int ret;
 | 
						int ret;
 | 
				
			||||||
| 
						 | 
					@ -1589,10 +1617,27 @@ int fit_check_format(const void *fit, ulong size)
 | 
				
			||||||
		if (size == IMAGE_SIZE_INVAL)
 | 
							if (size == IMAGE_SIZE_INVAL)
 | 
				
			||||||
			size = fdt_totalsize(fit);
 | 
								size = fdt_totalsize(fit);
 | 
				
			||||||
		ret = fdt_check_full(fit, size);
 | 
							ret = fdt_check_full(fit, size);
 | 
				
			||||||
 | 
							if (ret)
 | 
				
			||||||
 | 
								ret = -EINVAL;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							/*
 | 
				
			||||||
 | 
							 * U-Boot stopped using unit addressed in 2017. Since libfdt
 | 
				
			||||||
 | 
							 * can match nodes ignoring any unit address, signature
 | 
				
			||||||
 | 
							 * verification can see the wrong node if one is inserted with
 | 
				
			||||||
 | 
							 * the same name as a valid node but with a unit address
 | 
				
			||||||
 | 
							 * attached. Protect against this by disallowing unit addresses.
 | 
				
			||||||
 | 
							 */
 | 
				
			||||||
 | 
							if (!ret && CONFIG_IS_ENABLED(FIT_SIGNATURE)) {
 | 
				
			||||||
 | 
								ret = fdt_check_no_at(fit, 0);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
								if (ret) {
 | 
				
			||||||
 | 
									log_debug("FIT check error %d\n", ret);
 | 
				
			||||||
 | 
									return ret;
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		if (ret) {
 | 
							if (ret) {
 | 
				
			||||||
			log_debug("FIT check error %d\n", ret);
 | 
								log_debug("FIT check error %d\n", ret);
 | 
				
			||||||
			return -EINVAL;
 | 
								return ret;
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -1955,10 +2000,13 @@ int fit_image_load(bootm_headers_t *images, ulong addr,
 | 
				
			||||||
	printf("## Loading %s from FIT Image at %08lx ...\n", prop_name, addr);
 | 
						printf("## Loading %s from FIT Image at %08lx ...\n", prop_name, addr);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bootstage_mark(bootstage_id + BOOTSTAGE_SUB_FORMAT);
 | 
						bootstage_mark(bootstage_id + BOOTSTAGE_SUB_FORMAT);
 | 
				
			||||||
	if (fit_check_format(fit, IMAGE_SIZE_INVAL)) {
 | 
						ret = fit_check_format(fit, IMAGE_SIZE_INVAL);
 | 
				
			||||||
		printf("Bad FIT %s image format!\n", prop_name);
 | 
						if (ret) {
 | 
				
			||||||
 | 
							printf("Bad FIT %s image format! (err=%d)\n", prop_name, ret);
 | 
				
			||||||
 | 
							if (CONFIG_IS_ENABLED(FIT_SIGNATURE) && ret == -EADDRNOTAVAIL)
 | 
				
			||||||
 | 
								printf("Signature checking prevents use of unit addresses (@) in nodes\n");
 | 
				
			||||||
		bootstage_error(bootstage_id + BOOTSTAGE_SUB_FORMAT);
 | 
							bootstage_error(bootstage_id + BOOTSTAGE_SUB_FORMAT);
 | 
				
			||||||
		return -ENOEXEC;
 | 
							return ret;
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	bootstage_mark(bootstage_id + BOOTSTAGE_SUB_FORMAT_OK);
 | 
						bootstage_mark(bootstage_id + BOOTSTAGE_SUB_FORMAT_OK);
 | 
				
			||||||
	if (fit_uname) {
 | 
						if (fit_uname) {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -232,8 +232,8 @@ def test_vboot(u_boot_console, sha_algo, padding, sign_options, required,
 | 
				
			||||||
        util.run_and_log(cons, [fit_check_sign, '-f', fit, '-k', dtb])
 | 
					        util.run_and_log(cons, [fit_check_sign, '-f', fit, '-k', dtb])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if full_test:
 | 
					        if full_test:
 | 
				
			||||||
            # Make sure that U-Boot checks that the config is in the list of hashed
 | 
					            # Make sure that U-Boot checks that the config is in the list of
 | 
				
			||||||
            # nodes. If it isn't, a security bypass is possible.
 | 
					            # hashed nodes. If it isn't, a security bypass is possible.
 | 
				
			||||||
            ffit = '%stest.forged.fit' % tmpdir
 | 
					            ffit = '%stest.forged.fit' % tmpdir
 | 
				
			||||||
            shutil.copyfile(fit, ffit)
 | 
					            shutil.copyfile(fit, ffit)
 | 
				
			||||||
            with open(ffit, 'rb') as fd:
 | 
					            with open(ffit, 'rb') as fd:
 | 
				
			||||||
| 
						 | 
					@ -263,10 +263,11 @@ def test_vboot(u_boot_console, sha_algo, padding, sign_options, required,
 | 
				
			||||||
            shutil.copyfile(fit, efit)
 | 
					            shutil.copyfile(fit, efit)
 | 
				
			||||||
            vboot_evil.add_evil_node(fit, efit, evil_kernel, 'kernel@')
 | 
					            vboot_evil.add_evil_node(fit, efit, evil_kernel, 'kernel@')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            msg = 'Signature checking prevents use of unit addresses (@) in nodes'
 | 
				
			||||||
            util.run_and_log_expect_exception(
 | 
					            util.run_and_log_expect_exception(
 | 
				
			||||||
                cons, [fit_check_sign, '-f', efit, '-k', dtb],
 | 
					                cons, [fit_check_sign, '-f', efit, '-k', dtb],
 | 
				
			||||||
                1, 'Node name contains @')
 | 
					                1, msg)
 | 
				
			||||||
            run_bootm(sha_algo, 'evil kernel@', 'Bad Data Hash', False, efit)
 | 
					            run_bootm(sha_algo, 'evil kernel@', msg, False, efit)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # Create a new properly signed fit and replace header bytes
 | 
					        # Create a new properly signed fit and replace header bytes
 | 
				
			||||||
        make_fit('sign-configs-%s%s.its' % (sha_algo, padding))
 | 
					        make_fit('sign-configs-%s%s.its' % (sha_algo, padding))
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue