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