mirror of
https://git.haproxy.org/git/haproxy.git/
synced 2026-03-19 05:51:10 +01:00
MINOR: jwt: Improve 'jwt_tokenize' function
The 'jwt_tokenize' function that can be used to split a JWT token into its subparts can either fully process the token (from beginning to end) when we need to check its signature, or only partially when using the jwt_header_query or jwt_member_query converters. In this case we relied on the fact that the return value of the 'jwt_tokenize' function was not checked because a '-1' was returned (which was not actually an error). In order to make this logic more explicit, the 'jwt_tokenize' function now has a way to warn the caller that the token was invalid (less subparts than the specified 'item_num') or that the token was not processed in full (enough subparts found without parsing the token all the way). The function will now only return 0 if we found strictly the same number of subparts as 'item_num'.
This commit is contained in:
parent
1babe8cb1b
commit
e34b633be3
@ -27,7 +27,7 @@
|
||||
|
||||
#ifdef USE_OPENSSL
|
||||
enum jwt_alg jwt_parse_alg(const char *alg_str, unsigned int alg_len);
|
||||
int jwt_tokenize(const struct buffer *jwt, struct jwt_item *items, unsigned int *item_num);
|
||||
int jwt_tokenize(const struct buffer *jwt, struct jwt_item *items, unsigned int item_num);
|
||||
int jwt_tree_load_cert(char *path, int pathlen, int tryload_cert, const char *file, int line, char **err);
|
||||
|
||||
enum jwt_vrfy_status jwt_verify(const struct buffer *token, const struct buffer *alg,
|
||||
|
||||
@ -16,7 +16,7 @@ feature cmd "$HAPROXY_PROGRAM -cc 'feature(OPENSSL)'"
|
||||
feature cmd "command -v socat"
|
||||
feature ignore_unknown_macro
|
||||
|
||||
server s1 -repeat 27 {
|
||||
server s1 -repeat 40 {
|
||||
rxreq
|
||||
txresp
|
||||
} -start
|
||||
@ -542,3 +542,44 @@ client c27 -connect ${h1_mainfe_sock} {
|
||||
expect resp.http.x-jwt-verify-RS256-var2 == "1"
|
||||
|
||||
} -run
|
||||
|
||||
client c28 -connect ${h1_mainfe_sock} {
|
||||
# Token content : {"alg":"none"}
|
||||
# {"iss":"joe", "exp":1300819380, "http://example.com/is_root":true}
|
||||
txreq -url "/none" -hdr "Authorization: Bearer eyJhbGciOiJub25lIn0.eyJpc3MiOiJqb2UiLA0KICJleHAiOjEzMDA4MTkzODAsDQogImh0dHA6Ly9leGFtcGxlLmNvbS9pc19yb290Ijp0cnVlfQ."
|
||||
rxresp
|
||||
expect resp.status == 200
|
||||
expect resp.http.x-jwt-alg == "none"
|
||||
expect resp.http.x-jwt-verify == "1"
|
||||
} -run
|
||||
|
||||
client c29 -connect ${h1_mainfe_sock} {
|
||||
# Invalid Token : too many subparts
|
||||
txreq -url "/errors" -hdr "Authorization: Bearer eyJhbGciOiJub25lIn0.aa.aa.aa"
|
||||
rxresp
|
||||
expect resp.status == 200
|
||||
expect resp.http.x-jwt-alg == "none"
|
||||
expect resp.http.x-jwt-verify == "-3"
|
||||
|
||||
# Invalid Token : too many subparts
|
||||
txreq -url "/errors" -hdr "Authorization: Bearer eyJhbGciOiJub25lIn0.aa.aa."
|
||||
rxresp
|
||||
expect resp.status == 200
|
||||
expect resp.http.x-jwt-alg == "none"
|
||||
expect resp.http.x-jwt-verify == "-3"
|
||||
|
||||
# Invalid Token : too few subparts
|
||||
txreq -url "/errors" -hdr "Authorization: Bearer eyJhbGciOiJub25lIn0.aa"
|
||||
rxresp
|
||||
expect resp.status == 200
|
||||
expect resp.http.x-jwt-alg == "none"
|
||||
expect resp.http.x-jwt-verify == "-3"
|
||||
|
||||
# Invalid Token : no signature but alg different than "none"
|
||||
txreq -url "/errors" -hdr "Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ."
|
||||
rxresp
|
||||
expect resp.status == 200
|
||||
expect resp.http.x-jwt-alg == "RS256"
|
||||
expect resp.http.x-jwt-verify == "-3"
|
||||
} -run
|
||||
|
||||
|
||||
@ -534,7 +534,7 @@ static int sample_conv_jwt_decrypt_secret(const struct arg *args, struct sample
|
||||
if (!chunk_cpy(input, &smp->data.u.str))
|
||||
goto end;
|
||||
|
||||
if (jwt_tokenize(input, items, &item_num) || item_num != JWE_ELT_MAX)
|
||||
if (jwt_tokenize(input, items, item_num))
|
||||
goto end;
|
||||
|
||||
alg_tag = alloc_trash_chunk();
|
||||
@ -789,7 +789,7 @@ static int sample_conv_jwt_decrypt_cert(const struct arg *args, struct sample *s
|
||||
if (!chunk_cpy(input, &smp->data.u.str))
|
||||
goto end;
|
||||
|
||||
if (jwt_tokenize(input, items, &item_num) || item_num != JWE_ELT_MAX)
|
||||
if (jwt_tokenize(input, items, item_num))
|
||||
goto end;
|
||||
|
||||
/* Base64Url decode the JOSE header */
|
||||
@ -1302,7 +1302,7 @@ static int sample_conv_jwt_decrypt_jwk(const struct arg *args, struct sample *sm
|
||||
if (!chunk_cpy(input, &smp->data.u.str))
|
||||
goto end;
|
||||
|
||||
if (jwt_tokenize(input, items, &item_num) || item_num != JWE_ELT_MAX)
|
||||
if (jwt_tokenize(input, items, item_num))
|
||||
goto end;
|
||||
|
||||
alg_tag = alloc_trash_chunk();
|
||||
|
||||
35
src/jwt.c
35
src/jwt.c
@ -94,26 +94,30 @@ enum jwt_alg jwt_parse_alg(const char *alg_str, unsigned int alg_len)
|
||||
* now, we don't need to manage more than three subparts in the tokens.
|
||||
* See section 3.1 of RFC7515 for more information about JWS Compact
|
||||
* Serialization.
|
||||
* Returns 0 in case of success.
|
||||
* Returns -1 in case of error, 0 if the token has exactly <item_num> parts, a
|
||||
* positive value otherwise.
|
||||
*/
|
||||
int jwt_tokenize(const struct buffer *jwt, struct jwt_item *items, unsigned int *item_num)
|
||||
int jwt_tokenize(const struct buffer *jwt, struct jwt_item *items, unsigned int item_num)
|
||||
{
|
||||
char *ptr = jwt->area;
|
||||
char *jwt_end = jwt->area + jwt->data;
|
||||
unsigned int index = 0;
|
||||
unsigned int length = 0;
|
||||
|
||||
if (index < *item_num) {
|
||||
items[index].start = ptr;
|
||||
items[index].length = 0;
|
||||
}
|
||||
if (item_num == 0)
|
||||
return -1;
|
||||
|
||||
while (index < *item_num && ptr < jwt_end) {
|
||||
items[index].start = ptr;
|
||||
items[index].length = 0;
|
||||
|
||||
while (ptr < jwt_end) {
|
||||
if (*ptr++ == '.') {
|
||||
items[index++].length = length;
|
||||
/* We found enough items, no need to keep looking for
|
||||
* separators. */
|
||||
if (index == item_num)
|
||||
return 1;
|
||||
|
||||
if (index == *item_num)
|
||||
return -1;
|
||||
items[index].start = ptr;
|
||||
items[index].length = 0;
|
||||
length = 0;
|
||||
@ -121,10 +125,11 @@ int jwt_tokenize(const struct buffer *jwt, struct jwt_item *items, unsigned int
|
||||
++length;
|
||||
}
|
||||
|
||||
if (index < *item_num)
|
||||
items[index].length = length;
|
||||
/* We might not have found enough items */
|
||||
if (index < item_num - 1)
|
||||
return -1;
|
||||
|
||||
*item_num = (index+1);
|
||||
items[index].length = length;
|
||||
|
||||
return (ptr != jwt_end);
|
||||
}
|
||||
@ -493,13 +498,9 @@ enum jwt_vrfy_status jwt_verify(const struct buffer *token, const struct buffer
|
||||
if (ctx.alg == JWT_ALG_DEFAULT)
|
||||
return JWT_VRFY_UNKNOWN_ALG;
|
||||
|
||||
if (jwt_tokenize(token, items, &item_num))
|
||||
if (jwt_tokenize(token, items, item_num))
|
||||
return JWT_VRFY_INVALID_TOKEN;
|
||||
|
||||
if (item_num != JWT_ELT_MAX)
|
||||
if (ctx.alg != JWS_ALG_NONE || item_num != JWT_ELT_SIG)
|
||||
return JWT_VRFY_INVALID_TOKEN;
|
||||
|
||||
ctx.jose = items[JWT_ELT_JOSE];
|
||||
ctx.claims = items[JWT_ELT_CLAIMS];
|
||||
ctx.signature = items[JWT_ELT_SIG];
|
||||
|
||||
@ -4795,9 +4795,10 @@ static int sample_conv_jwt_member_query(const struct arg *args, struct sample *s
|
||||
int retval = 0;
|
||||
int ret;
|
||||
|
||||
jwt_tokenize(&smp->data.u.str, items, &item_num);
|
||||
|
||||
if (item_num < member + 1)
|
||||
/* We don't need to extract all the parts from the token, we only need a
|
||||
* specific one.
|
||||
*/
|
||||
if (jwt_tokenize(&smp->data.u.str, items, item_num) < 0)
|
||||
goto end;
|
||||
|
||||
decoded_header = get_trash_chunk_sz(items[member].length);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user