Adding even more stuff from the CABAL branch.

This commit is contained in:
Guus Sliepen 2002-04-12 08:25:01 +00:00
parent 191dcd5add
commit 9f2c50e159
6 changed files with 869 additions and 0 deletions

3
cvsusers Normal file
View file

@ -0,0 +1,3 @@
zarq:Ivo Timmermans <itimmermans@bigfoot.com>
guus:Guus Sliepen <guus@sliepen.warande.net>
wsl:Wessel Dankers <wsl@nl.linux.org>

354
doc/CONNECTIVITY Normal file
View file

@ -0,0 +1,354 @@
This document describes how nodes in a VPN find and connect to eachother and
maintain a stable network.
Copyright 2001-2002 Guus Sliepen <guus@sliepen.warande.net>
Permission is granted to make and distribute verbatim copies of
this documentation provided the copyright notice and this
permission notice are preserved on all copies.
Permission is granted to copy and distribute modified versions of
this documentation under the conditions for verbatim copying,
provided that the entire resulting derived work is distributed
under the terms of a permission notice identical to this one.
$Id: CONNECTIVITY,v 1.2 2002/04/12 08:25:01 guus Exp $
1. Problem
==========
We have a set of nodes (A, B, C, ...) that are part of the same VPN. They need
to connect to eachother and form a single graph that satisfies the tree
property.
There is the possibility that loops are formed, the offending connections must
be eliminated.
Suppose we start with two smaller graphs that want to form a single larger
graph. Both graphs consist of three nodes:
A-----B-----C
D-----E-----F
It is very well possible that A wants to connect to D, and F wants to connect
to C, both at the same time. The following loop will occur:
A-----B-----C
| ^
| |
v |
D-----E-----F
The situation described here is totally symmetric, there is no preference to
one connection over the other. The problem of resolving the loop, maintaining
consistency and stability is therefore not a trivial one.
What happens when A---D and C---F are connected to eachother? They exchange
lists of known hosts. A knows of B and C, and D knows of E and F. The protocol
defines ADD_HOST messages, from now on we will say that "node X sends and
ADD_HOST(Y) to Z".
There are two possible scenarios: either both A---D and C---F finish
authentication at the same time, or A---D finishes first, so that ADD_HOST
messages will reach C and F before they finish authentication.
1.1 A---D finishes first
------------------------
After A---D authentication finishes the following actions are taken:
1 A sends ADD_HOST(B) to D
A sends ADD_HOST(C) to D
D sends ADD_HOST(E) to A
D sends ADD_HOST(F) to A
2 A sends ADD_HOST(D) to B
A receives ADD_HOST(E) from D:
A sends ADD_HOST(E) to B
A receives ADD_HOST(F) from D:
A sends ADD_HOST(F) to B
D sends ADD_HOST(A) to E
D receives ADD_HOST(B) from A:
D sends ADD_HOST(B) to E
D receives ADD_HOST(C) from A:
D sends ADD_HOST(C) to E
3 B receives ADD_HOST(D) from A,
B sends ADD_HOST(D) to C
B receives ADD_HOST(E) from A:
B sends ADD_HOST(E) to C
B receives ADD_HOST(F) from A:
B sends ADD_HOST(F) to C
E receives ADD_HOST(A) from D:
E sends ADD_HOST(A) to F
E receives ADD_HOST(B) from D:
E sends ADD_HOST(B) to F
E receives ADD_HOST(C) from D:
E sends ADD_HOST(C) to F
4 C receives ADD_HOST(D) from B.
C receives ADD_HOST(E) from B.
C receives ADD_HOST(F) from B.
F receives ADD_HOST(A) from E.
F receives ADD_HOST(B) from E.
F receives ADD_HOST(C) from E.
Then C---F authentication finishes, the following actions are taken:
1 C notes that F is already known:
Connection is closed.
F notes that C is already known:
Connection is closed.
1.2 Both A---D and C---F finish at the same time.
-------------------------------------------------
1 A sends ADD_HOST(B) to D
A sends ADD_HOST(C) to D
D sends ADD_HOST(E) to A
D sends ADD_HOST(F) to A
C sends ADD_HOST(A) to F
C sends ADD_HOST(B) to F
F sends ADD_HOST(D) to C
F sends ADD_HOST(E) to C
2 A sends ADD_HOST(D) to B
A receives ADD_HOST(E) from D:
A sends ADD_HOST(E) to B
A receives ADD_HOST(F) from D:
A sends ADD_HOST(F) to B
D sends ADD_HOST(A) to E
D receives ADD_HOST(B) from A:
D sends ADD_HOST(B) to E
D receives ADD_HOST(C) from A:
D sends ADD_HOST(C) to E
C sends ADD_HOST(F) to B
C receives ADD_HOST(D) from F:
A sends ADD_HOST(D) to B
C receives ADD_HOST(E) from F:
A sends ADD_HOST(E) to B
F sends ADD_HOSTS(C) to E
F receives ADD_HOST(A) from C:
D sends ADD_HOST(A) to E
F receives ADD_HOST(B) from C:
D sends ADD_HOST(B) to E
3 B receives ADD_HOST(D) from A,
B sends ADD_HOST(D) to C
B receives ADD_HOST(E) from A:
B sends ADD_HOST(E) to C
B receives ADD_HOST(F) from A:
B sends ADD_HOST(F) to C
E receives ADD_HOST(A) from D:
E sends ADD_HOST(A) to F
E receives ADD_HOST(B) from D:
E sends ADD_HOST(B) to F
E receives ADD_HOST(C) from D:
E sends ADD_HOST(C) to F
B receives ADD_HOST(F) from C, and notes that is is already known:
<insert solution here>
B receives ADD_HOST(D) from C, and notes that is is already known:
<insert solution here>
B receives ADD_HOST(E) from C, and notes that is is already known:
<insert solution here>
E receives ADD_HOST(C) from F, and notes that is is already known:
<insert solution here>
E receives ADD_HOST(A) from F, and notes that is is already known:
<insert solution here>
E receives ADD_HOST(B) from F, and notes that is is already known:
<insert solution here>
4 A receives ADD_HOST(D) from B, and notes that it is already known:
<insert solution here>
A receives ADD_HOST(E) from B, and notes that it is already known:
<insert solution here>
A receives ADD_HOST(F) from B, and notes that it is already known:
<insert solution here>
F receives ADD_HOST(A) from E, and notes that it is already known:
<insert solution here>
F receives ADD_HOST(B) from E, and notes that it is already known:
<insert solution here>
F receives ADD_HOST(B) from E, and notes that it is already known:
<insert solution here>
...
1.2.1 Augmenting ADD_HOST
-------------------------
A solution would be to augment ADD_HOST with an extra parameter, the nexthop of
the added host:
3 B receives ADD_HOST(D,A) from A,
B sends ADD_HOST(D,A) to C
B receives ADD_HOST(E,D) from A:
B sends ADD_HOST(E,D) to C
B receives ADD_HOST(F,E) from A:
B sends ADD_HOST(F,E) to C
E receives ADD_HOST(A,D) from D:
E sends ADD_HOST(A,D) to F
E receives ADD_HOST(B,A) from D:
E sends ADD_HOST(B,A) to F
E receives ADD_HOST(C,B) from D:
E sends ADD_HOST(C,B) to F
B receives ADD_HOST(F,C) from C, and notes that F is already known:
<insert solution here>
B receives ADD_HOST(D,E) from C, and notes that D is already known:
<insert solution here>
B receives ADD_HOST(E,F) from C, and notes that E is already known:
<insert solution here>
E receives ADD_HOST(C,F) from F, and notes that C is already known:
<insert solution here>
E receives ADD_HOST(A,B) from F, and notes that A is already known:
<insert solution here>
E receives ADD_HOST(B,C) from F, and notes that B is already known:
<insert solution here>
So, B and E have to make a choice. Which ADD_HOST is going to win? Fortunately,
since the ADD_HOST messages are augmented, they have an extra piece of
information they can use to decide in a deterministic way which one is going to
win. For example, B got ADD_HOST(F,E) and ADD_HOST(F,C). Since "E" > "C", it
could let ADD_HOST(F,E) win.
B receives ADD_HOST(F,C) from C, and notes that F is already known:
since "C" < "E", B ignores ADD_HOST(F,E)
B sends ADD_HOST(F,C) to A
...
E receives ADD_HOST(C,F) from F, and notes that C is already known:
since "F" > "B", E removes the ADD_HOST(C,B) in favour of the new one
E sends ADD_HOST(C,F) to D
4 A receives ADD_HOST(F,E) from B, and notes that F is already known:
since "E" < "D", A ignores ADD_HOST(F,D).
...
D receives ADD_HOST(C,F) from E, and notes that C is already known:
since "F" > "B", D removes the ADD_HOST(C,B),
closes the connection with C, in favour of the new one.
Ok, time to forget this crap.
1.2.2
-----
The problem with the current ADD/DEL_HOST technique is that each host only
knows the general direction in which to send packets for the other hosts. It
really doesn't know much about the true topology of the network, only about
it's direct neighbours. With so little information each host cannot make a
certain decision which it knows for sure all the others will decide too.
Let's do something totally different. Instead of notifying every host of the
addition of a new host, which is represented by a vertex in a graph, lets send
out notifications of new connections, which are the edges in a graph. This is
rather cheap, since our graphs are (almost) spanning trees, there is
approximately one edge for each vertex in the graph, so we don't need to send
more messages. Furthermore, an edge is characterized by two vertices, so we
only send a fixed amount of extra information. The size/complexity of the
problem therefore does not increase much.
What is the advantage of notifying each vertex of new edges instead of new
vertices? Well, all the vertices now know exactly which connections are made
between each host. This was not known with the former schemes.
Ok back to our problem:
A-----B-----C
D-----E-----F
Edges are undirected, and are characterised by the vertices it connects, sorted
alphabetically, so the edges in the two graphs are:
(A,B), (B,C), (D,E) and (E,F).
So again we have that A wants to connect to D, and F wants to connect to C,
both at the same time. The following loop will occur:
A-----B-----C
| ^
| |
v |
D-----E-----F
Instead of sending ADD_HOSTs, lets assume the hosts send ADD_EDGEs. So, after
making the connections:
1 A sends ADD_EDGE(A,D) to B
A sends ADD_EDGE(A,B) to D
A sends ADD_EDGE(B,C) to D
D sends ADD_EDGE(A,D) to E
D sends ADD_EDGE(D,E) to A
D sends ADD_EDGE(E,F) to A
C sends ADD_EDGE(C,F) to B
C sends ADD_EDGE(A,B) to F
C sends ADD_EDGE(B,C) to F
F sends ADD_EDGE(C,F) to E
F sends ADD_EDGE(D,E) to C
F sends ADD_EDGE(E,F) to C
2 B receives ADD_EDGE(A,D) from A:
B sends ADD_EDGE(A,D) to C
B receives ADD_EDGE(D,E) from A:
B sends ADD_EDGE(D,E) to C
B receives ADD_EDGE(E,F) from A:
B sends ADD_EDGE(E,F) to C
...
B receives ADD_EDGE(C,F) from C, notes that both C and F are already known,
but that the edge (C,F) was not known, so a loop has been created:
<resolve loop here>
Ok, how to resolve the loop? Remeber, we want to do that in such a way that it
is consistent with the way all the other hosts resolve the loop. Here is the
things B does when it notices that a loop is going to be formed:
B performs a Breadth First Search from the first element of the list of all
known hosts sorted alfabetically, in this case A, and thereby finds a
spanning tree. (This might later be changed into a minimum spanning tree
alhorithm, but the key point here is that all hosts do this with exactly the
same starting parameters.) All known edges that are not in the spanning tree
are marked inactive.
An edge marked inactive does not mean anything, unless this edge is connected
to B itself. In that case, B will stop sending messages over that edge. B might
consider closing this edge, but this is not really needed. Keeping it means no
DEL_EDGE has to be sent for it, and if another edge is removed (which will
quite certainly split the graph if it's a spanning tree), this edge might be
reactivated, without the need of sending a new ADD_EDGE for it. On the other
hand, we mustn't keep to many inactive edges, because we want to keep the
number of known edges linear to the number of hosts (otherwise the size of the
problem will grow quadratically).
So, since B didn't deactivate one of it's own edges, it forwards the
ADD_EDGE(C,F) to A, which also does a BFS, and so on, until it reaches F. F of
course also does a BFS, notes that is is one of it's own edges. It deactivates
the edge (C,F), and consequently will not forward the ADD_EDGE(C,F) to C
anymore. In the mean time, C got messages from B which will make C do the same.
Ok, suppose a DEL_EDGE was sent, and it means an inactive edge has to be
reactivated. The vertices connected by that edge must exchange their entire
knowledge of edges again, because in the mean time other messages could have
been sent, which were not properly forwarded. Take this example:
X C-----D
| | |
| | |
v | |
A-----B- - -E
The edge (B,E) is inactive. X is trying to make a new connection with A. A
sends an ADD_EDGE(A,X) to B, which forwards it to C. At that time, the
connection between C and D goes down, so C sends a DEL_EDGE(C,D) to B, and D
sends a DEL_EDGE(C,D) to E. If we just allow (B,E) to be reactivated again
without anything else, then E and D will never have received the ADD_EDGE(A,X).
So, B and E have to exchange edges again, and propagate them to the hosts they
already know.

175
doc/HOWTO Normal file
View file

@ -0,0 +1,175 @@
==============
The TINC HOWTO
==============
Wessel Dankers
wsl@nl.linux.org
Introduction
------------
Tinc is a system to create a virtual ethernet network on top of an existing
infrastructure. This infrastructure can be anything from modem lines to
gigabit ethernet networks, as long as they talk IP. Once you install and
configure tinc, your host will get an extra IP address, just like it would
when you stick an extra ethernet card into it. Using this IP address, it can
communicate with all hosts in its virtual network using strong encryption.
If you install Tinc on a router (and pick your numbers correctly) you can
have the router forward all packets. This way you can---instead of
connecting hosts---connect entire sites together! Now you need only one
outgoing network connection for both internet and intranet.
Architecture
------------
When a few Tinc daemons are running they will try to seek contact with
eachother. A daemon is all the time connected to a few other daemons,
but if traffic is required with a daemon it doesn't know yet, it will
instantly contact it and exchange keys. These so-called meta-connections
are made over TCP, using encryption of course.
When actual traffic has to be sent, a daemon checks his connection list to
see if the addressee is known (and makes contact with it if neccessary).
All packets are then sent using UDP to the other host, just like in a real
network. If a packet gets lost, the connection layer of Linux will resend
the packet, just like it would over a normal network.
Once in a while the daemons will renegotiate keys so that even if a cracker
breaks one, it'll be of limited use.
Getting Tinc
------------
Before you fetch the latest tarball, you might want to check if there's a
package for your Linux distribution. One of the main authors is a Debian
Developer, so you can expect the Debian packages to be very up to date.
The official website for Tinc can be found at http://tinc.nl.linux.org/.
There you can find Debian packages, RPM's and of course... the tarball!
Since we run Doohickey Linux Pro 1.0, for which no package exists (or
indeed the distribution itself) we shall compile the package ourselves.
Building
--------
The Tinc source adheres to so many standards it makes you head spin.
Even the debug messages have been localized! Amazing. Tinc also comes
with a configuration script. If you like to see what is there to
configure run ./configure --help | more. If you don't have time for such
nonsense:
./configure --sysconfdir=/etc
This will see if your system is nice enough to run tinc on, and will
create some Makefiles and other stuff which will together build tinc.
make
make install
The first will do the actual build, the second copies all files into place.
The kernel
----------
Next you will have to configure the kernel to support the tap device.
It is important that you run a recent kernel, but anything after 2.2.16
will do. You have to enable both the netlink device AND the ethertap
device (in that order). Enable them as modules!
Compile, install =) You don't even have to reboot.
Picking your numbers
--------------------
The first thing we should do is pick network numbers. Tinc has a very
peculiar taste for network numbers, which is caused by the way it routes
traffic. However, it turns out to be really handy if you want to use
your tinc host as a router for a site.
The numbers have to be in a range that is not yet in use in your existing,
real network! In this example we will use numbers from the 192.168.0/16
range. This is standard CIDR notation for all IP addresses from 192.168.0.0
to 192.168.255.255. The /16 means that the first 16 bits form the network
part.
It is common practice for Tinc networks to use private (RFC 1918) addresses.
This is not necessary, but it would be a waste to use official addresses
for a private network!
In the example we will connect three machines: f00f, fdiv and hlt. We will
give each an address, but not just that, also a slice of our address space
to play with.
Host Real address Tinc network
---------------------------------------------------
f00f 126.202.37.20 192.168.1.1/24
fdiv 126.202.37.81 192.168.2.1/24
hlt 103.22.1.218 192.168.3.1/24
It is very important that none of the Tinc netmasks overlap! Note how the
192.168.0/16 network covers the entire address space of the three hosts.
We will refer to the 192.168.0/16 network as the `umbrella' from now on.
As you can see we can fit 256 hosts into this umbrella this way, which is
also the practical maximum for tinc. Let's name our VPN 'fubar'.
The configuration file
----------------------
Let's create a configuration file for f00f. We have to put it in
/etc/tinc/fubar because that's how we named our VPN.
MyOwnVPNIP = 192.168.1.1/24
VpnMask = 255.255.0.0
ConnectTo = 126.202.37.81
ConnectTo = 103.22.1.218
TapDevice = /dev/tap0
The first two lines tell Tinc about the numbers we have chosen above.
Using the ConnectTo lines, the daemon will seek contact with the rest of
the umbrella. It's possible to configure any number of ConnectTo lines,
you can even omit them so that it just sits and waits until someone else
contacts it. Until someone does, the poor daemon won't be able to send
any data because it doesn't know where everybody is.
The TapDevice is where the tinc daemon will interface with the kernel.
The passphrases
---------------
We will have to generate keys for ourselves, and get a key from everybody
we want to ConnectTo. All of these go into a directory named
/etc/tinc/fubar/passphrases. PROTECT THIS DIRECTORY!
mkdir -m 700 /etc/tinc/fubar/passphrases
To generate our own key:
genauth 1024 >/etc/tinc/fubar/passphrases/local
You should then proceed to give this key to anyone who wants to ConnectTo
you. DO THIS IN A SECURE MANNER! Anyone who has this number can do icky
things to the umbrella network! Encrypt it using PGP, GPG or another
program using asymmetric keys. Read it over the phone (without anyone
listening of course). Send it by snailmail. Write the key down and bring
it to your partners personally!
If you get any keys from your partners, store them under their network
number. For example, the key we get from fdiv's network administrator
will be stored in /etc/tinc/fubar/passphrases/192.168.2.0 (note the 0).
Running the daemon
------------------
If you use a package manager to install Tinc, the startup scripts use a file
called /etc/tinc/nets.boot to see which umbrella's exist. It has a line
per VPN, and lines starting with a # are ignored. Ours will contain:
# Example VPN from the HOWTO
fubar
In Debian, /etc/init.d/tinc start will start the daemons.
If you use Doohickey Linux just like we do, you'll have to edit the systems
startup scripts by hand. It should contain something along the lines of:
insmod ethertap -s --name=tap0 unit=0
ifconfig tap0 hw ether fe:fd:c0:a8:01:01
ifconfig tap0 192.168.1.1 netmask 255.255.0.0 broadcast 192.168.255.255 -arp
There are two things to note here! First, the MAC address of the ethertap
device is very important. It must start with fe:fd, and end in the
hexadecimal representation of the VPN IP number.
Second, the netmask of the tap device is set to that of the umbrella!
--
$Id: HOWTO,v 1.6 2002/04/12 08:25:01 guus Exp $

83
doc/NETWORKING Normal file
View file

@ -0,0 +1,83 @@
This is the network infrastructure documentation for tinc, a Virtual Private
Network daemon.
Copyright 2001-2002 Guus Sliepen <guus@sliepen.warande.net>
Permission is granted to make and distribute verbatim copies of
this documentation provided the copyright notice and this
permission notice are preserved on all copies.
Permission is granted to copy and distribute modified versions of
this documentation under the conditions for verbatim copying,
provided that the entire resulting derived work is distributed
under the terms of a permission notice identical to this one.
$Id: NETWORKING,v 1.2 2002/04/12 08:25:01 guus Exp $
1. Packet flow
==============
There are two directions for packets. There are packets received from the tap
device that have to be sent out to other tinc daemon, and there are packets
that are received from other tinc daemons which have to be send to the tap
device. The first direction will be called the outgoing direction, while the
latter will be called the incoming direction.
1.1 Outgoing flow
-----------------
handle_tap_input()
|
|
V
route_outgoing()
|
|
V
send_packet() ----
/ \ / \
/ \ | queue
V V V /
send_tcppacket() send_udppacket()--
Packets are read from the tap device by handle_tap_input(). The packets will be
marked as coming from ourself, and are then handled by route_outgoing(). This
function will determine the destination tinc daemon this packet has to be sent
to, and in the future it may also determine if this packet has to be broadcast
or multicast. route_outgoing() will call send_packet() (in case of
broad/multicast several times). send_packet() will check the destination
connection_t entry to see if it is a valid destination, and whether it has to
be sent via TCP or UDP. It will then either call send_tcppacket() or
send_udppacket(). Since a different key is used for UDP packets, which might
not be available at that time, send_udppacket() might put the packet in a queue
and send a REQ_KEY to the destination tinc daemon. If the key has been retrieved,
the packet will be fed to send_udppacket() again.
1.2 Incoming flow
-----------------
handle_vpn_input()
|
|
V
tcppacket_h() receive_udppacket()
\ /
\ /
V V
receive_packet()
|
|
V
route_incoming()
|
|
V
accept_packet()
Packets from other tinc daemons can be received by tcppacket_h(), for TCP
packets, and receive_udppacket() via handle_vpn_input() for UDP packets.
receive_packet() actually does not have to do much, except logging and calling
route_incoming(), but it's there for symmetry with the scheme for the outgoing
flow. route_incoming() will change the MAC header of the packet if necessary to
let the kernel accept the packet after it has been sent to the tap device by
accept_packet().

129
doc/PROTOCOL Normal file
View file

@ -0,0 +1,129 @@
This is the protocol documentation for tinc, a Virtual Private Network daemon.
Copyright 2000-2002 Guus Sliepen <guus@sliepen.warande.net>,
2000-2002 Ivo Timmmermans <itimmermans@bigfoot.com>
Permission is granted to make and distribute verbatim copies of
this documentation provided the copyright notice and this
permission notice are preserved on all copies.
Permission is granted to copy and distribute modified versions of
this documentation under the conditions for verbatim copying,
provided that the entire resulting derived work is distributed
under the terms of a permission notice identical to this one.
$Id: PROTOCOL,v 1.2 2002/04/12 08:25:01 guus Exp $
1. Protocols used in tinc
-------------------------
tinc uses several protocols to function correctly. To enter the
network of tinc daemons that make up the virtual private network, tinc
makes TCP connections to other tinc daemons. It uses the "meta
protocol" for these connections. To exchange packets on the virtual
network, UDP connections are made and the "packet protocol" is used.
Tinc also needs to exchange network packets with the kernel. This is
done using the ethertap device or the universal TUN/TAP device that
can be found in various UNIX flavours.
2. Packet protocol
------------------
Normal packets are sent without any state information, so the layout
is pretty basic.
A data packet can only be sent if the encryption key, cipher and digest are
known to both parties, and the connection is activated. If the encryption key
is not known, a request is sent to the destination using the meta connection to
retreive it.
0 1 2 3 4 5 6 7 ... 97 98 99 100
| seqno | data | MAC |
\____________________________________/\_______________/
| |
encrypted using symmetric cipher digest
The sequence number prevents replay attacks, the message authentication code
prevents altered packets from being accepted.
3. Meta protocol
----------------
The meta protocol is used to tie all tinc daemons together, and
exchange information about which tinc daemon serves which virtual
subnet.
The meta protocol consists of requests that can be sent to the other
side. Each request has a unique number and several parameters. All
requests are represented in the standard ASCII character set. It is
possible to use tools such as telnet or netcat to connect to a tinc
daemon and to read and write requests by hand, provided that one
understands the numeric codes sent.
The authentication scheme is described in the SECURITY2 file. After a
succesful authentication, the server and the client will exchange all the
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
synchronised.
daemon message
--------------------------------------------------------------------------
origin ADD_EDGE node1 12.23.34.45 655 node2 21.32.43.54 655 222 0
| | | \___________________/ | +-> options
| | | | +----> weight
| | | +----------------> see below
| | +--> UDP port
| +----------> real address
+------------------> name of node on one side of the edge
origin ADD_SUBNET node 192.168.1.0/24
| | +--> prefixlength
| +--------> IPv4 network address
+------------------> owner of this subnet
--------------------------------------------------------------------------
In case a connection between two daemons is closed or broken, DEL_EDGE messages
are sent to inform the other daemons of that fact. Each daemon will calculate a
new route to the the daemons, or mark them unreachable if there isn't any.
The keys used to encrypt VPN packets are not sent out directly. This is
because it would generate a lot of traffic on VPNs with many daemons, and
chances are that not every tinc daemon will ever send a packet to every
other daemon. Instead, if a daemon needs a key it sends a request for it
via the meta connection of the nearest hop in the direction of the
destination. If any hop on the way has already learned the key, it will
act as a proxy and forward its copy back to the requestor.
daemon message
--------------------------------------------------------------------------
daemon REQ_KEY origin destination
| +--> name of the tinc daemon it wants the key from
+----------> name of the daemon that wants the key
daemon ANS_KEY origin destination 4ae0b0a82d6e0078 91 64 4
| | \______________/ | | +--> MAC length
| | | | +-----> digest algorithm
| | | +--------> cipher algorithm
| | +--> 128 bits key
| +--> name of the daemon that wants the key
+----------> name of the daemon that uses this key
daemon KEY_CHANGED origin
+--> daemon that has changed it's packet key
--------------------------------------------------------------------------
There is also a mechanism to check if hosts are still alive. Since network
failures or a crash can cause a daemon to be killed without properly
shutting down the TCP connection, this is necessary to keep an up to date
connection list. Pings are sent at regular intervals, except when there
is also some other traffic.
daemon message
--------------------------------------------------------------------------
origin PING
dest. PONG
--------------------------------------------------------------------------
This basically covers everything that is sent over the meta connection by
tinc.

125
doc/SECURITY2 Normal file
View file

@ -0,0 +1,125 @@
This is the security documentation for tinc, a Virtual Private Network daemon.
Copyright 2001-2002 Guus Sliepen <guus@sliepen.warande.net>,
2001-2002 Wessel Dankers <wsl@nl.linux.org>
Permission is granted to make and distribute verbatim copies of
this documentation provided the copyright notice and this
permission notice are preserved on all copies.
Permission is granted to copy and distribute modified versions of
this documentation under the conditions for verbatim copying,
provided that the entire resulting derived work is distributed
under the terms of a permission notice identical to this one.
$Id: SECURITY2,v 1.2 2002/04/12 08:25:01 guus Exp $
Proposed new authentication scheme
----------------------------------
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.
daemon message
--------------------------------------------------------------------------
client <attempts connection>
server <accepts connection>
client ID client 12
| +---> version
+-------> name of tinc daemon
server ID server 12
| +---> version
+-------> name of tinc daemon
client META_KEY 5f0823a93e35b69e...7086ec7866ce582b
\_________________________________/
+-> RSAKEYLEN bits totally random string S1,
encrypted with server's public RSA key
server META_KEY 6ab9c1640388f8f0...45d1a07f8a672630
\_________________________________/
+-> RSAKEYLEN bits totally random string S2,
encrypted with client's public RSA key
From now on:
- the client will symmetrically encrypt outgoing traffic using S1
- the server will symmetrically encrypt outgoing traffic using S2
client CHALLENGE da02add1817c1920989ba6ae2a49cecbda0
\_________________________________/
+-> CHALLEN bits totally random string H1
server CHALLENGE 57fb4b2ccd70d6bb35a64c142f47e61d57f
\_________________________________/
+-> CHALLEN bits totally random string H2
client CHAL_REPLY 816a86
+-> 160 bits SHA1 of H2
server CHAL_REPLY 928ffe
+-> 160 bits SHA1 of H1
After the correct challenge replies are recieved, both ends have proved
their identity. Further information is exchanged.
client ACK 655 12.23.34.45 123 0
| | | +-> options
| | +----> estimated weight
| +------------> IP address of server as seen by client
+--------------------> UDP port of client
server ACK 655 21.32.43.54 321 0
| | | +-> options
| | +----> estimated weight
| +------------> IP address of client as seen by server
+--------------------> UDP port of server
--------------------------------------------------------------------------
This new scheme has several improvements, both in efficiency and security.
First of all, the server sends exactly the same kind of messages over the wire
as the client. The previous versions of tinc first authenticated the client,
and then the server. This scheme even allows both sides to send their messages
simultaneously, there is no need to wait for the other to send something first.
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
amount of information attackers can see (and thus use for a crypto attack). It
also improves speed by a factor two, making the total speedup a factor 4.
Third, and most important:
The symmetric cipher keys are exchanged first, the challenge is done
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
rest of the messages are then encrypted with the symmetric cipher. Then, each
side can only read received messages if they have their private key. The
challenge is there to let the other side know that the private key is really
known, because a challenge reply can only be sent back if the challenge is
decrypted correctly, and that can only be done with knowledge of the private
key.
Fourth: the first thing that is send via the symmetric cipher encrypted
connection is a totally random string, so that there is no known plaintext (for
an attacker) in the beginning of the encrypted stream.
Some things to be discussed:
- What should CHALLEN be? Same as RSAKEYLEN? 256 bits? More/less?