2002-02-11 10:05:58 +00:00
/*
protocol_auth . c - - handle the meta - protocol , authentication
2006-04-26 13:52:58 +00:00
Copyright ( C ) 1999 - 2005 Ivo Timmermans ,
2014-02-07 19:38:48 +00:00
2000 - 2014 Guus Sliepen < guus @ tinc - vpn . org >
2002-02-11 10:05:58 +00:00
This program is free software ; you can redistribute it and / or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation ; either version 2 of the License , or
( at your option ) any later version .
This program is distributed in the hope that it will be useful ,
but WITHOUT ANY WARRANTY ; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
GNU General Public License for more details .
2009-09-24 22:01:00 +00:00
You should have received a copy of the GNU General Public License along
with this program ; if not , write to the Free Software Foundation , Inc . ,
51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 USA .
2002-02-11 10:05:58 +00:00
*/
2003-07-17 15:06:27 +00:00
# include "system.h"
2002-02-11 10:05:58 +00:00
# include "conf.h"
# include "connection.h"
2009-11-07 22:43:25 +00:00
# include "control.h"
# include "control_common.h"
2011-07-07 20:30:55 +00:00
# include "cipher.h"
2008-12-11 14:44:44 +00:00
# include "crypto.h"
2013-08-20 22:24:55 +00:00
# include "device.h"
2011-07-07 20:30:55 +00:00
# include "digest.h"
Add an invitation protocol.
Using the tinc command, an administrator of an existing VPN can generate
invitations for new nodes. The invitation is a small URL that can easily
be copy&pasted into email or live chat. Another person can have tinc
automatically setup the necessary configuration files and exchange keys
with the server, by only using the invitation URL.
The invitation protocol uses temporary ECDSA keys. The invitation URL
consists of the hostname and port of the server, a hash of the server's
temporary ECDSA key and a cookie. When the client wants to accept an
invitation, it also creates a temporary ECDSA key, connects to the server
and says it wants to accept an invitation. Both sides exchange their
temporary keys. The client verifies that the server's key matches the hash
in the invitation URL. After setting up an SPTPS connection using the
temporary keys, the client gives the cookie to the server. If the cookie
is valid, the server sends the client an invitation file containing the
client's new name and a copy of the server's host config file. If everything
is ok, the client will generate a long-term ECDSA key and send it to the
server, which will add it to a new host config file for the client.
The invitation protocol currently allows multiple host config files to be
send from the server to the client. However, the client filters out
most configuration variables for its own host configuration file. In
particular, it only accepts Name, Mode, Broadcast, ConnectTo, Subnet and
AutoConnect. Also, at the moment no tinc-up script is generated.
When an invitation has succesfully been accepted, the client needs to start
the tinc daemon manually.
2013-05-29 16:31:10 +00:00
# include "ecdsa.h"
2002-09-04 13:48:52 +00:00
# include "edge.h"
# include "graph.h"
2003-07-06 22:11:37 +00:00
# include "logger.h"
2012-02-25 17:25:21 +00:00
# include "meta.h"
Add an invitation protocol.
Using the tinc command, an administrator of an existing VPN can generate
invitations for new nodes. The invitation is a small URL that can easily
be copy&pasted into email or live chat. Another person can have tinc
automatically setup the necessary configuration files and exchange keys
with the server, by only using the invitation URL.
The invitation protocol uses temporary ECDSA keys. The invitation URL
consists of the hostname and port of the server, a hash of the server's
temporary ECDSA key and a cookie. When the client wants to accept an
invitation, it also creates a temporary ECDSA key, connects to the server
and says it wants to accept an invitation. Both sides exchange their
temporary keys. The client verifies that the server's key matches the hash
in the invitation URL. After setting up an SPTPS connection using the
temporary keys, the client gives the cookie to the server. If the cookie
is valid, the server sends the client an invitation file containing the
client's new name and a copy of the server's host config file. If everything
is ok, the client will generate a long-term ECDSA key and send it to the
server, which will add it to a new host config file for the client.
The invitation protocol currently allows multiple host config files to be
send from the server to the client. However, the client filters out
most configuration variables for its own host configuration file. In
particular, it only accepts Name, Mode, Broadcast, ConnectTo, Subnet and
AutoConnect. Also, at the moment no tinc-up script is generated.
When an invitation has succesfully been accepted, the client needs to start
the tinc daemon manually.
2013-05-29 16:31:10 +00:00
# include "names.h"
2003-07-17 15:06:27 +00:00
# include "net.h"
# include "netutl.h"
# include "node.h"
2011-07-07 20:30:55 +00:00
# include "prf.h"
2003-07-17 15:06:27 +00:00
# include "protocol.h"
2008-12-11 14:44:44 +00:00
# include "rsa.h"
2013-08-23 17:24:36 +00:00
# include "script.h"
2012-02-25 17:25:21 +00:00
# include "sptps.h"
2003-07-17 15:06:27 +00:00
# include "utils.h"
# include "xalloc.h"
2002-02-11 10:05:58 +00:00
2014-12-29 21:57:18 +00:00
# include "ed25519/sha512.h"
Add an invitation protocol.
Using the tinc command, an administrator of an existing VPN can generate
invitations for new nodes. The invitation is a small URL that can easily
be copy&pasted into email or live chat. Another person can have tinc
automatically setup the necessary configuration files and exchange keys
with the server, by only using the invitation URL.
The invitation protocol uses temporary ECDSA keys. The invitation URL
consists of the hostname and port of the server, a hash of the server's
temporary ECDSA key and a cookie. When the client wants to accept an
invitation, it also creates a temporary ECDSA key, connects to the server
and says it wants to accept an invitation. Both sides exchange their
temporary keys. The client verifies that the server's key matches the hash
in the invitation URL. After setting up an SPTPS connection using the
temporary keys, the client gives the cookie to the server. If the cookie
is valid, the server sends the client an invitation file containing the
client's new name and a copy of the server's host config file. If everything
is ok, the client will generate a long-term ECDSA key and send it to the
server, which will add it to a new host config file for the client.
The invitation protocol currently allows multiple host config files to be
send from the server to the client. However, the client filters out
most configuration variables for its own host configuration file. In
particular, it only accepts Name, Mode, Broadcast, ConnectTo, Subnet and
AutoConnect. Also, at the moment no tinc-up script is generated.
When an invitation has succesfully been accepted, the client needs to start
the tinc daemon manually.
2013-05-29 16:31:10 +00:00
ecdsa_t * invitation_key = NULL ;
2012-04-18 21:19:40 +00:00
static bool send_proxyrequest ( connection_t * c ) {
switch ( proxytype ) {
case PROXY_HTTP : {
char * host ;
char * port ;
sockaddr2str ( & c - > address , & host , & port ) ;
send_request ( c , " CONNECT %s:%s HTTP/1.1 \r \n \r " , host , port ) ;
free ( host ) ;
free ( port ) ;
return true ;
}
case PROXY_SOCKS4 : {
if ( c - > address . sa . sa_family ! = AF_INET ) {
2012-06-26 11:24:20 +00:00
logger ( DEBUG_ALWAYS , LOG_ERR , " Cannot connect to an IPv6 host through a SOCKS 4 proxy! " ) ;
2012-04-18 21:19:40 +00:00
return false ;
}
char s4req [ 9 + ( proxyuser ? strlen ( proxyuser ) : 0 ) ] ;
s4req [ 0 ] = 4 ;
s4req [ 1 ] = 1 ;
memcpy ( s4req + 2 , & c - > address . in . sin_port , 2 ) ;
memcpy ( s4req + 4 , & c - > address . in . sin_addr , 4 ) ;
if ( proxyuser )
2012-07-21 13:50:50 +00:00
memcpy ( s4req + 8 , proxyuser , strlen ( proxyuser ) ) ;
2012-04-18 21:19:40 +00:00
s4req [ sizeof s4req - 1 ] = 0 ;
c - > tcplen = 8 ;
return send_meta ( c , s4req , sizeof s4req ) ;
}
2012-04-19 12:10:54 +00:00
case PROXY_SOCKS5 : {
int len = 3 + 6 + ( c - > address . sa . sa_family = = AF_INET ? 4 : 16 ) ;
c - > tcplen = 2 ;
if ( proxypass )
len + = 3 + strlen ( proxyuser ) + strlen ( proxypass ) ;
char s5req [ len ] ;
int i = 0 ;
s5req [ i + + ] = 5 ;
s5req [ i + + ] = 1 ;
if ( proxypass ) {
s5req [ i + + ] = 2 ;
s5req [ i + + ] = 1 ;
s5req [ i + + ] = strlen ( proxyuser ) ;
2012-07-21 11:53:22 +00:00
memcpy ( s5req + i , proxyuser , strlen ( proxyuser ) ) ;
2012-04-19 12:10:54 +00:00
i + = strlen ( proxyuser ) ;
s5req [ i + + ] = strlen ( proxypass ) ;
2012-07-21 11:53:22 +00:00
memcpy ( s5req + i , proxypass , strlen ( proxypass ) ) ;
2012-04-19 12:10:54 +00:00
i + = strlen ( proxypass ) ;
c - > tcplen + = 2 ;
} else {
s5req [ i + + ] = 0 ;
}
s5req [ i + + ] = 5 ;
s5req [ i + + ] = 1 ;
s5req [ i + + ] = 0 ;
if ( c - > address . sa . sa_family = = AF_INET ) {
s5req [ i + + ] = 1 ;
memcpy ( s5req + i , & c - > address . in . sin_addr , 4 ) ;
i + = 4 ;
memcpy ( s5req + i , & c - > address . in . sin_port , 2 ) ;
i + = 2 ;
c - > tcplen + = 10 ;
} else if ( c - > address . sa . sa_family = = AF_INET6 ) {
s5req [ i + + ] = 3 ;
memcpy ( s5req + i , & c - > address . in6 . sin6_addr , 16 ) ;
i + = 16 ;
memcpy ( s5req + i , & c - > address . in6 . sin6_port , 2 ) ;
i + = 2 ;
c - > tcplen + = 22 ;
} else {
2012-06-26 11:24:20 +00:00
logger ( DEBUG_ALWAYS , LOG_ERR , " Address family %hx not supported for SOCKS 5 proxies! " , c - > address . sa . sa_family ) ;
2012-04-19 12:10:54 +00:00
return false ;
}
if ( i > len )
abort ( ) ;
return send_meta ( c , s5req , sizeof s5req ) ;
}
2012-04-18 21:19:40 +00:00
case PROXY_SOCKS4A :
2012-06-26 11:24:20 +00:00
logger ( DEBUG_ALWAYS , LOG_ERR , " Proxy type not implemented yet " ) ;
2012-04-18 21:19:40 +00:00
return false ;
2012-04-19 13:18:31 +00:00
case PROXY_EXEC :
return true ;
2012-04-18 21:19:40 +00:00
default :
2012-06-26 11:24:20 +00:00
logger ( DEBUG_ALWAYS , LOG_ERR , " Unknown proxy type " ) ;
2012-04-18 21:19:40 +00:00
return false ;
}
}
2007-05-18 10:00:00 +00:00
bool send_id ( connection_t * c ) {
2007-10-19 18:53:48 +00:00
gettimeofday ( & c - > start , NULL ) ;
2011-07-11 19:54:01 +00:00
int minor = 0 ;
2011-07-10 20:34:17 +00:00
2011-07-11 19:54:01 +00:00
if ( experimental ) {
2013-05-11 12:04:39 +00:00
if ( c - > outgoing & & ! read_ecdsa_public_key ( c ) )
2011-07-11 19:54:01 +00:00
minor = 1 ;
else
minor = myself - > connection - > protocol_minor ;
}
2013-02-07 13:22:28 +00:00
if ( proxytype & & c - > outgoing )
2012-04-18 21:19:40 +00:00
if ( ! send_proxyrequest ( c ) )
return false ;
2011-07-11 19:54:01 +00:00
return send_request ( c , " %d %s %d.%d " , ID , myself - > connection - > name , myself - > connection - > protocol_major , minor ) ;
2002-02-11 10:05:58 +00:00
}
Add an invitation protocol.
Using the tinc command, an administrator of an existing VPN can generate
invitations for new nodes. The invitation is a small URL that can easily
be copy&pasted into email or live chat. Another person can have tinc
automatically setup the necessary configuration files and exchange keys
with the server, by only using the invitation URL.
The invitation protocol uses temporary ECDSA keys. The invitation URL
consists of the hostname and port of the server, a hash of the server's
temporary ECDSA key and a cookie. When the client wants to accept an
invitation, it also creates a temporary ECDSA key, connects to the server
and says it wants to accept an invitation. Both sides exchange their
temporary keys. The client verifies that the server's key matches the hash
in the invitation URL. After setting up an SPTPS connection using the
temporary keys, the client gives the cookie to the server. If the cookie
is valid, the server sends the client an invitation file containing the
client's new name and a copy of the server's host config file. If everything
is ok, the client will generate a long-term ECDSA key and send it to the
server, which will add it to a new host config file for the client.
The invitation protocol currently allows multiple host config files to be
send from the server to the client. However, the client filters out
most configuration variables for its own host configuration file. In
particular, it only accepts Name, Mode, Broadcast, ConnectTo, Subnet and
AutoConnect. Also, at the moment no tinc-up script is generated.
When an invitation has succesfully been accepted, the client needs to start
the tinc daemon manually.
2013-05-29 16:31:10 +00:00
static bool finalize_invitation ( connection_t * c , const char * data , uint16_t len ) {
if ( strchr ( data , ' \n ' ) ) {
logger ( DEBUG_ALWAYS , LOG_ERR , " Received invalid key from invited node %s (%s)! \n " , c - > name , c - > hostname ) ;
return false ;
}
// Create a new host config file
char filename [ PATH_MAX ] ;
snprintf ( filename , sizeof filename , " %s " SLASH " hosts " SLASH " %s " , confbase , c - > name ) ;
if ( ! access ( filename , F_OK ) ) {
logger ( DEBUG_ALWAYS , LOG_ERR , " Host config file for %s (%s) already exists! \n " , c - > name , c - > hostname ) ;
return false ;
}
FILE * f = fopen ( filename , " w " ) ;
if ( ! f ) {
logger ( DEBUG_ALWAYS , LOG_ERR , " Error trying to create %s: %s \n " , filename , strerror ( errno ) ) ;
return false ;
}
2014-05-18 18:47:04 +00:00
fprintf ( f , " Ed25519PublicKey = %s \n " , data ) ;
Add an invitation protocol.
Using the tinc command, an administrator of an existing VPN can generate
invitations for new nodes. The invitation is a small URL that can easily
be copy&pasted into email or live chat. Another person can have tinc
automatically setup the necessary configuration files and exchange keys
with the server, by only using the invitation URL.
The invitation protocol uses temporary ECDSA keys. The invitation URL
consists of the hostname and port of the server, a hash of the server's
temporary ECDSA key and a cookie. When the client wants to accept an
invitation, it also creates a temporary ECDSA key, connects to the server
and says it wants to accept an invitation. Both sides exchange their
temporary keys. The client verifies that the server's key matches the hash
in the invitation URL. After setting up an SPTPS connection using the
temporary keys, the client gives the cookie to the server. If the cookie
is valid, the server sends the client an invitation file containing the
client's new name and a copy of the server's host config file. If everything
is ok, the client will generate a long-term ECDSA key and send it to the
server, which will add it to a new host config file for the client.
The invitation protocol currently allows multiple host config files to be
send from the server to the client. However, the client filters out
most configuration variables for its own host configuration file. In
particular, it only accepts Name, Mode, Broadcast, ConnectTo, Subnet and
AutoConnect. Also, at the moment no tinc-up script is generated.
When an invitation has succesfully been accepted, the client needs to start
the tinc daemon manually.
2013-05-29 16:31:10 +00:00
fclose ( f ) ;
logger ( DEBUG_CONNECTIONS , LOG_INFO , " Key succesfully received from %s (%s) " , c - > name , c - > hostname ) ;
2013-08-20 22:24:55 +00:00
// Call invitation-accepted script
char * envp [ 7 ] = { NULL } ;
char * address , * port ;
xasprintf ( & envp [ 0 ] , " NETNAME=%s " , netname ? : " " ) ;
xasprintf ( & envp [ 1 ] , " DEVICE=%s " , device ? : " " ) ;
xasprintf ( & envp [ 2 ] , " INTERFACE=%s " , iface ? : " " ) ;
xasprintf ( & envp [ 3 ] , " NODE=%s " , c - > name ) ;
sockaddr2str ( & c - > address , & address , & port ) ;
xasprintf ( & envp [ 4 ] , " REMOTEADDRESS=%s " , address ) ;
xasprintf ( & envp [ 5 ] , " NAME=%s " , myself - > name ) ;
execute_script ( " invitation-accepted " , envp ) ;
for ( int i = 0 ; envp [ i ] & & i < 7 ; i + + )
free ( envp [ i ] ) ;
2013-08-20 20:36:31 +00:00
sptps_send_record ( & c - > sptps , 2 , data , 0 ) ;
Add an invitation protocol.
Using the tinc command, an administrator of an existing VPN can generate
invitations for new nodes. The invitation is a small URL that can easily
be copy&pasted into email or live chat. Another person can have tinc
automatically setup the necessary configuration files and exchange keys
with the server, by only using the invitation URL.
The invitation protocol uses temporary ECDSA keys. The invitation URL
consists of the hostname and port of the server, a hash of the server's
temporary ECDSA key and a cookie. When the client wants to accept an
invitation, it also creates a temporary ECDSA key, connects to the server
and says it wants to accept an invitation. Both sides exchange their
temporary keys. The client verifies that the server's key matches the hash
in the invitation URL. After setting up an SPTPS connection using the
temporary keys, the client gives the cookie to the server. If the cookie
is valid, the server sends the client an invitation file containing the
client's new name and a copy of the server's host config file. If everything
is ok, the client will generate a long-term ECDSA key and send it to the
server, which will add it to a new host config file for the client.
The invitation protocol currently allows multiple host config files to be
send from the server to the client. However, the client filters out
most configuration variables for its own host configuration file. In
particular, it only accepts Name, Mode, Broadcast, ConnectTo, Subnet and
AutoConnect. Also, at the moment no tinc-up script is generated.
When an invitation has succesfully been accepted, the client needs to start
the tinc daemon manually.
2013-05-29 16:31:10 +00:00
return true ;
}
2014-12-24 21:15:40 +00:00
static bool receive_invitation_sptps ( void * handle , uint8_t type , const void * data , uint16_t len ) {
Add an invitation protocol.
Using the tinc command, an administrator of an existing VPN can generate
invitations for new nodes. The invitation is a small URL that can easily
be copy&pasted into email or live chat. Another person can have tinc
automatically setup the necessary configuration files and exchange keys
with the server, by only using the invitation URL.
The invitation protocol uses temporary ECDSA keys. The invitation URL
consists of the hostname and port of the server, a hash of the server's
temporary ECDSA key and a cookie. When the client wants to accept an
invitation, it also creates a temporary ECDSA key, connects to the server
and says it wants to accept an invitation. Both sides exchange their
temporary keys. The client verifies that the server's key matches the hash
in the invitation URL. After setting up an SPTPS connection using the
temporary keys, the client gives the cookie to the server. If the cookie
is valid, the server sends the client an invitation file containing the
client's new name and a copy of the server's host config file. If everything
is ok, the client will generate a long-term ECDSA key and send it to the
server, which will add it to a new host config file for the client.
The invitation protocol currently allows multiple host config files to be
send from the server to the client. However, the client filters out
most configuration variables for its own host configuration file. In
particular, it only accepts Name, Mode, Broadcast, ConnectTo, Subnet and
AutoConnect. Also, at the moment no tinc-up script is generated.
When an invitation has succesfully been accepted, the client needs to start
the tinc daemon manually.
2013-05-29 16:31:10 +00:00
connection_t * c = handle ;
if ( type = = 128 )
return true ;
if ( type = = 1 & & c - > status . invitation_used )
return finalize_invitation ( c , data , len ) ;
if ( type ! = 0 | | len ! = 18 | | c - > status . invitation_used )
return false ;
2013-08-20 21:09:36 +00:00
// Recover the filename from the cookie and the key
char * fingerprint = ecdsa_get_base64_public_key ( invitation_key ) ;
char hashbuf [ 18 + strlen ( fingerprint ) ] ;
2014-12-29 21:57:18 +00:00
char cookie [ 64 ] ;
2013-08-20 21:09:36 +00:00
memcpy ( hashbuf , data , 18 ) ;
memcpy ( hashbuf + 18 , fingerprint , sizeof hashbuf - 18 ) ;
2014-12-29 21:57:18 +00:00
sha512 ( hashbuf , sizeof hashbuf , cookie ) ;
2013-08-20 21:09:36 +00:00
b64encode_urlsafe ( cookie , cookie , 18 ) ;
free ( fingerprint ) ;
Add an invitation protocol.
Using the tinc command, an administrator of an existing VPN can generate
invitations for new nodes. The invitation is a small URL that can easily
be copy&pasted into email or live chat. Another person can have tinc
automatically setup the necessary configuration files and exchange keys
with the server, by only using the invitation URL.
The invitation protocol uses temporary ECDSA keys. The invitation URL
consists of the hostname and port of the server, a hash of the server's
temporary ECDSA key and a cookie. When the client wants to accept an
invitation, it also creates a temporary ECDSA key, connects to the server
and says it wants to accept an invitation. Both sides exchange their
temporary keys. The client verifies that the server's key matches the hash
in the invitation URL. After setting up an SPTPS connection using the
temporary keys, the client gives the cookie to the server. If the cookie
is valid, the server sends the client an invitation file containing the
client's new name and a copy of the server's host config file. If everything
is ok, the client will generate a long-term ECDSA key and send it to the
server, which will add it to a new host config file for the client.
The invitation protocol currently allows multiple host config files to be
send from the server to the client. However, the client filters out
most configuration variables for its own host configuration file. In
particular, it only accepts Name, Mode, Broadcast, ConnectTo, Subnet and
AutoConnect. Also, at the moment no tinc-up script is generated.
When an invitation has succesfully been accepted, the client needs to start
the tinc daemon manually.
2013-05-29 16:31:10 +00:00
char filename [ PATH_MAX ] , usedname [ PATH_MAX ] ;
snprintf ( filename , sizeof filename , " %s " SLASH " invitations " SLASH " %s " , confbase , cookie ) ;
snprintf ( usedname , sizeof usedname , " %s " SLASH " invitations " SLASH " %s.used " , confbase , cookie ) ;
// Atomically rename the invitation file
if ( rename ( filename , usedname ) ) {
if ( errno = = ENOENT )
logger ( DEBUG_ALWAYS , LOG_ERR , " Peer %s tried to use non-existing invitation %s \n " , c - > hostname , cookie ) ;
else
logger ( DEBUG_ALWAYS , LOG_ERR , " Error trying to rename invitation %s \n " , cookie ) ;
return false ;
}
// Open the renamed file
FILE * f = fopen ( usedname , " r " ) ;
if ( ! f ) {
logger ( DEBUG_ALWAYS , LOG_ERR , " Error trying to open invitation %s \n " , cookie ) ;
return false ;
}
// Read the new node's Name from the file
char buf [ 1024 ] ;
fgets ( buf , sizeof buf , f ) ;
if ( * buf )
buf [ strlen ( buf ) - 1 ] = 0 ;
len = strcspn ( buf , " \t = " ) ;
char * name = buf + len ;
name + = strspn ( name , " \t " ) ;
if ( * name = = ' = ' ) {
name + + ;
name + = strspn ( name , " \t " ) ;
}
buf [ len ] = 0 ;
if ( ! * buf | | ! * name | | strcasecmp ( buf , " Name " ) | | ! check_id ( name ) ) {
logger ( DEBUG_ALWAYS , LOG_ERR , " Invalid invitation file %s \n " , cookie ) ;
fclose ( f ) ;
return false ;
}
free ( c - > name ) ;
c - > name = xstrdup ( name ) ;
// Send the node the contents of the invitation file
rewind ( f ) ;
size_t result ;
while ( ( result = fread ( buf , 1 , sizeof buf , f ) ) )
sptps_send_record ( & c - > sptps , 0 , buf , result ) ;
sptps_send_record ( & c - > sptps , 1 , buf , 0 ) ;
fclose ( f ) ;
unlink ( usedname ) ;
c - > status . invitation_used = true ;
logger ( DEBUG_CONNECTIONS , LOG_INFO , " Invitation %s succesfully sent to %s (%s) " , cookie , c - > name , c - > hostname ) ;
return true ;
}
2012-05-08 14:44:15 +00:00
bool id_h ( connection_t * c , const char * request ) {
2002-09-09 21:25:28 +00:00
char name [ MAX_STRING_SIZE ] ;
2011-07-05 19:19:48 +00:00
if ( sscanf ( request , " %*d " MAX_STRING " %d.%d " , name , & c - > protocol_major , & c - > protocol_minor ) < 2 ) {
2012-02-26 17:37:36 +00:00
logger ( DEBUG_ALWAYS , LOG_ERR , " Got bad %s from %s (%s) " , " ID " , c - > name ,
2002-09-09 21:25:28 +00:00
c - > hostname ) ;
2003-07-22 20:55:21 +00:00
return false ;
2002-09-09 21:25:28 +00:00
}
2009-11-07 22:43:25 +00:00
/* Check if this is a control connection */
if ( name [ 0 ] = = ' ^ ' & & ! strcmp ( name + 1 , controlcookie ) ) {
c - > status . control = true ;
c - > allow_request = CONTROL ;
2013-03-08 13:11:15 +00:00
c - > last_ping_time = now . tv_sec + 3600 ;
2012-04-21 01:44:24 +00:00
2012-10-10 15:17:49 +00:00
free ( c - > name ) ;
c - > name = xstrdup ( " <control> " ) ;
2012-04-21 01:44:24 +00:00
2009-11-07 22:43:25 +00:00
return send_request ( c , " %d %d %d " , ACK , TINC_CTL_VERSION_CURRENT , getpid ( ) ) ;
}
Add an invitation protocol.
Using the tinc command, an administrator of an existing VPN can generate
invitations for new nodes. The invitation is a small URL that can easily
be copy&pasted into email or live chat. Another person can have tinc
automatically setup the necessary configuration files and exchange keys
with the server, by only using the invitation URL.
The invitation protocol uses temporary ECDSA keys. The invitation URL
consists of the hostname and port of the server, a hash of the server's
temporary ECDSA key and a cookie. When the client wants to accept an
invitation, it also creates a temporary ECDSA key, connects to the server
and says it wants to accept an invitation. Both sides exchange their
temporary keys. The client verifies that the server's key matches the hash
in the invitation URL. After setting up an SPTPS connection using the
temporary keys, the client gives the cookie to the server. If the cookie
is valid, the server sends the client an invitation file containing the
client's new name and a copy of the server's host config file. If everything
is ok, the client will generate a long-term ECDSA key and send it to the
server, which will add it to a new host config file for the client.
The invitation protocol currently allows multiple host config files to be
send from the server to the client. However, the client filters out
most configuration variables for its own host configuration file. In
particular, it only accepts Name, Mode, Broadcast, ConnectTo, Subnet and
AutoConnect. Also, at the moment no tinc-up script is generated.
When an invitation has succesfully been accepted, the client needs to start
the tinc daemon manually.
2013-05-29 16:31:10 +00:00
if ( name [ 0 ] = = ' ? ' ) {
if ( ! invitation_key ) {
logger ( DEBUG_ALWAYS , LOG_ERR , " Got invitation from %s but we don't have an invitation key " , c - > hostname ) ;
return false ;
}
c - > ecdsa = ecdsa_set_base64_public_key ( name + 1 ) ;
if ( ! c - > ecdsa ) {
logger ( DEBUG_ALWAYS , LOG_ERR , " Got bad invitation from %s " , c - > hostname ) ;
return false ;
}
c - > status . invitation = true ;
char * mykey = ecdsa_get_base64_public_key ( invitation_key ) ;
if ( ! mykey )
return false ;
if ( ! send_request ( c , " %d %s " , ACK , mykey ) )
return false ;
free ( mykey ) ;
c - > protocol_minor = 2 ;
return sptps_start ( & c - > sptps , c , false , false , invitation_key , c - > ecdsa , " tinc invitation " , 15 , send_meta_sptps , receive_invitation_sptps ) ;
}
2002-09-09 21:25:28 +00:00
/* Check if identity is a valid name */
2003-07-22 20:55:21 +00:00
if ( ! check_id ( name ) ) {
2012-02-26 17:37:36 +00:00
logger ( DEBUG_ALWAYS , LOG_ERR , " Got bad %s from %s (%s): %s " , " ID " , c - > name ,
2002-09-09 21:25:28 +00:00
c - > hostname , " invalid name " ) ;
2003-07-22 20:55:21 +00:00
return false ;
2002-09-09 21:25:28 +00:00
}
2007-05-17 19:15:48 +00:00
/* If this is an outgoing connection, make sure we are connected to the right host */
2002-09-09 21:25:28 +00:00
2007-05-17 19:15:48 +00:00
if ( c - > outgoing ) {
2002-09-09 21:25:28 +00:00
if ( strcmp ( c - > name , name ) ) {
2012-02-26 17:37:36 +00:00
logger ( DEBUG_ALWAYS , LOG_ERR , " Peer %s is %s instead of %s " , c - > hostname , name ,
2002-09-09 21:25:28 +00:00
c - > name ) ;
2003-07-22 20:55:21 +00:00
return false ;
2002-09-09 21:25:28 +00:00
}
2005-05-04 15:51:45 +00:00
} else {
if ( c - > name )
free ( c - > name ) ;
2002-09-09 21:25:28 +00:00
c - > name = xstrdup ( name ) ;
2005-05-04 15:51:45 +00:00
}
2002-09-09 21:25:28 +00:00
/* Check if version matches */
2011-07-05 19:19:48 +00:00
if ( c - > protocol_major ! = myself - > connection - > protocol_major ) {
2012-02-26 17:37:36 +00:00
logger ( DEBUG_ALWAYS , LOG_ERR , " Peer %s (%s) uses incompatible version %d.%d " ,
2013-07-20 22:13:38 +00:00
c - > name , c - > hostname , c - > protocol_major , c - > protocol_minor ) ;
2003-07-22 20:55:21 +00:00
return false ;
2002-09-09 21:25:28 +00:00
}
if ( bypass_security ) {
if ( ! c - > config_tree )
init_configuration ( & c - > config_tree ) ;
c - > allow_request = ACK ;
return send_ack ( c ) ;
}
2011-07-19 19:11:11 +00:00
if ( ! experimental )
c - > protocol_minor = 0 ;
2002-09-09 21:25:28 +00:00
if ( ! c - > config_tree ) {
init_configuration ( & c - > config_tree ) ;
2012-10-07 19:02:40 +00:00
if ( ! read_host_config ( c - > config_tree , c - > name ) ) {
logger ( DEBUG_ALWAYS , LOG_ERR , " Peer %s had unknown identity (%s) " , c - > hostname , c - > name ) ;
2003-07-22 20:55:21 +00:00
return false ;
2002-09-09 21:25:28 +00:00
}
2011-07-10 20:34:17 +00:00
2013-07-20 22:13:38 +00:00
if ( experimental )
2014-12-05 01:41:55 +00:00
read_ecdsa_public_key ( c ) ;
/* Ignore failures if no key known yet */
2002-09-09 21:25:28 +00:00
}
2014-12-05 02:06:44 +00:00
if ( c - > protocol_minor & & ! ecdsa_active ( c - > ecdsa ) )
c - > protocol_minor = 1 ;
2014-05-18 18:47:04 +00:00
/* Forbid version rollback for nodes whose Ed25519 key we know */
2013-07-20 22:13:38 +00:00
if ( ecdsa_active ( c - > ecdsa ) & & c - > protocol_minor < 2 ) {
logger ( DEBUG_ALWAYS , LOG_ERR , " Peer %s (%s) tries to roll back protocol version to %d.%d " ,
c - > name , c - > hostname , c - > protocol_major , c - > protocol_minor ) ;
return false ;
}
2011-07-07 20:30:55 +00:00
c - > allow_request = METAKEY ;
2012-02-25 17:25:21 +00:00
if ( c - > protocol_minor > = 2 ) {
c - > allow_request = ACK ;
char label [ 25 + strlen ( myself - > name ) + strlen ( c - > name ) ] ;
2011-07-07 20:30:55 +00:00
2012-02-25 17:25:21 +00:00
if ( c - > outgoing )
snprintf ( label , sizeof label , " tinc TCP key expansion %s %s " , myself - > name , c - > name ) ;
else
snprintf ( label , sizeof label , " tinc TCP key expansion %s %s " , c - > name , myself - > name ) ;
2011-07-07 20:30:55 +00:00
2012-03-18 15:42:02 +00:00
return sptps_start ( & c - > sptps , c , c - > outgoing , false , myself - > connection - > ecdsa , c - > ecdsa , label , sizeof label , send_meta_sptps , receive_meta_sptps ) ;
2012-02-25 17:25:21 +00:00
} else {
return send_metakey ( c ) ;
}
2002-02-11 10:05:58 +00:00
}
2007-05-18 10:00:00 +00:00
bool send_metakey ( connection_t * c ) {
2014-12-29 21:57:18 +00:00
# ifdef DISABLE_LEGACY
return false ;
# else
2014-12-26 13:38:01 +00:00
if ( ! myself - > connection - > rsa ) {
logger ( DEBUG_CONNECTIONS , LOG_ERR , " Peer %s (%s) uses legacy protocol which we don't support " , c - > name , c - > hostname ) ;
return false ;
}
2011-07-07 20:30:55 +00:00
if ( ! read_rsa_public_key ( c ) )
return false ;
2013-05-01 15:17:22 +00:00
if ( ! ( c - > outcipher = cipher_open_blowfish_ofb ( ) ) )
2008-12-11 14:44:44 +00:00
return false ;
2012-10-10 15:17:49 +00:00
2013-05-01 15:17:22 +00:00
if ( ! ( c - > outdigest = digest_open_sha1 ( - 1 ) ) )
2008-12-11 14:44:44 +00:00
return false ;
2002-09-09 21:25:28 +00:00
2014-12-24 21:23:24 +00:00
const size_t len = rsa_size ( c - > rsa ) ;
2011-07-10 20:34:17 +00:00
char key [ len ] ;
char enckey [ len ] ;
char hexkey [ 2 * len + 1 ] ;
2008-12-11 14:44:44 +00:00
/* Create a random key */
2002-09-09 21:25:28 +00:00
2008-12-11 14:44:44 +00:00
randomize ( key , len ) ;
2002-09-09 21:25:28 +00:00
/* The message we send must be smaller than the modulus of the RSA key.
By definition , for a key of k bits , the following formula holds :
2 ^ ( k - 1 ) < = modulus < 2 ^ ( k )
Where ^ means " to the power of " , not " xor " .
This means that to be sure , we must choose our message < 2 ^ ( k - 1 ) .
This can be done by setting the most significant bit to zero .
*/
2008-12-11 14:44:44 +00:00
key [ 0 ] & = 0x7F ;
2013-05-10 18:30:47 +00:00
if ( ! cipher_set_key_from_rsa ( c - > outcipher , key , len , true ) )
return false ;
2002-09-09 21:25:28 +00:00
2012-02-26 17:37:36 +00:00
if ( debug_level > = DEBUG_SCARY_THINGS ) {
2008-12-11 14:44:44 +00:00
bin2hex ( key , hexkey , len ) ;
2012-02-26 17:37:36 +00:00
logger ( DEBUG_SCARY_THINGS , LOG_DEBUG , " Generated random meta key (unencrypted): %s " , hexkey ) ;
2002-09-09 21:25:28 +00:00
}
/* Encrypt the random data
We do not use one of the PKCS padding schemes here .
This is allowed , because we encrypt a totally random string
with a length equal to that of the modulus of the RSA key .
*/
2013-05-01 15:17:22 +00:00
if ( ! rsa_public_encrypt ( c - > rsa , key , len , enckey ) ) {
2012-02-26 17:37:36 +00:00
logger ( DEBUG_ALWAYS , LOG_ERR , " Error during encryption of meta key for %s (%s) " , c - > name , c - > hostname ) ;
2003-07-22 20:55:21 +00:00
return false ;
2002-09-09 21:25:28 +00:00
}
/* Convert the encrypted random data to a hexadecimal formatted string */
2008-12-11 14:44:44 +00:00
bin2hex ( enckey , hexkey , len ) ;
2002-09-09 21:25:28 +00:00
/* Send the meta key */
2008-12-11 14:44:44 +00:00
bool result = send_request ( c , " %d %d %d %d %d %s " , METAKEY ,
2013-05-01 15:17:22 +00:00
cipher_get_nid ( c - > outcipher ) ,
digest_get_nid ( c - > outdigest ) , c - > outmaclength ,
2008-12-11 14:44:44 +00:00
c - > outcompression , hexkey ) ;
2012-10-10 15:17:49 +00:00
2008-12-11 14:44:44 +00:00
c - > status . encryptout = true ;
return result ;
2014-12-29 21:57:18 +00:00
# endif
2002-02-11 10:05:58 +00:00
}
2012-05-08 14:44:15 +00:00
bool metakey_h ( connection_t * c , const char * request ) {
2014-12-29 21:57:18 +00:00
# ifdef DISABLE_LEGACY
return false ;
# else
2014-12-26 13:38:01 +00:00
if ( ! myself - > connection - > rsa )
return false ;
2008-12-11 14:44:44 +00:00
char hexkey [ MAX_STRING_SIZE ] ;
2002-09-09 21:25:28 +00:00
int cipher , digest , maclength , compression ;
2014-12-24 21:23:24 +00:00
const size_t len = rsa_size ( myself - > connection - > rsa ) ;
2008-12-11 14:44:44 +00:00
char enckey [ len ] ;
char key [ len ] ;
2002-09-09 21:25:28 +00:00
2008-12-11 14:44:44 +00:00
if ( sscanf ( request , " %*d %d %d %d %d " MAX_STRING , & cipher , & digest , & maclength , & compression , hexkey ) ! = 5 ) {
2012-02-26 17:37:36 +00:00
logger ( DEBUG_ALWAYS , LOG_ERR , " Got bad %s from %s (%s) " , " METAKEY " , c - > name , c - > hostname ) ;
2003-07-22 20:55:21 +00:00
return false ;
2002-09-09 21:25:28 +00:00
}
2011-07-12 21:43:12 +00:00
/* Convert the challenge from hexadecimal back to binary */
int inlen = hex2bin ( hexkey , enckey , sizeof enckey ) ;
2002-09-09 21:25:28 +00:00
/* Check if the length of the meta key is all right */
2011-07-12 21:43:12 +00:00
if ( inlen ! = len ) {
2012-02-26 17:37:36 +00:00
logger ( DEBUG_ALWAYS , LOG_ERR , " Possible intruder %s (%s): %s " , c - > name , c - > hostname , " wrong keylength " ) ;
2003-07-22 20:55:21 +00:00
return false ;
2002-09-09 21:25:28 +00:00
}
/* Decrypt the meta key */
2013-05-01 15:17:22 +00:00
if ( ! rsa_private_decrypt ( myself - > connection - > rsa , enckey , len , key ) ) {
2012-02-26 17:37:36 +00:00
logger ( DEBUG_ALWAYS , LOG_ERR , " Error during decryption of meta key for %s (%s) " , c - > name , c - > hostname ) ;
2003-07-22 20:55:21 +00:00
return false ;
2002-09-09 21:25:28 +00:00
}
2012-02-26 17:37:36 +00:00
if ( debug_level > = DEBUG_SCARY_THINGS ) {
2008-12-11 14:44:44 +00:00
bin2hex ( key , hexkey , len ) ;
2012-02-26 17:37:36 +00:00
logger ( DEBUG_SCARY_THINGS , LOG_DEBUG , " Received random meta key (unencrypted): %s " , hexkey ) ;
2002-02-20 19:25:09 +00:00
}
2002-09-09 21:25:28 +00:00
/* Check and lookup cipher and digest algorithms */
2014-05-18 19:51:42 +00:00
if ( cipher ) {
if ( ! ( c - > incipher = cipher_open_by_nid ( cipher ) ) | | ! cipher_set_key_from_rsa ( c - > incipher , key , len , false ) ) {
logger ( DEBUG_ALWAYS , LOG_ERR , " Error during initialisation of cipher from %s (%s) " , c - > name , c - > hostname ) ;
return false ;
}
} else {
c - > incipher = NULL ;
2002-02-20 19:25:09 +00:00
}
2002-09-09 21:25:28 +00:00
2014-05-18 19:51:42 +00:00
if ( digest ) {
if ( ! ( c - > indigest = digest_open_by_nid ( digest , - 1 ) ) ) {
logger ( DEBUG_ALWAYS , LOG_ERR , " Error during initialisation of digest from %s (%s) " , c - > name , c - > hostname ) ;
return false ;
}
} else {
c - > indigest = NULL ;
2002-02-20 19:25:09 +00:00
}
2008-12-11 14:44:44 +00:00
c - > status . decryptin = true ;
2002-09-09 21:25:28 +00:00
c - > allow_request = CHALLENGE ;
2002-02-11 10:05:58 +00:00
2002-09-09 21:25:28 +00:00
return send_challenge ( c ) ;
2014-12-29 21:57:18 +00:00
# endif
2002-02-11 10:05:58 +00:00
}
2007-05-18 10:00:00 +00:00
bool send_challenge ( connection_t * c ) {
2014-12-29 21:57:18 +00:00
# ifdef DISABLE_LEGACY
return false ;
# else
2014-12-24 21:23:24 +00:00
const size_t len = rsa_size ( c - > rsa ) ;
2008-12-11 14:44:44 +00:00
char buffer [ len * 2 + 1 ] ;
2002-02-11 10:05:58 +00:00
2002-09-09 21:25:28 +00:00
if ( ! c - > hischallenge )
2009-06-05 21:03:28 +00:00
c - > hischallenge = xrealloc ( c - > hischallenge , len ) ;
2002-02-11 10:05:58 +00:00
2002-09-09 21:25:28 +00:00
/* Copy random data to the buffer */
2002-02-11 10:05:58 +00:00
2008-12-11 14:44:44 +00:00
randomize ( c - > hischallenge , len ) ;
2002-02-11 10:05:58 +00:00
2002-09-09 21:25:28 +00:00
/* Convert to hex */
bin2hex ( c - > hischallenge , buffer , len ) ;
/* Send the challenge */
2003-07-22 20:55:21 +00:00
return send_request ( c , " %d %s " , CHALLENGE , buffer ) ;
2014-12-29 21:57:18 +00:00
# endif
2002-02-11 10:05:58 +00:00
}
2012-05-08 14:44:15 +00:00
bool challenge_h ( connection_t * c , const char * request ) {
2014-12-29 21:57:18 +00:00
# ifdef DISABLE_LEGACY
return false ;
# else
2014-12-26 13:38:01 +00:00
if ( ! myself - > connection - > rsa )
return false ;
2002-09-09 21:25:28 +00:00
char buffer [ MAX_STRING_SIZE ] ;
2014-12-24 21:23:24 +00:00
const size_t len = rsa_size ( myself - > connection - > rsa ) ;
2013-05-01 15:17:22 +00:00
size_t digestlen = digest_length ( c - > indigest ) ;
2008-12-11 14:44:44 +00:00
char digest [ digestlen ] ;
2002-02-11 10:05:58 +00:00
2007-05-19 22:23:02 +00:00
if ( sscanf ( request , " %*d " MAX_STRING , buffer ) ! = 1 ) {
2012-02-26 17:37:36 +00:00
logger ( DEBUG_ALWAYS , LOG_ERR , " Got bad %s from %s (%s) " , " CHALLENGE " , c - > name , c - > hostname ) ;
2003-07-22 20:55:21 +00:00
return false ;
2002-09-09 21:25:28 +00:00
}
2011-07-12 21:43:12 +00:00
/* Convert the challenge from hexadecimal back to binary */
int inlen = hex2bin ( buffer , buffer , sizeof buffer ) ;
2002-09-09 21:25:28 +00:00
/* Check if the length of the challenge is all right */
2011-07-12 21:43:12 +00:00
if ( inlen ! = len ) {
2012-02-26 17:37:36 +00:00
logger ( DEBUG_ALWAYS , LOG_ERR , " Possible intruder %s (%s): %s " , c - > name , c - > hostname , " wrong challenge length " ) ;
2003-07-22 20:55:21 +00:00
return false ;
2002-09-09 21:25:28 +00:00
}
2002-02-11 10:05:58 +00:00
2002-09-09 21:25:28 +00:00
/* Calculate the hash from the challenge we received */
2002-02-11 10:05:58 +00:00
2013-05-10 18:30:47 +00:00
if ( ! digest_create ( c - > indigest , buffer , len , digest ) )
return false ;
2002-02-11 10:05:58 +00:00
2002-09-09 21:25:28 +00:00
/* Convert the hash to a hexadecimal formatted string */
2002-02-11 10:05:58 +00:00
2008-12-11 14:44:44 +00:00
bin2hex ( digest , buffer , digestlen ) ;
2002-02-11 10:05:58 +00:00
2002-09-09 21:25:28 +00:00
/* Send the reply */
2013-05-10 18:30:47 +00:00
c - > allow_request = CHAL_REPLY ;
2008-12-11 14:44:44 +00:00
return send_request ( c , " %d %s " , CHAL_REPLY , buffer ) ;
2014-12-29 21:57:18 +00:00
# endif
2002-02-11 10:05:58 +00:00
}
2012-05-08 14:44:15 +00:00
bool chal_reply_h ( connection_t * c , const char * request ) {
2014-12-29 21:57:18 +00:00
# ifdef DISABLE_LEGACY
return false ;
# else
2002-09-09 21:25:28 +00:00
char hishash [ MAX_STRING_SIZE ] ;
2007-05-19 22:23:02 +00:00
if ( sscanf ( request , " %*d " MAX_STRING , hishash ) ! = 1 ) {
2012-02-26 17:37:36 +00:00
logger ( DEBUG_ALWAYS , LOG_ERR , " Got bad %s from %s (%s) " , " CHAL_REPLY " , c - > name ,
2002-09-09 21:25:28 +00:00
c - > hostname ) ;
2003-07-22 20:55:21 +00:00
return false ;
2002-09-09 21:25:28 +00:00
}
2011-07-12 21:43:12 +00:00
/* Convert the hash to binary format */
int inlen = hex2bin ( hishash , hishash , sizeof hishash ) ;
2002-09-09 21:25:28 +00:00
/* Check if the length of the hash is all right */
2013-05-01 15:17:22 +00:00
if ( inlen ! = digest_length ( c - > outdigest ) ) {
2012-02-26 17:37:36 +00:00
logger ( DEBUG_ALWAYS , LOG_ERR , " Possible intruder %s (%s): %s " , c - > name , c - > hostname , " wrong challenge reply length " ) ;
2003-07-22 20:55:21 +00:00
return false ;
2002-09-09 21:25:28 +00:00
}
2008-12-11 14:44:44 +00:00
/* Verify the hash */
2002-09-09 21:25:28 +00:00
2013-05-01 15:17:22 +00:00
if ( ! digest_verify ( c - > outdigest , c - > hischallenge , rsa_size ( c - > rsa ) , hishash ) ) {
2012-02-26 17:37:36 +00:00
logger ( DEBUG_ALWAYS , LOG_ERR , " Possible intruder %s (%s): %s " , c - > name , c - > hostname , " wrong challenge reply " ) ;
2003-07-22 20:55:21 +00:00
return false ;
2002-09-09 21:25:28 +00:00
}
/* Identity has now been positively verified.
Send an acknowledgement with the rest of the information needed .
*/
2008-12-11 14:44:44 +00:00
free ( c - > hischallenge ) ;
c - > hischallenge = NULL ;
2002-09-09 21:25:28 +00:00
c - > allow_request = ACK ;
return send_ack ( c ) ;
2014-12-29 21:57:18 +00:00
# endif
2002-02-11 10:05:58 +00:00
}
2011-07-10 20:34:17 +00:00
static bool send_upgrade ( connection_t * c ) {
2014-12-29 21:57:18 +00:00
# ifdef DISABLE_LEGACY
return false ;
# else
2014-05-18 18:47:04 +00:00
/* Special case when protocol_minor is 1: the other end is Ed25519 capable,
2011-07-10 20:34:17 +00:00
* but doesn ' t know our key yet . So send it now . */
2013-05-01 15:17:22 +00:00
char * pubkey = ecdsa_get_base64_public_key ( myself - > connection - > ecdsa ) ;
2011-07-10 20:34:17 +00:00
if ( ! pubkey )
return false ;
bool result = send_request ( c , " %d %s " , ACK , pubkey ) ;
free ( pubkey ) ;
return result ;
2014-12-29 21:57:18 +00:00
# endif
2011-07-10 20:34:17 +00:00
}
2007-05-18 10:00:00 +00:00
bool send_ack ( connection_t * c ) {
2011-07-10 20:34:17 +00:00
if ( c - > protocol_minor = = 1 )
return send_upgrade ( c ) ;
2002-09-09 21:25:28 +00:00
/* ACK message contains rest of the information the other end needs
to create node_t and edge_t structures . */
struct timeval now ;
2003-11-10 22:31:53 +00:00
bool choice ;
2002-09-09 21:25:28 +00:00
/* Estimate weight */
gettimeofday ( & now , NULL ) ;
2003-07-22 20:55:21 +00:00
c - > estimated_weight = ( now . tv_sec - c - > start . tv_sec ) * 1000 + ( now . tv_usec - c - > start . tv_usec ) / 1000 ;
2002-09-09 21:25:28 +00:00
2003-11-10 22:31:53 +00:00
/* Check some options */
if ( ( get_config_bool ( lookup_config ( c - > config_tree , " IndirectData " ) , & choice ) & & choice ) | | myself - > options & OPTION_INDIRECT )
c - > options | = OPTION_INDIRECT ;
if ( ( get_config_bool ( lookup_config ( c - > config_tree , " TCPOnly " ) , & choice ) & & choice ) | | myself - > options & OPTION_TCPONLY )
c - > options | = OPTION_TCPONLY | OPTION_INDIRECT ;
2009-03-09 12:48:54 +00:00
if ( myself - > options & OPTION_PMTU_DISCOVERY )
2003-12-20 21:25:17 +00:00
c - > options | = OPTION_PMTU_DISCOVERY ;
2003-12-20 19:47:53 +00:00
2010-01-16 19:16:33 +00:00
choice = myself - > options & OPTION_CLAMP_MSS ;
get_config_bool ( lookup_config ( c - > config_tree , " ClampMSS " ) , & choice ) ;
if ( choice )
c - > options | = OPTION_CLAMP_MSS ;
2014-01-29 16:32:18 +00:00
if ( ! get_config_int ( lookup_config ( c - > config_tree , " Weight " ) , & c - > estimated_weight ) )
get_config_int ( lookup_config ( config_tree , " Weight " ) , & c - > estimated_weight ) ;
2003-12-22 11:04:17 +00:00
2012-07-31 19:43:49 +00:00
return send_request ( c , " %d %s %d %x " , ACK , myport , c - > estimated_weight , ( c - > options & 0xffffff ) | ( experimental ? ( PROT_MINOR < < 24 ) : 0 ) ) ;
2002-02-11 10:05:58 +00:00
}
2007-05-18 10:00:00 +00:00
static void send_everything ( connection_t * c ) {
2002-09-09 21:25:28 +00:00
/* Send all known subnets and edges */
2013-04-12 15:15:05 +00:00
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 ) ;
}
2003-11-17 15:30:18 +00:00
if ( tunnelserver ) {
2012-10-07 22:35:38 +00:00
for splay_each ( subnet_t , s , myself - > subnet_tree )
2003-11-17 15:30:18 +00:00
send_add_subnet ( c , s ) ;
return ;
}
2012-10-07 22:35:38 +00:00
for splay_each ( node_t , n , node_tree ) {
for splay_each ( subnet_t , s , n - > subnet_tree )
2002-09-09 21:25:28 +00:00
send_add_subnet ( c , s ) ;
2012-10-07 22:35:38 +00:00
for splay_each ( edge_t , e , n - > edge_tree )
2002-09-09 21:25:28 +00:00
send_add_edge ( c , e ) ;
}
2002-02-11 10:05:58 +00:00
}
2012-05-08 14:44:15 +00:00
static bool upgrade_h ( connection_t * c , const char * request ) {
2011-07-10 20:34:17 +00:00
char pubkey [ MAX_STRING_SIZE ] ;
if ( sscanf ( request , " %*d " MAX_STRING , pubkey ) ! = 1 ) {
2012-02-26 17:37:36 +00:00
logger ( DEBUG_ALWAYS , LOG_ERR , " Got bad %s from %s (%s) " , " ACK " , c - > name , c - > hostname ) ;
2011-07-10 20:34:17 +00:00
return false ;
}
2013-05-01 15:17:22 +00:00
if ( ecdsa_active ( c - > ecdsa ) | | read_ecdsa_public_key ( c ) ) {
2014-12-07 20:42:20 +00:00
char * knownkey = ecdsa_get_base64_public_key ( c - > ecdsa ) ;
bool different = strcmp ( knownkey , pubkey ) ;
free ( knownkey ) ;
if ( different ) {
logger ( DEBUG_ALWAYS , LOG_ERR , " Already have an Ed25519 public key from %s (%s) which is different from the one presented now! " , c - > name , c - > hostname ) ;
return false ;
}
logger ( DEBUG_ALWAYS , LOG_INFO , " Already have Ed25519 public key from %s (%s), ignoring. " , c - > name , c - > hostname ) ;
c - > allow_request = TERMREQ ;
return send_termreq ( c ) ;
2011-07-10 20:34:17 +00:00
}
2014-12-07 16:20:18 +00:00
c - > ecdsa = ecdsa_set_base64_public_key ( pubkey ) ;
if ( ! c - > ecdsa ) {
logger ( DEBUG_ALWAYS , LOG_INFO , " Got bad Ed25519 public key from %s (%s), not upgrading. " , c - > name , c - > hostname ) ;
return false ;
}
2014-05-18 18:47:04 +00:00
logger ( DEBUG_ALWAYS , LOG_INFO , " Got Ed25519 public key from %s (%s), upgrading! " , c - > name , c - > hostname ) ;
append_config_file ( c - > name , " Ed25519PublicKey " , pubkey ) ;
2011-07-10 20:34:17 +00:00
c - > allow_request = TERMREQ ;
return send_termreq ( c ) ;
}
2012-05-08 14:44:15 +00:00
bool ack_h ( connection_t * c , const char * request ) {
2011-07-10 20:34:17 +00:00
if ( c - > protocol_minor = = 1 )
return upgrade_h ( c , request ) ;
2002-09-09 21:25:28 +00:00
char hisport [ MAX_STRING_SIZE ] ;
2010-04-03 08:46:45 +00:00
char * hisaddress ;
2003-12-22 11:04:17 +00:00
int weight , mtu ;
2009-10-24 14:15:24 +00:00
uint32_t options ;
2002-09-09 21:25:28 +00:00
node_t * n ;
2010-01-16 19:16:33 +00:00
bool choice ;
2002-09-09 21:25:28 +00:00
2009-11-02 13:24:27 +00:00
if ( sscanf ( request , " %*d " MAX_STRING " %d %x " , hisport , & weight , & options ) ! = 3 ) {
2012-02-26 17:37:36 +00:00
logger ( DEBUG_ALWAYS , LOG_ERR , " Got bad %s from %s (%s) " , " ACK " , c - > name ,
2002-09-09 21:25:28 +00:00
c - > hostname ) ;
2003-07-22 20:55:21 +00:00
return false ;
2002-09-09 21:25:28 +00:00
}
/* Check if we already have a node_t for him */
n = lookup_node ( c - > name ) ;
if ( ! n ) {
n = new_node ( ) ;
n - > name = xstrdup ( c - > name ) ;
node_add ( n ) ;
} else {
if ( n - > connection ) {
/* Oh dear, we already have a connection to this node. */
2012-02-26 17:37:36 +00:00
logger ( DEBUG_CONNECTIONS , LOG_DEBUG , " Established a second connection with %s (%s), closing old connection " , n - > connection - > name , n - > connection - > hostname ) ;
2007-05-18 10:29:10 +00:00
if ( n - > connection - > outgoing ) {
if ( c - > outgoing )
2012-02-26 17:37:36 +00:00
logger ( DEBUG_ALWAYS , LOG_WARNING , " Two outgoing connections to the same node! " ) ;
2007-05-18 10:29:10 +00:00
else
c - > outgoing = n - > connection - > outgoing ;
n - > connection - > outgoing = NULL ;
}
2003-07-22 20:55:21 +00:00
terminate_connection ( n - > connection , false ) ;
2003-01-12 17:02:23 +00:00
/* Run graph algorithm to purge key and make sure up/down scripts are rerun with new IP addresses and stuff */
graph ( ) ;
2002-09-09 21:25:28 +00:00
}
}
n - > connection = c ;
c - > node = n ;
2009-03-09 12:48:54 +00:00
if ( ! ( c - > options & options & OPTION_PMTU_DISCOVERY ) ) {
c - > options & = ~ OPTION_PMTU_DISCOVERY ;
options & = ~ OPTION_PMTU_DISCOVERY ;
}
2002-09-09 21:25:28 +00:00
c - > options | = options ;
2003-12-22 11:04:17 +00:00
if ( get_config_int ( lookup_config ( c - > config_tree , " PMTU " ) , & mtu ) & & mtu < n - > mtu )
n - > mtu = mtu ;
2010-10-22 10:47:12 +00:00
if ( get_config_int ( lookup_config ( config_tree , " PMTU " ) , & mtu ) & & mtu < n - > mtu )
2003-12-22 11:04:17 +00:00
n - > mtu = mtu ;
2010-01-16 19:16:33 +00:00
if ( get_config_bool ( lookup_config ( c - > config_tree , " ClampMSS " ) , & choice ) ) {
if ( choice )
c - > options | = OPTION_CLAMP_MSS ;
else
c - > options & = ~ OPTION_CLAMP_MSS ;
}
2002-09-09 21:25:28 +00:00
/* Activate this connection */
c - > allow_request = ALL ;
2012-02-26 17:37:36 +00:00
logger ( DEBUG_CONNECTIONS , LOG_NOTICE , " Connection with %s (%s) activated " , c - > name ,
2002-09-09 21:25:28 +00:00
c - > hostname ) ;
/* Send him everything we know */
2003-11-17 15:30:18 +00:00
send_everything ( c ) ;
2002-09-09 21:25:28 +00:00
/* Create an edge_t for this connection */
c - > edge = new_edge ( ) ;
c - > edge - > from = myself ;
c - > edge - > to = n ;
2010-04-03 08:46:45 +00:00
sockaddr2str ( & c - > address , & hisaddress , NULL ) ;
2002-09-09 21:25:28 +00:00
c - > edge - > address = str2sockaddr ( hisaddress , hisport ) ;
free ( hisaddress ) ;
2014-06-22 15:29:30 +00:00
sockaddr_t local_sa ;
socklen_t local_salen = sizeof local_sa ;
if ( getsockname ( c - > socket , & local_sa . sa , & local_salen ) < 0 )
logger ( DEBUG_ALWAYS , LOG_WARNING , " Could not get local socket address for connection with %s " , c - > name ) ;
else {
char * local_address ;
sockaddr2str ( & local_sa , & local_address , NULL ) ;
c - > edge - > local_address = str2sockaddr ( local_address , myport ) ;
free ( local_address ) ;
}
2002-09-09 21:25:28 +00:00
c - > edge - > weight = ( weight + c - > estimated_weight ) / 2 ;
c - > edge - > connection = c ;
c - > edge - > options = c - > options ;
edge_add ( c - > edge ) ;
/* Notify everyone of the new edge */
2003-11-17 15:30:18 +00:00
if ( tunnelserver )
2003-11-10 22:31:53 +00:00
send_add_edge ( c , c - > edge ) ;
2003-11-17 15:30:18 +00:00
else
2012-02-20 16:12:48 +00:00
send_add_edge ( everyone , c - > edge ) ;
2002-09-09 21:25:28 +00:00
/* Run MST and SSSP algorithms */
graph ( ) ;
2003-07-22 20:55:21 +00:00
return true ;
2002-02-11 10:05:58 +00:00
}