]> git.meshlink.io Git - meshlink/blob - src/invitation.c
Dejavu: Use snprintf() instead of xasprintf() when generating filenames
[meshlink] / src / invitation.c
1 /*
2     invitation.c -- Create and accept invitations
3     Copyright (C) 2014 Guus Sliepen <guus@meshlink.io>
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 "invitation.h"
27 #include "netutl.h"
28 #include "sptps.h"
29 #include "tincctl.h"
30 #include "utils.h"
31 #include "xalloc.h"
32
33 int addressfamily = AF_UNSPEC;
34
35 static void scan_for_hostname(const char *filename, char **hostname, char **port) {
36         if(!filename || (*hostname && *port))
37                 return;
38
39         FILE *f = fopen(filename, "r");
40         if(!f)
41                 return;
42
43         while(fgets(line, sizeof line, f)) {
44                 if(!rstrip(line))
45                         continue;
46                 char *p = line, *q;
47                 p += strcspn(p, "\t =");
48                 if(!*p)
49                         continue;
50                 q = p + strspn(p, "\t ");
51                 if(*q == '=')
52                         q += 1 + strspn(q + 1, "\t ");
53                 *p = 0;
54                 p = q + strcspn(q, "\t ");
55                 if(*p)
56                         *p++ = 0;
57                 p += strspn(p, "\t ");
58                 p[strcspn(p, "\t ")] = 0;
59
60                 if(!*port && !strcasecmp(line, "Port")) {
61                         *port = xstrdup(q);
62                 } else if(!*hostname && !strcasecmp(line, "Address")) {
63                         *hostname = xstrdup(q);
64                         if(*p) {
65                                 free(*port);
66                                 *port = xstrdup(p);
67                         }
68                 }
69
70                 if(*hostname && *port)
71                         break;
72         }
73
74         fclose(f);
75 }
76
77 char *get_my_hostname() {
78         char *hostname = NULL;
79         char *port = NULL;
80         char *hostport = NULL;
81         char *name = get_my_name(false);
82         char filename[PATH_MAX];
83
84         // Use first Address statement in own host config file
85         if(check_id(name)) {
86                 snprintf(filename,PATH_MAX, "%s" SLASH "hosts" SLASH "%s", confbase, name);
87                 scan_for_hostname(filename, &hostname, &port);
88                 scan_for_hostname(tinc_conf, &hostname, &port);
89         }
90
91         if(hostname)
92                 goto done;
93
94         // If that doesn't work, guess externally visible hostname
95         fprintf(stderr, "Trying to discover externally visible hostname...\n");
96         struct addrinfo *ai = str2addrinfo("tinc-vpn.org", "80", SOCK_STREAM);
97         struct addrinfo *aip = ai;
98         static const char request[] = "GET http://tinc-vpn.org/host.cgi HTTP/1.0\r\n\r\n";
99
100         while(aip) {
101                 int s = socket(aip->ai_family, aip->ai_socktype, aip->ai_protocol);
102                 if(s >= 0) {
103                         if(connect(s, aip->ai_addr, aip->ai_addrlen)) {
104                                 closesocket(s);
105                                 s = -1;
106                         }
107                 }
108                 if(s >= 0) {
109                         send(s, request, sizeof request - 1, 0);
110                         int len = recv(s, line, sizeof line - 1, MSG_WAITALL);
111                         if(len > 0) {
112                                 line[len] = 0;
113                                 if(line[len - 1] == '\n')
114                                         line[--len] = 0;
115                                 char *p = strrchr(line, '\n');
116                                 if(p && p[1])
117                                         hostname = xstrdup(p + 1);
118                         }
119                         closesocket(s);
120                         if(hostname)
121                                 break;
122                 }
123                 aip = aip->ai_next;
124                 continue;
125         }
126
127         if(ai)
128                 freeaddrinfo(ai);
129
130         // Check that the hostname is reasonable
131         if(hostname) {
132                 for(char *p = hostname; *p; p++) {
133                         if(isalnum(*p) || *p == '-' || *p == '.' || *p == ':')
134                                 continue;
135                         // If not, forget it.
136                         free(hostname);
137                         hostname = NULL;
138                         break;
139                 }
140         }
141
142         if(!tty) {
143                 if(!hostname) {
144                         fprintf(stderr, "Could not determine the external address or hostname. Please set Address manually.\n");
145                         return NULL;
146                 }
147                 goto save;
148         }
149
150 again:
151         fprintf(stderr, "Please enter your host's external address or hostname");
152         if(hostname)
153                 fprintf(stderr, " [%s]", hostname);
154         fprintf(stderr, ": ");
155
156         if(!fgets(line, sizeof line, stdin)) {
157                 fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
158                 free(hostname);
159                 return NULL;
160         }
161
162         if(!rstrip(line)) {
163                 if(hostname)
164                         goto save;
165                 else
166                         goto again;
167         }
168
169         for(char *p = line; *p; p++) {
170                 if(isalnum(*p) || *p == '-' || *p == '.')
171                         continue;
172                 fprintf(stderr, "Invalid address or hostname.\n");
173                 goto again;
174         }
175
176         free(hostname);
177         hostname = xstrdup(line);
178
179 save:
180         if(filename) {
181                 FILE *f = fopen(filename, "a");
182                 if(f) {
183                         fprintf(f, "\nAddress = %s\n", hostname);
184                         fclose(f);
185                 } else {
186                         fprintf(stderr, "Could not append Address to %s: %s\n", filename, strerror(errno));
187                 }
188         }
189
190 done:
191         if(port) {
192                 if(strchr(hostname, ':'))
193                         xasprintf(&hostport, "[%s]:%s", hostname, port);
194                 else
195                         xasprintf(&hostport, "%s:%s", hostname, port);
196         } else {
197                 hostport = hostname;
198                 hostname = NULL;
199         }
200
201         free(hostname);
202         free(port);
203         return hostport;
204 }
205
206 static bool fcopy(FILE *out, const char *filename) {
207         FILE *in = fopen(filename, "r");
208         if(!in) {
209                 fprintf(stderr, "Could not open %s: %s\n", filename, strerror(errno));
210                 return false;
211         }
212
213         char buf[1024];
214         size_t len;
215         while((len = fread(buf, 1, sizeof buf, in)))
216                 fwrite(buf, len, 1, out);
217         fclose(in);
218         return true;
219 }
220
221 int cmd_invite(int argc, char *argv[]) {
222         if(argc < 2) {
223                 fprintf(stderr, "Not enough arguments!\n");
224                 return 1;
225         }
226
227         // Check validity of the new node's name
228         if(!check_id(argv[1])) {
229                 fprintf(stderr, "Invalid name for node.\n");
230                 return 1;
231         }
232
233         char *myname = get_my_name(true);
234         if(!myname)
235                 return 1;
236
237         // Ensure no host configuration file with that name exists
238         char filename [PATH_MAX];
239         snprintf(filename,PATH_MAX, "%s" SLASH "hosts" SLASH "%s", confbase, argv[1]);
240         if(!access(filename, F_OK)) {
241                 fprintf(stderr, "A host config file for %s already exists!\n", argv[1]);
242                 return 1;
243         }
244
245         // If a daemon is running, ensure no other nodes now about this name
246         bool found = false;
247         if(connect_tincd(false)) {
248                 sendline(fd, "%d %d", CONTROL, REQ_DUMP_NODES);
249
250                 while(recvline(fd, line, sizeof line)) {
251                         char node[4096];
252                         int code, req;
253                         if(sscanf(line, "%d %d %s", &code, &req, node) != 3)
254                                 break;
255                         if(!strcmp(node, argv[1]))
256                                 found = true;
257                 }
258
259                 if(found) {
260                         fprintf(stderr, "A node with name %s is already known!\n", argv[1]);
261                         return 1;
262                 }
263         }
264
265         char hash[64];
266
267         snprintf(filename,PATH_MAX, "%s" SLASH "invitations", confbase);
268         if(mkdir(filename, 0700) && errno != EEXIST) {
269                 fprintf(stderr, "Could not create directory %s: %s\n", filename, strerror(errno));
270                 return 1;
271         }
272
273         // Count the number of valid invitations, clean up old ones
274         DIR *dir = opendir(filename);
275         if(!dir) {
276                 fprintf(stderr, "Could not read directory %s: %s\n", filename, strerror(errno));
277                 return 1;
278         }
279
280         errno = 0;
281         int count = 0;
282         struct dirent *ent;
283         time_t deadline = time(NULL) - 604800; // 1 week in the past
284
285         while((ent = readdir(dir))) {
286                 if(strlen(ent->d_name) != 24)
287                         continue;
288                 char invname[PATH_MAX];
289                 struct stat st;
290                 snprintf(invname,PATH_MAX, "%s" SLASH "%s", filename, ent->d_name);
291                 if(!stat(invname, &st)) {
292                         if(deadline < st.st_mtime)
293                                 count++;
294                         else
295                                 unlink(invname);
296                 } else {
297                         fprintf(stderr, "Could not stat %s: %s\n", invname, strerror(errno));
298                         errno = 0;
299                 }
300         }
301
302         if(errno) {
303                 fprintf(stderr, "Error while reading directory %s: %s\n", filename, strerror(errno));
304                 closedir(dir);
305                 return 1;
306         }
307                 
308         closedir(dir);
309
310         ecdsa_t *key;
311         snprintf(filename,PATH_MAX, "%s" SLASH "invitations" SLASH "ecdsa_key.priv", confbase);
312
313         // Remove the key if there are no outstanding invitations.
314         if(!count)
315                 unlink(filename);
316
317         // Create a new key if necessary.
318         FILE *f = fopen(filename, "r");
319         if(!f) {
320                 if(errno != ENOENT) {
321                         fprintf(stderr, "Could not read %s: %s\n", filename, strerror(errno));
322                         return 1;
323                 }
324
325                 key = ecdsa_generate();
326                 if(!key) {
327                         return 1;
328                 }
329                 f = fopen(filename, "w");
330                 if(!f) {
331                         fprintf(stderr, "Could not write %s: %s\n", filename, strerror(errno));
332                         return 1;
333                 }
334                 chmod(filename, 0600);
335                 ecdsa_write_pem_private_key(key, f);
336                 fclose(f);
337
338                 if(connect_tincd(false))
339                         sendline(fd, "%d %d", CONTROL, REQ_RELOAD);
340         } else {
341                 key = ecdsa_read_pem_private_key(f);
342                 fclose(f);
343                 if(!key)
344                         fprintf(stderr, "Could not read private key from %s\n", filename);
345         }
346
347         if(!key)
348                 return 1;
349
350         // Create a hash of the key.
351         char *fingerprint = ecdsa_get_base64_public_key(key);
352         sha512(fingerprint, strlen(fingerprint), hash);
353         b64encode_urlsafe(hash, hash, 18);
354
355         // Create a random cookie for this invitation.
356         char cookie[25];
357         randomize(cookie, 18);
358
359         // Create a filename that doesn't reveal the cookie itself
360         char buf[18 + strlen(fingerprint)];
361         char cookiehash[64];
362         memcpy(buf, cookie, 18);
363         memcpy(buf + 18, fingerprint, sizeof buf - 18);
364         sha512(buf, sizeof buf, cookiehash);
365         b64encode_urlsafe(cookiehash, cookiehash, 18);
366
367         b64encode_urlsafe(cookie, cookie, 18);
368
369         // Create a file containing the details of the invitation.
370         snprintf(filename,PATH_MAX, "%s" SLASH "invitations" SLASH "%s", confbase, cookiehash);
371         int ifd = open(filename, O_RDWR | O_CREAT | O_EXCL, 0600);
372         if(!ifd) {
373                 fprintf(stderr, "Could not create invitation file %s: %s\n", filename, strerror(errno));
374                 return 1;
375         }
376         f = fdopen(ifd, "w");
377         if(!f)
378                 abort();
379
380         // Get the local address
381         char *address = get_my_hostname();
382
383         // Fill in the details.
384         fprintf(f, "Name = %s\n", argv[1]);
385         if(netname)
386                 fprintf(f, "NetName = %s\n", netname);
387         fprintf(f, "ConnectTo = %s\n", myname);
388
389         // Copy Broadcast and Mode
390         FILE *tc = fopen(tinc_conf, "r");
391         if(tc) {
392                 char buf[1024];
393                 while(fgets(buf, sizeof buf, tc)) {
394                         if((!strncasecmp(buf, "Mode", 4) && strchr(" \t=", buf[4]))
395                                         || (!strncasecmp(buf, "Broadcast", 9) && strchr(" \t=", buf[9]))) {
396                                 fputs(buf, f);
397                                 // Make sure there is a newline character.
398                                 if(!strchr(buf, '\n'))
399                                         fputc('\n', f);
400                         }
401                 }
402                 fclose(tc);
403         }
404
405         fprintf(f, "#---------------------------------------------------------------#\n");
406         fprintf(f, "Name = %s\n", myname);
407
408         char filename2[PATH_MAX];
409         snsprintf(filename2,PATH_MAX, "%s" SLASH "hosts" SLASH "%s", confbase, myname);
410         fcopy(f, filename2);
411         fclose(f);
412
413         // Create an URL from the local address, key hash and cookie
414         char *url;
415         xasprintf(&url, "%s/%s%s", address, hash, cookie);
416
417         return 0;
418 }
419
420 static int sock;
421 static char cookie[18];
422 static sptps_t sptps;
423 static char *data;
424 static size_t datalen;
425 static bool success = false;
426
427 static char cookie[18], hash[18];
428
429 static char *get_line(const char **data) {
430         if(!data || !*data)
431                 return NULL;
432
433         if(!**data) {
434                 *data = NULL;
435                 return NULL;
436         }
437
438         static char line[1024];
439         const char *end = strchr(*data, '\n');
440         size_t len = end ? end - *data : strlen(*data);
441         if(len >= sizeof line) {
442                 fprintf(stderr, "Maximum line length exceeded!\n");
443                 return NULL;
444         }
445         if(len && !isprint(**data))
446                 abort();
447
448         memcpy(line, *data, len);
449         line[len] = 0;
450
451         if(end)
452                 *data = end + 1;
453         else
454                 *data = NULL;
455
456         return line;
457 }
458
459 static char *get_value(const char *data, const char *var) {
460         char *line = get_line(&data);
461         if(!line)
462                 return NULL;
463
464         char *sep = line + strcspn(line, " \t=");
465         char *val = sep + strspn(sep, " \t");
466         if(*val == '=')
467                 val += 1 + strspn(val + 1, " \t");
468         *sep = 0;
469         if(strcasecmp(line, var))
470                 return NULL;
471         return val;
472 }
473
474 static char *grep(const char *data, const char *var) {
475         static char value[1024];
476
477         const char *p = data;
478         int varlen = strlen(var);
479
480         // Skip all lines not starting with var
481         while(strncasecmp(p, var, varlen) || !strchr(" \t=", p[varlen])) {
482                 p = strchr(p, '\n');
483                 if(!p)
484                         break;
485                 else
486                         p++;
487         }
488
489         if(!p)
490                 return NULL;
491
492         p += varlen;
493         p += strspn(p, " \t");
494         if(*p == '=')
495                 p += 1 + strspn(p + 1, " \t");
496
497         const char *e = strchr(p, '\n');
498         if(!e)
499                 return xstrdup(p);
500
501         if(e - p >= sizeof value) {
502                 fprintf(stderr, "Maximum line length exceeded!\n");
503                 return NULL;
504         }
505
506         memcpy(value, p, e - p);
507         value[e - p] = 0;
508         return value;
509 }
510
511 static bool finalize_join(void) {
512         char *name = xstrdup(get_value(data, "Name"));
513         if(!name) {
514                 fprintf(stderr, "No Name found in invitation!\n");
515                 return false;
516         }
517
518         if(!check_id(name)) {
519                 fprintf(stderr, "Invalid Name found in invitation: %s!\n", name);
520                 return false;
521         }
522
523         if(!netname)
524                 netname = grep(data, "NetName");
525
526         bool ask_netname = false;
527         char temp_netname[32];
528
529 make_names:
530         free(tinc_conf);
531         free(hosts_dir);
532
533         xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
534         xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
535
536         if(!access(tinc_conf, F_OK)) {
537                 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
538                 if(confbasegiven)
539                         return false;
540
541                 // Generate a random netname, ask for a better one later.
542                 ask_netname = true;
543                 snprintf(temp_netname, sizeof temp_netname, "join_%x", rand());
544                 netname = temp_netname;
545                 goto make_names;
546         }       
547
548         if(mkdir(confbase, 0777) && errno != EEXIST) {
549                 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
550                 return false;
551         }
552
553         if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
554                 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
555                 return false;
556         }
557
558         FILE *f = fopen(tinc_conf, "w");
559         if(!f) {
560                 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
561                 return false;
562         }
563
564         fprintf(f, "Name = %s\n", name);
565
566         char filename[PATH_MAX];
567         snprintf(filename,PATH_MAX, "%s" SLASH "%s", hosts_dir, name);
568         FILE *fh = fopen(filename, "w");
569         if(!fh) {
570                 fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
571                 return false;
572         }
573
574         // Filter first chunk on approved keywords, split between tinc.conf and hosts/Name
575         // Other chunks go unfiltered to their respective host config files
576         const char *p = data;
577         char *l, *value;
578
579         while((l = get_line(&p))) {
580                 // Ignore comments
581                 if(*l == '#')
582                         continue;
583
584                 // Split line into variable and value
585                 int len = strcspn(l, "\t =");
586                 value = l + len;
587                 value += strspn(value, "\t ");
588                 if(*value == '=') {
589                         value++;
590                         value += strspn(value, "\t ");
591                 }
592                 l[len] = 0;
593
594                 // Is it a Name?
595                 if(!strcasecmp(l, "Name"))
596                         if(strcmp(value, name))
597                                 break;
598                         else
599                                 continue;
600                 else if(!strcasecmp(l, "NetName"))
601                         continue;
602
603                 // Check the list of known variables
604                 bool found = false;
605                 int i;
606                 for(i = 0; variables[i].name; i++) {
607                         if(strcasecmp(l, variables[i].name))
608                                 continue;
609                         found = true;
610                         break;
611                 }
612
613                 // Ignore unknown and unsafe variables
614                 if(!found) {
615                         fprintf(stderr, "Ignoring unknown variable '%s' in invitation.\n", l);
616                         continue;
617                 } else if(!(variables[i].type & VAR_SAFE)) {
618                         fprintf(stderr, "Ignoring unsafe variable '%s' in invitation.\n", l);
619                         continue;
620                 }
621
622                 // Copy the safe variable to the right config file
623                 fprintf(variables[i].type & VAR_HOST ? fh : f, "%s = %s\n", l, value);
624         }
625
626         fclose(f);
627
628         while(l && !strcasecmp(l, "Name")) {
629                 if(!check_id(value)) {
630                         fprintf(stderr, "Invalid Name found in invitation.\n");
631                         return false;
632                 }
633
634                 if(!strcmp(value, name)) {
635                         fprintf(stderr, "Secondary chunk would overwrite our own host config file.\n");
636                         return false;
637                 }
638
639                 snprintf(filename,PATH_MAX, "%s" SLASH "%s", hosts_dir, value);
640                 f = fopen(filename, "w");
641
642                 if(!f) {
643                         fprintf(stderr, "Could not create file %s: %s\n", filename, strerror(errno));
644                         return false;
645                 }
646
647                 while((l = get_line(&p))) {
648                         if(!strcmp(l, "#---------------------------------------------------------------#"))
649                                 continue;
650                         int len = strcspn(l, "\t =");
651                         if(len == 4 && !strncasecmp(l, "Name", 4)) {
652                                 value = l + len;
653                                 value += strspn(value, "\t ");
654                                 if(*value == '=') {
655                                         value++;
656                                         value += strspn(value, "\t ");
657                                 }
658                                 l[len] = 0;
659                                 break;
660                         }
661
662                         fputs(l, f);
663                         fputc('\n', f);
664                 }
665
666                 fclose(f);
667         }
668
669         // Generate our key and send a copy to the server
670         ecdsa_t *key = ecdsa_generate();
671         if(!key)
672                 return false;
673
674         char *b64key = ecdsa_get_base64_public_key(key);
675         if(!b64key)
676                 return false;
677
678         snprintf(filename,PATH_MAX, "%s" SLASH "ecdsa_key.priv", confbase);
679         f = fopenmask(filename, "w", 0600);
680
681         if(!ecdsa_write_pem_private_key(key, f)) {
682                 fprintf(stderr, "Error writing private key!\n");
683                 ecdsa_free(key);
684                 fclose(f);
685                 return false;
686         }
687
688         fclose(f);
689
690         fprintf(fh, "ECDSAPublicKey = %s\n", b64key);
691
692         sptps_send_record(&sptps, 1, b64key, strlen(b64key));
693         free(b64key);
694
695         ecdsa_free(key);
696
697         check_port(name);
698
699 ask_netname:
700         if(ask_netname && tty) {
701                 fprintf(stderr, "Enter a new netname: ");
702                 if(!fgets(line, sizeof line, stdin)) {
703                         fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
704                         return false;
705                 }
706                 if(!*line || *line == '\n')
707                         goto ask_netname;
708
709                 line[strlen(line) - 1] = 0;
710
711                 char *newbase;
712                 xasprintf(&newbase, CONFDIR SLASH "tinc" SLASH "%s", line);
713                 if(rename(confbase, newbase)) {
714                         fprintf(stderr, "Error trying to rename %s to %s: %s\n", confbase, newbase, strerror(errno));
715                         free(newbase);
716                         goto ask_netname;
717                 }
718
719                 free(newbase);
720                 netname = line;
721         }
722
723         fprintf(stderr, "Configuration stored in: %s\n", confbase);
724
725         return true;
726 }
727
728
729 static bool invitation_send(void *handle, uint8_t type, const char *data, size_t len) {
730         while(len) {
731                 int result = send(sock, data, len, 0);
732                 if(result == -1 && errno == EINTR)
733                         continue;
734                 else if(result <= 0)
735                         return false;
736                 data += result;
737                 len -= result;
738         }
739         return true;
740 }
741
742 static bool invitation_receive(void *handle, uint8_t type, const char *msg, uint16_t len) {
743         switch(type) {
744                 case SPTPS_HANDSHAKE:
745                         return sptps_send_record(&sptps, 0, cookie, sizeof cookie);
746
747                 case 0:
748                         data = xrealloc(data, datalen + len + 1);
749                         memcpy(data + datalen, msg, len);
750                         datalen += len;
751                         data[datalen] = 0;
752                         break;
753
754                 case 1:
755                         return finalize_join();
756
757                 case 2:
758                         fprintf(stderr, "Invitation succesfully accepted.\n");
759                         shutdown(sock, SHUT_RDWR);
760                         success = true;
761                         break;
762
763                 default:
764                         return false;
765         }
766
767         return true;
768 }
769
770 int cmd_join(int argc, char *argv[]) {
771         free(data);
772         data = NULL;
773         datalen = 0;
774
775         if(argc > 2) {
776                 fprintf(stderr, "Too many arguments!\n");
777                 return 1;
778         }
779
780         // Make sure confbase exists and is accessible.
781         if(mkdir(confbase, 0777) && errno != EEXIST) {
782                 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
783                 return 1;
784         }
785
786         if(access(confbase, R_OK | W_OK | X_OK)) {
787                 fprintf(stderr, "No permission to write in directory %s: %s\n", confbase, strerror(errno));
788                 return 1;
789         }
790
791         // If a netname or explicit configuration directory is specified, check for an existing tinc.conf.
792         if((netname || confbasegiven) && !access(tinc_conf, F_OK)) {
793                 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
794                 return 1;
795         }
796
797         // Either read the invitation from the command line or from stdin.
798         char *invitation;
799
800         if(argc > 1) {
801                 invitation = argv[1];
802         } else {
803                 if(tty)
804                         fprintf(stderr, "Enter invitation URL: ");
805                 errno = EPIPE;
806                 if(!fgets(line, sizeof line, stdin)) {
807                         fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
808                         return false;
809                 }
810                 invitation = line;
811         }
812
813         // Parse the invitation URL.
814         rstrip(line);
815
816         char *slash = strchr(invitation, '/');
817         if(!slash)
818                 goto invalid;
819
820         *slash++ = 0;
821
822         if(strlen(slash) != 48)
823                 goto invalid;
824
825         char *address = invitation;
826         char *port = NULL;
827         if(*address == '[') {
828                 address++;
829                 char *bracket = strchr(address, ']');
830                 if(!bracket)
831                         goto invalid;
832                 *bracket = 0;
833                 if(bracket[1] == ':')
834                         port = bracket + 2;
835         } else {
836                 port = strchr(address, ':');
837                 if(port)
838                         *port++ = 0;
839         }
840
841         if(!port || !*port)
842                 port = "655";
843
844         if(!b64decode(slash, hash, 18) || !b64decode(slash + 24, cookie, 18))
845                 goto invalid;
846
847         // Generate a throw-away key for the invitation.
848         ecdsa_t *key = ecdsa_generate();
849         if(!key)
850                 return 1;
851
852         char *b64key = ecdsa_get_base64_public_key(key);
853
854         // Connect to the tinc daemon mentioned in the URL.
855         struct addrinfo *ai = str2addrinfo(address, port, SOCK_STREAM);
856         if(!ai)
857                 return 1;
858
859         sock = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
860         if(sock <= 0) {
861                 fprintf(stderr, "Could not open socket: %s\n", strerror(errno));
862                 return 1;
863         }
864
865         if(connect(sock, ai->ai_addr, ai->ai_addrlen)) {
866                 fprintf(stderr, "Could not connect to %s port %s: %s\n", address, port, strerror(errno));
867                 closesocket(sock);
868                 return 1;
869         }
870
871         fprintf(stderr, "Connected to %s port %s...\n", address, port);
872
873         // Tell him we have an invitation, and give him our throw-away key.
874         int len = snprintf(line, sizeof line, "0 ?%s %d.%d\n", b64key, PROT_MAJOR, PROT_MINOR);
875         if(len <= 0 || len >= sizeof line)
876                 abort();
877
878         if(!sendline(sock, "0 ?%s %d.%d", b64key, PROT_MAJOR, 1)) {
879                 fprintf(stderr, "Error sending request to %s port %s: %s\n", address, port, strerror(errno));
880                 closesocket(sock);
881                 return 1;
882         }
883
884         char hisname[4096] = "";
885         int code, hismajor, hisminor = 0;
886
887         if(!recvline(sock, line, sizeof line) || sscanf(line, "%d %s %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) {
888                 fprintf(stderr, "Cannot read greeting from peer\n");
889                 closesocket(sock);
890                 return 1;
891         }
892
893         // Check if the hash of the key he gave us matches the hash in the URL.
894         char *fingerprint = line + 2;
895         char hishash[64];
896         if(!sha512(fingerprint, strlen(fingerprint), hishash)) {
897                 fprintf(stderr, "Could not create hash\n%s\n", line + 2);
898                 return 1;
899         }
900         if(memcmp(hishash, hash, 18)) {
901                 fprintf(stderr, "Peer has an invalid key!\n%s\n", line + 2);
902                 return 1;
903
904         }
905         
906         ecdsa_t *hiskey = ecdsa_set_base64_public_key(fingerprint);
907         if(!hiskey)
908                 return 1;
909
910         // Start an SPTPS session
911         if(!sptps_start(&sptps, NULL, true, false, key, hiskey, "tinc invitation", 15, invitation_send, invitation_receive))
912                 return 1;
913
914         // Feed rest of input buffer to SPTPS
915         if(!sptps_receive_data(&sptps, buffer, blen))
916                 return 1;
917
918         while((len = recv(sock, line, sizeof line, 0))) {
919                 if(len < 0) {
920                         if(errno == EINTR)
921                                 continue;
922                         fprintf(stderr, "Error reading data from %s port %s: %s\n", address, port, strerror(errno));
923                         return 1;
924                 }
925
926                 if(!sptps_receive_data(&sptps, line, len))
927                         return 1;
928         }
929         
930         sptps_stop(&sptps);
931         ecdsa_free(hiskey);
932         ecdsa_free(key);
933         closesocket(sock);
934
935         if(!success) {
936                 fprintf(stderr, "Connection closed by peer, invitation cancelled.\n");
937                 return 1;
938         }
939
940         return 0;
941
942 invalid:
943         fprintf(stderr, "Invalid invitation URL.\n");
944         return 1;
945 }