1/***************************************************************** 2** 3** @(#) zkt-ls.c (c) Jan 2010 Holger Zuleger hznet.de 4** 5** Secure DNS zone key tool 6** A command to list dnssec keys 7** 8** Copyright (c) 2005 - 2010, Holger Zuleger HZnet. All rights reserved. 9** 10** This software is open source. 11** 12** Redistribution and use in source and binary forms, with or without 13** modification, are permitted provided that the following conditions 14** are met: 15** 16** Redistributions of source code must retain the above copyright notice, 17** this list of conditions and the following disclaimer. 18** 19** Redistributions in binary form must reproduce the above copyright notice, 20** this list of conditions and the following disclaimer in the documentation 21** and/or other materials provided with the distribution. 22** 23** Neither the name of Holger Zuleger HZnet nor the names of its contributors may 24** be used to endorse or promote products derived from this software without 25** specific prior written permission. 26** 27** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 28** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 29** TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 30** PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE 31** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 32** CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 33** SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 34** INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 35** CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 36** ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 37** POSSIBILITY OF SUCH DAMAGE. 38** 39*****************************************************************/ 40 41# include <stdio.h> 42# include <stdlib.h> /* abort(), exit(), ... */ 43# include <string.h> 44# include <dirent.h> 45# include <assert.h> 46# include <unistd.h> 47# include <ctype.h> 48 49#ifdef HAVE_CONFIG_H 50# include <config.h> 51#endif 52# include "config_zkt.h" 53#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG 54# include <getopt.h> 55#endif 56 57# include "debug.h" 58# include "misc.h" 59# include "strlist.h" 60# include "zconf.h" 61# include "dki.h" 62# include "tcap.h" 63# include "zkt.h" 64 65extern int optopt; 66extern int opterr; 67extern int optind; 68extern char *optarg; 69const char *progname; 70 71char *labellist = NULL; 72 73int headerflag = 1; 74int ageflag = 0; 75int lifetime = 0; 76int lifetimeflag = 0; 77int timeflag = 1; 78int exptimeflag = 0; 79int pathflag = 0; 80int kskflag = 1; 81int zskflag = 1; 82int ljustflag = 0; 83int subdomain_before_parent = 1; 84 85static int dirflag = 0; 86static int recflag = RECURSIVE; 87static int trustedkeyflag = 0; 88static const char *view = ""; 89static const char *term = NULL; 90 91#if defined(COLOR_MODE) && COLOR_MODE 92# define short_options ":HKTV:afC::c:O:dhkLl:prstez" 93#else 94# define short_options ":HKTV:af:c:O:dhkLl:prstez" 95#endif 96#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG 97static struct option long_options[] = { 98 {"list-dnskeys", no_argument, NULL, 'K'}, 99 {"list-trustedkeys", no_argument, NULL, 'T'}, 100 {"ksk", no_argument, NULL, 'k'}, 101 {"zsk", no_argument, NULL, 'z'}, 102 {"age", no_argument, NULL, 'a'}, 103 {"lifetime", no_argument, NULL, 'f'}, 104 {"time", no_argument, NULL, 't'}, 105 {"expire", no_argument, NULL, 'e'}, 106 {"recursive", no_argument, NULL, 'r'}, 107 {"leftjust", no_argument, NULL, 'L'}, 108 {"label-list", no_argument, NULL, 'l'}, 109 {"path", no_argument, NULL, 'p'}, 110 {"sort", no_argument, NULL, 's'}, 111 {"subdomain", no_argument, NULL, 's'}, 112 {"nohead", no_argument, NULL, 'h'}, 113 {"directory", no_argument, NULL, 'd'}, 114#if defined(COLOR_MODE) && COLOR_MODE 115 {"color", optional_argument, NULL, 'C'}, 116#endif 117 {"config", required_argument, NULL, 'c'}, 118 {"option", required_argument, NULL, 'O'}, 119 {"config-option", required_argument, NULL, 'O'}, 120 {"view", required_argument, NULL, 'V' }, 121 {"help", no_argument, NULL, 'H'}, 122 {0, 0, 0, 0} 123}; 124#endif 125 126static int parsedirectory (const char *dir, dki_t **listp, int sub_before); 127static void parsefile (const char *file, dki_t **listp, int sub_before); 128static void usage (char *mesg, zconf_t *cp); 129 130static void setglobalflags (zconf_t *config) 131{ 132 recflag = config->recursive; 133 ageflag = config->printage; 134 timeflag = config->printtime; 135 ljustflag = config->ljust; 136 term = config->colorterm; 137 if ( term && *term == '\0' ) 138 term = getenv ("TERM"); 139} 140 141int main (int argc, char *argv[]) 142{ 143 dki_t *data = NULL; 144 int c; 145 int opt_index; 146 int action; 147 const char *file; 148 const char *defconfname = NULL; 149 char *p; 150 char str[254+1]; 151 zconf_t *config; 152 153 progname = *argv; 154 if ( (p = strrchr (progname, '/')) ) 155 progname = ++p; 156 view = getnameappendix (progname, "zkt-ls"); 157 158 defconfname = getdefconfname (view); 159 config = loadconfig ("", (zconf_t *)NULL); /* load built in config */ 160 if ( fileexist (defconfname) ) /* load default config file */ 161 config = loadconfig (defconfname, config); 162 if ( config == NULL ) 163 fatal ("Out of memory\n"); 164 setglobalflags (config); 165 166 opterr = 0; 167 opt_index = 0; 168 action = 0; 169#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG 170 while ( (c = getopt_long (argc, argv, short_options, long_options, &opt_index)) != -1 ) 171#else 172 while ( (c = getopt (argc, argv, short_options)) != -1 ) 173#endif 174 { 175 switch ( c ) 176 { 177#if defined(COLOR_MODE) && COLOR_MODE 178 case 'C': /* color mode on; optional with terminal name */ 179 if ( optarg ) 180 term = optarg; 181 else 182 term = getenv ("TERM"); 183 break; 184#endif 185 case 'T': 186 trustedkeyflag = 1; 187 subdomain_before_parent = 0; 188 zskflag = pathflag = 0; 189 /* fall through */ 190 case 'H': 191 case 'K': 192 case 'Z': 193 action = c; 194 break; 195 case 'a': /* age */ 196 ageflag = !ageflag; 197 break; 198 case 'f': /* key lifetime */ 199 lifetimeflag = !lifetimeflag; 200 break; 201 case 'V': /* view name */ 202 view = optarg; 203 defconfname = getdefconfname (view); 204 if ( fileexist (defconfname) ) /* load default config file */ 205 config = loadconfig (defconfname, config); 206 if ( config == NULL ) 207 fatal ("Out of memory\n"); 208 setglobalflags (config); 209 break; 210 case 'c': 211 config = loadconfig (optarg, config); 212 setglobalflags (config); 213 checkconfig (config); 214 break; 215 case 'O': /* read option from commandline */ 216 config = loadconfig_fromstr (optarg, config); 217 setglobalflags (config); 218 checkconfig (config); 219 break; 220 case 'd': /* ignore directory arg */ 221 dirflag = 1; 222 break; 223 case 'h': /* print no headline */ 224 headerflag = 0; 225 break; 226 case 'k': /* ksk only */ 227 zskflag = 0; 228 break; 229 case 'L': /* ljust */ 230 ljustflag = !ljustflag; 231 break; 232 case 'l': /* label list */ 233 labellist = prepstrlist (optarg, LISTDELIM); 234 if ( labellist == NULL ) 235 fatal ("Out of memory\n"); 236 break; 237 case 'p': /* print path */ 238 pathflag = 1; 239 break; 240 case 'r': /* switch recursive flag */ 241 recflag = !recflag; 242 break; 243 case 's': /* switch subdomain sorting flag */ 244 subdomain_before_parent = !subdomain_before_parent; 245 break; 246 case 't': /* time */ 247 timeflag = !timeflag; 248 break; 249 case 'e': /* expire time */ 250 exptimeflag = !exptimeflag; 251 break; 252 case 'z': /* zsk only */ 253 kskflag = 0; 254 break; 255 case ':': 256 snprintf (str, sizeof(str), "option \"-%c\" requires an argument.\n", 257 optopt); 258 usage (str, config); 259 break; 260 case '?': 261 if ( isprint (optopt) ) 262 snprintf (str, sizeof(str), "Unknown option \"-%c\".\n", 263 optopt); 264 else 265 snprintf (str, sizeof (str), "Unknown option char \\x%x.\n", 266 optopt); 267 usage (str, config); 268 break; 269 default: 270 abort(); 271 } 272 } 273 274 if ( kskflag == 0 && zskflag == 0 ) 275 kskflag = zskflag = 1; 276 277 tc_init (stdout, term); 278 279 c = optind; 280 do { 281 if ( c >= argc ) /* no args left */ 282 file = config->zonedir; /* use default directory */ 283 else 284 file = argv[c++]; 285 286 if ( is_directory (file) ) 287 parsedirectory (file, &data, subdomain_before_parent); 288 else 289 parsefile (file, &data, subdomain_before_parent); 290 291 } while ( c < argc ); /* for all arguments */ 292 293 switch ( action ) 294 { 295 case 'H': 296 usage ("", config); 297 case 'K': 298 zkt_list_dnskeys (data); 299 break; 300 case 'T': 301 zkt_list_trustedkeys (data); 302 break; 303 default: 304 zkt_list_keys (data); 305 } 306 307 tc_end (stdout, term); 308 309 return 0; 310} 311 312# define sopt_usage(mesg, value) fprintf (stderr, mesg, value) 313#if defined(HAVE_GETOPT_LONG) && HAVE_GETOPT_LONG 314# define lopt_usage(mesg, value) fprintf (stderr, mesg, value) 315# define loptstr(lstr, sstr) lstr 316#else 317# define lopt_usage(mesg, value) 318# define loptstr(lstr, sstr) sstr 319#endif 320static void usage (char *mesg, zconf_t *cp) 321{ 322 fprintf (stderr, "Secure DNS Zone Key Tool %s\n", ZKT_VERSION); 323 fprintf (stderr, "\n"); 324 325 fprintf (stderr, "List keys in current or given directory (-r for recursive mode)\n"); 326 sopt_usage ("\tusage: %s [-adefhkLprtzC] [-c config] [file|dir ...]\n", progname); 327 fprintf (stderr, "\n"); 328 fprintf (stderr, "List public part of keys in DNSKEY RR format\n"); 329 sopt_usage ("\tusage: %s -K [-dhkrz] [-c config] [file|dir ...]\n", progname); 330 lopt_usage ("\tusage: %s --list-dnskeys [-dhkzr] [-c config] [file|dir ...]\n", progname); 331 fprintf (stderr, "\n"); 332 fprintf (stderr, "List keys (output is suitable for trusted-keys section)\n"); 333 sopt_usage ("\tusage: %s -T [-dhrz] [-c config] [file|dir ...]\n", progname); 334 lopt_usage ("\tusage: %s --list-trustedkeys [-dhzr] [-c config] [file|dir ...]\n", progname); 335 fprintf (stderr, "\n"); 336 337 fprintf (stderr, "General options \n"); 338 fprintf (stderr, "\t-c file%s", loptstr (", --config=file\n", "")); 339 fprintf (stderr, "\t\t read config from <file> instead of %s\n", CONFIG_FILE); 340 fprintf (stderr, "\t-O optstr%s", loptstr (", --config-option=\"optstr\"\n", "")); 341 fprintf (stderr, "\t\t read config options from commandline\n"); 342 fprintf (stderr, "\t-h%s\t no headline or trusted-key section header/trailer in -T mode\n", loptstr (", --nohead", "\t")); 343 fprintf (stderr, "\t-d%s\t skip directory arguments\n", loptstr (", --directory", "\t")); 344 fprintf (stderr, "\t-L%s\t print the domain name left justified (default: %s)\n", loptstr (", --leftjust", "\t"), ljustflag ? "on": "off"); 345 fprintf (stderr, "\t-l list%s", loptstr (", --label=\"list\"\n\t", "")); 346 fprintf (stderr, "\t\t print out only zone keys from the given domain list\n"); 347 fprintf (stderr, "\t-C[term]%s", loptstr (", --color[=\"term\"]\n\t", "")); 348 fprintf (stderr, "\t\t turn color mode on \n"); 349 fprintf (stderr, "\t-p%s\t show path of keyfile / create key in current directory\n", loptstr (", --path", "\t")); 350 fprintf (stderr, "\t-r%s\t recursive mode on/off (default: %s)\n", loptstr(", --recursive", "\t"), recflag ? "on": "off"); 351 fprintf (stderr, "\t-s%s\t change sorting of subdomains\n", loptstr(", --subdomain", "\t")); 352 fprintf (stderr, "\t-a%s\t print age of key (default: %s)\n", loptstr (", --age", "\t"), ageflag ? "on": "off"); 353 fprintf (stderr, "\t-t%s\t print key generation time (default: %s)\n", loptstr (", --time", "\t"), 354 timeflag ? "on": "off"); 355 fprintf (stderr, "\t-e%s\t print key expiration time\n", loptstr (", --expire", "\t")); 356 fprintf (stderr, "\t-f%s\t print key lifetime\n", loptstr (", --lifetime", "\t")); 357 fprintf (stderr, "\t-k%s\t key signing keys only\n", loptstr (", --ksk", "\t")); 358 fprintf (stderr, "\t-z%s\t zone signing keys only\n", loptstr (", --zsk", "\t")); 359 if ( mesg && *mesg ) 360 fprintf (stderr, "%s\n", mesg); 361 exit (1); 362} 363 364static int parsedirectory (const char *dir, dki_t **listp, int sub_before) 365{ 366 dki_t *dkp; 367 DIR *dirp; 368 struct dirent *dentp; 369 char path[MAX_PATHSIZE+1]; 370 371 if ( dirflag ) 372 return 0; 373 374 dbg_val ("directory: opendir(%s)\n", dir); 375 if ( (dirp = opendir (dir)) == NULL ) 376 return 0; 377 378 while ( (dentp = readdir (dirp)) != NULL ) 379 { 380 if ( is_dotfilename (dentp->d_name) ) 381 continue; 382 383 dbg_val ("directory: check %s\n", dentp->d_name); 384 pathname (path, sizeof (path), dir, dentp->d_name, NULL); 385 if ( is_directory (path) && recflag ) 386 { 387 dbg_val ("directory: recursive %s\n", path); 388 parsedirectory (path, listp, sub_before); 389 } 390 else if ( is_keyfilename (dentp->d_name) ) 391 if ( (dkp = dki_read (dir, dentp->d_name)) ) 392 { 393 // fprintf (stderr, "parsedir: tssearch (%d %s)\n", dkp, dkp->name); 394#if defined (USE_TREE) && USE_TREE 395 dki_tadd (listp, dkp, sub_before); 396#else 397 dki_add (listp, dkp); 398#endif 399 } 400 } 401 closedir (dirp); 402 return 1; 403} 404 405static void parsefile (const char *file, dki_t **listp, int sub_before) 406{ 407 char path[MAX_PATHSIZE+1]; 408 dki_t *dkp; 409 410 /* file arg contains path ? ... */ 411 file = splitpath (path, sizeof (path), file); /* ... then split of */ 412 413 if ( is_keyfilename (file) ) /* plain file name looks like DNS key file ? */ 414 { 415 if ( (dkp = dki_read (path, file)) ) /* read DNS key file ... */ 416#if defined (USE_TREE) && USE_TREE 417 dki_tadd (listp, dkp, sub_before); /* ... and add to tree */ 418#else 419 dki_add (listp, dkp); /* ... and add to list */ 420#endif 421 else 422 error ("error parsing %s: (%s)\n", file, dki_geterrstr()); 423 } 424} 425