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.36 2000/09/26 14:06:06 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, length);
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 */
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[keylength*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)) == -1)
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 return subnet_add(owner, subnet);
488 int send_del_subnet(conn_list_t *cl, conn_list_t *other, subnet_t *subnet)
491 return send_request(cl, "%d %s %s", DEL_SUBNET, other->name, net2str(subnet));
494 int del_subnet_h(conn_list_t *cl)
499 subnet_t *subnet, *old;
501 if(sscanf(cl->buffer, "%*d %as %as", &name, &subnetstr) != 3)
503 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s)"), cl->name, cl->hostname);
504 free(name); free(subnetstr);
508 /* Check if owner name is a valid */
512 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s): invalid identity name"), cl->name, cl->hostname);
513 free(name); free(subnetstr);
517 /* Check if subnet string is valid */
519 if((subnet = str2net(subnetstr)) == -1)
521 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s): invalid subnet string"), cl->name, cl->hostname);
522 free(name); free(subnetstr);
528 /* Check if somebody tries to add a subnet of ourself */
530 if(!strcmp(name, myself->name))
532 syslog(LOG_ERR, _("Warning: got DEL_SUBNET from %s (%s) for ourself, restarting"),
533 cl->name, cl->hostname);
539 /* Check if the owner of the new subnet is in the connection list */
541 if(!(owner = lookup_id(name))
543 syslog(LOG_ERR, _("Got DEL_SUBNET for %s from %s (%s) which is not in our connection list"),
544 name, cl->name, cl->hostname);
549 /* If everything is correct, add the subnet to the list of the owner */
551 return subnet_del(owner, subnet);
554 /* New and closed connections notification */
556 int send_add_host(conn_list_t *cl, conn_list_t *other)
559 return send_request(cl, "%d %s %s %lx:%d %lx", ADD_HOST,
560 myself->name, other->name, other->real_ip, other->port, other->options);
563 int add_host_h(conn_list_t *cl)
566 conn_list_t *old, *new, *hisuplink;
568 new = new_conn_list();
570 if(sscanf(cl->buffer, "%*d %as %as %lx:%d %lx", &sender, &new->name, &new->address, &new->port, &new->options) != 5)
572 syslog(LOG_ERR, _("Got bad ADD_HOST from %s (%s)"), cl->name, cl->hostname);
576 /* Check if identity is a valid name */
578 if(!check_id(new->name) || !check_id(sender))
580 syslog(LOG_ERR, _("Got bad ADD_HOST from %s (%s): invalid identity name"), cl->name, cl->hostname);
585 /* Check if somebody tries to add ourself */
587 if(!strcmp(new->name, myself->name))
589 syslog(LOG_ERR, _("Warning: got ADD_HOST from %s (%s) for ourself, restarting"), cl->name, cl->hostname);
595 /* We got an ADD_HOST from ourself!? */
597 if(!strcmp(sender, myself->name))
599 syslog(LOG_ERR, _("Warning: got ADD_HOST from %s (%s) from ourself, restarting"), cl->name, cl->hostname);
605 /* Lookup his uplink */
607 if(!(new->hisuplink = lookup_id(sender))
609 syslog(LOG_ERR, _("Got ADD_HOST from %s (%s) with origin %s which is not in our connection list"),
610 sender, cl->name, cl->hostname);
617 /* Fill in more of the new conn_list structure */
619 new->hostname = hostlookup(htonl(new->real_ip));
621 /* Check if the new host already exists in the connnection list */
623 if((old = lookup_id(new->name)))
625 if((new->real_ip == old->real_ip) && (new->port == old->port))
627 if(debug_lvl > DEBUG_CONNECTIONS)
628 syslog(LOG_NOTICE, _("Got duplicate ADD_HOST for %s (%s) from %s (%s)"),
629 old->name, old->hostname, new->name, new->hostname);
634 if(debug_lvl > DEBUG_CONNECTIONS)
635 syslog(LOG_NOTICE, _("Removing old entry for %s (%s)"),
636 old->name, old->hostname);
637 old->status.active = 0;
638 terminate_connection(old);
642 /* Fill in rest of conn_list structure */
645 new->status.active = 1;
647 /* Hook it up into the conn_list */
649 conn_list_add(conn_list, new);
651 /* Tell the rest about the new host */
653 notify_others(new, cl, send_add_host);
659 int send_del_host(conn_list_t *cl, conn_list_t *other)
662 return send_request(cl, "%d %s %s %lx:%d %lx", DEL_HOST,
663 myself->name, other->name, other->real_ip, other->port, other->options);
666 int del_host_h(conn_list_t *cl)
673 conn_list_t *old, *hisuplink;
676 if(sscanf(cl->buffer, "%*d %as %as %lx:%d %lx", &sender, &name, &address, &port, &options) != 5)
678 syslog(LOG_ERR, _("Got bad DEL_HOST from %s (%s)"),
679 cl->name, cl->hostname);
683 /* Check if identity is a valid name */
685 if(!check_id(name) || !check_id(sender))
687 syslog(LOG_ERR, _("Got bad DEL_HOST from %s (%s): invalid identity name"), cl->name, cl->hostname);
688 free(name); free(sender);
692 /* Check if somebody tries to delete ourself */
694 if(!strcmp(name, myself->name))
696 syslog(LOG_ERR, _("Warning: got DEL_HOST from %s (%s) for ourself, restarting"),
697 cl->name, cl->hostname);
698 free(name); free(sender);
703 /* We got an ADD_HOST from ourself!? */
705 if(!strcmp(sender, myself->name))
707 syslog(LOG_ERR, _("Warning: got DEL_HOST from %s (%s) from ourself, restarting"), cl->name, cl->hostname);
709 free(name); free(sender);
713 /* Lookup his uplink */
715 if(!(hisuplink = lookup_id(sender))
717 syslog(LOG_ERR, _("Got DEL_HOST from %s (%s) with origin %s which is not in our connection list"),
718 cl->name, cl->hostname, sender);
719 free(name); free(sender);
725 /* Check if the new host already exists in the connnection list */
727 if(!(old = lookup_id(name)))
729 syslog(LOG_ERR, _("Got DEL_HOST from %s (%s) for %s which is not in our connection list"),
730 name, cl->name, cl->hostname);
735 /* Check if the rest matches */
737 if(address!=old->address || port!=old->port || options!=old->options || hisuplink!=old->hisuplink || cl!=old->myuplink)
739 syslog(LOG_WARNING, _("Got DEL_HOST from %s (%s) for %s which doesn't match"), cl->name, cl->hostname, old->name);
743 /* Ok, since EVERYTHING seems to check out all right, delete it */
745 old->status.termreq = 1;
746 old->status.active = 0;
748 terminate_connection(old);
753 /* Status and error notification routines */
755 int send_status(conn_list_t *cl, int statusno, char *statusstring)
759 statusstring = status_text[statusno];
761 return send_request(cl, "%d %d %s", STATUS, statusno, statusstring);
764 int status_h(conn_list_t *cl)
769 if(sscanf(cl->buffer, "%*d %d %as", &statusno, &statusstring) != 2)
771 syslog(LOG_ERR, _("Got bad STATUS from %s (%s)"),
772 cl->name, cl->hostname);
776 if(debug_lvl > DEBUG_STATUS)
778 syslog(LOG_NOTICE, _("Status message from %s (%s): %s: %s"),
779 cl->name, cl->hostname, status_text[statusno], statusstring);
787 int send_error(conn_list_t *cl, int errno, char *errstring)
791 errstring = strerror(errno);
792 return send_request(cl, "%d %d %s", ERROR, errno, errstring);
795 int error_h(conn_list_t *cl)
800 if(sscanf(cl->buffer, "%*d %d %as", &errno, &errorstring) != 2)
802 syslog(LOG_ERR, _("Got bad error from %s (%s)"),
803 cl->name, cl->hostname);
807 if(debug_lvl > DEBUG_error)
809 syslog(LOG_NOTICE, _("Error message from %s (%s): %s: %s"),
810 cl->name, cl->hostname, strerror(errno), errorstring);
814 cl->status.termreq = 1;
815 terminate_connection(cl);
820 int send_termreq(conn_list_t *cl)
823 return send_request(cl, "%d", TERMREQ);
826 int termreq_h(conn_list_t *cl)
829 cl->status.termreq = 1;
830 terminate_connection(cl);
835 /* Keepalive routines - FIXME: needs a closer look */
837 int send_ping(conn_list_t *cl)
839 cl->status.pinged = 1;
841 return send_request(cl, "%d", PING);
844 int ping_h(conn_list_t *cl)
847 return send_pong(cl);
850 int send_pong(conn_list_t *cl)
853 return send_request(cl, "%d", PONG);
856 int pong_h(conn_list_t *cl)
859 cl->status.got_pong = 1;
866 int send_key_changed(conn_list_t *from, conn_list_t *cl)
870 for(p = conn_list; p != NULL; p = p->next)
872 if(p!=cl && p->status.meta && p->status.active)
873 send_request(p, "%d %s", KEY_CHANGED,
880 int key_changed_h(conn_list_t *cl)
885 if(sscanf(cl->buffer, "%*d %as", &from_id) != 1)
887 syslog(LOG_ERR, _("Got bad KEY_CHANGED from %s (%s)"),
888 cl->name, cl->hostname);
892 if(!(from = lookup_id(from_id)))
894 syslog(LOG_ERR, _("Got KEY_CHANGED from %s (%s) origin %s which does not exist in our connection list"),
895 cl->name, cl->hostname, from_id);
902 from->status.validkey = 0;
903 from->status.waitingforkey = 0;
905 send_key_changed(from, cl);
910 int send_req_key(conn_list_t *from, conn_list_t *to)
913 return send_request(to->nexthop, "%d %s %s", REQ_KEY,
914 from->name, to->name);
917 int req_key_h(conn_list_t *cl)
919 char *from_id, *to_id;
920 conn_list_t *from, *to;
922 if(sscanf(cl->buffer, "%*d %as %as", &from_id, &to_id) != 2)
924 syslog(LOG_ERR, _("Got bad REQ_KEY from %s (%s)"),
925 cl->name, cl->hostname);
929 if(!(from = lookup_id(from_id)))
931 syslog(LOG_ERR, _("Got REQ_KEY from %s (%s) origin %s which does not exist in our connection list"),
932 cl->name, cl->hostname, from_id);
933 free(from_id); free(to_id);
937 /* Check if this key request is for us */
939 if(!strcmp(to_id, myself->name))
941 send_ans_key(myself, from, myself->datakey->key);
945 if(!(to = lookup_id(to_id)))
947 syslog(LOG_ERR, _("Got REQ_KEY from %s (%s) destination %s which does not exist in our connection list"),
948 cl->name, cl->hostname, to_id);
949 free(from_id); free(to_id);
952 send_req_key(from, to);
955 free(from_id); free(to_id);
960 int send_ans_key(conn_list_t *from, conn_list_t *to, char *datakey)
963 return send_request(to->nexthop, "%d %s %s %s", ANS_KEY,
964 from->name, to->name, datakey);
967 int ans_key_h(conn_list_t *cl)
969 char *from_id, *to_id, *datakey;
971 conn_list_t *from, *to;
973 if(sscanf(cl->buffer, "%*d %as %as %as", &from_id, &to_id, &datakey) != 3)
975 syslog(LOG_ERR, _("Got bad ANS_KEY from %s (%s)"),
976 cl->name, cl->hostname);
980 if(!(from = lookup_id(from_id)))
982 syslog(LOG_ERR, _("Got ANS_KEY from %s (%s) origin %s which does not exist in our connection list"),
983 cl->name, cl->hostname, from_id);
984 free(from_id); free(to_id); free(datakey);
988 /* Check if this key request is for us */
990 if(!strcmp(to_id, myself->name))
992 /* It is for us, convert it to binary and set the key with it. */
994 keylength = strlen(datakey);
996 if((keylength%2) || (keylength <= 0))
998 syslog(LOG_ERR, _("Got bad ANS_KEY from %s (%s) origin %s: invalid key"),
999 cl->name, cl->hostname, from->name);
1000 free(from_id); free(to_id); free(datakey);
1004 hex2bin(datakey, datakey, keylength);
1005 BF_set_key(cl->datakey, keylength, datakey);
1009 if(!(to = lookup_id(to_id)))
1011 syslog(LOG_ERR, _("Got ANS_KEY from %s (%s) destination %s which does not exist in our connection list"),
1012 cl->name, cl->hostname, to_id);
1013 free(from_id); free(to_id); free(datakey);
1016 send_ans_key(from, to, datakey);
1019 free(from_id); free(to_id); free(datakey);
1024 /* Jumptable for the request handlers */
1026 int (*request_handlers[])(conn_list_t*) = {
1027 id_h, challenge_h, chal_reply_h, ack_h,
1028 status_h, error_h, termreq_h,
1030 add_host_h, del_host_h,
1031 add_subnet_h, del_subnet_h,
1032 key_changed_h, req_key_h, ans_key_h,
1037 char (*request_name[]) = {
1038 "ID", "CHALLENGE", "CHAL_REPLY", "ACK",
1039 "STATUS", "ERROR", "TERMREQ",
1041 "ADD_HOST", "DEL_HOST",
1042 "ADD_SUBNET", "DEL_SUBNET",
1043 "KEY_CHANGED", "REQ_KEY", "ANS_KEY",