/****************************************************************
**
** @(#) zconf.c -- configuration file parser for dnssec.conf
**
** Most of the code is from the SixXS Heartbeat Client
** written by Jeroen Massar <jeroen@sixxs.net>
**
** New config types and many code changes by Holger Zuleger
**
** Copyright (c) Aug 2005, Jeroen Massar.
** Copyright (c) Aug 2005 - Apr 2010, 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 <sys/types.h>
# include <stdio.h>
# include <errno.h>
# include <unistd.h>
# include <stdlib.h>
# include <stdarg.h>
# include <string.h>
# include <strings.h>
# include <assert.h>
# include <ctype.h>
#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) == '=')
# define cmdln (0)
# define first (1)
# define last (0x7FFF)
# define iscmdline(x) ((x)->used_since == cmdln)
# define iscompatible(x) ((x)->used_since != cmdln && compversion >= (x)->used_since && \
((x)->used_till == 1 || (compversion <= (x)->used_till)))
typedef enum {
CONF_END = 0,
CONF_STRING,
CONF_INT,
CONF_TIMEINT,
CONF_BOOL,
CONF_ALGO,
CONF_SERIAL,
CONF_FACILITY,
CONF_LEVEL,
CONF_NSEC3,
CONF_COMMENT,
CONF_VERSION,
} ctype_t;
/*****************************************************************
** private (static) variables
*****************************************************************/
static int compversion;
static zconf_t def = {
ZONEDIR, RECURSIVE,
PRINTTIME, PRINTAGE, LJUST, LSCOLORTERM,
SIG_VALIDITY, MAX_TTL, KEY_TTL, PROPTIME, Unixtime,
RESIGN_INT,
KEY_ALGO, ADDITIONAL_KEY_ALGO,
KSK_LIFETIME, KSK_BITS, KSK_RANDOM,
ZSK_LIFETIME, ZSK_BITS, ZSK_RANDOM,
NSEC3_OFF, SALTLEN,
NULL, /* viewname cmdline parameter */
0, /* noexec cmdline parameter */
LOGFILE, LOGLEVEL, LOGDOMAINDIR, SYSLOGFACILITY, SYSLOGLEVEL, VERBOSELOG, 0,
DNSKEYFILE, ZONEFILE, KEYSETDIR,
LOOKASIDEDOMAIN,
SIG_RANDOM, SIG_PSEUDO, SIG_GENDS, SIG_DNSKEY_KSK, SIG_PARAM,
DIST_CMD, /* defaults to NULL which means to run "rndc reload" */
NAMED_CHROOT
};
typedef struct {
char *label; /* the name of the paramter */
short used_since; /* compability (from version; 0 == command line) */
short used_till; /* compability (to version) */
ctype_t type; /* the parameter type */
void *var; /* pointer to the parameter variable */
const void *var2; /* pointer to a second parameter variable */
/* this is a ugly hack needed by cmpconfig () */
} zconf_para_t;
static zconf_para_t confpara[] = {
{ "", first, last, CONF_COMMENT, ""},
{ "", first, last, CONF_COMMENT, "\t@(#) dnssec.conf "},
{ "", first, last, CONF_VERSION, "" },
{ "", first, last, CONF_COMMENT, ""},
{ "", first, last, CONF_COMMENT, NULL },
{ "", first, 99, CONF_COMMENT, "dnssec-zkt options" },
{ "", 100, last, CONF_COMMENT, "zkt-ls options" },
{ "ZoneDir", first, last, CONF_STRING, &def.zonedir },
{ "Recursive", first, last, CONF_BOOL, &def.recursive },
{ "PrintTime", first, last, CONF_BOOL, &def.printtime },
{ "PrintAge", first, last, CONF_BOOL, &def.printage },
{ "LeftJustify", first, last, CONF_BOOL, &def.ljust },
{ "lsColor", 100, last, CONF_STRING, &def.colorterm },
{ "", first, last, CONF_COMMENT, NULL },
{ "", first, last, CONF_COMMENT, "zone specific values" },
{ "ResignInterval", first, last, CONF_TIMEINT, &def.resign },
{ "SigValidity", first, last, CONF_TIMEINT, &def.sigvalidity },
{ "Max_TTL", first, 100, CONF_TIMEINT, &def.max_ttl },
{ "MaximumTTL", 101, last, CONF_TIMEINT, &def.max_ttl },
{ "Propagation", first, last, CONF_TIMEINT, &def.proptime },
{ "Key_TTL", 90, 100, CONF_TIMEINT, &def.key_ttl },
{ "DnsKeyTTL", 101, last, CONF_TIMEINT, &def.key_ttl },
#if defined (DEF_TTL)
{ "def_ttl", first, last, CONF_TIMEINT, &def.def_ttl },
#endif
{ "SerialFormat", 92, last, CONF_SERIAL, &def.serialform },
{ "", first, last, CONF_COMMENT, NULL },
{ "", first, last, CONF_COMMENT, "signing key parameters"},
{ "Key_Algo", 99, 100, CONF_ALGO, &def.k_algo }, /* now used as general KEY algoritjm (KSK & ZSK) */
{ "KeyAlgo", 101, last, CONF_ALGO, &def.k_algo }, /* now used as general KEY algoritjm (KSK & ZSK) */
{ "AddKey_Algo", 99, 100, CONF_ALGO, &def.k2_algo }, /* second key algorithm added (v0.99) */
{ "AddKeyAlgo", 101, last, CONF_ALGO, &def.k2_algo }, /* second key algorithm added (v0.99) */
{ "KSK_lifetime", first, 100, CONF_TIMEINT, &def.k_life },
{ "KSKlifetime", 101, last, CONF_TIMEINT, &def.k_life },
{ "KSK_algo", first, 98, CONF_ALGO, &def.k_algo }, /* old KSK value changed to key algorithm */
{ "KSK_bits", first, 100, CONF_INT, &def.k_bits },
{ "KSKbits", 101, last, CONF_INT, &def.k_bits },
{ "KSK_randfile", first, 100, CONF_STRING, &def.k_random },
{ "KSKrandfile", 101, last, CONF_STRING, &def.k_random },
{ "ZSK_lifetime", first, 100, CONF_TIMEINT, &def.z_life },
{ "ZSKlifetime", 101, last, CONF_TIMEINT, &def.z_life },
/* { "ZSK_algo", 1, CONF_ALGO, &def.z_algo }, ZSK algo removed (set to same as ksk) */
{ "ZSK_algo", first, 98, CONF_ALGO, &def.k2_algo }, /* if someone using it already, map the algo to the additional key algorithm */
{ "ZSK_bits", first, 100, CONF_INT, &def.z_bits },
{ "ZSKbits", 101, last, CONF_INT, &def.z_bits },
{ "ZSK_randfile", first, 100, CONF_STRING, &def.z_random },
{ "ZSKrandfile", 101, last, CONF_STRING, &def.z_random },
{ "NSEC3", 100, last, CONF_NSEC3, &def.nsec3 },
{ "SaltBits", 98, last, CONF_INT, &def.saltbits },
{ "", first, last, CONF_COMMENT, NULL },
{ "", first, 99, CONF_COMMENT, "dnssec-signer options"},
{ "", 100, last, CONF_COMMENT, "zkt-signer options"},
{ "--view", cmdln, last, CONF_STRING, &def.view },
{ "--noexec", cmdln, last, CONF_BOOL, &def.noexec },
{ "LogFile", 96, last, CONF_STRING, &def.logfile },
{ "LogLevel", 96, last, CONF_LEVEL, &def.loglevel },
{ "LogDomainDir", 96, last, CONF_STRING, &def.logdomaindir },
{ "SyslogFacility", 96, last, CONF_FACILITY, &def.syslogfacility },
{ "SyslogLevel", 96, last, CONF_LEVEL, &def.sysloglevel },
{ "VerboseLog", 96, last, CONF_INT, &def.verboselog },
{ "-v", cmdln, last, CONF_INT, &def.verbosity },
{ "KeyFile", first, last, CONF_STRING, &def.keyfile },
{ "ZoneFile", first, last, CONF_STRING, &def.zonefile },
{ "KeySetDir", first, last, CONF_STRING, &def.keysetdir },
{ "DLV_Domain", first, 100, CONF_STRING, &def.lookaside },
{ "DLVdomain", 101, last, CONF_STRING, &def.lookaside },
{ "Sig_Randfile", first, 100, CONF_STRING, &def.sig_random },
{ "SigRandfile", 101, last, CONF_STRING, &def.sig_random },
{ "Sig_Pseudorand", first, 100, CONF_BOOL, &def.sig_pseudo },
{ "SigPseudorand", 101, last, CONF_BOOL, &def.sig_pseudo },
{ "Sig_GenerateDS", first, 100, CONF_BOOL, &def.sig_gends },
{ "SigGenerateDS", 101, last, CONF_BOOL, &def.sig_gends },
{ "Sig_DnsKeyKSK", 99, 100, CONF_BOOL, &def.sig_dnskeyksk },
{ "SigDnsKeyKSK", 101, last, CONF_BOOL, &def.sig_dnskeyksk },
{ "Sig_Parameter", first, 100, CONF_STRING, &def.sig_param },
{ "SigParameter", 101, last, CONF_STRING, &def.sig_param },
{ "Distribute_Cmd", 97, 100, CONF_STRING, &def.dist_cmd },
{ "DistributeCmd", 101, last, CONF_STRING, &def.dist_cmd },
{ "NamedChrootDir", 99, last, CONF_STRING, &def.chroot_dir },
{ NULL, 0, 0, CONF_END, NULL},
};
/*****************************************************************
** private (static) function deklaration and definition
*****************************************************************/
static const char *bool2str (int val)
{
return val ? "True" : "False";
}
static int set_varptr (char *entry, void *ptr, const void *ptr2)
{
zconf_para_t *c;
for ( c = confpara; c->label; c++ )
if ( strcasecmp (entry, c->label) == 0 )
{
c->var = ptr;
c->var2 = ptr2;
return 1;
}
return 0;
}
static void set_all_varptr (zconf_t *cp, const zconf_t *cp2)
{
set_varptr ("zonedir", &cp->zonedir, cp2 ? &cp2->zonedir: NULL);
set_varptr ("recursive", &cp->recursive, cp2 ? &cp2->recursive: NULL);
set_varptr ("printage", &cp->printage, cp2 ? &cp2->printage: NULL);
set_varptr ("printtime", &cp->printtime, cp2 ? &cp2->printtime: NULL);
set_varptr ("leftjustify", &cp->ljust, cp2 ? &cp2->ljust: NULL);
set_varptr ("lscolor", &cp->colorterm, cp2 ? &cp2->colorterm: NULL);
set_varptr ("resigninterval", &cp->resign, cp2 ? &cp2->resign: NULL);
set_varptr ("sigvalidity", &cp->sigvalidity, cp2 ? &cp2->sigvalidity: NULL);
set_varptr ("max_ttl", &cp->max_ttl, cp2 ? &cp2->max_ttl: NULL);
set_varptr ("key_ttl", &cp->key_ttl, cp2 ? &cp2->key_ttl: NULL);
set_varptr ("propagation", &cp->proptime, cp2 ? &cp2->proptime: NULL);
#if defined (DEF_TTL)
set_varptr ("def_ttl", &cp->def_ttl, cp2 ? &cp2->def_ttl: NULLl);
#endif
set_varptr ("serialformat", &cp->serialform, cp2 ? &cp2->serialform: NULL);
set_varptr ("key_algo", &cp->k_algo, cp2 ? &cp2->k_algo: NULL);
set_varptr ("addkey_algo", &cp->k2_algo, cp2 ? &cp2->k2_algo: NULL);
set_varptr ("ksk_lifetime", &cp->k_life, cp2 ? &cp2->k_life: NULL);
set_varptr ("ksk_algo", &cp->k_algo, cp2 ? &cp2->k_algo: NULL); /* used only in compability mode */
set_varptr ("ksk_bits", &cp->k_bits, cp2 ? &cp2->k_bits: NULL);
set_varptr ("ksk_randfile", &cp->k_random, cp2 ? &cp2->k_random: NULL);
set_varptr ("zsk_lifetime", &cp->z_life, cp2 ? &cp2->z_life: NULL);
// set_varptr ("zsk_algo", &cp->z_algo, cp2 ? &cp2->z_algo: NULL);
set_varptr ("zsk_algo", &cp->k2_algo, cp2 ? &cp2->k2_algo: NULL);
set_varptr ("zsk_bits", &cp->z_bits, cp2 ? &cp2->z_bits: NULL);
set_varptr ("zsk_randfile", &cp->z_random, cp2 ? &cp2->z_random: NULL);
set_varptr ("nsec3", &cp->nsec3, cp2 ? &cp2->nsec3: NULL);
set_varptr ("saltbits", &cp->saltbits, cp2 ? &cp2->saltbits: NULL);
set_varptr ("--view", &cp->view, cp2 ? &cp2->view: NULL);
set_varptr ("--noexec", &cp->noexec, cp2 ? &cp2->noexec: NULL);
set_varptr ("logfile", &cp->logfile, cp2 ? &cp2->logfile: NULL);
set_varptr ("loglevel", &cp->loglevel, cp2 ? &cp2->loglevel: NULL);
set_varptr ("logdomaindir", &cp->logdomaindir, cp2 ? &cp2->logdomaindir: NULL);
set_varptr ("syslogfacility", &cp->syslogfacility, cp2 ? &cp2->syslogfacility: NULL);
set_varptr ("sysloglevel", &cp->sysloglevel, cp2 ? &cp2->sysloglevel: NULL);
set_varptr ("verboselog", &cp->verboselog, cp2 ? &cp2->verboselog: NULL);
set_varptr ("-v", &cp->verbosity, cp2 ? &cp2->verbosity: NULL);
set_varptr ("keyfile", &cp->keyfile, cp2 ? &cp2->keyfile: NULL);
set_varptr ("zonefile", &cp->zonefile, cp2 ? &cp2->zonefile: NULL);
set_varptr ("keysetdir", &cp->keysetdir, cp2 ? &cp2->keysetdir: NULL);
set_varptr ("dlv_domain", &cp->lookaside, cp2 ? &cp2->lookaside: NULL);
set_varptr ("sig_randfile", &cp->sig_random, cp2 ? &cp2->sig_random: NULL);
set_varptr ("sig_pseudorand", &cp->sig_pseudo, cp2 ? &cp2->sig_pseudo: NULL);
set_varptr ("sig_generateds", &cp->sig_gends, cp2 ? &cp2->sig_gends: NULL);
set_varptr ("sig_dnskeyksk", &cp->sig_dnskeyksk, cp2 ? &cp2->sig_dnskeyksk: NULL);
set_varptr ("sig_parameter", &cp->sig_param, cp2 ? &cp2->sig_param: NULL);
set_varptr ("distribute_cmd", &cp->dist_cmd, cp2 ? &cp2->dist_cmd: NULL);
set_varptr ("namedchrootdir", &cp->chroot_dir, cp2 ? &cp2->chroot_dir: NULL);
}
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;
assert (buf[0] != '\0');
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;
long lval;
found = 1;
switch ( c->type )
{
case CONF_VERSION:
break;
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';
if ( *val == 'u' || *val == 'U' )
lval = 0L;
else
sscanf (val, "%ld%c", &lval, &quantity);
if ( quantity == 'm' )
lval *= MINSEC;
else if ( quantity == 'h' )
lval *= HOURSEC;
else if ( quantity == 'd' )
lval *= DAYSEC;
else if ( quantity == 'w' )
lval *= WEEKSEC;
else if ( quantity == 'y' )
lval *= YEARSEC;
(*(long *)c->var) = lval;
break;
case CONF_ALGO:
if ( strcmp (val, "1") == 0 || strcasecmp (val, "rsa") == 0 ||
strcasecmp (val, "rsamd5") == 0 )
*((int *)c->var) = DK_ALGO_RSA;
else if ( strcmp (val, "3") == 0 ||
strcasecmp (val, "dsa") == 0 )
*((int *)c->var) = DK_ALGO_DSA;
else if ( strcmp (val, "5") == 0 ||
strcasecmp (val, "rsasha1") == 0 )
*((int *)c->var) = DK_ALGO_RSASHA1;
else if ( strcmp (val, "6") == 0 ||
strcasecmp (val, "nsec3dsa") == 0 ||
strcasecmp (val, "n3dsa") == 0 )
*((int *)c->var) = DK_ALGO_NSEC3DSA;
else if ( strcmp (val, "7") == 0 ||
strcasecmp (val, "nsec3rsasha1") == 0 ||
strcasecmp (val, "n3rsasha1") == 0 )
*((int *)c->var) = DK_ALGO_NSEC3RSASHA1;
#if defined(BIND_VERSION) && BIND_VERSION >= 970
else if ( strcmp (val, "8") == 0 ||
strcasecmp (val, "rsasha2") == 0 ||
strcasecmp (val, "rsasha256") == 0 ||
strcasecmp (val, "nsec3rsasha2") == 0 ||
strcasecmp (val, "n3rsasha2") == 0 ||
strcasecmp (val, "nsec3rsasha256") == 0 ||
strcasecmp (val, "n3rsasha256") == 0 )
*((int *)c->var) = DK_ALGO_RSASHA256;
else if ( strcmp (val, "10") == 0 ||
strcasecmp (val, "rsasha5") == 0 ||
strcasecmp (val, "rsasha212") == 0 ||
strcasecmp (val, "nsec3rsasha5") == 0 ||
strcasecmp (val, "n3rsasha5") == 0 ||
strcasecmp (val, "nsec3rsasha512") == 0 ||
strcasecmp (val, "n3rsasha512") == 0 )
*((int *)c->var) = DK_ALGO_RSASHA512;
#endif
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 || strcasecmp (val, "inc") == 0 )
*((serial_form_t *)c->var) = Incremental;
else
error ("Illegal serial no format \"%s\" "
"in line %d.\n" , val, line);
break;
case CONF_NSEC3:
if ( strcasecmp (val, "off") == 0 )
*((nsec3_t *)c->var) = NSEC3_OFF;
else if ( strcasecmp (val, "on") == 0 )
*((nsec3_t *)c->var) = NSEC3_ON;
else if ( strcasecmp (val, "optout") == 0 )
*((nsec3_t *)c->var) = NSEC3_OPTOUT;
else
error ("Illegal NSEC3 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;
long lval;
assert (fp != NULL);
assert (cp != NULL);
switch ( cp->type )
{
case CONF_VERSION:
fprintf (fp, "#\tZKT config file for version %d.%02d\n",
compversion / 100, compversion % 100);
break;
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:
lval = *(ulong*)cp->var; /* in that case it should be of type ulong */
fprintf (fp, "%s:\t%s", cp->label, timeint2str (lval));
if ( lval )
fprintf (fp, "\t\t# (%ld seconds)", lval);
putc ('\n', fp);
break;
case CONF_ALGO:
i = *(int*)cp->var;
if ( i )
{
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");
else
fprintf (fp, "Incremental");
fprintf (fp, "\t# (UnixTime|Incremental)\n");
break;
case CONF_NSEC3:
fprintf (fp, "%s:\t\t", cp->label);
if ( *(nsec3_t*)cp->var == NSEC3_OFF )
fprintf (fp, "Off");
else if ( *(nsec3_t*)cp->var == NSEC3_ON )
fprintf (fp, "On");
else if ( *(nsec3_t*)cp->var == NSEC3_OPTOUT )
fprintf (fp, "OptOut");
fprintf (fp, "\t\t# (On|Off|OptOut)\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
*****************************************************************/
void setconfigversion (int version)
{
compversion = version;
}
const char *timeint2str (unsigned long val)
{
static char str[20+1];
if ( val == 0 )
snprintf (str, sizeof (str), "Unset");
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;
}
/*****************************************************************
** 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
** with 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 (zconf_t)); /* init new struct with defaults */
}
if ( filename == NULL || *filename == '\0' ) /* no file name given... */
{
dbg_val0("loadconfig (NULL)\n");
memcpy (z, &def, sizeof (zconf_t)); /* ..then init with defaults */
return z;
}
dbg_val1 ("loadconfig (%s)\n", filename);
set_all_varptr (z, NULL);
if ( (fp = fopen(filename, "r")) == NULL )
fatal ("Could not open config file \"%s\"\n", filename);
line = 0;
while (fgets(buf, sizeof(buf), fp))
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 (zconf_t)); /* init with defaults */
}
if ( str == NULL || *str == '\0' )
{
dbg_val0("loadconfig_fromstr (NULL)\n");
memcpy (z, &def, sizeof (zconf_t)); /* init with defaults */
return z;
}
dbg_val1 ("loadconfig_fromstr (\"%s\")\n", str);
set_all_varptr (z, NULL);
/* 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 (zconf_t));
return z;
}
/*****************************************************************
** freeconfig (config)
** free memory for config struct and return a NULL ptr
*****************************************************************/
zconf_t *freeconfig (zconf_t *conf)
{
if (conf != NULL);
free (conf);
return (zconf_t *)NULL;
}
/*****************************************************************
** setconfigpar (entry, pval)
*****************************************************************/
int setconfigpar (zconf_t *config, char *entry, const void *pval)
{
char *str;
zconf_para_t *c;
set_all_varptr (config, NULL);
for ( c = confpara; c->type != CONF_END; c++ )
if ( strcasecmp (entry, c->label) == 0 )
{
switch ( c->type )
{
case CONF_VERSION:
break;
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_INT:
*((int *)c->var) = *((int *)pval);
break;
case CONF_TIMEINT:
*((long *)c->var) = *((long *)pval);
break;
case CONF_NSEC3:
*((nsec3_t *)c->var) = *((nsec3_t *)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, NULL);
for ( cp = confpara; cp->type != CONF_END; cp++ ) /* loop through all parameter */
if ( iscompatible (cp) ) /* is parameter compatible to current version? */
printconfigline (fp, cp); /* print it out */
if ( fp && fp != stdout && fp != stderr )
fclose (fp);
return 1;
}
/*****************************************************************
** printconfigdiff (fname, conf_a, conf_b)
*****************************************************************/
int printconfigdiff (const char *fname, const zconf_t *ref, const zconf_t *z)
{
zconf_para_t *cp;
int eq;
char *p1, *p2;
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, ref);
for ( cp = confpara; cp->type != CONF_END; cp++ ) /* loop through all parameter */
{
eq = 0;
if ( iscmdline (cp) ) /* skip command line parameter */
continue;
switch ( cp->type )
{
case CONF_VERSION:
case CONF_END:
case CONF_COMMENT:
continue;
case CONF_NSEC3:
eq = ( *(nsec3_t *)cp->var == *(nsec3_t *)cp->var2 );
break;
case CONF_SERIAL:
eq = ( *(serial_form_t *)cp->var == *(serial_form_t *)cp->var2 );
break;
case CONF_BOOL:
case CONF_ALGO:
case CONF_INT:
eq = ( *(int *)cp->var == *(int *)cp->var2 );
break;
case CONF_TIMEINT:
eq = ( *(long *)cp->var == *(long *)cp->var2 );
break;
case CONF_LEVEL:
case CONF_FACILITY:
case CONF_STRING:
p1 = *(char **)cp->var;
p2 = *(char **)cp->var2;
if ( p1 && p2 )
eq = strcmp (p1, p2) == 0;
else if ( p1 == NULL || p2 == NULL )
eq = 0;
else
eq = 1;
}
if ( !eq )
printconfigline (fp, cp); /* print it out */
}
if ( fp && fp != stdout && fp != stderr )
fclose (fp);
return 1;
}
/*****************************************************************
** checkconfig (config)
*****************************************************************/
int checkconfig (const zconf_t *z)
{
int ret;
long max_ttl;
if ( z == NULL )
return 1;
max_ttl = z->max_ttl;
if ( max_ttl <= 0 )
max_ttl = z->sigvalidity;
ret = 0;
if ( strcmp (z->k_random, "/dev/urandom") == 0 )
ret = fprintf (stderr, "random device without enough entropie used for KSK generation \n");
if ( strcmp (z->z_random, "/dev/urandom") == 0 )
ret = fprintf (stderr, "random device without enough entropie used for ZSK generation\n");
if ( z->saltbits < 4 )
ret = fprintf (stderr, "Saltlength must be at least 4 bits\n");
if ( z->saltbits > 128 )
{
fprintf (stderr, "While the maximum is 520 bits of salt, it's not recommended to use more than 128 bits.\n");
ret = fprintf (stderr, "The current value is %d bits\n", z->saltbits);
}
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");
ret = fprintf (stderr, "The current value is %s\n", timeint2str (z->sigvalidity));
}
if ( z->max_ttl <= 0 )
{
ret = fprintf (stderr, "The max TTL is unknown which results in suboptimal key rollover.\n");
fprintf (stderr, "Please set max_ttl to the maximum ttl used in the zone (run zkt-conf -w zone.db)\n");
}
else
if ( max_ttl > z->sigvalidity/2 )
ret = fprintf (stderr, "Max TTL (%ld) should be less or equal signature validity (%ld)\n",
max_ttl, z->sigvalidity);
// if ( z->resign > (z->sigvalidity*5/6) - (max_ttl + z->proptime) )
if ( z->resign > (z->sigvalidity*5/6) )
{
fprintf (stderr, "Re-signing interval (%s) should be less than ", timeint2str (z->resign));
ret = fprintf (stderr, "5/6 of sigvalidity (%s)\n", timeint2str (z->sigvalidity));
}
if ( z->max_ttl > 0 && z->resign > (z->sigvalidity - max_ttl) )
{
fprintf (stderr, "Re-signing interval (%s) should be ", timeint2str (z->resign));
fprintf (stderr, "end at least one max_ttl (%ld) before the end of ", max_ttl);
ret = fprintf (stderr, "signature lifetime (%ld) (%s)\n", z->sigvalidity, timeint2str(z->sigvalidity - max_ttl));
}
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 ");
ret = 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));
ret = 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 ");
ret = fprintf (stderr, "(In respect of key size (%d))\n", z->k_bits);
}
return !ret;
}
#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