/* * CDDL HEADER START * * The contents of this file are subject to the terms of the * Common Development and Distribution License (the "License"). * You may not use this file except in compliance with the License. * * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE * or http://www.opensolaris.org/os/licensing. * See the License for the specific language governing permissions * and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each * file and include the License file at usr/src/OPENSOLARIS.LICENSE. * If applicable, add the following below this CDDL HEADER, with the * fields enclosed by brackets "[]" replaced with your own identifying * information: Portions Copyright [yyyy] [name of copyright owner] * * CDDL HEADER END * * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include typedef struct { char *ekuname; KMF_OID *oid; } EKUName2OID; static EKUName2OID EKUList[] = { {"serverAuth", (KMF_OID *)&KMFOID_PKIX_KP_ServerAuth}, {"clientAuth", (KMF_OID *)&KMFOID_PKIX_KP_ClientAuth}, {"codeSigning", (KMF_OID *)&KMFOID_PKIX_KP_CodeSigning}, {"emailProtection", (KMF_OID *)&KMFOID_PKIX_KP_EmailProtection}, {"ipsecEndSystem", (KMF_OID *)&KMFOID_PKIX_KP_IPSecEndSystem}, {"ipsecTunnel", (KMF_OID *)&KMFOID_PKIX_KP_IPSecTunnel}, {"ipsecUser", (KMF_OID *)&KMFOID_PKIX_KP_IPSecUser}, {"timeStamping", (KMF_OID *)&KMFOID_PKIX_KP_TimeStamping}, {"OCSPSigning", (KMF_OID *)&KMFOID_PKIX_KP_OCSPSigning}, {"KPClientAuth", (KMF_OID *)&KMFOID_PKINIT_ClientAuth}, {"KPKdc", (KMF_OID *)&KMFOID_PKINIT_Kdc}, {"scLogon", (KMF_OID *)&KMFOID_MS_KP_SCLogon} }; static int num_ekus = sizeof (EKUList) / sizeof (EKUName2OID); static void addFormatting(xmlNodePtr parent, char *text) { xmlNodePtr snode; if (parent == NULL || text == NULL) return; snode = xmlNewText((const xmlChar *)text); if (snode != NULL) { (void) xmlAddChild(parent, snode); } } static void parseOCSPValidation(xmlNodePtr node, KMF_VALIDATION_POLICY *vinfo) { xmlNodePtr n; char *c; n = node->children; while (n != NULL) { if (!xmlStrcmp((const xmlChar *)n->name, (const xmlChar *)KMF_OCSP_BASIC_ELEMENT)) { vinfo->ocsp_info.basic.responderURI = (char *)xmlGetProp(n, (const xmlChar *)KMF_OCSP_RESPONDER_ATTR); vinfo->ocsp_info.basic.proxy = (char *)xmlGetProp(n, (const xmlChar *)KMF_OCSP_PROXY_ATTR); c = (char *)xmlGetProp(n, (const xmlChar *)KMF_OCSP_URI_ATTR); if (c != NULL && !strcasecmp(c, "true")) { vinfo->ocsp_info.basic.uri_from_cert = 1; xmlFree(c); } vinfo->ocsp_info.basic.response_lifetime = (char *)xmlGetProp(n, (const xmlChar *)KMF_OCSP_RESPONSE_LIFETIME_ATTR); c = (char *)xmlGetProp(n, (const xmlChar *)KMF_OCSP_IGNORE_SIGN_ATTR); if (c != NULL && !strcasecmp(c, "true")) { vinfo->ocsp_info.basic.ignore_response_sign = 1; xmlFree(c); } } else if (!xmlStrcmp((const xmlChar *)n->name, (const xmlChar *)KMF_OCSP_RESPONDER_CERT_ELEMENT)) { vinfo->ocsp_info.resp_cert.name = (char *)xmlGetProp(n, (const xmlChar *)KMF_CERT_NAME_ATTR); vinfo->ocsp_info.resp_cert.serial = (char *)xmlGetProp(n, (const xmlChar *)KMF_CERT_SERIAL_ATTR); vinfo->ocsp_info.has_resp_cert = 1; } n = n->next; } } /* * Parse the "validation-methods" section of the policy. */ static void parseValidation(xmlNodePtr node, KMF_VALIDATION_POLICY *vinfo, KMF_POLICY_RECORD *policy) { xmlNodePtr n; char *c; n = node->children; while (n != NULL) { if (!xmlStrcmp((const xmlChar *)n->name, (const xmlChar *)KMF_OCSP_ELEMENT)) { parseOCSPValidation(n, &policy->validation_info); policy->revocation |= KMF_REVOCATION_METHOD_OCSP; } else if (!xmlStrcmp((const xmlChar *)n->name, (const xmlChar *)KMF_CRL_ELEMENT)) { vinfo->crl_info.basefilename = (char *)xmlGetProp(n, (const xmlChar *)KMF_CRL_BASENAME_ATTR); vinfo->crl_info.directory = (char *)xmlGetProp(n, (const xmlChar *)KMF_CRL_DIRECTORY_ATTR); c = (char *)xmlGetProp(n, (const xmlChar *)KMF_CRL_GET_URI_ATTR); if (c != NULL && !strcasecmp(c, "true")) { vinfo->crl_info.get_crl_uri = 1; } else { vinfo->crl_info.get_crl_uri = 0; } xmlFree(c); vinfo->crl_info.proxy = (char *)xmlGetProp(n, (const xmlChar *)KMF_CRL_PROXY_ATTR); c = (char *)xmlGetProp(n, (const xmlChar *)KMF_CRL_IGNORE_SIGN_ATTR); if (c != NULL && !strcasecmp(c, "true")) { vinfo->crl_info.ignore_crl_sign = 1; } else { vinfo->crl_info.ignore_crl_sign = 0; } xmlFree(c); c = (char *)xmlGetProp(n, (const xmlChar *)KMF_CRL_IGNORE_DATE_ATTR); if (c != NULL && !strcasecmp(c, "true")) { vinfo->crl_info.ignore_crl_date = 1; } else { vinfo->crl_info.ignore_crl_date = 0; } xmlFree(c); policy->revocation |= KMF_REVOCATION_METHOD_CRL; } n = n->next; } } char * kmf_ku_to_string(uint32_t bitfield) { if (bitfield & KMF_digitalSignature) return ("digitalSignature"); if (bitfield & KMF_nonRepudiation) return ("nonRepudiation"); if (bitfield & KMF_keyEncipherment) return ("keyEncipherment"); if (bitfield & KMF_dataEncipherment) return ("dataEncipherment"); if (bitfield & KMF_keyAgreement) return ("keyAgreement"); if (bitfield & KMF_keyCertSign) return ("keyCertSign"); if (bitfield & KMF_cRLSign) return ("cRLSign"); if (bitfield & KMF_encipherOnly) return ("encipherOnly"); if (bitfield & KMF_decipherOnly) return ("decipherOnly"); return (NULL); } uint32_t kmf_string_to_ku(char *kustring) { if (kustring == NULL || !strlen(kustring)) return (0); if (strcasecmp(kustring, "digitalSignature") == 0) return (KMF_digitalSignature); if (strcasecmp(kustring, "nonRepudiation") == 0) return (KMF_nonRepudiation); if (strcasecmp(kustring, "keyEncipherment") == 0) return (KMF_keyEncipherment); if (strcasecmp(kustring, "dataEncipherment") == 0) return (KMF_dataEncipherment); if (strcasecmp(kustring, "keyAgreement") == 0) return (KMF_keyAgreement); if (strcasecmp(kustring, "keyCertSign") == 0) return (KMF_keyCertSign); if (strcasecmp(kustring, "cRLSign") == 0) return (KMF_cRLSign); if (strcasecmp(kustring, "encipherOnly") == 0) return (KMF_encipherOnly); if (strcasecmp(kustring, "decipherOnly") == 0) return (KMF_decipherOnly); return (0); } static void parseKeyUsageSet(xmlNodePtr node, uint32_t *kubits) { xmlNodePtr n; char *c; n = node->children; while (n != NULL) { if (!xmlStrcmp((const xmlChar *)n->name, (const xmlChar *)KMF_KEY_USAGE_ELEMENT)) { c = (char *)xmlGetProp(n, (const xmlChar *)KMF_KEY_USAGE_USE_ATTR); if (c) { *kubits |= kmf_string_to_ku(c); xmlFree(c); } } n = n->next; } } static KMF_OID * dup_oid(KMF_OID *oldoid) { KMF_OID *oid; oid = malloc(sizeof (KMF_OID)); if (oid == NULL) return (NULL); oid->Length = oldoid->Length; oid->Data = malloc(oid->Length); if (oid->Data == NULL) { free(oid); return (NULL); } (void) memcpy(oid->Data, oldoid->Data, oid->Length); return (oid); } KMF_OID * kmf_ekuname_to_oid(char *ekuname) { KMF_OID *oid; int i; if (ekuname == NULL) return (NULL); for (i = 0; i < num_ekus; i++) { if (strcasecmp(EKUList[i].ekuname, ekuname) == 0) { oid = dup_oid(EKUList[i].oid); return (oid); } } return (NULL); } char * kmf_oid_to_ekuname(KMF_OID *oid) { int i; for (i = 0; i < num_ekus; i++) { if (oid->Length == EKUList[i].oid->Length && !memcmp(oid->Data, EKUList[i].oid->Data, oid->Length)) { return (EKUList[i].ekuname); } } return (NULL); } static KMF_RETURN parseExtKeyUsage(xmlNodePtr node, KMF_EKU_POLICY *ekus) { xmlNodePtr n; char *c; KMF_RETURN ret = KMF_OK; boolean_t found = FALSE; n = node->children; while (n != NULL && ret == KMF_OK) { KMF_OID newoid, *oidptr; oidptr = NULL; newoid.Data = NULL; newoid.Length = 0; if (!xmlStrcmp((const xmlChar *)n->name, (const xmlChar *)KMF_EKU_NAME_ELEMENT)) { c = (char *)xmlGetProp(n, (const xmlChar *)KMF_EKU_NAME_ATTR); if (c != NULL) { oidptr = kmf_ekuname_to_oid(c); xmlFree(c); found = TRUE; if (oidptr != NULL) newoid = *oidptr; } } else if (!xmlStrcmp((const xmlChar *)n->name, (const xmlChar *)KMF_EKU_OID_ELEMENT)) { c = (char *)xmlGetProp(n, (const xmlChar *)KMF_EKU_OID_ATTR); if (c != NULL) { (void) kmf_string_to_oid(c, &newoid); xmlFree(c); found = TRUE; } } else { n = n->next; if ((n == NULL) && (!found)) ret = KMF_ERR_POLICY_DB_FORMAT; continue; } if (newoid.Data != NULL) { ekus->eku_count++; ekus->ekulist = realloc(ekus->ekulist, ekus->eku_count * sizeof (KMF_OID)); if (ekus->ekulist != NULL) { ekus->ekulist[ekus->eku_count-1].Length = newoid.Length; ekus->ekulist[ekus->eku_count-1].Data = newoid.Data; } else { ret = KMF_ERR_MEMORY; } } else { ret = KMF_ERR_POLICY_DB_FORMAT; } n = n->next; } return (ret); } static KMF_RETURN parseMapper(xmlNodePtr node, KMF_MAPPER_RECORD *mapper) { xmlNodePtr n; n = node; mapper->mapname = (char *)xmlGetProp(n, (const xmlChar *)KMF_CERT_MAPPER_NAME_ATTR); mapper->dir = (char *)xmlGetProp(n, (const xmlChar *)KMF_CERT_MAPPER_DIR_ATTR); mapper->pathname = (char *)xmlGetProp(n, (const xmlChar *)KMF_CERT_MAPPER_PATH_ATTR); mapper->options = (char *)xmlGetProp(n, (const xmlChar *)KMF_CERT_MAPPER_OPTIONS_ATTR); /* * These are set according to whether mapper setting is taken from the * database or init function attributes. */ mapper->curpathname = NULL; mapper->curoptions = NULL; return (KMF_OK); } int parsePolicyElement(xmlNodePtr node, KMF_POLICY_RECORD *policy) { int ret = 0; xmlNodePtr n = node->xmlChildrenNode; char *c; if (node->type == XML_ELEMENT_NODE) { if (node->properties != NULL) { policy->name = (char *)xmlGetProp(node, (const xmlChar *)KMF_POLICY_NAME_ATTR); c = (char *)xmlGetProp(node, (const xmlChar *)KMF_OPTIONS_IGNORE_DATE_ATTR); if (c && !strcasecmp(c, "true")) { policy->ignore_date = 1; xmlFree((xmlChar *)c); } c = (char *)xmlGetProp(node, (const xmlChar *)KMF_OPTIONS_IGNORE_UNKNOWN_EKUS); if (c && !strcasecmp(c, "true")) { policy->ignore_unknown_ekus = 1; xmlFree(c); } c = (char *)xmlGetProp(node, (const xmlChar *)KMF_OPTIONS_IGNORE_TRUST_ANCHOR); if (c && !strcasecmp(c, "true")) { policy->ignore_trust_anchor = 1; xmlFree(c); } c = (char *)xmlGetProp(node, (const xmlChar *)KMF_OPTIONS_VALIDITY_ADJUSTTIME); if (c) { policy->validity_adjusttime = c; } else { policy->validity_adjusttime = NULL; } policy->ta_name = (char *)xmlGetProp(node, (const xmlChar *)KMF_POLICY_TA_NAME_ATTR); policy->ta_serial = (char *)xmlGetProp(node, (const xmlChar *)KMF_POLICY_TA_SERIAL_ATTR); } n = node->children; while (n != NULL) { if (!xmlStrcmp((const xmlChar *)n->name, (const xmlChar *)KMF_VALIDATION_METHODS_ELEMENT)) parseValidation(n, &policy->validation_info, policy); else if (!xmlStrcmp((const xmlChar *)n->name, (const xmlChar *)KMF_KEY_USAGE_SET_ELEMENT)) parseKeyUsageSet(n, &policy->ku_bits); else if (!xmlStrcmp((const xmlChar *)n->name, (const xmlChar *)KMF_EKU_ELEMENT)) { ret = parseExtKeyUsage(n, &policy->eku_set); if (ret != KMF_OK) return (ret); } else if (!xmlStrcmp((const xmlChar *)n->name, (const xmlChar *)KMF_CERT_MAPPER_ELEMENT)) { ret = parseMapper(n, &policy->mapper); if (ret != KMF_OK) return (ret); } n = n->next; } } return (ret); } static int newprop(xmlNodePtr node, char *attrname, char *src) { xmlAttrPtr newattr; if (src != NULL && strlen(src)) { newattr = xmlNewProp(node, (const xmlChar *)attrname, (xmlChar *)src); if (newattr == NULL) { xmlUnlinkNode(node); xmlFreeNode(node); return (-1); } } return (0); } /* * Add CRL policy information to the XML tree. * Return non-zero on any failure, else 0 for success. * * This function is called only when the KMF_REVOCATION_METHOD_CRL flag is on. */ static int AddCRLNodes(xmlNodePtr node, KMF_CRL_POLICY *crlinfo) { xmlNodePtr n; addFormatting(node, "\t\t"); n = xmlNewChild(node, NULL, (const xmlChar *)"crl", NULL); if (n == NULL) return (-1); if (crlinfo->basefilename && newprop(n, KMF_CRL_BASENAME_ATTR, crlinfo->basefilename)) return (-1); if (crlinfo->directory && newprop(n, KMF_CRL_DIRECTORY_ATTR, crlinfo->directory)) return (-1); if (crlinfo->get_crl_uri && newprop(n, KMF_CRL_GET_URI_ATTR, "TRUE")) { return (-1); } if (crlinfo->proxy && newprop(n, KMF_CRL_PROXY_ATTR, crlinfo->proxy)) return (-1); if (crlinfo->ignore_crl_sign && newprop(n, KMF_CRL_IGNORE_SIGN_ATTR, "TRUE")) { return (-1); } if (crlinfo->ignore_crl_date && newprop(n, KMF_CRL_IGNORE_DATE_ATTR, "TRUE")) { return (-1); } addFormatting(node, "\n"); return (0); } /* * Add OCSP information to the policy tree. * Return non-zero on any failure, else 0 for success. * * This function is called only when the KMF_REVOCATION_METHOD_OCSP flag is on. */ static int AddOCSPNodes(xmlNodePtr parent, KMF_OCSP_POLICY *ocsp) { int ret = 0; xmlNodePtr n_ocsp, n_basic, n_resp; KMF_OCSP_BASIC_POLICY *basic; KMF_RESP_CERT_POLICY *resp_cert; basic = &(ocsp->basic); resp_cert = &(ocsp->resp_cert); if (basic->responderURI != NULL || basic->uri_from_cert == B_TRUE) { addFormatting(parent, "\t\t"); /* basic node */ n_ocsp = xmlNewChild(parent, NULL, (const xmlChar *)KMF_OCSP_ELEMENT, NULL); if (n_ocsp == NULL) return (-1); addFormatting(n_ocsp, "\n\t\t\t"); n_basic = xmlNewChild(n_ocsp, NULL, (const xmlChar *)KMF_OCSP_BASIC_ELEMENT, NULL); if (n_basic == NULL) return (-1); if (basic->responderURI && newprop(n_basic, KMF_OCSP_RESPONDER_ATTR, basic->responderURI)) return (-1); if (basic->proxy && newprop(n_basic, KMF_OCSP_PROXY_ATTR, basic->proxy)) return (-1); if (basic->uri_from_cert && newprop(n_basic, KMF_OCSP_URI_ATTR, "TRUE")) return (-1); if (basic->response_lifetime && newprop(n_basic, KMF_OCSP_RESPONSE_LIFETIME_ATTR, basic->response_lifetime)) return (-1); if (basic->ignore_response_sign && newprop(n_basic, KMF_OCSP_IGNORE_SIGN_ATTR, "TRUE")) return (-1); addFormatting(n_ocsp, "\n\t\t\t"); /* responder cert node */ if (ocsp->has_resp_cert) { n_resp = xmlNewChild(n_ocsp, NULL, (const xmlChar *)KMF_OCSP_RESPONDER_CERT_ELEMENT, NULL); if (n_resp == NULL) return (-1); if (newprop(n_resp, KMF_CERT_NAME_ATTR, resp_cert->name)) return (-1); if (newprop(n_resp, KMF_CERT_SERIAL_ATTR, resp_cert->serial)) return (-1); } addFormatting(n_ocsp, "\n\t\t"); } addFormatting(parent, "\n"); return (ret); } /* * Add validation method information to the policy tree. * Return non-zero on any failure, else 0 for success. */ static int AddValidationNodes(xmlNodePtr parent, KMF_POLICY_RECORD *policy) { xmlNodePtr mnode; int ret = 0; addFormatting(parent, "\t"); mnode = xmlNewChild(parent, NULL, (const xmlChar *)KMF_VALIDATION_METHODS_ELEMENT, NULL); if (mnode == NULL) return (-1); addFormatting(mnode, "\n"); if (policy->revocation & KMF_REVOCATION_METHOD_OCSP) { ret = AddOCSPNodes(mnode, &(policy->validation_info.ocsp_info)); if (ret != KMF_OK) goto end; } if (policy->revocation & KMF_REVOCATION_METHOD_CRL) { ret = AddCRLNodes(mnode, &(policy->validation_info.crl_info)); if (ret != KMF_OK) goto end; } addFormatting(mnode, "\t"); addFormatting(parent, "\n"); end: if (ret != 0) { xmlUnlinkNode(mnode); xmlFreeNode(mnode); } return (ret); } /* * Add mapper policy info to the policy tree. * Return non-zero on any failure, else 0 for success. */ static KMF_RETURN AddMapperPolicyNodes(xmlNodePtr parent, KMF_MAPPER_RECORD *mapper) { KMF_RETURN ret = KMF_OK; xmlNodePtr mapper_node; addFormatting(parent, "\n\t"); mapper_node = xmlNewChild(parent, NULL, (const xmlChar *)KMF_CERT_MAPPER_ELEMENT, NULL); if (mapper_node == NULL) return (KMF_ERR_POLICY_ENGINE); if (mapper->mapname != NULL && newprop(mapper_node, KMF_CERT_MAPPER_NAME_ATTR, mapper->mapname)) { ret = KMF_ERR_POLICY_ENGINE; goto end; } if (mapper->pathname != NULL && newprop(mapper_node, KMF_CERT_MAPPER_PATH_ATTR, mapper->pathname)) { ret = KMF_ERR_POLICY_ENGINE; goto end; } if (mapper->dir != NULL && newprop(mapper_node, KMF_CERT_MAPPER_DIR_ATTR, mapper->dir)) { ret = KMF_ERR_POLICY_ENGINE; goto end; } if (mapper->options != NULL && newprop(mapper_node, KMF_CERT_MAPPER_OPTIONS_ATTR, mapper->options)) ret = KMF_ERR_POLICY_ENGINE; if (ret == KMF_OK) { addFormatting(mapper_node, "\n\t"); addFormatting(parent, "\n"); } end: if (ret != KMF_OK) { xmlUnlinkNode(mapper_node); xmlFreeNode(mapper_node); } return (ret); } /* * Add Key Usage information to the policy tree. * Return non-zero on any failure, else 0 for success. */ static KMF_RETURN AddKeyUsageNodes(xmlNodePtr parent, uint32_t kubits) { int ret = KMF_OK; int i; xmlNodePtr kuset, kunode; if (kubits == 0) return (0); addFormatting(parent, "\n\t"); kuset = xmlNewChild(parent, NULL, (const xmlChar *)KMF_KEY_USAGE_SET_ELEMENT, NULL); if (kuset == NULL) return (KMF_ERR_POLICY_ENGINE); for (i = KULOWBIT; i <= KUHIGHBIT && ret == KMF_OK; i++) { char *s = kmf_ku_to_string((kubits & (1<eku_count > 0) { addFormatting(parent, "\n\t"); n = xmlNewChild(parent, NULL, (const xmlChar *)KMF_EKU_ELEMENT, NULL); if (n == NULL) return (KMF_ERR_POLICY_ENGINE); for (i = 0; i < ekus->eku_count; i++) { char *s = kmf_oid_to_string(&ekus->ekulist[i]); if (s != NULL) { addFormatting(n, "\n\t\t"); kunode = xmlNewChild(n, NULL, (const xmlChar *)KMF_EKU_OID_ELEMENT, NULL); if (kunode == NULL) ret = KMF_ERR_POLICY_ENGINE; else if (newprop(kunode, KMF_EKU_OID_ATTR, s)) ret = KMF_ERR_POLICY_ENGINE; free(s); } else { ret = KMF_ERR_POLICY_ENGINE; } } addFormatting(n, "\n\t"); addFormatting(parent, "\n"); } if (ret != KMF_OK) { xmlUnlinkNode(n); xmlFreeNode(n); } return (ret); } void kmf_free_eku_policy(KMF_EKU_POLICY *ekus) { if (ekus->eku_count > 0) { int i; for (i = 0; i < ekus->eku_count; i++) { kmf_free_data(&ekus->ekulist[i]); } free(ekus->ekulist); } } #define FREE_POLICY_STR(s) if (s != NULL) free(s); void kmf_free_policy_record(KMF_POLICY_RECORD *policy) { if (policy == NULL) return; FREE_POLICY_STR(policy->name) FREE_POLICY_STR(policy->VAL_OCSP_BASIC.responderURI) FREE_POLICY_STR(policy->VAL_OCSP_BASIC.proxy) FREE_POLICY_STR(policy->VAL_OCSP_BASIC.response_lifetime) FREE_POLICY_STR(policy->VAL_OCSP_RESP_CERT.name) FREE_POLICY_STR(policy->VAL_OCSP_RESP_CERT.serial) FREE_POLICY_STR(policy->validation_info.crl_info.basefilename) FREE_POLICY_STR(policy->validation_info.crl_info.directory) FREE_POLICY_STR(policy->validation_info.crl_info.proxy) FREE_POLICY_STR(policy->validity_adjusttime) FREE_POLICY_STR(policy->ta_name) FREE_POLICY_STR(policy->ta_serial) FREE_POLICY_STR(policy->mapper.mapname) FREE_POLICY_STR(policy->mapper.pathname) FREE_POLICY_STR(policy->mapper.options) FREE_POLICY_STR(policy->mapper.dir) kmf_free_eku_policy(&policy->eku_set); (void) memset(policy, 0, sizeof (KMF_POLICY_RECORD)); } /* * kmf_get_policy * * Find a policy record in the database. */ KMF_RETURN kmf_get_policy(char *filename, char *policy_name, KMF_POLICY_RECORD *plc) { KMF_RETURN ret = KMF_OK; xmlParserCtxtPtr ctxt; xmlDocPtr doc = NULL; xmlNodePtr cur, node; int found = 0; if (filename == NULL || policy_name == NULL || plc == NULL) return (KMF_ERR_BAD_PARAMETER); (void) memset(plc, 0, sizeof (KMF_POLICY_RECORD)); /* Create a parser context */ ctxt = xmlNewParserCtxt(); if (ctxt == NULL) return (KMF_ERR_POLICY_DB_FORMAT); /* Read the policy DB and verify it against the schema. */ doc = xmlCtxtReadFile(ctxt, filename, NULL, XML_PARSE_DTDVALID | XML_PARSE_NOERROR | XML_PARSE_NOWARNING); if (doc == NULL || ctxt->valid == 0) { ret = KMF_ERR_POLICY_DB_FORMAT; goto out; } cur = xmlDocGetRootElement(doc); if (cur == NULL) { ret = KMF_ERR_POLICY_DB_FORMAT; goto out; } node = cur->xmlChildrenNode; while (node != NULL && !found) { char *c; /* * Search for the policy that matches the given name. */ if (!xmlStrcmp((const xmlChar *)node->name, (const xmlChar *)KMF_POLICY_ELEMENT)) { /* Check the name attribute */ c = (char *)xmlGetProp(node, (const xmlChar *)KMF_POLICY_NAME_ATTR); /* If a match, parse the rest of the data */ if (c != NULL) { if (strcmp(c, policy_name) == 0) { ret = parsePolicyElement(node, plc); found = (ret == KMF_OK); } xmlFree(c); } } node = node->next; } if (!found) { ret = KMF_ERR_POLICY_NOT_FOUND; goto out; } out: if (ctxt != NULL) xmlFreeParserCtxt(ctxt); if (doc != NULL) xmlFreeDoc(doc); return (ret); } /* * kmf_set_policy * * Set the policy record in the handle. This searches * the policy DB for the named policy. If it is not found * or an error occurred in processing, the existing policy * is kept and an error code is returned. */ KMF_RETURN kmf_set_policy(KMF_HANDLE_T handle, char *policyfile, char *policyname) { KMF_RETURN ret = KMF_OK; KMF_POLICY_RECORD *newpolicy = NULL; CLEAR_ERROR(handle, ret); if (ret != KMF_OK) return (ret); newpolicy = malloc(sizeof (KMF_POLICY_RECORD)); if (newpolicy == NULL) return (KMF_ERR_MEMORY); (void) memset(newpolicy, 0, sizeof (KMF_POLICY_RECORD)); ret = kmf_get_policy( policyfile == NULL ? KMF_DEFAULT_POLICY_FILE : policyfile, policyname == NULL ? KMF_DEFAULT_POLICY_NAME : policyname, newpolicy); if (ret != KMF_OK) goto out; ret = kmf_verify_policy(newpolicy); if (ret != KMF_OK) goto out; /* release the existing policy data (if any). */ if (handle->policy != NULL) { kmf_free_policy_record(handle->policy); free(handle->policy); } handle->policy = newpolicy; out: /* Cleanup any data allocated before the error occurred */ if (ret != KMF_OK) { kmf_free_policy_record(newpolicy); free(newpolicy); } return (ret); } static KMF_RETURN deletePolicyNode(xmlNodePtr node, char *policy_name) { KMF_RETURN ret = KMF_OK; int found = 0; xmlNodePtr dnode = NULL; while (node != NULL && !found) { char *c; /* * Search for the policy that matches the given name. */ if (!xmlStrcmp((const xmlChar *)node->name, (const xmlChar *)KMF_POLICY_ELEMENT)) { /* Check the name attribute */ c = (char *)xmlGetProp(node, (const xmlChar *)KMF_POLICY_NAME_ATTR); /* If a match, parse the rest of the data */ if (c != NULL) { if (strcmp(c, policy_name) == 0) { found = 1; dnode = node; } xmlFree(c); } } if (!found) node = node->next; } if (found && dnode != NULL) { /* Unlink the node */ xmlUnlinkNode(dnode); /* Delete it from the document tree */ xmlFreeNode(dnode); } else { ret = KMF_ERR_POLICY_NOT_FOUND; } return (ret); } /* * update_policyfile * * Attempt to do a "safe" file update as follows: * 1. Lock the original file. * 2. Create and write to a temporary file * 3. Replace the original file with the temporary file. */ static KMF_RETURN update_policyfile(xmlDocPtr doc, char *filename) { KMF_RETURN ret = KMF_OK; FILE *pfile, *tmpfile; char tmpfilename[MAXPATHLEN]; char *p; int prefix_len, tmpfd; mode_t old_mode; /* * Open and lock the DB file. First try to open an existing file, * if that fails, open it as if it were new. */ if ((pfile = fopen(filename, "r+")) == NULL && errno == ENOENT) pfile = fopen(filename, "w+"); if (pfile == NULL) return (KMF_ERR_POLICY_DB_FILE); if (lockf(fileno(pfile), F_TLOCK, 0) == -1) { (void) fclose(pfile); return (KMF_ERR_POLICY_DB_FILE); } /* * Create a temporary file to hold the new data. */ (void) memset(tmpfilename, 0, sizeof (tmpfilename)); p = (char *)strrchr(filename, '/'); if (p == NULL) { /* * filename contains basename only so we * create a temp file in current directory. */ if (strlcpy(tmpfilename, TMPFILE_TEMPLATE, sizeof (tmpfilename)) >= sizeof (tmpfilename)) return (KMF_ERR_INTERNAL); } else { /* * create a temp file in the same directory * as the policy file. */ prefix_len = p - filename; (void) strncpy(tmpfilename, filename, prefix_len); (void) strncat(tmpfilename, "/", 1); (void) strncat(tmpfilename, TMPFILE_TEMPLATE, sizeof (TMPFILE_TEMPLATE)); } old_mode = umask(077); tmpfd = mkstemp(tmpfilename); (void) umask(old_mode); if (tmpfd == -1) { return (KMF_ERR_POLICY_DB_FILE); } if ((tmpfile = fdopen(tmpfd, "w")) == NULL) { (void) close(tmpfd); (void) unlink(tmpfilename); (void) fclose(pfile); return (KMF_ERR_POLICY_DB_FILE); } /* * Write the new info to the temporary file. */ if (xmlDocFormatDump(tmpfile, doc, 1) == -1) { (void) fclose(pfile); (void) fclose(tmpfile); (void) unlink(tmpfilename); return (KMF_ERR_POLICY_ENGINE); } (void) fclose(pfile); if (fchmod(tmpfd, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) == -1) { (void) close(tmpfd); (void) unlink(tmpfilename); return (KMF_ERR_POLICY_DB_FILE); } if (fclose(tmpfile) != 0) return (KMF_ERR_POLICY_DB_FILE); /* * Replace the original file with the updated tempfile. */ if (rename(tmpfilename, filename) == -1) { ret = KMF_ERR_POLICY_DB_FILE; } if (ret != KMF_OK) { /* try to remove the tmp file */ (void) unlink(tmpfilename); } return (ret); } /* * kmf_delete_policy_from_db * * Find a policy by name and remove it from the policy DB file. * If the policy is not found, return an error. */ KMF_RETURN kmf_delete_policy_from_db(char *policy_name, char *dbfilename) { KMF_RETURN ret; xmlParserCtxtPtr ctxt = NULL; xmlDocPtr doc = NULL; xmlNodePtr cur, node; if (policy_name == NULL || dbfilename == NULL) return (KMF_ERR_BAD_PARAMETER); /* * Cannot delete the default policy record from the system * default policy database (/etc/security/kmfpolicy.xml). */ if (strcmp(dbfilename, KMF_DEFAULT_POLICY_FILE) == 0 && strcmp(policy_name, KMF_DEFAULT_POLICY_NAME) == 0) return (KMF_ERR_BAD_PARAMETER); /* Make sure the policy file exists */ if (access(dbfilename, R_OK | W_OK)) return (KMF_ERR_BAD_PARAMETER); /* Read the policy DB and verify it against the schema. */ ctxt = xmlNewParserCtxt(); if (ctxt == NULL) return (KMF_ERR_POLICY_DB_FORMAT); doc = xmlCtxtReadFile(ctxt, dbfilename, NULL, XML_PARSE_DTDVALID | XML_PARSE_NOERROR | XML_PARSE_NOWARNING); if (doc == NULL || ctxt->valid == 0) { ret = KMF_ERR_POLICY_DB_FORMAT; goto end; } cur = xmlDocGetRootElement(doc); if (cur == NULL) { xmlFreeDoc(doc); return (KMF_ERR_POLICY_DB_FORMAT); } node = cur->xmlChildrenNode; ret = deletePolicyNode(node, policy_name); if (ret == KMF_OK) ret = update_policyfile(doc, dbfilename); end: if (ctxt != NULL) xmlFreeParserCtxt(ctxt); if (doc != NULL) xmlFreeDoc(doc); return (ret); } /* * Add a new policy node to the Policy DB XML tree. */ static KMF_RETURN addPolicyNode(xmlNodePtr pnode, KMF_POLICY_RECORD *policy) { KMF_RETURN ret = KMF_OK; if (pnode != NULL && policy != NULL) { if (newprop(pnode, KMF_POLICY_NAME_ATTR, policy->name) != 0) { ret = KMF_ERR_POLICY_ENGINE; goto out; } if (policy->ignore_date) { if (newprop(pnode, KMF_OPTIONS_IGNORE_DATE_ATTR, "TRUE")) { ret = KMF_ERR_POLICY_ENGINE; goto out; } } if (policy->ignore_unknown_ekus) { if (newprop(pnode, KMF_OPTIONS_IGNORE_UNKNOWN_EKUS, "TRUE")) { ret = KMF_ERR_POLICY_ENGINE; goto out; } } if (policy->ignore_trust_anchor) { if (newprop(pnode, KMF_OPTIONS_IGNORE_TRUST_ANCHOR, "TRUE")) { ret = KMF_ERR_POLICY_ENGINE; goto out; } } if (policy->validity_adjusttime) { if (newprop(pnode, KMF_OPTIONS_VALIDITY_ADJUSTTIME, policy->validity_adjusttime)) { ret = KMF_ERR_POLICY_ENGINE; goto out; } } if (newprop(pnode, KMF_POLICY_TA_NAME_ATTR, policy->ta_name) != 0) { ret = KMF_ERR_POLICY_ENGINE; goto out; } if (newprop(pnode, KMF_POLICY_TA_SERIAL_ATTR, policy->ta_serial) != 0) { ret = KMF_ERR_POLICY_ENGINE; goto out; } /* Add a text node for readability */ addFormatting(pnode, "\n"); if (ret = AddValidationNodes(pnode, policy)) { goto out; } if ((ret = AddKeyUsageNodes(pnode, policy->ku_bits))) { goto out; } if ((ret = AddExtKeyUsageNodes(pnode, &policy->eku_set))) { goto out; } if ((ret = AddMapperPolicyNodes(pnode, &policy->mapper))) { goto out; } } else { ret = KMF_ERR_BAD_PARAMETER; } out: if (ret != KMF_OK && pnode != NULL) { xmlUnlinkNode(pnode); xmlFreeNode(pnode); } return (ret); } KMF_RETURN kmf_verify_policy(KMF_POLICY_RECORD *policy) { KMF_RETURN ret = KMF_OK; boolean_t has_ta; if (policy->name == NULL || !strlen(policy->name)) return (KMF_ERR_POLICY_NAME); /* Check the TA related policy */ if (policy->ta_name != NULL && strcasecmp(policy->ta_name, "search") == 0) { has_ta = B_TRUE; } else if (policy->ta_name != NULL && policy->ta_serial != NULL) { has_ta = B_TRUE; } else if (policy->ta_name == NULL && policy->ta_serial == NULL) { has_ta = B_FALSE; } else { /* * If the TA cert is set, then both name and serial number * need to be specified. */ return (KMF_ERR_TA_POLICY); } if (has_ta == B_FALSE && policy->ignore_trust_anchor == B_FALSE) return (KMF_ERR_TA_POLICY); if (policy->revocation & KMF_REVOCATION_METHOD_OCSP) { /* * For OCSP, either use a fixed responder or use the * value from the cert, but not both. */ if ((policy->VAL_OCSP_BASIC.responderURI == NULL && policy->VAL_OCSP_BASIC.uri_from_cert == B_FALSE) || (policy->VAL_OCSP_BASIC.responderURI != NULL && policy->VAL_OCSP_BASIC.uri_from_cert == B_TRUE)) return (KMF_ERR_OCSP_POLICY); /* * If the OCSP responder cert is set, then both name and serial * number need to be specified. */ if ((policy->VAL_OCSP_RESP_CERT.name != NULL && policy->VAL_OCSP_RESP_CERT.serial == NULL) || (policy->VAL_OCSP_RESP_CERT.name == NULL && policy->VAL_OCSP_RESP_CERT.serial != NULL)) return (KMF_ERR_OCSP_POLICY); } return (ret); } /* * Update the KMF policy file by creating a new XML Policy doc tree * from the data in the KMF_POLICY_RECORD structure. If "check_policy" * is true, then we check the policy sanity also. */ KMF_RETURN kmf_add_policy_to_db(KMF_POLICY_RECORD *policy, char *dbfilename, boolean_t check_policy) { KMF_RETURN ret = KMF_OK; xmlDocPtr doc = NULL; xmlNodePtr root, node; xmlParserCtxtPtr ctxt = NULL; if (policy == NULL || dbfilename == NULL) return (KMF_ERR_BAD_PARAMETER); if (check_policy == B_TRUE) { if (ret = kmf_verify_policy(policy)) return (ret); } /* If the policyDB exists, load it into memory */ if (!access(dbfilename, R_OK)) { /* Create a parser context */ ctxt = xmlNewParserCtxt(); if (ctxt == NULL) return (KMF_ERR_POLICY_DB_FORMAT); doc = xmlCtxtReadFile(ctxt, dbfilename, NULL, XML_PARSE_DTDVALID | XML_PARSE_NOERROR | XML_PARSE_NOWARNING); if (doc == NULL || ctxt->valid == 0) { ret = KMF_ERR_POLICY_DB_FORMAT; goto out; } root = xmlDocGetRootElement(doc); if (root == NULL) { ret = KMF_ERR_POLICY_DB_FORMAT; goto out; } node = root->xmlChildrenNode; /* * If the DB has an existing policy of the * same name, delete it from the tree. */ ret = deletePolicyNode(node, policy->name); if (ret == KMF_ERR_POLICY_NOT_FOUND) ret = KMF_OK; } else { /* Initialize a new DB tree */ doc = xmlNewDoc((const xmlChar *)"1.0"); if (doc == NULL) return (KMF_ERR_POLICY_ENGINE); /* * Add the DOCTYPE header to the tree so the * DTD link is embedded */ doc->intSubset = xmlCreateIntSubset(doc, (const xmlChar *)KMF_POLICY_ROOT, NULL, (const xmlChar *)KMF_POLICY_DTD); root = xmlNewDocNode(doc, NULL, (const xmlChar *)KMF_POLICY_ROOT, NULL); if (root != NULL) { (void) xmlDocSetRootElement(doc, root); } } /* Append the new policy info to the root node. */ if (root != NULL) { xmlNodePtr pnode; pnode = xmlNewChild(root, NULL, (const xmlChar *)KMF_POLICY_ELEMENT, NULL); ret = addPolicyNode(pnode, policy); /* If that worked, update the DB file. */ if (ret == KMF_OK) ret = update_policyfile(doc, dbfilename); } else { ret = KMF_ERR_POLICY_ENGINE; } out: if (ctxt != NULL) xmlFreeParserCtxt(ctxt); if (doc != NULL) xmlFreeDoc(doc); return (ret); }