Improve recently seen address cache
[tinc] / src / invitation.c
1 /*
2     invitation.c -- Create and accept invitations
3     Copyright (C) 2013-2022 Guus Sliepen <guus@tinc-vpn.org>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "system.h"
21
22 #include "control_common.h"
23 #include "crypto.h"
24 #include "ecdsa.h"
25 #include "ecdsagen.h"
26 #include "ifconfig.h"
27 #include "invitation.h"
28 #include "names.h"
29 #include "netutl.h"
30 #include "rsagen.h"
31 #include "script.h"
32 #include "sptps.h"
33 #include "subnet.h"
34 #include "tincctl.h"
35 #include "utils.h"
36 #include "xalloc.h"
37 #include "random.h"
38 #include "pidfile.h"
39
40 #include "ed25519/sha512.h"
41
42 int addressfamily = AF_UNSPEC;
43
44 static void scan_for_hostname(const char *filename, char **hostname, char **port) {
45         if(!filename || (*hostname && *port)) {
46                 return;
47         }
48
49         FILE *f = fopen(filename, "r");
50
51         if(!f) {
52                 return;
53         }
54
55         while(fgets(line, sizeof(line), f)) {
56                 if(!rstrip(line)) {
57                         continue;
58                 }
59
60                 char *p = line, *q;
61                 p += strcspn(p, "\t =");
62
63                 if(!*p) {
64                         continue;
65                 }
66
67                 q = p + strspn(p, "\t ");
68
69                 if(*q == '=') {
70                         q += 1 + strspn(q + 1, "\t ");
71                 }
72
73                 *p = 0;
74                 p = q + strcspn(q, "\t ");
75
76                 if(*p) {
77                         *p++ = 0;
78                 }
79
80                 p += strspn(p, "\t ");
81                 p[strcspn(p, "\t ")] = 0;
82
83                 if(!*port && !strcasecmp(line, "Port")) {
84                         free(*port);
85                         *port = xstrdup(q);
86                 } else if(!*hostname && !strcasecmp(line, "Address")) {
87                         free(*hostname);
88                         *hostname = xstrdup(q);
89
90                         if(*p) {
91                                 free(*port);
92                                 *port = xstrdup(p);
93                         }
94                 }
95
96                 if(*hostname && *port) {
97                         break;
98                 }
99         }
100
101         fclose(f);
102 }
103
104 static bool get_my_hostname(char **out_address, char **out_port) {
105         char *hostname = NULL;
106         char *port = NULL;
107         char *hostport = NULL;
108         char *name = get_my_name(false);
109         char filename[PATH_MAX] = {0};
110
111         // Use first Address statement in own host config file
112         if(check_id(name)) {
113                 snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, name);
114                 scan_for_hostname(filename, &hostname, &port);
115                 scan_for_hostname(tinc_conf, &hostname, &port);
116         }
117
118         free(name);
119         name = NULL;
120
121         if(!port || (is_decimal(port) && atoi(port) == 0)) {
122                 pidfile_t *pidfile = read_pidfile();
123
124                 if(pidfile) {
125                         free(port);
126                         port = xstrdup(pidfile->port);
127                         free(pidfile);
128                 } else {
129                         fprintf(stderr, "tincd is using a dynamic port and is not running. Please start tincd or set the Port option to a non-zero value.\n");
130                         goto exit;
131                 }
132         }
133
134         if(hostname) {
135                 goto done;
136         }
137
138         // If that doesn't work, guess externally visible hostname
139         fprintf(stderr, "Trying to discover externally visible hostname...\n");
140         struct addrinfo *ai = str2addrinfo("tinc-vpn.org", "80", SOCK_STREAM);
141         struct addrinfo *aip = ai;
142         static const char request[] = "GET http://tinc-vpn.org/host.cgi HTTP/1.0\r\n\r\n";
143
144         while(aip) {
145                 int s = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol);
146
147                 if(s >= 0) {
148                         if(connect(s, aip->ai_addr, aip->ai_addrlen)) {
149                                 closesocket(s);
150                                 s = -1;
151                         }
152                 }
153
154                 if(s >= 0) {
155                         send(s, request, sizeof(request) - 1, 0);
156                         ssize_t len = recv(s, line, sizeof(line) - 1, MSG_WAITALL);
157
158                         if(len > 0) {
159                                 line[len] = 0;
160
161                                 if(line[len - 1] == '\n') {
162                                         line[--len] = 0;
163                                 }
164
165                                 char *p = strrchr(line, '\n');
166
167                                 if(p && p[1]) {
168                                         hostname = xstrdup(p + 1);
169                                 }
170                         }
171
172                         closesocket(s);
173
174                         if(hostname) {
175                                 break;
176                         }
177                 }
178
179                 aip = aip->ai_next;
180                 continue;
181         }
182
183         if(ai) {
184                 freeaddrinfo(ai);
185         }
186
187         // Check that the hostname is reasonable
188         if(hostname) {
189                 for(char *p = hostname; *p; p++) {
190                         if(isalnum((uint8_t) *p) || *p == '-' || *p == '.' || *p == ':') {
191                                 continue;
192                         }
193
194                         // If not, forget it.
195                         free(hostname);
196                         hostname = NULL;
197                         break;
198                 }
199         }
200
201         if(!tty) {
202                 if(!hostname) {
203                         fprintf(stderr, "Could not determine the external address or hostname. Please set Address manually.\n");
204                         free(port);
205                         return false;
206                 }
207
208                 goto save;
209         }
210
211 again:
212         fprintf(stderr, "Please enter your host's external address or hostname");
213
214         if(hostname) {
215                 fprintf(stderr, " [%s]", hostname);
216         }
217
218         fprintf(stderr, ": ");
219
220         if(!fgets(line, sizeof(line), stdin)) {
221                 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
222                 free(hostname);
223                 free(port);
224                 return false;
225         }
226
227         if(!rstrip(line)) {
228                 if(hostname) {
229                         goto save;
230                 } else {
231                         goto again;
232                 }
233         }
234
235         for(char *p = line; *p; p++) {
236                 if(isalnum((uint8_t) *p) || *p == '-' || *p == '.') {
237                         continue;
238                 }
239
240                 fprintf(stderr, "Invalid address or hostname.\n");
241                 goto again;
242         }
243
244         free(hostname);
245         hostname = xstrdup(line);
246
247 save:
248
249         if(*filename) {
250                 FILE *f = fopen(filename, "a");
251
252                 if(f) {
253                         fprintf(f, "\nAddress = %s\n", hostname);
254                         fclose(f);
255                 } else {
256                         fprintf(stderr, "Could not append Address to %s: %s\n", filename, strerror(errno));
257                 }
258         }
259
260 done:
261
262         if(port) {
263                 if(strchr(hostname, ':')) {
264                         xasprintf(&hostport, "[%s]:%s", hostname, port);
265                 } else {
266                         xasprintf(&hostport, "%s:%s", hostname, port);
267                 }
268         } else {
269                 if(strchr(hostname, ':')) {
270                         xasprintf(&hostport, "[%s]", hostname);
271                 } else {
272                         hostport = xstrdup(hostname);
273                 }
274         }
275
276 exit:
277         free(hostname);
278
279         if(hostport && port) {
280                 *out_address = hostport;
281                 *out_port = port;
282                 return true;
283         }
284
285         free(hostport);
286         free(port);
287         return false;
288 }
289
290 // Copy host configuration file, replacing Port with the value passed here. Host
291 // configs may contain this clause: `Port = 0`, which means 'ask the operating
292 // system to allocate any available port'. This obviously won't do for invitation
293 // files, so replace it with an actual port we've obtained previously.
294 static bool copy_config_replacing_port(FILE *out, const char *filename, const char *port) {
295         FILE *in = fopen(filename, "r");
296
297         if(!in) {
298                 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
299                 return false;
300         }
301
302         char line[1024];
303
304         while(fgets(line, sizeof(line), in)) {
305                 const char *var_beg = line + strspn(line, "\t ");
306                 const char *var_end = var_beg + strcspn(var_beg, "\t ");
307
308                 // Check the name of the variable we've read. If it's Port, replace it with
309                 // a port we'll use in invitation URL. Otherwise, just copy the line.
310                 if(var_end > var_beg && !strncasecmp(var_beg, "Port", var_end - var_beg)) {
311                         fprintf(out, "Port = %s\n", port);
312                 } else {
313                         fprintf(out, "%s", line);
314                 }
315         }
316
317         memzero(line, sizeof(line));
318         fclose(in);
319         return true;
320 }
321
322 static bool append_host_config(FILE *f, const char *nodename, const char *port) {
323         char path[PATH_MAX];
324         snprintf(path, sizeof(path), "%s" SLASH "hosts" SLASH "%s", confbase, nodename);
325         bool success = copy_config_replacing_port(f, path, port);
326         fclose(f);
327         return success;
328 }
329
330 int cmd_invite(int argc, char *argv[]) {
331         if(argc < 2) {
332                 fprintf(stderr, "Not enough arguments!\n");
333                 return 1;
334         }
335
336         if(argc > 2) {
337                 fprintf(stderr, "Too many arguments!\n");
338                 return 1;
339         }
340
341         // Check validity of the new node's name
342         if(!check_id(argv[1])) {
343                 fprintf(stderr, "Invalid name for node.\n");
344                 return 1;
345         }
346
347         free(myname);
348         myname = get_my_name(true);
349
350         if(!myname) {
351                 return 1;
352         }
353
354         // Ensure no host configuration file with that name exists
355         char filename[PATH_MAX];
356         snprintf(filename, sizeof(filename), "%s" SLASH "hosts" SLASH "%s", confbase, argv[1]);
357
358         if(!access(filename, F_OK)) {
359                 fprintf(stderr, "A host config file for %s already exists!\n", argv[1]);
360                 return 1;
361         }
362
363         // If a daemon is running, ensure no other nodes know about this name
364         if(connect_tincd(false)) {
365                 bool found = false;
366                 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
367
368                 while(recvline(fd, line, sizeof(line))) {
369                         char node[4096];
370                         int code, req;
371
372                         if(sscanf(line, "%d %d %4095s", &code, &req, node) != 3) {
373                                 break;
374                         }
375
376                         if(!strcmp(node, argv[1])) {
377                                 found = true;
378                         }
379                 }
380
381                 if(found) {
382                         fprintf(stderr, "A node with name %s is already known!\n", argv[1]);
383                         return 1;
384                 }
385         }
386
387         if(!makedirs(DIR_INVITATIONS)) {
388                 return false;
389         }
390
391         snprintf(filename, sizeof(filename), "%s" SLASH "invitations", confbase);
392
393         // Count the number of valid invitations, clean up old ones
394         DIR *dir = opendir(filename);
395
396         if(!dir) {
397                 fprintf(stderr, "Could not read directory %s: %s\n", filename, strerror(errno));
398                 return 1;
399         }
400
401         errno = 0;
402         int count = 0;
403         struct dirent *ent;
404         time_t deadline = time(NULL) - 604800; // 1 week in the past
405
406         while((ent = readdir(dir))) {
407                 if(strlen(ent->d_name) != 24) {
408                         continue;
409                 }
410
411                 char invname[PATH_MAX];
412                 struct stat st;
413
414                 if((size_t)snprintf(invname, sizeof(invname), "%s" SLASH "%s", filename, ent->d_name) >= sizeof(invname)) {
415                         fprintf(stderr, "Filename too long: %s" SLASH "%s\n", filename, ent->d_name);
416                         continue;
417                 }
418
419                 if(!stat(invname, &st)) {
420                         if(deadline < st.st_mtime) {
421                                 count++;
422                         } else {
423                                 unlink(invname);
424                         }
425                 } else {
426                         fprintf(stderr, "Could not stat %s: %s\n", invname, strerror(errno));
427                         errno = 0;
428                 }
429         }
430
431         closedir(dir);
432
433         if(errno) {
434                 fprintf(stderr, "Error while reading directory %s: %s\n", filename, strerror(errno));
435                 return 1;
436         }
437
438         ecdsa_t *key;
439         snprintf(filename, sizeof(filename), "%s" SLASH "invitations" SLASH "ed25519_key.priv", confbase);
440
441         // Remove the key if there are no outstanding invitations.
442         if(!count) {
443                 unlink(filename);
444         }
445
446         // Create a new key if necessary.
447         FILE *f = fopen(filename, "r");
448
449         if(!f) {
450                 if(errno != ENOENT) {
451                         fprintf(stderr, "Could not read %s: %s\n", filename, strerror(errno));
452                         return 1;
453                 }
454
455                 key = ecdsa_generate();
456
457                 if(!key) {
458                         return 1;
459                 }
460
461                 f = fopen(filename, "w");
462
463                 if(!f) {
464                         fprintf(stderr, "Could not write %s: %s\n", filename, strerror(errno));
465                         ecdsa_free(key);
466                         return 1;
467                 }
468
469                 chmod(filename, 0600);
470
471                 if(!ecdsa_write_pem_private_key(key, f)) {
472                         fprintf(stderr, "Could not write ECDSA private key\n");
473                         fclose(f);
474                         ecdsa_free(key);
475                         return 1;
476                 }
477
478                 fclose(f);
479
480                 if(connect_tincd(true)) {
481                         sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
482                 } else {
483                         fprintf(stderr, "Could not signal the tinc daemon. Please restart or reload it manually.\n");
484                 }
485         } else {
486                 key = ecdsa_read_pem_private_key(f);
487                 fclose(f);
488
489                 if(!key) {
490                         fprintf(stderr, "Could not read private key from %s\n", filename);
491                 }
492         }
493
494         if(!key) {
495                 return 1;
496         }
497
498         // Create a hash of the key.
499         char hash[64];
500         char *fingerprint = ecdsa_get_base64_public_key(key);
501         sha512(fingerprint, strlen(fingerprint), hash);
502         b64encode_tinc_urlsafe(hash, hash, 18);
503
504         ecdsa_free(key);
505
506         // Create a random cookie for this invitation.
507         char cookie[25];
508         randomize(cookie, 18);
509
510         // Create a filename that doesn't reveal the cookie itself
511         const size_t buflen = 18 + strlen(fingerprint);
512         uint8_t *buf = alloca(buflen);
513
514         char cookiehash[64];
515         memcpy(buf, cookie, 18);
516         memcpy(buf + 18, fingerprint, buflen - 18);
517         sha512(buf, buflen, cookiehash);
518         b64encode_tinc_urlsafe(cookiehash, cookiehash, 18);
519
520         free(fingerprint);
521
522         b64encode_tinc_urlsafe(cookie, cookie, 18);
523
524         // Create a file containing the details of the invitation.
525         snprintf(filename, sizeof(filename), "%s" SLASH "invitations" SLASH "%s", confbase, cookiehash);
526         int ifd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600);
527
528         if(!ifd) {
529                 memzero(cookie, sizeof(cookie));
530                 fprintf(stderr, "Could not create invitation file %s: %s\n", filename, strerror(errno));
531                 return 1;
532         }
533
534         f = fdopen(ifd, "w");
535
536         if(!f) {
537                 abort();
538         }
539
540         // Get the local address
541         char *address = NULL;
542         char *port = NULL;
543
544         if(!get_my_hostname(&address, &port)) {
545                 memzero(cookie, sizeof(cookie));
546                 return 1;
547         }
548
549         // Create an URL from the local address, key hash and cookie
550         char *url;
551         xasprintf(&url, "%s/%s%s", address, hash, cookie);
552
553         memzero(cookie, sizeof(cookie));
554         free(address);
555
556         // Fill in the details.
557         fprintf(f, "Name = %s\n", argv[1]);
558
559         if(check_netname(netname, true)) {
560                 fprintf(f, "NetName = %s\n", netname);
561         }
562
563         fprintf(f, "ConnectTo = %s\n", myname);
564
565         // Copy Broadcast and Mode
566         FILE *tc = fopen(tinc_conf, "r");
567
568         if(tc) {
569                 char buf[1024];
570
571                 while(fgets(buf, sizeof(buf), tc)) {
572                         if((!strncasecmp(buf, "Mode", 4) && strchr(" \t=", buf[4]))
573                                         || (!strncasecmp(buf, "Broadcast", 9) && strchr(" \t=", buf[9]))) {
574                                 fputs(buf, f);
575
576                                 // Make sure there is a newline character.
577                                 if(!strchr(buf, '\n')) {
578                                         fputc('\n', f);
579                                 }
580                         }
581                 }
582
583                 fclose(tc);
584         }
585
586         fprintf(f, "#---------------------------------------------------------------#\n");
587         fprintf(f, "Name = %s\n", myname);
588
589         bool appended = append_host_config(f, myname, port);
590         free(port);
591
592         if(!appended) {
593                 fprintf(stderr, "Could not append my config to invitation file: %s.\n", strerror(errno));
594                 free_string(url);
595                 return 1;
596         }
597
598         // Call the inviation-created script
599         environment_t env;
600         environment_init(&env);
601         environment_add(&env, "NODE=%s", argv[1]);
602         environment_add(&env, "INVITATION_FILE=%s", filename);
603         environment_add(&env, "INVITATION_URL=%s", url);
604         execute_script("invitation-created", &env);
605         environment_exit(&env);
606
607         puts(url);
608         free_string(url);
609
610         return 0;
611 }
612
613 static int sock;
614 static char cookie[18], hash[18];
615 static sptps_t sptps;
616 static char *data;
617 static size_t datalen;
618 static bool success = false;
619
620 static char *get_line(char *line, size_t linelen, const char **data) {
621         if(!data || !*data) {
622                 return NULL;
623         }
624
625         if(! **data) {
626                 *data = NULL;
627                 return NULL;
628         }
629
630         const char *end = strchr(*data, '\n');
631         size_t len = end ? (size_t)(end - *data) : strlen(*data);
632
633         if(len >= linelen) {
634                 fprintf(stderr, "Maximum line length exceeded!\n");
635                 return NULL;
636         }
637
638         if(len && !isprint((uint8_t) **data)) {
639                 abort();
640         }
641
642         memcpy(line, *data, len);
643         line[len] = 0;
644
645         if(end) {
646                 *data = end + 1;
647         } else {
648                 *data = NULL;
649         }
650
651         return line;
652 }
653
654 static char *get_value(const char *data, const char *var) {
655         static char buf[1024];
656
657         char *line = get_line(buf, sizeof(buf), &data);
658
659         if(!line) {
660                 return NULL;
661         }
662
663         char *sep = line + strcspn(line, " \t=");
664         char *val = sep + strspn(sep, " \t");
665
666         if(*val == '=') {
667                 val += 1 + strspn(val + 1, " \t");
668         }
669
670         *sep = 0;
671
672         if(strcasecmp(line, var)) {
673                 return NULL;
674         }
675
676         return val;
677 }
678
679 static char *grep(const char *data, const char *var) {
680         char value[1024];
681
682         const char *p = data;
683         size_t varlen = strlen(var);
684
685         // Skip all lines not starting with var
686         while(strncasecmp(p, var, varlen) || !strchr(" \t=", p[varlen])) {
687                 p = strchr(p, '\n');
688
689                 if(!p) {
690                         break;
691                 } else {
692                         p++;
693                 }
694         }
695
696         if(!p) {
697                 return NULL;
698         }
699
700         p += varlen;
701         p += strspn(p, " \t");
702
703         if(*p == '=') {
704                 p += 1 + strspn(p + 1, " \t");
705         }
706
707         const char *e = strchr(p, '\n');
708
709         if(!e) {
710                 return xstrdup(p);
711         }
712
713         if((size_t)(e - p) >= sizeof(value)) {
714                 fprintf(stderr, "Maximum line length exceeded!\n");
715                 return NULL;
716         }
717
718         memcpy(value, p, e - p);
719         value[e - p] = 0;
720         return xstrdup(value);
721 }
722
723 static bool finalize_join(void) {
724         const char *name = get_value(data, "Name");
725
726         if(!name) {
727                 fprintf(stderr, "No Name found in invitation!\n");
728                 return false;
729         }
730
731         if(!check_id(name)) {
732                 fprintf(stderr, "Invalid Name found in invitation!\n");
733                 return false;
734         }
735
736         if(!netname) {
737                 char *net = grep(data, "NetName");
738
739                 if(net) {
740                         netname = net;
741
742                         if(!check_netname(netname, true)) {
743                                 fprintf(stderr, "Unsafe NetName found in invitation!\n");
744                                 return false;
745                         }
746                 }
747         }
748
749         bool ask_netname = false;
750         char temp_netname[32];
751
752 make_names:
753
754         if(!confbasegiven) {
755                 free(confbase);
756                 confbase = NULL;
757         }
758
759         make_names(false);
760
761         free(tinc_conf);
762         free(hosts_dir);
763
764         xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
765         xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
766
767         if(!access(tinc_conf, F_OK)) {
768                 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
769
770                 if(confbasegiven) {
771                         return false;
772                 }
773
774                 // Generate a random netname, ask for a better one later.
775                 ask_netname = true;
776                 snprintf(temp_netname, sizeof(temp_netname), "join_%x", prng(UINT32_MAX));
777                 netname = temp_netname;
778                 goto make_names;
779         }
780
781         if(!makedirs(DIR_HOSTS | DIR_CONFBASE | DIR_CACHE)) {
782                 return false;
783         }
784
785         FILE *f = fopen(tinc_conf, "w");
786
787         if(!f) {
788                 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
789                 return false;
790         }
791
792         fprintf(f, "Name = %s\n", name);
793
794         char filename[PATH_MAX];
795         snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
796         FILE *fh = fopen(filename, "w");
797
798         if(!fh) {
799                 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
800                 fclose(f);
801                 return false;
802         }
803
804         snprintf(filename, sizeof(filename), "%s" SLASH "invitation-data", confbase);
805         FILE *finv = fopen(filename, "w");
806
807         if(!finv || fwrite(data, datalen, 1, finv) != 1) {
808                 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
809                 fclose(fh);
810                 fclose(f);
811                 fclose(finv);
812                 return false;
813         }
814
815         fclose(finv);
816
817         snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up.invitation", confbase);
818         FILE *fup = fopen(filename, "w");
819
820         if(!fup) {
821                 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
822                 fclose(f);
823                 fclose(fh);
824                 return false;
825         }
826
827         ifconfig_header(fup);
828
829         // Filter first chunk on approved keywords, split between tinc.conf and hosts/Name
830         // Generate a tinc-up script from Ifconfig and Route keywords.
831         // Other chunks go unfiltered to their respective host config files
832         const char *p = data;
833         char *l, *value;
834
835         static char line[1024];
836
837         while((l = get_line(line, sizeof(line), &p))) {
838                 // Ignore comments
839                 if(*l == '#') {
840                         continue;
841                 }
842
843                 // Split line into variable and value
844                 size_t len = strcspn(l, "\t =");
845                 value = l + len;
846                 value += strspn(value, "\t ");
847
848                 if(*value == '=') {
849                         value++;
850                         value += strspn(value, "\t ");
851                 }
852
853                 l[len] = 0;
854
855                 // Ignore lines with empty variable names
856                 if(!*l) {
857                         continue;
858                 }
859
860                 // Is it a Name?
861                 if(!strcasecmp(l, "Name")) {
862                         if(strcmp(value, name)) {
863                                 break;
864                         } else {
865                                 continue;
866                         }
867                 } else if(!strcasecmp(l, "NetName")) {
868                         continue;
869                 }
870
871                 // Check the list of known variables
872                 bool found = false;
873                 int i;
874
875                 for(i = 0; variables[i].name; i++) {
876                         if(strcasecmp(l, variables[i].name)) {
877                                 continue;
878                         }
879
880                         found = true;
881                         break;
882                 }
883
884                 // Handle Ifconfig and Route statements
885                 if(!found) {
886                         if(!strcasecmp(l, "Ifconfig")) {
887                                 if(!strcasecmp(value, "dhcp")) {
888                                         ifconfig_dhcp(fup);
889                                 } else if(!strcasecmp(value, "dhcp6")) {
890                                         ifconfig_dhcp6(fup);
891                                 } else if(!strcasecmp(value, "slaac")) {
892                                         ifconfig_slaac(fup);
893                                 } else {
894                                         ifconfig_address(fup, value);
895                                 }
896
897                                 continue;
898                         } else if(!strcasecmp(l, "Route")) {
899                                 ifconfig_route(fup, value);
900                                 continue;
901                         }
902                 }
903
904                 // Ignore unknown and unsafe variables
905                 if(!found) {
906                         fprintf(stderr, "Ignoring unknown variable '%s' in invitation.\n", l);
907                         continue;
908                 } else if(!(variables[i].type & VAR_SAFE)) {
909                         if(force) {
910                                 fprintf(stderr, "Warning: unsafe variable '%s' in invitation.\n", l);
911                         } else {
912                                 fprintf(stderr, "Ignoring unsafe variable '%s' in invitation.\n", l);
913                                 continue;
914                         }
915                 }
916
917                 // Copy the safe variable to the right config file
918                 fprintf((variables[i].type & VAR_HOST) ? fh : f, "%s = %s\n", l, value);
919         }
920
921         fclose(f);
922         bool valid_tinc_up = ifconfig_footer(fup);
923         fclose(fup);
924
925         while(l && !strcasecmp(l, "Name")) {
926                 if(!check_id(value)) {
927                         fprintf(stderr, "Invalid Name found in invitation.\n");
928                         return false;
929                 }
930
931                 if(!strcmp(value, name)) {
932                         fprintf(stderr, "Secondary chunk would overwrite our own host config file.\n");
933                         return false;
934                 }
935
936                 snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, value);
937                 f = fopen(filename, "w");
938
939                 if(!f) {
940                         fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
941                         return false;
942                 }
943
944                 while((l = get_line(line, sizeof(line), &p))) {
945                         if(!strcmp(l, "#---------------------------------------------------------------#")) {
946                                 continue;
947                         }
948
949                         size_t len = strcspn(l, "\t =");
950
951                         if(len == 4 && !strncasecmp(l, "Name", 4)) {
952                                 value = l + len;
953                                 value += strspn(value, "\t ");
954
955                                 if(*value == '=') {
956                                         value++;
957                                         value += strspn(value, "\t ");
958                                 }
959
960                                 l[len] = 0;
961                                 break;
962                         }
963
964                         fputs(l, f);
965                         fputc('\n', f);
966                 }
967
968                 fclose(f);
969         }
970
971         // Generate our key and send a copy to the server
972         ecdsa_t *key = ecdsa_generate();
973
974         if(!key) {
975                 return false;
976         }
977
978         char *b64_pubkey = ecdsa_get_base64_public_key(key);
979
980         if(!b64_pubkey) {
981                 return false;
982         }
983
984         snprintf(filename, sizeof(filename), "%s" SLASH "ed25519_key.priv", confbase);
985         f = fopenmask(filename, "w", 0600);
986
987         if(!f) {
988                 return false;
989         }
990
991         if(!ecdsa_write_pem_private_key(key, f)) {
992                 fprintf(stderr, "Error writing private key!\n");
993                 ecdsa_free(key);
994                 fclose(f);
995                 return false;
996         }
997
998         fclose(f);
999
1000         fprintf(fh, "Ed25519PublicKey = %s\n", b64_pubkey);
1001
1002         sptps_send_record(&sptps, 1, b64_pubkey, strlen(b64_pubkey));
1003         free(b64_pubkey);
1004         ecdsa_free(key);
1005
1006 #ifndef DISABLE_LEGACY
1007         rsa_t *rsa = rsa_generate(2048, 0x1001);
1008         snprintf(filename, sizeof(filename), "%s" SLASH "rsa_key.priv", confbase);
1009         f = fopenmask(filename, "w", 0600);
1010
1011         if(!f || !rsa_write_pem_private_key(rsa, f)) {
1012                 fprintf(stderr, "Could not write private RSA key\n");
1013         } else if(!rsa_write_pem_public_key(rsa, fh)) {
1014                 fprintf(stderr, "Could not write public RSA key\n");
1015         }
1016
1017         fclose(f);
1018
1019         fclose(fh);
1020
1021         rsa_free(rsa);
1022 #endif
1023
1024         check_port(name);
1025
1026 ask_netname:
1027
1028         if(ask_netname && tty) {
1029                 fprintf(stderr, "Enter a new netname: ");
1030
1031                 if(!fgets(line, sizeof(line), stdin)) {
1032                         fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1033                         return false;
1034                 }
1035
1036                 if(!*line || *line == '\n') {
1037                         goto ask_netname;
1038                 }
1039
1040                 line[strlen(line) - 1] = 0;
1041
1042                 char newbase[PATH_MAX];
1043
1044                 if((size_t)snprintf(newbase, sizeof(newbase), CONFDIR SLASH "tinc" SLASH "%s", line) >= sizeof(newbase)) {
1045                         fprintf(stderr, "Filename too long: " CONFDIR SLASH "tinc" SLASH "%s\n", line);
1046                         goto ask_netname;
1047                 }
1048
1049                 if(rename(confbase, newbase)) {
1050                         fprintf(stderr, "Error trying to rename %s to %s: %s\n", confbase, newbase, strerror(errno));
1051                         goto ask_netname;
1052                 }
1053
1054                 netname = line;
1055                 make_names(false);
1056         }
1057
1058         char filename2[PATH_MAX];
1059         snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up.invitation", confbase);
1060
1061 #ifdef HAVE_WINDOWS
1062         snprintf(filename2, sizeof(filename2), "%s" SLASH "tinc-up.bat", confbase);
1063 #else
1064         snprintf(filename2, sizeof(filename2), "%s" SLASH "tinc-up", confbase);
1065 #endif
1066
1067         if(valid_tinc_up) {
1068                 if(tty) {
1069                         FILE *fup = fopen(filename, "r");
1070
1071                         if(fup) {
1072                                 fprintf(stderr, "\nPlease review the following tinc-up script:\n\n");
1073
1074                                 char buf[MAXSIZE];
1075
1076                                 while(fgets(buf, sizeof(buf), fup)) {
1077                                         fputs(buf, stderr);
1078                                 }
1079
1080                                 fclose(fup);
1081
1082                                 int response = 0;
1083
1084                                 do {
1085                                         fprintf(stderr, "\nDo you want to use this script [y]es/[n]o/[e]dit? ");
1086                                         response = tolower(getchar());
1087                                 } while(!strchr("yne", response));
1088
1089                                 fprintf(stderr, "\n");
1090
1091                                 if(response == 'e') {
1092                                         char *command;
1093 #ifndef HAVE_WINDOWS
1094                                         const char *editor = getenv("VISUAL");
1095
1096                                         if(!editor) {
1097                                                 editor = getenv("EDITOR");
1098                                         }
1099
1100                                         if(!editor) {
1101                                                 editor = "vi";
1102                                         }
1103
1104                                         xasprintf(&command, "\"%s\" \"%s\"", editor, filename);
1105 #else
1106                                         xasprintf(&command, "edit \"%s\"", filename);
1107 #endif
1108
1109                                         if(system(command)) {
1110                                                 response = 'n';
1111                                         } else {
1112                                                 response = 'y';
1113                                         }
1114
1115                                         free(command);
1116                                 }
1117
1118                                 if(response == 'y') {
1119                                         rename(filename, filename2);
1120                                         chmod(filename2, 0755);
1121                                         fprintf(stderr, "tinc-up enabled.\n");
1122                                 } else {
1123                                         fprintf(stderr, "tinc-up has been left disabled.\n");
1124                                 }
1125                         }
1126                 } else {
1127                         if(force) {
1128                                 rename(filename, filename2);
1129                                 chmod(filename2, 0755);
1130                                 fprintf(stderr, "tinc-up enabled.\n");
1131                         } else {
1132                                 fprintf(stderr, "A tinc-up script was generated, but has been left disabled.\n");
1133                         }
1134                 }
1135         } else {
1136                 // A placeholder was generated.
1137                 rename(filename, filename2);
1138                 chmod(filename2, 0755);
1139         }
1140
1141         fprintf(stderr, "Configuration stored in: %s\n", confbase);
1142
1143         return true;
1144 }
1145
1146
1147 static bool invitation_send(void *handle, uint8_t type, const void *vdata, size_t len) {
1148         (void)handle;
1149         (void)type;
1150         const char *data = vdata;
1151
1152         while(len) {
1153                 ssize_t result = send(sock, data, len, 0);
1154
1155                 if(result == -1 && sockwouldblock(sockerrno)) {
1156                         continue;
1157                 } else if(result <= 0) {
1158                         return false;
1159                 }
1160
1161                 data += result;
1162                 len -= result;
1163         }
1164
1165         return true;
1166 }
1167
1168 static bool invitation_receive(void *handle, uint8_t type, const void *msg, uint16_t len) {
1169         (void)handle;
1170
1171         switch(type) {
1172         case SPTPS_HANDSHAKE:
1173                 return sptps_send_record(&sptps, 0, cookie, sizeof(cookie));
1174
1175         case 0:
1176                 data = xrealloc(data, datalen + len + 1);
1177                 memcpy(data + datalen, msg, len);
1178                 datalen += len;
1179                 data[datalen] = 0;
1180                 break;
1181
1182         case 1:
1183                 return finalize_join();
1184
1185         case 2:
1186                 fprintf(stderr, "Invitation successfully accepted.\n");
1187                 shutdown(sock, SHUT_RDWR);
1188                 success = true;
1189                 break;
1190
1191         default:
1192                 return false;
1193         }
1194
1195         return true;
1196 }
1197
1198 int cmd_join(int argc, char *argv[]) {
1199         free(data);
1200         data = NULL;
1201         datalen = 0;
1202
1203         if(argc > 2) {
1204                 fprintf(stderr, "Too many arguments!\n");
1205                 return 1;
1206         }
1207
1208         // Make sure confbase exists and is accessible.
1209         if(!makedirs(DIR_CONFDIR | DIR_CONFBASE)) {
1210                 return false;
1211         }
1212
1213         if(access(confbase, R_OK | W_OK | X_OK)) {
1214                 fprintf(stderr, "No permission to write in directory %s: %s\n", confbase, strerror(errno));
1215                 return 1;
1216         }
1217
1218         // If a netname or explicit configuration directory is specified, check for an existing tinc.conf.
1219         if((netname || confbasegiven) && !access(tinc_conf, F_OK)) {
1220                 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
1221                 return 1;
1222         }
1223
1224         // Either read the invitation from the command line or from stdin.
1225         char *invitation;
1226
1227         if(argc > 1) {
1228                 invitation = argv[1];
1229         } else {
1230                 if(tty) {
1231                         fprintf(stderr, "Enter invitation URL: ");
1232                 }
1233
1234                 errno = EPIPE;
1235
1236                 if(!fgets(line, sizeof(line), stdin)) {
1237                         fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
1238                         return 1;
1239                 }
1240
1241                 invitation = line;
1242         }
1243
1244         // Parse the invitation URL.
1245         rstrip(line);
1246
1247         char *slash = strchr(invitation, '/');
1248
1249         if(!slash) {
1250                 goto invalid;
1251         }
1252
1253         *slash++ = 0;
1254
1255         if(strlen(slash) != 48) {
1256                 goto invalid;
1257         }
1258
1259         char *address = invitation;
1260         char *port = NULL;
1261
1262         if(*address == '[') {
1263                 address++;
1264                 char *bracket = strchr(address, ']');
1265
1266                 if(!bracket) {
1267                         goto invalid;
1268                 }
1269
1270                 *bracket = 0;
1271
1272                 if(bracket[1] == ':') {
1273                         port = bracket + 2;
1274                 }
1275         } else {
1276                 port = strchr(address, ':');
1277
1278                 if(port) {
1279                         *port++ = 0;
1280                 }
1281         }
1282
1283         if(!port || !*port) {
1284                 static char default_port[] = "655";
1285                 port = default_port;
1286         }
1287
1288         if(!b64decode_tinc(slash, hash, 24) || !b64decode_tinc(slash + 24, cookie, 24)) {
1289                 goto invalid;
1290         }
1291
1292         // Generate a throw-away key for the invitation.
1293         ecdsa_t *key = ecdsa_generate();
1294
1295         if(!key) {
1296                 return 1;
1297         }
1298
1299         char *b64_pubkey = ecdsa_get_base64_public_key(key);
1300
1301         // Connect to the tinc daemon mentioned in the URL.
1302         struct addrinfo *ai = str2addrinfo(address, port, SOCK_STREAM);
1303
1304         if(!ai) {
1305                 free(b64_pubkey);
1306                 ecdsa_free(key);
1307                 return 1;
1308         }
1309
1310         struct addrinfo *aip = NULL;
1311
1312 next:
1313         if(!aip) {
1314                 aip = ai;
1315         } else {
1316                 aip = aip->ai_next;
1317
1318                 if(!aip) {
1319                         freeaddrinfo(ai);
1320                         free(b64_pubkey);
1321                         ecdsa_free(key);
1322                         return 1;
1323                 }
1324         }
1325
1326         sock = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol);
1327
1328         if(sock <= 0) {
1329                 fprintf(stderr, "Could not open socket: %s\n", strerror(errno));
1330                 goto next;
1331         }
1332
1333         if(connect(sock, aip->ai_addr, aip->ai_addrlen)) {
1334                 char *addrstr, *portstr;
1335                 sockaddr2str((sockaddr_t *)aip->ai_addr, &addrstr, &portstr);
1336                 fprintf(stderr, "Could not connect to %s port %s: %s\n", addrstr, portstr, strerror(errno));
1337                 free(addrstr);
1338                 free(portstr);
1339                 closesocket(sock);
1340                 goto next;
1341         }
1342
1343         fprintf(stderr, "Connected to %s port %s...\n", address, port);
1344
1345         // Tell him we have an invitation, and give him our throw-away key.
1346         ssize_t len = snprintf(line, sizeof(line), "0 ?%s %d.%d\n", b64_pubkey, PROT_MAJOR, PROT_MINOR);
1347
1348         if(len <= 0 || (size_t)len >= sizeof(line)) {
1349                 abort();
1350         }
1351
1352         if(!sendline(sock, "0 ?%s %d.%d", b64_pubkey, PROT_MAJOR, 1)) {
1353                 fprintf(stderr, "Error sending request to %s port %s: %s\n", address, port, strerror(errno));
1354                 closesocket(sock);
1355                 goto next;
1356         }
1357
1358         char hisname[4096] = "";
1359         int code, hismajor, hisminor = 0;
1360
1361         if(!recvline(sock, line, sizeof(line)) || sscanf(line, "%d %4095s %d.%d", &code, hisname, &hismajor, &hisminor) < 3 || code != 0 || hismajor != PROT_MAJOR || !check_id(hisname) || !recvline(sock, line, sizeof(line)) || !rstrip(line) || sscanf(line, "%d ", &code) != 1 || code != ACK || strlen(line) < 3) {
1362                 fprintf(stderr, "Cannot read greeting from peer\n");
1363                 closesocket(sock);
1364                 goto next;
1365         }
1366
1367         freeaddrinfo(ai);
1368         ai = NULL;
1369         aip = NULL;
1370
1371         free(b64_pubkey);
1372         b64_pubkey = NULL;
1373
1374         // Check if the hash of the key he gave us matches the hash in the URL.
1375         char *fingerprint = line + 2;
1376         char hishash[64];
1377
1378         if(sha512(fingerprint, strlen(fingerprint), hishash)) {
1379                 fprintf(stderr, "Could not create digest\n%s\n", line + 2);
1380                 ecdsa_free(key);
1381                 return 1;
1382         }
1383
1384         if(memcmp(hishash, hash, 18)) {
1385                 fprintf(stderr, "Peer has an invalid key!\n%s\n", line + 2);
1386                 ecdsa_free(key);
1387                 return 1;
1388
1389         }
1390
1391         ecdsa_t *hiskey = ecdsa_set_base64_public_key(fingerprint);
1392
1393         if(!hiskey) {
1394                 ecdsa_free(key);
1395                 return 1;
1396         }
1397
1398         // Start an SPTPS session
1399         if(!sptps_start(&sptps, NULL, true, false, key, hiskey, "tinc invitation", 15, invitation_send, invitation_receive)) {
1400                 ecdsa_free(hiskey);
1401                 ecdsa_free(key);
1402                 return 1;
1403         }
1404
1405         // Feed rest of input buffer to SPTPS
1406         if(!sptps_receive_data(&sptps, buffer, blen)) {
1407                 success = false;
1408                 goto exit;
1409         }
1410
1411         while((len = recv(sock, line, sizeof(line), 0))) {
1412                 if(len < 0) {
1413                         if(sockwouldblock(sockerrno)) {
1414                                 continue;
1415                         }
1416
1417 #if HAVE_WINDOWS
1418
1419                         // If socket has been shut down, recv() on Windows returns -1 and sets sockerrno
1420                         // to WSAESHUTDOWN, while on UNIX-like operating systems recv() returns 0, so we
1421                         // have to do an explicit check here.
1422                         if(sockshutdown(sockerrno)) {
1423                                 break;
1424                         }
1425
1426 #endif
1427                         fprintf(stderr, "Error reading data from %s port %s: %s\n", address, port, sockstrerror(sockerrno));
1428                         success = false;
1429                         goto exit;
1430                 }
1431
1432                 char *p = line;
1433
1434                 while(len) {
1435                         size_t done = sptps_receive_data(&sptps, p, len);
1436
1437                         if(!done) {
1438                                 success = false;
1439                                 goto exit;
1440                         }
1441
1442                         len -= (ssize_t) done;
1443                         p += done;
1444                 }
1445         }
1446
1447 exit:
1448         sptps_stop(&sptps);
1449         ecdsa_free(hiskey);
1450         ecdsa_free(key);
1451         closesocket(sock);
1452
1453         if(!success) {
1454                 fprintf(stderr, "Invitation cancelled.\n");
1455                 return 1;
1456         }
1457
1458         return 0;
1459
1460 invalid:
1461         fprintf(stderr, "Invalid invitation URL.\n");
1462         return 1;
1463 }