Initial revision

This commit is contained in:
Ivo Timmermans 2000-03-26 00:33:07 +00:00
commit 1243156a5e
87 changed files with 27214 additions and 0 deletions

19
src/Makefile.am Normal file
View file

@ -0,0 +1,19 @@
## Produce this file with automake to get Makefile.in
sbin_PROGRAMS = tincd genauth
genauth_SOURCES = genauth.c
tincd_SOURCES = conf.c encr.c net.c netutl.c protocol.c tincd.c
INCLUDES = -I$(top_builddir) -I$(top_srcdir)/cipher -I$(top_srcdir)/lib
noinst_HEADERS = conf.h encr.h net.h netutl.h protocol.h
LIBS = @LIBS@
tincd_LDADD = $(top_builddir)/cipher/libcipher.la \
$(top_builddir)/lib/libvpn.a -ldl
genauth_LDADD = $(top_builddir)/lib/libvpn.a
CFLAGS += -DPKGLIBDIR=$(pkglibdir) -DCONFDIR=\"@sysconfdir@\"

203
src/conf.c Normal file
View file

@ -0,0 +1,203 @@
/*
conf.c -- configuration code
Copyright (C) 1998 Emphyrio,
Copyright (C) 1998,99 Ivo Timmermans <zarq@iname.com>
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
/* foute config read code, GPL, emphyrio 1998 */
/* Mutilated by me -- Ivo */
#include "config.h"
#include <ctype.h>
#include <errno.h>
#include <netdb.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <xalloc.h>
#include "conf.h"
#include "netutl.h" /* for strtoip */
config_t *config;
int debug_lvl = 0;
int timeout = 0; /* seconds before timeout */
typedef struct internal_config_t {
char *name;
enum which_t which;
int argtype;
} internal_config_t;
/*
These are all the possible configurable values
*/
static internal_config_t hazahaza[] = {
{ "AllowConnect", allowconnect, TYPE_BOOL },
{ "ConnectTo", upstreamip, TYPE_IP },
{ "ConnectPort", upstreamport, TYPE_INT },
{ "ListenPort", listenport, TYPE_INT },
{ "MyOwnVPNIP", myvpnip, TYPE_IP },
{ "MyVirtualIP", myvpnip, TYPE_IP }, /* an alias */
{ "Passphrases", passphrasesdir, TYPE_NAME },
{ "PingTimeout", pingtimeout, TYPE_INT },
{ "TapDevice", tapdevice, TYPE_NAME },
{ "KeyExpire", keyexpire, TYPE_INT },
{ NULL, 0, 0 }
};
/*
Add given value to the list of configs cfg
*/
config_t *
add_config_val(config_t **cfg, int argtype, char *val)
{
config_t *p;
char *q;
p = (config_t*)xmalloc(sizeof(config_t));
p->data.val = 0;
switch(argtype)
{
case TYPE_INT:
p->data.val = strtol(val, &q, 0);
if(q && *q)
p->data.val = 0;
break;
case TYPE_NAME:
p->data.ptr = xmalloc(strlen(val) + 1);
strcpy(p->data.ptr, val);
break;
case TYPE_IP:
p->data.ip = strtoip(val);
break;
case TYPE_BOOL:
if(!strcasecmp("yes", val))
p->data.val = stupid_true;
else if(!strcasecmp("no", val))
p->data.val = stupid_false;
else
p->data.val = 0;
}
if(p->data.val)
{
p->next = *cfg;
*cfg = p;
return p;
}
free(p);
return NULL;
}
/*
Get variable from a section in a configfile. returns -1 on failure.
*/
int
readconfig(const char *fname, FILE *fp)
{
char line[81];
char *p, *q;
int i, lineno = 0;
config_t *cfg;
for(;;)
{
if(fgets(line, 80, fp) == NULL)
return 0;
lineno++;
if((p = strtok(line, "\t\n\r =")) == NULL)
continue; /* no tokens on this line */
if(p[0] == '#')
continue; /* comment: ignore */
for(i = 0; hazahaza[i].name != NULL; i++)
if(!strcasecmp(hazahaza[i].name, p))
break;
if(!hazahaza[i].name)
{
fprintf(stderr, "%s: %d: Invalid variable name `%s'.\n",
fname, lineno, p);
return -1;
}
if(((q = strtok(NULL, "\t\n\r =")) == NULL) || q[0] == '#')
{
fprintf(stderr, "%s: %d: No value given for `%s'.\n",
fname, lineno, hazahaza[i].name);
return -1;
}
cfg = add_config_val(&config, hazahaza[i].argtype, q);
if(cfg == NULL)
{
fprintf(stderr, "%s: %d: Invalid value `%s' for variable `%s'.\n",
fname, lineno, q, hazahaza[i].name);
return -1;
}
cfg->which = hazahaza[i].which;
if(!config)
config = cfg;
}
}
/*
wrapper function for readconfig
*/
int
read_config_file(const char *fname)
{
FILE *fp;
if((fp = fopen (fname, "r")) == NULL)
{
fprintf(stderr, "Could not open %s: %s\n", fname, sys_errlist[errno]);
return 1;
}
if(readconfig(fname, fp))
return -1;
fclose (fp);
return 0;
}
/*
Look up the value of the config option type
*/
const config_t *
get_config_val(which_t type)
{
config_t *p;
for(p = config; p != NULL; p = p->next)
if(p->which == type)
return p;
/* Not found */
return NULL;
}

72
src/conf.h Normal file
View file

@ -0,0 +1,72 @@
/*
conf.h -- header for conf.c
Copyright (C) 1998,99 Ivo Timmermans <zarq@iname.com>
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __TINC_CONF_H__
#define __TINC_CONF_H__
typedef struct ip_mask_t {
unsigned long ip;
unsigned long mask;
} ip_mask_t;
typedef union data_t {
unsigned long val;
void *ptr;
ip_mask_t *ip;
} data_t;
typedef enum which_t {
passphrasesdir = 1,
upstreamip,
upstreamport,
listenport,
myvpnip,
tapdevice,
allowconnect,
pingtimeout,
keyexpire,
} which_t;
typedef struct config_t {
struct config_t *next;
which_t which;
data_t data;
} config_t;
enum {
stupid_false = 1,
stupid_true
};
enum {
TYPE_NAME = 1,
TYPE_INT,
TYPE_IP,
TYPE_BOOL
};
extern config_t *config;
extern int debug_lvl;
extern int timeout;
extern config_t *add_config_val(config_t **, int, char *);
extern int read_config_file(const char *);
extern const config_t *get_config_val(which_t type);
#endif /* __TINC_CONF_H__ */

325
src/encr.c Normal file
View file

@ -0,0 +1,325 @@
/*
encr.c -- everything that deals with encryption
Copyright (C) 1998,99 Ivo Timmermans <zarq@iname.com>
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "config.h"
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <sys/socket.h>
#include <sys/time.h>
#ifdef HAVE_GMP_H
# include <gmp.h>
#else
# ifdef HAVE_GMP2_GMP_H
# include <gmp2/gmp.h>
# endif
#endif
#include <utils.h>
#include <xalloc.h>
#include <cipher.h>
#include "conf.h"
#include "encr.h"
#include "net.h"
#include "protocol.h"
#define ENCR_GENERATOR "0xd"
#define ENCR_PRIME "0x7fffffffffffffffffffffffffffffff" /* Mersenne :) */
char text_key[1000];
char *my_public_key_base36;
int key_inited = 0, encryption_keylen;
mpz_t my_private_key, my_public_key, generator, shared_prime;
int my_key_expiry = (time_t)(-1);
static char* mypassphrase;
static int mypassphraselen;
int char_hex_to_bin(int c)
{
if(isdigit(c))
return c - '0';
else
return tolower(c) - 'a' + 10;
}
int str_hex_to_bin(unsigned char *bin, unsigned char *hex)
{
int i = 0, j = 0, l = strlen(hex);
if(l&1)
{
i = j = 1;
bin[0] = char_hex_to_bin(hex[0]);
}
for(; i < l; i+=2, j++)
bin[j] = (char_hex_to_bin(hex[i]) << 4) + char_hex_to_bin(hex[i+1]);
return j&1?j+1:j;
}
int read_passphrase(char *which, char **out)
{
FILE *f;
config_t const *cfg;
char *filename;
int size;
extern char *confbase;
char *pp;
if((cfg = get_config_val(passphrasesdir)) == NULL)
{
filename = xmalloc(strlen(confbase)+13+strlen(which));
sprintf(filename, "%spassphrases/%s", confbase, which);
}
else
{
filename = xmalloc(strlen(cfg->data.ptr)+2+strlen(which));
sprintf(filename, "%s/%s", (char*)cfg->data.ptr, which);
}
if((f = fopen(filename, "rb")) == NULL)
{
syslog(LOG_ERR, "Could not open %s: %m", filename);
return -1;
}
fscanf(f, "%d ", &size);
size >>= 2; /* nibbles->bits */
pp = xmalloc(size+2);
fgets(pp, size+1, f);
fclose(f);
*out = xmalloc(size);
return str_hex_to_bin(*out, pp);
}
int read_my_passphrase(void)
{
if((mypassphraselen = read_passphrase("local", &mypassphrase)) < 0)
return -1;
return 0;
}
int generate_private_key(void)
{
FILE *f;
int i;
char *s;
config_t const *cfg;
if((cfg = get_config_val(keyexpire)) == NULL)
my_key_expiry = (time_t)(time(NULL) + 3600);
else
my_key_expiry = (time_t)(time(NULL) + cfg->data.val);
syslog(LOG_NOTICE, "Generating %d bits keys.", PRIVATE_KEY_BITS);
if((f = fopen("/dev/urandom", "r")) == NULL)
{
syslog(LOG_ERR, "Opening /dev/urandom failed: %m");
return -1;
}
s = xmalloc((2 * PRIVATE_KEY_LENGTH) + 1);
for(i = 0; i < PRIVATE_KEY_LENGTH; i++)
sprintf(&s[i << 1], "%02x", fgetc(f));
s[2 * PRIVATE_KEY_LENGTH] = '\0';
mpz_set_str(my_private_key, s, 16);
return 0;
}
void calculate_public_key(void)
{
mpz_powm(my_public_key, generator, my_private_key, shared_prime);
my_public_key_base36 = mpz_get_str(NULL, 36, my_public_key);
}
unsigned char static_key[] = { 0x9c, 0xbf, 0x36, 0xa9, 0xce, 0x20, 0x1b, 0x8b, 0x67, 0x56, 0x21, 0x5d, 0x27, 0x1b, 0xd8, 0x7a };
int security_init(void)
{
mpz_init(my_private_key);
mpz_init(my_public_key);
mpz_init_set_str(shared_prime, ENCR_PRIME, 0);
mpz_init_set_str(generator, ENCR_GENERATOR, 0);
if(read_my_passphrase() < 0)
return -1;
if(generate_private_key() < 0)
return -1;
if(cipher_init(CIPHER_BLOWFISH) < 0)
return -1;
calculate_public_key();
return 0;
}
void set_shared_key(char *almost_key)
{
char *tmp;
int len;
mpz_t ak, our_shared_key;
mpz_init_set_str(ak, almost_key, 36);
mpz_init(our_shared_key);
mpz_powm(our_shared_key, ak, my_private_key, shared_prime);
tmp = mpz_get_str(NULL, 16, our_shared_key);
len = str_hex_to_bin(text_key, tmp);
cipher_set_key(&encryption_key, len, &text_key[0]);
key_inited = 1;
encryption_keylen = len;
if(debug_lvl > 2)
syslog(LOG_INFO, "Encryption key set to %s", tmp);
free(tmp);
mpz_clear(ak);
mpz_clear(our_shared_key);
}
void encrypt_passphrase(passphrase_t *pp)
{
char key[1000];
char tmp[1000];
int len;
BF_KEY bf_key;
mpz_get_str(&tmp[0], 16, my_public_key);
len = str_hex_to_bin(key, tmp);
cipher_set_key(&bf_key, len, &key[0]);
low_crypt_key(mypassphrase, pp->phrase, &bf_key, mypassphraselen, BF_ENCRYPT);
pp->len = ((mypassphraselen - 1) | 7) + 5;
if(key_inited)
cipher_set_key(&encryption_key, encryption_keylen, &text_key[0]);
}
int verify_passphrase(conn_list_t *cl, unsigned char *his_pubkey)
{
char key[1000];
char tmp[1000];
int len;
mpz_t pk;
unsigned char *out;
BF_KEY bf_key;
char which[sizeof("123.123.123.123")+1];
char *meuk;
mpz_init_set_str(pk, his_pubkey, 36);
mpz_get_str(&tmp[0], 16, pk);
len = str_hex_to_bin(key, tmp);
out = xmalloc(cl->pp->len+3);
cipher_set_key(&bf_key, len, &key[0]);
low_crypt_key(cl->pp->phrase, out, &bf_key, cl->pp->len, BF_DECRYPT);
if(key_inited)
cipher_set_key(&encryption_key, encryption_keylen, &text_key[0]);
sprintf(&which[0], IP_ADDR_S, IP_ADDR_V(cl->vpn_ip));
if((len = read_passphrase(which, &meuk)) < 0)
return -1;
if(memcmp(meuk, out, len))
return -1;
return 0;
}
char *make_shared_key(char *pk)
{
mpz_t tmp, res;
char *r;
mpz_init_set_str(tmp, pk, 36);
mpz_init(res);
mpz_powm(res, tmp, my_private_key, shared_prime);
r = mpz_get_str(NULL, 36, res);
mpz_clear(res);
mpz_clear(tmp);
return r;
}
/*
free a key after overwriting it
*/
void free_key(enc_key_t *k)
{
if(!k)
return;
if(k->key)
{
memset(k->key, (char)(-1), k->length);
free(k->key);
}
free(k);
}
void recalculate_encryption_keys(void)
{
conn_list_t *p;
char *ek;
for(p = conn_list; p != NULL; p = p->next)
{
if(!p->public_key || !p->public_key->key)
continue;
ek = make_shared_key(p->public_key->key);
if(!p->key)
{
p->key = xmalloc(sizeof(enc_key_t));
p->key->key = NULL;
}
if(p->key->key)
free(p->key->key);
p->key->length = strlen(ek);
p->key->expiry = p->public_key->expiry;
p->key->key = xmalloc(strlen(ek) + 1);
strcpy(p->key->key, ek);
}
}
void regenerate_keys(void)
{
generate_private_key();
calculate_public_key();
send_key_changed2();
recalculate_encryption_keys();
}

47
src/encr.h Normal file
View file

@ -0,0 +1,47 @@
/*
encr.h -- header for encr.c
Copyright (C) 1998,99 Ivo Timmermans <zarq@iname.com>
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __TINC_ENCR_H__
#define __TINC_ENCR_H__
#include "net.h"
#define PRIVATE_KEY_BITS 128
#define PRIVATE_KEY_LENGTH (PRIVATE_KEY_BITS >> 3)
extern char *my_public_key_base36;
extern int my_key_expiry;
extern int security_init(void);
extern void do_bf_encrypt(vpn_packet_t *, real_packet_t *);
extern void do_bf_decrypt(real_packet_t *, vpn_packet_t *);
extern int send_portnumbers(int);
extern void set_shared_key(char *);
extern int send_passphrase(conn_list_t *);
extern int send_public_key(conn_list_t *);
extern int verify_passphrase(conn_list_t *, unsigned char *);
extern char *make_shared_key(char*);
extern void encrypt_passphrase(passphrase_t *pp);
extern void free_key(enc_key_t*);
extern void regenerate_keys(void);
#endif /* __TINC_ENCR_H__ */

94
src/genauth.c Normal file
View file

@ -0,0 +1,94 @@
/*
genauth.c -- generate a random passphrase
Copyright (C) 1998,99 Ivo Timmermans <zarq@iname.com>
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "config.h"
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <xalloc.h>
#include "encr.h"
unsigned char initvec[] = { 0x22, 0x7b, 0xad, 0x55, 0x41, 0xf4, 0x3e, 0xf3 };
int main(int argc, char **argv)
{
FILE *fp;
int bits, c, i, bytes;
unsigned char *p;
if(argc != 2)
{
fprintf(stderr, "Usage: %s bits\n", argv[0]);
return 1;
}
if(!(bits = atol(argv[1])))
{
fprintf(stderr, "Illegal number: %s\n", argv[1]);
return 1;
}
bits = ((bits - 1) | 63) + 1;
fprintf(stderr, "Generating %d bits number", bits);
bytes = bits >> 3;
if((fp = fopen("/dev/urandom", "r")) == NULL)
{
perror("Opening /dev/urandom");
return 1;
}
p = xmalloc(bytes);
setbuf(stdout, NULL);
for(i = 0; i < 128; i++)
{
c = fgetc(fp);
if(feof(fp))
{
puts("");
fprintf(stderr, "File was empty!\n");
}
p[i] = c;
}
for(i = 0; i < (bytes); i++)
{
c = fgetc(fp);
if(feof(fp))
{
puts("");
fprintf(stderr, "File was empty!\n");
}
p[i] = c;
}
fclose(fp);
printf("%d ", bits);
for(i = 0; i < bytes; i++)
printf("%02x", p[i]);
puts("");
return 0;
}

1094
src/net.c Normal file

File diff suppressed because it is too large Load diff

139
src/net.h Normal file
View file

@ -0,0 +1,139 @@
/*
net.h -- header for net.c
Copyright (C) 1998,99 Ivo Timmermans <zarq@iname.com>
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __TINC_NET_H__
#define __TINC_NET_H__
#include <sys/time.h>
#include "config.h"
#include "conf.h"
#define MAXSIZE 1700 /* should be a bit more than the MTU for the tapdevice */
#define MTU 1600
#define MAX_PASSPHRASE_SIZE 2000 /* 2kb is really waaaay too much. nobody's
gonna need a 16 kbit passphrase */
#define MAC_ADDR_S "%02x:%02x:%02x:%02x:%02x:%02x"
#define MAC_ADDR_V(x) ((unsigned char*)&(x))[0],((unsigned char*)&(x))[1], \
((unsigned char*)&(x))[2],((unsigned char*)&(x))[3], \
((unsigned char*)&(x))[4],((unsigned char*)&(x))[5]
#define IP_ADDR_S "%d.%d.%d.%d"
#ifdef WORDS_BIGENDIAN
# define IP_ADDR_V(x) ((unsigned char*)&(x))[0],((unsigned char*)&(x))[1], \
((unsigned char*)&(x))[2],((unsigned char*)&(x))[3]
#else
# define IP_ADDR_V(x) ((unsigned char*)&(x))[3],((unsigned char*)&(x))[2], \
((unsigned char*)&(x))[1],((unsigned char*)&(x))[0]
#endif
typedef unsigned long ip_t;
typedef short length_t;
typedef struct vpn_packet_t {
length_t len; /* the actual number of bytes in the `data' field */
unsigned char data[MAXSIZE];
} vpn_packet_t;
typedef struct real_packet_t {
length_t len; /* the length of the entire packet */
ip_t from; /* where the packet came from */
vpn_packet_t data; /* encrypted vpn_packet_t */
} real_packet_t;
typedef struct passphrase_t {
unsigned char type;
unsigned short len;
unsigned char phrase[MAX_PASSPHRASE_SIZE];
} passphrase_t;
typedef struct status_bits_t {
int pinged:1; /* sent ping */
int got_pong:1; /* received pong */
int meta:1; /* meta connection exists */
int active:1; /* 1 if active.. */
int outgoing:1; /* I myself asked for this conn */
int termreq:1; /* the termination of this connection was requested */
int remove:1; /* Set to 1 if you want this connection removed */
int timeout:1; /* 1 if gotten timeout */
int validkey:1; /* 1 if we currently have a valid key for him */
int waitingforkey:1; /* 1 if we already sent out a request */
int dataopen:1; /* 1 if we have a valid UDP connection open */
int unused:22;
} status_bits_t;
typedef struct queue_element_t {
void *packet;
struct queue_element_t *next;
} queue_element_t;
typedef struct packet_queue_t {
queue_element_t *head;
queue_element_t *tail;
} packet_queue_t;
typedef struct enc_key_t {
int length;
char *key;
time_t expiry;
} enc_key_t;
typedef struct conn_list_t {
ip_t vpn_ip; /* his vpn ip */
ip_t vpn_mask; /* his vpn network address */
ip_t real_ip; /* his real (internet) ip */
char *hostname; /* the hostname of its real ip */
short int port; /* his portnumber */
int socket; /* our udp vpn socket */
int meta_socket; /* our tcp meta socket */
unsigned char protocol_version; /* used protocol */
status_bits_t status; /* status info */
passphrase_t *pp; /* encoded passphrase */
packet_queue_t *sq; /* pending outgoing packets */
packet_queue_t *rq; /* pending incoming packets (they have no
valid key to be decrypted with) */
enc_key_t *public_key; /* the other party's public key */
enc_key_t *key; /* encrypt with this key */
struct conn_list_t *nexthop; /* nearest meta-hop in this direction */
struct conn_list_t *next; /* after all, it's a list of connections */
} conn_list_t;
extern int tap_fd;
extern int total_tap_in;
extern int total_tap_out;
extern int total_socket_in;
extern int total_socket_out;
extern conn_list_t *conn_list;
extern conn_list_t *myself;
extern int send_packet(ip_t, vpn_packet_t *);
extern int send_broadcast(conn_list_t *, vpn_packet_t *);
extern int setup_network_connections(void);
extern void close_network_connections(void);
extern void main_loop(void);
extern int setup_vpn_connection(conn_list_t *);
extern void terminate_connection(conn_list_t *);
extern void flush_queues(conn_list_t*);
#endif /* __TINC_NET_H__ */

232
src/netutl.c Normal file
View file

@ -0,0 +1,232 @@
/*
netutl.c -- some supporting network utility code
Copyright (C) 1998,99 Ivo Timmermans <zarq@iname.com>
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "config.h"
#include <arpa/inet.h>
#include <netdb.h>
#include <netinet/in.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/socket.h>
#include <syslog.h>
#include <utils.h>
#include <xalloc.h>
#include "encr.h"
#include "net.h"
#include "netutl.h"
/*
look for a connection associated with the given vpn ip,
return its connection structure
*/
conn_list_t *lookup_conn(ip_t ip)
{
conn_list_t *p = conn_list;
/* Exact match suggested by James B. MacLean */
for(p = conn_list; p != NULL; p = p->next)
if(ip == p->vpn_ip)
return p;
for(p = conn_list; p != NULL; p = p->next)
if((ip & p->vpn_mask) == (p->vpn_ip & p->vpn_mask))
return p;
return NULL;
}
/*
free a queue and all of its elements
*/
void destroy_queue(packet_queue_t *pq)
{
queue_element_t *p, *q;
for(p = pq->head; p != NULL; p = q)
{
q = p->next;
if(p->packet)
free(p->packet);
free(p);
}
free(pq);
}
/*
free a conn_list_t element and all its pointers
*/
void free_conn_element(conn_list_t *p)
{
if(p->hostname)
free(p->hostname);
if(p->pp)
free(p->pp);
if(p->sq)
destroy_queue(p->sq);
if(p->rq)
destroy_queue(p->rq);
free_key(p->public_key);
free_key(p->key);
free(p);
}
/*
remove all marked connections
*/
void prune_conn_list(void)
{
conn_list_t *p, *prev = NULL, *next = NULL;
for(p = conn_list; p != NULL; )
{
next = p->next;
if(p->status.remove)
{
if(prev)
prev->next = next;
else
conn_list = next;
free_conn_element(p);
}
else
prev = p;
p = next;
}
}
/*
creates new conn_list element, and initializes it
*/
conn_list_t *new_conn_list(void)
{
conn_list_t *p = xmalloc(sizeof(conn_list_t));
/* initialise all those stupid pointers at once */
memset(p, '\0', sizeof(conn_list_t));
p->nexthop = p;
return p;
}
/*
free all elements of conn_list
*/
void destroy_conn_list(void)
{
conn_list_t *p, *next;
cp
for(p = conn_list; p != NULL; )
{
next = p->next;
free_conn_element(p);
p = next;
}
cp
conn_list = NULL;
}
/*
look up the name associated with the ip
address `addr'
*/
char *hostlookup(unsigned long addr)
{
char *name;
struct hostent *host = NULL;
struct in_addr in;
in.s_addr = addr;
host = gethostbyaddr((char *)&in, sizeof(in), AF_INET);
if(host)
{
name = xmalloc(strlen(host->h_name)+20);
sprintf(name, "%s (%s)", host->h_name, inet_ntoa(in));
}
else
{
name = xmalloc(20);
sprintf(name, "%s", inet_ntoa(in));
}
return name;
}
/*
Turn a string into an IP addy with netmask
return NULL on failure
*/
ip_mask_t *strtoip(char *str)
{
ip_mask_t *ip;
int masker;
char *q, *p;
struct hostent *h;
p = str;
if((q = strchr(p, '/')))
{
*q = '\0';
q++; /* q now points to netmask part, or NULL if no mask */
}
if(!(h = gethostbyname(p)))
{
fprintf(stderr, "Error looking up `%s': %s\n", p, sys_errlist[h_errno]);
return NULL;
}
masker = 0;
if(q)
{
masker = strtol(q, &p, 10);
if(q == p || (*p))
return NULL;
}
ip = xmalloc(sizeof(ip_mask_t));
ip->ip = ntohl(*((ip_t*)(h->h_addr_list[0])));
ip->mask = masker ? ~((1 << (32 - masker)) - 1) : 0;
return ip;
}
void dump_conn_list(void)
{
conn_list_t *p;
syslog(LOG_DEBUG, "Connection list:");
for(p = conn_list; p != NULL; p = p->next)
{
syslog(LOG_DEBUG, " " IP_ADDR_S "/" IP_ADDR_S ": %04x (%d|%d)",
IP_ADDR_V(p->vpn_ip), IP_ADDR_V(p->vpn_mask), p->status,
p->socket, p->meta_socket);
}
}

35
src/netutl.h Normal file
View file

@ -0,0 +1,35 @@
/*
netutl.h -- header file for netutl.c
Copyright (C) 1998,99 Ivo Timmermans <zarq@iname.com>
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __TINC_NETUTL_H__
#define __TINC_NETUTL_H__
#include "net.h"
extern conn_list_t *lookup_conn(ip_t);
extern void free_conn_element(conn_list_t *);
extern void free_conn_list(conn_list_t*);
extern void prune_conn_list(void);
extern conn_list_t *new_conn_list(void);
extern void destroy_conn_list(void);
extern char *hostlookup(unsigned long);
extern ip_mask_t *strtoip(char*);
extern void dump_conn_list(void);
#endif /* __TINC_NETUTL_H__ */

739
src/protocol.c Normal file
View file

@ -0,0 +1,739 @@
/*
protocol.c -- handle the meta-protocol
Copyright (C) 1999 Ivo Timmermans <zarq@iname.com>
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "config.h"
#include <stdlib.h>
#include <string.h>
#include <syslog.h>
#include <sys/socket.h>
#include <utils.h>
#include <xalloc.h>
#include "conf.h"
#include "encr.h"
#include "net.h"
#include "netutl.h"
#include "protocol.h"
int send_ack(conn_list_t *cl)
{
unsigned char tmp = ACK;
if(debug_lvl > 2)
syslog(LOG_DEBUG, "Send ACK to %s", cl->hostname);
syslog(LOG_NOTICE, "Connection with %s activated.", cl->hostname);
if((send(cl->meta_socket, &tmp, sizeof(tmp), 0)) < 0)
{
syslog(LOG_ERR, "send failed: %d:%d: %m", __FILE__, __LINE__);
return -1;
}
return 0;
}
int send_termreq(conn_list_t *cl)
{
termreq_t tmp;
tmp.type = TERMREQ;
tmp.vpn_ip = myself->vpn_ip;
if(debug_lvl > 2)
syslog(LOG_DEBUG, "Send TERMREQ(" IP_ADDR_S ") to " IP_ADDR_S, IP_ADDR_V(tmp.vpn_ip),
IP_ADDR_V(cl->vpn_ip));
if((send(cl->meta_socket, &tmp, sizeof(tmp), 0)) < 0)
{
syslog(LOG_ERR, "send failed: %s:%d: %m", __FILE__, __LINE__);
return -1;
}
return 0;
}
int send_timeout(conn_list_t *cl)
{
termreq_t tmp;
tmp.type = PINGTIMEOUT;
tmp.vpn_ip = myself->vpn_ip;
if(debug_lvl > 2)
syslog(LOG_DEBUG, "Send TIMEOUT(" IP_ADDR_S ") to " IP_ADDR_S, IP_ADDR_V(tmp.vpn_ip),
IP_ADDR_V(cl->vpn_ip));
if((send(cl->meta_socket, &tmp, sizeof(tmp), 0)) < 0)
{
syslog(LOG_ERR, "send failed: %s:%d: %m", __FILE__, __LINE__);
return -1;
}
return 0;
}
int send_del_host(conn_list_t *cl, conn_list_t *new_host)
{
del_host_t tmp;
tmp.type = DEL_HOST;
tmp.vpn_ip = new_host->vpn_ip;
if(debug_lvl > 2)
syslog(LOG_DEBUG, "Sending delete host %lx to " IP_ADDR_S,
tmp.vpn_ip, IP_ADDR_V(cl->vpn_ip));
if((send(cl->meta_socket, (unsigned char*)&tmp, sizeof(del_host_t), 0)) < 0)
{
syslog(LOG_ERR, "send failed: %s:%d: %m", __FILE__, __LINE__);
return -1;
}
return 0;
}
int send_ping(conn_list_t *cl)
{
unsigned char tmp = PING;
if(debug_lvl > 3)
syslog(LOG_DEBUG, "pinging " IP_ADDR_S, IP_ADDR_V(cl->vpn_ip));
if((send(cl->meta_socket, &tmp, sizeof(tmp), 0)) < 0)
{
syslog(LOG_ERR, "send failed: %s:%d: %m", __FILE__, __LINE__);
return -1;
}
return 0;
}
int send_pong(conn_list_t *cl)
{
unsigned char tmp = PONG;
if((send(cl->meta_socket, &tmp, sizeof(tmp), 0)) < 0)
{
syslog(LOG_ERR, "send failed: %s:%d: %m", __FILE__, __LINE__);
return -1;
}
return 0;
}
int send_add_host(conn_list_t *cl, conn_list_t *new_host)
{
add_host_t tmp;
tmp.type = ADD_HOST;
tmp.real_ip = new_host->real_ip;
tmp.vpn_ip = new_host->vpn_ip;
tmp.vpn_mask = new_host->vpn_mask;
tmp.portnr = new_host->port;
if(debug_lvl > 2)
syslog(LOG_DEBUG, "Sending add host (%lx/%lx %lx:%hd) to " IP_ADDR_S,
tmp.vpn_ip, tmp.vpn_mask, tmp.real_ip, tmp.portnr,
IP_ADDR_V(cl->vpn_ip));
if((send(cl->meta_socket, (unsigned char*)&tmp, sizeof(add_host_t), 0)) < 0)
{
syslog(LOG_ERR, "send failed: %s:%d: %m", __FILE__, __LINE__);
return -1;
}
return 0;
}
int send_key_changed(conn_list_t *cl, conn_list_t *src)
{
key_changed_t tmp;
tmp.type = KEY_CHANGED;
tmp.from = src->vpn_ip;
if(debug_lvl > 2)
syslog(LOG_DEBUG, "Sending KEY_CHANGED (%lx) to " IP_ADDR_S,
tmp.from, IP_ADDR_V(cl->vpn_ip));
if((send(cl->meta_socket, (unsigned char*)&tmp, sizeof(key_changed_t), 0)) < 0)
{
syslog(LOG_ERR, "send failed: %s:%d: %m", __FILE__, __LINE__);
return -1;
}
return 0;
}
void send_key_changed2(void)
{
conn_list_t *p;
for(p = conn_list; p != NULL; p = p->next)
if(p->status.meta && p->protocol_version > PROT_3)
send_key_changed(p, myself);
}
int send_basic_info(conn_list_t *cl)
{
basic_info_t tmp;
tmp.type = BASIC_INFO;
tmp.protocol = PROT_CURRENT;
tmp.portnr = myself->port;
tmp.vpn_ip = myself->vpn_ip;
tmp.vpn_mask = myself->vpn_mask;
if(debug_lvl > 2)
syslog(LOG_DEBUG, "Send BASIC_INFO(%d,%hd," IP_ADDR_S "," IP_ADDR_S ") to " IP_ADDR_S,
tmp.protocol, tmp.portnr, IP_ADDR_V(tmp.vpn_ip), IP_ADDR_V(tmp.vpn_mask),
IP_ADDR_V(cl->real_ip));
if((send(cl->meta_socket, &tmp, sizeof(tmp), 0)) < 0)
{
syslog(LOG_ERR, "send failed: %s:%d: %m", __FILE__, __LINE__);
return -1;
}
return 0;
}
int send_passphrase(conn_list_t *cl)
{
passphrase_t tmp;
tmp.type = PASSPHRASE;
encrypt_passphrase(&tmp);
if(debug_lvl > 2)
syslog(LOG_DEBUG, "Send PASSPHRASE(%hd,...) to " IP_ADDR_S, tmp.len,
IP_ADDR_V(cl->vpn_ip));
if((send(cl->meta_socket, &tmp, tmp.len+3, 0)) < 0)
{
syslog(LOG_ERR, "send failed: %s:%d: %m", __FILE__, __LINE__);
return -1;
}
return 0;
}
int send_public_key(conn_list_t *cl)
{
public_key_t *tmp;
tmp = (public_key_t*)xmalloc(strlen(my_public_key_base36)+sizeof(public_key_t));
tmp->type = PUBLIC_KEY;
tmp->len = strlen(my_public_key_base36);
strcpy(&tmp->key, my_public_key_base36);
if(debug_lvl > 2)
syslog(LOG_DEBUG, "Send PUBLIC_KEY(%hd,%s) to " IP_ADDR_S, tmp->len, &tmp->key,
IP_ADDR_V(cl->vpn_ip));
if((send(cl->meta_socket, tmp, tmp->len+sizeof(public_key_t), 0)) < 0)
{
syslog(LOG_ERR, "send failed: %s:%d: %m", __FILE__, __LINE__);
return -1;
}
return 0;
}
int send_calculate(conn_list_t *cl, char *k)
{
calculate_t *tmp;
tmp = xmalloc(strlen(k)+sizeof(calculate_t));
tmp->type = CALCULATE;
tmp->len = strlen(k);
strcpy(&tmp->key, k);
if(send(cl->meta_socket, tmp, tmp->len+4, 0) < 0)
{
syslog(LOG_ERR, "send failed: %s:%d: %m", __FILE__, __LINE__);
return -1;
}
return 0;
}
int send_key_request(ip_t to)
{
key_req_t *tmp;
conn_list_t *fw;
tmp = xmalloc(sizeof(key_req_t));
tmp->type = REQ_KEY;
tmp->to = to;
tmp->from = myself->vpn_ip;
tmp->len = 0;
fw = lookup_conn(to);
if(!fw)
{
syslog(LOG_ERR, "Attempting to send key request to " IP_ADDR_S ", which does not exist?",
IP_ADDR_V(to));
return -1;
}
if(debug_lvl > 2)
syslog(LOG_DEBUG, "Sending out request for public key to " IP_ADDR_S,
IP_ADDR_V(fw->nexthop->vpn_ip));
if(send(fw->nexthop->meta_socket, tmp, sizeof(key_req_t), 0) < 0)
{
syslog(LOG_ERR, "send failed: %s:%d: %m", __FILE__, __LINE__);
return -1;
}
fw->status.waitingforkey = 1;
return 0;
}
int send_key_answer(conn_list_t *cl, ip_t to)
{
key_req_t *tmp;
conn_list_t *fw;
tmp = xmalloc(sizeof(key_req_t)+strlen(my_public_key_base36));
tmp->type = ANS_KEY;
tmp->to = to;
tmp->from = myself->vpn_ip;
tmp->expiry = my_key_expiry;
tmp->len = strlen(my_public_key_base36);
strcpy(&tmp->key, my_public_key_base36);
fw = lookup_conn(to);
if(debug_lvl > 2)
syslog(LOG_DEBUG, "Sending public key to " IP_ADDR_S,
IP_ADDR_V(fw->nexthop->vpn_ip));
if(send(fw->nexthop->meta_socket, tmp, sizeof(key_req_t)+tmp->len, 0) < 0)
{
syslog(LOG_ERR, "send failed: %s:%d: %m", __FILE__, __LINE__);
return -1;
}
return 0;
}
/*
notify all my direct connections of a new host
that was added to the vpn, with the exception
of the source of the announcement.
*/
int notify_others(conn_list_t *new, conn_list_t *source,
int (*function)(conn_list_t*, conn_list_t*))
{
conn_list_t *p;
for(p = conn_list; p != NULL; p = p->next)
if(p != new && p != source && p->status.meta && p->protocol_version > PROT_3)
function(p, new);
return 0;
}
/*
notify one connection of everything
i have connected
*/
int notify_one(conn_list_t *new)
{
conn_list_t *p;
for(p = conn_list; p != NULL; p = p->next)
if(p != new && p->protocol_version > PROT_3)
send_add_host(new, p);
return 0;
}
/*
The incoming request handlers
*/
int basic_info_h(conn_list_t *cl, unsigned char *d, int len)
{
basic_info_t *tmp = (basic_info_t*)d;
cl->protocol_version = tmp->protocol;
cl->port = tmp->portnr;
cl->vpn_ip = tmp->vpn_ip;
cl->vpn_mask = tmp->vpn_mask;
if(cl->protocol_version < PROT_CURRENT)
{
syslog(LOG_ERR, "Peer uses protocol version %d which is too old.",
cl->protocol_version);
return -1;
}
if(debug_lvl > 2)
syslog(LOG_DEBUG, "got BASIC_INFO(%hd," IP_ADDR_S "," IP_ADDR_S ")", cl->port,
IP_ADDR_V(cl->vpn_ip), IP_ADDR_V(cl->vpn_mask));
if(debug_lvl > 1)
syslog(LOG_DEBUG, "Peer uses protocol version %d",
cl->protocol_version);
if(cl->status.outgoing)
{
if(setup_vpn_connection(cl) < 0)
return -1;
send_basic_info(cl);
}
else
{
if(setup_vpn_connection(cl) < 0)
return -1;
send_passphrase(cl);
}
cl->status.active = 0;
return 0;
}
int passphrase_h(conn_list_t *cl, unsigned char *d, int len)
{
passphrase_t *tmp = (passphrase_t*)d;
cl->pp = xmalloc(tmp->len+3);
memcpy(cl->pp, tmp, tmp->len+3);
if(debug_lvl > 2)
syslog(LOG_DEBUG, "got PASSPHRASE(%hd,...)", cl->pp->len);
if(cl->status.outgoing)
send_passphrase(cl);
else
send_public_key(cl);
return 0;
}
int public_key_h(conn_list_t *cl, unsigned char *d, int len)
{
char *g_n;
public_key_t *tmp = (public_key_t*)d;
if(debug_lvl > 2)
syslog(LOG_DEBUG, "got PUBLIC_KEY(%hd,%s)", tmp->len, &tmp->key);
g_n = xmalloc(tmp->len+1);
strcpy(g_n, &tmp->key);
if(verify_passphrase(cl, g_n))
{
/* intruder! */
syslog(LOG_ERR, "Intruder: passphrase does not match.");
return -1;
}
if(debug_lvl > 2)
syslog(LOG_INFO, "Passphrase OK");
if(cl->status.outgoing)
send_public_key(cl);
else
send_ack(cl);
cl->status.active = 1;
notify_others(cl, NULL, send_add_host);
notify_one(cl);
return 0;
}
int ack_h(conn_list_t *cl, unsigned char *d, int len)
{
if(debug_lvl > 2)
syslog(LOG_DEBUG, "got ACK");
cl->status.active = 1;
syslog(LOG_NOTICE, "Connection with %s activated.", cl->hostname);
/*
Now I'm going to cheat. The meta protocol is actually
a stream of requests, that may come in in the same TCP
packet. This is the only place that it will happen,
though.
I may change it in the future, if it appears that this
is not retainable.
*/
if(len > 1) /* An ADD_HOST follows */
{
if(request_handlers[d[1]] == NULL)
syslog(LOG_ERR, "Unknown request %d.", d[1]);
if(request_handlers[d[1]](cl, d, len - 1) < 0)
return -1;
}
return 0;
}
int termreq_h(conn_list_t *cl, unsigned char *d, int len)
{
syslog(LOG_NOTICE, IP_ADDR_S " wants to quit", IP_ADDR_V(cl->vpn_ip));
cl->status.termreq = 1;
terminate_connection(cl);
notify_others(cl, NULL, send_del_host);
return 0;
}
int timeout_h(conn_list_t *cl, unsigned char *d, int len)
{
syslog(LOG_NOTICE, IP_ADDR_S " says it's gotten a timeout from us", IP_ADDR_V(cl->vpn_ip));
cl->status.termreq = 1;
terminate_connection(cl);
return 0;
}
int del_host_h(conn_list_t *cl, unsigned char *d, int len)
{
del_host_t *tmp = (del_host_t*)d;
conn_list_t *fw;
if(debug_lvl > 2)
syslog(LOG_DEBUG, "got DEL_HOST for " IP_ADDR_S,
IP_ADDR_V(tmp->vpn_ip));
if(!(fw = lookup_conn(tmp->vpn_ip)))
{
syslog(LOG_ERR, "Somebody wanted to delete " IP_ADDR_S " which does not exist?",
IP_ADDR_V(tmp->vpn_ip));
return 0;
}
notify_others(cl, fw, send_del_host);
fw->status.termreq = 1;
terminate_connection(fw);
return 0;
}
int ping_h(conn_list_t *cl, unsigned char *d, int len)
{
if(debug_lvl > 3)
syslog(LOG_DEBUG, "responding to ping from " IP_ADDR_S, IP_ADDR_V(cl->vpn_ip));
cl->status.pinged = 0;
cl->status.got_pong = 1;
send_pong(cl);
return 0;
}
int pong_h(conn_list_t *cl, unsigned char *d, int len)
{
if(debug_lvl > 3)
syslog(LOG_DEBUG, "ok, got pong from " IP_ADDR_S, IP_ADDR_V(cl->vpn_ip));
cl->status.got_pong = 1;
return 0;
}
int add_host_h(conn_list_t *cl, unsigned char *d, int len)
{
add_host_t *tmp = (add_host_t*)d;
conn_list_t *ncn, *fw;
if(debug_lvl > 2)
syslog(LOG_DEBUG, "Add host request from " IP_ADDR_S, IP_ADDR_V(cl->vpn_ip));
if(debug_lvl > 3)
syslog(LOG_DEBUG, "got ADD_HOST(" IP_ADDR_S "," IP_ADDR_S ",%hd)",
IP_ADDR_V(tmp->vpn_ip), IP_ADDR_V(tmp->vpn_mask), tmp->portnr);
/*
Suggestion of Hans Bayle
*/
if((fw = lookup_conn(tmp->vpn_ip)))
{
notify_others(fw, cl, send_add_host);
return 0;
}
ncn = new_conn_list();
ncn->real_ip = tmp->real_ip;
ncn->vpn_ip = tmp->vpn_ip;
ncn->vpn_mask = tmp->vpn_mask;
ncn->port = tmp->portnr;
ncn->hostname = hostlookup(tmp->real_ip);
ncn->nexthop = cl;
ncn->next = conn_list;
conn_list = ncn;
ncn->status.active = 1;
notify_others(ncn, cl, send_add_host);
/*
again, i'm cheating here. see the comment in ack_h.
*/
if(len > sizeof(add_host_t)) /* Another ADD_HOST follows */
{
if(request_handlers[d[sizeof(add_host_t)]] == NULL)
syslog(LOG_ERR, "Unknown request %d.", d[sizeof(add_host_t)]);
if(request_handlers[d[sizeof(add_host_t)]](cl, d, len - sizeof(add_host_t)) < 0)
return -1;
}
return 0;
}
int req_key_h(conn_list_t *cl, unsigned char *d, int len)
{
key_req_t *tmp = (key_req_t*)d;
conn_list_t *fw;
if(debug_lvl > 2)
syslog(LOG_DEBUG, "got REQ_KEY from " IP_ADDR_S " for " IP_ADDR_S,
IP_ADDR_V(tmp->from), IP_ADDR_V(tmp->to));
if((tmp->to & myself->vpn_mask) == (myself->vpn_ip & myself->vpn_mask))
{ /* hey! they want something from ME! :) */
send_key_answer(cl, tmp->from);
return 0;
}
fw = lookup_conn(tmp->to);
if(debug_lvl > 3)
syslog(LOG_DEBUG, "Forwarding request for public key to " IP_ADDR_S,
IP_ADDR_V(fw->nexthop->vpn_ip));
if(send(fw->nexthop->meta_socket, tmp, sizeof(key_req_t), 0) < 0)
{
syslog(LOG_ERR, "send failed: %s:%d: %m", __FILE__, __LINE__);
return -1;
}
return 0;
}
void set_keys(conn_list_t *cl, key_req_t *k)
{
char *ek;
if(!cl->public_key)
{
cl->public_key = xmalloc(sizeof(enc_key_t));
cl->public_key->key = NULL;
}
if(cl->public_key->key)
free(cl->public_key->key);
cl->public_key->length = k->len;
cl->public_key->expiry = k->expiry;
cl->public_key->key = xmalloc(k->len + 1);
strcpy(cl->public_key->key, &(k->key));
ek = make_shared_key(&(k->key));
if(!cl->key)
{
cl->key = xmalloc(sizeof(enc_key_t));
cl->key->key = NULL;
}
if(cl->key->key)
free(cl->key->key);
cl->key->length = strlen(ek);
cl->key->expiry = k->expiry;
cl->key->key = xmalloc(strlen(ek) + 1);
strcpy(cl->key->key, ek);
}
int ans_key_h(conn_list_t *cl, unsigned char *d, int len)
{
key_req_t *tmp = (key_req_t*)d;
conn_list_t *fw, *gk;
if(debug_lvl > 3)
syslog(LOG_DEBUG, "got ANS_KEY from " IP_ADDR_S " for " IP_ADDR_S,
IP_ADDR_V(tmp->from), IP_ADDR_V(tmp->to));
if(tmp->to == myself->vpn_ip)
{ /* hey! that key's for ME! :) */
if(debug_lvl > 2)
syslog(LOG_DEBUG, "Yeah! key arrived. Now do something with it.");
gk = lookup_conn(tmp->from);
set_keys(gk, tmp);
gk->status.validkey = 1;
gk->status.waitingforkey = 0;
flush_queues(gk);
return 0;
}
fw = lookup_conn(tmp->to);
if(debug_lvl > 2)
syslog(LOG_DEBUG, "Forwarding public key to " IP_ADDR_S,
IP_ADDR_V(fw->nexthop->vpn_ip));
if(send(fw->nexthop->meta_socket, tmp, sizeof(key_req_t)+tmp->len, 0) < 0)
{
syslog(LOG_ERR, "send failed: %s:%d: %m", __FILE__, __LINE__);
return -1;
}
return 0;
}
int key_changed_h(conn_list_t *cl, unsigned char *d, int len)
{
key_changed_t *tmp = (key_changed_t*)d;
conn_list_t *ik;
if(debug_lvl > 2)
syslog(LOG_DEBUG, "got KEY_CHANGED from " IP_ADDR_S,
IP_ADDR_V(tmp->from));
ik = lookup_conn(tmp->from);
ik->status.validkey = 0;
ik->status.waitingforkey = 0;
if(debug_lvl > 3)
syslog(LOG_DEBUG, "Forwarding key invalidation request");
notify_others(cl, ik, send_key_changed);
return 0;
}
int (*request_handlers[256])(conn_list_t*, unsigned char*, int) = {
0, ack_h, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
termreq_h, timeout_h, del_host_h, 0, 0, 0, 0, 0, 0, 0,
ping_h, pong_h, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
add_host_h, basic_info_h, passphrase_h, public_key_h, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
req_key_h, ans_key_h, key_changed_h, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};

124
src/protocol.h Normal file
View file

@ -0,0 +1,124 @@
/*
protocol.h -- header for protocol.c
Copyright (C) 1999 Ivo Timmermans <zarq@iname.com>
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#ifndef __TINC_PROTOCOL_H__
#define __TINC_PROTOCOL_H__
#include "net.h"
enum {
PROT_RESERVED = 0, /* reserved: do not use. */
PROT_NOT_IN_USE,
PROT_TOO_OLD = 2,
PROT_3,
PROT_CURRENT, /* protocol currently in use */
};
enum {
ACK = 1, /* acknowledged */
AUTH_S_INIT = 10, /* initiate authentication */
AUTH_C_INIT,
AUTH_S_SPP, /* send passphrase */
AUTH_C_SPP,
AUTH_S_SKEY, /* send g^k */
AUTH_C_SKEY,
AUTH_S_SACK, /* send ack */
AUTH_C_RACK, /* waiting for ack */
TERMREQ = 30, /* terminate connection */
PINGTIMEOUT, /* terminate due to ping t.o. */
DEL_HOST, /* forward a termreq to others */
PING = 40, /* ping */
PONG,
ADD_HOST = 60, /* Add new given host to connection list */
BASIC_INFO, /* some basic info follows */
PASSPHRASE, /* encrypted passphrase */
PUBLIC_KEY, /* public key in base-36 */
HOLD = 80, /* don't send any data */
RESUME, /* resume dataflow with new encryption key */
CALCULATE = 100, /* calculate the following numer^privkey and send me the result */
CALC_RES, /* result of the above */
ALMOST_KEY, /* this number^privkey is the shared key */
REQ_KEY = 160, /* request public key */
ANS_KEY, /* answer to such request */
KEY_CHANGED, /* public key has changed */
};
typedef struct add_host_t {
unsigned char type;
ip_t real_ip;
ip_t vpn_ip;
ip_t vpn_mask;
unsigned short portnr;
} add_host_t;
typedef struct termreq_t {
unsigned char type;
ip_t vpn_ip;
} termreq_t;
typedef struct basic_info_t {
unsigned char type;
unsigned char protocol;
unsigned short portnr;
ip_t vpn_ip;
ip_t vpn_mask;
} basic_info_t;
typedef struct calculate_t {
unsigned char type;
unsigned short len;
char key;
} calculate_t;
typedef struct public_key_t {
unsigned char type;
unsigned short len;
char key;
} public_key_t;
typedef struct key_req_t {
unsigned char type;
ip_t from;
ip_t to;
time_t expiry;
short int len; /* 0 if requesting */
char key;
} key_req_t;
typedef struct key_changed_t {
unsigned char type;
ip_t from;
} key_changed_t;
typedef struct del_host_t {
unsigned char type;
ip_t vpn_ip;
} del_host_t;
extern int (*request_handlers[256])(conn_list_t*, unsigned char*, int);
extern int send_ping(conn_list_t*);
extern int send_basic_info(conn_list_t *);
extern int send_termreq(conn_list_t *);
extern int send_timeout(conn_list_t *);
extern int send_key_request(ip_t);
extern void send_key_changed2(void);
#endif /* __TINC_PROTOCOL_H__ */

468
src/tincd.c Normal file
View file

@ -0,0 +1,468 @@
/*
tincd.c -- the main file for tincd
Copyright (C) 1998,99 Ivo Timmermans <zarq@iname.com>
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.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include "config.h"
#include <errno.h>
#include <fcntl.h>
#include <getopt.h>
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
#include <syslog.h>
#include <unistd.h>
#ifdef HAVE_SYS_IOCTL_H
# include <sys/ioctl.h>
#endif
#include <pidfile.h>
#include <utils.h>
#include <xalloc.h>
#include "conf.h"
#include "encr.h"
#include "net.h"
#include "netutl.h"
/* The name this program was run with. */
char *program_name;
/* If nonzero, display usage information and exit. */
static int show_help;
/* If nonzero, print the version on standard output and exit. */
static int show_version;
/* If nonzero, it will attempt to kill a running tincd and exit. */
static int kill_tincd = 0;
char *confbase = NULL; /* directory in which all config files are */
char *configfilename = NULL; /* configuration file name */
char *identname; /* program name for syslog */
char *netname = NULL; /* name of the vpn network */
char *pidfilename; /* pid file location */
static pid_t ppid; /* pid of non-detached part */
char **g_argv; /* a copy of the cmdline arguments */
void cleanup_and_exit(int);
int detach(void);
int kill_other(void);
void make_names(void);
RETSIGTYPE parent_exit(int a);
void setup_signals(void);
int write_pidfile(void);
static struct option const long_options[] =
{
{ "kill", no_argument, NULL, 'k' },
{ "net", required_argument, NULL, 'n' },
{ "timeout", required_argument, NULL, 'p' },
{ "help", no_argument, &show_help, 1 },
{ "version", no_argument, &show_version, 1 },
{ NULL, 0, NULL, 0 }
};
static void
usage(int status)
{
if(status != 0)
fprintf(stderr, "Try `%s --help\' for more information.\n", program_name);
else
{
printf("Usage: %s [option]...\n\n", program_name);
printf(" -c, --config=FILE Read configuration options from FILE.\n"
" -d Increase debug level.\n"
" -k, --kill Attempt to kill a running tincd and exit.\n"
" -n, --net=NETNAME Connect to net NETNAME.\n"
" -t, --timeout=TIMEOUT Seconds to wait before giving a timeout.\n");
printf(" --help Display this help and exit.\n"
" --version Output version information and exit.\n\n");
printf("Report bugs to zarq@iname.com.\n");
}
exit(status);
}
void
parse_options(int argc, char **argv, char **envp)
{
int r;
int option_index = 0;
config_t *p;
while((r = getopt_long(argc, argv, "c:dkn:t:", long_options, &option_index)) != EOF)
{
switch(r)
{
case 0: /* long option */
break;
case 'c': /* config file */
configfilename = xmalloc(strlen(optarg)+1);
strcpy(configfilename, optarg);
break;
case 'd': /* inc debug level */
debug_lvl++;
break;
case 'k': /* kill old tincds */
kill_tincd = 1;
break;
case 'n': /* net name given */
netname = xmalloc(strlen(optarg)+1);
strcpy(netname, optarg);
break;
case 't': /* timeout */
if(!(p = add_config_val(&config, TYPE_INT, optarg)))
{
printf("Invalid timeout value `%s'.\n", optarg);
usage(1);
}
break;
case '?':
usage(1);
default:
break;
}
}
}
void memory_full(void)
{
syslog(LOG_ERR, "Memory exhausted; exiting.");
exit(1);
}
/*
Detach from current terminal, write pidfile, kill parent
*/
int detach(void)
{
int fd;
pid_t pid;
ppid = getpid();
if((pid = fork()) < 0)
{
perror("fork");
return -1;
}
if(pid) /* parent process */
{
signal(SIGTERM, parent_exit);
sleep(600); /* wait 10 minutes */
exit(1);
}
if(write_pidfile())
return -1;
if((fd = open("/dev/tty", O_RDWR)) >= 0)
{
if(ioctl(fd, TIOCNOTTY, NULL))
{
perror("ioctl");
return -1;
}
close(fd);
}
kill(ppid, SIGTERM);
if(setsid() < 0)
return -1;
chdir("/"); /* avoid keeping a mointpoint busy */
openlog(identname, LOG_CONS | LOG_PID, LOG_DAEMON);
if(debug_lvl > 1)
syslog(LOG_NOTICE, "tincd %s (%s %s) starting, debug level %d.",
VERSION, __DATE__, __TIME__, debug_lvl);
else
syslog(LOG_NOTICE, "tincd %s starting, debug level %d.", VERSION, debug_lvl);
xalloc_fail_func = memory_full;
return 0;
}
/*
Close network connections, and terminate neatly
*/
void cleanup_and_exit(int c)
{
close_network_connections();
if(debug_lvl > 0)
syslog(LOG_INFO, "Total bytes written: tap %d, socket %d; bytes read: tap %d, socket %d.",
total_tap_out, total_socket_out, total_tap_in, total_socket_in);
closelog();
kill(ppid, SIGTERM);
exit(c);
}
/*
check for an existing tinc for this net, and write pid to pidfile
*/
int write_pidfile(void)
{
int pid;
if((pid = check_pid(pidfilename)))
{
if(netname)
fprintf(stderr, "A tincd is already running for net `%s' with pid %d.\n",
netname, pid);
else
fprintf(stderr, "A tincd is already running with pid %d.\n", pid);
return 1;
}
/* if it's locked, write-protected, or whatever */
if(!write_pid(pidfilename))
return 1;
return 0;
}
/*
kill older tincd for this net
*/
int kill_other(void)
{
int pid;
if(!(pid = read_pid(pidfilename)))
{
if(netname)
fprintf(stderr, "No other tincd is running for net `%s'.\n", netname);
else
fprintf(stderr, "No other tincd is running.\n");
return 1;
}
errno = 0; /* No error, sometimes errno is only changed on error */
/* ESRCH is returned when no process with that pid is found */
if(kill(pid, SIGTERM) && errno == ESRCH)
fprintf(stderr, "Removing stale lock file.\n");
remove_pid(pidfilename);
return 0;
}
/*
Set all files and paths according to netname
*/
void make_names(void)
{
if(!configfilename)
{
if(netname)
{
configfilename = xmalloc(strlen(netname)+18+strlen(CONFDIR));
sprintf(configfilename, "%s/tinc/%s/tincd.conf", CONFDIR, netname);
}
else
{
configfilename = xmalloc(17+strlen(CONFDIR));
sprintf(configfilename, "%s/tinc/tincd.conf", CONFDIR);
}
}
if(netname)
{
pidfilename = xmalloc(strlen(netname)+20);
sprintf(pidfilename, "/var/run/tincd.%s.pid", netname);
confbase = xmalloc(strlen(netname)+8+strlen(CONFDIR));
sprintf(confbase, "%s/tinc/%s/", CONFDIR, netname);
identname = xmalloc(strlen(netname)+7);
sprintf(identname, "tincd.%s", netname);
}
else
{
pidfilename = "/var/run/tincd.pid";
confbase = xmalloc(7+strlen(CONFDIR));
sprintf(confbase, "%s/tinc/", CONFDIR);
identname = "tincd";
}
}
int
main(int argc, char **argv, char **envp)
{
program_name = argv[0];
parse_options(argc, argv, envp);
if(show_version)
{
printf("%s version %s\nCopyright (C) 1998,99 Ivo Timmermans and others,\n"
"see the AUTHORS file for a complete list.\n\n"
"tinc comes with ABSOLUTELY NO WARRANTY. This is free software,\n"
"and you are welcome to redistribute it under certain conditions;\n"
"see the file COPYING for details.\n\n", PACKAGE, VERSION);
printf("This product includes software developed by Eric Young (eay@mincom.oz.au)\n");
return 0;
}
if(show_help)
usage(0);
if(geteuid())
{
fprintf(stderr, "You must be root to run this program. sorry.\n");
return 1;
}
g_argv = argv;
make_names();
if(kill_tincd)
exit(kill_other());
if(read_config_file(configfilename))
return 1;
setup_signals();
if(detach())
cleanup_and_exit(1);
if(security_init())
return 1;
if(setup_network_connections())
cleanup_and_exit(1);
main_loop();
cleanup_and_exit(1);
return 1;
}
RETSIGTYPE
sigterm_handler(int a)
{
if(debug_lvl > 0)
syslog(LOG_NOTICE, "Got TERM signal");
cleanup_and_exit(0);
}
RETSIGTYPE
sigquit_handler(int a)
{
if(debug_lvl > 0)
syslog(LOG_NOTICE, "Got QUIT signal");
cleanup_and_exit(0);
}
RETSIGTYPE
sigsegv_square(int a)
{
syslog(LOG_NOTICE, "Got another SEGV signal: not restarting");
exit(0);
}
RETSIGTYPE
sigsegv_handler(int a)
{
if(cp_file)
syslog(LOG_NOTICE, "Got SEGV signal after %s line %d. Trying to re-execute.",
cp_file, cp_line);
else
syslog(LOG_NOTICE, "Got SEGV signal; trying to re-execute.");
signal(SIGSEGV, sigsegv_square);
close_network_connections();
remove_pid(pidfilename);
execvp(g_argv[0], g_argv);
}
RETSIGTYPE
sighup_handler(int a)
{
if(debug_lvl > 0)
syslog(LOG_NOTICE, "Got HUP signal");
close_network_connections();
setup_network_connections();
/* FIXME: read config-file and re-establish network connections */
}
RETSIGTYPE
sigint_handler(int a)
{
if(debug_lvl > 0)
syslog(LOG_NOTICE, "Got INT signal");
cleanup_and_exit(0);
}
RETSIGTYPE
sigusr1_handler(int a)
{
dump_conn_list();
}
RETSIGTYPE
sigusr2_handler(int a)
{
if(debug_lvl > 1)
syslog(LOG_NOTICE, "Forcing new keys");
regenerate_keys();
}
RETSIGTYPE
sighuh(int a)
{
if(cp_file)
syslog(LOG_NOTICE, "Got unexpected signal after %s line %d.",
cp_file, cp_line);
else
syslog(LOG_NOTICE, "Got unexpected signal.");
}
void
setup_signals(void)
{
int i;
for(i=0;i<32;i++)
signal(i,sighuh);
if(signal(SIGTERM, SIG_IGN) != SIG_ERR)
signal(SIGTERM, sigterm_handler);
if(signal(SIGQUIT, SIG_IGN) != SIG_ERR)
signal(SIGQUIT, sigquit_handler);
if(signal(SIGSEGV, SIG_IGN) != SIG_ERR)
signal(SIGSEGV, sigsegv_handler);
if(signal(SIGHUP, SIG_IGN) != SIG_ERR)
signal(SIGHUP, sighup_handler);
signal(SIGPIPE, SIG_IGN);
if(signal(SIGINT, SIG_IGN) != SIG_ERR)
signal(SIGINT, sigint_handler);
signal(SIGUSR1, sigusr1_handler);
signal(SIGUSR2, sigusr2_handler);
}
RETSIGTYPE parent_exit(int a)
{
exit(0);
}