xref: /illumos-gate/usr/src/cmd/cmd-crypto/kmfcfg/util.c (revision fc8ae2ec4282de7ec96f48e11078345f3dc0ac3d)
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(). */
39 typedef 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 
46 static av_opts		*opts_av = NULL;
47 static const char	*_save_optstr = NULL;
48 static int		_save_numopts = 0;
49 int			optind_av = 1;
50 char			*optarg_av = NULL;
51 
52 void
53 free_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 
69 int
70 load_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 
139 end:
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  */
159 uint16_t
160 parseKUlist(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 
180 static void
181 addToEKUList(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 
193 int
194 parseEKUNames(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 
238 int
239 parseEKUOIDs(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 
277 int
278 get_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  */
298 char *
299 get_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  */
366 static int
367 populate_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  */
422 int
423 getopt_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 
472 void
473 print_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 
493 conf_entry_t *
494 get_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