certop.c revision 6b35cb3cf158584a9408d44b9b6796564e8e1882
/*
* 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 <stdio.h>
#include <link.h>
#include <fcntl.h>
#include <ctype.h>
#include <sys/param.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <ber_der.h>
#include <kmfapiP.h>
#include <pem_encode.h>
#include <libgen.h>
#include <cryptoutil.h>
#define CERTFILE_TEMPNAME "/tmp/user.certXXXXXX"
#define CRLFILE_TEMPNAME "/tmp/crlXXXXXX"
#define X509_FORMAT_VERSION 2
static KMF_RETURN
sign_cert(KMF_HANDLE_T, const KMF_DATA *, KMF_KEY_HANDLE *,
KMF_OID *, KMF_DATA *);
static KMF_RETURN
verify_cert_with_key(KMF_HANDLE_T, KMF_DATA *, const KMF_DATA *);
static KMF_RETURN
verify_cert_with_cert(KMF_HANDLE_T, const KMF_DATA *, const KMF_DATA *);
static KMF_RETURN
get_keyalg_from_cert(KMF_DATA *cert, KMF_KEY_ALG *keyalg)
{
KMF_RETURN rv;
KMF_X509_CERTIFICATE *SignerCert = NULL;
KMF_ALGORITHM_INDEX AlgorithmId;
rv = DerDecodeSignedCertificate(cert, &SignerCert);
if (rv != KMF_OK)
return (rv);
/* Get the algorithm info from the signer certificate */
AlgorithmId = x509_algoid_to_algid(
&SignerCert->signature.algorithmIdentifier.algorithm);
switch (AlgorithmId) {
case KMF_ALGID_MD5WithRSA:
case KMF_ALGID_SHA1WithRSA:
case KMF_ALGID_SHA256WithRSA:
case KMF_ALGID_SHA384WithRSA:
case KMF_ALGID_SHA512WithRSA:
*keyalg = KMF_RSA;
break;
case KMF_ALGID_SHA1WithDSA:
case KMF_ALGID_SHA256WithDSA:
*keyalg = KMF_DSA;
break;
case KMF_ALGID_SHA1WithECDSA:
case KMF_ALGID_SHA256WithECDSA:
case KMF_ALGID_SHA384WithECDSA:
case KMF_ALGID_SHA512WithECDSA:
case KMF_ALGID_ECDSA:
*keyalg = KMF_ECDSA;
break;
default:
rv = KMF_ERR_BAD_ALGORITHM;
}
kmf_free_signed_cert(SignerCert);
free(SignerCert);
return (rv);
}
/*
* Name: kmf_find_prikey_by_cert
*
* Description:
* This function finds the corresponding private key in keystore
* for a certificate
*/
KMF_RETURN
kmf_find_prikey_by_cert(KMF_HANDLE_T handle, int numattr,
KMF_ATTRIBUTE *attrlist)
{
KMF_PLUGIN *plugin;
KMF_RETURN ret = KMF_OK;
KMF_KEYSTORE_TYPE kstype;
KMF_KEY_ALG keyalg;
KMF_KEY_HANDLE *key = NULL;
KMF_DATA *cert = NULL;
KMF_ATTRIBUTE_TESTER required_attrs[] = {
{KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
{KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)},
{KMF_KEY_HANDLE_ATTR, TRUE, sizeof (KMF_KEY_HANDLE),
sizeof (KMF_KEY_HANDLE)}
};
int num_req_attrs = sizeof (required_attrs) /
sizeof (KMF_ATTRIBUTE_TESTER);
if (handle == NULL)
return (KMF_ERR_BAD_PARAMETER);
CLEAR_ERROR(handle, ret);
ret = test_attributes(num_req_attrs, required_attrs,
0, NULL, numattr, attrlist);
if (ret != KMF_OK)
return (ret);
/*
* First, get the key algorithm info from the certificate and saves it
* in the returned key handle.
*/
cert = kmf_get_attr_ptr(KMF_CERT_DATA_ATTR, attrlist, numattr);
if (cert == NULL)
return (KMF_ERR_BAD_PARAMETER);
ret = get_keyalg_from_cert(cert, &keyalg);
if (ret != KMF_OK)
return (ret);
key = kmf_get_attr_ptr(KMF_KEY_HANDLE_ATTR, attrlist, numattr);
if (key == NULL)
return (KMF_ERR_BAD_PARAMETER);
key->keyalg = keyalg;
/* Call the plugin to do the work. */
ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr,
&kstype, NULL);
if (ret != KMF_OK)
return (ret);
plugin = FindPlugin(handle, kstype);
if (plugin == NULL || plugin->funclist->FindPrikeyByCert == NULL)
return (KMF_ERR_PLUGIN_NOTFOUND);
return (plugin->funclist->FindPrikeyByCert(handle, numattr, attrlist));
}
KMF_RETURN
check_key_usage(void *handle,
const KMF_DATA *cert,
const KMF_KU_PURPOSE purpose)
{
KMF_X509EXT_BASICCONSTRAINTS constraint;
KMF_BOOL critical = B_FALSE;
KMF_X509EXT_KEY_USAGE keyusage;
KMF_RETURN ret = KMF_OK;
if (handle == NULL || cert == NULL)
return (KMF_ERR_BAD_PARAMETER);
(void) memset(&constraint, 0, sizeof (KMF_X509EXT_BASICCONSTRAINTS));
(void) memset(&keyusage, 0, sizeof (KMF_X509EXT_KEY_USAGE));
ret = kmf_get_cert_ku(cert, &keyusage);
if (ret != KMF_OK)
/*
* If absent or error, the cert is assumed to be invalid
* for all key usage checking.
*/
return (ret);
switch (purpose) {
case KMF_KU_SIGN_CERT:
/*
* RFC 3280:
* The keyCertSign bit is asserted when the subject
* public key is used for verifying a signature on
* public key certificates. If the keyCertSign bit
* is asserted, then the cA bit in the basic constraints
* extension (section 4.2.1.10) MUST also be asserted.
* The basic constraints extension MUST appear as a
* critical extension in all CA certificates that
* contain public keys used to validate digital
* signatures on certificates.
*/
if (keyusage.KeyUsageBits & KMF_keyCertSign) {
ret = kmf_get_cert_basic_constraint(cert,
&critical, &constraint);
if (ret != KMF_OK)
return (ret);
if ((!critical) || (!constraint.cA))
return (KMF_ERR_KEYUSAGE);
} else {
return (KMF_ERR_KEYUSAGE);
}
break;
case KMF_KU_SIGN_DATA:
/*
* RFC 3280:
* The digitalSignature bit is asserted when the subject
* public key is used with a digital signature mechanism
* to support security services other than certificate
* signing(bit 5), or CRL signing(bit 6).
*/
if (!(keyusage.KeyUsageBits & KMF_digitalSignature))
return (KMF_ERR_KEYUSAGE);
break;
case KMF_KU_ENCRYPT_DATA:
/*
* RFC 3280:
* The dataEncipherment bit is asserted when the subject
* public key is used for enciphering user data, other than
* cryptographic keys.
*/
if (!(keyusage.KeyUsageBits & KMF_dataEncipherment))
return (KMF_ERR_KEYUSAGE);
break;
default:
return (KMF_ERR_BAD_PARAMETER);
}
return (KMF_OK);
}
KMF_RETURN
kmf_find_cert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist)
{
KMF_PLUGIN *plugin;
KMF_RETURN ret = KMF_OK;
KMF_KEYSTORE_TYPE kstype;
KMF_ATTRIBUTE_TESTER required_attrs[] = {
{KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
{KMF_COUNT_ATTR, FALSE, sizeof (uint32_t), sizeof (uint32_t)}
};
int num_req_attrs = sizeof (required_attrs) /
sizeof (KMF_ATTRIBUTE_TESTER);
if (handle == NULL)
return (KMF_ERR_BAD_PARAMETER);
CLEAR_ERROR(handle, ret);
ret = test_attributes(num_req_attrs, required_attrs,
0, NULL, numattr, attrlist);
if (ret != KMF_OK)
return (ret);
ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr,
&kstype, NULL);
if (ret != KMF_OK)
return (ret);
plugin = FindPlugin(handle, kstype);
if (plugin == NULL || plugin->funclist->FindCert == NULL)
return (KMF_ERR_PLUGIN_NOTFOUND);
return (plugin->funclist->FindCert(handle, numattr, attrlist));
}
#define NODATA(d) (d.Data == NULL || d.Length == NULL)
KMF_RETURN
kmf_encode_cert_record(KMF_X509_CERTIFICATE *CertData, KMF_DATA *encodedCert)
{
KMF_RETURN ret;
KMF_X509_TBS_CERT *tbs_cert;
if (CertData == NULL || encodedCert == NULL)
return (KMF_ERR_BAD_PARAMETER);
/*
* Validate that all required fields are present.
*/
tbs_cert = &(CertData->certificate);
if (NODATA(tbs_cert->version) ||
NODATA(tbs_cert->signature.algorithm) ||
NODATA(tbs_cert->subjectPublicKeyInfo.subjectPublicKey) ||
tbs_cert->serialNumber.val == NULL ||
tbs_cert->serialNumber.len == 0 ||
tbs_cert->subject.numberOfRDNs == 0 ||
tbs_cert->issuer.numberOfRDNs == 0) {
return (KMF_ERR_INCOMPLETE_TBS_CERT);
}
encodedCert->Length = 0;
encodedCert->Data = NULL;
/* Pack the new certificate */
ret = DerEncodeSignedCertificate(CertData, encodedCert);
return (ret);
}
/*
* This function is used to setup the attribute list before calling
* kmf_find_prikey_by_cert(). This function is used by
* kmf_decrypt_with_cert
* kmf_sign_cert
* kmf_sign_data
*
* The attribute list in these callers contain all the attributes
* needed by kmf_find_prikey_by_cert(), except the
* KMF_KEY_HANDLE attribute and the KMF_CERT_DATA_ATTR attribute.
* These 2 attributes need to be added or reset.
*
* The caller should free the new_attrlist after use it.
*/
static KMF_RETURN
setup_findprikey_attrlist(KMF_ATTRIBUTE *src_attrlist, int src_num,
KMF_ATTRIBUTE **new_attrlist, int *new_num, KMF_KEY_HANDLE *key,
KMF_DATA *cert)
{
KMF_ATTRIBUTE *attrlist = NULL;
int cur_num = src_num;
int index;
int i;
if (src_attrlist == NULL || new_num == NULL || key == NULL ||
cert == NULL)
return (KMF_ERR_BAD_PARAMETER);
/* Create a new attribute list with 2 more elements */
attrlist = (KMF_ATTRIBUTE *) malloc(
(src_num + 2) * sizeof (KMF_ATTRIBUTE));
if (attrlist == NULL)
return (KMF_ERR_MEMORY);
/* Copy the src_attrlist to the new list */
for (i = 0; i < src_num; i++) {
attrlist[i].type = src_attrlist[i].type;
attrlist[i].pValue = src_attrlist[i].pValue;
attrlist[i].valueLen = src_attrlist[i].valueLen;
}
/* Add or reset the key handle attribute */
index = kmf_find_attr(KMF_KEY_HANDLE_ATTR, attrlist, cur_num);
if (index == -1) {
/* not found; add it */
kmf_set_attr_at_index(attrlist, cur_num,
KMF_KEY_HANDLE_ATTR, key, sizeof (KMF_KEY_HANDLE));
cur_num++;
} else {
/* found; just reset it */
kmf_set_attr_at_index(attrlist, index,
KMF_KEY_HANDLE_ATTR, key, sizeof (KMF_KEY_HANDLE));
}
/* add or reset the cert data attribute */
index = kmf_find_attr(KMF_CERT_DATA_ATTR, attrlist, cur_num);
if (index == -1) {
/* not found; add it */
kmf_set_attr_at_index(attrlist, cur_num,
KMF_CERT_DATA_ATTR, cert, sizeof (KMF_DATA));
cur_num++;
} else {
/* found; just reset it */
kmf_set_attr_at_index(attrlist, index,
KMF_CERT_DATA_ATTR, cert, sizeof (KMF_DATA));
}
*new_attrlist = attrlist;
*new_num = cur_num;
return (KMF_OK);
}
/*
* Determine a default signature type to use based on
* the key algorithm.
*/
static KMF_OID *
get_default_signoid(KMF_KEY_HANDLE *key)
{
KMF_OID *oid;
switch (key->keyalg) {
case KMF_RSA:
oid = (KMF_OID *)&KMFOID_SHA256WithRSA;
break;
case KMF_DSA:
/* NSS doesnt support DSA-SHA2 hashes yet */
if (key->kstype == KMF_KEYSTORE_NSS)
oid = (KMF_OID *)&KMFOID_X9CM_DSAWithSHA1;
else
oid = (KMF_OID *)&KMFOID_SHA256WithDSA;
break;
case KMF_ECDSA:
oid = (KMF_OID *)&KMFOID_SHA256WithECDSA;
break;
default:
oid = NULL;
break;
}
return (oid);
}
/*
* This is to check to see if a certificate being signed has
* the keyCertSign KeyUsage bit set, and if so, make sure the
* "BasicConstraints" extension is also set accordingly.
*/
static KMF_RETURN
check_for_basic_constraint(KMF_DATA *cert)
{
KMF_RETURN rv = KMF_OK;
KMF_X509EXT_KEY_USAGE keyUsage;
KMF_X509_CERTIFICATE *x509cert = NULL;
rv = kmf_get_cert_ku((const KMF_DATA *)cert, &keyUsage);
if (rv == KMF_OK) {
KMF_X509EXT_BASICCONSTRAINTS basicConstraint;
KMF_BOOL critical;
/* If keyCertSign is set, look for basicConstraints */
if (keyUsage.KeyUsageBits & KMF_keyCertSign)
rv = kmf_get_cert_basic_constraint(
(const KMF_DATA *)cert,
&critical, &basicConstraint);
/*
* If we got KMF_OK (or an error), then return
* because the extension is already present. We
* only want to continue with this function if
* the extension is NOT found.
*/
if (rv != KMF_ERR_EXTENSION_NOT_FOUND)
return (rv);
/*
* Don't limit the pathLen (for now).
* This should probably be a policy setting in the
* future.
*/
basicConstraint.cA = TRUE;
basicConstraint.pathLenConstraintPresent = FALSE;
/*
* Decode the DER cert data into the internal
* X.509 structure we need to set extensions.
*/
rv = DerDecodeSignedCertificate(cert, &x509cert);
if (rv != KMF_OK)
return (rv);
/*
* Add the missing basic constraint.
*/
rv = kmf_set_cert_basic_constraint(x509cert,
TRUE, &basicConstraint);
if (rv != KMF_OK) {
kmf_free_signed_cert(x509cert);
free(x509cert);
return (rv);
}
/* Free the old cert data record */
kmf_free_data(cert);
/* Re-encode the cert with the extension */
rv = kmf_encode_cert_record(x509cert, cert);
/* cleanup */
kmf_free_signed_cert(x509cert);
free(x509cert);
}
if (rv == KMF_ERR_EXTENSION_NOT_FOUND)
rv = KMF_OK;
return (rv);
}
/*
* Name: kmf_sign_cert
*
* Description:
* This function signs a certificate using the signer cert and
* returns a signed and DER-encoded certificate.
*
* The following types of certificate data can be submitted to be signed:
* KMF_TBS_CERT_DATA_ATTR - a KMF_DATA ptr is provided in the attrlist
* and is signed directly.
* KMF_X509_CERTIFICATE_ATTR - a KMF_X509_CERTIFICATE record is provided
* in the attribute list. This is converted to raw KMF_DATA
* prior to signing.
*
* The key for the signing operation can be provided as a KMF_KEY_HANDLE_ATTR
* or the caller may choose to provide a KMF_SIGNER_CERT_ATTR (KMF_DATA *).
* If the latter, this function will then attempt to find the private key
* associated with the certificate. The private key must be stored in
* the same keystore as the signer certificate.
*/
KMF_RETURN
kmf_sign_cert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist)
{
KMF_RETURN ret;
int new_numattr = numattr + 1;
KMF_ATTRIBUTE *new_attrlist = NULL;
KMF_DATA *signer_cert = NULL;
KMF_DATA *tbs_cert = NULL; /* to be signed cert */
KMF_DATA *signed_cert = NULL;
KMF_DATA unsignedCert = { 0, NULL };
KMF_KEY_HANDLE sign_key, *sign_key_ptr;
int freethekey = 0;
KMF_POLICY_RECORD *policy;
KMF_OID *oid = NULL;
KMF_X509_CERTIFICATE *x509cert;
KMF_X509_TBS_CERT *decodedTbsCert = NULL;
KMF_ATTRIBUTE_TESTER required_attrs[] = {
{KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
{KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)}
};
int num_req_attrs = sizeof (required_attrs) /
sizeof (KMF_ATTRIBUTE_TESTER);
if (handle == NULL)
return (KMF_ERR_BAD_PARAMETER);
CLEAR_ERROR(handle, ret);
ret = test_attributes(num_req_attrs, required_attrs,
0, NULL, numattr, attrlist);
if (ret != KMF_OK)
return (ret);
/* Get the signer cert and check its keyUsage */
signer_cert = kmf_get_attr_ptr(KMF_SIGNER_CERT_DATA_ATTR, attrlist,
numattr);
sign_key_ptr = kmf_get_attr_ptr(KMF_KEY_HANDLE_ATTR, attrlist,
numattr);
/*
* Only accept 1 or the other, not both.
*/
if (signer_cert == NULL && sign_key_ptr == NULL)
return (KMF_ERR_BAD_PARAMETER);
if (signer_cert != NULL && sign_key_ptr != NULL)
return (KMF_ERR_BAD_PARAMETER);
oid = kmf_get_attr_ptr(KMF_OID_ATTR, attrlist, numattr);
if (oid == NULL) {
/*
* If the signature OID was not given, check
* for an algorithm index identifier instead.
*/
KMF_ALGORITHM_INDEX AlgId;
ret = kmf_get_attr(KMF_ALGORITHM_INDEX_ATTR, attrlist, numattr,
&AlgId, NULL);
if (ret == KMF_OK)
oid = x509_algid_to_algoid(AlgId);
}
if (signer_cert != NULL) {
policy = handle->policy;
ret = check_key_usage(handle, signer_cert, KMF_KU_SIGN_CERT);
if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0)
ret = KMF_OK;
if (ret != KMF_OK)
return (ret);
/*
* Find the private key from the signer certificate by calling
* kmf_find_prikey_by_cert().
*/
ret = setup_findprikey_attrlist(attrlist, numattr,
&new_attrlist, &new_numattr, &sign_key, signer_cert);
if (ret != KMF_OK)
goto out;
ret = kmf_find_prikey_by_cert(handle, new_numattr,
new_attrlist);
if (ret != KMF_OK) {
goto out;
}
sign_key_ptr = &sign_key;
freethekey = 1;
}
tbs_cert = kmf_get_attr_ptr(KMF_TBS_CERT_DATA_ATTR, attrlist,
numattr);
if (tbs_cert == NULL) {
x509cert = kmf_get_attr_ptr(KMF_X509_CERTIFICATE_ATTR, attrlist,
numattr);
if (x509cert == NULL) {
ret = KMF_ERR_BAD_PARAMETER;
goto out;
}
ret = kmf_encode_cert_record(x509cert, &unsignedCert);
if (ret != KMF_OK)
goto out;
tbs_cert = &unsignedCert;
}
/*
* Check for the keyCertSign bit in the KeyUsage extn. If it is set,
* then the basicConstraints must also be present and be
* marked critical.
*/
ret = check_for_basic_constraint(tbs_cert);
if (ret)
goto out;
if (oid == NULL) {
/*
* If OID is not known yet, use a default value
* based on the signers key type.
*/
oid = get_default_signoid(sign_key_ptr);
}
signed_cert = kmf_get_attr_ptr(KMF_CERT_DATA_ATTR, attrlist,
numattr);
if (signed_cert == NULL) {
ret = KMF_ERR_BAD_PARAMETER;
goto out;
}
ret = sign_cert(handle, tbs_cert, sign_key_ptr, oid, signed_cert);
out:
if (new_attrlist)
(void) free(new_attrlist);
/* If we had to find the key, free it here. */
if (freethekey)
kmf_free_kmf_key(handle, &sign_key);
kmf_free_data(&unsignedCert);
if (decodedTbsCert != NULL) {
kmf_free_tbs_cert(decodedTbsCert);
free(decodedTbsCert);
}
return (ret);
}
/*
* Name: kmf_sign_data
*
* Description:
* This function signs a block of data using the signer cert and
* returns the the signature in output
*/
KMF_RETURN
kmf_sign_data(KMF_HANDLE_T handle, int numattr,
KMF_ATTRIBUTE *attrlist)
{
KMF_PLUGIN *plugin;
KMF_RETURN ret = KMF_OK;
KMF_ATTRIBUTE *new_attrlist = NULL;
int new_numattr = numattr;
KMF_DATA *signer_cert = NULL;
KMF_DATA *tbs_data = NULL; /* to be signed data */
KMF_DATA *output = NULL;
KMF_KEY_HANDLE sign_key, *sign_key_ptr;
KMF_ALGORITHM_INDEX AlgId = KMF_ALGID_NONE;
KMF_DATA signature = { 0, NULL };
KMF_OID *oid;
KMF_POLICY_RECORD *policy;
KMF_ATTRIBUTE_TESTER required_attrs[] = {
{KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
{KMF_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)},
{KMF_OUT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)}
};
int num_req_attrs = sizeof (required_attrs) /
sizeof (KMF_ATTRIBUTE_TESTER);
if (handle == NULL)
return (KMF_ERR_BAD_PARAMETER);
CLEAR_ERROR(handle, ret);
ret = test_attributes(num_req_attrs, required_attrs,
0, NULL, numattr, attrlist);
if (ret != KMF_OK)
return (ret);
/* Get the signer cert and check its keyUsage. */
signer_cert = kmf_get_attr_ptr(KMF_SIGNER_CERT_DATA_ATTR, attrlist,
numattr);
sign_key_ptr = kmf_get_attr_ptr(KMF_KEY_HANDLE_ATTR, attrlist,
numattr);
if (signer_cert == NULL && sign_key_ptr == NULL)
return (KMF_ERR_BAD_PARAMETER);
/*
* If a signer cert was given, use it to find the private key
* to use for signing the data.
*/
if (signer_cert != NULL) {
ret = check_key_usage(handle, signer_cert, KMF_KU_SIGN_DATA);
/*
* Signing generic data does not require the
* KeyUsage extension.
*/
policy = handle->policy;
if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0)
ret = KMF_OK;
if (ret != KMF_OK)
return (ret);
/*
* Find the private key from the signer certificate.
*/
ret = setup_findprikey_attrlist(attrlist, numattr,
&new_attrlist, &new_numattr, &sign_key, signer_cert);
if (ret != KMF_OK) {
goto cleanup;
}
ret = kmf_find_prikey_by_cert(handle, new_numattr,
new_attrlist);
if (ret != KMF_OK) {
goto cleanup;
}
sign_key_ptr = &sign_key;
}
/* Get the tbs_data and signed_data attributes now */
tbs_data = kmf_get_attr_ptr(KMF_DATA_ATTR, attrlist, numattr);
if (tbs_data == NULL) {
ret = KMF_ERR_BAD_PARAMETER;
goto cleanup;
}
output = kmf_get_attr_ptr(KMF_OUT_DATA_ATTR, attrlist, numattr);
if (output == NULL) {
ret = KMF_ERR_BAD_PARAMETER;
goto cleanup;
}
/*
* Get the algorithm index attribute and its oid. If this attribute
* is not provided, then we use a default value.
*/
oid = kmf_get_attr_ptr(KMF_OID_ATTR, attrlist, numattr);
if (oid == NULL) {
ret = kmf_get_attr(KMF_ALGORITHM_INDEX_ATTR, attrlist,
numattr, &AlgId, NULL);
/* If there was no Algorithm ID, use default based on key */
if (ret != KMF_OK)
oid = get_default_signoid(sign_key_ptr);
else
oid = x509_algid_to_algoid(AlgId);
}
if (sign_key_ptr->keyp == NULL) {
ret = KMF_ERR_BAD_PARAMETER;
goto cleanup;
}
/* Now call the plugin function to sign it */
plugin = FindPlugin(handle, sign_key_ptr->kstype);
if (plugin == NULL || plugin->funclist->SignData == NULL) {
ret = KMF_ERR_PLUGIN_NOTFOUND;
goto cleanup;
}
ret = plugin->funclist->SignData(handle, sign_key_ptr, oid, tbs_data,
output);
if (ret != KMF_OK)
goto cleanup;
/*
* For DSA, NSS returns an encoded signature. Decode the
* signature and expect a 40-byte DSA signature.
*/
if (plugin->type == KMF_KEYSTORE_NSS &&
(IsEqualOid(oid, (KMF_OID *)&KMFOID_X9CM_DSAWithSHA1) ||
IsEqualOid(oid, (KMF_OID *)&KMFOID_SHA256WithDSA))) {
ret = DerDecodeDSASignature(output, &signature);
if (ret != KMF_OK)
goto cleanup;
output->Length = signature.Length;
(void) memcpy(output->Data, signature.Data, signature.Length);
}
cleanup:
if (new_attrlist != NULL)
free(new_attrlist);
if (signature.Data)
free(signature.Data);
if (signer_cert != NULL && sign_key_ptr != NULL)
kmf_free_kmf_key(handle, sign_key_ptr);
return (ret);
}
/*
* kmf_verify_data
*
* This routine will try to verify a block of data using
* either a public key or a certificate as the source
* of the verification (the key).
*
* The caller may provider either a KMF_KEY_HANDLE_ATTR or
* a KMF_SIGNER_CERT_DATA_ATTR (with a KMF_DATA record) to
* use for the key to the verification step. If a certificate
* is used and that certificate has the KeyUsage extension,
* the SIGN-DATA bit must be set. Also, if a certificate
* is used, the verification will be done in a specific
* keystore mechanism.
*
* If a KMF_KEY_HANDLE is given in the attribute list, the
* verification will occur in the framework itself using
* PKCS#11 C_Verify functions.
*/
KMF_RETURN
kmf_verify_data(KMF_HANDLE_T handle,
int num_args,
KMF_ATTRIBUTE *attrlist)
{
KMF_RETURN ret = KMF_OK;
KMF_PLUGIN *plugin;
KMF_KEYSTORE_TYPE kstype;
uint32_t len;
KMF_DATA derkey = { 0, NULL };
KMF_KEY_HANDLE *KMFKey;
KMF_ALGORITHM_INDEX sigAlg = KMF_ALGID_NONE;
KMF_DATA *indata;
KMF_DATA *insig;
KMF_DATA *signer_cert;
KMF_X509_SPKI spki;
KMF_POLICY_RECORD *policy;
KMF_ATTRIBUTE_TESTER required_attrs[] = {
{KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
{KMF_DATA_ATTR, FALSE, sizeof (KMF_DATA),
sizeof (KMF_DATA)},
{KMF_IN_SIGN_ATTR, FALSE, sizeof (KMF_DATA),
sizeof (KMF_DATA)}
};
int num_req_attrs = sizeof (required_attrs) /
sizeof (KMF_ATTRIBUTE_TESTER);
if (handle == NULL)
return (KMF_ERR_BAD_PARAMETER);
CLEAR_ERROR(handle, ret);
ret = test_attributes(num_req_attrs, required_attrs,
0, NULL, num_args, attrlist);
if (ret != KMF_OK)
return (ret);
len = sizeof (kstype);
ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, num_args,
&kstype, &len);
if (ret != KMF_OK)
return (ret);
KMFKey = kmf_get_attr_ptr(KMF_KEY_HANDLE_ATTR, attrlist, num_args);
signer_cert = kmf_get_attr_ptr(KMF_SIGNER_CERT_DATA_ATTR, attrlist,
num_args);
if (KMFKey == NULL && signer_cert == NULL) {
return (KMF_ERR_BAD_PARAMETER);
}
len = sizeof (sigAlg);
ret = kmf_get_attr(KMF_ALGORITHM_INDEX_ATTR, attrlist, num_args,
&sigAlg, &len);
/* We only need the algorithm index if we don't have a signer cert. */
if (ret != KMF_OK && signer_cert == NULL)
return (ret);
indata = kmf_get_attr_ptr(KMF_DATA_ATTR, attrlist, num_args);
if (indata == NULL)
return (KMF_ERR_BAD_PARAMETER);
insig = kmf_get_attr_ptr(KMF_IN_SIGN_ATTR, attrlist, num_args);
if (insig == NULL)
return (KMF_ERR_BAD_PARAMETER);
/* If the caller passed a signer cert instead of a key use it. */
if (signer_cert != NULL) {
KMF_X509_CERTIFICATE *SignerCert = NULL;
policy = handle->policy;
ret = check_key_usage(handle, signer_cert, KMF_KU_SIGN_DATA);
if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0)
ret = KMF_OK;
if (ret != KMF_OK)
return (ret);
/* Decode the signer cert so we can get the SPKI data */
ret = DerDecodeSignedCertificate(signer_cert, &SignerCert);
if (ret != KMF_OK)
return (ret);
/* If no algorithm specified, use the certs signature alg */
if (sigAlg == KMF_ALGID_NONE)
sigAlg = x509_algoid_to_algid(CERT_ALG_OID(SignerCert));
if (sigAlg == KMF_ALGID_NONE) {
kmf_free_signed_cert(SignerCert);
free(SignerCert);
return (KMF_ERR_BAD_ALGORITHM);
}
/*
* Verify the data locally (i.e. using PKCS#11).
* The verify operation uses a public key and does not
* require access to a specific keystore. Save time
* (and code) by just using the frameworks implementation
* of the verify operation using crypto framework
* APIs.
*/
ret = PKCS_VerifyData(handle, sigAlg,
&SignerCert->certificate.subjectPublicKeyInfo,
indata, insig);
kmf_free_signed_cert(SignerCert);
free(SignerCert);
} else {
/* Retrieve public key data from keystore */
plugin = FindPlugin(handle, kstype);
if (plugin != NULL &&
plugin->funclist->EncodePubkeyData != NULL) {
ret = plugin->funclist->EncodePubkeyData(handle,
KMFKey, &derkey);
} else {
return (KMF_ERR_PLUGIN_NOTFOUND);
}
ret = DerDecodeSPKI(&derkey, &spki);
if (ret == KMF_OK)
ret = PKCS_VerifyData(handle, sigAlg, &spki,
indata, insig);
if (derkey.Data != NULL)
free(derkey.Data);
kmf_free_algoid(&spki.algorithm);
kmf_free_data(&spki.subjectPublicKey);
}
return (ret);
}
/*
* Name: kmf_verify_cert
*
* Description:
* This function verifies that the a certificate was signed
* using a specific private key and that the certificate has not
* been altered since it was signed using that private key
* The public key used for verification may be given in the
* attribute list as a KMF_KEY_HANDLE or the caller may give
* just the signing certificate (as KMF_SIGNER_CERT_DATA_ATTR)
* from which the public key needed for verification can be
* derived.
*
* Parameters:
* handle(input) - opaque handle for KMF session
* numattr - number of attributes in the list
* attrlist - KMF_ATTRIBUTES
*
* Returns:
* A KMF_RETURN value indicating success or specifying a particular
* error condition. The value KMF_OK indicates success. All other
* values represent an error condition.
*/
KMF_RETURN
kmf_verify_cert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist)
{
KMF_RETURN ret;
KMF_DATA derkey = { 0, NULL };
KMF_PLUGIN *plugin;
KMF_KEY_HANDLE *KMFKey;
KMF_DATA *CertToBeVerified;
KMF_DATA *SignerCert;
KMF_ATTRIBUTE_TESTER required_attrs[] = {
{KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)}
};
int num_req_attrs = sizeof (required_attrs) /
sizeof (KMF_ATTRIBUTE_TESTER);
CLEAR_ERROR(handle, ret);
if (ret != KMF_OK)
return (ret);
ret = test_attributes(num_req_attrs, required_attrs,
0, NULL, numattr, attrlist);
if (ret != KMF_OK)
return (ret);
KMFKey = kmf_get_attr_ptr(KMF_KEY_HANDLE_ATTR, attrlist, numattr);
SignerCert = kmf_get_attr_ptr(KMF_SIGNER_CERT_DATA_ATTR, attrlist,
numattr);
/*
* Caller must provide at least a key handle or a cert to use
* as the "key" for verification.
*/
if (KMFKey == NULL && SignerCert == NULL)
return (KMF_ERR_BAD_PARAMETER);
CertToBeVerified = kmf_get_attr_ptr(KMF_CERT_DATA_ATTR, attrlist,
numattr);
if (CertToBeVerified == NULL)
return (KMF_ERR_BAD_PARAMETER);
if (SignerCert != NULL) {
ret = verify_cert_with_cert(handle, CertToBeVerified,
SignerCert);
} else {
/*
* The keystore must extract the pubkey data because
* the framework doesn't have access to the raw key bytes
* that are needed to construct the DER encoded public
* key information needed for the verify operation.
*/
plugin = FindPlugin(handle, KMFKey->kstype);
if (plugin != NULL && plugin->funclist->EncodePubkeyData !=
NULL) {
ret = plugin->funclist->EncodePubkeyData(handle,
KMFKey, &derkey);
} else {
return (KMF_ERR_PLUGIN_NOTFOUND);
}
if (ret == KMF_OK && derkey.Length > 0) {
ret = verify_cert_with_key(handle, &derkey,
CertToBeVerified);
if (derkey.Data != NULL)
free(derkey.Data);
}
}
return (ret);
}
/*
* Name: kmf_encrypt
*
* Description:
* Uses the public key from the cert to encrypt the plaintext
* into the ciphertext.
*
* Parameters:
* handle(input) - opaque handle for KMF session
* cert(input) - pointer to a DER encoded certificate for encryption
* by using its public key
* plaintext(input) - pointer to the plaintext to be encrypted
* ciphertext(output) - pointer to the ciphertext contains
* encrypted data
*
* Returns:
* A KMF_RETURN value indicating success or specifying a particular
* error condition.
* The value KMF_OK indicates success. All other values represent
* an error condition.
*
*/
KMF_RETURN
kmf_encrypt(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist)
{
KMF_RETURN ret;
KMF_X509_CERTIFICATE *x509cert = NULL;
KMF_X509_SPKI *pubkey;
KMF_OID *alg;
KMF_ALGORITHM_INDEX algid;
KMF_DATA *cert;
KMF_DATA *plaintext;
KMF_DATA *ciphertext;
KMF_POLICY_RECORD *policy;
KMF_ATTRIBUTE_TESTER required_attrs[] = {
{KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA),
sizeof (KMF_DATA)},
{KMF_PLAINTEXT_DATA_ATTR, FALSE, sizeof (KMF_DATA),
sizeof (KMF_DATA)},
{KMF_CIPHERTEXT_DATA_ATTR, FALSE, sizeof (KMF_DATA),
sizeof (KMF_DATA)}
};
int num_req_attrs = sizeof (required_attrs) /
sizeof (KMF_ATTRIBUTE_TESTER);
CLEAR_ERROR(handle, ret);
if (ret != KMF_OK)
return (ret);
ret = test_attributes(num_req_attrs, required_attrs,
0, NULL, numattr, attrlist);
if (ret != KMF_OK)
return (ret);
cert = kmf_get_attr_ptr(KMF_CERT_DATA_ATTR, attrlist,
numattr);
plaintext = kmf_get_attr_ptr(KMF_PLAINTEXT_DATA_ATTR, attrlist,
numattr);
ciphertext = kmf_get_attr_ptr(KMF_CIPHERTEXT_DATA_ATTR, attrlist,
numattr);
if (cert == NULL || plaintext == NULL || ciphertext == NULL)
return (KMF_ERR_BAD_PARAMETER);
/* check the keyUsage of the certificate */
policy = handle->policy;
ret = check_key_usage(handle, cert, KMF_KU_ENCRYPT_DATA);
if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0)
ret = KMF_OK;
if (ret != KMF_OK)
return (ret);
/* Decode the cert so we can get the SPKI data */
if ((ret = DerDecodeSignedCertificate(cert, &x509cert)) != KMF_OK)
return (ret);
/* Get the public key info from the certificate */
pubkey = &x509cert->certificate.subjectPublicKeyInfo;
/* Use the algorithm in SPKI to encrypt data */
alg = &pubkey->algorithm.algorithm;
algid = x509_algoid_to_algid(alg);
/* [EC]DSA does not support encrypt */
if (algid == KMF_ALGID_DSA ||
algid == KMF_ALGID_SHA1WithDSA ||
algid == KMF_ALGID_SHA256WithDSA ||
algid == KMF_ALGID_SHA1WithECDSA ||
algid == KMF_ALGID_SHA256WithECDSA ||
algid == KMF_ALGID_SHA384WithECDSA ||
algid == KMF_ALGID_SHA512WithECDSA ||
algid == KMF_ALGID_NONE) {
kmf_free_signed_cert(x509cert);
free(x509cert);
return (KMF_ERR_BAD_ALGORITHM);
}
/*
* Encrypt using the crypto framework (not the KMF plugin mechanism).
*/
ret = PKCS_EncryptData(handle, algid, pubkey, plaintext, ciphertext);
kmf_free_signed_cert(x509cert);
free(x509cert);
return (ret);
}
/*
* Name: kmf_decrypt
*
* Description:
* Uses the private key associated with the cert to decrypt
* the ciphertext into the plaintext.
*/
KMF_RETURN
kmf_decrypt(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist)
{
KMF_RETURN ret;
KMF_X509_CERTIFICATE *x509cert = NULL;
KMF_X509_SPKI *spki_ptr;
KMF_PLUGIN *plugin;
KMF_ALGORITHM_INDEX AlgorithmId;
KMF_ATTRIBUTE *new_attrlist = NULL;
int new_numattr;
KMF_DATA *cert = NULL;
KMF_DATA *ciphertext = NULL;
KMF_DATA *plaintext = NULL;
KMF_KEY_HANDLE prikey;
KMF_POLICY_RECORD *policy;
KMF_ATTRIBUTE_TESTER required_attrs[] = {
{KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
{KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)},
{KMF_PLAINTEXT_DATA_ATTR, FALSE, sizeof (KMF_DATA),
sizeof (KMF_DATA)},
{KMF_CIPHERTEXT_DATA_ATTR, FALSE, sizeof (KMF_DATA),
sizeof (KMF_DATA)},
};
int num_req_attrs = sizeof (required_attrs) /
sizeof (KMF_ATTRIBUTE_TESTER);
if (handle == NULL)
return (KMF_ERR_BAD_PARAMETER);
CLEAR_ERROR(handle, ret);
ret = test_attributes(num_req_attrs, required_attrs,
0, NULL, numattr, attrlist);
if (ret != KMF_OK)
return (ret);
/* Get the cert and check its keyUsage */
cert = kmf_get_attr_ptr(KMF_CERT_DATA_ATTR, attrlist,
numattr);
if (cert == NULL)
return (KMF_ERR_BAD_PARAMETER);
/* check the keyUsage of the certificate */
policy = handle->policy;
ret = check_key_usage(handle, cert, KMF_KU_ENCRYPT_DATA);
if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0)
ret = KMF_OK;
if (ret != KMF_OK)
return (ret);
/* Get the ciphertext and plaintext attributes */
ciphertext = kmf_get_attr_ptr(KMF_CIPHERTEXT_DATA_ATTR, attrlist,
numattr);
if (ciphertext == NULL)
return (KMF_ERR_BAD_PARAMETER);
plaintext = kmf_get_attr_ptr(KMF_PLAINTEXT_DATA_ATTR, attrlist,
numattr);
if (plaintext == NULL)
return (KMF_ERR_BAD_PARAMETER);
/*
* Retrieve the private key from the keystore based on
* the certificate.
*/
ret = setup_findprikey_attrlist(attrlist, numattr, &new_attrlist,
&new_numattr, &prikey, cert);
if (ret != KMF_OK)
goto cleanup;
ret = kmf_find_prikey_by_cert(handle, new_numattr, new_attrlist);
if (ret != KMF_OK)
goto cleanup;
/* Decode the cert so we can get the alogorithm */
ret = DerDecodeSignedCertificate(cert, &x509cert);
if (ret != KMF_OK)
goto cleanup;
spki_ptr = &x509cert->certificate.subjectPublicKeyInfo;
AlgorithmId = x509_algoid_to_algid((KMF_OID *)
&spki_ptr->algorithm.algorithm);
/* [EC]DSA does not support decrypt */
if (AlgorithmId == KMF_ALGID_DSA ||
AlgorithmId == KMF_ALGID_ECDSA) {
ret = KMF_ERR_BAD_ALGORITHM;
goto cleanup;
}
plugin = FindPlugin(handle, prikey.kstype);
if (plugin != NULL && plugin->funclist->DecryptData != NULL) {
ret = plugin->funclist->DecryptData(handle,
&prikey, &spki_ptr->algorithm.algorithm,
ciphertext, plaintext);
} else {
ret = KMF_ERR_PLUGIN_NOTFOUND;
}
cleanup:
if (new_attrlist != NULL)
free(new_attrlist);
kmf_free_kmf_key(handle, &prikey);
kmf_free_signed_cert(x509cert);
free(x509cert);
return (ret);
}
KMF_RETURN
kmf_store_cert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist)
{
KMF_PLUGIN *plugin;
KMF_RETURN ret = KMF_OK;
KMF_KEYSTORE_TYPE kstype;
KMF_ATTRIBUTE_TESTER required_attrs[] = {
{KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
{KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)},
};
int num_req_attrs = sizeof (required_attrs) /
sizeof (KMF_ATTRIBUTE_TESTER);
if (handle == NULL)
return (KMF_ERR_BAD_PARAMETER);
CLEAR_ERROR(handle, ret);
ret = test_attributes(num_req_attrs, required_attrs,
0, NULL, numattr, attrlist);
if (ret != KMF_OK)
return (ret);
ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr,
&kstype, NULL);
if (ret != KMF_OK)
return (ret);
plugin = FindPlugin(handle, kstype);
if (plugin == NULL || plugin->funclist->StoreCert == NULL)
return (KMF_ERR_PLUGIN_NOTFOUND);
return (plugin->funclist->StoreCert(handle, numattr, attrlist));
}
KMF_RETURN
kmf_import_cert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist)
{
KMF_PLUGIN *plugin;
KMF_RETURN ret = KMF_OK;
KMF_KEYSTORE_TYPE kstype;
KMF_ATTRIBUTE_TESTER required_attrs[] = {
{KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
{KMF_CERT_FILENAME_ATTR, TRUE, 1, 0},
};
int num_req_attrs = sizeof (required_attrs) /
sizeof (KMF_ATTRIBUTE_TESTER);
if (handle == NULL)
return (KMF_ERR_BAD_PARAMETER);
CLEAR_ERROR(handle, ret);
ret = test_attributes(num_req_attrs, required_attrs, 0, NULL,
numattr, attrlist);
if (ret != KMF_OK)
return (ret);
ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr,
&kstype, NULL);
if (ret != KMF_OK)
return (ret);
plugin = FindPlugin(handle, kstype);
if (plugin == NULL || plugin->funclist->ImportCert == NULL)
return (KMF_ERR_PLUGIN_NOTFOUND);
return (plugin->funclist->ImportCert(handle, numattr, attrlist));
}
KMF_RETURN
kmf_delete_cert_from_keystore(KMF_HANDLE_T handle, int numattr,
KMF_ATTRIBUTE *attrlist)
{
KMF_PLUGIN *plugin;
KMF_RETURN ret = KMF_OK;
KMF_KEYSTORE_TYPE kstype;
KMF_ATTRIBUTE_TESTER required_attrs[] = {
{KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)}
};
int num_req_attrs = sizeof (required_attrs) /
sizeof (KMF_ATTRIBUTE_TESTER);
if (handle == NULL)
return (KMF_ERR_BAD_PARAMETER);
CLEAR_ERROR(handle, ret);
ret = test_attributes(num_req_attrs, required_attrs,
0, NULL, numattr, attrlist);
if (ret != KMF_OK)
return (ret);
ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr,
&kstype, NULL);
if (ret != KMF_OK)
return (ret);
plugin = FindPlugin(handle, kstype);
if (plugin == NULL || plugin->funclist->DeleteCert == NULL)
return (KMF_ERR_PLUGIN_NOTFOUND);
return (plugin->funclist->DeleteCert(handle, numattr, attrlist));
}
/*
* This function gets the CRL URI entries from the certificate's Distribution
* points extension, and downloads the CRL file. The function also returns
* the URI string and the format of the CRL file. The caller should free
* the space allocated for the returned URI string.
*/
static KMF_RETURN
cert_get_crl(KMF_HANDLE_T handle, const KMF_DATA *cert, char *proxy,
char *filename, char **retn_uri, KMF_ENCODE_FORMAT *format)
{
KMF_RETURN ret = KMF_OK;
KMF_X509EXT_CRLDISTPOINTS crl_dps;
boolean_t done = B_FALSE;
char uri[1024];
char *proxyname = NULL;
char *proxy_port_s = NULL;
int proxy_port = 0;
int i, j;
char *path = NULL;
if (handle == NULL || cert == NULL || filename == NULL ||
retn_uri == NULL || format == NULL)
return (KMF_ERR_BAD_PARAMETER);
/* Get the proxy info */
if (proxy != NULL) {
proxyname = strtok(proxy, ":");
proxy_port_s = strtok(NULL, "\0");
if (proxy_port_s != NULL) {
proxy_port = strtol(proxy_port_s, NULL, 0);
} else {
proxy_port = 8080; /* default */
}
}
/*
* Get the CRL URI from the certificate's CRL Distribution
* Points extension and download the CRL file. There maybe more than
* one CRL URI entries in the DP extension, so we will continue
* the process until a CRL file is sucessfully downloaded or we
* are running out the CRL URI's.
*/
ret = kmf_get_cert_crl_dist_pts((const KMF_DATA *)cert,
&crl_dps);
if (ret != KMF_OK)
goto out;
for (i = 0; i < crl_dps.number; i++) {
KMF_CRL_DIST_POINT *dp = &(crl_dps.dplist[i]);
KMF_GENERALNAMES *fullname = &(dp->name.full_name);
KMF_DATA *data;
if (done)
break;
for (j = 0; j < fullname->number; j++) {
data = &(fullname->namelist[j].name);
(void) memcpy(uri, data->Data, data->Length);
uri[data->Length] = '\0';
ret = kmf_download_crl(handle, uri, proxyname,
proxy_port, 30, filename, format);
if (ret == KMF_OK) {
done = B_TRUE;
path = malloc(data->Length + 1);
if (path == NULL) {
ret = KMF_ERR_MEMORY;
goto out;
}
(void) strncpy(path, uri, data->Length);
*retn_uri = path;
break;
}
}
}
out:
kmf_free_crl_dist_pts(&crl_dps);
return (ret);
}
static KMF_RETURN
check_crl_validity(KMF_HANDLE_T handle, KMF_KEYSTORE_TYPE kstype,
char *crlfilename, KMF_DATA *issuer_cert)
{
KMF_RETURN ret = KMF_OK;
KMF_POLICY_RECORD *policy;
if (handle == NULL)
return (KMF_ERR_BAD_PARAMETER);
policy = handle->policy;
/*
* NSS CRL is not file based, and its signature
* has been verified during CRL import.
* We only check CRL validity for file-based CRLs,
* NSS handles these checks internally.
*/
if (kstype == KMF_KEYSTORE_NSS)
return (KMF_OK);
/*
* Check the CRL signature if needed.
*/
if (!policy->validation_info.crl_info.ignore_crl_sign) {
ret = kmf_verify_crl_file(handle, crlfilename,
issuer_cert);
if (ret != KMF_OK)
return (ret);
}
/*
* Check the CRL validity if needed.
*/
if (!policy->validation_info.crl_info.ignore_crl_date) {
ret = kmf_check_crl_date(handle, crlfilename);
if (ret != KMF_OK)
return (ret);
}
return (ret);
}
static KMF_RETURN
cert_crl_check(KMF_HANDLE_T handle, KMF_KEYSTORE_TYPE *kstype,
KMF_DATA *user_cert, KMF_DATA *issuer_cert)
{
KMF_POLICY_RECORD *policy;
KMF_RETURN ret = KMF_OK;
KMF_ATTRIBUTE attrlist[16];
int numattr = 0;
int fd;
boolean_t crlchk;
char user_certfile[MAXPATHLEN];
char crlfile_tmp[MAXPATHLEN];
char *basefilename = NULL;
char *dir = NULL;
char *crlfilename = NULL;
char *proxy = NULL;
char *uri = NULL;
KMF_ENCODE_FORMAT format;
if (handle == NULL || kstype == NULL || user_cert == NULL ||
issuer_cert == NULL)
return (KMF_ERR_BAD_PARAMETER);
if (!is_valid_keystore_type(*kstype))
return (KMF_ERR_BAD_PARAMETER);
policy = handle->policy;
/*
* If the get-crl-uri policy is TRUE, then download the CRL
* file first. The newly downloaded file will be stored in the
* NSS internal database for NSS keystore, and stored in a file for
* the File-based CRL plugins (OpenSSL and PKCS11).
*
* For file-based plugins, if the get-crl-uri policy is FALSE,
* then the caller should provide a CRL file in the policy.
* Also, after this step is done, the "crlfilename" variable should
* contain the proper CRL file to be used for the rest of CRL
* validation process.
*/
basefilename = policy->validation_info.crl_info.basefilename;
dir = policy->validation_info.crl_info.directory;
if (policy->validation_info.crl_info.get_crl_uri) {
/*
* Check to see if we already have this CRL.
*/
if (basefilename == NULL)
basefilename = basename(uri);
crlfilename = get_fullpath(dir == NULL ? "./" : dir,
basefilename);
if (crlfilename == NULL) {
ret = KMF_ERR_BAD_CRLFILE;
goto cleanup;
}
/*
* If this file already exists and is valid, we don't need to
* download a new one.
*/
if ((fd = open(crlfilename, O_RDONLY)) != -1) {
(void) close(fd);
if ((ret = check_crl_validity(handle, *kstype,
crlfilename, issuer_cert)) == KMF_OK) {
goto checkcrl;
}
}
/*
* Create a temporary file to hold the new CRL file initially.
*/
(void) strlcpy(crlfile_tmp, CRLFILE_TEMPNAME,
sizeof (crlfile_tmp));
if (mkstemp(crlfile_tmp) == -1) {
ret = KMF_ERR_INTERNAL;
goto cleanup;
}
/*
* Get the URI entry from the certificate's CRL distribution
* points extension and download the CRL file.
*/
proxy = policy->validation_info.crl_info.proxy;
ret = cert_get_crl(handle, user_cert, proxy, crlfile_tmp,
&uri, &format);
if (ret != KMF_OK) {
(void) unlink(crlfile_tmp);
goto cleanup;
}
/*
* If we just downloaded one, make sure it is OK.
*/
if ((ret = check_crl_validity(handle, *kstype, crlfile_tmp,
issuer_cert)) != KMF_OK)
return (ret);
/* Cache the CRL file. */
if (*kstype == KMF_KEYSTORE_NSS) {
/*
* For NSS keystore, import this CRL file into th
* internal database.
*/
numattr = 0;
kmf_set_attr_at_index(attrlist, numattr,
KMF_KEYSTORE_TYPE_ATTR, kstype, sizeof (kstype));
numattr++;
kmf_set_attr_at_index(attrlist, numattr,
KMF_CRL_FILENAME_ATTR, crlfile_tmp,
strlen(crlfile_tmp));
numattr++;
crlchk = B_FALSE;
kmf_set_attr_at_index(attrlist, numattr,
KMF_CRL_CHECK_ATTR, &crlchk, sizeof (boolean_t));
numattr++;
ret = kmf_import_crl(handle, numattr, attrlist);
(void) unlink(crlfile_tmp);
if (ret != KMF_OK)
goto cleanup;
} else {
if (rename(crlfile_tmp, crlfilename) == -1) {
(void) unlink(crlfile_tmp);
ret = KMF_ERR_WRITE_FILE;
goto cleanup;
}
}
} else {
/*
* If the get_crl_uri policy is FALSE, for File-based CRL
* plugins, get the input CRL file from the policy.
*/
if (*kstype != KMF_KEYSTORE_NSS) {
if (basefilename == NULL) {
ret = KMF_ERR_BAD_PARAMETER;
goto cleanup;
}
crlfilename = get_fullpath(dir == NULL ? "./" : dir,
basefilename);
if (crlfilename == NULL) {
ret = KMF_ERR_BAD_CRLFILE;
goto cleanup;
}
/*
* Make sure this CRL is still valid.
*/
if ((ret = check_crl_validity(handle, *kstype,
crlfilename, issuer_cert)) != KMF_OK)
return (ret);
}
}
checkcrl:
/*
* Check the CRL revocation for the certificate.
*/
numattr = 0;
kmf_set_attr_at_index(attrlist, numattr, KMF_KEYSTORE_TYPE_ATTR,
kstype, sizeof (kstype));
numattr++;
switch (*kstype) {
case KMF_KEYSTORE_NSS:
kmf_set_attr_at_index(attrlist, numattr,
KMF_CERT_DATA_ATTR, user_cert, sizeof (KMF_DATA));
numattr++;
break;
case KMF_KEYSTORE_PK11TOKEN:
case KMF_KEYSTORE_OPENSSL:
/*
* Create temporary file to hold the user certificate.
*/
(void) strlcpy(user_certfile, CERTFILE_TEMPNAME,
sizeof (user_certfile));
if (mkstemp(user_certfile) == -1) {
ret = KMF_ERR_INTERNAL;
goto cleanup;
}
ret = kmf_create_cert_file(user_cert, KMF_FORMAT_ASN1,
user_certfile);
if (ret != KMF_OK) {
goto cleanup;
}
kmf_set_attr_at_index(attrlist, numattr,
KMF_CERT_FILENAME_ATTR,
user_certfile, strlen(user_certfile));
numattr++;
kmf_set_attr_at_index(attrlist, numattr,
KMF_CRL_FILENAME_ATTR,
crlfilename, strlen(crlfilename));
numattr++;
break;
default:
ret = KMF_ERR_PLUGIN_NOTFOUND;
goto cleanup;
}
ret = kmf_find_cert_in_crl(handle, numattr, attrlist);
if (ret == KMF_ERR_NOT_REVOKED) {
ret = KMF_OK;
}
cleanup:
(void) unlink(user_certfile);
if (crlfilename != NULL)
free(crlfilename);
if (uri != NULL)
free(uri);
return (ret);
}
static KMF_RETURN
cert_ocsp_check(KMF_HANDLE_T handle, KMF_KEYSTORE_TYPE *kstype,
KMF_DATA *user_cert, KMF_DATA *issuer_cert, KMF_DATA *response,
char *slotlabel, char *dirpath)
{
KMF_RETURN ret = KMF_OK;
KMF_POLICY_RECORD *policy;
KMF_DATA *new_response = NULL;
boolean_t ignore_response_sign = B_FALSE;
uint32_t ltime = 0;
KMF_DATA *signer_cert = NULL;
KMF_BIGINT sernum = { NULL, 0 };
int response_status;
int reason;
int cert_status;
KMF_ATTRIBUTE attrlist[32];
int numattr;
if (handle == NULL || kstype == NULL || user_cert == NULL ||
issuer_cert == NULL)
return (KMF_ERR_BAD_PARAMETER);
policy = handle->policy;
/*
* Get the response lifetime from policy.
*/
if (policy->VAL_OCSP_BASIC.response_lifetime != NULL &&
(str2lifetime(policy->VAL_OCSP_BASIC.response_lifetime, &ltime)
< 0))
return (KMF_ERR_OCSP_RESPONSE_LIFETIME);
/*
* Get the ignore_response_sign policy.
*
* If ignore_response_sign is FALSE, we need to verify the response.
* Find the OCSP Responder certificate if it is specified in the OCSP
* policy.
*/
ignore_response_sign = policy->VAL_OCSP_BASIC.ignore_response_sign;
if (ignore_response_sign == B_FALSE &&
policy->VAL_OCSP.has_resp_cert == B_TRUE) {
char *signer_name;
KMF_X509_DER_CERT signer_retrcert;
uchar_t *bytes = NULL;
size_t bytelen;
uint32_t num = 0;
KMF_ATTRIBUTE fc_attrlist[16];
int fc_numattr = 0;
char *dir = "./";
if (policy->VAL_OCSP_RESP_CERT.name == NULL ||
policy->VAL_OCSP_RESP_CERT.serial == NULL)
return (KMF_ERR_POLICY_NOT_FOUND);
signer_cert = malloc(sizeof (KMF_DATA));
if (signer_cert == NULL) {
ret = KMF_ERR_MEMORY;
goto out;
}
(void) memset(signer_cert, 0, sizeof (KMF_DATA));
signer_name = policy->VAL_OCSP_RESP_CERT.name;
ret = kmf_hexstr_to_bytes(
(uchar_t *)policy->VAL_OCSP_RESP_CERT.serial,
&bytes, &bytelen);
if (ret != KMF_OK || bytes == NULL) {
ret = KMF_ERR_OCSP_POLICY;
goto out;
}
sernum.val = bytes;
sernum.len = bytelen;
kmf_set_attr_at_index(fc_attrlist, fc_numattr,
KMF_KEYSTORE_TYPE_ATTR, kstype,
sizeof (KMF_KEYSTORE_TYPE));
fc_numattr++;
kmf_set_attr_at_index(fc_attrlist, fc_numattr,
KMF_SUBJECT_NAME_ATTR, signer_name, strlen(signer_name));
fc_numattr++;
kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_BIGINT_ATTR,
&sernum, sizeof (KMF_BIGINT));
fc_numattr++;
if (*kstype == KMF_KEYSTORE_NSS && slotlabel != NULL) {
kmf_set_attr_at_index(fc_attrlist, fc_numattr,
KMF_TOKEN_LABEL_ATTR, slotlabel,
strlen(slotlabel));
fc_numattr++;
}
if (*kstype == KMF_KEYSTORE_OPENSSL) {
if (dirpath == NULL) {
kmf_set_attr_at_index(fc_attrlist, fc_numattr,
KMF_DIRPATH_ATTR, dir, strlen(dir));
fc_numattr++;
} else {
kmf_set_attr_at_index(fc_attrlist, fc_numattr,
KMF_DIRPATH_ATTR, dirpath,
strlen(dirpath));
fc_numattr++;
}
}
num = 0;
kmf_set_attr_at_index(fc_attrlist, fc_numattr,
KMF_COUNT_ATTR, &num, sizeof (uint32_t));
fc_numattr++;
ret = kmf_find_cert(handle, fc_numattr, fc_attrlist);
if (ret != KMF_OK || num != 1) {
if (num == 0)
ret = KMF_ERR_CERT_NOT_FOUND;
if (num > 0)
ret = KMF_ERR_CERT_MULTIPLE_FOUND;
goto out;
}
(void) memset(&signer_retrcert, 0, sizeof (KMF_X509_DER_CERT));
kmf_set_attr_at_index(fc_attrlist, fc_numattr,
KMF_X509_DER_CERT_ATTR, &signer_retrcert,
sizeof (KMF_X509_DER_CERT));
fc_numattr++;
ret = kmf_find_cert(handle, fc_numattr, fc_attrlist);
if (ret == KMF_OK) {
signer_cert->Length =
signer_retrcert.certificate.Length;
signer_cert->Data = signer_retrcert.certificate.Data;
} else {
goto out;
}
}
/*
* If the caller provides an OCSP response, we will use it directly.
* Otherwise, we will try to fetch an OCSP response for the given
* certificate now.
*/
if (response == NULL) {
new_response = (KMF_DATA *) malloc(sizeof (KMF_DATA));
if (new_response == NULL) {
ret = KMF_ERR_MEMORY;
goto out;
}
new_response->Data = NULL;
new_response->Length = 0;
ret = kmf_get_ocsp_for_cert(handle, user_cert, issuer_cert,
new_response);
if (ret != KMF_OK)
goto out;
}
/*
* Process the OCSP response and retrieve the certificate status.
*/
numattr = 0;
kmf_set_attr_at_index(attrlist, numattr, KMF_ISSUER_CERT_DATA_ATTR,
issuer_cert, sizeof (KMF_DATA));
numattr++;
kmf_set_attr_at_index(attrlist, numattr, KMF_USER_CERT_DATA_ATTR,
user_cert, sizeof (KMF_DATA));
numattr++;
if (signer_cert != NULL) {
kmf_set_attr_at_index(attrlist, numattr,
KMF_SIGNER_CERT_DATA_ATTR, user_cert, sizeof (KMF_DATA));
numattr++;
}
kmf_set_attr_at_index(attrlist, numattr, KMF_OCSP_RESPONSE_DATA_ATTR,
response == NULL ? new_response : response, sizeof (KMF_DATA));
numattr++;
kmf_set_attr_at_index(attrlist, numattr, KMF_RESPONSE_LIFETIME_ATTR,
&ltime, sizeof (uint32_t));
numattr++;
kmf_set_attr_at_index(attrlist, numattr,
KMF_IGNORE_RESPONSE_SIGN_ATTR, &ignore_response_sign,
sizeof (boolean_t));
numattr++;
kmf_set_attr_at_index(attrlist, numattr,
KMF_OCSP_RESPONSE_STATUS_ATTR, &response_status, sizeof (int));
numattr++;
kmf_set_attr_at_index(attrlist, numattr,
KMF_OCSP_RESPONSE_REASON_ATTR, &reason, sizeof (int));
numattr++;
kmf_set_attr_at_index(attrlist, numattr,
KMF_OCSP_RESPONSE_CERT_STATUS_ATTR, &cert_status, sizeof (int));
numattr++;
ret = kmf_get_ocsp_status_for_cert(handle, numattr, attrlist);
if (ret == KMF_OK) {
switch (cert_status) {
case OCSP_GOOD:
break;
case OCSP_UNKNOWN:
ret = KMF_ERR_OCSP_UNKNOWN_CERT;
break;
case OCSP_REVOKED:
ret = KMF_ERR_OCSP_REVOKED;
break;
}
}
out:
if (new_response) {
kmf_free_data(new_response);
free(new_response);
}
if (signer_cert) {
kmf_free_data(signer_cert);
free(signer_cert);
}
if (sernum.val != NULL)
free(sernum.val);
return (ret);
}
static KMF_RETURN
cert_ku_check(KMF_HANDLE_T handle, KMF_DATA *cert)
{
KMF_POLICY_RECORD *policy;
KMF_X509EXT_KEY_USAGE keyusage;
KMF_RETURN ret = KMF_OK;
KMF_X509EXT_BASICCONSTRAINTS constraint;
KMF_BOOL critical = B_FALSE;
if (handle == NULL || cert == NULL)
return (KMF_ERR_BAD_PARAMETER);
policy = handle->policy;
(void) memset(&keyusage, 0, sizeof (keyusage));
ret = kmf_get_cert_ku(cert, &keyusage);
if (ret == KMF_ERR_EXTENSION_NOT_FOUND) {
if (policy->ku_bits) {
/* keyusage is not set in cert but is set in policy */
return (KMF_ERR_KEYUSAGE);
} else {
/* no keyusage set in both cert and policy */
return (KMF_OK);
}
}
if (ret != KMF_OK) {
/* real error */
return (ret);
}
/*
* If KeyCertSign is set, then constraints.cA must be TRUE and
* marked critical.
*/
if ((keyusage.KeyUsageBits & KMF_keyCertSign)) {
(void) memset(&constraint, 0, sizeof (constraint));
ret = kmf_get_cert_basic_constraint(cert,
&critical, &constraint);
if (ret != KMF_OK) {
/* real error */
return (ret);
}
if (!constraint.cA || !critical)
return (KMF_ERR_KEYUSAGE);
}
/*
* Rule: if the KU bit is set in policy, the corresponding KU bit
* must be set in the certificate (but not vice versa).
*/
if ((policy->ku_bits & keyusage.KeyUsageBits) == policy->ku_bits) {
return (KMF_OK);
} else {
return (KMF_ERR_KEYUSAGE);
}
}
static KMF_RETURN
cert_eku_check(KMF_HANDLE_T handle, KMF_DATA *cert)
{
KMF_POLICY_RECORD *policy;
KMF_RETURN ret = KMF_OK;
KMF_X509EXT_EKU eku;
uint16_t cert_eku = 0, policy_eku = 0;
int i;
if (handle == NULL || cert == NULL)
return (KMF_ERR_BAD_PARAMETER);
policy = handle->policy;
/*
* If the policy does not have any EKU, then there is
* nothing further to check.
*/
if (policy->eku_set.eku_count == 0)
return (KMF_OK);
ret = kmf_get_cert_eku(cert, &eku);
if ((ret != KMF_ERR_EXTENSION_NOT_FOUND) && (ret != KMF_OK)) {
/* real error */
return (ret);
}
if (ret == KMF_ERR_EXTENSION_NOT_FOUND) {
cert_eku = 0;
} else {
/*
* Build the EKU bitmap based on the certificate
*/
for (i = 0; i < eku.nEKUs; i++) {
if (IsEqualOid(&eku.keyPurposeIdList[i],
(KMF_OID *)&KMFOID_PKIX_KP_ServerAuth)) {
cert_eku |= KMF_EKU_SERVERAUTH;
} else if (IsEqualOid(&eku.keyPurposeIdList[i],
(KMF_OID *)&KMFOID_PKIX_KP_ClientAuth)) {
cert_eku |= KMF_EKU_CLIENTAUTH;
} else if (IsEqualOid(&eku.keyPurposeIdList[i],
(KMF_OID *)&KMFOID_PKIX_KP_CodeSigning)) {
cert_eku |= KMF_EKU_CODESIGNING;
} else if (IsEqualOid(&eku.keyPurposeIdList[i],
(KMF_OID *)&KMFOID_PKIX_KP_EmailProtection)) {
cert_eku |= KMF_EKU_EMAIL;
} else if (IsEqualOid(&eku.keyPurposeIdList[i],
(KMF_OID *)&KMFOID_PKIX_KP_TimeStamping)) {
cert_eku |= KMF_EKU_TIMESTAMP;
} else if (IsEqualOid(&eku.keyPurposeIdList[i],
(KMF_OID *)&KMFOID_PKIX_KP_OCSPSigning)) {
cert_eku |= KMF_EKU_OCSPSIGNING;
} else if (!policy->ignore_unknown_ekus) {
return (KMF_ERR_KEYUSAGE);
}
} /* for */
}
/*
* Build the EKU bitmap based on the policy
*/
for (i = 0; i < policy->eku_set.eku_count; i++) {
if (IsEqualOid(&policy->eku_set.ekulist[i],
(KMF_OID *)&KMFOID_PKIX_KP_ServerAuth)) {
policy_eku |= KMF_EKU_SERVERAUTH;
} else if (IsEqualOid(&policy->eku_set.ekulist[i],
(KMF_OID *)&KMFOID_PKIX_KP_ClientAuth)) {
policy_eku |= KMF_EKU_CLIENTAUTH;
} else if (IsEqualOid(&policy->eku_set.ekulist[i],
(KMF_OID *)&KMFOID_PKIX_KP_CodeSigning)) {
policy_eku |= KMF_EKU_CODESIGNING;
} else if (IsEqualOid(&policy->eku_set.ekulist[i],
(KMF_OID *)&KMFOID_PKIX_KP_EmailProtection)) {
policy_eku |= KMF_EKU_EMAIL;
} else if (IsEqualOid(&policy->eku_set.ekulist[i],
(KMF_OID *)&KMFOID_PKIX_KP_TimeStamping)) {
policy_eku |= KMF_EKU_TIMESTAMP;
} else if (IsEqualOid(&policy->eku_set.ekulist[i],
(KMF_OID *)&KMFOID_PKIX_KP_OCSPSigning)) {
policy_eku |= KMF_EKU_OCSPSIGNING;
} else if (!policy->ignore_unknown_ekus) {
return (KMF_ERR_KEYUSAGE);
}
} /* for */
/*
* Rule: if the EKU OID is set in policy, the corresponding EKU OID
* must be set in the certificate (but not vice versa).
*/
if ((policy_eku & cert_eku) == policy_eku) {
return (KMF_OK);
} else {
return (KMF_ERR_KEYUSAGE);
}
}
static KMF_RETURN
find_issuer_cert(KMF_HANDLE_T handle, KMF_KEYSTORE_TYPE *kstype,
char *user_issuer, KMF_DATA *issuer_cert,
char *slotlabel, char *dirpath)
{
KMF_RETURN ret = KMF_OK;
KMF_X509_DER_CERT *certlist = NULL;
uint32_t i, num = 0;
time_t t_notbefore;
time_t t_notafter;
time_t latest;
KMF_DATA tmp_cert = { 0, NULL };
KMF_ATTRIBUTE fc_attrlist[16];
int fc_numattr = 0;
char *dir = "./";
if (handle == NULL || kstype == NULL || user_issuer == NULL ||
issuer_cert == NULL)
return (KMF_ERR_BAD_PARAMETER);
if (!is_valid_keystore_type(*kstype))
return (KMF_ERR_BAD_PARAMETER);
kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_KEYSTORE_TYPE_ATTR,
kstype, sizeof (KMF_KEYSTORE_TYPE));
fc_numattr++;
kmf_set_attr_at_index(fc_attrlist, fc_numattr, KMF_SUBJECT_NAME_ATTR,
user_issuer, strlen(user_issuer));
fc_numattr++;
if (*kstype == KMF_KEYSTORE_NSS && slotlabel != NULL) {
kmf_set_attr_at_index(fc_attrlist, fc_numattr,
KMF_TOKEN_LABEL_ATTR, slotlabel, strlen(slotlabel));
fc_numattr++;
}
if (*kstype == KMF_KEYSTORE_OPENSSL) {
if (dirpath == NULL) {
kmf_set_attr_at_index(fc_attrlist, fc_numattr,
KMF_DIRPATH_ATTR, dir, strlen(dir));
fc_numattr++;
} else {
kmf_set_attr_at_index(fc_attrlist, fc_numattr,
KMF_DIRPATH_ATTR, dirpath, strlen(dirpath));
fc_numattr++;
}
}
num = 0;
kmf_set_attr_at_index(fc_attrlist, fc_numattr,
KMF_COUNT_ATTR, &num, sizeof (uint32_t));
fc_numattr++;
ret = kmf_find_cert(handle, fc_numattr, fc_attrlist);
if (ret == KMF_OK && num > 0) {
certlist = (KMF_X509_DER_CERT *)malloc(num *
sizeof (KMF_X509_DER_CERT));
if (certlist == NULL) {
ret = KMF_ERR_MEMORY;
goto out;
}
kmf_set_attr_at_index(fc_attrlist, fc_numattr,
KMF_X509_DER_CERT_ATTR, certlist,
sizeof (KMF_X509_DER_CERT));
fc_numattr++;
ret = kmf_find_cert(handle, fc_numattr, fc_attrlist);
if (ret != KMF_OK) {
free(certlist);
certlist = NULL;
goto out;
}
} else {
goto out;
}
if (num == 1) {
/* only one issuer cert is found */
tmp_cert.Length = certlist[0].certificate.Length;
tmp_cert.Data = certlist[0].certificate.Data;
} else {
/*
* More than one issuer certs are found. We will
* pick the latest one.
*/
latest = 0;
for (i = 0; i < num; i++) {
ret = kmf_get_cert_validity(&certlist[i].certificate,
&t_notbefore, &t_notafter);
if (ret != KMF_OK) {
ret = KMF_ERR_VALIDITY_PERIOD;
goto out;
}
if (t_notbefore > latest) {
tmp_cert.Length =
certlist[i].certificate.Length;
tmp_cert.Data =
certlist[i].certificate.Data;
latest = t_notbefore;
}
}
}
issuer_cert->Length = tmp_cert.Length;
issuer_cert->Data = malloc(tmp_cert.Length);
if (issuer_cert->Data == NULL) {
ret = KMF_ERR_MEMORY;
goto out;
}
(void) memcpy(issuer_cert->Data, tmp_cert.Data,
tmp_cert.Length);
out:
if (certlist != NULL) {
for (i = 0; i < num; i++)
kmf_free_kmf_cert(handle, &certlist[i]);
free(certlist);
}
return (ret);
}
static KMF_RETURN
find_ta_cert(KMF_HANDLE_T handle, KMF_KEYSTORE_TYPE *kstype,
KMF_DATA *ta_cert, KMF_X509_NAME *user_issuerDN,
char *slotlabel, char *dirpath)
{
KMF_POLICY_RECORD *policy;
KMF_RETURN ret = KMF_OK;
uint32_t num = 0;
char *ta_name;
KMF_BIGINT serial = { NULL, 0 };
uchar_t *bytes = NULL;
size_t bytelen;
KMF_X509_DER_CERT ta_retrCert;
char *ta_subject = NULL;
KMF_X509_NAME ta_subjectDN;
KMF_ATTRIBUTE fc_attrlist[16];
int fc_numattr = 0;
char *dir = "./";
if (handle == NULL || kstype == NULL || ta_cert == NULL ||
user_issuerDN == NULL)
return (KMF_ERR_BAD_PARAMETER);
if (!is_valid_keystore_type(*kstype))
return (KMF_ERR_BAD_PARAMETER);
/* Get the TA name and serial number from the policy */
policy = handle->policy;
ta_name = policy->ta_name;
/*
* Use name and serial from policy.
*/
ret = kmf_hexstr_to_bytes((uchar_t *)policy->ta_serial,
&bytes, &bytelen);
if (ret != KMF_OK || bytes == NULL) {
ret = KMF_ERR_TA_POLICY;
goto out;
}
serial.val = bytes;
serial.len = bytelen;
/* set up fc_attrlist for kmf_find_cert */
kmf_set_attr_at_index(fc_attrlist,
fc_numattr++, KMF_BIGINT_ATTR,
&serial, sizeof (KMF_BIGINT));
kmf_set_attr_at_index(fc_attrlist,
fc_numattr++, KMF_SUBJECT_NAME_ATTR,
ta_name, strlen(ta_name));
kmf_set_attr_at_index(fc_attrlist, fc_numattr++, KMF_KEYSTORE_TYPE_ATTR,
kstype, sizeof (KMF_KEYSTORE_TYPE));
if (*kstype == KMF_KEYSTORE_NSS && slotlabel != NULL) {
kmf_set_attr_at_index(fc_attrlist, fc_numattr++,
KMF_TOKEN_LABEL_ATTR, slotlabel, strlen(slotlabel));
}
if (*kstype == KMF_KEYSTORE_OPENSSL) {
if (dirpath == NULL) {
kmf_set_attr_at_index(fc_attrlist, fc_numattr++,
KMF_DIRPATH_ATTR, dir, strlen(dir));
} else {
kmf_set_attr_at_index(fc_attrlist, fc_numattr++,
KMF_DIRPATH_ATTR, dirpath, strlen(dirpath));
}
}
num = 0;
kmf_set_attr_at_index(fc_attrlist, fc_numattr++,
KMF_COUNT_ATTR, &num, sizeof (uint32_t));
ret = kmf_find_cert(handle, fc_numattr, fc_attrlist);
if (ret != KMF_OK || num != 1) {
if (num == 0)
ret = KMF_ERR_CERT_NOT_FOUND;
if (num > 1)
ret = KMF_ERR_CERT_MULTIPLE_FOUND;
goto out;
}
kmf_set_attr_at_index(fc_attrlist, fc_numattr,
KMF_X509_DER_CERT_ATTR, &ta_retrCert, sizeof (KMF_X509_DER_CERT));
fc_numattr++;
ret = kmf_find_cert(handle, fc_numattr, fc_attrlist);
if (ret == KMF_OK) {
ta_cert->Length = ta_retrCert.certificate.Length;
ta_cert->Data = malloc(ta_retrCert.certificate.Length);
if (ta_cert->Data == NULL) {
ret = KMF_ERR_MEMORY;
goto out;
}
(void) memcpy(ta_cert->Data, ta_retrCert.certificate.Data,
ta_retrCert.certificate.Length);
} else {
goto out;
}
/*
* The found TA's name must be matching with issuer name in
* subscriber's certificate.
*/
(void) memset(&ta_subjectDN, 0, sizeof (ta_subjectDN));
ret = kmf_get_cert_subject_str(handle, ta_cert, &ta_subject);
if (ret != KMF_OK)
goto out;
ret = kmf_dn_parser(ta_subject, &ta_subjectDN);
if (ret != KMF_OK)
goto out;
if (kmf_compare_rdns(user_issuerDN, &ta_subjectDN) != 0)
ret = KMF_ERR_CERT_NOT_FOUND;
kmf_free_dn(&ta_subjectDN);
/* Make sure the TA cert has the correct extensions */
if (ret == KMF_OK) {
ret = check_key_usage(handle, ta_cert, KMF_KU_SIGN_CERT);
if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0)
ret = KMF_OK;
}
out:
if (ta_retrCert.certificate.Data)
kmf_free_kmf_cert(handle, &ta_retrCert);
if ((ret != KMF_OK))
kmf_free_data(ta_cert);
if (ta_subject != NULL)
free(ta_subject);
if (serial.val != NULL)
free(serial.val);
return (ret);
}
KMF_RETURN
kmf_validate_cert(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist)
{
KMF_RETURN ret = KMF_OK;
KMF_KEYSTORE_TYPE *kstype = NULL;
KMF_DATA *pcert = NULL;
int *result = NULL;
char *slotlabel = NULL;
char *dirpath = NULL;
KMF_DATA *ocsp_response = NULL;
KMF_DATA ta_cert = { 0, NULL };
KMF_DATA issuer_cert = { 0, NULL };
char *user_issuer = NULL, *user_subject = NULL;
KMF_X509_NAME user_issuerDN, user_subjectDN;
boolean_t self_signed = B_FALSE;
KMF_POLICY_RECORD *policy;
KMF_ATTRIBUTE_TESTER required_attrs[] = {
{KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
{KMF_CERT_DATA_ATTR, FALSE, sizeof (KMF_DATA), sizeof (KMF_DATA)},
{KMF_VALIDATE_RESULT_ATTR, FALSE, 1, sizeof (int)}
};
int num_req_attrs = sizeof (required_attrs) /
sizeof (KMF_ATTRIBUTE_TESTER);
if (handle == NULL)
return (KMF_ERR_BAD_PARAMETER);
CLEAR_ERROR(handle, ret);
ret = test_attributes(num_req_attrs, required_attrs,
0, NULL, numattr, attrlist);
if (ret != KMF_OK)
return (ret);
policy = handle->policy;
/* Get the attribute values */
kstype = kmf_get_attr_ptr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr);
pcert = kmf_get_attr_ptr(KMF_CERT_DATA_ATTR, attrlist, numattr);
result = kmf_get_attr_ptr(KMF_VALIDATE_RESULT_ATTR, attrlist, numattr);
if (kstype == NULL || pcert == NULL || result == NULL)
return (KMF_ERR_BAD_PARAMETER);
slotlabel = kmf_get_attr_ptr(KMF_TOKEN_LABEL_ATTR, attrlist, numattr);
dirpath = kmf_get_attr_ptr(KMF_DIRPATH_ATTR, attrlist, numattr);
ocsp_response = kmf_get_attr_ptr(KMF_OCSP_RESPONSE_DATA_ATTR, attrlist,
numattr);
/* Initialize the returned result */
*result = KMF_CERT_VALIDATE_OK;
/*
* Get the issuer information from the input certficate first.
*/
if ((ret = kmf_get_cert_issuer_str(handle, pcert,
&user_issuer)) != KMF_OK) {
*result |= KMF_CERT_VALIDATE_ERR_USER;
} else if ((ret = kmf_dn_parser(user_issuer, &user_issuerDN)) !=
KMF_OK) {
*result |= KMF_CERT_VALIDATE_ERR_USER;
}
/*
* Check if the certificate is a self-signed cert.
*/
if ((ret = kmf_get_cert_subject_str(handle, pcert,
&user_subject)) != KMF_OK) {
*result |= KMF_CERT_VALIDATE_ERR_USER;
} else if ((ret = kmf_dn_parser(user_subject, &user_subjectDN)) !=
KMF_OK) {
*result |= KMF_CERT_VALIDATE_ERR_USER;
}
if ((*result & KMF_CERT_VALIDATE_ERR_USER) == 0 &&
(kmf_compare_rdns(&user_issuerDN, &user_subjectDN)) == 0) {
/*
* this is a self-signed cert
*/
self_signed = B_TRUE;
}
kmf_free_dn(&user_subjectDN);
/*
* Check KeyUsage extension of the subscriber's certificate
*/
ret = cert_ku_check(handle, pcert);
if (ret != KMF_OK) {
*result |= KMF_CERT_VALIDATE_ERR_KEYUSAGE;
}
/*
* Validate Extended KeyUsage extension
*/
ret = cert_eku_check(handle, pcert);
if (ret != KMF_OK) {
*result |= KMF_CERT_VALIDATE_ERR_EXT_KEYUSAGE;
}
/*
* Check the certificate's validity period
*
* This step is needed when "ignore_date" in policy is set
* to false.
*/
if (!policy->ignore_date) {
/*
* Validate expiration date
*/
ret = kmf_check_cert_date(handle, pcert);
if (ret != KMF_OK)
*result |= KMF_CERT_VALIDATE_ERR_TIME;
}
/*
* When "ignore_trust_anchor" in policy is set to FALSE,
* we will try to find the TA cert based on the TA policy
* attributes.
*
* TA's subject name (ta_name) and serial number (ta_serial)
* are defined as optional attributes in policy dtd, but they
* should exist in policy when "ignore_trust_anchor" is set
* to FALSE. The policy verification code has enforced that.
*
* The serial number may be NULL if the ta_name == "search"
* which indicates that KMF should try to locate the issuer
* of the subject cert instead of using a specific TA name.
*/
if (policy->ignore_trust_anchor) {
goto check_revocation;
}
/*
* Verify the signature of subscriber's certificate using
* TA certificate.
*/
if (self_signed) {
ret = verify_cert_with_cert(handle, pcert, pcert);
if (ret != KMF_OK)
*result |= KMF_CERT_VALIDATE_ERR_SIGNATURE;
} else if (user_issuer != NULL) {
if (policy->ta_name != NULL &&
strcasecmp(policy->ta_name, "search") == 0) {
ret = find_issuer_cert(handle, kstype, user_issuer,
&issuer_cert, slotlabel, dirpath);
if (ret != KMF_OK) {
*result |= KMF_CERT_VALIDATE_ERR_TA;
} else {
ta_cert = issuer_cert; /* used later */
}
} else {
/*
* If we didnt find the user_issuer string, we
* won't have a "user_issuerDN" either.
*/
ret = find_ta_cert(handle, kstype, &ta_cert,
&user_issuerDN, slotlabel, dirpath);
}
if (ret != KMF_OK) {
*result |= KMF_CERT_VALIDATE_ERR_TA;
}
/* Only verify if we got the TA without an error. */
if ((*result & KMF_CERT_VALIDATE_ERR_TA) == 0) {
ret = verify_cert_with_cert(handle, pcert,
&ta_cert);
if (ret != KMF_OK)
*result |= KMF_CERT_VALIDATE_ERR_SIGNATURE;
}
} else {
/* No issuer was found, so we cannot find a trust anchor */
*result |= KMF_CERT_VALIDATE_ERR_TA;
}
check_revocation:
/*
* Check certificate revocation
*/
if (self_signed) {
/* skip revocation checking */
goto out;
}
/*
* When CRL or OCSP revocation method is set in the policy,
* we will try to find the issuer of the subscriber certificate
* using the issuer name of the subscriber certificate. The
* issuer certificate will be used to do the CRL checking
* and OCSP checking.
*/
if (!(policy->revocation & KMF_REVOCATION_METHOD_CRL) &&
!(policy->revocation & KMF_REVOCATION_METHOD_OCSP)) {
goto out;
}
/*
* If we did not find the issuer cert earlier
* (when policy->ta_name == "search"), get it here.
* We need the issuer cert if the revocation method is
* CRL or OCSP.
*/
if (issuer_cert.Length == 0 &&
policy->revocation & KMF_REVOCATION_METHOD_CRL ||
policy->revocation & KMF_REVOCATION_METHOD_OCSP) {
ret = find_issuer_cert(handle, kstype, user_issuer,
&issuer_cert, slotlabel, dirpath);
if (ret != KMF_OK) {
*result |= KMF_CERT_VALIDATE_ERR_ISSUER;
}
}
if (policy->revocation & KMF_REVOCATION_METHOD_CRL &&
(*result & KMF_CERT_VALIDATE_ERR_ISSUER) == 0) {
ret = cert_crl_check(handle, kstype, pcert, &issuer_cert);
if (ret != KMF_OK) {
*result |= KMF_CERT_VALIDATE_ERR_CRL;
}
}
if (policy->revocation & KMF_REVOCATION_METHOD_OCSP &&
(*result & KMF_CERT_VALIDATE_ERR_ISSUER) == 0) {
ret = cert_ocsp_check(handle, kstype, pcert, &issuer_cert,
ocsp_response, slotlabel, dirpath);
if (ret != KMF_OK) {
*result |= KMF_CERT_VALIDATE_ERR_OCSP;
}
}
out:
if (user_issuer) {
kmf_free_dn(&user_issuerDN);
free(user_issuer);
}
if (user_subject)
free(user_subject);
/*
* If we did not copy ta_cert to issuer_cert, free it.
*/
if (issuer_cert.Data &&
issuer_cert.Data != ta_cert.Data)
kmf_free_data(&issuer_cert);
kmf_free_data(&ta_cert);
/*
* If we got an error flag from any of the checks,
* remap the return code to a generic "CERT_VALIDATION"
* error so the caller knows to check the individual flags.
*/
if (*result != 0)
ret = KMF_ERR_CERT_VALIDATION;
return (ret);
}
KMF_RETURN
kmf_create_cert_file(const KMF_DATA *certdata, KMF_ENCODE_FORMAT format,
char *certfile)
{
KMF_RETURN rv = KMF_OK;
int fd = -1;
KMF_DATA pemdata = { 0, NULL };
if (certdata == NULL || certfile == NULL)
return (KMF_ERR_BAD_PARAMETER);
if (format != KMF_FORMAT_PEM && format != KMF_FORMAT_ASN1)
return (KMF_ERR_BAD_PARAMETER);
if (format == KMF_FORMAT_PEM) {
int len;
rv = kmf_der_to_pem(KMF_CERT,
certdata->Data, certdata->Length,
&pemdata.Data, &len);
if (rv != KMF_OK)
goto cleanup;
pemdata.Length = (size_t)len;
}
if ((fd = open(certfile, O_CREAT | O_RDWR | O_TRUNC, 0644)) == -1) {
rv = KMF_ERR_OPEN_FILE;
goto cleanup;
}
if (format == KMF_FORMAT_PEM) {
if (write(fd, pemdata.Data, pemdata.Length) !=
pemdata.Length) {
rv = KMF_ERR_WRITE_FILE;
}
} else {
if (write(fd, certdata->Data, certdata->Length) !=
certdata->Length) {
rv = KMF_ERR_WRITE_FILE;
}
}
cleanup:
if (fd != -1)
(void) close(fd);
kmf_free_data(&pemdata);
return (rv);
}
/*
* kmf_is_cert_data
*
* Determine if a KMF_DATA buffer contains an encoded X.509 certificate.
*
* Return:
* KMF_OK if it is a certificate
* KMF_ERR_ENCODING (or other error) if not.
*/
KMF_RETURN
kmf_is_cert_data(KMF_DATA *data, KMF_ENCODE_FORMAT *fmt)
{
KMF_RETURN rv = KMF_OK;
KMF_X509_CERTIFICATE *x509 = NULL;
KMF_DATA oldpem = { 0, NULL };
uchar_t *d = NULL;
int len = 0;
if (data == NULL || fmt == NULL)
return (KMF_ERR_BAD_PARAMETER);
rv = kmf_get_data_format(data, fmt);
if (rv != KMF_OK)
return (rv);
switch (*fmt) {
case KMF_FORMAT_ASN1:
rv = DerDecodeSignedCertificate(data, &x509);
break;
case KMF_FORMAT_PEM:
/* Convert to ASN.1 DER first */
rv = kmf_pem_to_der(data->Data, data->Length,
&d, &len);
if (rv != KMF_OK)
return (rv);
oldpem.Data = d;
oldpem.Length = len;
rv = DerDecodeSignedCertificate(&oldpem, &x509);
kmf_free_data(&oldpem);
break;
case KMF_FORMAT_PKCS12:
case KMF_FORMAT_UNDEF:
default:
return (KMF_ERR_ENCODING);
}
if (x509 != NULL) {
kmf_free_signed_cert(x509);
free(x509);
}
return (rv);
}
KMF_RETURN
kmf_is_cert_file(KMF_HANDLE_T handle, char *filename,
KMF_ENCODE_FORMAT *pformat)
{
KMF_RETURN ret;
KMF_DATA filedata;
CLEAR_ERROR(handle, ret);
if (ret != KMF_OK)
return (ret);
if (filename == NULL || pformat == NULL)
return (KMF_ERR_BAD_PARAMETER);
ret = kmf_read_input_file(handle, filename, &filedata);
if (ret != KMF_OK)
return (ret);
ret = kmf_is_cert_data(&filedata, pformat);
if (ret == KMF_ERR_BAD_CERT_FORMAT)
ret = KMF_ERR_BAD_CERTFILE;
kmf_free_data(&filedata);
return (ret);
}
/*
* This function checks the validity period of a der-encoded certificate.
*/
KMF_RETURN
kmf_check_cert_date(KMF_HANDLE_T handle, const KMF_DATA *cert)
{
KMF_RETURN rv;
struct tm *gmt;
time_t t_now;
time_t t_notbefore;
time_t t_notafter;
KMF_POLICY_RECORD *policy;
uint32_t adj;
CLEAR_ERROR(handle, rv);
if (rv != KMF_OK)
return (rv);
if (cert == NULL || cert->Data == NULL || cert->Length == 0)
return (KMF_ERR_BAD_PARAMETER);
policy = handle->policy;
rv = kmf_get_cert_validity(cert, &t_notbefore, &t_notafter);
if (rv != KMF_OK)
return (rv);
/*
* Get the current time. The time returned from time() is local which
* cannot be used directly. It must be converted to UTC/GMT first.
*/
t_now = time(NULL);
gmt = gmtime(&t_now);
t_now = mktime(gmt);
/*
* Adjust the validity time
*/
if (policy->validity_adjusttime != NULL) {
if (str2lifetime(policy->validity_adjusttime, &adj) < 0)
return (KMF_ERR_VALIDITY_PERIOD);
} else {
adj = 0;
}
t_notafter += adj;
t_notbefore -= adj;
if (t_now <= t_notafter && t_now >= t_notbefore) {
rv = KMF_OK;
} else {
rv = KMF_ERR_VALIDITY_PERIOD;
}
return (rv);
}
KMF_RETURN
kmf_export_pk12(KMF_HANDLE_T handle, int numattr, KMF_ATTRIBUTE *attrlist)
{
KMF_PLUGIN *plugin;
KMF_RETURN ret = KMF_OK;
KMF_KEYSTORE_TYPE kstype;
KMF_ATTRIBUTE_TESTER required_attrs[] = {
{KMF_KEYSTORE_TYPE_ATTR, FALSE, 1, sizeof (KMF_KEYSTORE_TYPE)},
{KMF_OUTPUT_FILENAME_ATTR, TRUE, 1, 0},
};
int num_req_attrs = sizeof (required_attrs) /
sizeof (KMF_ATTRIBUTE_TESTER);
if (handle == NULL)
return (KMF_ERR_BAD_PARAMETER);
CLEAR_ERROR(handle, ret);
ret = test_attributes(num_req_attrs, required_attrs, 0, NULL,
numattr, attrlist);
if (ret != KMF_OK)
return (ret);
ret = kmf_get_attr(KMF_KEYSTORE_TYPE_ATTR, attrlist, numattr,
&kstype, NULL);
if (ret != KMF_OK)
return (ret);
plugin = FindPlugin(handle, kstype);
if (plugin == NULL || plugin->funclist->ExportPK12 == NULL)
return (KMF_ERR_PLUGIN_NOTFOUND);
return (plugin->funclist->ExportPK12(handle, numattr, attrlist));
}
KMF_RETURN
kmf_build_pk12(KMF_HANDLE_T handle, int numcerts,
KMF_X509_DER_CERT *certlist, int numkeys, KMF_KEY_HANDLE *keylist,
KMF_CREDENTIAL *p12cred, char *filename)
{
KMF_RETURN rv;
KMF_PLUGIN *plugin;
KMF_RETURN (*buildpk12)(KMF_HANDLE *, int, KMF_X509_DER_CERT *,
int, KMF_KEY_HANDLE *, KMF_CREDENTIAL *, char *);
CLEAR_ERROR(handle, rv);
if (rv != KMF_OK)
return (rv);
if (filename == NULL || p12cred == NULL ||
(certlist == NULL && keylist == NULL))
return (KMF_ERR_BAD_PARAMETER);
plugin = FindPlugin(handle, KMF_KEYSTORE_OPENSSL);
if (plugin == NULL || plugin->dldesc == NULL) {
return (KMF_ERR_PLUGIN_NOTFOUND);
}
buildpk12 = (KMF_RETURN(*)())dlsym(plugin->dldesc,
"openssl_build_pk12");
if (buildpk12 == NULL) {
return (KMF_ERR_FUNCTION_NOT_FOUND);
}
rv = buildpk12(handle, numcerts, certlist, numkeys, keylist, p12cred,
filename);
return (rv);
}
KMF_RETURN
kmf_import_objects(KMF_HANDLE_T handle, char *filename,
KMF_CREDENTIAL *cred,
KMF_X509_DER_CERT **certs, int *ncerts,
KMF_RAW_KEY_DATA **rawkeys, int *nkeys)
{
KMF_RETURN rv;
KMF_PLUGIN *plugin;
KMF_RETURN (*import_objects)(KMF_HANDLE *, char *, KMF_CREDENTIAL *,
KMF_X509_DER_CERT **, int *, KMF_RAW_KEY_DATA **, int *);
CLEAR_ERROR(handle, rv);
if (rv != KMF_OK)
return (rv);
if (filename == NULL || cred == NULL || certs == NULL ||
ncerts == NULL ||rawkeys == NULL || nkeys == NULL)
return (KMF_ERR_BAD_PARAMETER);
/*
* Use the Keypair reader from the OpenSSL plugin.
*/
plugin = FindPlugin(handle, KMF_KEYSTORE_OPENSSL);
if (plugin == NULL || plugin->dldesc == NULL) {
return (KMF_ERR_PLUGIN_NOTFOUND);
}
import_objects = (KMF_RETURN(*)())dlsym(plugin->dldesc,
"openssl_import_objects");
if (import_objects == NULL) {
return (KMF_ERR_FUNCTION_NOT_FOUND);
}
/* Use OpenSSL interfaces to get raw key and cert data */
rv = import_objects(handle, filename, cred, certs, ncerts,
rawkeys, nkeys);
return (rv);
}
KMF_BOOL
IsEqualOid(KMF_OID *Oid1, KMF_OID *Oid2)
{
return ((Oid1->Length == Oid2->Length) &&
!memcmp(Oid1->Data, Oid2->Data, Oid1->Length));
}
static KMF_RETURN
set_algoid(KMF_X509_ALGORITHM_IDENTIFIER *destid,
KMF_OID *newoid)
{
if (destid == NULL || newoid == NULL)
return (KMF_ERR_BAD_PARAMETER);
destid->algorithm.Length = newoid->Length;
destid->algorithm.Data = malloc(destid->algorithm.Length);
if (destid->algorithm.Data == NULL)
return (KMF_ERR_MEMORY);
(void) memcpy(destid->algorithm.Data, newoid->Data,
destid->algorithm.Length);
return (KMF_OK);
}
KMF_RETURN
copy_algoid(KMF_X509_ALGORITHM_IDENTIFIER *destid,
KMF_X509_ALGORITHM_IDENTIFIER *srcid)
{
KMF_RETURN ret = KMF_OK;
if (!destid || !srcid)
return (KMF_ERR_BAD_PARAMETER);
destid->algorithm.Length = srcid->algorithm.Length;
destid->algorithm.Data = malloc(destid->algorithm.Length);
if (destid->algorithm.Data == NULL)
return (KMF_ERR_MEMORY);
(void) memcpy(destid->algorithm.Data, srcid->algorithm.Data,
destid->algorithm.Length);
destid->parameters.Length = srcid->parameters.Length;
if (destid->parameters.Length > 0) {
destid->parameters.Data = malloc(destid->parameters.Length);
if (destid->parameters.Data == NULL)
return (KMF_ERR_MEMORY);
(void) memcpy(destid->parameters.Data, srcid->parameters.Data,
destid->parameters.Length);
} else {
destid->parameters.Data = NULL;
}
return (ret);
}
static KMF_RETURN
sign_cert(KMF_HANDLE_T handle,
const KMF_DATA *SubjectCert,
KMF_KEY_HANDLE *Signkey,
KMF_OID *signature_oid,
KMF_DATA *SignedCert)
{
KMF_X509_CERTIFICATE *subj_cert = NULL;
KMF_DATA data_to_sign = { 0, NULL };
KMF_DATA signed_data = { 0, NULL };
KMF_RETURN ret = KMF_OK;
KMF_ALGORITHM_INDEX algid;
int i = 0;
KMF_ATTRIBUTE attrlist[8];
if (!SignedCert)
return (KMF_ERR_BAD_PARAMETER);
SignedCert->Length = 0;
SignedCert->Data = NULL;
if (!SubjectCert)
return (KMF_ERR_BAD_PARAMETER);
if (!SubjectCert->Data || !SubjectCert->Length)
return (KMF_ERR_BAD_PARAMETER);
/*
* Shortcut - just extract the already encoded TBS cert data from
* the original data buffer. Since we haven't changed anything,
* there is no need to re-encode it.
*/
ret = ExtractX509CertParts((KMF_DATA *)SubjectCert,
&data_to_sign, NULL);
if (ret != KMF_OK) {
goto cleanup;
}
/* Estimate the signed data length generously */
signed_data.Length = data_to_sign.Length*2;
signed_data.Data = calloc(1, signed_data.Length);
if (!signed_data.Data) {
ret = KMF_ERR_MEMORY;
goto cleanup;
}
/*
* If we got here OK, decode into a structure and then re-encode
* the complete certificate.
*/
ret = DerDecodeSignedCertificate(SubjectCert, &subj_cert);
if (ret != KMF_OK) {
goto cleanup;
}
/* We are re-signing this cert, so clear out old signature data */
if (!IsEqualOid(&subj_cert->signature.algorithmIdentifier.algorithm,
signature_oid)) {
kmf_free_algoid(&subj_cert->signature.algorithmIdentifier);
ret = set_algoid(&subj_cert->signature.algorithmIdentifier,
signature_oid);
if (ret != KMF_OK)
goto cleanup;
ret = set_algoid(&subj_cert->certificate.signature,
signature_oid);
if (ret)
goto cleanup;
/* Free the previous "data to be signed" block */
kmf_free_data(&data_to_sign);
/*
* We changed the cert (updated the signature OID), so we
* need to re-encode it so the correct data gets signed.
*/
ret = DerEncodeTbsCertificate(&subj_cert->certificate,
&data_to_sign);
if (ret != KMF_OK)
goto cleanup;
}
kmf_set_attr_at_index(attrlist, i, KMF_KEYSTORE_TYPE_ATTR,
&Signkey->kstype, sizeof (KMF_KEYSTORE_TYPE));
i++;
kmf_set_attr_at_index(attrlist, i, KMF_KEY_HANDLE_ATTR,
Signkey, sizeof (KMF_KEY_HANDLE));
i++;
kmf_set_attr_at_index(attrlist, i, KMF_DATA_ATTR,
&data_to_sign, sizeof (KMF_DATA));
i++;
kmf_set_attr_at_index(attrlist, i, KMF_OUT_DATA_ATTR,
&signed_data, sizeof (KMF_DATA));
i++;
kmf_set_attr_at_index(attrlist, i, KMF_OID_ATTR,
signature_oid, sizeof (KMF_OID));
i++;
/* Sign the data */
ret = kmf_sign_data(handle, i, attrlist);
if (ret != KMF_OK)
goto cleanup;
algid = x509_algoid_to_algid(signature_oid);
if (algid == KMF_ALGID_SHA1WithECDSA ||
algid == KMF_ALGID_SHA256WithECDSA ||
algid == KMF_ALGID_SHA384WithECDSA ||
algid == KMF_ALGID_SHA512WithECDSA) {
/* ASN.1 encode ECDSA signature */
KMF_DATA signature;
ret = DerEncodeECDSASignature(&signed_data, &signature);
kmf_free_data(&signed_data);
if (ret != KMF_OK)
goto cleanup;
subj_cert->signature.encrypted = signature;
} else if (algid == KMF_ALGID_SHA1WithDSA ||
algid == KMF_ALGID_SHA256WithDSA) {
/*
* For DSA, kmf_sign_data() returns a 40-byte
* signature. We must encode the signature correctly.
*/
KMF_DATA signature;
ret = DerEncodeDSASignature(&signed_data, &signature);
kmf_free_data(&signed_data);
if (ret != KMF_OK)
goto cleanup;
subj_cert->signature.encrypted = signature;
} else {
ret = copy_data(&subj_cert->signature.encrypted, &signed_data);
kmf_free_data(&signed_data);
if (ret != KMF_OK)
goto cleanup;
}
/* Now, re-encode the cert with the new signature */
ret = DerEncodeSignedCertificate(subj_cert, SignedCert);
cleanup:
/* Cleanup & return */
if (ret != KMF_OK)
kmf_free_data(SignedCert);
kmf_free_data(&data_to_sign);
if (subj_cert != NULL) {
kmf_free_signed_cert(subj_cert);
free(subj_cert);
}
return (ret);
}
static KMF_RETURN
verify_cert_with_key(KMF_HANDLE_T handle,
KMF_DATA *derkey,
const KMF_DATA *CertToBeVerified)
{
KMF_RETURN ret = KMF_OK;
KMF_X509_CERTIFICATE *signed_cert = NULL;
KMF_X509_SPKI spki;
KMF_DATA data_to_verify = { 0, NULL };
KMF_DATA signed_data = { 0, NULL };
KMF_DATA signature = { 0, NULL };
KMF_ALGORITHM_INDEX algid;
/* check the caller and do other setup for this SPI call */
if (handle == NULL || CertToBeVerified == NULL ||
derkey == NULL || derkey->Data == NULL)
return (KMF_ERR_BAD_PARAMETER);
(void) memset(&spki, 0, sizeof (KMF_X509_SPKI));
ret = ExtractX509CertParts((KMF_DATA *)CertToBeVerified,
&data_to_verify, &signed_data);
if (ret != KMF_OK)
goto cleanup;
ret = DerDecodeSPKI(derkey, &spki);
if (ret != KMF_OK)
goto cleanup;
/* Decode the signer cert so we can get the Algorithm data */
ret = DerDecodeSignedCertificate(CertToBeVerified, &signed_cert);
if (ret != KMF_OK)
return (ret);
algid = x509_algoid_to_algid(CERT_SIG_OID(signed_cert));
if (algid == KMF_ALGID_NONE)
return (KMF_ERR_BAD_ALGORITHM);
if (algid == KMF_ALGID_SHA1WithDSA ||
algid == KMF_ALGID_SHA256WithDSA) {
ret = DerDecodeDSASignature(&signed_data, &signature);
if (ret != KMF_OK)
goto cleanup;
} else if (algid == KMF_ALGID_SHA1WithECDSA ||
algid == KMF_ALGID_SHA256WithECDSA ||
algid == KMF_ALGID_SHA384WithECDSA ||
algid == KMF_ALGID_SHA512WithECDSA) {
ret = DerDecodeECDSASignature(&signed_data, &signature);
if (ret != KMF_OK)
goto cleanup;
} else {
signature.Data = signed_data.Data;
signature.Length = signed_data.Length;
}
ret = PKCS_VerifyData(handle, algid, &spki,
&data_to_verify, &signature);
cleanup:
if (data_to_verify.Data != NULL)
free(data_to_verify.Data);
if (signed_data.Data != NULL)
free(signed_data.Data);
if (signed_cert) {
kmf_free_signed_cert(signed_cert);
free(signed_cert);
}
if (algid == KMF_ALGID_SHA1WithDSA ||
algid == KMF_ALGID_SHA256WithDSA ||
algid == KMF_ALGID_SHA1WithECDSA ||
algid == KMF_ALGID_SHA256WithECDSA ||
algid == KMF_ALGID_SHA384WithECDSA ||
algid == KMF_ALGID_SHA512WithECDSA) {
free(signature.Data);
}
kmf_free_algoid(&spki.algorithm);
kmf_free_data(&spki.subjectPublicKey);
return (ret);
}
/*
* Use a signer cert to verify another certificate's signature.
* This code forces the use of the PKCS11 mechanism for the verify
* operation for the Cryptographic Framework's FIPS-140 boundary.
*/
static KMF_RETURN
verify_cert_with_cert(KMF_HANDLE_T handle,
const KMF_DATA *CertToBeVerifiedData,
const KMF_DATA *SignerCertData)
{
KMF_RETURN ret = KMF_OK;
KMF_X509_CERTIFICATE *SignerCert = NULL;
KMF_X509_CERTIFICATE *ToBeVerifiedCert = NULL;
KMF_DATA data_to_verify = { 0, NULL };
KMF_DATA signed_data = { 0, NULL };
KMF_DATA signature;
KMF_ALGORITHM_INDEX algid;
KMF_POLICY_RECORD *policy;
if (handle == NULL ||
!CertToBeVerifiedData ||
!CertToBeVerifiedData->Data ||
!CertToBeVerifiedData->Length)
return (KMF_ERR_BAD_PARAMETER);
if (!SignerCertData ||
!SignerCertData->Data ||
!SignerCertData->Length)
return (KMF_ERR_BAD_PARAMETER);
policy = handle->policy;
/* Make sure the signer has proper key usage bits */
ret = check_key_usage(handle, SignerCertData, KMF_KU_SIGN_CERT);
if (ret == KMF_ERR_EXTENSION_NOT_FOUND && policy->ku_bits == 0)
ret = KMF_OK;
if (ret != KMF_OK)
return (ret);
/* Decode the cert into parts for verification */
ret = ExtractX509CertParts((KMF_DATA *)CertToBeVerifiedData,
&data_to_verify, &signed_data);
if (ret != KMF_OK)
goto cleanup;
/* Decode the to-be-verified cert so we know what algorithm to use */
ret = DerDecodeSignedCertificate(CertToBeVerifiedData,
&ToBeVerifiedCert);
if (ret != KMF_OK)
goto cleanup;
algid = x509_algoid_to_algid(CERT_SIG_OID(ToBeVerifiedCert));
if (algid == KMF_ALGID_SHA1WithDSA ||
algid == KMF_ALGID_SHA256WithDSA) {
ret = DerDecodeDSASignature(&signed_data, &signature);
if (ret != KMF_OK)
goto cleanup;
} else if (algid == KMF_ALGID_SHA1WithECDSA ||
algid == KMF_ALGID_SHA256WithECDSA ||
algid == KMF_ALGID_SHA384WithECDSA ||
algid == KMF_ALGID_SHA512WithECDSA) {
ret = DerDecodeECDSASignature(&signed_data, &signature);
if (ret != KMF_OK)
goto cleanup;
} else {
signature.Data = signed_data.Data;
signature.Length = signed_data.Length;
}
ret = DerDecodeSignedCertificate(SignerCertData, &SignerCert);
if (ret != KMF_OK)
goto cleanup;
/*
* Force use of PKCS11 API for kcfd/libelfsign. This is
* required for the Cryptographic Framework's FIPS-140 boundary.
*/
ret = PKCS_VerifyData(handle, algid,
&SignerCert->certificate.subjectPublicKeyInfo,
&data_to_verify, &signature);
cleanup:
kmf_free_data(&data_to_verify);
kmf_free_data(&signed_data);
if (SignerCert) {
kmf_free_signed_cert(SignerCert);
free(SignerCert);
}
if (ToBeVerifiedCert) {
kmf_free_signed_cert(ToBeVerifiedCert);
free(ToBeVerifiedCert);
}
if (algid == KMF_ALGID_SHA1WithDSA ||
algid == KMF_ALGID_SHA256WithDSA ||
algid == KMF_ALGID_SHA1WithECDSA ||
algid == KMF_ALGID_SHA256WithECDSA ||
algid == KMF_ALGID_SHA384WithECDSA ||
algid == KMF_ALGID_SHA512WithECDSA) {
free(signature.Data);
}
return (ret);
}