Related
I wrote a program that runs in Windows 8.1 & 10 to verify a fingerprint against the currently logged in user's identity using WinBioVerify. I do this asynchronously by starting the session with WinBioAsyncOpenSession. I tried to run the same program on Windows 7, and it turns out that WinBioAsyncOpenSession doesn't exist in the Win7 version of WinBio.dll.
This shouldn't have been a problem, since it was easy enough to make a separate EXE for Win7 that opened the session normally and called WinBioVerifyWithCallback instead. The problem I'm having now has the same symptoms as when the original program was not properly setting the application's window focus: the fingerprint unit lights up when touched, but no result is returned to the code.
A Google search turns up some references that SetForegroundWindow() is unreliable in Win7, but none of the alternatives I've tried have worked either (BringWindowToTop, AttachThreadInput, WinBioAcquireFocus) assuming the focus is even the problem.
Does anyone have or know of any working examples of WinBioVerifyWithCallback on Windows 7?
EDITED TO ADD:
Here's the code that works (on Win8.1/Win10):
unsigned int verifyUser(string &userName) {
string libPath;
OutputDebugString("verifyUser: getApplicationPath");
libPath = getApplicationPath();
try {
//Make sure path ends with backslash
if (libPath[libPath.length()-1] != '\\') libPath += "\\";
string userHash = getHash(userName);
//Get timeout from globals
int timeoutValue = globals.timeout * 1000;
//Check to see if we need an infinite timeout
if (globals.disable_timeout)
{
timeoutValue = 1000 * 60 * 5;
}
TotalTickCount = timeoutValue; //5 minutes
startTick = GetTickCount();
globals.last_scan_result = BIO_INPROGRESS;
countdownTimer = SetTimer(hMainWnd, ID_COUNTDOWN, 1000, NULL);
CString lsr;
lsr.Format("globals.last_scan_result = %d", globals.last_scan_result);
OutputDebugString(lsr);
sessionHandle = NULL;
HRESULT hr = WinBioAsyncOpenSession(
WINBIO_TYPE_FINGERPRINT, // Service provider
WINBIO_POOL_SYSTEM, // Pool type
WINBIO_FLAG_DEFAULT, // Configuration and access
NULL, // Array of biometric unit IDs
0, // Count of biometric unit IDs
NULL, // Database ID
WINBIO_ASYNC_NOTIFY_MESSAGE,// Notification method
hMainWnd, // Target window
WM_APP+BIO_INPROGRESS, // Message code
NULL, // Callback routine
NULL, // User data
FALSE, // Asynchronous open
&sessionHandle // [out] Session handle
);
if (FAILED(hr))
{
CString s;
s.Format("WinBioAsyncOpenSession failed. hr = 0x%x\n", hr);
OutputDebugString(s);
KillTimer(hMainWnd, countdownTimer);
ShowWindow(hMainWnd, SW_HIDE);
LockSetForegroundWindow(LSFW_UNLOCK);
if (sessionHandle != NULL)
{
WinBioCloseSession(sessionHandle);
sessionHandle = NULL;
}
return BIO_ERROR;
}
// Find the identity of the user.
WINBIO_IDENTITY identity = { 0 };
hr = GetCurrentUserIdentity(&identity);
if (FAILED(hr))
{
CString s;
s.Format("User identity not found. hr = 0x%x\n", hr);
OutputDebugString(s);
KillTimer(hMainWnd, countdownTimer);
ShowWindow(hMainWnd, SW_HIDE);
LockSetForegroundWindow(LSFW_UNLOCK);
if (sessionHandle != NULL)
{
WinBioCloseSession(sessionHandle);
sessionHandle = NULL;
}
return BIO_USER_NOT_ENROLLED;
}
// Verify a biometric sample.
WINBIO_UNIT_ID unitId = 0;
WINBIO_REJECT_DETAIL rejectDetail = 0;
BOOLEAN match = FALSE;
OutputDebugString("Calling WinBioVerify");
SetWindowText(hStaticWnd, "To sign in, scan your finger on the fingerprint reader.");
UpdateWindow(hStaticWnd);
ShowWindow(hMainWnd, SW_SHOW);
SetForegroundWindow(hMainWnd);
LockSetForegroundWindow(LSFW_LOCK);
hr = WinBioVerify(
sessionHandle,
&identity,
WINBIO_SUBTYPE_ANY,
&unitId,
&match,
&rejectDetail
);
CString msg;
msg.Format("Swipe processed - Unit ID: %d\n", unitId);
OutputDebugString(msg);
lsr.Format("globals.last_scan_result = %d", globals.last_scan_result);
OutputDebugString(lsr);
while (globals.last_scan_result == BIO_INPROGRESS)
{
OutputDebugString("Waiting for verify...");
lsr.Format("globals.last_scan_result = %d", globals.last_scan_result);
OutputDebugString(lsr);
MSG msg;
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT) break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else if (globals.last_scan_result == BIO_INPROGRESS)
{
WaitMessage();
}
}
OutputDebugString("Waiting for verify DONE.");
if (globals.last_scan_result == BIO_INPROGRESS)
{
globals.last_scan_result = BIO_CANCEL;
}
lsr.Format("globals.last_scan_result = %d", globals.last_scan_result);
OutputDebugString(lsr);
ShowWindow(hMainWnd, SW_HIDE);
LockSetForegroundWindow(LSFW_UNLOCK);
if (sessionHandle != NULL)
{
/*
if (globals.last_scan_result == BIO_CANCEL || globals.last_scan_result == BIO_TIMEOUT)
{
WinBioCancel(sessionHandle);
//WinBioWait(sessionHandle);
}
*/
WinBioCloseSession(sessionHandle);
sessionHandle = NULL;
}
return globals.last_scan_result;
} catch (...) {
OutputDebugString("Verify catch Error");
}
return BIO_ERROR;
}
Here's the code that doesn't work on Win7:
unsigned int verifyUser7(string &userName) {
string libPath;
OutputDebugString("verifyUser7: getApplicationPath");
libPath = getApplicationPath();
// HWND capwnd;
try {
//Make sure path ends with backslash
if (libPath[libPath.length() - 1] != '\\') libPath += "\\";
string userHash = getHash(userName);
//Get timeout from globals
int timeoutValue = globals.timeout * 1000;
//Check to see if we need an infinite timeout
if (globals.disable_timeout)
{
timeoutValue = 1000 * 60 * 5;
}
TotalTickCount = timeoutValue; //5 minutes
startTick = GetTickCount();
globals.last_scan_result = BIO_INPROGRESS;
countdownTimer = SetTimer(hMainWnd, ID_COUNTDOWN, 1000, NULL);
CString lsr;
lsr.Format("globals.last_scan_result = %d", globals.last_scan_result);
OutputDebugString(lsr);
sessionHandle = NULL;
HRESULT hr = WinBioOpenSession(
WINBIO_TYPE_FINGERPRINT, // Service provider
WINBIO_POOL_SYSTEM, // Pool type
WINBIO_FLAG_DEFAULT, // Configuration and access
NULL, // Array of biometric unit IDs
0, // Count of biometric unit IDs
NULL, // Database ID
&sessionHandle // [out] Session handle
);
if (FAILED(hr))
{
CString s;
s.Format("WinBioOpenSession failed. hr = 0x%x\n", hr);
OutputDebugString(s);
KillTimer(hMainWnd, countdownTimer);
ShowWindow(hMainWnd, SW_HIDE);
LockSetForegroundWindow(LSFW_UNLOCK);
if (sessionHandle != NULL)
{
WinBioCloseSession(sessionHandle);
sessionHandle = NULL;
}
return BIO_ERROR;
}
// Find the identity of the user.
WINBIO_IDENTITY identity = { 0 };
hr = GetCurrentUserIdentity(&identity);
if (FAILED(hr))
{
CString s;
s.Format("User identity not found. hr = 0x%x\n", hr);
OutputDebugString(s);
KillTimer(hMainWnd, countdownTimer);
ShowWindow(hMainWnd, SW_HIDE);
LockSetForegroundWindow(LSFW_UNLOCK);
if (sessionHandle != NULL)
{
WinBioCloseSession(sessionHandle);
sessionHandle = NULL;
}
return BIO_USER_NOT_ENROLLED;
}
// Verify a biometric sample.
WINBIO_UNIT_ID unitId = 0;
WINBIO_REJECT_DETAIL rejectDetail = 0;
BOOLEAN match = FALSE;
OutputDebugString("Calling WinBioVerifyWithCallback");
SetWindowText(hStaticWnd, "To sign in, scan your finger on the fingerprint reader.");
UpdateWindow(hStaticWnd);
ShowWindow(hMainWnd, SW_SHOW);
SetForegroundWindow(hMainWnd);
LockSetForegroundWindow(LSFW_LOCK);
hr = WinBioVerifyWithCallback(
sessionHandle,
&identity,
WINBIO_SUBTYPE_ANY,
VerifyCallback, // Callback function
NULL // Optional context
);
CString msg;
//msg.Format("Swipe processed - Unit ID: %d\n", unitId);
//OutputDebugString(msg);
lsr.Format("globals.last_scan_result = %d", globals.last_scan_result);
OutputDebugString(lsr);
while (globals.last_scan_result == BIO_INPROGRESS)
{
OutputDebugString("Waiting for verify...");
lsr.Format("globals.last_scan_result = %d", globals.last_scan_result);
OutputDebugString(lsr);
MSG msg;
if (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE))
{
if (msg.message == WM_QUIT) break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
else if (globals.last_scan_result == BIO_INPROGRESS)
{
WaitMessage();
}
}
OutputDebugString("Waiting for verify DONE.");
if (globals.last_scan_result == BIO_INPROGRESS)
{
globals.last_scan_result = BIO_CANCEL;
}
lsr.Format("globals.last_scan_result = %d", globals.last_scan_result);
OutputDebugString(lsr);
ShowWindow(hMainWnd, SW_HIDE);
WinBioReleaseFocus();
//LockSetForegroundWindow(LSFW_UNLOCK);
if (sessionHandle != NULL)
{
WinBioCloseSession(sessionHandle);
sessionHandle = NULL;
}
return globals.last_scan_result;
}
catch (...) {
OutputDebugString("Verify catch Error");
}
return BIO_ERROR;
}
Solved by adding SetFocus() after SetForegroundWindow(). Not sure why this extra step is necessary on Windows 7.
I've been using the following code (taken from KB323809 article) to retrieve information about the code signature on the executable file. This works fine for a single digital signature.
But how to retrieve information for multiple code signatures?
In that case the Microsoft code below simply retrives info only for the first signature.
My thought was to call CryptMsgGetParam with CMSG_SIGNER_COUNT_PARAM to get the number of signatures and then pass each signature index to the subsequent call to CryptMsgGetParam with CMSG_SIGNER_INFO_PARAM (in the code below.) But this approach always returns 1 signature, even if I clearly have more, like 3 in this example:
#include <windows.h>
#include <wincrypt.h>
#include <wintrust.h>
#include <stdio.h>
#include <tchar.h>
#pragma comment(lib, "crypt32.lib")
#define ENCODING (X509_ASN_ENCODING | PKCS_7_ASN_ENCODING)
typedef struct {
LPWSTR lpszProgramName;
LPWSTR lpszPublisherLink;
LPWSTR lpszMoreInfoLink;
} SPROG_PUBLISHERINFO, *PSPROG_PUBLISHERINFO;
BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo,
PSPROG_PUBLISHERINFO Info);
BOOL GetDateOfTimeStamp(PCMSG_SIGNER_INFO pSignerInfo, SYSTEMTIME *st);
BOOL PrintCertificateInfo(PCCERT_CONTEXT pCertContext);
BOOL GetTimeStampSignerInfo(PCMSG_SIGNER_INFO pSignerInfo,
PCMSG_SIGNER_INFO *pCounterSignerInfo);
int _tmain(int argc, TCHAR *argv[])
{
WCHAR szFileName[MAX_PATH];
HCERTSTORE hStore = NULL;
HCRYPTMSG hMsg = NULL;
PCCERT_CONTEXT pCertContext = NULL;
BOOL fResult;
DWORD dwEncoding, dwContentType, dwFormatType;
PCMSG_SIGNER_INFO pSignerInfo = NULL;
PCMSG_SIGNER_INFO pCounterSignerInfo = NULL;
DWORD dwSignerInfo;
CERT_INFO CertInfo;
SPROG_PUBLISHERINFO ProgPubInfo;
SYSTEMTIME st;
ZeroMemory(&ProgPubInfo, sizeof(ProgPubInfo));
__try
{
if (argc != 2)
{
_tprintf(_T("Usage: SignedFileInfo <filename>\n"));
return 0;
}
#ifdef UNICODE
lstrcpynW(szFileName, argv[1], MAX_PATH);
#else
if (mbstowcs(szFileName, argv[1], MAX_PATH) == -1)
{
printf("Unable to convert to unicode.\n");
__leave;
}
#endif
// Get message handle and store handle from the signed file.
fResult = CryptQueryObject(CERT_QUERY_OBJECT_FILE,
szFileName,
CERT_QUERY_CONTENT_FLAG_PKCS7_SIGNED_EMBED,
CERT_QUERY_FORMAT_FLAG_BINARY,
0,
&dwEncoding,
&dwContentType,
&dwFormatType,
&hStore,
&hMsg,
NULL);
if (!fResult)
{
_tprintf(_T("CryptQueryObject failed with %x\n"), GetLastError());
__leave;
}
// Get signer information size.
fResult = CryptMsgGetParam(hMsg,
CMSG_SIGNER_INFO_PARAM,
0,
NULL,
&dwSignerInfo);
if (!fResult)
{
_tprintf(_T("CryptMsgGetParam failed with %x\n"), GetLastError());
__leave;
}
// Allocate memory for signer information.
pSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSignerInfo);
if (!pSignerInfo)
{
_tprintf(_T("Unable to allocate memory for Signer Info.\n"));
__leave;
}
// Get Signer Information.
fResult = CryptMsgGetParam(hMsg,
CMSG_SIGNER_INFO_PARAM,
0,
(PVOID)pSignerInfo,
&dwSignerInfo);
if (!fResult)
{
_tprintf(_T("CryptMsgGetParam failed with %x\n"), GetLastError());
__leave;
}
// Get program name and publisher information from
// signer info structure.
if (GetProgAndPublisherInfo(pSignerInfo, &ProgPubInfo))
{
if (ProgPubInfo.lpszProgramName != NULL)
{
wprintf(L"Program Name : %s\n",
ProgPubInfo.lpszProgramName);
}
if (ProgPubInfo.lpszPublisherLink != NULL)
{
wprintf(L"Publisher Link : %s\n",
ProgPubInfo.lpszPublisherLink);
}
if (ProgPubInfo.lpszMoreInfoLink != NULL)
{
wprintf(L"MoreInfo Link : %s\n",
ProgPubInfo.lpszMoreInfoLink);
}
}
_tprintf(_T("\n"));
// Search for the signer certificate in the temporary
// certificate store.
CertInfo.Issuer = pSignerInfo->Issuer;
CertInfo.SerialNumber = pSignerInfo->SerialNumber;
pCertContext = CertFindCertificateInStore(hStore,
ENCODING,
0,
CERT_FIND_SUBJECT_CERT,
(PVOID)&CertInfo,
NULL);
if (!pCertContext)
{
_tprintf(_T("CertFindCertificateInStore failed with %x\n"),
GetLastError());
__leave;
}
// Print Signer certificate information.
_tprintf(_T("Signer Certificate:\n\n"));
PrintCertificateInfo(pCertContext);
_tprintf(_T("\n"));
// Get the timestamp certificate signerinfo structure.
if (GetTimeStampSignerInfo(pSignerInfo, &pCounterSignerInfo))
{
// Search for Timestamp certificate in the temporary
// certificate store.
CertInfo.Issuer = pCounterSignerInfo->Issuer;
CertInfo.SerialNumber = pCounterSignerInfo->SerialNumber;
pCertContext = CertFindCertificateInStore(hStore,
ENCODING,
0,
CERT_FIND_SUBJECT_CERT,
(PVOID)&CertInfo,
NULL);
if (!pCertContext)
{
_tprintf(_T("CertFindCertificateInStore failed with %x\n"),
GetLastError());
__leave;
}
// Print timestamp certificate information.
_tprintf(_T("TimeStamp Certificate:\n\n"));
PrintCertificateInfo(pCertContext);
_tprintf(_T("\n"));
// Find Date of timestamp.
if (GetDateOfTimeStamp(pCounterSignerInfo, &st))
{
_tprintf(_T("Date of TimeStamp : %02d/%02d/%04d %02d:%02d\n"),
st.wMonth,
st.wDay,
st.wYear,
st.wHour,
st.wMinute);
}
_tprintf(_T("\n"));
}
}
__finally
{
// Clean up.
if (ProgPubInfo.lpszProgramName != NULL)
LocalFree(ProgPubInfo.lpszProgramName);
if (ProgPubInfo.lpszPublisherLink != NULL)
LocalFree(ProgPubInfo.lpszPublisherLink);
if (ProgPubInfo.lpszMoreInfoLink != NULL)
LocalFree(ProgPubInfo.lpszMoreInfoLink);
if (pSignerInfo != NULL) LocalFree(pSignerInfo);
if (pCounterSignerInfo != NULL) LocalFree(pCounterSignerInfo);
if (pCertContext != NULL) CertFreeCertificateContext(pCertContext);
if (hStore != NULL) CertCloseStore(hStore, 0);
if (hMsg != NULL) CryptMsgClose(hMsg);
}
return 0;
}
BOOL PrintCertificateInfo(PCCERT_CONTEXT pCertContext)
{
BOOL fReturn = FALSE;
LPTSTR szName = NULL;
DWORD dwData;
__try
{
// Print Serial Number.
_tprintf(_T("Serial Number: "));
dwData = pCertContext->pCertInfo->SerialNumber.cbData;
for (DWORD n = 0; n < dwData; n++)
{
_tprintf(_T("%02x "),
pCertContext->pCertInfo->SerialNumber.pbData[dwData - (n + 1)]);
}
_tprintf(_T("\n"));
// Get Issuer name size.
if (!(dwData = CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG,
NULL,
NULL,
0)))
{
_tprintf(_T("CertGetNameString failed.\n"));
__leave;
}
// Allocate memory for Issuer name.
szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR));
if (!szName)
{
_tprintf(_T("Unable to allocate memory for issuer name.\n"));
__leave;
}
// Get Issuer name.
if (!(CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
CERT_NAME_ISSUER_FLAG,
NULL,
szName,
dwData)))
{
_tprintf(_T("CertGetNameString failed.\n"));
__leave;
}
// print Issuer name.
_tprintf(_T("Issuer Name: %s\n"), szName);
LocalFree(szName);
szName = NULL;
// Get Subject name size.
if (!(dwData = CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
NULL,
0)))
{
_tprintf(_T("CertGetNameString failed.\n"));
__leave;
}
// Allocate memory for subject name.
szName = (LPTSTR)LocalAlloc(LPTR, dwData * sizeof(TCHAR));
if (!szName)
{
_tprintf(_T("Unable to allocate memory for subject name.\n"));
__leave;
}
// Get subject name.
if (!(CertGetNameString(pCertContext,
CERT_NAME_SIMPLE_DISPLAY_TYPE,
0,
NULL,
szName,
dwData)))
{
_tprintf(_T("CertGetNameString failed.\n"));
__leave;
}
// Print Subject Name.
_tprintf(_T("Subject Name: %s\n"), szName);
fReturn = TRUE;
}
__finally
{
if (szName != NULL) LocalFree(szName);
}
return fReturn;
}
LPWSTR AllocateAndCopyWideString(LPCWSTR inputString)
{
LPWSTR outputString = NULL;
outputString = (LPWSTR)LocalAlloc(LPTR,
(wcslen(inputString) + 1) * sizeof(WCHAR));
if (outputString != NULL)
{
lstrcpyW(outputString, inputString);
}
return outputString;
}
BOOL GetProgAndPublisherInfo(PCMSG_SIGNER_INFO pSignerInfo,
PSPROG_PUBLISHERINFO Info)
{
BOOL fReturn = FALSE;
PSPC_SP_OPUS_INFO OpusInfo = NULL;
DWORD dwData;
BOOL fResult;
__try
{
// Loop through authenticated attributes and find
// SPC_SP_OPUS_INFO_OBJID OID.
for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
{
if (lstrcmpA(SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0)
{
// Get Size of SPC_SP_OPUS_INFO structure.
fResult = CryptDecodeObject(ENCODING,
SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
NULL,
&dwData);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
// Allocate memory for SPC_SP_OPUS_INFO structure.
OpusInfo = (PSPC_SP_OPUS_INFO)LocalAlloc(LPTR, dwData);
if (!OpusInfo)
{
_tprintf(_T("Unable to allocate memory for Publisher Info.\n"));
__leave;
}
// Decode and get SPC_SP_OPUS_INFO structure.
fResult = CryptDecodeObject(ENCODING,
SPC_SP_OPUS_INFO_OBJID,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
OpusInfo,
&dwData);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
// Fill in Program Name if present.
if (OpusInfo->pwszProgramName)
{
Info->lpszProgramName =
AllocateAndCopyWideString(OpusInfo->pwszProgramName);
}
else
Info->lpszProgramName = NULL;
// Fill in Publisher Information if present.
if (OpusInfo->pPublisherInfo)
{
switch (OpusInfo->pPublisherInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
Info->lpszPublisherLink =
AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
Info->lpszPublisherLink =
AllocateAndCopyWideString(OpusInfo->pPublisherInfo->pwszFile);
break;
default:
Info->lpszPublisherLink = NULL;
break;
}
}
else
{
Info->lpszPublisherLink = NULL;
}
// Fill in More Info if present.
if (OpusInfo->pMoreInfo)
{
switch (OpusInfo->pMoreInfo->dwLinkChoice)
{
case SPC_URL_LINK_CHOICE:
Info->lpszMoreInfoLink =
AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszUrl);
break;
case SPC_FILE_LINK_CHOICE:
Info->lpszMoreInfoLink =
AllocateAndCopyWideString(OpusInfo->pMoreInfo->pwszFile);
break;
default:
Info->lpszMoreInfoLink = NULL;
break;
}
}
else
{
Info->lpszMoreInfoLink = NULL;
}
fReturn = TRUE;
break; // Break from for loop.
} // lstrcmp SPC_SP_OPUS_INFO_OBJID
} // for
}
__finally
{
if (OpusInfo != NULL) LocalFree(OpusInfo);
}
return fReturn;
}
BOOL GetDateOfTimeStamp(PCMSG_SIGNER_INFO pSignerInfo, SYSTEMTIME *st)
{
BOOL fResult;
FILETIME lft, ft;
DWORD dwData;
BOOL fReturn = FALSE;
// Loop through authenticated attributes and find
// szOID_RSA_signingTime OID.
for (DWORD n = 0; n < pSignerInfo->AuthAttrs.cAttr; n++)
{
if (lstrcmpA(szOID_RSA_signingTime,
pSignerInfo->AuthAttrs.rgAttr[n].pszObjId) == 0)
{
// Decode and get FILETIME structure.
dwData = sizeof(ft);
fResult = CryptDecodeObject(ENCODING,
szOID_RSA_signingTime,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->AuthAttrs.rgAttr[n].rgValue[0].cbData,
0,
(PVOID)&ft,
&dwData);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
break;
}
// Convert to local time.
FileTimeToLocalFileTime(&ft, &lft);
FileTimeToSystemTime(&lft, st);
fReturn = TRUE;
break; // Break from for loop.
} //lstrcmp szOID_RSA_signingTime
} // for
return fReturn;
}
BOOL GetTimeStampSignerInfo(PCMSG_SIGNER_INFO pSignerInfo, PCMSG_SIGNER_INFO *pCounterSignerInfo)
{
PCCERT_CONTEXT pCertContext = NULL;
BOOL fReturn = FALSE;
BOOL fResult;
DWORD dwSize;
__try
{
*pCounterSignerInfo = NULL;
// Loop through unathenticated attributes for
// szOID_RSA_counterSign OID.
for (DWORD n = 0; n < pSignerInfo->UnauthAttrs.cAttr; n++)
{
if (lstrcmpA(pSignerInfo->UnauthAttrs.rgAttr[n].pszObjId,
szOID_RSA_counterSign) == 0)
{
// Get size of CMSG_SIGNER_INFO structure.
fResult = CryptDecodeObject(ENCODING,
PKCS7_SIGNER_INFO,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData,
0,
NULL,
&dwSize);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
// Allocate memory for CMSG_SIGNER_INFO.
*pCounterSignerInfo = (PCMSG_SIGNER_INFO)LocalAlloc(LPTR, dwSize);
if (!*pCounterSignerInfo)
{
_tprintf(_T("Unable to allocate memory for timestamp info.\n"));
__leave;
}
// Decode and get CMSG_SIGNER_INFO structure
// for timestamp certificate.
fResult = CryptDecodeObject(ENCODING,
PKCS7_SIGNER_INFO,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].pbData,
pSignerInfo->UnauthAttrs.rgAttr[n].rgValue[0].cbData,
0,
(PVOID)*pCounterSignerInfo,
&dwSize);
if (!fResult)
{
_tprintf(_T("CryptDecodeObject failed with %x\n"),
GetLastError());
__leave;
}
fReturn = TRUE;
break; // Break from for loop.
}
}
}
__finally
{
// Clean up.
if (pCertContext != NULL) CertFreeCertificateContext(pCertContext);
}
return fReturn;
}
In addition to the answer of Daniel Sie.
Found an attribute (szOID_NESTED_SIGNATURE) that would contain the CMSG_SIGNER_INFO that need to be decoded with following steps (this is Delphi code but sense is clear):
LNestedMsg := CryptMsgOpenToDecode(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, 0, 0, 0, nil, 0);
CryptMsgUpdate(LNestedMsg, LFindedAttr.rgValue.pbData, LFindedAttr.rgValue.cbData, True);
CryptMsgGetParam(LNestedMsg, CMSG_SIGNER_INFO_PARAM, 0, nil, #LSize);
CryptMsgGetParam(LNestedMsg, CMSG_SIGNER_INFO_PARAM, 0, LNestedSignerInfo, #LSize);
The acquired CMSG_SIGNER_INFO (LNestedSignerInfo) is the nested signature (in our case SHA2 signature).
To obtain the time-stamp information (RFC3161) of that signature - search the Unauthenticated attribute with pszObjId = szOID_RFC3161_counterSign (1.3.6.1.4.1.311.3.3.1).
Found attribute would contain the CMSG_SIGNER_INFO of the time-stamp counter signature, that also need to be decoded by previously described steps (CryptMsgOpenToDecode, CryptMsgUpdate, CryptMsgGetParam).
The CERT_CONTEXT of nested signature or timestamp counter signature is need to be searched in store with is obtained from corresponding HCRYPTMSG (result of CryptMsgOpenToDecode).
LNestedStore := CertOpenStore(CERT_STORE_PROV_MSG, PKCS_7_ASN_ENCODING or X509_ASN_ENCODING, 0, 0, LNestedMsg);
LTimeStampStore := CertOpenStore(CERT_STORE_PROV_MSG, PKCS_7_ASN_ENCODING or X509_ASN_ENCODING, 0, 0, LTimeStampMsg);
Example to decode szOID_RFC3161_counterSign attribute:
LNestedSignerAttr := LNestedSigner.UnauthAttrs.rgAttr;
for I := 0 to LNestedSigner.UnauthAttrs.cAttr - 1 do
begin
if SameText(string(LNestedSignerAttr.pszObjId), szOID_RFC3161_counterSign) then
begin
LNestedTimeStampMsg := CryptMsgOpenToDecode(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING, 0, 0, 0, nil, nil);
if not Assigned(LNestedTimeStampMsg) then
RaiseLastOSError;
try
if not CryptMsgUpdate(LNestedTimeStampMsg, LNestedSignerAttr.rgValue.pbData, LNestedSignerAttr.rgValue.cbData, True) then
RaiseLastOSError;
if not CryptMsgGetParam(LNestedTimeStampMsg, CMSG_SIGNER_INFO_PARAM, 0, nil, #LSize) then
RaiseLastOSError;
GetMem(LTimeStampSigner, LSize);
try
if not CryptMsgGetParam(LNestedTimeStampMsg, CMSG_SIGNER_INFO_PARAM, 0, LTimeStampSigner, #LSize) then
RaiseLastOSError;
LAttr := LTimeStampSigner.AuthAttrs.rgAttr;
for J := 0 to LTimeStampSigner.AuthAttrs.cAttr - 1 do
begin
if SameText(string(LAttr.pszObjId), szOID_RSA_signingTime) then
begin
LSize := SizeOf(LFileTime);
if not CryptDecodeObject(X509_ASN_ENCODING or PKCS_7_ASN_ENCODING,
szOID_RSA_signingTime, LAttr.rgValue.pbData, LAttr.rgValue.cbData, 0, #LFileTime, #LSize) then
RaiseLastOSError;
if FileTimeToLocalFileTime(#LFileTime, LLocalFileTime)
and FileTimeToSystemTime(#LLocalFileTime, LSystemTime) then
SHA2TimeStamp := SystemTimeToDateTime(LSystemTime)
else
SHA2TimeStamp := 0;
end;
Inc(LAttr);
end;
finally
FreeMem(LTimeStampSigner);
end;
finally
if not CryptMsgClose(LNestedTimeStampMsg) then
RaiseLastOSError;
end;
end;
Inc(LNestedSignerAttr);
end;
Authenticode stores secondary signatures in the UnauthenticatedAttributes of primary signer (index 0), instead of additional PKCS 7 signer.
From the primary signature, search the UnauthenticatedAttribue for below:
//Indicates the attribute is an octet encoded PKCS7
define szOID_NESTED_SIGNATURE "1.3.6.1.4.1.311.2.4.1"
The encoded object of this attribute is a full PKCS 7 signer.
Thanks.
Iam new to c++. Using the help of MSDN article iam trying to fetch all the computers under a OU in domain using ADSI. But Iam getting 8254L (FILTER_UNKNOWN) error. Iam not sure what iam doing wrong here. I tried to update the filter but no change in the error. Please suggest what iam doing wrong here.
Below is the code am using for getting the list.
// ConsoleApplication3.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <objbase.h>
#include <wchar.h>
#include <activeds.h>
#include <sddl.h>
#include <comutil.h>
#include <string.h>
#include <stdio.h>
HRESULT FindComputers(IDirectorySearch *pContainerToSearch, // IDirectorySearch pointer to the container to search.
LPOLESTR szFilter, // Filter to find specific users.
// NULL returns all user objects.
LPOLESTR *pszPropertiesToReturn, // Properties to return for user objects found.
// NULL returns all set properties.
BOOL bIsVerbose // TRUE indicates that display all properties for the found objects.
// FALSE indicates that only the RDN.
);
// Entry point for the application.
void wmain(int argc, wchar_t *argv[])
{
// Initialize COM.
CoInitialize(NULL);
HRESULT hr = S_OK;
// Get rootDSE and the current user domain container distinguished name.
IADs *pObject = NULL;
IDirectorySearch *pContainerToSearch = NULL;
LPOLESTR szPath = new OLECHAR[MAX_PATH];
BOOL bReturnVerbose = FALSE;
DWORD dwLength = MAX_PATH * 2;
LPOLESTR pszBuffer = new OLECHAR[dwLength];
VARIANT var;
hr = ADsOpenObject(L"LDAP://rootDSE",
NULL,
NULL,
ADS_SECURE_AUTHENTICATION, // Use Secure Authentication.
IID_IADs,
(void**)&pObject);
if (FAILED(hr))
{
wprintf(L"Cannot execute query. Cannot bind to LDAP://rootDSE.\n");
if (pObject)
pObject->Release();
return;
}
if (SUCCEEDED(hr))
{
hr = pObject->Get(_bstr_t("defaultNamingContext"), &var);
if (SUCCEEDED(hr))
{
wprintf(L"bstrVal: %s\n", var.bstrVal);
// Build path to the domain container.
// wcsncpy_s(szPath, L"LDAP://", MAX_PATH);
// wcsncat_s(szPath, var.bstrVal, MAX_PATH - wcslen(szPath));
//hr = ADsOpenObject(szPath,
hr = ADsOpenObject(L"LDAP://OU=IA Computers,OU=Infosys,DC=iaseries,Dc=local",
NULL,
NULL,
ADS_SECURE_AUTHENTICATION, // Use Secure Authentication.
IID_IDirectorySearch,
(void**)&pContainerToSearch);
if (SUCCEEDED(hr))
{
hr = FindComputers(pContainerToSearch, // IDirectorySearch pointer to domainDNS container.
pszBuffer,
NULL, // Return all properties.
bReturnVerbose
);
if (SUCCEEDED(hr))
{
if (S_FALSE == hr)
wprintf(L"Computer object cannot be found.\n");
}
else if (E_ADS_INVALID_FILTER == hr)
wprintf(L"Cannot execute query. Invalid filter was specified.\n");
else
wprintf(L"Query failed to run. HRESULT: %x\n", hr);
}
else
{
wprintf(L"Cannot execute query. Cannot bind to the container.\n");
}
if (pContainerToSearch)
pContainerToSearch->Release();
}
VariantClear(&var);
}
if (pObject)
pObject->Release();
// Uninitialize COM.
CoUninitialize();
delete[] szPath;
delete[] pszBuffer;
getchar();
}
HRESULT FindComputers(IDirectorySearch *pContainerToSearch, // IDirectorySearch pointer to the container to search.
LPOLESTR szFilter, // Filter for finding specific users.
// NULL returns all user objects.
LPOLESTR *pszPropertiesToReturn, // Properties to return for user objects found.
// NULL returns all set properties.
BOOL bIsVerbose // TRUE indicates that all properties for the found objects are displayed.
// FALSE indicates only the RDN.
)
{
if (!pContainerToSearch)
return E_POINTER;
DWORD dwLength = (MAX_PATH * 2)+100;
// Create search filter.
LPOLESTR pszSearchFilter = new OLECHAR[dwLength];
// Add the filter.
//swprintf_s(pszSearchFilter, dwLength, L"((objectClass=Computer)%s)", szFilter);
swprintf_s(pszSearchFilter, dwLength, L"(&(objectClass=*)(objectCategory=Computer)%s)", szFilter);
// Specify subtree search.
ADS_SEARCHPREF_INFO SearchPrefs;
SearchPrefs.dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
SearchPrefs.vValue.dwType = ADSTYPE_INTEGER;
SearchPrefs.vValue.Integer = ADS_SCOPE_SUBTREE;
DWORD dwNumPrefs = 1;
// COL for iterations.
LPOLESTR pszColumn = NULL;
ADS_SEARCH_COLUMN col;
HRESULT hr = S_OK;
// Interface Pointers
IADs *pObj = NULL;
IADs * pIADs = NULL;
// Search handle.
ADS_SEARCH_HANDLE hSearch = NULL;
// Set search preference.
hr = pContainerToSearch->SetSearchPreference(&SearchPrefs, dwNumPrefs);
if (FAILED(hr))
return hr;
LPOLESTR pszBool = NULL;
DWORD dwBool = 0;
PSID pObjectSID = NULL;
LPOLESTR szSID = NULL;
LPOLESTR szDSGUID = new WCHAR[39];
LPGUID pObjectGUID = NULL;
SYSTEMTIME systemtime;
DATE date;
VARIANT varDate;
LPOLESTR *pszPropertyList = NULL;
LPOLESTR pszNonVerboseList[] = { L"name", L"distinguishedName" };
LPOLESTR szName = new OLECHAR[MAX_PATH];
LPOLESTR szDN = new OLECHAR[MAX_PATH];
VariantInit(&varDate);
int iCount = 0;
DWORD x = 0L;
if (!bIsVerbose)
{
// Return non-verbose list properties only.
hr = pContainerToSearch->ExecuteSearch(pszSearchFilter,
pszNonVerboseList,
sizeof(pszNonVerboseList) / sizeof(LPOLESTR),
&hSearch
);
}
else
{
if (!pszPropertiesToReturn)
{
// Return all properties.
hr = pContainerToSearch->ExecuteSearch(pszSearchFilter,
NULL,
(DWORD)-1,
&hSearch
);
}
else
{
// Specified subset.
pszPropertyList = pszPropertiesToReturn;
// Return specified properties.
hr = pContainerToSearch->ExecuteSearch(pszSearchFilter,
pszPropertyList,
sizeof(pszPropertyList) / sizeof(LPOLESTR),
&hSearch
);
}
}
if (SUCCEEDED(hr))
{
// Call IDirectorySearch::GetNextRow() to retrieve the next data row.
hr = pContainerToSearch->GetFirstRow(hSearch);
if (SUCCEEDED(hr))
{
while (hr != S_ADS_NOMORE_ROWS)
{
// Keep track of count.
iCount++;
if (bIsVerbose)
wprintf(L"----------------------------------\n");
// Loop through the array of passed column names,
// print the data for each column.
while (pContainerToSearch->GetNextColumnName(hSearch, &pszColumn) != S_ADS_NOMORE_COLUMNS)
{
hr = pContainerToSearch->GetColumn(hSearch, pszColumn, &col);
if (SUCCEEDED(hr))
{
// Print the data for the column and free the column.
if (bIsVerbose)
{
// Get the data for this column.
wprintf(L"%s\n", col.pszAttrName);
switch (col.dwADsType)
{
case ADSTYPE_DN_STRING:
for (x = 0; x< col.dwNumValues; x++)
{
wprintf(L" %s\r\n", col.pADsValues[x].DNString);
}
break;
case ADSTYPE_CASE_EXACT_STRING:
case ADSTYPE_CASE_IGNORE_STRING:
case ADSTYPE_PRINTABLE_STRING:
case ADSTYPE_NUMERIC_STRING:
case ADSTYPE_TYPEDNAME:
case ADSTYPE_FAXNUMBER:
case ADSTYPE_PATH:
for (x = 0; x< col.dwNumValues; x++)
{
wprintf(L" %s\r\n", col.pADsValues[x].CaseIgnoreString);
}
break;
case ADSTYPE_BOOLEAN:
for (x = 0; x< col.dwNumValues; x++)
{
dwBool = col.pADsValues[x].Boolean;
pszBool = dwBool ? L"TRUE" : L"FALSE";
wprintf(L" %s\r\n", pszBool);
}
break;
case ADSTYPE_INTEGER:
for (x = 0; x< col.dwNumValues; x++)
{
wprintf(L" %d\r\n", col.pADsValues[x].Integer);
}
break;
case ADSTYPE_OCTET_STRING:
if (_wcsicmp(col.pszAttrName, L"objectSID") == 0)
{
for (x = 0; x< col.dwNumValues; x++)
{
pObjectSID = (PSID)(col.pADsValues[x].OctetString.lpValue);
// Convert SID to string.
ConvertSidToStringSid(pObjectSID, &szSID);
wprintf(L" %s\r\n", szSID);
LocalFree(szSID);
}
}
else if ((_wcsicmp(col.pszAttrName, L"objectGUID") == 0))
{
for (x = 0; x< col.dwNumValues; x++)
{
// Cast to LPGUID.
pObjectGUID = (LPGUID)(col.pADsValues[x].OctetString.lpValue);
// Convert GUID to string.
::StringFromGUID2(*pObjectGUID, szDSGUID, 39);
// Print the GUID.
wprintf(L" %s\r\n", szDSGUID);
}
}
else
wprintf(L" Value of type Octet String. No Conversion.");
break;
case ADSTYPE_UTC_TIME:
for (x = 0; x< col.dwNumValues; x++)
{
systemtime = col.pADsValues[x].UTCTime;
if (SystemTimeToVariantTime(&systemtime,
&date) != 0)
{
// Pack in variant.vt.
varDate.vt = VT_DATE;
varDate.date = date;
VariantChangeType(&varDate, &varDate, VARIANT_NOVALUEPROP, VT_BSTR);
wprintf(L" %s\r\n", varDate.bstrVal);
VariantClear(&varDate);
}
else
wprintf(L" Could not convert UTC-Time.\n", pszColumn);
}
break;
case ADSTYPE_NT_SECURITY_DESCRIPTOR:
for (x = 0; x< col.dwNumValues; x++)
{
wprintf(L" Security descriptor.\n");
}
break;
default:
wprintf(L"Unknown type %d.\n", col.dwADsType);
}
}
else
{
#ifdef _MBCS
// Verbose handles only the two single-valued attributes: cn and ldapdisplayname,
// so this is a special case.
if (0 == wcscmp(L"name", pszColumn))
{
//wcscpy_s(szName, col.pADsValues->CaseIgnoreString);
szName = col.pADsValues->CaseIgnoreString;
}
if (0 == wcscmp(L"distinguishedName", pszColumn))
{
//wcscpy_s(szDN, col.pADsValues->CaseIgnoreString);
szDN = col.pADsValues->CaseIgnoreString;
}
#endif _MBCS
}
pContainerToSearch->FreeColumn(&col);
}
FreeADsMem(pszColumn);
}
if (!bIsVerbose)
wprintf(L"%s\n DN: %s\n\n", szName, szDN);
// Get the next row.
hr = pContainerToSearch->GetNextRow(hSearch);
}
}
// Close the search handle to cleanup.
pContainerToSearch->CloseSearchHandle(hSearch);
}
if (SUCCEEDED(hr) && 0 == iCount)
hr = S_FALSE;
delete[] szName;
delete[] szDN;
delete[] szDSGUID;
delete[] pszSearchFilter;
return hr;
}
Iam able to figure out the issue. Below is the code for working sample.
I haven't cleaned the code much. Please update if you intend to use.
// ConsoleApplication3.cpp : Defines the entry point for the console application.
//
#include "stdafx.h"
#include <objbase.h>
#include <wchar.h>
#include <activeds.h>
#include <sddl.h>
#include <comutil.h>
#include <string.h>
#include <stdio.h>
HRESULT FindComputers(IDirectorySearch *pContainerToSearch); // IDirectorySearch pointer to the container to search.
// Entry point for the application.
void wmain(int argc, wchar_t *argv[])
{
// Initialize COM.
CoInitialize(NULL);
HRESULT hr = S_OK;
// Get rootDSE and the current user domain container distinguished name.
IADs *pObject = NULL;
IDirectorySearch *pContainerToSearch = NULL;
LPOLESTR szPath = new OLECHAR[MAX_PATH];
BOOL bReturnVerbose = FALSE;
DWORD dwLength = MAX_PATH * 2;
VARIANT var;
hr = ADsOpenObject(L"LDAP://rootDSE",
NULL,
NULL,
ADS_SECURE_AUTHENTICATION, // Use Secure Authentication.
IID_IADs,
(void**)&pObject);
if (FAILED(hr))
{
wprintf(L"Cannot execute query. Cannot bind to LDAP://rootDSE.\n");
if (pObject)
pObject->Release();
return;
}
if (SUCCEEDED(hr))
{
hr = pObject->Get(_bstr_t("defaultNamingContext"), &var);
if (SUCCEEDED(hr))
{
//wprintf(L"bstrVal: %s\n", var.bstrVal);
// Build path to the domain container.
// wcsncpy_s(szPath, L"LDAP://", MAX_PATH);
// wcsncat_s(szPath, var.bstrVal, MAX_PATH - wcslen(szPath));
//hr = ADsOpenObject(szPath,
hr = ADsOpenObject(L"LDAP://OU=IA Computers,OU=MyDept,DC=Test,Dc=com",
NULL,
NULL,
ADS_SECURE_AUTHENTICATION, // Use Secure Authentication.
IID_IDirectorySearch,
(void**)&pContainerToSearch);
if (SUCCEEDED(hr))
{
hr = FindComputers(pContainerToSearch); // IDirectorySearch pointer to domainDNS container.
if (SUCCEEDED(hr))
{
if (S_FALSE == hr)
wprintf(L"Computer object cannot be found.\n");
}
else if (E_ADS_INVALID_FILTER == hr)
wprintf(L"Cannot execute query. Invalid filter was specified.\n");
else
wprintf(L"Query failed to run. HRESULT: %x\n", hr);
}
else
{
wprintf(L"Cannot execute query. Cannot bind to the container.\n");
}
if (pContainerToSearch)
pContainerToSearch->Release();
}
VariantClear(&var);
}
if (pObject)
pObject->Release();
// Uninitialize COM.
CoUninitialize();
delete[] szPath;
getchar();
}
HRESULT FindComputers(IDirectorySearch *pContainerToSearch) // IDirectorySearch pointer to the container to search.
{
if (!pContainerToSearch)
return E_POINTER;
DWORD dwLength = (MAX_PATH * 2);
// Create search filter.
LPOLESTR pszSearchFilter = new OLECHAR[dwLength];
// Add the filter.
pszSearchFilter = L"((objectCategory=computer))";
// Specify subtree search.
ADS_SEARCHPREF_INFO SearchPrefs;
SearchPrefs.dwSearchPref = ADS_SEARCHPREF_SEARCH_SCOPE;
SearchPrefs.vValue.dwType = ADSTYPE_INTEGER;
SearchPrefs.vValue.Integer = ADS_SCOPE_SUBTREE;
DWORD dwNumPrefs = 1;
// COL for iterations.
LPOLESTR pszColumn = NULL;
ADS_SEARCH_COLUMN col;
HRESULT hr = S_OK;
// Interface Pointers
IADs *pObj = NULL;
IADs * pIADs = NULL;
// Search handle.
ADS_SEARCH_HANDLE hSearch = NULL;
// Set search preference.
hr = pContainerToSearch->SetSearchPreference(&SearchPrefs, dwNumPrefs);
if (FAILED(hr))
return hr;
LPOLESTR pszNonVerboseList[] = { L"name", L"distinguishedName" };
LPOLESTR szName = new OLECHAR[MAX_PATH];
LPOLESTR szDN = new OLECHAR[MAX_PATH];
int iCount = 0;
DWORD x = 0L;
// Return non-verbose list properties only.
hr = pContainerToSearch->ExecuteSearch(pszSearchFilter,
pszNonVerboseList,
sizeof(pszNonVerboseList) / sizeof(LPOLESTR),
&hSearch
);
if (SUCCEEDED(hr))
{
// Call IDirectorySearch::GetNextRow() to retrieve the next data row.
hr = pContainerToSearch->GetFirstRow(hSearch);
if (SUCCEEDED(hr))
{
while (hr != S_ADS_NOMORE_ROWS)
{
// Keep track of count.
iCount++;
// Loop through the array of passed column names,
// print the data for each column.
while (pContainerToSearch->GetNextColumnName(hSearch, &pszColumn) != S_ADS_NOMORE_COLUMNS)
{
hr = pContainerToSearch->GetColumn(hSearch, pszColumn, &col);
if (SUCCEEDED(hr))
{
// Verbose handles only the two single-valued attributes: cn and ldapdisplayname,
// so this is a special case.
if (0 == wcscmp(L"name", pszColumn))
{
//wcscpy_s(szName, col.pADsValues->CaseIgnoreString);
szName = col.pADsValues->CaseIgnoreString;
}
if (0 == wcscmp(L"distinguishedName", pszColumn))
{
//wcscpy_s(szDN, col.pADsValues->CaseIgnoreString);
szDN = col.pADsValues->CaseIgnoreString;
}
pContainerToSearch->FreeColumn(&col);
}
FreeADsMem(pszColumn);
}
wprintf(L"%s\n DN: %s\n\n", szName, szDN);
// Get the next row.
hr = pContainerToSearch->GetNextRow(hSearch);
}
}
// Close the search handle to cleanup.
pContainerToSearch->CloseSearchHandle(hSearch);
}
if (SUCCEEDED(hr) && 0 == iCount)
hr = S_FALSE;
delete[] szName;
delete[] szDN;
delete[] pszSearchFilter;
return hr;
}
Thanks,
Vijay
Following my previous question here, now I get two shells - one parent (not elevated), and a child shell (elevated). What does one need to do to the code to have instead one shell only that's elevated? E.g. how about somehow closing the parent process?
BOOL IsAppRunningAsAdminMode()
{
BOOL fIsRunAsAdmin = FALSE;
DWORD dwError = ERROR_SUCCESS;
PSID pAdministratorsGroup = NULL;
// Allocate and initialize a SID of the administrators group.
SID_IDENTIFIER_AUTHORITY NtAuthority = SECURITY_NT_AUTHORITY;
if (!AllocateAndInitializeSid(
&NtAuthority,
2,
SECURITY_BUILTIN_DOMAIN_RID,
DOMAIN_ALIAS_RID_ADMINS,
0, 0, 0, 0, 0, 0,
&pAdministratorsGroup))
{
dwError = GetLastError();
goto Cleanup;
}
// Determine whether the SID of administrators group is enabled in
// the primary access token of the process.
if (!CheckTokenMembership(NULL, pAdministratorsGroup, &fIsRunAsAdmin))
{
dwError = GetLastError();
goto Cleanup;
}
Cleanup:
// Centralized cleanup for all allocated resources.
if (pAdministratorsGroup)
{
FreeSid(pAdministratorsGroup);
pAdministratorsGroup = NULL;
}
// Throw the error if something failed in the function.
if (ERROR_SUCCESS != dwError)
{
throw dwError;
}
return fIsRunAsAdmin;
}
int main() {
bool fIsRunAsAdmin = IsAppRunningAsAdminMode();
if (fIsRunAsAdmin == false)
{
wchar_t szPath[MAX_PATH];
if (GetModuleFileName(NULL, szPath, ARRAYSIZE(szPath)))
{
// Launch itself as admin
SHELLEXECUTEINFO sei = { sizeof(sei) };
sei.lpVerb = L"runas";
sei.lpFile = szPath;
sei.hwnd = NULL;
sei.nShow = SW_NORMAL;
if (!ShellExecuteEx(&sei))
{
DWORD dwError = GetLastError();
if (dwError == ERROR_CANCELLED)
{
// The user refused to allow privileges elevation.
std::cout << "User did not allow elevation" << std::endl;
}
}
}
}
else {
//do nothing since process already elevated
}
//other code following omitted
We are currently using the NetBios method, and it works ok under XP. Preliminary tests under Vista show that it also works, but there are caveats - NetBIOS has to be present, for instance, and from what I've been reading, the order of the adapters is bound to change. Our alternative method - with SNMPExtensionQuery - seems to be broken under Vista.
The question is: do you know of a reliable way to get a list of the local MAC addresses on a Vista machine? Backwards compatibility with XP is a plus (I'd rather have one single method than lots of ugly #ifdef's). Thanks!
This will give you a list of all MAC addresses on your computer. It will work with all versions of Windows as well:
void getdMacAddresses(std::vector<std::string> &vMacAddresses;)
{
vMacAddresses.clear();
IP_ADAPTER_INFO AdapterInfo[32]; // Allocate information for up to 32 NICs
DWORD dwBufLen = sizeof(AdapterInfo); // Save memory size of buffer
DWORD dwStatus = GetAdaptersInfo( // Call GetAdapterInfo
AdapterInfo, // [out] buffer to receive data
&dwBufLen); // [in] size of receive data buffer
//No network card? Other error?
if(dwStatus != ERROR_SUCCESS)
return;
PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo;
char szBuffer[512];
while(pAdapterInfo)
{
if(pAdapterInfo->Type == MIB_IF_TYPE_ETHERNET)
{
sprintf_s(szBuffer, sizeof(szBuffer), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x"
, pAdapterInfo->Address[0]
, pAdapterInfo->Address[1]
, pAdapterInfo->Address[2]
, pAdapterInfo->Address[3]
, pAdapterInfo->Address[4]
, pAdapterInfo->Address[5]
);
vMacAddresses.push_back(szBuffer);
}
pAdapterInfo = pAdapterInfo->Next;
}
}
Could you use the WMIService? I used it to get the mac-address of a machine in pre-Vista days though.
Old question, already answered, but this is safer code - in case WMI can't be fully initialized.
For getting access to information about your system, here is a minimalist class that tries to stay safe:
NOTE: ToString, convertToUtf8 and convertFromUtf8 are left as an exercise for the reader. :)
NOTE: I've just shown the safe initialization and tear-down of the WMI system, and the basics of getting values from WMI, and getting the MAC Addresses (the question in the OP).
This came from working code, but was modified as I pasted it in here. So it is possible other things got left out that ought to have been included. Oops.
class WmiAccessor
{
public:
WmiAccessor()
: _pWbemLocator(NULL)
, _pWbemServices(NULL)
, _com_initialized(false)
, _com_need_uninitialize(false)
, _svc_initialized(false)
, _loc_initialized(false)
, _all_initialized(false)
, _errors("")
, m_mutex()
{
HRESULT hr;
hr = ::CoInitializeEx(NULL, COINIT_MULTITHREADED);
switch (hr)
{
case S_OK:
// The COM library was initialized successfully on this thread.
_com_initialized = true;
_com_need_uninitialize = true;
break;
case S_FALSE:
// The COM library is already initialized on this thread.
_com_initialized = true;
_com_need_uninitialize = true;
break;
case RPC_E_CHANGED_MODE:
// A previous call to CoInitializeEx specified the concurrency model
// for this thread as multithread apartment (MTA).
// This could also indicate that a change from neutral-threaded apartment to
// single-threaded apartment has occurred.
_com_initialized = true;
_com_need_uninitialize = false;
break;
default:
_com_initialized = false;
_com_need_uninitialize = false;
_errors += "Failed to initialize COM.\r\n";
return;
}
hr = ::CoInitializeSecurity(NULL, -1, NULL, NULL,
0 /*RPC_C_AUTHN_LEVEL_DEFAULT*/,
3 /*RPC_C_IMP_LEVEL_IMPERSONATE*/,
NULL, EOAC_NONE, NULL);
// RPC_E_TOO_LATE == Security must be initialized before!
// It cannot be changed once initialized. I don't care!
if (FAILED(hr) && (hr != RPC_E_TOO_LATE))
{
_errors += "Failed to initialize COM Security.\r\n";
if (_com_need_uninitialize)
{
::CoUninitialize();
_com_need_uninitialize = false;
}
return;
}
hr = _pWbemLocator.CoCreateInstance(CLSID_WbemLocator);
if (FAILED(hr) || (_pWbemLocator == nullptr))
{
_errors += "Failed to initialize WBEM Locator.\r\n";
return;
}
_loc_initialized = true;
hr = _pWbemLocator->ConnectServer(
CComBSTR(L"root\\cimv2"), NULL, NULL, 0, NULL, 0, NULL, &_pWbemServices);
if (FAILED(hr) || (_pWbemServices == nullptr))
{
_errors += "Failed to connect WBEM Locator.\r\n";
_pWbemLocator.Release();
_loc_initialized = false;
return;
}
else
{
_svc_initialized = true;
// Set security Levels on the proxy
hr = CoSetProxyBlanket(_pWbemServices,
RPC_C_AUTHN_WINNT, // RPC_C_AUTHN_xxx
RPC_C_AUTHZ_NONE, // RPC_C_AUTHZ_xxx
NULL, // Server principal name
RPC_C_AUTHN_LEVEL_CALL, // RPC_C_AUTHN_LEVEL_xxx
RPC_C_IMP_LEVEL_IMPERSONATE, // RPC_C_IMP_LEVEL_xxx
NULL, // client identity
EOAC_NONE // proxy capabilities
);
if (FAILED(hr))
{
_errors += "Failed to set proxy blanket.\r\n";
return;
}
}
_all_initialized = true;
}
~WmiAccessor()
{
std::unique_lock<std::mutex> slock(m_mutex);
if (_svc_initialized)
{
if (_pWbemServices)
_pWbemServices.Release();
_svc_initialized = false;
}
if (_loc_initialized)
{
if (_pWbemLocator)
_pWbemLocator.Release();
_loc_initialized = false;
}
if (_com_initialized)
{
if (_com_need_uninitialize)
{
::CoUninitialize();
}
_com_initialized = false;
_com_need_uninitialize = false;
}
_all_initialized = false;
}
// public: must lock
std::string get_and_clear_errors()
{
std::string result = "";
std::unique_lock<std::mutex> slock(m_mutex);
std::swap(result, _errors);
return result;
}
// public: must lock
std::string get_string(const std::string& name, const std::string& dflt /*= ""*/)
{
std::unique_lock<std::mutex> slock(m_mutex);
return _all_initialized ? _string(name) : dflt;
}
// public: must lock
uint32_t get_uint32(const std::string& name, uint32_t dflt /*= 0*/)
{
std::unique_lock<std::mutex> slock(m_mutex);
return _all_initialized ? _uint32(name) : dflt;
}
// similarly for other public accessors of basic types.
private:
CComPtr<IWbemLocator> _pWbemLocator;
CComPtr<IWbemServices> _pWbemServices;
volatile bool _com_initialized;
volatile bool _com_need_uninitialize;
volatile bool _svc_initialized;
volatile bool _loc_initialized;
volatile bool _all_initialized;
std::string _errors;
CComVariant _variant(const std::wstring& name);
std::string _string(const std::string& name);
uint32_t _uint32(const std::string& name);
uint16_t _uint16(const std::string& name);
uint8_t _uint8(const std::string& name);
std::vector<std::string> _macAddresses(bool forceReCalculate = false);
// to protect internal objects, public methods need to protect the internals.
//
mutable std::mutex m_mutex;
std::vector<std::string> _macs; // unlikely to change, so save them once found.
// internal: assumes inside a lock
CComVariant _variant(const std::wstring& name)
{
if (!_all_initialized)
return CComVariant();
CComPtr<IEnumWbemClassObject> pEnum;
CComBSTR cbsQuery = std::wstring(L"Select " + name + L" from Win32_OperatingSystem").c_str();
HRESULT hr = _pWbemServices->ExecQuery(
CComBSTR(L"WQL"), cbsQuery, WBEM_FLAG_FORWARD_ONLY, NULL, &pEnum);
CComVariant cvtValue;
if (FAILED(hr) || !pEnum)
{
std::wstring wquery(cbsQuery, SysStringLen(cbsQuery));
_errors += "Failed to exec WMI query: '" + convertToUtf8(wquery) + "'\r\n";
return cvtValue;
}
ULONG uObjectCount = 0;
CComPtr<IWbemClassObject> pWmiObject;
hr = pEnum->Next(WBEM_INFINITE, 1, &pWmiObject, &uObjectCount);
if (FAILED(hr) || !pWmiObject)
{
_errors
+= "Failed to get WMI Next result for: '" + convertToUtf8(name) + "'\r\n";
return cvtValue;
}
hr = pWmiObject->Get(name.c_str(), 0, &cvtValue, 0, 0);
if (FAILED(hr))
{
_errors
+= "Failed to get WMI result value for: '" + convertToUtf8(name) + "'\r\n";
}
return cvtValue;
}
// internal: assumes inside a lock
std::string _string(const std::string& name)
{
if (!_all_initialized)
return "";
CComVariant cvtValue = _variant(convertFromUtf8(name).c_str());
std::wstring wValue(cvtValue.bstrVal, SysStringLen(cvtValue.bstrVal));
std::string sValue = convertToUtf8(wValue);
return sValue;
}
// internal: assumes inside a lock
uint32_t _uint32(const std::string& name)
{
if (!_all_initialized)
return 0;
CComVariant cvtValue = _variant(convertFromUtf8(name).c_str());
uint32_t uValue = static_cast<uint32_t>(cvtValue.lVal);
return uValue;
}
// similarly for other internal access of basic types.
// internal: assumes inside a lock
std::vector<std::string> _macAddresses(bool forceReCalculate /*= false*/)
{
if (!_all_initialized)
{
return _macs; // it will still be empty at this point.
}
if (forceReCalculate)
{
_macs.clear();
}
if (_macs.empty())
{
// hr == 0x80041010 == WBEM_E_INVALID_CLASS
// hr == 0x80041017 == WBEM_E_INVALID_QUERY
// hr == 0x80041018 == WBEM_E_INVALID_QUERY_TYPE
CComBSTR cbsQuery = std::wstring(L"Select * from Win32_NetworkAdapter").c_str();
CComPtr<IEnumWbemClassObject> pEnum;
HRESULT hr = _pWbemServices->ExecQuery(
CComBSTR(L"WQL"), cbsQuery, WBEM_RETURN_IMMEDIATELY, NULL, &pEnum);
if (FAILED(hr))
{
_errors += "error: MacAddresses: ExecQuery('"
+ convertToUtf8((LPWSTR)cbsQuery) + "') returned "
+ ToString(hr) + "\r\n";
}
if (SUCCEEDED(hr))
{
ULONG fetched;
VARIANT var;
IWbemClassObject* pclsObj = NULL;
while (pEnum)
{
hr = pEnum->Next(WBEM_INFINITE, 1, &pclsObj, &fetched);
if (0 == fetched)
break;
std::string theMac = "";
VariantInit(&var);
hr = pclsObj->Get(L"MACAddress", 0, &var, 0, 0);
if (SUCCEEDED(hr))
{
switch (var.vt)
{
case VT_NULL: break;
case VT_BSTR:
theMac = (var.bstrVal == NULL)
? ""
: convertToUtf8(var.bstrVal);
break;
case VT_LPSTR:
theMac = (var.bstrVal == NULL)
? ""
: (const char*)var.bstrVal;
break;
case VT_LPWSTR:
theMac = (var.bstrVal == NULL)
? ""
: convertToUtf8((LPWSTR)var.bstrVal);
break;
// _could_ be array of BSTR, LPSTR, LPWSTR; unlikely, but ....
case VT_ARRAY | VT_BSTR:
case VT_ARRAY | VT_LPSTR:
case VT_ARRAY | VT_LPWSTR:
_errors += "warning: MacAddresses: unexpected array of addresses";
_errors += "\r\n";
// yet another exercise for the reader :)
break;
default:
_errors += "error: MacAddresses: unexpected VARIANT.vt = "
+ ToString(var.vt) + "\r\n";
break;
}
// local loopback has an empty address?
if (!theMac.empty())
{
_macs.push_back(theMac);
}
}
VariantClear(&var);
pclsObj->Release();
}
}
}
return _macs;
}
...
}
GetAdaptersInfo() is the official method, it enumerates all adapters even ones that are disconnected.
See this post for example code codeguru
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <stdio.h>
#include <vector>
#include <Windows.h>
#include <Iphlpapi.h>
#include <Assert.h>
#include <string>
#pragma comment(lib, "iphlpapi.lib")
char* getdMacAddresses()
{
IP_ADAPTER_INFO AdapterInfo[32]; // Allocate information for up to 32 NICs
DWORD dwBufLen = sizeof(AdapterInfo); // Save memory size of buffer
DWORD dwStatus = GetAdaptersInfo( // Call GetAdapterInfo
AdapterInfo, // [out] buffer to receive data
&dwBufLen); // [in] size of receive data buffer
//Exit When Error
if (dwStatus != ERROR_SUCCESS)
return "ERROR";
PIP_ADAPTER_INFO pAdapterInfo = AdapterInfo;
char szBuffer[512];
while (pAdapterInfo)
{
if (pAdapterInfo->Type == MIB_IF_TYPE_ETHERNET)
{
sprintf_s(szBuffer, sizeof(szBuffer), "%.2x-%.2x-%.2x-%.2x-%.2x-%.2x"
, pAdapterInfo->Address[0]
, pAdapterInfo->Address[1]
, pAdapterInfo->Address[2]
, pAdapterInfo->Address[3]
, pAdapterInfo->Address[4]
, pAdapterInfo->Address[5]
);
return szBuffer;
}
pAdapterInfo = pAdapterInfo->Next;
}
return "ERROR";
}
You can use WMI on both XP and Vista, there are a number of examples online. e.g:
Use Windows Management Instrumentation (WMI) to get a MAC Address