From 299b356df6939f71619bf45bf7a7d2222e17d840 Mon Sep 17 00:00:00 2001 From: Sarah Larsen 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 run in client mode, connecting to \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];