You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2354 lines
61 KiB
2354 lines
61 KiB
//+--------------------------------------------------------------------------
|
|
//
|
|
// Microsoft Windows
|
|
// Copyright (C) Microsoft Corporation, 1996 - 1999
|
|
//
|
|
// File: request.cpp
|
|
//
|
|
// Contents: Cert Server client implementation
|
|
//
|
|
// History: 24-Aug-96 vich created
|
|
//
|
|
//---------------------------------------------------------------------------
|
|
|
|
#include "pch.cpp"
|
|
|
|
#pragma hdrstop
|
|
|
|
#include <objbase.h>
|
|
#include "certsrvd.h"
|
|
#include "csdisp.h"
|
|
#include "certrpc.h"
|
|
#include <certca.h>
|
|
|
|
#include "request.h"
|
|
|
|
#define __dwFILE__ __dwFILE_CERTCLI_REQUEST_CPP__
|
|
|
|
|
|
#define CR_RPC_CANCEL_TIMEOUT 5
|
|
#define CR_RPC_REQUEST_TIMEOUT 60000 /* 60 seconds for the request timeout */
|
|
|
|
|
|
|
|
typedef struct _RPC_TIMEOUT_CONTEXT
|
|
{
|
|
HANDLE hWait;
|
|
HANDLE hEvent;
|
|
HANDLE hThread;
|
|
HRESULT hrRpcError;
|
|
} RPC_TIMEOUT_CONTEXT, *PRPC_TIMEOUT_CONTEXT;
|
|
|
|
typedef struct _WZR_RPC_BINDING_LIST
|
|
{
|
|
LPWSTR pszProtSeq;
|
|
LPWSTR pszEndpoint;
|
|
} WZR_RPC_BINDING_LIST;
|
|
|
|
WZR_RPC_BINDING_LIST g_awzrBindingList[] =
|
|
{
|
|
{ L"ncacn_ip_tcp", NULL },
|
|
{ L"ncacn_np", L"\\pipe\\cert" }
|
|
};
|
|
|
|
INT g_cwzrBindingList = sizeof(g_awzrBindingList)/sizeof(g_awzrBindingList[0]);
|
|
|
|
typedef struct _WZR_RPC_ATHN_LIST
|
|
{
|
|
DWORD dwAuthnLevel;
|
|
DWORD dwAuthnService;
|
|
} WZR_RPC_ATHN_LIST;
|
|
|
|
WZR_RPC_ATHN_LIST g_awzrAthnList[] =
|
|
{
|
|
{ RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_GSS_NEGOTIATE},
|
|
{ RPC_C_AUTHN_LEVEL_PKT_INTEGRITY, RPC_C_AUTHN_GSS_NEGOTIATE},
|
|
{ RPC_C_AUTHN_LEVEL_NONE, RPC_C_AUTHN_NONE }
|
|
};
|
|
|
|
INT g_cwzrAthnList = sizeof(g_awzrAthnList)/sizeof(g_awzrAthnList[0]);
|
|
|
|
HRESULT
|
|
crRegisterRPCCallTimeout(
|
|
IN DWORD dwMilliseconds,
|
|
OUT PRPC_TIMEOUT_CONTEXT pTimeout);
|
|
|
|
HRESULT
|
|
crCloseRPCCallTimeout(
|
|
IN PRPC_TIMEOUT_CONTEXT pTimeout);
|
|
|
|
|
|
|
|
HRESULT
|
|
crSetRPCSecurity(
|
|
IN handle_t hRPCCertServer,
|
|
IN OUT INT *prpcAuthProtocol)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
LPWSTR pwszCAPrinceName = NULL;
|
|
INT rpcAuthProtocol = *prpcAuthProtocol;
|
|
|
|
// Set the RPC connect as the SNEGO connect, which can authenticate
|
|
// a machine if supported by the system.
|
|
// Don't need to check the return value since not supported by NT4/Win9x.
|
|
|
|
if (rpcAuthProtocol >= g_cwzrAthnList)
|
|
{
|
|
hr = RPC_S_UNKNOWN_AUTHN_SERVICE;
|
|
goto error;
|
|
}
|
|
for ( ; rpcAuthProtocol < g_cwzrAthnList; rpcAuthProtocol++)
|
|
{
|
|
pwszCAPrinceName = NULL;
|
|
if (RPC_C_AUTHN_NONE != g_awzrAthnList[rpcAuthProtocol].dwAuthnService)
|
|
{
|
|
hr = RpcMgmtInqServerPrincName(
|
|
hRPCCertServer,
|
|
g_awzrAthnList[rpcAuthProtocol].dwAuthnService,
|
|
&pwszCAPrinceName);
|
|
if (hr == RPC_S_UNKNOWN_AUTHN_SERVICE)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
hr = RpcBindingSetAuthInfo(
|
|
hRPCCertServer,
|
|
pwszCAPrinceName,
|
|
g_awzrAthnList[rpcAuthProtocol].dwAuthnLevel,
|
|
g_awzrAthnList[rpcAuthProtocol].dwAuthnService,
|
|
NULL,
|
|
RPC_C_AUTHZ_NONE);
|
|
|
|
if (NULL != pwszCAPrinceName)
|
|
{
|
|
RpcStringFree(&pwszCAPrinceName);
|
|
}
|
|
if (hr != RPC_S_UNKNOWN_AUTHN_SERVICE)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
error:
|
|
*prpcAuthProtocol = rpcAuthProtocol;
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
crOpenRPCConnection(
|
|
IN WCHAR const *pwszServerName,
|
|
IN OUT INT *prpcAuthProtocol,
|
|
OUT handle_t *phRPCCertServer)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
INT i;
|
|
WCHAR *pwszStringBinding = NULL;
|
|
|
|
for (i = 0; i < g_cwzrBindingList; i++)
|
|
{
|
|
if (RPC_S_OK != RpcNetworkIsProtseqValid(
|
|
g_awzrBindingList[i].pszProtSeq))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
hr = RpcStringBindingCompose(
|
|
NULL,
|
|
g_awzrBindingList[i].pszProtSeq,
|
|
const_cast<WCHAR *>(pwszServerName),
|
|
g_awzrBindingList[i].pszEndpoint,
|
|
NULL,
|
|
&pwszStringBinding);
|
|
if (S_OK != hr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
hr = RpcBindingFromStringBinding(
|
|
pwszStringBinding,
|
|
phRPCCertServer);
|
|
if (NULL != pwszStringBinding)
|
|
{
|
|
RpcStringFree(&pwszStringBinding);
|
|
}
|
|
if (S_OK != hr)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
hr = RpcEpResolveBinding(
|
|
*phRPCCertServer,
|
|
ICertPassage_v0_0_c_ifspec);
|
|
if (S_OK == hr)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
_JumpIfError(hr, error, "RPC Resolve Binding Loop");
|
|
|
|
hr = crSetRPCSecurity(*phRPCCertServer, prpcAuthProtocol);
|
|
_JumpIfError(hr, error, "_SetRPCSecurity");
|
|
|
|
error:
|
|
if (NULL != pwszStringBinding)
|
|
{
|
|
RpcStringFree(&pwszStringBinding);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
VOID
|
|
crCloseRPCConnection(
|
|
IN OUT handle_t *phRPCCertServer)
|
|
{
|
|
if (NULL != *phRPCCertServer)
|
|
{
|
|
RpcBindingFree(phRPCCertServer);
|
|
*phRPCCertServer = NULL;
|
|
}
|
|
}
|
|
|
|
|
|
HRESULT
|
|
crCertServerRequest(
|
|
IN handle_t hRPCCertServer,
|
|
IN OUT INT *prpcAuthProtocol,
|
|
IN DWORD Flags,
|
|
IN WCHAR const *pwszAuthority,
|
|
IN OUT DWORD *pRequestId,
|
|
OUT DWORD *pDisposition,
|
|
IN CERTTRANSBLOB const *pctbAttrib,
|
|
IN CERTTRANSBLOB const *pctbSerial,
|
|
IN CERTTRANSBLOB const *pctbRequest,
|
|
OUT CERTTRANSBLOB *pctbCertChain,
|
|
OUT CERTTRANSBLOB *pctbCert,
|
|
OUT CERTTRANSBLOB *pctbDispositionMessage)
|
|
{
|
|
HRESULT hr;
|
|
|
|
RPC_TIMEOUT_CONTEXT Timeout = {NULL, NULL, NULL, S_OK};
|
|
|
|
do
|
|
{
|
|
// Midl_user_allocate registers memory in RPC case
|
|
|
|
hr = crRegisterRPCCallTimeout(CR_RPC_REQUEST_TIMEOUT, &Timeout);
|
|
_JumpIfError(hr, error, "crRegisterRPCCallTimeout");
|
|
|
|
// for pending requests, pass the serial number in pctbAttrib
|
|
|
|
if (NULL == pctbAttrib ||
|
|
NULL == pctbAttrib->pb ||
|
|
0 == pctbAttrib->cb)
|
|
{
|
|
pctbAttrib = pctbSerial;
|
|
}
|
|
|
|
__try
|
|
{
|
|
hr = CertServerRequest(
|
|
hRPCCertServer,
|
|
Flags,
|
|
pwszAuthority,
|
|
pRequestId,
|
|
pDisposition,
|
|
pctbAttrib, // or SerialNumber
|
|
pctbRequest,
|
|
pctbCertChain,
|
|
pctbCert,
|
|
pctbDispositionMessage);
|
|
}
|
|
__except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
}
|
|
if (HRESULT_FROM_WIN32(RPC_S_CALL_CANCELLED) == hr)
|
|
{
|
|
hr = Timeout.hrRpcError;
|
|
crCloseRPCCallTimeout(&Timeout);
|
|
}
|
|
_PrintIfError(hr, "CertServerRequest");
|
|
|
|
if (hr == RPC_S_UNKNOWN_AUTHN_SERVICE)
|
|
{
|
|
(*prpcAuthProtocol)++;
|
|
hr = crSetRPCSecurity(hRPCCertServer, prpcAuthProtocol);
|
|
if (hr == RPC_S_UNKNOWN_AUTHN_SERVICE)
|
|
{
|
|
break;
|
|
}
|
|
if (hr == S_OK)
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
} while (hr == RPC_S_UNKNOWN_AUTHN_SERVICE);
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
crRequestCertificate(
|
|
IN DWORD Flags,
|
|
OPTIONAL IN BYTE const *pbRequest,
|
|
IN DWORD cbRequest,
|
|
IN DWORD RequestId,
|
|
OPTIONAL IN WCHAR const *pwszRequestAttributes,
|
|
OPTIONAL IN WCHAR const *pwszSerialNumber,
|
|
IN WCHAR const *pwszServerName,
|
|
IN WCHAR const *pwszAuthority,
|
|
OUT CERTSERVERENROLL **ppcsEnroll) // free via CertServerFreeMemory
|
|
{
|
|
HRESULT hr;
|
|
handle_t hRPCCertServer = NULL;
|
|
INT rpcAuthProtocol = 0;
|
|
CERTTRANSBLOB ctbRequest;
|
|
CERTTRANSBLOB ctbAttrib;
|
|
CERTTRANSBLOB ctbSerial;
|
|
CERTTRANSBLOB ctbCert = { 0, NULL };
|
|
CERTTRANSBLOB ctbCertChain = { 0, NULL };
|
|
CERTTRANSBLOB ctbDispositionMessage = { 0, NULL };
|
|
CERTSERVERENROLL csEnroll;
|
|
CERTSERVERENROLL *pcsEnroll = NULL;
|
|
BYTE *pbOut;
|
|
DWORD cbAlloc;
|
|
|
|
if (NULL == pwszServerName || NULL == pwszAuthority || NULL == ppcsEnroll)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "NULL parm");
|
|
}
|
|
*ppcsEnroll = NULL;
|
|
|
|
ZeroMemory(&csEnroll, sizeof(csEnroll));
|
|
csEnroll.hrLastStatus = E_FAIL;
|
|
csEnroll.Disposition = CR_DISP_ERROR;
|
|
csEnroll.RequestId = RequestId;
|
|
|
|
ctbRequest.pb = const_cast<BYTE *>(pbRequest);
|
|
ctbRequest.cb = cbRequest;
|
|
|
|
ctbAttrib.pb = (BYTE *) pwszRequestAttributes;
|
|
ctbAttrib.cb = 0;
|
|
if (NULL != pwszRequestAttributes)
|
|
{
|
|
ctbAttrib.cb = (wcslen(pwszRequestAttributes) + 1) * sizeof(WCHAR);
|
|
}
|
|
|
|
ctbSerial.pb = (BYTE *) pwszSerialNumber;
|
|
ctbSerial.cb = 0;
|
|
if (NULL != pwszSerialNumber)
|
|
{
|
|
ctbSerial.cb = (wcslen(pwszSerialNumber) + 1) * sizeof(WCHAR);
|
|
}
|
|
|
|
hr = crOpenRPCConnection(pwszServerName, &rpcAuthProtocol, &hRPCCertServer);
|
|
_JumpIfError(hr, error, "crOpenRPCConnection");
|
|
|
|
hr = crCertServerRequest(
|
|
hRPCCertServer,
|
|
&rpcAuthProtocol,
|
|
Flags,
|
|
pwszAuthority,
|
|
&csEnroll.RequestId,
|
|
&csEnroll.Disposition,
|
|
&ctbAttrib,
|
|
&ctbSerial,
|
|
&ctbRequest,
|
|
&ctbCertChain,
|
|
&ctbCert,
|
|
&ctbDispositionMessage);
|
|
_JumpIfError(hr, error, "crCertServerRequest");
|
|
|
|
csEnroll.hrLastStatus = hr;
|
|
if (FAILED(csEnroll.Disposition))
|
|
{
|
|
csEnroll.hrLastStatus = csEnroll.Disposition;
|
|
csEnroll.Disposition = CR_DISP_DENIED;
|
|
}
|
|
|
|
cbAlloc = sizeof(*pcsEnroll) +
|
|
DWORDROUND(ctbCert.cb) +
|
|
DWORDROUND(ctbCertChain.cb) +
|
|
DWORDROUND(ctbDispositionMessage.cb);
|
|
|
|
pcsEnroll = (CERTSERVERENROLL *) LocalAlloc(LMEM_FIXED, cbAlloc);
|
|
if (NULL == pcsEnroll)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
*pcsEnroll = csEnroll; // structure copy
|
|
|
|
pbOut = (BYTE *) &pcsEnroll[1];
|
|
if (0 != ctbCert.cb)
|
|
{
|
|
CSASSERT(NULL != ctbCert.pb);
|
|
pcsEnroll->pbCert = pbOut;
|
|
pcsEnroll->cbCert = ctbCert.cb;
|
|
CopyMemory(pbOut, ctbCert.pb, ctbCert.cb);
|
|
pbOut += DWORDROUND(ctbCert.cb);
|
|
}
|
|
if (0 != ctbCertChain.cb)
|
|
{
|
|
CSASSERT(NULL != ctbCertChain.pb);
|
|
pcsEnroll->pbCertChain = pbOut;
|
|
pcsEnroll->cbCertChain = ctbCertChain.cb;
|
|
CopyMemory(pbOut, ctbCertChain.pb, ctbCertChain.cb);
|
|
pbOut += DWORDROUND(ctbCertChain.cb);
|
|
}
|
|
if (0 != ctbDispositionMessage.cb)
|
|
{
|
|
CSASSERT(NULL != ctbDispositionMessage.pb);
|
|
pcsEnroll->pwszDispositionMessage = (WCHAR *) pbOut;
|
|
CopyMemory(pbOut, ctbDispositionMessage.pb, ctbDispositionMessage.cb);
|
|
pbOut += DWORDROUND(ctbDispositionMessage.cb);
|
|
}
|
|
CSASSERT(pbOut == &((BYTE *) pcsEnroll)[cbAlloc]);
|
|
|
|
*ppcsEnroll = pcsEnroll;
|
|
|
|
error:
|
|
if (NULL != ctbCert.pb)
|
|
{
|
|
MIDL_user_free(ctbCert.pb);
|
|
}
|
|
if (NULL != ctbCertChain.pb)
|
|
{
|
|
MIDL_user_free(ctbCertChain.pb);
|
|
}
|
|
if (NULL != ctbDispositionMessage.pb)
|
|
{
|
|
MIDL_user_free(ctbDispositionMessage.pb);
|
|
}
|
|
crCloseRPCConnection(&hRPCCertServer);
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CertServerSubmitRequest(
|
|
IN DWORD Flags,
|
|
IN BYTE const *pbRequest,
|
|
IN DWORD cbRequest,
|
|
OPTIONAL IN WCHAR const *pwszRequestAttributes,
|
|
IN WCHAR const *pwszServerName,
|
|
IN WCHAR const *pwszAuthority,
|
|
OUT CERTSERVERENROLL **ppcsEnroll) // free via CertServerFreeMemory
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (NULL == pbRequest)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "NULL parm");
|
|
}
|
|
if (CR_IN_BINARY != (CR_IN_ENCODEMASK & Flags))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "not CR_IN_BINARY");
|
|
}
|
|
hr = crRequestCertificate(
|
|
Flags,
|
|
pbRequest,
|
|
cbRequest,
|
|
0, // RequestId
|
|
pwszRequestAttributes,
|
|
NULL, // pwszSerialNumber
|
|
pwszServerName,
|
|
pwszAuthority,
|
|
ppcsEnroll);
|
|
_JumpIfError(hr, error, "crRequestCertificate");
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CertServerRetrievePending(
|
|
IN DWORD RequestId,
|
|
OPTIONAL IN WCHAR const *pwszSerialNumber,
|
|
IN WCHAR const *pwszServerName,
|
|
IN WCHAR const *pwszAuthority,
|
|
OUT CERTSERVERENROLL **ppcsEnroll) // free via CertServerFreeMemory
|
|
{
|
|
HRESULT hr;
|
|
|
|
if ((0 == RequestId) ^ (NULL != pwszSerialNumber))
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "use RequestId OR pwszSerialNumber");
|
|
}
|
|
hr = crRequestCertificate(
|
|
0, // Flags
|
|
NULL, // pbRequest
|
|
0, // cbRequest
|
|
RequestId,
|
|
NULL, // pwszRequestAttributes
|
|
pwszSerialNumber,
|
|
pwszServerName,
|
|
pwszAuthority,
|
|
ppcsEnroll);
|
|
_JumpIfError(hr, error, "crRequestCertificate");
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
VOID
|
|
CertServerFreeMemory(
|
|
IN VOID *pv)
|
|
{
|
|
LocalFree(pv);
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CCertRequest::~CCertRequest -- destructor
|
|
//
|
|
// free memory associated with this instance
|
|
//+--------------------------------------------------------------------------
|
|
|
|
CCertRequest::~CCertRequest()
|
|
{
|
|
_Cleanup();
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CCertRequest::_CleanupOldConnection -- free memory
|
|
//
|
|
// free memory associated with this instance
|
|
//+--------------------------------------------------------------------------
|
|
|
|
VOID
|
|
CCertRequest::_CleanupOldConnection()
|
|
{
|
|
// bytes returned from interfaces are MIDL_user_allocate
|
|
|
|
_CleanupCAPropInfo();
|
|
|
|
if (NULL != m_pwszDispositionMessage)
|
|
{
|
|
MIDL_user_free(m_pwszDispositionMessage);
|
|
m_pwszDispositionMessage = NULL;
|
|
}
|
|
if (NULL != m_pbCert)
|
|
{
|
|
MIDL_user_free(m_pbCert);
|
|
m_pbCert = NULL;
|
|
}
|
|
if (NULL != m_pbCertificateChain)
|
|
{
|
|
MIDL_user_free(m_pbCertificateChain);
|
|
m_pbCertificateChain = NULL;
|
|
}
|
|
if (NULL != m_pbFullResponse)
|
|
{
|
|
MIDL_user_free(m_pbFullResponse);
|
|
m_pbFullResponse = NULL;
|
|
}
|
|
if (NULL != m_pbRequest)
|
|
{
|
|
LocalFree(m_pbRequest);
|
|
m_pbRequest = NULL;
|
|
}
|
|
if (NULL != m_rgResponse)
|
|
{
|
|
FreeCMCResponse(m_rgResponse, m_cResponse);
|
|
m_rgResponse = NULL;
|
|
}
|
|
if (NULL != m_hStoreResponse)
|
|
{
|
|
CertCloseStore(m_hStoreResponse, CERT_CLOSE_STORE_CHECK_FLAG);
|
|
m_hStoreResponse = NULL;
|
|
}
|
|
m_cResponse = 0;
|
|
m_LastStatus = S_OK;
|
|
m_RequestId = 0;
|
|
m_Disposition = 0;
|
|
_CleanupCAPropInfo();
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CCertRequest::_Cleanup -- free memory
|
|
//
|
|
// free memory associated with this instance
|
|
//+--------------------------------------------------------------------------
|
|
|
|
VOID
|
|
CCertRequest::_Cleanup()
|
|
{
|
|
_CloseConnection();
|
|
_CleanupOldConnection();
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CCertRequest::_OpenRPCConnection -- establish RPC connection
|
|
//
|
|
// establish RPC connection
|
|
//+--------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CCertRequest::_OpenRPCConnection(
|
|
IN WCHAR const *pwszConfig,
|
|
OUT BOOL *pfNewConnection,
|
|
OUT WCHAR const **ppwszAuthority)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR *pwszServerName = NULL;
|
|
WCHAR *pwsz;
|
|
DWORD cwc;
|
|
|
|
CSASSERT(NULL != pwszConfig && NULL != pfNewConnection);
|
|
|
|
*pfNewConnection = FALSE;
|
|
pwsz = wcschr(pwszConfig, L'\\');
|
|
if (NULL == pwsz)
|
|
{
|
|
cwc = wcslen(pwszConfig);
|
|
*ppwszAuthority = &pwszConfig[cwc];
|
|
}
|
|
else
|
|
{
|
|
cwc = SAFE_SUBTRACT_POINTERS(pwsz, pwszConfig);
|
|
*ppwszAuthority = &pwsz[1];
|
|
}
|
|
pwszServerName = (WCHAR *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
(cwc + 1) * sizeof(WCHAR));
|
|
if (NULL == pwszServerName)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
CopyMemory(pwszServerName, pwszConfig, cwc * sizeof(WCHAR));
|
|
pwszServerName[cwc] = L'\0';
|
|
|
|
if (NULL == m_hRPCCertServer ||
|
|
NULL == m_pwszServerName ||
|
|
0 != mylstrcmpiL(pwszServerName, m_pwszServerName))
|
|
{
|
|
_CloseConnection();
|
|
CSASSERT(NULL == m_pwszServerName);
|
|
m_pwszServerName = pwszServerName;
|
|
pwszServerName = NULL;
|
|
|
|
hr = crOpenRPCConnection(
|
|
m_pwszServerName,
|
|
&m_rpcAuthProtocol,
|
|
&m_hRPCCertServer);
|
|
_JumpIfError(hr, error, "crOpenRPCConnection");
|
|
|
|
*pfNewConnection = TRUE;
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (S_OK != hr)
|
|
{
|
|
_CloseConnection();
|
|
hr = myHError(hr);
|
|
}
|
|
if (NULL != pwszServerName)
|
|
{
|
|
LocalFree(pwszServerName);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CCertRequest::_OpenConnection -- establish RPC connection
|
|
//
|
|
//+--------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CCertRequest::_OpenConnection(
|
|
IN BOOL fRPC,
|
|
IN WCHAR const *pwszConfig,
|
|
IN DWORD RequiredVersion,
|
|
OUT WCHAR const **ppwszAuthority)
|
|
{
|
|
HRESULT hr;
|
|
BOOL fNewConnection = FALSE;
|
|
|
|
if (NULL == pwszConfig)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "pwszConfig");
|
|
}
|
|
if (fRPC)
|
|
{
|
|
if (NULL != m_pICertRequestD)
|
|
{
|
|
_CloseConnection(); // switching to RPC
|
|
}
|
|
hr = _OpenRPCConnection(pwszConfig, &fNewConnection, ppwszAuthority);
|
|
_JumpIfError(hr, error, "_OpenRPCConnection");
|
|
|
|
CSASSERT(NULL != m_hRPCCertServer);
|
|
CSASSERT(0 == m_dwServerVersion);
|
|
}
|
|
else
|
|
{
|
|
if (NULL != m_hRPCCertServer)
|
|
{
|
|
_CloseConnection(); // switching to DCOM
|
|
}
|
|
hr = myOpenRequestDComConnection(
|
|
pwszConfig,
|
|
ppwszAuthority,
|
|
&m_pwszServerName,
|
|
&fNewConnection,
|
|
&m_dwServerVersion,
|
|
&m_pICertRequestD);
|
|
_JumpIfError(hr, error, "myOpenRequestDComConnection");
|
|
|
|
CSASSERT(NULL != m_pICertRequestD);
|
|
CSASSERT(0 != m_dwServerVersion);
|
|
}
|
|
if (m_dwServerVersion < RequiredVersion)
|
|
{
|
|
hr = RPC_E_VERSION_MISMATCH;
|
|
_JumpError(hr, error, "old server");
|
|
}
|
|
if (fNewConnection)
|
|
{
|
|
_CleanupOldConnection();
|
|
}
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CCertRequest::_CloseConnection -- release DCOM object
|
|
//
|
|
//+--------------------------------------------------------------------------
|
|
|
|
VOID
|
|
CCertRequest::_CloseConnection()
|
|
{
|
|
crCloseRPCConnection(&m_hRPCCertServer);
|
|
myCloseDComConnection((IUnknown **) &m_pICertRequestD, &m_pwszServerName);
|
|
m_dwServerVersion = 0;
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CCertRequest::Submit -- Submit a cert request and return the disposition.
|
|
//
|
|
// Submit the passed certificate request to the Certificate Server and retrieve
|
|
// the certificate from the server, if it is immediately available. If the
|
|
// returned disposition so indicates, other CCertRequest methods may be called
|
|
// to return the certificate or certificate chain to the caller.
|
|
//
|
|
// All state from previous method calls is cleared.
|
|
//
|
|
// After the Submit method completes execution, the GetDispositionMessage and
|
|
// GetLastStatus methods may be called to retrieve informational disposition
|
|
// text and a more specific error code.
|
|
//
|
|
// Flags contains flags that describe the input data format as defined above.
|
|
//
|
|
// strRequest points to the input request data, in base64-encoded form.
|
|
//
|
|
// strAttributes is optional. When non-NULL, it points to a string containing
|
|
// attribute value pairs, one pair per line. The attribute name and value
|
|
// strings may contain any text of the caller's choosing. Only the syntax of a
|
|
// colon-separated attribute name and value string followed by a newline is
|
|
// enforced. Attribute names that are not understood by the Certificate Server
|
|
// will be available to Policy Modules, but are otherwise ignored by the
|
|
// Certificate Server.
|
|
// Example:
|
|
// "Phone: 0424-12-3456\r\nServer: Microsoft Key Manager for IIS 2.0\r\n"
|
|
// "Version: 3\r\nRequestType: Client\r\n"
|
|
//
|
|
// strConfig points to a string that contains the server name and Certificate
|
|
// Authority name. See the ICertConfig interface.
|
|
//
|
|
// pDisposition points to the returned disposition of the request as defined
|
|
// above. When the request cannot be immediately granted or denied (some off-
|
|
// line processing may be required), *pDisposition is set to
|
|
// CR_DISP_UNDER_SUBMISSION. After CR_DISP_UNDER_SUBMISSION is returned for
|
|
// the initial request's disposition, the RetrievePending method may be called
|
|
// to interrogate the disposition again and to retrieve the certificate if it
|
|
// has been issued. If the returned disposition so indicates, RetrievePending
|
|
// will retrieve the certificate and allow the other methods defined here to
|
|
// return the certificate to the caller. If denied, the appropriate
|
|
// disposition code will be returned. If the request has still not been
|
|
// processed, CR_DISP_UNDER_SUBMISSION will again be returned by the
|
|
// RetrievePending method.
|
|
//
|
|
// Returns S_OK if the method completed execution. Errors are indicated by
|
|
// the returned disposition.
|
|
//+--------------------------------------------------------------------------
|
|
|
|
|
|
STDMETHODIMP
|
|
CCertRequest::Submit(
|
|
/* [in] */ LONG Flags,
|
|
/* [in] */ BSTR const strRequest,
|
|
/* [in] */ BSTR const strAttributes,
|
|
/* [in] */ BSTR const strConfig,
|
|
/* [out, retval] */ LONG __RPC_FAR *pDisposition)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if ((NULL == strRequest) || (NULL == pDisposition))
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "strRequest or pDisposition");
|
|
}
|
|
hr = _RequestCertificate(
|
|
Flags,
|
|
0, // RequestId
|
|
strRequest,
|
|
strAttributes,
|
|
NULL, // pwszSerialNumber
|
|
strConfig,
|
|
(CR_IN_RPC & Flags)? 0 : 1, // RequiredVersion
|
|
pDisposition);
|
|
_JumpIfError2(
|
|
hr,
|
|
error,
|
|
"_RequestCertificate",
|
|
HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
|
|
|
|
error:
|
|
return(_SetErrorInfo(hr, L"CCertRequest::Submit"));
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CCertRequest::RetrievePending -- Retrieve pending request disposition.
|
|
//
|
|
// Interrogate the Certificate Server and retrieve the certificate identified
|
|
// by the passed RequestId, if it is now available. If the returned
|
|
// disposition so indicates, other CCertRequest methods may be called to return
|
|
// the certificate or certificate chain to the caller.
|
|
//
|
|
// All state from previous method calls is cleared.
|
|
//
|
|
// After the RetrievePending method completes execution, the
|
|
// GetDispositionMessage and GetLastStatus methods may be called to retrieve
|
|
// informational disposition text and a more specific error code.
|
|
//
|
|
// RequestId identifies a previously submitted request.
|
|
//
|
|
// strConfig points to a string that contains the server name and Certificate
|
|
// Authority name.
|
|
//
|
|
// pDisposition points to the returned disposition of the pending request.
|
|
//
|
|
// Returns S_OK if the method completed execution. Errors are indicated by
|
|
// the returned disposition.
|
|
//+--------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CCertRequest::RetrievePending(
|
|
/* [in] */ LONG RequestId,
|
|
/* [in] */ BSTR const strConfig,
|
|
/* [out, retval] */ LONG __RPC_FAR *pDisposition)
|
|
{
|
|
HRESULT hr;
|
|
BSTR strConfigT = strConfig;
|
|
WCHAR *pwszSerialNumber = NULL;
|
|
|
|
if (NULL == pDisposition || NULL == strConfig)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "NULL param");
|
|
}
|
|
if (0 == RequestId)
|
|
{
|
|
DWORD cwc;
|
|
|
|
pwszSerialNumber = wcschr(strConfigT, L'\\');
|
|
if (NULL != pwszSerialNumber)
|
|
{
|
|
pwszSerialNumber = wcschr(&pwszSerialNumber[1], L'\\');
|
|
}
|
|
if (NULL == pwszSerialNumber)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "Missing SerialNumber");
|
|
}
|
|
|
|
cwc = SAFE_SUBTRACT_POINTERS(pwszSerialNumber, strConfigT);
|
|
pwszSerialNumber++;
|
|
strConfigT = SysAllocStringLen(strConfigT, cwc);
|
|
if (NULL == strConfigT)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "SysAllocStringLen");
|
|
}
|
|
}
|
|
hr = _RequestCertificate(
|
|
0, // Flags
|
|
RequestId,
|
|
NULL, // strRequest
|
|
NULL, // strAttributes
|
|
pwszSerialNumber,
|
|
strConfigT,
|
|
1, // RequiredVersion
|
|
pDisposition);
|
|
_JumpIfError(hr, error, "_RequestCertificate");
|
|
|
|
error:
|
|
if (NULL != strConfigT && strConfig != strConfigT)
|
|
{
|
|
SysFreeString(strConfigT);
|
|
}
|
|
return(_SetErrorInfo(hr, L"CCertRequest::RetrievePending"));
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CCertRequest::GetIssuedCertificate -- Get an issued Certificate
|
|
//
|
|
// Returns S_OK on success.
|
|
//+--------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CCertRequest::GetIssuedCertificate(
|
|
/* [in] */ const BSTR strConfig,
|
|
/* [in] */ LONG RequestId,
|
|
/* [in] */ const BSTR strSerialNumber, // OPTIONAL
|
|
/* [out, retval] */ LONG __RPC_FAR *pDisposition)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR const *pwszSerialNumber = NULL;
|
|
|
|
if (NULL == pDisposition || NULL == strConfig)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "NULL param");
|
|
}
|
|
|
|
// VB callers pass "" instead of NULL, so treat them identically.
|
|
|
|
if (NULL != strSerialNumber && L'\0' != *strSerialNumber)
|
|
{
|
|
pwszSerialNumber = strSerialNumber;
|
|
}
|
|
hr = _RequestCertificate(
|
|
0, // Flags
|
|
RequestId,
|
|
NULL, // strRequest
|
|
NULL, // strAttributes
|
|
pwszSerialNumber, // pwszSerialNumber
|
|
strConfig,
|
|
2, // RequiredVersion
|
|
pDisposition);
|
|
_JumpIfError(hr, error, "_RequestCertificate");
|
|
|
|
error:
|
|
return(_SetErrorInfo(hr, L"CCertRequest::GetIssuedCertificate"));
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CCertRequest::_RequestCertificate -- Submit the request
|
|
//
|
|
// Returns S_OK on success.
|
|
//+--------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CCertRequest::_RequestCertificate(
|
|
IN LONG Flags,
|
|
IN LONG RequestId,
|
|
OPTIONAL IN BSTR const strRequest,
|
|
OPTIONAL IN BSTR const strAttributes,
|
|
OPTIONAL IN WCHAR const *pwszSerialNumber,
|
|
IN BSTR const strConfig,
|
|
IN DWORD RequiredVersion,
|
|
OUT LONG *pDisposition)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR const *pwszAuthority;
|
|
WCHAR *pwszAttrib = strAttributes;
|
|
WCHAR *pwszAttribAlloc = NULL;
|
|
BYTE *pbT = NULL;
|
|
CERTTRANSBLOB ctbRequest = { 0, NULL };
|
|
CERTTRANSBLOB ctbCert = { 0, NULL };
|
|
CERTTRANSBLOB ctbCertChain = { 0, NULL };
|
|
CERTTRANSBLOB ctbFullResponse = { 0, NULL };
|
|
CERTTRANSBLOB ctbDispositionMessage = { 0, NULL };
|
|
DWORD adwEncode[] = { CR_IN_BASE64HEADER, CR_IN_BASE64, CR_IN_BINARY };
|
|
DWORD dwEncode;
|
|
DWORD *pdwEncode;
|
|
DWORD cEncode;
|
|
WCHAR *pwszDnsName = NULL;
|
|
|
|
if (NULL == pDisposition)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "NULL parm");
|
|
}
|
|
_Cleanup();
|
|
*pDisposition = CR_DISP_INCOMPLETE;
|
|
|
|
hr = _OpenConnection(
|
|
(CR_IN_RPC & Flags)? TRUE : FALSE,
|
|
strConfig,
|
|
RequiredVersion,
|
|
&pwszAuthority);
|
|
_JumpIfError(hr, error, "_OpenConnection");
|
|
|
|
// If a new request, point at the attributes & decode the Base64 request.
|
|
|
|
if (NULL != strRequest)
|
|
{
|
|
DWORD cchHeader;
|
|
DWORD cwc;
|
|
WCHAR *pch;
|
|
|
|
CSASSERT(CR_IN_BASE64HEADER == CRYPT_STRING_BASE64HEADER);
|
|
CSASSERT(CR_IN_BASE64 == CRYPT_STRING_BASE64);
|
|
CSASSERT(CR_IN_BINARY == CRYPT_STRING_BINARY);
|
|
|
|
hr = myGetMachineDnsName(&pwszDnsName);
|
|
_JumpIfError(hr, error, "myGetMachineDnsName");
|
|
|
|
dwEncode = CR_IN_ENCODEMASK & Flags;
|
|
switch (dwEncode)
|
|
{
|
|
case CR_IN_BASE64HEADER:
|
|
case CR_IN_BASE64:
|
|
case CR_IN_BINARY:
|
|
cEncode = 1;
|
|
pdwEncode = &dwEncode;
|
|
break;
|
|
|
|
case CR_IN_ENCODEANY:
|
|
cEncode = ARRAYSIZE(adwEncode);
|
|
pdwEncode = adwEncode;
|
|
break;
|
|
|
|
default:
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "Flags");
|
|
}
|
|
while (TRUE)
|
|
{
|
|
hr = DecodeCertString(
|
|
strRequest,
|
|
*pdwEncode,
|
|
&m_pbRequest,
|
|
(DWORD *) &m_cbRequest);
|
|
if (S_OK == hr)
|
|
{
|
|
Flags = (~CR_IN_ENCODEMASK & Flags) | *pdwEncode;
|
|
break;
|
|
}
|
|
if (1 == cEncode || HRESULT_FROM_WIN32(ERROR_INVALID_DATA) != hr)
|
|
{
|
|
_JumpError(hr, error, "DecodeCertString");
|
|
}
|
|
_PrintErrorStr2(
|
|
hr,
|
|
"DecodeCertString",
|
|
L"CR_IN_ENCODEANY",
|
|
HRESULT_FROM_WIN32(ERROR_INVALID_DATA));
|
|
cEncode--;
|
|
pdwEncode++;
|
|
}
|
|
CSASSERT(0 < cEncode);
|
|
CSASSERT(S_OK == hr);
|
|
|
|
ctbRequest.pb = m_pbRequest;
|
|
ctbRequest.cb = m_cbRequest;
|
|
|
|
cchHeader = 0;
|
|
if (CR_IN_BASE64HEADER == *pdwEncode)
|
|
{
|
|
DWORD cb;
|
|
|
|
hr = myCryptStringToBinary(
|
|
strRequest,
|
|
wcslen(strRequest),
|
|
CRYPT_STRING_BASE64HEADER,
|
|
&pbT,
|
|
&cb,
|
|
&cchHeader,
|
|
NULL);
|
|
if (S_OK != hr)
|
|
{
|
|
cchHeader = 0;
|
|
}
|
|
}
|
|
cwc = cchHeader;
|
|
if (NULL != pwszAttrib)
|
|
{
|
|
cwc += 1 + wcslen(pwszAttrib);
|
|
}
|
|
cwc += 1 + WSZARRAYSIZE(wszPROPCERTCLIENTMACHINE) + 1 + wcslen(pwszDnsName);
|
|
pwszAttribAlloc = (WCHAR *) LocalAlloc(
|
|
LMEM_FIXED,
|
|
(cwc + 1) * sizeof(WCHAR));
|
|
if (NULL == pwszAttribAlloc)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "alloc attributes");
|
|
}
|
|
pch = pwszAttribAlloc;
|
|
if (0 != cchHeader)
|
|
{
|
|
CopyMemory(pch, strRequest, cchHeader * sizeof(WCHAR));
|
|
pch += cchHeader;
|
|
}
|
|
*pch = L'\0';
|
|
if (NULL != pwszAttrib)
|
|
{
|
|
*pch++ = L'\n';
|
|
wcscpy(pch, (WCHAR const *) pwszAttrib);
|
|
}
|
|
wcscat(pch, L"\n" wszPROPCERTCLIENTMACHINE L":");
|
|
wcscat(pch, pwszDnsName);
|
|
CSASSERT(wcslen(pwszAttribAlloc) == cwc);
|
|
|
|
pwszAttrib = pwszAttribAlloc;
|
|
}
|
|
m_RequestId = RequestId;
|
|
|
|
__try
|
|
{
|
|
Flags |= CR_IN_FULLRESPONSE;
|
|
if (NULL != m_hRPCCertServer)
|
|
{
|
|
CERTTRANSBLOB ctbAttrib;
|
|
CERTTRANSBLOB ctbSerial;
|
|
|
|
ctbAttrib.cb = 0;
|
|
ctbAttrib.pb = (BYTE *) pwszAttrib;
|
|
if (NULL != pwszAttrib)
|
|
{
|
|
ctbAttrib.cb = (wcslen(pwszAttrib) + 1) * sizeof(WCHAR);
|
|
}
|
|
|
|
ctbSerial.cb = 0;
|
|
ctbSerial.pb = (BYTE *) pwszSerialNumber;
|
|
if (NULL != pwszSerialNumber)
|
|
{
|
|
ctbAttrib.cb = (wcslen(pwszSerialNumber) + 1) * sizeof(WCHAR);
|
|
}
|
|
|
|
hr = crCertServerRequest(
|
|
m_hRPCCertServer,
|
|
&m_rpcAuthProtocol,
|
|
Flags,
|
|
pwszAuthority,
|
|
(DWORD *) &m_RequestId,
|
|
(DWORD *) &m_Disposition,
|
|
&ctbAttrib,
|
|
&ctbSerial,
|
|
&ctbRequest,
|
|
&ctbCertChain,
|
|
&ctbCert,
|
|
&ctbDispositionMessage);
|
|
_PrintIfError(hr, "crCertServerRequest");
|
|
}
|
|
else
|
|
{
|
|
if (2 <= m_dwServerVersion)
|
|
{
|
|
hr = m_pICertRequestD->Request2(
|
|
pwszAuthority,
|
|
Flags,
|
|
pwszSerialNumber,
|
|
(DWORD *) &m_RequestId,
|
|
(DWORD *) &m_Disposition,
|
|
pwszAttrib,
|
|
&ctbRequest,
|
|
&ctbFullResponse,
|
|
&ctbCert,
|
|
&ctbDispositionMessage);
|
|
_PrintIfError(hr, "m_pICertRequestD->Request2");
|
|
}
|
|
else
|
|
{
|
|
Flags &= ~CR_IN_FULLRESPONSE;
|
|
hr = m_pICertRequestD->Request(
|
|
Flags,
|
|
pwszAuthority,
|
|
(DWORD *) &m_RequestId,
|
|
(DWORD *) &m_Disposition,
|
|
pwszAttrib,
|
|
&ctbRequest,
|
|
&ctbCertChain,
|
|
&ctbCert,
|
|
&ctbDispositionMessage);
|
|
_PrintIfError(hr, "m_pICertRequestD->Request");
|
|
}
|
|
|
|
// Midl_user_allocate registers memory in RPC case
|
|
|
|
if (NULL != ctbCertChain.pb)
|
|
{
|
|
myRegisterMemAlloc(
|
|
ctbCertChain.pb,
|
|
ctbCertChain.cb,
|
|
CSM_COTASKALLOC);
|
|
}
|
|
if (NULL != ctbFullResponse.pb)
|
|
{
|
|
myRegisterMemAlloc(
|
|
ctbFullResponse.pb,
|
|
ctbFullResponse.cb,
|
|
CSM_COTASKALLOC);
|
|
}
|
|
if (NULL != ctbCert.pb)
|
|
{
|
|
myRegisterMemAlloc(ctbCert.pb, ctbCert.cb, CSM_COTASKALLOC);
|
|
}
|
|
if (NULL != ctbDispositionMessage.pb)
|
|
{
|
|
myRegisterMemAlloc(
|
|
ctbDispositionMessage.pb,
|
|
ctbDispositionMessage.cb,
|
|
CSM_COTASKALLOC);
|
|
}
|
|
}
|
|
}
|
|
__except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
}
|
|
|
|
if (HRESULT_FROM_WIN32(RPC_X_WRONG_STUB_VERSION) == hr)
|
|
{
|
|
_PrintError(hr, "Compile with MIDL_NO_ROBUST=1 to run on NT 4");
|
|
}
|
|
|
|
m_LastStatus = hr;
|
|
_JumpIfError(hr, error, "Request");
|
|
|
|
if (FAILED(m_Disposition))
|
|
{
|
|
m_LastStatus = m_Disposition;
|
|
m_Disposition = CR_DISP_DENIED;
|
|
}
|
|
|
|
*pDisposition = m_Disposition;
|
|
m_pbCertificateChain = ctbCertChain.pb; // CoTaskMem*
|
|
m_cbCertificateChain = ctbCertChain.cb;
|
|
m_pbFullResponse = ctbFullResponse.pb; // CoTaskMem*
|
|
m_cbFullResponse = ctbFullResponse.cb;
|
|
m_pbCert = ctbCert.pb; // CoTaskMem*
|
|
m_cbCert = ctbCert.cb;
|
|
m_pwszDispositionMessage = (WCHAR *) ctbDispositionMessage.pb; // CoTaskMem*
|
|
CSASSERT(0 == (ctbDispositionMessage.cb & (sizeof(WCHAR) - 1)));
|
|
CSASSERT(
|
|
NULL == m_pwszDispositionMessage ||
|
|
L'\0' ==
|
|
m_pwszDispositionMessage[ctbDispositionMessage.cb/sizeof(WCHAR) - 1]);
|
|
|
|
if (S_OK == hr && NULL != ctbFullResponse.pb)
|
|
{
|
|
hr = ParseCMCResponse(
|
|
m_pbFullResponse,
|
|
m_cbFullResponse,
|
|
&m_hStoreResponse,
|
|
&m_rgResponse,
|
|
&m_cResponse);
|
|
#if 0 // When all Whistler servers are upgraded to return full responses...
|
|
if (S_OK != hr && NULL != m_hRPCCertServer)
|
|
#else
|
|
if (S_OK != hr)
|
|
#endif
|
|
{
|
|
// Must be an old RPC cert server that ignored CR_IN_FULLRESPONSE,
|
|
// and returned a PKCS7 chain instead.
|
|
|
|
CSASSERT(NULL == m_pbCertificateChain);
|
|
m_pbCertificateChain = m_pbFullResponse;
|
|
m_cbCertificateChain = m_cbFullResponse;
|
|
m_pbFullResponse = NULL;
|
|
m_cbFullResponse = 0;
|
|
hr = S_OK;
|
|
}
|
|
_JumpIfError(hr, error, "ParseCMCResponse");
|
|
}
|
|
|
|
error:
|
|
if (NULL != pwszDnsName)
|
|
{
|
|
LocalFree(pwszDnsName);
|
|
}
|
|
if (NULL != pwszAttribAlloc)
|
|
{
|
|
LocalFree(pwszAttribAlloc);
|
|
}
|
|
if (NULL != pbT)
|
|
{
|
|
LocalFree(pbT);
|
|
}
|
|
return(myHError(hr));
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CCertRequest::GetLastStatus -- Get the status of the last request
|
|
//
|
|
// One of the Submit, RetrievePending or GetCACertificate methods must
|
|
// have been previously called for the returned status to be meaningful.
|
|
//
|
|
// Returns S_OK on success.
|
|
//+--------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CCertRequest::GetLastStatus(
|
|
/* [out, retval] */ LONG __RPC_FAR *pLastStatus)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (NULL == pLastStatus)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "pLastStatus");
|
|
}
|
|
*pLastStatus = m_LastStatus;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return(_SetErrorInfo(hr, L"CCertRequest::GetLastStatus"));
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CCertRequest::GetRequestId -- Get the RequestId of the last request
|
|
//
|
|
// The Submit or RetrievePending method must have been previously called for
|
|
// the returned RequestId to be meaningful.
|
|
//
|
|
// Returns S_OK on success.
|
|
//+--------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CCertRequest::GetRequestId(
|
|
/* [out, retval] */ LONG __RPC_FAR *pRequestId)
|
|
{
|
|
HRESULT hr;
|
|
|
|
if (NULL == pRequestId)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "pRequestId");
|
|
}
|
|
*pRequestId = m_RequestId;
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return(_SetErrorInfo(hr, L"CCertRequest::GetRequestId"));
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CCertRequest::GetDispositionMessage -- Get the Disposition Message
|
|
//
|
|
// The Submit or RetrievePending method must have been previously called for
|
|
// the returned disposition message text to be meaningful.
|
|
//
|
|
// Returns S_OK on success.
|
|
//+--------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CCertRequest::GetDispositionMessage(
|
|
/* [out, retval] */ BSTR __RPC_FAR *pstrDispositionMessage)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
if (NULL == pstrDispositionMessage)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "pstrDispositionMessage");
|
|
}
|
|
if (NULL != *pstrDispositionMessage)
|
|
{
|
|
SysFreeString(*pstrDispositionMessage);
|
|
*pstrDispositionMessage = NULL;
|
|
}
|
|
if (NULL != m_pwszDispositionMessage)
|
|
{
|
|
if (!ConvertWszToBstr(
|
|
pstrDispositionMessage,
|
|
m_pwszDispositionMessage,
|
|
-1))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "ConvertWszToBstr");
|
|
}
|
|
}
|
|
|
|
error:
|
|
return(_SetErrorInfo(hr, L"CCertRequest::GetDispositionMessage"));
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CCertRequest::_BuildIssuedCertificateChain -- Build issued cert chain
|
|
//
|
|
// Returns S_OK on success.
|
|
//+--------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CCertRequest::_BuildIssuedCertificateChain(
|
|
OPTIONAL IN BYTE const *pbCertHash,
|
|
IN DWORD cbCertHash,
|
|
IN BOOL fIncludeCRLs,
|
|
OUT BYTE **ppbCertChain,
|
|
OUT DWORD *pcbCertChain)
|
|
{
|
|
HRESULT hr;
|
|
CERT_CONTEXT const *pccIssued = NULL;
|
|
CERT_CHAIN_PARA CertChainPara;
|
|
CERT_CHAIN_CONTEXT const *pCertChainContext = NULL;
|
|
CERT_SIMPLE_CHAIN *pSimpleChain;
|
|
CRYPT_SIGN_MESSAGE_PARA csmp;
|
|
CRYPT_ALGORITHM_IDENTIFIER DigestAlgorithm = { szOID_OIWSEC_sha1, 0, 0 };
|
|
CERT_CONTEXT const **ppcc;
|
|
DWORD i;
|
|
|
|
*ppbCertChain = NULL;
|
|
|
|
// init csmp for empty signature
|
|
|
|
ZeroMemory(&csmp, sizeof(csmp));
|
|
csmp.cbSize = sizeof(csmp);
|
|
csmp.dwMsgEncodingType = PKCS_7_ASN_ENCODING;
|
|
//csmp.pSigningCert = NULL;
|
|
csmp.HashAlgorithm = DigestAlgorithm;
|
|
//csmp.cMsgCert = 0;
|
|
//csmp.rgpMsgCert = NULL;
|
|
//csmp.cMsgCrl = 0;
|
|
//csmp.rgpMsgCrl = NULL;
|
|
|
|
hr = _FindIssuedCertificate(pbCertHash, cbCertHash, &pccIssued);
|
|
_JumpIfError(hr, error, "_FindIssuedCertificate");
|
|
|
|
// build the user cert chain
|
|
|
|
ZeroMemory(&CertChainPara, sizeof(CertChainPara));
|
|
CertChainPara.cbSize = sizeof(CertChainPara);
|
|
|
|
if (!CertGetCertificateChain(
|
|
HCCE_LOCAL_MACHINE,
|
|
pccIssued,
|
|
NULL, // pTime
|
|
m_hStoreResponse,
|
|
&CertChainPara,
|
|
fIncludeCRLs?
|
|
CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT :
|
|
0,
|
|
NULL, // pvReserved
|
|
&pCertChainContext))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertGetCertificateChain");
|
|
}
|
|
|
|
// make sure there is at least 1 simple chain
|
|
|
|
if (0 == pCertChainContext->cChain)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "No user chain");
|
|
}
|
|
pSimpleChain = pCertChainContext->rgpChain[0];
|
|
|
|
csmp.cMsgCert = pSimpleChain->cElement;
|
|
if (0 == csmp.cMsgCert)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "no certs");
|
|
}
|
|
|
|
csmp.rgpMsgCert = (CERT_CONTEXT const **) LocalAlloc(
|
|
LMEM_FIXED,
|
|
csmp.cMsgCert * sizeof(csmp.rgpMsgCert[0]));
|
|
if (NULL == csmp.rgpMsgCert)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
|
|
if (fIncludeCRLs)
|
|
{
|
|
csmp.rgpMsgCrl = (CRL_CONTEXT const **) LocalAlloc(
|
|
LMEM_FIXED,
|
|
2 * csmp.cMsgCert * sizeof(csmp.rgpMsgCrl[0]));
|
|
if (NULL == csmp.rgpMsgCrl)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
}
|
|
|
|
ppcc = csmp.rgpMsgCert;
|
|
for (i = 0; i < csmp.cMsgCert; i++)
|
|
{
|
|
*ppcc++ = pSimpleChain->rgpElement[i]->pCertContext;
|
|
if (fIncludeCRLs)
|
|
{
|
|
CERT_REVOCATION_INFO *pRevocationInfo;
|
|
|
|
pRevocationInfo = pSimpleChain->rgpElement[i]->pRevocationInfo;
|
|
|
|
if (NULL != pRevocationInfo &&
|
|
CCSIZEOF_STRUCT(CERT_REVOCATION_INFO, pCrlInfo) <=
|
|
pRevocationInfo->cbSize &&
|
|
NULL != pRevocationInfo->pCrlInfo)
|
|
{
|
|
CERT_REVOCATION_CRL_INFO *pCrlInfo;
|
|
|
|
pCrlInfo = pRevocationInfo->pCrlInfo;
|
|
if (NULL != pCrlInfo)
|
|
{
|
|
if (NULL != pCrlInfo->pBaseCrlContext)
|
|
{
|
|
csmp.rgpMsgCrl[csmp.cMsgCrl++] = pCrlInfo->pBaseCrlContext;
|
|
}
|
|
if (NULL != pCrlInfo->pDeltaCrlContext)
|
|
{
|
|
csmp.rgpMsgCrl[csmp.cMsgCrl++] = pCrlInfo->pDeltaCrlContext;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
CSASSERT(csmp.cMsgCrl <= 2 * csmp.cMsgCert);
|
|
|
|
if (!myCryptSignMessage(
|
|
&csmp,
|
|
pccIssued->pbCertEncoded,
|
|
pccIssued->cbCertEncoded,
|
|
CERTLIB_USE_LOCALALLOC,
|
|
ppbCertChain,
|
|
pcbCertChain))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "myCryptSignMessage");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != csmp.rgpMsgCert)
|
|
{
|
|
LocalFree(csmp.rgpMsgCert);
|
|
}
|
|
if (NULL != csmp.rgpMsgCrl)
|
|
{
|
|
LocalFree(csmp.rgpMsgCrl);
|
|
}
|
|
if (NULL != pccIssued)
|
|
{
|
|
CertFreeCertificateContext(pccIssued);
|
|
}
|
|
if (NULL != pCertChainContext)
|
|
{
|
|
CertFreeCertificateChain(pCertChainContext);
|
|
}
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CCertRequest::_FindIssuedCertificate -- Find Issued cert in store.
|
|
//
|
|
// Returns S_OK on success.
|
|
//+--------------------------------------------------------------------------
|
|
|
|
HRESULT
|
|
CCertRequest::_FindIssuedCertificate(
|
|
OPTIONAL IN BYTE const *pbCertHash,
|
|
IN DWORD cbCertHash,
|
|
OUT CERT_CONTEXT const **ppccIssued)
|
|
{
|
|
HRESULT hr;
|
|
CRYPT_HASH_BLOB BlobHash;
|
|
|
|
*ppccIssued = NULL;
|
|
|
|
if (NULL == pbCertHash)
|
|
{
|
|
if (1 < m_cResponse || NULL == m_pbCert)
|
|
{
|
|
hr = CERTSRV_E_PROPERTY_EMPTY;
|
|
_JumpError(hr, error, "no cert");
|
|
}
|
|
*ppccIssued = CertCreateCertificateContext(
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
m_pbCert,
|
|
m_cbCert);
|
|
if (NULL == *ppccIssued)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertCreateCertificateContext");
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BlobHash.pbData = const_cast<BYTE *>(pbCertHash);
|
|
BlobHash.cbData = cbCertHash;
|
|
|
|
*ppccIssued = CertFindCertificateInStore(
|
|
m_hStoreResponse,
|
|
X509_ASN_ENCODING | PKCS_7_ASN_ENCODING,
|
|
0, // dwFindFlags
|
|
CERT_FIND_HASH,
|
|
&BlobHash, // pvFindPara
|
|
NULL); // pPrevCertContext
|
|
if (NULL == *ppccIssued)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CertFindCertificateInStore");
|
|
}
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
return(hr);
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CCertRequest::GetCertificate -- Get the Certificate encoding as requested
|
|
//
|
|
// The Submit or RetrievePending method must have previously returned
|
|
// CR_DISP_ISSUED, or this method will fail.
|
|
//
|
|
// Returns S_OK on success.
|
|
//+--------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CCertRequest::GetCertificate(
|
|
/* [in] */ LONG Flags,
|
|
/* [out, retval] */ BSTR __RPC_FAR *pstrCertificate)
|
|
{
|
|
HRESULT hr;
|
|
BYTE *pbChain = NULL;
|
|
DWORD cbChain;
|
|
BYTE *pbCert;
|
|
DWORD cbCert;
|
|
|
|
if (NULL == pstrCertificate)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "pstrCertificate");
|
|
}
|
|
pbCert = m_pbCert;
|
|
cbCert = m_cbCert;
|
|
if (CR_OUT_CHAIN & Flags)
|
|
{
|
|
pbCert = m_pbCertificateChain;
|
|
cbCert = m_cbCertificateChain;
|
|
if (NULL == m_pbCertificateChain)
|
|
{
|
|
hr = _BuildIssuedCertificateChain(
|
|
NULL, // pbCertHash
|
|
0, // cbCertHash
|
|
0 != (CR_OUT_CRLS & Flags),
|
|
&pbChain,
|
|
&cbChain);
|
|
_JumpIfError(hr, error, "_BuildIssuedCertificateChain");
|
|
|
|
pbCert = pbChain;
|
|
cbCert = cbChain;
|
|
}
|
|
}
|
|
|
|
CSASSERT(CR_OUT_BASE64HEADER == CRYPT_STRING_BASE64HEADER);
|
|
CSASSERT(CR_OUT_BASE64 == CRYPT_STRING_BASE64);
|
|
CSASSERT(CR_OUT_BINARY == CRYPT_STRING_BINARY);
|
|
|
|
hr = EncodeCertString(
|
|
pbCert,
|
|
cbCert,
|
|
~(CR_OUT_CHAIN | CR_OUT_CRLS) & Flags,
|
|
pstrCertificate);
|
|
_JumpIfError(hr, error, "EncodeCertString");
|
|
|
|
error:
|
|
if (NULL != pbChain)
|
|
{
|
|
LocalFree(pbChain);
|
|
}
|
|
hr = myHError(hr);
|
|
return(_SetErrorInfo(hr, L"CCertRequest::GetCertificate"));
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CCertRequest::GetCACertificate -- Get the specified CA Certificate
|
|
//
|
|
// Interrogate the Certificate Server and retrieve the base64-encoded exchange
|
|
// or signature site certificate as indicated by fExchangeCertificate.
|
|
//
|
|
// All state from previous method calls is cleared.
|
|
//
|
|
// After the GetCACertificate method completes execution, the GetLastStatus
|
|
// method may be called to retrieve a more specific error code.
|
|
//
|
|
// fExchangeCertificate is TRUE to retrieve the Certificate Server's Exchange
|
|
// certificate. fExchangeCertificate is FALSE to retrieve the Certificate
|
|
// Server's Signature site certificate.
|
|
//
|
|
// Returns S_OK on success.
|
|
//+--------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CCertRequest::GetCACertificate(
|
|
/* [in] */ LONG fExchangeCertificate,
|
|
/* [in] */ BSTR const strConfig,
|
|
/* [in] */ LONG Flags,
|
|
/* [out, retval] */ BSTR __RPC_FAR *pstrCACertificate)
|
|
{
|
|
HRESULT hr;
|
|
CERTTRANSBLOB ctbSite = { 0, NULL };
|
|
WCHAR const *pwszAuthority;
|
|
WCHAR const *pwszOut = NULL;
|
|
CAINFO const *pCAInfo;
|
|
BYTE *pbOut;
|
|
BOOL fCallServer;
|
|
DWORD Index;
|
|
WCHAR wszBuf[5 * (10 + 1)]; // enough for 5 numbers
|
|
|
|
if (NULL == pstrCACertificate)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "pstrCACertificate");
|
|
}
|
|
|
|
fCallServer = TRUE;
|
|
pbOut = NULL;
|
|
switch (fExchangeCertificate)
|
|
{
|
|
case GETCERT_ERRORTEXT1:
|
|
case GETCERT_ERRORTEXT2:
|
|
pwszOut = myGetErrorMessageText(
|
|
Flags, // error code passed in Flags parm
|
|
GETCERT_ERRORTEXT2 == fExchangeCertificate);
|
|
if (NULL == pwszOut)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
Flags = CR_OUT_BINARY;
|
|
pbOut = (BYTE *) pwszOut;
|
|
fCallServer = FALSE;
|
|
break;
|
|
}
|
|
Index = MAXDWORD;
|
|
switch (GETCERT_BYINDEXMASK & fExchangeCertificate)
|
|
{
|
|
case GETCERT_CACERTSTATEBYINDEX:
|
|
case GETCERT_CRLSTATEBYINDEX:
|
|
if (CR_OUT_CHAIN & Flags)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "Flags");
|
|
}
|
|
Index = GETCERT_INDEXVALUEMASK & fExchangeCertificate;
|
|
fExchangeCertificate &= ~GETCERT_INDEXVALUEMASK;
|
|
|
|
fCallServer =
|
|
NULL == ((GETCERT_CACERTSTATEBYINDEX == fExchangeCertificate)?
|
|
m_pbCACertState : m_pbCRLState);
|
|
break;
|
|
}
|
|
if (fCallServer)
|
|
{
|
|
hr = _OpenConnection(FALSE, strConfig, 1, &pwszAuthority);
|
|
_JumpIfError(hr, error, "_OpenConnection");
|
|
|
|
if (CR_OUT_CHAIN & Flags)
|
|
{
|
|
fExchangeCertificate |= GETCERT_CHAIN;
|
|
if (CR_OUT_CRLS & Flags)
|
|
{
|
|
fExchangeCertificate |= GETCERT_CRLS;
|
|
}
|
|
}
|
|
|
|
__try
|
|
{
|
|
hr = m_pICertRequestD->GetCACert(
|
|
fExchangeCertificate,
|
|
pwszAuthority,
|
|
&ctbSite);
|
|
}
|
|
__except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
}
|
|
_JumpIfError2(
|
|
hr,
|
|
error,
|
|
"GetCACert",
|
|
HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND));
|
|
|
|
// must register this memory
|
|
myRegisterMemAlloc(ctbSite.pb, ctbSite.cb, CSM_COTASKALLOC);
|
|
pbOut = ctbSite.pb;
|
|
}
|
|
|
|
CSASSERT(CR_OUT_BASE64HEADER == CRYPT_STRING_BASE64HEADER);
|
|
CSASSERT(CR_OUT_BASE64 == CRYPT_STRING_BASE64);
|
|
CSASSERT(CR_OUT_BINARY == CRYPT_STRING_BINARY);
|
|
|
|
switch (fExchangeCertificate)
|
|
{
|
|
// Serialize CAType into a string:
|
|
|
|
case GETCERT_CATYPE:
|
|
wsprintf(wszBuf, L"%u", *(ENUM_CATYPES const *) pbOut);
|
|
pwszOut = wszBuf;
|
|
pbOut = (BYTE *) pwszOut;
|
|
break;
|
|
|
|
// Serialize CAInfo into a string:
|
|
|
|
case GETCERT_CAINFO:
|
|
pCAInfo = (CAINFO const *) pbOut;
|
|
if (CCSIZEOF_STRUCT(CAINFO, cCASignatureCerts) > pCAInfo->cbSize)
|
|
{
|
|
hr = HRESULT_FROM_WIN32(ERROR_INVALID_DATA);
|
|
_JumpError(hr, error, "CAINFO size");
|
|
}
|
|
wsprintf(
|
|
wszBuf,
|
|
L"%u,%u",
|
|
pCAInfo->CAType,
|
|
pCAInfo->cCASignatureCerts);
|
|
pwszOut = wszBuf;
|
|
pbOut = (BYTE *) pwszOut;
|
|
break;
|
|
|
|
case GETCERT_CACERTSTATEBYINDEX:
|
|
case GETCERT_CRLSTATEBYINDEX:
|
|
{
|
|
BYTE **ppb;
|
|
DWORD *pcb;
|
|
|
|
if (GETCERT_CACERTSTATEBYINDEX == fExchangeCertificate)
|
|
{
|
|
ppb = &m_pbCACertState;
|
|
pcb = &m_cbCACertState;
|
|
}
|
|
else
|
|
{
|
|
ppb = &m_pbCRLState;
|
|
pcb = &m_cbCRLState;
|
|
}
|
|
if (fCallServer)
|
|
{
|
|
CSASSERT(NULL == *ppb);
|
|
CSASSERT(NULL != ctbSite.pb);
|
|
*pcb = ctbSite.cb;
|
|
*ppb = ctbSite.pb;
|
|
ctbSite.pb = NULL;
|
|
}
|
|
if (Index >= *pcb)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "Index");
|
|
}
|
|
wsprintf(wszBuf, L"%u", (*ppb)[Index]);
|
|
pwszOut = wszBuf;
|
|
pbOut = (BYTE *) pwszOut;
|
|
break;
|
|
}
|
|
|
|
// If retrieving a CRL in Base64, use "-----BEGIN X509 CRL..."
|
|
default:
|
|
if (GETCERT_CRLBYINDEX !=
|
|
(GETCERT_BYINDEXMASK & fExchangeCertificate))
|
|
{
|
|
break;
|
|
}
|
|
// FALLTHROUGH
|
|
|
|
case GETCERT_CURRENTCRL:
|
|
if (CR_OUT_BASE64HEADER == (~CR_OUT_CHAIN & Flags))
|
|
{
|
|
Flags = CRYPT_STRING_BASE64X509CRLHEADER;
|
|
}
|
|
break;
|
|
}
|
|
hr = EncodeCertString(
|
|
pbOut,
|
|
pbOut == (BYTE *) pwszOut?
|
|
wcslen(pwszOut) * sizeof(WCHAR) : ctbSite.cb,
|
|
~CR_OUT_CHAIN & Flags,
|
|
pstrCACertificate);
|
|
_JumpIfError(hr, error, "EncodeCertString");
|
|
|
|
error:
|
|
m_LastStatus = hr;
|
|
if (NULL != pwszOut && wszBuf != pwszOut)
|
|
{
|
|
LocalFree(const_cast<WCHAR *>(pwszOut));
|
|
}
|
|
if (NULL != ctbSite.pb)
|
|
{
|
|
CoTaskMemFree(ctbSite.pb);
|
|
}
|
|
return(_SetErrorInfo(hr, L"CCertRequest::GetCACertificate"));
|
|
}
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CCertRequest::GetErrorMessageText -- Get error message text
|
|
//
|
|
// Returns S_OK on success.
|
|
//+--------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CCertRequest::GetErrorMessageText(
|
|
/* [in] */ LONG hrMessage,
|
|
/* [in] */ LONG Flags,
|
|
/* [out, retval] */ BSTR __RPC_FAR *pstrErrorMessageText)
|
|
{
|
|
HRESULT hr;
|
|
WCHAR const *pwszError = NULL;
|
|
|
|
if (~CR_GEMT_HRESULT_STRING & Flags)
|
|
{
|
|
hr = E_INVALIDARG;
|
|
_JumpError(hr, error, "not CR_IN_BINARY");
|
|
}
|
|
|
|
pwszError = myGetErrorMessageText(
|
|
hrMessage,
|
|
0 != (CR_GEMT_HRESULT_STRING & Flags));
|
|
if (NULL == pwszError)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "LocalAlloc");
|
|
}
|
|
if (!ConvertWszToBstr(
|
|
pstrErrorMessageText,
|
|
pwszError,
|
|
-1))
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
_JumpError(hr, error, "ConvertWszToBstr");
|
|
}
|
|
hr = S_OK;
|
|
|
|
error:
|
|
if (NULL != pwszError)
|
|
{
|
|
LocalFree(const_cast<WCHAR *>(pwszError));
|
|
}
|
|
return(_SetErrorInfo(hr, L"CCertRequest::GetErrorMessageText"));
|
|
}
|
|
|
|
|
|
// for ICertRequest2::GetFullResponseProperty
|
|
|
|
CAPROP s_aFRProp[] = {
|
|
{ FR_PROP_FULLRESPONSE, PROPTYPE_BINARY, },
|
|
{ FR_PROP_FULLRESPONSENOPKCS7, PROPTYPE_BINARY, },
|
|
{ FR_PROP_STATUSINFOCOUNT, PROPTYPE_LONG, },
|
|
{ FR_PROP_BODYPARTSTRING, PROPTYPE_STRING | PROPFLAGS_INDEXED, },
|
|
{ FR_PROP_STATUS, PROPTYPE_LONG | PROPFLAGS_INDEXED, },
|
|
{ FR_PROP_STATUSSTRING, PROPTYPE_STRING | PROPFLAGS_INDEXED, },
|
|
{ FR_PROP_OTHERINFOCHOICE, PROPTYPE_LONG | PROPFLAGS_INDEXED, },
|
|
{ FR_PROP_FAILINFO, PROPTYPE_LONG | PROPFLAGS_INDEXED, },
|
|
{ FR_PROP_PENDINFOTOKEN, PROPTYPE_BINARY | PROPFLAGS_INDEXED, },
|
|
{ FR_PROP_PENDINFOTIME, PROPTYPE_DATE | PROPFLAGS_INDEXED, },
|
|
{ FR_PROP_ISSUEDCERTIFICATEHASH, PROPTYPE_BINARY | PROPFLAGS_INDEXED, },
|
|
{ FR_PROP_ISSUEDCERTIFICATE, PROPTYPE_BINARY | PROPFLAGS_INDEXED, },
|
|
{ FR_PROP_ISSUEDCERTIFICATECHAIN, PROPTYPE_BINARY | PROPFLAGS_INDEXED, },
|
|
{ FR_PROP_ISSUEDCERTIFICATECRLCHAIN, PROPTYPE_BINARY | PROPFLAGS_INDEXED, },
|
|
{ FR_PROP_ENCRYPTEDKEYHASH, PROPTYPE_BINARY | PROPFLAGS_INDEXED, },
|
|
};
|
|
|
|
|
|
//+--------------------------------------------------------------------------
|
|
// CCertRequest::GetFullResponseProperty -- Get CMC Response property
|
|
//
|
|
// Returns S_OK on success.
|
|
//+--------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP
|
|
CCertRequest::GetFullResponseProperty(
|
|
/* [in] */ LONG PropId, // FR_PROP_*
|
|
/* [in] */ LONG PropIndex,
|
|
/* [in] */ LONG PropType, // PROPTYPE_*
|
|
/* [in] */ LONG Flags, // CR_OUT_*
|
|
/* [out, retval] */ VARIANT *pvarPropertyValue)
|
|
{
|
|
HRESULT hr;
|
|
DWORD i;
|
|
BYTE const *pbOut;
|
|
WCHAR const *pwszOut;
|
|
DWORD cbOut;
|
|
DWORD dw;
|
|
XCMCRESPONSE *pResponse = NULL;
|
|
CERT_CONTEXT const *pccIssued = NULL;
|
|
BYTE *pbChain = NULL;
|
|
DWORD cbChain;
|
|
|
|
if (NULL == pvarPropertyValue)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "NULL parm");
|
|
}
|
|
VariantInit(pvarPropertyValue);
|
|
|
|
hr = E_INVALIDARG;
|
|
for (i = 0; PropId != s_aFRProp[i].lPropId; i++)
|
|
{
|
|
if (i >= ARRAYSIZE(s_aFRProp))
|
|
{
|
|
_JumpError(hr, error, "PropId");
|
|
}
|
|
}
|
|
if ((PROPTYPE_MASK & s_aFRProp[i].lPropFlags) != PropType)
|
|
{
|
|
_JumpError(hr, error, "PropType");
|
|
}
|
|
if (PROPFLAGS_INDEXED & s_aFRProp[i].lPropFlags)
|
|
{
|
|
if ((DWORD) PropIndex >= m_cResponse)
|
|
{
|
|
_JumpError(hr, error, "PropIndex");
|
|
}
|
|
pResponse = &m_rgResponse[PropIndex];
|
|
}
|
|
else if (0 != PropIndex)
|
|
{
|
|
_JumpError(hr, error, "non-zero PropIndex");
|
|
}
|
|
|
|
|
|
pbOut = NULL;
|
|
cbOut = 0;
|
|
pwszOut = NULL;
|
|
switch (PropId)
|
|
{
|
|
case FR_PROP_FULLRESPONSE:
|
|
case FR_PROP_FULLRESPONSENOPKCS7:
|
|
pbOut = m_pbFullResponse;
|
|
cbOut = m_cbFullResponse;
|
|
if (NULL == pbOut && FR_PROP_FULLRESPONSE == PropId)
|
|
{
|
|
pbOut = m_pbCertificateChain;
|
|
cbOut = m_cbCertificateChain;
|
|
}
|
|
break;
|
|
|
|
case FR_PROP_STATUSINFOCOUNT:
|
|
pbOut = (BYTE const *) &m_cResponse;
|
|
cbOut = sizeof(m_cResponse);
|
|
break;
|
|
|
|
case FR_PROP_BODYPARTSTRING:
|
|
if (pResponse == NULL)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "Bad switch setup: NULL pResponse");
|
|
}
|
|
pwszOut = pResponse->pwszBodyPart;
|
|
break;
|
|
|
|
case FR_PROP_STATUS:
|
|
if (pResponse == NULL)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "Bad switch setup: NULL pResponse");
|
|
}
|
|
pbOut = (BYTE const *) &pResponse->StatusInfo.dwStatus;
|
|
cbOut = sizeof(pResponse->StatusInfo.dwStatus);
|
|
break;
|
|
|
|
case FR_PROP_STATUSSTRING:
|
|
if (pResponse == NULL)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "Bad switch setup: NULL pResponse");
|
|
}
|
|
pwszOut = pResponse->StatusInfo.pwszStatusString;
|
|
break;
|
|
|
|
case FR_PROP_OTHERINFOCHOICE:
|
|
if (pResponse == NULL)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "Bad switch setup: NULL pResponse");
|
|
}
|
|
pbOut = (BYTE const *) &pResponse->StatusInfo.dwOtherInfoChoice;
|
|
cbOut = sizeof(pResponse->StatusInfo.dwOtherInfoChoice);
|
|
break;
|
|
|
|
case FR_PROP_FAILINFO:
|
|
if (pResponse == NULL)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "Bad switch setup: NULL pResponse");
|
|
}
|
|
if (CMC_OTHER_INFO_FAIL_CHOICE ==
|
|
pResponse->StatusInfo.dwOtherInfoChoice)
|
|
{
|
|
pbOut = (BYTE const *) &pResponse->StatusInfo.dwFailInfo;
|
|
cbOut = sizeof(pResponse->StatusInfo.dwFailInfo);
|
|
}
|
|
break;
|
|
|
|
case FR_PROP_PENDINFOTOKEN:
|
|
if (pResponse == NULL)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "Bad switch setup: NULL pResponse");
|
|
}
|
|
if (CMC_OTHER_INFO_PEND_CHOICE ==
|
|
pResponse->StatusInfo.dwOtherInfoChoice)
|
|
{
|
|
pbOut = (BYTE const *) &dw;
|
|
cbOut = sizeof(dw);
|
|
pbOut = pResponse->StatusInfo.pPendInfo->PendToken.pbData;
|
|
cbOut = pResponse->StatusInfo.pPendInfo->PendToken.cbData;
|
|
}
|
|
break;
|
|
|
|
case FR_PROP_PENDINFOTIME:
|
|
if (pResponse == NULL)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "Bad switch setup: NULL pResponse");
|
|
}
|
|
if (CMC_OTHER_INFO_PEND_CHOICE ==
|
|
pResponse->StatusInfo.dwOtherInfoChoice)
|
|
{
|
|
pbOut = (BYTE const *) &pResponse->StatusInfo.pPendInfo->PendTime;
|
|
cbOut = sizeof(pResponse->StatusInfo.pPendInfo->PendTime);
|
|
}
|
|
break;
|
|
|
|
case FR_PROP_ISSUEDCERTIFICATEHASH:
|
|
if (pResponse == NULL)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "Bad switch setup: NULL pResponse");
|
|
}
|
|
pbOut = pResponse->pbCertHash;
|
|
cbOut = pResponse->cbCertHash;
|
|
break;
|
|
|
|
case FR_PROP_ENCRYPTEDKEYHASH:
|
|
if (pResponse == NULL)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "Bad switch setup: NULL pResponse");
|
|
}
|
|
pbOut = pResponse->pbEncryptedKeyHash;
|
|
cbOut = pResponse->cbEncryptedKeyHash;
|
|
break;
|
|
|
|
case FR_PROP_ISSUEDCERTIFICATE:
|
|
if (pResponse == NULL)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "Bad switch setup: NULL pResponse");
|
|
}
|
|
hr = _FindIssuedCertificate(
|
|
pResponse->pbCertHash,
|
|
pResponse->cbCertHash,
|
|
&pccIssued);
|
|
_JumpIfError(hr, error, "_FindIssuedCertificate");
|
|
|
|
pbOut = pccIssued->pbCertEncoded;
|
|
cbOut = pccIssued->cbCertEncoded;
|
|
break;
|
|
|
|
case FR_PROP_ISSUEDCERTIFICATECHAIN:
|
|
case FR_PROP_ISSUEDCERTIFICATECRLCHAIN:
|
|
if (pResponse == NULL)
|
|
{
|
|
hr = E_POINTER;
|
|
_JumpError(hr, error, "Bad switch setup: NULL pResponse");
|
|
}
|
|
hr = _BuildIssuedCertificateChain(
|
|
pResponse->pbCertHash,
|
|
pResponse->cbCertHash,
|
|
FR_PROP_ISSUEDCERTIFICATECRLCHAIN == PropId ||
|
|
0 != (CR_OUT_CRLS & Flags),
|
|
&pbChain,
|
|
&cbChain);
|
|
_JumpIfError(hr, error, "_BuildIssuedCertificateChain");
|
|
|
|
pbOut = pbChain;
|
|
cbOut = cbChain;
|
|
break;
|
|
}
|
|
if (NULL != pwszOut)
|
|
{
|
|
pbOut = (BYTE const *) pwszOut;
|
|
cbOut = (wcslen(pwszOut) + 1) * sizeof(WCHAR);
|
|
}
|
|
if (NULL == pbOut || 0 == cbOut)
|
|
{
|
|
hr = CERTSRV_E_PROPERTY_EMPTY;
|
|
_JumpError2(hr, error, "Empty", CERTSRV_E_PROPERTY_EMPTY);
|
|
}
|
|
__try
|
|
{
|
|
hr = myUnmarshalFormattedVariant(
|
|
Flags,
|
|
CR_PROP_CASIGCERT,
|
|
PropType,
|
|
cbOut,
|
|
pbOut,
|
|
pvarPropertyValue);
|
|
}
|
|
__except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
|
|
{
|
|
}
|
|
_JumpIfError(hr, error, "myUnmarshalFormattedVariant");
|
|
|
|
error:
|
|
if (NULL != pccIssued)
|
|
{
|
|
CertFreeCertificateContext(pccIssued);
|
|
}
|
|
if (S_OK != hr && NULL != pvarPropertyValue)
|
|
{
|
|
VariantClear(pvarPropertyValue);
|
|
}
|
|
if (NULL != pbChain)
|
|
{
|
|
LocalFree(pbChain);
|
|
}
|
|
return(_SetErrorInfo(hr, L"CCertRequest::GetFullResponseProperty"));
|
|
}
|
|
|
|
|
|
HRESULT
|
|
CCertRequest::_SetErrorInfo(
|
|
IN HRESULT hrError,
|
|
IN WCHAR const *pwszDescription)
|
|
{
|
|
CSASSERT(FAILED(hrError) || S_OK == hrError || S_FALSE == hrError);
|
|
if (FAILED(hrError))
|
|
{
|
|
HRESULT hr;
|
|
|
|
hr = DispatchSetErrorInfo(
|
|
hrError,
|
|
pwszDescription,
|
|
wszCLASS_CERTREQUEST,
|
|
&IID_ICertRequest);
|
|
CSASSERT(hr == hrError);
|
|
}
|
|
return(hrError);
|
|
}
|
|
|
|
|
|
VOID
|
|
crRPCTimeoutCallback(
|
|
IN OUT VOID *pVoid,
|
|
IN BOOLEAN fTimeout)
|
|
{
|
|
|
|
PRPC_TIMEOUT_CONTEXT pTimeout = (RPC_TIMEOUT_CONTEXT *) pVoid;
|
|
|
|
if(fTimeout)
|
|
{
|
|
RpcCancelThreadEx(pTimeout->hThread, CR_RPC_CANCEL_TIMEOUT);
|
|
pTimeout->hrRpcError = RPC_E_TIMEOUT;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
HRESULT
|
|
crRegisterRPCCallTimeout(
|
|
IN DWORD dwMilliseconds,
|
|
OUT PRPC_TIMEOUT_CONTEXT pTimeout)
|
|
{
|
|
HRESULT hr = S_OK;
|
|
|
|
pTimeout->hrRpcError = RPC_S_CALL_CANCELLED;
|
|
|
|
if (!DuplicateHandle(
|
|
GetCurrentProcess(), // hSourceProcessHandle
|
|
GetCurrentThread(), // hSourceHandle
|
|
GetCurrentProcess(), // hTargetProcessHandle
|
|
&pTimeout->hThread, // lpTargetHandle
|
|
0, // dwDesiredAccess
|
|
FALSE, // bInheritHandle
|
|
DUPLICATE_SAME_ACCESS)) // dwOptions
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "DuplicateHandle");
|
|
}
|
|
|
|
pTimeout->hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
|
if(pTimeout->hEvent == NULL)
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "CreateEvent");
|
|
}
|
|
|
|
|
|
if (!RegisterWaitForSingleObject(&pTimeout->hWait,
|
|
pTimeout->hEvent,
|
|
crRPCTimeoutCallback,
|
|
(PVOID)pTimeout ,
|
|
dwMilliseconds,
|
|
WT_EXECUTEONLYONCE))
|
|
{
|
|
hr = myHLastError();
|
|
_JumpError(hr, error, "RegisterWaitForSingleObject");
|
|
}
|
|
|
|
error:
|
|
if (S_OK != hr)
|
|
{
|
|
crCloseRPCCallTimeout(pTimeout);
|
|
}
|
|
return hr;
|
|
|
|
}
|
|
|
|
|
|
HRESULT
|
|
crCloseRPCCallTimeout(
|
|
IN PRPC_TIMEOUT_CONTEXT pTimeout)
|
|
{
|
|
if(pTimeout->hWait)
|
|
{
|
|
UnregisterWait(pTimeout->hWait);
|
|
pTimeout->hWait = NULL;
|
|
}
|
|
|
|
if(pTimeout->hEvent)
|
|
{
|
|
CloseHandle(pTimeout->hEvent);
|
|
pTimeout->hEvent = NULL;
|
|
}
|
|
|
|
if(pTimeout->hThread)
|
|
{
|
|
CloseHandle(pTimeout->hThread);
|
|
pTimeout->hThread = NULL;
|
|
}
|
|
|
|
return S_OK;
|
|
}
|
|
|
|
|
|
#undef __DIR__
|
|
#undef __dwFILE__
|
|
#define CCERTREQUEST
|
|
#include "csprop2.cpp"
|