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