policy.c revision 30a5e8fa1253cb33980ee4514743cf683f584b4e
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 <stdlib.h>
28#include <ctype.h>
29#include <strings.h>
30#include <unistd.h>
31#include <errno.h>
32#include <sys/param.h>
33#include <sys/stat.h>
34
35#include <kmfapiP.h>
36#include <libxml/tree.h>
37#include <libxml/parser.h>
38
39typedef struct {
40	char	*ekuname;
41	KMF_OID	*oid;
42} EKUName2OID;
43
44static EKUName2OID EKUList[] = {
45	{"serverAuth",		(KMF_OID *)&KMFOID_PKIX_KP_ServerAuth},
46	{"clientAuth",		(KMF_OID *)&KMFOID_PKIX_KP_ClientAuth},
47	{"codeSigning",		(KMF_OID *)&KMFOID_PKIX_KP_CodeSigning},
48	{"emailProtection",	(KMF_OID *)&KMFOID_PKIX_KP_EmailProtection},
49	{"ipsecEndSystem",	(KMF_OID *)&KMFOID_PKIX_KP_IPSecEndSystem},
50	{"ipsecTunnel",		(KMF_OID *)&KMFOID_PKIX_KP_IPSecTunnel},
51	{"ipsecUser",		(KMF_OID *)&KMFOID_PKIX_KP_IPSecUser},
52	{"timeStamping",	(KMF_OID *)&KMFOID_PKIX_KP_TimeStamping},
53	{"OCSPSigning", 	(KMF_OID *)&KMFOID_PKIX_KP_OCSPSigning}
54};
55
56static int num_ekus = sizeof (EKUList) / sizeof (EKUName2OID);
57
58static void
59addFormatting(xmlNodePtr parent, char *text)
60{
61	xmlNodePtr snode;
62
63	if (parent == NULL || text == NULL)
64		return;
65
66	snode = xmlNewText((const xmlChar *)text);
67	if (snode != NULL) {
68		xmlAddChild(parent, snode);
69	}
70}
71
72static void
73parseOCSPValidation(xmlNodePtr node, KMF_VALIDATION_POLICY *vinfo)
74{
75	xmlNodePtr n;
76	char *c;
77	n = node->children;
78	while (n != NULL) {
79		if (!xmlStrcmp((const xmlChar *)n->name,
80		    (const xmlChar *)KMF_OCSP_BASIC_ELEMENT)) {
81
82			vinfo->ocsp_info.basic.responderURI =
83			    (char *)xmlGetProp(n,
84			    (const xmlChar *)KMF_OCSP_RESPONDER_ATTR);
85
86			vinfo->ocsp_info.basic.proxy = (char *)xmlGetProp(n,
87			    (const xmlChar *)KMF_OCSP_PROXY_ATTR);
88
89			c = (char *)xmlGetProp(n,
90			    (const xmlChar *)KMF_OCSP_URI_ATTR);
91			if (c != NULL && !strcasecmp(c, "true")) {
92				vinfo->ocsp_info.basic.uri_from_cert = 1;
93				xmlFree(c);
94			}
95
96			vinfo->ocsp_info.basic.response_lifetime =
97			    (char *)xmlGetProp(n,
98			    (const xmlChar *)KMF_OCSP_RESPONSE_LIFETIME_ATTR);
99
100			c = (char *)xmlGetProp(n,
101			    (const xmlChar *)KMF_OCSP_IGNORE_SIGN_ATTR);
102			if (c != NULL && !strcasecmp(c, "true")) {
103				vinfo->ocsp_info.basic.ignore_response_sign = 1;
104				xmlFree(c);
105			}
106
107		} else if (!xmlStrcmp((const xmlChar *)n->name,
108		    (const xmlChar *)KMF_OCSP_RESPONDER_CERT_ELEMENT)) {
109
110			vinfo->ocsp_info.resp_cert.name =
111			    (char *)xmlGetProp(n,
112			    (const xmlChar *)KMF_CERT_NAME_ATTR);
113			vinfo->ocsp_info.resp_cert.serial =
114			    (char *)xmlGetProp(n,
115			    (const xmlChar *)KMF_CERT_SERIAL_ATTR);
116			vinfo->ocsp_info.has_resp_cert = 1;
117		}
118
119		n = n->next;
120	}
121
122}
123
124/*
125 * Parse the "validation-methods" section of the policy.
126 */
127static void
128parseValidation(xmlNodePtr node, KMF_VALIDATION_POLICY *vinfo,
129	KMF_POLICY_RECORD *policy)
130{
131	xmlNodePtr n;
132	char *c;
133	n = node->children;
134	while (n != NULL) {
135		if (!xmlStrcmp((const xmlChar *)n->name,
136		    (const xmlChar *)KMF_OCSP_ELEMENT)) {
137
138			parseOCSPValidation(n, &policy->validation_info);
139			policy->revocation |= KMF_REVOCATION_METHOD_OCSP;
140
141
142		} else if (!xmlStrcmp((const xmlChar *)n->name,
143		    (const xmlChar *)KMF_CRL_ELEMENT)) {
144
145			vinfo->crl_info.basefilename = (char *)xmlGetProp(n,
146			    (const xmlChar *)KMF_CRL_BASENAME_ATTR);
147
148			vinfo->crl_info.directory = (char *)xmlGetProp(n,
149			    (const xmlChar *)KMF_CRL_DIRECTORY_ATTR);
150
151			c = (char *)xmlGetProp(n,
152			    (const xmlChar *)KMF_CRL_GET_URI_ATTR);
153			if (c != NULL && !strcasecmp(c, "true")) {
154				vinfo->crl_info.get_crl_uri = 1;
155			} else {
156				vinfo->crl_info.get_crl_uri = 0;
157			}
158			xmlFree(c);
159
160			vinfo->crl_info.proxy = (char *)xmlGetProp(n,
161			    (const xmlChar *)KMF_CRL_PROXY_ATTR);
162
163			c = (char *)xmlGetProp(n,
164			    (const xmlChar *)KMF_CRL_IGNORE_SIGN_ATTR);
165			if (c != NULL && !strcasecmp(c, "true")) {
166				vinfo->crl_info.ignore_crl_sign = 1;
167			} else {
168				vinfo->crl_info.ignore_crl_sign = 0;
169			}
170			xmlFree(c);
171
172			c = (char *)xmlGetProp(n,
173			    (const xmlChar *)KMF_CRL_IGNORE_DATE_ATTR);
174			if (c != NULL && !strcasecmp(c, "true")) {
175				vinfo->crl_info.ignore_crl_date = 1;
176			} else {
177				vinfo->crl_info.ignore_crl_date = 0;
178			}
179			xmlFree(c);
180
181			policy->revocation |= KMF_REVOCATION_METHOD_CRL;
182		}
183
184		n = n->next;
185	}
186}
187
188char *
189kmf_ku_to_string(uint32_t bitfield)
190{
191	if (bitfield & KMF_digitalSignature)
192		return ("digitalSignature");
193
194	if (bitfield & KMF_nonRepudiation)
195		return ("nonRepudiation");
196
197	if (bitfield & KMF_keyEncipherment)
198		return ("keyEncipherment");
199
200	if (bitfield & KMF_dataEncipherment)
201		return ("dataEncipherment");
202
203	if (bitfield & KMF_keyAgreement)
204		return ("keyAgreement");
205
206	if (bitfield & KMF_keyCertSign)
207		return ("keyCertSign");
208
209	if (bitfield & KMF_cRLSign)
210		return ("cRLSign");
211
212	if (bitfield & KMF_encipherOnly)
213		return ("encipherOnly");
214
215	if (bitfield & KMF_decipherOnly)
216		return ("decipherOnly");
217
218	return (NULL);
219}
220
221uint32_t
222kmf_string_to_ku(char *kustring)
223{
224	if (kustring == NULL || !strlen(kustring))
225		return (0);
226	if (strcasecmp(kustring, "digitalSignature") == 0)
227		return (KMF_digitalSignature);
228	if (strcasecmp(kustring, "nonRepudiation") == 0)
229		return (KMF_nonRepudiation);
230	if (strcasecmp(kustring, "keyEncipherment") == 0)
231		return (KMF_keyEncipherment);
232	if (strcasecmp(kustring, "dataEncipherment") == 0)
233		return (KMF_dataEncipherment);
234	if (strcasecmp(kustring, "keyAgreement") == 0)
235		return (KMF_keyAgreement);
236	if (strcasecmp(kustring, "keyCertSign") == 0)
237		return (KMF_keyCertSign);
238	if (strcasecmp(kustring, "cRLSign") == 0)
239		return (KMF_cRLSign);
240	if (strcasecmp(kustring, "encipherOnly") == 0)
241		return (KMF_encipherOnly);
242	if (strcasecmp(kustring, "decipherOnly") == 0)
243		return (KMF_decipherOnly);
244
245	return (0);
246}
247
248static void
249parseKeyUsageSet(xmlNodePtr node, uint32_t *kubits)
250{
251	xmlNodePtr n;
252	char *c;
253
254	n = node->children;
255	while (n != NULL) {
256		if (!xmlStrcmp((const xmlChar *)n->name,
257		    (const xmlChar *)KMF_KEY_USAGE_ELEMENT)) {
258			c = (char *)xmlGetProp(n,
259			    (const xmlChar *)KMF_KEY_USAGE_USE_ATTR);
260			if (c) {
261				*kubits |= kmf_string_to_ku(c);
262				xmlFree(c);
263			}
264		}
265
266		n = n->next;
267	}
268}
269
270static KMF_OID *
271dup_oid(KMF_OID *oldoid)
272{
273	KMF_OID *oid;
274
275	oid = malloc(sizeof (KMF_OID));
276	if (oid == NULL)
277		return (NULL);
278
279	oid->Length = oldoid->Length;
280	oid->Data = malloc(oid->Length);
281	if (oid->Data == NULL) {
282		free(oid);
283		return (NULL);
284	}
285	(void) memcpy(oid->Data, oldoid->Data, oid->Length);
286
287	return (oid);
288}
289
290KMF_OID *
291kmf_ekuname_to_oid(char *ekuname)
292{
293	KMF_OID *oid;
294	int i;
295
296	if (ekuname == NULL)
297		return (NULL);
298
299	for (i = 0; i < num_ekus; i++) {
300		if (strcasecmp(EKUList[i].ekuname, ekuname) == 0) {
301			oid = dup_oid(EKUList[i].oid);
302			return (oid);
303		}
304	}
305
306	return (NULL);
307}
308
309char *
310kmf_oid_to_eku_string(KMF_OID *oid)
311{
312	int i;
313	for (i = 0; i < num_ekus; i++) {
314		if (oid->Length == EKUList[i].oid->Length &&
315		    !memcmp(oid->Data, EKUList[i].oid->Data, oid->Length)) {
316			return (EKUList[i].ekuname);
317		}
318	}
319	return (NULL);
320}
321
322static KMF_RETURN
323parseExtKeyUsage(xmlNodePtr node, KMF_EKU_POLICY *ekus)
324{
325	xmlNodePtr n;
326	char *c;
327	KMF_RETURN ret = KMF_OK;
328	boolean_t found = FALSE;
329
330	n = node->children;
331	while (n != NULL && ret == KMF_OK) {
332		KMF_OID newoid, *oidptr;
333
334		oidptr = NULL;
335		newoid.Data = NULL;
336		newoid.Length = 0;
337
338		if (!xmlStrcmp((const xmlChar *)n->name,
339		    (const xmlChar *)KMF_EKU_NAME_ELEMENT)) {
340			c = (char *)xmlGetProp(n,
341			    (const xmlChar *)KMF_EKU_NAME_ATTR);
342			if (c != NULL) {
343				oidptr = kmf_ekuname_to_oid(c);
344				xmlFree(c);
345				found = TRUE;
346				if (oidptr != NULL)
347					newoid = *oidptr;
348			}
349		} else if (!xmlStrcmp((const xmlChar *)n->name,
350		    (const xmlChar *)KMF_EKU_OID_ELEMENT)) {
351			c = (char *)xmlGetProp(n,
352			    (const xmlChar *)KMF_EKU_OID_ATTR);
353			if (c != NULL) {
354				(void) kmf_string_to_oid(c, &newoid);
355				xmlFree(c);
356				found = TRUE;
357			}
358		} else {
359			n = n->next;
360			if ((n == NULL) && (!found))
361				ret = KMF_ERR_POLICY_DB_FORMAT;
362			continue;
363		}
364
365		if (newoid.Data != NULL) {
366			ekus->eku_count++;
367			ekus->ekulist = realloc(ekus->ekulist,
368			    ekus->eku_count * sizeof (KMF_OID));
369			if (ekus->ekulist != NULL) {
370				ekus->ekulist[ekus->eku_count-1].Length =
371				    newoid.Length;
372				ekus->ekulist[ekus->eku_count-1].Data =
373				    newoid.Data;
374			} else {
375				ret = KMF_ERR_MEMORY;
376			}
377		} else {
378			ret = KMF_ERR_POLICY_DB_FORMAT;
379		}
380
381		n = n->next;
382	}
383
384	return (ret);
385}
386
387int
388parsePolicyElement(xmlNodePtr node, KMF_POLICY_RECORD *policy)
389{
390	int ret = 0;
391	xmlNodePtr n = node->xmlChildrenNode;
392	char *c;
393
394	if (node->type == XML_ELEMENT_NODE) {
395		if (node->properties != NULL) {
396			policy->name = (char *)xmlGetProp(node,
397			    (const xmlChar *)KMF_POLICY_NAME_ATTR);
398
399			c = (char *)xmlGetProp(node,
400			    (const xmlChar *)KMF_OPTIONS_IGNORE_DATE_ATTR);
401			if (c && !strcasecmp(c, "true")) {
402				policy->ignore_date = 1;
403				xmlFree((xmlChar *)c);
404			}
405
406			c = (char *)xmlGetProp(node,
407			    (const xmlChar *)KMF_OPTIONS_IGNORE_UNKNOWN_EKUS);
408			if (c && !strcasecmp(c, "true")) {
409				policy->ignore_unknown_ekus = 1;
410				xmlFree(c);
411			}
412
413			c = (char *)xmlGetProp(node,
414			    (const xmlChar *)KMF_OPTIONS_IGNORE_TRUST_ANCHOR);
415			if (c && !strcasecmp(c, "true")) {
416				policy->ignore_trust_anchor = 1;
417				xmlFree(c);
418			}
419
420			c = (char *)xmlGetProp(node,
421			    (const xmlChar *)KMF_OPTIONS_VALIDITY_ADJUSTTIME);
422			if (c) {
423				policy->validity_adjusttime = c;
424			} else {
425				policy->validity_adjusttime = NULL;
426			}
427
428			policy->ta_name = (char *)xmlGetProp(node,
429			    (const xmlChar *)KMF_POLICY_TA_NAME_ATTR);
430
431			policy->ta_serial = (char *)xmlGetProp(node,
432			    (const xmlChar *)KMF_POLICY_TA_SERIAL_ATTR);
433		}
434
435		n = node->children;
436		while (n != NULL) {
437			if (!xmlStrcmp((const xmlChar *)n->name,
438			    (const xmlChar *)KMF_VALIDATION_METHODS_ELEMENT))
439				parseValidation(n, &policy->validation_info,
440				    policy);
441			else if (!xmlStrcmp((const xmlChar *)n->name,
442			    (const xmlChar *)KMF_KEY_USAGE_SET_ELEMENT))
443				parseKeyUsageSet(n, &policy->ku_bits);
444			else if (!xmlStrcmp((const xmlChar *)n->name,
445			    (const xmlChar *)KMF_EKU_ELEMENT)) {
446				ret = parseExtKeyUsage(n, &policy->eku_set);
447				if (ret != KMF_OK)
448					return (ret);
449			}
450
451			n = n->next;
452		}
453	}
454
455	return (ret);
456}
457
458static int
459newprop(xmlNodePtr node, char *attrname, char *src)
460{
461	xmlAttrPtr newattr;
462
463	if (src != NULL && strlen(src)) {
464		newattr = xmlNewProp(node, (const xmlChar *)attrname,
465		    (xmlChar *)src);
466		if (newattr == NULL) {
467			xmlUnlinkNode(node);
468			xmlFreeNode(node);
469			return (-1);
470		}
471	}
472	return (0);
473}
474
475/*
476 * Add CRL policy information to the XML tree.
477 * Return non-zero on any failure, else 0 for success.
478 *
479 * This function is called only when the KMF_REVOCATION_METHOD_CRL flag is on.
480 */
481static int
482AddCRLNodes(xmlNodePtr node, KMF_CRL_POLICY *crlinfo)
483{
484	xmlNodePtr n;
485
486	addFormatting(node, "\t\t");
487	n = xmlNewChild(node, NULL, (const xmlChar *)"crl", NULL);
488	if (n == NULL)
489		return (-1);
490
491	if (crlinfo->basefilename &&
492	    newprop(n, KMF_CRL_BASENAME_ATTR, crlinfo->basefilename))
493		return (-1);
494
495	if (crlinfo->directory &&
496	    newprop(n, KMF_CRL_DIRECTORY_ATTR, crlinfo->directory))
497		return (-1);
498
499	if (crlinfo->get_crl_uri &&
500	    newprop(n, KMF_CRL_GET_URI_ATTR, "TRUE")) {
501		return (-1);
502	}
503
504	if (crlinfo->proxy &&
505	    newprop(n, KMF_CRL_PROXY_ATTR, crlinfo->proxy))
506		return (-1);
507
508	if (crlinfo->ignore_crl_sign &&
509	    newprop(n, KMF_CRL_IGNORE_SIGN_ATTR, "TRUE")) {
510		return (-1);
511	}
512
513	if (crlinfo->ignore_crl_date &&
514	    newprop(n, KMF_CRL_IGNORE_DATE_ATTR, "TRUE")) {
515		return (-1);
516	}
517
518	addFormatting(node, "\n");
519	return (0);
520}
521
522/*
523 * Add OCSP information to the policy tree.
524 * Return non-zero on any failure, else 0 for success.
525 *
526 * This function is called only when the KMF_REVOCATION_METHOD_OCSP flag is on.
527 */
528static int
529AddOCSPNodes(xmlNodePtr parent, KMF_OCSP_POLICY *ocsp)
530{
531	int ret = 0;
532	xmlNodePtr n_ocsp, n_basic, n_resp;
533	KMF_OCSP_BASIC_POLICY *basic;
534	KMF_RESP_CERT_POLICY *resp_cert;
535
536	basic = &(ocsp->basic);
537	resp_cert = &(ocsp->resp_cert);
538
539	if (basic->responderURI != NULL || basic->uri_from_cert == B_TRUE) {
540
541		addFormatting(parent, "\t\t");
542
543		/* basic node */
544		n_ocsp = xmlNewChild(parent, NULL,
545		    (const xmlChar *)KMF_OCSP_ELEMENT, NULL);
546		if (n_ocsp == NULL)
547			return (-1);
548		addFormatting(n_ocsp, "\n\t\t\t");
549
550		n_basic = xmlNewChild(n_ocsp, NULL,
551		    (const xmlChar *)KMF_OCSP_BASIC_ELEMENT, NULL);
552		if (n_basic == NULL)
553			return (-1);
554		if (basic->responderURI && newprop(n_basic,
555		    KMF_OCSP_RESPONDER_ATTR, basic->responderURI))
556			return (-1);
557		if (basic->proxy &&
558		    newprop(n_basic, KMF_OCSP_PROXY_ATTR, basic->proxy))
559			return (-1);
560		if (basic->uri_from_cert &&
561		    newprop(n_basic, KMF_OCSP_URI_ATTR, "TRUE"))
562			return (-1);
563		if (basic->response_lifetime &&
564		    newprop(n_basic, KMF_OCSP_RESPONSE_LIFETIME_ATTR,
565		    basic->response_lifetime))
566			return (-1);
567		if (basic->ignore_response_sign &&
568		    newprop(n_basic, KMF_OCSP_IGNORE_SIGN_ATTR, "TRUE"))
569			return (-1);
570
571		addFormatting(n_ocsp, "\n\t\t\t");
572
573		/* responder cert node */
574		if (ocsp->has_resp_cert) {
575			n_resp = xmlNewChild(n_ocsp, NULL,
576			    (const xmlChar *)KMF_OCSP_RESPONDER_CERT_ELEMENT,
577			    NULL);
578			if (n_resp == NULL)
579				return (-1);
580			if (newprop(n_resp, KMF_CERT_NAME_ATTR,
581			    resp_cert->name))
582				return (-1);
583			if (newprop(n_resp, KMF_CERT_SERIAL_ATTR,
584			    resp_cert->serial))
585				return (-1);
586		}
587		addFormatting(n_ocsp, "\n\t\t");
588	}
589
590	addFormatting(parent, "\n");
591	return (ret);
592}
593
594/*
595 * Add validation method information to the policy tree.
596 * Return non-zero on any failure, else 0 for success.
597 */
598static int
599AddValidationNodes(xmlNodePtr parent, KMF_POLICY_RECORD *policy)
600{
601	xmlNodePtr mnode;
602	int ret = 0;
603
604	addFormatting(parent, "\t");
605	mnode = xmlNewChild(parent, NULL,
606	    (const xmlChar *)KMF_VALIDATION_METHODS_ELEMENT, NULL);
607	if (mnode == NULL)
608		return (-1);
609
610	addFormatting(mnode, "\n");
611
612	if (policy->revocation & KMF_REVOCATION_METHOD_OCSP) {
613		ret = AddOCSPNodes(mnode, &(policy->validation_info.ocsp_info));
614		if (ret != KMF_OK)
615			goto end;
616	}
617
618	if (policy->revocation & KMF_REVOCATION_METHOD_CRL) {
619		ret = AddCRLNodes(mnode, &(policy->validation_info.crl_info));
620		if (ret != KMF_OK)
621			goto end;
622	}
623
624	addFormatting(mnode, "\t");
625	addFormatting(parent, "\n");
626
627end:
628	if (ret != 0) {
629		xmlUnlinkNode(mnode);
630		xmlFreeNode(mnode);
631	}
632	return (ret);
633
634}
635
636/*
637 * Add Key Usage information to the policy tree.
638 * Return non-zero on any failure, else 0 for success.
639 */
640static KMF_RETURN
641AddKeyUsageNodes(xmlNodePtr parent, uint32_t kubits)
642{
643	int ret = KMF_OK;
644	int i;
645
646	xmlNodePtr kuset, kunode;
647
648	if (kubits == 0)
649		return (0);
650
651	addFormatting(parent, "\n\t");
652	kuset = xmlNewChild(parent, NULL,
653	    (const xmlChar *)KMF_KEY_USAGE_SET_ELEMENT, NULL);
654	if (kuset == NULL)
655		return (KMF_ERR_POLICY_ENGINE);
656
657	for (i = KULOWBIT; i <= KUHIGHBIT && ret == KMF_OK; i++) {
658		char *s = kmf_ku_to_string((kubits & (1<<i)));
659		if (s != NULL) {
660			addFormatting(kuset, "\n\t\t");
661
662			kunode = xmlNewChild(kuset, NULL,
663			    (const xmlChar *)KMF_KEY_USAGE_ELEMENT, NULL);
664			if (kunode == NULL)
665				ret = KMF_ERR_POLICY_ENGINE;
666
667			else if (newprop(kunode, KMF_KEY_USAGE_USE_ATTR, s))
668				ret = KMF_ERR_POLICY_ENGINE;
669		}
670	}
671	addFormatting(kuset, "\n\t");
672	addFormatting(parent, "\n");
673
674	if (ret != KMF_OK) {
675		xmlUnlinkNode(kuset);
676		xmlFreeNode(kuset);
677	}
678
679	return (ret);
680}
681
682/*
683 * Add Extended-Key-Usage information to the policy tree.
684 * Return non-zero on any failure, else 0 for success.
685 */
686static KMF_RETURN
687AddExtKeyUsageNodes(xmlNodePtr parent, KMF_EKU_POLICY *ekus)
688{
689	KMF_RETURN ret = KMF_OK;
690	xmlNodePtr n, kunode;
691	int i;
692
693	if (ekus != NULL && ekus->eku_count > 0) {
694		addFormatting(parent, "\n\t");
695		n = xmlNewChild(parent, NULL,
696		    (const xmlChar *)KMF_EKU_ELEMENT, NULL);
697		if (n == NULL)
698			return (KMF_ERR_POLICY_ENGINE);
699
700		for (i = 0; i < ekus->eku_count; i++) {
701			char *s = kmf_oid_to_string(&ekus->ekulist[i]);
702			if (s != NULL) {
703				addFormatting(n, "\n\t\t");
704				kunode = xmlNewChild(n, NULL,
705				    (const xmlChar *)KMF_EKU_OID_ELEMENT,
706				    NULL);
707				if (kunode == NULL)
708					ret = KMF_ERR_POLICY_ENGINE;
709
710				else if (newprop(kunode, KMF_EKU_OID_ATTR, s))
711					ret = KMF_ERR_POLICY_ENGINE;
712				free(s);
713			} else {
714				ret = KMF_ERR_POLICY_ENGINE;
715			}
716		}
717		addFormatting(n, "\n\t");
718		addFormatting(parent, "\n");
719	}
720
721	if (ret != KMF_OK) {
722		xmlUnlinkNode(n);
723		xmlFreeNode(n);
724	}
725	return (ret);
726}
727
728void
729kmf_free_eku_policy(KMF_EKU_POLICY *ekus)
730{
731	if (ekus->eku_count > 0) {
732		int i;
733		for (i = 0; i < ekus->eku_count; i++) {
734			kmf_free_data(&ekus->ekulist[i]);
735		}
736		free(ekus->ekulist);
737	}
738}
739
740#define	FREE_POLICY_STR(s) if (s != NULL) free(s);
741
742void
743kmf_free_policy_record(KMF_POLICY_RECORD *policy)
744{
745	if (policy == NULL)
746		return;
747
748	FREE_POLICY_STR(policy->name)
749	FREE_POLICY_STR(policy->VAL_OCSP_BASIC.responderURI)
750	FREE_POLICY_STR(policy->VAL_OCSP_BASIC.proxy)
751	FREE_POLICY_STR(policy->VAL_OCSP_BASIC.response_lifetime)
752	FREE_POLICY_STR(policy->VAL_OCSP_RESP_CERT.name)
753	FREE_POLICY_STR(policy->VAL_OCSP_RESP_CERT.serial)
754	FREE_POLICY_STR(policy->validation_info.crl_info.basefilename)
755	FREE_POLICY_STR(policy->validation_info.crl_info.directory)
756	FREE_POLICY_STR(policy->validation_info.crl_info.proxy)
757	FREE_POLICY_STR(policy->validity_adjusttime)
758	FREE_POLICY_STR(policy->ta_name)
759	FREE_POLICY_STR(policy->ta_serial)
760
761	kmf_free_eku_policy(&policy->eku_set);
762
763	(void) memset(policy, 0, sizeof (KMF_POLICY_RECORD));
764}
765
766/*
767 * kmf_get_policy
768 *
769 * Find a policy record in the database.
770 */
771KMF_RETURN
772kmf_get_policy(char *filename, char *policy_name, KMF_POLICY_RECORD *plc)
773{
774	KMF_RETURN ret = KMF_OK;
775	xmlParserCtxtPtr ctxt;
776	xmlDocPtr doc = NULL;
777	xmlNodePtr cur, node;
778	int found = 0;
779
780	if (filename == NULL || policy_name == NULL || plc == NULL)
781		return (KMF_ERR_BAD_PARAMETER);
782
783	(void) memset(plc, 0, sizeof (KMF_POLICY_RECORD));
784
785	/* Create a parser context */
786	ctxt = xmlNewParserCtxt();
787	if (ctxt == NULL)
788		return (KMF_ERR_POLICY_DB_FORMAT);
789
790	/* Read the policy DB and verify it against the schema. */
791	doc = xmlCtxtReadFile(ctxt, filename, NULL,
792	    XML_PARSE_DTDVALID | XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
793	if (doc == NULL || ctxt->valid == 0) {
794		ret = KMF_ERR_POLICY_DB_FORMAT;
795		goto out;
796	}
797
798	cur = xmlDocGetRootElement(doc);
799	if (cur == NULL) {
800		ret = KMF_ERR_POLICY_DB_FORMAT;
801		goto out;
802	}
803
804	node = cur->xmlChildrenNode;
805	while (node != NULL && !found) {
806		char *c;
807		/*
808		 * Search for the policy that matches the given name.
809		 */
810		if (!xmlStrcmp((const xmlChar *)node->name,
811		    (const xmlChar *)KMF_POLICY_ELEMENT)) {
812			/* Check the name attribute */
813			c = (char *)xmlGetProp(node,
814			    (const xmlChar *)KMF_POLICY_NAME_ATTR);
815
816			/* If a match, parse the rest of the data */
817			if (c != NULL) {
818				if (strcmp(c, policy_name) == 0) {
819					ret = parsePolicyElement(node, plc);
820					found = (ret == KMF_OK);
821				}
822				xmlFree(c);
823			}
824		}
825		node = node->next;
826	}
827
828	if (!found) {
829		ret = KMF_ERR_POLICY_NOT_FOUND;
830		goto out;
831	}
832
833out:
834	if (ctxt != NULL)
835		xmlFreeParserCtxt(ctxt);
836
837	if (doc != NULL)
838		xmlFreeDoc(doc);
839
840	return (ret);
841}
842
843/*
844 * kmf_set_policy
845 *
846 * Set the policy record in the handle.  This searches
847 * the policy DB for the named policy.  If it is not found
848 * or an error occurred in processing, the existing policy
849 * is kept and an error code is returned.
850 */
851KMF_RETURN
852kmf_set_policy(KMF_HANDLE_T handle, char *policyfile, char *policyname)
853{
854	KMF_RETURN ret = KMF_OK;
855	KMF_POLICY_RECORD *newpolicy = NULL;
856
857	CLEAR_ERROR(handle, ret);
858	if (ret != KMF_OK)
859		return (ret);
860
861	newpolicy = malloc(sizeof (KMF_POLICY_RECORD));
862	if (newpolicy == NULL)
863		return (KMF_ERR_MEMORY);
864	(void) memset(newpolicy, 0, sizeof (KMF_POLICY_RECORD));
865
866	ret = kmf_get_policy(
867	    policyfile == NULL ? KMF_DEFAULT_POLICY_FILE : policyfile,
868	    policyname == NULL ? KMF_DEFAULT_POLICY_NAME : policyname,
869	    newpolicy);
870	if (ret != KMF_OK)
871		goto out;
872
873	ret = kmf_verify_policy(newpolicy);
874	if (ret != KMF_OK)
875		goto out;
876
877	/* release the existing policy data (if any). */
878	if (handle->policy != NULL) {
879		kmf_free_policy_record(handle->policy);
880		free(handle->policy);
881	}
882
883	handle->policy = newpolicy;
884
885out:
886	/* Cleanup any data allocated before the error occurred */
887	if (ret != KMF_OK) {
888		kmf_free_policy_record(newpolicy);
889		free(newpolicy);
890	}
891
892	return (ret);
893}
894
895
896static KMF_RETURN
897deletePolicyNode(xmlNodePtr node, char *policy_name)
898{
899	KMF_RETURN ret = KMF_OK;
900	int found = 0;
901	xmlNodePtr dnode = NULL;
902
903	while (node != NULL && !found) {
904		char *c;
905		/*
906		 * Search for the policy that matches the given name.
907		 */
908		if (!xmlStrcmp((const xmlChar *)node->name,
909		    (const xmlChar *)KMF_POLICY_ELEMENT)) {
910			/* Check the name attribute */
911			c = (char *)xmlGetProp(node,
912			    (const xmlChar *)KMF_POLICY_NAME_ATTR);
913
914			/* If a match, parse the rest of the data */
915			if (c != NULL) {
916				if (strcmp(c, policy_name) == 0) {
917					found = 1;
918					dnode = node;
919				}
920				xmlFree(c);
921			}
922		}
923		if (!found)
924			node = node->next;
925	}
926
927	if (found && dnode != NULL) {
928		/* Unlink the node */
929		xmlUnlinkNode(dnode);
930
931		/* Delete it from the document tree */
932		xmlFreeNode(dnode);
933	} else {
934		ret = KMF_ERR_POLICY_NOT_FOUND;
935	}
936
937	return (ret);
938}
939
940/*
941 * update_policyfile
942 *
943 * Attempt to do a "safe" file update as follows:
944 *  1. Lock the original file.
945 *  2. Create and write to a temporary file
946 *  3. Replace the original file with the temporary file.
947 */
948static KMF_RETURN
949update_policyfile(xmlDocPtr doc, char *filename)
950{
951	KMF_RETURN ret = KMF_OK;
952	FILE *pfile, *tmpfile;
953	char tmpfilename[MAXPATHLEN];
954	char *p;
955	int prefix_len, tmpfd;
956	mode_t old_mode;
957
958	/*
959	 * Open and lock the DB file. First try to open an existing file,
960	 * if that fails, open it as if it were new.
961	 */
962	if ((pfile = fopen(filename, "r+")) == NULL && errno == ENOENT)
963		pfile = fopen(filename, "w+");
964
965	if (pfile == NULL)
966		return (KMF_ERR_POLICY_DB_FILE);
967
968	if (lockf(fileno(pfile), F_TLOCK, 0) == -1) {
969		(void) fclose(pfile);
970		return (KMF_ERR_POLICY_DB_FILE);
971	}
972
973	/*
974	 * Create a temporary file to hold the new data.
975	 */
976	(void) memset(tmpfilename, 0, sizeof (tmpfilename));
977	p = (char *)strrchr(filename, '/');
978	if (p == NULL) {
979		/*
980		 * filename contains basename only so we
981		 * create a temp file in current directory.
982		 */
983		if (strlcpy(tmpfilename, TMPFILE_TEMPLATE,
984		    sizeof (tmpfilename)) >= sizeof (tmpfilename))
985			return (KMF_ERR_INTERNAL);
986	} else {
987		/*
988		 * create a temp file in the same directory
989		 * as the policy file.
990		 */
991		prefix_len = p - filename;
992		(void) strncpy(tmpfilename, filename, prefix_len);
993		(void) strncat(tmpfilename, "/", 1);
994		(void) strncat(tmpfilename, TMPFILE_TEMPLATE,
995		    sizeof (TMPFILE_TEMPLATE));
996	}
997
998	old_mode = umask(077);
999	tmpfd = mkstemp(tmpfilename);
1000	(void) umask(old_mode);
1001	if (tmpfd == -1) {
1002		return (KMF_ERR_POLICY_DB_FILE);
1003	}
1004
1005	if ((tmpfile = fdopen(tmpfd, "w")) == NULL) {
1006		(void) close(tmpfd);
1007		(void) unlink(tmpfilename);
1008		(void) fclose(pfile);
1009		return (KMF_ERR_POLICY_DB_FILE);
1010	}
1011
1012	/*
1013	 * Write the new info to the temporary file.
1014	 */
1015	if (xmlDocFormatDump(tmpfile, doc, 1) == -1) {
1016		(void) fclose(pfile);
1017		(void) fclose(tmpfile);
1018		(void) unlink(tmpfilename);
1019		return (KMF_ERR_POLICY_ENGINE);
1020	}
1021
1022	(void) fclose(pfile);
1023
1024	if (fchmod(tmpfd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) {
1025		(void) close(tmpfd);
1026		(void) unlink(tmpfilename);
1027		return (KMF_ERR_POLICY_DB_FILE);
1028	}
1029	if (fclose(tmpfile) != 0)
1030		return (KMF_ERR_POLICY_DB_FILE);
1031
1032	/*
1033	 * Replace the original file with the updated tempfile.
1034	 */
1035	if (rename(tmpfilename, filename) == -1) {
1036		ret = KMF_ERR_POLICY_DB_FILE;
1037	}
1038
1039	if (ret != KMF_OK) {
1040		/* try to remove the tmp file */
1041		(void) unlink(tmpfilename);
1042	}
1043
1044	return (ret);
1045}
1046
1047/*
1048 * kmf_delete_policy_from_db
1049 *
1050 * Find a policy by name and remove it from the policy DB file.
1051 * If the policy is not found, return an error.
1052 */
1053KMF_RETURN
1054kmf_delete_policy_from_db(char *policy_name, char *dbfilename)
1055{
1056	KMF_RETURN ret;
1057	xmlParserCtxtPtr ctxt = NULL;
1058	xmlDocPtr doc = NULL;
1059	xmlNodePtr cur, node;
1060
1061	if (policy_name == NULL || dbfilename == NULL)
1062		return (KMF_ERR_BAD_PARAMETER);
1063
1064	/*
1065	 * Cannot delete the default policy record from the system
1066	 * default policy database (/etc/security/kmfpolicy.xml).
1067	 */
1068	if (strcmp(dbfilename, KMF_DEFAULT_POLICY_FILE) == 0 &&
1069	    strcmp(policy_name, KMF_DEFAULT_POLICY_NAME) == 0)
1070		return (KMF_ERR_BAD_PARAMETER);
1071
1072	/* Make sure the policy file exists */
1073	if (access(dbfilename, R_OK | W_OK))
1074		return (KMF_ERR_BAD_PARAMETER);
1075
1076	/* Read the policy DB and verify it against the schema. */
1077	ctxt = xmlNewParserCtxt();
1078	if (ctxt == NULL)
1079		return (KMF_ERR_POLICY_DB_FORMAT);
1080
1081	doc = xmlCtxtReadFile(ctxt, dbfilename, NULL,
1082	    XML_PARSE_DTDVALID | XML_PARSE_NOERROR | XML_PARSE_NOWARNING);
1083	if (doc == NULL || ctxt->valid == 0) {
1084		ret = KMF_ERR_POLICY_DB_FORMAT;
1085		goto end;
1086	}
1087
1088	cur = xmlDocGetRootElement(doc);
1089	if (cur == NULL) {
1090		xmlFreeDoc(doc);
1091		return (KMF_ERR_POLICY_DB_FORMAT);
1092	}
1093	node = cur->xmlChildrenNode;
1094
1095	ret = deletePolicyNode(node, policy_name);
1096
1097	if (ret == KMF_OK)
1098		ret = update_policyfile(doc, dbfilename);
1099
1100end:
1101	if (ctxt != NULL)
1102		xmlFreeParserCtxt(ctxt);
1103
1104	if (doc != NULL)
1105		xmlFreeDoc(doc);
1106
1107	return (ret);
1108}
1109
1110/*
1111 * Add a new policy node to the Policy DB XML tree.
1112 */
1113static KMF_RETURN
1114addPolicyNode(xmlNodePtr pnode, KMF_POLICY_RECORD *policy)
1115{
1116	KMF_RETURN ret = KMF_OK;
1117
1118	if (pnode != NULL && policy != NULL) {
1119		if (newprop(pnode, KMF_POLICY_NAME_ATTR, policy->name) != 0) {
1120			ret = KMF_ERR_POLICY_ENGINE;
1121			goto out;
1122		}
1123		if (policy->ignore_date) {
1124			if (newprop(pnode, KMF_OPTIONS_IGNORE_DATE_ATTR,
1125			    "TRUE")) {
1126				ret = KMF_ERR_POLICY_ENGINE;
1127				goto out;
1128			}
1129		}
1130
1131		if (policy->ignore_unknown_ekus) {
1132			if (newprop(pnode, KMF_OPTIONS_IGNORE_UNKNOWN_EKUS,
1133			    "TRUE")) {
1134				ret = KMF_ERR_POLICY_ENGINE;
1135				goto out;
1136			}
1137		}
1138
1139		if (policy->ignore_trust_anchor) {
1140			if (newprop(pnode, KMF_OPTIONS_IGNORE_TRUST_ANCHOR,
1141			    "TRUE")) {
1142				ret = KMF_ERR_POLICY_ENGINE;
1143				goto out;
1144			}
1145		}
1146
1147		if (policy->validity_adjusttime) {
1148			if (newprop(pnode, KMF_OPTIONS_VALIDITY_ADJUSTTIME,
1149			    policy->validity_adjusttime)) {
1150				ret = KMF_ERR_POLICY_ENGINE;
1151				goto out;
1152			}
1153		}
1154
1155		if (newprop(pnode, KMF_POLICY_TA_NAME_ATTR,
1156		    policy->ta_name) != 0) {
1157			ret = KMF_ERR_POLICY_ENGINE;
1158			goto out;
1159		}
1160
1161		if (newprop(pnode, KMF_POLICY_TA_SERIAL_ATTR,
1162		    policy->ta_serial) != 0) {
1163			ret = KMF_ERR_POLICY_ENGINE;
1164			goto out;
1165		}
1166
1167		/* Add a text node for readability */
1168		addFormatting(pnode, "\n");
1169
1170		if (ret = AddValidationNodes(pnode, policy)) {
1171			goto out;
1172		}
1173
1174		if ((ret = AddKeyUsageNodes(pnode, policy->ku_bits))) {
1175			goto out;
1176		}
1177
1178		if ((ret = AddExtKeyUsageNodes(pnode, &policy->eku_set))) {
1179			goto out;
1180		}
1181	} else {
1182		ret = KMF_ERR_BAD_PARAMETER;
1183	}
1184out:
1185	if (ret != KMF_OK && pnode != NULL) {
1186		xmlUnlinkNode(pnode);
1187		xmlFreeNode(pnode);
1188	}
1189
1190	return (ret);
1191}
1192
1193
1194KMF_RETURN
1195kmf_verify_policy(KMF_POLICY_RECORD *policy)
1196{
1197	KMF_RETURN ret = KMF_OK;
1198	boolean_t has_ta;
1199
1200	if (policy->name == NULL || !strlen(policy->name))
1201		return (KMF_ERR_POLICY_NAME);
1202
1203	/* Check the TA related policy */
1204	if (policy->ta_name != NULL && policy->ta_serial != NULL) {
1205		has_ta = B_TRUE;
1206	} else if (policy->ta_name == NULL && policy->ta_serial == NULL) {
1207		has_ta = B_FALSE;
1208	} else {
1209		/*
1210		 * If the TA cert is set, then both name and serial number
1211		 * need to be specified.
1212		 */
1213		return (KMF_ERR_TA_POLICY);
1214	}
1215
1216	if (has_ta == B_FALSE && policy->ignore_trust_anchor == B_FALSE)
1217		return (KMF_ERR_TA_POLICY);
1218
1219	if (policy->revocation & KMF_REVOCATION_METHOD_OCSP) {
1220		/*
1221		 * For OCSP, either use a fixed responder or use the
1222		 * value from the cert, but not both.
1223		 */
1224		if ((policy->VAL_OCSP_BASIC.responderURI == NULL &&
1225		    policy->VAL_OCSP_BASIC.uri_from_cert == B_FALSE) ||
1226		    (policy->VAL_OCSP_BASIC.responderURI != NULL &&
1227		    policy->VAL_OCSP_BASIC.uri_from_cert == B_TRUE))
1228			return (KMF_ERR_OCSP_POLICY);
1229
1230		/*
1231		 * If the OCSP responder cert is set, then both name and serial
1232		 * number need to be specified.
1233		 */
1234		if ((policy->VAL_OCSP_RESP_CERT.name != NULL &&
1235		    policy->VAL_OCSP_RESP_CERT.serial == NULL) ||
1236		    (policy->VAL_OCSP_RESP_CERT.name == NULL &&
1237		    policy->VAL_OCSP_RESP_CERT.serial != NULL))
1238			return (KMF_ERR_OCSP_POLICY);
1239	}
1240
1241	return (ret);
1242}
1243
1244/*
1245 * Update the KMF policy file by creating a new XML Policy doc tree
1246 * from the data in the KMF_POLICY_RECORD structure. If "check_policy"
1247 * is true, then we check the policy sanity also.
1248 */
1249KMF_RETURN
1250kmf_add_policy_to_db(KMF_POLICY_RECORD *policy, char *dbfilename,
1251    boolean_t check_policy)
1252{
1253	KMF_RETURN ret = KMF_OK;
1254	xmlDocPtr doc = NULL;
1255	xmlNodePtr root, node;
1256	xmlParserCtxtPtr ctxt = NULL;
1257
1258	if (policy == NULL || dbfilename == NULL)
1259		return (KMF_ERR_BAD_PARAMETER);
1260
1261	if (check_policy == B_TRUE) {
1262		if (ret = kmf_verify_policy(policy))
1263			return (ret);
1264	}
1265
1266	/* If the policyDB exists, load it into memory */
1267	if (!access(dbfilename, R_OK)) {
1268
1269		/* Create a parser context */
1270		ctxt = xmlNewParserCtxt();
1271		if (ctxt == NULL)
1272			return (KMF_ERR_POLICY_DB_FORMAT);
1273
1274		doc = xmlCtxtReadFile(ctxt, dbfilename, NULL,
1275		    XML_PARSE_DTDVALID | XML_PARSE_NOERROR |
1276		    XML_PARSE_NOWARNING);
1277		if (doc == NULL || ctxt->valid == 0) {
1278			ret = KMF_ERR_POLICY_DB_FORMAT;
1279			goto out;
1280		}
1281
1282		root = xmlDocGetRootElement(doc);
1283		if (root == NULL) {
1284			ret = KMF_ERR_POLICY_DB_FORMAT;
1285			goto out;
1286		}
1287
1288		node = root->xmlChildrenNode;
1289		/*
1290		 * If the DB has an existing policy of the
1291		 * same name, delete it from the tree.
1292		 */
1293		ret = deletePolicyNode(node, policy->name);
1294		if (ret == KMF_ERR_POLICY_NOT_FOUND)
1295			ret = KMF_OK;
1296	} else {
1297		/* Initialize a new DB tree */
1298		doc = xmlNewDoc((const xmlChar *)"1.0");
1299		if (doc == NULL)
1300			return (KMF_ERR_POLICY_ENGINE);
1301
1302		/*
1303		 * Add the DOCTYPE header to the tree so the
1304		 * DTD link is embedded
1305		 */
1306		doc->intSubset = xmlCreateIntSubset(doc,
1307		    (const xmlChar *)KMF_POLICY_ROOT,
1308		    NULL, (const xmlChar *)KMF_POLICY_DTD);
1309
1310		root = xmlNewDocNode(doc, NULL,
1311		    (const xmlChar *)KMF_POLICY_ROOT, NULL);
1312		if (root != NULL) {
1313			xmlDocSetRootElement(doc, root);
1314		}
1315	}
1316
1317	/* Append the new policy info to the root node. */
1318	if (root != NULL) {
1319		xmlNodePtr pnode;
1320
1321		pnode = xmlNewChild(root, NULL,
1322		    (const xmlChar *)KMF_POLICY_ELEMENT, NULL);
1323
1324		ret = addPolicyNode(pnode, policy);
1325		/* If that worked, update the DB file. */
1326		if (ret == KMF_OK)
1327			ret = update_policyfile(doc, dbfilename);
1328	} else {
1329		ret = KMF_ERR_POLICY_ENGINE;
1330	}
1331
1332
1333out:
1334	if (ctxt != NULL)
1335		xmlFreeParserCtxt(ctxt);
1336
1337	if (doc != NULL)
1338		xmlFreeDoc(doc);
1339
1340	return (ret);
1341}
1342