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