/**************************************************************** ** ** @(#) zconf.c -- configuration file parser for dnssec.conf ** ** Most of the code is from the SixXS Heartbeat Client ** written by Jeroen Massar ** ** New config types and some slightly code changes ** by Holger Zuleger ** ** Copyright (c) Aug 2005, Jeroen Massar, Holger Zuleger. ** All rights reserved. ** ** This software is open source. ** ** Redistribution and use in source and binary forms, with or without ** modification, are permitted provided that the following conditions ** are met: ** ** Redistributions of source code must retain the above copyright notice, ** this list of conditions and the following disclaimer. ** ** Redistributions in binary form must reproduce the above copyright notice, ** this list of conditions and the following disclaimer in the documentation ** and/or other materials provided with the distribution. ** ** Neither the name of Jeroen Masar or Holger Zuleger nor the ** names of its contributors may be used to endorse or promote products ** derived from this software without specific prior written permission. ** ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED ** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR ** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR ** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF ** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS ** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN ** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE ** POSSIBILITY OF SUCH DAMAGE. ** ****************************************************************/ # include # include # include # include # include # include # include # include # include # include #ifdef HAVE_CONFIG_H # include "config.h" #endif # include "config_zkt.h" # include "debug.h" # include "misc.h" #define extern # include "zconf.h" #undef extern # include "dki.h" # define ISTRUE(val) (strcasecmp (val, "yes") == 0 || \ strcasecmp (val, "true") == 0 ) # define ISCOMMENT(cp) (*(cp) == '#' || *(cp) == ';' || \ (*(cp) == '/' && *((cp)+1) == '/') ) # define ISDELIM(c) ( isspace (c) || (c) == ':' || (c) == '=' ) typedef enum { CONF_END = 0, CONF_STRING, CONF_INT, CONF_TIMEINT, CONF_BOOL, CONF_ALGO, CONF_SERIAL, CONF_FACILITY, CONF_LEVEL, CONF_COMMENT, } ctype_t; /***************************************************************** ** private (static) variables *****************************************************************/ static zconf_t def = { ZONEDIR, RECURSIVE, PRINTTIME, PRINTAGE, LJUST, SIG_VALIDITY, MAX_TTL, KEY_TTL, PROPTIME, Incremental, RESIGN_INT, KSK_LIFETIME, KSK_ALGO, KSK_BITS, KSK_RANDOM, ZSK_LIFETIME, ZSK_ALGO, ZSK_BITS, ZSK_RANDOM, NULL, /* viewname cmdline paramter */ LOGFILE, LOGLEVEL, SYSLOGFACILITY, SYSLOGLEVEL, VERBOSELOG, 0, DNSKEYFILE, ZONEFILE, KEYSETDIR, LOOKASIDEDOMAIN, SIG_RANDOM, SIG_PSEUDO, SIG_GENDS, SIG_PARAM, DIST_CMD /* deafults to NULL which means to run "rndc reload" */ }; typedef struct { char *label; /* the name of the paramter */ int cmdline; /* is this a command line parameter ? */ ctype_t type; /* the parameter type */ void *var; /* pointer to the parameter variable */ } zconf_para_t; static zconf_para_t confpara[] = { { "", 0, CONF_COMMENT, ""}, { "", 0, CONF_COMMENT, "\t@(#) dnssec.conf " ZKT_VERSION }, { "", 0, CONF_COMMENT, ""}, { "", 0, CONF_COMMENT, NULL }, { "", 0, CONF_COMMENT, "dnssec-zkt options" }, { "Zonedir", 0, CONF_STRING, &def.zonedir }, { "Recursive", 0, CONF_BOOL, &def.recursive }, { "PrintTime", 0, CONF_BOOL, &def.printtime }, { "PrintAge", 0, CONF_BOOL, &def.printage }, { "LeftJustify", 0, CONF_BOOL, &def.ljust }, { "", 0, CONF_COMMENT, NULL }, { "", 0, CONF_COMMENT, "zone specific values" }, { "ResignInterval", 0, CONF_TIMEINT, &def.resign }, { "Sigvalidity", 0, CONF_TIMEINT, &def.sigvalidity }, { "Max_TTL", 0, CONF_TIMEINT, &def.max_ttl }, { "Propagation", 0, CONF_TIMEINT, &def.proptime }, { "KEY_TTL", 0, CONF_TIMEINT, &def.key_ttl }, #if defined (DEF_TTL) { "def_ttl", 0, CONF_TIMEINT, &def.def_ttl }, #endif { "Serialformat", 0, CONF_SERIAL, &def.serialform }, { "", 0, CONF_COMMENT, NULL }, { "", 0, CONF_COMMENT, "signing key parameters"}, { "KSK_lifetime", 0, CONF_TIMEINT, &def.k_life }, { "KSK_algo", 0, CONF_ALGO, &def.k_algo }, { "KSK_bits", 0, CONF_INT, &def.k_bits }, { "KSK_randfile", 0, CONF_STRING, &def.k_random }, { "ZSK_lifetime", 0, CONF_TIMEINT, &def.z_life }, { "ZSK_algo", 0, CONF_ALGO, &def.z_algo }, { "ZSK_bits", 0, CONF_INT, &def.z_bits }, { "ZSK_randfile", 0, CONF_STRING, &def.z_random }, { "", 0, CONF_COMMENT, NULL }, { "", 0, CONF_COMMENT, "dnssec-signer options"}, { "--view", 1, CONF_STRING, &def.view }, { "LogFile", 0, CONF_STRING, &def.logfile }, { "LogLevel", 0, CONF_LEVEL, &def.loglevel }, { "SyslogFacility", 0, CONF_FACILITY, &def.syslogfacility }, { "SyslogLevel", 0, CONF_LEVEL, &def.sysloglevel }, { "VerboseLog", 0, CONF_INT, &def.verboselog }, { "-v", 1, CONF_INT, &def.verbosity }, { "Keyfile", 0, CONF_STRING, &def.keyfile }, { "Zonefile", 0, CONF_STRING, &def.zonefile }, { "KeySetDir", 0, CONF_STRING, &def.keysetdir }, { "DLV_Domain", 0, CONF_STRING, &def.lookaside }, { "Sig_Randfile", 0, CONF_STRING, &def.sig_random }, { "Sig_Pseudorand", 0, CONF_BOOL, &def.sig_pseudo }, { "Sig_GenerateDS", 1, CONF_BOOL, &def.sig_gends }, { "Sig_Parameter", 0, CONF_STRING, &def.sig_param }, { "Distribute_Cmd", 0, CONF_STRING, &def.dist_cmd }, { NULL, 0, CONF_END, NULL}, }; /***************************************************************** ** private (static) function deklaration and definition *****************************************************************/ static const char *bool2str (int val) { return val ? "True" : "False"; } static const char *timeint2str (ulong val) { static char str[20+1]; if ( val == 0 ) snprintf (str, sizeof (str), "%lu", val / YEARSEC); else if ( val % YEARSEC == 0 ) snprintf (str, sizeof (str), "%luy", val / YEARSEC); else if ( val % WEEKSEC == 0 ) snprintf (str, sizeof (str), "%luw", val / WEEKSEC); else if ( val % DAYSEC == 0 ) snprintf (str, sizeof (str), "%lud", val / DAYSEC); else if ( val % HOURSEC == 0 ) snprintf (str, sizeof (str), "%luh", val / HOURSEC); else if ( val % MINSEC == 0 ) snprintf (str, sizeof (str), "%lum", val / MINSEC); else snprintf (str, sizeof (str), "%lus", val); return str; } static int set_varptr (char *entry, void *ptr) { zconf_para_t *c; for ( c = confpara; c->label; c++ ) if ( strcasecmp (entry, c->label) == 0 ) { c->var = ptr; return 1; } return 0; } static void set_all_varptr (zconf_t *cp) { set_varptr ("zonedir", &cp->zonedir); set_varptr ("recursive", &cp->recursive); set_varptr ("printage", &cp->printage); set_varptr ("printtime", &cp->printtime); set_varptr ("leftjustify", &cp->ljust); set_varptr ("resigninterval", &cp->resign); set_varptr ("sigvalidity", &cp->sigvalidity); set_varptr ("max_ttl", &cp->max_ttl); set_varptr ("key_ttl", &cp->key_ttl); set_varptr ("propagation", &cp->proptime); #if defined (DEF_TTL) set_varptr ("def_ttl", &cp->def_ttl); #endif set_varptr ("serialformat", &cp->serialform); set_varptr ("ksk_lifetime", &cp->k_life); set_varptr ("ksk_algo", &cp->k_algo); set_varptr ("ksk_bits", &cp->k_bits); set_varptr ("ksk_randfile", &cp->k_random); set_varptr ("zsk_lifetime", &cp->z_life); set_varptr ("zsk_algo", &cp->z_algo); set_varptr ("zsk_bits", &cp->z_bits); set_varptr ("zsk_randfile", &cp->z_random); set_varptr ("--view", &cp->view); set_varptr ("logfile", &cp->logfile); set_varptr ("loglevel", &cp->loglevel); set_varptr ("syslogfacility", &cp->syslogfacility); set_varptr ("sysloglevel", &cp->sysloglevel); set_varptr ("verboselog", &cp->verboselog); set_varptr ("-v", &cp->verbosity); set_varptr ("keyfile", &cp->keyfile); set_varptr ("zonefile", &cp->zonefile); set_varptr ("keysetdir", &cp->keysetdir); set_varptr ("dlv_domain", &cp->lookaside); set_varptr ("sig_randfile", &cp->sig_random); set_varptr ("sig_pseudorand", &cp->sig_pseudo); set_varptr ("sig_generateds", &cp->sig_gends); set_varptr ("sig_parameter", &cp->sig_param); set_varptr ("distribute_cmd", &cp->dist_cmd); } static void parseconfigline (char *buf, unsigned int line, zconf_t *z) { char *end, *val, *p; char *tag; unsigned int len, found; zconf_para_t *c; p = &buf[strlen(buf)-1]; /* Chop off white space at eol */ while ( p >= buf && isspace (*p) ) *p-- = '\0'; for (p = buf; isspace (*p); p++ ) /* Ignore leading white space */ ; /* Ignore comments and emtpy lines */ if ( *p == '\0' || ISCOMMENT (p) ) return; tag = p; /* Get the end of the first argument */ end = &buf[strlen(buf)-1]; while ( p < end && !ISDELIM (*p) ) /* Skip until delim */ p++; *p++ = '\0'; /* Terminate this argument */ dbg_val1 ("Parsing \"%s\"\n", tag); while ( p < end && ISDELIM (*p) ) /* Skip delim chars */ p++; val = p; /* Start of the value */ dbg_val1 ("\tgot value \"%s\"\n", val); /* If starting with quote, skip until next quote */ if ( *p == '"' || *p == '\'' ) { p++; /* Find next quote */ while ( p <= end && *p && *p != *val ) p++; *p = '\0'; val++; /* Skip the first quote */ } else /* Otherwise check if there is any comment char at the end */ { while ( p < end && *p && !ISCOMMENT(p) ) p++; if ( ISCOMMENT (p) ) { do /* Chop off white space before comment */ *p-- = '\0'; while ( p >= val && isspace (*p) ); } } /* Otherwise it is already terminated above */ found = 0; c = confpara; while ( !found && c->type != CONF_END ) { len = strlen (c->label); if ( strcasecmp (tag, c->label) == 0 ) { char **str; char quantity; int ival; found = 1; switch ( c->type ) { case CONF_LEVEL: case CONF_FACILITY: case CONF_STRING: str = (char **)c->var; *str = strdup (val); str_untaint (*str); /* remove "bad" characters */ break; case CONF_INT: sscanf (val, "%d", (int *)c->var); break; case CONF_TIMEINT: quantity = 'd'; sscanf (val, "%d%c", &ival, &quantity); if ( quantity == 'm' ) ival *= MINSEC; else if ( quantity == 'h' ) ival *= HOURSEC; else if ( quantity == 'd' ) ival *= DAYSEC; else if ( quantity == 'w' ) ival *= WEEKSEC; else if ( quantity == 'y' ) ival *= YEARSEC; (*(int *)c->var) = ival; break; case CONF_ALGO: if ( strcasecmp (val, "rsa") == 0 || strcasecmp (val, "rsamd5") == 0 ) *((int *)c->var) = DK_ALGO_RSA; else if ( strcasecmp (val, "dsa") == 0 ) *((int *)c->var) = DK_ALGO_DSA; else if ( strcasecmp (val, "rsasha1") == 0 ) *((int *)c->var) = DK_ALGO_RSASHA1; else error ("Illegal algorithm \"%s\" " "in line %d.\n" , val, line); break; case CONF_SERIAL: if ( strcasecmp (val, "unixtime") == 0 ) *((serial_form_t *)c->var) = Unixtime; else if ( strcasecmp (val, "incremental") == 0 ) *((serial_form_t *)c->var) = Incremental; else error ("Illegal serial no format \"%s\" " "in line %d.\n" , val, line); break; case CONF_BOOL: *((int *)c->var) = ISTRUE (val); break; default: fatal ("Illegal configuration type in line %d.\n", line); } } c++; } if ( !found ) error ("Unknown configuration statement: %s \"%s\"\n", tag, val); return; } static void printconfigline (FILE *fp, zconf_para_t *cp) { int i; assert (fp != NULL); assert (cp != NULL); switch ( cp->type ) { case CONF_COMMENT: if ( cp->var ) fprintf (fp, "# %s\n", (char *)cp->var); else fprintf (fp, "\n"); break; case CONF_LEVEL: case CONF_FACILITY: if ( *(char **)cp->var != NULL ) { if ( **(char **)cp->var != '\0' ) { char *p; fprintf (fp, "%s:\t", cp->label); for ( p = *(char **)cp->var; *p; p++ ) putc (toupper (*p), fp); fprintf (fp, "\n"); } else fprintf (fp, "%s:\tNONE", cp->label); } break; case CONF_STRING: if ( *(char **)cp->var ) fprintf (fp, "%s:\t\"%s\"\n", cp->label, *(char **)cp->var); break; case CONF_BOOL: fprintf (fp, "%s:\t%s\n", cp->label, bool2str ( *(int*)cp->var )); break; case CONF_TIMEINT: i = *(ulong*)cp->var; fprintf (fp, "%s:\t%s", cp->label, timeint2str (i)); if ( i ) fprintf (fp, "\t# (%d seconds)", i); putc ('\n', fp); break; case CONF_ALGO: i = *(int*)cp->var; fprintf (fp, "%s:\t%s", cp->label, dki_algo2str (i)); fprintf (fp, "\t# (Algorithm ID %d)\n", i); break; case CONF_SERIAL: fprintf (fp, "%s:\t", cp->label); if ( *(serial_form_t*)cp->var == Unixtime ) fprintf (fp, "unixtime\n"); else fprintf (fp, "incremental\n"); break; case CONF_INT: fprintf (fp, "%s:\t%d\n", cp->label, *(int *)cp->var); break; case CONF_END: /* NOTREACHED */ break; } } /***************************************************************** ** public function definition *****************************************************************/ /***************************************************************** ** loadconfig (file, conf) ** Loads a config file into the "conf" structure pointed to by "z". ** If "z" is NULL then a new conf struct will be dynamically ** allocated. ** If no filename is given the conf struct will be initialized ** by the builtin default config *****************************************************************/ zconf_t *loadconfig (const char *filename, zconf_t *z) { FILE *fp; char buf[1023+1]; unsigned int line; if ( z == NULL ) /* allocate new memory for zconf_t */ { if ( (z = calloc (1, sizeof (zconf_t))) == NULL ) return NULL; if ( filename && *filename ) memcpy (z, &def, sizeof (*z)); /* init new struct with defaults */ } if ( filename == NULL || *filename == '\0' ) /* no file name given... */ { dbg_val0("loadconfig (NULL)\n"); memcpy (z, &def, sizeof (*z)); /* ..then init with defaults */ return z; } dbg_val1 ("loadconfig (%s)\n", filename); set_all_varptr (z); if ( (fp = fopen(filename, "r")) == NULL ) fatal ("Could not open config file \"%s\"\n", filename); line = 0; while (fgets(buf, sizeof(buf), fp)) { line++; parseconfigline (buf, line, z); } fclose(fp); return z; } # define STRCONFIG_DELIMITER ";\r\n" zconf_t *loadconfig_fromstr (const char *str, zconf_t *z) { char *buf; char *tok, *toksave; unsigned int line; if ( z == NULL ) { if ( (z = calloc (1, sizeof (zconf_t))) == NULL ) return NULL; memcpy (z, &def, sizeof (*z)); /* init with defaults */ } if ( str == NULL || *str == '\0' ) { dbg_val0("loadconfig_fromstr (NULL)\n"); memcpy (z, &def, sizeof (*z)); /* init with defaults */ return z; } dbg_val1 ("loadconfig_fromstr (\"%s\")\n", str); set_all_varptr (z); /* str is const, so we have to copy it into a new buffer */ if ( (buf = strdup (str)) == NULL ) fatal ("loadconfig_fromstr: Out of memory"); line = 0; tok = strtok_r (buf, STRCONFIG_DELIMITER, &toksave); while ( tok ) { line++; parseconfigline (tok, line, z); tok = strtok_r (NULL, STRCONFIG_DELIMITER, &toksave); } free (buf); return z; } /***************************************************************** ** dupconfig (config) ** duplicate config struct and return a ptr to the new struct *****************************************************************/ zconf_t *dupconfig (const zconf_t *conf) { zconf_t *z; assert (conf != NULL); if ( (z = calloc (1, sizeof (zconf_t))) == NULL ) return NULL; memcpy (z, conf, sizeof (*conf)); return z; } /***************************************************************** ** setconfigpar (entry, pval) *****************************************************************/ int setconfigpar (zconf_t *config, char *entry, const void *pval) { char *str; zconf_para_t *c; set_all_varptr (config); for ( c = confpara; c->type != CONF_END; c++ ) if ( strcasecmp (entry, c->label) == 0 ) { switch ( c->type ) { case CONF_LEVEL: case CONF_FACILITY: case CONF_STRING: if ( pval ) { str = strdup ((char *)pval); str_untaint (str); /* remove "bad" characters */ } else str = NULL; *((char **)c->var) = str; break; case CONF_BOOL: /* fall through */ case CONF_ALGO: /* fall through */ case CONF_TIMEINT: /* fall through */ case CONF_INT: *((int *)c->var) = *((int *)pval); break; case CONF_SERIAL: *((serial_form_t *)c->var) = *((serial_form_t *)pval); break; case CONF_COMMENT: case CONF_END: /* NOTREACHED */ break; } return 1; } return 0; } /***************************************************************** ** printconfig (fname, config) *****************************************************************/ int printconfig (const char *fname, const zconf_t *z) { zconf_para_t *cp; FILE *fp; if ( z == NULL ) return 0; fp = stdout; if ( fname && *fname ) { if ( strcmp (fname, "stdout") == 0 ) fp = stdout; else if ( strcmp (fname, "stderr") == 0 ) fp = stderr; else if ( (fp = fopen(fname, "w")) == NULL ) { error ("Could not open config file \"%s\" for writing\n", fname); return -1; } } set_all_varptr ((zconf_t *)z); for ( cp = confpara; cp->type != CONF_END; cp++ ) /* loop through all parameter */ if ( !cp->cmdline ) /* if this is not a command line parameter ? */ printconfigline (fp, cp); /* print it out */ if ( fp && fp != stdout && fp != stderr ) fclose (fp); return 1; } #if 0 /***************************************************************** ** printconfigdiff (fname, conf_a, conf_b) *****************************************************************/ int printconfigdiff (const char *fname, const zconf_t *ref, const zconf_t *z) { zconf_para_t *cp; FILE *fp; if ( ref == NULL || z == NULL ) return 0; fp = NULL; if ( fname && *fname ) { if ( strcmp (fname, "stdout") == 0 ) fp = stdout; else if ( strcmp (fname, "stderr") == 0 ) fp = stderr; else if ( (fp = fopen(fname, "w")) == NULL ) { error ("Could not open config file \"%s\" for writing\n", fname); return -1; } } set_all_varptr ((zconf_t *)z); for ( cp = confpara; cp->type != CONF_END; cp++ ) /* loop through all parameter */ { if ( cp->cmdline ) continue; printconfigline (fp, cp); /* print it out */ } if ( fp && fp != stdout && fp != stderr ) fclose (fp); return 1; } #endif /***************************************************************** ** checkconfig (config) *****************************************************************/ int checkconfig (const zconf_t *z) { if ( z == NULL ) return 1; if ( z->sigvalidity < (1 * DAYSEC) || z->sigvalidity > (12 * WEEKSEC) ) { fprintf (stderr, "Signature should be valid for at least 1 day and no longer than 3 month (12 weeks)\n"); fprintf (stderr, "The current value is %s\n", timeint2str (z->sigvalidity)); } if ( z->resign > (z->sigvalidity*5/6) - (z->max_ttl + z->proptime) ) { fprintf (stderr, "Re-signing interval (%s) should be less than ", timeint2str (z->resign)); fprintf (stderr, "5/6 of sigvalidity\n"); } if ( z->resign < (z->max_ttl + z->proptime) ) { fprintf (stderr, "Re-signing interval (%s) should be ", timeint2str (z->resign)); fprintf (stderr, "greater than max_ttl (%d) plus ", z->max_ttl); fprintf (stderr, "propagation time (%d)\n", z->proptime); } if ( z->max_ttl >= z->sigvalidity ) fprintf (stderr, "Max TTL (%d) should be less than signatur validity (%d)\n", z->max_ttl, z->sigvalidity); if ( z->z_life > (12 * WEEKSEC) * (z->z_bits / 512.) ) { fprintf (stderr, "Lifetime of zone signing key (%s) ", timeint2str (z->z_life)); fprintf (stderr, "seems a little bit high "); fprintf (stderr, "(In respect of key size (%d))\n", z->z_bits); } if ( z->k_life > 0 && z->k_life <= z->z_life ) { fprintf (stderr, "Lifetime of key signing key (%s) ", timeint2str (z->k_life)); fprintf (stderr, "should be greater than lifetime of zsk\n"); } if ( z->k_life > 0 && z->k_life > (26 * WEEKSEC) * (z->k_bits / 512.) ) { fprintf (stderr, "Lifetime of key signing key (%s) ", timeint2str (z->k_life)); fprintf (stderr, "seems a little bit high "); fprintf (stderr, "(In respect of key size (%d))\n", z->k_bits); } return 1; } #ifdef CONF_TEST const char *progname; static zconf_t *config; main (int argc, char *argv[]) { char *optstr; int val; progname = *argv; config = loadconfig ("", (zconf_t *) NULL); /* load built in defaults */ while ( --argc >= 1 ) { optstr = *++argv; config = loadconfig_fromstr (optstr, config); } val = 1; setconfigpar (config, "-v", &val); val = 2; setconfigpar (config, "verboselog", &val); val = 1; setconfigpar (config, "recursive", &val); val = 1200; setconfigpar (config, "propagation", &val); printconfig ("stdout", config); } #endif