Fix more memory leaks found by ASAN.
[tinc] / src / fsck.c
1 /*
2     fsck.c -- Check the configuration files for problems
3     Copyright (C) 2014-2021 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
22 #include "crypto.h"
23 #include "ecdsa.h"
24 #include "ecdsagen.h"
25 #include "fsck.h"
26 #include "names.h"
27 #ifndef DISABLE_LEGACY
28 #include "rsa.h"
29 #include "rsagen.h"
30 #endif
31 #include "tincctl.h"
32 #include "utils.h"
33
34 static bool ask_fix(void) {
35         if(force) {
36                 return true;
37         }
38
39         if(!tty) {
40                 return false;
41         }
42
43 again:
44         fprintf(stderr, "Fix y/n? ");
45         char buf[1024];
46
47         if(!fgets(buf, sizeof(buf), stdin)) {
48                 tty = false;
49                 return false;
50         }
51
52         if(buf[0] == 'y' || buf[0] == 'Y') {
53                 return true;
54         }
55
56         if(buf[0] == 'n' || buf[0] == 'N') {
57                 return false;
58         }
59
60         goto again;
61 }
62
63 static void print_tinc_cmd(const char *argv0, const char *format, ...) {
64         if(confbasegiven) {
65                 fprintf(stderr, "%s -c %s ", argv0, confbase);
66         } else if(netname) {
67                 fprintf(stderr, "%s -n %s ", argv0, netname);
68         } else {
69                 fprintf(stderr, "%s ", argv0);
70         }
71
72         va_list va;
73         va_start(va, format);
74         vfprintf(stderr, format, va);
75         va_end(va);
76         fputc('\n', stderr);
77 }
78
79 static int strtailcmp(const char *str, const char *tail) {
80         size_t slen = strlen(str);
81         size_t tlen = strlen(tail);
82
83         if(tlen > slen) {
84                 return -1;
85         }
86
87         return memcmp(str + slen - tlen, tail, tlen);
88 }
89
90 static void check_conffile(const char *fname, bool server) {
91         (void)server;
92
93         FILE *f = fopen(fname, "r");
94
95         if(!f) {
96                 fprintf(stderr, "ERROR: cannot read %s: %s\n", fname, strerror(errno));
97                 return;
98         }
99
100         char line[2048];
101         int lineno = 0;
102         bool skip = false;
103         const int maxvariables = 50;
104         int count[maxvariables];
105         memset(count, 0, sizeof(count));
106
107         while(fgets(line, sizeof(line), f)) {
108                 if(skip) {
109                         if(!strncmp(line, "-----END", 8)) {
110                                 skip = false;
111                         }
112
113                         continue;
114                 } else {
115                         if(!strncmp(line, "-----BEGIN", 10)) {
116                                 skip = true;
117                                 continue;
118                         }
119                 }
120
121                 int len;
122                 char *variable, *value, *eol;
123                 variable = value = line;
124
125                 lineno++;
126
127                 eol = line + strlen(line);
128
129                 while(strchr("\t \r\n", *--eol)) {
130                         *eol = '\0';
131                 }
132
133                 if(!line[0] || line[0] == '#') {
134                         continue;
135                 }
136
137                 len = strcspn(value, "\t =");
138                 value += len;
139                 value += strspn(value, "\t ");
140
141                 if(*value == '=') {
142                         value++;
143                         value += strspn(value, "\t ");
144                 }
145
146                 variable[len] = '\0';
147
148                 bool found = false;
149
150                 for(int i = 0; variables[i].name; i++) {
151                         if(strcasecmp(variables[i].name, variable)) {
152                                 continue;
153                         }
154
155                         found = true;
156
157                         if(variables[i].type & VAR_OBSOLETE) {
158                                 fprintf(stderr, "WARNING: obsolete variable %s in %s line %d\n", variable, fname, lineno);
159                         }
160
161                         if(i < maxvariables) {
162                                 count[i]++;
163                         }
164                 }
165
166                 if(!found) {
167                         fprintf(stderr, "WARNING: unknown variable %s in %s line %d\n", variable, fname, lineno);
168                 }
169
170                 if(!*value) {
171                         fprintf(stderr, "ERROR: no value for variable %s in %s line %d\n", variable, fname, lineno);
172                 }
173         }
174
175         for(int i = 0; variables[i].name && i < maxvariables; i++) {
176                 if(count[i] > 1 && !(variables[i].type & VAR_MULTIPLE)) {
177                         fprintf(stderr, "WARNING: multiple instances of variable %s in %s\n", variables[i].name, fname);
178                 }
179         }
180
181         if(ferror(f)) {
182                 fprintf(stderr, "ERROR: while reading %s: %s\n", fname, strerror(errno));
183         }
184
185         fclose(f);
186 }
187
188 int fsck(const char *argv0) {
189 #ifdef HAVE_MINGW
190         int uid = 0;
191 #else
192         uid_t uid = getuid();
193 #endif
194
195         // Check that tinc.conf is readable.
196
197         if(access(tinc_conf, R_OK)) {
198                 fprintf(stderr, "ERROR: cannot read %s: %s\n", tinc_conf, strerror(errno));
199
200                 if(errno == ENOENT) {
201                         fprintf(stderr, "No tinc configuration found. Create a new one with:\n\n");
202                         print_tinc_cmd(argv0, "init");
203                 } else if(errno == EACCES) {
204                         if(uid != 0) {
205                                 fprintf(stderr, "You are currently not running tinc as root. Use sudo?\n");
206                         } else {
207                                 fprintf(stderr, "Check the permissions of each component of the path %s.\n", tinc_conf);
208                         }
209                 }
210
211                 return 1;
212         }
213
214         char *name = get_my_name(true);
215
216         if(!name) {
217                 fprintf(stderr, "ERROR: tinc cannot run without a valid Name.\n");
218                 return 1;
219         }
220
221         // Check for private keys.
222         // TODO: use RSAPrivateKeyFile and Ed25519PrivateKeyFile variables if present.
223
224         struct stat st;
225         char fname[PATH_MAX];
226         char dname[PATH_MAX];
227
228 #ifndef DISABLE_LEGACY
229         rsa_t *rsa_priv = NULL;
230         snprintf(fname, sizeof(fname), "%s/rsa_key.priv", confbase);
231
232         if(stat(fname, &st)) {
233                 if(errno != ENOENT) {
234                         // 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.
235                         fprintf(stderr, "ERROR: cannot read %s: %s\n", fname, strerror(errno));
236                         fprintf(stderr, "Please correct this error.\n");
237                         free(name);
238                         return 1;
239                 }
240         } else {
241                 FILE *f = fopen(fname, "r");
242
243                 if(!f) {
244                         fprintf(stderr, "ERROR: could not open %s: %s\n", fname, strerror(errno));
245                         free(name);
246                         return 1;
247                 }
248
249                 rsa_priv = rsa_read_pem_private_key(f);
250                 fclose(f);
251
252                 if(!rsa_priv) {
253                         fprintf(stderr, "ERROR: No key or unusable key found in %s.\n", fname);
254                         fprintf(stderr, "You can generate a new RSA key with:\n\n");
255                         print_tinc_cmd(argv0, "generate-rsa-keys");
256                         free(name);
257                         return 1;
258                 }
259
260 #ifndef HAVE_MINGW
261
262                 if(st.st_mode & 077) {
263                         fprintf(stderr, "WARNING: unsafe file permissions on %s.\n", fname);
264
265                         if(st.st_uid != uid) {
266                                 fprintf(stderr, "You are not running %s as the same uid as %s.\n", argv0, fname);
267                         } else if(ask_fix()) {
268                                 if(chmod(fname, st.st_mode & ~077)) {
269                                         fprintf(stderr, "ERROR: could not change permissions of %s: %s\n", fname, strerror(errno));
270                                 } else {
271                                         fprintf(stderr, "Fixed permissions of %s.\n", fname);
272                                 }
273                         }
274                 }
275
276 #endif
277         }
278
279 #endif
280
281         ecdsa_t *ecdsa_priv = NULL;
282         snprintf(fname, sizeof(fname), "%s/ed25519_key.priv", confbase);
283
284         if(stat(fname, &st)) {
285                 if(errno != ENOENT) {
286                         // 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.
287                         fprintf(stderr, "ERROR: cannot read %s: %s\n", fname, strerror(errno));
288                         fprintf(stderr, "Please correct this error.\n");
289                         free(name);
290                         return 1;
291                 }
292         } else {
293                 FILE *f = fopen(fname, "r");
294
295                 if(!f) {
296                         fprintf(stderr, "ERROR: could not open %s: %s\n", fname, strerror(errno));
297                         free(name);
298                         return 1;
299                 }
300
301                 ecdsa_priv = ecdsa_read_pem_private_key(f);
302                 fclose(f);
303
304                 if(!ecdsa_priv) {
305                         fprintf(stderr, "ERROR: No key or unusable key found in %s.\n", fname);
306                         fprintf(stderr, "You can generate a new Ed25519 key with:\n\n");
307                         print_tinc_cmd(argv0, "generate-ed25519-keys");
308                         free(name);
309                         return 1;
310                 }
311
312 #ifndef HAVE_MINGW
313
314                 if(st.st_mode & 077) {
315                         fprintf(stderr, "WARNING: unsafe file permissions on %s.\n", fname);
316
317                         if(st.st_uid != uid) {
318                                 fprintf(stderr, "You are not running %s as the same uid as %s.\n", argv0, fname);
319                         } else if(ask_fix()) {
320                                 if(chmod(fname, st.st_mode & ~077)) {
321                                         fprintf(stderr, "ERROR: could not change permissions of %s: %s\n", fname, strerror(errno));
322                                 } else {
323                                         fprintf(stderr, "Fixed permissions of %s.\n", fname);
324                                 }
325                         }
326                 }
327
328 #endif
329         }
330
331 #ifdef DISABLE_LEGACY
332
333         if(!ecdsa_priv) {
334                 fprintf(stderr, "ERROR: No Ed25519 private key found.\n");
335 #else
336
337         if(!rsa_priv && !ecdsa_priv) {
338                 fprintf(stderr, "ERROR: Neither RSA or Ed25519 private key found.\n");
339 #endif
340                 fprintf(stderr, "You can generate new keys with:\n\n");
341                 print_tinc_cmd(argv0, "generate-keys");
342                 free(name);
343                 return 1;
344         }
345
346         // Check for public keys.
347         // TODO: use RSAPublicKeyFile variable if present.
348
349         snprintf(fname, sizeof(fname), "%s/hosts/%s", confbase, name);
350
351         free(name);
352
353         if(access(fname, R_OK)) {
354                 fprintf(stderr, "WARNING: cannot read %s\n", fname);
355         }
356
357         FILE *f;
358
359 #ifndef DISABLE_LEGACY
360         rsa_t *rsa_pub = NULL;
361
362         f = fopen(fname, "r");
363
364         if(f) {
365                 rsa_pub = rsa_read_pem_public_key(f);
366                 fclose(f);
367         }
368
369         if(rsa_priv) {
370                 if(!rsa_pub) {
371                         fprintf(stderr, "WARNING: No (usable) public RSA key found.\n");
372
373                         if(ask_fix()) {
374                                 FILE *f = fopen(fname, "a");
375
376                                 if(f) {
377                                         if(rsa_write_pem_public_key(rsa_priv, f)) {
378                                                 fprintf(stderr, "Wrote RSA public key to %s.\n", fname);
379                                         } else {
380                                                 fprintf(stderr, "ERROR: could not write RSA public key to %s.\n", fname);
381                                         }
382
383                                         fclose(f);
384                                 } else {
385                                         fprintf(stderr, "ERROR: could not append to %s: %s\n", fname, strerror(errno));
386                                 }
387                         }
388                 } else {
389                         // TODO: suggest remedies
390                         size_t len = rsa_size(rsa_priv);
391
392                         if(len != rsa_size(rsa_pub)) {
393                                 fprintf(stderr, "ERROR: public and private RSA keys do not match.\n");
394                                 rsa_free(rsa_pub);
395                                 rsa_free(rsa_priv);
396                                 free(ecdsa_priv);
397                                 return 1;
398                         }
399
400                         char *buf1 = malloc(len);
401                         char *buf2 = malloc(len);
402                         char *buf3 = malloc(len);
403
404                         randomize(buf1, len);
405                         buf1[0] &= 0x7f;
406                         memset(buf2, 0, len);
407                         memset(buf3, 0, len);
408                         bool result = false;
409
410                         if(rsa_public_encrypt(rsa_pub, buf1, len, buf2)) {
411                                 if(rsa_private_decrypt(rsa_priv, buf2, len, buf3)) {
412                                         if(memcmp(buf1, buf3, len)) {
413                                                 result = true;
414                                         } else {
415                                                 fprintf(stderr, "ERROR: public and private RSA keys do not match.\n");
416                                         }
417                                 } else {
418                                         fprintf(stderr, "ERROR: private RSA key does not work.\n");
419                                 }
420                         } else {
421                                 fprintf(stderr, "ERROR: public RSA key does not work.\n");
422                         }
423
424
425                         rsa_free(rsa_pub);
426                         rsa_pub = NULL;
427
428                         free(buf3);
429                         free(buf2);
430                         free(buf1);
431
432                         if(!result) {
433                                 rsa_free(rsa_priv);
434                                 free(ecdsa_priv);
435                                 return 1;
436                         }
437                 }
438
439                 rsa_free(rsa_priv);
440                 rsa_priv = NULL;
441         } else {
442                 if(rsa_pub) {
443                         fprintf(stderr, "WARNING: A public RSA key was found but no private key is known.\n");
444                         rsa_free(rsa_pub);
445                         rsa_pub = NULL;
446                 }
447         }
448
449 #endif
450
451         ecdsa_t *ecdsa_pub = NULL;
452
453         f = fopen(fname, "r");
454
455         if(f) {
456                 ecdsa_pub = get_pubkey(f);
457
458                 if(!ecdsa_pub) {
459                         rewind(f);
460                         ecdsa_pub = ecdsa_read_pem_public_key(f);
461                 }
462
463                 fclose(f);
464         }
465
466         if(ecdsa_priv) {
467                 if(!ecdsa_pub) {
468                         fprintf(stderr, "WARNING: No (usable) public Ed25519 key found.\n");
469
470                         if(ask_fix()) {
471                                 FILE *f = fopen(fname, "a");
472
473                                 if(f) {
474                                         if(ecdsa_write_pem_public_key(ecdsa_priv, f)) {
475                                                 fprintf(stderr, "Wrote Ed25519 public key to %s.\n", fname);
476                                         } else {
477                                                 fprintf(stderr, "ERROR: could not write Ed25519 public key to %s.\n", fname);
478                                         }
479
480                                         fclose(f);
481                                 } else {
482                                         fprintf(stderr, "ERROR: could not append to %s: %s\n", fname, strerror(errno));
483                                 }
484                         }
485                 } else {
486                         // TODO: suggest remedies
487                         char *key1 = ecdsa_get_base64_public_key(ecdsa_pub);
488
489                         free(ecdsa_pub);
490                         ecdsa_pub = NULL;
491
492                         if(!key1) {
493                                 fprintf(stderr, "ERROR: public Ed25519 key does not work.\n");
494                                 free(ecdsa_priv);
495                                 return 1;
496                         }
497
498                         char *key2 = ecdsa_get_base64_public_key(ecdsa_priv);
499
500                         if(!key2) {
501                                 fprintf(stderr, "ERROR: private Ed25519 key does not work.\n");
502                                 free(ecdsa_priv);
503                                 free(key1);
504                                 return 1;
505                         }
506
507                         int result = strcmp(key1, key2);
508                         free(key1);
509                         free(key2);
510
511                         if(result) {
512                                 fprintf(stderr, "ERROR: public and private Ed25519 keys do not match.\n");
513                                 free(ecdsa_priv);
514                                 return 1;
515                         }
516                 }
517
518                 free(ecdsa_priv);
519                 ecdsa_priv = NULL;
520         } else {
521                 if(ecdsa_pub) {
522                         fprintf(stderr, "WARNING: A public Ed25519 key was found but no private key is known.\n");
523                         free(ecdsa_pub);
524                         ecdsa_pub = NULL;
525                 }
526         }
527
528         // Check whether scripts are executable
529
530         struct dirent *ent;
531         DIR *dir = opendir(confbase);
532
533         if(!dir) {
534                 fprintf(stderr, "ERROR: cannot read directory %s: %s\n", confbase, strerror(errno));
535                 return 1;
536         }
537
538         while((ent = readdir(dir))) {
539                 if(strtailcmp(ent->d_name, "-up") && strtailcmp(ent->d_name, "-down")) {
540                         continue;
541                 }
542
543                 strncpy(fname, ent->d_name, sizeof(fname));
544                 char *dash = strrchr(fname, '-');
545
546                 if(!dash) {
547                         continue;
548                 }
549
550                 *dash = 0;
551
552                 if(strcmp(fname, "tinc") && strcmp(fname, "host") && strcmp(fname, "subnet")) {
553                         static bool explained = false;
554                         fprintf(stderr, "WARNING: Unknown script %s" SLASH "%s found.\n", confbase, ent->d_name);
555
556                         if(!explained) {
557                                 fprintf(stderr, "The only scripts in %s executed by tinc are:\n", confbase);
558                                 fprintf(stderr, "tinc-up, tinc-down, host-up, host-down, subnet-up and subnet-down.\n");
559                                 explained = true;
560                         }
561
562                         continue;
563                 }
564
565                 snprintf(fname, sizeof(fname), "%s" SLASH "%s", confbase, ent->d_name);
566
567                 if(access(fname, R_OK | X_OK)) {
568                         if(errno != EACCES) {
569                                 fprintf(stderr, "ERROR: cannot access %s: %s\n", fname, strerror(errno));
570                                 continue;
571                         }
572
573                         fprintf(stderr, "WARNING: cannot read and execute %s: %s\n", fname, strerror(errno));
574
575                         if(ask_fix()) {
576                                 if(chmod(fname, 0755)) {
577                                         fprintf(stderr, "ERROR: cannot change permissions on %s: %s\n", fname, strerror(errno));
578                                 }
579                         }
580                 }
581         }
582
583         closedir(dir);
584
585         snprintf(dname, sizeof(dname), "%s" SLASH "hosts", confbase);
586         dir = opendir(dname);
587
588         if(!dir) {
589                 fprintf(stderr, "ERROR: cannot read directory %s: %s\n", dname, strerror(errno));
590                 return 1;
591         }
592
593         while((ent = readdir(dir))) {
594                 if(strtailcmp(ent->d_name, "-up") && strtailcmp(ent->d_name, "-down")) {
595                         continue;
596                 }
597
598                 strncpy(fname, ent->d_name, sizeof(fname));
599                 char *dash = strrchr(fname, '-');
600
601                 if(!dash) {
602                         continue;
603                 }
604
605                 *dash = 0;
606
607                 snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, ent->d_name);
608
609                 if(access(fname, R_OK | X_OK)) {
610                         if(errno != EACCES) {
611                                 fprintf(stderr, "ERROR: cannot access %s: %s\n", fname, strerror(errno));
612                                 continue;
613                         }
614
615                         fprintf(stderr, "WARNING: cannot read and execute %s: %s\n", fname, strerror(errno));
616
617                         if(ask_fix()) {
618                                 if(chmod(fname, 0755)) {
619                                         fprintf(stderr, "ERROR: cannot change permissions on %s: %s\n", fname, strerror(errno));
620                                 }
621                         }
622                 }
623         }
624
625         closedir(dir);
626
627         // Check for obsolete / unsafe / unknown configuration variables.
628
629         check_conffile(tinc_conf, true);
630
631         dir = opendir(dname);
632
633         if(dir) {
634                 while((ent = readdir(dir))) {
635                         if(!check_id(ent->d_name)) {
636                                 continue;
637                         }
638
639                         snprintf(fname, sizeof(fname), "%s" SLASH "hosts" SLASH "%s", confbase, ent->d_name);
640                         check_conffile(fname, false);
641                 }
642
643                 closedir(dir);
644         }
645
646         return 0;
647 }
648