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