comparison nss/lib/softoken/pkcs11u.c @ 0:1e5118fa0cb1

This is NSS with a Cmake Buildsyste To compile a static NSS library for Windows we've used the Chromium-NSS fork and added a Cmake buildsystem to compile it statically for Windows. See README.chromium for chromium changes and README.trustbridge for our modifications.
author Andre Heinecke <andre.heinecke@intevation.de>
date Mon, 28 Jul 2014 10:47:06 +0200
parents
children
comparison
equal deleted inserted replaced
-1:000000000000 0:1e5118fa0cb1
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 * Internal PKCS #11 functions. Should only be called by pkcs11.c
6 */
7 #include "pkcs11.h"
8 #include "pkcs11i.h"
9 #include "lowkeyi.h"
10 #include "secasn1.h"
11 #include "blapi.h"
12 #include "secerr.h"
13 #include "prnetdb.h" /* for PR_ntohl */
14 #include "sftkdb.h"
15 #include "softoken.h"
16
17 /*
18 * ******************** Attribute Utilities *******************************
19 */
20
21 /*
22 * create a new attribute with type, value, and length. Space is allocated
23 * to hold value.
24 */
25 static SFTKAttribute *
26 sftk_NewAttribute(SFTKObject *object,
27 CK_ATTRIBUTE_TYPE type, const void *value, CK_ULONG len)
28 {
29 SFTKAttribute *attribute;
30
31 SFTKSessionObject *so = sftk_narrowToSessionObject(object);
32 int index;
33
34 if (so == NULL) {
35 /* allocate new attribute in a buffer */
36 PORT_Assert(0);
37 return NULL;
38 }
39 /*
40 * We attempt to keep down contention on Malloc and Arena locks by
41 * limiting the number of these calls on high traversed paths. This
42 * is done for attributes by 'allocating' them from a pool already
43 * allocated by the parent object.
44 */
45 PZ_Lock(so->attributeLock);
46 index = so->nextAttr++;
47 PZ_Unlock(so->attributeLock);
48 PORT_Assert(index < MAX_OBJS_ATTRS);
49 if (index >= MAX_OBJS_ATTRS) return NULL;
50
51 attribute = &so->attrList[index];
52 attribute->attrib.type = type;
53 attribute->freeAttr = PR_FALSE;
54 attribute->freeData = PR_FALSE;
55 if (value) {
56 if (len <= ATTR_SPACE) {
57 attribute->attrib.pValue = attribute->space;
58 } else {
59 attribute->attrib.pValue = PORT_Alloc(len);
60 attribute->freeData = PR_TRUE;
61 }
62 if (attribute->attrib.pValue == NULL) {
63 return NULL;
64 }
65 PORT_Memcpy(attribute->attrib.pValue,value,len);
66 attribute->attrib.ulValueLen = len;
67 } else {
68 attribute->attrib.pValue = NULL;
69 attribute->attrib.ulValueLen = 0;
70 }
71 attribute->attrib.type = type;
72 attribute->handle = type;
73 attribute->next = attribute->prev = NULL;
74 return attribute;
75 }
76
77 /*
78 * Free up all the memory associated with an attribute. Reference count
79 * must be zero to call this.
80 */
81 static void
82 sftk_DestroyAttribute(SFTKAttribute *attribute)
83 {
84 if (attribute->freeData) {
85 if (attribute->attrib.pValue) {
86 /* clear out the data in the attribute value... it may have been
87 * sensitive data */
88 PORT_Memset(attribute->attrib.pValue, 0,
89 attribute->attrib.ulValueLen);
90 }
91 PORT_Free(attribute->attrib.pValue);
92 }
93 PORT_Free(attribute);
94 }
95
96 /*
97 * release a reference to an attribute structure
98 */
99 void
100 sftk_FreeAttribute(SFTKAttribute *attribute)
101 {
102 if (attribute->freeAttr) {
103 sftk_DestroyAttribute(attribute);
104 return;
105 }
106 }
107
108 static SFTKAttribute *
109 sftk_FindTokenAttribute(SFTKTokenObject *object,CK_ATTRIBUTE_TYPE type)
110 {
111 SFTKAttribute *myattribute = NULL;
112 SFTKDBHandle *dbHandle = NULL;
113 CK_RV crv = CKR_HOST_MEMORY;
114
115 myattribute = (SFTKAttribute*)PORT_Alloc(sizeof(SFTKAttribute));
116 if (myattribute == NULL) {
117 goto loser;
118 }
119
120 dbHandle = sftk_getDBForTokenObject(object->obj.slot, object->obj.handle);
121
122 myattribute->handle = type;
123 myattribute->attrib.type = type;
124 myattribute->attrib.pValue = myattribute->space;
125 myattribute->attrib.ulValueLen = ATTR_SPACE;
126 myattribute->next = myattribute->prev = NULL;
127 myattribute->freeAttr = PR_TRUE;
128 myattribute->freeData = PR_FALSE;
129
130 crv = sftkdb_GetAttributeValue(dbHandle, object->obj.handle,
131 &myattribute->attrib, 1);
132
133 /* attribute is bigger than our attribute space buffer, malloc it */
134 if (crv == CKR_BUFFER_TOO_SMALL) {
135 myattribute->attrib.pValue = NULL;
136 crv = sftkdb_GetAttributeValue(dbHandle, object->obj.handle,
137 &myattribute->attrib, 1);
138 if (crv != CKR_OK) {
139 goto loser;
140 }
141 myattribute->attrib.pValue = PORT_Alloc(myattribute->attrib.ulValueLen);
142 if (myattribute->attrib.pValue == NULL) {
143 crv = CKR_HOST_MEMORY;
144 goto loser;
145 }
146 myattribute->freeData = PR_TRUE;
147 crv = sftkdb_GetAttributeValue(dbHandle, object->obj.handle,
148 &myattribute->attrib, 1);
149 }
150 loser:
151 if (dbHandle) {
152 sftk_freeDB(dbHandle);
153 }
154 if (crv != CKR_OK) {
155 if (myattribute) {
156 myattribute->attrib.ulValueLen = 0;
157 sftk_FreeAttribute(myattribute);
158 myattribute = NULL;
159 }
160 }
161 return myattribute;
162 }
163
164 /*
165 * look up and attribute structure from a type and Object structure.
166 * The returned attribute is referenced and needs to be freed when
167 * it is no longer needed.
168 */
169 SFTKAttribute *
170 sftk_FindAttribute(SFTKObject *object,CK_ATTRIBUTE_TYPE type)
171 {
172 SFTKAttribute *attribute;
173 SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
174
175 if (sessObject == NULL) {
176 return sftk_FindTokenAttribute(sftk_narrowToTokenObject(object),type);
177 }
178
179 PZ_Lock(sessObject->attributeLock);
180 sftkqueue_find(attribute,type,sessObject->head, sessObject->hashSize);
181 PZ_Unlock(sessObject->attributeLock);
182
183 return(attribute);
184 }
185
186 /*
187 * Take a buffer and it's length and return it's true size in bits;
188 */
189 unsigned int
190 sftk_GetLengthInBits(unsigned char *buf, unsigned int bufLen)
191 {
192 unsigned int size = bufLen * 8;
193 unsigned int i;
194
195 /* Get the real length in bytes */
196 for (i=0; i < bufLen; i++) {
197 unsigned char c = *buf++;
198 if (c != 0) {
199 unsigned char m;
200 for (m=0x80; m > 0 ; m = m >> 1) {
201 if ((c & m) != 0) {
202 break;
203 }
204 size--;
205 }
206 break;
207 }
208 size-=8;
209 }
210 return size;
211 }
212
213 /*
214 * Constrain a big num attribute. to size and padding
215 * minLength means length of the object must be greater than equal to minLength
216 * maxLength means length of the object must be less than equal to maxLength
217 * minMultiple means that object length mod minMultiple must equal 0.
218 * all input sizes are in bits.
219 * if any constraint is '0' that constraint is not checked.
220 */
221 CK_RV
222 sftk_ConstrainAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
223 int minLength, int maxLength, int minMultiple)
224 {
225 SFTKAttribute *attribute;
226 int size;
227 unsigned char *ptr;
228
229 attribute = sftk_FindAttribute(object, type);
230 if (!attribute) {
231 return CKR_TEMPLATE_INCOMPLETE;
232 }
233 ptr = (unsigned char *) attribute->attrib.pValue;
234 if (ptr == NULL) {
235 sftk_FreeAttribute(attribute);
236 return CKR_ATTRIBUTE_VALUE_INVALID;
237 }
238 size = sftk_GetLengthInBits(ptr, attribute->attrib.ulValueLen);
239 sftk_FreeAttribute(attribute);
240
241 if ((minLength != 0) && (size < minLength)) {
242 return CKR_ATTRIBUTE_VALUE_INVALID;
243 }
244 if ((maxLength != 0) && (size > maxLength)) {
245 return CKR_ATTRIBUTE_VALUE_INVALID;
246 }
247 if ((minMultiple != 0) && ((size % minMultiple) != 0)) {
248 return CKR_ATTRIBUTE_VALUE_INVALID;
249 }
250 return CKR_OK;
251 }
252
253 PRBool
254 sftk_hasAttributeToken(SFTKTokenObject *object, CK_ATTRIBUTE_TYPE type)
255 {
256 CK_ATTRIBUTE template;
257 CK_RV crv;
258 SFTKDBHandle *dbHandle;
259
260 dbHandle = sftk_getDBForTokenObject(object->obj.slot, object->obj.handle);
261 template.type = type;
262 template.pValue = NULL;
263 template.ulValueLen = 0;
264
265 crv = sftkdb_GetAttributeValue(dbHandle, object->obj.handle, &template, 1);
266 sftk_freeDB(dbHandle);
267
268 /* attribute is bigger than our attribute space buffer, malloc it */
269 return (crv == CKR_OK) ? PR_TRUE : PR_FALSE;
270 }
271
272 /*
273 * return true if object has attribute
274 */
275 PRBool
276 sftk_hasAttribute(SFTKObject *object,CK_ATTRIBUTE_TYPE type)
277 {
278 SFTKAttribute *attribute;
279 SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
280
281 if (sessObject == NULL) {
282 return sftk_hasAttributeToken(sftk_narrowToTokenObject(object), type);
283 }
284
285 PZ_Lock(sessObject->attributeLock);
286 sftkqueue_find(attribute,type,sessObject->head, sessObject->hashSize);
287 PZ_Unlock(sessObject->attributeLock);
288
289 return (PRBool)(attribute != NULL);
290 }
291
292 /*
293 * add an attribute to an object
294 */
295 static void
296 sftk_AddAttribute(SFTKObject *object,SFTKAttribute *attribute)
297 {
298 SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
299
300 if (sessObject == NULL) return;
301 PZ_Lock(sessObject->attributeLock);
302 sftkqueue_add(attribute,attribute->handle,
303 sessObject->head, sessObject->hashSize);
304 PZ_Unlock(sessObject->attributeLock);
305 }
306
307 /*
308 * copy an unsigned attribute into a SECItem. Secitem is allocated in
309 * the specified arena.
310 */
311 CK_RV
312 sftk_Attribute2SSecItem(PLArenaPool *arena,SECItem *item,SFTKObject *object,
313 CK_ATTRIBUTE_TYPE type)
314 {
315 SFTKAttribute *attribute;
316
317 item->data = NULL;
318
319 attribute = sftk_FindAttribute(object, type);
320 if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE;
321
322 (void)SECITEM_AllocItem(arena, item, attribute->attrib.ulValueLen);
323 if (item->data == NULL) {
324 sftk_FreeAttribute(attribute);
325 return CKR_HOST_MEMORY;
326 }
327 PORT_Memcpy(item->data, attribute->attrib.pValue, item->len);
328 sftk_FreeAttribute(attribute);
329 return CKR_OK;
330 }
331
332 /*
333 * fetch multiple attributes into SECItems. Secitem data is allocated in
334 * the specified arena.
335 */
336 CK_RV
337 sftk_MultipleAttribute2SecItem(PLArenaPool *arena, SFTKObject *object,
338 SFTKItemTemplate *itemTemplate, int itemTemplateCount)
339 {
340
341 CK_RV crv = CKR_OK;
342 CK_ATTRIBUTE templateSpace[SFTK_MAX_ITEM_TEMPLATE];
343 CK_ATTRIBUTE *template;
344 SFTKTokenObject *tokObject;
345 SFTKDBHandle *dbHandle = NULL;
346 int i;
347
348 tokObject = sftk_narrowToTokenObject(object);
349
350 /* session objects, just loop through the list */
351 if (tokObject == NULL) {
352 for (i=0; i < itemTemplateCount; i++) {
353 crv = sftk_Attribute2SecItem(arena,itemTemplate[i].item, object,
354 itemTemplate[i].type);
355 if (crv != CKR_OK) {
356 return crv;
357 }
358 }
359 return CKR_OK;
360 }
361
362 /* don't do any work if none is required */
363 if (itemTemplateCount == 0) {
364 return CKR_OK;
365 }
366
367 /* don't allocate the template unless we need it */
368 if (itemTemplateCount > SFTK_MAX_ITEM_TEMPLATE) {
369 template = PORT_NewArray(CK_ATTRIBUTE, itemTemplateCount);
370 } else {
371 template = templateSpace;
372 }
373
374 if (template == NULL) {
375 crv = CKR_HOST_MEMORY;
376 goto loser;
377 }
378
379 dbHandle = sftk_getDBForTokenObject(object->slot, object->handle);
380 if (dbHandle == NULL) {
381 crv = CKR_OBJECT_HANDLE_INVALID;
382 goto loser;
383 }
384
385 /* set up the PKCS #11 template */
386 for (i=0; i < itemTemplateCount; i++) {
387 template[i].type = itemTemplate[i].type;
388 template[i].pValue = NULL;
389 template[i].ulValueLen = 0;
390 }
391
392 /* fetch the attribute lengths */
393 crv = sftkdb_GetAttributeValue(dbHandle, object->handle,
394 template, itemTemplateCount);
395 if (crv != CKR_OK) {
396 goto loser;
397 }
398
399 /* allocate space for the attributes */
400 for (i=0; i < itemTemplateCount ; i++) {
401 template[i].pValue = PORT_ArenaAlloc(arena, template[i].ulValueLen);
402 if (template[i].pValue == NULL) {
403 crv = CKR_HOST_MEMORY;
404 goto loser;
405 }
406 }
407
408 /* fetch the attributes */
409 crv = sftkdb_GetAttributeValue(dbHandle, object->handle,
410 template, itemTemplateCount);
411 if (crv != CKR_OK) {
412 goto loser;
413 }
414
415 /* Fill in the items */
416 for (i=0; i < itemTemplateCount; i++) {
417 itemTemplate[i].item->data = template[i].pValue;
418 itemTemplate[i].item->len = template[i].ulValueLen;
419 }
420
421 loser:
422 if (template != templateSpace) {
423 PORT_Free(template);
424 }
425 if (dbHandle) {
426 sftk_freeDB(dbHandle);
427 }
428
429 return crv;
430 }
431
432
433 /*
434 * delete an attribute from an object
435 */
436 static void
437 sftk_DeleteAttribute(SFTKObject *object, SFTKAttribute *attribute)
438 {
439 SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
440
441 if (sessObject == NULL) {
442 return ;
443 }
444 PZ_Lock(sessObject->attributeLock);
445 if (sftkqueue_is_queued(attribute,attribute->handle,
446 sessObject->head, sessObject->hashSize)) {
447 sftkqueue_delete(attribute,attribute->handle,
448 sessObject->head, sessObject->hashSize);
449 }
450 PZ_Unlock(sessObject->attributeLock);
451 }
452
453 /*
454 * this is only valid for CK_BBOOL type attributes. Return the state
455 * of that attribute.
456 */
457 PRBool
458 sftk_isTrue(SFTKObject *object,CK_ATTRIBUTE_TYPE type)
459 {
460 SFTKAttribute *attribute;
461 PRBool tok = PR_FALSE;
462
463 attribute=sftk_FindAttribute(object,type);
464 if (attribute == NULL) { return PR_FALSE; }
465 tok = (PRBool)(*(CK_BBOOL *)attribute->attrib.pValue);
466 sftk_FreeAttribute(attribute);
467
468 return tok;
469 }
470
471 /*
472 * force an attribute to null.
473 * this is for sensitive keys which are stored in the database, we don't
474 * want to keep this info around in memory in the clear.
475 */
476 void
477 sftk_nullAttribute(SFTKObject *object,CK_ATTRIBUTE_TYPE type)
478 {
479 SFTKAttribute *attribute;
480
481 attribute=sftk_FindAttribute(object,type);
482 if (attribute == NULL) return;
483
484 if (attribute->attrib.pValue != NULL) {
485 PORT_Memset(attribute->attrib.pValue,0,attribute->attrib.ulValueLen);
486 if (attribute->freeData) {
487 PORT_Free(attribute->attrib.pValue);
488 }
489 attribute->freeData = PR_FALSE;
490 attribute->attrib.pValue = NULL;
491 attribute->attrib.ulValueLen = 0;
492 }
493 sftk_FreeAttribute(attribute);
494 }
495
496
497 static CK_RV
498 sftk_forceTokenAttribute(SFTKObject *object,CK_ATTRIBUTE_TYPE type,
499 const void *value, unsigned int len)
500 {
501 CK_ATTRIBUTE attribute;
502 SFTKDBHandle *dbHandle = NULL;
503 SFTKTokenObject *to = sftk_narrowToTokenObject(object);
504 CK_RV crv;
505
506 PORT_Assert(to);
507 if (to == NULL) {
508 return CKR_DEVICE_ERROR;
509 }
510
511 dbHandle = sftk_getDBForTokenObject(object->slot, object->handle);
512
513 attribute.type = type;
514 attribute.pValue = (void *)value;
515 attribute.ulValueLen = len;
516
517 crv = sftkdb_SetAttributeValue(dbHandle, object, &attribute, 1);
518 sftk_freeDB(dbHandle);
519 return crv;
520 }
521
522 /*
523 * force an attribute to a specifc value.
524 */
525 CK_RV
526 sftk_forceAttribute(SFTKObject *object,CK_ATTRIBUTE_TYPE type,
527 const void *value, unsigned int len)
528 {
529 SFTKAttribute *attribute;
530 void *att_val = NULL;
531 PRBool freeData = PR_FALSE;
532
533 PORT_Assert(object);
534 PORT_Assert(object->refCount);
535 PORT_Assert(object->slot);
536 if (!object ||
537 !object->refCount ||
538 !object->slot) {
539 return CKR_DEVICE_ERROR;
540 }
541 if (sftk_isToken(object->handle)) {
542 return sftk_forceTokenAttribute(object,type,value,len);
543 }
544 attribute=sftk_FindAttribute(object,type);
545 if (attribute == NULL) return sftk_AddAttributeType(object,type,value,len);
546
547
548 if (value) {
549 if (len <= ATTR_SPACE) {
550 att_val = attribute->space;
551 } else {
552 att_val = PORT_Alloc(len);
553 freeData = PR_TRUE;
554 }
555 if (att_val == NULL) {
556 return CKR_HOST_MEMORY;
557 }
558 if (attribute->attrib.pValue == att_val) {
559 PORT_Memset(attribute->attrib.pValue,0,
560 attribute->attrib.ulValueLen);
561 }
562 PORT_Memcpy(att_val,value,len);
563 }
564 if (attribute->attrib.pValue != NULL) {
565 if (attribute->attrib.pValue != att_val) {
566 PORT_Memset(attribute->attrib.pValue,0,
567 attribute->attrib.ulValueLen);
568 }
569 if (attribute->freeData) {
570 PORT_Free(attribute->attrib.pValue);
571 }
572 attribute->freeData = PR_FALSE;
573 attribute->attrib.pValue = NULL;
574 attribute->attrib.ulValueLen = 0;
575 }
576 if (att_val) {
577 attribute->attrib.pValue = att_val;
578 attribute->attrib.ulValueLen = len;
579 attribute->freeData = freeData;
580 }
581 sftk_FreeAttribute(attribute);
582 return CKR_OK;
583 }
584
585 /*
586 * return a null terminated string from attribute 'type'. This string
587 * is allocated and needs to be freed with PORT_Free() When complete.
588 */
589 char *
590 sftk_getString(SFTKObject *object,CK_ATTRIBUTE_TYPE type)
591 {
592 SFTKAttribute *attribute;
593 char *label = NULL;
594
595 attribute=sftk_FindAttribute(object,type);
596 if (attribute == NULL) return NULL;
597
598 if (attribute->attrib.pValue != NULL) {
599 label = (char *) PORT_Alloc(attribute->attrib.ulValueLen+1);
600 if (label == NULL) {
601 sftk_FreeAttribute(attribute);
602 return NULL;
603 }
604
605 PORT_Memcpy(label,attribute->attrib.pValue,
606 attribute->attrib.ulValueLen);
607 label[attribute->attrib.ulValueLen] = 0;
608 }
609 sftk_FreeAttribute(attribute);
610 return label;
611 }
612
613 /*
614 * decode when a particular attribute may be modified
615 * SFTK_NEVER: This attribute must be set at object creation time and
616 * can never be modified.
617 * SFTK_ONCOPY: This attribute may be modified only when you copy the
618 * object.
619 * SFTK_SENSITIVE: The CKA_SENSITIVE attribute can only be changed from
620 * CK_FALSE to CK_TRUE.
621 * SFTK_ALWAYS: This attribute can always be modified.
622 * Some attributes vary their modification type based on the class of the
623 * object.
624 */
625 SFTKModifyType
626 sftk_modifyType(CK_ATTRIBUTE_TYPE type, CK_OBJECT_CLASS inClass)
627 {
628 /* if we don't know about it, user user defined, always allow modify */
629 SFTKModifyType mtype = SFTK_ALWAYS;
630
631 switch(type) {
632 /* NEVER */
633 case CKA_CLASS:
634 case CKA_CERTIFICATE_TYPE:
635 case CKA_KEY_TYPE:
636 case CKA_MODULUS:
637 case CKA_MODULUS_BITS:
638 case CKA_PUBLIC_EXPONENT:
639 case CKA_PRIVATE_EXPONENT:
640 case CKA_PRIME:
641 case CKA_SUBPRIME:
642 case CKA_BASE:
643 case CKA_PRIME_1:
644 case CKA_PRIME_2:
645 case CKA_EXPONENT_1:
646 case CKA_EXPONENT_2:
647 case CKA_COEFFICIENT:
648 case CKA_VALUE_LEN:
649 case CKA_ALWAYS_SENSITIVE:
650 case CKA_NEVER_EXTRACTABLE:
651 case CKA_NETSCAPE_DB:
652 mtype = SFTK_NEVER;
653 break;
654
655 /* ONCOPY */
656 case CKA_TOKEN:
657 case CKA_PRIVATE:
658 case CKA_MODIFIABLE:
659 mtype = SFTK_ONCOPY;
660 break;
661
662 /* SENSITIVE */
663 case CKA_SENSITIVE:
664 case CKA_EXTRACTABLE:
665 mtype = SFTK_SENSITIVE;
666 break;
667
668 /* ALWAYS */
669 case CKA_LABEL:
670 case CKA_APPLICATION:
671 case CKA_ID:
672 case CKA_SERIAL_NUMBER:
673 case CKA_START_DATE:
674 case CKA_END_DATE:
675 case CKA_DERIVE:
676 case CKA_ENCRYPT:
677 case CKA_DECRYPT:
678 case CKA_SIGN:
679 case CKA_VERIFY:
680 case CKA_SIGN_RECOVER:
681 case CKA_VERIFY_RECOVER:
682 case CKA_WRAP:
683 case CKA_UNWRAP:
684 mtype = SFTK_ALWAYS;
685 break;
686
687 /* DEPENDS ON CLASS */
688 case CKA_VALUE:
689 mtype = (inClass == CKO_DATA) ? SFTK_ALWAYS : SFTK_NEVER;
690 break;
691
692 case CKA_SUBJECT:
693 mtype = (inClass == CKO_CERTIFICATE) ? SFTK_NEVER : SFTK_ALWAYS;
694 break;
695 default:
696 break;
697 }
698 return mtype;
699 }
700
701 /* decode if a particular attribute is sensitive (cannot be read
702 * back to the user of if the object is set to SENSITIVE) */
703 PRBool
704 sftk_isSensitive(CK_ATTRIBUTE_TYPE type, CK_OBJECT_CLASS inClass)
705 {
706 switch(type) {
707 /* ALWAYS */
708 case CKA_PRIVATE_EXPONENT:
709 case CKA_PRIME_1:
710 case CKA_PRIME_2:
711 case CKA_EXPONENT_1:
712 case CKA_EXPONENT_2:
713 case CKA_COEFFICIENT:
714 return PR_TRUE;
715
716 /* DEPENDS ON CLASS */
717 case CKA_VALUE:
718 /* PRIVATE and SECRET KEYS have SENSITIVE values */
719 return (PRBool)((inClass == CKO_PRIVATE_KEY) || (inClass == CKO_SECRET_KEY));
720
721 default:
722 break;
723 }
724 return PR_FALSE;
725 }
726
727 /*
728 * copy an attribute into a SECItem. Secitem is allocated in the specified
729 * arena.
730 */
731 CK_RV
732 sftk_Attribute2SecItem(PLArenaPool *arena,SECItem *item,SFTKObject *object,
733 CK_ATTRIBUTE_TYPE type)
734 {
735 int len;
736 SFTKAttribute *attribute;
737
738 attribute = sftk_FindAttribute(object, type);
739 if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE;
740 len = attribute->attrib.ulValueLen;
741
742 if (arena) {
743 item->data = (unsigned char *) PORT_ArenaAlloc(arena,len);
744 } else {
745 item->data = (unsigned char *) PORT_Alloc(len);
746 }
747 if (item->data == NULL) {
748 sftk_FreeAttribute(attribute);
749 return CKR_HOST_MEMORY;
750 }
751 item->len = len;
752 PORT_Memcpy(item->data,attribute->attrib.pValue, len);
753 sftk_FreeAttribute(attribute);
754 return CKR_OK;
755 }
756
757 CK_RV
758 sftk_GetULongAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
759 CK_ULONG *longData)
760 {
761 SFTKAttribute *attribute;
762
763 attribute = sftk_FindAttribute(object, type);
764 if (attribute == NULL) return CKR_TEMPLATE_INCOMPLETE;
765
766 if (attribute->attrib.ulValueLen != sizeof(CK_ULONG)) {
767 return CKR_ATTRIBUTE_VALUE_INVALID;
768 }
769
770 *longData = *(CK_ULONG *)attribute->attrib.pValue;
771 sftk_FreeAttribute(attribute);
772 return CKR_OK;
773 }
774
775 void
776 sftk_DeleteAttributeType(SFTKObject *object,CK_ATTRIBUTE_TYPE type)
777 {
778 SFTKAttribute *attribute;
779 attribute = sftk_FindAttribute(object, type);
780 if (attribute == NULL) return ;
781 sftk_DeleteAttribute(object,attribute);
782 sftk_FreeAttribute(attribute);
783 }
784
785 CK_RV
786 sftk_AddAttributeType(SFTKObject *object,CK_ATTRIBUTE_TYPE type,
787 const void *valPtr, CK_ULONG length)
788 {
789 SFTKAttribute *attribute;
790 attribute = sftk_NewAttribute(object,type,valPtr,length);
791 if (attribute == NULL) { return CKR_HOST_MEMORY; }
792 sftk_AddAttribute(object,attribute);
793 return CKR_OK;
794 }
795
796 /*
797 * ******************** Object Utilities *******************************
798 */
799
800 /* must be called holding sftk_tokenKeyLock(slot) */
801 static SECItem *
802 sftk_lookupTokenKeyByHandle(SFTKSlot *slot, CK_OBJECT_HANDLE handle)
803 {
804 return (SECItem *)PL_HashTableLookup(slot->tokObjHashTable, (void *)handle);
805 }
806
807 /*
808 * use the refLock. This operations should be very rare, so the added
809 * contention on the ref lock should be lower than the overhead of adding
810 * a new lock. We use separate functions for this just in case I'm wrong.
811 */
812 static void
813 sftk_tokenKeyLock(SFTKSlot *slot) {
814 SKIP_AFTER_FORK(PZ_Lock(slot->objectLock));
815 }
816
817 static void
818 sftk_tokenKeyUnlock(SFTKSlot *slot) {
819 SKIP_AFTER_FORK(PZ_Unlock(slot->objectLock));
820 }
821
822 static PRIntn
823 sftk_freeHashItem(PLHashEntry* entry, PRIntn index, void *arg)
824 {
825 SECItem *item = (SECItem *)entry->value;
826
827 SECITEM_FreeItem(item, PR_TRUE);
828 return HT_ENUMERATE_NEXT;
829 }
830
831 CK_RV
832 SFTK_ClearTokenKeyHashTable(SFTKSlot *slot)
833 {
834 sftk_tokenKeyLock(slot);
835 PORT_Assert(!slot->present);
836 PL_HashTableEnumerateEntries(slot->tokObjHashTable, sftk_freeHashItem, NULL);
837 sftk_tokenKeyUnlock(slot);
838 return CKR_OK;
839 }
840
841
842 /* allocation hooks that allow us to recycle old object structures */
843 static SFTKObjectFreeList sessionObjectList = { NULL, NULL, 0 };
844 static SFTKObjectFreeList tokenObjectList = { NULL, NULL, 0 };
845
846 SFTKObject *
847 sftk_GetObjectFromList(PRBool *hasLocks, PRBool optimizeSpace,
848 SFTKObjectFreeList *list, unsigned int hashSize, PRBool isSessionObject)
849 {
850 SFTKObject *object;
851 int size = 0;
852
853 if (!optimizeSpace) {
854 PZ_Lock(list->lock);
855 object = list->head;
856 if (object) {
857 list->head = object->next;
858 list->count--;
859 }
860 PZ_Unlock(list->lock);
861 if (object) {
862 object->next = object->prev = NULL;
863 *hasLocks = PR_TRUE;
864 return object;
865 }
866 }
867 size = isSessionObject ? sizeof(SFTKSessionObject)
868 + hashSize *sizeof(SFTKAttribute *) : sizeof(SFTKTokenObject);
869
870 object = (SFTKObject*)PORT_ZAlloc(size);
871 if (isSessionObject && object) {
872 ((SFTKSessionObject *)object)->hashSize = hashSize;
873 }
874 *hasLocks = PR_FALSE;
875 return object;
876 }
877
878 static void
879 sftk_PutObjectToList(SFTKObject *object, SFTKObjectFreeList *list,
880 PRBool isSessionObject) {
881
882 /* the code below is equivalent to :
883 * optimizeSpace = isSessionObject ? object->optimizeSpace : PR_FALSE;
884 * just faster.
885 */
886 PRBool optimizeSpace = isSessionObject &&
887 ((SFTKSessionObject *)object)->optimizeSpace;
888 if (object->refLock && !optimizeSpace
889 && (list->count < MAX_OBJECT_LIST_SIZE)) {
890 PZ_Lock(list->lock);
891 object->next = list->head;
892 list->head = object;
893 list->count++;
894 PZ_Unlock(list->lock);
895 return;
896 }
897 if (isSessionObject) {
898 SFTKSessionObject *so = (SFTKSessionObject *)object;
899 PZ_DestroyLock(so->attributeLock);
900 so->attributeLock = NULL;
901 }
902 if (object->refLock) {
903 PZ_DestroyLock(object->refLock);
904 object->refLock = NULL;
905 }
906 PORT_Free(object);
907 }
908
909 static SFTKObject *
910 sftk_freeObjectData(SFTKObject *object) {
911 SFTKObject *next = object->next;
912
913 PORT_Free(object);
914 return next;
915 }
916
917 static void
918 sftk_InitFreeList(SFTKObjectFreeList *list)
919 {
920 list->lock = PZ_NewLock(nssILockObject);
921 }
922
923 void sftk_InitFreeLists(void)
924 {
925 sftk_InitFreeList(&sessionObjectList);
926 sftk_InitFreeList(&tokenObjectList);
927 }
928
929 static void
930 sftk_CleanupFreeList(SFTKObjectFreeList *list, PRBool isSessionList)
931 {
932 SFTKObject *object;
933
934 if (!list->lock) {
935 return;
936 }
937 SKIP_AFTER_FORK(PZ_Lock(list->lock));
938 for (object= list->head; object != NULL;
939 object = sftk_freeObjectData(object)) {
940 PZ_DestroyLock(object->refLock);
941 if (isSessionList) {
942 PZ_DestroyLock(((SFTKSessionObject *)object)->attributeLock);
943 }
944 }
945 list->count = 0;
946 list->head = NULL;
947 SKIP_AFTER_FORK(PZ_Unlock(list->lock));
948 SKIP_AFTER_FORK(PZ_DestroyLock(list->lock));
949 list->lock = NULL;
950 }
951
952 void
953 sftk_CleanupFreeLists(void)
954 {
955 sftk_CleanupFreeList(&sessionObjectList, PR_TRUE);
956 sftk_CleanupFreeList(&tokenObjectList, PR_FALSE);
957 }
958
959
960 /*
961 * Create a new object
962 */
963 SFTKObject *
964 sftk_NewObject(SFTKSlot *slot)
965 {
966 SFTKObject *object;
967 SFTKSessionObject *sessObject;
968 PRBool hasLocks = PR_FALSE;
969 unsigned int i;
970 unsigned int hashSize = 0;
971
972 hashSize = (slot->optimizeSpace) ? SPACE_ATTRIBUTE_HASH_SIZE :
973 TIME_ATTRIBUTE_HASH_SIZE;
974
975 object = sftk_GetObjectFromList(&hasLocks, slot->optimizeSpace,
976 &sessionObjectList, hashSize, PR_TRUE);
977 if (object == NULL) {
978 return NULL;
979 }
980 sessObject = (SFTKSessionObject *)object;
981 sessObject->nextAttr = 0;
982
983 for (i=0; i < MAX_OBJS_ATTRS; i++) {
984 sessObject->attrList[i].attrib.pValue = NULL;
985 sessObject->attrList[i].freeData = PR_FALSE;
986 }
987 sessObject->optimizeSpace = slot->optimizeSpace;
988
989 object->handle = 0;
990 object->next = object->prev = NULL;
991 object->slot = slot;
992
993 object->refCount = 1;
994 sessObject->sessionList.next = NULL;
995 sessObject->sessionList.prev = NULL;
996 sessObject->sessionList.parent = object;
997 sessObject->session = NULL;
998 sessObject->wasDerived = PR_FALSE;
999 if (!hasLocks) object->refLock = PZ_NewLock(nssILockRefLock);
1000 if (object->refLock == NULL) {
1001 PORT_Free(object);
1002 return NULL;
1003 }
1004 if (!hasLocks) sessObject->attributeLock = PZ_NewLock(nssILockAttribute);
1005 if (sessObject->attributeLock == NULL) {
1006 PZ_DestroyLock(object->refLock);
1007 PORT_Free(object);
1008 return NULL;
1009 }
1010 for (i=0; i < sessObject->hashSize; i++) {
1011 sessObject->head[i] = NULL;
1012 }
1013 object->objectInfo = NULL;
1014 object->infoFree = NULL;
1015 return object;
1016 }
1017
1018 static CK_RV
1019 sftk_DestroySessionObjectData(SFTKSessionObject *so)
1020 {
1021 int i;
1022
1023 for (i=0; i < MAX_OBJS_ATTRS; i++) {
1024 unsigned char *value = so->attrList[i].attrib.pValue;
1025 if (value) {
1026 PORT_Memset(value,0,so->attrList[i].attrib.ulValueLen);
1027 if (so->attrList[i].freeData) {
1028 PORT_Free(value);
1029 }
1030 so->attrList[i].attrib.pValue = NULL;
1031 so->attrList[i].freeData = PR_FALSE;
1032 }
1033 }
1034 /* PZ_DestroyLock(so->attributeLock);*/
1035 return CKR_OK;
1036 }
1037
1038 /*
1039 * free all the data associated with an object. Object reference count must
1040 * be 'zero'.
1041 */
1042 static CK_RV
1043 sftk_DestroyObject(SFTKObject *object)
1044 {
1045 CK_RV crv = CKR_OK;
1046 SFTKSessionObject *so = sftk_narrowToSessionObject(object);
1047 SFTKTokenObject *to = sftk_narrowToTokenObject(object);
1048
1049 PORT_Assert(object->refCount == 0);
1050
1051 /* delete the database value */
1052 if (to) {
1053 if (to->dbKey.data) {
1054 PORT_Free(to->dbKey.data);
1055 to->dbKey.data = NULL;
1056 }
1057 }
1058 if (so) {
1059 sftk_DestroySessionObjectData(so);
1060 }
1061 if (object->objectInfo) {
1062 (*object->infoFree)(object->objectInfo);
1063 object->objectInfo = NULL;
1064 object->infoFree = NULL;
1065 }
1066 if (so) {
1067 sftk_PutObjectToList(object,&sessionObjectList,PR_TRUE);
1068 } else {
1069 sftk_PutObjectToList(object,&tokenObjectList,PR_FALSE);
1070 }
1071 return crv;
1072 }
1073
1074 void
1075 sftk_ReferenceObject(SFTKObject *object)
1076 {
1077 PZ_Lock(object->refLock);
1078 object->refCount++;
1079 PZ_Unlock(object->refLock);
1080 }
1081
1082 static SFTKObject *
1083 sftk_ObjectFromHandleOnSlot(CK_OBJECT_HANDLE handle, SFTKSlot *slot)
1084 {
1085 SFTKObject *object;
1086 PRUint32 index = sftk_hash(handle, slot->sessObjHashSize);
1087
1088 if (sftk_isToken(handle)) {
1089 return sftk_NewTokenObject(slot, NULL, handle);
1090 }
1091
1092 PZ_Lock(slot->objectLock);
1093 sftkqueue_find2(object, handle, index, slot->sessObjHashTable);
1094 if (object) {
1095 sftk_ReferenceObject(object);
1096 }
1097 PZ_Unlock(slot->objectLock);
1098
1099 return(object);
1100 }
1101 /*
1102 * look up and object structure from a handle. OBJECT_Handles only make
1103 * sense in terms of a given session. make a reference to that object
1104 * structure returned.
1105 */
1106 SFTKObject *
1107 sftk_ObjectFromHandle(CK_OBJECT_HANDLE handle, SFTKSession *session)
1108 {
1109 SFTKSlot *slot = sftk_SlotFromSession(session);
1110
1111 return sftk_ObjectFromHandleOnSlot(handle,slot);
1112 }
1113
1114
1115 /*
1116 * release a reference to an object handle
1117 */
1118 SFTKFreeStatus
1119 sftk_FreeObject(SFTKObject *object)
1120 {
1121 PRBool destroy = PR_FALSE;
1122 CK_RV crv;
1123
1124 PZ_Lock(object->refLock);
1125 if (object->refCount == 1) destroy = PR_TRUE;
1126 object->refCount--;
1127 PZ_Unlock(object->refLock);
1128
1129 if (destroy) {
1130 crv = sftk_DestroyObject(object);
1131 if (crv != CKR_OK) {
1132 return SFTK_DestroyFailure;
1133 }
1134 return SFTK_Destroyed;
1135 }
1136 return SFTK_Busy;
1137 }
1138
1139 /*
1140 * add an object to a slot and session queue. These two functions
1141 * adopt the object.
1142 */
1143 void
1144 sftk_AddSlotObject(SFTKSlot *slot, SFTKObject *object)
1145 {
1146 PRUint32 index = sftk_hash(object->handle, slot->sessObjHashSize);
1147 sftkqueue_init_element(object);
1148 PZ_Lock(slot->objectLock);
1149 sftkqueue_add2(object, object->handle, index, slot->sessObjHashTable);
1150 PZ_Unlock(slot->objectLock);
1151 }
1152
1153 void
1154 sftk_AddObject(SFTKSession *session, SFTKObject *object)
1155 {
1156 SFTKSlot *slot = sftk_SlotFromSession(session);
1157 SFTKSessionObject *so = sftk_narrowToSessionObject(object);
1158
1159 if (so) {
1160 PZ_Lock(session->objectLock);
1161 sftkqueue_add(&so->sessionList,0,session->objects,0);
1162 so->session = session;
1163 PZ_Unlock(session->objectLock);
1164 }
1165 sftk_AddSlotObject(slot,object);
1166 sftk_ReferenceObject(object);
1167 }
1168
1169 /*
1170 * delete an object from a slot and session queue
1171 */
1172 CK_RV
1173 sftk_DeleteObject(SFTKSession *session, SFTKObject *object)
1174 {
1175 SFTKSlot *slot = sftk_SlotFromSession(session);
1176 SFTKSessionObject *so = sftk_narrowToSessionObject(object);
1177 SFTKTokenObject *to = sftk_narrowToTokenObject(object);
1178 CK_RV crv = CKR_OK;
1179 PRUint32 index = sftk_hash(object->handle, slot->sessObjHashSize);
1180
1181 /* Handle Token case */
1182 if (so && so->session) {
1183 SFTKSession *session = so->session;
1184 PZ_Lock(session->objectLock);
1185 sftkqueue_delete(&so->sessionList,0,session->objects,0);
1186 PZ_Unlock(session->objectLock);
1187 PZ_Lock(slot->objectLock);
1188 sftkqueue_delete2(object, object->handle, index, slot->sessObjHashTable);
1189 PZ_Unlock(slot->objectLock);
1190 sftkqueue_clear_deleted_element(object);
1191 sftk_FreeObject(object); /* free the reference owned by the queue */
1192 } else {
1193 SFTKDBHandle *handle = sftk_getDBForTokenObject(slot, object->handle);
1194
1195 PORT_Assert(to);
1196 crv = sftkdb_DestroyObject(handle, object->handle);
1197 sftk_freeDB(handle);
1198 }
1199 return crv;
1200 }
1201
1202 /*
1203 * Token objects don't explicitly store their attributes, so we need to know
1204 * what attributes make up a particular token object before we can copy it.
1205 * below are the tables by object type.
1206 */
1207 static const CK_ATTRIBUTE_TYPE commonAttrs[] = {
1208 CKA_CLASS, CKA_TOKEN, CKA_PRIVATE, CKA_LABEL, CKA_MODIFIABLE
1209 };
1210 static const CK_ULONG commonAttrsCount =
1211 sizeof(commonAttrs)/sizeof(commonAttrs[0]);
1212
1213 static const CK_ATTRIBUTE_TYPE commonKeyAttrs[] = {
1214 CKA_ID, CKA_START_DATE, CKA_END_DATE, CKA_DERIVE, CKA_LOCAL, CKA_KEY_TYPE
1215 };
1216 static const CK_ULONG commonKeyAttrsCount =
1217 sizeof(commonKeyAttrs)/sizeof(commonKeyAttrs[0]);
1218
1219 static const CK_ATTRIBUTE_TYPE secretKeyAttrs[] = {
1220 CKA_SENSITIVE, CKA_EXTRACTABLE, CKA_ENCRYPT, CKA_DECRYPT, CKA_SIGN,
1221 CKA_VERIFY, CKA_WRAP, CKA_UNWRAP, CKA_VALUE
1222 };
1223 static const CK_ULONG secretKeyAttrsCount =
1224 sizeof(secretKeyAttrs)/sizeof(secretKeyAttrs[0]);
1225
1226 static const CK_ATTRIBUTE_TYPE commonPubKeyAttrs[] = {
1227 CKA_ENCRYPT, CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_WRAP, CKA_SUBJECT
1228 };
1229 static const CK_ULONG commonPubKeyAttrsCount =
1230 sizeof(commonPubKeyAttrs)/sizeof(commonPubKeyAttrs[0]);
1231
1232 static const CK_ATTRIBUTE_TYPE rsaPubKeyAttrs[] = {
1233 CKA_MODULUS, CKA_PUBLIC_EXPONENT
1234 };
1235 static const CK_ULONG rsaPubKeyAttrsCount =
1236 sizeof(rsaPubKeyAttrs)/sizeof(rsaPubKeyAttrs[0]);
1237
1238 static const CK_ATTRIBUTE_TYPE dsaPubKeyAttrs[] = {
1239 CKA_SUBPRIME, CKA_PRIME, CKA_BASE, CKA_VALUE
1240 };
1241 static const CK_ULONG dsaPubKeyAttrsCount =
1242 sizeof(dsaPubKeyAttrs)/sizeof(dsaPubKeyAttrs[0]);
1243
1244 static const CK_ATTRIBUTE_TYPE dhPubKeyAttrs[] = {
1245 CKA_PRIME, CKA_BASE, CKA_VALUE
1246 };
1247 static const CK_ULONG dhPubKeyAttrsCount =
1248 sizeof(dhPubKeyAttrs)/sizeof(dhPubKeyAttrs[0]);
1249 #ifndef NSS_DISABLE_ECC
1250 static const CK_ATTRIBUTE_TYPE ecPubKeyAttrs[] = {
1251 CKA_EC_PARAMS, CKA_EC_POINT
1252 };
1253 static const CK_ULONG ecPubKeyAttrsCount =
1254 sizeof(ecPubKeyAttrs)/sizeof(ecPubKeyAttrs[0]);
1255 #endif
1256
1257 static const CK_ATTRIBUTE_TYPE commonPrivKeyAttrs[] = {
1258 CKA_DECRYPT, CKA_SIGN, CKA_SIGN_RECOVER, CKA_UNWRAP, CKA_SUBJECT,
1259 CKA_SENSITIVE, CKA_EXTRACTABLE, CKA_NETSCAPE_DB
1260 };
1261 static const CK_ULONG commonPrivKeyAttrsCount =
1262 sizeof(commonPrivKeyAttrs)/sizeof(commonPrivKeyAttrs[0]);
1263
1264 static const CK_ATTRIBUTE_TYPE rsaPrivKeyAttrs[] = {
1265 CKA_MODULUS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT,
1266 CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT
1267 };
1268 static const CK_ULONG rsaPrivKeyAttrsCount =
1269 sizeof(rsaPrivKeyAttrs)/sizeof(rsaPrivKeyAttrs[0]);
1270
1271 static const CK_ATTRIBUTE_TYPE dsaPrivKeyAttrs[] = {
1272 CKA_SUBPRIME, CKA_PRIME, CKA_BASE, CKA_VALUE
1273 };
1274 static const CK_ULONG dsaPrivKeyAttrsCount =
1275 sizeof(dsaPrivKeyAttrs)/sizeof(dsaPrivKeyAttrs[0]);
1276
1277 static const CK_ATTRIBUTE_TYPE dhPrivKeyAttrs[] = {
1278 CKA_PRIME, CKA_BASE, CKA_VALUE
1279 };
1280 static const CK_ULONG dhPrivKeyAttrsCount =
1281 sizeof(dhPrivKeyAttrs)/sizeof(dhPrivKeyAttrs[0]);
1282 #ifndef NSS_DISABLE_ECC
1283 static const CK_ATTRIBUTE_TYPE ecPrivKeyAttrs[] = {
1284 CKA_EC_PARAMS, CKA_VALUE
1285 };
1286 static const CK_ULONG ecPrivKeyAttrsCount =
1287 sizeof(ecPrivKeyAttrs)/sizeof(ecPrivKeyAttrs[0]);
1288 #endif
1289
1290 static const CK_ATTRIBUTE_TYPE certAttrs[] = {
1291 CKA_CERTIFICATE_TYPE, CKA_VALUE, CKA_SUBJECT, CKA_ISSUER, CKA_SERIAL_NUMBER
1292 };
1293 static const CK_ULONG certAttrsCount =
1294 sizeof(certAttrs)/sizeof(certAttrs[0]);
1295
1296 static const CK_ATTRIBUTE_TYPE trustAttrs[] = {
1297 CKA_ISSUER, CKA_SERIAL_NUMBER, CKA_CERT_SHA1_HASH, CKA_CERT_MD5_HASH,
1298 CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH, CKA_TRUST_EMAIL_PROTECTION,
1299 CKA_TRUST_CODE_SIGNING, CKA_TRUST_STEP_UP_APPROVED
1300 };
1301 static const CK_ULONG trustAttrsCount =
1302 sizeof(trustAttrs)/sizeof(trustAttrs[0]);
1303
1304 static const CK_ATTRIBUTE_TYPE smimeAttrs[] = {
1305 CKA_SUBJECT, CKA_NETSCAPE_EMAIL, CKA_NETSCAPE_SMIME_TIMESTAMP, CKA_VALUE
1306 };
1307 static const CK_ULONG smimeAttrsCount =
1308 sizeof(smimeAttrs)/sizeof(smimeAttrs[0]);
1309
1310 static const CK_ATTRIBUTE_TYPE crlAttrs[] = {
1311 CKA_SUBJECT, CKA_VALUE, CKA_NETSCAPE_URL, CKA_NETSCAPE_KRL
1312 };
1313 static const CK_ULONG crlAttrsCount =
1314 sizeof(crlAttrs)/sizeof(crlAttrs[0]);
1315
1316 /* copy an object based on it's table */
1317 CK_RV
1318 stfk_CopyTokenAttributes(SFTKObject *destObject,SFTKTokenObject *src_to,
1319 const CK_ATTRIBUTE_TYPE *attrArray, CK_ULONG attrCount)
1320 {
1321 SFTKAttribute *attribute;
1322 SFTKAttribute *newAttribute;
1323 CK_RV crv = CKR_OK;
1324 unsigned int i;
1325
1326 for (i=0; i < attrCount; i++) {
1327 if (!sftk_hasAttribute(destObject,attrArray[i])) {
1328 attribute =sftk_FindAttribute(&src_to->obj, attrArray[i]);
1329 if (!attribute) {
1330 continue; /* return CKR_ATTRIBUTE_VALUE_INVALID; */
1331 }
1332 /* we need to copy the attribute since each attribute
1333 * only has one set of link list pointers */
1334 newAttribute = sftk_NewAttribute( destObject,
1335 sftk_attr_expand(&attribute->attrib));
1336 sftk_FreeAttribute(attribute); /* free the old attribute */
1337 if (!newAttribute) {
1338 return CKR_HOST_MEMORY;
1339 }
1340 sftk_AddAttribute(destObject,newAttribute);
1341 }
1342 }
1343 return crv;
1344 }
1345
1346 CK_RV
1347 stfk_CopyTokenPrivateKey(SFTKObject *destObject,SFTKTokenObject *src_to)
1348 {
1349 CK_RV crv;
1350 CK_KEY_TYPE key_type;
1351 SFTKAttribute *attribute;
1352
1353 /* copy the common attributes for all keys first */
1354 crv = stfk_CopyTokenAttributes(destObject, src_to, commonKeyAttrs,
1355 commonKeyAttrsCount);
1356 if (crv != CKR_OK) {
1357 goto fail;
1358 }
1359 /* copy the common attributes for all private keys next */
1360 crv = stfk_CopyTokenAttributes(destObject, src_to, commonPrivKeyAttrs,
1361 commonPrivKeyAttrsCount);
1362 if (crv != CKR_OK) {
1363 goto fail;
1364 }
1365 attribute =sftk_FindAttribute(&src_to->obj, CKA_KEY_TYPE);
1366 PORT_Assert(attribute); /* if it wasn't here, ww should have failed
1367 * copying the common attributes */
1368 if (!attribute) {
1369 /* OK, so CKR_ATTRIBUTE_VALUE_INVALID is the immediate error, but
1370 * the fact is, the only reason we couldn't get the attribute would
1371 * be a memory error or database error (an error in the 'device').
1372 * if we have a database error code, we could return it here */
1373 crv = CKR_DEVICE_ERROR;
1374 goto fail;
1375 }
1376 key_type = *(CK_KEY_TYPE *)attribute->attrib.pValue;
1377 sftk_FreeAttribute(attribute);
1378
1379 /* finally copy the attributes for various private key types */
1380 switch (key_type) {
1381 case CKK_RSA:
1382 crv = stfk_CopyTokenAttributes(destObject, src_to, rsaPrivKeyAttrs,
1383 rsaPrivKeyAttrsCount);
1384 break;
1385 case CKK_DSA:
1386 crv = stfk_CopyTokenAttributes(destObject, src_to, dsaPrivKeyAttrs,
1387 dsaPrivKeyAttrsCount);
1388 break;
1389 case CKK_DH:
1390 crv = stfk_CopyTokenAttributes(destObject, src_to, dhPrivKeyAttrs,
1391 dhPrivKeyAttrsCount);
1392 break;
1393 #ifndef NSS_DISABLE_ECC
1394 case CKK_EC:
1395 crv = stfk_CopyTokenAttributes(destObject, src_to, ecPrivKeyAttrs,
1396 ecPrivKeyAttrsCount);
1397 break;
1398 #endif
1399 default:
1400 crv = CKR_DEVICE_ERROR; /* shouldn't happen unless we store more types
1401 * of token keys into our database. */
1402 }
1403 fail:
1404 return crv;
1405 }
1406
1407 CK_RV
1408 stfk_CopyTokenPublicKey(SFTKObject *destObject,SFTKTokenObject *src_to)
1409 {
1410 CK_RV crv;
1411 CK_KEY_TYPE key_type;
1412 SFTKAttribute *attribute;
1413
1414 /* copy the common attributes for all keys first */
1415 crv = stfk_CopyTokenAttributes(destObject, src_to, commonKeyAttrs,
1416 commonKeyAttrsCount);
1417 if (crv != CKR_OK) {
1418 goto fail;
1419 }
1420
1421 /* copy the common attributes for all public keys next */
1422 crv = stfk_CopyTokenAttributes(destObject, src_to, commonPubKeyAttrs,
1423 commonPubKeyAttrsCount);
1424 if (crv != CKR_OK) {
1425 goto fail;
1426 }
1427 attribute =sftk_FindAttribute(&src_to->obj, CKA_KEY_TYPE);
1428 PORT_Assert(attribute); /* if it wasn't here, ww should have failed
1429 * copying the common attributes */
1430 if (!attribute) {
1431 /* OK, so CKR_ATTRIBUTE_VALUE_INVALID is the immediate error, but
1432 * the fact is, the only reason we couldn't get the attribute would
1433 * be a memory error or database error (an error in the 'device').
1434 * if we have a database error code, we could return it here */
1435 crv = CKR_DEVICE_ERROR;
1436 goto fail;
1437 }
1438 key_type = *(CK_KEY_TYPE *)attribute->attrib.pValue;
1439 sftk_FreeAttribute(attribute);
1440
1441 /* finally copy the attributes for various public key types */
1442 switch (key_type) {
1443 case CKK_RSA:
1444 crv = stfk_CopyTokenAttributes(destObject, src_to, rsaPubKeyAttrs,
1445 rsaPubKeyAttrsCount);
1446 break;
1447 case CKK_DSA:
1448 crv = stfk_CopyTokenAttributes(destObject, src_to, dsaPubKeyAttrs,
1449 dsaPubKeyAttrsCount);
1450 break;
1451 case CKK_DH:
1452 crv = stfk_CopyTokenAttributes(destObject, src_to, dhPubKeyAttrs,
1453 dhPubKeyAttrsCount);
1454 break;
1455 #ifndef NSS_DISABLE_ECC
1456 case CKK_EC:
1457 crv = stfk_CopyTokenAttributes(destObject, src_to, ecPubKeyAttrs,
1458 ecPubKeyAttrsCount);
1459 break;
1460 #endif
1461 default:
1462 crv = CKR_DEVICE_ERROR; /* shouldn't happen unless we store more types
1463 * of token keys into our database. */
1464 }
1465 fail:
1466 return crv;
1467 }
1468 CK_RV
1469 stfk_CopyTokenSecretKey(SFTKObject *destObject,SFTKTokenObject *src_to)
1470 {
1471 CK_RV crv;
1472 crv = stfk_CopyTokenAttributes(destObject, src_to, commonKeyAttrs,
1473 commonKeyAttrsCount);
1474 if (crv != CKR_OK) {
1475 goto fail;
1476 }
1477 crv = stfk_CopyTokenAttributes(destObject, src_to, secretKeyAttrs,
1478 secretKeyAttrsCount);
1479 fail:
1480 return crv;
1481 }
1482
1483 /*
1484 * Copy a token object. We need to explicitly copy the relevant
1485 * attributes since token objects don't store those attributes in
1486 * the token itself.
1487 */
1488 CK_RV
1489 sftk_CopyTokenObject(SFTKObject *destObject,SFTKObject *srcObject)
1490 {
1491 SFTKTokenObject *src_to = sftk_narrowToTokenObject(srcObject);
1492 CK_RV crv;
1493
1494 PORT_Assert(src_to);
1495 if (src_to == NULL) {
1496 return CKR_DEVICE_ERROR; /* internal state inconsistant */
1497 }
1498
1499 crv = stfk_CopyTokenAttributes(destObject, src_to, commonAttrs,
1500 commonAttrsCount);
1501 if (crv != CKR_OK) {
1502 goto fail;
1503 }
1504 switch (src_to->obj.objclass) {
1505 case CKO_CERTIFICATE:
1506 crv = stfk_CopyTokenAttributes(destObject, src_to, certAttrs,
1507 certAttrsCount);
1508 break;
1509 case CKO_NETSCAPE_TRUST:
1510 crv = stfk_CopyTokenAttributes(destObject, src_to, trustAttrs,
1511 trustAttrsCount);
1512 break;
1513 case CKO_NETSCAPE_SMIME:
1514 crv = stfk_CopyTokenAttributes(destObject, src_to, smimeAttrs,
1515 smimeAttrsCount);
1516 break;
1517 case CKO_NETSCAPE_CRL:
1518 crv = stfk_CopyTokenAttributes(destObject, src_to, crlAttrs,
1519 crlAttrsCount);
1520 break;
1521 case CKO_PRIVATE_KEY:
1522 crv = stfk_CopyTokenPrivateKey(destObject,src_to);
1523 break;
1524 case CKO_PUBLIC_KEY:
1525 crv = stfk_CopyTokenPublicKey(destObject,src_to);
1526 break;
1527 case CKO_SECRET_KEY:
1528 crv = stfk_CopyTokenSecretKey(destObject,src_to);
1529 break;
1530 default:
1531 crv = CKR_DEVICE_ERROR; /* shouldn't happen unless we store more types
1532 * of token keys into our database. */
1533 }
1534 fail:
1535 return crv;
1536 }
1537
1538 /*
1539 * copy the attributes from one object to another. Don't overwrite existing
1540 * attributes. NOTE: This is a pretty expensive operation since it
1541 * grabs the attribute locks for the src object for a *long* time.
1542 */
1543 CK_RV
1544 sftk_CopyObject(SFTKObject *destObject,SFTKObject *srcObject)
1545 {
1546 SFTKAttribute *attribute;
1547 SFTKSessionObject *src_so = sftk_narrowToSessionObject(srcObject);
1548 unsigned int i;
1549
1550 if (src_so == NULL) {
1551 return sftk_CopyTokenObject(destObject,srcObject);
1552 }
1553
1554 PZ_Lock(src_so->attributeLock);
1555 for(i=0; i < src_so->hashSize; i++) {
1556 attribute = src_so->head[i];
1557 do {
1558 if (attribute) {
1559 if (!sftk_hasAttribute(destObject,attribute->handle)) {
1560 /* we need to copy the attribute since each attribute
1561 * only has one set of link list pointers */
1562 SFTKAttribute *newAttribute = sftk_NewAttribute(
1563 destObject,sftk_attr_expand(&attribute->attrib));
1564 if (newAttribute == NULL) {
1565 PZ_Unlock(src_so->attributeLock);
1566 return CKR_HOST_MEMORY;
1567 }
1568 sftk_AddAttribute(destObject,newAttribute);
1569 }
1570 attribute=attribute->next;
1571 }
1572 } while (attribute != NULL);
1573 }
1574 PZ_Unlock(src_so->attributeLock);
1575
1576 return CKR_OK;
1577 }
1578
1579 /*
1580 * ******************** Search Utilities *******************************
1581 */
1582
1583 /* add an object to a search list */
1584 CK_RV
1585 AddToList(SFTKObjectListElement **list,SFTKObject *object)
1586 {
1587 SFTKObjectListElement *newElem =
1588 (SFTKObjectListElement *)PORT_Alloc(sizeof(SFTKObjectListElement));
1589
1590 if (newElem == NULL) return CKR_HOST_MEMORY;
1591
1592 newElem->next = *list;
1593 newElem->object = object;
1594 sftk_ReferenceObject(object);
1595
1596 *list = newElem;
1597 return CKR_OK;
1598 }
1599
1600
1601 /* return true if the object matches the template */
1602 PRBool
1603 sftk_objectMatch(SFTKObject *object,CK_ATTRIBUTE_PTR theTemplate,int count)
1604 {
1605 int i;
1606
1607 for (i=0; i < count; i++) {
1608 SFTKAttribute *attribute = sftk_FindAttribute(object,theTemplate[i].type);
1609 if (attribute == NULL) {
1610 return PR_FALSE;
1611 }
1612 if (attribute->attrib.ulValueLen == theTemplate[i].ulValueLen) {
1613 if (PORT_Memcmp(attribute->attrib.pValue,theTemplate[i].pValue,
1614 theTemplate[i].ulValueLen) == 0) {
1615 sftk_FreeAttribute(attribute);
1616 continue;
1617 }
1618 }
1619 sftk_FreeAttribute(attribute);
1620 return PR_FALSE;
1621 }
1622 return PR_TRUE;
1623 }
1624
1625 /* search through all the objects in the queue and return the template matches
1626 * in the object list.
1627 */
1628 CK_RV
1629 sftk_searchObjectList(SFTKSearchResults *search,SFTKObject **head,
1630 unsigned int size, PZLock *lock, CK_ATTRIBUTE_PTR theTemplate,
1631 int count, PRBool isLoggedIn)
1632 {
1633 unsigned int i;
1634 SFTKObject *object;
1635 CK_RV crv = CKR_OK;
1636
1637 for(i=0; i < size; i++) {
1638 /* We need to hold the lock to copy a consistant version of
1639 * the linked list. */
1640 PZ_Lock(lock);
1641 for (object = head[i]; object != NULL; object= object->next) {
1642 if (sftk_objectMatch(object,theTemplate,count)) {
1643 /* don't return objects that aren't yet visible */
1644 if ((!isLoggedIn) && sftk_isTrue(object,CKA_PRIVATE)) continue;
1645 sftk_addHandle(search,object->handle);
1646 }
1647 }
1648 PZ_Unlock(lock);
1649 }
1650 return crv;
1651 }
1652
1653 /*
1654 * free a single list element. Return the Next object in the list.
1655 */
1656 SFTKObjectListElement *
1657 sftk_FreeObjectListElement(SFTKObjectListElement *objectList)
1658 {
1659 SFTKObjectListElement *ol = objectList->next;
1660
1661 sftk_FreeObject(objectList->object);
1662 PORT_Free(objectList);
1663 return ol;
1664 }
1665
1666 /* free an entire object list */
1667 void
1668 sftk_FreeObjectList(SFTKObjectListElement *objectList)
1669 {
1670 SFTKObjectListElement *ol;
1671
1672 for (ol= objectList; ol != NULL; ol = sftk_FreeObjectListElement(ol)) {}
1673 }
1674
1675 /*
1676 * free a search structure
1677 */
1678 void
1679 sftk_FreeSearch(SFTKSearchResults *search)
1680 {
1681 if (search->handles) {
1682 PORT_Free(search->handles);
1683 }
1684 PORT_Free(search);
1685 }
1686
1687 /*
1688 * ******************** Session Utilities *******************************
1689 */
1690
1691 /* update the sessions state based in it's flags and wether or not it's
1692 * logged in */
1693 void
1694 sftk_update_state(SFTKSlot *slot,SFTKSession *session)
1695 {
1696 if (slot->isLoggedIn) {
1697 if (slot->ssoLoggedIn) {
1698 session->info.state = CKS_RW_SO_FUNCTIONS;
1699 } else if (session->info.flags & CKF_RW_SESSION) {
1700 session->info.state = CKS_RW_USER_FUNCTIONS;
1701 } else {
1702 session->info.state = CKS_RO_USER_FUNCTIONS;
1703 }
1704 } else {
1705 if (session->info.flags & CKF_RW_SESSION) {
1706 session->info.state = CKS_RW_PUBLIC_SESSION;
1707 } else {
1708 session->info.state = CKS_RO_PUBLIC_SESSION;
1709 }
1710 }
1711 }
1712
1713 /* update the state of all the sessions on a slot */
1714 void
1715 sftk_update_all_states(SFTKSlot *slot)
1716 {
1717 unsigned int i;
1718 SFTKSession *session;
1719
1720 for (i=0; i < slot->sessHashSize; i++) {
1721 PZLock *lock = SFTK_SESSION_LOCK(slot,i);
1722 PZ_Lock(lock);
1723 for (session = slot->head[i]; session; session = session->next) {
1724 sftk_update_state(slot,session);
1725 }
1726 PZ_Unlock(lock);
1727 }
1728 }
1729
1730 /*
1731 * context are cipher and digest contexts that are associated with a session
1732 */
1733 void
1734 sftk_FreeContext(SFTKSessionContext *context)
1735 {
1736 if (context->cipherInfo) {
1737 (*context->destroy)(context->cipherInfo,PR_TRUE);
1738 }
1739 if (context->hashInfo) {
1740 (*context->hashdestroy)(context->hashInfo,PR_TRUE);
1741 }
1742 if (context->key) {
1743 sftk_FreeObject(context->key);
1744 context->key = NULL;
1745 }
1746 PORT_Free(context);
1747 }
1748
1749 /*
1750 * create a new nession. NOTE: The session handle is not set, and the
1751 * session is not added to the slot's session queue.
1752 */
1753 SFTKSession *
1754 sftk_NewSession(CK_SLOT_ID slotID, CK_NOTIFY notify, CK_VOID_PTR pApplication,
1755 CK_FLAGS flags)
1756 {
1757 SFTKSession *session;
1758 SFTKSlot *slot = sftk_SlotFromID(slotID, PR_FALSE);
1759
1760 if (slot == NULL) return NULL;
1761
1762 session = (SFTKSession*)PORT_Alloc(sizeof(SFTKSession));
1763 if (session == NULL) return NULL;
1764
1765 session->next = session->prev = NULL;
1766 session->refCount = 1;
1767 session->enc_context = NULL;
1768 session->hash_context = NULL;
1769 session->sign_context = NULL;
1770 session->search = NULL;
1771 session->objectIDCount = 1;
1772 session->objectLock = PZ_NewLock(nssILockObject);
1773 if (session->objectLock == NULL) {
1774 PORT_Free(session);
1775 return NULL;
1776 }
1777 session->objects[0] = NULL;
1778
1779 session->slot = slot;
1780 session->notify = notify;
1781 session->appData = pApplication;
1782 session->info.flags = flags;
1783 session->info.slotID = slotID;
1784 session->info.ulDeviceError = 0;
1785 sftk_update_state(slot,session);
1786 return session;
1787 }
1788
1789
1790 /* free all the data associated with a session. */
1791 static void
1792 sftk_DestroySession(SFTKSession *session)
1793 {
1794 SFTKObjectList *op,*next;
1795 PORT_Assert(session->refCount == 0);
1796
1797 /* clean out the attributes */
1798 /* since no one is referencing us, it's safe to walk the chain
1799 * without a lock */
1800 for (op = session->objects[0]; op != NULL; op = next) {
1801 next = op->next;
1802 /* paranoia */
1803 op->next = op->prev = NULL;
1804 sftk_DeleteObject(session,op->parent);
1805 }
1806 PZ_DestroyLock(session->objectLock);
1807 if (session->enc_context) {
1808 sftk_FreeContext(session->enc_context);
1809 }
1810 if (session->hash_context) {
1811 sftk_FreeContext(session->hash_context);
1812 }
1813 if (session->sign_context) {
1814 sftk_FreeContext(session->sign_context);
1815 }
1816 if (session->search) {
1817 sftk_FreeSearch(session->search);
1818 }
1819 PORT_Free(session);
1820 }
1821
1822
1823 /*
1824 * look up a session structure from a session handle
1825 * generate a reference to it.
1826 */
1827 SFTKSession *
1828 sftk_SessionFromHandle(CK_SESSION_HANDLE handle)
1829 {
1830 SFTKSlot *slot = sftk_SlotFromSessionHandle(handle);
1831 SFTKSession *session;
1832 PZLock *lock;
1833
1834 if (!slot) return NULL;
1835 lock = SFTK_SESSION_LOCK(slot,handle);
1836
1837 PZ_Lock(lock);
1838 sftkqueue_find(session,handle,slot->head,slot->sessHashSize);
1839 if (session) session->refCount++;
1840 PZ_Unlock(lock);
1841
1842 return (session);
1843 }
1844
1845 /*
1846 * release a reference to a session handle
1847 */
1848 void
1849 sftk_FreeSession(SFTKSession *session)
1850 {
1851 PRBool destroy = PR_FALSE;
1852 SFTKSlot *slot = sftk_SlotFromSession(session);
1853 PZLock *lock = SFTK_SESSION_LOCK(slot,session->handle);
1854
1855 PZ_Lock(lock);
1856 if (session->refCount == 1) destroy = PR_TRUE;
1857 session->refCount--;
1858 PZ_Unlock(lock);
1859
1860 if (destroy) sftk_DestroySession(session);
1861 }
1862
1863 void
1864 sftk_addHandle(SFTKSearchResults *search, CK_OBJECT_HANDLE handle)
1865 {
1866 if (search->handles == NULL) {
1867 return;
1868 }
1869 if (search->size >= search->array_size) {
1870 search->array_size += NSC_SEARCH_BLOCK_SIZE;
1871 search->handles = (CK_OBJECT_HANDLE *) PORT_Realloc(search->handles,
1872 sizeof(CK_OBJECT_HANDLE)* search->array_size);
1873 if (search->handles == NULL) {
1874 return;
1875 }
1876 }
1877 search->handles[search->size] = handle;
1878 search->size++;
1879 }
1880
1881 static CK_RV
1882 handleToClass(SFTKSlot *slot, CK_OBJECT_HANDLE handle,
1883 CK_OBJECT_CLASS *objClass)
1884 {
1885 SFTKDBHandle *dbHandle = sftk_getDBForTokenObject(slot, handle);
1886 CK_ATTRIBUTE objClassTemplate;
1887 CK_RV crv;
1888
1889 *objClass = CKO_DATA;
1890 objClassTemplate.type = CKA_CLASS;
1891 objClassTemplate.pValue = objClass;
1892 objClassTemplate.ulValueLen = sizeof(*objClass);
1893 crv = sftkdb_GetAttributeValue(dbHandle, handle, &objClassTemplate, 1);
1894 sftk_freeDB(dbHandle);
1895 return crv;
1896 }
1897
1898 SFTKObject *
1899 sftk_NewTokenObject(SFTKSlot *slot, SECItem *dbKey, CK_OBJECT_HANDLE handle)
1900 {
1901 SFTKObject *object = NULL;
1902 SFTKTokenObject *tokObject = NULL;
1903 PRBool hasLocks = PR_FALSE;
1904 CK_RV crv;
1905
1906 object = sftk_GetObjectFromList(&hasLocks, PR_FALSE, &tokenObjectList, 0,
1907 PR_FALSE);
1908 if (object == NULL) {
1909 return NULL;
1910 }
1911 tokObject = (SFTKTokenObject *) object;
1912
1913 object->handle = handle;
1914 /* every object must have a class, if we can't get it, the object
1915 * doesn't exist */
1916 crv = handleToClass(slot, handle, &object->objclass);
1917 if (crv != CKR_OK) {
1918 goto loser;
1919 }
1920 object->slot = slot;
1921 object->objectInfo = NULL;
1922 object->infoFree = NULL;
1923 if (!hasLocks) {
1924 object->refLock = PZ_NewLock(nssILockRefLock);
1925 }
1926 if (object->refLock == NULL) {
1927 goto loser;
1928 }
1929 object->refCount = 1;
1930
1931 return object;
1932 loser:
1933 if (object) {
1934 (void) sftk_DestroyObject(object);
1935 }
1936 return NULL;
1937
1938 }
1939
1940 SFTKTokenObject *
1941 sftk_convertSessionToToken(SFTKObject *obj)
1942 {
1943 SECItem *key;
1944 SFTKSessionObject *so = (SFTKSessionObject *)obj;
1945 SFTKTokenObject *to = sftk_narrowToTokenObject(obj);
1946 SECStatus rv;
1947
1948 sftk_DestroySessionObjectData(so);
1949 PZ_DestroyLock(so->attributeLock);
1950 if (to == NULL) {
1951 return NULL;
1952 }
1953 sftk_tokenKeyLock(so->obj.slot);
1954 key = sftk_lookupTokenKeyByHandle(so->obj.slot,so->obj.handle);
1955 if (key == NULL) {
1956 sftk_tokenKeyUnlock(so->obj.slot);
1957 return NULL;
1958 }
1959 rv = SECITEM_CopyItem(NULL,&to->dbKey,key);
1960 sftk_tokenKeyUnlock(so->obj.slot);
1961 if (rv == SECFailure) {
1962 return NULL;
1963 }
1964
1965 return to;
1966 }
1967
1968 SFTKSessionObject *
1969 sftk_narrowToSessionObject(SFTKObject *obj)
1970 {
1971 return !sftk_isToken(obj->handle) ? (SFTKSessionObject *)obj : NULL;
1972 }
1973
1974 SFTKTokenObject *
1975 sftk_narrowToTokenObject(SFTKObject *obj)
1976 {
1977 return sftk_isToken(obj->handle) ? (SFTKTokenObject *)obj : NULL;
1978 }
1979
This site is hosted by Intevation GmbH (Datenschutzerklärung und Impressum | Privacy Policy and Imprint)