From: Lennart Poettering Date: Tue, 8 Jan 2019 17:33:32 +0000 (+0100) Subject: json: add a new "sensitive" flags for JsonVariant objects X-Git-Tag: v245-rc1~249^2~21 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=83bc6cb792330760efd02f1c9df80d3bcfae1453;p=thirdparty%2Fsystemd.git json: add a new "sensitive" flags for JsonVariant objects An object marked with this flag will be erased from memory when it is freed. This is useful for dealing with sensitive data (key material, passphrases) encoded in JSON objects. --- diff --git a/src/shared/json.c b/src/shared/json.c index d85cc9f116e..b9a4fe4e23e 100644 --- a/src/shared/json.c +++ b/src/shared/json.c @@ -71,6 +71,9 @@ struct JsonVariant { /* While comparing two arrays, we use this for marking what we already have seen */ bool is_marked:1; + /* Ersase from memory when freeing */ + bool sensitive:1; + /* The current 'depth' of the JsonVariant, i.e. how many levels of member variants this has */ uint16_t depth; @@ -650,7 +653,46 @@ int json_variant_new_object(JsonVariant **ret, JsonVariant **array, size_t n) { return 0; } -static void json_variant_free_inner(JsonVariant *v) { +static size_t json_variant_size(JsonVariant* v) { + + if (!json_variant_is_regular(v)) + return 0; + + if (v->is_reference) + return offsetof(JsonVariant, reference) + sizeof(JsonVariant*); + + switch (v->type) { + + case JSON_VARIANT_STRING: + return offsetof(JsonVariant, string) + strlen(v->string) + 1; + + case JSON_VARIANT_REAL: + return offsetof(JsonVariant, value) + sizeof(long double); + + case JSON_VARIANT_UNSIGNED: + return offsetof(JsonVariant, value) + sizeof(uintmax_t); + + case JSON_VARIANT_INTEGER: + return offsetof(JsonVariant, value) + sizeof(intmax_t); + + case JSON_VARIANT_BOOLEAN: + return offsetof(JsonVariant, value) + sizeof(bool); + + case JSON_VARIANT_ARRAY: + case JSON_VARIANT_OBJECT: + return offsetof(JsonVariant, n_elements) + sizeof(size_t); + + case JSON_VARIANT_NULL: + return offsetof(JsonVariant, value); + + default: + assert_not_reached("unexpected type"); + } +} + +static void json_variant_free_inner(JsonVariant *v, bool force_sensitive) { + bool sensitive; + assert(v); if (!json_variant_is_regular(v)) @@ -658,7 +700,12 @@ static void json_variant_free_inner(JsonVariant *v) { json_source_unref(v->source); + sensitive = v->sensitive || force_sensitive; + if (v->is_reference) { + if (sensitive) + json_variant_sensitive(v->reference); + json_variant_unref(v->reference); return; } @@ -667,8 +714,11 @@ static void json_variant_free_inner(JsonVariant *v) { size_t i; for (i = 0; i < v->n_elements; i++) - json_variant_free_inner(v + 1 + i); + json_variant_free_inner(v + 1 + i, sensitive); } + + if (sensitive) + explicit_bzero_safe(v, json_variant_size(v)); } JsonVariant *json_variant_ref(JsonVariant *v) { @@ -700,7 +750,7 @@ JsonVariant *json_variant_unref(JsonVariant *v) { v->n_ref--; if (v->n_ref == 0) { - json_variant_free_inner(v); + json_variant_free_inner(v, false); free(v); } } @@ -1242,6 +1292,26 @@ bool json_variant_equal(JsonVariant *a, JsonVariant *b) { } } +void json_variant_sensitive(JsonVariant *v) { + assert(v); + + /* Marks a variant as "sensitive", so that it is erased from memory when it is destroyed. This is a + * one-way operation: as soon as it is marked this way it remains marked this way until it's + * destoryed. A magic variant is never sensitive though, even when asked, since it's too + * basic. Similar, const string variant are never sensitive either, after all they are included in + * the source code as they are, which is not suitable for inclusion of secrets. + * + * Note that this flag has a recursive effect: when we destroy an object or array we'll propagate the + * flag to all contained variants. And if those are then destroyed this is propagated further down, + * and so on. */ + + v = json_variant_normalize(v); + if (!json_variant_is_regular(v)) + return; + + v->sensitive = true; +} + int json_variant_get_source(JsonVariant *v, const char **ret_source, unsigned *ret_line, unsigned *ret_column) { assert_return(v, -EINVAL); diff --git a/src/shared/json.h b/src/shared/json.h index 0a9b2e111ae..8e589a0b842 100644 --- a/src/shared/json.h +++ b/src/shared/json.h @@ -130,6 +130,8 @@ JsonVariant *json_variant_by_key_full(JsonVariant *v, const char *key, JsonVaria bool json_variant_equal(JsonVariant *a, JsonVariant *b); +void json_variant_sensitive(JsonVariant *v); + struct json_variant_foreach_state { JsonVariant *variant; size_t idx;