X-Git-Url: https://www.tinc-vpn.org/git/browse?p=tinc;a=blobdiff_plain;f=src%2Ffsck.c;h=e17b46c553432f0c0fa25aeddd939ee82b007472;hp=42f40f428558788a445edcb02b40465e1979f1e2;hb=f6e87ab476a0faf8b124ecaaa27f967d825e6457;hpb=3a316823b971396a428f020f401b9fe41252d98d diff --git a/src/fsck.c b/src/fsck.c index 42f40f42..e17b46c5 100644 --- a/src/fsck.c +++ b/src/fsck.c @@ -32,31 +32,43 @@ #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); @@ -67,13 +79,17 @@ static void print_tinc_cmd(const char *argv0, const char *format, ...) { 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) { FILE *f = fopen(fname, "r"); + if(!f) { fprintf(stderr, "ERROR: cannot read %s: %s\n", fname, strerror(errno)); return; @@ -88,8 +104,10 @@ static void check_conffile(const char *fname, bool server) { 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)) { @@ -105,26 +123,32 @@ static void check_conffile(const char *fname, bool server) { 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; @@ -132,24 +156,29 @@ static void check_conffile(const char *fname, bool server) { 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); } @@ -165,19 +194,23 @@ int fsck(const char *argv0) { 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; @@ -203,12 +236,15 @@ int fsck(const char *argv0) { } } 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"); @@ -217,19 +253,24 @@ int fsck(const char *argv0) { } #if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN) + 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; @@ -244,12 +285,15 @@ int fsck(const char *argv0) { } } 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"); @@ -258,24 +302,30 @@ int fsck(const char *argv0) { } #if !defined(HAVE_MINGW) && !defined(HAVE_CYGWIN) + 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 @@ -288,8 +338,10 @@ int fsck(const char *argv0) { // 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; @@ -297,6 +349,7 @@ int fsck(const char *argv0) { rsa_t *rsa_pub = NULL; f = fopen(fname, "r"); + if(f) { rsa_pub = rsa_read_pem_public_key(f); fclose(f); @@ -305,13 +358,17 @@ int fsck(const char *argv0) { 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)); @@ -320,56 +377,70 @@ int fsck(const char *argv0) { } 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)); buf1[0] &= 0x7f; memset(buf2, 0, sizeof(buf2)); memset(buf3, 0, sizeof(buf2)); + if(!rsa_public_encrypt(rsa_pub, buf1, sizeof(buf1), buf2)) { 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"); 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)); @@ -378,119 +449,150 @@ int fsck(const char *argv0) { } 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); }