summaryrefslogtreecommitdiffstats
path: root/src/shared/json.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/shared/json.c')
-rw-r--r--src/shared/json.c187
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] = {