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.39 2000/10/14 17:04:15 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 /* FIXME: reprogram this.
403 notify_others(cl, NULL, send_add_host);
409 if(cl->status.outgoing)
415 /* Address and subnet information exchange */
417 int send_add_subnet(conn_list_t *cl, conn_list_t *other, subnet_t *subnet)
422 x = send_request(cl, "%d %s %s", ADD_SUBNET,
423 other->name, netstr = net2str(subnet));
429 int add_subnet_h(conn_list_t *cl)
434 subnet_t *subnet, *old;
436 if(sscanf(cl->buffer, "%*d %as %as", &name, &subnetstr) != 3)
438 syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s)"), cl->name, cl->hostname);
439 free(name); free(subnetstr);
443 /* Check if owner name is a valid */
447 syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s): invalid identity name"), cl->name, cl->hostname);
448 free(name); free(subnetstr);
452 /* Check if subnet string is valid */
454 if(!(subnet = str2net(subnetstr)))
456 syslog(LOG_ERR, _("Got bad ADD_SUBNET from %s (%s): invalid subnet string"), cl->name, cl->hostname);
457 free(name); free(subnetstr);
463 /* Check if somebody tries to add a subnet of ourself */
465 if(!strcmp(name, myself->name))
467 syslog(LOG_ERR, _("Warning: got ADD_SUBNET from %s (%s) for ourself, restarting"),
468 cl->name, cl->hostname);
474 /* Check if the owner of the new subnet is in the connection list */
476 if(!(owner = lookup_id(name)))
478 syslog(LOG_ERR, _("Got ADD_SUBNET for %s from %s (%s) which is not in our connection list"),
479 name, cl->name, cl->hostname);
484 /* If everything is correct, add the subnet to the list of the owner */
486 subnet_add(owner, subnet);
491 int send_del_subnet(conn_list_t *cl, conn_list_t *other, subnet_t *subnet)
494 return send_request(cl, "%d %s %s", DEL_SUBNET, other->name, net2str(subnet));
497 int del_subnet_h(conn_list_t *cl)
502 subnet_t *subnet, *old;
504 if(sscanf(cl->buffer, "%*d %as %as", &name, &subnetstr) != 3)
506 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s)"), cl->name, cl->hostname);
507 free(name); free(subnetstr);
511 /* Check if owner name is a valid */
515 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s): invalid identity name"), cl->name, cl->hostname);
516 free(name); free(subnetstr);
520 /* Check if subnet string is valid */
522 if(!(subnet = str2net(subnetstr)))
524 syslog(LOG_ERR, _("Got bad DEL_SUBNET from %s (%s): invalid subnet string"), cl->name, cl->hostname);
525 free(name); free(subnetstr);
531 /* Check if somebody tries to add a subnet of ourself */
533 if(!strcmp(name, myself->name))
535 syslog(LOG_ERR, _("Warning: got DEL_SUBNET from %s (%s) for ourself, restarting"),
536 cl->name, cl->hostname);
542 /* Check if the owner of the new subnet is in the connection list */
544 if(!(owner = lookup_id(name)))
546 syslog(LOG_ERR, _("Got DEL_SUBNET for %s from %s (%s) which is not in our connection list"),
547 name, cl->name, cl->hostname);
552 /* If everything is correct, delete the subnet from the list of the owner */
559 /* New and closed connections notification */
561 int send_add_host(conn_list_t *cl, conn_list_t *other)
564 return send_request(cl, "%d %s %s %lx:%d %lx", ADD_HOST,
565 myself->name, other->name, other->address, other->port, other->options);
568 int add_host_h(conn_list_t *cl)
571 conn_list_t *old, *new, *hisuplink;
573 new = new_conn_list();
575 if(sscanf(cl->buffer, "%*d %as %as %lx:%d %lx", &sender, &new->name, &new->address, &new->port, &new->options) != 5)
577 syslog(LOG_ERR, _("Got bad ADD_HOST from %s (%s)"), cl->name, cl->hostname);
581 /* Check if identity is a valid name */
583 if(!check_id(new->name) || !check_id(sender))
585 syslog(LOG_ERR, _("Got bad ADD_HOST from %s (%s): invalid identity name"), cl->name, cl->hostname);
590 /* Check if somebody tries to add ourself */
592 if(!strcmp(new->name, myself->name))
594 syslog(LOG_ERR, _("Warning: got ADD_HOST from %s (%s) for ourself, restarting"), cl->name, cl->hostname);
600 /* We got an ADD_HOST from ourself!? */
602 if(!strcmp(sender, myself->name))
604 syslog(LOG_ERR, _("Warning: got ADD_HOST from %s (%s) from ourself, restarting"), cl->name, cl->hostname);
610 /* Lookup his uplink */
612 if(!(new->hisuplink = lookup_id(sender)))
614 syslog(LOG_ERR, _("Got ADD_HOST from %s (%s) with origin %s which is not in our connection list"),
615 sender, cl->name, cl->hostname);
622 /* Fill in more of the new conn_list structure */
624 new->hostname = hostlookup(htonl(new->address));
626 /* Check if the new host already exists in the connnection list */
628 if((old = lookup_id(new->name)))
630 if((new->address == old->address) && (new->port == old->port))
632 if(debug_lvl > DEBUG_CONNECTIONS)
633 syslog(LOG_NOTICE, _("Got duplicate ADD_HOST for %s (%s) from %s (%s)"),
634 old->name, old->hostname, new->name, new->hostname);
639 if(debug_lvl > DEBUG_CONNECTIONS)
640 syslog(LOG_NOTICE, _("Removing old entry for %s (%s)"),
641 old->name, old->hostname);
642 old->status.active = 0;
643 terminate_connection(old);
647 /* Fill in rest of conn_list structure */
650 new->status.active = 1;
652 /* Hook it up into the conn_list */
654 conn_list_add(conn_list, new);
656 /* Tell the rest about the new host */
657 /* FIXME: reprogram this.
658 notify_others(new, cl, send_add_host);
664 int send_del_host(conn_list_t *cl, conn_list_t *other)
667 return send_request(cl, "%d %s %s %lx:%d %lx", DEL_HOST,
668 myself->name, other->name, other->address, other->port, other->options);
671 int del_host_h(conn_list_t *cl)
678 conn_list_t *old, *hisuplink;
681 if(sscanf(cl->buffer, "%*d %as %as %lx:%d %lx", &sender, &name, &address, &port, &options) != 5)
683 syslog(LOG_ERR, _("Got bad DEL_HOST from %s (%s)"),
684 cl->name, cl->hostname);
688 /* Check if identity is a valid name */
690 if(!check_id(name) || !check_id(sender))
692 syslog(LOG_ERR, _("Got bad DEL_HOST from %s (%s): invalid identity name"), cl->name, cl->hostname);
693 free(name); free(sender);
697 /* Check if somebody tries to delete ourself */
699 if(!strcmp(name, myself->name))
701 syslog(LOG_ERR, _("Warning: got DEL_HOST from %s (%s) for ourself, restarting"),
702 cl->name, cl->hostname);
703 free(name); free(sender);
708 /* We got an ADD_HOST from ourself!? */
710 if(!strcmp(sender, myself->name))
712 syslog(LOG_ERR, _("Warning: got DEL_HOST from %s (%s) from ourself, restarting"), cl->name, cl->hostname);
714 free(name); free(sender);
718 /* Lookup his uplink */
720 if(!(hisuplink = lookup_id(sender)))
722 syslog(LOG_ERR, _("Got DEL_HOST from %s (%s) with origin %s which is not in our connection list"),
723 cl->name, cl->hostname, sender);
724 free(name); free(sender);
730 /* Check if the new host already exists in the connnection list */
732 if(!(old = lookup_id(name)))
734 syslog(LOG_ERR, _("Got DEL_HOST from %s (%s) for %s which is not in our connection list"),
735 name, cl->name, cl->hostname);
740 /* Check if the rest matches */
742 if(address!=old->address || port!=old->port || options!=old->options || hisuplink!=old->hisuplink || cl!=old->myuplink)
744 syslog(LOG_WARNING, _("Got DEL_HOST from %s (%s) for %s which doesn't match"), cl->name, cl->hostname, old->name);
748 /* Ok, since EVERYTHING seems to check out all right, delete it */
750 old->status.termreq = 1;
751 old->status.active = 0;
753 terminate_connection(old);
758 /* Status and error notification routines */
760 int send_status(conn_list_t *cl, int statusno, char *statusstring)
764 statusstring = status_text[statusno];
766 return send_request(cl, "%d %d %s", STATUS, statusno, statusstring);
769 int status_h(conn_list_t *cl)
774 if(sscanf(cl->buffer, "%*d %d %as", &statusno, &statusstring) != 2)
776 syslog(LOG_ERR, _("Got bad STATUS from %s (%s)"),
777 cl->name, cl->hostname);
781 if(debug_lvl > DEBUG_STATUS)
783 syslog(LOG_NOTICE, _("Status message from %s (%s): %s: %s"),
784 cl->name, cl->hostname, status_text[statusno], statusstring);
792 int send_error(conn_list_t *cl, int errno, char *errstring)
796 errstring = strerror(errno);
797 return send_request(cl, "%d %d %s", ERROR, errno, errstring);
800 int error_h(conn_list_t *cl)
805 if(sscanf(cl->buffer, "%*d %d %as", &errno, &errorstring) != 2)
807 syslog(LOG_ERR, _("Got bad error from %s (%s)"),
808 cl->name, cl->hostname);
812 if(debug_lvl > DEBUG_error)
814 syslog(LOG_NOTICE, _("Error message from %s (%s): %s: %s"),
815 cl->name, cl->hostname, strerror(errno), errorstring);
819 cl->status.termreq = 1;
820 terminate_connection(cl);
825 int send_termreq(conn_list_t *cl)
828 return send_request(cl, "%d", TERMREQ);
831 int termreq_h(conn_list_t *cl)
834 cl->status.termreq = 1;
835 terminate_connection(cl);
840 /* Keepalive routines - FIXME: needs a closer look */
842 int send_ping(conn_list_t *cl)
844 cl->status.pinged = 1;
846 return send_request(cl, "%d", PING);
849 int ping_h(conn_list_t *cl)
852 return send_pong(cl);
855 int send_pong(conn_list_t *cl)
858 return send_request(cl, "%d", PONG);
861 int pong_h(conn_list_t *cl)
864 cl->status.got_pong = 1;
871 int send_key_changed(conn_list_t *from, conn_list_t *cl)
875 for(p = conn_list; p != NULL; p = p->next)
877 if(p!=cl && p->status.meta && p->status.active)
878 send_request(p, "%d %s", KEY_CHANGED,
885 int key_changed_h(conn_list_t *cl)
890 if(sscanf(cl->buffer, "%*d %as", &from_id) != 1)
892 syslog(LOG_ERR, _("Got bad KEY_CHANGED from %s (%s)"),
893 cl->name, cl->hostname);
897 if(!(from = lookup_id(from_id)))
899 syslog(LOG_ERR, _("Got KEY_CHANGED from %s (%s) origin %s which does not exist in our connection list"),
900 cl->name, cl->hostname, from_id);
907 from->status.validkey = 0;
908 from->status.waitingforkey = 0;
910 send_key_changed(from, cl);
915 int send_req_key(conn_list_t *from, conn_list_t *to)
918 return send_request(to->nexthop, "%d %s %s", REQ_KEY,
919 from->name, to->name);
922 int req_key_h(conn_list_t *cl)
924 char *from_id, *to_id;
925 conn_list_t *from, *to;
927 if(sscanf(cl->buffer, "%*d %as %as", &from_id, &to_id) != 2)
929 syslog(LOG_ERR, _("Got bad REQ_KEY from %s (%s)"),
930 cl->name, cl->hostname);
934 if(!(from = lookup_id(from_id)))
936 syslog(LOG_ERR, _("Got REQ_KEY from %s (%s) origin %s which does not exist in our connection list"),
937 cl->name, cl->hostname, from_id);
938 free(from_id); free(to_id);
942 /* Check if this key request is for us */
944 if(!strcmp(to_id, myself->name))
946 send_ans_key(myself, from, myself->cipher_pktkey);
950 if(!(to = lookup_id(to_id)))
952 syslog(LOG_ERR, _("Got REQ_KEY from %s (%s) destination %s which does not exist in our connection list"),
953 cl->name, cl->hostname, to_id);
954 free(from_id); free(to_id);
957 send_req_key(from, to);
960 free(from_id); free(to_id);
965 int send_ans_key(conn_list_t *from, conn_list_t *to, char *pktkey)
968 return send_request(to->nexthop, "%d %s %s %s", ANS_KEY,
969 from->name, to->name, pktkey);
972 int ans_key_h(conn_list_t *cl)
974 char *from_id, *to_id, *pktkey;
976 conn_list_t *from, *to;
978 if(sscanf(cl->buffer, "%*d %as %as %as", &from_id, &to_id, &pktkey) != 3)
980 syslog(LOG_ERR, _("Got bad ANS_KEY from %s (%s)"),
981 cl->name, cl->hostname);
985 if(!(from = lookup_id(from_id)))
987 syslog(LOG_ERR, _("Got ANS_KEY from %s (%s) origin %s which does not exist in our connection list"),
988 cl->name, cl->hostname, from_id);
989 free(from_id); free(to_id); free(pktkey);
993 /* Check if this key request is for us */
995 if(!strcmp(to_id, myself->name))
997 /* It is for us, convert it to binary and set the key with it. */
999 keylength = strlen(pktkey);
1001 if((keylength%2) || (keylength <= 0))
1003 syslog(LOG_ERR, _("Got bad ANS_KEY from %s (%s) origin %s: invalid key"),
1004 cl->name, cl->hostname, from->name);
1005 free(from_id); free(to_id); free(pktkey);
1009 hex2bin(pktkey, pktkey, keylength);
1010 BF_set_key(cl->cipher_pktkey, keylength, pktkey);
1014 if(!(to = lookup_id(to_id)))
1016 syslog(LOG_ERR, _("Got ANS_KEY from %s (%s) destination %s which does not exist in our connection list"),
1017 cl->name, cl->hostname, to_id);
1018 free(from_id); free(to_id); free(pktkey);
1021 send_ans_key(from, to, pktkey);
1024 free(from_id); free(to_id); free(pktkey);
1029 /* Jumptable for the request handlers */
1031 int (*request_handlers[])(conn_list_t*) = {
1032 id_h, challenge_h, chal_reply_h, ack_h,
1033 status_h, error_h, termreq_h,
1035 add_host_h, del_host_h,
1036 add_subnet_h, del_subnet_h,
1037 key_changed_h, req_key_h, ans_key_h,
1042 char (*request_name[]) = {
1043 "ID", "CHALLENGE", "CHAL_REPLY", "ACK",
1044 "STATUS", "ERROR", "TERMREQ",
1046 "ADD_HOST", "DEL_HOST",
1047 "ADD_SUBNET", "DEL_SUBNET",
1048 "KEY_CHANGED", "REQ_KEY", "ANS_KEY",
1051 /* Status strings */
1053 char (*status_text[]) = {
1054 "FIXME: status text",
1059 char (*error_text[]) = {
1060 "FIXME: error text",