From: Luca Boccassi Date: Fri, 22 Dec 2023 22:23:20 +0000 (+0100) Subject: json: add JSON_FORMAT_REFUSE_SENSITIVE to json_variant_format() X-Git-Tag: v256-rc1~1307^2~1 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=fa9a6db478e3f0f2753e4633af6d0d4881707c2b;p=thirdparty%2Fsystemd.git json: add JSON_FORMAT_REFUSE_SENSITIVE to json_variant_format() Returns -EPERM if any node in the variant is marked as sensitive, useful to avoid leaking data to log messages and so on --- diff --git a/src/shared/json.c b/src/shared/json.c index af2fc9aa70c..15675a7046e 100644 --- a/src/shared/json.c +++ b/src/shared/json.c @@ -1771,6 +1771,28 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha return 0; } +static bool json_variant_is_sensitive_recursive(JsonVariant *v) { + if (!v) + return false; + if (json_variant_is_sensitive(v)) + return true; + if (v == JSON_VARIANT_MAGIC_EMPTY_ARRAY || + v == JSON_VARIANT_MAGIC_EMPTY_OBJECT) + return false; + if (!json_variant_is_regular(v)) + return false; + if (!IN_SET(v->type, JSON_VARIANT_ARRAY, JSON_VARIANT_OBJECT)) + return false; + if (v->is_reference) + return json_variant_is_sensitive_recursive(v->reference); + + for (size_t i = 0; i < json_variant_elements(v); i++) + if (json_variant_is_sensitive_recursive(json_variant_by_index(v, i))) + return true; + + return false; +} + int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret) { _cleanup_(memstream_done) MemStream m = {}; size_t sz; @@ -1786,6 +1808,10 @@ int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret) { if (flags & JSON_FORMAT_OFF) return -ENOEXEC; + if ((flags & JSON_FORMAT_REFUSE_SENSITIVE)) + if (json_variant_is_sensitive_recursive(v)) + return -EPERM; + f = memstream_init(&m); if (!f) return -ENOMEM; diff --git a/src/shared/json.h b/src/shared/json.h index c40c23487ab..975cf562a96 100644 --- a/src/shared/json.h +++ b/src/shared/json.h @@ -185,17 +185,18 @@ struct json_variant_foreach_state { int json_variant_get_source(JsonVariant *v, const char **ret_source, unsigned *ret_line, unsigned *ret_column); typedef enum JsonFormatFlags { - JSON_FORMAT_NEWLINE = 1 << 0, /* suffix with newline */ - JSON_FORMAT_PRETTY = 1 << 1, /* add internal whitespace to appeal to human readers */ - JSON_FORMAT_PRETTY_AUTO = 1 << 2, /* same, but only if connected to a tty (and JSON_FORMAT_NEWLINE otherwise) */ - JSON_FORMAT_COLOR = 1 << 3, /* insert ANSI color sequences */ - JSON_FORMAT_COLOR_AUTO = 1 << 4, /* insert ANSI color sequences if colors_enabled() says so */ - JSON_FORMAT_SOURCE = 1 << 5, /* prefix with source filename/line/column */ - JSON_FORMAT_SSE = 1 << 6, /* prefix/suffix with W3C server-sent events */ - JSON_FORMAT_SEQ = 1 << 7, /* prefix/suffix with RFC 7464 application/json-seq */ - JSON_FORMAT_FLUSH = 1 << 8, /* call fflush() after dumping JSON */ - JSON_FORMAT_EMPTY_ARRAY = 1 << 9, /* output "[]" for empty input */ - JSON_FORMAT_OFF = 1 << 10, /* make json_variant_format() fail with -ENOEXEC */ + JSON_FORMAT_NEWLINE = 1 << 0, /* suffix with newline */ + JSON_FORMAT_PRETTY = 1 << 1, /* add internal whitespace to appeal to human readers */ + JSON_FORMAT_PRETTY_AUTO = 1 << 2, /* same, but only if connected to a tty (and JSON_FORMAT_NEWLINE otherwise) */ + JSON_FORMAT_COLOR = 1 << 3, /* insert ANSI color sequences */ + JSON_FORMAT_COLOR_AUTO = 1 << 4, /* insert ANSI color sequences if colors_enabled() says so */ + JSON_FORMAT_SOURCE = 1 << 5, /* prefix with source filename/line/column */ + JSON_FORMAT_SSE = 1 << 6, /* prefix/suffix with W3C server-sent events */ + JSON_FORMAT_SEQ = 1 << 7, /* prefix/suffix with RFC 7464 application/json-seq */ + JSON_FORMAT_FLUSH = 1 << 8, /* call fflush() after dumping JSON */ + JSON_FORMAT_EMPTY_ARRAY = 1 << 9, /* output "[]" for empty input */ + JSON_FORMAT_OFF = 1 << 10, /* make json_variant_format() fail with -ENOEXEC */ + JSON_FORMAT_REFUSE_SENSITIVE = 1 << 11, /* return EPERM if any node in the tree is marked as senstitive */ } JsonFormatFlags; int json_variant_format(JsonVariant *v, JsonFormatFlags flags, char **ret); diff --git a/src/test/test-json.c b/src/test/test-json.c index c120a702c66..4ceb084c0c8 100644 --- a/src/test/test-json.c +++ b/src/test/test-json.c @@ -104,6 +104,17 @@ static void test_variant_one(const char *data, Test test) { assert_se(json_variant_has_type(w, json_variant_type(v))); assert_se(json_variant_equal(v, w)); + s = mfree(s); + r = json_variant_format(w, JSON_FORMAT_REFUSE_SENSITIVE, &s); + assert_se(r == -EPERM); + assert_se(!s); + + s = mfree(s); + r = json_variant_format(w, JSON_FORMAT_PRETTY, &s); + assert_se(r >= 0); + assert_se(s); + assert_se((size_t) r == strlen(s)); + s = mfree(s); w = json_variant_unref(w); @@ -813,4 +824,98 @@ TEST(json_dispatch) { assert_se(foobar.l == INT16_MIN); } +TEST(json_sensitive) { + _cleanup_(json_variant_unrefp) JsonVariant *a = NULL, *b = NULL, *v = NULL; + _cleanup_free_ char *s = NULL; + int r; + + assert_se(json_build(&a, JSON_BUILD_STRV(STRV_MAKE("foo", "bar", "baz", "bar", "baz", "foo", "qux", "baz"))) >= 0); + assert_se(json_build(&b, JSON_BUILD_STRV(STRV_MAKE("foo", "bar", "baz", "qux"))) >= 0); + + json_variant_sensitive(a); + + assert_se(json_variant_format(a, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM); + assert_se(!s); + + r = json_variant_format(b, JSON_FORMAT_REFUSE_SENSITIVE, &s); + assert_se(r >= 0); + assert_se(s); + assert_se((size_t) r == strlen(s)); + s = mfree(s); + + assert_se(json_build(&v, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)), + JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")), + JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT))) >= 0); + json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL); + + r = json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s); + assert_se(r >= 0); + assert_se(s); + assert_se((size_t) r == strlen(s)); + s = mfree(s); + v = json_variant_unref(v); + + assert_se(json_build(&v, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR_VARIANT("b", b), + JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)), + JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")), + JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT))) >= 0); + json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL); + + r = json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s); + assert_se(r >= 0); + assert_se(s); + assert_se((size_t) r == strlen(s)); + s = mfree(s); + v = json_variant_unref(v); + + assert_se(json_build(&v, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR_VARIANT("b", b), + JSON_BUILD_PAIR_VARIANT("a", a), + JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)), + JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")), + JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT))) >= 0); + json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL); + + assert_se(json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM); + assert_se(!s); + v = json_variant_unref(v); + + assert_se(json_build(&v, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR_VARIANT("b", b), + JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)), + JSON_BUILD_PAIR_VARIANT("a", a), + JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")), + JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT))) >= 0); + json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL); + + assert_se(json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM); + assert_se(!s); + v = json_variant_unref(v); + + assert_se(json_build(&v, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR_VARIANT("b", b), + JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)), + JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")), + JSON_BUILD_PAIR_VARIANT("a", a), + JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT))) >= 0); + json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL); + + assert_se(json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM); + assert_se(!s); + v = json_variant_unref(v); + + assert_se(json_build(&v, JSON_BUILD_OBJECT( + JSON_BUILD_PAIR_VARIANT("b", b), + JSON_BUILD_PAIR("c", JSON_BUILD_INTEGER(INT64_MIN)), + JSON_BUILD_PAIR("d", JSON_BUILD_STRING("-9223372036854775808")), + JSON_BUILD_PAIR("e", JSON_BUILD_EMPTY_OBJECT), + JSON_BUILD_PAIR_VARIANT("a", a))) >= 0); + json_variant_dump(v, JSON_FORMAT_COLOR|JSON_FORMAT_PRETTY, NULL, NULL); + + assert_se(json_variant_format(v, JSON_FORMAT_REFUSE_SENSITIVE, &s) == -EPERM); + assert_se(!s); +} + DEFINE_TEST_MAIN(LOG_DEBUG);