From 684a2342645f3b44b823a907f0309eb4fcfba50b Mon Sep 17 00:00:00 2001 From: Christian Grothoff Date: Sun, 9 May 2021 13:41:23 +0200 Subject: fix #6855 --- src/json/json.c | 98 +++++++++++++++++++++++++++++++++++++++++++++++++--- src/json/test_json.c | 69 ++++++++++++++++++++++++++++++++++++ 2 files changed, 163 insertions(+), 4 deletions(-) (limited to 'src') diff --git a/src/json/json.c b/src/json/json.c index e9fbb91f8..4b91c0c52 100644 --- a/src/json/json.c +++ b/src/json/json.c @@ -17,6 +17,7 @@ * @file json/json.c * @brief helper functions for JSON processing using libjansson * @author Sree Harsha Totakura + * @author Christian Grothoff */ #include "platform.h" #include @@ -24,6 +25,41 @@ #include "taler_json_lib.h" +/** + * Check if @a json contains a 'real' value anywhere. + * + * @param json json to check + * @return true if a real is in it somewhere + */ +static bool +contains_real (const json_t *json) +{ + if (json_is_real (json)) + return true; + if (json_is_object (json)) + { + json_t *member; + const char *name; + + json_object_foreach ((json_t *) json, name, member) + if (contains_real (member)) + return true; + return false; + } + if (json_is_array (json)) + { + json_t *member; + size_t index; + + json_array_foreach ((json_t *) json, index, member) + if (contains_real (member)) + return true; + return false; + } + return false; +} + + /** * Dump the @a json to a string and hash it. * @@ -41,7 +77,16 @@ dump_and_hash (const json_t *json, char *wire_enc; size_t len; - GNUNET_break (NULL != json); + if (NULL == json) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } + if (contains_real (json)) + { + GNUNET_break (0); + return GNUNET_SYSERR; + } if (NULL == (wire_enc = json_dumps (json, JSON_ENCODE_ANY | JSON_COMPACT @@ -79,7 +124,7 @@ dump_and_hash (const json_t *json, /** - * Replace "forgettable" parts of a JSON object with its salted hash. + * Replace "forgettable" parts of a JSON object with their salted hash. * * @param[in] in some JSON value * @return NULL on error @@ -87,6 +132,12 @@ dump_and_hash (const json_t *json, static json_t * forget (const json_t *in) { + if (json_is_real (in)) + { + /* floating point is not allowed! */ + GNUNET_break (0); + return NULL; + } if (json_is_array (in)) { /* array is a JSON array */ @@ -202,6 +253,16 @@ forget (const json_t *in) return NULL; } json_decref (t); + /* scrub salt */ + if (0 != + json_object_del (fg, + key)) + { + GNUNET_break (0); + json_decref (ret); + json_decref (rx); + return NULL; + } if (NULL == rx) rx = json_object (); if (NULL == rx) @@ -268,8 +329,11 @@ TALER_JSON_contract_hash (const json_t *json, { int ret; json_t *cjson; + json_t *dc; - cjson = forget (json); + dc = json_deep_copy (json); + cjson = forget (dc); + json_decref (dc); if (NULL == cjson) { GNUNET_break (0); @@ -295,9 +359,26 @@ TALER_JSON_contract_mark_forgettable (json_t *json, GNUNET_break (0); return GNUNET_SYSERR; } + /* check field name is legal for forgettable field */ + for (const char *f = field; '\0' != *f; f++) + { + char c = *f; + + if ( (c >= 'a') && (c <= 'z') ) + continue; + if ( (c >= 'A') && (c <= 'Z') ) + continue; + if ( (c >= '0') && (c <= '9') ) + continue; + if ('_' == c) + continue; + GNUNET_break (0); + return GNUNET_SYSERR; + } if (NULL == json_object_get (json, field)) { + /* field must exist */ GNUNET_break (0); return GNUNET_SYSERR; } @@ -335,7 +416,7 @@ int TALER_JSON_contract_part_forget (json_t *json, const char *field) { - const json_t *fg; + json_t *fg; const json_t *part; json_t *fp; json_t *rx; @@ -391,6 +472,15 @@ TALER_JSON_contract_part_forget (json_t *json, return GNUNET_SYSERR; } json_decref (fp); + /* drop salt */ + if (0 != + json_object_del (fg, + field)) + { + json_decref (fp); + GNUNET_break (0); + return GNUNET_SYSERR; + } rx = json_object_get (json, "_forgotten"); diff --git a/src/json/test_json.c b/src/json/test_json.c index 066ba4e73..21073d697 100644 --- a/src/json/test_json.c +++ b/src/json/test_json.c @@ -104,12 +104,49 @@ test_contract (void) GNUNET_assert (GNUNET_OK == TALER_JSON_contract_mark_forgettable (c1, "k1")); + GNUNET_assert (GNUNET_OK == + TALER_JSON_contract_mark_forgettable (c1, + "k2")); GNUNET_assert (GNUNET_OK == TALER_JSON_contract_hash (c1, &h1)); GNUNET_assert (GNUNET_OK == TALER_JSON_contract_part_forget (c1, "k1")); + /* check salt was forgotten */ + GNUNET_assert (NULL == + json_object_get (json_object_get (c1, + "_forgettable"), + "k1")); + GNUNET_assert (GNUNET_OK == + TALER_JSON_contract_hash (c1, + &h2)); + if (0 != + GNUNET_memcmp (&h1, + &h2)) + { + GNUNET_break (0); + json_decref (c1); + return 1; + } + GNUNET_assert (GNUNET_OK == + TALER_JSON_contract_part_forget (json_object_get (c1, + "k2"), + "n1")); + GNUNET_assert (GNUNET_OK == + TALER_JSON_contract_hash (c1, + &h2)); + if (0 != + GNUNET_memcmp (&h1, + &h2)) + { + GNUNET_break (0); + json_decref (c1); + return 1; + } + GNUNET_assert (GNUNET_OK == + TALER_JSON_contract_part_forget (c1, + "k2")); GNUNET_assert (GNUNET_OK == TALER_JSON_contract_hash (c1, &h2)); @@ -121,6 +158,38 @@ test_contract (void) GNUNET_break (0); return 1; } + + + c1 = json_pack ("{s:I, s:{s:s}, s:{s:b, s:{s:s}}, s:{s:s}}", + "k1", 1, + "_forgettable", "k1", "SALT", + "k2", "n1", true, + /***/ "_forgettable", "n1", "salt", + "k3", "n1", "string"); + GNUNET_assert (GNUNET_OK == + TALER_JSON_contract_hash (c1, + &h1)); + json_dumpf (c1, stderr, JSON_INDENT (2)); + json_decref (c1); + { + char *s; + + s = GNUNET_STRINGS_data_to_string_alloc (&h1, + sizeof (h1)); + if (0 != + strcmp (s, + "287VXK8T6PXKD05W8Y94QJNEFCMRXBC9S7KNKTWGH2G2J2D7RYKPSHNH1HG9NT1K2HRHGC67W6QM6GEC4BSN1DPNEBCS0AVDT2DBP5G")) + { + GNUNET_log (GNUNET_ERROR_TYPE_ERROR, + "Invalid reference hash: %s\n", + s); + GNUNET_free (s); + return 1; + } + GNUNET_free (s); + } + + c2 = json_pack ("{s:s}", "n1", "n2"); GNUNET_assert (NULL != c2); -- cgit v1.2.3