#include "iperf_config.h"
#include <string.h>
#include <time.h>
#include <sys/types.h>
#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++)
{
snprintf(outputBuffer + (i * 2), 3, "%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];
snprintf(salted, sizeof(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)){
for (i = 0; buf[i] != '\0'; i++){
if (buf[i] == '\n' || buf[i] == '\r'){
buf[i] = '\0';
break;
}
}
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) { 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); 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); }
size_t calcDecodeLength(const char* b64input) { size_t len = strlen(b64input), padding = 0;
if (len >= 2 && b64input[len-1] == '=' && b64input[len-2] == '=') padding = 2;
else if (len >= 1 && b64input[len-1] == '=') padding = 1;
size_t decoded_len = (len*3)/4;
if (padding > decoded_len) {
return 0;
}
return decoded_len - padding;
}
int Base64Decode(const char* b64message, unsigned char** buffer, size_t* length) { BIO *bio, *b64;
size_t decodeLen = calcDecodeLength(b64message);
*buffer = (unsigned char*)malloc(decodeLen + 1);
if (!*buffer)
return -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); *length = BIO_read(bio, *buffer, strlen(b64message));
BIO_free_all(bio);
return (0); }
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, plaintext_len = 0;
int rsa_buffer_len, output_buffer_len;
#if OPENSSL_VERSION_MAJOR >= 3
int rc;
ctx = EVP_PKEY_CTX_new_from_pkey(NULL, public_key, "");
rc = EVP_PKEY_get_int_param(public_key, OSSL_PKEY_PARAM_MAX_SIZE, &output_buffer_len);
if (!rc) {
goto errreturn;
}
#else
rsa = EVP_PKEY_get1_RSA(public_key);
output_buffer_len = RSA_size(rsa);
#endif
plaintext_len = strlen(plaintext);
if (plaintext_len > output_buffer_len) {
fprintf(stderr, "Plaintext of size %zd truncated to %d; data is lost.\n", plaintext_len, output_buffer_len);
}
rsa_buffer = OPENSSL_malloc(output_buffer_len);
*encryptedtext = (unsigned char*)OPENSSL_malloc(output_buffer_len);
encryptedtext_len = output_buffer_len;
BIO *bioBuff = BIO_new_mem_buf((void*)plaintext, (int)plaintext_len);
rsa_buffer_len = BIO_read(bioBuff, rsa_buffer, plaintext_len);
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
int encrypt_ret = RSA_public_encrypt(rsa_buffer_len, rsa_buffer, *encryptedtext, rsa, padding);
encryptedtext_len = encrypt_ret < 0 ? 0 : (size_t) encrypt_ret;
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, output_buffer_len;
#if OPENSSL_VERSION_MAJOR >= 3
int rc;
ctx = EVP_PKEY_CTX_new_from_pkey(NULL, private_key, "");
rc = EVP_PKEY_get_int_param(private_key, OSSL_PKEY_PARAM_MAX_SIZE, &output_buffer_len);
if (!rc) {
goto errreturn;
}
#else
rsa = EVP_PKEY_get1_RSA(private_key);
output_buffer_len = RSA_size(rsa);
#endif
if (encryptedtext_len > output_buffer_len) {
fprintf(stderr, "Encrypted text of size %d truncated to %d; likely invalid input.\n", encryptedtext_len, output_buffer_len);
}
rsa_buffer = OPENSSL_malloc(output_buffer_len);
*plaintext = (unsigned char*)OPENSSL_malloc(output_buffer_len + 1);
BIO *bioBuff = BIO_new_mem_buf((void*)encryptedtext, encryptedtext_len);
rsa_buffer_len = BIO_read(bioBuff, rsa_buffer, encryptedtext_len);
int padding = RSA_PKCS1_OAEP_PADDING;
if (use_pkcs1_padding){
padding = RSA_PKCS1_PADDING;
}
#if OPENSSL_VERSION_MAJOR >= 3
int ret = 0;
plaintext_len = output_buffer_len;
EVP_PKEY_decrypt_init(ctx);
ret = EVP_PKEY_CTX_set_rsa_padding(ctx, padding);
if (ret < 0){
goto errreturn;
}
ret = EVP_PKEY_decrypt(ctx, *plaintext, &plaintext_len, rsa_buffer, rsa_buffer_len);
EVP_PKEY_CTX_free(ctx);
#else
int decrypt_ret = RSA_private_decrypt(rsa_buffer_len, rsa_buffer, *plaintext, rsa, padding);
plaintext_len = decrypt_ret < 0 ? 0 : (size_t) decrypt_ret;
RSA_free(rsa);
#endif
OPENSSL_free(rsa_buffer);
BIO_free(bioBuff);
if (plaintext_len <= 0) {
plaintext_len = 0;
}
return plaintext_len;
#if OPENSSL_VERSION_MAJOR >= 3
errreturn:
fprintf(stderr, "%s\n", ERR_error_string(ERR_get_error(), NULL));
return 0;
#endif
}
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));
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); }
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 =0;
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) {
OPENSSL_free(plaintext);
return -1;
}
s_password = (char *) calloc(plaintext_len, sizeof(char));
if (s_password == NULL) {
OPENSSL_free(plaintext);
free(s_username);
return -1;
}
int rc = sscanf((char *) plaintext, auth_text_format, s_username, s_password, &utc_seconds);
if (rc != 3) {
OPENSSL_free(plaintext);
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
ssize_t iperf_getpass (char **lineptr, size_t *n, FILE *stream) {
struct termios old, new;
ssize_t nread;
if (tcgetattr (fileno (stream), &old) != 0)
return -1;
new = old;
new.c_lflag &= ~ECHO;
if (tcsetattr (fileno (stream), TCSAFLUSH, &new) != 0)
return -1;
printf("Password: ");
nread = getline (lineptr, n, stream);
(void) tcsetattr (fileno (stream), TCSAFLUSH, &old);
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;
}