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 #include "secoid.h"
17 
18 #if !defined(NSS_FIPS_DISABLED) && defined(NSS_ENABLE_FIPS_INDICATORS)
19 /* this file should be supplied by the vendor and include all the
20  * algorithms which have Algorithm certs and have been reviewed by
21  * the lab. A blank file is included for the base so that FIPS mode
22  * will still be compiled and run, but FIPS indicators will always
23  * return PR_FALSE
24  */
25 #include "fips_algorithms.h"
26 #define NSS_HAS_FIPS_INDICATORS 1
27 #endif
28 
29 /*
30  * ******************** Error mapping *******************************
31  */
32 /*
33  * map all the SEC_ERROR_xxx error codes that may be returned by freebl
34  * functions to CKR_xxx.  return CKR_DEVICE_ERROR by default for backward
35  * compatibility.
36  */
37 CK_RV
sftk_MapCryptError(int error)38 sftk_MapCryptError(int error)
39 {
40     switch (error) {
41         case SEC_ERROR_INVALID_ARGS:
42         case SEC_ERROR_BAD_DATA: /* MP_RANGE gets mapped to this */
43             return CKR_ARGUMENTS_BAD;
44         case SEC_ERROR_INPUT_LEN:
45             return CKR_DATA_LEN_RANGE;
46         case SEC_ERROR_OUTPUT_LEN:
47             return CKR_BUFFER_TOO_SMALL;
48         case SEC_ERROR_LIBRARY_FAILURE:
49             return CKR_GENERAL_ERROR;
50         case SEC_ERROR_NO_MEMORY:
51             return CKR_HOST_MEMORY;
52         case SEC_ERROR_BAD_SIGNATURE:
53             return CKR_SIGNATURE_INVALID;
54         case SEC_ERROR_INVALID_KEY:
55             return CKR_KEY_SIZE_RANGE;
56         case SEC_ERROR_BAD_KEY:        /* an EC public key that fails validation */
57             return CKR_KEY_SIZE_RANGE; /* the closest error code */
58         case SEC_ERROR_UNSUPPORTED_EC_POINT_FORM:
59             return CKR_TEMPLATE_INCONSISTENT;
60         case SEC_ERROR_UNSUPPORTED_KEYALG:
61             return CKR_MECHANISM_INVALID;
62         case SEC_ERROR_UNSUPPORTED_ELLIPTIC_CURVE:
63             return CKR_DOMAIN_PARAMS_INVALID;
64         /* key pair generation failed after max number of attempts */
65         case SEC_ERROR_NEED_RANDOM:
66             return CKR_FUNCTION_FAILED;
67     }
68     return CKR_DEVICE_ERROR;
69 }
70 
71 /*
72  * functions which adjust the mapping based on different contexts
73  * (Decrypt or Verify).
74  */
75 
76 /* used by Decrypt and UnwrapKey (indirectly) and Decrypt message */
77 CK_RV
sftk_MapDecryptError(int error)78 sftk_MapDecryptError(int error)
79 {
80     switch (error) {
81         /* usually a padding error, or aead tag mismatch */
82         case SEC_ERROR_BAD_DATA:
83             return CKR_ENCRYPTED_DATA_INVALID;
84         default:
85             return sftk_MapCryptError(error);
86     }
87 }
88 
89 /*
90  * return CKR_SIGNATURE_INVALID instead of CKR_DEVICE_ERROR by default for
91  * backward compatibilty.
92  */
93 CK_RV
sftk_MapVerifyError(int error)94 sftk_MapVerifyError(int error)
95 {
96     CK_RV crv = sftk_MapCryptError(error);
97     if (crv == CKR_DEVICE_ERROR)
98         crv = CKR_SIGNATURE_INVALID;
99     return crv;
100 }
101 
102 /*
103  * ******************** Attribute Utilities *******************************
104  */
105 
106 /*
107  * create a new attribute with type, value, and length. Space is allocated
108  * to hold value.
109  */
110 static SFTKAttribute *
sftk_NewAttribute(SFTKObject * object,CK_ATTRIBUTE_TYPE type,const void * value,CK_ULONG len)111 sftk_NewAttribute(SFTKObject *object,
112                   CK_ATTRIBUTE_TYPE type, const void *value, CK_ULONG len)
113 {
114     SFTKAttribute *attribute;
115 
116     SFTKSessionObject *so = sftk_narrowToSessionObject(object);
117     int index;
118 
119     if (so == NULL) {
120         /* allocate new attribute in a buffer */
121         PORT_Assert(0);
122         return NULL;
123     }
124     /*
125      * We attempt to keep down contention on Malloc and Arena locks by
126      * limiting the number of these calls on high traversed paths. This
127      * is done for attributes by 'allocating' them from a pool already
128      * allocated by the parent object.
129      */
130     PZ_Lock(so->attributeLock);
131     index = so->nextAttr++;
132     PZ_Unlock(so->attributeLock);
133     PORT_Assert(index < MAX_OBJS_ATTRS);
134     if (index >= MAX_OBJS_ATTRS)
135         return NULL;
136 
137     attribute = &so->attrList[index];
138     attribute->attrib.type = type;
139     attribute->freeAttr = PR_FALSE;
140     attribute->freeData = PR_FALSE;
141     if (value) {
142         if (len <= ATTR_SPACE) {
143             attribute->attrib.pValue = attribute->space;
144         } else {
145             attribute->attrib.pValue = PORT_Alloc(len);
146             attribute->freeData = PR_TRUE;
147         }
148         if (attribute->attrib.pValue == NULL) {
149             return NULL;
150         }
151         PORT_Memcpy(attribute->attrib.pValue, value, len);
152         attribute->attrib.ulValueLen = len;
153     } else {
154         attribute->attrib.pValue = NULL;
155         attribute->attrib.ulValueLen = 0;
156     }
157     attribute->attrib.type = type;
158     attribute->handle = type;
159     attribute->next = attribute->prev = NULL;
160     return attribute;
161 }
162 
163 /*
164  * Free up all the memory associated with an attribute. Reference count
165  * must be zero to call this.
166  */
167 static void
sftk_DestroyAttribute(SFTKAttribute * attribute)168 sftk_DestroyAttribute(SFTKAttribute *attribute)
169 {
170     if (attribute->attrib.pValue) {
171         /* clear out the data in the attribute value... it may have been
172          * sensitive data */
173         PORT_Memset(attribute->attrib.pValue, 0, attribute->attrib.ulValueLen);
174         if (attribute->freeData) {
175             PORT_Free(attribute->attrib.pValue);
176             attribute->attrib.pValue = NULL;
177             attribute->freeData = PR_FALSE;
178         }
179     }
180     if (attribute->freeAttr) {
181         PORT_Free(attribute);
182     }
183 }
184 
185 /*
186  * release a reference to an attribute structure
187  */
188 void
sftk_FreeAttribute(SFTKAttribute * attribute)189 sftk_FreeAttribute(SFTKAttribute *attribute)
190 {
191     if (attribute && attribute->freeAttr) {
192         sftk_DestroyAttribute(attribute);
193         return;
194     }
195 }
196 
197 static SFTKAttribute *
sftk_FindTokenAttribute(SFTKTokenObject * object,CK_ATTRIBUTE_TYPE type)198 sftk_FindTokenAttribute(SFTKTokenObject *object, CK_ATTRIBUTE_TYPE type)
199 {
200     SFTKAttribute *myattribute = NULL;
201     SFTKDBHandle *dbHandle = NULL;
202     CK_RV crv = CKR_HOST_MEMORY;
203 
204     myattribute = (SFTKAttribute *)PORT_Alloc(sizeof(SFTKAttribute));
205     if (myattribute == NULL) {
206         goto loser;
207     }
208 
209     dbHandle = sftk_getDBForTokenObject(object->obj.slot, object->obj.handle);
210 
211     myattribute->handle = type;
212     myattribute->attrib.type = type;
213     myattribute->attrib.pValue = myattribute->space;
214     myattribute->attrib.ulValueLen = ATTR_SPACE;
215     myattribute->next = myattribute->prev = NULL;
216     myattribute->freeAttr = PR_TRUE;
217     myattribute->freeData = PR_FALSE;
218 
219     crv = sftkdb_GetAttributeValue(dbHandle, object->obj.handle,
220                                    &myattribute->attrib, 1);
221 
222     /* attribute is bigger than our attribute space buffer, malloc it */
223     if (crv == CKR_BUFFER_TOO_SMALL) {
224         myattribute->attrib.pValue = NULL;
225         crv = sftkdb_GetAttributeValue(dbHandle, object->obj.handle,
226                                        &myattribute->attrib, 1);
227         if (crv != CKR_OK) {
228             goto loser;
229         }
230         myattribute->attrib.pValue = PORT_Alloc(myattribute->attrib.ulValueLen);
231         if (myattribute->attrib.pValue == NULL) {
232             crv = CKR_HOST_MEMORY;
233             goto loser;
234         }
235         myattribute->freeData = PR_TRUE;
236         crv = sftkdb_GetAttributeValue(dbHandle, object->obj.handle,
237                                        &myattribute->attrib, 1);
238     }
239 loser:
240     if (dbHandle) {
241         sftk_freeDB(dbHandle);
242     }
243     if (crv != CKR_OK) {
244         if (myattribute) {
245             myattribute->attrib.ulValueLen = 0;
246             sftk_FreeAttribute(myattribute);
247             myattribute = NULL;
248         }
249     }
250     return myattribute;
251 }
252 
253 /*
254  * look up and attribute structure from a type and Object structure.
255  * The returned attribute is referenced and needs to be freed when
256  * it is no longer needed.
257  */
258 SFTKAttribute *
sftk_FindAttribute(SFTKObject * object,CK_ATTRIBUTE_TYPE type)259 sftk_FindAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type)
260 {
261     SFTKAttribute *attribute;
262     SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
263 
264     if (sessObject == NULL) {
265         return sftk_FindTokenAttribute(sftk_narrowToTokenObject(object), type);
266     }
267 
268     PZ_Lock(sessObject->attributeLock);
269     sftkqueue_find(attribute, type, sessObject->head, sessObject->hashSize);
270     PZ_Unlock(sessObject->attributeLock);
271 
272     return (attribute);
273 }
274 
275 /*
276  * Take a buffer and it's length and return it's true size in bits;
277  */
278 unsigned int
sftk_GetLengthInBits(unsigned char * buf,unsigned int bufLen)279 sftk_GetLengthInBits(unsigned char *buf, unsigned int bufLen)
280 {
281     unsigned int size = bufLen * 8;
282     unsigned int i;
283 
284     /* Get the real length in bytes */
285     for (i = 0; i < bufLen; i++) {
286         unsigned char c = *buf++;
287         if (c != 0) {
288             unsigned char m;
289             for (m = 0x80; m > 0; m = m >> 1) {
290                 if ((c & m) != 0) {
291                     break;
292                 }
293                 size--;
294             }
295             break;
296         }
297         size -= 8;
298     }
299     return size;
300 }
301 
302 /*
303  * Constrain a big num attribute. to size and padding
304  * minLength means length of the object must be greater than equal to minLength
305  * maxLength means length of the object must be less than equal to maxLength
306  * minMultiple means that object length mod minMultiple must equal 0.
307  * all input sizes are in bits.
308  * if any constraint is '0' that constraint is not checked.
309  */
310 CK_RV
sftk_ConstrainAttribute(SFTKObject * object,CK_ATTRIBUTE_TYPE type,int minLength,int maxLength,int minMultiple)311 sftk_ConstrainAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
312                         int minLength, int maxLength, int minMultiple)
313 {
314     SFTKAttribute *attribute;
315     int size;
316     unsigned char *ptr;
317 
318     attribute = sftk_FindAttribute(object, type);
319     if (!attribute) {
320         return CKR_TEMPLATE_INCOMPLETE;
321     }
322     ptr = (unsigned char *)attribute->attrib.pValue;
323     if (ptr == NULL) {
324         sftk_FreeAttribute(attribute);
325         return CKR_ATTRIBUTE_VALUE_INVALID;
326     }
327     size = sftk_GetLengthInBits(ptr, attribute->attrib.ulValueLen);
328     sftk_FreeAttribute(attribute);
329 
330     if ((minLength != 0) && (size < minLength)) {
331         return CKR_ATTRIBUTE_VALUE_INVALID;
332     }
333     if ((maxLength != 0) && (size > maxLength)) {
334         return CKR_ATTRIBUTE_VALUE_INVALID;
335     }
336     if ((minMultiple != 0) && ((size % minMultiple) != 0)) {
337         return CKR_ATTRIBUTE_VALUE_INVALID;
338     }
339     return CKR_OK;
340 }
341 
342 PRBool
sftk_hasAttributeToken(SFTKTokenObject * object,CK_ATTRIBUTE_TYPE type)343 sftk_hasAttributeToken(SFTKTokenObject *object, CK_ATTRIBUTE_TYPE type)
344 {
345     CK_ATTRIBUTE template;
346     CK_RV crv;
347     SFTKDBHandle *dbHandle;
348 
349     dbHandle = sftk_getDBForTokenObject(object->obj.slot, object->obj.handle);
350     template.type = type;
351     template.pValue = NULL;
352     template.ulValueLen = 0;
353 
354     crv = sftkdb_GetAttributeValue(dbHandle, object->obj.handle, &template, 1);
355     sftk_freeDB(dbHandle);
356 
357     /* attribute is bigger than our attribute space buffer, malloc it */
358     return (crv == CKR_OK) ? PR_TRUE : PR_FALSE;
359 }
360 
361 /*
362  * return true if object has attribute
363  */
364 PRBool
sftk_hasAttribute(SFTKObject * object,CK_ATTRIBUTE_TYPE type)365 sftk_hasAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type)
366 {
367     SFTKAttribute *attribute;
368     SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
369 
370     if (sessObject == NULL) {
371         return sftk_hasAttributeToken(sftk_narrowToTokenObject(object), type);
372     }
373 
374     PZ_Lock(sessObject->attributeLock);
375     sftkqueue_find(attribute, type, sessObject->head, sessObject->hashSize);
376     PZ_Unlock(sessObject->attributeLock);
377 
378     return (PRBool)(attribute != NULL);
379 }
380 
381 /*
382  * add an attribute to an object
383  */
384 static void
sftk_AddAttribute(SFTKObject * object,SFTKAttribute * attribute)385 sftk_AddAttribute(SFTKObject *object, SFTKAttribute *attribute)
386 {
387     SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
388 
389     if (sessObject == NULL)
390         return;
391     PZ_Lock(sessObject->attributeLock);
392     sftkqueue_add(attribute, attribute->handle,
393                   sessObject->head, sessObject->hashSize);
394     PZ_Unlock(sessObject->attributeLock);
395 }
396 
397 /*
398  * copy an unsigned attribute into a SECItem. Secitem is allocated in
399  * the specified arena.
400  */
401 CK_RV
sftk_Attribute2SSecItem(PLArenaPool * arena,SECItem * item,SFTKObject * object,CK_ATTRIBUTE_TYPE type)402 sftk_Attribute2SSecItem(PLArenaPool *arena, SECItem *item, SFTKObject *object,
403                         CK_ATTRIBUTE_TYPE type)
404 {
405     SFTKAttribute *attribute;
406 
407     item->data = NULL;
408 
409     attribute = sftk_FindAttribute(object, type);
410     if (attribute == NULL)
411         return CKR_TEMPLATE_INCOMPLETE;
412 
413     (void)SECITEM_AllocItem(arena, item, attribute->attrib.ulValueLen);
414     if (item->data == NULL) {
415         sftk_FreeAttribute(attribute);
416         return CKR_HOST_MEMORY;
417     }
418     PORT_Memcpy(item->data, attribute->attrib.pValue, item->len);
419     sftk_FreeAttribute(attribute);
420     return CKR_OK;
421 }
422 
423 /*
424  * fetch multiple attributes into  SECItems. Secitem data is allocated in
425  * the specified arena.
426  */
427 CK_RV
sftk_MultipleAttribute2SecItem(PLArenaPool * arena,SFTKObject * object,SFTKItemTemplate * itemTemplate,int itemTemplateCount)428 sftk_MultipleAttribute2SecItem(PLArenaPool *arena, SFTKObject *object,
429                                SFTKItemTemplate *itemTemplate, int itemTemplateCount)
430 {
431 
432     CK_RV crv = CKR_OK;
433     CK_ATTRIBUTE templateSpace[SFTK_MAX_ITEM_TEMPLATE];
434     CK_ATTRIBUTE *template;
435     SFTKTokenObject *tokObject;
436     SFTKDBHandle *dbHandle = NULL;
437     int i;
438 
439     tokObject = sftk_narrowToTokenObject(object);
440 
441     /* session objects, just loop through the list */
442     if (tokObject == NULL) {
443         for (i = 0; i < itemTemplateCount; i++) {
444             crv = sftk_Attribute2SecItem(arena, itemTemplate[i].item, object,
445                                          itemTemplate[i].type);
446             if (crv != CKR_OK) {
447                 return crv;
448             }
449         }
450         return CKR_OK;
451     }
452 
453     /* don't do any work if none is required */
454     if (itemTemplateCount == 0) {
455         return CKR_OK;
456     }
457 
458     /* don't allocate the template unless we need it */
459     if (itemTemplateCount > SFTK_MAX_ITEM_TEMPLATE) {
460         template = PORT_NewArray(CK_ATTRIBUTE, itemTemplateCount);
461     } else {
462         template = templateSpace;
463     }
464 
465     if (template == NULL) {
466         crv = CKR_HOST_MEMORY;
467         goto loser;
468     }
469 
470     dbHandle = sftk_getDBForTokenObject(object->slot, object->handle);
471     if (dbHandle == NULL) {
472         crv = CKR_OBJECT_HANDLE_INVALID;
473         goto loser;
474     }
475 
476     /* set up the PKCS #11 template */
477     for (i = 0; i < itemTemplateCount; i++) {
478         template[i].type = itemTemplate[i].type;
479         template[i].pValue = NULL;
480         template[i].ulValueLen = 0;
481     }
482 
483     /* fetch the attribute lengths */
484     crv = sftkdb_GetAttributeValue(dbHandle, object->handle,
485                                    template, itemTemplateCount);
486     if (crv != CKR_OK) {
487         goto loser;
488     }
489 
490     /* allocate space for the attributes */
491     for (i = 0; i < itemTemplateCount; i++) {
492         template[i].pValue = PORT_ArenaAlloc(arena, template[i].ulValueLen);
493         if (template[i].pValue == NULL) {
494             crv = CKR_HOST_MEMORY;
495             goto loser;
496         }
497     }
498 
499     /* fetch the attributes */
500     crv = sftkdb_GetAttributeValue(dbHandle, object->handle,
501                                    template, itemTemplateCount);
502     if (crv != CKR_OK) {
503         goto loser;
504     }
505 
506     /* Fill in the items */
507     for (i = 0; i < itemTemplateCount; i++) {
508         itemTemplate[i].item->data = template[i].pValue;
509         itemTemplate[i].item->len = template[i].ulValueLen;
510     }
511 
512 loser:
513     if (template != templateSpace) {
514         PORT_Free(template);
515     }
516     if (dbHandle) {
517         sftk_freeDB(dbHandle);
518     }
519 
520     return crv;
521 }
522 
523 /*
524  * delete an attribute from an object
525  */
526 static void
sftk_DeleteAttribute(SFTKObject * object,SFTKAttribute * attribute)527 sftk_DeleteAttribute(SFTKObject *object, SFTKAttribute *attribute)
528 {
529     SFTKSessionObject *sessObject = sftk_narrowToSessionObject(object);
530 
531     if (sessObject == NULL) {
532         return;
533     }
534     PZ_Lock(sessObject->attributeLock);
535     if (sftkqueue_is_queued(attribute, attribute->handle,
536                             sessObject->head, sessObject->hashSize)) {
537         sftkqueue_delete(attribute, attribute->handle,
538                          sessObject->head, sessObject->hashSize);
539     }
540     PZ_Unlock(sessObject->attributeLock);
541 }
542 
543 /*
544  * this is only valid for CK_BBOOL type attributes. Return the state
545  * of that attribute.
546  */
547 PRBool
sftk_isTrue(SFTKObject * object,CK_ATTRIBUTE_TYPE type)548 sftk_isTrue(SFTKObject *object, CK_ATTRIBUTE_TYPE type)
549 {
550     SFTKAttribute *attribute;
551     PRBool tok = PR_FALSE;
552 
553     attribute = sftk_FindAttribute(object, type);
554     if (attribute == NULL) {
555         return PR_FALSE;
556     }
557     tok = (PRBool)(*(CK_BBOOL *)attribute->attrib.pValue);
558     sftk_FreeAttribute(attribute);
559 
560     return tok;
561 }
562 
563 /*
564  * force an attribute to null.
565  * this is for sensitive keys which are stored in the database, we don't
566  * want to keep this info around in memory in the clear.
567  */
568 void
sftk_nullAttribute(SFTKObject * object,CK_ATTRIBUTE_TYPE type)569 sftk_nullAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type)
570 {
571     SFTKAttribute *attribute;
572 
573     attribute = sftk_FindAttribute(object, type);
574     if (attribute == NULL)
575         return;
576 
577     if (attribute->attrib.pValue != NULL) {
578         PORT_Memset(attribute->attrib.pValue, 0, attribute->attrib.ulValueLen);
579         if (attribute->freeData) {
580             PORT_Free(attribute->attrib.pValue);
581         }
582         attribute->freeData = PR_FALSE;
583         attribute->attrib.pValue = NULL;
584         attribute->attrib.ulValueLen = 0;
585     }
586     sftk_FreeAttribute(attribute);
587 }
588 
589 static CK_RV
sftk_forceTokenAttribute(SFTKObject * object,CK_ATTRIBUTE_TYPE type,const void * value,unsigned int len)590 sftk_forceTokenAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
591                          const void *value, unsigned int len)
592 {
593     CK_ATTRIBUTE attribute;
594     SFTKDBHandle *dbHandle = NULL;
595     SFTKTokenObject *to = sftk_narrowToTokenObject(object);
596     CK_RV crv;
597 
598     PORT_Assert(to);
599     if (to == NULL) {
600         return CKR_DEVICE_ERROR;
601     }
602 
603     dbHandle = sftk_getDBForTokenObject(object->slot, object->handle);
604 
605     attribute.type = type;
606     attribute.pValue = (void *)value;
607     attribute.ulValueLen = len;
608 
609     crv = sftkdb_SetAttributeValue(dbHandle, object, &attribute, 1);
610     sftk_freeDB(dbHandle);
611     return crv;
612 }
613 
614 /*
615  * force an attribute to a specifc value.
616  */
617 CK_RV
sftk_forceAttribute(SFTKObject * object,CK_ATTRIBUTE_TYPE type,const void * value,unsigned int len)618 sftk_forceAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
619                     const void *value, unsigned int len)
620 {
621     SFTKAttribute *attribute;
622     void *att_val = NULL;
623     PRBool freeData = PR_FALSE;
624 
625     PORT_Assert(object);
626     PORT_Assert(object->refCount);
627     PORT_Assert(object->slot);
628     if (!object ||
629         !object->refCount ||
630         !object->slot) {
631         return CKR_DEVICE_ERROR;
632     }
633     if (sftk_isToken(object->handle)) {
634         return sftk_forceTokenAttribute(object, type, value, len);
635     }
636     attribute = sftk_FindAttribute(object, type);
637     if (attribute == NULL)
638         return sftk_AddAttributeType(object, type, value, len);
639 
640     if (value) {
641         if (len <= ATTR_SPACE) {
642             att_val = attribute->space;
643         } else {
644             att_val = PORT_Alloc(len);
645             freeData = PR_TRUE;
646         }
647         if (att_val == NULL) {
648             return CKR_HOST_MEMORY;
649         }
650         if (attribute->attrib.pValue == att_val) {
651             PORT_Memset(attribute->attrib.pValue, 0,
652                         attribute->attrib.ulValueLen);
653         }
654         PORT_Memcpy(att_val, value, len);
655     }
656     if (attribute->attrib.pValue != NULL) {
657         if (attribute->attrib.pValue != att_val) {
658             PORT_Memset(attribute->attrib.pValue, 0,
659                         attribute->attrib.ulValueLen);
660         }
661         if (attribute->freeData) {
662             PORT_Free(attribute->attrib.pValue);
663         }
664         attribute->freeData = PR_FALSE;
665         attribute->attrib.pValue = NULL;
666         attribute->attrib.ulValueLen = 0;
667     }
668     if (att_val) {
669         attribute->attrib.pValue = att_val;
670         attribute->attrib.ulValueLen = len;
671         attribute->freeData = freeData;
672     }
673     sftk_FreeAttribute(attribute);
674     return CKR_OK;
675 }
676 
677 /*
678  * return a null terminated string from attribute 'type'. This string
679  * is allocated and needs to be freed with PORT_Free() When complete.
680  */
681 char *
sftk_getString(SFTKObject * object,CK_ATTRIBUTE_TYPE type)682 sftk_getString(SFTKObject *object, CK_ATTRIBUTE_TYPE type)
683 {
684     SFTKAttribute *attribute;
685     char *label = NULL;
686 
687     attribute = sftk_FindAttribute(object, type);
688     if (attribute == NULL)
689         return NULL;
690 
691     if (attribute->attrib.pValue != NULL) {
692         label = (char *)PORT_Alloc(attribute->attrib.ulValueLen + 1);
693         if (label == NULL) {
694             sftk_FreeAttribute(attribute);
695             return NULL;
696         }
697 
698         PORT_Memcpy(label, attribute->attrib.pValue,
699                     attribute->attrib.ulValueLen);
700         label[attribute->attrib.ulValueLen] = 0;
701     }
702     sftk_FreeAttribute(attribute);
703     return label;
704 }
705 
706 /*
707  * decode when a particular attribute may be modified
708  *  SFTK_NEVER: This attribute must be set at object creation time and
709  *  can never be modified.
710  *  SFTK_ONCOPY: This attribute may be modified only when you copy the
711  *  object.
712  *  SFTK_SENSITIVE: The CKA_SENSITIVE attribute can only be changed from
713  *  CK_FALSE to CK_TRUE.
714  *  SFTK_ALWAYS: This attribute can always be modified.
715  * Some attributes vary their modification type based on the class of the
716  *   object.
717  */
718 SFTKModifyType
sftk_modifyType(CK_ATTRIBUTE_TYPE type,CK_OBJECT_CLASS inClass)719 sftk_modifyType(CK_ATTRIBUTE_TYPE type, CK_OBJECT_CLASS inClass)
720 {
721     /* if we don't know about it, user user defined, always allow modify */
722     SFTKModifyType mtype = SFTK_ALWAYS;
723 
724     switch (type) {
725         /* NEVER */
726         case CKA_CLASS:
727         case CKA_CERTIFICATE_TYPE:
728         case CKA_KEY_TYPE:
729         case CKA_MODULUS:
730         case CKA_MODULUS_BITS:
731         case CKA_PUBLIC_EXPONENT:
732         case CKA_PRIVATE_EXPONENT:
733         case CKA_PRIME:
734         case CKA_BASE:
735         case CKA_PRIME_1:
736         case CKA_PRIME_2:
737         case CKA_EXPONENT_1:
738         case CKA_EXPONENT_2:
739         case CKA_COEFFICIENT:
740         case CKA_VALUE_LEN:
741         case CKA_ALWAYS_SENSITIVE:
742         case CKA_NEVER_EXTRACTABLE:
743         case CKA_NSS_DB:
744             mtype = SFTK_NEVER;
745             break;
746 
747         /* ONCOPY */
748         case CKA_TOKEN:
749         case CKA_PRIVATE:
750         case CKA_MODIFIABLE:
751             mtype = SFTK_ONCOPY;
752             break;
753 
754         /* SENSITIVE */
755         case CKA_SENSITIVE:
756         case CKA_EXTRACTABLE:
757             mtype = SFTK_SENSITIVE;
758             break;
759 
760         /* ALWAYS */
761         case CKA_LABEL:
762         case CKA_APPLICATION:
763         case CKA_ID:
764         case CKA_SERIAL_NUMBER:
765         case CKA_START_DATE:
766         case CKA_END_DATE:
767         case CKA_DERIVE:
768         case CKA_ENCRYPT:
769         case CKA_DECRYPT:
770         case CKA_SIGN:
771         case CKA_VERIFY:
772         case CKA_SIGN_RECOVER:
773         case CKA_VERIFY_RECOVER:
774         case CKA_WRAP:
775         case CKA_UNWRAP:
776             mtype = SFTK_ALWAYS;
777             break;
778 
779         /* DEPENDS ON CLASS */
780         case CKA_VALUE:
781             mtype = (inClass == CKO_DATA) ? SFTK_ALWAYS : SFTK_NEVER;
782             break;
783 
784         case CKA_SUBPRIME:
785             /* allow the CKA_SUBPRIME to be added to dh private keys */
786             mtype = (inClass == CKO_PRIVATE_KEY) ? SFTK_ALWAYS : SFTK_NEVER;
787             break;
788 
789         case CKA_SUBJECT:
790             mtype = (inClass == CKO_CERTIFICATE) ? SFTK_NEVER : SFTK_ALWAYS;
791             break;
792         default:
793             break;
794     }
795     return mtype;
796 }
797 
798 /* decode if a particular attribute is sensitive (cannot be read
799  * back to the user of if the object is set to SENSITIVE) */
800 PRBool
sftk_isSensitive(CK_ATTRIBUTE_TYPE type,CK_OBJECT_CLASS inClass)801 sftk_isSensitive(CK_ATTRIBUTE_TYPE type, CK_OBJECT_CLASS inClass)
802 {
803     switch (type) {
804         /* ALWAYS */
805         case CKA_PRIVATE_EXPONENT:
806         case CKA_PRIME_1:
807         case CKA_PRIME_2:
808         case CKA_EXPONENT_1:
809         case CKA_EXPONENT_2:
810         case CKA_COEFFICIENT:
811             return PR_TRUE;
812 
813         /* DEPENDS ON CLASS */
814         case CKA_VALUE:
815             /* PRIVATE and SECRET KEYS have SENSITIVE values */
816             return (PRBool)((inClass == CKO_PRIVATE_KEY) || (inClass == CKO_SECRET_KEY));
817 
818         default:
819             break;
820     }
821     return PR_FALSE;
822 }
823 
824 /*
825  * copy an attribute into a SECItem. Secitem is allocated in the specified
826  * arena.
827  */
828 CK_RV
sftk_Attribute2SecItem(PLArenaPool * arena,SECItem * item,SFTKObject * object,CK_ATTRIBUTE_TYPE type)829 sftk_Attribute2SecItem(PLArenaPool *arena, SECItem *item, SFTKObject *object,
830                        CK_ATTRIBUTE_TYPE type)
831 {
832     int len;
833     SFTKAttribute *attribute;
834 
835     attribute = sftk_FindAttribute(object, type);
836     if (attribute == NULL)
837         return CKR_TEMPLATE_INCOMPLETE;
838     len = attribute->attrib.ulValueLen;
839 
840     if (arena) {
841         item->data = (unsigned char *)PORT_ArenaAlloc(arena, len);
842     } else {
843         item->data = (unsigned char *)PORT_Alloc(len);
844     }
845     if (item->data == NULL) {
846         sftk_FreeAttribute(attribute);
847         return CKR_HOST_MEMORY;
848     }
849     item->len = len;
850     PORT_Memcpy(item->data, attribute->attrib.pValue, len);
851     sftk_FreeAttribute(attribute);
852     return CKR_OK;
853 }
854 
855 CK_RV
sftk_GetULongAttribute(SFTKObject * object,CK_ATTRIBUTE_TYPE type,CK_ULONG * longData)856 sftk_GetULongAttribute(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
857                        CK_ULONG *longData)
858 {
859     SFTKAttribute *attribute;
860 
861     attribute = sftk_FindAttribute(object, type);
862     if (attribute == NULL)
863         return CKR_TEMPLATE_INCOMPLETE;
864 
865     if (attribute->attrib.ulValueLen != sizeof(CK_ULONG)) {
866         return CKR_ATTRIBUTE_VALUE_INVALID;
867     }
868 
869     *longData = *(CK_ULONG *)attribute->attrib.pValue;
870     sftk_FreeAttribute(attribute);
871     return CKR_OK;
872 }
873 
874 void
sftk_DeleteAttributeType(SFTKObject * object,CK_ATTRIBUTE_TYPE type)875 sftk_DeleteAttributeType(SFTKObject *object, CK_ATTRIBUTE_TYPE type)
876 {
877     SFTKAttribute *attribute;
878     attribute = sftk_FindAttribute(object, type);
879     if (attribute == NULL)
880         return;
881     sftk_DeleteAttribute(object, attribute);
882     sftk_DestroyAttribute(attribute);
883 }
884 
885 CK_RV
sftk_AddAttributeType(SFTKObject * object,CK_ATTRIBUTE_TYPE type,const void * valPtr,CK_ULONG length)886 sftk_AddAttributeType(SFTKObject *object, CK_ATTRIBUTE_TYPE type,
887                       const void *valPtr, CK_ULONG length)
888 {
889     SFTKAttribute *attribute;
890     attribute = sftk_NewAttribute(object, type, valPtr, length);
891     if (attribute == NULL) {
892         return CKR_HOST_MEMORY;
893     }
894     sftk_AddAttribute(object, attribute);
895     return CKR_OK;
896 }
897 
898 /*
899  * ******************** Object Utilities *******************************
900  */
901 
902 /* must be called holding sftk_tokenKeyLock(slot) */
903 static SECItem *
sftk_lookupTokenKeyByHandle(SFTKSlot * slot,CK_OBJECT_HANDLE handle)904 sftk_lookupTokenKeyByHandle(SFTKSlot *slot, CK_OBJECT_HANDLE handle)
905 {
906     return (SECItem *)PL_HashTableLookup(slot->tokObjHashTable, (void *)handle);
907 }
908 
909 /*
910  * use the refLock. This operations should be very rare, so the added
911  * contention on the ref lock should be lower than the overhead of adding
912  * a new lock. We use separate functions for this just in case I'm wrong.
913  */
914 static void
sftk_tokenKeyLock(SFTKSlot * slot)915 sftk_tokenKeyLock(SFTKSlot *slot)
916 {
917     SKIP_AFTER_FORK(PZ_Lock(slot->objectLock));
918 }
919 
920 static void
sftk_tokenKeyUnlock(SFTKSlot * slot)921 sftk_tokenKeyUnlock(SFTKSlot *slot)
922 {
923     SKIP_AFTER_FORK(PZ_Unlock(slot->objectLock));
924 }
925 
926 static PRIntn
sftk_freeHashItem(PLHashEntry * entry,PRIntn index,void * arg)927 sftk_freeHashItem(PLHashEntry *entry, PRIntn index, void *arg)
928 {
929     SECItem *item = (SECItem *)entry->value;
930 
931     SECITEM_FreeItem(item, PR_TRUE);
932     return HT_ENUMERATE_NEXT;
933 }
934 
935 CK_RV
SFTK_ClearTokenKeyHashTable(SFTKSlot * slot)936 SFTK_ClearTokenKeyHashTable(SFTKSlot *slot)
937 {
938     sftk_tokenKeyLock(slot);
939     PORT_Assert(!slot->present);
940     PL_HashTableEnumerateEntries(slot->tokObjHashTable, sftk_freeHashItem, NULL);
941     sftk_tokenKeyUnlock(slot);
942     return CKR_OK;
943 }
944 
945 /* allocation hooks that allow us to recycle old object structures */
946 static SFTKObjectFreeList sessionObjectList = { NULL, NULL, 0 };
947 static SFTKObjectFreeList tokenObjectList = { NULL, NULL, 0 };
948 
949 SFTKObject *
sftk_GetObjectFromList(PRBool * hasLocks,PRBool optimizeSpace,SFTKObjectFreeList * list,unsigned int hashSize,PRBool isSessionObject)950 sftk_GetObjectFromList(PRBool *hasLocks, PRBool optimizeSpace,
951                        SFTKObjectFreeList *list, unsigned int hashSize, PRBool isSessionObject)
952 {
953     SFTKObject *object;
954     int size = 0;
955 
956     if (!optimizeSpace) {
957         PZ_Lock(list->lock);
958         object = list->head;
959         if (object) {
960             list->head = object->next;
961             list->count--;
962         }
963         PZ_Unlock(list->lock);
964         if (object) {
965             object->next = object->prev = NULL;
966             *hasLocks = PR_TRUE;
967             return object;
968         }
969     }
970     size = isSessionObject ? sizeof(SFTKSessionObject) + hashSize * sizeof(SFTKAttribute *) : sizeof(SFTKTokenObject);
971 
972     object = (SFTKObject *)PORT_ZAlloc(size);
973     if (isSessionObject && object) {
974         ((SFTKSessionObject *)object)->hashSize = hashSize;
975     }
976     *hasLocks = PR_FALSE;
977     return object;
978 }
979 
980 static void
sftk_PutObjectToList(SFTKObject * object,SFTKObjectFreeList * list,PRBool isSessionObject)981 sftk_PutObjectToList(SFTKObject *object, SFTKObjectFreeList *list,
982                      PRBool isSessionObject)
983 {
984 
985     /* the code below is equivalent to :
986      *     optimizeSpace = isSessionObject ? object->optimizeSpace : PR_FALSE;
987      * just faster.
988      */
989     PRBool optimizeSpace = isSessionObject &&
990                            ((SFTKSessionObject *)object)->optimizeSpace;
991     if (object->refLock && !optimizeSpace && (list->count < MAX_OBJECT_LIST_SIZE)) {
992         PZ_Lock(list->lock);
993         object->next = list->head;
994         list->head = object;
995         list->count++;
996         PZ_Unlock(list->lock);
997         return;
998     }
999     if (isSessionObject) {
1000         SFTKSessionObject *so = (SFTKSessionObject *)object;
1001         PZ_DestroyLock(so->attributeLock);
1002         so->attributeLock = NULL;
1003     }
1004     if (object->refLock) {
1005         PZ_DestroyLock(object->refLock);
1006         object->refLock = NULL;
1007     }
1008     PORT_Free(object);
1009 }
1010 
1011 static SFTKObject *
sftk_freeObjectData(SFTKObject * object)1012 sftk_freeObjectData(SFTKObject *object)
1013 {
1014     SFTKObject *next = object->next;
1015 
1016     PORT_Free(object);
1017     return next;
1018 }
1019 
1020 static void
sftk_InitFreeList(SFTKObjectFreeList * list)1021 sftk_InitFreeList(SFTKObjectFreeList *list)
1022 {
1023     if (!list->lock) {
1024         list->lock = PZ_NewLock(nssILockObject);
1025     }
1026 }
1027 
1028 void
sftk_InitFreeLists(void)1029 sftk_InitFreeLists(void)
1030 {
1031     sftk_InitFreeList(&sessionObjectList);
1032     sftk_InitFreeList(&tokenObjectList);
1033 }
1034 
1035 static void
sftk_CleanupFreeList(SFTKObjectFreeList * list,PRBool isSessionList)1036 sftk_CleanupFreeList(SFTKObjectFreeList *list, PRBool isSessionList)
1037 {
1038     SFTKObject *object;
1039 
1040     if (!list->lock) {
1041         return;
1042     }
1043     SKIP_AFTER_FORK(PZ_Lock(list->lock));
1044     for (object = list->head; object != NULL;
1045          object = sftk_freeObjectData(object)) {
1046         PZ_DestroyLock(object->refLock);
1047         if (isSessionList) {
1048             PZ_DestroyLock(((SFTKSessionObject *)object)->attributeLock);
1049         }
1050     }
1051     list->count = 0;
1052     list->head = NULL;
1053     SKIP_AFTER_FORK(PZ_Unlock(list->lock));
1054     SKIP_AFTER_FORK(PZ_DestroyLock(list->lock));
1055     list->lock = NULL;
1056 }
1057 
1058 void
sftk_CleanupFreeLists(void)1059 sftk_CleanupFreeLists(void)
1060 {
1061     sftk_CleanupFreeList(&sessionObjectList, PR_TRUE);
1062     sftk_CleanupFreeList(&tokenObjectList, PR_FALSE);
1063 }
1064 
1065 /*
1066  * Create a new object
1067  */
1068 SFTKObject *
sftk_NewObject(SFTKSlot * slot)1069 sftk_NewObject(SFTKSlot *slot)
1070 {
1071     SFTKObject *object;
1072     SFTKSessionObject *sessObject;
1073     PRBool hasLocks = PR_FALSE;
1074     unsigned int i;
1075     unsigned int hashSize = 0;
1076 
1077     hashSize = (slot->optimizeSpace) ? SPACE_ATTRIBUTE_HASH_SIZE : TIME_ATTRIBUTE_HASH_SIZE;
1078 
1079     object = sftk_GetObjectFromList(&hasLocks, slot->optimizeSpace,
1080                                     &sessionObjectList, hashSize, PR_TRUE);
1081     if (object == NULL) {
1082         return NULL;
1083     }
1084     sessObject = (SFTKSessionObject *)object;
1085     sessObject->nextAttr = 0;
1086 
1087     for (i = 0; i < MAX_OBJS_ATTRS; i++) {
1088         sessObject->attrList[i].attrib.pValue = NULL;
1089         sessObject->attrList[i].freeData = PR_FALSE;
1090     }
1091     sessObject->optimizeSpace = slot->optimizeSpace;
1092 
1093     object->handle = 0;
1094     object->next = object->prev = NULL;
1095     object->slot = slot;
1096     object->isFIPS = sftk_isFIPS(slot->slotID);
1097 
1098     object->refCount = 1;
1099     sessObject->sessionList.next = NULL;
1100     sessObject->sessionList.prev = NULL;
1101     sessObject->sessionList.parent = object;
1102     sessObject->session = NULL;
1103     sessObject->wasDerived = PR_FALSE;
1104     if (!hasLocks)
1105         object->refLock = PZ_NewLock(nssILockRefLock);
1106     if (object->refLock == NULL) {
1107         PORT_Free(object);
1108         return NULL;
1109     }
1110     if (!hasLocks)
1111         sessObject->attributeLock = PZ_NewLock(nssILockAttribute);
1112     if (sessObject->attributeLock == NULL) {
1113         PZ_DestroyLock(object->refLock);
1114         PORT_Free(object);
1115         return NULL;
1116     }
1117     for (i = 0; i < sessObject->hashSize; i++) {
1118         sessObject->head[i] = NULL;
1119     }
1120     object->objectInfo = NULL;
1121     object->infoFree = NULL;
1122     return object;
1123 }
1124 
1125 static CK_RV
sftk_DestroySessionObjectData(SFTKSessionObject * so)1126 sftk_DestroySessionObjectData(SFTKSessionObject *so)
1127 {
1128     int i;
1129 
1130     for (i = 0; i < MAX_OBJS_ATTRS; i++) {
1131         unsigned char *value = so->attrList[i].attrib.pValue;
1132         if (value) {
1133             PORT_Memset(value, 0, so->attrList[i].attrib.ulValueLen);
1134             if (so->attrList[i].freeData) {
1135                 PORT_Free(value);
1136             }
1137             so->attrList[i].attrib.pValue = NULL;
1138             so->attrList[i].freeData = PR_FALSE;
1139         }
1140     }
1141     /*  PZ_DestroyLock(so->attributeLock);*/
1142     return CKR_OK;
1143 }
1144 
1145 /*
1146  * free all the data associated with an object. Object reference count must
1147  * be 'zero'.
1148  */
1149 static CK_RV
sftk_DestroyObject(SFTKObject * object)1150 sftk_DestroyObject(SFTKObject *object)
1151 {
1152     CK_RV crv = CKR_OK;
1153     SFTKSessionObject *so = sftk_narrowToSessionObject(object);
1154     SFTKTokenObject *to = sftk_narrowToTokenObject(object);
1155 
1156     PORT_Assert(object->refCount == 0);
1157 
1158     /* delete the database value */
1159     if (to) {
1160         if (to->dbKey.data) {
1161             PORT_Free(to->dbKey.data);
1162             to->dbKey.data = NULL;
1163         }
1164     }
1165     if (so) {
1166         sftk_DestroySessionObjectData(so);
1167     }
1168     if (object->objectInfo) {
1169         (*object->infoFree)(object->objectInfo);
1170         object->objectInfo = NULL;
1171         object->infoFree = NULL;
1172     }
1173     if (so) {
1174         sftk_PutObjectToList(object, &sessionObjectList, PR_TRUE);
1175     } else {
1176         sftk_PutObjectToList(object, &tokenObjectList, PR_FALSE);
1177     }
1178     return crv;
1179 }
1180 
1181 void
sftk_ReferenceObject(SFTKObject * object)1182 sftk_ReferenceObject(SFTKObject *object)
1183 {
1184     PZ_Lock(object->refLock);
1185     object->refCount++;
1186     PZ_Unlock(object->refLock);
1187 }
1188 
1189 static SFTKObject *
sftk_ObjectFromHandleOnSlot(CK_OBJECT_HANDLE handle,SFTKSlot * slot)1190 sftk_ObjectFromHandleOnSlot(CK_OBJECT_HANDLE handle, SFTKSlot *slot)
1191 {
1192     SFTKObject *object;
1193     PRUint32 index = sftk_hash(handle, slot->sessObjHashSize);
1194 
1195     if (sftk_isToken(handle)) {
1196         return sftk_NewTokenObject(slot, NULL, handle);
1197     }
1198 
1199     PZ_Lock(slot->objectLock);
1200     sftkqueue_find2(object, handle, index, slot->sessObjHashTable);
1201     if (object) {
1202         sftk_ReferenceObject(object);
1203     }
1204     PZ_Unlock(slot->objectLock);
1205 
1206     return (object);
1207 }
1208 /*
1209  * look up and object structure from a handle. OBJECT_Handles only make
1210  * sense in terms of a given session.  make a reference to that object
1211  * structure returned.
1212  */
1213 SFTKObject *
sftk_ObjectFromHandle(CK_OBJECT_HANDLE handle,SFTKSession * session)1214 sftk_ObjectFromHandle(CK_OBJECT_HANDLE handle, SFTKSession *session)
1215 {
1216     SFTKSlot *slot = sftk_SlotFromSession(session);
1217 
1218     return sftk_ObjectFromHandleOnSlot(handle, slot);
1219 }
1220 
1221 /*
1222  * release a reference to an object handle
1223  */
1224 SFTKFreeStatus
sftk_FreeObject(SFTKObject * object)1225 sftk_FreeObject(SFTKObject *object)
1226 {
1227     PRBool destroy = PR_FALSE;
1228     CK_RV crv;
1229 
1230     PZ_Lock(object->refLock);
1231     if (object->refCount == 1)
1232         destroy = PR_TRUE;
1233     object->refCount--;
1234     PZ_Unlock(object->refLock);
1235 
1236     if (destroy) {
1237         crv = sftk_DestroyObject(object);
1238         if (crv != CKR_OK) {
1239             return SFTK_DestroyFailure;
1240         }
1241         return SFTK_Destroyed;
1242     }
1243     return SFTK_Busy;
1244 }
1245 
1246 /*
1247  * add an object to a slot and session queue. These two functions
1248  * adopt the object.
1249  */
1250 void
sftk_AddSlotObject(SFTKSlot * slot,SFTKObject * object)1251 sftk_AddSlotObject(SFTKSlot *slot, SFTKObject *object)
1252 {
1253     PRUint32 index = sftk_hash(object->handle, slot->sessObjHashSize);
1254     sftkqueue_init_element(object);
1255     PZ_Lock(slot->objectLock);
1256     sftkqueue_add2(object, object->handle, index, slot->sessObjHashTable);
1257     PZ_Unlock(slot->objectLock);
1258 }
1259 
1260 void
sftk_AddObject(SFTKSession * session,SFTKObject * object)1261 sftk_AddObject(SFTKSession *session, SFTKObject *object)
1262 {
1263     SFTKSlot *slot = sftk_SlotFromSession(session);
1264     SFTKSessionObject *so = sftk_narrowToSessionObject(object);
1265 
1266     if (so) {
1267         PZ_Lock(session->objectLock);
1268         sftkqueue_add(&so->sessionList, 0, session->objects, 0);
1269         so->session = session;
1270         PZ_Unlock(session->objectLock);
1271     }
1272     sftk_AddSlotObject(slot, object);
1273     sftk_ReferenceObject(object);
1274 }
1275 
1276 /*
1277  * delete an object from a slot and session queue
1278  */
1279 CK_RV
sftk_DeleteObject(SFTKSession * session,SFTKObject * object)1280 sftk_DeleteObject(SFTKSession *session, SFTKObject *object)
1281 {
1282     SFTKSlot *slot = sftk_SlotFromSession(session);
1283     SFTKSessionObject *so = sftk_narrowToSessionObject(object);
1284     CK_RV crv = CKR_OK;
1285     PRUint32 index = sftk_hash(object->handle, slot->sessObjHashSize);
1286 
1287     /* Handle Token case */
1288     if (so && so->session) {
1289         session = so->session;
1290         PZ_Lock(session->objectLock);
1291         sftkqueue_delete(&so->sessionList, 0, session->objects, 0);
1292         PZ_Unlock(session->objectLock);
1293         PZ_Lock(slot->objectLock);
1294         sftkqueue_delete2(object, object->handle, index, slot->sessObjHashTable);
1295         PZ_Unlock(slot->objectLock);
1296         sftkqueue_clear_deleted_element(object);
1297         sftk_FreeObject(object); /* free the reference owned by the queue */
1298     } else {
1299         SFTKDBHandle *handle = sftk_getDBForTokenObject(slot, object->handle);
1300 #ifdef DEBUG
1301         SFTKTokenObject *to = sftk_narrowToTokenObject(object);
1302         PORT_Assert(to);
1303 #endif
1304         crv = sftkdb_DestroyObject(handle, object->handle, object->objclass);
1305         sftk_freeDB(handle);
1306     }
1307     return crv;
1308 }
1309 
1310 /*
1311  * Token objects don't explicitly store their attributes, so we need to know
1312  * what attributes make up a particular token object before we can copy it.
1313  * below are the tables by object type.
1314  */
1315 static const CK_ATTRIBUTE_TYPE commonAttrs[] = {
1316     CKA_CLASS, CKA_TOKEN, CKA_PRIVATE, CKA_LABEL, CKA_MODIFIABLE
1317 };
1318 static const CK_ULONG commonAttrsCount =
1319     sizeof(commonAttrs) / sizeof(commonAttrs[0]);
1320 
1321 static const CK_ATTRIBUTE_TYPE commonKeyAttrs[] = {
1322     CKA_ID, CKA_START_DATE, CKA_END_DATE, CKA_DERIVE, CKA_LOCAL, CKA_KEY_TYPE
1323 };
1324 static const CK_ULONG commonKeyAttrsCount =
1325     sizeof(commonKeyAttrs) / sizeof(commonKeyAttrs[0]);
1326 
1327 static const CK_ATTRIBUTE_TYPE secretKeyAttrs[] = {
1328     CKA_SENSITIVE, CKA_EXTRACTABLE, CKA_ENCRYPT, CKA_DECRYPT, CKA_SIGN,
1329     CKA_VERIFY, CKA_WRAP, CKA_UNWRAP, CKA_VALUE
1330 };
1331 static const CK_ULONG secretKeyAttrsCount =
1332     sizeof(secretKeyAttrs) / sizeof(secretKeyAttrs[0]);
1333 
1334 static const CK_ATTRIBUTE_TYPE commonPubKeyAttrs[] = {
1335     CKA_ENCRYPT, CKA_VERIFY, CKA_VERIFY_RECOVER, CKA_WRAP, CKA_SUBJECT
1336 };
1337 static const CK_ULONG commonPubKeyAttrsCount =
1338     sizeof(commonPubKeyAttrs) / sizeof(commonPubKeyAttrs[0]);
1339 
1340 static const CK_ATTRIBUTE_TYPE rsaPubKeyAttrs[] = {
1341     CKA_MODULUS, CKA_PUBLIC_EXPONENT
1342 };
1343 static const CK_ULONG rsaPubKeyAttrsCount =
1344     sizeof(rsaPubKeyAttrs) / sizeof(rsaPubKeyAttrs[0]);
1345 
1346 static const CK_ATTRIBUTE_TYPE dsaPubKeyAttrs[] = {
1347     CKA_SUBPRIME, CKA_PRIME, CKA_BASE, CKA_VALUE
1348 };
1349 static const CK_ULONG dsaPubKeyAttrsCount =
1350     sizeof(dsaPubKeyAttrs) / sizeof(dsaPubKeyAttrs[0]);
1351 
1352 static const CK_ATTRIBUTE_TYPE dhPubKeyAttrs[] = {
1353     CKA_PRIME, CKA_BASE, CKA_VALUE
1354 };
1355 static const CK_ULONG dhPubKeyAttrsCount =
1356     sizeof(dhPubKeyAttrs) / sizeof(dhPubKeyAttrs[0]);
1357 static const CK_ATTRIBUTE_TYPE ecPubKeyAttrs[] = {
1358     CKA_EC_PARAMS, CKA_EC_POINT
1359 };
1360 static const CK_ULONG ecPubKeyAttrsCount =
1361     sizeof(ecPubKeyAttrs) / sizeof(ecPubKeyAttrs[0]);
1362 
1363 static const CK_ATTRIBUTE_TYPE commonPrivKeyAttrs[] = {
1364     CKA_DECRYPT, CKA_SIGN, CKA_SIGN_RECOVER, CKA_UNWRAP, CKA_SUBJECT,
1365     CKA_SENSITIVE, CKA_EXTRACTABLE, CKA_NSS_DB, CKA_PUBLIC_KEY_INFO
1366 };
1367 static const CK_ULONG commonPrivKeyAttrsCount =
1368     sizeof(commonPrivKeyAttrs) / sizeof(commonPrivKeyAttrs[0]);
1369 
1370 static const CK_ATTRIBUTE_TYPE rsaPrivKeyAttrs[] = {
1371     CKA_MODULUS, CKA_PUBLIC_EXPONENT, CKA_PRIVATE_EXPONENT,
1372     CKA_PRIME_1, CKA_PRIME_2, CKA_EXPONENT_1, CKA_EXPONENT_2, CKA_COEFFICIENT
1373 };
1374 static const CK_ULONG rsaPrivKeyAttrsCount =
1375     sizeof(rsaPrivKeyAttrs) / sizeof(rsaPrivKeyAttrs[0]);
1376 
1377 static const CK_ATTRIBUTE_TYPE dsaPrivKeyAttrs[] = {
1378     CKA_SUBPRIME, CKA_PRIME, CKA_BASE, CKA_VALUE
1379 };
1380 static const CK_ULONG dsaPrivKeyAttrsCount =
1381     sizeof(dsaPrivKeyAttrs) / sizeof(dsaPrivKeyAttrs[0]);
1382 
1383 static const CK_ATTRIBUTE_TYPE dhPrivKeyAttrs[] = {
1384     CKA_PRIME, CKA_BASE, CKA_VALUE
1385 };
1386 static const CK_ULONG dhPrivKeyAttrsCount =
1387     sizeof(dhPrivKeyAttrs) / sizeof(dhPrivKeyAttrs[0]);
1388 static const CK_ATTRIBUTE_TYPE ecPrivKeyAttrs[] = {
1389     CKA_EC_PARAMS, CKA_VALUE
1390 };
1391 static const CK_ULONG ecPrivKeyAttrsCount =
1392     sizeof(ecPrivKeyAttrs) / sizeof(ecPrivKeyAttrs[0]);
1393 
1394 static const CK_ATTRIBUTE_TYPE certAttrs[] = {
1395     CKA_CERTIFICATE_TYPE, CKA_VALUE, CKA_SUBJECT, CKA_ISSUER, CKA_SERIAL_NUMBER
1396 };
1397 static const CK_ULONG certAttrsCount =
1398     sizeof(certAttrs) / sizeof(certAttrs[0]);
1399 
1400 static const CK_ATTRIBUTE_TYPE trustAttrs[] = {
1401     CKA_ISSUER, CKA_SERIAL_NUMBER, CKA_CERT_SHA1_HASH, CKA_CERT_MD5_HASH,
1402     CKA_TRUST_SERVER_AUTH, CKA_TRUST_CLIENT_AUTH, CKA_TRUST_EMAIL_PROTECTION,
1403     CKA_TRUST_CODE_SIGNING, CKA_TRUST_STEP_UP_APPROVED
1404 };
1405 static const CK_ULONG trustAttrsCount =
1406     sizeof(trustAttrs) / sizeof(trustAttrs[0]);
1407 
1408 static const CK_ATTRIBUTE_TYPE smimeAttrs[] = {
1409     CKA_SUBJECT, CKA_NSS_EMAIL, CKA_NSS_SMIME_TIMESTAMP, CKA_VALUE
1410 };
1411 static const CK_ULONG smimeAttrsCount =
1412     sizeof(smimeAttrs) / sizeof(smimeAttrs[0]);
1413 
1414 static const CK_ATTRIBUTE_TYPE crlAttrs[] = {
1415     CKA_SUBJECT, CKA_VALUE, CKA_NSS_URL, CKA_NSS_KRL
1416 };
1417 static const CK_ULONG crlAttrsCount =
1418     sizeof(crlAttrs) / sizeof(crlAttrs[0]);
1419 
1420 /* copy an object based on it's table */
1421 CK_RV
stfk_CopyTokenAttributes(SFTKObject * destObject,SFTKTokenObject * src_to,const CK_ATTRIBUTE_TYPE * attrArray,CK_ULONG attrCount)1422 stfk_CopyTokenAttributes(SFTKObject *destObject, SFTKTokenObject *src_to,
1423                          const CK_ATTRIBUTE_TYPE *attrArray, CK_ULONG attrCount)
1424 {
1425     SFTKAttribute *attribute;
1426     SFTKAttribute *newAttribute;
1427     CK_RV crv = CKR_OK;
1428     unsigned int i;
1429 
1430     for (i = 0; i < attrCount; i++) {
1431         if (!sftk_hasAttribute(destObject, attrArray[i])) {
1432             attribute = sftk_FindAttribute(&src_to->obj, attrArray[i]);
1433             if (!attribute) {
1434                 continue; /* return CKR_ATTRIBUTE_VALUE_INVALID; */
1435             }
1436             /* we need to copy the attribute since each attribute
1437              * only has one set of link list pointers */
1438             newAttribute = sftk_NewAttribute(destObject,
1439                                              sftk_attr_expand(&attribute->attrib));
1440             sftk_FreeAttribute(attribute); /* free the old attribute */
1441             if (!newAttribute) {
1442                 return CKR_HOST_MEMORY;
1443             }
1444             sftk_AddAttribute(destObject, newAttribute);
1445         }
1446     }
1447     return crv;
1448 }
1449 
1450 CK_RV
stfk_CopyTokenPrivateKey(SFTKObject * destObject,SFTKTokenObject * src_to)1451 stfk_CopyTokenPrivateKey(SFTKObject *destObject, SFTKTokenObject *src_to)
1452 {
1453     CK_RV crv;
1454     CK_KEY_TYPE key_type;
1455     SFTKAttribute *attribute;
1456 
1457     /* copy the common attributes for all keys first */
1458     crv = stfk_CopyTokenAttributes(destObject, src_to, commonKeyAttrs,
1459                                    commonKeyAttrsCount);
1460     if (crv != CKR_OK) {
1461         goto fail;
1462     }
1463     /* copy the common attributes for all private keys next */
1464     crv = stfk_CopyTokenAttributes(destObject, src_to, commonPrivKeyAttrs,
1465                                    commonPrivKeyAttrsCount);
1466     if (crv != CKR_OK) {
1467         goto fail;
1468     }
1469     attribute = sftk_FindAttribute(&src_to->obj, CKA_KEY_TYPE);
1470     PORT_Assert(attribute); /* if it wasn't here, ww should have failed
1471                  * copying the common attributes */
1472     if (!attribute) {
1473         /* OK, so CKR_ATTRIBUTE_VALUE_INVALID is the immediate error, but
1474          * the fact is, the only reason we couldn't get the attribute would
1475          * be a memory error or database error (an error in the 'device').
1476          * if we have a database error code, we could return it here */
1477         crv = CKR_DEVICE_ERROR;
1478         goto fail;
1479     }
1480     key_type = *(CK_KEY_TYPE *)attribute->attrib.pValue;
1481     sftk_FreeAttribute(attribute);
1482 
1483     /* finally copy the attributes for various private key types */
1484     switch (key_type) {
1485         case CKK_RSA:
1486             crv = stfk_CopyTokenAttributes(destObject, src_to, rsaPrivKeyAttrs,
1487                                            rsaPrivKeyAttrsCount);
1488             break;
1489         case CKK_DSA:
1490             crv = stfk_CopyTokenAttributes(destObject, src_to, dsaPrivKeyAttrs,
1491                                            dsaPrivKeyAttrsCount);
1492             break;
1493         case CKK_DH:
1494             crv = stfk_CopyTokenAttributes(destObject, src_to, dhPrivKeyAttrs,
1495                                            dhPrivKeyAttrsCount);
1496             break;
1497         case CKK_EC:
1498             crv = stfk_CopyTokenAttributes(destObject, src_to, ecPrivKeyAttrs,
1499                                            ecPrivKeyAttrsCount);
1500             break;
1501         default:
1502             crv = CKR_DEVICE_ERROR; /* shouldn't happen unless we store more types
1503                                      * of token keys into our database. */
1504     }
1505 fail:
1506     return crv;
1507 }
1508 
1509 CK_RV
stfk_CopyTokenPublicKey(SFTKObject * destObject,SFTKTokenObject * src_to)1510 stfk_CopyTokenPublicKey(SFTKObject *destObject, SFTKTokenObject *src_to)
1511 {
1512     CK_RV crv;
1513     CK_KEY_TYPE key_type;
1514     SFTKAttribute *attribute;
1515 
1516     /* copy the common attributes for all keys first */
1517     crv = stfk_CopyTokenAttributes(destObject, src_to, commonKeyAttrs,
1518                                    commonKeyAttrsCount);
1519     if (crv != CKR_OK) {
1520         goto fail;
1521     }
1522 
1523     /* copy the common attributes for all public keys next */
1524     crv = stfk_CopyTokenAttributes(destObject, src_to, commonPubKeyAttrs,
1525                                    commonPubKeyAttrsCount);
1526     if (crv != CKR_OK) {
1527         goto fail;
1528     }
1529     attribute = sftk_FindAttribute(&src_to->obj, CKA_KEY_TYPE);
1530     PORT_Assert(attribute); /* if it wasn't here, ww should have failed
1531                  * copying the common attributes */
1532     if (!attribute) {
1533         /* OK, so CKR_ATTRIBUTE_VALUE_INVALID is the immediate error, but
1534          * the fact is, the only reason we couldn't get the attribute would
1535          * be a memory error or database error (an error in the 'device').
1536          * if we have a database error code, we could return it here */
1537         crv = CKR_DEVICE_ERROR;
1538         goto fail;
1539     }
1540     key_type = *(CK_KEY_TYPE *)attribute->attrib.pValue;
1541     sftk_FreeAttribute(attribute);
1542 
1543     /* finally copy the attributes for various public key types */
1544     switch (key_type) {
1545         case CKK_RSA:
1546             crv = stfk_CopyTokenAttributes(destObject, src_to, rsaPubKeyAttrs,
1547                                            rsaPubKeyAttrsCount);
1548             break;
1549         case CKK_DSA:
1550             crv = stfk_CopyTokenAttributes(destObject, src_to, dsaPubKeyAttrs,
1551                                            dsaPubKeyAttrsCount);
1552             break;
1553         case CKK_DH:
1554             crv = stfk_CopyTokenAttributes(destObject, src_to, dhPubKeyAttrs,
1555                                            dhPubKeyAttrsCount);
1556             break;
1557         case CKK_EC:
1558             crv = stfk_CopyTokenAttributes(destObject, src_to, ecPubKeyAttrs,
1559                                            ecPubKeyAttrsCount);
1560             break;
1561         default:
1562             crv = CKR_DEVICE_ERROR; /* shouldn't happen unless we store more types
1563                                      * of token keys into our database. */
1564     }
1565 fail:
1566     return crv;
1567 }
1568 CK_RV
stfk_CopyTokenSecretKey(SFTKObject * destObject,SFTKTokenObject * src_to)1569 stfk_CopyTokenSecretKey(SFTKObject *destObject, SFTKTokenObject *src_to)
1570 {
1571     CK_RV crv;
1572     crv = stfk_CopyTokenAttributes(destObject, src_to, commonKeyAttrs,
1573                                    commonKeyAttrsCount);
1574     if (crv != CKR_OK) {
1575         goto fail;
1576     }
1577     crv = stfk_CopyTokenAttributes(destObject, src_to, secretKeyAttrs,
1578                                    secretKeyAttrsCount);
1579 fail:
1580     return crv;
1581 }
1582 
1583 /*
1584  * Copy a token object. We need to explicitly copy the relevant
1585  * attributes since token objects don't store those attributes in
1586  * the token itself.
1587  */
1588 CK_RV
sftk_CopyTokenObject(SFTKObject * destObject,SFTKObject * srcObject)1589 sftk_CopyTokenObject(SFTKObject *destObject, SFTKObject *srcObject)
1590 {
1591     SFTKTokenObject *src_to = sftk_narrowToTokenObject(srcObject);
1592     CK_RV crv;
1593 
1594     PORT_Assert(src_to);
1595     if (src_to == NULL) {
1596         return CKR_DEVICE_ERROR; /* internal state inconsistant */
1597     }
1598 
1599     crv = stfk_CopyTokenAttributes(destObject, src_to, commonAttrs,
1600                                    commonAttrsCount);
1601     if (crv != CKR_OK) {
1602         goto fail;
1603     }
1604     switch (src_to->obj.objclass) {
1605         case CKO_CERTIFICATE:
1606             crv = stfk_CopyTokenAttributes(destObject, src_to, certAttrs,
1607                                            certAttrsCount);
1608             break;
1609         case CKO_NSS_TRUST:
1610             crv = stfk_CopyTokenAttributes(destObject, src_to, trustAttrs,
1611                                            trustAttrsCount);
1612             break;
1613         case CKO_NSS_SMIME:
1614             crv = stfk_CopyTokenAttributes(destObject, src_to, smimeAttrs,
1615                                            smimeAttrsCount);
1616             break;
1617         case CKO_NSS_CRL:
1618             crv = stfk_CopyTokenAttributes(destObject, src_to, crlAttrs,
1619                                            crlAttrsCount);
1620             break;
1621         case CKO_PRIVATE_KEY:
1622             crv = stfk_CopyTokenPrivateKey(destObject, src_to);
1623             break;
1624         case CKO_PUBLIC_KEY:
1625             crv = stfk_CopyTokenPublicKey(destObject, src_to);
1626             break;
1627         case CKO_SECRET_KEY:
1628             crv = stfk_CopyTokenSecretKey(destObject, src_to);
1629             break;
1630         default:
1631             crv = CKR_DEVICE_ERROR; /* shouldn't happen unless we store more types
1632                                      * of token keys into our database. */
1633     }
1634 fail:
1635     return crv;
1636 }
1637 
1638 /*
1639  * copy the attributes from one object to another. Don't overwrite existing
1640  * attributes. NOTE: This is a pretty expensive operation since it
1641  * grabs the attribute locks for the src object for a *long* time.
1642  */
1643 CK_RV
sftk_CopyObject(SFTKObject * destObject,SFTKObject * srcObject)1644 sftk_CopyObject(SFTKObject *destObject, SFTKObject *srcObject)
1645 {
1646     SFTKAttribute *attribute;
1647     SFTKSessionObject *src_so = sftk_narrowToSessionObject(srcObject);
1648     unsigned int i;
1649 
1650     destObject->isFIPS = srcObject->isFIPS;
1651     if (src_so == NULL) {
1652         return sftk_CopyTokenObject(destObject, srcObject);
1653     }
1654 
1655     PZ_Lock(src_so->attributeLock);
1656     for (i = 0; i < src_so->hashSize; i++) {
1657         attribute = src_so->head[i];
1658         do {
1659             if (attribute) {
1660                 if (!sftk_hasAttribute(destObject, attribute->handle)) {
1661                     /* we need to copy the attribute since each attribute
1662                      * only has one set of link list pointers */
1663                     SFTKAttribute *newAttribute = sftk_NewAttribute(
1664                         destObject, sftk_attr_expand(&attribute->attrib));
1665                     if (newAttribute == NULL) {
1666                         PZ_Unlock(src_so->attributeLock);
1667                         return CKR_HOST_MEMORY;
1668                     }
1669                     sftk_AddAttribute(destObject, newAttribute);
1670                 }
1671                 attribute = attribute->next;
1672             }
1673         } while (attribute != NULL);
1674     }
1675     PZ_Unlock(src_so->attributeLock);
1676 
1677     return CKR_OK;
1678 }
1679 
1680 /*
1681  * ******************** Search Utilities *******************************
1682  */
1683 
1684 /* add an object to a search list */
1685 CK_RV
AddToList(SFTKObjectListElement ** list,SFTKObject * object)1686 AddToList(SFTKObjectListElement **list, SFTKObject *object)
1687 {
1688     SFTKObjectListElement *newElem =
1689         (SFTKObjectListElement *)PORT_Alloc(sizeof(SFTKObjectListElement));
1690 
1691     if (newElem == NULL)
1692         return CKR_HOST_MEMORY;
1693 
1694     newElem->next = *list;
1695     newElem->object = object;
1696     sftk_ReferenceObject(object);
1697 
1698     *list = newElem;
1699     return CKR_OK;
1700 }
1701 
1702 /* return true if the object matches the template */
1703 PRBool
sftk_objectMatch(SFTKObject * object,CK_ATTRIBUTE_PTR theTemplate,int count)1704 sftk_objectMatch(SFTKObject *object, CK_ATTRIBUTE_PTR theTemplate, int count)
1705 {
1706     int i;
1707 
1708     for (i = 0; i < count; i++) {
1709         SFTKAttribute *attribute = sftk_FindAttribute(object, theTemplate[i].type);
1710         if (attribute == NULL) {
1711             return PR_FALSE;
1712         }
1713         if (attribute->attrib.ulValueLen == theTemplate[i].ulValueLen) {
1714             if (PORT_Memcmp(attribute->attrib.pValue, theTemplate[i].pValue,
1715                             theTemplate[i].ulValueLen) == 0) {
1716                 sftk_FreeAttribute(attribute);
1717                 continue;
1718             }
1719         }
1720         sftk_FreeAttribute(attribute);
1721         return PR_FALSE;
1722     }
1723     return PR_TRUE;
1724 }
1725 
1726 /* search through all the objects in the queue and return the template matches
1727  * in the object list.
1728  */
1729 CK_RV
sftk_searchObjectList(SFTKSearchResults * search,SFTKObject ** head,unsigned int size,PZLock * lock,CK_ATTRIBUTE_PTR theTemplate,int count,PRBool isLoggedIn)1730 sftk_searchObjectList(SFTKSearchResults *search, SFTKObject **head,
1731                       unsigned int size, PZLock *lock, CK_ATTRIBUTE_PTR theTemplate,
1732                       int count, PRBool isLoggedIn)
1733 {
1734     unsigned int i;
1735     SFTKObject *object;
1736     CK_RV crv = CKR_OK;
1737 
1738     PZ_Lock(lock);
1739     for (i = 0; i < size; i++) {
1740         for (object = head[i]; object != NULL; object = object->next) {
1741             if (sftk_objectMatch(object, theTemplate, count)) {
1742                 /* don't return objects that aren't yet visible */
1743                 if ((!isLoggedIn) && sftk_isTrue(object, CKA_PRIVATE))
1744                     continue;
1745                 sftk_addHandle(search, object->handle);
1746             }
1747         }
1748     }
1749     PZ_Unlock(lock);
1750     return crv;
1751 }
1752 
1753 /*
1754  * free a single list element. Return the Next object in the list.
1755  */
1756 SFTKObjectListElement *
sftk_FreeObjectListElement(SFTKObjectListElement * objectList)1757 sftk_FreeObjectListElement(SFTKObjectListElement *objectList)
1758 {
1759     SFTKObjectListElement *ol = objectList->next;
1760 
1761     sftk_FreeObject(objectList->object);
1762     PORT_Free(objectList);
1763     return ol;
1764 }
1765 
1766 /* free an entire object list */
1767 void
sftk_FreeObjectList(SFTKObjectListElement * objectList)1768 sftk_FreeObjectList(SFTKObjectListElement *objectList)
1769 {
1770     SFTKObjectListElement *ol;
1771 
1772     for (ol = objectList; ol != NULL; ol = sftk_FreeObjectListElement(ol)) {
1773     }
1774 }
1775 
1776 /*
1777  * free a search structure
1778  */
1779 void
sftk_FreeSearch(SFTKSearchResults * search)1780 sftk_FreeSearch(SFTKSearchResults *search)
1781 {
1782     if (search->handles) {
1783         PORT_Free(search->handles);
1784     }
1785     PORT_Free(search);
1786 }
1787 
1788 /*
1789  * ******************** Session Utilities *******************************
1790  */
1791 
1792 /* update the sessions state based in it's flags and wether or not it's
1793  * logged in */
1794 void
sftk_update_state(SFTKSlot * slot,SFTKSession * session)1795 sftk_update_state(SFTKSlot *slot, SFTKSession *session)
1796 {
1797     if (slot->isLoggedIn) {
1798         if (slot->ssoLoggedIn) {
1799             session->info.state = CKS_RW_SO_FUNCTIONS;
1800         } else if (session->info.flags & CKF_RW_SESSION) {
1801             session->info.state = CKS_RW_USER_FUNCTIONS;
1802         } else {
1803             session->info.state = CKS_RO_USER_FUNCTIONS;
1804         }
1805     } else {
1806         if (session->info.flags & CKF_RW_SESSION) {
1807             session->info.state = CKS_RW_PUBLIC_SESSION;
1808         } else {
1809             session->info.state = CKS_RO_PUBLIC_SESSION;
1810         }
1811     }
1812 }
1813 
1814 /* update the state of all the sessions on a slot */
1815 void
sftk_update_all_states(SFTKSlot * slot)1816 sftk_update_all_states(SFTKSlot *slot)
1817 {
1818     unsigned int i;
1819     SFTKSession *session;
1820 
1821     for (i = 0; i < slot->sessHashSize; i++) {
1822         PZLock *lock = SFTK_SESSION_LOCK(slot, i);
1823         PZ_Lock(lock);
1824         for (session = slot->head[i]; session; session = session->next) {
1825             sftk_update_state(slot, session);
1826         }
1827         PZ_Unlock(lock);
1828     }
1829 }
1830 
1831 /*
1832  * context are cipher and digest contexts that are associated with a session
1833  */
1834 void
sftk_FreeContext(SFTKSessionContext * context)1835 sftk_FreeContext(SFTKSessionContext *context)
1836 {
1837     if (context->cipherInfo) {
1838         (*context->destroy)(context->cipherInfo, PR_TRUE);
1839     }
1840     if (context->hashInfo) {
1841         (*context->hashdestroy)(context->hashInfo, PR_TRUE);
1842     }
1843     if (context->key) {
1844         sftk_FreeObject(context->key);
1845         context->key = NULL;
1846     }
1847     PORT_Free(context);
1848 }
1849 
1850 /*
1851  * create a new nession. NOTE: The session handle is not set, and the
1852  * session is not added to the slot's session queue.
1853  */
1854 SFTKSession *
sftk_NewSession(CK_SLOT_ID slotID,CK_NOTIFY notify,CK_VOID_PTR pApplication,CK_FLAGS flags)1855 sftk_NewSession(CK_SLOT_ID slotID, CK_NOTIFY notify, CK_VOID_PTR pApplication,
1856                 CK_FLAGS flags)
1857 {
1858     SFTKSession *session;
1859     SFTKSlot *slot = sftk_SlotFromID(slotID, PR_FALSE);
1860 
1861     if (slot == NULL)
1862         return NULL;
1863 
1864     session = (SFTKSession *)PORT_Alloc(sizeof(SFTKSession));
1865     if (session == NULL)
1866         return NULL;
1867 
1868     session->next = session->prev = NULL;
1869     session->enc_context = NULL;
1870     session->hash_context = NULL;
1871     session->sign_context = NULL;
1872     session->search = NULL;
1873     session->objectIDCount = 1;
1874     session->objectLock = PZ_NewLock(nssILockObject);
1875     if (session->objectLock == NULL) {
1876         PORT_Free(session);
1877         return NULL;
1878     }
1879     session->objects[0] = NULL;
1880 
1881     session->slot = slot;
1882     session->notify = notify;
1883     session->appData = pApplication;
1884     session->info.flags = flags;
1885     session->info.slotID = slotID;
1886     session->info.ulDeviceError = 0;
1887     sftk_update_state(slot, session);
1888     /* no ops completed yet, so the last one couldn't be a FIPS op */
1889     session->lastOpWasFIPS = PR_FALSE;
1890     return session;
1891 }
1892 
1893 /* free all the data associated with a session. */
1894 void
sftk_DestroySession(SFTKSession * session)1895 sftk_DestroySession(SFTKSession *session)
1896 {
1897     SFTKObjectList *op, *next;
1898 
1899     /* clean out the attributes */
1900     /* since no one is referencing us, it's safe to walk the chain
1901      * without a lock */
1902     for (op = session->objects[0]; op != NULL; op = next) {
1903         next = op->next;
1904         /* paranoia */
1905         op->next = op->prev = NULL;
1906         sftk_DeleteObject(session, op->parent);
1907     }
1908     PZ_DestroyLock(session->objectLock);
1909     if (session->enc_context) {
1910         sftk_FreeContext(session->enc_context);
1911     }
1912     if (session->hash_context) {
1913         sftk_FreeContext(session->hash_context);
1914     }
1915     if (session->sign_context) {
1916         sftk_FreeContext(session->sign_context);
1917     }
1918     if (session->search) {
1919         sftk_FreeSearch(session->search);
1920     }
1921     PORT_Free(session);
1922 }
1923 
1924 /*
1925  * look up a session structure from a session handle
1926  * generate a reference to it.
1927  */
1928 SFTKSession *
sftk_SessionFromHandle(CK_SESSION_HANDLE handle)1929 sftk_SessionFromHandle(CK_SESSION_HANDLE handle)
1930 {
1931     SFTKSlot *slot = sftk_SlotFromSessionHandle(handle);
1932     SFTKSession *session;
1933     PZLock *lock;
1934 
1935     if (!slot)
1936         return NULL;
1937     lock = SFTK_SESSION_LOCK(slot, handle);
1938 
1939     PZ_Lock(lock);
1940     sftkqueue_find(session, handle, slot->head, slot->sessHashSize);
1941     PZ_Unlock(lock);
1942 
1943     return (session);
1944 }
1945 
1946 /*
1947  * release a reference to a session handle. This method of using SFTKSessions
1948  * is deprecated, but the pattern should be retained until a future effort
1949  * to refactor all SFTKSession users at once is completed.
1950  */
1951 void
sftk_FreeSession(SFTKSession * session)1952 sftk_FreeSession(SFTKSession *session)
1953 {
1954     return;
1955 }
1956 
1957 void
sftk_addHandle(SFTKSearchResults * search,CK_OBJECT_HANDLE handle)1958 sftk_addHandle(SFTKSearchResults *search, CK_OBJECT_HANDLE handle)
1959 {
1960     if (search->handles == NULL) {
1961         return;
1962     }
1963     if (search->size >= search->array_size) {
1964         search->array_size += NSC_SEARCH_BLOCK_SIZE;
1965         search->handles = (CK_OBJECT_HANDLE *)PORT_Realloc(search->handles,
1966                                                            sizeof(CK_OBJECT_HANDLE) * search->array_size);
1967         if (search->handles == NULL) {
1968             return;
1969         }
1970     }
1971     search->handles[search->size] = handle;
1972     search->size++;
1973 }
1974 
1975 static CK_RV
handleToClass(SFTKSlot * slot,CK_OBJECT_HANDLE handle,CK_OBJECT_CLASS * objClass)1976 handleToClass(SFTKSlot *slot, CK_OBJECT_HANDLE handle,
1977               CK_OBJECT_CLASS *objClass)
1978 {
1979     SFTKDBHandle *dbHandle = sftk_getDBForTokenObject(slot, handle);
1980     CK_ATTRIBUTE objClassTemplate;
1981     CK_RV crv;
1982 
1983     *objClass = CKO_DATA;
1984     objClassTemplate.type = CKA_CLASS;
1985     objClassTemplate.pValue = objClass;
1986     objClassTemplate.ulValueLen = sizeof(*objClass);
1987     crv = sftkdb_GetAttributeValue(dbHandle, handle, &objClassTemplate, 1);
1988     sftk_freeDB(dbHandle);
1989     return crv;
1990 }
1991 
1992 SFTKObject *
sftk_NewTokenObject(SFTKSlot * slot,SECItem * dbKey,CK_OBJECT_HANDLE handle)1993 sftk_NewTokenObject(SFTKSlot *slot, SECItem *dbKey, CK_OBJECT_HANDLE handle)
1994 {
1995     SFTKObject *object = NULL;
1996     PRBool hasLocks = PR_FALSE;
1997     CK_RV crv;
1998 
1999     object = sftk_GetObjectFromList(&hasLocks, PR_FALSE, &tokenObjectList, 0,
2000                                     PR_FALSE);
2001     if (object == NULL) {
2002         return NULL;
2003     }
2004 
2005     object->handle = handle;
2006     /* every object must have a class, if we can't get it, the object
2007      * doesn't exist */
2008     crv = handleToClass(slot, handle, &object->objclass);
2009     if (crv != CKR_OK) {
2010         goto loser;
2011     }
2012     object->slot = slot;
2013     object->isFIPS = sftk_isFIPS(slot->slotID);
2014     object->objectInfo = NULL;
2015     object->infoFree = NULL;
2016     if (!hasLocks) {
2017         object->refLock = PZ_NewLock(nssILockRefLock);
2018     }
2019     if (object->refLock == NULL) {
2020         goto loser;
2021     }
2022     object->refCount = 1;
2023 
2024     return object;
2025 loser:
2026     (void)sftk_DestroyObject(object);
2027     return NULL;
2028 }
2029 
2030 SFTKTokenObject *
sftk_convertSessionToToken(SFTKObject * obj)2031 sftk_convertSessionToToken(SFTKObject *obj)
2032 {
2033     SECItem *key;
2034     SFTKSessionObject *so = (SFTKSessionObject *)obj;
2035     SFTKTokenObject *to = sftk_narrowToTokenObject(obj);
2036     SECStatus rv;
2037 
2038     sftk_DestroySessionObjectData(so);
2039     PZ_DestroyLock(so->attributeLock);
2040     if (to == NULL) {
2041         return NULL;
2042     }
2043     sftk_tokenKeyLock(so->obj.slot);
2044     key = sftk_lookupTokenKeyByHandle(so->obj.slot, so->obj.handle);
2045     if (key == NULL) {
2046         sftk_tokenKeyUnlock(so->obj.slot);
2047         return NULL;
2048     }
2049     rv = SECITEM_CopyItem(NULL, &to->dbKey, key);
2050     sftk_tokenKeyUnlock(so->obj.slot);
2051     if (rv == SECFailure) {
2052         return NULL;
2053     }
2054 
2055     return to;
2056 }
2057 
2058 SFTKSessionObject *
sftk_narrowToSessionObject(SFTKObject * obj)2059 sftk_narrowToSessionObject(SFTKObject *obj)
2060 {
2061     return !sftk_isToken(obj->handle) ? (SFTKSessionObject *)obj : NULL;
2062 }
2063 
2064 SFTKTokenObject *
sftk_narrowToTokenObject(SFTKObject * obj)2065 sftk_narrowToTokenObject(SFTKObject *obj)
2066 {
2067     return sftk_isToken(obj->handle) ? (SFTKTokenObject *)obj : NULL;
2068 }
2069 
2070 /* Constant time helper functions */
2071 
2072 /* sftk_CKRVToMask returns, in constant time, a mask value of
2073  * all ones if rv == CKR_OK.  Otherwise it returns zero. */
2074 unsigned int
sftk_CKRVToMask(CK_RV rv)2075 sftk_CKRVToMask(CK_RV rv)
2076 {
2077     PR_STATIC_ASSERT(CKR_OK == 0);
2078     return ~PORT_CT_NOT_ZERO(rv);
2079 }
2080 
2081 /* sftk_CheckCBCPadding checks, in constant time, the padding validity and
2082  * accordingly sets the pad length. */
2083 CK_RV
sftk_CheckCBCPadding(CK_BYTE_PTR pBuf,unsigned int bufLen,unsigned int blockSize,unsigned int * outPadSize)2084 sftk_CheckCBCPadding(CK_BYTE_PTR pBuf, unsigned int bufLen,
2085                      unsigned int blockSize, unsigned int *outPadSize)
2086 {
2087     PORT_Assert(outPadSize);
2088 
2089     unsigned int padSize = (unsigned int)pBuf[bufLen - 1];
2090 
2091     /* If padSize <= blockSize, set goodPad to all-1s and all-0s otherwise.*/
2092     unsigned int goodPad = PORT_CT_DUPLICATE_MSB_TO_ALL(~(blockSize - padSize));
2093     /* padSize should not be 0 */
2094     goodPad &= PORT_CT_NOT_ZERO(padSize);
2095 
2096     unsigned int i;
2097     for (i = 0; i < blockSize; i++) {
2098         /* If i < padSize, set loopMask to all-1s and all-0s otherwise.*/
2099         unsigned int loopMask = PORT_CT_DUPLICATE_MSB_TO_ALL(~(padSize - 1 - i));
2100         /* Get the padding value (should be padSize) from buffer */
2101         unsigned int padVal = pBuf[bufLen - 1 - i];
2102         /* Update goodPad only if i < padSize */
2103         goodPad &= PORT_CT_SEL(loopMask, ~(padVal ^ padSize), goodPad);
2104     }
2105 
2106     /* If any of the final padding bytes had the wrong value, one or more
2107      * of the lower eight bits of |goodPad| will be cleared. We AND the
2108      * bottom 8 bits together and duplicate the result to all the bits. */
2109     goodPad &= goodPad >> 4;
2110     goodPad &= goodPad >> 2;
2111     goodPad &= goodPad >> 1;
2112     goodPad <<= sizeof(goodPad) * 8 - 1;
2113     goodPad = PORT_CT_DUPLICATE_MSB_TO_ALL(goodPad);
2114 
2115     /* Set outPadSize to padSize or 0 */
2116     *outPadSize = PORT_CT_SEL(goodPad, padSize, 0);
2117     /* Return OK if the pad is valid */
2118     return PORT_CT_SEL(goodPad, CKR_OK, CKR_ENCRYPTED_DATA_INVALID);
2119 }
2120 
2121 void
sftk_EncodeInteger(PRUint64 integer,CK_ULONG num_bits,CK_BBOOL littleEndian,CK_BYTE_PTR output,CK_ULONG_PTR output_len)2122 sftk_EncodeInteger(PRUint64 integer, CK_ULONG num_bits, CK_BBOOL littleEndian,
2123                    CK_BYTE_PTR output, CK_ULONG_PTR output_len)
2124 {
2125     if (output_len) {
2126         *output_len = (num_bits / 8);
2127     }
2128 
2129     PR_ASSERT(num_bits > 0 && num_bits <= 64 && (num_bits % 8) == 0);
2130 
2131     if (littleEndian == CK_TRUE) {
2132         for (size_t offset = 0; offset < num_bits / 8; offset++) {
2133             output[offset] = (unsigned char)((integer >> (offset * 8)) & 0xFF);
2134         }
2135     } else {
2136         for (size_t offset = 0; offset < num_bits / 8; offset++) {
2137             PRUint64 shift = num_bits - (offset + 1) * 8;
2138             output[offset] = (unsigned char)((integer >> shift) & 0xFF);
2139         }
2140     }
2141 }
2142 
2143 CK_FLAGS
sftk_AttributeToFlags(CK_ATTRIBUTE_TYPE op)2144 sftk_AttributeToFlags(CK_ATTRIBUTE_TYPE op)
2145 {
2146     CK_FLAGS flags = 0;
2147 
2148     switch (op) {
2149         case CKA_ENCRYPT:
2150             flags = CKF_ENCRYPT;
2151             break;
2152         case CKA_DECRYPT:
2153             flags = CKF_DECRYPT;
2154             break;
2155         case CKA_WRAP:
2156             flags = CKF_WRAP;
2157             break;
2158         case CKA_UNWRAP:
2159             flags = CKF_UNWRAP;
2160             break;
2161         case CKA_SIGN:
2162             flags = CKF_SIGN;
2163             break;
2164         case CKA_SIGN_RECOVER:
2165             flags = CKF_SIGN_RECOVER;
2166             break;
2167         case CKA_VERIFY:
2168             flags = CKF_VERIFY;
2169             break;
2170         case CKA_VERIFY_RECOVER:
2171             flags = CKF_VERIFY_RECOVER;
2172             break;
2173         case CKA_DERIVE:
2174             flags = CKF_DERIVE;
2175             break;
2176         /* fake attribute to select digesting */
2177         case CKA_DIGEST:
2178             flags = CKF_DIGEST;
2179             break;
2180         case CKA_NSS_MESSAGE | CKA_ENCRYPT:
2181             flags = CKF_MESSAGE_ENCRYPT;
2182             break;
2183         case CKA_NSS_MESSAGE | CKA_DECRYPT:
2184             flags = CKF_MESSAGE_DECRYPT;
2185             break;
2186         case CKA_NSS_MESSAGE | CKA_SIGN:
2187             flags = CKF_MESSAGE_SIGN;
2188             break;
2189         case CKA_NSS_MESSAGE | CKA_VERIFY:
2190             flags = CKF_MESSAGE_VERIFY;
2191             break;
2192         default:
2193             break;
2194     }
2195     return flags;
2196 }
2197 
2198 #ifdef NSS_HAS_FIPS_INDICATORS
2199 /* sigh, we probably need a version of this in secutil so that both
2200  * softoken and NSS can use it */
2201 static SECOidTag
sftk_quickGetECCCurveOid(SFTKObject * source)2202 sftk_quickGetECCCurveOid(SFTKObject *source)
2203 {
2204     SFTKAttribute *attribute = sftk_FindAttribute(source, CKA_EC_PARAMS);
2205     unsigned char *encoded;
2206     int len;
2207     SECItem oid;
2208     SECOidTag tag;
2209 
2210     if (attribute == NULL) {
2211         return SEC_OID_UNKNOWN;
2212     }
2213     encoded = attribute->attrib.pValue;
2214     len = attribute->attrib.ulValueLen;
2215     if ((len < 2) || (encoded[0] != SEC_ASN1_OBJECT_ID) ||
2216         (len != encoded[1] + 2)) {
2217         sftk_FreeAttribute(attribute);
2218         return SEC_OID_UNKNOWN;
2219     }
2220     oid.data = encoded + 2;
2221     oid.len = len - 2;
2222     tag = SECOID_FindOIDTag(&oid);
2223     sftk_FreeAttribute(attribute);
2224     return tag;
2225 }
2226 
2227 /* This function currently only returns valid lengths for
2228  * FIPS approved ECC curves. If we want to make this generic
2229  * in the future, that Curve determination can be done in
2230  * the sftk_handleSpecial. Since it's currently only used
2231  * in FIPS indicators, it's currently only compiled with
2232  * the FIPS indicator code */
2233 static int
sftk_getKeyLength(SFTKObject * source)2234 sftk_getKeyLength(SFTKObject *source)
2235 {
2236     CK_KEY_TYPE keyType = CK_INVALID_HANDLE;
2237     CK_ATTRIBUTE_TYPE keyAttribute;
2238     CK_ULONG keyLength = 0;
2239     SFTKAttribute *attribute;
2240     CK_RV crv;
2241 
2242     /* If we don't have a key, then it doesn't have a length.
2243      * this may be OK (say we are hashing). The mech info will
2244      * sort this out because algorithms which expect no keys
2245      * will accept zero length for the keys */
2246     if (source == NULL) {
2247         return 0;
2248     }
2249 
2250     crv = sftk_GetULongAttribute(source, CKA_KEY_TYPE, &keyType);
2251     if (crv != CKR_OK) {
2252         /* sometimes we're passed a data object, in that case the
2253          * key length is CKA_VALUE, which is the default */
2254         keyType = CKK_INVALID_KEY_TYPE;
2255     }
2256     if (keyType == CKK_EC) {
2257         SECOidTag curve = sftk_quickGetECCCurveOid(source);
2258         switch (curve) {
2259             case SEC_OID_CURVE25519:
2260                 /* change when we start algorithm testing on curve25519 */
2261                 return 0;
2262             case SEC_OID_SECG_EC_SECP256R1:
2263                 return 256;
2264             case SEC_OID_SECG_EC_SECP384R1:
2265                 return 384;
2266             case SEC_OID_SECG_EC_SECP521R1:
2267                 /* this is a lie, but it makes the table easier. We don't
2268                  * have to have a double entry for every ECC mechanism */
2269                 return 512;
2270             default:
2271                 break;
2272         }
2273         /* other curves aren't NIST approved, returning 0 will cause these
2274          * curves to fail FIPS length criteria */
2275         return 0;
2276     }
2277 
2278     switch (keyType) {
2279         case CKK_RSA:
2280             keyAttribute = CKA_MODULUS;
2281             break;
2282         case CKK_DSA:
2283         case CKK_DH:
2284             keyAttribute = CKA_PRIME;
2285             break;
2286         default:
2287             keyAttribute = CKA_VALUE;
2288             break;
2289     }
2290     attribute = sftk_FindAttribute(source, keyAttribute);
2291     if (attribute) {
2292         keyLength = attribute->attrib.ulValueLen * 8;
2293         sftk_FreeAttribute(attribute);
2294     }
2295     return keyLength;
2296 }
2297 
2298 /*
2299  * handle specialized FIPS semantics that are too complicated to
2300  * handle with just a table. NOTE: this means any additional semantics
2301  * would have to be coded here before they can be added to the table */
2302 static PRBool
sftk_handleSpecial(SFTKSlot * slot,CK_MECHANISM * mech,SFTKFIPSAlgorithmList * mechInfo,SFTKObject * source)2303 sftk_handleSpecial(SFTKSlot *slot, CK_MECHANISM *mech,
2304                    SFTKFIPSAlgorithmList *mechInfo, SFTKObject *source)
2305 {
2306     switch (mechInfo->special) {
2307         case SFTKFIPSDH: {
2308             SECItem dhPrime;
2309             const SECItem *dhSubPrime;
2310             CK_RV crv = sftk_Attribute2SecItem(NULL, &dhPrime,
2311                                                source, CKA_PRIME);
2312             if (crv != CKR_OK) {
2313                 return PR_FALSE;
2314             }
2315             dhSubPrime = sftk_VerifyDH_Prime(&dhPrime);
2316             SECITEM_ZfreeItem(&dhPrime, PR_FALSE);
2317             return (dhSubPrime) ? PR_TRUE : PR_FALSE;
2318         }
2319         case SFTKFIPSNone:
2320             return PR_FALSE;
2321         case SFTKFIPSECC:
2322             /* we've already handled the curve selection in the 'getlength'
2323           * function */
2324             return PR_TRUE;
2325         case SFTKFIPSAEAD: {
2326             if (mech->ulParameterLen == 0) {
2327                 /* AEAD ciphers are only in FIPS mode if we are using the
2328                  * MESSAGE interface. This takes an empty parameter
2329                  * in the init function */
2330                 return PR_TRUE;
2331             }
2332             return PR_FALSE;
2333         }
2334         default:
2335             break;
2336     }
2337     /* if we didn't understand the special processing, mark it non-fips */
2338     return PR_FALSE;
2339 }
2340 #endif
2341 
2342 PRBool
sftk_operationIsFIPS(SFTKSlot * slot,CK_MECHANISM * mech,CK_ATTRIBUTE_TYPE op,SFTKObject * source)2343 sftk_operationIsFIPS(SFTKSlot *slot, CK_MECHANISM *mech, CK_ATTRIBUTE_TYPE op,
2344                      SFTKObject *source)
2345 {
2346 #ifndef NSS_HAS_FIPS_INDICATORS
2347     return PR_FALSE;
2348 #else
2349     int i;
2350     CK_FLAGS opFlags;
2351     CK_ULONG keyLength;
2352 
2353     /* handle all the quick stuff first */
2354     if (!sftk_isFIPS(slot->slotID)) {
2355         return PR_FALSE;
2356     }
2357     if (source && !source->isFIPS) {
2358         return PR_FALSE;
2359     }
2360     if (mech == NULL) {
2361         return PR_FALSE;
2362     }
2363 
2364     /* now get the calculated values */
2365     opFlags = sftk_AttributeToFlags(op);
2366     if (opFlags == 0) {
2367         return PR_FALSE;
2368     }
2369     keyLength = sftk_getKeyLength(source);
2370 
2371     /* check against our algorithm array */
2372     for (i = 0; i < SFTK_NUMBER_FIPS_ALGORITHMS; i++) {
2373         SFTKFIPSAlgorithmList *mechs = &sftk_fips_mechs[i];
2374         /* if we match the number of records exactly, then we are an
2375          * approved algorithm in the approved mode with an approved key */
2376         if (((mech->mechanism == mechs->type) &&
2377              (opFlags == (mechs->info.flags & opFlags)) &&
2378              (keyLength <= mechs->info.ulMaxKeySize) &&
2379              (keyLength >= mechs->info.ulMinKeySize) &&
2380              ((keyLength - mechs->info.ulMinKeySize) % mechs->step) == 0) &&
2381             ((mechs->special == SFTKFIPSNone) ||
2382              sftk_handleSpecial(slot, mech, mechs, source))) {
2383             return PR_TRUE;
2384         }
2385     }
2386     return PR_FALSE;
2387 #endif
2388 }
2389