tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

pkibase.c (35962B)


      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 #ifndef DEV_H
      6 #include "dev.h"
      7 #endif /* DEV_H */
      8 
      9 #ifndef PKIM_H
     10 #include "pkim.h"
     11 #endif /* PKIM_H */
     12 
     13 #include "pki3hack.h"
     14 
     15 extern const NSSError NSS_ERROR_NOT_FOUND;
     16 
     17 NSS_IMPLEMENT void
     18 nssPKIObject_Lock(nssPKIObject *object)
     19 {
     20    switch (object->lockType) {
     21        case nssPKIMonitor:
     22            PZ_EnterMonitor(object->sync.mlock);
     23            break;
     24        case nssPKILock:
     25            PZ_Lock(object->sync.lock);
     26            break;
     27        default:
     28            PORT_Assert(0);
     29    }
     30 }
     31 
     32 NSS_IMPLEMENT void
     33 nssPKIObject_Unlock(nssPKIObject *object)
     34 {
     35    switch (object->lockType) {
     36        case nssPKIMonitor:
     37            PZ_ExitMonitor(object->sync.mlock);
     38            break;
     39        case nssPKILock:
     40            PZ_Unlock(object->sync.lock);
     41            break;
     42        default:
     43            PORT_Assert(0);
     44    }
     45 }
     46 
     47 NSS_IMPLEMENT PRStatus
     48 nssPKIObject_NewLock(nssPKIObject *object, nssPKILockType lockType)
     49 {
     50    object->lockType = lockType;
     51    switch (lockType) {
     52        case nssPKIMonitor:
     53            object->sync.mlock = PZ_NewMonitor(nssILockSSL);
     54            return (object->sync.mlock ? PR_SUCCESS : PR_FAILURE);
     55        case nssPKILock:
     56            object->sync.lock = PZ_NewLock(nssILockSSL);
     57            return (object->sync.lock ? PR_SUCCESS : PR_FAILURE);
     58        default:
     59            PORT_Assert(0);
     60            return PR_FAILURE;
     61    }
     62 }
     63 
     64 NSS_IMPLEMENT void
     65 nssPKIObject_DestroyLock(nssPKIObject *object)
     66 {
     67    switch (object->lockType) {
     68        case nssPKIMonitor:
     69            PZ_DestroyMonitor(object->sync.mlock);
     70            object->sync.mlock = NULL;
     71            break;
     72        case nssPKILock:
     73            PZ_DestroyLock(object->sync.lock);
     74            object->sync.lock = NULL;
     75            break;
     76        default:
     77            PORT_Assert(0);
     78    }
     79 }
     80 
     81 NSS_IMPLEMENT nssPKIObject *
     82 nssPKIObject_Create(
     83    NSSArena *arenaOpt,
     84    nssCryptokiObject *instanceOpt,
     85    NSSTrustDomain *td,
     86    NSSCryptoContext *cc,
     87    nssPKILockType lockType)
     88 {
     89    NSSArena *arena;
     90    nssArenaMark *mark = NULL;
     91    nssPKIObject *object;
     92    if (arenaOpt) {
     93        arena = arenaOpt;
     94        mark = nssArena_Mark(arena);
     95    } else {
     96        arena = nssArena_Create();
     97        if (!arena) {
     98            return (nssPKIObject *)NULL;
     99        }
    100    }
    101    object = nss_ZNEW(arena, nssPKIObject);
    102    if (!object) {
    103        goto loser;
    104    }
    105    object->arena = arena;
    106    object->trustDomain = td; /* XXX */
    107    object->cryptoContext = cc;
    108    if (PR_SUCCESS != nssPKIObject_NewLock(object, lockType)) {
    109        goto loser;
    110    }
    111    if (instanceOpt) {
    112        if (nssPKIObject_AddInstance(object, instanceOpt) != PR_SUCCESS) {
    113            goto loser;
    114        }
    115    }
    116    PR_ATOMIC_INCREMENT(&object->refCount);
    117    if (mark) {
    118        nssArena_Unmark(arena, mark);
    119    }
    120    return object;
    121 loser:
    122    if (mark) {
    123        nssArena_Release(arena, mark);
    124    } else {
    125        nssArena_Destroy(arena);
    126    }
    127    return (nssPKIObject *)NULL;
    128 }
    129 
    130 NSS_IMPLEMENT PRBool
    131 nssPKIObject_Destroy(
    132    nssPKIObject *object)
    133 {
    134    PRUint32 i;
    135    PR_ASSERT(object->refCount > 0);
    136    if (PR_ATOMIC_DECREMENT(&object->refCount) == 0) {
    137        for (i = 0; i < object->numInstances; i++) {
    138            nssCryptokiObject_Destroy(object->instances[i]);
    139        }
    140        nssPKIObject_DestroyLock(object);
    141        nssArena_Destroy(object->arena);
    142        return PR_TRUE;
    143    }
    144    return PR_FALSE;
    145 }
    146 
    147 NSS_IMPLEMENT nssPKIObject *
    148 nssPKIObject_AddRef(
    149    nssPKIObject *object)
    150 {
    151    PR_ATOMIC_INCREMENT(&object->refCount);
    152    return object;
    153 }
    154 
    155 NSS_IMPLEMENT PRStatus
    156 nssPKIObject_AddInstance(
    157    nssPKIObject *object,
    158    nssCryptokiObject *instance)
    159 {
    160    nssCryptokiObject **newInstances = NULL;
    161 
    162    nssPKIObject_Lock(object);
    163    if (object->numInstances == 0) {
    164        newInstances = nss_ZNEWARRAY(object->arena,
    165                                     nssCryptokiObject *,
    166                                     object->numInstances + 1);
    167    } else {
    168        PRBool found = PR_FALSE;
    169        PRUint32 i;
    170        for (i = 0; i < object->numInstances; i++) {
    171            if (nssCryptokiObject_Equal(object->instances[i], instance)) {
    172                found = PR_TRUE;
    173                break;
    174            }
    175        }
    176        if (found) {
    177            /* The new instance is identical to one in the array, except
    178             * perhaps that the label may be different.  So replace
    179             * the label in the array instance with the label from the
    180             * new instance, and discard the new instance.
    181             */
    182            nss_ZFreeIf(object->instances[i]->label);
    183            object->instances[i]->label = instance->label;
    184            nssPKIObject_Unlock(object);
    185            instance->label = NULL;
    186            nssCryptokiObject_Destroy(instance);
    187            return PR_SUCCESS;
    188        }
    189        newInstances = nss_ZREALLOCARRAY(object->instances,
    190                                         nssCryptokiObject *,
    191                                         object->numInstances + 1);
    192    }
    193    if (newInstances) {
    194        object->instances = newInstances;
    195        newInstances[object->numInstances++] = instance;
    196    }
    197    nssPKIObject_Unlock(object);
    198    return (newInstances ? PR_SUCCESS : PR_FAILURE);
    199 }
    200 
    201 NSS_IMPLEMENT PRBool
    202 nssPKIObject_HasInstance(
    203    nssPKIObject *object,
    204    nssCryptokiObject *instance)
    205 {
    206    PRUint32 i;
    207    PRBool hasIt = PR_FALSE;
    208    ;
    209    nssPKIObject_Lock(object);
    210    for (i = 0; i < object->numInstances; i++) {
    211        if (nssCryptokiObject_Equal(object->instances[i], instance)) {
    212            hasIt = PR_TRUE;
    213            break;
    214        }
    215    }
    216    nssPKIObject_Unlock(object);
    217    return hasIt;
    218 }
    219 
    220 NSS_IMPLEMENT PRStatus
    221 nssPKIObject_RemoveInstanceForToken(
    222    nssPKIObject *object,
    223    NSSToken *token)
    224 {
    225    PRUint32 i;
    226    nssCryptokiObject *instanceToRemove = NULL;
    227    nssPKIObject_Lock(object);
    228    if (object->numInstances == 0) {
    229        nssPKIObject_Unlock(object);
    230        return PR_SUCCESS;
    231    }
    232    for (i = 0; i < object->numInstances; i++) {
    233        if (object->instances[i]->token == token) {
    234            instanceToRemove = object->instances[i];
    235            object->instances[i] = object->instances[object->numInstances - 1];
    236            object->instances[object->numInstances - 1] = NULL;
    237            break;
    238        }
    239    }
    240    if (--object->numInstances > 0) {
    241        nssCryptokiObject **instances = nss_ZREALLOCARRAY(object->instances,
    242                                                          nssCryptokiObject *,
    243                                                          object->numInstances);
    244        if (instances) {
    245            object->instances = instances;
    246        }
    247    } else {
    248        nss_ZFreeIf(object->instances);
    249    }
    250    nssCryptokiObject_Destroy(instanceToRemove);
    251    nssPKIObject_Unlock(object);
    252    return PR_SUCCESS;
    253 }
    254 
    255 /* this needs more thought on what will happen when there are multiple
    256 * instances
    257 */
    258 NSS_IMPLEMENT PRStatus
    259 nssPKIObject_DeleteStoredObject(
    260    nssPKIObject *object,
    261    NSSCallback *uhh,
    262    PRBool isFriendly)
    263 {
    264    PRUint32 i, numNotDestroyed;
    265    PRStatus status = PR_SUCCESS;
    266    numNotDestroyed = 0;
    267    nssPKIObject_Lock(object);
    268    for (i = 0; i < object->numInstances; i++) {
    269        nssCryptokiObject *instance = object->instances[i];
    270        status = nssToken_DeleteStoredObject(instance);
    271        object->instances[i] = NULL;
    272        if (status == PR_SUCCESS) {
    273            nssCryptokiObject_Destroy(instance);
    274        } else {
    275            object->instances[numNotDestroyed++] = instance;
    276        }
    277    }
    278    if (numNotDestroyed == 0) {
    279        nss_ZFreeIf(object->instances);
    280        object->numInstances = 0;
    281    } else {
    282        object->numInstances = numNotDestroyed;
    283    }
    284    nssPKIObject_Unlock(object);
    285    return status;
    286 }
    287 
    288 NSS_IMPLEMENT NSSToken **
    289 nssPKIObject_GetTokens(
    290    nssPKIObject *object,
    291    PRStatus *statusOpt)
    292 {
    293    NSSToken **tokens = NULL;
    294    nssPKIObject_Lock(object);
    295    if (object->numInstances > 0) {
    296        tokens = nss_ZNEWARRAY(NULL, NSSToken *, object->numInstances + 1);
    297        if (tokens) {
    298            PRUint32 i;
    299            for (i = 0; i < object->numInstances; i++) {
    300                tokens[i] = nssToken_AddRef(object->instances[i]->token);
    301            }
    302        }
    303    }
    304    nssPKIObject_Unlock(object);
    305    if (statusOpt)
    306        *statusOpt = PR_SUCCESS; /* until more logic here */
    307    return tokens;
    308 }
    309 
    310 NSS_IMPLEMENT NSSUTF8 *
    311 nssPKIObject_GetNicknameForToken(
    312    nssPKIObject *object,
    313    NSSToken *tokenOpt)
    314 {
    315    PRUint32 i;
    316    NSSUTF8 *nickname = NULL;
    317    nssPKIObject_Lock(object);
    318    for (i = 0; i < object->numInstances; i++) {
    319        if ((!tokenOpt && object->instances[i]->label) ||
    320            (object->instances[i]->token == tokenOpt)) {
    321            /* Must copy, see bug 745548 */
    322            nickname = nssUTF8_Duplicate(object->instances[i]->label, NULL);
    323            break;
    324        }
    325    }
    326    nssPKIObject_Unlock(object);
    327    return nickname;
    328 }
    329 
    330 NSS_IMPLEMENT nssCryptokiObject **
    331 nssPKIObject_GetInstances(
    332    nssPKIObject *object)
    333 {
    334    nssCryptokiObject **instances = NULL;
    335    PRUint32 i;
    336 
    337    nssPKIObject_Lock(object);
    338    if (object->numInstances == 0) {
    339        nssPKIObject_Unlock(object);
    340        return (nssCryptokiObject **)NULL;
    341    }
    342    instances = nss_ZNEWARRAY(NULL, nssCryptokiObject *,
    343                              object->numInstances + 1);
    344    if (instances) {
    345        for (i = 0; i < object->numInstances; i++) {
    346            instances[i] = nssCryptokiObject_Clone(object->instances[i]);
    347        }
    348    }
    349    nssPKIObject_Unlock(object);
    350    return instances;
    351 }
    352 
    353 NSS_IMPLEMENT void
    354 nssCertificateArray_Destroy(
    355    NSSCertificate **certs)
    356 {
    357    if (certs) {
    358        NSSCertificate **certp;
    359        for (certp = certs; *certp; certp++) {
    360            if ((*certp)->decoding) {
    361                CERTCertificate *cc = STAN_GetCERTCertificate(*certp);
    362                if (cc) {
    363                    CERT_DestroyCertificate(cc);
    364                }
    365                continue;
    366            }
    367            nssCertificate_Destroy(*certp);
    368        }
    369        nss_ZFreeIf(certs);
    370    }
    371 }
    372 
    373 NSS_IMPLEMENT void
    374 NSSCertificateArray_Destroy(
    375    NSSCertificate **certs)
    376 {
    377    nssCertificateArray_Destroy(certs);
    378 }
    379 
    380 NSS_IMPLEMENT NSSCertificate **
    381 nssCertificateArray_Join(
    382    NSSCertificate **certs1,
    383    NSSCertificate **certs2)
    384 {
    385    if (certs1 && certs2) {
    386        NSSCertificate **certs, **cp;
    387        PRUint32 count = 0;
    388        PRUint32 count1 = 0;
    389        cp = certs1;
    390        while (*cp++)
    391            count1++;
    392        count = count1;
    393        cp = certs2;
    394        while (*cp++)
    395            count++;
    396        certs = nss_ZREALLOCARRAY(certs1, NSSCertificate *, count + 1);
    397        if (!certs) {
    398            nss_ZFreeIf(certs1);
    399            nss_ZFreeIf(certs2);
    400            return (NSSCertificate **)NULL;
    401        }
    402        for (cp = certs2; *cp; cp++, count1++) {
    403            certs[count1] = *cp;
    404        }
    405        nss_ZFreeIf(certs2);
    406        return certs;
    407    } else if (certs1) {
    408        return certs1;
    409    } else {
    410        return certs2;
    411    }
    412 }
    413 
    414 NSS_IMPLEMENT NSSCertificate *
    415 nssCertificateArray_FindBestCertificate(
    416    NSSCertificate **certs,
    417    NSSTime *timeOpt,
    418    const NSSUsage *usage,
    419    NSSPolicies *policiesOpt)
    420 {
    421    NSSCertificate *bestCert = NULL;
    422    nssDecodedCert *bestdc = NULL;
    423    NSSTime *time, sTime;
    424    PRBool bestCertMatches = PR_FALSE;
    425    PRBool thisCertMatches;
    426    PRBool bestCertIsValidAtTime = PR_FALSE;
    427    PRBool bestCertIsTrusted = PR_FALSE;
    428 
    429    if (timeOpt) {
    430        time = timeOpt;
    431    } else {
    432        NSSTime_Now(&sTime);
    433        time = &sTime;
    434    }
    435    if (!certs) {
    436        return (NSSCertificate *)NULL;
    437    }
    438    for (; *certs; certs++) {
    439        nssDecodedCert *dc;
    440        NSSCertificate *c = *certs;
    441        dc = nssCertificate_GetDecoding(c);
    442        if (!dc)
    443            continue;
    444        thisCertMatches = dc->matchUsage(dc, usage);
    445        if (!bestCert) {
    446            /* always take the first cert, but remember whether or not
    447             * the usage matched
    448             */
    449            bestCert = nssCertificate_AddRef(c);
    450            bestCertMatches = thisCertMatches;
    451            bestdc = dc;
    452            continue;
    453        } else {
    454            if (bestCertMatches && !thisCertMatches) {
    455                /* if already have a cert for this usage, and if this cert
    456                 * doesn't have the correct usage, continue
    457                 */
    458                continue;
    459            } else if (!bestCertMatches && thisCertMatches) {
    460                /* this one does match usage, replace the other */
    461                nssCertificate_Destroy(bestCert);
    462                bestCert = nssCertificate_AddRef(c);
    463                bestCertMatches = thisCertMatches;
    464                bestdc = dc;
    465                continue;
    466            }
    467            /* this cert match as well as any cert we've found so far,
    468             * defer to time/policies
    469             * */
    470        }
    471        /* time */
    472        if (bestCertIsValidAtTime || bestdc->isValidAtTime(bestdc, time)) {
    473            /* The current best cert is valid at time */
    474            bestCertIsValidAtTime = PR_TRUE;
    475            if (!dc->isValidAtTime(dc, time)) {
    476                /* If the new cert isn't valid at time, it's not better */
    477                continue;
    478            }
    479        } else {
    480            /* The current best cert is not valid at time */
    481            if (dc->isValidAtTime(dc, time)) {
    482                /* If the new cert is valid at time, it's better */
    483                nssCertificate_Destroy(bestCert);
    484                bestCert = nssCertificate_AddRef(c);
    485                bestdc = dc;
    486                bestCertIsValidAtTime = PR_TRUE;
    487                continue;
    488            }
    489        }
    490        /* Either they are both valid at time, or neither valid.
    491         * If only one is trusted for this usage, take it.
    492         */
    493        if (bestCertIsTrusted || bestdc->isTrustedForUsage(bestdc, usage)) {
    494            bestCertIsTrusted = PR_TRUE;
    495            if (!dc->isTrustedForUsage(dc, usage)) {
    496                continue;
    497            }
    498        } else {
    499            /* The current best cert is not trusted */
    500            if (dc->isTrustedForUsage(dc, usage)) {
    501                /* If the new cert is trusted, it's better */
    502                nssCertificate_Destroy(bestCert);
    503                bestCert = nssCertificate_AddRef(c);
    504                bestdc = dc;
    505                bestCertIsTrusted = PR_TRUE;
    506                continue;
    507            }
    508        }
    509        /* Otherwise, take the newer one. */
    510        if (!bestdc->isNewerThan(bestdc, dc)) {
    511            nssCertificate_Destroy(bestCert);
    512            bestCert = nssCertificate_AddRef(c);
    513            bestdc = dc;
    514            continue;
    515        }
    516        /* policies */
    517        /* XXX later -- defer to policies */
    518    }
    519    return bestCert;
    520 }
    521 
    522 NSS_IMPLEMENT PRStatus
    523 nssCertificateArray_Traverse(
    524    NSSCertificate **certs,
    525    PRStatus (*callback)(NSSCertificate *c, void *arg),
    526    void *arg)
    527 {
    528    PRStatus status = PR_SUCCESS;
    529    if (certs) {
    530        NSSCertificate **certp;
    531        for (certp = certs; *certp; certp++) {
    532            status = (*callback)(*certp, arg);
    533            if (status != PR_SUCCESS) {
    534                break;
    535            }
    536        }
    537    }
    538    return status;
    539 }
    540 
    541 NSS_IMPLEMENT void
    542 nssCRLArray_Destroy(
    543    NSSCRL **crls)
    544 {
    545    if (crls) {
    546        NSSCRL **crlp;
    547        for (crlp = crls; *crlp; crlp++) {
    548            nssCRL_Destroy(*crlp);
    549        }
    550        nss_ZFreeIf(crls);
    551    }
    552 }
    553 
    554 /*
    555 * Object collections
    556 */
    557 
    558 typedef enum {
    559    pkiObjectType_Certificate = 0,
    560    pkiObjectType_CRL = 1,
    561    pkiObjectType_PrivateKey = 2,
    562    pkiObjectType_PublicKey = 3
    563 } pkiObjectType;
    564 
    565 /* Each object is defined by a set of items that uniquely identify it.
    566 * Here are the uid sets:
    567 *
    568 * NSSCertificate ==>  { issuer, serial }
    569 * NSSPrivateKey
    570 *         (RSA) ==> { modulus, public exponent }
    571 *
    572 */
    573 #define MAX_ITEMS_FOR_UID 2
    574 
    575 /* pkiObjectCollectionNode
    576 *
    577 * A node in the collection is the set of unique identifiers for a single
    578 * object, along with either the actual object or a proto-object.
    579 */
    580 typedef struct
    581 {
    582    PRCList link;
    583    PRBool haveObject;
    584    nssPKIObject *object;
    585    NSSItem uid[MAX_ITEMS_FOR_UID];
    586 } pkiObjectCollectionNode;
    587 
    588 /* nssPKIObjectCollection
    589 *
    590 * The collection is the set of all objects, plus the interfaces needed
    591 * to manage the objects.
    592 *
    593 */
    594 struct nssPKIObjectCollectionStr {
    595    NSSArena *arena;
    596    NSSTrustDomain *td;
    597    NSSCryptoContext *cc;
    598    PRCList head; /* list of pkiObjectCollectionNode's */
    599    PRUint32 size;
    600    pkiObjectType objectType;
    601    void (*destroyObject)(nssPKIObject *o);
    602    PRStatus (*getUIDFromObject)(nssPKIObject *o, NSSItem *uid);
    603    PRStatus (*getUIDFromInstance)(nssCryptokiObject *co, NSSItem *uid,
    604                                   NSSArena *arena);
    605    nssPKIObject *(*createObject)(nssPKIObject *o);
    606    nssPKILockType lockType; /* type of lock to use for new proto-objects */
    607 };
    608 
    609 static nssPKIObjectCollection *
    610 nssPKIObjectCollection_Create(
    611    NSSTrustDomain *td,
    612    NSSCryptoContext *ccOpt,
    613    nssPKILockType lockType)
    614 {
    615    NSSArena *arena;
    616    nssPKIObjectCollection *rvCollection = NULL;
    617    arena = nssArena_Create();
    618    if (!arena) {
    619        return (nssPKIObjectCollection *)NULL;
    620    }
    621    rvCollection = nss_ZNEW(arena, nssPKIObjectCollection);
    622    if (!rvCollection) {
    623        goto loser;
    624    }
    625    PR_INIT_CLIST(&rvCollection->head);
    626    rvCollection->arena = arena;
    627    rvCollection->td = td; /* XXX */
    628    rvCollection->cc = ccOpt;
    629    rvCollection->lockType = lockType;
    630    return rvCollection;
    631 loser:
    632    nssArena_Destroy(arena);
    633    return (nssPKIObjectCollection *)NULL;
    634 }
    635 
    636 NSS_IMPLEMENT void
    637 nssPKIObjectCollection_Destroy(
    638    nssPKIObjectCollection *collection)
    639 {
    640    if (collection) {
    641        PRCList *link;
    642        pkiObjectCollectionNode *node;
    643        /* first destroy any objects in the collection */
    644        link = PR_NEXT_LINK(&collection->head);
    645        while (link != &collection->head) {
    646            node = (pkiObjectCollectionNode *)link;
    647            if (node->haveObject) {
    648                (*collection->destroyObject)(node->object);
    649            } else {
    650                nssPKIObject_Destroy(node->object);
    651            }
    652            link = PR_NEXT_LINK(link);
    653        }
    654        /* then destroy it */
    655        nssArena_Destroy(collection->arena);
    656    }
    657 }
    658 
    659 NSS_IMPLEMENT PRUint32
    660 nssPKIObjectCollection_Count(
    661    nssPKIObjectCollection *collection)
    662 {
    663    return collection->size;
    664 }
    665 
    666 NSS_IMPLEMENT PRStatus
    667 nssPKIObjectCollection_AddObject(
    668    nssPKIObjectCollection *collection,
    669    nssPKIObject *object)
    670 {
    671    pkiObjectCollectionNode *node;
    672    node = nss_ZNEW(collection->arena, pkiObjectCollectionNode);
    673    if (!node) {
    674        return PR_FAILURE;
    675    }
    676    node->haveObject = PR_TRUE;
    677    node->object = nssPKIObject_AddRef(object);
    678    (*collection->getUIDFromObject)(object, node->uid);
    679    PR_INIT_CLIST(&node->link);
    680    PR_INSERT_BEFORE(&node->link, &collection->head);
    681    collection->size++;
    682    return PR_SUCCESS;
    683 }
    684 
    685 static pkiObjectCollectionNode *
    686 find_instance_in_collection(
    687    nssPKIObjectCollection *collection,
    688    nssCryptokiObject *instance)
    689 {
    690    PRCList *link;
    691    pkiObjectCollectionNode *node;
    692    link = PR_NEXT_LINK(&collection->head);
    693    while (link != &collection->head) {
    694        node = (pkiObjectCollectionNode *)link;
    695        if (nssPKIObject_HasInstance(node->object, instance)) {
    696            return node;
    697        }
    698        link = PR_NEXT_LINK(link);
    699    }
    700    return (pkiObjectCollectionNode *)NULL;
    701 }
    702 
    703 static pkiObjectCollectionNode *
    704 find_object_in_collection(
    705    nssPKIObjectCollection *collection,
    706    NSSItem *uid)
    707 {
    708    PRUint32 i;
    709    PRStatus status;
    710    PRCList *link;
    711    pkiObjectCollectionNode *node;
    712    link = PR_NEXT_LINK(&collection->head);
    713    while (link != &collection->head) {
    714        node = (pkiObjectCollectionNode *)link;
    715        for (i = 0; i < MAX_ITEMS_FOR_UID; i++) {
    716            if (!nssItem_Equal(&node->uid[i], &uid[i], &status)) {
    717                break;
    718            }
    719        }
    720        if (i == MAX_ITEMS_FOR_UID) {
    721            return node;
    722        }
    723        link = PR_NEXT_LINK(link);
    724    }
    725    return (pkiObjectCollectionNode *)NULL;
    726 }
    727 
    728 static pkiObjectCollectionNode *
    729 add_object_instance(
    730    nssPKIObjectCollection *collection,
    731    nssCryptokiObject *instance,
    732    PRBool *foundIt)
    733 {
    734    PRUint32 i;
    735    PRStatus status;
    736    pkiObjectCollectionNode *node;
    737    nssArenaMark *mark = NULL;
    738    NSSItem uid[MAX_ITEMS_FOR_UID];
    739    nsslibc_memset(uid, 0, sizeof uid);
    740    /* The list is traversed twice, first (here) looking to match the
    741     * { token, handle } tuple, and if that is not found, below a search
    742     * for unique identifier is done.  Here, a match means this exact object
    743     * instance is already in the collection, and we have nothing to do.
    744     */
    745    *foundIt = PR_FALSE;
    746    node = find_instance_in_collection(collection, instance);
    747    if (node) {
    748        /* The collection is assumed to take over the instance.  Since we
    749         * are not using it, it must be destroyed.
    750         */
    751        nssCryptokiObject_Destroy(instance);
    752        *foundIt = PR_TRUE;
    753        return node;
    754    }
    755    mark = nssArena_Mark(collection->arena);
    756    if (!mark) {
    757        goto loser;
    758    }
    759    status = (*collection->getUIDFromInstance)(instance, uid,
    760                                               collection->arena);
    761    if (status != PR_SUCCESS) {
    762        goto loser;
    763    }
    764    /* Search for unique identifier.  A match here means the object exists
    765     * in the collection, but does not have this instance, so the instance
    766     * needs to be added.
    767     */
    768    node = find_object_in_collection(collection, uid);
    769    if (node) {
    770        /* This is an object with multiple instances */
    771        status = nssPKIObject_AddInstance(node->object, instance);
    772    } else {
    773        /* This is a completely new object.  Create a node for it. */
    774        node = nss_ZNEW(collection->arena, pkiObjectCollectionNode);
    775        if (!node) {
    776            goto loser;
    777        }
    778        node->object = nssPKIObject_Create(NULL, instance,
    779                                           collection->td, collection->cc,
    780                                           collection->lockType);
    781        if (!node->object) {
    782            goto loser;
    783        }
    784        for (i = 0; i < MAX_ITEMS_FOR_UID; i++) {
    785            node->uid[i] = uid[i];
    786        }
    787        node->haveObject = PR_FALSE;
    788        PR_INIT_CLIST(&node->link);
    789        PR_INSERT_BEFORE(&node->link, &collection->head);
    790        collection->size++;
    791        status = PR_SUCCESS;
    792    }
    793    nssArena_Unmark(collection->arena, mark);
    794    return node;
    795 loser:
    796    if (mark) {
    797        nssArena_Release(collection->arena, mark);
    798    }
    799    nssCryptokiObject_Destroy(instance);
    800    return (pkiObjectCollectionNode *)NULL;
    801 }
    802 
    803 NSS_IMPLEMENT PRStatus
    804 nssPKIObjectCollection_AddInstances(
    805    nssPKIObjectCollection *collection,
    806    nssCryptokiObject **instances,
    807    PRUint32 numInstances)
    808 {
    809    PRStatus status = PR_SUCCESS;
    810    PRUint32 i = 0;
    811    PRBool foundIt;
    812    pkiObjectCollectionNode *node;
    813    if (instances) {
    814        while ((!numInstances || i < numInstances) && *instances) {
    815            if (status == PR_SUCCESS) {
    816                node = add_object_instance(collection, *instances, &foundIt);
    817                if (node == NULL) {
    818                    /* add_object_instance freed the current instance */
    819                    /* free the remaining instances */
    820                    status = PR_FAILURE;
    821                }
    822            } else {
    823                nssCryptokiObject_Destroy(*instances);
    824            }
    825            instances++;
    826            i++;
    827        }
    828    }
    829    return status;
    830 }
    831 
    832 static void
    833 nssPKIObjectCollection_RemoveNode(
    834    nssPKIObjectCollection *collection,
    835    pkiObjectCollectionNode *node)
    836 {
    837    PR_REMOVE_LINK(&node->link);
    838    collection->size--;
    839 }
    840 
    841 static PRStatus
    842 nssPKIObjectCollection_GetObjects(
    843    nssPKIObjectCollection *collection,
    844    nssPKIObject **rvObjects,
    845    PRUint32 rvSize)
    846 {
    847    PRUint32 i = 0;
    848    PRCList *link = PR_NEXT_LINK(&collection->head);
    849    pkiObjectCollectionNode *node;
    850    int error = 0;
    851    while ((i < rvSize) && (link != &collection->head)) {
    852        node = (pkiObjectCollectionNode *)link;
    853        if (!node->haveObject) {
    854            /* Convert the proto-object to an object */
    855            node->object = (*collection->createObject)(node->object);
    856            if (!node->object) {
    857                link = PR_NEXT_LINK(link);
    858                /*remove bogus object from list*/
    859                nssPKIObjectCollection_RemoveNode(collection, node);
    860                error++;
    861                continue;
    862            }
    863            node->haveObject = PR_TRUE;
    864        }
    865        rvObjects[i++] = nssPKIObject_AddRef(node->object);
    866        link = PR_NEXT_LINK(link);
    867    }
    868    if (!error && *rvObjects == NULL) {
    869        nss_SetError(NSS_ERROR_NOT_FOUND);
    870    }
    871    return PR_SUCCESS;
    872 }
    873 
    874 NSS_IMPLEMENT PRStatus
    875 nssPKIObjectCollection_Traverse(
    876    nssPKIObjectCollection *collection,
    877    nssPKIObjectCallback *callback)
    878 {
    879    PRCList *link = PR_NEXT_LINK(&collection->head);
    880    pkiObjectCollectionNode *node;
    881    while (link != &collection->head) {
    882        node = (pkiObjectCollectionNode *)link;
    883        if (!node->haveObject) {
    884            node->object = (*collection->createObject)(node->object);
    885            if (!node->object) {
    886                link = PR_NEXT_LINK(link);
    887                /*remove bogus object from list*/
    888                nssPKIObjectCollection_RemoveNode(collection, node);
    889                continue;
    890            }
    891            node->haveObject = PR_TRUE;
    892        }
    893        switch (collection->objectType) {
    894            case pkiObjectType_Certificate:
    895                (void)(*callback->func.cert)((NSSCertificate *)node->object,
    896                                             callback->arg);
    897                break;
    898            case pkiObjectType_CRL:
    899                (void)(*callback->func.crl)((NSSCRL *)node->object,
    900                                            callback->arg);
    901                break;
    902            case pkiObjectType_PrivateKey:
    903                (void)(*callback->func.pvkey)((NSSPrivateKey *)node->object,
    904                                              callback->arg);
    905                break;
    906            case pkiObjectType_PublicKey:
    907                (void)(*callback->func.pbkey)((NSSPublicKey *)node->object,
    908                                              callback->arg);
    909                break;
    910        }
    911        link = PR_NEXT_LINK(link);
    912    }
    913    return PR_SUCCESS;
    914 }
    915 
    916 NSS_IMPLEMENT PRStatus
    917 nssPKIObjectCollection_AddInstanceAsObject(
    918    nssPKIObjectCollection *collection,
    919    nssCryptokiObject *instance)
    920 {
    921    pkiObjectCollectionNode *node;
    922    PRBool foundIt;
    923    node = add_object_instance(collection, instance, &foundIt);
    924    if (node == NULL) {
    925        return PR_FAILURE;
    926    }
    927    if (!node->haveObject) {
    928        nssPKIObject *original = node->object;
    929        node->object = (*collection->createObject)(node->object);
    930        if (!node->object) {
    931            /*remove bogus object from list*/
    932            nssPKIObject_Destroy(original);
    933            nssPKIObjectCollection_RemoveNode(collection, node);
    934            return PR_FAILURE;
    935        }
    936        node->haveObject = PR_TRUE;
    937    } else if (!foundIt) {
    938        /* The instance was added to a pre-existing node.  This
    939         * function is *only* being used for certificates, and having
    940         * multiple instances of certs in 3.X requires updating the
    941         * CERTCertificate.
    942         * But only do it if it was a new instance!!!  If the same instance
    943         * is encountered, we set *foundIt to true.  Detect that here and
    944         * ignore it.
    945         */
    946        STAN_ForceCERTCertificateUpdate((NSSCertificate *)node->object);
    947    }
    948    return PR_SUCCESS;
    949 }
    950 
    951 /*
    952 * Certificate collections
    953 */
    954 
    955 static void
    956 cert_destroyObject(nssPKIObject *o)
    957 {
    958    NSSCertificate *c = (NSSCertificate *)o;
    959    if (c->decoding) {
    960        CERTCertificate *cc = STAN_GetCERTCertificate(c);
    961        if (cc) {
    962            CERT_DestroyCertificate(cc);
    963            return;
    964        } /* else destroy it as NSSCertificate below */
    965    }
    966    nssCertificate_Destroy(c);
    967 }
    968 
    969 static PRStatus
    970 cert_getUIDFromObject(nssPKIObject *o, NSSItem *uid)
    971 {
    972    NSSCertificate *c = (NSSCertificate *)o;
    973    /* The builtins are still returning decoded serial numbers.  Until
    974     * this compatibility issue is resolved, use the full DER of the
    975     * cert to uniquely identify it.
    976     */
    977    NSSDER *derCert;
    978    derCert = nssCertificate_GetEncoding(c);
    979    uid[0].data = NULL;
    980    uid[0].size = 0;
    981    uid[1].data = NULL;
    982    uid[1].size = 0;
    983    if (derCert != NULL) {
    984        uid[0] = *derCert;
    985    }
    986    return PR_SUCCESS;
    987 }
    988 
    989 static PRStatus
    990 cert_getUIDFromInstance(nssCryptokiObject *instance, NSSItem *uid,
    991                        NSSArena *arena)
    992 {
    993    /* The builtins are still returning decoded serial numbers.  Until
    994     * this compatibility issue is resolved, use the full DER of the
    995     * cert to uniquely identify it.
    996     */
    997    uid[1].data = NULL;
    998    uid[1].size = 0;
    999    return nssCryptokiCertificate_GetAttributes(instance,
   1000                                                NULL,    /* XXX sessionOpt */
   1001                                                arena,   /* arena    */
   1002                                                NULL,    /* type     */
   1003                                                NULL,    /* id       */
   1004                                                &uid[0], /* encoding */
   1005                                                NULL,    /* issuer   */
   1006                                                NULL,    /* serial   */
   1007                                                NULL);   /* subject  */
   1008 }
   1009 
   1010 static nssPKIObject *
   1011 cert_createObject(nssPKIObject *o)
   1012 {
   1013    NSSCertificate *cert;
   1014    cert = nssCertificate_Create(o);
   1015    /*    if (STAN_GetCERTCertificate(cert) == NULL) {
   1016        nssCertificate_Destroy(cert);
   1017        return (nssPKIObject *)NULL;
   1018    } */
   1019    /* In 3.4, have to maintain uniqueness of cert pointers by caching all
   1020     * certs.  Cache the cert here, before returning.  If it is already
   1021     * cached, take the cached entry.
   1022     */
   1023    {
   1024        NSSTrustDomain *td = o->trustDomain;
   1025        nssTrustDomain_AddCertsToCache(td, &cert, 1);
   1026    }
   1027    return (nssPKIObject *)cert;
   1028 }
   1029 
   1030 NSS_IMPLEMENT nssPKIObjectCollection *
   1031 nssCertificateCollection_Create(
   1032    NSSTrustDomain *td,
   1033    NSSCertificate **certsOpt)
   1034 {
   1035    nssPKIObjectCollection *collection;
   1036    collection = nssPKIObjectCollection_Create(td, NULL, nssPKIMonitor);
   1037    if (!collection) {
   1038        return NULL;
   1039    }
   1040    collection->objectType = pkiObjectType_Certificate;
   1041    collection->destroyObject = cert_destroyObject;
   1042    collection->getUIDFromObject = cert_getUIDFromObject;
   1043    collection->getUIDFromInstance = cert_getUIDFromInstance;
   1044    collection->createObject = cert_createObject;
   1045    if (certsOpt) {
   1046        for (; *certsOpt; certsOpt++) {
   1047            nssPKIObject *object = (nssPKIObject *)(*certsOpt);
   1048            (void)nssPKIObjectCollection_AddObject(collection, object);
   1049        }
   1050    }
   1051    return collection;
   1052 }
   1053 
   1054 NSS_IMPLEMENT NSSCertificate **
   1055 nssPKIObjectCollection_GetCertificates(
   1056    nssPKIObjectCollection *collection,
   1057    NSSCertificate **rvOpt,
   1058    PRUint32 maximumOpt,
   1059    NSSArena *arenaOpt)
   1060 {
   1061    PRStatus status;
   1062    PRUint32 rvSize;
   1063    PRBool allocated = PR_FALSE;
   1064    if (collection->size == 0) {
   1065        return (NSSCertificate **)NULL;
   1066    }
   1067    if (maximumOpt == 0) {
   1068        rvSize = collection->size;
   1069    } else {
   1070        rvSize = PR_MIN(collection->size, maximumOpt);
   1071    }
   1072    if (!rvOpt) {
   1073        rvOpt = nss_ZNEWARRAY(arenaOpt, NSSCertificate *, rvSize + 1);
   1074        if (!rvOpt) {
   1075            return (NSSCertificate **)NULL;
   1076        }
   1077        allocated = PR_TRUE;
   1078    }
   1079    status = nssPKIObjectCollection_GetObjects(collection,
   1080                                               (nssPKIObject **)rvOpt,
   1081                                               rvSize);
   1082    if (status != PR_SUCCESS) {
   1083        if (allocated) {
   1084            nss_ZFreeIf(rvOpt);
   1085        }
   1086        return (NSSCertificate **)NULL;
   1087    }
   1088    return rvOpt;
   1089 }
   1090 
   1091 /*
   1092 * CRL/KRL collections
   1093 */
   1094 
   1095 static void
   1096 crl_destroyObject(nssPKIObject *o)
   1097 {
   1098    NSSCRL *crl = (NSSCRL *)o;
   1099    nssCRL_Destroy(crl);
   1100 }
   1101 
   1102 static PRStatus
   1103 crl_getUIDFromObject(nssPKIObject *o, NSSItem *uid)
   1104 {
   1105    NSSCRL *crl = (NSSCRL *)o;
   1106    NSSDER *encoding;
   1107    encoding = nssCRL_GetEncoding(crl);
   1108    if (!encoding) {
   1109        nss_SetError(NSS_ERROR_INVALID_ARGUMENT);
   1110        return PR_FALSE;
   1111    }
   1112    uid[0] = *encoding;
   1113    uid[1].data = NULL;
   1114    uid[1].size = 0;
   1115    return PR_SUCCESS;
   1116 }
   1117 
   1118 static PRStatus
   1119 crl_getUIDFromInstance(nssCryptokiObject *instance, NSSItem *uid,
   1120                       NSSArena *arena)
   1121 {
   1122    return nssCryptokiCRL_GetAttributes(instance,
   1123                                        NULL,    /* XXX sessionOpt */
   1124                                        arena,   /* arena    */
   1125                                        &uid[0], /* encoding */
   1126                                        NULL,    /* subject  */
   1127                                        NULL,    /* class    */
   1128                                        NULL,    /* url      */
   1129                                        NULL);   /* isKRL    */
   1130 }
   1131 
   1132 static nssPKIObject *
   1133 crl_createObject(nssPKIObject *o)
   1134 {
   1135    return (nssPKIObject *)nssCRL_Create(o);
   1136 }
   1137 
   1138 NSS_IMPLEMENT nssPKIObjectCollection *
   1139 nssCRLCollection_Create(
   1140    NSSTrustDomain *td,
   1141    NSSCRL **crlsOpt)
   1142 {
   1143    nssPKIObjectCollection *collection;
   1144    collection = nssPKIObjectCollection_Create(td, NULL, nssPKILock);
   1145    if (!collection) {
   1146        return NULL;
   1147    }
   1148    collection->objectType = pkiObjectType_CRL;
   1149    collection->destroyObject = crl_destroyObject;
   1150    collection->getUIDFromObject = crl_getUIDFromObject;
   1151    collection->getUIDFromInstance = crl_getUIDFromInstance;
   1152    collection->createObject = crl_createObject;
   1153    if (crlsOpt) {
   1154        for (; *crlsOpt; crlsOpt++) {
   1155            nssPKIObject *object = (nssPKIObject *)(*crlsOpt);
   1156            (void)nssPKIObjectCollection_AddObject(collection, object);
   1157        }
   1158    }
   1159    return collection;
   1160 }
   1161 
   1162 NSS_IMPLEMENT NSSCRL **
   1163 nssPKIObjectCollection_GetCRLs(
   1164    nssPKIObjectCollection *collection,
   1165    NSSCRL **rvOpt,
   1166    PRUint32 maximumOpt,
   1167    NSSArena *arenaOpt)
   1168 {
   1169    PRStatus status;
   1170    PRUint32 rvSize;
   1171    PRBool allocated = PR_FALSE;
   1172    if (collection->size == 0) {
   1173        return (NSSCRL **)NULL;
   1174    }
   1175    if (maximumOpt == 0) {
   1176        rvSize = collection->size;
   1177    } else {
   1178        rvSize = PR_MIN(collection->size, maximumOpt);
   1179    }
   1180    if (!rvOpt) {
   1181        rvOpt = nss_ZNEWARRAY(arenaOpt, NSSCRL *, rvSize + 1);
   1182        if (!rvOpt) {
   1183            return (NSSCRL **)NULL;
   1184        }
   1185        allocated = PR_TRUE;
   1186    }
   1187    status = nssPKIObjectCollection_GetObjects(collection,
   1188                                               (nssPKIObject **)rvOpt,
   1189                                               rvSize);
   1190    if (status != PR_SUCCESS) {
   1191        if (allocated) {
   1192            nss_ZFreeIf(rvOpt);
   1193        }
   1194        return (NSSCRL **)NULL;
   1195    }
   1196    return rvOpt;
   1197 }
   1198 
   1199 /* how bad would it be to have a static now sitting around, updated whenever
   1200 * this was called?  would avoid repeated allocs...
   1201 */
   1202 NSS_IMPLEMENT NSSTime *
   1203 NSSTime_Now(NSSTime *timeOpt)
   1204 {
   1205    return NSSTime_SetPRTime(timeOpt, PR_Now());
   1206 }
   1207 
   1208 NSS_IMPLEMENT NSSTime *
   1209 NSSTime_SetPRTime(
   1210    NSSTime *timeOpt,
   1211    PRTime prTime)
   1212 {
   1213    NSSTime *rvTime;
   1214    rvTime = (timeOpt) ? timeOpt : nss_ZNEW(NULL, NSSTime);
   1215    if (rvTime) {
   1216        rvTime->prTime = prTime;
   1217    }
   1218    return rvTime;
   1219 }
   1220 
   1221 NSS_IMPLEMENT PRTime
   1222 NSSTime_GetPRTime(
   1223    NSSTime *time)
   1224 {
   1225    return time->prTime;
   1226 }