Add authentication functions to libiperf (#713)

Fixes #712.  A subsequent commit will add some information to the libiperf manual page.
diff --git a/src/iperf.h b/src/iperf.h
index 8469083..b33ba37 100755
--- a/src/iperf.h
+++ b/src/iperf.h
@@ -62,6 +62,11 @@
 #include "queue.h"
 #include "cjson.h"
 
+#if defined(HAVE_SSL)
+#include <openssl/bio.h>
+#include <openssl/evp.h>
+#endif // HAVE_SSL
+
 typedef uint64_t iperf_size_t;
 
 struct iperf_interval_results
@@ -141,7 +146,12 @@
     iperf_size_t blocks;            /* number of blocks (packets) to send */
     char      unit_format;          /* -f */
     int       num_ostreams;         /* SCTP initmsg settings */
+#if defined(HAVE_SSL)
     char      *authtoken;           /* Authentication token */
+    char      *client_username;
+    char      *client_password;
+    EVP_PKEY  *client_rsa_pubkey;
+#endif // HAVE_SSL
     int	      connect_timeout;	    /* socket connection timeout, in ms */
 };
 
@@ -257,8 +267,11 @@
     int       prot_listener;
 
     int	      ctrl_sck_mss;			/* MSS for the control channel */
-    char     *server_rsa_private_key;
-    char     *server_authorized_users;
+
+#if defined(HAVE_SSL)
+    char      *server_authorized_users;
+    EVP_PKEY  *server_rsa_private_key;
+#endif // HAVE_SSL
 
     /* boolean variables for Options */
     int       daemon;                           /* -D option */
diff --git a/src/iperf_api.c b/src/iperf_api.c
index 92252e6..8d3608b 100755
--- a/src/iperf_api.c
+++ b/src/iperf_api.c
@@ -83,6 +83,7 @@
 #include "iperf_locale.h"
 #include "version.h"
 #if defined(HAVE_SSL)
+#include <openssl/bio.h>
 #include "iperf_auth.h"
 #endif /* HAVE_SSL */
 
@@ -455,6 +456,26 @@
     ipt->settings->unit_format = unit_format;
 }
 
+#if defined(HAVE_SSL)
+void
+iperf_set_test_client_username(struct iperf_test *ipt, char *client_username)
+{
+    ipt->settings->client_username = client_username;
+}
+
+void
+iperf_set_test_client_password(struct iperf_test *ipt, char *client_password)
+{
+    ipt->settings->client_password = client_password;
+}
+
+void
+iperf_set_test_client_rsa_pubkey(struct iperf_test *ipt, char *client_rsa_pubkey_base64)
+{
+    ipt->settings->client_rsa_pubkey = load_pubkey_from_base64(client_rsa_pubkey_base64);
+}
+#endif // HAVE_SSL
+
 void
 iperf_set_test_bind_address(struct iperf_test *ipt, char *bnd_address)
 {
@@ -720,7 +741,7 @@
     blksize = 0;
     server_flag = client_flag = rate_flag = duration_flag = 0;
 #if defined(HAVE_SSL)
-    char *client_username = NULL, *client_rsa_public_key = NULL;
+    char *client_username = NULL, *client_rsa_public_key = NULL, *server_rsa_private_key = NULL;
 #endif /* HAVE_SSL */
 
     while ((flag = getopt_long(argc, argv, "p:f:i:D1VJvsc:ub:t:n:k:l:P:Rw:B:M:N46S:L:ZO:F:A:T:C:dI:hX:", longopts, NULL)) != -1) {
@@ -1049,7 +1070,7 @@
             client_rsa_public_key = strdup(optarg);
             break;
         case OPT_SERVER_RSA_PRIVATE_KEY:
-            test->server_rsa_private_key = strdup(optarg);
+            server_rsa_private_key = strdup(optarg);
             break;
         case OPT_SERVER_AUTHORIZED_USERS:
             test->server_authorized_users = strdup(optarg);
@@ -1113,24 +1134,30 @@
             return -1;
         }
 
-        if (test_load_pubkey(client_rsa_public_key) < 0){
+        if (test_load_pubkey_from_file(client_rsa_public_key) < 0){
             i_errno = IESETCLIENTAUTH;
             return -1;
         }
-        encode_auth_setting(client_username, client_password, client_rsa_public_key, &test->settings->authtoken);
+
+        test->settings->client_username = client_username;
+        test->settings->client_password = client_password;
+        test->settings->client_rsa_pubkey = load_pubkey_from_file(client_rsa_public_key);
     }
 
-    if (test->role == 'c' && (test->server_rsa_private_key || test->server_authorized_users)){
+    if (test->role == 'c' && (server_rsa_private_key || test->server_authorized_users)){
         i_errno = IESERVERONLY;
         return -1;
-    } else if (test->role == 's' && (test->server_rsa_private_key || test->server_authorized_users) && 
-        !(test->server_rsa_private_key && test->server_authorized_users)) {
+    } else if (test->role == 's' && (server_rsa_private_key || test->server_authorized_users) && 
+        !(server_rsa_private_key && test->server_authorized_users)) {
          i_errno = IESETSERVERAUTH;
         return -1;
-    } else if (test->role == 's' && test->server_rsa_private_key && test_load_private_key(test->server_rsa_private_key) < 0){
+    } else if (test->role == 's' && server_rsa_private_key && test_load_private_key_from_file(server_rsa_private_key) < 0){
         i_errno = IESETSERVERAUTH;
         return -1;
+    } else {
+        test->server_rsa_private_key = load_privkey_from_file(server_rsa_private_key);
     }
+
 #endif //HAVE_SSL
     if (blksize == 0) {
 	if (test->protocol->id == Pudp)
@@ -1529,8 +1556,10 @@
 	if (test->udp_counters_64bit)
 	    cJSON_AddNumberToObject(j, "udp_counters_64bit", iperf_get_test_udp_counters_64bit(test));
 #if defined(HAVE_SSL)
-    if (test->settings->authtoken)
+    if (test->settings->client_username && test->settings->client_password && test->settings->client_rsa_pubkey){
+        encode_auth_setting(test->settings->client_username, test->settings->client_password, test->settings->client_rsa_pubkey, &test->settings->authtoken);
         cJSON_AddStringToObject(j, "authtoken", test->settings->authtoken);
+    }
 #endif // HAVE_SSL
 	cJSON_AddStringToObject(j, "client_version", IPERF_VERSION);
 
@@ -2328,10 +2357,26 @@
     test->settings->burst = 0;
     test->settings->mss = 0;
     test->settings->tos = 0;
+
+#if defined(HAVE_SSL)
     if (test->settings->authtoken) {
-	free(test->settings->authtoken);
-	test->settings->authtoken = NULL;
+        free(test->settings->authtoken);
+        test->settings->authtoken = NULL;
     }
+    if (test->settings->client_username) {
+        free(test->settings->client_username);
+        test->settings->client_username = NULL;
+    }
+    if (test->settings->client_password) {
+        free(test->settings->client_password);
+        test->settings->client_password = NULL;
+    }
+    if (test->settings->client_rsa_pubkey) {
+        EVP_PKEY_free(test->settings->client_rsa_pubkey);
+        test->settings->client_rsa_pubkey = NULL;
+    }
+#endif /* HAVE_SSL */
+
     memset(test->cookie, 0, COOKIE_SIZE);
     test->multisend = 10;	/* arbitrary */
     test->udp_counters_64bit = 0;
diff --git a/src/iperf_api.h b/src/iperf_api.h
index 7ebeb3c..7e8346b 100755
--- a/src/iperf_api.h
+++ b/src/iperf_api.h
@@ -145,6 +145,12 @@
 void	iperf_set_test_one_off( struct iperf_test* ipt, int one_off );
 void    iperf_set_test_tos( struct iperf_test* ipt, int tos );
 
+#if defined(HAVE_SSL)
+void    iperf_set_test_client_username(struct iperf_test *ipt, char *client_username);
+void    iperf_set_test_client_password(struct iperf_test *ipt, char *client_password);
+void    iperf_set_test_client_rsa_pubkey(struct iperf_test *ipt, char *client_rsa_pubkey_base64);
+#endif // HAVE_SSL
+
 /**
  * exchange_parameters - handles the param_Exchange part for client
  *
diff --git a/src/iperf_auth.c b/src/iperf_auth.c
index f8d2b0a..b9cd98a 100644
--- a/src/iperf_auth.c
+++ b/src/iperf_auth.c
@@ -103,7 +103,7 @@
 }
 
 
-int Base64Encode(unsigned char* buffer, const size_t length, char** b64text) { //Encodes a binary safe base 64 string
+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;
 
@@ -133,7 +133,7 @@
     return (len*3)/4 - padding;
 }
 
-int Base64Decode(char* b64message, unsigned char** buffer, size_t* length) { //Decodes a base64 encoded string
+int Base64Decode(const char* b64message, unsigned char** buffer, size_t* length) { //Decodes a base64 encoded string
     BIO *bio, *b64;
 
     int decodeLen = calcDecodeLength(b64message);
@@ -153,7 +153,7 @@
 }
 
 
-EVP_PKEY *load_pubkey(const char *file) {
+EVP_PKEY *load_pubkey_from_file(const char *file) {
     BIO *key = NULL;
     EVP_PKEY *pkey = NULL;
 
@@ -164,7 +164,18 @@
     return (pkey);
 }   
 
-EVP_PKEY *load_key(const char *file) {
+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);
+    EVP_PKEY *pkey = PEM_read_bio_PUBKEY(bio, NULL, NULL, NULL);
+    return (pkey);
+}
+
+EVP_PKEY *load_privkey_from_file(const char *file) {
     BIO *key = NULL;
     EVP_PKEY *pkey = NULL;
 
@@ -176,8 +187,8 @@
 }
 
 
-int test_load_pubkey(const char *file){
-    EVP_PKEY *key = load_pubkey(file);
+int test_load_pubkey_from_file(const char *file){
+    EVP_PKEY *key = load_pubkey_from_file(file);
     if (key == NULL){
         return -1;
     }
@@ -185,8 +196,8 @@
     return 0;
 }
 
-int test_load_private_key(const char *file){
-    EVP_PKEY *key = load_key(file);
+int test_load_private_key_from_file(const char *file){
+    EVP_PKEY *key = load_privkey_from_file(file);
     if (key == NULL){
         return -1;
     }
@@ -194,17 +205,14 @@
     return 0;
 }
 
-int encrypt_rsa_message(const char *plaintext, const char *public_keyfile, unsigned char **encryptedtext) {
-    EVP_PKEY *public_key = NULL;
+int encrypt_rsa_message(const char *plaintext, EVP_PKEY *public_key, unsigned char **encryptedtext) {
     RSA *rsa = NULL;
     unsigned char *rsa_buffer = NULL, pad = RSA_PKCS1_PADDING;
     int keysize, encryptedtext_len, rsa_buffer_len;
 
-    public_key = load_pubkey(public_keyfile);
     rsa = EVP_PKEY_get1_RSA(public_key);
-    EVP_PKEY_free(public_key);
-
     keysize = RSA_size(rsa);
+
     rsa_buffer  = OPENSSL_malloc(keysize * 2);
     *encryptedtext = (unsigned char*)OPENSSL_malloc(keysize);
 
@@ -219,15 +227,12 @@
     return encryptedtext_len;  
 }
 
-int decrypt_rsa_message(const unsigned char *encryptedtext, const int encryptedtext_len, const char *private_keyfile, unsigned char **plaintext) {
-    EVP_PKEY *private_key = NULL;
+int decrypt_rsa_message(const unsigned char *encryptedtext, const int encryptedtext_len, EVP_PKEY *private_key, unsigned char **plaintext) {
     RSA *rsa = NULL;
     unsigned char *rsa_buffer = NULL, pad = RSA_PKCS1_PADDING;
     int plaintext_len, rsa_buffer_len, keysize;
     
-    private_key = load_key(private_keyfile);
     rsa = EVP_PKEY_get1_RSA(private_key);
-    EVP_PKEY_free(private_key);
 
     keysize = RSA_size(rsa);
     rsa_buffer  = OPENSSL_malloc(keysize * 2);
@@ -244,26 +249,26 @@
     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, EVP_PKEY *public_key, char **authtoken){
     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_key, &encrypted);
     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, EVP_PKEY *private_key, char **username, char **password, time_t *ts){
     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_key, &plaintext);
     plaintext[plaintext_len] = '\0';
 
     char s_username[20], s_password[20];
diff --git a/src/iperf_auth.h b/src/iperf_auth.h
index 38971d8..0c6fb0f 100644
--- a/src/iperf_auth.h
+++ b/src/iperf_auth.h
@@ -27,10 +27,14 @@
 
 #include <time.h>
 #include <sys/types.h>
+#include <openssl/bio.h>
 
-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 test_load_pubkey_from_file(const char *public_keyfile);
+int test_load_private_key_from_file(const char *private_keyfile);
+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);
+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 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);