Import Debian changes 1.1~pre7-1

tinc (1.1~pre7-1) experimental; urgency=high

  * New upstream release.
    - Drop packets forwarded via TCP if they are too big (CVE-2013-1428).
This commit is contained in:
Guus Sliepen 2013-04-23 11:37:38 +02:00
commit a62bf04cde
43 changed files with 1372 additions and 867 deletions

View file

@ -1,4 +1,4 @@
Copyright (C) 1998-2012 Ivo Timmermans, Guus Sliepen and others. Copyright (C) 1998-2013 Ivo Timmermans, Guus Sliepen and others.
See the AUTHORS file for a complete list. See the AUTHORS file for a complete list.
This program is free software; you can redistribute it and/or modify it under This program is free software; you can redistribute it and/or modify it under

View file

@ -1,3 +1,20 @@
Version 1.1pre7 April 22 2013
------------------------------------------------------------------------
Guus Sliepen (12):
Use UDP when using sptps_test in datagram mode.
Flush output buffers in the tap reader thread on Windows.
Better default output file for generated public keys.
Allow changing configuration with tincctl without the "config" keyword.
Avoid calling time(NULL).
Include README.android in the tarballs.
Rename tincctl to tinc.
Remove references to the config keyword.
Describe the SPTPS protocol in the manual.
Fix completion of add/del/get/set commands.
Drop packets forwarded via TCP if they are too big (CVE-2013-1428).
Releasing 1.1pre7.
Version 1.1pre6 February 20 2013 Version 1.1pre6 February 20 2013
------------------------------------------------------------------------ ------------------------------------------------------------------------

View file

@ -6,7 +6,7 @@ SUBDIRS = m4 src doc gui
ACLOCAL_AMFLAGS = -I m4 ACLOCAL_AMFLAGS = -I m4
EXTRA_DIST = have.h system.h COPYING.README EXTRA_DIST = have.h system.h COPYING.README README.android
ChangeLog: ChangeLog:
git log > ChangeLog git log > ChangeLog

View file

@ -236,7 +236,7 @@ top_srcdir = @top_srcdir@
AUTOMAKE_OPTIONS = gnu AUTOMAKE_OPTIONS = gnu
SUBDIRS = m4 src doc gui SUBDIRS = m4 src doc gui
ACLOCAL_AMFLAGS = -I m4 ACLOCAL_AMFLAGS = -I m4
EXTRA_DIST = have.h system.h COPYING.README EXTRA_DIST = have.h system.h COPYING.README README.android
all: config.h all: config.h
$(MAKE) $(AM_MAKEFLAGS) all-recursive $(MAKE) $(AM_MAKEFLAGS) all-recursive

14
NEWS
View file

@ -1,3 +1,17 @@
Version 1.1pre7 April 22 2013
* Fixed large latencies on Windows.
* Renamed the tincctl tool to tinc.
* Simplified changing the configuration using the tinc tool.
* Added a full description of the ExperimentalProtocol to the manual.
* Drop packets forwarded via TCP if they are too big (CVE-2013-1428).
Thanks to Martin Schobert for auditing tinc and reporting the vulnerability.
Version 1.1pre6 February 20 2013 Version 1.1pre6 February 20 2013
* Fixed tincd exitting immediately on Windows. * Fixed tincd exitting immediately on Windows.

10
README
View file

@ -1,4 +1,4 @@
This is the README file for tinc version 1.1pre6. Installation This is the README file for tinc version 1.1pre7. Installation
instructions may be found in the INSTALL file. instructions may be found in the INSTALL file.
tinc is Copyright (C) 1998-2013 by: tinc is Copyright (C) 1998-2013 by:
@ -22,7 +22,7 @@ Please note that this is NOT a stable release. Until version 1.1.0 is released,
please use one of the 1.0.x versions if you need a stable version of tinc. please use one of the 1.0.x versions if you need a stable version of tinc.
Although tinc 1.1 will be protocol compatible with tinc 1.0.x, the Although tinc 1.1 will be protocol compatible with tinc 1.0.x, the
functionality of the tincctl program may still change, and the control socket functionality of the tinc program may still change, and the control socket
protocol is not fixed yet. protocol is not fixed yet.
@ -36,11 +36,11 @@ at your own risk.
Compatibility Compatibility
------------- -------------
Version 1.1pre6 is compatible with 1.0pre8, 1.0 and later, but not with older Version 1.1pre7 is compatible with 1.0pre8, 1.0 and later, but not with older
versions of tinc. versions of tinc.
When the ExperimentalProtocol option is used, tinc is still compatible with When the ExperimentalProtocol option is used, tinc is still compatible with
1.0.X and 1.1pre6 itself, but not with any other 1.1preX version. 1.0.X and 1.1pre7 itself, but not with any other 1.1preX version.
Requirements Requirements
@ -88,6 +88,6 @@ Windows environment this means tinc will intall itself as a service, which will
restart after reboots. To prevent tinc from detaching or running as a service, restart after reboots. To prevent tinc from detaching or running as a service,
use the -D option. use the -D option.
The status of the VPN can be queried using the "tincctl" tool, which connects The status of the VPN can be queried using the "tinc" command, which connects
to a running tinc daemon via a control connection. The same tool also makes it to a running tinc daemon via a control connection. The same tool also makes it
easy to start and stop tinc, and to change its configuration. easy to start and stop tinc, and to change its configuration.

20
README.android Normal file
View file

@ -0,0 +1,20 @@
Quick how-o cross compile tinc for android (done from $HOME/android/):
- Download android NDK and setup local ARM toolchain:
wget http://dl.google.com/android/ndk/android-ndk-r8b-linux-x86.tar.bz2
tar xfj android-ndk-r8b-linux-x86.tar.bz2
./android-ndk-r8b/build/tools/make-standalone-toolchain.sh --platform=android-5 --install-dir=/tmp/my-android-toolchain
- Download and cross-compile openSSL for ARM:
wget http://www.openssl.org/source/openssl-1.0.1c.tar.gz
tar xfz openssl-1.0.1c.tar.gz
cd openssl-1.0.1c
./Configure dist
make CC=/tmp/my-android-toolchain/bin/arm-linux-androideabi-gcc AR="/tmp/my-android-toolchain/bin/arm-linux-androideabi-ar r" RANLIB=/tmp/my-android-toolchain/bin/arm-linux-androideabi-ranlib
- Clone and cross-compile tinc:
git clone git://tinc-vpn.org/tinc
cd tinc
autoreconf -fsi
CC=/tmp/my-android-toolchain/bin/arm-linux-androideabi-gcc ./configure --host=arm-linux --disable-lzo --with-openssl-lib=$HOME/android/openssl-1.0.1c --with-openssl-include=$HOME/android/openssl-1.0.1c/include/
make -j5

1
THANKS
View file

@ -30,6 +30,7 @@ We would like to thank the following people for their contributions to tinc:
* Mark Glines * Mark Glines
* Markus Goetz * Markus Goetz
* Martin Kihlgren * Martin Kihlgren
* Martin Schobert
* Martin Schürrer * Martin Schürrer
* Matias Carrasco * Matias Carrasco
* Max Rijevski * Max Rijevski

2
configure vendored
View file

@ -4095,7 +4095,7 @@ fi
# Define the identity of the package. # Define the identity of the package.
PACKAGE=tinc PACKAGE=tinc
VERSION=1.1pre6 VERSION=1.1pre7
cat >>confdefs.h <<_ACEOF cat >>confdefs.h <<_ACEOF

View file

@ -4,7 +4,7 @@ AC_PREREQ(2.61)
AC_INIT AC_INIT
AC_CONFIG_SRCDIR([src/tincd.c]) AC_CONFIG_SRCDIR([src/tincd.c])
AC_GNU_SOURCE AC_GNU_SOURCE
AM_INIT_AUTOMAKE(tinc, 1.1pre6) AM_INIT_AUTOMAKE(tinc, 1.1pre7)
AC_CONFIG_HEADERS([config.h]) AC_CONFIG_HEADERS([config.h])
AM_MAINTAINER_MODE AM_MAINTAINER_MODE

7
debian/changelog vendored
View file

@ -1,3 +1,10 @@
tinc (1.1~pre7-1) experimental; urgency=high
* New upstream release.
- Drop packets forwarded via TCP if they are too big (CVE-2013-1428).
-- Guus Sliepen <guus@debian.org> Tue, 23 Apr 2013 11:37:38 +0200
tinc (1.1~pre6-1) experimental; urgency=low tinc (1.1~pre6-1) experimental; urgency=low
* New upstream release. * New upstream release.

2
debian/control vendored
View file

@ -3,7 +3,7 @@ Section: net
Priority: optional Priority: optional
Maintainer: Guus Sliepen <guus@debian.org> Maintainer: Guus Sliepen <guus@debian.org>
Standards-Version: 3.9.3 Standards-Version: 3.9.3
Build-Depends: libssl-dev (>>1.0.0), debhelper (>= 9), texi2html, texinfo, zlib1g-dev, liblzo2-dev, libncurses5-dev, libreadline-dev Build-Depends: libssl-dev (>>1.0.0), debhelper (>= 9), texi2html, texinfo, zlib1g-dev, liblzo2-dev, libncurses5-dev, libreadline-dev, libvdeplug-dev
Homepage: http://www.tinc-vpn.org/ Homepage: http://www.tinc-vpn.org/
Package: tinc Package: tinc

2
debian/rules vendored
View file

@ -15,7 +15,7 @@
dh $@ --parallel dh $@ --parallel
override_dh_auto_configure: override_dh_auto_configure:
dh_auto_configure -- --disable-uml --disable-vde dh_auto_configure -- --enable-uml --enable-vde
override_dh_auto_build: override_dh_auto_build:
dh_auto_build dh_auto_build

2
debian/tinc.files vendored
View file

@ -1,5 +1,5 @@
usr/sbin/tincd usr/sbin/tincd
usr/sbin/tincctl usr/sbin/tinc
usr/share/man usr/share/man
etc etc
usr/share/doc/tinc usr/share/doc/tinc

View file

@ -3,7 +3,7 @@
set -e set -e
if [ "$IF_TINC_NET" ] ; then if [ "$IF_TINC_NET" ] ; then
tincctl -n "$IF_TINC_NET" stop tinc -n "$IF_TINC_NET" stop
sleep 0.1 sleep 0.1
i=0; i=0;
while [ -f "/var/run/tinc.$IF_TINC_NET.pid" ] ; do while [ -f "/var/run/tinc.$IF_TINC_NET.pid" ] ; do

2
debian/tinc.init vendored
View file

@ -19,7 +19,7 @@
# Based on Lubomir Bulej's Redhat init script. # Based on Lubomir Bulej's Redhat init script.
DAEMON="/usr/sbin/tincd" DAEMON="/usr/sbin/tincd"
CONTROL="/usr/sbin/tincctl" CONTROL="/usr/sbin/tinc"
NAME="tinc" NAME="tinc"
DESC="tinc daemons" DESC="tinc daemons"
TCONF="/etc/tinc" TCONF="/etc/tinc"

4
debian/tinc.install vendored
View file

@ -1,6 +1,6 @@
usr/sbin/tincd usr/sbin/tincd
usr/sbin/tincctl usr/sbin/tinc
usr/share/man/man5 usr/share/man/man5
usr/share/man/man8/tincd.* usr/share/man/man8/tincd.*
usr/share/man/man8/tincctl.* usr/share/man/man8/tinc.*
usr/share/info usr/share/info

View file

@ -2,11 +2,11 @@
info_TEXINFOS = tinc.texi info_TEXINFOS = tinc.texi
man_MANS = tincd.8 tincctl.8 tinc.conf.5 tinc-gui.8 man_MANS = tincd.8 tinc.8 tinc.conf.5 tinc-gui.8
EXTRA_DIST = tincinclude.texi.in tincd.8.in tincctl.8.in tinc.conf.5.in tinc-gui.8.in sample-config.tar.gz EXTRA_DIST = tincinclude.texi.in tincd.8.in tinc.8.in tinc.conf.5.in tinc-gui.8.in sample-config.tar.gz
CLEANFILES = *.html tinc.info tincd.8 tincctl.8 tinc.conf.5 tinc-gui.8 tincinclude.texi CLEANFILES = *.html tinc.info tincd.8 tinc.8 tinc.conf.5 tinc-gui.8 tincinclude.texi
# Use `ginstall' in the definition of man_MANS to avoid # Use `ginstall' in the definition of man_MANS to avoid
# confusion with the `install' target. The install rule transforms `ginstall' # confusion with the `install' target. The install rule transforms `ginstall'
@ -25,7 +25,7 @@ texi2html: tinc.texi
tincd.8.html: tincd.8 tincd.8.html: tincd.8
w3mman2html $? > $@ w3mman2html $? > $@
tincctl.8.html: tincctl.8 tinc.8.html: tinc.8
w3mman2html $? > $@ w3mman2html $? > $@
tinc-gui.8.html: tinc-gui.8 tinc-gui.8.html: tinc-gui.8
@ -43,7 +43,7 @@ substitute = sed \
tincd.8: tincd.8.in tincd.8: tincd.8.in
$(substitute) $? > $@ $(substitute) $? > $@
tincctl.8: tincctl.8.in tinc.8: tinc.8.in
$(substitute) $? > $@ $(substitute) $? > $@
tinc-gui.8: tinc-gui.8.in tinc-gui.8: tinc-gui.8.in

View file

@ -224,9 +224,9 @@ top_build_prefix = @top_build_prefix@
top_builddir = @top_builddir@ top_builddir = @top_builddir@
top_srcdir = @top_srcdir@ top_srcdir = @top_srcdir@
info_TEXINFOS = tinc.texi info_TEXINFOS = tinc.texi
man_MANS = tincd.8 tincctl.8 tinc.conf.5 tinc-gui.8 man_MANS = tincd.8 tinc.8 tinc.conf.5 tinc-gui.8
EXTRA_DIST = tincinclude.texi.in tincd.8.in tincctl.8.in tinc.conf.5.in tinc-gui.8.in sample-config.tar.gz EXTRA_DIST = tincinclude.texi.in tincd.8.in tinc.8.in tinc.conf.5.in tinc-gui.8.in sample-config.tar.gz
CLEANFILES = *.html tinc.info tincd.8 tincctl.8 tinc.conf.5 tinc-gui.8 tincinclude.texi CLEANFILES = *.html tinc.info tincd.8 tinc.8 tinc.conf.5 tinc-gui.8 tincinclude.texi
substitute = sed \ substitute = sed \
-e s,'@PACKAGE\@',"$(PACKAGE)",g \ -e s,'@PACKAGE\@',"$(PACKAGE)",g \
-e s,'@VERSION\@',"$(VERSION)",g \ -e s,'@VERSION\@',"$(VERSION)",g \
@ -772,7 +772,7 @@ texi2html: tinc.texi
tincd.8.html: tincd.8 tincd.8.html: tincd.8
w3mman2html $? > $@ w3mman2html $? > $@
tincctl.8.html: tincctl.8 tinc.8.html: tinc.8
w3mman2html $? > $@ w3mman2html $? > $@
tinc-gui.8.html: tinc-gui.8 tinc-gui.8.html: tinc-gui.8
@ -784,7 +784,7 @@ tinc.conf.5.html: tinc.conf.5
tincd.8: tincd.8.in tincd.8: tincd.8.in
$(substitute) $? > $@ $(substitute) $? > $@
tincctl.8: tincctl.8.in tinc.8: tinc.8.in
$(substitute) $? > $@ $(substitute) $? > $@
tinc-gui.8: tinc-gui.8.in tinc-gui.8: tinc-gui.8.in

View file

@ -3,7 +3,7 @@
.\" Manual page created by: .\" Manual page created by:
.\" Scott Lamb .\" Scott Lamb
.Sh NAME .Sh NAME
.Nm tincctl .Nm tinc
.Nd tinc VPN control .Nd tinc VPN control
.Sh SYNOPSIS .Sh SYNOPSIS
.Nm .Nm
@ -51,12 +51,12 @@ Create initial configuration files and RSA and ECDSA keypairs with default lengt
If no If no
.Ar name .Ar name
for this node is given, it will be asked for. for this node is given, it will be asked for.
.It config Oo get Oc Ar variable .It get Ar variable
Print the current value of configuration variable Print the current value of configuration variable
.Ar variable . .Ar variable .
If more than one variable with the same name exists, If more than one variable with the same name exists,
the value of each of them will be printed on a separate line. the value of each of them will be printed on a separate line.
.It config Oo set Oc Ar variable Ar value .It set Ar variable Ar value
Set configuration variable Set configuration variable
.Ar variable .Ar variable
to the given to the given
@ -64,9 +64,9 @@ to the given
All previously existing configuration variables with the same name are removed. All previously existing configuration variables with the same name are removed.
To set a variable for a specific host, use the notation To set a variable for a specific host, use the notation
.Ar host Ns Li . Ns Ar variable . .Ar host Ns Li . Ns Ar variable .
.It config add Ar variable Ar value .It add Ar variable Ar value
As above, but without removing any previously existing configuration variables. As above, but without removing any previously existing configuration variables.
.It config del Ar variable Op Ar value .It del Ar variable Op Ar value
Remove configuration variables with the same name and Remove configuration variables with the same name and
.Ar value . .Ar value .
If no If no
@ -147,7 +147,7 @@ Sets debug level to
.It log Op Ar N .It log Op Ar N
Capture log messages from a running tinc daemon. Capture log messages from a running tinc daemon.
An optional debug level can be given that will be applied only for log messages sent to An optional debug level can be given that will be applied only for log messages sent to
.Nm tincctl . .Nm tinc .
.It retry .It retry
Forces Forces
.Xr tincd 8 .Xr tincd 8
@ -182,19 +182,19 @@ such as
.Sh EXAMPLES .Sh EXAMPLES
Examples of some commands: Examples of some commands:
.Bd -literal -offset indent .Bd -literal -offset indent
tincctl -n vpn dump graph | circo -Txlib tinc -n vpn dump graph | circo -Txlib
tincctl -n vpn pcap | tcpdump -r - tinc -n vpn pcap | tcpdump -r -
tincctl -n vpn top tinc -n vpn top
.Pp .Pp
.Ed .Ed
Example of configuring tinc using Example of configuring tinc using
.Nm : .Nm :
.Bd -literal -offset indent .Bd -literal -offset indent
tincctl -n vpn init foo tinc -n vpn init foo
tincctl -n vpn config Subnet 192.168.1.0/24 tinc -n vpn add Subnet 192.168.1.0/24
tincctl -n vpn config bar.Address bar.example.com tinc -n vpn add bar.Address bar.example.com
tincctl -n vpn config ConnectTo bar tinc -n vpn add ConnectTo bar
tincctl -n vpn export | gpg --clearsign | mail -s "My config" vpnmaster@example.com tinc -n vpn export | gpg --clearsign | mail -s "My config" vpnmaster@example.com
.Ed .Ed
.Sh TOP .Sh TOP
The top command connects to a running tinc daemon and repeatedly queries its per-node traffic counters. The top command connects to a running tinc daemon and repeatedly queries its per-node traffic counters.

View file

@ -55,15 +55,15 @@ However, you are only allowed to use alphanumerical characters (a-z, A-Z, and 0-
.Sh INITIAL CONFIGURATION .Sh INITIAL CONFIGURATION
If you have not configured tinc yet, you can easily create a basic configuration using the following command: If you have not configured tinc yet, you can easily create a basic configuration using the following command:
.Bd -literal -offset indent .Bd -literal -offset indent
.Nm tincctl Fl n Ar NETNAME Li init Ar NAME .Nm tinc Fl n Ar NETNAME Li init Ar NAME
.Ed .Ed
.Pp .Pp
You can further change the configuration as needed either by manually editing the configuration files, You can further change the configuration as needed either by manually editing the configuration files,
or by using or by using
.Xr tincctl 8 . .Xr tinc 8 .
.Sh PUBLIC/PRIVATE KEYS .Sh PUBLIC/PRIVATE KEYS
The The
.Nm tincctl Li init .Nm tinc Li init
command will have generated both RSA and ECDSA public/private keypairs. command will have generated both RSA and ECDSA public/private keypairs.
The private keys should be stored in files named The private keys should be stored in files named
.Pa rsa_key.priv .Pa rsa_key.priv
@ -77,7 +77,7 @@ The RSA keys are used for backwards compatibility with tinc version 1.0.
If you are upgrading from version 1.0 to 1.1, you can keep the old configuration files, If you are upgrading from version 1.0 to 1.1, you can keep the old configuration files,
but you will need to create ECDSA keys using the following command: but you will need to create ECDSA keys using the following command:
.Bd -literal -offset indent .Bd -literal -offset indent
.Nm tincctl Fl n Ar NETNAME Li generate-ecdsa-keys .Nm tinc Fl n Ar NETNAME Li generate-ecdsa-keys
.Ed .Ed
.Sh SERVER CONFIGURATION .Sh SERVER CONFIGURATION
The server configuration of the daemon is done in the file The server configuration of the daemon is done in the file
@ -102,7 +102,7 @@ it is recommended to put host specific configuration options in the host configu
as this makes it easy to exchange with other nodes. as this makes it easy to exchange with other nodes.
.Pp .Pp
You can edit the config file manually, but it is recommended that you use You can edit the config file manually, but it is recommended that you use
.Xr tincctl 8 .Xr tinc 8
to change configuration variables for you. to change configuration variables for you.
.Pp .Pp
Here are all valid variables, listed in alphabetical order. Here are all valid variables, listed in alphabetical order.
@ -279,7 +279,7 @@ When this option is enabled, experimental protocol enhancements will be used.
Ephemeral ECDH will be used for key exchanges, Ephemeral ECDH will be used for key exchanges,
and ECDSA will be used instead of RSA for authentication. and ECDSA will be used instead of RSA for authentication.
When enabled, an ECDSA key must have been generated before with When enabled, an ECDSA key must have been generated before with
.Nm tincctl generate-ecdsa-keys . .Nm tinc generate-ecdsa-keys .
The experimental protocol may change at any time, The experimental protocol may change at any time,
and there is no guarantee that tinc will run stable when it is used. and there is no guarantee that tinc will run stable when it is used.
.It Va Forwarding Li = off | internal | kernel Po internal Pc Bq experimental .It Va Forwarding Li = off | internal | kernel Po internal Pc Bq experimental
@ -482,6 +482,8 @@ Furthermore, specifying
.Qq none .Qq none
will turn off packet encryption. will turn off packet encryption.
It is best to use only those ciphers which support CBC mode. It is best to use only those ciphers which support CBC mode.
This option has no effect for connections between nodes using
.Va ExperimentalProtocol .
.It Va ClampMSS Li = yes | no Pq yes .It Va ClampMSS Li = yes | no Pq yes
This option specifies whether tinc should clamp the maximum segment size (MSS) This option specifies whether tinc should clamp the maximum segment size (MSS)
of TCP packets to the path MTU. This helps in situations where ICMP of TCP packets to the path MTU. This helps in situations where ICMP
@ -496,6 +498,8 @@ Any digest supported by OpenSSL is recognised.
Furthermore, specifying Furthermore, specifying
.Qq none .Qq none
will turn off packet authentication. will turn off packet authentication.
This option has no effect for connections between nodes using
.Va ExperimentalProtocol .
.It Va IndirectData Li = yes | no Pq no .It Va IndirectData Li = yes | no Pq no
When set to yes, other nodes which do not already have a meta connection to you When set to yes, other nodes which do not already have a meta connection to you
will not try to establish direct communication with you. will not try to establish direct communication with you.
@ -505,6 +509,8 @@ The length of the message authentication code used to authenticate UDP packets.
Can be anything from Can be anything from
.Qq 0 .Qq 0
up to the length of the digest produced by the digest algorithm. up to the length of the digest produced by the digest algorithm.
This option has no effect for connections between nodes using
.Va ExperimentalProtocol .
.It Va PMTU Li = Ar mtu Po 1514 Pc .It Va PMTU Li = Ar mtu Po 1514 Pc
This option controls the initial path MTU to this node. This option controls the initial path MTU to this node.
.It Va PMTUDiscovery Li = yes | no Po yes Pc .It Va PMTUDiscovery Li = yes | no Po yes Pc
@ -653,7 +659,7 @@ its connection to the virtual network device.
.El .El
.Sh SEE ALSO .Sh SEE ALSO
.Xr tincd 8 , .Xr tincd 8 ,
.Xr tincctl 8 , .Xr tinc 8 ,
.Pa http://www.tinc-vpn.org/ , .Pa http://www.tinc-vpn.org/ ,
.Pa http://www.tldp.org/LDP/nag2/ . .Pa http://www.tldp.org/LDP/nag2/ .
.Pp .Pp

File diff suppressed because it is too large Load diff

View file

@ -30,6 +30,10 @@ permission notice identical to this one.
@end ifinfo @end ifinfo
@afourpaper
@paragraphindent none
@finalout
@titlepage @titlepage
@title tinc Manual @title tinc Manual
@subtitle Setting up a Virtual Private Network with tinc @subtitle Setting up a Virtual Private Network with tinc
@ -262,7 +266,7 @@ alias char-major-10-200 tun
@subsection Configuration of FreeBSD kernels @subsection Configuration of FreeBSD kernels
For FreeBSD version 4.1 and higher, tun and tap drivers are included in the default kernel configuration. For FreeBSD version 4.1 and higher, tun and tap drivers are included in the default kernel configuration.
The tap driver can be loaded with @code{kldload if_tap}, or by adding @code{if_tap_load="YES"} to @file{/boot/loader.conf}. The tap driver can be loaded with @code{kldload if_tap}, or by adding @code{if_tap_load="YES"} to @file{/boot/loader.conf}.
@c ================================================================== @c ==================================================================
@ -462,7 +466,7 @@ default).
@subsection libcurses @subsection libcurses
@cindex libcurses @cindex libcurses
For the "tincctl top" command, tinc requires a curses library. For the "tinc top" command, tinc requires a curses library.
If this library is not installed, you wil get an error when running the If this library is not installed, you wil get an error when running the
configure script. You can either install a suitable curses library, or disable configure script. You can either install a suitable curses library, or disable
@ -485,7 +489,7 @@ of this package.
@subsection libreadline @subsection libreadline
@cindex libreadline @cindex libreadline
For the "tincctl" command's shell functionality, tinc uses the readline library. For the "tinc" command's shell functionality, tinc uses the readline library.
If this library is not installed, you wil get an error when running the If this library is not installed, you wil get an error when running the
configure script. You can either install a suitable readline library, or configure script. You can either install a suitable readline library, or
@ -696,12 +700,12 @@ If you have everything clearly pictured in your mind,
proceed in the following order: proceed in the following order:
First, create the initial configuration files and public/private keypairs using the following command: First, create the initial configuration files and public/private keypairs using the following command:
@example @example
tincctl -n @var{NETNAME} init @var{NAME} tinc -n @var{NETNAME} init @var{NAME}
@end example @end example
Second, use @samp{tincctl -n @var{NETNAME} config ...} to further configure tinc. Second, use @samp{tinc -n @var{NETNAME} add ...} to further configure tinc.
Finally, export your host configuration file using @samp{tincctl -n @var{NETNAME} export} and send it to those Finally, export your host configuration file using @samp{tinc -n @var{NETNAME} export} and send it to those
people or computers you want tinc to connect to. people or computers you want tinc to connect to.
They should send you their host configuration file back, which you can import using @samp{tincctl -n @var{NETNAME} import}. They should send you their host configuration file back, which you can import using @samp{tinc -n @var{NETNAME} import}.
These steps are described in the subsections below. These steps are described in the subsections below.
@ -721,7 +725,7 @@ it doesn't even have to be the same on all the nodes of your VPN,
but it is recommended that you choose one anyway. but it is recommended that you choose one anyway.
We will asume you use a netname throughout this document. We will asume you use a netname throughout this document.
This means that you call tincctl with the -n argument, This means that you call tinc with the -n argument,
which will specify the netname. which will specify the netname.
The effect of this option is that tinc will set its configuration The effect of this option is that tinc will set its configuration
@ -809,7 +813,7 @@ put host specific configuration options in the host configuration file, as this
makes it easy to exchange with other nodes. makes it easy to exchange with other nodes.
You can edit the config file manually, but it is recommended that you use You can edit the config file manually, but it is recommended that you use
tincctl to change configuration variables for you. the tinc command to change configuration variables for you.
In the following two subsections all valid variables are listed in alphabetical order. In the following two subsections all valid variables are listed in alphabetical order.
The default value is given between parentheses, The default value is given between parentheses,
@ -1003,7 +1007,7 @@ When this option is enabled, experimental protocol enhancements will be used.
Ephemeral ECDH will be used for key exchanges, Ephemeral ECDH will be used for key exchanges,
and ECDSA will be used instead of RSA for authentication. and ECDSA will be used instead of RSA for authentication.
When enabled, an ECDSA key must have been generated before with When enabled, an ECDSA key must have been generated before with
@samp{tincctl generate-ecdsa-keys}. @samp{tinc generate-ecdsa-keys}.
The experimental protocol may change at any time, The experimental protocol may change at any time,
and there is no guarantee that tinc will run stable when it is used. and there is no guarantee that tinc will run stable when it is used.
@ -1130,7 +1134,7 @@ accidental eavesdropping if you are editting the configuration file.
@cindex PrivateKeyFile @cindex PrivateKeyFile
@item PrivateKeyFile = <@var{path}> (@file{@value{sysconfdir}/tinc/@var{netname}/rsa_key.priv}) @item PrivateKeyFile = <@var{path}> (@file{@value{sysconfdir}/tinc/@var{netname}/rsa_key.priv})
This is the full path name of the RSA private key file that was This is the full path name of the RSA private key file that was
generated by @samp{tincctl generate-keys}. It must be a full path, not a generated by @samp{tinc generate-keys}. It must be a full path, not a
relative directory. relative directory.
@cindex ProcessPriority @cindex ProcessPriority
@ -1217,10 +1221,11 @@ If no port is specified, the default Port is used.
@cindex Cipher @cindex Cipher
@item Cipher = <@var{cipher}> (blowfish) @item Cipher = <@var{cipher}> (blowfish)
The symmetric cipher algorithm used to encrypt UDP packets. The symmetric cipher algorithm used to encrypt UDP packets using the legacy protocol.
Any cipher supported by OpenSSL is recognized. Any cipher supported by OpenSSL is recognized.
Furthermore, specifying "none" will turn off packet encryption. Furthermore, specifying "none" will turn off packet encryption.
It is best to use only those ciphers which support CBC mode. It is best to use only those ciphers which support CBC mode.
This option has no effect for connections using the SPTPS protocol, which always use AES-256-CTR.
@cindex ClampMSS @cindex ClampMSS
@item ClampMSS = <yes|no> (yes) @item ClampMSS = <yes|no> (yes)
@ -1236,9 +1241,10 @@ Possible values are 0 (off), 1 (fast zlib) and any integer up to 9 (best zlib),
@cindex Digest @cindex Digest
@item Digest = <@var{digest}> (sha1) @item Digest = <@var{digest}> (sha1)
The digest algorithm used to authenticate UDP packets. The digest algorithm used to authenticate UDP packets using the legacy protocol.
Any digest supported by OpenSSL is recognized. Any digest supported by OpenSSL is recognized.
Furthermore, specifying "none" will turn off packet authentication. Furthermore, specifying "none" will turn off packet authentication.
This option has no effect for connections using the SPTPS protocol, which always use HMAC-SHA-256.
@cindex IndirectData @cindex IndirectData
@item IndirectData = <yes|no> (no) @item IndirectData = <yes|no> (no)
@ -1248,9 +1254,10 @@ It is best to leave this option out or set it to no.
@cindex MACLength @cindex MACLength
@item MACLength = <@var{bytes}> (4) @item MACLength = <@var{bytes}> (4)
The length of the message authentication code used to authenticate UDP packets. The length of the message authentication code used to authenticate UDP packets using the legacy protocol.
Can be anything from 0 Can be anything from 0
up to the length of the digest produced by the digest algorithm. up to the length of the digest produced by the digest algorithm.
This option has no effect for connections using the SPTPS protocol, which never truncate MACs.
@cindex PMTU @cindex PMTU
@item PMTU = <@var{mtu}> (1514) @item PMTU = <@var{mtu}> (1514)
@ -1273,7 +1280,7 @@ This is the RSA public key for this host.
@cindex PublicKeyFile @cindex PublicKeyFile
@item PublicKeyFile = <@var{path}> [obsolete] @item PublicKeyFile = <@var{path}> [obsolete]
This is the full path name of the RSA public key file that was generated This is the full path name of the RSA public key file that was generated
by @samp{tincctl generate-keys}. It must be a full path, not a relative by @samp{tinc generate-keys}. It must be a full path, not a relative
directory. directory.
@cindex PEM format @cindex PEM format
@ -1421,7 +1428,7 @@ When a subnet becomes (un)reachable, this is set to the subnet.
The initial directory structure, configuration files and public/private keypairs are created using the following command: The initial directory structure, configuration files and public/private keypairs are created using the following command:
@example @example
tincctl -n @var{netname} init @var{name} tinc -n @var{netname} init @var{name}
@end example @end example
(You will need to run this as root, or use "sudo".) (You will need to run this as root, or use "sudo".)
@ -1449,7 +1456,7 @@ and you yourself have a smaller portion of that range: 192.168.2.0/24.
Then you should run the following command: Then you should run the following command:
@example @example
tincctl -n @var{netname} config add subnet 192.168.2.0/24 tinc -n @var{netname} add subnet 192.168.2.0/24
@end example @end example
This will add a Subnet statement to your host configuration file. This will add a Subnet statement to your host configuration file.
@ -1465,18 +1472,18 @@ If you will use more than one address range, you can add more Subnets.
For example, if you also use the IPv6 subnet fec0:0:0:2::/64, you can add it as well: For example, if you also use the IPv6 subnet fec0:0:0:2::/64, you can add it as well:
@example @example
tincctl -n @var{netname} config add subnet fec0:0:0:2::/24 tinc -n @var{netname} add subnet fec0:0:0:2::/24
@end example @end example
This will add another line to the file @file{hosts/@var{name}}. This will add another line to the file @file{hosts/@var{name}}.
If you make a mistake, you can undo it by simply using @samp{config del} instead of @samp{config add}. If you make a mistake, you can undo it by simply using @samp{del} instead of @samp{add}.
If you want other tinc daemons to create meta-connections to your daemon, If you want other tinc daemons to create meta-connections to your daemon,
you should add your public IP address or hostname to your host configuration file. you should add your public IP address or hostname to your host configuration file.
For example, if your hostname is foo.example.org, run: For example, if your hostname is foo.example.org, run:
@example @example
tincctl -n @var{netname} config add address foo.example.org tinc -n @var{netname} add address foo.example.org
@end example @end example
If you already know to which daemons your daemon should make meta-connections, If you already know to which daemons your daemon should make meta-connections,
@ -1484,7 +1491,7 @@ you should configure that now as well.
Suppose you want to connect to a daemon named "bar", run: Suppose you want to connect to a daemon named "bar", run:
@example @example
tincctl -n @var{netname} config add connectto bar tinc -n @var{netname} add connectto bar
@end example @end example
Note that you specify the Name of the other daemon here, not an IP address or hostname! Note that you specify the Name of the other daemon here, not an IP address or hostname!
@ -1501,7 +1508,7 @@ If you are on a UNIX platform, you can easily send an email containing the neces
(assuming the owner of bar has the email address bar@@example.org): (assuming the owner of bar has the email address bar@@example.org):
@example @example
tincctl -n @var{netname} export | mail -s "My config file" bar@@example.org tinc -n @var{netname} export | mail -s "My config file" bar@@example.org
@end example @end example
If the owner of bar does the same to send his host configuration file to you, If the owner of bar does the same to send his host configuration file to you,
@ -1509,16 +1516,16 @@ you can probably pipe his email through the following command,
or you can just start this command in a terminal and copy&paste the email: or you can just start this command in a terminal and copy&paste the email:
@example @example
tincctl -n @var{netname} import tinc -n @var{netname} import
@end example @end example
If you are the owner of bar yourself, and you have SSH access to that computer, If you are the owner of bar yourself, and you have SSH access to that computer,
you can also swap the host configuration files using the following command: you can also swap the host configuration files using the following command:
@example @example
tincctl -n @var{netname} export \ tinc -n @var{netname} export \
| ssh bar.example.org tincctl -n @var{netname} exchange \ | ssh bar.example.org tinc -n @var{netname} exchange \
| tincctl -n @var{netname} import | tinc -n @var{netname} import
@end example @end example
You should repeat this for all nodes you ConnectTo, or which ConnectTo you. You should repeat this for all nodes you ConnectTo, or which ConnectTo you.
@ -1551,7 +1558,7 @@ When tinc starts, this script will be executed. When tinc exits, it will execute
You can manually open the script in an editor, or use the following command: You can manually open the script in an editor, or use the following command:
@example @example
tincctl -n @var{netname} edit tinc-up tinc -n @var{netname} edit tinc-up
@end example @end example
An example @file{tinc-up} script, that would be appropriate for the scenario in the previous section, is: An example @file{tinc-up} script, that would be appropriate for the scenario in the previous section, is:
@ -1612,7 +1619,7 @@ the real interface is also shown as a comment, to give you an idea of
how these example host is set up. All branches use the netname `company' how these example host is set up. All branches use the netname `company'
for this particular VPN. for this particular VPN.
Each branch is set up using the @samp{tincctl init} and @samp{tincctl config} commands, Each branch is set up using the @samp{tinc init} and @samp{tinc config} commands,
here we just show the end results: here we just show the end results:
@subsubheading For Branch A @subsubheading For Branch A
@ -1783,7 +1790,7 @@ their daemons, tinc will try connecting until they are available.
If everything else is done, you can start tinc by typing the following command: If everything else is done, you can start tinc by typing the following command:
@example @example
tincctl -n @var{netname} start tinc -n @var{netname} start
@end example @end example
@cindex daemon @cindex daemon
@ -1834,7 +1841,7 @@ Specifying . for @var{netname} is the same as not specifying any @var{netname}.
@xref{Multiple networks}. @xref{Multiple networks}.
@item --pidfile=@var{filename} @item --pidfile=@var{filename}
Store a cookie in @var{filename} which allows tincctl to authenticate. Store a cookie in @var{filename} which allows tinc to authenticate.
If unspecified, the default is If unspecified, the default is
@file{@value{localstatedir}/run/tinc.@var{netname}.pid}. @file{@value{localstatedir}/run/tinc.@var{netname}.pid}.
@ -2059,7 +2066,7 @@ Note that you will only see this message if you specified a debug
level of 5 or higher! level of 5 or higher!
@item Chances are that a @samp{Subnet = ...} line in the host configuration file of this tinc daemon is wrong. @item Chances are that a @samp{Subnet = ...} line in the host configuration file of this tinc daemon is wrong.
Change it to a subnet that is accepted locally by another interface, Change it to a subnet that is accepted locally by another interface,
or if that is not the case, try changing the prefix length into /32. or if that is not the case, try changing the prefix length into /32.
@end itemize @end itemize
@item Node foo (1.2.3.4) is not reachable @item Node foo (1.2.3.4) is not reachable
@ -2108,25 +2115,25 @@ Be sure to include the following information in your bugreport:
@node Controlling tinc @node Controlling tinc
@chapter Controlling tinc @chapter Controlling tinc
You can control and inspect a running tincd through the tincctl You can control and inspect a running tincd through the tinc
command. A quick example: command. A quick example:
@example @example
tincctl -n @var{netname} reload tinc -n @var{netname} reload
@end example @end example
@menu @menu
* tincctl runtime options:: * tinc runtime options::
* tincctl environment variables:: * tinc environment variables::
* tincctl commands:: * tinc commands::
* tincctl examples:: * tinc examples::
* tincctl top:: * tinc top::
@end menu @end menu
@c ================================================================== @c ==================================================================
@node tincctl runtime options @node tinc runtime options
@section tincctl runtime options @section tinc runtime options
@c from the manpage @c from the manpage
@table @option @table @option
@ -2151,8 +2158,8 @@ Output version information and exit.
@end table @end table
@c ================================================================== @c ==================================================================
@node tincctl environment variables @node tinc environment variables
@section tincctl environment variables @section tinc environment variables
@table @env @table @env
@cindex NETNAME @cindex NETNAME
@ -2162,8 +2169,8 @@ the value of this environment variable is used.
@end table @end table
@c ================================================================== @c ==================================================================
@node tincctl commands @node tinc commands
@section tincctl commands @section tinc commands
@c from the manpage @c from the manpage
@table @code @table @code
@ -2172,20 +2179,20 @@ the value of this environment variable is used.
Create initial configuration files and RSA and ECDSA keypairs with default length. Create initial configuration files and RSA and ECDSA keypairs with default length.
If no @var{name} for this node is given, it will be asked for. If no @var{name} for this node is given, it will be asked for.
@item config [get] @var{variable} @item get @var{variable}
Print the current value of configuration variable @var{variable}. Print the current value of configuration variable @var{variable}.
If more than one variable with the same name exists, If more than one variable with the same name exists,
the value of each of them will be printed on a separate line. the value of each of them will be printed on a separate line.
@item config [set] @var{variable} @var{value} @item set @var{variable} @var{value}
Set configuration variable @var{variable} to the given @var{value}. Set configuration variable @var{variable} to the given @var{value}.
All previously existing configuration variables with the same name are removed. All previously existing configuration variables with the same name are removed.
To set a variable for a specific host, use the notation @var{host}.@var{variable}. To set a variable for a specific host, use the notation @var{host}.@var{variable}.
@item config add @var{variable} @var{value} @item add @var{variable} @var{value}
As above, but without removing any previously existing configuration variables. As above, but without removing any previously existing configuration variables.
@item config del @var{variable} [@var{value}] @item del @var{variable} [@var{value}]
Remove configuration variables with the same name and @var{value}. Remove configuration variables with the same name and @var{value}.
If no @var{value} is given, all configuration variables with the same name will be removed. If no @var{value} is given, all configuration variables with the same name will be removed.
@ -2200,7 +2207,7 @@ Export the host configuration file of the local node to standard output.
Export all host configuration files to standard output. Export all host configuration files to standard output.
@item import [--force] @item import [--force]
Import host configuration file(s) generated by the tincctl export command from standard input. Import host configuration file(s) generated by the tinc export command from standard input.
Already existing host configuration files are not overwritten unless the option --force is used. Already existing host configuration files are not overwritten unless the option --force is used.
@item exchange [--force] @item exchange [--force]
@ -2263,7 +2270,7 @@ Sets debug level to @var{level}.
@item log [@var{level}] @item log [@var{level}]
Capture log messages from a running tinc daemon. Capture log messages from a running tinc daemon.
An optional debug level can be given that will be applied only for log messages sent to tincctl. An optional debug level can be given that will be applied only for log messages sent to tinc.
@item retry @item retry
Forces tinc to try to connect to all uplinks immediately. Forces tinc to try to connect to all uplinks immediately.
@ -2276,7 +2283,7 @@ it defaults to the maximum time of 15 minutes.
Closes the meta connection with the given @var{node}. Closes the meta connection with the given @var{node}.
@item top @item top
If tincctl is compiled with libcurses support, this will display live traffic statistics for all the known nodes, If tinc is compiled with libcurses support, this will display live traffic statistics for all the known nodes,
similar to the UNIX top command. similar to the UNIX top command.
See below for more information. See below for more information.
@ -2288,30 +2295,30 @@ such as tcpdump.
@end table @end table
@c ================================================================== @c ==================================================================
@node tincctl examples @node tinc examples
@section tincctl examples @section tinc examples
Examples of some commands: Examples of some commands:
@example @example
tincctl -n vpn dump graph | circo -Txlib tinc -n vpn dump graph | circo -Txlib
tincctl -n vpn pcap | tcpdump -r - tinc -n vpn pcap | tcpdump -r -
tincctl -n vpn top tinc -n vpn top
@end example @end example
Example of configuring tinc using tincctl: Example of configuring tinc using the tinc command:
@example @example
tincctl -n vpn init foo tinc -n vpn init foo
tincctl -n vpn config Subnet 192.168.1.0/24 tinc -n vpn add Subnet 192.168.1.0/24
tincctl -n vpn config bar.Address bar.example.com tinc -n vpn add bar.Address bar.example.com
tincctl -n vpn config ConnectTo bar tinc -n vpn add ConnectTo bar
tincctl -n vpn export | gpg --clearsign | mail -s "My config" vpnmaster@@example.com tinc -n vpn export | gpg --clearsign | mail -s "My config" vpnmaster@@example.com
@end example @end example
@c ================================================================== @c ==================================================================
@node tincctl top @node tinc top
@section tincctl top @section tinc top
The top command connects to a running tinc daemon and repeatedly queries its per-node traffic counters. The top command connects to a running tinc daemon and repeatedly queries its per-node traffic counters.
It displays a list of all the known nodes in the left-most column, It displays a list of all the known nodes in the left-most column,
@ -2442,7 +2449,7 @@ If the virtual network device is a `tun' device (a point-to-point tunnel),
there is no problem for the kernel to accept a packet. there is no problem for the kernel to accept a packet.
However, if it is a `tap' device (this is the only available type on FreeBSD), However, if it is a `tap' device (this is the only available type on FreeBSD),
the destination MAC address must match that of the virtual network interface. the destination MAC address must match that of the virtual network interface.
If tinc is in it's default routing mode, ARP does not work, so the correct destination MAC If tinc is in it's default routing mode, ARP does not work, so the correct destination MAC
can not be known by the sending host. can not be known by the sending host.
Tinc solves this by letting the receiving end detect the MAC address of its own virtual network interface Tinc solves this by letting the receiving end detect the MAC address of its own virtual network interface
and overwriting the destination MAC address of the received packet. and overwriting the destination MAC address of the received packet.
@ -2504,7 +2511,7 @@ daemon started with the --bypass-security option
and to read and write requests by hand, provided that one and to read and write requests by hand, provided that one
understands the numeric codes sent. understands the numeric codes sent.
The authentication scheme is described in @ref{Authentication protocol}. After a The authentication scheme is described in @ref{Security}. After a
successful authentication, the server and the client will exchange all the successful authentication, the server and the client will exchange all the
information about other tinc daemons and subnets they know of, so that both information about other tinc daemons and subnets they know of, so that both
sides (and all the other tinc daemons behind them) have their information sides (and all the other tinc daemons behind them) have their information
@ -2566,7 +2573,7 @@ message
------------------------------------------------------------------ ------------------------------------------------------------------
REQ_KEY origin destination REQ_KEY origin destination
| +--> name of the tinc daemon it wants the key from | +--> name of the tinc daemon it wants the key from
+----------> name of the daemon that wants the key +----------> name of the daemon that wants the key
ANS_KEY origin destination 4ae0b0a82d6e0078 91 64 4 ANS_KEY origin destination 4ae0b0a82d6e0078 91 64 4
| | \______________/ | | +--> MAC length | | \______________/ | | +--> MAC length
@ -2591,10 +2598,10 @@ destination.
@cindex PING @cindex PING
@cindex PONG @cindex PONG
@example @example
daemon message daemon message
------------------------------------------------------------------ ------------------------------------------------------------------
origin PING origin PING
dest. PONG dest. PONG
------------------------------------------------------------------ ------------------------------------------------------------------
@end example @end example
@ -2622,31 +2629,30 @@ the tinc project after TINC.
@cindex SVPN @cindex SVPN
But in order to be ``immune'' to eavesdropping, you'll have to encrypt But in order to be ``immune'' to eavesdropping, you'll have to encrypt
your data. Because tinc is a @emph{Secure} VPN (SVPN) daemon, it does your data. Because tinc is a @emph{Secure} VPN (SVPN) daemon, it does
exactly that: encrypt. exactly that: encrypt.
Tinc by default uses blowfish encryption with 128 bit keys in CBC mode, 32 bit However, encryption in itself does not prevent an attacker from modifying the encrypted data.
sequence numbers and 4 byte long message authentication codes to make sure Therefore, tinc also authenticates the data.
eavesdroppers cannot get and cannot change any information at all from the Finally, tinc uses sequence numbers (which themselves are also authenticated) to prevent an attacker from replaying valid packets.
packets they can intercept. The encryption algorithm and message authentication
algorithm can be changed in the configuration. The length of the message Since version 1.1pre3, tinc has two protocols used to protect your data; the legacy protocol, and the new Simple Peer-to-Peer Security (SPTPS) protocol.
authentication codes is also adjustable. The length of the key for the The SPTPS protocol is designed to address some weaknesses in the legacy protocol.
encryption algorithm is always the default length used by OpenSSL. The new authentication protocol is used when two nodes connect to each other that both have the ExperimentalProtocol option set to yes,
otherwise the legacy protocol will be used.
@menu @menu
* Authentication protocol:: * Legacy authentication protocol::
* Simple Peer-to-Peer Security::
* Encryption of network packets:: * Encryption of network packets::
* Security issues:: * Security issues::
@end menu @end menu
@c ================================================================== @c ==================================================================
@node Authentication protocol @node Legacy authentication protocol
@subsection Authentication protocol @subsection Legacy authentication protocol
@cindex authentication @cindex legacy authentication protocol
A new scheme for authentication in tinc has been devised, which offers some
improvements over the protocol used in 1.0pre2 and 1.0pre3. Explanation is
below.
@cindex ID @cindex ID
@cindex META_KEY @cindex META_KEY
@ -2660,28 +2666,50 @@ client <attempts connection>
server <accepts connection> server <accepts connection>
client ID client 12 client ID client 17.2
| +---> version | | +-> minor protocol version
+-------> name of tinc daemon | +----> major protocol version
+--------> name of tinc daemon
server ID server 12 server ID server 17.2
| +---> version | | +-> minor protocol version
+-------> name of tinc daemon | +----> major protocol version
+--------> name of tinc daemon
client META_KEY 5f0823a93e35b69e...7086ec7866ce582b client META_KEY 94 64 0 0 5f0823a93e35b69e...7086ec7866ce582b
\_________________________________/ | | | | \_________________________________/
+-> RSAKEYLEN bits totally random string S1, | | | | +-> RSAKEYLEN bits totally random string S1,
encrypted with server's public RSA key | | | | encrypted with server's public RSA key
| | | +-> compression level
| | +---> MAC length
| +------> digest algorithm NID
+---------> cipher algorithm NID
server META_KEY 6ab9c1640388f8f0...45d1a07f8a672630 server META_KEY 94 64 0 0 6ab9c1640388f8f0...45d1a07f8a672630
\_________________________________/ | | | | \_________________________________/
+-> RSAKEYLEN bits totally random string S2, | | | | +-> RSAKEYLEN bits totally random string S2,
encrypted with client's public RSA key | | | | encrypted with client's public RSA key
| | | +-> compression level
| | +---> MAC length
| +------> digest algorithm NID
+---------> cipher algorithm NID
--------------------------------------------------------------------------
@end example
The protocol allows each side to specify encryption algorithms and parameters,
but in practice they are always fixed, since older versions of tinc did not
allow them to be different from the default values. The cipher is always
Blowfish in OFB mode, the digest is SHA1, but the MAC length is zero and no
compression is used.
From now on: From now on:
- the client will symmetrically encrypt outgoing traffic using S1 @itemize
- the server will symmetrically encrypt outgoing traffic using S2 @item the client will symmetrically encrypt outgoing traffic using S1
@item the server will symmetrically encrypt outgoing traffic using S2
@end itemize
@example
--------------------------------------------------------------------------
client CHALLENGE da02add1817c1920989ba6ae2a49cecbda0 client CHALLENGE da02add1817c1920989ba6ae2a49cecbda0
\_________________________________/ \_________________________________/
+-> CHALLEN bits totally random string H1 +-> CHALLEN bits totally random string H1
@ -2701,57 +2729,188 @@ their identity. Further information is exchanged.
client ACK 655 123 0 client ACK 655 123 0
| | +-> options | | +-> options
| +----> estimated weight | +----> estimated weight
+--------> listening port of client +--------> listening port of client
server ACK 655 321 0 server ACK 655 321 0
| | +-> options | | +-> options
| +----> estimated weight | +----> estimated weight
+--------> listening port of server +--------> listening port of server
-------------------------------------------------------------------------- --------------------------------------------------------------------------
@end example @end example
This new scheme has several improvements, both in efficiency and security. This legacy authentication protocol has several weaknesses, pointed out by security export Peter Gutmann.
First, data is encrypted with RSA without padding.
Padding schemes are designed to prevent attacks when the size of the plaintext is not equal to the size of the RSA key.
Tinc always encrypts random nonces that have the same size as the RSA key, so we do not believe this leads to a break of the security.
There might be timing or other side-channel attacks against RSA encryption and decryption, tinc does not employ any protection against those.
Furthermore, both sides send identical messages to each other, there is no distinction between server and client,
which could make a MITM attack easier.
However, no exploit is known in which a third party who is not already trusted by other nodes in the VPN could gain access.
Finally, the RSA keys are used to directly encrypt the session keys, which means that if the RSA keys are compromised, it is possible to decrypt all previous VPN traffic.
In other words, the legacy protocol does not provide perfect forward secrecy.
First of all, the server sends exactly the same kind of messages over the wire @c ==================================================================
as the client. The previous versions of tinc first authenticated the client, @node Simple Peer-to-Peer Security
and then the server. This scheme even allows both sides to send their messages @subsection Simple Peer-to-Peer Security
simultaneously, there is no need to wait for the other to send something first. @cindex SPTPS
This means that any calculations that need to be done upon sending or receiving
a message can also be done in parallel. This is especially important when doing
RSA encryption/decryption. Given that these calculations are the main part of
the CPU time spent for the authentication, speed is improved by a factor 2.
Second, only one RSA encrypted message is sent instead of two. This reduces the The SPTPS protocol is designed to address the weaknesses in the legacy protocol.
amount of information attackers can see (and thus use for a cryptographic SPTPS is based on TLS 1.2, but has been simplified: there is no support for exchanging public keys, and there is no cipher suite negotiation.
attack). It also improves speed by a factor two, making the total speedup a Instead, SPTPS always uses a very strong cipher suite:
factor 4. peers authenticate each other using 521 bits ECC keys,
Diffie-Hellman using ephemeral 521 bits ECC keys is used to provide perfect forward secrecy (PFS),
AES-256-CTR is used for encryption, and HMAC-SHA-256 for message authentication.
Third, and most important: Similar to TLS, messages are split up in records.
The symmetric cipher keys are exchanged first, the challenge is done A complete logical record contains the following information:
afterwards. In the previous authentication scheme, because a man-in-the-middle
could pass the challenge/chal_reply phase (by just copying the messages between
the two real tinc daemons), but no information was exchanged that was really
needed to read the rest of the messages, the challenge/chal_reply phase was of
no real use. The man-in-the-middle was only stopped by the fact that only after
the ACK messages were encrypted with the symmetric cipher. Potentially, it
could even send it's own symmetric key to the server (if it knew the server's
public key) and read some of the metadata the server would send it (it was
impossible for the mitm to read actual network packets though). The new scheme
however prevents this.
This new scheme makes sure that first of all, symmetric keys are exchanged. The @itemize
rest of the messages are then encrypted with the symmetric cipher. Then, each @item uint32_t seqno (network byte order)
side can only read received messages if they have their private key. The @item uint16_t length (network byte order)
challenge is there to let the other side know that the private key is really @item uint8_t type
known, because a challenge reply can only be sent back if the challenge is @item opaque data[length]
decrypted correctly, and that can only be done with knowledge of the private @item opaque hmac[HMAC_SIZE] (HMAC over all preceding fields)
key. @end itemize
Fourth: the first thing that is sent via the symmetric cipher encrypted Depending on whether SPTPS records are sent via TCP or UDP, either the seqno or the length field is omitted on the wire
connection is a totally random string, so that there is no known plaintext (for (but they are still included in the calculation of the HMAC);
an attacker) in the beginning of the encrypted stream. for TCP packets are guaranteed to arrive in-order so we can infer the seqno, but packets can be split or merged, so we still need the length field to determine the boundaries between records;
for UDP packets we know that there is exactly one record per packet, and we know the length of a packet, but packets can be dropped, duplicated and/or reordered, so we need to include the seqno.
The type field is used to distinguish between application records or handshake records.
Types 0 to 127 are application records, type 128 is a handshake record, and types 129 to 255 are reserved.
Before the initial handshake, no fields are encrypted, and the HMAC field is not present.
After the authentication handshake, the length (if present), type and data fields are encrypted, and the HMAC field is present.
For UDP packets, the seqno field is not encrypted, as it is used to determine the value of the counter used for encryption.
The authentication consists of an exchange of Key EXchange, SIGnature and ACKnowledge messages, transmitted using type 128 records.
Overview:
@example
Initiator Responder
---------------------
KEX ->
<- KEX
SIG ->
<- SIG
...encrypt and HMAC using session keys from now on...
App ->
<- App
...
...
...key renegotiation starts here...
KEX ->
<- KEX
SIG ->
<- SIG
ACK ->
<- ACK
...encrypt and HMAC using new session keys from now on...
App ->
<- App
...
...
---------------------
@end example
Note that the responder does not need to wait before it receives the first KEX message,
it can immediately send its own once it has accepted an incoming connection.
Key EXchange message:
@itemize
@item uint8_t kex_version (always 0 in this version of SPTPS)
@item opaque nonce[32] (random number)
@item opaque ecdh_key[ECDH_SIZE]
@end itemize
SIGnature message:
@itemize
@item opaque ecdsa_signature[ECDSA_SIZE]
@end itemize
ACKnowledge message:
@itemize
@item empty (only sent after key renegotiation)
@end itemize
Remarks:
@itemize
@item At the start, both peers generate a random nonce and an Elliptic Curve public key and send it to the other in the KEX message.
@item After receiving the other's KEX message, both KEX messages are concatenated (see below),
and the result is signed using ECDSA.
The result is sent to the other.
@item After receiving the other's SIG message, the signature is verified.
If it is correct, the shared secret is calculated from the public keys exchanged in the KEX message using the Elliptic Curve Diffie-Helman algorithm.
@item The shared secret key is expanded using a PRF.
Both nonces and the application specific label are also used as input for the PRF.
@item An ACK message is sent only when doing key renegotiation, and is sent using the old encryption keys.
@item The expanded key is used to key the encryption and HMAC algorithms.
@end itemize
The signature is calculated over this string:
@itemize
@item uint8_t initiator (0 = local peer, 1 = remote peer is initiator)
@item opaque remote_kex_message[1 + 32 + ECDH_SIZE]
@item opaque local_kex_message[1 + 32 + ECDH_SIZE]
@item opaque label[label_length]
@end itemize
The PRF is calculated as follows:
@itemize
@item A HMAC using SHA512 is used, the shared secret is used as the key.
@item For each block of 64 bytes, a HMAC is calculated. For block n: hmac[n] =
HMAC_SHA512(hmac[n - 1] + seed)
@item For the first block (n = 1), hmac[0] is given by HMAC_SHA512(zeroes + seed),
where zeroes is a block of 64 zero bytes.
@end itemize
The seed is as follows:
@itemize
@item const char[13] "key expansion"
@item opaque responder_nonce[32]
@item opaque initiator_nonce[32]
@item opaque label[label_length]
@end itemize
The expanded key is used as follows:
@itemize
@item opaque responder_cipher_key[CIPHER_KEYSIZE]
@item opaque responder_digest_key[DIGEST_KEYSIZE]
@item opaque initiator_cipher_key[CIPHER_KEYSIZE]
@item opaque initiator_digest_key[DIGEST_KEYSIZE]
@end itemize
Where initiator_cipher_key is the key used by session initiator to encrypt
messages sent to the responder.
When using 521 bits EC keys, the AES-256-CTR cipher and HMAC-SHA-256 digest algorithm,
the sizes are as follows:
@example
ECDH_SIZE: 67 (= ceil(521/8) + 1)
ECDSA_SIZE: 141 (= 2 * ceil(521/8) + 9)
CIPHER_KEYSIZE: 48 (= 256/8 + 128/8)
DIGEST_KEYSIZE: 32 (= 256/8)
@end example
Note that the cipher key also includes the initial value for the counter.
@c ================================================================== @c ==================================================================
@node Encryption of network packets @node Encryption of network packets
@ -2761,11 +2920,11 @@ an attacker) in the beginning of the encrypted stream.
A data packet can only be sent if the encryption key is known to both A data packet can only be sent if the encryption key is known to both
parties, and the connection is activated. If the encryption key is not parties, and the connection is activated. If the encryption key is not
known, a request is sent to the destination using the meta connection known, a request is sent to the destination using the meta connection
to retrieve it. The packet is stored in a queue while waiting for the to retrieve it.
key to arrive.
@cindex UDP @cindex UDP
The UDP packet containing the network packet from the VPN has the following layout: The UDP packets can be either encrypted with the legacy protocol or with SPTPS.
In case of the legacy protocol, the UDP packet containing the network packet from the VPN has the following layout:
@example @example
... | IP header | UDP header | seqno | VPN packet | MAC | UDP trailer ... | IP header | UDP header | seqno | VPN packet | MAC | UDP trailer
@ -2775,12 +2934,38 @@ The UDP packet containing the network packet from the VPN has the following layo
Encrypted with symmetric cipher Encrypted with symmetric cipher
@end example @end example
So, the entire VPN packet is encrypted using a symmetric cipher, including a 32 bits So, the entire VPN packet is encrypted using a symmetric cipher, including a 32 bits
sequence number that is added in front of the actual VPN packet, to act as a unique sequence number that is added in front of the actual VPN packet, to act as a unique
IV for each packet and to prevent replay attacks. A message authentication code IV for each packet and to prevent replay attacks. A message authentication code
is added to the UDP packet to prevent alteration of packets. By default the is added to the UDP packet to prevent alteration of packets.
first 4 bytes of the digest are used for this, but this can be changed using Tinc by default encrypts network packets using Blowfish with 128 bit keys in CBC mode
the MACLength configuration variable. and uses 4 byte long message authentication codes to make sure
eavesdroppers cannot get and cannot change any information at all from the
packets they can intercept. The encryption algorithm and message authentication
algorithm can be changed in the configuration. The length of the message
authentication codes is also adjustable. The length of the key for the
encryption algorithm is always the default length used by OpenSSL.
The SPTPS protocol is described in @ref{Simple Peer-to-Peer Security}.
For comparison, this is how SPTPS UDP packets look:
@example
... | IP header | UDP header | seqno | type | VPN packet | MAC | UDP trailer
\__________________/\_____/
| |
V +---> digest algorithm
Encrypted with symmetric cipher
@end example
The difference is that the seqno is not encrypted, since the encryption cipher is used in CTR mode,
and therefore the seqno must be known before the packet can be decrypted.
Furthermore, the MAC is never truncated.
The SPTPS protocol always uses the AES-256-CTR cipher and HMAC-SHA-256 digest,
this cannot be changed.
@c ================================================================== @c ==================================================================
@node Security issues @node Security issues
@ -2803,8 +2988,10 @@ On the 15th of September 2003, Peter Gutmann posted a security analysis of tinc
1.0.1. He argues that the 32 bit sequence number used by tinc is not a good IV, 1.0.1. He argues that the 32 bit sequence number used by tinc is not a good IV,
that tinc's default length of 4 bytes for the MAC is too short, and he doesn't that tinc's default length of 4 bytes for the MAC is too short, and he doesn't
like tinc's use of RSA during authentication. We do not know of a security hole like tinc's use of RSA during authentication. We do not know of a security hole
in this version of tinc, but tinc's security is not as strong as TLS or IPsec. in the legacy protocol of tinc, but it is not as strong as TLS or IPsec.
We will address these issues in tinc 2.0.
This version of tinc comes with an improved protocol, called Simple Peer-to-Peer Security,
which aims to be as strong as TLS with one of the strongest cipher suites.
Cryptography is a hard thing to get right. We cannot make any Cryptography is a hard thing to get right. We cannot make any
guarantees. Time, review and feedback are the only things that can guarantees. Time, review and feedback are the only things that can

View file

@ -92,7 +92,7 @@ is omitted, the default is
Store a cookie in Store a cookie in
.Ar FILENAME .Ar FILENAME
which allows which allows
.Xr tincctl 8 .Xr tinc 8
to authenticate. to authenticate.
If If
.Ar FILE .Ar FILE
@ -186,7 +186,7 @@ If you find any bugs, report them to tinc@tinc-vpn.org.
.Sh TODO .Sh TODO
A lot, especially security auditing. A lot, especially security auditing.
.Sh SEE ALSO .Sh SEE ALSO
.Xr tincctl 8 , .Xr tinc 8 ,
.Xr tinc.conf 5 , .Xr tinc.conf 5 ,
.Pa http://www.tinc-vpn.org/ , .Pa http://www.tinc-vpn.org/ ,
.Pa http://www.cabal.org/ . .Pa http://www.cabal.org/ .

View file

@ -1,6 +1,6 @@
## Produce this file with automake to get Makefile.in ## Produce this file with automake to get Makefile.in
sbin_PROGRAMS = tincd tincctl sptps_test sbin_PROGRAMS = tincd tinc sptps_test
EXTRA_DIST = linux bsd solaris cygwin mingw openssl gcrypt EXTRA_DIST = linux bsd solaris cygwin mingw openssl gcrypt
@ -22,11 +22,11 @@ endif
nodist_tincd_SOURCES = \ nodist_tincd_SOURCES = \
device.c cipher.c crypto.c ecdh.c ecdsa.c digest.c prf.c rsa.c device.c cipher.c crypto.c ecdh.c ecdsa.c digest.c prf.c rsa.c
tincctl_SOURCES = \ tinc_SOURCES = \
utils.c getopt.c getopt1.c dropin.c \ utils.c getopt.c getopt1.c dropin.c \
info.c list.c subnet_parse.c tincctl.c top.c names.c info.c list.c subnet_parse.c tincctl.c top.c names.c
nodist_tincctl_SOURCES = \ nodist_tinc_SOURCES = \
ecdsagen.c rsagen.c ecdsagen.c rsagen.c
sptps_test_SOURCES = \ sptps_test_SOURCES = \
@ -37,7 +37,7 @@ if TUNEMU
tincd_SOURCES += bsd/tunemu.c tincd_SOURCES += bsd/tunemu.c
endif endif
tincctl_LDADD = $(READLINE_LIBS) $(CURSES_LIBS) tinc_LDADD = $(READLINE_LIBS) $(CURSES_LIBS)
DEFAULT_INCLUDES = DEFAULT_INCLUDES =

View file

@ -52,7 +52,7 @@ PRE_UNINSTALL = :
POST_UNINSTALL = : POST_UNINSTALL = :
build_triplet = @build@ build_triplet = @build@
host_triplet = @host@ host_triplet = @host@
sbin_PROGRAMS = tincd$(EXEEXT) tincctl$(EXEEXT) sptps_test$(EXEEXT) sbin_PROGRAMS = tincd$(EXEEXT) tinc$(EXEEXT) sptps_test$(EXEEXT)
@UML_TRUE@am__append_1 = uml_device.c @UML_TRUE@am__append_1 = uml_device.c
@VDE_TRUE@am__append_2 = vde_device.c @VDE_TRUE@am__append_2 = vde_device.c
@TUNEMU_TRUE@am__append_3 = bsd/tunemu.c @TUNEMU_TRUE@am__append_3 = bsd/tunemu.c
@ -79,14 +79,14 @@ am_sptps_test_OBJECTS = logger.$(OBJEXT) cipher.$(OBJEXT) \
sptps_test.$(OBJEXT) utils.$(OBJEXT) sptps_test.$(OBJEXT) utils.$(OBJEXT)
sptps_test_OBJECTS = $(am_sptps_test_OBJECTS) sptps_test_OBJECTS = $(am_sptps_test_OBJECTS)
sptps_test_LDADD = $(LDADD) sptps_test_LDADD = $(LDADD)
am_tincctl_OBJECTS = utils.$(OBJEXT) getopt.$(OBJEXT) \ am_tinc_OBJECTS = utils.$(OBJEXT) getopt.$(OBJEXT) getopt1.$(OBJEXT) \
getopt1.$(OBJEXT) dropin.$(OBJEXT) info.$(OBJEXT) \ dropin.$(OBJEXT) info.$(OBJEXT) list.$(OBJEXT) \
list.$(OBJEXT) subnet_parse.$(OBJEXT) tincctl.$(OBJEXT) \ subnet_parse.$(OBJEXT) tincctl.$(OBJEXT) top.$(OBJEXT) \
top.$(OBJEXT) names.$(OBJEXT) names.$(OBJEXT)
nodist_tincctl_OBJECTS = ecdsagen.$(OBJEXT) rsagen.$(OBJEXT) nodist_tinc_OBJECTS = ecdsagen.$(OBJEXT) rsagen.$(OBJEXT)
tincctl_OBJECTS = $(am_tincctl_OBJECTS) $(nodist_tincctl_OBJECTS) tinc_OBJECTS = $(am_tinc_OBJECTS) $(nodist_tinc_OBJECTS)
am__DEPENDENCIES_1 = am__DEPENDENCIES_1 =
tincctl_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1) tinc_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_1)
am__tincd_SOURCES_DIST = utils.c getopt.c getopt1.c list.c \ am__tincd_SOURCES_DIST = utils.c getopt.c getopt1.c list.c \
splay_tree.c dropin.c fake-getaddrinfo.c fake-getnameinfo.c \ splay_tree.c dropin.c fake-getaddrinfo.c fake-getnameinfo.c \
hash.c buffer.c conf.c connection.c control.c edge.c graph.c \ hash.c buffer.c conf.c connection.c control.c edge.c graph.c \
@ -128,10 +128,9 @@ COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
$(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
CCLD = $(CC) CCLD = $(CC)
LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@ LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
SOURCES = $(sptps_test_SOURCES) $(tincctl_SOURCES) \ SOURCES = $(sptps_test_SOURCES) $(tinc_SOURCES) $(nodist_tinc_SOURCES) \
$(nodist_tincctl_SOURCES) $(tincd_SOURCES) \ $(tincd_SOURCES) $(nodist_tincd_SOURCES)
$(nodist_tincd_SOURCES) DIST_SOURCES = $(sptps_test_SOURCES) $(tinc_SOURCES) \
DIST_SOURCES = $(sptps_test_SOURCES) $(tincctl_SOURCES) \
$(am__tincd_SOURCES_DIST) $(am__tincd_SOURCES_DIST)
am__can_run_installinfo = \ am__can_run_installinfo = \
case $$AM_UPDATE_INFO_DIR in \ case $$AM_UPDATE_INFO_DIR in \
@ -258,18 +257,18 @@ tincd_SOURCES = utils.c getopt.c getopt1.c list.c splay_tree.c \
nodist_tincd_SOURCES = \ nodist_tincd_SOURCES = \
device.c cipher.c crypto.c ecdh.c ecdsa.c digest.c prf.c rsa.c device.c cipher.c crypto.c ecdh.c ecdsa.c digest.c prf.c rsa.c
tincctl_SOURCES = \ tinc_SOURCES = \
utils.c getopt.c getopt1.c dropin.c \ utils.c getopt.c getopt1.c dropin.c \
info.c list.c subnet_parse.c tincctl.c top.c names.c info.c list.c subnet_parse.c tincctl.c top.c names.c
nodist_tincctl_SOURCES = \ nodist_tinc_SOURCES = \
ecdsagen.c rsagen.c ecdsagen.c rsagen.c
sptps_test_SOURCES = \ sptps_test_SOURCES = \
logger.c cipher.c crypto.c ecdh.c ecdsa.c digest.c prf.c \ logger.c cipher.c crypto.c ecdh.c ecdsa.c digest.c prf.c \
sptps.c sptps_test.c utils.c sptps.c sptps_test.c utils.c
tincctl_LDADD = $(READLINE_LIBS) $(CURSES_LIBS) tinc_LDADD = $(READLINE_LIBS) $(CURSES_LIBS)
DEFAULT_INCLUDES = DEFAULT_INCLUDES =
noinst_HEADERS = \ noinst_HEADERS = \
xalloc.h utils.h getopt.h list.h splay_tree.h dropin.h fake-getaddrinfo.h fake-getnameinfo.h fake-gai-errnos.h ipv6.h ipv4.h ethernet.h \ xalloc.h utils.h getopt.h list.h splay_tree.h dropin.h fake-getaddrinfo.h fake-getnameinfo.h fake-gai-errnos.h ipv6.h ipv4.h ethernet.h \
@ -357,9 +356,9 @@ clean-sbinPROGRAMS:
sptps_test$(EXEEXT): $(sptps_test_OBJECTS) $(sptps_test_DEPENDENCIES) $(EXTRA_sptps_test_DEPENDENCIES) sptps_test$(EXEEXT): $(sptps_test_OBJECTS) $(sptps_test_DEPENDENCIES) $(EXTRA_sptps_test_DEPENDENCIES)
@rm -f sptps_test$(EXEEXT) @rm -f sptps_test$(EXEEXT)
$(LINK) $(sptps_test_OBJECTS) $(sptps_test_LDADD) $(LIBS) $(LINK) $(sptps_test_OBJECTS) $(sptps_test_LDADD) $(LIBS)
tincctl$(EXEEXT): $(tincctl_OBJECTS) $(tincctl_DEPENDENCIES) $(EXTRA_tincctl_DEPENDENCIES) tinc$(EXEEXT): $(tinc_OBJECTS) $(tinc_DEPENDENCIES) $(EXTRA_tinc_DEPENDENCIES)
@rm -f tincctl$(EXEEXT) @rm -f tinc$(EXEEXT)
$(LINK) $(tincctl_OBJECTS) $(tincctl_LDADD) $(LIBS) $(LINK) $(tinc_OBJECTS) $(tinc_LDADD) $(LIBS)
tincd$(EXEEXT): $(tincd_OBJECTS) $(tincd_DEPENDENCIES) $(EXTRA_tincd_DEPENDENCIES) tincd$(EXEEXT): $(tincd_OBJECTS) $(tincd_DEPENDENCIES) $(EXTRA_tincd_DEPENDENCIES)
@rm -f tincd$(EXEEXT) @rm -f tincd$(EXEEXT)
$(LINK) $(tincd_OBJECTS) $(tincd_LDADD) $(LIBS) $(LINK) $(tincd_OBJECTS) $(tincd_LDADD) $(LIBS)

View file

@ -245,6 +245,12 @@ bool event_loop(void) {
return true; return true;
} }
void event_flush_output(void) {
for splay_each(io_t, io, &io_tree)
if(FD_ISSET(io->fd, &writefds))
io->cb(io->data, IO_WRITE);
}
void event_exit(void) { void event_exit(void) {
running = false; running = false;
} }

View file

@ -65,6 +65,7 @@ extern void signal_add(signal_t *sig, signal_cb_t cb, void *data, int signum);
extern void signal_del(signal_t *sig); extern void signal_del(signal_t *sig);
extern bool event_loop(void); extern bool event_loop(void);
extern void event_flush_output(void);
extern void event_exit(void); extern void event_exit(void);
#endif #endif

View file

@ -204,7 +204,7 @@ static void check_reachability(void) {
for splay_each(node_t, n, node_tree) { for splay_each(node_t, n, node_tree) {
if(n->status.visited != n->status.reachable) { if(n->status.visited != n->status.reachable) {
n->status.reachable = !n->status.reachable; n->status.reachable = !n->status.reachable;
n->last_state_change = time(NULL); n->last_state_change = now.tv_sec;
if(n->status.reachable) { if(n->status.reachable) {
logger(DEBUG_TRAFFIC, LOG_DEBUG, "Node %s (%s) became reachable", logger(DEBUG_TRAFFIC, LOG_DEBUG, "Node %s (%s) became reachable",

View file

@ -80,6 +80,7 @@ static DWORD WINAPI tapreader(void *bla) {
packet.len = len; packet.len = len;
packet.priority = 0; packet.priority = 0;
route(myself, &packet); route(myself, &packet);
event_flush_output();
LeaveCriticalSection(&mutex); LeaveCriticalSection(&mutex);
} }
} }

View file

@ -406,7 +406,7 @@ int reload_configuration(void) {
free(fname); free(fname);
} }
last_config_check = time(NULL); last_config_check = now.tv_sec;
return 0; return 0;
} }

View file

@ -135,6 +135,7 @@ extern int udp_sndbuf;
extern bool do_prune; extern bool do_prune;
extern char *myport; extern char *myport;
extern int autoconnect; extern int autoconnect;
extern bool disablebuggypeers;
extern int contradicting_add_edge; extern int contradicting_add_edge;
extern int contradicting_del_edge; extern int contradicting_del_edge;
extern time_t last_config_check; extern time_t last_config_check;

View file

@ -443,6 +443,9 @@ static void receive_udppacket(node_t *n, vpn_packet_t *inpkt) {
void receive_tcppacket(connection_t *c, const char *buffer, int len) { void receive_tcppacket(connection_t *c, const char *buffer, int len) {
vpn_packet_t outpkt; vpn_packet_t outpkt;
if(len > sizeof outpkt.data)
return;
outpkt.len = len; outpkt.len = len;
if(c->options & OPTION_TCPONLY) if(c->options & OPTION_TCPONLY)
outpkt.priority = 0; outpkt.priority = 0;
@ -458,7 +461,7 @@ static void send_sptps_packet(node_t *n, vpn_packet_t *origpkt) {
logger(DEBUG_TRAFFIC, LOG_INFO, "No valid key known yet for %s (%s)", n->name, n->hostname); logger(DEBUG_TRAFFIC, LOG_INFO, "No valid key known yet for %s (%s)", n->name, n->hostname);
if(!n->status.waitingforkey) if(!n->status.waitingforkey)
send_req_key(n); send_req_key(n);
else if(n->last_req_key + 10 < time(NULL)) { else if(n->last_req_key + 10 < now.tv_sec) {
logger(DEBUG_ALWAYS, LOG_DEBUG, "No key from %s after 10 seconds, restarting SPTPS", n->name); logger(DEBUG_ALWAYS, LOG_DEBUG, "No key from %s after 10 seconds, restarting SPTPS", n->name);
sptps_stop(&n->sptps); sptps_stop(&n->sptps);
n->status.waitingforkey = false; n->status.waitingforkey = false;

View file

@ -52,6 +52,7 @@ char *proxyuser;
char *proxypass; char *proxypass;
proxytype_t proxytype; proxytype_t proxytype;
int autoconnect; int autoconnect;
bool disablebuggypeers;
char *scriptinterpreter; char *scriptinterpreter;
char *scriptextension; char *scriptextension;
@ -598,6 +599,8 @@ bool setup_myself_reloadable(void) {
get_config_int(lookup_config(config_tree, "AutoConnect"), &autoconnect); get_config_int(lookup_config(config_tree, "AutoConnect"), &autoconnect);
get_config_bool(lookup_config(config_tree, "DisableBuggyPeers"), &disablebuggypeers);
return true; return true;
} }
@ -751,7 +754,7 @@ static bool setup_myself(void) {
myself->nexthop = myself; myself->nexthop = myself;
myself->via = myself; myself->via = myself;
myself->status.reachable = true; myself->status.reachable = true;
myself->last_state_change = time(NULL); myself->last_state_change = now.tv_sec;
myself->status.sptps = experimental; myself->status.sptps = experimental;
node_add(myself); node_add(myself);
@ -958,7 +961,7 @@ static bool setup_myself(void) {
return false; return false;
} }
last_config_check = time(NULL); last_config_check = now.tv_sec;
return true; return true;
} }

View file

@ -294,7 +294,7 @@ void retry_outgoing(outgoing_t *outgoing) {
void finish_connecting(connection_t *c) { void finish_connecting(connection_t *c) {
logger(DEBUG_CONNECTIONS, LOG_INFO, "Connected to %s (%s)", c->name, c->hostname); logger(DEBUG_CONNECTIONS, LOG_INFO, "Connected to %s (%s)", c->name, c->hostname);
c->last_ping_time = time(NULL); c->last_ping_time = now.tv_sec;
c->status.connecting = false; c->status.connecting = false;
send_id(c); send_id(c);
@ -349,6 +349,9 @@ static void do_outgoing_pipe(connection_t *c, char *command) {
} }
static void handle_meta_write(connection_t *c) { static void handle_meta_write(connection_t *c) {
if(c->outbuf.len <= c->outbuf.offset)
return;
ssize_t outlen = send(c->socket, c->outbuf.data + c->outbuf.offset, c->outbuf.len - c->outbuf.offset, 0); ssize_t outlen = send(c->socket, c->outbuf.data + c->outbuf.offset, c->outbuf.len - c->outbuf.offset, 0);
if(outlen <= 0) { if(outlen <= 0) {
if(!errno || errno == EPIPE) { if(!errno || errno == EPIPE) {
@ -505,7 +508,7 @@ begin:
c->outdigest = myself->connection->outdigest; c->outdigest = myself->connection->outdigest;
c->outmaclength = myself->connection->outmaclength; c->outmaclength = myself->connection->outmaclength;
c->outcompression = myself->connection->outcompression; c->outcompression = myself->connection->outcompression;
c->last_ping_time = time(NULL); c->last_ping_time = now.tv_sec;
connection_add(c); connection_add(c);
@ -568,7 +571,7 @@ void handle_new_meta_connection(void *data, int flags) {
c->address = sa; c->address = sa;
c->hostname = sockaddr2hostname(&sa); c->hostname = sockaddr2hostname(&sa);
c->socket = fd; c->socket = fd;
c->last_ping_time = time(NULL); c->last_ping_time = now.tv_sec;
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection from %s", c->hostname); logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection from %s", c->hostname);
@ -607,7 +610,7 @@ void handle_new_unix_connection(void *data, int flags) {
c->address = sa; c->address = sa;
c->hostname = xstrdup("localhost port unix"); c->hostname = xstrdup("localhost port unix");
c->socket = fd; c->socket = fd;
c->last_ping_time = time(NULL); c->last_ping_time = now.tv_sec;
logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection from %s", c->hostname); logger(DEBUG_CONNECTIONS, LOG_NOTICE, "Connection from %s", c->hostname);

View file

@ -195,7 +195,7 @@ bool seen_request(const char *request) {
} else { } else {
new = xmalloc(sizeof *new); new = xmalloc(sizeof *new);
new->request = xstrdup(request); new->request = xstrdup(request);
new->firstseen = time(NULL); new->firstseen = now.tv_sec;
splay_insert(past_request_tree, new); splay_insert(past_request_tree, new);
timeout_add(&past_request_timeout, age_past_requests, NULL, &(struct timeval){10, rand() % 100000}); timeout_add(&past_request_timeout, age_past_requests, NULL, &(struct timeval){10, rand() % 100000});
return false; return false;

View file

@ -160,7 +160,7 @@ bool id_h(connection_t *c, const char *request) {
if(name[0] == '^' && !strcmp(name + 1, controlcookie)) { if(name[0] == '^' && !strcmp(name + 1, controlcookie)) {
c->status.control = true; c->status.control = true;
c->allow_request = CONTROL; c->allow_request = CONTROL;
c->last_ping_time = time(NULL) + 3600; c->last_ping_time = now.tv_sec + 3600;
free(c->name); free(c->name);
c->name = xstrdup("<control>"); c->name = xstrdup("<control>");
@ -510,6 +510,17 @@ bool send_ack(connection_t *c) {
static void send_everything(connection_t *c) { static void send_everything(connection_t *c) {
/* Send all known subnets and edges */ /* Send all known subnets and edges */
if(disablebuggypeers) {
static struct {
vpn_packet_t pkt;
char pad[MAXBUFSIZE - MAXSIZE];
} zeropkt;
memset(&zeropkt, 0, sizeof zeropkt);
zeropkt.pkt.len = MAXBUFSIZE;
send_tcppacket(c, &zeropkt.pkt);
}
if(tunnelserver) { if(tunnelserver) {
for splay_each(subnet_t, s, myself->subnet_tree) for splay_each(subnet_t, s, myself->subnet_tree)
send_add_subnet(c, s); send_add_subnet(c, s);

View file

@ -111,7 +111,7 @@ bool send_req_key(node_t *to) {
sptps_stop(&to->sptps); sptps_stop(&to->sptps);
to->status.validkey = false; to->status.validkey = false;
to->status.waitingforkey = true; to->status.waitingforkey = true;
to->last_req_key = time(NULL); to->last_req_key = now.tv_sec;
to->incompression = myself->incompression; to->incompression = myself->incompression;
return sptps_start(&to->sptps, to, true, true, myself->connection->ecdsa, to->ecdsa, label, sizeof label, send_initial_sptps_data, receive_sptps_record); return sptps_start(&to->sptps, to, true, true, myself->connection->ecdsa, to->ecdsa, label, sizeof label, send_initial_sptps_data, receive_sptps_record);
} }
@ -169,7 +169,7 @@ static bool req_key_ext_h(connection_t *c, const char *request, node_t *from, in
sptps_stop(&from->sptps); sptps_stop(&from->sptps);
from->status.validkey = false; from->status.validkey = false;
from->status.waitingforkey = true; from->status.waitingforkey = true;
from->last_req_key = time(NULL); from->last_req_key = now.tv_sec;
sptps_start(&from->sptps, from, false, true, myself->connection->ecdsa, from->ecdsa, label, sizeof label, send_sptps_data, receive_sptps_record); sptps_start(&from->sptps, from, false, true, myself->connection->ecdsa, from->ecdsa, label, sizeof label, send_sptps_data, receive_sptps_record);
sptps_receive_data(&from->sptps, buf, len); sptps_receive_data(&from->sptps, buf, len);
return true; return true;

View file

@ -89,7 +89,7 @@ bool termreq_h(connection_t *c, const char *request) {
bool send_ping(connection_t *c) { bool send_ping(connection_t *c) {
c->status.pinged = true; c->status.pinged = true;
c->last_ping_time = time(NULL); c->last_ping_time = now.tv_sec;
return send_request(c, "%d", PING); return send_request(c, "%d", PING);
} }

View file

@ -229,7 +229,7 @@ static void learn_mac(mac_t *address) {
subnet = new_subnet(); subnet = new_subnet();
subnet->type = SUBNET_MAC; subnet->type = SUBNET_MAC;
subnet->expires = time(NULL) + macexpire; subnet->expires = now.tv_sec + macexpire;
subnet->net.mac.address = *address; subnet->net.mac.address = *address;
subnet->weight = 10; subnet->weight = 10;
subnet_add(myself, subnet); subnet_add(myself, subnet);
@ -244,7 +244,7 @@ static void learn_mac(mac_t *address) {
timeout_add(&age_subnets_timeout, age_subnets, NULL, &(struct timeval){10, rand() % 100000}); timeout_add(&age_subnets_timeout, age_subnets, NULL, &(struct timeval){10, rand() % 100000});
} else { } else {
if(subnet->expires) if(subnet->expires)
subnet->expires = time(NULL) + macexpire; subnet->expires = now.tv_sec + macexpire;
} }
} }

View file

@ -77,8 +77,8 @@ int main(int argc, char *argv[]) {
memset(&hint, 0, sizeof hint); memset(&hint, 0, sizeof hint);
hint.ai_family = AF_UNSPEC; hint.ai_family = AF_UNSPEC;
hint.ai_socktype = SOCK_STREAM; hint.ai_socktype = datagram ? SOCK_DGRAM : SOCK_STREAM;
hint.ai_protocol = IPPROTO_TCP; hint.ai_protocol = datagram ? IPPROTO_UDP : IPPROTO_TCP;
hint.ai_flags = initiator ? 0 : AI_PASSIVE; hint.ai_flags = initiator ? 0 : AI_PASSIVE;
if(getaddrinfo(initiator ? argv[3] : NULL, initiator ? argv[4] : argv[3], &hint, &ai) || !ai) { if(getaddrinfo(initiator ? argv[3] : NULL, initiator ? argv[4] : argv[3], &hint, &ai) || !ai) {
@ -86,7 +86,7 @@ int main(int argc, char *argv[]) {
return 1; return 1;
} }
int sock = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP); int sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
if(sock < 0) { if(sock < 0) {
fprintf(stderr, "Could not create socket: %s\n", strerror(errno)); fprintf(stderr, "Could not create socket: %s\n", strerror(errno));
return 1; return 1;
@ -106,16 +106,35 @@ int main(int argc, char *argv[]) {
fprintf(stderr, "Could not bind socket: %s\n", strerror(errno)); fprintf(stderr, "Could not bind socket: %s\n", strerror(errno));
return 1; return 1;
} }
if(listen(sock, 1)) {
fprintf(stderr, "Could not listen on socket: %s\n", strerror(errno));
return 1;
}
fprintf(stderr, "Listening...\n");
sock = accept(sock, NULL, NULL); if(!datagram) {
if(sock < 0) { if(listen(sock, 1)) {
fprintf(stderr, "Could not accept connection: %s\n", strerror(errno)); fprintf(stderr, "Could not listen on socket: %s\n", strerror(errno));
return 1; return 1;
}
fprintf(stderr, "Listening...\n");
sock = accept(sock, NULL, NULL);
if(sock < 0) {
fprintf(stderr, "Could not accept connection: %s\n", strerror(errno));
return 1;
}
} else {
fprintf(stderr, "Listening...\n");
char buf[65536];
struct sockaddr addr;
socklen_t addrlen = sizeof addr;
if(recvfrom(sock, buf, sizeof buf, MSG_PEEK, &addr, &addrlen) <= 0) {
fprintf(stderr, "Could not read from socket: %s\n", strerror(errno));
return 1;
}
if(connect(sock, &addr, addrlen)) {
fprintf(stderr, "Could not accept connection: %s\n", strerror(errno));
return 1;
}
} }
fprintf(stderr, "Connected\n"); fprintf(stderr, "Connected\n");

View file

@ -112,11 +112,10 @@ static void usage(bool status) {
"\n" "\n"
"Valid commands are:\n" "Valid commands are:\n"
" init [name] Create initial configuration files.\n" " init [name] Create initial configuration files.\n"
" config Change configuration:\n" " get VARIABLE Print current value of VARIABLE\n"
" [get] VARIABLE - print current value of VARIABLE\n" " set VARIABLE VALUE Set VARIABLE to VALUE\n"
" [set] VARIABLE VALUE - set VARIABLE to VALUE\n" " add VARIABLE VALUE Add VARIABLE with the given VALUE\n"
" add VARIABLE VALUE - add VARIABLE with the given VALUE\n" " del VARIABLE [VALUE] Remove VARIABLE [only ones with watching VALUE]\n"
" del VARIABLE [VALUE] - remove VARIABLE [only ones with watching VALUE]\n"
" start [tincd options] Start tincd.\n" " start [tincd options] Start tincd.\n"
" stop Stop tincd.\n" " stop Stop tincd.\n"
" restart Restart tincd.\n" " restart Restart tincd.\n"
@ -1148,7 +1147,7 @@ static int cmd_top(int argc, char *argv[]) {
top(fd); top(fd);
return 0; return 0;
#else #else
fprintf(stderr, "This version of tincctl was compiled without support for the curses library.\n"); fprintf(stderr, "This version of tinc was compiled without support for the curses library.\n");
return 1; return 1;
#endif #endif
} }
@ -1199,10 +1198,11 @@ static int rstrip(char *value) {
return len; return len;
} }
static char *get_my_name() { static char *get_my_name(bool verbose) {
FILE *f = fopen(tinc_conf, "r"); FILE *f = fopen(tinc_conf, "r");
if(!f) { if(!f) {
fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno)); if(verbose)
fprintf(stderr, "Could not open %s: %s\n", tinc_conf, strerror(errno));
return NULL; return NULL;
} }
@ -1228,7 +1228,8 @@ static char *get_my_name() {
} }
fclose(f); fclose(f);
fprintf(stderr, "Could not find Name in %s.\n", tinc_conf); if(verbose)
fprintf(stderr, "Could not find Name in %s.\n", tinc_conf);
return NULL; return NULL;
} }
@ -1309,6 +1310,9 @@ static int cmd_config(int argc, char *argv[]) {
return 1; return 1;
} }
if(strcasecmp(argv[0], "config"))
argv--, argc++;
int action = -2; int action = -2;
if(!strcasecmp(argv[1], "get")) { if(!strcasecmp(argv[1], "get")) {
argv++, argc--; argv++, argc--;
@ -1402,7 +1406,7 @@ static int cmd_config(int argc, char *argv[]) {
/* Should this go into our own host config file? */ /* Should this go into our own host config file? */
if(!node && !(variables[i].type & VAR_SERVER)) { if(!node && !(variables[i].type & VAR_SERVER)) {
node = get_my_name(); node = get_my_name(true);
if(!node) if(!node)
return 1; return 1;
} }
@ -1692,6 +1696,9 @@ static int cmd_generate_keys(int argc, char *argv[]) {
return 1; return 1;
} }
if(!name)
name = get_my_name(false);
return !(rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true) && ecdsa_keygen(true)); return !(rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true) && ecdsa_keygen(true));
} }
@ -1701,6 +1708,9 @@ static int cmd_generate_rsa_keys(int argc, char *argv[]) {
return 1; return 1;
} }
if(!name)
name = get_my_name(false);
return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true); return !rsa_keygen(argc > 1 ? atoi(argv[1]) : 2048, true);
} }
@ -1710,6 +1720,9 @@ static int cmd_generate_ecdsa_keys(int argc, char *argv[]) {
return 1; return 1;
} }
if(!name)
name = get_my_name(false);
return !ecdsa_keygen(true); return !ecdsa_keygen(true);
} }
@ -1831,7 +1844,7 @@ static int cmd_export(int argc, char *argv[]) {
return 1; return 1;
} }
char *name = get_my_name(); char *name = get_my_name(true);
if(!name) if(!name)
return 1; return 1;
@ -1961,6 +1974,7 @@ static int cmd_exchange_all(int argc, char *argv[]) {
static const struct { static const struct {
const char *command; const char *command;
int (*function)(int argc, char *argv[]); int (*function)(int argc, char *argv[]);
bool hidden;
} commands[] = { } commands[] = {
{"start", cmd_start}, {"start", cmd_start},
{"stop", cmd_stop}, {"stop", cmd_stop},
@ -1976,7 +1990,11 @@ static const struct {
{"pcap", cmd_pcap}, {"pcap", cmd_pcap},
{"log", cmd_log}, {"log", cmd_log},
{"pid", cmd_pid}, {"pid", cmd_pid},
{"config", cmd_config}, {"config", cmd_config, true},
{"add", cmd_config},
{"del", cmd_config},
{"get", cmd_config},
{"set", cmd_config},
{"init", cmd_init}, {"init", cmd_init},
{"generate-keys", cmd_generate_keys}, {"generate-keys", cmd_generate_keys},
{"generate-rsa-keys", cmd_generate_rsa_keys}, {"generate-rsa-keys", cmd_generate_rsa_keys},
@ -2003,7 +2021,7 @@ static char *complete_command(const char *text, int state) {
i++; i++;
while(commands[i].command) { while(commands[i].command) {
if(!strncasecmp(commands[i].command, text, strlen(text))) if(!commands[i].hidden && !strncasecmp(commands[i].command, text, strlen(text)))
return xstrdup(commands[i].command); return xstrdup(commands[i].command);
i++; i++;
} }
@ -2030,42 +2048,24 @@ static char *complete_dump(const char *text, int state) {
} }
static char *complete_config(const char *text, int state) { static char *complete_config(const char *text, int state) {
const char *sub[] = {"get", "set", "add", "del"};
static int i; static int i;
if(!state) {
i = 0;
if(!strchr(rl_line_buffer + 7, ' '))
i = -4;
else {
bool found = false;
for(int i = 0; i < 4; i++) {
if(!strncasecmp(rl_line_buffer + 7, sub[i], strlen(sub[i])) && rl_line_buffer[7 + strlen(sub[i])] == ' ') {
found = true;
break;
}
}
if(!found)
return NULL;
}
} else {
i++;
}
while(i < 0 || variables[i].name) { if(!state)
if(i < 0 && !strncasecmp(sub[i + 4], text, strlen(text))) i = 0;
return xstrdup(sub[i + 4]); else
if(i >= 0) { i++;
char *dot = strchr(text, '.');
if(dot) { while(variables[i].name) {
if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) { char *dot = strchr(text, '.');
char *match; if(dot) {
xasprintf(&match, "%.*s.%s", dot - text, text, variables[i].name); if((variables[i].type & VAR_HOST) && !strncasecmp(variables[i].name, dot + 1, strlen(dot + 1))) {
return match; char *match;
} xasprintf(&match, "%.*s.%s", dot - text, text, variables[i].name);
} else { return match;
if(!strncasecmp(variables[i].name, text, strlen(text)))
return xstrdup(variables[i].name);
} }
} else {
if(!strncasecmp(variables[i].name, text, strlen(text)))
return xstrdup(variables[i].name);
} }
i++; i++;
} }
@ -2118,7 +2118,13 @@ static char **completion (const char *text, int start, int end) {
matches = rl_completion_matches(text, complete_command); matches = rl_completion_matches(text, complete_command);
else if(!strncasecmp(rl_line_buffer, "dump ", 5)) else if(!strncasecmp(rl_line_buffer, "dump ", 5))
matches = rl_completion_matches(text, complete_dump); matches = rl_completion_matches(text, complete_dump);
else if(!strncasecmp(rl_line_buffer, "config ", 7)) else if(!strncasecmp(rl_line_buffer, "add ", 4))
matches = rl_completion_matches(text, complete_config);
else if(!strncasecmp(rl_line_buffer, "del ", 4))
matches = rl_completion_matches(text, complete_config);
else if(!strncasecmp(rl_line_buffer, "get ", 4))
matches = rl_completion_matches(text, complete_config);
else if(!strncasecmp(rl_line_buffer, "set ", 4))
matches = rl_completion_matches(text, complete_config); matches = rl_completion_matches(text, complete_config);
else if(!strncasecmp(rl_line_buffer, "info ", 5)) else if(!strncasecmp(rl_line_buffer, "info ", 5))
matches = rl_completion_matches(text, complete_info); matches = rl_completion_matches(text, complete_info);

View file

@ -346,7 +346,8 @@ int main(int argc, char **argv) {
/* Slllluuuuuuurrrrp! */ /* Slllluuuuuuurrrrp! */
srand(time(NULL)); gettimeofday(&now, NULL);
srand(now.tv_sec + now.tv_usec);
crypto_init(); crypto_init();
if(!read_server_config()) if(!read_server_config())