/* * 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) 2008, 2010, Oracle and/or its affiliates. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include KMF_RETURN copy_data(KMF_DATA *dst, KMF_DATA *src) { KMF_RETURN ret = KMF_OK; if (dst == NULL || src == NULL) return (KMF_ERR_BAD_PARAMETER); if (src->Length == 0) { dst->Length = 0; dst->Data = NULL; src->Data = NULL; return (ret); } dst->Data = malloc(src->Length); if (dst->Data == NULL) return (KMF_ERR_MEMORY); dst->Length = src->Length; (void) memcpy(dst->Data, src->Data, src->Length); return (ret); } KMF_RETURN copy_extension_data(KMF_X509_EXTENSION *dstext, KMF_X509_EXTENSION *srcext) { KMF_RETURN ret = KMF_OK; if (dstext == NULL || srcext == NULL) return (KMF_ERR_BAD_PARAMETER); (void) memset(dstext, 0, sizeof (KMF_X509_EXTENSION)); ret = copy_data(&dstext->extnId, &srcext->extnId); if (ret != KMF_OK) goto cleanup; dstext->extnId.Length = srcext->extnId.Length; dstext->critical = srcext->critical; dstext->format = srcext->format; ret = copy_data(&dstext->BERvalue, &srcext->BERvalue); if (ret != KMF_OK) goto cleanup; dstext->value.tagAndValue = malloc(sizeof (KMF_X509EXT_TAGandVALUE)); if (dstext->value.tagAndValue == NULL) { ret = KMF_ERR_MEMORY; goto cleanup; } (void) memset(dstext->value.tagAndValue, 0, sizeof (KMF_X509EXT_TAGandVALUE)); ret = copy_data(&dstext->value.tagAndValue->value, &srcext->value.tagAndValue->value); if (ret != KMF_OK) goto cleanup; dstext->value.tagAndValue->type = srcext->value.tagAndValue->type; cleanup: if (ret != KMF_OK) { if (dstext->extnId.Data != NULL) kmf_free_data(&dstext->extnId); if (dstext->BERvalue.Data != NULL) kmf_free_data(&dstext->BERvalue); if (dstext->value.tagAndValue->value.Data == NULL) kmf_free_data(&dstext->value.tagAndValue->value); } return (ret); } /* * Given a block of DER encoded X.509 certificate data and * an OID for the desired extension, this routine will * parse the cert data and return the data associated with * the extension if it is found. * * RETURNS: * KMF_OK - if extension found and copied OK. * KMF_ERR_EXTENSION_NOT_FOUND - extension not found. * parsing and memory allocation errors are also possible. */ KMF_RETURN kmf_get_cert_extn(const KMF_DATA *certdata, KMF_OID *extoid, KMF_X509_EXTENSION *extdata) { KMF_RETURN ret = KMF_OK; KMF_X509_CERTIFICATE *cert = NULL; KMF_X509_EXTENSION *eptr = NULL; int i, found = 0; if (certdata == NULL || extoid == NULL || extdata == NULL) return (KMF_ERR_BAD_PARAMETER); ret = DerDecodeSignedCertificate(certdata, &cert); if (ret != KMF_OK) return (ret); if (cert->certificate.extensions.numberOfExtensions == 0) { goto end; } (void) memset((void *)extdata, 0, sizeof (KMF_X509_EXTENSION)); for (i = 0; !found && i < cert->certificate.extensions.numberOfExtensions; i++) { eptr = &cert->certificate.extensions.extensions[i]; if (IsEqualOid(extoid, &eptr->extnId)) { ret = copy_extension_data(extdata, eptr); found++; } } end: if (!found) ret = KMF_ERR_EXTENSION_NOT_FOUND; if (cert != NULL) { kmf_free_signed_cert(cert); free(cert); } return (ret); } /* * Given a block of DER encoded X.509 certificate data and * a "crit/non-crit/all" flag, search the extensions and * return the OIDs for critical, non-critical or all extensions. * * RETURNS: * KMF_OK - if extension found and copied OK. * parsing and memory allocation errors are also possible. * * OIDlist - array of KMF_OID records, allocated * by this function. * NumOIDs - number of critical extensions found. */ KMF_RETURN kmf_get_cert_extns(const KMF_DATA *certdata, KMF_FLAG_CERT_EXTN flag, KMF_X509_EXTENSION **extlist, int *nextns) { KMF_RETURN ret = KMF_OK; KMF_X509_CERTIFICATE *cert; KMF_X509_EXTENSION *eptr, *elist; int i; if (certdata == NULL || extlist == NULL || nextns == NULL) return (KMF_ERR_BAD_PARAMETER); if (flag < KMF_ALL_EXTNS || flag > KMF_NONCRITICAL_EXTNS) return (KMF_ERR_BAD_PARAMETER); *nextns = 0; *extlist = elist = NULL; ret = DerDecodeSignedCertificate(certdata, &cert); if (ret != KMF_OK) return (ret); if (cert->certificate.extensions.numberOfExtensions == 0) return (KMF_ERR_EXTENSION_NOT_FOUND); for (i = 0; i < cert->certificate.extensions.numberOfExtensions; i++) { eptr = &cert->certificate.extensions.extensions[i]; if (flag == KMF_CRITICAL_EXTNS && eptr->critical == 0) continue; else if (flag == KMF_NONCRITICAL_EXTNS && eptr->critical != 0) continue; (*nextns)++; elist = realloc(elist, sizeof (KMF_X509_EXTENSION) * (*nextns)); if (elist == NULL) { ret = KMF_ERR_MEMORY; goto end; } ret = copy_extension_data(&elist[(*nextns) - 1], eptr); if (ret != KMF_OK) goto end; } end: kmf_free_signed_cert(cert); free(cert); if (ret != KMF_OK) { if (elist != NULL) { free(elist); elist = NULL; } *nextns = 0; } /* * If the flag is not all, then it is possible that we did not find * any critical or non_critical extensions. When that happened, * return KMF_ERR_EXTENSION_NOT_FOUND. */ if (flag != KMF_ALL_EXTNS && ret == KMF_OK && *nextns == 0) ret = KMF_ERR_EXTENSION_NOT_FOUND; *extlist = elist; return (ret); } /* * If the given certificate data (X.509 DER encoded data) * contains the Key Usage extension, parse that * data and return it in the KMF_X509EXT_BASICCONSTRAINTS * record. * * RETURNS: * KMF_OK - success * KMF_ERR_BAD_PARAMETER - input data was bad. * KMF_ERR_EXTENSION_NOT_FOUND - extension not found. */ KMF_RETURN kmf_get_cert_ku(const KMF_DATA *certdata, KMF_X509EXT_KEY_USAGE *keyusage) { KMF_RETURN ret = KMF_OK; KMF_X509_EXTENSION extn; if (certdata == NULL || keyusage == NULL) return (KMF_ERR_BAD_PARAMETER); (void) memset(&extn, 0, sizeof (extn)); /* * Check standard KeyUsage bits */ ret = kmf_get_cert_extn(certdata, (KMF_OID *)&KMFOID_KeyUsage, &extn); if (ret != KMF_OK) { goto end; } keyusage->critical = (extn.critical != 0); if (extn.value.tagAndValue->value.Length > 1) { keyusage->KeyUsageBits = extn.value.tagAndValue->value.Data[1] << 8; } else { keyusage->KeyUsageBits = extn.value.tagAndValue->value.Data[0]; } end: kmf_free_extn(&extn); return (ret); } KMF_BOOL is_eku_present(KMF_X509EXT_EKU *ekuptr, KMF_OID *ekuoid) { int i; if (ekuptr == NULL || ekuoid == NULL) return (0); for (i = 0; i < ekuptr->nEKUs; i++) if (IsEqualOid(&ekuptr->keyPurposeIdList[i], ekuoid)) return (1); return (0); } KMF_RETURN parse_eku_data(const KMF_DATA *asn1data, KMF_X509EXT_EKU *ekuptr) { KMF_RETURN ret = KMF_OK; BerElement *asn1 = NULL; BerValue exdata; KMF_OID oid; char *end = NULL; ber_len_t size; /* * Decode the ASN.1 data for the extension. */ exdata.bv_val = (char *)asn1data->Data; exdata.bv_len = asn1data->Length; if ((asn1 = kmfder_init(&exdata)) == NULL) { ret = KMF_ERR_MEMORY; goto end; } /* * certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation */ if (kmfber_first_element(asn1, &size, &end) != BER_OBJECT_IDENTIFIER) { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } /* * Count the number of EKU OIDs and store in * the array. */ while (kmfber_next_element(asn1, &size, end) == BER_OBJECT_IDENTIFIER) { /* Skip over the CONSTRUCTED SET tag */ if (kmfber_scanf(asn1, "D", &oid) == KMFBER_DEFAULT) { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } ekuptr->nEKUs++; ekuptr->keyPurposeIdList = realloc(ekuptr->keyPurposeIdList, ekuptr->nEKUs * sizeof (KMF_OID)); if (ekuptr->keyPurposeIdList == NULL) { ret = KMF_ERR_MEMORY; goto end; } ekuptr->keyPurposeIdList[ekuptr->nEKUs - 1] = oid; } end: if (asn1 != NULL) kmfber_free(asn1, 1); if (ret != KMF_OK) { if (ekuptr->keyPurposeIdList != NULL) { free_keyidlist(ekuptr->keyPurposeIdList, ekuptr->nEKUs); ekuptr->keyPurposeIdList = NULL; ekuptr->critical = 0; } } return (ret); } KMF_RETURN kmf_get_cert_eku(const KMF_DATA *certdata, KMF_X509EXT_EKU *ekuptr) { KMF_RETURN ret = KMF_OK; KMF_X509_EXTENSION extn; if (certdata == NULL || ekuptr == NULL) return (KMF_ERR_BAD_PARAMETER); (void) memset(&extn, 0, sizeof (KMF_X509_EXTENSION)); ekuptr->nEKUs = 0; ekuptr->keyPurposeIdList = NULL; ekuptr->critical = 0; ret = kmf_get_cert_extn(certdata, (KMF_OID *)&KMFOID_ExtendedKeyUsage, &extn); if (ret != KMF_OK) { goto end; } ret = parse_eku_data(&extn.BERvalue, ekuptr); end: kmf_free_extn(&extn); return (ret); } /* * If the given certificate data (X.509 DER encoded data) * contains the Basic Constraints extension, parse that * data and return it in the KMF_X509EXT_BASICCONSTRAINTS * record. * * RETURNS: * KMF_OK - success * KMF_ERR_BAD_PARAMETER - input data was bad. * KMF_ERR_EXTENSION_NOT_FOUND - extension not found. */ KMF_RETURN kmf_get_cert_basic_constraint(const KMF_DATA *certdata, KMF_BOOL *critical, KMF_X509EXT_BASICCONSTRAINTS *constraint) { KMF_RETURN ret = KMF_OK; KMF_X509_EXTENSION extn; BerElement *asn1 = NULL; BerValue exdata; ber_len_t size; char *end = NULL; int tag; if (certdata == NULL || constraint == NULL || critical == NULL) return (KMF_ERR_BAD_PARAMETER); (void) memset(&extn, 0, sizeof (KMF_X509_EXTENSION)); ret = kmf_get_cert_extn(certdata, (KMF_OID *)&KMFOID_BasicConstraints, &extn); if (ret != KMF_OK) { goto end; } *critical = (extn.critical != 0); exdata.bv_val = (char *)extn.value.tagAndValue->value.Data; exdata.bv_len = extn.value.tagAndValue->value.Length; if ((asn1 = kmfder_init(&exdata)) == NULL) { ret = KMF_ERR_MEMORY; goto end; } if (kmfber_scanf(asn1, "b", &constraint->cA) == KMFBER_DEFAULT) { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } constraint->pathLenConstraintPresent = KMF_FALSE; tag = kmfber_next_element(asn1, &size, end); if (tag == BER_INTEGER) { if (kmfber_scanf(asn1, "i", &constraint->pathLenConstraint) == KMFBER_DEFAULT) { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } constraint->pathLenConstraintPresent = KMF_TRUE; } end: kmf_free_extn(&extn); if (asn1 != NULL) kmfber_free(asn1, 1); return (ret); } static KMF_X509EXT_POLICYQUALIFIERINFO * get_pqinfo(BerElement *asn1) { KMF_X509EXT_POLICYQUALIFIERINFO *pqinfo = NULL; KMF_RETURN ret = KMF_OK; int tag; ber_len_t size; char *end = NULL; /* * Policy Qualifiers may be a list of sequences. * * PolicyInformation ::= SEQUENCE { * policyIdentifier CertPolicyId, * policyQualifiers SEQUENCE SIZE (1..MAX) OF * PolicyQualifierInfo OPTIONAL * } * * PolicyQualifierInfo ::= SEQUENCE { * policyQualifierId PolicyQualifierId, * qualifier ANY DEFINED BY policyQualifierId * } */ /* * We already got the CertPolicyId, we just need to * find all of the policyQualifiers in the set. * * Mark the first element of the SEQUENCE and reset the end ptr * so the ber/der code knows when to stop looking. */ if ((tag = kmfber_first_element(asn1, &size, &end)) != BER_CONSTRUCTED_SEQUENCE) { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } /* We found a sequence, loop until done */ while ((tag = kmfber_next_element(asn1, &size, end)) == BER_CONSTRUCTED_SEQUENCE) { /* Skip over the CONSTRUCTED SET tag */ if (kmfber_scanf(asn1, "T", &tag) == KMFBER_DEFAULT) { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } /* * Allocate memory for the Policy Qualifier Info */ pqinfo = malloc(sizeof (KMF_X509EXT_POLICYQUALIFIERINFO)); if (pqinfo == NULL) { ret = KMF_ERR_MEMORY; goto end; } (void) memset((void *)pqinfo, 0, sizeof (KMF_X509EXT_POLICYQUALIFIERINFO)); /* * Read the PolicyQualifier OID */ if (kmfber_scanf(asn1, "D", &pqinfo->policyQualifierId) == KMFBER_DEFAULT) { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } /* * The OID of the policyQualifierId determines what * sort of data comes next. */ if (IsEqualOid(&pqinfo->policyQualifierId, (KMF_OID *)&KMFOID_PKIX_PQ_CPSuri)) { /* * CPS uri must be an IA5STRING */ if (kmfber_scanf(asn1, "tl", &tag, &size) == KMFBER_DEFAULT || tag != BER_IA5STRING || size == 0) { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } if ((pqinfo->value.Data = malloc(size)) == NULL) { ret = KMF_ERR_MEMORY; goto end; } if (kmfber_scanf(asn1, "s", pqinfo->value.Data, &pqinfo->value.Length) == KMFBER_DEFAULT) { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } } else if (IsEqualOid(&pqinfo->policyQualifierId, (KMF_OID *)&KMFOID_PKIX_PQ_Unotice)) { if (kmfber_scanf(asn1, "tl", &tag, &size) == KMFBER_DEFAULT || tag != BER_CONSTRUCTED_SEQUENCE) { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } /* * For now, just copy the while UserNotice ASN.1 * blob into the pqinfo data record. * TBD - parse it into individual fields. */ if ((pqinfo->value.Data = malloc(size)) == NULL) { ret = KMF_ERR_MEMORY; goto end; } if (kmfber_scanf(asn1, "s", pqinfo->value.Data, &pqinfo->value.Length) == KMFBER_DEFAULT) { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } } else { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } } end: if (ret != KMF_OK) { if (pqinfo != NULL) { kmf_free_data(&pqinfo->value); kmf_free_data(&pqinfo->policyQualifierId); free(pqinfo); pqinfo = NULL; } } return (pqinfo); } /* * If the given certificate data (X.509 DER encoded data) * contains the Certificate Policies extension, parse that * data and return it in the KMF_X509EXT_CERT_POLICIES * record. * * RETURNS: * KMF_OK - success * KMF_ERR_BAD_PARAMETER - input data was bad. * KMF_ERR_EXTENSION_NOT_FOUND - extension not found. * parsing and memory allocation errors are also possible. */ KMF_RETURN kmf_get_cert_policies(const KMF_DATA *certdata, KMF_BOOL *critical, KMF_X509EXT_CERT_POLICIES *extptr) { KMF_RETURN ret = KMF_OK; KMF_X509_EXTENSION extn; KMF_X509EXT_POLICYINFO *pinfo; KMF_X509EXT_POLICYQUALIFIERINFO *pqinfo; BerElement *asn1 = NULL; BerValue exdata; ber_len_t size; char *end = NULL; int tag; if (certdata == NULL || critical == NULL || extptr == NULL) return (KMF_ERR_BAD_PARAMETER); (void) memset(&extn, 0, sizeof (extn)); ret = kmf_get_cert_extn(certdata, (KMF_OID *)&KMFOID_CertificatePolicies, &extn); if (ret != KMF_OK) { goto end; } *critical = (extn.critical != 0); /* * Decode the ASN.1 data for the extension. */ exdata.bv_val = (char *)extn.BERvalue.Data; exdata.bv_len = extn.BERvalue.Length; (void) memset((void *)extptr, 0, sizeof (KMF_X509EXT_CERT_POLICIES)); if ((asn1 = kmfder_init(&exdata)) == NULL) { ret = KMF_ERR_MEMORY; goto end; } /* * certificatePolicies ::= SEQUENCE SIZE (1..MAX) OF PolicyInformation */ if ((tag = kmfber_first_element(asn1, &size, &end)) != BER_CONSTRUCTED_SEQUENCE) { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } /* * Collect all of the PolicyInformation SEQUENCES * * PolicyInformation ::= SEQUENCE { * policyIdentifier CertPolicyId, * policyQualifiers SEQUENCE SIZE (1..MAX) OF * PolicyQualifierInfo OPTIONAL * } * * Loop over the SEQUENCES of PolicyInfo */ while ((tag = kmfber_next_element(asn1, &size, end)) == BER_CONSTRUCTED_SEQUENCE) { /* Skip over the CONSTRUCTED SET tag */ if (kmfber_scanf(asn1, "T", &tag) == KMFBER_DEFAULT) { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } pinfo = malloc(sizeof (KMF_X509EXT_POLICYINFO)); if (pinfo == NULL) { ret = KMF_ERR_MEMORY; goto end; } (void) memset((void *)pinfo, 0, sizeof (KMF_X509EXT_POLICYINFO)); /* * Decode the PolicyInformation SEQUENCE */ if ((tag = kmfber_scanf(asn1, "D", &pinfo->policyIdentifier)) == KMFBER_DEFAULT) { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } /* * Gather all of the associated PolicyQualifierInfo recs */ pqinfo = get_pqinfo(asn1); if (pqinfo != NULL) { int cnt = pinfo->policyQualifiers.numberOfPolicyQualifiers; cnt++; pinfo->policyQualifiers.policyQualifier = realloc( pinfo->policyQualifiers.policyQualifier, cnt * sizeof (KMF_X509EXT_POLICYQUALIFIERINFO)); if (pinfo->policyQualifiers.policyQualifier == NULL) { ret = KMF_ERR_MEMORY; goto end; } pinfo->policyQualifiers.numberOfPolicyQualifiers = cnt; pinfo->policyQualifiers.policyQualifier[cnt-1] = *pqinfo; free(pqinfo); } extptr->numberOfPolicyInfo++; extptr->policyInfo = realloc(extptr->policyInfo, extptr->numberOfPolicyInfo * sizeof (KMF_X509EXT_POLICYINFO)); if (extptr->policyInfo == NULL) { ret = KMF_ERR_MEMORY; goto end; } extptr->policyInfo[extptr->numberOfPolicyInfo-1] = *pinfo; free(pinfo); } end: kmf_free_extn(&extn); if (asn1 != NULL) kmfber_free(asn1, 1); return (ret); } /* * If the given certificate data (X.509 DER encoded data) * contains the Authority Information Access extension, parse that * data and return it in the KMF_X509EXT_AUTHINFOACCESS * record. * * RETURNS: * KMF_OK - success * KMF_ERR_BAD_PARAMETER - input data was bad. * KMF_ERR_EXTENSION_NOT_FOUND - extension not found. */ KMF_RETURN kmf_get_cert_auth_info_access(const KMF_DATA *certdata, KMF_X509EXT_AUTHINFOACCESS *aia) { KMF_RETURN ret = KMF_OK; KMF_X509_EXTENSION extn; BerElement *asn1 = NULL; BerValue exdata; ber_len_t size; char *end = NULL; int tag; KMF_X509EXT_ACCESSDESC *access_info = NULL; if (certdata == NULL || aia == NULL) { return (KMF_ERR_BAD_PARAMETER); } (void) memset(&extn, 0, sizeof (KMF_X509_EXTENSION)); ret = kmf_get_cert_extn(certdata, (KMF_OID *)&KMFOID_AuthorityInfoAccess, &extn); if (ret != KMF_OK) { goto end; } /* * Decode the ASN.1 data for the extension. */ exdata.bv_val = (char *)extn.BERvalue.Data; exdata.bv_len = extn.BERvalue.Length; (void) memset((void *)aia, 0, sizeof (KMF_X509EXT_AUTHINFOACCESS)); if ((asn1 = kmfder_init(&exdata)) == NULL) { ret = KMF_ERR_MEMORY; goto end; } /* * AuthorityInfoAccessSyntax ::= * SEQUENCE SIZE (1..MAX) OF AccessDescription */ if ((tag = kmfber_first_element(asn1, &size, &end)) != BER_CONSTRUCTED_SEQUENCE) { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } /* * AccessDescription ::= SEQUENCE { * accessMethod OBJECT IDENTIFIER, * accessLocation GeneralName } */ while ((tag = kmfber_next_element(asn1, &size, end)) == BER_CONSTRUCTED_SEQUENCE) { /* Skip over the CONSTRUCTED SET tag */ if (kmfber_scanf(asn1, "T", &tag) == KMFBER_DEFAULT) { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } access_info = malloc(sizeof (KMF_X509EXT_ACCESSDESC)); if (access_info == NULL) { ret = KMF_ERR_MEMORY; goto end; } (void) memset((void *)access_info, 0, sizeof (KMF_X509EXT_ACCESSDESC)); /* * Read the AccessMethod OID */ if (kmfber_scanf(asn1, "D", &access_info->AccessMethod) == KMFBER_DEFAULT) { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } /* * The OID of the AccessMethod determines what * sort of data comes next. */ if (IsEqualOid(&access_info->AccessMethod, (KMF_OID *)&KMFOID_PkixAdOcsp)) { if (kmfber_scanf(asn1, "tl", &tag, &size) == KMFBER_DEFAULT || size == 0) { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } /* * OCSP uri must be an IA5STRING or a GENNAME_URI * with an implicit tag. */ if (tag != BER_IA5STRING && tag != (0x80 | GENNAME_URI)) { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } if ((access_info->AccessLocation.Data = malloc(size)) == NULL) { ret = KMF_ERR_MEMORY; goto end; } if (kmfber_scanf(asn1, "s", access_info->AccessLocation.Data, &access_info->AccessLocation.Length) == KMFBER_DEFAULT) { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } } else if (IsEqualOid(&access_info->AccessMethod, (KMF_OID *)&KMFOID_PkixAdCaIssuers)) { /* will be supported later with PKIX */ free(access_info); access_info = NULL; continue; } else { ret = KMF_ERR_BAD_CERT_FORMAT; goto end; } aia->numberOfAccessDescription++; aia->AccessDesc = realloc(aia->AccessDesc, aia->numberOfAccessDescription * sizeof (KMF_X509EXT_ACCESSDESC)); if (aia->AccessDesc == NULL) { ret = KMF_ERR_MEMORY; goto end; } aia->AccessDesc[aia->numberOfAccessDescription-1] = *access_info; free(access_info); access_info = NULL; } end: kmf_free_extn(&extn); if (access_info != NULL) free(access_info); if (asn1 != NULL) kmfber_free(asn1, 1); return (ret); } /* * This function parses the name portion of a der-encoded distribution point * returns it in the KMF_CRL_DIST_POINT record. * * The "DistributionPointName" syntax is * * DistributionPointName ::= CHOICE { * fullName [0] GeneralNames, * nameRelativeToCRLIssuer [1] RelativeDistinguishedName } * * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GerneralName * * Note: for phase 1, we support fullName only. */ static KMF_RETURN parse_dp_name(char *dp_der_code, int dp_der_size, KMF_CRL_DIST_POINT *dp) { KMF_RETURN ret = KMF_OK; char *url = NULL; BerElement *asn1 = NULL; BerValue ber_data; ber_len_t size; char *end = NULL; int tag; KMF_GENERALNAMES *fullname; if (dp_der_code == NULL || dp_der_size == 0 || dp == NULL) return (KMF_ERR_BAD_PARAMETER); ber_data.bv_val = dp_der_code; ber_data.bv_len = dp_der_size; if ((asn1 = kmfder_init(&ber_data)) == NULL) return (KMF_ERR_BAD_CERT_FORMAT); tag = kmfber_first_element(asn1, &size, &end); if (tag != 0xA0 && tag != 0xA1) { ret = KMF_ERR_BAD_CERT_FORMAT; goto out; } if (tag == 0xA0) { /* fullName */ dp->type = DP_GENERAL_NAME; fullname = &(dp->name.full_name); fullname->number = 0; /* Skip over the explicit tag and size */ (void) kmfber_scanf(asn1, "T", &tag); tag = kmfber_next_element(asn1, &size, end); while (tag != KMFBER_DEFAULT && tag != KMFBER_END_OF_SEQORSET) { if (kmfber_scanf(asn1, "tl", &tag, &size) == KMFBER_DEFAULT || size == 0) { ret = KMF_ERR_BAD_CERT_FORMAT; goto out; } /* For phase 1, we are interested in a URI name only */ if (tag != (0x80 | GENNAME_URI)) { tag = kmfber_next_element(asn1, &size, end); continue; } if ((url = malloc(size)) == NULL) { ret = KMF_ERR_MEMORY; goto out; } /* Skip type and len, then read url and save it. */ if (kmfber_read(asn1, url, 2) != 2) { ret = KMF_ERR_BAD_CERT_FORMAT; goto out; } if (kmfber_read(asn1, url, size) != (ber_slen_t)size) { ret = KMF_ERR_BAD_CERT_FORMAT; goto out; } fullname->number++; fullname->namelist = realloc(fullname->namelist, fullname->number * sizeof (KMF_GENERALNAME)); if (fullname->namelist == NULL) { ret = KMF_ERR_MEMORY; goto out; } fullname->namelist[fullname->number - 1].choice = GENNAME_URI; fullname->namelist[fullname->number - 1].name.Length = size; fullname->namelist[fullname->number - 1].name.Data = (unsigned char *)url; /* next */ tag = kmfber_next_element(asn1, &size, end); } } else if (tag == 0xA1) { /* "nameRelativeToCRLIssuer" is not supported at phase 1. */ ret = KMF_ERR_BAD_CERT_FORMAT; goto out; } out: if (asn1 != NULL) kmfber_free(asn1, 1); if (ret != KMF_OK) { free_dp_name(dp); } if (ret == KMF_OK && fullname->number == 0) { ret = KMF_ERR_EXTENSION_NOT_FOUND; if (url != NULL) free(url); } return (ret); } /* * This function retrieves the CRL Distribution Points extension data from * a DER encoded certificate if it contains this extension, parses the * extension data, and returns it in the KMF_X509EXT_CRLDISTPOINTS record. */ KMF_RETURN kmf_get_cert_crl_dist_pts(const KMF_DATA *certdata, KMF_X509EXT_CRLDISTPOINTS *crl_dps) { KMF_RETURN ret = KMF_OK; KMF_X509_EXTENSION extn; BerElement *asn1 = NULL; BerValue exdata; ber_len_t size; char *end = NULL; int tag; KMF_CRL_DIST_POINT *dp = NULL; int i; if (certdata == NULL || crl_dps == NULL) { return (KMF_ERR_BAD_PARAMETER); } /* Get the ASN.1 data for this extension. */ (void) memset(&extn, 0, sizeof (KMF_X509_EXTENSION)); ret = kmf_get_cert_extn(certdata, (KMF_OID *)&KMFOID_CrlDistributionPoints, &extn); if (ret != KMF_OK) { return (ret); } /* * Decode the CRLDistributionPoints ASN.1 data. The Syntax for * CRLDistributionPoints is * * CRLDistributionPoints ::= * SEQUENCE SIZE (1..MAX) OF DistributionPoint * * DistributionPoint ::= SEQUENCE { * distributionPoint [0] DistributionPointName OPTIONAL, * reasons [1] ReasonFlags OPTIONAL, * cRLIssuer [2] GeneralNames OPTIONAL } */ exdata.bv_val = (char *)extn.BERvalue.Data; exdata.bv_len = extn.BERvalue.Length; if ((asn1 = kmfder_init(&exdata)) == NULL) { ret = KMF_ERR_MEMORY; goto out; } if ((tag = kmfber_first_element(asn1, &size, &end)) != BER_CONSTRUCTED_SEQUENCE) { ret = KMF_ERR_BAD_CERT_FORMAT; goto out; } (void) memset((void *)crl_dps, 0, sizeof (KMF_X509EXT_CRLDISTPOINTS)); while ((tag = kmfber_next_element(asn1, &size, end)) == BER_CONSTRUCTED_SEQUENCE) { boolean_t has_name = B_FALSE; boolean_t has_issuer = B_FALSE; /* Skip over the CONSTRUCTED SET tag */ if (kmfber_scanf(asn1, "T", &tag) == KMFBER_DEFAULT) { ret = KMF_ERR_BAD_CERT_FORMAT; goto out; } tag = kmfber_next_element(asn1, &size, end); if (tag != 0xA0 && tag != 0xA1 && tag != 0xA2) goto out; if ((dp = malloc(sizeof (KMF_CRL_DIST_POINT))) == NULL) { ret = KMF_ERR_MEMORY; goto out; } (void) memset((void *)dp, 0, sizeof (KMF_CRL_DIST_POINT)); if (tag == 0xA0) { /* distributionPoint Name */ char *name_der; int name_size = size + 2; if ((name_der = malloc(name_size)) == NULL) { ret = KMF_ERR_MEMORY; free(dp); dp = NULL; goto out; } if (kmfber_read(asn1, name_der, name_size) != (ber_slen_t)(name_size)) { ret = KMF_ERR_BAD_CERT_FORMAT; free(name_der); free(dp); dp = NULL; goto out; } has_name = B_TRUE; ret = parse_dp_name(name_der, name_size, dp); free(name_der); if (ret != KMF_OK) { free(dp); dp = NULL; goto out; } /* next field */ tag = kmfber_next_element(asn1, &size, end); } if (tag == 0XA1) { /* reasons */ char *bit_string; int len; if (kmfber_scanf(asn1, "B", &bit_string, &len) != BER_BIT_STRING) { ret = KMF_ERR_BAD_CERT_FORMAT; free(dp); dp = NULL; goto out; } dp->reasons.Length = len / 8; if ((dp->reasons.Data = malloc(dp->reasons.Length)) == NULL) { ret = KMF_ERR_MEMORY; free(dp); dp = NULL; goto out; } (void) memcpy(dp->reasons.Data, (uchar_t *)bit_string, dp->reasons.Length); /* next field */ tag = kmfber_next_element(asn1, &size, end); } if (tag == 0XA2) { /* cRLIssuer */ char *issuer_der = NULL; int issuer_size; /* For cRLIssuer, read the data only at phase 1 */ issuer_size = size + 2; issuer_der = malloc(issuer_size); if (issuer_der == NULL) { ret = KMF_ERR_MEMORY; free(dp); dp = NULL; goto out; } if (kmfber_read(asn1, issuer_der, issuer_size) != (ber_slen_t)(issuer_size)) { free(issuer_der); ret = KMF_ERR_BAD_CERT_FORMAT; free(dp); dp = NULL; goto out; } has_issuer = B_TRUE; free(issuer_der); } /* A distribution point cannot have a "reasons" field only. */ if (has_name == B_FALSE && has_issuer == B_FALSE) { ret = KMF_ERR_BAD_CERT_FORMAT; free_dp(dp); free(dp); dp = NULL; goto out; } /* * Although it is legal that a distributioon point contains * a cRLIssuer field only, with or without "reasons", we will * skip it if the name field is not presented for phase 1. */ if (has_name == B_FALSE) { free_dp(dp); } else { crl_dps->number++; crl_dps->dplist = realloc(crl_dps->dplist, crl_dps->number * sizeof (KMF_CRL_DIST_POINT)); if (crl_dps->dplist == NULL) { ret = KMF_ERR_MEMORY; free_dp(dp); free(dp); dp = NULL; goto out; } crl_dps->dplist[crl_dps->number - 1] = *dp; /* free the dp itself since we just used its contents */ } if (dp != NULL) { free(dp); dp = NULL; } } out: kmf_free_extn(&extn); if (asn1 != NULL) kmfber_free(asn1, 1); if (ret != KMF_OK) { for (i = 0; i < crl_dps->number; i++) free_dp(&(crl_dps->dplist[i])); free(crl_dps->dplist); } if (ret == KMF_OK && crl_dps->number == 0) { ret = KMF_ERR_BAD_CERT_FORMAT; } return (ret); } static KMF_RETURN KMF_CertGetPrintable(KMF_HANDLE_T handle, const KMF_DATA *SignedCert, KMF_PRINTABLE_ITEM flag, char *resultStr) { KMF_PLUGIN *plugin; KMF_RETURN (*getPrintableFn)(void *, const KMF_DATA *, KMF_PRINTABLE_ITEM, char *); KMF_RETURN ret; CLEAR_ERROR(handle, ret); if (ret != KMF_OK) return (ret); if (SignedCert == NULL || resultStr == NULL) { return (KMF_ERR_BAD_PARAMETER); } /* * This framework function is actually implemented in the openssl * plugin library, so we find the function address and call it. */ plugin = FindPlugin(handle, KMF_KEYSTORE_OPENSSL); if (plugin == NULL || plugin->dldesc == NULL) { return (KMF_ERR_PLUGIN_NOTFOUND); } getPrintableFn = (KMF_RETURN(*)())dlsym(plugin->dldesc, "OpenSSL_CertGetPrintable"); if (getPrintableFn == NULL) { return (KMF_ERR_FUNCTION_NOT_FOUND); } return (getPrintableFn(handle, SignedCert, flag, resultStr)); } KMF_RETURN kmf_get_cert_version_str(KMF_HANDLE_T handle, const KMF_DATA *SignedCert, char **result) { KMF_RETURN ret; char *tmpstr; CLEAR_ERROR(handle, ret); if (ret != KMF_OK) return (ret); if (SignedCert == NULL || result == NULL) return (KMF_ERR_BAD_PARAMETER); tmpstr = malloc(KMF_CERT_PRINTABLE_LEN); if (tmpstr == NULL) return (KMF_ERR_MEMORY); (void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN); ret = KMF_CertGetPrintable(handle, SignedCert, KMF_CERT_VERSION, tmpstr); if (ret == KMF_OK) { *result = tmpstr; } else { free(tmpstr); *result = NULL; } return (ret); } KMF_RETURN kmf_get_cert_subject_str(KMF_HANDLE_T handle, const KMF_DATA *SignedCert, char **result) { KMF_RETURN ret; char *tmpstr; CLEAR_ERROR(handle, ret); if (ret != KMF_OK) return (ret); if (SignedCert == NULL || result == NULL) return (KMF_ERR_BAD_PARAMETER); tmpstr = malloc(KMF_CERT_PRINTABLE_LEN); if (tmpstr == NULL) return (KMF_ERR_MEMORY); (void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN); ret = KMF_CertGetPrintable(handle, SignedCert, KMF_CERT_SUBJECT, tmpstr); if (ret == KMF_OK) { *result = tmpstr; } else { free(tmpstr); *result = NULL; } return (ret); } KMF_RETURN kmf_get_cert_issuer_str(KMF_HANDLE_T handle, const KMF_DATA *SignedCert, char **result) { KMF_RETURN ret; char *tmpstr; CLEAR_ERROR(handle, ret); if (ret != KMF_OK) return (ret); if (SignedCert == NULL || result == NULL) return (KMF_ERR_BAD_PARAMETER); tmpstr = malloc(KMF_CERT_PRINTABLE_LEN); if (tmpstr == NULL) return (KMF_ERR_MEMORY); (void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN); ret = KMF_CertGetPrintable(handle, SignedCert, KMF_CERT_ISSUER, tmpstr); if (ret == KMF_OK) { *result = tmpstr; } else { free(tmpstr); *result = NULL; } return (ret); } KMF_RETURN kmf_get_cert_serial_str(KMF_HANDLE_T handle, const KMF_DATA *SignedCert, char **result) { KMF_RETURN ret; char *tmpstr; CLEAR_ERROR(handle, ret); if (ret != KMF_OK) return (ret); if (SignedCert == NULL || result == NULL) return (KMF_ERR_BAD_PARAMETER); tmpstr = malloc(KMF_CERT_PRINTABLE_LEN); if (tmpstr == NULL) return (KMF_ERR_MEMORY); (void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN); ret = KMF_CertGetPrintable(handle, SignedCert, KMF_CERT_SERIALNUM, tmpstr); if (ret == KMF_OK) { *result = tmpstr; } else { free(tmpstr); *result = NULL; } return (ret); } KMF_RETURN kmf_get_cert_start_date_str(KMF_HANDLE_T handle, const KMF_DATA *SignedCert, char **result) { KMF_RETURN ret; char *tmpstr; CLEAR_ERROR(handle, ret); if (ret != KMF_OK) return (ret); if (SignedCert == NULL || result == NULL) return (KMF_ERR_BAD_PARAMETER); tmpstr = malloc(KMF_CERT_PRINTABLE_LEN); if (tmpstr == NULL) return (KMF_ERR_MEMORY); (void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN); ret = KMF_CertGetPrintable(handle, SignedCert, KMF_CERT_NOTBEFORE, tmpstr); if (ret == KMF_OK) { *result = tmpstr; } else { free(tmpstr); *result = NULL; } return (ret); } KMF_RETURN kmf_get_cert_end_date_str(KMF_HANDLE_T handle, const KMF_DATA *SignedCert, char **result) { KMF_RETURN ret; char *tmpstr; CLEAR_ERROR(handle, ret); if (ret != KMF_OK) return (ret); if (SignedCert == NULL || result == NULL) return (KMF_ERR_BAD_PARAMETER); tmpstr = malloc(KMF_CERT_PRINTABLE_LEN); if (tmpstr == NULL) return (KMF_ERR_MEMORY); (void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN); ret = KMF_CertGetPrintable(handle, SignedCert, KMF_CERT_NOTAFTER, tmpstr); if (ret == KMF_OK) { *result = tmpstr; } else { free(tmpstr); *result = NULL; } return (ret); } KMF_RETURN kmf_get_cert_pubkey_alg_str(KMF_HANDLE_T handle, const KMF_DATA *SignedCert, char **result) { KMF_RETURN ret; char *tmpstr; CLEAR_ERROR(handle, ret); if (ret != KMF_OK) return (ret); if (SignedCert == NULL || result == NULL) return (KMF_ERR_BAD_PARAMETER); tmpstr = malloc(KMF_CERT_PRINTABLE_LEN); if (tmpstr == NULL) return (KMF_ERR_MEMORY); (void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN); ret = KMF_CertGetPrintable(handle, SignedCert, KMF_CERT_PUBKEY_ALG, tmpstr); if (ret == KMF_OK) { *result = tmpstr; } else { free(tmpstr); *result = NULL; } return (ret); } KMF_RETURN kmf_get_cert_sig_alg_str(KMF_HANDLE_T handle, const KMF_DATA *SignedCert, char **result) { KMF_RETURN ret; char *tmpstr; CLEAR_ERROR(handle, ret); if (ret != KMF_OK) return (ret); if (SignedCert == NULL || result == NULL) return (KMF_ERR_BAD_PARAMETER); tmpstr = malloc(KMF_CERT_PRINTABLE_LEN); if (tmpstr == NULL) return (KMF_ERR_MEMORY); (void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN); ret = KMF_CertGetPrintable(handle, SignedCert, KMF_CERT_SIGNATURE_ALG, tmpstr); if (ret == KMF_OK) { *result = tmpstr; } else { free(tmpstr); *result = NULL; } return (ret); } KMF_RETURN kmf_get_cert_pubkey_str(KMF_HANDLE_T handle, const KMF_DATA *SignedCert, char **result) { KMF_RETURN ret; char *tmpstr; CLEAR_ERROR(handle, ret); if (ret != KMF_OK) return (ret); if (SignedCert == NULL || result == NULL) return (KMF_ERR_BAD_PARAMETER); tmpstr = malloc(KMF_CERT_PRINTABLE_LEN); if (tmpstr == NULL) return (KMF_ERR_MEMORY); (void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN); ret = KMF_CertGetPrintable(handle, SignedCert, KMF_CERT_PUBKEY_DATA, tmpstr); if (ret == KMF_OK) { *result = tmpstr; } else { free(tmpstr); *result = NULL; } return (ret); } KMF_RETURN kmf_get_cert_email_str(KMF_HANDLE_T handle, const KMF_DATA *SignedCert, char **result) { KMF_RETURN ret; char *tmpstr; CLEAR_ERROR(handle, ret); if (ret != KMF_OK) return (ret); if (SignedCert == NULL || result == NULL) return (KMF_ERR_BAD_PARAMETER); tmpstr = malloc(KMF_CERT_PRINTABLE_LEN); if (tmpstr == NULL) return (KMF_ERR_MEMORY); (void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN); ret = KMF_CertGetPrintable(handle, SignedCert, KMF_CERT_EMAIL, tmpstr); if (ret == KMF_OK) { *result = tmpstr; } else { free(tmpstr); *result = NULL; } return (ret); } /* * Given a certificate (DER Encoded data) and a KMF * extension identifier constant (e.g. KMF_X509_EXT_*), * return a human readable interpretation of the * extension data. * * The string will be a maximum of KMF_CERT_PRINTABLE_LEN * bytes long. The string is allocated locally and * must be freed by the caller. */ KMF_RETURN kmf_get_cert_extn_str(KMF_HANDLE_T handle, const KMF_DATA *cert, KMF_PRINTABLE_ITEM extension, char **result) { KMF_RETURN ret; char *tmpstr; CLEAR_ERROR(handle, ret); if (ret != KMF_OK) return (ret); if (cert == NULL || result == NULL) return (KMF_ERR_BAD_PARAMETER); tmpstr = malloc(KMF_CERT_PRINTABLE_LEN); if (tmpstr == NULL) return (KMF_ERR_MEMORY); (void) memset(tmpstr, 0, KMF_CERT_PRINTABLE_LEN); ret = KMF_CertGetPrintable(handle, cert, extension, tmpstr); if (ret == KMF_OK) { *result = tmpstr; } else { free(tmpstr); *result = NULL; } return (ret); } KMF_RETURN kmf_get_cert_id_data(const KMF_DATA *SignedCert, KMF_DATA *ID) { KMF_RETURN ret; KMF_X509_CERTIFICATE *cert = NULL; if (SignedCert == NULL || ID == NULL) return (KMF_ERR_BAD_PARAMETER); ret = DerDecodeSignedCertificate(SignedCert, &cert); if (ret != KMF_OK) return (ret); ret = GetIDFromSPKI(&cert->certificate.subjectPublicKeyInfo, ID); kmf_free_signed_cert(cert); free(cert); return (ret); } KMF_RETURN kmf_get_cert_id_str(const KMF_DATA *SignedCert, char **idstr) { KMF_RETURN ret; KMF_DATA ID = { 0, NULL }; char tmpstr[256]; int i; if (SignedCert == NULL || idstr == NULL) return (KMF_ERR_BAD_PARAMETER); ret = kmf_get_cert_id_data(SignedCert, &ID); if (ret != KMF_OK) { kmf_free_data(&ID); return (ret); } (void) memset(tmpstr, 0, sizeof (tmpstr)); for (i = 0; i < ID.Length; i++) { int len = strlen(tmpstr); (void) snprintf(&tmpstr[len], sizeof (tmpstr) - len, "%02x", (uchar_t)ID.Data[i]); if ((i+1) < ID.Length) (void) strcat(tmpstr, ":"); } *idstr = strdup(tmpstr); if ((*idstr) == NULL) ret = KMF_ERR_MEMORY; kmf_free_data(&ID); return (ret); } /* * This function gets the time_t values of the notbefore and notafter dates * from a der-encoded certificate. */ KMF_RETURN kmf_get_cert_validity(const KMF_DATA *cert, time_t *not_before, time_t *not_after) { KMF_RETURN rv = KMF_OK; KMF_X509_CERTIFICATE *certData = NULL; struct tm tm_tmp; time_t t_notbefore; time_t t_notafter; unsigned char *not_before_str; unsigned char *not_after_str; if (cert == NULL || not_before == NULL || not_after == NULL) return (KMF_ERR_BAD_PARAMETER); rv = DerDecodeSignedCertificate(cert, &certData); if (rv != KMF_OK) return (rv); /* Get notBefore */ not_before_str = certData->certificate.validity.notBefore.time.Data; if (strptime((const char *)not_before_str, "%y %m %d %H %M %S", &tm_tmp) == NULL) { rv = KMF_ERR_VALIDITY_PERIOD; goto out; } errno = 0; if (((t_notbefore = mktime(&tm_tmp)) == (time_t)(-1)) && errno == EOVERFLOW) { rv = KMF_ERR_VALIDITY_PERIOD; goto out; } *not_before = t_notbefore; /* Get notAfter */ not_after_str = certData->certificate.validity.notAfter.time.Data; if (strptime((const char *)not_after_str, "%y %m %d %H %M %S", &tm_tmp) == NULL) { rv = KMF_ERR_VALIDITY_PERIOD; goto out; } errno = 0; if (((t_notafter = mktime(&tm_tmp)) == (time_t)(-1)) && errno == EOVERFLOW) { rv = KMF_ERR_VALIDITY_PERIOD; goto out; } *not_after = t_notafter; out: if (certData != NULL) { kmf_free_signed_cert(certData); free(certData); } return (rv); } KMF_RETURN kmf_set_cert_pubkey(KMF_HANDLE_T handle, KMF_KEY_HANDLE *KMFKey, KMF_X509_CERTIFICATE *Cert) { KMF_RETURN ret = KMF_OK; KMF_X509_SPKI *spki_ptr; KMF_PLUGIN *plugin; KMF_DATA KeyData = { 0, NULL }; CLEAR_ERROR(handle, ret); if (ret != KMF_OK) return (ret); if (KMFKey == NULL || Cert == NULL) { return (KMF_ERR_BAD_PARAMETER); } /* The keystore must extract the pubkey data */ plugin = FindPlugin(handle, KMFKey->kstype); if (plugin != NULL && plugin->funclist->EncodePubkeyData != NULL) { ret = plugin->funclist->EncodePubkeyData(handle, KMFKey, &KeyData); } else { return (KMF_ERR_PLUGIN_NOTFOUND); } spki_ptr = &Cert->certificate.subjectPublicKeyInfo; if (KeyData.Data != NULL) { ret = DerDecodeSPKI(&KeyData, spki_ptr); free(KeyData.Data); } return (ret); } KMF_RETURN kmf_set_cert_subject(KMF_X509_CERTIFICATE *CertData, KMF_X509_NAME *subject_name_ptr) { KMF_RETURN rv = KMF_OK; KMF_X509_NAME *temp_name_ptr = NULL; if (CertData != NULL && subject_name_ptr != NULL) { rv = CopyRDN(subject_name_ptr, &temp_name_ptr); if (rv == KMF_OK) { CertData->certificate.subject = *temp_name_ptr; } } else { return (KMF_ERR_BAD_PARAMETER); } return (rv); } KMF_RETURN set_key_usage_extension(KMF_X509_EXTENSIONS *extns, int critical, uint32_t bits) { KMF_RETURN ret = KMF_OK; KMF_X509_EXTENSION extn; BerElement *asn1 = NULL; BerValue *extdata; int bitlen, i; uint16_t kubits = (uint16_t)(bits & 0x0000ffff); if (extns == NULL) return (KMF_ERR_BAD_PARAMETER); (void) memset(&extn, 0, sizeof (extn)); ret = copy_data(&extn.extnId, (KMF_OID *)&KMFOID_KeyUsage); if (ret != KMF_OK) return (ret); extn.critical = critical; extn.format = KMF_X509_DATAFORMAT_ENCODED; for (i = 7; i <= 15 && !(kubits & (1 << i)); i++) /* empty body */ ; bitlen = 16 - i; if ((asn1 = kmfder_alloc()) == NULL) return (KMF_ERR_MEMORY); kubits = htons(kubits); if (kmfber_printf(asn1, "B", (char *)&kubits, bitlen) == -1) { ret = KMF_ERR_ENCODING; goto out; } if (kmfber_flatten(asn1, &extdata) == -1) { ret = KMF_ERR_ENCODING; goto out; } extn.BERvalue.Data = (uchar_t *)extdata->bv_val; extn.BERvalue.Length = extdata->bv_len; free(extdata); ret = add_an_extension(extns, &extn); if (ret != KMF_OK) { free(extn.BERvalue.Data); } out: if (asn1 != NULL) kmfber_free(asn1, 1); return (ret); } KMF_RETURN kmf_set_cert_ku(KMF_X509_CERTIFICATE *CertData, int critical, uint16_t kubits) { KMF_RETURN ret = KMF_OK; if (CertData == NULL) return (KMF_ERR_BAD_PARAMETER); ret = set_key_usage_extension(&CertData->certificate.extensions, critical, kubits); return (ret); } KMF_RETURN kmf_set_cert_issuer(KMF_X509_CERTIFICATE *CertData, KMF_X509_NAME *issuer_name_ptr) { KMF_RETURN rv = KMF_OK; KMF_X509_NAME *temp_name_ptr = NULL; if (CertData != NULL && issuer_name_ptr != NULL) { rv = CopyRDN(issuer_name_ptr, &temp_name_ptr); if (rv == KMF_OK) { CertData->certificate.issuer = *temp_name_ptr; } } else { return (KMF_ERR_BAD_PARAMETER); } return (rv); } KMF_RETURN kmf_set_cert_sig_alg(KMF_X509_CERTIFICATE *CertData, KMF_ALGORITHM_INDEX sigAlg) { KMF_OID *alg; if (CertData == NULL) return (KMF_ERR_BAD_PARAMETER); alg = x509_algid_to_algoid(sigAlg); if (alg != NULL) { (void) copy_data((KMF_DATA *) &CertData->certificate.signature.algorithm, (KMF_DATA *)alg); (void) copy_data(&CertData->certificate.signature.parameters, &CertData->certificate.subjectPublicKeyInfo.algorithm. parameters); (void) copy_data( &CertData->signature.algorithmIdentifier.algorithm, &CertData->certificate.signature.algorithm); (void) copy_data( &CertData->signature.algorithmIdentifier.parameters, &CertData->certificate.signature.parameters); } else { return (KMF_ERR_BAD_PARAMETER); } return (KMF_OK); } KMF_RETURN kmf_set_cert_validity(KMF_X509_CERTIFICATE *CertData, time_t notBefore, uint32_t delta) { time_t clock; struct tm *gmt; char szNotBefore[256]; char szNotAfter[256]; if (CertData == NULL) return (KMF_ERR_BAD_PARAMETER); /* Set up validity fields */ if (notBefore == 0) clock = time(NULL); else clock = notBefore; gmt = gmtime(&clock); /* valid starting today */ /* Build the format in 2 parts so SCCS doesn't get confused */ (void) strftime(szNotBefore, sizeof (szNotBefore), "%y%m%d%H" "%M00Z", gmt); CertData->certificate.validity.notBefore.timeType = BER_UTCTIME; CertData->certificate.validity.notBefore.time.Length = strlen((char *)szNotBefore); CertData->certificate.validity.notBefore.time.Data = (uchar_t *)strdup(szNotBefore); clock += delta; gmt = gmtime(&clock); /* Build the format in 2 parts so SCCS doesn't get confused */ (void) strftime(szNotAfter, sizeof (szNotAfter), "%y%m%d%H" "%M00Z", gmt); CertData->certificate.validity.notAfter.timeType = BER_UTCTIME; CertData->certificate.validity.notAfter.time.Length = strlen((char *)szNotAfter); CertData->certificate.validity.notAfter.time.Data = (uchar_t *)strdup(szNotAfter); return (KMF_OK); } /* * Utility routine to set Integer values in the Certificate template * for things like serialNumber and Version. The data structure * expects pointers, not literal values, so we must allocate * and copy here. Don't use memory from the stack since this data * is freed later and that would be bad. */ KMF_RETURN set_integer(KMF_DATA *data, void *value, int length) { if (data == NULL || value == NULL) return (KMF_ERR_BAD_PARAMETER); data->Data = malloc(length); if (data->Data == NULL) return (KMF_ERR_MEMORY); data->Length = length; (void) memcpy((void *)data->Data, (const void *)value, length); return (KMF_OK); } static KMF_RETURN set_bigint(KMF_BIGINT *data, KMF_BIGINT *bigint) { if (data == NULL || bigint == NULL || bigint->len == 0) return (KMF_ERR_BAD_PARAMETER); data->val = malloc(bigint->len); if (data->val == NULL) return (KMF_ERR_MEMORY); data->len = bigint->len; (void) memcpy((void *)data->val, bigint->val, bigint->len); return (KMF_OK); } KMF_RETURN kmf_set_cert_serial(KMF_X509_CERTIFICATE *CertData, KMF_BIGINT *serno) { if (CertData == NULL || serno == NULL || serno->len == 0) return (KMF_ERR_BAD_PARAMETER); return (set_bigint(&CertData->certificate.serialNumber, serno)); } KMF_RETURN kmf_set_cert_version(KMF_X509_CERTIFICATE *CertData, uint32_t version) { if (CertData == NULL) return (KMF_ERR_BAD_PARAMETER); /* * From RFC 3280: * Version ::= INTEGER { v1(0), v2(1), v3(2) } */ if (version != 0 && version != 1 && version != 2) return (KMF_ERR_BAD_PARAMETER); return (set_integer(&CertData->certificate.version, (void *)&version, sizeof (uint32_t))); } KMF_RETURN kmf_set_cert_issuer_altname(KMF_X509_CERTIFICATE *CertData, int critical, KMF_GENERALNAMECHOICES nametype, char *namedata) { if (CertData == NULL || namedata == NULL) return (KMF_ERR_BAD_PARAMETER); return (kmf_set_altname(&CertData->certificate.extensions, (KMF_OID *)&KMFOID_IssuerAltName, critical, nametype, namedata)); } KMF_RETURN kmf_set_cert_subject_altname(KMF_X509_CERTIFICATE *CertData, int critical, KMF_GENERALNAMECHOICES nametype, char *namedata) { if (CertData == NULL || namedata == NULL) return (KMF_ERR_BAD_PARAMETER); return (kmf_set_altname(&CertData->certificate.extensions, (KMF_OID *)&KMFOID_SubjectAltName, critical, nametype, namedata)); } KMF_RETURN kmf_add_cert_eku(KMF_X509_CERTIFICATE *CertData, KMF_OID *ekuOID, int critical) { KMF_RETURN ret = KMF_OK; KMF_X509_EXTENSION *foundextn; KMF_X509_EXTENSION newextn; BerElement *asn1 = NULL; BerValue *extdata = NULL; char *olddata = NULL; size_t oldsize = 0; KMF_X509EXT_EKU ekudata; if (CertData == NULL || ekuOID == NULL) return (KMF_ERR_BAD_PARAMETER); (void) memset(&ekudata, 0, sizeof (KMF_X509EXT_EKU)); (void) memset(&newextn, 0, sizeof (newextn)); foundextn = FindExtn(&CertData->certificate.extensions, (KMF_OID *)&KMFOID_ExtendedKeyUsage); if (foundextn != NULL) { ret = GetSequenceContents((char *)foundextn->BERvalue.Data, foundextn->BERvalue.Length, &olddata, &oldsize); if (ret != KMF_OK) goto out; /* * If the EKU is already in the cert, then just return OK. */ ret = parse_eku_data(&foundextn->BERvalue, &ekudata); if (ret == KMF_OK) { if (is_eku_present(&ekudata, ekuOID)) { goto out; } } } if ((asn1 = kmfder_alloc()) == NULL) return (KMF_ERR_MEMORY); if (kmfber_printf(asn1, "{") == -1) { ret = KMF_ERR_ENCODING; goto out; } /* Write the old extension data first */ if (olddata != NULL && oldsize > 0) { if (kmfber_write(asn1, olddata, oldsize, 0) == -1) { ret = KMF_ERR_ENCODING; goto out; } } /* Append this EKU OID and close the sequence */ if (kmfber_printf(asn1, "D}", ekuOID) == -1) { ret = KMF_ERR_ENCODING; goto out; } if (kmfber_flatten(asn1, &extdata) == -1) { ret = KMF_ERR_ENCODING; goto out; } /* * If we are just adding to an existing list of EKU OIDs, * just replace the BER data associated with the found extension. */ if (foundextn != NULL) { free(foundextn->BERvalue.Data); foundextn->critical = critical; foundextn->BERvalue.Data = (uchar_t *)extdata->bv_val; foundextn->BERvalue.Length = extdata->bv_len; } else { ret = copy_data(&newextn.extnId, (KMF_DATA *)&KMFOID_ExtendedKeyUsage); if (ret != KMF_OK) goto out; newextn.critical = critical; newextn.format = KMF_X509_DATAFORMAT_ENCODED; newextn.BERvalue.Data = (uchar_t *)extdata->bv_val; newextn.BERvalue.Length = extdata->bv_len; ret = kmf_set_cert_extn(CertData, &newextn); if (ret != KMF_OK) free(newextn.BERvalue.Data); } out: kmf_free_eku(&ekudata); if (extdata != NULL) free(extdata); if (olddata != NULL) free(olddata); if (asn1 != NULL) kmfber_free(asn1, 1); if (ret != KMF_OK) kmf_free_data(&newextn.extnId); return (ret); } KMF_RETURN kmf_set_cert_extn(KMF_X509_CERTIFICATE *CertData, KMF_X509_EXTENSION *extn) { KMF_RETURN ret = KMF_OK; KMF_X509_EXTENSIONS *exts; if (CertData == NULL || extn == NULL) return (KMF_ERR_BAD_PARAMETER); exts = &CertData->certificate.extensions; ret = add_an_extension(exts, extn); return (ret); } KMF_RETURN kmf_set_cert_basic_constraint(KMF_X509_CERTIFICATE *CertData, KMF_BOOL critical, KMF_X509EXT_BASICCONSTRAINTS *constraint) { KMF_RETURN ret = KMF_OK; KMF_X509_EXTENSION extn; BerElement *asn1 = NULL; BerValue *extdata; if ((CertData == NULL) || (constraint == NULL)) return (KMF_ERR_BAD_PARAMETER); (void) memset(&extn, 0, sizeof (extn)); ret = copy_data(&extn.extnId, (KMF_OID *)&KMFOID_BasicConstraints); if (ret != KMF_OK) return (ret); extn.critical = critical; extn.format = KMF_X509_DATAFORMAT_ENCODED; if ((asn1 = kmfder_alloc()) == NULL) return (KMF_ERR_MEMORY); if (kmfber_printf(asn1, "{") == -1) { ret = KMF_ERR_ENCODING; goto out; } if (kmfber_printf(asn1, "b", constraint->cA) == -1) { ret = KMF_ERR_ENCODING; goto out; } if (constraint->pathLenConstraintPresent) { /* Write the pathLenConstraint value */ if (kmfber_printf(asn1, "i", constraint->pathLenConstraint) == -1) { ret = KMF_ERR_ENCODING; goto out; } } if (kmfber_printf(asn1, "}") == -1) { ret = KMF_ERR_ENCODING; goto out; } if (kmfber_flatten(asn1, &extdata) == -1) { ret = KMF_ERR_ENCODING; goto out; } extn.BERvalue.Data = (uchar_t *)extdata->bv_val; extn.BERvalue.Length = extdata->bv_len; free(extdata); ret = kmf_set_cert_extn(CertData, &extn); if (ret != KMF_OK) { free(extn.BERvalue.Data); } out: if (asn1 != NULL) kmfber_free(asn1, 1); return (ret); } /* * Phase 1 APIs still needed to maintain compat with elfsign. */ KMF_RETURN KMF_GetCertSubjectNameString(KMF_HANDLE_T handle, const KMF_DATA *SignedCert, char **result) { return (kmf_get_cert_subject_str(handle, SignedCert, result)); } KMF_RETURN KMF_GetCertIssuerNameString(KMF_HANDLE_T handle, const KMF_DATA *SignedCert, char **result) { return (kmf_get_cert_issuer_str(handle, SignedCert, result)); } KMF_RETURN KMF_GetCertIDString(const KMF_DATA *SignedCert, char **idstr) { return (kmf_get_cert_id_str(SignedCert, idstr)); }