From d642f640bf39f9086a24920d97808fa40159a372 Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Tue, 8 Jan 2019 18:48:48 +0100 Subject: [PATCH] json: add flags parameter to json_parse_file(), for parsing "sensitive" data This will call json_variant_sensitive() internally while parsing for each allocated sub-variant. This is better than calling it a posteriori at the end, because partially parsed variants will always be properly erased from memory this way. --- src/fuzz/fuzz-json.c | 2 +- src/nspawn/nspawn-oci.c | 2 +- src/shared/json.c | 21 ++++++++++++++------- src/shared/json.h | 14 +++++++++----- src/shared/varlink.c | 2 +- src/test/test-json.c | 12 ++++++------ 6 files changed, 32 insertions(+), 21 deletions(-) diff --git a/src/fuzz/fuzz-json.c b/src/fuzz/fuzz-json.c index ce7b69dbb99..c01e2a570c5 100644 --- a/src/fuzz/fuzz-json.c +++ b/src/fuzz/fuzz-json.c @@ -18,7 +18,7 @@ int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) { f = fmemopen_unlocked((char*) data, size, "re"); assert_se(f); - if (json_parse_file(f, NULL, &v, NULL, NULL) < 0) + if (json_parse_file(f, NULL, 0, &v, NULL, NULL) < 0) return 0; g = open_memstream_unlocked(&out, &out_size); diff --git a/src/nspawn/nspawn-oci.c b/src/nspawn/nspawn-oci.c index 4519c74b95b..ba8b142c99e 100644 --- a/src/nspawn/nspawn-oci.c +++ b/src/nspawn/nspawn-oci.c @@ -2214,7 +2214,7 @@ int oci_load(FILE *f, const char *bundle, Settings **ret) { path = strjoina(bundle, "/config.json"); - r = json_parse_file(f, path, &oci, &line, &column); + r = json_parse_file(f, path, 0, &oci, &line, &column); if (r < 0) { if (line != 0 && column != 0) return log_error_errno(r, "Failed to parse '%s' at %u:%u: %m", path, line, column); diff --git a/src/shared/json.c b/src/shared/json.c index 377a2c455a2..98fa067ef4d 100644 --- a/src/shared/json.c +++ b/src/shared/json.c @@ -2479,6 +2479,7 @@ static void json_stack_release(JsonStack *s) { static int json_parse_internal( const char **input, JsonSource *source, + JsonParseFlags flags, JsonVariant **ret, unsigned *line, unsigned *column, @@ -2797,6 +2798,12 @@ static int json_parse_internal( } if (add) { + /* If we are asked to make this parsed object sensitive, then let's apply this + * immediately after allocating each variant, so that when we abort half-way + * everything we already allocated that is then freed is correctly marked. */ + if (FLAGS_SET(flags, JSON_PARSE_SENSITIVE)) + json_variant_sensitive(add); + (void) json_variant_set_source(&add, source, line_token, column_token); if (!GREEDY_REALLOC(current->elements, current->n_elements_allocated, current->n_elements + 1)) { @@ -2825,15 +2832,15 @@ finish: return r; } -int json_parse(const char *input, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) { - return json_parse_internal(&input, NULL, ret, ret_line, ret_column, false); +int json_parse(const char *input, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) { + return json_parse_internal(&input, NULL, flags, ret, ret_line, ret_column, false); } -int json_parse_continue(const char **p, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) { - return json_parse_internal(p, NULL, ret, ret_line, ret_column, true); +int json_parse_continue(const char **p, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) { + return json_parse_internal(p, NULL, flags, ret, ret_line, ret_column, true); } -int json_parse_file_at(FILE *f, int dir_fd, const char *path, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) { +int json_parse_file_at(FILE *f, int dir_fd, const char *path, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) { _cleanup_(json_source_unrefp) JsonSource *source = NULL; _cleanup_free_ char *text = NULL; const char *p; @@ -2855,7 +2862,7 @@ int json_parse_file_at(FILE *f, int dir_fd, const char *path, JsonVariant **ret, } p = text; - return json_parse_internal(&p, source, ret, ret_line, ret_column, false); + return json_parse_internal(&p, source, flags, ret, ret_line, ret_column, false); } int json_buildv(JsonVariant **ret, va_list ap) { @@ -3093,7 +3100,7 @@ int json_buildv(JsonVariant **ret, va_list ap) { /* Note that we don't care for current->n_suppress here, we should generate parsing * errors even in suppressed object properties */ - r = json_parse(l, &add, NULL, NULL); + r = json_parse(l, 0, &add, NULL, NULL); if (r < 0) goto finish; } else diff --git a/src/shared/json.h b/src/shared/json.h index dd1aff450ab..d3e44d1b043 100644 --- a/src/shared/json.h +++ b/src/shared/json.h @@ -174,12 +174,16 @@ int json_variant_filter(JsonVariant **v, char **to_remove); int json_variant_set_field(JsonVariant **v, const char *field, JsonVariant *value); -int json_parse(const char *string, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column); -int json_parse_continue(const char **p, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column); -int json_parse_file_at(FILE *f, int dir_fd, const char *path, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column); +typedef enum JsonParseFlags { + JSON_PARSE_SENSITIVE = 1 << 0, /* mark variant as "sensitive", i.e. something containing secret key material or such */ +} JsonParseFlags; -static inline int json_parse_file(FILE *f, const char *path, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) { - return json_parse_file_at(f, AT_FDCWD, path, ret, ret_line, ret_column); +int json_parse(const char *string, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column); +int json_parse_continue(const char **p, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column); +int json_parse_file_at(FILE *f, int dir_fd, const char *path, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column); + +static inline int json_parse_file(FILE *f, const char *path, JsonParseFlags flags, JsonVariant **ret, unsigned *ret_line, unsigned *ret_column) { + return json_parse_file_at(f, AT_FDCWD, path, flags, ret, ret_line, ret_column); } enum { diff --git a/src/shared/varlink.c b/src/shared/varlink.c index ee4fb9e8439..e1d956e6490 100644 --- a/src/shared/varlink.c +++ b/src/shared/varlink.c @@ -577,7 +577,7 @@ static int varlink_parse_message(Varlink *v) { varlink_log(v, "New incoming message: %s", begin); - r = json_parse(begin, &v->current, NULL, NULL); + r = json_parse(begin, 0, &v->current, NULL, NULL); if (r < 0) return r; diff --git a/src/test/test-json.c b/src/test/test-json.c index a6613043b92..90b91f6310c 100644 --- a/src/test/test-json.c +++ b/src/test/test-json.c @@ -82,7 +82,7 @@ static void test_variant(const char *data, Test test) { _cleanup_free_ char *s = NULL; int r; - r = json_parse(data, &v, NULL, NULL); + r = json_parse(data, 0, &v, NULL, NULL); assert_se(r == 0); assert_se(v); @@ -93,7 +93,7 @@ static void test_variant(const char *data, Test test) { log_info("formatted normally: %s\n", s); - r = json_parse(data, &w, NULL, NULL); + r = json_parse(data, JSON_PARSE_SENSITIVE, &w, NULL, NULL); assert_se(r == 0); assert_se(w); assert_se(json_variant_has_type(v, json_variant_type(w))); @@ -110,7 +110,7 @@ static void test_variant(const char *data, Test test) { log_info("formatted prettily:\n%s", s); - r = json_parse(data, &w, NULL, NULL); + r = json_parse(data, 0, &w, NULL, NULL); assert_se(r == 0); assert_se(w); @@ -302,7 +302,7 @@ static void test_build(void) { assert_se(json_variant_format(a, 0, &s) >= 0); log_info("GOT: %s\n", s); - assert_se(json_parse(s, &b, NULL, NULL) >= 0); + assert_se(json_parse(s, 0, &b, NULL, NULL) >= 0); assert_se(json_variant_equal(a, b)); a = json_variant_unref(a); @@ -313,7 +313,7 @@ static void test_build(void) { s = mfree(s); assert_se(json_variant_format(a, 0, &s) >= 0); log_info("GOT: %s\n", s); - assert_se(json_parse(s, &b, NULL, NULL) >= 0); + assert_se(json_parse(s, 0, &b, NULL, NULL) >= 0); assert_se(json_variant_format(b, 0, &t) >= 0); log_info("GOT: %s\n", t); @@ -365,7 +365,7 @@ static void test_source(void) { assert_se(f = fmemopen_unlocked((void*) data, strlen(data), "r")); - assert_se(json_parse_file(f, "waldo", &v, NULL, NULL) >= 0); + assert_se(json_parse_file(f, "waldo", 0, &v, NULL, NULL) >= 0); printf("--- non-pretty begin ---\n"); json_variant_dump(v, 0, stdout, NULL);