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.28.4.38 2000/10/11 22:01:00 guus 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++)
56 if(!isalpha(id[i]) && id[i] != '_')
65 /* Generic request routines - takes care of logging and error detection as well */
67 int send_request(conn_list_t *cl, const char *format, int request, /*args*/ ...)
70 char buffer[MAXBUFSIZE+1];
74 /* Use vsnprintf instead of vasprintf: faster, no memory fragmentation, cleanup is automatic,
75 and there is a limit on the input buffer anyway */
77 va_start(args, request);
78 len = vsnprintf(buffer, MAXBUFSIZE+1, format, args);
81 if(len < 0 || len > MAXBUFSIZE)
83 syslog(LOG_ERR, _("Output buffer overflow while sending %s to %s (%s)"), request_name[request], cl->name, cl->hostname);
87 if(debug_lvl >= DEBUG_PROTOCOL)
88 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 return send_request(cl, "%d %s %d %lx", ID, myself->name, myself->protocol_version, myself->options);
161 int id_h(conn_list_t *cl)
165 if(sscanf(cl->buffer, "%*d %as %d %lx", &cl->name, &cl->protocol_version, &cl->options) != 3)
167 syslog(LOG_ERR, _("Got bad ID from %s"), cl->hostname);
171 /* Check if version matches */
173 if(cl->protocol_version != myself->protocol_version)
175 syslog(LOG_ERR, _("Peer %s (%s) uses incompatible version %d"),
176 cl->name, cl->hostname, cl->protocol_version);
180 /* Check if identity is a valid name */
182 if(!check_id(cl->name))
184 syslog(LOG_ERR, _("Peer %s uses invalid identity name"), cl->hostname);
188 /* Load information about peer */
190 if(!read_host_config(cl))
192 syslog(LOG_ERR, _("Peer %s had unknown identity (%s)"), cl->hostname, cl->name);
197 /* First check if the host we connected to is already in our
198 connection list. If so, we are probably making a loop, which
202 if(cl->status.outgoing)
204 if((old = lookup_id(cl->name)))
206 if(debug_lvl > DEBUG_CONNECTIONS)
207 syslog(LOG_NOTICE, _("Uplink %s (%s) is already in our connection list"), cl->name, cl->hostname);
208 cl->status.outgoing = 0;
209 old->status.outgoing = 1;
210 terminate_connection(cl);
215 /* Send a challenge to verify the identity */
217 cl->allow_request = CHAL_REPLY;
219 return send_challenge(cl);
222 int send_challenge(conn_list_t *cl)
224 char buffer[CHAL_LENGTH*2+1];
226 /* Allocate buffers for the challenge */
228 if(!cl->hischallenge)
229 cl->hischallenge = xmalloc(CHAL_LENGTH);
231 /* Copy random data to the buffer */
233 RAND_bytes(cl->hischallenge, CHAL_LENGTH);
235 /* Convert the random data to a hexadecimal formatted string */
237 bin2hex(cl->hischallenge,buffer,CHAL_LENGTH);
238 buffer[CHAL_LENGTH*2] = '\0';
240 /* Send the challenge */
242 cl->allow_request = CHAL_REPLY;
244 return send_request(cl, "%d %s", CHALLENGE, buffer);
247 int challenge_h(conn_list_t *cl)
251 if(sscanf(cl->buffer, "%*d %as", &buffer) != 1)
253 syslog(LOG_ERR, _("Got bad CHALLENGE from %s (%s)"), cl->name, cl->hostname);
257 /* Check if the length of the challenge is all right */
259 if(strlen(buffer) != CHAL_LENGTH*2)
261 syslog(LOG_ERR, _("Intruder: wrong challenge length from %s (%s)"), cl->name, cl->hostname);
266 /* Allocate buffers for the challenge */
269 cl->mychallenge = xmalloc(CHAL_LENGTH);
271 /* Convert the challenge from hexadecimal back to binary */
273 hex2bin(buffer,cl->mychallenge,CHAL_LENGTH);
276 /* Rest is done by send_chal_reply() */
278 return send_chal_reply(cl);
281 int send_chal_reply(conn_list_t *cl)
283 char hash[SHA_DIGEST_LENGTH*2+1];
287 syslog(LOG_ERR, _("Trying to send CHAL_REPLY to %s (%s) without a valid CHALLENGE"), cl->name, cl->hostname);
291 /* Calculate the hash from the challenge we received */
293 SHA1(cl->mychallenge, CHAL_LENGTH, hash);
295 /* Convert the hash to a hexadecimal formatted string */
297 bin2hex(hash,hash,SHA_DIGEST_LENGTH);
298 hash[SHA_DIGEST_LENGTH*2] = '\0';
302 if(cl->status.outgoing)
303 cl->allow_request = ID;
305 cl->allow_request = ACK;
308 return send_request(cl, "%d %s", CHAL_REPLY, hash);
311 int chal_reply_h(conn_list_t *cl)
314 char myhash[SHA_DIGEST_LENGTH];
316 if(sscanf(cl->buffer, "%*d %as", &hishash) != 2)
318 syslog(LOG_ERR, _("Got bad CHAL_REPLY from %s (%s)"), cl->name, cl->hostname);
323 /* Check if the length of the hash is all right */
325 if(strlen(hishash) != SHA_DIGEST_LENGTH*2)
327 syslog(LOG_ERR, _("Intruder: wrong challenge reply length from %s (%s)"), cl->name, cl->hostname);
332 /* Convert the hash to binary format */
334 hex2bin(hishash, hishash, SHA_DIGEST_LENGTH);
336 /* Calculate the hash from the challenge we sent */
338 SHA1(cl->hischallenge, CHAL_LENGTH, myhash);
340 /* Verify the incoming hash with the calculated hash */
342 if(!memcmp(hishash, myhash, SHA_DIGEST_LENGTH))
344 syslog(LOG_ERR, _("Intruder: wrong challenge reply from %s (%s)"), cl->name, cl->hostname);
351 /* Identity has now been positively verified.
352 If we are accepting this new connection, then send our identity,
353 if we are making this connecting, acknowledge.
356 if(cl->status.outgoing)
358 cl->allow_request = ACK;
363 cl->allow_request = CHALLENGE;
368 int send_ack(conn_list_t *cl)
371 return send_request(cl, "%d", ACK);
374 int ack_h(conn_list_t *cl)
378 /* Okay, before we active the connection, we check if there is another entry
379 in the connection list with the same name. If so, it presumably is an
380 old connection that has timed out but we don't know it yet.
383 while((old = lookup_id(cl->name)))
385 if(debug_lvl > DEBUG_CONNECTIONS)
386 syslog(LOG_NOTICE, _("Removing old entry for %s at %s in favour of new connection from %s"),
387 cl->name, old->hostname, cl->hostname);
388 old->status.active = 0;
389 terminate_connection(old);
392 /* Activate this connection */
394 cl->allow_request = ALL;
395 cl->status.active = 1;
397 if(debug_lvl > DEBUG_CONNECTIONS)
398 syslog(LOG_NOTICE, _("Connection with %s (%s) activated"), cl->name, cl->hostname);
400 /* Exchange information about other tinc daemons */
402 notify_others(cl, NULL, send_add_host);
408 if(cl->status.outgoing)
414 /* Address and subnet information exchange */
416 int send_add_subnet(conn_list_t *cl, conn_list_t *other, subnet_t *subnet)
421 x = send_request(cl, "%d %s %s", ADD_SUBNET,
422 other->name, netstr = net2str(subnet));
428 int add_subnet_h(conn_list_t *cl)
433 subnet_t *subnet, *old;
435 if(sscanf(cl->buffer, "%*d %as %as", &name, &subnetstr) != 3)
437 syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s)"), cl->name, cl->hostname);
438 free(name); free(subnetstr);
442 /* Check if owner name is a valid */
446 syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s): invalid identity name"), cl->name, cl->hostname);
447 free(name); free(subnetstr);
451 /* Check if subnet string is valid */
453 if(!(subnet = str2net(subnetstr)))
455 syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s): invalid subnet string"), cl->name, cl->hostname);
456 free(name); free(subnetstr);
462 /* Check if somebody tries to add a subnet of ourself */
464 if(!strcmp(name, myself->name))
466 syslog(LOG_ERR, _("Warning: got ADD_SUBNET from %s (%s) for ourself, restarting"),
467 cl->name, cl->hostname);
473 /* Check if the owner of the new subnet is in the connection list */
475 if(!(owner = lookup_id(name)))
477 syslog(LOG_ERR, _("Got ADD_SUBNET for %s from %s (%s) which is not in our connection list"),
478 name, cl->name, cl->hostname);
483 /* If everything is correct, add the subnet to the list of the owner */
485 subnet_add(owner, subnet);
490 int send_del_subnet(conn_list_t *cl, conn_list_t *other, subnet_t *subnet)
493 return send_request(cl, "%d %s %s", DEL_SUBNET, other->name, net2str(subnet));
496 int del_subnet_h(conn_list_t *cl)
501 subnet_t *subnet, *old;
503 if(sscanf(cl->buffer, "%*d %as %as", &name, &subnetstr) != 3)
505 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s)"), cl->name, cl->hostname);
506 free(name); free(subnetstr);
510 /* Check if owner name is a valid */
514 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s): invalid identity name"), cl->name, cl->hostname);
515 free(name); free(subnetstr);
519 /* Check if subnet string is valid */
521 if(!(subnet = str2net(subnetstr)))
523 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s): invalid subnet string"), cl->name, cl->hostname);
524 free(name); free(subnetstr);
530 /* Check if somebody tries to add a subnet of ourself */
532 if(!strcmp(name, myself->name))
534 syslog(LOG_ERR, _("Warning: got DEL_SUBNET from %s (%s) for ourself, restarting"),
535 cl->name, cl->hostname);
541 /* Check if the owner of the new subnet is in the connection list */
543 if(!(owner = lookup_id(name)))
545 syslog(LOG_ERR, _("Got DEL_SUBNET for %s from %s (%s) which is not in our connection list"),
546 name, cl->name, cl->hostname);
551 /* If everything is correct, delete the subnet from the list of the owner */
558 /* New and closed connections notification */
560 int send_add_host(conn_list_t *cl, conn_list_t *other)
563 return send_request(cl, "%d %s %s %lx:%d %lx", ADD_HOST,
564 myself->name, other->name, other->address, other->port, other->options);
567 int add_host_h(conn_list_t *cl)
570 conn_list_t *old, *new, *hisuplink;
572 new = new_conn_list();
574 if(sscanf(cl->buffer, "%*d %as %as %lx:%d %lx", &sender, &new->name, &new->address, &new->port, &new->options) != 5)
576 syslog(LOG_ERR, _("Got bad ADD_HOST from %s (%s)"), cl->name, cl->hostname);
580 /* Check if identity is a valid name */
582 if(!check_id(new->name) || !check_id(sender))
584 syslog(LOG_ERR, _("Got bad ADD_HOST from %s (%s): invalid identity name"), cl->name, cl->hostname);
589 /* Check if somebody tries to add ourself */
591 if(!strcmp(new->name, myself->name))
593 syslog(LOG_ERR, _("Warning: got ADD_HOST from %s (%s) for ourself, restarting"), cl->name, cl->hostname);
599 /* We got an ADD_HOST from ourself!? */
601 if(!strcmp(sender, myself->name))
603 syslog(LOG_ERR, _("Warning: got ADD_HOST from %s (%s) from ourself, restarting"), cl->name, cl->hostname);
609 /* Lookup his uplink */
611 if(!(new->hisuplink = lookup_id(sender)))
613 syslog(LOG_ERR, _("Got ADD_HOST from %s (%s) with origin %s which is not in our connection list"),
614 sender, cl->name, cl->hostname);
621 /* Fill in more of the new conn_list structure */
623 new->hostname = hostlookup(htonl(new->address));
625 /* Check if the new host already exists in the connnection list */
627 if((old = lookup_id(new->name)))
629 if((new->address == old->address) && (new->port == old->port))
631 if(debug_lvl > DEBUG_CONNECTIONS)
632 syslog(LOG_NOTICE, _("Got duplicate ADD_HOST for %s (%s) from %s (%s)"),
633 old->name, old->hostname, new->name, new->hostname);
638 if(debug_lvl > DEBUG_CONNECTIONS)
639 syslog(LOG_NOTICE, _("Removing old entry for %s (%s)"),
640 old->name, old->hostname);
641 old->status.active = 0;
642 terminate_connection(old);
646 /* Fill in rest of conn_list structure */
649 new->status.active = 1;
651 /* Hook it up into the conn_list */
653 conn_list_add(conn_list, new);
655 /* Tell the rest about the new host */
657 notify_others(new, cl, send_add_host);
663 int send_del_host(conn_list_t *cl, conn_list_t *other)
666 return send_request(cl, "%d %s %s %lx:%d %lx", DEL_HOST,
667 myself->name, other->name, other->address, other->port, other->options);
670 int del_host_h(conn_list_t *cl)
677 conn_list_t *old, *hisuplink;
680 if(sscanf(cl->buffer, "%*d %as %as %lx:%d %lx", &sender, &name, &address, &port, &options) != 5)
682 syslog(LOG_ERR, _("Got bad DEL_HOST from %s (%s)"),
683 cl->name, cl->hostname);
687 /* Check if identity is a valid name */
689 if(!check_id(name) || !check_id(sender))
691 syslog(LOG_ERR, _("Got bad DEL_HOST from %s (%s): invalid identity name"), cl->name, cl->hostname);
692 free(name); free(sender);
696 /* Check if somebody tries to delete ourself */
698 if(!strcmp(name, myself->name))
700 syslog(LOG_ERR, _("Warning: got DEL_HOST from %s (%s) for ourself, restarting"),
701 cl->name, cl->hostname);
702 free(name); free(sender);
707 /* We got an ADD_HOST from ourself!? */
709 if(!strcmp(sender, myself->name))
711 syslog(LOG_ERR, _("Warning: got DEL_HOST from %s (%s) from ourself, restarting"), cl->name, cl->hostname);
713 free(name); free(sender);
717 /* Lookup his uplink */
719 if(!(hisuplink = lookup_id(sender)))
721 syslog(LOG_ERR, _("Got DEL_HOST from %s (%s) with origin %s which is not in our connection list"),
722 cl->name, cl->hostname, sender);
723 free(name); free(sender);
729 /* Check if the new host already exists in the connnection list */
731 if(!(old = lookup_id(name)))
733 syslog(LOG_ERR, _("Got DEL_HOST from %s (%s) for %s which is not in our connection list"),
734 name, cl->name, cl->hostname);
739 /* Check if the rest matches */
741 if(address!=old->address || port!=old->port || options!=old->options || hisuplink!=old->hisuplink || cl!=old->myuplink)
743 syslog(LOG_WARNING, _("Got DEL_HOST from %s (%s) for %s which doesn't match"), cl->name, cl->hostname, old->name);
747 /* Ok, since EVERYTHING seems to check out all right, delete it */
749 old->status.termreq = 1;
750 old->status.active = 0;
752 terminate_connection(old);
757 /* Status and error notification routines */
759 int send_status(conn_list_t *cl, int statusno, char *statusstring)
763 statusstring = status_text[statusno];
765 return send_request(cl, "%d %d %s", STATUS, statusno, statusstring);
768 int status_h(conn_list_t *cl)
773 if(sscanf(cl->buffer, "%*d %d %as", &statusno, &statusstring) != 2)
775 syslog(LOG_ERR, _("Got bad STATUS from %s (%s)"),
776 cl->name, cl->hostname);
780 if(debug_lvl > DEBUG_STATUS)
782 syslog(LOG_NOTICE, _("Status message from %s (%s): %s: %s"),
783 cl->name, cl->hostname, status_text[statusno], statusstring);
791 int send_error(conn_list_t *cl, int errno, char *errstring)
795 errstring = strerror(errno);
796 return send_request(cl, "%d %d %s", ERROR, errno, errstring);
799 int error_h(conn_list_t *cl)
804 if(sscanf(cl->buffer, "%*d %d %as", &errno, &errorstring) != 2)
806 syslog(LOG_ERR, _("Got bad error from %s (%s)"),
807 cl->name, cl->hostname);
811 if(debug_lvl > DEBUG_error)
813 syslog(LOG_NOTICE, _("Error message from %s (%s): %s: %s"),
814 cl->name, cl->hostname, strerror(errno), errorstring);
818 cl->status.termreq = 1;
819 terminate_connection(cl);
824 int send_termreq(conn_list_t *cl)
827 return send_request(cl, "%d", TERMREQ);
830 int termreq_h(conn_list_t *cl)
833 cl->status.termreq = 1;
834 terminate_connection(cl);
839 /* Keepalive routines - FIXME: needs a closer look */
841 int send_ping(conn_list_t *cl)
843 cl->status.pinged = 1;
845 return send_request(cl, "%d", PING);
848 int ping_h(conn_list_t *cl)
851 return send_pong(cl);
854 int send_pong(conn_list_t *cl)
857 return send_request(cl, "%d", PONG);
860 int pong_h(conn_list_t *cl)
863 cl->status.got_pong = 1;
870 int send_key_changed(conn_list_t *from, conn_list_t *cl)
874 for(p = conn_list; p != NULL; p = p->next)
876 if(p!=cl && p->status.meta && p->status.active)
877 send_request(p, "%d %s", KEY_CHANGED,
884 int key_changed_h(conn_list_t *cl)
889 if(sscanf(cl->buffer, "%*d %as", &from_id) != 1)
891 syslog(LOG_ERR, _("Got bad KEY_CHANGED from %s (%s)"),
892 cl->name, cl->hostname);
896 if(!(from = lookup_id(from_id)))
898 syslog(LOG_ERR, _("Got KEY_CHANGED from %s (%s) origin %s which does not exist in our connection list"),
899 cl->name, cl->hostname, from_id);
906 from->status.validkey = 0;
907 from->status.waitingforkey = 0;
909 send_key_changed(from, cl);
914 int send_req_key(conn_list_t *from, conn_list_t *to)
917 return send_request(to->nexthop, "%d %s %s", REQ_KEY,
918 from->name, to->name);
921 int req_key_h(conn_list_t *cl)
923 char *from_id, *to_id;
924 conn_list_t *from, *to;
926 if(sscanf(cl->buffer, "%*d %as %as", &from_id, &to_id) != 2)
928 syslog(LOG_ERR, _("Got bad REQ_KEY from %s (%s)"),
929 cl->name, cl->hostname);
933 if(!(from = lookup_id(from_id)))
935 syslog(LOG_ERR, _("Got REQ_KEY from %s (%s) origin %s which does not exist in our connection list"),
936 cl->name, cl->hostname, from_id);
937 free(from_id); free(to_id);
941 /* Check if this key request is for us */
943 if(!strcmp(to_id, myself->name))
945 send_ans_key(myself, from, myself->datakey->key);
949 if(!(to = lookup_id(to_id)))
951 syslog(LOG_ERR, _("Got REQ_KEY from %s (%s) destination %s which does not exist in our connection list"),
952 cl->name, cl->hostname, to_id);
953 free(from_id); free(to_id);
956 send_req_key(from, to);
959 free(from_id); free(to_id);
964 int send_ans_key(conn_list_t *from, conn_list_t *to, char *datakey)
967 return send_request(to->nexthop, "%d %s %s %s", ANS_KEY,
968 from->name, to->name, datakey);
971 int ans_key_h(conn_list_t *cl)
973 char *from_id, *to_id, *datakey;
975 conn_list_t *from, *to;
977 if(sscanf(cl->buffer, "%*d %as %as %as", &from_id, &to_id, &datakey) != 3)
979 syslog(LOG_ERR, _("Got bad ANS_KEY from %s (%s)"),
980 cl->name, cl->hostname);
984 if(!(from = lookup_id(from_id)))
986 syslog(LOG_ERR, _("Got ANS_KEY from %s (%s) origin %s which does not exist in our connection list"),
987 cl->name, cl->hostname, from_id);
988 free(from_id); free(to_id); free(datakey);
992 /* Check if this key request is for us */
994 if(!strcmp(to_id, myself->name))
996 /* It is for us, convert it to binary and set the key with it. */
998 keylength = strlen(datakey);
1000 if((keylength%2) || (keylength <= 0))
1002 syslog(LOG_ERR, _("Got bad ANS_KEY from %s (%s) origin %s: invalid key"),
1003 cl->name, cl->hostname, from->name);
1004 free(from_id); free(to_id); free(datakey);
1008 hex2bin(datakey, datakey, keylength);
1009 BF_set_key(cl->datakey, keylength, datakey);
1013 if(!(to = lookup_id(to_id)))
1015 syslog(LOG_ERR, _("Got ANS_KEY from %s (%s) destination %s which does not exist in our connection list"),
1016 cl->name, cl->hostname, to_id);
1017 free(from_id); free(to_id); free(datakey);
1020 send_ans_key(from, to, datakey);
1023 free(from_id); free(to_id); free(datakey);
1028 /* Jumptable for the request handlers */
1030 int (*request_handlers[])(conn_list_t*) = {
1031 id_h, challenge_h, chal_reply_h, ack_h,
1032 status_h, error_h, termreq_h,
1034 add_host_h, del_host_h,
1035 add_subnet_h, del_subnet_h,
1036 key_changed_h, req_key_h, ans_key_h,
1041 char (*request_name[]) = {
1042 "ID", "CHALLENGE", "CHAL_REPLY", "ACK",
1043 "STATUS", "ERROR", "TERMREQ",
1045 "ADD_HOST", "DEL_HOST",
1046 "ADD_SUBNET", "DEL_SUBNET",
1047 "KEY_CHANGED", "REQ_KEY", "ANS_KEY",