OLD | NEW |
| (Empty) |
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 "pratom.h" | |
27 #include "lgglue.h" | |
28 #include "utilpars.h" | |
29 #include "secerr.h" | |
30 #include "softoken.h" | |
31 | |
32 /* | |
33 * We want all databases to have the same binary representation independent of | |
34 * endianness or length of the host architecture. In general PKCS #11 attributes | |
35 * are endian/length independent except those attributes that pass CK_ULONG. | |
36 * | |
37 * The following functions fixes up the CK_ULONG type attributes so that the dat
a | |
38 * base sees a machine independent view. CK_ULONGs are stored as 4 byte network | |
39 * byte order values (big endian). | |
40 */ | |
41 #define BBP 8 | |
42 | |
43 static PRBool | |
44 sftkdb_isULONGAttribute(CK_ATTRIBUTE_TYPE type) | |
45 { | |
46 switch(type) { | |
47 case CKA_CERTIFICATE_CATEGORY: | |
48 case CKA_CERTIFICATE_TYPE: | |
49 case CKA_CLASS: | |
50 case CKA_JAVA_MIDP_SECURITY_DOMAIN: | |
51 case CKA_KEY_GEN_MECHANISM: | |
52 case CKA_KEY_TYPE: | |
53 case CKA_MECHANISM_TYPE: | |
54 case CKA_MODULUS_BITS: | |
55 case CKA_PRIME_BITS: | |
56 case CKA_SUBPRIME_BITS: | |
57 case CKA_VALUE_BITS: | |
58 case CKA_VALUE_LEN: | |
59 | |
60 case CKA_TRUST_DIGITAL_SIGNATURE: | |
61 case CKA_TRUST_NON_REPUDIATION: | |
62 case CKA_TRUST_KEY_ENCIPHERMENT: | |
63 case CKA_TRUST_DATA_ENCIPHERMENT: | |
64 case CKA_TRUST_KEY_AGREEMENT: | |
65 case CKA_TRUST_KEY_CERT_SIGN: | |
66 case CKA_TRUST_CRL_SIGN: | |
67 | |
68 case CKA_TRUST_SERVER_AUTH: | |
69 case CKA_TRUST_CLIENT_AUTH: | |
70 case CKA_TRUST_CODE_SIGNING: | |
71 case CKA_TRUST_EMAIL_PROTECTION: | |
72 case CKA_TRUST_IPSEC_END_SYSTEM: | |
73 case CKA_TRUST_IPSEC_TUNNEL: | |
74 case CKA_TRUST_IPSEC_USER: | |
75 case CKA_TRUST_TIME_STAMPING: | |
76 case CKA_TRUST_STEP_UP_APPROVED: | |
77 return PR_TRUE; | |
78 default: | |
79 break; | |
80 } | |
81 return PR_FALSE; | |
82 | |
83 } | |
84 | |
85 /* are the attributes private? */ | |
86 static PRBool | |
87 sftkdb_isPrivateAttribute(CK_ATTRIBUTE_TYPE type) | |
88 { | |
89 switch(type) { | |
90 case CKA_VALUE: | |
91 case CKA_PRIVATE_EXPONENT: | |
92 case CKA_PRIME_1: | |
93 case CKA_PRIME_2: | |
94 case CKA_EXPONENT_1: | |
95 case CKA_EXPONENT_2: | |
96 case CKA_COEFFICIENT: | |
97 return PR_TRUE; | |
98 default: | |
99 break; | |
100 } | |
101 return PR_FALSE; | |
102 } | |
103 | |
104 /* These attributes must be authenticated with an hmac. */ | |
105 static PRBool | |
106 sftkdb_isAuthenticatedAttribute(CK_ATTRIBUTE_TYPE type) | |
107 { | |
108 switch(type) { | |
109 case CKA_MODULUS: | |
110 case CKA_PUBLIC_EXPONENT: | |
111 case CKA_CERT_SHA1_HASH: | |
112 case CKA_CERT_MD5_HASH: | |
113 case CKA_TRUST_SERVER_AUTH: | |
114 case CKA_TRUST_CLIENT_AUTH: | |
115 case CKA_TRUST_EMAIL_PROTECTION: | |
116 case CKA_TRUST_CODE_SIGNING: | |
117 case CKA_TRUST_STEP_UP_APPROVED: | |
118 case CKA_NSS_OVERRIDE_EXTENSIONS: | |
119 return PR_TRUE; | |
120 default: | |
121 break; | |
122 } | |
123 return PR_FALSE; | |
124 } | |
125 | |
126 /* | |
127 * convert a native ULONG to a database ulong. Database ulong's | |
128 * are all 4 byte big endian values. | |
129 */ | |
130 void | |
131 sftk_ULong2SDBULong(unsigned char *data, CK_ULONG value) | |
132 { | |
133 int i; | |
134 | |
135 for (i=0; i < SDB_ULONG_SIZE; i++) { | |
136 data[i] = (value >> (SDB_ULONG_SIZE-1-i)*BBP) & 0xff; | |
137 } | |
138 } | |
139 | |
140 /* | |
141 * convert a database ulong back to a native ULONG. (reverse of the above | |
142 * function. | |
143 */ | |
144 static CK_ULONG | |
145 sftk_SDBULong2ULong(unsigned char *data) | |
146 { | |
147 int i; | |
148 CK_ULONG value = 0; | |
149 | |
150 for (i=0; i < SDB_ULONG_SIZE; i++) { | |
151 value |= (((CK_ULONG)data[i]) << (SDB_ULONG_SIZE-1-i)*BBP); | |
152 } | |
153 return value; | |
154 } | |
155 | |
156 /* | |
157 * fix up the input templates. Our fixed up ints are stored in data and must | |
158 * be freed by the caller. The new template must also be freed. If there are no | |
159 * CK_ULONG attributes, the orignal template is passed in as is. | |
160 */ | |
161 static CK_ATTRIBUTE * | |
162 sftkdb_fixupTemplateIn(const CK_ATTRIBUTE *template, int count, | |
163 unsigned char **dataOut) | |
164 { | |
165 int i; | |
166 int ulongCount = 0; | |
167 unsigned char *data; | |
168 CK_ATTRIBUTE *ntemplate; | |
169 | |
170 *dataOut = NULL; | |
171 | |
172 /* first count the number of CK_ULONG attributes */ | |
173 for (i=0; i < count; i++) { | |
174 /* Don't 'fixup' NULL values */ | |
175 if (!template[i].pValue) { | |
176 continue; | |
177 } | |
178 if (template[i].ulValueLen == sizeof (CK_ULONG)) { | |
179 if ( sftkdb_isULONGAttribute(template[i].type)) { | |
180 ulongCount++; | |
181 } | |
182 } | |
183 } | |
184 /* no attributes to fixup, just call on through */ | |
185 if (ulongCount == 0) { | |
186 return (CK_ATTRIBUTE *)template; | |
187 } | |
188 | |
189 /* allocate space for new ULONGS */ | |
190 data = (unsigned char *)PORT_Alloc(SDB_ULONG_SIZE*ulongCount); | |
191 if (!data) { | |
192 return NULL; | |
193 } | |
194 | |
195 /* allocate new template */ | |
196 ntemplate = PORT_NewArray(CK_ATTRIBUTE,count); | |
197 if (!ntemplate) { | |
198 PORT_Free(data); | |
199 return NULL; | |
200 } | |
201 *dataOut = data; | |
202 /* copy the old template, fixup the actual ulongs */ | |
203 for (i=0; i < count; i++) { | |
204 ntemplate[i] = template[i]; | |
205 /* Don't 'fixup' NULL values */ | |
206 if (!template[i].pValue) { | |
207 continue; | |
208 } | |
209 if (template[i].ulValueLen == sizeof (CK_ULONG)) { | |
210 if ( sftkdb_isULONGAttribute(template[i].type) ) { | |
211 CK_ULONG value = *(CK_ULONG *) template[i].pValue; | |
212 sftk_ULong2SDBULong(data, value); | |
213 ntemplate[i].pValue = data; | |
214 ntemplate[i].ulValueLen = SDB_ULONG_SIZE; | |
215 data += SDB_ULONG_SIZE; | |
216 } | |
217 } | |
218 } | |
219 return ntemplate; | |
220 } | |
221 | |
222 | |
223 static const char SFTKDB_META_SIG_TEMPLATE[] = "sig_%s_%08x_%08x"; | |
224 | |
225 /* | |
226 * return a string describing the database type (key or cert) | |
227 */ | |
228 const char * | |
229 sftkdb_TypeString(SFTKDBHandle *handle) | |
230 { | |
231 return (handle->type == SFTK_KEYDB_TYPE) ? "key" : "cert"; | |
232 } | |
233 | |
234 /* | |
235 * Some attributes are signed with an Hmac and a pbe key generated from | |
236 * the password. This signature is stored indexed by object handle and | |
237 * attribute type in the meta data table in the key database. | |
238 * | |
239 * Signature entries are indexed by the string | |
240 * sig_[cert/key]_{ObjectID}_{Attribute} | |
241 * | |
242 * This function fetches that pkcs5 signature. Caller supplies a SECItem | |
243 * pre-allocated to the appropriate size if the SECItem is too small the | |
244 * function will fail with CKR_BUFFER_TOO_SMALL. | |
245 */ | |
246 static CK_RV | |
247 sftkdb_getAttributeSignature(SFTKDBHandle *handle, SFTKDBHandle *keyHandle, | |
248 CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE type, | |
249 SECItem *signText) | |
250 { | |
251 SDB *db; | |
252 char id[30]; | |
253 CK_RV crv; | |
254 | |
255 db = SFTK_GET_SDB(keyHandle); | |
256 | |
257 sprintf(id, SFTKDB_META_SIG_TEMPLATE, | |
258 sftkdb_TypeString(handle), | |
259 (unsigned int)objectID, (unsigned int)type); | |
260 | |
261 crv = (*db->sdb_GetMetaData)(db, id, signText, NULL); | |
262 return crv; | |
263 } | |
264 | |
265 /* | |
266 * Some attributes are signed with an Hmac and a pbe key generated from | |
267 * the password. This signature is stored indexed by object handle and | |
268 * attribute type in the meta data table in the key database. | |
269 * | |
270 * Signature entries are indexed by the string | |
271 * sig_[cert/key]_{ObjectID}_{Attribute} | |
272 * | |
273 * This function stores that pkcs5 signature. | |
274 */ | |
275 CK_RV | |
276 sftkdb_PutAttributeSignature(SFTKDBHandle *handle, SDB *keyTarget, | |
277 CK_OBJECT_HANDLE objectID, CK_ATTRIBUTE_TYPE type, | |
278 SECItem *signText) | |
279 { | |
280 char id[30]; | |
281 CK_RV crv; | |
282 | |
283 sprintf(id, SFTKDB_META_SIG_TEMPLATE, | |
284 sftkdb_TypeString(handle), | |
285 (unsigned int)objectID, (unsigned int)type); | |
286 | |
287 crv = (*keyTarget->sdb_PutMetaData)(keyTarget, id, signText, NULL); | |
288 return crv; | |
289 } | |
290 | |
291 /* | |
292 * fix up returned data. NOTE: sftkdb_fixupTemplateIn has already allocated | |
293 * separate data sections for the database ULONG values. | |
294 */ | |
295 static CK_RV | |
296 sftkdb_fixupTemplateOut(CK_ATTRIBUTE *template, CK_OBJECT_HANDLE objectID, | |
297 CK_ATTRIBUTE *ntemplate, int count, SFTKDBHandle *handle) | |
298 { | |
299 int i; | |
300 CK_RV crv = CKR_OK; | |
301 SFTKDBHandle *keyHandle; | |
302 PRBool checkSig = PR_TRUE; | |
303 PRBool checkEnc = PR_TRUE; | |
304 | |
305 PORT_Assert(handle); | |
306 | |
307 /* find the key handle */ | |
308 keyHandle = handle; | |
309 if (handle->type != SFTK_KEYDB_TYPE) { | |
310 checkEnc = PR_FALSE; | |
311 keyHandle = handle->peerDB; | |
312 } | |
313 | |
314 if ((keyHandle == NULL) || | |
315 ((SFTK_GET_SDB(keyHandle)->sdb_flags & SDB_HAS_META) == 0) || | |
316 (keyHandle->passwordKey.data == NULL)) { | |
317 checkSig = PR_FALSE; | |
318 } | |
319 | |
320 for (i=0; i < count; i++) { | |
321 CK_ULONG length = template[i].ulValueLen; | |
322 template[i].ulValueLen = ntemplate[i].ulValueLen; | |
323 /* fixup ulongs */ | |
324 if (ntemplate[i].ulValueLen == SDB_ULONG_SIZE) { | |
325 if (sftkdb_isULONGAttribute(template[i].type)) { | |
326 if (template[i].pValue) { | |
327 CK_ULONG value; | |
328 | |
329 value = sftk_SDBULong2ULong(ntemplate[i].pValue); | |
330 if (length < sizeof(CK_ULONG)) { | |
331 template[i].ulValueLen = -1; | |
332 crv = CKR_BUFFER_TOO_SMALL; | |
333 continue; | |
334 } | |
335 PORT_Memcpy(template[i].pValue,&value,sizeof(CK_ULONG)); | |
336 } | |
337 template[i].ulValueLen = sizeof(CK_ULONG); | |
338 } | |
339 } | |
340 | |
341 /* if no data was retrieved, no need to process encrypted or signed | |
342 * attributes */ | |
343 if ((template[i].pValue == NULL) || (template[i].ulValueLen == -1)) { | |
344 continue; | |
345 } | |
346 | |
347 /* fixup private attributes */ | |
348 if (checkEnc && sftkdb_isPrivateAttribute(ntemplate[i].type)) { | |
349 /* we have a private attribute */ | |
350 /* This code depends on the fact that the cipherText is bigger | |
351 * than the plain text */ | |
352 SECItem cipherText; | |
353 SECItem *plainText; | |
354 SECStatus rv; | |
355 | |
356 cipherText.data = ntemplate[i].pValue; | |
357 cipherText.len = ntemplate[i].ulValueLen; | |
358 PZ_Lock(handle->passwordLock); | |
359 if (handle->passwordKey.data == NULL) { | |
360 PZ_Unlock(handle->passwordLock); | |
361 template[i].ulValueLen = -1; | |
362 crv = CKR_USER_NOT_LOGGED_IN; | |
363 continue; | |
364 } | |
365 rv = sftkdb_DecryptAttribute(&handle->passwordKey, | |
366 &cipherText, &plainText); | |
367 PZ_Unlock(handle->passwordLock); | |
368 if (rv != SECSuccess) { | |
369 PORT_Memset(template[i].pValue, 0, template[i].ulValueLen); | |
370 template[i].ulValueLen = -1; | |
371 crv = CKR_GENERAL_ERROR; | |
372 continue; | |
373 } | |
374 PORT_Assert(template[i].ulValueLen >= plainText->len); | |
375 if (template[i].ulValueLen < plainText->len) { | |
376 SECITEM_FreeItem(plainText,PR_TRUE); | |
377 PORT_Memset(template[i].pValue, 0, template[i].ulValueLen); | |
378 template[i].ulValueLen = -1; | |
379 crv = CKR_GENERAL_ERROR; | |
380 continue; | |
381 } | |
382 | |
383 /* copy the plain text back into the template */ | |
384 PORT_Memcpy(template[i].pValue, plainText->data, plainText->len); | |
385 template[i].ulValueLen = plainText->len; | |
386 SECITEM_FreeItem(plainText,PR_TRUE); | |
387 } | |
388 /* make sure signed attributes are valid */ | |
389 if (checkSig && sftkdb_isAuthenticatedAttribute(ntemplate[i].type)) { | |
390 SECStatus rv; | |
391 SECItem signText; | |
392 SECItem plainText; | |
393 unsigned char signData[SDB_MAX_META_DATA_LEN]; | |
394 | |
395 signText.data = signData; | |
396 signText.len = sizeof(signData); | |
397 | |
398 rv = sftkdb_getAttributeSignature(handle, keyHandle, | |
399 objectID, ntemplate[i].type, &signText); | |
400 if (rv != SECSuccess) { | |
401 PORT_Memset(template[i].pValue, 0, template[i].ulValueLen); | |
402 template[i].ulValueLen = -1; | |
403 crv = CKR_DATA_INVALID; /* better error code? */ | |
404 continue; | |
405 } | |
406 | |
407 plainText.data = ntemplate[i].pValue; | |
408 plainText.len = ntemplate[i].ulValueLen; | |
409 | |
410 /* | |
411 * we do a second check holding the lock just in case the user | |
412 * loggout while we were trying to get the signature. | |
413 */ | |
414 PZ_Lock(keyHandle->passwordLock); | |
415 if (keyHandle->passwordKey.data == NULL) { | |
416 /* if we are no longer logged in, no use checking the other | |
417 * Signatures either. */ | |
418 checkSig = PR_FALSE; | |
419 PZ_Unlock(keyHandle->passwordLock); | |
420 continue; | |
421 } | |
422 | |
423 rv = sftkdb_VerifyAttribute(&keyHandle->passwordKey, | |
424 objectID, ntemplate[i].type, | |
425 &plainText, &signText); | |
426 PZ_Unlock(keyHandle->passwordLock); | |
427 if (rv != SECSuccess) { | |
428 PORT_Memset(template[i].pValue, 0, template[i].ulValueLen); | |
429 template[i].ulValueLen = -1; | |
430 crv = CKR_SIGNATURE_INVALID; /* better error code? */ | |
431 } | |
432 /* This Attribute is fine */ | |
433 } | |
434 } | |
435 return crv; | |
436 } | |
437 | |
438 /* | |
439 * Some attributes are signed with an HMAC and a pbe key generated from | |
440 * the password. This signature is stored indexed by object handle and | |
441 * | |
442 * Those attributes are: | |
443 * 1) Trust object hashes and trust values. | |
444 * 2) public key values. | |
445 * | |
446 * Certs themselves are considered properly authenticated by virtue of their | |
447 * signature, or their matching hash with the trust object. | |
448 * | |
449 * These signature is only checked for objects coming from shared databases. | |
450 * Older dbm style databases have such no signature checks. HMACs are also | |
451 * only checked when the token is logged in, as it requires a pbe generated | |
452 * from the password. | |
453 * | |
454 * Tokens which have no key database (and therefore no master password) do not | |
455 * have any stored signature values. Signature values are stored in the key | |
456 * database, since the signature data is tightly coupled to the key database | |
457 * password. | |
458 * | |
459 * This function takes a template of attributes that were either created or | |
460 * modified. These attributes are checked to see if the need to be signed. | |
461 * If they do, then this function signs the attributes and writes them | |
462 * to the meta data store. | |
463 * | |
464 * This function can fail if there are attributes that must be signed, but | |
465 * the token is not logged in. | |
466 * | |
467 * The caller is expected to abort any transaction he was in in the | |
468 * event of a failure of this function. | |
469 */ | |
470 static CK_RV | |
471 sftk_signTemplate(PLArenaPool *arena, SFTKDBHandle *handle, | |
472 PRBool mayBeUpdateDB, | |
473 CK_OBJECT_HANDLE objectID, const CK_ATTRIBUTE *template, | |
474 CK_ULONG count) | |
475 { | |
476 unsigned int i; | |
477 CK_RV crv; | |
478 SFTKDBHandle *keyHandle = handle; | |
479 SDB *keyTarget = NULL; | |
480 PRBool usingPeerDB = PR_FALSE; | |
481 PRBool inPeerDBTransaction = PR_FALSE; | |
482 | |
483 PORT_Assert(handle); | |
484 | |
485 if (handle->type != SFTK_KEYDB_TYPE) { | |
486 keyHandle = handle->peerDB; | |
487 usingPeerDB = PR_TRUE; | |
488 } | |
489 | |
490 /* no key DB defined? then no need to sign anything */ | |
491 if (keyHandle == NULL) { | |
492 crv = CKR_OK; | |
493 goto loser; | |
494 } | |
495 | |
496 /* When we are in a middle of an update, we have an update database set, | |
497 * but we want to write to the real database. The bool mayBeUpdateDB is | |
498 * set to TRUE if it's possible that we want to write an update database | |
499 * rather than a primary */ | |
500 keyTarget = (mayBeUpdateDB && keyHandle->update) ? | |
501 keyHandle->update : keyHandle->db; | |
502 | |
503 /* skip the the database does not support meta data */ | |
504 if ((keyTarget->sdb_flags & SDB_HAS_META) == 0) { | |
505 crv = CKR_OK; | |
506 goto loser; | |
507 } | |
508 | |
509 /* If we had to switch databases, we need to initialize a transaction. */ | |
510 if (usingPeerDB) { | |
511 crv = (*keyTarget->sdb_Begin)(keyTarget); | |
512 if (crv != CKR_OK) { | |
513 goto loser; | |
514 } | |
515 inPeerDBTransaction = PR_TRUE; | |
516 } | |
517 | |
518 for (i=0; i < count; i ++) { | |
519 if (sftkdb_isAuthenticatedAttribute(template[i].type)) { | |
520 SECStatus rv; | |
521 SECItem *signText; | |
522 SECItem plainText; | |
523 | |
524 plainText.data = template[i].pValue; | |
525 plainText.len = template[i].ulValueLen; | |
526 PZ_Lock(keyHandle->passwordLock); | |
527 if (keyHandle->passwordKey.data == NULL) { | |
528 PZ_Unlock(keyHandle->passwordLock); | |
529 crv = CKR_USER_NOT_LOGGED_IN; | |
530 goto loser; | |
531 } | |
532 rv = sftkdb_SignAttribute(arena, &keyHandle->passwordKey, | |
533 objectID, template[i].type, | |
534 &plainText, &signText); | |
535 PZ_Unlock(keyHandle->passwordLock); | |
536 if (rv != SECSuccess) { | |
537 crv = CKR_GENERAL_ERROR; /* better error code here? */ | |
538 goto loser; | |
539 } | |
540 rv = sftkdb_PutAttributeSignature(handle, keyTarget, | |
541 objectID, template[i].type, signText); | |
542 if (rv != SECSuccess) { | |
543 crv = CKR_GENERAL_ERROR; /* better error code here? */ | |
544 goto loser; | |
545 } | |
546 } | |
547 } | |
548 crv = CKR_OK; | |
549 | |
550 /* If necessary, commit the transaction */ | |
551 if (inPeerDBTransaction) { | |
552 crv = (*keyTarget->sdb_Commit)(keyTarget); | |
553 if (crv != CKR_OK) { | |
554 goto loser; | |
555 } | |
556 inPeerDBTransaction = PR_FALSE; | |
557 } | |
558 | |
559 loser: | |
560 if (inPeerDBTransaction) { | |
561 /* The transaction must have failed. Abort. */ | |
562 (*keyTarget->sdb_Abort)(keyTarget); | |
563 PORT_Assert(crv != CKR_OK); | |
564 if (crv == CKR_OK) crv = CKR_GENERAL_ERROR; | |
565 } | |
566 return crv; | |
567 } | |
568 | |
569 static CK_RV | |
570 sftkdb_CreateObject(PLArenaPool *arena, SFTKDBHandle *handle, | |
571 SDB *db, CK_OBJECT_HANDLE *objectID, | |
572 CK_ATTRIBUTE *template, CK_ULONG count) | |
573 { | |
574 CK_RV crv; | |
575 | |
576 crv = (*db->sdb_CreateObject)(db, objectID, template, count); | |
577 if (crv != CKR_OK) { | |
578 goto loser; | |
579 } | |
580 crv = sftk_signTemplate(arena, handle, (db == handle->update), | |
581 *objectID, template, count); | |
582 loser: | |
583 | |
584 return crv; | |
585 } | |
586 | |
587 | |
588 CK_ATTRIBUTE * | |
589 sftk_ExtractTemplate(PLArenaPool *arena, SFTKObject *object, | |
590 SFTKDBHandle *handle,CK_ULONG *pcount, | |
591 CK_RV *crv) | |
592 { | |
593 unsigned int count; | |
594 CK_ATTRIBUTE *template; | |
595 unsigned int i, templateIndex; | |
596 SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object); | |
597 PRBool doEnc = PR_TRUE; | |
598 | |
599 *crv = CKR_OK; | |
600 | |
601 if (sessObject == NULL) { | |
602 *crv = CKR_GENERAL_ERROR; /* internal programming error */ | |
603 return NULL; | |
604 } | |
605 | |
606 PORT_Assert(handle); | |
607 /* find the key handle */ | |
608 if (handle->type != SFTK_KEYDB_TYPE) { | |
609 doEnc = PR_FALSE; | |
610 } | |
611 | |
612 PZ_Lock(sessObject->attributeLock); | |
613 count = 0; | |
614 for (i=0; i < sessObject->hashSize; i++) { | |
615 SFTKAttribute *attr; | |
616 for (attr=sessObject->head[i]; attr; attr=attr->next) { | |
617 count++; | |
618 } | |
619 } | |
620 template = PORT_ArenaNewArray(arena, CK_ATTRIBUTE, count); | |
621 if (template == NULL) { | |
622 PZ_Unlock(sessObject->attributeLock); | |
623 *crv = CKR_HOST_MEMORY; | |
624 return NULL; | |
625 } | |
626 templateIndex = 0; | |
627 for (i=0; i < sessObject->hashSize; i++) { | |
628 SFTKAttribute *attr; | |
629 for (attr=sessObject->head[i]; attr; attr=attr->next) { | |
630 CK_ATTRIBUTE *tp = &template[templateIndex++]; | |
631 /* copy the attribute */ | |
632 *tp = attr->attrib; | |
633 | |
634 /* fixup ULONG s */ | |
635 if ((tp->ulValueLen == sizeof (CK_ULONG)) && | |
636 (sftkdb_isULONGAttribute(tp->type)) ) { | |
637 CK_ULONG value = *(CK_ULONG *) tp->pValue; | |
638 unsigned char *data; | |
639 | |
640 tp->pValue = PORT_ArenaAlloc(arena, SDB_ULONG_SIZE); | |
641 data = (unsigned char *)tp->pValue; | |
642 if (data == NULL) { | |
643 *crv = CKR_HOST_MEMORY; | |
644 break; | |
645 } | |
646 sftk_ULong2SDBULong(data, value); | |
647 tp->ulValueLen = SDB_ULONG_SIZE; | |
648 } | |
649 | |
650 /* encrypt private attributes */ | |
651 if (doEnc && sftkdb_isPrivateAttribute(tp->type)) { | |
652 /* we have a private attribute */ | |
653 SECItem *cipherText; | |
654 SECItem plainText; | |
655 SECStatus rv; | |
656 | |
657 plainText.data = tp->pValue; | |
658 plainText.len = tp->ulValueLen; | |
659 PZ_Lock(handle->passwordLock); | |
660 if (handle->passwordKey.data == NULL) { | |
661 PZ_Unlock(handle->passwordLock); | |
662 *crv = CKR_USER_NOT_LOGGED_IN; | |
663 break; | |
664 } | |
665 rv = sftkdb_EncryptAttribute(arena, &handle->passwordKey, | |
666 &plainText, &cipherText); | |
667 PZ_Unlock(handle->passwordLock); | |
668 if (rv == SECSuccess) { | |
669 tp->pValue = cipherText->data; | |
670 tp->ulValueLen = cipherText->len; | |
671 } else { | |
672 *crv = CKR_GENERAL_ERROR; /* better error code here? */ | |
673 break; | |
674 } | |
675 PORT_Memset(plainText.data, 0, plainText.len); | |
676 } | |
677 } | |
678 } | |
679 PORT_Assert(templateIndex <= count); | |
680 PZ_Unlock(sessObject->attributeLock); | |
681 | |
682 if (*crv != CKR_OK) { | |
683 return NULL; | |
684 } | |
685 if (pcount) { | |
686 *pcount = count; | |
687 } | |
688 return template; | |
689 | |
690 } | |
691 | |
692 /* | |
693 * return a pointer to the attribute in the give template. | |
694 * The return value is not const, as the caller may modify | |
695 * the given attribute value, but such modifications will | |
696 * modify the actual value in the template. | |
697 */ | |
698 static CK_ATTRIBUTE * | |
699 sftkdb_getAttributeFromTemplate(CK_ATTRIBUTE_TYPE attribute, | |
700 CK_ATTRIBUTE *ptemplate, CK_ULONG len) | |
701 { | |
702 CK_ULONG i; | |
703 | |
704 for (i=0; i < len; i++) { | |
705 if (attribute == ptemplate[i].type) { | |
706 return &ptemplate[i]; | |
707 } | |
708 } | |
709 return NULL; | |
710 } | |
711 | |
712 static const CK_ATTRIBUTE * | |
713 sftkdb_getAttributeFromConstTemplate(CK_ATTRIBUTE_TYPE attribute, | |
714 const CK_ATTRIBUTE *ptemplate, CK_ULONG len) | |
715 { | |
716 CK_ULONG i; | |
717 | |
718 for (i=0; i < len; i++) { | |
719 if (attribute == ptemplate[i].type) { | |
720 return &ptemplate[i]; | |
721 } | |
722 } | |
723 return NULL; | |
724 } | |
725 | |
726 | |
727 /* | |
728 * fetch a template which identifies 'unique' entries based on object type | |
729 */ | |
730 static CK_RV | |
731 sftkdb_getFindTemplate(CK_OBJECT_CLASS objectType, unsigned char *objTypeData, | |
732 CK_ATTRIBUTE *findTemplate, CK_ULONG *findCount, | |
733 CK_ATTRIBUTE *ptemplate, int len) | |
734 { | |
735 CK_ATTRIBUTE *attr; | |
736 CK_ULONG count = 1; | |
737 | |
738 sftk_ULong2SDBULong(objTypeData, objectType); | |
739 findTemplate[0].type = CKA_CLASS; | |
740 findTemplate[0].pValue = objTypeData; | |
741 findTemplate[0].ulValueLen = SDB_ULONG_SIZE; | |
742 | |
743 switch (objectType) { | |
744 case CKO_CERTIFICATE: | |
745 case CKO_NSS_TRUST: | |
746 attr = sftkdb_getAttributeFromTemplate(CKA_ISSUER, ptemplate, len); | |
747 if (attr == NULL) { | |
748 return CKR_TEMPLATE_INCOMPLETE; | |
749 } | |
750 findTemplate[1] = *attr; | |
751 attr = sftkdb_getAttributeFromTemplate(CKA_SERIAL_NUMBER, | |
752 ptemplate, len); | |
753 if (attr == NULL) { | |
754 return CKR_TEMPLATE_INCOMPLETE; | |
755 } | |
756 findTemplate[2] = *attr; | |
757 count = 3; | |
758 break; | |
759 | |
760 case CKO_PRIVATE_KEY: | |
761 case CKO_PUBLIC_KEY: | |
762 case CKO_SECRET_KEY: | |
763 attr = sftkdb_getAttributeFromTemplate(CKA_ID, ptemplate, len); | |
764 if (attr == NULL) { | |
765 return CKR_TEMPLATE_INCOMPLETE; | |
766 } | |
767 if (attr->ulValueLen == 0) { | |
768 /* key is too generic to determine that it's unique, usually | |
769 * happens in the key gen case */ | |
770 return CKR_OBJECT_HANDLE_INVALID; | |
771 } | |
772 | |
773 findTemplate[1] = *attr; | |
774 count = 2; | |
775 break; | |
776 | |
777 case CKO_NSS_CRL: | |
778 attr = sftkdb_getAttributeFromTemplate(CKA_SUBJECT, ptemplate, len); | |
779 if (attr == NULL) { | |
780 return CKR_TEMPLATE_INCOMPLETE; | |
781 } | |
782 findTemplate[1] = *attr; | |
783 count = 2; | |
784 break; | |
785 | |
786 case CKO_NSS_SMIME: | |
787 attr = sftkdb_getAttributeFromTemplate(CKA_SUBJECT, ptemplate, len); | |
788 if (attr == NULL) { | |
789 return CKR_TEMPLATE_INCOMPLETE; | |
790 } | |
791 findTemplate[1] = *attr; | |
792 attr = sftkdb_getAttributeFromTemplate(CKA_NSS_EMAIL, ptemplate, len); | |
793 if (attr == NULL) { | |
794 return CKR_TEMPLATE_INCOMPLETE; | |
795 } | |
796 findTemplate[2] = *attr; | |
797 count = 3; | |
798 break; | |
799 default: | |
800 attr = sftkdb_getAttributeFromTemplate(CKA_VALUE, ptemplate, len); | |
801 if (attr == NULL) { | |
802 return CKR_TEMPLATE_INCOMPLETE; | |
803 } | |
804 findTemplate[1] = *attr; | |
805 count = 2; | |
806 break; | |
807 } | |
808 *findCount = count; | |
809 | |
810 return CKR_OK; | |
811 } | |
812 | |
813 /* | |
814 * look to see if this object already exists and return its object ID if | |
815 * it does. | |
816 */ | |
817 static CK_RV | |
818 sftkdb_lookupObject(SDB *db, CK_OBJECT_CLASS objectType, | |
819 CK_OBJECT_HANDLE *id, CK_ATTRIBUTE *ptemplate, CK_ULONG len) | |
820 { | |
821 CK_ATTRIBUTE findTemplate[3]; | |
822 CK_ULONG count = 1; | |
823 CK_ULONG objCount = 0; | |
824 SDBFind *find = NULL; | |
825 unsigned char objTypeData[SDB_ULONG_SIZE]; | |
826 CK_RV crv; | |
827 | |
828 *id = CK_INVALID_HANDLE; | |
829 if (objectType == CKO_NSS_CRL) { | |
830 return CKR_OK; | |
831 } | |
832 crv = sftkdb_getFindTemplate(objectType, objTypeData, | |
833 findTemplate, &count, ptemplate, len); | |
834 | |
835 if (crv == CKR_OBJECT_HANDLE_INVALID) { | |
836 /* key is too generic to determine that it's unique, usually | |
837 * happens in the key gen case, tell the caller to go ahead | |
838 * and just create it */ | |
839 return CKR_OK; | |
840 } | |
841 if (crv != CKR_OK) { | |
842 return crv; | |
843 } | |
844 | |
845 /* use the raw find, so we get the correct database */ | |
846 crv = (*db->sdb_FindObjectsInit)(db, findTemplate, count, &find); | |
847 if (crv != CKR_OK) { | |
848 return crv; | |
849 } | |
850 (*db->sdb_FindObjects)(db, find, id, 1, &objCount); | |
851 (*db->sdb_FindObjectsFinal)(db, find); | |
852 | |
853 if (objCount == 0) { | |
854 *id = CK_INVALID_HANDLE; | |
855 } | |
856 return CKR_OK; | |
857 } | |
858 | |
859 | |
860 /* | |
861 * check to see if this template conflicts with others in our current database. | |
862 */ | |
863 static CK_RV | |
864 sftkdb_checkConflicts(SDB *db, CK_OBJECT_CLASS objectType, | |
865 const CK_ATTRIBUTE *ptemplate, CK_ULONG len, | |
866 CK_OBJECT_HANDLE sourceID) | |
867 { | |
868 CK_ATTRIBUTE findTemplate[2]; | |
869 unsigned char objTypeData[SDB_ULONG_SIZE]; | |
870 /* we may need to allocate some temporaries. Keep track of what was | |
871 * allocated so we can free it in the end */ | |
872 unsigned char *temp1 = NULL; | |
873 unsigned char *temp2 = NULL; | |
874 CK_ULONG objCount = 0; | |
875 SDBFind *find = NULL; | |
876 CK_OBJECT_HANDLE id; | |
877 const CK_ATTRIBUTE *attr, *attr2; | |
878 CK_RV crv; | |
879 CK_ATTRIBUTE subject; | |
880 | |
881 /* Currently the only conflict is with nicknames pointing to the same | |
882 * subject when creating or modifying a certificate. */ | |
883 /* If the object is not a cert, no problem. */ | |
884 if (objectType != CKO_CERTIFICATE) { | |
885 return CKR_OK; | |
886 } | |
887 /* if not setting a nickname then there's still no problem */ | |
888 attr = sftkdb_getAttributeFromConstTemplate(CKA_LABEL, ptemplate, len); | |
889 if ((attr == NULL) || (attr->ulValueLen == 0)) { | |
890 return CKR_OK; | |
891 } | |
892 /* fetch the subject of the source. For creation and merge, this should | |
893 * be found in the template */ | |
894 attr2 = sftkdb_getAttributeFromConstTemplate(CKA_SUBJECT, ptemplate, len); | |
895 if (sourceID == CK_INVALID_HANDLE) { | |
896 if ((attr2 == NULL) || ((CK_LONG)attr2->ulValueLen < 0)) { | |
897 crv = CKR_TEMPLATE_INCOMPLETE; | |
898 goto done; | |
899 } | |
900 } else if ((attr2 == NULL) || ((CK_LONG)attr2->ulValueLen <= 0)) { | |
901 /* sourceID is set if we are trying to modify an existing entry instead | |
902 * of creating a new one. In this case the subject may not be (probably | |
903 * isn't) in the template, we have to read it from the database */ | |
904 subject.type = CKA_SUBJECT; | |
905 subject.pValue = NULL; | |
906 subject.ulValueLen = 0; | |
907 crv = (*db->sdb_GetAttributeValue)(db, sourceID, &subject, 1); | |
908 if (crv != CKR_OK) { | |
909 goto done; | |
910 } | |
911 if ((CK_LONG)subject.ulValueLen < 0) { | |
912 crv = CKR_DEVICE_ERROR; /* closest pkcs11 error to corrupted DB */ | |
913 goto done; | |
914 } | |
915 temp1 = subject.pValue = PORT_Alloc(++subject.ulValueLen); | |
916 if (temp1 == NULL) { | |
917 crv = CKR_HOST_MEMORY; | |
918 goto done; | |
919 } | |
920 crv = (*db->sdb_GetAttributeValue)(db, sourceID, &subject, 1); | |
921 if (crv != CKR_OK) { | |
922 goto done; | |
923 } | |
924 attr2 = &subject; | |
925 } | |
926 | |
927 /* check for another cert in the database with the same nickname */ | |
928 sftk_ULong2SDBULong(objTypeData, objectType); | |
929 findTemplate[0].type = CKA_CLASS; | |
930 findTemplate[0].pValue = objTypeData; | |
931 findTemplate[0].ulValueLen = SDB_ULONG_SIZE; | |
932 findTemplate[1] = *attr; | |
933 | |
934 crv = (*db->sdb_FindObjectsInit)(db, findTemplate, 2, &find); | |
935 if (crv != CKR_OK) { | |
936 goto done; | |
937 } | |
938 (*db->sdb_FindObjects)(db, find, &id, 1, &objCount); | |
939 (*db->sdb_FindObjectsFinal)(db, find); | |
940 | |
941 /* object count == 0 means no conflicting certs found, | |
942 * go on with the operation */ | |
943 if (objCount == 0) { | |
944 crv = CKR_OK; | |
945 goto done; | |
946 } | |
947 | |
948 /* There is a least one cert that shares the nickname, make sure it also | |
949 * matches the subject. */ | |
950 findTemplate[0] = *attr2; | |
951 /* we know how big the source subject was. Use that length to create the | |
952 * space for the target. If it's not enough space, then it means the | |
953 * source subject is too big, and therefore not a match. GetAttributeValue | |
954 * will return CKR_BUFFER_TOO_SMALL. Otherwise it should be exactly enough | |
955 * space (or enough space to be able to compare the result. */ | |
956 temp2 = findTemplate[0].pValue = PORT_Alloc(++findTemplate[0].ulValueLen); | |
957 if (temp2 == NULL) { | |
958 crv = CKR_HOST_MEMORY; | |
959 goto done; | |
960 } | |
961 crv = (*db->sdb_GetAttributeValue)(db, id, findTemplate, 1); | |
962 if (crv != CKR_OK) { | |
963 if (crv == CKR_BUFFER_TOO_SMALL) { | |
964 /* if our buffer is too small, then the Subjects clearly do | |
965 * not match */ | |
966 crv = CKR_ATTRIBUTE_VALUE_INVALID; | |
967 goto loser; | |
968 } | |
969 /* otherwise we couldn't get the value, just fail */ | |
970 goto done; | |
971 } | |
972 | |
973 /* Ok, we have both subjects, make sure they are the same. | |
974 * Compare the subjects */ | |
975 if ((findTemplate[0].ulValueLen != attr2->ulValueLen) || | |
976 (attr2->ulValueLen > 0 && | |
977 PORT_Memcmp(findTemplate[0].pValue, attr2->pValue, attr2->ulValueLen) | |
978 != 0)) { | |
979 crv = CKR_ATTRIBUTE_VALUE_INVALID; | |
980 goto loser; | |
981 } | |
982 crv = CKR_OK; | |
983 | |
984 done: | |
985 /* If we've failed for some other reason than a conflict, make sure we | |
986 * return an error code other than CKR_ATTRIBUTE_VALUE_INVALID. | |
987 * (NOTE: neither sdb_FindObjectsInit nor sdb_GetAttributeValue should | |
988 * return CKR_ATTRIBUTE_VALUE_INVALID, so the following is paranoia). | |
989 */ | |
990 if (crv == CKR_ATTRIBUTE_VALUE_INVALID) { | |
991 crv = CKR_GENERAL_ERROR; /* clearly a programming error */ | |
992 } | |
993 | |
994 /* exit point if we found a conflict */ | |
995 loser: | |
996 PORT_Free(temp1); | |
997 PORT_Free(temp2); | |
998 return crv; | |
999 } | |
1000 | |
1001 /* | |
1002 * try to update the template to fix any errors. This is only done | |
1003 * during update. | |
1004 * | |
1005 * NOTE: we must update the template or return an error, or the update caller | |
1006 * will loop forever! | |
1007 * | |
1008 * Two copies of the source code for this algorithm exist in NSS. | |
1009 * Changes must be made in both copies. | |
1010 * The other copy is in pk11_IncrementNickname() in pk11wrap/pk11merge.c. | |
1011 * | |
1012 */ | |
1013 static CK_RV | |
1014 sftkdb_resolveConflicts(PLArenaPool *arena, CK_OBJECT_CLASS objectType, | |
1015 CK_ATTRIBUTE *ptemplate, CK_ULONG *plen) | |
1016 { | |
1017 CK_ATTRIBUTE *attr; | |
1018 char *nickname, *newNickname; | |
1019 unsigned int end, digit; | |
1020 | |
1021 /* sanity checks. We should never get here with these errors */ | |
1022 if (objectType != CKO_CERTIFICATE) { | |
1023 return CKR_GENERAL_ERROR; /* shouldn't happen */ | |
1024 } | |
1025 attr = sftkdb_getAttributeFromTemplate(CKA_LABEL, ptemplate, *plen); | |
1026 if ((attr == NULL) || (attr->ulValueLen == 0)) { | |
1027 return CKR_GENERAL_ERROR; /* shouldn't happen */ | |
1028 } | |
1029 | |
1030 /* update the nickname */ | |
1031 /* is there a number at the end of the nickname already? | |
1032 * if so just increment that number */ | |
1033 nickname = (char *)attr->pValue; | |
1034 | |
1035 /* does nickname end with " #n*" ? */ | |
1036 for (end = attr->ulValueLen - 1; | |
1037 end >= 2 && (digit = nickname[end]) <= '9' && digit >= '0'; | |
1038 end--) /* just scan */ ; | |
1039 if (attr->ulValueLen >= 3 && | |
1040 end < (attr->ulValueLen - 1) /* at least one digit */ && | |
1041 nickname[end] == '#' && | |
1042 nickname[end - 1] == ' ') { | |
1043 /* Already has a suitable suffix string */ | |
1044 } else { | |
1045 /* ... append " #2" to the name */ | |
1046 static const char num2[] = " #2"; | |
1047 newNickname = PORT_ArenaAlloc(arena, attr->ulValueLen + sizeof(num2)); | |
1048 if (!newNickname) { | |
1049 return CKR_HOST_MEMORY; | |
1050 } | |
1051 PORT_Memcpy(newNickname, nickname, attr->ulValueLen); | |
1052 PORT_Memcpy(&newNickname[attr->ulValueLen], num2, sizeof(num2)); | |
1053 attr->pValue = newNickname; /* modifies ptemplate */ | |
1054 attr->ulValueLen += 3; /* 3 is strlen(num2) */ | |
1055 return CKR_OK; | |
1056 } | |
1057 | |
1058 for (end = attr->ulValueLen; end-- > 0;) { | |
1059 digit = nickname[end]; | |
1060 if (digit > '9' || digit < '0') { | |
1061 break; | |
1062 } | |
1063 if (digit < '9') { | |
1064 nickname[end]++; | |
1065 return CKR_OK; | |
1066 } | |
1067 nickname[end] = '0'; | |
1068 } | |
1069 | |
1070 /* we overflowed, insert a new '1' for a carry in front of the number */ | |
1071 newNickname = PORT_ArenaAlloc(arena, attr->ulValueLen + 1); | |
1072 if (!newNickname) { | |
1073 return CKR_HOST_MEMORY; | |
1074 } | |
1075 /* PORT_Memcpy should handle len of '0' */ | |
1076 PORT_Memcpy(newNickname, nickname, ++end); | |
1077 newNickname[end] = '1'; | |
1078 PORT_Memset(&newNickname[end+1],'0',attr->ulValueLen - end); | |
1079 attr->pValue = newNickname; | |
1080 attr->ulValueLen++; | |
1081 return CKR_OK; | |
1082 } | |
1083 | |
1084 /* | |
1085 * set an attribute and sign it if necessary | |
1086 */ | |
1087 static CK_RV | |
1088 sftkdb_setAttributeValue(PLArenaPool *arena, SFTKDBHandle *handle, | |
1089 SDB *db, CK_OBJECT_HANDLE objectID, const CK_ATTRIBUTE *template, | |
1090 CK_ULONG count) | |
1091 { | |
1092 CK_RV crv; | |
1093 crv = (*db->sdb_SetAttributeValue)(db, objectID, template, count); | |
1094 if (crv != CKR_OK) { | |
1095 return crv; | |
1096 } | |
1097 crv = sftk_signTemplate(arena, handle, db == handle->update, | |
1098 objectID, template, count); | |
1099 return crv; | |
1100 } | |
1101 | |
1102 /* | |
1103 * write a softoken object out to the database. | |
1104 */ | |
1105 CK_RV | |
1106 sftkdb_write(SFTKDBHandle *handle, SFTKObject *object, | |
1107 CK_OBJECT_HANDLE *objectID) | |
1108 { | |
1109 CK_ATTRIBUTE *template; | |
1110 PLArenaPool *arena; | |
1111 CK_ULONG count; | |
1112 CK_RV crv; | |
1113 SDB *db; | |
1114 PRBool inTransaction = PR_FALSE; | |
1115 CK_OBJECT_HANDLE id; | |
1116 | |
1117 *objectID = CK_INVALID_HANDLE; | |
1118 | |
1119 if (handle == NULL) { | |
1120 return CKR_TOKEN_WRITE_PROTECTED; | |
1121 } | |
1122 db = SFTK_GET_SDB(handle); | |
1123 | |
1124 /* | |
1125 * we have opened a new database, but we have not yet updated it. We are | |
1126 * still running pointing to the old database (so the application can | |
1127 * still read). We don't want to write to the old database at this point, | |
1128 * however, since it leads to user confusion. So at this point we simply | |
1129 * require a user login. Let NSS know this so it can prompt the user. | |
1130 */ | |
1131 if (db == handle->update) { | |
1132 return CKR_USER_NOT_LOGGED_IN; | |
1133 } | |
1134 | |
1135 arena = PORT_NewArena(256); | |
1136 if (arena == NULL) { | |
1137 return CKR_HOST_MEMORY; | |
1138 } | |
1139 | |
1140 template = sftk_ExtractTemplate(arena, object, handle, &count, &crv); | |
1141 if (!template) { | |
1142 goto loser; | |
1143 } | |
1144 | |
1145 crv = (*db->sdb_Begin)(db); | |
1146 if (crv != CKR_OK) { | |
1147 goto loser; | |
1148 } | |
1149 inTransaction = PR_TRUE; | |
1150 | |
1151 /* | |
1152 * We want to make the base database as free from object specific knowledge | |
1153 * as possible. To maintain compatibility, keep some of the desirable | |
1154 * object specific semantics of the old database. | |
1155 * | |
1156 * These were 2 fold: | |
1157 * 1) there were certain conflicts (like trying to set the same nickname | |
1158 * on two different subjects) that would return an error. | |
1159 * 2) Importing the 'same' object would silently update that object. | |
1160 * | |
1161 * The following 2 functions mimic the desirable effects of these two | |
1162 * semantics without pushing any object knowledge to the underlying database | |
1163 * code. | |
1164 */ | |
1165 | |
1166 /* make sure we don't have attributes that conflict with the existing DB */ | |
1167 crv = sftkdb_checkConflicts(db, object->objclass, template, count, | |
1168 CK_INVALID_HANDLE); | |
1169 if (crv != CKR_OK) { | |
1170 goto loser; | |
1171 } | |
1172 /* Find any copies that match this particular object */ | |
1173 crv = sftkdb_lookupObject(db, object->objclass, &id, template, count); | |
1174 if (crv != CKR_OK) { | |
1175 goto loser; | |
1176 } | |
1177 if (id == CK_INVALID_HANDLE) { | |
1178 crv = sftkdb_CreateObject(arena, handle, db, objectID, template, count); | |
1179 } else { | |
1180 /* object already exists, modify it's attributes */ | |
1181 *objectID = id; | |
1182 crv = sftkdb_setAttributeValue(arena, handle, db, id, template, count); | |
1183 } | |
1184 if (crv != CKR_OK) { | |
1185 goto loser; | |
1186 } | |
1187 | |
1188 crv = (*db->sdb_Commit)(db); | |
1189 inTransaction = PR_FALSE; | |
1190 | |
1191 loser: | |
1192 if (inTransaction) { | |
1193 (*db->sdb_Abort)(db); | |
1194 /* It is trivial to show the following code cannot | |
1195 * happen unless something is horribly wrong with our compilier or | |
1196 * hardware */ | |
1197 PORT_Assert(crv != CKR_OK); | |
1198 if (crv == CKR_OK) crv = CKR_GENERAL_ERROR; | |
1199 } | |
1200 | |
1201 if (arena) { | |
1202 PORT_FreeArena(arena,PR_FALSE); | |
1203 } | |
1204 if (crv == CKR_OK) { | |
1205 *objectID |= (handle->type | SFTK_TOKEN_TYPE); | |
1206 } | |
1207 return crv; | |
1208 } | |
1209 | |
1210 | |
1211 CK_RV | |
1212 sftkdb_FindObjectsInit(SFTKDBHandle *handle, const CK_ATTRIBUTE *template, | |
1213 CK_ULONG count, SDBFind **find) | |
1214 { | |
1215 unsigned char *data = NULL; | |
1216 CK_ATTRIBUTE *ntemplate = NULL; | |
1217 CK_RV crv; | |
1218 SDB *db; | |
1219 | |
1220 if (handle == NULL) { | |
1221 return CKR_OK; | |
1222 } | |
1223 db = SFTK_GET_SDB(handle); | |
1224 | |
1225 if (count != 0) { | |
1226 ntemplate = sftkdb_fixupTemplateIn(template, count, &data); | |
1227 if (ntemplate == NULL) { | |
1228 return CKR_HOST_MEMORY; | |
1229 } | |
1230 } | |
1231 | |
1232 crv = (*db->sdb_FindObjectsInit)(db, ntemplate, | |
1233 count, find); | |
1234 if (data) { | |
1235 PORT_Free(ntemplate); | |
1236 PORT_Free(data); | |
1237 } | |
1238 return crv; | |
1239 } | |
1240 | |
1241 CK_RV | |
1242 sftkdb_FindObjects(SFTKDBHandle *handle, SDBFind *find, | |
1243 CK_OBJECT_HANDLE *ids, int arraySize, CK_ULONG *count) | |
1244 { | |
1245 CK_RV crv; | |
1246 SDB *db; | |
1247 | |
1248 if (handle == NULL) { | |
1249 *count = 0; | |
1250 return CKR_OK; | |
1251 } | |
1252 db = SFTK_GET_SDB(handle); | |
1253 | |
1254 crv = (*db->sdb_FindObjects)(db, find, ids, | |
1255 arraySize, count); | |
1256 if (crv == CKR_OK) { | |
1257 unsigned int i; | |
1258 for (i=0; i < *count; i++) { | |
1259 ids[i] |= (handle->type | SFTK_TOKEN_TYPE); | |
1260 } | |
1261 } | |
1262 return crv; | |
1263 } | |
1264 | |
1265 CK_RV sftkdb_FindObjectsFinal(SFTKDBHandle *handle, SDBFind *find) | |
1266 { | |
1267 SDB *db; | |
1268 if (handle == NULL) { | |
1269 return CKR_OK; | |
1270 } | |
1271 db = SFTK_GET_SDB(handle); | |
1272 return (*db->sdb_FindObjectsFinal)(db, find); | |
1273 } | |
1274 | |
1275 CK_RV | |
1276 sftkdb_GetAttributeValue(SFTKDBHandle *handle, CK_OBJECT_HANDLE objectID, | |
1277 CK_ATTRIBUTE *template, CK_ULONG count) | |
1278 { | |
1279 CK_RV crv,crv2; | |
1280 CK_ATTRIBUTE *ntemplate; | |
1281 unsigned char *data = NULL; | |
1282 SDB *db; | |
1283 | |
1284 if (handle == NULL) { | |
1285 return CKR_GENERAL_ERROR; | |
1286 } | |
1287 | |
1288 /* short circuit common attributes */ | |
1289 if (count == 1 && | |
1290 (template[0].type == CKA_TOKEN || | |
1291 template[0].type == CKA_PRIVATE || | |
1292 template[0].type == CKA_SENSITIVE)) { | |
1293 CK_BBOOL boolVal = CK_TRUE; | |
1294 | |
1295 if (template[0].pValue == NULL) { | |
1296 template[0].ulValueLen = sizeof(CK_BBOOL); | |
1297 return CKR_OK; | |
1298 } | |
1299 if (template[0].ulValueLen < sizeof(CK_BBOOL)) { | |
1300 template[0].ulValueLen = -1; | |
1301 return CKR_BUFFER_TOO_SMALL; | |
1302 } | |
1303 | |
1304 if ((template[0].type == CKA_PRIVATE) && | |
1305 (handle->type != SFTK_KEYDB_TYPE)) { | |
1306 boolVal = CK_FALSE; | |
1307 } | |
1308 if ((template[0].type == CKA_SENSITIVE) && | |
1309 (handle->type != SFTK_KEYDB_TYPE)) { | |
1310 boolVal = CK_FALSE; | |
1311 } | |
1312 *(CK_BBOOL *)template[0].pValue = boolVal; | |
1313 template[0].ulValueLen = sizeof(CK_BBOOL); | |
1314 return CKR_OK; | |
1315 } | |
1316 | |
1317 db = SFTK_GET_SDB(handle); | |
1318 /* nothing to do */ | |
1319 if (count == 0) { | |
1320 return CKR_OK; | |
1321 } | |
1322 ntemplate = sftkdb_fixupTemplateIn(template, count, &data); | |
1323 if (ntemplate == NULL) { | |
1324 return CKR_HOST_MEMORY; | |
1325 } | |
1326 objectID &= SFTK_OBJ_ID_MASK; | |
1327 crv = (*db->sdb_GetAttributeValue)(db, objectID, | |
1328 ntemplate, count); | |
1329 crv2 = sftkdb_fixupTemplateOut(template, objectID, ntemplate, | |
1330 count, handle); | |
1331 if (crv == CKR_OK) crv = crv2; | |
1332 if (data) { | |
1333 PORT_Free(ntemplate); | |
1334 PORT_Free(data); | |
1335 } | |
1336 return crv; | |
1337 | |
1338 } | |
1339 | |
1340 CK_RV | |
1341 sftkdb_SetAttributeValue(SFTKDBHandle *handle, SFTKObject *object, | |
1342 const CK_ATTRIBUTE *template, CK_ULONG count) | |
1343 { | |
1344 CK_ATTRIBUTE *ntemplate; | |
1345 unsigned char *data = NULL; | |
1346 PLArenaPool *arena = NULL; | |
1347 SDB *db; | |
1348 CK_RV crv = CKR_OK; | |
1349 CK_OBJECT_HANDLE objectID = (object->handle & SFTK_OBJ_ID_MASK); | |
1350 PRBool inTransaction = PR_FALSE; | |
1351 | |
1352 if (handle == NULL) { | |
1353 return CKR_TOKEN_WRITE_PROTECTED; | |
1354 } | |
1355 | |
1356 db = SFTK_GET_SDB(handle); | |
1357 /* nothing to do */ | |
1358 if (count == 0) { | |
1359 return CKR_OK; | |
1360 } | |
1361 /* | |
1362 * we have opened a new database, but we have not yet updated it. We are | |
1363 * still running pointing to the old database (so the application can | |
1364 * still read). We don't want to write to the old database at this point, | |
1365 * however, since it leads to user confusion. So at this point we simply | |
1366 * require a user login. Let NSS know this so it can prompt the user. | |
1367 */ | |
1368 if (db == handle->update) { | |
1369 return CKR_USER_NOT_LOGGED_IN; | |
1370 } | |
1371 | |
1372 ntemplate = sftkdb_fixupTemplateIn(template, count, &data); | |
1373 if (ntemplate == NULL) { | |
1374 return CKR_HOST_MEMORY; | |
1375 } | |
1376 | |
1377 /* make sure we don't have attributes that conflict with the existing DB */ | |
1378 crv = sftkdb_checkConflicts(db, object->objclass, template, count, objectID)
; | |
1379 if (crv != CKR_OK) { | |
1380 goto loser; | |
1381 } | |
1382 | |
1383 arena = PORT_NewArena(256); | |
1384 if (arena == NULL) { | |
1385 crv = CKR_HOST_MEMORY; | |
1386 goto loser; | |
1387 } | |
1388 | |
1389 crv = (*db->sdb_Begin)(db); | |
1390 if (crv != CKR_OK) { | |
1391 goto loser; | |
1392 } | |
1393 inTransaction = PR_TRUE; | |
1394 crv = sftkdb_setAttributeValue(arena, handle, db, | |
1395 objectID, template, count); | |
1396 if (crv != CKR_OK) { | |
1397 goto loser; | |
1398 } | |
1399 crv = (*db->sdb_Commit)(db); | |
1400 loser: | |
1401 if (crv != CKR_OK && inTransaction) { | |
1402 (*db->sdb_Abort)(db); | |
1403 } | |
1404 if (data) { | |
1405 PORT_Free(ntemplate); | |
1406 PORT_Free(data); | |
1407 } | |
1408 if (arena) { | |
1409 PORT_FreeArena(arena, PR_FALSE); | |
1410 } | |
1411 return crv; | |
1412 } | |
1413 | |
1414 CK_RV | |
1415 sftkdb_DestroyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE objectID) | |
1416 { | |
1417 CK_RV crv = CKR_OK; | |
1418 SDB *db; | |
1419 | |
1420 if (handle == NULL) { | |
1421 return CKR_TOKEN_WRITE_PROTECTED; | |
1422 } | |
1423 db = SFTK_GET_SDB(handle); | |
1424 objectID &= SFTK_OBJ_ID_MASK; | |
1425 crv = (*db->sdb_Begin)(db); | |
1426 if (crv != CKR_OK) { | |
1427 goto loser; | |
1428 } | |
1429 crv = (*db->sdb_DestroyObject)(db, objectID); | |
1430 if (crv != CKR_OK) { | |
1431 goto loser; | |
1432 } | |
1433 crv = (*db->sdb_Commit)(db); | |
1434 loser: | |
1435 if (crv != CKR_OK) { | |
1436 (*db->sdb_Abort)(db); | |
1437 } | |
1438 return crv; | |
1439 } | |
1440 | |
1441 CK_RV | |
1442 sftkdb_CloseDB(SFTKDBHandle *handle) | |
1443 { | |
1444 #ifdef NO_FORK_CHECK | |
1445 PRBool parentForkedAfterC_Initialize = PR_FALSE; | |
1446 #endif | |
1447 if (handle == NULL) { | |
1448 return CKR_OK; | |
1449 } | |
1450 if (handle->update) { | |
1451 if (handle->db->sdb_SetForkState) { | |
1452 (*handle->db->sdb_SetForkState)(parentForkedAfterC_Initialize); | |
1453 } | |
1454 (*handle->update->sdb_Close)(handle->update); | |
1455 } | |
1456 if (handle->db) { | |
1457 if (handle->db->sdb_SetForkState) { | |
1458 (*handle->db->sdb_SetForkState)(parentForkedAfterC_Initialize); | |
1459 } | |
1460 (*handle->db->sdb_Close)(handle->db); | |
1461 } | |
1462 if (handle->passwordKey.data) { | |
1463 PORT_ZFree(handle->passwordKey.data, handle->passwordKey.len); | |
1464 } | |
1465 if (handle->passwordLock) { | |
1466 SKIP_AFTER_FORK(PZ_DestroyLock(handle->passwordLock)); | |
1467 } | |
1468 if (handle->updatePasswordKey) { | |
1469 SECITEM_FreeItem(handle->updatePasswordKey, PR_TRUE); | |
1470 } | |
1471 if (handle->updateID) { | |
1472 PORT_Free(handle->updateID); | |
1473 } | |
1474 PORT_Free(handle); | |
1475 return CKR_OK; | |
1476 } | |
1477 | |
1478 /* | |
1479 * reset a database to it's uninitialized state. | |
1480 */ | |
1481 static CK_RV | |
1482 sftkdb_ResetDB(SFTKDBHandle *handle) | |
1483 { | |
1484 CK_RV crv = CKR_OK; | |
1485 SDB *db; | |
1486 if (handle == NULL) { | |
1487 return CKR_TOKEN_WRITE_PROTECTED; | |
1488 } | |
1489 db = SFTK_GET_SDB(handle); | |
1490 crv = (*db->sdb_Begin)(db); | |
1491 if (crv != CKR_OK) { | |
1492 goto loser; | |
1493 } | |
1494 crv = (*db->sdb_Reset)(db); | |
1495 if (crv != CKR_OK) { | |
1496 goto loser; | |
1497 } | |
1498 crv = (*db->sdb_Commit)(db); | |
1499 loser: | |
1500 if (crv != CKR_OK) { | |
1501 (*db->sdb_Abort)(db); | |
1502 } | |
1503 return crv; | |
1504 } | |
1505 | |
1506 | |
1507 CK_RV | |
1508 sftkdb_Begin(SFTKDBHandle *handle) | |
1509 { | |
1510 CK_RV crv = CKR_OK; | |
1511 SDB *db; | |
1512 | |
1513 if (handle == NULL) { | |
1514 return CKR_OK; | |
1515 } | |
1516 db = SFTK_GET_SDB(handle); | |
1517 if (db) { | |
1518 crv = (*db->sdb_Begin)(db); | |
1519 } | |
1520 return crv; | |
1521 } | |
1522 | |
1523 CK_RV | |
1524 sftkdb_Commit(SFTKDBHandle *handle) | |
1525 { | |
1526 CK_RV crv = CKR_OK; | |
1527 SDB *db; | |
1528 | |
1529 if (handle == NULL) { | |
1530 return CKR_OK; | |
1531 } | |
1532 db = SFTK_GET_SDB(handle); | |
1533 if (db) { | |
1534 (*db->sdb_Commit)(db); | |
1535 } | |
1536 return crv; | |
1537 } | |
1538 | |
1539 CK_RV | |
1540 sftkdb_Abort(SFTKDBHandle *handle) | |
1541 { | |
1542 CK_RV crv = CKR_OK; | |
1543 SDB *db; | |
1544 | |
1545 if (handle == NULL) { | |
1546 return CKR_OK; | |
1547 } | |
1548 db = SFTK_GET_SDB(handle); | |
1549 if (db) { | |
1550 crv = (db->sdb_Abort)(db); | |
1551 } | |
1552 return crv; | |
1553 } | |
1554 | |
1555 | |
1556 /* | |
1557 * functions to update the database from an old database | |
1558 */ | |
1559 | |
1560 /* | |
1561 * known attributes | |
1562 */ | |
1563 static const CK_ATTRIBUTE_TYPE known_attributes[] = { | |
1564 CKA_CLASS, CKA_TOKEN, CKA_PRIVATE, CKA_LABEL, CKA_APPLICATION, | |
1565 CKA_VALUE, CKA_OBJECT_ID, CKA_CERTIFICATE_TYPE, CKA_ISSUER, | |
1566 CKA_SERIAL_NUMBER, CKA_AC_ISSUER, CKA_OWNER, CKA_ATTR_TYPES, CKA_TRUSTED, | |
1567 CKA_CERTIFICATE_CATEGORY, CKA_JAVA_MIDP_SECURITY_DOMAIN, CKA_URL, | |
1568 CKA_HASH_OF_SUBJECT_PUBLIC_KEY, CKA_HASH_OF_ISSUER_PUBLIC_KEY, | |
1569 CKA_CHECK_VALUE, CKA_KEY_TYPE, CKA_SUBJECT, CKA_ID, CKA_SENSITIVE, | |
1570 CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP, CKA_SIGN, CKA_SIGN_RECOVER, | |
1571 CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_DERIVE, CKA_START_DATE, CKA_END_DATE, | |
1572 CKA_MODULUS, CKA_MODULUS_BITS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT, | |
1573 CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT, | |
1574 CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_PRIME_BITS, | |
1575 CKA_SUB_PRIME_BITS, CKA_VALUE_BITS, CKA_VALUE_LEN, CKA_EXTRACTABLE, | |
1576 CKA_LOCAL, CKA_NEVER_EXTRACTABLE, CKA_ALWAYS_SENSITIVE, | |
1577 CKA_KEY_GEN_MECHANISM, CKA_MODIFIABLE, CKA_EC_PARAMS, | |
1578 CKA_EC_POINT, CKA_SECONDARY_AUTH, CKA_AUTH_PIN_FLAGS, | |
1579 CKA_ALWAYS_AUTHENTICATE, CKA_WRAP_WITH_TRUSTED, CKA_WRAP_TEMPLATE, | |
1580 CKA_UNWRAP_TEMPLATE, CKA_HW_FEATURE_TYPE, CKA_RESET_ON_INIT, | |
1581 CKA_HAS_RESET, CKA_PIXEL_X, CKA_PIXEL_Y, CKA_RESOLUTION, CKA_CHAR_ROWS, | |
1582 CKA_CHAR_COLUMNS, CKA_COLOR, CKA_BITS_PER_PIXEL, CKA_CHAR_SETS, | |
1583 CKA_ENCODING_METHODS, CKA_MIME_TYPES, CKA_MECHANISM_TYPE, | |
1584 CKA_REQUIRED_CMS_ATTRIBUTES, CKA_DEFAULT_CMS_ATTRIBUTES, | |
1585 CKA_SUPPORTED_CMS_ATTRIBUTES, CKA_NSS_URL, CKA_NSS_EMAIL, | |
1586 CKA_NSS_SMIME_INFO, CKA_NSS_SMIME_TIMESTAMP, | |
1587 CKA_NSS_PKCS8_SALT, CKA_NSS_PASSWORD_CHECK, CKA_NSS_EXPIRES, | |
1588 CKA_NSS_KRL, CKA_NSS_PQG_COUNTER, CKA_NSS_PQG_SEED, | |
1589 CKA_NSS_PQG_H, CKA_NSS_PQG_SEED_BITS, CKA_NSS_MODULE_SPEC, | |
1590 CKA_TRUST_DIGITAL_SIGNATURE, CKA_TRUST_NON_REPUDIATION, | |
1591 CKA_TRUST_KEY_ENCIPHERMENT, CKA_TRUST_DATA_ENCIPHERMENT, | |
1592 CKA_TRUST_KEY_AGREEMENT, CKA_TRUST_KEY_CERT_SIGN, CKA_TRUST_CRL_SIGN, | |
1593 CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH, CKA_TRUST_CODE_SIGNING, | |
1594 CKA_TRUST_EMAIL_PROTECTION, CKA_TRUST_IPSEC_END_SYSTEM, | |
1595 CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER, CKA_TRUST_TIME_STAMPING, | |
1596 CKA_TRUST_STEP_UP_APPROVED, CKA_CERT_SHA1_HASH, CKA_CERT_MD5_HASH, | |
1597 CKA_NETSCAPE_DB, CKA_NETSCAPE_TRUST, CKA_NSS_OVERRIDE_EXTENSIONS | |
1598 }; | |
1599 | |
1600 static unsigned int known_attributes_size= sizeof(known_attributes)/ | |
1601 sizeof(known_attributes[0]); | |
1602 | |
1603 static CK_RV | |
1604 sftkdb_GetObjectTemplate(SDB *source, CK_OBJECT_HANDLE id, | |
1605 CK_ATTRIBUTE *ptemplate, CK_ULONG *max) | |
1606 { | |
1607 unsigned int i,j; | |
1608 CK_RV crv; | |
1609 | |
1610 if (*max < known_attributes_size) { | |
1611 *max = known_attributes_size; | |
1612 return CKR_BUFFER_TOO_SMALL; | |
1613 } | |
1614 for (i=0; i < known_attributes_size; i++) { | |
1615 ptemplate[i].type = known_attributes[i]; | |
1616 ptemplate[i].pValue = NULL; | |
1617 ptemplate[i].ulValueLen = 0; | |
1618 } | |
1619 | |
1620 crv = (*source->sdb_GetAttributeValue)(source, id, | |
1621 ptemplate, known_attributes_size); | |
1622 | |
1623 if ((crv != CKR_OK) && (crv != CKR_ATTRIBUTE_TYPE_INVALID)) { | |
1624 return crv; | |
1625 } | |
1626 | |
1627 for (i=0, j=0; i < known_attributes_size; i++, j++) { | |
1628 while (i < known_attributes_size && (ptemplate[i].ulValueLen == -1)) { | |
1629 i++; | |
1630 } | |
1631 if (i >= known_attributes_size) { | |
1632 break; | |
1633 } | |
1634 /* cheap optimization */ | |
1635 if (i == j) { | |
1636 continue; | |
1637 } | |
1638 ptemplate[j] = ptemplate[i]; | |
1639 } | |
1640 *max = j; | |
1641 return CKR_OK; | |
1642 } | |
1643 | |
1644 static const char SFTKDB_META_UPDATE_TEMPLATE[] = "upd_%s_%s"; | |
1645 | |
1646 /* | |
1647 * check to see if we have already updated this database. | |
1648 * a NULL updateID means we are trying to do an in place | |
1649 * single database update. In that case we have already | |
1650 * determined that an update was necessary. | |
1651 */ | |
1652 static PRBool | |
1653 sftkdb_hasUpdate(const char *typeString, SDB *db, const char *updateID) | |
1654 { | |
1655 char *id; | |
1656 CK_RV crv; | |
1657 SECItem dummy = { 0, NULL, 0 }; | |
1658 unsigned char dummyData[SDB_MAX_META_DATA_LEN]; | |
1659 | |
1660 if (!updateID) { | |
1661 return PR_FALSE; | |
1662 } | |
1663 id = PR_smprintf(SFTKDB_META_UPDATE_TEMPLATE, typeString, updateID); | |
1664 if (id == NULL) { | |
1665 return PR_FALSE; | |
1666 } | |
1667 dummy.data = dummyData; | |
1668 dummy.len = sizeof(dummyData); | |
1669 | |
1670 crv = (*db->sdb_GetMetaData)(db, id, &dummy, NULL); | |
1671 PR_smprintf_free(id); | |
1672 return crv == CKR_OK ? PR_TRUE : PR_FALSE; | |
1673 } | |
1674 | |
1675 /* | |
1676 * we just completed an update, store the update id | |
1677 * so we don't need to do it again. If non was given, | |
1678 * there is nothing to do. | |
1679 */ | |
1680 static CK_RV | |
1681 sftkdb_putUpdate(const char *typeString, SDB *db, const char *updateID) | |
1682 { | |
1683 char *id; | |
1684 CK_RV crv; | |
1685 SECItem dummy = { 0, NULL, 0 }; | |
1686 | |
1687 /* if no id was given, nothing to do */ | |
1688 if (updateID == NULL) { | |
1689 return CKR_OK; | |
1690 } | |
1691 | |
1692 dummy.data = (unsigned char *)updateID; | |
1693 dummy.len = PORT_Strlen(updateID); | |
1694 | |
1695 id = PR_smprintf(SFTKDB_META_UPDATE_TEMPLATE, typeString, updateID); | |
1696 if (id == NULL) { | |
1697 return PR_FALSE; | |
1698 } | |
1699 | |
1700 crv = (*db->sdb_PutMetaData)(db, id, &dummy, NULL); | |
1701 PR_smprintf_free(id); | |
1702 return crv; | |
1703 } | |
1704 | |
1705 /* | |
1706 * get a ULong attribute from a template: | |
1707 * NOTE: this is a raw templated stored in database order! | |
1708 */ | |
1709 static CK_ULONG | |
1710 sftkdb_getULongFromTemplate(CK_ATTRIBUTE_TYPE type, | |
1711 CK_ATTRIBUTE *ptemplate, CK_ULONG len) | |
1712 { | |
1713 CK_ATTRIBUTE *attr = sftkdb_getAttributeFromTemplate(type, | |
1714 ptemplate, len); | |
1715 | |
1716 if (attr && attr->pValue && attr->ulValueLen == SDB_ULONG_SIZE) { | |
1717 return sftk_SDBULong2ULong(attr->pValue); | |
1718 } | |
1719 return (CK_ULONG)-1; | |
1720 } | |
1721 | |
1722 /* | |
1723 * we need to find a unique CKA_ID. | |
1724 * The basic idea is to just increment the lowest byte. | |
1725 * This code also handles the following corner cases: | |
1726 * 1) the single byte overflows. On overflow we increment the next byte up | |
1727 * and so forth until we have overflowed the entire CKA_ID. | |
1728 * 2) If we overflow the entire CKA_ID we expand it by one byte. | |
1729 * 3) the CKA_ID is non-existant, we create a new one with one byte. | |
1730 * This means no matter what CKA_ID is passed, the result of this function | |
1731 * is always a new CKA_ID, and this function will never return the same | |
1732 * CKA_ID the it has returned in the passed. | |
1733 */ | |
1734 static CK_RV | |
1735 sftkdb_incrementCKAID(PLArenaPool *arena, CK_ATTRIBUTE *ptemplate) | |
1736 { | |
1737 unsigned char *buf = ptemplate->pValue; | |
1738 CK_ULONG len = ptemplate->ulValueLen; | |
1739 | |
1740 if (buf == NULL || len == (CK_ULONG)-1) { | |
1741 /* we have no valid CKAID, we'll create a basic one byte CKA_ID below */ | |
1742 len = 0; | |
1743 } else { | |
1744 CK_ULONG i; | |
1745 | |
1746 /* walk from the back to front, incrementing | |
1747 * the CKA_ID until we no longer have a carry, | |
1748 * or have hit the front of the id. */ | |
1749 for (i=len; i != 0; i--) { | |
1750 buf[i-1]++; | |
1751 if (buf[i-1] != 0) { | |
1752 /* no more carries, the increment is complete */ | |
1753 return CKR_OK; | |
1754 } | |
1755 } | |
1756 /* we've now overflowed, fall through and expand the CKA_ID by | |
1757 * one byte */ | |
1758 } | |
1759 buf = PORT_ArenaAlloc(arena, len+1); | |
1760 if (!buf) { | |
1761 return CKR_HOST_MEMORY; | |
1762 } | |
1763 if (len > 0) { | |
1764 PORT_Memcpy(buf, ptemplate->pValue, len); | |
1765 } | |
1766 buf[len] = 0; | |
1767 ptemplate->pValue = buf; | |
1768 ptemplate->ulValueLen = len+1; | |
1769 return CKR_OK; | |
1770 } | |
1771 | |
1772 /* | |
1773 * drop an attribute from a template. | |
1774 */ | |
1775 void | |
1776 sftkdb_dropAttribute(CK_ATTRIBUTE *attr, CK_ATTRIBUTE *ptemplate, | |
1777 CK_ULONG *plen) | |
1778 { | |
1779 CK_ULONG count = *plen; | |
1780 CK_ULONG i; | |
1781 | |
1782 for (i=0; i < count; i++) { | |
1783 if (attr->type == ptemplate[i].type) { | |
1784 break; | |
1785 } | |
1786 } | |
1787 | |
1788 if (i == count) { | |
1789 /* attribute not found */ | |
1790 return; | |
1791 } | |
1792 | |
1793 /* copy the remaining attributes up */ | |
1794 for ( i++; i < count; i++) { | |
1795 ptemplate[i-1] = ptemplate[i]; | |
1796 } | |
1797 | |
1798 /* decrement the template size */ | |
1799 *plen = count -1; | |
1800 } | |
1801 | |
1802 /* | |
1803 * create some defines for the following functions to document the meaning | |
1804 * of true/false. (make's it easier to remember what means what. | |
1805 */ | |
1806 typedef enum { | |
1807 SFTKDB_DO_NOTHING = 0, | |
1808 SFTKDB_ADD_OBJECT, | |
1809 SFTKDB_MODIFY_OBJECT, | |
1810 SFTKDB_DROP_ATTRIBUTE | |
1811 } sftkdbUpdateStatus; | |
1812 | |
1813 /* | |
1814 * helper function to reconcile a single trust entry. | |
1815 * Identify which trust entry we want to keep. | |
1816 * If we don't need to do anything (the records are already equal). | |
1817 * return SFTKDB_DO_NOTHING. | |
1818 * If we want to use the source version, | |
1819 * return SFTKDB_MODIFY_OBJECT | |
1820 * If we want to use the target version, | |
1821 * return SFTKDB_DROP_ATTRIBUTE | |
1822 * | |
1823 * In the end the caller will remove any attributes in the source | |
1824 * template when SFTKDB_DROP_ATTRIBUTE is specified, then use do a | |
1825 * set attributes with that template on the target if we received | |
1826 * any SFTKDB_MODIFY_OBJECT returns. | |
1827 */ | |
1828 sftkdbUpdateStatus | |
1829 sftkdb_reconcileTrustEntry(PLArenaPool *arena, CK_ATTRIBUTE *target, | |
1830 CK_ATTRIBUTE *source) | |
1831 { | |
1832 CK_ULONG targetTrust = sftkdb_getULongFromTemplate(target->type, | |
1833 target, 1); | |
1834 CK_ULONG sourceTrust = sftkdb_getULongFromTemplate(target->type, | |
1835 source, 1); | |
1836 | |
1837 /* | |
1838 * try to pick the best solution between the source and the | |
1839 * target. Update the source template if we want the target value | |
1840 * to win out. Prefer cases where we don't actually update the | |
1841 * trust entry. | |
1842 */ | |
1843 | |
1844 /* they are the same, everything is already kosher */ | |
1845 if (targetTrust == sourceTrust) { | |
1846 return SFTKDB_DO_NOTHING; | |
1847 } | |
1848 | |
1849 /* handle the case where the source Trust attribute may be a bit | |
1850 * flakey */ | |
1851 if (sourceTrust == (CK_ULONG)-1) { | |
1852 /* | |
1853 * The source Trust is invalid. We know that the target Trust | |
1854 * must be valid here, otherwise the above | |
1855 * targetTrust == sourceTrust check would have succeeded. | |
1856 */ | |
1857 return SFTKDB_DROP_ATTRIBUTE; | |
1858 } | |
1859 | |
1860 /* target is invalid, use the source's idea of the trust value */ | |
1861 if (targetTrust == (CK_ULONG)-1) { | |
1862 /* overwriting the target in this case is OK */ | |
1863 return SFTKDB_MODIFY_OBJECT; | |
1864 } | |
1865 | |
1866 /* at this point we know that both attributes exist and have the | |
1867 * appropriate length (SDB_ULONG_SIZE). We no longer need to check | |
1868 * ulValueLen for either attribute. | |
1869 */ | |
1870 if (sourceTrust == CKT_NSS_TRUST_UNKNOWN) { | |
1871 return SFTKDB_DROP_ATTRIBUTE; | |
1872 } | |
1873 | |
1874 /* target has no idea, use the source's idea of the trust value */ | |
1875 if (targetTrust == CKT_NSS_TRUST_UNKNOWN) { | |
1876 /* overwriting the target in this case is OK */ | |
1877 return SFTKDB_MODIFY_OBJECT; | |
1878 } | |
1879 | |
1880 /* so both the target and the source have some idea of what this | |
1881 * trust attribute should be, and neither agree exactly. | |
1882 * At this point, we prefer 'hard' attributes over 'soft' ones. | |
1883 * 'hard' ones are CKT_NSS_TRUSTED, CKT_NSS_TRUSTED_DELEGATOR, and | |
1884 * CKT_NSS_NOT_TRUTED. Soft ones are ones which don't change the | |
1885 * actual trust of the cert (CKT_MUST_VERIFY_TRUST, | |
1886 * CKT_NSS_VALID_DELEGATOR). | |
1887 */ | |
1888 if ((sourceTrust == CKT_NSS_MUST_VERIFY_TRUST) | |
1889 || (sourceTrust == CKT_NSS_VALID_DELEGATOR)) { | |
1890 return SFTKDB_DROP_ATTRIBUTE; | |
1891 } | |
1892 if ((targetTrust == CKT_NSS_MUST_VERIFY_TRUST) | |
1893 || (targetTrust == CKT_NSS_VALID_DELEGATOR)) { | |
1894 /* again, overwriting the target in this case is OK */ | |
1895 return SFTKDB_MODIFY_OBJECT; | |
1896 } | |
1897 | |
1898 /* both have hard attributes, we have a conflict, let the target win. */ | |
1899 return SFTKDB_DROP_ATTRIBUTE; | |
1900 } | |
1901 | |
1902 const CK_ATTRIBUTE_TYPE sftkdb_trustList[] = | |
1903 { CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH, | |
1904 CKA_TRUST_CODE_SIGNING, CKA_TRUST_EMAIL_PROTECTION, | |
1905 CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER, | |
1906 CKA_TRUST_TIME_STAMPING }; | |
1907 | |
1908 #define SFTK_TRUST_TEMPLATE_COUNT \ | |
1909 (sizeof(sftkdb_trustList)/sizeof(sftkdb_trustList[0])) | |
1910 /* | |
1911 * Run through the list of known trust types, and reconcile each trust | |
1912 * entry one by one. Keep track of we really need to write out the source | |
1913 * trust object (overwriting the existing one). | |
1914 */ | |
1915 static sftkdbUpdateStatus | |
1916 sftkdb_reconcileTrust(PLArenaPool *arena, SDB *db, CK_OBJECT_HANDLE id, | |
1917 CK_ATTRIBUTE *ptemplate, CK_ULONG *plen) | |
1918 { | |
1919 CK_ATTRIBUTE trustTemplate[SFTK_TRUST_TEMPLATE_COUNT]; | |
1920 unsigned char trustData[SFTK_TRUST_TEMPLATE_COUNT*SDB_ULONG_SIZE]; | |
1921 sftkdbUpdateStatus update = SFTKDB_DO_NOTHING; | |
1922 CK_ULONG i; | |
1923 CK_RV crv; | |
1924 | |
1925 | |
1926 for (i=0; i < SFTK_TRUST_TEMPLATE_COUNT; i++) { | |
1927 trustTemplate[i].type = sftkdb_trustList[i]; | |
1928 trustTemplate[i].pValue = &trustData[i*SDB_ULONG_SIZE]; | |
1929 trustTemplate[i].ulValueLen = SDB_ULONG_SIZE; | |
1930 } | |
1931 crv = (*db->sdb_GetAttributeValue)(db, id, | |
1932 trustTemplate, SFTK_TRUST_TEMPLATE_COUNT); | |
1933 if ((crv != CKR_OK) && (crv != CKR_ATTRIBUTE_TYPE_INVALID)) { | |
1934 /* target trust has some problems, update it */ | |
1935 update = SFTKDB_MODIFY_OBJECT; | |
1936 goto done; | |
1937 } | |
1938 | |
1939 for (i=0; i < SFTK_TRUST_TEMPLATE_COUNT; i++) { | |
1940 CK_ATTRIBUTE *attr = sftkdb_getAttributeFromTemplate( | |
1941 trustTemplate[i].type, ptemplate, *plen); | |
1942 sftkdbUpdateStatus status; | |
1943 | |
1944 | |
1945 /* if target trust value doesn't exist, nothing to merge */ | |
1946 if (trustTemplate[i].ulValueLen == (CK_ULONG)-1) { | |
1947 /* if the source exists, then we want the source entry, | |
1948 * go ahead and update */ | |
1949 if (attr && attr->ulValueLen != (CK_ULONG)-1) { | |
1950 update = SFTKDB_MODIFY_OBJECT; | |
1951 } | |
1952 continue; | |
1953 } | |
1954 | |
1955 /* | |
1956 * the source doesn't have the attribute, go to the next attribute | |
1957 */ | |
1958 if (attr == NULL) { | |
1959 continue; | |
1960 | |
1961 } | |
1962 status = sftkdb_reconcileTrustEntry(arena, &trustTemplate[i], attr); | |
1963 if (status == SFTKDB_MODIFY_OBJECT) { | |
1964 update = SFTKDB_MODIFY_OBJECT; | |
1965 } else if (status == SFTKDB_DROP_ATTRIBUTE) { | |
1966 /* drop the source copy of the attribute, we are going with | |
1967 * the target's version */ | |
1968 sftkdb_dropAttribute(attr, ptemplate, plen); | |
1969 } | |
1970 } | |
1971 | |
1972 /* finally manage stepup */ | |
1973 if (update == SFTKDB_MODIFY_OBJECT) { | |
1974 CK_BBOOL stepUpBool = CK_FALSE; | |
1975 /* if we are going to write from the source, make sure we don't | |
1976 * overwrite the stepup bit if it's on*/ | |
1977 trustTemplate[0].type = CKA_TRUST_STEP_UP_APPROVED; | |
1978 trustTemplate[0].pValue = &stepUpBool; | |
1979 trustTemplate[0].ulValueLen = sizeof(stepUpBool); | |
1980 crv = (*db->sdb_GetAttributeValue)(db, id, trustTemplate, 1); | |
1981 if ((crv == CKR_OK) && (stepUpBool == CK_TRUE)) { | |
1982 sftkdb_dropAttribute(trustTemplate, ptemplate, plen); | |
1983 } | |
1984 } else { | |
1985 /* we currently aren't going to update. If the source stepup bit is | |
1986 * on however, do an update so the target gets it as well */ | |
1987 CK_ATTRIBUTE *attr; | |
1988 | |
1989 attr = sftkdb_getAttributeFromTemplate(CKA_TRUST_STEP_UP_APPROVED, | |
1990 ptemplate, *plen); | |
1991 if (attr && (attr->ulValueLen == sizeof(CK_BBOOL)) && | |
1992 (*(CK_BBOOL *)(attr->pValue) == CK_TRUE)) { | |
1993 update = SFTKDB_MODIFY_OBJECT; | |
1994 } | |
1995 } | |
1996 | |
1997 done: | |
1998 return update; | |
1999 } | |
2000 | |
2001 static sftkdbUpdateStatus | |
2002 sftkdb_handleIDAndName(PLArenaPool *arena, SDB *db, CK_OBJECT_HANDLE id, | |
2003 CK_ATTRIBUTE *ptemplate, CK_ULONG *plen) | |
2004 { | |
2005 sftkdbUpdateStatus update = SFTKDB_DO_NOTHING; | |
2006 CK_ATTRIBUTE *attr1, *attr2; | |
2007 CK_ATTRIBUTE ttemplate[2] = { | |
2008 {CKA_ID, NULL, 0}, | |
2009 {CKA_LABEL, NULL, 0} | |
2010 }; | |
2011 | |
2012 attr1 = sftkdb_getAttributeFromTemplate(CKA_LABEL, ptemplate, *plen); | |
2013 attr2 = sftkdb_getAttributeFromTemplate(CKA_ID, ptemplate, *plen); | |
2014 | |
2015 /* if the source has neither an id nor label, don't bother updating */ | |
2016 if ( (!attr1 || attr1->ulValueLen == 0) && | |
2017 (! attr2 || attr2->ulValueLen == 0) ) { | |
2018 return SFTKDB_DO_NOTHING; | |
2019 } | |
2020 | |
2021 /* the source has either an id or a label, see what the target has */ | |
2022 (void)(*db->sdb_GetAttributeValue)(db, id, ttemplate, 2); | |
2023 | |
2024 /* if the target has neither, update from the source */ | |
2025 if ( ((ttemplate[0].ulValueLen == 0) || | |
2026 (ttemplate[0].ulValueLen == (CK_ULONG)-1)) && | |
2027 ((ttemplate[1].ulValueLen == 0) || | |
2028 (ttemplate[1].ulValueLen == (CK_ULONG)-1)) ) { | |
2029 return SFTKDB_MODIFY_OBJECT; | |
2030 } | |
2031 | |
2032 /* check the CKA_ID */ | |
2033 if ((ttemplate[0].ulValueLen != 0) && | |
2034 (ttemplate[0].ulValueLen != (CK_ULONG)-1)) { | |
2035 /* we have a CKA_ID in the target, don't overwrite | |
2036 * the target with an empty CKA_ID from the source*/ | |
2037 if (attr1 && attr1->ulValueLen == 0) { | |
2038 sftkdb_dropAttribute(attr1, ptemplate, plen); | |
2039 } | |
2040 } else if (attr1 && attr1->ulValueLen != 0) { | |
2041 /* source has a CKA_ID, but the target doesn't, update the target */ | |
2042 update = SFTKDB_MODIFY_OBJECT; | |
2043 } | |
2044 | |
2045 | |
2046 /* check the nickname */ | |
2047 if ((ttemplate[1].ulValueLen != 0) && | |
2048 (ttemplate[1].ulValueLen != (CK_ULONG)-1)) { | |
2049 | |
2050 /* we have a nickname in the target, and we don't have to update | |
2051 * the CKA_ID. We are done. NOTE: if we add addition attributes | |
2052 * in this check, this shortcut can only go on the last of them. */ | |
2053 if (update == SFTKDB_DO_NOTHING) { | |
2054 return update; | |
2055 } | |
2056 /* we have a nickname in the target, don't overwrite | |
2057 * the target with an empty nickname from the source */ | |
2058 if (attr2 && attr2->ulValueLen == 0) { | |
2059 sftkdb_dropAttribute(attr2, ptemplate, plen); | |
2060 } | |
2061 } else if (attr2 && attr2->ulValueLen != 0) { | |
2062 /* source has a nickname, but the target doesn't, update the target */ | |
2063 update = SFTKDB_MODIFY_OBJECT; | |
2064 } | |
2065 | |
2066 return update; | |
2067 } | |
2068 | |
2069 | |
2070 | |
2071 /* | |
2072 * This function updates the template before we write the object out. | |
2073 * | |
2074 * If we are going to skip updating this object, return PR_FALSE. | |
2075 * If it should be updated we return PR_TRUE. | |
2076 * To help readability, these have been defined | |
2077 * as SFTK_DONT_UPDATE and SFTK_UPDATE respectively. | |
2078 */ | |
2079 static PRBool | |
2080 sftkdb_updateObjectTemplate(PLArenaPool *arena, SDB *db, | |
2081 CK_OBJECT_CLASS objectType, | |
2082 CK_ATTRIBUTE *ptemplate, CK_ULONG *plen, | |
2083 CK_OBJECT_HANDLE *targetID) | |
2084 { | |
2085 PRBool done; /* should we repeat the loop? */ | |
2086 CK_OBJECT_HANDLE id; | |
2087 CK_RV crv = CKR_OK; | |
2088 | |
2089 do { | |
2090 crv = sftkdb_checkConflicts(db, objectType, ptemplate, | |
2091 *plen, CK_INVALID_HANDLE); | |
2092 if (crv != CKR_ATTRIBUTE_VALUE_INVALID) { | |
2093 break; | |
2094 } | |
2095 crv = sftkdb_resolveConflicts(arena, objectType, ptemplate, plen); | |
2096 } while (crv == CKR_OK); | |
2097 | |
2098 if (crv != CKR_OK) { | |
2099 return SFTKDB_DO_NOTHING; | |
2100 } | |
2101 | |
2102 do { | |
2103 done = PR_TRUE; | |
2104 crv = sftkdb_lookupObject(db, objectType, &id, ptemplate, *plen); | |
2105 if (crv != CKR_OK) { | |
2106 return SFTKDB_DO_NOTHING; | |
2107 } | |
2108 | |
2109 /* This object already exists, merge it, don't update */ | |
2110 if (id != CK_INVALID_HANDLE) { | |
2111 CK_ATTRIBUTE *attr = NULL; | |
2112 /* special post processing for attributes */ | |
2113 switch (objectType) { | |
2114 case CKO_CERTIFICATE: | |
2115 case CKO_PUBLIC_KEY: | |
2116 case CKO_PRIVATE_KEY: | |
2117 /* update target's CKA_ID and labels if they don't already | |
2118 * exist */ | |
2119 *targetID = id; | |
2120 return sftkdb_handleIDAndName(arena, db, id, ptemplate, plen); | |
2121 case CKO_NSS_TRUST: | |
2122 /* if we have conflicting trust object types, | |
2123 * we need to reconcile them */ | |
2124 *targetID = id; | |
2125 return sftkdb_reconcileTrust(arena, db, id, ptemplate, plen); | |
2126 case CKO_SECRET_KEY: | |
2127 /* secret keys in the old database are all sdr keys, | |
2128 * unfortunately they all appear to have the same CKA_ID, | |
2129 * even though they are truly different keys, so we always | |
2130 * want to update these keys, but we need to | |
2131 * give them a new CKA_ID */ | |
2132 /* NOTE: this changes ptemplate */ | |
2133 attr = sftkdb_getAttributeFromTemplate(CKA_ID,ptemplate,*plen); | |
2134 crv = attr ? sftkdb_incrementCKAID(arena, attr) | |
2135 : CKR_HOST_MEMORY; | |
2136 /* in the extremely rare event that we needed memory and | |
2137 * couldn't get it, just drop the key */ | |
2138 if (crv != CKR_OK) { | |
2139 return SFTKDB_DO_NOTHING; | |
2140 } | |
2141 done = PR_FALSE; /* repeat this find loop */ | |
2142 break; | |
2143 default: | |
2144 /* for all other objects, if we found the equivalent object, | |
2145 * don't update it */ | |
2146 return SFTKDB_DO_NOTHING; | |
2147 } | |
2148 } | |
2149 } while (!done); | |
2150 | |
2151 /* this object doesn't exist, update it */ | |
2152 return SFTKDB_ADD_OBJECT; | |
2153 } | |
2154 | |
2155 | |
2156 #define MAX_ATTRIBUTES 500 | |
2157 static CK_RV | |
2158 sftkdb_mergeObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE id, | |
2159 SECItem *key) | |
2160 { | |
2161 CK_ATTRIBUTE template[MAX_ATTRIBUTES]; | |
2162 CK_ATTRIBUTE *ptemplate; | |
2163 CK_ULONG max_attributes = MAX_ATTRIBUTES; | |
2164 CK_OBJECT_CLASS objectType; | |
2165 SDB *source = handle->update; | |
2166 SDB *target = handle->db; | |
2167 unsigned int i; | |
2168 CK_RV crv; | |
2169 PLArenaPool *arena = NULL; | |
2170 | |
2171 arena = PORT_NewArena(256); | |
2172 if (arena == NULL) { | |
2173 return CKR_HOST_MEMORY; | |
2174 } | |
2175 | |
2176 ptemplate = &template[0]; | |
2177 id &= SFTK_OBJ_ID_MASK; | |
2178 crv = sftkdb_GetObjectTemplate(source, id, ptemplate, &max_attributes); | |
2179 if (crv == CKR_BUFFER_TOO_SMALL) { | |
2180 ptemplate = PORT_ArenaNewArray(arena, CK_ATTRIBUTE, max_attributes); | |
2181 if (ptemplate == NULL) { | |
2182 crv = CKR_HOST_MEMORY; | |
2183 } else { | |
2184 crv = sftkdb_GetObjectTemplate(source, id, | |
2185 ptemplate, &max_attributes); | |
2186 } | |
2187 } | |
2188 if (crv != CKR_OK) { | |
2189 goto loser; | |
2190 } | |
2191 | |
2192 for (i=0; i < max_attributes; i++) { | |
2193 ptemplate[i].pValue = PORT_ArenaAlloc(arena,ptemplate[i].ulValueLen); | |
2194 if (ptemplate[i].pValue == NULL) { | |
2195 crv = CKR_HOST_MEMORY; | |
2196 goto loser; | |
2197 } | |
2198 } | |
2199 crv = (*source->sdb_GetAttributeValue)(source, id, | |
2200 ptemplate, max_attributes); | |
2201 if (crv != CKR_OK) { | |
2202 goto loser; | |
2203 } | |
2204 | |
2205 objectType = sftkdb_getULongFromTemplate(CKA_CLASS, ptemplate, | |
2206 max_attributes); | |
2207 | |
2208 /* | |
2209 * Update Object updates the object template if necessary then returns | |
2210 * whether or not we need to actually write the object out to our target | |
2211 * database. | |
2212 */ | |
2213 if (!handle->updateID) { | |
2214 crv = sftkdb_CreateObject(arena, handle, target, &id, | |
2215 ptemplate, max_attributes); | |
2216 } else { | |
2217 sftkdbUpdateStatus update_status; | |
2218 update_status = sftkdb_updateObjectTemplate(arena, target, | |
2219 objectType, ptemplate, &max_attributes, &id); | |
2220 switch (update_status) { | |
2221 case SFTKDB_ADD_OBJECT: | |
2222 crv = sftkdb_CreateObject(arena, handle, target, &id, | |
2223 ptemplate, max_attributes); | |
2224 break; | |
2225 case SFTKDB_MODIFY_OBJECT: | |
2226 crv = sftkdb_setAttributeValue(arena, handle, target, | |
2227 id, ptemplate, max_attributes); | |
2228 break; | |
2229 case SFTKDB_DO_NOTHING: | |
2230 case SFTKDB_DROP_ATTRIBUTE: | |
2231 break; | |
2232 } | |
2233 } | |
2234 | |
2235 loser: | |
2236 if (arena) { | |
2237 PORT_FreeArena(arena,PR_TRUE); | |
2238 } | |
2239 return crv; | |
2240 } | |
2241 | |
2242 | |
2243 #define MAX_IDS 10 | |
2244 /* | |
2245 * update a new database from an old one, now that we have the key | |
2246 */ | |
2247 CK_RV | |
2248 sftkdb_Update(SFTKDBHandle *handle, SECItem *key) | |
2249 { | |
2250 SDBFind *find = NULL; | |
2251 CK_ULONG idCount = MAX_IDS; | |
2252 CK_OBJECT_HANDLE ids[MAX_IDS]; | |
2253 SECItem *updatePasswordKey = NULL; | |
2254 CK_RV crv, crv2; | |
2255 PRBool inTransaction = PR_FALSE; | |
2256 unsigned int i; | |
2257 | |
2258 if (handle == NULL) { | |
2259 return CKR_OK; | |
2260 } | |
2261 if (handle->update == NULL) { | |
2262 return CKR_OK; | |
2263 } | |
2264 | |
2265 /* | |
2266 * put the whole update under a transaction. This allows us to handle | |
2267 * any possible race conditions between with the updateID check. | |
2268 */ | |
2269 crv = (*handle->db->sdb_Begin)(handle->db); | |
2270 if (crv != CKR_OK) { | |
2271 goto loser; | |
2272 } | |
2273 inTransaction = PR_TRUE; | |
2274 | |
2275 /* some one else has already updated this db */ | |
2276 if (sftkdb_hasUpdate(sftkdb_TypeString(handle), | |
2277 handle->db, handle->updateID)) { | |
2278 crv = CKR_OK; | |
2279 goto done; | |
2280 } | |
2281 | |
2282 updatePasswordKey = sftkdb_GetUpdatePasswordKey(handle); | |
2283 if (updatePasswordKey) { | |
2284 /* pass the source DB key to the legacy code, | |
2285 * so it can decrypt things */ | |
2286 handle->oldKey = updatePasswordKey; | |
2287 } | |
2288 | |
2289 /* find all the objects */ | |
2290 crv = sftkdb_FindObjectsInit(handle, NULL, 0, &find); | |
2291 | |
2292 if (crv != CKR_OK) { | |
2293 goto loser; | |
2294 } | |
2295 while ((crv == CKR_OK) && (idCount == MAX_IDS)) { | |
2296 crv = sftkdb_FindObjects(handle, find, ids, MAX_IDS, &idCount); | |
2297 for (i=0; (crv == CKR_OK) && (i < idCount); i++) { | |
2298 crv = sftkdb_mergeObject(handle, ids[i], key); | |
2299 } | |
2300 } | |
2301 crv2 = sftkdb_FindObjectsFinal(handle, find); | |
2302 if (crv == CKR_OK) crv = crv2; | |
2303 | |
2304 loser: | |
2305 /* no longer need the old key value */ | |
2306 handle->oldKey = NULL; | |
2307 | |
2308 /* update the password - even if we didn't update objects */ | |
2309 if (handle->type == SFTK_KEYDB_TYPE) { | |
2310 SECItem item1, item2; | |
2311 unsigned char data1[SDB_MAX_META_DATA_LEN]; | |
2312 unsigned char data2[SDB_MAX_META_DATA_LEN]; | |
2313 | |
2314 item1.data = data1; | |
2315 item1.len = sizeof(data1); | |
2316 item2.data = data2; | |
2317 item2.len = sizeof(data2); | |
2318 | |
2319 /* if the target db already has a password, skip this. */ | |
2320 crv = (*handle->db->sdb_GetMetaData)(handle->db, "password", | |
2321 &item1, &item2); | |
2322 if (crv == CKR_OK) { | |
2323 goto done; | |
2324 } | |
2325 | |
2326 | |
2327 /* nope, update it from the source */ | |
2328 crv = (*handle->update->sdb_GetMetaData)(handle->update, "password", | |
2329 &item1, &item2); | |
2330 if (crv != CKR_OK) { | |
2331 goto done; | |
2332 } | |
2333 crv = (*handle->db->sdb_PutMetaData)(handle->db, "password", &item1, | |
2334 &item2); | |
2335 if (crv != CKR_OK) { | |
2336 goto done; | |
2337 } | |
2338 } | |
2339 | |
2340 done: | |
2341 /* finally mark this up to date db up to date */ | |
2342 /* some one else has already updated this db */ | |
2343 if (crv == CKR_OK) { | |
2344 crv = sftkdb_putUpdate(sftkdb_TypeString(handle), | |
2345 handle->db, handle->updateID); | |
2346 } | |
2347 | |
2348 if (inTransaction) { | |
2349 if (crv == CKR_OK) { | |
2350 crv = (*handle->db->sdb_Commit)(handle->db); | |
2351 } else { | |
2352 (*handle->db->sdb_Abort)(handle->db); | |
2353 } | |
2354 } | |
2355 if (handle->update) { | |
2356 (*handle->update->sdb_Close)(handle->update); | |
2357 handle->update = NULL; | |
2358 } | |
2359 if (handle->updateID) { | |
2360 PORT_Free(handle->updateID); | |
2361 handle->updateID = NULL; | |
2362 } | |
2363 sftkdb_FreeUpdatePasswordKey(handle); | |
2364 if (updatePasswordKey) { | |
2365 SECITEM_ZfreeItem(updatePasswordKey, PR_TRUE); | |
2366 } | |
2367 handle->updateDBIsInit = PR_FALSE; | |
2368 return crv; | |
2369 } | |
2370 | |
2371 /****************************************************************** | |
2372 * DB handle managing functions. | |
2373 * | |
2374 * These functions are called by softoken to initialize, acquire, | |
2375 * and release database handles. | |
2376 */ | |
2377 | |
2378 const char * | |
2379 sftkdb_GetUpdateID(SFTKDBHandle *handle) | |
2380 { | |
2381 return handle->updateID; | |
2382 } | |
2383 | |
2384 /* release a database handle */ | |
2385 void | |
2386 sftk_freeDB(SFTKDBHandle *handle) | |
2387 { | |
2388 PRInt32 ref; | |
2389 | |
2390 if (!handle) return; | |
2391 ref = PR_ATOMIC_DECREMENT(&handle->ref); | |
2392 if (ref == 0) { | |
2393 sftkdb_CloseDB(handle); | |
2394 } | |
2395 return; | |
2396 } | |
2397 | |
2398 | |
2399 /* | |
2400 * acquire a database handle for a certificate db | |
2401 * (database for public objects) | |
2402 */ | |
2403 SFTKDBHandle * | |
2404 sftk_getCertDB(SFTKSlot *slot) | |
2405 { | |
2406 SFTKDBHandle *dbHandle; | |
2407 | |
2408 PZ_Lock(slot->slotLock); | |
2409 dbHandle = slot->certDB; | |
2410 if (dbHandle) { | |
2411 (void)PR_ATOMIC_INCREMENT(&dbHandle->ref); | |
2412 } | |
2413 PZ_Unlock(slot->slotLock); | |
2414 return dbHandle; | |
2415 } | |
2416 | |
2417 /* | |
2418 * acquire a database handle for a key database | |
2419 * (database for private objects) | |
2420 */ | |
2421 SFTKDBHandle * | |
2422 sftk_getKeyDB(SFTKSlot *slot) | |
2423 { | |
2424 SFTKDBHandle *dbHandle; | |
2425 | |
2426 SKIP_AFTER_FORK(PZ_Lock(slot->slotLock)); | |
2427 dbHandle = slot->keyDB; | |
2428 if (dbHandle) { | |
2429 (void)PR_ATOMIC_INCREMENT(&dbHandle->ref); | |
2430 } | |
2431 SKIP_AFTER_FORK(PZ_Unlock(slot->slotLock)); | |
2432 return dbHandle; | |
2433 } | |
2434 | |
2435 /* | |
2436 * acquire the database for a specific object. NOTE: objectID must point | |
2437 * to a Token object! | |
2438 */ | |
2439 SFTKDBHandle * | |
2440 sftk_getDBForTokenObject(SFTKSlot *slot, CK_OBJECT_HANDLE objectID) | |
2441 { | |
2442 SFTKDBHandle *dbHandle; | |
2443 | |
2444 PZ_Lock(slot->slotLock); | |
2445 dbHandle = objectID & SFTK_KEYDB_TYPE ? slot->keyDB : slot->certDB; | |
2446 if (dbHandle) { | |
2447 (void)PR_ATOMIC_INCREMENT(&dbHandle->ref); | |
2448 } | |
2449 PZ_Unlock(slot->slotLock); | |
2450 return dbHandle; | |
2451 } | |
2452 | |
2453 /* | |
2454 * initialize a new database handle | |
2455 */ | |
2456 static SFTKDBHandle * | |
2457 sftk_NewDBHandle(SDB *sdb, int type) | |
2458 { | |
2459 SFTKDBHandle *handle = PORT_New(SFTKDBHandle); | |
2460 handle->ref = 1; | |
2461 handle->db = sdb; | |
2462 handle->update = NULL; | |
2463 handle->peerDB = NULL; | |
2464 handle->newKey = NULL; | |
2465 handle->oldKey = NULL; | |
2466 handle->updatePasswordKey = NULL; | |
2467 handle->updateID = NULL; | |
2468 handle->type = type; | |
2469 handle->passwordKey.data = NULL; | |
2470 handle->passwordKey.len = 0; | |
2471 handle->passwordLock = NULL; | |
2472 if (type == SFTK_KEYDB_TYPE) { | |
2473 handle->passwordLock = PZ_NewLock(nssILockAttribute); | |
2474 } | |
2475 sdb->app_private = handle; | |
2476 return handle; | |
2477 } | |
2478 | |
2479 /* | |
2480 * reset the key database to it's uninitialized state. This call | |
2481 * will clear all the key entried. | |
2482 */ | |
2483 SECStatus | |
2484 sftkdb_ResetKeyDB(SFTKDBHandle *handle) | |
2485 { | |
2486 CK_RV crv; | |
2487 | |
2488 /* only rest the key db */ | |
2489 if (handle->type != SFTK_KEYDB_TYPE) { | |
2490 return SECFailure; | |
2491 } | |
2492 crv = sftkdb_ResetDB(handle); | |
2493 if (crv != CKR_OK) { | |
2494 /* set error */ | |
2495 return SECFailure; | |
2496 } | |
2497 return SECSuccess; | |
2498 } | |
2499 | |
2500 static PRBool | |
2501 sftk_oldVersionExists(const char *dir, int version) | |
2502 { | |
2503 int i; | |
2504 PRStatus exists = PR_FAILURE; | |
2505 char *file = NULL; | |
2506 | |
2507 for (i=version; i > 1 ; i--) { | |
2508 file = PR_smprintf("%s%d.db",dir,i); | |
2509 if (file == NULL) { | |
2510 continue; | |
2511 } | |
2512 exists = PR_Access(file, PR_ACCESS_EXISTS); | |
2513 PR_smprintf_free(file); | |
2514 if (exists == PR_SUCCESS) { | |
2515 return PR_TRUE; | |
2516 } | |
2517 } | |
2518 return PR_FALSE; | |
2519 } | |
2520 | |
2521 static PRBool | |
2522 sftk_hasLegacyDB(const char *confdir, const char *certPrefix, | |
2523 const char *keyPrefix, int certVersion, int keyVersion) | |
2524 { | |
2525 char *dir; | |
2526 PRBool exists; | |
2527 | |
2528 if (certPrefix == NULL) { | |
2529 certPrefix = ""; | |
2530 } | |
2531 | |
2532 if (keyPrefix == NULL) { | |
2533 keyPrefix = ""; | |
2534 } | |
2535 | |
2536 dir= PR_smprintf("%s/%scert", confdir, certPrefix); | |
2537 if (dir == NULL) { | |
2538 return PR_FALSE; | |
2539 } | |
2540 | |
2541 exists = sftk_oldVersionExists(dir, certVersion); | |
2542 PR_smprintf_free(dir); | |
2543 if (exists) { | |
2544 return PR_TRUE; | |
2545 } | |
2546 | |
2547 dir= PR_smprintf("%s/%skey", confdir, keyPrefix); | |
2548 if (dir == NULL) { | |
2549 return PR_FALSE; | |
2550 } | |
2551 | |
2552 exists = sftk_oldVersionExists(dir, keyVersion); | |
2553 PR_smprintf_free(dir); | |
2554 return exists; | |
2555 } | |
2556 | |
2557 /* | |
2558 * initialize certificate and key database handles as a pair. | |
2559 * | |
2560 * This function figures out what type of database we are opening and | |
2561 * calls the appropriate low level function to open the database. | |
2562 * It also figures out whether or not to setup up automatic update. | |
2563 */ | |
2564 CK_RV | |
2565 sftk_DBInit(const char *configdir, const char *certPrefix, | |
2566 const char *keyPrefix, const char *updatedir, | |
2567 const char *updCertPrefix, const char *updKeyPrefix, | |
2568 const char *updateID, PRBool readOnly, PRBool noCertDB, | |
2569 PRBool noKeyDB, PRBool forceOpen, PRBool isFIPS, | |
2570 SFTKDBHandle **certDB, SFTKDBHandle **keyDB) | |
2571 { | |
2572 const char *confdir; | |
2573 NSSDBType dbType = NSS_DB_TYPE_NONE; | |
2574 char *appName = NULL; | |
2575 SDB *keySDB, *certSDB; | |
2576 CK_RV crv = CKR_OK; | |
2577 int flags = SDB_RDONLY; | |
2578 PRBool newInit = PR_FALSE; | |
2579 PRBool needUpdate = PR_FALSE; | |
2580 | |
2581 if (!readOnly) { | |
2582 flags = SDB_CREATE; | |
2583 } | |
2584 | |
2585 *certDB = NULL; | |
2586 *keyDB = NULL; | |
2587 | |
2588 if (noKeyDB && noCertDB) { | |
2589 return CKR_OK; | |
2590 } | |
2591 confdir = _NSSUTIL_EvaluateConfigDir(configdir, &dbType, &appName); | |
2592 | |
2593 /* | |
2594 * now initialize the appropriate database | |
2595 */ | |
2596 switch (dbType) { | |
2597 case NSS_DB_TYPE_LEGACY: | |
2598 crv = sftkdbCall_open(confdir, certPrefix, keyPrefix, 8, 3, flags, | |
2599 isFIPS, noCertDB? NULL : &certSDB, noKeyDB ? NULL: &keySDB); | |
2600 break; | |
2601 case NSS_DB_TYPE_MULTIACCESS: | |
2602 crv = sftkdbCall_open(configdir, certPrefix, keyPrefix, 8, 3, flags, | |
2603 isFIPS, noCertDB? NULL : &certSDB, noKeyDB ? NULL: &keySDB); | |
2604 break; | |
2605 case NSS_DB_TYPE_SQL: | |
2606 case NSS_DB_TYPE_EXTERN: /* SHOULD open a loadable db */ | |
2607 crv = s_open(confdir, certPrefix, keyPrefix, 9, 4, flags, | |
2608 noCertDB? NULL : &certSDB, noKeyDB ? NULL : &keySDB, &newInit); | |
2609 | |
2610 /* | |
2611 * if we failed to open the DB's read only, use the old ones if | |
2612 * the exists. | |
2613 */ | |
2614 if (crv != CKR_OK) { | |
2615 if ((flags == SDB_RDONLY) && | |
2616 sftk_hasLegacyDB(confdir, certPrefix, keyPrefix, 8, 3)) { | |
2617 /* we have legacy databases, if we failed to open the new format | |
2618 * DB's read only, just use the legacy ones */ | |
2619 crv = sftkdbCall_open(confdir, certPrefix, | |
2620 keyPrefix, 8, 3, flags, isFIPS, | |
2621 noCertDB? NULL : &certSDB, noKeyDB ? NULL : &keySDB); | |
2622 } | |
2623 /* Handle the database merge case. | |
2624 * | |
2625 * For the merge case, we need help from the application. Only | |
2626 * the application knows where the old database is, and what unique | |
2627 * identifier it has associated with it. | |
2628 * | |
2629 * If the client supplies these values, we use them to determine | |
2630 * if we need to update. | |
2631 */ | |
2632 } else if ( | |
2633 /* both update params have been supplied */ | |
2634 updatedir && *updatedir && updateID && *updateID | |
2635 /* old dbs exist? */ | |
2636 && sftk_hasLegacyDB(updatedir, updCertPrefix, updKeyPrefix, 8, 3) | |
2637 /* and they have not yet been updated? */ | |
2638 && ((noKeyDB || !sftkdb_hasUpdate("key", keySDB, updateID)) | |
2639 || (noCertDB || !sftkdb_hasUpdate("cert", certSDB, updateID)))) { | |
2640 /* we need to update */ | |
2641 confdir = updatedir; | |
2642 certPrefix = updCertPrefix; | |
2643 keyPrefix = updKeyPrefix; | |
2644 needUpdate = PR_TRUE; | |
2645 } else if (newInit) { | |
2646 /* if the new format DB was also a newly created DB, and we | |
2647 * succeeded, then need to update that new database with data | |
2648 * from the existing legacy DB */ | |
2649 if (sftk_hasLegacyDB(confdir, certPrefix, keyPrefix, 8, 3)) { | |
2650 needUpdate = PR_TRUE; | |
2651 } | |
2652 } | |
2653 break; | |
2654 default: | |
2655 crv = CKR_GENERAL_ERROR; /* can't happen, EvaluationConfigDir MUST | |
2656 * return one of the types we already | |
2657 * specified. */ | |
2658 } | |
2659 if (crv != CKR_OK) { | |
2660 goto done; | |
2661 } | |
2662 if (!noCertDB) { | |
2663 *certDB = sftk_NewDBHandle(certSDB, SFTK_CERTDB_TYPE); | |
2664 } else { | |
2665 *certDB = NULL; | |
2666 } | |
2667 if (!noKeyDB) { | |
2668 *keyDB = sftk_NewDBHandle(keySDB, SFTK_KEYDB_TYPE); | |
2669 } else { | |
2670 *keyDB = NULL; | |
2671 } | |
2672 | |
2673 /* link them together */ | |
2674 if (*certDB) { | |
2675 (*certDB)->peerDB = *keyDB; | |
2676 } | |
2677 if (*keyDB) { | |
2678 (*keyDB)->peerDB = *certDB; | |
2679 } | |
2680 | |
2681 /* | |
2682 * if we need to update, open the legacy database and | |
2683 * mark the handle as needing update. | |
2684 */ | |
2685 if (needUpdate) { | |
2686 SDB *updateCert = NULL; | |
2687 SDB *updateKey = NULL; | |
2688 CK_RV crv2; | |
2689 | |
2690 crv2 = sftkdbCall_open(confdir, certPrefix, keyPrefix, 8, 3, flags, | |
2691 isFIPS, noCertDB ? NULL : &updateCert, | |
2692 noKeyDB ? NULL : &updateKey); | |
2693 if (crv2 == CKR_OK) { | |
2694 if (*certDB) { | |
2695 (*certDB)->update = updateCert; | |
2696 (*certDB)->updateID = updateID && *updateID | |
2697 ? PORT_Strdup(updateID) : NULL; | |
2698 updateCert->app_private = (*certDB); | |
2699 } | |
2700 if (*keyDB) { | |
2701 PRBool tokenRemoved = PR_FALSE; | |
2702 (*keyDB)->update = updateKey; | |
2703 (*keyDB)->updateID = updateID && *updateID ? | |
2704 PORT_Strdup(updateID) : NULL; | |
2705 updateKey->app_private = (*keyDB); | |
2706 (*keyDB)->updateDBIsInit = PR_TRUE; | |
2707 (*keyDB)->updateDBIsInit = | |
2708 (sftkdb_HasPasswordSet(*keyDB) == SECSuccess) ? | |
2709 PR_TRUE : PR_FALSE; | |
2710 /* if the password on the key db is NULL, kick off our update | |
2711 * chain of events */ | |
2712 sftkdb_CheckPassword((*keyDB), "", &tokenRemoved); | |
2713 } else { | |
2714 /* we don't have a key DB, update the certificate DB now */ | |
2715 sftkdb_Update(*certDB, NULL); | |
2716 } | |
2717 } | |
2718 } | |
2719 done: | |
2720 if (appName) { | |
2721 PORT_Free(appName); | |
2722 } | |
2723 return forceOpen ? CKR_OK : crv; | |
2724 } | |
2725 | |
2726 CK_RV | |
2727 sftkdb_Shutdown(void) | |
2728 { | |
2729 s_shutdown(); | |
2730 sftkdbCall_Shutdown(); | |
2731 return CKR_OK; | |
2732 } | |
2733 | |
OLD | NEW |