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