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 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 #include #include +#if OPENSSL_VERSION_MAJOR >= 3 +#include +#include +#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 run in client mode, connecting to \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.