Import from 1.9a8 tarball
[mozilla-nss.git] / security / nss / lib / softoken / sftkdb.c
blobebb0e5160073922c37171a75d4df989838646b86
1 /* ***** BEGIN LICENSE BLOCK *****
2 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
4 * The contents of this file are subject to the Mozilla Public License Version
5 * 1.1 (the "License"); you may not use this file except in compliance with
6 * the License. You may obtain a copy of the License at
7 * http://www.mozilla.org/MPL/
9 * Software distributed under the License is distributed on an "AS IS" basis,
10 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
11 * for the specific language governing rights and limitations under the
12 * License.
14 * The Original Code is the Netscape security libraries.
16 * The Initial Developer of the Original Code is
17 * Netscape Communications Corporation.
18 * Portions created by the Initial Developer are Copyright (C) 1994-2000
19 * the Initial Developer. All Rights Reserved.
21 * Contributor(s):
23 * Alternatively, the contents of this file may be used under the terms of
24 * either the GNU General Public License Version 2 or later (the "GPL"), or
25 * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
26 * in which case the provisions of the GPL or the LGPL are applicable instead
27 * of those above. If you wish to allow use of your version of this file only
28 * under the terms of either the GPL or the LGPL, and not to allow others to
29 * use your version of this file under the terms of the MPL, indicate your
30 * decision by deleting the provisions above and replace them with the notice
31 * and other provisions required by the GPL or the LGPL. If you do not delete
32 * the provisions above, a recipient may use your version of this file under
33 * the terms of any one of the MPL, the GPL or the LGPL.
35 * ***** END LICENSE BLOCK ***** */
36 /*
37 * The following code handles the storage of PKCS 11 modules used by the
38 * NSS. For the rest of NSS, only one kind of database handle exists:
40 * SFTKDBHandle
42 * There is one SFTKDBHandle for the each key database and one for each cert
43 * database. These databases are opened as associated pairs, one pair per
44 * slot. SFTKDBHandles are reference counted objects.
46 * Each SFTKDBHandle points to a low level database handle (SDB). This handle
47 * represents the underlying physical database. These objects are not
48 * reference counted, an are 'owned' by their respective SFTKDBHandles.
52 #include "sftkdb.h"
53 #include "pkcs11t.h"
54 #include "pkcs11i.h"
55 #include "sdb.h"
56 #include "prprf.h"
57 #include "secmodt.h"
58 #include "sftkpars.h"
59 #include "pratom.h"
60 #include "blapi.h"
61 #include "secoid.h"
62 #include "sechash.h"
63 #include "lowpbe.h"
64 #include "secdert.h"
65 #include "prsystem.h"
66 #include "lgglue.h"
69 * private defines
71 struct SFTKDBHandleStr {
72 SDB *db;
73 PRInt32 ref;
74 CK_OBJECT_HANDLE type;
75 SECItem passwordKey;
76 SECItem *newKey;
77 PZLock *passwordLock;
78 SFTKDBHandle *peerDB;
79 SDB *update;
82 #define SFTK_KEYDB_TYPE 0x40000000
83 #define SFTK_CERTDB_TYPE 0x00000000
84 #define SFTK_OBJ_TYPE_MASK 0xc0000000
85 #define SFTK_OBJ_ID_MASK (~SFTK_OBJ_TYPE_MASK)
86 #define SFTK_TOKEN_TYPE 0x80000000
88 static SECStatus sftkdb_decrypt(SECItem *passKey, SECItem *cipherText,
89 SECItem **plainText);
90 static SECStatus sftkdb_encrypt(PLArenaPool *arena, SECItem *passKey,
91 SECItem *plainText, SECItem **cipherText);
95 * We want all databases to have the same binary representation independent of
96 * endianness or length of the host architecture. In general PKCS #11 attributes
97 * are endian/length independent except those attributes that pass CK_ULONG.
99 * The following functions fixes up the CK_ULONG type attributes so that the data
100 * base sees a machine independent view. CK_ULONGs are stored as 4 byte network
101 * byte order values (big endian).
103 #define DB_ULONG_SIZE 4
104 #define BBP 8
106 static PRBool
107 sftkdb_isULONG(CK_ATTRIBUTE_TYPE type)
109 switch(type) {
110 case CKA_CLASS:
111 case CKA_CERTIFICATE_TYPE:
112 case CKA_CERTIFICATE_CATEGORY:
113 case CKA_KEY_TYPE:
114 case CKA_JAVA_MIDP_SECURITY_DOMAIN:
116 case CKA_TRUST_DIGITAL_SIGNATURE:
117 case CKA_TRUST_NON_REPUDIATION:
118 case CKA_TRUST_KEY_ENCIPHERMENT:
119 case CKA_TRUST_DATA_ENCIPHERMENT:
120 case CKA_TRUST_KEY_AGREEMENT:
121 case CKA_TRUST_KEY_CERT_SIGN:
122 case CKA_TRUST_CRL_SIGN:
124 case CKA_TRUST_SERVER_AUTH:
125 case CKA_TRUST_CLIENT_AUTH:
126 case CKA_TRUST_CODE_SIGNING:
127 case CKA_TRUST_EMAIL_PROTECTION:
128 case CKA_TRUST_IPSEC_END_SYSTEM:
129 case CKA_TRUST_IPSEC_TUNNEL:
130 case CKA_TRUST_IPSEC_USER:
131 case CKA_TRUST_TIME_STAMPING:
132 case CKA_TRUST_STEP_UP_APPROVED:
133 return PR_TRUE;
134 default:
135 break;
137 return PR_FALSE;
141 /* are the attributes private? */
142 static PRBool
143 sftkdb_isPrivate(CK_ATTRIBUTE_TYPE type)
145 switch(type) {
146 case CKA_VALUE:
147 case CKA_PRIVATE_EXPONENT:
148 case CKA_PRIME_1:
149 case CKA_PRIME_2:
150 case CKA_EXPONENT_1:
151 case CKA_EXPONENT_2:
152 case CKA_COEFFICIENT:
153 return PR_TRUE;
154 default:
155 break;
157 return PR_FALSE;
161 * fix up the input templates. Our fixed up ints are stored in data and must
162 * be freed by the caller. The new template must also be freed. If there are no
163 * CK_ULONG attributes, the orignal template is passed in as is.
165 static CK_ATTRIBUTE *
166 sftkdb_fixupTemplateIn(const CK_ATTRIBUTE *template, int count,
167 unsigned char **dataOut)
169 int i,j;
170 int ulongCount = 0;
171 unsigned char *data;
172 CK_ATTRIBUTE *ntemplate;
174 *dataOut = NULL;
176 /* first count the number of CK_ULONG attributes */
177 for (i=0; i < count; i++) {
178 /* Don't 'fixup' NULL values */
179 if (!template[i].pValue) {
180 continue;
182 if (template[i].ulValueLen == sizeof (CK_ULONG)) {
183 if ( sftkdb_isULONG(template[i].type)) {
184 ulongCount++;
188 /* no attributes to fixup, just call on through */
189 if (ulongCount == 0) {
190 return (CK_ATTRIBUTE *)template;
193 /* allocate space for new ULONGS */
194 data = (unsigned char *)PORT_Alloc(DB_ULONG_SIZE*ulongCount);
195 if (!data) {
196 return NULL;
199 /* allocate new template */
200 ntemplate = PORT_NewArray(CK_ATTRIBUTE,count);
201 if (!ntemplate) {
202 PORT_Free(data);
203 return NULL;
205 *dataOut = data;
206 /* copy the old template, fixup the actual ulongs */
207 for (i=0; i < count; i++) {
208 ntemplate[i] = template[i];
209 /* Don't 'fixup' NULL values */
210 if (!template[i].pValue) {
211 continue;
213 if (template[i].ulValueLen == sizeof (CK_ULONG)) {
214 if ( sftkdb_isULONG(template[i].type) ) {
215 CK_ULONG value = *(CK_ULONG *) template[i].pValue;
216 for (j=0; j < DB_ULONG_SIZE; j++) {
217 data[j] = (value >> (DB_ULONG_SIZE-1-j)*BBP) & 0xff;
219 ntemplate[i].pValue = data;
220 ntemplate[i].ulValueLen = DB_ULONG_SIZE;
221 data += DB_ULONG_SIZE;
225 return ntemplate;
231 * fix up returned data. NOTE: sftkdb_fixupTemplateIn has already allocated
232 * separate data sections for the database ULONG values.
234 static CK_RV
235 sftkdb_fixupTemplateOut(CK_ATTRIBUTE *template, CK_ATTRIBUTE *ntemplate,
236 int count, SFTKDBHandle *handle)
238 int i,j;
239 CK_RV crv = CKR_OK;
241 for (i=0; i < count; i++) {
242 CK_ULONG length = template[i].ulValueLen;
243 template[i].ulValueLen = ntemplate[i].ulValueLen;
244 /* fixup ulongs */
245 if (ntemplate[i].ulValueLen == DB_ULONG_SIZE) {
246 if (sftkdb_isULONG(template[i].type)) {
247 if (template[i].pValue) {
248 CK_ULONG value = 0;
249 unsigned char *data;
251 data = (unsigned char *)ntemplate[i].pValue;
252 for (j=0; j < DB_ULONG_SIZE; j++) {
253 value |= (((CK_ULONG)data[j]) << (DB_ULONG_SIZE-1-j)*BBP);
255 if (length < sizeof(CK_ULONG)) {
256 template[i].ulValueLen = -1;
257 crv = CKR_BUFFER_TOO_SMALL;
258 continue;
260 PORT_Memcpy(template[i].pValue,&value,sizeof(CK_ULONG));
262 template[i].ulValueLen = sizeof(CK_ULONG);
265 /* fixup private attributes */
266 if ((handle != NULL) && (handle->type == SFTK_KEYDB_TYPE) &&
267 (template[i].pValue != NULL) && (template[i].ulValueLen != -1)
268 && sftkdb_isPrivate(ntemplate[i].type)) {
269 /* we have a private attribute */
270 /* This code depends on the fact that the cipherText is bigger
271 * than the plain text */
272 SECItem cipherText;
273 SECItem *plainText;
274 SECStatus rv;
276 cipherText.data = ntemplate[i].pValue;
277 cipherText.len = ntemplate[i].ulValueLen;
278 PZ_Lock(handle->passwordLock);
279 if (handle->passwordKey.data == NULL) {
280 PZ_Unlock(handle->passwordLock);
281 template[i].ulValueLen = -1;
282 crv = CKR_USER_NOT_LOGGED_IN;
283 continue;
285 rv = sftkdb_decrypt(&handle->passwordKey, &cipherText, &plainText);
286 PZ_Unlock(handle->passwordLock);
287 if (rv != SECSuccess) {
288 PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
289 template[i].ulValueLen = -1;
290 crv = CKR_GENERAL_ERROR;
291 continue;
293 PORT_Assert(template[i].ulValueLen >= plainText->len);
294 if (template[i].ulValueLen < plainText->len) {
295 SECITEM_FreeItem(plainText,PR_TRUE);
296 PORT_Memset(template[i].pValue, 0, template[i].ulValueLen);
297 template[i].ulValueLen = -1;
298 crv = CKR_GENERAL_ERROR;
299 continue;
302 /* copy the plain text back into the template */
303 PORT_Memcpy(template[i].pValue, plainText->data, plainText->len);
304 template[i].ulValueLen = plainText->len;
305 SECITEM_FreeItem(plainText,PR_TRUE);
308 return crv;
311 static CK_RV
312 sftkdb_CreateObject(SFTKDBHandle *handle, SDB *db, CK_OBJECT_HANDLE *objectID,
313 CK_ATTRIBUTE *template, CK_ULONG count)
315 PRBool inTransaction = PR_FALSE;
316 CK_RV crv;
318 crv = (*db->sdb_Begin)(db);
319 if (crv != CKR_OK) {
320 goto loser;
322 inTransaction = PR_TRUE;
323 crv = (*db->sdb_CreateObject)(db, objectID, template, count);
324 if (crv != CKR_OK) {
325 goto loser;
327 crv = (*db->sdb_Commit)(db);
328 inTransaction = PR_FALSE;
330 loser:
331 if (inTransaction) {
332 (*handle->db->sdb_Abort)(handle->db);
333 /* It is trivial to show the following code cannot
334 * happen unless something is horribly wrong with our compilier or
335 * hardware */
336 PORT_Assert(crv != CKR_OK);
337 if (crv == CKR_OK) crv = CKR_GENERAL_ERROR;
339 return crv;
342 CK_ATTRIBUTE *
343 sftk_ExtractTemplate(PLArenaPool *arena, SFTKObject *object,
344 SFTKDBHandle *handle,CK_ULONG *pcount,
345 CK_RV *crv)
347 int count;
348 CK_ATTRIBUTE *template;
349 int i, templateIndex;
350 SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
352 *crv = CKR_OK;
354 if (sessObject == NULL) {
355 *crv = CKR_GENERAL_ERROR; /* internal programming error */
356 return NULL;
359 PZ_Lock(sessObject->attributeLock);
360 count = 0;
361 for (i=0; i < sessObject->hashSize; i++) {
362 SFTKAttribute *attr;
363 for (attr=sessObject->head[i]; attr; attr=attr->next) {
364 count++;
367 template = PORT_ArenaNewArray(arena, CK_ATTRIBUTE, count);
368 if (template == NULL) {
369 PZ_Unlock(sessObject->attributeLock);
370 *crv = CKR_HOST_MEMORY;
371 return NULL;
373 templateIndex = 0;
374 for (i=0; i < sessObject->hashSize; i++) {
375 SFTKAttribute *attr;
376 for (attr=sessObject->head[i]; attr; attr=attr->next) {
377 CK_ATTRIBUTE *tp = &template[templateIndex++];
378 /* copy the attribute */
379 *tp = attr->attrib;
381 /* fixup ULONG s */
382 if ((tp->ulValueLen == sizeof (CK_ULONG)) &&
383 (sftkdb_isULONG(tp->type)) ) {
384 CK_ULONG value = *(CK_ULONG *) tp->pValue;
385 unsigned char *data;
386 int j;
388 tp->pValue = PORT_ArenaAlloc(arena, DB_ULONG_SIZE);
389 data = (unsigned char *)tp->pValue;
390 if (data == NULL) {
391 *crv = CKR_HOST_MEMORY;
392 break;
394 for (j=0; j < DB_ULONG_SIZE; j++) {
395 data[j] = (value >> (DB_ULONG_SIZE-1-j)*BBP) & 0xff;
397 tp->ulValueLen = DB_ULONG_SIZE;
400 /* encrypt private attributes */
401 if ((handle != NULL) && (handle->type == SFTK_KEYDB_TYPE) &&
402 sftkdb_isPrivate(tp->type)) {
404 /* we have a private attribute */
405 SECItem *cipherText;
406 SECItem plainText;
407 SECStatus rv;
409 plainText.data = tp->pValue;
410 plainText.len = tp->ulValueLen;
411 PZ_Lock(handle->passwordLock);
412 if (handle->passwordKey.data == NULL) {
413 PZ_Unlock(handle->passwordLock);
414 *crv = CKR_USER_NOT_LOGGED_IN;
415 break;
417 rv = sftkdb_encrypt(arena, &handle->passwordKey, &plainText,
418 &cipherText);
419 PZ_Unlock(handle->passwordLock);
420 if (rv == SECSuccess) {
421 tp->pValue = cipherText->data;
422 tp->ulValueLen = cipherText->len;
423 } else {
424 *crv = CKR_GENERAL_ERROR; /* better error code here? */
425 break;
427 PORT_Memset(plainText.data, 0, plainText.len);
431 PORT_Assert(templateIndex <= count);
432 PZ_Unlock(sessObject->attributeLock);
434 if (*crv != CKR_OK) {
435 return NULL;
437 if (pcount) {
438 *pcount = count;
440 return template;
444 #define GET_SDB(handle) ((handle)->update ? (handle)->update : (handle)->db)
446 CK_RV
447 sftkdb_write(SFTKDBHandle *handle, SFTKObject *object,
448 CK_OBJECT_HANDLE *objectID)
450 CK_ATTRIBUTE *template;
451 PLArenaPool *arena;
452 CK_ULONG count;
453 CK_RV crv;
454 SDB *db;
456 *objectID = CK_INVALID_HANDLE;
458 if (handle == NULL) {
459 return CKR_TOKEN_WRITE_PROTECTED;
461 db = GET_SDB(handle);
463 arena = PORT_NewArena(256);
464 if (arena == NULL) {
465 return CKR_HOST_MEMORY;
468 template = sftk_ExtractTemplate(arena, object, handle, &count, &crv);
469 if (!template) {
470 goto loser;
473 crv = sftkdb_CreateObject(handle, db, objectID, template, count);
475 loser:
476 if (arena) {
477 PORT_FreeArena(arena,PR_FALSE);
479 if (crv == CKR_OK) {
480 *objectID |= (handle->type | SFTK_TOKEN_TYPE);
482 return crv;
488 CK_RV
489 sftkdb_FindObjectsInit(SFTKDBHandle *handle, const CK_ATTRIBUTE *template,
490 CK_ULONG count, SDBFind **find)
492 unsigned char *data = NULL;
493 CK_ATTRIBUTE *ntemplate = NULL;
494 CK_RV crv;
495 SDB *db;
497 if (handle == NULL) {
498 return CKR_OK;
500 db = GET_SDB(handle);
502 if (count != 0) {
503 ntemplate = sftkdb_fixupTemplateIn(template, count, &data);
504 if (ntemplate == NULL) {
505 return CKR_HOST_MEMORY;
509 crv = (*db->sdb_FindObjectsInit)(db, ntemplate,
510 count, find);
511 if (data) {
512 PORT_Free(ntemplate);
513 PORT_Free(data);
515 return crv;
518 CK_RV
519 sftkdb_FindObjects(SFTKDBHandle *handle, SDBFind *find,
520 CK_OBJECT_HANDLE *ids, int arraySize, CK_ULONG *count)
522 CK_RV crv;
523 SDB *db;
525 if (handle == NULL) {
526 *count = 0;
527 return CKR_OK;
529 db = GET_SDB(handle);
531 crv = (*db->sdb_FindObjects)(db, find, ids,
532 arraySize, count);
533 if (crv == CKR_OK) {
534 int i;
535 for (i=0; i < *count; i++) {
536 ids[i] |= (handle->type | SFTK_TOKEN_TYPE);
539 return crv;
542 CK_RV sftkdb_FindObjectsFinal(SFTKDBHandle *handle, SDBFind *find)
544 SDB *db;
545 if (handle == NULL) {
546 return CKR_OK;
548 db = GET_SDB(handle);
549 return (*db->sdb_FindObjectsFinal)(db, find);
552 CK_RV
553 sftkdb_GetAttributeValue(SFTKDBHandle *handle, CK_OBJECT_HANDLE object_id,
554 CK_ATTRIBUTE *template, CK_ULONG count)
556 CK_RV crv,crv2;
557 CK_ATTRIBUTE *ntemplate;
558 unsigned char *data = NULL;
559 SDB *db;
561 if (handle == NULL) {
562 return CKR_GENERAL_ERROR;
565 /* short circuit common attributes */
566 if (count == 1 &&
567 (template[0].type == CKA_TOKEN ||
568 template[0].type == CKA_PRIVATE ||
569 template[0].type == CKA_SENSITIVE)) {
570 CK_BBOOL boolVal = CK_TRUE;
572 if (template[0].pValue == NULL) {
573 template[0].ulValueLen = sizeof(CK_BBOOL);
574 return CKR_OK;
576 if (template[0].ulValueLen < sizeof(CK_BBOOL)) {
577 template[0].ulValueLen = -1;
578 return CKR_BUFFER_TOO_SMALL;
581 if ((template[0].type == CKA_PRIVATE) &&
582 (handle->type != SFTK_KEYDB_TYPE)) {
583 boolVal = CK_FALSE;
585 if ((template[0].type == CKA_SENSITIVE) &&
586 (handle->type != SFTK_KEYDB_TYPE)) {
587 boolVal = CK_FALSE;
589 *(CK_BBOOL *)template[0].pValue = boolVal;
590 template[0].ulValueLen = sizeof(CK_BBOOL);
591 return CKR_OK;
594 db = GET_SDB(handle);
595 /* nothing to do */
596 if (count == 0) {
597 return CKR_OK;
599 ntemplate = sftkdb_fixupTemplateIn(template, count, &data);
600 if (ntemplate == NULL) {
601 return CKR_HOST_MEMORY;
603 object_id &= SFTK_OBJ_ID_MASK;
604 crv = (*db->sdb_GetAttributeValue)(db, object_id,
605 ntemplate, count);
606 crv2 = sftkdb_fixupTemplateOut(template, ntemplate, count, handle);
607 if (crv == CKR_OK) crv = crv2;
608 if (data) {
609 PORT_Free(ntemplate);
610 PORT_Free(data);
612 return crv;
616 CK_RV
617 sftkdb_SetAttributeValue(SFTKDBHandle *handle, CK_OBJECT_HANDLE object_id,
618 const CK_ATTRIBUTE *template, CK_ULONG count)
620 CK_RV crv = CKR_OK;
621 CK_ATTRIBUTE *ntemplate;
622 unsigned char *data = NULL;
623 SDB *db;
625 if (handle == NULL) {
626 return CKR_TOKEN_WRITE_PROTECTED;
629 db = GET_SDB(handle);
630 /* nothing to do */
631 if (count == 0) {
632 return CKR_OK;
634 ntemplate = sftkdb_fixupTemplateIn(template, count, &data);
635 if (ntemplate == NULL) {
636 return CKR_HOST_MEMORY;
638 object_id &= SFTK_OBJ_ID_MASK;
639 crv = (*db->sdb_Begin)(db);
640 if (crv != CKR_OK) {
641 goto loser;
643 crv = (*db->sdb_SetAttributeValue)(db, object_id,
644 ntemplate, count);
645 if (crv != CKR_OK) {
646 goto loser;
648 crv = (*db->sdb_Commit)(db);
649 loser:
650 if (crv != CKR_OK) {
651 (*db->sdb_Abort)(db);
653 if (data) {
654 PORT_Free(ntemplate);
655 PORT_Free(data);
657 return crv;
660 CK_RV
661 sftkdb_DestroyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE object_id)
663 CK_RV crv = CKR_OK;
664 SDB *db;
666 if (handle == NULL) {
667 return CKR_TOKEN_WRITE_PROTECTED;
669 db = GET_SDB(handle);
670 object_id &= SFTK_OBJ_ID_MASK;
671 crv = (*db->sdb_Begin)(db);
672 if (crv != CKR_OK) {
673 goto loser;
675 crv = (*db->sdb_DestroyObject)(db, object_id);
676 if (crv != CKR_OK) {
677 goto loser;
679 crv = (*db->sdb_Commit)(db);
680 loser:
681 if (crv != CKR_OK) {
682 (*db->sdb_Abort)(db);
684 return crv;
687 CK_RV
688 sftkdb_CloseDB(SFTKDBHandle *handle)
690 if (handle == NULL) {
691 return CKR_OK;
693 if (handle->update) {
694 (*handle->update->sdb_Close)(handle->update);
696 if (handle->db) {
697 (*handle->db->sdb_Close)(handle->db);
699 if (handle->passwordLock) {
700 PZ_DestroyLock(handle->passwordLock);
702 PORT_Free(handle);
703 return CKR_OK;
707 * reset a database to it's uninitialized state.
709 static CK_RV
710 sftkdb_ResetDB(SFTKDBHandle *handle)
712 CK_RV crv = CKR_OK;
713 SDB *db;
714 if (handle == NULL) {
715 return CKR_TOKEN_WRITE_PROTECTED;
717 db = GET_SDB(handle);
718 crv = (*db->sdb_Begin)(db);
719 if (crv != CKR_OK) {
720 goto loser;
722 crv = (*db->sdb_Reset)(db);
723 if (crv != CKR_OK) {
724 goto loser;
726 crv = (*db->sdb_Commit)(db);
727 loser:
728 if (crv != CKR_OK) {
729 (*db->sdb_Abort)(db);
731 return crv;
735 CK_RV
736 sftkdb_Begin(SFTKDBHandle *handle)
738 CK_RV crv = CKR_OK;
739 SDB *db;
741 if (handle == NULL) {
742 return CKR_OK;
744 db = GET_SDB(handle);
745 if (db) {
746 crv = (*db->sdb_Begin)(db);
748 return crv;
751 CK_RV
752 sftkdb_Commit(SFTKDBHandle *handle)
754 CK_RV crv = CKR_OK;
755 SDB *db;
757 if (handle == NULL) {
758 return CKR_OK;
760 db = GET_SDB(handle);
761 if (db) {
762 (*db->sdb_Commit)(db);
764 return crv;
767 CK_RV
768 sftkdb_Abort(SFTKDBHandle *handle)
770 CK_RV crv = CKR_OK;
771 SDB *db;
773 if (handle == NULL) {
774 return CKR_OK;
776 db = GET_SDB(handle);
777 if (db) {
778 crv = (db->sdb_Abort)(db);
780 return crv;
784 /****************************************************************
786 * Secmod database.
788 * The new secmod database is simply a text file with each of the module
789 * entries. in the following form:
792 * # This is a comment The next line is the library to load
793 * library=libmypkcs11.so
794 * name="My PKCS#11 module"
795 * params="my library's param string"
796 * nss="NSS parameters"
797 * other="parameters for other libraries and applications"
799 * library=libmynextpk11.so
800 * name="My other PKCS#11 module"
803 static char *
804 sftkdb_quote(const char *string, char quote)
806 char *newString = 0;
807 int escapes = 0, size = 0;
808 const char *src;
809 char *dest;
811 size=2;
812 for (src=string; *src ; src++) {
813 if ((*src == quote) || (*src == '\\')) escapes++;
814 size++;
817 dest = newString = PORT_ZAlloc(escapes+size+1);
818 if (newString == NULL) {
819 return NULL;
822 *dest++=quote;
823 for (src=string; *src; src++,dest++) {
824 if ((*src == '\\') || (*src == quote)) {
825 *dest++ = '\\';
827 *dest = *src;
829 *dest=quote;
831 return newString;
835 * Smart string cat functions. Automatically manage the memory.
836 * The first parameter is the source string. If it's null, we
837 * allocate memory for it. If it's not, we reallocate memory
838 * so the the concanenated string fits.
840 static char *
841 sftkdb_DupnCat(char *baseString, const char *str, int str_len)
843 int len = (baseString ? PORT_Strlen(baseString) : 0) + 1;
844 char *newString;
846 len += str_len;
847 newString = (char *) PORT_Realloc(baseString,len);
848 if (newString == NULL) {
849 PORT_Free(baseString);
850 return NULL;
852 if (baseString == NULL) *newString = 0;
853 return PORT_Strncat(newString,str, str_len);
856 /* Same as sftkdb_DupnCat except it concatenates the full string, not a
857 * partial one */
858 static char *
859 sftkdb_DupCat(char *baseString, const char *str)
861 return sftkdb_DupnCat(baseString, str, PORT_Strlen(str));
864 /* function to free up all the memory associated with a null terminated
865 * array of module specs */
866 static SECStatus
867 sftkdb_releaseSpecList(char **moduleSpecList)
869 if (moduleSpecList) {
870 char **index;
871 for(index = moduleSpecList; *index; index++) {
872 PORT_Free(*index);
874 PORT_Free(moduleSpecList);
876 return SECSuccess;
879 #define SECMOD_STEP 10
880 static SECStatus
881 sftkdb_growList(char ***pModuleList, int *useCount, int last)
883 char **newModuleList;
885 *useCount += SECMOD_STEP;
886 newModuleList = (char **)PORT_Realloc(*pModuleList,
887 *useCount*sizeof(char *));
888 if (newModuleList == NULL) {
889 return SECFailure;
891 PORT_Memset(&newModuleList[last],0, sizeof(char *)*SECMOD_STEP);
892 *pModuleList = newModuleList;
893 return SECSuccess;
896 static
897 char *sftk_getOldSecmodName(const char *dbname,const char *filename)
899 char *file = NULL;
900 char *dirPath = PORT_Strdup(dbname);
901 char *sep;
903 sep = PORT_Strrchr(dirPath,*PATH_SEPARATOR);
904 #ifdef WINDOWS
905 if (!sep) {
906 sep = PORT_Strrchr(dirPath,'/');
908 #endif
909 if (sep) {
910 *(sep)=0;
912 file= PR_smprintf("%s"PATH_SEPARATOR"%s", dirPath, filename);
913 PORT_Free(dirPath);
914 return file;
917 #define MAX_LINE_LENGTH 2048
918 #define SFTK_DEFAULT_INTERNAL_INIT1 "library= name=\"NSS Internal PKCS #11 Module\" parameters="
919 #define SFTK_DEFAULT_INTERNAL_INIT2 " NSS=\"Flags=internal,critical trustOrder=75 cipherOrder=100 slotParams=(1={"
920 #define SFTK_DEFAULT_INTERNAL_INIT3 " askpw=any timeout=30})\""
922 #ifdef XP_UNIX
923 #include <unistd.h>
924 #endif
926 * Read all the existing modules in out of the file.
928 char **
929 sftkdb_ReadSecmodDB(SDBType dbType, const char *appName,
930 const char *filename, const char *dbname,
931 char *params, PRBool rw)
933 FILE *fd = NULL;
934 char **moduleList = NULL;
935 int moduleCount = 1;
936 int useCount = SECMOD_STEP;
937 char line[MAX_LINE_LENGTH];
938 PRBool internal = PR_FALSE;
939 PRBool skipParams = PR_FALSE;
940 char *moduleString = NULL;
941 char *paramsValue=NULL;
942 PRBool failed = PR_TRUE;
944 if ((dbType == SDB_LEGACY) || (dbType == SDB_MULTIACCESS)) {
945 return sftkdbCall_ReadSecmodDB(appName, filename, dbname, params, rw);
948 moduleList = (char **) PORT_ZAlloc(useCount*sizeof(char **));
949 if (moduleList == NULL) return NULL;
951 /* do we really want to use streams here */
952 fd = fopen(dbname, "r");
953 if (fd == NULL) goto done;
956 * the following loop takes line separated config lines and colapses
957 * the lines to a single string, escaping and quoting as necessary.
959 /* loop state variables */
960 moduleString = NULL; /* current concatenated string */
961 internal = PR_FALSE; /* is this an internal module */
962 skipParams = PR_FALSE; /* did we find an override parameter block*/
963 paramsValue = NULL; /* the current parameter block value */
964 while (fgets(line, sizeof(line), fd) != NULL) {
965 int len = PORT_Strlen(line);
967 /* remove the ending newline */
968 if (len && line[len-1] == '\n') {
969 len--;
970 line[len] = 0;
972 if (*line == '#') {
973 continue;
975 if (*line != 0) {
977 * The PKCS #11 group standard assumes blocks of strings
978 * separated by new lines, clumped by new lines. Internally
979 * we take strings separated by spaces, so we may need to escape
980 * certain spaces.
982 char *value = PORT_Strchr(line,'=');
984 /* there is no value, write out the stanza as is */
985 if (value == NULL || value[1] == 0) {
986 if (moduleString) {
987 moduleString = sftkdb_DupnCat(moduleString," ", 1);
988 if (moduleString == NULL) goto loser;
990 moduleString = sftkdb_DupCat(moduleString, line);
991 if (moduleString == NULL) goto loser;
992 /* value is already quoted, just write it out */
993 } else if (value[1] == '"') {
994 if (moduleString) {
995 moduleString = sftkdb_DupnCat(moduleString," ", 1);
996 if (moduleString == NULL) goto loser;
998 moduleString = sftkdb_DupCat(moduleString, line);
999 if (moduleString == NULL) goto loser;
1000 /* we have an override parameter section, remember that
1001 * we found this (see following comment about why this
1002 * is necessary). */
1003 if (PORT_Strncasecmp(line, "parameters", 10) == 0) {
1004 skipParams = PR_TRUE;
1007 * The internal token always overrides it's parameter block
1008 * from the passed in parameters, so wait until then end
1009 * before we include the parameter block in case we need to
1010 * override it. NOTE: if the parameter block is quoted with ("),
1011 * this override does not happen. This allows you to override
1012 * the application's parameter configuration.
1014 * parameter block state is controlled by the following variables:
1015 * skipParams - Bool : set to true of we have an override param
1016 * block (all other blocks, either implicit or explicit are
1017 * ignored).
1018 * paramsValue - char * : pointer to the current param block. In
1019 * the absence of overrides, paramsValue is set to the first
1020 * parameter block we find. All subsequent blocks are ignored.
1021 * When we find an internal token, the application passed
1022 * parameters take precident.
1024 } else if (PORT_Strncasecmp(line, "parameters", 10) == 0) {
1025 /* already have parameters */
1026 if (paramsValue) {
1027 continue;
1029 paramsValue = sftkdb_quote(&value[1], '"');
1030 if (paramsValue == NULL) goto loser;
1031 continue;
1032 } else {
1033 /* may need to quote */
1034 char *newLine;
1035 if (moduleString) {
1036 moduleString = sftkdb_DupnCat(moduleString," ", 1);
1037 if (moduleString == NULL) goto loser;
1039 moduleString = sftkdb_DupnCat(moduleString,line,value-line+1);
1040 if (moduleString == NULL) goto loser;
1041 newLine = sftkdb_quote(&value[1],'"');
1042 if (newLine == NULL) goto loser;
1043 moduleString = sftkdb_DupCat(moduleString,newLine);
1044 PORT_Free(newLine);
1045 if (moduleString == NULL) goto loser;
1048 /* check to see if it's internal? */
1049 if (PORT_Strncasecmp(line, "NSS=", 4) == 0) {
1050 /* This should be case insensitive! reviewers make
1051 * me fix it if it's not */
1052 if (PORT_Strstr(line,"internal")) {
1053 internal = PR_TRUE;
1054 /* override the parameters */
1055 if (paramsValue) {
1056 PORT_Free(paramsValue);
1058 paramsValue = sftkdb_quote(params, '"');
1061 continue;
1063 if ((moduleString == NULL) || (*moduleString == 0)) {
1064 continue;
1068 * if we are here, we have found a complete stanza. Now write out
1069 * any param section we may have found.
1071 if (paramsValue) {
1072 /* we had an override */
1073 if (!skipParams) {
1074 moduleString = sftkdb_DupnCat(moduleString," parameters=", 12);
1075 if (moduleString == NULL) goto loser;
1076 moduleString = sftkdb_DupCat(moduleString, paramsValue);
1077 if (moduleString == NULL) goto loser;
1079 PORT_Free(paramsValue);
1080 paramsValue = NULL;
1083 if ((moduleCount+1) >= useCount) {
1084 SECStatus rv;
1085 rv = sftkdb_growList(&moduleList, &useCount, moduleCount+1);
1086 if (rv != SECSuccess) {
1087 goto loser;
1091 if (internal) {
1092 moduleList[0] = moduleString;
1093 } else {
1094 moduleList[moduleCount] = moduleString;
1095 moduleCount++;
1097 moduleString = NULL;
1098 internal = PR_FALSE;
1099 skipParams = PR_FALSE;
1102 if (moduleString) {
1103 PORT_Free(moduleString);
1104 moduleString = NULL;
1106 done:
1107 /* if we couldn't open a pkcs11 database, look for the old one */
1108 if (fd == NULL) {
1109 char *olddbname = sftk_getOldSecmodName(dbname,filename);
1110 PRStatus status;
1111 char **oldModuleList;
1112 int i;
1114 /* couldn't get the old name */
1115 if (!olddbname) {
1116 goto bail;
1119 /* old one doesn't exist */
1120 status = PR_Access(olddbname, PR_ACCESS_EXISTS);
1121 if (status != PR_SUCCESS) {
1122 goto bail;
1125 oldModuleList = sftkdbCall_ReadSecmodDB(appName, filename,
1126 olddbname, params, rw);
1127 /* old one had no modules */
1128 if (!oldModuleList) {
1129 goto bail;
1132 /* count the modules */
1133 for (i=0; oldModuleList[i]; i++) { }
1135 /* grow the moduleList if necessary */
1136 if (i >= useCount) {
1137 SECStatus rv;
1138 rv = sftkdb_growList(&moduleList,&useCount,moduleCount+1);
1139 if (rv != SECSuccess) {
1140 goto loser;
1144 /* write each module out, and copy it */
1145 for (i=0; oldModuleList[i]; i++) {
1146 if (rw) {
1147 sftkdb_AddSecmodDB(dbType,appName,filename,dbname,
1148 oldModuleList[i],rw);
1150 if (moduleList[i]) {
1151 PORT_Free(moduleList[i]);
1153 moduleList[i] = PORT_Strdup(oldModuleList[i]);
1156 /* done with the old module list */
1157 sftkdbCall_ReleaseSecmodDBData(appName, filename, olddbname,
1158 oldModuleList, rw);
1160 bail:
1162 if (!moduleList[0]) {
1163 char * newParams;
1164 moduleString = PORT_Strdup(SFTK_DEFAULT_INTERNAL_INIT1);
1165 newParams = sftkdb_quote(params,'"');
1166 if (newParams == NULL) goto loser;
1167 moduleString = sftkdb_DupCat(moduleString, newParams);
1168 PORT_Free(newParams);
1169 if (moduleString == NULL) goto loser;
1170 moduleString = sftkdb_DupCat(moduleString, SFTK_DEFAULT_INTERNAL_INIT2);
1171 if (moduleString == NULL) goto loser;
1172 moduleString = sftkdb_DupCat(moduleString, SECMOD_SLOT_FLAGS);
1173 if (moduleString == NULL) goto loser;
1174 moduleString = sftkdb_DupCat(moduleString, SFTK_DEFAULT_INTERNAL_INIT3);
1175 if (moduleString == NULL) goto loser;
1176 moduleList[0] = moduleString;
1177 moduleString = NULL;
1179 failed = PR_FALSE;
1181 loser:
1183 * cleanup
1185 /* deal with trust cert db here */
1186 if (moduleString) {
1187 PORT_Free(moduleString);
1188 moduleString = NULL;
1190 if (paramsValue) {
1191 PORT_Free(paramsValue);
1192 paramsValue = NULL;
1194 if (failed || (moduleList[0] == NULL)) {
1195 /* This is wrong! FIXME */
1196 sftkdb_releaseSpecList(moduleList);
1197 moduleList = NULL;
1198 failed = PR_TRUE;
1200 if (fd != NULL) {
1201 fclose(fd);
1202 } else if (!failed && rw) {
1203 /* update our internal module */
1204 sftkdb_AddSecmodDB(dbType,appName,filename,dbname,moduleList[0],rw);
1206 return moduleList;
1209 SECStatus
1210 sftkdb_ReleaseSecmodDBData(SDBType dbType, const char *appName,
1211 const char *filename, const char *dbname,
1212 char **moduleSpecList, PRBool rw)
1214 if ((dbType == SDB_LEGACY) || (dbType == SDB_MULTIACCESS)) {
1215 return sftkdbCall_ReleaseSecmodDBData(appName, filename, dbname,
1216 moduleSpecList, rw);
1218 if (moduleSpecList) {
1219 sftkdb_releaseSpecList(moduleSpecList);
1221 return SECSuccess;
1226 * Delete a module from the Data Base
1228 SECStatus
1229 sftkdb_DeleteSecmodDB(SDBType dbType, const char *appName,
1230 const char *filename, const char *dbname,
1231 char *args, PRBool rw)
1233 /* SHDB_FIXME implement */
1234 FILE *fd = NULL;
1235 FILE *fd2 = NULL;
1236 char line[MAX_LINE_LENGTH];
1237 char *dbname2 = NULL;
1238 char *block = NULL;
1239 char *name = NULL;
1240 char *lib = NULL;
1241 int name_len, lib_len;
1242 PRBool skip = PR_FALSE;
1243 PRBool found = PR_FALSE;
1245 if ((dbType == SDB_LEGACY) || (dbType == SDB_MULTIACCESS)) {
1246 return sftkdbCall_DeleteSecmodDB(appName, filename, dbname, args, rw);
1249 if (!rw) {
1250 return SECFailure;
1253 dbname2 = strdup(dbname);
1254 if (dbname2 == NULL) goto loser;
1255 dbname2[strlen(dbname)-1]++;
1257 /* do we really want to use streams here */
1258 fd = fopen(dbname, "r");
1259 if (fd == NULL) goto loser;
1260 fd2 = fopen(dbname2, "w+");
1261 if (fd2 == NULL) goto loser;
1263 name = sftk_argGetParamValue("name",args);
1264 if (name) {
1265 name_len = PORT_Strlen(name);
1267 lib = sftk_argGetParamValue("library",args);
1268 if (lib) {
1269 lib_len = PORT_Strlen(lib);
1274 * the following loop takes line separated config files and colapses
1275 * the lines to a single string, escaping and quoting as necessary.
1277 /* loop state variables */
1278 block = NULL;
1279 skip = PR_FALSE;
1280 while (fgets(line, sizeof(line), fd) != NULL) {
1281 /* If we are processing a block (we haven't hit a blank line yet */
1282 if (*line != '\n') {
1283 /* skip means we are in the middle of a block we are deleting */
1284 if (skip) {
1285 continue;
1287 /* if we haven't found the block yet, check to see if this block
1288 * matches our requirements */
1289 if (!found && ((name && (PORT_Strncasecmp(line,"name=",5) == 0) &&
1290 (PORT_Strncmp(line+5,name,name_len) == 0)) ||
1291 (lib && (PORT_Strncasecmp(line,"library=",8) == 0) &&
1292 (PORT_Strncmp(line+8,lib,lib_len) == 0)))) {
1294 /* yup, we don't need to save any more data, */
1295 PORT_Free(block);
1296 block=NULL;
1297 /* we don't need to collect more of this block */
1298 skip = PR_TRUE;
1299 /* we don't need to continue searching for the block */
1300 found =PR_TRUE;
1301 continue;
1303 /* not our match, continue to collect data in this block */
1304 block = sftkdb_DupCat(block,line);
1305 continue;
1307 /* we've collected a block of data that wasn't the module we were
1308 * looking for, write it out */
1309 if (block) {
1310 fwrite(block, PORT_Strlen(block), 1, fd2);
1311 PORT_Free(block);
1312 block = NULL;
1314 /* If we didn't just delete the this block, keep the blank line */
1315 if (!skip) {
1316 fputs(line,fd2);
1318 /* we are definately not in a deleted block anymore */
1319 skip = PR_FALSE;
1321 fclose(fd);
1322 fclose(fd2);
1323 /* rename dbname2 to dbname */
1324 if (found) {
1325 PR_Delete(dbname);
1326 PR_Rename(dbname2,dbname);
1328 PORT_Free(dbname2);
1329 return SECSuccess;
1331 loser:
1332 if (fd != NULL) {
1333 fclose(fd);
1335 if (fd2 != NULL) {
1336 fclose(fd2);
1338 if (dbname2) {
1339 PR_Delete(dbname2);
1340 PORT_Free(dbname2);
1342 return SECFailure;
1346 * Add a module to the Data base
1348 SECStatus
1349 sftkdb_AddSecmodDB(SDBType dbType, const char *appName,
1350 const char *filename, const char *dbname,
1351 char *module, PRBool rw)
1353 FILE *fd = NULL;
1354 char *block = NULL;
1355 PRBool libFound = PR_FALSE;
1357 if ((dbType == SDB_LEGACY) || (dbType == SDB_MULTIACCESS)) {
1358 return sftkdbCall_AddSecmodDB(appName, filename, dbname, module, rw);
1361 /* can't write to a read only module */
1362 if (!rw) {
1363 return SECFailure;
1366 /* remove the previous version if it exists */
1367 (void) sftkdb_DeleteSecmodDB(dbType, appName, filename, dbname, module, rw);
1369 /* do we really want to use streams here */
1370 fd = fopen(dbname, "a+");
1371 if (fd == NULL) {
1372 return SECFailure;
1374 module = sftk_argStrip(module);
1375 while (*module) {
1376 int count;
1377 char *keyEnd = PORT_Strchr(module,'=');
1378 char *value;
1380 if (PORT_Strncmp(module, "library=", 8) == 0) {
1381 libFound=PR_TRUE;
1383 if (keyEnd == NULL) {
1384 block = sftkdb_DupCat(block, module);
1385 break;
1387 value = sftk_argFetchValue(&keyEnd[1], &count);
1388 block = sftkdb_DupnCat(block, module, keyEnd-module+1);
1389 if (block == NULL) { goto loser; }
1390 if (value) {
1391 block = sftkdb_DupCat(block, sftk_argStrip(value));
1392 PORT_Free(value);
1394 if (block == NULL) { goto loser; }
1395 block = sftkdb_DupnCat(block, "\n", 1);
1396 module = keyEnd + 1 + count;
1397 module = sftk_argStrip(module);
1399 if (block) {
1400 if (!libFound) {
1401 fprintf(fd,"library=\n");
1403 fwrite(block, PORT_Strlen(block), 1, fd);
1404 fprintf(fd,"\n");
1405 PORT_Free(block);
1406 block = NULL;
1408 fclose(fd);
1409 return SECSuccess;
1411 loser:
1412 PORT_Free(block);
1413 fclose(fd);
1414 return SECFailure;
1417 /******************************************************************
1419 * Key DB password handling functions
1421 * These functions manage the key db password (set, reset, initialize, use).
1423 * The key is managed on 'this side' of the database. All private data is
1424 * encrypted before it is sent to the database itself. Besides PBE's, the
1425 * database management code can also mix in various fixed keys so the data
1426 * in the database is no longer considered 'plain text'.
1430 /* take string password and turn it into a key. The key is dependent
1431 * on a global salt entry acquired from the database. This salted
1432 * value will be based to a pkcs5 pbe function before it is used
1433 * in an actual encryption */
1434 static SECStatus
1435 sftkdb_passwordToKey(SFTKDBHandle *keydb, SDBPasswordEntry *entry,
1436 const char *pw, SECItem *key)
1438 SHA1Context *cx = NULL;
1439 SECStatus rv = SECFailure;
1441 key->data = PORT_Alloc(SHA1_LENGTH);
1442 if (key->data == NULL) {
1443 goto loser;
1445 key->len = SHA1_LENGTH;
1447 cx = SHA1_NewContext();
1448 if ( cx == NULL) {
1449 goto loser;
1451 SHA1_Begin(cx);
1452 if (entry && entry->salt.data ) {
1453 SHA1_Update(cx, entry->salt.data, entry->salt.len);
1455 SHA1_Update(cx, (unsigned char *)pw, PORT_Strlen(pw));
1456 SHA1_End(cx, key->data, &key->len, key->len);
1457 rv = SECSuccess;
1459 loser:
1460 if (cx) {
1461 SHA1_DestroyContext(cx, PR_TRUE);
1463 if (rv != SECSuccess) {
1464 if (key->data != NULL) {
1465 PORT_ZFree(key->data,key->len);
1467 key->data = NULL;
1469 return rv;
1473 * Cipher text stored in the database contains 3 elements:
1474 * 1) an identifier describing the encryption algorithm.
1475 * 2) an entry specific salt value.
1476 * 3) the encrypted value.
1478 * The following data structure represents the encrypted data in a decoded
1479 * (but still encrypted) form.
1481 typedef struct sftkCipherValueStr sftkCipherValue;
1482 struct sftkCipherValueStr {
1483 PLArenaPool *arena;
1484 SECOidTag alg;
1485 NSSPKCS5PBEParameter *param;
1486 SECItem salt;
1487 SECItem value;
1490 #define SFTK_CIPHERTEXT_VERSION 3
1492 struct SFTKDBEncryptedDataInfoStr {
1493 SECAlgorithmID algorithm;
1494 SECItem encryptedData;
1496 typedef struct SFTKDBEncryptedDataInfoStr SFTKDBEncryptedDataInfo;
1498 const SEC_ASN1Template sftkdb_EncryptedDataInfoTemplate[] = {
1499 { SEC_ASN1_SEQUENCE,
1500 0, NULL, sizeof(SFTKDBEncryptedDataInfo) },
1501 { SEC_ASN1_INLINE,
1502 offsetof(SFTKDBEncryptedDataInfo,algorithm),
1503 SECOID_AlgorithmIDTemplate },
1504 { SEC_ASN1_OCTET_STRING,
1505 offsetof(SFTKDBEncryptedDataInfo,encryptedData) },
1506 { 0 }
1510 * This parses the cipherText into cipher value. NOTE: cipherValue will point
1511 * to data in cipherText, if cipherText is freed, cipherValue will be invalid.
1513 * Use existing NSS data record: (sizes and offsets in bytes)
1515 * offset size label Description
1516 * 0 1 version Data base version number must be 3
1517 * 1 1 slen Length of Salt
1518 * 2 1 nlen Length of optional nickname
1519 * 3 slen sdata Salt data
1520 * 3+slen nlen ndata Optional nickname data
1521 * 3+nlen+slen 1 olen Length of algorithm OID
1522 * 4+nlen+slen olen odata Algorithm OID data.
1523 * 4+nlen+slen+
1524 * olen rest vdata Encrypted data.
1526 * rest is the rest of the block passed into us.
1528 static SECStatus
1529 sftkdb_decodeCipherText(SECItem *cipherText, sftkCipherValue *cipherValue)
1531 PLArenaPool *arena = NULL;
1532 SFTKDBEncryptedDataInfo edi;
1533 SECStatus rv;
1535 arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1536 if (arena == NULL) {
1537 return SECFailure;
1539 cipherValue->arena = NULL;
1540 cipherValue->param = NULL;
1542 rv = SEC_QuickDERDecodeItem(arena, &edi, sftkdb_EncryptedDataInfoTemplate,
1543 cipherText);
1544 if (rv != SECSuccess) {
1545 goto loser;
1547 cipherValue->alg = SECOID_GetAlgorithmTag(&edi.algorithm);
1548 cipherValue->param = nsspkcs5_AlgidToParam(&edi.algorithm);
1549 if (cipherValue->param == NULL) {
1550 goto loser;
1552 cipherValue->value = edi.encryptedData;
1553 cipherValue->arena = arena;
1555 return SECSuccess;
1556 loser:
1557 if (cipherValue->param) {
1558 nsspkcs5_DestroyPBEParameter(cipherValue->param);
1559 cipherValue->param = NULL;
1561 if (arena) {
1562 PORT_FreeArena(arena,PR_FALSE);
1564 return SECFailure;
1570 * unlike decode, Encode actually allocates a SECItem the caller must free
1571 * The caller can pass an optional arena to to indicate where to place
1572 * the resultant cipherText.
1574 static SECStatus
1575 sftkdb_encodeCipherText(PLArenaPool *arena, sftkCipherValue *cipherValue,
1576 SECItem **cipherText)
1578 SFTKDBEncryptedDataInfo edi;
1579 SECAlgorithmID *algid;
1580 SECStatus rv;
1581 PLArenaPool *localArena = NULL;
1584 localArena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
1585 if (localArena == NULL) {
1586 return SECFailure;
1589 algid = nsspkcs5_CreateAlgorithmID(localArena, cipherValue->alg,
1590 cipherValue->param);
1591 if (algid == NULL) {
1592 rv = SECFailure;
1593 goto loser;
1595 rv = SECOID_CopyAlgorithmID(localArena, &edi.algorithm, algid);
1596 SECOID_DestroyAlgorithmID(algid, PR_TRUE);
1597 if (rv != SECSuccess) {
1598 goto loser;
1600 edi.encryptedData = cipherValue->value;
1602 *cipherText = SEC_ASN1EncodeItem(arena, NULL, &edi,
1603 sftkdb_EncryptedDataInfoTemplate);
1604 if (*cipherText == NULL) {
1605 rv = SECFailure;
1608 loser:
1609 if (localArena) {
1610 PORT_FreeArena(localArena,PR_FALSE);
1613 return rv;
1618 * Use our key to decode a cipherText block from the database.
1620 * plain text is allocated by nsspkcs5_CipherData and must be freed
1621 * with SECITEM_FreeItem by the caller.
1623 static SECStatus
1624 sftkdb_decrypt(SECItem *passKey, SECItem *cipherText, SECItem **plain)
1626 SECStatus rv;
1627 sftkCipherValue cipherValue;
1629 /* First get the cipher type */
1630 rv = sftkdb_decodeCipherText(cipherText, &cipherValue);
1631 if (rv != SECSuccess) {
1632 goto loser;
1635 *plain = nsspkcs5_CipherData(cipherValue.param, passKey, &cipherValue.value,
1636 PR_FALSE, NULL);
1637 if (*plain == NULL) {
1638 rv = SECFailure;
1639 goto loser;
1642 loser:
1643 if (cipherValue.param) {
1644 nsspkcs5_DestroyPBEParameter(cipherValue.param);
1646 if (cipherValue.arena) {
1647 PORT_FreeArena(cipherValue.arena,PR_FALSE);
1649 return rv;
1652 #define SALT_LENGTH 20
1655 * encrypt a block. This function returned the encrypted ciphertext which
1656 * the caller must free. If the caller provides an arena, cipherText will
1657 * be allocated out of that arena. This also generated the per entry
1658 * salt automatically.
1660 static SECStatus
1661 sftkdb_encrypt(PLArenaPool *arena, SECItem *passKey, SECItem *plainText,
1662 SECItem **cipherText)
1664 SECStatus rv;
1665 sftkCipherValue cipherValue;
1666 SECItem *cipher = NULL;
1667 NSSPKCS5PBEParameter *param = NULL;
1668 unsigned char saltData[SALT_LENGTH];
1670 cipherValue.alg = SEC_OID_PKCS12_PBE_WITH_SHA1_AND_TRIPLE_DES_CBC;
1671 cipherValue.salt.len = SALT_LENGTH;
1672 cipherValue.salt.data = saltData;
1673 RNG_GenerateGlobalRandomBytes(saltData,SALT_LENGTH);
1675 param = nsspkcs5_NewParam(cipherValue.alg, &cipherValue.salt, 1);
1676 if (param == NULL) {
1677 rv = SECFailure;
1678 goto loser;
1680 cipher = nsspkcs5_CipherData(param, passKey, plainText, PR_TRUE, NULL);
1681 if (cipher == NULL) {
1682 rv = SECFailure;
1683 goto loser;
1685 cipherValue.value = *cipher;
1686 cipherValue.param = param;
1688 rv = sftkdb_encodeCipherText(arena, &cipherValue, cipherText);
1689 if (rv != SECSuccess) {
1690 goto loser;
1693 loser:
1694 if (cipher) {
1695 SECITEM_FreeItem(cipher, PR_TRUE);
1697 if (param) {
1698 nsspkcs5_DestroyPBEParameter(param);
1700 return rv;
1705 * stub files for legacy db's to be able to encrypt and decrypt
1706 * various keys and attributes.
1708 SECStatus
1709 sftkdb_encrypt_stub(PRArenaPool *arena, SDB *sdb, SECItem *plainText,
1710 SECItem **cipherText)
1712 SFTKDBHandle *handle = sdb->app_private;
1713 SECStatus rv;
1715 if (handle == NULL) {
1716 return SECFailure;
1719 /* if we aren't th handle, try the other handle */
1720 if (handle->type != SFTK_KEYDB_TYPE) {
1721 handle = handle->peerDB;
1724 /* not a key handle */
1725 if (handle == NULL || handle->passwordLock == NULL) {
1726 return SECFailure;
1729 PZ_Lock(handle->passwordLock);
1730 if (handle->passwordKey.data == NULL) {
1731 PZ_Unlock(handle->passwordLock);
1732 /* PORT_SetError */
1733 return SECFailure;
1736 rv = sftkdb_encrypt(arena,
1737 handle->newKey?handle->newKey:&handle->passwordKey,
1738 plainText, cipherText);
1739 PZ_Unlock(handle->passwordLock);
1741 return rv;
1745 * stub files for legacy db's to be able to encrypt and decrypt
1746 * various keys and attributes.
1748 SECStatus
1749 sftkdb_decrypt_stub(SDB *sdb, SECItem *cipherText, SECItem **plainText)
1751 SFTKDBHandle *handle = sdb->app_private;
1752 SECStatus rv;
1754 if (handle == NULL) {
1755 return SECFailure;
1758 /* if we aren't th handle, try the other handle */
1759 if (handle->type != SFTK_KEYDB_TYPE) {
1760 handle = handle->peerDB;
1763 /* not a key handle */
1764 if (handle == NULL || handle->passwordLock == NULL) {
1765 return SECFailure;
1768 PZ_Lock(handle->passwordLock);
1769 if (handle->passwordKey.data == NULL) {
1770 PZ_Unlock(handle->passwordLock);
1771 /* PORT_SetError */
1772 return SECFailure;
1774 rv = sftkdb_decrypt(&handle->passwordKey, cipherText, plainText);
1775 PZ_Unlock(handle->passwordLock);
1777 return rv;
1781 * safely swith the passed in key for the one caches in the keydb handle
1783 * A key attached to the handle tells us the the token is logged in.
1784 * We can used the key attached to the handle in sftkdb_encrypt
1785 * and sftkdb_decrypt calls.
1787 static void
1788 sftkdb_switchKeys(SFTKDBHandle *keydb, SECItem *passKey)
1790 unsigned char *data;
1791 int len;
1793 if (keydb->passwordLock == NULL) {
1794 PORT_Assert(keydb->type != SFTK_KEYDB_TYPE);
1795 return;
1798 /* an atomic pointer set would be nice */
1799 PZ_Lock(keydb->passwordLock);
1800 data = keydb->passwordKey.data;
1801 len = keydb->passwordKey.len;
1802 keydb->passwordKey.data = passKey->data;
1803 keydb->passwordKey.len = passKey->len;
1804 passKey->data = data;
1805 passKey->len = len;
1806 PZ_Unlock(keydb->passwordLock);
1810 * known attributes
1812 static const CK_ATTRIBUTE_TYPE known_attributes[] = {
1813 CKA_CLASS, CKA_TOKEN, CKA_PRIVATE, CKA_LABEL, CKA_APPLICATION,
1814 CKA_VALUE, CKA_OBJECT_ID, CKA_CERTIFICATE_TYPE, CKA_ISSUER,
1815 CKA_SERIAL_NUMBER, CKA_AC_ISSUER, CKA_OWNER, CKA_ATTR_TYPES, CKA_TRUSTED,
1816 CKA_CERTIFICATE_CATEGORY, CKA_JAVA_MIDP_SECURITY_DOMAIN, CKA_URL,
1817 CKA_HASH_OF_SUBJECT_PUBLIC_KEY, CKA_HASH_OF_ISSUER_PUBLIC_KEY,
1818 CKA_CHECK_VALUE, CKA_KEY_TYPE, CKA_SUBJECT, CKA_ID, CKA_SENSITIVE,
1819 CKA_ENCRYPT, CKA_DECRYPT, CKA_WRAP, CKA_UNWRAP, CKA_SIGN, CKA_SIGN_RECOVER,
1820 CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_DERIVE, CKA_START_DATE, CKA_END_DATE,
1821 CKA_MODULUS, CKA_MODULUS_BITS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT,
1822 CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT,
1823 CKA_PRIME, CKA_SUBPRIME, CKA_BASE, CKA_PRIME_BITS,
1824 CKA_SUB_PRIME_BITS, CKA_VALUE_BITS, CKA_VALUE_LEN, CKA_EXTRACTABLE,
1825 CKA_LOCAL, CKA_NEVER_EXTRACTABLE, CKA_ALWAYS_SENSITIVE,
1826 CKA_KEY_GEN_MECHANISM, CKA_MODIFIABLE, CKA_EC_PARAMS,
1827 CKA_EC_POINT, CKA_SECONDARY_AUTH, CKA_AUTH_PIN_FLAGS,
1828 CKA_ALWAYS_AUTHENTICATE, CKA_WRAP_WITH_TRUSTED, CKA_WRAP_TEMPLATE,
1829 CKA_UNWRAP_TEMPLATE, CKA_HW_FEATURE_TYPE, CKA_RESET_ON_INIT,
1830 CKA_HAS_RESET, CKA_PIXEL_X, CKA_PIXEL_Y, CKA_RESOLUTION, CKA_CHAR_ROWS,
1831 CKA_CHAR_COLUMNS, CKA_COLOR, CKA_BITS_PER_PIXEL, CKA_CHAR_SETS,
1832 CKA_ENCODING_METHODS, CKA_MIME_TYPES, CKA_MECHANISM_TYPE,
1833 CKA_REQUIRED_CMS_ATTRIBUTES, CKA_DEFAULT_CMS_ATTRIBUTES,
1834 CKA_SUPPORTED_CMS_ATTRIBUTES, CKA_NETSCAPE_URL, CKA_NETSCAPE_EMAIL,
1835 CKA_NETSCAPE_SMIME_INFO, CKA_NETSCAPE_SMIME_TIMESTAMP,
1836 CKA_NETSCAPE_PKCS8_SALT, CKA_NETSCAPE_PASSWORD_CHECK, CKA_NETSCAPE_EXPIRES,
1837 CKA_NETSCAPE_KRL, CKA_NETSCAPE_PQG_COUNTER, CKA_NETSCAPE_PQG_SEED,
1838 CKA_NETSCAPE_PQG_H, CKA_NETSCAPE_PQG_SEED_BITS, CKA_NETSCAPE_MODULE_SPEC,
1839 CKA_TRUST_DIGITAL_SIGNATURE, CKA_TRUST_NON_REPUDIATION,
1840 CKA_TRUST_KEY_ENCIPHERMENT, CKA_TRUST_DATA_ENCIPHERMENT,
1841 CKA_TRUST_KEY_AGREEMENT, CKA_TRUST_KEY_CERT_SIGN, CKA_TRUST_CRL_SIGN,
1842 CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH, CKA_TRUST_CODE_SIGNING,
1843 CKA_TRUST_EMAIL_PROTECTION, CKA_TRUST_IPSEC_END_SYSTEM,
1844 CKA_TRUST_IPSEC_TUNNEL, CKA_TRUST_IPSEC_USER, CKA_TRUST_TIME_STAMPING,
1845 CKA_TRUST_STEP_UP_APPROVED, CKA_CERT_SHA1_HASH, CKA_CERT_MD5_HASH,
1846 CKA_NETSCAPE_DB, CKA_NETSCAPE_TRUST, CKA_NSS_OVERRIDE_EXTENSIONS
1849 static int known_attributes_size= sizeof(known_attributes)/
1850 sizeof(known_attributes[0]);
1852 static CK_RV
1853 sftkdb_GetObjectTemplate(SDB *source, CK_OBJECT_HANDLE id,
1854 CK_ATTRIBUTE *ptemplate, CK_ULONG *max)
1856 int i,j;
1857 CK_RV crv;
1859 if (*max < known_attributes_size) {
1860 *max = known_attributes_size;
1861 return CKR_BUFFER_TOO_SMALL;
1863 for (i=0; i < known_attributes_size; i++) {
1864 ptemplate[i].type = known_attributes[i];
1865 ptemplate[i].pValue = NULL;
1866 ptemplate[i].ulValueLen = 0;
1869 crv = (*source->sdb_GetAttributeValue)(source, id,
1870 ptemplate, known_attributes_size);
1872 if ((crv != CKR_OK) && (crv != CKR_ATTRIBUTE_TYPE_INVALID)) {
1873 return crv;
1876 for (i=0, j=0; i < known_attributes_size; i++, j++) {
1877 while (i < known_attributes_size && (ptemplate[i].ulValueLen == -1)) {
1878 i++;
1880 if (i >= known_attributes_size) {
1881 break;
1883 /* cheap optimization */
1884 if (i == j) {
1885 continue;
1887 ptemplate[j] = ptemplate[i];
1889 *max = j;
1890 return CKR_OK;
1893 #ifdef notdef
1894 static void
1895 dump_attribute(CK_ATTRIBUTE *attr)
1897 unsigned char *buf = attr->pValue;
1898 int count,i;
1900 printf("%08x: (%d) ",attr->type, attr->ulValueLen);
1901 count = attr->ulValueLen;
1902 if (count > 10) count = 10;
1903 for (i=0; i < count; i++) {
1904 printf("%02x",buf[i]);
1906 printf("\n");
1908 #endif
1911 #define MAX_ATTRIBUTES 500
1912 static CK_RV
1913 sftkdb_copyObject(SFTKDBHandle *handle, CK_OBJECT_HANDLE id, SECItem *key)
1915 CK_ATTRIBUTE template[MAX_ATTRIBUTES];
1916 CK_ATTRIBUTE *ptemplate;
1917 CK_ULONG max_attributes = MAX_ATTRIBUTES;
1918 SDB *source = handle->update;
1919 SDB *target = handle->db;
1920 int i;
1921 CK_RV crv;
1923 ptemplate = &template[0];
1924 id &= SFTK_OBJ_ID_MASK;
1925 crv = sftkdb_GetObjectTemplate(source, id, ptemplate, &max_attributes);
1926 if (crv == CKR_BUFFER_TOO_SMALL) {
1927 ptemplate = PORT_NewArray(CK_ATTRIBUTE, max_attributes);
1928 if (ptemplate == NULL) {
1929 crv = CKR_HOST_MEMORY;
1930 } else {
1931 crv = sftkdb_GetObjectTemplate(source, id,
1932 ptemplate, &max_attributes);
1935 if (crv != CKR_OK) {
1936 goto loser;
1939 for (i=0; i < max_attributes; i++) {
1940 ptemplate[i].pValue = PORT_Alloc(ptemplate[i].ulValueLen);
1941 if (ptemplate[i].pValue == NULL) {
1942 crv = CKR_HOST_MEMORY;
1943 goto loser;
1946 crv = (*source->sdb_GetAttributeValue)(source, id,
1947 ptemplate, max_attributes);
1948 if (crv != CKR_OK) {
1949 goto loser;
1952 crv = sftkdb_CreateObject(handle, target, &id, ptemplate, max_attributes);
1953 if (ptemplate && ptemplate != template) {
1954 PORT_Free(ptemplate);
1957 loser:
1958 if (ptemplate) {
1959 for (i=0; i < max_attributes; i++) {
1960 if (ptemplate[i].pValue) {
1961 PORT_Memset(ptemplate[i].pValue, 0, ptemplate[i].ulValueLen);
1962 PORT_Free(ptemplate[i].pValue);
1965 if (ptemplate != template) {
1966 PORT_Free(ptemplate);
1969 return crv;
1973 #define MAX_IDS 10
1975 * update a new database from an old one, now that we have the key
1977 static CK_RV
1978 sftkdb_update(SFTKDBHandle *handle, SECItem *key)
1980 SDBFind *find = NULL;
1981 CK_ULONG idCount = MAX_IDS;
1982 CK_OBJECT_HANDLE ids[MAX_IDS];
1983 CK_RV crv, crv2;
1984 PRBool inTransaction = PR_FALSE;
1985 int i;
1987 if (handle == NULL) {
1988 return CKR_OK;
1990 if (handle->update == NULL) {
1991 return CKR_OK;
1994 /* find all the objects */
1995 crv = sftkdb_FindObjectsInit(handle, NULL, 0, &find);
1997 if (crv != CKR_OK) {
1998 goto loser;
2000 while ((crv == CKR_OK) && (idCount == MAX_IDS)) {
2001 crv = sftkdb_FindObjects(handle, find, ids, MAX_IDS, &idCount);
2002 for (i=0; (crv == CKR_OK) && (i < idCount); i++) {
2003 crv = sftkdb_copyObject(handle, ids[i], key);
2006 crv2 = sftkdb_FindObjectsFinal(handle, find);
2007 if (crv == CKR_OK) crv = crv2;
2009 loser:
2010 /* update Meta data - even if we didn't update objects */
2011 if (handle->type == SFTK_KEYDB_TYPE) {
2012 SDBPasswordEntry entry;
2013 crv = (*handle->db->sdb_Begin)(handle->db);
2014 if (crv != CKR_OK) {
2015 goto loser2;
2017 inTransaction = PR_TRUE;
2018 crv = (*handle->update->sdb_GetPWEntry)(handle->update, & entry);
2019 if (crv != CKR_OK) {
2020 goto loser2;
2022 crv = (*handle->db->sdb_PutPWEntry)(handle->db, &entry);
2023 if (crv != CKR_OK) {
2024 goto loser2;
2026 crv = (*handle->db->sdb_Commit)(handle->db);
2027 inTransaction = PR_FALSE;
2029 loser2:
2030 if (inTransaction) {
2031 (*handle->db->sdb_Abort)(handle->db);
2033 if (handle->update) {
2034 (*handle->update->sdb_Close)(handle->update);
2035 handle->update = NULL;
2037 return crv;
2041 * return success if we have a valid password entry.
2042 * This is will show up outside of PKCS #11 as CKF_USER_PIN_INIT
2043 * in the token flags.
2045 SECStatus
2046 sftkdb_HasPasswordSet(SFTKDBHandle *keydb)
2048 SDBPasswordEntry entry;
2049 CK_RV crv;
2050 SDB *db;
2052 if (keydb == NULL) {
2053 return SECFailure;
2056 db = GET_SDB(keydb);
2057 if (db == NULL) {
2058 return SECFailure;
2061 crv = (*db->sdb_GetPWEntry)(db, &entry);
2062 return (crv == CKR_OK) ? SECSuccess : SECFailure;
2065 #define SFTK_PW_CHECK_STRING "password-check"
2066 #define SFTK_PW_CHECK_LEN 14
2069 * check if the supplied password is valid
2071 SECStatus
2072 sftkdb_CheckPassword(SFTKDBHandle *keydb, const char *pw)
2074 SECStatus rv;
2075 SDBPasswordEntry entry;
2076 SECItem key;
2077 SECItem *result = NULL;
2078 SDB *db;
2079 CK_RV crv;
2081 if (keydb == NULL) {
2082 return SECFailure;
2085 db = GET_SDB(keydb);
2086 if (db == NULL) {
2087 return SECFailure;
2090 key.data = NULL;
2091 key.len = 0;
2093 if (pw == NULL) pw="";
2095 /* get the entry from the database */
2096 crv = (*db->sdb_GetPWEntry)(db, &entry);
2097 if (crv != CKR_OK) {
2098 rv = SECFailure;
2099 goto loser;
2102 /* get our intermediate key based on the entry salt value */
2103 rv = sftkdb_passwordToKey(keydb, &entry, pw, &key);
2104 if (rv != SECSuccess) {
2105 goto loser;
2108 /* decrypt the entry value */
2109 rv = sftkdb_decrypt(&key, &entry.value, &result);
2110 if (rv != SECSuccess) {
2111 goto loser;
2114 /* if it's what we expect, update our key in the database handle and
2115 * return Success */
2116 if ((result->len == SFTK_PW_CHECK_LEN) &&
2117 PORT_Memcmp(result->data, SFTK_PW_CHECK_STRING, SFTK_PW_CHECK_LEN) == 0){
2118 /* load the keys, so the keydb can parse it's key set */
2119 sftkdb_switchKeys(keydb, &key);
2120 if (keydb->update) {
2121 /* update the peer certdb if it exists */
2122 if (keydb->peerDB) {
2123 sftkdb_update(keydb->peerDB, &key);
2125 sftkdb_update(keydb, &key);
2127 } else {
2128 rv = SECFailure;
2129 /*PORT_SetError( bad password); */
2132 loser:
2133 if (key.data) {
2134 PORT_ZFree(key.data,key.len);
2136 if (result) {
2137 SECITEM_FreeItem(result,PR_TRUE);
2139 return rv;
2143 * return Success if the there is a cached password key.
2145 SECStatus
2146 sftkdb_PWCached(SFTKDBHandle *keydb)
2148 return keydb->passwordKey.data ? SECSuccess : SECFailure;
2151 static SECStatus
2152 sftk_convertPrivateAttributes(SFTKDBHandle *keydb, CK_OBJECT_HANDLE id,
2153 SECItem *newKey)
2155 CK_RV crv = CKR_OK;
2156 CK_RV crv2;
2157 CK_ATTRIBUTE *first, *last;
2158 CK_ATTRIBUTE privAttrs[] = {
2159 {CKA_VALUE, NULL, 0},
2160 {CKA_PRIVATE_EXPONENT, NULL, 0},
2161 {CKA_PRIME_1, NULL, 0},
2162 {CKA_PRIME_2, NULL, 0},
2163 {CKA_EXPONENT_1, NULL, 0},
2164 {CKA_EXPONENT_2, NULL, 0},
2165 {CKA_COEFFICIENT, NULL, 0} };
2166 CK_ULONG privAttrCount = sizeof(privAttrs)/sizeof(CK_ATTRIBUTE);
2167 PLArenaPool *arena = NULL;
2168 int i, count;
2171 /* get a new arena to simplify cleanup */
2172 arena = PORT_NewArena(1024);
2173 if (!arena) {
2174 return SECFailure;
2178 * STEP 1. Read the old attributes in the clear.
2181 /* Get the attribute sizes.
2182 * ignore the error code, we will have unknown attributes here */
2183 crv2 = sftkdb_GetAttributeValue(keydb, id, privAttrs, privAttrCount);
2186 * find the valid block of attributes and fill allocate space for
2187 * their data */
2188 first = last = NULL;
2189 for (i=0; i < privAttrCount; i++) {
2190 /* find the block of attributes that are appropriate for this
2191 * objects. There should only be once contiguous block, if not
2192 * there's an error.
2194 * find the first and last good entry.
2196 if ((privAttrs[i].ulValueLen == -1) || (privAttrs[i].ulValueLen == 0)){
2197 if (!first) continue;
2198 if (!last) {
2199 /* previous entry was last good entry */
2200 last= &privAttrs[i-1];
2202 continue;
2204 if (!first) {
2205 first = &privAttrs[i];
2207 if (last) {
2208 /* OOPS, we've found another good entry beyond the end of the
2209 * last good entry, we need to fail here. */
2210 crv = CKR_GENERAL_ERROR;
2211 break;
2213 privAttrs[i].pValue = PORT_ArenaAlloc(arena,privAttrs[i].ulValueLen);
2214 if (privAttrs[i].pValue == NULL) {
2215 crv = CKR_HOST_MEMORY;
2216 break;
2219 if (first == NULL) {
2220 /* no valid entries found, return error based on crv2 */
2221 /* set error */
2222 goto loser;
2224 if (last == NULL) {
2225 last = &privAttrs[privAttrCount-1];
2227 if (crv != CKR_OK) {
2228 /* set error */
2229 goto loser;
2231 /* read the attributes */
2232 count = (last-first)+1;
2233 crv = sftkdb_GetAttributeValue(keydb, id, first, count);
2234 if (crv != CKR_OK) {
2235 /* set error */
2236 goto loser;
2241 * STEP 2: read the encrypt the attributes with the new key.
2243 for (i=0; i < count; i++) {
2244 SECItem plainText;
2245 SECItem *result;
2246 SECStatus rv;
2248 plainText.data = first[i].pValue;
2249 plainText.len = first[i].ulValueLen;
2250 rv = sftkdb_encrypt(arena, newKey, &plainText, &result);
2251 if (rv != SECSuccess) {
2252 goto loser;
2254 first[i].pValue = result->data;
2255 first[i].ulValueLen = result->len;
2256 /* clear our sensitive data out */
2257 PORT_Memset(plainText.data, 0, plainText.len);
2262 * STEP 3: write the newly encrypted attributes out directly
2264 id &= SFTK_OBJ_ID_MASK;
2265 keydb->newKey = newKey;
2266 crv = (*keydb->db->sdb_SetAttributeValue)(keydb->db, id, first, count);
2267 keydb->newKey = NULL;
2268 if (crv != CKR_OK) {
2269 /* set error */
2270 goto loser;
2273 /* free up our mess */
2274 /* NOTE: at this point we know we've cleared out any unencrypted data */
2275 PORT_FreeArena(arena, PR_FALSE);
2276 return SECSuccess;
2278 loser:
2279 /* there may be unencrypted data, clear it out down */
2280 PORT_FreeArena(arena, PR_TRUE);
2281 return SECFailure;
2286 * must be called with the old key active.
2288 SECStatus
2289 sftkdb_convertPrivateObjects(SFTKDBHandle *keydb, SECItem *newKey)
2291 SDBFind *find = NULL;
2292 CK_ULONG idCount = MAX_IDS;
2293 CK_OBJECT_HANDLE ids[MAX_IDS];
2294 CK_RV crv, crv2;
2295 int i;
2297 /* find all the private objects */
2298 crv = sftkdb_FindObjectsInit(keydb, NULL, 0, &find);
2300 if (crv != CKR_OK) {
2301 /* set error */
2302 return SECFailure;
2304 while ((crv == CKR_OK) && (idCount == MAX_IDS)) {
2305 crv = sftkdb_FindObjects(keydb, find, ids, MAX_IDS, &idCount);
2306 for (i=0; (crv == CKR_OK) && (i < idCount); i++) {
2307 SECStatus rv;
2308 rv = sftk_convertPrivateAttributes(keydb, ids[i], newKey);
2309 if (rv != SECSuccess) {
2310 crv = CKR_GENERAL_ERROR;
2311 /* error should be already set here */
2315 crv2 = sftkdb_FindObjectsFinal(keydb, find);
2316 if (crv == CKR_OK) crv = crv2;
2317 if (crv != CKR_OK) {
2318 /* set error */
2319 return SECFailure;
2321 return SECSuccess;
2326 * change the database password.
2328 SECStatus
2329 sftkdb_ChangePassword(SFTKDBHandle *keydb, char *oldPin, char *newPin)
2331 SECStatus rv = SECSuccess;
2332 SECItem plainText;
2333 SECItem newKey;
2334 SECItem *result = NULL;
2335 SDBPasswordEntry entry;
2336 CK_RV crv;
2337 SDB *db;
2339 if (keydb == NULL) {
2340 return SECFailure;
2343 db = GET_SDB(keydb);
2344 if (db == NULL) {
2345 return SECFailure;
2348 newKey.data = NULL;
2350 /* make sure we have a valid old pin */
2351 crv = (*keydb->db->sdb_Begin)(keydb->db);
2352 if (crv != CKR_OK) {
2353 rv = SECFailure;
2354 goto loser;
2356 crv = (*db->sdb_GetPWEntry)(db, &entry);
2357 if (crv == CKR_OK) {
2358 rv = sftkdb_CheckPassword(keydb, oldPin);
2359 if (rv == SECFailure) {
2360 goto loser;
2362 } else {
2363 entry.salt.data = entry.data;
2364 entry.salt.len = SALT_LENGTH;
2365 RNG_GenerateGlobalRandomBytes(entry.data,entry.salt.len);
2368 rv = sftkdb_passwordToKey(keydb, &entry, newPin, &newKey);
2369 if (rv != SECSuccess) {
2370 goto loser;
2375 * convert encrypted entries here.
2377 rv = sftkdb_convertPrivateObjects(keydb, &newKey);
2378 if (rv != SECSuccess) {
2379 goto loser;
2383 plainText.data = (unsigned char *)SFTK_PW_CHECK_STRING;
2384 plainText.len = SFTK_PW_CHECK_LEN;
2386 rv = sftkdb_encrypt(NULL, &newKey, &plainText, &result);
2387 if (rv != SECSuccess) {
2388 goto loser;
2390 entry.value.data = result->data;
2391 entry.value.len = result->len;
2392 crv = (*keydb->db->sdb_PutPWEntry)(keydb->db, &entry);
2393 if (crv != CKR_OK) {
2394 rv = SECFailure;
2395 goto loser;
2397 crv = (*keydb->db->sdb_Commit)(keydb->db);
2398 if (crv != CKR_OK) {
2399 rv = SECFailure;
2400 goto loser;
2403 keydb->newKey = NULL;
2405 sftkdb_switchKeys(keydb, &newKey);
2407 loser:
2408 if (newKey.data) {
2409 PORT_ZFree(newKey.data,newKey.len);
2411 if (result) {
2412 SECITEM_FreeItem(result, PR_FALSE);
2414 if (rv != SECSuccess) {
2415 (*keydb->db->sdb_Abort)(keydb->db);
2418 return rv;
2422 * loose our cached password
2424 SECStatus
2425 sftkdb_ClearPassword(SFTKDBHandle *keydb)
2427 SECItem oldKey;
2428 oldKey.data = NULL;
2429 oldKey.len = 0;
2430 sftkdb_switchKeys(keydb, &oldKey);
2431 if (oldKey.data) {
2432 PORT_ZFree(oldKey.data, oldKey.len);
2434 return SECSuccess;
2437 /******************************************************************
2438 * DB handle managing functions.
2440 * These functions are called by softoken to initialize, acquire,
2441 * and release database handles.
2444 /* release a database handle */
2445 void
2446 sftk_freeDB(SFTKDBHandle *handle)
2448 PRInt32 ref;
2450 if (!handle) return;
2451 ref = PR_AtomicDecrement(&handle->ref);
2452 if (ref == 0) {
2453 sftkdb_CloseDB(handle);
2455 return;
2460 * acquire a database handle for a certificate db
2461 * (database for public objects)
2463 SFTKDBHandle *
2464 sftk_getCertDB(SFTKSlot *slot)
2466 SFTKDBHandle *dbHandle;
2468 PZ_Lock(slot->slotLock);
2469 dbHandle = slot->certDB;
2470 if (dbHandle) {
2471 PR_AtomicIncrement(&dbHandle->ref);
2473 PZ_Unlock(slot->slotLock);
2474 return dbHandle;
2478 * acquire a database handle for a key database
2479 * (database for private objects)
2481 SFTKDBHandle *
2482 sftk_getKeyDB(SFTKSlot *slot)
2484 SFTKDBHandle *dbHandle;
2486 PZ_Lock(slot->slotLock);
2487 dbHandle = slot->keyDB;
2488 if (dbHandle) {
2489 PR_AtomicIncrement(&dbHandle->ref);
2491 PZ_Unlock(slot->slotLock);
2492 return dbHandle;
2496 * acquire the database for a specific object. NOTE: objectID must point
2497 * to a Token object!
2499 SFTKDBHandle *
2500 sftk_getDBForTokenObject(SFTKSlot *slot, CK_OBJECT_HANDLE objectID)
2502 SFTKDBHandle *dbHandle;
2504 PZ_Lock(slot->slotLock);
2505 dbHandle = objectID & SFTK_KEYDB_TYPE ? slot->keyDB : slot->certDB;
2506 if (dbHandle) {
2507 PR_AtomicIncrement(&dbHandle->ref);
2509 PZ_Unlock(slot->slotLock);
2510 return dbHandle;
2514 * initialize a new database handle
2516 static SFTKDBHandle *
2517 sftk_NewDBHandle(SDB *sdb, int type)
2519 SFTKDBHandle *handle = PORT_New(SFTKDBHandle);
2520 handle->ref = 1;
2521 handle->db = sdb;
2522 handle->update = NULL;
2523 handle->peerDB = NULL;
2524 handle->newKey = NULL;
2525 handle->type = type;
2526 handle->passwordKey.data = NULL;
2527 handle->passwordKey.len = 0;
2528 handle->passwordLock = NULL;
2529 if (type == SFTK_KEYDB_TYPE) {
2530 handle->passwordLock = PZ_NewLock();
2532 sdb->app_private = handle;
2533 return handle;
2537 * reset the key database to it's uninitialized state. This call
2538 * will clear all the key entried.
2540 SECStatus
2541 sftkdb_ResetKeyDB(SFTKDBHandle *handle)
2543 CK_RV crv;
2545 /* only rest the key db */
2546 if (handle->type != SFTK_KEYDB_TYPE) {
2547 return SECFailure;
2549 crv = sftkdb_ResetDB(handle);
2550 if (crv != CKR_OK) {
2551 /* set error */
2552 return SECFailure;
2554 return SECSuccess;
2557 static PRBool
2558 sftk_oldVersionExists(const char *dir, int version)
2560 int i;
2561 PRStatus exists = PR_FAILURE;
2562 char *file = NULL;
2564 for (i=version; i > 1 ; i--) {
2565 file = PR_smprintf("%s%d.db",dir,i);
2566 if (file == NULL) {
2567 continue;
2569 exists = PR_Access(file, PR_ACCESS_EXISTS);
2570 PR_smprintf_free(file);
2571 if (exists == PR_SUCCESS) {
2572 return PR_TRUE;
2575 return PR_FALSE;
2578 static PRBool
2579 sftk_hasLegacyDB(const char *confdir, const char *certPrefix,
2580 const char *keyPrefix, int certVersion, int keyVersion)
2582 char *dir;
2583 PRBool exists;
2585 dir= PR_smprintf("%s/%scert", confdir, certPrefix);
2586 if (dir == NULL) {
2587 return PR_FALSE;
2590 exists = sftk_oldVersionExists(dir, certVersion);
2591 PR_smprintf_free(dir);
2592 if (exists) {
2593 return PR_TRUE;
2596 dir= PR_smprintf("%s/%skey", confdir, keyPrefix);
2597 if (dir == NULL) {
2598 return PR_FALSE;
2601 exists = sftk_oldVersionExists(dir, keyVersion);
2602 PR_smprintf_free(dir);
2603 return exists;
2607 * initialize certificate and key database handles as a pair.
2609 * This function figures out what type of database we are opening and
2610 * calls the appropriate low level function to open the database.
2611 * It also figures out whether or not to setup up automatic update.
2613 CK_RV
2614 sftk_DBInit(const char *configdir, const char *certPrefix,
2615 const char *keyPrefix, PRBool readOnly, PRBool noCertDB,
2616 PRBool noKeyDB, PRBool forceOpen,
2617 SFTKDBHandle **certDB, SFTKDBHandle **keyDB)
2619 const char *confdir;
2620 SDBType dbType;
2621 char *appName = NULL;
2622 SDB *keySDB, *certSDB;
2623 CK_RV crv = CKR_OK;
2624 int flags = SDB_RDONLY;
2625 PRBool newInit = PR_FALSE;
2626 PRBool needUpdate = PR_FALSE;
2628 if (!readOnly) {
2629 flags = SDB_CREATE;
2632 *certDB = NULL;
2633 *keyDB = NULL;
2635 if (noKeyDB && noCertDB) {
2636 return CKR_OK;
2638 confdir = sftk_EvaluateConfigDir(configdir, &dbType, &appName);
2641 * now initialize the appropriate database
2643 switch (dbType) {
2644 case SDB_LEGACY:
2645 crv = sftkdbCall_open(confdir, certPrefix, keyPrefix, 8, 3, flags,
2646 noCertDB? NULL : &certSDB, noKeyDB ? NULL: &keySDB);
2647 break;
2648 case SDB_MULTIACCESS:
2649 crv = sftkdbCall_open(configdir, certPrefix, keyPrefix, 8, 3, flags,
2650 noCertDB? NULL : &certSDB, noKeyDB ? NULL: &keySDB);
2651 break;
2652 case SDB_SQL:
2653 case SDB_EXTERN: /* SHOULD open a loadable db */
2654 crv = s_open(confdir, certPrefix, keyPrefix, 9, 4, flags,
2655 noCertDB? NULL : &certSDB, noKeyDB ? NULL : &keySDB, &newInit);
2658 * if we failed to open the DB's read only, use the old ones if
2659 * the exists.
2661 if (crv != CKR_OK && (flags == SDB_RDONLY)) {
2662 if (sftk_hasLegacyDB(confdir, certPrefix, keyPrefix, 8, 3)) {
2663 /* we have legacy databases, if we failed to open the new format
2664 * DB's read only, just use the legacy ones */
2665 crv = sftkdbCall_open(confdir, certPrefix,
2666 keyPrefix, 8, 3, flags, noCertDB? NULL : &certSDB,
2667 noKeyDB ? NULL : &keySDB);
2669 } else if (newInit && crv == CKR_OK) {
2670 /* if the new format DB was also a newly created DB, and we
2671 * succeeded, then need to update that new database with data
2672 * from the existing legacy DB */
2673 if (sftk_hasLegacyDB(confdir, certPrefix, keyPrefix, 8, 3)) {
2674 needUpdate = 1;
2677 break;
2678 default:
2679 crv = CKR_GENERAL_ERROR; /* can't happen, EvaluationConfigDir MUST
2680 * return one of the types we already
2681 * specified. */
2683 if (crv != CKR_OK) {
2684 goto loser;
2686 if (!noCertDB) {
2687 *certDB = sftk_NewDBHandle(certSDB, SFTK_CERTDB_TYPE);
2688 } else {
2689 *certDB = NULL;
2691 if (!noKeyDB) {
2692 *keyDB = sftk_NewDBHandle(keySDB, SFTK_KEYDB_TYPE);
2693 } else {
2694 *keyDB = NULL;
2697 /* link them together */
2698 if (*certDB) {
2699 (*certDB)->peerDB = *keyDB;
2701 if (*keyDB) {
2702 (*keyDB)->peerDB = *certDB;
2705 if (needUpdate) {
2706 SDB *updateCert = NULL;
2707 SDB *updateKey = NULL;
2708 CK_RV crv2;
2710 crv2 = sftkdbCall_open(confdir, certPrefix, keyPrefix, 8, 3, flags,
2711 noCertDB ? NULL : &updateCert, noKeyDB ? NULL : &updateKey);
2712 if (crv2 == CKR_OK) {
2713 if (*certDB) {
2714 (*certDB)->update = updateCert;
2715 updateCert->app_private = (*certDB);
2717 if (*keyDB) {
2718 (*keyDB)->update = updateKey;
2719 updateKey->app_private = (*keyDB);
2720 } else {
2721 /* we don't have a key DB, update the certificate DB now */
2722 sftkdb_update(*certDB, NULL);
2726 loser:
2727 if (appName) {
2728 PORT_Free(appName);
2730 return forceOpen ? CKR_OK : crv;
2733 CK_RV
2734 sftkdb_Shutdown(void)
2736 s_shutdown();
2737 sftkdbCall_Shutdown();
2738 return CKR_OK;