summaryrefslogtreecommitdiffstats
path: root/src/iperf_auth.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/iperf_auth.c')
-rw-r--r--src/iperf_auth.c438
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;
+}