Source code of Windows XP (NT5)
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.
 
 
 
 
 
 

2347 lines
58 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_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");
__try
{
hr = CertServerRequest(
hRPCCertServer,
Flags,
pwszAuthority,
pRequestId,
pDisposition,
pctbAttrib,
pctbRequest,
pctbCertChain,
pctbCert,
pctbDispositionMessage);
}
__except(
HRESULT_FROM_WIN32(RPC_S_CALL_CANCELLED) == myHEXCEPTIONCODE()?
EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH)
{
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
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_pCAPropInfo)
{
MIDL_user_free(m_pCAPropInfo);
m_pCAPropInfo = 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 != lstrcmpi(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;
WCHAR *pwszConfig = strConfig;
WCHAR *pwszSerialNumber = NULL;
if (NULL == pDisposition || NULL == strConfig)
{
hr = E_POINTER;
_JumpError(hr, error, "NULL param");
}
if (0 == RequestId)
{
DWORD cwc;
pwszSerialNumber = wcschr(pwszConfig, 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, pwszConfig);
pwszSerialNumber++;
pwszConfig = (WCHAR *) LocalAlloc(
LMEM_FIXED,
(cwc + 1) * sizeof(WCHAR));
if (NULL == pwszConfig)
{
hr = E_OUTOFMEMORY;
_JumpError(hr, error, "LocalAlloc");
}
CopyMemory(pwszConfig, strConfig, cwc * sizeof(WCHAR));
pwszConfig[cwc] = L'\0';
}
hr = _RequestCertificate(
0, // Flags
RequestId,
NULL, // strRequest
pwszSerialNumber, // strAttributes
NULL, // pwszSerialNumber
pwszConfig,
1, // RequiredVersion
pDisposition);
_JumpIfError(hr, error, "_RequestCertificate");
error:
if (NULL != pwszConfig && strConfig != pwszConfig)
{
LocalFree(pwszConfig);
}
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;
CRL_CONTEXT const **ppCRL;
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_END_CERT |
CERT_CHAIN_REVOCATION_CHECK_CHAIN) :
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);
}
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;
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;
}
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;
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"));
}
#define CCERTREQUEST
#include "csprop2.cpp"
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;
}