diff options
Diffstat (limited to 'src/shared/json.c')
-rw-r--r-- | src/shared/json.c | 187 |
1 files changed, 176 insertions, 11 deletions
diff --git a/src/shared/json.c b/src/shared/json.c index 06c9e85..4af34e5 100644 --- a/src/shared/json.c +++ b/src/shared/json.c @@ -14,13 +14,16 @@ #include "fd-util.h" #include "fileio.h" #include "float.h" +#include "glyph-util.h" #include "hexdecoct.h" +#include "iovec-util.h" #include "json-internal.h" #include "json.h" #include "macro.h" #include "math-util.h" #include "memory-util.h" #include "memstream-util.h" +#include "path-util.h" #include "set.h" #include "string-table.h" #include "string-util.h" @@ -87,6 +90,9 @@ struct JsonVariant { /* Erase from memory when freeing */ bool sensitive:1; + /* True if we know that any referenced json object is marked sensitive */ + bool recursive_sensitive:1; + /* If this is an object the fields are strictly ordered by name */ bool sorted:1; @@ -559,7 +565,7 @@ static int _json_variant_array_put_element(JsonVariant *array, JsonVariant *elem return -ELNRNG; if (d >= array->depth) array->depth = d + 1; - array->n_elements ++; + array->n_elements++; *w = (JsonVariant) { .is_embedded = true, @@ -1451,6 +1457,33 @@ bool json_variant_is_sensitive(JsonVariant *v) { return v->sensitive; } +bool json_variant_is_sensitive_recursive(JsonVariant *v) { + if (!v) + return false; + if (json_variant_is_sensitive(v)) + return true; + if (!json_variant_is_regular(v)) + return false; + if (v->recursive_sensitive) /* Already checked this before */ + return true; + if (!IN_SET(v->type, JSON_VARIANT_ARRAY, JSON_VARIANT_OBJECT)) + return false; + if (v->is_reference) { + if (!json_variant_is_sensitive_recursive(v->reference)) + return false; + + return (v->recursive_sensitive = true); + } + + for (size_t i = 0; i < json_variant_elements(v); i++) + if (json_variant_is_sensitive_recursive(json_variant_by_index(v, i))) + return (v->recursive_sensitive = true); + + /* Note: we only cache the result here in case true, since we allow all elements down the tree to + * have their sensitive flag toggled later on (but never off) */ + return false; +} + static void json_variant_propagate_sensitive(JsonVariant *from, JsonVariant *to) { if (json_variant_is_sensitive(from)) json_variant_sensitive(to); @@ -1580,6 +1613,15 @@ static int json_format(FILE *f, JsonVariant *v, JsonFormatFlags flags, const cha assert(f); assert(v); + if (FLAGS_SET(flags, JSON_FORMAT_CENSOR_SENSITIVE) && json_variant_is_sensitive(v)) { + if (flags & JSON_FORMAT_COLOR) + fputs(ansi_red(), f); + fputs("\"<sensitive data>\"", f); + if (flags & JSON_FORMAT_COLOR) + fputs(ANSI_NORMAL, f); + return 0; + } + switch (json_variant_type(v)) { case JSON_VARIANT_REAL: { @@ -2310,7 +2352,7 @@ static int json_variant_copy(JsonVariant **nv, JsonVariant *v) { source = json_variant_string(v); k = strnlen(source, INLINE_STRING_MAX + 1); if (k <= INLINE_STRING_MAX) { - k ++; + k++; break; } @@ -2594,7 +2636,7 @@ static int json_parse_string(const char **p, char **ret) { return -ENOMEM; s[n++] = ch; - c ++; + c++; continue; } @@ -3790,7 +3832,8 @@ int json_buildv(JsonVariant **ret, va_list ap) { break; } - case _JSON_BUILD_IOVEC_BASE64: { + case _JSON_BUILD_IOVEC_BASE64: + case _JSON_BUILD_IOVEC_HEX: { const struct iovec *iov; if (!IN_SET(current->expect, EXPECT_TOPLEVEL, EXPECT_OBJECT_VALUE, EXPECT_ARRAY_ELEMENT)) { @@ -3798,10 +3841,14 @@ int json_buildv(JsonVariant **ret, va_list ap) { goto finish; } - iov = ASSERT_PTR(va_arg(ap, const struct iovec*)); + iov = va_arg(ap, const struct iovec*); if (current->n_suppress == 0) { - r = json_variant_new_base64(&add, iov->iov_base, iov->iov_len); + if (iov) + r = command == _JSON_BUILD_IOVEC_BASE64 ? json_variant_new_base64(&add, iov->iov_base, iov->iov_len) : + json_variant_new_hex(&add, iov->iov_base, iov->iov_len); + else + r = json_variant_new_string(&add, ""); if (r < 0) goto finish; } @@ -4574,7 +4621,7 @@ int json_dispatch_full( } } - done ++; + done++; } else { /* Didn't find a matching entry! ☹️ */ @@ -4589,11 +4636,15 @@ int json_dispatch_full( return r; } else - done ++; + done++; } else { - json_log(value, flags, 0, "Unexpected object field '%s'.", json_variant_string(key)); + if (flags & JSON_ALLOW_EXTENSIONS) { + json_log(value, flags|JSON_DEBUG, 0, "Unrecognized object field '%s', assuming extension.", json_variant_string(key)); + continue; + } + json_log(value, flags, 0, "Unexpected object field '%s'.", json_variant_string(key)); if (flags & JSON_PERMISSIVE) continue; @@ -4762,6 +4813,42 @@ int json_dispatch_uint16(const char *name, JsonVariant *variant, JsonDispatchFla return 0; } +int json_dispatch_int8(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { + int8_t *i = ASSERT_PTR(userdata); + int64_t i64; + int r; + + assert(variant); + + r = json_dispatch_int64(name, variant, flags, &i64); + if (r < 0) + return r; + + if (i64 < INT8_MIN || i64 > INT8_MAX) + return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds.", strna(name)); + + *i = (int8_t) i64; + return 0; +} + +int json_dispatch_uint8(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { + uint8_t *u = ASSERT_PTR(userdata); + uint64_t u64; + int r; + + assert(variant); + + r = json_dispatch_uint64(name, variant, flags, &u64); + if (r < 0) + return r; + + if (u64 > UINT8_MAX) + return json_log(variant, flags, SYNTHETIC_ERRNO(ERANGE), "JSON field '%s' out of bounds.", strna(name)); + + *u = (uint8_t) u64; + return 0; +} + int json_dispatch_string(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { char **s = ASSERT_PTR(userdata); int r; @@ -4920,6 +5007,27 @@ int json_dispatch_user_group_name(const char *name, JsonVariant *variant, JsonDi return 0; } +int json_dispatch_absolute_path(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { + const char *path; + char **p = ASSERT_PTR(userdata); + + assert(variant); + + if (!json_variant_is_string(variant)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a string.", strna(name)); + + path = json_variant_string(variant); + if (!path_is_valid(path)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not a valid path.", strna(name)); + if (!path_is_absolute(path)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' must be an absolute path.", strna(name)); + + if (free_and_strdup(p, path) < 0) + return json_log_oom(variant, flags); + + return 0; +} + int json_dispatch_id128(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { sd_id128_t *uuid = userdata; int r; @@ -4961,6 +5069,63 @@ int json_dispatch_unbase64_iovec(const char *name, JsonVariant *variant, JsonDis return 0; } +int json_dispatch_byte_array_iovec(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { + _cleanup_free_ uint8_t *buffer = NULL; + struct iovec *iov = ASSERT_PTR(userdata); + size_t sz, k = 0; + + assert(variant); + + if (!json_variant_is_array(variant)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is not an array.", strna(name)); + + sz = json_variant_elements(variant); + + buffer = new(uint8_t, sz + 1); + if (!buffer) + return json_log(variant, flags, SYNTHETIC_ERRNO(ENOMEM), "Out of memory."); + + JsonVariant *i; + JSON_VARIANT_ARRAY_FOREACH(i, variant) { + uint64_t b; + + if (!json_variant_is_unsigned(i)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "Element %zu of JSON field '%s' is not an unsigned integer.", k, strna(name)); + + b = json_variant_unsigned(i); + if (b > 0xff) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), + "Element %zu of JSON field '%s' is out of range 0%s255.", + k, strna(name), special_glyph(SPECIAL_GLYPH_ELLIPSIS)); + + buffer[k++] = (uint8_t) b; + } + assert(k == sz); + + /* Append a NUL byte for safety, like we do in memdup_suffix0() and others. */ + buffer[sz] = 0; + + free_and_replace(iov->iov_base, buffer); + iov->iov_len = sz; + return 0; +} + +int json_dispatch_in_addr(const char *name, JsonVariant *variant, JsonDispatchFlags flags, void *userdata) { + struct in_addr *address = ASSERT_PTR(userdata); + _cleanup_(iovec_done) struct iovec iov = {}; + int r; + + r = json_dispatch_byte_array_iovec(name, variant, flags, &iov); + if (r < 0) + return r; + + if (iov.iov_len != sizeof(struct in_addr)) + return json_log(variant, flags, SYNTHETIC_ERRNO(EINVAL), "JSON field '%s' is array of unexpected size.", strna(name)); + + memcpy(address, iov.iov_base, iov.iov_len); + return 0; +} + static int json_cmp_strings(const void *x, const void *y) { JsonVariant *const *a = x, *const *b = y; @@ -5107,14 +5272,14 @@ int json_variant_unbase64(JsonVariant *v, void **ret, size_t *ret_size) { if (!json_variant_is_string(v)) return -EINVAL; - return unbase64mem(json_variant_string(v), SIZE_MAX, ret, ret_size); + return unbase64mem_full(json_variant_string(v), SIZE_MAX, /* secure= */ json_variant_is_sensitive(v), ret, ret_size); } int json_variant_unhex(JsonVariant *v, void **ret, size_t *ret_size) { if (!json_variant_is_string(v)) return -EINVAL; - return unhexmem(json_variant_string(v), SIZE_MAX, ret, ret_size); + return unhexmem_full(json_variant_string(v), SIZE_MAX, /* secure= */ json_variant_is_sensitive(v), ret, ret_size); } static const char* const json_variant_type_table[_JSON_VARIANT_TYPE_MAX] = { |