/* * 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 #include #include #include /* FreeBSD needs _WITH_GETLINE to enable the getline() declaration */ #define _WITH_GETLINE #include #include #include #include #if defined(HAVE_SSL) #include #include #include #include #include #include #if OPENSSL_VERSION_MAJOR >= 3 #include #include #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, int use_pkcs1_padding) { #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); 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) { 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 use_pkcs1_padding) { #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); 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); /* Treat a decryption error as an empty string. */ if (plaintext_len < 0) { 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 use_pkcs1_padding){ 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, use_pkcs1_padding); 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, int use_pkcs1_padding){ 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, use_pkcs1_padding); 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; }