/*
fsck.c -- Check the configuration files for problems
- Copyright (C) 2014 Guus Sliepen <guus@tinc-vpn.org>
+ Copyright (C) 2014-2021 Guus Sliepen <guus@tinc-vpn.org>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
#include "utils.h"
static bool ask_fix(void) {
- if(force)
+ if(force) {
return true;
- if(!tty)
+ }
+
+ if(!tty) {
return false;
+ }
+
again:
fprintf(stderr, "Fix y/n? ");
char buf[1024];
+
if(!fgets(buf, sizeof(buf), stdin)) {
tty = false;
return false;
}
- if(buf[0] == 'y' || buf[0] == 'Y')
+
+ if(buf[0] == 'y' || buf[0] == 'Y') {
return true;
- if(buf[0] == 'n' || buf[0] == 'N')
+ }
+
+ if(buf[0] == 'n' || buf[0] == 'N') {
return false;
+ }
+
goto again;
}
static void print_tinc_cmd(const char *argv0, const char *format, ...) {
- if(confbasegiven)
+ if(confbasegiven) {
fprintf(stderr, "%s -c %s ", argv0, confbase);
- else if(netname)
+ } else if(netname) {
fprintf(stderr, "%s -n %s ", argv0, netname);
- else
+ } else {
fprintf(stderr, "%s ", argv0);
+ }
+
va_list va;
va_start(va, format);
vfprintf(stderr, format, va);
static int strtailcmp(const char *str, const char *tail) {
size_t slen = strlen(str);
size_t tlen = strlen(tail);
- if(tlen > slen)
+
+ if(tlen > slen) {
return -1;
+ }
+
return memcmp(str + slen - tlen, tail, tlen);
}
static void check_conffile(const char *fname, bool server) {
+ (void)server;
+
FILE *f = fopen(fname, "r");
+
if(!f) {
fprintf(stderr, "ERROR: cannot read %s: %s\n", fname, strerror(errno));
return;
while(fgets(line, sizeof(line), f)) {
if(skip) {
- if(!strncmp(line, "-----END", 8))
+ if(!strncmp(line, "-----END", 8)) {
skip = false;
+ }
+
continue;
} else {
if(!strncmp(line, "-----BEGIN", 10)) {
lineno++;
eol = line + strlen(line);
- while(strchr("\t \r\n", *--eol))
+
+ while(strchr("\t \r\n", *--eol)) {
*eol = '\0';
+ }
- if(!line[0] || line[0] == '#')
+ if(!line[0] || line[0] == '#') {
continue;
+ }
len = strcspn(value, "\t =");
value += len;
value += strspn(value, "\t ");
+
if(*value == '=') {
value++;
value += strspn(value, "\t ");
}
+
variable[len] = '\0';
bool found = false;
for(int i = 0; variables[i].name; i++) {
- if(strcasecmp(variables[i].name, variable))
+ if(strcasecmp(variables[i].name, variable)) {
continue;
+ }
found = true;
fprintf(stderr, "WARNING: obsolete variable %s in %s line %d\n", variable, fname, lineno);
}
- if(i < maxvariables)
+ if(i < maxvariables) {
count[i]++;
+ }
}
- if(!found)
+ if(!found) {
fprintf(stderr, "WARNING: unknown variable %s in %s line %d\n", variable, fname, lineno);
+ }
- if(!*value)
+ if(!*value) {
fprintf(stderr, "ERROR: no value for variable %s in %s line %d\n", variable, fname, lineno);
+ }
}
for(int i = 0; variables[i].name && i < maxvariables; i++) {
- if(count[i] > 1 && !(variables[i].type & VAR_MULTIPLE))
+ if(count[i] > 1 && !(variables[i].type & VAR_MULTIPLE)) {
fprintf(stderr, "WARNING: multiple instances of variable %s in %s\n", variables[i].name, fname);
+ }
}
- if(ferror(f))
+ if(ferror(f)) {
fprintf(stderr, "ERROR: while reading %s: %s\n", fname, strerror(errno));
+ }
fclose(f);
}
if(access(tinc_conf, R_OK)) {
fprintf(stderr, "ERROR: cannot read %s: %s\n", tinc_conf, strerror(errno));
+
if(errno == ENOENT) {
fprintf(stderr, "No tinc configuration found. Create a new one with:\n\n");
print_tinc_cmd(argv0, "init");
} else if(errno == EACCES) {
- if(uid != 0)
+ if(uid != 0) {
fprintf(stderr, "You are currently not running tinc as root. Use sudo?\n");
- else
+ } else {
fprintf(stderr, "Check the permissions of each component of the path %s.\n", tinc_conf);
+ }
}
+
return 1;
}
char *name = get_my_name(true);
+
if(!name) {
fprintf(stderr, "ERROR: tinc cannot run without a valid Name.\n");
return 1;
}
} else {
FILE *f = fopen(fname, "r");
+
if(!f) {
fprintf(stderr, "ERROR: could not open %s: %s\n", fname, strerror(errno));
return 1;
}
+
rsa_priv = rsa_read_pem_private_key(f);
fclose(f);
+
if(!rsa_priv) {
fprintf(stderr, "ERROR: No key or unusable key found in %s.\n", fname);
fprintf(stderr, "You can generate a new RSA key with:\n\n");
return 1;
}
-#if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN)
+#ifndef HAVE_MINGW
+
if(st.st_mode & 077) {
fprintf(stderr, "WARNING: unsafe file permissions on %s.\n", fname);
+
if(st.st_uid != uid) {
fprintf(stderr, "You are not running %s as the same uid as %s.\n", argv0, fname);
} else if(ask_fix()) {
- if(chmod(fname, st.st_mode & ~077))
+ if(chmod(fname, st.st_mode & ~077)) {
fprintf(stderr, "ERROR: could not change permissions of %s: %s\n", fname, strerror(errno));
- else
+ } else {
fprintf(stderr, "Fixed permissions of %s.\n", fname);
+ }
}
}
+
#endif
}
+
#endif
ecdsa_t *ecdsa_priv = NULL;
}
} else {
FILE *f = fopen(fname, "r");
+
if(!f) {
fprintf(stderr, "ERROR: could not open %s: %s\n", fname, strerror(errno));
return 1;
}
+
ecdsa_priv = ecdsa_read_pem_private_key(f);
fclose(f);
+
if(!ecdsa_priv) {
fprintf(stderr, "ERROR: No key or unusable key found in %s.\n", fname);
fprintf(stderr, "You can generate a new Ed25519 key with:\n\n");
return 1;
}
-#if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN)
+#ifndef HAVE_MINGW
+
if(st.st_mode & 077) {
fprintf(stderr, "WARNING: unsafe file permissions on %s.\n", fname);
+
if(st.st_uid != uid) {
fprintf(stderr, "You are not running %s as the same uid as %s.\n", argv0, fname);
} else if(ask_fix()) {
- if(chmod(fname, st.st_mode & ~077))
+ if(chmod(fname, st.st_mode & ~077)) {
fprintf(stderr, "ERROR: could not change permissions of %s: %s\n", fname, strerror(errno));
- else
+ } else {
fprintf(stderr, "Fixed permissions of %s.\n", fname);
+ }
}
}
+
#endif
}
#ifdef DISABLE_LEGACY
+
if(!ecdsa_priv) {
fprintf(stderr, "ERROR: No Ed25519 private key found.\n");
#else
+
if(!rsa_priv && !ecdsa_priv) {
fprintf(stderr, "ERROR: Neither RSA or Ed25519 private key found.\n");
#endif
// TODO: use RSAPublicKeyFile variable if present.
snprintf(fname, sizeof(fname), "%s/hosts/%s", confbase, name);
- if(access(fname, R_OK))
+
+ if(access(fname, R_OK)) {
fprintf(stderr, "WARNING: cannot read %s\n", fname);
+ }
FILE *f;
rsa_t *rsa_pub = NULL;
f = fopen(fname, "r");
+
if(f) {
rsa_pub = rsa_read_pem_public_key(f);
fclose(f);
if(rsa_priv) {
if(!rsa_pub) {
fprintf(stderr, "WARNING: No (usable) public RSA key found.\n");
+
if(ask_fix()) {
FILE *f = fopen(fname, "a");
+
if(f) {
- if(rsa_write_pem_public_key(rsa_priv, f))
+ if(rsa_write_pem_public_key(rsa_priv, f)) {
fprintf(stderr, "Wrote RSA public key to %s.\n", fname);
- else
+ } else {
fprintf(stderr, "ERROR: could not write RSA public key to %s.\n", fname);
+ }
+
fclose(f);
} else {
fprintf(stderr, "ERROR: could not append to %s: %s\n", fname, strerror(errno));
} else {
// TODO: suggest remedies
size_t len = rsa_size(rsa_priv);
+
if(len != rsa_size(rsa_pub)) {
fprintf(stderr, "ERROR: public and private RSA keys do not match.\n");
return 1;
}
- char buf1[len], buf2[len], buf3[len];
- randomize(buf1, sizeof(buf1));
+
+ char *buf1 = malloc(len);
+ char *buf2 = malloc(len);
+ char *buf3 = malloc(len);
+
+ randomize(buf1, len);
buf1[0] &= 0x7f;
- memset(buf2, 0, sizeof(buf2));
- memset(buf3, 0, sizeof(buf2));
- if(!rsa_public_encrypt(rsa_pub, buf1, sizeof(buf1), buf2)) {
+ memset(buf2, 0, len);
+ memset(buf3, 0, len);
+ bool result = false;
+
+ if(rsa_public_encrypt(rsa_pub, buf1, len, buf2)) {
+ if(rsa_private_decrypt(rsa_priv, buf2, len, buf3)) {
+ if(memcmp(buf1, buf3, len)) {
+ result = true;
+ } else {
+ fprintf(stderr, "ERROR: public and private RSA keys do not match.\n");
+ }
+ } else {
+ fprintf(stderr, "ERROR: private RSA key does not work.\n");
+ }
+ } else {
fprintf(stderr, "ERROR: public RSA key does not work.\n");
- return 1;
}
- if(!rsa_private_decrypt(rsa_priv, buf2, sizeof(buf2), buf3)) {
- fprintf(stderr, "ERROR: private RSA key does not work.\n");
- return 1;
- }
- if(memcmp(buf1, buf3, sizeof(buf1))) {
- fprintf(stderr, "ERROR: public and private RSA keys do not match.\n");
+
+ free(buf3);
+ free(buf2);
+ free(buf1);
+
+ if(!result) {
return 1;
}
+
}
} else {
- if(rsa_pub)
+ if(rsa_pub) {
fprintf(stderr, "WARNING: A public RSA key was found but no private key is known.\n");
+ }
}
+
#endif
ecdsa_t *ecdsa_pub = NULL;
f = fopen(fname, "r");
+
if(f) {
ecdsa_pub = get_pubkey(f);
+
if(!ecdsa_pub) {
rewind(f);
ecdsa_pub = ecdsa_read_pem_public_key(f);
}
+
fclose(f);
}
if(ecdsa_priv) {
if(!ecdsa_pub) {
fprintf(stderr, "WARNING: No (usable) public Ed25519 key found.\n");
+
if(ask_fix()) {
FILE *f = fopen(fname, "a");
+
if(f) {
- if(ecdsa_write_pem_public_key(ecdsa_priv, f))
+ if(ecdsa_write_pem_public_key(ecdsa_priv, f)) {
fprintf(stderr, "Wrote Ed25519 public key to %s.\n", fname);
- else
+ } else {
fprintf(stderr, "ERROR: could not write Ed25519 public key to %s.\n", fname);
+ }
+
fclose(f);
} else {
fprintf(stderr, "ERROR: could not append to %s: %s\n", fname, strerror(errno));
} else {
// TODO: suggest remedies
char *key1 = ecdsa_get_base64_public_key(ecdsa_pub);
+
if(!key1) {
fprintf(stderr, "ERROR: public Ed25519 key does not work.\n");
return 1;
}
+
char *key2 = ecdsa_get_base64_public_key(ecdsa_priv);
+
if(!key2) {
free(key1);
fprintf(stderr, "ERROR: private Ed25519 key does not work.\n");
return 1;
}
+
int result = strcmp(key1, key2);
free(key1);
free(key2);
+
if(result) {
fprintf(stderr, "ERROR: public and private Ed25519 keys do not match.\n");
return 1;
}
}
} else {
- if(ecdsa_pub)
+ if(ecdsa_pub) {
fprintf(stderr, "WARNING: A public Ed25519 key was found but no private key is known.\n");
+ }
}
// Check whether scripts are executable
struct dirent *ent;
DIR *dir = opendir(confbase);
+
if(!dir) {
fprintf(stderr, "ERROR: cannot read directory %s: %s\n", confbase, strerror(errno));
return 1;
}
while((ent = readdir(dir))) {
- if(strtailcmp(ent->d_name, "-up") && strtailcmp(ent->d_name, "-down"))
+ if(strtailcmp(ent->d_name, "-up") && strtailcmp(ent->d_name, "-down")) {
continue;
+ }
strncpy(fname, ent->d_name, sizeof(fname));
char *dash = strrchr(fname, '-');
- if(!dash)
+
+ if(!dash) {
continue;
+ }
+
*dash = 0;
if(strcmp(fname, "tinc") && strcmp(fname, "host") && strcmp(fname, "subnet")) {
static bool explained = false;
fprintf(stderr, "WARNING: Unknown script %s" SLASH "%s found.\n", confbase, ent->d_name);
+
if(!explained) {
fprintf(stderr, "The only scripts in %s executed by tinc are:\n", confbase);
fprintf(stderr, "tinc-up, tinc-down, host-up, host-down, subnet-up and subnet-down.\n");
explained = true;
}
+
continue;
}
snprintf(fname, sizeof(fname), "%s" SLASH "%s", confbase, ent->d_name);
+
if(access(fname, R_OK | X_OK)) {
if(errno != EACCES) {
fprintf(stderr, "ERROR: cannot access %s: %s\n", fname, strerror(errno));
continue;
}
+
fprintf(stderr, "WARNING: cannot read and execute %s: %s\n", fname, strerror(errno));
+
if(ask_fix()) {
- if(chmod(fname, 0755))
+ if(chmod(fname, 0755)) {
fprintf(stderr, "ERROR: cannot change permissions on %s: %s\n", fname, strerror(errno));
+ }
}
}
}
+
closedir(dir);
snprintf(dname, sizeof(dname), "%s" SLASH "hosts", confbase);
dir = opendir(dname);
+
if(!dir) {
fprintf(stderr, "ERROR: cannot read directory %s: %s\n", dname, strerror(errno));
return 1;
}
while((ent = readdir(dir))) {
- if(strtailcmp(ent->d_name, "-up") && strtailcmp(ent->d_name, "-down"))
+ if(strtailcmp(ent->d_name, "-up") && strtailcmp(ent->d_name, "-down")) {
continue;
+ }
strncpy(fname, ent->d_name, sizeof(fname));
char *dash = strrchr(fname, '-');
- if(!dash)
+
+ if(!dash) {
continue;
+ }
+
*dash = 0;
snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, ent->d_name);
+
if(access(fname, R_OK | X_OK)) {
if(errno != EACCES) {
fprintf(stderr, "ERROR: cannot access %s: %s\n", fname, strerror(errno));
continue;
}
+
fprintf(stderr, "WARNING: cannot read and execute %s: %s\n", fname, strerror(errno));
+
if(ask_fix()) {
- if(chmod(fname, 0755))
+ if(chmod(fname, 0755)) {
fprintf(stderr, "ERROR: cannot change permissions on %s: %s\n", fname, strerror(errno));
+ }
}
}
}
+
closedir(dir);
-
+
// Check for obsolete / unsafe / unknown configuration variables.
check_conffile(tinc_conf, true);
dir = opendir(dname);
+
if(dir) {
while((ent = readdir(dir))) {
- if(!check_id(ent->d_name))
+ if(!check_id(ent->d_name)) {
continue;
+ }
snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, ent->d_name);
check_conffile(fname, false);
}
+
closedir(dir);
}