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(). */
41 typedef 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 
48 static av_opts		*opts_av = NULL;
49 static const char	*_save_optstr = NULL;
50 static int		_save_numopts = 0;
51 int			optind_av = 1;
52 char			*optarg_av = NULL;
53 
54 void
55 free_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 
71 int
72 load_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 
141 end:
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  */
161 uint16_t
162 parseKUlist(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 
182 static void
183 addToEKUList(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 
195 int
196 parseEKUNames(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 
240 int
241 parseEKUOIDs(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 
279 int
280 get_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  */
300 char *
301 get_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  */
368 static int
369 populate_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  */
424 int
425 getopt_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 
474 void
475 print_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