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