security/nss/lib/softoken/sftkpwd.c

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/security/nss/lib/softoken/sftkpwd.c	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1275 @@
     1.4 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.5 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.6 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.7 +/* 
     1.8 + *  The following code handles the storage of PKCS 11 modules used by the
     1.9 + * NSS. For the rest of NSS, only one kind of database handle exists:
    1.10 + *
    1.11 + *     SFTKDBHandle
    1.12 + *
    1.13 + * There is one SFTKDBHandle for the each key database and one for each cert 
    1.14 + * database. These databases are opened as associated pairs, one pair per
    1.15 + * slot. SFTKDBHandles are reference counted objects.
    1.16 + *
    1.17 + * Each SFTKDBHandle points to a low level database handle (SDB). This handle
    1.18 + * represents the underlying physical database. These objects are not 
    1.19 + * reference counted, an are 'owned' by their respective SFTKDBHandles.
    1.20 + *
    1.21 + *  
    1.22 + */
    1.23 +#include "sftkdb.h"
    1.24 +#include "sftkdbti.h"
    1.25 +#include "pkcs11t.h"
    1.26 +#include "pkcs11i.h"
    1.27 +#include "sdb.h"
    1.28 +#include "prprf.h" 
    1.29 +#include "secasn1.h"
    1.30 +#include "pratom.h"
    1.31 +#include "blapi.h"
    1.32 +#include "secoid.h"
    1.33 +#include "lowpbe.h"
    1.34 +#include "secdert.h"
    1.35 +#include "prsystem.h"
    1.36 +#include "lgglue.h"
    1.37 +#include "secerr.h"
    1.38 +#include "softoken.h"
    1.39 +  
    1.40 +/******************************************************************
    1.41 + * 
    1.42 + * Key DB password handling functions
    1.43 + *
    1.44 + * These functions manage the key db password (set, reset, initialize, use).
    1.45 + *
    1.46 + * The key is managed on 'this side' of the database. All private data is
    1.47 + * encrypted before it is sent to the database itself. Besides PBE's, the
    1.48 + * database management code can also mix in various fixed keys so the data
    1.49 + * in the database is no longer considered 'plain text'.
    1.50 + */
    1.51 +
    1.52 +
    1.53 +/* take string password and turn it into a key. The key is dependent
    1.54 + * on a global salt entry acquired from the database. This salted
    1.55 + * value will be based to a pkcs5 pbe function before it is used
    1.56 + * in an actual encryption */
    1.57 +static SECStatus
    1.58 +sftkdb_passwordToKey(SFTKDBHandle *keydb, SECItem *salt,
    1.59 +			const char *pw, SECItem *key)
    1.60 +{
    1.61 +    SHA1Context *cx = NULL;
    1.62 +    SECStatus rv = SECFailure;
    1.63 +
    1.64 +    key->data = PORT_Alloc(SHA1_LENGTH);
    1.65 +    if (key->data == NULL) {
    1.66 +	goto loser;
    1.67 +    }
    1.68 +    key->len = SHA1_LENGTH;
    1.69 +
    1.70 +    cx = SHA1_NewContext();
    1.71 +    if ( cx == NULL) {
    1.72 +	goto loser;
    1.73 +    }
    1.74 +    SHA1_Begin(cx);
    1.75 +    if (salt  && salt->data ) {
    1.76 +	SHA1_Update(cx, salt->data, salt->len);
    1.77 +    }
    1.78 +    SHA1_Update(cx, (unsigned char *)pw, PORT_Strlen(pw));
    1.79 +    SHA1_End(cx, key->data, &key->len, key->len);
    1.80 +    rv = SECSuccess;
    1.81 +    
    1.82 +loser:
    1.83 +    if (cx) {
    1.84 +	SHA1_DestroyContext(cx, PR_TRUE);
    1.85 +    }
    1.86 +    if (rv != SECSuccess) {
    1.87 +	if (key->data != NULL) {
    1.88 +	    PORT_ZFree(key->data,key->len);
    1.89 +	}
    1.90 +	key->data = NULL;
    1.91 +    }
    1.92 +    return rv;
    1.93 +}
    1.94 +
    1.95 +/*
    1.96 + * Cipher text stored in the database contains 3 elements:
    1.97 + * 1) an identifier describing the encryption algorithm.
    1.98 + * 2) an entry specific salt value.
    1.99 + * 3) the encrypted value.
   1.100 + *
   1.101 + * The following data structure represents the encrypted data in a decoded
   1.102 + * (but still encrypted) form.
   1.103 + */
   1.104 +typedef struct sftkCipherValueStr sftkCipherValue;
   1.105 +struct sftkCipherValueStr {
   1.106 +    PLArenaPool *arena;
   1.107 +    SECOidTag  alg;
   1.108 +    NSSPKCS5PBEParameter *param;
   1.109 +    SECItem    salt;
   1.110 +    SECItem    value;
   1.111 +};
   1.112 +
   1.113 +#define SFTK_CIPHERTEXT_VERSION 3
   1.114 +
   1.115 +struct SFTKDBEncryptedDataInfoStr {
   1.116 +    SECAlgorithmID algorithm;
   1.117 +    SECItem encryptedData;
   1.118 +};
   1.119 +typedef struct SFTKDBEncryptedDataInfoStr SFTKDBEncryptedDataInfo;
   1.120 +
   1.121 +SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
   1.122 +
   1.123 +const SEC_ASN1Template sftkdb_EncryptedDataInfoTemplate[] = {
   1.124 +    { SEC_ASN1_SEQUENCE,
   1.125 +        0, NULL, sizeof(SFTKDBEncryptedDataInfo) },
   1.126 +    { SEC_ASN1_INLINE | SEC_ASN1_XTRN ,
   1.127 +        offsetof(SFTKDBEncryptedDataInfo,algorithm),
   1.128 +        SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
   1.129 +    { SEC_ASN1_OCTET_STRING,
   1.130 +        offsetof(SFTKDBEncryptedDataInfo,encryptedData) },
   1.131 +    { 0 }
   1.132 +};
   1.133 +
   1.134 +/*
   1.135 + * This parses the cipherText into cipher value. NOTE: cipherValue will point
   1.136 + * to data in cipherText, if cipherText is freed, cipherValue will be invalid.
   1.137 + */
   1.138 +static SECStatus
   1.139 +sftkdb_decodeCipherText(SECItem *cipherText, sftkCipherValue *cipherValue)
   1.140 +{
   1.141 +    PLArenaPool *arena = NULL;
   1.142 +    SFTKDBEncryptedDataInfo edi;
   1.143 +    SECStatus rv;
   1.144 +
   1.145 +    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   1.146 +    if (arena == NULL) {
   1.147 +	return SECFailure;
   1.148 +    }
   1.149 +    cipherValue->arena = NULL;
   1.150 +    cipherValue->param = NULL;
   1.151 +
   1.152 +    rv = SEC_QuickDERDecodeItem(arena, &edi, sftkdb_EncryptedDataInfoTemplate,
   1.153 +                            cipherText);
   1.154 +    if (rv != SECSuccess) {
   1.155 +	goto loser;
   1.156 +    }
   1.157 +    cipherValue->alg = SECOID_GetAlgorithmTag(&edi.algorithm);
   1.158 +    cipherValue->param = nsspkcs5_AlgidToParam(&edi.algorithm);
   1.159 +    if (cipherValue->param == NULL) {
   1.160 +	goto loser;
   1.161 +    }
   1.162 +    cipherValue->value = edi.encryptedData;
   1.163 +    cipherValue->arena = arena;
   1.164 +
   1.165 +    return SECSuccess;
   1.166 +loser:
   1.167 +    if (cipherValue->param) {
   1.168 +	nsspkcs5_DestroyPBEParameter(cipherValue->param);
   1.169 +	cipherValue->param = NULL;
   1.170 +    }
   1.171 +    if (arena) {
   1.172 +	PORT_FreeArena(arena,PR_FALSE);
   1.173 +    }
   1.174 +    return SECFailure;
   1.175 +}
   1.176 +
   1.177 +
   1.178 +
   1.179 +/* 
   1.180 + * unlike decode, Encode actually allocates a SECItem the caller must free
   1.181 + * The caller can pass an optional arena to to indicate where to place
   1.182 + * the resultant cipherText.
   1.183 + */
   1.184 +static SECStatus
   1.185 +sftkdb_encodeCipherText(PLArenaPool *arena, sftkCipherValue *cipherValue, 
   1.186 +                        SECItem **cipherText)
   1.187 +{
   1.188 +    SFTKDBEncryptedDataInfo edi;
   1.189 +    SECAlgorithmID *algid;
   1.190 +    SECStatus rv;
   1.191 +    PLArenaPool *localArena = NULL;
   1.192 +
   1.193 +
   1.194 +    localArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
   1.195 +    if (localArena == NULL) {
   1.196 +	return SECFailure;
   1.197 +    }
   1.198 +
   1.199 +    algid = nsspkcs5_CreateAlgorithmID(localArena, cipherValue->alg, 
   1.200 +					cipherValue->param);
   1.201 +    if (algid == NULL) {
   1.202 +	rv = SECFailure;
   1.203 +	goto loser;
   1.204 +    }
   1.205 +    rv = SECOID_CopyAlgorithmID(localArena, &edi.algorithm, algid);
   1.206 +    SECOID_DestroyAlgorithmID(algid, PR_TRUE);
   1.207 +    if (rv != SECSuccess) {
   1.208 +	goto loser;
   1.209 +    }
   1.210 +    edi.encryptedData = cipherValue->value;
   1.211 +
   1.212 +    *cipherText = SEC_ASN1EncodeItem(arena, NULL, &edi, 
   1.213 +				    sftkdb_EncryptedDataInfoTemplate);
   1.214 +    if (*cipherText == NULL) {
   1.215 +	rv = SECFailure;
   1.216 +    }
   1.217 +
   1.218 +loser:
   1.219 +    if (localArena) {
   1.220 +	PORT_FreeArena(localArena,PR_FALSE);
   1.221 +    }
   1.222 +
   1.223 +    return rv;
   1.224 +}
   1.225 +
   1.226 +
   1.227 +/*
   1.228 + * Use our key to decode a cipherText block from the database.
   1.229 + *
   1.230 + * plain text is allocated by nsspkcs5_CipherData and must be freed
   1.231 + * with SECITEM_FreeItem by the caller.
   1.232 + */
   1.233 +SECStatus
   1.234 +sftkdb_DecryptAttribute(SECItem *passKey, SECItem *cipherText, SECItem **plain) 
   1.235 +{
   1.236 +    SECStatus rv;
   1.237 +    sftkCipherValue cipherValue;
   1.238 +
   1.239 +    /* First get the cipher type */
   1.240 +    rv = sftkdb_decodeCipherText(cipherText, &cipherValue);
   1.241 +    if (rv != SECSuccess) {
   1.242 +	goto loser;
   1.243 +    }
   1.244 +
   1.245 +    *plain = nsspkcs5_CipherData(cipherValue.param, passKey, &cipherValue.value, 
   1.246 +				    PR_FALSE, NULL);
   1.247 +    if (*plain == NULL) {
   1.248 +	rv = SECFailure;
   1.249 +	goto loser;
   1.250 +    } 
   1.251 +
   1.252 +loser:
   1.253 +    if (cipherValue.param) {
   1.254 +	nsspkcs5_DestroyPBEParameter(cipherValue.param);
   1.255 +    }
   1.256 +    if (cipherValue.arena) {
   1.257 +	PORT_FreeArena(cipherValue.arena,PR_FALSE);
   1.258 +    }
   1.259 +    return rv;
   1.260 +}
   1.261 +
   1.262 +/*
   1.263 + * encrypt a block. This function returned the encrypted ciphertext which
   1.264 + * the caller must free. If the caller provides an arena, cipherText will
   1.265 + * be allocated out of that arena. This also generated the per entry
   1.266 + * salt automatically.
   1.267 + */
   1.268 +SECStatus
   1.269 +sftkdb_EncryptAttribute(PLArenaPool *arena, SECItem *passKey, 
   1.270 +		SECItem *plainText, SECItem **cipherText) 
   1.271 +{
   1.272 +    SECStatus rv;
   1.273 +    sftkCipherValue cipherValue;
   1.274 +    SECItem *cipher = NULL;
   1.275 +    NSSPKCS5PBEParameter *param = NULL;
   1.276 +    unsigned char saltData[HASH_LENGTH_MAX];
   1.277 +
   1.278 +    cipherValue.alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC;
   1.279 +    cipherValue.salt.len = SHA1_LENGTH;
   1.280 +    cipherValue.salt.data = saltData;
   1.281 +    RNG_GenerateGlobalRandomBytes(saltData,cipherValue.salt.len);
   1.282 +
   1.283 +    param = nsspkcs5_NewParam(cipherValue.alg, &cipherValue.salt, 1);
   1.284 +    if (param == NULL) {
   1.285 +	rv = SECFailure;
   1.286 +	goto loser;
   1.287 +    }
   1.288 +    cipher = nsspkcs5_CipherData(param, passKey, plainText, PR_TRUE, NULL);
   1.289 +    if (cipher == NULL) {
   1.290 +	rv = SECFailure;
   1.291 +	goto loser;
   1.292 +    } 
   1.293 +    cipherValue.value = *cipher;
   1.294 +    cipherValue.param = param;
   1.295 +
   1.296 +    rv = sftkdb_encodeCipherText(arena, &cipherValue, cipherText);
   1.297 +    if (rv != SECSuccess) {
   1.298 +	goto loser;
   1.299 +    }
   1.300 +
   1.301 +loser:
   1.302 +    if (cipher) {
   1.303 +	SECITEM_FreeItem(cipher, PR_TRUE);
   1.304 +    }
   1.305 +    if (param) {
   1.306 +	nsspkcs5_DestroyPBEParameter(param);
   1.307 +    }
   1.308 +    return rv;
   1.309 +}
   1.310 +
   1.311 +/*
   1.312 + * use the password and the pbe parameters to generate an HMAC for the
   1.313 + * given plain text data. This is used by sftkdb_VerifyAttribute and
   1.314 + * sftkdb_SignAttribute. Signature is returned in signData. The caller
   1.315 + * must preallocate the space in the secitem.
   1.316 + */
   1.317 +static SECStatus
   1.318 +sftkdb_pbehash(SECOidTag sigOid, SECItem *passKey, 
   1.319 +	       NSSPKCS5PBEParameter *param,
   1.320 +	       CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE attrType,
   1.321 +	       SECItem *plainText, SECItem *signData)
   1.322 +{
   1.323 +    SECStatus rv = SECFailure;
   1.324 +    SECItem *key = NULL;
   1.325 +    HMACContext *hashCx = NULL;
   1.326 +    HASH_HashType hashType = HASH_AlgNULL;
   1.327 +    const SECHashObject *hashObj;
   1.328 +    unsigned char addressData[SDB_ULONG_SIZE];
   1.329 +
   1.330 +    hashType = HASH_FromHMACOid(param->encAlg);
   1.331 +    if (hashType == HASH_AlgNULL) {
   1.332 +	PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
   1.333 +	return SECFailure;
   1.334 +    }
   1.335 +
   1.336 +    hashObj = HASH_GetRawHashObject(hashType);
   1.337 +    if (hashObj == NULL) {
   1.338 +	goto loser;
   1.339 +    }
   1.340 +
   1.341 +    key = nsspkcs5_ComputeKeyAndIV(param, passKey, NULL, PR_FALSE);
   1.342 +    if (!key) {
   1.343 +	goto loser;
   1.344 +    }
   1.345 +
   1.346 +    hashCx = HMAC_Create(hashObj, key->data, key->len, PR_TRUE);
   1.347 +    if (!hashCx) {
   1.348 +	goto loser;
   1.349 +    }
   1.350 +    HMAC_Begin(hashCx);
   1.351 +    /* Tie this value to a particular object. This is most important for
   1.352 +     * the trust attributes, where and attacker could copy a value for
   1.353 +     * 'validCA' from another cert in the database */
   1.354 +    sftk_ULong2SDBULong(addressData, objectID);
   1.355 +    HMAC_Update(hashCx, addressData, SDB_ULONG_SIZE);
   1.356 +    sftk_ULong2SDBULong(addressData, attrType);
   1.357 +    HMAC_Update(hashCx, addressData, SDB_ULONG_SIZE);
   1.358 +
   1.359 +    HMAC_Update(hashCx, plainText->data, plainText->len);
   1.360 +    rv = HMAC_Finish(hashCx, signData->data, &signData->len, signData->len);
   1.361 +
   1.362 +loser:
   1.363 +    if (hashCx) {
   1.364 +	HMAC_Destroy(hashCx, PR_TRUE);
   1.365 +    }
   1.366 +    if (key) {
   1.367 +	SECITEM_FreeItem(key,PR_TRUE);
   1.368 +    }
   1.369 +    return rv;
   1.370 +}
   1.371 +
   1.372 +/*
   1.373 + * Use our key to verify a signText block from the database matches
   1.374 + * the plainText from the database. The signText is a PKCS 5 v2 pbe.
   1.375 + * plainText is the plainText of the attribute.
   1.376 + */
   1.377 +SECStatus
   1.378 +sftkdb_VerifyAttribute(SECItem *passKey, CK_OBJECT_HANDLE objectID, 
   1.379 +	     CK_ATTRIBUTE_TYPE attrType, 
   1.380 +	     SECItem *plainText, SECItem *signText) 
   1.381 +{
   1.382 +    SECStatus rv;
   1.383 +    sftkCipherValue signValue;
   1.384 +    SECItem signature;
   1.385 +    unsigned char signData[HASH_LENGTH_MAX];
   1.386 +    
   1.387 +
   1.388 +    /* First get the cipher type */
   1.389 +    rv = sftkdb_decodeCipherText(signText, &signValue);
   1.390 +    if (rv != SECSuccess) {
   1.391 +	goto loser;
   1.392 +    }
   1.393 +    signature.data = signData;
   1.394 +    signature.len = sizeof(signData);
   1.395 +
   1.396 +    rv = sftkdb_pbehash(signValue.alg, passKey, signValue.param, 
   1.397 +			objectID, attrType, plainText, &signature);
   1.398 +    if (rv != SECSuccess) {
   1.399 +	goto loser;
   1.400 +    }
   1.401 +    if (SECITEM_CompareItem(&signValue.value,&signature) != 0) {
   1.402 +	PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
   1.403 +	rv = SECFailure;
   1.404 +    }
   1.405 +
   1.406 +loser:
   1.407 +    if (signValue.param) {
   1.408 +	nsspkcs5_DestroyPBEParameter(signValue.param);
   1.409 +    }
   1.410 +    if (signValue.arena) {
   1.411 +	PORT_FreeArena(signValue.arena,PR_FALSE);
   1.412 +    }
   1.413 +    return rv;
   1.414 +}
   1.415 +
   1.416 +/*
   1.417 + * Use our key to create a signText block the plain text of an
   1.418 + * attribute. The signText is a PKCS 5 v2 pbe.
   1.419 + */
   1.420 +SECStatus
   1.421 +sftkdb_SignAttribute(PLArenaPool *arena, SECItem *passKey, 
   1.422 +	 CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE attrType, 
   1.423 +	 SECItem *plainText, SECItem **signature) 
   1.424 +{
   1.425 +    SECStatus rv;
   1.426 +    sftkCipherValue signValue;
   1.427 +    NSSPKCS5PBEParameter *param = NULL;
   1.428 +    unsigned char saltData[HASH_LENGTH_MAX];
   1.429 +    unsigned char signData[HASH_LENGTH_MAX];
   1.430 +    SECOidTag hmacAlg = SEC_OID_HMAC_SHA256; /* hash for authentication */
   1.431 +    SECOidTag prfAlg = SEC_OID_HMAC_SHA256;  /* hash for pb key generation */
   1.432 +    HASH_HashType prfType;
   1.433 +    unsigned int hmacLength;
   1.434 +    unsigned int prfLength;
   1.435 +
   1.436 +    /* this code allows us to fetch the lengths and hashes on the fly
   1.437 +     * by simply changing the OID above */
   1.438 +    prfType = HASH_FromHMACOid(prfAlg);
   1.439 +    PORT_Assert(prfType != HASH_AlgNULL);
   1.440 +    prfLength = HASH_GetRawHashObject(prfType)->length;
   1.441 +    PORT_Assert(prfLength <= HASH_LENGTH_MAX);
   1.442 +
   1.443 +    hmacLength = HASH_GetRawHashObject(HASH_FromHMACOid(hmacAlg))->length;
   1.444 +    PORT_Assert(hmacLength <= HASH_LENGTH_MAX);
   1.445 +
   1.446 +    /* initialize our CipherValue structure */
   1.447 +    signValue.alg = SEC_OID_PKCS5_PBMAC1;
   1.448 +    signValue.salt.len = prfLength;
   1.449 +    signValue.salt.data = saltData;
   1.450 +    signValue.value.data = signData;
   1.451 +    signValue.value.len = hmacLength;
   1.452 +    RNG_GenerateGlobalRandomBytes(saltData,prfLength);
   1.453 +
   1.454 +    /* initialize our pkcs5 parameter */
   1.455 +    param = nsspkcs5_NewParam(signValue.alg, &signValue.salt, 1);
   1.456 +    if (param == NULL) {
   1.457 +	rv = SECFailure;
   1.458 +	goto loser;
   1.459 +    }
   1.460 +    param->keyID = pbeBitGenIntegrityKey;
   1.461 +    /* set the PKCS 5 v2 parameters, not extractable from the
   1.462 +     * data passed into nsspkcs5_NewParam */
   1.463 +    param->encAlg = hmacAlg;
   1.464 +    param->hashType = prfType;
   1.465 +    param->keyLen = hmacLength;
   1.466 +    rv = SECOID_SetAlgorithmID(param->poolp, &param->prfAlg, prfAlg, NULL);
   1.467 +    if (rv != SECSuccess) {
   1.468 +	goto loser;
   1.469 +    }
   1.470 +
   1.471 +
   1.472 +    /* calculate the mac */
   1.473 +    rv = sftkdb_pbehash(signValue.alg, passKey, param, objectID, attrType,
   1.474 +			plainText, &signValue.value);
   1.475 +    if (rv != SECSuccess) {
   1.476 +	goto loser;
   1.477 +    }
   1.478 +    signValue.param = param;
   1.479 +
   1.480 +    /* write it out */
   1.481 +    rv = sftkdb_encodeCipherText(arena, &signValue, signature);
   1.482 +    if (rv != SECSuccess) {
   1.483 +	goto loser;
   1.484 +    }
   1.485 +
   1.486 +loser:
   1.487 +    if (param) {
   1.488 +	nsspkcs5_DestroyPBEParameter(param);
   1.489 +    }
   1.490 +    return rv;
   1.491 +}
   1.492 +
   1.493 +/*
   1.494 + * safely swith the passed in key for the one caches in the keydb handle
   1.495 + * 
   1.496 + * A key attached to the handle tells us the the token is logged in.
   1.497 + * We can used the key attached to the handle in sftkdb_EncryptAttribute 
   1.498 + *  and sftkdb_DecryptAttribute calls.
   1.499 + */  
   1.500 +static void 
   1.501 +sftkdb_switchKeys(SFTKDBHandle *keydb, SECItem *passKey)
   1.502 +{
   1.503 +    unsigned char *data;
   1.504 +    int len;
   1.505 +
   1.506 +    if (keydb->passwordLock == NULL) {
   1.507 +	PORT_Assert(keydb->type != SFTK_KEYDB_TYPE);
   1.508 +	return;
   1.509 +    }
   1.510 +
   1.511 +    /* an atomic pointer set would be nice */
   1.512 +    SKIP_AFTER_FORK(PZ_Lock(keydb->passwordLock));
   1.513 +    data = keydb->passwordKey.data;
   1.514 +    len = keydb->passwordKey.len;
   1.515 +    keydb->passwordKey.data = passKey->data;
   1.516 +    keydb->passwordKey.len = passKey->len;
   1.517 +    passKey->data = data;
   1.518 +    passKey->len = len;
   1.519 +    SKIP_AFTER_FORK(PZ_Unlock(keydb->passwordLock));
   1.520 +}
   1.521 +
   1.522 +/*
   1.523 + * returns true if we are in a middle of a merge style update.
   1.524 + */
   1.525 +PRBool
   1.526 +sftkdb_InUpdateMerge(SFTKDBHandle *keydb)
   1.527 +{
   1.528 +    return keydb->updateID ? PR_TRUE : PR_FALSE;
   1.529 +}
   1.530 +
   1.531 +/*
   1.532 + * returns true if we are looking for the password for the user's old source
   1.533 + * database as part of a merge style update.
   1.534 + */
   1.535 +PRBool
   1.536 +sftkdb_NeedUpdateDBPassword(SFTKDBHandle *keydb)
   1.537 +{
   1.538 +    if (!sftkdb_InUpdateMerge(keydb)) {
   1.539 +	return PR_FALSE;
   1.540 +    }
   1.541 +    if (keydb->updateDBIsInit && !keydb->updatePasswordKey) {
   1.542 +	return PR_TRUE;
   1.543 +    }
   1.544 +    return PR_FALSE;
   1.545 +}
   1.546 +
   1.547 +/*
   1.548 + * fetch an update password key from a handle.
   1.549 + */
   1.550 +SECItem *
   1.551 +sftkdb_GetUpdatePasswordKey(SFTKDBHandle *handle)
   1.552 +{
   1.553 +    SECItem *key = NULL;
   1.554 +
   1.555 +    /* if we're a cert db, fetch it from our peer key db */
   1.556 +    if (handle->type == SFTK_CERTDB_TYPE) {
   1.557 +	handle = handle->peerDB;
   1.558 +    }
   1.559 +
   1.560 +    /* don't have one */
   1.561 +    if (!handle) {
   1.562 +	return NULL;
   1.563 +    }
   1.564 +
   1.565 +    PZ_Lock(handle->passwordLock);
   1.566 +    if (handle->updatePasswordKey) {
   1.567 +	key = SECITEM_DupItem(handle->updatePasswordKey);
   1.568 +    }
   1.569 +    PZ_Unlock(handle->passwordLock);
   1.570 +
   1.571 +    return key;
   1.572 +}
   1.573 +
   1.574 +/*
   1.575 + * free the update password key from a handle.
   1.576 + */
   1.577 +void
   1.578 +sftkdb_FreeUpdatePasswordKey(SFTKDBHandle *handle)
   1.579 +{
   1.580 +    SECItem *key = NULL;
   1.581 +
   1.582 +    /* don't have one */
   1.583 +    if (!handle) {
   1.584 +	return;
   1.585 +    }
   1.586 +
   1.587 +    /* if we're a cert db, we don't have one */
   1.588 +    if (handle->type == SFTK_CERTDB_TYPE) {
   1.589 +	return;
   1.590 +    }
   1.591 +
   1.592 +    PZ_Lock(handle->passwordLock);
   1.593 +    if (handle->updatePasswordKey) {
   1.594 +	key = handle->updatePasswordKey;
   1.595 +	handle->updatePasswordKey = NULL;
   1.596 +    }
   1.597 +    PZ_Unlock(handle->passwordLock);
   1.598 +
   1.599 +    if (key) {
   1.600 +	SECITEM_ZfreeItem(key, PR_TRUE);
   1.601 +    }
   1.602 +
   1.603 +    return;
   1.604 +}
   1.605 +
   1.606 +/*
   1.607 + * what password db we use depends heavily on the update state machine
   1.608 + * 
   1.609 + *  1) no update db, return the normal database.
   1.610 + *  2) update db and no merge return the update db.
   1.611 + *  3) update db and in merge: 
   1.612 + *      return the update db if we need the update db's password, 
   1.613 + *      otherwise return our normal datbase.
   1.614 + */
   1.615 +static SDB *
   1.616 +sftk_getPWSDB(SFTKDBHandle *keydb)
   1.617 +{
   1.618 +    if (!keydb->update) {
   1.619 +	return keydb->db;
   1.620 +    }
   1.621 +    if (!sftkdb_InUpdateMerge(keydb)) {
   1.622 +	return keydb->update;
   1.623 +    }
   1.624 +    if (sftkdb_NeedUpdateDBPassword(keydb)) {
   1.625 +	return keydb->update;
   1.626 +    }
   1.627 +    return keydb->db;
   1.628 +}
   1.629 +
   1.630 +/*
   1.631 + * return success if we have a valid password entry.
   1.632 + * This is will show up outside of PKCS #11 as CKF_USER_PIN_INIT
   1.633 + * in the token flags.
   1.634 + */
   1.635 +SECStatus 
   1.636 +sftkdb_HasPasswordSet(SFTKDBHandle *keydb)
   1.637 +{
   1.638 +    SECItem salt, value;
   1.639 +    unsigned char saltData[SDB_MAX_META_DATA_LEN];
   1.640 +    unsigned char valueData[SDB_MAX_META_DATA_LEN];
   1.641 +    CK_RV crv;
   1.642 +    SDB *db;
   1.643 +
   1.644 +    if (keydb == NULL) {
   1.645 +	return SECFailure;
   1.646 +    }
   1.647 +
   1.648 +    db = sftk_getPWSDB(keydb);
   1.649 +    if (db == NULL) {
   1.650 +	return SECFailure;
   1.651 +    }
   1.652 +
   1.653 +    salt.data = saltData;
   1.654 +    salt.len = sizeof(saltData);
   1.655 +    value.data = valueData;
   1.656 +    value.len = sizeof(valueData);
   1.657 +    crv = (*db->sdb_GetMetaData)(db, "password", &salt, &value);
   1.658 +
   1.659 +    /* If no password is set, we can update right away */
   1.660 +    if (((keydb->db->sdb_flags & SDB_RDONLY) == 0) && keydb->update 
   1.661 +	&& crv != CKR_OK) {
   1.662 +	/* update the peer certdb if it exists */
   1.663 +	if (keydb->peerDB) {
   1.664 +	    sftkdb_Update(keydb->peerDB, NULL);
   1.665 +	}
   1.666 +	sftkdb_Update(keydb, NULL);
   1.667 +    }
   1.668 +    return (crv == CKR_OK) ? SECSuccess : SECFailure;
   1.669 +}
   1.670 +
   1.671 +#define SFTK_PW_CHECK_STRING "password-check"
   1.672 +#define SFTK_PW_CHECK_LEN 14
   1.673 +
   1.674 +/*
   1.675 + * check if the supplied password is valid
   1.676 + */
   1.677 +SECStatus  
   1.678 +sftkdb_CheckPassword(SFTKDBHandle *keydb, const char *pw, PRBool *tokenRemoved)
   1.679 +{
   1.680 +    SECStatus rv;
   1.681 +    SECItem salt, value;
   1.682 +    unsigned char saltData[SDB_MAX_META_DATA_LEN];
   1.683 +    unsigned char valueData[SDB_MAX_META_DATA_LEN];
   1.684 +    SECItem key;
   1.685 +    SECItem *result = NULL;
   1.686 +    SDB *db;
   1.687 +    CK_RV crv;
   1.688 +
   1.689 +    if (keydb == NULL) {
   1.690 +	return SECFailure;
   1.691 +    }
   1.692 +
   1.693 +    db = sftk_getPWSDB(keydb);
   1.694 +    if (db == NULL) {
   1.695 +	return SECFailure;
   1.696 +    }
   1.697 +
   1.698 +    key.data = NULL;
   1.699 +    key.len = 0;
   1.700 +
   1.701 +    if (pw == NULL) pw="";
   1.702 +
   1.703 +    /* get the entry from the database */
   1.704 +    salt.data = saltData;
   1.705 +    salt.len = sizeof(saltData);
   1.706 +    value.data = valueData;
   1.707 +    value.len = sizeof(valueData);
   1.708 +    crv = (*db->sdb_GetMetaData)(db, "password", &salt, &value);
   1.709 +    if (crv != CKR_OK) {
   1.710 +	rv = SECFailure;
   1.711 +	goto done;
   1.712 +    }
   1.713 +
   1.714 +    /* get our intermediate key based on the entry salt value */
   1.715 +    rv = sftkdb_passwordToKey(keydb, &salt, pw, &key);
   1.716 +    if (rv != SECSuccess) {
   1.717 +	goto done;
   1.718 +    }
   1.719 +
   1.720 +    /* decrypt the entry value */
   1.721 +    rv = sftkdb_DecryptAttribute(&key, &value, &result);
   1.722 +    if (rv != SECSuccess) {
   1.723 +	goto done;
   1.724 +    }
   1.725 +
   1.726 +    /* if it's what we expect, update our key in the database handle and
   1.727 +     * return Success */
   1.728 +    if ((result->len == SFTK_PW_CHECK_LEN) &&
   1.729 +      PORT_Memcmp(result->data, SFTK_PW_CHECK_STRING, SFTK_PW_CHECK_LEN) == 0){
   1.730 +	/*
   1.731 +	 * We have a password, now lets handle any potential update cases..
   1.732 +	 * 
   1.733 +	 * First, the normal case: no update. In this case we only need the
   1.734 +	 *  the password for our only DB, which we now have, we switch 
   1.735 +	 *  the keys and fall through.
   1.736 +	 * Second regular (non-merge) update: The target DB does not yet have
   1.737 +	 *  a password initialized, we now have the password for the source DB,
   1.738 +	 *  so we can switch the keys and simply update the target database.
   1.739 +	 * Merge update case: This one is trickier.
   1.740 +	 *   1) If we need the source DB password, then we just got it here.
   1.741 +	 *       We need to save that password,
   1.742 +	 *       then we need to check to see if we need or have the target 
   1.743 +	 *         database password.
   1.744 +	 *       If we have it (it's the same as the source), or don't need 
   1.745 +	 *         it (it's not set or is ""), we can start the update now.
   1.746 +	 *       If we don't have it, we need the application to get it from 
   1.747 +	 *         the user. Clear our sessions out to simulate a token 
   1.748 +	 *         removal. C_GetTokenInfo will change the token description 
   1.749 +	 *         and the token will still appear to be logged out.
   1.750 +	 *   2) If we already have the source DB  password, this password is 
   1.751 +	 *         for the target database. We can now move forward with the 
   1.752 +	 *         update, as we now have both required passwords.
   1.753 +	 *
   1.754 +	 */
   1.755 +        PZ_Lock(keydb->passwordLock);
   1.756 +	if (sftkdb_NeedUpdateDBPassword(keydb)) {
   1.757 +	    /* Squirrel this special key away.
   1.758 +	     * This has the side effect of turning sftkdb_NeedLegacyPW off,
   1.759 +	     * as well as changing which database is returned from 
   1.760 +	     * SFTK_GET_PW_DB (thus effecting both sftkdb_CheckPassword()
   1.761 +	     * and sftkdb_HasPasswordSet()) */
   1.762 +	    keydb->updatePasswordKey = SECITEM_DupItem(&key);
   1.763 +	    PZ_Unlock(keydb->passwordLock);
   1.764 +	    if (keydb->updatePasswordKey == NULL) {
   1.765 +		/* PORT_Error set by SECITEM_DupItem */
   1.766 +		rv = SECFailure;
   1.767 +		goto done;
   1.768 +	    }
   1.769 +
   1.770 +	    /* Simulate a token removal -- we need to do this any
   1.771 +             * any case at this point so the token name is correct. */
   1.772 +	    *tokenRemoved = PR_TRUE;
   1.773 +
   1.774 +	    /* 
   1.775 +	     * OK, we got the update DB password, see if we need a password
   1.776 +	     * for the target...
   1.777 +	     */
   1.778 +	    if (sftkdb_HasPasswordSet(keydb) == SECSuccess) {
   1.779 +		/* We have a password, do we know what the password is?
   1.780 +		 *  check 1) for the password the user supplied for the 
   1.781 +		 *           update DB,
   1.782 +		 *    and 2) for the null password.
   1.783 +		 *
   1.784 +		 * RECURSION NOTE: we are calling ourselves here. This means
   1.785 +		 *  any updates, switchKeys, etc will have been completed
   1.786 +		 *  if these functions return successfully, in those cases
   1.787 +		 *  just exit returning Success. We don't recurse infinitely
   1.788 +		 *  because we are making this call from a NeedUpdateDBPassword
   1.789 +		 *  block and we've already set that update password at this
   1.790 +		 *  point.  */
   1.791 +		rv = sftkdb_CheckPassword(keydb, pw, tokenRemoved);
   1.792 +		if (rv == SECSuccess) {
   1.793 +		    /* source and target databases have the same password, we 
   1.794 +		     * are good to go */
   1.795 +		    goto done;
   1.796 +		}
   1.797 +		sftkdb_CheckPassword(keydb, "", tokenRemoved);
   1.798 +
   1.799 +		/*
   1.800 +		 * Important 'NULL' code here. At this point either we 
   1.801 +		 * succeeded in logging in with "" or we didn't.
   1.802 +                 *
   1.803 +                 *  If we did succeed at login, our machine state will be set
   1.804 +		 * to logged in appropriately. The application will find that 
   1.805 +		 * it's logged in as soon as it opens a new session. We have 
   1.806 +		 * also completed the update. Life is good.
   1.807 +		 * 
   1.808 +		 *  If we did not succeed, well the user still successfully
   1.809 +		 * logged into the update database, since we faked the token 
   1.810 +		 * removal it's just like the user logged into his smart card 
   1.811 +		 * then removed it. the actual login work, so we report that 
   1.812 +		 * success back to the user, but we won't actually be
   1.813 +		 * logged in. The application will find this out when it
   1.814 +		 * checks it's login state, thus triggering another password
   1.815 +		 * prompt so we can get the real target DB password.
   1.816 +		 *
   1.817 +		 * summary, we exit from here with SECSuccess no matter what.
   1.818 +		 */
   1.819 +		rv = SECSuccess;
   1.820 +		goto done;
   1.821 +	    } else {
   1.822 +		/* there is no password, just fall through to update.
   1.823 +		 * update will write the source DB's password record
   1.824 +		 * into the target DB just like it would in a non-merge
   1.825 +		 * update case. */
   1.826 +	    }
   1.827 +	} else {
   1.828 +	    PZ_Unlock(keydb->passwordLock);
   1.829 +	}
   1.830 +	/* load the keys, so the keydb can parse it's key set */
   1.831 +	sftkdb_switchKeys(keydb, &key);
   1.832 +
   1.833 +	/* we need to update, do it now */
   1.834 +	if (((keydb->db->sdb_flags & SDB_RDONLY) == 0) && keydb->update) {
   1.835 +	    /* update the peer certdb if it exists */
   1.836 +	    if (keydb->peerDB) {
   1.837 +		sftkdb_Update(keydb->peerDB, &key);
   1.838 +	    }
   1.839 +	    sftkdb_Update(keydb, &key);
   1.840 +	}
   1.841 +    } else {
   1.842 +        rv = SECFailure;
   1.843 +	/*PORT_SetError( bad password); */
   1.844 +    }
   1.845 +
   1.846 +done:
   1.847 +    if (key.data) {
   1.848 +	PORT_ZFree(key.data,key.len);
   1.849 +    }
   1.850 +    if (result) {
   1.851 +	SECITEM_FreeItem(result,PR_TRUE);
   1.852 +    }
   1.853 +    return rv;
   1.854 +}
   1.855 +
   1.856 +/*
   1.857 + * return Success if the there is a cached password key.
   1.858 + */
   1.859 +SECStatus
   1.860 +sftkdb_PWCached(SFTKDBHandle *keydb)
   1.861 +{
   1.862 +    return keydb->passwordKey.data ? SECSuccess : SECFailure;
   1.863 +}
   1.864 +
   1.865 +
   1.866 +static CK_RV
   1.867 +sftk_updateMacs(PLArenaPool *arena, SFTKDBHandle *handle,
   1.868 +		       CK_OBJECT_HANDLE id, SECItem *newKey)
   1.869 +{
   1.870 +    CK_RV crv = CKR_OK;
   1.871 +    CK_RV crv2;
   1.872 +    CK_ATTRIBUTE authAttrs[] = {
   1.873 +	{CKA_MODULUS, NULL, 0},
   1.874 +	{CKA_PUBLIC_EXPONENT, NULL, 0},
   1.875 +	{CKA_CERT_SHA1_HASH, NULL, 0},
   1.876 +	{CKA_CERT_MD5_HASH, NULL, 0},
   1.877 +	{CKA_TRUST_SERVER_AUTH, NULL, 0},
   1.878 +	{CKA_TRUST_CLIENT_AUTH, NULL, 0},
   1.879 +	{CKA_TRUST_EMAIL_PROTECTION, NULL, 0},
   1.880 +	{CKA_TRUST_CODE_SIGNING, NULL, 0},
   1.881 +	{CKA_TRUST_STEP_UP_APPROVED, NULL, 0},
   1.882 +	{CKA_NSS_OVERRIDE_EXTENSIONS, NULL, 0},
   1.883 +    };
   1.884 +    CK_ULONG authAttrCount = sizeof(authAttrs)/sizeof(CK_ATTRIBUTE);
   1.885 +    int i, count;
   1.886 +    SFTKDBHandle *keyHandle = handle;
   1.887 +    SDB *keyTarget = NULL;
   1.888 +
   1.889 +    id &= SFTK_OBJ_ID_MASK;
   1.890 +
   1.891 +    if (handle->type != SFTK_KEYDB_TYPE) {
   1.892 +	keyHandle = handle->peerDB;
   1.893 +    }
   1.894 +
   1.895 +    if (keyHandle == NULL) {
   1.896 +	return CKR_OK;
   1.897 +    }
   1.898 +
   1.899 +    /* old DB's don't have meta data, finished with MACs */
   1.900 +    keyTarget = SFTK_GET_SDB(keyHandle);
   1.901 +    if ((keyTarget->sdb_flags &SDB_HAS_META) == 0) {
   1.902 +	return CKR_OK;
   1.903 +    }
   1.904 +
   1.905 +    /*
   1.906 +     * STEP 1: find the MACed attributes of this object 
   1.907 +     */
   1.908 +    crv2 = sftkdb_GetAttributeValue(handle, id, authAttrs, authAttrCount);
   1.909 +    count = 0;
   1.910 +    /* allocate space for the attributes */
   1.911 +    for (i=0; i < authAttrCount; i++) {
   1.912 +	if ((authAttrs[i].ulValueLen == -1) || (authAttrs[i].ulValueLen == 0)){
   1.913 +	    continue;
   1.914 +	}
   1.915 +	count++;
   1.916 +        authAttrs[i].pValue = PORT_ArenaAlloc(arena,authAttrs[i].ulValueLen);
   1.917 +	if (authAttrs[i].pValue == NULL) {
   1.918 +	    crv = CKR_HOST_MEMORY;
   1.919 +	    break;
   1.920 +	}
   1.921 +    }
   1.922 +
   1.923 +    /* if count was zero, none were found, finished with MACs */
   1.924 +    if (count == 0) {
   1.925 +	return CKR_OK;
   1.926 +    }
   1.927 +
   1.928 +    crv = sftkdb_GetAttributeValue(handle, id, authAttrs, authAttrCount);
   1.929 +    /* ignore error code, we expect some possible errors */
   1.930 +
   1.931 +    /* GetAttributeValue just verified the old macs, safe to write
   1.932 +     * them out then... */
   1.933 +    for (i=0; i < authAttrCount; i++) {
   1.934 +	SECItem *signText;
   1.935 +	SECItem plainText;
   1.936 +	SECStatus rv;
   1.937 +
   1.938 +	if ((authAttrs[i].ulValueLen == -1) || (authAttrs[i].ulValueLen == 0)){
   1.939 +	    continue;
   1.940 +	}
   1.941 +
   1.942 +	plainText.data = authAttrs[i].pValue;
   1.943 +	plainText.len = authAttrs[i].ulValueLen;
   1.944 +	rv = sftkdb_SignAttribute(arena, newKey, id, 
   1.945 +			authAttrs[i].type, &plainText, &signText);
   1.946 +	if (rv != SECSuccess) {
   1.947 +	    return CKR_GENERAL_ERROR;
   1.948 +	}
   1.949 +	rv = sftkdb_PutAttributeSignature(handle, keyTarget, id, 
   1.950 +				authAttrs[i].type, signText);
   1.951 +	if (rv != SECSuccess) {
   1.952 +	    return CKR_GENERAL_ERROR;
   1.953 +	}
   1.954 +    }
   1.955 +
   1.956 +    return CKR_OK;
   1.957 +}
   1.958 +	
   1.959 +static CK_RV
   1.960 +sftk_updateEncrypted(PLArenaPool *arena, SFTKDBHandle *keydb,
   1.961 +		       CK_OBJECT_HANDLE id, SECItem *newKey)
   1.962 +{
   1.963 +    CK_RV crv = CKR_OK;
   1.964 +    CK_RV crv2;
   1.965 +    CK_ATTRIBUTE *first, *last;
   1.966 +    CK_ATTRIBUTE privAttrs[] = {
   1.967 +	{CKA_VALUE, NULL, 0},
   1.968 +	{CKA_PRIVATE_EXPONENT, NULL, 0},
   1.969 +	{CKA_PRIME_1, NULL, 0},
   1.970 +	{CKA_PRIME_2, NULL, 0},
   1.971 +	{CKA_EXPONENT_1, NULL, 0},
   1.972 +	{CKA_EXPONENT_2, NULL, 0},
   1.973 +	{CKA_COEFFICIENT, NULL, 0} };
   1.974 +    CK_ULONG privAttrCount = sizeof(privAttrs)/sizeof(CK_ATTRIBUTE);
   1.975 +    int i, count;
   1.976 +
   1.977 +    /*
   1.978 +     * STEP 1. Read the old attributes in the clear.
   1.979 +     */
   1.980 +
   1.981 +    /* Get the attribute sizes.
   1.982 +     *  ignore the error code, we will have unknown attributes here */
   1.983 +    crv2 = sftkdb_GetAttributeValue(keydb, id, privAttrs, privAttrCount);
   1.984 +
   1.985 +    /*
   1.986 +     * find the valid block of attributes and fill allocate space for
   1.987 +     * their data */
   1.988 +    first = last = NULL;
   1.989 +    for (i=0; i < privAttrCount; i++) {
   1.990 +         /* find the block of attributes that are appropriate for this 
   1.991 +          * objects. There should only be once contiguous block, if not 
   1.992 +          * there's an error.
   1.993 +          *
   1.994 +          * find the first and last good entry.
   1.995 +          */
   1.996 +	if ((privAttrs[i].ulValueLen == -1) || (privAttrs[i].ulValueLen == 0)){
   1.997 +	    if (!first) continue;
   1.998 +	    if (!last) {
   1.999 +		/* previous entry was last good entry */
  1.1000 +		last= &privAttrs[i-1];
  1.1001 +	    }
  1.1002 +	    continue;
  1.1003 +	}
  1.1004 +	if (!first) {
  1.1005 +	    first = &privAttrs[i];
  1.1006 +	}
  1.1007 +	if (last) {
  1.1008 +	   /* OOPS, we've found another good entry beyond the end of the
  1.1009 +	    * last good entry, we need to fail here. */
  1.1010 +	   crv = CKR_GENERAL_ERROR;
  1.1011 +	   break;
  1.1012 +	}
  1.1013 +        privAttrs[i].pValue = PORT_ArenaAlloc(arena,privAttrs[i].ulValueLen);
  1.1014 +	if (privAttrs[i].pValue == NULL) {
  1.1015 +	    crv = CKR_HOST_MEMORY;
  1.1016 +	    break;
  1.1017 +	}
  1.1018 +    }
  1.1019 +    if (first == NULL) {
  1.1020 +	/* no valid entries found, return error based on crv2 */
  1.1021 +	return crv2;
  1.1022 +    }
  1.1023 +    if (last == NULL) {
  1.1024 +	last = &privAttrs[privAttrCount-1];
  1.1025 +    }
  1.1026 +    if (crv != CKR_OK) {
  1.1027 +	return crv;
  1.1028 +    }
  1.1029 +    /* read the attributes */
  1.1030 +    count = (last-first)+1;
  1.1031 +    crv = sftkdb_GetAttributeValue(keydb, id, first, count);
  1.1032 +    if (crv != CKR_OK) {
  1.1033 +	return crv;
  1.1034 +    }
  1.1035 +
  1.1036 +    /*
  1.1037 +     * STEP 2: read the encrypt the attributes with the new key.
  1.1038 +     */
  1.1039 +    for (i=0; i < count; i++) {
  1.1040 +	SECItem plainText;
  1.1041 +	SECItem *result;
  1.1042 +	SECStatus rv;
  1.1043 +
  1.1044 +	plainText.data = first[i].pValue;
  1.1045 +	plainText.len = first[i].ulValueLen;
  1.1046 +    	rv = sftkdb_EncryptAttribute(arena, newKey, &plainText, &result);
  1.1047 +	if (rv != SECSuccess) {
  1.1048 +	   return CKR_GENERAL_ERROR;
  1.1049 +	}
  1.1050 +	first[i].pValue = result->data;
  1.1051 +	first[i].ulValueLen = result->len;
  1.1052 +	/* clear our sensitive data out */
  1.1053 +	PORT_Memset(plainText.data, 0, plainText.len);
  1.1054 +    }
  1.1055 +
  1.1056 +
  1.1057 +    /*
  1.1058 +     * STEP 3: write the newly encrypted attributes out directly
  1.1059 +     */
  1.1060 +    id &= SFTK_OBJ_ID_MASK;
  1.1061 +    keydb->newKey = newKey;
  1.1062 +    crv = (*keydb->db->sdb_SetAttributeValue)(keydb->db, id, first, count);
  1.1063 +    keydb->newKey = NULL;
  1.1064 +
  1.1065 +    return crv;
  1.1066 +}
  1.1067 +	
  1.1068 +static CK_RV
  1.1069 +sftk_convertAttributes(SFTKDBHandle *handle, 
  1.1070 +			CK_OBJECT_HANDLE id, SECItem *newKey)
  1.1071 +{
  1.1072 +    CK_RV crv = CKR_OK;
  1.1073 +    PLArenaPool *arena = NULL;
  1.1074 +
  1.1075 +    /* get a new arena to simplify cleanup */
  1.1076 +    arena = PORT_NewArena(1024);
  1.1077 +    if (!arena) {
  1.1078 +	return CKR_HOST_MEMORY;
  1.1079 +    }
  1.1080 +
  1.1081 +    /*
  1.1082 +     * first handle the MACS
  1.1083 +     */
  1.1084 +    crv = sftk_updateMacs(arena, handle, id, newKey);
  1.1085 +    if (crv != CKR_OK) {
  1.1086 +	goto loser;
  1.1087 +    }
  1.1088 +
  1.1089 +    if (handle->type == SFTK_KEYDB_TYPE) {
  1.1090 +	crv = sftk_updateEncrypted(arena, handle, id, newKey);
  1.1091 +	if (crv != CKR_OK) {
  1.1092 +	    goto loser;
  1.1093 +	}
  1.1094 +    }
  1.1095 +
  1.1096 +    /* free up our mess */
  1.1097 +    /* NOTE: at this point we know we've cleared out any unencrypted data */
  1.1098 +    PORT_FreeArena(arena, PR_FALSE);
  1.1099 +    return CKR_OK;
  1.1100 +
  1.1101 +loser:
  1.1102 +    /* there may be unencrypted data, clear it out down */
  1.1103 +    PORT_FreeArena(arena, PR_TRUE);
  1.1104 +    return crv;
  1.1105 +}
  1.1106 +
  1.1107 +
  1.1108 +/*
  1.1109 + * must be called with the old key active.
  1.1110 + */
  1.1111 +CK_RV
  1.1112 +sftkdb_convertObjects(SFTKDBHandle *handle, CK_ATTRIBUTE *template, 
  1.1113 +			CK_ULONG count, SECItem *newKey)
  1.1114 +{
  1.1115 +    SDBFind *find = NULL;
  1.1116 +    CK_ULONG idCount = SFTK_MAX_IDS;
  1.1117 +    CK_OBJECT_HANDLE ids[SFTK_MAX_IDS];
  1.1118 +    CK_RV crv, crv2;
  1.1119 +    int i;
  1.1120 +
  1.1121 +    crv = sftkdb_FindObjectsInit(handle, template, count, &find);
  1.1122 +
  1.1123 +    if (crv != CKR_OK) {
  1.1124 +	return crv;
  1.1125 +    }
  1.1126 +    while ((crv == CKR_OK) && (idCount == SFTK_MAX_IDS)) {
  1.1127 +	crv = sftkdb_FindObjects(handle, find, ids, SFTK_MAX_IDS, &idCount);
  1.1128 +	for (i=0; (crv == CKR_OK) && (i < idCount); i++) {
  1.1129 +	    crv = sftk_convertAttributes(handle, ids[i], newKey);
  1.1130 +	}
  1.1131 +    }
  1.1132 +    crv2 = sftkdb_FindObjectsFinal(handle, find);
  1.1133 +    if (crv == CKR_OK) crv = crv2;
  1.1134 +
  1.1135 +    return crv;
  1.1136 +}
  1.1137 +
  1.1138 +
  1.1139 +/*
  1.1140 + * change the database password.
  1.1141 + */
  1.1142 +SECStatus
  1.1143 +sftkdb_ChangePassword(SFTKDBHandle *keydb, 
  1.1144 +                      char *oldPin, char *newPin, PRBool *tokenRemoved)
  1.1145 +{
  1.1146 +    SECStatus rv = SECSuccess;
  1.1147 +    SECItem plainText;
  1.1148 +    SECItem newKey;
  1.1149 +    SECItem *result = NULL;
  1.1150 +    SECItem salt, value;
  1.1151 +    SFTKDBHandle *certdb;
  1.1152 +    unsigned char saltData[SDB_MAX_META_DATA_LEN];
  1.1153 +    unsigned char valueData[SDB_MAX_META_DATA_LEN];
  1.1154 +    CK_RV crv;
  1.1155 +    SDB *db;
  1.1156 +
  1.1157 +    if (keydb == NULL) {
  1.1158 +	return SECFailure;
  1.1159 +    }
  1.1160 +
  1.1161 +    db = SFTK_GET_SDB(keydb);
  1.1162 +    if (db == NULL) {
  1.1163 +	return SECFailure;
  1.1164 +    }
  1.1165 +
  1.1166 +    newKey.data = NULL;
  1.1167 +
  1.1168 +    /* make sure we have a valid old pin */
  1.1169 +    crv = (*keydb->db->sdb_Begin)(keydb->db);
  1.1170 +    if (crv != CKR_OK) {
  1.1171 +	rv = SECFailure;
  1.1172 +	goto loser;
  1.1173 +    }
  1.1174 +    salt.data = saltData;
  1.1175 +    salt.len = sizeof(saltData);
  1.1176 +    value.data = valueData;
  1.1177 +    value.len = sizeof(valueData);
  1.1178 +    crv = (*db->sdb_GetMetaData)(db, "password", &salt, &value);
  1.1179 +    if (crv == CKR_OK) {
  1.1180 +	rv = sftkdb_CheckPassword(keydb, oldPin, tokenRemoved);
  1.1181 +	if (rv == SECFailure) {
  1.1182 +	    goto loser;
  1.1183 +	}
  1.1184 +    } else {
  1.1185 +	salt.len = SHA1_LENGTH;
  1.1186 +    	RNG_GenerateGlobalRandomBytes(salt.data,salt.len);
  1.1187 +    }
  1.1188 +
  1.1189 +    rv = sftkdb_passwordToKey(keydb, &salt, newPin, &newKey);
  1.1190 +    if (rv != SECSuccess) {
  1.1191 +	goto loser;
  1.1192 +    }
  1.1193 +
  1.1194 +
  1.1195 +    /*
  1.1196 +     * convert encrypted entries here.
  1.1197 +     */
  1.1198 +    crv = sftkdb_convertObjects(keydb, NULL, 0, &newKey);
  1.1199 +    if (crv != CKR_OK) {
  1.1200 +	rv = SECFailure;
  1.1201 +	goto loser;
  1.1202 +    }
  1.1203 +    /* fix up certdb macs */
  1.1204 +    certdb = keydb->peerDB;
  1.1205 +    if (certdb) {
  1.1206 +	CK_ATTRIBUTE objectType = { CKA_CLASS, 0, sizeof(CK_OBJECT_CLASS) };
  1.1207 +	CK_OBJECT_CLASS myClass = CKO_NETSCAPE_TRUST;
  1.1208 +
  1.1209 +	objectType.pValue = &myClass;
  1.1210 +	crv = sftkdb_convertObjects(certdb, &objectType, 1, &newKey);
  1.1211 +	if (crv != CKR_OK) {
  1.1212 +	    rv = SECFailure;
  1.1213 +	    goto loser;
  1.1214 +	}
  1.1215 +	myClass = CKO_PUBLIC_KEY;
  1.1216 +	crv = sftkdb_convertObjects(certdb, &objectType, 1, &newKey);
  1.1217 +	if (crv != CKR_OK) {
  1.1218 +	    rv = SECFailure;
  1.1219 +	    goto loser;
  1.1220 +	}
  1.1221 +    }
  1.1222 +
  1.1223 +
  1.1224 +    plainText.data = (unsigned char *)SFTK_PW_CHECK_STRING;
  1.1225 +    plainText.len = SFTK_PW_CHECK_LEN;
  1.1226 +
  1.1227 +    rv = sftkdb_EncryptAttribute(NULL, &newKey, &plainText, &result);
  1.1228 +    if (rv != SECSuccess) {
  1.1229 +	goto loser;
  1.1230 +    }
  1.1231 +    value.data = result->data;
  1.1232 +    value.len = result->len;
  1.1233 +    crv = (*keydb->db->sdb_PutMetaData)(keydb->db, "password", &salt, &value);
  1.1234 +    if (crv != CKR_OK) {
  1.1235 +	rv = SECFailure;
  1.1236 +	goto loser;
  1.1237 +    }
  1.1238 +    crv = (*keydb->db->sdb_Commit)(keydb->db);
  1.1239 +    if (crv != CKR_OK) {
  1.1240 +	rv = SECFailure;
  1.1241 +	goto loser;
  1.1242 +    }
  1.1243 +
  1.1244 +    keydb->newKey = NULL;
  1.1245 +
  1.1246 +    sftkdb_switchKeys(keydb, &newKey);
  1.1247 +
  1.1248 +loser:
  1.1249 +    if (newKey.data) {
  1.1250 +	PORT_ZFree(newKey.data,newKey.len);
  1.1251 +    }
  1.1252 +    if (result) {
  1.1253 +	SECITEM_FreeItem(result, PR_FALSE);
  1.1254 +    }
  1.1255 +    if (rv != SECSuccess) {
  1.1256 +        (*keydb->db->sdb_Abort)(keydb->db);
  1.1257 +    }
  1.1258 +    
  1.1259 +    return rv;
  1.1260 +}
  1.1261 +
  1.1262 +/*
  1.1263 + * lose our cached password
  1.1264 + */
  1.1265 +SECStatus
  1.1266 +sftkdb_ClearPassword(SFTKDBHandle *keydb)
  1.1267 +{
  1.1268 +    SECItem oldKey;
  1.1269 +    oldKey.data = NULL;
  1.1270 +    oldKey.len = 0;
  1.1271 +    sftkdb_switchKeys(keydb, &oldKey);
  1.1272 +    if (oldKey.data) {
  1.1273 +	PORT_ZFree(oldKey.data, oldKey.len);
  1.1274 +    }
  1.1275 +    return SECSuccess;
  1.1276 +}
  1.1277 +
  1.1278 +

mercurial