ignore some files
[tridge/bind9.git] / contrib / zkt / dki.c
1 /*****************************************************************
2 **
3 **      @(#) dki.c  (c) Jan 2005  Holger Zuleger  hznet.de
4 **
5 **      A library for managing BIND dnssec key files.
6 **
7 **      Copyright (c) Jan 2005, Holger Zuleger HZnet. All rights reserved.
8 **
9 **      This software is open source.
10 **
11 **      Redistribution and use in source and binary forms, with or without
12 **      modification, are permitted provided that the following conditions
13 **      are met:
14 **
15 **      Redistributions of source code must retain the above copyright notice,
16 **      this list of conditions and the following disclaimer.
17 **
18 **      Redistributions in binary form must reproduce the above copyright notice,
19 **      this list of conditions and the following disclaimer in the documentation
20 **      and/or other materials provided with the distribution.
21 **
22 **      Neither the name of Holger Zuleger HZnet nor the names of its contributors may
23 **      be used to endorse or promote products derived from this software without
24 **      specific prior written permission.
25 **
26 **      THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
27 **      "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28 **      TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29 **      PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE
30 **      LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31 **      CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32 **      SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33 **      INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34 **      CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35 **      ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36 **      POSSIBILITY OF SUCH DAMAGE.
37 **
38 **
39 *****************************************************************/
40
41 # include <stdio.h>
42 # include <string.h>
43 # include <ctype.h>     /* tolower(), ... */
44 # include <unistd.h>    /* link(), unlink(), ... */
45 # include <stdlib.h>
46 # include <sys/types.h>
47 # include <sys/time.h>
48 # include <sys/stat.h>
49 # include <dirent.h>
50 # include <assert.h>
51 #ifdef HAVE_CONFIG_H
52 # include <config.h>
53 #endif
54 # include "config_zkt.h"
55 # include "debug.h"
56 # include "domaincmp.h"
57 # include "misc.h"
58 # include "zconf.h"
59 #define extern
60 # include "dki.h"
61 #undef  extern
62
63 /*****************************************************************
64 **      private (static) function declaration and definition
65 *****************************************************************/
66 static  char    dki_estr[255+1];
67
68 static  dki_t   *dki_alloc ()
69 {
70         dki_estr[0] = '\0';
71         dki_t   *dkp = malloc (sizeof (dki_t));
72
73         if ( (dkp = malloc (sizeof (dki_t))) )
74         {
75                 memset (dkp, 0, sizeof (dki_t));
76                 return dkp;
77         }
78
79         snprintf (dki_estr, sizeof (dki_estr),
80                         "dki_alloc: Out of memory");
81         return NULL;
82 }
83
84 static  int     dki_readfile (FILE *fp, dki_t *dkp)
85 {
86         int     algo,   flags,  type;
87         int     c;
88         char    *p;
89         char    buf[4095+1];
90         char    tag[25+1];
91         char    val[14+1];      /* e.g. "YYYYMMDDhhmmss" | "60d" */
92
93         assert (dkp != NULL);
94         assert (fp != NULL);
95
96         while ( (c = getc (fp)) == ';' )        /* line start with comment ? */
97         {       
98                 tag[0] = val[0] = '\0';
99                 if ( (c = getc (fp)) == '%' )   /* special comment? */
100                 {
101                         while ( (c = getc (fp)) == ' ' || c == '\t' )
102                                 ;
103                         ungetc (c, fp);
104                         /* then try to read in the creation, expire and lifetime */
105                         if ( fscanf (fp, "%25[a-zA-Z]=%14s", tag, val) == 2 )
106                         {
107                                 dbg_val2 ("dki_readfile: tag=%s val=%s \n", tag, val);
108                                 switch ( tolower (tag[0]) )
109                                 {
110                                 case 'g': dkp->gentime = timestr2time (val);    break;
111                                 case 'e': dkp->exptime = timestr2time (val);    break;
112                                 case 'l': dkp->lifetime = atoi (val) * DAYSEC;  break;
113                                 }
114                         }
115                 }
116                 else
117                         ungetc (c, fp);
118                 while ( (c = getc (fp)) != EOF && c != '\n' )   /* eat up rest of the line */
119                         ;
120         }
121         ungetc (c, fp); /* push back last char */
122
123         if ( fscanf (fp, "%4095s", buf) != 1 )  /* read label */
124                 return -1;
125
126         if ( strcmp (buf, dkp->name) != 0 )
127                 return -2;
128
129 #if defined(TTL_IN_KEYFILE_ALLOWED) && TTL_IN_KEYFILE_ALLOWED
130         /* skip optional TTL value */
131         while ( (c = getc (fp)) != EOF && isspace (c) ) /* skip spaces */
132                 ;
133         if ( isdigit (c) )                              /* skip ttl */
134                 fscanf (fp, "%*d");
135         else
136                 ungetc (c, fp);                         /* oops, no ttl */
137 #endif
138
139         if ( (c = fscanf (fp, " IN DNSKEY %d %d %d", &flags, &type, &algo)) != 3 &&
140              (c = fscanf (fp, "KEY %d %d %d", &flags, &type, &algo)) != 3 )
141                 return -3;
142         if ( type != 3 || algo != dkp->algo )
143                 return -4;              /* no DNSKEY or algorithm mismatch */
144         if ( ((flags >> 8) & 0xFF) != 01 )
145                 return -5;              /* no ZONE key */
146         dkp->flags = flags;
147
148         if ( fgets (buf, sizeof buf, fp) == NULL || buf[0] == '\0' )
149                 return -6;
150         p = buf + strlen (buf);
151         *--p = '\0';            /* delete trailing \n */
152         /* delete leading ws */
153         for ( p = buf; *p  && isspace (*p); p++ )
154                 ;
155
156         dkp->pubkey = strdup (p);
157
158         return 0;
159 }
160
161 static  int     dki_writeinfo (const dki_t *dkp, const char *path)
162 {
163         FILE    *fp;
164
165         assert (dkp != NULL);
166         assert (path != NULL && path[0] != '\0');
167
168         if ( (fp = fopen (path, "w")) == NULL )
169                 return 0;
170         dbg_val1 ("dki_writeinfo %s\n", path);
171         if ( dki_prt_dnskey_raw (dkp, fp) == 0 )
172                 return 0;
173         fclose (fp);
174         touch (path, dkp->time);        /* restore time of key file */
175
176         return 1;
177 }
178
179 static  int     dki_setstat (dki_t *dkp, int status, int preserve_time);
180
181 /*****************************************************************
182 **      public function definition
183 *****************************************************************/
184
185 /*****************************************************************
186 **      dki_free ()
187 *****************************************************************/
188 void    dki_free (dki_t *dkp)
189 {
190         assert (dkp != NULL);
191
192         if ( dkp->pubkey )
193                 free (dkp->pubkey);
194         free (dkp);
195 }
196
197 /*****************************************************************
198 **      dki_freelist ()
199 *****************************************************************/
200 void    dki_freelist (dki_t **listp)
201 {
202         dki_t   *curr;
203         dki_t   *next;
204
205         assert (listp != NULL);
206
207         curr = *listp;
208         while ( curr )
209         {
210                 next = curr->next;
211                 dki_free (curr);
212                 curr = next;
213         }
214         if ( *listp )
215                 *listp = NULL;
216 }
217
218 #if defined(USE_TREE) && USE_TREE
219 /*****************************************************************
220 **      dki_tfree ()
221 *****************************************************************/
222 void    dki_tfree (dki_t **tree)
223 {
224         assert (tree != NULL);
225         // TODO: tdestroy is a GNU extension
226         // tdestroy (*tree, dki_free);
227 }
228 #endif
229
230 /*****************************************************************
231 **      dki_new ()
232 **      create new keyfile
233 **      allocate memory for new dki key and init with keyfile
234 *****************************************************************/
235 dki_t   *dki_new (const char *dir, const char *name, int ksk, int algo, int bitsize, const char *rfile, int lf_days)
236 {
237         char    cmdline[511+1];
238         char    fname[254+1];
239         char    randfile[254+1];
240         FILE    *fp;
241         int     len;
242         char    *flag = "";
243         char    *expflag = "";
244         dki_t   *new;
245
246         if ( ksk )
247                 flag = "-f KSK";
248
249         randfile[0] = '\0';
250         if ( rfile && *rfile )
251                 snprintf (randfile, sizeof (randfile), "-r %.250s ", rfile);
252                 
253         if ( algo == DK_ALGO_RSA || algo == DK_ALGO_RSASHA1 )
254                 expflag = "-e ";
255
256         if ( dir && *dir )
257                 snprintf (cmdline, sizeof (cmdline), "cd %s ; %s %s%s-n ZONE -a %s -b %d %s %s",
258                         dir, KEYGENCMD, randfile, expflag, dki_algo2str(algo), bitsize, flag, name);
259         else
260                 snprintf (cmdline, sizeof (cmdline), "%s %s%s-n ZONE -a %s -b %d %s %s",
261                         KEYGENCMD, randfile, expflag, dki_algo2str(algo), bitsize, flag, name);
262
263         dbg_msg (cmdline);
264
265         if ( (fp = popen (cmdline, "r")) == NULL || fgets (fname, sizeof fname, fp) == NULL )
266                 return NULL;
267         pclose (fp);
268
269         len = strlen (fname) - 1;
270         if ( len >= 0 && fname[len] == '\n' )
271                 fname[len] = '\0';
272
273         new = dki_read (dir, fname);
274         if ( new )
275                 dki_setlifetime (new, lf_days); /* sets gentime + proposed lifetime */
276         
277         return new;
278 }
279
280 /*****************************************************************
281 **      dki_read ()
282 **      read key from file 'filename' (independed of the extension)
283 *****************************************************************/
284 dki_t   *dki_read (const char *dirname, const char *filename)
285 {
286         dki_t   *dkp;
287         FILE    *fp;
288         struct  stat    st;
289         int     len;
290         int     err;
291         char    fname[MAX_FNAMESIZE+1];
292         char    path[MAX_PATHSIZE+1];
293
294         dki_estr[0] = '\0';
295         if ( (dkp = dki_alloc ()) == NULL )
296                 return (NULL);
297
298         len = sizeof (fname) - 1;
299         fname[len] = '\0';
300         strncpy (fname, filename, len);
301
302         len = strlen (fname);                   /* delete extension */
303         if ( len > 4 && strcmp (&fname[len - 4], DKI_KEY_FILEEXT) == 0 )
304                 fname[len - 4] = '\0';
305         else if ( len > 10 && strcmp (&fname[len - 10], DKI_PUB_FILEEXT) == 0 )
306                 fname[len - 10] = '\0';
307         else if ( len > 8 && strcmp (&fname[len - 8], DKI_ACT_FILEEXT) == 0 )
308                 fname[len - 8] = '\0';
309         else if ( len > 12 && strcmp (&fname[len - 12], DKI_DEP_FILEEXT) == 0 )
310                 fname[len - 12] = '\0';
311         dbg_line ();
312
313         assert (strlen (dirname)+1 < sizeof (dkp->dname));
314         strcpy (dkp->dname, dirname);
315
316         assert (strlen (fname)+1 < sizeof (dkp->fname));
317         strcpy (dkp->fname, fname);
318         dbg_line ();
319         if ( sscanf (fname, "K%254[^+]+%hd+%d", dkp->name, &dkp->algo, &dkp->tag) != 3 )
320         {
321                 snprintf (dki_estr, sizeof (dki_estr),
322                         "dki_read: Filename don't match expected format (%s)", fname);
323                 return (NULL);
324         }
325
326         pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_KEY_FILEEXT);
327         dbg_val ("dki_read: path \"%s\"\n", path);
328         if ( (fp = fopen (path, "r")) == NULL )
329         {
330                 snprintf (dki_estr, sizeof (dki_estr),
331                         "dki_read: Can\'t open file \"%s\" for reading", path);
332                 return (NULL);
333         }
334         
335         dbg_line ();
336         if ( (err = dki_readfile (fp, dkp)) != 0 )
337         {
338                 dbg_line ();
339                 snprintf (dki_estr, sizeof (dki_estr),
340                         "dki_read: Can\'t read key from file %s (errno %d)", path, err);
341                 fclose (fp);
342                 return (NULL);
343         }
344
345         dbg_line ();
346         if ( fstat (fileno(fp), &st) )
347         {
348                 snprintf (dki_estr, sizeof (dki_estr),
349                         "dki_read: Can\'t stat file %s", fname);
350                 return (NULL);
351         }
352         dkp->time = st.st_mtime;
353
354         dbg_line ();
355         pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_ACT_FILEEXT);
356         if ( fileexist (path) )
357         {
358                 if ( dki_isrevoked (dkp) )
359                         dkp->status = DKI_REV;
360                 else
361                         dkp->status = DKI_ACT;
362         }
363         else
364         {
365                 pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_PUB_FILEEXT);
366                 if ( fileexist (path) )
367                         dkp->status = DKI_PUB;
368                 else
369                 {
370                         pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_DEP_FILEEXT);
371                         if ( fileexist (path) )
372                                 dkp->status = DKI_DEP;
373                         else
374                                 dkp->status = DKI_SEP;
375                 }
376         }
377
378         dbg_line ();
379         fclose (fp);
380
381         dbg_line ();
382         return dkp;
383 }
384
385 /*****************************************************************
386 **      dki_readdir ()
387 **      read key files from directory 'dir' and, if recursive is
388 **      true, from all directorys below that.
389 *****************************************************************/
390 int     dki_readdir (const char *dir, dki_t **listp, int recursive)
391 {
392         dki_t   *dkp;
393         DIR     *dirp;
394         struct  dirent  *dentp;
395         char    path[MAX_PATHSIZE+1];
396
397         dbg_val ("directory: opendir(%s)\n", dir);
398         if ( (dirp = opendir (dir)) == NULL )
399                 return 0;
400
401         while ( (dentp = readdir (dirp)) != NULL )
402         {
403                 if ( is_dotfile (dentp->d_name) )
404                         continue;
405
406                 dbg_val ("directory: check %s\n", dentp->d_name);
407                 pathname (path, sizeof (path), dir, dentp->d_name, NULL);
408                 if ( is_directory (path) && recursive )
409                 {
410                         dbg_val ("directory: recursive %s\n", path);
411                         dki_readdir (path, listp, recursive);
412                 }
413                 else if ( is_keyfilename (dentp->d_name) )
414                         if ( (dkp = dki_read (dir, dentp->d_name)) )
415                                 dki_add (listp, dkp);
416         }
417         closedir (dirp);
418         return 1;
419 }
420
421 /*****************************************************************
422 **      dki_setstatus_preservetime ()
423 **      set status of key and change extension to
424 **      ".published", ".private" or ".depreciated"
425 *****************************************************************/
426 int     dki_setstatus_preservetime (dki_t *dkp, int status)
427 {
428         return dki_setstat (dkp, status, 1);
429 }
430
431 /*****************************************************************
432 **      dki_setstatus ()
433 **      set status of key and change extension to
434 **      ".published", ".private" or ".depreciated"
435 *****************************************************************/
436 int     dki_setstatus (dki_t *dkp, int status)
437 {
438         return dki_setstat (dkp, status, 0);
439 }
440
441 /*****************************************************************
442 **      dki_setstat ()
443 **      low level function of dki_setstatus and dki_setstatus_preservetime
444 *****************************************************************/
445 static  int     dki_setstat (dki_t *dkp, int status, int preserve_time)
446 {
447         char    frompath[MAX_PATHSIZE+1];
448         char    topath[MAX_PATHSIZE+1];
449         time_t  totime;
450         time_t  currtime;
451
452         if ( dkp == NULL )
453                 return 0;
454
455         currtime = time (NULL);
456         status = tolower (status);
457         switch ( dkp->status )  /* look at old status */
458         {
459         case 'r':
460                 if ( status == 'r' )
461                         return 1;
462                 break;
463         case 'a':
464                 if ( status == 'a' )
465                         return 1;
466                 pathname (frompath, sizeof (frompath), dkp->dname, dkp->fname, DKI_ACT_FILEEXT);
467                 break;
468         case 'd':
469                 if ( status == 'd' )
470                         return 1;
471                 pathname (frompath, sizeof (frompath), dkp->dname, dkp->fname, DKI_DEP_FILEEXT);
472                 break;
473         case 'p':       /* or 's' */
474                 if ( status == 'p' || status == 's' )
475                         return 1;
476                 pathname (frompath, sizeof (frompath), dkp->dname, dkp->fname, DKI_PUB_FILEEXT);
477                 break;
478         default:
479                 /* TODO: set error code */
480                 return 0;
481         }
482
483         dbg_val ("dki_setstat: \"%s\"\n", frompath);
484         dbg_val ("dki_setstat: to status \"%c\"\n", status);
485
486         /* a state change could result in different things: */
487         /* 1) write a new keyfile when the REVOKE bit is set or unset */
488         if ( status == 'r' || (status == 'a' && dki_isrevoked (dkp)) )
489         {
490                 pathname (topath, sizeof (topath), dkp->dname, dkp->fname, DKI_KEY_FILEEXT);
491
492                 if ( status == 'r' )
493                         dki_setflag (dkp, DK_FLAG_REVOKE);      /* set REVOKE bit */
494                 else
495                         dki_unsetflag (dkp, DK_FLAG_REVOKE);    /* clear REVOKE bit */
496                         
497
498                 dki_writeinfo (dkp, topath);    /* ..and write it to the key file */
499                 
500                 if ( !preserve_time )
501                         touch (topath, time (NULL));
502                         
503                 return 0;
504         }
505
506
507         /* 2) change the filename of the private key in all other cases */
508         totime = 0L;
509         if ( preserve_time )
510                 totime = file_mtime (frompath);    /* get original timestamp */
511         topath[0] = '\0';
512         switch ( status )
513         {
514         case 'a':
515                 pathname (topath, sizeof (topath), dkp->dname, dkp->fname, DKI_ACT_FILEEXT);
516                 break;
517         case 'd':
518                 pathname (topath, sizeof (topath), dkp->dname, dkp->fname, DKI_DEP_FILEEXT);
519                 break;
520         case 's':               /* standby means a "published KSK" */
521                 if ( !dki_isksk (dkp) )
522                         return 2;
523                 status = 'p';
524                 /* fall through */
525         case 'p':
526                 pathname (topath, sizeof (topath), dkp->dname, dkp->fname, DKI_PUB_FILEEXT);
527                 break;
528         }
529
530         if ( topath[0] )
531         {
532                 dbg_val ("dki_setstat: to  \"%s\"\n", topath);
533                 if ( link (frompath, topath) == 0 )
534                         unlink (frompath);
535                 dkp->status = status;
536                 if ( !totime )
537                         totime = time (NULL);   /* set .key file to current time */
538                 pathname (topath, sizeof (topath), dkp->dname, dkp->fname, DKI_KEY_FILEEXT);
539                 touch (topath, totime); /* store/restore time of status change */
540         }
541
542         return 0;
543 }
544
545 /*****************************************************************
546 **      dki_remove ()
547 **      rename files associated with key, so that the keys are not
548 **      recognized by the zkt tools e.g.
549 **      Kdo.ma.in.+001+12345.key ==> kdo.ma.in.+001+12345.key 
550 **      (second one starts with a lower case 'k')
551 *****************************************************************/
552 dki_t   *dki_remove (dki_t *dkp)
553 {
554         char    path[MAX_PATHSIZE+1];
555         char    newpath[MAX_PATHSIZE+1];
556         char    newfile[MAX_FNAMESIZE+1];
557         dki_t   *next;
558         const   char    **pext;
559         static  const   char    *ext[] = {
560                 DKI_KEY_FILEEXT, DKI_PUB_FILEEXT, 
561                 DKI_ACT_FILEEXT, DKI_DEP_FILEEXT, 
562                 NULL
563         };
564
565         if ( dkp == NULL )
566                 return NULL;
567
568         strncpy (newfile, dkp->fname, sizeof (newfile));
569         *newfile = tolower (*newfile);
570         for ( pext = ext; *pext; pext++ )
571         {
572                 pathname (path, sizeof (path), dkp->dname, dkp->fname, *pext);
573                 if ( fileexist (path) )
574                 {
575                         pathname (newpath, sizeof (newpath), dkp->dname, newfile, *pext);
576                         
577                         dbg_val2 ("dki_remove: %s ==> %s \n", path, newpath);
578                         rename (path, newpath);
579                 }
580         }
581         next = dkp->next;
582         dki_free (dkp);
583
584         return next;
585 }
586
587 /*****************************************************************
588 **      dki_destroy ()
589 **      delete files associated with key and free allocated memory
590 *****************************************************************/
591 dki_t   *dki_destroy (dki_t *dkp)
592 {
593         char    path[MAX_PATHSIZE+1];
594         dki_t   *next;
595         const   char    **pext;
596         static  const   char    *ext[] = {
597                 DKI_KEY_FILEEXT, DKI_PUB_FILEEXT, 
598                 DKI_ACT_FILEEXT, DKI_DEP_FILEEXT, 
599                 NULL
600         };
601
602         if ( dkp == NULL )
603                 return NULL;
604
605         for ( pext = ext; *pext; pext++ )
606         {
607                 pathname (path, sizeof (path), dkp->dname, dkp->fname, *pext);
608                 if ( fileexist (path) )
609                 {
610                         dbg_val ("dki_remove: %s \n", path);
611                         unlink (path);
612                 }
613         }
614         next = dkp->next;
615         dki_free (dkp);
616
617         return next;
618 }
619
620 /*****************************************************************
621 **      dki_algo2str ()
622 **      return a string describing the key algorithm
623 *****************************************************************/
624 char    *dki_algo2str (int algo)
625 {
626         switch ( algo )
627         {
628         case DK_ALGO_RSA:               return ("RSAMD5");
629         case DK_ALGO_DH:                return ("DH");
630         case DK_ALGO_DSA:               return ("DSA");
631         case DK_ALGO_EC:                return ("EC");
632         case DK_ALGO_RSASHA1:           return ("RSASHA1");
633         case DK_ALGO_NSEC3DSA:          return ("NSEC3DSA");
634         case DK_ALGO_NSEC3RSASHA1:      return ("NSEC3RSASHA1");
635         }
636         return ("unknown");
637 }
638
639 /*****************************************************************
640 **      dki_algo2sstr ()
641 **      return a short string describing the key algorithm
642 *****************************************************************/
643 char    *dki_algo2sstr (int algo)
644 {
645         switch ( algo )
646         {
647         case DK_ALGO_RSA:               return ("RSAMD5");
648         case DK_ALGO_DH:                return ("DH");
649         case DK_ALGO_DSA:               return ("DSA");
650         case DK_ALGO_EC:                return ("EC");
651         case DK_ALGO_RSASHA1:           return ("RSASHA1");
652         case DK_ALGO_NSEC3DSA:          return ("N3DSA");
653         case DK_ALGO_NSEC3RSASHA1:      return ("N3RSA1");
654         }
655         return ("unknown");
656 }
657
658 /*****************************************************************
659 **      dki_geterrstr ()
660 **      return error string 
661 *****************************************************************/
662 const   char    *dki_geterrstr ()
663 {
664         return dki_estr;
665 }
666
667 /*****************************************************************
668 **      dki_prt_dnskey ()
669 *****************************************************************/
670 int     dki_prt_dnskey (const dki_t *dkp, FILE *fp)
671 {
672         return dki_prt_dnskeyttl (dkp, fp, 0);
673 }
674
675 /*****************************************************************
676 **      dki_prt_dnskeyttl ()
677 *****************************************************************/
678 int     dki_prt_dnskeyttl (const dki_t *dkp, FILE *fp, int ttl)
679 {
680         char    *p;
681
682         dki_estr[0] = '\0';
683         if ( dkp == NULL )
684                 return 0;
685
686         fprintf (fp, "%s ", dkp->name);
687         if ( ttl > 0 )
688                 fprintf (fp, "%d ", ttl);
689         fprintf (fp, "IN DNSKEY  ");
690         fprintf (fp, "%d 3 %d (", dkp->flags, dkp->algo);
691         fprintf (fp, "\n\t\t\t"); 
692         for ( p = dkp->pubkey; *p ; p++ )
693                 if ( *p == ' ' )
694                         fprintf (fp, "\n\t\t\t"); 
695                 else
696                         putc (*p, fp);
697         fprintf (fp, "\n\t\t");
698         if ( dki_isrevoked (dkp) )
699                 fprintf (fp, ") ; key id = %u (original key id = %u)", (dkp->tag + 128) % 65535, dkp->tag); 
700         else
701                 fprintf (fp, ") ; key id = %u", dkp->tag); 
702         fprintf (fp, "\n"); 
703
704         return 1;
705 }
706
707 /*****************************************************************
708 **      dki_prt_dnskey_raw ()
709 *****************************************************************/
710 int     dki_prt_dnskey_raw (const dki_t *dkp, FILE *fp)
711 {
712         int     days;
713
714         dki_estr[0] = '\0';
715         if ( dkp == NULL )
716                 return 0;
717
718         if ( dkp->gentime  )
719                 fprintf (fp, ";%%\tgenerationtime=%s\n", time2isostr (dkp->gentime, 's'));
720         if ( (days = dki_lifetimedays (dkp)) )
721                 fprintf (fp, ";%%\tlifetime=%dd\n", days);
722         if ( dkp->exptime  )
723                 fprintf (fp, ";%%\texpirationtime=%s\n", time2isostr (dkp->exptime, 's'));
724
725         fprintf (fp, "%s ", dkp->name);
726 #if 0
727         if ( ttl > 0 )
728                 fprintf (fp, "%d ", ttl);
729 #endif
730         fprintf (fp, "IN DNSKEY  ");
731         fprintf (fp, "%d 3 %d ", dkp->flags, dkp->algo);
732         fprintf (fp, "%s\n", dkp->pubkey); 
733
734         return 1;
735 }
736
737 /*****************************************************************
738 **      dki_prt_comment ()
739 *****************************************************************/
740 int     dki_prt_comment (const dki_t *dkp, FILE *fp)
741 {
742         int     len = 0;
743
744         dki_estr[0] = '\0';
745         if ( dkp == NULL )
746                 return len;
747         len += fprintf (fp, "; %s  ", dkp->name);
748         len += fprintf (fp, "tag=%u  ", dkp->tag);
749         len += fprintf (fp, "algo=%s  ", dki_algo2str(dkp->algo));
750         len += fprintf (fp, "generated %s\n", time2str (dkp->time, 's')); 
751
752         return len;
753 }
754
755 /*****************************************************************
756 **      dki_prt_trustedkey ()
757 *****************************************************************/
758 int     dki_prt_trustedkey (const dki_t *dkp, FILE *fp)
759 {
760         char    *p;
761         int     spaces;
762         int     len = 0;
763
764         dki_estr[0] = '\0';
765         if ( dkp == NULL )
766                 return len;
767         len += fprintf (fp, "\"%s\"  ", dkp->name);
768         spaces = 22 - (strlen (dkp->name) + 3);
769         len += fprintf (fp, "%*s", spaces > 0 ? spaces : 0 , " ");
770         len += fprintf (fp, "%d 3 %d ", dkp->flags, dkp->algo);
771         if ( spaces < 0 )
772                 len += fprintf (fp, "\n\t\t\t%7s", " "); 
773         len += fprintf (fp, "\"");
774         for ( p = dkp->pubkey; *p ; p++ )
775                 if ( *p == ' ' )
776                         len += fprintf (fp, "\n\t\t\t\t"); 
777                 else
778                         putc (*p, fp), len += 1;
779
780         if ( dki_isrevoked (dkp) )
781                 len += fprintf (fp, "\" ; # key id = %u (original key id = %u)\n\n", (dkp->tag + 128) % 65535, dkp->tag); 
782         else
783                 len += fprintf (fp, "\" ; # key id = %u\n\n", dkp->tag); 
784         return len;
785 }
786
787
788 /*****************************************************************
789 **      dki_cmp ()      return <0 | 0 | >0
790 *****************************************************************/
791 int     dki_cmp (const dki_t *a, const dki_t *b)
792 {
793         int     res;
794
795         dki_estr[0] = '\0';
796         if ( a == NULL ) return -1;
797         if ( b == NULL ) return 1;
798
799         /* sort by domain name, */
800         if ( (res = domaincmp (a->name, b->name)) != 0 )
801                 return res; 
802
803         /* then by key type, */
804         if ( (res = dki_isksk (b) - dki_isksk (a)) != 0 )
805                 return res;
806
807         /* and last by creation time,  */
808         return (ulong)a->time - (ulong)b->time;
809 }
810
811 #if defined(USE_TREE) && USE_TREE
812 /*****************************************************************
813 **      dki_allcmp ()   return <0 | 0 | >0
814 *****************************************************************/
815 int     dki_allcmp (const dki_t *a, const dki_t *b)
816 {
817         int     res;
818
819         dki_estr[0] = '\0';
820         if ( a == NULL ) return -1;
821         if ( b == NULL ) return 1;
822
823 // fprintf (stderr, "dki_allcmp %s, %s)\n", a->name, b->name);
824         /* sort by domain name, */
825         if ( (res = domaincmp (a->name, b->name)) != 0 )
826                 return res; 
827
828         /* then by key type, */
829         if ( (res = dki_isksk (b) - dki_isksk (a)) != 0 )
830                 return res;
831
832         /* creation time,  */
833         if ( (res = (ulong)a->time - (ulong)b->time) != 0 )
834                 return res;
835
836         /* and last by tag */
837         return a->tag - b->tag;
838 }
839
840 /*****************************************************************
841 **      dki_namecmp ()  return <0 | 0 | >0
842 *****************************************************************/
843 int     dki_namecmp (const dki_t *a, const dki_t *b)
844 {
845         dki_estr[0] = '\0';
846         if ( a == NULL ) return -1;
847         if ( b == NULL ) return 1;
848
849         return domaincmp (a->name, b->name);
850 }
851 /*****************************************************************
852 **      dki_tagcmp ()   return <0 | 0 | >0
853 *****************************************************************/
854 int     dki_tagcmp (const dki_t *a, const dki_t *b)
855 {
856         dki_estr[0] = '\0';
857         if ( a == NULL ) return -1;
858         if ( b == NULL ) return 1;
859
860         return a->tag - b->tag;
861 }
862 #endif
863
864 /*****************************************************************
865 **      dki_timecmp ()
866 *****************************************************************/
867 int     dki_timecmp (const dki_t *a, const dki_t *b)
868 {
869         dki_estr[0] = '\0';
870         if ( a == NULL ) return -1;
871         if ( b == NULL ) return 1;
872
873         return ((ulong)a->time - (ulong)b->time);
874 }
875
876 /*****************************************************************
877 **      dki_time ()     return the timestamp of the key
878 *****************************************************************/
879 time_t  dki_time (const dki_t *dkp)
880 {
881         dki_estr[0] = '\0';
882         assert (dkp != NULL);
883         return (dkp->time);
884 }
885
886 /*****************************************************************
887 **      dki_exptime ()  return the expiration timestamp of the key
888 *****************************************************************/
889 time_t  dki_exptime (const dki_t *dkp)
890 {
891         dki_estr[0] = '\0';
892         assert (dkp != NULL);
893         return (dkp->exptime);
894 }
895
896 /*****************************************************************
897 **      dki_lifetime (dkp)      return the lifetime of the key in sec!
898 *****************************************************************/
899 time_t  dki_lifetime (const dki_t *dkp)
900 {
901         dki_estr[0] = '\0';
902         assert (dkp != NULL);
903         return (dkp->lifetime);
904 }
905
906 /*****************************************************************
907 **      dki_lifetimedays (dkp)  return the lifetime of the key in days!
908 *****************************************************************/
909 ushort  dki_lifetimedays (const dki_t *dkp)
910 {
911         dki_estr[0] = '\0';
912         assert (dkp != NULL);
913         return (dkp->lifetime / DAYSEC);
914 }
915
916 /*****************************************************************
917 **      dki_gentime (dkp)       return the generation timestamp of the key
918 *****************************************************************/
919 time_t  dki_gentime (const dki_t *dkp)
920 {
921         dki_estr[0] = '\0';
922         assert (dkp != NULL);
923         return (dkp->gentime > 0L ? dkp->gentime: dkp->time);
924 }
925
926 /*****************************************************************
927 **      dki_setlifetime (dkp, int days)
928 **      set the lifetime in days (and also the gentime if not set)
929 **      return the old lifetime of the key in days!
930 *****************************************************************/
931 ushort  dki_setlifetime (dki_t *dkp, int days)
932 {
933         ulong   lifetsec;
934         char    path[MAX_PATHSIZE+1];
935
936         dki_estr[0] = '\0';
937         assert (dkp != NULL);
938
939         lifetsec = dkp->lifetime;               /* old lifetime */
940         dkp->lifetime = days * DAYSEC;          /* set new lifetime */
941
942         dbg_val1 ("dki_setlifetime (%d)\n", days);
943         if ( lifetsec == 0 )    /* initial setup (old lifetime was zero)? */
944                 dkp->gentime = dkp->time;
945
946         pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_KEY_FILEEXT);
947         dki_writeinfo (dkp, path);
948
949         return (lifetsec / DAYSEC);
950 }
951
952 /*****************************************************************
953 **      dki_setexptime (dkp, time_t sec)
954 **      set the expiration time of the key in seconds since the epoch
955 **      return the old exptime 
956 *****************************************************************/
957 time_t  dki_setexptime (dki_t *dkp, time_t sec)
958 {
959         char    path[MAX_PATHSIZE+1];
960         time_t  oldexptime;
961
962         dki_estr[0] = '\0';
963         assert (dkp != NULL);
964
965         dbg_val1 ("dki_setexptime (%ld)\n", sec);
966         oldexptime = dkp->exptime;
967         dkp->exptime = sec;
968
969         pathname (path, sizeof (path), dkp->dname, dkp->fname, DKI_KEY_FILEEXT);
970         dki_writeinfo (dkp, path);
971
972 #if 0   /* not necessary ? */
973         touch (path, time (NULL));
974 #endif
975         return (oldexptime);
976 }
977
978 /*****************************************************************
979 **      dki_age ()      return age of key in seconds since 'curr'
980 *****************************************************************/
981 int     dki_age (const dki_t *dkp, time_t curr)
982 {
983         dki_estr[0] = '\0';
984         assert (dkp != NULL);
985         return ((ulong)curr - (ulong)dkp->time);
986 }
987
988 /*****************************************************************
989 **      dki_getflag ()  return the flags field of a key 
990 *****************************************************************/
991 dk_flag_t       dki_getflag (const dki_t *dkp, time_t curr)
992 {
993         dki_estr[0] = '\0';
994         return dkp->flags;
995 }
996
997 /*****************************************************************
998 **      dki_setflag ()  set a flag of a key 
999 *****************************************************************/
1000 dk_flag_t       dki_setflag (dki_t *dkp, dk_flag_t flag)
1001 {
1002         dki_estr[0] = '\0';
1003         return dkp->flags |= (ushort)flag;
1004 }
1005
1006 /*****************************************************************
1007 **      dki_unsetflag ()        unset a flag of a key 
1008 *****************************************************************/
1009 dk_flag_t       dki_unsetflag (dki_t *dkp, dk_flag_t flag)
1010 {
1011         dki_estr[0] = '\0';
1012         return dkp->flags &= ~((ushort)flag);
1013 }
1014
1015 /*****************************************************************
1016 **      dki_isksk ()
1017 *****************************************************************/
1018 int     dki_isksk (const dki_t *dkp)
1019 {
1020         dki_estr[0] = '\0';
1021         assert (dkp != NULL);
1022         return (dkp->flags & DK_FLAG_KSK) == DK_FLAG_KSK;
1023 }
1024
1025 /*****************************************************************
1026 **      dki_isrevoked ()
1027 *****************************************************************/
1028 int     dki_isrevoked (const dki_t *dkp)
1029 {
1030         dki_estr[0] = '\0';
1031         assert (dkp != NULL);
1032         return (dkp->flags & DK_FLAG_REVOKE) == DK_FLAG_REVOKE;
1033 }
1034
1035 /*****************************************************************
1036 **      dki_isdepreciated ()
1037 *****************************************************************/
1038 int     dki_isdepreciated (const dki_t *dkp)
1039 {
1040         dki_estr[0] = '\0';
1041         return dki_status (dkp) == DKI_DEPRECIATED;
1042 }
1043
1044 /*****************************************************************
1045 **      dki_isactive ()
1046 *****************************************************************/
1047 int     dki_isactive (const dki_t *dkp)
1048 {
1049         dki_estr[0] = '\0';
1050         return dki_status (dkp) == DKI_ACTIVE;
1051 }
1052
1053 /*****************************************************************
1054 **      dki_ispublished ()
1055 *****************************************************************/
1056 int     dki_ispublished (const dki_t *dkp)
1057 {
1058         dki_estr[0] = '\0';
1059         return dki_status (dkp) == DKI_PUBLISHED;
1060 }
1061
1062
1063 /*****************************************************************
1064 **      dki_status ()   return key status
1065 *****************************************************************/
1066 dk_status_t     dki_status (const dki_t *dkp)
1067 {
1068         dki_estr[0] = '\0';
1069         assert (dkp != NULL);
1070         return (dkp->status);
1071 }
1072
1073 /*****************************************************************
1074 **      dki_statusstr ()        return key status as string
1075 *****************************************************************/
1076 const   char    *dki_statusstr (const dki_t *dkp)
1077 {
1078         dki_estr[0] = '\0';
1079         assert (dkp != NULL);
1080         switch ( dkp->status )
1081         {
1082         case DKI_ACT:   return "active";
1083         case DKI_PUB:   if ( dki_isksk (dkp) )
1084                                 return "standby";
1085                         else
1086                                 return "published";
1087         case DKI_DEP:   return "depreciated";
1088         case DKI_REV:   return "revoked";
1089         case DKI_SEP:   return "sep";
1090         }
1091         return "unknown";
1092 }
1093
1094 /*****************************************************************
1095 **      dki_add ()      add a key to the given list
1096 *****************************************************************/
1097 dki_t   *dki_add (dki_t **list, dki_t *new)
1098 {
1099         dki_t   *curr;
1100         dki_t   *last;
1101
1102         dki_estr[0] = '\0';
1103         if ( list == NULL )
1104                 return NULL;
1105         if ( new == NULL )
1106                 return *list;
1107
1108         last = curr = *list;
1109         while ( curr && dki_cmp (curr, new) < 0 )
1110         {
1111                 last = curr;
1112                 curr = curr->next;
1113         }
1114
1115         if ( curr == *list )    /* add node at start of list */
1116                 *list = new;
1117         else                    /* add node at end or between two nodes */
1118                 last->next = new;
1119         new->next = curr;
1120         
1121         return *list;
1122 }
1123
1124 /*****************************************************************
1125 **      dki_search ()   search a key with the given tag, or the first
1126 **                      occurence of a key with the given name
1127 *****************************************************************/
1128 const dki_t     *dki_search (const dki_t *list, int tag, const char *name)
1129 {
1130         const dki_t     *curr;
1131
1132         dki_estr[0] = '\0';
1133         curr = list;
1134         if ( tag )
1135                 while ( curr && (tag != curr->tag ||
1136                                 (name && *name && strcmp (name, curr->name) != 0)) )
1137                         curr = curr->next;
1138         else if ( name && *name )
1139                 while ( curr && strcmp (name, curr->name) != 0 )
1140                         curr = curr->next;
1141         else
1142                 curr = NULL;
1143
1144         return curr;
1145 }
1146
1147 #if defined(USE_TREE) && USE_TREE
1148 /*****************************************************************
1149 **      dki_tadd ()     add a key to the given tree
1150 *****************************************************************/
1151 dki_t   *dki_tadd (dki_t **tree, dki_t *new)
1152 {
1153         dki_t   **p;
1154
1155         dki_estr[0] = '\0';
1156         p = tsearch (new, tree, dki_namecmp);
1157         if ( *p == new )
1158                 dbg_val ("dki_tadd: New entry %s added\n", new->name);
1159         else
1160         {
1161                 dbg_val ("dki_tadd: New key added to %s\n", new->name);
1162                 dki_add (p, new);
1163         }
1164
1165         return *p;
1166 }
1167
1168 /*****************************************************************
1169 **      dki_tsearch ()  search a key with the given tag, or the first
1170 **                      occurence of a key with the given name
1171 *****************************************************************/
1172 const dki_t     *dki_tsearch (const dki_t *tree, int tag, const char *name)
1173 {
1174         dki_t   search;
1175         dki_t   **p;
1176
1177         dki_estr[0] = '\0';
1178         search.tag = tag;
1179         snprintf (search.name, sizeof (search.name), "%s", name);
1180         p = tfind (&search, &tree, dki_namecmp);
1181         if ( p == NULL )
1182                 return NULL;
1183
1184         return dki_search (*p, tag, name);
1185 }
1186 #endif
1187
1188 /*****************************************************************
1189 **      dki_find ()     find the n'th ksk or zsk key with given status
1190 *****************************************************************/
1191 const dki_t     *dki_find (const dki_t *list, int ksk, int status, int no)
1192 {
1193         const   dki_t   *dkp;
1194         const   dki_t   *last;
1195
1196         dki_estr[0] = '\0';
1197         last = NULL;
1198         for ( dkp = list; no > 0 && dkp; dkp = dkp->next )
1199                 if ( dki_isksk (dkp) == ksk && dki_status (dkp) == status )
1200                 {
1201                         no--;
1202                         last = dkp;
1203                 }
1204
1205         return last;
1206 }