mirror of
https://github.com/ibm-s390-linux/s390-tools.git
synced 2026-08-05 02:14:52 +00:00
The endianness handling is already done before, so there is no need for this function. Let's remove the useless `uint64_to_uint8_buf` function. Suggested-by: Steffen Eiden <seiden@linux.ibm.com> Signed-off-by: Marc Hartmayer <mhartmay@linux.ibm.com> Reviewed-by: Steffen Eiden <seiden@linux.ibm.com> Signed-off-by: Jan Höppner <hoeppner@linux.ibm.com>
434 lines
9.5 KiB
C
434 lines
9.5 KiB
C
/*
|
|
* PV component related definitions and functions
|
|
*
|
|
* Copyright IBM Corp. 2020
|
|
*
|
|
* s390-tools is free software; you can redistribute it and/or modify
|
|
* it under the terms of the MIT license. See LICENSE for details.
|
|
*/
|
|
|
|
#include <glib.h>
|
|
#include <glib/gtypes.h>
|
|
#include <openssl/bn.h>
|
|
#include <openssl/evp.h>
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
|
|
#include "boot/s390.h"
|
|
#include "common.h"
|
|
#include "utils/align.h"
|
|
#include "utils/buffer.h"
|
|
#include "utils/crypto.h"
|
|
#include "utils/file_utils.h"
|
|
|
|
#include "pv_comp.h"
|
|
#include "pv_error.h"
|
|
|
|
static void comp_file_free(CompFile *comp)
|
|
{
|
|
if (!comp)
|
|
return;
|
|
|
|
g_free(comp->path);
|
|
g_free(comp);
|
|
}
|
|
|
|
WRAPPED_G_DEFINE_AUTOPTR_CLEANUP_FUNC(CompFile, comp_file_free)
|
|
|
|
static PvComponent *pv_component_new(PvComponentType type, gsize size,
|
|
PvComponentDataType d_type, void **data,
|
|
GError **err)
|
|
{
|
|
g_autoptr(PvComponent) ret = g_new0(PvComponent, 1);
|
|
|
|
g_assert(type >= 0 && type <= UINT16_MAX);
|
|
|
|
ret->type = (int)type;
|
|
ret->d_type = (int)d_type;
|
|
ret->data = g_steal_pointer(data);
|
|
ret->orig_size = size;
|
|
|
|
if (generate_tweak(&ret->tweak, (uint16_t)type, err) < 0)
|
|
return NULL;
|
|
|
|
return g_steal_pointer(&ret);
|
|
}
|
|
|
|
PvComponent *pv_component_new_file(PvComponentType type, const gchar *path,
|
|
GError **err)
|
|
{
|
|
g_autoptr(CompFile) file = g_new0(CompFile, 1);
|
|
gsize size;
|
|
gint rc;
|
|
|
|
g_assert(path != NULL);
|
|
|
|
rc = file_size(path, &size, err);
|
|
if (rc < 0)
|
|
return NULL;
|
|
|
|
file->path = g_strdup(path);
|
|
file->size = size;
|
|
return pv_component_new(type, size, DATA_FILE, (void **)&file, err);
|
|
}
|
|
|
|
PvComponent *pv_component_new_buf(PvComponentType type, const PvBuffer *buf,
|
|
GError **err)
|
|
{
|
|
g_assert(buf);
|
|
|
|
g_autoptr(PvBuffer) dup_buf = pv_buffer_dup(buf, FALSE);
|
|
return pv_component_new(type, buf->size, DATA_BUFFER, (void **)&dup_buf,
|
|
err);
|
|
}
|
|
|
|
void pv_component_free(PvComponent *component)
|
|
{
|
|
if (!component)
|
|
return;
|
|
|
|
switch ((PvComponentDataType)component->d_type) {
|
|
case DATA_BUFFER:
|
|
pv_buffer_clear(&component->buf);
|
|
break;
|
|
case DATA_FILE:
|
|
comp_file_free(component->file);
|
|
break;
|
|
}
|
|
|
|
g_free(component);
|
|
}
|
|
|
|
gint pv_component_type(const PvComponent *component)
|
|
{
|
|
return component->type;
|
|
}
|
|
|
|
const gchar *pv_component_name(const PvComponent *component)
|
|
{
|
|
gint type = pv_component_type(component);
|
|
|
|
switch ((PvComponentType)type) {
|
|
case PV_COMP_TYPE_KERNEL:
|
|
return "kernel";
|
|
case PV_COMP_TYPE_INITRD:
|
|
return "ramdisk";
|
|
case PV_COMP_TYPE_CMDLINE:
|
|
return "parmline";
|
|
case PV_COMP_TYPE_STAGE3B:
|
|
return "stage3b";
|
|
}
|
|
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
uint64_t pv_component_size(const PvComponent *component)
|
|
{
|
|
switch ((PvComponentDataType)component->d_type) {
|
|
case DATA_BUFFER:
|
|
return component->buf->size;
|
|
case DATA_FILE:
|
|
return component->file->size;
|
|
}
|
|
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
uint64_t pv_component_get_src_addr(const PvComponent *component)
|
|
{
|
|
return component->src_addr;
|
|
}
|
|
|
|
uint64_t pv_component_get_orig_size(const PvComponent *component)
|
|
{
|
|
return component->orig_size;
|
|
}
|
|
|
|
uint64_t pv_component_get_tweak_prefix(const PvComponent *component)
|
|
{
|
|
return GUINT64_FROM_BE(component->tweak.cmp_idx.data);
|
|
}
|
|
|
|
gboolean pv_component_is_stage3b(const PvComponent *component)
|
|
{
|
|
return pv_component_type(component) == PV_COMP_TYPE_STAGE3B;
|
|
}
|
|
|
|
gint pv_component_align_and_encrypt(PvComponent *component, const gchar *tmp_path,
|
|
void *opaque, GError **err)
|
|
{
|
|
struct cipher_parms *parms = opaque;
|
|
|
|
switch ((PvComponentDataType)component->d_type) {
|
|
case DATA_BUFFER: {
|
|
g_autoptr(PvBuffer) enc_buf = NULL;
|
|
|
|
if (!(IS_PAGE_ALIGNED(pv_component_size(component)))) {
|
|
g_autoptr(PvBuffer) new = NULL;
|
|
|
|
/* create a page aligned copy */
|
|
new = pv_buffer_dup(component->buf, TRUE);
|
|
pv_buffer_clear(&component->buf);
|
|
component->buf = g_steal_pointer(&new);
|
|
}
|
|
enc_buf = encrypt_buf(parms, component->buf, err);
|
|
if (!enc_buf)
|
|
return -1;
|
|
|
|
pv_buffer_clear(&component->buf);
|
|
component->buf = g_steal_pointer(&enc_buf);
|
|
return 0;
|
|
}
|
|
case DATA_FILE: {
|
|
const gchar *comp_name = pv_component_name(component);
|
|
gchar *path_in = component->file->path;
|
|
g_autofree gchar *path_out = NULL;
|
|
gsize orig_size;
|
|
gsize prep_size;
|
|
|
|
g_assert(path_in);
|
|
|
|
path_out = g_build_filename(tmp_path, comp_name, NULL);
|
|
if (encrypt_file(parms, path_in, path_out, &orig_size,
|
|
&prep_size, err) < 0)
|
|
return -1;
|
|
|
|
if (component->orig_size != orig_size) {
|
|
g_set_error(err, G_FILE_ERROR, PV_ERROR_INTERNAL,
|
|
_("File has changed during the preparation '%s'"),
|
|
path_out);
|
|
return -1;
|
|
}
|
|
|
|
g_free(component->file->path);
|
|
component->file->size = prep_size;
|
|
component->file->path = g_steal_pointer(&path_out);
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
/* Page align the size of the component */
|
|
gint pv_component_align(PvComponent *component, const gchar *tmp_path,
|
|
void *opaque G_GNUC_UNUSED, GError **err)
|
|
{
|
|
if (IS_PAGE_ALIGNED(pv_component_size(component)))
|
|
return 0;
|
|
|
|
switch (component->d_type) {
|
|
case DATA_BUFFER: {
|
|
g_autoptr(PvBuffer) buf = NULL;
|
|
|
|
buf = pv_buffer_dup(component->buf, TRUE);
|
|
pv_buffer_clear(&component->buf);
|
|
component->buf = g_steal_pointer(&buf);
|
|
return 0;
|
|
} break;
|
|
case DATA_FILE: {
|
|
const gchar *comp_name = pv_component_name(component);
|
|
g_autofree gchar *path_out =
|
|
g_build_filename(tmp_path, comp_name, NULL);
|
|
gchar *path_in = component->file->path;
|
|
gsize size_out;
|
|
|
|
if (pad_file_right(path_out, path_in, &size_out, PAGE_SIZE,
|
|
err) < 0)
|
|
return -1;
|
|
|
|
g_free(component->file->path);
|
|
component->file->path = g_steal_pointer(&path_out);
|
|
component->file->size = size_out;
|
|
return 0;
|
|
} break;
|
|
}
|
|
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
int64_t pv_component_update_ald(const PvComponent *comp, EVP_MD_CTX *ctx,
|
|
GError **err)
|
|
{
|
|
uint64_t addr = pv_component_get_src_addr(comp);
|
|
uint64_t size = pv_component_size(comp);
|
|
uint64_t cur = addr;
|
|
int64_t nep = 0;
|
|
|
|
g_assert(IS_PAGE_ALIGNED(size) && size != 0);
|
|
|
|
do {
|
|
uint64_t cur_be = GUINT64_TO_BE(cur);
|
|
|
|
if (EVP_DigestUpdate(ctx, &cur_be, sizeof(cur_be)) != 1) {
|
|
g_set_error(err, PV_CRYPTO_ERROR,
|
|
PV_CRYPTO_ERROR_INTERNAL,
|
|
_("EVP_DigestUpdate failed"));
|
|
return -1;
|
|
}
|
|
|
|
cur += PAGE_SIZE;
|
|
nep++;
|
|
} while (cur < addr + size);
|
|
|
|
return nep;
|
|
}
|
|
|
|
int64_t pv_component_update_pld(const PvComponent *comp, EVP_MD_CTX *ctx,
|
|
GError **err)
|
|
{
|
|
uint64_t size = pv_component_size(comp);
|
|
int64_t nep = 0;
|
|
|
|
g_assert(IS_PAGE_ALIGNED(size) && size != 0);
|
|
|
|
switch (comp->d_type) {
|
|
case DATA_BUFFER: {
|
|
const PvBuffer *buf = comp->buf;
|
|
|
|
g_assert(buf->size <= INT64_MAX);
|
|
g_assert(buf->size == size);
|
|
|
|
if (EVP_DigestUpdate(ctx, buf->data, buf->size) != 1) {
|
|
g_set_error(err, PV_CRYPTO_ERROR,
|
|
PV_CRYPTO_ERROR_INTERNAL,
|
|
_("EVP_DigestUpdate failed"));
|
|
return -1;
|
|
}
|
|
|
|
nep = (int64_t)(buf->size / PAGE_SIZE);
|
|
break;
|
|
}
|
|
case DATA_FILE: {
|
|
const gchar *in_path = comp->file->path;
|
|
guchar in_buf[PAGE_SIZE];
|
|
gsize num_bytes_read_total = 0;
|
|
gsize num_bytes_read = 0;
|
|
FILE *f_in;
|
|
|
|
f_in = file_open(in_path, "rb", err);
|
|
if (!f_in)
|
|
return -1;
|
|
|
|
do {
|
|
/* Read data in blocks. Update the digest
|
|
* context each read.
|
|
*/
|
|
if (file_read(f_in, in_buf, sizeof(*in_buf),
|
|
sizeof(in_buf), &num_bytes_read,
|
|
err) < 0) {
|
|
fclose(f_in);
|
|
return -1;
|
|
}
|
|
num_bytes_read_total += num_bytes_read;
|
|
|
|
if (EVP_DigestUpdate(ctx, in_buf, sizeof(in_buf)) != 1) {
|
|
g_set_error(err, PV_CRYPTO_ERROR,
|
|
PV_CRYPTO_ERROR_INTERNAL,
|
|
_("EVP_DigestUpdate failed"));
|
|
fclose(f_in);
|
|
return -1;
|
|
}
|
|
|
|
nep++;
|
|
} while (num_bytes_read_total < pv_component_size(comp) &&
|
|
num_bytes_read != 0);
|
|
|
|
if (num_bytes_read_total != pv_component_size(comp)) {
|
|
g_set_error(err, G_FILE_ERROR, PV_ERROR_INTERNAL,
|
|
_("'%s' has changed during the preparation"),
|
|
in_path);
|
|
fclose(f_in);
|
|
return -1;
|
|
}
|
|
fclose(f_in);
|
|
break;
|
|
}
|
|
default:
|
|
g_assert_not_reached();
|
|
}
|
|
|
|
return nep;
|
|
}
|
|
|
|
int64_t pv_component_update_tld(const PvComponent *comp, EVP_MD_CTX *ctx,
|
|
GError **err)
|
|
{
|
|
uint64_t size = pv_component_size(comp);
|
|
const union tweak *tweak = &comp->tweak;
|
|
g_autoptr(BIGNUM) tweak_num = NULL;
|
|
int64_t nep = 0;
|
|
|
|
g_assert(IS_PAGE_ALIGNED(size) && size != 0);
|
|
|
|
tweak_num = BN_bin2bn(tweak->data, sizeof(tweak->data), NULL);
|
|
if (!tweak_num) {
|
|
g_set_error(err, PV_CRYPTO_ERROR,
|
|
PV_CRYPTO_ERROR_INTERNAL,
|
|
_("BN_bin2bn failed"));
|
|
return -1;
|
|
}
|
|
|
|
for (uint64_t cur = 0; cur < size; cur += PAGE_SIZE) {
|
|
guchar tmp[sizeof(tweak->data)] = { 0 };
|
|
|
|
g_assert(BN_num_bytes(tweak_num) >= 0);
|
|
g_assert(sizeof(tmp) - (guint)BN_num_bytes(tweak_num) > 0);
|
|
|
|
if (BN_bn2binpad(tweak_num, tmp, sizeof(tmp)) < 0) {
|
|
g_set_error(err, PV_CRYPTO_ERROR,
|
|
PV_CRYPTO_ERROR_INTERNAL,
|
|
_("BN_bn2binpad failed"));
|
|
return -1;
|
|
}
|
|
|
|
if (EVP_DigestUpdate(ctx, tmp, sizeof(tmp)) != 1) {
|
|
g_set_error(err, PV_CRYPTO_ERROR,
|
|
PV_CRYPTO_ERROR_INTERNAL,
|
|
_("EVP_DigestUpdate failed"));
|
|
return -1;
|
|
}
|
|
|
|
/* calculate new tweak value */
|
|
if (BN_add_word(tweak_num, PAGE_SIZE) != 1) {
|
|
g_set_error(err, PV_CRYPTO_ERROR,
|
|
PV_CRYPTO_ERROR_INTERNAL,
|
|
_("BN_add_word failed"));
|
|
return -1;
|
|
}
|
|
|
|
nep++;
|
|
}
|
|
|
|
return nep;
|
|
}
|
|
|
|
gint pv_component_write(const PvComponent *component, FILE *f, GError **err)
|
|
{
|
|
uint64_t offset = pv_component_get_src_addr(component);
|
|
|
|
g_assert(f);
|
|
|
|
switch (component->d_type) {
|
|
case DATA_BUFFER: {
|
|
const PvBuffer *buf = component->buf;
|
|
|
|
if (seek_and_write_buffer(f, buf, offset, err) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
case DATA_FILE: {
|
|
const CompFile *file = component->file;
|
|
|
|
if (seek_and_write_file(f, file, offset, err) < 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
g_assert_not_reached();
|
|
}
|