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