diff --git a/src/basic/iovec-util.h b/src/basic/iovec-util.h index 1f72898cba..1f652a0ede 100644 --- a/src/basic/iovec-util.h +++ b/src/basic/iovec-util.h @@ -5,6 +5,7 @@ #include #include +#include "alloc-util.h" #include "macro.h" size_t iovec_total_size(const struct iovec *i, size_t n); @@ -20,6 +21,27 @@ bool iovec_increment(struct iovec *i, size_t n, size_t k); IOVEC_MAKE((char*) _s, strlen(_s)); \ }) +#define TAKE_IOVEC(p) TAKE_GENERIC((p), struct iovec, IOVEC_NULL) + +static inline void iovec_done(struct iovec *iovec) { + /* A _cleanup_() helper that frees the iov_base in the iovec */ + assert(iovec); + + iovec->iov_base = mfree(iovec->iov_base); + iovec->iov_len = 0; +} + +static inline void iovec_done_erase(struct iovec *iovec) { + assert(iovec); + + iovec->iov_base = erase_and_free(iovec->iov_base); + iovec->iov_len = 0; +} + +static inline bool iovec_is_set(const struct iovec *iov) { + return iov && iov->iov_len > 0 && iov->iov_base; +} + char* set_iovec_string_field(struct iovec *iovec, size_t *n_iovec, const char *field, const char *value); char* set_iovec_string_field_free(struct iovec *iovec, size_t *n_iovec, const char *field, char *value); diff --git a/src/partition/repart.c b/src/partition/repart.c index 20c30c1e15..4e57f97726 100644 --- a/src/partition/repart.c +++ b/src/partition/repart.c @@ -268,8 +268,7 @@ typedef struct Partition { int read_only; int growfs; - uint8_t *roothash; - size_t roothash_size; + struct iovec roothash; char *split_name_format; char *split_path; @@ -418,7 +417,7 @@ static Partition* partition_free(Partition *p) { strv_free(p->subvolumes); free(p->verity_match_key); - free(p->roothash); + iovec_done(&p->roothash); free(p->split_name_format); unlink_and_free(p->split_path); @@ -2824,7 +2823,7 @@ static int context_dump_partitions(Context *context) { if (p->verity != VERITY_OFF) { Partition *hp = p->verity == VERITY_HASH ? p : p->siblings[VERITY_HASH]; - rh = hp->roothash ? hexmem(hp->roothash, hp->roothash_size) : strdup("TBD"); + rh = iovec_is_set(&hp->roothash) ? hexmem(hp->roothash.iov_base, hp->roothash.iov_len) : strdup("TBD"); if (!rh) return log_oom(); } @@ -3101,7 +3100,7 @@ static int context_dump_partition_bar(Context *context) { static bool context_has_roothash(Context *context) { LIST_FOREACH(partitions, p, context->partitions) - if (p->roothash) + if (iovec_is_set(&p->roothash)) return true; return false; @@ -3986,8 +3985,6 @@ static int partition_format_verity_hash( _cleanup_(partition_target_freep) PartitionTarget *t = NULL; _cleanup_(sym_crypt_freep) struct crypt_device *cd = NULL; _cleanup_free_ char *hint = NULL; - _cleanup_free_ uint8_t *rh = NULL; - size_t rhs; int r; assert(context); @@ -4066,30 +4063,31 @@ static int partition_format_verity_hash( r = sym_crypt_get_volume_key_size(cd); if (r < 0) return log_error_errno(r, "Failed to determine verity root hash size of partition %s: %m", strna(hint)); - rhs = (size_t) r; - rh = malloc(rhs); - if (!rh) + _cleanup_(iovec_done) struct iovec rh = { + .iov_base = malloc(r), + .iov_len = r, + }; + if (!rh.iov_base) return log_oom(); - r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, (char *) rh, &rhs, NULL, 0); + r = sym_crypt_volume_key_get(cd, CRYPT_ANY_SLOT, (char *) rh.iov_base, &rh.iov_len, NULL, 0); if (r < 0) return log_error_errno(r, "Failed to get verity root hash of partition %s: %m", strna(hint)); - assert(rhs >= sizeof(sd_id128_t) * 2); + assert(rh.iov_len >= sizeof(sd_id128_t) * 2); if (!dp->new_uuid_is_set) { - memcpy_safe(dp->new_uuid.bytes, rh, sizeof(sd_id128_t)); + memcpy_safe(dp->new_uuid.bytes, rh.iov_base, sizeof(sd_id128_t)); dp->new_uuid_is_set = true; } if (!p->new_uuid_is_set) { - memcpy_safe(p->new_uuid.bytes, rh + rhs - sizeof(sd_id128_t), sizeof(sd_id128_t)); + memcpy_safe(p->new_uuid.bytes, (uint8_t*) rh.iov_base + (rh.iov_len - sizeof(sd_id128_t)), sizeof(sd_id128_t)); p->new_uuid_is_set = true; } - p->roothash = TAKE_PTR(rh); - p->roothash_size = rhs; + p->roothash = TAKE_IOVEC(rh); return 0; #else @@ -4098,10 +4096,8 @@ static int partition_format_verity_hash( } static int sign_verity_roothash( - const uint8_t *roothash, - size_t roothash_size, - uint8_t **ret_signature, - size_t *ret_signature_size) { + const struct iovec *roothash, + struct iovec *ret_signature) { #if HAVE_OPENSSL _cleanup_(BIO_freep) BIO *rb = NULL; @@ -4111,11 +4107,10 @@ static int sign_verity_roothash( int sigsz; assert(roothash); - assert(roothash_size > 0); + assert(iovec_is_set(roothash)); assert(ret_signature); - assert(ret_signature_size); - hex = hexmem(roothash, roothash_size); + hex = hexmem(roothash->iov_base, roothash->iov_len); if (!hex) return log_oom(); @@ -4133,8 +4128,8 @@ static int sign_verity_roothash( return log_error_errno(SYNTHETIC_ERRNO(EIO), "Failed to convert PKCS7 signature to DER: %s", ERR_error_string(ERR_get_error(), NULL)); - *ret_signature = TAKE_PTR(sig); - *ret_signature_size = sigsz; + ret_signature->iov_base = TAKE_PTR(sig); + ret_signature->iov_len = sigsz; return 0; #else @@ -4144,11 +4139,10 @@ static int sign_verity_roothash( static int partition_format_verity_sig(Context *context, Partition *p) { _cleanup_(json_variant_unrefp) JsonVariant *v = NULL; - _cleanup_free_ uint8_t *sig = NULL; + _cleanup_(iovec_done) struct iovec sig = IOVEC_NULL; _cleanup_free_ char *text = NULL, *hint = NULL; Partition *hp; uint8_t fp[X509_FINGERPRINT_SIZE]; - size_t sigsz = 0; /* avoid false maybe-uninitialized warning */ int whole_fd, r; assert(p->verity == VERITY_SIG); @@ -4168,7 +4162,7 @@ static int partition_format_verity_sig(Context *context, Partition *p) { assert_se((whole_fd = fdisk_get_devfd(context->fdisk_context)) >= 0); - r = sign_verity_roothash(hp->roothash, hp->roothash_size, &sig, &sigsz); + r = sign_verity_roothash(&hp->roothash, &sig); if (r < 0) return r; @@ -4178,12 +4172,12 @@ static int partition_format_verity_sig(Context *context, Partition *p) { r = json_build(&v, JSON_BUILD_OBJECT( - JSON_BUILD_PAIR("rootHash", JSON_BUILD_HEX(hp->roothash, hp->roothash_size)), + JSON_BUILD_PAIR("rootHash", JSON_BUILD_HEX(hp->roothash.iov_base, hp->roothash.iov_len)), JSON_BUILD_PAIR( "certificateFingerprint", JSON_BUILD_HEX(fp, sizeof(fp)) ), - JSON_BUILD_PAIR("signature", JSON_BUILD_BASE64(sig, sigsz)) + JSON_BUILD_PAIR("signature", JSON_BUILD_IOVEC_BASE64(&sig)) ) ); if (r < 0) diff --git a/src/shared/json.c b/src/shared/json.c index 592ab35638..cc69998670 100644 --- a/src/shared/json.c +++ b/src/shared/json.c @@ -3790,6 +3790,34 @@ int json_buildv(JsonVariant **ret, va_list ap) { break; } + case _JSON_BUILD_IOVEC_BASE64: { + const struct iovec *iov; + + if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { + r = -EINVAL; + goto finish; + } + + iov = ASSERT_PTR(va_arg(ap, const struct iovec*)); + + if (current->n_suppress == 0) { + r = json_variant_new_base64(&add, iov->iov_base, iov->iov_len); + if (r < 0) + goto finish; + } + + n_subtract = 1; + + if (current->expect == EXPECT_TOPLEVEL) + current->expect = EXPECT_END; + else if (current->expect == EXPECT_OBJECT_VALUE) + current->expect = EXPECT_OBJECT_KEY; + else + assert(current->expect == EXPECT_ARRAY_ELEMENT); + + break; + } + case _JSON_BUILD_ID128: case _JSON_BUILD_UUID: { const sd_id128_t *id; @@ -4863,6 +4891,24 @@ int json_dispatch_unsupported(const char *name, JsonVariant *variant, JsonDispat return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not allowed in this object.", strna(name)); } +int json_dispatch_unbase64_iovec(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { + _cleanup_free_ void *buffer = NULL; + struct iovec *iov = ASSERT_PTR(userdata); + size_t sz; + int r; + + if (!json_variant_is_string(variant)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name)); + + r = json_variant_unbase64(variant, &buffer, &sz); + if (r < 0) + return json_log(variant, flags, r, "JSON field '%s' is not valid Base64 data.", strna(name)); + + free_and_replace(iov->iov_base, buffer); + iov->iov_len = sz; + return 0; +} + static int json_cmp_strings(const void *x, const void *y) { JsonVariant *const *a = x, *const *b = y; diff --git a/src/shared/json.h b/src/shared/json.h index f24fabfeb6..1cd3baaade 100644 --- a/src/shared/json.h +++ b/src/shared/json.h @@ -268,6 +268,7 @@ enum { _JSON_BUILD_STRV, _JSON_BUILD_STRV_ENV_PAIR, _JSON_BUILD_BASE64, + _JSON_BUILD_IOVEC_BASE64, _JSON_BUILD_BASE32HEX, _JSON_BUILD_HEX, _JSON_BUILD_OCTESCAPE, @@ -311,6 +312,7 @@ typedef int (*JsonBuildCallback)(JsonVariant **ret, const char *name, void *user #define JSON_BUILD_STRV(l) _JSON_BUILD_STRV, (char**) { l } #define JSON_BUILD_STRV_ENV_PAIR(l) _JSON_BUILD_STRV_ENV_PAIR, (char**) { l } #define JSON_BUILD_BASE64(p, n) _JSON_BUILD_BASE64, (const void*) { p }, (size_t) { n } +#define JSON_BUILD_IOVEC_BASE64(iov) _JSON_BUILD_IOVEC_BASE64, (const struct iovec*) { iov } #define JSON_BUILD_BASE32HEX(p, n) _JSON_BUILD_BASE32HEX, (const void*) { p }, (size_t) { n } #define JSON_BUILD_HEX(p, n) _JSON_BUILD_HEX, (const void*) { p }, (size_t) { n } #define JSON_BUILD_OCTESCAPE(p, n) _JSON_BUILD_OCTESCAPE, (const void*) { p }, (size_t) { n } @@ -341,6 +343,7 @@ typedef int (*JsonBuildCallback)(JsonVariant **ret, const char *name, void *user #define JSON_BUILD_PAIR_LITERAL(name, l) JSON_BUILD_PAIR(name, JSON_BUILD_LITERAL(l)) #define JSON_BUILD_PAIR_STRV(name, l) JSON_BUILD_PAIR(name, JSON_BUILD_STRV(l)) #define JSON_BUILD_PAIR_BASE64(name, p, n) JSON_BUILD_PAIR(name, JSON_BUILD_BASE64(p, n)) +#define JSON_BUILD_PAIR_IOVEC_BASE64(name, iov) JSON_BUILD_PAIR(name, JSON_BUILD_IOVEC_BASE64(iov)) #define JSON_BUILD_PAIR_HEX(name, p, n) JSON_BUILD_PAIR(name, JSON_BUILD_HEX(p, n)) #define JSON_BUILD_PAIR_ID128(name, id) JSON_BUILD_PAIR(name, JSON_BUILD_ID128(id)) #define JSON_BUILD_PAIR_UUID(name, id) JSON_BUILD_PAIR(name, JSON_BUILD_UUID(id)) @@ -412,6 +415,7 @@ int json_dispatch_uid_gid(const char *name, JsonVariant *variant, JsonDispatchFl int json_dispatch_user_group_name(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); int json_dispatch_id128(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); int json_dispatch_unsupported(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); +int json_dispatch_unbase64_iovec(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata); assert_cc(sizeof(uint32_t) == sizeof(unsigned)); #define json_dispatch_uint json_dispatch_uint32