Merge branch 'master' into 1.1

Conflicts:
	doc/tincd.8.in
	lib/pidfile.c
	src/graph.c
	src/net.c
	src/net.h
	src/net_packet.c
	src/net_setup.c
	src/net_socket.c
	src/netutl.c
	src/node.c
	src/node.h
	src/protocol_auth.c
	src/protocol_key.c
	src/tincd.c
This commit is contained in:
Guus Sliepen 2009-06-05 23:03:28 +02:00
commit 5a132550de
23 changed files with 534 additions and 183 deletions

View file

@ -312,27 +312,8 @@ void sssp_bfs(void) {
e->to->via = indirect ? n->via : e->to;
e->to->options = e->options;
if(sockaddrcmp(&e->to->address, &e->address)) {
node = splay_unlink(node_udp_tree, e->to);
sockaddrfree(&e->to->address);
sockaddrcpy(&e->to->address, &e->address);
if(e->to->hostname)
free(e->to->hostname);
e->to->hostname = sockaddr2hostname(&e->to->address);
if(node)
splay_insert_node(node_udp_tree, node);
if(e->to->options & OPTION_PMTU_DISCOVERY) {
e->to->mtuprobes = 0;
e->to->minmtu = 0;
e->to->maxmtu = MTU;
if(e->to->status.validkey)
send_mtu_probe(e->to);
}
}
if(e->to->address.sa.sa_family == AF_UNSPEC && e->address.sa.sa_family != AF_UNKNOWN)
update_node_udp(e->to, &e->address);
list_insert_tail(todo_list, e->to);
}
@ -364,13 +345,13 @@ void check_reachability() {
if(n->status.reachable) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Node %s (%s) became reachable"),
n->name, n->hostname);
splay_insert(node_udp_tree, n);
} else {
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Node %s (%s) became unreachable"),
n->name, n->hostname);
splay_delete(node_udp_tree, n);
}
/* TODO: only clear status.validkey if node is unreachable? */
n->status.validkey = false;
n->status.waitingforkey = false;

View file

@ -62,7 +62,8 @@ bool setup_device(void) {
if(!get_config_string(lookup_config(config_tree, "Interface"), &iface))
#ifdef HAVE_LINUX_IF_TUN_H
iface = xstrdup(netname);
if (netname != NULL)
iface = xstrdup(netname);
#else
iface = xstrdup(rindex(device, '/') ? rindex(device, '/') + 1 : device);
#endif

View file

@ -131,7 +131,7 @@ extern int setup_vpn_in_socket(const sockaddr_t *);
extern void send_packet(const struct node_t *, vpn_packet_t *);
extern void receive_tcppacket(struct connection_t *, char *, int);
extern void broadcast_packet(const struct node_t *, vpn_packet_t *);
extern bool setup_network_connections(void);
extern bool setup_network(void);
extern void setup_outgoing_connection(struct outgoing_t *);
extern void try_outgoing_connections(void);
extern void close_network_connections(void);

View file

@ -49,6 +49,7 @@
#endif
int keylifetime = 0;
int keyexpires = 0;
static char lzo_wrkmem[LZO1X_999_MEM_COMPRESS > LZO1X_1_MEM_COMPRESS ? LZO1X_999_MEM_COMPRESS : LZO1X_1_MEM_COMPRESS];
static void send_udppacket(node_t *, vpn_packet_t *);
@ -99,15 +100,15 @@ void send_mtu_probe(node_t *n) {
send_mtu_probe_handler(0, 0, n);
}
void mtu_probe_h(node_t *n, vpn_packet_t *packet) {
void mtu_probe_h(node_t *n, vpn_packet_t *packet, length_t len) {
ifdebug(TRAFFIC) logger(LOG_INFO, _("Got MTU probe length %d from %s (%s)"), packet->len, n->name, n->hostname);
if(!packet->data[0]) {
packet->data[0] = 1;
send_packet(n, packet);
} else {
if(n->minmtu < packet->len)
n->minmtu = packet->len;
if(n->minmtu < len)
n->minmtu = len;
}
}
@ -160,7 +161,16 @@ static void receive_packet(node_t *n, vpn_packet_t *packet) {
route(n, packet);
}
static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
static bool try_mac(node_t *n, const vpn_packet_t *inpkt)
{
if(!digest_active(&n->indigest) || !n->inmaclength || inpkt->len < sizeof inpkt->seqno + n->inmaclength)
return false;
return digest_verify(&n->indigest, &inpkt->seqno, inpkt->len, &inpkt->seqno + inpkt->len);
}
static void receive_udppacket(node_t *n, vpn_packet_t *inpkt)
{
vpn_packet_t pkt1, pkt2;
vpn_packet_t *pkt[] = { &pkt1, &pkt2, &pkt1, &pkt2 };
int nextpkt = 0;
@ -170,9 +180,15 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
cp();
if(!cipher_active(&n->incipher)) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got packet from %s (%s) but he hasn't got our key yet"),
n->name, n->hostname);
return;
}
/* Check packet length */
if(inpkt->len < sizeof inpkt->seqno + digest_length(&myself->digest)) {
if(inpkt->len < sizeof inpkt->seqno + digest_length(&n->indigest)) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got too short packet from %s (%s)"),
n->name, n->hostname);
return;
@ -180,18 +196,18 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
/* Check the message authentication code */
if(digest_active(&myself->digest) && !digest_verify(&myself->digest, &inpkt->seqno, inpkt->len, &inpkt->seqno + inpkt->len)) {
if(digest_active(&n->indigest) && !digest_verify(&n->indigest, &inpkt->seqno, inpkt->len, &inpkt->seqno + inpkt->len)) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got unauthenticated packet from %s (%s)"), n->name, n->hostname);
return;
}
/* Decrypt the packet */
if(cipher_active(&myself->cipher)) {
if(cipher_active(&n->incipher)) {
outpkt = pkt[nextpkt++];
outlen = MAXSIZE;
if(!cipher_decrypt(&myself->cipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
if(!cipher_decrypt(&n->incipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Error decrypting packet from %s (%s)"), n->name, n->hostname);
return;
}
@ -233,22 +249,26 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
/* Decompress the packet */
if(myself->compression) {
length_t origlen = inpkt->len;
if(n->incompression) {
outpkt = pkt[nextpkt++];
if((outpkt->len = uncompress_packet(outpkt->data, inpkt->data, inpkt->len, myself->compression)) < 0) {
if((outpkt->len = uncompress_packet(outpkt->data, inpkt->data, inpkt->len, n->incompression)) < 0) {
ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while uncompressing packet from %s (%s)"),
n->name, n->hostname);
return;
}
inpkt = outpkt;
origlen -= MTU/64 + 20;
}
inpkt->priority = 0;
if(!inpkt->data[12] && !inpkt->data[13])
mtu_probe_h(n, inpkt);
mtu_probe_h(n, inpkt, origlen);
else
receive_packet(n, inpkt);
}
@ -290,7 +310,7 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
n->name, n->hostname);
if(!n->status.waitingforkey)
send_req_key(n->nexthop->connection, myself, n);
send_req_key(n);
n->status.waitingforkey = true;
@ -299,12 +319,14 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
return;
}
if(!n->minmtu && (inpkt->data[12] | inpkt->data[13])) {
if(n->options & OPTION_PMTU_DISCOVERY && !n->minmtu && (inpkt->data[12] | inpkt->data[13])) {
ifdebug(TRAFFIC) logger(LOG_INFO,
_("No minimum MTU established yet for %s (%s), forwarding via TCP"),
n->name, n->hostname);
send_tcppacket(n->nexthop->connection, origpkt);
return;
}
origlen = inpkt->len;
@ -312,10 +334,10 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
/* Compress the packet */
if(n->compression) {
if(n->outcompression) {
outpkt = pkt[nextpkt++];
if((outpkt->len = compress_packet(outpkt->data, inpkt->data, inpkt->len, n->compression)) < 0) {
if((outpkt->len = compress_packet(outpkt->data, inpkt->data, inpkt->len, n->outcompression)) < 0) {
ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while compressing packet to %s (%s)"),
n->name, n->hostname);
return;
@ -331,11 +353,11 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
/* Encrypt the packet */
if(cipher_active(&n->cipher)) {
if(cipher_active(&n->outcipher)) {
outpkt = pkt[nextpkt++];
outlen = MAXSIZE;
if(!cipher_encrypt(&n->cipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
if(!cipher_encrypt(&n->outcipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while encrypting packet to %s (%s)"), n->name, n->hostname);
goto end;
}
@ -346,9 +368,9 @@ static void send_udppacket(node_t *n, vpn_packet_t *origpkt) {
/* Add the message authentication code */
if(digest_active(&n->digest)) {
digest_create(&n->digest, &inpkt->seqno, inpkt->len, &inpkt->seqno + inpkt->len);
inpkt->len += digest_length(&n->digest);
if(digest_active(&n->outdigest)) {
digest_create(&n->outdigest, &inpkt->seqno, inpkt->len, &inpkt->seqno + inpkt->len);
inpkt->len += digest_length(&n->outdigest);
}
/* Determine which socket we have to use */
@ -434,9 +456,15 @@ void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
ifdebug(TRAFFIC) logger(LOG_INFO, _("Broadcasting packet of %d bytes from %s (%s)"),
packet->len, from->name, from->hostname);
if(from != myself)
if(from != myself) {
send_packet(myself, packet);
// In TunnelServer mode, do not forward broadcast packets.
// The MST might not be valid and create loops.
if(tunnelserver)
return;
}
for(node = connection_tree->head; node; node = node->next) {
c = node->data;
@ -445,6 +473,30 @@ void broadcast_packet(const node_t *from, vpn_packet_t *packet) {
}
}
static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) {
splay_node_t *node;
edge_t *e;
node_t *n = NULL;
for(node = edge_weight_tree->head; node; node = node->next) {
e = node->data;
if(sockaddrcmp_noport(from, &e->address))
continue;
if(!n)
n = e->to;
if(!try_mac(e->to, pkt))
continue;
n = e->to;
break;
}
return n;
}
void handle_incoming_vpn_data(int sock, short events, void *data)
{
vpn_packet_t pkt;
@ -467,11 +519,17 @@ void handle_incoming_vpn_data(int sock, short events, void *data)
n = lookup_node_udp(&from);
if(!n) {
hostname = sockaddr2hostname(&from);
logger(LOG_WARNING, _("Received UDP packet from unknown source %s"),
hostname);
free(hostname);
return;
n = try_harder(&from, &pkt);
if(n)
update_node_udp(n, &from);
else ifdebug(PROTOCOL) {
hostname = sockaddr2hostname(&from);
logger(LOG_WARNING, _("Received UDP packet from unknown source %s"), hostname);
free(hostname);
return;
}
else
return;
}
receive_udppacket(n, &pkt);

View file

@ -148,14 +148,8 @@ static void keyexpire_handler(int fd, short events, void *data) {
}
void regenerate_key() {
ifdebug(STATUS) logger(LOG_INFO, _("Regenerating symmetric key"));
if(!cipher_regenerate_key(&myself->cipher, true)) {
logger(LOG_ERR, _("Error regenerating key!"));
abort();
}
if(timeout_initialized(&keyexpire_event)) {
ifdebug(STATUS) logger(LOG_INFO, _("Expiring symmetric keys"));
event_del(&keyexpire_event);
send_key_changed(broadcast, myself);
} else {
@ -270,7 +264,7 @@ bool setup_myself(void) {
#if !defined(SOL_IP) || !defined(IP_TOS)
if(priorityinheritance)
logger(LOG_WARNING, _("PriorityInheritance not supported on this platform"));
logger(LOG_WARNING, _("%s not supported on this platform"), "PriorityInheritance");
#endif
if(!get_config_int(lookup_config(config_tree, "MACExpire"), &macexpire))
@ -305,7 +299,7 @@ bool setup_myself(void) {
if(!get_config_string(lookup_config(myself->connection->config_tree, "Cipher"), &cipher))
cipher = xstrdup("blowfish");
if(!cipher_open_by_name(&myself->cipher, cipher)) {
if(!cipher_open_by_name(&myself->incipher, cipher)) {
logger(LOG_ERR, _("Unrecognized cipher type!"));
return false;
}
@ -320,18 +314,18 @@ bool setup_myself(void) {
if(!get_config_string(lookup_config(myself->connection->config_tree, "Digest"), &digest))
digest = xstrdup("sha1");
if(!digest_open_by_name(&myself->digest, digest)) {
if(!digest_open_by_name(&myself->indigest, digest)) {
logger(LOG_ERR, _("Unrecognized digest type!"));
return false;
}
if(!get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), &myself->maclength))
if(!get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), &myself->inmaclength))
if(digest_active(&myself->digest)) {
if(myself->maclength > digest_length(&myself->digest)) {
if(digest_active(&myself->indigest)) {
if(myself->inmaclength > digest_length(&myself->indigest)) {
logger(LOG_ERR, _("MAC length exceeds size of digest!"));
return false;
} else if(myself->maclength < 0) {
} else if(myself->inmaclength < 0) {
logger(LOG_ERR, _("Bogus MAC length!"));
return false;
}
@ -339,13 +333,13 @@ bool setup_myself(void) {
/* Compression */
if(get_config_int(lookup_config(myself->connection->config_tree, "Compression"), &myself->compression)) {
if(myself->compression < 0 || myself->compression > 11) {
if(get_config_int(lookup_config(myself->connection->config_tree, "Compression"), &myself->incompression)) {
if(myself->incompression < 0 || myself->incompression > 11) {
logger(LOG_ERR, _("Bogus compression level!"));
return false;
}
} else
myself->compression = 0;
myself->incompression = 0;
myself->connection->outcompression = 0;
@ -467,9 +461,10 @@ bool setup_myself(void) {
}
/*
setup all initial network connections
initialize network
*/
bool setup_network_connections(void) {
bool setup_network(void)
{
cp();
init_connections();
@ -496,8 +491,6 @@ bool setup_network_connections(void) {
if(!setup_myself())
return false;
try_outgoing_connections();
return true;
}

View file

@ -33,6 +33,8 @@
#include "utils.h"
#include "xalloc.h"
#include <assert.h>
#ifdef WSAEINPROGRESS
#define EINPROGRESS WSAEINPROGRESS
#endif
@ -80,7 +82,95 @@ static void configure_tcp(connection_t *c) {
#endif
}
int setup_listen_socket(const sockaddr_t *sa) {
static bool bind_to_interface(int sd) { /* {{{ */
char *iface;
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
struct ifreq ifr;
int status;
#endif /* defined(SOL_SOCKET) && defined(SO_BINDTODEVICE) */
if(!get_config_string (lookup_config (config_tree, "BindToInterface"), &iface))
return true;
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
memset(&ifr, 0, sizeof(ifr));
strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
ifr.ifr_ifrn.ifrn_name[IFNAMSIZ - 1] = 0;
status = setsockopt(sd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof(ifr));
if(status) {
logger(LOG_ERR, _("Can't bind to interface %s: %s"), iface,
strerror(errno));
return false;
}
#else /* if !defined(SOL_SOCKET) || !defined(SO_BINDTODEVICE) */
logger(LOG_WARNING, _("%s not supported on this platform"), "BindToInterface");
#endif
return true;
} /* }}} bool bind_to_interface */
static bool bind_to_address(connection_t *c) { /* {{{ */
char *node;
struct addrinfo *ai_list;
struct addrinfo *ai_ptr;
struct addrinfo ai_hints;
int status;
assert(c != NULL);
assert(c->socket >= 0);
node = NULL;
if(!get_config_string(lookup_config(config_tree, "BindToAddress"),
&node))
return true;
assert(node != NULL);
memset(&ai_hints, 0, sizeof(ai_hints));
ai_hints.ai_family = c->address.sa.sa_family;
/* We're called from `do_outgoing_connection' only. */
ai_hints.ai_socktype = SOCK_STREAM;
ai_hints.ai_protocol = IPPROTO_TCP;
ai_list = NULL;
status = getaddrinfo(node, /* service = */ NULL,
&ai_hints, &ai_list);
if(status) {
free(node);
logger(LOG_WARNING, _("Error looking up %s port %s: %s"),
node, _("any"), gai_strerror(status));
return false;
}
assert(ai_list != NULL);
status = -1;
for(ai_ptr = ai_list; ai_ptr != NULL; ai_ptr = ai_ptr->ai_next) {
status = bind(c->socket,
ai_list->ai_addr, ai_list->ai_addrlen);
if(!status)
break;
}
if(status) {
logger(LOG_ERR, _("Can't bind to %s/tcp: %s"), node,
strerror(errno));
} else ifdebug(CONNECTIONS) {
logger(LOG_DEBUG, "Successfully bound outgoing "
"TCP socket to %s", node);
}
free(node);
freeaddrinfo(ai_list);
return status ? false : true;
} /* }}} bool bind_to_address */
int setup_listen_socket(const sockaddr_t *sa)
{
int nfd;
char *addrstr;
int option;
@ -120,7 +210,7 @@ int setup_listen_socket(const sockaddr_t *sa) {
return -1;
}
#else
logger(LOG_WARNING, _("BindToInterface not supported on this platform"));
logger(LOG_WARNING, _("%s not supported on this platform"), "BindToInterface");
#endif
}
@ -202,24 +292,10 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
}
#endif
#if defined(SOL_SOCKET) && defined(SO_BINDTODEVICE)
{
char *iface;
struct ifreq ifr;
if(get_config_string(lookup_config(config_tree, "BindToInterface"), &iface)) {
memset(&ifr, 0, sizeof ifr);
strncpy(ifr.ifr_ifrn.ifrn_name, iface, IFNAMSIZ);
if(setsockopt(nfd, SOL_SOCKET, SO_BINDTODEVICE, &ifr, sizeof ifr)) {
closesocket(nfd);
logger(LOG_ERR, _("Can't bind to interface %s: %s"), iface,
strerror(errno));
return -1;
}
}
if (!bind_to_interface(nfd)) {
closesocket(nfd);
return -1;
}
#endif
if(bind(nfd, &sa->sa, SALEN(sa->sa))) {
closesocket(nfd);
@ -231,7 +307,7 @@ int setup_vpn_in_socket(const sockaddr_t *sa) {
}
return nfd;
}
} /* int setup_vpn_in_socket */
static void retry_outgoing_handler(int fd, short events, void *data) {
setup_outgoing_connection(data);
@ -329,6 +405,9 @@ begin:
setsockopt(c->socket, SOL_IPV6, IPV6_V6ONLY, &option, sizeof option);
#endif
bind_to_interface(c->socket);
bind_to_address(c);
/* Optimize TCP settings */
configure_tcp(c);

View file

@ -140,7 +140,41 @@ char *sockaddr2hostname(const sockaddr_t *sa) {
return str;
}
int sockaddrcmp(const sockaddr_t *a, const sockaddr_t *b) {
int sockaddrcmp_noport(const sockaddr_t *a, const sockaddr_t *b)
{
int result;
cp();
result = a->sa.sa_family - b->sa.sa_family;
if(result)
return result;
switch (a->sa.sa_family) {
case AF_UNSPEC:
return 0;
case AF_UNKNOWN:
return strcmp(a->unknown.address, b->unknown.address);
case AF_INET:
return memcmp(&a->in.sin_addr, &b->in.sin_addr, sizeof(a->in.sin_addr));
case AF_INET6:
return memcmp(&a->in6.sin6_addr, &b->in6.sin6_addr, sizeof(a->in6.sin6_addr));
default:
logger(LOG_ERR, _("sockaddrcmp() was called with unknown address family %d, exitting!"),
a->sa.sa_family);
cp_trace();
raise(SIGFPE);
exit(0);
}
}
int sockaddrcmp(const sockaddr_t *a, const sockaddr_t *b)
{
int result;
cp();

View file

@ -32,6 +32,7 @@ extern sockaddr_t str2sockaddr(const char *, const char *);
extern void sockaddr2str(const sockaddr_t *, char **, char **);
extern char *sockaddr2hostname(const sockaddr_t *);
extern int sockaddrcmp(const sockaddr_t *, const sockaddr_t *);
extern int sockaddrcmp_noport(const sockaddr_t *, const sockaddr_t *);
extern void sockaddrunmap(sockaddr_t *);
extern void sockaddrfree(sockaddr_t *);
extern void sockaddrcpy(sockaddr_t *, const sockaddr_t *);

View file

@ -90,8 +90,10 @@ void free_node(node_t *n) {
sockaddrfree(&n->address);
cipher_close(&n->cipher);
digest_close(&n->digest);
cipher_close(&n->incipher);
digest_close(&n->indigest);
cipher_close(&n->outcipher);
digest_close(&n->outdigest);
event_del(&n->mtuevent);
@ -130,6 +132,7 @@ void node_del(node_t *n) {
}
splay_delete(node_tree, n);
splay_delete(node_udp_tree, n);
}
node_t *lookup_node(char *name) {
@ -153,6 +156,25 @@ node_t *lookup_node_udp(const sockaddr_t *sa) {
return splay_search(node_udp_tree, &n);
}
void update_node_udp(node_t *n, const sockaddr_t *sa)
{
splay_delete(node_udp_tree, n);
if(n->hostname)
free(n->hostname);
if(sa) {
n->address = *sa;
n->hostname = sockaddr2hostname(&n->address);
splay_delete(node_udp_tree, n);
splay_insert(node_udp_tree, n);
logger(LOG_DEBUG, "UDP address of %s set to %s", n->name, n->hostname);
} else {
memset(&n->address, 0, sizeof n->address);
logger(LOG_DEBUG, "UDP address of %s cleared", n->name);
}
}
int dump_nodes(struct evbuffer *out) {
splay_node_t *node;
node_t *n;
@ -162,8 +184,8 @@ int dump_nodes(struct evbuffer *out) {
for(node = node_tree->head; node; node = node->next) {
n = node->data;
if(evbuffer_add_printf(out, _(" %s at %s cipher %d digest %d maclength %d compression %d options %lx status %04x nexthop %s via %s distance %d pmtu %d (min %d max %d)\n"),
n->name, n->hostname, cipher_get_nid(&n->cipher),
digest_get_nid(&n->digest), n->maclength, n->compression,
n->name, n->hostname, cipher_get_nid(&n->outcipher),
digest_get_nid(&n->outdigest), n->outmaclength, n->outcompression,
n->options, *(uint32_t *)&n->status, n->nexthop ? n->nexthop->name : "-",
n->via ? n->via->name : "-", n->distance, n->mtu, n->minmtu, n->maxmtu) == -1)
return errno;

View file

@ -54,11 +54,16 @@ typedef struct node_t {
node_status_t status;
cipher_t cipher; /* Cipher for UDP packets */
digest_t digest; /* Digest for UDP packets */
int maclength; /* Portion of digest to use */
cipher_t incipher; /* Cipher for UDP packets */
digest_t indigest; /* Digest for UDP packets */
int inmaclength; /* Portion of digest to use */
int compression; /* Compressionlevel, 0 = no compression */
cipher_t outcipher; /* Cipher for UDP packets */
digest_t outdigest; /* Digest for UDP packets */
int outmaclength; /* Portion of digest to use */
int incompression; /* Compressionlevel, 0 = no compression */
int outcompression; /* Compressionlevel, 0 = no compression */
int distance;
struct node_t *nexthop; /* nearest node from us to him */
@ -94,5 +99,6 @@ extern void node_del(node_t *);
extern node_t *lookup_node(char *);
extern node_t *lookup_node_udp(const sockaddr_t *);
extern int dump_nodes(struct evbuffer *);
extern void update_node_udp(node_t *, const sockaddr_t *);
#endif /* __TINC_NODE_H__ */

View file

@ -52,7 +52,7 @@ bool digest_open_sha1(digest_t *digest) {
void digest_close(digest_t *digest) {
}
bool digest_create(digest_t *digest, void *indata, size_t inlen, void *outdata) {
bool digest_create(digest_t *digest, const void *indata, size_t inlen, void *outdata) {
EVP_MD_CTX ctx;
if(EVP_DigestInit(&ctx, digest->digest)
@ -64,7 +64,7 @@ bool digest_create(digest_t *digest, void *indata, size_t inlen, void *outdata)
return false;
}
bool digest_verify(digest_t *digest, void *indata, size_t inlen, void *cmpdata) {
bool digest_verify(digest_t *digest, const void *indata, size_t inlen, const void *cmpdata) {
size_t len = EVP_MD_size(digest->digest);
char outdata[len];

View file

@ -34,8 +34,8 @@ extern bool digest_open_by_name(struct digest *, const char *);
extern bool digest_open_by_nid(struct digest *, int);
extern bool digest_open_sha1(struct digest *);
extern void digest_close(struct digest *);
extern bool digest_create(struct digest *, void *indata, size_t inlen, void *outdata);
extern bool digest_verify(struct digest *, void *indata, size_t inlen, void *digestdata);
extern bool digest_create(struct digest *, const void *indata, size_t inlen, void *outdata);
extern bool digest_verify(struct digest *, const void *indata, size_t inlen, const void *digestdata);
extern int digest_get_nid(const struct digest *);
extern size_t digest_length(const struct digest *);
extern bool digest_active(const struct digest *);

View file

@ -96,9 +96,9 @@ extern bool send_add_subnet(struct connection_t *, const struct subnet_t *);
extern bool send_del_subnet(struct connection_t *, const struct subnet_t *);
extern bool send_add_edge(struct connection_t *, const struct edge_t *);
extern bool send_del_edge(struct connection_t *, const struct edge_t *);
extern bool send_key_changed(struct connection_t *, const struct node_t *);
extern bool send_req_key(struct connection_t *, const struct node_t *, const struct node_t *);
extern bool send_ans_key(struct connection_t *, const struct node_t *, const struct node_t *);
extern bool send_key_changed();
extern bool send_req_key(struct node_t *);
extern bool send_ans_key(struct node_t *);
extern bool send_tcppacket(struct connection_t *, struct vpn_packet_t *);
/* Request handlers */

View file

@ -243,7 +243,7 @@ bool send_challenge(connection_t *c) {
cp();
if(!c->hischallenge)
c->hischallenge = xmalloc(len);
c->hischallenge = xrealloc(c->hischallenge, len);
/* Copy random data to the buffer */

View file

@ -93,6 +93,17 @@ bool add_edge_h(connection_t *c, char *request) {
/* Lookup nodes */
from = lookup_node(from_name);
to = lookup_node(to_name);
if(tunnelserver &&
from != myself && from != c->node &&
to != myself && to != c->node) {
/* ignore indirect edge registrations for tunnelserver */
ifdebug(PROTOCOL) logger(LOG_WARNING,
_("Ignoring indirect %s from %s (%s)"),
"ADD_EDGE", c->name, c->hostname);
return true;
}
if(!from) {
from = new_node();
@ -100,16 +111,12 @@ bool add_edge_h(connection_t *c, char *request) {
node_add(from);
}
to = lookup_node(to_name);
if(!to) {
to = new_node();
to->name = xstrdup(to_name);
node_add(to);
}
if(tunnelserver && from != myself && from != c->node && to != myself && to != c->node)
return false;
/* Convert addresses */
@ -206,6 +213,17 @@ bool del_edge_h(connection_t *c, char *request) {
/* Lookup nodes */
from = lookup_node(from_name);
to = lookup_node(to_name);
if(tunnelserver &&
from != myself && from != c->node &&
to != myself && to != c->node) {
/* ignore indirect edge registrations for tunnelserver */
ifdebug(PROTOCOL) logger(LOG_WARNING,
_("Ignoring indirect %s from %s (%s)"),
"DEL_EDGE", c->name, c->hostname);
return true;
}
if(!from) {
ifdebug(PROTOCOL) logger(LOG_ERR, _("Got %s from %s (%s) which does not appear in the edge tree"),
@ -213,17 +231,12 @@ bool del_edge_h(connection_t *c, char *request) {
return true;
}
to = lookup_node(to_name);
if(!to) {
ifdebug(PROTOCOL) logger(LOG_ERR, _("Got %s from %s (%s) which does not appear in the edge tree"),
"DEL_EDGE", c->name, c->hostname);
return true;
}
if(tunnelserver && from != myself && from != c->node && to != myself && to != c->node)
return false;
/* Check if edge exists */
e = lookup_edge(from, to);

View file

@ -25,6 +25,7 @@
#include "splay_tree.h"
#include "cipher.h"
#include "connection.h"
#include "crypto.h"
#include "logger.h"
#include "net.h"
#include "netutl.h"
@ -35,17 +36,17 @@
static bool mykeyused = false;
bool send_key_changed(connection_t *c, const node_t *n) {
bool send_key_changed() {
cp();
/* Only send this message if some other daemon requested our key previously.
This reduces unnecessary key_changed broadcasts.
*/
if(n == myself && !mykeyused)
if(!mykeyused)
return true;
return send_request(c, "%d %lx %s", KEY_CHANGED, random(), n->name);
return send_request(broadcast, "%d %lx %s", KEY_CHANGED, random(), myself->name);
}
bool key_changed_h(connection_t *c, char *request) {
@ -82,10 +83,10 @@ bool key_changed_h(connection_t *c, char *request) {
return true;
}
bool send_req_key(connection_t *c, const node_t *from, const node_t *to) {
bool send_req_key(node_t *to) {
cp();
return send_request(c, "%d %s %s", REQ_KEY, from->name, to->name);
return send_request(to->nexthop->connection, "%d %s %s", REQ_KEY, myself->name, to->name);
}
bool req_key_h(connection_t *c, char *request) {
@ -120,10 +121,8 @@ bool req_key_h(connection_t *c, char *request) {
/* Check if this key request is for us */
if(to == myself) { /* Yes, send our own key back */
mykeyused = true;
from->received_seqno = 0;
memset(from->late, 0, sizeof from->late);
send_ans_key(c, myself, from);
send_ans_key(from);
} else {
if(tunnelserver)
return false;
@ -134,27 +133,39 @@ bool req_key_h(connection_t *c, char *request) {
return true;
}
send_req_key(to->nexthop->connection, from, to);
send_request(to->nexthop->connection, "%s", request);
}
return true;
}
bool send_ans_key(connection_t *c, const node_t *from, const node_t *to) {
size_t keylen = cipher_keylength(&from->cipher);
bool send_ans_key(node_t *to) {
size_t keylen = cipher_keylength(&myself->incipher);
char key[keylen * 2 + 1];
cp();
cipher_get_key(&from->cipher, key);
cipher_open_by_nid(&to->incipher, cipher_get_nid(&myself->incipher));
digest_open_by_nid(&to->indigest, digest_get_nid(&myself->indigest));
to->inmaclength = myself->inmaclength;
to->incompression = myself->incompression;
randomize(key, keylen);
cipher_set_key(&to->incipher, key, true);
bin2hex(key, key, keylen);
key[keylen * 2] = '\0';
return send_request(c, "%d %s %s %s %d %d %d %d", ANS_KEY,
from->name, to->name, key,
cipher_get_nid(&from->cipher),
digest_get_nid(&from->digest), from->maclength,
from->compression);
// Reset sequence number and late packet window
mykeyused = true;
to->received_seqno = 0;
memset(to->late, 0, sizeof(to->late));
return send_request(to->nexthop->connection, "%d %s %s %s %d %d %d %d", ANS_KEY,
myself->name, to->name, key,
cipher_get_nid(&to->incipher),
digest_get_nid(&to->indigest), to->inmaclength,
to->incompression);
}
bool ans_key_h(connection_t *c, char *request) {
@ -207,24 +218,24 @@ bool ans_key_h(connection_t *c, char *request) {
/* Check and lookup cipher and digest algorithms */
if(!cipher_open_by_nid(&from->cipher, cipher)) {
if(!cipher_open_by_nid(&from->outcipher, cipher)) {
logger(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name, from->hostname);
return false;
}
if(strlen(key) / 2 != cipher_keylength(&from->cipher)) {
if(strlen(key) / 2 != cipher_keylength(&from->outcipher)) {
logger(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name, from->hostname);
return false;
}
from->maclength = maclength;
from->outmaclength = maclength;
if(!digest_open_by_nid(&from->digest, digest)) {
if(!digest_open_by_nid(&from->outdigest, digest)) {
logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name, from->hostname);
return false;
}
if(from->maclength > digest_length(&from->digest) || from->maclength < 0) {
if(from->outmaclength > digest_length(&from->outdigest) || from->outmaclength < 0) {
logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"), from->name, from->hostname);
return false;
}
@ -234,12 +245,12 @@ bool ans_key_h(connection_t *c, char *request) {
return false;
}
from->compression = compression;
from->outcompression = compression;
/* Update our copy of the origin's packet key */
hex2bin(key, key, cipher_keylength(&from->cipher));
cipher_set_key(&from->cipher, key, false);
hex2bin(key, key, cipher_keylength(&from->outcipher));
cipher_set_key(&from->outcipher, key, false);
from->status.validkey = true;
from->status.waitingforkey = false;

View file

@ -60,7 +60,7 @@ bool add_subnet_h(connection_t *c, char *request)
return false;
}
/* Check if owner name is a valid */
/* Check if owner name is valid */
if(!check_id(name)) {
logger(LOG_ERR, _("Got bad %s from %s (%s): %s"), "ADD_SUBNET", c->name,
@ -127,8 +127,11 @@ bool add_subnet_h(connection_t *c, char *request)
free_subnet(allowed);
}
if(!cfg)
if(!cfg) {
logger(LOG_WARNING, _("Unauthorized %s from %s (%s) for %s"),
"ADD_SUBNET", c->name, c->hostname, subnetstr);
return false;
}
free_subnet(allowed);
}
@ -176,7 +179,7 @@ bool del_subnet_h(connection_t *c, char *request)
return false;
}
/* Check if owner name is a valid */
/* Check if owner name is valid */
if(!check_id(name)) {
logger(LOG_ERR, _("Got bad %s from %s (%s): %s"), "DEL_SUBNET", c->name,
@ -184,19 +187,6 @@ bool del_subnet_h(connection_t *c, char *request)
return false;
}
/* Check if the owner of the new subnet is in the connection list */
owner = lookup_node(name);
if(!owner) {
ifdebug(PROTOCOL) logger(LOG_WARNING, _("Got %s from %s (%s) for %s which is not in our node tree"),
"DEL_SUBNET", c->name, c->hostname, name);
return true;
}
if(tunnelserver && owner != myself && owner != c->node)
return false;
/* Check if subnet string is valid */
if(!str2net(&s, subnetstr)) {
@ -208,6 +198,23 @@ bool del_subnet_h(connection_t *c, char *request)
if(seen_request(request))
return true;
/* Check if the owner of the subnet being deleted is in the connection list */
owner = lookup_node(name);
if(tunnelserver && owner != myself && owner != c->node) {
/* in case of tunnelserver, ignore indirect subnet deletion */
ifdebug(PROTOCOL) logger(LOG_WARNING, _("Ignoring indirect %s from %s (%s) for %s"),
"DEL_SUBNET", c->name, c->hostname, subnetstr);
return true;
}
if(!owner) {
ifdebug(PROTOCOL) logger(LOG_WARNING, _("Got %s from %s (%s) for %s which is not in our node tree"),
"DEL_SUBNET", c->name, c->hostname, name);
return true;
}
/* If everything is correct, delete the subnet from the list of the owner */
s.owner = owner;

View file

@ -33,6 +33,12 @@
#include LZO1X_H
#ifndef HAVE_MINGW
#include <pwd.h>
#include <grp.h>
#include <time.h>
#endif
#include <getopt.h>
#include "conf.h"
@ -62,6 +68,12 @@ bool bypass_security = false;
/* If nonzero, disable swapping for this process. */
bool do_mlock = false;
/* If nonzero, chroot to netdir after startup. */
static bool do_chroot = false;
/* If !NULL, do setuid to given user after startup */
static const char *switchuser = NULL;
/* If nonzero, write log entries to a separate file. */
bool use_logfile = false;
@ -81,6 +93,8 @@ static struct option const long_options[] = {
{"debug", optional_argument, NULL, 'd'},
{"bypass-security", no_argument, NULL, 3},
{"mlock", no_argument, NULL, 'L'},
{"chroot", no_argument, NULL, 'R'},
{"user", required_argument, NULL, 'U'},
{"logfile", optional_argument, NULL, 4},
{"controlsocket", required_argument, NULL, 5},
{NULL, 0, NULL, 0}
@ -105,7 +119,8 @@ static void usage(bool status)
" --logfile[=FILENAME] Write log entries to a logfile.\n"
" --controlsocket=FILENAME Open control socket at FILENAME.\n"
" --bypass-security Disables meta protocol security, for debugging.\n"
" --help Display this help and exit.\n"
" -R, --chroot chroot to NET dir at startup.\n"
" -U, --user=USER setuid to given USER at startup.\n" " --help Display this help and exit.\n"
" --version Output version information and exit.\n\n"));
printf(_("Report bugs to tinc@tinc-vpn.org.\n"));
}
@ -116,7 +131,7 @@ static bool parse_options(int argc, char **argv)
int r;
int option_index = 0;
while((r = getopt_long(argc, argv, "c:DLd::n:", long_options, &option_index)) != EOF) {
while((r = getopt_long(argc, argv, "c:DLd::n:RU:", long_options, &option_index)) != EOF) {
switch (r) {
case 0: /* long option */
break;
@ -130,8 +145,13 @@ static bool parse_options(int argc, char **argv)
break;
case 'L': /* no detach */
#ifndef HAVE_MLOCKALL
logger(LOG_ERR, _("%s not supported on this platform"), "mlockall()");
return false;
#else
do_mlock = true;
break;
#endif
case 'd': /* inc debug level */
if(optarg)
@ -144,6 +164,14 @@ static bool parse_options(int argc, char **argv)
netname = xstrdup(optarg);
break;
case 'R': /* chroot to NETNAME dir */
do_chroot = true;
break;
case 'U': /* setuid to USER */
switchuser = optarg;
break;
case 1: /* show help */
show_help = true;
break;
@ -237,6 +265,63 @@ static void free_names() {
if (confbase) free(confbase);
}
static bool drop_privs() {
#ifdef HAVE_MINGW
if (switchuser) {
logger(LOG_ERR, _("%s not supported on this platform"), "-U");
return false;
}
if (do_chroot) {
logger(LOG_ERR, _("%s not supported on this platform"), "-R");
return false;
}
#else
uid_t uid = 0;
if (switchuser) {
struct passwd *pw = getpwnam(switchuser);
if (!pw) {
logger(LOG_ERR, _("unknown user `%s'"), switchuser);
return false;
}
uid = pw->pw_uid;
if (initgroups(switchuser, pw->pw_gid) != 0 ||
setgid(pw->pw_gid) != 0) {
logger(LOG_ERR, _("System call `%s' failed: %s"),
"initgroups", strerror(errno));
return false;
}
endgrent();
endpwent();
}
if (do_chroot) {
tzset(); /* for proper timestamps in logs */
if (chroot(confbase) != 0 || chdir("/") != 0) {
logger(LOG_ERR, _("System call `%s' failed: %s"),
"chroot", strerror(errno));
return false;
}
free(confbase);
confbase = xstrdup("");
}
if (switchuser)
if (setuid(uid) != 0) {
logger(LOG_ERR, _("System call `%s' failed: %s"),
"setuid", strerror(errno));
return false;
}
#endif
return true;
}
#ifdef HAVE_MINGW
# define setpriority(level) SetPriorityClass(GetCurrentProcess(), level);
#else
# define NORMAL_PRIORITY_CLASS 0
# define BELOW_NORMAL_PRIORITY_CLASS 10
# define HIGH_PRIORITY_CLASS -10
# define setpriority(level) nice(level)
#endif
int main(int argc, char **argv)
{
program_name = argv[0];
@ -277,20 +362,6 @@ int main(int argc, char **argv)
if(!init_control())
return 1;
/* Lock all pages into memory if requested */
if(do_mlock)
#ifdef HAVE_MLOCKALL
if(mlockall(MCL_CURRENT | MCL_FUTURE)) {
logger(LOG_ERR, _("System call `%s' failed: %s"), "mlockall",
strerror(errno));
#else
{
logger(LOG_ERR, _("mlockall() not supported on this platform!"));
#endif
return -1;
}
g_argv = argv;
init_configuration(&config_tree);
@ -326,11 +397,46 @@ int main2(int argc, char **argv)
if(!detach())
return 1;
#ifdef HAVE_MLOCKALL
/* Lock all pages into memory if requested.
* This has to be done after daemon()/fork() so it works for child.
* No need to do that in parent as it's very short-lived. */
if(do_mlock && mlockall(MCL_CURRENT | MCL_FUTURE) != 0) {
logger(LOG_ERR, _("System call `%s' failed: %s"), "mlockall",
strerror(errno));
return 1;
}
#endif
/* Setup sockets and open device. */
if(!setup_network_connections())
if(!setup_network())
goto end;
/* Initiate all outgoing connections. */
try_outgoing_connections();
/* Change process priority */
char *priority = 0;
if(get_config_string(lookup_config(config_tree, "ProcessPriority"), &priority)) {
if(!strcasecmp(priority, "Normal"))
setpriority(NORMAL_PRIORITY_CLASS);
else if(!strcasecmp(priority, "Low"))
setpriority(BELOW_NORMAL_PRIORITY_CLASS);
else if(!strcasecmp(priority, "High"))
setpriority(HIGH_PRIORITY_CLASS);
else {
logger(LOG_ERR, _("Invalid priority `%s`!"), priority);
goto end;
}
}
/* drop privileges */
if (!drop_privs())
goto end;
/* Start main loop. It only exits when tinc is killed. */
@ -353,5 +459,8 @@ end:
crypto_exit();
exit_configuration(&config_tree);
free_names();
return status;
}