Port to libsodium, switch to AES256-GCM, revamp authentication

This commit is contained in:
Frank Denis 2015-10-08 19:17:30 +02:00
parent 9aac84a0ce
commit ed49289d7b
10 changed files with 211 additions and 852 deletions

91
auth.c
View File

@ -27,6 +27,7 @@
#include "config.h" #include "config.h"
#include <assert.h>
#include <stdio.h> #include <stdio.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
@ -56,67 +57,45 @@
#include "auth.h" #include "auth.h"
/* Encryption and Decryption of the challenge key */ /* Encryption and Decryption of the challenge key */
#ifdef HAVE_SSL #include <sodium.h>
#include <openssl/md5.h> static int derive_key(struct vtun_host *host)
#include <openssl/blowfish.h>
#include <openssl/rand.h>
static void gen_chal(char *buf)
{ {
RAND_bytes(buf, VTUN_CHAL_SIZE); unsigned char salt[crypto_pwhash_scryptsalsa208sha256_SALTBYTES];
int ret = -1;
if (host->key != NULL) {
return 0;
}
if ((host->key = sodium_malloc(HOST_KEYBYTES)) == NULL) {
return -1;
}
memset(salt, 0xd1, sizeof salt);
if (crypto_pwhash_scryptsalsa208sha256
(host->key, HOST_KEYBYTES, host->passwd, strlen(host->passwd), salt,
crypto_pwhash_scryptsalsa208sha256_OPSLIMIT_INTERACTIVE,
crypto_pwhash_scryptsalsa208sha256_MEMLIMIT_INTERACTIVE) == 0) {
ret = 0;
}
sodium_memzero(host->passwd, strlen(host->passwd));
free(host->passwd);
host->passwd = NULL;
vtun_syslog(LOG_DEBUG,"Key ready for host %s.", host->host);
return ret;
} }
static void encrypt_chal(char *chal, char *pwd) static void gen_chal(char *chal)
{ {
register int i; randombytes_buf((unsigned char *) chal, VTUN_CHAL_SIZE);
BF_KEY key;
BF_set_key(&key, 16, MD5(pwd,strlen(pwd),NULL));
for(i=0; i < VTUN_CHAL_SIZE; i += 8 )
BF_ecb_encrypt(chal + i, chal + i, &key, BF_ENCRYPT);
} }
static void decrypt_chal(char *chal, char *pwd) static void auth_chal(char *chal, const struct vtun_host *host)
{ {
register int i; crypto_generichash(chal, VTUN_CHAL_SIZE, chal, VTUN_CHAL_SIZE,
BF_KEY key; host->key, HOST_KEYBYTES);
BF_set_key(&key, 16, MD5(pwd,strlen(pwd),NULL));
for(i=0; i < VTUN_CHAL_SIZE; i += 8 )
BF_ecb_encrypt(chal + i, chal + i, &key, BF_DECRYPT);
} }
#else /* HAVE_SSL */
static void encrypt_chal(char *chal, char *pwd)
{
char * xor_msk = pwd;
register int i, xor_len = strlen(xor_msk);
for(i=0; i < VTUN_CHAL_SIZE; i++)
chal[i] ^= xor_msk[i%xor_len];
}
static void inline decrypt_chal(char *chal, char *pwd)
{
encrypt_chal(chal, pwd);
}
/* Generate PSEUDO random challenge key. */
static void gen_chal(char *buf)
{
register int i;
srand(time(NULL));
for(i=0; i < VTUN_CHAL_SIZE; i++)
buf[i] = (unsigned int)(255.0 * rand()/RAND_MAX);
}
#endif /* HAVE_SSL */
/* /*
* Functions to convert binary flags to character string. * Functions to convert binary flags to character string.
* string format: <CS64> * string format: <CS64>
@ -348,10 +327,10 @@ struct vtun_host * auth_server(int fd)
if( !(h = find_host(host)) ) if( !(h = find_host(host)) )
break; break;
derive_key(h);
auth_chal(chal_req, h);
decrypt_chal(chal_res, h->passwd); if( !sodium_memcmp(chal_req, chal_res, VTUN_CHAL_SIZE) ){
if( !memcmp(chal_req, chal_res, VTUN_CHAL_SIZE) ){
/* Auth successeful. */ /* Auth successeful. */
/* Lock host */ /* Lock host */
@ -400,8 +379,8 @@ int auth_client(int fd, struct vtun_host *host)
case ST_HOST: case ST_HOST:
if( !strncmp(buf,"OK",2) && cs2cl(buf,chal)){ if( !strncmp(buf,"OK",2) && cs2cl(buf,chal)){
stage = ST_CHAL; stage = ST_CHAL;
derive_key(host);
encrypt_chal(chal,host->passwd); auth_chal(chal, host);
print_p(fd,"CHAL: %s\n", cl2cs(chal)); print_p(fd,"CHAL: %s\n", cl2cs(chal));
continue; continue;

View File

@ -107,6 +107,7 @@ statement: '\n'
memcpy(parse_host, &default_host, sizeof(struct vtun_host)); memcpy(parse_host, &default_host, sizeof(struct vtun_host));
parse_host->host = strdup($1); parse_host->host = strdup($1);
parse_host->passwd = NULL; parse_host->passwd = NULL;
parse_host->key = NULL;
parse_host->sopt.host = strdup($1); parse_host->sopt.host = strdup($1);
/* Copy local address */ /* Copy local address */

View File

@ -86,17 +86,6 @@ struct kword cfg_param[] = {
{ "inetd", VTUN_INETD }, { "inetd", VTUN_INETD },
{ "stand", VTUN_STAND_ALONE }, { "stand", VTUN_STAND_ALONE },
{ "keep", VTUN_PERSIST_KEEPIF }, { "keep", VTUN_PERSIST_KEEPIF },
{ "blowfish128cbc", VTUN_ENC_BF128CBC }, { "aes256gcm",VTUN_ENC_AES256GCM },
{ "blowfish128cfb", VTUN_ENC_BF128CFB },
{ "blowfish128ofb", VTUN_ENC_BF128OFB },
{ "blowfish256cbc", VTUN_ENC_BF256CBC },
{ "blowfish256cfb", VTUN_ENC_BF256CFB },
{ "blowfish256ofb", VTUN_ENC_BF256OFB },
{ "aes128cbc", VTUN_ENC_AES128CBC },
{ "aes128cfb", VTUN_ENC_AES128CFB },
{ "aes128ofb", VTUN_ENC_AES128OFB },
{ "aes256cbc", VTUN_ENC_AES256CBC },
{ "aes256cfb", VTUN_ENC_AES256CFB },
{ "aes256ofb", VTUN_ENC_AES256OFB },
{ NULL , 0 } { NULL , 0 }
}; };

View File

@ -17,10 +17,10 @@ AC_ARG_ENABLE(shaper,
SHAPER=yes SHAPER=yes
) )
dnl Encryption support dnl Encryption support
AC_ARG_ENABLE(ssl, AC_ARG_ENABLE(sodium,
--disable-ssl Don not compile encryption module, --disable-sodium Don not compile encryption module,
SSL=$enableval, SODIUM=$enableval,
SSL=yes SODIUM=yes
) )
dnl ZLIB support dnl ZLIB support
AC_ARG_ENABLE(zlib, AC_ARG_ENABLE(zlib,
@ -48,13 +48,13 @@ AC_ARG_ENABLE(nathack,
NATHACK=yes NATHACK=yes
) )
AC_ARG_WITH(ssl-headers, AC_ARG_WITH(sodium-headers,
--with-ssl-headers=DIR Crypto Include files location, --with-sodium-headers=DIR Crypto Include files location,
SSL_HDR_DIR="$withval" SODIUM_HDR_DIR="$withval"
CPPFLAGS="$CPPFLAGS -I$withval" CPPFLAGS="$CPPFLAGS -I$withval"
) )
AC_ARG_WITH(ssl-lib, AC_ARG_WITH(sodium-lib,
--with-ssl-lib=DIR Crypto Library location, --with-sodium-lib=DIR Crypto Library location,
LIBS="$LIBS -L$withval" LIBS="$LIBS -L$withval"
) )
@ -165,60 +165,16 @@ if test "$LZO" = "yes"; then
fi fi
fi fi
if test "$SSL" = "yes"; then if test "$SODIUM" = "yes"; then
AC_MSG_RESULT() AC_MSG_RESULT()
AC_CHECKING( for md5 Library and Header files ... ) AC_CHECKING( for Sodium Library and Header files ... )
AC_SEARCH_HEADERS(md5.h, AC_SEARCH_LIBS(crypto_aead_aes256gcm_aesni_encrypt, [sodium],
$SSL_HDR_DIR /usr/include/openssl "" /usr/include /usr/include/ssl /usr/local/include /usr/local/ssl/include /usr/include/sys, [AC_DEFINE(HAVE_SODIUM, [1], [Define to 1 if you have Sodium])],
AC_ERROR([libsodium with crypto_aead_aes256gcm_aesni_encrypt not found]))
AC_SEARCH_HEADERS(sodium.h,
$SSL_HDR_DIR /usr/include /usr/local/include /opt/include,
, ,
AC_MSG_ERROR( SSL headers not found. ) AC_MSG_ERROR( Sodium headers not found. )
)
fi
if test "$SSL" = "yes"; then
AC_MSG_RESULT()
AC_CHECKING( for blowfish Library and Header files ... )
AC_SEARCH_HEADERS(blowfish.h,
$BLOWFISH_HDR_DIR /usr/include/ssl /usr/include/openssl /usr/include /usr/local/include /usr/local/ssl/include /usr/include/crypto,
AC_CHECK_LIB(crypto, BF_set_key,
[
LIBS="$LIBS -lcrypto"
AC_DEFINE(HAVE_SSL, [1], [Define to 1 if you have openssl])
AC_DEFINE(HAVE_SSL_BLOWFISH, [1], [Define to 1 if you have blowfish in openssl])
],
AC_MSG_ERROR( SSL library not found. )
),
AC_MSG_ERROR( SSL headers not found. )
)
fi
if test "$SSL" = "yes"; then
AC_MSG_RESULT()
AC_CHECKING( for AES Library and Header files ... )
AC_SEARCH_HEADERS(aes.h,
$SSL_HDR_DIR /usr/include/ssl /usr/include/openssl /usr/include /usr/local/include /usr/local/ssl/include /usr/include/crypto,
AC_CHECK_LIB(crypto, AES_set_encrypt_key,
[
AC_DEFINE(HAVE_SSL_AES, [1], [Define to 1 if you have AES in openssl])
],
AC_MSG_ERROR( AES library not found. )
),
AC_MSG_ERROR( AES headers not found. )
)
fi
if test "$SSL" = "yes"; then
AC_MSG_RESULT()
AC_CHECKING( for EVP Library and Header files ... )
AC_SEARCH_HEADERS(evp.h,
$SSL_HDR_DIR /usr/include/ssl /usr/include/openssl /usr/include /usr/local/include /usr/local/ssl/include /usr/include/crypto,
AC_CHECK_LIB(crypto, EVP_EncryptInit,
[
AC_DEFINE(HAVE_SSL_EVP, [1], [Define to 1 if you have EVP in openssl])
],
AC_MSG_ERROR( EVP library not found. )
),
AC_MSG_ERROR( EVP headers not found. )
) )
fi fi

View File

@ -1,707 +1,172 @@
/*
VTun - Virtual Tunnel over TCP/IP network.
Copyright (C) 1998-2008 Maxim Krasnyansky <max_mk@yahoo.com>
VTun has been derived from VPPP package by Maxim Krasnyansky.
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
*/
/*
Encryption module uses software developed by the OpenSSL Project
for use in the OpenSSL Toolkit. (http://www.openssl.org/)
Copyright (c) 1998-2000 The OpenSSL Project. All rights reserved.
*/
/*
* This lfd_encrypt module uses MD5 to create 128 bits encryption
* keys and BlowFish for actual data encryption.
* It is based on code written by Chris Todd<christ@insynq.com> with
* several improvements and modifications by me.
*/
/*
* The current lfd_encrypt module is based on code attributed above and
* uses new code written by Dale Fountain <dpf-vtun@fountainbay.com> to
* allow multiple ciphers, modes, and key sizes. Feb 2004.
*/
#include "config.h" #include "config.h"
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h> #include <stdio.h>
#include <syslog.h> #include <stdlib.h>
#include <strings.h>
#include <string.h> #include <string.h>
#include <syslog.h>
#include <time.h> #include <time.h>
#include <unistd.h>
#include "vtun.h" #include "vtun.h"
#include "linkfd.h" #include "linkfd.h"
#include "lib.h"
#ifdef HAVE_SSL #ifdef HAVE_SODIUM
#include <sodium.h>
/* OpenSSL includes */ #define crypto_aead_NPUBBYTES crypto_aead_aes256gcm_NPUBBYTES
#include <openssl/evp.h> #define crypto_aead_ABYTES crypto_aead_aes256gcm_ABYTES
#include <openssl/md5.h> #define crypto_aead_KEYBYTES crypto_aead_aes256gcm_KEYBYTES
#include <openssl/blowfish.h>
#include <openssl/rand.h>
/* #define MESSAGE_MAX_SIZE VTUN_FRAME_SIZE
* #define LFD_ENCRYPT_DEBUG #define CIPHERTEXT_ABYTES (crypto_aead_ABYTES + crypto_aead_NPUBBYTES)
*/ #define CIPHERTEXT_MAX_SIZE MESSAGE_MAX_SIZE
#define CIPHERTEXT_MAX_TOTAL_SIZE (CIPHERTEXT_MAX_SIZE + CIPHERTEXT_ABYTES)
#define ENC_BUF_SIZE VTUN_FRAME_SIZE + 128 #define MINIMUM_DATE 1444341043UL
#define ENC_KEY_SIZE 16 #define SLEEP_WHEN_CLOCK_IS_OFF 10
static BF_KEY key; typedef struct CryptoCtx {
static char * enc_buf; crypto_aead_aes256gcm_aesni_state *state;
static char * dec_buf; unsigned char *ciphertext;
unsigned char *key;
unsigned char *message;
unsigned char *nonce;
unsigned char *previous_decrypted_nonce;
} CryptoCtx;
#define CIPHER_INIT 0 static CryptoCtx ctx;
#define CIPHER_CODE 1
#define CIPHER_SEQUENCE 2
#define CIPHER_REQ_INIT 3
static struct vtun_host *phost; static int
derive_key(unsigned char *key, size_t key_size, struct vtun_host *host)
extern int send_a_packet;
/* out of sync packet threshold before forcing a re-init */
#define MAX_GIBBERISH 10
#define MIN_GIBBERISH 1
#define MAX_GIBBERISH_TIME 2
static int gibberish;
static time_t gib_time_start;
static int cipher_enc_state;
static int cipher_dec_state;
static int cipher;
static int blocksize;
static int keysize;
static int enc_init_first_time;
static int dec_init_first_time;
static unsigned long sequence_num;
static char * pkey;
static char * iv_buf;
static EVP_CIPHER_CTX ctx_enc; /* encrypt */
static EVP_CIPHER_CTX ctx_dec; /* decrypt */
static EVP_CIPHER_CTX ctx_enc_ecb; /* sideband ecb encrypt */
static EVP_CIPHER_CTX ctx_dec_ecb; /* sideband ecb decrypt */
static int send_msg(int len, char *in, char **out);
static int recv_msg(int len, char *in, char **out);
static int send_ib_mesg(int *len, char **in);
static int recv_ib_mesg(int *len, char **in);
static int prep_key(char **key, int size, struct vtun_host *host)
{ {
int tmplen, halflen; crypto_generichash(key, key_size, host->key, HOST_KEYBYTES, NULL, 0U);
char *hashkey; sodium_free(host->key);
host->key = NULL;
}
if ( !(hashkey = malloc(size)) ) static int
{ init_nonce(unsigned char *nonce, size_t nonce_size)
vtun_syslog(LOG_ERR,"Can't allocate buffer for key hash"); {
time_t now;
if (nonce_size < 5) {
return -1; return -1;
} }
memset(hashkey,0,size); time(&now);
if (now < MINIMUM_DATE) {
sleep(SLEEP_WHEN_CLOCK_IS_OFF);
randombytes_buf(nonce, nonce_size);
} else {
randombytes_buf(nonce + 4, nonce_size - 4);
now <<= 2;
memcpy(nonce, &now, 3);
nonce[3] = (nonce[3] & 0x3) ^ *(((unsigned char *) &now) + 3);
}
return 0;
}
if (size == 32) static int
{ alloc_encrypt(struct vtun_host *host)
tmplen = strlen(host->passwd); {
if (tmplen != 0) halflen = tmplen>>1; if (sodium_init() < 0) {
else halflen = 0;
MD5(host->passwd, halflen, hashkey);
MD5((host->passwd)+halflen, tmplen-halflen, hashkey+16);
}
else if (size == 16)
{
MD5(host->passwd,strlen(host->passwd), hashkey);
}
else
{
/* don't know what to do */
free(hashkey);
*key = NULL;
return -1; return -1;
} }
*key = hashkey; ctx.state = sodium_malloc(sizeof *ctx.state);
return 0; ctx.key = sodium_malloc(crypto_aead_KEYBYTES);
} ctx.message = sodium_malloc(MESSAGE_MAX_SIZE);
ctx.ciphertext = sodium_malloc(CIPHERTEXT_MAX_TOTAL_SIZE);
static void free_key (char *key) ctx.nonce = sodium_malloc(crypto_aead_NPUBBYTES);
{ ctx.previous_decrypted_nonce = sodium_malloc(crypto_aead_NPUBBYTES);
free(key); if (ctx.state == NULL || ctx.key == NULL || ctx.message == NULL ||
} ctx.ciphertext == NULL || ctx.ciphertext == NULL || ctx.nonce == NULL ||
ctx.previous_decrypted_nonce == NULL) {
static int alloc_encrypt(struct vtun_host *host) abort();
{ }
int sb_init = 0; if (init_nonce(ctx.nonce, crypto_aead_NPUBBYTES) != 0) {
int var_key = 0;
const EVP_CIPHER *cipher_type;
char tmpstr[64];
char cipher_name[32];
EVP_CIPHER_CTX *pctx_enc;
EVP_CIPHER_CTX *pctx_dec;
enc_init_first_time = 1;
dec_init_first_time = 1;
if( !(enc_buf = lfd_alloc(ENC_BUF_SIZE)) ){
vtun_syslog(LOG_ERR,"Can't allocate buffer for encryptor");
return -1; return -1;
} }
if( !(dec_buf = lfd_alloc(ENC_BUF_SIZE)) ){ if (derive_key(ctx.key, crypto_aead_KEYBYTES, host) != 0) {
vtun_syslog(LOG_ERR,"Can't allocate buffer for decryptor");
return -1; return -1;
} }
crypto_aead_aes256gcm_aesni_beforenm(ctx.state, ctx.key);
RAND_bytes((char *)&sequence_num, 4); sodium_free(ctx.key);
gibberish = 0; ctx.key = NULL;
gib_time_start = 0;
phost = host;
cipher = host->cipher;
switch(cipher)
{
case VTUN_ENC_AES128OFB:
case VTUN_ENC_AES128CFB:
case VTUN_ENC_AES128CBC:
blocksize = 16;
keysize = 16;
sb_init=1;
cipher_type = EVP_aes_128_ecb();
pctx_enc = &ctx_enc_ecb;
pctx_dec = &ctx_dec_ecb;
break;
case VTUN_ENC_BF256OFB:
case VTUN_ENC_BF256CFB:
case VTUN_ENC_BF256CBC:
blocksize = 8;
keysize = 32;
var_key = 1;
sb_init = 1;
cipher_type = EVP_bf_ecb();
pctx_enc = &ctx_enc_ecb;
pctx_dec = &ctx_dec_ecb;
break;
case VTUN_ENC_BF128OFB:
case VTUN_ENC_BF128CFB:
case VTUN_ENC_BF128CBC:
blocksize = 8;
keysize = 16;
var_key = 1;
sb_init = 1;
cipher_type = EVP_bf_ecb();
pctx_enc = &ctx_enc_ecb;
pctx_dec = &ctx_dec_ecb;
break;
case VTUN_ENC_AES256OFB:
case VTUN_ENC_AES256CFB:
case VTUN_ENC_AES256CBC:
default:
blocksize = 16;
keysize = 32;
sb_init = 1;
cipher_type = EVP_aes_256_ecb();
pctx_enc = &ctx_enc_ecb;
pctx_dec = &ctx_dec_ecb;
strcpy(cipher_name,"AES-256-CBC");
} /* switch(host->cipher) */
if (prep_key(&pkey, keysize, host) != 0) return -1;
EVP_CIPHER_CTX_init(pctx_enc);
EVP_CIPHER_CTX_init(pctx_dec);
EVP_EncryptInit_ex(pctx_enc, cipher_type, NULL, NULL, NULL);
EVP_DecryptInit_ex(pctx_dec, cipher_type, NULL, NULL, NULL);
if (var_key)
{
EVP_CIPHER_CTX_set_key_length(pctx_enc, keysize);
EVP_CIPHER_CTX_set_key_length(pctx_dec, keysize);
}
EVP_EncryptInit_ex(pctx_enc, NULL, NULL, pkey, NULL);
EVP_DecryptInit_ex(pctx_dec, NULL, NULL, pkey, NULL);
EVP_CIPHER_CTX_set_padding(pctx_enc, 0);
EVP_CIPHER_CTX_set_padding(pctx_dec, 0);
if (sb_init)
{
cipher_enc_state=CIPHER_INIT;
cipher_dec_state=CIPHER_INIT;
}
else
{
cipher_enc_state=CIPHER_CODE;
cipher_dec_state=CIPHER_CODE;
sprintf(tmpstr,"%s encryption initialized", cipher_name);
vtun_syslog(LOG_INFO, tmpstr);
}
return 0; return 0;
} }
static int free_encrypt() static int
free_encrypt(void)
{ {
free_key(pkey); pkey = NULL; sodium_free(ctx.key);
sodium_free(ctx.message);
lfd_free(enc_buf); enc_buf = NULL; sodium_free(ctx.ciphertext);
lfd_free(dec_buf); dec_buf = NULL; sodium_free(ctx.nonce);
sodium_free(ctx.previous_decrypted_nonce);
EVP_CIPHER_CTX_cleanup(&ctx_enc);
EVP_CIPHER_CTX_cleanup(&ctx_dec);
EVP_CIPHER_CTX_cleanup(&ctx_enc_ecb);
EVP_CIPHER_CTX_cleanup(&ctx_dec_ecb);
return 0; return 0;
} }
static int encrypt_buf(int len, char *in, char **out) static int
is_lower_or_equal(const unsigned char *a, const unsigned char *b, size_t size)
{ {
register int pad, p, msg_len; size_t i;
int outlen;
char *in_ptr, *out_ptr = enc_buf;
msg_len = send_msg(len, in, out); for (i = 0U; i < size; i++) {
in = *out; if (a[i] > b[i]) {
in_ptr = in+msg_len;
memcpy(out_ptr,in,msg_len);
out_ptr += msg_len;
send_ib_mesg(&len, &in_ptr);
if (!len) return 0;
/* ( len % blocksize ) */
p = (len & (blocksize-1)); pad = blocksize - p;
memset(in_ptr+len, pad, pad);
outlen=len+pad;
if (pad == blocksize)
RAND_bytes(in_ptr+len, blocksize-1);
EVP_EncryptUpdate(&ctx_enc, out_ptr, &outlen, in_ptr, len+pad);
*out = enc_buf;
sequence_num++;
return outlen+msg_len;
}
static int decrypt_buf(int len, char *in, char **out)
{
register int pad;
char *tmp_ptr, *in_ptr, *out_ptr = dec_buf;
int outlen;
len = recv_msg(len, in, out);
in = *out;
in_ptr = in;
outlen=len;
if (!len) return 0;
EVP_DecryptUpdate(&ctx_dec, out_ptr, &outlen, in_ptr, len);
recv_ib_mesg(&outlen, &out_ptr);
if (!outlen) return 0;
tmp_ptr = out_ptr + outlen; tmp_ptr--;
pad = *tmp_ptr;
if (pad < 1 || pad > blocksize) {
vtun_syslog(LOG_INFO, "decrypt_buf: bad pad length");
return 0; return 0;
} }
*out = out_ptr; }
return outlen - pad; return 1;
} }
static int cipher_enc_init(char * iv) static int
encrypt_buf(int message_len_, char *message_, char ** const ciphertext_p)
{ {
int var_key = 0; const unsigned char *message = (const unsigned char *) message_;
const EVP_CIPHER *cipher_type; const size_t message_len = (size_t) message_len_;
char tmpstr[64]; unsigned long long ciphertext_len;
char cipher_name[32];
switch(cipher) if (message_len_ < 0 || message_len > MESSAGE_MAX_SIZE) {
{
case VTUN_ENC_AES256OFB:
cipher_type = EVP_aes_256_ofb();
strcpy(cipher_name, "AES-256-OFB");
break;
case VTUN_ENC_AES256CFB:
cipher_type = EVP_aes_256_cfb();
strcpy(cipher_name, "AES-256-CFB");
break;
case VTUN_ENC_AES256CBC:
cipher_type = EVP_aes_256_cbc();
strcpy(cipher_name, "AES-256-CBC");
break;
case VTUN_ENC_AES128OFB:
cipher_type = EVP_aes_128_ofb();
strcpy(cipher_name, "AES-128-OFB");
break;
case VTUN_ENC_AES128CFB:
cipher_type = EVP_aes_128_cfb();
strcpy(cipher_name, "AES-128-CFB");
break;
case VTUN_ENC_AES128CBC:
cipher_type = EVP_aes_128_cbc();
strcpy(cipher_name, "AES-128-CBC");
break;
case VTUN_ENC_BF256OFB:
var_key = 1;
cipher_type = EVP_bf_ofb();
strcpy(cipher_name, "Blowfish-256-OFB");
break;
case VTUN_ENC_BF256CFB:
var_key = 1;
cipher_type = EVP_bf_cfb();
strcpy(cipher_name, "Blowfish-256-CFB");
break;
case VTUN_ENC_BF256CBC:
var_key = 1;
cipher_type = EVP_bf_cbc();
strcpy(cipher_name, "Blowfish-256-CBC");
break;
case VTUN_ENC_BF128OFB:
var_key = 1;
cipher_type = EVP_bf_ofb();
strcpy(cipher_name, "Blowfish-128-OFB");
break;
case VTUN_ENC_BF128CFB:
var_key = 1;
cipher_type = EVP_bf_cfb();
strcpy(cipher_name, "Blowfish-128-CFB");
break;
case VTUN_ENC_BF128CBC:
var_key = 1;
cipher_type = EVP_bf_cbc();
strcpy(cipher_name, "Blowfish-128-CBC");
break;
default:
/* if we're here, something weird's going on */
return -1; return -1;
break;
} /* switch(cipher) */
EVP_CIPHER_CTX_init(&ctx_enc);
EVP_EncryptInit_ex(&ctx_enc, cipher_type, NULL, NULL, NULL);
if (var_key)
EVP_CIPHER_CTX_set_key_length(&ctx_enc, keysize);
EVP_EncryptInit_ex(&ctx_enc, NULL, NULL, pkey, NULL);
EVP_EncryptInit_ex(&ctx_enc, NULL, NULL, NULL, iv);
EVP_CIPHER_CTX_set_padding(&ctx_enc, 0);
if (enc_init_first_time)
{
sprintf(tmpstr,"%s encryption initialized", cipher_name);
vtun_syslog(LOG_INFO, tmpstr);
enc_init_first_time = 0;
} }
return 0; crypto_aead_aes256gcm_aesni_encrypt_afternm(ctx.ciphertext, &ciphertext_len,
message, message_len,
NULL, 0ULL,
NULL, ctx.nonce, ctx.state);
memcpy(ctx.ciphertext + message_len + crypto_aead_ABYTES,
ctx.nonce, crypto_aead_NPUBBYTES);
sodium_increment(ctx.nonce, crypto_aead_NPUBBYTES);
*ciphertext_p = ctx.ciphertext;
return (int) ciphertext_len + crypto_aead_NPUBBYTES;
} }
static int cipher_dec_init(char * iv) static int
decrypt_buf(int ciphertext_len_, char *ciphertext_, char ** const message_p)
{ {
int var_key = 0; const unsigned char *ciphertext = (const unsigned char *) ciphertext_;
const EVP_CIPHER *cipher_type; const unsigned char *nonce;
char tmpstr[64]; size_t ciphertext_len = (size_t) ciphertext_len_;
char cipher_name[32]; unsigned long long message_len;
switch(cipher) if (ciphertext_len_ < CIPHERTEXT_ABYTES ||
{ ciphertext_len > CIPHERTEXT_MAX_SIZE) {
case VTUN_ENC_AES256OFB:
cipher_type = EVP_aes_256_ofb();
strcpy(cipher_name, "AES-256-OFB");
break;
case VTUN_ENC_AES256CFB:
cipher_type = EVP_aes_256_cfb();
strcpy(cipher_name, "AES-256-CFB");
break;
case VTUN_ENC_AES256CBC:
cipher_type = EVP_aes_256_cbc();
strcpy(cipher_name, "AES-256-CBC");
break;
case VTUN_ENC_AES128OFB:
cipher_type = EVP_aes_128_ofb();
strcpy(cipher_name, "AES-128-OFB");
break;
case VTUN_ENC_AES128CFB:
cipher_type = EVP_aes_128_cfb();
strcpy(cipher_name, "AES-128-CFB");
break;
case VTUN_ENC_AES128CBC:
cipher_type = EVP_aes_128_cbc();
strcpy(cipher_name, "AES-128-CBC");
break;
case VTUN_ENC_BF256OFB:
var_key = 1;
cipher_type = EVP_bf_ofb();
strcpy(cipher_name, "Blowfish-256-OFB");
break;
case VTUN_ENC_BF256CFB:
var_key = 1;
cipher_type = EVP_bf_cfb();
strcpy(cipher_name, "Blowfish-256-CFB");
break;
case VTUN_ENC_BF256CBC:
var_key = 1;
cipher_type = EVP_bf_cbc();
strcpy(cipher_name, "Blowfish-256-CBC");
break;
case VTUN_ENC_BF128OFB:
var_key = 1;
cipher_type = EVP_bf_ofb();
strcpy(cipher_name, "Blowfish-128-OFB");
break;
case VTUN_ENC_BF128CFB:
var_key = 1;
cipher_type = EVP_bf_cfb();
strcpy(cipher_name, "Blowfish-128-CFB");
break;
case VTUN_ENC_BF128CBC:
var_key = 1;
cipher_type = EVP_bf_cbc();
strcpy(cipher_name, "Blowfish-128-CBC");
break;
default:
/* if we're here, something weird's going on */
return -1; return -1;
break;
} /* switch(cipher) */
EVP_CIPHER_CTX_init(&ctx_dec);
EVP_DecryptInit_ex(&ctx_dec, cipher_type, NULL, NULL, NULL);
if (var_key)
EVP_CIPHER_CTX_set_key_length(&ctx_dec, keysize);
EVP_DecryptInit_ex(&ctx_dec, NULL, NULL, pkey, NULL);
EVP_DecryptInit_ex(&ctx_dec, NULL, NULL, NULL, iv);
EVP_CIPHER_CTX_set_padding(&ctx_dec, 0);
if (dec_init_first_time)
{
sprintf(tmpstr,"%s decryption initialized", cipher_name);
vtun_syslog(LOG_INFO, tmpstr);
dec_init_first_time = 0;
} }
return 0; ciphertext_len -= crypto_aead_NPUBBYTES;
nonce = ciphertext + ciphertext_len;
if (is_lower_or_equal(nonce, ctx.previous_decrypted_nonce, crypto_aead_NPUBBYTES) ||
crypto_aead_aes256gcm_aesni_decrypt_afternm(ctx.message, &message_len, NULL,
ciphertext, ciphertext_len,
NULL, 0ULL, nonce, ctx.state) != 0) {
return -1;
}
memcpy(ctx.previous_decrypted_nonce, nonce, crypto_aead_NPUBBYTES);
*message_p = ctx.message;
return (int) message_len;
} }
static int send_msg(int len, char *in, char **out)
{
char * iv; char * in_ptr;
int outlen;
switch(cipher_enc_state)
{
case CIPHER_INIT:
in_ptr = in - blocksize*2;
iv = malloc(blocksize);
RAND_bytes(iv, blocksize);
strncpy(in_ptr,"ivec",4);
in_ptr += 4;
memcpy(in_ptr,iv,blocksize);
in_ptr += blocksize;
cipher_enc_init(iv);
memset(iv,0,blocksize); free(iv); iv = NULL;
RAND_bytes(in_ptr, in - in_ptr);
in_ptr = in - blocksize*2;
outlen = blocksize*2;
EVP_EncryptUpdate(&ctx_enc_ecb, in_ptr,
&outlen, in_ptr, blocksize*2);
*out = in_ptr;
len = outlen;
cipher_enc_state = CIPHER_SEQUENCE;
break;
case CIPHER_CODE:
default:
*out = in;
len = 0;
break;
}
return len;
}
static int recv_msg(int len, char *in, char **out)
{
char * iv; char * in_ptr;
int outlen;
switch(cipher_dec_state)
{
case CIPHER_INIT:
in_ptr = in;
iv = malloc(blocksize);
outlen = blocksize*2;
EVP_DecryptUpdate(&ctx_dec_ecb, in_ptr, &outlen, in_ptr, blocksize*2);
if ( !strncmp(in_ptr, "ivec", 4) )
{
memcpy(iv, in_ptr+4, blocksize);
cipher_dec_init(iv);
*out = in_ptr + blocksize*2;
len -= blocksize*2;
cipher_dec_state = CIPHER_SEQUENCE;
gibberish = 0;
gib_time_start = 0;
}
else
{
len = 0;
*out = in;
gibberish++;
if (gibberish == 1) gib_time_start = time(NULL);
if (gibberish == MIN_GIBBERISH)
{
cipher_enc_state = CIPHER_REQ_INIT;
send_a_packet = 1;
#ifdef LFD_ENCRYPT_DEBUG
vtun_syslog(LOG_INFO,
"Min. gibberish threshold reached");
#endif
}
if (gibberish >= MAX_GIBBERISH ||
difftime(time(NULL), gib_time_start) >= MAX_GIBBERISH_TIME)
{
gibberish = 0;
gib_time_start = 0;
send_a_packet = 1;
#ifdef LFD_ENCRYPT_DEBUG
vtun_syslog(LOG_INFO,
"Max. gibberish threshold reached");
#endif
if (cipher_enc_state != CIPHER_INIT)
{
cipher_enc_state = CIPHER_INIT;
EVP_CIPHER_CTX_cleanup(&ctx_enc);
#ifdef LFD_ENCRYPT_DEBUG
vtun_syslog(LOG_INFO,
"Forcing local encryptor re-init");
#endif
}
}
}
memset(iv,0,blocksize); free(iv); iv = NULL;
memset(in_ptr,0,blocksize*2);
break;
case CIPHER_CODE:
default:
*out = in;
break;
}
return len;
}
/* Send In-Band Message */
static int send_ib_mesg(int *len, char **in)
{
char *in_ptr = *in;
/* To simplify matters, I assume that blocksize
will not be less than 8 bytes */
if (cipher_enc_state == CIPHER_SEQUENCE)
{
in_ptr -= blocksize;
memset(in_ptr,0,blocksize);
strncpy(in_ptr,"seq#",4);
in_ptr+=4;
*((unsigned long *)in_ptr) = htonl(sequence_num);
in_ptr-=4;
*in = in_ptr;
*len += blocksize;
}
else if (cipher_enc_state == CIPHER_REQ_INIT)
{
in_ptr -= blocksize;
memset(in_ptr,0,blocksize);
strncpy(in_ptr,"rsyn",4);
in_ptr+=4;
*((unsigned long *)in_ptr) = htonl(sequence_num);
in_ptr-=4;
*in = in_ptr;
*len += blocksize;
#ifdef LFD_ENCRYPT_DEBUG
vtun_syslog(LOG_INFO, "Requesting remote encryptor re-init");
#endif
cipher_enc_state = CIPHER_SEQUENCE;
send_a_packet = 1;
}
return 0;
}
/* Receive In-Band Message */
static int recv_ib_mesg(int *len, char **in)
{
char *in_ptr = *in;
if (cipher_dec_state == CIPHER_SEQUENCE)
{
/* To simplify matters, I assume that blocksize
will not be less than 8 bytes */
if ( !strncmp(in_ptr, "seq#", 4) )
{
*in += blocksize;
*len -= blocksize;
}
else if ( !strncmp(in_ptr, "rsyn", 4) )
{
*in += blocksize;
*len -= blocksize;
if (cipher_enc_state != CIPHER_INIT)
{
cipher_enc_state = CIPHER_INIT;
EVP_CIPHER_CTX_cleanup(&ctx_enc);
}
#ifdef LFD_ENCRYPT_DEBUG
vtun_syslog(LOG_INFO, "Remote requests encryptor re-init");
#endif
}
else
{
*len = 0;
if (cipher_dec_state != CIPHER_INIT &&
cipher_enc_state != CIPHER_REQ_INIT &&
cipher_enc_state != CIPHER_INIT)
{
EVP_CIPHER_CTX_cleanup (&ctx_dec);
cipher_dec_state = CIPHER_INIT;
cipher_enc_state = CIPHER_REQ_INIT;
}
#ifdef LFD_ENCRYPT_DEBUG
vtun_syslog(LOG_INFO, "Local decryptor out of sync");
#endif
}
}
return 0;
}
/*
* Module structure.
*/
struct lfd_mod lfd_encrypt = { struct lfd_mod lfd_encrypt = {
"Encryptor", "Encryptor",
alloc_encrypt, alloc_encrypt,
@ -714,9 +179,10 @@ struct lfd_mod lfd_encrypt = {
NULL NULL
}; };
#else /* HAVE_SSL */ #else /* HAVE_SODIUM */
static int no_encrypt(struct vtun_host *host) static int
no_encrypt(struct vtun_host *host)
{ {
vtun_syslog(LOG_INFO, "Encryption is not supported"); vtun_syslog(LOG_INFO, "Encryption is not supported");
return -1; return -1;
@ -727,4 +193,4 @@ struct lfd_mod lfd_encrypt = {
no_encrypt, NULL, NULL, NULL, NULL, NULL, NULL, NULL no_encrypt, NULL, NULL, NULL, NULL, NULL, NULL, NULL
}; };
#endif /* HAVE_SSL */ #endif

10
main.c
View File

@ -35,6 +35,10 @@
#include <netinet/in.h> #include <netinet/in.h>
#endif #endif
#ifdef HAVE_SODIUM
#include <sodium.h>
#endif
#include "vtun.h" #include "vtun.h"
#include "lib.h" #include "lib.h"
#include "compat.h" #include "compat.h"
@ -194,6 +198,12 @@ int main(int argc, char *argv[], char *env[])
break; break;
} }
#ifdef HAVE_SODIUM
if (sodium_init() != 0) {
abort();
}
#endif
if( daemon ){ if( daemon ){
#ifdef HAVE_WORKING_FORK #ifdef HAVE_WORKING_FORK
if( dofork && fork() ) if( dofork && fork() )

17
vtun.h
View File

@ -82,9 +82,12 @@ struct vtun_addr {
#define VTUN_ADDR_IFACE 0x01 #define VTUN_ADDR_IFACE 0x01
#define VTUN_ADDR_NAME 0x02 #define VTUN_ADDR_NAME 0x02
#define HOST_KEYBYTES 32
struct vtun_host { struct vtun_host {
char *host; char *host;
char *passwd; char *passwd;
unsigned char *key;
char *dev; char *dev;
llist up; llist up;
@ -138,19 +141,7 @@ extern llist host_list;
#define VTUN_ENCRYPT 0x0008 #define VTUN_ENCRYPT 0x0008
/* Cipher options */ /* Cipher options */
#define VTUN_ENC_BF128CBC 2 #define VTUN_ENC_AES256GCM 17
#define VTUN_ENC_BF128CFB 3
#define VTUN_ENC_BF128OFB 4
#define VTUN_ENC_BF256CBC 6
#define VTUN_ENC_BF256CFB 7
#define VTUN_ENC_BF256OFB 8
#define VTUN_ENC_AES128CBC 10
#define VTUN_ENC_AES128CFB 11
#define VTUN_ENC_AES128OFB 12
#define VTUN_ENC_AES256CBC 14
#define VTUN_ENC_AES256CFB 15
#define VTUN_ENC_AES256OFB 16
/* Mask to drop the flags which will be supplied by the server */ /* Mask to drop the flags which will be supplied by the server */
#define VTUN_CLNT_MASK 0xf000 #define VTUN_CLNT_MASK 0xf000

View File

@ -150,18 +150,7 @@
# ----------- # -----------
# encrypt - Enable 'yes' or disable 'no' encryption. # encrypt - Enable 'yes' or disable 'no' encryption.
# It is also possible to specify a method: # It is also possible to specify a method:
# 'blowfish128cbc' - Blowfish cipher, 128 bit key, mode CBC # 'aes256gcm' - AES cipher, 256 bit key, mode GCM
# 'blowfish128cfb' - Blowfish cipher, 128 bit key, mode CFB
# 'blowfish128ofb' - Blowfish cipher, 128 bit key, mode OFB
# 'blowfish256cbc' - Blowfish cipher, 256 bit key, mode CBC
# 'blowfish256cfb' - Blowfish cipher, 256 bit key, mode CFB
# 'blowfish256ofb' - Blowfish cipher, 256 bit key, mode OFB
# 'aes128cbc' - AES cipher, 128 bit key, mode CBC
# 'aes128cfb' - AES cipher, 128 bit key, mode CFB
# 'aes128ofb' - AES cipher, 128 bit key, mode OFB
# 'aes256cbc' - AES cipher, 256 bit key, mode CBC
# 'aes256cfb' - AES cipher, 256 bit key, mode CFB
# 'aes256ofb' - AES cipher, 256 bit key, mode OFB
# #
# Ignored by the client. # Ignored by the client.
# #

View File

@ -199,30 +199,8 @@ specifies encryption method to use. Encryption \fImethod\fRs include:
no encryption no encryption
.IP \fByes\fR .IP \fByes\fR
default encryption method default encryption method
.IP \fBblowfish128cbc\fR .IP \fBaes256gcm\fR
Blowfish cipher, 128 bit key, mode CBC AES cipher, 256 bit key, mode GCM
.IP \fBblowfish128cfb\fR
Blowfish cipher, 128 bit key, mode CFB
.IP \fBblowfish128ofb\fR
Blowfish cipher, 128 bit key, mode OFB
.IP \fBblowfish256cbc\fR
Blowfish cipher, 256 bit key, mode CBC
.IP \fBblowfish256cfb\fR
Blowfish cipher, 256 bit key, mode CFB
.IP \fBblowfish256ofb\fR
Blowfish cipher, 256 bit key, mode OFB
.IP \fBaes128cbc\fR
AES cipher, 128 bit key, mode CBC
.IP \fBaes128cfb\fR
AES cipher, 128 bit key, mode CFB
.IP \fBaes128ofb\fR
AES cipher, 128 bit key, mode OFB
.IP \fBaes256cbc\fR
AES cipher, 256 bit key, mode CBC
.IP \fBaes256cfb\fR
AES cipher, 256 bit key, mode CFB
.IP \fBaes256ofb\fR
AES cipher, 256 bit key, mode OFB
.RE .RE
.IP .IP
This option is ignored by the client. This option is ignored by the client.