michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: /* michael@0: * The following code handles the storage of PKCS 11 modules used by the michael@0: * NSS. For the rest of NSS, only one kind of database handle exists: michael@0: * michael@0: * SFTKDBHandle michael@0: * michael@0: * There is one SFTKDBHandle for the each key database and one for each cert michael@0: * database. These databases are opened as associated pairs, one pair per michael@0: * slot. SFTKDBHandles are reference counted objects. michael@0: * michael@0: * Each SFTKDBHandle points to a low level database handle (SDB). This handle michael@0: * represents the underlying physical database. These objects are not michael@0: * reference counted, an are 'owned' by their respective SFTKDBHandles. michael@0: * michael@0: * michael@0: */ michael@0: #include "sftkdb.h" michael@0: #include "sftkdbti.h" michael@0: #include "pkcs11t.h" michael@0: #include "pkcs11i.h" michael@0: #include "sdb.h" michael@0: #include "prprf.h" michael@0: #include "secasn1.h" michael@0: #include "pratom.h" michael@0: #include "blapi.h" michael@0: #include "secoid.h" michael@0: #include "lowpbe.h" michael@0: #include "secdert.h" michael@0: #include "prsystem.h" michael@0: #include "lgglue.h" michael@0: #include "secerr.h" michael@0: #include "softoken.h" michael@0: michael@0: /****************************************************************** michael@0: * michael@0: * Key DB password handling functions michael@0: * michael@0: * These functions manage the key db password (set, reset, initialize, use). michael@0: * michael@0: * The key is managed on 'this side' of the database. All private data is michael@0: * encrypted before it is sent to the database itself. Besides PBE's, the michael@0: * database management code can also mix in various fixed keys so the data michael@0: * in the database is no longer considered 'plain text'. michael@0: */ michael@0: michael@0: michael@0: /* take string password and turn it into a key. The key is dependent michael@0: * on a global salt entry acquired from the database. This salted michael@0: * value will be based to a pkcs5 pbe function before it is used michael@0: * in an actual encryption */ michael@0: static SECStatus michael@0: sftkdb_passwordToKey(SFTKDBHandle *keydb, SECItem *salt, michael@0: const char *pw, SECItem *key) michael@0: { michael@0: SHA1Context *cx = NULL; michael@0: SECStatus rv = SECFailure; michael@0: michael@0: key->data = PORT_Alloc(SHA1_LENGTH); michael@0: if (key->data == NULL) { michael@0: goto loser; michael@0: } michael@0: key->len = SHA1_LENGTH; michael@0: michael@0: cx = SHA1_NewContext(); michael@0: if ( cx == NULL) { michael@0: goto loser; michael@0: } michael@0: SHA1_Begin(cx); michael@0: if (salt && salt->data ) { michael@0: SHA1_Update(cx, salt->data, salt->len); michael@0: } michael@0: SHA1_Update(cx, (unsigned char *)pw, PORT_Strlen(pw)); michael@0: SHA1_End(cx, key->data, &key->len, key->len); michael@0: rv = SECSuccess; michael@0: michael@0: loser: michael@0: if (cx) { michael@0: SHA1_DestroyContext(cx, PR_TRUE); michael@0: } michael@0: if (rv != SECSuccess) { michael@0: if (key->data != NULL) { michael@0: PORT_ZFree(key->data,key->len); michael@0: } michael@0: key->data = NULL; michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * Cipher text stored in the database contains 3 elements: michael@0: * 1) an identifier describing the encryption algorithm. michael@0: * 2) an entry specific salt value. michael@0: * 3) the encrypted value. michael@0: * michael@0: * The following data structure represents the encrypted data in a decoded michael@0: * (but still encrypted) form. michael@0: */ michael@0: typedef struct sftkCipherValueStr sftkCipherValue; michael@0: struct sftkCipherValueStr { michael@0: PLArenaPool *arena; michael@0: SECOidTag alg; michael@0: NSSPKCS5PBEParameter *param; michael@0: SECItem salt; michael@0: SECItem value; michael@0: }; michael@0: michael@0: #define SFTK_CIPHERTEXT_VERSION 3 michael@0: michael@0: struct SFTKDBEncryptedDataInfoStr { michael@0: SECAlgorithmID algorithm; michael@0: SECItem encryptedData; michael@0: }; michael@0: typedef struct SFTKDBEncryptedDataInfoStr SFTKDBEncryptedDataInfo; michael@0: michael@0: SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate) michael@0: michael@0: const SEC_ASN1Template sftkdb_EncryptedDataInfoTemplate[] = { michael@0: { SEC_ASN1_SEQUENCE, michael@0: 0, NULL, sizeof(SFTKDBEncryptedDataInfo) }, michael@0: { SEC_ASN1_INLINE | SEC_ASN1_XTRN , michael@0: offsetof(SFTKDBEncryptedDataInfo,algorithm), michael@0: SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) }, michael@0: { SEC_ASN1_OCTET_STRING, michael@0: offsetof(SFTKDBEncryptedDataInfo,encryptedData) }, michael@0: { 0 } michael@0: }; michael@0: michael@0: /* michael@0: * This parses the cipherText into cipher value. NOTE: cipherValue will point michael@0: * to data in cipherText, if cipherText is freed, cipherValue will be invalid. michael@0: */ michael@0: static SECStatus michael@0: sftkdb_decodeCipherText(SECItem *cipherText, sftkCipherValue *cipherValue) michael@0: { michael@0: PLArenaPool *arena = NULL; michael@0: SFTKDBEncryptedDataInfo edi; michael@0: SECStatus rv; michael@0: michael@0: arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); michael@0: if (arena == NULL) { michael@0: return SECFailure; michael@0: } michael@0: cipherValue->arena = NULL; michael@0: cipherValue->param = NULL; michael@0: michael@0: rv = SEC_QuickDERDecodeItem(arena, &edi, sftkdb_EncryptedDataInfoTemplate, michael@0: cipherText); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: cipherValue->alg = SECOID_GetAlgorithmTag(&edi.algorithm); michael@0: cipherValue->param = nsspkcs5_AlgidToParam(&edi.algorithm); michael@0: if (cipherValue->param == NULL) { michael@0: goto loser; michael@0: } michael@0: cipherValue->value = edi.encryptedData; michael@0: cipherValue->arena = arena; michael@0: michael@0: return SECSuccess; michael@0: loser: michael@0: if (cipherValue->param) { michael@0: nsspkcs5_DestroyPBEParameter(cipherValue->param); michael@0: cipherValue->param = NULL; michael@0: } michael@0: if (arena) { michael@0: PORT_FreeArena(arena,PR_FALSE); michael@0: } michael@0: return SECFailure; michael@0: } michael@0: michael@0: michael@0: michael@0: /* michael@0: * unlike decode, Encode actually allocates a SECItem the caller must free michael@0: * The caller can pass an optional arena to to indicate where to place michael@0: * the resultant cipherText. michael@0: */ michael@0: static SECStatus michael@0: sftkdb_encodeCipherText(PLArenaPool *arena, sftkCipherValue *cipherValue, michael@0: SECItem **cipherText) michael@0: { michael@0: SFTKDBEncryptedDataInfo edi; michael@0: SECAlgorithmID *algid; michael@0: SECStatus rv; michael@0: PLArenaPool *localArena = NULL; michael@0: michael@0: michael@0: localArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE); michael@0: if (localArena == NULL) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: algid = nsspkcs5_CreateAlgorithmID(localArena, cipherValue->alg, michael@0: cipherValue->param); michael@0: if (algid == NULL) { michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: rv = SECOID_CopyAlgorithmID(localArena, &edi.algorithm, algid); michael@0: SECOID_DestroyAlgorithmID(algid, PR_TRUE); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: edi.encryptedData = cipherValue->value; michael@0: michael@0: *cipherText = SEC_ASN1EncodeItem(arena, NULL, &edi, michael@0: sftkdb_EncryptedDataInfoTemplate); michael@0: if (*cipherText == NULL) { michael@0: rv = SECFailure; michael@0: } michael@0: michael@0: loser: michael@0: if (localArena) { michael@0: PORT_FreeArena(localArena,PR_FALSE); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * Use our key to decode a cipherText block from the database. michael@0: * michael@0: * plain text is allocated by nsspkcs5_CipherData and must be freed michael@0: * with SECITEM_FreeItem by the caller. michael@0: */ michael@0: SECStatus michael@0: sftkdb_DecryptAttribute(SECItem *passKey, SECItem *cipherText, SECItem **plain) michael@0: { michael@0: SECStatus rv; michael@0: sftkCipherValue cipherValue; michael@0: michael@0: /* First get the cipher type */ michael@0: rv = sftkdb_decodeCipherText(cipherText, &cipherValue); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: *plain = nsspkcs5_CipherData(cipherValue.param, passKey, &cipherValue.value, michael@0: PR_FALSE, NULL); michael@0: if (*plain == NULL) { michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: michael@0: loser: michael@0: if (cipherValue.param) { michael@0: nsspkcs5_DestroyPBEParameter(cipherValue.param); michael@0: } michael@0: if (cipherValue.arena) { michael@0: PORT_FreeArena(cipherValue.arena,PR_FALSE); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * encrypt a block. This function returned the encrypted ciphertext which michael@0: * the caller must free. If the caller provides an arena, cipherText will michael@0: * be allocated out of that arena. This also generated the per entry michael@0: * salt automatically. michael@0: */ michael@0: SECStatus michael@0: sftkdb_EncryptAttribute(PLArenaPool *arena, SECItem *passKey, michael@0: SECItem *plainText, SECItem **cipherText) michael@0: { michael@0: SECStatus rv; michael@0: sftkCipherValue cipherValue; michael@0: SECItem *cipher = NULL; michael@0: NSSPKCS5PBEParameter *param = NULL; michael@0: unsigned char saltData[HASH_LENGTH_MAX]; michael@0: michael@0: cipherValue.alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC; michael@0: cipherValue.salt.len = SHA1_LENGTH; michael@0: cipherValue.salt.data = saltData; michael@0: RNG_GenerateGlobalRandomBytes(saltData,cipherValue.salt.len); michael@0: michael@0: param = nsspkcs5_NewParam(cipherValue.alg, &cipherValue.salt, 1); michael@0: if (param == NULL) { michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: cipher = nsspkcs5_CipherData(param, passKey, plainText, PR_TRUE, NULL); michael@0: if (cipher == NULL) { michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: cipherValue.value = *cipher; michael@0: cipherValue.param = param; michael@0: michael@0: rv = sftkdb_encodeCipherText(arena, &cipherValue, cipherText); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: loser: michael@0: if (cipher) { michael@0: SECITEM_FreeItem(cipher, PR_TRUE); michael@0: } michael@0: if (param) { michael@0: nsspkcs5_DestroyPBEParameter(param); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * use the password and the pbe parameters to generate an HMAC for the michael@0: * given plain text data. This is used by sftkdb_VerifyAttribute and michael@0: * sftkdb_SignAttribute. Signature is returned in signData. The caller michael@0: * must preallocate the space in the secitem. michael@0: */ michael@0: static SECStatus michael@0: sftkdb_pbehash(SECOidTag sigOid, SECItem *passKey, michael@0: NSSPKCS5PBEParameter *param, michael@0: CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE attrType, michael@0: SECItem *plainText, SECItem *signData) michael@0: { michael@0: SECStatus rv = SECFailure; michael@0: SECItem *key = NULL; michael@0: HMACContext *hashCx = NULL; michael@0: HASH_HashType hashType = HASH_AlgNULL; michael@0: const SECHashObject *hashObj; michael@0: unsigned char addressData[SDB_ULONG_SIZE]; michael@0: michael@0: hashType = HASH_FromHMACOid(param->encAlg); michael@0: if (hashType == HASH_AlgNULL) { michael@0: PORT_SetError(SEC_ERROR_INVALID_ALGORITHM); michael@0: return SECFailure; michael@0: } michael@0: michael@0: hashObj = HASH_GetRawHashObject(hashType); michael@0: if (hashObj == NULL) { michael@0: goto loser; michael@0: } michael@0: michael@0: key = nsspkcs5_ComputeKeyAndIV(param, passKey, NULL, PR_FALSE); michael@0: if (!key) { michael@0: goto loser; michael@0: } michael@0: michael@0: hashCx = HMAC_Create(hashObj, key->data, key->len, PR_TRUE); michael@0: if (!hashCx) { michael@0: goto loser; michael@0: } michael@0: HMAC_Begin(hashCx); michael@0: /* Tie this value to a particular object. This is most important for michael@0: * the trust attributes, where and attacker could copy a value for michael@0: * 'validCA' from another cert in the database */ michael@0: sftk_ULong2SDBULong(addressData, objectID); michael@0: HMAC_Update(hashCx, addressData, SDB_ULONG_SIZE); michael@0: sftk_ULong2SDBULong(addressData, attrType); michael@0: HMAC_Update(hashCx, addressData, SDB_ULONG_SIZE); michael@0: michael@0: HMAC_Update(hashCx, plainText->data, plainText->len); michael@0: rv = HMAC_Finish(hashCx, signData->data, &signData->len, signData->len); michael@0: michael@0: loser: michael@0: if (hashCx) { michael@0: HMAC_Destroy(hashCx, PR_TRUE); michael@0: } michael@0: if (key) { michael@0: SECITEM_FreeItem(key,PR_TRUE); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * Use our key to verify a signText block from the database matches michael@0: * the plainText from the database. The signText is a PKCS 5 v2 pbe. michael@0: * plainText is the plainText of the attribute. michael@0: */ michael@0: SECStatus michael@0: sftkdb_VerifyAttribute(SECItem *passKey, CK_OBJECT_HANDLE objectID, michael@0: CK_ATTRIBUTE_TYPE attrType, michael@0: SECItem *plainText, SECItem *signText) michael@0: { michael@0: SECStatus rv; michael@0: sftkCipherValue signValue; michael@0: SECItem signature; michael@0: unsigned char signData[HASH_LENGTH_MAX]; michael@0: michael@0: michael@0: /* First get the cipher type */ michael@0: rv = sftkdb_decodeCipherText(signText, &signValue); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: signature.data = signData; michael@0: signature.len = sizeof(signData); michael@0: michael@0: rv = sftkdb_pbehash(signValue.alg, passKey, signValue.param, michael@0: objectID, attrType, plainText, &signature); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: if (SECITEM_CompareItem(&signValue.value,&signature) != 0) { michael@0: PORT_SetError(SEC_ERROR_BAD_SIGNATURE); michael@0: rv = SECFailure; michael@0: } michael@0: michael@0: loser: michael@0: if (signValue.param) { michael@0: nsspkcs5_DestroyPBEParameter(signValue.param); michael@0: } michael@0: if (signValue.arena) { michael@0: PORT_FreeArena(signValue.arena,PR_FALSE); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * Use our key to create a signText block the plain text of an michael@0: * attribute. The signText is a PKCS 5 v2 pbe. michael@0: */ michael@0: SECStatus michael@0: sftkdb_SignAttribute(PLArenaPool *arena, SECItem *passKey, michael@0: CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE attrType, michael@0: SECItem *plainText, SECItem **signature) michael@0: { michael@0: SECStatus rv; michael@0: sftkCipherValue signValue; michael@0: NSSPKCS5PBEParameter *param = NULL; michael@0: unsigned char saltData[HASH_LENGTH_MAX]; michael@0: unsigned char signData[HASH_LENGTH_MAX]; michael@0: SECOidTag hmacAlg = SEC_OID_HMAC_SHA256; /* hash for authentication */ michael@0: SECOidTag prfAlg = SEC_OID_HMAC_SHA256; /* hash for pb key generation */ michael@0: HASH_HashType prfType; michael@0: unsigned int hmacLength; michael@0: unsigned int prfLength; michael@0: michael@0: /* this code allows us to fetch the lengths and hashes on the fly michael@0: * by simply changing the OID above */ michael@0: prfType = HASH_FromHMACOid(prfAlg); michael@0: PORT_Assert(prfType != HASH_AlgNULL); michael@0: prfLength = HASH_GetRawHashObject(prfType)->length; michael@0: PORT_Assert(prfLength <= HASH_LENGTH_MAX); michael@0: michael@0: hmacLength = HASH_GetRawHashObject(HASH_FromHMACOid(hmacAlg))->length; michael@0: PORT_Assert(hmacLength <= HASH_LENGTH_MAX); michael@0: michael@0: /* initialize our CipherValue structure */ michael@0: signValue.alg = SEC_OID_PKCS5_PBMAC1; michael@0: signValue.salt.len = prfLength; michael@0: signValue.salt.data = saltData; michael@0: signValue.value.data = signData; michael@0: signValue.value.len = hmacLength; michael@0: RNG_GenerateGlobalRandomBytes(saltData,prfLength); michael@0: michael@0: /* initialize our pkcs5 parameter */ michael@0: param = nsspkcs5_NewParam(signValue.alg, &signValue.salt, 1); michael@0: if (param == NULL) { michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: param->keyID = pbeBitGenIntegrityKey; michael@0: /* set the PKCS 5 v2 parameters, not extractable from the michael@0: * data passed into nsspkcs5_NewParam */ michael@0: param->encAlg = hmacAlg; michael@0: param->hashType = prfType; michael@0: param->keyLen = hmacLength; michael@0: rv = SECOID_SetAlgorithmID(param->poolp, ¶m->prfAlg, prfAlg, NULL); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: michael@0: /* calculate the mac */ michael@0: rv = sftkdb_pbehash(signValue.alg, passKey, param, objectID, attrType, michael@0: plainText, &signValue.value); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: signValue.param = param; michael@0: michael@0: /* write it out */ michael@0: rv = sftkdb_encodeCipherText(arena, &signValue, signature); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: loser: michael@0: if (param) { michael@0: nsspkcs5_DestroyPBEParameter(param); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * safely swith the passed in key for the one caches in the keydb handle michael@0: * michael@0: * A key attached to the handle tells us the the token is logged in. michael@0: * We can used the key attached to the handle in sftkdb_EncryptAttribute michael@0: * and sftkdb_DecryptAttribute calls. michael@0: */ michael@0: static void michael@0: sftkdb_switchKeys(SFTKDBHandle *keydb, SECItem *passKey) michael@0: { michael@0: unsigned char *data; michael@0: int len; michael@0: michael@0: if (keydb->passwordLock == NULL) { michael@0: PORT_Assert(keydb->type != SFTK_KEYDB_TYPE); michael@0: return; michael@0: } michael@0: michael@0: /* an atomic pointer set would be nice */ michael@0: SKIP_AFTER_FORK(PZ_Lock(keydb->passwordLock)); michael@0: data = keydb->passwordKey.data; michael@0: len = keydb->passwordKey.len; michael@0: keydb->passwordKey.data = passKey->data; michael@0: keydb->passwordKey.len = passKey->len; michael@0: passKey->data = data; michael@0: passKey->len = len; michael@0: SKIP_AFTER_FORK(PZ_Unlock(keydb->passwordLock)); michael@0: } michael@0: michael@0: /* michael@0: * returns true if we are in a middle of a merge style update. michael@0: */ michael@0: PRBool michael@0: sftkdb_InUpdateMerge(SFTKDBHandle *keydb) michael@0: { michael@0: return keydb->updateID ? PR_TRUE : PR_FALSE; michael@0: } michael@0: michael@0: /* michael@0: * returns true if we are looking for the password for the user's old source michael@0: * database as part of a merge style update. michael@0: */ michael@0: PRBool michael@0: sftkdb_NeedUpdateDBPassword(SFTKDBHandle *keydb) michael@0: { michael@0: if (!sftkdb_InUpdateMerge(keydb)) { michael@0: return PR_FALSE; michael@0: } michael@0: if (keydb->updateDBIsInit && !keydb->updatePasswordKey) { michael@0: return PR_TRUE; michael@0: } michael@0: return PR_FALSE; michael@0: } michael@0: michael@0: /* michael@0: * fetch an update password key from a handle. michael@0: */ michael@0: SECItem * michael@0: sftkdb_GetUpdatePasswordKey(SFTKDBHandle *handle) michael@0: { michael@0: SECItem *key = NULL; michael@0: michael@0: /* if we're a cert db, fetch it from our peer key db */ michael@0: if (handle->type == SFTK_CERTDB_TYPE) { michael@0: handle = handle->peerDB; michael@0: } michael@0: michael@0: /* don't have one */ michael@0: if (!handle) { michael@0: return NULL; michael@0: } michael@0: michael@0: PZ_Lock(handle->passwordLock); michael@0: if (handle->updatePasswordKey) { michael@0: key = SECITEM_DupItem(handle->updatePasswordKey); michael@0: } michael@0: PZ_Unlock(handle->passwordLock); michael@0: michael@0: return key; michael@0: } michael@0: michael@0: /* michael@0: * free the update password key from a handle. michael@0: */ michael@0: void michael@0: sftkdb_FreeUpdatePasswordKey(SFTKDBHandle *handle) michael@0: { michael@0: SECItem *key = NULL; michael@0: michael@0: /* don't have one */ michael@0: if (!handle) { michael@0: return; michael@0: } michael@0: michael@0: /* if we're a cert db, we don't have one */ michael@0: if (handle->type == SFTK_CERTDB_TYPE) { michael@0: return; michael@0: } michael@0: michael@0: PZ_Lock(handle->passwordLock); michael@0: if (handle->updatePasswordKey) { michael@0: key = handle->updatePasswordKey; michael@0: handle->updatePasswordKey = NULL; michael@0: } michael@0: PZ_Unlock(handle->passwordLock); michael@0: michael@0: if (key) { michael@0: SECITEM_ZfreeItem(key, PR_TRUE); michael@0: } michael@0: michael@0: return; michael@0: } michael@0: michael@0: /* michael@0: * what password db we use depends heavily on the update state machine michael@0: * michael@0: * 1) no update db, return the normal database. michael@0: * 2) update db and no merge return the update db. michael@0: * 3) update db and in merge: michael@0: * return the update db if we need the update db's password, michael@0: * otherwise return our normal datbase. michael@0: */ michael@0: static SDB * michael@0: sftk_getPWSDB(SFTKDBHandle *keydb) michael@0: { michael@0: if (!keydb->update) { michael@0: return keydb->db; michael@0: } michael@0: if (!sftkdb_InUpdateMerge(keydb)) { michael@0: return keydb->update; michael@0: } michael@0: if (sftkdb_NeedUpdateDBPassword(keydb)) { michael@0: return keydb->update; michael@0: } michael@0: return keydb->db; michael@0: } michael@0: michael@0: /* michael@0: * return success if we have a valid password entry. michael@0: * This is will show up outside of PKCS #11 as CKF_USER_PIN_INIT michael@0: * in the token flags. michael@0: */ michael@0: SECStatus michael@0: sftkdb_HasPasswordSet(SFTKDBHandle *keydb) michael@0: { michael@0: SECItem salt, value; michael@0: unsigned char saltData[SDB_MAX_META_DATA_LEN]; michael@0: unsigned char valueData[SDB_MAX_META_DATA_LEN]; michael@0: CK_RV crv; michael@0: SDB *db; michael@0: michael@0: if (keydb == NULL) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: db = sftk_getPWSDB(keydb); michael@0: if (db == NULL) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: salt.data = saltData; michael@0: salt.len = sizeof(saltData); michael@0: value.data = valueData; michael@0: value.len = sizeof(valueData); michael@0: crv = (*db->sdb_GetMetaData)(db, "password", &salt, &value); michael@0: michael@0: /* If no password is set, we can update right away */ michael@0: if (((keydb->db->sdb_flags & SDB_RDONLY) == 0) && keydb->update michael@0: && crv != CKR_OK) { michael@0: /* update the peer certdb if it exists */ michael@0: if (keydb->peerDB) { michael@0: sftkdb_Update(keydb->peerDB, NULL); michael@0: } michael@0: sftkdb_Update(keydb, NULL); michael@0: } michael@0: return (crv == CKR_OK) ? SECSuccess : SECFailure; michael@0: } michael@0: michael@0: #define SFTK_PW_CHECK_STRING "password-check" michael@0: #define SFTK_PW_CHECK_LEN 14 michael@0: michael@0: /* michael@0: * check if the supplied password is valid michael@0: */ michael@0: SECStatus michael@0: sftkdb_CheckPassword(SFTKDBHandle *keydb, const char *pw, PRBool *tokenRemoved) michael@0: { michael@0: SECStatus rv; michael@0: SECItem salt, value; michael@0: unsigned char saltData[SDB_MAX_META_DATA_LEN]; michael@0: unsigned char valueData[SDB_MAX_META_DATA_LEN]; michael@0: SECItem key; michael@0: SECItem *result = NULL; michael@0: SDB *db; michael@0: CK_RV crv; michael@0: michael@0: if (keydb == NULL) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: db = sftk_getPWSDB(keydb); michael@0: if (db == NULL) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: key.data = NULL; michael@0: key.len = 0; michael@0: michael@0: if (pw == NULL) pw=""; michael@0: michael@0: /* get the entry from the database */ michael@0: salt.data = saltData; michael@0: salt.len = sizeof(saltData); michael@0: value.data = valueData; michael@0: value.len = sizeof(valueData); michael@0: crv = (*db->sdb_GetMetaData)(db, "password", &salt, &value); michael@0: if (crv != CKR_OK) { michael@0: rv = SECFailure; michael@0: goto done; michael@0: } michael@0: michael@0: /* get our intermediate key based on the entry salt value */ michael@0: rv = sftkdb_passwordToKey(keydb, &salt, pw, &key); michael@0: if (rv != SECSuccess) { michael@0: goto done; michael@0: } michael@0: michael@0: /* decrypt the entry value */ michael@0: rv = sftkdb_DecryptAttribute(&key, &value, &result); michael@0: if (rv != SECSuccess) { michael@0: goto done; michael@0: } michael@0: michael@0: /* if it's what we expect, update our key in the database handle and michael@0: * return Success */ michael@0: if ((result->len == SFTK_PW_CHECK_LEN) && michael@0: PORT_Memcmp(result->data, SFTK_PW_CHECK_STRING, SFTK_PW_CHECK_LEN) == 0){ michael@0: /* michael@0: * We have a password, now lets handle any potential update cases.. michael@0: * michael@0: * First, the normal case: no update. In this case we only need the michael@0: * the password for our only DB, which we now have, we switch michael@0: * the keys and fall through. michael@0: * Second regular (non-merge) update: The target DB does not yet have michael@0: * a password initialized, we now have the password for the source DB, michael@0: * so we can switch the keys and simply update the target database. michael@0: * Merge update case: This one is trickier. michael@0: * 1) If we need the source DB password, then we just got it here. michael@0: * We need to save that password, michael@0: * then we need to check to see if we need or have the target michael@0: * database password. michael@0: * If we have it (it's the same as the source), or don't need michael@0: * it (it's not set or is ""), we can start the update now. michael@0: * If we don't have it, we need the application to get it from michael@0: * the user. Clear our sessions out to simulate a token michael@0: * removal. C_GetTokenInfo will change the token description michael@0: * and the token will still appear to be logged out. michael@0: * 2) If we already have the source DB password, this password is michael@0: * for the target database. We can now move forward with the michael@0: * update, as we now have both required passwords. michael@0: * michael@0: */ michael@0: PZ_Lock(keydb->passwordLock); michael@0: if (sftkdb_NeedUpdateDBPassword(keydb)) { michael@0: /* Squirrel this special key away. michael@0: * This has the side effect of turning sftkdb_NeedLegacyPW off, michael@0: * as well as changing which database is returned from michael@0: * SFTK_GET_PW_DB (thus effecting both sftkdb_CheckPassword() michael@0: * and sftkdb_HasPasswordSet()) */ michael@0: keydb->updatePasswordKey = SECITEM_DupItem(&key); michael@0: PZ_Unlock(keydb->passwordLock); michael@0: if (keydb->updatePasswordKey == NULL) { michael@0: /* PORT_Error set by SECITEM_DupItem */ michael@0: rv = SECFailure; michael@0: goto done; michael@0: } michael@0: michael@0: /* Simulate a token removal -- we need to do this any michael@0: * any case at this point so the token name is correct. */ michael@0: *tokenRemoved = PR_TRUE; michael@0: michael@0: /* michael@0: * OK, we got the update DB password, see if we need a password michael@0: * for the target... michael@0: */ michael@0: if (sftkdb_HasPasswordSet(keydb) == SECSuccess) { michael@0: /* We have a password, do we know what the password is? michael@0: * check 1) for the password the user supplied for the michael@0: * update DB, michael@0: * and 2) for the null password. michael@0: * michael@0: * RECURSION NOTE: we are calling ourselves here. This means michael@0: * any updates, switchKeys, etc will have been completed michael@0: * if these functions return successfully, in those cases michael@0: * just exit returning Success. We don't recurse infinitely michael@0: * because we are making this call from a NeedUpdateDBPassword michael@0: * block and we've already set that update password at this michael@0: * point. */ michael@0: rv = sftkdb_CheckPassword(keydb, pw, tokenRemoved); michael@0: if (rv == SECSuccess) { michael@0: /* source and target databases have the same password, we michael@0: * are good to go */ michael@0: goto done; michael@0: } michael@0: sftkdb_CheckPassword(keydb, "", tokenRemoved); michael@0: michael@0: /* michael@0: * Important 'NULL' code here. At this point either we michael@0: * succeeded in logging in with "" or we didn't. michael@0: * michael@0: * If we did succeed at login, our machine state will be set michael@0: * to logged in appropriately. The application will find that michael@0: * it's logged in as soon as it opens a new session. We have michael@0: * also completed the update. Life is good. michael@0: * michael@0: * If we did not succeed, well the user still successfully michael@0: * logged into the update database, since we faked the token michael@0: * removal it's just like the user logged into his smart card michael@0: * then removed it. the actual login work, so we report that michael@0: * success back to the user, but we won't actually be michael@0: * logged in. The application will find this out when it michael@0: * checks it's login state, thus triggering another password michael@0: * prompt so we can get the real target DB password. michael@0: * michael@0: * summary, we exit from here with SECSuccess no matter what. michael@0: */ michael@0: rv = SECSuccess; michael@0: goto done; michael@0: } else { michael@0: /* there is no password, just fall through to update. michael@0: * update will write the source DB's password record michael@0: * into the target DB just like it would in a non-merge michael@0: * update case. */ michael@0: } michael@0: } else { michael@0: PZ_Unlock(keydb->passwordLock); michael@0: } michael@0: /* load the keys, so the keydb can parse it's key set */ michael@0: sftkdb_switchKeys(keydb, &key); michael@0: michael@0: /* we need to update, do it now */ michael@0: if (((keydb->db->sdb_flags & SDB_RDONLY) == 0) && keydb->update) { michael@0: /* update the peer certdb if it exists */ michael@0: if (keydb->peerDB) { michael@0: sftkdb_Update(keydb->peerDB, &key); michael@0: } michael@0: sftkdb_Update(keydb, &key); michael@0: } michael@0: } else { michael@0: rv = SECFailure; michael@0: /*PORT_SetError( bad password); */ michael@0: } michael@0: michael@0: done: michael@0: if (key.data) { michael@0: PORT_ZFree(key.data,key.len); michael@0: } michael@0: if (result) { michael@0: SECITEM_FreeItem(result,PR_TRUE); michael@0: } michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * return Success if the there is a cached password key. michael@0: */ michael@0: SECStatus michael@0: sftkdb_PWCached(SFTKDBHandle *keydb) michael@0: { michael@0: return keydb->passwordKey.data ? SECSuccess : SECFailure; michael@0: } michael@0: michael@0: michael@0: static CK_RV michael@0: sftk_updateMacs(PLArenaPool *arena, SFTKDBHandle *handle, michael@0: CK_OBJECT_HANDLE id, SECItem *newKey) michael@0: { michael@0: CK_RV crv = CKR_OK; michael@0: CK_RV crv2; michael@0: CK_ATTRIBUTE authAttrs[] = { michael@0: {CKA_MODULUS, NULL, 0}, michael@0: {CKA_PUBLIC_EXPONENT, NULL, 0}, michael@0: {CKA_CERT_SHA1_HASH, NULL, 0}, michael@0: {CKA_CERT_MD5_HASH, NULL, 0}, michael@0: {CKA_TRUST_SERVER_AUTH, NULL, 0}, michael@0: {CKA_TRUST_CLIENT_AUTH, NULL, 0}, michael@0: {CKA_TRUST_EMAIL_PROTECTION, NULL, 0}, michael@0: {CKA_TRUST_CODE_SIGNING, NULL, 0}, michael@0: {CKA_TRUST_STEP_UP_APPROVED, NULL, 0}, michael@0: {CKA_NSS_OVERRIDE_EXTENSIONS, NULL, 0}, michael@0: }; michael@0: CK_ULONG authAttrCount = sizeof(authAttrs)/sizeof(CK_ATTRIBUTE); michael@0: int i, count; michael@0: SFTKDBHandle *keyHandle = handle; michael@0: SDB *keyTarget = NULL; michael@0: michael@0: id &= SFTK_OBJ_ID_MASK; michael@0: michael@0: if (handle->type != SFTK_KEYDB_TYPE) { michael@0: keyHandle = handle->peerDB; michael@0: } michael@0: michael@0: if (keyHandle == NULL) { michael@0: return CKR_OK; michael@0: } michael@0: michael@0: /* old DB's don't have meta data, finished with MACs */ michael@0: keyTarget = SFTK_GET_SDB(keyHandle); michael@0: if ((keyTarget->sdb_flags &SDB_HAS_META) == 0) { michael@0: return CKR_OK; michael@0: } michael@0: michael@0: /* michael@0: * STEP 1: find the MACed attributes of this object michael@0: */ michael@0: crv2 = sftkdb_GetAttributeValue(handle, id, authAttrs, authAttrCount); michael@0: count = 0; michael@0: /* allocate space for the attributes */ michael@0: for (i=0; i < authAttrCount; i++) { michael@0: if ((authAttrs[i].ulValueLen == -1) || (authAttrs[i].ulValueLen == 0)){ michael@0: continue; michael@0: } michael@0: count++; michael@0: authAttrs[i].pValue = PORT_ArenaAlloc(arena,authAttrs[i].ulValueLen); michael@0: if (authAttrs[i].pValue == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: /* if count was zero, none were found, finished with MACs */ michael@0: if (count == 0) { michael@0: return CKR_OK; michael@0: } michael@0: michael@0: crv = sftkdb_GetAttributeValue(handle, id, authAttrs, authAttrCount); michael@0: /* ignore error code, we expect some possible errors */ michael@0: michael@0: /* GetAttributeValue just verified the old macs, safe to write michael@0: * them out then... */ michael@0: for (i=0; i < authAttrCount; i++) { michael@0: SECItem *signText; michael@0: SECItem plainText; michael@0: SECStatus rv; michael@0: michael@0: if ((authAttrs[i].ulValueLen == -1) || (authAttrs[i].ulValueLen == 0)){ michael@0: continue; michael@0: } michael@0: michael@0: plainText.data = authAttrs[i].pValue; michael@0: plainText.len = authAttrs[i].ulValueLen; michael@0: rv = sftkdb_SignAttribute(arena, newKey, id, michael@0: authAttrs[i].type, &plainText, &signText); michael@0: if (rv != SECSuccess) { michael@0: return CKR_GENERAL_ERROR; michael@0: } michael@0: rv = sftkdb_PutAttributeSignature(handle, keyTarget, id, michael@0: authAttrs[i].type, signText); michael@0: if (rv != SECSuccess) { michael@0: return CKR_GENERAL_ERROR; michael@0: } michael@0: } michael@0: michael@0: return CKR_OK; michael@0: } michael@0: michael@0: static CK_RV michael@0: sftk_updateEncrypted(PLArenaPool *arena, SFTKDBHandle *keydb, michael@0: CK_OBJECT_HANDLE id, SECItem *newKey) michael@0: { michael@0: CK_RV crv = CKR_OK; michael@0: CK_RV crv2; michael@0: CK_ATTRIBUTE *first, *last; michael@0: CK_ATTRIBUTE privAttrs[] = { michael@0: {CKA_VALUE, NULL, 0}, michael@0: {CKA_PRIVATE_EXPONENT, NULL, 0}, michael@0: {CKA_PRIME_1, NULL, 0}, michael@0: {CKA_PRIME_2, NULL, 0}, michael@0: {CKA_EXPONENT_1, NULL, 0}, michael@0: {CKA_EXPONENT_2, NULL, 0}, michael@0: {CKA_COEFFICIENT, NULL, 0} }; michael@0: CK_ULONG privAttrCount = sizeof(privAttrs)/sizeof(CK_ATTRIBUTE); michael@0: int i, count; michael@0: michael@0: /* michael@0: * STEP 1. Read the old attributes in the clear. michael@0: */ michael@0: michael@0: /* Get the attribute sizes. michael@0: * ignore the error code, we will have unknown attributes here */ michael@0: crv2 = sftkdb_GetAttributeValue(keydb, id, privAttrs, privAttrCount); michael@0: michael@0: /* michael@0: * find the valid block of attributes and fill allocate space for michael@0: * their data */ michael@0: first = last = NULL; michael@0: for (i=0; i < privAttrCount; i++) { michael@0: /* find the block of attributes that are appropriate for this michael@0: * objects. There should only be once contiguous block, if not michael@0: * there's an error. michael@0: * michael@0: * find the first and last good entry. michael@0: */ michael@0: if ((privAttrs[i].ulValueLen == -1) || (privAttrs[i].ulValueLen == 0)){ michael@0: if (!first) continue; michael@0: if (!last) { michael@0: /* previous entry was last good entry */ michael@0: last= &privAttrs[i-1]; michael@0: } michael@0: continue; michael@0: } michael@0: if (!first) { michael@0: first = &privAttrs[i]; michael@0: } michael@0: if (last) { michael@0: /* OOPS, we've found another good entry beyond the end of the michael@0: * last good entry, we need to fail here. */ michael@0: crv = CKR_GENERAL_ERROR; michael@0: break; michael@0: } michael@0: privAttrs[i].pValue = PORT_ArenaAlloc(arena,privAttrs[i].ulValueLen); michael@0: if (privAttrs[i].pValue == NULL) { michael@0: crv = CKR_HOST_MEMORY; michael@0: break; michael@0: } michael@0: } michael@0: if (first == NULL) { michael@0: /* no valid entries found, return error based on crv2 */ michael@0: return crv2; michael@0: } michael@0: if (last == NULL) { michael@0: last = &privAttrs[privAttrCount-1]; michael@0: } michael@0: if (crv != CKR_OK) { michael@0: return crv; michael@0: } michael@0: /* read the attributes */ michael@0: count = (last-first)+1; michael@0: crv = sftkdb_GetAttributeValue(keydb, id, first, count); michael@0: if (crv != CKR_OK) { michael@0: return crv; michael@0: } michael@0: michael@0: /* michael@0: * STEP 2: read the encrypt the attributes with the new key. michael@0: */ michael@0: for (i=0; i < count; i++) { michael@0: SECItem plainText; michael@0: SECItem *result; michael@0: SECStatus rv; michael@0: michael@0: plainText.data = first[i].pValue; michael@0: plainText.len = first[i].ulValueLen; michael@0: rv = sftkdb_EncryptAttribute(arena, newKey, &plainText, &result); michael@0: if (rv != SECSuccess) { michael@0: return CKR_GENERAL_ERROR; michael@0: } michael@0: first[i].pValue = result->data; michael@0: first[i].ulValueLen = result->len; michael@0: /* clear our sensitive data out */ michael@0: PORT_Memset(plainText.data, 0, plainText.len); michael@0: } michael@0: michael@0: michael@0: /* michael@0: * STEP 3: write the newly encrypted attributes out directly michael@0: */ michael@0: id &= SFTK_OBJ_ID_MASK; michael@0: keydb->newKey = newKey; michael@0: crv = (*keydb->db->sdb_SetAttributeValue)(keydb->db, id, first, count); michael@0: keydb->newKey = NULL; michael@0: michael@0: return crv; michael@0: } michael@0: michael@0: static CK_RV michael@0: sftk_convertAttributes(SFTKDBHandle *handle, michael@0: CK_OBJECT_HANDLE id, SECItem *newKey) michael@0: { michael@0: CK_RV crv = CKR_OK; michael@0: PLArenaPool *arena = NULL; michael@0: michael@0: /* get a new arena to simplify cleanup */ michael@0: arena = PORT_NewArena(1024); michael@0: if (!arena) { michael@0: return CKR_HOST_MEMORY; michael@0: } michael@0: michael@0: /* michael@0: * first handle the MACS michael@0: */ michael@0: crv = sftk_updateMacs(arena, handle, id, newKey); michael@0: if (crv != CKR_OK) { michael@0: goto loser; michael@0: } michael@0: michael@0: if (handle->type == SFTK_KEYDB_TYPE) { michael@0: crv = sftk_updateEncrypted(arena, handle, id, newKey); michael@0: if (crv != CKR_OK) { michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: /* free up our mess */ michael@0: /* NOTE: at this point we know we've cleared out any unencrypted data */ michael@0: PORT_FreeArena(arena, PR_FALSE); michael@0: return CKR_OK; michael@0: michael@0: loser: michael@0: /* there may be unencrypted data, clear it out down */ michael@0: PORT_FreeArena(arena, PR_TRUE); michael@0: return crv; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * must be called with the old key active. michael@0: */ michael@0: CK_RV michael@0: sftkdb_convertObjects(SFTKDBHandle *handle, CK_ATTRIBUTE *template, michael@0: CK_ULONG count, SECItem *newKey) michael@0: { michael@0: SDBFind *find = NULL; michael@0: CK_ULONG idCount = SFTK_MAX_IDS; michael@0: CK_OBJECT_HANDLE ids[SFTK_MAX_IDS]; michael@0: CK_RV crv, crv2; michael@0: int i; michael@0: michael@0: crv = sftkdb_FindObjectsInit(handle, template, count, &find); michael@0: michael@0: if (crv != CKR_OK) { michael@0: return crv; michael@0: } michael@0: while ((crv == CKR_OK) && (idCount == SFTK_MAX_IDS)) { michael@0: crv = sftkdb_FindObjects(handle, find, ids, SFTK_MAX_IDS, &idCount); michael@0: for (i=0; (crv == CKR_OK) && (i < idCount); i++) { michael@0: crv = sftk_convertAttributes(handle, ids[i], newKey); michael@0: } michael@0: } michael@0: crv2 = sftkdb_FindObjectsFinal(handle, find); michael@0: if (crv == CKR_OK) crv = crv2; michael@0: michael@0: return crv; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * change the database password. michael@0: */ michael@0: SECStatus michael@0: sftkdb_ChangePassword(SFTKDBHandle *keydb, michael@0: char *oldPin, char *newPin, PRBool *tokenRemoved) michael@0: { michael@0: SECStatus rv = SECSuccess; michael@0: SECItem plainText; michael@0: SECItem newKey; michael@0: SECItem *result = NULL; michael@0: SECItem salt, value; michael@0: SFTKDBHandle *certdb; michael@0: unsigned char saltData[SDB_MAX_META_DATA_LEN]; michael@0: unsigned char valueData[SDB_MAX_META_DATA_LEN]; michael@0: CK_RV crv; michael@0: SDB *db; michael@0: michael@0: if (keydb == NULL) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: db = SFTK_GET_SDB(keydb); michael@0: if (db == NULL) { michael@0: return SECFailure; michael@0: } michael@0: michael@0: newKey.data = NULL; michael@0: michael@0: /* make sure we have a valid old pin */ michael@0: crv = (*keydb->db->sdb_Begin)(keydb->db); michael@0: if (crv != CKR_OK) { michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: salt.data = saltData; michael@0: salt.len = sizeof(saltData); michael@0: value.data = valueData; michael@0: value.len = sizeof(valueData); michael@0: crv = (*db->sdb_GetMetaData)(db, "password", &salt, &value); michael@0: if (crv == CKR_OK) { michael@0: rv = sftkdb_CheckPassword(keydb, oldPin, tokenRemoved); michael@0: if (rv == SECFailure) { michael@0: goto loser; michael@0: } michael@0: } else { michael@0: salt.len = SHA1_LENGTH; michael@0: RNG_GenerateGlobalRandomBytes(salt.data,salt.len); michael@0: } michael@0: michael@0: rv = sftkdb_passwordToKey(keydb, &salt, newPin, &newKey); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: michael@0: michael@0: /* michael@0: * convert encrypted entries here. michael@0: */ michael@0: crv = sftkdb_convertObjects(keydb, NULL, 0, &newKey); michael@0: if (crv != CKR_OK) { michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: /* fix up certdb macs */ michael@0: certdb = keydb->peerDB; michael@0: if (certdb) { michael@0: CK_ATTRIBUTE objectType = { CKA_CLASS, 0, sizeof(CK_OBJECT_CLASS) }; michael@0: CK_OBJECT_CLASS myClass = CKO_NETSCAPE_TRUST; michael@0: michael@0: objectType.pValue = &myClass; michael@0: crv = sftkdb_convertObjects(certdb, &objectType, 1, &newKey); michael@0: if (crv != CKR_OK) { michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: myClass = CKO_PUBLIC_KEY; michael@0: crv = sftkdb_convertObjects(certdb, &objectType, 1, &newKey); michael@0: if (crv != CKR_OK) { michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: } michael@0: michael@0: michael@0: plainText.data = (unsigned char *)SFTK_PW_CHECK_STRING; michael@0: plainText.len = SFTK_PW_CHECK_LEN; michael@0: michael@0: rv = sftkdb_EncryptAttribute(NULL, &newKey, &plainText, &result); michael@0: if (rv != SECSuccess) { michael@0: goto loser; michael@0: } michael@0: value.data = result->data; michael@0: value.len = result->len; michael@0: crv = (*keydb->db->sdb_PutMetaData)(keydb->db, "password", &salt, &value); michael@0: if (crv != CKR_OK) { michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: crv = (*keydb->db->sdb_Commit)(keydb->db); michael@0: if (crv != CKR_OK) { michael@0: rv = SECFailure; michael@0: goto loser; michael@0: } michael@0: michael@0: keydb->newKey = NULL; michael@0: michael@0: sftkdb_switchKeys(keydb, &newKey); michael@0: michael@0: loser: michael@0: if (newKey.data) { michael@0: PORT_ZFree(newKey.data,newKey.len); michael@0: } michael@0: if (result) { michael@0: SECITEM_FreeItem(result, PR_FALSE); michael@0: } michael@0: if (rv != SECSuccess) { michael@0: (*keydb->db->sdb_Abort)(keydb->db); michael@0: } michael@0: michael@0: return rv; michael@0: } michael@0: michael@0: /* michael@0: * lose our cached password michael@0: */ michael@0: SECStatus michael@0: sftkdb_ClearPassword(SFTKDBHandle *keydb) michael@0: { michael@0: SECItem oldKey; michael@0: oldKey.data = NULL; michael@0: oldKey.len = 0; michael@0: sftkdb_switchKeys(keydb, &oldKey); michael@0: if (oldKey.data) { michael@0: PORT_ZFree(oldKey.data, oldKey.len); michael@0: } michael@0: return SECSuccess; michael@0: } michael@0: michael@0: