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