]> the.earth.li Git - onak.git/blob - onak-conf.c
Add ability to drop overly large packets
[onak.git] / onak-conf.c
1 /*
2  * onak-conf.c - Routines related to runtime config.
3  *
4  * Copyright 2002,2012 Jonathan McDowell <noodles@earth.li>
5  *
6  * This program is free software: you can redistribute it and/or modify it
7  * under the terms of the GNU General Public License as published by the Free
8  * Software Foundation; version 2 of the License.
9  *
10  * This program is distributed in the hope that it will be useful, but WITHOUT
11  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
13  * more details.
14  *
15  * You should have received a copy of the GNU General Public License along with
16  * this program.  If not, see <https://www.gnu.org/licenses/>.
17  */
18
19 #include "config.h"
20
21 #include <ctype.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <string.h>
25 #include <strings.h>
26
27 #include "cleankey.h"
28 #include "ll.h"
29 #include "log.h"
30 #include "onak-conf.h"
31
32 extern struct onak_dbctx *DBINIT(struct onak_db_config *dbcfg, bool readonly);
33
34 /*
35  *      config - Runtime configuration for onak.
36  *
37  *      This is the default config; normally overridden with values from the
38  *      config file.
39  */
40 struct onak_config config = {
41         .maxkeys = 128,
42         .thissite = NULL,
43         .adminemail = NULL,
44         .mta = NULL,
45         .syncsites = NULL,
46         .logfile = NULL,
47
48         .use_keyd = false,
49         .sock_dir = NULL,
50
51         .backends = NULL,
52         .backends_dir = NULL,
53
54         .dbinit = DBINIT,
55
56         .clean_policies = ONAK_CLEAN_CHECK_SIGHASH,
57
58         .bin_dir = NULL,
59         .mail_dir = NULL,
60 };
61
62 struct onak_db_config *find_db_backend_config(struct ll *backends, char *name)
63 {
64         struct ll *cur;
65
66         cur = backends;
67         while (cur != NULL && strcmp(name,
68                         ((struct onak_db_config *) cur->object)->name)) {
69                 cur = cur->next;
70         }
71
72         return (cur != NULL) ? (struct onak_db_config *) cur->object : NULL;
73 }
74
75 bool parsebool(char *str, bool fallback)
76 {
77         if (!strcasecmp(str, "false") || !strcasecmp(str, "no") ||
78                         !strcasecmp(str, "0")) {
79                 return false;
80         } else if (!strcasecmp(str, "true") || !strcasecmp(str, "yes") ||
81                         !strcasecmp(str, "1")) {
82                 return true;
83         } else {
84                 logthing(LOGTHING_CRITICAL,
85                         "Couldn't parse %s as a boolean config variable, "
86                         "returning fallback of '%s'.",
87                         str,
88                         fallback ? "true" : "false");
89                 return fallback;
90         }
91 }
92
93 /*
94  * Parse an old pksd style config line, as used in onak 0.4.6 and earlier.
95  */
96 static bool parseoldconfigline(char *line)
97 {
98         if (line[0] == '#' || line[0] == 0) {
99                 /*
100                  * Comment line, ignore.
101                  */
102         } else if (!strncmp("db_dir ", line, 7)) {
103                 config.backend->location = strdup(&line[7]);
104         } else if (!strncmp("debug ", line, 6)) {
105                 /*
106                  * Not supported yet; ignore for compatibility with
107                  * pksd.
108                  */
109         } else if (!strncmp("default_language ", line, 17)) {
110                 /*
111                  * Not supported yet; ignore for compatibility with
112                  * pksd.
113                  */
114         } else if (!strncmp("mail_delivery_client ", line, 21)) {
115                 config.mta = strdup(&line[21]);
116         } else if (!strncmp("maintainer_email ", line, 17)) {
117                 config.adminemail = strdup(&line[17]);
118         } else if (!strncmp("mail_intro_file ", line, 16)) {
119                 /*
120                  * Not supported yet; ignore for compatibility with
121                  * pksd.
122                  */
123         } else if (!strncmp("help_dir ", line, 9)) {
124                 /*
125                  * Not supported yet; ignore for compatibility with
126                  * pksd.
127                  */
128         } else if (!strncmp("max_last ", line, 9)) {
129                 /*
130                  * Not supported yet; ignore for compatibility with
131                  * pksd.
132                  */
133         } else if (!strncmp("max_reply_keys ", line, 15)) {
134                 config.maxkeys = atoi(&line[15]);
135         } else if (!strncmp("pg_dbhost ", line, 10)) {
136                 config.backend->hostname = strdup(&line[10]);
137         } else if (!strncmp("pg_dbname ", line, 10)) {
138                 config.backend->location = strdup(&line[10]);
139         } else if (!strncmp("pg_dbuser ", line, 10)) {
140                 config.backend->username = strdup(&line[10]);
141         } else if (!strncmp("pg_dbpass ", line, 10)) {
142                 config.backend->password = strdup(&line[10]);
143         } else if (!strncmp("syncsite ", line, 9)) {
144                 config.syncsites =
145                         lladd(config.syncsites, strdup(&line[9]));
146         } else if (!strncmp("logfile ", line, 8)) {
147                 config.logfile = strdup(&line[8]);
148         } else if (!strncmp("loglevel ", line, 9)) {
149                 setlogthreshold(atoi(&line[9]));
150         } else if (!strncmp("this_site ", line, 10)) {
151                 config.thissite = strdup(&line[10]);
152         } else if (!strncmp("socket_name ", line, 12) ||
153                         !strncmp("www_port ", line, 9)) {
154                 /*
155                  * Not applicable; ignored for compatibility with pksd.
156                  */
157         } else if (!strncmp("pks_bin_dir ", line, 12)) {
158                 config.bin_dir = strdup(&line[12]);
159         } else if (!strncmp("mail_dir ", line, 9)) {
160                 config.mail_dir = strdup(&line[9]);
161         } else if (!strncmp("db_backend ", line, 11)) {
162                 config.backend->type = strdup(&line[11]);
163                 config.backend->name = strdup(&line[11]);
164                 config.db_backend = strdup(&line[11]);
165         } else if (!strncmp("backends_dir ", line, 13)) {
166                 config.backends_dir = strdup(&line[13]);
167         } else if (!strncmp("use_keyd ", line, 9)) {
168                 config.use_keyd = parsebool(&line[9],
169                                         config.use_keyd);
170         } else if (!strncmp("sock_dir ", line, 9)) {
171                 config.sock_dir = strdup(&line[9]);
172         } else if (!strncmp("check_sighash ", line, 9)) {
173                 if (parsebool(&line[9], config.clean_policies &
174                                         ONAK_CLEAN_CHECK_SIGHASH)) {
175                         config.clean_policies |=
176                                 ONAK_CLEAN_CHECK_SIGHASH;
177                 } else {
178                         config.clean_policies &=
179                                 ~ONAK_CLEAN_CHECK_SIGHASH;
180                 }
181         } else {
182                 return false;
183         }
184
185         return true;
186 }
187
188 /*
189  * Parse a new style .ini config line, supporting [sections] and name=value
190  * format.
191  */
192 static bool parseconfigline(char *line)
193 {
194         /* Yes, this means we're not re-entrant. */
195         static char section[32] = "";
196         struct onak_db_config *backend;
197         size_t len;
198         char *name, *value;
199
200         if (line[0] == '#' || line[0] == ';' ||
201                         line[0] == 0) {
202                 /*
203                  * Comment line, ignore.
204                  */
205         } else if (line[0] == '[') {
206                 /* Section name */
207                 len = strlen(line);
208                 if (line[len - 1] != ']') {
209                         logthing(LOGTHING_CRITICAL,
210                                 "Malformed section header '%s' in "
211                                 "config file.", line);
212                         return false;
213                 }
214                 if (len > sizeof(section)) {
215                         logthing(LOGTHING_CRITICAL,
216                                 "Section header '%s' is too long in "
217                                 "config file.", line);
218                         return false;
219                 }
220                 line[len - 1] = 0;
221                 strncpy(section, &line[1], len);
222         } else if ((value = strchr(line, '=')) != NULL) {
223                 name = line;
224                 *value++ = 0;
225
226                 /* We can have multiple backend: sections */
227                 if (!strncmp(section, "backend:", 8)) {
228                         backend = find_db_backend_config(
229                                 config.backends, &section[8]);
230                         if (backend == NULL) {
231                                 backend = calloc(1,
232                                         sizeof(struct onak_db_config));
233                                 backend->name = strdup(&section[8]);
234                                 config.backends = lladd(config.backends,
235                                                         backend);
236                         }
237
238                         if (!strcmp(name, "type")) {
239                                 backend->type = strdup(value);
240                         } else if (!strcmp(name, "location")) {
241                                 backend->location = strdup(value);
242                         } else if (!strcmp(name, "hostname")) {
243                                 backend->location = strdup(value);
244                         } else if (!strcmp(name, "username")) {
245                                 backend->location = strdup(value);
246                         } else if (!strcmp(name, "password")) {
247                                 backend->location = strdup(value);
248                         }
249
250 #define MATCH(s, n) !strcmp(section, s) && !strcmp(name, n)
251                 /* [main] section */
252                 } else if (MATCH("main", "backend")) {
253                         config.db_backend = strdup(value);
254                 } else if (MATCH("main", "backends_dir")) {
255                         config.backends_dir = strdup(value);
256                 } else if (MATCH("main", "logfile")) {
257                         config.logfile = strdup(value);
258                 } else if (MATCH("main", "loglevel")) {
259                         setlogthreshold(atoi(value));
260                 } else if (MATCH("main", "use_keyd")) {
261                         config.use_keyd = parsebool(value,
262                                         config.use_keyd);
263                 } else if (MATCH("main", "sock_dir")) {
264                         config.sock_dir = strdup(value);
265                 } else if (MATCH("main", "max_reply_keys")) {
266                         config.maxkeys = atoi(value);
267                 /* [mail] section */
268                 } else if (MATCH("mail", "maintainer_email")) {
269                         config.adminemail = strdup(value);
270                 } else if (MATCH("mail", "mail_dir")) {
271                 config.mail_dir = strdup(value);
272                 } else if (MATCH("mail", "mta")) {
273                         config.mta = strdup(value);
274                 } else if (MATCH("mail", "bin_dir")) {
275                         config.bin_dir = strdup(value);
276                 } else if (MATCH("mail", "this_site")) {
277                         config.thissite = strdup(value);
278                 } else if (MATCH("mail", "syncsite")) {
279                         config.syncsites = lladd(config.syncsites,
280                                 strdup(value));
281                 /* [verification] section */
282                 } else if (MATCH("verification", "check_sighash")) {
283                         if (parsebool(value, config.clean_policies &
284                                         ONAK_CLEAN_CHECK_SIGHASH)) {
285                                 config.clean_policies |=
286                                         ONAK_CLEAN_CHECK_SIGHASH;
287                         } else {
288                                 config.clean_policies &=
289                                         ~ONAK_CLEAN_CHECK_SIGHASH;
290                         }
291                 } else if (MATCH("verification", "check_packet_size")) {
292                         if (parsebool(value, config.clean_policies &
293                                         ONAK_CLEAN_LARGE_PACKETS)) {
294                                 config.clean_policies |=
295                                         ONAK_CLEAN_LARGE_PACKETS;
296                         } else {
297                                 config.clean_policies &=
298                                         ~ONAK_CLEAN_LARGE_PACKETS;
299                         }
300                 } else {
301                         return false;
302                 }
303         } else {
304                 return false;
305         }
306
307         return true;
308 }
309
310 void readconfig(const char *configfile) {
311         FILE *conffile;
312         char  curline[1024];
313         int   i;
314         char *dir, *conf;
315         size_t len;
316         struct onak_db_config *backend;
317         bool oldstyle = false;
318         bool res;
319
320         curline[1023] = 0;
321
322         /*
323          * Try to find a config file to use. If one is explicitly provided,
324          * use that. Otherwise look in $XDG_CONFIG_HOME, $HOME and finally
325          * the build in configuration directory. We try an old style onak.conf
326          * first and then the new style onak.ini if that wasn't found - this
327          * is to prevent breaking existing installs on upgrade.
328          */
329         if (configfile == NULL) {
330                 conffile = NULL;
331                 if ((dir = getenv("XDG_CONFIG_HOME")) != NULL) {
332                         /* dir + / + onak.conf + NUL */
333                         len = strlen(dir) + 1 + 9 + 1;
334                         conf = malloc(len);
335                         snprintf(conf, len, "%s/onak.conf", dir);
336                         conffile = fopen(conf, "r");
337                         if (conffile == NULL) {
338                                 /* Conveniently .ini is shorter than .conf */
339                                 snprintf(conf, len, "%s/onak.ini", dir);
340                                 conffile = fopen(conf, "r");
341                         } else {
342                                 oldstyle = true;
343                         }
344                         free(conf);
345                 } else if ((dir = getenv("HOME")) != NULL) {
346                         /* dir + /.config/onak.conf + NUL */
347                         len = strlen(dir) + 18 + 1;
348                         conf = malloc(len);
349                         snprintf(conf, len, "%s/.config/onak.conf", dir);
350                         conffile = fopen(conf, "r");
351                         if (conffile == NULL) {
352                                 /* Conveniently .ini is shorter than .conf */
353                                 snprintf(conf, len, "%s/onak.ini", dir);
354                                 conffile = fopen(conf, "r");
355                         } else {
356                                 oldstyle = true;
357                         }
358                         free(conf);
359                 }
360                 if (conffile == NULL) {
361                         conffile = fopen(CONFIGDIR "/onak.conf", "r");
362                         if (conffile == NULL) {
363                                 conffile = fopen(CONFIGDIR "/onak.ini", "r");
364                         } else {
365                                 oldstyle = true;
366                         }
367                 }
368         } else {
369                 /*
370                  * Explicitly provided config file; if the filename ends .conf
371                  * assume it's old style.
372                  */
373                 len = strlen(configfile);
374                 if (!strcmp(&configfile[len - 5], ".conf")) {
375                         oldstyle = true;
376                 }
377                 conffile = fopen(configfile, "r");
378         }
379
380         if (conffile != NULL) {
381                 if (!fgets(curline, 1023, conffile)) {
382                         logthing(LOGTHING_CRITICAL,
383                                 "Problem reading configuration file.");
384                         fclose(conffile);
385                         return;
386                 }
387
388                 if (oldstyle) {
389                         /* Add a single DB configuration */
390                         backend = calloc(1, sizeof(*backend));
391                         config.backend = backend;
392                         config.backends = lladd(NULL, backend);
393                 }
394
395                 while (!feof(conffile)) {
396                         /* Strip any trailing white space */
397                         for (i = strlen(curline) - 1;
398                                         i >= 0 && isspace(curline[i]);
399                                         i--) {
400                                 curline[i] = 0;
401                         }
402
403                         /* Strip any leading white space */
404                         i = 0;
405                         while (curline[i] != 0 && isspace(curline[i])) {
406                                 i++;
407                         }
408
409                         if (oldstyle) {
410                                 res = parseoldconfigline(&curline[i]);
411                         } else {
412                                 res = parseconfigline(&curline[i]);
413                         }
414                         if (!res) {
415                                 logthing(LOGTHING_ERROR,
416                                         "Unknown config line: %s", curline);
417                         }
418
419                         if (!fgets(curline, 1023, conffile) &&
420                                         !feof(conffile)) {
421                                 logthing(LOGTHING_CRITICAL,
422                                         "Problem reading configuration file.");
423                                 break;
424                         }
425                 }
426                 fclose(conffile);
427
428                 if (config.db_backend == NULL) {
429                         logthing(LOGTHING_CRITICAL,
430                                 "No database backend configured.");
431                 } else if (!oldstyle) {
432                         config.backend = find_db_backend_config(
433                                 config.backends, config.db_backend);
434                         if (config.backend == NULL) {
435                                 logthing(LOGTHING_NOTICE,
436                                         "Couldn't find configuration for %s "
437                                         "backend.", config.db_backend);
438                         }
439                 }
440         } else {
441                 logthing(LOGTHING_NOTICE,
442                                 "Couldn't open config file; using defaults.");
443         }
444 }
445
446 void writeconfig(const char *configfile)
447 {
448         FILE *conffile;
449         struct ll *cur;
450
451         if (configfile) {
452                 conffile = fopen(configfile, "w");
453         } else {
454                 conffile = stdout;
455         }
456
457 #define WRITE_IF_NOT_NULL(c, s) if (c != NULL) { \
458         fprintf(conffile, s "=%s\n", c); \
459 }
460 #define WRITE_BOOL(c, s) fprintf(conffile, s "=%s\n", s ? "true" : "false");
461
462         fprintf(conffile, "[main]\n");
463         WRITE_IF_NOT_NULL(config.backend->name, "backend");
464         WRITE_IF_NOT_NULL(config.backends_dir, "backends_dir");
465         WRITE_IF_NOT_NULL(config.logfile, "logfile");
466         fprintf(conffile, "loglevel=%d\n", getlogthreshold());
467         WRITE_BOOL(config.use_keyd, "use_keyd");
468         WRITE_IF_NOT_NULL(config.sock_dir, "sock_dir");
469         fprintf(conffile, "max_reply_keys=%d\n", config.maxkeys);
470         fprintf(conffile, "\n");
471
472         fprintf(conffile, "[verification]\n");
473         WRITE_BOOL(config.clean_policies & ONAK_CLEAN_CHECK_SIGHASH,
474                         "check_sighash");
475         fprintf(conffile, "\n");
476
477         fprintf(conffile, "[mail]\n");
478         WRITE_IF_NOT_NULL(config.adminemail, "maintainer_email");
479         WRITE_IF_NOT_NULL(config.mail_dir, "mail_dir");
480         WRITE_IF_NOT_NULL(config.mta, "mta");
481         WRITE_IF_NOT_NULL(config.bin_dir, "bin_dir");
482         WRITE_IF_NOT_NULL(config.thissite, "this_site");
483
484         cur = config.syncsites;
485         while (cur != NULL) {
486                 fprintf(conffile, "syncsite=%s\n", (char *) cur->object);
487                 cur = cur->next;
488         }
489
490         cur = config.backends;
491         while (cur != NULL) {
492                 struct onak_db_config *backend =
493                         (struct onak_db_config *) cur->object;
494                 fprintf(conffile, "\n[backend:%s]\n", backend->name);
495                 WRITE_IF_NOT_NULL(backend->type, "type");
496                 WRITE_IF_NOT_NULL(backend->location, "location");
497                 WRITE_IF_NOT_NULL(backend->hostname, "hostname");
498                 WRITE_IF_NOT_NULL(backend->username, "username");
499                 WRITE_IF_NOT_NULL(backend->password, "password");
500                 cur = cur->next;
501         }
502
503         if (configfile) {
504                 fclose(conffile);
505         }
506 }
507
508 void cleanupdbconfig(void *object)
509 {
510         struct onak_db_config *dbconfig = (struct onak_db_config *) object;
511
512         if (dbconfig->name != NULL) {
513                 free(dbconfig->name);
514                 dbconfig->name = NULL;
515         }
516         if (dbconfig->type != NULL) {
517                 free(dbconfig->type);
518                 dbconfig->type = NULL;
519         }
520         if (dbconfig->location != NULL) {
521                 free(dbconfig->location);
522                 dbconfig->location = NULL;
523         }
524         if (dbconfig->hostname != NULL) {
525                 free(dbconfig->hostname);
526                 dbconfig->hostname = NULL;
527         }
528         if (dbconfig->username != NULL) {
529                 free(dbconfig->username);
530                 dbconfig->username = NULL;
531         }
532         if (dbconfig->password != NULL) {
533                 free(dbconfig->password);
534                 dbconfig->password = NULL;
535         }
536
537         free(dbconfig);
538 }
539
540 void cleanupconfig(void) {
541         /* Free any defined DB backend configuration first */
542         llfree(config.backends, cleanupdbconfig);
543         config.backends = NULL;
544
545         if (config.thissite != NULL) {
546                 free(config.thissite);
547                 config.thissite = NULL;
548         }
549         if (config.adminemail != NULL) {
550                 free(config.adminemail);
551                 config.adminemail = NULL;
552         }
553         if (config.mta != NULL) {
554                 free(config.mta);
555                 config.mta = NULL;
556         }
557         if (config.syncsites != NULL) {
558                 llfree(config.syncsites, free);
559                 config.syncsites = NULL;
560         }
561         if (config.logfile != NULL) {
562                 free(config.logfile);
563                 config.logfile = NULL;
564         }
565         if (config.db_backend != NULL) {
566                 free(config.db_backend);
567                 config.db_backend = NULL;
568         }
569         if (config.backends_dir != NULL) {
570                 free(config.backends_dir);
571                 config.backends_dir = NULL;
572         }
573         if (config.sock_dir != NULL) {
574                 free(config.sock_dir);
575                 config.sock_dir = NULL;
576         }
577         if (config.bin_dir != NULL) {
578                 free(config.bin_dir);
579                 config.bin_dir = NULL;
580         }
581         if (config.mail_dir != NULL) {
582                 free(config.mail_dir);
583                 config.mail_dir = NULL;
584         }
585 }