util.c revision 3089:8ddeb2ace8aa
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 2006 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_FreePolicyRecord(&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_StringToKeyUsage(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,
189			ekus->eku_count * sizeof (KMF_OID));
190		if (ekus->ekulist != NULL) {
191			ekus->ekulist[ekus->eku_count-1] = *newoid;
192		}
193	}
194}
195
196int
197parseEKUNames(char *ekulist, KMF_POLICY_RECORD *plc)
198{
199	int rv = KC_OK;
200	char *p;
201	KMF_OID *newoid;
202	KMF_EKU_POLICY *ekus = &plc->eku_set;
203
204	if (ekulist == NULL || !strlen(ekulist))
205		return (0);
206
207	/*
208	 * The list should be comma separated list of EKU Names.
209	 */
210	p = strtok(ekulist, ",");
211
212	/* If no tokens found, then maybe its just a single EKU value */
213	if (p == NULL) {
214		newoid = kmf_ekuname2oid(ekulist);
215		if (newoid != NULL) {
216			addToEKUList(ekus, newoid);
217			free(newoid);
218		} else {
219			rv = KC_ERR_USAGE;
220		}
221	}
222
223	while (p != NULL) {
224		newoid = kmf_ekuname2oid(p);
225		if (newoid != NULL) {
226			addToEKUList(ekus, newoid);
227			free(newoid);
228		} else {
229			rv = KC_ERR_USAGE;
230			break;
231		}
232		p = strtok(NULL, ",");
233	}
234
235	if (rv != KC_OK)
236		KMF_FreeEKUPolicy(ekus);
237
238	return (rv);
239}
240
241int
242parseEKUOIDs(char *ekulist, KMF_POLICY_RECORD *plc)
243{
244	int rv = KC_OK;
245	char *p;
246	KMF_OID *newoid;
247	KMF_EKU_POLICY *ekus = &plc->eku_set;
248
249	if (ekulist == NULL || !strlen(ekulist))
250		return (0);
251
252	/*
253	 * The list should be comma separated list of EKU Names.
254	 */
255	p = strtok(ekulist, ",");
256	if (p == NULL) {
257		newoid = kmf_string2oid(ekulist);
258		if (newoid != NULL) {
259			addToEKUList(ekus, newoid);
260			free(newoid);
261		} else {
262			rv = KC_ERR_USAGE;
263		}
264	}
265
266	while (p != NULL && rv == 0) {
267		newoid = kmf_string2oid(p);
268		if (newoid != NULL) {
269			addToEKUList(ekus, newoid);
270			free(newoid);
271		} else {
272			rv = KC_ERR_USAGE;
273			break;
274		}
275		p = strtok(NULL, ",");
276	}
277
278	if (rv != KC_OK)
279		KMF_FreeEKUPolicy(ekus);
280
281	return (rv);
282}
283
284int
285get_boolean(char *arg)
286{
287	if (arg == NULL)
288		return (-1);
289	if (strcasecmp(arg, "true") == 0)
290		return (1);
291	if (strcasecmp(arg, "false") == 0)
292		return (0);
293	return (-1);
294}
295
296/*
297 * This function processes the input string.  It removes the beginning
298 * and ending blank's first, makes a copy of the resulting string and
299 * return it.
300 *
301 * This function returns NULL, if there is an error in the
302 * input string or when the system is out of memory.  The output
303 * "err_flag" argument will record the error code, if it is not NULL.
304 */
305char *
306get_string(char *str, int *err_flag)
307{
308	char *p;
309	int len, i;
310	char *retstr = NULL;
311
312	if (str == NULL) {
313		if (err_flag != NULL)
314			*err_flag = KC_ERR_USAGE;
315		return (NULL);
316	}
317
318	/* Remove beginning whitespace */
319	p = str;
320	while (p != NULL && isspace(*p))
321		p++;
322
323	if (p == NULL) {
324		if (err_flag != NULL)
325			*err_flag = KC_ERR_USAGE;
326		return (NULL);
327	}
328
329	/* Remove the trailing blanks */
330	len = strlen(p);
331	while (len > 0 && isspace(p[len-1]))
332		len--;
333
334	if (len == 0) {
335		if (err_flag != NULL)
336			*err_flag = KC_ERR_USAGE;
337		return (NULL);
338	}
339
340	/* Check if there is any non-printable character */
341	i = 0;
342	while (i < len) {
343		if (isprint(p[i]))
344			i++;
345		else {
346			if (err_flag != NULL)
347				*err_flag = KC_ERR_USAGE;
348			return (NULL);
349		}
350	}
351
352	/* Make a copy of the string and return it */
353	retstr = malloc(len + 1);
354	if (retstr == NULL) {
355		if (err_flag != NULL)
356			*err_flag = KC_ERR_MEMORY;
357		return (NULL);
358	}
359
360	if (err_flag != NULL)
361		*err_flag = KC_OK;
362
363	(void) strncpy(retstr, p, len);
364	retstr[len] = '\0';
365	return (retstr);
366}
367
368/*
369 * Breaks out the getopt-style option string into a structure that can be
370 * traversed later for calls to getopt_av().  Option string is NOT altered,
371 * but the struct fields point to locations within option string.
372 */
373static int
374populate_opts(char *optstring)
375{
376	int		i;
377	av_opts		*temp;
378	char		*marker;
379
380	if (optstring == NULL || *optstring == '\0')
381		return (0);
382
383	/*
384	 * This tries to imitate getopt(3c) Each option must conform to:
385	 * <short name char> [ ':' ] [ '(' <long name string> ')' ]
386	 * If long name is missing, the short name is used for long name.
387	 */
388	for (i = 0; *optstring != '\0'; i++) {
389		if ((temp = (av_opts *)((i == 0) ? malloc(sizeof (av_opts)) :
390		    realloc(opts_av, (i+1) * sizeof (av_opts)))) == NULL) {
391			free(opts_av);
392			opts_av = NULL;
393			return (0);
394		} else
395			opts_av = (av_opts *)temp;
396
397		marker = optstring;		/* may need optstring later */
398
399		opts_av[i].shortnm = *marker++;	/* set short name */
400
401		if (*marker == ':') {		/* check for opt arg */
402			marker++;
403			opts_av[i].has_arg = B_TRUE;
404		}
405
406		if (*marker == '(') {		/* check and set long name */
407			marker++;
408			opts_av[i].longnm = marker;
409			opts_av[i].longnm_len = strcspn(marker, ")");
410			optstring = marker + opts_av[i].longnm_len + 1;
411		} else {
412			/* use short name option character */
413			opts_av[i].longnm = optstring;
414			opts_av[i].longnm_len = 1;
415			optstring = marker;
416		}
417	}
418
419	return (i);
420}
421
422/*
423 * getopt_av() is very similar to getopt(3c) in that the takes an option
424 * string, compares command line arguments for matches, and returns a single
425 * letter option when a match is found.  However, getopt_av() differs from
426 * getopt(3c) by allowing both longname options and values be found
427 * on the command line.
428 */
429int
430getopt_av(int argc, char * const *argv, const char *optstring)
431{
432	int	i;
433	int	len;
434
435	if (optind_av >= argc)
436		return (EOF);
437
438	/* First time or when optstring changes from previous one */
439	if (_save_optstr != optstring) {
440		if (opts_av != NULL)
441		    free(opts_av);
442		opts_av = NULL;
443		_save_optstr = optstring;
444		_save_numopts = populate_opts((char *)optstring);
445	}
446
447	for (i = 0; i < _save_numopts; i++) {
448		if (strcmp(argv[optind_av], "--") == 0) {
449			optind_av++;
450			break;
451		}
452
453		len = strcspn(argv[optind_av], "=");
454
455		if (len == opts_av[i].longnm_len && strncmp(argv[optind_av],
456		    opts_av[i].longnm, opts_av[i].longnm_len) == 0) {
457			/* matched */
458			if (!opts_av[i].has_arg) {
459				optind_av++;
460				return (opts_av[i].shortnm);
461			}
462
463			/* needs optarg */
464			if (argv[optind_av][len] == '=') {
465				optarg_av = &(argv[optind_av][len+1]);
466				optind_av++;
467				return (opts_av[i].shortnm);
468			}
469
470			optarg_av = NULL;
471			optind_av++;
472			return ((int)'?');
473		}
474	}
475
476	return (EOF);
477}
478
479void
480print_sanity_error(KMF_RETURN ret)
481{
482	switch (ret) {
483	case KMF_ERR_POLICY_NAME:
484		(void) fprintf(stderr, gettext("Error in the policy name\n"));
485		break;
486	case KMF_ERR_TA_POLICY:
487		(void) fprintf(stderr,
488		    gettext("Error in trust anchor attributes\n"));
489		break;
490	case KMF_ERR_OCSP_POLICY:
491		(void) fprintf(stderr,
492		    gettext("Error in OCSP policy attributes\n"));
493		break;
494	default:
495		break;
496	}
497}
498