vtun/auth.c

400 lines
8.3 KiB
C
Raw Normal View History

/*
VTun - Virtual Tunnel over TCP/IP network.
2008-01-08 00:35:18 +02:00
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.
*/
/*
* $Id: auth.c,v 1.9.2.5 2013/07/07 19:54:20 mtbishop Exp $
*/
/*
* Challenge based authentication.
* Thanx to Chris Todd<christ@insynq.com> for the good idea.
*/
#include "config.h"
#include <assert.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <errno.h>
#include <fcntl.h>
#include <signal.h>
#include <syslog.h>
#include <time.h>
#ifdef HAVE_NETINET_IN_H
#include <netinet/in.h>
#endif
#ifdef HAVE_NETINET_TCP_H
#include <netinet/tcp.h>
#endif
#ifdef HAVE_ARPA_INET_H
#include <arpa/inet.h>
#endif
#include "vtun.h"
#include "lib.h"
#include "lock.h"
#include "auth.h"
/* Encryption and Decryption of the challenge key */
#include <sodium.h>
static int derive_key(struct vtun_host *host)
{
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 gen_chal(char *chal)
{
randombytes_buf((unsigned char *) chal, VTUN_CHAL_SIZE);
}
static void auth_chal(char *chal, const struct vtun_host *host)
{
crypto_generichash(chal, VTUN_CHAL_SIZE, chal, VTUN_CHAL_SIZE,
host->key, HOST_KEYBYTES);
}
/*
* Functions to convert binary flags to character string.
* string format: <CS64>
* C - compression, S - speed for shaper and so on.
*/
static char *bf2cf(struct vtun_host *host)
{
static char str[20], *ptr = str;
*(ptr++) = '<';
switch( host->flags & VTUN_PROT_MASK ){
case VTUN_TCP:
*(ptr++) = 'T';
break;
case VTUN_UDP:
*(ptr++) = 'U';
break;
}
switch( host->flags & VTUN_TYPE_MASK ){
case VTUN_TTY:
*(ptr++) = 't';
break;
case VTUN_PIPE:
*(ptr++) = 'p';
break;
case VTUN_ETHER:
*(ptr++) = 'e';
break;
case VTUN_TUN:
*(ptr++) = 'u';
break;
}
if( (host->flags & VTUN_SHAPE) /* && host->spd_in */)
ptr += sprintf(ptr,"S%d",host->spd_in);
if( host->flags & VTUN_ZLIB )
ptr += sprintf(ptr,"C%d", host->zlevel);
if( host->flags & VTUN_LZO )
ptr += sprintf(ptr,"L%d", host->zlevel);
if( host->flags & VTUN_KEEP_ALIVE )
*(ptr++) = 'K';
if( host->flags & VTUN_ENCRYPT ) {
ptr += sprintf(ptr,"E%d", host->cipher);
}
strcat(ptr,">");
return str;
}
/* return 1 on success, otherwise 0
Example:
FLAGS: <TuE1>
*/
static int cf2bf(char *str, struct vtun_host *host)
{
char *ptr, *p;
int s;
if( (ptr = strchr(str,'<')) ){
vtun_syslog(LOG_DEBUG,"Remote Server sends %s.", ptr);
ptr++;
while(*ptr){
switch(*ptr++){
case 't':
host->flags |= VTUN_TTY;
break;
case 'p':
host->flags |= VTUN_PIPE;
break;
case 'e':
host->flags |= VTUN_ETHER;
break;
case 'u':
host->flags |= VTUN_TUN;
break;
case 'U':
host->flags &= ~VTUN_PROT_MASK;
host->flags |= VTUN_UDP;
break;
case 'T':
host->flags &= ~VTUN_PROT_MASK;
host->flags |= VTUN_TCP;
break;
case 'K':
host->flags |= VTUN_KEEP_ALIVE;
break;
case 'C':
if((s = strtol(ptr,&p,10)) == ERANGE || ptr == p)
return 0;
host->flags |= VTUN_ZLIB;
host->zlevel = s;
ptr = p;
break;
case 'L':
if((s = strtol(ptr,&p,10)) == ERANGE || ptr == p)
return 0;
host->flags |= VTUN_LZO;
host->zlevel = s;
ptr = p;
break;
case 'E':
/* new form is 'E10', old form is 'E', so remove the
ptr==p check */
if((s = strtol(ptr,&p,10)) == ERANGE) {
vtun_syslog(LOG_ERR,"Garbled encryption method. Bailing out.");
return 0;
}
host->flags |= VTUN_ENCRYPT;
host->cipher = s;
ptr = p;
break;
case 'S':
if((s = strtol(ptr,&p,10)) == ERANGE || ptr == p)
return 0;
if( s ){
host->flags |= VTUN_SHAPE;
host->spd_out = s;
}
ptr = p;
break;
case 'F':
/* reserved for Feature transmit */
break;
case '>':
return 1;
default:
return 0;
}
}
}
return 0;
}
/*
* Functions to convert binary key data to character string.
* string format: <char_data>
*/
static char *cl2cs(char *chal)
{
static char str[VTUN_CHAL_SIZE*2+3], *chr="abcdefghijklmnop";
register char *ptr = str;
register int i;
*(ptr++) = '<';
for(i=0; i<VTUN_CHAL_SIZE; i++){
*(ptr++) = chr[ ((chal[i] & 0xf0) >> 4) ];
*(ptr++) = chr[ (chal[i] & 0x0f) ];
}
*(ptr++) = '>';
*ptr = '\0';
return str;
}
static int cs2cl(char *str, char *chal)
{
register char *ptr = str;
register int i;
if( !(ptr = strchr(str,'<')) )
return 0;
ptr++;
if( !strtok(ptr,">") || strlen(ptr) != VTUN_CHAL_SIZE*2 )
return 0;
for(i=0; i<VTUN_CHAL_SIZE && *ptr; i++, ptr+=2) {
chal[i] = (*ptr - 'a') << 4;
chal[i] |= *(ptr+1) - 'a';
}
return 1;
}
/* Authentication (Server side) */
struct vtun_host * auth_server(int fd)
{
char chal_req[VTUN_CHAL_SIZE], chal_res[VTUN_CHAL_SIZE];
char buf[VTUN_MESG_SIZE], *str1, *str2;
struct vtun_host *h = NULL;
char *host = NULL;
int stage;
set_title("authentication");
print_p(fd,"VTUN server ver %s\n",VTUN_VER);
stage = ST_HOST;
while( readn_t(fd, buf, VTUN_MESG_SIZE, vtun.timeout) > 0 ){
buf[sizeof(buf)-1]='\0';
strtok(buf,"\r\n");
if( !(str1=strtok(buf," :")) )
break;
if( !(str2=strtok(NULL," :")) )
break;
switch( stage ){
case ST_HOST:
if( !strcmp(str1,"HOST") ){
host = strdup(str2);
gen_chal(chal_req);
print_p(fd,"OK CHAL: %s\n", cl2cs(chal_req));
stage = ST_CHAL;
continue;
}
break;
case ST_CHAL:
if( !strcmp(str1,"CHAL") ){
if( !cs2cl(str2,chal_res) )
break;
if( !(h = find_host(host)) )
break;
derive_key(h);
auth_chal(chal_req, h);
if( !sodium_memcmp(chal_req, chal_res, VTUN_CHAL_SIZE) ){
/* Auth successeful. */
/* Lock host */
if( lock_host(h) < 0 ){
/* Multiple connections are denied */
h = NULL;
break;
}
print_p(fd,"OK FLAGS: %s\n", bf2cf(h));
} else
h = NULL;
}
break;
}
break;
}
if( host )
free(host);
if( !h )
print_p(fd,"ERR\n");
return h;
}
/* Authentication (Client side) */
int auth_client(int fd, struct vtun_host *host)
{
char buf[VTUN_MESG_SIZE], chal[VTUN_CHAL_SIZE];
int stage, success=0 ;
stage = ST_INIT;
while( readn_t(fd, buf, VTUN_MESG_SIZE, vtun.timeout) > 0 ){
buf[sizeof(buf)-1]='\0';
switch( stage ){
case ST_INIT:
if( !strncmp(buf,"VTUN",4) ){
stage = ST_HOST;
print_p(fd,"HOST: %s\n",host->host);
continue;
}
break;
case ST_HOST:
if( !strncmp(buf,"OK",2) && cs2cl(buf,chal)){
stage = ST_CHAL;
derive_key(host);
auth_chal(chal, host);
print_p(fd,"CHAL: %s\n", cl2cs(chal));
continue;
}
break;
case ST_CHAL:
if( !strncmp(buf,"OK",2) && cf2bf(buf,host) )
success = 1;
break;
}
break;
}
return success;
}