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 #include "pkcs11.h"
6 
7 #ifndef DEVM_H
8 #include "devm.h"
9 #endif /* DEVM_H */
10 
11 #ifndef CKHELPER_H
12 #include "ckhelper.h"
13 #endif /* CKHELPER_H */
14 
15 #include "pk11func.h"
16 #include "dev3hack.h"
17 #include "secerr.h"
18 
19 extern const NSSError NSS_ERROR_NOT_FOUND;
20 extern const NSSError NSS_ERROR_INVALID_ARGUMENT;
21 extern const NSSError NSS_ERROR_PKCS11;
22 
23 /* The number of object handles to grab during each call to C_FindObjects */
24 #define OBJECT_STACK_SIZE 16
25 
26 NSS_IMPLEMENT PRStatus
nssToken_Destroy(NSSToken * tok)27 nssToken_Destroy(
28     NSSToken *tok)
29 {
30     if (tok) {
31         if (PR_ATOMIC_DECREMENT(&tok->base.refCount) == 0) {
32             PK11_FreeSlot(tok->pk11slot);
33             PZ_DestroyLock(tok->base.lock);
34             nssTokenObjectCache_Destroy(tok->cache);
35             (void)nssSlot_Destroy(tok->slot);
36             return nssArena_Destroy(tok->base.arena);
37         }
38     }
39     return PR_SUCCESS;
40 }
41 
42 NSS_IMPLEMENT void
nssToken_Remove(NSSToken * tok)43 nssToken_Remove(
44     NSSToken *tok)
45 {
46     nssTokenObjectCache_Clear(tok->cache);
47 }
48 
49 NSS_IMPLEMENT NSSToken *
nssToken_AddRef(NSSToken * tok)50 nssToken_AddRef(
51     NSSToken *tok)
52 {
53     PR_ATOMIC_INCREMENT(&tok->base.refCount);
54     return tok;
55 }
56 
57 NSS_IMPLEMENT NSSSlot *
nssToken_GetSlot(NSSToken * tok)58 nssToken_GetSlot(
59     NSSToken *tok)
60 {
61     return nssSlot_AddRef(tok->slot);
62 }
63 
64 NSS_IMPLEMENT void *
nssToken_GetCryptokiEPV(NSSToken * token)65 nssToken_GetCryptokiEPV(
66     NSSToken *token)
67 {
68     return nssSlot_GetCryptokiEPV(token->slot);
69 }
70 
71 NSS_IMPLEMENT nssSession *
nssToken_GetDefaultSession(NSSToken * token)72 nssToken_GetDefaultSession(
73     NSSToken *token)
74 {
75     return token->defaultSession;
76 }
77 
78 NSS_IMPLEMENT NSSUTF8 *
nssToken_GetName(NSSToken * tok)79 nssToken_GetName(
80     NSSToken *tok)
81 {
82     if (tok == NULL) {
83         return "";
84     }
85     if (tok->base.name[0] == 0) {
86         (void)nssSlot_IsTokenPresent(tok->slot);
87     }
88     return tok->base.name;
89 }
90 
91 NSS_IMPLEMENT NSSUTF8 *
NSSToken_GetName(NSSToken * token)92 NSSToken_GetName(
93     NSSToken *token)
94 {
95     return nssToken_GetName(token);
96 }
97 
98 NSS_IMPLEMENT PRBool
nssToken_IsLoginRequired(NSSToken * token)99 nssToken_IsLoginRequired(
100     NSSToken *token)
101 {
102     return (token->ckFlags & CKF_LOGIN_REQUIRED);
103 }
104 
105 NSS_IMPLEMENT PRBool
nssToken_NeedsPINInitialization(NSSToken * token)106 nssToken_NeedsPINInitialization(
107     NSSToken *token)
108 {
109     return (!(token->ckFlags & CKF_USER_PIN_INITIALIZED));
110 }
111 
112 NSS_IMPLEMENT PRStatus
nssToken_DeleteStoredObject(nssCryptokiObject * instance)113 nssToken_DeleteStoredObject(
114     nssCryptokiObject *instance)
115 {
116     CK_RV ckrv;
117     PRStatus status;
118     PRBool createdSession = PR_FALSE;
119     NSSToken *token = instance->token;
120     nssSession *session = NULL;
121     void *epv = nssToken_GetCryptokiEPV(instance->token);
122     if (token->cache) {
123         nssTokenObjectCache_RemoveObject(token->cache, instance);
124     }
125     if (instance->isTokenObject) {
126         if (token->defaultSession &&
127             nssSession_IsReadWrite(token->defaultSession)) {
128             session = token->defaultSession;
129         } else {
130             session = nssSlot_CreateSession(token->slot, NULL, PR_TRUE);
131             createdSession = PR_TRUE;
132         }
133     }
134     if (session == NULL) {
135         return PR_FAILURE;
136     }
137     nssSession_EnterMonitor(session);
138     ckrv = CKAPI(epv)->C_DestroyObject(session->handle, instance->handle);
139     nssSession_ExitMonitor(session);
140     if (createdSession) {
141         nssSession_Destroy(session);
142     }
143     status = PR_SUCCESS;
144     if (ckrv != CKR_OK) {
145         status = PR_FAILURE;
146         /* use the error stack to pass the PKCS #11 error out  */
147         nss_SetError(ckrv);
148         nss_SetError(NSS_ERROR_PKCS11);
149     }
150     return status;
151 }
152 
153 static nssCryptokiObject *
import_object(NSSToken * tok,nssSession * sessionOpt,CK_ATTRIBUTE_PTR objectTemplate,CK_ULONG otsize)154 import_object(
155     NSSToken *tok,
156     nssSession *sessionOpt,
157     CK_ATTRIBUTE_PTR objectTemplate,
158     CK_ULONG otsize)
159 {
160     nssSession *session = NULL;
161     PRBool createdSession = PR_FALSE;
162     nssCryptokiObject *object = NULL;
163     CK_OBJECT_HANDLE handle;
164     CK_RV ckrv;
165     void *epv = nssToken_GetCryptokiEPV(tok);
166     if (nssCKObject_IsTokenObjectTemplate(objectTemplate, otsize)) {
167         if (sessionOpt) {
168             if (!nssSession_IsReadWrite(sessionOpt)) {
169                 nss_SetError(NSS_ERROR_INVALID_ARGUMENT);
170                 return NULL;
171             }
172             session = sessionOpt;
173         } else if (tok->defaultSession &&
174                    nssSession_IsReadWrite(tok->defaultSession)) {
175             session = tok->defaultSession;
176         } else {
177             session = nssSlot_CreateSession(tok->slot, NULL, PR_TRUE);
178             createdSession = PR_TRUE;
179         }
180     } else {
181         session = (sessionOpt) ? sessionOpt : tok->defaultSession;
182     }
183     if (session == NULL) {
184         nss_SetError(NSS_ERROR_INVALID_ARGUMENT);
185         return NULL;
186     }
187     nssSession_EnterMonitor(session);
188     ckrv = CKAPI(epv)->C_CreateObject(session->handle,
189                                       objectTemplate, otsize,
190                                       &handle);
191     nssSession_ExitMonitor(session);
192     if (ckrv == CKR_OK) {
193         object = nssCryptokiObject_Create(tok, session, handle);
194     } else {
195         nss_SetError(ckrv);
196         nss_SetError(NSS_ERROR_PKCS11);
197     }
198     if (createdSession) {
199         nssSession_Destroy(session);
200     }
201     return object;
202 }
203 
204 static nssCryptokiObject **
create_objects_from_handles(NSSToken * tok,nssSession * session,CK_OBJECT_HANDLE * handles,PRUint32 numH)205 create_objects_from_handles(
206     NSSToken *tok,
207     nssSession *session,
208     CK_OBJECT_HANDLE *handles,
209     PRUint32 numH)
210 {
211     nssCryptokiObject **objects;
212     objects = nss_ZNEWARRAY(NULL, nssCryptokiObject *, numH + 1);
213     if (objects) {
214         PRInt32 i;
215         for (i = 0; i < (PRInt32)numH; i++) {
216             objects[i] = nssCryptokiObject_Create(tok, session, handles[i]);
217             if (!objects[i]) {
218                 for (--i; i > 0; --i) {
219                     nssCryptokiObject_Destroy(objects[i]);
220                 }
221                 nss_ZFreeIf(objects);
222                 objects = NULL;
223                 break;
224             }
225         }
226     }
227     return objects;
228 }
229 
230 static nssCryptokiObject **
find_objects(NSSToken * tok,nssSession * sessionOpt,CK_ATTRIBUTE_PTR obj_template,CK_ULONG otsize,PRUint32 maximumOpt,PRStatus * statusOpt)231 find_objects(
232     NSSToken *tok,
233     nssSession *sessionOpt,
234     CK_ATTRIBUTE_PTR obj_template,
235     CK_ULONG otsize,
236     PRUint32 maximumOpt,
237     PRStatus *statusOpt)
238 {
239     CK_RV ckrv = CKR_OK;
240     CK_ULONG count;
241     CK_OBJECT_HANDLE *objectHandles = NULL;
242     CK_OBJECT_HANDLE staticObjects[OBJECT_STACK_SIZE];
243     PRUint32 arraySize, numHandles;
244     void *epv = nssToken_GetCryptokiEPV(tok);
245     nssCryptokiObject **objects;
246     nssSession *session = (sessionOpt) ? sessionOpt : tok->defaultSession;
247 
248     /* Don't ask the module to use an invalid session handle. */
249     if (!session || session->handle == CK_INVALID_HANDLE) {
250         ckrv = CKR_SESSION_HANDLE_INVALID;
251         goto loser;
252     }
253 
254     /* the arena is only for the array of object handles */
255     if (maximumOpt > 0) {
256         arraySize = maximumOpt;
257     } else {
258         arraySize = OBJECT_STACK_SIZE;
259     }
260     numHandles = 0;
261     if (arraySize <= OBJECT_STACK_SIZE) {
262         objectHandles = staticObjects;
263     } else {
264         objectHandles = nss_ZNEWARRAY(NULL, CK_OBJECT_HANDLE, arraySize);
265     }
266     if (!objectHandles) {
267         ckrv = CKR_HOST_MEMORY;
268         goto loser;
269     }
270     nssSession_EnterMonitor(session); /* ==== session lock === */
271     /* Initialize the find with the template */
272     ckrv = CKAPI(epv)->C_FindObjectsInit(session->handle,
273                                          obj_template, otsize);
274     if (ckrv != CKR_OK) {
275         nssSession_ExitMonitor(session);
276         goto loser;
277     }
278     while (PR_TRUE) {
279         /* Issue the find for up to arraySize - numHandles objects */
280         ckrv = CKAPI(epv)->C_FindObjects(session->handle,
281                                          objectHandles + numHandles,
282                                          arraySize - numHandles,
283                                          &count);
284         if (ckrv != CKR_OK) {
285             nssSession_ExitMonitor(session);
286             goto loser;
287         }
288         /* bump the number of found objects */
289         numHandles += count;
290         if (maximumOpt > 0 || numHandles < arraySize) {
291             /* When a maximum is provided, the search is done all at once,
292              * so the search is finished.  If the number returned was less
293              * than the number sought, the search is finished.
294              */
295             break;
296         }
297         /* the array is filled, double it and continue */
298         arraySize *= 2;
299         if (objectHandles == staticObjects) {
300             objectHandles = nss_ZNEWARRAY(NULL, CK_OBJECT_HANDLE, arraySize);
301             if (objectHandles) {
302                 PORT_Memcpy(objectHandles, staticObjects,
303                             OBJECT_STACK_SIZE * sizeof(objectHandles[1]));
304             }
305         } else {
306             objectHandles = nss_ZREALLOCARRAY(objectHandles,
307                                               CK_OBJECT_HANDLE,
308                                               arraySize);
309         }
310         if (!objectHandles) {
311             nssSession_ExitMonitor(session);
312             ckrv = CKR_HOST_MEMORY;
313             goto loser;
314         }
315     }
316     ckrv = CKAPI(epv)->C_FindObjectsFinal(session->handle);
317     nssSession_ExitMonitor(session); /* ==== end session lock === */
318     if (ckrv != CKR_OK) {
319         goto loser;
320     }
321     if (numHandles > 0) {
322         objects = create_objects_from_handles(tok, session,
323                                               objectHandles, numHandles);
324     } else {
325         nss_SetError(NSS_ERROR_NOT_FOUND);
326         objects = NULL;
327     }
328     if (objectHandles && objectHandles != staticObjects) {
329         nss_ZFreeIf(objectHandles);
330     }
331     if (statusOpt)
332         *statusOpt = PR_SUCCESS;
333     return objects;
334 loser:
335     if (objectHandles && objectHandles != staticObjects) {
336         nss_ZFreeIf(objectHandles);
337     }
338     /*
339      * These errors should be treated the same as if the objects just weren't
340      * found..
341      */
342     if ((ckrv == CKR_ATTRIBUTE_TYPE_INVALID) ||
343         (ckrv == CKR_ATTRIBUTE_VALUE_INVALID) ||
344         (ckrv == CKR_DATA_INVALID) ||
345         (ckrv == CKR_DATA_LEN_RANGE) ||
346         (ckrv == CKR_FUNCTION_NOT_SUPPORTED) ||
347         (ckrv == CKR_TEMPLATE_INCOMPLETE) ||
348         (ckrv == CKR_TEMPLATE_INCONSISTENT)) {
349 
350         nss_SetError(NSS_ERROR_NOT_FOUND);
351         if (statusOpt)
352             *statusOpt = PR_SUCCESS;
353     } else {
354         nss_SetError(ckrv);
355         nss_SetError(NSS_ERROR_PKCS11);
356         if (statusOpt)
357             *statusOpt = PR_FAILURE;
358     }
359     return (nssCryptokiObject **)NULL;
360 }
361 
362 NSS_IMPLEMENT nssCryptokiObject **
nssToken_FindObjectsByTemplate(NSSToken * token,nssSession * sessionOpt,CK_ATTRIBUTE_PTR obj_template,CK_ULONG otsize,PRUint32 maximumOpt,PRStatus * statusOpt)363 nssToken_FindObjectsByTemplate(
364     NSSToken *token,
365     nssSession *sessionOpt,
366     CK_ATTRIBUTE_PTR obj_template,
367     CK_ULONG otsize,
368     PRUint32 maximumOpt,
369     PRStatus *statusOpt)
370 {
371     CK_OBJECT_CLASS objclass = (CK_OBJECT_CLASS)-1;
372     nssCryptokiObject **objects = NULL;
373     PRUint32 i;
374 
375     if (!token) {
376         PORT_SetError(SEC_ERROR_NO_TOKEN);
377         if (statusOpt)
378             *statusOpt = PR_FAILURE;
379         return NULL;
380     }
381     for (i = 0; i < otsize; i++) {
382         if (obj_template[i].type == CKA_CLASS) {
383             objclass = *(CK_OBJECT_CLASS *)obj_template[i].pValue;
384             break;
385         }
386     }
387     PR_ASSERT(i < otsize);
388     if (i == otsize) {
389         PORT_SetError(SEC_ERROR_LIBRARY_FAILURE);
390         if (statusOpt)
391             *statusOpt = PR_FAILURE;
392         return NULL;
393     }
394     /* If these objects are being cached, try looking there first */
395     if (token->cache &&
396         nssTokenObjectCache_HaveObjectClass(token->cache, objclass)) {
397         PRStatus status;
398         objects = nssTokenObjectCache_FindObjectsByTemplate(token->cache,
399                                                             objclass,
400                                                             obj_template,
401                                                             otsize,
402                                                             maximumOpt,
403                                                             &status);
404         if (status == PR_SUCCESS) {
405             if (statusOpt)
406                 *statusOpt = status;
407             return objects;
408         }
409     }
410     /* Either they are not cached, or cache failed; look on token. */
411     objects = find_objects(token, sessionOpt,
412                            obj_template, otsize,
413                            maximumOpt, statusOpt);
414     return objects;
415 }
416 
417 extern const NSSError NSS_ERROR_INVALID_CERTIFICATE;
418 
419 NSS_IMPLEMENT nssCryptokiObject *
nssToken_ImportCertificate(NSSToken * tok,nssSession * sessionOpt,NSSCertificateType certType,NSSItem * id,const NSSUTF8 * nickname,NSSDER * encoding,NSSDER * issuer,NSSDER * subject,NSSDER * serial,NSSASCII7 * email,PRBool asTokenObject)420 nssToken_ImportCertificate(
421     NSSToken *tok,
422     nssSession *sessionOpt,
423     NSSCertificateType certType,
424     NSSItem *id,
425     const NSSUTF8 *nickname,
426     NSSDER *encoding,
427     NSSDER *issuer,
428     NSSDER *subject,
429     NSSDER *serial,
430     NSSASCII7 *email,
431     PRBool asTokenObject)
432 {
433     PRStatus status;
434     CK_CERTIFICATE_TYPE cert_type;
435     CK_ATTRIBUTE_PTR attr;
436     CK_ATTRIBUTE cert_tmpl[10];
437     CK_ULONG ctsize;
438     nssTokenSearchType searchType;
439     nssCryptokiObject *rvObject = NULL;
440 
441     if (!tok) {
442         PORT_SetError(SEC_ERROR_NO_TOKEN);
443         return NULL;
444     }
445     if (certType == NSSCertificateType_PKIX) {
446         cert_type = CKC_X_509;
447     } else {
448         return (nssCryptokiObject *)NULL;
449     }
450     NSS_CK_TEMPLATE_START(cert_tmpl, attr, ctsize);
451     if (asTokenObject) {
452         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true);
453         searchType = nssTokenSearchType_TokenOnly;
454     } else {
455         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false);
456         searchType = nssTokenSearchType_SessionOnly;
457     }
458     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert);
459     NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_CERTIFICATE_TYPE, cert_type);
460     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ID, id);
461     NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_LABEL, nickname);
462     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_VALUE, encoding);
463     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ISSUER, issuer);
464     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SUBJECT, subject);
465     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SERIAL_NUMBER, serial);
466     if (email) {
467         NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_NSS_EMAIL, email);
468     }
469     NSS_CK_TEMPLATE_FINISH(cert_tmpl, attr, ctsize);
470     /* see if the cert is already there */
471     rvObject = nssToken_FindCertificateByIssuerAndSerialNumber(tok,
472                                                                sessionOpt,
473                                                                issuer,
474                                                                serial,
475                                                                searchType,
476                                                                NULL);
477     if (rvObject) {
478         NSSItem existingDER;
479         NSSSlot *slot = nssToken_GetSlot(tok);
480         nssSession *session = nssSlot_CreateSession(slot, NULL, PR_TRUE);
481         if (!session) {
482             nssCryptokiObject_Destroy(rvObject);
483             nssSlot_Destroy(slot);
484             return (nssCryptokiObject *)NULL;
485         }
486         /* Reject any attempt to import a new cert that has the same
487          * issuer/serial as an existing cert, but does not have the
488          * same encoding
489          */
490         NSS_CK_TEMPLATE_START(cert_tmpl, attr, ctsize);
491         NSS_CK_SET_ATTRIBUTE_NULL(attr, CKA_VALUE);
492         NSS_CK_TEMPLATE_FINISH(cert_tmpl, attr, ctsize);
493         status = nssCKObject_GetAttributes(rvObject->handle,
494                                            cert_tmpl, ctsize, NULL,
495                                            session, slot);
496         NSS_CK_ATTRIBUTE_TO_ITEM(cert_tmpl, &existingDER);
497         if (status == PR_SUCCESS) {
498             if (!nssItem_Equal(encoding, &existingDER, NULL)) {
499                 nss_SetError(NSS_ERROR_INVALID_CERTIFICATE);
500                 status = PR_FAILURE;
501             }
502             nss_ZFreeIf(existingDER.data);
503         }
504         if (status == PR_FAILURE) {
505             nssCryptokiObject_Destroy(rvObject);
506             nssSession_Destroy(session);
507             nssSlot_Destroy(slot);
508             return (nssCryptokiObject *)NULL;
509         }
510         /* according to PKCS#11, label, ID, issuer, and serial number
511          * may change after the object has been created.  For PKIX, the
512          * last two attributes can't change, so for now we'll only worry
513          * about the first two.
514          */
515         NSS_CK_TEMPLATE_START(cert_tmpl, attr, ctsize);
516         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ID, id);
517         if (!rvObject->label && nickname) {
518             NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_LABEL, nickname);
519         }
520         NSS_CK_TEMPLATE_FINISH(cert_tmpl, attr, ctsize);
521         /* reset the mutable attributes on the token */
522         nssCKObject_SetAttributes(rvObject->handle,
523                                   cert_tmpl, ctsize,
524                                   session, slot);
525         if (!rvObject->label && nickname) {
526             rvObject->label = nssUTF8_Duplicate(nickname, NULL);
527         }
528         nssSession_Destroy(session);
529         nssSlot_Destroy(slot);
530     } else {
531         /* Import the certificate onto the token */
532         rvObject = import_object(tok, sessionOpt, cert_tmpl, ctsize);
533     }
534     if (rvObject && tok->cache) {
535         /* The cache will overwrite the attributes if the object already
536          * exists.
537          */
538         nssTokenObjectCache_ImportObject(tok->cache, rvObject,
539                                          CKO_CERTIFICATE,
540                                          cert_tmpl, ctsize);
541     }
542     return rvObject;
543 }
544 
545 /* traverse all objects of the given class - this should only happen
546  * if the token has been marked as "traversable"
547  */
548 NSS_IMPLEMENT nssCryptokiObject **
nssToken_FindObjects(NSSToken * token,nssSession * sessionOpt,CK_OBJECT_CLASS objclass,nssTokenSearchType searchType,PRUint32 maximumOpt,PRStatus * statusOpt)549 nssToken_FindObjects(
550     NSSToken *token,
551     nssSession *sessionOpt,
552     CK_OBJECT_CLASS objclass,
553     nssTokenSearchType searchType,
554     PRUint32 maximumOpt,
555     PRStatus *statusOpt)
556 {
557     CK_ATTRIBUTE_PTR attr;
558     CK_ATTRIBUTE obj_template[2];
559     CK_ULONG obj_size;
560     nssCryptokiObject **objects;
561     NSS_CK_TEMPLATE_START(obj_template, attr, obj_size);
562     /* Set the search to token/session only if provided */
563     if (searchType == nssTokenSearchType_SessionOnly) {
564         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false);
565     } else if (searchType == nssTokenSearchType_TokenOnly ||
566                searchType == nssTokenSearchType_TokenForced) {
567         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true);
568     }
569     NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_CLASS, objclass);
570     NSS_CK_TEMPLATE_FINISH(obj_template, attr, obj_size);
571 
572     if (searchType == nssTokenSearchType_TokenForced) {
573         objects = find_objects(token, sessionOpt,
574                                obj_template, obj_size,
575                                maximumOpt, statusOpt);
576     } else {
577         objects = nssToken_FindObjectsByTemplate(token, sessionOpt,
578                                                  obj_template, obj_size,
579                                                  maximumOpt, statusOpt);
580     }
581     return objects;
582 }
583 
584 NSS_IMPLEMENT nssCryptokiObject **
nssToken_FindCertificatesBySubject(NSSToken * token,nssSession * sessionOpt,NSSDER * subject,nssTokenSearchType searchType,PRUint32 maximumOpt,PRStatus * statusOpt)585 nssToken_FindCertificatesBySubject(
586     NSSToken *token,
587     nssSession *sessionOpt,
588     NSSDER *subject,
589     nssTokenSearchType searchType,
590     PRUint32 maximumOpt,
591     PRStatus *statusOpt)
592 {
593     CK_ATTRIBUTE_PTR attr;
594     CK_ATTRIBUTE subj_template[3];
595     CK_ULONG stsize;
596     nssCryptokiObject **objects;
597     NSS_CK_TEMPLATE_START(subj_template, attr, stsize);
598     /* Set the search to token/session only if provided */
599     if (searchType == nssTokenSearchType_SessionOnly) {
600         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false);
601     } else if (searchType == nssTokenSearchType_TokenOnly) {
602         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true);
603     }
604     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert);
605     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SUBJECT, subject);
606     NSS_CK_TEMPLATE_FINISH(subj_template, attr, stsize);
607     /* now locate the token certs matching this template */
608     objects = nssToken_FindObjectsByTemplate(token, sessionOpt,
609                                              subj_template, stsize,
610                                              maximumOpt, statusOpt);
611     return objects;
612 }
613 
614 NSS_IMPLEMENT nssCryptokiObject **
nssToken_FindCertificatesByNickname(NSSToken * token,nssSession * sessionOpt,const NSSUTF8 * name,nssTokenSearchType searchType,PRUint32 maximumOpt,PRStatus * statusOpt)615 nssToken_FindCertificatesByNickname(
616     NSSToken *token,
617     nssSession *sessionOpt,
618     const NSSUTF8 *name,
619     nssTokenSearchType searchType,
620     PRUint32 maximumOpt,
621     PRStatus *statusOpt)
622 {
623     CK_ATTRIBUTE_PTR attr;
624     CK_ATTRIBUTE nick_template[3];
625     CK_ULONG ntsize;
626     nssCryptokiObject **objects;
627     NSS_CK_TEMPLATE_START(nick_template, attr, ntsize);
628     NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_LABEL, name);
629     /* Set the search to token/session only if provided */
630     if (searchType == nssTokenSearchType_SessionOnly) {
631         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false);
632     } else if (searchType == nssTokenSearchType_TokenOnly) {
633         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true);
634     }
635     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert);
636     NSS_CK_TEMPLATE_FINISH(nick_template, attr, ntsize);
637     /* now locate the token certs matching this template */
638     objects = nssToken_FindObjectsByTemplate(token, sessionOpt,
639                                              nick_template, ntsize,
640                                              maximumOpt, statusOpt);
641     if (!objects) {
642         /* This is to workaround the fact that PKCS#11 doesn't specify
643          * whether the '\0' should be included.  XXX Is that still true?
644          * im - this is not needed by the current softoken.  However, I'm
645          * leaving it in until I have surveyed more tokens to see if it needed.
646          * well, its needed by the builtin token...
647          */
648         nick_template[0].ulValueLen++;
649         objects = nssToken_FindObjectsByTemplate(token, sessionOpt,
650                                                  nick_template, ntsize,
651                                                  maximumOpt, statusOpt);
652     }
653     return objects;
654 }
655 
656 /* XXX
657  * This function *does not* use the token object cache, because not even
658  * the softoken will return a value for CKA_NSS_EMAIL from a call
659  * to GetAttributes.  The softoken does allow searches with that attribute,
660  * it just won't return a value for it.
661  */
662 NSS_IMPLEMENT nssCryptokiObject **
nssToken_FindCertificatesByEmail(NSSToken * token,nssSession * sessionOpt,NSSASCII7 * email,nssTokenSearchType searchType,PRUint32 maximumOpt,PRStatus * statusOpt)663 nssToken_FindCertificatesByEmail(
664     NSSToken *token,
665     nssSession *sessionOpt,
666     NSSASCII7 *email,
667     nssTokenSearchType searchType,
668     PRUint32 maximumOpt,
669     PRStatus *statusOpt)
670 {
671     CK_ATTRIBUTE_PTR attr;
672     CK_ATTRIBUTE email_template[3];
673     CK_ULONG etsize;
674     nssCryptokiObject **objects;
675     NSS_CK_TEMPLATE_START(email_template, attr, etsize);
676     NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_NSS_EMAIL, email);
677     /* Set the search to token/session only if provided */
678     if (searchType == nssTokenSearchType_SessionOnly) {
679         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false);
680     } else if (searchType == nssTokenSearchType_TokenOnly) {
681         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true);
682     }
683     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert);
684     NSS_CK_TEMPLATE_FINISH(email_template, attr, etsize);
685     /* now locate the token certs matching this template */
686     objects = find_objects(token, sessionOpt,
687                            email_template, etsize,
688                            maximumOpt, statusOpt);
689     if (!objects) {
690         /* This is to workaround the fact that PKCS#11 doesn't specify
691          * whether the '\0' should be included.  XXX Is that still true?
692          * im - this is not needed by the current softoken.  However, I'm
693          * leaving it in until I have surveyed more tokens to see if it needed.
694          * well, its needed by the builtin token...
695          */
696         email_template[0].ulValueLen++;
697         objects = find_objects(token, sessionOpt,
698                                email_template, etsize,
699                                maximumOpt, statusOpt);
700     }
701     return objects;
702 }
703 
704 NSS_IMPLEMENT nssCryptokiObject **
nssToken_FindCertificatesByID(NSSToken * token,nssSession * sessionOpt,NSSItem * id,nssTokenSearchType searchType,PRUint32 maximumOpt,PRStatus * statusOpt)705 nssToken_FindCertificatesByID(
706     NSSToken *token,
707     nssSession *sessionOpt,
708     NSSItem *id,
709     nssTokenSearchType searchType,
710     PRUint32 maximumOpt,
711     PRStatus *statusOpt)
712 {
713     CK_ATTRIBUTE_PTR attr;
714     CK_ATTRIBUTE id_template[3];
715     CK_ULONG idtsize;
716     nssCryptokiObject **objects;
717     NSS_CK_TEMPLATE_START(id_template, attr, idtsize);
718     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ID, id);
719     /* Set the search to token/session only if provided */
720     if (searchType == nssTokenSearchType_SessionOnly) {
721         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false);
722     } else if (searchType == nssTokenSearchType_TokenOnly) {
723         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true);
724     }
725     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert);
726     NSS_CK_TEMPLATE_FINISH(id_template, attr, idtsize);
727     /* now locate the token certs matching this template */
728     objects = nssToken_FindObjectsByTemplate(token, sessionOpt,
729                                              id_template, idtsize,
730                                              maximumOpt, statusOpt);
731     return objects;
732 }
733 
734 /*
735  * decode the serial item and return our result.
736  * NOTE serialDecode's data is really stored in serial. Don't free it.
737  */
738 static PRStatus
nssToken_decodeSerialItem(NSSItem * serial,NSSItem * serialDecode)739 nssToken_decodeSerialItem(NSSItem *serial, NSSItem *serialDecode)
740 {
741     unsigned char *data = (unsigned char *)serial->data;
742     int data_left, data_len, index;
743 
744     if ((serial->size >= 3) && (data[0] == 0x2)) {
745         /* remove the der encoding of the serial number before generating the
746          * key.. */
747         data_left = serial->size - 2;
748         data_len = data[1];
749         index = 2;
750 
751         /* extended length ? (not very likely for a serial number) */
752         if (data_len & 0x80) {
753             int len_count = data_len & 0x7f;
754 
755             data_len = 0;
756             data_left -= len_count;
757             if (data_left > 0) {
758                 while (len_count--) {
759                     data_len = (data_len << 8) | data[index++];
760                 }
761             }
762         }
763         /* XXX leaving any leading zeros on the serial number for backwards
764          * compatibility
765          */
766         /* not a valid der, must be just an unlucky serial number value */
767         if (data_len == data_left) {
768             serialDecode->size = data_len;
769             serialDecode->data = &data[index];
770             return PR_SUCCESS;
771         }
772     }
773     return PR_FAILURE;
774 }
775 
776 NSS_IMPLEMENT nssCryptokiObject *
nssToken_FindCertificateByIssuerAndSerialNumber(NSSToken * token,nssSession * sessionOpt,NSSDER * issuer,NSSDER * serial,nssTokenSearchType searchType,PRStatus * statusOpt)777 nssToken_FindCertificateByIssuerAndSerialNumber(
778     NSSToken *token,
779     nssSession *sessionOpt,
780     NSSDER *issuer,
781     NSSDER *serial,
782     nssTokenSearchType searchType,
783     PRStatus *statusOpt)
784 {
785     CK_ATTRIBUTE_PTR attr;
786     CK_ATTRIBUTE_PTR serialAttr;
787     CK_ATTRIBUTE cert_template[4];
788     CK_ULONG ctsize;
789     nssCryptokiObject **objects;
790     nssCryptokiObject *rvObject = NULL;
791     NSS_CK_TEMPLATE_START(cert_template, attr, ctsize);
792 
793     if (!token) {
794         PORT_SetError(SEC_ERROR_NO_TOKEN);
795         if (statusOpt)
796             *statusOpt = PR_FAILURE;
797         return NULL;
798     }
799     /* Set the search to token/session only if provided */
800     if (searchType == nssTokenSearchType_SessionOnly) {
801         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false);
802     } else if ((searchType == nssTokenSearchType_TokenOnly) ||
803                (searchType == nssTokenSearchType_TokenForced)) {
804         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true);
805     }
806     /* Set the unique id */
807     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert);
808     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ISSUER, issuer);
809     serialAttr = attr;
810     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SERIAL_NUMBER, serial);
811     NSS_CK_TEMPLATE_FINISH(cert_template, attr, ctsize);
812     /* get the object handle */
813     if (searchType == nssTokenSearchType_TokenForced) {
814         objects = find_objects(token, sessionOpt,
815                                cert_template, ctsize,
816                                1, statusOpt);
817     } else {
818         objects = nssToken_FindObjectsByTemplate(token, sessionOpt,
819                                                  cert_template, ctsize,
820                                                  1, statusOpt);
821     }
822     if (objects) {
823         rvObject = objects[0];
824         nss_ZFreeIf(objects);
825     }
826 
827     /*
828      * NSS used to incorrectly store serial numbers in their decoded form.
829      * because of this old tokens have decoded serial numbers.
830      */
831     if (!objects) {
832         NSSItem serialDecode;
833         PRStatus status;
834 
835         status = nssToken_decodeSerialItem(serial, &serialDecode);
836         if (status != PR_SUCCESS) {
837             return NULL;
838         }
839         NSS_CK_SET_ATTRIBUTE_ITEM(serialAttr, CKA_SERIAL_NUMBER, &serialDecode);
840         if (searchType == nssTokenSearchType_TokenForced) {
841             objects = find_objects(token, sessionOpt,
842                                    cert_template, ctsize,
843                                    1, statusOpt);
844         } else {
845             objects = nssToken_FindObjectsByTemplate(token, sessionOpt,
846                                                      cert_template, ctsize,
847                                                      1, statusOpt);
848         }
849         if (objects) {
850             rvObject = objects[0];
851             nss_ZFreeIf(objects);
852         }
853     }
854     return rvObject;
855 }
856 
857 NSS_IMPLEMENT nssCryptokiObject *
nssToken_FindCertificateByEncodedCertificate(NSSToken * token,nssSession * sessionOpt,NSSBER * encodedCertificate,nssTokenSearchType searchType,PRStatus * statusOpt)858 nssToken_FindCertificateByEncodedCertificate(
859     NSSToken *token,
860     nssSession *sessionOpt,
861     NSSBER *encodedCertificate,
862     nssTokenSearchType searchType,
863     PRStatus *statusOpt)
864 {
865     CK_ATTRIBUTE_PTR attr;
866     CK_ATTRIBUTE cert_template[3];
867     CK_ULONG ctsize;
868     nssCryptokiObject **objects;
869     nssCryptokiObject *rvObject = NULL;
870     NSS_CK_TEMPLATE_START(cert_template, attr, ctsize);
871     /* Set the search to token/session only if provided */
872     if (searchType == nssTokenSearchType_SessionOnly) {
873         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false);
874     } else if (searchType == nssTokenSearchType_TokenOnly) {
875         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true);
876     }
877     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert);
878     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_VALUE, encodedCertificate);
879     NSS_CK_TEMPLATE_FINISH(cert_template, attr, ctsize);
880     /* get the object handle */
881     objects = nssToken_FindObjectsByTemplate(token, sessionOpt,
882                                              cert_template, ctsize,
883                                              1, statusOpt);
884     if (objects) {
885         rvObject = objects[0];
886         nss_ZFreeIf(objects);
887     }
888     return rvObject;
889 }
890 
891 NSS_IMPLEMENT nssCryptokiObject **
nssToken_FindPrivateKeys(NSSToken * token,nssSession * sessionOpt,nssTokenSearchType searchType,PRUint32 maximumOpt,PRStatus * statusOpt)892 nssToken_FindPrivateKeys(
893     NSSToken *token,
894     nssSession *sessionOpt,
895     nssTokenSearchType searchType,
896     PRUint32 maximumOpt,
897     PRStatus *statusOpt)
898 {
899     CK_ATTRIBUTE_PTR attr;
900     CK_ATTRIBUTE key_template[2];
901     CK_ULONG ktsize;
902     nssCryptokiObject **objects;
903 
904     NSS_CK_TEMPLATE_START(key_template, attr, ktsize);
905     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_privkey);
906     if (searchType == nssTokenSearchType_SessionOnly) {
907         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false);
908     } else if (searchType == nssTokenSearchType_TokenOnly) {
909         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true);
910     }
911     NSS_CK_TEMPLATE_FINISH(key_template, attr, ktsize);
912 
913     objects = nssToken_FindObjectsByTemplate(token, sessionOpt,
914                                              key_template, ktsize,
915                                              maximumOpt, statusOpt);
916     return objects;
917 }
918 
919 /* XXX ?there are no session cert objects, so only search token objects */
920 NSS_IMPLEMENT nssCryptokiObject *
nssToken_FindPrivateKeyByID(NSSToken * token,nssSession * sessionOpt,NSSItem * keyID)921 nssToken_FindPrivateKeyByID(
922     NSSToken *token,
923     nssSession *sessionOpt,
924     NSSItem *keyID)
925 {
926     CK_ATTRIBUTE_PTR attr;
927     CK_ATTRIBUTE key_template[3];
928     CK_ULONG ktsize;
929     nssCryptokiObject **objects;
930     nssCryptokiObject *rvKey = NULL;
931 
932     NSS_CK_TEMPLATE_START(key_template, attr, ktsize);
933     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_privkey);
934     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true);
935     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ID, keyID);
936     NSS_CK_TEMPLATE_FINISH(key_template, attr, ktsize);
937 
938     objects = nssToken_FindObjectsByTemplate(token, sessionOpt,
939                                              key_template, ktsize,
940                                              1, NULL);
941     if (objects) {
942         rvKey = objects[0];
943         nss_ZFreeIf(objects);
944     }
945     return rvKey;
946 }
947 
948 /* XXX ?there are no session cert objects, so only search token objects */
949 NSS_IMPLEMENT nssCryptokiObject *
nssToken_FindPublicKeyByID(NSSToken * token,nssSession * sessionOpt,NSSItem * keyID)950 nssToken_FindPublicKeyByID(
951     NSSToken *token,
952     nssSession *sessionOpt,
953     NSSItem *keyID)
954 {
955     CK_ATTRIBUTE_PTR attr;
956     CK_ATTRIBUTE key_template[3];
957     CK_ULONG ktsize;
958     nssCryptokiObject **objects;
959     nssCryptokiObject *rvKey = NULL;
960 
961     NSS_CK_TEMPLATE_START(key_template, attr, ktsize);
962     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_pubkey);
963     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true);
964     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ID, keyID);
965     NSS_CK_TEMPLATE_FINISH(key_template, attr, ktsize);
966 
967     objects = nssToken_FindObjectsByTemplate(token, sessionOpt,
968                                              key_template, ktsize,
969                                              1, NULL);
970     if (objects) {
971         rvKey = objects[0];
972         nss_ZFreeIf(objects);
973     }
974     return rvKey;
975 }
976 
977 static void
sha1_hash(NSSItem * input,NSSItem * output)978 sha1_hash(NSSItem *input, NSSItem *output)
979 {
980     NSSAlgorithmAndParameters *ap;
981     PK11SlotInfo *internal = PK11_GetInternalSlot();
982     NSSToken *token = PK11Slot_GetNSSToken(internal);
983     ap = NSSAlgorithmAndParameters_CreateSHA1Digest(NULL);
984     (void)nssToken_Digest(token, NULL, ap, input, output, NULL);
985     nss_ZFreeIf(ap);
986     (void)nssToken_Destroy(token);
987     PK11_FreeSlot(internal);
988 }
989 
990 static void
md5_hash(NSSItem * input,NSSItem * output)991 md5_hash(NSSItem *input, NSSItem *output)
992 {
993     NSSAlgorithmAndParameters *ap;
994     PK11SlotInfo *internal = PK11_GetInternalSlot();
995     NSSToken *token = PK11Slot_GetNSSToken(internal);
996     ap = NSSAlgorithmAndParameters_CreateMD5Digest(NULL);
997     (void)nssToken_Digest(token, NULL, ap, input, output, NULL);
998     nss_ZFreeIf(ap);
999     (void)nssToken_Destroy(token);
1000     PK11_FreeSlot(internal);
1001 }
1002 
1003 static CK_TRUST
get_ck_trust(nssTrustLevel nssTrust)1004 get_ck_trust(
1005     nssTrustLevel nssTrust)
1006 {
1007     CK_TRUST t;
1008     switch (nssTrust) {
1009         case nssTrustLevel_NotTrusted:
1010             t = CKT_NSS_NOT_TRUSTED;
1011             break;
1012         case nssTrustLevel_TrustedDelegator:
1013             t = CKT_NSS_TRUSTED_DELEGATOR;
1014             break;
1015         case nssTrustLevel_ValidDelegator:
1016             t = CKT_NSS_VALID_DELEGATOR;
1017             break;
1018         case nssTrustLevel_Trusted:
1019             t = CKT_NSS_TRUSTED;
1020             break;
1021         case nssTrustLevel_MustVerify:
1022             t = CKT_NSS_MUST_VERIFY_TRUST;
1023             break;
1024         case nssTrustLevel_Unknown:
1025         default:
1026             t = CKT_NSS_TRUST_UNKNOWN;
1027             break;
1028     }
1029     return t;
1030 }
1031 
1032 NSS_IMPLEMENT nssCryptokiObject *
nssToken_ImportTrust(NSSToken * tok,nssSession * sessionOpt,NSSDER * certEncoding,NSSDER * certIssuer,NSSDER * certSerial,nssTrustLevel serverAuth,nssTrustLevel clientAuth,nssTrustLevel codeSigning,nssTrustLevel emailProtection,PRBool stepUpApproved,PRBool asTokenObject)1033 nssToken_ImportTrust(
1034     NSSToken *tok,
1035     nssSession *sessionOpt,
1036     NSSDER *certEncoding,
1037     NSSDER *certIssuer,
1038     NSSDER *certSerial,
1039     nssTrustLevel serverAuth,
1040     nssTrustLevel clientAuth,
1041     nssTrustLevel codeSigning,
1042     nssTrustLevel emailProtection,
1043     PRBool stepUpApproved,
1044     PRBool asTokenObject)
1045 {
1046     nssCryptokiObject *object;
1047     CK_OBJECT_CLASS tobjc = CKO_NSS_TRUST;
1048     CK_TRUST ckSA, ckCA, ckCS, ckEP;
1049     CK_ATTRIBUTE_PTR attr;
1050     CK_ATTRIBUTE trust_tmpl[11];
1051     CK_ULONG tsize;
1052     PRUint8 sha1[20]; /* this is cheating... */
1053     PRUint8 md5[16];
1054     NSSItem sha1_result, md5_result;
1055     sha1_result.data = sha1;
1056     sha1_result.size = sizeof sha1;
1057     md5_result.data = md5;
1058     md5_result.size = sizeof md5;
1059     sha1_hash(certEncoding, &sha1_result);
1060     md5_hash(certEncoding, &md5_result);
1061     ckSA = get_ck_trust(serverAuth);
1062     ckCA = get_ck_trust(clientAuth);
1063     ckCS = get_ck_trust(codeSigning);
1064     ckEP = get_ck_trust(emailProtection);
1065     NSS_CK_TEMPLATE_START(trust_tmpl, attr, tsize);
1066     if (asTokenObject) {
1067         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true);
1068     } else {
1069         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false);
1070     }
1071     NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_CLASS, tobjc);
1072     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ISSUER, certIssuer);
1073     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SERIAL_NUMBER, certSerial);
1074     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CERT_SHA1_HASH, &sha1_result);
1075     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CERT_MD5_HASH, &md5_result);
1076     /* now set the trust values */
1077     NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_SERVER_AUTH, ckSA);
1078     NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_CLIENT_AUTH, ckCA);
1079     NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_CODE_SIGNING, ckCS);
1080     NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_TRUST_EMAIL_PROTECTION, ckEP);
1081     if (stepUpApproved) {
1082         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TRUST_STEP_UP_APPROVED,
1083                                   &g_ck_true);
1084     } else {
1085         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TRUST_STEP_UP_APPROVED,
1086                                   &g_ck_false);
1087     }
1088     NSS_CK_TEMPLATE_FINISH(trust_tmpl, attr, tsize);
1089     /* import the trust object onto the token */
1090     object = import_object(tok, sessionOpt, trust_tmpl, tsize);
1091     if (object && tok->cache) {
1092         nssTokenObjectCache_ImportObject(tok->cache, object, tobjc,
1093                                          trust_tmpl, tsize);
1094     }
1095     return object;
1096 }
1097 
1098 NSS_IMPLEMENT nssCryptokiObject *
nssToken_FindTrustForCertificate(NSSToken * token,nssSession * sessionOpt,NSSDER * certEncoding,NSSDER * certIssuer,NSSDER * certSerial,nssTokenSearchType searchType)1099 nssToken_FindTrustForCertificate(
1100     NSSToken *token,
1101     nssSession *sessionOpt,
1102     NSSDER *certEncoding,
1103     NSSDER *certIssuer,
1104     NSSDER *certSerial,
1105     nssTokenSearchType searchType)
1106 {
1107     CK_OBJECT_CLASS tobjc = CKO_NSS_TRUST;
1108     CK_ATTRIBUTE_PTR attr;
1109     CK_ATTRIBUTE tobj_template[5];
1110     CK_ULONG tobj_size;
1111     nssSession *session = sessionOpt ? sessionOpt : token->defaultSession;
1112     nssCryptokiObject *object = NULL, **objects;
1113 
1114     /* Don't ask the module to use an invalid session handle. */
1115     if (!session || session->handle == CK_INVALID_HANDLE) {
1116         PORT_SetError(SEC_ERROR_NO_TOKEN);
1117         return object;
1118     }
1119 
1120     NSS_CK_TEMPLATE_START(tobj_template, attr, tobj_size);
1121     if (searchType == nssTokenSearchType_TokenOnly) {
1122         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true);
1123     }
1124     NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_CLASS, tobjc);
1125     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_ISSUER, certIssuer);
1126     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SERIAL_NUMBER, certSerial);
1127     NSS_CK_TEMPLATE_FINISH(tobj_template, attr, tobj_size);
1128     objects = nssToken_FindObjectsByTemplate(token, session,
1129                                              tobj_template, tobj_size,
1130                                              1, NULL);
1131     if (objects) {
1132         object = objects[0];
1133         nss_ZFreeIf(objects);
1134     }
1135     return object;
1136 }
1137 
1138 NSS_IMPLEMENT nssCryptokiObject *
nssToken_ImportCRL(NSSToken * token,nssSession * sessionOpt,NSSDER * subject,NSSDER * encoding,PRBool isKRL,NSSUTF8 * url,PRBool asTokenObject)1139 nssToken_ImportCRL(
1140     NSSToken *token,
1141     nssSession *sessionOpt,
1142     NSSDER *subject,
1143     NSSDER *encoding,
1144     PRBool isKRL,
1145     NSSUTF8 *url,
1146     PRBool asTokenObject)
1147 {
1148     nssCryptokiObject *object;
1149     CK_OBJECT_CLASS crlobjc = CKO_NSS_CRL;
1150     CK_ATTRIBUTE_PTR attr;
1151     CK_ATTRIBUTE crl_tmpl[6];
1152     CK_ULONG crlsize;
1153 
1154     NSS_CK_TEMPLATE_START(crl_tmpl, attr, crlsize);
1155     if (asTokenObject) {
1156         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true);
1157     } else {
1158         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false);
1159     }
1160     NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_CLASS, crlobjc);
1161     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SUBJECT, subject);
1162     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_VALUE, encoding);
1163     NSS_CK_SET_ATTRIBUTE_UTF8(attr, CKA_NSS_URL, url);
1164     if (isKRL) {
1165         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_NSS_KRL, &g_ck_true);
1166     } else {
1167         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_NSS_KRL, &g_ck_false);
1168     }
1169     NSS_CK_TEMPLATE_FINISH(crl_tmpl, attr, crlsize);
1170 
1171     /* import the crl object onto the token */
1172     object = import_object(token, sessionOpt, crl_tmpl, crlsize);
1173     if (object && token->cache) {
1174         nssTokenObjectCache_ImportObject(token->cache, object, crlobjc,
1175                                          crl_tmpl, crlsize);
1176     }
1177     return object;
1178 }
1179 
1180 NSS_IMPLEMENT nssCryptokiObject **
nssToken_FindCRLsBySubject(NSSToken * token,nssSession * sessionOpt,NSSDER * subject,nssTokenSearchType searchType,PRUint32 maximumOpt,PRStatus * statusOpt)1181 nssToken_FindCRLsBySubject(
1182     NSSToken *token,
1183     nssSession *sessionOpt,
1184     NSSDER *subject,
1185     nssTokenSearchType searchType,
1186     PRUint32 maximumOpt,
1187     PRStatus *statusOpt)
1188 {
1189     CK_OBJECT_CLASS crlobjc = CKO_NSS_CRL;
1190     CK_ATTRIBUTE_PTR attr;
1191     CK_ATTRIBUTE crlobj_template[3];
1192     CK_ULONG crlobj_size;
1193     nssCryptokiObject **objects = NULL;
1194     nssSession *session = sessionOpt ? sessionOpt : token->defaultSession;
1195 
1196     /* Don't ask the module to use an invalid session handle. */
1197     if (!session || session->handle == CK_INVALID_HANDLE) {
1198         PORT_SetError(SEC_ERROR_NO_TOKEN);
1199         return objects;
1200     }
1201 
1202     NSS_CK_TEMPLATE_START(crlobj_template, attr, crlobj_size);
1203     if (searchType == nssTokenSearchType_SessionOnly) {
1204         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false);
1205     } else if (searchType == nssTokenSearchType_TokenOnly ||
1206                searchType == nssTokenSearchType_TokenForced) {
1207         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true);
1208     }
1209     NSS_CK_SET_ATTRIBUTE_VAR(attr, CKA_CLASS, crlobjc);
1210     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_SUBJECT, subject);
1211     NSS_CK_TEMPLATE_FINISH(crlobj_template, attr, crlobj_size);
1212 
1213     objects = nssToken_FindObjectsByTemplate(token, session,
1214                                              crlobj_template, crlobj_size,
1215                                              maximumOpt, statusOpt);
1216     return objects;
1217 }
1218 
1219 NSS_IMPLEMENT PRStatus
nssToken_GetCachedObjectAttributes(NSSToken * token,NSSArena * arenaOpt,nssCryptokiObject * object,CK_OBJECT_CLASS objclass,CK_ATTRIBUTE_PTR atemplate,CK_ULONG atlen)1220 nssToken_GetCachedObjectAttributes(
1221     NSSToken *token,
1222     NSSArena *arenaOpt,
1223     nssCryptokiObject *object,
1224     CK_OBJECT_CLASS objclass,
1225     CK_ATTRIBUTE_PTR atemplate,
1226     CK_ULONG atlen)
1227 {
1228     if (!token->cache) {
1229         return PR_FAILURE;
1230     }
1231     return nssTokenObjectCache_GetObjectAttributes(token->cache, arenaOpt,
1232                                                    object, objclass,
1233                                                    atemplate, atlen);
1234 }
1235 
1236 NSS_IMPLEMENT NSSItem *
nssToken_Digest(NSSToken * tok,nssSession * sessionOpt,NSSAlgorithmAndParameters * ap,NSSItem * data,NSSItem * rvOpt,NSSArena * arenaOpt)1237 nssToken_Digest(
1238     NSSToken *tok,
1239     nssSession *sessionOpt,
1240     NSSAlgorithmAndParameters *ap,
1241     NSSItem *data,
1242     NSSItem *rvOpt,
1243     NSSArena *arenaOpt)
1244 {
1245     CK_RV ckrv;
1246     CK_ULONG digestLen;
1247     CK_BYTE_PTR digest;
1248     NSSItem *rvItem = NULL;
1249     void *epv = nssToken_GetCryptokiEPV(tok);
1250     nssSession *session = (sessionOpt) ? sessionOpt : tok->defaultSession;
1251 
1252     /* Don't ask the module to use an invalid session handle. */
1253     if (!session || session->handle == CK_INVALID_HANDLE) {
1254         PORT_SetError(SEC_ERROR_NO_TOKEN);
1255         return rvItem;
1256     }
1257 
1258     nssSession_EnterMonitor(session);
1259     ckrv = CKAPI(epv)->C_DigestInit(session->handle, &ap->mechanism);
1260     if (ckrv != CKR_OK) {
1261         nssSession_ExitMonitor(session);
1262         return NULL;
1263     }
1264 #if 0
1265     /* XXX the standard says this should work, but it doesn't */
1266     ckrv = CKAPI(epv)->C_Digest(session->handle, NULL, 0, NULL, &digestLen);
1267     if (ckrv != CKR_OK) {
1268 	nssSession_ExitMonitor(session);
1269 	return NULL;
1270     }
1271 #endif
1272     digestLen = 0; /* XXX for now */
1273     digest = NULL;
1274     if (rvOpt) {
1275         if (rvOpt->size > 0 && rvOpt->size < digestLen) {
1276             nssSession_ExitMonitor(session);
1277             /* the error should be bad args */
1278             return NULL;
1279         }
1280         if (rvOpt->data) {
1281             digest = rvOpt->data;
1282         }
1283         digestLen = rvOpt->size;
1284     }
1285     if (!digest) {
1286         digest = (CK_BYTE_PTR)nss_ZAlloc(arenaOpt, digestLen);
1287         if (!digest) {
1288             nssSession_ExitMonitor(session);
1289             return NULL;
1290         }
1291     }
1292     ckrv = CKAPI(epv)->C_Digest(session->handle,
1293                                 (CK_BYTE_PTR)data->data,
1294                                 (CK_ULONG)data->size,
1295                                 (CK_BYTE_PTR)digest,
1296                                 &digestLen);
1297     nssSession_ExitMonitor(session);
1298     if (ckrv != CKR_OK) {
1299         nss_ZFreeIf(digest);
1300         return NULL;
1301     }
1302     if (!rvOpt) {
1303         rvItem = nssItem_Create(arenaOpt, NULL, digestLen, (void *)digest);
1304     }
1305     return rvItem;
1306 }
1307 
1308 NSS_IMPLEMENT PRStatus
nssToken_BeginDigest(NSSToken * tok,nssSession * sessionOpt,NSSAlgorithmAndParameters * ap)1309 nssToken_BeginDigest(
1310     NSSToken *tok,
1311     nssSession *sessionOpt,
1312     NSSAlgorithmAndParameters *ap)
1313 {
1314     CK_RV ckrv;
1315     void *epv = nssToken_GetCryptokiEPV(tok);
1316     nssSession *session = (sessionOpt) ? sessionOpt : tok->defaultSession;
1317 
1318     /* Don't ask the module to use an invalid session handle. */
1319     if (!session || session->handle == CK_INVALID_HANDLE) {
1320         PORT_SetError(SEC_ERROR_NO_TOKEN);
1321         return PR_FAILURE;
1322     }
1323 
1324     nssSession_EnterMonitor(session);
1325     ckrv = CKAPI(epv)->C_DigestInit(session->handle, &ap->mechanism);
1326     nssSession_ExitMonitor(session);
1327     return (ckrv == CKR_OK) ? PR_SUCCESS : PR_FAILURE;
1328 }
1329 
1330 NSS_IMPLEMENT PRStatus
nssToken_ContinueDigest(NSSToken * tok,nssSession * sessionOpt,NSSItem * item)1331 nssToken_ContinueDigest(
1332     NSSToken *tok,
1333     nssSession *sessionOpt,
1334     NSSItem *item)
1335 {
1336     CK_RV ckrv;
1337     void *epv = nssToken_GetCryptokiEPV(tok);
1338     nssSession *session = (sessionOpt) ? sessionOpt : tok->defaultSession;
1339 
1340     /* Don't ask the module to use an invalid session handle. */
1341     if (!session || session->handle == CK_INVALID_HANDLE) {
1342         PORT_SetError(SEC_ERROR_NO_TOKEN);
1343         return PR_FAILURE;
1344     }
1345 
1346     nssSession_EnterMonitor(session);
1347     ckrv = CKAPI(epv)->C_DigestUpdate(session->handle,
1348                                       (CK_BYTE_PTR)item->data,
1349                                       (CK_ULONG)item->size);
1350     nssSession_ExitMonitor(session);
1351     return (ckrv == CKR_OK) ? PR_SUCCESS : PR_FAILURE;
1352 }
1353 
1354 NSS_IMPLEMENT NSSItem *
nssToken_FinishDigest(NSSToken * tok,nssSession * sessionOpt,NSSItem * rvOpt,NSSArena * arenaOpt)1355 nssToken_FinishDigest(
1356     NSSToken *tok,
1357     nssSession *sessionOpt,
1358     NSSItem *rvOpt,
1359     NSSArena *arenaOpt)
1360 {
1361     CK_RV ckrv;
1362     CK_ULONG digestLen;
1363     CK_BYTE_PTR digest;
1364     NSSItem *rvItem = NULL;
1365     void *epv = nssToken_GetCryptokiEPV(tok);
1366     nssSession *session = (sessionOpt) ? sessionOpt : tok->defaultSession;
1367 
1368     /* Don't ask the module to use an invalid session handle. */
1369     if (!session || session->handle == CK_INVALID_HANDLE) {
1370         PORT_SetError(SEC_ERROR_NO_TOKEN);
1371         return NULL;
1372     }
1373 
1374     nssSession_EnterMonitor(session);
1375     ckrv = CKAPI(epv)->C_DigestFinal(session->handle, NULL, &digestLen);
1376     if (ckrv != CKR_OK || digestLen == 0) {
1377         nssSession_ExitMonitor(session);
1378         return NULL;
1379     }
1380     digest = NULL;
1381     if (rvOpt) {
1382         if (rvOpt->size > 0 && rvOpt->size < digestLen) {
1383             nssSession_ExitMonitor(session);
1384             /* the error should be bad args */
1385             return NULL;
1386         }
1387         if (rvOpt->data) {
1388             digest = rvOpt->data;
1389         }
1390         digestLen = rvOpt->size;
1391     }
1392     if (!digest) {
1393         digest = (CK_BYTE_PTR)nss_ZAlloc(arenaOpt, digestLen);
1394         if (!digest) {
1395             nssSession_ExitMonitor(session);
1396             return NULL;
1397         }
1398     }
1399     ckrv = CKAPI(epv)->C_DigestFinal(session->handle, digest, &digestLen);
1400     nssSession_ExitMonitor(session);
1401     if (ckrv != CKR_OK) {
1402         nss_ZFreeIf(digest);
1403         return NULL;
1404     }
1405     if (!rvOpt) {
1406         rvItem = nssItem_Create(arenaOpt, NULL, digestLen, (void *)digest);
1407     }
1408     return rvItem;
1409 }
1410 
1411 NSS_IMPLEMENT PRBool
nssToken_IsPresent(NSSToken * token)1412 nssToken_IsPresent(
1413     NSSToken *token)
1414 {
1415     return nssSlot_IsTokenPresent(token->slot);
1416 }
1417 
1418 /* Sigh.  The methods to find objects declared above cause problems with
1419  * the low-level object cache in the softoken -- the objects are found in
1420  * toto, then one wave of GetAttributes is done, then another.  Having a
1421  * large number of objects causes the cache to be thrashed, as the objects
1422  * are gone before there's any chance to ask for their attributes.
1423  * So, for now, bringing back traversal methods for certs.  This way all of
1424  * the cert's attributes can be grabbed immediately after finding it,
1425  * increasing the likelihood that the cache takes care of it.
1426  */
1427 NSS_IMPLEMENT PRStatus
nssToken_TraverseCertificates(NSSToken * token,nssSession * sessionOpt,nssTokenSearchType searchType,PRStatus (* callback)(nssCryptokiObject * instance,void * arg),void * arg)1428 nssToken_TraverseCertificates(
1429     NSSToken *token,
1430     nssSession *sessionOpt,
1431     nssTokenSearchType searchType,
1432     PRStatus (*callback)(nssCryptokiObject *instance, void *arg),
1433     void *arg)
1434 {
1435     CK_RV ckrv;
1436     CK_ULONG count;
1437     CK_OBJECT_HANDLE *objectHandles;
1438     CK_ATTRIBUTE_PTR attr;
1439     CK_ATTRIBUTE cert_template[2];
1440     CK_ULONG ctsize;
1441     NSSArena *arena;
1442     PRUint32 arraySize, numHandles;
1443     nssCryptokiObject **objects;
1444     void *epv = nssToken_GetCryptokiEPV(token);
1445     nssSession *session = (sessionOpt) ? sessionOpt : token->defaultSession;
1446 
1447     /* Don't ask the module to use an invalid session handle. */
1448     if (!session || session->handle == CK_INVALID_HANDLE) {
1449         PORT_SetError(SEC_ERROR_NO_TOKEN);
1450         return PR_FAILURE;
1451     }
1452 
1453     /* template for all certs */
1454     NSS_CK_TEMPLATE_START(cert_template, attr, ctsize);
1455     if (searchType == nssTokenSearchType_SessionOnly) {
1456         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_false);
1457     } else if (searchType == nssTokenSearchType_TokenOnly ||
1458                searchType == nssTokenSearchType_TokenForced) {
1459         NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_TOKEN, &g_ck_true);
1460     }
1461     NSS_CK_SET_ATTRIBUTE_ITEM(attr, CKA_CLASS, &g_ck_class_cert);
1462     NSS_CK_TEMPLATE_FINISH(cert_template, attr, ctsize);
1463 
1464     /* the arena is only for the array of object handles */
1465     arena = nssArena_Create();
1466     if (!arena) {
1467         return PR_FAILURE;
1468     }
1469     arraySize = OBJECT_STACK_SIZE;
1470     numHandles = 0;
1471     objectHandles = nss_ZNEWARRAY(arena, CK_OBJECT_HANDLE, arraySize);
1472     if (!objectHandles) {
1473         goto loser;
1474     }
1475     nssSession_EnterMonitor(session); /* ==== session lock === */
1476     /* Initialize the find with the template */
1477     ckrv = CKAPI(epv)->C_FindObjectsInit(session->handle,
1478                                          cert_template, ctsize);
1479     if (ckrv != CKR_OK) {
1480         nssSession_ExitMonitor(session);
1481         goto loser;
1482     }
1483     while (PR_TRUE) {
1484         /* Issue the find for up to arraySize - numHandles objects */
1485         ckrv = CKAPI(epv)->C_FindObjects(session->handle,
1486                                          objectHandles + numHandles,
1487                                          arraySize - numHandles,
1488                                          &count);
1489         if (ckrv != CKR_OK) {
1490             nssSession_ExitMonitor(session);
1491             goto loser;
1492         }
1493         /* bump the number of found objects */
1494         numHandles += count;
1495         if (numHandles < arraySize) {
1496             break;
1497         }
1498         /* the array is filled, double it and continue */
1499         arraySize *= 2;
1500         objectHandles = nss_ZREALLOCARRAY(objectHandles,
1501                                           CK_OBJECT_HANDLE,
1502                                           arraySize);
1503         if (!objectHandles) {
1504             nssSession_ExitMonitor(session);
1505             goto loser;
1506         }
1507     }
1508     ckrv = CKAPI(epv)->C_FindObjectsFinal(session->handle);
1509     nssSession_ExitMonitor(session); /* ==== end session lock === */
1510     if (ckrv != CKR_OK) {
1511         goto loser;
1512     }
1513     if (numHandles > 0) {
1514         objects = create_objects_from_handles(token, session,
1515                                               objectHandles, numHandles);
1516         if (objects) {
1517             nssCryptokiObject **op;
1518             for (op = objects; *op; op++) {
1519                 (void)(*callback)(*op, arg);
1520             }
1521             nss_ZFreeIf(objects);
1522         }
1523     }
1524     nssArena_Destroy(arena);
1525     return PR_SUCCESS;
1526 loser:
1527     nssArena_Destroy(arena);
1528     return PR_FAILURE;
1529 }
1530 
1531 NSS_IMPLEMENT PRBool
nssToken_IsPrivateKeyAvailable(NSSToken * token,NSSCertificate * c,nssCryptokiObject * instance)1532 nssToken_IsPrivateKeyAvailable(
1533     NSSToken *token,
1534     NSSCertificate *c,
1535     nssCryptokiObject *instance)
1536 {
1537     CK_OBJECT_CLASS theClass;
1538 
1539     if (token == NULL)
1540         return PR_FALSE;
1541     if (c == NULL)
1542         return PR_FALSE;
1543 
1544     theClass = CKO_PRIVATE_KEY;
1545     if (!nssSlot_IsLoggedIn(token->slot)) {
1546         theClass = CKO_PUBLIC_KEY;
1547     }
1548     if (PK11_MatchItem(token->pk11slot, instance->handle, theClass) !=
1549         CK_INVALID_HANDLE) {
1550         return PR_TRUE;
1551     }
1552     return PR_FALSE;
1553 }
1554