X-Git-Url: http://the.earth.li/gitweb/?p=onak.git;a=blobdiff_plain;f=onak-conf.c;h=6d67a74610c81b06f1677badca51a10e1f1a7378;hp=e3085e1996be9e1fa6ec7690ea8f70fdf7e10951;hb=de18b56efecadc4b5d2473904828db9c08cd2162;hpb=8e0907be1d73011075a99a0c029c56664e12843e diff --git a/onak-conf.c b/onak-conf.c index e3085e1..6d67a74 100644 --- a/onak-conf.c +++ b/onak-conf.c @@ -13,22 +13,24 @@ * more details. * * You should have received a copy of the GNU General Public License along with - * this program; if not, write to the Free Software Foundation, Inc., 51 - * Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * this program. If not, see . */ - -#include "config.h" - #include #include #include #include +#include +#include "build-config.h" + +#include "cleankey.h" #include "ll.h" #include "log.h" #include "onak-conf.h" -extern struct onak_dbctx *DBINIT(bool readonly); +#ifdef DBINIT +extern struct onak_dbctx *DBINIT(struct onak_db_config *dbcfg, bool readonly); +#endif /* * config - Runtime configuration for onak. @@ -37,39 +39,44 @@ extern struct onak_dbctx *DBINIT(bool readonly); * config file. */ struct onak_config config = { - 128, /* maxkeys */ - NULL, /* thissite */ - NULL, /* adminemail */ - NULL, /* mta */ - NULL, /* syncsites */ - NULL, /* logfile */ + .maxkeys = 128, + .thissite = NULL, + .adminemail = NULL, + .mta = NULL, + .syncsites = NULL, + .logfile = NULL, - false, /* use_keyd */ + .use_keyd = false, + .sock_dir = NULL, - /* - * Options for directory backends. - */ - NULL, /* db_dir */ + .backends = NULL, + .backends_dir = NULL, - /* - * Options for the Postgres backend. - */ - NULL, /* pg_dbhost */ - NULL, /* pg_dbname */ - NULL, /* pg_dbuser */ - NULL, /* pg_dbpass */ +#ifdef DBINIT + .dbinit = DBINIT, +#else + .dbinit = NULL, +#endif - /* - * Options for dynamic backends. - */ - NULL, /* db_backend */ - NULL, /* backends_dir */ - - DBINIT, /* Default db initialisation function */ + .clean_policies = ONAK_CLEAN_DROP_V3_KEYS | ONAK_CLEAN_CHECK_SIGHASH, - true, /* Check packet sig hashes */ + .bin_dir = NULL, + .mail_dir = NULL, }; +struct onak_db_config *find_db_backend_config(struct ll *backends, char *name) +{ + struct ll *cur; + + cur = backends; + while (cur != NULL && strcmp(name, + ((struct onak_db_config *) cur->object)->name)) { + cur = cur->next; + } + + return (cur != NULL) ? (struct onak_db_config *) cur->object : NULL; +} + bool parsebool(char *str, bool fallback) { if (!strcasecmp(str, "false") || !strcasecmp(str, "no") || @@ -88,131 +95,514 @@ bool parsebool(char *str, bool fallback) } } +/* + * Parse an old pksd style config line, as used in onak 0.4.6 and earlier. + */ +static bool parseoldconfigline(char *line) +{ + if (line[0] == '#' || line[0] == 0) { + /* + * Comment line, ignore. + */ + } else if (!strncmp("db_dir ", line, 7)) { + config.backend->location = strdup(&line[7]); + } else if (!strncmp("debug ", line, 6)) { + /* + * Not supported yet; ignore for compatibility with + * pksd. + */ + } else if (!strncmp("default_language ", line, 17)) { + /* + * Not supported yet; ignore for compatibility with + * pksd. + */ + } else if (!strncmp("mail_delivery_client ", line, 21)) { + config.mta = strdup(&line[21]); + } else if (!strncmp("maintainer_email ", line, 17)) { + config.adminemail = strdup(&line[17]); + } else if (!strncmp("mail_intro_file ", line, 16)) { + /* + * Not supported yet; ignore for compatibility with + * pksd. + */ + } else if (!strncmp("help_dir ", line, 9)) { + /* + * Not supported yet; ignore for compatibility with + * pksd. + */ + } else if (!strncmp("max_last ", line, 9)) { + /* + * Not supported yet; ignore for compatibility with + * pksd. + */ + } else if (!strncmp("max_reply_keys ", line, 15)) { + config.maxkeys = atoi(&line[15]); + } else if (!strncmp("pg_dbhost ", line, 10)) { + config.backend->hostname = strdup(&line[10]); + } else if (!strncmp("pg_dbname ", line, 10)) { + config.backend->location = strdup(&line[10]); + } else if (!strncmp("pg_dbuser ", line, 10)) { + config.backend->username = strdup(&line[10]); + } else if (!strncmp("pg_dbpass ", line, 10)) { + config.backend->password = strdup(&line[10]); + } else if (!strncmp("syncsite ", line, 9)) { + config.syncsites = + lladd(config.syncsites, strdup(&line[9])); + } else if (!strncmp("logfile ", line, 8)) { + config.logfile = strdup(&line[8]); + } else if (!strncmp("loglevel ", line, 9)) { + setlogthreshold(atoi(&line[9])); + } else if (!strncmp("this_site ", line, 10)) { + config.thissite = strdup(&line[10]); + } else if (!strncmp("socket_name ", line, 12) || + !strncmp("www_port ", line, 9)) { + /* + * Not applicable; ignored for compatibility with pksd. + */ + } else if (!strncmp("pks_bin_dir ", line, 12)) { + config.bin_dir = strdup(&line[12]); + } else if (!strncmp("mail_dir ", line, 9)) { + config.mail_dir = strdup(&line[9]); + } else if (!strncmp("db_backend ", line, 11)) { + config.backend->type = strdup(&line[11]); + config.backend->name = strdup(&line[11]); + config.db_backend = strdup(&line[11]); + } else if (!strncmp("backends_dir ", line, 13)) { + config.backends_dir = strdup(&line[13]); + } else if (!strncmp("use_keyd ", line, 9)) { + config.use_keyd = parsebool(&line[9], + config.use_keyd); + } else if (!strncmp("sock_dir ", line, 9)) { + config.sock_dir = strdup(&line[9]); + } else if (!strncmp("check_sighash ", line, 9)) { + if (parsebool(&line[9], config.clean_policies & + ONAK_CLEAN_CHECK_SIGHASH)) { + config.clean_policies |= + ONAK_CLEAN_CHECK_SIGHASH; + } else { + config.clean_policies &= + ~ONAK_CLEAN_CHECK_SIGHASH; + } + } else { + return false; + } + + return true; +} + +/* + * Parse a new style .ini config line, supporting [sections] and name=value + * format. + */ +static bool parseconfigline(char *line) +{ + /* Yes, this means we're not re-entrant. */ + static char section[32] = ""; + struct onak_db_config *backend; + size_t len; + char *name, *value; + + if (line[0] == '#' || line[0] == ';' || + line[0] == 0) { + /* + * Comment line, ignore. + */ + } else if (line[0] == '[') { + /* Section name */ + len = strlen(line); + if (line[len - 1] != ']') { + logthing(LOGTHING_CRITICAL, + "Malformed section header '%s' in " + "config file.", line); + return false; + } + if (len > sizeof(section)) { + logthing(LOGTHING_CRITICAL, + "Section header '%s' is too long in " + "config file.", line); + return false; + } + line[len - 1] = 0; + strncpy(section, &line[1], len); + } else if ((value = strchr(line, '=')) != NULL) { + name = line; + *value++ = 0; + + /* We can have multiple backend: sections */ + if (!strncmp(section, "backend:", 8)) { + backend = find_db_backend_config( + config.backends, §ion[8]); + if (backend == NULL) { + backend = calloc(1, + sizeof(struct onak_db_config)); + backend->name = strdup(§ion[8]); + config.backends = lladd(config.backends, + backend); + } + + if (!strcmp(name, "type")) { + backend->type = strdup(value); + } else if (!strcmp(name, "location")) { + backend->location = strdup(value); + } else if (!strcmp(name, "hostname")) { + backend->location = strdup(value); + } else if (!strcmp(name, "username")) { + backend->location = strdup(value); + } else if (!strcmp(name, "password")) { + backend->location = strdup(value); + } + +#define MATCH(s, n) !strcmp(section, s) && !strcmp(name, n) + /* [main] section */ + } else if (MATCH("main", "backend")) { + config.db_backend = strdup(value); + } else if (MATCH("main", "backends_dir")) { + config.backends_dir = strdup(value); + } else if (MATCH("main", "logfile")) { + config.logfile = strdup(value); + } else if (MATCH("main", "loglevel")) { + setlogthreshold(atoi(value)); + } else if (MATCH("main", "use_keyd")) { + config.use_keyd = parsebool(value, + config.use_keyd); + } else if (MATCH("main", "sock_dir")) { + config.sock_dir = strdup(value); + } else if (MATCH("main", "max_reply_keys")) { + config.maxkeys = atoi(value); + /* [mail] section */ + } else if (MATCH("mail", "maintainer_email")) { + config.adminemail = strdup(value); + } else if (MATCH("mail", "mail_dir")) { + config.mail_dir = strdup(value); + } else if (MATCH("mail", "mta")) { + config.mta = strdup(value); + } else if (MATCH("mail", "bin_dir")) { + config.bin_dir = strdup(value); + } else if (MATCH("mail", "this_site")) { + config.thissite = strdup(value); + } else if (MATCH("mail", "syncsite")) { + config.syncsites = lladd(config.syncsites, + strdup(value)); + /* [verification] section */ + } else if (MATCH("verification", "blacklist")) { + array_load(&config.blacklist, value); + } else if (MATCH("verification", "drop_v3")) { + if (parsebool(value, config.clean_policies & + ONAK_CLEAN_DROP_V3_KEYS)) { + config.clean_policies |= + ONAK_CLEAN_DROP_V3_KEYS; + } else { + config.clean_policies &= + ~ONAK_CLEAN_DROP_V3_KEYS; + } + } else if (MATCH("verification", "check_sighash")) { + if (parsebool(value, config.clean_policies & + ONAK_CLEAN_CHECK_SIGHASH)) { + config.clean_policies |= + ONAK_CLEAN_CHECK_SIGHASH; + } else { + config.clean_policies &= + ~ONAK_CLEAN_CHECK_SIGHASH; + } + } else if (MATCH("verification", "check_packet_size")) { + if (parsebool(value, config.clean_policies & + ONAK_CLEAN_LARGE_PACKETS)) { + config.clean_policies |= + ONAK_CLEAN_LARGE_PACKETS; + } else { + config.clean_policies &= + ~ONAK_CLEAN_LARGE_PACKETS; + } + } else if (MATCH("verification", "require_other_sig")) { +#if HAVE_CRYPTO + if (parsebool(value, config.clean_policies & + ONAK_CLEAN_NEED_OTHER_SIG)) { + config.clean_policies |= + ONAK_CLEAN_NEED_OTHER_SIG; + } else { + config.clean_policies &= + ~ONAK_CLEAN_NEED_OTHER_SIG; + } +#else + logthing(LOGTHING_ERROR, + "Compiled without crypto support, " + "require_other_sig not available."); +#endif + } else if (MATCH("verification", "update_only")) { + if (parsebool(value, config.clean_policies & + ONAK_CLEAN_UPDATE_ONLY)) { + config.clean_policies |= + ONAK_CLEAN_UPDATE_ONLY; + } else { + config.clean_policies &= + ~ONAK_CLEAN_UPDATE_ONLY; + } + } else if (MATCH("verification", "verify_signatures")) { +#if HAVE_CRYPTO + if (parsebool(value, config.clean_policies & + ONAK_CLEAN_VERIFY_SIGNATURES)) { + config.clean_policies |= + ONAK_CLEAN_VERIFY_SIGNATURES; + } else { + config.clean_policies &= + ~ONAK_CLEAN_VERIFY_SIGNATURES; + } +#else + logthing(LOGTHING_ERROR, + "Compiled without crypto support, " + "verify_signatures not available."); +#endif + } else { + return false; + } + } else { + return false; + } + + return true; +} + void readconfig(const char *configfile) { FILE *conffile; char curline[1024]; int i; char *dir, *conf; size_t len; + struct onak_db_config *backend; + bool oldstyle = false; + bool res; curline[1023] = 0; + + /* + * Try to find a config file to use. If one is explicitly provided, + * use that. Otherwise look in $XDG_CONFIG_HOME, $HOME and finally + * the build in configuration directory. We try an old style onak.conf + * first and then the new style onak.ini if that wasn't found - this + * is to prevent breaking existing installs on upgrade. + */ if (configfile == NULL) { conffile = NULL; if ((dir = getenv("XDG_CONFIG_HOME")) != NULL) { - len = strlen(dir) + 1 + 9 + 1; /* dir + / + onak.conf + NUL */ + /* dir + / + onak.conf + NUL */ + len = strlen(dir) + 1 + 9 + 1; conf = malloc(len); snprintf(conf, len, "%s/onak.conf", dir); conffile = fopen(conf, "r"); + if (conffile == NULL) { + /* Conveniently .ini is shorter than .conf */ + snprintf(conf, len, "%s/onak.ini", dir); + conffile = fopen(conf, "r"); + } else { + oldstyle = true; + } free(conf); } else if ((dir = getenv("HOME")) != NULL) { - len = strlen(dir) + 18 + 1; /* dir + /.config/onak.conf + NUL */ + /* dir + /.config/onak.conf + NUL */ + len = strlen(dir) + 18 + 1; conf = malloc(len); snprintf(conf, len, "%s/.config/onak.conf", dir); conffile = fopen(conf, "r"); + if (conffile == NULL) { + /* Conveniently .ini is shorter than .conf */ + snprintf(conf, len, "%s/onak.ini", dir); + conffile = fopen(conf, "r"); + } else { + oldstyle = true; + } free(conf); } if (conffile == NULL) { - conffile = fopen(CONFIGFILE, "r"); + conffile = fopen(CONFIGDIR "/onak.conf", "r"); + if (conffile == NULL) { + conffile = fopen(CONFIGDIR "/onak.ini", "r"); + } else { + oldstyle = true; + } } } else { + /* + * Explicitly provided config file; if the filename ends .conf + * assume it's old style. + */ + len = strlen(configfile); + if (!strcmp(&configfile[len - 5], ".conf")) { + oldstyle = true; + } conffile = fopen(configfile, "r"); } + + if (oldstyle) { + logthing(LOGTHING_CRITICAL, "Reading deprecated old-style " + "configuration file. This will not be " + "supported in the next release."); + } + if (conffile != NULL) { - fgets(curline, 1023, conffile); + if (!fgets(curline, 1023, conffile)) { + logthing(LOGTHING_CRITICAL, + "Problem reading configuration file."); + fclose(conffile); + return; + } + + if (oldstyle) { + /* Add a single DB configuration */ + backend = calloc(1, sizeof(*backend)); + config.backend = backend; + config.backends = lladd(NULL, backend); + } while (!feof(conffile)) { + /* Strip any trailing white space */ for (i = strlen(curline) - 1; i >= 0 && isspace(curline[i]); i--) { curline[i] = 0; } - if (curline[0] == '#' || curline[0] == 0) { - /* - * Comment line, ignore. - */ - } else if (!strncmp("db_dir ", curline, 7)) { - config.db_dir = strdup(&curline[7]); - } else if (!strncmp("debug ", curline, 6)) { - /* - * Not supported yet; ignore for compatibility with - * pksd. - */ - } else if (!strncmp("default_language ", curline, 17)) { - /* - * Not supported yet; ignore for compatibility with - * pksd. - */ - } else if (!strncmp("mail_delivery_client ", curline, 21)) { - config.mta = strdup(&curline[21]); - } else if (!strncmp("maintainer_email ", curline, 17)) { - config.adminemail = strdup(&curline[17]); - } else if (!strncmp("mail_intro_file ", curline, 16)) { - /* - * Not supported yet; ignore for compatibility with - * pksd. - */ - } else if (!strncmp("help_dir ", curline, 9)) { - /* - * Not supported yet; ignore for compatibility with - * pksd. - */ - } else if (!strncmp("max_last ", curline, 9)) { - /* - * Not supported yet; ignore for compatibility with - * pksd. - */ - } else if (!strncmp("max_reply_keys ", curline, 15)) { - config.maxkeys = atoi(&curline[15]); - } else if (!strncmp("pg_dbhost ", curline, 10)) { - config.pg_dbhost = strdup(&curline[10]); - } else if (!strncmp("pg_dbname ", curline, 10)) { - config.pg_dbname = strdup(&curline[10]); - } else if (!strncmp("pg_dbuser ", curline, 10)) { - config.pg_dbuser = strdup(&curline[10]); - } else if (!strncmp("pg_dbpass ", curline, 10)) { - config.pg_dbpass = strdup(&curline[10]); - } else if (!strncmp("syncsite ", curline, 9)) { - config.syncsites = - lladd(config.syncsites, strdup(&curline[9])); - } else if (!strncmp("logfile ", curline, 8)) { - config.logfile = strdup(&curline[8]); - } else if (!strncmp("loglevel ", curline, 9)) { - setlogthreshold(atoi(&curline[9])); - } else if (!strncmp("this_site ", curline, 10)) { - config.thissite = strdup(&curline[10]); - } else if (!strncmp("socket_name ", curline, 12) || - !strncmp("pks_bin_dir ", curline, 12) || - !strncmp("mail_dir ", curline, 9) || - !strncmp("www_port ", curline, 9)) { - /* - * Not applicable; ignored for compatibility with pksd. - */ - } else if (!strncmp("db_backend ", curline, 11)) { - config.db_backend = strdup(&curline[11]); - } else if (!strncmp("backends_dir ", curline, 13)) { - config.backends_dir = strdup(&curline[13]); - } else if (!strncmp("use_keyd ", curline, 9)) { - config.use_keyd = parsebool(&curline[9], - config.use_keyd); - } else if (!strncmp("check_sighash ", curline, 9)) { - config.check_sighash = parsebool(&curline[9], - config.check_sighash); - } else { - logthing(LOGTHING_ERROR, - "Unknown config line: %s", curline); - } + /* Strip any leading white space */ + i = 0; + while (curline[i] != 0 && isspace(curline[i])) { + i++; + } + + if (oldstyle) { + res = parseoldconfigline(&curline[i]); + } else { + res = parseconfigline(&curline[i]); + } + if (!res) { + logthing(LOGTHING_ERROR, + "Unknown config line: %s", curline); + } - fgets(curline, 1023, conffile); + if (!fgets(curline, 1023, conffile) && + !feof(conffile)) { + logthing(LOGTHING_CRITICAL, + "Problem reading configuration file."); + break; + } } fclose(conffile); + + if (config.db_backend == NULL) { + logthing(LOGTHING_CRITICAL, + "No database backend configured."); + } else if (!oldstyle) { + config.backend = find_db_backend_config( + config.backends, config.db_backend); + if (config.backend == NULL) { + logthing(LOGTHING_NOTICE, + "Couldn't find configuration for %s " + "backend.", config.db_backend); + } + } } else { logthing(LOGTHING_NOTICE, "Couldn't open config file; using defaults."); } } +void writeconfig(const char *configfile) +{ + FILE *conffile; + struct ll *cur; + + if (configfile) { + conffile = fopen(configfile, "w"); + } else { + conffile = stdout; + } + +#define WRITE_IF_NOT_NULL(c, s) if (c != NULL) { \ + fprintf(conffile, s "=%s\n", c); \ +} +#define WRITE_BOOL(c, s) fprintf(conffile, s "=%s\n", s ? "true" : "false"); + + fprintf(conffile, "[main]\n"); + WRITE_IF_NOT_NULL(config.backend->name, "backend"); + WRITE_IF_NOT_NULL(config.backends_dir, "backends_dir"); + WRITE_IF_NOT_NULL(config.logfile, "logfile"); + fprintf(conffile, "loglevel=%d\n", getlogthreshold()); + WRITE_BOOL(config.use_keyd, "use_keyd"); + WRITE_IF_NOT_NULL(config.sock_dir, "sock_dir"); + fprintf(conffile, "max_reply_keys=%d\n", config.maxkeys); + fprintf(conffile, "\n"); + + fprintf(conffile, "[verification]\n"); + WRITE_BOOL(config.clean_policies & ONAK_CLEAN_CHECK_SIGHASH, + "check_sighash"); + fprintf(conffile, "\n"); + + fprintf(conffile, "[mail]\n"); + WRITE_IF_NOT_NULL(config.adminemail, "maintainer_email"); + WRITE_IF_NOT_NULL(config.mail_dir, "mail_dir"); + WRITE_IF_NOT_NULL(config.mta, "mta"); + WRITE_IF_NOT_NULL(config.bin_dir, "bin_dir"); + WRITE_IF_NOT_NULL(config.thissite, "this_site"); + + cur = config.syncsites; + while (cur != NULL) { + fprintf(conffile, "syncsite=%s\n", (char *) cur->object); + cur = cur->next; + } + + cur = config.backends; + while (cur != NULL) { + struct onak_db_config *backend = + (struct onak_db_config *) cur->object; + fprintf(conffile, "\n[backend:%s]\n", backend->name); + WRITE_IF_NOT_NULL(backend->type, "type"); + WRITE_IF_NOT_NULL(backend->location, "location"); + WRITE_IF_NOT_NULL(backend->hostname, "hostname"); + WRITE_IF_NOT_NULL(backend->username, "username"); + WRITE_IF_NOT_NULL(backend->password, "password"); + cur = cur->next; + } + + if (configfile) { + fclose(conffile); + } +} + +void cleanupdbconfig(void *object) +{ + struct onak_db_config *dbconfig = (struct onak_db_config *) object; + + if (dbconfig->name != NULL) { + free(dbconfig->name); + dbconfig->name = NULL; + } + if (dbconfig->type != NULL) { + free(dbconfig->type); + dbconfig->type = NULL; + } + if (dbconfig->location != NULL) { + free(dbconfig->location); + dbconfig->location = NULL; + } + if (dbconfig->hostname != NULL) { + free(dbconfig->hostname); + dbconfig->hostname = NULL; + } + if (dbconfig->username != NULL) { + free(dbconfig->username); + dbconfig->username = NULL; + } + if (dbconfig->password != NULL) { + free(dbconfig->password); + dbconfig->password = NULL; + } + + free(dbconfig); +} + void cleanupconfig(void) { + /* Free any defined DB backend configuration first */ + llfree(config.backends, cleanupdbconfig); + config.backends = NULL; + if (config.thissite != NULL) { free(config.thissite); config.thissite = NULL; @@ -225,26 +615,6 @@ void cleanupconfig(void) { free(config.mta); config.mta = NULL; } - if (config.db_dir != NULL) { - free(config.db_dir); - config.db_dir = NULL; - } - if (config.pg_dbhost != NULL) { - free(config.pg_dbhost); - config.pg_dbhost = NULL; - } - if (config.pg_dbname != NULL) { - free(config.pg_dbname); - config.pg_dbname = NULL; - } - if (config.pg_dbuser != NULL) { - free(config.pg_dbuser); - config.pg_dbuser = NULL; - } - if (config.pg_dbpass != NULL) { - free(config.pg_dbpass); - config.pg_dbpass = NULL; - } if (config.syncsites != NULL) { llfree(config.syncsites, free); config.syncsites = NULL; @@ -261,4 +631,19 @@ void cleanupconfig(void) { free(config.backends_dir); config.backends_dir = NULL; } + if (config.sock_dir != NULL) { + free(config.sock_dir); + config.sock_dir = NULL; + } + if (config.bin_dir != NULL) { + free(config.bin_dir); + config.bin_dir = NULL; + } + if (config.mail_dir != NULL) { + free(config.mail_dir); + config.mail_dir = NULL; + } + if (config.blacklist.count != 0) { + array_free(&config.blacklist); + } }