]> git.meshlink.io Git - meshlink/blob - src/libmeshlink.c
tinc_start() - skeleton of the API call. The function starts the main tinc thread...
[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 "crypto.h"
22 #include "ecdsagen.h"
23 char *hosts_dir = NULL;
24 static char *name = NULL;
25 char *tinc_conf = NULL;
26 static bool tty = false;
27
28 /* Open a file with the desired permissions, minus the umask.
29    Also, if we want to create an executable file, we call fchmod()
30    to set the executable bits. */
31
32 FILE *fopenmask(const char *filename, const char *mode, mode_t perms) {
33         mode_t mask = umask(0);
34         perms &= ~mask;
35         umask(~perms);
36         FILE *f = fopen(filename, mode);
37 #ifdef HAVE_FCHMOD
38         if((perms & 0444) && f)
39                 fchmod(fileno(f), perms);
40 #endif
41         umask(mask);
42         return f;
43 }
44
45 static void disable_old_keys(const char *filename, const char *what) {
46         char tmpfile[PATH_MAX] = "";
47         char buf[1024];
48         bool disabled = false;
49         bool block = false;
50         bool error = false;
51         FILE *r, *w;
52
53         r = fopen(filename, "r");
54         if(!r)
55                 return;
56
57         snprintf(tmpfile, sizeof tmpfile, "%s.tmp", filename);
58
59         struct stat st = {.st_mode = 0600};
60         fstat(fileno(r), &st);
61         w = fopenmask(tmpfile, "w", st.st_mode);
62
63         while(fgets(buf, sizeof buf, r)) {
64                 if(!block && !strncmp(buf, "-----BEGIN ", 11)) {
65                         if((strstr(buf, " EC ") && strstr(what, "ECDSA")) || (strstr(buf, " RSA ") && strstr(what, "RSA"))) {
66                                 disabled = true;
67                                 block = true;
68                         }
69                 }
70
71                 bool ecdsapubkey = !strncasecmp(buf, "ECDSAPublicKey", 14) && strchr(" \t=", buf[14]) && strstr(what, "ECDSA");
72
73                 if(ecdsapubkey)
74                         disabled = true;
75
76                 if(w) {
77                         if(block || ecdsapubkey)
78                                 fputc('#', w);
79                         if(fputs(buf, w) < 0) {
80                                 error = true;
81                                 break;
82                         }
83                 }
84
85                 if(block && !strncmp(buf, "-----END ", 9))
86                         block = false;
87         }
88
89         if(w)
90                 if(fclose(w) < 0)
91                         error = true;
92         if(ferror(r) || fclose(r) < 0)
93                 error = true;
94
95         if(disabled) {
96                 if(!w || error) {
97                         fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
98                         if(w)
99                                 unlink(tmpfile);
100                         return;
101                 }
102
103 #ifdef HAVE_MINGW
104                 // We cannot atomically replace files on Windows.
105                 char bakfile[PATH_MAX] = "";
106                 snprintf(bakfile, sizeof bakfile, "%s.bak", filename);
107                 if(rename(filename, bakfile) || rename(tmpfile, filename)) {
108                         rename(bakfile, filename);
109 #else
110                 if(rename(tmpfile, filename)) {
111 #endif
112                         fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
113                 } else  {
114 #ifdef HAVE_MINGW
115                         unlink(bakfile);
116 #endif
117                         fprintf(stderr, "Warning: old key(s) found and disabled.\n");
118                 }
119         }
120
121         unlink(tmpfile);
122 }
123
124 static FILE *ask_and_open(const char *filename, const char *what, const char *mode, bool ask, mode_t perms) {
125         FILE *r;
126         char *directory;
127         char buf[PATH_MAX];
128         char buf2[PATH_MAX];
129
130         /* Check stdin and stdout */
131         if(ask && tty) {
132                 /* Ask for a file and/or directory name. */
133                 fprintf(stderr, "Please enter a file to save %s to [%s]: ", what, filename);
134
135                 if(fgets(buf, sizeof buf, stdin) == NULL) {
136                         fprintf(stderr, "Error while reading stdin: %s\n", strerror(errno));
137                         return NULL;
138                 }
139
140                 size_t len = strlen(buf);
141                 if(len)
142                         buf[--len] = 0;
143
144                 if(len)
145                         filename = buf;
146         }
147
148 #ifdef HAVE_MINGW
149         if(filename[0] != '\\' && filename[0] != '/' && !strchr(filename, ':')) {
150 #else
151         if(filename[0] != '/') {
152 #endif
153                 /* The directory is a relative path or a filename. */
154                 directory = get_current_dir_name();
155                 snprintf(buf2, sizeof buf2, "%s" SLASH "%s", directory, filename);
156                 filename = buf2;
157         }
158
159         disable_old_keys(filename, what);
160
161         /* Open it first to keep the inode busy */
162
163         r = fopenmask(filename, mode, perms);
164
165         if(!r) {
166                 fprintf(stderr, "Error opening file `%s': %s\n", filename, strerror(errno));
167                 return NULL;
168         }
169
170         return r;
171 }
172
173 /*
174   Generate a public/private ECDSA keypair, and ask for a file to store
175   them in.
176 */
177 bool ecdsa_keygen(bool ask) {
178         ecdsa_t *key;
179         FILE *f;
180         char *pubname, *privname;
181
182         fprintf(stderr, "Generating ECDSA keypair:\n");
183
184         if(!(key = ecdsa_generate())) {
185                 fprintf(stderr, "Error during key generation!\n");
186                 return false;
187         } else
188                 fprintf(stderr, "Done.\n");
189
190         xasprintf(&privname, "%s" SLASH "ecdsa_key.priv", confbase);
191         f = ask_and_open(privname, "private ECDSA key", "a", ask, 0600);
192         free(privname);
193
194         if(!f)
195                 return false;
196
197         if(!ecdsa_write_pem_private_key(key, f)) {
198                 fprintf(stderr, "Error writing private key!\n");
199                 ecdsa_free(key);
200                 fclose(f);
201                 return false;
202         }
203
204         fclose(f);
205
206         if(name)
207                 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
208         else
209                 xasprintf(&pubname, "%s" SLASH "ecdsa_key.pub", confbase);
210
211         f = ask_and_open(pubname, "public ECDSA key", "a", ask, 0666);
212         free(pubname);
213
214         if(!f)
215                 return false;
216
217         char *pubkey = ecdsa_get_base64_public_key(key);
218         fprintf(f, "ECDSAPublicKey = %s\n", pubkey);
219         free(pubkey);
220
221         fclose(f);
222         ecdsa_free(key);
223
224         return true;
225 }
226
227 /*
228   Generate a public/private RSA keypair, and ask for a file to store
229   them in.
230 */
231 bool rsa_keygen(int bits, bool ask) {
232         rsa_t *key;
233         FILE *f;
234         char *pubname, *privname;
235
236         fprintf(stderr, "Generating %d bits keys:\n", bits);
237
238         if(!(key = rsa_generate(bits, 0x10001))) {
239                 fprintf(stderr, "Error during key generation!\n");
240                 return false;
241         } else
242                 fprintf(stderr, "Done.\n");
243
244         xasprintf(&privname, "%s" SLASH "rsa_key.priv", confbase);
245         f = ask_and_open(privname, "private RSA key", "a", ask, 0600);
246         free(privname);
247
248         if(!f)
249                 return false;
250
251         if(!rsa_write_pem_private_key(key, f)) {
252                 fprintf(stderr, "Error writing private key!\n");
253                 fclose(f);
254                 rsa_free(key);
255                 return false;
256         }
257
258         fclose(f);
259
260         if(name)
261                 xasprintf(&pubname, "%s" SLASH "hosts" SLASH "%s", confbase, name);
262         else
263                 xasprintf(&pubname, "%s" SLASH "rsa_key.pub", confbase);
264
265         f = ask_and_open(pubname, "public RSA key", "a", ask, 0666);
266         free(pubname);
267
268         if(!f)
269                 return false;
270
271         if(!rsa_write_pem_public_key(key, f)) {
272                 fprintf(stderr, "Error writing public key!\n");
273                 fclose(f);
274                 rsa_free(key);
275                 return false;
276         }
277
278         fclose(f);
279         rsa_free(key);
280
281         return true;
282 }
283
284 static bool try_bind(int port) {
285         struct addrinfo *ai = NULL;
286         struct addrinfo hint = {
287                 .ai_flags = AI_PASSIVE,
288                 .ai_family = AF_UNSPEC,
289                 .ai_socktype = SOCK_STREAM,
290                 .ai_protocol = IPPROTO_TCP,
291         };
292
293         char portstr[16];
294         snprintf(portstr, sizeof portstr, "%d", port);
295
296         if(getaddrinfo(NULL, portstr, &hint, &ai) || !ai)
297                 return false;
298
299         while(ai) {
300                 int fd = socket(ai->ai_family, SOCK_STREAM, IPPROTO_TCP);
301                 if(!fd)
302                         return false;
303                 int result = bind(fd, ai->ai_addr, ai->ai_addrlen);
304                 closesocket(fd);
305                 if(result)
306                         return false;
307                 ai = ai->ai_next;
308         }
309
310         return true;
311 }
312
313 int check_port(char *name) {
314         if(try_bind(655))
315                 return 655;
316
317         fprintf(stderr, "Warning: could not bind to port 655. ");
318
319         for(int i = 0; i < 100; i++) {
320                 int port = 0x1000 + (rand() & 0x7fff);
321                 if(try_bind(port)) {
322                         char *filename;
323                         xasprintf(&filename, "%s" SLASH "hosts" SLASH "%s", confbase, name);
324                         FILE *f = fopen(filename, "a");
325                         free(filename);
326                         if(!f) {
327                                 fprintf(stderr, "Please change tinc's Port manually.\n");
328                                 return 0;
329                         }
330
331                         fprintf(f, "Port = %d\n", port);
332                         fclose(f);
333                         fprintf(stderr, "Tinc will instead listen on port %d.\n", port);
334                         return port;
335                 }
336         }
337
338         fprintf(stderr, "Please change tinc's Port manually.\n");
339         return 0;
340 }
341 //tinc_setup() should basically do what cmd_init() from src/tincctl.c does, except it doesn't have to generate a tinc-up script.
342 bool tinc_setup(const char* confbaseapi, const char* name) {
343         confbase = confbaseapi;
344         make_names();
345         xasprintf(&tinc_conf, "%s" SLASH "tinc.conf", confbase);
346         xasprintf(&hosts_dir, "%s" SLASH "hosts", confbase);
347         if(!access(tinc_conf, F_OK)) {
348                 fprintf(stderr, "Configuration file %s already exists!\n", tinc_conf);
349                 return false;
350         }
351
352         if(!check_id(name)) {
353                 fprintf(stderr, "Invalid Name! Only a-z, A-Z, 0-9 and _ are allowed characters.\n");
354                 return false;
355         }
356
357         if(!confbase_given && mkdir(confdir, 0755) && errno != EEXIST) {
358                 fprintf(stderr, "Could not create directory %s: %s\n", confdir, strerror(errno));
359                 return false;
360         }
361
362         if(mkdir(confbase, 0777) && errno != EEXIST) {
363                 fprintf(stderr, "Could not create directory %s: %s\n", confbase, strerror(errno));
364                 return false;
365         }
366
367         if(mkdir(hosts_dir, 0777) && errno != EEXIST) {
368                 fprintf(stderr, "Could not create directory %s: %s\n", hosts_dir, strerror(errno));
369                 return false;
370         }
371
372         FILE *f = fopen(tinc_conf, "w");
373         if(!f) {
374                 fprintf(stderr, "Could not create file %s: %s\n", tinc_conf, strerror(errno));
375                 return 1;
376         }
377
378         fprintf(f, "Name = %s\n", name);
379         fclose(f);
380
381         if(!rsa_keygen(2048, false) || !ecdsa_keygen(false))
382                 return false;
383
384         check_port(name);
385
386         return true;
387
388 }
389
390
391 bool tinc_start(const char* confbaseapi) {
392         pthread_t tincThread;
393         confbase = confbaseapi;
394         pthread_create(&tincThread,NULL,tinc_main_thread,confbaseapi);
395         pthread_detach(tincThread);
396 return true;
397 }
398
399 bool tinc_main_thread(void * in) {
400
401 confbase = (char*) in;
402 printf("Hello World %s\n",confbase);
403
404 }
405
406 bool tinc_stop();
407
408 // can be called from any thread
409 bool tinc_send_packet(node_t *receiver, const char* buf, unsigned int len);
410
411 // handler runs in tinc thread and should return immediately
412 bool tinc_set_packet_receive_handler(void (*handler)(const char* sender, const char* buf, unsigned int len));
413
414
415 //It might also be a good idea to add the option of looking up hosts by public
416 //key (fingerprints) instead of names.
417
418 node_t *tinc_get_host(const char *name);
419
420 bool tinc_get_hosts(node_t** hosts);
421
422 bool tinc_sign(const char* payload, unsigned int len, const char** signature);
423
424 int tinc_verify(const char* sender, const char* payload, unsigned int plen, const char* signature, unsigned int slen);
425
426 /*
427 TODO: It would be good to add a void pointer here that will be passed on to the
428 handler function whenever it is called, or have a void pointer in node_t
429 that can be filled in by the application. That way, you can easily link an
430 application-specific data structure to a node_t.
431 */
432 void channel_set_packet_send_handler(int (*handler)(const char* receiver, const char* buf, unsigned int len));
433 void channel_packet_receive_handler(const char* sender, const char* buf, unsigned int len);
434
435 bool channel_open(const char* partner, void(*read)(int id, const char* buf, unsigned int len), void(*result)(int result, int id));
436 void channel_close(int id);
437 bool channel_write(int id, const char* buf, unsigned int len, void(*result)(int result, int id, unsigned int written));
438
439
440 //We do need some more functions. First of all, we need to be able to add nodes
441 //to a VPN. To do that, either an invitation protocol should be used:
442
443 bool tinc_join_network(const char *invitation);
444 const char *tinc_generate_invitation(const char *name);
445
446 /*
447 Or two nodes should exchange some information (their name, address, public
448 key). If the application provides a way to exchange some data with another
449 node, then:
450 */
451
452 bool tinc_export(char *buf, size_t *len);
453 node_t *tinc_import(const char *buf, size_t len);
454 /*
455 Last but not least, some way to get rid of unwanted nodes. Simplest is a
456 function that just blacklists a node.
457 Which should immediately cause the local tincd to ignore any data from that
458 host from that point on. Of course, a somewhat centrally managed,
459 automatically distributed blacklist or whitelist would be the next step.
460 */
461 bool tinc_blacklist(node_t *host);
462
463
464
465