# 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) == '=' )
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;
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,
LOGFILE, LOGLEVEL, SYSLOGFACILITY, SYSLOGLEVEL, VERBOSELOG, 0,
DNSKEYFILE, ZONEFILE, KEYSETDIR,
LOOKASIDEDOMAIN,
SIG_RANDOM, SIG_PSEUDO, SIG_GENDS, SIG_PARAM,
DIST_CMD
};
typedef struct {
char *label;
int cmdline;
ctype_t type;
void *var;
} 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},
};
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];
while ( p >= buf && isspace (*p) )
*p-- = '\0';
for (p = buf; isspace (*p); p++ )
;
if ( *p == '\0' || ISCOMMENT (p) )
return;
tag = p;
end = &buf[strlen(buf)-1];
while ( p < end && !ISDELIM (*p) )
p++;
*p++ = '\0';
dbg_val1 ("Parsing \"%s\"\n", tag);
while ( p < end && ISDELIM (*p) )
p++;
val = p;
dbg_val1 ("\tgot value \"%s\"\n", val);
if ( *p == '"' || *p == '\'' )
{
p++;
while ( p <= end && *p && *p != *val )
p++;
*p = '\0';
val++;
}
else
{
while ( p < end && *p && !ISCOMMENT(p) )
p++;
if ( ISCOMMENT (p) )
{
do
*p-- = '\0';
while ( p >= val && isspace (*p) );
}
}
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);
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:
break;
}
}
zconf_t *loadconfig (const char *filename, zconf_t *z)
{
FILE *fp;
char buf[1023+1];
unsigned int line;
if ( z == NULL )
{
if ( (z = calloc (1, sizeof (zconf_t))) == NULL )
return NULL;
if ( filename && *filename )
memcpy (z, &def, sizeof (*z));
}
if ( filename == NULL || *filename == '\0' )
{
dbg_val0("loadconfig (NULL)\n");
memcpy (z, &def, sizeof (*z));
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));
}
if ( str == NULL || *str == '\0' )
{
dbg_val0("loadconfig_fromstr (NULL)\n");
memcpy (z, &def, sizeof (*z));
return z;
}
dbg_val1 ("loadconfig_fromstr (\"%s\")\n", str);
set_all_varptr (z);
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;
}
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;
}
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);
}
else
str = NULL;
*((char **)c->var) = str;
break;
case CONF_BOOL:
case CONF_ALGO:
case CONF_TIMEINT:
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:
break;
}
return 1;
}
return 0;
}
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++ )
if ( !cp->cmdline )
printconfigline (fp, cp);
if ( fp && fp != stdout && fp != stderr )
fclose (fp);
return 1;
}
#if 0
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++ )
{
if ( cp->cmdline )
continue;
printconfigline (fp, cp);
}
if ( fp && fp != stdout && fp != stderr )
fclose (fp);
return 1;
}
#endif
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);
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