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