Enable and fix many extra warnings supported by GCC and Clang.
[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         uint8_t buf[18 + strlen(fingerprint)];
461         char cookiehash[64];
462         memcpy(buf, cookie, 18);
463         memcpy(buf + 18, fingerprint, sizeof(buf) - 18);
464         sha512(buf, sizeof(buf), cookiehash);
465         b64encode_tinc_urlsafe(cookiehash, cookiehash, 18);
466
467         free(fingerprint);
468
469         b64encode_tinc_urlsafe(cookie, cookie, 18);
470
471         // Create a file containing the details of the invitation.
472         snprintf(filename, sizeof(filename), "%s" SLASH "invitations" SLASH "%s", confbase, cookiehash);
473         int ifd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600);
474
475         if(!ifd) {
476                 fprintf(stderr, "Could not create invitation file %s: %s\n", filename, strerror(errno));
477                 return 1;
478         }
479
480         f = fdopen(ifd, "w");
481
482         if(!f) {
483                 abort();
484         }
485
486         // Get the local address
487         char *address = get_my_hostname();
488
489         // Fill in the details.
490         fprintf(f, "Name = %s\n", argv[1]);
491
492         if(check_netname(netname, true)) {
493                 fprintf(f, "NetName = %s\n", netname);
494         }
495
496         fprintf(f, "ConnectTo = %s\n", myname);
497
498         // Copy Broadcast and Mode
499         FILE *tc = fopen(tinc_conf, "r");
500
501         if(tc) {
502                 char buf[1024];
503
504                 while(fgets(buf, sizeof(buf), tc)) {
505                         if((!strncasecmp(buf, "Mode", 4) && strchr(" \t=", buf[4]))
506                                         || (!strncasecmp(buf, "Broadcast", 9) && strchr(" \t=", buf[9]))) {
507                                 fputs(buf, f);
508
509                                 // Make sure there is a newline character.
510                                 if(!strchr(buf, '\n')) {
511                                         fputc('\n', f);
512                                 }
513                         }
514                 }
515
516                 fclose(tc);
517         }
518
519         fprintf(f, "#---------------------------------------------------------------#\n");
520         fprintf(f, "Name = %s\n", myname);
521
522         char filename2[PATH_MAX];
523         snprintf(filename2, sizeof(filename2), "%s" SLASH "hosts" SLASH "%s", confbase, myname);
524         fcopy(f, filename2);
525         fclose(f);
526
527         // Create an URL from the local address, key hash and cookie
528         char *url;
529         xasprintf(&url, "%s/%s%s", address, hash, cookie);
530
531         // Call the inviation-created script
532         environment_t env;
533         environment_init(&env);
534         environment_add(&env, "NODE=%s", argv[1]);
535         environment_add(&env, "INVITATION_FILE=%s", filename);
536         environment_add(&env, "INVITATION_URL=%s", url);
537         execute_script("invitation-created", &env);
538         environment_exit(&env);
539
540         puts(url);
541         free(url);
542         free(address);
543
544         return 0;
545 }
546
547 static int sock;
548 static char cookie[18], hash[18];
549 static sptps_t sptps;
550 static char *data;
551 static size_t datalen;
552 static bool success = false;
553
554 static char *get_line(const char **data) {
555         if(!data || !*data) {
556                 return NULL;
557         }
558
559         if(! **data) {
560                 *data = NULL;
561                 return NULL;
562         }
563
564         static char line[1024];
565         const char *end = strchr(*data, '\n');
566         size_t len = end ? (size_t)(end - *data) : strlen(*data);
567
568         if(len >= sizeof(line)) {
569                 fprintf(stderr, "Maximum line length exceeded!\n");
570                 return NULL;
571         }
572
573         if(len && !isprint((uint8_t) **data)) {
574                 abort();
575         }
576
577         memcpy(line, *data, len);
578         line[len] = 0;
579
580         if(end) {
581                 *data = end + 1;
582         } else {
583                 *data = NULL;
584         }
585
586         return line;
587 }
588
589 static char *get_value(const char *data, const char *var) {
590         char *line = get_line(&data);
591
592         if(!line) {
593                 return NULL;
594         }
595
596         char *sep = line + strcspn(line, " \t=");
597         char *val = sep + strspn(sep, " \t");
598
599         if(*val == '=') {
600                 val += 1 + strspn(val + 1, " \t");
601         }
602
603         *sep = 0;
604
605         if(strcasecmp(line, var)) {
606                 return NULL;
607         }
608
609         return val;
610 }
611
612 static char *grep(const char *data, const char *var) {
613         static char value[1024];
614
615         const char *p = data;
616         size_t varlen = strlen(var);
617
618         // Skip all lines not starting with var
619         while(strncasecmp(p, var, varlen) || !strchr(" \t=", p[varlen])) {
620                 p = strchr(p, '\n');
621
622                 if(!p) {
623                         break;
624                 } else {
625                         p++;
626                 }
627         }
628
629         if(!p) {
630                 return NULL;
631         }
632
633         p += varlen;
634         p += strspn(p, " \t");
635
636         if(*p == '=') {
637                 p += 1 + strspn(p + 1, " \t");
638         }
639
640         const char *e = strchr(p, '\n');
641
642         if(!e) {
643                 return xstrdup(p);
644         }
645
646         if((size_t)(e - p) >= sizeof(value)) {
647                 fprintf(stderr, "Maximum line length exceeded!\n");
648                 return NULL;
649         }
650
651         memcpy(value, p, e - p);
652         value[e - p] = 0;
653         return value;
654 }
655
656 static bool finalize_join(void) {
657         const char *temp_name = get_value(data, "Name");
658
659         if(!temp_name) {
660                 fprintf(stderr, "No Name found in invitation!\n");
661                 return false;
662         }
663
664         size_t len = strlen(temp_name);
665         char name[len + 1];
666         memcpy(name, temp_name, len);
667         name[len] = 0;
668
669         if(!check_id(name)) {
670                 fprintf(stderr, "Invalid Name found in invitation!\n");
671                 return false;
672         }
673
674         if(!netname) {
675                 netname = xstrdup(grep(data, "NetName"));
676
677                 if(netname && !check_netname(netname, true)) {
678                         fprintf(stderr, "Unsafe NetName found in invitation!\n");
679                         return false;
680                 }
681         }
682
683         bool ask_netname = false;
684         char temp_netname[32];
685
686 make_names:
687
688         if(!confbasegiven) {
689                 free(confbase);
690                 confbase = NULL;
691         }
692
693         make_names(false);
694
695         free(tinc_conf);
696         free(hosts_dir);
697
698         xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
699         xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
700
701         if(!access(tinc_conf, F_OK)) {
702                 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
703
704                 if(confbasegiven) {
705                         return false;
706                 }
707
708                 // Generate a random netname, ask for a better one later.
709                 ask_netname = true;
710                 snprintf(temp_netname, sizeof(temp_netname), "join_%x", prng(UINT32_MAX));
711                 netname = temp_netname;
712                 goto make_names;
713         }
714
715         if(mkdir(confbase, 0777) && errno != EEXIST) {
716                 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
717                 return false;
718         }
719
720         if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
721                 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
722                 return false;
723         }
724
725         FILE *f = fopen(tinc_conf, "w");
726
727         if(!f) {
728                 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
729                 return false;
730         }
731
732         fprintf(f, "Name = %s\n", name);
733
734         char filename[PATH_MAX];
735         snprintf(filename, sizeof(filename), "%s" SLASH "%s", hosts_dir, name);
736         FILE *fh = fopen(filename, "w");
737
738         if(!fh) {
739                 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
740                 fclose(f);
741                 return false;
742         }
743
744         snprintf(filename, sizeof(filename), "%s" SLASH "invitation-data", confbase);
745         FILE *finv = fopen(filename, "w");
746
747         if(!finv || fwrite(data, datalen, 1, finv) != 1) {
748                 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
749                 fclose(fh);
750                 fclose(f);
751                 fclose(finv);
752                 return false;
753         }
754
755         fclose(finv);
756
757         snprintf(filename, sizeof(filename), "%s" SLASH "tinc-up.invitation", confbase);
758         FILE *fup = fopen(filename, "w");
759
760         if(!fup) {
761                 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
762                 fclose(f);
763                 fclose(fh);
764                 return false;
765         }
766
767         ifconfig_header(fup);
768
769         // Filter first chunk on approved keywords, split between tinc.conf and hosts/Name
770         // Generate a tinc-up script from Ifconfig and Route keywords.
771         // Other chunks go unfiltered to their respective host config files
772         const char *p = data;
773         char *l, *value;
774
775         while((l = get_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(&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 }