security/nss/lib/softoken/sftkpwd.c

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

michael@0 1 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 2 * License, v. 2.0. If a copy of the MPL was not distributed with this
michael@0 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 4 /*
michael@0 5 * The following code handles the storage of PKCS 11 modules used by the
michael@0 6 * NSS. For the rest of NSS, only one kind of database handle exists:
michael@0 7 *
michael@0 8 * SFTKDBHandle
michael@0 9 *
michael@0 10 * There is one SFTKDBHandle for the each key database and one for each cert
michael@0 11 * database. These databases are opened as associated pairs, one pair per
michael@0 12 * slot. SFTKDBHandles are reference counted objects.
michael@0 13 *
michael@0 14 * Each SFTKDBHandle points to a low level database handle (SDB). This handle
michael@0 15 * represents the underlying physical database. These objects are not
michael@0 16 * reference counted, an are 'owned' by their respective SFTKDBHandles.
michael@0 17 *
michael@0 18 *
michael@0 19 */
michael@0 20 #include "sftkdb.h"
michael@0 21 #include "sftkdbti.h"
michael@0 22 #include "pkcs11t.h"
michael@0 23 #include "pkcs11i.h"
michael@0 24 #include "sdb.h"
michael@0 25 #include "prprf.h"
michael@0 26 #include "secasn1.h"
michael@0 27 #include "pratom.h"
michael@0 28 #include "blapi.h"
michael@0 29 #include "secoid.h"
michael@0 30 #include "lowpbe.h"
michael@0 31 #include "secdert.h"
michael@0 32 #include "prsystem.h"
michael@0 33 #include "lgglue.h"
michael@0 34 #include "secerr.h"
michael@0 35 #include "softoken.h"
michael@0 36
michael@0 37 /******************************************************************
michael@0 38 *
michael@0 39 * Key DB password handling functions
michael@0 40 *
michael@0 41 * These functions manage the key db password (set, reset, initialize, use).
michael@0 42 *
michael@0 43 * The key is managed on 'this side' of the database. All private data is
michael@0 44 * encrypted before it is sent to the database itself. Besides PBE's, the
michael@0 45 * database management code can also mix in various fixed keys so the data
michael@0 46 * in the database is no longer considered 'plain text'.
michael@0 47 */
michael@0 48
michael@0 49
michael@0 50 /* take string password and turn it into a key. The key is dependent
michael@0 51 * on a global salt entry acquired from the database. This salted
michael@0 52 * value will be based to a pkcs5 pbe function before it is used
michael@0 53 * in an actual encryption */
michael@0 54 static SECStatus
michael@0 55 sftkdb_passwordToKey(SFTKDBHandle *keydb, SECItem *salt,
michael@0 56 const char *pw, SECItem *key)
michael@0 57 {
michael@0 58 SHA1Context *cx = NULL;
michael@0 59 SECStatus rv = SECFailure;
michael@0 60
michael@0 61 key->data = PORT_Alloc(SHA1_LENGTH);
michael@0 62 if (key->data == NULL) {
michael@0 63 goto loser;
michael@0 64 }
michael@0 65 key->len = SHA1_LENGTH;
michael@0 66
michael@0 67 cx = SHA1_NewContext();
michael@0 68 if ( cx == NULL) {
michael@0 69 goto loser;
michael@0 70 }
michael@0 71 SHA1_Begin(cx);
michael@0 72 if (salt && salt->data ) {
michael@0 73 SHA1_Update(cx, salt->data, salt->len);
michael@0 74 }
michael@0 75 SHA1_Update(cx, (unsigned char *)pw, PORT_Strlen(pw));
michael@0 76 SHA1_End(cx, key->data, &key->len, key->len);
michael@0 77 rv = SECSuccess;
michael@0 78
michael@0 79 loser:
michael@0 80 if (cx) {
michael@0 81 SHA1_DestroyContext(cx, PR_TRUE);
michael@0 82 }
michael@0 83 if (rv != SECSuccess) {
michael@0 84 if (key->data != NULL) {
michael@0 85 PORT_ZFree(key->data,key->len);
michael@0 86 }
michael@0 87 key->data = NULL;
michael@0 88 }
michael@0 89 return rv;
michael@0 90 }
michael@0 91
michael@0 92 /*
michael@0 93 * Cipher text stored in the database contains 3 elements:
michael@0 94 * 1) an identifier describing the encryption algorithm.
michael@0 95 * 2) an entry specific salt value.
michael@0 96 * 3) the encrypted value.
michael@0 97 *
michael@0 98 * The following data structure represents the encrypted data in a decoded
michael@0 99 * (but still encrypted) form.
michael@0 100 */
michael@0 101 typedef struct sftkCipherValueStr sftkCipherValue;
michael@0 102 struct sftkCipherValueStr {
michael@0 103 PLArenaPool *arena;
michael@0 104 SECOidTag alg;
michael@0 105 NSSPKCS5PBEParameter *param;
michael@0 106 SECItem salt;
michael@0 107 SECItem value;
michael@0 108 };
michael@0 109
michael@0 110 #define SFTK_CIPHERTEXT_VERSION 3
michael@0 111
michael@0 112 struct SFTKDBEncryptedDataInfoStr {
michael@0 113 SECAlgorithmID algorithm;
michael@0 114 SECItem encryptedData;
michael@0 115 };
michael@0 116 typedef struct SFTKDBEncryptedDataInfoStr SFTKDBEncryptedDataInfo;
michael@0 117
michael@0 118 SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
michael@0 119
michael@0 120 const SEC_ASN1Template sftkdb_EncryptedDataInfoTemplate[] = {
michael@0 121 { SEC_ASN1_SEQUENCE,
michael@0 122 0, NULL, sizeof(SFTKDBEncryptedDataInfo) },
michael@0 123 { SEC_ASN1_INLINE | SEC_ASN1_XTRN ,
michael@0 124 offsetof(SFTKDBEncryptedDataInfo,algorithm),
michael@0 125 SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
michael@0 126 { SEC_ASN1_OCTET_STRING,
michael@0 127 offsetof(SFTKDBEncryptedDataInfo,encryptedData) },
michael@0 128 { 0 }
michael@0 129 };
michael@0 130
michael@0 131 /*
michael@0 132 * This parses the cipherText into cipher value. NOTE: cipherValue will point
michael@0 133 * to data in cipherText, if cipherText is freed, cipherValue will be invalid.
michael@0 134 */
michael@0 135 static SECStatus
michael@0 136 sftkdb_decodeCipherText(SECItem *cipherText, sftkCipherValue *cipherValue)
michael@0 137 {
michael@0 138 PLArenaPool *arena = NULL;
michael@0 139 SFTKDBEncryptedDataInfo edi;
michael@0 140 SECStatus rv;
michael@0 141
michael@0 142 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
michael@0 143 if (arena == NULL) {
michael@0 144 return SECFailure;
michael@0 145 }
michael@0 146 cipherValue->arena = NULL;
michael@0 147 cipherValue->param = NULL;
michael@0 148
michael@0 149 rv = SEC_QuickDERDecodeItem(arena, &edi, sftkdb_EncryptedDataInfoTemplate,
michael@0 150 cipherText);
michael@0 151 if (rv != SECSuccess) {
michael@0 152 goto loser;
michael@0 153 }
michael@0 154 cipherValue->alg = SECOID_GetAlgorithmTag(&edi.algorithm);
michael@0 155 cipherValue->param = nsspkcs5_AlgidToParam(&edi.algorithm);
michael@0 156 if (cipherValue->param == NULL) {
michael@0 157 goto loser;
michael@0 158 }
michael@0 159 cipherValue->value = edi.encryptedData;
michael@0 160 cipherValue->arena = arena;
michael@0 161
michael@0 162 return SECSuccess;
michael@0 163 loser:
michael@0 164 if (cipherValue->param) {
michael@0 165 nsspkcs5_DestroyPBEParameter(cipherValue->param);
michael@0 166 cipherValue->param = NULL;
michael@0 167 }
michael@0 168 if (arena) {
michael@0 169 PORT_FreeArena(arena,PR_FALSE);
michael@0 170 }
michael@0 171 return SECFailure;
michael@0 172 }
michael@0 173
michael@0 174
michael@0 175
michael@0 176 /*
michael@0 177 * unlike decode, Encode actually allocates a SECItem the caller must free
michael@0 178 * The caller can pass an optional arena to to indicate where to place
michael@0 179 * the resultant cipherText.
michael@0 180 */
michael@0 181 static SECStatus
michael@0 182 sftkdb_encodeCipherText(PLArenaPool *arena, sftkCipherValue *cipherValue,
michael@0 183 SECItem **cipherText)
michael@0 184 {
michael@0 185 SFTKDBEncryptedDataInfo edi;
michael@0 186 SECAlgorithmID *algid;
michael@0 187 SECStatus rv;
michael@0 188 PLArenaPool *localArena = NULL;
michael@0 189
michael@0 190
michael@0 191 localArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
michael@0 192 if (localArena == NULL) {
michael@0 193 return SECFailure;
michael@0 194 }
michael@0 195
michael@0 196 algid = nsspkcs5_CreateAlgorithmID(localArena, cipherValue->alg,
michael@0 197 cipherValue->param);
michael@0 198 if (algid == NULL) {
michael@0 199 rv = SECFailure;
michael@0 200 goto loser;
michael@0 201 }
michael@0 202 rv = SECOID_CopyAlgorithmID(localArena, &edi.algorithm, algid);
michael@0 203 SECOID_DestroyAlgorithmID(algid, PR_TRUE);
michael@0 204 if (rv != SECSuccess) {
michael@0 205 goto loser;
michael@0 206 }
michael@0 207 edi.encryptedData = cipherValue->value;
michael@0 208
michael@0 209 *cipherText = SEC_ASN1EncodeItem(arena, NULL, &edi,
michael@0 210 sftkdb_EncryptedDataInfoTemplate);
michael@0 211 if (*cipherText == NULL) {
michael@0 212 rv = SECFailure;
michael@0 213 }
michael@0 214
michael@0 215 loser:
michael@0 216 if (localArena) {
michael@0 217 PORT_FreeArena(localArena,PR_FALSE);
michael@0 218 }
michael@0 219
michael@0 220 return rv;
michael@0 221 }
michael@0 222
michael@0 223
michael@0 224 /*
michael@0 225 * Use our key to decode a cipherText block from the database.
michael@0 226 *
michael@0 227 * plain text is allocated by nsspkcs5_CipherData and must be freed
michael@0 228 * with SECITEM_FreeItem by the caller.
michael@0 229 */
michael@0 230 SECStatus
michael@0 231 sftkdb_DecryptAttribute(SECItem *passKey, SECItem *cipherText, SECItem **plain)
michael@0 232 {
michael@0 233 SECStatus rv;
michael@0 234 sftkCipherValue cipherValue;
michael@0 235
michael@0 236 /* First get the cipher type */
michael@0 237 rv = sftkdb_decodeCipherText(cipherText, &cipherValue);
michael@0 238 if (rv != SECSuccess) {
michael@0 239 goto loser;
michael@0 240 }
michael@0 241
michael@0 242 *plain = nsspkcs5_CipherData(cipherValue.param, passKey, &cipherValue.value,
michael@0 243 PR_FALSE, NULL);
michael@0 244 if (*plain == NULL) {
michael@0 245 rv = SECFailure;
michael@0 246 goto loser;
michael@0 247 }
michael@0 248
michael@0 249 loser:
michael@0 250 if (cipherValue.param) {
michael@0 251 nsspkcs5_DestroyPBEParameter(cipherValue.param);
michael@0 252 }
michael@0 253 if (cipherValue.arena) {
michael@0 254 PORT_FreeArena(cipherValue.arena,PR_FALSE);
michael@0 255 }
michael@0 256 return rv;
michael@0 257 }
michael@0 258
michael@0 259 /*
michael@0 260 * encrypt a block. This function returned the encrypted ciphertext which
michael@0 261 * the caller must free. If the caller provides an arena, cipherText will
michael@0 262 * be allocated out of that arena. This also generated the per entry
michael@0 263 * salt automatically.
michael@0 264 */
michael@0 265 SECStatus
michael@0 266 sftkdb_EncryptAttribute(PLArenaPool *arena, SECItem *passKey,
michael@0 267 SECItem *plainText, SECItem **cipherText)
michael@0 268 {
michael@0 269 SECStatus rv;
michael@0 270 sftkCipherValue cipherValue;
michael@0 271 SECItem *cipher = NULL;
michael@0 272 NSSPKCS5PBEParameter *param = NULL;
michael@0 273 unsigned char saltData[HASH_LENGTH_MAX];
michael@0 274
michael@0 275 cipherValue.alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC;
michael@0 276 cipherValue.salt.len = SHA1_LENGTH;
michael@0 277 cipherValue.salt.data = saltData;
michael@0 278 RNG_GenerateGlobalRandomBytes(saltData,cipherValue.salt.len);
michael@0 279
michael@0 280 param = nsspkcs5_NewParam(cipherValue.alg, &cipherValue.salt, 1);
michael@0 281 if (param == NULL) {
michael@0 282 rv = SECFailure;
michael@0 283 goto loser;
michael@0 284 }
michael@0 285 cipher = nsspkcs5_CipherData(param, passKey, plainText, PR_TRUE, NULL);
michael@0 286 if (cipher == NULL) {
michael@0 287 rv = SECFailure;
michael@0 288 goto loser;
michael@0 289 }
michael@0 290 cipherValue.value = *cipher;
michael@0 291 cipherValue.param = param;
michael@0 292
michael@0 293 rv = sftkdb_encodeCipherText(arena, &cipherValue, cipherText);
michael@0 294 if (rv != SECSuccess) {
michael@0 295 goto loser;
michael@0 296 }
michael@0 297
michael@0 298 loser:
michael@0 299 if (cipher) {
michael@0 300 SECITEM_FreeItem(cipher, PR_TRUE);
michael@0 301 }
michael@0 302 if (param) {
michael@0 303 nsspkcs5_DestroyPBEParameter(param);
michael@0 304 }
michael@0 305 return rv;
michael@0 306 }
michael@0 307
michael@0 308 /*
michael@0 309 * use the password and the pbe parameters to generate an HMAC for the
michael@0 310 * given plain text data. This is used by sftkdb_VerifyAttribute and
michael@0 311 * sftkdb_SignAttribute. Signature is returned in signData. The caller
michael@0 312 * must preallocate the space in the secitem.
michael@0 313 */
michael@0 314 static SECStatus
michael@0 315 sftkdb_pbehash(SECOidTag sigOid, SECItem *passKey,
michael@0 316 NSSPKCS5PBEParameter *param,
michael@0 317 CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE attrType,
michael@0 318 SECItem *plainText, SECItem *signData)
michael@0 319 {
michael@0 320 SECStatus rv = SECFailure;
michael@0 321 SECItem *key = NULL;
michael@0 322 HMACContext *hashCx = NULL;
michael@0 323 HASH_HashType hashType = HASH_AlgNULL;
michael@0 324 const SECHashObject *hashObj;
michael@0 325 unsigned char addressData[SDB_ULONG_SIZE];
michael@0 326
michael@0 327 hashType = HASH_FromHMACOid(param->encAlg);
michael@0 328 if (hashType == HASH_AlgNULL) {
michael@0 329 PORT_SetError(SEC_ERROR_INVALID_ALGORITHM);
michael@0 330 return SECFailure;
michael@0 331 }
michael@0 332
michael@0 333 hashObj = HASH_GetRawHashObject(hashType);
michael@0 334 if (hashObj == NULL) {
michael@0 335 goto loser;
michael@0 336 }
michael@0 337
michael@0 338 key = nsspkcs5_ComputeKeyAndIV(param, passKey, NULL, PR_FALSE);
michael@0 339 if (!key) {
michael@0 340 goto loser;
michael@0 341 }
michael@0 342
michael@0 343 hashCx = HMAC_Create(hashObj, key->data, key->len, PR_TRUE);
michael@0 344 if (!hashCx) {
michael@0 345 goto loser;
michael@0 346 }
michael@0 347 HMAC_Begin(hashCx);
michael@0 348 /* Tie this value to a particular object. This is most important for
michael@0 349 * the trust attributes, where and attacker could copy a value for
michael@0 350 * 'validCA' from another cert in the database */
michael@0 351 sftk_ULong2SDBULong(addressData, objectID);
michael@0 352 HMAC_Update(hashCx, addressData, SDB_ULONG_SIZE);
michael@0 353 sftk_ULong2SDBULong(addressData, attrType);
michael@0 354 HMAC_Update(hashCx, addressData, SDB_ULONG_SIZE);
michael@0 355
michael@0 356 HMAC_Update(hashCx, plainText->data, plainText->len);
michael@0 357 rv = HMAC_Finish(hashCx, signData->data, &signData->len, signData->len);
michael@0 358
michael@0 359 loser:
michael@0 360 if (hashCx) {
michael@0 361 HMAC_Destroy(hashCx, PR_TRUE);
michael@0 362 }
michael@0 363 if (key) {
michael@0 364 SECITEM_FreeItem(key,PR_TRUE);
michael@0 365 }
michael@0 366 return rv;
michael@0 367 }
michael@0 368
michael@0 369 /*
michael@0 370 * Use our key to verify a signText block from the database matches
michael@0 371 * the plainText from the database. The signText is a PKCS 5 v2 pbe.
michael@0 372 * plainText is the plainText of the attribute.
michael@0 373 */
michael@0 374 SECStatus
michael@0 375 sftkdb_VerifyAttribute(SECItem *passKey, CK_OBJECT_HANDLE objectID,
michael@0 376 CK_ATTRIBUTE_TYPE attrType,
michael@0 377 SECItem *plainText, SECItem *signText)
michael@0 378 {
michael@0 379 SECStatus rv;
michael@0 380 sftkCipherValue signValue;
michael@0 381 SECItem signature;
michael@0 382 unsigned char signData[HASH_LENGTH_MAX];
michael@0 383
michael@0 384
michael@0 385 /* First get the cipher type */
michael@0 386 rv = sftkdb_decodeCipherText(signText, &signValue);
michael@0 387 if (rv != SECSuccess) {
michael@0 388 goto loser;
michael@0 389 }
michael@0 390 signature.data = signData;
michael@0 391 signature.len = sizeof(signData);
michael@0 392
michael@0 393 rv = sftkdb_pbehash(signValue.alg, passKey, signValue.param,
michael@0 394 objectID, attrType, plainText, &signature);
michael@0 395 if (rv != SECSuccess) {
michael@0 396 goto loser;
michael@0 397 }
michael@0 398 if (SECITEM_CompareItem(&signValue.value,&signature) != 0) {
michael@0 399 PORT_SetError(SEC_ERROR_BAD_SIGNATURE);
michael@0 400 rv = SECFailure;
michael@0 401 }
michael@0 402
michael@0 403 loser:
michael@0 404 if (signValue.param) {
michael@0 405 nsspkcs5_DestroyPBEParameter(signValue.param);
michael@0 406 }
michael@0 407 if (signValue.arena) {
michael@0 408 PORT_FreeArena(signValue.arena,PR_FALSE);
michael@0 409 }
michael@0 410 return rv;
michael@0 411 }
michael@0 412
michael@0 413 /*
michael@0 414 * Use our key to create a signText block the plain text of an
michael@0 415 * attribute. The signText is a PKCS 5 v2 pbe.
michael@0 416 */
michael@0 417 SECStatus
michael@0 418 sftkdb_SignAttribute(PLArenaPool *arena, SECItem *passKey,
michael@0 419 CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE attrType,
michael@0 420 SECItem *plainText, SECItem **signature)
michael@0 421 {
michael@0 422 SECStatus rv;
michael@0 423 sftkCipherValue signValue;
michael@0 424 NSSPKCS5PBEParameter *param = NULL;
michael@0 425 unsigned char saltData[HASH_LENGTH_MAX];
michael@0 426 unsigned char signData[HASH_LENGTH_MAX];
michael@0 427 SECOidTag hmacAlg = SEC_OID_HMAC_SHA256; /* hash for authentication */
michael@0 428 SECOidTag prfAlg = SEC_OID_HMAC_SHA256; /* hash for pb key generation */
michael@0 429 HASH_HashType prfType;
michael@0 430 unsigned int hmacLength;
michael@0 431 unsigned int prfLength;
michael@0 432
michael@0 433 /* this code allows us to fetch the lengths and hashes on the fly
michael@0 434 * by simply changing the OID above */
michael@0 435 prfType = HASH_FromHMACOid(prfAlg);
michael@0 436 PORT_Assert(prfType != HASH_AlgNULL);
michael@0 437 prfLength = HASH_GetRawHashObject(prfType)->length;
michael@0 438 PORT_Assert(prfLength <= HASH_LENGTH_MAX);
michael@0 439
michael@0 440 hmacLength = HASH_GetRawHashObject(HASH_FromHMACOid(hmacAlg))->length;
michael@0 441 PORT_Assert(hmacLength <= HASH_LENGTH_MAX);
michael@0 442
michael@0 443 /* initialize our CipherValue structure */
michael@0 444 signValue.alg = SEC_OID_PKCS5_PBMAC1;
michael@0 445 signValue.salt.len = prfLength;
michael@0 446 signValue.salt.data = saltData;
michael@0 447 signValue.value.data = signData;
michael@0 448 signValue.value.len = hmacLength;
michael@0 449 RNG_GenerateGlobalRandomBytes(saltData,prfLength);
michael@0 450
michael@0 451 /* initialize our pkcs5 parameter */
michael@0 452 param = nsspkcs5_NewParam(signValue.alg, &signValue.salt, 1);
michael@0 453 if (param == NULL) {
michael@0 454 rv = SECFailure;
michael@0 455 goto loser;
michael@0 456 }
michael@0 457 param->keyID = pbeBitGenIntegrityKey;
michael@0 458 /* set the PKCS 5 v2 parameters, not extractable from the
michael@0 459 * data passed into nsspkcs5_NewParam */
michael@0 460 param->encAlg = hmacAlg;
michael@0 461 param->hashType = prfType;
michael@0 462 param->keyLen = hmacLength;
michael@0 463 rv = SECOID_SetAlgorithmID(param->poolp, &param->prfAlg, prfAlg, NULL);
michael@0 464 if (rv != SECSuccess) {
michael@0 465 goto loser;
michael@0 466 }
michael@0 467
michael@0 468
michael@0 469 /* calculate the mac */
michael@0 470 rv = sftkdb_pbehash(signValue.alg, passKey, param, objectID, attrType,
michael@0 471 plainText, &signValue.value);
michael@0 472 if (rv != SECSuccess) {
michael@0 473 goto loser;
michael@0 474 }
michael@0 475 signValue.param = param;
michael@0 476
michael@0 477 /* write it out */
michael@0 478 rv = sftkdb_encodeCipherText(arena, &signValue, signature);
michael@0 479 if (rv != SECSuccess) {
michael@0 480 goto loser;
michael@0 481 }
michael@0 482
michael@0 483 loser:
michael@0 484 if (param) {
michael@0 485 nsspkcs5_DestroyPBEParameter(param);
michael@0 486 }
michael@0 487 return rv;
michael@0 488 }
michael@0 489
michael@0 490 /*
michael@0 491 * safely swith the passed in key for the one caches in the keydb handle
michael@0 492 *
michael@0 493 * A key attached to the handle tells us the the token is logged in.
michael@0 494 * We can used the key attached to the handle in sftkdb_EncryptAttribute
michael@0 495 * and sftkdb_DecryptAttribute calls.
michael@0 496 */
michael@0 497 static void
michael@0 498 sftkdb_switchKeys(SFTKDBHandle *keydb, SECItem *passKey)
michael@0 499 {
michael@0 500 unsigned char *data;
michael@0 501 int len;
michael@0 502
michael@0 503 if (keydb->passwordLock == NULL) {
michael@0 504 PORT_Assert(keydb->type != SFTK_KEYDB_TYPE);
michael@0 505 return;
michael@0 506 }
michael@0 507
michael@0 508 /* an atomic pointer set would be nice */
michael@0 509 SKIP_AFTER_FORK(PZ_Lock(keydb->passwordLock));
michael@0 510 data = keydb->passwordKey.data;
michael@0 511 len = keydb->passwordKey.len;
michael@0 512 keydb->passwordKey.data = passKey->data;
michael@0 513 keydb->passwordKey.len = passKey->len;
michael@0 514 passKey->data = data;
michael@0 515 passKey->len = len;
michael@0 516 SKIP_AFTER_FORK(PZ_Unlock(keydb->passwordLock));
michael@0 517 }
michael@0 518
michael@0 519 /*
michael@0 520 * returns true if we are in a middle of a merge style update.
michael@0 521 */
michael@0 522 PRBool
michael@0 523 sftkdb_InUpdateMerge(SFTKDBHandle *keydb)
michael@0 524 {
michael@0 525 return keydb->updateID ? PR_TRUE : PR_FALSE;
michael@0 526 }
michael@0 527
michael@0 528 /*
michael@0 529 * returns true if we are looking for the password for the user's old source
michael@0 530 * database as part of a merge style update.
michael@0 531 */
michael@0 532 PRBool
michael@0 533 sftkdb_NeedUpdateDBPassword(SFTKDBHandle *keydb)
michael@0 534 {
michael@0 535 if (!sftkdb_InUpdateMerge(keydb)) {
michael@0 536 return PR_FALSE;
michael@0 537 }
michael@0 538 if (keydb->updateDBIsInit && !keydb->updatePasswordKey) {
michael@0 539 return PR_TRUE;
michael@0 540 }
michael@0 541 return PR_FALSE;
michael@0 542 }
michael@0 543
michael@0 544 /*
michael@0 545 * fetch an update password key from a handle.
michael@0 546 */
michael@0 547 SECItem *
michael@0 548 sftkdb_GetUpdatePasswordKey(SFTKDBHandle *handle)
michael@0 549 {
michael@0 550 SECItem *key = NULL;
michael@0 551
michael@0 552 /* if we're a cert db, fetch it from our peer key db */
michael@0 553 if (handle->type == SFTK_CERTDB_TYPE) {
michael@0 554 handle = handle->peerDB;
michael@0 555 }
michael@0 556
michael@0 557 /* don't have one */
michael@0 558 if (!handle) {
michael@0 559 return NULL;
michael@0 560 }
michael@0 561
michael@0 562 PZ_Lock(handle->passwordLock);
michael@0 563 if (handle->updatePasswordKey) {
michael@0 564 key = SECITEM_DupItem(handle->updatePasswordKey);
michael@0 565 }
michael@0 566 PZ_Unlock(handle->passwordLock);
michael@0 567
michael@0 568 return key;
michael@0 569 }
michael@0 570
michael@0 571 /*
michael@0 572 * free the update password key from a handle.
michael@0 573 */
michael@0 574 void
michael@0 575 sftkdb_FreeUpdatePasswordKey(SFTKDBHandle *handle)
michael@0 576 {
michael@0 577 SECItem *key = NULL;
michael@0 578
michael@0 579 /* don't have one */
michael@0 580 if (!handle) {
michael@0 581 return;
michael@0 582 }
michael@0 583
michael@0 584 /* if we're a cert db, we don't have one */
michael@0 585 if (handle->type == SFTK_CERTDB_TYPE) {
michael@0 586 return;
michael@0 587 }
michael@0 588
michael@0 589 PZ_Lock(handle->passwordLock);
michael@0 590 if (handle->updatePasswordKey) {
michael@0 591 key = handle->updatePasswordKey;
michael@0 592 handle->updatePasswordKey = NULL;
michael@0 593 }
michael@0 594 PZ_Unlock(handle->passwordLock);
michael@0 595
michael@0 596 if (key) {
michael@0 597 SECITEM_ZfreeItem(key, PR_TRUE);
michael@0 598 }
michael@0 599
michael@0 600 return;
michael@0 601 }
michael@0 602
michael@0 603 /*
michael@0 604 * what password db we use depends heavily on the update state machine
michael@0 605 *
michael@0 606 * 1) no update db, return the normal database.
michael@0 607 * 2) update db and no merge return the update db.
michael@0 608 * 3) update db and in merge:
michael@0 609 * return the update db if we need the update db's password,
michael@0 610 * otherwise return our normal datbase.
michael@0 611 */
michael@0 612 static SDB *
michael@0 613 sftk_getPWSDB(SFTKDBHandle *keydb)
michael@0 614 {
michael@0 615 if (!keydb->update) {
michael@0 616 return keydb->db;
michael@0 617 }
michael@0 618 if (!sftkdb_InUpdateMerge(keydb)) {
michael@0 619 return keydb->update;
michael@0 620 }
michael@0 621 if (sftkdb_NeedUpdateDBPassword(keydb)) {
michael@0 622 return keydb->update;
michael@0 623 }
michael@0 624 return keydb->db;
michael@0 625 }
michael@0 626
michael@0 627 /*
michael@0 628 * return success if we have a valid password entry.
michael@0 629 * This is will show up outside of PKCS #11 as CKF_USER_PIN_INIT
michael@0 630 * in the token flags.
michael@0 631 */
michael@0 632 SECStatus
michael@0 633 sftkdb_HasPasswordSet(SFTKDBHandle *keydb)
michael@0 634 {
michael@0 635 SECItem salt, value;
michael@0 636 unsigned char saltData[SDB_MAX_META_DATA_LEN];
michael@0 637 unsigned char valueData[SDB_MAX_META_DATA_LEN];
michael@0 638 CK_RV crv;
michael@0 639 SDB *db;
michael@0 640
michael@0 641 if (keydb == NULL) {
michael@0 642 return SECFailure;
michael@0 643 }
michael@0 644
michael@0 645 db = sftk_getPWSDB(keydb);
michael@0 646 if (db == NULL) {
michael@0 647 return SECFailure;
michael@0 648 }
michael@0 649
michael@0 650 salt.data = saltData;
michael@0 651 salt.len = sizeof(saltData);
michael@0 652 value.data = valueData;
michael@0 653 value.len = sizeof(valueData);
michael@0 654 crv = (*db->sdb_GetMetaData)(db, "password", &salt, &value);
michael@0 655
michael@0 656 /* If no password is set, we can update right away */
michael@0 657 if (((keydb->db->sdb_flags & SDB_RDONLY) == 0) && keydb->update
michael@0 658 && crv != CKR_OK) {
michael@0 659 /* update the peer certdb if it exists */
michael@0 660 if (keydb->peerDB) {
michael@0 661 sftkdb_Update(keydb->peerDB, NULL);
michael@0 662 }
michael@0 663 sftkdb_Update(keydb, NULL);
michael@0 664 }
michael@0 665 return (crv == CKR_OK) ? SECSuccess : SECFailure;
michael@0 666 }
michael@0 667
michael@0 668 #define SFTK_PW_CHECK_STRING "password-check"
michael@0 669 #define SFTK_PW_CHECK_LEN 14
michael@0 670
michael@0 671 /*
michael@0 672 * check if the supplied password is valid
michael@0 673 */
michael@0 674 SECStatus
michael@0 675 sftkdb_CheckPassword(SFTKDBHandle *keydb, const char *pw, PRBool *tokenRemoved)
michael@0 676 {
michael@0 677 SECStatus rv;
michael@0 678 SECItem salt, value;
michael@0 679 unsigned char saltData[SDB_MAX_META_DATA_LEN];
michael@0 680 unsigned char valueData[SDB_MAX_META_DATA_LEN];
michael@0 681 SECItem key;
michael@0 682 SECItem *result = NULL;
michael@0 683 SDB *db;
michael@0 684 CK_RV crv;
michael@0 685
michael@0 686 if (keydb == NULL) {
michael@0 687 return SECFailure;
michael@0 688 }
michael@0 689
michael@0 690 db = sftk_getPWSDB(keydb);
michael@0 691 if (db == NULL) {
michael@0 692 return SECFailure;
michael@0 693 }
michael@0 694
michael@0 695 key.data = NULL;
michael@0 696 key.len = 0;
michael@0 697
michael@0 698 if (pw == NULL) pw="";
michael@0 699
michael@0 700 /* get the entry from the database */
michael@0 701 salt.data = saltData;
michael@0 702 salt.len = sizeof(saltData);
michael@0 703 value.data = valueData;
michael@0 704 value.len = sizeof(valueData);
michael@0 705 crv = (*db->sdb_GetMetaData)(db, "password", &salt, &value);
michael@0 706 if (crv != CKR_OK) {
michael@0 707 rv = SECFailure;
michael@0 708 goto done;
michael@0 709 }
michael@0 710
michael@0 711 /* get our intermediate key based on the entry salt value */
michael@0 712 rv = sftkdb_passwordToKey(keydb, &salt, pw, &key);
michael@0 713 if (rv != SECSuccess) {
michael@0 714 goto done;
michael@0 715 }
michael@0 716
michael@0 717 /* decrypt the entry value */
michael@0 718 rv = sftkdb_DecryptAttribute(&key, &value, &result);
michael@0 719 if (rv != SECSuccess) {
michael@0 720 goto done;
michael@0 721 }
michael@0 722
michael@0 723 /* if it's what we expect, update our key in the database handle and
michael@0 724 * return Success */
michael@0 725 if ((result->len == SFTK_PW_CHECK_LEN) &&
michael@0 726 PORT_Memcmp(result->data, SFTK_PW_CHECK_STRING, SFTK_PW_CHECK_LEN) == 0){
michael@0 727 /*
michael@0 728 * We have a password, now lets handle any potential update cases..
michael@0 729 *
michael@0 730 * First, the normal case: no update. In this case we only need the
michael@0 731 * the password for our only DB, which we now have, we switch
michael@0 732 * the keys and fall through.
michael@0 733 * Second regular (non-merge) update: The target DB does not yet have
michael@0 734 * a password initialized, we now have the password for the source DB,
michael@0 735 * so we can switch the keys and simply update the target database.
michael@0 736 * Merge update case: This one is trickier.
michael@0 737 * 1) If we need the source DB password, then we just got it here.
michael@0 738 * We need to save that password,
michael@0 739 * then we need to check to see if we need or have the target
michael@0 740 * database password.
michael@0 741 * If we have it (it's the same as the source), or don't need
michael@0 742 * it (it's not set or is ""), we can start the update now.
michael@0 743 * If we don't have it, we need the application to get it from
michael@0 744 * the user. Clear our sessions out to simulate a token
michael@0 745 * removal. C_GetTokenInfo will change the token description
michael@0 746 * and the token will still appear to be logged out.
michael@0 747 * 2) If we already have the source DB password, this password is
michael@0 748 * for the target database. We can now move forward with the
michael@0 749 * update, as we now have both required passwords.
michael@0 750 *
michael@0 751 */
michael@0 752 PZ_Lock(keydb->passwordLock);
michael@0 753 if (sftkdb_NeedUpdateDBPassword(keydb)) {
michael@0 754 /* Squirrel this special key away.
michael@0 755 * This has the side effect of turning sftkdb_NeedLegacyPW off,
michael@0 756 * as well as changing which database is returned from
michael@0 757 * SFTK_GET_PW_DB (thus effecting both sftkdb_CheckPassword()
michael@0 758 * and sftkdb_HasPasswordSet()) */
michael@0 759 keydb->updatePasswordKey = SECITEM_DupItem(&key);
michael@0 760 PZ_Unlock(keydb->passwordLock);
michael@0 761 if (keydb->updatePasswordKey == NULL) {
michael@0 762 /* PORT_Error set by SECITEM_DupItem */
michael@0 763 rv = SECFailure;
michael@0 764 goto done;
michael@0 765 }
michael@0 766
michael@0 767 /* Simulate a token removal -- we need to do this any
michael@0 768 * any case at this point so the token name is correct. */
michael@0 769 *tokenRemoved = PR_TRUE;
michael@0 770
michael@0 771 /*
michael@0 772 * OK, we got the update DB password, see if we need a password
michael@0 773 * for the target...
michael@0 774 */
michael@0 775 if (sftkdb_HasPasswordSet(keydb) == SECSuccess) {
michael@0 776 /* We have a password, do we know what the password is?
michael@0 777 * check 1) for the password the user supplied for the
michael@0 778 * update DB,
michael@0 779 * and 2) for the null password.
michael@0 780 *
michael@0 781 * RECURSION NOTE: we are calling ourselves here. This means
michael@0 782 * any updates, switchKeys, etc will have been completed
michael@0 783 * if these functions return successfully, in those cases
michael@0 784 * just exit returning Success. We don't recurse infinitely
michael@0 785 * because we are making this call from a NeedUpdateDBPassword
michael@0 786 * block and we've already set that update password at this
michael@0 787 * point. */
michael@0 788 rv = sftkdb_CheckPassword(keydb, pw, tokenRemoved);
michael@0 789 if (rv == SECSuccess) {
michael@0 790 /* source and target databases have the same password, we
michael@0 791 * are good to go */
michael@0 792 goto done;
michael@0 793 }
michael@0 794 sftkdb_CheckPassword(keydb, "", tokenRemoved);
michael@0 795
michael@0 796 /*
michael@0 797 * Important 'NULL' code here. At this point either we
michael@0 798 * succeeded in logging in with "" or we didn't.
michael@0 799 *
michael@0 800 * If we did succeed at login, our machine state will be set
michael@0 801 * to logged in appropriately. The application will find that
michael@0 802 * it's logged in as soon as it opens a new session. We have
michael@0 803 * also completed the update. Life is good.
michael@0 804 *
michael@0 805 * If we did not succeed, well the user still successfully
michael@0 806 * logged into the update database, since we faked the token
michael@0 807 * removal it's just like the user logged into his smart card
michael@0 808 * then removed it. the actual login work, so we report that
michael@0 809 * success back to the user, but we won't actually be
michael@0 810 * logged in. The application will find this out when it
michael@0 811 * checks it's login state, thus triggering another password
michael@0 812 * prompt so we can get the real target DB password.
michael@0 813 *
michael@0 814 * summary, we exit from here with SECSuccess no matter what.
michael@0 815 */
michael@0 816 rv = SECSuccess;
michael@0 817 goto done;
michael@0 818 } else {
michael@0 819 /* there is no password, just fall through to update.
michael@0 820 * update will write the source DB's password record
michael@0 821 * into the target DB just like it would in a non-merge
michael@0 822 * update case. */
michael@0 823 }
michael@0 824 } else {
michael@0 825 PZ_Unlock(keydb->passwordLock);
michael@0 826 }
michael@0 827 /* load the keys, so the keydb can parse it's key set */
michael@0 828 sftkdb_switchKeys(keydb, &key);
michael@0 829
michael@0 830 /* we need to update, do it now */
michael@0 831 if (((keydb->db->sdb_flags & SDB_RDONLY) == 0) && keydb->update) {
michael@0 832 /* update the peer certdb if it exists */
michael@0 833 if (keydb->peerDB) {
michael@0 834 sftkdb_Update(keydb->peerDB, &key);
michael@0 835 }
michael@0 836 sftkdb_Update(keydb, &key);
michael@0 837 }
michael@0 838 } else {
michael@0 839 rv = SECFailure;
michael@0 840 /*PORT_SetError( bad password); */
michael@0 841 }
michael@0 842
michael@0 843 done:
michael@0 844 if (key.data) {
michael@0 845 PORT_ZFree(key.data,key.len);
michael@0 846 }
michael@0 847 if (result) {
michael@0 848 SECITEM_FreeItem(result,PR_TRUE);
michael@0 849 }
michael@0 850 return rv;
michael@0 851 }
michael@0 852
michael@0 853 /*
michael@0 854 * return Success if the there is a cached password key.
michael@0 855 */
michael@0 856 SECStatus
michael@0 857 sftkdb_PWCached(SFTKDBHandle *keydb)
michael@0 858 {
michael@0 859 return keydb->passwordKey.data ? SECSuccess : SECFailure;
michael@0 860 }
michael@0 861
michael@0 862
michael@0 863 static CK_RV
michael@0 864 sftk_updateMacs(PLArenaPool *arena, SFTKDBHandle *handle,
michael@0 865 CK_OBJECT_HANDLE id, SECItem *newKey)
michael@0 866 {
michael@0 867 CK_RV crv = CKR_OK;
michael@0 868 CK_RV crv2;
michael@0 869 CK_ATTRIBUTE authAttrs[] = {
michael@0 870 {CKA_MODULUS, NULL, 0},
michael@0 871 {CKA_PUBLIC_EXPONENT, NULL, 0},
michael@0 872 {CKA_CERT_SHA1_HASH, NULL, 0},
michael@0 873 {CKA_CERT_MD5_HASH, NULL, 0},
michael@0 874 {CKA_TRUST_SERVER_AUTH, NULL, 0},
michael@0 875 {CKA_TRUST_CLIENT_AUTH, NULL, 0},
michael@0 876 {CKA_TRUST_EMAIL_PROTECTION, NULL, 0},
michael@0 877 {CKA_TRUST_CODE_SIGNING, NULL, 0},
michael@0 878 {CKA_TRUST_STEP_UP_APPROVED, NULL, 0},
michael@0 879 {CKA_NSS_OVERRIDE_EXTENSIONS, NULL, 0},
michael@0 880 };
michael@0 881 CK_ULONG authAttrCount = sizeof(authAttrs)/sizeof(CK_ATTRIBUTE);
michael@0 882 int i, count;
michael@0 883 SFTKDBHandle *keyHandle = handle;
michael@0 884 SDB *keyTarget = NULL;
michael@0 885
michael@0 886 id &= SFTK_OBJ_ID_MASK;
michael@0 887
michael@0 888 if (handle->type != SFTK_KEYDB_TYPE) {
michael@0 889 keyHandle = handle->peerDB;
michael@0 890 }
michael@0 891
michael@0 892 if (keyHandle == NULL) {
michael@0 893 return CKR_OK;
michael@0 894 }
michael@0 895
michael@0 896 /* old DB's don't have meta data, finished with MACs */
michael@0 897 keyTarget = SFTK_GET_SDB(keyHandle);
michael@0 898 if ((keyTarget->sdb_flags &SDB_HAS_META) == 0) {
michael@0 899 return CKR_OK;
michael@0 900 }
michael@0 901
michael@0 902 /*
michael@0 903 * STEP 1: find the MACed attributes of this object
michael@0 904 */
michael@0 905 crv2 = sftkdb_GetAttributeValue(handle, id, authAttrs, authAttrCount);
michael@0 906 count = 0;
michael@0 907 /* allocate space for the attributes */
michael@0 908 for (i=0; i < authAttrCount; i++) {
michael@0 909 if ((authAttrs[i].ulValueLen == -1) || (authAttrs[i].ulValueLen == 0)){
michael@0 910 continue;
michael@0 911 }
michael@0 912 count++;
michael@0 913 authAttrs[i].pValue = PORT_ArenaAlloc(arena,authAttrs[i].ulValueLen);
michael@0 914 if (authAttrs[i].pValue == NULL) {
michael@0 915 crv = CKR_HOST_MEMORY;
michael@0 916 break;
michael@0 917 }
michael@0 918 }
michael@0 919
michael@0 920 /* if count was zero, none were found, finished with MACs */
michael@0 921 if (count == 0) {
michael@0 922 return CKR_OK;
michael@0 923 }
michael@0 924
michael@0 925 crv = sftkdb_GetAttributeValue(handle, id, authAttrs, authAttrCount);
michael@0 926 /* ignore error code, we expect some possible errors */
michael@0 927
michael@0 928 /* GetAttributeValue just verified the old macs, safe to write
michael@0 929 * them out then... */
michael@0 930 for (i=0; i < authAttrCount; i++) {
michael@0 931 SECItem *signText;
michael@0 932 SECItem plainText;
michael@0 933 SECStatus rv;
michael@0 934
michael@0 935 if ((authAttrs[i].ulValueLen == -1) || (authAttrs[i].ulValueLen == 0)){
michael@0 936 continue;
michael@0 937 }
michael@0 938
michael@0 939 plainText.data = authAttrs[i].pValue;
michael@0 940 plainText.len = authAttrs[i].ulValueLen;
michael@0 941 rv = sftkdb_SignAttribute(arena, newKey, id,
michael@0 942 authAttrs[i].type, &plainText, &signText);
michael@0 943 if (rv != SECSuccess) {
michael@0 944 return CKR_GENERAL_ERROR;
michael@0 945 }
michael@0 946 rv = sftkdb_PutAttributeSignature(handle, keyTarget, id,
michael@0 947 authAttrs[i].type, signText);
michael@0 948 if (rv != SECSuccess) {
michael@0 949 return CKR_GENERAL_ERROR;
michael@0 950 }
michael@0 951 }
michael@0 952
michael@0 953 return CKR_OK;
michael@0 954 }
michael@0 955
michael@0 956 static CK_RV
michael@0 957 sftk_updateEncrypted(PLArenaPool *arena, SFTKDBHandle *keydb,
michael@0 958 CK_OBJECT_HANDLE id, SECItem *newKey)
michael@0 959 {
michael@0 960 CK_RV crv = CKR_OK;
michael@0 961 CK_RV crv2;
michael@0 962 CK_ATTRIBUTE *first, *last;
michael@0 963 CK_ATTRIBUTE privAttrs[] = {
michael@0 964 {CKA_VALUE, NULL, 0},
michael@0 965 {CKA_PRIVATE_EXPONENT, NULL, 0},
michael@0 966 {CKA_PRIME_1, NULL, 0},
michael@0 967 {CKA_PRIME_2, NULL, 0},
michael@0 968 {CKA_EXPONENT_1, NULL, 0},
michael@0 969 {CKA_EXPONENT_2, NULL, 0},
michael@0 970 {CKA_COEFFICIENT, NULL, 0} };
michael@0 971 CK_ULONG privAttrCount = sizeof(privAttrs)/sizeof(CK_ATTRIBUTE);
michael@0 972 int i, count;
michael@0 973
michael@0 974 /*
michael@0 975 * STEP 1. Read the old attributes in the clear.
michael@0 976 */
michael@0 977
michael@0 978 /* Get the attribute sizes.
michael@0 979 * ignore the error code, we will have unknown attributes here */
michael@0 980 crv2 = sftkdb_GetAttributeValue(keydb, id, privAttrs, privAttrCount);
michael@0 981
michael@0 982 /*
michael@0 983 * find the valid block of attributes and fill allocate space for
michael@0 984 * their data */
michael@0 985 first = last = NULL;
michael@0 986 for (i=0; i < privAttrCount; i++) {
michael@0 987 /* find the block of attributes that are appropriate for this
michael@0 988 * objects. There should only be once contiguous block, if not
michael@0 989 * there's an error.
michael@0 990 *
michael@0 991 * find the first and last good entry.
michael@0 992 */
michael@0 993 if ((privAttrs[i].ulValueLen == -1) || (privAttrs[i].ulValueLen == 0)){
michael@0 994 if (!first) continue;
michael@0 995 if (!last) {
michael@0 996 /* previous entry was last good entry */
michael@0 997 last= &privAttrs[i-1];
michael@0 998 }
michael@0 999 continue;
michael@0 1000 }
michael@0 1001 if (!first) {
michael@0 1002 first = &privAttrs[i];
michael@0 1003 }
michael@0 1004 if (last) {
michael@0 1005 /* OOPS, we've found another good entry beyond the end of the
michael@0 1006 * last good entry, we need to fail here. */
michael@0 1007 crv = CKR_GENERAL_ERROR;
michael@0 1008 break;
michael@0 1009 }
michael@0 1010 privAttrs[i].pValue = PORT_ArenaAlloc(arena,privAttrs[i].ulValueLen);
michael@0 1011 if (privAttrs[i].pValue == NULL) {
michael@0 1012 crv = CKR_HOST_MEMORY;
michael@0 1013 break;
michael@0 1014 }
michael@0 1015 }
michael@0 1016 if (first == NULL) {
michael@0 1017 /* no valid entries found, return error based on crv2 */
michael@0 1018 return crv2;
michael@0 1019 }
michael@0 1020 if (last == NULL) {
michael@0 1021 last = &privAttrs[privAttrCount-1];
michael@0 1022 }
michael@0 1023 if (crv != CKR_OK) {
michael@0 1024 return crv;
michael@0 1025 }
michael@0 1026 /* read the attributes */
michael@0 1027 count = (last-first)+1;
michael@0 1028 crv = sftkdb_GetAttributeValue(keydb, id, first, count);
michael@0 1029 if (crv != CKR_OK) {
michael@0 1030 return crv;
michael@0 1031 }
michael@0 1032
michael@0 1033 /*
michael@0 1034 * STEP 2: read the encrypt the attributes with the new key.
michael@0 1035 */
michael@0 1036 for (i=0; i < count; i++) {
michael@0 1037 SECItem plainText;
michael@0 1038 SECItem *result;
michael@0 1039 SECStatus rv;
michael@0 1040
michael@0 1041 plainText.data = first[i].pValue;
michael@0 1042 plainText.len = first[i].ulValueLen;
michael@0 1043 rv = sftkdb_EncryptAttribute(arena, newKey, &plainText, &result);
michael@0 1044 if (rv != SECSuccess) {
michael@0 1045 return CKR_GENERAL_ERROR;
michael@0 1046 }
michael@0 1047 first[i].pValue = result->data;
michael@0 1048 first[i].ulValueLen = result->len;
michael@0 1049 /* clear our sensitive data out */
michael@0 1050 PORT_Memset(plainText.data, 0, plainText.len);
michael@0 1051 }
michael@0 1052
michael@0 1053
michael@0 1054 /*
michael@0 1055 * STEP 3: write the newly encrypted attributes out directly
michael@0 1056 */
michael@0 1057 id &= SFTK_OBJ_ID_MASK;
michael@0 1058 keydb->newKey = newKey;
michael@0 1059 crv = (*keydb->db->sdb_SetAttributeValue)(keydb->db, id, first, count);
michael@0 1060 keydb->newKey = NULL;
michael@0 1061
michael@0 1062 return crv;
michael@0 1063 }
michael@0 1064
michael@0 1065 static CK_RV
michael@0 1066 sftk_convertAttributes(SFTKDBHandle *handle,
michael@0 1067 CK_OBJECT_HANDLE id, SECItem *newKey)
michael@0 1068 {
michael@0 1069 CK_RV crv = CKR_OK;
michael@0 1070 PLArenaPool *arena = NULL;
michael@0 1071
michael@0 1072 /* get a new arena to simplify cleanup */
michael@0 1073 arena = PORT_NewArena(1024);
michael@0 1074 if (!arena) {
michael@0 1075 return CKR_HOST_MEMORY;
michael@0 1076 }
michael@0 1077
michael@0 1078 /*
michael@0 1079 * first handle the MACS
michael@0 1080 */
michael@0 1081 crv = sftk_updateMacs(arena, handle, id, newKey);
michael@0 1082 if (crv != CKR_OK) {
michael@0 1083 goto loser;
michael@0 1084 }
michael@0 1085
michael@0 1086 if (handle->type == SFTK_KEYDB_TYPE) {
michael@0 1087 crv = sftk_updateEncrypted(arena, handle, id, newKey);
michael@0 1088 if (crv != CKR_OK) {
michael@0 1089 goto loser;
michael@0 1090 }
michael@0 1091 }
michael@0 1092
michael@0 1093 /* free up our mess */
michael@0 1094 /* NOTE: at this point we know we've cleared out any unencrypted data */
michael@0 1095 PORT_FreeArena(arena, PR_FALSE);
michael@0 1096 return CKR_OK;
michael@0 1097
michael@0 1098 loser:
michael@0 1099 /* there may be unencrypted data, clear it out down */
michael@0 1100 PORT_FreeArena(arena, PR_TRUE);
michael@0 1101 return crv;
michael@0 1102 }
michael@0 1103
michael@0 1104
michael@0 1105 /*
michael@0 1106 * must be called with the old key active.
michael@0 1107 */
michael@0 1108 CK_RV
michael@0 1109 sftkdb_convertObjects(SFTKDBHandle *handle, CK_ATTRIBUTE *template,
michael@0 1110 CK_ULONG count, SECItem *newKey)
michael@0 1111 {
michael@0 1112 SDBFind *find = NULL;
michael@0 1113 CK_ULONG idCount = SFTK_MAX_IDS;
michael@0 1114 CK_OBJECT_HANDLE ids[SFTK_MAX_IDS];
michael@0 1115 CK_RV crv, crv2;
michael@0 1116 int i;
michael@0 1117
michael@0 1118 crv = sftkdb_FindObjectsInit(handle, template, count, &find);
michael@0 1119
michael@0 1120 if (crv != CKR_OK) {
michael@0 1121 return crv;
michael@0 1122 }
michael@0 1123 while ((crv == CKR_OK) && (idCount == SFTK_MAX_IDS)) {
michael@0 1124 crv = sftkdb_FindObjects(handle, find, ids, SFTK_MAX_IDS, &idCount);
michael@0 1125 for (i=0; (crv == CKR_OK) && (i < idCount); i++) {
michael@0 1126 crv = sftk_convertAttributes(handle, ids[i], newKey);
michael@0 1127 }
michael@0 1128 }
michael@0 1129 crv2 = sftkdb_FindObjectsFinal(handle, find);
michael@0 1130 if (crv == CKR_OK) crv = crv2;
michael@0 1131
michael@0 1132 return crv;
michael@0 1133 }
michael@0 1134
michael@0 1135
michael@0 1136 /*
michael@0 1137 * change the database password.
michael@0 1138 */
michael@0 1139 SECStatus
michael@0 1140 sftkdb_ChangePassword(SFTKDBHandle *keydb,
michael@0 1141 char *oldPin, char *newPin, PRBool *tokenRemoved)
michael@0 1142 {
michael@0 1143 SECStatus rv = SECSuccess;
michael@0 1144 SECItem plainText;
michael@0 1145 SECItem newKey;
michael@0 1146 SECItem *result = NULL;
michael@0 1147 SECItem salt, value;
michael@0 1148 SFTKDBHandle *certdb;
michael@0 1149 unsigned char saltData[SDB_MAX_META_DATA_LEN];
michael@0 1150 unsigned char valueData[SDB_MAX_META_DATA_LEN];
michael@0 1151 CK_RV crv;
michael@0 1152 SDB *db;
michael@0 1153
michael@0 1154 if (keydb == NULL) {
michael@0 1155 return SECFailure;
michael@0 1156 }
michael@0 1157
michael@0 1158 db = SFTK_GET_SDB(keydb);
michael@0 1159 if (db == NULL) {
michael@0 1160 return SECFailure;
michael@0 1161 }
michael@0 1162
michael@0 1163 newKey.data = NULL;
michael@0 1164
michael@0 1165 /* make sure we have a valid old pin */
michael@0 1166 crv = (*keydb->db->sdb_Begin)(keydb->db);
michael@0 1167 if (crv != CKR_OK) {
michael@0 1168 rv = SECFailure;
michael@0 1169 goto loser;
michael@0 1170 }
michael@0 1171 salt.data = saltData;
michael@0 1172 salt.len = sizeof(saltData);
michael@0 1173 value.data = valueData;
michael@0 1174 value.len = sizeof(valueData);
michael@0 1175 crv = (*db->sdb_GetMetaData)(db, "password", &salt, &value);
michael@0 1176 if (crv == CKR_OK) {
michael@0 1177 rv = sftkdb_CheckPassword(keydb, oldPin, tokenRemoved);
michael@0 1178 if (rv == SECFailure) {
michael@0 1179 goto loser;
michael@0 1180 }
michael@0 1181 } else {
michael@0 1182 salt.len = SHA1_LENGTH;
michael@0 1183 RNG_GenerateGlobalRandomBytes(salt.data,salt.len);
michael@0 1184 }
michael@0 1185
michael@0 1186 rv = sftkdb_passwordToKey(keydb, &salt, newPin, &newKey);
michael@0 1187 if (rv != SECSuccess) {
michael@0 1188 goto loser;
michael@0 1189 }
michael@0 1190
michael@0 1191
michael@0 1192 /*
michael@0 1193 * convert encrypted entries here.
michael@0 1194 */
michael@0 1195 crv = sftkdb_convertObjects(keydb, NULL, 0, &newKey);
michael@0 1196 if (crv != CKR_OK) {
michael@0 1197 rv = SECFailure;
michael@0 1198 goto loser;
michael@0 1199 }
michael@0 1200 /* fix up certdb macs */
michael@0 1201 certdb = keydb->peerDB;
michael@0 1202 if (certdb) {
michael@0 1203 CK_ATTRIBUTE objectType = { CKA_CLASS, 0, sizeof(CK_OBJECT_CLASS) };
michael@0 1204 CK_OBJECT_CLASS myClass = CKO_NETSCAPE_TRUST;
michael@0 1205
michael@0 1206 objectType.pValue = &myClass;
michael@0 1207 crv = sftkdb_convertObjects(certdb, &objectType, 1, &newKey);
michael@0 1208 if (crv != CKR_OK) {
michael@0 1209 rv = SECFailure;
michael@0 1210 goto loser;
michael@0 1211 }
michael@0 1212 myClass = CKO_PUBLIC_KEY;
michael@0 1213 crv = sftkdb_convertObjects(certdb, &objectType, 1, &newKey);
michael@0 1214 if (crv != CKR_OK) {
michael@0 1215 rv = SECFailure;
michael@0 1216 goto loser;
michael@0 1217 }
michael@0 1218 }
michael@0 1219
michael@0 1220
michael@0 1221 plainText.data = (unsigned char *)SFTK_PW_CHECK_STRING;
michael@0 1222 plainText.len = SFTK_PW_CHECK_LEN;
michael@0 1223
michael@0 1224 rv = sftkdb_EncryptAttribute(NULL, &newKey, &plainText, &result);
michael@0 1225 if (rv != SECSuccess) {
michael@0 1226 goto loser;
michael@0 1227 }
michael@0 1228 value.data = result->data;
michael@0 1229 value.len = result->len;
michael@0 1230 crv = (*keydb->db->sdb_PutMetaData)(keydb->db, "password", &salt, &value);
michael@0 1231 if (crv != CKR_OK) {
michael@0 1232 rv = SECFailure;
michael@0 1233 goto loser;
michael@0 1234 }
michael@0 1235 crv = (*keydb->db->sdb_Commit)(keydb->db);
michael@0 1236 if (crv != CKR_OK) {
michael@0 1237 rv = SECFailure;
michael@0 1238 goto loser;
michael@0 1239 }
michael@0 1240
michael@0 1241 keydb->newKey = NULL;
michael@0 1242
michael@0 1243 sftkdb_switchKeys(keydb, &newKey);
michael@0 1244
michael@0 1245 loser:
michael@0 1246 if (newKey.data) {
michael@0 1247 PORT_ZFree(newKey.data,newKey.len);
michael@0 1248 }
michael@0 1249 if (result) {
michael@0 1250 SECITEM_FreeItem(result, PR_FALSE);
michael@0 1251 }
michael@0 1252 if (rv != SECSuccess) {
michael@0 1253 (*keydb->db->sdb_Abort)(keydb->db);
michael@0 1254 }
michael@0 1255
michael@0 1256 return rv;
michael@0 1257 }
michael@0 1258
michael@0 1259 /*
michael@0 1260 * lose our cached password
michael@0 1261 */
michael@0 1262 SECStatus
michael@0 1263 sftkdb_ClearPassword(SFTKDBHandle *keydb)
michael@0 1264 {
michael@0 1265 SECItem oldKey;
michael@0 1266 oldKey.data = NULL;
michael@0 1267 oldKey.len = 0;
michael@0 1268 sftkdb_switchKeys(keydb, &oldKey);
michael@0 1269 if (oldKey.data) {
michael@0 1270 PORT_ZFree(oldKey.data, oldKey.len);
michael@0 1271 }
michael@0 1272 return SECSuccess;
michael@0 1273 }
michael@0 1274
michael@0 1275

mercurial