]> git.ipfire.org Git - thirdparty/systemd.git/commitdiff
json: add JSON_FORMAT_REFUSE_SENSITIVE to json_variant_format()
authorLuca Boccassi <bluca@debian.org>
Fri, 22 Dec 2023 22:23:20 +0000 (23:23 +0100)
committerLuca Boccassi <bluca@debian.org>
Wed, 3 Jan 2024 10:54:48 +0000 (11:54 +0100)
Returns -EPERM if any node in the variant is marked as sensitive,
useful to avoid leaking data to log messages and so on

src/shared/json.c
src/shared/json.h
src/test/test-json.c

index af2fc9aa70c10dc2f0d72e2d50488f47cc454ed7..15675a7046ea84b19658ece1b68c1ad61e4f4384 100644 (file)
@@ -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;
index c40c23487ab97fd5899e284d42e35835cc04585a..975cf562a9640421bc5baa7fddb0d8e658138fd3 100644 (file)
@@ -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);
index c120a702c66a634f2d30e4beaa07e718bbe39623..4ceb084c0c81fdcfa822aaa8e46d7a0309ea8713 100644 (file)
@@ -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);