2 protocol.c -- handle the meta-protocol
3 Copyright (C) 1999,2000 Ivo Timmermans <itimmermans@bigfoot.com>,
4 2000 Guus Sliepen <guus@sliepen.warande.net>
6 This program is free software; you can redistribute it and/or modify
7 it under the terms of the GNU General Public License as published by
8 the Free Software Foundation; either version 2 of the License, or
9 (at your option) any later version.
11 This program is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 GNU General Public License for more details.
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 $Id: protocol.c,v 1.29 2000/10/18 20:12:09 zarq Exp $
25 #include <sys/types.h>
30 #include <sys/socket.h>
37 #include <netinet/in.h>
39 #include <openssl/sha.h>
50 int check_id(char *id)
54 for (i = 0; i < strlen(id); i++)
55 if(!isalnum(id[i]) && id[i] != '_')
61 /* Generic request routines - takes care of logging and error detection as well */
63 int send_request(conn_list_t *cl, const char *format, ...)
66 char buffer[MAXBUFSIZE];
70 /* Use vsnprintf instead of vasprintf: faster, no memory fragmentation, cleanup is automatic,
71 and there is a limit on the input buffer anyway */
73 va_start(args, format);
74 len = vsnprintf(buffer, MAXBUFSIZE, format, args);
75 request = va_arg(args, int);
78 if(len < 0 || len > MAXBUFSIZE-1)
80 syslog(LOG_ERR, _("Output buffer overflow while sending %s to %s (%s)"), request_name[request], cl->name, cl->hostname);
86 if(debug_lvl >= DEBUG_PROTOCOL)
87 syslog(LOG_DEBUG, _("Sending %s to %s (%s)"), request_name[request], cl->name, cl->hostname);
90 return send_meta(cl, buffer, len);
93 int receive_request(conn_list_t *cl)
97 if(sscanf(cl->buffer, "%d", &request) == 1)
99 if((request < 0) || (request > 255) || (request_handlers[request] == NULL))
101 syslog(LOG_ERR, _("Unknown request from %s (%s)"),
102 cl->name, cl->hostname);
107 if(debug_lvl > DEBUG_PROTOCOL)
108 syslog(LOG_DEBUG, _("Got %s from %s (%s)"),
109 request_name[request], cl->name, cl->hostname);
111 if(request_handlers[request](cl))
112 /* Something went wrong. Probably scriptkiddies. Terminate. */
114 syslog(LOG_ERR, _("Error while processing %s from %s (%s)"),
115 request_name[request], cl->name, cl->hostname);
121 syslog(LOG_ERR, _("Bogus data received from %s (%s)"),
122 cl->name, cl->hostname);
127 /* Connection protocol:
136 ---------------------------------------
137 Any negotations about the meta protocol
138 encryption go here(u).
139 ---------------------------------------
142 ---------------------------------------
148 (E) Encrypted with symmetric cipher.
150 Part of the challenge is directly used to set the symmetric cipher key and the initial vector.
151 Since a man-in-the-middle cannot decrypt the RSA challenges, this means that he cannot get or
152 forge the key for the symmetric cipher.
155 int send_id(conn_list_t *cl)
158 cl->allow_request = CHALLENGE;
160 return send_request(cl, "%d %s %d %lx %hd", ID, myself->name, myself->protocol_version, myself->options, myself->port);
163 int id_h(conn_list_t *cl)
167 if(sscanf(cl->buffer, "%*d %as %d %lx %hd", &cl->name, &cl->protocol_version, &cl->options, &cl->port) != 4)
169 syslog(LOG_ERR, _("Got bad ID from %s"), cl->hostname);
173 /* Check if version matches */
175 if(cl->protocol_version != myself->protocol_version)
177 syslog(LOG_ERR, _("Peer %s (%s) uses incompatible version %d"),
178 cl->name, cl->hostname, cl->protocol_version);
182 /* Check if identity is a valid name */
184 if(check_id(cl->name))
186 syslog(LOG_ERR, _("Peer %s uses invalid identity name"), cl->hostname);
190 /* Load information about peer */
192 if(read_host_config(cl))
194 syslog(LOG_ERR, _("Peer %s had unknown identity (%s)"), cl->hostname, cl->name);
199 /* First check if the host we connected to is already in our
200 connection list. If so, we are probably making a loop, which
204 if(cl->status.outgoing)
206 if((old = lookup_id(cl->name)))
208 if(debug_lvl > DEBUG_CONNECTIONS)
209 syslog(LOG_NOTICE, _("Uplink %s (%s) is already in our connection list"), cl->name, cl->hostname);
210 cl->status.outgoing = 0;
211 old->status.outgoing = 1;
212 terminate_connection(cl);
217 return send_challenge(cl);
220 int send_challenge(conn_list_t *cl)
222 char buffer[CHAL_LENGTH*2+1];
224 /* Allocate buffers for the challenge */
226 if(!cl->hischallenge)
227 cl->hischallenge = xmalloc(CHAL_LENGTH);
229 /* Copy random data to the buffer */
231 RAND_bytes(cl->hischallenge, CHAL_LENGTH);
233 /* Convert the random data to a hexadecimal formatted string */
235 bin2hex(cl->hischallenge, buffer, CHAL_LENGTH);
236 buffer[CHAL_LENGTH*2] = '\0';
238 /* Send the challenge */
240 cl->allow_request = CHAL_REPLY;
242 return send_request(cl, "%d %s", CHALLENGE, buffer);
245 int challenge_h(conn_list_t *cl)
249 if(sscanf(cl->buffer, "%*d %as", &buffer) != 1)
251 syslog(LOG_ERR, _("Got bad CHALLENGE from %s (%s)"), cl->name, cl->hostname);
255 /* Check if the length of the challenge is all right */
257 if(strlen(buffer) != CHAL_LENGTH*2)
259 syslog(LOG_ERR, _("Intruder: wrong challenge length from %s (%s)"), cl->name, cl->hostname);
264 /* Allocate buffers for the challenge */
267 cl->mychallenge = xmalloc(CHAL_LENGTH);
269 /* Convert the challenge from hexadecimal back to binary */
271 hex2bin(buffer,cl->mychallenge,CHAL_LENGTH);
274 /* Rest is done by send_chal_reply() */
276 return send_chal_reply(cl);
279 int send_chal_reply(conn_list_t *cl)
281 char hash[SHA_DIGEST_LENGTH*2+1];
285 syslog(LOG_ERR, _("Trying to send CHAL_REPLY to %s (%s) without a valid CHALLENGE"), cl->name, cl->hostname);
289 /* Calculate the hash from the challenge we received */
291 SHA1(cl->mychallenge, CHAL_LENGTH, hash);
293 /* Convert the hash to a hexadecimal formatted string */
295 bin2hex(hash,hash,SHA_DIGEST_LENGTH);
296 hash[SHA_DIGEST_LENGTH*2] = '\0';
300 if(cl->status.outgoing)
301 cl->allow_request = ID;
303 cl->allow_request = ACK;
306 return send_request(cl, "%d %s", CHAL_REPLY, hash);
309 int chal_reply_h(conn_list_t *cl)
312 char myhash[SHA_DIGEST_LENGTH];
314 if(sscanf(cl->buffer, "%*d %as", &hishash) != 1)
316 syslog(LOG_ERR, _("Got bad CHAL_REPLY from %s (%s)"), cl->name, cl->hostname);
321 /* Check if the length of the hash is all right */
323 if(strlen(hishash) != SHA_DIGEST_LENGTH*2)
325 syslog(LOG_ERR, _("Intruder: wrong challenge reply length from %s (%s)"), cl->name, cl->hostname);
330 /* Convert the hash to binary format */
332 hex2bin(hishash, hishash, SHA_DIGEST_LENGTH);
334 /* Calculate the hash from the challenge we sent */
336 SHA1(cl->hischallenge, CHAL_LENGTH, myhash);
338 /* Verify the incoming hash with the calculated hash */
340 if(memcmp(hishash, myhash, SHA_DIGEST_LENGTH))
342 syslog(LOG_ERR, _("Intruder: wrong challenge reply from %s (%s)"), cl->name, cl->hostname);
349 /* Identity has now been positively verified.
350 If we are accepting this new connection, then send our identity,
351 if we are making this connecting, acknowledge.
354 if(cl->status.outgoing)
360 int send_ack(conn_list_t *cl)
363 cl->allow_request = ACK;
365 return send_request(cl, "%d", ACK);
368 int ack_h(conn_list_t *cl)
372 /* Okay, before we active the connection, we check if there is another entry
373 in the connection list with the same name. If so, it presumably is an
374 old connection that has timed out but we don't know it yet.
377 while((old = lookup_id(cl->name)))
379 if(debug_lvl > DEBUG_CONNECTIONS)
380 syslog(LOG_NOTICE, _("Removing old entry for %s at %s in favour of new connection from %s"),
381 cl->name, old->hostname, cl->hostname);
382 old->status.active = 0;
383 terminate_connection(old);
386 /* Activate this connection */
388 cl->allow_request = ALL;
389 cl->status.active = 1;
391 if(debug_lvl > DEBUG_CONNECTIONS)
392 syslog(LOG_NOTICE, _("Connection with %s (%s) activated"), cl->name, cl->hostname);
394 /* Exchange information about other tinc daemons */
396 /* FIXME: reprogram this.
397 notify_others(cl, NULL, send_add_host);
403 if(cl->status.outgoing)
409 /* Address and subnet information exchange */
411 int send_add_subnet(conn_list_t *cl, conn_list_t *other, subnet_t *subnet)
416 x = send_request(cl, "%d %s %s", ADD_SUBNET,
417 other->name, netstr = net2str(subnet));
423 int add_subnet_h(conn_list_t *cl)
428 subnet_t *subnet, *old;
430 if(sscanf(cl->buffer, "%*d %as %as", &name, &subnetstr) != 3)
432 syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s)"), cl->name, cl->hostname);
433 free(name); free(subnetstr);
437 /* Check if owner name is a valid */
441 syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s): invalid identity name"), cl->name, cl->hostname);
442 free(name); free(subnetstr);
446 /* Check if subnet string is valid */
448 if(!(subnet = str2net(subnetstr)))
450 syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s): invalid subnet string"), cl->name, cl->hostname);
451 free(name); free(subnetstr);
457 /* Check if somebody tries to add a subnet of ourself */
459 if(!strcmp(name, myself->name))
461 syslog(LOG_ERR, _("Warning: got ADD_SUBNET from %s (%s) for ourself, restarting"),
462 cl->name, cl->hostname);
468 /* Check if the owner of the new subnet is in the connection list */
470 if(!(owner = lookup_id(name)))
472 syslog(LOG_ERR, _("Got ADD_SUBNET for %s from %s (%s) which is not in our connection list"),
473 name, cl->name, cl->hostname);
478 /* If everything is correct, add the subnet to the list of the owner */
480 subnet_add(owner, subnet);
485 int send_del_subnet(conn_list_t *cl, conn_list_t *other, subnet_t *subnet)
488 return send_request(cl, "%d %s %s", DEL_SUBNET, other->name, net2str(subnet));
491 int del_subnet_h(conn_list_t *cl)
496 subnet_t *subnet, *old;
498 if(sscanf(cl->buffer, "%*d %as %as", &name, &subnetstr) != 3)
500 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s)"), cl->name, cl->hostname);
501 free(name); free(subnetstr);
505 /* Check if owner name is a valid */
509 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s): invalid identity name"), cl->name, cl->hostname);
510 free(name); free(subnetstr);
514 /* Check if subnet string is valid */
516 if(!(subnet = str2net(subnetstr)))
518 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s): invalid subnet string"), cl->name, cl->hostname);
519 free(name); free(subnetstr);
525 /* Check if somebody tries to add a subnet of ourself */
527 if(!strcmp(name, myself->name))
529 syslog(LOG_ERR, _("Warning: got DEL_SUBNET from %s (%s) for ourself, restarting"),
530 cl->name, cl->hostname);
536 /* Check if the owner of the new subnet is in the connection list */
538 if(!(owner = lookup_id(name)))
540 syslog(LOG_ERR, _("Got DEL_SUBNET for %s from %s (%s) which is not in our connection list"),
541 name, cl->name, cl->hostname);
546 /* If everything is correct, delete the subnet from the list of the owner */
553 /* New and closed connections notification */
555 int send_add_host(conn_list_t *cl, conn_list_t *other)
558 return send_request(cl, "%d %s %s %lx:%d %lx", ADD_HOST,
559 myself->name, other->name, other->address, other->port, other->options);
562 int add_host_h(conn_list_t *cl)
565 conn_list_t *old, *new, *hisuplink;
567 new = new_conn_list();
569 if(sscanf(cl->buffer, "%*d %as %as %lx:%d %lx", &sender, &new->name, &new->address, &new->port, &new->options) != 5)
571 syslog(LOG_ERR, _("Got bad ADD_HOST from %s (%s)"), cl->name, cl->hostname);
575 /* Check if identity is a valid name */
577 if(check_id(new->name) || check_id(sender))
579 syslog(LOG_ERR, _("Got bad ADD_HOST from %s (%s): invalid identity name"), cl->name, cl->hostname);
584 /* Check if somebody tries to add ourself */
586 if(!strcmp(new->name, myself->name))
588 syslog(LOG_ERR, _("Warning: got ADD_HOST from %s (%s) for ourself, restarting"), cl->name, cl->hostname);
594 /* We got an ADD_HOST from ourself!? */
596 if(!strcmp(sender, myself->name))
598 syslog(LOG_ERR, _("Warning: got ADD_HOST from %s (%s) from ourself, restarting"), cl->name, cl->hostname);
604 /* Lookup his uplink */
606 if(!(new->hisuplink = lookup_id(sender)))
608 syslog(LOG_ERR, _("Got ADD_HOST from %s (%s) with origin %s which is not in our connection list"),
609 sender, cl->name, cl->hostname);
616 /* Fill in more of the new conn_list structure */
618 new->hostname = hostlookup(htonl(new->address));
620 /* Check if the new host already exists in the connnection list */
622 if((old = lookup_id(new->name)))
624 if((new->address == old->address) && (new->port == old->port))
626 if(debug_lvl > DEBUG_CONNECTIONS)
627 syslog(LOG_NOTICE, _("Got duplicate ADD_HOST for %s (%s) from %s (%s)"),
628 old->name, old->hostname, new->name, new->hostname);
633 if(debug_lvl > DEBUG_CONNECTIONS)
634 syslog(LOG_NOTICE, _("Removing old entry for %s (%s)"),
635 old->name, old->hostname);
636 old->status.active = 0;
637 terminate_connection(old);
641 /* Fill in rest of conn_list structure */
644 new->status.active = 1;
646 /* Hook it up into the conn_list */
648 conn_list_add(conn_list, new);
650 /* Tell the rest about the new host */
651 /* FIXME: reprogram this.
652 notify_others(new, cl, send_add_host);
658 int send_del_host(conn_list_t *cl, conn_list_t *other)
661 return send_request(cl, "%d %s %s %lx:%d %lx", DEL_HOST,
662 myself->name, other->name, other->address, other->port, other->options);
665 int del_host_h(conn_list_t *cl)
672 conn_list_t *old, *hisuplink;
675 if(sscanf(cl->buffer, "%*d %as %as %lx:%d %lx", &sender, &name, &address, &port, &options) != 5)
677 syslog(LOG_ERR, _("Got bad DEL_HOST from %s (%s)"),
678 cl->name, cl->hostname);
682 /* Check if identity is a valid name */
684 if(check_id(name) || check_id(sender))
686 syslog(LOG_ERR, _("Got bad DEL_HOST from %s (%s): invalid identity name"), cl->name, cl->hostname);
687 free(name); free(sender);
691 /* Check if somebody tries to delete ourself */
693 if(!strcmp(name, myself->name))
695 syslog(LOG_ERR, _("Warning: got DEL_HOST from %s (%s) for ourself, restarting"),
696 cl->name, cl->hostname);
697 free(name); free(sender);
702 /* We got an ADD_HOST from ourself!? */
704 if(!strcmp(sender, myself->name))
706 syslog(LOG_ERR, _("Warning: got DEL_HOST from %s (%s) from ourself, restarting"), cl->name, cl->hostname);
708 free(name); free(sender);
712 /* Lookup his uplink */
714 if(!(hisuplink = lookup_id(sender)))
716 syslog(LOG_ERR, _("Got DEL_HOST from %s (%s) with origin %s which is not in our connection list"),
717 cl->name, cl->hostname, sender);
718 free(name); free(sender);
724 /* Check if the new host already exists in the connnection list */
726 if(!(old = lookup_id(name)))
728 syslog(LOG_ERR, _("Got DEL_HOST from %s (%s) for %s which is not in our connection list"),
729 name, cl->name, cl->hostname);
734 /* Check if the rest matches */
736 if(address!=old->address || port!=old->port || options!=old->options || hisuplink!=old->hisuplink || cl!=old->myuplink)
738 syslog(LOG_WARNING, _("Got DEL_HOST from %s (%s) for %s which doesn't match"), cl->name, cl->hostname, old->name);
742 /* Ok, since EVERYTHING seems to check out all right, delete it */
744 old->status.termreq = 1;
745 old->status.active = 0;
747 terminate_connection(old);
752 /* Status and error notification routines */
754 int send_status(conn_list_t *cl, int statusno, char *statusstring)
758 statusstring = status_text[statusno];
760 return send_request(cl, "%d %d %s", STATUS, statusno, statusstring);
763 int status_h(conn_list_t *cl)
768 if(sscanf(cl->buffer, "%*d %d %as", &statusno, &statusstring) != 2)
770 syslog(LOG_ERR, _("Got bad STATUS from %s (%s)"),
771 cl->name, cl->hostname);
775 if(debug_lvl > DEBUG_STATUS)
777 syslog(LOG_NOTICE, _("Status message from %s (%s): %s: %s"),
778 cl->name, cl->hostname, status_text[statusno], statusstring);
786 int send_error(conn_list_t *cl, int errno, char *errstring)
790 errstring = strerror(errno);
791 return send_request(cl, "%d %d %s", ERROR, errno, errstring);
794 int error_h(conn_list_t *cl)
799 if(sscanf(cl->buffer, "%*d %d %as", &errno, &errorstring) != 2)
801 syslog(LOG_ERR, _("Got bad ERROR from %s (%s)"),
802 cl->name, cl->hostname);
806 if(debug_lvl > DEBUG_ERROR)
808 syslog(LOG_NOTICE, _("Error message from %s (%s): %s: %s"),
809 cl->name, cl->hostname, strerror(errno), errorstring);
813 cl->status.termreq = 1;
814 terminate_connection(cl);
819 int send_termreq(conn_list_t *cl)
822 return send_request(cl, "%d", TERMREQ);
825 int termreq_h(conn_list_t *cl)
828 cl->status.termreq = 1;
829 terminate_connection(cl);
834 /* Keepalive routines - FIXME: needs a closer look */
836 int send_ping(conn_list_t *cl)
838 cl->status.pinged = 1;
840 return send_request(cl, "%d", PING);
843 int ping_h(conn_list_t *cl)
846 return send_pong(cl);
849 int send_pong(conn_list_t *cl)
852 return send_request(cl, "%d", PONG);
855 int pong_h(conn_list_t *cl)
858 cl->status.got_pong = 1;
865 int send_key_changed(conn_list_t *from, conn_list_t *cl)
869 for(p = conn_list; p != NULL; p = p->next)
871 if(p!=cl && p->status.meta && p->status.active)
872 send_request(p, "%d %s", KEY_CHANGED,
879 int key_changed_h(conn_list_t *cl)
884 if(sscanf(cl->buffer, "%*d %as", &from_id) != 1)
886 syslog(LOG_ERR, _("Got bad KEY_CHANGED from %s (%s)"),
887 cl->name, cl->hostname);
891 if(!(from = lookup_id(from_id)))
893 syslog(LOG_ERR, _("Got KEY_CHANGED from %s (%s) origin %s which does not exist in our connection list"),
894 cl->name, cl->hostname, from_id);
901 from->status.validkey = 0;
902 from->status.waitingforkey = 0;
904 send_key_changed(from, cl);
909 int send_req_key(conn_list_t *from, conn_list_t *to)
912 return send_request(to->nexthop, "%d %s %s", REQ_KEY,
913 from->name, to->name);
916 int req_key_h(conn_list_t *cl)
918 char *from_id, *to_id;
919 conn_list_t *from, *to;
921 if(sscanf(cl->buffer, "%*d %as %as", &from_id, &to_id) != 2)
923 syslog(LOG_ERR, _("Got bad REQ_KEY from %s (%s)"),
924 cl->name, cl->hostname);
928 if(!(from = lookup_id(from_id)))
930 syslog(LOG_ERR, _("Got REQ_KEY from %s (%s) origin %s which does not exist in our connection list"),
931 cl->name, cl->hostname, from_id);
932 free(from_id); free(to_id);
936 /* Check if this key request is for us */
938 if(!strcmp(to_id, myself->name))
940 send_ans_key(myself, from, myself->cipher_pktkey);
944 if(!(to = lookup_id(to_id)))
946 syslog(LOG_ERR, _("Got REQ_KEY from %s (%s) destination %s which does not exist in our connection list"),
947 cl->name, cl->hostname, to_id);
948 free(from_id); free(to_id);
951 send_req_key(from, to);
954 free(from_id); free(to_id);
959 int send_ans_key(conn_list_t *from, conn_list_t *to, char *pktkey)
962 return send_request(to->nexthop, "%d %s %s %s", ANS_KEY,
963 from->name, to->name, pktkey);
966 int ans_key_h(conn_list_t *cl)
968 char *from_id, *to_id, *pktkey;
970 conn_list_t *from, *to;
972 if(sscanf(cl->buffer, "%*d %as %as %as", &from_id, &to_id, &pktkey) != 3)
974 syslog(LOG_ERR, _("Got bad ANS_KEY from %s (%s)"),
975 cl->name, cl->hostname);
979 if(!(from = lookup_id(from_id)))
981 syslog(LOG_ERR, _("Got ANS_KEY from %s (%s) origin %s which does not exist in our connection list"),
982 cl->name, cl->hostname, from_id);
983 free(from_id); free(to_id); free(pktkey);
987 /* Check if this key request is for us */
989 if(!strcmp(to_id, myself->name))
991 /* It is for us, convert it to binary and set the key with it. */
993 keylength = strlen(pktkey);
995 if((keylength%2) || (keylength <= 0))
997 syslog(LOG_ERR, _("Got bad ANS_KEY from %s (%s) origin %s: invalid key"),
998 cl->name, cl->hostname, from->name);
999 free(from_id); free(to_id); free(pktkey);
1003 hex2bin(pktkey, pktkey, keylength);
1004 BF_set_key(cl->cipher_pktkey, keylength, pktkey);
1008 if(!(to = lookup_id(to_id)))
1010 syslog(LOG_ERR, _("Got ANS_KEY from %s (%s) destination %s which does not exist in our connection list"),
1011 cl->name, cl->hostname, to_id);
1012 free(from_id); free(to_id); free(pktkey);
1015 send_ans_key(from, to, pktkey);
1018 free(from_id); free(to_id); free(pktkey);
1023 /* Jumptable for the request handlers */
1025 int (*request_handlers[])(conn_list_t*) = {
1026 id_h, challenge_h, chal_reply_h, ack_h,
1027 status_h, error_h, termreq_h,
1029 add_host_h, del_host_h,
1030 add_subnet_h, del_subnet_h,
1031 key_changed_h, req_key_h, ans_key_h,
1036 char (*request_name[]) = {
1037 "ID", "CHALLENGE", "CHAL_REPLY", "ACK",
1038 "STATUS", "ERROR", "TERMREQ",
1040 "ADD_HOST", "DEL_HOST",
1041 "ADD_SUBNET", "DEL_SUBNET",
1042 "KEY_CHANGED", "REQ_KEY", "ANS_KEY",
1045 /* Status strings */
1047 char (*status_text[]) = {
1053 char (*error_text[]) = {