<reponame>npocmaka/Windows-Server-2003
//+-------------------------------------------------------------------------
//
//  Microsoft Windows
//
//  Copyright (C) Microsoft Corporation, 1998 - 1999
//
//  File:       certexts.cpp
//
//  Contents:   NTSD/WINDBG certificate extensions dll
//
//              See DECLARE_API( help ) for a list of extensions
//
//
//  Functions:  help
//              store
//              context
//              ele
//              cert
//              crl
//              ctl
//              chain
//
//  History:    06-Jun-98   philh   created
//--------------------------------------------------------------------------

#include "global.hxx"

#define MAX_HASH_LEN  20
#define SHA1_HASH_LEN 20

#define MAX_PROP_CNT            100
#define MAX_CONTEXT_CNT         1000
#define MAX_STORE_SIBLING_CNT   100
#define MAX_STORE_NEST_CNT      100

#define MAX_SIMPLE_CHAIN_CNT    50
#define MAX_CHAIN_ELEMENT_CNT   50


WINDBG_EXTENSION_APIS ExtensionApis;
HANDLE ExtensionCurrentProcess;

// Display flags
#define DISPLAY_SHORT_FLAG          0x00000001
#define DISPLAY_VERBOSE_FLAG        0x00000002
#define DISPLAY_UI_FLAG             0x00000004
#define DISPLAY_EXT_FLAG            0x00000008
#define DISPLAY_PROP_FLAG           0x00000010

// ATTENTION: the following were lifted from newstor.cpp. Should be moved
// to a shared .h file

//+-------------------------------------------------------------------------
//  Store data structure definitions
//--------------------------------------------------------------------------

// Assumes
//  0 - Certificates
//  1 - CRLs
//  2 - CTLs
#define CONTEXT_COUNT       3

typedef struct _CONTEXT_ELEMENT CONTEXT_ELEMENT, *PCONTEXT_ELEMENT;
typedef struct _PROP_ELEMENT PROP_ELEMENT, *PPROP_ELEMENT;

typedef struct _CERT_STORE CERT_STORE, *PCERT_STORE;
typedef struct _CERT_STORE_LINK CERT_STORE_LINK, *PCERT_STORE_LINK;

typedef struct _COLLECTION_STACK_ENTRY COLLECTION_STACK_ENTRY,
    *PCOLLECTION_STACK_ENTRY;

// Used to maintain collection state across context find next calls.
//
// Ref count on pStoreLink. No ref count on pCollection.
// pStoreLink may be NULL.
struct _COLLECTION_STACK_ENTRY {
    PCERT_STORE                 pCollection;
    PCERT_STORE_LINK            pStoreLink;
    PCOLLECTION_STACK_ENTRY     pPrev;
};

typedef struct _CONTEXT_CACHE_INFO {
    PPROP_ELEMENT               pPropHead;
} CONTEXT_CACHE_INFO;

typedef struct _CONTEXT_EXTERNAL_INFO {
    // For ELEMENT_FIND_NEXT_FLAG
    void                        *pvProvInfo;
} CONTEXT_EXTERNAL_INFO;

typedef struct _CONTEXT_COLLECTION_INFO {
    // For Find
    PCOLLECTION_STACK_ENTRY     pCollectionStack;
} CONTEXT_COLLECTION_INFO;

#define ELEMENT_DELETED_FLAG                    0x00010000

// Only set for external elements
#define ELEMENT_FIND_NEXT_FLAG                  0x00020000

// Set during CertCloseStore if ELEMENT_FIND_NEXT_FLAG was set.
#define ELEMENT_CLOSE_FIND_NEXT_FLAG            0x00040000

// Set if the element has a CERT_ARCHIVED_PROP_ID
#define ELEMENT_ARCHIVED_FLAG                   0x00080000

#define ELEMENT_TYPE_CACHE                      1
#define ELEMENT_TYPE_LINK_CONTEXT               2
#define ELEMENT_TYPE_EXTERNAL                   3
#define ELEMENT_TYPE_COLLECTION                 4

typedef struct _CONTEXT_NOCOPY_INFO {
    PFN_CRYPT_FREE      pfnFree;
    void                *pvFree;
} CONTEXT_NOCOPY_INFO, *PCONTEXT_NOCOPY_INFO;

// Identical contexts (having the same SHA1 hash) can share the same encoded
// byte array and decoded info data structure.
//
// CreateShareElement() creates with dwRefCnt of 1. FindShareElement() finds
// an existing and increments dwRefCnt. ReleaseShareElement() decrements
// dwRefCnt and frees when 0.
typedef struct _SHARE_ELEMENT SHARE_ELEMENT, *PSHARE_ELEMENT;
struct _SHARE_ELEMENT {
    BYTE                rgbSha1Hash[SHA1_HASH_LEN];
    DWORD               dwContextType;
    BYTE                *pbEncoded;         // allocated
    DWORD               cbEncoded;
    void                *pvInfo;            // allocated

    DWORD               dwRefCnt;
    PSHARE_ELEMENT      pNext;
    PSHARE_ELEMENT      pPrev;
};

// The CONTEXT_ELEMENT is inserted before the CERT_CONTEXT, CRL_CONTEXT or
// CTL_CONTEXT. The dwContextType used is 0 based and not 1 based. For
// example, dwContextType = CERT_STORE_CERTIFICATE_CONTEXT - 1.
struct _CONTEXT_ELEMENT {
    DWORD               dwElementType;
    DWORD               dwContextType;
    DWORD               dwFlags;
    LONG                lRefCnt;

    // For ELEMENT_TYPE_CACHE, pEle points to itself. Otherwise, pEle points
    // to the element being linked to and the pEle is addRef'ed. The
    // cached element is found by iterating through the pEle's until pEle
    // points to itself.
    PCONTEXT_ELEMENT    pEle;
    PCERT_STORE         pStore;
    PCONTEXT_ELEMENT    pNext;
    PCONTEXT_ELEMENT    pPrev;
    PCERT_STORE         pProvStore;
    PCONTEXT_NOCOPY_INFO pNoCopyInfo;

    // When nonNULL, the context's pbEncoded and pInfo aren't allocated.
    // Instead, use the shared element's pbEncoded and pInfo. When
    // context element is freed, the pSharedEle is ReleaseShareElement()'ed.
    PSHARE_ELEMENT      pShareEle;          // RefCnt'ed

    union {
        CONTEXT_CACHE_INFO      Cache;      // ELEMENT_TYPE_CACHE
        CONTEXT_EXTERNAL_INFO   External;   // ELEMENT_TYPE_EXTERNAL
        CONTEXT_COLLECTION_INFO Collection; // ELEMENT_TYPE_COLLECTION
    };
};


struct _PROP_ELEMENT {
    DWORD               dwPropId;
    DWORD               dwFlags;
    BYTE                *pbData;
    DWORD               cbData;
    PPROP_ELEMENT       pNext;
    PPROP_ELEMENT       pPrev;
};


#define STORE_LINK_DELETED_FLAG        0x00010000
struct _CERT_STORE_LINK {
    DWORD               dwFlags;
    LONG                lRefCnt;

    // Whatever is passed to CertAddStoreToCollection
    DWORD               dwUpdateFlags;
    DWORD               dwPriority;

    PCERT_STORE         pCollection;
    PCERT_STORE         pSibling;       // CertStoreDuplicate'd.
    PCERT_STORE_LINK    pNext;
    PCERT_STORE_LINK    pPrev;
};


// Store types
#define STORE_TYPE_CACHE            1
#define STORE_TYPE_EXTERNAL         2
#define STORE_TYPE_COLLECTION       3

// CACHE store may have CACHE or LINK_CONTEXT elements. Until deleted,
// the store has a reference count to.

// EXTERNAL store only has EXTERNAL elements. These elements are always
// deleted, wherein, the store doesn't hold a refCnt.

// COLLECTION store has COLLECTION elements. These elements
// are always deleted, wherein, the store doesn't hold a refCnt.


struct _CERT_STORE {
    DWORD               dwStoreType;
    LONG                lRefCnt;
    HCRYPTPROV          hCryptProv;
    DWORD               dwFlags;
    DWORD               dwState;
    CRITICAL_SECTION    CriticalSection;
    PCONTEXT_ELEMENT    rgpContextListHead[CONTEXT_COUNT];
    PCERT_STORE_LINK    pStoreListHead;                     // COLLECTION
    PPROP_ELEMENT       pPropHead;      // properties for entire store

    // For CERT_STORE_DEFER_CLOSE_UNTIL_LAST_FREE_FLAG
    // Incremented for each context duplicated
    LONG                lDeferCloseRefCnt;

    // Event handle set by CertControlStore(CERT_STORE_CTRL_AUTO_RESYNC)
    HANDLE              hAutoResyncEvent;

    // Store provider info
    LONG                lStoreProvRefCnt;
    HANDLE              hStoreProvWait;
    HCRYPTOIDFUNCADDR   hStoreProvFuncAddr;
    CERT_STORE_PROV_INFO StoreProvInfo;
};

//+-------------------------------------------------------------------------
//  Store states
//--------------------------------------------------------------------------
#define STORE_STATE_DELETED         0
#define STORE_STATE_NULL            1
#define STORE_STATE_OPENING         2
#define STORE_STATE_OPEN            3
#define STORE_STATE_DEFER_CLOSING   4
#define STORE_STATE_CLOSING         5
#define STORE_STATE_CLOSED          6

inline PCONTEXT_ELEMENT ToContextElement(
    IN PCCERT_CONTEXT pCertContext
    )
{
    if (pCertContext)
        return (PCONTEXT_ELEMENT)
            (((BYTE *) pCertContext) - sizeof(CONTEXT_ELEMENT));
    else
        return NULL;
}

inline PCCERT_CONTEXT ToCertContext(
    IN PCONTEXT_ELEMENT pEle
    )
{
    if (pEle)
        return (PCCERT_CONTEXT)
            (((BYTE *) pEle) + sizeof(CONTEXT_ELEMENT));
    else
        return NULL;
}


DECLARE_API( help )
{
    INIT_API();

    while (*lpArgumentString == ' ')
        lpArgumentString++;

    if (*lpArgumentString == '\0') {
        dprintf("certexts help:\n\n");
        dprintf("!context {[s|v|u|x|p]} address - Dump cert, CRL or CTL context\n");
        dprintf("!ele {[s|v|u|x|p]} address     - Dump cert, CRL or CTL element\n");
        dprintf("!cert {[s|v|u|x|p]} address    - Dump encoded cert\n");
        dprintf("!crl {[s|v|u|x|p]} address     - Dump encoded crl\n");
        dprintf("!ctl {[s|v|u|x|p]} address     - Dump encoded ctl\n");
        dprintf("!store {[s|v|x|p]} address     - Dump certificate store\n");
        dprintf("!chain {[s|v|x|p]} address     - Dump certificate chain context\n");
        dprintf("!help [cmd]                    - Displays this list or gives details on command\n");
        dprintf("\n");
        dprintf("Options:\n");
        dprintf(" s - Short, brief display\n");
        dprintf(" v - Verbose display\n");
        dprintf(" u - UI display\n");
        dprintf(" x - Display eXtensions\n");
        dprintf(" p - Display Properties\n");
    } else {
        if (*lpArgumentString == '!')
            lpArgumentString++;
        if (strcmp(lpArgumentString, "xyz") == 0) {
            dprintf("!xyz      - Invalid command\n");
        } else {
            dprintf("Invalid command and/or no help available\n");
        }
    }
}

//+-------------------------------------------------------------------------
//  Error output routines
//--------------------------------------------------------------------------
void PrintError(LPCSTR pszMsg)
{
    dprintf("%s\n", pszMsg);
}
void PrintLastError(LPCSTR pszMsg)
{
    DWORD dwErr = GetLastError();
    dprintf("%s failed => 0x%x (%d) \n", pszMsg, dwErr, dwErr);
}

//+-------------------------------------------------------------------------
//  CertExts allocation and free routines
//--------------------------------------------------------------------------
LPVOID
WINAPI
CertExtsAlloc(
    IN UINT cbBytes
    )
{
    LPVOID pv;
    if (NULL == (pv = (LPVOID) LocalAlloc(LPTR, cbBytes))) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        PrintLastError("CertExtsAlloc");
    }
    return pv;
}

LPVOID
WINAPI
CertExtsRealloc(
    IN LPVOID pvOrg,
    IN UINT cbBytes
    )
{
    LPVOID pv;
    if (NULL == (pv = pvOrg ?
            (LPVOID) LocalReAlloc((HLOCAL)pvOrg, cbBytes, LMEM_MOVEABLE) :
            (LPVOID) LocalAlloc(NONZEROLPTR, cbBytes))) {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        PrintLastError("CertExtsRealloc");
    }
    return pv;
}

VOID
WINAPI
CertExtsFree(
    IN LPVOID pv
    )
{
    if (pv)
        LocalFree((HLOCAL)pv);
}

LPCSTR FileTimeText(FILETIME *pft)
{
    static char buf[80];
    FILETIME ftLocal;
    struct tm ctm;
    SYSTEMTIME st;

    FileTimeToLocalFileTime(pft, &ftLocal);
    if (FileTimeToSystemTime(&ftLocal, &st))
    {
        ctm.tm_sec = st.wSecond;
        ctm.tm_min = st.wMinute;
        ctm.tm_hour = st.wHour;
        ctm.tm_mday = st.wDay;
        ctm.tm_mon = st.wMonth-1;
        ctm.tm_year = st.wYear-1900;
        ctm.tm_wday = st.wDayOfWeek;
        ctm.tm_yday  = 0;
        ctm.tm_isdst = 0;
        strcpy(buf, asctime(&ctm));
        buf[strlen(buf)-1] = 0;

        if (st.wMilliseconds) {
            char *pch = buf + strlen(buf);
            sprintf(pch, " <milliseconds:: %03d>", st.wMilliseconds);
        }
    }
    else
        sprintf(buf, "<FILETIME %08lX:%08lX>", pft->dwHighDateTime,
                pft->dwLowDateTime);
    return buf;
}

#define CROW 16
void PrintBytes(LPCSTR pszHdr, BYTE *pb, DWORD cbSize)
{
    ULONG cb, i;

    if (cbSize == 0) {
        dprintf("%s NO Value Bytes\n", pszHdr);
        return;
    }

    while (cbSize > 0)
    {
        dprintf("%s", pszHdr);
        cb = min(CROW, cbSize);
        cbSize -= cb;
        for (i = 0; i<cb; i++)
            dprintf(" %02X", pb[i]);
        for (i = cb; i<CROW; i++)
            dprintf("   ");
        dprintf("    '");
        for (i = 0; i<cb; i++)
            if (pb[i] >= 0x20 && pb[i] <= 0x7f)
                dprintf("%c", pb[i]);
            else
                dprintf(".");
        pb += cb;
        dprintf("'\n");
    }
}


BOOL GetEncodedBlob(
    IN LPVOID pvAddr,
    OUT BYTE **ppbEncoded,
    OUT DWORD *pcbEncoded
    )
{
    BOOL fResult;
    BYTE rgbTagLength[6];
    BYTE *pbEncoded = NULL;
    DWORD cbEncoded = 0;

    LONG lLen;
    DWORD cbLen;
    DWORD cbContent;
    const BYTE *pbContent;

    __try {
        memset(rgbTagLength, 0, sizeof(rgbTagLength));
        ReadMemory(pvAddr, rgbTagLength, sizeof(rgbTagLength), NULL);
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        dprintf("Unable to read encoded blob at 0x%p\n", pvAddr);
        goto ErrorReturn;
    }

    lLen = Asn1UtilExtractContent(rgbTagLength, sizeof(rgbTagLength),
        &cbContent, &pbContent);
    if (lLen <= 0 || cbContent == CMSG_INDEFINITE_LENGTH) {
        dprintf("Unable to decode ASN1 tag/length at 0x%p\n", pvAddr);
        goto ErrorReturn;
    }

    cbEncoded = (DWORD)lLen + cbContent;
    if (NULL == (pbEncoded = (BYTE *) CertExtsAlloc(cbEncoded)))
        goto ErrorReturn;

    __try {
        memset(pbEncoded, 0, cbEncoded);
        ReadMemory(pvAddr, pbEncoded, cbEncoded, NULL);
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        dprintf("Unable to read encoded blob at 0x%p .. 0x%p\n", pvAddr,
            ((BYTE *) pvAddr) + cbEncoded - 1);
        goto ErrorReturn;
    }

    fResult = TRUE;
CommonReturn:
    *ppbEncoded = pbEncoded;
    *pcbEncoded = cbEncoded;
    return fResult;

ErrorReturn:
    CertExtsFree(pbEncoded);
    pbEncoded = NULL;
    cbEncoded = 0;
    fResult = FALSE;
    goto CommonReturn;
}

void DisplaySerialNumber(
    PCRYPT_INTEGER_BLOB pSerialNumber
    )
{
    DWORD cb;
    BYTE *pb;
    for (cb = pSerialNumber->cbData,
         pb = pSerialNumber->pbData + (cb - 1); cb > 0; cb--, pb--) {
        dprintf(" %02X", *pb);
    }
    dprintf("\n");
}

void DisplayThumbprint(
    LPCSTR pszHash,
    BYTE *pbHash,
    DWORD cbHash
    )
{
    dprintf("%s Thumbprint:: ", pszHash);
    if (cbHash == 0)
        dprintf("???");
    else {
        ULONG cb;

        while (cbHash > 0) {
            cb = min(4, cbHash);
            cbHash -= cb;
            for (; cb > 0; cb--, pbHash++)
                dprintf("%02X", *pbHash);
            dprintf(" ");
        }
    }
    dprintf("\n");
}

void DisplayName(
    IN PCERT_NAME_BLOB pName
    )
{
    DWORD csz;
    LPSTR psz;

    csz = CertNameToStrA(
        X509_ASN_ENCODING,
        pName,
        CERT_X500_NAME_STR + CERT_NAME_STR_REVERSE_FLAG,
        NULL,                   // psz
        0);                     // csz
    if (psz = (LPSTR) CertExtsAlloc(csz)) {
        CertNameToStrA(
            X509_ASN_ENCODING,
            pName,
            CERT_X500_NAME_STR + CERT_NAME_STR_REVERSE_FLAG,
            psz,
            csz);
        dprintf("  %s\n", psz);
        CertExtsFree(psz);
    } else
        dprintf("  ???\n");
}

//+-------------------------------------------------------------------------
//  Returns OID's name string. If not found returns L"???".
//--------------------------------------------------------------------------
LPCWSTR GetOIDName(LPCSTR pszOID, DWORD dwGroupId = 0)
{
    PCCRYPT_OID_INFO pInfo;

    if (pInfo = CryptFindOIDInfo(
            CRYPT_OID_INFO_OID_KEY,
            (void *) pszOID,
            dwGroupId
            )) {
        if (L'\0' != pInfo->pwszName[0])
            return pInfo->pwszName;
    }

    return L"???";
}

ALG_ID GetAlgid(LPCSTR pszOID, DWORD dwGroupId = 0)
{
    PCCRYPT_OID_INFO pInfo;

    if (pInfo = CryptFindOIDInfo(
            CRYPT_OID_INFO_OID_KEY,
            (void *) pszOID,
            dwGroupId
            ))
        return pInfo->Algid;
    return 0;
}

void PrintExtensions(
    IN DWORD cExt,
    IN PCERT_EXTENSION pExt,
    IN DWORD dwDisplayFlags
    )
{
    DWORD i;

    if (0 == (dwDisplayFlags & (DISPLAY_VERBOSE_FLAG | DISPLAY_EXT_FLAG)))
        return;

    for (i = 0; i < cExt; i++, pExt++) {
        DWORD cbFormat;

        LPSTR pszObjId = pExt->pszObjId;
        if (pszObjId == NULL)
            pszObjId = "<NULL OBJID>";
        LPSTR pszCritical = pExt->fCritical ? "TRUE" : "FALSE";
        dprintf("Extension[%d] %s (%S) Critical: %s::\n",
            i, pszObjId, GetOIDName(pszObjId), pszCritical);
        PrintBytes("    ", pExt->Value.pbData, pExt->Value.cbData);

        cbFormat = 0;
        if (CryptFormatObject(
                X509_ASN_ENCODING,
                0,                  // dwFormatType
                CRYPT_FORMAT_STR_MULTI_LINE | CRYPT_FORMAT_STR_NO_HEX,
                NULL,               // pFormatStruct,
                pszObjId,
                pExt->Value.pbData,
                pExt->Value.cbData,
                NULL,
                &cbFormat
                ) && cbFormat) {
            BYTE *pbFormat = NULL;
            if (pbFormat = (BYTE *) CertExtsAlloc(cbFormat)) {
                if (CryptFormatObject(
                        X509_ASN_ENCODING,
                        0,                  // dwFormatType
                        CRYPT_FORMAT_STR_MULTI_LINE | CRYPT_FORMAT_STR_NO_HEX,
                        NULL,               // pFormatStruct,
                        pszObjId,
                        pExt->Value.pbData,
                        pExt->Value.cbData,
                        pbFormat,
                        &cbFormat
                        )) {
                    dprintf("--- Decoded ---\n");
                    dprintf("%S\n", pbFormat);
                }

                CertExtsFree(pbFormat);
            }
        }
    }
}

typedef struct _PROP_INFO {
    DWORD       dwPropId;
    LPCSTR      pszPropName;
} PROP_INFO, *PPROP_INFO;

const PROP_INFO rgPropInfo[] = {
    CERT_KEY_PROV_HANDLE_PROP_ID, "KeyProvHandle",
    CERT_KEY_PROV_INFO_PROP_ID, "KeyProvInfo",
    CERT_SHA1_HASH_PROP_ID, "Sha1Hash",
    CERT_MD5_HASH_PROP_ID, "MD5Hash",
    CERT_KEY_CONTEXT_PROP_ID, "KeyContext",
    CERT_KEY_SPEC_PROP_ID, "KeySpec",
    CERT_IE30_RESERVED_PROP_ID, "IE30Reserved",
    CERT_PUBKEY_HASH_RESERVED_PROP_ID, "PubKeyHashReserved",
    CERT_ENHKEY_USAGE_PROP_ID, "EnhKeyUsage",
    CERT_NEXT_UPDATE_LOCATION_PROP_ID, "NextUpdateLocation",
    CERT_FRIENDLY_NAME_PROP_ID, "FriendlyName",
    CERT_PVK_FILE_PROP_ID, "PvkFile",
    CERT_DESCRIPTION_PROP_ID, "Description",
    CERT_ACCESS_STATE_PROP_ID, "AccessState",
    CERT_SIGNATURE_HASH_PROP_ID, "SignatureHash",
    CERT_SMART_CARD_DATA_PROP_ID, "SmartCartData",
    CERT_EFS_PROP_ID, "EFS",
    CERT_FORTEZZA_DATA_PROP_ID, "FortezzaData",
    CERT_ARCHIVED_PROP_ID, "Archived",
    CERT_KEY_IDENTIFIER_PROP_ID, "KeyIdentifier",
    CERT_AUTO_ENROLL_PROP_ID, "AutoEnroll",
    CERT_STORE_LOCALIZED_NAME_PROP_ID, "LocalizedName",
    0, "???"
};

LPCSTR GetPropName(
    IN DWORD dwPropId
    )
{
    const PROP_INFO *pInfo;

    for (pInfo = rgPropInfo; pInfo->dwPropId; pInfo++) {
        if (dwPropId == pInfo->dwPropId)
            return pInfo->pszPropName;
    }

    return "???";
}

void PrintProperties(
    IN PPROP_ELEMENT pProp,
    IN DWORD dwDisplayFlags
    )
{
    DWORD i;

    if (0 == (dwDisplayFlags & (DISPLAY_VERBOSE_FLAG | DISPLAY_PROP_FLAG)))
        return;

    i = 0;
    while (pProp) {
        PROP_ELEMENT Prop;

        __try {
            memset(&Prop, 0, sizeof(Prop));
            ReadMemory((LPVOID) pProp, &Prop, sizeof(Prop), NULL);
        } __except (EXCEPTION_EXECUTE_HANDLER) {
            dprintf("Unable to read Property Element at 0x%p\n", pProp);
            goto ErrorReturn;
        }

        dprintf("PropId %ld (0x%lx) %s ::\n", Prop.dwPropId, Prop.dwPropId,
            GetPropName(Prop.dwPropId));
        if (Prop.dwFlags)
            dprintf("     Property dwFlags: 0x%lx\n", Prop.dwFlags);

        if (Prop.cbData) {
            BYTE *pbData;

            if (pbData = (BYTE *) CertExtsAlloc(Prop.cbData)) {
                __try {
                    memset(pbData, 0, Prop.cbData);
                    ReadMemory((LPVOID) Prop.pbData, pbData, Prop.cbData, NULL);
                    PrintBytes("    ", pbData, Prop.cbData);
                } __except (EXCEPTION_EXECUTE_HANDLER) {
                    dprintf("Unable to read Property at 0x%p .. 0x%p\n",
                        Prop.pbData, Prop.pbData + Prop.cbData - 1);
                }

                CertExtsFree(pbData);
            }
        } else
            dprintf("     NO Property Bytes\n");

        pProp = Prop.pNext;
        if (i++ > MAX_PROP_CNT) {
            dprintf("Exceeded maximum prop count of %d\n", MAX_PROP_CNT);
            break;
        }
    }

CommonReturn:
    return;
ErrorReturn:
    goto CommonReturn;
}

void DisplayCert(
    IN PCCERT_CONTEXT pCert,
    IN DWORD dwDisplayFlags
    )
{
    dprintf("Version:: %d\n", pCert->pCertInfo->dwVersion);
    dprintf("Subject::");
    DisplayName(&pCert->pCertInfo->Subject);
    dprintf("Issuer::");
    DisplayName(&pCert->pCertInfo->Issuer);
    dprintf("SerialNumber::");
    DisplaySerialNumber(&pCert->pCertInfo->SerialNumber);
    dprintf("NotBefore:: %s\n", FileTimeText(&pCert->pCertInfo->NotBefore));
    dprintf("NotAfter:: %s\n", FileTimeText(&pCert->pCertInfo->NotAfter));

    {
        BYTE rgbHash[MAX_HASH_LEN];
        DWORD cbHash = MAX_HASH_LEN;

        CertGetCertificateContextProperty(
            pCert,
            CERT_SHA1_HASH_PROP_ID,
            rgbHash,
            &cbHash
            );
        DisplayThumbprint("SHA1", rgbHash, cbHash);
    }

    if (dwDisplayFlags & DISPLAY_VERBOSE_FLAG) {
        LPSTR pszObjId;
        ALG_ID aiPubKey;
        DWORD dwBitLen;

        pszObjId = pCert->pCertInfo->SignatureAlgorithm.pszObjId;
        if (pszObjId == NULL)
            pszObjId = "<NULL OBJID>";
        dprintf("SignatureAlgorithm:: %s (%S)\n",
            pszObjId, GetOIDName(pszObjId, CRYPT_SIGN_ALG_OID_GROUP_ID));
        if (pCert->pCertInfo->SignatureAlgorithm.Parameters.cbData) {
            dprintf("SignatureAlgorithm.Parameters::\n");
            PrintBytes("    ",
                pCert->pCertInfo->SignatureAlgorithm.Parameters.pbData,
                pCert->pCertInfo->SignatureAlgorithm.Parameters.cbData);
        }

        pszObjId = pCert->pCertInfo->SubjectPublicKeyInfo.Algorithm.pszObjId;
        if (pszObjId == NULL)
            pszObjId = "<NULL OBJID>";
        dprintf("SubjectPublicKeyInfo.Algorithm:: %s (%S)\n",
            pszObjId, GetOIDName(pszObjId, CRYPT_PUBKEY_ALG_OID_GROUP_ID));
        aiPubKey = GetAlgid(pszObjId, CRYPT_PUBKEY_ALG_OID_GROUP_ID);

        if (pCert->pCertInfo->SubjectPublicKeyInfo.Algorithm.Parameters.cbData)
        {
            dprintf("SubjectPublicKeyInfo.Algorithm.Parameters::\n");
            PrintBytes("    ",
                pCert->pCertInfo->SubjectPublicKeyInfo.Algorithm.Parameters.pbData,
                pCert->pCertInfo->SubjectPublicKeyInfo.Algorithm.Parameters.cbData);
        }
        dprintf("SubjectPublicKeyInfo.PublicKey");
        if (0 != (dwBitLen = CertGetPublicKeyLength(
                X509_ASN_ENCODING,
                &pCert->pCertInfo->SubjectPublicKeyInfo)))
            dprintf(" (BitLength: %d)", dwBitLen);
        if (pCert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cUnusedBits)
            dprintf(" (UnusedBits: %d)",
                pCert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cUnusedBits);
        dprintf("::\n");
        if (pCert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData) {
            PrintBytes("    ",
                pCert->pCertInfo->SubjectPublicKeyInfo.PublicKey.pbData,
                pCert->pCertInfo->SubjectPublicKeyInfo.PublicKey.cbData);

        } else
           dprintf("  No public key\n");
    }

    PrintExtensions(pCert->pCertInfo->cExtension,
        pCert->pCertInfo->rgExtension, dwDisplayFlags);

    if (dwDisplayFlags & DISPLAY_UI_FLAG) {
        CRYPTUI_VIEWCERTIFICATE_STRUCTW CertViewInfo;

        memset(&CertViewInfo, 0, sizeof(CertViewInfo));
        CertViewInfo.pCertContext = pCert;
        CertViewInfo.dwSize = sizeof(CertViewInfo);

        if (!CryptUIDlgViewCertificateW(
                &CertViewInfo,
                NULL                        // pfPropertiesChanged
                ))
            PrintLastError("CryptUIDlgViewCertificateW");
    }
}

void PrintCrlEntries(DWORD cEntry, PCRL_ENTRY pEntry, DWORD dwDisplayFlags)
{
    DWORD i;

    for (i = 0; i < cEntry; i++, pEntry++) {
        {
            dprintf(" [%d] SerialNumber::", i);
            DisplaySerialNumber(&pEntry->SerialNumber);
        }

        dprintf(" [%d] RevocationDate:: %s\n", i,
            FileTimeText(&pEntry->RevocationDate));

        if (dwDisplayFlags & (DISPLAY_VERBOSE_FLAG | DISPLAY_EXT_FLAG)) {
            if (pEntry->cExtension == 0)
                dprintf(" [%d] Extensions:: NONE\n", i);
            else {
                dprintf(" [%d] Extensions::\n", i);
                PrintExtensions(pEntry->cExtension, pEntry->rgExtension,
                    dwDisplayFlags);
            }
        }
    }
}

void DisplayCrl(
    IN PCCRL_CONTEXT pCrl,
    IN DWORD dwDisplayFlags
    )
{
    dprintf("Issuer::\n");
    DisplayName(&pCrl->pCrlInfo->Issuer);
    dprintf("ThisUpdate:: %s\n", FileTimeText(&pCrl->pCrlInfo->ThisUpdate));
    dprintf("NextUpdate:: %s\n", FileTimeText(&pCrl->pCrlInfo->NextUpdate));

    {
        BYTE rgbHash[MAX_HASH_LEN];
        DWORD cbHash = MAX_HASH_LEN;

        CertGetCRLContextProperty(
            pCrl,
            CERT_SHA1_HASH_PROP_ID,
            rgbHash,
            &cbHash
            );
        DisplayThumbprint("SHA1", rgbHash, cbHash);
    }

    PrintExtensions(pCrl->pCrlInfo->cExtension, pCrl->pCrlInfo->rgExtension,
        dwDisplayFlags);

    if (dwDisplayFlags & DISPLAY_VERBOSE_FLAG) {
        if (pCrl->pCrlInfo->cCRLEntry == 0)
            dprintf("Entries:: NONE\n");
        else {
            dprintf("Entries::\n");
            PrintCrlEntries(pCrl->pCrlInfo->cCRLEntry,
                pCrl->pCrlInfo->rgCRLEntry, dwDisplayFlags);
        }
    }

    if (dwDisplayFlags & DISPLAY_UI_FLAG) {
        CRYPTUI_VIEWCRL_STRUCTW CrlViewInfo;

        memset(&CrlViewInfo, 0, sizeof(CrlViewInfo));
        CrlViewInfo.pCRLContext = pCrl;
        CrlViewInfo.dwSize = sizeof(CrlViewInfo);

        if (!CryptUIDlgViewCRLW(
                &CrlViewInfo
                ))
            PrintLastError("CryptUIDlgViewCRLW");
    }
}

static void PrintCtlEntries(
    IN PCCTL_CONTEXT pCtl,
    IN DWORD dwDisplayFlags
    )
{
    PCTL_INFO pInfo = pCtl->pCtlInfo;
    DWORD cEntry = pInfo->cCTLEntry;
    PCTL_ENTRY pEntry = pInfo->rgCTLEntry;
    DWORD i;

    for (i = 0; i < cEntry; i++, pEntry++) {
        dprintf(" [%d] SubjectIdentifier::\n", i);
        PrintBytes("      ",
            pEntry->SubjectIdentifier.pbData,
            pEntry->SubjectIdentifier.cbData);

    }
}

void DisplayCtl(
    IN PCCTL_CONTEXT pCtl,
    IN DWORD dwDisplayFlags
    )
{
    PCTL_INFO pInfo = pCtl->pCtlInfo;

    dprintf("Version:: %d\n", pInfo->dwVersion);

    {
        DWORD cId;
        LPSTR *ppszId;
        DWORD i;

        dprintf("SubjectUsage::\n");
        cId = pInfo->SubjectUsage.cUsageIdentifier;
        ppszId = pInfo->SubjectUsage.rgpszUsageIdentifier;
        if (cId == 0)
            dprintf("  No Usage Identifiers\n");
        for (i = 0; i < cId; i++, ppszId++)
            dprintf("  [%d] %s\n", i, *ppszId);
    }

    if (pInfo->ListIdentifier.cbData) {
        dprintf("ListIdentifier::\n");
        PrintBytes("    ",
            pInfo->ListIdentifier.pbData,
            pInfo->ListIdentifier.cbData);
    }
    if (pInfo->SequenceNumber.cbData) {
        dprintf("SequenceNumber::");
        DisplaySerialNumber(&pInfo->SequenceNumber);
    }

    dprintf("ThisUpdate:: %s\n", FileTimeText(&pCtl->pCtlInfo->ThisUpdate));
    dprintf("NextUpdate:: %s\n", FileTimeText(&pCtl->pCtlInfo->NextUpdate));

    {
        BYTE rgbHash[MAX_HASH_LEN];
        DWORD cbHash = MAX_HASH_LEN;
        CertGetCTLContextProperty(
            pCtl,
            CERT_SHA1_HASH_PROP_ID,
            rgbHash,
            &cbHash
            );
        DisplayThumbprint("SHA1", rgbHash, cbHash);
    }

    if (dwDisplayFlags & DISPLAY_VERBOSE_FLAG) {
        LPSTR pszObjId;

        pszObjId = pInfo->SubjectAlgorithm.pszObjId;
        if (pszObjId == NULL)
            pszObjId = "<NULL OBJID>";
        dprintf("SubjectAlgorithm:: %s\n", pszObjId);
        if (pInfo->SubjectAlgorithm.Parameters.cbData) {
            dprintf("SubjectAlgorithm.Parameters::\n");
            PrintBytes("    ",
                pInfo->SubjectAlgorithm.Parameters.pbData,
                pInfo->SubjectAlgorithm.Parameters.cbData);
        }

    }
    PrintExtensions(pInfo->cExtension, pInfo->rgExtension, dwDisplayFlags);

    if (dwDisplayFlags & DISPLAY_VERBOSE_FLAG) {
        DWORD dwSignerCount;
        DWORD cbData;

        cbData = sizeof(dwSignerCount);
        if (!CryptMsgGetParam(
                pCtl->hCryptMsg,
                CMSG_SIGNER_COUNT_PARAM,
                0,                      // dwIndex
                &dwSignerCount,
                &cbData)) {
            dprintf("-----  Signer  -----\n");
            PrintLastError("CryptMsgGetParam(SIGNER_COUNT)");
        } else if (0 == dwSignerCount)
            dprintf("-----  No Signers  -----\n");
        else {
            DWORD dwSignerIndex;
            for (dwSignerIndex = 0; dwSignerIndex < dwSignerCount;
                                                            dwSignerIndex++) {
                DWORD dwFlags;
                PCCERT_CONTEXT pSigner;

                dwFlags = CMSG_USE_SIGNER_INDEX_FLAG;
                if (CryptMsgGetAndVerifySigner(
                        pCtl->hCryptMsg,
                        0,                  // cSignerStore
                        NULL,               // rghSignerStore
                        dwFlags,
                        &pSigner,
                        &dwSignerIndex
                        )) {
                    dprintf("-----  Signer [%d]  -----\n", dwSignerIndex);
                    DisplayCert(pSigner, 0);
                    CertFreeCertificateContext(pSigner);
                } else {
                    DWORD dwErr = GetLastError();
                    if (CRYPT_E_NO_TRUSTED_SIGNER == dwErr)
                        dprintf("-----  No Trusted Signer [%d]  -----\n",
                            dwSignerIndex);
                    else {
                        dprintf("-----  Signer [%d]  -----\n", dwSignerIndex);
                        PrintLastError("CryptMsgGetAndVerifySigner");
                    }
                }

            }
        }
    }

    if (dwDisplayFlags & DISPLAY_VERBOSE_FLAG) {
        if (pInfo->cCTLEntry == 0)
            dprintf("-----  No Entries  -----\n");
        else {
            dprintf("-----  Entries  -----\n");
            PrintCtlEntries(pCtl, dwDisplayFlags);
        }
    }

    if (dwDisplayFlags & DISPLAY_UI_FLAG) {
        CRYPTUI_VIEWCTL_STRUCTW CtlViewInfo;

        memset(&CtlViewInfo, 0, sizeof(CtlViewInfo));
        CtlViewInfo.pCTLContext = pCtl;
        CtlViewInfo.dwSize = sizeof(CtlViewInfo);

        if (!CryptUIDlgViewCTLW(
                &CtlViewInfo
                ))
            PrintLastError("CryptUIDlgViewCTLW");
    }
}

#define MAX_LINK_DEPTH  20

PPROP_ELEMENT GetCachePropHead(
    IN PCONTEXT_ELEMENT pEle
    )
{
    DWORD dwInnerDepth;

    if (ELEMENT_TYPE_CACHE == pEle->dwElementType)
        return pEle->Cache.pPropHead;

    pEle = pEle->pEle;

    // Skip past any links to get to the cache element
    dwInnerDepth = 0;
    while (pEle && dwInnerDepth++ < MAX_LINK_DEPTH) {
        CONTEXT_ELEMENT Ele;

        __try {
            memset(&Ele, 0, sizeof(Ele));
            ReadMemory((LPVOID) pEle, &Ele, sizeof(Ele), NULL);
        } __except (EXCEPTION_EXECUTE_HANDLER) {
            dprintf("Unable to read Context Element at 0x%p\n", pEle);
            return NULL;
        }

        if (ELEMENT_TYPE_CACHE == Ele.dwElementType)
            return Ele.Cache.pPropHead;

        pEle = Ele.pEle;
    }

    return NULL;
}


void DisplayContextElement(
    IN PCONTEXT_ELEMENT pEle,
    IN DWORD dwDisplayFlags,
    IN BOOL fNext = FALSE
    )
{
    DWORD i;

    i = 0;
    while (pEle) {
        CONTEXT_ELEMENT Ele;
        const void *pvContext = (const void *) ((BYTE *) pEle +
            sizeof(CONTEXT_ELEMENT));

        __try {
            memset(&Ele, 0, sizeof(Ele));
            ReadMemory((LPVOID) pEle, &Ele, sizeof(Ele), NULL);
        } __except (EXCEPTION_EXECUTE_HANDLER) {
            dprintf("Unable to read Context Element at 0x%p\n", pEle);
            goto ErrorReturn;
        }

        dprintf("====  ");

        switch (Ele.dwContextType + 1) {
            case CERT_STORE_CERTIFICATE_CONTEXT:
                dprintf("Certificate");
                break;
            case CERT_STORE_CRL_CONTEXT:
                dprintf("CRL");
                break;
            case CERT_STORE_CTL_CONTEXT:
                dprintf("CTL");
                break;
            default:
                dprintf("ContextType[%ld]", Ele.dwContextType);
        }

        dprintf("[");

        if (fNext)
            dprintf("%ld : ", i);
        dprintf("0x%p]  ====\n", pvContext);


        if (0 == (dwDisplayFlags & DISPLAY_SHORT_FLAG)) {
            dprintf("Element[0x%p]:: Type: ", pEle);
            switch (Ele.dwElementType) {
                case ELEMENT_TYPE_CACHE:
                    dprintf("CACHE");
                    break;
                case ELEMENT_TYPE_LINK_CONTEXT:
                    dprintf("LINK");
                    break;
                case ELEMENT_TYPE_EXTERNAL:
                    dprintf("EXTERNAL");
                    break;
                case ELEMENT_TYPE_COLLECTION:
                    dprintf("COLLECTION");
                    break;
                default:
                    dprintf("%ld", Ele.dwElementType);
            }

            dprintf(" RefCnt: %ld", Ele.lRefCnt);
            dprintf(" Flags: 0x%lx", Ele.dwFlags);
            if (Ele.pEle != pEle)
                dprintf(" pEle: 0x%p", Ele.pEle);
            dprintf(" NextContext: 0x%p", ToCertContext(Ele.pNext));
            dprintf(" PrevContext: 0x%p", ToCertContext(Ele.pPrev));
            dprintf(" Store: 0x%p", Ele.pStore);
            dprintf(" ProvStore: 0x%p", Ele.pProvStore);
            dprintf("\n");

            switch (Ele.dwContextType + 1) {
                case CERT_STORE_CERTIFICATE_CONTEXT:
                    {
                        CERT_CONTEXT CertContext;
                        BYTE *pbEncoded;
                        DWORD cbEncoded;
                        PCCERT_CONTEXT pCert;

                        __try {
                            memset(&CertContext, 0, sizeof(CertContext));
                            ReadMemory(pvContext, &CertContext,
                                sizeof(CertContext), NULL);
                        } __except (EXCEPTION_EXECUTE_HANDLER) {
                            dprintf("Unable to read CertContext at 0x%p\n",
                                pvContext);
                            break;
                        }
                        if (!GetEncodedBlob(CertContext.pbCertEncoded,
                                &pbEncoded, &cbEncoded))
                            break;
                        pCert = CertCreateCertificateContext(
                            X509_ASN_ENCODING,
                            pbEncoded,
                            cbEncoded
                            );
                        if (pCert) {
                            DisplayCert(pCert, dwDisplayFlags);
                            CertFreeCertificateContext(pCert);
                        } else 
                            PrintLastError("CertCreateCertificateContext");
                        CertExtsFree(pbEncoded);
                    }
                    break;
                case CERT_STORE_CRL_CONTEXT:
                    {
                        CRL_CONTEXT CrlContext;
                        BYTE *pbEncoded;
                        DWORD cbEncoded;
                        PCCRL_CONTEXT pCrl;

                        __try {
                            memset(&CrlContext, 0, sizeof(CrlContext));
                            ReadMemory(pvContext, &CrlContext,
                                sizeof(CrlContext), NULL);
                        } __except (EXCEPTION_EXECUTE_HANDLER) {
                            dprintf("Unable to read CrlContext at 0x%p\n",
                                pvContext);
                            break;
                        }
                        if (!GetEncodedBlob(CrlContext.pbCrlEncoded,
                                &pbEncoded, &cbEncoded))
                            break;
                        pCrl = CertCreateCRLContext(
                            X509_ASN_ENCODING,
                            pbEncoded,
                            cbEncoded
                            );
                        if (pCrl) {
                            DisplayCrl(pCrl, dwDisplayFlags);
                            CertFreeCRLContext(pCrl);
                        } else 
                            PrintLastError("CertCreateCRLContext");
                        CertExtsFree(pbEncoded);
                    }
                    break;
                case CERT_STORE_CTL_CONTEXT:
                    {
                        CTL_CONTEXT CtlContext;
                        BYTE *pbEncoded;
                        DWORD cbEncoded;
                        PCCTL_CONTEXT pCtl;

                        __try {
                            memset(&CtlContext, 0, sizeof(CtlContext));
                            ReadMemory(pvContext, &CtlContext,
                                sizeof(CtlContext), NULL);
                        } __except (EXCEPTION_EXECUTE_HANDLER) {
                            dprintf("Unable to read CtlContext at 0x%p\n",
                                pvContext);
                            break;
                        }
                        if (!GetEncodedBlob(CtlContext.pbCtlEncoded,
                                &pbEncoded, &cbEncoded))
                            break;
                        pCtl = CertCreateCTLContext(
                            X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
                            pbEncoded,
                            cbEncoded
                            );
                        if (pCtl) {
                            DisplayCtl(pCtl, dwDisplayFlags);
                            CertFreeCTLContext(pCtl);
                        } else 
                            PrintLastError("CertCreateCTLContext");
                        CertExtsFree(pbEncoded);
                    }
                    break;
            }

            PrintProperties(GetCachePropHead(&Ele), dwDisplayFlags);
        }


        if (!fNext)
            break;

        pEle = Ele.pNext;
        if (i++ > MAX_CONTEXT_CNT) {
            dprintf("Exceeded maximum context count of %d\n", MAX_CONTEXT_CNT);
            break;
        }
    }


CommonReturn:
    return;
ErrorReturn:
    goto CommonReturn;

}

void DisplayContext(
    IN const void *pvContext,
    IN DWORD dwDisplayFlags
    )
{
    if (((const void *)(UINT_PTR) sizeof(CONTEXT_ELEMENT)) > pvContext) {
        dprintf("Invalid, NULL Context address\n");
        return;
    }

    DisplayContextElement(
        ToContextElement((PCCERT_CONTEXT) pvContext),
        dwDisplayFlags
        );
}
    

void DisplayStore(
    IN PCERT_STORE pStore,
    IN DWORD dwDisplayFlags,
    IN DWORD dwNest
    )
{
    CERT_STORE Store;
    PCERT_STORE_LINK pStoreLink;
    DWORD i;

    __try {
        memset(&Store, 0, sizeof(Store));
        ReadMemory((LPVOID) pStore, &Store, sizeof(Store), NULL);
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        dprintf("Unable to read Store at 0x%p\n", pStore);
        goto ErrorReturn;
    }

    dprintf("Store[0x%p]:: Type: ", pStore);
    switch (Store.dwStoreType) {
        case STORE_TYPE_CACHE:
            dprintf("CACHE");
            break;
        case STORE_TYPE_EXTERNAL:
            dprintf("EXTERNAL");
            break;
        case STORE_TYPE_COLLECTION:
            dprintf("COLLECTION");
            break;
        default:
            dprintf("%ld", Store.dwStoreType);
    }
    dprintf(" RefCnt: %ld", Store.lRefCnt);
    dprintf(" Flags: 0x%lx", Store.dwFlags);
    dprintf(" State: ");
    switch (Store.dwState) {
        case STORE_STATE_DELETED:
            dprintf("DELETED");
            break;
        case STORE_STATE_NULL:
            dprintf("NULL");
            break;
        case STORE_STATE_OPENING:
            dprintf("OPENING");
            break;
        case STORE_STATE_OPEN:
            dprintf("OPEN");
            break;
        case STORE_STATE_DEFER_CLOSING:
            dprintf("DEFER_CLOSING");
            break;
        case STORE_STATE_CLOSING:
            dprintf("CLOSING");
            break;
        case STORE_STATE_CLOSED:
            dprintf("CLOSED");
            break;
        default:
            dprintf("%ld", Store.dwState);
    }
    dprintf("\n");

    PrintProperties(Store.pPropHead, dwDisplayFlags | DISPLAY_PROP_FLAG);

    for (i = 0; i < CONTEXT_COUNT; i++)
        DisplayContextElement(Store.rgpContextListHead[i], dwDisplayFlags,
            TRUE);

    i = 0;
    pStoreLink = Store.pStoreListHead;
    while (pStoreLink) {
        CERT_STORE_LINK StoreLink;
        DWORD j;

        __try {
            memset(&StoreLink, 0, sizeof(StoreLink));
            ReadMemory((LPVOID) pStoreLink, &StoreLink, sizeof(StoreLink),
                NULL);
        } __except (EXCEPTION_EXECUTE_HANDLER) {
            dprintf("Unable to read StoreLink at 0x%p\n", pStoreLink);
            goto ErrorReturn;
        }

        dprintf("######  SiblingStore[%ld, %ld]  ######\n", dwNest, i);
        dprintf("Link[0x%p]:: Flags: 0x%lx", pStoreLink, StoreLink.dwFlags);
        dprintf(" RefCnt: %ld", StoreLink.lRefCnt);
        dprintf(" UpdateFlags: 0x%lx", StoreLink.dwUpdateFlags);
        dprintf(" Priority: %ld", StoreLink.dwPriority);
        dprintf("\n");
        if (dwNest > MAX_STORE_NEST_CNT) {
            dprintf("Exceeded maximum store nest count of %d\n",
                MAX_STORE_NEST_CNT);
            break;
        }
        DisplayStore(StoreLink.pSibling, dwDisplayFlags, dwNest + 1);

        pStoreLink = StoreLink.pNext;
        if (i++ > MAX_STORE_SIBLING_CNT) {
            dprintf("Exceeded maximum store sibling count of %d\n",
                MAX_STORE_SIBLING_CNT);
            break;
        }
    }

CommonReturn:
    return;
ErrorReturn:
    goto CommonReturn;
}

DWORD GetDisplayFlags(
    IN OUT LPSTR *lppArgumentString
    )
{
    DWORD dwDisplayFlags = 0;
    LPSTR lpArgumentString = *lppArgumentString;

    while (TRUE) {
        while (*lpArgumentString == ' ')
            lpArgumentString++;

        if (*lpArgumentString == 's') {
            dwDisplayFlags |= DISPLAY_SHORT_FLAG;
            lpArgumentString++;
        } else if (*lpArgumentString == 'v') {
            dwDisplayFlags |= DISPLAY_VERBOSE_FLAG;
            lpArgumentString++;
        } else if (*lpArgumentString == 'u') {
            dwDisplayFlags |= DISPLAY_UI_FLAG;
            lpArgumentString++;
        } else if (*lpArgumentString == 'x') {
            dwDisplayFlags |= DISPLAY_EXT_FLAG;
            lpArgumentString++;
        } else if (*lpArgumentString == 'p') {
            dwDisplayFlags |= DISPLAY_PROP_FLAG;
            lpArgumentString++;
        } else
            break;
    }

    *lppArgumentString = lpArgumentString;

    return dwDisplayFlags;
}

//
// context {[s|v|u|x|p]} address
//

DECLARE_API( context )
{
    DWORD dwDisplayFlags;
    const void *pvContext;

    INIT_API();

    dwDisplayFlags = GetDisplayFlags(&lpArgumentString);

    pvContext = (const void *)(ULONG_PTR) GetExpression(lpArgumentString);
    if (NULL == pvContext) {
        dprintf("Invalid, NULL address\n");
        return;
    }

    DisplayContext(
        pvContext,
        dwDisplayFlags
        );
}

//
// ele {[s|v|u|x|p]} address
//

DECLARE_API( ele )
{
    DWORD dwDisplayFlags;
    PCONTEXT_ELEMENT pEle;

    INIT_API();

    dwDisplayFlags = GetDisplayFlags(&lpArgumentString);

    pEle = (PCONTEXT_ELEMENT)(ULONG_PTR) GetExpression(lpArgumentString);
    if (NULL == pEle) {
        dprintf("Invalid, NULL address\n");
        return;
    }

    DisplayContextElement(
        pEle,
        dwDisplayFlags
        );
}

//
// store {[s|v|x|p]} address
//

DECLARE_API( store )
{
    DWORD dwDisplayFlags;
    PCERT_STORE pStore;

    INIT_API();

    dwDisplayFlags = GetDisplayFlags(&lpArgumentString);
    dwDisplayFlags &= ~DISPLAY_UI_FLAG;

    pStore = (PCERT_STORE)(ULONG_PTR) GetExpression(lpArgumentString);
    if (NULL == pStore) {
        dprintf("Invalid, NULL address\n");
        return;
    }

    dprintf("######  Parent Store  ######\n");
    DisplayStore(
        pStore,
        dwDisplayFlags,
        0                   // dwNest
        );
}

//
// cert {[s|v|u|x|p]} address
//

DECLARE_API( cert )
{
    DWORD dwDisplayFlags;
    LPVOID pvAddr;

    BYTE *pbEncoded = NULL;
    DWORD cbEncoded;
    PCCERT_CONTEXT pCert = NULL;

    INIT_API();

    dwDisplayFlags = GetDisplayFlags(&lpArgumentString);

    pvAddr = (LPVOID)(ULONG_PTR) GetExpression(lpArgumentString);
    if (NULL == pvAddr) {
        dprintf("Invalid, NULL address\n");
        goto ErrorReturn;
    }

    //dprintf("Expression:: 0x%p\n", pvAddr);

    if (!GetEncodedBlob(pvAddr, &pbEncoded, &cbEncoded))
        goto ErrorReturn;

    pCert = CertCreateCertificateContext(
        X509_ASN_ENCODING,
        pbEncoded,
        cbEncoded
        );
    if (pCert == NULL) {
        PrintLastError("CertCreateCertificateContext");
        goto ErrorReturn;
    }

    dprintf("=====  Certificate  =====\n");
    DisplayCert(pCert, dwDisplayFlags);

CommonReturn:
    CertExtsFree(pbEncoded);
    CertFreeCertificateContext(pCert);
    return;
ErrorReturn:
    goto CommonReturn;

}

//
// crl {[s|v|u|x|p]} address
//

DECLARE_API( crl )
{
    DWORD dwDisplayFlags;
    LPVOID pvAddr;

    BYTE *pbEncoded = NULL;
    DWORD cbEncoded;
    PCCRL_CONTEXT pCrl = NULL;

    INIT_API();

    dwDisplayFlags = GetDisplayFlags(&lpArgumentString);

    pvAddr = (LPVOID)(ULONG_PTR) GetExpression(lpArgumentString);
    if (NULL == pvAddr) {
        dprintf("Invalid, NULL address\n");
        goto ErrorReturn;
    }

    //dprintf("Expression:: 0x%p\n", pvAddr);

    if (!GetEncodedBlob(pvAddr, &pbEncoded, &cbEncoded))
        goto ErrorReturn;

    pCrl = CertCreateCRLContext(
        X509_ASN_ENCODING,
        pbEncoded,
        cbEncoded
        );
    if (pCrl == NULL) {
        PrintLastError("CertCreateCRLContext");
        goto ErrorReturn;
    }

    dprintf("=====  CRL  =====\n");
    DisplayCrl(pCrl, dwDisplayFlags);

CommonReturn:
    CertExtsFree(pbEncoded);
    CertFreeCRLContext(pCrl);
    return;
ErrorReturn:
    goto CommonReturn;

}

//
// ctl {[s|v|u|x|p]} address
//

DECLARE_API( ctl )
{
    DWORD dwDisplayFlags;
    LPVOID pvAddr;

    BYTE *pbEncoded = NULL;
    DWORD cbEncoded;
    PCCTL_CONTEXT pCtl = NULL;

    INIT_API();

    dwDisplayFlags = GetDisplayFlags(&lpArgumentString);

    pvAddr = (LPVOID)(ULONG_PTR) GetExpression(lpArgumentString);
    if (NULL == pvAddr) {
        dprintf("Invalid, NULL address\n");
        goto ErrorReturn;
    }

    //dprintf("Expression:: 0x%p\n", pvAddr);

    if (!GetEncodedBlob(pvAddr, &pbEncoded, &cbEncoded))
        goto ErrorReturn;

    pCtl = CertCreateCTLContext(
        X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
        pbEncoded,
        cbEncoded
        );
    if (pCtl == NULL) {
        PrintLastError("CertCreateCTLContext");
        goto ErrorReturn;
    }

    dprintf("=====  CTL  =====\n");
    DisplayCtl(pCtl, dwDisplayFlags);

CommonReturn:
    CertExtsFree(pbEncoded);
    CertFreeCTLContext(pCtl);
    return;
ErrorReturn:
    goto CommonReturn;

}


//+---------------------------------------------------------------------------
//
//  Synopsis:   Chain Display Functions
//
//----------------------------------------------------------------------------
LPCSTR rgszErrorStatus[] = {

    "CERT_TRUST_IS_NOT_TIME_VALID",             // 0x00000001
    "CERT_TRUST_IS_NOT_TIME_NESTED",            // 0x00000002
    "CERT_TRUST_IS_REVOKED",                    // 0x00000004
    "CERT_TRUST_IS_NOT_SIGNATURE_VALID",        // 0x00000008
    "CERT_TRUST_IS_NOT_VALID_FOR_USAGE",        // 0x00000010
    "CERT_TRUST_IS_UNTRUSTED_ROOT",             // 0x00000020
    "CERT_TRUST_REVOCATION_STATUS_UNKNOWN",     // 0x00000040
    "CERT_TRUST_IS_CYCLIC",                     // 0x00000080
    "Unknown Error Status",                     // 0x00000100
    "Unknown Error Status",                     // 0x00000200
    "Unknown Error Status",                     // 0x00000400
    "Unknown Error Status",                     // 0x00000800
    "Unknown Error Status",                     // 0x00001000
    "Unknown Error Status",                     // 0x00002000
    "Unknown Error Status",                     // 0x00004000
    "Unknown Error Status",                     // 0x00008000
    "CERT_TRUST_IS_PARTIAL_CHAIN",              // 0x00010000
    "CERT_TRUST_CTL_IS_NOT_TIME_VALID",         // 0x00020000
    "CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID",    // 0x00040000
    "CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE",    // 0x00080000
    "Unknown Error Status",
    "Unknown Error Status",
    "Unknown Error Status",
    "Unknown Error Status",
    "Unknown Error Status",
    "Unknown Error Status",
    "Unknown Error Status",
    "Unknown Error Status",
    "Unknown Error Status",
    "Unknown Error Status",
    "Unknown Error Status",
    "Unknown Error Status",
    "Unknown Error Status",
    "Unknown Error Status"
};

LPCSTR rgszInfoStatus[] = {

    "CERT_TRUST_HAS_EXACT_MATCH_ISSUER",// 0x00000001
    "CERT_TRUST_HAS_KEY_MATCH_ISSUER",  // 0x00000002
    "CERT_TRUST_HAS_NAME_MATCH_ISSUER", // 0x00000004
    "CERT_TRUST_IS_SELF_SIGNED",        // 0x00000008
    "Unknown Info Status",              // 0x00000010
    "Unknown Info Status",              // 0x00000020
    "Unknown Info Status",              // 0x00000040
    "Unknown Info Status",              // 0x00000080
    "Unknown Info Status",              // 0x00000100
    "Unknown Info Status",              // 0x00000200
    "Unknown Info Status",              // 0x00000400
    "Unknown Info Status",              // 0x00000800
    "Unknown Info Status",              // 0x00001000
    "Unknown Info Status",              // 0x00002000
    "Unknown Info Status",              // 0x00004000
    "Unknown Info Status",              // 0x00008000
    "CERT_TRUST_IS_COMPLEX_CHAIN",      // 0x00010000
    "Unknown Info Status",              // 0x00020000
    "Unknown Info Status",              // 0x00040000
    "Unknown Info Status",              // 0x00080000
    "Unknown Info Status",              // 0x00100000
    "Unknown Info Status",              // 0x00200000
    "Unknown Info Status",              // 0x00400000
    "Unknown Info Status",              // 0x00800000
    "Unknown Info Status",              // 0x01000000
    "Unknown Info Status",              // 0x02000000
    "Unknown Info Status",              // 0x04000000
    "Unknown Info Status",              // 0x08000000
    "Unknown Info Status",              // 0x10000000
    "Unknown Info Status",              // 0x20000000
    "Unknown Info Status",              // 0x40000000
    "Unknown Info Status"               // 0x80000000
};

void DisplayTrustStatus(
    IN PCERT_TRUST_STATUS pStatus
    )
{
    DWORD dwMask;
    DWORD cCount;

    dprintf(
       "Trust Status (E=0x%lx,I=0x%lx)\n\n",
       pStatus->dwErrorStatus,
       pStatus->dwInfoStatus
       );

    dwMask = 1;
    for ( cCount = 0; cCount < 32; cCount++ )
    {
        if ( pStatus->dwErrorStatus & dwMask )
        {
            if ( strcmp( rgszErrorStatus[ cCount ], "Unknown Error Status" ) != 0 )
            {
                dprintf("%s\n", rgszErrorStatus[ cCount ]);
            }
        }

        dwMask = dwMask << 1;
    }

    dwMask = 1;
    for ( cCount = 0; cCount < 32; cCount++ )
    {
        if ( pStatus->dwInfoStatus & dwMask )
        {
            if ( strcmp( rgszInfoStatus[ cCount ], "Unknown Info Status" ) != 0 )
            {
                dprintf("%s\n", rgszInfoStatus[ cCount ]);
            }
        }

        dwMask = dwMask << 1;
    }

    dprintf("\n");
}

void DisplayChainElement(
    IN PCERT_CHAIN_ELEMENT pElement,
    IN DWORD dwDisplayFlags
    )
{
    CERT_CHAIN_ELEMENT Element;

    __try {
        memset(&Element, 0, sizeof(Element));
        ReadMemory((LPVOID) pElement, &Element, sizeof(Element), NULL);
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        dprintf("Unable to read Chain Element at 0x%p\n", pElement);
        return;
    }
    DisplayContext( Element.pCertContext, dwDisplayFlags );
    dprintf("\n");
    DisplayTrustStatus( &Element.TrustStatus );
}

void DisplaySimpleChain(
    IN PCERT_SIMPLE_CHAIN pChain,
    IN DWORD dwDisplayFlags
    )
{
    CERT_SIMPLE_CHAIN Chain;
    DWORD cElement;
    PCERT_CHAIN_ELEMENT *ppElement = NULL;

    if (NULL == pChain)
        return;

    __try {
        memset(&Chain, 0, sizeof(Chain));
        ReadMemory((LPVOID) pChain, &Chain, sizeof(Chain), NULL);
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        dprintf("Unable to read Simple Chain at 0x%p\n", pChain);
        goto ErrorReturn;
    }

    DisplayTrustStatus( &Chain.TrustStatus );
    dprintf("Chain Element Count = %d\n", Chain.cElement);

    if (MAX_CHAIN_ELEMENT_CNT < Chain.cElement) {
        dprintf("Exceeded maximum chain element count of %d\n",
            MAX_CHAIN_ELEMENT_CNT);
        goto ErrorReturn;
    }

    if (Chain.cElement) {
        if (NULL == (ppElement = (PCERT_CHAIN_ELEMENT *) CertExtsAlloc(
                Chain.cElement * sizeof(PCERT_CHAIN_ELEMENT))))
            goto ErrorReturn;

        __try {
            memset(ppElement, 0, Chain.cElement * sizeof(PCERT_CHAIN_ELEMENT));
            ReadMemory((LPVOID) Chain.rgpElement, ppElement,
                 Chain.cElement * sizeof(PCERT_CHAIN_ELEMENT), NULL);
        } __except (EXCEPTION_EXECUTE_HANDLER) {
            dprintf("Unable to read ChainElement pointers at 0x%p\n",
                Chain.rgpElement);
            goto ErrorReturn;
        }
    }

    for ( cElement = 0; cElement < Chain.cElement; cElement++ )
    {
        dprintf("Chain Element [%d]\n", cElement);
        DisplayChainElement( ppElement[ cElement ], dwDisplayFlags );
    }
CommonReturn:
    CertExtsFree(ppElement);
    return;
ErrorReturn:
    goto CommonReturn;
}

void DisplayChain(
    IN PCCERT_CHAIN_CONTEXT pChainContext,
    IN DWORD dwDisplayFlags
    )
{
    CERT_CHAIN_CONTEXT ChainContext;
    DWORD cChain;
    PCERT_SIMPLE_CHAIN *ppChain = NULL;

    if (NULL == pChainContext)
        return;

    __try {
        memset(&ChainContext, 0, sizeof(ChainContext));
        ReadMemory((LPVOID) pChainContext, &ChainContext,
            sizeof(ChainContext), NULL);
    } __except (EXCEPTION_EXECUTE_HANDLER) {
        dprintf("Unable to read ChainContext at 0x%p\n", pChainContext);
        goto ErrorReturn;
    }

    dprintf("Chain Context\n\n");
    DisplayTrustStatus( (PCERT_TRUST_STATUS)&ChainContext.TrustStatus );
    dprintf("Simple Chain Count = %d\n\n", ChainContext.cChain );

    if (MAX_SIMPLE_CHAIN_CNT < ChainContext.cChain) {
        dprintf("Exceeded maximum simple chain count of %d\n",
            MAX_SIMPLE_CHAIN_CNT);
        goto ErrorReturn;
    }

    if (ChainContext.cChain) {
        if (NULL == (ppChain = (PCERT_SIMPLE_CHAIN *) CertExtsAlloc(
                ChainContext.cChain * sizeof(PCERT_SIMPLE_CHAIN))))
            goto ErrorReturn;

        __try {
            memset(ppChain, 0,
                ChainContext.cChain * sizeof(PCERT_SIMPLE_CHAIN));
            ReadMemory((LPVOID) ChainContext.rgpChain, ppChain,
                 ChainContext.cChain * sizeof(PCERT_SIMPLE_CHAIN), NULL);
        } __except (EXCEPTION_EXECUTE_HANDLER) {
            dprintf("Unable to read SimpleChain pointers at 0x%p\n",
                ChainContext.rgpChain);
            goto ErrorReturn;
        }
    }

    for ( cChain = 0; cChain < ChainContext.cChain; cChain++ )
    {
        dprintf("Simple Chain [%d]\n", cChain);
        DisplaySimpleChain( ppChain[ cChain ], dwDisplayFlags );
    }

CommonReturn:
    CertExtsFree(ppChain);
    return;
ErrorReturn:
    goto CommonReturn;
}

//
// chain {[s|v|x|p]} address
//

DECLARE_API( chain )
{
    DWORD dwDisplayFlags;
    PCCERT_CHAIN_CONTEXT pChainContext;

    INIT_API();

    dwDisplayFlags = GetDisplayFlags(&lpArgumentString);
    dwDisplayFlags &= ~DISPLAY_UI_FLAG;

    pChainContext = (PCCERT_CHAIN_CONTEXT)(ULONG_PTR) GetExpression(lpArgumentString);
    if (NULL == pChainContext) {
        dprintf("Invalid, NULL address\n");
        return;
    }

    DisplayChain(
        pChainContext,
        dwDisplayFlags
        );
}
