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