summaryrefslogtreecommitdiffstats
path: root/security/nss/lib/dev
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--security/nss/lib/dev/Makefile27
-rw-r--r--security/nss/lib/dev/ckhelper.c595
-rw-r--r--security/nss/lib/dev/ckhelper.h148
-rw-r--r--security/nss/lib/dev/dev.gyp26
-rw-r--r--security/nss/lib/dev/dev.h751
-rw-r--r--security/nss/lib/dev/devm.h157
-rw-r--r--security/nss/lib/dev/devslot.c322
-rw-r--r--security/nss/lib/dev/devt.h161
-rw-r--r--security/nss/lib/dev/devtm.h25
-rw-r--r--security/nss/lib/dev/devtoken.c1553
-rw-r--r--security/nss/lib/dev/devutil.c998
-rw-r--r--security/nss/lib/dev/exports.gyp31
-rw-r--r--security/nss/lib/dev/manifest.mn33
-rw-r--r--security/nss/lib/dev/nssdev.h36
-rw-r--r--security/nss/lib/dev/nssdevt.h36
15 files changed, 4899 insertions, 0 deletions
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 */