From: Stephan Bosch Date: Sat, 22 Feb 2020 11:35:25 +0000 (+0100) Subject: lib-oauth2: Use the new lib-json X-Git-Tag: 2.4.0~2365 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=cec88df0bb0f110afadac110325c3e1007ff7c4d;p=thirdparty%2Fdovecot%2Fcore.git lib-oauth2: Use the new lib-json --- diff --git a/src/lib-oauth2/Makefile.am b/src/lib-oauth2/Makefile.am index 0403ce1c99..5db401c258 100644 --- a/src/lib-oauth2/Makefile.am +++ b/src/lib-oauth2/Makefile.am @@ -1,6 +1,7 @@ AM_CPPFLAGS = \ -I$(top_srcdir)/src/lib \ -I$(top_srcdir)/src/lib-test \ + -I$(top_srcdir)/src/lib-json \ -I$(top_srcdir)/src/lib-http \ -I$(top_srcdir)/src/lib-dcrypt \ -I$(top_srcdir)/src/lib-dict \ @@ -21,6 +22,8 @@ liboauth2_la_SOURCES = \ oauth2-request.c \ oauth2-jwt.c \ oauth2-key-cache.c +liboauth2_la_DEPENDENCIES = \ + ../lib-json/libjson.la test_programs = \ test-oauth2-json \ @@ -63,7 +66,8 @@ test_oauth2_json_DEPENDENCIES = $(test_deps) test_oauth2_jwt_SOURCES = test-oauth2-jwt.c test_oauth2_jwt_LDADD = $(test_libs) if HAVE_WHOLE_ARCHIVE -test_oauth2_jwt_LDFLAGS = -Wl,$(LD_WHOLE_ARCHIVE),../lib-ssl-iostream/.libs/libssl_iostream.a,$(LD_NO_WHOLE_ARCHIVE) +test_oauth2_jwt_LDFLAGS = \ + -Wl,$(LD_WHOLE_ARCHIVE),../lib-json/.libs/libjson.a,../lib-ssl-iostream/.libs/libssl_iostream.a,$(LD_NO_WHOLE_ARCHIVE) endif test_oauth2_jwt_DEPENDENCIES = $(test_deps) @@ -71,4 +75,3 @@ check-local: for bin in $(test_programs); do \ if ! $(RUN_TEST) ./$$bin; then exit 1; fi; \ done - diff --git a/src/lib-oauth2/oauth2-jwt.c b/src/lib-oauth2/oauth2-jwt.c index 67d9f3099b..21c807a90c 100644 --- a/src/lib-oauth2/oauth2-jwt.c +++ b/src/lib-oauth2/oauth2-jwt.c @@ -9,7 +9,7 @@ #include "hash-method.h" #include "istream.h" #include "iso8601-date.h" -#include "json-tree.h" +#include "json-tree.new.h" #include "array.h" #include "base64.h" #include "str-sanitize.h" @@ -24,11 +24,13 @@ static const struct json_tree_node * get_field_node(const struct json_tree *tree, const char *key) { - const struct json_tree_node *root = json_tree_root(tree); - const struct json_tree_node *value_node = json_tree_find_key(root, key); + const struct json_tree_node *root = json_tree_get_root_const(tree); + const struct json_tree_node *value_node; - if (value_node == NULL || value_node->value_type == JSON_TYPE_OBJECT || - value_node->value_type == JSON_TYPE_ARRAY) + value_node = json_tree_node_get_member(root, key); + if (value_node == NULL || + json_tree_node_is_object(value_node) || + json_tree_node_is_array(value_node)) return NULL; return value_node; @@ -43,30 +45,34 @@ get_field(const struct json_tree *tree, const char *key, if (value_node == NULL) return NULL; if (type_r != NULL) - *type_r = value_node->value_type; - return json_tree_get_value_str(value_node); + *type_r = json_tree_node_get_type(value_node); + return json_tree_node_as_str(value_node); } static const char * get_field_multiple(const struct json_tree *tree, const char *key) { - const struct json_tree_node *root = json_tree_root(tree); - const struct json_tree_node *value_node = json_tree_find_key(root, key); + const struct json_tree_node *root = json_tree_get_root_const(tree); + const struct json_tree_node *value_node; - if (value_node == NULL || value_node->value_type == JSON_TYPE_OBJECT) + value_node = json_tree_node_get_member(root, key); + if (value_node == NULL || json_tree_node_is_object(value_node)) return NULL; - if (value_node->value_type != JSON_TYPE_ARRAY) - return json_tree_get_value_str(value_node); + if (!json_tree_node_is_array(value_node)) + return json_tree_node_as_str(value_node); - const struct json_tree_node *entry_node = json_tree_get_child(value_node); + const struct json_tree_node *entry_node; + + entry_node = json_tree_node_get_child(value_node); string_t *values = t_str_new(64); - for (; entry_node != NULL; entry_node = entry_node->next) { - if (entry_node->value_type == JSON_TYPE_OBJECT || - entry_node->value_type == JSON_TYPE_ARRAY) + for (; entry_node != NULL; + entry_node = json_tree_node_get_next(entry_node)) { + if (json_tree_node_is_object(entry_node) || + json_tree_node_is_array(entry_node)) continue; - const char *value_str = json_tree_get_value_str(entry_node); + const char *value_str = json_tree_node_as_str(entry_node); if (str_len(values) > 0) str_append_c(values, '\t'); @@ -76,30 +82,32 @@ get_field_multiple(const struct json_tree *tree, const char *key) } static int -get_time_field(const struct json_tree *tree, const char *key, int64_t *value_r) +get_time_field(const struct json_tree *tree, const char *key, + int64_t *value_r) { - time_t tvalue; - enum json_type value_type; - const char *value = get_field(tree, key, &value_type); - int tz_offset ATTR_UNUSED; + const struct json_tree_node *value_node = get_field_node(tree, key); - if (value == NULL) + if (value_node == NULL) return 0; + + enum json_type value_type = json_tree_node_get_type(value_node); + if (value_type == JSON_TYPE_NUMBER) { - /* Parse with atof() to handle the json valid exponential - formats, but discard the decimal part of the fields as we are - not interested in them. - - The worst case of x.99999 would appear as almost a second - older than the actual x which is same as saying we processed - it a second later for the purpose of JWT tokens */ - double v = atof(value); - *value_r = (int64_t) v; - if (*value_r < 0) + int64_t tstamp; + + if (json_tree_node_get_int64(value_node, &tstamp) < 0 || + tstamp < 0) return -1; + *value_r = tstamp; return 1; - } else if (iso8601_date_parse((const unsigned char*)value, - strlen(value), &tvalue, &tz_offset)) { + } + + const char *value = json_tree_node_as_str(value_node); + time_t tvalue; + int tz_offset ATTR_UNUSED; + + if (iso8601_date_parse((const unsigned char*)value, strlen(value), + &tvalue, &tz_offset)) { if (tvalue < 0) return -1; *value_r = tvalue; @@ -356,7 +364,8 @@ struct jwt_node { }; static void -oauth2_jwt_copy_fields(ARRAY_TYPE(oauth2_field) *fields, struct json_tree *tree) +oauth2_jwt_copy_fields(ARRAY_TYPE(oauth2_field) *fields, + struct json_tree *tree) { pool_t pool = array_get_pool(fields); ARRAY(struct jwt_node) nodes; @@ -366,24 +375,26 @@ oauth2_jwt_copy_fields(ARRAY_TYPE(oauth2_field) *fields, struct json_tree *tree) struct jwt_node *root = array_append_space(&nodes); root->prefix = ""; - root->root = json_tree_root(tree); + root->root = json_tree_get_root_const(tree); while (array_count(&nodes) > 0) { const struct jwt_node *subroot = array_front(&nodes); - const struct json_tree_node *node = subroot->root; + const struct json_tree_node *tnode = subroot->root; + + while (tnode != NULL) { + const struct json_node *jnode = + json_tree_node_get(tnode); - while (node != NULL) { - if (node->value_type == JSON_TYPE_OBJECT || - node->value_type == JSON_TYPE_ARRAY) { + if (!json_node_is_singular(jnode)) { root = array_append_space(&nodes); - root->root = node->value.child; - root->array = node->value_type == JSON_TYPE_ARRAY; - if (node->key == NULL) + root->root = json_tree_node_get_child(tnode); + root->array = json_node_is_array(jnode); + if (jnode->name == NULL) root->prefix = subroot->prefix; else if (*subroot->prefix != '\0') - root->prefix = t_strconcat(subroot->prefix, node->key, "_", NULL); + root->prefix = t_strconcat(subroot->prefix, jnode->name, "_", NULL); else - root->prefix = t_strconcat(node->key, "_", NULL); + root->prefix = t_strconcat(jnode->name, "_", NULL); } else { struct oauth2_field *field; const char *name; @@ -404,10 +415,10 @@ oauth2_jwt_copy_fields(ARRAY_TYPE(oauth2_field) *fields, struct json_tree *tree) } } else { field = array_append_space(fields); - field->name = p_strconcat(pool, subroot->prefix, node->key, NULL); + field->name = p_strconcat(pool, subroot->prefix, jnode->name, NULL); } - const char *value = str_tabescape(json_tree_get_value_str(node)); + const char *value = str_tabescape(json_node_as_str(jnode)); if (field->value != NULL) { field->value = p_strconcat(pool, field->value, "\t", value, NULL); @@ -415,7 +426,7 @@ oauth2_jwt_copy_fields(ARRAY_TYPE(oauth2_field) *fields, struct json_tree *tree) field->value = p_strdup(pool, value); } } - node = node->next; + tnode = json_tree_node_get_next(tnode); } array_pop_front(&nodes); } @@ -452,10 +463,11 @@ static bool check_scope(const char *req, const char *got) } static int -oauth2_jwt_body_process(const struct oauth2_settings *set, const char *alg, - const char *kid, ARRAY_TYPE(oauth2_field) *fields, - struct json_tree *tree, const char *const *blobs, - const char **error_r) +oauth2_jwt_body_process(const struct oauth2_settings *set, + const char *alg, const char *kid, + ARRAY_TYPE(oauth2_field) *fields, + struct json_tree *tree, + const char *const *blobs, const char **error_r) { const char *sub = get_field(tree, "sub", NULL); int64_t t0 = time(NULL); @@ -600,7 +612,7 @@ int oauth2_try_parse_jwt(const struct oauth2_settings *set, const char *alg, *kid; ret = oauth2_jwt_header_process(header_tree, &alg, &kid, error_r); - json_tree_deinit(&header_tree); + json_tree_unref(&header_tree); if (ret < 0) return -1; @@ -624,7 +636,7 @@ int oauth2_try_parse_jwt(const struct oauth2_settings *set, return -1; ret = oauth2_jwt_body_process(set, alg, kid, fields, body_tree, blobs, error_r); - json_tree_deinit(&body_tree); + json_tree_unref(&body_tree); return ret; } diff --git a/src/lib-oauth2/oauth2-private.h b/src/lib-oauth2/oauth2-private.h index 038f9e14a3..2a8e74916e 100644 --- a/src/lib-oauth2/oauth2-private.h +++ b/src/lib-oauth2/oauth2-private.h @@ -9,7 +9,7 @@ struct oauth2_request { const struct oauth2_settings *set; struct http_client_request *req; - struct json_parser *parser; + struct json_istream *json_istream; struct istream *is; struct io *io; @@ -22,7 +22,6 @@ struct oauth2_request { void (*json_parsed_cb)(struct oauth2_request *, const char *error); ARRAY_TYPE(oauth2_field) fields; - char *field_name; oauth2_request_callback_t *req_callback; void *req_context; @@ -31,7 +30,7 @@ struct oauth2_request { }; void oauth2_request_parse_json(struct oauth2_request *req); -int oauth2_json_tree_build(const buffer_t *json, struct json_tree **tree_r, +int oauth2_json_tree_build(const buffer_t *json, struct json_tree **jtree_r, const char **error_r); int oauth2_validation_key_cache_lookup_pubkey( diff --git a/src/lib-oauth2/oauth2-request.c b/src/lib-oauth2/oauth2-request.c index 462e828b01..eb1bbdc35f 100644 --- a/src/lib-oauth2/oauth2-request.c +++ b/src/lib-oauth2/oauth2-request.c @@ -6,12 +6,16 @@ #include "str.h" #include "http-client.h" #include "http-url.h" -#include "json-parser.h" +#include "json-istream.h" #include "oauth2.h" #include "oauth2-private.h" static void oauth2_request_free(struct oauth2_request *req) { + json_istream_destroy(&req->json_istream); + io_remove(&req->io); + i_stream_unref(&req->is); + timeout_remove(&req->to_delayed_error); pool_unref(&req->pool); } @@ -85,50 +89,48 @@ static int request_field_cmp(const char *key, const struct oauth2_field *field) void oauth2_request_parse_json(struct oauth2_request *req) { - enum json_type type; - const char *token, *error; + struct json_node jnode; + struct json_istream *jinput = req->json_istream; + const char *error; int ret; - while((ret = json_parse_next(req->parser, &type, &token)) > 0) { - if (req->field_name == NULL) { - if (type != JSON_TYPE_OBJECT_KEY) break; - /* cannot use t_strdup because we might - have to read more */ - req->field_name = p_strdup(req->pool, token); - } else if (type < JSON_TYPE_STRING) { - /* this should be last allocation */ - p_free(req->pool, req->field_name); - json_parse_skip(req->parser); - } else { + ret = 1; + while (ret > 0) { + ret = json_istream_read_next(jinput, &jnode); + if (ret <= 0) + break; + i_assert(jnode.name != NULL); + if (json_node_is_singular(&jnode)) { if (!array_is_created(&req->fields)) p_array_init(&req->fields, req->pool, 4); struct oauth2_field *field = array_append_space(&req->fields); - field->name = req->field_name; - req->field_name = NULL; - field->value = p_strdup(req->pool, token); + field->name = p_strdup(req->pool, jnode.name); + field->value = p_strdup(req->pool, + json_node_get_str(&jnode)); } } - /* read more */ - if (ret == 0) return; - - io_remove(&req->io); + if (ret == 0) { + /* need more data */ + return; + } - if (ret > 0) { - (void)json_parser_deinit(&req->parser, &error); - error = "Invalid response data"; - } else if (i_stream_read_eof(req->is) && - req->is->v_offset == 0 && req->is->stream_errno == 0) { + if (i_stream_read_eof(req->is) && + req->is->v_offset == 0 && req->is->stream_errno == 0) { /* discard error, empty response is OK. */ - (void)json_parser_deinit(&req->parser, &error); - error = NULL; - } else if (json_parser_deinit(&req->parser, &error) == 0) { error = NULL; } else { - i_assert(error != NULL); + ret = json_istream_finish(&req->json_istream, &error); + i_assert(ret != 0); + if (ret < 0) { + error = t_strdup_printf("Invalid JSON in response: %s", + error); + } } + json_istream_destroy(&req->json_istream); + io_remove(&req->io); i_stream_unref(&req->is); /* check if fields contain error now */ @@ -171,7 +173,8 @@ oauth2_request_response(const struct http_response *response, } p_array_init(&req->fields, req->pool, 1); - req->parser = json_parser_init(req->is); + req->json_istream = json_istream_create_object( + req->is, NULL, JSON_PARSER_FLAG_NUMBERS_AS_STRING); req->json_parsed_cb = oauth2_request_continue; req->io = io_add_istream(req->is, oauth2_request_parse_json, req); oauth2_request_parse_json(req); diff --git a/src/lib-oauth2/oauth2.c b/src/lib-oauth2/oauth2.c index b96995eab2..1911fbbf25 100644 --- a/src/lib-oauth2/oauth2.c +++ b/src/lib-oauth2/oauth2.c @@ -3,34 +3,15 @@ #include "lib.h" #include "buffer.h" #include "istream.h" -#include "json-tree.h" +#include "json-istream.h" +#include "json-tree-io.h" #include "oauth2.h" #include "oauth2-private.h" -int oauth2_json_tree_build(const buffer_t *json, struct json_tree **tree_r, +int oauth2_json_tree_build(const buffer_t *json, struct json_tree **jtree_r, const char **error_r) { - struct istream *is = i_stream_create_from_buffer(json); - struct json_parser *parser = json_parser_init(is); - struct json_tree *tree = json_tree_init(); - enum json_type type; - const char *value; - int ret; - - while ((ret = json_parse_next(parser, &type, &value)) > 0) { - /* this is safe to reuse here because it gets rewritten in while - loop */ - ret = json_tree_append(tree, type, value); - i_assert(ret == 0); - } - i_assert(ret != 0); - ret = json_parser_deinit(&parser, error_r); - i_stream_unref(&is); - if (ret != 0) - json_tree_deinit(&tree); - else - *tree_r = tree; - return ret; + return json_tree_read_buffer(json, 0, jtree_r, error_r); } bool oauth2_valid_token(const char *token) diff --git a/src/lib-oauth2/test-oauth2-json.c b/src/lib-oauth2/test-oauth2-json.c index 3e50931b2f..d5222bf439 100644 --- a/src/lib-oauth2/test-oauth2-json.c +++ b/src/lib-oauth2/test-oauth2-json.c @@ -2,7 +2,8 @@ #include "lib.h" #include "array.h" -#include "json-parser.h" +#include "istream.h" +#include "json-istream.h" #include "oauth2.h" #include "oauth2-private.h" #include "test-common.h" @@ -70,7 +71,8 @@ static void test_oauth2_json_valid(void) req->pool = pool; p_array_init(&req->fields, req->pool, 1); req->is = test_istream_create_data(test_input, strlen(test_input)); - req->parser = json_parser_init(req->is); + req->json_istream = json_istream_create_object( + req->is, NULL, JSON_PARSER_FLAG_NUMBERS_AS_STRING); req->json_parsed_cb = test_oauth_json_valid_parsed; cb_got_called = FALSE; @@ -124,7 +126,8 @@ static void test_oauth2_json_error(void) req->pool = pool; p_array_init(&req->fields, req->pool, 1); req->is = test_istream_create_data(test_input_1, strlen(test_input_1)); - req->parser = json_parser_init(req->is); + req->json_istream = json_istream_create_object( + req->is, NULL, JSON_PARSER_FLAG_NUMBERS_AS_STRING); req->req_context = "invalid_request"; req->json_parsed_cb = test_oauth_json_has_error; cb_got_called = FALSE; @@ -145,7 +148,8 @@ static void test_oauth2_json_error(void) req->pool = pool; p_array_init(&req->fields, req->pool, 1); req->is = test_istream_create_data(test_input_2, strlen(test_input_2)); - req->parser = json_parser_init(req->is); + req->json_istream = json_istream_create_object( + req->is, NULL, JSON_PARSER_FLAG_NUMBERS_AS_STRING); req->req_context = "Access denied"; req->json_parsed_cb = test_oauth_json_has_error; cb_got_called = FALSE; diff --git a/src/lib-oauth2/test-oauth2-jwt.c b/src/lib-oauth2/test-oauth2-jwt.c index 541b49216d..2a966c81ef 100644 --- a/src/lib-oauth2/test-oauth2-jwt.c +++ b/src/lib-oauth2/test-oauth2-jwt.c @@ -9,8 +9,8 @@ #include "base64.h" #include "randgen.h" #include "array.h" -#include "json-parser.h" #include "iso8601-date.h" +#include "json-generator.h" #include "oauth2.h" #include "oauth2-private.h" #include "dcrypt.h" @@ -756,7 +756,7 @@ static void test_jwt_nested_fields(void) "\"aud\":[\"dacity\",\"iron\"]," "\"user\":{" "\"name\":\"test\"," - "\"features\":[\"one\",\"two\",\"tap\tdance\"]," + "\"features\":[\"one\",\"two\",\"tap\\tdance\"]," "\"enabled\":true," "\"locations\":{\"shared\":true,\"private\":false}" "},"