Replace MinGW with Windows to avoid ambiguities
[tinc] / src / fsck.c
1 /*
2     fsck.c -- Check the configuration files for problems
3     Copyright (C) 2014-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 #include "crypto.h"
22 #include "ecdsa.h"
23 #include "ecdsagen.h"
24 #include "fsck.h"
25 #include "names.h"
26 #ifndef DISABLE_LEGACY
27 #include "rsa.h"
28 #include "rsagen.h"
29 #endif
30 #include "tincctl.h"
31 #include "utils.h"
32 #include "xalloc.h"
33 #include "keys.h"
34 #include "conf.h"
35
36 static const char *exe_name = NULL;
37
38 static bool ask_fix(void) {
39         if(force) {
40                 return true;
41         }
42
43         if(!tty) {
44                 return false;
45         }
46
47 again:
48         fprintf(stderr, "Fix y/n? ");
49         char buf[1024];
50
51         if(!fgets(buf, sizeof(buf), stdin)) {
52                 tty = false;
53                 return false;
54         }
55
56         if(buf[0] == 'y' || buf[0] == 'Y') {
57                 return true;
58         }
59
60         if(buf[0] == 'n' || buf[0] == 'N') {
61                 return false;
62         }
63
64         goto again;
65 }
66
67 static void print_tinc_cmd(const char *format, ...) {
68         if(confbasegiven) {
69                 fprintf(stderr, "%s -c %s ", exe_name, confbase);
70         } else if(netname) {
71                 fprintf(stderr, "%s -n %s ", exe_name, netname);
72         } else {
73                 fprintf(stderr, "%s ", exe_name);
74         }
75
76         va_list va;
77         va_start(va, format);
78         vfprintf(stderr, format, va);
79         va_end(va);
80         fputc('\n', stderr);
81 }
82
83 typedef enum {
84         KEY_RSA,
85         KEY_ED25519,
86         KEY_BOTH,
87 } key_type_t;
88
89 static void print_new_keys_cmd(key_type_t key_type, const char *message) {
90         fprintf(stderr, "%s\n\n", message);
91
92         switch(key_type) {
93         case KEY_RSA:
94                 fprintf(stderr, "You can generate a new RSA keypair with:\n\n");
95                 print_tinc_cmd("generate-rsa-keys");
96                 break;
97
98         case KEY_ED25519:
99                 fprintf(stderr, "You can generate a new Ed25519 keypair with:\n\n");
100                 print_tinc_cmd("generate-ed25519-keys");
101                 break;
102
103         case KEY_BOTH:
104                 fprintf(stderr, "You can generate new keys with:\n\n");
105                 print_tinc_cmd("generate-keys");
106                 break;
107         }
108 }
109
110 static int strtailcmp(const char *str, const char *tail) {
111         size_t slen = strlen(str);
112         size_t tlen = strlen(tail);
113
114         if(tlen > slen) {
115                 return -1;
116         }
117
118         return memcmp(str + slen - tlen, tail, tlen);
119 }
120
121 static void check_conffile(const char *nodename, bool server) {
122         splay_tree_t config;
123         init_configuration(&config);
124
125         bool read;
126
127         if(server) {
128                 read = read_server_config(&config);
129         } else {
130                 read = read_host_config(&config, nodename, true);
131         }
132
133         if(!read) {
134                 splay_empty_tree(&config);
135                 return;
136         }
137
138         size_t total_vars = 0;
139
140         while(variables[total_vars].name) {
141                 ++total_vars;
142         }
143
144         const size_t countlen = total_vars * sizeof(int);
145         int *count = alloca(countlen);
146         memset(count, 0, countlen);
147
148         for splay_each(config_t, conf, &config) {
149                 int var_type = 0;
150
151                 for(size_t i = 0; variables[i].name; ++i) {
152                         if(strcasecmp(variables[i].name, conf->variable) == 0) {
153                                 count[i]++;
154                                 var_type = variables[i].type;
155                         }
156                 }
157
158                 if(var_type == 0) {
159                         continue;
160                 }
161
162                 if(var_type & VAR_OBSOLETE) {
163                         fprintf(stderr, "WARNING: obsolete variable %s in %s line %d\n",
164                                 conf->variable, conf->file, conf->line);
165                 }
166
167                 if(server && !(var_type & VAR_SERVER)) {
168                         fprintf(stderr, "WARNING: host variable %s found in server config %s line %d \n",
169                                 conf->variable, conf->file, conf->line);
170                 }
171
172                 if(!server && !(var_type & VAR_HOST)) {
173                         fprintf(stderr, "WARNING: server variable %s found in host config %s line %d \n",
174                                 conf->variable, conf->file, conf->line);
175                 }
176         }
177
178         for(size_t i = 0; i < total_vars; ++i) {
179                 if(count[i] > 1 && !(variables[i].type & VAR_MULTIPLE)) {
180                         fprintf(stderr, "WARNING: multiple instances of variable %s in %s\n",
181                                 variables[i].name, nodename ? nodename : "tinc.conf");
182                 }
183         }
184
185         splay_empty_tree(&config);
186 }
187
188 #ifdef HAVE_WINDOWS
189 typedef int uid_t;
190
191 static uid_t getuid(void) {
192         return 0;
193 }
194
195 static void check_key_file_mode(const char *fname) {
196         (void)fname;
197 }
198 #else
199 static void check_key_file_mode(const char *fname) {
200         const uid_t uid = getuid();
201         struct stat st;
202
203         if(stat(fname, &st)) {
204                 fprintf(stderr, "ERROR: could not stat private key file %s\n", fname);
205                 return;
206         }
207
208         if(st.st_mode & 077) {
209                 fprintf(stderr, "WARNING: unsafe file permissions on %s.\n", fname);
210
211                 if(st.st_uid != uid) {
212                         fprintf(stderr, "You are not running %s as the same uid as %s.\n", exe_name, fname);
213                 } else if(ask_fix()) {
214                         if(chmod(fname, st.st_mode & ~077u)) {
215                                 fprintf(stderr, "ERROR: could not change permissions of %s: %s\n", fname, strerror(errno));
216                         } else {
217                                 fprintf(stderr, "Fixed permissions of %s.\n", fname);
218                         }
219                 }
220         }
221 }
222 #endif // HAVE_WINDOWS
223
224 static char *read_node_name(void) {
225         if(access(tinc_conf, R_OK) == 0) {
226                 return get_my_name(true);
227         }
228
229         fprintf(stderr, "ERROR: cannot read %s: %s\n", tinc_conf, strerror(errno));
230
231         if(errno == ENOENT) {
232                 fprintf(stderr, "No tinc configuration found. Create a new one with:\n\n");
233                 print_tinc_cmd("init");
234                 return NULL;
235         }
236
237         if(errno == EACCES) {
238                 uid_t uid = getuid();
239
240                 if(uid != 0) {
241                         fprintf(stderr, "You are currently not running tinc as root. Use sudo?\n");
242                 } else {
243                         fprintf(stderr, "Check the permissions of each component of the path %s.\n", tinc_conf);
244                 }
245         }
246
247         return NULL;
248 }
249
250 static bool build_host_conf_path(char *fname, const size_t len) {
251         char *name = get_my_name(true);
252
253         if(!name) {
254                 fprintf(stderr, "ERROR: tinc cannot run without a valid Name.\n");
255                 return false;
256         }
257
258         snprintf(fname, len, "%s/hosts/%s", confbase, name);
259         free(name);
260         return true;
261 }
262
263 static bool ask_fix_ec_public_key(const char *fname, ecdsa_t *ec_priv) {
264         if(!ask_fix()) {
265                 return true;
266         }
267
268         if(!disable_old_keys(fname, "public Ed25519 key")) {
269                 return false;
270         }
271
272         FILE *f = fopen(fname, "a");
273
274         if(!f) {
275                 fprintf(stderr, "ERROR: could not append to %s: %s\n", fname, strerror(errno));
276                 return false;
277         }
278
279         bool success = ecdsa_write_pem_public_key(ec_priv, f);
280         fclose(f);
281
282         if(success) {
283                 fprintf(stderr, "Wrote Ed25519 public key to %s.\n", fname);
284         } else {
285                 fprintf(stderr, "ERROR: could not write Ed25519 public key to %s.\n", fname);
286         }
287
288         return success;
289 }
290
291 #ifndef DISABLE_LEGACY
292 static bool ask_fix_rsa_public_key(const char *fname, rsa_t *rsa_priv) {
293         if(!ask_fix()) {
294                 return true;
295         }
296
297         if(!disable_old_keys(fname, "public RSA key")) {
298                 return false;
299         }
300
301         FILE *f = fopen(fname, "a");
302
303         if(!f) {
304                 fprintf(stderr, "ERROR: could not append to %s: %s\n", fname, strerror(errno));
305                 return false;
306         }
307
308         bool success = rsa_write_pem_public_key(rsa_priv, f);
309         fclose(f);
310
311         if(success) {
312                 fprintf(stderr, "Wrote RSA public key to %s.\n", fname);
313         } else {
314                 fprintf(stderr, "ERROR: could not write RSA public key to %s.\n", fname);
315         }
316
317         return success;
318 }
319
320 static bool test_rsa_keypair(rsa_t *rsa_priv, rsa_t *rsa_pub, const char *host_file) {
321         size_t len = rsa_size(rsa_priv);
322
323         if(len != rsa_size(rsa_pub)) {
324                 fprintf(stderr, "ERROR: public and private RSA key lengths do not match.\n");
325                 return false;
326         }
327
328         bool success = false;
329         uint8_t *plaintext = xmalloc(len);
330         uint8_t *encrypted = xzalloc(len);
331         uint8_t *decrypted = xzalloc(len);
332
333         prng_randomize(plaintext, len);
334         plaintext[0] &= 0x7f;
335
336         if(rsa_public_encrypt(rsa_pub, plaintext, len, encrypted)) {
337                 if(rsa_private_decrypt(rsa_priv, encrypted, len, decrypted)) {
338                         if(memcmp(plaintext, decrypted, len) == 0) {
339                                 success = true;
340                         } else {
341                                 fprintf(stderr, "ERROR: public and private RSA keys do not match.\n");
342                                 success = ask_fix_rsa_public_key(host_file, rsa_priv);
343                         }
344                 } else {
345                         print_new_keys_cmd(KEY_RSA, "ERROR: private RSA key does not work.");
346                 }
347         } else {
348                 fprintf(stderr, "ERROR: public RSA key does not work.\n");
349                 success = ask_fix_rsa_public_key(host_file, rsa_priv);
350         }
351
352         free(decrypted);
353         free(encrypted);
354         free(plaintext);
355
356         return success;
357 }
358
359 static bool check_rsa_pubkey(rsa_t *rsa_priv, rsa_t *rsa_pub, const char *host_file) {
360         if(!rsa_pub) {
361                 fprintf(stderr, "WARNING: No (usable) public RSA key found.\n");
362                 return ask_fix_rsa_public_key(host_file, rsa_priv);
363         }
364
365         if(!rsa_priv) {
366                 fprintf(stderr, "WARNING: A public RSA key was found but no private key is known.\n");
367                 return true;
368         }
369
370         return test_rsa_keypair(rsa_priv, rsa_pub, host_file);
371 }
372 #endif // DISABLE_LEGACY
373
374 static bool test_ec_keypair(ecdsa_t *ec_priv, ecdsa_t *ec_pub, const char *host_file) {
375         // base64-encoded public key obtained from the PRIVATE key.
376         char *b64_priv_pub = ecdsa_get_base64_public_key(ec_priv);
377
378         if(!b64_priv_pub) {
379                 print_new_keys_cmd(KEY_ED25519, "ERROR: private Ed25519 key does not work.");
380                 return false;
381         }
382
383         // base64-encoded public key obtained from the PUBLIC key.
384         char *b64_pub_pub = ecdsa_get_base64_public_key(ec_pub);
385
386         if(!b64_pub_pub) {
387                 fprintf(stderr, "ERROR: public Ed25519 key does not work.\n");
388                 free(b64_priv_pub);
389                 return ask_fix_ec_public_key(host_file, ec_priv);
390         }
391
392         bool match = strcmp(b64_pub_pub, b64_priv_pub) == 0;
393         free(b64_pub_pub);
394         free(b64_priv_pub);
395
396         if(match) {
397                 return true;
398         }
399
400         fprintf(stderr, "ERROR: public and private Ed25519 keys do not match.\n");
401         return ask_fix_ec_public_key(host_file, ec_priv);
402 }
403
404 static bool check_ec_pubkey(ecdsa_t *ec_priv, ecdsa_t *ec_pub, const char *host_file) {
405         if(!ec_priv) {
406                 if(ec_pub) {
407                         print_new_keys_cmd(KEY_ED25519, "WARNING: A public Ed25519 key was found but no private key is known.");
408                 }
409
410                 return true;
411         }
412
413         if(ec_pub) {
414                 return test_ec_keypair(ec_priv, ec_pub, host_file);
415         }
416
417         fprintf(stderr, "WARNING: No (usable) public Ed25519 key found.\n");
418         return ask_fix_ec_public_key(host_file, ec_priv);
419 }
420
421 static bool check_config_mode(const char *fname) {
422         if(access(fname, R_OK | X_OK) == 0) {
423                 return true;
424         }
425
426         if(errno != EACCES) {
427                 fprintf(stderr, "ERROR: cannot access %s: %s\n", fname, strerror(errno));
428                 return false;
429         }
430
431         fprintf(stderr, "WARNING: cannot read and execute %s: %s\n", fname, strerror(errno));
432
433         if(ask_fix()) {
434                 if(chmod(fname, 0755)) {
435                         fprintf(stderr, "ERROR: cannot change permissions on %s: %s\n", fname, strerror(errno));
436                 }
437         }
438
439         return true;
440 }
441
442 static bool check_script_confdir(void) {
443         char fname[PATH_MAX];
444         DIR *dir = opendir(confbase);
445
446         if(!dir) {
447                 fprintf(stderr, "ERROR: cannot read directory %s: %s\n", confbase, strerror(errno));
448                 return false;
449         }
450
451         struct dirent *ent;
452
453         while((ent = readdir(dir))) {
454                 if(strtailcmp(ent->d_name, "-up") && strtailcmp(ent->d_name, "-down")) {
455                         continue;
456                 }
457
458                 strncpy(fname, ent->d_name, sizeof(fname));
459                 char *dash = strrchr(fname, '-');
460
461                 if(!dash) {
462                         continue;
463                 }
464
465                 *dash = 0;
466
467                 if(strcmp(fname, "tinc") && strcmp(fname, "host") && strcmp(fname, "subnet")) {
468                         static bool explained = false;
469                         fprintf(stderr, "WARNING: Unknown script %s" SLASH "%s found.\n", confbase, ent->d_name);
470
471                         if(!explained) {
472                                 fprintf(stderr, "The only scripts in %s executed by tinc are:\n", confbase);
473                                 fprintf(stderr, "tinc-up, tinc-down, host-up, host-down, subnet-up and subnet-down.\n");
474                                 explained = true;
475                         }
476
477                         continue;
478                 }
479
480                 snprintf(fname, sizeof(fname), "%s" SLASH "%s", confbase, ent->d_name);
481                 check_config_mode(fname);
482         }
483
484         closedir(dir);
485
486         return true;
487 }
488
489 static bool check_script_hostdir(const char *host_dir) {
490         char fname[PATH_MAX];
491         DIR *dir = opendir(host_dir);
492
493         if(!dir) {
494                 fprintf(stderr, "ERROR: cannot read directory %s: %s\n", host_dir, strerror(errno));
495                 return false;
496         }
497
498         struct dirent *ent;
499
500         while((ent = readdir(dir))) {
501                 if(strtailcmp(ent->d_name, "-up") && strtailcmp(ent->d_name, "-down")) {
502                         continue;
503                 }
504
505                 strncpy(fname, ent->d_name, sizeof(fname));
506                 char *dash = strrchr(fname, '-');
507
508                 if(!dash) {
509                         continue;
510                 }
511
512                 *dash = 0;
513
514                 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, ent->d_name);
515                 check_config_mode(fname);
516         }
517
518         closedir(dir);
519
520         return true;
521 }
522
523 #ifdef DISABLE_LEGACY
524 static bool check_public_keys(splay_tree_t *config, const char *name, ecdsa_t *ec_priv) {
525 #else
526 static bool check_public_keys(splay_tree_t *config, const char *name, rsa_t *rsa_priv, ecdsa_t *ec_priv) {
527 #endif
528         // Check public keys.
529         char host_file[PATH_MAX];
530
531         if(!build_host_conf_path(host_file, sizeof(host_file))) {
532                 return false;
533         }
534
535         if(access(host_file, R_OK)) {
536                 fprintf(stderr, "WARNING: cannot read %s\n", host_file);
537         }
538
539         ecdsa_t *ec_pub = NULL;
540         read_ecdsa_public_key(&ec_pub, &config, name);
541
542         bool success = true;
543 #ifndef DISABLE_LEGACY
544         rsa_t *rsa_pub = NULL;
545         read_rsa_public_key(&rsa_pub, config, name);
546
547         success = check_rsa_pubkey(rsa_priv, rsa_pub, host_file);
548         rsa_free(rsa_pub);
549 #endif
550
551         if(!check_ec_pubkey(ec_priv, ec_pub, host_file)) {
552                 success = false;
553         }
554
555         ecdsa_free(ec_pub);
556
557         return success;
558 }
559
560 static bool check_keypairs(splay_tree_t *config, const char *name) {
561         // Check private keys.
562         char *priv_keyfile = NULL;
563         ecdsa_t *ec_priv = read_ecdsa_private_key(config, &priv_keyfile);
564
565         if(priv_keyfile) {
566                 check_key_file_mode(priv_keyfile);
567                 free(priv_keyfile);
568                 priv_keyfile = NULL;
569         }
570
571 #ifdef DISABLE_LEGACY
572
573         if(!ec_priv) {
574                 print_new_keys_cmd(KEY_ED25519, "ERROR: No Ed25519 private key found.");
575                 return false;
576         }
577
578 #else
579         rsa_t *rsa_priv = read_rsa_private_key(config, &priv_keyfile);
580
581         if(priv_keyfile) {
582                 check_key_file_mode(priv_keyfile);
583                 free(priv_keyfile);
584         }
585
586         if(!rsa_priv && !ec_priv) {
587                 print_new_keys_cmd(KEY_BOTH, "ERROR: Neither RSA or Ed25519 private key found.");
588                 return false;
589         }
590
591 #endif
592
593 #ifdef DISABLE_LEGACY
594         bool success = check_public_keys(config, name, ec_priv);
595 #else
596         bool success = check_public_keys(config, name, rsa_priv, ec_priv);
597         rsa_free(rsa_priv);
598 #endif
599         ecdsa_free(ec_priv);
600
601         return success;
602 }
603
604 static void check_config_variables(const char *host_dir) {
605         check_conffile(NULL, true);
606
607         DIR *dir = opendir(host_dir);
608
609         if(dir) {
610                 for(struct dirent * ent; (ent = readdir(dir));) {
611                         if(check_id(ent->d_name)) {
612                                 check_conffile(ent->d_name, false);
613                         }
614                 }
615
616                 closedir(dir);
617         }
618 }
619
620 static bool check_scripts_and_configs(void) {
621         // Check whether scripts are executable.
622         if(!check_script_confdir()) {
623                 return false;
624         }
625
626         char host_dir[PATH_MAX];
627         snprintf(host_dir, sizeof(host_dir), "%s" SLASH "hosts", confbase);
628
629         if(!check_script_hostdir(host_dir)) {
630                 return false;
631         }
632
633         // Check for obsolete / unsafe / unknown configuration variables (and print warnings).
634         check_config_variables(host_dir);
635
636         return true;
637 }
638
639 int fsck(const char *argv0) {
640         exe_name = argv0;
641
642         // Check that tinc.conf is readable and read our name if it is.
643         char *name = read_node_name();
644
645         if(!name) {
646                 fprintf(stderr, "ERROR: tinc cannot run without a valid Name.\n");
647                 exe_name = NULL;
648                 return EXIT_FAILURE;
649         }
650
651         // Avoid touching global configuration here. Read the config files into
652         // a temporary configuration tree, then throw it away after fsck is done.
653         splay_tree_t config;
654         init_configuration(&config);
655
656         // Read the server configuration file and append host configuration for our node.
657         bool success = read_server_config(&config) &&
658                        read_host_config(&config, name, true);
659
660         // Check both RSA and EC key pairs.
661         // We need working configuration to run this check.
662         if(success) {
663                 success = check_keypairs(&config, name);
664         }
665
666         // Check that scripts are executable and check the config for invalid variables.
667         // This check does not require working configuration, so run it always.
668         // This way, we can diagnose more issues on the first run.
669         success = success & check_scripts_and_configs();
670
671         splay_empty_tree(&config);
672         free(name);
673         exe_name = NULL;
674
675         return success ? EXIT_SUCCESS : EXIT_FAILURE;
676 }