Back to index

lightning-sunbird  0.9+nobinonly
devutil.c
Go to the documentation of this file.
00001 /* ***** BEGIN LICENSE BLOCK *****
00002  * Version: MPL 1.1/GPL 2.0/LGPL 2.1
00003  *
00004  * The contents of this file are subject to the Mozilla Public License Version
00005  * 1.1 (the "License"); you may not use this file except in compliance with
00006  * the License. You may obtain a copy of the License at
00007  * http://www.mozilla.org/MPL/
00008  *
00009  * Software distributed under the License is distributed on an "AS IS" basis,
00010  * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
00011  * for the specific language governing rights and limitations under the
00012  * License.
00013  *
00014  * The Original Code is the Netscape security libraries.
00015  *
00016  * The Initial Developer of the Original Code is
00017  * Netscape Communications Corporation.
00018  * Portions created by the Initial Developer are Copyright (C) 1994-2000
00019  * the Initial Developer. All Rights Reserved.
00020  *
00021  * Contributor(s):
00022  *
00023  * Alternatively, the contents of this file may be used under the terms of
00024  * either the GNU General Public License Version 2 or later (the "GPL"), or
00025  * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
00026  * in which case the provisions of the GPL or the LGPL are applicable instead
00027  * of those above. If you wish to allow use of your version of this file only
00028  * under the terms of either the GPL or the LGPL, and not to allow others to
00029  * use your version of this file under the terms of the MPL, indicate your
00030  * decision by deleting the provisions above and replace them with the notice
00031  * and other provisions required by the GPL or the LGPL. If you do not delete
00032  * the provisions above, a recipient may use your version of this file under
00033  * the terms of any one of the MPL, the GPL or the LGPL.
00034  *
00035  * ***** END LICENSE BLOCK ***** */
00036 
00037 #ifdef DEBUG
00038 static const char CVS_ID[] = "@(#) $RCSfile: devutil.c,v $ $Revision: 1.26.28.1 $ $Date: 2007/11/16 05:25:07 $";
00039 #endif /* DEBUG */
00040 
00041 #ifndef DEVM_H
00042 #include "devm.h"
00043 #endif /* DEVM_H */
00044 
00045 #ifndef CKHELPER_H
00046 #include "ckhelper.h"
00047 #endif /* CKHELPER_H */
00048 
00049 NSS_IMPLEMENT nssCryptokiObject *
00050 nssCryptokiObject_Create (
00051   NSSToken *t, 
00052   nssSession *session,
00053   CK_OBJECT_HANDLE h
00054 )
00055 {
00056     PRStatus status;
00057     NSSSlot *slot;
00058     nssCryptokiObject *object;
00059     CK_BBOOL *isTokenObject;
00060     CK_ATTRIBUTE cert_template[] = {
00061        { CKA_TOKEN, NULL, 0 },
00062        { CKA_LABEL, NULL, 0 }
00063     };
00064     slot = nssToken_GetSlot(t);
00065     status = nssCKObject_GetAttributes(h, cert_template, 2,
00066                                        NULL, session, slot);
00067     nssSlot_Destroy(slot);
00068     if (status != PR_SUCCESS) {
00069        /* a failure here indicates a device error */
00070        return (nssCryptokiObject *)NULL;
00071     }
00072     object = nss_ZNEW(NULL, nssCryptokiObject);
00073     if (!object) {
00074        return (nssCryptokiObject *)NULL;
00075     }
00076     object->handle = h;
00077     object->token = nssToken_AddRef(t);
00078     isTokenObject = (CK_BBOOL *)cert_template[0].pValue;
00079     object->isTokenObject = *isTokenObject;
00080     nss_ZFreeIf(isTokenObject);
00081     NSS_CK_ATTRIBUTE_TO_UTF8(&cert_template[1], object->label);
00082     return object;
00083 }
00084 
00085 NSS_IMPLEMENT void
00086 nssCryptokiObject_Destroy (
00087   nssCryptokiObject *object
00088 )
00089 {
00090     if (object) {
00091        nssToken_Destroy(object->token);
00092        nss_ZFreeIf(object->label);
00093        nss_ZFreeIf(object);
00094     }
00095 }
00096 
00097 NSS_IMPLEMENT nssCryptokiObject *
00098 nssCryptokiObject_Clone (
00099   nssCryptokiObject *object
00100 )
00101 {
00102     nssCryptokiObject *rvObject;
00103     rvObject = nss_ZNEW(NULL, nssCryptokiObject);
00104     if (rvObject) {
00105        rvObject->handle = object->handle;
00106        rvObject->token = nssToken_AddRef(object->token);
00107        rvObject->isTokenObject = object->isTokenObject;
00108        if (object->label) {
00109            rvObject->label = nssUTF8_Duplicate(object->label, NULL);
00110        }
00111     }
00112     return rvObject;
00113 }
00114 
00115 NSS_EXTERN PRBool
00116 nssCryptokiObject_Equal (
00117   nssCryptokiObject *o1,
00118   nssCryptokiObject *o2
00119 )
00120 {
00121     return (o1->token == o2->token && o1->handle == o2->handle);
00122 }
00123 
00124 NSS_IMPLEMENT PRUint32
00125 nssPKCS11String_Length(CK_CHAR *pkcs11Str, PRUint32 bufLen)
00126 {
00127     PRInt32 i;
00128     for (i = bufLen - 1; i>=0; ) {
00129        if (pkcs11Str[i] != ' ' && pkcs11Str[i] != '\0') break;
00130        --i;
00131     }
00132     return (PRUint32)(i + 1);
00133 }
00134 
00135 /*
00136  * Slot arrays
00137  */
00138 
00139 NSS_IMPLEMENT NSSSlot **
00140 nssSlotArray_Clone (
00141   NSSSlot **slots
00142 )
00143 {
00144     NSSSlot **rvSlots = NULL;
00145     NSSSlot **sp = slots;
00146     PRUint32 count = 0;
00147     while (sp && *sp) count++;
00148     if (count > 0) {
00149        rvSlots = nss_ZNEWARRAY(NULL, NSSSlot *, count + 1);
00150        if (rvSlots) {
00151            sp = slots;
00152            count = 0;
00153            for (sp = slots; *sp; sp++) {
00154               rvSlots[count++] = nssSlot_AddRef(*sp);
00155            }
00156        }
00157     }
00158     return rvSlots;
00159 }
00160 
00161 NSS_IMPLEMENT void
00162 nssSlotArray_Destroy (
00163   NSSSlot **slots
00164 )
00165 {
00166     if (slots) {
00167        NSSSlot **slotp;
00168        for (slotp = slots; *slotp; slotp++) {
00169            nssSlot_Destroy(*slotp);
00170        }
00171        nss_ZFreeIf(slots);
00172     }
00173 }
00174 
00175 NSS_IMPLEMENT void
00176 NSSSlotArray_Destroy (
00177   NSSSlot **slots
00178 )
00179 {
00180     nssSlotArray_Destroy(slots);
00181 }
00182 
00183 NSS_IMPLEMENT void
00184 nssTokenArray_Destroy (
00185   NSSToken **tokens
00186 )
00187 {
00188     if (tokens) {
00189        NSSToken **tokenp;
00190        for (tokenp = tokens; *tokenp; tokenp++) {
00191            nssToken_Destroy(*tokenp);
00192        }
00193        nss_ZFreeIf(tokens);
00194     }
00195 }
00196 
00197 NSS_IMPLEMENT void
00198 NSSTokenArray_Destroy (
00199   NSSToken **tokens
00200 )
00201 {
00202     nssTokenArray_Destroy(tokens);
00203 }
00204 
00205 NSS_IMPLEMENT void
00206 nssCryptokiObjectArray_Destroy (
00207   nssCryptokiObject **objects
00208 )
00209 {
00210     if (objects) {
00211        nssCryptokiObject **op;
00212        for (op = objects; *op; op++) {
00213            nssCryptokiObject_Destroy(*op);
00214        }
00215        nss_ZFreeIf(objects);
00216     }
00217 }
00218 
00219 /* object cache for token */
00220 
00221 typedef struct
00222 {
00223   NSSArena *arena;
00224   nssCryptokiObject *object;
00225   CK_ATTRIBUTE_PTR attributes;
00226   CK_ULONG numAttributes;
00227 }
00228 nssCryptokiObjectAndAttributes;
00229 
00230 enum {
00231   cachedCerts = 0,
00232   cachedTrust = 1,
00233   cachedCRLs = 2
00234 } cachedObjectType;
00235 
00236 struct nssTokenObjectCacheStr
00237 {
00238   NSSToken *token;
00239   PZLock *lock;
00240   PRBool loggedIn;
00241   PRBool doObjectType[3];
00242   PRBool searchedObjectType[3];
00243   nssCryptokiObjectAndAttributes **objects[3];
00244 };
00245 
00246 NSS_IMPLEMENT nssTokenObjectCache *
00247 nssTokenObjectCache_Create (
00248   NSSToken *token,
00249   PRBool cacheCerts,
00250   PRBool cacheTrust,
00251   PRBool cacheCRLs
00252 )
00253 {
00254     nssTokenObjectCache *rvCache;
00255     rvCache = nss_ZNEW(NULL, nssTokenObjectCache);
00256     if (!rvCache) {
00257        goto loser;
00258     }
00259     rvCache->lock = PZ_NewLock(nssILockOther); /* XXX */
00260     if (!rvCache->lock) {
00261        goto loser;
00262     }
00263     rvCache->doObjectType[cachedCerts] = cacheCerts;
00264     rvCache->doObjectType[cachedTrust] = cacheTrust;
00265     rvCache->doObjectType[cachedCRLs] = cacheCRLs;
00266     rvCache->token = token; /* cache goes away with token */
00267     return rvCache;
00268 loser:
00269     return (nssTokenObjectCache *)NULL;
00270 }
00271 
00272 static void
00273 clear_cache (
00274   nssTokenObjectCache *cache
00275 )
00276 {
00277     nssCryptokiObjectAndAttributes **oa;
00278     PRUint32 objectType;
00279     for (objectType = cachedCerts; objectType <= cachedCRLs; objectType++) {
00280        cache->searchedObjectType[objectType] = PR_FALSE;
00281        if (!cache->objects[objectType]) {
00282            continue;
00283        }
00284        for (oa = cache->objects[objectType]; *oa; oa++) {
00285            /* prevent the token from being destroyed */
00286            (*oa)->object->token = NULL;
00287            nssCryptokiObject_Destroy((*oa)->object);
00288            nssArena_Destroy((*oa)->arena);
00289        }
00290        nss_ZFreeIf(cache->objects[objectType]);
00291        cache->objects[objectType] = NULL;
00292     }
00293 }
00294 
00295 NSS_IMPLEMENT void
00296 nssTokenObjectCache_Clear (
00297   nssTokenObjectCache *cache
00298 )
00299 {
00300     if (cache) {
00301        PZ_Lock(cache->lock);
00302        clear_cache(cache);
00303        PZ_Unlock(cache->lock);
00304     }
00305 }
00306 
00307 NSS_IMPLEMENT void
00308 nssTokenObjectCache_Destroy (
00309   nssTokenObjectCache *cache
00310 )
00311 {
00312     if (cache) {
00313        clear_cache(cache);
00314        PZ_DestroyLock(cache->lock);
00315        nss_ZFreeIf(cache);
00316     }
00317 }
00318 
00319 NSS_IMPLEMENT PRBool
00320 nssTokenObjectCache_HaveObjectClass (
00321   nssTokenObjectCache *cache,
00322   CK_OBJECT_CLASS objclass
00323 )
00324 {
00325     PRBool haveIt;
00326     PZ_Lock(cache->lock);
00327     switch (objclass) {
00328     case CKO_CERTIFICATE:    haveIt = cache->doObjectType[cachedCerts]; break;
00329     case CKO_NETSCAPE_TRUST: haveIt = cache->doObjectType[cachedTrust]; break;
00330     case CKO_NETSCAPE_CRL:   haveIt = cache->doObjectType[cachedCRLs];  break;
00331     default:                 haveIt = PR_FALSE;
00332     }
00333     PZ_Unlock(cache->lock);
00334     return haveIt;
00335 }
00336 
00337 static nssCryptokiObjectAndAttributes **
00338 create_object_array (
00339   nssCryptokiObject **objects,
00340   PRBool *doObjects,
00341   PRUint32 *numObjects,
00342   PRStatus *status
00343 )
00344 {
00345     nssCryptokiObjectAndAttributes **rvOandA = NULL;
00346     *numObjects = 0;
00347     /* There are no objects for this type */
00348     if (!objects || !*objects) {
00349        *status = PR_SUCCESS;
00350        return rvOandA;
00351     }
00352     while (*objects++) (*numObjects)++;
00353     if (*numObjects >= MAX_LOCAL_CACHE_OBJECTS) {
00354        /* Hit the maximum allowed, so don't use a cache (there are
00355         * too many objects to make caching worthwhile, presumably, if
00356         * the token can handle that many objects, it can handle searching.
00357         */
00358        *doObjects = PR_FALSE;
00359        *status = PR_FAILURE;
00360        *numObjects = 0;
00361     } else {
00362        rvOandA = nss_ZNEWARRAY(NULL, 
00363                                nssCryptokiObjectAndAttributes *, 
00364                                *numObjects + 1);
00365        *status = rvOandA ? PR_SUCCESS : PR_FAILURE;
00366     }
00367     return rvOandA;
00368 }
00369 
00370 static nssCryptokiObjectAndAttributes *
00371 create_object (
00372   nssCryptokiObject *object,
00373   const CK_ATTRIBUTE_TYPE *types,
00374   PRUint32 numTypes,
00375   PRStatus *status
00376 )
00377 {
00378     PRUint32 j;
00379     NSSArena *arena;
00380     NSSSlot *slot = NULL;
00381     nssSession *session = NULL;
00382     nssCryptokiObjectAndAttributes *rvCachedObject = NULL;
00383 
00384     slot = nssToken_GetSlot(object->token);
00385     session = nssToken_GetDefaultSession(object->token);
00386 
00387     arena = nssArena_Create();
00388     if (!arena) {
00389        goto loser;
00390     }
00391     rvCachedObject = nss_ZNEW(arena, nssCryptokiObjectAndAttributes);
00392     if (!rvCachedObject) {
00393        goto loser;
00394     }
00395     rvCachedObject->arena = arena;
00396     /* The cache is tied to the token, and therefore the objects
00397      * in it should not hold references to the token.
00398      */
00399     nssToken_Destroy(object->token);
00400     rvCachedObject->object = object;
00401     rvCachedObject->attributes = nss_ZNEWARRAY(arena, CK_ATTRIBUTE, numTypes);
00402     if (!rvCachedObject->attributes) {
00403        goto loser;
00404     }
00405     for (j=0; j<numTypes; j++) {
00406        rvCachedObject->attributes[j].type = types[j];
00407     }
00408     *status = nssCKObject_GetAttributes(object->handle,
00409                                         rvCachedObject->attributes,
00410                                         numTypes,
00411                                         arena,
00412                                         session,
00413                                         slot);
00414     if (*status != PR_SUCCESS) {
00415        goto loser;
00416     }
00417     rvCachedObject->numAttributes = numTypes;
00418     *status = PR_SUCCESS;
00419     if (slot) {
00420        nssSlot_Destroy(slot);
00421     }
00422     return rvCachedObject;
00423 loser:
00424     *status = PR_FAILURE;
00425     if (slot) {
00426        nssSlot_Destroy(slot);
00427     }
00428     if (arena)
00429        nssArena_Destroy(arena);
00430     return (nssCryptokiObjectAndAttributes *)NULL;
00431 }
00432 
00433 /*
00434  *
00435  * State diagram for cache:
00436  *
00437  *            token !present            token removed
00438  *        +-------------------------+<----------------------+
00439  *        |                         ^                       |
00440  *        v                         |                       |
00441  *  +----------+   slot friendly    |  token present   +----------+ 
00442  *  |   cache  | -----------------> % ---------------> |   cache  |
00443  *  | unloaded |                                       |  loaded  |
00444  *  +----------+                                       +----------+
00445  *    ^   |                                                 ^   |
00446  *    |   |   slot !friendly           slot logged in       |   |
00447  *    |   +-----------------------> % ----------------------+   |
00448  *    |                             |                           |
00449  *    | slot logged out             v  slot !friendly           |
00450  *    +-----------------------------+<--------------------------+
00451  *
00452  */
00453 
00454 /* This function must not be called with cache->lock locked. */
00455 static PRBool
00456 token_is_present (
00457   nssTokenObjectCache *cache
00458 )
00459 {
00460     NSSSlot *slot = nssToken_GetSlot(cache->token);
00461     PRBool tokenPresent = nssSlot_IsTokenPresent(slot);
00462     nssSlot_Destroy(slot);
00463     return tokenPresent;
00464 }
00465 
00466 static PRBool
00467 search_for_objects (
00468   nssTokenObjectCache *cache
00469 )
00470 {
00471     PRBool doSearch = PR_FALSE;
00472     NSSSlot *slot = nssToken_GetSlot(cache->token);
00473     /* Handle non-friendly slots (slots which require login for objects) */
00474     if (!nssSlot_IsFriendly(slot)) {
00475        if (nssSlot_IsLoggedIn(slot)) {
00476            /* Either no state change, or went from !logged in -> logged in */
00477            cache->loggedIn = PR_TRUE;
00478            doSearch = PR_TRUE;
00479        } else {
00480            if (cache->loggedIn) {
00481               /* went from logged in -> !logged in, destroy cached objects */
00482               clear_cache(cache);
00483               cache->loggedIn = PR_FALSE;
00484            } /* else no state change, still not logged in, so exit */
00485        }
00486     } else {
00487        /* slot is friendly, thus always available for search */
00488        doSearch = PR_TRUE;
00489     }
00490     nssSlot_Destroy(slot);
00491     return doSearch;
00492 }
00493 
00494 static nssCryptokiObjectAndAttributes *
00495 create_cert (
00496   nssCryptokiObject *object,
00497   PRStatus *status
00498 )
00499 {
00500     static const CK_ATTRIBUTE_TYPE certAttr[] = {
00501        CKA_CLASS,
00502        CKA_TOKEN,
00503        CKA_LABEL,
00504        CKA_CERTIFICATE_TYPE,
00505        CKA_ID,
00506        CKA_VALUE,
00507        CKA_ISSUER,
00508        CKA_SERIAL_NUMBER,
00509        CKA_SUBJECT,
00510        CKA_NETSCAPE_EMAIL
00511     };
00512     static const PRUint32 numCertAttr = sizeof(certAttr) / sizeof(certAttr[0]);
00513     return create_object(object, certAttr, numCertAttr, status);
00514 }
00515 
00516 static PRStatus
00517 get_token_certs_for_cache (
00518   nssTokenObjectCache *cache
00519 )
00520 {
00521     PRStatus status;
00522     nssCryptokiObject **objects;
00523     PRBool *doIt = &cache->doObjectType[cachedCerts];
00524     PRUint32 i, numObjects;
00525 
00526     if (!search_for_objects(cache) || 
00527          cache->searchedObjectType[cachedCerts] || 
00528         !cache->doObjectType[cachedCerts]) 
00529     {
00530        /* Either there was a state change that prevents a search
00531         * (token logged out), or the search was already done,
00532         * or certs are not being cached.
00533         */
00534        return PR_SUCCESS;
00535     }
00536     objects = nssToken_FindCertificates(cache->token, NULL,
00537                                         nssTokenSearchType_TokenForced,
00538                                     MAX_LOCAL_CACHE_OBJECTS, &status);
00539     if (status != PR_SUCCESS) {
00540        return status;
00541     }
00542     cache->objects[cachedCerts] = create_object_array(objects,
00543                                                       doIt,
00544                                                       &numObjects,
00545                                                       &status);
00546     if (status != PR_SUCCESS) {
00547        return status;
00548     }
00549     for (i=0; i<numObjects; i++) {
00550        cache->objects[cachedCerts][i] = create_cert(objects[i], &status);
00551        if (status != PR_SUCCESS) {
00552            break;
00553        }
00554     }
00555     if (status == PR_SUCCESS) {
00556        nss_ZFreeIf(objects);
00557     } else {
00558        PRUint32 j;
00559        for (j=0; j<i; j++) {
00560            /* sigh */
00561            nssToken_AddRef(cache->objects[cachedCerts][j]->object->token);
00562            nssArena_Destroy(cache->objects[cachedCerts][j]->arena);
00563        }
00564        nssCryptokiObjectArray_Destroy(objects);
00565     }
00566     cache->searchedObjectType[cachedCerts] = PR_TRUE;
00567     return status;
00568 }
00569 
00570 static nssCryptokiObjectAndAttributes *
00571 create_trust (
00572   nssCryptokiObject *object,
00573   PRStatus *status
00574 )
00575 {
00576     static const CK_ATTRIBUTE_TYPE trustAttr[] = {
00577        CKA_CLASS,
00578        CKA_TOKEN,
00579        CKA_LABEL,
00580        CKA_CERT_SHA1_HASH,
00581        CKA_CERT_MD5_HASH,
00582        CKA_ISSUER,
00583        CKA_SUBJECT,
00584        CKA_TRUST_SERVER_AUTH,
00585        CKA_TRUST_CLIENT_AUTH,
00586        CKA_TRUST_EMAIL_PROTECTION,
00587        CKA_TRUST_CODE_SIGNING
00588     };
00589     static const PRUint32 numTrustAttr = sizeof(trustAttr) / sizeof(trustAttr[0]);
00590     return create_object(object, trustAttr, numTrustAttr, status);
00591 }
00592 
00593 static PRStatus
00594 get_token_trust_for_cache (
00595   nssTokenObjectCache *cache
00596 )
00597 {
00598     PRStatus status;
00599     nssCryptokiObject **objects;
00600     PRBool *doIt = &cache->doObjectType[cachedTrust];
00601     PRUint32 i, numObjects;
00602 
00603     if (!search_for_objects(cache) || 
00604          cache->searchedObjectType[cachedTrust] || 
00605         !cache->doObjectType[cachedTrust]) 
00606     {
00607        /* Either there was a state change that prevents a search
00608         * (token logged out), or the search was already done,
00609         * or trust is not being cached.
00610         */
00611        return PR_SUCCESS;
00612     }
00613     objects = nssToken_FindTrustObjects(cache->token, NULL,
00614                                         nssTokenSearchType_TokenForced,
00615                                     MAX_LOCAL_CACHE_OBJECTS, &status);
00616     if (status != PR_SUCCESS) {
00617        return status;
00618     }
00619     cache->objects[cachedTrust] = create_object_array(objects,
00620                                                       doIt,
00621                                                       &numObjects,
00622                                                       &status);
00623     if (status != PR_SUCCESS) {
00624        return status;
00625     }
00626     for (i=0; i<numObjects; i++) {
00627        cache->objects[cachedTrust][i] = create_trust(objects[i], &status);
00628        if (status != PR_SUCCESS) {
00629            break;
00630        }
00631     }
00632     if (status == PR_SUCCESS) {
00633        nss_ZFreeIf(objects);
00634     } else {
00635        PRUint32 j;
00636        for (j=0; j<i; j++) {
00637            /* sigh */
00638            nssToken_AddRef(cache->objects[cachedTrust][j]->object->token);
00639            nssArena_Destroy(cache->objects[cachedTrust][j]->arena);
00640        }
00641        nssCryptokiObjectArray_Destroy(objects);
00642     }
00643     cache->searchedObjectType[cachedTrust] = PR_TRUE;
00644     return status;
00645 }
00646 
00647 static nssCryptokiObjectAndAttributes *
00648 create_crl (
00649   nssCryptokiObject *object,
00650   PRStatus *status
00651 )
00652 {
00653     static const CK_ATTRIBUTE_TYPE crlAttr[] = {
00654        CKA_CLASS,
00655        CKA_TOKEN,
00656        CKA_LABEL,
00657        CKA_VALUE,
00658        CKA_SUBJECT,
00659        CKA_NETSCAPE_KRL,
00660        CKA_NETSCAPE_URL
00661     };
00662     static const PRUint32 numCRLAttr = sizeof(crlAttr) / sizeof(crlAttr[0]);
00663     return create_object(object, crlAttr, numCRLAttr, status);
00664 }
00665 
00666 static PRStatus
00667 get_token_crls_for_cache (
00668   nssTokenObjectCache *cache
00669 )
00670 {
00671     PRStatus status;
00672     nssCryptokiObject **objects;
00673     PRBool *doIt = &cache->doObjectType[cachedCRLs];
00674     PRUint32 i, numObjects;
00675 
00676     if (!search_for_objects(cache) || 
00677          cache->searchedObjectType[cachedCRLs] || 
00678         !cache->doObjectType[cachedCRLs]) 
00679     {
00680        /* Either there was a state change that prevents a search
00681         * (token logged out), or the search was already done,
00682         * or CRLs are not being cached.
00683         */
00684        return PR_SUCCESS;
00685     }
00686     objects = nssToken_FindCRLs(cache->token, NULL,
00687                                 nssTokenSearchType_TokenForced,
00688                             MAX_LOCAL_CACHE_OBJECTS, &status);
00689     if (status != PR_SUCCESS) {
00690        return status;
00691     }
00692     cache->objects[cachedCRLs] = create_object_array(objects,
00693                                                      doIt,
00694                                                      &numObjects,
00695                                                      &status);
00696     if (status != PR_SUCCESS) {
00697        return status;
00698     }
00699     for (i=0; i<numObjects; i++) {
00700        cache->objects[cachedCRLs][i] = create_crl(objects[i], &status);
00701        if (status != PR_SUCCESS) {
00702            break;
00703        }
00704     }
00705     if (status == PR_SUCCESS) {
00706        nss_ZFreeIf(objects);
00707     } else {
00708        PRUint32 j;
00709        for (j=0; j<i; j++) {
00710            /* sigh */
00711            nssToken_AddRef(cache->objects[cachedCRLs][j]->object->token);
00712            nssArena_Destroy(cache->objects[cachedCRLs][j]->arena);
00713        }
00714        nssCryptokiObjectArray_Destroy(objects);
00715     }
00716     cache->searchedObjectType[cachedCRLs] = PR_TRUE;
00717     return status;
00718 }
00719 
00720 static CK_ATTRIBUTE_PTR
00721 find_attribute_in_object (
00722   nssCryptokiObjectAndAttributes *obj,
00723   CK_ATTRIBUTE_TYPE attrType
00724 )
00725 {
00726     PRUint32 j;
00727     for (j=0; j<obj->numAttributes; j++) {
00728        if (attrType == obj->attributes[j].type) {
00729            return &obj->attributes[j];
00730        }
00731     }
00732     return (CK_ATTRIBUTE_PTR)NULL;
00733 }
00734 
00735 /* Find all objects in the array that match the supplied template */
00736 static nssCryptokiObject **
00737 find_objects_in_array (
00738   nssCryptokiObjectAndAttributes **objArray,
00739   CK_ATTRIBUTE_PTR ot,
00740   CK_ULONG otlen,
00741   PRUint32 maximumOpt
00742 )
00743 {
00744     PRIntn oi = 0;
00745     PRUint32 i;
00746     NSSArena *arena;
00747     PRUint32 size = 8;
00748     PRUint32 numMatches = 0;
00749     nssCryptokiObject **objects = NULL;
00750     nssCryptokiObjectAndAttributes **matches = NULL;
00751     CK_ATTRIBUTE_PTR attr;
00752 
00753     if (!objArray) {
00754        return (nssCryptokiObject **)NULL;
00755     }
00756     arena = nssArena_Create();
00757     if (!arena) {
00758        return (nssCryptokiObject **)NULL;
00759     }
00760     matches = nss_ZNEWARRAY(arena, nssCryptokiObjectAndAttributes *, size);
00761     if (!matches) {
00762        goto loser;
00763     }
00764     if (maximumOpt == 0) maximumOpt = ~0;
00765     /* loop over the cached objects */
00766     for (; *objArray && numMatches < maximumOpt; objArray++) {
00767        nssCryptokiObjectAndAttributes *obj = *objArray;
00768        /* loop over the test template */
00769        for (i=0; i<otlen; i++) {
00770            /* see if the object has the attribute */
00771            attr = find_attribute_in_object(obj, ot[i].type);
00772            if (!attr) {
00773               /* nope, match failed */
00774               break;
00775            }
00776            /* compare the attribute against the test value */
00777            if (ot[i].ulValueLen != attr->ulValueLen ||
00778                !nsslibc_memequal(ot[i].pValue, 
00779                                  attr->pValue,
00780                                  attr->ulValueLen, NULL))
00781            {
00782               /* nope, match failed */
00783               break;
00784            }
00785        }
00786        if (i == otlen) {
00787            /* all of the attributes in the test template were found
00788             * in the object's template, and they all matched
00789             */
00790            matches[numMatches++] = obj;
00791            if (numMatches == size) {
00792               size *= 2;
00793               matches = nss_ZREALLOCARRAY(matches, 
00794                                           nssCryptokiObjectAndAttributes *, 
00795                                           size);
00796               if (!matches) {
00797                   goto loser;
00798               }
00799            }
00800        }
00801     }
00802     if (numMatches > 0) {
00803        objects = nss_ZNEWARRAY(NULL, nssCryptokiObject *, numMatches + 1);
00804        if (!objects) {
00805            goto loser;
00806        }
00807        for (oi=0; oi<(PRIntn)numMatches; oi++) {
00808            objects[oi] = nssCryptokiObject_Clone(matches[oi]->object);
00809            if (!objects[oi]) {
00810               goto loser;
00811            }
00812        }
00813     }
00814     nssArena_Destroy(arena);
00815     return objects;
00816 loser:
00817     if (objects) {
00818        for (--oi; oi>=0; --oi) {
00819            nssCryptokiObject_Destroy(objects[oi]);
00820        }
00821     }
00822     nssArena_Destroy(arena);
00823     return (nssCryptokiObject **)NULL;
00824 }
00825 
00826 NSS_IMPLEMENT nssCryptokiObject **
00827 nssTokenObjectCache_FindObjectsByTemplate (
00828   nssTokenObjectCache *cache,
00829   CK_OBJECT_CLASS objclass,
00830   CK_ATTRIBUTE_PTR otemplate,
00831   CK_ULONG otlen,
00832   PRUint32 maximumOpt,
00833   PRStatus *statusOpt
00834 )
00835 {
00836     PRStatus status = PR_FAILURE;
00837     nssCryptokiObject **rvObjects = NULL;
00838     if (!token_is_present(cache)) {
00839        status = PR_SUCCESS;
00840        goto finish;
00841     }
00842     PZ_Lock(cache->lock);
00843     switch (objclass) {
00844     case CKO_CERTIFICATE:
00845        if (cache->doObjectType[cachedCerts]) {
00846            status = get_token_certs_for_cache(cache);
00847            if (status != PR_SUCCESS) {
00848               goto unlock;
00849            }
00850            rvObjects = find_objects_in_array(cache->objects[cachedCerts], 
00851                                              otemplate, otlen, maximumOpt);
00852        }
00853        break;
00854     case CKO_NETSCAPE_TRUST:
00855        if (cache->doObjectType[cachedTrust]) {
00856            status = get_token_trust_for_cache(cache);
00857            if (status != PR_SUCCESS) {
00858               goto unlock;
00859            }
00860            rvObjects = find_objects_in_array(cache->objects[cachedTrust], 
00861                                              otemplate, otlen, maximumOpt);
00862        }
00863        break;
00864     case CKO_NETSCAPE_CRL:
00865        if (cache->doObjectType[cachedCRLs]) {
00866            status = get_token_crls_for_cache(cache);
00867            if (status != PR_SUCCESS) {
00868               goto unlock;
00869            }
00870            rvObjects = find_objects_in_array(cache->objects[cachedCRLs], 
00871                                              otemplate, otlen, maximumOpt);
00872        }
00873        break;
00874     default: break;
00875     }
00876 unlock:
00877     PZ_Unlock(cache->lock);
00878 finish:
00879     if (statusOpt) {
00880        *statusOpt = status;
00881     }
00882     return rvObjects;
00883 }
00884 
00885 static PRBool
00886 cache_available_for_object_type (
00887   nssTokenObjectCache *cache,
00888   PRUint32 objectType
00889 )
00890 {
00891     if (!cache->doObjectType[objectType]) {
00892        /* not caching this object kind */
00893        return PR_FALSE;
00894     }
00895     if (!cache->searchedObjectType[objectType]) {
00896        /* objects are not cached yet */
00897        return PR_FALSE;
00898     }
00899     if (!search_for_objects(cache)) {
00900        /* not logged in */
00901        return PR_FALSE;
00902     }
00903     return PR_TRUE;
00904 }
00905 
00906 NSS_IMPLEMENT PRStatus
00907 nssTokenObjectCache_GetObjectAttributes (
00908   nssTokenObjectCache *cache,
00909   NSSArena *arenaOpt,
00910   nssCryptokiObject *object,
00911   CK_OBJECT_CLASS objclass,
00912   CK_ATTRIBUTE_PTR atemplate,
00913   CK_ULONG atlen
00914 )
00915 {
00916     PRUint32 i, j;
00917     NSSArena *arena = NULL;
00918     nssArenaMark *mark = NULL;
00919     nssCryptokiObjectAndAttributes *cachedOA = NULL;
00920     nssCryptokiObjectAndAttributes **oa = NULL;
00921     PRUint32 objectType;
00922     if (!token_is_present(cache)) {
00923        return PR_FAILURE;
00924     }
00925     PZ_Lock(cache->lock);
00926     switch (objclass) {
00927     case CKO_CERTIFICATE:    objectType = cachedCerts; break;
00928     case CKO_NETSCAPE_TRUST: objectType = cachedTrust; break;
00929     case CKO_NETSCAPE_CRL:   objectType = cachedCRLs;  break;
00930     default: goto loser;
00931     }
00932     if (!cache_available_for_object_type(cache, objectType)) {
00933        goto loser;
00934     }
00935     oa = cache->objects[objectType];
00936     if (!oa) {
00937        goto loser;
00938     }
00939     for (; *oa; oa++) {
00940        if (nssCryptokiObject_Equal((*oa)->object, object)) {
00941            cachedOA = *oa;
00942            break;
00943        }
00944     }
00945     if (!cachedOA) {
00946        goto loser; /* don't have this object */
00947     }
00948     if (arenaOpt) {
00949        arena = arenaOpt;
00950        mark = nssArena_Mark(arena);
00951     }
00952     for (i=0; i<atlen; i++) {
00953        for (j=0; j<cachedOA->numAttributes; j++) {
00954            if (atemplate[i].type == cachedOA->attributes[j].type) {
00955               CK_ATTRIBUTE_PTR attr = &cachedOA->attributes[j];
00956               if (cachedOA->attributes[j].ulValueLen == 0 ||
00957                   cachedOA->attributes[j].ulValueLen == (CK_ULONG)-1) 
00958               {
00959                   break; /* invalid attribute */
00960               }
00961               if (atemplate[i].ulValueLen > 0) {
00962                   if (atemplate[i].pValue == NULL ||
00963                       atemplate[i].ulValueLen < attr->ulValueLen) 
00964                   {
00965                      goto loser;
00966                   }
00967               } else {
00968                   atemplate[i].pValue = nss_ZAlloc(arena, attr->ulValueLen);
00969                   if (!atemplate[i].pValue) {
00970                      goto loser;
00971                   }
00972               }
00973               nsslibc_memcpy(atemplate[i].pValue,
00974                              attr->pValue, attr->ulValueLen);
00975               atemplate[i].ulValueLen = attr->ulValueLen;
00976               break;
00977            }
00978        }
00979        if (j == cachedOA->numAttributes) {
00980            atemplate[i].ulValueLen = (CK_ULONG)-1;
00981        }
00982     }
00983     PZ_Unlock(cache->lock);
00984     if (mark) {
00985        nssArena_Unmark(arena, mark);
00986     }
00987     return PR_SUCCESS;
00988 loser:
00989     PZ_Unlock(cache->lock);
00990     if (mark) {
00991        nssArena_Release(arena, mark);
00992     }
00993     return PR_FAILURE;
00994 }
00995 
00996 NSS_IMPLEMENT PRStatus
00997 nssTokenObjectCache_ImportObject (
00998   nssTokenObjectCache *cache,
00999   nssCryptokiObject *object,
01000   CK_OBJECT_CLASS objclass,
01001   CK_ATTRIBUTE_PTR ot,
01002   CK_ULONG otlen
01003 )
01004 {
01005     PRStatus status = PR_SUCCESS;
01006     PRUint32 count;
01007     nssCryptokiObjectAndAttributes **oa, ***otype;
01008     PRUint32 objectType;
01009     PRBool haveIt = PR_FALSE;
01010 
01011     if (!token_is_present(cache)) {
01012        return PR_SUCCESS; /* cache not active, ignored */
01013     }
01014     PZ_Lock(cache->lock);
01015     switch (objclass) {
01016     case CKO_CERTIFICATE:    objectType = cachedCerts; break;
01017     case CKO_NETSCAPE_TRUST: objectType = cachedTrust; break;
01018     case CKO_NETSCAPE_CRL:   objectType = cachedCRLs;  break;
01019     default:
01020        PZ_Unlock(cache->lock);
01021        return PR_SUCCESS; /* don't need to import it here */
01022     }
01023     if (!cache_available_for_object_type(cache, objectType)) {
01024        PZ_Unlock(cache->lock);
01025        return PR_SUCCESS; /* cache not active, ignored */
01026     }
01027     count = 0;
01028     otype = &cache->objects[objectType]; /* index into array of types */
01029     oa = *otype; /* the array of objects for this type */
01030     while (oa && *oa) {
01031        if (nssCryptokiObject_Equal((*oa)->object, object)) {
01032            haveIt = PR_TRUE;
01033            break;
01034        }
01035        count++;
01036        oa++;
01037     }
01038     if (haveIt) {
01039        /* Destroy the old entry */
01040        (*oa)->object->token = NULL;
01041        nssCryptokiObject_Destroy((*oa)->object);
01042        nssArena_Destroy((*oa)->arena);
01043     } else {
01044        /* Create space for a new entry */
01045        if (count > 0) {
01046            *otype = nss_ZREALLOCARRAY(*otype,
01047                                       nssCryptokiObjectAndAttributes *, 
01048                                       count + 2);
01049        } else {
01050            *otype = nss_ZNEWARRAY(NULL, nssCryptokiObjectAndAttributes *, 2);
01051        }
01052     }
01053     if (*otype) {
01054        nssCryptokiObject *copyObject = nssCryptokiObject_Clone(object);
01055        if (objectType == cachedCerts) {
01056            (*otype)[count] = create_cert(copyObject, &status);
01057        } else if (objectType == cachedTrust) {
01058            (*otype)[count] = create_trust(copyObject, &status);
01059        } else if (objectType == cachedCRLs) {
01060            (*otype)[count] = create_crl(copyObject, &status);
01061        }
01062     } else {
01063        status = PR_FAILURE;
01064     }
01065     PZ_Unlock(cache->lock);
01066     return status;
01067 }
01068 
01069 NSS_IMPLEMENT void
01070 nssTokenObjectCache_RemoveObject (
01071   nssTokenObjectCache *cache,
01072   nssCryptokiObject *object
01073 )
01074 {
01075     PRUint32 oType;
01076     nssCryptokiObjectAndAttributes **oa, **swp = NULL;
01077     if (!token_is_present(cache)) {
01078        return;
01079     }
01080     PZ_Lock(cache->lock);
01081     for (oType=0; oType<3; oType++) {
01082        if (!cache_available_for_object_type(cache, oType) ||
01083            !cache->objects[oType])
01084        {
01085            continue;
01086        }
01087        for (oa = cache->objects[oType]; *oa; oa++) {
01088            if (nssCryptokiObject_Equal((*oa)->object, object)) {
01089               swp = oa; /* the entry to remove */
01090               while (oa[1]) oa++; /* go to the tail */
01091               (*swp)->object->token = NULL;
01092               nssCryptokiObject_Destroy((*swp)->object);
01093               nssArena_Destroy((*swp)->arena); /* destroy it */
01094               *swp = *oa; /* swap the last with the removed */
01095               *oa = NULL; /* null-terminate the array */
01096               break;
01097            }
01098        }
01099        if (swp) {
01100            break;
01101        }
01102     }
01103     if ((oType <3) &&
01104               cache->objects[oType] && cache->objects[oType][0] == NULL) {
01105        nss_ZFreeIf(cache->objects[oType]); /* no entries remaining */
01106        cache->objects[oType] = NULL;
01107     }
01108     PZ_Unlock(cache->lock);
01109 }
01110 
01111 /* These two hash algorithms are presently sufficient.
01112 ** They are used for fingerprints of certs which are stored as the 
01113 ** CKA_CERT_SHA1_HASH and CKA_CERT_MD5_HASH attributes.
01114 ** We don't need to add SHAxxx to these now.
01115 */
01116 /* XXX of course this doesn't belong here */
01117 NSS_IMPLEMENT NSSAlgorithmAndParameters *
01118 NSSAlgorithmAndParameters_CreateSHA1Digest (
01119   NSSArena *arenaOpt
01120 )
01121 {
01122     NSSAlgorithmAndParameters *rvAP = NULL;
01123     rvAP = nss_ZNEW(arenaOpt, NSSAlgorithmAndParameters);
01124     if (rvAP) {
01125        rvAP->mechanism.mechanism = CKM_SHA_1;
01126        rvAP->mechanism.pParameter = NULL;
01127        rvAP->mechanism.ulParameterLen = 0;
01128     }
01129     return rvAP;
01130 }
01131 
01132 NSS_IMPLEMENT NSSAlgorithmAndParameters *
01133 NSSAlgorithmAndParameters_CreateMD5Digest (
01134   NSSArena *arenaOpt
01135 )
01136 {
01137     NSSAlgorithmAndParameters *rvAP = NULL;
01138     rvAP = nss_ZNEW(arenaOpt, NSSAlgorithmAndParameters);
01139     if (rvAP) {
01140        rvAP->mechanism.mechanism = CKM_MD5;
01141        rvAP->mechanism.pParameter = NULL;
01142        rvAP->mechanism.ulParameterLen = 0;
01143     }
01144     return rvAP;
01145 }
01146