2 fsck.c -- Check the configuration files for problems
3 Copyright (C) 2014-2022 Guus Sliepen <guus@tinc-vpn.org>
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.
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.
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.
26 #ifndef DISABLE_LEGACY
36 static const char *exe_name = NULL;
38 static bool ask_fix(void) {
48 fprintf(stderr, "Fix y/n? ");
51 if(!fgets(buf, sizeof(buf), stdin)) {
56 if(buf[0] == 'y' || buf[0] == 'Y') {
60 if(buf[0] == 'n' || buf[0] == 'N') {
67 static void print_tinc_cmd(const char *format, ...) ATTR_FORMAT(printf, 1, 2);
68 static void print_tinc_cmd(const char *format, ...) {
70 fprintf(stderr, "%s -c %s ", exe_name, confbase);
72 fprintf(stderr, "%s -n %s ", exe_name, netname);
74 fprintf(stderr, "%s ", exe_name);
79 vfprintf(stderr, format, va);
90 static void print_new_keys_cmd(key_type_t key_type, const char *message) {
91 fprintf(stderr, "%s\n\n", message);
95 fprintf(stderr, "You can generate a new RSA keypair with:\n\n");
96 print_tinc_cmd("generate-rsa-keys");
100 fprintf(stderr, "You can generate a new Ed25519 keypair with:\n\n");
101 print_tinc_cmd("generate-ed25519-keys");
105 fprintf(stderr, "You can generate new keys with:\n\n");
106 print_tinc_cmd("generate-keys");
111 static int strtailcmp(const char *str, const char *tail) {
112 size_t slen = strlen(str);
113 size_t tlen = strlen(tail);
119 return memcmp(str + slen - tlen, tail, tlen);
122 static void check_conffile(const char *nodename, bool server) {
124 init_configuration(&config);
129 read = read_server_config(&config);
131 read = read_host_config(&config, nodename, true);
135 splay_empty_tree(&config);
139 size_t total_vars = 0;
141 while(variables[total_vars].name) {
146 splay_empty_tree(&config);
150 const size_t countlen = total_vars * sizeof(int);
151 int *count = alloca(countlen);
152 memset(count, 0, countlen);
154 for splay_each(config_t, conf, &config) {
157 for(size_t i = 0; variables[i].name; ++i) {
158 if(strcasecmp(variables[i].name, conf->variable) == 0) {
160 var_type = variables[i].type;
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);
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);
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);
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");
191 splay_empty_tree(&config);
197 static uid_t getuid(void) {
201 static void check_key_file_mode(const char *fname) {
205 static void check_key_file_mode(const char *fname) {
206 const uid_t uid = getuid();
209 if(stat(fname, &st)) {
210 fprintf(stderr, "ERROR: could not stat private key file %s\n", fname);
214 if(st.st_mode & 077) {
215 fprintf(stderr, "WARNING: unsafe file permissions on %s.\n", fname);
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));
223 fprintf(stderr, "Fixed permissions of %s.\n", fname);
228 #endif // HAVE_WINDOWS
230 static char *read_node_name(void) {
231 if(access(tinc_conf, R_OK) == 0) {
232 return get_my_name(true);
235 fprintf(stderr, "ERROR: cannot read %s: %s\n", tinc_conf, strerror(errno));
237 if(errno == ENOENT) {
238 fprintf(stderr, "No tinc configuration found. Create a new one with:\n\n");
239 print_tinc_cmd("init");
243 if(errno == EACCES) {
244 uid_t uid = getuid();
247 fprintf(stderr, "You are currently not running tinc as root. Use sudo?\n");
249 fprintf(stderr, "Check the permissions of each component of the path %s.\n", tinc_conf);
256 static bool build_host_conf_path(char *fname, const size_t len) {
257 char *name = get_my_name(true);
260 fprintf(stderr, "ERROR: tinc cannot run without a valid Name.\n");
264 snprintf(fname, len, "%s/hosts/%s", confbase, name);
269 static bool ask_fix_ec_public_key(const char *fname, ecdsa_t *ec_priv) {
274 if(!disable_old_keys(fname, "public Ed25519 key")) {
278 FILE *f = fopen(fname, "a");
281 fprintf(stderr, "ERROR: could not append to %s: %s\n", fname, strerror(errno));
285 bool success = ecdsa_write_pem_public_key(ec_priv, f);
289 fprintf(stderr, "Wrote Ed25519 public key to %s.\n", fname);
291 fprintf(stderr, "ERROR: could not write Ed25519 public key to %s.\n", fname);
297 #ifndef DISABLE_LEGACY
298 static bool ask_fix_rsa_public_key(const char *fname, rsa_t *rsa_priv) {
303 if(!disable_old_keys(fname, "public RSA key")) {
307 FILE *f = fopen(fname, "a");
310 fprintf(stderr, "ERROR: could not append to %s: %s\n", fname, strerror(errno));
314 bool success = rsa_write_pem_public_key(rsa_priv, f);
318 fprintf(stderr, "Wrote RSA public key to %s.\n", fname);
320 fprintf(stderr, "ERROR: could not write RSA public key to %s.\n", fname);
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);
329 if(len != rsa_size(rsa_pub)) {
330 fprintf(stderr, "ERROR: public and private RSA key lengths do not match.\n");
334 bool success = false;
335 uint8_t *plaintext = xmalloc(len);
336 uint8_t *encrypted = xzalloc(len);
337 uint8_t *decrypted = xzalloc(len);
339 prng_randomize(plaintext, len);
340 plaintext[0] &= 0x7f;
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) {
347 fprintf(stderr, "ERROR: public and private RSA keys do not match.\n");
348 success = ask_fix_rsa_public_key(host_file, rsa_priv);
351 print_new_keys_cmd(KEY_RSA, "ERROR: private RSA key does not work.");
354 fprintf(stderr, "ERROR: public RSA key does not work.\n");
355 success = ask_fix_rsa_public_key(host_file, rsa_priv);
365 static bool check_rsa_pubkey(rsa_t *rsa_priv, rsa_t *rsa_pub, const char *host_file) {
367 fprintf(stderr, "WARNING: No (usable) public RSA key found.\n");
368 return ask_fix_rsa_public_key(host_file, rsa_priv);
372 fprintf(stderr, "WARNING: A public RSA key was found but no private key is known.\n");
376 return test_rsa_keypair(rsa_priv, rsa_pub, host_file);
378 #endif // DISABLE_LEGACY
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);
385 print_new_keys_cmd(KEY_ED25519, "ERROR: private Ed25519 key does not work.");
389 // base64-encoded public key obtained from the PUBLIC key.
390 char *b64_pub_pub = ecdsa_get_base64_public_key(ec_pub);
393 fprintf(stderr, "ERROR: public Ed25519 key does not work.\n");
395 return ask_fix_ec_public_key(host_file, ec_priv);
398 bool match = strcmp(b64_pub_pub, b64_priv_pub) == 0;
406 fprintf(stderr, "ERROR: public and private Ed25519 keys do not match.\n");
407 return ask_fix_ec_public_key(host_file, ec_priv);
410 static bool check_ec_pubkey(ecdsa_t *ec_priv, ecdsa_t *ec_pub, const char *host_file) {
413 print_new_keys_cmd(KEY_ED25519, "WARNING: A public Ed25519 key was found but no private key is known.");
420 return test_ec_keypair(ec_priv, ec_pub, host_file);
423 fprintf(stderr, "WARNING: No (usable) public Ed25519 key found.\n");
424 return ask_fix_ec_public_key(host_file, ec_priv);
427 static bool check_config_mode(const char *fname) {
428 if(access(fname, R_OK | X_OK) == 0) {
432 if(errno != EACCES) {
433 fprintf(stderr, "ERROR: cannot access %s: %s\n", fname, strerror(errno));
437 fprintf(stderr, "WARNING: cannot read and execute %s: %s\n", fname, strerror(errno));
440 if(chmod(fname, 0755)) {
441 fprintf(stderr, "ERROR: cannot change permissions on %s: %s\n", fname, strerror(errno));
448 static bool check_script_confdir(void) {
449 char fname[PATH_MAX];
450 DIR *dir = opendir(confbase);
453 fprintf(stderr, "ERROR: cannot read directory %s: %s\n", confbase, strerror(errno));
459 while((ent = readdir(dir))) {
460 if(strtailcmp(ent->d_name, "-up") && strtailcmp(ent->d_name, "-down")) {
464 strncpy(fname, ent->d_name, sizeof(fname));
465 char *dash = strrchr(fname, '-');
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);
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");
486 snprintf(fname, sizeof(fname), "%s" SLASH "%s", confbase, ent->d_name);
487 check_config_mode(fname);
495 static bool check_script_hostdir(const char *host_dir) {
496 char fname[PATH_MAX];
497 DIR *dir = opendir(host_dir);
500 fprintf(stderr, "ERROR: cannot read directory %s: %s\n", host_dir, strerror(errno));
506 while((ent = readdir(dir))) {
507 if(strtailcmp(ent->d_name, "-up") && strtailcmp(ent->d_name, "-down")) {
511 strncpy(fname, ent->d_name, sizeof(fname));
512 char *dash = strrchr(fname, '-');
520 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, ent->d_name);
521 check_config_mode(fname);
529 #ifdef DISABLE_LEGACY
530 static bool check_public_keys(splay_tree_t *config, const char *name, ecdsa_t *ec_priv) {
532 static bool check_public_keys(splay_tree_t *config, const char *name, rsa_t *rsa_priv, ecdsa_t *ec_priv) {
534 // Check public keys.
535 char host_file[PATH_MAX];
537 if(!build_host_conf_path(host_file, sizeof(host_file))) {
541 if(access(host_file, R_OK)) {
542 fprintf(stderr, "WARNING: cannot read %s\n", host_file);
545 ecdsa_t *ec_pub = read_ecdsa_public_key(&config, name);
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);
554 if(!check_ec_pubkey(ec_priv, ec_pub, host_file)) {
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);
569 check_key_file_mode(priv_keyfile);
574 #ifdef DISABLE_LEGACY
577 print_new_keys_cmd(KEY_ED25519, "ERROR: No Ed25519 private key found.");
582 rsa_t *rsa_priv = read_rsa_private_key(config, &priv_keyfile);
585 check_key_file_mode(priv_keyfile);
589 if(!rsa_priv && !ec_priv) {
590 print_new_keys_cmd(KEY_BOTH, "ERROR: Neither RSA or Ed25519 private key found.");
596 #ifdef DISABLE_LEGACY
597 bool success = check_public_keys(config, name, ec_priv);
599 bool success = check_public_keys(config, name, rsa_priv, ec_priv);
607 static void check_config_variables(const char *host_dir) {
608 check_conffile(NULL, true);
610 DIR *dir = opendir(host_dir);
613 for(struct dirent * ent; (ent = readdir(dir));) {
614 if(check_id(ent->d_name)) {
615 check_conffile(ent->d_name, false);
623 static bool check_scripts_and_configs(void) {
624 // Check whether scripts are executable.
625 if(!check_script_confdir()) {
629 char host_dir[PATH_MAX];
630 snprintf(host_dir, sizeof(host_dir), "%s" SLASH "hosts", confbase);
632 if(!check_script_hostdir(host_dir)) {
636 // Check for obsolete / unsafe / unknown configuration variables (and print warnings).
637 check_config_variables(host_dir);
642 int fsck(const char *argv0) {
645 // Check that tinc.conf is readable and read our name if it is.
646 char *name = read_node_name();
649 fprintf(stderr, "ERROR: tinc cannot run without a valid Name.\n");
654 // Avoid touching global configuration here. Read the config files into
655 // a temporary configuration tree, then throw it away after fsck is done.
657 init_configuration(&config);
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);
663 // Check both RSA and EC key pairs.
664 // We need working configuration to run this check.
666 success = check_keypairs(&config, name);
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();
674 splay_empty_tree(&config);
678 return success ? EXIT_SUCCESS : EXIT_FAILURE;