summaryrefslogtreecommitdiffstats
path: root/contrib/zkt/dki.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/zkt/dki.c')
-rw-r--r--contrib/zkt/dki.c1185
1 files changed, 1185 insertions, 0 deletions
diff --git a/contrib/zkt/dki.c b/contrib/zkt/dki.c
new file mode 100644
index 0000000..81498ae
--- /dev/null
+++ b/contrib/zkt/dki.c
@@ -0,0 +1,1185 @@
+/*****************************************************************
+**
+** @(#) dki.c (c) Jan 2005 Holger Zuleger hznet.de
+**
+** A library for managing BIND dnssec key files.
+**
+** Copyright (c) Jan 2005, Holger Zuleger HZnet. 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 Holger Zuleger HZnet 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 <stdio.h>
+# include <string.h>
+# include <ctype.h> /* tolower(), ... */
+# include <unistd.h> /* link(), unlink(), ... */
+# include <stdlib.h>
+# include <sys/types.h>
+# include <sys/time.h>
+# include <sys/stat.h>
+# include <dirent.h>
+# include <assert.h>
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+# include "config_zkt.h"
+# include "debug.h"
+# include "domaincmp.h"
+# include "misc.h"
+# include "zconf.h"
+#define extern
+# include "dki.h"
+#undef extern
+
+/*****************************************************************
+** private (static) function declaration and definition
+*****************************************************************/
+static char dki_estr[255+1];
+
+static dki_t *dki_alloc ()
+{
+ dki_estr[0] = '\0';
+ dki_t *dkp = malloc (sizeof (dki_t));
+
+ if ( (dkp = malloc (sizeof (dki_t))) )
+ {
+ memset (dkp, 0, sizeof (dki_t));
+ return dkp;
+ }
+
+ snprintf (dki_estr, sizeof (dki_estr),
+ "dki_alloc: Out of memory");
+ return NULL;
+}
+
+static int dki_readfile (FILE *fp, dki_t *dkp)
+{
+ int algo, flags, type;
+ int c;
+ char *p;
+ char buf[4095+1];
+ char tag[25+1];
+ char val[14+1]; /* e.g. "YYYYMMDDhhmmss" | "60d" */
+
+ assert (dkp != NULL);
+ assert (fp != NULL);
+
+ while ( (c = getc (fp)) == ';' ) /* line start with comment ? */
+ {
+ tag[0] = val[0] = '\0';
+ if ( (c = getc (fp)) == '%' ) /* special comment? */
+ {
+ while ( (c = getc (fp)) == ' ' || c == '\t' )
+ ;
+ ungetc (c, fp);
+ /* then try to read in the creation, expire and lifetime */
+ if ( fscanf (fp, "%25[a-zA-Z]=%14s", tag, val) == 2 )
+ {
+ dbg_val2 ("dki_readfile: tag=%s val=%s \n", tag, val);
+ switch ( tolower (tag[0]) )
+ {
+ case 'g': dkp->gentime = timestr2time (val); break;
+ case 'e': dkp->exptime = timestr2time (val); break;
+ case 'l': dkp->lifetime = atoi (val) * DAYSEC; break;
+ }
+ }
+ }
+ else
+ ungetc (c, fp);
+ while ( (c = getc (fp)) != EOF && c != '\n' ) /* eat up rest of the line */
+ ;
+ }
+ ungetc (c, fp); /* push back last char */
+
+ if ( fscanf (fp, "%4095s", buf) != 1 ) /* read label */
+ return -1;
+
+ if ( strcmp (buf, dkp->name) != 0 )
+ return -2;
+
+#if defined(TTL_IN_KEYFILE_ALLOWED) && TTL_IN_KEYFILE_ALLOWED
+ /* skip optional TTL value */
+ while ( (c = getc (fp)) != EOF && isspace (c) ) /* skip spaces */
+ ;
+ if ( isdigit (c) ) /* skip ttl */
+ fscanf (fp, "%*d");
+ else
+ ungetc (c, fp); /* oops, no ttl */
+#endif
+
+ if ( (c = fscanf (fp, " IN DNSKEY %d %d %d", &flags, &type, &algo)) != 3 &&
+ (c = fscanf (fp, "KEY %d %d %d", &flags, &type, &algo)) != 3 )
+ return -3;
+ if ( type != 3 || algo != dkp->algo )
+ return -4; /* no DNSKEY or algorithm mismatch */
+ if ( ((flags >> 8) & 0xFF) != 01 )
+ return -5; /* no ZONE key */
+ dkp->flags = flags;
+
+ if ( fgets (buf, sizeof buf, fp) == NULL || buf[0] == '\0' )
+ return -6;
+ p = buf + strlen (buf);
+ *--p = '\0'; /* delete trailing \n */
+ /* delete leading ws */
+ for ( p = buf; *p && isspace (*p); p++ )
+ ;
+
+ dkp->pubkey = strdup (p);
+
+ return 0;
+}
+
+static int dki_writeinfo (const dki_t *dkp, const char *path)
+{
+ FILE *fp;
+
+ assert (dkp != NULL);
+ assert (path != NULL && path[0] != '\0');
+
+ if ( (fp = fopen (path, "w")) == NULL )
+ return 0;
+ dbg_val1 ("dki_writeinfo %s\n", path);
+ if ( dki_prt_dnskey_raw (dkp, fp) == 0 )
+ return 0;
+ fclose (fp);
+ touch (path, dkp->time); /* restore time of key file */
+
+ return 1;
+}
+
+static int dki_setstat (dki_t *dkp, int status, int preserve_time);
+
+/*****************************************************************
+** public function definition
+*****************************************************************/
+
+/*****************************************************************
+** dki_free ()
+*****************************************************************/
+void dki_free (dki_t *dkp)
+{
+ assert (dkp != NULL);
+
+ if ( dkp->pubkey )
+ free (dkp->pubkey);
+ free (dkp);
+}
+
+/*****************************************************************
+** dki_freelist ()
+*****************************************************************/
+void dki_freelist (dki_t **listp)
+{
+ dki_t *curr;
+ dki_t *next;
+
+ assert (listp != NULL);
+
+ curr = *listp;
+ while ( curr )
+ {
+ next = curr->next;
+ dki_free (curr);
+ curr = next;
+ }
+ if ( *listp )
+ *listp = NULL;
+}
+
+#if defined(USE_TREE) && USE_TREE
+/*****************************************************************
+** dki_tfree ()
+*****************************************************************/
+void dki_tfree (dki_t **tree)
+{
+ assert (tree != NULL);
+ // TODO: tdestroy is a GNU extension
+ // tdestroy (*tree, dki_free);
+}
+#endif
+
+/*****************************************************************
+** dki_new ()
+** create new keyfile
+** allocate memory for new dki key and init with keyfile
+*****************************************************************/
+dki_t *dki_new (const char *dir, const char *name, int ksk, int algo, int bitsize, const char *rfile, int lf_days)
+{
+ char cmdline[511+1];
+ char fname[254+1];
+ char randfile[254+1];
+ FILE *fp;
+ int len;
+ char *flag = "";
+ char *expflag = "";
+ dki_t *new;
+
+ if ( ksk )
+ flag = "-f KSK";
+
+ randfile[0] = '\0';
+ if ( rfile && *rfile )
+ snprintf (randfile, sizeof (randfile), "-r %.250s ", rfile);
+
+ if ( algo == DK_ALGO_RSA || algo == DK_ALGO_RSASHA1 )
+ expflag = "-e ";
+
+ if ( dir && *dir )
+ snprintf (cmdline, sizeof (cmdline), "cd %s ; %s %s%s-n ZONE -a %s -b %d %s %s",
+ dir, KEYGENCMD, randfile, expflag, dki_algo2str(algo), bitsize, flag, name);
+ else
+ snprintf (cmdline, sizeof (cmdline), "%s %s%s-n ZONE -a %s -b %d %s %s",
+ KEYGENCMD, randfile, expflag, dki_algo2str(algo), bitsize, flag, name);
+
+ dbg_msg (cmdline);
+
+ if ( (fp = popen (cmdline, "r")) == NULL || fgets (fname, sizeof fname, fp) == NULL )
+ return NULL;
+ pclose (fp);
+
+ len = strlen (fname) - 1;
+ if ( len >= 0 && fname[len] == '\n' )
+ fname[len] = '\0';
+
+ new = dki_read (dir, fname);
+ if ( new )
+ dki_setlifetime (new, lf_days); /* sets gentime + proposed lifetime */
+
+ return new;
+}
+
+/*****************************************************************
+** dki_read ()
+** read key from file 'filename' (independed of the extension)
+*****************************************************************/
+dki_t *dki_read (const char *dirname, const char *filename)
+{
+ dki_t *dkp;
+ FILE *fp;
+ struct stat st;
+ int len;
+ int err;
+ char fname[MAX_FNAMESIZE+1];
+ char path[MAX_PATHSIZE+1];
+
+ dki_estr[0] = '\0';
+ if ( (dkp = dki_alloc ()) == NULL )
+ return (NULL);
+
+ len = sizeof (fname) - 1;
+ fname[len] = '\0';
+ strncpy (fname, filename, len);
+
+ len = strlen (fname); /* delete extension */
+ if ( len > 4 && strcmp (&fname[len - 4], DKI_KEY_FILEEXT) == 0 )
+ fname[len - 4] = '\0';
+ else if ( len > 10 && strcmp (&fname[len - 10], DKI_PUB_FILEEXT) == 0 )
+ fname[len - 10] = '\0';
+ else if ( len > 8 && strcmp (&fname[len - 8], DKI_ACT_FILEEXT) == 0 )
+ fname[len - 8] = '\0';
+ else if ( len > 12 && strcmp (&fname[len - 12], DKI_DEP_FILEEXT) == 0 )
+ fname[len - 12] = '\0';
+ dbg_line ();
+
+ assert (strlen (dirname)+1 < sizeof (dkp->dname));
+ strcpy (dkp->dname, dirname);
+
+ assert (strlen (fname)+1 < sizeof (dkp->fname));
+ strcpy (dkp->fname, fname);
+ dbg_line ();
+ if ( sscanf (fname, "K%254[^+]+%hd+%d", dkp->name, &dkp->algo, &dkp->tag) != 3 )
+ {
+ snprintf (dki_estr, sizeof (dki_estr),
+ "dki_read: Filename don't match expected format (%s)", fname);
+ return (NULL);
+ }
+
+ pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_KEY_FILEEXT);
+ dbg_val ("dki_read: path \"%s\"\n", path);
+ if ( (fp = fopen (path, "r")) == NULL )
+ {
+ snprintf (dki_estr, sizeof (dki_estr),
+ "dki_read: Can\'t open file \"%s\" for reading", path);
+ return (NULL);
+ }
+
+ dbg_line ();
+ if ( (err = dki_readfile (fp, dkp)) != 0 )
+ {
+ dbg_line ();
+ snprintf (dki_estr, sizeof (dki_estr),
+ "dki_read: Can\'t read key from file %s (errno %d)", path, err);
+ fclose (fp);
+ return (NULL);
+ }
+
+ dbg_line ();
+ if ( fstat (fileno(fp), &st) )
+ {
+ snprintf (dki_estr, sizeof (dki_estr),
+ "dki_read: Can\'t stat file %s", fname);
+ return (NULL);
+ }
+ dkp->time = st.st_mtime;
+
+ dbg_line ();
+ pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_ACT_FILEEXT);
+ if ( fileexist (path) )
+ {
+ if ( dki_isrevoked (dkp) )
+ dkp->status = DKI_REV;
+ else
+ dkp->status = DKI_ACT;
+ }
+ else
+ {
+ pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_PUB_FILEEXT);
+ if ( fileexist (path) )
+ dkp->status = DKI_PUB;
+ else
+ {
+ pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_DEP_FILEEXT);
+ if ( fileexist (path) )
+ dkp->status = DKI_DEP;
+ else
+ dkp->status = DKI_SEP;
+ }
+ }
+
+ dbg_line ();
+ fclose (fp);
+
+ dbg_line ();
+ return dkp;
+}
+
+/*****************************************************************
+** dki_readdir ()
+** read key files from directory 'dir' and, if recursive is
+** true, from all directorys below that.
+*****************************************************************/
+int dki_readdir (const char *dir, dki_t **listp, int recursive)
+{
+ dki_t *dkp;
+ DIR *dirp;
+ struct dirent *dentp;
+ char path[MAX_PATHSIZE+1];
+
+ dbg_val ("directory: opendir(%s)\n", dir);
+ if ( (dirp = opendir (dir)) == NULL )
+ return 0;
+
+ while ( (dentp = readdir (dirp)) != NULL )
+ {
+ if ( is_dotfile (dentp->d_name) )
+ continue;
+
+ dbg_val ("directory: check %s\n", dentp->d_name);
+ pathname (path, sizeof (path), dir, dentp->d_name, NULL);
+ if ( is_directory (path) && recursive )
+ {
+ dbg_val ("directory: recursive %s\n", path);
+ dki_readdir (path, listp, recursive);
+ }
+ else if ( is_keyfilename (dentp->d_name) )
+ if ( (dkp = dki_read (dir, dentp->d_name)) )
+ dki_add (listp, dkp);
+ }
+ closedir (dirp);
+ return 1;
+}
+
+/*****************************************************************
+** dki_setstatus_preservetime ()
+** set status of key and change extension to
+** ".published", ".private" or ".depreciated"
+*****************************************************************/
+int dki_setstatus_preservetime (dki_t *dkp, int status)
+{
+ return dki_setstat (dkp, status, 1);
+}
+
+/*****************************************************************
+** dki_setstatus ()
+** set status of key and change extension to
+** ".published", ".private" or ".depreciated"
+*****************************************************************/
+int dki_setstatus (dki_t *dkp, int status)
+{
+ return dki_setstat (dkp, status, 0);
+}
+
+/*****************************************************************
+** dki_setstat ()
+** low level function of dki_setstatus and dki_setstatus_preservetime
+*****************************************************************/
+static int dki_setstat (dki_t *dkp, int status, int preserve_time)
+{
+ char frompath[MAX_PATHSIZE+1];
+ char topath[MAX_PATHSIZE+1];
+ time_t totime;
+ time_t currtime;
+
+ if ( dkp == NULL )
+ return 0;
+
+ currtime = time (NULL);
+ status = tolower (status);
+ switch ( dkp->status ) /* look at old status */
+ {
+ case 'r':
+ if ( status == 'r' )
+ return 1;
+ break;
+ case 'a':
+ if ( status == 'a' )
+ return 1;
+ pathname (frompath, sizeof (frompath), dkp->dname, dkp->fname, DKI_ACT_FILEEXT);
+ break;
+ case 'd':
+ if ( status == 'd' )
+ return 1;
+ pathname (frompath, sizeof (frompath), dkp->dname, dkp->fname, DKI_DEP_FILEEXT);
+ break;
+ case 'p': /* or 's' */
+ if ( status == 'p' || status == 's' )
+ return 1;
+ pathname (frompath, sizeof (frompath), dkp->dname, dkp->fname, DKI_PUB_FILEEXT);
+ break;
+ default:
+ /* TODO: set error code */
+ return 0;
+ }
+
+ dbg_val ("dki_setstat: \"%s\"\n", frompath);
+ dbg_val ("dki_setstat: to status \"%c\"\n", status);
+
+ /* a state change could result in different things: */
+ /* 1) write a new keyfile when the REVOKE bit is set or unset */
+ if ( status == 'r' || (status == 'a' && dki_isrevoked (dkp)) )
+ {
+ pathname (topath, sizeof (topath), dkp->dname, dkp->fname, DKI_KEY_FILEEXT);
+
+ if ( status == 'r' )
+ dki_setflag (dkp, DK_FLAG_REVOKE); /* set REVOKE bit */
+ else
+ dki_unsetflag (dkp, DK_FLAG_REVOKE); /* clear REVOKE bit */
+
+
+ dki_writeinfo (dkp, topath); /* ..and write it to the key file */
+
+ if ( !preserve_time )
+ touch (topath, time (NULL));
+
+ return 0;
+ }
+
+
+ /* 2) change the filename of the private key in all other cases */
+ totime = 0L;
+ if ( preserve_time )
+ totime = file_mtime (frompath); /* get original timestamp */
+ topath[0] = '\0';
+ switch ( status )
+ {
+ case 'a':
+ pathname (topath, sizeof (topath), dkp->dname, dkp->fname, DKI_ACT_FILEEXT);
+ break;
+ case 'd':
+ pathname (topath, sizeof (topath), dkp->dname, dkp->fname, DKI_DEP_FILEEXT);
+ break;
+ case 's': /* standby means a "published KSK" */
+ if ( !dki_isksk (dkp) )
+ return 2;
+ status = 'p';
+ /* fall through */
+ case 'p':
+ pathname (topath, sizeof (topath), dkp->dname, dkp->fname, DKI_PUB_FILEEXT);
+ break;
+ }
+
+ if ( topath[0] )
+ {
+ dbg_val ("dki_setstat: to \"%s\"\n", topath);
+ if ( link (frompath, topath) == 0 )
+ unlink (frompath);
+ dkp->status = status;
+ if ( !totime )
+ totime = time (NULL); /* set .key file to current time */
+ pathname (topath, sizeof (topath), dkp->dname, dkp->fname, DKI_KEY_FILEEXT);
+ touch (topath, totime); /* store/restore time of status change */
+ }
+
+ return 0;
+}
+
+/*****************************************************************
+** dki_remove ()
+** rename files associated with key, so that the keys are not
+** recognized by the zkt tools e.g.
+** Kdo.ma.in.+001+12345.key ==> kdo.ma.in.+001+12345.key
+** (second one starts with a lower case 'k')
+*****************************************************************/
+dki_t *dki_remove (dki_t *dkp)
+{
+ char path[MAX_PATHSIZE+1];
+ char newpath[MAX_PATHSIZE+1];
+ char newfile[MAX_FNAMESIZE+1];
+ dki_t *next;
+ const char **pext;
+ static const char *ext[] = {
+ DKI_KEY_FILEEXT, DKI_PUB_FILEEXT,
+ DKI_ACT_FILEEXT, DKI_DEP_FILEEXT,
+ NULL
+ };
+
+ if ( dkp == NULL )
+ return NULL;
+
+ strncpy (newfile, dkp->fname, sizeof (newfile));
+ *newfile = tolower (*newfile);
+ for ( pext = ext; *pext; pext++ )
+ {
+ pathname (path, sizeof (path), dkp->dname, dkp->fname, *pext);
+ if ( fileexist (path) )
+ {
+ pathname (newpath, sizeof (newpath), dkp->dname, newfile, *pext);
+
+ dbg_val2 ("dki_remove: %s ==> %s \n", path, newpath);
+ rename (path, newpath);
+ }
+ }
+ next = dkp->next;
+ dki_free (dkp);
+
+ return next;
+}
+
+/*****************************************************************
+** dki_destroy ()
+** delete files associated with key and free allocated memory
+*****************************************************************/
+dki_t *dki_destroy (dki_t *dkp)
+{
+ char path[MAX_PATHSIZE+1];
+ dki_t *next;
+ const char **pext;
+ static const char *ext[] = {
+ DKI_KEY_FILEEXT, DKI_PUB_FILEEXT,
+ DKI_ACT_FILEEXT, DKI_DEP_FILEEXT,
+ NULL
+ };
+
+ if ( dkp == NULL )
+ return NULL;
+
+ for ( pext = ext; *pext; pext++ )
+ {
+ pathname (path, sizeof (path), dkp->dname, dkp->fname, *pext);
+ if ( fileexist (path) )
+ {
+ dbg_val ("dki_remove: %s \n", path);
+ unlink (path);
+ }
+ }
+ next = dkp->next;
+ dki_free (dkp);
+
+ return next;
+}
+
+/*****************************************************************
+** dki_algo2str ()
+** return a string describing the key algorithm
+*****************************************************************/
+char *dki_algo2str (int algo)
+{
+ switch ( algo )
+ {
+ case DK_ALGO_RSA: return ("RSAMD5");
+ case DK_ALGO_DH: return ("DH");
+ case DK_ALGO_DSA: return ("DSA");
+ case DK_ALGO_EC: return ("EC");
+ case DK_ALGO_RSASHA1: return ("RSASHA1");
+ }
+ return ("unknown");
+}
+
+/*****************************************************************
+** dki_geterrstr ()
+** return error string
+*****************************************************************/
+const char *dki_geterrstr ()
+{
+ return dki_estr;
+}
+
+/*****************************************************************
+** dki_prt_dnskey ()
+*****************************************************************/
+int dki_prt_dnskey (const dki_t *dkp, FILE *fp)
+{
+ return dki_prt_dnskeyttl (dkp, fp, 0);
+}
+
+/*****************************************************************
+** dki_prt_dnskeyttl ()
+*****************************************************************/
+int dki_prt_dnskeyttl (const dki_t *dkp, FILE *fp, int ttl)
+{
+ char *p;
+
+ dki_estr[0] = '\0';
+ if ( dkp == NULL )
+ return 0;
+
+ fprintf (fp, "%s ", dkp->name);
+ if ( ttl > 0 )
+ fprintf (fp, "%d ", ttl);
+ fprintf (fp, "IN DNSKEY ");
+ fprintf (fp, "%d 3 %d (", dkp->flags, dkp->algo);
+ fprintf (fp, "\n\t\t\t");
+ for ( p = dkp->pubkey; *p ; p++ )
+ if ( *p == ' ' )
+ fprintf (fp, "\n\t\t\t");
+ else
+ putc (*p, fp);
+ fprintf (fp, "\n\t\t");
+ if ( dki_isrevoked (dkp) )
+ fprintf (fp, ") ; key id = %u (original key id = %u)", (dkp->tag + 128) % 65535, dkp->tag);
+ else
+ fprintf (fp, ") ; key id = %u", dkp->tag);
+ fprintf (fp, "\n");
+
+ return 1;
+}
+
+/*****************************************************************
+** dki_prt_dnskey_raw ()
+*****************************************************************/
+int dki_prt_dnskey_raw (const dki_t *dkp, FILE *fp)
+{
+ int days;
+
+ dki_estr[0] = '\0';
+ if ( dkp == NULL )
+ return 0;
+
+ if ( dkp->gentime )
+ fprintf (fp, ";%%\tgenerationtime=%s\n", time2isostr (dkp->gentime, 's'));
+ if ( (days = dki_lifetimedays (dkp)) )
+ fprintf (fp, ";%%\tlifetime=%dd\n", days);
+ if ( dkp->exptime )
+ fprintf (fp, ";%%\texpirationtime=%s\n", time2isostr (dkp->exptime, 's'));
+
+ fprintf (fp, "%s ", dkp->name);
+#if 0
+ if ( ttl > 0 )
+ fprintf (fp, "%d ", ttl);
+#endif
+ fprintf (fp, "IN DNSKEY ");
+ fprintf (fp, "%d 3 %d ", dkp->flags, dkp->algo);
+ fprintf (fp, "%s\n", dkp->pubkey);
+
+ return 1;
+}
+
+/*****************************************************************
+** dki_prt_comment ()
+*****************************************************************/
+int dki_prt_comment (const dki_t *dkp, FILE *fp)
+{
+ int len = 0;
+
+ dki_estr[0] = '\0';
+ if ( dkp == NULL )
+ return len;
+ len += fprintf (fp, "; %s ", dkp->name);
+ len += fprintf (fp, "tag=%u ", dkp->tag);
+ len += fprintf (fp, "algo=%s ", dki_algo2str(dkp->algo));
+ len += fprintf (fp, "generated %s\n", time2str (dkp->time, 's'));
+
+ return len;
+}
+
+/*****************************************************************
+** dki_prt_trustedkey ()
+*****************************************************************/
+int dki_prt_trustedkey (const dki_t *dkp, FILE *fp)
+{
+ char *p;
+ int spaces;
+ int len = 0;
+
+ dki_estr[0] = '\0';
+ if ( dkp == NULL )
+ return len;
+ len += fprintf (fp, "\"%s\" ", dkp->name);
+ spaces = 22 - (strlen (dkp->name) + 3);
+ len += fprintf (fp, "%*s", spaces > 0 ? spaces : 0 , " ");
+ len += fprintf (fp, "%d 3 %d ", dkp->flags, dkp->algo);
+ if ( spaces < 0 )
+ len += fprintf (fp, "\n\t\t\t%7s", " ");
+ len += fprintf (fp, "\"");
+ for ( p = dkp->pubkey; *p ; p++ )
+ if ( *p == ' ' )
+ len += fprintf (fp, "\n\t\t\t\t");
+ else
+ putc (*p, fp), len += 1;
+
+ if ( dki_isrevoked (dkp) )
+ len += fprintf (fp, "\" ; # key id = %u (original key id = %u)\n\n", (dkp->tag + 128) % 65535, dkp->tag);
+ else
+ len += fprintf (fp, "\" ; # key id = %u\n\n", dkp->tag);
+ return len;
+}
+
+
+/*****************************************************************
+** dki_cmp () return <0 | 0 | >0
+*****************************************************************/
+int dki_cmp (const dki_t *a, const dki_t *b)
+{
+ int res;
+
+ dki_estr[0] = '\0';
+ if ( a == NULL ) return -1;
+ if ( b == NULL ) return 1;
+
+ /* sort by domain name, */
+ if ( (res = domaincmp (a->name, b->name)) != 0 )
+ return res;
+
+ /* then by key type, */
+ if ( (res = dki_isksk (b) - dki_isksk (a)) != 0 )
+ return res;
+
+ /* and last by creation time, */
+ return (ulong)a->time - (ulong)b->time;
+}
+
+#if defined(USE_TREE) && USE_TREE
+/*****************************************************************
+** dki_allcmp () return <0 | 0 | >0
+*****************************************************************/
+int dki_allcmp (const dki_t *a, const dki_t *b)
+{
+ int res;
+
+ dki_estr[0] = '\0';
+ if ( a == NULL ) return -1;
+ if ( b == NULL ) return 1;
+
+// fprintf (stderr, "dki_allcmp %s, %s)\n", a->name, b->name);
+ /* sort by domain name, */
+ if ( (res = domaincmp (a->name, b->name)) != 0 )
+ return res;
+
+ /* then by key type, */
+ if ( (res = dki_isksk (b) - dki_isksk (a)) != 0 )
+ return res;
+
+ /* creation time, */
+ if ( (res = (ulong)a->time - (ulong)b->time) != 0 )
+ return res;
+
+ /* and last by tag */
+ return a->tag - b->tag;
+}
+
+/*****************************************************************
+** dki_namecmp () return <0 | 0 | >0
+*****************************************************************/
+int dki_namecmp (const dki_t *a, const dki_t *b)
+{
+ dki_estr[0] = '\0';
+ if ( a == NULL ) return -1;
+ if ( b == NULL ) return 1;
+
+ return domaincmp (a->name, b->name);
+}
+/*****************************************************************
+** dki_tagcmp () return <0 | 0 | >0
+*****************************************************************/
+int dki_tagcmp (const dki_t *a, const dki_t *b)
+{
+ dki_estr[0] = '\0';
+ if ( a == NULL ) return -1;
+ if ( b == NULL ) return 1;
+
+ return a->tag - b->tag;
+}
+#endif
+
+/*****************************************************************
+** dki_timecmp ()
+*****************************************************************/
+int dki_timecmp (const dki_t *a, const dki_t *b)
+{
+ dki_estr[0] = '\0';
+ if ( a == NULL ) return -1;
+ if ( b == NULL ) return 1;
+
+ return ((ulong)a->time - (ulong)b->time);
+}
+
+/*****************************************************************
+** dki_time () return the timestamp of the key
+*****************************************************************/
+time_t dki_time (const dki_t *dkp)
+{
+ dki_estr[0] = '\0';
+ assert (dkp != NULL);
+ return (dkp->time);
+}
+
+/*****************************************************************
+** dki_exptime () return the expiration timestamp of the key
+*****************************************************************/
+time_t dki_exptime (const dki_t *dkp)
+{
+ dki_estr[0] = '\0';
+ assert (dkp != NULL);
+ return (dkp->exptime);
+}
+
+/*****************************************************************
+** dki_lifetime (dkp) return the lifetime of the key in sec!
+*****************************************************************/
+time_t dki_lifetime (const dki_t *dkp)
+{
+ dki_estr[0] = '\0';
+ assert (dkp != NULL);
+ return (dkp->lifetime);
+}
+
+/*****************************************************************
+** dki_lifetimedays (dkp) return the lifetime of the key in days!
+*****************************************************************/
+ushort dki_lifetimedays (const dki_t *dkp)
+{
+ dki_estr[0] = '\0';
+ assert (dkp != NULL);
+ return (dkp->lifetime / DAYSEC);
+}
+
+/*****************************************************************
+** dki_gentime (dkp) return the generation timestamp of the key
+*****************************************************************/
+time_t dki_gentime (const dki_t *dkp)
+{
+ dki_estr[0] = '\0';
+ assert (dkp != NULL);
+ return (dkp->gentime > 0L ? dkp->gentime: dkp->time);
+}
+
+/*****************************************************************
+** dki_setlifetime (dkp, int days)
+** set the lifetime in days (and also the gentime if not set)
+** return the old lifetime of the key in days!
+*****************************************************************/
+ushort dki_setlifetime (dki_t *dkp, int days)
+{
+ ulong lifetsec;
+ char path[MAX_PATHSIZE+1];
+
+ dki_estr[0] = '\0';
+ assert (dkp != NULL);
+
+ lifetsec = dkp->lifetime; /* old lifetime */
+ dkp->lifetime = days * DAYSEC; /* set new lifetime */
+
+ dbg_val1 ("dki_setlifetime (%d)\n", days);
+ if ( lifetsec == 0 ) /* initial setup (old lifetime was zero)? */
+ dkp->gentime = dkp->time;
+
+ pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_KEY_FILEEXT);
+ dki_writeinfo (dkp, path);
+
+ return (lifetsec / DAYSEC);
+}
+
+/*****************************************************************
+** dki_setexptime (dkp, time_t sec)
+** set the expiration time of the key in seconds since the epoch
+** return the old exptime
+*****************************************************************/
+time_t dki_setexptime (dki_t *dkp, time_t sec)
+{
+ char path[MAX_PATHSIZE+1];
+ time_t oldexptime;
+
+ dki_estr[0] = '\0';
+ assert (dkp != NULL);
+
+ dbg_val1 ("dki_setexptime (%ld)\n", sec);
+ oldexptime = dkp->exptime;
+ dkp->exptime = sec;
+
+ pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_KEY_FILEEXT);
+ dki_writeinfo (dkp, path);
+
+#if 0 /* not necessary ? */
+ touch (path, time (NULL));
+#endif
+ return (oldexptime);
+}
+
+/*****************************************************************
+** dki_age () return age of key in seconds since 'curr'
+*****************************************************************/
+int dki_age (const dki_t *dkp, time_t curr)
+{
+ dki_estr[0] = '\0';
+ assert (dkp != NULL);
+ return ((ulong)curr - (ulong)dkp->time);
+}
+
+/*****************************************************************
+** dki_getflag () return the flags field of a key
+*****************************************************************/
+dk_flag_t dki_getflag (const dki_t *dkp, time_t curr)
+{
+ dki_estr[0] = '\0';
+ return dkp->flags;
+}
+
+/*****************************************************************
+** dki_setflag () set a flag of a key
+*****************************************************************/
+dk_flag_t dki_setflag (dki_t *dkp, dk_flag_t flag)
+{
+ dki_estr[0] = '\0';
+ return dkp->flags |= (ushort)flag;
+}
+
+/*****************************************************************
+** dki_unsetflag () unset a flag of a key
+*****************************************************************/
+dk_flag_t dki_unsetflag (dki_t *dkp, dk_flag_t flag)
+{
+ dki_estr[0] = '\0';
+ return dkp->flags &= ~((ushort)flag);
+}
+
+/*****************************************************************
+** dki_isksk ()
+*****************************************************************/
+int dki_isksk (const dki_t *dkp)
+{
+ dki_estr[0] = '\0';
+ assert (dkp != NULL);
+ return (dkp->flags & DK_FLAG_KSK) == DK_FLAG_KSK;
+}
+
+/*****************************************************************
+** dki_isrevoked ()
+*****************************************************************/
+int dki_isrevoked (const dki_t *dkp)
+{
+ dki_estr[0] = '\0';
+ assert (dkp != NULL);
+ return (dkp->flags & DK_FLAG_REVOKE) == DK_FLAG_REVOKE;
+}
+
+/*****************************************************************
+** dki_isdepreciated ()
+*****************************************************************/
+int dki_isdepreciated (const dki_t *dkp)
+{
+ dki_estr[0] = '\0';
+ return dki_status (dkp) == DKI_DEPRECIATED;
+}
+
+/*****************************************************************
+** dki_isactive ()
+*****************************************************************/
+int dki_isactive (const dki_t *dkp)
+{
+ dki_estr[0] = '\0';
+ return dki_status (dkp) == DKI_ACTIVE;
+}
+
+/*****************************************************************
+** dki_ispublished ()
+*****************************************************************/
+int dki_ispublished (const dki_t *dkp)
+{
+ dki_estr[0] = '\0';
+ return dki_status (dkp) == DKI_PUBLISHED;
+}
+
+
+/*****************************************************************
+** dki_status () return key status
+*****************************************************************/
+dk_status_t dki_status (const dki_t *dkp)
+{
+ dki_estr[0] = '\0';
+ assert (dkp != NULL);
+ return (dkp->status);
+}
+
+/*****************************************************************
+** dki_statusstr () return key status as string
+*****************************************************************/
+const char *dki_statusstr (const dki_t *dkp)
+{
+ dki_estr[0] = '\0';
+ assert (dkp != NULL);
+ switch ( dkp->status )
+ {
+ case DKI_ACT: return "active";
+ case DKI_PUB: if ( dki_isksk (dkp) )
+ return "standby";
+ else
+ return "published";
+ case DKI_DEP: return "depreciated";
+ case DKI_REV: return "revoked";
+ case DKI_SEP: return "sep";
+ }
+ return "unknown";
+}
+
+/*****************************************************************
+** dki_add () add a key to the given list
+*****************************************************************/
+dki_t *dki_add (dki_t **list, dki_t *new)
+{
+ dki_t *curr;
+ dki_t *last;
+
+ dki_estr[0] = '\0';
+ if ( list == NULL )
+ return NULL;
+ if ( new == NULL )
+ return *list;
+
+ last = curr = *list;
+ while ( curr && dki_cmp (curr, new) < 0 )
+ {
+ last = curr;
+ curr = curr->next;
+ }
+
+ if ( curr == *list ) /* add node at start of list */
+ *list = new;
+ else /* add node at end or between two nodes */
+ last->next = new;
+ new->next = curr;
+
+ return *list;
+}
+
+/*****************************************************************
+** dki_search () search a key with the given tag, or the first
+** occurence of a key with the given name
+*****************************************************************/
+const dki_t *dki_search (const dki_t *list, int tag, const char *name)
+{
+ const dki_t *curr;
+
+ dki_estr[0] = '\0';
+ curr = list;
+ if ( tag )
+ while ( curr && (tag != curr->tag ||
+ (name && *name && strcmp (name, curr->name) != 0)) )
+ curr = curr->next;
+ else if ( name && *name )
+ while ( curr && strcmp (name, curr->name) != 0 )
+ curr = curr->next;
+ else
+ curr = NULL;
+
+ return curr;
+}
+
+#if defined(USE_TREE) && USE_TREE
+/*****************************************************************
+** dki_tadd () add a key to the given tree
+*****************************************************************/
+dki_t *dki_tadd (dki_t **tree, dki_t *new)
+{
+ dki_t **p;
+
+ dki_estr[0] = '\0';
+ p = tsearch (new, tree, dki_namecmp);
+ if ( *p == new )
+ dbg_val ("dki_tadd: New entry %s added\n", new->name);
+ else
+ {
+ dbg_val ("dki_tadd: New key added to %s\n", new->name);
+ dki_add (p, new);
+ }
+
+ return *p;
+}
+
+/*****************************************************************
+** dki_tsearch () search a key with the given tag, or the first
+** occurence of a key with the given name
+*****************************************************************/
+const dki_t *dki_tsearch (const dki_t *tree, int tag, const char *name)
+{
+ dki_t search;
+ dki_t **p;
+
+ dki_estr[0] = '\0';
+ search.tag = tag;
+ snprintf (search.name, sizeof (search.name), "%s", name);
+ p = tfind (&search, &tree, dki_namecmp);
+ if ( p == NULL )
+ return NULL;
+
+ return dki_search (*p, tag, name);
+}
+#endif
+
+/*****************************************************************
+** dki_find () find the n'th ksk or zsk key with given status
+*****************************************************************/
+const dki_t *dki_find (const dki_t *list, int ksk, int status, int no)
+{
+ const dki_t *dkp;
+ const dki_t *last;
+
+ dki_estr[0] = '\0';
+ last = NULL;
+ for ( dkp = list; no > 0 && dkp; dkp = dkp->next )
+ if ( dki_isksk (dkp) == ksk && dki_status (dkp) == status )
+ {
+ no--;
+ last = dkp;
+ }
+
+ return last;
+}