1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
7 *
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
12 *
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
18 *
19 * CDDL HEADER END
20 *
21 * Copyright 2007 Sun Microsystems, Inc.  All rights reserved.
22 * Use is subject to license terms.
23 */
24
25#include <stdio.h>
26#include <strings.h>
27#include <ctype.h>
28#include <libgen.h>
29#include <libintl.h>
30
31#include <libxml/tree.h>
32#include <libxml/parser.h>
33
34#include <kmfapiP.h>
35#include "util.h"
36
37
38/* Supporting structures and global variables for getopt_av(). */
39typedef struct	av_opts_s {
40	int		shortnm;	/* short name character */
41	char		*longnm;	/* long name string, NOT terminated */
42	int		longnm_len;	/* length of long name string */
43	boolean_t	has_arg;	/* takes optional argument */
44} av_opts;
45
46static av_opts		*opts_av = NULL;
47static const char	*_save_optstr = NULL;
48static int		_save_numopts = 0;
49int			optind_av = 1;
50char			*optarg_av = NULL;
51
52void
53free_policy_list(POLICY_LIST *plist)
54{
55	POLICY_LIST *n = plist, *old;
56
57	if (plist == NULL)
58		return;
59
60	while (n != NULL) {
61		old = n;
62		kmf_free_policy_record(&n->plc);
63		n = n->next;
64		free(old);
65	}
66	plist = NULL;
67}
68
69int
70load_policies(char *file, POLICY_LIST **policy_list)
71{
72	int rv = KC_OK;
73	KMF_RETURN kmfrv = KMF_OK;
74	POLICY_LIST *newitem, *plist = NULL;
75	xmlParserCtxtPtr ctxt;
76	xmlDocPtr doc = NULL;
77	xmlNodePtr cur, node;
78
79	/* Create a parser context */
80	ctxt = xmlNewParserCtxt();
81	if (ctxt == NULL)
82		return (KMF_ERR_POLICY_DB_FORMAT);
83
84	/* Read the policy DB and verify it against the schema. */
85	doc = xmlCtxtReadFile(ctxt, file, NULL,
86	    XML_PARSE_DTDVALID | XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
87	if (doc == NULL || ctxt->valid == 0) {
88		kmfrv = KMF_ERR_POLICY_DB_FORMAT;
89		goto end;
90	}
91
92	cur = xmlDocGetRootElement(doc);
93	if (cur == NULL) {
94		kmfrv = KMF_ERR_POLICY_DB_FORMAT;
95		goto end;
96	}
97
98	node = cur->xmlChildrenNode;
99	while (node != NULL) {
100		char *c;
101		/*
102		 * Search for the policy that matches the given name.
103		 */
104		if (!xmlStrcmp((const xmlChar *)node->name,
105		    (const xmlChar *)KMF_POLICY_ELEMENT)) {
106			/* Check the name attribute */
107			c = (char *)xmlGetProp(node,
108			    (const xmlChar *)KMF_POLICY_NAME_ATTR);
109
110			/* If a match, parse the rest of the data */
111			if (c != NULL) {
112				xmlFree(c);
113				newitem = malloc(sizeof (POLICY_LIST));
114				if (newitem != NULL) {
115					(void) memset(newitem, 0,
116					    sizeof (POLICY_LIST));
117					kmfrv = parsePolicyElement(node,
118					    &newitem->plc);
119				} else {
120					kmfrv = KMF_ERR_MEMORY;
121					goto end;
122				}
123				/* add to linked list */
124				if (plist == NULL) {
125					plist = newitem;
126				} else {
127					POLICY_LIST *n = plist;
128					while (n->next != NULL)
129						n = n->next;
130
131					n->next = newitem;
132					newitem->next = NULL;
133				}
134			}
135		}
136		node = node->next;
137	}
138
139end:
140	if (ctxt != NULL)
141		xmlFreeParserCtxt(ctxt);
142
143	if (doc != NULL)
144		xmlFreeDoc(doc);
145
146	if (kmfrv != KMF_OK) {
147		free_policy_list(plist);
148		rv = KC_ERR_LOADDB;
149	} else {
150		*policy_list = plist;
151	}
152
153	return (rv);
154}
155
156/*
157 * Return 0 if there is any error in the input string.
158 */
159uint16_t
160parseKUlist(char *kustring)
161{
162	uint16_t cur_bit;
163	uint16_t kubits = 0;
164	char *p;
165
166	p = strtok(kustring, ",");
167	while (p != NULL) {
168		cur_bit = kmf_string_to_ku(p);
169		if (cur_bit == 0) {
170			kubits = 0;
171			break;
172		}
173		kubits |= cur_bit;
174		p = strtok(NULL, ",");
175	}
176
177	return (kubits);
178}
179
180static void
181addToEKUList(KMF_EKU_POLICY *ekus, KMF_OID *newoid)
182{
183	if (newoid != NULL && ekus != NULL) {
184		ekus->eku_count++;
185		ekus->ekulist = realloc(
186		    ekus->ekulist, ekus->eku_count * sizeof (KMF_OID));
187		if (ekus->ekulist != NULL) {
188			ekus->ekulist[ekus->eku_count-1] = *newoid;
189		}
190	}
191}
192
193int
194parseEKUNames(char *ekulist, KMF_POLICY_RECORD *plc)
195{
196	int rv = KC_OK;
197	char *p;
198	KMF_OID *newoid;
199	KMF_EKU_POLICY *ekus = &plc->eku_set;
200
201	if (ekulist == NULL || !strlen(ekulist))
202		return (0);
203
204	/*
205	 * The list should be comma separated list of EKU Names.
206	 */
207	p = strtok(ekulist, ",");
208
209	/* If no tokens found, then maybe its just a single EKU value */
210	if (p == NULL) {
211		newoid = kmf_ekuname_to_oid(ekulist);
212		if (newoid != NULL) {
213			addToEKUList(ekus, newoid);
214			free(newoid);
215		} else {
216			rv = KC_ERR_USAGE;
217		}
218	}
219
220	while (p != NULL) {
221		newoid = kmf_ekuname_to_oid(p);
222		if (newoid != NULL) {
223			addToEKUList(ekus, newoid);
224			free(newoid);
225		} else {
226			rv = KC_ERR_USAGE;
227			break;
228		}
229		p = strtok(NULL, ",");
230	}
231
232	if (rv != KC_OK)
233		kmf_free_eku_policy(ekus);
234
235	return (rv);
236}
237
238int
239parseEKUOIDs(char *ekulist, KMF_POLICY_RECORD *plc)
240{
241	int rv = KC_OK;
242	char *p;
243	KMF_OID newoid = { 0, NULL };
244	KMF_EKU_POLICY *ekus = &plc->eku_set;
245
246	if (ekulist == NULL || !strlen(ekulist))
247		return (0);
248
249	/*
250	 * The list should be comma separated list of EKU Names.
251	 */
252	p = strtok(ekulist, ",");
253	if (p == NULL) {
254		if (kmf_string_to_oid(ekulist, &newoid) == KMF_OK) {
255			addToEKUList(ekus, &newoid);
256		} else {
257			rv = KC_ERR_USAGE;
258		}
259	}
260
261	while (p != NULL && rv == 0) {
262		if (kmf_string_to_oid(p, &newoid) == KMF_OK) {
263			addToEKUList(ekus, &newoid);
264		} else {
265			rv = KC_ERR_USAGE;
266			break;
267		}
268		p = strtok(NULL, ",");
269	}
270
271	if (rv != KC_OK)
272		kmf_free_eku_policy(ekus);
273
274	return (rv);
275}
276
277int
278get_boolean(char *arg)
279{
280	if (arg == NULL)
281		return (-1);
282	if (strcasecmp(arg, "true") == 0)
283		return (1);
284	if (strcasecmp(arg, "false") == 0)
285		return (0);
286	return (-1);
287}
288
289/*
290 * This function processes the input string.  It removes the beginning
291 * and ending blank's first, makes a copy of the resulting string and
292 * return it.
293 *
294 * This function returns NULL, if there is an error in the
295 * input string or when the system is out of memory.  The output
296 * "err_flag" argument will record the error code, if it is not NULL.
297 */
298char *
299get_string(char *str, int *err_flag)
300{
301	char *p;
302	int len, i;
303	char *retstr = NULL;
304
305	if (str == NULL) {
306		if (err_flag != NULL)
307			*err_flag = KC_ERR_USAGE;
308		return (NULL);
309	}
310
311	/* Remove beginning whitespace */
312	p = str;
313	while (p != NULL && isspace(*p))
314		p++;
315
316	if (p == NULL) {
317		if (err_flag != NULL)
318			*err_flag = KC_ERR_USAGE;
319		return (NULL);
320	}
321
322	/* Remove the trailing blanks */
323	len = strlen(p);
324	while (len > 0 && isspace(p[len-1]))
325		len--;
326
327	if (len == 0) {
328		if (err_flag != NULL)
329			*err_flag = KC_ERR_USAGE;
330		return (NULL);
331	}
332
333	/* Check if there is any non-printable character */
334	i = 0;
335	while (i < len) {
336		if (isprint(p[i]))
337			i++;
338		else {
339			if (err_flag != NULL)
340				*err_flag = KC_ERR_USAGE;
341			return (NULL);
342		}
343	}
344
345	/* Make a copy of the string and return it */
346	retstr = malloc(len + 1);
347	if (retstr == NULL) {
348		if (err_flag != NULL)
349			*err_flag = KC_ERR_MEMORY;
350		return (NULL);
351	}
352
353	if (err_flag != NULL)
354		*err_flag = KC_OK;
355
356	(void) strncpy(retstr, p, len);
357	retstr[len] = '\0';
358	return (retstr);
359}
360
361/*
362 * Breaks out the getopt-style option string into a structure that can be
363 * traversed later for calls to getopt_av().  Option string is NOT altered,
364 * but the struct fields point to locations within option string.
365 */
366static int
367populate_opts(char *optstring)
368{
369	int		i;
370	av_opts		*temp;
371	char		*marker;
372
373	if (optstring == NULL || *optstring == '\0')
374		return (0);
375
376	/*
377	 * This tries to imitate getopt(3c) Each option must conform to:
378	 * <short name char> [ ':' ] [ '(' <long name string> ')' ]
379	 * If long name is missing, the short name is used for long name.
380	 */
381	for (i = 0; *optstring != '\0'; i++) {
382		if ((temp = (av_opts *)((i == 0) ? malloc(sizeof (av_opts)) :
383		    realloc(opts_av, (i+1) * sizeof (av_opts)))) == NULL) {
384			free(opts_av);
385			opts_av = NULL;
386			return (0);
387		} else
388			opts_av = (av_opts *)temp;
389
390		marker = optstring;		/* may need optstring later */
391
392		opts_av[i].shortnm = *marker++;	/* set short name */
393
394		if (*marker == ':') {		/* check for opt arg */
395			marker++;
396			opts_av[i].has_arg = B_TRUE;
397		}
398
399		if (*marker == '(') {		/* check and set long name */
400			marker++;
401			opts_av[i].longnm = marker;
402			opts_av[i].longnm_len = strcspn(marker, ")");
403			optstring = marker + opts_av[i].longnm_len + 1;
404		} else {
405			/* use short name option character */
406			opts_av[i].longnm = optstring;
407			opts_av[i].longnm_len = 1;
408			optstring = marker;
409		}
410	}
411
412	return (i);
413}
414
415/*
416 * getopt_av() is very similar to getopt(3c) in that the takes an option
417 * string, compares command line arguments for matches, and returns a single
418 * letter option when a match is found.  However, getopt_av() differs from
419 * getopt(3c) by allowing both longname options and values be found
420 * on the command line.
421 */
422int
423getopt_av(int argc, char * const *argv, const char *optstring)
424{
425	int	i;
426	int	len;
427
428	if (optind_av >= argc)
429		return (EOF);
430
431	/* First time or when optstring changes from previous one */
432	if (_save_optstr != optstring) {
433		if (opts_av != NULL)
434			free(opts_av);
435		opts_av = NULL;
436		_save_optstr = optstring;
437		_save_numopts = populate_opts((char *)optstring);
438	}
439
440	for (i = 0; i < _save_numopts; i++) {
441		if (strcmp(argv[optind_av], "--") == 0) {
442			optind_av++;
443			break;
444		}
445
446		len = strcspn(argv[optind_av], "=");
447
448		if (len == opts_av[i].longnm_len && strncmp(argv[optind_av],
449		    opts_av[i].longnm, opts_av[i].longnm_len) == 0) {
450			/* matched */
451			if (!opts_av[i].has_arg) {
452				optind_av++;
453				return (opts_av[i].shortnm);
454			}
455
456			/* needs optarg */
457			if (argv[optind_av][len] == '=') {
458				optarg_av = &(argv[optind_av][len+1]);
459				optind_av++;
460				return (opts_av[i].shortnm);
461			}
462
463			optarg_av = NULL;
464			optind_av++;
465			return ((int)'?');
466		}
467	}
468
469	return (EOF);
470}
471
472void
473print_sanity_error(KMF_RETURN ret)
474{
475	switch (ret) {
476	case KMF_ERR_POLICY_NAME:
477		(void) fprintf(stderr, gettext("Error in the policy name\n"));
478		break;
479	case KMF_ERR_TA_POLICY:
480		(void) fprintf(stderr,
481		    gettext("Error in trust anchor attributes\n"));
482		break;
483	case KMF_ERR_OCSP_POLICY:
484		(void) fprintf(stderr,
485		    gettext("Error in OCSP policy attributes\n"));
486		break;
487	default:
488		break;
489	}
490}
491
492
493conf_entry_t *
494get_keystore_entry(char *kstore_name)
495{
496	conf_entrylist_t *phead = NULL;
497	conf_entrylist_t *ptr;
498	conf_entry_t	*rtn_entry = NULL;
499
500	if (kstore_name == NULL)
501		return (NULL);
502
503	if (get_entrylist(&phead) != KMF_OK)
504		return (NULL);
505
506	ptr = phead;
507	while (ptr != NULL) {
508		if (strcmp(ptr->entry->keystore, kstore_name) == 0)
509			break;
510		ptr = ptr->next;
511	}
512
513	if (ptr != NULL) /* found the entry */
514		rtn_entry = dup_entry(ptr->entry);
515
516	free_entrylist(phead);
517	return (rtn_entry);
518}
519