Extract Certificate Chain from SChannel with C++ and CryptoApi/SChannel - c++

Is it possible to extract the certificate chain sent by the remote computer in TLS 1.0 Handshake?
The API QueryContextAttributes with SECPKG_ATTR_REMOTE_CERT_CONTEXT value, returns only the end certificate.
Is possible to extract all the chain certificates using some methods?
Environment Windows and C++ using CryptoApi and SChannel.
Thanks!

Yes, it is.
Use QueryContextAttributes() with SECPKG_ATTR_REMOTE_CERT_CONTEXT and server certificate returned will have hCertStore member set to a certificate store containing all server's intermediate CA certificates. (See remarks at MSDN.)
See the code snippet below (Source: WebClient.c, Microsoft Platform SDK) how you can parse the chain:
static
void
DisplayCertChain(
PCCERT_CONTEXT pServerCert,
BOOL fLocal)
{
CHAR szName[1000];
PCCERT_CONTEXT pCurrentCert;
PCCERT_CONTEXT pIssuerCert;
DWORD dwVerificationFlags;
printf("\n");
// display leaf name
if(!CertNameToStr(pServerCert->dwCertEncodingType,
&pServerCert->pCertInfo->Subject,
CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
szName, sizeof(szName)))
{
printf("**** Error 0x%x building subject name\n", GetLastError());
}
if(fLocal)
{
printf("Client subject: %s\n", szName);
}
else
{
printf("Server subject: %s\n", szName);
}
if(!CertNameToStr(pServerCert->dwCertEncodingType,
&pServerCert->pCertInfo->Issuer,
CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
szName, sizeof(szName)))
{
printf("**** Error 0x%x building issuer name\n", GetLastError());
}
if(fLocal)
{
printf("Client issuer: %s\n", szName);
}
else
{
printf("Server issuer: %s\n\n", szName);
}
// display certificate chain
pCurrentCert = pServerCert;
while(pCurrentCert != NULL)
{
dwVerificationFlags = 0;
pIssuerCert = CertGetIssuerCertificateFromStore(pServerCert->hCertStore,
pCurrentCert,
NULL,
&dwVerificationFlags);
if(pIssuerCert == NULL)
{
if(pCurrentCert != pServerCert)
{
CertFreeCertificateContext(pCurrentCert);
}
break;
}
if(!CertNameToStr(pIssuerCert->dwCertEncodingType,
&pIssuerCert->pCertInfo->Subject,
CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
szName, sizeof(szName)))
{
printf("**** Error 0x%x building subject name\n", GetLastError());
}
printf("CA subject: %s\n", szName);
if(!CertNameToStr(pIssuerCert->dwCertEncodingType,
&pIssuerCert->pCertInfo->Issuer,
CERT_X500_NAME_STR | CERT_NAME_STR_NO_PLUS_FLAG,
szName, sizeof(szName)))
{
printf("**** Error 0x%x building issuer name\n", GetLastError());
}
printf("CA issuer: %s\n\n", szName);
if(pCurrentCert != pServerCert)
{
CertFreeCertificateContext(pCurrentCert);
}
pCurrentCert = pIssuerCert;
pIssuerCert = NULL;
}
}

Related

How is an integrity level SID constructed?

I feel like I'm missing something obvious but I've been stuck on this for hours. I am attempting to call SetTokenInformation on a duplicate primary token of the current process, but it fails with 0x5 (Access denied).
I'm trying to create a child process with no privileges. Then I want to adjust the integrity level of the token to low.
Flow of execution in the minimal example:
We enable every single privilege. All of them.
We open the current process with PROCESS_ALL_ACCESS, and open the process token with TOKEN_ALL_ACCESS
We get a duplicate token for our current process.
We attempt to change the mandatory integrity level of the token, and we get Access denied.
Please note this is for a minimal example, I know the above would be very bad practice in production code. We are simply eliminating the possibility of permission issues.
Privileges:
Output:
What could I be missing here? I have tried everything I can think of:
Debugged it to verify the method being called is correct and the arguments on the stack are all valid
Verified arguments and return values for every function in the program
Tried every process and token access/duplication flag
Minimum 2 hours of Googling for random info with "StackOverflow ...
token" (this one is a joke, but I really did)
etc.
So I figure it has to be a problem with the SID itself. But I have been able to find nothing on the internet about how this SID is constructed.
MinimalMain.cpp:
// TokenMinimalExample.cpp : This file contains the 'main' function. Program execution begins and ends there.
#include <windows.h>
#include <stdio.h>
#include "token_example.h"
int main()
{
// Enable every privilege under the sun.
if (!EnableEveryPrivilegeUnderTheSun())
{
printf("Failed to enable every privilege under the sun: %d\n", GetLastError());
return 1;
}
// Get a duplicate token for the current process
HANDLE hToken = GetCurrentProcessPrimaryToken();
if (hToken == INVALID_HANDLE_VALUE)
{
printf("Failed to get a duplicate token for the current process: %d\n", GetLastError());
return 1;
}
// Create a mandatory low integrity SID
SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_MANDATORY_LABEL_AUTHORITY;
PSID pSid = NULL;
// Can be low, med, high, system, they're all 0x5
if(!AllocateAndInitializeSid(&SIDAuth, 1, SECURITY_MANDATORY_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &pSid))
{
printf("Failed to create a mandatory low integrity SID: %d\n", GetLastError());
system("PAUSE");
return 1;
}
// Change the integrity level of the duplicate token
TOKEN_MANDATORY_LABEL tml;
tml.Label.Attributes = SE_GROUP_INTEGRITY;
tml.Label.Sid = pSid;
if (!SetTokenInformation(hToken, TokenIntegrityLevel, &tml, sizeof(tml)))
{
// Access denied ??????
printf("Failed to set token information: %d\n", ::GetLastError());
system("PAUSE");
return 1;
}
system("PAUSE");
return 0;
}
token_example.h:
#pragma once
#include <Windows.h>
#include <stdio.h>
// Helper function to adjust our processes current privileges
bool SetThreadProcessPrivilege(LPCWSTR PrivilegeName, bool Enable)
{
HANDLE Token;
TOKEN_PRIVILEGES TokenPrivs;
LUID TempLuid;
bool Result;
if (!LookupPrivilegeValueW(NULL, PrivilegeName, &TempLuid)) return false;
if (!OpenThreadToken(GetCurrentThread(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, FALSE, &Token))
{
if (!OpenProcessToken(GetCurrentProcess(), TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY, &Token)) return false;
}
TokenPrivs.PrivilegeCount = 1;
TokenPrivs.Privileges[0].Luid = TempLuid;
TokenPrivs.Privileges[0].Attributes = (Enable ? SE_PRIVILEGE_ENABLED : 0);
Result = (AdjustTokenPrivileges(Token, FALSE, &TokenPrivs, 0, NULL, NULL) && ::GetLastError() == ERROR_SUCCESS);
// Even if AdjustTokenPrivileges returns TRUE, it may not have succeeded
// check last error top confirm
if (GetLastError() == ERROR_NOT_ALL_ASSIGNED)
{
printf(" Unable to set privilege: %S Error: %d \n", PrivilegeName, GetLastError());
CloseHandle(Token);
return FALSE;
}
CloseHandle(Token);
return Result;
}
// Test method, trying to figure out if I was missing any privileges
bool EnableEveryPrivilegeUnderTheSun()
{
if (!SetThreadProcessPrivilege(L"SeBackupPrivilege", true))
{
printf("Failed to enable SeBackupPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeRestorePrivilege", true))
{
printf("Failed to enable SeRestorePrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeIncreaseQuotaPrivilege", true))
{
printf("Failed to enable SeIncreaseQuotaPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeAssignPrimaryTokenPrivilege", true))
{
printf("Failed to enable SeAssignPrimaryTokenPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeTcbPrivilege", true))
{
printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeDebugPrivilege", true))
{
printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeSecurityPrivilege", true))
{
printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeSystemtimePrivilege", true))
{
printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeShutdownPrivilege", true))
{
printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeSystemEnvironmentPrivilege", true))
{
printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeTakeOwnershipPrivilege", true))
{
printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeLoadDriverPrivilege", true))
{
printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());
return false;
}
if (!SetThreadProcessPrivilege(L"SeManageVolumePrivilege", true))
{
printf("Failed to enable SeTcbPrivilege: %d\n", GetLastError());
return false;
}
return true;
}
// Helper function to get a duplicate primary token for the current process
HANDLE GetCurrentProcessPrimaryToken()
{
HANDLE tempproc;
HANDLE realtoken, duplicatetoken;
DWORD accessmode = TOKEN_ALL_ACCESS;
// Enable SeDebugPrivilege.
SetThreadProcessPrivilege(L"SeDebugPrivilege", true);
// Open a handle to the process.
tempproc = OpenProcess(PROCESS_ALL_ACCESS, FALSE, GetCurrentProcessId());
if (tempproc == NULL)
{
printf("OpenProcess failed with error %d\n", GetLastError());
return INVALID_HANDLE_VALUE;
}
if (!OpenProcessToken(tempproc, accessmode, &realtoken))
{
printf("OpenProcessToken failed with error %d\n", GetLastError());
CloseHandle(tempproc);
return INVALID_HANDLE_VALUE;
}
CloseHandle(tempproc);
SECURITY_ATTRIBUTES secattr = { 0 };
secattr.nLength = sizeof(secattr);
secattr.bInheritHandle = FALSE;
secattr.lpSecurityDescriptor = NULL;
if (!DuplicateTokenEx(realtoken, MAXIMUM_ALLOWED, &secattr, SecurityImpersonation, TokenPrimary, &duplicatetoken))
{
printf("DuplicateTokenEx failed with error %d\n", GetLastError());
CloseHandle(realtoken);
return INVALID_HANDLE_VALUE;
}
CloseHandle(realtoken);
return duplicatetoken;
}
Not sure why you cannot assign the system IL if you are running as System but the RID you are really looking for is SECURITY_MANDATORY_LOW_RID:
HANDLE hToken, hPrimTok = 0;
if (OpenProcessToken(GetCurrentProcess(), TOKEN_DUPLICATE, &hToken))
{
if (!DuplicateTokenEx(hToken, MAXIMUM_ALLOWED, 0, SecurityAnonymous, TokenPrimary, &hPrimTok)) hPrimTok = 0;
CloseHandle(hToken);
}
if (hPrimTok)
{
SID_IDENTIFIER_AUTHORITY SIDAuth = SECURITY_MANDATORY_LABEL_AUTHORITY;
TOKEN_MANDATORY_LABEL tml;
tml.Label.Attributes = SE_GROUP_INTEGRITY;
if (AllocateAndInitializeSid(&SIDAuth, 1, SECURITY_MANDATORY_LOW_RID, 0, 0, 0, 0, 0, 0, 0, &tml.Label.Sid))
{
if (!SetTokenInformation(hPrimTok, TokenIntegrityLevel, &tml, sizeof(tml)))
printf("SetTokenInformation failed, %u\n", GetLastError());
else
{
STARTUPINFOA si = { sizeof (STARTUPINFO) };
PROCESS_INFORMATION pi;
if (CreateProcessAsUserA(hPrimTok, 0, "Notepad.exe", 0, 0, FALSE, 0, 0, 0, &si, &pi))
{
// CloseHandle *2
}
}
FreeSid(tml.Label.Sid);
}
CloseHandle(hPrimTok);
}
Please note that you should also call CreateRestrictedToken to really restrict the new process. Disable all groups and privileges you don't need.

How to check the revocation status of a x509 certificate in c++ with wincrypt.h?

I'm trying to check the revocation state of a X509 certificate in a c++ program with the use of wincrypt.h. Sadly I'm not able to find a comprehensive example. In c# the code would be the following:
X509Certificate2 certificate = new X509Certificate2();
//Create X509Certificate2 object from .cer file.
byte[] rawData = ReadFile(#"C:\Users\z002m76a\Desktop\cert.pem");
certificate.Import(rawData);
X509Chain ch = new X509Chain();
ch.ChainPolicy.RevocationMode = X509RevocationMode.Online;
ch.Build(certificate);
Console.WriteLine("Chain revocation flag: {0}", ch.ChainPolicy.RevocationFlag);
Based on the excellent comment by #crypt32 I created the following c++ program. Probably it is not the most beautiful c++ code in the world (mine c++ is pretty rusty) but it seems so work
// ValidationCheckCPP.cpp : This file contains the 'main' function. Program execution begins and ends there.
//
#include <iostream>
#include <windows.h>
#include <wincrypt.h>
#include "ValidationCheckCPP.h"
int main()
{
char keyFile[] = "C:\\Users\\z002m76a\\Desktop\\2698514447.crt";
SECURITY_ATTRIBUTES sa;
sa.nLength = sizeof(sa);
sa.lpSecurityDescriptor = NULL;
sa.bInheritHandle = FALSE;
HANDLE hKeyFile;
hKeyFile = CreateFile(keyFile, GENERIC_READ, FILE_SHARE_READ, &sa, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
if (hKeyFile) {
BYTE lp[65536];
DWORD flags;
DWORD bytes;
if (ReadFile(hKeyFile, lp, GetFileSize(hKeyFile, NULL), &bytes, NULL) && bytes > 0) {
BYTE* p;
p = lp + bytes;
if (CryptStringToBinary((char*)lp, p - lp, CRYPT_STRING_BASE64_ANY, p, &bytes, NULL, &flags) && bytes > 0) {
PCCERT_CONTEXT pCertContext;
pCertContext = CertCreateCertificateContext(X509_ASN_ENCODING, p, bytes);
if (pCertContext)
{
printf("Certificate loaded");
CERT_ENHKEY_USAGE EnhkeyUsage;
EnhkeyUsage.cUsageIdentifier = 0;
EnhkeyUsage.rgpszUsageIdentifier = NULL;
CERT_USAGE_MATCH CertUsage;
CertUsage.dwType = USAGE_MATCH_TYPE_AND;
CertUsage.Usage = EnhkeyUsage;
CERT_CHAIN_PARA ChainPara;
ChainPara.cbSize = sizeof(CERT_CHAIN_PARA);
ChainPara.RequestedUsage = CertUsage;
PCCERT_CHAIN_CONTEXT pChainContext;
if (CertGetCertificateChain(
NULL, // use the default chain engine
pCertContext, // pointer to the end certificate
NULL, // use the default time
NULL, // search no additional stores
&ChainPara,
CERT_CHAIN_REVOCATION_CHECK_CHAIN_EXCLUDE_ROOT,
NULL, // currently reserved
&pChainContext)) // return a pointer to the chain created
{
printf("Chain built with %d certificates.\n", pChainContext->rgpChain[0]->cElement);
CERT_CHAIN_POLICY_PARA ChainPolicy = { 0 };
ChainPolicy.cbSize = sizeof(ChainPolicy);
CERT_CHAIN_POLICY_STATUS PolicyStatus = { 0 };
PolicyStatus.cbSize = sizeof(PolicyStatus);
CertVerifyCertificateChainPolicy(
CERT_CHAIN_POLICY_BASE,
pChainContext, // pointer to the chain
&ChainPolicy,
&PolicyStatus);
CERT_REVOCATION_STATUS revocationStatus;
revocationStatus.cbSize = sizeof(CERT_REVOCATION_STATUS);
PCERT_CONTEXT* pCerts = new PCERT_CONTEXT[pChainContext->cChain];
for (DWORD i = 0; i < pChainContext->cChain; i++) {
pCerts[i] = (PCERT_CONTEXT)(pChainContext->rgpChain[i]->rgpElement[0]->pCertContext);
}
// CERT_VERIFY_REV_CHAIN_FLAG
// Verification of the chain of certificates is done assuming each certificate except the first certificate is the issuer of the certificate that precedes it.If dwRevType is not CERT_CONTEXT_REVOCATION_TYPE, no assumptions are made about the order of the contexts.
// CERT_VERIFY_CACHE_ONLY_BASED_REVOCATION
// Prevents the revocation handler from accessing any network - based resources for revocation checking.
// CERT_VERIFY_REV_ACCUMULATIVE_TIMEOUT_FLAG
// When set, dwUrlRetrievalTimeout is the cumulative time - out across all URL wire retrievals.
// CERT_VERIFY_REV_SERVER_OCSP_FLAG
// When set, this function only uses online certificate status protocol(OCSP) for revocation checking.If the certificate does not have any OCSP AIA URLs, the dwError member of the pRevStatus parameter is set to CRYPT_E_NOT_IN_REVOCATION_DATABASE.
DWORD revocationCheckType = CERT_VERIFY_REV_CHAIN_FLAG;
BOOL bRc = CertVerifyRevocation(
X509_ASN_ENCODING,
CERT_CONTEXT_REVOCATION_TYPE,
pChainContext->cChain,
(void**)pCerts,
revocationCheckType,
NULL,
&revocationStatus);
printf("The size of the chain context is %d. \n", pChainContext->cbSize);
printf("%d simple chains found.\n", pChainContext->cChain);
printf("\nError status for the chain:\n");
switch (pChainContext->TrustStatus.dwErrorStatus)
{
case CERT_TRUST_NO_ERROR:
printf("No error found for this certificate or chain.\n");
break;
case CERT_TRUST_IS_NOT_TIME_VALID:
printf("This certificate or one of the certificates in the certificate chain is not time-valid.\n");
break;
case CERT_TRUST_IS_REVOKED:
printf("Trust for this certificate or one of the certificates in the certificate chain has been revoked.\n");
break;
case CERT_TRUST_IS_NOT_SIGNATURE_VALID:
printf("The certificate or one of the certificates in the certificate chain does not have a valid signature.\n");
break;
case CERT_TRUST_IS_NOT_VALID_FOR_USAGE:
printf("The certificate or certificate chain is not valid in its proposed usage.\n");
break;
case CERT_TRUST_IS_UNTRUSTED_ROOT:
printf("The certificate or certificate chain is based on an untrusted root.\n");
break;
case CERT_TRUST_REVOCATION_STATUS_UNKNOWN:
printf("The revocation status of the certificate or one of the certificates in the certificate chain is unknown.\n");
break;
case CERT_TRUST_IS_CYCLIC:
printf("One of the certificates in the chain was issued by a certification authority that the original certificate had certified.\n");
break;
case CERT_TRUST_IS_PARTIAL_CHAIN:
printf("The certificate chain is not complete.\n");
break;
case CERT_TRUST_CTL_IS_NOT_TIME_VALID:
printf("A CTL used to create this chain was not time-valid.\n");
break;
case CERT_TRUST_CTL_IS_NOT_SIGNATURE_VALID:
printf("A CTL used to create this chain did not have a valid signature.\n");
break;
case CERT_TRUST_CTL_IS_NOT_VALID_FOR_USAGE:
printf("A CTL used to create this chain is not valid for this usage.\n");
}
printf("Info status for the chain:\n");
switch (pChainContext->TrustStatus.dwInfoStatus)
{
case 0:
printf("No information status reported.\n");
break;
case CERT_TRUST_HAS_EXACT_MATCH_ISSUER:
printf("An exact match issuer certificate has been found for this certificate.\n");
break;
case CERT_TRUST_HAS_KEY_MATCH_ISSUER:
printf("A key match issuer certificate has been found for this certificate.\n");
break;
case CERT_TRUST_HAS_NAME_MATCH_ISSUER:
printf("A name match issuer certificate has been found for this certificate.\n");
break;
case CERT_TRUST_IS_SELF_SIGNED:
printf("This certificate is self-signed.\n");
break;
case CERT_TRUST_IS_COMPLEX_CHAIN:
printf("The certificate chain created is a complex chain.\n");
break;
} // end switch
}
CertFreeCertificateContext(pCertContext);
}
else {
printf("Could not convert certificate to internal form\n");
}
}
else {
printf("Failed to convert from PEM\n");
}
}
else {
printf("Failed to read key file: %s\n", keyFile);
}
}
else {
printf("Failed to open key file: %s\n", keyFile);
}
CloseHandle(hKeyFile);
return 0;
}
In C++, you use CertGetCertificateChain function. Along with call result, the function returns a a pointer to CERT_CHAIN_CONTEXT (via ppChainContext member). This CERT_CHAIN_CONTEXT structure stores a pointer to a CERT_SIMPLE_CHAIN structure (rgpChain member) which represents an array of chain elements (this is what X509Chain.ChainElements represents in .NET). Essentially, you need to check the very first element in rgpChain, which is a pointer to a CERT_SIMPLE_CHAIN structure. And examine TrustStatus member for revocation status flags (two flags).
The example provided by #Rufus Buschart is quite lovely. If checking OCSP in his example, simply change revocationCheckType to CERT_VERIFY_REV_SERVER_OCSP_FLAG.
Here I'd like to provide an example using another api CertOpenServerOcspResponse()
// after construct the PCCERT_CHAIN_CONTEXT ppChainContext, which can be the same as #Rufus
// If async operation is required, change second parameter 0 to 1 (available since Win8.1, but not announced on official documentations)
HCERT_SERVER_OCSP_RESPONSE hServerOcspResponse = CertOpenServerOcspResponse(ppChainContext, 0, nullptr);
PCCERT_SERVER_OCSP_RESPONSE_CONTEXT recvdResponseCtx = CertGetServerOcspResponseContext(hServerOcspResponse, 0, nullptr);
if (recvdResponseCtx == nullptr)
{
printf("Receive OCSP server response failed\n");
showStrError("CertGetServerOcspResponseContext"); // show the description about GetLastError()
return 1;
}
POCSP_RESPONSE_INFO pocspResponseInfo = nullptr;
DWORD ocspResponseSize = 0; // X509_SEQUENCE_OF_ANY
ret = CryptDecodeObjectEx(PKCS_7_ASN_ENCODING, OCSP_RESPONSE, recvdResponseCtx->pbEncodedOcspResponse, recvdResponseCtx->cbEncodedOcspResponse, CRYPT_DECODE_ALLOC_FLAG, nullptr, &pocspResponseInfo, &ocspResponseSize);
if (!ret)
{
printf("Error decode ocsp response info from CERT_SERVER_OCSP_RESPONSE_CONTEXT.\n");
showStrError("DecodeOcspServerResponse");
return 1;
}
if (pocspResponseInfo->dwStatus != OCSP_SUCCESSFUL_RESPONSE)
{
printf("Unsuccessfully ocsp response. The status value is %d, which means %s\n", pocspResponseInfo->dwStatus, "OCSP_MALFORMED_REQUEST_RESPONSE if it is 1");
return 1;
}
if (strcmp(szOID_PKIX_OCSP_BASIC_SIGNED_RESPONSE, pocspResponseInfo->pszObjId))
{
printf("Ocsp response info is %s\n, but it should be %s\n", pocspResponseInfo->pszObjId, szOID_PKIX_OCSP_BASIC_SIGNED_RESPONSE);
return 1;
}
POCSP_BASIC_SIGNED_RESPONSE_INFO pocspBasicSignedResponseInfo = nullptr;
DWORD ocspBasicSignedResponseSize = 0;
ret = CryptDecodeObjectEx(PKCS_7_ASN_ENCODING, OCSP_BASIC_SIGNED_RESPONSE, pocspResponseInfo->Value.pbData, pocspResponseInfo->Value.cbData, CRYPT_DECODE_ALLOC_FLAG, nullptr, &pocspBasicSignedResponseInfo, &ocspBasicSignedResponseSize);
if (!ret)
{
showStrError("DecodeOCSP_BASIC_SIGNED_RESPONSE");
return 1;
}
POCSP_BASIC_RESPONSE_INFO pocspBasicResponseInfo = nullptr;
DWORD ocspBasicResponseSize = 0;
ret = CryptDecodeObjectEx(PKCS_7_ASN_ENCODING, OCSP_BASIC_RESPONSE, pocspBasicSignedResponseInfo->ToBeSigned.pbData, pocspBasicSignedResponseInfo->ToBeSigned.cbData, CRYPT_DECODE_ALLOC_FLAG, nullptr, &pocspBasicResponseInfo, &ocspBasicResponseSize);
if (!ret)
{
showStrError("DecodeOCSP_BASIC_RESPONSE");
return 1;
}
DWORD certStatus = pocspBasicResponseInfo->rgResponseEntry->dwCertStatus;
printf("cert status is %d", certStatus); // good:0, revoked:1, unknown:2
return 0;
The showStrError() is modified from WinCrypt examples as below:
void showStrError(const char* lpszFunction)
{
LPVOID lpMsgBuf;
LPVOID lpDisplayBuf;
DWORD dw = GetLastError();
FormatMessage(
FORMAT_MESSAGE_ALLOCATE_BUFFER |
FORMAT_MESSAGE_FROM_SYSTEM |
FORMAT_MESSAGE_IGNORE_INSERTS,
NULL,
dw,
MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
(LPTSTR)&lpMsgBuf,
0, NULL);
// Display the error message and exit the process
lpDisplayBuf = (LPVOID)LocalAlloc(LMEM_ZEROINIT,
(lstrlen((LPCTSTR)lpMsgBuf) + lstrlen((LPCTSTR)lpszFunction) + 40) * sizeof(TCHAR));
StringCchPrintf((LPTSTR)lpDisplayBuf,
LocalSize(lpDisplayBuf) / sizeof(TCHAR),
TEXT("%s failed with error %d: %s"),
lpszFunction, dw, lpMsgBuf);
printf("%s failed with error %d: %ws\n", lpszFunction, dw, (LPCTSTR)lpMsgBuf);
LocalFree(lpMsgBuf);
LocalFree(lpDisplayBuf);
}

ERROR_IO_PENDING on FTP Asynchronous Operation [Wininet C++]

I've recently been working on an application that needs to establish an FTP connection with a server and download/upload files from it. For performance reasons, I would like to download multiple files at a time. For that reason I've tried to implement asynchronous operation on the Wininet API using the InternetOpen function along with the INTERNET_FLAG_ASYNC flag, as well as the InternetSetStatusCallback function. Here is a sample of my code, where I want to list all the files in the main directory of the remote server recursively:
/*Global variables*/
HANDLE MayContinue=0;
DWORD LatestResult=1;
/*Prototypes*/
void CALLBACK CallbackFunction(HINTERNET,DWORD_PTR,DWORD,LPVOID,DWORD);
//Iteration function called by main()
void FTPIterate()
{
WIN32_FIND_DATAA data;
HINTERNET Internet;
INTERNET_STATUS_CALLBACK call;
HINTERNET h_file;
MayContinue = ::CreateEvent (NULL, FALSE, FALSE, NULL);
iconnect=InternetOpen(NULL,INTERNET_OPEN_TYPE_PROXY,proxy_url,NULL,INTERNET_FLAG_ASYNC);
call=InternetSetStatusCallback(iconnect,(INTERNET_STATUS_CALLBACK)CallbackFunction);
while(f[FLAG_FTP_ITERATE])
{
MayContinue = ::CreateEvent(NULL,FALSE,FALSE,NULL);
InternetConnect(iconnect,ftp_url,INTERNET_DEFAULT_FTP_PORT,ftp_user,ftp_pass,INTERNET_SERVICE_FTP,NULL,LatestResult);
WaitForSingleObject (MayContinue, INFINITE);
server=(HINTERNET)LatestResult;
printf("Server handle: %i\n",(int)server);
printf("Server Error: %i\n",GetLastError());
SetLastError(0);
MayContinue = ::CreateEvent(NULL,FALSE,FALSE,NULL);
FtpFindFirstFile(server,ftp_base,&data,INTERNET_FLAG_NO_CACHE_WRITE,LatestResult);
WaitForSingleObject(MayContinue,INFINITE);
h_file=(HINTERNET)LatestResult;
//do stuff
printf("FindFirstFile handle: %i\n",(int)h_File);
while((MayContinue = ::CreateEvent(NULL,FALSE,FALSE,NULL)) && InternetFindNextFileA(h_file,&data))
{
WaitForSingleObject(MayContinue,INFINITE);
//do stuff
}
printf("FindNextFile Error: %i\n",GetLastError()); //loop is never executed
InternetCloseHandle(server);
}
}
void CALLBACK CallbackFunction(HINTERNET hInternet,DWORD_PTR dwContext,DWORD dwInternetStatus,LPVOID lpvStatusInformation,DWORD dwStatusInformationLength)
{
if (dwInternetStatus == INTERNET_STATUS_REQUEST_COMPLETE)
{
LatestResult = ((LPINTERNET_ASYNC_RESULT)lpvStatusInformation)->dwResult;
SetEvent (MayContinue);
}
}
My code is based on this post from Stack Overflow. When I run it, I first of all get an error after the call to InternetConnect, which is ERROR_IO_PENDING. According to the WinAPI reference this means that there's still some operation being performed. Shouldn't the call to WaitForSingleObject prevent this from happening? (actually, the HINTERNET handle returned by InternetConnect seems to be valid).
When I call the FtpFindFirstFile function it correctly retrieves the first file, but when I use the HINTERNET handle (which, again, seems to be valid) returned by it in the InternetFindNextFile function it fails with error INVALID_HANDLE_VALUE.
EDIT: I egt those errors when using Remy's code:
Connect Handle 00CC0004
Waiting for server handle
Unable to find first file. OS Error: 6 //aren't those calls to FindFirstFile weird if InternetConnect hasn't returned yet?
Waiting for server handle
Unable to find first file. OS Error: 6
Waiting for server handle
Unable to find first file. OS Error: 6
Waiting for server handle
Unable to find first file. OS Error: 6
Waiting for server handle
Unable to find first file. OS Error: 6
Waiting for server handle
Unable to find first file. OS Error: 6
Waiting for server handle.
Unable to connect to Server. Inet Error: 12015 Waiting for server handle
Can someone help me find the mistake?
Thank you in advance.
The ERROR_IO_PENDING error is coming from InternetOpen() itself. Since WaitForSingleObject() is succeeding, it is not overwriting GetLastError() (it only does so on error, as most APIs do), so the error is being carried over from the result of InternetOpen(). This is not the correct way to use GetLastError(). Assume that all APIs overwrite GetLastError() (if documented to use GetLastError() at all), and make sure you call it immediately only if an API fails (unless documented as being used during success conditions).
What your code is doing is NOT asynchronous! You are issuing asynchronous API calls, but you are waiting for their results, which defeats the purpose. Your code is acting synchronously, the same as if you were to omit the INTERNAL_FLAG_ASYNC flag and WaitForSingleObject() calls (not to mention you are leaking event resource by calling CreateEvent() unnecessarily), eg:
void LogError(const char *Op)
{
DWORD err = GetLastError();
if (err == ERROR_INTERNET_EXTENDED_ERROR)
{
LPSTR szBuffer;
DWORD dwLength = 0;
InternetGetLastResponseInfoA(&err, NULL, &dwLength);
if (GetLastError() != INSUFFICIENT_BUFFER)
{
printf("%s. Unknown Inet Error. OS Error: %u", Op, GetLastError());
return;
}
szBuffer = new char[dwLength+1];
InternetGetLastResponseInfoA(&err, szBuffer, &dwLength);
szBuffer[dwLength] = 0;
printf("%s. Inet Error: %u %s", Op, err, szBuffer);
delete[] szBuffer;
}
else
{
LPSTR lpBuffer = NULL;
DWORD dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, (LPSTR)&lpBuffer, 0, NULL);
if (lpBuffer)
{
printf("%s. OS Error: %u %s", Op, err, lpBuffer);
LocalFree(lpBuffer);
}
else
printf("%s. OS Error: %u", Op, err);
}
printf("\n");
}
void FTPIterate()
{
WIN32_FIND_DATAA data;
HINTERNET hConnect;
HINTERNET hServer;
HINTERNET hFile;
hConnect = InternetOpen(NULL, INTERNET_OPEN_TYPE_PROXY, proxy_url, NULL, 0);
if (hConnect == NULL)
{
LogError("Unable to Open Internet");
return;
}
printf("Connect handle: %p\n", hConnect);
while (f[FLAG_FTP_ITERATE])
{
printf("Connecting to Server\n");
hServer = InternetConnect(hConnect, ftp_url, INTERNET_DEFAULT_FTP_PORT, ftp_user, ftp_pass, INTERNET_SERVICE_FTP, NULL, 0);
if (hServer == NULL)
{
LogError("Unable to connect to Server");
continue;
}
printf("Connected to Server. Server handle: %p\n", hServer);
printf("Finding first file\n");
hFile = FtpFindFirstFileA(hServer, ftp_base, &data, INTERNET_FLAG_NO_CACHE_WRITE, 0);
if (hFile == NULL)
{
if (GetLastError() == ERROR_NO_MORE_FILES)
printf("No files were found\n");
else
LogError("Unable to find first file");
}
else
{
printf("Find handle: %p\n", hFile);
do
{
//do stuff
printf("Finding next file\n");
if (!InternetFindNextFileA(hFile, &data))
{
if (GetLastError() == ERROR_NO_MORE_FILES)
printf("No more files were found\n");
else
LogError("Unable to find next file")
break;
}
}
while (true);
InternetCloseHandle(hFile);
}
InternetCloseHandle(hServer);
}
InternetCloseHandle(hConnect);
}
To make this code run asynchronously, get rid of all off the waits and implement a state machine that your callback advances as needed, eg:
enum FTPState {ftpConnect, ftpWaitingForConnect, ftpFindFirstFile, ftpWaitingForFirstFind, ftpFindNextFile, ftpWaitingForNextFind, ftpProcessFile, ftpDisconnect};
struct REQ_CONTEXT
{
FTPState State;
WIN32_FIND_DATAA data;
HINTERNET hConnect;
HINTERNET hServer;
HINTERNET hFile;
HANDLE hDoneEvent;
};
void LogError(const char *Op, DWORD err)
{
if (err == ERROR_INTERNET_EXTENDED_ERROR)
{
LPSTR szBuffer;
DWORD dwLength = 0;
InternetGetLastResponseInfoA(&err, NULL, &dwLength);
if (GetLastError() != ERROR_INSUFFICIENT_BUFFER)
{
printf("%s. Unknown Inet Error. OS Error: %u", Op, GetLastError());
return;
}
szBuffer = new char[dwLength+1];
InternetGetLastResponseInfoA(&err, szBuffer, &dwLength);
szBuffer[dwLength] = 0;
printf("%s. Inet Error: %u %s", Op, err, szBuffer);
delete[] szBuffer;
}
else
{
LPSTR lpBuffer = NULL;
DWORD dwLen = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_ARGUMENT_ARRAY | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, err, 0, (LPSTR)&lpBuffer, 0, NULL);
if (lpBuffer)
{
printf("%s. OS Error: %u %s", Op, err, lpBuffer);
LocalFree(lpBuffer);
}
else
printf("%s. OS Error: %u", Op, err);
}
printf("\n");
}
void LogError(const char *Op)
{
LogError(Op, GetLastError());
}
void DoNextStep(REQ_CONTEXT *ctx)
{
do
{
if ((ctx->State == ftpConnect) && (!f[FLAG_FTP_ITERATE]))
{
printf("Done!\n");
SetEvent(ctx->hDoneEvent);
return;
}
switch (ctx->State)
{
case ftpConnect:
{
printf("Connecting to Server\n");
HINTERNET hServer = InternetConnect(ctx->hConnect, ftp_url, INTERNET_DEFAULT_FTP_PORT, ftp_user, ftp_pass, INTERNET_SERVICE_FTP, NULL, (DWORD_PTR)ctx);
if (hServer != NULL)
{
if (ctx->hServer == NULL)
{
ctx->hServer = hServer;
printf("Server handle: %p\n", ctx->hServer);
}
printf("Connected to Server\n");
ctx->State = ftpFindFirstFile;
}
else if (GetLastError() == ERROR_IO_PENDING)
{
if (ctx->hServer == NULL)
printf("Waiting for Server handle\n");
printf("Waiting for Server connect to complete\n");
ctx->State = ftpWaitingForConnect;
}
else
LogError("Unable to connect to Server");
break;
}
case ftpWaitingForConnect:
return;
case ftpFindFirstFile:
{
printf("Finding first file\n");
HINTERNET hFile = FtpFindFirstFileA(ctx->hServer, ftp_base, &ctx->data, INTERNET_FLAG_NO_CACHE_WRITE, (DWORD_PTR)ctx);
if (hFile != NULL)
{
if (ctx->hFile == NULL)
{
ctx->hFile = hFile;
printf("Find handle: %p\n", ctx->hFile);
}
ctx->State = ftpProcessFile;
}
else if (GetLastError() == ERROR_IO_PENDING)
{
if (ctx->hFile == NULL)
printf("Waiting for Find handle\n");
printf("Waiting for Find to complete\n");
ctx->State = ftpWaitingForFirstFind;
}
else
{
if (GetLastError() == ERROR_NO_MORE_FILES)
printf("No files were found\n");
else
LogError("Unable to find first file");
ctx->State = ftpDisconnect;
}
break;
}
case ftpWaitingForFirstFind:
case ftpWaitingForNextFind:
return;
case ftpProcessFile:
{
//do stuff
printf("Finding next file\n");
if (!InternetFindNextFileA(ctx->hFile, &ctx->data))
{
if (GetLastError() == ERROR_IO_PENDING)
{
printf("Waiting for next file to complete\n");
ctx->State = ftpWaitingForNextFind;
}
else
{
if (GetLastError() == ERROR_NO_MORE_FILES)
printf("No more files were found\n");
else
LogError("Unable to find next file");
ctx->State = ftpDisconnect;
}
}
break;
}
case ftpDisconnect:
{
printf("Disconnecting\n");
if (ctx->hFile != NULL)
{
InternetCloseHandle(ctx->hFile);
ctx->hFile = NULL;
}
if (ctx->hServer != NULL)
{
InternetCloseHandle(ctx->hServer);
ctx->hServer = NULL;
}
ctx->State = ftpConnect;
break;
}
}
}
while (true);
}
void CALLBACK CallbackFunction(HINTERNET hInternet, DWORD_PTR dwContext, DWORD dwInternetStatus, LPVOID lpvStatusInformation, DWORD dwStatusInformationLength)
{
REQ_CONTEXT *ctx = (REQ_CONTEXT*) dwContext;
switch (dwInternetStatus)
{
case INTERNET_STATUS_HANDLE_CREATED:
{
LPINTERNET_ASYNC_RESULT Result = (LPINTERNET_ASYNC_RESULT) lpvStatusInformation;
switch (ctx->State)
{
case ftpConnect:
case ftpWaitingForConnect:
ctx->hServer = (HINTERNET) Result->dwResult;
printf("Server handle: %p\n", ctx->hServer);
break;
case ftpFindFirstFile:
case ftpWaitingForFirstFind:
ctx->hFile = (HINTERNET) Result->dwResult;
printf("Find handle: %p\n", ctx->hFile);
break;
}
break;
}
case INTERNET_STATUS_REQUEST_COMPLETE:
{
LPINTERNET_ASYNC_RESULT Result = (LPINTERNET_ASYNC_RESULT) lpvStatusInformation;
switch (ctx->State)
{
case ftpWaitingForConnect:
{
if (!Result->dwResult)
{
LogError("Unable to connect to Server", Result->dwError);
ctx->State = ftpDisconnect;
}
else
{
printf("Connected to Server\n");
ctx->State = ftpFindFirstFile;
}
break;
}
case ftpWaitingForFirstFind:
case ftpWaitingForNextFind:
{
if (!Result->dwResult)
{
if (Result->dwError == ERROR_NO_MORE_FILES)
printf("No %sfiles were found\n", (ctx->State == ftpWaitingForNextFind) ? "more " : "");
else if (ctx->State == ftpWaitingForFirstFind)
LogError("Unable to find first file", Result->dwError);
else
LogError("Unable to find next file", Result->dwError);
ctx->State = ftpDisconnect;
}
else
ctx->State = ftpProcessFile;
break;
}
}
DoNextStep(ctx);
break;
}
}
}
void FTPIterate()
{
REQ_CONTEXT ctx = {0};
ctx.State = ftpConnect;
ctx.hDoneEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
if (ctx.hDoneEvent == NULL)
{
LogError("Unable to Create Done Event");
return;
}
ctx.hConnect = InternetOpen(NULL, INTERNET_OPEN_TYPE_PROXY, proxy_url, NULL, INTERNET_FLAG_ASYNC);
if (ctx.hConnect == NULL)
{
LogError("Unable to Open Internet");
CloseHandle(ctx.hDoneEvent);
return;
}
printf("Connect handle: %p\n", ctx.hConnect);
InternetSetStatusCallback(ctx.hConnect, &CallbackFunction);
DoNextStep(&ctx);
WaitForSingleObject(ctx.hDoneEvent, INFINITE);
InternetCloseHandle(ctx.hConnect);
CloseHandle(ctx.hDoneEvent);
}

WsOpenServiceHost returns WS_E_OTHER

I'm tryind to run HttpCalculatorServiceExample located in MSDN. Function WsOpenServiceHost returns error code 0x803d0021. This error means
MessageId: WS_E_OTHER
Unrecognized error occurred in the Windows Web Services framework.
Printed error:
Unable to add URL to HTTP URL group.
Unrecognized error occurred in the Windows Web Services framework.
The process cannot access the file because it is being used by another process.
How to solve this problem?
int __cdecl wmain(int argc, __in_ecount(argc) wchar_t **argv)
{
UNREFERENCED_PARAMETER(argc);
UNREFERENCED_PARAMETER(argv);
HRESULT hr = NOERROR;
WS_SERVICE_HOST* host = NULL;
WS_SERVICE_ENDPOINT serviceEndpoint = {};
const WS_SERVICE_ENDPOINT* serviceEndpoints[1];
serviceEndpoints[0] = &serviceEndpoint;
WS_ERROR* error = NULL;
WS_SERVICE_ENDPOINT_PROPERTY serviceEndpointProperties[1];
WS_SERVICE_PROPERTY_CLOSE_CALLBACK closeCallbackProperty = {CloseChannelCallback};
serviceEndpointProperties[0].id = WS_SERVICE_ENDPOINT_PROPERTY_CLOSE_CHANNEL_CALLBACK;
serviceEndpointProperties[0].value = &closeCallbackProperty;
serviceEndpointProperties[0].valueSize = sizeof(closeCallbackProperty);
// Initialize service endpoint
serviceEndpoint.address.url.chars = L"http://+:80/example"; // address given as uri
serviceEndpoint.address.url.length = (ULONG)wcslen(serviceEndpoint.address.url.chars);
serviceEndpoint.channelBinding = WS_HTTP_CHANNEL_BINDING; // channel binding for the endpoint
serviceEndpoint.channelType = WS_CHANNEL_TYPE_REPLY; // the channel type
serviceEndpoint.contract = &calculatorContract; // the contract
serviceEndpoint.properties = serviceEndpointProperties;
serviceEndpoint.propertyCount = WsCountOf(serviceEndpointProperties);
// Create an error object for storing rich error information
hr = WsCreateError(
NULL,
0,
&error);
if (FAILED(hr))
{
goto Exit;
}
// Create Event object for closing the server
closeServer = CreateEvent(
NULL,
TRUE,
FALSE,
NULL);
if (closeServer == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
goto Exit;
}
// Creating a service host
hr = WsCreateServiceHost(
serviceEndpoints,
1,
NULL,
0,
&host,
error);
if (FAILED(hr))
{
goto Exit;
}
// WsOpenServiceHost to start the listeners in the service host
hr = WsOpenServiceHost(
host,
NULL,
error);
if (FAILED(hr))
{
goto Exit;
}
WaitForSingleObject(closeServer, INFINITE);
// Close the service host
hr = WsCloseServiceHost(host, NULL, error);
if (FAILED(hr))
{
goto Exit;
}
Exit:
if (FAILED(hr))
{
// Print out the error
PrintError(hr, error);
}
fflush(
stdout);
if (host != NULL)
{
WsFreeServiceHost(host);
}
if (error != NULL)
{
WsFreeError(error);
}
if (closeServer != NULL)
{
CloseHandle(closeServer);
}
fflush(stdout);
return SUCCEEDED(hr) ? 0 : -1;
}
I had the same error "Unrecognized error occurred in the Windows Web Services framework", while tried to debug remote target (C++ project).
I've tried all possible solutions, which I've found over internet: turn-off/uninstall antivirus(I use MS Essentials), turnoff firewall, reinstall VS etc.
But it didn't help.
So, I've deleted all programms (not windows updates) which I've installed last month, reboot computer, and the bug went away.
Uninstalled software:
Foxit PhantomPDF Standard. Версия: 7.0.5.1021.
widecap
CCleaner
Uninstall Tool
just in case, I've also uninstalled Microsoft Security Essentials and after the bug went away I've installed it again and had no problems.
Hope, this helps

How to obtain Provider name of a CSP from PCCERT_CONTEXT?

I have been try to get a provider name from PCCERT_CONTEXT because in my current project i must have load all certificate from smart card into my program. And in the future I have to deal with those certificate with some task like renewal certificate, delete certificate. But I have problem, I must to map CSP name and provider name with CryptAcquireContext to executive. And I currently confused how to archive this, can anyone have some guide to help me resolve this problem.
I have try CertGetCertificateContextProperty with dwPropId is CERT_KEY_PROV_INFO_PROP_ID but i can not get CRYPT_KEY_PROV_INFO.
If I have understood you correctly, the following snippet shows how to extract the key provider information from a certificate.
void trace(char* message, DWORD errorCode)
{
cout << message << errorCode;
}
std::wstring Test_CertGetCertificateContextProperty(PCCERT_CONTEXT pCertContext)
{
DWORD dwSize = 0;
BOOL bIsSuccess = CertGetCertificateContextProperty(pCertContext,
CERT_KEY_PROV_INFO_PROP_ID,
NULL,
&dwSize);
if (!bIsSuccess)
{
trace("CertGetCertificateContextProperty failed with error: ", GetLastError());
return L"";
}
PCRYPT_KEY_PROV_INFO pKeyProvInfo = (PCRYPT_KEY_PROV_INFO)LocalAlloc(LMEM_ZEROINIT, dwSize);
if (pKeyProvInfo == NULL)
{
trace("LocalAlloc failed with error:", GetLastError());
return L"";
}
bIsSuccess = CertGetCertificateContextProperty(pCertContext,
CERT_KEY_PROV_INFO_PROP_ID,
pKeyProvInfo,
&dwSize);
std::wstring provName;
if (bIsSuccess)
{
provName = pKeyProvInfo->pwszProvName;
}
LocalFree(pKeyProvInfo);
return provName;
}