2 fsck.c -- Check the configuration files for problems
3 Copyright (C) 2014 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.
27 #ifndef DISABLE_LEGACY
34 static bool ask_fix(void) {
44 fprintf(stderr, "Fix y/n? ");
47 if(!fgets(buf, sizeof(buf), stdin)) {
52 if(buf[0] == 'y' || buf[0] == 'Y') {
56 if(buf[0] == 'n' || buf[0] == 'N') {
63 static void print_tinc_cmd(const char *argv0, const char *format, ...) {
65 fprintf(stderr, "%s -c %s ", argv0, confbase);
67 fprintf(stderr, "%s -n %s ", argv0, netname);
69 fprintf(stderr, "%s ", argv0);
74 vfprintf(stderr, format, va);
79 static int strtailcmp(const char *str, const char *tail) {
80 size_t slen = strlen(str);
81 size_t tlen = strlen(tail);
87 return memcmp(str + slen - tlen, tail, tlen);
90 static void check_conffile(const char *fname, bool server) {
91 FILE *f = fopen(fname, "r");
94 fprintf(stderr, "ERROR: cannot read %s: %s\n", fname, strerror(errno));
101 const int maxvariables = 50;
102 int count[maxvariables];
103 memset(count, 0, sizeof(count));
105 while(fgets(line, sizeof(line), f)) {
107 if(!strncmp(line, "-----END", 8)) {
113 if(!strncmp(line, "-----BEGIN", 10)) {
120 char *variable, *value, *eol;
121 variable = value = line;
125 eol = line + strlen(line);
127 while(strchr("\t \r\n", *--eol)) {
131 if(!line[0] || line[0] == '#') {
135 len = strcspn(value, "\t =");
137 value += strspn(value, "\t ");
141 value += strspn(value, "\t ");
144 variable[len] = '\0';
148 for(int i = 0; variables[i].name; i++) {
149 if(strcasecmp(variables[i].name, variable)) {
155 if(variables[i].type & VAR_OBSOLETE) {
156 fprintf(stderr, "WARNING: obsolete variable %s in %s line %d\n", variable, fname, lineno);
159 if(i < maxvariables) {
165 fprintf(stderr, "WARNING: unknown variable %s in %s line %d\n", variable, fname, lineno);
169 fprintf(stderr, "ERROR: no value for variable %s in %s line %d\n", variable, fname, lineno);
173 for(int i = 0; variables[i].name && i < maxvariables; i++) {
174 if(count[i] > 1 && !(variables[i].type & VAR_MULTIPLE)) {
175 fprintf(stderr, "WARNING: multiple instances of variable %s in %s\n", variables[i].name, fname);
180 fprintf(stderr, "ERROR: while reading %s: %s\n", fname, strerror(errno));
186 int fsck(const char *argv0) {
190 uid_t uid = getuid();
193 // Check that tinc.conf is readable.
195 if(access(tinc_conf, R_OK)) {
196 fprintf(stderr, "ERROR: cannot read %s: %s\n", tinc_conf, strerror(errno));
198 if(errno == ENOENT) {
199 fprintf(stderr, "No tinc configuration found. Create a new one with:\n\n");
200 print_tinc_cmd(argv0, "init");
201 } else if(errno == EACCES) {
203 fprintf(stderr, "You are currently not running tinc as root. Use sudo?\n");
205 fprintf(stderr, "Check the permissions of each component of the path %s.\n", tinc_conf);
212 char *name = get_my_name(true);
215 fprintf(stderr, "ERROR: tinc cannot run without a valid Name.\n");
219 // Check for private keys.
220 // TODO: use RSAPrivateKeyFile and Ed25519PrivateKeyFile variables if present.
223 char fname[PATH_MAX];
224 char dname[PATH_MAX];
226 #ifndef DISABLE_LEGACY
227 rsa_t *rsa_priv = NULL;
228 snprintf(fname, sizeof(fname), "%s/rsa_key.priv", confbase);
230 if(stat(fname, &st)) {
231 if(errno != ENOENT) {
232 // Something is seriously wrong here. If we can access the directory with tinc.conf in it, we should certainly be able to stat() an existing file.
233 fprintf(stderr, "ERROR: cannot read %s: %s\n", fname, strerror(errno));
234 fprintf(stderr, "Please correct this error.\n");
238 FILE *f = fopen(fname, "r");
241 fprintf(stderr, "ERROR: could not open %s: %s\n", fname, strerror(errno));
245 rsa_priv = rsa_read_pem_private_key(f);
249 fprintf(stderr, "ERROR: No key or unusable key found in %s.\n", fname);
250 fprintf(stderr, "You can generate a new RSA key with:\n\n");
251 print_tinc_cmd(argv0, "generate-rsa-keys");
255 #if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN)
257 if(st.st_mode & 077) {
258 fprintf(stderr, "WARNING: unsafe file permissions on %s.\n", fname);
260 if(st.st_uid != uid) {
261 fprintf(stderr, "You are not running %s as the same uid as %s.\n", argv0, fname);
262 } else if(ask_fix()) {
263 if(chmod(fname, st.st_mode & ~077)) {
264 fprintf(stderr, "ERROR: could not change permissions of %s: %s\n", fname, strerror(errno));
266 fprintf(stderr, "Fixed permissions of %s.\n", fname);
276 ecdsa_t *ecdsa_priv = NULL;
277 snprintf(fname, sizeof(fname), "%s/ed25519_key.priv", confbase);
279 if(stat(fname, &st)) {
280 if(errno != ENOENT) {
281 // Something is seriously wrong here. If we can access the directory with tinc.conf in it, we should certainly be able to stat() an existing file.
282 fprintf(stderr, "ERROR: cannot read %s: %s\n", fname, strerror(errno));
283 fprintf(stderr, "Please correct this error.\n");
287 FILE *f = fopen(fname, "r");
290 fprintf(stderr, "ERROR: could not open %s: %s\n", fname, strerror(errno));
294 ecdsa_priv = ecdsa_read_pem_private_key(f);
298 fprintf(stderr, "ERROR: No key or unusable key found in %s.\n", fname);
299 fprintf(stderr, "You can generate a new Ed25519 key with:\n\n");
300 print_tinc_cmd(argv0, "generate-ed25519-keys");
304 #if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN)
306 if(st.st_mode & 077) {
307 fprintf(stderr, "WARNING: unsafe file permissions on %s.\n", fname);
309 if(st.st_uid != uid) {
310 fprintf(stderr, "You are not running %s as the same uid as %s.\n", argv0, fname);
311 } else if(ask_fix()) {
312 if(chmod(fname, st.st_mode & ~077)) {
313 fprintf(stderr, "ERROR: could not change permissions of %s: %s\n", fname, strerror(errno));
315 fprintf(stderr, "Fixed permissions of %s.\n", fname);
323 #ifdef DISABLE_LEGACY
326 fprintf(stderr, "ERROR: No Ed25519 private key found.\n");
329 if(!rsa_priv && !ecdsa_priv) {
330 fprintf(stderr, "ERROR: Neither RSA or Ed25519 private key found.\n");
332 fprintf(stderr, "You can generate new keys with:\n\n");
333 print_tinc_cmd(argv0, "generate-keys");
337 // Check for public keys.
338 // TODO: use RSAPublicKeyFile variable if present.
340 snprintf(fname, sizeof(fname), "%s/hosts/%s", confbase, name);
342 if(access(fname, R_OK)) {
343 fprintf(stderr, "WARNING: cannot read %s\n", fname);
348 #ifndef DISABLE_LEGACY
349 rsa_t *rsa_pub = NULL;
351 f = fopen(fname, "r");
354 rsa_pub = rsa_read_pem_public_key(f);
360 fprintf(stderr, "WARNING: No (usable) public RSA key found.\n");
363 FILE *f = fopen(fname, "a");
366 if(rsa_write_pem_public_key(rsa_priv, f)) {
367 fprintf(stderr, "Wrote RSA public key to %s.\n", fname);
369 fprintf(stderr, "ERROR: could not write RSA public key to %s.\n", fname);
374 fprintf(stderr, "ERROR: could not append to %s: %s\n", fname, strerror(errno));
378 // TODO: suggest remedies
379 size_t len = rsa_size(rsa_priv);
381 if(len != rsa_size(rsa_pub)) {
382 fprintf(stderr, "ERROR: public and private RSA keys do not match.\n");
386 char buf1[len], buf2[len], buf3[len];
387 randomize(buf1, sizeof(buf1));
389 memset(buf2, 0, sizeof(buf2));
390 memset(buf3, 0, sizeof(buf2));
392 if(!rsa_public_encrypt(rsa_pub, buf1, sizeof(buf1), buf2)) {
393 fprintf(stderr, "ERROR: public RSA key does not work.\n");
397 if(!rsa_private_decrypt(rsa_priv, buf2, sizeof(buf2), buf3)) {
398 fprintf(stderr, "ERROR: private RSA key does not work.\n");
402 if(memcmp(buf1, buf3, sizeof(buf1))) {
403 fprintf(stderr, "ERROR: public and private RSA keys do not match.\n");
409 fprintf(stderr, "WARNING: A public RSA key was found but no private key is known.\n");
415 ecdsa_t *ecdsa_pub = NULL;
417 f = fopen(fname, "r");
420 ecdsa_pub = get_pubkey(f);
424 ecdsa_pub = ecdsa_read_pem_public_key(f);
432 fprintf(stderr, "WARNING: No (usable) public Ed25519 key found.\n");
435 FILE *f = fopen(fname, "a");
438 if(ecdsa_write_pem_public_key(ecdsa_priv, f)) {
439 fprintf(stderr, "Wrote Ed25519 public key to %s.\n", fname);
441 fprintf(stderr, "ERROR: could not write Ed25519 public key to %s.\n", fname);
446 fprintf(stderr, "ERROR: could not append to %s: %s\n", fname, strerror(errno));
450 // TODO: suggest remedies
451 char *key1 = ecdsa_get_base64_public_key(ecdsa_pub);
454 fprintf(stderr, "ERROR: public Ed25519 key does not work.\n");
458 char *key2 = ecdsa_get_base64_public_key(ecdsa_priv);
462 fprintf(stderr, "ERROR: private Ed25519 key does not work.\n");
466 int result = strcmp(key1, key2);
471 fprintf(stderr, "ERROR: public and private Ed25519 keys do not match.\n");
477 fprintf(stderr, "WARNING: A public Ed25519 key was found but no private key is known.\n");
481 // Check whether scripts are executable
484 DIR *dir = opendir(confbase);
487 fprintf(stderr, "ERROR: cannot read directory %s: %s\n", confbase, strerror(errno));
491 while((ent = readdir(dir))) {
492 if(strtailcmp(ent->d_name, "-up") && strtailcmp(ent->d_name, "-down")) {
496 strncpy(fname, ent->d_name, sizeof(fname));
497 char *dash = strrchr(fname, '-');
505 if(strcmp(fname, "tinc") && strcmp(fname, "host") && strcmp(fname, "subnet")) {
506 static bool explained = false;
507 fprintf(stderr, "WARNING: Unknown script %s" SLASH "%s found.\n", confbase, ent->d_name);
510 fprintf(stderr, "The only scripts in %s executed by tinc are:\n", confbase);
511 fprintf(stderr, "tinc-up, tinc-down, host-up, host-down, subnet-up and subnet-down.\n");
518 snprintf(fname, sizeof(fname), "%s" SLASH "%s", confbase, ent->d_name);
520 if(access(fname, R_OK | X_OK)) {
521 if(errno != EACCES) {
522 fprintf(stderr, "ERROR: cannot access %s: %s\n", fname, strerror(errno));
526 fprintf(stderr, "WARNING: cannot read and execute %s: %s\n", fname, strerror(errno));
529 if(chmod(fname, 0755)) {
530 fprintf(stderr, "ERROR: cannot change permissions on %s: %s\n", fname, strerror(errno));
538 snprintf(dname, sizeof(dname), "%s" SLASH "hosts", confbase);
539 dir = opendir(dname);
542 fprintf(stderr, "ERROR: cannot read directory %s: %s\n", dname, strerror(errno));
546 while((ent = readdir(dir))) {
547 if(strtailcmp(ent->d_name, "-up") && strtailcmp(ent->d_name, "-down")) {
551 strncpy(fname, ent->d_name, sizeof(fname));
552 char *dash = strrchr(fname, '-');
560 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, ent->d_name);
562 if(access(fname, R_OK | X_OK)) {
563 if(errno != EACCES) {
564 fprintf(stderr, "ERROR: cannot access %s: %s\n", fname, strerror(errno));
568 fprintf(stderr, "WARNING: cannot read and execute %s: %s\n", fname, strerror(errno));
571 if(chmod(fname, 0755)) {
572 fprintf(stderr, "ERROR: cannot change permissions on %s: %s\n", fname, strerror(errno));
580 // Check for obsolete / unsafe / unknown configuration variables.
582 check_conffile(tinc_conf, true);
584 dir = opendir(dname);
587 while((ent = readdir(dir))) {
588 if(!check_id(ent->d_name)) {
592 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, ent->d_name);
593 check_conffile(fname, false);