tor-browser

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

dbtool.c (25412B)


      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 /*
      6 ** dbtool.c
      7 **
      8 ** tool to dump the underlying encoding of a database. This tool duplicates
      9 **  some private functions in softoken. It uses libsec and libutil, but no
     10 **  other portions of NSS. It currently only works on sqlite databases. For
     11 **  an even more primitive dump, use sqlite3 on the individual files.
     12 **
     13 **   TODO: dump the meta data for the databases.
     14 **         optionally dump more PKCS5 information (KDF/salt/iterations)
     15 **         take a password and decode encrypted attributes/verify signed
     16 **             attributes.
     17 */
     18 #include <stdio.h>
     19 #include <string.h>
     20 
     21 #if defined(WIN32)
     22 #include "fcntl.h"
     23 #include "io.h"
     24 #endif
     25 
     26 /*#include "secutil.h" */
     27 /*#include "pk11pub.h" */
     28 
     29 #if defined(XP_UNIX)
     30 #include <unistd.h>
     31 #endif
     32 
     33 #include "nspr.h"
     34 #include "prtypes.h"
     35 #include "nss.h"
     36 #include "secasn1.h"
     37 #include "secder.h"
     38 #include "pk11table.h"
     39 #include "sftkdbt.h"
     40 #include "sdb.h"
     41 #include "secoid.h"
     42 
     43 #include "plgetopt.h"
     44 
     45 static char *progName;
     46 
     47 char *dbDir = NULL;
     48 
     49 static void
     50 Usage()
     51 {
     52    printf("Usage:  %s [-c certprefix] [-k keyprefix] "
     53           "[-V certversion] [-v keyversion]\n"
     54           "           [-d dbdir]\n",
     55           progName);
     56    printf("%-20s Directory with cert database (default is .)\n",
     57           "-d certdir");
     58    printf("%-20s prefix for the cert database (default is \"\")\n",
     59           "-c certprefix");
     60    printf("%-20s prefix for the key database (default is \"\")\n",
     61           "-k keyprefix");
     62    printf("%-20s version of the cert database (default is 9)\n",
     63           "-V certversion");
     64    printf("%-20s version of the key database (default is 4)\n",
     65           "-v keyversion");
     66    exit(1);
     67 }
     68 #define SFTK_KEYDB_TYPE 0x40000000
     69 #define SFTK_TOKEN_TYPE 0x80000000
     70 
     71 /*
     72 * known attributes
     73 */
     74 extern const CK_ATTRIBUTE_TYPE sftkdb_known_attributes[];
     75 extern size_t sftkdb_known_attributes_size;
     76 
     77 PRBool
     78 isULONGAttribute(CK_ATTRIBUTE_TYPE type)
     79 {
     80    switch (type) {
     81        case CKA_CERTIFICATE_CATEGORY:
     82        case CKA_CERTIFICATE_TYPE:
     83        case CKA_CLASS:
     84        case CKA_JAVA_MIDP_SECURITY_DOMAIN:
     85        case CKA_KEY_GEN_MECHANISM:
     86        case CKA_KEY_TYPE:
     87        case CKA_MECHANISM_TYPE:
     88        case CKA_MODULUS_BITS:
     89        case CKA_PRIME_BITS:
     90        case CKA_SUBPRIME_BITS:
     91        case CKA_VALUE_BITS:
     92        case CKA_VALUE_LEN:
     93 
     94        case CKA_NSS_TRUST_DIGITAL_SIGNATURE:
     95        case CKA_NSS_TRUST_NON_REPUDIATION:
     96        case CKA_NSS_TRUST_KEY_ENCIPHERMENT:
     97        case CKA_NSS_TRUST_DATA_ENCIPHERMENT:
     98        case CKA_NSS_TRUST_KEY_AGREEMENT:
     99        case CKA_NSS_TRUST_KEY_CERT_SIGN:
    100        case CKA_NSS_TRUST_CRL_SIGN:
    101 
    102        case CKA_NSS_TRUST_SERVER_AUTH:
    103        case CKA_NSS_TRUST_CLIENT_AUTH:
    104        case CKA_NSS_TRUST_CODE_SIGNING:
    105        case CKA_NSS_TRUST_EMAIL_PROTECTION:
    106        case CKA_NSS_TRUST_IPSEC_END_SYSTEM:
    107        case CKA_NSS_TRUST_IPSEC_TUNNEL:
    108        case CKA_NSS_TRUST_IPSEC_USER:
    109        case CKA_NSS_TRUST_TIME_STAMPING:
    110        case CKA_PKCS_TRUST_SERVER_AUTH:
    111        case CKA_PKCS_TRUST_CLIENT_AUTH:
    112        case CKA_PKCS_TRUST_CODE_SIGNING:
    113        case CKA_PKCS_TRUST_EMAIL_PROTECTION:
    114        case CKA_TRUST_IPSEC_IKE:
    115        case CKA_PKCS_TRUST_TIME_STAMPING:
    116        case CKA_NAME_HASH_ALGORITHM:
    117            return PR_TRUE;
    118        default:
    119            break;
    120    }
    121    return PR_FALSE;
    122 }
    123 
    124 /* are the attributes private? */
    125 static PRBool
    126 isPrivateAttribute(CK_ATTRIBUTE_TYPE type)
    127 {
    128    switch (type) {
    129        case CKA_VALUE:
    130        case CKA_PRIVATE_EXPONENT:
    131        case CKA_PRIME_1:
    132        case CKA_PRIME_2:
    133        case CKA_EXPONENT_1:
    134        case CKA_EXPONENT_2:
    135        case CKA_COEFFICIENT:
    136            return PR_TRUE;
    137        default:
    138            break;
    139    }
    140    return PR_FALSE;
    141 }
    142 
    143 /* These attributes must be authenticated with an hmac. */
    144 static PRBool
    145 isAuthenticatedAttribute(CK_ATTRIBUTE_TYPE type)
    146 {
    147    switch (type) {
    148        case CKA_MODULUS:
    149        case CKA_PUBLIC_EXPONENT:
    150        case CKA_NSS_CERT_SHA1_HASH:
    151        case CKA_NSS_CERT_MD5_HASH:
    152        case CKA_NSS_TRUST_SERVER_AUTH:
    153        case CKA_NSS_TRUST_CLIENT_AUTH:
    154        case CKA_NSS_TRUST_EMAIL_PROTECTION:
    155        case CKA_NSS_TRUST_CODE_SIGNING:
    156        case CKA_HASH_OF_CERTIFICATE:
    157        case CKA_NAME_HASH_ALGORITHM:
    158        case CKA_PKCS_TRUST_SERVER_AUTH:
    159        case CKA_PKCS_TRUST_CLIENT_AUTH:
    160        case CKA_PKCS_TRUST_EMAIL_PROTECTION:
    161        case CKA_PKCS_TRUST_CODE_SIGNING:
    162        case CKA_NSS_OVERRIDE_EXTENSIONS:
    163            return PR_TRUE;
    164        default:
    165            break;
    166    }
    167    return PR_FALSE;
    168 }
    169 
    170 /*
    171 * convert a database ulong back to a native ULONG. (reverse of the above
    172 * function.
    173 */
    174 static CK_ULONG
    175 sdbULong2ULong(unsigned char *data)
    176 {
    177    int i;
    178    CK_ULONG value = 0;
    179 
    180    for (i = 0; i < SDB_ULONG_SIZE; i++) {
    181        value |= (((CK_ULONG)data[i]) << (SDB_ULONG_SIZE - 1 - i) * PR_BITS_PER_BYTE);
    182    }
    183    return value;
    184 }
    185 
    186 /* PBE defines and functions */
    187 SEC_ASN1_MKSUB(SECOID_AlgorithmIDTemplate)
    188 
    189 typedef struct EncryptedDataInfoStr {
    190    SECAlgorithmID algorithm;
    191    SECItem encryptedData;
    192 } EncryptedDataInfo;
    193 
    194 static const SEC_ASN1Template encryptedDataInfoTemplate[] = {
    195    { SEC_ASN1_SEQUENCE,
    196      0, NULL, sizeof(EncryptedDataInfo) },
    197    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
    198      offsetof(EncryptedDataInfo, algorithm),
    199      SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
    200    { SEC_ASN1_OCTET_STRING,
    201      offsetof(EncryptedDataInfo, encryptedData) },
    202    { 0 }
    203 };
    204 
    205 typedef struct PBEParameterStr {
    206    SECAlgorithmID prfAlg;
    207    SECItem salt;
    208    SECItem iteration;
    209    SECItem keyLength;
    210 } PBEParameter;
    211 
    212 static const SEC_ASN1Template pkcs5V1PBEParameterTemplate[] = {
    213    { SEC_ASN1_SEQUENCE,
    214      0, NULL, sizeof(PBEParameter) },
    215    { SEC_ASN1_OCTET_STRING,
    216      offsetof(PBEParameter, salt) },
    217    { SEC_ASN1_INTEGER,
    218      offsetof(PBEParameter, iteration) },
    219    { 0 }
    220 };
    221 
    222 static const SEC_ASN1Template pkcs12V2PBEParameterTemplate[] = {
    223    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(PBEParameter) },
    224    { SEC_ASN1_OCTET_STRING, offsetof(PBEParameter, salt) },
    225    { SEC_ASN1_INTEGER, offsetof(PBEParameter, iteration) },
    226    { 0 }
    227 };
    228 
    229 static const SEC_ASN1Template pkcs5V2PBEParameterTemplate[] = {
    230    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(PBEParameter) },
    231    /* this is really a choice, but since we don't understand any other
    232     * choice, just inline it. */
    233    { SEC_ASN1_OCTET_STRING, offsetof(PBEParameter, salt) },
    234    { SEC_ASN1_INTEGER, offsetof(PBEParameter, iteration) },
    235    { SEC_ASN1_INTEGER, offsetof(PBEParameter, keyLength) },
    236    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
    237      offsetof(PBEParameter, prfAlg),
    238      SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
    239    { 0 }
    240 };
    241 
    242 typedef struct Pkcs5v2PBEParameterStr {
    243    SECAlgorithmID keyParams; /* parameters of the key generation */
    244    SECAlgorithmID algParams; /* parameters for the encryption or mac op */
    245 } Pkcs5v2PBEParameter;
    246 
    247 static const SEC_ASN1Template pkcs5v2PBES2ParameterTemplate[] = {
    248    { SEC_ASN1_SEQUENCE, 0, NULL, sizeof(Pkcs5v2PBEParameter) },
    249    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
    250      offsetof(Pkcs5v2PBEParameter, keyParams),
    251      SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
    252    { SEC_ASN1_INLINE | SEC_ASN1_XTRN,
    253      offsetof(Pkcs5v2PBEParameter, algParams),
    254      SEC_ASN1_SUB(SECOID_AlgorithmIDTemplate) },
    255    { 0 }
    256 };
    257 
    258 static inline PRBool
    259 isPKCS12PBE(SECOidTag alg)
    260 {
    261    switch (alg) {
    262        case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_2KEY_TRIPLE_DES_CBC:
    263        case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_3KEY_TRIPLE_DES_CBC:
    264        case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC2_CBC:
    265        case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC2_CBC:
    266        case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_128_BIT_RC4:
    267        case SEC_OID_PKCS12_V2_PBE_WITH_SHA1_AND_40_BIT_RC4:
    268            return PR_TRUE;
    269        default:
    270            break;
    271    }
    272    return PR_FALSE;
    273 }
    274 
    275 /* helper functions */
    276 
    277 /* output an NSS specific attribute or name that wasn't found in our
    278 * pkcs #11 table */
    279 const char *
    280 makeNSSVendorName(CK_ATTRIBUTE_TYPE attribute, const char *nameType)
    281 {
    282    static char nss_name[256];
    283    const char *name = NULL;
    284    if ((attribute >= CKA_NSS) && (attribute < 0xffffffffUL)) {
    285        snprintf(nss_name, sizeof(nss_name), "%s+%d", nameType, (int)(attribute - CKA_NSS));
    286        name = nss_name;
    287    }
    288    return name;
    289 }
    290 
    291 /*  turn and attribute into a name */
    292 const char *
    293 AttributeName(CK_ATTRIBUTE_TYPE attribute)
    294 {
    295    const char *name = getNameFromAttribute(attribute);
    296    if (!name) {
    297        name = makeNSSVendorName(attribute, "CKA_NSS");
    298    }
    299 
    300    return name ? name : "UNKNOWN_ATTRIBUTE_TYPE";
    301 }
    302 
    303 /*  turn and error code into a name */
    304 const char *
    305 ErrorName(CK_RV crv)
    306 {
    307    const char *error = getName(crv, ConstResult);
    308    if (!error) {
    309        error = makeNSSVendorName(crv, "CKR_NSS");
    310    }
    311    return error ? error : "UNKNOWN_ERROR";
    312 }
    313 
    314 /* turn an oud tag into a string */
    315 const char *
    316 oid2string(SECOidTag alg)
    317 {
    318    const char *oidstring = SECOID_FindOIDTagDescription(alg);
    319    const char *def = "Invalid oid tag"; /* future build a dotted oid string value here */
    320    return oidstring ? oidstring : def;
    321 }
    322 
    323 /* dump an arbitary data blob. Dump it has hex with ascii on the side */
    324 #define ASCCHAR(val) ((val) >= ' ' && (val) <= 0x7e ? (val) : '.')
    325 #define LINE_LENGTH 16
    326 void
    327 dumpValue(const unsigned char *v, int len)
    328 {
    329    int i, next = 0;
    330    char string[LINE_LENGTH + 1];
    331    char space[LINE_LENGTH * 2 + 1];
    332    char *nl = "";
    333    char *sp = "";
    334    PORT_Memset(string, 0, sizeof(string));
    335 
    336    for (i = 0; i < len; i++) {
    337        if ((i % LINE_LENGTH) == 0) {
    338            printf("%s%s%s        ", sp, string, nl);
    339            PORT_Memset(string, 0, sizeof(string));
    340            next = 0;
    341            nl = "\n";
    342            sp = " ";
    343        }
    344        printf("%02x", v[i]);
    345        string[next++] = ASCCHAR(v[i]);
    346    }
    347    PORT_Memset(space, 0, sizeof(space));
    348    i = LINE_LENGTH - (len % LINE_LENGTH);
    349    if (i != LINE_LENGTH) {
    350        int j;
    351        for (j = 0; j < i; j++) {
    352            space[j * 2] = ' ';
    353            space[j * 2 + 1] = ' ';
    354        }
    355    }
    356    printf("%s%s%s%s", space, sp, string, nl);
    357 }
    358 
    359 /* dump a PKCS5/12 PBE blob */
    360 void
    361 dumpPKCS(unsigned char *val, CK_ULONG len, PRBool *hasSig)
    362 {
    363    EncryptedDataInfo edi;
    364    SECStatus rv;
    365    SECItem data;
    366    PLArenaPool *arena;
    367    SECOidTag alg, prfAlg;
    368    PBEParameter pbeParam;
    369    unsigned char zero = 0;
    370    const SEC_ASN1Template *template = pkcs5V1PBEParameterTemplate;
    371    int iter, keyLen, i;
    372 
    373    if (hasSig) {
    374        *hasSig = PR_FALSE;
    375    }
    376 
    377    data.data = val;
    378    data.len = len;
    379    arena = PORT_NewArena(DER_DEFAULT_CHUNKSIZE);
    380    if (arena == NULL) {
    381        printf("Couldn't allocate arena\n");
    382        return;
    383    }
    384 
    385    /* initialize default values */
    386    PORT_Memset(&pbeParam, 0, sizeof(pbeParam));
    387    pbeParam.keyLength.data = &zero;
    388    pbeParam.keyLength.len = sizeof(zero);
    389    SECOID_SetAlgorithmID(arena, &pbeParam.prfAlg, SEC_OID_SHA1, NULL);
    390 
    391    /* first crack the encrypted data from the PBE algorithm ID */
    392    rv = SEC_QuickDERDecodeItem(arena, &edi, encryptedDataInfoTemplate, &data);
    393    if (rv != SECSuccess) {
    394        printf("Encrypted Data, failed to decode\n");
    395        dumpValue(val, len);
    396        PORT_FreeArena(arena, PR_FALSE);
    397        return;
    398    }
    399    /* now use the pbe secalg to dump info on the pbe */
    400    alg = SECOID_GetAlgorithmTag(&edi.algorithm);
    401    if ((alg == SEC_OID_PKCS5_PBES2) || (alg == SEC_OID_PKCS5_PBMAC1)) {
    402        Pkcs5v2PBEParameter param;
    403        SECOidTag palg;
    404        const char *typeName = (alg == SEC_OID_PKCS5_PBES2) ? "Encrypted Data PBES2" : "Mac Data PBMAC1";
    405 
    406        rv = SEC_QuickDERDecodeItem(arena, &param,
    407                                    pkcs5v2PBES2ParameterTemplate,
    408                                    &edi.algorithm.parameters);
    409        if (rv != SECSuccess) {
    410            printf("%s, failed to decode\n", typeName);
    411            dumpValue(val, len);
    412            PORT_FreeArena(arena, PR_FALSE);
    413            return;
    414        }
    415        palg = SECOID_GetAlgorithmTag(&param.algParams);
    416        printf("%s alg=%s ", typeName, oid2string(palg));
    417        if (hasSig && palg == SEC_OID_AES_256_CBC) {
    418            *hasSig = PR_TRUE;
    419        }
    420        template = pkcs5V2PBEParameterTemplate;
    421        edi.algorithm.parameters = param.keyParams.parameters;
    422    } else {
    423        printf("Encrypted Data alg=%s ", oid2string(alg));
    424        if (alg == SEC_OID_PKCS5_PBKDF2) {
    425            template = pkcs5V2PBEParameterTemplate;
    426        } else if (isPKCS12PBE(alg)) {
    427            template = pkcs12V2PBEParameterTemplate;
    428        } else {
    429            template = pkcs5V1PBEParameterTemplate;
    430        }
    431    }
    432    rv = SEC_QuickDERDecodeItem(arena, &pbeParam,
    433                                template,
    434                                &edi.algorithm.parameters);
    435    if (rv != SECSuccess) {
    436        printf("( failed to decode params)\n");
    437        PORT_FreeArena(arena, PR_FALSE);
    438        return;
    439    }
    440    /* dump the pbe parmeters */
    441    iter = DER_GetInteger(&pbeParam.iteration);
    442    keyLen = DER_GetInteger(&pbeParam.keyLength);
    443    prfAlg = SECOID_GetAlgorithmTag(&pbeParam.prfAlg);
    444    printf("(prf=%s iter=%d keyLen=%d salt=0x",
    445           oid2string(prfAlg), iter, keyLen);
    446    for (i = 0; i < pbeParam.salt.len; i++)
    447        printf("%02x", pbeParam.salt.data[i]);
    448    printf(")\n");
    449    /* finally dump the raw encrypted data */
    450    dumpValue(edi.encryptedData.data, edi.encryptedData.len);
    451    PORT_FreeArena(arena, PR_FALSE);
    452 }
    453 
    454 /* dump a long attribute, convert to an unsigned long. PKCS #11 Longs are
    455 * limited to 32 bits by the spec, even if the CK_ULONG is longer */
    456 void
    457 dumpLongAttribute(CK_ATTRIBUTE_TYPE type, CK_ULONG value)
    458 {
    459    const char *nameType = "CK_NSS";
    460    ConstType constType = ConstNone;
    461    const char *valueName = NULL;
    462 
    463    switch (type) {
    464        case CKA_CLASS:
    465            nameType = "CKO_NSS";
    466            constType = ConstObject;
    467            break;
    468        case CKA_CERTIFICATE_TYPE:
    469            nameType = "CKC_NSS";
    470            constType = ConstCertType;
    471            break;
    472        case CKA_KEY_TYPE:
    473            nameType = "CKK_NSS";
    474            constType = ConstKeyType;
    475            break;
    476        case CKA_MECHANISM_TYPE:
    477        case CKA_NAME_HASH_ALGORITHM:
    478            nameType = "CKM_NSS";
    479            constType = ConstMechanism;
    480            break;
    481        case CKA_NSS_TRUST_SERVER_AUTH:
    482        case CKA_NSS_TRUST_CLIENT_AUTH:
    483        case CKA_NSS_TRUST_CODE_SIGNING:
    484        case CKA_NSS_TRUST_EMAIL_PROTECTION:
    485        case CKA_NSS_TRUST_IPSEC_END_SYSTEM:
    486        case CKA_NSS_TRUST_IPSEC_TUNNEL:
    487        case CKA_NSS_TRUST_IPSEC_USER:
    488        case CKA_NSS_TRUST_TIME_STAMPING:
    489            nameType = "CKT_NSS";
    490            constType = ConstTrust;
    491            break;
    492        case CKA_PKCS_TRUST_SERVER_AUTH:
    493        case CKA_PKCS_TRUST_CLIENT_AUTH:
    494        case CKA_PKCS_TRUST_CODE_SIGNING:
    495        case CKA_PKCS_TRUST_EMAIL_PROTECTION:
    496        case CKA_TRUST_IPSEC_IKE:
    497        case CKA_PKCS_TRUST_TIME_STAMPING:
    498            nameType = "CKT_";
    499            constType = ConstTrust;
    500            break;
    501        default:
    502            break;
    503    }
    504    /* if value has a symbolic name, use it */
    505    if (constType != ConstNone) {
    506        valueName = getName(value, constType);
    507    }
    508    if (!valueName) {
    509        valueName = makeNSSVendorName(value, nameType);
    510    }
    511    if (!valueName) {
    512        printf("%d (0x%08x)\n", (int)value, (int)value);
    513    } else {
    514        printf("%s (0x%08x)\n", valueName, (int)value);
    515    }
    516 }
    517 
    518 /* dump a signature for an object */
    519 static const char META_SIG_TEMPLATE[] = "sig_%s_%08x_%08x";
    520 void
    521 dumpSignature(CK_ATTRIBUTE_TYPE attribute, SDB *keydb, PRBool isKey,
    522              CK_OBJECT_HANDLE objectID, PRBool force)
    523 {
    524    char id[30];
    525    CK_RV crv;
    526    SECItem signText;
    527    unsigned char signData[SDB_MAX_META_DATA_LEN];
    528 
    529    if (!force && !isAuthenticatedAttribute(attribute)) {
    530        return;
    531    }
    532    snprintf(id, sizeof(id), META_SIG_TEMPLATE,
    533             isKey ? "key" : "cert",
    534             (unsigned int)objectID, (unsigned int)attribute);
    535    printf("        Signature %s:", id);
    536    signText.data = signData;
    537    signText.len = sizeof(signData);
    538 
    539    crv = (*keydb->sdb_GetMetaData)(keydb, id, &signText, NULL);
    540    if ((crv != CKR_OK) && isKey) {
    541        snprintf(id, sizeof(id), META_SIG_TEMPLATE,
    542                 isKey ? "key" : "cert", (unsigned int)(objectID | SFTK_KEYDB_TYPE | SFTK_TOKEN_TYPE),
    543                 (unsigned int)attribute);
    544        crv = (*keydb->sdb_GetMetaData)(keydb, id, &signText, NULL);
    545    }
    546    if (crv != CKR_OK) {
    547        printf(" FAILED %s with %s (0x%08x)\n", id, ErrorName(crv), (int)crv);
    548        return;
    549    }
    550    dumpPKCS(signText.data, signText.len, NULL);
    551    return;
    552 }
    553 
    554 /* dump an attribute. use the helper functions above */
    555 void
    556 dumpAttribute(CK_ATTRIBUTE *template, SDB *keydb, PRBool isKey,
    557              CK_OBJECT_HANDLE id)
    558 {
    559    CK_ATTRIBUTE_TYPE attribute = template->type;
    560    printf("      %s(0x%08x): ", AttributeName(attribute), (int)attribute);
    561    if (template->pValue == NULL) {
    562        printf("NULL (%d)\n", (int)template->ulValueLen);
    563        return;
    564    }
    565    if (template->ulValueLen == SDB_ULONG_SIZE && isULONGAttribute(attribute)) {
    566        CK_ULONG value = sdbULong2ULong(template->pValue);
    567        dumpLongAttribute(attribute, value);
    568        return;
    569    }
    570    if (template->ulValueLen == 1) {
    571        unsigned char val = *(unsigned char *)template->pValue;
    572        switch (val) {
    573            case 0:
    574                printf("CK_FALSE\n");
    575                break;
    576            case 1:
    577                printf("CK_TRUE\n");
    578                break;
    579            default:
    580                printf("%d 0x%02x %c\n", val, val, ASCCHAR(val));
    581                break;
    582        }
    583        return;
    584    }
    585    if (isKey && isPrivateAttribute(attribute)) {
    586        PRBool hasSig = PR_FALSE;
    587        dumpPKCS(template->pValue, template->ulValueLen, &hasSig);
    588        if (hasSig) {
    589            dumpSignature(attribute, keydb, isKey, id, PR_TRUE);
    590        }
    591        return;
    592    }
    593    if (template->ulValueLen == 0) {
    594        printf("empty");
    595    }
    596    printf("\n");
    597    dumpValue(template->pValue, template->ulValueLen);
    598 }
    599 
    600 /* dump all the attributes in an object */
    601 void
    602 dumpObject(CK_OBJECT_HANDLE id, SDB *db, SDB *keydb, PRBool isKey)
    603 {
    604    CK_RV crv;
    605    size_t i;
    606    CK_ATTRIBUTE template;
    607    char buffer[2048];
    608    char *alloc = NULL;
    609 
    610    printf("  Object 0x%08x:\n", (int)id);
    611    for (i = 0; i < sftkdb_known_attributes_size; i++) {
    612        CK_ATTRIBUTE_TYPE attribute = sftkdb_known_attributes[i];
    613        template.type = attribute;
    614        template.pValue = NULL;
    615        template.ulValueLen = 0;
    616        crv = (*db->sdb_GetAttributeValue)(db, id, &template, 1);
    617 
    618        if (crv != CKR_OK) {
    619            if (crv != CKR_ATTRIBUTE_TYPE_INVALID) {
    620                PR_fprintf(PR_STDERR, "    "
    621                                      "Get Attribute %s (0x%08x):FAILED\"%s\"(0x%08x)\n",
    622                           AttributeName(attribute), (int)attribute,
    623                           ErrorName(crv), (int)crv);
    624            }
    625            continue;
    626        }
    627 
    628        if (template.ulValueLen < sizeof(buffer)) {
    629            template.pValue = buffer;
    630        } else {
    631            alloc = PORT_Alloc(template.ulValueLen);
    632            template.pValue = alloc;
    633        }
    634        if (template.pValue == NULL) {
    635            PR_fprintf(PR_STDERR, "    "
    636                                  "Could allocate %d bytes for  Attribute %s (0x%08x)\n",
    637                       (int)template.ulValueLen,
    638                       AttributeName(attribute), (int)attribute);
    639            continue;
    640        }
    641        crv = (*db->sdb_GetAttributeValue)(db, id, &template, 1);
    642 
    643        if (crv != CKR_OK) {
    644            if (crv != CKR_ATTRIBUTE_TYPE_INVALID) {
    645                PR_fprintf(PR_STDERR, "    "
    646                                      "Get Attribute %s (0x%08x):FAILED\"%s\"(0x%08x)\n",
    647                           AttributeName(attribute), (int)attribute,
    648                           ErrorName(crv), (int)crv);
    649            }
    650            if (alloc) {
    651                PORT_Free(alloc);
    652                alloc = NULL;
    653            }
    654            continue;
    655        }
    656 
    657        dumpAttribute(&template, keydb, isKey, id);
    658        dumpSignature(template.type, keydb, isKey, id, PR_FALSE);
    659        if (alloc) {
    660            PORT_Free(alloc);
    661            alloc = NULL;
    662        }
    663    }
    664 }
    665 
    666 /* dump all the objects in a database */
    667 void
    668 dumpDB(SDB *db, const char *name, SDB *keydb, PRBool isKey)
    669 {
    670    SDBFind *findHandle = NULL;
    671    CK_BBOOL isTrue = 1;
    672    CK_ATTRIBUTE allObjectTemplate = { CKA_TOKEN, NULL, 1 };
    673    CK_ULONG allObjectTemplateCount = 1;
    674    PRBool recordFound = PR_FALSE;
    675    CK_RV crv = CKR_OK;
    676    CK_ULONG objectCount = 0;
    677    printf("%s:\n", name);
    678 
    679    allObjectTemplate.pValue = &isTrue;
    680    crv = (*db->sdb_FindObjectsInit)(db, &allObjectTemplate,
    681                                     allObjectTemplateCount, &findHandle);
    682    do {
    683        CK_OBJECT_HANDLE id;
    684        recordFound = PR_FALSE;
    685        crv = (*db->sdb_FindObjects)(db, findHandle, &id, 1, &objectCount);
    686        if ((crv == CKR_OK) && (objectCount == 1)) {
    687            recordFound = PR_TRUE;
    688            dumpObject(id, db, keydb, isKey);
    689        }
    690    } while (recordFound);
    691    if (crv != CKR_OK) {
    692        PR_fprintf(PR_STDERR,
    693                   "Last record return PKCS #11 error = %s (0x%08x)\n",
    694                   ErrorName(crv), (int)crv);
    695    }
    696    (*db->sdb_FindObjectsFinal)(db, findHandle);
    697 }
    698 
    699 static char *
    700 secu_ConfigDirectory(const char *base)
    701 {
    702    static PRBool initted = PR_FALSE;
    703    const char *dir = ".netscape";
    704    char *home;
    705    static char buf[1000];
    706 
    707    if (initted)
    708        return buf;
    709 
    710    if (base == NULL || *base == 0) {
    711        home = PR_GetEnvSecure("HOME");
    712        if (!home)
    713            home = "";
    714 
    715        if (*home && home[strlen(home) - 1] == '/')
    716            snprintf(buf, sizeof(buf), "%.900s%s", home, dir);
    717        else
    718            snprintf(buf, sizeof(buf), "%.900s/%s", home, dir);
    719    } else {
    720        snprintf(buf, sizeof(buf), "%.900s", base);
    721        if (buf[strlen(buf) - 1] == '/')
    722            buf[strlen(buf) - 1] = 0;
    723    }
    724 
    725    initted = PR_TRUE;
    726    return buf;
    727 }
    728 
    729 int
    730 main(int argc, char **argv)
    731 {
    732    PLOptState *optstate;
    733    PLOptStatus optstatus;
    734    char *certPrefix = "", *keyPrefix = "";
    735    int cert_version = 9;
    736    int key_version = 4;
    737    SDB *certdb = NULL;
    738    SDB *keydb = NULL;
    739    PRBool isNew = PR_FALSE;
    740 
    741    CK_RV crv;
    742 
    743    progName = strrchr(argv[0], '/');
    744    if (!progName)
    745        progName = strrchr(argv[0], '\\');
    746    progName = progName ? progName + 1 : argv[0];
    747 
    748    optstate = PL_CreateOptState(argc, argv, "d:c:k:v:V:h");
    749 
    750    while ((optstatus = PL_GetNextOpt(optstate)) == PL_OPT_OK) {
    751        switch (optstate->option) {
    752            case 'h':
    753            default:
    754                Usage();
    755                break;
    756 
    757            case 'd':
    758                dbDir = PORT_Strdup(optstate->value);
    759                break;
    760 
    761            case 'c':
    762                certPrefix = PORT_Strdup(optstate->value);
    763                break;
    764 
    765            case 'k':
    766                keyPrefix = PORT_Strdup(optstate->value);
    767                break;
    768 
    769            case 'v':
    770                key_version = atoi(optstate->value);
    771                break;
    772 
    773            case 'V':
    774                cert_version = atoi(optstate->value);
    775                break;
    776        }
    777    }
    778    PL_DestroyOptState(optstate);
    779    if (optstatus == PL_OPT_BAD)
    780        Usage();
    781 
    782    if (dbDir) {
    783        char *tmp = dbDir;
    784        dbDir = secu_ConfigDirectory(tmp);
    785        PORT_Free(tmp);
    786    } else {
    787        dbDir = secu_ConfigDirectory(NULL);
    788    }
    789    PR_fprintf(PR_STDERR, "dbdir selected is %s\n\n", dbDir);
    790 
    791    if (dbDir[0] == '\0') {
    792        PR_fprintf(PR_STDERR,
    793                   "ERROR: Directory \"%s\" does not exist.\n", dbDir);
    794        return 1;
    795    }
    796 
    797    PR_Init(PR_USER_THREAD, PR_PRIORITY_NORMAL, 0);
    798    SECOID_Init();
    799 
    800    crv = s_open(dbDir, certPrefix, keyPrefix, cert_version, key_version,
    801                 SDB_RDONLY, &certdb, &keydb, &isNew);
    802    if (crv != CKR_OK) {
    803        PR_fprintf(PR_STDERR,
    804                   "Couldn't open databased in %s, error=%s (0x%08x)\n",
    805                   dbDir, ErrorName(crv), (int)crv);
    806        return 1;
    807    }
    808 
    809    /* now dump the objects in the cert database */
    810    dumpDB(certdb, "CertDB", keydb, PR_FALSE);
    811    dumpDB(keydb, "KeyDB", keydb, PR_TRUE);
    812 
    813    crv = sdb_Close(certdb);
    814    if (crv != CKR_OK) {
    815        PR_fprintf(PR_STDERR,
    816                   "Couldn't close cert database in %s, error=%s (0x%08x)\n",
    817                   dbDir, ErrorName(crv), (int)crv);
    818    }
    819 
    820    crv = sdb_Close(keydb);
    821    if (crv != CKR_OK) {
    822        PR_fprintf(PR_STDERR,
    823                   "Couldn't close key database in %s, error=%s (0x%08x)\n",
    824                   dbDir, ErrorName(crv), (int)crv);
    825    }
    826 
    827    crv = s_shutdown();
    828    if (crv != CKR_OK) {
    829        PR_fprintf(PR_STDERR,
    830                   "Error in s_shutdown, error=%s (0x%08x)\n",
    831                   ErrorName(crv), (int)crv);
    832    }
    833    return 0;
    834 }