diff --git a/doc/configuration.txt b/doc/configuration.txt
index de877ab59..ac94b324f 100644
--- a/doc/configuration.txt
+++ b/doc/configuration.txt
@@ -1791,6 +1791,8 @@ The following keywords are supported in the "global" section :
- insecure-fork-wanted
- insecure-setuid-wanted
- issuers-chain-path
+ - jwt.decrypt_alg_list
+ - jwt.decrypt_enc_list
- key-base
- limited-quic
- localpeer
@@ -2888,6 +2890,40 @@ issuers-chain-path
The OCSP features are able to use the completed chain when no .issuer was
used, or no chain was provided in the PEM.
+jwt.decrypt_alg_list
+ Set the list of algorithms allowed in the jwt_decrypt_XXX converters. JWT
+ tokens using an unsupported or disabled algorithms will never be decrypted.
+ The specified algorithms must have the same format as in section 4.1 of
+ RFC7518 and must be colon-separated. The special "ALL" name can be used to
+ enable all the supported algorithms (see "jwt_decrypt_jwk" converter for a
+ complete list) and a '!' can be appended to an algorithm name to explicitly
+ disable it.
+ Please note that unless "ALL" is specified, using this option will disable
+ any algorithm that is not explicitly mentioned in the provided list.
+
+ Examples:
+ # Enable all algorithms but the "ECDH-ES" one
+ jwt.decrypt_alg_list ALL:!ECDH-ES
+
+ # Only enable ECDH-ES algorithms
+ jwt.decrypt_alg_list ECDH-ES:ECDH-ES+A128KW:ECDH-ES+A192KW:ECDH-ES+A256KW
+
+jwt.decrypt_enc_list
+ Set the list of encryption algorithms allowed in the jwt_decrypt_XXX
+ converters. JWT tokens using an unsupported or disabled encryption algorithms
+ will never be decrypted.
+ The specified algorithms must have the same format as in section 5.1 of
+ RFC7518 and must be colon-separated. The special "ALL" name can be used to
+ enable all the supported algorithms (see "jwt_decrypt_jwk" converter for a
+ complete list) and a '!' can be appended to an algorithm name to explicitly
+ disable it.
+ Please note that unless "ALL" is specified, using this option will disable
+ any algorithm that is not explicitly mentioned in the provided list.
+
+ Examples:
+ # Enable only AES GCM encrypting algorithms
+ jwt.decrypt_enc_list A128GCM:A192GCM:A256GCM
+
key-base
Assigns a default directory to fetch SSL private keys from when a relative
path is used with "key" directives. Absolute locations specified prevail and
@@ -21897,6 +21933,10 @@ jwt_decrypt_cert()
the JOSE header) among the following: RSA1_5, RSA-OAEP, RSA-OAEP-256,
ECDH-ES, ECDH-ES+A128KW, ECDH-ES+A192KW or ECDH-ES+A256KW.
+ The supported algorithms and encryption algorithms ("alg" and "enc" fields of
+ the JOSE header respectively) can be modified thanks to the
+ 'jwt.decrypt_alg_list' and 'jwt.decrypt_enc_list' global options.
+
The JWE token must be provided base64url-encoded and the output will be
provided "raw". If an error happens during token parsing, signature
verification or content decryption, an empty string will be returned.
@@ -21932,6 +21972,10 @@ jwt_decrypt_jwk()
so the A128KW, A192KW, ECDH-ES+A128KW and ECDH-ES+A192KW algorithms won't
work.
+ The supported algorithms and encryption algorithms ("alg" and "enc" fields of
+ the JOSE header respectively) can be modified thanks to the
+ 'jwt.decrypt_alg_list' and 'jwt.decrypt_enc_list' global options.
+
The JWE token must be provided base64url-encoded and the output will be
provided "raw". If an error happens during token parsing, signature
verification or content decryption, an empty string will be returned.
diff --git a/src/jwe.c b/src/jwe.c
index e36b7b0d8..a022e33ab 100644
--- a/src/jwe.c
+++ b/src/jwe.c
@@ -26,6 +26,7 @@
struct alg_enc {
const char *name;
int value;
+ int enabled;
};
/* https://datatracker.ietf.org/doc/html/rfc7518#section-4.1 */
@@ -50,27 +51,34 @@ typedef enum {
// JWE_ALG_PBES2_HS512_A256KW,
} jwe_alg;
-struct alg_enc jwe_algs[] = {
- { "RSA1_5", JWE_ALG_RSA1_5 },
- { "RSA-OAEP", JWE_ALG_RSA_OAEP },
- { "RSA-OAEP-256", JWE_ALG_RSA_OAEP_256 },
- { "A128KW", JWE_ALG_A128KW },
- { "A192KW", JWE_ALG_A192KW },
- { "A256KW", JWE_ALG_A256KW },
- { "dir", JWE_ALG_DIR },
- { "ECDH-ES", JWE_ALG_ECDH_ES },
- { "ECDH-ES+A128KW", JWE_ALG_ECDH_ES_A128KW },
- { "ECDH-ES+A192KW", JWE_ALG_ECDH_ES_A192KW },
- { "ECDH-ES+A256KW", JWE_ALG_ECDH_ES_A256KW },
- { "A128GCMKW", JWE_ALG_A128GCMKW },
- { "A192GCMKW", JWE_ALG_A192GCMKW },
- { "A256GCMKW", JWE_ALG_A256GCMKW },
- { "PBES2-HS256+A128KW", JWE_ALG_UNMANAGED },
- { "PBES2-HS384+A192KW", JWE_ALG_UNMANAGED },
- { "PBES2-HS512+A256KW", JWE_ALG_UNMANAGED },
- { NULL, JWE_ALG_UNMANAGED },
+enum {
+ ALG_ENC_DISABLED = 0,
+ ALG_ENC_ENABLED = 1
};
+struct alg_enc jwe_algs_dflt[] = {
+ { "RSA1_5", JWE_ALG_RSA1_5, ALG_ENC_ENABLED },
+ { "RSA-OAEP", JWE_ALG_RSA_OAEP, ALG_ENC_ENABLED },
+ { "RSA-OAEP-256", JWE_ALG_RSA_OAEP_256, ALG_ENC_ENABLED },
+ { "A128KW", JWE_ALG_A128KW, ALG_ENC_ENABLED },
+ { "A192KW", JWE_ALG_A192KW, ALG_ENC_ENABLED },
+ { "A256KW", JWE_ALG_A256KW, ALG_ENC_ENABLED },
+ { "dir", JWE_ALG_DIR, ALG_ENC_ENABLED },
+ { "ECDH-ES", JWE_ALG_ECDH_ES, ALG_ENC_ENABLED },
+ { "ECDH-ES+A128KW", JWE_ALG_ECDH_ES_A128KW, ALG_ENC_ENABLED },
+ { "ECDH-ES+A192KW", JWE_ALG_ECDH_ES_A192KW, ALG_ENC_ENABLED },
+ { "ECDH-ES+A256KW", JWE_ALG_ECDH_ES_A256KW, ALG_ENC_ENABLED },
+ { "A128GCMKW", JWE_ALG_A128GCMKW, ALG_ENC_ENABLED },
+ { "A192GCMKW", JWE_ALG_A192GCMKW, ALG_ENC_ENABLED },
+ { "A256GCMKW", JWE_ALG_A256GCMKW, ALG_ENC_ENABLED },
+ { "PBES2-HS256+A128KW", JWE_ALG_UNMANAGED, ALG_ENC_DISABLED },
+ { "PBES2-HS384+A192KW", JWE_ALG_UNMANAGED, ALG_ENC_DISABLED },
+ { "PBES2-HS512+A256KW", JWE_ALG_UNMANAGED, ALG_ENC_DISABLED },
+ { NULL, JWE_ALG_UNMANAGED, ALG_ENC_DISABLED },
+};
+
+struct alg_enc *jwe_algs = NULL;
+
/* https://datatracker.ietf.org/doc/html/rfc7518#section-5.1 */
typedef enum {
JWE_ENC_UNMANAGED = -1,
@@ -82,16 +90,18 @@ typedef enum {
JWE_ENC_A256GCM,
} jwe_enc;
-struct alg_enc jwe_encodings[] = {
- { "A128CBC-HS256", JWE_ENC_A128CBC_HS256 },
- { "A192CBC-HS384", JWE_ENC_A192CBC_HS384 },
- { "A256CBC-HS512", JWE_ENC_A256CBC_HS512 },
- { "A128GCM", JWE_ENC_A128GCM },
- { "A192GCM", JWE_ENC_A192GCM },
- { "A256GCM", JWE_ENC_A256GCM },
- { NULL, JWE_ENC_UNMANAGED },
+struct alg_enc jwe_encodings_dflt[] = {
+ { "A128CBC-HS256", JWE_ENC_A128CBC_HS256, ALG_ENC_ENABLED },
+ { "A192CBC-HS384", JWE_ENC_A192CBC_HS384, ALG_ENC_ENABLED },
+ { "A256CBC-HS512", JWE_ENC_A256CBC_HS512, ALG_ENC_ENABLED },
+ { "A128GCM", JWE_ENC_A128GCM, ALG_ENC_ENABLED },
+ { "A192GCM", JWE_ENC_A192GCM, ALG_ENC_ENABLED },
+ { "A256GCM", JWE_ENC_A256GCM, ALG_ENC_ENABLED },
+ { NULL, JWE_ENC_UNMANAGED, ALG_ENC_DISABLED },
};
+struct alg_enc *jwe_encodings = NULL;
+
/*
* In the JWE Compact Serialization, a JWE is represented as the concatenation:
@@ -131,6 +141,8 @@ static inline int parse_alg_enc(struct buffer *buf, struct alg_enc *array)
while (item->name) {
if (strncmp(item->name, b_orig(buf), (int)b_data(buf)) == 0) {
+ if (item->enabled == ALG_ENC_DISABLED)
+ return -1;
val = item->value;
break;
}
@@ -2059,6 +2071,165 @@ end:
}
+/*
+ * Duplicate algorithm and encoding arrays so that their enabled flags
+ * can be overwritten (when using the jwt.encrypt_alg/enc_list options).
+ * Return 0 in case of success, -1 otherwise.
+ */
+static int dup_alg_enc_arrays(void)
+{
+ int ret = -1;
+
+ if (!jwe_algs) {
+ jwe_algs = malloc(sizeof(jwe_algs_dflt));
+ if (!jwe_algs) {
+ ha_alert("Unable to allocate the JWE algorithms array.\n");
+ goto end;
+ }
+ memcpy(jwe_algs, jwe_algs_dflt, sizeof(jwe_algs_dflt));
+ }
+
+ if (!jwe_encodings) {
+ jwe_encodings = malloc(sizeof(jwe_encodings_dflt));
+ if (!jwe_encodings) {
+ ha_alert("Unable to allocate the JWE encodings array.\n");
+ goto end;
+ }
+ memcpy(jwe_encodings, jwe_encodings_dflt, sizeof(jwe_encodings_dflt));
+ }
+
+ ret = 0;
+
+end:
+ return ret;
+}
+
+static int jwe_init(void)
+{
+ /* Duplicate algorithm and encoding arrays so that their enabled flags
+ * can be overwritten */
+ return dup_alg_enc_arrays();
+}
+
+REGISTER_PRE_CHECK(jwe_init);
+
+static void jwe_deinit(void)
+{
+ ha_free(&jwe_algs);
+ ha_free(&jwe_encodings);
+}
+
+REGISTER_POST_DEINIT(jwe_deinit);
+
+/*
+ * Look for an entry named in array and set its 'enabled' field
+ * to .
+ * Return 0 in case of success, 1 otherwise (item not found).
+ */
+static int set_alg_enc_flag(struct alg_enc *arr, char *algname, int algname_len, int value)
+{
+ struct alg_enc *item = &arr[0];
+
+ while (item->name) {
+ if (algname_len == strlen(item->name) && strncmp(algname, item->name, algname_len) == 0) {
+ item->enabled = value;
+ return 0;
+ }
+ ++item;
+ }
+
+ return 1;
+}
+
+/* Set the 'enabled' field to for all members of . */
+static void set_all_alg_enc_flags(struct alg_enc *arr, int val)
+{
+ struct alg_enc *item = &arr[0];
+
+ while (item->name) {
+ item->enabled = val;
+ ++item;
+ }
+}
+
+/*
+ * Parse algorithm list comprised of colon-separated algorithms having the same
+ * format as in jwe_algs_dflt or jwe_encodings_dflt arrays.
+ * When the option is used, all the algorithms are first disabled so any
+ * unspecified algo will not be enabled anymore.
+ */
+static int jwe_parse_global_alg_enc_list(char **args, int section_type, struct proxy *curpx,
+ const struct proxy *defpx, const char *file, int line,
+ char **err)
+{
+ struct alg_enc *arr = NULL;
+ char *p, *arg = args[1];
+ char *arg_end = arg+strlen(arg);
+ char *alg, *alg_end;
+
+ int value = ALG_ENC_ENABLED;
+ int ret = -1;
+
+ if (dup_alg_enc_arrays())
+ goto end;
+
+ if (args[0][14] == 'a') {
+ /* "jwe.supported_algorithms" */
+ arr = jwe_algs;
+ } else {
+ /* "jwe.supported_encodings" */
+ arr = jwe_encodings;
+ }
+
+ /* Disable all algorithms/encodings */
+ set_all_alg_enc_flags(arr, ALG_ENC_DISABLED);
+
+ while (*arg) {
+ value = ALG_ENC_ENABLED;
+
+ if (*arg == '!') {
+ ++arg;
+ value = ALG_ENC_DISABLED;
+ }
+ alg = arg;
+ alg_end = NULL;
+
+ p = strchr(arg, ':');
+ if (p) {
+ alg_end = p;
+ arg = p + 1;
+ } else {
+ alg_end = arg_end;
+ arg = arg_end;
+ }
+
+ /* If "ALL" is used all previously disabled algo will be
+ * reenabled */
+ if (strncmp(alg, "ALL", alg_end - alg) == 0) {
+ set_all_alg_enc_flags(arr, ALG_ENC_ENABLED);
+ } else {
+ if (set_alg_enc_flag(arr, alg, alg_end-alg, value) != 0) {
+ memprintf(err, "Unknown algorithm/encoding %.*s\n", (int)(alg_end-alg), alg);
+ goto end;
+ }
+ }
+ }
+
+ ret = 0;
+end:
+ return ret;
+
+}
+
+static struct cfg_kw_list cfg_kws = {ILH, {
+ { CFG_GLOBAL, "jwt.decrypt_alg_list", jwe_parse_global_alg_enc_list },
+ { CFG_GLOBAL, "jwt.decrypt_enc_list", jwe_parse_global_alg_enc_list },
+ { 0, NULL, NULL },
+}};
+
+INITCALL1(STG_REGISTER, cfg_register_keywords, &cfg_kws);
+
+
static struct sample_conv_kw_list sample_conv_kws = {ILH, {
/* JSON Web Token converters */
{ "jwt_decrypt_secret", sample_conv_jwt_decrypt_secret, ARG1(1,STR), sample_conv_jwt_decrypt_secret_check, SMP_T_BIN, SMP_T_BIN },