.Nd tinc VPN daemon
.Sh SYNOPSIS
.Nm
- .Op Fl cdDKnL
-.Op Fl cdDkKnLRU
++.Op Fl cdDKnLRU
.Op Fl -config Ns = Ns Ar DIR
.Op Fl -no-detach
.Op Fl -debug Ns Op = Ns Ar LEVEL
-.Op Fl -kill Ns Op = Ns Ar SIGNAL
.Op Fl -net Ns = Ns Ar NETNAME
-.Op Fl -generate-keys Ns Op = Ns Ar BITS
.Op Fl -mlock
.Op Fl -logfile Ns Op = Ns Ar FILE
-.Op Fl -pidfile Ns = Ns Ar FILE
.Op Fl -bypass-security
+ .Op Fl -chroot
+ .Op Fl -user Ns = Ns Ar USER
.Op Fl -help
.Op Fl -version
.Sh DESCRIPTION
volatile int cp_index = 0;
#endif
- char *hexadecimals = "0123456789ABCDEF";
+ const char hexadecimals[] = "0123456789ABCDEF";
-int charhex2bin(char c)
-{
+int charhex2bin(char c) {
if(isdigit(c))
return c - '0';
else
send_udppacket(n, &packet);
}
- n->mtuevent = new_event();
- n->mtuevent->handler = (event_handler_t)send_mtu_probe;
- n->mtuevent->data = n;
- n->mtuevent->time = now + 1;
- event_add(n->mtuevent);
+ event_add(&n->mtuevent, &(struct timeval){1, 0});
+}
+
+void send_mtu_probe(node_t *n) {
+ if(!timeout_initialized(&n->mtuevent))
+ timeout_set(&n->mtuevent, send_mtu_probe_handler, 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]) {
route(n, packet);
}
- static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
-static bool try_mac(const node_t *n, const vpn_packet_t *inpkt)
++static bool try_mac(node_t *n, const vpn_packet_t *inpkt)
+ {
- unsigned char hmac[EVP_MAX_MD_SIZE];
-
- if(!n->indigest || !n->inmaclength || !n->inkey || inpkt->len < sizeof inpkt->seqno + n->inmaclength)
++ if(!digest_active(&n->indigest) || !n->inmaclength || inpkt->len < sizeof inpkt->seqno + n->inmaclength)
+ return false;
+
- HMAC(n->indigest, n->inkey, n->inkeylength, (unsigned char *) &inpkt->seqno, inpkt->len - n->inmaclength, (unsigned char *)hmac, NULL);
-
- return !memcmp(hmac, (char *) &inpkt->seqno + inpkt->len - n->inmaclength, n->inmaclength);
++ 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;
cp();
- if(!n->inkey) {
++ 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) + n->inmaclength) {
++ 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;
/* Check the message authentication code */
- if(digest_active(&myself->digest) && !digest_verify(&myself->digest, &inpkt->seqno, inpkt->len, &inpkt->seqno + inpkt->len)) {
- if(n->indigest && n->inmaclength) {
- inpkt->len -= n->inmaclength;
- HMAC(n->indigest, n->inkey, n->inkeylength,
- (unsigned char *) &inpkt->seqno, inpkt->len, (unsigned char *)hmac, NULL);
-
- if(memcmp(hmac, (char *) &inpkt->seqno + inpkt->len, n->inmaclength)) {
- ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Got unauthenticated packet from %s (%s)"),
- n->name, n->hostname);
- return;
- }
++ 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(n->incipher) {
++ if(cipher_active(&n->incipher)) {
outpkt = pkt[nextpkt++];
+ outlen = MAXSIZE;
- if(!cipher_decrypt(&myself->cipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
- if(!EVP_DecryptInit_ex(&n->inctx, NULL, NULL, NULL, NULL)
- || !EVP_DecryptUpdate(&n->inctx, (unsigned char *) &outpkt->seqno, &outlen,
- (unsigned char *) &inpkt->seqno, inpkt->len)
- || !EVP_DecryptFinal_ex(&n->inctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) {
- ifdebug(TRAFFIC) logger(LOG_DEBUG, _("Error decrypting packet from %s (%s): %s"),
- n->name, n->hostname, ERR_error_string(ERR_get_error(), NULL));
++ 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;
}
inpkt->priority = 0;
- if(n->connection)
- n->connection->last_ping_time = now;
-
if(!inpkt->data[12] && !inpkt->data[13])
- mtu_probe_h(n, inpkt);
+ mtu_probe_h(n, inpkt, origlen);
else
receive_packet(n, inpkt);
}
/* Encrypt the packet */
- if(cipher_active(&n->cipher)) {
- if(n->outcipher) {
++ if(cipher_active(&n->outcipher)) {
outpkt = pkt[nextpkt++];
+ outlen = MAXSIZE;
- if(!cipher_encrypt(&n->cipher, &inpkt->seqno, inpkt->len, &outpkt->seqno, &outlen, true)) {
- if(!EVP_EncryptInit_ex(&n->outctx, NULL, NULL, NULL, NULL)
- || !EVP_EncryptUpdate(&n->outctx, (unsigned char *) &outpkt->seqno, &outlen,
- (unsigned char *) &inpkt->seqno, inpkt->len)
- || !EVP_EncryptFinal_ex(&n->outctx, (unsigned char *) &outpkt->seqno + outlen, &outpad)) {
- ifdebug(TRAFFIC) logger(LOG_ERR, _("Error while encrypting packet to %s (%s): %s"),
- n->name, n->hostname, ERR_error_string(ERR_get_error(), NULL));
++ 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;
}
/* 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(n->outdigest && n->outmaclength) {
- HMAC(n->outdigest, n->outkey, n->outkeylength, (unsigned char *) &inpkt->seqno,
- inpkt->len, (unsigned char *) &inpkt->seqno + inpkt->len, NULL);
- inpkt->len += n->outmaclength;
++ 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 */
}
}
- avl_node_t *node;
+ static node_t *try_harder(const sockaddr_t *from, const vpn_packet_t *pkt) {
-void handle_incoming_vpn_data(int sock)
++ 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;
char *hostname;
logger(LOG_WARNING, _("Warning: insecure file permissions for RSA private key file `%s'!"), fname);
#endif
- myself->connection->rsa_key = PEM_read_RSAPrivateKey(fp, NULL, NULL, NULL);
+ result = rsa_read_pem_private_key(&myself->connection->rsa, fp);
fclose(fp);
- if(!myself->connection->rsa_key) {
- logger(LOG_ERR, _("Reading RSA private key file `%s' failed: %s"),
- fname, strerror(errno));
- free(fname);
- return false;
+ if(!result)
+ logger(LOG_ERR, _("Reading RSA private key file `%s' failed: %s"), fname, strerror(errno));
+ free(fname);
+ return result;
+}
+
+static struct event keyexpire_event;
+
+static void keyexpire_handler(int fd, short events, void *data) {
+ regenerate_key();
+}
+
+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 {
+ timeout_set(&keyexpire_event, keyexpire_handler, NULL);
}
- free(fname);
- return true;
+ event_add(&keyexpire_event, &(struct timeval){keylifetime, 0});
}
/*
/* Generate packet encryption key */
- if(get_config_string
- (lookup_config(myself->connection->config_tree, "Cipher"), &cipher)) {
- if(!strcasecmp(cipher, "none")) {
- myself->incipher = NULL;
- } else {
- myself->incipher = EVP_get_cipherbyname(cipher);
-
- if(!myself->incipher) {
- logger(LOG_ERR, _("Unrecognized cipher type!"));
- return false;
- }
- }
- } else
- myself->incipher = EVP_bf_cbc();
-
- if(myself->incipher)
- myself->inkeylength = myself->incipher->key_len + myself->incipher->iv_len;
- else
- myself->inkeylength = 1;
+ if(!get_config_string(lookup_config(myself->connection->config_tree, "Cipher"), &cipher))
+ cipher = xstrdup("blowfish");
- if(!cipher_open_by_name(&myself->cipher, cipher)) {
- myself->connection->outcipher = EVP_bf_ofb();
++ if(!cipher_open_by_name(&myself->incipher, cipher)) {
+ logger(LOG_ERR, _("Unrecognized cipher type!"));
+ return false;
+ }
if(!get_config_int(lookup_config(config_tree, "KeyExpire"), &keylifetime))
keylifetime = 3600;
- keyexpires = now + keylifetime;
-
+ regenerate_key();
+
/* Check if we want to use message authentication codes... */
- if(get_config_string(lookup_config(myself->connection->config_tree, "Digest"), &digest)) {
- if(!strcasecmp(digest, "none")) {
- myself->indigest = NULL;
- } else {
- myself->indigest = EVP_get_digestbyname(digest);
+ if(!get_config_string(lookup_config(myself->connection->config_tree, "Digest"), &digest))
+ digest = xstrdup("sha1");
- if(!digest_open_by_name(&myself->digest, digest)) {
- if(!myself->indigest) {
- logger(LOG_ERR, _("Unrecognized digest type!"));
- return false;
- }
- }
- } else
- myself->indigest = EVP_sha1();
-
- myself->connection->outdigest = EVP_sha1();
-
- if(get_config_int(lookup_config(myself->connection->config_tree, "MACLength"), &myself->inmaclength)) {
- if(myself->indigest) {
- if(myself->inmaclength > myself->indigest->md_size) {
- logger(LOG_ERR, _("MAC length exceeds size of digest!"));
- return false;
- } else if(myself->inmaclength < 0) {
- logger(LOG_ERR, _("Bogus MAC length!"));
- return false;
- }
- }
- } else
- myself->inmaclength = 4;
++ 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))
- myself->connection->outmaclength = 0;
++ 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;
+ }
+ }
/* Compression */
}
/*
- setup all initial network connections
+ initialize network
*/
- bool setup_network_connections(void) {
+ bool setup_network(void)
+ {
cp();
- now = time(NULL);
-
- init_events();
init_connections();
init_subnets();
init_nodes();
}
return nfd;
- }
+ } /* int setup_vpn_in_socket */
-void retry_outgoing(outgoing_t *outgoing)
-{
- event_t *event;
+static void retry_outgoing_handler(int fd, short events, void *data) {
+ setup_outgoing_connection(data);
+}
+void retry_outgoing(outgoing_t *outgoing) {
cp();
outgoing->timeout += 5;
sockaddrfree(&n->address);
- cipher_close(&n->cipher);
- digest_close(&n->digest);
- EVP_CIPHER_CTX_cleanup(&n->inctx);
- EVP_CIPHER_CTX_cleanup(&n->outctx);
++ cipher_close(&n->incipher);
++ digest_close(&n->indigest);
++ cipher_close(&n->outcipher);
++ digest_close(&n->outdigest);
- if(n->mtuevent)
- event_del(n->mtuevent);
+ event_del(&n->mtuevent);
if(n->hostname)
free(n->hostname);
edge_del(e);
}
- avl_delete(node_tree, n);
- avl_delete(node_udp_tree, n);
+ splay_delete(node_tree, n);
++ splay_delete(node_udp_tree, n);
}
-node_t *lookup_node(char *name)
-{
+node_t *lookup_node(char *name) {
node_t n = {0};
cp();
n.address = *sa;
n.name = NULL;
- return avl_search(node_udp_tree, &n);
+ return splay_search(node_udp_tree, &n);
}
- avl_delete(node_udp_tree, n);
+ void update_node_udp(node_t *n, const sockaddr_t *sa)
+ {
- avl_delete(node_udp_tree, n);
- avl_insert(node_udp_tree, n);
++ splay_delete(node_udp_tree, n);
+
+ if(n->hostname)
+ free(n->hostname);
+
+ if(sa) {
+ n->address = *sa;
+ n->hostname = sockaddr2hostname(&n->address);
-void dump_nodes(void)
-{
- avl_node_t *node;
++ 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;
cp();
- logger(LOG_DEBUG, _("Nodes:"));
-
for(node = node_tree->head; node; node = node->next) {
n = node->data;
- logger(LOG_DEBUG, _(" %s at %s cipher %d digest %d maclength %d compression %d options %lx status %04x nexthop %s via %s pmtu %d (min %d max %d)"),
- n->name, n->hostname, n->outcipher ? n->outcipher->nid : 0,
- n->outdigest ? n->outdigest->type : 0, n->outmaclength, n->outcompression,
+ 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->mtu, n->minmtu, n->maxmtu);
+ n->via ? n->via->name : "-", n->distance, n->mtu, n->minmtu, n->maxmtu) == -1)
+ return errno;
}
- logger(LOG_DEBUG, _("End of nodes."));
+ return 0;
}
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 */
- const EVP_CIPHER *incipher; /* Cipher type for UDP packets received from him */
- char *inkey; /* Cipher key and iv */
- int inkeylength; /* Cipher key and iv length */
- EVP_CIPHER_CTX inctx; /* Cipher context */
-
- const EVP_CIPHER *outcipher; /* Cipher type for UDP packets sent to him*/
- char *outkey; /* Cipher key and iv */
- int outkeylength; /* Cipher key and iv length */
- EVP_CIPHER_CTX outctx; /* Cipher context */
-
- const EVP_MD *indigest; /* Digest type for MAC of packets received from him */
- int inmaclength; /* Length of MAC */
-
- const EVP_MD *outdigest; /* Digest type for MAC of packets sent to him*/
- int outmaclength; /* Length of MAC */
++ 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 */
struct node_t *via; /* next hop for UDP packets */
extern void node_del(node_t *);
extern node_t *lookup_node(char *);
extern node_t *lookup_node_udp(const sockaddr_t *);
-extern void dump_nodes(void);
+extern int dump_nodes(struct evbuffer *);
+ extern void update_node_udp(node_t *, const sockaddr_t *);
#endif /* __TINC_NODE_H__ */
--- /dev/null
- bool digest_create(digest_t *digest, void *indata, size_t inlen, void *outdata) {
+/*
+ digest.c -- Digest handling
+ Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
+
+ 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.
+
+ $Id$
+*/
+
+#include "system.h"
+
+#include <openssl/err.h>
+
+#include "digest.h"
+#include "logger.h"
+
+bool digest_open_by_name(digest_t *digest, const char *name) {
+ digest->digest = EVP_get_digestbyname(name);
+ if(digest->digest)
+ return true;
+
+ logger(LOG_DEBUG, _("Unknown digest name '%s'!"), name);
+ return false;
+}
+
+bool digest_open_by_nid(digest_t *digest, int nid) {
+ digest->digest = EVP_get_digestbynid(nid);
+ if(digest->digest)
+ return true;
+
+ logger(LOG_DEBUG, _("Unknown digest nid %d!"), nid);
+ return false;
+}
+
+bool digest_open_sha1(digest_t *digest) {
+ digest->digest = EVP_sha1();
+ return true;
+}
+
+void digest_close(digest_t *digest) {
+}
+
- bool digest_verify(digest_t *digest, void *indata, size_t inlen, void *cmpdata) {
++bool digest_create(digest_t *digest, const void *indata, size_t inlen, void *outdata) {
+ EVP_MD_CTX ctx;
+
+ if(EVP_DigestInit(&ctx, digest->digest)
+ && EVP_DigestUpdate(&ctx, indata, inlen)
+ && EVP_DigestFinal(&ctx, outdata, NULL))
+ return true;
+
+ logger(LOG_DEBUG, _("Error creating digest: %s"), ERR_error_string(ERR_get_error(), NULL));
+ return false;
+}
+
++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];
+
+ return digest_create(digest, indata, inlen, outdata) && !memcmp(cmpdata, outdata, len);
+}
+
+int digest_get_nid(const digest_t *digest) {
+ return digest->digest ? digest->digest->type : 0;
+}
+
+size_t digest_length(const digest_t *digest) {
+ return EVP_MD_size(digest->digest);
+}
+
+bool digest_active(const digest_t *digest) {
+ return digest->digest && digest->digest->type != 0;
+}
--- /dev/null
- 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);
+/*
+ digest.h -- header file digest.c
+ Copyright (C) 2007 Guus Sliepen <guus@tinc-vpn.org>
+
+ 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.
+
+ $Id$
+*/
+
+#ifndef __TINC_DIGEST_H__
+#define __TINC_DIGEST_H__
+
+#include <openssl/evp.h>
+
+#define DIGEST_MAX_SIZE EVP_MAX_MD_SIZE
+
+typedef struct digest {
+ const EVP_MD *digest;
+} digest_t;
+
+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 *, 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 *);
+
+#endif
cp();
- /* CHECKME: what is most reasonable value for len? */
-
- len = RSA_size(c->rsa_key);
-
- /* Allocate buffers for the challenge */
-
- buffer = alloca(2 * len + 1);
-
- c->hischallenge = xrealloc(c->hischallenge, len);
+ if(!c->hischallenge)
- c->hischallenge = xmalloc(len);
++ c->hischallenge = xrealloc(c->hischallenge, len);
/* Copy random data to the buffer */
#include "system.h"
-#include <openssl/evp.h>
-#include <openssl/err.h>
-#include <openssl/rand.h>
-
-#include "avl_tree.h"
+#include "splay_tree.h"
+#include "cipher.h"
#include "connection.h"
++#include "crypto.h"
#include "logger.h"
#include "net.h"
#include "netutl.h"
#include "utils.h"
#include "xalloc.h"
-bool mykeyused = false;
+static bool mykeyused = false;
- bool send_key_changed(connection_t *c, const node_t *n) {
-bool send_key_changed()
-{
++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)
-{
+bool key_changed_h(connection_t *c, char *request) {
char name[MAX_STRING_SIZE];
node_t *n;
return true;
}
- bool send_req_key(connection_t *c, const node_t *from, const node_t *to) {
-bool send_req_key(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)
-{
+bool req_key_h(connection_t *c, char *request) {
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
node_t *from, *to;
/* 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;
return true;
}
- send_req_key(to->nexthop->connection, from, to);
- send_request(to->nexthop->connection, "%s", c->buffer);
++ 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)
-{
- char *key;
++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);
- // Set key parameters
- to->incipher = myself->incipher;
- to->inkeylength = myself->inkeylength;
- to->indigest = myself->indigest;
++ 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;
+
- // Allocate memory for key
- to->inkey = xrealloc(to->inkey, to->inkeylength);
++ randomize(key, keylen);
++ cipher_set_key(&to->incipher, key, true);
+
- // Create a new key
- RAND_pseudo_bytes((unsigned char *)to->inkey, to->inkeylength);
- if(to->incipher)
- EVP_DecryptInit_ex(&to->inctx, to->incipher, NULL, (unsigned char *)to->inkey, (unsigned char *)to->inkey + to->incipher->key_len);
+ 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));
+
- // Convert to hexadecimal and send
- key = alloca(2 * to->inkeylength + 1);
- bin2hex(to->inkey, key, to->inkeylength);
- key[to->inkeylength * 2] = '\0';
-
+ return send_request(to->nexthop->connection, "%d %s %s %s %d %d %d %d", ANS_KEY,
- myself->name, to->name, key,
- to->incipher ? to->incipher->nid : 0,
- to->indigest ? to->indigest->type : 0, to->inmaclength,
- to->incompression);
++ 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)
-{
+bool ans_key_h(connection_t *c, char *request) {
char from_name[MAX_STRING_SIZE];
char to_name[MAX_STRING_SIZE];
char key[MAX_STRING_SIZE];
return true;
}
- return send_request(to->nexthop->connection, "%s", c->buffer);
+ return send_request(to->nexthop->connection, "%s", request);
}
- /* Update our copy of the origin's packet key */
- from->outkey = xrealloc(from->outkey, strlen(key) / 2);
-
- from->outkey = xstrdup(key);
- from->outkeylength = strlen(key) / 2;
- hex2bin(key, from->outkey, from->outkeylength);
-
- from->status.waitingforkey = false;
/* Check and lookup cipher and digest algorithms */
- if(!cipher_open_by_nid(&from->cipher, cipher)) {
- if(cipher) {
- from->outcipher = EVP_get_cipherbynid(cipher);
-
- if(!from->outcipher) {
- logger(LOG_ERR, _("Node %s (%s) uses unknown cipher!"), from->name,
- from->hostname);
- return false;
- }
++ 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(from->outkeylength != from->outcipher->key_len + from->outcipher->iv_len) {
- logger(LOG_ERR, _("Node %s (%s) uses wrong keylength!"), from->name,
- from->hostname);
- return false;
- }
- } else {
- from->outcipher = NULL;
++ 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) {
- from->outdigest = EVP_get_digestbynid(digest);
-
- if(!from->outdigest) {
- logger(LOG_ERR, _("Node %s (%s) uses unknown digest!"), from->name,
- from->hostname);
- return false;
- }
++ 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 > from->outdigest->md_size || from->outmaclength < 0) {
- logger(LOG_ERR, _("Node %s (%s) uses bogus MAC length!"),
- from->name, from->hostname);
- return false;
- }
- } else {
- from->outdigest = NULL;
++ 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;
}
if(compression < 0 || compression > 11) {
return false;
}
- from->compression = compression;
+ from->outcompression = compression;
- if(from->outcipher)
- if(!EVP_EncryptInit_ex(&from->outctx, from->outcipher, NULL, (unsigned char *)from->outkey, (unsigned char *)from->outkey + from->outcipher->key_len)) {
- logger(LOG_ERR, _("Error during initialisation of key from %s (%s): %s"),
- from->name, from->hostname, ERR_error_string(ERR_get_error(), NULL));
- return false;
- }
+ /* 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;
from->sent_seqno = 0;
if(from->options & OPTION_PMTU_DISCOVERY && !from->mtuprobes)
return false;
}
- if(seen_request(c->buffer))
+ 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;
#include <sys/mman.h>
#endif
-#include <openssl/rand.h>
-#include <openssl/rsa.h>
-#include <openssl/pem.h>
-#include <openssl/evp.h>
-#include <openssl/engine.h>
-
#include LZO1X_H
+ #ifndef HAVE_MINGW
+ #include <pwd.h>
+ #include <grp.h>
+ #include <time.h>
+ #endif
+
#include <getopt.h>
-#include "pidfile.h"
#include "conf.h"
+#include "control.h"
+#include "crypto.h"
#include "device.h"
#include "logger.h"
#include "net.h"
{"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},
- {"pidfile", required_argument, NULL, 5},
+ {"controlsocket", required_argument, NULL, 5},
{NULL, 0, NULL, 0}
};
program_name);
else {
printf(_("Usage: %s [option]...\n\n"), program_name);
- printf(_(" -c, --config=DIR Read configuration options from DIR.\n"
- " -D, --no-detach Don't fork and detach.\n"
- " -d, --debug[=LEVEL] Increase debug level or set it to LEVEL.\n"
- " -k, --kill[=SIGNAL] Attempt to kill a running tincd and exit.\n"
- " -n, --net=NETNAME Connect to net NETNAME.\n"
- " -K, --generate-keys[=BITS] Generate public/private RSA keypair.\n"
- " -L, --mlock Lock tinc into main memory.\n"
- " --logfile[=FILENAME] Write log entries to a logfile.\n"
- " --pidfile=FILENAME Write PID to FILENAME.\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(_( " -c, --config=DIR Read configuration options from DIR.\n"
+ " -D, --no-detach Don't fork and detach.\n"
+ " -d, --debug[=LEVEL] Increase debug level or set it to LEVEL.\n"
+ " -n, --net=NETNAME Connect to net NETNAME.\n"
+ " -L, --mlock Lock tinc into main memory.\n"
+ " --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"));
}
}
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::k::n:K::RU:", 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;
netname = xstrdup(optarg);
break;
- case 'K': /* generate public/private keypair */
- if(optarg) {
- generate_keys = atoi(optarg);
-
- if(generate_keys < 512) {
- fprintf(stderr, _("Invalid argument `%s'; BITS must be a number equal to or greater than 512.\n"),
- optarg);
- usage(true);
- return false;
- }
-
- generate_keys &= ~7; /* Round it to bytes */
- } else
- generate_keys = 1024;
- 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;
return 0;
}
- if(kill_tincd)
- return !kill_other(kill_tincd);
-
openlogger("tinc", use_logfile?LOGMODE_FILE:LOGMODE_STDERR);
- /* 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;
- }
-
+ if(!event_init()) {
+ logger(LOG_ERR, _("Error initializing libevent!"));
+ return 1;
+ }
+
+ if(!init_control())
+ return 1;
+
g_argv = argv;
init_configuration(&config_tree);
logger(LOG_NOTICE, _("Terminating"));
#ifndef HAVE_MINGW
- remove_pid(pidfilename);
+ exit_control();
#endif
- EVP_cleanup();
- ENGINE_cleanup();
- CRYPTO_cleanup_all_ex_data();
- ERR_remove_state(0);
- ERR_free_strings();
+ crypto_exit();
+ exit_configuration(&config_tree);
+ free_names();
+
return status;
}