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