232 lines
10 KiB
Diff
232 lines
10 KiB
Diff
|
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 f137b07..f6c0313 100755
|
||
|
--- a/src/iperf.h
|
||
|
+++ b/src/iperf.h
|
||
|
@@ -260,6 +260,7 @@ struct iperf_test
|
||
|
int ctrl_sck_mss; /* MSS for the control channel */
|
||
|
char *server_rsa_private_key;
|
||
|
char *server_authorized_users;
|
||
|
+ int use_pkcs1_padding;
|
||
|
|
||
|
/* boolean variables for Options */
|
||
|
int daemon; /* -D option */
|
||
|
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);
|
||
|
@@ -1100,7 +1104,7 @@ iperf_parse_arguments(struct iperf_test *test, int argc, char **argv)
|
||
|
i_errno = IESETCLIENTAUTH;
|
||
|
return -1;
|
||
|
}
|
||
|
- encode_auth_setting(client_username, client_password, client_rsa_public_key, &test->settings->authtoken);
|
||
|
+ encode_auth_setting(client_username, client_password, client_rsa_public_key, &test->settings->authtoken, test->use_pkcs1_padding);
|
||
|
}
|
||
|
|
||
|
if (test->role == 'c' && (test->server_rsa_private_key || test->server_authorized_users)){
|
||
|
@@ -1346,7 +1350,7 @@ int test_is_authorized(struct iperf_test *test){
|
||
|
if (test->settings->authtoken){
|
||
|
char *username = NULL, *password = NULL;
|
||
|
time_t ts;
|
||
|
- decode_auth_setting(test->debug, test->settings->authtoken, test->server_rsa_private_key, &username, &password, &ts);
|
||
|
+ decode_auth_setting(test->debug, test->settings->authtoken, test->server_rsa_private_key, &username, &password, &ts, test->use_pkcs1_padding);
|
||
|
int ret = check_authentication(username, password, ts, test->server_authorized_users);
|
||
|
if (ret == 0){
|
||
|
iperf_printf(test, report_authetication_successed, username, ts);
|
||
|
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/iperf_api.h b/src/iperf_api.h
|
||
|
index 3a5df03..255227c 100755
|
||
|
--- a/src/iperf_api.h
|
||
|
+++ b/src/iperf_api.h
|
||
|
@@ -68,6 +68,7 @@ struct iperf_stream;
|
||
|
#define OPT_SERVER_AUTHORIZED_USERS 15
|
||
|
#define OPT_PACING_TIMER 16
|
||
|
#define OPT_CONNECT_TIMEOUT 17
|
||
|
+#define OPT_USE_PKCS1_PADDING 30
|
||
|
|
||
|
/* states */
|
||
|
#define TEST_START 1
|
||
|
diff --git a/src/iperf_auth.h b/src/iperf_auth.h
|
||
|
index 38971d8..1f78699 100644
|
||
|
--- a/src/iperf_auth.h
|
||
|
+++ b/src/iperf_auth.h
|
||
|
@@ -30,7 +30,7 @@
|
||
|
|
||
|
int test_load_pubkey(const char *public_keyfile);
|
||
|
int test_load_private_key(const char *private_keyfile);
|
||
|
-int encode_auth_setting(const char *username, const char *password, const char *public_keyfile, char **authtoken);
|
||
|
-int decode_auth_setting(int enable_debug, const char *authtoken, const char *private_keyfile, char **username, char **password, time_t *ts);
|
||
|
+int encode_auth_setting(const char *username, const char *password, const char *public_keyfile, char **authtoken, int use_pkcs1_padding);
|
||
|
+int decode_auth_setting(int enable_debug, const char *authtoken, const char *private_keyfile, 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/iperf3.1 b/src/iperf3.1
|
||
|
index 1be8cc3..87c3e02 100644
|
||
|
--- a/src/iperf3.1
|
||
|
+++ b/src/iperf3.1
|
||
|
@@ -155,6 +155,15 @@ send output to a log file.
|
||
|
force flushing output at every interval.
|
||
|
Used to avoid buffering when sending output to pipe.
|
||
|
.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.
|
||
|
diff --git a/src/iperf_auth.c b/src/iperf_auth.c
|
||
|
index f8d2b0a..2d7d519 100644
|
||
|
--- a/src/iperf_auth.c
|
||
|
+++ b/src/iperf_auth.c
|
||
|
@@ -194,11 +194,12 @@ int test_load_private_key(const char *file){
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
-int encrypt_rsa_message(const char *plaintext, const char *public_keyfile, unsigned char **encryptedtext) {
|
||
|
+int encrypt_rsa_message(const char *plaintext, const char *public_keyfile, unsigned char **encryptedtext, int use_pkcs1_padding) {
|
||
|
EVP_PKEY *public_key = NULL;
|
||
|
RSA *rsa = NULL;
|
||
|
- unsigned char *rsa_buffer = NULL, pad = RSA_PKCS1_PADDING;
|
||
|
- int keysize, encryptedtext_len, rsa_buffer_len;
|
||
|
+ unsigned char *rsa_buffer = NULL;
|
||
|
+ size_t encryptedtext_len = 0;
|
||
|
+ int rsa_buffer_len, keysize;
|
||
|
|
||
|
public_key = load_pubkey(public_keyfile);
|
||
|
rsa = EVP_PKEY_get1_RSA(public_key);
|
||
|
@@ -210,20 +211,35 @@ int encrypt_rsa_message(const char *plaintext, const char *public_keyfile, unsig
|
||
|
|
||
|
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;
|
||
|
+ }
|
||
|
+
|
||
|
+ encryptedtext_len = RSA_public_encrypt(rsa_buffer_len, rsa_buffer, *encryptedtext, rsa, padding);
|
||
|
|
||
|
RSA_free(rsa);
|
||
|
OPENSSL_free(rsa_buffer);
|
||
|
- OPENSSL_free(bioBuff);
|
||
|
+ OPENSSL_free(bioBuff);
|
||
|
+
|
||
|
+ if (encryptedtext_len < 0) {
|
||
|
+ goto errreturn;
|
||
|
+ }
|
||
|
+
|
||
|
+ return encryptedtext_len;
|
||
|
|
||
|
- 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, const char *private_keyfile, unsigned char **plaintext) {
|
||
|
+int decrypt_rsa_message(const unsigned char *encryptedtext, const int encryptedtext_len, const char *private_keyfile, unsigned char **plaintext, int use_pkcs1_padding) {
|
||
|
EVP_PKEY *private_key = NULL;
|
||
|
RSA *rsa = NULL;
|
||
|
- unsigned char *rsa_buffer = NULL, pad = RSA_PKCS1_PADDING;
|
||
|
- int plaintext_len, rsa_buffer_len, keysize;
|
||
|
+ unsigned char *rsa_buffer = NULL;
|
||
|
+ size_t plaintext_len = 0;
|
||
|
+ int rsa_buffer_len, keysize;
|
||
|
|
||
|
private_key = load_key(private_keyfile);
|
||
|
rsa = EVP_PKEY_get1_RSA(private_key);
|
||
|
@@ -235,35 +250,45 @@ int decrypt_rsa_message(const unsigned char *encryptedtext, const int encryptedt
|
||
|
|
||
|
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;
|
||
|
+ }
|
||
|
+
|
||
|
+ plaintext_len = RSA_private_decrypt(rsa_buffer_len, rsa_buffer, *plaintext, rsa, padding);
|
||
|
|
||
|
RSA_free(rsa);
|
||
|
OPENSSL_free(rsa_buffer);
|
||
|
OPENSSL_free(bioBuff);
|
||
|
|
||
|
+ if (plaintext_len < 0) {
|
||
|
+ plaintext_len = 0;
|
||
|
+ }
|
||
|
+
|
||
|
return plaintext_len;
|
||
|
}
|
||
|
|
||
|
-int encode_auth_setting(const char *username, const char *password, const char *public_keyfile, char **authtoken){
|
||
|
+int encode_auth_setting(const char *username, const char *password, const char *public_keyfile, char **authtoken, int use_pkcs1_padding){
|
||
|
time_t t = time(NULL);
|
||
|
time_t utc_seconds = mktime(localtime(&t));
|
||
|
char text[150];
|
||
|
sprintf (text, "user: %s\npwd: %s\nts: %ld", username, password, utc_seconds);
|
||
|
unsigned char *encrypted = NULL;
|
||
|
int encrypted_len;
|
||
|
- encrypted_len = encrypt_rsa_message(text, public_keyfile, &encrypted);
|
||
|
+ encrypted_len = encrypt_rsa_message(text, public_keyfile, &encrypted, use_pkcs1_padding);
|
||
|
Base64Encode(encrypted, encrypted_len, authtoken);
|
||
|
return (0); //success
|
||
|
}
|
||
|
|
||
|
-int decode_auth_setting(int enable_debug, char *authtoken, const char *private_keyfile, char **username, char **password, time_t *ts){
|
||
|
+int decode_auth_setting(int enable_debug, char *authtoken, const char *private_keyfile, 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_keyfile, &plaintext);
|
||
|
+ plaintext_len = decrypt_rsa_message(encrypted_b64, encrypted_len_b64, private_keyfile, &plaintext, use_pkcs1_padding);
|
||
|
plaintext[plaintext_len] = '\0';
|
||
|
|
||
|
char s_username[20], s_password[20];
|