File fix-CVE-2021-32791.patch of Package apache2-mod_auth_openidc.28532
From 375407c16c61a70b56fdbe13b0d2c8f11398e92c Mon Sep 17 00:00:00 2001
From: Hans Zandbelt <hans.zandbelt@zmartzone.eu>
Date: Thu, 10 Jun 2021 15:32:48 +0200
Subject: [PATCH] use encrypted JWTs for storing encrypted cache contents
- avoid using static AAD/IV; thanks @niebardzo
- bump to 2.4.9-dev
Signed-off-by: Hans Zandbelt <hans.zandbelt@zmartzone.eu>
---
.gitignore | 2 +
ChangeLog | 4 +
Dockerfile | 11 +-
configure.ac | 2 +-
openidc.conf | 2 +
src/cache/common.c | 332 ++++----------------------------------
test/mod_auth_openidc.jmx | 127 +++++++--------
7 files changed, 103 insertions(+), 377 deletions(-)
diff --git a/src/cache/common.c b/src/cache/common.c
index e4ae68a5..0bd6ae2d 100644
--- a/src/cache/common.c
+++ b/src/cache/common.c
@@ -229,311 +229,48 @@ apr_byte_t oidc_cache_mutex_destroy(serv
return rv;
}
-#define oidc_cache_crypto_openssl_error(r, fmt, ...) \
- oidc_error(r, "%s: %s", apr_psprintf(r->pool, fmt, ##__VA_ARGS__), ERR_error_string(ERR_get_error(), NULL))
-
-#define OIDC_CACHE_CIPHER EVP_aes_256_gcm()
-#define OIDC_CACHE_TAG_LEN 16
-
-#if (OPENSSL_VERSION_NUMBER >= 0x10100005L && !defined(LIBRESSL_VERSION_NUMBER))
-#define OIDC_CACHE_CRYPTO_GET_TAG EVP_CTRL_AEAD_GET_TAG
-#define OIDC_CACHE_CRYPTO_SET_TAG EVP_CTRL_AEAD_SET_TAG
-#define OIDC_CACHE_CRYPTO_SET_IVLEN EVP_CTRL_AEAD_SET_IVLEN
-#else
-#define OIDC_CACHE_CRYPTO_GET_TAG EVP_CTRL_GCM_GET_TAG
-#define OIDC_CACHE_CRYPTO_SET_TAG EVP_CTRL_GCM_SET_TAG
-#define OIDC_CACHE_CRYPTO_SET_IVLEN EVP_CTRL_GCM_SET_IVLEN
-#endif
+#define OIDC_CACHE_CRYPTO_JSON_KEY "c"
/*
- * AES GCM encrypt
+ * AES GCM encrypt using the crypto passphrase as symmetric key
*/
-static int oidc_cache_crypto_encrypt_impl(request_rec *r,
- unsigned char *plaintext, int plaintext_len, const unsigned char *aad,
- int aad_len, unsigned char *key, const unsigned char *iv, int iv_len,
- unsigned char *ciphertext, const unsigned char *tag, int tag_len) {
- EVP_CIPHER_CTX *ctx;
-
- int len;
-
- int ciphertext_len;
-
- /* create and initialize the context */
- if (!(ctx = EVP_CIPHER_CTX_new())) {
- oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_new");
- return -1;
- }
-
- /* initialize the encryption cipher */
- if (!EVP_EncryptInit_ex(ctx, OIDC_CACHE_CIPHER, NULL, NULL, NULL)) {
- oidc_cache_crypto_openssl_error(r, "EVP_EncryptInit_ex");
- return -1;
- }
-
- /* set IV length */
- if (!EVP_CIPHER_CTX_ctrl(ctx, OIDC_CACHE_CRYPTO_SET_IVLEN, iv_len, NULL)) {
- oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_ctrl");
- return -1;
- }
-
- /* initialize key and IV */
- if (!EVP_EncryptInit_ex(ctx, NULL, NULL, key, iv)) {
- oidc_cache_crypto_openssl_error(r, "EVP_EncryptInit_ex");
- return -1;
- }
-
- /* provide AAD data */
- if (!EVP_EncryptUpdate(ctx, NULL, &len, aad, aad_len)) {
- oidc_cache_crypto_openssl_error(r, "EVP_DecryptUpdate aad: aad_len=%d",
- aad_len);
- return -1;
- }
-
- /* provide the message to be encrypted and obtain the encrypted output */
- if (!EVP_EncryptUpdate(ctx, ciphertext, &len, plaintext, plaintext_len)) {
- oidc_cache_crypto_openssl_error(r, "EVP_EncryptUpdate ciphertext");
- return -1;
- }
- ciphertext_len = len;
-
- /*
- * finalize the encryption; normally ciphertext bytes may be written at
- * this stage, but this does not occur in GCM mode
- */
- if (!EVP_EncryptFinal_ex(ctx, ciphertext + len, &len)) {
- oidc_cache_crypto_openssl_error(r, "EVP_EncryptFinal_ex");
- return -1;
- }
- ciphertext_len += len;
-
- /* get the tag */
- if (!EVP_CIPHER_CTX_ctrl(ctx, OIDC_CACHE_CRYPTO_GET_TAG, tag_len,
- (void *) tag)) {
- oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_ctrl");
- return -1;
- }
-
- /* clean up */
- EVP_CIPHER_CTX_free(ctx);
+static apr_byte_t oidc_cache_crypto_encrypt(request_rec *r, const char *plaintext, const char *key,
+ char **result) {
+ apr_byte_t rv = FALSE;
+ json_t *json = NULL;
- return ciphertext_len;
-}
-
-/*
- * AES GCM decrypt
- */
-static int oidc_cache_crypto_decrypt_impl(request_rec *r,
- unsigned char *ciphertext, int ciphertext_len, const unsigned char *aad,
- int aad_len, const unsigned char *tag, int tag_len, unsigned char *key,
- const unsigned char *iv, int iv_len, unsigned char *plaintext) {
- EVP_CIPHER_CTX *ctx;
- int len;
- int plaintext_len;
- int ret;
-
- /* create and initialize the context */
- if (!(ctx = EVP_CIPHER_CTX_new())) {
- oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_new");
- return -1;
- }
-
- /* initialize the decryption cipher */
- if (!EVP_DecryptInit_ex(ctx, OIDC_CACHE_CIPHER, NULL, NULL, NULL)) {
- oidc_cache_crypto_openssl_error(r, "EVP_DecryptInit_ex");
- return -1;
- }
-
- /* set IV length */
- if (!EVP_CIPHER_CTX_ctrl(ctx, OIDC_CACHE_CRYPTO_SET_IVLEN, iv_len, NULL)) {
- oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_ctrl");
- return -1;
- }
-
- /* initialize key and IV */
- if (!EVP_DecryptInit_ex(ctx, NULL, NULL, key, iv)) {
- oidc_cache_crypto_openssl_error(r, "EVP_DecryptInit_ex");
- return -1;
- }
-
- /* provide AAD data */
- if (!EVP_DecryptUpdate(ctx, NULL, &len, aad, aad_len)) {
- oidc_cache_crypto_openssl_error(r, "EVP_DecryptUpdate aad: aad_len=%d",
- aad_len);
- return -1;
- }
-
- /* provide the message to be decrypted and obtain the plaintext output */
- if (!EVP_DecryptUpdate(ctx, plaintext, &len, ciphertext, ciphertext_len)) {
- oidc_cache_crypto_openssl_error(r, "EVP_DecryptUpdate ciphertext");
- return -1;
- }
- plaintext_len = len;
-
- /* set expected tag value; works in OpenSSL 1.0.1d and later */
- if (!EVP_CIPHER_CTX_ctrl(ctx, OIDC_CACHE_CRYPTO_SET_TAG, tag_len,
- (void *) tag)) {
- oidc_cache_crypto_openssl_error(r, "EVP_CIPHER_CTX_ctrl");
- return -1;
- }
-
- /*
- * finalize the decryption; a positive return value indicates success,
- * anything else is a failure - the plaintext is not trustworthy
- */
- ret = EVP_DecryptFinal_ex(ctx, plaintext + len, &len);
-
- /* clean up */
- EVP_CIPHER_CTX_free(ctx);
-
- if (ret > 0) {
- /* success */
- plaintext_len += len;
- return plaintext_len;
- } else {
- /* verify failed */
- oidc_cache_crypto_openssl_error(r, "EVP_DecryptFinal_ex");
- return -1;
- }
-}
+ json = json_object();
+ json_object_set_new(json, OIDC_CACHE_CRYPTO_JSON_KEY, json_string(plaintext));
-/*
- * static AAD value for encryption/decryption
- */
-static const unsigned char OIDC_CACHE_CRYPTO_GCM_AAD[] = { 0x4d, 0x23, 0xc3,
- 0xce, 0xc3, 0x34, 0xb4, 0x9b, 0xdb, 0x37, 0x0c, 0x43, 0x7f, 0xec, 0x78,
- 0xde };
+ rv = oidc_util_jwt_create(r, (const char*) key, json, result);
-/*
- * static IV value for encryption/decryption
- */
-static const unsigned char OIDC_CACHE_CRYPTO_GCM_IV[] = { 0x00, 0x01, 0x02,
- 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e,
- 0x0f };
+ if (json)
+ json_decref(json);
-/*
- * AES GCM encrypt using the static AAD and IV
- */
-static int oidc_cache_crypto_encrypt(request_rec *r, const char *plaintext,
- unsigned char *key, char **result) {
- char *encoded = NULL, *p = NULL, *e_tag = NULL;
- unsigned char *ciphertext = NULL;
- int plaintext_len, ciphertext_len, encoded_len, e_tag_len;
- unsigned char tag[OIDC_CACHE_TAG_LEN];
-
- /* allocate space for the ciphertext */
- plaintext_len = strlen(plaintext) + 1;
- ciphertext = apr_pcalloc(r->pool,
- (plaintext_len + EVP_CIPHER_block_size(OIDC_CACHE_CIPHER)));
-
- ciphertext_len = oidc_cache_crypto_encrypt_impl(r,
- (unsigned char *) plaintext, plaintext_len,
- OIDC_CACHE_CRYPTO_GCM_AAD, sizeof(OIDC_CACHE_CRYPTO_GCM_AAD), key,
- OIDC_CACHE_CRYPTO_GCM_IV, sizeof(OIDC_CACHE_CRYPTO_GCM_IV),
- ciphertext, tag, sizeof(tag));
-
- /* base64url encode the resulting ciphertext */
- encoded_len = oidc_base64url_encode(r, &encoded, (const char *) ciphertext,
- ciphertext_len, 1);
- if (encoded_len > 0) {
- p = encoded;
-
- /* now allocated space for the concatenated base64url encoded ciphertext and tag */
- encoded = apr_pcalloc(r->pool,
- encoded_len + 1 + OIDC_CACHE_TAG_LEN + 1);
- memcpy(encoded, p, encoded_len);
- p = encoded + encoded_len;
- *p = OIDC_CHAR_DOT;
- p++;
-
- /* base64url encode the tag and append it in the buffer */
- e_tag_len = oidc_base64url_encode(r, &e_tag, (const char *) tag,
- OIDC_CACHE_TAG_LEN, 1);
- memcpy(p, e_tag, e_tag_len);
- encoded_len += e_tag_len + 1;
-
- /* make sure the result is \0 terminated */
- encoded[encoded_len] = '\0';
-
- *result = encoded;
- }
-
- return encoded_len;
+ return rv;
}
/*
- * AES GCM decrypt using the static AAD and IV
+ * AES GCM decrypt using the crypto passphrase as symmetric key
*/
-static int oidc_cache_crypto_decrypt(request_rec *r, const char *cache_value,
- unsigned char *key, unsigned char **plaintext) {
+static apr_byte_t oidc_cache_crypto_decrypt(request_rec *r, const char *cache_value,
+ const char *key, char **plaintext) {
- int len = -1;
+ apr_byte_t rv = FALSE;
+ json_t *json = NULL;
- /* grab the base64url-encoded tag after the "." */
- char *encoded_tag = strstr(cache_value, ".");
- if (encoded_tag == NULL) {
- oidc_error(r,
- "corrupted cache value: no tag separator found in encrypted value");
- return FALSE;
- }
-
- /* make sure we don't modify the original string since it may be just a pointer into the cache (shm) */
- cache_value = apr_pstrmemdup(r->pool, cache_value,
- strlen(cache_value) - strlen(encoded_tag));
- encoded_tag++;
-
- /* base64url decode the ciphertext */
- char *d_bytes = NULL;
- int d_len = oidc_base64url_decode(r->pool, &d_bytes, cache_value);
-
- /* base64url decode the tag */
- char *t_bytes = NULL;
- int t_len = oidc_base64url_decode(r->pool, &t_bytes, encoded_tag);
-
- /* see if we're still good to go */
- if ((d_len > 0) && (t_len > 0)) {
-
- /* allocated space for the plaintext */
- *plaintext = apr_pcalloc(r->pool,
- (d_len + EVP_CIPHER_block_size(OIDC_CACHE_CIPHER) - 1));
-
- /* decrypt the ciphertext providing the tag value */
-
- len = oidc_cache_crypto_decrypt_impl(r, (unsigned char *) d_bytes,
- d_len, OIDC_CACHE_CRYPTO_GCM_AAD,
- sizeof(OIDC_CACHE_CRYPTO_GCM_AAD), (unsigned char *) t_bytes,
- t_len, key, OIDC_CACHE_CRYPTO_GCM_IV,
- sizeof(OIDC_CACHE_CRYPTO_GCM_IV), *plaintext);
-
- /* check the result and make sure it is \0 terminated */
- if (len > -1) {
- (*plaintext)[len] = '\0';
- } else {
- *plaintext = NULL;
- }
+ rv = oidc_util_jwt_verify(r, (const char*) key, cache_value, &json);
+ if (rv == FALSE)
+ goto end;
- }
+ rv = oidc_json_object_get_string(r->pool, json, OIDC_CACHE_CRYPTO_JSON_KEY, plaintext, NULL);
- return len;
-}
+ end:
-/*
- * hash the crypto passhphrase so it has enough key length for AES GCM 256
- */
-static unsigned char *oidc_cache_hash_passphrase(request_rec *r,
- const char *passphrase) {
+ if (json)
+ json_decref(json);
- unsigned char *key = NULL;
- unsigned int key_len = 0;
- oidc_jose_error_t err;
-
- if (oidc_jose_hash_bytes(r->pool, OIDC_JOSE_ALG_SHA256,
- (const unsigned char *) passphrase, strlen(passphrase), &key,
- &key_len, &err) == FALSE) {
- oidc_error(r, "oidc_jose_hash_bytes returned an error: %s", err.text);
- return NULL;
- }
-
- return key;
+ return rv;
}
/*
@@ -604,9 +338,7 @@ apr_byte_t oidc_cache_get(request_rec *r, const char *section, const char *key,
goto out;
}
- rc = (oidc_cache_crypto_decrypt(r, cache_value,
- oidc_cache_hash_passphrase(r, cfg->crypto_passphrase),
- (unsigned char **) value) > 0);
+ rc = oidc_cache_crypto_decrypt(r, cache_value, cfg->crypto_passphrase, value);
out:
/* log the result */
@@ -650,9 +382,7 @@ apr_byte_t oidc_cache_set(request_rec *r, const char *section, const char *key,
goto out;
if (value != NULL) {
- if (oidc_cache_crypto_encrypt(r, value,
- oidc_cache_hash_passphrase(r, cfg->crypto_passphrase),
- &encoded) <= 0)
+ if (oidc_cache_crypto_encrypt(r, value, cfg->crypto_passphrase, &encoded) == FALSE)
goto out;
value = encoded;
}