conf.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602
  1. /*
  2. conf.c -- configuration code
  3. Copyright (C) 1998 Robert van der Meulen
  4. 1998-2005 Ivo Timmermans
  5. 2000-2014 Guus Sliepen <guus@tinc-vpn.org>
  6. 2010-2011 Julien Muchembled <jm@jmuchemb.eu>
  7. 2000 Cris van Pelt
  8. This program is free software; you can redistribute it and/or modify
  9. it under the terms of the GNU General Public License as published by
  10. the Free Software Foundation; either version 2 of the License, or
  11. (at your option) any later version.
  12. This program is distributed in the hope that it will be useful,
  13. but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  15. GNU General Public License for more details.
  16. You should have received a copy of the GNU General Public License along
  17. with this program; if not, write to the Free Software Foundation, Inc.,
  18. 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
  19. */
  20. #include "system.h"
  21. #include "avl_tree.h"
  22. #include "connection.h"
  23. #include "conf.h"
  24. #include "list.h"
  25. #include "logger.h"
  26. #include "netutl.h" /* for str2address */
  27. #include "protocol.h"
  28. #include "utils.h" /* for cp */
  29. #include "xalloc.h"
  30. avl_tree_t *config_tree;
  31. int pinginterval = 0; /* seconds between pings */
  32. int pingtimeout = 0; /* seconds to wait for response */
  33. char *confbase = NULL; /* directory in which all config files are */
  34. char *netname = NULL; /* name of the vpn network */
  35. list_t *cmdline_conf = NULL; /* global/host configuration values given at the command line */
  36. static int config_compare(const config_t *a, const config_t *b) {
  37. int result;
  38. result = strcasecmp(a->variable, b->variable);
  39. if(result) {
  40. return result;
  41. }
  42. /* give priority to command line options */
  43. result = !b->file - !a->file;
  44. if(result) {
  45. return result;
  46. }
  47. result = a->line - b->line;
  48. if(result) {
  49. return result;
  50. } else {
  51. return a->file ? strcmp(a->file, b->file) : 0;
  52. }
  53. }
  54. void init_configuration(avl_tree_t **config_tree) {
  55. *config_tree = avl_alloc_tree((avl_compare_t) config_compare, (avl_action_t) free_config);
  56. }
  57. void exit_configuration(avl_tree_t **config_tree) {
  58. avl_delete_tree(*config_tree);
  59. *config_tree = NULL;
  60. }
  61. config_t *new_config(void) {
  62. return xmalloc_and_zero(sizeof(config_t));
  63. }
  64. void free_config(config_t *cfg) {
  65. free(cfg->variable);
  66. free(cfg->value);
  67. free(cfg->file);
  68. free(cfg);
  69. }
  70. void config_add(avl_tree_t *config_tree, config_t *cfg) {
  71. avl_insert(config_tree, cfg);
  72. }
  73. config_t *lookup_config(const avl_tree_t *config_tree, char *variable) {
  74. config_t cfg, *found;
  75. cfg.variable = variable;
  76. cfg.file = NULL;
  77. cfg.line = 0;
  78. found = avl_search_closest_greater(config_tree, &cfg);
  79. if(!found) {
  80. return NULL;
  81. }
  82. if(strcasecmp(found->variable, variable)) {
  83. return NULL;
  84. }
  85. return found;
  86. }
  87. config_t *lookup_config_next(const avl_tree_t *config_tree, const config_t *cfg) {
  88. avl_node_t *node;
  89. config_t *found;
  90. node = avl_search_node(config_tree, cfg);
  91. if(node) {
  92. if(node->next) {
  93. found = node->next->data;
  94. if(!strcasecmp(found->variable, cfg->variable)) {
  95. return found;
  96. }
  97. }
  98. }
  99. return NULL;
  100. }
  101. bool get_config_bool(const config_t *cfg, bool *result) {
  102. if(!cfg) {
  103. return false;
  104. }
  105. if(!strcasecmp(cfg->value, "yes")) {
  106. *result = true;
  107. return true;
  108. } else if(!strcasecmp(cfg->value, "no")) {
  109. *result = false;
  110. return true;
  111. }
  112. logger(LOG_ERR, "\"yes\" or \"no\" expected for configuration variable %s in %s line %d",
  113. cfg->variable, cfg->file, cfg->line);
  114. return false;
  115. }
  116. bool get_config_int(const config_t *cfg, int *result) {
  117. if(!cfg) {
  118. return false;
  119. }
  120. if(sscanf(cfg->value, "%d", result) == 1) {
  121. return true;
  122. }
  123. logger(LOG_ERR, "Integer expected for configuration variable %s in %s line %d",
  124. cfg->variable, cfg->file, cfg->line);
  125. return false;
  126. }
  127. bool get_config_string(const config_t *cfg, char **result) {
  128. if(!cfg) {
  129. return false;
  130. }
  131. *result = xstrdup(cfg->value);
  132. return true;
  133. }
  134. bool get_config_address(const config_t *cfg, struct addrinfo **result) {
  135. struct addrinfo *ai;
  136. if(!cfg) {
  137. return false;
  138. }
  139. ai = str2addrinfo(cfg->value, NULL, 0);
  140. if(ai) {
  141. *result = ai;
  142. return true;
  143. }
  144. logger(LOG_ERR, "Hostname or IP address expected for configuration variable %s in %s line %d",
  145. cfg->variable, cfg->file, cfg->line);
  146. return false;
  147. }
  148. bool get_config_subnet(const config_t *cfg, subnet_t **result) {
  149. subnet_t subnet = {0};
  150. if(!cfg) {
  151. return false;
  152. }
  153. if(!str2net(&subnet, cfg->value)) {
  154. logger(LOG_ERR, "Subnet expected for configuration variable %s in %s line %d",
  155. cfg->variable, cfg->file, cfg->line);
  156. return false;
  157. }
  158. /* Teach newbies what subnets are... */
  159. if(((subnet.type == SUBNET_IPV4)
  160. && !maskcheck(&subnet.net.ipv4.address, subnet.net.ipv4.prefixlength, sizeof(ipv4_t)))
  161. || ((subnet.type == SUBNET_IPV6)
  162. && !maskcheck(&subnet.net.ipv6.address, subnet.net.ipv6.prefixlength, sizeof(ipv6_t)))) {
  163. logger(LOG_ERR, "Network address and prefix length do not match for configuration variable %s in %s line %d",
  164. cfg->variable, cfg->file, cfg->line);
  165. return false;
  166. }
  167. *(*result = new_subnet()) = subnet;
  168. return true;
  169. }
  170. /*
  171. Read exactly one line and strip the trailing newline if any.
  172. */
  173. static char *readline(FILE *fp, char *buf, size_t buflen) {
  174. char *newline = NULL;
  175. char *p;
  176. if(feof(fp)) {
  177. return NULL;
  178. }
  179. p = fgets(buf, buflen, fp);
  180. if(!p) {
  181. return NULL;
  182. }
  183. newline = strchr(p, '\n');
  184. if(!newline) {
  185. return buf;
  186. }
  187. *newline = '\0'; /* kill newline */
  188. if(newline > p && newline[-1] == '\r') { /* and carriage return if necessary */
  189. newline[-1] = '\0';
  190. }
  191. return buf;
  192. }
  193. config_t *parse_config_line(char *line, const char *fname, int lineno) {
  194. config_t *cfg;
  195. int len;
  196. char *variable, *value, *eol;
  197. variable = value = line;
  198. eol = line + strlen(line);
  199. while(strchr("\t ", *--eol)) {
  200. *eol = '\0';
  201. }
  202. len = strcspn(value, "\t =");
  203. value += len;
  204. value += strspn(value, "\t ");
  205. if(*value == '=') {
  206. value++;
  207. value += strspn(value, "\t ");
  208. }
  209. variable[len] = '\0';
  210. if(!*value) {
  211. const char err[] = "No value for variable";
  212. if(fname)
  213. logger(LOG_ERR, "%s `%s' on line %d while reading config file %s",
  214. err, variable, lineno, fname);
  215. else
  216. logger(LOG_ERR, "%s `%s' in command line option %d",
  217. err, variable, lineno);
  218. return NULL;
  219. }
  220. cfg = new_config();
  221. cfg->variable = xstrdup(variable);
  222. cfg->value = xstrdup(value);
  223. cfg->file = fname ? xstrdup(fname) : NULL;
  224. cfg->line = lineno;
  225. return cfg;
  226. }
  227. /*
  228. Parse a configuration file and put the results in the configuration tree
  229. starting at *base.
  230. */
  231. bool read_config_file(avl_tree_t *config_tree, const char *fname) {
  232. FILE *fp;
  233. char buffer[MAX_STRING_SIZE];
  234. char *line;
  235. int lineno = 0;
  236. bool ignore = false;
  237. config_t *cfg;
  238. bool result = false;
  239. fp = fopen(fname, "r");
  240. if(!fp) {
  241. logger(LOG_ERR, "Cannot open config file %s: %s", fname, strerror(errno));
  242. return false;
  243. }
  244. for(;;) {
  245. line = readline(fp, buffer, sizeof(buffer));
  246. if(!line) {
  247. if(feof(fp)) {
  248. result = true;
  249. }
  250. break;
  251. }
  252. lineno++;
  253. if(!*line || *line == '#') {
  254. continue;
  255. }
  256. if(ignore) {
  257. if(!strncmp(line, "-----END", 8)) {
  258. ignore = false;
  259. }
  260. continue;
  261. }
  262. if(!strncmp(line, "-----BEGIN", 10)) {
  263. ignore = true;
  264. continue;
  265. }
  266. cfg = parse_config_line(line, fname, lineno);
  267. if(!cfg) {
  268. break;
  269. }
  270. config_add(config_tree, cfg);
  271. }
  272. fclose(fp);
  273. return result;
  274. }
  275. void read_config_options(avl_tree_t *config_tree, const char *prefix) {
  276. size_t prefix_len = prefix ? strlen(prefix) : 0;
  277. for(const list_node_t *node = cmdline_conf->tail; node; node = node->prev) {
  278. const config_t *cfg = node->data;
  279. if(!prefix) {
  280. if(strchr(cfg->variable, '.')) {
  281. continue;
  282. }
  283. } else {
  284. if(strncmp(prefix, cfg->variable, prefix_len) ||
  285. cfg->variable[prefix_len] != '.') {
  286. continue;
  287. }
  288. }
  289. config_t *new = new_config();
  290. if(prefix) {
  291. new->variable = xstrdup(cfg->variable + prefix_len + 1);
  292. } else {
  293. new->variable = xstrdup(cfg->variable);
  294. }
  295. new->value = xstrdup(cfg->value);
  296. new->file = NULL;
  297. new->line = cfg->line;
  298. config_add(config_tree, new);
  299. }
  300. }
  301. bool read_server_config(void) {
  302. char fname[PATH_MAX];
  303. bool x;
  304. read_config_options(config_tree, NULL);
  305. snprintf(fname, sizeof(fname), "%s/tinc.conf", confbase);
  306. errno = 0;
  307. x = read_config_file(config_tree, fname);
  308. // We will try to read the conf files in the "conf.d" dir
  309. if(x) {
  310. char dname[PATH_MAX];
  311. snprintf(dname, sizeof(dname), "%s/conf.d", confbase);
  312. DIR *dir = opendir(dname);
  313. // If we can find this dir
  314. if(dir) {
  315. struct dirent *ep;
  316. // We list all the files in it
  317. while(x && (ep = readdir(dir))) {
  318. size_t l = strlen(ep->d_name);
  319. // And we try to read the ones that end with ".conf"
  320. if(l > 5 && !strcmp(".conf", & ep->d_name[ l - 5 ])) {
  321. if((size_t)snprintf(fname, sizeof(fname), "%s/%s", dname, ep->d_name) >= sizeof(fname)) {
  322. logger(LOG_ERR, "Pathname too long: %s/%s", dname, ep->d_name);
  323. return false;
  324. }
  325. x = read_config_file(config_tree, fname);
  326. }
  327. }
  328. closedir(dir);
  329. }
  330. }
  331. if(!x && errno) {
  332. logger(LOG_ERR, "Failed to read `%s': %s", fname, strerror(errno));
  333. }
  334. return x;
  335. }
  336. bool read_connection_config(connection_t *c) {
  337. char fname[PATH_MAX];
  338. bool x;
  339. read_config_options(c->config_tree, c->name);
  340. snprintf(fname, sizeof(fname), "%s/hosts/%s", confbase, c->name);
  341. x = read_config_file(c->config_tree, fname);
  342. return x;
  343. }
  344. static void disable_old_keys(const char *filename) {
  345. char tmpfile[PATH_MAX] = "";
  346. char buf[1024];
  347. bool disabled = false;
  348. FILE *r, *w;
  349. r = fopen(filename, "r");
  350. if(!r) {
  351. return;
  352. }
  353. int len = snprintf(tmpfile, sizeof(tmpfile), "%s.tmp", filename);
  354. if(len < 0 || len >= PATH_MAX) {
  355. fprintf(stderr, "Pathname too long: %s.tmp\n", filename);
  356. w = NULL;
  357. } else {
  358. w = fopen(tmpfile, "w");
  359. }
  360. while(fgets(buf, sizeof(buf), r)) {
  361. if(!strncmp(buf, "-----BEGIN RSA", 14)) {
  362. buf[11] = 'O';
  363. buf[12] = 'L';
  364. buf[13] = 'D';
  365. disabled = true;
  366. } else if(!strncmp(buf, "-----END RSA", 12)) {
  367. buf[ 9] = 'O';
  368. buf[10] = 'L';
  369. buf[11] = 'D';
  370. disabled = true;
  371. }
  372. if(w && fputs(buf, w) < 0) {
  373. disabled = false;
  374. break;
  375. }
  376. }
  377. if(w) {
  378. fclose(w);
  379. }
  380. fclose(r);
  381. if(!w && disabled) {
  382. fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
  383. return;
  384. }
  385. if(disabled) {
  386. #ifdef HAVE_MINGW
  387. // We cannot atomically replace files on Windows.
  388. char bakfile[PATH_MAX] = "";
  389. snprintf(bakfile, sizeof(bakfile), "%s.bak", filename);
  390. if(rename(filename, bakfile) || rename(tmpfile, filename)) {
  391. rename(bakfile, filename);
  392. #else
  393. if(rename(tmpfile, filename)) {
  394. #endif
  395. fprintf(stderr, "Warning: old key(s) found, remove them by hand!\n");
  396. } else {
  397. #ifdef HAVE_MINGW
  398. unlink(bakfile);
  399. #endif
  400. fprintf(stderr, "Warning: old key(s) found and disabled.\n");
  401. }
  402. }
  403. unlink(tmpfile);
  404. }
  405. FILE *ask_and_open(const char *filename, const char *what) {
  406. FILE *r;
  407. char directory[PATH_MAX];
  408. char line[PATH_MAX];
  409. char abspath[PATH_MAX];
  410. const char *fn;
  411. /* Check stdin and stdout */
  412. if(!isatty(0) || !isatty(1)) {
  413. /* Argh, they are running us from a script or something. Write
  414. the files to the current directory and let them burn in hell
  415. for ever. */
  416. fn = filename;
  417. } else {
  418. /* Ask for a file and/or directory name. */
  419. fprintf(stdout, "Please enter a file to save %s to [%s]: ",
  420. what, filename);
  421. fflush(stdout);
  422. fn = readline(stdin, line, sizeof(line));
  423. if(!fn) {
  424. fprintf(stderr, "Error while reading stdin: %s\n",
  425. strerror(errno));
  426. return NULL;
  427. }
  428. if(!strlen(fn))
  429. /* User just pressed enter. */
  430. {
  431. fn = filename;
  432. }
  433. }
  434. #ifdef HAVE_MINGW
  435. if(fn[0] != '\\' && fn[0] != '/' && !strchr(fn, ':')) {
  436. #else
  437. if(fn[0] != '/') {
  438. #endif
  439. /* The directory is a relative path or a filename. */
  440. getcwd(directory, sizeof(directory));
  441. if((size_t)snprintf(abspath, sizeof(abspath), "%s/%s", directory, fn) >= sizeof(abspath)) {
  442. fprintf(stderr, "Pathname too long: %s/%s\n", directory, fn);
  443. return NULL;
  444. }
  445. fn = abspath;
  446. }
  447. umask(0077); /* Disallow everything for group and other */
  448. disable_old_keys(fn);
  449. /* Open it first to keep the inode busy */
  450. r = fopen(fn, "a");
  451. if(!r) {
  452. fprintf(stderr, "Error opening file `%s': %s\n",
  453. fn, strerror(errno));
  454. return NULL;
  455. }
  456. return r;
  457. }