Resolves: RHEL-29579 - vulnerable to marvin attack if the authentication option is used

This commit is contained in:
Michal Ruprich 2024-06-11 12:50:45 +02:00
parent 04d91429ed
commit 4cd78d3412
6 changed files with 377 additions and 24 deletions

315
0002-cve-2024-26306.patch Normal file
View File

@ -0,0 +1,315 @@
From 299b356df6939f71619bf45bf7a7d2222e17d840 Mon Sep 17 00:00:00 2001
From: Sarah Larsen <swlarsen@Sarahs-MBP.lan>
Date: Wed, 20 Mar 2024 17:02:31 -0700
Subject: [PATCH] Using OAEP padding instead of PKCS1 padding for OpenSSL. Fix
for CVE-2024-26306.
Special thanks to Hubert Kario at Red Hat for finding the vulnerability.
diff --git a/src/iperf.h b/src/iperf.h
index c1d839be1..527e549ed 100644
--- a/src/iperf.h
+++ b/src/iperf.h
@@ -319,6 +319,7 @@ struct iperf_test
#if defined(HAVE_SSL)
char *server_authorized_users;
EVP_PKEY *server_rsa_private_key;
+ int use_pkcs1_padding;
#endif // HAVE_SSL
/* boolean variables for Options */
diff --git a/src/iperf_api.c b/src/iperf_api.c
index d40561c10..7fb741e77 100644
--- a/src/iperf_api.c
+++ b/src/iperf_api.c
@@ -1137,6 +1137,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
{"rsa-public-key-path", required_argument, NULL, OPT_CLIENT_RSA_PUBLIC_KEY},
{"rsa-private-key-path", required_argument, NULL, OPT_SERVER_RSA_PRIVATE_KEY},
{"authorized-users-path", required_argument, NULL, OPT_SERVER_AUTHORIZED_USERS},
+ {"use-pkcs1-padding", no_argument, NULL, OPT_USE_PKCS1_PADDING},
#endif /* HAVE_SSL */
{"fq-rate", required_argument, NULL, OPT_FQ_RATE},
{"pacing-timer", required_argument, NULL, OPT_PACING_TIMER},
@@ -1630,6 +1631,9 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
case OPT_SERVER_AUTHORIZED_USERS:
test->server_authorized_users = strdup(optarg);
break;
+ case OPT_USE_PKCS1_PADDING:
+ test->use_pkcs1_padding = 1;
+ break;
#endif /* HAVE_SSL */
case OPT_PACING_TIMER:
test->settings->pacing_timer = unit_atoi(optarg);
@@ -2070,7 +2074,7 @@ int test_is_authorized(struct iperf_test *test){
if (test->settings->authtoken){
char *username = NULL, *password = NULL;
time_t ts;
- int rc = decode_auth_setting(test->debug, test->settings->authtoken, test->server_rsa_private_key, &username, &password, &ts);
+ int rc = decode_auth_setting(test->debug, test->settings->authtoken, test->server_rsa_private_key, &username, &password, &ts, test->use_pkcs1_padding);
if (rc) {
return -1;
}
@@ -2255,7 +2259,7 @@ send_parameters(struct iperf_test *test)
#if defined(HAVE_SSL)
/* Send authentication parameters */
if (test->settings->client_username && test->settings->client_password && test->settings->client_rsa_pubkey){
- int rc = encode_auth_setting(test->settings->client_username, test->settings->client_password, test->settings->client_rsa_pubkey, &test->settings->authtoken);
+ int rc = encode_auth_setting(test->settings->client_username, test->settings->client_password, test->settings->client_rsa_pubkey, &test->settings->authtoken, test->use_pkcs1_padding);
if (rc) {
cJSON_Delete(j);
diff --git a/src/iperf_api.h b/src/iperf_api.h
index d2bbdfe96..131314243 100644
--- a/src/iperf_api.h
+++ b/src/iperf_api.h
@@ -100,6 +100,7 @@ typedef atomic_uint_fast64_t atomic_iperf_size_t;
#define OPT_BIDIRECTIONAL 20
#define OPT_SERVER_BITRATE_LIMIT 21
#define OPT_TIMESTAMPS 22
+#define OPT_USE_PKCS1_PADDING 30
/* states */
#define TEST_START 1
diff --git a/src/t_auth.c b/src/t_auth.c
index 77c225531..3b0fd2f32 100644
--- a/src/t_auth.c
+++ b/src/t_auth.c
@@ -101,8 +101,9 @@ test_authtoken(const char *authUser, const char *authPassword, EVP_PKEY *pubkey,
char *decodePassword;
time_t decodeTime;
- assert(encode_auth_setting(authUser, authPassword, pubkey, &authToken) == 0);
- assert(decode_auth_setting(0, authToken, privkey, &decodeUser, &decodePassword, &decodeTime) == 0);
+ int use_pkcs1_padding = 1;
+ assert(encode_auth_setting(authUser, authPassword, pubkey, &authToken, use_pkcs1_padding) == 0);
+ assert(decode_auth_setting(0, authToken, privkey, &decodeUser, &decodePassword, &decodeTime, use_pkcs1_padding) == 0);
assert(strcmp(decodeUser, authUser) == 0);
assert(strcmp(decodePassword, authPassword) == 0);
diff --git a/src/iperf_auth.c b/src/iperf_auth.c
index eb4610f..2025a71 100644
--- a/src/iperf_auth.c
+++ b/src/iperf_auth.c
@@ -44,6 +44,10 @@
#include <openssl/sha.h>
#include <openssl/buffer.h>
#include <openssl/err.h>
+#if OPENSSL_VERSION_MAJOR >= 3
+#include <openssl/evp.h>
+#include <openssl/core_names.h>
+#endif
const char *auth_text_format = "user: %s\npwd: %s\nts: %ld";
@@ -224,61 +224,123 @@ int test_load_private_key_from_file(const char *file){
return 0;
}
-int encrypt_rsa_message(const char *plaintext, EVP_PKEY *public_key, unsigned char **encryptedtext) {
+int encrypt_rsa_message(const char *plaintext, EVP_PKEY *public_key, unsigned char **encryptedtext, int use_pkcs1_padding) {
+#if OPENSSL_VERSION_MAJOR >= 3
+ EVP_PKEY_CTX *ctx;
+#else
RSA *rsa = NULL;
- unsigned char *rsa_buffer = NULL, pad = RSA_PKCS1_PADDING;
- int keysize, encryptedtext_len, rsa_buffer_len;
-
+#endif
+ unsigned char *rsa_buffer = NULL;
+ size_t encryptedtext_len = 0;
+ int rsa_buffer_len, keysize;
+
+#if OPENSSL_VERSION_MAJOR >= 3
+ int rc;
+ ctx = EVP_PKEY_CTX_new_from_pkey(NULL, public_key, "");
+ /* See evp_pkey_rsa(7) and provider-keymgmt(7) */
+ rc = EVP_PKEY_get_int_param(public_key, OSSL_PKEY_PARAM_MAX_SIZE, &keysize); /* XXX not really keysize */
+ if (!rc) {
+ goto errreturn;
+ }
+#else
rsa = EVP_PKEY_get1_RSA(public_key);
keysize = RSA_size(rsa);
-
+#endif
rsa_buffer = OPENSSL_malloc(keysize * 2);
*encryptedtext = (unsigned char*)OPENSSL_malloc(keysize);
BIO *bioBuff = BIO_new_mem_buf((void*)plaintext, (int)strlen(plaintext));
rsa_buffer_len = BIO_read(bioBuff, rsa_buffer, keysize * 2);
- encryptedtext_len = RSA_public_encrypt(rsa_buffer_len, rsa_buffer, *encryptedtext, rsa, pad);
+ int padding = RSA_PKCS1_OAEP_PADDING;
+ if (use_pkcs1_padding){
+ padding = RSA_PKCS1_PADDING;
+ }
+#if OPENSSL_VERSION_MAJOR >= 3
+ EVP_PKEY_encrypt_init(ctx);
+ EVP_PKEY_CTX_set_rsa_padding(ctx, padding);
+
+ EVP_PKEY_encrypt(ctx, *encryptedtext, &encryptedtext_len, rsa_buffer, rsa_buffer_len);
+ EVP_PKEY_CTX_free(ctx);
+#else
+ encryptedtext_len = RSA_public_encrypt(rsa_buffer_len, rsa_buffer, *encryptedtext, rsa, padding);
RSA_free(rsa);
+#endif
OPENSSL_free(rsa_buffer);
BIO_free(bioBuff);
if (encryptedtext_len < 0) {
- /* We probably shoudln't be printing stuff like this */
- fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL));
+ goto errreturn;
}
return encryptedtext_len;
+
+ errreturn:
+ fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL));
+ return 0;
}
-int decrypt_rsa_message(const unsigned char *encryptedtext, const int encryptedtext_len, EVP_PKEY *private_key, unsigned char **plaintext) {
+int decrypt_rsa_message(const unsigned char *encryptedtext, const int encryptedtext_len, EVP_PKEY *private_key, unsigned char **plaintext, int use_pkcs1_padding) {
+#if OPENSSL_VERSION_MAJOR >= 3
+ EVP_PKEY_CTX *ctx;
+#else
RSA *rsa = NULL;
- unsigned char *rsa_buffer = NULL, pad = RSA_PKCS1_PADDING;
- int plaintext_len, rsa_buffer_len, keysize;
+#endif
+ unsigned char *rsa_buffer = NULL;
+ size_t plaintext_len = 0;
+ int rsa_buffer_len, keysize;
+#if OPENSSL_VERSION_MAJOR >= 3
+ int rc;
+ ctx = EVP_PKEY_CTX_new_from_pkey(NULL, private_key, "");
+ /* See evp_pkey_rsa(7) and provider-keymgmt(7) */
+ rc = EVP_PKEY_get_int_param(private_key, OSSL_PKEY_PARAM_MAX_SIZE, &keysize); /* XXX not really keysize */
+ if (!rc) {
+ goto errreturn;
+ }
+#else
rsa = EVP_PKEY_get1_RSA(private_key);
-
keysize = RSA_size(rsa);
+#endif
rsa_buffer = OPENSSL_malloc(keysize * 2);
*plaintext = (unsigned char*)OPENSSL_malloc(keysize);
BIO *bioBuff = BIO_new_mem_buf((void*)encryptedtext, encryptedtext_len);
rsa_buffer_len = BIO_read(bioBuff, rsa_buffer, keysize * 2);
- plaintext_len = RSA_private_decrypt(rsa_buffer_len, rsa_buffer, *plaintext, rsa, pad);
+ int padding = RSA_PKCS1_OAEP_PADDING;
+ if (use_pkcs1_padding){
+ padding = RSA_PKCS1_PADDING;
+ }
+#if OPENSSL_VERSION_MAJOR >= 3
+ plaintext_len = keysize;
+ EVP_PKEY_decrypt_init(ctx);
+ int ret = EVP_PKEY_CTX_set_rsa_padding(ctx, padding);
+ if (ret < 0){
+ goto errreturn;
+ }
+ EVP_PKEY_decrypt(ctx, *plaintext, &plaintext_len, rsa_buffer, rsa_buffer_len);
+ EVP_PKEY_CTX_free(ctx);
+#else
+ plaintext_len = RSA_private_decrypt(rsa_buffer_len, rsa_buffer, *plaintext, rsa, padding);
RSA_free(rsa);
+#endif
+
OPENSSL_free(rsa_buffer);
BIO_free(bioBuff);
if (plaintext_len < 0) {
- /* We probably shoudln't be printing stuff like this */
- fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL));
+ plaintext_len = 0;
}
return plaintext_len;
+
+ errreturn:
+ fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL));
+ return 0;
}
-int encode_auth_setting(const char *username, const char *password, EVP_PKEY *public_key, char **authtoken){
+int encode_auth_setting(const char *username, const char *password, EVP_PKEY *public_key, char **authtoken, int use_pkcs1_padding){
time_t t = time(NULL);
time_t utc_seconds = mktime(localtime(&t));
@@ -295,7 +353,7 @@ int encode_auth_setting(const char *username, const char *password, EVP_PKEY *pu
unsigned char *encrypted = NULL;
int encrypted_len;
- encrypted_len = encrypt_rsa_message(text, public_key, &encrypted);
+ encrypted_len = encrypt_rsa_message(text, public_key, &encrypted, use_pkcs1_padding);
free(text);
if (encrypted_len < 0) {
return -1;
@@ -306,14 +364,14 @@ int encode_auth_setting(const char *username, const char *password, EVP_PKEY *pu
return (0); //success
}
-int decode_auth_setting(int enable_debug, const char *authtoken, EVP_PKEY *private_key, char **username, char **password, time_t *ts){
+int decode_auth_setting(int enable_debug, const char *authtoken, EVP_PKEY *private_key, char **username, char **password, time_t *ts, int use_pkcs1_padding){
unsigned char *encrypted_b64 = NULL;
size_t encrypted_len_b64;
Base64Decode(authtoken, &encrypted_b64, &encrypted_len_b64);
unsigned char *plaintext = NULL;
int plaintext_len;
- plaintext_len = decrypt_rsa_message(encrypted_b64, encrypted_len_b64, private_key, &plaintext);
+ plaintext_len = decrypt_rsa_message(encrypted_b64, encrypted_len_b64, private_key, &plaintext, use_pkcs1_padding);
free(encrypted_b64);
if (plaintext_len < 0) {
return -1;
diff --git a/src/iperf_auth.h b/src/iperf_auth.h
index ffadbf3e5..eedd45abd 100644
--- a/src/iperf_auth.h
+++ b/src/iperf_auth.h
@@ -35,7 +35,7 @@ EVP_PKEY *load_pubkey_from_file(const char *file);
EVP_PKEY *load_pubkey_from_base64(const char *buffer);
EVP_PKEY *load_privkey_from_file(const char *file);
EVP_PKEY *load_privkey_from_base64(const char *buffer);
-int encode_auth_setting(const char *username, const char *password, EVP_PKEY *public_key, char **authtoken);
-int decode_auth_setting(int enable_debug, const char *authtoken, EVP_PKEY *private_key, char **username, char **password, time_t *ts);
+int encode_auth_setting(const char *username, const char *password, EVP_PKEY *public_key, char **authtoken, int use_pkcs1_padding);
+int decode_auth_setting(int enable_debug, const char *authtoken, EVP_PKEY *private_key, char **username, char **password, time_t *ts, int use_pkcs1_padding);
int check_authentication(const char *username, const char *password, const time_t ts, const char *filename);
ssize_t iperf_getpass (char **lineptr, size_t *n, FILE *stream);
diff --git a/src/iperf_locale.c b/src/iperf_locale.c
index d5a5354..3b6860d 100644
--- a/src/iperf_locale.c
+++ b/src/iperf_locale.c
@@ -128,6 +128,7 @@ const char usage_longstr[] = "Usage: iperf3 [-s|-c host] [options]\n"
" authentication credentials\n"
" --authorized-users-path path to the configuration file containing user\n"
" credentials\n"
+ " --use-pkcs1-padding use pkcs1 padding at your own risk\n"
#endif //HAVE_SSL
"Client specific:\n"
" -c, --client <host> run in client mode, connecting to <host>\n"
diff --git a/src/iperf3.1 b/src/iperf3.1
index 97d66ed..6fe71c9 100644
--- a/src/iperf3.1
+++ b/src/iperf3.1
@@ -161,6 +161,15 @@ Optionally, a format specification can be passed to customize the
timestamps, see
.BR strftime ( 3 ).
.TP
+.BR --use-pkcs1-padding
+This option is only meaningful when using iperf3's authentication
+features. Versions of iperf3 prior to 3.17 used PCKS1 padding in the
+RSA-encrypted credentials, which was vulnerable to a side-channel
+attack that could reveal a server's private key. Beginning with
+iperf-3.17, OAEP padding is used, however this is a breaking change
+that is not compatible with older iperf3 versions. Use this option to
+preserve the less secure, but more compatible, behavior.
+.TP
.BR -d ", " --debug " "
emit debugging output.
Primarily (perhaps exclusively) of use to developers.

View File

@ -1,9 +1,25 @@
--- !Policy
product_versions:
- rhel-9
- fedora-*
decision_context: bodhi_update_push_testing
subject_type: koji_build
rules:
- !PassingTestCaseRule {test_case_name: fedora-ci.koji-build./plans/tier1-public.functional}
#Rawhide
--- !Policy
product_versions:
- fedora-*
decision_context: bodhi_update_push_stable
subject_type: koji_build
rules:
- !PassingTestCaseRule {test_case_name: fedora-ci.koji-build./plans/tier1-public.functional}
#gating rhel
--- !Policy
product_versions:
- rhel-*
decision_context: osci_compose_gate
rules:
- !PassingTestCaseRule {test_case_name: osci.brew-build./plans/tier1-internal.functional}
- !PassingTestCaseRule {test_case_name: osci.brew-build./plans/public.functional}
- !PassingTestCaseRule {test_case_name: baseos-ci.brew-build.tier1.functional}
- !PassingTestCaseRule {test_case_name: baseos-ci.brew-build.tedude.validation}
- !PassingTestCaseRule {test_case_name: osci.brew-build./plans/tier1-public.functional}
- !PassingTestCaseRule {test_case_name: osci.brew-build./plans/tier1-internal.functional}

View File

@ -1,6 +1,6 @@
Name: iperf3
Version: 3.9
Release: 12%{?dist}
Release: 13%{?dist}
Summary: Measurement tool for TCP/UDP bandwidth performance
License: BSD
@ -8,6 +8,7 @@ URL: https://github.com/esnet/iperf
Source0: https://github.com/esnet/iperf/archive/%{version}.tar.gz
Patch0000: 0000-cve-2023-38403.patch
Patch0001: 0001-cve-2023-7250.patch
Patch0002: 0002-cve-2024-26306.patch
BuildRequires: libuuid-devel
BuildRequires: gcc
@ -55,6 +56,9 @@ rm -f %{buildroot}%{_libdir}/libiperf.la
%{_libdir}/*.so
%changelog
* Tue Jun 11 2024 Michal Ruprich <mruprich@redhat.com> - 3.9-13
- Resolves: RHEL-29579 - vulnerable to marvin attack if the authentication option is used
* Tue Jun 04 2024 Michal Ruprich <mruprich@redhat.com> - 3.9-12
- Resolves: RHEL-39975 - possible denial of service

36
plans.fmf Normal file
View File

@ -0,0 +1,36 @@
/tier1-internal:
plan:
import:
url: https://src.fedoraproject.org/tests/iperf3.git
name: /plans/tier1/internal
/tier1-public:
plan:
import:
url: https://src.fedoraproject.org/tests/iperf3.git
name: /plans/tier1/public
/tier2-tier3-internal:
plan:
import:
url: https://src.fedoraproject.org/tests/iperf3.git
name: /plans/tier2-tier3/internal
/tier2-tier3-public:
plan:
import:
url: https://src.fedoraproject.org/tests/iperf3.git
name: /plans/tier2-tier3/public
/others-internal:
plan:
import:
url: https://src.fedoraproject.org/tests/iperf3.git
name: /plans/others/internal
/others-public:
plan:
import:
url: https://src.fedoraproject.org/tests/iperf3.git
name: /plans/others/public

View File

@ -1,7 +0,0 @@
summary: Test plan that runs all tests from tests repo.
discover:
how: fmf
url: https://src.fedoraproject.org/tests/iperf3.git
execute:
how: tmt

View File

@ -1,11 +0,0 @@
summary: CI plan, picks Tier1 tests, runs in beakerlib.
discover:
- name: rhel
how: fmf
filter: 'tier: 1'
url: git://pkgs.devel.redhat.com/tests/iperf3
execute:
how: tmt
adjust:
enabled: false
when: distro == centos-stream-9