diff options
Diffstat (limited to '')
-rw-r--r-- | security/nss/lib/dev/ckhelper.c | 595 |
1 files changed, 595 insertions, 0 deletions
diff --git a/security/nss/lib/dev/ckhelper.c b/security/nss/lib/dev/ckhelper.c new file mode 100644 index 0000000000..8ebf58fabd --- /dev/null +++ b/security/nss/lib/dev/ckhelper.c @@ -0,0 +1,595 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ + +#include "pkcs11.h" + +#ifndef DEVM_H +#include "devm.h" +#endif /* DEVM_H */ + +#ifndef CKHELPER_H +#include "ckhelper.h" +#endif /* CKHELPER_H */ + +extern const NSSError NSS_ERROR_DEVICE_ERROR; + +static const CK_BBOOL s_true = CK_TRUE; +NSS_IMPLEMENT_DATA const NSSItem + g_ck_true = { (CK_VOID_PTR)&s_true, sizeof(s_true) }; + +static const CK_BBOOL s_false = CK_FALSE; +NSS_IMPLEMENT_DATA const NSSItem + g_ck_false = { (CK_VOID_PTR)&s_false, sizeof(s_false) }; + +static const CK_OBJECT_CLASS s_class_cert = CKO_CERTIFICATE; +NSS_IMPLEMENT_DATA const NSSItem + g_ck_class_cert = { (CK_VOID_PTR)&s_class_cert, sizeof(s_class_cert) }; + +static const CK_OBJECT_CLASS s_class_pubkey = CKO_PUBLIC_KEY; +NSS_IMPLEMENT_DATA const NSSItem + g_ck_class_pubkey = { (CK_VOID_PTR)&s_class_pubkey, sizeof(s_class_pubkey) }; + +static const CK_OBJECT_CLASS s_class_privkey = CKO_PRIVATE_KEY; +NSS_IMPLEMENT_DATA const NSSItem + g_ck_class_privkey = { (CK_VOID_PTR)&s_class_privkey, sizeof(s_class_privkey) }; + +static PRBool +is_string_attribute( + CK_ATTRIBUTE_TYPE aType) +{ + PRBool isString; + switch (aType) { + case CKA_LABEL: + case CKA_NSS_EMAIL: + isString = PR_TRUE; + break; + default: + isString = PR_FALSE; + break; + } + return isString; +} + +NSS_IMPLEMENT PRStatus +nssCKObject_GetAttributes( + CK_OBJECT_HANDLE object, + CK_ATTRIBUTE_PTR obj_template, + CK_ULONG count, + NSSArena *arenaOpt, + nssSession *session, + NSSSlot *slot) +{ + nssArenaMark *mark = NULL; + CK_SESSION_HANDLE hSession; + CK_ULONG i = 0; + CK_RV ckrv; + PRStatus nssrv; + PRBool alloced = PR_FALSE; + void *epv = nssSlot_GetCryptokiEPV(slot); + hSession = session->handle; + if (arenaOpt) { + mark = nssArena_Mark(arenaOpt); + if (!mark) { + goto loser; + } + } + nssSession_EnterMonitor(session); + /* XXX kinda hacky, if the storage size is already in the first template + * item, then skip the alloc portion + */ + if (obj_template[0].ulValueLen == 0) { + /* Get the storage size needed for each attribute */ + ckrv = CKAPI(epv)->C_GetAttributeValue(hSession, + object, obj_template, count); + if (ckrv != CKR_OK && + ckrv != CKR_ATTRIBUTE_TYPE_INVALID && + ckrv != CKR_ATTRIBUTE_SENSITIVE) { + nssSession_ExitMonitor(session); + nss_SetError(NSS_ERROR_DEVICE_ERROR); + goto loser; + } + /* Allocate memory for each attribute. */ + for (i = 0; i < count; i++) { + CK_ULONG ulValueLen = obj_template[i].ulValueLen; + if (ulValueLen == 0 || ulValueLen == (CK_ULONG)-1) { + obj_template[i].pValue = NULL; + obj_template[i].ulValueLen = 0; + continue; + } + if (is_string_attribute(obj_template[i].type)) { + ulValueLen++; + } + obj_template[i].pValue = nss_ZAlloc(arenaOpt, ulValueLen); + if (!obj_template[i].pValue) { + nssSession_ExitMonitor(session); + goto loser; + } + } + alloced = PR_TRUE; + } + /* Obtain the actual attribute values. */ + ckrv = CKAPI(epv)->C_GetAttributeValue(hSession, + object, obj_template, count); + nssSession_ExitMonitor(session); + if (ckrv != CKR_OK && + ckrv != CKR_ATTRIBUTE_TYPE_INVALID && + ckrv != CKR_ATTRIBUTE_SENSITIVE) { + nss_SetError(NSS_ERROR_DEVICE_ERROR); + goto loser; + } + if (alloced && arenaOpt) { + nssrv = nssArena_Unmark(arenaOpt, mark); + if (nssrv != PR_SUCCESS) { + goto loser; + } + } + + if (count > 1 && ((ckrv == CKR_ATTRIBUTE_TYPE_INVALID) || + (ckrv == CKR_ATTRIBUTE_SENSITIVE))) { + /* old tokens would keep the length of '0' and not deal with any + * of the attributes we passed. For those tokens read them one at + * a time */ + for (i = 0; i < count; i++) { + if ((obj_template[i].ulValueLen == 0) || + (obj_template[i].ulValueLen == -1)) { + obj_template[i].ulValueLen = 0; + (void)nssCKObject_GetAttributes(object, &obj_template[i], 1, + arenaOpt, session, slot); + } + } + } + return PR_SUCCESS; +loser: + if (alloced) { + if (arenaOpt) { + /* release all arena memory allocated before the failure. */ + (void)nssArena_Release(arenaOpt, mark); + } else { + CK_ULONG j; + /* free each heap object that was allocated before the failure. */ + for (j = 0; j < i; j++) { + nss_ZFreeIf(obj_template[j].pValue); + } + } + } + return PR_FAILURE; +} + +NSS_IMPLEMENT PRStatus +nssCKObject_GetAttributeItem( + CK_OBJECT_HANDLE object, + CK_ATTRIBUTE_TYPE attribute, + NSSArena *arenaOpt, + nssSession *session, + NSSSlot *slot, + NSSItem *rvItem) +{ + CK_ATTRIBUTE attr = { 0, NULL, 0 }; + PRStatus nssrv; + attr.type = attribute; + nssrv = nssCKObject_GetAttributes(object, &attr, 1, + arenaOpt, session, slot); + if (nssrv != PR_SUCCESS) { + return nssrv; + } + rvItem->data = (void *)attr.pValue; + rvItem->size = (PRUint32)attr.ulValueLen; + return PR_SUCCESS; +} + +NSS_IMPLEMENT PRBool +nssCKObject_IsAttributeTrue( + CK_OBJECT_HANDLE object, + CK_ATTRIBUTE_TYPE attribute, + nssSession *session, + NSSSlot *slot, + PRStatus *rvStatus) +{ + CK_BBOOL bool; + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE atemplate = { 0, NULL, 0 }; + CK_RV ckrv; + void *epv = nssSlot_GetCryptokiEPV(slot); + attr = &atemplate; + NSS_CK_SET_ATTRIBUTE_VAR(attr, attribute, bool); + nssSession_EnterMonitor(session); + ckrv = CKAPI(epv)->C_GetAttributeValue(session->handle, object, + &atemplate, 1); + nssSession_ExitMonitor(session); + if (ckrv != CKR_OK) { + *rvStatus = PR_FAILURE; + return PR_FALSE; + } + *rvStatus = PR_SUCCESS; + return (PRBool)(bool == CK_TRUE); +} + +NSS_IMPLEMENT PRStatus +nssCKObject_SetAttributes( + CK_OBJECT_HANDLE object, + CK_ATTRIBUTE_PTR obj_template, + CK_ULONG count, + nssSession *session, + NSSSlot *slot) +{ + CK_RV ckrv; + void *epv = nssSlot_GetCryptokiEPV(slot); + nssSession_EnterMonitor(session); + ckrv = CKAPI(epv)->C_SetAttributeValue(session->handle, object, + obj_template, count); + nssSession_ExitMonitor(session); + if (ckrv == CKR_OK) { + return PR_SUCCESS; + } else { + return PR_FAILURE; + } +} + +NSS_IMPLEMENT PRBool +nssCKObject_IsTokenObjectTemplate( + CK_ATTRIBUTE_PTR objectTemplate, + CK_ULONG otsize) +{ + CK_ULONG ul; + for (ul = 0; ul < otsize; ul++) { + if (objectTemplate[ul].type == CKA_TOKEN) { + return (*((CK_BBOOL *)objectTemplate[ul].pValue) == CK_TRUE); + } + } + return PR_FALSE; +} + +static NSSCertificateType +nss_cert_type_from_ck_attrib(CK_ATTRIBUTE_PTR attrib) +{ + CK_CERTIFICATE_TYPE ckCertType; + if (!attrib->pValue) { + /* default to PKIX */ + return NSSCertificateType_PKIX; + } + ckCertType = *((CK_ULONG *)attrib->pValue); + switch (ckCertType) { + case CKC_X_509: + return NSSCertificateType_PKIX; + default: + break; + } + return NSSCertificateType_Unknown; +} + +/* incoming pointers must be valid */ +NSS_IMPLEMENT PRStatus +nssCryptokiCertificate_GetAttributes( + nssCryptokiObject *certObject, + nssSession *sessionOpt, + NSSArena *arenaOpt, + NSSCertificateType *certTypeOpt, + NSSItem *idOpt, + NSSDER *encodingOpt, + NSSDER *issuerOpt, + NSSDER *serialOpt, + NSSDER *subjectOpt) +{ + PRStatus status; + PRUint32 i; + nssSession *session; + NSSSlot *slot; + CK_ULONG template_size; + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE cert_template[6]; + /* Set up a template of all options chosen by caller */ + NSS_CK_TEMPLATE_START(cert_template, attr, template_size); + if (certTypeOpt) { + NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_CERTIFICATE_TYPE); + } + if (idOpt) { + NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_ID); + } + if (encodingOpt) { + NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_VALUE); + } + if (issuerOpt) { + NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_ISSUER); + } + if (serialOpt) { + NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_SERIAL_NUMBER); + } + if (subjectOpt) { + NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_SUBJECT); + } + NSS_CK_TEMPLATE_FINISH(cert_template, attr, template_size); + if (template_size == 0) { + /* caller didn't want anything */ + return PR_SUCCESS; + } + + status = nssToken_GetCachedObjectAttributes(certObject->token, arenaOpt, + certObject, CKO_CERTIFICATE, + cert_template, template_size); + if (status != PR_SUCCESS) { + + session = sessionOpt ? sessionOpt + : nssToken_GetDefaultSession(certObject->token); + if (!session) { + nss_SetError(NSS_ERROR_INVALID_ARGUMENT); + return PR_FAILURE; + } + + slot = nssToken_GetSlot(certObject->token); + status = nssCKObject_GetAttributes(certObject->handle, + cert_template, template_size, + arenaOpt, session, slot); + nssSlot_Destroy(slot); + if (status != PR_SUCCESS) { + return status; + } + } + + i = 0; + if (certTypeOpt) { + *certTypeOpt = nss_cert_type_from_ck_attrib(&cert_template[i]); + i++; + } + if (idOpt) { + NSS_CK_ATTRIBUTE_TO_ITEM(&cert_template[i], idOpt); + i++; + } + if (encodingOpt) { + NSS_CK_ATTRIBUTE_TO_ITEM(&cert_template[i], encodingOpt); + i++; + } + if (issuerOpt) { + NSS_CK_ATTRIBUTE_TO_ITEM(&cert_template[i], issuerOpt); + i++; + } + if (serialOpt) { + NSS_CK_ATTRIBUTE_TO_ITEM(&cert_template[i], serialOpt); + i++; + } + if (subjectOpt) { + NSS_CK_ATTRIBUTE_TO_ITEM(&cert_template[i], subjectOpt); + i++; + } + return PR_SUCCESS; +} + +static nssTrustLevel +get_nss_trust( + CK_TRUST ckt) +{ + nssTrustLevel t; + switch (ckt) { + case CKT_NSS_NOT_TRUSTED: + t = nssTrustLevel_NotTrusted; + break; + case CKT_NSS_TRUSTED_DELEGATOR: + t = nssTrustLevel_TrustedDelegator; + break; + case CKT_NSS_VALID_DELEGATOR: + t = nssTrustLevel_ValidDelegator; + break; + case CKT_NSS_TRUSTED: + t = nssTrustLevel_Trusted; + break; + case CKT_NSS_MUST_VERIFY_TRUST: + t = nssTrustLevel_MustVerify; + break; + case CKT_NSS_TRUST_UNKNOWN: + default: + t = nssTrustLevel_Unknown; + break; + } + return t; +} + +NSS_IMPLEMENT PRStatus +nssCryptokiTrust_GetAttributes( + nssCryptokiObject *trustObject, + nssSession *sessionOpt, + NSSItem *sha1_hash, + nssTrustLevel *serverAuth, + nssTrustLevel *clientAuth, + nssTrustLevel *codeSigning, + nssTrustLevel *emailProtection, + PRBool *stepUpApproved) +{ + PRStatus status; + NSSSlot *slot; + nssSession *session; + CK_BBOOL isToken = PR_FALSE; + /* default values if the trust is record does not exist. In the highly + * unlikely case that these change, be sure to update softoken's + * 'sftkdb_isNullTrust()' function */ + CK_BBOOL stepUp = PR_FALSE; + CK_TRUST saTrust = CKT_NSS_TRUST_UNKNOWN; + CK_TRUST caTrust = CKT_NSS_TRUST_UNKNOWN; + CK_TRUST epTrust = CKT_NSS_TRUST_UNKNOWN; + CK_TRUST csTrust = CKT_NSS_TRUST_UNKNOWN; + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE trust_template[7]; + CK_ATTRIBUTE_PTR sha1_hash_attr; + CK_ULONG trust_size; + + /* Use the trust object to find the trust settings */ + NSS_CK_TEMPLATE_START(trust_template, attr, trust_size); + NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TOKEN, isToken); + NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_SERVER_AUTH, saTrust); + NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_CLIENT_AUTH, caTrust); + NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_EMAIL_PROTECTION, epTrust); + NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_CODE_SIGNING, csTrust); + NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_STEP_UP_APPROVED, stepUp); + sha1_hash_attr = attr; + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CERT_SHA1_HASH, sha1_hash); + NSS_CK_TEMPLATE_FINISH(trust_template, attr, trust_size); + + status = nssToken_GetCachedObjectAttributes(trustObject->token, NULL, + trustObject, + CKO_NSS_TRUST, + trust_template, trust_size); + if (status != PR_SUCCESS) { + session = sessionOpt ? sessionOpt + : nssToken_GetDefaultSession(trustObject->token); + if (!session) { + nss_SetError(NSS_ERROR_INVALID_ARGUMENT); + return PR_FAILURE; + } + + slot = nssToken_GetSlot(trustObject->token); + status = nssCKObject_GetAttributes(trustObject->handle, + trust_template, trust_size, + NULL, session, slot); + nssSlot_Destroy(slot); + if (status != PR_SUCCESS) { + return status; + } + } + + if (sha1_hash_attr->ulValueLen == -1) { + /* The trust object does not have the CKA_CERT_SHA1_HASH attribute. */ + sha1_hash_attr->ulValueLen = 0; + } + sha1_hash->size = sha1_hash_attr->ulValueLen; + *serverAuth = get_nss_trust(saTrust); + *clientAuth = get_nss_trust(caTrust); + *emailProtection = get_nss_trust(epTrust); + *codeSigning = get_nss_trust(csTrust); + *stepUpApproved = stepUp; + return PR_SUCCESS; +} + +NSS_IMPLEMENT PRStatus +nssCryptokiCRL_GetAttributes( + nssCryptokiObject *crlObject, + nssSession *sessionOpt, + NSSArena *arenaOpt, + NSSItem *encodingOpt, + NSSItem *subjectOpt, + CK_ULONG *crl_class, + NSSUTF8 **urlOpt, + PRBool *isKRLOpt) +{ + PRStatus status; + NSSSlot *slot; + nssSession *session; + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE crl_template[7]; + CK_ULONG crl_size; + PRUint32 i; + + NSS_CK_TEMPLATE_START(crl_template, attr, crl_size); + if (crl_class) { + NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_CLASS); + } + if (encodingOpt) { + NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_VALUE); + } + if (urlOpt) { + NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_NSS_URL); + } + if (isKRLOpt) { + NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_NSS_KRL); + } + if (subjectOpt) { + NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_SUBJECT); + } + NSS_CK_TEMPLATE_FINISH(crl_template, attr, crl_size); + + status = nssToken_GetCachedObjectAttributes(crlObject->token, NULL, + crlObject, + CKO_NSS_CRL, + crl_template, crl_size); + if (status != PR_SUCCESS) { + session = sessionOpt ? sessionOpt + : nssToken_GetDefaultSession(crlObject->token); + if (session == NULL) { + nss_SetError(NSS_ERROR_INVALID_ARGUMENT); + return PR_FAILURE; + } + + slot = nssToken_GetSlot(crlObject->token); + status = nssCKObject_GetAttributes(crlObject->handle, + crl_template, crl_size, + arenaOpt, session, slot); + nssSlot_Destroy(slot); + if (status != PR_SUCCESS) { + return status; + } + } + + i = 0; + if (crl_class) { + NSS_CK_ATTRIBUTE_TO_ULONG(&crl_template[i], *crl_class); + i++; + } + if (encodingOpt) { + NSS_CK_ATTRIBUTE_TO_ITEM(&crl_template[i], encodingOpt); + i++; + } + if (urlOpt) { + NSS_CK_ATTRIBUTE_TO_UTF8(&crl_template[i], *urlOpt); + i++; + } + if (isKRLOpt) { + NSS_CK_ATTRIBUTE_TO_BOOL(&crl_template[i], *isKRLOpt); + i++; + } + if (subjectOpt) { + NSS_CK_ATTRIBUTE_TO_ITEM(&crl_template[i], subjectOpt); + i++; + } + return PR_SUCCESS; +} + +NSS_IMPLEMENT PRStatus +nssCryptokiPrivateKey_SetCertificate( + nssCryptokiObject *keyObject, + nssSession *sessionOpt, + const NSSUTF8 *nickname, + NSSItem *id, + NSSDER *subject) +{ + CK_RV ckrv; + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE key_template[3]; + CK_ULONG key_size; + void *epv = nssToken_GetCryptokiEPV(keyObject->token); + nssSession *session; + NSSToken *token = keyObject->token; + nssSession *defaultSession = nssToken_GetDefaultSession(token); + PRBool createdSession = PR_FALSE; + + NSS_CK_TEMPLATE_START(key_template, attr, key_size); + NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_LABEL, nickname); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ID, id); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SUBJECT, subject); + NSS_CK_TEMPLATE_FINISH(key_template, attr, key_size); + + if (sessionOpt) { + if (!nssSession_IsReadWrite(sessionOpt)) { + return PR_FAILURE; + } + session = sessionOpt; + } else if (defaultSession && nssSession_IsReadWrite(defaultSession)) { + session = defaultSession; + } else { + NSSSlot *slot = nssToken_GetSlot(token); + session = nssSlot_CreateSession(token->slot, NULL, PR_TRUE); + nssSlot_Destroy(slot); + if (!session) { + return PR_FAILURE; + } + createdSession = PR_TRUE; + } + + ckrv = CKAPI(epv)->C_SetAttributeValue(session->handle, + keyObject->handle, + key_template, + key_size); + + if (createdSession) { + nssSession_Destroy(session); + } + + return (ckrv == CKR_OK) ? PR_SUCCESS : PR_FAILURE; +} |