diff options
Diffstat (limited to 'src/iperf_auth.c')
-rw-r--r-- | src/iperf_auth.c | 438 |
1 files changed, 438 insertions, 0 deletions
diff --git a/src/iperf_auth.c b/src/iperf_auth.c new file mode 100644 index 0000000..c89bde7 --- /dev/null +++ b/src/iperf_auth.c @@ -0,0 +1,438 @@ +/* + * iperf, Copyright (c) 2014-2023, The Regents of the University of + * California, through Lawrence Berkeley National Laboratory (subject + * to receipt of any required approvals from the U.S. Dept. of + * Energy). All rights reserved. + * + * If you have questions about your rights to use or distribute this + * software, please contact Berkeley Lab's Technology Transfer + * Department at TTD@lbl.gov. + * + * NOTICE. This software is owned by the U.S. Department of Energy. + * As such, the U.S. Government has been granted for itself and others + * acting on its behalf a paid-up, nonexclusive, irrevocable, + * worldwide license in the Software to reproduce, prepare derivative + * works, and perform publicly and display publicly. Beginning five + * (5) years after the date permission to assert copyright is obtained + * from the U.S. Department of Energy, and subject to any subsequent + * five (5) year renewals, the U.S. Government is granted for itself + * and others acting on its behalf a paid-up, nonexclusive, + * irrevocable, worldwide license in the Software to reproduce, + * prepare derivative works, distribute copies to the public, perform + * publicly and display publicly, and to permit others to do so. + * + * This code is distributed under a BSD style license, see the LICENSE file + * for complete information. + */ + +#include "iperf_config.h" + +#include <string.h> +#include <assert.h> +#include <time.h> +#include <sys/types.h> +/* FreeBSD needs _WITH_GETLINE to enable the getline() declaration */ +#define _WITH_GETLINE +#include <stdio.h> +#include <termios.h> +#include <inttypes.h> +#include <stdint.h> + +#if defined(HAVE_SSL) + +#include <openssl/rsa.h> +#include <openssl/bio.h> +#include <openssl/pem.h> +#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: %"PRId64; + +void sha256(const char *string, char outputBuffer[65]) +{ + unsigned char hash[SHA256_DIGEST_LENGTH]; + + SHA256((const unsigned char *) string, strlen(string), hash); + int i = 0; + for(i = 0; i < SHA256_DIGEST_LENGTH; i++) + { + sprintf(outputBuffer + (i * 2), "%02x", hash[i]); + } + outputBuffer[64] = 0; +} + +int check_authentication(const char *username, const char *password, const time_t ts, const char *filename, int skew_threshold){ + time_t t = time(NULL); + time_t utc_seconds = mktime(localtime(&t)); + if ( (utc_seconds - ts) > skew_threshold || (utc_seconds - ts) < -skew_threshold ) { + return 1; + } + + char passwordHash[65]; + char salted[strlen(username) + strlen(password) + 3]; + sprintf(salted, "{%s}%s", username, password); + sha256(&salted[0], passwordHash); + + char *s_username, *s_password; + int i; + FILE *ptr_file; + char buf[1024]; + + ptr_file =fopen(filename,"r"); + if (!ptr_file) + return 2; + + while (fgets(buf,1024, ptr_file)){ + //strip the \n or \r\n chars + for (i = 0; buf[i] != '\0'; i++){ + if (buf[i] == '\n' || buf[i] == '\r'){ + buf[i] = '\0'; + break; + } + } + //skip empty / not completed / comment lines + if (strlen(buf) == 0 || strchr(buf, ',') == NULL || buf[0] == '#'){ + continue; + } + s_username = strtok(buf, ","); + s_password = strtok(NULL, ","); + if (strcmp( username, s_username ) == 0 && strcmp( passwordHash, s_password ) == 0){ + fclose(ptr_file); + return 0; + } + } + fclose(ptr_file); + return 3; +} + + +int Base64Encode(const unsigned char* buffer, const size_t length, char** b64text) { //Encodes a binary safe base 64 string + BIO *bio, *b64; + BUF_MEM *bufferPtr; + + b64 = BIO_new(BIO_f_base64()); + bio = BIO_new(BIO_s_mem()); + bio = BIO_push(b64, bio); + + BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Ignore newlines - write everything in one line + BIO_write(bio, buffer, length); + BIO_flush(bio); + BIO_get_mem_ptr(bio, &bufferPtr); + *b64text = strndup( (*bufferPtr).data, (*bufferPtr).length ); + BIO_free_all(bio); + + return (0); //success +} + +size_t calcDecodeLength(const char* b64input) { //Calculates the length of a decoded string + size_t len = strlen(b64input), padding = 0; + if (b64input[len-1] == '=' && b64input[len-2] == '=') //last two chars are = + padding = 2; + else if (b64input[len-1] == '=') //last char is = + padding = 1; + + return (len*3)/4 - padding; +} + +int Base64Decode(const char* b64message, unsigned char** buffer, size_t* length) { //Decodes a base64 encoded string + BIO *bio, *b64; + + int decodeLen = calcDecodeLength(b64message); + *buffer = (unsigned char*)malloc(decodeLen + 1); + (*buffer)[decodeLen] = '\0'; + + bio = BIO_new_mem_buf(b64message, -1); + b64 = BIO_new(BIO_f_base64()); + bio = BIO_push(b64, bio); + + BIO_set_flags(bio, BIO_FLAGS_BASE64_NO_NL); //Do not use newlines to flush buffer + *length = BIO_read(bio, *buffer, strlen(b64message)); + assert(*length == decodeLen); //length should equal decodeLen, else something went horribly wrong + BIO_free_all(bio); + + return (0); //success +} + +EVP_PKEY *load_pubkey_from_file(const char *file) { + BIO *key = NULL; + EVP_PKEY *pkey = NULL; + + if (file) { + key = BIO_new_file(file, "r"); + if (key != NULL) { + pkey = PEM_read_bio_PUBKEY(key, NULL, NULL, NULL); + BIO_free(key); + } + } + return (pkey); +} + +EVP_PKEY *load_pubkey_from_base64(const char *buffer) { + unsigned char *key = NULL; + size_t key_len; + Base64Decode(buffer, &key, &key_len); + + BIO* bio = BIO_new(BIO_s_mem()); + BIO_write(bio, key, key_len); + free(key); + EVP_PKEY *pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL); + BIO_free(bio); + return (pkey); +} + +EVP_PKEY *load_privkey_from_file(const char *file) { + BIO *key = NULL; + EVP_PKEY *pkey = NULL; + + if (file) { + key = BIO_new_file(file, "r"); + if (key != NULL) { + pkey = PEM_read_bio_PrivateKey(key, NULL, NULL, NULL); + BIO_free(key); + } + } + return (pkey); +} + +EVP_PKEY *load_privkey_from_base64(const char *buffer) { + unsigned char *key = NULL; + size_t key_len; + Base64Decode(buffer, &key, &key_len); + + BIO* bio = BIO_new(BIO_s_mem()); + BIO_write(bio, key, key_len); + free(key); + EVP_PKEY *pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL); + BIO_free(bio); + return (pkey); +} + +int test_load_pubkey_from_file(const char *file){ + EVP_PKEY *key = load_pubkey_from_file(file); + if (key == NULL){ + return -1; + } + EVP_PKEY_free(key); + return 0; +} + +int test_load_private_key_from_file(const char *file){ + EVP_PKEY *key = load_privkey_from_file(file); + if (key == NULL){ + return -1; + } + EVP_PKEY_free(key); + return 0; +} + +int encrypt_rsa_message(const char *plaintext, EVP_PKEY *public_key, unsigned char **encryptedtext) { +#if OPENSSL_VERSION_MAJOR >= 3 + EVP_PKEY_CTX *ctx; +#else + RSA *rsa = NULL; +#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); +#if OPENSSL_VERSION_MAJOR >= 3 + EVP_PKEY_encrypt_init(ctx); + 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, RSA_PKCS1_PADDING); + RSA_free(rsa); +#endif + + OPENSSL_free(rsa_buffer); + BIO_free(bioBuff); + + if (encryptedtext_len <= 0) { + 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) { +#if OPENSSL_VERSION_MAJOR >= 3 + EVP_PKEY_CTX *ctx; +#else + RSA *rsa = NULL; +#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); +#if OPENSSL_VERSION_MAJOR >= 3 + plaintext_len = keysize; + EVP_PKEY_decrypt_init(ctx); + 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, RSA_PKCS1_PADDING); + RSA_free(rsa); +#endif + + OPENSSL_free(rsa_buffer); + BIO_free(bioBuff); + + if (plaintext_len <= 0) { + goto errreturn; + } + + 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){ + time_t t = time(NULL); + time_t utc_seconds = mktime(localtime(&t)); + + /* + * Compute a pessimistic/conservative estimate of storage required. + * It's OK to allocate too much storage but too little is bad. + */ + const int text_len = strlen(auth_text_format) + strlen(username) + strlen(password) + 32; + char *text = (char *) calloc(text_len, sizeof(char)); + if (text == NULL) { + return -1; + } + snprintf(text, text_len, auth_text_format, username, password, (int64_t)utc_seconds); + + unsigned char *encrypted = NULL; + int encrypted_len; + encrypted_len = encrypt_rsa_message(text, public_key, &encrypted); + free(text); + if (encrypted_len < 0) { + return -1; + } + Base64Encode(encrypted, encrypted_len, authtoken); + OPENSSL_free(encrypted); + + return (0); //success +} + +int decode_auth_setting(int enable_debug, const char *authtoken, EVP_PKEY *private_key, char **username, char **password, time_t *ts){ + unsigned char *encrypted_b64 = NULL; + size_t encrypted_len_b64; + int64_t utc_seconds; + 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); + free(encrypted_b64); + if (plaintext_len < 0) { + return -1; + } + plaintext[plaintext_len] = '\0'; + + char *s_username, *s_password; + s_username = (char *) calloc(plaintext_len, sizeof(char)); + if (s_username == NULL) { + return -1; + } + s_password = (char *) calloc(plaintext_len, sizeof(char)); + if (s_password == NULL) { + free(s_username); + return -1; + } + + int rc = sscanf((char *) plaintext, auth_text_format, s_username, s_password, &utc_seconds); + if (rc != 3) { + free(s_password); + free(s_username); + return -1; + } + + if (enable_debug) { + printf("Auth Token Content:\n%s\n", plaintext); + printf("Auth Token Credentials:\n--> %s %s\n", s_username, s_password); + } + *username = s_username; + *password = s_password; + *ts = (time_t)utc_seconds; + OPENSSL_free(plaintext); + return (0); +} + +#endif //HAVE_SSL + +ssize_t iperf_getpass (char **lineptr, size_t *n, FILE *stream) { + struct termios old, new; + ssize_t nread; + + /* Turn echoing off and fail if we can't. */ + if (tcgetattr (fileno (stream), &old) != 0) + return -1; + new = old; + new.c_lflag &= ~ECHO; + if (tcsetattr (fileno (stream), TCSAFLUSH, &new) != 0) + return -1; + + /* Read the password. */ + printf("Password: "); + nread = getline (lineptr, n, stream); + + /* Restore terminal. */ + (void) tcsetattr (fileno (stream), TCSAFLUSH, &old); + + //strip the \n or \r\n chars + char *buf = *lineptr; + int i; + for (i = 0; buf[i] != '\0'; i++){ + if (buf[i] == '\n' || buf[i] == '\r'){ + buf[i] = '\0'; + break; + } + } + + return nread; +} |