1
0
mirror of https://github.com/postgres/postgres.git synced 2025-10-22 14:32:25 +03:00

oauth: Limit JSON parsing depth in the client

Check the ctx->nested level as we go, to prevent a server from running
the client out of stack space.

The limit we choose when communicating with authorization servers can't
be overly strict, since those servers will continue to add extensions in
their JSON documents which we need to correctly ignore. For the SASL
communication, we can be more conservative, since there are no defined
extensions (and the peer is probably more Postgres code).

Reviewed-by: Aleksander Alekseev <aleksander@timescale.com>
Discussion: https://postgr.es/m/CAOYmi%2Bm71aRUEi0oQE9ciBnBS8xVtMn3CifaPu2kmJzUfhOZgA%40mail.gmail.com
This commit is contained in:
Jacob Champion
2025-05-23 13:05:33 -07:00
parent 1ca583f6c0
commit cbc8fd0c9a
4 changed files with 97 additions and 7 deletions

View File

@@ -82,6 +82,20 @@
*/
#define MAX_OAUTH_RESPONSE_SIZE (256 * 1024)
/*
* Similarly, a limit on the maximum JSON nesting level keeps a server from
* running us out of stack space. A common nesting level in practice is 2 (for a
* top-level object containing arrays of strings). As of May 2025, the maximum
* depth for standard server metadata appears to be 6, if the document contains
* a full JSON Web Key Set in its "jwks" parameter.
*
* Since it's easy to nest JSON, and the number of parameters and key types
* keeps growing, take a healthy buffer of 16. (If this ever proves to be a
* problem in practice, we may want to switch over to the incremental JSON
* parser instead of playing with this parameter.)
*/
#define MAX_OAUTH_NESTING_LEVEL 16
/*
* Parsed JSON Representations
*
@@ -495,6 +509,12 @@ oauth_json_object_start(void *state)
}
++ctx->nested;
if (ctx->nested > MAX_OAUTH_NESTING_LEVEL)
{
oauth_parse_set_error(ctx, "JSON is too deeply nested");
return JSON_SEM_ACTION_FAILED;
}
return JSON_SUCCESS;
}
@@ -599,6 +619,12 @@ oauth_json_array_start(void *state)
}
++ctx->nested;
if (ctx->nested > MAX_OAUTH_NESTING_LEVEL)
{
oauth_parse_set_error(ctx, "JSON is too deeply nested");
return JSON_SEM_ACTION_FAILED;
}
return JSON_SUCCESS;
}

View File

@@ -157,6 +157,14 @@ client_initial_response(PGconn *conn, bool discover)
#define ERROR_SCOPE_FIELD "scope"
#define ERROR_OPENID_CONFIGURATION_FIELD "openid-configuration"
/*
* Limit the maximum number of nested objects/arrays. Because OAUTHBEARER
* doesn't have any defined extensions for its JSON yet, we can be much more
* conservative here than with libpq-oauth's MAX_OAUTH_NESTING_LEVEL; we expect
* a nesting level of 1 in practice.
*/
#define MAX_SASL_NESTING_LEVEL 8
struct json_ctx
{
char *errmsg; /* any non-NULL value stops all processing */
@@ -196,6 +204,9 @@ oauth_json_object_start(void *state)
}
++ctx->nested;
if (ctx->nested > MAX_SASL_NESTING_LEVEL)
oauth_json_set_error(ctx, libpq_gettext("JSON is too deeply nested"));
return oauth_json_has_error(ctx) ? JSON_SEM_ACTION_FAILED : JSON_SUCCESS;
}
@@ -254,9 +265,22 @@ oauth_json_array_start(void *state)
ctx->target_field_name);
}
++ctx->nested;
if (ctx->nested > MAX_SASL_NESTING_LEVEL)
oauth_json_set_error(ctx, libpq_gettext("JSON is too deeply nested"));
return oauth_json_has_error(ctx) ? JSON_SEM_ACTION_FAILED : JSON_SUCCESS;
}
static JsonParseErrorType
oauth_json_array_end(void *state)
{
struct json_ctx *ctx = state;
--ctx->nested;
return JSON_SUCCESS;
}
static JsonParseErrorType
oauth_json_scalar(void *state, char *token, JsonTokenType type)
{
@@ -519,6 +543,7 @@ handle_oauth_sasl_error(PGconn *conn, const char *msg, int msglen)
sem.object_end = oauth_json_object_end;
sem.object_field_start = oauth_json_object_field_start;
sem.array_start = oauth_json_array_start;
sem.array_end = oauth_json_array_end;
sem.scalar = oauth_json_scalar;
err = pg_parse_json(lex, &sem);