]> git.meshlink.io Git - meshlink/blob - src/libmeshlink.c
Remove support for Subnets.
[meshlink] / src / libmeshlink.c
1 /*
2     libmeshlink.h -- Tincd Library
3     Copyright (C) 2014 Guus Sliepen <guus@tinc-vpn.org> Saverio Proto <zioproto@gmail.com>
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 "libmeshlink.h"
21 #include LZO1X_H
22 #ifdef HAVE_SYS_MMAN_H
23 #include <sys/mman.h>
24 #endif
25 #include "crypto.h"
26 #include "ecdsagen.h"
27 char *hosts_dir = NULL;
28 static char *name = NULL;
29 char *tinc_conf = NULL;
30 static bool tty = false;
31
32 #ifdef HAVE_MLOCKALL
33 /* If nonzero, disable swapping for this process. */
34 static bool do_mlock = false;
35 #endif
36
37 /*
38   initialize network
39 */
40 bool setup_meshlink_network(void) {
41         init_connections();
42         init_nodes();
43         init_edges();
44         init_requests();
45
46         if(get_config_int(lookup_config(config_tree, "PingInterval"), &pinginterval)) {
47                 if(pinginterval < 1) {
48                         pinginterval = 86400;
49                 }
50         } else
51                 pinginterval = 60;
52
53         if(!get_config_int(lookup_config(config_tree, "PingTimeout"), &pingtimeout))
54                 pingtimeout = 5;
55         if(pingtimeout < 1 || pingtimeout > pinginterval)
56                 pingtimeout = pinginterval;
57
58         //TODO: check if this makes sense in libmeshlink
59         if(!get_config_int(lookup_config(config_tree, "MaxOutputBufferSize"), &maxoutbufsize))
60                 maxoutbufsize = 10 * MTU;
61
62         if(!setup_myself())
63                 return false;
64
65         if(!init_control())
66                 return false;
67
68         return true;
69 }
70
71 /* Open a file with the desired permissions, minus the umask.
72    Also, if we want to create an executable file, we call fchmod()
73    to set the executable bits. */
74
75 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
76         mode_t mask = umask(0);
77         perms &= ~mask;
78         umask(~perms);
79         FILE *f = fopen(filename, mode);
80 #ifdef HAVE_FCHMOD
81         if((perms & 0444) && f)
82                 fchmod(fileno(f), perms);
83 #endif
84         umask(mask);
85         return f;
86 }
87
88 static void disable_old_keys(const char *filename, const char *what) {
89         char tmpfile[PATH_MAX] = "";
90         char buf[1024];
91         bool disabled = false;
92         bool block = false;
93         bool error = false;
94         FILE *r, *w;
95
96         r = fopen(filename, "r");
97         if(!r)
98                 return;
99
100         snprintf(tmpfile, sizeof tmpfile, "%s.tmp", filename);
101
102         struct stat st = {.st_mode = 0600};
103         fstat(fileno(r), &st);
104         w = fopenmask(tmpfile, "w", st.st_mode);
105
106         while(fgets(buf, sizeof buf, r)) {
107                 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
108                         if((strstr(buf, " EC ") && strstr(what, "ECDSA")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
109                                 disabled = true;
110                                 block = true;
111                         }
112                 }
113
114                 bool ecdsapubkey = !strncasecmp(buf, "ECDSAPublicKey", 14) && strchr(" \t=", buf[14]) && strstr(what, "ECDSA");
115
116                 if(ecdsapubkey)
117                         disabled = true;
118
119                 if(w) {
120                         if(block || ecdsapubkey)
121                                 fputc('#', w);
122                         if(fputs(buf, w) < 0) {
123                                 error = true;
124                                 break;
125                         }
126                 }
127
128                 if(block && !strncmp(buf, "-----END ", 9))
129                         block = false;
130         }
131
132         if(w)
133                 if(fclose(w) < 0)
134                         error = true;
135         if(ferror(r) || fclose(r) < 0)
136                 error = true;
137
138         if(disabled) {
139                 if(!w || error) {
140                         fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
141                         if(w)
142                                 unlink(tmpfile);
143                         return;
144                 }
145
146 #ifdef HAVE_MINGW
147                 // We cannot atomically replace files on Windows.
148                 char bakfile[PATH_MAX] = "";
149                 snprintf(bakfile, sizeof bakfile, "%s.bak", filename);
150                 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
151                         rename(bakfile, filename);
152 #else
153                 if(rename(tmpfile, filename)) {
154 #endif
155                         fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
156                 } else  {
157 #ifdef HAVE_MINGW
158                         unlink(bakfile);
159 #endif
160                         fprintf(stderr, "Warning: old key(s) found and disabled.\n");
161                 }
162         }
163
164         unlink(tmpfile);
165 }
166
167 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
168         FILE *r;
169         char *directory;
170         char buf[PATH_MAX];
171         char buf2[PATH_MAX];
172
173         /* Check stdin and stdout */
174         if(ask && tty) {
175                 /* Ask for a file and/or directory name. */
176                 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
177
178                 if(fgets(buf, sizeof buf, stdin) == NULL) {
179                         fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
180                         return NULL;
181                 }
182
183                 size_t len = strlen(buf);
184                 if(len)
185                         buf[--len] = 0;
186
187                 if(len)
188                         filename = buf;
189         }
190
191 #ifdef HAVE_MINGW
192         if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
193 #else
194         if(filename[0] != '/') {
195 #endif
196                 /* The directory is a relative path or a filename. */
197                 directory = get_current_dir_name();
198                 snprintf(buf2, sizeof buf2, "%s" SLASH "%s", directory, filename);
199                 filename = buf2;
200         }
201
202         disable_old_keys(filename, what);
203
204         /* Open it first to keep the inode busy */
205
206         r = fopenmask(filename, mode, perms);
207
208         if(!r) {
209                 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
210                 return NULL;
211         }
212
213         return r;
214 }
215
216 /*
217   Generate a public/private ECDSA keypair, and ask for a file to store
218   them in.
219 */
220 bool ecdsa_keygen(bool ask) {
221         ecdsa_t *key;
222         FILE *f;
223         char *pubname, *privname;
224
225         fprintf(stderr, "Generating ECDSA keypair:\n");
226
227         if(!(key = ecdsa_generate())) {
228                 fprintf(stderr, "Error during key generation!\n");
229                 return false;
230         } else
231                 fprintf(stderr, "Done.\n");
232
233         xasprintf(&privname, "%s" SLASH "ecdsa_key.priv", confbase);
234         f = ask_and_open(privname, "private ECDSA key", "a", ask, 0600);
235         free(privname);
236
237         if(!f)
238                 return false;
239
240         if(!ecdsa_write_pem_private_key(key, f)) {
241                 fprintf(stderr, "Error writing private key!\n");
242                 ecdsa_free(key);
243                 fclose(f);
244                 return false;
245         }
246
247         fclose(f);
248
249         if(name)
250                 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
251         else
252                 xasprintf(&pubname, "%s" SLASH "ecdsa_key.pub", confbase);
253
254         f = ask_and_open(pubname, "public ECDSA key", "a", ask, 0666);
255         free(pubname);
256
257         if(!f)
258                 return false;
259
260         char *pubkey = ecdsa_get_base64_public_key(key);
261         fprintf(f, "ECDSAPublicKey = %s\n", pubkey);
262         free(pubkey);
263
264         fclose(f);
265         ecdsa_free(key);
266
267         return true;
268 }
269
270 /*
271   Generate a public/private RSA keypair, and ask for a file to store
272   them in.
273 */
274 bool rsa_keygen(int bits, bool ask) {
275         rsa_t *key;
276         FILE *f;
277         char *pubname, *privname;
278
279         fprintf(stderr, "Generating %d bits keys:\n", bits);
280
281         if(!(key = rsa_generate(bits, 0x10001))) {
282                 fprintf(stderr, "Error during key generation!\n");
283                 return false;
284         } else
285                 fprintf(stderr, "Done.\n");
286
287         xasprintf(&privname, "%s" SLASH "rsa_key.priv", confbase);
288         f = ask_and_open(privname, "private RSA key", "a", ask, 0600);
289         free(privname);
290
291         if(!f)
292                 return false;
293
294         if(!rsa_write_pem_private_key(key, f)) {
295                 fprintf(stderr, "Error writing private key!\n");
296                 fclose(f);
297                 rsa_free(key);
298                 return false;
299         }
300
301         fclose(f);
302
303         if(name)
304                 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
305         else
306                 xasprintf(&pubname, "%s" SLASH "rsa_key.pub", confbase);
307
308         f = ask_and_open(pubname, "public RSA key", "a", ask, 0666);
309         free(pubname);
310
311         if(!f)
312                 return false;
313
314         if(!rsa_write_pem_public_key(key, f)) {
315                 fprintf(stderr, "Error writing public key!\n");
316                 fclose(f);
317                 rsa_free(key);
318                 return false;
319         }
320
321         fclose(f);
322         rsa_free(key);
323
324         return true;
325 }
326
327 static bool try_bind(int port) {
328         struct addrinfo *ai = NULL;
329         struct addrinfo hint = {
330                 .ai_flags = AI_PASSIVE,
331                 .ai_family = AF_UNSPEC,
332                 .ai_socktype = SOCK_STREAM,
333                 .ai_protocol = IPPROTO_TCP,
334         };
335
336         char portstr[16];
337         snprintf(portstr, sizeof portstr, "%d", port);
338
339         if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
340                 return false;
341
342         while(ai) {
343                 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
344                 if(!fd)
345                         return false;
346                 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
347                 closesocket(fd);
348                 if(result)
349                         return false;
350                 ai = ai->ai_next;
351         }
352
353         return true;
354 }
355
356 int check_port(char *name) {
357         if(try_bind(655))
358                 return 655;
359
360         fprintf(stderr, "Warning: could not bind to port 655. ");
361
362         for(int i = 0; i < 100; i++) {
363                 int port = 0x1000 + (rand() & 0x7fff);
364                 if(try_bind(port)) {
365                         char *filename;
366                         xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
367                         FILE *f = fopen(filename, "a");
368                         free(filename);
369                         if(!f) {
370                                 fprintf(stderr, "Please change tinc's Port manually.\n");
371                                 return 0;
372                         }
373
374                         fprintf(f, "Port = %d\n", port);
375                         fclose(f);
376                         fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
377                         return port;
378                 }
379         }
380
381         fprintf(stderr, "Please change tinc's Port manually.\n");
382         return 0;
383 }
384 //tinc_setup() should basically do what cmd_init() from src/tincctl.c does, except it doesn't have to generate a tinc-up script.
385 bool tinc_setup(const char* confbaseapi, const char* name) {
386         confbase = confbaseapi;
387         make_names();
388         xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
389         xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
390         if(!access(tinc_conf, F_OK)) {
391                 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
392                 return false;
393         }
394
395         if(!check_id(name)) {
396                 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
397                 return false;
398         }
399
400         if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
401                 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
402                 return false;
403         }
404
405         if(mkdir(confbase, 0777) && errno != EEXIST) {
406                 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
407                 return false;
408         }
409
410         if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
411                 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
412                 return false;
413         }
414
415         FILE *f = fopen(tinc_conf, "w");
416         if(!f) {
417                 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
418                 return 1;
419         }
420
421         fprintf(f, "Name = %s\n", name);
422         fclose(f);
423
424         if(!rsa_keygen(2048, false) || !ecdsa_keygen(false))
425                 return false;
426
427         check_port(name);
428
429         return true;
430
431 }
432
433
434 bool tinc_start(const char* confbaseapi) {
435         pthread_t tincThread;
436         confbase = confbaseapi;
437         pthread_create(&tincThread,NULL,tinc_main_thread,confbaseapi);
438         pthread_detach(tincThread);
439 return true;
440 }
441
442 bool tinc_main_thread(void * in) {
443
444 static bool status = false;
445
446 /* If nonzero, write log entries to a separate file. */
447 bool use_logfile = false;
448
449 confbase = (char*) in;
450
451         openlogger("tinc", use_logfile?LOGMODE_FILE:LOGMODE_STDERR);
452
453         init_configuration(&config_tree);
454
455         /* Slllluuuuuuurrrrp! */
456
457         gettimeofday(&now, NULL);
458         srand(now.tv_sec + now.tv_usec);
459         crypto_init();
460
461         if(!read_server_config())
462                 return false;
463
464 #ifdef HAVE_LZO
465         if(lzo_init() != LZO_E_OK) {
466                 logger(DEBUG_ALWAYS, LOG_ERR, "Error initializing LZO compressor!");
467                 return false;
468         }
469 #endif
470
471         //char *priority = NULL; //shoud be not needed in libmeshlink
472
473 #ifdef HAVE_MLOCKALL
474         /* Lock all pages into memory if requested.
475          * This has to be done after daemon()/fork() so it works for child.
476          * No need to do that in parent as it's very short-lived. */
477         if(do_mlock && mlockall(MCL_CURRENT | MCL_FUTURE) != 0) {
478                 logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "mlockall",
479                    strerror(errno));
480                 return 1;
481         }
482 #endif
483
484         /* Setup sockets and open device. */
485
486         if(!setup_meshlink_network())
487                 goto end;
488
489         /* Change process priority */
490         //should be not needed in libmeshlink
491         //if(get_config_string(lookup_config(config_tree, "ProcessPriority"), &priority)) {
492         //      if(!strcasecmp(priority, "Normal")) {
493         //              if (setpriority(NORMAL_PRIORITY_CLASS) != 0) {
494         //                      logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "setpriority", strerror(errno));
495         //                      goto end;
496         //              }
497         //      } else if(!strcasecmp(priority, "Low")) {
498         //              if (setpriority(BELOW_NORMAL_PRIORITY_CLASS) != 0) {
499         //                             logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "setpriority", strerror(errno));
500         //                      goto end;
501         //              }
502         //      } else if(!strcasecmp(priority, "High")) {
503         //              if (setpriority(HIGH_PRIORITY_CLASS) != 0) {
504         //                      logger(DEBUG_ALWAYS, LOG_ERR, "System call `%s' failed: %s", "setpriority", strerror(errno));
505         //                      goto end;
506         //              }
507         //      } else {
508         //              logger(DEBUG_ALWAYS, LOG_ERR, "Invalid priority `%s`!", priority);
509         //              goto end;
510         //      }
511         //}
512
513         /* drop privileges */
514         //if (!drop_privs())
515         //      goto end;
516
517         /* Start main loop. It only exits when tinc is killed. */
518
519         logger(DEBUG_ALWAYS, LOG_NOTICE, "Ready");
520
521         try_outgoing_connections();
522
523         status = main_loop();
524
525         /* Shutdown properly. */
526
527 end:
528         close_network_connections();
529
530         logger(DEBUG_ALWAYS, LOG_NOTICE, "Terminating");
531
532         //free(priority);
533
534         crypto_exit();
535
536         exit_configuration(&config_tree);
537         free(cmdline_conf);
538         free_names();
539
540         return status;
541
542 }
543
544 bool tinc_stop();
545
546 bool route_meshlink(node_t *source,vpn_packet_t *packet) {
547
548         printf("data %s\n",packet->data);
549         printf("data 11%s\n",packet->data+11);
550         printf("data 32%s\n",packet->data+32);
551         node_t* owner = NULL;
552
553         tincpackethdr* hdr = (tincpackethdr*)packet->data;
554         owner = lookup_node(hdr->destination);
555
556         if (owner == NULL) {
557         //Lookup failed
558         printf("NULL\n");
559         return false;
560         }
561         printf("lookupnode %s\n",owner->name);
562
563         if(!owner->status.reachable) {
564         //Do some here
565         return false;
566         }
567
568         //TODO: I skipped here a lot of checks !
569
570         send_packet(owner,packet);
571
572 }
573 // can be called from any thread
574 bool tinc_send_packet(node_t *receiver, const char* buf, unsigned int len) {
575
576         vpn_packet_t packet;
577         tincpackethdr* hdr = malloc(sizeof(tincpackethdr));
578
579         if (sizeof(hdr) + len > MAXSIZE) {
580
581         //log something
582         return false;
583         }
584
585         memcpy(hdr->destination,receiver->name,sizeof(hdr->destination));
586         memcpy(hdr->source,myself->name,sizeof(hdr->source));
587
588         packet.priority = 0;
589
590         memcpy(packet.data,hdr,32);
591         memcpy(packet.data+32,buf,len);
592
593         myself->in_packets++;
594         myself->in_bytes += packet.len;
595         route_meshlink(myself, &packet);
596
597 return true;
598 }
599
600 // handler runs in tinc thread and should return immediately
601 bool tinc_set_packet_receive_handler(void (*handler)(const char* sender, const char* buf, unsigned int len));
602
603
604 //It might also be a good idea to add the option of looking up hosts by public
605 //key (fingerprints) instead of names.
606
607 node_t *tinc_get_host(const char *name) {
608
609
610
611 };
612
613 bool tinc_get_hosts(node_t** hosts);
614
615 bool tinc_sign(const char* payload, unsigned int len, const char** signature);
616
617 int tinc_verify(const char* sender, const char* payload, unsigned int plen, const char* signature, unsigned int slen);
618
619 /*
620 TODO: It would be good to add a void pointer here that will be passed on to the
621 handler function whenever it is called, or have a void pointer in node_t
622 that can be filled in by the application. That way, you can easily link an
623 application-specific data structure to a node_t.
624 */
625 void channel_set_packet_send_handler(int (*handler)(const char* receiver, const char* buf, unsigned int len));
626 void channel_packet_receive_handler(const char* sender, const char* buf, unsigned int len);
627
628 bool channel_open(const char* partner, void(*read)(int id, const char* buf, unsigned int len), void(*result)(int result, int id));
629 void channel_close(int id);
630 bool channel_write(int id, const char* buf, unsigned int len, void(*result)(int result, int id, unsigned int written));
631
632
633 //We do need some more functions. First of all, we need to be able to add nodes
634 //to a VPN. To do that, either an invitation protocol should be used:
635
636 bool tinc_join_network(const char *invitation);
637 const char *tinc_generate_invitation(const char *name);
638
639 /*
640 Or two nodes should exchange some information (their name, address, public
641 key). If the application provides a way to exchange some data with another
642 node, then:
643 */
644
645 bool tinc_export(char *buf, size_t *len);
646 node_t *tinc_import(const char *buf, size_t len);
647 /*
648 Last but not least, some way to get rid of unwanted nodes. Simplest is a
649 function that just blacklists a node.
650 Which should immediately cause the local tincd to ignore any data from that
651 host from that point on. Of course, a somewhat centrally managed,
652 automatically distributed blacklist or whitelist would be the next step.
653 */
654 bool tinc_blacklist(node_t *host);
655
656
657
658