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