/* * The contents of this file are subject to the Mozilla Public * License Version 1.1 (the "License"); you may not use this file * except in compliance with the License. You may obtain a copy of * the License at http://www.mozilla.org/MPL/ * * Software distributed under the License is distributed on an "AS * IS" basis, WITHOUT WARRANTY OF ANY KIND, either express or * implied. See the License for the specific language governing * rights and limitations under the License. * * The Original Code is the Netscape security libraries. * * The Initial Developer of the Original Code is Netscape * Communications Corporation. Portions created by Netscape are * Copyright (C) 1994-2000 Netscape Communications Corporation. All * Rights Reserved. * * Portions created by Sun Microsystems, Inc. are Copyright (C) 2003 * Sun Microsystems, Inc. All Rights Reserved. * * Contributor(s): * Dr Vipul Gupta , Sun Microsystems Laboratories * * Alternatively, the contents of this file may be used under the * terms of the GNU General Public License Version 2 or later (the * "GPL"), in which case the provisions of the GPL are applicable * instead of those above. If you wish to allow use of your * version of this file only under the terms of the GPL and not to * allow others to use your version of this file under the MPL, * indicate your decision by deleting the provisions above and * replace them with the notice and other provisions required by * the GPL. If you do not delete the provisions above, a recipient * may use your version of this file under either the MPL or the * GPL. */ /* * Internal PKCS #11 functions. Should only be called by pkcs11.c */ #include "pkcs11.h" #include "pkcs11i.h" #include "pcertt.h" #include "lowkeyi.h" #include "pcert.h" #include "secasn1.h" #include "blapi.h" #include "secerr.h" #include "prnetdb.h" /* for PR_ntohl */ /* * ******************** Attribute Utilities ******************************* */ /* * create a new attribute with type, value, and length. Space is allocated * to hold value. */ static PK11Attribute * pk11_NewAttribute(PK11Object *object, CK_ATTRIBUTE_TYPE type, CK_VOID_PTR value, CK_ULONG len) { PK11Attribute *attribute; #ifdef PKCS11_STATIC_ATTRIBUTES PK11SessionObject *so = pk11_narrowToSessionObject(object); int index; if (so == NULL) { /* allocate new attribute in a buffer */ PORT_Assert(0); } /* * PKCS11_STATIC_ATTRIBUTES attempts to keep down contention on Malloc and Arena locks * by limiting the number of these calls on high traversed paths. this * is done for attributes by 'allocating' them from a pool already allocated * by the parent object. */ PK11_USE_THREADS(PZ_Lock(so->attributeLock);) index = so->nextAttr++; PK11_USE_THREADS(PZ_Unlock(so->attributeLock);) PORT_Assert(index < MAX_OBJS_ATTRS); if (index >= MAX_OBJS_ATTRS) return NULL; attribute = &so->attrList[index]; attribute->attrib.type = type; attribute->freeAttr = PR_FALSE; attribute->freeData = PR_FALSE; if (value) { if (len <= ATTR_SPACE) { attribute->attrib.pValue = attribute->space; } else { attribute->attrib.pValue = PORT_Alloc(len); attribute->freeData = PR_TRUE; } if (attribute->attrib.pValue == NULL) { return NULL; } PORT_Memcpy(attribute->attrib.pValue,value,len); attribute->attrib.ulValueLen = len; } else { attribute->attrib.pValue = NULL; attribute->attrib.ulValueLen = 0; } #else #ifdef PKCS11_REF_COUNT_ATTRIBUTES attribute = (PK11Attribute*)PORT_Alloc(sizeof(PK11Attribute)); attribute->freeAttr = PR_TRUE; #else attribute = (PK11Attribute*)PORT_ArenaAlloc(object->arena,sizeof(PK11Attribute)); attribute->freeAttr = PR_FALSE; #endif /* PKCS11_REF_COUNT_ATTRIBUTES */ if (attribute == NULL) return NULL; attribute->freeData = PR_FALSE; if (value) { #ifdef PKCS11_REF_COUNT_ATTRIBUTES attribute->attrib.pValue = PORT_Alloc(len); attribute->freeData = PR_TRUE; #else attribute->attrib.pValue = PORT_ArenaAlloc(object->arena,len); #endif /* PKCS11_REF_COUNT_ATTRIBUTES */ if (attribute->attrib.pValue == NULL) { #ifdef PKCS11_REF_COUNT_ATTRIBUTES PORT_Free(attribute); #endif /* PKCS11_REF_COUNT_ATTRIBUTES */ return NULL; } PORT_Memcpy(attribute->attrib.pValue,value,len); attribute->attrib.ulValueLen = len; } else { attribute->attrib.pValue = NULL; attribute->attrib.ulValueLen = 0; } #endif /* PKCS11_STATIC_ATTRIBUTES */ attribute->attrib.type = type; attribute->handle = type; attribute->next = attribute->prev = NULL; #ifdef PKCS11_REF_COUNT_ATTRIBUTES attribute->refCount = 1; #ifdef PKCS11_USE_THREADS attribute->refLock = PZ_NewLock(nssILockRefLock); if (attribute->refLock == NULL) { if (attribute->attrib.pValue) PORT_Free(attribute->attrib.pValue); PORT_Free(attribute); return NULL; } #else attribute->refLock = NULL; #endif #endif /* PKCS11_REF_COUNT_ATTRIBUTES */ return attribute; } static PK11Attribute * pk11_NewTokenAttribute(CK_ATTRIBUTE_TYPE type, CK_VOID_PTR value, CK_ULONG len, PRBool copy) { PK11Attribute *attribute; attribute = (PK11Attribute*)PORT_Alloc(sizeof(PK11Attribute)); if (attribute == NULL) return NULL; attribute->attrib.type = type; attribute->handle = type; attribute->next = attribute->prev = NULL; attribute->freeAttr = PR_TRUE; attribute->freeData = PR_FALSE; #ifdef PKCS11_REF_COUNT_ATTRIBUTES attribute->refCount = 1; #ifdef PKCS11_USE_THREADS attribute->refLock = PZ_NewLock(nssILockRefLock); if (attribute->refLock == NULL) { PORT_Free(attribute); return NULL; } #else attribute->refLock = NULL; #endif #endif /* PKCS11_REF_COUNT_ATTRIBUTES */ attribute->attrib.type = type; if (!copy) { attribute->attrib.pValue = value; attribute->attrib.ulValueLen = len; return attribute; } if (value) { #ifdef PKCS11_STATIC_ATTRIBUTES if (len <= ATTR_SPACE) { attribute->attrib.pValue = attribute->space; } else { attribute->attrib.pValue = PORT_Alloc(len); attribute->freeData = PR_TRUE; } #else attribute->attrib.pValue = PORT_Alloc(len); attribute->freeData = PR_TRUE; #endif if (attribute->attrib.pValue == NULL) { #ifdef PKCS11_REF_COUNT_ATTRIBUTES if (attribute->refLock) { PK11_USE_THREADS(PZ_DestroyLock(attribute->refLock);) } #endif PORT_Free(attribute); return NULL; } PORT_Memcpy(attribute->attrib.pValue,value,len); attribute->attrib.ulValueLen = len; } else { attribute->attrib.pValue = NULL; attribute->attrib.ulValueLen = 0; } return attribute; } static PK11Attribute * pk11_NewTokenAttributeSigned(CK_ATTRIBUTE_TYPE type, CK_VOID_PTR value, CK_ULONG len, PRBool copy) { unsigned char * dval = (unsigned char *)value; if (*dval == 0) { dval++; len--; } return pk11_NewTokenAttribute(type,dval,len,copy); } /* * Free up all the memory associated with an attribute. Reference count * must be zero to call this. */ static void pk11_DestroyAttribute(PK11Attribute *attribute) { #ifdef PKCS11_REF_COUNT_ATTRIBUTES PORT_Assert(attribute->refCount == 0); PK11_USE_THREADS(PZ_DestroyLock(attribute->refLock);) #endif if (attribute->freeData) { if (attribute->attrib.pValue) { /* clear out the data in the attribute value... it may have been * sensitive data */ PORT_Memset(attribute->attrib.pValue, 0, attribute->attrib.ulValueLen); } PORT_Free(attribute->attrib.pValue); } PORT_Free(attribute); } /* * release a reference to an attribute structure */ void pk11_FreeAttribute(PK11Attribute *attribute) { #ifdef PKCS11_REF_COUNT_ATTRIBUTES PRBool destroy = PR_FALSE; #endif if (attribute->freeAttr) { pk11_DestroyAttribute(attribute); return; } #ifdef PKCS11_REF_COUNT_ATTRIBUTES PK11_USE_THREADS(PZ_Lock(attribute->refLock);) if (attribute->refCount == 1) destroy = PR_TRUE; attribute->refCount--; PK11_USE_THREADS(PZ_Unlock(attribute->refLock);) if (destroy) pk11_DestroyAttribute(attribute); #endif } #ifdef PKCS11_REF_COUNT_ATTRIBUTES #define PK11_DEF_ATTRIBUTE(value,len) \ { NULL, NULL, PR_FALSE, PR_FALSE, 1, NULL, 0, { 0, value, len } } #else #define PK11_DEF_ATTRIBUTE(value,len) \ { NULL, NULL, PR_FALSE, PR_FALSE, 0, { 0, value, len } } #endif CK_BBOOL pk11_staticTrueValue = CK_TRUE; CK_BBOOL pk11_staticFalseValue = CK_FALSE; static const PK11Attribute pk11_StaticTrueAttr = PK11_DEF_ATTRIBUTE(&pk11_staticTrueValue,sizeof(pk11_staticTrueValue)); static const PK11Attribute pk11_StaticFalseAttr = PK11_DEF_ATTRIBUTE(&pk11_staticFalseValue,sizeof(pk11_staticFalseValue)); static const PK11Attribute pk11_StaticNullAttr = PK11_DEF_ATTRIBUTE(NULL,0); char pk11_StaticOneValue = 1; static const PK11Attribute pk11_StaticOneAttr = PK11_DEF_ATTRIBUTE(&pk11_StaticOneValue,sizeof(pk11_StaticOneValue)); CK_CERTIFICATE_TYPE pk11_staticX509Value = CKC_X_509; static const PK11Attribute pk11_StaticX509Attr = PK11_DEF_ATTRIBUTE(&pk11_staticX509Value, sizeof(pk11_staticX509Value)); CK_TRUST pk11_staticTrustedValue = CKT_NETSCAPE_TRUSTED; CK_TRUST pk11_staticTrustedDelegatorValue = CKT_NETSCAPE_TRUSTED_DELEGATOR; CK_TRUST pk11_staticValidDelegatorValue = CKT_NETSCAPE_VALID_DELEGATOR; CK_TRUST pk11_staticUnTrustedValue = CKT_NETSCAPE_UNTRUSTED; CK_TRUST pk11_staticTrustUnknownValue = CKT_NETSCAPE_TRUST_UNKNOWN; CK_TRUST pk11_staticValidPeerValue = CKT_NETSCAPE_VALID; CK_TRUST pk11_staticMustVerifyValue = CKT_NETSCAPE_MUST_VERIFY; static const PK11Attribute pk11_StaticTrustedAttr = PK11_DEF_ATTRIBUTE(&pk11_staticTrustedValue, sizeof(pk11_staticTrustedValue)); static const PK11Attribute pk11_StaticTrustedDelegatorAttr = PK11_DEF_ATTRIBUTE(&pk11_staticTrustedDelegatorValue, sizeof(pk11_staticTrustedDelegatorValue)); static const PK11Attribute pk11_StaticValidDelegatorAttr = PK11_DEF_ATTRIBUTE(&pk11_staticValidDelegatorValue, sizeof(pk11_staticValidDelegatorValue)); static const PK11Attribute pk11_StaticUnTrustedAttr = PK11_DEF_ATTRIBUTE(&pk11_staticUnTrustedValue, sizeof(pk11_staticUnTrustedValue)); static const PK11Attribute pk11_StaticTrustUnknownAttr = PK11_DEF_ATTRIBUTE(&pk11_staticTrustUnknownValue, sizeof(pk11_staticTrustUnknownValue)); static const PK11Attribute pk11_StaticValidPeerAttr = PK11_DEF_ATTRIBUTE(&pk11_staticValidPeerValue, sizeof(pk11_staticValidPeerValue)); static const PK11Attribute pk11_StaticMustVerifyAttr = PK11_DEF_ATTRIBUTE(&pk11_staticMustVerifyValue, sizeof(pk11_staticMustVerifyValue)); static certDBEntrySMime * pk11_getSMime(PK11TokenObject *object) { certDBEntrySMime *entry; if (object->obj.objclass != CKO_NETSCAPE_SMIME) { return NULL; } if (object->obj.objectInfo) { return (certDBEntrySMime *)object->obj.objectInfo; } entry = nsslowcert_ReadDBSMimeEntry(object->obj.slot->certDB, (char *)object->dbKey.data); object->obj.objectInfo = (void *)entry; object->obj.infoFree = (PK11Free) nsslowcert_DestroyDBEntry; return entry; } static certDBEntryRevocation * pk11_getCrl(PK11TokenObject *object) { certDBEntryRevocation *crl; PRBool isKrl; if (object->obj.objclass != CKO_NETSCAPE_CRL) { return NULL; } if (object->obj.objectInfo) { return (certDBEntryRevocation *)object->obj.objectInfo; } isKrl = (PRBool) object->obj.handle == PK11_TOKEN_KRL_HANDLE; crl = nsslowcert_FindCrlByKey(object->obj.slot->certDB, &object->dbKey, isKrl); object->obj.objectInfo = (void *)crl; object->obj.infoFree = (PK11Free) nsslowcert_DestroyDBEntry; return crl; } static NSSLOWCERTCertificate * pk11_getCert(PK11TokenObject *object) { NSSLOWCERTCertificate *cert; CK_OBJECT_CLASS objClass = object->obj.objclass; if ((objClass != CKO_CERTIFICATE) && (objClass != CKO_NETSCAPE_TRUST)) { return NULL; } if (objClass == CKO_CERTIFICATE && object->obj.objectInfo) { return (NSSLOWCERTCertificate *)object->obj.objectInfo; } cert = nsslowcert_FindCertByKey(object->obj.slot->certDB,&object->dbKey); if (objClass == CKO_CERTIFICATE) { object->obj.objectInfo = (void *)cert; object->obj.infoFree = (PK11Free) nsslowcert_DestroyCertificate ; } return cert; } static NSSLOWCERTTrust * pk11_getTrust(PK11TokenObject *object) { NSSLOWCERTTrust *trust; if (object->obj.objclass != CKO_NETSCAPE_TRUST) { return NULL; } if (object->obj.objectInfo) { return (NSSLOWCERTTrust *)object->obj.objectInfo; } trust = nsslowcert_FindTrustByKey(object->obj.slot->certDB,&object->dbKey); object->obj.objectInfo = (void *)trust; object->obj.infoFree = (PK11Free) nsslowcert_DestroyTrust ; return trust; } static NSSLOWKEYPublicKey * pk11_GetPublicKey(PK11TokenObject *object) { NSSLOWKEYPublicKey *pubKey; NSSLOWKEYPrivateKey *privKey; if (object->obj.objclass != CKO_PUBLIC_KEY) { return NULL; } if (object->obj.objectInfo) { return (NSSLOWKEYPublicKey *)object->obj.objectInfo; } privKey = nsslowkey_FindKeyByPublicKey(object->obj.slot->keyDB, &object->dbKey, object->obj.slot->password); if (privKey == NULL) { return NULL; } pubKey = nsslowkey_ConvertToPublicKey(privKey); nsslowkey_DestroyPrivateKey(privKey); object->obj.objectInfo = (void *) pubKey; object->obj.infoFree = (PK11Free) nsslowkey_DestroyPublicKey ; return pubKey; } static NSSLOWKEYPrivateKey * pk11_GetPrivateKey(PK11TokenObject *object) { NSSLOWKEYPrivateKey *privKey; if ((object->obj.objclass != CKO_PRIVATE_KEY) && (object->obj.objclass != CKO_SECRET_KEY)) { return NULL; } if (object->obj.objectInfo) { return (NSSLOWKEYPrivateKey *)object->obj.objectInfo; } privKey = nsslowkey_FindKeyByPublicKey(object->obj.slot->keyDB, &object->dbKey, object->obj.slot->password); if (privKey == NULL) { return NULL; } object->obj.objectInfo = (void *) privKey; object->obj.infoFree = (PK11Free) nsslowkey_DestroyPrivateKey ; return privKey; } /* pk11_GetPubItem returns data associated with the public key. * one only needs to free the public key. This comment is here * because this sematic would be non-obvious otherwise. All callers * should include this comment. */ static SECItem * pk11_GetPubItem(NSSLOWKEYPublicKey *pubKey) { SECItem *pubItem = NULL; /* get value to compare from the cert's public key */ switch ( pubKey->keyType ) { case NSSLOWKEYRSAKey: pubItem = &pubKey->u.rsa.modulus; break; case NSSLOWKEYDSAKey: pubItem = &pubKey->u.dsa.publicValue; break; case NSSLOWKEYDHKey: pubItem = &pubKey->u.dh.publicValue; break; #ifdef NSS_ENABLE_ECC case NSSLOWKEYECKey: pubItem = &pubKey->u.ec.publicValue; break; #endif /* NSS_ENABLE_ECC */ default: break; } return pubItem; } static const SEC_ASN1Template pk11_SerialTemplate[] = { { SEC_ASN1_INTEGER, offsetof(NSSLOWCERTCertificate,serialNumber) }, { 0 } }; static PK11Attribute * pk11_FindRSAPublicKeyAttribute(NSSLOWKEYPublicKey *key, CK_ATTRIBUTE_TYPE type) { unsigned char hash[SHA1_LENGTH]; CK_KEY_TYPE keyType = CKK_RSA; switch (type) { case CKA_KEY_TYPE: return pk11_NewTokenAttribute(type,&keyType,sizeof(keyType), PR_TRUE); case CKA_ID: SHA1_HashBuf(hash,key->u.rsa.modulus.data,key->u.rsa.modulus.len); return pk11_NewTokenAttribute(type,hash,SHA1_LENGTH, PR_TRUE); case CKA_DERIVE: return (PK11Attribute *) &pk11_StaticFalseAttr; case CKA_ENCRYPT: case CKA_VERIFY: case CKA_VERIFY_RECOVER: case CKA_WRAP: return (PK11Attribute *) &pk11_StaticTrueAttr; case CKA_MODULUS: return pk11_NewTokenAttributeSigned(type,key->u.rsa.modulus.data, key->u.rsa.modulus.len, PR_FALSE); case CKA_PUBLIC_EXPONENT: return pk11_NewTokenAttributeSigned(type,key->u.rsa.publicExponent.data, key->u.rsa.publicExponent.len, PR_FALSE); default: break; } return NULL; } static PK11Attribute * pk11_FindDSAPublicKeyAttribute(NSSLOWKEYPublicKey *key, CK_ATTRIBUTE_TYPE type) { unsigned char hash[SHA1_LENGTH]; CK_KEY_TYPE keyType = CKK_DSA; switch (type) { case CKA_KEY_TYPE: return pk11_NewTokenAttribute(type,&keyType,sizeof(keyType), PR_TRUE); case CKA_ID: SHA1_HashBuf(hash,key->u.dsa.publicValue.data, key->u.dsa.publicValue.len); return pk11_NewTokenAttribute(type,hash,SHA1_LENGTH, PR_TRUE); case CKA_DERIVE: case CKA_ENCRYPT: case CKA_VERIFY_RECOVER: case CKA_WRAP: return (PK11Attribute *) &pk11_StaticFalseAttr; case CKA_VERIFY: return (PK11Attribute *) &pk11_StaticTrueAttr; case CKA_VALUE: return pk11_NewTokenAttributeSigned(type,key->u.dsa.publicValue.data, key->u.dsa.publicValue.len, PR_FALSE); case CKA_PRIME: return pk11_NewTokenAttributeSigned(type,key->u.dsa.params.prime.data, key->u.dsa.params.prime.len, PR_FALSE); case CKA_SUBPRIME: return pk11_NewTokenAttributeSigned(type, key->u.dsa.params.subPrime.data, key->u.dsa.params.subPrime.len, PR_FALSE); case CKA_BASE: return pk11_NewTokenAttributeSigned(type,key->u.dsa.params.base.data, key->u.dsa.params.base.len, PR_FALSE); default: break; } return NULL; } static PK11Attribute * pk11_FindDHPublicKeyAttribute(NSSLOWKEYPublicKey *key, CK_ATTRIBUTE_TYPE type) { unsigned char hash[SHA1_LENGTH]; CK_KEY_TYPE keyType = CKK_DH; switch (type) { case CKA_KEY_TYPE: return pk11_NewTokenAttribute(type,&keyType,sizeof(keyType), PR_TRUE); case CKA_ID: SHA1_HashBuf(hash,key->u.dh.publicValue.data,key->u.dh.publicValue.len); return pk11_NewTokenAttribute(type,hash,SHA1_LENGTH, PR_TRUE); case CKA_DERIVE: return (PK11Attribute *) &pk11_StaticTrueAttr; case CKA_ENCRYPT: case CKA_VERIFY: case CKA_VERIFY_RECOVER: case CKA_WRAP: return (PK11Attribute *) &pk11_StaticFalseAttr; case CKA_VALUE: return pk11_NewTokenAttributeSigned(type,key->u.dh.publicValue.data, key->u.dh.publicValue.len, PR_FALSE); case CKA_PRIME: return pk11_NewTokenAttributeSigned(type,key->u.dh.prime.data, key->u.dh.prime.len, PR_FALSE); case CKA_BASE: return pk11_NewTokenAttributeSigned(type,key->u.dh.base.data, key->u.dh.base.len, PR_FALSE); default: break; } return NULL; } #ifdef NSS_ENABLE_ECC static PK11Attribute * pk11_FindECPublicKeyAttribute(NSSLOWKEYPublicKey *key, CK_ATTRIBUTE_TYPE type) { unsigned char hash[SHA1_LENGTH]; CK_KEY_TYPE keyType = CKK_EC; switch (type) { case CKA_KEY_TYPE: return pk11_NewTokenAttribute(type,&keyType,sizeof(keyType), PR_TRUE); case CKA_ID: SHA1_HashBuf(hash, key->u.ec.publicValue.data, key->u.ec.publicValue.len); return pk11_NewTokenAttribute(type,hash,SHA1_LENGTH, PR_TRUE); case CKA_DERIVE: case CKA_VERIFY: return (PK11Attribute *) &pk11_StaticTrueAttr; case CKA_ENCRYPT: case CKA_VERIFY_RECOVER: case CKA_WRAP: return (PK11Attribute *) &pk11_StaticFalseAttr; case CKA_EC_PARAMS: /* XXX Why is the last arg PR_FALSE? */ return pk11_NewTokenAttributeSigned(type, key->u.ec.ecParams.DEREncoding.data, key->u.ec.ecParams.DEREncoding.len, PR_FALSE); case CKA_EC_POINT: /* XXX Why is the last arg PR_FALSE? */ return pk11_NewTokenAttributeSigned(type,key->u.ec.publicValue.data, key->u.ec.publicValue.len, PR_FALSE); default: break; } return NULL; } #endif /* NSS_ENABLE_ECC */ static PK11Attribute * pk11_FindPublicKeyAttribute(PK11TokenObject *object, CK_ATTRIBUTE_TYPE type) { NSSLOWKEYPublicKey *key; PK11Attribute *att = NULL; char *label; switch (type) { case CKA_PRIVATE: case CKA_SENSITIVE: case CKA_ALWAYS_SENSITIVE: case CKA_NEVER_EXTRACTABLE: return (PK11Attribute *) &pk11_StaticFalseAttr; case CKA_MODIFIABLE: case CKA_EXTRACTABLE: return (PK11Attribute *) &pk11_StaticTrueAttr; case CKA_LABEL: label = nsslowkey_FindKeyNicknameByPublicKey(object->obj.slot->keyDB, &object->dbKey, object->obj.slot->password); if (label == NULL) { return (PK11Attribute *)&pk11_StaticOneAttr; } att = pk11_NewTokenAttribute(type,label,PORT_Strlen(label), PR_TRUE); PORT_Free(label); return att; default: break; } key = pk11_GetPublicKey(object); if (key == NULL) { return NULL; } switch (key->keyType) { case NSSLOWKEYRSAKey: return pk11_FindRSAPublicKeyAttribute(key,type); case NSSLOWKEYDSAKey: return pk11_FindDSAPublicKeyAttribute(key,type); case NSSLOWKEYDHKey: return pk11_FindDHPublicKeyAttribute(key,type); #ifdef NSS_ENABLE_ECC case NSSLOWKEYECKey: return pk11_FindECPublicKeyAttribute(key,type); #endif /* NSS_ENABLE_ECC */ default: break; } return NULL; } static PK11Attribute * pk11_FindSecretKeyAttribute(PK11TokenObject *object, CK_ATTRIBUTE_TYPE type) { NSSLOWKEYPrivateKey *key; char *label; unsigned char *keyString; PK11Attribute *att; int keyTypeLen; CK_ULONG keyLen; CK_KEY_TYPE keyType; PRUint32 keyTypeStorage; switch (type) { case CKA_PRIVATE: case CKA_SENSITIVE: case CKA_ALWAYS_SENSITIVE: case CKA_EXTRACTABLE: case CKA_DERIVE: case CKA_ENCRYPT: case CKA_DECRYPT: case CKA_SIGN: case CKA_VERIFY: case CKA_WRAP: case CKA_UNWRAP: case CKA_MODIFIABLE: return (PK11Attribute *) &pk11_StaticTrueAttr; case CKA_NEVER_EXTRACTABLE: return (PK11Attribute *) &pk11_StaticFalseAttr; case CKA_LABEL: label = nsslowkey_FindKeyNicknameByPublicKey(object->obj.slot->keyDB, &object->dbKey, object->obj.slot->password); if (label == NULL) { return (PK11Attribute *)&pk11_StaticNullAttr; } att = pk11_NewTokenAttribute(type,label,PORT_Strlen(label), PR_TRUE); PORT_Free(label); return att; case CKA_KEY_TYPE: case CKA_VALUE_LEN: case CKA_VALUE: break; default: return NULL; } key = pk11_GetPrivateKey(object); if (key == NULL) { return NULL; } switch (type) { case CKA_KEY_TYPE: /* handle legacy databases. In legacy databases key_type was stored * in host order, with any leading zeros stripped off. Only key types * under 0x1f (AES) were stored. We assume that any values which are * either 1 byte long (big endian), or have byte[0] between 0 and * 0x7f and bytes[1]-bytes[3] equal to '0' (little endian). All other * values are assumed to be from the new database, which is always 4 * bytes in network order */ keyType=0; keyString = key->u.rsa.coefficient.data; keyTypeLen = key->u.rsa.coefficient.len; /* * Because of various endian and word lengths, the database may have * stored the keyType value in one of the following formats: * (kt) <= 0x1f * length data * Big Endian, pre-3.9, all lengths: 1 (kt) * Little Endian, pre-3.9, 32 bits: 4 (kt) 0 0 0 * Little Endian, pre-3.9, 64 bits: 8 (kt) 0 0 0 0 0 0 0 * All platforms, 3.9, 32 bits: 4 0 0 0 (kt) * Big Endian, 3.9, 64 bits: 8 0 0 0 (kt) 0 0 0 0 * Little Endian, 3.9, 64 bits: 8 0 0 0 0 0 0 0 (kt) * All platforms, >= 3.9.1, all lengths: 4 (a) k1 k2 k3 * where (a) is 0 or >= 0x80. currently (a) can only be 0. */ /* * This key was written on a 64 bit platform with a using NSS 3.9 * or earlier. Reduce the 64 bit possibilities above. When through, * we will only have: * * Big Endian, pre-3.9, all lengths: 1 (kt) * Little Endian, pre-3.9, all lengths: 4 (kt) 0 0 0 * All platforms, 3.9, all lengths: 4 0 0 0 (kt) * All platforms, => 3.9.1, all lengths: 4 (a) k1 k2 k3 */ if (keyTypeLen == 8) { keyTypeStorage = *(PRUint32 *) keyString; if (keyTypeStorage == 0) { keyString += sizeof(PRUint32); } keyTypeLen = 4; } /* * Now Handle: * * All platforms, 3.9, all lengths: 4 0 0 0 (kt) * All platforms, => 3.9.1, all lengths: 4 (a) k1 k2 k3 * * NOTE: if kt == 0 or ak1k2k3 == 0, the test fails and * we handle it as: * * Little Endian, pre-3.9, all lengths: 4 (kt) 0 0 0 */ if (keyTypeLen == sizeof(keyTypeStorage) && (((keyString[0] & 0x80) == 0x80) || !((keyString[1] == 0) && (keyString[2] == 0) && (keyString[3] == 0))) ) { PORT_Memcpy(&keyTypeStorage, keyString, sizeof(keyTypeStorage)); keyType = (CK_KEY_TYPE) PR_ntohl(keyTypeStorage); } else { /* * Now Handle: * * Big Endian, pre-3.9, all lengths: 1 (kt) * Little Endian, pre-3.9, all lengths: 4 (kt) 0 0 0 * -- KeyType == 0 all other cases ---: 4 0 0 0 0 */ keyType = (CK_KEY_TYPE) keyString[0] ; } return pk11_NewTokenAttribute(type,&keyType,sizeof(keyType),PR_TRUE); case CKA_VALUE: return pk11_NewTokenAttribute(type,key->u.rsa.privateExponent.data, key->u.rsa.privateExponent.len, PR_FALSE); case CKA_VALUE_LEN: keyLen=key->u.rsa.privateExponent.len; return pk11_NewTokenAttribute(type, &keyLen, sizeof(CK_ULONG), PR_TRUE); } return NULL; } static PK11Attribute * pk11_FindRSAPrivateKeyAttribute(NSSLOWKEYPrivateKey *key, CK_ATTRIBUTE_TYPE type) { unsigned char hash[SHA1_LENGTH]; CK_KEY_TYPE keyType = CKK_RSA; switch (type) { case CKA_KEY_TYPE: return pk11_NewTokenAttribute(type,&keyType,sizeof(keyType), PR_TRUE); case CKA_ID: SHA1_HashBuf(hash,key->u.rsa.modulus.data,key->u.rsa.modulus.len); return pk11_NewTokenAttribute(type,hash,SHA1_LENGTH, PR_TRUE); case CKA_DERIVE: return (PK11Attribute *) &pk11_StaticFalseAttr; case CKA_DECRYPT: case CKA_SIGN: case CKA_SIGN_RECOVER: case CKA_UNWRAP: return (PK11Attribute *) &pk11_StaticTrueAttr; case CKA_MODULUS: return pk11_NewTokenAttributeSigned(type,key->u.rsa.modulus.data, key->u.rsa.modulus.len, PR_FALSE); case CKA_PUBLIC_EXPONENT: return pk11_NewTokenAttributeSigned(type,key->u.rsa.publicExponent.data, key->u.rsa.publicExponent.len, PR_FALSE); case CKA_PRIVATE_EXPONENT: case CKA_PRIME_1: case CKA_PRIME_2: case CKA_EXPONENT_1: case CKA_EXPONENT_2: case CKA_COEFFICIENT: return (PK11Attribute *) &pk11_StaticNullAttr; default: break; } return NULL; } static PK11Attribute * pk11_FindDSAPrivateKeyAttribute(NSSLOWKEYPrivateKey *key, CK_ATTRIBUTE_TYPE type) { unsigned char hash[SHA1_LENGTH]; CK_KEY_TYPE keyType = CKK_DSA; switch (type) { case CKA_KEY_TYPE: return pk11_NewTokenAttribute(type,&keyType,sizeof(keyType), PR_TRUE); case CKA_ID: SHA1_HashBuf(hash,key->u.dsa.publicValue.data, key->u.dsa.publicValue.len); return pk11_NewTokenAttribute(type,hash,SHA1_LENGTH, PR_TRUE); case CKA_DERIVE: case CKA_DECRYPT: case CKA_SIGN_RECOVER: case CKA_UNWRAP: return (PK11Attribute *) &pk11_StaticFalseAttr; case CKA_SIGN: return (PK11Attribute *) &pk11_StaticTrueAttr; case CKA_VALUE: return (PK11Attribute *) &pk11_StaticNullAttr; case CKA_PRIME: return pk11_NewTokenAttributeSigned(type,key->u.dsa.params.prime.data, key->u.dsa.params.prime.len, PR_FALSE); case CKA_SUBPRIME: return pk11_NewTokenAttributeSigned(type, key->u.dsa.params.subPrime.data, key->u.dsa.params.subPrime.len, PR_FALSE); case CKA_BASE: return pk11_NewTokenAttributeSigned(type,key->u.dsa.params.base.data, key->u.dsa.params.base.len, PR_FALSE); default: break; } return NULL; } static PK11Attribute * pk11_FindDHPrivateKeyAttribute(NSSLOWKEYPrivateKey *key, CK_ATTRIBUTE_TYPE type) { unsigned char hash[SHA1_LENGTH]; CK_KEY_TYPE keyType = CKK_DH; switch (type) { case CKA_KEY_TYPE: return pk11_NewTokenAttribute(type,&keyType,sizeof(keyType), PR_TRUE); case CKA_ID: SHA1_HashBuf(hash,key->u.dh.publicValue.data,key->u.dh.publicValue.len); return pk11_NewTokenAttribute(type,hash,SHA1_LENGTH, PR_TRUE); case CKA_DERIVE: return (PK11Attribute *) &pk11_StaticTrueAttr; case CKA_DECRYPT: case CKA_SIGN: case CKA_SIGN_RECOVER: case CKA_UNWRAP: return (PK11Attribute *) &pk11_StaticFalseAttr; case CKA_VALUE: return (PK11Attribute *) &pk11_StaticNullAttr; case CKA_PRIME: return pk11_NewTokenAttributeSigned(type,key->u.dh.prime.data, key->u.dh.prime.len, PR_FALSE); case CKA_BASE: return pk11_NewTokenAttributeSigned(type,key->u.dh.base.data, key->u.dh.base.len, PR_FALSE); default: break; } return NULL; } #ifdef NSS_ENABLE_ECC static PK11Attribute * pk11_FindECPrivateKeyAttribute(NSSLOWKEYPrivateKey *key, CK_ATTRIBUTE_TYPE type) { unsigned char hash[SHA1_LENGTH]; CK_KEY_TYPE keyType = CKK_EC; switch (type) { case CKA_KEY_TYPE: return pk11_NewTokenAttribute(type,&keyType,sizeof(keyType), PR_TRUE); case CKA_ID: SHA1_HashBuf(hash,key->u.ec.publicValue.data,key->u.ec.publicValue.len); return pk11_NewTokenAttribute(type,hash,SHA1_LENGTH, PR_TRUE); case CKA_DERIVE: case CKA_SIGN: return (PK11Attribute *) &pk11_StaticTrueAttr; case CKA_DECRYPT: case CKA_SIGN_RECOVER: case CKA_UNWRAP: return (PK11Attribute *) &pk11_StaticFalseAttr; case CKA_VALUE: return (PK11Attribute *) &pk11_StaticNullAttr; case CKA_EC_PARAMS: /* XXX Why is the last arg PR_FALSE? */ return pk11_NewTokenAttributeSigned(type, key->u.ec.ecParams.DEREncoding.data, key->u.ec.ecParams.DEREncoding.len, PR_FALSE); default: break; } return NULL; } #endif /* NSS_ENABLE_ECC */ static PK11Attribute * pk11_FindPrivateKeyAttribute(PK11TokenObject *object, CK_ATTRIBUTE_TYPE type) { NSSLOWKEYPrivateKey *key; char *label; PK11Attribute *att; switch (type) { case CKA_PRIVATE: case CKA_SENSITIVE: case CKA_ALWAYS_SENSITIVE: case CKA_EXTRACTABLE: case CKA_MODIFIABLE: return (PK11Attribute *) &pk11_StaticTrueAttr; case CKA_NEVER_EXTRACTABLE: return (PK11Attribute *) &pk11_StaticFalseAttr; case CKA_SUBJECT: return (PK11Attribute *)&pk11_StaticNullAttr; case CKA_LABEL: label = nsslowkey_FindKeyNicknameByPublicKey(object->obj.slot->keyDB, &object->dbKey, object->obj.slot->password); if (label == NULL) { return (PK11Attribute *)&pk11_StaticNullAttr; } att = pk11_NewTokenAttribute(type,label,PORT_Strlen(label), PR_TRUE); PORT_Free(label); return att; default: break; } key = pk11_GetPrivateKey(object); if (key == NULL) { return NULL; } switch (key->keyType) { case NSSLOWKEYRSAKey: return pk11_FindRSAPrivateKeyAttribute(key,type); case NSSLOWKEYDSAKey: return pk11_FindDSAPrivateKeyAttribute(key,type); case NSSLOWKEYDHKey: return pk11_FindDHPrivateKeyAttribute(key,type); #ifdef NSS_ENABLE_ECC case NSSLOWKEYECKey: return pk11_FindECPrivateKeyAttribute(key,type); #endif /* NSS_ENABLE_ECC */ default: break; } return NULL; } static PK11Attribute * pk11_FindSMIMEAttribute(PK11TokenObject *object, CK_ATTRIBUTE_TYPE type) { certDBEntrySMime *entry; switch (type) { case CKA_PRIVATE: case CKA_MODIFIABLE: return (PK11Attribute *) &pk11_StaticFalseAttr; case CKA_NETSCAPE_EMAIL: return pk11_NewTokenAttribute(type,object->dbKey.data, object->dbKey.len-1, PR_FALSE); case CKA_NETSCAPE_SMIME_TIMESTAMP: case CKA_SUBJECT: case CKA_VALUE: break; default: return NULL; } entry = pk11_getSMime(object); if (entry == NULL) { return NULL; } switch (type) { case CKA_NETSCAPE_SMIME_TIMESTAMP: return pk11_NewTokenAttribute(type,entry->optionsDate.data, entry->optionsDate.len, PR_FALSE); case CKA_SUBJECT: return pk11_NewTokenAttribute(type,entry->subjectName.data, entry->subjectName.len, PR_FALSE); case CKA_VALUE: return pk11_NewTokenAttribute(type,entry->smimeOptions.data, entry->smimeOptions.len, PR_FALSE); default: break; } return NULL; } static PK11Attribute * pk11_FindTrustAttribute(PK11TokenObject *object, CK_ATTRIBUTE_TYPE type) { NSSLOWCERTTrust *trust; unsigned char hash[SHA1_LENGTH]; unsigned int trustFlags; switch (type) { case CKA_PRIVATE: return (PK11Attribute *) &pk11_StaticFalseAttr; case CKA_MODIFIABLE: return (PK11Attribute *) &pk11_StaticTrueAttr; case CKA_CERT_SHA1_HASH: case CKA_CERT_MD5_HASH: case CKA_TRUST_CLIENT_AUTH: case CKA_TRUST_SERVER_AUTH: case CKA_TRUST_EMAIL_PROTECTION: case CKA_TRUST_CODE_SIGNING: break; default: return NULL; } trust = pk11_getTrust(object); if (trust == NULL) { return NULL; } switch (type) { case CKA_CERT_SHA1_HASH: SHA1_HashBuf(hash,trust->derCert->data,trust->derCert->len); return pk11_NewTokenAttribute(type, hash, SHA1_LENGTH, PR_TRUE); case CKA_CERT_MD5_HASH: MD5_HashBuf(hash,trust->derCert->data,trust->derCert->len); return pk11_NewTokenAttribute(type, hash, MD5_LENGTH, PR_TRUE); case CKA_TRUST_CLIENT_AUTH: trustFlags = trust->trust->sslFlags & CERTDB_TRUSTED_CLIENT_CA ? trust->trust->sslFlags | CERTDB_TRUSTED_CA : 0 ; goto trust; case CKA_TRUST_SERVER_AUTH: trustFlags = trust->trust->sslFlags; goto trust; case CKA_TRUST_EMAIL_PROTECTION: trustFlags = trust->trust->emailFlags; goto trust; case CKA_TRUST_CODE_SIGNING: trustFlags = trust->trust->objectSigningFlags; trust: if (trustFlags & CERTDB_TRUSTED_CA ) { return (PK11Attribute *)&pk11_StaticTrustedDelegatorAttr; } if (trustFlags & CERTDB_TRUSTED) { return (PK11Attribute *)&pk11_StaticTrustedAttr; } if (trustFlags & CERTDB_NOT_TRUSTED) { return (PK11Attribute *)&pk11_StaticUnTrustedAttr; } if (trustFlags & CERTDB_TRUSTED_UNKNOWN) { return (PK11Attribute *)&pk11_StaticTrustUnknownAttr; } if (trustFlags & CERTDB_VALID_CA) { return (PK11Attribute *)&pk11_StaticValidDelegatorAttr; } if (trustFlags & CERTDB_VALID_PEER) { return (PK11Attribute *)&pk11_StaticValidPeerAttr; } return (PK11Attribute *)&pk11_StaticMustVerifyAttr; default: break; } #ifdef notdef switch (type) { case CKA_ISSUER: cert = pk11_getCertObject(object); if (cert == NULL) break; attr = pk11_NewTokenAttribute(type,cert->derIssuer.data, cert->derIssuer.len, PR_FALSE); case CKA_SERIAL_NUMBER: cert = pk11_getCertObject(object); if (cert == NULL) break; item = SEC_ASN1EncodeItem(NULL,NULL,cert,pk11_SerialTemplate); if (item == NULL) break; attr = pk11_NewTokenAttribute(type, item->data, item->len, PR_TRUE); SECITEM_FreeItem(item,PR_TRUE); } if (cert) { NSSLOWCERTDestroyCertificate(cert); return attr; } #endif return NULL; } static PK11Attribute * pk11_FindCrlAttribute(PK11TokenObject *object, CK_ATTRIBUTE_TYPE type) { certDBEntryRevocation *crl; switch (type) { case CKA_PRIVATE: case CKA_MODIFIABLE: return (PK11Attribute *) &pk11_StaticFalseAttr; case CKA_NETSCAPE_KRL: return (PK11Attribute *) ((object->obj.handle == PK11_TOKEN_KRL_HANDLE) ? &pk11_StaticTrueAttr : &pk11_StaticFalseAttr); case CKA_SUBJECT: return pk11_NewTokenAttribute(type,object->dbKey.data, object->dbKey.len, PR_FALSE); case CKA_NETSCAPE_URL: case CKA_VALUE: break; default: PORT_SetError(SEC_ERROR_INVALID_ARGS); return NULL; } crl = pk11_getCrl(object); if (!crl) { return NULL; } switch (type) { case CKA_NETSCAPE_URL: if (crl->url == NULL) { return (PK11Attribute *) &pk11_StaticNullAttr; } return pk11_NewTokenAttribute(type, crl->url, PORT_Strlen(crl->url)+1, PR_TRUE); case CKA_VALUE: return pk11_NewTokenAttribute(type, crl->derCrl.data, crl->derCrl.len, PR_FALSE); default: break; } return NULL; } static PK11Attribute * pk11_FindCertAttribute(PK11TokenObject *object, CK_ATTRIBUTE_TYPE type) { NSSLOWCERTCertificate *cert; NSSLOWKEYPublicKey *pubKey; unsigned char hash[SHA1_LENGTH]; SECItem *item; switch (type) { case CKA_PRIVATE: return (PK11Attribute *) &pk11_StaticFalseAttr; case CKA_MODIFIABLE: return (PK11Attribute *) &pk11_StaticTrueAttr; case CKA_CERTIFICATE_TYPE: /* hardcoding X.509 into here */ return (PK11Attribute *)&pk11_StaticX509Attr; case CKA_VALUE: case CKA_ID: case CKA_LABEL: case CKA_SUBJECT: case CKA_ISSUER: case CKA_SERIAL_NUMBER: case CKA_NETSCAPE_EMAIL: break; default: return NULL; } cert = pk11_getCert(object); if (cert == NULL) { return NULL; } switch (type) { case CKA_VALUE: return pk11_NewTokenAttribute(type,cert->derCert.data, cert->derCert.len,PR_FALSE); case CKA_ID: if (((cert->trust->sslFlags & CERTDB_USER) == 0) && ((cert->trust->emailFlags & CERTDB_USER) == 0) && ((cert->trust->objectSigningFlags & CERTDB_USER) == 0)) { return (PK11Attribute *) &pk11_StaticNullAttr; } pubKey = nsslowcert_ExtractPublicKey(cert); if (pubKey == NULL) break; item = pk11_GetPubItem(pubKey); if (item == NULL) { nsslowkey_DestroyPublicKey(pubKey); break; } SHA1_HashBuf(hash,item->data,item->len); /* item is imbedded in pubKey, just free the key */ nsslowkey_DestroyPublicKey(pubKey); return pk11_NewTokenAttribute(type, hash, SHA1_LENGTH, PR_TRUE); case CKA_LABEL: return cert->nickname ? pk11_NewTokenAttribute(type, cert->nickname, PORT_Strlen(cert->nickname), PR_FALSE) : (PK11Attribute *) &pk11_StaticNullAttr; case CKA_SUBJECT: return pk11_NewTokenAttribute(type,cert->derSubject.data, cert->derSubject.len, PR_FALSE); case CKA_ISSUER: return pk11_NewTokenAttribute(type,cert->derIssuer.data, cert->derIssuer.len, PR_FALSE); case CKA_SERIAL_NUMBER: return pk11_NewTokenAttribute(type,cert->derSN.data, cert->derSN.len, PR_FALSE); case CKA_NETSCAPE_EMAIL: return (cert->emailAddr && cert->emailAddr[0]) ? pk11_NewTokenAttribute(type, cert->emailAddr, PORT_Strlen(cert->emailAddr), PR_FALSE) : (PK11Attribute *) &pk11_StaticNullAttr; default: break; } return NULL; } static PK11Attribute * pk11_FindTokenAttribute(PK11TokenObject *object,CK_ATTRIBUTE_TYPE type) { /* handle the common ones */ switch (type) { case CKA_CLASS: return pk11_NewTokenAttribute(type,&object->obj.objclass, sizeof(object->obj.objclass),PR_FALSE); case CKA_TOKEN: return (PK11Attribute *) &pk11_StaticTrueAttr; case CKA_LABEL: if ( (object->obj.objclass == CKO_CERTIFICATE) || (object->obj.objclass == CKO_PRIVATE_KEY) || (object->obj.objclass == CKO_PUBLIC_KEY) || (object->obj.objclass == CKO_SECRET_KEY)) { break; } return (PK11Attribute *) &pk11_StaticNullAttr; default: break; } switch (object->obj.objclass) { case CKO_CERTIFICATE: return pk11_FindCertAttribute(object,type); case CKO_NETSCAPE_CRL: return pk11_FindCrlAttribute(object,type); case CKO_NETSCAPE_TRUST: return pk11_FindTrustAttribute(object,type); case CKO_NETSCAPE_SMIME: return pk11_FindSMIMEAttribute(object,type); case CKO_PUBLIC_KEY: return pk11_FindPublicKeyAttribute(object,type); case CKO_PRIVATE_KEY: return pk11_FindPrivateKeyAttribute(object,type); case CKO_SECRET_KEY: return pk11_FindSecretKeyAttribute(object,type); default: break; } PORT_Assert(0); return NULL; } /* * look up and attribute structure from a type and Object structure. * The returned attribute is referenced and needs to be freed when * it is no longer needed. */ PK11Attribute * pk11_FindAttribute(PK11Object *object,CK_ATTRIBUTE_TYPE type) { PK11Attribute *attribute; PK11SessionObject *sessObject = pk11_narrowToSessionObject(object); if (sessObject == NULL) { return pk11_FindTokenAttribute(pk11_narrowToTokenObject(object),type); } PK11_USE_THREADS(PZ_Lock(sessObject->attributeLock);) pk11queue_find(attribute,type,sessObject->head, sessObject->hashSize); #ifdef PKCS11_REF_COUNT_ATTRIBUTES if (attribute) { /* atomic increment would be nice here */ PK11_USE_THREADS(PZ_Lock(attribute->refLock);) attribute->refCount++; PK11_USE_THREADS(PZ_Unlock(attribute->refLock);) } #endif PK11_USE_THREADS(PZ_Unlock(sessObject->attributeLock);) return(attribute); } /* * Take a buffer and it's length and return it's true size in bits; */ unsigned int pk11_GetLengthInBits(unsigned char *buf, unsigned int bufLen) { unsigned int size = bufLen * 8; int i; /* Get the real length in bytes */ for (i=0; i < bufLen; i++) { unsigned char c = *buf++; if (c != 0) { unsigned char m; for (m=0x80; m > 0 ; m = m >> 1) { if ((c & m) != 0) { break; } size--; } break; } size-=8; } return size; } /* * Constrain a big num attribute. to size and padding * minLength means length of the object must be greater than equal to minLength * maxLength means length of the object must be less than equal to maxLength * minMultiple means that object length mod minMultiple must equal 0. * all input sizes are in bits. * if any constraint is '0' that constraint is not checked. */ CK_RV pk11_ConstrainAttribute(PK11Object *object, CK_ATTRIBUTE_TYPE type, int minLength, int maxLength, int minMultiple) { PK11Attribute *attribute; unsigned int size; unsigned char *ptr; attribute = pk11_FindAttribute(object, type); if (!attribute) { return CKR_TEMPLATE_INCOMPLETE; } ptr = (unsigned char *) attribute->attrib.pValue; if (ptr == NULL) { pk11_FreeAttribute(attribute); return CKR_ATTRIBUTE_VALUE_INVALID; } size = pk11_GetLengthInBits(ptr, attribute->attrib.ulValueLen); pk11_FreeAttribute(attribute); if ((minLength != 0) && (size < minLength)) { return CKR_ATTRIBUTE_VALUE_INVALID; } if ((maxLength != 0) && (size > maxLength)) { return CKR_ATTRIBUTE_VALUE_INVALID; } if ((minMultiple != 0) && ((size % minMultiple) != 0)) { return CKR_ATTRIBUTE_VALUE_INVALID; } return CKR_OK; } PRBool pk11_hasAttributeToken(PK11TokenObject *object) { return PR_FALSE; } /* * return true if object has attribute */ PRBool pk11_hasAttribute(PK11Object *object,CK_ATTRIBUTE_TYPE type) { PK11Attribute *attribute; PK11SessionObject *sessObject = pk11_narrowToSessionObject(object); if (sessObject == NULL) { return pk11_hasAttributeToken(pk11_narrowToTokenObject(object)); } PK11_USE_THREADS(PZ_Lock(sessObject->attributeLock);) pk11queue_find(attribute,type,sessObject->head, sessObject->hashSize); PK11_USE_THREADS(PZ_Unlock(sessObject->attributeLock);) return (PRBool)(attribute != NULL); } /* * add an attribute to an object */ static void pk11_AddAttribute(PK11Object *object,PK11Attribute *attribute) { PK11SessionObject *sessObject = pk11_narrowToSessionObject(object); if (sessObject == NULL) return; PK11_USE_THREADS(PZ_Lock(sessObject->attributeLock);) pk11queue_add(attribute,attribute->handle, sessObject->head, sessObject->hashSize); PK11_USE_THREADS(PZ_Unlock(sessObject->attributeLock);) } /* * copy an unsigned attribute into a SECItem. Secitem is allocated in * the specified arena. */ CK_RV pk11_Attribute2SSecItem(PLArenaPool *arena,SECItem *item,PK11Object *object, CK_ATTRIBUTE_TYPE type) { PK11Attribute *attribute; item->data = NULL; attribute = pk11_FindAttribute(object, type); if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE; (void)SECITEM_AllocItem(arena, item, attribute->attrib.ulValueLen); if (item->data == NULL) { pk11_FreeAttribute(attribute); return CKR_HOST_MEMORY; } PORT_Memcpy(item->data, attribute->attrib.pValue, item->len); pk11_FreeAttribute(attribute); return CKR_OK; } /* * delete an attribute from an object */ static void pk11_DeleteAttribute(PK11Object *object, PK11Attribute *attribute) { PK11SessionObject *sessObject = pk11_narrowToSessionObject(object); if (sessObject == NULL) { return ; } PK11_USE_THREADS(PZ_Lock(sessObject->attributeLock);) if (pk11queue_is_queued(attribute,attribute->handle, sessObject->head, sessObject->hashSize)) { pk11queue_delete(attribute,attribute->handle, sessObject->head, sessObject->hashSize); } PK11_USE_THREADS(PZ_Unlock(sessObject->attributeLock);) pk11_FreeAttribute(attribute); } /* * this is only valid for CK_BBOOL type attributes. Return the state * of that attribute. */ PRBool pk11_isTrue(PK11Object *object,CK_ATTRIBUTE_TYPE type) { PK11Attribute *attribute; PRBool tok = PR_FALSE; attribute=pk11_FindAttribute(object,type); if (attribute == NULL) { return PR_FALSE; } tok = (PRBool)(*(CK_BBOOL *)attribute->attrib.pValue); pk11_FreeAttribute(attribute); return tok; } /* * force an attribute to null. * this is for sensitive keys which are stored in the database, we don't * want to keep this info around in memory in the clear. */ void pk11_nullAttribute(PK11Object *object,CK_ATTRIBUTE_TYPE type) { PK11Attribute *attribute; attribute=pk11_FindAttribute(object,type); if (attribute == NULL) return; if (attribute->attrib.pValue != NULL) { PORT_Memset(attribute->attrib.pValue,0,attribute->attrib.ulValueLen); if (attribute->freeData) { PORT_Free(attribute->attrib.pValue); } attribute->freeData = PR_FALSE; attribute->attrib.pValue = NULL; attribute->attrib.ulValueLen = 0; } pk11_FreeAttribute(attribute); } static CK_RV pk11_SetCertAttribute(PK11TokenObject *to, CK_ATTRIBUTE_TYPE type, void *value, unsigned int len) { NSSLOWCERTCertificate *cert; char *nickname = NULL; SECStatus rv; /* we can't change the EMAIL values, but let the * upper layers feel better about the fact we tried to set these */ if (type == CKA_NETSCAPE_EMAIL) { return CKR_OK; } if (to->obj.slot->certDB == NULL) { return CKR_TOKEN_WRITE_PROTECTED; } if ((type != CKA_LABEL) && (type != CKA_ID)) { return CKR_ATTRIBUTE_READ_ONLY; } cert = pk11_getCert(to); if (cert == NULL) { return CKR_OBJECT_HANDLE_INVALID; } /* if the app is trying to set CKA_ID, it's probably because it just * imported the key. Look to see if we need to set the CERTDB_USER bits. */ if (type == CKA_ID) { if (((cert->trust->sslFlags & CERTDB_USER) == 0) && ((cert->trust->emailFlags & CERTDB_USER) == 0) && ((cert->trust->objectSigningFlags & CERTDB_USER) == 0)) { PK11Slot *slot = to->obj.slot; if (slot->keyDB && nsslowkey_KeyForCertExists(slot->keyDB,cert)) { NSSLOWCERTCertTrust trust = *cert->trust; trust.sslFlags |= CERTDB_USER; trust.emailFlags |= CERTDB_USER; trust.objectSigningFlags |= CERTDB_USER; nsslowcert_ChangeCertTrust(slot->certDB,cert,&trust); } } return CKR_OK; } /* must be CKA_LABEL */ if (value != NULL) { nickname = PORT_ZAlloc(len+1); if (nickname == NULL) { return CKR_HOST_MEMORY; } PORT_Memcpy(nickname,value,len); nickname[len] = 0; } rv = nsslowcert_AddPermNickname(to->obj.slot->certDB, cert, nickname); if (nickname) PORT_Free(nickname); if (rv != SECSuccess) { return CKR_DEVICE_ERROR; } return CKR_OK; } static CK_RV pk11_SetPrivateKeyAttribute(PK11TokenObject *to, CK_ATTRIBUTE_TYPE type, void *value, unsigned int len) { NSSLOWKEYPrivateKey *privKey; char *nickname = NULL; SECStatus rv; /* we can't change the ID and we don't store the subject, but let the * upper layers feel better about the fact we tried to set these */ if ((type == CKA_ID) || (type == CKA_SUBJECT)) { return CKR_OK; } if (to->obj.slot->keyDB == NULL) { return CKR_TOKEN_WRITE_PROTECTED; } if (type != CKA_LABEL) { return CKR_ATTRIBUTE_READ_ONLY; } privKey = pk11_GetPrivateKey(to); if (privKey == NULL) { return CKR_OBJECT_HANDLE_INVALID; } if (value != NULL) { nickname = PORT_ZAlloc(len+1); if (nickname == NULL) { return CKR_HOST_MEMORY; } PORT_Memcpy(nickname,value,len); nickname[len] = 0; } rv = nsslowkey_UpdateNickname(to->obj.slot->keyDB, privKey, &to->dbKey, nickname, to->obj.slot->password); if (nickname) PORT_Free(nickname); if (rv != SECSuccess) { return CKR_DEVICE_ERROR; } return CKR_OK; } static CK_RV pk11_SetTrustAttribute(PK11TokenObject *to, CK_ATTRIBUTE_TYPE type, void *value, unsigned int len) { unsigned int flags; CK_TRUST trust; NSSLOWCERTCertificate *cert; NSSLOWCERTCertTrust dbTrust; SECStatus rv; if (to->obj.slot->certDB == NULL) { return CKR_TOKEN_WRITE_PROTECTED; } if (len != sizeof (CK_TRUST)) { return CKR_ATTRIBUTE_VALUE_INVALID; } trust = *(CK_TRUST *)value; flags = pk11_MapTrust(trust, (PRBool) (type == CKA_TRUST_SERVER_AUTH)); cert = pk11_getCert(to); if (cert == NULL) { return CKR_OBJECT_HANDLE_INVALID; } dbTrust = *cert->trust; switch (type) { case CKA_TRUST_EMAIL_PROTECTION: dbTrust.emailFlags = flags | (cert->trust->emailFlags & CERTDB_PRESERVE_TRUST_BITS); break; case CKA_TRUST_CODE_SIGNING: dbTrust.objectSigningFlags = flags | (cert->trust->objectSigningFlags & CERTDB_PRESERVE_TRUST_BITS); break; case CKA_TRUST_CLIENT_AUTH: dbTrust.sslFlags = flags | (cert->trust->sslFlags & (CERTDB_PRESERVE_TRUST_BITS|CERTDB_TRUSTED_CA)); break; case CKA_TRUST_SERVER_AUTH: dbTrust.sslFlags = flags | (cert->trust->sslFlags & (CERTDB_PRESERVE_TRUST_BITS|CERTDB_TRUSTED_CLIENT_CA)); break; default: return CKR_ATTRIBUTE_READ_ONLY; } rv = nsslowcert_ChangeCertTrust(to->obj.slot->certDB,cert,&dbTrust); if (rv != SECSuccess) { return CKR_DEVICE_ERROR; } return CKR_OK; } static CK_RV pk11_forceTokenAttribute(PK11Object *object,CK_ATTRIBUTE_TYPE type, void *value, unsigned int len) { PK11Attribute *attribute; PK11TokenObject *to = pk11_narrowToTokenObject(object); CK_RV crv = CKR_ATTRIBUTE_READ_ONLY; PORT_Assert(to); if (to == NULL) { return CKR_DEVICE_ERROR; } /* if we are just setting it to the value we already have, * allow it to happen. Let label setting go through so * we have the opportunity to repair any database corruption. */ attribute=pk11_FindAttribute(object,type); if ((type != CKA_LABEL) && (attribute->attrib.ulValueLen == len) && PORT_Memcmp(attribute->attrib.pValue,value,len) == 0) { pk11_FreeAttribute(attribute); return CKR_OK; } switch (object->objclass) { case CKO_CERTIFICATE: /* change NICKNAME, EMAIL, */ crv = pk11_SetCertAttribute(to,type,value,len); break; case CKO_NETSCAPE_CRL: /* change URL */ break; case CKO_NETSCAPE_TRUST: crv = pk11_SetTrustAttribute(to,type,value,len); break; case CKO_PRIVATE_KEY: case CKO_SECRET_KEY: crv = pk11_SetPrivateKeyAttribute(to,type,value,len); break; } pk11_FreeAttribute(attribute); return crv; } /* * force an attribute to a spaecif value. */ CK_RV pk11_forceAttribute(PK11Object *object,CK_ATTRIBUTE_TYPE type, void *value, unsigned int len) { PK11Attribute *attribute; void *att_val = NULL; PRBool freeData = PR_FALSE; if (pk11_isToken(object->handle)) { return pk11_forceTokenAttribute(object,type,value,len); } attribute=pk11_FindAttribute(object,type); if (attribute == NULL) return pk11_AddAttributeType(object,type,value,len); if (value) { #ifdef PKCS11_STATIC_ATTRIBUTES if (len <= ATTR_SPACE) { att_val = attribute->space; } else { att_val = PORT_Alloc(len); freeData = PR_TRUE; } #else #ifdef PKCS11_REF_COUNT_ATTRIBUTES att_val = PORT_Alloc(len); freeData = PR_TRUE; #else att_val = PORT_ArenaAlloc(object->arena,len); #endif /* PKCS11_REF_COUNT_ATTRIBUTES */ #endif /* PKCS11_STATIC_ATTRIBUTES */ if (att_val == NULL) { return CKR_HOST_MEMORY; } if (attribute->attrib.pValue == att_val) { PORT_Memset(attribute->attrib.pValue,0, attribute->attrib.ulValueLen); } PORT_Memcpy(att_val,value,len); } if (attribute->attrib.pValue != NULL) { if (attribute->attrib.pValue != att_val) { PORT_Memset(attribute->attrib.pValue,0, attribute->attrib.ulValueLen); } if (attribute->freeData) { PORT_Free(attribute->attrib.pValue); } attribute->freeData = PR_FALSE; attribute->attrib.pValue = NULL; attribute->attrib.ulValueLen = 0; } if (att_val) { attribute->attrib.pValue = att_val; attribute->attrib.ulValueLen = len; attribute->freeData = freeData; } pk11_FreeAttribute(attribute); return CKR_OK; } /* * return a null terminated string from attribute 'type'. This string * is allocated and needs to be freed with PORT_Free() When complete. */ char * pk11_getString(PK11Object *object,CK_ATTRIBUTE_TYPE type) { PK11Attribute *attribute; char *label = NULL; attribute=pk11_FindAttribute(object,type); if (attribute == NULL) return NULL; if (attribute->attrib.pValue != NULL) { label = (char *) PORT_Alloc(attribute->attrib.ulValueLen+1); if (label == NULL) { pk11_FreeAttribute(attribute); return NULL; } PORT_Memcpy(label,attribute->attrib.pValue, attribute->attrib.ulValueLen); label[attribute->attrib.ulValueLen] = 0; } pk11_FreeAttribute(attribute); return label; } /* * decode when a particular attribute may be modified * PK11_NEVER: This attribute must be set at object creation time and * can never be modified. * PK11_ONCOPY: This attribute may be modified only when you copy the * object. * PK11_SENSITIVE: The CKA_SENSITIVE attribute can only be changed from * CK_FALSE to CK_TRUE. * PK11_ALWAYS: This attribute can always be modified. * Some attributes vary their modification type based on the class of the * object. */ PK11ModifyType pk11_modifyType(CK_ATTRIBUTE_TYPE type, CK_OBJECT_CLASS inClass) { /* if we don't know about it, user user defined, always allow modify */ PK11ModifyType mtype = PK11_ALWAYS; switch(type) { /* NEVER */ case CKA_CLASS: case CKA_CERTIFICATE_TYPE: case CKA_KEY_TYPE: case CKA_MODULUS: case CKA_MODULUS_BITS: case CKA_PUBLIC_EXPONENT: case CKA_PRIVATE_EXPONENT: case CKA_PRIME: case CKA_SUBPRIME: case CKA_BASE: case CKA_PRIME_1: case CKA_PRIME_2: case CKA_EXPONENT_1: case CKA_EXPONENT_2: case CKA_COEFFICIENT: case CKA_VALUE_LEN: case CKA_ALWAYS_SENSITIVE: case CKA_NEVER_EXTRACTABLE: case CKA_NETSCAPE_DB: mtype = PK11_NEVER; break; /* ONCOPY */ case CKA_TOKEN: case CKA_PRIVATE: case CKA_MODIFIABLE: mtype = PK11_ONCOPY; break; /* SENSITIVE */ case CKA_SENSITIVE: case CKA_EXTRACTABLE: mtype = PK11_SENSITIVE; break; /* ALWAYS */ case CKA_LABEL: case CKA_APPLICATION: case CKA_ID: case CKA_SERIAL_NUMBER: case CKA_START_DATE: case CKA_END_DATE: case CKA_DERIVE: case CKA_ENCRYPT: case CKA_DECRYPT: case CKA_SIGN: case CKA_VERIFY: case CKA_SIGN_RECOVER: case CKA_VERIFY_RECOVER: case CKA_WRAP: case CKA_UNWRAP: mtype = PK11_ALWAYS; break; /* DEPENDS ON CLASS */ case CKA_VALUE: mtype = (inClass == CKO_DATA) ? PK11_ALWAYS : PK11_NEVER; break; case CKA_SUBJECT: mtype = (inClass == CKO_CERTIFICATE) ? PK11_NEVER : PK11_ALWAYS; break; default: break; } return mtype; } /* decode if a particular attribute is sensitive (cannot be read * back to the user of if the object is set to SENSITIVE) */ PRBool pk11_isSensitive(CK_ATTRIBUTE_TYPE type, CK_OBJECT_CLASS inClass) { switch(type) { /* ALWAYS */ case CKA_PRIVATE_EXPONENT: case CKA_PRIME_1: case CKA_PRIME_2: case CKA_EXPONENT_1: case CKA_EXPONENT_2: case CKA_COEFFICIENT: return PR_TRUE; /* DEPENDS ON CLASS */ case CKA_VALUE: /* PRIVATE and SECRET KEYS have SENSITIVE values */ return (PRBool)((inClass == CKO_PRIVATE_KEY) || (inClass == CKO_SECRET_KEY)); default: break; } return PR_FALSE; } /* * copy an attribute into a SECItem. Secitem is allocated in the specified * arena. */ CK_RV pk11_Attribute2SecItem(PLArenaPool *arena,SECItem *item,PK11Object *object, CK_ATTRIBUTE_TYPE type) { int len; PK11Attribute *attribute; attribute = pk11_FindAttribute(object, type); if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE; len = attribute->attrib.ulValueLen; if (arena) { item->data = (unsigned char *) PORT_ArenaAlloc(arena,len); } else { item->data = (unsigned char *) PORT_Alloc(len); } if (item->data == NULL) { pk11_FreeAttribute(attribute); return CKR_HOST_MEMORY; } item->len = len; PORT_Memcpy(item->data,attribute->attrib.pValue, len); pk11_FreeAttribute(attribute); return CKR_OK; } CK_RV pk11_GetULongAttribute(PK11Object *object, CK_ATTRIBUTE_TYPE type, CK_ULONG *longData) { PK11Attribute *attribute; attribute = pk11_FindAttribute(object, type); if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE; if (attribute->attrib.ulValueLen != sizeof(CK_ULONG)) { return CKR_ATTRIBUTE_VALUE_INVALID; } *longData = *(CK_ULONG *)attribute->attrib.pValue; pk11_FreeAttribute(attribute); return CKR_OK; } void pk11_DeleteAttributeType(PK11Object *object,CK_ATTRIBUTE_TYPE type) { PK11Attribute *attribute; attribute = pk11_FindAttribute(object, type); if (attribute == NULL) return ; pk11_DeleteAttribute(object,attribute); pk11_FreeAttribute(attribute); } CK_RV pk11_AddAttributeType(PK11Object *object,CK_ATTRIBUTE_TYPE type,void *valPtr, CK_ULONG length) { PK11Attribute *attribute; attribute = pk11_NewAttribute(object,type,valPtr,length); if (attribute == NULL) { return CKR_HOST_MEMORY; } pk11_AddAttribute(object,attribute); return CKR_OK; } /* * ******************** Object Utilities ******************************* */ static SECStatus pk11_deleteTokenKeyByHandle(PK11Slot *slot, CK_OBJECT_HANDLE handle) { SECItem *item; PRBool rem; item = (SECItem *)PL_HashTableLookup(slot->tokenHashTable, (void *)handle); rem = PL_HashTableRemove(slot->tokenHashTable,(void *)handle) ; if (rem && item) { SECITEM_FreeItem(item,PR_TRUE); } return rem ? SECSuccess : SECFailure; } static SECStatus pk11_addTokenKeyByHandle(PK11Slot *slot, CK_OBJECT_HANDLE handle, SECItem *key) { PLHashEntry *entry; SECItem *item; item = SECITEM_DupItem(key); if (item == NULL) { return SECFailure; } entry = PL_HashTableAdd(slot->tokenHashTable,(void *)handle,item); if (entry == NULL) { SECITEM_FreeItem(item,PR_TRUE); return SECFailure; } return SECSuccess; } static SECItem * pk11_lookupTokenKeyByHandle(PK11Slot *slot, CK_OBJECT_HANDLE handle) { return (SECItem *)PL_HashTableLookup(slot->tokenHashTable, (void *)handle); } /* * use the refLock. This operations should be very rare, so the added * contention on the ref lock should be lower than the overhead of adding * a new lock. We use separate functions for this just in case I'm wrong. */ static void pk11_tokenKeyLock(PK11Slot *slot) { PK11_USE_THREADS(PZ_Lock(slot->objectLock);) } static void pk11_tokenKeyUnlock(PK11Slot *slot) { PK11_USE_THREADS(PZ_Unlock(slot->objectLock);) } /* allocation hooks that allow us to recycle old object structures */ static PK11ObjectFreeList sessionObjectList = { NULL, NULL, 0 }; static PK11ObjectFreeList tokenObjectList = { NULL, NULL, 0 }; PK11Object * pk11_GetObjectFromList(PRBool *hasLocks, PRBool optimizeSpace, PK11ObjectFreeList *list, unsigned int hashSize, PRBool isSessionObject) { PK11Object *object; int size = 0; if (!optimizeSpace) { if (list->lock == NULL) { list->lock = PZ_NewLock(nssILockObject); } PK11_USE_THREADS(PZ_Lock(list->lock)); object = list->head; if (object) { list->head = object->next; list->count--; } PK11_USE_THREADS(PZ_Unlock(list->lock)); if (object) { object->next = object->prev = NULL; *hasLocks = PR_TRUE; return object; } } size = isSessionObject ? sizeof(PK11SessionObject) + hashSize *sizeof(PK11Attribute *) : sizeof(PK11TokenObject); object = (PK11Object*)PORT_ZAlloc(size); if (isSessionObject) { ((PK11SessionObject *)object)->hashSize = hashSize; } *hasLocks = PR_FALSE; return object; } static void pk11_PutObjectToList(PK11Object *object, PK11ObjectFreeList *list, PRBool isSessionObject) { /* the code below is equivalent to : * optimizeSpace = isSessionObject ? object->optimizeSpace : PR_FALSE; * just faster. */ PRBool optimizeSpace = isSessionObject && ((PK11SessionObject *)object)->optimizeSpace; if (!optimizeSpace && (list->count < MAX_OBJECT_LIST_SIZE)) { if (list->lock == NULL) { list->lock = PZ_NewLock(nssILockObject); } PK11_USE_THREADS(PZ_Lock(list->lock)); object->next = list->head; list->head = object; list->count++; PK11_USE_THREADS(PZ_Unlock(list->lock)); return; } if (isSessionObject) { PK11SessionObject *so = (PK11SessionObject *)object; PK11_USE_THREADS(PZ_DestroyLock(so->attributeLock);) so->attributeLock = NULL; } PK11_USE_THREADS(PZ_DestroyLock(object->refLock);) object->refLock = NULL; PORT_Free(object); } static PK11Object * pk11_freeObjectData(PK11Object *object) { PK11Object *next = object->next; PORT_Free(object); return next; } static void pk11_CleanupFreeList(PK11ObjectFreeList *list, PRBool isSessionList) { PK11Object *object; if (!list->lock) { return; } PK11_USE_THREADS(PZ_Lock(list->lock)); for (object= list->head; object != NULL; object = pk11_freeObjectData(object)) { #ifdef PKCS11_USE_THREADS PZ_DestroyLock(object->refLock); if (isSessionList) { PZ_DestroyLock(((PK11SessionObject *)object)->attributeLock); } #endif } list->count = 0; list->head = NULL; PK11_USE_THREADS(PZ_Unlock(list->lock)); PK11_USE_THREADS(PZ_DestroyLock(list->lock)); list->lock = NULL; } void pk11_CleanupFreeLists(void) { pk11_CleanupFreeList(&sessionObjectList, PR_TRUE); pk11_CleanupFreeList(&tokenObjectList, PR_FALSE); } /* * Create a new object */ PK11Object * pk11_NewObject(PK11Slot *slot) { PK11Object *object; PK11SessionObject *sessObject; PRBool hasLocks = PR_FALSE; unsigned int i; unsigned int hashSize = 0; hashSize = (slot->optimizeSpace) ? SPACE_ATTRIBUTE_HASH_SIZE : TIME_ATTRIBUTE_HASH_SIZE; #ifdef PKCS11_STATIC_ATTRIBUTES object = pk11_GetObjectFromList(&hasLocks, slot->optimizeSpace, &sessionObjectList, hashSize, PR_TRUE); if (object == NULL) { return NULL; } sessObject = (PK11SessionObject *)object; sessObject->nextAttr = 0; for (i=0; i < MAX_OBJS_ATTRS; i++) { sessObject->attrList[i].attrib.pValue = NULL; sessObject->attrList[i].freeData = PR_FALSE; } #else PRArenaPool *arena; arena = PORT_NewArena(2048); if (arena == NULL) return NULL; object = (PK11Object*)PORT_ArenaAlloc(arena,sizeof(PK11SessionObject) +hashSize * sizeof(PK11Attribute *)); if (object == NULL) { PORT_FreeArena(arena,PR_FALSE); return NULL; } object->arena = arena; sessObject = (PK11SessionObject *)object; sessObject->hashSize = hashSize; #endif sessObject->optimizeSpace = slot->optimizeSpace; object->handle = 0; object->next = object->prev = NULL; object->slot = slot; object->refCount = 1; sessObject->sessionList.next = NULL; sessObject->sessionList.prev = NULL; sessObject->sessionList.parent = object; sessObject->session = NULL; sessObject->wasDerived = PR_FALSE; #ifdef PKCS11_USE_THREADS if (!hasLocks) object->refLock = PZ_NewLock(nssILockRefLock); if (object->refLock == NULL) { #ifdef PKCS11_STATIC_ATTRIBUTES PORT_Free(object); #else PORT_FreeArena(arena,PR_FALSE); #endif return NULL; } if (!hasLocks) sessObject->attributeLock = PZ_NewLock(nssILockAttribute); if (sessObject->attributeLock == NULL) { PK11_USE_THREADS(PZ_DestroyLock(object->refLock);) #ifdef PKCS11_STATIC_ATTRIBUTES PORT_Free(object); #else PORT_FreeArena(arena,PR_FALSE); #endif return NULL; } #else sessObject->attributeLock = NULL; object->refLock = NULL; #endif for (i=0; i < sessObject->hashSize; i++) { sessObject->head[i] = NULL; } object->objectInfo = NULL; object->infoFree = NULL; return object; } static CK_RV pk11_DestroySessionObjectData(PK11SessionObject *so) { #if defined(PKCS11_STATIC_ATTRIBUTES) || defined(PKCS11_REF_COUNT_ATTRIBUTES) int i; #endif #ifdef PKCS11_STATIC_ATTRIBUTES for (i=0; i < MAX_OBJS_ATTRS; i++) { unsigned char *value = so->attrList[i].attrib.pValue; if (value) { PORT_Memset(value,0,so->attrList[i].attrib.ulValueLen); if (so->attrList[i].freeData) { PORT_Free(value); } so->attrList[i].attrib.pValue = NULL; so->attrList[i].freeData = PR_FALSE; } } #endif #ifdef PKCS11_REF_COUNT_ATTRIBUTES /* clean out the attributes */ /* since no one is referencing us, it's safe to walk the chain * without a lock */ for (i=0; i < so->hashSize; i++) { PK11Attribute *ap,*next; for (ap = so->head[i]; ap != NULL; ap = next) { next = ap->next; /* paranoia */ ap->next = ap->prev = NULL; pk11_FreeAttribute(ap); } so->head[i] = NULL; } #endif /* PK11_USE_THREADS(PZ_DestroyLock(so->attributeLock));*/ return CKR_OK; } /* * free all the data associated with an object. Object reference count must * be 'zero'. */ static CK_RV pk11_DestroyObject(PK11Object *object) { CK_RV crv = CKR_OK; PK11SessionObject *so = pk11_narrowToSessionObject(object); PK11TokenObject *to = pk11_narrowToTokenObject(object); PORT_Assert(object->refCount == 0); /* delete the database value */ if (to) { if (to->dbKey.data) { PORT_Free(to->dbKey.data); to->dbKey.data = NULL; } } if (so) { pk11_DestroySessionObjectData(so); } if (object->objectInfo) { (*object->infoFree)(object->objectInfo); object->objectInfo = NULL; object->infoFree = NULL; } #ifdef PKCS11_STATIC_ATTRIBUTES if (so) { pk11_PutObjectToList(object,&sessionObjectList,PR_TRUE); } else { pk11_PutObjectToList(object,&tokenObjectList,PR_FALSE); } #else if (object->refLock) { PK11_USE_THREADS(PZ_DestroyLock(object->refLock);) } arena = object->arena; PORT_FreeArena(arena,PR_FALSE); #endif return crv; } void pk11_ReferenceObject(PK11Object *object) { PK11_USE_THREADS(PZ_Lock(object->refLock);) object->refCount++; PK11_USE_THREADS(PZ_Unlock(object->refLock);) } static PK11Object * pk11_ObjectFromHandleOnSlot(CK_OBJECT_HANDLE handle, PK11Slot *slot) { PK11Object *object; PRUint32 index = pk11_hash(handle, slot->tokObjHashSize); if (pk11_isToken(handle)) { return pk11_NewTokenObject(slot, NULL, handle); } PK11_USE_THREADS(PZ_Lock(slot->objectLock);) pk11queue_find2(object, handle, index, slot->tokObjects); if (object) { pk11_ReferenceObject(object); } PK11_USE_THREADS(PZ_Unlock(slot->objectLock);) return(object); } /* * look up and object structure from a handle. OBJECT_Handles only make * sense in terms of a given session. make a reference to that object * structure returned. */ PK11Object * pk11_ObjectFromHandle(CK_OBJECT_HANDLE handle, PK11Session *session) { PK11Slot *slot = pk11_SlotFromSession(session); return pk11_ObjectFromHandleOnSlot(handle,slot); } /* * release a reference to an object handle */ PK11FreeStatus pk11_FreeObject(PK11Object *object) { PRBool destroy = PR_FALSE; CK_RV crv; PK11_USE_THREADS(PZ_Lock(object->refLock);) if (object->refCount == 1) destroy = PR_TRUE; object->refCount--; PK11_USE_THREADS(PZ_Unlock(object->refLock);) if (destroy) { crv = pk11_DestroyObject(object); if (crv != CKR_OK) { return PK11_DestroyFailure; } return PK11_Destroyed; } return PK11_Busy; } /* * add an object to a slot and session queue. These two functions * adopt the object. */ void pk11_AddSlotObject(PK11Slot *slot, PK11Object *object) { PRUint32 index = pk11_hash(object->handle, slot->tokObjHashSize); pk11queue_init_element(object); PK11_USE_THREADS(PZ_Lock(slot->objectLock);) pk11queue_add2(object, object->handle, index, slot->tokObjects); PK11_USE_THREADS(PZ_Unlock(slot->objectLock);) } void pk11_AddObject(PK11Session *session, PK11Object *object) { PK11Slot *slot = pk11_SlotFromSession(session); PK11SessionObject *so = pk11_narrowToSessionObject(object); if (so) { PK11_USE_THREADS(PZ_Lock(session->objectLock);) pk11queue_add(&so->sessionList,0,session->objects,0); so->session = session; PK11_USE_THREADS(PZ_Unlock(session->objectLock);) } pk11_AddSlotObject(slot,object); pk11_ReferenceObject(object); } /* * add an object to a slot andsession queue */ CK_RV pk11_DeleteObject(PK11Session *session, PK11Object *object) { PK11Slot *slot = pk11_SlotFromSession(session); PK11SessionObject *so = pk11_narrowToSessionObject(object); PK11TokenObject *to = pk11_narrowToTokenObject(object); CK_RV crv = CKR_OK; SECStatus rv; NSSLOWCERTCertificate *cert; NSSLOWCERTCertTrust tmptrust; PRBool isKrl; PRUint32 index = pk11_hash(object->handle, slot->tokObjHashSize); /* Handle Token case */ if (so && so->session) { PK11Session *session = so->session; PK11_USE_THREADS(PZ_Lock(session->objectLock);) pk11queue_delete(&so->sessionList,0,session->objects,0); PK11_USE_THREADS(PZ_Unlock(session->objectLock);) PK11_USE_THREADS(PZ_Lock(slot->objectLock);) pk11queue_delete2(object, object->handle, index, slot->tokObjects); PK11_USE_THREADS(PZ_Unlock(slot->objectLock);) pk11queue_clear_deleted_element(object); pk11_FreeObject(object); /* reduce it's reference count */ } else { PORT_Assert(to); /* remove the objects from the real data base */ switch (object->handle & PK11_TOKEN_TYPE_MASK) { case PK11_TOKEN_TYPE_PRIV: case PK11_TOKEN_TYPE_KEY: /* KEYID is the public KEY for DSA and DH, and the MODULUS for * RSA */ PORT_Assert(slot->keyDB); rv = nsslowkey_DeleteKey(slot->keyDB, &to->dbKey); if (rv != SECSuccess) crv= CKR_DEVICE_ERROR; break; case PK11_TOKEN_TYPE_PUB: break; /* public keys only exist at the behest of the priv key */ case PK11_TOKEN_TYPE_CERT: cert = nsslowcert_FindCertByKey(slot->certDB,&to->dbKey); if (cert == NULL) { crv = CKR_DEVICE_ERROR; break; } rv = nsslowcert_DeletePermCertificate(cert); if (rv != SECSuccess) crv = CKR_DEVICE_ERROR; nsslowcert_DestroyCertificate(cert); break; case PK11_TOKEN_TYPE_CRL: isKrl = (PRBool) (object->handle == PK11_TOKEN_KRL_HANDLE); rv = nsslowcert_DeletePermCRL(slot->certDB,&to->dbKey,isKrl); if (rv == SECFailure) crv = CKR_DEVICE_ERROR; break; case PK11_TOKEN_TYPE_TRUST: cert = nsslowcert_FindCertByKey(slot->certDB,&to->dbKey); if (cert == NULL) { crv = CKR_DEVICE_ERROR; break; } tmptrust = *cert->trust; tmptrust.sslFlags &= CERTDB_PRESERVE_TRUST_BITS; tmptrust.emailFlags &= CERTDB_PRESERVE_TRUST_BITS; tmptrust.objectSigningFlags &= CERTDB_PRESERVE_TRUST_BITS; tmptrust.sslFlags |= CERTDB_TRUSTED_UNKNOWN; tmptrust.emailFlags |= CERTDB_TRUSTED_UNKNOWN; tmptrust.objectSigningFlags |= CERTDB_TRUSTED_UNKNOWN; rv = nsslowcert_ChangeCertTrust(slot->certDB,cert,&tmptrust); if (rv != SECSuccess) crv = CKR_DEVICE_ERROR; nsslowcert_DestroyCertificate(cert); break; default: break; } pk11_tokenKeyLock(object->slot); pk11_deleteTokenKeyByHandle(object->slot,object->handle); pk11_tokenKeyUnlock(object->slot); } return crv; } /* * copy the attributes from one object to another. Don't overwrite existing * attributes. NOTE: This is a pretty expensive operation since it * grabs the attribute locks for the src object for a *long* time. */ CK_RV pk11_CopyObject(PK11Object *destObject,PK11Object *srcObject) { PK11Attribute *attribute; PK11SessionObject *src_so = pk11_narrowToSessionObject(srcObject); unsigned int i; if (src_so == NULL) { return CKR_DEVICE_ERROR; /* can't copy token objects yet */ } PK11_USE_THREADS(PZ_Lock(src_so->attributeLock);) for(i=0; i < src_so->hashSize; i++) { attribute = src_so->head[i]; do { if (attribute) { if (!pk11_hasAttribute(destObject,attribute->handle)) { /* we need to copy the attribute since each attribute * only has one set of link list pointers */ PK11Attribute *newAttribute = pk11_NewAttribute( destObject,pk11_attr_expand(&attribute->attrib)); if (newAttribute == NULL) { PK11_USE_THREADS(PZ_Unlock(src_so->attributeLock);) return CKR_HOST_MEMORY; } pk11_AddAttribute(destObject,newAttribute); } attribute=attribute->next; } } while (attribute != NULL); } PK11_USE_THREADS(PZ_Unlock(src_so->attributeLock);) return CKR_OK; } /* * ******************** Search Utilities ******************************* */ /* add an object to a search list */ CK_RV AddToList(PK11ObjectListElement **list,PK11Object *object) { PK11ObjectListElement *newElem = (PK11ObjectListElement *)PORT_Alloc(sizeof(PK11ObjectListElement)); if (newElem == NULL) return CKR_HOST_MEMORY; newElem->next = *list; newElem->object = object; pk11_ReferenceObject(object); *list = newElem; return CKR_OK; } /* return true if the object matches the template */ PRBool pk11_objectMatch(PK11Object *object,CK_ATTRIBUTE_PTR theTemplate,int count) { int i; for (i=0; i < count; i++) { PK11Attribute *attribute = pk11_FindAttribute(object,theTemplate[i].type); if (attribute == NULL) { return PR_FALSE; } if (attribute->attrib.ulValueLen == theTemplate[i].ulValueLen) { if (PORT_Memcmp(attribute->attrib.pValue,theTemplate[i].pValue, theTemplate[i].ulValueLen) == 0) { pk11_FreeAttribute(attribute); continue; } } pk11_FreeAttribute(attribute); return PR_FALSE; } return PR_TRUE; } /* search through all the objects in the queue and return the template matches * in the object list. */ CK_RV pk11_searchObjectList(PK11SearchResults *search,PK11Object **head, unsigned int size, PZLock *lock, CK_ATTRIBUTE_PTR theTemplate, int count, PRBool isLoggedIn) { unsigned int i; PK11Object *object; CK_RV crv = CKR_OK; for(i=0; i < size; i++) { /* We need to hold the lock to copy a consistant version of * the linked list. */ PK11_USE_THREADS(PZ_Lock(lock);) for (object = head[i]; object != NULL; object= object->next) { if (pk11_objectMatch(object,theTemplate,count)) { /* don't return objects that aren't yet visible */ if ((!isLoggedIn) && pk11_isTrue(object,CKA_PRIVATE)) continue; pk11_addHandle(search,object->handle); } } PK11_USE_THREADS(PZ_Unlock(lock);) } return crv; } /* * free a single list element. Return the Next object in the list. */ PK11ObjectListElement * pk11_FreeObjectListElement(PK11ObjectListElement *objectList) { PK11ObjectListElement *ol = objectList->next; pk11_FreeObject(objectList->object); PORT_Free(objectList); return ol; } /* free an entire object list */ void pk11_FreeObjectList(PK11ObjectListElement *objectList) { PK11ObjectListElement *ol; for (ol= objectList; ol != NULL; ol = pk11_FreeObjectListElement(ol)) {} } /* * free a search structure */ void pk11_FreeSearch(PK11SearchResults *search) { if (search->handles) { PORT_Free(search->handles); } PORT_Free(search); } /* * ******************** Session Utilities ******************************* */ /* update the sessions state based in it's flags and wether or not it's * logged in */ void pk11_update_state(PK11Slot *slot,PK11Session *session) { if (slot->isLoggedIn) { if (slot->ssoLoggedIn) { session->info.state = CKS_RW_SO_FUNCTIONS; } else if (session->info.flags & CKF_RW_SESSION) { session->info.state = CKS_RW_USER_FUNCTIONS; } else { session->info.state = CKS_RO_USER_FUNCTIONS; } } else { if (session->info.flags & CKF_RW_SESSION) { session->info.state = CKS_RW_PUBLIC_SESSION; } else { session->info.state = CKS_RO_PUBLIC_SESSION; } } } /* update the state of all the sessions on a slot */ void pk11_update_all_states(PK11Slot *slot) { unsigned int i; PK11Session *session; for (i=0; i < slot->sessHashSize; i++) { PK11_USE_THREADS(PZ_Lock(PK11_SESSION_LOCK(slot,i));) for (session = slot->head[i]; session; session = session->next) { pk11_update_state(slot,session); } PK11_USE_THREADS(PZ_Unlock(PK11_SESSION_LOCK(slot,i));) } } /* * context are cipher and digest contexts that are associated with a session */ void pk11_FreeContext(PK11SessionContext *context) { if (context->cipherInfo) { (*context->destroy)(context->cipherInfo,PR_TRUE); } if (context->hashInfo) { (*context->hashdestroy)(context->hashInfo,PR_TRUE); } if (context->key) { pk11_FreeObject(context->key); context->key = NULL; } PORT_Free(context); } /* * create a new nession. NOTE: The session handle is not set, and the * session is not added to the slot's session queue. */ PK11Session * pk11_NewSession(CK_SLOT_ID slotID, CK_NOTIFY notify, CK_VOID_PTR pApplication, CK_FLAGS flags) { PK11Session *session; PK11Slot *slot = pk11_SlotFromID(slotID); if (slot == NULL) return NULL; session = (PK11Session*)PORT_Alloc(sizeof(PK11Session)); if (session == NULL) return NULL; session->next = session->prev = NULL; session->refCount = 1; session->enc_context = NULL; session->hash_context = NULL; session->sign_context = NULL; session->search = NULL; session->objectIDCount = 1; #ifdef PKCS11_USE_THREADS session->objectLock = PZ_NewLock(nssILockObject); if (session->objectLock == NULL) { PORT_Free(session); return NULL; } #else session->objectLock = NULL; #endif session->objects[0] = NULL; session->slot = slot; session->notify = notify; session->appData = pApplication; session->info.flags = flags; session->info.slotID = slotID; session->info.ulDeviceError = 0; pk11_update_state(slot,session); return session; } /* free all the data associated with a session. */ static void pk11_DestroySession(PK11Session *session) { PK11ObjectList *op,*next; PORT_Assert(session->refCount == 0); /* clean out the attributes */ /* since no one is referencing us, it's safe to walk the chain * without a lock */ for (op = session->objects[0]; op != NULL; op = next) { next = op->next; /* paranoia */ op->next = op->prev = NULL; pk11_DeleteObject(session,op->parent); } PK11_USE_THREADS(PZ_DestroyLock(session->objectLock);) if (session->enc_context) { pk11_FreeContext(session->enc_context); } if (session->hash_context) { pk11_FreeContext(session->hash_context); } if (session->sign_context) { pk11_FreeContext(session->sign_context); } if (session->search) { pk11_FreeSearch(session->search); } PORT_Free(session); } /* * look up a session structure from a session handle * generate a reference to it. */ PK11Session * pk11_SessionFromHandle(CK_SESSION_HANDLE handle) { PK11Slot *slot = pk11_SlotFromSessionHandle(handle); PK11Session *session; PK11_USE_THREADS(PZ_Lock(PK11_SESSION_LOCK(slot,handle));) pk11queue_find(session,handle,slot->head,slot->sessHashSize); if (session) session->refCount++; PK11_USE_THREADS(PZ_Unlock(PK11_SESSION_LOCK(slot,handle));) return (session); } /* * release a reference to a session handle */ void pk11_FreeSession(PK11Session *session) { PRBool destroy = PR_FALSE; PK11_USE_THREADS(PK11Slot *slot = pk11_SlotFromSession(session);) PK11_USE_THREADS(PZ_Lock(PK11_SESSION_LOCK(slot,session->handle));) if (session->refCount == 1) destroy = PR_TRUE; session->refCount--; PK11_USE_THREADS(PZ_Unlock(PK11_SESSION_LOCK(slot,session->handle));) if (destroy) pk11_DestroySession(session); } /* * handle Token Object stuff */ static void pk11_XORHash(unsigned char *key, unsigned char *dbkey, int len) { int i; PORT_Memset(key, 0, 4); for (i=0; i < len-4; i += 4) { key[0] ^= dbkey[i]; key[1] ^= dbkey[i+1]; key[2] ^= dbkey[i+2]; key[3] ^= dbkey[i+3]; } } /* Make a token handle for an object and record it so we can find it again */ CK_OBJECT_HANDLE pk11_mkHandle(PK11Slot *slot, SECItem *dbKey, CK_OBJECT_HANDLE class) { unsigned char hashBuf[4]; CK_OBJECT_HANDLE handle; SECItem *key; handle = class; /* there is only one KRL, use a fixed handle for it */ if (handle != PK11_TOKEN_KRL_HANDLE) { pk11_XORHash(hashBuf,dbKey->data,dbKey->len); handle = (hashBuf[0] << 24) | (hashBuf[1] << 16) | (hashBuf[2] << 8) | hashBuf[3]; handle = PK11_TOKEN_MAGIC | class | (handle & ~(PK11_TOKEN_TYPE_MASK|PK11_TOKEN_MASK)); /* we have a CRL who's handle has randomly matched the reserved KRL * handle, increment it */ if (handle == PK11_TOKEN_KRL_HANDLE) { handle++; } } pk11_tokenKeyLock(slot); while ((key = pk11_lookupTokenKeyByHandle(slot,handle)) != NULL) { if (SECITEM_ItemsAreEqual(key,dbKey)) { pk11_tokenKeyUnlock(slot); return handle; } handle++; } pk11_addTokenKeyByHandle(slot,handle,dbKey); pk11_tokenKeyUnlock(slot); return handle; } PRBool pk11_poisonHandle(PK11Slot *slot, SECItem *dbKey, CK_OBJECT_HANDLE class) { unsigned char hashBuf[4]; CK_OBJECT_HANDLE handle; SECItem *key; handle = class; /* there is only one KRL, use a fixed handle for it */ if (handle != PK11_TOKEN_KRL_HANDLE) { pk11_XORHash(hashBuf,dbKey->data,dbKey->len); handle = (hashBuf[0] << 24) | (hashBuf[1] << 16) | (hashBuf[2] << 8) | hashBuf[3]; handle = PK11_TOKEN_MAGIC | class | (handle & ~(PK11_TOKEN_TYPE_MASK|PK11_TOKEN_MASK)); /* we have a CRL who's handle has randomly matched the reserved KRL * handle, increment it */ if (handle == PK11_TOKEN_KRL_HANDLE) { handle++; } } pk11_tokenKeyLock(slot); while ((key = pk11_lookupTokenKeyByHandle(slot,handle)) != NULL) { if (SECITEM_ItemsAreEqual(key,dbKey)) { key->data[0] ^= 0x80; pk11_tokenKeyUnlock(slot); return PR_TRUE; } handle++; } pk11_tokenKeyUnlock(slot); return PR_FALSE; } void pk11_addHandle(PK11SearchResults *search, CK_OBJECT_HANDLE handle) { if (search->handles == NULL) { return; } if (search->size >= search->array_size) { search->array_size += NSC_SEARCH_BLOCK_SIZE; search->handles = (CK_OBJECT_HANDLE *) PORT_Realloc(search->handles, sizeof(CK_OBJECT_HANDLE)* search->array_size); if (search->handles == NULL) { return; } } search->handles[search->size] = handle; search->size++; } static const CK_OBJECT_HANDLE pk11_classArray[] = { 0, CKO_PRIVATE_KEY, CKO_PUBLIC_KEY, CKO_SECRET_KEY, CKO_NETSCAPE_TRUST, CKO_NETSCAPE_CRL, CKO_NETSCAPE_SMIME, CKO_CERTIFICATE }; #define handleToClass(handle) \ pk11_classArray[((handle & PK11_TOKEN_TYPE_MASK))>>28] PK11Object * pk11_NewTokenObject(PK11Slot *slot, SECItem *dbKey, CK_OBJECT_HANDLE handle) { PK11Object *object = NULL; PK11TokenObject *tokObject = NULL; PRBool hasLocks = PR_FALSE; SECStatus rv; #ifdef PKCS11_STATIC_ATTRIBUTES object = pk11_GetObjectFromList(&hasLocks, PR_FALSE, &tokenObjectList, 0, PR_FALSE); if (object == NULL) { return NULL; } #else PRArenaPool *arena; arena = PORT_NewArena(2048); if (arena == NULL) return NULL; object = (PK11Object*)PORT_ArenaZAlloc(arena,sizeof(PK11TokenObject)); if (object == NULL) { PORT_FreeArena(arena,PR_FALSE); return NULL; } object->arena = arena; #endif tokObject = (PK11TokenObject *) object; object->objclass = handleToClass(handle); object->handle = handle; object->slot = slot; object->objectInfo = NULL; object->infoFree = NULL; if (dbKey == NULL) { pk11_tokenKeyLock(slot); dbKey = pk11_lookupTokenKeyByHandle(slot,handle); if (dbKey == NULL) { pk11_tokenKeyUnlock(slot); goto loser; } rv = SECITEM_CopyItem(NULL,&tokObject->dbKey,dbKey); pk11_tokenKeyUnlock(slot); } else { rv = SECITEM_CopyItem(NULL,&tokObject->dbKey,dbKey); } if (rv != SECSuccess) { goto loser; } #ifdef PKCS11_USE_THREADS if (!hasLocks) { object->refLock = PZ_NewLock(nssILockRefLock); } if (object->refLock == NULL) { goto loser; } #endif object->refCount = 1; return object; loser: if (object) { (void) pk11_DestroyObject(object); } return NULL; } PRBool pk11_tokenMatch(PK11Slot *slot, SECItem *dbKey, CK_OBJECT_HANDLE class, CK_ATTRIBUTE_PTR theTemplate,int count) { PK11Object *object; PRBool ret; object = pk11_NewTokenObject(slot,dbKey,PK11_TOKEN_MASK|class); if (object == NULL) { return PR_FALSE; } ret = pk11_objectMatch(object,theTemplate,count); pk11_FreeObject(object); return ret; } PK11TokenObject * pk11_convertSessionToToken(PK11Object *obj) { SECItem *key; PK11SessionObject *so = (PK11SessionObject *)obj; PK11TokenObject *to = pk11_narrowToTokenObject(obj); SECStatus rv; pk11_DestroySessionObjectData(so); PK11_USE_THREADS(PZ_DestroyLock(so->attributeLock)); if (to == NULL) { return NULL; } pk11_tokenKeyLock(so->obj.slot); key = pk11_lookupTokenKeyByHandle(so->obj.slot,so->obj.handle); if (key == NULL) { pk11_tokenKeyUnlock(so->obj.slot); return NULL; } rv = SECITEM_CopyItem(NULL,&to->dbKey,key); pk11_tokenKeyUnlock(so->obj.slot); if (rv == SECFailure) { return NULL; } return to; } PK11SessionObject * pk11_narrowToSessionObject(PK11Object *obj) { return !pk11_isToken(obj->handle) ? (PK11SessionObject *)obj : NULL; } PK11TokenObject * pk11_narrowToTokenObject(PK11Object *obj) { return pk11_isToken(obj->handle) ? (PK11TokenObject *)obj : NULL; }