From 26a029d407be480d791972afb5975cf62c9360a6 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Fri, 19 Apr 2024 02:47:55 +0200 Subject: Adding upstream version 124.0.1. Signed-off-by: Daniel Baumann --- security/nss/lib/dev/Makefile | 27 + security/nss/lib/dev/ckhelper.c | 595 +++++++++++++++ security/nss/lib/dev/ckhelper.h | 148 ++++ security/nss/lib/dev/dev.gyp | 26 + security/nss/lib/dev/dev.h | 751 ++++++++++++++++++ security/nss/lib/dev/devm.h | 157 ++++ security/nss/lib/dev/devslot.c | 322 ++++++++ security/nss/lib/dev/devt.h | 161 ++++ security/nss/lib/dev/devtm.h | 25 + security/nss/lib/dev/devtoken.c | 1553 ++++++++++++++++++++++++++++++++++++++ security/nss/lib/dev/devutil.c | 998 ++++++++++++++++++++++++ security/nss/lib/dev/exports.gyp | 31 + security/nss/lib/dev/manifest.mn | 33 + security/nss/lib/dev/nssdev.h | 36 + security/nss/lib/dev/nssdevt.h | 36 + 15 files changed, 4899 insertions(+) create mode 100644 security/nss/lib/dev/Makefile create mode 100644 security/nss/lib/dev/ckhelper.c create mode 100644 security/nss/lib/dev/ckhelper.h create mode 100644 security/nss/lib/dev/dev.gyp create mode 100644 security/nss/lib/dev/dev.h create mode 100644 security/nss/lib/dev/devm.h create mode 100644 security/nss/lib/dev/devslot.c create mode 100644 security/nss/lib/dev/devt.h create mode 100644 security/nss/lib/dev/devtm.h create mode 100644 security/nss/lib/dev/devtoken.c create mode 100644 security/nss/lib/dev/devutil.c create mode 100644 security/nss/lib/dev/exports.gyp create mode 100644 security/nss/lib/dev/manifest.mn create mode 100644 security/nss/lib/dev/nssdev.h create mode 100644 security/nss/lib/dev/nssdevt.h (limited to 'security/nss/lib/dev') diff --git a/security/nss/lib/dev/Makefile b/security/nss/lib/dev/Makefile new file mode 100644 index 0000000000..f41333402c --- /dev/null +++ b/security/nss/lib/dev/Makefile @@ -0,0 +1,27 @@ +# +# 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 manifest.mn + +include $(CORE_DEPTH)/coreconf/config.mk + +ifdef BUILD_IDG +DEFINES += -DNSSDEBUG +endif + +include $(CORE_DEPTH)/coreconf/rules.mk + +# On AIX 4.3, IBM xlC_r compiler (version 3.6.6) cannot compile +# ckhelper.c in 64-bit mode for unknown reasons. A workaround is +# to compile it with optimizations turned on. (Bugzilla bug #63815) +ifeq ($(OS_TARGET)$(OS_RELEASE),AIX4.3) +ifeq ($(USE_64),1) +ifndef BUILD_OPT +$(OBJDIR)/ckhelper.o: ckhelper.c | $$(@D)/d + $(CC) -o $@ -c -O2 $(CFLAGS) $< +endif +endif +endif + 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; +} diff --git a/security/nss/lib/dev/ckhelper.h b/security/nss/lib/dev/ckhelper.h new file mode 100644 index 0000000000..169fc207db --- /dev/null +++ b/security/nss/lib/dev/ckhelper.h @@ -0,0 +1,148 @@ +/* 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/. */ + +/* + * ckhelper.h + * + * This file contains some helper utilities for interaction with cryptoki. + */ + +#ifndef CKHELPER_H +#define CKHELPER_H + +PR_BEGIN_EXTERN_C + +/* Some globals to keep from constantly redeclaring common cryptoki + * attribute types on the stack. + */ + +/* Boolean values */ +NSS_EXTERN_DATA const NSSItem g_ck_true; +NSS_EXTERN_DATA const NSSItem g_ck_false; + +/* Object classes */ +NSS_EXTERN_DATA const NSSItem g_ck_class_cert; +NSS_EXTERN_DATA const NSSItem g_ck_class_pubkey; +NSS_EXTERN_DATA const NSSItem g_ck_class_privkey; + +#define NSS_CK_TEMPLATE_START(_template, attr, size) \ + attr = _template; \ + size = 0; + +#define NSS_CK_SET_ATTRIBUTE_ITEM(pattr, kind, item) \ + (pattr)->type = kind; \ + (pattr)->pValue = (CK_VOID_PTR)(item)->data; \ + (pattr)->ulValueLen = (CK_ULONG)(item)->size; \ + (pattr)++; + +#define NSS_CK_SET_ATTRIBUTE_UTF8(pattr, kind, utf8) \ + (pattr)->type = kind; \ + (pattr)->pValue = (CK_VOID_PTR)utf8; \ + (pattr)->ulValueLen = (CK_ULONG)nssUTF8_Size(utf8, NULL); \ + if ((pattr)->ulValueLen) \ + ((pattr)->ulValueLen)--; \ + (pattr)++; + +#define NSS_CK_SET_ATTRIBUTE_VAR(pattr, kind, var) \ + (pattr)->type = kind; \ + (pattr)->pValue = (CK_VOID_PTR)&var; \ + (pattr)->ulValueLen = (CK_ULONG)sizeof(var); \ + (pattr)++; + +#define NSS_CK_SET_ATTRIBUTE_NULL(pattr, kind) \ + (pattr)->type = kind; \ + (pattr)->pValue = (CK_VOID_PTR)NULL; \ + (pattr)->ulValueLen = 0; \ + (pattr)++; + +#define NSS_CK_TEMPLATE_FINISH(_template, attr, size) \ + size = (attr) - (_template); \ + PR_ASSERT(size <= sizeof(_template) / sizeof(_template[0])); + +/* NSS_CK_ATTRIBUTE_TO_ITEM(attrib, item) + * + * Convert a CK_ATTRIBUTE to an NSSItem. + */ +#define NSS_CK_ATTRIBUTE_TO_ITEM(attrib, item) \ + if ((CK_LONG)(attrib)->ulValueLen > 0) { \ + (item)->data = (void *)(attrib)->pValue; \ + (item)->size = (PRUint32)(attrib)->ulValueLen; \ + } else { \ + (item)->data = 0; \ + (item)->size = 0; \ + } + +#define NSS_CK_ATTRIBUTE_TO_BOOL(attrib, boolvar) \ + if ((attrib)->ulValueLen > 0) { \ + if (*((CK_BBOOL *)(attrib)->pValue) == CK_TRUE) { \ + boolvar = PR_TRUE; \ + } else { \ + boolvar = PR_FALSE; \ + } \ + } + +#define NSS_CK_ATTRIBUTE_TO_ULONG(attrib, ulongvar) \ + if ((attrib)->ulValueLen > 0) { \ + ulongvar = *((CK_ULONG *)(attrib)->pValue); \ + } + +/* NSS_CK_ATTRIBUTE_TO_UTF8(attrib, str) + * + * Convert a CK_ATTRIBUTE to a string. + */ +#define NSS_CK_ATTRIBUTE_TO_UTF8(attrib, str) \ + str = (NSSUTF8 *)((attrib)->pValue); + +/* NSS_CK_ITEM_TO_ATTRIBUTE(item, attrib) + * + * Convert an NSSItem to a CK_ATTRIBUTE. + */ +#define NSS_CK_ITEM_TO_ATTRIBUTE(item, attrib) \ + (attrib)->pValue = (CK_VOID_PTR)(item)->data; \ + (attrib)->ulValueLen = (CK_ULONG)(item)->size; + +/* Get an array of attributes from an object. */ +NSS_EXTERN PRStatus +nssCKObject_GetAttributes( + CK_OBJECT_HANDLE object, + CK_ATTRIBUTE_PTR obj_template, + CK_ULONG count, + NSSArena *arenaOpt, + nssSession *session, + NSSSlot *slot); + +/* Get a single attribute as an item. */ +NSS_EXTERN PRStatus +nssCKObject_GetAttributeItem( + CK_OBJECT_HANDLE object, + CK_ATTRIBUTE_TYPE attribute, + NSSArena *arenaOpt, + nssSession *session, + NSSSlot *slot, + NSSItem *rvItem); + +NSS_EXTERN PRBool +nssCKObject_IsAttributeTrue( + CK_OBJECT_HANDLE object, + CK_ATTRIBUTE_TYPE attribute, + nssSession *session, + NSSSlot *slot, + PRStatus *rvStatus); + +NSS_EXTERN PRStatus +nssCKObject_SetAttributes( + CK_OBJECT_HANDLE object, + CK_ATTRIBUTE_PTR obj_template, + CK_ULONG count, + nssSession *session, + NSSSlot *slot); + +NSS_EXTERN PRBool +nssCKObject_IsTokenObjectTemplate( + CK_ATTRIBUTE_PTR objectTemplate, + CK_ULONG otsize); + +PR_END_EXTERN_C + +#endif /* CKHELPER_H */ diff --git a/security/nss/lib/dev/dev.gyp b/security/nss/lib/dev/dev.gyp new file mode 100644 index 0000000000..dfa8f7e3e6 --- /dev/null +++ b/security/nss/lib/dev/dev.gyp @@ -0,0 +1,26 @@ +# 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/. +{ + 'includes': [ + '../../coreconf/config.gypi' + ], + 'targets': [ + { + 'target_name': 'nssdev', + 'type': 'static_library', + 'sources': [ + 'ckhelper.c', + 'devslot.c', + 'devtoken.c', + 'devutil.c' + ], + 'dependencies': [ + '<(DEPTH)/exports.gyp:nss_exports' + ] + } + ], + 'variables': { + 'module': 'nss' + } +} \ No newline at end of file diff --git a/security/nss/lib/dev/dev.h b/security/nss/lib/dev/dev.h new file mode 100644 index 0000000000..762ea5635f --- /dev/null +++ b/security/nss/lib/dev/dev.h @@ -0,0 +1,751 @@ +/* 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/. */ + +#ifndef DEV_H +#define DEV_H + +/* + * dev.h + * + * Low-level methods for interaction with cryptoki devices + */ + +#ifndef NSSDEV_H +#include "nssdev.h" +#endif /* NSSDEV_H */ + +#ifndef DEVT_H +#include "devt.h" +#endif /* DEVT_H */ + +PR_BEGIN_EXTERN_C + +/* the global module list + * + * These functions are for managing the global set of modules. Trust Domains, + * etc., will draw from this set. These functions are completely internal + * and only invoked when there are changes to the global module state + * (load or unload). + * + * nss_InitializeGlobalModuleList + * nss_DestroyGlobalModuleList + * nss_GetLoadedModules + * + * nssGlobalModuleList_Add + * nssGlobalModuleList_Remove + * nssGlobalModuleList_FindModuleByName + * nssGlobalModuleList_FindSlotByName + * nssGlobalModuleList_FindTokenByName + */ + +NSS_EXTERN PRStatus +nss_InitializeGlobalModuleList( + void); + +NSS_EXTERN PRStatus +nss_DestroyGlobalModuleList( + void); + +NSS_EXTERN NSSModule ** +nss_GetLoadedModules( + void); + +NSS_EXTERN PRStatus +nssGlobalModuleList_Add( + NSSModule *module); + +NSS_EXTERN PRStatus +nssGlobalModuleList_Remove( + NSSModule *module); + +NSS_EXTERN NSSModule * +nssGlobalModuleList_FindModuleByName( + NSSUTF8 *moduleName); + +NSS_EXTERN NSSSlot * +nssGlobalModuleList_FindSlotByName( + NSSUTF8 *slotName); + +NSS_EXTERN NSSToken * +nssGlobalModuleList_FindTokenByName( + NSSUTF8 *tokenName); + +NSS_EXTERN NSSToken * +nss_GetDefaultCryptoToken( + void); + +NSS_EXTERN NSSToken * +nss_GetDefaultDatabaseToken( + void); + +/* + * |-----------|<---> NSSSlot <--> NSSToken + * | NSSModule |<---> NSSSlot <--> NSSToken + * |-----------|<---> NSSSlot <--> NSSToken + */ + +/* NSSModule + * + * nssModule_Create + * nssModule_CreateFromSpec + * nssModule_AddRef + * nssModule_GetName + * nssModule_GetSlots + * nssModule_FindSlotByName + * nssModule_FindTokenByName + * nssModule_GetCertOrder + */ + +NSS_EXTERN NSSModule * +nssModule_Create( + NSSUTF8 *moduleOpt, + NSSUTF8 *uriOpt, + NSSUTF8 *opaqueOpt, + void *reserved); + +/* This is to use the new loading mechanism. */ +NSS_EXTERN NSSModule * +nssModule_CreateFromSpec( + NSSUTF8 *moduleSpec, + NSSModule *parent, + PRBool loadSubModules); + +NSS_EXTERN PRStatus +nssModule_Destroy( + NSSModule *mod); + +NSS_EXTERN NSSModule * +nssModule_AddRef( + NSSModule *mod); + +NSS_EXTERN NSSUTF8 * +nssModule_GetName( + NSSModule *mod); + +NSS_EXTERN NSSSlot ** +nssModule_GetSlots( + NSSModule *mod); + +NSS_EXTERN NSSSlot * +nssModule_FindSlotByName( + NSSModule *mod, + NSSUTF8 *slotName); + +NSS_EXTERN NSSToken * +nssModule_FindTokenByName( + NSSModule *mod, + NSSUTF8 *tokenName); + +NSS_EXTERN PRInt32 +nssModule_GetCertOrder( + NSSModule *module); + +/* NSSSlot + * + * nssSlot_Destroy + * nssSlot_AddRef + * nssSlot_GetName + * nssSlot_IsTokenPresent + * nssSlot_IsPermanent + * nssSlot_IsFriendly + * nssSlot_IsHardware + * nssSlot_Refresh + * nssSlot_GetModule + * nssSlot_GetToken + * nssSlot_Login + * nssSlot_Logout + * nssSlot_SetPassword + * nssSlot_CreateSession + */ + +NSS_EXTERN PRStatus +nssSlot_Destroy( + NSSSlot *slot); + +NSS_EXTERN NSSSlot * +nssSlot_AddRef( + NSSSlot *slot); + +NSS_EXTERN void +nssSlot_ResetDelay( + NSSSlot *slot); + +NSS_EXTERN NSSUTF8 * +nssSlot_GetName( + NSSSlot *slot); + +NSS_EXTERN NSSModule * +nssSlot_GetModule( + NSSSlot *slot); + +NSS_EXTERN NSSToken * +nssSlot_GetToken( + NSSSlot *slot); + +NSS_EXTERN PRBool +nssSlot_IsTokenPresent( + NSSSlot *slot); + +NSS_EXTERN PRBool +nssSlot_IsPermanent( + NSSSlot *slot); + +NSS_EXTERN PRBool +nssSlot_IsFriendly( + NSSSlot *slot); + +NSS_EXTERN PRBool +nssSlot_IsHardware( + NSSSlot *slot); + +NSS_EXTERN PRBool +nssSlot_IsLoggedIn( + NSSSlot *slot); + +NSS_EXTERN PRStatus +nssSlot_Refresh( + NSSSlot *slot); + +NSS_EXTERN PRStatus +nssSlot_Login( + NSSSlot *slot, + NSSCallback *pwcb); +extern const NSSError NSS_ERROR_INVALID_PASSWORD; +extern const NSSError NSS_ERROR_USER_CANCELED; + +NSS_EXTERN PRStatus +nssSlot_Logout( + NSSSlot *slot, + nssSession *sessionOpt); + +NSS_EXTERN void +nssSlot_EnterMonitor( + NSSSlot *slot); + +NSS_EXTERN void +nssSlot_ExitMonitor( + NSSSlot *slot); + +#define NSSSLOT_ASK_PASSWORD_FIRST_TIME -1 +#define NSSSLOT_ASK_PASSWORD_EVERY_TIME 0 +NSS_EXTERN void +nssSlot_SetPasswordDefaults( + NSSSlot *slot, + PRInt32 askPasswordTimeout); + +NSS_EXTERN PRStatus +nssSlot_SetPassword( + NSSSlot *slot, + NSSUTF8 *oldPasswordOpt, + NSSUTF8 *newPassword); +extern const NSSError NSS_ERROR_INVALID_PASSWORD; +extern const NSSError NSS_ERROR_USER_CANCELED; + +/* + * nssSlot_IsLoggedIn + */ + +NSS_EXTERN nssSession * +nssSlot_CreateSession( + NSSSlot *slot, + NSSArena *arenaOpt, + PRBool readWrite /* so far, this is the only flag used */ +); + +/* NSSToken + * + * nssToken_Destroy + * nssToken_AddRef + * nssToken_GetName + * nssToken_GetModule + * nssToken_GetSlot + * nssToken_NeedsPINInitialization + * nssToken_ImportCertificate + * nssToken_ImportTrust + * nssToken_ImportCRL + * nssToken_GenerateKeyPair + * nssToken_GenerateSymmetricKey + * nssToken_DeleteStoredObject + * nssToken_FindObjects + * nssToken_FindCertificatesBySubject + * nssToken_FindCertificatesByNickname + * nssToken_FindCertificatesByEmail + * nssToken_FindCertificateByIssuerAndSerialNumber + * nssToken_FindCertificateByEncodedCertificate + * nssToken_FindTrustForCertificate + * nssToken_FindCRLsBySubject + * nssToken_FindPrivateKeys + * nssToken_FindPrivateKeyByID + * nssToken_Digest + * nssToken_BeginDigest + * nssToken_ContinueDigest + * nssToken_FinishDigest + */ + +NSS_EXTERN PRStatus +nssToken_Destroy( + NSSToken *tok); + +NSS_EXTERN NSSToken * +nssToken_AddRef( + NSSToken *tok); + +NSS_EXTERN NSSUTF8 * +nssToken_GetName( + NSSToken *tok); + +NSS_EXTERN NSSModule * +nssToken_GetModule( + NSSToken *token); + +NSS_EXTERN NSSSlot * +nssToken_GetSlot( + NSSToken *tok); + +NSS_EXTERN PRBool +nssToken_NeedsPINInitialization( + NSSToken *token); + +NSS_EXTERN nssCryptokiObject ** +nssToken_FindObjectsByTemplate( + NSSToken *token, + nssSession *sessionOpt, + CK_ATTRIBUTE_PTR obj_template, + CK_ULONG otsize, + PRUint32 maximumOpt, + PRStatus *statusOpt); + +NSS_EXTERN nssCryptokiObject * +nssToken_ImportCertificate( + NSSToken *tok, + nssSession *sessionOpt, + NSSCertificateType certType, + NSSItem *id, + const NSSUTF8 *nickname, + NSSDER *encoding, + NSSDER *issuer, + NSSDER *subject, + NSSDER *serial, + NSSASCII7 *emailAddr, + PRBool asTokenObject); + +NSS_EXTERN nssCryptokiObject * +nssToken_ImportTrust( + NSSToken *tok, + nssSession *sessionOpt, + NSSDER *certEncoding, + NSSDER *certIssuer, + NSSDER *certSerial, + nssTrustLevel serverAuth, + nssTrustLevel clientAuth, + nssTrustLevel codeSigning, + nssTrustLevel emailProtection, + PRBool stepUpApproved, + PRBool asTokenObject); + +NSS_EXTERN nssCryptokiObject * +nssToken_ImportCRL( + NSSToken *token, + nssSession *sessionOpt, + NSSDER *subject, + NSSDER *encoding, + PRBool isKRL, + NSSUTF8 *url, + PRBool asTokenObject); + +/* Permanently remove an object from the token. */ +NSS_EXTERN PRStatus +nssToken_DeleteStoredObject( + nssCryptokiObject *instance); + +NSS_EXTERN nssCryptokiObject ** +nssToken_FindObjects( + NSSToken *token, + nssSession *sessionOpt, + CK_OBJECT_CLASS objclass, + nssTokenSearchType searchType, + PRUint32 maximumOpt, + PRStatus *statusOpt); + +NSS_EXTERN nssCryptokiObject ** +nssToken_FindCertificatesBySubject( + NSSToken *token, + nssSession *sessionOpt, + NSSDER *subject, + nssTokenSearchType searchType, + PRUint32 maximumOpt, + PRStatus *statusOpt); + +NSS_EXTERN nssCryptokiObject ** +nssToken_FindCertificatesByNickname( + NSSToken *token, + nssSession *sessionOpt, + const NSSUTF8 *name, + nssTokenSearchType searchType, + PRUint32 maximumOpt, + PRStatus *statusOpt); + +NSS_EXTERN nssCryptokiObject ** +nssToken_FindCertificatesByEmail( + NSSToken *token, + nssSession *sessionOpt, + NSSASCII7 *email, + nssTokenSearchType searchType, + PRUint32 maximumOpt, + PRStatus *statusOpt); + +NSS_EXTERN nssCryptokiObject ** +nssToken_FindCertificatesByID( + NSSToken *token, + nssSession *sessionOpt, + NSSItem *id, + nssTokenSearchType searchType, + PRUint32 maximumOpt, + PRStatus *statusOpt); + +NSS_EXTERN nssCryptokiObject * +nssToken_FindCertificateByIssuerAndSerialNumber( + NSSToken *token, + nssSession *sessionOpt, + NSSDER *issuer, + NSSDER *serial, + nssTokenSearchType searchType, + PRStatus *statusOpt); + +NSS_EXTERN nssCryptokiObject * +nssToken_FindCertificateByEncodedCertificate( + NSSToken *token, + nssSession *sessionOpt, + NSSBER *encodedCertificate, + nssTokenSearchType searchType, + PRStatus *statusOpt); + +NSS_EXTERN nssCryptokiObject * +nssToken_FindTrustForCertificate( + NSSToken *token, + nssSession *sessionOpt, + NSSDER *certEncoding, + NSSDER *certIssuer, + NSSDER *certSerial, + nssTokenSearchType searchType); + +NSS_EXTERN nssCryptokiObject ** +nssToken_FindCRLsBySubject( + NSSToken *token, + nssSession *sessionOpt, + NSSDER *subject, + nssTokenSearchType searchType, + PRUint32 maximumOpt, + PRStatus *statusOpt); + +NSS_EXTERN nssCryptokiObject ** +nssToken_FindPrivateKeys( + NSSToken *token, + nssSession *sessionOpt, + nssTokenSearchType searchType, + PRUint32 maximumOpt, + PRStatus *statusOpt); + +NSS_EXTERN nssCryptokiObject * +nssToken_FindPrivateKeyByID( + NSSToken *token, + nssSession *sessionOpt, + NSSItem *keyID); + +NSS_EXTERN nssCryptokiObject * +nssToken_FindPublicKeyByID( + NSSToken *token, + nssSession *sessionOpt, + NSSItem *keyID); + +NSS_EXTERN NSSItem * +nssToken_Digest( + NSSToken *tok, + nssSession *sessionOpt, + NSSAlgorithmAndParameters *ap, + NSSItem *data, + NSSItem *rvOpt, + NSSArena *arenaOpt); + +NSS_EXTERN PRStatus +nssToken_BeginDigest( + NSSToken *tok, + nssSession *sessionOpt, + NSSAlgorithmAndParameters *ap); + +NSS_EXTERN PRStatus +nssToken_ContinueDigest( + NSSToken *tok, + nssSession *sessionOpt, + NSSItem *item); + +NSS_EXTERN NSSItem * +nssToken_FinishDigest( + NSSToken *tok, + nssSession *sessionOpt, + NSSItem *rvOpt, + NSSArena *arenaOpt); + +/* nssSession + * + * nssSession_Destroy + * nssSession_EnterMonitor + * nssSession_ExitMonitor + * nssSession_IsReadWrite + */ + +NSS_EXTERN PRStatus +nssSession_Destroy( + nssSession *s); + +/* would like to inline */ +NSS_EXTERN PRStatus +nssSession_EnterMonitor( + nssSession *s); + +/* would like to inline */ +NSS_EXTERN PRStatus +nssSession_ExitMonitor( + nssSession *s); + +/* would like to inline */ +NSS_EXTERN PRBool +nssSession_IsReadWrite( + nssSession *s); + +/* nssCryptokiObject + * + * An object living on a cryptoki token. + * Not really proper to mix up the object types just because + * nssCryptokiObject itself is generic, but doing so anyway. + * + * nssCryptokiObject_Destroy + * nssCryptokiObject_Equal + * nssCryptokiObject_Clone + * nssCryptokiCertificate_GetAttributes + * nssCryptokiPrivateKey_GetAttributes + * nssCryptokiPublicKey_GetAttributes + * nssCryptokiTrust_GetAttributes + * nssCryptokiCRL_GetAttributes + */ + +NSS_EXTERN void +nssCryptokiObject_Destroy( + nssCryptokiObject *object); + +NSS_EXTERN PRBool +nssCryptokiObject_Equal( + nssCryptokiObject *object1, + nssCryptokiObject *object2); + +NSS_EXTERN nssCryptokiObject * +nssCryptokiObject_Clone( + nssCryptokiObject *object); + +NSS_EXTERN PRStatus +nssCryptokiCertificate_GetAttributes( + nssCryptokiObject *object, + nssSession *sessionOpt, + NSSArena *arenaOpt, + NSSCertificateType *certTypeOpt, + NSSItem *idOpt, + NSSDER *encodingOpt, + NSSDER *issuerOpt, + NSSDER *serialOpt, + NSSDER *subjectOpt); + +NSS_EXTERN PRStatus +nssCryptokiTrust_GetAttributes( + nssCryptokiObject *trustObject, + nssSession *sessionOpt, + NSSItem *sha1_hash, + nssTrustLevel *serverAuth, + nssTrustLevel *clientAuth, + nssTrustLevel *codeSigning, + nssTrustLevel *emailProtection, + PRBool *stepUpApproved); + +NSS_EXTERN PRStatus +nssCryptokiCRL_GetAttributes( + nssCryptokiObject *crlObject, + nssSession *sessionOpt, + NSSArena *arenaOpt, + NSSItem *encodingOpt, + NSSItem *subjectOpt, + CK_ULONG *crl_class, + NSSUTF8 **urlOpt, + PRBool *isKRLOpt); + +/* I'm including this to handle import of certificates in NSS 3.5. This + * function will set the cert-related attributes of a key, in order to + * associate it with a cert. Does it stay like this for 4.0? + */ +NSS_EXTERN PRStatus +nssCryptokiPrivateKey_SetCertificate( + nssCryptokiObject *keyObject, + nssSession *sessionOpt, + const NSSUTF8 *nickname, + NSSItem *id, + NSSDER *subject); + +NSS_EXTERN void +nssModuleArray_Destroy( + NSSModule **modules); + +/* nssSlotArray + * + * nssSlotArray_Destroy + */ + +NSS_EXTERN void +nssSlotArray_Destroy( + NSSSlot **slots); + +/* nssTokenArray + * + * nssTokenArray_Destroy + */ + +NSS_EXTERN void +nssTokenArray_Destroy( + NSSToken **tokens); + +/* nssCryptokiObjectArray + * + * nssCryptokiObjectArray_Destroy + */ +NSS_EXTERN void +nssCryptokiObjectArray_Destroy( + nssCryptokiObject **object); + +/* nssSlotList + * + * An ordered list of slots. The order can be anything, it is set in the + * Add methods. Perhaps it should be CreateInCertOrder, ...? + * + * nssSlotList_Create + * nssSlotList_Destroy + * nssSlotList_Add + * nssSlotList_AddModuleSlots + * nssSlotList_GetSlots + * nssSlotList_FindSlotByName + * nssSlotList_FindTokenByName + * nssSlotList_GetBestSlot + * nssSlotList_GetBestSlotForAlgorithmAndParameters + * nssSlotList_GetBestSlotForAlgorithmsAndParameters + */ + +/* nssSlotList_Create + */ +NSS_EXTERN nssSlotList * +nssSlotList_Create( + NSSArena *arenaOpt); + +/* nssSlotList_Destroy + */ +NSS_EXTERN void +nssSlotList_Destroy( + nssSlotList *slotList); + +/* nssSlotList_Add + * + * Add the given slot in the given order. + */ +NSS_EXTERN PRStatus +nssSlotList_Add( + nssSlotList *slotList, + NSSSlot *slot, + PRUint32 order); + +/* nssSlotList_AddModuleSlots + * + * Add all slots in the module, in the given order (the slots will have + * equal weight). + */ +NSS_EXTERN PRStatus +nssSlotList_AddModuleSlots( + nssSlotList *slotList, + NSSModule *module, + PRUint32 order); + +/* nssSlotList_GetSlots + */ +NSS_EXTERN NSSSlot ** +nssSlotList_GetSlots( + nssSlotList *slotList); + +/* nssSlotList_FindSlotByName + */ +NSS_EXTERN NSSSlot * +nssSlotList_FindSlotByName( + nssSlotList *slotList, + NSSUTF8 *slotName); + +/* nssSlotList_FindTokenByName + */ +NSS_EXTERN NSSToken * +nssSlotList_FindTokenByName( + nssSlotList *slotList, + NSSUTF8 *tokenName); + +/* nssSlotList_GetBestSlot + * + * The best slot is the highest ranking in order, i.e., the first in the + * list. + */ +NSS_EXTERN NSSSlot * +nssSlotList_GetBestSlot( + nssSlotList *slotList); + +/* nssSlotList_GetBestSlotForAlgorithmAndParameters + * + * Highest-ranking slot than can handle algorithm/parameters. + */ +NSS_EXTERN NSSSlot * +nssSlotList_GetBestSlotForAlgorithmAndParameters( + nssSlotList *slotList, + NSSAlgorithmAndParameters *ap); + +/* nssSlotList_GetBestSlotForAlgorithmsAndParameters + * + * Highest-ranking slot than can handle all algorithms/parameters. + */ +NSS_EXTERN NSSSlot * +nssSlotList_GetBestSlotForAlgorithmsAndParameters( + nssSlotList *slotList, + NSSAlgorithmAndParameters **ap); + +NSS_EXTERN PRBool +nssToken_IsPresent( + NSSToken *token); + +NSS_EXTERN nssSession * +nssToken_GetDefaultSession( + NSSToken *token); + +NSS_EXTERN PRStatus +nssToken_GetTrustOrder( + NSSToken *tok); + +NSS_EXTERN PRStatus +nssToken_NotifyCertsNotVisible( + NSSToken *tok); + +NSS_EXTERN PRStatus +nssToken_TraverseCertificates( + NSSToken *token, + nssSession *sessionOpt, + nssTokenSearchType searchType, + PRStatus (*callback)(nssCryptokiObject *instance, void *arg), + void *arg); + +NSS_EXTERN PRBool +nssToken_IsPrivateKeyAvailable( + NSSToken *token, + NSSCertificate *c, + nssCryptokiObject *instance); + +PR_END_EXTERN_C + +#endif /* DEV_H */ diff --git a/security/nss/lib/dev/devm.h b/security/nss/lib/dev/devm.h new file mode 100644 index 0000000000..16acf649f4 --- /dev/null +++ b/security/nss/lib/dev/devm.h @@ -0,0 +1,157 @@ +/* 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/. */ + +#ifndef DEVM_H +#define DEVM_H + +#ifndef BASE_H +#include "base.h" +#endif /* BASE_H */ + +#ifndef DEV_H +#include "dev.h" +#endif /* DEV_H */ + +#ifndef DEVTM_H +#include "devtm.h" +#endif /* DEVTM_H */ + +PR_BEGIN_EXTERN_C + +/* Shortcut to cryptoki API functions. */ +#define CKAPI(epv) \ + ((CK_FUNCTION_LIST_PTR)(epv)) + +NSS_EXTERN void +nssDevice_AddRef( + struct nssDeviceBaseStr *device); + +NSS_EXTERN PRBool +nssDevice_Destroy( + struct nssDeviceBaseStr *device); + +NSS_EXTERN PRBool +nssModule_IsThreadSafe( + NSSModule *module); + +NSS_EXTERN PRBool +nssModule_IsInternal( + NSSModule *mod); + +NSS_EXTERN PRBool +nssModule_IsModuleDBOnly( + NSSModule *mod); + +NSS_EXTERN void * +nssModule_GetCryptokiEPV( + NSSModule *mod); + +NSS_EXTERN NSSSlot * +nssSlot_Create( + CK_SLOT_ID slotId, + NSSModule *parent); + +NSS_EXTERN void * +nssSlot_GetCryptokiEPV( + NSSSlot *slot); + +NSS_EXTERN NSSToken * +nssToken_Create( + CK_SLOT_ID slotID, + NSSSlot *peer); + +NSS_EXTERN void * +nssToken_GetCryptokiEPV( + NSSToken *token); + +NSS_EXTERN nssSession * +nssToken_GetDefaultSession( + NSSToken *token); + +NSS_EXTERN PRBool +nssToken_IsLoginRequired( + NSSToken *token); + +NSS_EXTERN void +nssToken_Remove( + NSSToken *token); + +NSS_EXTERN nssCryptokiObject * +nssCryptokiObject_Create( + NSSToken *t, + nssSession *session, + CK_OBJECT_HANDLE h); + +NSS_EXTERN nssTokenObjectCache * +nssTokenObjectCache_Create( + NSSToken *token, + PRBool cacheCerts, + PRBool cacheTrust, + PRBool cacheCRLs); + +NSS_EXTERN void +nssTokenObjectCache_Destroy( + nssTokenObjectCache *cache); + +NSS_EXTERN void +nssTokenObjectCache_Clear( + nssTokenObjectCache *cache); + +NSS_EXTERN PRBool +nssTokenObjectCache_HaveObjectClass( + nssTokenObjectCache *cache, + CK_OBJECT_CLASS objclass); + +NSS_EXTERN nssCryptokiObject ** +nssTokenObjectCache_FindObjectsByTemplate( + nssTokenObjectCache *cache, + CK_OBJECT_CLASS objclass, + CK_ATTRIBUTE_PTR otemplate, + CK_ULONG otlen, + PRUint32 maximumOpt, + PRStatus *statusOpt); + +NSS_EXTERN PRStatus +nssTokenObjectCache_GetObjectAttributes( + nssTokenObjectCache *cache, + NSSArena *arenaOpt, + nssCryptokiObject *object, + CK_OBJECT_CLASS objclass, + CK_ATTRIBUTE_PTR atemplate, + CK_ULONG atlen); + +NSS_EXTERN PRStatus +nssTokenObjectCache_ImportObject( + nssTokenObjectCache *cache, + nssCryptokiObject *object, + CK_OBJECT_CLASS objclass, + CK_ATTRIBUTE_PTR ot, + CK_ULONG otlen); + +NSS_EXTERN void +nssTokenObjectCache_RemoveObject( + nssTokenObjectCache *cache, + nssCryptokiObject *object); + +/* XXX allows peek back into token */ +NSS_EXTERN PRStatus +nssToken_GetCachedObjectAttributes( + NSSToken *token, + NSSArena *arenaOpt, + nssCryptokiObject *object, + CK_OBJECT_CLASS objclass, + CK_ATTRIBUTE_PTR atemplate, + CK_ULONG atlen); + +/* PKCS#11 stores strings in a fixed-length buffer padded with spaces. This + * function gets the length of the actual string. + */ +NSS_EXTERN PRUint32 +nssPKCS11String_Length( + CK_CHAR *pkcs11str, + PRUint32 bufLen); + +PR_END_EXTERN_C + +#endif /* DEV_H */ diff --git a/security/nss/lib/dev/devslot.c b/security/nss/lib/dev/devslot.c new file mode 100644 index 0000000000..b28e7f313d --- /dev/null +++ b/security/nss/lib/dev/devslot.c @@ -0,0 +1,322 @@ +/* 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 */ + +#include "pkim.h" +#include "dev3hack.h" +#include "pk11func.h" + +/* measured in seconds */ +#define NSSSLOT_TOKEN_DELAY_TIME 1 + +/* this should track global and per-transaction login information */ + +#define NSSSLOT_IS_FRIENDLY(slot) \ + (slot->base.flags & NSSSLOT_FLAGS_FRIENDLY) + +/* measured as interval */ +static PRIntervalTime s_token_delay_time = 0; + +NSS_IMPLEMENT PRStatus +nssSlot_Destroy( + NSSSlot *slot) +{ + if (slot) { + if (PR_ATOMIC_DECREMENT(&slot->base.refCount) == 0) { + PK11_FreeSlot(slot->pk11slot); + PZ_DestroyLock(slot->base.lock); + PZ_DestroyCondVar(slot->isPresentCondition); + PZ_DestroyLock(slot->isPresentLock); + return nssArena_Destroy(slot->base.arena); + } + } + return PR_SUCCESS; +} + +void +nssSlot_EnterMonitor(NSSSlot *slot) +{ + if (slot->lock) { + PZ_Lock(slot->lock); + } +} + +void +nssSlot_ExitMonitor(NSSSlot *slot) +{ + if (slot->lock) { + PZ_Unlock(slot->lock); + } +} + +NSS_IMPLEMENT void +NSSSlot_Destroy( + NSSSlot *slot) +{ + (void)nssSlot_Destroy(slot); +} + +NSS_IMPLEMENT NSSSlot * +nssSlot_AddRef( + NSSSlot *slot) +{ + PR_ATOMIC_INCREMENT(&slot->base.refCount); + return slot; +} + +NSS_IMPLEMENT NSSUTF8 * +nssSlot_GetName( + NSSSlot *slot) +{ + return slot->base.name; +} + +NSS_IMPLEMENT void +nssSlot_ResetDelay( + NSSSlot *slot) +{ + PZ_Lock(slot->isPresentLock); + slot->lastTokenPingState = nssSlotLastPingState_Reset; + PZ_Unlock(slot->isPresentLock); +} + +static PRBool +token_status_checked(const NSSSlot *slot) +{ + PRIntervalTime time; + int lastPingState = slot->lastTokenPingState; + /* When called from the same thread, that means + * nssSlot_IsTokenPresent() is called recursively through + * nssSlot_Refresh(). Return immediately in that case. */ + if (slot->isPresentThread == PR_GetCurrentThread()) { + return PR_TRUE; + } + /* Set the delay time for checking the token presence */ + if (s_token_delay_time == 0) { + s_token_delay_time = PR_SecondsToInterval(NSSSLOT_TOKEN_DELAY_TIME); + } + time = PR_IntervalNow(); + if ((lastPingState == nssSlotLastPingState_Valid) && ((time - slot->lastTokenPingTime) < s_token_delay_time)) { + return PR_TRUE; + } + return PR_FALSE; +} + +NSS_IMPLEMENT PRBool +nssSlot_IsTokenPresent( + NSSSlot *slot) +{ + CK_RV ckrv; + PRStatus nssrv; + NSSToken *nssToken = NULL; + /* XXX */ + nssSession *session; + CK_SLOT_INFO slotInfo; + void *epv; + PRBool isPresent = PR_FALSE; + PRBool doUpdateCachedCerts = PR_FALSE; + + /* permanent slots are always present unless they're disabled */ + if (nssSlot_IsPermanent(slot)) { + return !PK11_IsDisabled(slot->pk11slot); + } + + /* avoid repeated calls to check token status within set interval */ + PZ_Lock(slot->isPresentLock); + if (token_status_checked(slot)) { + CK_FLAGS ckFlags = slot->ckFlags; + PZ_Unlock(slot->isPresentLock); + return ((ckFlags & CKF_TOKEN_PRESENT) != 0); + } + PZ_Unlock(slot->isPresentLock); + + /* First obtain the slot epv before we set up the condition + * variable, so we can just return if we couldn't get it. */ + epv = slot->epv; + if (!epv) { + return PR_FALSE; + } + + /* set up condition so only one thread is active in this part of the code at a time */ + PZ_Lock(slot->isPresentLock); + while (slot->isPresentThread) { + PR_WaitCondVar(slot->isPresentCondition, PR_INTERVAL_NO_TIMEOUT); + } + /* if we were one of multiple threads here, the first thread will have + * given us the answer, no need to make more queries of the token. */ + if (token_status_checked(slot)) { + CK_FLAGS ckFlags = slot->ckFlags; + PZ_Unlock(slot->isPresentLock); + return ((ckFlags & CKF_TOKEN_PRESENT) != 0); + } + /* this is the winning thread, block all others until we've determined + * if the token is present and that it needs initialization. */ + slot->lastTokenPingState = nssSlotLastPingState_Update; + slot->isPresentThread = PR_GetCurrentThread(); + + PZ_Unlock(slot->isPresentLock); + + nssToken = PK11Slot_GetNSSToken(slot->pk11slot); + if (!nssToken) { + isPresent = PR_FALSE; + goto done; + } + + if (PK11_GetSlotInfo(slot->pk11slot, &slotInfo) != SECSuccess) { + nssToken->base.name[0] = 0; /* XXX */ + isPresent = PR_FALSE; + goto done; + } + slot->ckFlags = slotInfo.flags; + /* check for the presence of the token */ + if ((slot->ckFlags & CKF_TOKEN_PRESENT) == 0) { + session = nssToken_GetDefaultSession(nssToken); + if (session) { + nssSession_EnterMonitor(session); + /* token is not present */ + if (session->handle != CK_INVALID_HANDLE) { + /* session is valid, close and invalidate it */ + CKAPI(epv) + ->C_CloseSession(session->handle); + session->handle = CK_INVALID_HANDLE; + } + nssSession_ExitMonitor(session); + } + if (nssToken->base.name[0] != 0) { + /* notify the high-level cache that the token is removed */ + nssToken->base.name[0] = 0; /* XXX */ + nssToken_NotifyCertsNotVisible(nssToken); + } + nssToken->base.name[0] = 0; /* XXX */ + /* clear the token cache */ + nssToken_Remove(nssToken); + isPresent = PR_FALSE; + goto done; + } + /* token is present, use the session info to determine if the card + * has been removed and reinserted. + */ + session = nssToken_GetDefaultSession(nssToken); + if (session) { + PRBool tokenRemoved; + nssSession_EnterMonitor(session); + if (session->handle != CK_INVALID_HANDLE) { + CK_SESSION_INFO sessionInfo; + ckrv = CKAPI(epv)->C_GetSessionInfo(session->handle, &sessionInfo); + if (ckrv != CKR_OK) { + /* session is screwy, close and invalidate it */ + CKAPI(epv) + ->C_CloseSession(session->handle); + session->handle = CK_INVALID_HANDLE; + } + } + tokenRemoved = (session->handle == CK_INVALID_HANDLE); + nssSession_ExitMonitor(session); + /* token not removed, finished */ + if (!tokenRemoved) { + isPresent = PR_TRUE; + goto done; + } + } + /* the token has been removed, and reinserted, or the slot contains + * a token it doesn't recognize. invalidate all the old + * information we had on this token, if we can't refresh, clear + * the present flag */ + nssToken_NotifyCertsNotVisible(nssToken); + nssToken_Remove(nssToken); + if (nssToken->base.name[0] == 0) { + doUpdateCachedCerts = PR_TRUE; + } + if (PK11_InitToken(slot->pk11slot, PR_FALSE) != SECSuccess) { + isPresent = PR_FALSE; + goto done; + } + if (doUpdateCachedCerts) { + nssTrustDomain_UpdateCachedTokenCerts(nssToken->trustDomain, + nssToken); + } + nssrv = nssToken_Refresh(nssToken); + if (nssrv != PR_SUCCESS) { + nssToken->base.name[0] = 0; /* XXX */ + slot->ckFlags &= ~CKF_TOKEN_PRESENT; + isPresent = PR_FALSE; + goto done; + } + isPresent = PR_TRUE; +done: + if (nssToken) { + (void)nssToken_Destroy(nssToken); + } + /* Once we've set up the condition variable, + * Before returning, it's necessary to: + * 1) Set the lastTokenPingTime so that any other threads waiting on this + * initialization and any future calls within the initialization window + * return the just-computed status. + * 2) Indicate we're complete, waking up all other threads that may still + * be waiting on initialization can progress. + */ + PZ_Lock(slot->isPresentLock); + /* don't update the time if we were reset while we were + * getting the token state */ + if (slot->lastTokenPingState == nssSlotLastPingState_Update) { + slot->lastTokenPingTime = PR_IntervalNow(); + slot->lastTokenPingState = nssSlotLastPingState_Valid; + } + slot->isPresentThread = NULL; + PR_NotifyAllCondVar(slot->isPresentCondition); + PZ_Unlock(slot->isPresentLock); + return isPresent; +} + +NSS_IMPLEMENT void * +nssSlot_GetCryptokiEPV( + NSSSlot *slot) +{ + return slot->epv; +} + +NSS_IMPLEMENT NSSToken * +nssSlot_GetToken( + NSSSlot *slot) +{ + NSSToken *rvToken = NULL; + + if (nssSlot_IsTokenPresent(slot)) { + rvToken = PK11Slot_GetNSSToken(slot->pk11slot); + } + + return rvToken; +} + +NSS_IMPLEMENT PRStatus +nssSession_EnterMonitor( + nssSession *s) +{ + if (s->lock) + PZ_Lock(s->lock); + return PR_SUCCESS; +} + +NSS_IMPLEMENT PRStatus +nssSession_ExitMonitor( + nssSession *s) +{ + return (s->lock) ? PZ_Unlock(s->lock) : PR_SUCCESS; +} + +NSS_EXTERN PRBool +nssSession_IsReadWrite( + nssSession *s) +{ + return s->isRW; +} diff --git a/security/nss/lib/dev/devt.h b/security/nss/lib/dev/devt.h new file mode 100644 index 0000000000..19af26f081 --- /dev/null +++ b/security/nss/lib/dev/devt.h @@ -0,0 +1,161 @@ +/* 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/. */ + +#ifndef DEVT_H +#define DEVT_H + +/* + * devt.h + * + * This file contains definitions for the low-level cryptoki devices. + */ + +#ifndef NSSBASET_H +#include "nssbaset.h" +#endif /* NSSBASET_H */ + +#ifndef NSSPKIT_H +#include "nsspkit.h" +#endif /* NSSPKIT_H */ + +#ifndef NSSDEVT_H +#include "nssdevt.h" +#endif /* NSSDEVT_H */ + +#ifndef BASET_H +#include "baset.h" +#endif /* BASET_H */ + +#include "secmodt.h" + +PR_BEGIN_EXTERN_C + +typedef struct nssSessionStr nssSession; + +/* XXX until NSSTokenStr is moved */ +struct nssDeviceBaseStr { + NSSArena *arena; + PZLock *lock; + PRInt32 refCount; + NSSUTF8 *name; + PRUint32 flags; +}; + +typedef struct nssTokenObjectCacheStr nssTokenObjectCache; + +/* XXX until devobject.c goes away */ +struct NSSTokenStr { + struct nssDeviceBaseStr base; + NSSSlot *slot; /* Parent (or peer, if you will) */ + CK_FLAGS ckFlags; /* from CK_TOKEN_INFO.flags */ + PRUint32 flags; + void *epv; + nssSession *defaultSession; + NSSTrustDomain *trustDomain; + PRIntervalTime lastTime; + nssTokenObjectCache *cache; + PK11SlotInfo *pk11slot; +}; + +typedef enum { + nssSlotAskPasswordTimes_FirstTime = 0, + nssSlotAskPasswordTimes_EveryTime = 1, + nssSlotAskPasswordTimes_Timeout = 2 +} nssSlotAskPasswordTimes; + +struct nssSlotAuthInfoStr { + PRTime lastLogin; + nssSlotAskPasswordTimes askTimes; + PRIntervalTime askPasswordTimeout; +}; + +/* values for lastTokenPingState */ +typedef enum { + nssSlotLastPingState_Reset = 0, /* the state has just been reset, discard + * our cache */ + nssSlotLastPingState_Update = 1, /* we are updating the lastTokenPingTime */ + nssSlotLastPingState_Valid = 2, /* lastTokenPingTime is valid */ +} nssSlotLastPingState; + +struct NSSSlotStr { + struct nssDeviceBaseStr base; + NSSModule *module; /* Parent */ + CK_SLOT_ID slotID; + CK_FLAGS ckFlags; /* from CK_SLOT_INFO.flags */ + struct nssSlotAuthInfoStr authInfo; + PRIntervalTime lastTokenPingTime; + nssSlotLastPingState lastTokenPingState; + PZLock *lock; + void *epv; + PK11SlotInfo *pk11slot; + PZLock *isPresentLock; + PRCondVar *isPresentCondition; + PRThread *isPresentThread; +}; + +struct nssSessionStr { + /* Must not hold slot->lock when taking lock. + * See ordering in nssSlot_IsTokenPresent. + */ + PZLock *lock; + CK_SESSION_HANDLE handle; + NSSSlot *slot; + PRBool isRW; + PRBool ownLock; +}; + +typedef enum { + NSSCertificateType_Unknown = 0, + NSSCertificateType_PKIX = 1 +} NSSCertificateType; + +typedef enum { + nssTrustLevel_Unknown = 0, + nssTrustLevel_NotTrusted = 1, + nssTrustLevel_Trusted = 2, + nssTrustLevel_TrustedDelegator = 3, + nssTrustLevel_MustVerify = 4, + nssTrustLevel_ValidDelegator = 5 +} nssTrustLevel; + +typedef struct nssCryptokiInstanceStr nssCryptokiInstance; + +struct nssCryptokiInstanceStr { + CK_OBJECT_HANDLE handle; + NSSToken *token; + PRBool isTokenObject; + NSSUTF8 *label; +}; + +typedef struct nssCryptokiInstanceStr nssCryptokiObject; + +typedef struct nssTokenCertSearchStr nssTokenCertSearch; + +typedef enum { + nssTokenSearchType_AllObjects = 0, + nssTokenSearchType_SessionOnly = 1, + nssTokenSearchType_TokenOnly = 2, + nssTokenSearchType_TokenForced = 3 +} nssTokenSearchType; + +struct nssTokenCertSearchStr { + nssTokenSearchType searchType; + PRStatus (*callback)(NSSCertificate *c, void *arg); + void *cbarg; + nssList *cached; + /* TODO: add a cache query callback if the list would be large + * (traversal) + */ +}; + +struct nssSlotListStr; +typedef struct nssSlotListStr nssSlotList; + +struct NSSAlgorithmAndParametersStr { + CK_MECHANISM mechanism; +}; + +PR_END_EXTERN_C + +#endif /* DEVT_H */ diff --git a/security/nss/lib/dev/devtm.h b/security/nss/lib/dev/devtm.h new file mode 100644 index 0000000000..ef3c4c076b --- /dev/null +++ b/security/nss/lib/dev/devtm.h @@ -0,0 +1,25 @@ +/* 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/. */ + +#ifndef DEVTM_H +#define DEVTM_H + +/* + * devtm.h + * + * This file contains module-private definitions for the low-level + * cryptoki devices. + */ + +#ifndef DEVT_H +#include "devt.h" +#endif /* DEVT_H */ + +PR_BEGIN_EXTERN_C + +#define MAX_LOCAL_CACHE_OBJECTS 10 + +PR_END_EXTERN_C + +#endif /* DEVTM_H */ diff --git a/security/nss/lib/dev/devtoken.c b/security/nss/lib/dev/devtoken.c new file mode 100644 index 0000000000..5e65dfdb1b --- /dev/null +++ b/security/nss/lib/dev/devtoken.c @@ -0,0 +1,1553 @@ +/* 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 */ + +#include "pk11func.h" +#include "dev3hack.h" +#include "secerr.h" + +extern const NSSError NSS_ERROR_NOT_FOUND; +extern const NSSError NSS_ERROR_INVALID_ARGUMENT; +extern const NSSError NSS_ERROR_PKCS11; + +/* The number of object handles to grab during each call to C_FindObjects */ +#define OBJECT_STACK_SIZE 16 + +NSS_IMPLEMENT PRStatus +nssToken_Destroy( + NSSToken *tok) +{ + if (tok) { + if (PR_ATOMIC_DECREMENT(&tok->base.refCount) == 0) { + PK11_FreeSlot(tok->pk11slot); + PZ_DestroyLock(tok->base.lock); + nssTokenObjectCache_Destroy(tok->cache); + (void)nssSlot_Destroy(tok->slot); + return nssArena_Destroy(tok->base.arena); + } + } + return PR_SUCCESS; +} + +NSS_IMPLEMENT void +nssToken_Remove( + NSSToken *tok) +{ + nssTokenObjectCache_Clear(tok->cache); +} + +NSS_IMPLEMENT NSSToken * +nssToken_AddRef( + NSSToken *tok) +{ + PR_ATOMIC_INCREMENT(&tok->base.refCount); + return tok; +} + +NSS_IMPLEMENT NSSSlot * +nssToken_GetSlot( + NSSToken *tok) +{ + return nssSlot_AddRef(tok->slot); +} + +NSS_IMPLEMENT void * +nssToken_GetCryptokiEPV( + NSSToken *token) +{ + return nssSlot_GetCryptokiEPV(token->slot); +} + +NSS_IMPLEMENT nssSession * +nssToken_GetDefaultSession( + NSSToken *token) +{ + return token->defaultSession; +} + +NSS_IMPLEMENT NSSUTF8 * +nssToken_GetName( + NSSToken *tok) +{ + if (tok == NULL) { + return ""; + } + if (tok->base.name[0] == 0) { + (void)nssSlot_IsTokenPresent(tok->slot); + } + return tok->base.name; +} + +NSS_IMPLEMENT NSSUTF8 * +NSSToken_GetName( + NSSToken *token) +{ + return nssToken_GetName(token); +} + +NSS_IMPLEMENT PRBool +nssToken_IsLoginRequired( + NSSToken *token) +{ + return (token->ckFlags & CKF_LOGIN_REQUIRED); +} + +NSS_IMPLEMENT PRBool +nssToken_NeedsPINInitialization( + NSSToken *token) +{ + return (!(token->ckFlags & CKF_USER_PIN_INITIALIZED)); +} + +NSS_IMPLEMENT PRStatus +nssToken_DeleteStoredObject( + nssCryptokiObject *instance) +{ + CK_RV ckrv; + PRStatus status; + PRBool createdSession = PR_FALSE; + NSSToken *token = instance->token; + nssSession *session = NULL; + void *epv = nssToken_GetCryptokiEPV(instance->token); + if (token->cache) { + nssTokenObjectCache_RemoveObject(token->cache, instance); + } + if (instance->isTokenObject) { + if (token->defaultSession && + nssSession_IsReadWrite(token->defaultSession)) { + session = token->defaultSession; + } else { + session = nssSlot_CreateSession(token->slot, NULL, PR_TRUE); + createdSession = PR_TRUE; + } + } + if (session == NULL) { + return PR_FAILURE; + } + nssSession_EnterMonitor(session); + ckrv = CKAPI(epv)->C_DestroyObject(session->handle, instance->handle); + nssSession_ExitMonitor(session); + if (createdSession) { + nssSession_Destroy(session); + } + status = PR_SUCCESS; + if (ckrv != CKR_OK) { + status = PR_FAILURE; + /* use the error stack to pass the PKCS #11 error out */ + nss_SetError(ckrv); + nss_SetError(NSS_ERROR_PKCS11); + } + return status; +} + +static nssCryptokiObject * +import_object( + NSSToken *tok, + nssSession *sessionOpt, + CK_ATTRIBUTE_PTR objectTemplate, + CK_ULONG otsize) +{ + nssSession *session = NULL; + PRBool createdSession = PR_FALSE; + nssCryptokiObject *object = NULL; + CK_OBJECT_HANDLE handle; + CK_RV ckrv; + void *epv = nssToken_GetCryptokiEPV(tok); + if (nssCKObject_IsTokenObjectTemplate(objectTemplate, otsize)) { + if (sessionOpt) { + if (!nssSession_IsReadWrite(sessionOpt)) { + nss_SetError(NSS_ERROR_INVALID_ARGUMENT); + return NULL; + } + session = sessionOpt; + } else if (tok->defaultSession && + nssSession_IsReadWrite(tok->defaultSession)) { + session = tok->defaultSession; + } else { + session = nssSlot_CreateSession(tok->slot, NULL, PR_TRUE); + createdSession = PR_TRUE; + } + } else { + session = (sessionOpt) ? sessionOpt : tok->defaultSession; + } + if (session == NULL) { + nss_SetError(NSS_ERROR_INVALID_ARGUMENT); + return NULL; + } + nssSession_EnterMonitor(session); + ckrv = CKAPI(epv)->C_CreateObject(session->handle, + objectTemplate, otsize, + &handle); + nssSession_ExitMonitor(session); + if (ckrv == CKR_OK) { + object = nssCryptokiObject_Create(tok, session, handle); + } else { + nss_SetError(ckrv); + nss_SetError(NSS_ERROR_PKCS11); + } + if (createdSession) { + nssSession_Destroy(session); + } + return object; +} + +static nssCryptokiObject ** +create_objects_from_handles( + NSSToken *tok, + nssSession *session, + CK_OBJECT_HANDLE *handles, + PRUint32 numH) +{ + nssCryptokiObject **objects; + objects = nss_ZNEWARRAY(NULL, nssCryptokiObject *, numH + 1); + if (objects) { + PRInt32 i; + for (i = 0; i < (PRInt32)numH; i++) { + objects[i] = nssCryptokiObject_Create(tok, session, handles[i]); + if (!objects[i]) { + for (--i; i > 0; --i) { + nssCryptokiObject_Destroy(objects[i]); + } + nss_ZFreeIf(objects); + objects = NULL; + break; + } + } + } + return objects; +} + +static nssCryptokiObject ** +find_objects( + NSSToken *tok, + nssSession *sessionOpt, + CK_ATTRIBUTE_PTR obj_template, + CK_ULONG otsize, + PRUint32 maximumOpt, + PRStatus *statusOpt) +{ + CK_RV ckrv = CKR_OK; + CK_ULONG count; + CK_OBJECT_HANDLE *objectHandles = NULL; + CK_OBJECT_HANDLE staticObjects[OBJECT_STACK_SIZE]; + PRUint32 arraySize, numHandles; + void *epv = nssToken_GetCryptokiEPV(tok); + nssCryptokiObject **objects; + nssSession *session = (sessionOpt) ? sessionOpt : tok->defaultSession; + + /* Don't ask the module to use an invalid session handle. */ + if (!session || session->handle == CK_INVALID_HANDLE) { + ckrv = CKR_SESSION_HANDLE_INVALID; + goto loser; + } + + /* the arena is only for the array of object handles */ + if (maximumOpt > 0) { + arraySize = maximumOpt; + } else { + arraySize = OBJECT_STACK_SIZE; + } + numHandles = 0; + if (arraySize <= OBJECT_STACK_SIZE) { + objectHandles = staticObjects; + } else { + objectHandles = nss_ZNEWARRAY(NULL, CK_OBJECT_HANDLE, arraySize); + } + if (!objectHandles) { + ckrv = CKR_HOST_MEMORY; + goto loser; + } + nssSession_EnterMonitor(session); /* ==== session lock === */ + /* Initialize the find with the template */ + ckrv = CKAPI(epv)->C_FindObjectsInit(session->handle, + obj_template, otsize); + if (ckrv != CKR_OK) { + nssSession_ExitMonitor(session); + goto loser; + } + while (PR_TRUE) { + /* Issue the find for up to arraySize - numHandles objects */ + ckrv = CKAPI(epv)->C_FindObjects(session->handle, + objectHandles + numHandles, + arraySize - numHandles, + &count); + if (ckrv != CKR_OK) { + nssSession_ExitMonitor(session); + goto loser; + } + /* bump the number of found objects */ + numHandles += count; + if (maximumOpt > 0 || numHandles < arraySize) { + /* When a maximum is provided, the search is done all at once, + * so the search is finished. If the number returned was less + * than the number sought, the search is finished. + */ + break; + } + /* the array is filled, double it and continue */ + arraySize *= 2; + if (objectHandles == staticObjects) { + objectHandles = nss_ZNEWARRAY(NULL, CK_OBJECT_HANDLE, arraySize); + if (objectHandles) { + PORT_Memcpy(objectHandles, staticObjects, + OBJECT_STACK_SIZE * sizeof(objectHandles[1])); + } + } else { + objectHandles = nss_ZREALLOCARRAY(objectHandles, + CK_OBJECT_HANDLE, + arraySize); + } + if (!objectHandles) { + nssSession_ExitMonitor(session); + ckrv = CKR_HOST_MEMORY; + goto loser; + } + } + ckrv = CKAPI(epv)->C_FindObjectsFinal(session->handle); + nssSession_ExitMonitor(session); /* ==== end session lock === */ + if (ckrv != CKR_OK) { + goto loser; + } + if (numHandles > 0) { + objects = create_objects_from_handles(tok, session, + objectHandles, numHandles); + } else { + nss_SetError(NSS_ERROR_NOT_FOUND); + objects = NULL; + } + if (objectHandles && objectHandles != staticObjects) { + nss_ZFreeIf(objectHandles); + } + if (statusOpt) + *statusOpt = PR_SUCCESS; + return objects; +loser: + if (objectHandles && objectHandles != staticObjects) { + nss_ZFreeIf(objectHandles); + } + /* + * These errors should be treated the same as if the objects just weren't + * found.. + */ + if ((ckrv == CKR_ATTRIBUTE_TYPE_INVALID) || + (ckrv == CKR_ATTRIBUTE_VALUE_INVALID) || + (ckrv == CKR_DATA_INVALID) || + (ckrv == CKR_DATA_LEN_RANGE) || + (ckrv == CKR_FUNCTION_NOT_SUPPORTED) || + (ckrv == CKR_TEMPLATE_INCOMPLETE) || + (ckrv == CKR_TEMPLATE_INCONSISTENT)) { + + nss_SetError(NSS_ERROR_NOT_FOUND); + if (statusOpt) + *statusOpt = PR_SUCCESS; + } else { + nss_SetError(ckrv); + nss_SetError(NSS_ERROR_PKCS11); + if (statusOpt) + *statusOpt = PR_FAILURE; + } + return (nssCryptokiObject **)NULL; +} + +NSS_IMPLEMENT nssCryptokiObject ** +nssToken_FindObjectsByTemplate( + NSSToken *token, + nssSession *sessionOpt, + CK_ATTRIBUTE_PTR obj_template, + CK_ULONG otsize, + PRUint32 maximumOpt, + PRStatus *statusOpt) +{ + CK_OBJECT_CLASS objclass = (CK_OBJECT_CLASS)-1; + nssCryptokiObject **objects = NULL; + PRUint32 i; + + if (!token) { + PORT_SetError(SEC_ERROR_NO_TOKEN); + if (statusOpt) + *statusOpt = PR_FAILURE; + return NULL; + } + for (i = 0; i < otsize; i++) { + if (obj_template[i].type == CKA_CLASS) { + objclass = *(CK_OBJECT_CLASS *)obj_template[i].pValue; + break; + } + } + PR_ASSERT(i < otsize); + if (i == otsize) { + PORT_SetError(SEC_ERROR_LIBRARY_FAILURE); + if (statusOpt) + *statusOpt = PR_FAILURE; + return NULL; + } + /* If these objects are being cached, try looking there first */ + if (token->cache && + nssTokenObjectCache_HaveObjectClass(token->cache, objclass)) { + PRStatus status; + objects = nssTokenObjectCache_FindObjectsByTemplate(token->cache, + objclass, + obj_template, + otsize, + maximumOpt, + &status); + if (status == PR_SUCCESS) { + if (statusOpt) + *statusOpt = status; + return objects; + } + } + /* Either they are not cached, or cache failed; look on token. */ + objects = find_objects(token, sessionOpt, + obj_template, otsize, + maximumOpt, statusOpt); + return objects; +} + +extern const NSSError NSS_ERROR_INVALID_CERTIFICATE; + +NSS_IMPLEMENT nssCryptokiObject * +nssToken_ImportCertificate( + NSSToken *tok, + nssSession *sessionOpt, + NSSCertificateType certType, + NSSItem *id, + const NSSUTF8 *nickname, + NSSDER *encoding, + NSSDER *issuer, + NSSDER *subject, + NSSDER *serial, + NSSASCII7 *email, + PRBool asTokenObject) +{ + PRStatus status; + CK_CERTIFICATE_TYPE cert_type; + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE cert_tmpl[10]; + CK_ULONG ctsize; + nssTokenSearchType searchType; + nssCryptokiObject *rvObject = NULL; + + if (!tok) { + PORT_SetError(SEC_ERROR_NO_TOKEN); + return NULL; + } + if (certType == NSSCertificateType_PKIX) { + cert_type = CKC_X_509; + } else { + return (nssCryptokiObject *)NULL; + } + NSS_CK_TEMPLATE_START(cert_tmpl, attr, ctsize); + if (asTokenObject) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); + searchType = nssTokenSearchType_TokenOnly; + } else { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); + searchType = nssTokenSearchType_SessionOnly; + } + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert); + NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_CERTIFICATE_TYPE, cert_type); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ID, id); + NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_LABEL, nickname); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_VALUE, encoding); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ISSUER, issuer); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SUBJECT, subject); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SERIAL_NUMBER, serial); + if (email) { + NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_NSS_EMAIL, email); + } + NSS_CK_TEMPLATE_FINISH(cert_tmpl, attr, ctsize); + /* see if the cert is already there */ + rvObject = nssToken_FindCertificateByIssuerAndSerialNumber(tok, + sessionOpt, + issuer, + serial, + searchType, + NULL); + if (rvObject) { + NSSItem existingDER; + NSSSlot *slot = nssToken_GetSlot(tok); + nssSession *session = nssSlot_CreateSession(slot, NULL, PR_TRUE); + if (!session) { + nssCryptokiObject_Destroy(rvObject); + nssSlot_Destroy(slot); + return (nssCryptokiObject *)NULL; + } + /* Reject any attempt to import a new cert that has the same + * issuer/serial as an existing cert, but does not have the + * same encoding + */ + NSS_CK_TEMPLATE_START(cert_tmpl, attr, ctsize); + NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_VALUE); + NSS_CK_TEMPLATE_FINISH(cert_tmpl, attr, ctsize); + status = nssCKObject_GetAttributes(rvObject->handle, + cert_tmpl, ctsize, NULL, + session, slot); + NSS_CK_ATTRIBUTE_TO_ITEM(cert_tmpl, &existingDER); + if (status == PR_SUCCESS) { + if (!nssItem_Equal(encoding, &existingDER, NULL)) { + nss_SetError(NSS_ERROR_INVALID_CERTIFICATE); + status = PR_FAILURE; + } + nss_ZFreeIf(existingDER.data); + } + if (status == PR_FAILURE) { + nssCryptokiObject_Destroy(rvObject); + nssSession_Destroy(session); + nssSlot_Destroy(slot); + return (nssCryptokiObject *)NULL; + } + /* according to PKCS#11, label, ID, issuer, and serial number + * may change after the object has been created. For PKIX, the + * last two attributes can't change, so for now we'll only worry + * about the first two. + */ + NSS_CK_TEMPLATE_START(cert_tmpl, attr, ctsize); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ID, id); + if (!rvObject->label && nickname) { + NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_LABEL, nickname); + } + NSS_CK_TEMPLATE_FINISH(cert_tmpl, attr, ctsize); + /* reset the mutable attributes on the token */ + nssCKObject_SetAttributes(rvObject->handle, + cert_tmpl, ctsize, + session, slot); + if (!rvObject->label && nickname) { + rvObject->label = nssUTF8_Duplicate(nickname, NULL); + } + nssSession_Destroy(session); + nssSlot_Destroy(slot); + } else { + /* Import the certificate onto the token */ + rvObject = import_object(tok, sessionOpt, cert_tmpl, ctsize); + } + if (rvObject && tok->cache) { + /* The cache will overwrite the attributes if the object already + * exists. + */ + nssTokenObjectCache_ImportObject(tok->cache, rvObject, + CKO_CERTIFICATE, + cert_tmpl, ctsize); + } + return rvObject; +} + +/* traverse all objects of the given class - this should only happen + * if the token has been marked as "traversable" + */ +NSS_IMPLEMENT nssCryptokiObject ** +nssToken_FindObjects( + NSSToken *token, + nssSession *sessionOpt, + CK_OBJECT_CLASS objclass, + nssTokenSearchType searchType, + PRUint32 maximumOpt, + PRStatus *statusOpt) +{ + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE obj_template[2]; + CK_ULONG obj_size; + nssCryptokiObject **objects; + NSS_CK_TEMPLATE_START(obj_template, attr, obj_size); + /* Set the search to token/session only if provided */ + if (searchType == nssTokenSearchType_SessionOnly) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); + } else if (searchType == nssTokenSearchType_TokenOnly || + searchType == nssTokenSearchType_TokenForced) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); + } + NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_CLASS, objclass); + NSS_CK_TEMPLATE_FINISH(obj_template, attr, obj_size); + + if (searchType == nssTokenSearchType_TokenForced) { + objects = find_objects(token, sessionOpt, + obj_template, obj_size, + maximumOpt, statusOpt); + } else { + objects = nssToken_FindObjectsByTemplate(token, sessionOpt, + obj_template, obj_size, + maximumOpt, statusOpt); + } + return objects; +} + +NSS_IMPLEMENT nssCryptokiObject ** +nssToken_FindCertificatesBySubject( + NSSToken *token, + nssSession *sessionOpt, + NSSDER *subject, + nssTokenSearchType searchType, + PRUint32 maximumOpt, + PRStatus *statusOpt) +{ + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE subj_template[3]; + CK_ULONG stsize; + nssCryptokiObject **objects; + NSS_CK_TEMPLATE_START(subj_template, attr, stsize); + /* Set the search to token/session only if provided */ + if (searchType == nssTokenSearchType_SessionOnly) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); + } else if (searchType == nssTokenSearchType_TokenOnly) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); + } + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SUBJECT, subject); + NSS_CK_TEMPLATE_FINISH(subj_template, attr, stsize); + /* now locate the token certs matching this template */ + objects = nssToken_FindObjectsByTemplate(token, sessionOpt, + subj_template, stsize, + maximumOpt, statusOpt); + return objects; +} + +NSS_IMPLEMENT nssCryptokiObject ** +nssToken_FindCertificatesByNickname( + NSSToken *token, + nssSession *sessionOpt, + const NSSUTF8 *name, + nssTokenSearchType searchType, + PRUint32 maximumOpt, + PRStatus *statusOpt) +{ + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE nick_template[3]; + CK_ULONG ntsize; + nssCryptokiObject **objects; + NSS_CK_TEMPLATE_START(nick_template, attr, ntsize); + NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_LABEL, name); + /* Set the search to token/session only if provided */ + if (searchType == nssTokenSearchType_SessionOnly) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); + } else if (searchType == nssTokenSearchType_TokenOnly) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); + } + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert); + NSS_CK_TEMPLATE_FINISH(nick_template, attr, ntsize); + /* now locate the token certs matching this template */ + objects = nssToken_FindObjectsByTemplate(token, sessionOpt, + nick_template, ntsize, + maximumOpt, statusOpt); + if (!objects) { + /* This is to workaround the fact that PKCS#11 doesn't specify + * whether the '\0' should be included. XXX Is that still true? + * im - this is not needed by the current softoken. However, I'm + * leaving it in until I have surveyed more tokens to see if it needed. + * well, its needed by the builtin token... + */ + nick_template[0].ulValueLen++; + objects = nssToken_FindObjectsByTemplate(token, sessionOpt, + nick_template, ntsize, + maximumOpt, statusOpt); + } + return objects; +} + +/* XXX + * This function *does not* use the token object cache, because not even + * the softoken will return a value for CKA_NSS_EMAIL from a call + * to GetAttributes. The softoken does allow searches with that attribute, + * it just won't return a value for it. + */ +NSS_IMPLEMENT nssCryptokiObject ** +nssToken_FindCertificatesByEmail( + NSSToken *token, + nssSession *sessionOpt, + NSSASCII7 *email, + nssTokenSearchType searchType, + PRUint32 maximumOpt, + PRStatus *statusOpt) +{ + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE email_template[3]; + CK_ULONG etsize; + nssCryptokiObject **objects; + NSS_CK_TEMPLATE_START(email_template, attr, etsize); + NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_NSS_EMAIL, email); + /* Set the search to token/session only if provided */ + if (searchType == nssTokenSearchType_SessionOnly) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); + } else if (searchType == nssTokenSearchType_TokenOnly) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); + } + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert); + NSS_CK_TEMPLATE_FINISH(email_template, attr, etsize); + /* now locate the token certs matching this template */ + objects = find_objects(token, sessionOpt, + email_template, etsize, + maximumOpt, statusOpt); + if (!objects) { + /* This is to workaround the fact that PKCS#11 doesn't specify + * whether the '\0' should be included. XXX Is that still true? + * im - this is not needed by the current softoken. However, I'm + * leaving it in until I have surveyed more tokens to see if it needed. + * well, its needed by the builtin token... + */ + email_template[0].ulValueLen++; + objects = find_objects(token, sessionOpt, + email_template, etsize, + maximumOpt, statusOpt); + } + return objects; +} + +NSS_IMPLEMENT nssCryptokiObject ** +nssToken_FindCertificatesByID( + NSSToken *token, + nssSession *sessionOpt, + NSSItem *id, + nssTokenSearchType searchType, + PRUint32 maximumOpt, + PRStatus *statusOpt) +{ + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE id_template[3]; + CK_ULONG idtsize; + nssCryptokiObject **objects; + NSS_CK_TEMPLATE_START(id_template, attr, idtsize); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ID, id); + /* Set the search to token/session only if provided */ + if (searchType == nssTokenSearchType_SessionOnly) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); + } else if (searchType == nssTokenSearchType_TokenOnly) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); + } + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert); + NSS_CK_TEMPLATE_FINISH(id_template, attr, idtsize); + /* now locate the token certs matching this template */ + objects = nssToken_FindObjectsByTemplate(token, sessionOpt, + id_template, idtsize, + maximumOpt, statusOpt); + return objects; +} + +/* + * decode the serial item and return our result. + * NOTE serialDecode's data is really stored in serial. Don't free it. + */ +static PRStatus +nssToken_decodeSerialItem(NSSItem *serial, NSSItem *serialDecode) +{ + unsigned char *data = (unsigned char *)serial->data; + int data_left, data_len, index; + + if ((serial->size >= 3) && (data[0] == 0x2)) { + /* remove the der encoding of the serial number before generating the + * key.. */ + data_left = serial->size - 2; + data_len = data[1]; + index = 2; + + /* extended length ? (not very likely for a serial number) */ + if (data_len & 0x80) { + int len_count = data_len & 0x7f; + + data_len = 0; + data_left -= len_count; + if (data_left > 0) { + while (len_count--) { + data_len = (data_len << 8) | data[index++]; + } + } + } + /* XXX leaving any leading zeros on the serial number for backwards + * compatibility + */ + /* not a valid der, must be just an unlucky serial number value */ + if (data_len == data_left) { + serialDecode->size = data_len; + serialDecode->data = &data[index]; + return PR_SUCCESS; + } + } + return PR_FAILURE; +} + +NSS_IMPLEMENT nssCryptokiObject * +nssToken_FindCertificateByIssuerAndSerialNumber( + NSSToken *token, + nssSession *sessionOpt, + NSSDER *issuer, + NSSDER *serial, + nssTokenSearchType searchType, + PRStatus *statusOpt) +{ + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE_PTR serialAttr; + CK_ATTRIBUTE cert_template[4]; + CK_ULONG ctsize; + nssCryptokiObject **objects; + nssCryptokiObject *rvObject = NULL; + NSS_CK_TEMPLATE_START(cert_template, attr, ctsize); + + if (!token) { + PORT_SetError(SEC_ERROR_NO_TOKEN); + if (statusOpt) + *statusOpt = PR_FAILURE; + return NULL; + } + /* Set the search to token/session only if provided */ + if (searchType == nssTokenSearchType_SessionOnly) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); + } else if ((searchType == nssTokenSearchType_TokenOnly) || + (searchType == nssTokenSearchType_TokenForced)) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); + } + /* Set the unique id */ + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ISSUER, issuer); + serialAttr = attr; + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SERIAL_NUMBER, serial); + NSS_CK_TEMPLATE_FINISH(cert_template, attr, ctsize); + /* get the object handle */ + if (searchType == nssTokenSearchType_TokenForced) { + objects = find_objects(token, sessionOpt, + cert_template, ctsize, + 1, statusOpt); + } else { + objects = nssToken_FindObjectsByTemplate(token, sessionOpt, + cert_template, ctsize, + 1, statusOpt); + } + if (objects) { + rvObject = objects[0]; + nss_ZFreeIf(objects); + } + + /* + * NSS used to incorrectly store serial numbers in their decoded form. + * because of this old tokens have decoded serial numbers. + */ + if (!objects) { + NSSItem serialDecode; + PRStatus status; + + status = nssToken_decodeSerialItem(serial, &serialDecode); + if (status != PR_SUCCESS) { + return NULL; + } + NSS_CK_SET_ATTRIBUTE_ITEM(serialAttr, CKA_SERIAL_NUMBER, &serialDecode); + if (searchType == nssTokenSearchType_TokenForced) { + objects = find_objects(token, sessionOpt, + cert_template, ctsize, + 1, statusOpt); + } else { + objects = nssToken_FindObjectsByTemplate(token, sessionOpt, + cert_template, ctsize, + 1, statusOpt); + } + if (objects) { + rvObject = objects[0]; + nss_ZFreeIf(objects); + } + } + return rvObject; +} + +NSS_IMPLEMENT nssCryptokiObject * +nssToken_FindCertificateByEncodedCertificate( + NSSToken *token, + nssSession *sessionOpt, + NSSBER *encodedCertificate, + nssTokenSearchType searchType, + PRStatus *statusOpt) +{ + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE cert_template[3]; + CK_ULONG ctsize; + nssCryptokiObject **objects; + nssCryptokiObject *rvObject = NULL; + NSS_CK_TEMPLATE_START(cert_template, attr, ctsize); + /* Set the search to token/session only if provided */ + if (searchType == nssTokenSearchType_SessionOnly) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); + } else if (searchType == nssTokenSearchType_TokenOnly) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); + } + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_VALUE, encodedCertificate); + NSS_CK_TEMPLATE_FINISH(cert_template, attr, ctsize); + /* get the object handle */ + objects = nssToken_FindObjectsByTemplate(token, sessionOpt, + cert_template, ctsize, + 1, statusOpt); + if (objects) { + rvObject = objects[0]; + nss_ZFreeIf(objects); + } + return rvObject; +} + +NSS_IMPLEMENT nssCryptokiObject ** +nssToken_FindPrivateKeys( + NSSToken *token, + nssSession *sessionOpt, + nssTokenSearchType searchType, + PRUint32 maximumOpt, + PRStatus *statusOpt) +{ + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE key_template[2]; + CK_ULONG ktsize; + nssCryptokiObject **objects; + + NSS_CK_TEMPLATE_START(key_template, attr, ktsize); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_privkey); + if (searchType == nssTokenSearchType_SessionOnly) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); + } else if (searchType == nssTokenSearchType_TokenOnly) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); + } + NSS_CK_TEMPLATE_FINISH(key_template, attr, ktsize); + + objects = nssToken_FindObjectsByTemplate(token, sessionOpt, + key_template, ktsize, + maximumOpt, statusOpt); + return objects; +} + +/* XXX ?there are no session cert objects, so only search token objects */ +NSS_IMPLEMENT nssCryptokiObject * +nssToken_FindPrivateKeyByID( + NSSToken *token, + nssSession *sessionOpt, + NSSItem *keyID) +{ + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE key_template[3]; + CK_ULONG ktsize; + nssCryptokiObject **objects; + nssCryptokiObject *rvKey = NULL; + + NSS_CK_TEMPLATE_START(key_template, attr, ktsize); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_privkey); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ID, keyID); + NSS_CK_TEMPLATE_FINISH(key_template, attr, ktsize); + + objects = nssToken_FindObjectsByTemplate(token, sessionOpt, + key_template, ktsize, + 1, NULL); + if (objects) { + rvKey = objects[0]; + nss_ZFreeIf(objects); + } + return rvKey; +} + +/* XXX ?there are no session cert objects, so only search token objects */ +NSS_IMPLEMENT nssCryptokiObject * +nssToken_FindPublicKeyByID( + NSSToken *token, + nssSession *sessionOpt, + NSSItem *keyID) +{ + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE key_template[3]; + CK_ULONG ktsize; + nssCryptokiObject **objects; + nssCryptokiObject *rvKey = NULL; + + NSS_CK_TEMPLATE_START(key_template, attr, ktsize); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_pubkey); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ID, keyID); + NSS_CK_TEMPLATE_FINISH(key_template, attr, ktsize); + + objects = nssToken_FindObjectsByTemplate(token, sessionOpt, + key_template, ktsize, + 1, NULL); + if (objects) { + rvKey = objects[0]; + nss_ZFreeIf(objects); + } + return rvKey; +} + +static void +sha1_hash(NSSItem *input, NSSItem *output) +{ + NSSAlgorithmAndParameters *ap; + PK11SlotInfo *internal = PK11_GetInternalSlot(); + NSSToken *token = PK11Slot_GetNSSToken(internal); + ap = NSSAlgorithmAndParameters_CreateSHA1Digest(NULL); + (void)nssToken_Digest(token, NULL, ap, input, output, NULL); + nss_ZFreeIf(ap); + (void)nssToken_Destroy(token); + PK11_FreeSlot(internal); +} + +static void +md5_hash(NSSItem *input, NSSItem *output) +{ + NSSAlgorithmAndParameters *ap; + PK11SlotInfo *internal = PK11_GetInternalSlot(); + NSSToken *token = PK11Slot_GetNSSToken(internal); + ap = NSSAlgorithmAndParameters_CreateMD5Digest(NULL); + (void)nssToken_Digest(token, NULL, ap, input, output, NULL); + nss_ZFreeIf(ap); + (void)nssToken_Destroy(token); + PK11_FreeSlot(internal); +} + +static CK_TRUST +get_ck_trust( + nssTrustLevel nssTrust) +{ + CK_TRUST t; + switch (nssTrust) { + case nssTrustLevel_NotTrusted: + t = CKT_NSS_NOT_TRUSTED; + break; + case nssTrustLevel_TrustedDelegator: + t = CKT_NSS_TRUSTED_DELEGATOR; + break; + case nssTrustLevel_ValidDelegator: + t = CKT_NSS_VALID_DELEGATOR; + break; + case nssTrustLevel_Trusted: + t = CKT_NSS_TRUSTED; + break; + case nssTrustLevel_MustVerify: + t = CKT_NSS_MUST_VERIFY_TRUST; + break; + case nssTrustLevel_Unknown: + default: + t = CKT_NSS_TRUST_UNKNOWN; + break; + } + return t; +} + +NSS_IMPLEMENT nssCryptokiObject * +nssToken_ImportTrust( + NSSToken *tok, + nssSession *sessionOpt, + NSSDER *certEncoding, + NSSDER *certIssuer, + NSSDER *certSerial, + nssTrustLevel serverAuth, + nssTrustLevel clientAuth, + nssTrustLevel codeSigning, + nssTrustLevel emailProtection, + PRBool stepUpApproved, + PRBool asTokenObject) +{ + nssCryptokiObject *object; + CK_OBJECT_CLASS tobjc = CKO_NSS_TRUST; + CK_TRUST ckSA, ckCA, ckCS, ckEP; + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE trust_tmpl[11]; + CK_ULONG tsize; + PRUint8 sha1[20]; /* this is cheating... */ + PRUint8 md5[16]; + NSSItem sha1_result, md5_result; + sha1_result.data = sha1; + sha1_result.size = sizeof sha1; + md5_result.data = md5; + md5_result.size = sizeof md5; + sha1_hash(certEncoding, &sha1_result); + md5_hash(certEncoding, &md5_result); + ckSA = get_ck_trust(serverAuth); + ckCA = get_ck_trust(clientAuth); + ckCS = get_ck_trust(codeSigning); + ckEP = get_ck_trust(emailProtection); + NSS_CK_TEMPLATE_START(trust_tmpl, attr, tsize); + if (asTokenObject) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); + } else { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); + } + NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_CLASS, tobjc); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ISSUER, certIssuer); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SERIAL_NUMBER, certSerial); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CERT_SHA1_HASH, &sha1_result); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CERT_MD5_HASH, &md5_result); + /* now set the trust values */ + NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_SERVER_AUTH, ckSA); + NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_CLIENT_AUTH, ckCA); + NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_CODE_SIGNING, ckCS); + NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_EMAIL_PROTECTION, ckEP); + if (stepUpApproved) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TRUST_STEP_UP_APPROVED, + &g_ck_true); + } else { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TRUST_STEP_UP_APPROVED, + &g_ck_false); + } + NSS_CK_TEMPLATE_FINISH(trust_tmpl, attr, tsize); + /* import the trust object onto the token */ + object = import_object(tok, sessionOpt, trust_tmpl, tsize); + if (object && tok->cache) { + nssTokenObjectCache_ImportObject(tok->cache, object, tobjc, + trust_tmpl, tsize); + } + return object; +} + +NSS_IMPLEMENT nssCryptokiObject * +nssToken_FindTrustForCertificate( + NSSToken *token, + nssSession *sessionOpt, + NSSDER *certEncoding, + NSSDER *certIssuer, + NSSDER *certSerial, + nssTokenSearchType searchType) +{ + CK_OBJECT_CLASS tobjc = CKO_NSS_TRUST; + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE tobj_template[5]; + CK_ULONG tobj_size; + nssSession *session = sessionOpt ? sessionOpt : token->defaultSession; + nssCryptokiObject *object = NULL, **objects; + + /* Don't ask the module to use an invalid session handle. */ + if (!session || session->handle == CK_INVALID_HANDLE) { + PORT_SetError(SEC_ERROR_NO_TOKEN); + return object; + } + + NSS_CK_TEMPLATE_START(tobj_template, attr, tobj_size); + if (searchType == nssTokenSearchType_TokenOnly) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); + } + NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_CLASS, tobjc); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ISSUER, certIssuer); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SERIAL_NUMBER, certSerial); + NSS_CK_TEMPLATE_FINISH(tobj_template, attr, tobj_size); + objects = nssToken_FindObjectsByTemplate(token, session, + tobj_template, tobj_size, + 1, NULL); + if (objects) { + object = objects[0]; + nss_ZFreeIf(objects); + } + return object; +} + +NSS_IMPLEMENT nssCryptokiObject * +nssToken_ImportCRL( + NSSToken *token, + nssSession *sessionOpt, + NSSDER *subject, + NSSDER *encoding, + PRBool isKRL, + NSSUTF8 *url, + PRBool asTokenObject) +{ + nssCryptokiObject *object; + CK_OBJECT_CLASS crlobjc = CKO_NSS_CRL; + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE crl_tmpl[6]; + CK_ULONG crlsize; + + NSS_CK_TEMPLATE_START(crl_tmpl, attr, crlsize); + if (asTokenObject) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); + } else { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); + } + NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_CLASS, crlobjc); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SUBJECT, subject); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_VALUE, encoding); + NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_NSS_URL, url); + if (isKRL) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_NSS_KRL, &g_ck_true); + } else { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_NSS_KRL, &g_ck_false); + } + NSS_CK_TEMPLATE_FINISH(crl_tmpl, attr, crlsize); + + /* import the crl object onto the token */ + object = import_object(token, sessionOpt, crl_tmpl, crlsize); + if (object && token->cache) { + nssTokenObjectCache_ImportObject(token->cache, object, crlobjc, + crl_tmpl, crlsize); + } + return object; +} + +NSS_IMPLEMENT nssCryptokiObject ** +nssToken_FindCRLsBySubject( + NSSToken *token, + nssSession *sessionOpt, + NSSDER *subject, + nssTokenSearchType searchType, + PRUint32 maximumOpt, + PRStatus *statusOpt) +{ + CK_OBJECT_CLASS crlobjc = CKO_NSS_CRL; + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE crlobj_template[3]; + CK_ULONG crlobj_size; + nssCryptokiObject **objects = NULL; + nssSession *session = sessionOpt ? sessionOpt : token->defaultSession; + + /* Don't ask the module to use an invalid session handle. */ + if (!session || session->handle == CK_INVALID_HANDLE) { + PORT_SetError(SEC_ERROR_NO_TOKEN); + return objects; + } + + NSS_CK_TEMPLATE_START(crlobj_template, attr, crlobj_size); + if (searchType == nssTokenSearchType_SessionOnly) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); + } else if (searchType == nssTokenSearchType_TokenOnly || + searchType == nssTokenSearchType_TokenForced) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); + } + NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_CLASS, crlobjc); + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SUBJECT, subject); + NSS_CK_TEMPLATE_FINISH(crlobj_template, attr, crlobj_size); + + objects = nssToken_FindObjectsByTemplate(token, session, + crlobj_template, crlobj_size, + maximumOpt, statusOpt); + return objects; +} + +NSS_IMPLEMENT PRStatus +nssToken_GetCachedObjectAttributes( + NSSToken *token, + NSSArena *arenaOpt, + nssCryptokiObject *object, + CK_OBJECT_CLASS objclass, + CK_ATTRIBUTE_PTR atemplate, + CK_ULONG atlen) +{ + if (!token->cache) { + return PR_FAILURE; + } + return nssTokenObjectCache_GetObjectAttributes(token->cache, arenaOpt, + object, objclass, + atemplate, atlen); +} + +NSS_IMPLEMENT NSSItem * +nssToken_Digest( + NSSToken *tok, + nssSession *sessionOpt, + NSSAlgorithmAndParameters *ap, + NSSItem *data, + NSSItem *rvOpt, + NSSArena *arenaOpt) +{ + CK_RV ckrv; + CK_ULONG digestLen; + CK_BYTE_PTR digest; + NSSItem *rvItem = NULL; + void *epv = nssToken_GetCryptokiEPV(tok); + nssSession *session = (sessionOpt) ? sessionOpt : tok->defaultSession; + + /* Don't ask the module to use an invalid session handle. */ + if (!session || session->handle == CK_INVALID_HANDLE) { + PORT_SetError(SEC_ERROR_NO_TOKEN); + return rvItem; + } + + nssSession_EnterMonitor(session); + ckrv = CKAPI(epv)->C_DigestInit(session->handle, &ap->mechanism); + if (ckrv != CKR_OK) { + nssSession_ExitMonitor(session); + return NULL; + } +#if 0 + /* XXX the standard says this should work, but it doesn't */ + ckrv = CKAPI(epv)->C_Digest(session->handle, NULL, 0, NULL, &digestLen); + if (ckrv != CKR_OK) { + nssSession_ExitMonitor(session); + return NULL; + } +#endif + digestLen = 0; /* XXX for now */ + digest = NULL; + if (rvOpt) { + if (rvOpt->size > 0 && rvOpt->size < digestLen) { + nssSession_ExitMonitor(session); + /* the error should be bad args */ + return NULL; + } + if (rvOpt->data) { + digest = rvOpt->data; + } + digestLen = rvOpt->size; + } + if (!digest) { + digest = (CK_BYTE_PTR)nss_ZAlloc(arenaOpt, digestLen); + if (!digest) { + nssSession_ExitMonitor(session); + return NULL; + } + } + ckrv = CKAPI(epv)->C_Digest(session->handle, + (CK_BYTE_PTR)data->data, + (CK_ULONG)data->size, + (CK_BYTE_PTR)digest, + &digestLen); + nssSession_ExitMonitor(session); + if (ckrv != CKR_OK) { + nss_ZFreeIf(digest); + return NULL; + } + if (!rvOpt) { + rvItem = nssItem_Create(arenaOpt, NULL, digestLen, (void *)digest); + } + return rvItem; +} + +NSS_IMPLEMENT PRStatus +nssToken_BeginDigest( + NSSToken *tok, + nssSession *sessionOpt, + NSSAlgorithmAndParameters *ap) +{ + CK_RV ckrv; + void *epv = nssToken_GetCryptokiEPV(tok); + nssSession *session = (sessionOpt) ? sessionOpt : tok->defaultSession; + + /* Don't ask the module to use an invalid session handle. */ + if (!session || session->handle == CK_INVALID_HANDLE) { + PORT_SetError(SEC_ERROR_NO_TOKEN); + return PR_FAILURE; + } + + nssSession_EnterMonitor(session); + ckrv = CKAPI(epv)->C_DigestInit(session->handle, &ap->mechanism); + nssSession_ExitMonitor(session); + return (ckrv == CKR_OK) ? PR_SUCCESS : PR_FAILURE; +} + +NSS_IMPLEMENT PRStatus +nssToken_ContinueDigest( + NSSToken *tok, + nssSession *sessionOpt, + NSSItem *item) +{ + CK_RV ckrv; + void *epv = nssToken_GetCryptokiEPV(tok); + nssSession *session = (sessionOpt) ? sessionOpt : tok->defaultSession; + + /* Don't ask the module to use an invalid session handle. */ + if (!session || session->handle == CK_INVALID_HANDLE) { + PORT_SetError(SEC_ERROR_NO_TOKEN); + return PR_FAILURE; + } + + nssSession_EnterMonitor(session); + ckrv = CKAPI(epv)->C_DigestUpdate(session->handle, + (CK_BYTE_PTR)item->data, + (CK_ULONG)item->size); + nssSession_ExitMonitor(session); + return (ckrv == CKR_OK) ? PR_SUCCESS : PR_FAILURE; +} + +NSS_IMPLEMENT NSSItem * +nssToken_FinishDigest( + NSSToken *tok, + nssSession *sessionOpt, + NSSItem *rvOpt, + NSSArena *arenaOpt) +{ + CK_RV ckrv; + CK_ULONG digestLen; + CK_BYTE_PTR digest; + NSSItem *rvItem = NULL; + void *epv = nssToken_GetCryptokiEPV(tok); + nssSession *session = (sessionOpt) ? sessionOpt : tok->defaultSession; + + /* Don't ask the module to use an invalid session handle. */ + if (!session || session->handle == CK_INVALID_HANDLE) { + PORT_SetError(SEC_ERROR_NO_TOKEN); + return NULL; + } + + nssSession_EnterMonitor(session); + ckrv = CKAPI(epv)->C_DigestFinal(session->handle, NULL, &digestLen); + if (ckrv != CKR_OK || digestLen == 0) { + nssSession_ExitMonitor(session); + return NULL; + } + digest = NULL; + if (rvOpt) { + if (rvOpt->size > 0 && rvOpt->size < digestLen) { + nssSession_ExitMonitor(session); + /* the error should be bad args */ + return NULL; + } + if (rvOpt->data) { + digest = rvOpt->data; + } + digestLen = rvOpt->size; + } + if (!digest) { + digest = (CK_BYTE_PTR)nss_ZAlloc(arenaOpt, digestLen); + if (!digest) { + nssSession_ExitMonitor(session); + return NULL; + } + } + ckrv = CKAPI(epv)->C_DigestFinal(session->handle, digest, &digestLen); + nssSession_ExitMonitor(session); + if (ckrv != CKR_OK) { + nss_ZFreeIf(digest); + return NULL; + } + if (!rvOpt) { + rvItem = nssItem_Create(arenaOpt, NULL, digestLen, (void *)digest); + } + return rvItem; +} + +NSS_IMPLEMENT PRBool +nssToken_IsPresent( + NSSToken *token) +{ + return nssSlot_IsTokenPresent(token->slot); +} + +/* Sigh. The methods to find objects declared above cause problems with + * the low-level object cache in the softoken -- the objects are found in + * toto, then one wave of GetAttributes is done, then another. Having a + * large number of objects causes the cache to be thrashed, as the objects + * are gone before there's any chance to ask for their attributes. + * So, for now, bringing back traversal methods for certs. This way all of + * the cert's attributes can be grabbed immediately after finding it, + * increasing the likelihood that the cache takes care of it. + */ +NSS_IMPLEMENT PRStatus +nssToken_TraverseCertificates( + NSSToken *token, + nssSession *sessionOpt, + nssTokenSearchType searchType, + PRStatus (*callback)(nssCryptokiObject *instance, void *arg), + void *arg) +{ + CK_RV ckrv; + CK_ULONG count; + CK_OBJECT_HANDLE *objectHandles; + CK_ATTRIBUTE_PTR attr; + CK_ATTRIBUTE cert_template[2]; + CK_ULONG ctsize; + NSSArena *arena; + PRUint32 arraySize, numHandles; + nssCryptokiObject **objects; + void *epv = nssToken_GetCryptokiEPV(token); + nssSession *session = (sessionOpt) ? sessionOpt : token->defaultSession; + + /* Don't ask the module to use an invalid session handle. */ + if (!session || session->handle == CK_INVALID_HANDLE) { + PORT_SetError(SEC_ERROR_NO_TOKEN); + return PR_FAILURE; + } + + /* template for all certs */ + NSS_CK_TEMPLATE_START(cert_template, attr, ctsize); + if (searchType == nssTokenSearchType_SessionOnly) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false); + } else if (searchType == nssTokenSearchType_TokenOnly || + searchType == nssTokenSearchType_TokenForced) { + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true); + } + NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert); + NSS_CK_TEMPLATE_FINISH(cert_template, attr, ctsize); + + /* the arena is only for the array of object handles */ + arena = nssArena_Create(); + if (!arena) { + return PR_FAILURE; + } + arraySize = OBJECT_STACK_SIZE; + numHandles = 0; + objectHandles = nss_ZNEWARRAY(arena, CK_OBJECT_HANDLE, arraySize); + if (!objectHandles) { + goto loser; + } + nssSession_EnterMonitor(session); /* ==== session lock === */ + /* Initialize the find with the template */ + ckrv = CKAPI(epv)->C_FindObjectsInit(session->handle, + cert_template, ctsize); + if (ckrv != CKR_OK) { + nssSession_ExitMonitor(session); + goto loser; + } + while (PR_TRUE) { + /* Issue the find for up to arraySize - numHandles objects */ + ckrv = CKAPI(epv)->C_FindObjects(session->handle, + objectHandles + numHandles, + arraySize - numHandles, + &count); + if (ckrv != CKR_OK) { + nssSession_ExitMonitor(session); + goto loser; + } + /* bump the number of found objects */ + numHandles += count; + if (numHandles < arraySize) { + break; + } + /* the array is filled, double it and continue */ + arraySize *= 2; + objectHandles = nss_ZREALLOCARRAY(objectHandles, + CK_OBJECT_HANDLE, + arraySize); + if (!objectHandles) { + nssSession_ExitMonitor(session); + goto loser; + } + } + ckrv = CKAPI(epv)->C_FindObjectsFinal(session->handle); + nssSession_ExitMonitor(session); /* ==== end session lock === */ + if (ckrv != CKR_OK) { + goto loser; + } + if (numHandles > 0) { + objects = create_objects_from_handles(token, session, + objectHandles, numHandles); + if (objects) { + nssCryptokiObject **op; + for (op = objects; *op; op++) { + (void)(*callback)(*op, arg); + } + nss_ZFreeIf(objects); + } + } + nssArena_Destroy(arena); + return PR_SUCCESS; +loser: + nssArena_Destroy(arena); + return PR_FAILURE; +} + +NSS_IMPLEMENT PRBool +nssToken_IsPrivateKeyAvailable( + NSSToken *token, + NSSCertificate *c, + nssCryptokiObject *instance) +{ + CK_OBJECT_CLASS theClass; + + if (token == NULL) + return PR_FALSE; + if (c == NULL) + return PR_FALSE; + + theClass = CKO_PRIVATE_KEY; + if (!nssSlot_IsLoggedIn(token->slot)) { + theClass = CKO_PUBLIC_KEY; + } + if (PK11_MatchItem(token->pk11slot, instance->handle, theClass) != + CK_INVALID_HANDLE) { + return PR_TRUE; + } + return PR_FALSE; +} diff --git a/security/nss/lib/dev/devutil.c b/security/nss/lib/dev/devutil.c new file mode 100644 index 0000000000..302a6b5627 --- /dev/null +++ b/security/nss/lib/dev/devutil.c @@ -0,0 +1,998 @@ +/* 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/. */ + +#ifndef DEVM_H +#include "devm.h" +#endif /* DEVM_H */ + +#ifndef CKHELPER_H +#include "ckhelper.h" +#endif /* CKHELPER_H */ + +NSS_IMPLEMENT nssCryptokiObject * +nssCryptokiObject_Create( + NSSToken *t, + nssSession *session, + CK_OBJECT_HANDLE h) +{ + PRStatus status; + NSSSlot *slot; + nssCryptokiObject *object; + CK_BBOOL *isTokenObject; + CK_ATTRIBUTE cert_template[] = { + { CKA_TOKEN, NULL, 0 }, + { CKA_LABEL, NULL, 0 } + }; + slot = nssToken_GetSlot(t); + status = nssCKObject_GetAttributes(h, cert_template, 2, + NULL, session, slot); + nssSlot_Destroy(slot); + if (status != PR_SUCCESS) { + /* a failure here indicates a device error */ + return (nssCryptokiObject *)NULL; + } + if (cert_template[0].ulValueLen == 0 || !cert_template[0].pValue) { + nss_ZFreeIf(cert_template[1].pValue); + return (nssCryptokiObject *)NULL; + } + object = nss_ZNEW(NULL, nssCryptokiObject); + if (!object) { + nss_ZFreeIf(cert_template[0].pValue); + nss_ZFreeIf(cert_template[1].pValue); + return (nssCryptokiObject *)NULL; + } + object->handle = h; + object->token = nssToken_AddRef(t); + isTokenObject = (CK_BBOOL *)cert_template[0].pValue; + object->isTokenObject = *isTokenObject; + nss_ZFreeIf(cert_template[0].pValue); + NSS_CK_ATTRIBUTE_TO_UTF8(&cert_template[1], object->label); + return object; +} + +NSS_IMPLEMENT void +nssCryptokiObject_Destroy( + nssCryptokiObject *object) +{ + if (object) { + (void)nssToken_Destroy(object->token); + nss_ZFreeIf(object->label); + nss_ZFreeIf(object); + } +} + +NSS_IMPLEMENT nssCryptokiObject * +nssCryptokiObject_Clone( + nssCryptokiObject *object) +{ + nssCryptokiObject *rvObject; + rvObject = nss_ZNEW(NULL, nssCryptokiObject); + if (rvObject) { + rvObject->handle = object->handle; + rvObject->token = nssToken_AddRef(object->token); + rvObject->isTokenObject = object->isTokenObject; + if (object->label) { + rvObject->label = nssUTF8_Duplicate(object->label, NULL); + } + } + return rvObject; +} + +NSS_EXTERN PRBool +nssCryptokiObject_Equal( + nssCryptokiObject *o1, + nssCryptokiObject *o2) +{ + return (o1->token == o2->token && o1->handle == o2->handle); +} + +NSS_IMPLEMENT PRUint32 +nssPKCS11String_Length(CK_CHAR *pkcs11Str, PRUint32 bufLen) +{ + PRInt32 i; + for (i = bufLen - 1; i >= 0;) { + if (pkcs11Str[i] != ' ' && pkcs11Str[i] != '\0') + break; + --i; + } + return (PRUint32)(i + 1); +} + +/* + * Slot arrays + */ + +NSS_IMPLEMENT NSSSlot ** +nssSlotArray_Clone( + NSSSlot **slots) +{ + NSSSlot **rvSlots = NULL; + NSSSlot **sp = slots; + PRUint32 count = 0; + while (sp && *sp) + count++; + if (count > 0) { + rvSlots = nss_ZNEWARRAY(NULL, NSSSlot *, count + 1); + if (rvSlots) { + for (sp = slots, count = 0; *sp; sp++) { + rvSlots[count++] = nssSlot_AddRef(*sp); + } + } + } + return rvSlots; +} + +NSS_IMPLEMENT void +nssSlotArray_Destroy( + NSSSlot **slots) +{ + if (slots) { + NSSSlot **slotp; + for (slotp = slots; *slotp; slotp++) { + nssSlot_Destroy(*slotp); + } + nss_ZFreeIf(slots); + } +} + +NSS_IMPLEMENT void +NSSSlotArray_Destroy( + NSSSlot **slots) +{ + nssSlotArray_Destroy(slots); +} + +NSS_IMPLEMENT void +nssTokenArray_Destroy( + NSSToken **tokens) +{ + if (tokens) { + NSSToken **tokenp; + for (tokenp = tokens; *tokenp; tokenp++) { + (void)nssToken_Destroy(*tokenp); + } + nss_ZFreeIf(tokens); + } +} + +NSS_IMPLEMENT void +nssCryptokiObjectArray_Destroy( + nssCryptokiObject **objects) +{ + if (objects) { + nssCryptokiObject **op; + for (op = objects; *op; op++) { + nssCryptokiObject_Destroy(*op); + } + nss_ZFreeIf(objects); + } +} + +/* object cache for token */ + +typedef struct +{ + NSSArena *arena; + nssCryptokiObject *object; + CK_ATTRIBUTE_PTR attributes; + CK_ULONG numAttributes; +} nssCryptokiObjectAndAttributes; + +enum { + cachedCerts = 0, + cachedTrust = 1, + cachedCRLs = 2 +} cachedObjectType; + +struct nssTokenObjectCacheStr { + NSSToken *token; + PZLock *lock; + PRBool loggedIn; + PRBool doObjectType[3]; + PRBool searchedObjectType[3]; + nssCryptokiObjectAndAttributes **objects[3]; +}; + +NSS_IMPLEMENT nssTokenObjectCache * +nssTokenObjectCache_Create( + NSSToken *token, + PRBool cacheCerts, + PRBool cacheTrust, + PRBool cacheCRLs) +{ + nssTokenObjectCache *rvCache; + rvCache = nss_ZNEW(NULL, nssTokenObjectCache); + if (!rvCache) { + goto loser; + } + rvCache->lock = PZ_NewLock(nssILockOther); /* XXX */ + if (!rvCache->lock) { + goto loser; + } + rvCache->doObjectType[cachedCerts] = cacheCerts; + rvCache->doObjectType[cachedTrust] = cacheTrust; + rvCache->doObjectType[cachedCRLs] = cacheCRLs; + rvCache->token = token; /* cache goes away with token */ + return rvCache; +loser: + nssTokenObjectCache_Destroy(rvCache); + return (nssTokenObjectCache *)NULL; +} + +static void +clear_cache( + nssTokenObjectCache *cache) +{ + nssCryptokiObjectAndAttributes **oa; + PRUint32 objectType; + for (objectType = cachedCerts; objectType <= cachedCRLs; objectType++) { + cache->searchedObjectType[objectType] = PR_FALSE; + if (!cache->objects[objectType]) { + continue; + } + for (oa = cache->objects[objectType]; *oa; oa++) { + /* prevent the token from being destroyed */ + (*oa)->object->token = NULL; + nssCryptokiObject_Destroy((*oa)->object); + nssArena_Destroy((*oa)->arena); + } + nss_ZFreeIf(cache->objects[objectType]); + cache->objects[objectType] = NULL; + } +} + +NSS_IMPLEMENT void +nssTokenObjectCache_Clear( + nssTokenObjectCache *cache) +{ + if (cache) { + PZ_Lock(cache->lock); + clear_cache(cache); + PZ_Unlock(cache->lock); + } +} + +NSS_IMPLEMENT void +nssTokenObjectCache_Destroy( + nssTokenObjectCache *cache) +{ + if (cache) { + clear_cache(cache); + if (cache->lock) { + PZ_DestroyLock(cache->lock); + } + nss_ZFreeIf(cache); + } +} + +NSS_IMPLEMENT PRBool +nssTokenObjectCache_HaveObjectClass( + nssTokenObjectCache *cache, + CK_OBJECT_CLASS objclass) +{ + PRBool haveIt; + PZ_Lock(cache->lock); + switch (objclass) { + case CKO_CERTIFICATE: + haveIt = cache->doObjectType[cachedCerts]; + break; + case CKO_NSS_TRUST: + haveIt = cache->doObjectType[cachedTrust]; + break; + case CKO_NSS_CRL: + haveIt = cache->doObjectType[cachedCRLs]; + break; + default: + haveIt = PR_FALSE; + } + PZ_Unlock(cache->lock); + return haveIt; +} + +static nssCryptokiObjectAndAttributes ** +create_object_array( + nssCryptokiObject **objects, + PRBool *doObjects, + PRUint32 *numObjects, + PRStatus *status) +{ + nssCryptokiObjectAndAttributes **rvOandA = NULL; + *numObjects = 0; + /* There are no objects for this type */ + if (!objects || !*objects) { + *status = PR_SUCCESS; + return rvOandA; + } + while (*objects++) + (*numObjects)++; + if (*numObjects >= MAX_LOCAL_CACHE_OBJECTS) { + /* Hit the maximum allowed, so don't use a cache (there are + * too many objects to make caching worthwhile, presumably, if + * the token can handle that many objects, it can handle searching. + */ + *doObjects = PR_FALSE; + *status = PR_FAILURE; + *numObjects = 0; + } else { + rvOandA = nss_ZNEWARRAY(NULL, + nssCryptokiObjectAndAttributes *, + *numObjects + 1); + *status = rvOandA ? PR_SUCCESS : PR_FAILURE; + } + return rvOandA; +} + +static nssCryptokiObjectAndAttributes * +create_object( + nssCryptokiObject *object, + const CK_ATTRIBUTE_TYPE *types, + PRUint32 numTypes, + PRStatus *status) +{ + PRUint32 j; + NSSArena *arena = NULL; + NSSSlot *slot = NULL; + nssSession *session = NULL; + nssCryptokiObjectAndAttributes *rvCachedObject = NULL; + + slot = nssToken_GetSlot(object->token); + if (!slot) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + goto loser; + } + session = nssToken_GetDefaultSession(object->token); + if (!session) { + nss_SetError(NSS_ERROR_INVALID_POINTER); + goto loser; + } + arena = nssArena_Create(); + if (!arena) { + goto loser; + } + rvCachedObject = nss_ZNEW(arena, nssCryptokiObjectAndAttributes); + if (!rvCachedObject) { + goto loser; + } + rvCachedObject->arena = arena; + /* The cache is tied to the token, and therefore the objects + * in it should not hold references to the token. + */ + (void)nssToken_Destroy(object->token); + rvCachedObject->object = object; + rvCachedObject->attributes = nss_ZNEWARRAY(arena, CK_ATTRIBUTE, numTypes); + if (!rvCachedObject->attributes) { + goto loser; + } + for (j = 0; j < numTypes; j++) { + rvCachedObject->attributes[j].type = types[j]; + } + *status = nssCKObject_GetAttributes(object->handle, + rvCachedObject->attributes, + numTypes, + arena, + session, + slot); + if (*status != PR_SUCCESS) { + goto loser; + } + rvCachedObject->numAttributes = numTypes; + *status = PR_SUCCESS; + nssSlot_Destroy(slot); + + return rvCachedObject; +loser: + *status = PR_FAILURE; + if (slot) { + nssSlot_Destroy(slot); + } + if (arena) + nssArena_Destroy(arena); + return (nssCryptokiObjectAndAttributes *)NULL; +} + +/* + * + * State diagram for cache: + * + * token !present token removed + * +-------------------------+<----------------------+ + * | ^ | + * v | | + * +----------+ slot friendly | token present +----------+ + * | cache | -----------------> % ---------------> | cache | + * | unloaded | | loaded | + * +----------+ +----------+ + * ^ | ^ | + * | | slot !friendly slot logged in | | + * | +-----------------------> % ----------------------+ | + * | | | + * | slot logged out v slot !friendly | + * +-----------------------------+<--------------------------+ + * + */ + +/* This function must not be called with cache->lock locked. */ +static PRBool +token_is_present( + nssTokenObjectCache *cache) +{ + NSSSlot *slot = nssToken_GetSlot(cache->token); + PRBool tokenPresent = nssSlot_IsTokenPresent(slot); + nssSlot_Destroy(slot); + return tokenPresent; +} + +static PRBool +search_for_objects( + nssTokenObjectCache *cache) +{ + PRBool doSearch = PR_FALSE; + NSSSlot *slot = nssToken_GetSlot(cache->token); + /* Handle non-friendly slots (slots which require login for objects) */ + if (!nssSlot_IsFriendly(slot)) { + if (nssSlot_IsLoggedIn(slot)) { + /* Either no state change, or went from !logged in -> logged in */ + cache->loggedIn = PR_TRUE; + doSearch = PR_TRUE; + } else { + if (cache->loggedIn) { + /* went from logged in -> !logged in, destroy cached objects */ + clear_cache(cache); + cache->loggedIn = PR_FALSE; + } /* else no state change, still not logged in, so exit */ + } + } else { + /* slot is friendly, thus always available for search */ + doSearch = PR_TRUE; + } + nssSlot_Destroy(slot); + return doSearch; +} + +static nssCryptokiObjectAndAttributes * +create_cert( + nssCryptokiObject *object, + PRStatus *status) +{ + static const CK_ATTRIBUTE_TYPE certAttr[] = { + CKA_CLASS, + CKA_TOKEN, + CKA_LABEL, + CKA_CERTIFICATE_TYPE, + CKA_ID, + CKA_VALUE, + CKA_ISSUER, + CKA_SERIAL_NUMBER, + CKA_SUBJECT, + CKA_NSS_EMAIL + }; + static const PRUint32 numCertAttr = sizeof(certAttr) / sizeof(certAttr[0]); + return create_object(object, certAttr, numCertAttr, status); +} + +static nssCryptokiObjectAndAttributes * +create_trust( + nssCryptokiObject *object, + PRStatus *status) +{ + static const CK_ATTRIBUTE_TYPE trustAttr[] = { + CKA_CLASS, + CKA_TOKEN, + CKA_LABEL, + CKA_CERT_SHA1_HASH, + CKA_CERT_MD5_HASH, + CKA_ISSUER, + CKA_SUBJECT, + CKA_TRUST_SERVER_AUTH, + CKA_TRUST_CLIENT_AUTH, + CKA_TRUST_EMAIL_PROTECTION, + CKA_TRUST_CODE_SIGNING + }; + static const PRUint32 numTrustAttr = sizeof(trustAttr) / sizeof(trustAttr[0]); + return create_object(object, trustAttr, numTrustAttr, status); +} + +static nssCryptokiObjectAndAttributes * +create_crl( + nssCryptokiObject *object, + PRStatus *status) +{ + static const CK_ATTRIBUTE_TYPE crlAttr[] = { + CKA_CLASS, + CKA_TOKEN, + CKA_LABEL, + CKA_VALUE, + CKA_SUBJECT, + CKA_NSS_KRL, + CKA_NSS_URL + }; + static const PRUint32 numCRLAttr = sizeof(crlAttr) / sizeof(crlAttr[0]); + return create_object(object, crlAttr, numCRLAttr, status); +} + +/* Dispatch to the create function for the object type */ +static nssCryptokiObjectAndAttributes * +create_object_of_type( + nssCryptokiObject *object, + PRUint32 objectType, + PRStatus *status) +{ + if (objectType == cachedCerts) { + return create_cert(object, status); + } + if (objectType == cachedTrust) { + return create_trust(object, status); + } + if (objectType == cachedCRLs) { + return create_crl(object, status); + } + return (nssCryptokiObjectAndAttributes *)NULL; +} + +static PRStatus +get_token_objects_for_cache( + nssTokenObjectCache *cache, + PRUint32 objectType, + CK_OBJECT_CLASS objclass) +{ + PRStatus status; + nssCryptokiObject **objects; + PRBool *doIt = &cache->doObjectType[objectType]; + PRUint32 i, numObjects; + + if (!search_for_objects(cache) || + cache->searchedObjectType[objectType] || + !cache->doObjectType[objectType]) { + /* Either there was a state change that prevents a search + * (token logged out), or the search was already done, + * or objects of this type are not being cached. + */ + return PR_SUCCESS; + } + objects = nssToken_FindObjects(cache->token, NULL, objclass, + nssTokenSearchType_TokenForced, + MAX_LOCAL_CACHE_OBJECTS, &status); + if (status != PR_SUCCESS) { + return status; + } + cache->objects[objectType] = create_object_array(objects, + doIt, + &numObjects, + &status); + if (status != PR_SUCCESS) { + nssCryptokiObjectArray_Destroy(objects); + return status; + } + for (i = 0; i < numObjects; i++) { + cache->objects[objectType][i] = create_object_of_type(objects[i], + objectType, + &status); + if (status != PR_SUCCESS) { + break; + } + } + if (status == PR_SUCCESS) { + nss_ZFreeIf(objects); + } else { + PRUint32 j; + for (j = 0; j < i; j++) { + /* Any token references that were removed in successful loop iterations + * need to be restored before we call nssCryptokiObjectArray_Destroy */ + nssToken_AddRef(cache->objects[objectType][j]->object->token); + nssArena_Destroy(cache->objects[objectType][j]->arena); + } + nss_ZFreeIf(cache->objects[objectType]); + cache->objects[objectType] = NULL; + nssCryptokiObjectArray_Destroy(objects); + } + cache->searchedObjectType[objectType] = PR_TRUE; + return status; +} + +static CK_ATTRIBUTE_PTR +find_attribute_in_object( + nssCryptokiObjectAndAttributes *obj, + CK_ATTRIBUTE_TYPE attrType) +{ + PRUint32 j; + for (j = 0; j < obj->numAttributes; j++) { + if (attrType == obj->attributes[j].type) { + return &obj->attributes[j]; + } + } + return (CK_ATTRIBUTE_PTR)NULL; +} + +/* Find all objects in the array that match the supplied template */ +static nssCryptokiObject ** +find_objects_in_array( + nssCryptokiObjectAndAttributes **objArray, + CK_ATTRIBUTE_PTR ot, + CK_ULONG otlen, + PRUint32 maximumOpt) +{ + PRIntn oi = 0; + PRUint32 i; + NSSArena *arena; + PRUint32 size = 8; + PRUint32 numMatches = 0; + nssCryptokiObject **objects = NULL; + nssCryptokiObjectAndAttributes **matches = NULL; + CK_ATTRIBUTE_PTR attr; + + if (!objArray) { + return (nssCryptokiObject **)NULL; + } + arena = nssArena_Create(); + if (!arena) { + return (nssCryptokiObject **)NULL; + } + matches = nss_ZNEWARRAY(arena, nssCryptokiObjectAndAttributes *, size); + if (!matches) { + goto loser; + } + if (maximumOpt == 0) + maximumOpt = ~0; + /* loop over the cached objects */ + for (; *objArray && numMatches < maximumOpt; objArray++) { + nssCryptokiObjectAndAttributes *obj = *objArray; + /* loop over the test template */ + for (i = 0; i < otlen; i++) { + /* see if the object has the attribute */ + attr = find_attribute_in_object(obj, ot[i].type); + if (!attr) { + /* nope, match failed */ + break; + } + /* compare the attribute against the test value */ + if (ot[i].ulValueLen != attr->ulValueLen || + !nsslibc_memequal(ot[i].pValue, + attr->pValue, + attr->ulValueLen, NULL)) { + /* nope, match failed */ + break; + } + } + if (i == otlen) { + /* all of the attributes in the test template were found + * in the object's template, and they all matched + */ + matches[numMatches++] = obj; + if (numMatches == size) { + size *= 2; + matches = nss_ZREALLOCARRAY(matches, + nssCryptokiObjectAndAttributes *, + size); + if (!matches) { + goto loser; + } + } + } + } + if (numMatches > 0) { + objects = nss_ZNEWARRAY(NULL, nssCryptokiObject *, numMatches + 1); + if (!objects) { + goto loser; + } + for (oi = 0; oi < (PRIntn)numMatches; oi++) { + objects[oi] = nssCryptokiObject_Clone(matches[oi]->object); + if (!objects[oi]) { + goto loser; + } + } + } + nssArena_Destroy(arena); + return objects; +loser: + nssCryptokiObjectArray_Destroy(objects); + nssArena_Destroy(arena); + return (nssCryptokiObject **)NULL; +} + +NSS_IMPLEMENT nssCryptokiObject ** +nssTokenObjectCache_FindObjectsByTemplate( + nssTokenObjectCache *cache, + CK_OBJECT_CLASS objclass, + CK_ATTRIBUTE_PTR otemplate, + CK_ULONG otlen, + PRUint32 maximumOpt, + PRStatus *statusOpt) +{ + PRStatus status = PR_FAILURE; + nssCryptokiObject **rvObjects = NULL; + PRUint32 objectType; + if (!token_is_present(cache)) { + status = PR_SUCCESS; + goto finish; + } + switch (objclass) { + case CKO_CERTIFICATE: + objectType = cachedCerts; + break; + case CKO_NSS_TRUST: + objectType = cachedTrust; + break; + case CKO_NSS_CRL: + objectType = cachedCRLs; + break; + default: + goto finish; + } + PZ_Lock(cache->lock); + if (cache->doObjectType[objectType]) { + status = get_token_objects_for_cache(cache, objectType, objclass); + if (status == PR_SUCCESS) { + rvObjects = find_objects_in_array(cache->objects[objectType], + otemplate, otlen, maximumOpt); + } + } + PZ_Unlock(cache->lock); +finish: + if (statusOpt) { + *statusOpt = status; + } + return rvObjects; +} + +static PRBool +cache_available_for_object_type( + nssTokenObjectCache *cache, + PRUint32 objectType) +{ + if (!cache->doObjectType[objectType]) { + /* not caching this object kind */ + return PR_FALSE; + } + if (!cache->searchedObjectType[objectType]) { + /* objects are not cached yet */ + return PR_FALSE; + } + if (!search_for_objects(cache)) { + /* not logged in */ + return PR_FALSE; + } + return PR_TRUE; +} + +NSS_IMPLEMENT PRStatus +nssTokenObjectCache_GetObjectAttributes( + nssTokenObjectCache *cache, + NSSArena *arenaOpt, + nssCryptokiObject *object, + CK_OBJECT_CLASS objclass, + CK_ATTRIBUTE_PTR atemplate, + CK_ULONG atlen) +{ + PRUint32 i, j; + NSSArena *arena = NULL; + nssArenaMark *mark = NULL; + nssCryptokiObjectAndAttributes *cachedOA = NULL; + nssCryptokiObjectAndAttributes **oa = NULL; + PRUint32 objectType; + if (!token_is_present(cache)) { + return PR_FAILURE; + } + PZ_Lock(cache->lock); + switch (objclass) { + case CKO_CERTIFICATE: + objectType = cachedCerts; + break; + case CKO_NSS_TRUST: + objectType = cachedTrust; + break; + case CKO_NSS_CRL: + objectType = cachedCRLs; + break; + default: + goto loser; + } + if (!cache_available_for_object_type(cache, objectType)) { + goto loser; + } + oa = cache->objects[objectType]; + if (!oa) { + goto loser; + } + for (; *oa; oa++) { + if (nssCryptokiObject_Equal((*oa)->object, object)) { + cachedOA = *oa; + break; + } + } + if (!cachedOA) { + goto loser; /* don't have this object */ + } + if (arenaOpt) { + arena = arenaOpt; + mark = nssArena_Mark(arena); + } + for (i = 0; i < atlen; i++) { + for (j = 0; j < cachedOA->numAttributes; j++) { + if (atemplate[i].type == cachedOA->attributes[j].type) { + CK_ATTRIBUTE_PTR attr = &cachedOA->attributes[j]; + if (cachedOA->attributes[j].ulValueLen == 0 || + cachedOA->attributes[j].ulValueLen == (CK_ULONG)-1) { + break; /* invalid attribute */ + } + if (atemplate[i].ulValueLen > 0) { + if (atemplate[i].pValue == NULL || + atemplate[i].ulValueLen < attr->ulValueLen) { + goto loser; + } + } else { + atemplate[i].pValue = nss_ZAlloc(arena, attr->ulValueLen); + if (!atemplate[i].pValue) { + goto loser; + } + } + nsslibc_memcpy(atemplate[i].pValue, + attr->pValue, attr->ulValueLen); + atemplate[i].ulValueLen = attr->ulValueLen; + break; + } + } + if (j == cachedOA->numAttributes) { + atemplate[i].ulValueLen = (CK_ULONG)-1; + } + } + PZ_Unlock(cache->lock); + if (mark) { + nssArena_Unmark(arena, mark); + } + return PR_SUCCESS; +loser: + PZ_Unlock(cache->lock); + if (mark) { + nssArena_Release(arena, mark); + } + return PR_FAILURE; +} + +NSS_IMPLEMENT PRStatus +nssTokenObjectCache_ImportObject( + nssTokenObjectCache *cache, + nssCryptokiObject *object, + CK_OBJECT_CLASS objclass, + CK_ATTRIBUTE_PTR ot, + CK_ULONG otlen) +{ + PRStatus status = PR_SUCCESS; + PRUint32 count; + nssCryptokiObjectAndAttributes **oa, ***otype; + PRUint32 objectType; + PRBool haveIt = PR_FALSE; + + if (!token_is_present(cache)) { + return PR_SUCCESS; /* cache not active, ignored */ + } + PZ_Lock(cache->lock); + switch (objclass) { + case CKO_CERTIFICATE: + objectType = cachedCerts; + break; + case CKO_NSS_TRUST: + objectType = cachedTrust; + break; + case CKO_NSS_CRL: + objectType = cachedCRLs; + break; + default: + PZ_Unlock(cache->lock); + return PR_SUCCESS; /* don't need to import it here */ + } + if (!cache_available_for_object_type(cache, objectType)) { + PZ_Unlock(cache->lock); + return PR_SUCCESS; /* cache not active, ignored */ + } + count = 0; + otype = &cache->objects[objectType]; /* index into array of types */ + oa = *otype; /* the array of objects for this type */ + while (oa && *oa) { + if (nssCryptokiObject_Equal((*oa)->object, object)) { + haveIt = PR_TRUE; + break; + } + count++; + oa++; + } + if (haveIt) { + /* Destroy the old entry */ + (*oa)->object->token = NULL; + nssCryptokiObject_Destroy((*oa)->object); + nssArena_Destroy((*oa)->arena); + } else { + /* Create space for a new entry */ + if (count > 0) { + *otype = nss_ZREALLOCARRAY(*otype, + nssCryptokiObjectAndAttributes *, + count + 2); + } else { + *otype = nss_ZNEWARRAY(NULL, nssCryptokiObjectAndAttributes *, 2); + } + } + if (*otype) { + nssCryptokiObject *copyObject = nssCryptokiObject_Clone(object); + (*otype)[count] = create_object_of_type(copyObject, objectType, + &status); + } else { + status = PR_FAILURE; + } + PZ_Unlock(cache->lock); + return status; +} + +NSS_IMPLEMENT void +nssTokenObjectCache_RemoveObject( + nssTokenObjectCache *cache, + nssCryptokiObject *object) +{ + PRUint32 oType; + nssCryptokiObjectAndAttributes **oa, **swp = NULL; + if (!token_is_present(cache)) { + return; + } + PZ_Lock(cache->lock); + for (oType = 0; oType < 3; oType++) { + if (!cache_available_for_object_type(cache, oType) || + !cache->objects[oType]) { + continue; + } + for (oa = cache->objects[oType]; *oa; oa++) { + if (nssCryptokiObject_Equal((*oa)->object, object)) { + swp = oa; /* the entry to remove */ + while (oa[1]) + oa++; /* go to the tail */ + (*swp)->object->token = NULL; + nssCryptokiObject_Destroy((*swp)->object); + nssArena_Destroy((*swp)->arena); /* destroy it */ + *swp = *oa; /* swap the last with the removed */ + *oa = NULL; /* null-terminate the array */ + break; + } + } + if (swp) { + break; + } + } + if ((oType < 3) && + cache->objects[oType] && cache->objects[oType][0] == NULL) { + nss_ZFreeIf(cache->objects[oType]); /* no entries remaining */ + cache->objects[oType] = NULL; + } + PZ_Unlock(cache->lock); +} + +/* These two hash algorithms are presently sufficient. +** They are used for fingerprints of certs which are stored as the +** CKA_CERT_SHA1_HASH and CKA_CERT_MD5_HASH attributes. +** We don't need to add SHAxxx to these now. +*/ +/* XXX of course this doesn't belong here */ +NSS_IMPLEMENT NSSAlgorithmAndParameters * +NSSAlgorithmAndParameters_CreateSHA1Digest( + NSSArena *arenaOpt) +{ + NSSAlgorithmAndParameters *rvAP = NULL; + rvAP = nss_ZNEW(arenaOpt, NSSAlgorithmAndParameters); + if (rvAP) { + rvAP->mechanism.mechanism = CKM_SHA_1; + rvAP->mechanism.pParameter = NULL; + rvAP->mechanism.ulParameterLen = 0; + } + return rvAP; +} + +NSS_IMPLEMENT NSSAlgorithmAndParameters * +NSSAlgorithmAndParameters_CreateMD5Digest( + NSSArena *arenaOpt) +{ + NSSAlgorithmAndParameters *rvAP = NULL; + rvAP = nss_ZNEW(arenaOpt, NSSAlgorithmAndParameters); + if (rvAP) { + rvAP->mechanism.mechanism = CKM_MD5; + rvAP->mechanism.pParameter = NULL; + rvAP->mechanism.ulParameterLen = 0; + } + return rvAP; +} diff --git a/security/nss/lib/dev/exports.gyp b/security/nss/lib/dev/exports.gyp new file mode 100644 index 0000000000..92cf7564b3 --- /dev/null +++ b/security/nss/lib/dev/exports.gyp @@ -0,0 +1,31 @@ +# 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/. +{ + 'includes': [ + '../../coreconf/config.gypi' + ], + 'targets': [ + { + 'target_name': 'lib_dev_exports', + 'type': 'none', + 'copies': [ + { + 'files': [ + 'ckhelper.h', + 'dev.h', + 'devm.h', + 'devt.h', + 'devtm.h', + 'nssdev.h', + 'nssdevt.h' + ], + 'destination': '<(nss_private_dist_dir)/<(module)' + } + ] + } + ], + 'variables': { + 'module': 'nss' + } +} diff --git a/security/nss/lib/dev/manifest.mn b/security/nss/lib/dev/manifest.mn new file mode 100644 index 0000000000..449ae58e3c --- /dev/null +++ b/security/nss/lib/dev/manifest.mn @@ -0,0 +1,33 @@ +# +# 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/. + +CORE_DEPTH = ../.. + +PRIVATE_EXPORTS = \ + ckhelper.h \ + devm.h \ + devtm.h \ + devt.h \ + dev.h \ + nssdevt.h \ + nssdev.h \ + $(NULL) + +MODULE = nss + +CSRCS = \ + devslot.c \ + devtoken.c \ + devutil.c \ + ckhelper.c \ + $(NULL) + +REQUIRES = nspr + +LIBRARY_NAME = nssdev +SHARED_LIBRARY = $(NULL) + +# This part of the code, including all sub-dirs, can be optimized for size +export ALLOW_OPT_CODE_SIZE = 1 diff --git a/security/nss/lib/dev/nssdev.h b/security/nss/lib/dev/nssdev.h new file mode 100644 index 0000000000..1066ec6cdf --- /dev/null +++ b/security/nss/lib/dev/nssdev.h @@ -0,0 +1,36 @@ +/* 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/. */ + +#ifndef NSSDEV_H +#define NSSDEV_H + +/* + * nssdev.h + * + * High-level methods for interaction with cryptoki devices + */ + +#ifndef NSSDEVT_H +#include "nssdevt.h" +#endif /* NSSDEVT_H */ + +PR_BEGIN_EXTERN_C + +/* NSSAlgorithmAndParameters + * + * NSSAlgorithmAndParameters_CreateSHA1Digest + * NSSAlgorithmAndParameters_CreateMD5Digest + */ + +NSS_EXTERN NSSAlgorithmAndParameters * +NSSAlgorithmAndParameters_CreateSHA1Digest( + NSSArena *arenaOpt); + +NSS_EXTERN NSSAlgorithmAndParameters * +NSSAlgorithmAndParameters_CreateMD5Digest( + NSSArena *arenaOpt); + +PR_END_EXTERN_C + +#endif /* DEV_H */ diff --git a/security/nss/lib/dev/nssdevt.h b/security/nss/lib/dev/nssdevt.h new file mode 100644 index 0000000000..b7b7b1e49f --- /dev/null +++ b/security/nss/lib/dev/nssdevt.h @@ -0,0 +1,36 @@ +/* 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/. */ + +#ifndef NSSDEVT_H +#define NSSDEVT_H + +/* + * nssdevt.h + * + * This file contains definitions for the low-level cryptoki devices. + */ + +#ifndef NSSBASET_H +#include "nssbaset.h" +#endif /* NSSBASET_H */ + +#ifndef NSSPKIT_H +#include "nsspkit.h" +#endif /* NSSPKIT_H */ + +PR_BEGIN_EXTERN_C + +/* + * NSSModule and NSSSlot -- placeholders for the PKCS#11 types + */ + +typedef struct NSSModuleStr NSSModule; + +typedef struct NSSSlotStr NSSSlot; + +typedef struct NSSTokenStr NSSToken; + +PR_END_EXTERN_C + +#endif /* NSSDEVT_H */ -- cgit v1.2.3