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