xref: /illumos-gate/usr/src/cmd/cmd-crypto/kmfcfg/util.c (revision 4e93fb0f6383eaac21897dcdae56b87118131e4d)
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(). */
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_FreePolicyRecord(&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_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 
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,
189 			ekus->eku_count * sizeof (KMF_OID));
190 		if (ekus->ekulist != NULL) {
191 			ekus->ekulist[ekus->eku_count-1] = *newoid;
192 		}
193 	}
194 }
195 
196 int
197 parseEKUNames(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 
241 int
242 parseEKUOIDs(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 
284 int
285 get_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  */
305 char *
306 get_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  */
373 static int
374 populate_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  */
429 int
430 getopt_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 
479 void
480 print_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