blob: 4c79408882c4623d1a6abd883e56dcc4a11833a1 [file] [log] [blame]
/* Copyright 2022 The ChromiumOS Authors
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "2common.h"
#include "2crypto.h"
#include "2return_codes.h"
#include "cbfstool.h"
#include "host_misc.h"
#include "subprocess.h"
#include "vboot_host.h"
static const char *get_cbfstool_path(void)
{
static const char *cbfstool = NULL;
if (cbfstool)
return cbfstool;
const char *env_cbfstool = getenv(ENV_CBFSTOOL);
if (env_cbfstool && env_cbfstool[0] != '\0') {
cbfstool = strdup(env_cbfstool);
return cbfstool;
}
cbfstool = DEFAULT_CBFSTOOL;
return cbfstool;
}
vb2_error_t cbfstool_truncate(const char *file, const char *region,
size_t *new_size)
{
int status;
char output_buffer[128];
const char *cbfstool = get_cbfstool_path();
struct subprocess_target output = {
.type = TARGET_BUFFER_NULL_TERMINATED,
.buffer = {
.buf = output_buffer,
.size = sizeof(output_buffer),
},
};
const char *const argv[] = {
cbfstool, file, "truncate", "-r", region, NULL,
};
status = subprocess_run(argv, &subprocess_null, &output,
&subprocess_null);
if (status < 0) {
fprintf(stderr, "%s(): cbfstool invocation failed: %m\n",
__func__);
exit(1);
}
/* Positive exit code means something is wrong with image. Return zero
as new size, because it might be problem with missing CBFS.*/
if (status > 0) {
*new_size = 0;
return VB2_ERROR_CBFSTOOL;
}
if (sscanf(output_buffer, "%zi", new_size) != 1) {
VB2_DEBUG("Failed to parse command output. Unexpected "
"output.\n");
*new_size = 0;
return VB2_ERROR_CBFSTOOL;
}
return VB2_SUCCESS;
}
/* Requires null-terminated buffer */
static vb2_error_t extract_metadata_hash(const char *buf, struct vb2_hash *hash)
{
enum vb2_hash_algorithm algo;
const char *to_find = "\n[METADATA HASH]";
char *algo_str = NULL;
char *hash_str = NULL;
vb2_error_t rv = VB2_ERROR_CBFSTOOL;
const char *start = strstr(buf, to_find);
if (start)
start += strlen(to_find);
if (start) {
const int matches = sscanf(start, " %m[^:\n\t ]:%m[^:\n\t ]",
&algo_str, &hash_str);
if (matches < 2)
goto done;
if (!algo_str || !vb2_lookup_hash_alg(algo_str, &algo) ||
algo == VB2_HASH_INVALID)
goto done;
hash->algo = algo;
if (!hash_str ||
strlen(hash_str) != (vb2_digest_size(algo) * 2) ||
!parse_hash(&hash->raw[0], vb2_digest_size(algo), hash_str))
goto done;
if (!strstr(buf, "]\tfully valid"))
goto done;
rv = VB2_SUCCESS;
}
done:
if (rv != VB2_SUCCESS)
hash->algo = VB2_HASH_INVALID;
free(algo_str);
free(hash_str);
return rv;
}
vb2_error_t cbfstool_get_metadata_hash(const char *file, const char *region,
struct vb2_hash *hash)
{
int status;
const char *cbfstool = get_cbfstool_path();
const size_t data_buffer_sz = 1024 * 1024;
char *data_buffer = malloc(data_buffer_sz);
vb2_error_t rv = VB2_ERROR_CBFSTOOL;
if (!data_buffer)
goto done;
memset(hash, 0, sizeof(*hash));
hash->algo = VB2_HASH_INVALID;
struct subprocess_target output = {
.type = TARGET_BUFFER_NULL_TERMINATED,
.buffer = {
.buf = data_buffer,
.size = data_buffer_sz,
},
};
const char *argv[] = {
cbfstool, file, "print", "-kv",
region ? "-r" : NULL, region, NULL
};
status = subprocess_run(argv, &subprocess_null, &output,
&subprocess_null);
if (status < 0) {
fprintf(stderr, "%s(): cbfstool invocation failed: %m\n",
__func__);
exit(1);
}
if (status > 0)
goto done;
rv = extract_metadata_hash(data_buffer, hash);
done:
free(data_buffer);
return rv;
}
/* Requires null-terminated buffer */
static char *extract_config_value(const char *buf, const char *config_field)
{
char *to_find = NULL;
if (asprintf(&to_find, "\n%s=", config_field) == -1) {
fprintf(stderr, "Out of Memory\n");
VB2_DIE("Out of memory\n");
}
const char *start = strstr(buf, to_find);
if (start)
start += strlen(to_find);
free(to_find);
if (start) {
char *end = strchr(start, '\n');
if (end)
return strndup(start, end - start);
}
return NULL;
}
static vb2_error_t get_config_value(const char *file, const char *region,
const char *config_field, char **value)
{
int status;
const char *cbfstool = get_cbfstool_path();
const size_t data_buffer_sz = 1024 * 1024;
char *data_buffer = malloc(data_buffer_sz);
vb2_error_t rv = VB2_ERROR_CBFSTOOL;
*value = NULL;
if (!data_buffer)
goto done;
struct subprocess_target output = {
.type = TARGET_BUFFER_NULL_TERMINATED,
.buffer = {
.buf = data_buffer,
.size = data_buffer_sz,
},
};
const char *argv[] = {
cbfstool, file, "extract", "-n", "config", "-f", "/dev/stdout",
region ? "-r" : NULL, region, NULL
};
status = subprocess_run(argv, &subprocess_null, &output,
&subprocess_null);
if (status < 0) {
fprintf(stderr, "%s(): cbfstool invocation failed: %m\n",
__func__);
exit(1);
}
if (status > 0)
goto done;
*value = extract_config_value(data_buffer, config_field);
rv = VB2_SUCCESS;
done:
free(data_buffer);
return rv;
}
vb2_error_t cbfstool_get_config_bool(const char *file, const char *region,
const char *config_field, bool *value)
{
vb2_error_t rv;
char *raw_value = NULL;
*value = false;
rv = get_config_value(file, region, config_field, &raw_value);
if (rv)
return rv;
if (raw_value && strcmp(raw_value, "y") == 0)
*value = true;
free(raw_value);
return VB2_SUCCESS;
}
vb2_error_t cbfstool_get_config_string(const char *file, const char *region,
const char *config_field, char **value)
{
vb2_error_t rv;
char *raw_value = NULL;
*value = NULL;
rv = get_config_value(file, region, config_field, &raw_value);
if (rv)
return rv;
/*
* With CL:4085654, all the valid configs of type 'str' should be
* present in the "config" CBFS file.
*/
if (!raw_value) {
VB2_DEBUG("Config '%s' not found in %s.", config_field, file);
return VB2_ERROR_CBFSTOOL;
}
size_t len = strlen(raw_value);
if (len < 2 || raw_value[0] != '"' || raw_value[len - 1] != '"') {
VB2_DEBUG("%s value '%s' is not enclosed with double quotes.",
config_field, raw_value);
rv = VB2_ERROR_CBFSTOOL;
goto done;
}
*value = strndup(&raw_value[1], len - 2);
done:
free(raw_value);
return rv;
}