tor-browser

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

devtoken.c (53438B)


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