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.
1278 lines
35 KiB
1278 lines
35 KiB
/*++
|
|
|
|
Copyright (C) Microsoft Corporation, 1999 - 1999
|
|
|
|
Module Name:
|
|
|
|
PropCert
|
|
|
|
Abstract:
|
|
|
|
This module initiates smart card cert propagation to 'My' store.
|
|
It is loaded and started by winlogon through an entry in the registry.
|
|
|
|
Author:
|
|
|
|
Klaus Schutz
|
|
|
|
--*/
|
|
|
|
#include "stdafx.h"
|
|
#include <wincrypt.h>
|
|
#include <winscard.h>
|
|
#include <winwlx.h>
|
|
|
|
#include "calaislb.h"
|
|
#include "scrdcert.h" // smart card cert store
|
|
#include "certprop.h"
|
|
#include "StatMon.h" // smart card reader status monitor
|
|
#include "scevents.h"
|
|
|
|
#include <mmsystem.h>
|
|
|
|
#ifndef SCARD_PROVIDER_CSP
|
|
#define SCARD_PROVIDER_CSP 2
|
|
#endif
|
|
|
|
#define REG_KEY "Software\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Notify\\ScCertProp"
|
|
|
|
static THREADDATA l_ThreadData;
|
|
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#undef THIS_FILE
|
|
static char THIS_FILE[] = __FILE__;
|
|
#endif
|
|
|
|
#if defined(DBG) || defined(DEBUG)
|
|
BOOL Debug = TRUE;
|
|
#define DebugPrint(a) _DebugPrint a
|
|
void
|
|
__cdecl
|
|
_DebugPrint(
|
|
LPCTSTR szFormat,
|
|
...
|
|
)
|
|
{
|
|
if (Debug) {
|
|
|
|
TCHAR szBuffer[512];
|
|
va_list ap;
|
|
|
|
va_start(ap, szFormat);
|
|
_vstprintf(szBuffer, szFormat, ap);
|
|
OutputDebugString(szBuffer);
|
|
}
|
|
}
|
|
|
|
#else
|
|
#define DebugPrint(a)
|
|
#endif
|
|
|
|
LPCTSTR
|
|
FirstString(
|
|
IN LPCTSTR szMultiString
|
|
)
|
|
/*++
|
|
|
|
FirstString:
|
|
|
|
This routine returns a pointer to the first string in a multistring, or NULL
|
|
if there aren't any.
|
|
|
|
Arguments:
|
|
|
|
szMultiString - This supplies the address of the current position within a
|
|
Multi-string structure.
|
|
|
|
Return Value:
|
|
|
|
The address of the first null-terminated string in the structure, or NULL if
|
|
there are no strings.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 11/25/1996
|
|
|
|
--*/
|
|
{
|
|
LPCTSTR szFirst = NULL;
|
|
|
|
try
|
|
{
|
|
if (0 != *szMultiString)
|
|
szFirst = szMultiString;
|
|
}
|
|
catch (...) {}
|
|
|
|
return szFirst;
|
|
}
|
|
|
|
LPCTSTR
|
|
NextString(
|
|
IN LPCTSTR szMultiString)
|
|
/*++
|
|
|
|
NextString:
|
|
|
|
In some cases, the Smartcard API returns multiple strings, separated by Null
|
|
characters, and terminated by two null characters in a row. This routine
|
|
simplifies access to such structures. Given the current string in a
|
|
multi-string structure, it returns the next string, or NULL if no other
|
|
strings follow the current string.
|
|
|
|
Arguments:
|
|
|
|
szMultiString - This supplies the address of the current position within a
|
|
Multi-string structure.
|
|
|
|
Return Value:
|
|
|
|
The address of the next Null-terminated string in the structure, or NULL if
|
|
no more strings follow.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 8/12/1996
|
|
|
|
--*/
|
|
{
|
|
LPCTSTR szNext;
|
|
|
|
try
|
|
{
|
|
DWORD cchLen = lstrlen(szMultiString);
|
|
if (0 == cchLen)
|
|
szNext = NULL;
|
|
else
|
|
{
|
|
szNext = szMultiString + cchLen + 1;
|
|
if (0 == *szNext)
|
|
szNext = NULL;
|
|
}
|
|
}
|
|
|
|
catch (...)
|
|
{
|
|
szNext = NULL;
|
|
}
|
|
|
|
return szNext;
|
|
}
|
|
|
|
/*++
|
|
|
|
MoveToUnicodeString:
|
|
|
|
This routine moves the internal string representation to a UNICODE output
|
|
buffer.
|
|
|
|
Arguments:
|
|
|
|
szDst receives the output string. It must be sufficiently large enough to
|
|
handle the string. If this parameter is NULL, then the number of
|
|
characters required to hold the result is returned.
|
|
|
|
szSrc supplies the input string.
|
|
|
|
cchLength supplies the length of the input string, with or without trailing
|
|
nulls. A -1 value implies the length should be computed based on a
|
|
trailing null.
|
|
|
|
Return Value:
|
|
|
|
The length of the resultant string, in characters, including the trailing
|
|
null.
|
|
|
|
Throws:
|
|
|
|
Errors as DWORD status codes.
|
|
|
|
Author:
|
|
|
|
Doug Barlow (dbarlow) 2/14/1997
|
|
|
|
--*/
|
|
|
|
DWORD
|
|
MoveToUnicodeString(
|
|
LPWSTR szDst,
|
|
LPCTSTR szSrc,
|
|
DWORD cchLength)
|
|
{
|
|
if ((DWORD)(-1) == cchLength)
|
|
cchLength = lstrlen(szSrc);
|
|
else
|
|
{
|
|
while ((0 < cchLength) && (0 == szSrc[cchLength - 1]))
|
|
cchLength -= 1;
|
|
}
|
|
|
|
#ifndef UNICODE
|
|
if (0 == *szSrc)
|
|
cchLength = 1;
|
|
else if (NULL == szDst)
|
|
{
|
|
cchLength =
|
|
MultiByteToWideChar(
|
|
GetACP(),
|
|
MB_PRECOMPOSED | MB_USEGLYPHCHARS,
|
|
szSrc,
|
|
cchLength,
|
|
NULL,
|
|
0);
|
|
if (0 == cchLength)
|
|
throw GetLastError();
|
|
cchLength += 1;
|
|
}
|
|
else
|
|
{
|
|
cchLength =
|
|
MultiByteToWideChar(
|
|
GetACP(),
|
|
MB_PRECOMPOSED | MB_USEGLYPHCHARS,
|
|
szSrc,
|
|
cchLength,
|
|
szDst,
|
|
cchLength);
|
|
if (0 == cchLength)
|
|
throw GetLastError();
|
|
szDst[cchLength++] = 0;
|
|
}
|
|
#else
|
|
cchLength += 1;
|
|
if (NULL != szDst)
|
|
CopyMemory(szDst, szSrc, cchLength * sizeof(TCHAR));
|
|
#endif
|
|
return cchLength;
|
|
}
|
|
|
|
void
|
|
StopMonitorReaders(
|
|
THREADDATA *ThreadData
|
|
)
|
|
{
|
|
DWORD dwRet;
|
|
|
|
_ASSERT(ThreadData != NULL);
|
|
|
|
SetEvent(ThreadData->hClose);
|
|
|
|
if (ThreadData->hSCardContext) {
|
|
|
|
SCardCancel(ThreadData->hSCardContext);
|
|
}
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
StartMonitorReaders(
|
|
LPVOID lpParameter
|
|
)
|
|
{
|
|
LPCTSTR szCardHandled = "KS";
|
|
LPCTSTR newPnPReader = SCPNP_NOTIFICATION;
|
|
THREADDATA *ThreadData = (THREADDATA *) lpParameter;
|
|
HANDLE hCalaisStarted = NULL;
|
|
HANDLE lHandles[2];
|
|
|
|
//
|
|
// We use this outer loop to restart in case the
|
|
// resource manager was stopped
|
|
//
|
|
while (WaitForSingleObject(ThreadData->hClose, 0) == WAIT_TIMEOUT) {
|
|
|
|
// Acquire context with resource manager
|
|
LONG lReturn = SCardEstablishContext(
|
|
SCARD_SCOPE_SYSTEM,
|
|
NULL,
|
|
NULL,
|
|
&ThreadData->hSCardContext
|
|
);
|
|
|
|
if (SCARD_S_SUCCESS != lReturn) {
|
|
|
|
if (SCARD_E_NO_SERVICE == lReturn) {
|
|
|
|
// SCRM not started yet. Give it a chance
|
|
hCalaisStarted = CalaisAccessStartedEvent();
|
|
|
|
if (hCalaisStarted == NULL) {
|
|
|
|
// no way to recover
|
|
break;
|
|
}
|
|
|
|
lHandles[0] = hCalaisStarted;
|
|
lHandles[1] = ThreadData->hClose;
|
|
|
|
lReturn = WaitForMultipleObjectsEx(
|
|
2,
|
|
lHandles,
|
|
FALSE,
|
|
120 * 1000, // only couple minutes
|
|
FALSE
|
|
);
|
|
|
|
if (lReturn != WAIT_OBJECT_0) {
|
|
|
|
// We stop if an error occured or if the user logs out
|
|
break;
|
|
}
|
|
|
|
// Otherwise the resource manager has started
|
|
DebugPrint(("ScCertProp: Smart card resource manager started\n"));
|
|
continue;
|
|
}
|
|
|
|
// The prev. call should never fail
|
|
// It's better to termninate this thread.
|
|
break;
|
|
}
|
|
|
|
LPCTSTR szReaderName = NULL;
|
|
DWORD dwAutoAllocate = SCARD_AUTOALLOCATE;
|
|
// now list the available readers
|
|
lReturn = SCardListReaders(
|
|
ThreadData->hSCardContext,
|
|
SCARD_DEFAULT_READERS,
|
|
(LPTSTR)&szReaderName,
|
|
&dwAutoAllocate
|
|
);
|
|
|
|
SCARD_READERSTATE rgReaders[MAXIMUM_SMARTCARD_READERS + 1];
|
|
// First, make sure the array is totally zeroed.
|
|
ZeroMemory((LPVOID) rgReaders, sizeof(rgReaders));
|
|
|
|
// add the 'new pnp reader has arrived' reader
|
|
rgReaders[0].szReader = newPnPReader;
|
|
rgReaders[0].dwCurrentState = 0;
|
|
DWORD dwNumReaders = 1;
|
|
|
|
// And build a bare-bones readerstatearray IFF there are any readers
|
|
if (SCARD_S_SUCCESS == lReturn)
|
|
{
|
|
szReaderName = FirstString( szReaderName );
|
|
|
|
while (NULL != szReaderName &&
|
|
dwNumReaders < MAXIMUM_SMARTCARD_READERS + 1) {
|
|
|
|
DebugPrint(
|
|
("ScCertProp: Found reader: '%s'\n",
|
|
szReaderName)
|
|
);
|
|
|
|
rgReaders[dwNumReaders].szReader = (LPCTSTR) szReaderName;
|
|
rgReaders[dwNumReaders].dwCurrentState = SCARD_STATE_EMPTY;
|
|
|
|
szReaderName = NextString(szReaderName);
|
|
dwNumReaders++;
|
|
}
|
|
}
|
|
|
|
if (SCARD_S_SUCCESS != lReturn) {
|
|
|
|
DebugPrint(("ScCertProp: No readers found. Waiting for new reader arrival...\n"));
|
|
//
|
|
// We now will call SCardGetStatusChange which
|
|
// will return upon new reader arrival
|
|
//
|
|
}
|
|
|
|
BOOL fNewReader = FALSE;
|
|
|
|
// analyze newly inserted cards and prop certs as necessary
|
|
while (WaitForSingleObject(ThreadData->hClose, 0) == WAIT_TIMEOUT &&
|
|
fNewReader == FALSE) {
|
|
|
|
//
|
|
// Wait for a change in the system status before continuing; quit if an error occurred
|
|
//
|
|
lReturn = SCardGetStatusChange(
|
|
ThreadData->hSCardContext,
|
|
INFINITE,
|
|
rgReaders,
|
|
dwNumReaders
|
|
);
|
|
|
|
#ifdef DEBUG_VERBOSE
|
|
DebugPrint(
|
|
("ScCertProp: SCardGetStatusChange returned %lx\n",
|
|
lReturn)
|
|
);
|
|
#endif
|
|
|
|
if (SCARD_E_SYSTEM_CANCELLED == lReturn) {
|
|
|
|
DebugPrint(("ScCertProp: Smart card resource manager stopped\n"));
|
|
|
|
// Clean up
|
|
if (NULL != szReaderName)
|
|
{
|
|
SCardFreeMemory(ThreadData->hSCardContext, (PVOID)szReaderName);
|
|
szReaderName = NULL;
|
|
}
|
|
if (NULL != ThreadData->hSCardContext)
|
|
{
|
|
SCardReleaseContext(ThreadData->hSCardContext);
|
|
ThreadData->hSCardContext = NULL;
|
|
}
|
|
|
|
//
|
|
// The resource manager has been stopped.
|
|
// Wait until it restarted or until the user logs out
|
|
//
|
|
hCalaisStarted = CalaisAccessStartedEvent();
|
|
|
|
if (hCalaisStarted == NULL) {
|
|
|
|
// no way to recover. stop cert prop
|
|
StopMonitorReaders(ThreadData);
|
|
break;
|
|
}
|
|
|
|
lHandles[0] = hCalaisStarted;
|
|
lHandles[1] = ThreadData->hClose;
|
|
|
|
lReturn = WaitForMultipleObjectsEx(
|
|
2,
|
|
lHandles,
|
|
FALSE,
|
|
INFINITE,
|
|
FALSE
|
|
);
|
|
|
|
if (lReturn != WAIT_OBJECT_0) {
|
|
|
|
// We stop if an error occured or if the user logs out
|
|
StopMonitorReaders(ThreadData);
|
|
break;
|
|
}
|
|
|
|
// Otherwise the resource manager has been restarted
|
|
DebugPrint(("ScCertProp: Smart card resource manager re-started\n"));
|
|
break;
|
|
}
|
|
|
|
if (SCARD_S_SUCCESS != lReturn)
|
|
{
|
|
if (SCARD_E_CANCELLED != lReturn)
|
|
StopMonitorReaders(ThreadData);
|
|
break;
|
|
}
|
|
|
|
#ifdef DEBUG_VERBOSE
|
|
DebugPrint(
|
|
("ScCertProp: Reader(PnP) state %lx/%lx\n",
|
|
rgReaders[0].dwCurrentState,
|
|
rgReaders[0].dwEventState)
|
|
);
|
|
#endif
|
|
// check if a new reader showed up
|
|
if ((dwNumReaders == 1 || rgReaders[0].dwCurrentState != 0) &&
|
|
rgReaders[0].dwEventState & SCARD_STATE_CHANGED) {
|
|
|
|
DebugPrint(("ScCertProp: New reader reported...\n"));
|
|
fNewReader = TRUE;
|
|
break;
|
|
}
|
|
|
|
rgReaders[0].dwCurrentState = rgReaders[0].dwEventState;
|
|
|
|
//
|
|
// Enumerate the readers and for every recognized card that's been
|
|
// inserted and has an associated CSP, get a cert (if there is one)
|
|
// off the default container and prop it to the 'My' store
|
|
//
|
|
for (DWORD dwIndex = 1; dwIndex < dwNumReaders; dwIndex++)
|
|
{
|
|
#ifdef DEBUG_VERBOSE
|
|
DebugPrint(
|
|
("ScCertProp: Reader(%s) state %lx/%lx\n",
|
|
rgReaders[dwIndex].szReader,
|
|
rgReaders[dwIndex].dwCurrentState,
|
|
rgReaders[dwIndex].dwEventState)
|
|
);
|
|
#endif
|
|
|
|
if ((rgReaders[dwIndex].dwCurrentState & SCARD_STATE_EMPTY) &&
|
|
(rgReaders[dwIndex].dwEventState & SCARD_STATE_PRESENT)) {
|
|
|
|
// play sound for card insertion
|
|
PlaySound(
|
|
TEXT("SmartcardInsertion"),
|
|
NULL,
|
|
SND_ASYNC | SND_ALIAS | SND_NODEFAULT
|
|
);
|
|
}
|
|
|
|
if ((rgReaders[dwIndex].dwCurrentState & SCARD_STATE_PRESENT) &&
|
|
(rgReaders[dwIndex].dwEventState & SCARD_STATE_EMPTY)) {
|
|
|
|
// play sound for card removal
|
|
PlaySound(
|
|
TEXT("SmartcardRemoval"),
|
|
NULL,
|
|
SND_ASYNC | SND_ALIAS | SND_NODEFAULT
|
|
);
|
|
}
|
|
|
|
if ((rgReaders[dwIndex].dwCurrentState & SCARD_STATE_EMPTY) &&
|
|
(rgReaders[dwIndex].dwEventState & SCARD_STATE_PRESENT) &&
|
|
(rgReaders[dwIndex].dwEventState & SCARD_STATE_CHANGED) &&
|
|
(rgReaders[dwIndex].pvUserData != (PVOID) szCardHandled))
|
|
{
|
|
|
|
// Get the name of the card
|
|
LPCSTR szCardName = NULL;
|
|
dwAutoAllocate = SCARD_AUTOALLOCATE;
|
|
lReturn = SCardListCards(
|
|
ThreadData->hSCardContext,
|
|
rgReaders[dwIndex].rgbAtr,
|
|
NULL,
|
|
0,
|
|
(LPTSTR)&szCardName,
|
|
&dwAutoAllocate
|
|
);
|
|
|
|
LPCSTR szCSPName = NULL;
|
|
if (SCARD_S_SUCCESS == lReturn)
|
|
{
|
|
dwAutoAllocate = SCARD_AUTOALLOCATE;
|
|
lReturn = SCardGetCardTypeProviderName(
|
|
ThreadData->hSCardContext,
|
|
szCardName,
|
|
SCARD_PROVIDER_CSP,
|
|
(LPTSTR)&szCSPName,
|
|
&dwAutoAllocate
|
|
);
|
|
}
|
|
|
|
DebugPrint(
|
|
("ScCertProp: Smart Card '%s' inserted into reader '%s'\n",
|
|
(strlen(szCardName) ? szCardName : "<Unknown>"),
|
|
rgReaders[dwIndex].szReader)
|
|
);
|
|
|
|
if (SCARD_S_SUCCESS == lReturn)
|
|
{
|
|
PPROPDATA PropData = (PPROPDATA) LocalAlloc(LPTR, sizeof(PROPDATA));
|
|
if (PropData) {
|
|
|
|
_tcscpy(PropData->szReader, rgReaders[dwIndex].szReader);
|
|
_tcscpy(PropData->szCardName, szCardName);
|
|
_tcscpy(PropData->szCSPName, szCSPName);
|
|
PropData->hUserToken = ThreadData->hUserToken;
|
|
|
|
//
|
|
// Create a thread to propagate this cert.
|
|
// The thread is responsible to free PropData
|
|
//
|
|
HANDLE hThread = CreateThread(
|
|
NULL,
|
|
0,
|
|
PropagateCertificates,
|
|
(LPVOID) PropData,
|
|
0,
|
|
NULL
|
|
);
|
|
|
|
if (hThread == NULL) {
|
|
|
|
LocalFree(PropData);
|
|
}
|
|
else {
|
|
|
|
CloseHandle(hThread);
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
//
|
|
// Clean up
|
|
//
|
|
if (NULL != szCSPName)
|
|
{
|
|
SCardFreeMemory(ThreadData->hSCardContext, (PVOID)szCSPName);
|
|
szCSPName = NULL;
|
|
}
|
|
if (NULL != szCardName)
|
|
{
|
|
SCardFreeMemory(ThreadData->hSCardContext, (PVOID)szCardName);
|
|
szCardName = NULL;
|
|
}
|
|
|
|
// Record that we're done with this card
|
|
rgReaders[dwIndex].pvUserData = (PVOID) szCardHandled;
|
|
|
|
}
|
|
else
|
|
{
|
|
// there's no card to handle in this reader; reset pvUserData
|
|
rgReaders[dwIndex].pvUserData = NULL;
|
|
}
|
|
|
|
// Update the "current state" of this reader
|
|
rgReaders[dwIndex].dwCurrentState = rgReaders[dwIndex].dwEventState;
|
|
}
|
|
}
|
|
|
|
// Clean up
|
|
if (NULL != szReaderName)
|
|
{
|
|
SCardFreeMemory(ThreadData->hSCardContext, (PVOID)szReaderName);
|
|
szReaderName = NULL;
|
|
}
|
|
if (NULL != ThreadData->hSCardContext)
|
|
{
|
|
SCardReleaseContext(ThreadData->hSCardContext);
|
|
ThreadData->hSCardContext = NULL;
|
|
}
|
|
}
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
DWORD
|
|
WINAPI
|
|
PropagateCertificates(
|
|
LPVOID lpParameter
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
This function propagates the cert of a card.
|
|
It runs as a seperate thread
|
|
|
|
--*/
|
|
{
|
|
PPROPDATA PropData = (PPROPDATA) lpParameter;
|
|
BOOL fSts = FALSE;
|
|
long lErr = 0;
|
|
DWORD dwIndex = 0;
|
|
|
|
DWORD cbContainerName = 0;
|
|
LPSTR szContainerName = NULL;
|
|
LPWSTR lpwszContainerName = NULL;
|
|
LPWSTR lpwszCSPName = NULL;
|
|
LPWSTR lpwszCardName = NULL;
|
|
LPSTR lpszContainerName = NULL;
|
|
DWORD dwCertLen = 0;
|
|
LPBYTE pbCert = NULL;
|
|
|
|
CRYPT_KEY_PROV_INFO keyProvInfo;
|
|
HCERTSTORE hCertStore = NULL;
|
|
HCRYPTKEY hKey = NULL;
|
|
HCRYPTPROV hCryptProv = NULL;
|
|
|
|
LPCTSTR szCSPName = PropData->szCSPName;
|
|
LPCTSTR szCardName = PropData->szCardName;
|
|
|
|
static const DWORD rgdwKeys[] = { AT_KEYEXCHANGE , AT_SIGNATURE };
|
|
static const DWORD cdwKeys = sizeof(rgdwKeys) / sizeof(DWORD);
|
|
|
|
#if defined(DBG) || defined(DEBUG)
|
|
time_t start = time(NULL);
|
|
#endif
|
|
|
|
lpszContainerName = (LPSTR) LocalAlloc(
|
|
LPTR,
|
|
strlen(PropData->szReader) + 10
|
|
);
|
|
|
|
if (lpszContainerName == NULL) {
|
|
|
|
lErr = NTE_NO_MEMORY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
sprintf(lpszContainerName, "\\\\.\\%s\\", PropData->szReader);
|
|
|
|
fSts = CryptAcquireContext(
|
|
&hCryptProv,
|
|
lpszContainerName,
|
|
PropData->szCSPName,
|
|
PROV_RSA_FULL,
|
|
CRYPT_SILENT
|
|
);
|
|
|
|
DebugPrint(
|
|
("ScCertProp(%s): CryptAcquireContext took %ld seconds to return %lx\n",
|
|
PropData->szCardName,
|
|
(time(NULL) - start),
|
|
GetLastError())
|
|
);
|
|
|
|
LocalFree(lpszContainerName);
|
|
|
|
if (fSts == FALSE) {
|
|
|
|
lErr = GetLastError();
|
|
goto ErrorExit;
|
|
}
|
|
|
|
// the following struct is always empty, for I_CryptAddSmartCardCertToStore
|
|
CRYPT_DATA_BLOB scCryptData;
|
|
memset(&scCryptData, 0, sizeof(CRYPT_DATA_BLOB));
|
|
|
|
//
|
|
// Get the default container name, so we can use it
|
|
//
|
|
fSts = CryptGetProvParam(
|
|
hCryptProv,
|
|
PP_CONTAINER,
|
|
NULL,
|
|
&cbContainerName,
|
|
0
|
|
);
|
|
|
|
if (!fSts)
|
|
{
|
|
lErr = GetLastError();
|
|
goto ErrorExit;
|
|
}
|
|
|
|
szContainerName = (LPSTR) LocalAlloc(LPTR, cbContainerName);
|
|
if (NULL == szContainerName)
|
|
{
|
|
lErr = NTE_NO_MEMORY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
fSts = CryptGetProvParam(
|
|
hCryptProv,
|
|
PP_CONTAINER,
|
|
(PBYTE)szContainerName,
|
|
&cbContainerName,
|
|
0
|
|
);
|
|
|
|
if (!fSts)
|
|
{
|
|
lErr = GetLastError();
|
|
goto ErrorExit;
|
|
}
|
|
|
|
//
|
|
// Prepare the key prov info that's generic to all keysets
|
|
//
|
|
lpwszContainerName = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * (lstrlen(szContainerName) + 1));
|
|
lpwszCSPName = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * (lstrlen(szCSPName) + 1));
|
|
lpwszCardName = (LPWSTR)LocalAlloc(LPTR, sizeof(WCHAR) * (lstrlen(szCardName) + 1));
|
|
if ((NULL == lpwszCSPName) || (NULL == lpwszCardName) || (NULL == lpwszContainerName))
|
|
{
|
|
lErr = NTE_NO_MEMORY;
|
|
goto ErrorExit;
|
|
}
|
|
|
|
MoveToUnicodeString(lpwszContainerName, szContainerName, lstrlen(szContainerName) + 1);
|
|
MoveToUnicodeString(lpwszCSPName, szCSPName, lstrlen(szCSPName) + 1);
|
|
MoveToUnicodeString(lpwszCardName, szCardName, lstrlen(szCardName) + 1);
|
|
|
|
memset(&keyProvInfo, 0, sizeof(CRYPT_KEY_PROV_INFO));
|
|
keyProvInfo.pwszContainerName = lpwszContainerName;
|
|
keyProvInfo.pwszProvName = lpwszCSPName;
|
|
keyProvInfo.dwProvType = PROV_RSA_FULL;
|
|
keyProvInfo.dwFlags = 0;
|
|
keyProvInfo.cProvParam = 0;
|
|
keyProvInfo.rgProvParam = NULL;
|
|
|
|
//
|
|
// For each keyset in this container, get the cert and make
|
|
// a cert context, then prop the cert to the My store
|
|
//
|
|
for (dwIndex = 0; dwIndex < cdwKeys; dwIndex += 1)
|
|
{
|
|
try
|
|
{
|
|
//
|
|
// Note which key we're working with and get the key handle.
|
|
//
|
|
keyProvInfo.dwKeySpec = rgdwKeys[dwIndex];
|
|
|
|
fSts = CryptGetUserKey(
|
|
hCryptProv,
|
|
rgdwKeys[dwIndex],
|
|
&hKey
|
|
);
|
|
|
|
if (!fSts)
|
|
{
|
|
lErr = GetLastError();
|
|
if (NTE_NO_KEY != lErr)
|
|
{
|
|
throw lErr;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Upload the certificate & prep CertData blob
|
|
//
|
|
if (fSts)
|
|
{
|
|
fSts = CryptGetKeyParam(
|
|
hKey,
|
|
KP_CERTIFICATE,
|
|
NULL,
|
|
&dwCertLen,
|
|
0
|
|
);
|
|
|
|
if (!fSts)
|
|
{
|
|
lErr = GetLastError();
|
|
if (ERROR_MORE_DATA == lErr)
|
|
{
|
|
// There's a certificate -- this means SUCCESS!
|
|
fSts = TRUE;
|
|
}
|
|
//
|
|
// otherwise, there may be a key but no cert
|
|
// this just means that there's nothing to do.
|
|
//
|
|
}
|
|
}
|
|
|
|
if (!fSts) {
|
|
|
|
DebugPrint(
|
|
("ScCertProp(%s): No %s certificate on card\n",
|
|
PropData->szCardName,
|
|
(dwIndex == 0 ? "key exchange" : "signature"))
|
|
);
|
|
}
|
|
|
|
if (fSts)
|
|
{
|
|
pbCert = (LPBYTE) LocalAlloc(LPTR, dwCertLen);
|
|
if (NULL == pbCert)
|
|
{
|
|
throw ERROR_OUTOFMEMORY;
|
|
}
|
|
|
|
fSts = CryptGetKeyParam(
|
|
hKey,
|
|
KP_CERTIFICATE,
|
|
pbCert,
|
|
&dwCertLen,
|
|
0
|
|
);
|
|
|
|
if (!fSts)
|
|
{
|
|
throw (long) GetLastError();
|
|
}
|
|
}
|
|
|
|
if (fSts)
|
|
{
|
|
CRYPT_DATA_BLOB cdbCertData;
|
|
cdbCertData.cbData = dwCertLen;
|
|
cdbCertData.pbData = pbCert;
|
|
|
|
if (PropData->hUserToken && !ImpersonateLoggedOnUser( PropData->hUserToken )) {
|
|
|
|
DebugPrint(("ScCertProp: ImpersonateLoggedOnUser failed\n"));
|
|
throw (long) GetLastError();
|
|
}
|
|
|
|
try
|
|
{
|
|
//
|
|
// Open the MyStore -- and add the cert if it's not there
|
|
//
|
|
hCertStore = CertOpenStore(
|
|
CERT_STORE_PROV_SYSTEM_W, // "My store"
|
|
0, // not applicable
|
|
hCryptProv,
|
|
CERT_STORE_NO_CRYPT_RELEASE_FLAG |
|
|
CERT_SYSTEM_STORE_CURRENT_USER,
|
|
L"MY"
|
|
);
|
|
|
|
if (NULL == hCertStore)
|
|
{
|
|
throw((long)GetLastError());
|
|
}
|
|
// is the cert already there?
|
|
SMART_CARD_CERT_FIND_DATA sccfd;
|
|
memset(&sccfd, 0, sizeof(SMART_CARD_CERT_FIND_DATA));
|
|
sccfd.cbSize = sizeof(SMART_CARD_CERT_FIND_DATA);
|
|
sccfd.pwszProvider = keyProvInfo.pwszProvName;
|
|
sccfd.dwProviderType = keyProvInfo.dwProvType;
|
|
sccfd.pwszContainer = keyProvInfo.pwszContainerName;
|
|
sccfd.dwKeySpec = keyProvInfo.dwKeySpec;
|
|
|
|
PCCERT_CONTEXT pCCtx = NULL;
|
|
pCCtx = I_CryptFindSmartCardCertInStore (
|
|
hCertStore,
|
|
pCCtx,
|
|
&sccfd,
|
|
NULL
|
|
);
|
|
|
|
BOOL fSame = FALSE;
|
|
|
|
if (pCCtx != NULL)
|
|
{
|
|
if ((pCCtx->cbCertEncoded == dwCertLen) &&
|
|
(memcmp(pCCtx->pbCertEncoded, pbCert, dwCertLen) == 0))
|
|
{
|
|
fSame = TRUE;
|
|
}
|
|
|
|
CertFreeCertificateContext(pCCtx);
|
|
pCCtx = NULL;
|
|
|
|
DebugPrint(
|
|
("ScCertProp(%s): %s certificate already in store\n",
|
|
PropData->szCardName,
|
|
(dwIndex == 0 ? "Key exchange" : "Signature"))
|
|
);
|
|
|
|
fSts = TRUE;
|
|
}
|
|
|
|
if (!fSame)
|
|
{
|
|
// this by default does "replace existing"
|
|
fSts = I_CryptAddSmartCardCertToStore(
|
|
hCertStore,
|
|
&cdbCertData,
|
|
NULL,
|
|
&scCryptData,
|
|
&keyProvInfo
|
|
);
|
|
|
|
DebugPrint(
|
|
("ScCertProp(%s): %s certificate %s propagated\n",
|
|
PropData->szCardName,
|
|
(dwIndex == 0 ? "Key exchange" : "Signature"),
|
|
(fSts ? "successfully" : "NOT"))
|
|
);
|
|
|
|
}
|
|
|
|
if (NULL != hCertStore)
|
|
{
|
|
CertCloseStore(hCertStore, CERT_CLOSE_STORE_FORCE_FLAG);
|
|
hCertStore = NULL;
|
|
}
|
|
if (!fSts)
|
|
{
|
|
throw((long) GetLastError());
|
|
}
|
|
RevertToSelf();
|
|
}
|
|
catch(...)
|
|
{
|
|
RevertToSelf();
|
|
throw;
|
|
}
|
|
}
|
|
}
|
|
catch (...)
|
|
{
|
|
}
|
|
|
|
// clean up each time around...
|
|
|
|
if (NULL != hKey)
|
|
{
|
|
CryptDestroyKey(hKey);
|
|
hKey = NULL;
|
|
}
|
|
if (NULL != pbCert)
|
|
{
|
|
LocalFree(pbCert);
|
|
pbCert = NULL;
|
|
dwCertLen = 0;
|
|
}
|
|
}
|
|
|
|
ErrorExit:
|
|
|
|
if (NULL != szContainerName)
|
|
{
|
|
LocalFree(szContainerName);
|
|
}
|
|
if (NULL != lpwszContainerName)
|
|
{
|
|
LocalFree(lpwszContainerName);
|
|
}
|
|
if (NULL != lpwszCSPName)
|
|
{
|
|
LocalFree(lpwszCSPName);
|
|
}
|
|
if (NULL != lpwszCardName)
|
|
{
|
|
LocalFree(lpwszCardName);
|
|
}
|
|
if (NULL != pbCert)
|
|
{
|
|
LocalFree(pbCert);
|
|
}
|
|
if (NULL != hKey)
|
|
{
|
|
CryptDestroyKey(hKey);
|
|
}
|
|
if (NULL != hCryptProv)
|
|
{
|
|
CryptReleaseContext(hCryptProv, 0);
|
|
}
|
|
|
|
DebugPrint(
|
|
("ScCertProp(%s): Certificate propagation took %ld seconds\n",
|
|
PropData->szCardName,
|
|
(time(NULL) - start))
|
|
);
|
|
|
|
LocalFree(PropData);
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD WINAPI
|
|
SCardStartCertProp(
|
|
LPVOID lpvParam
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Starts cert. propagation after the user logged on.
|
|
|
|
--*/
|
|
{
|
|
PWLX_NOTIFICATION_INFO User = (PWLX_NOTIFICATION_INFO) lpvParam;
|
|
HKEY hKey;
|
|
DWORD fEnabled = TRUE;
|
|
|
|
//
|
|
// First check if cert. prop. is enabled.
|
|
//
|
|
if (RegOpenKey(
|
|
HKEY_LOCAL_MACHINE,
|
|
REG_KEY,
|
|
&hKey) == ERROR_SUCCESS) {
|
|
|
|
ULONG uBufferLen = sizeof(fEnabled);
|
|
DWORD dwKeyType;
|
|
|
|
RegQueryValueEx(
|
|
hKey,
|
|
"Enabled",
|
|
NULL,
|
|
&dwKeyType,
|
|
(PUCHAR) &fEnabled,
|
|
&uBufferLen);
|
|
|
|
RegCloseKey(hKey);
|
|
}
|
|
|
|
if (FALSE == fEnabled) {
|
|
|
|
DebugPrint(("ScCertProp: Smart card certificate propagation is disabled\n"));
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
__try {
|
|
|
|
if(User) {
|
|
|
|
l_ThreadData.hUserToken = User->hToken;
|
|
}
|
|
|
|
l_ThreadData.hClose = CreateEvent(
|
|
NULL,
|
|
TRUE,
|
|
FALSE,
|
|
NULL
|
|
);
|
|
|
|
if (l_ThreadData.hClose == NULL) {
|
|
|
|
__leave;
|
|
}
|
|
|
|
l_ThreadData.hThread = CreateThread(
|
|
NULL,
|
|
0,
|
|
StartMonitorReaders,
|
|
(LPVOID) &l_ThreadData,
|
|
CREATE_SUSPENDED,
|
|
NULL
|
|
);
|
|
|
|
if (l_ThreadData.hThread == NULL) {
|
|
|
|
CloseHandle(l_ThreadData.hClose);
|
|
__leave;
|
|
}
|
|
|
|
l_ThreadData.fSuspended = FALSE; // was initially suspended, not anymore
|
|
|
|
ResumeThread(l_ThreadData.hThread);
|
|
|
|
DebugPrint(("ScCertProp: Smart card certificate propagation started\n"));
|
|
}
|
|
|
|
__finally {
|
|
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD WINAPI
|
|
SCardStopCertProp(
|
|
LPVOID lpvParam
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Stops cert. propagation when the user logs out.
|
|
|
|
Arguments:
|
|
lpvParam - Winlogon notification info.
|
|
|
|
--*/
|
|
{
|
|
UNREFERENCED_PARAMETER(lpvParam);
|
|
|
|
if(NULL != l_ThreadData.hThread)
|
|
{
|
|
DWORD dwStatus;
|
|
|
|
// If the user that unlocks the workstation is different from the one
|
|
// that locked it, Logoff occurs without an Unlock, thus the thread is
|
|
// still suspended. This should take care of it.
|
|
if (l_ThreadData.fSuspended)
|
|
{
|
|
ResumeThread(l_ThreadData.hThread);
|
|
l_ThreadData.fSuspended = FALSE;
|
|
}
|
|
|
|
StopMonitorReaders(&l_ThreadData);
|
|
dwStatus = WaitForSingleObject(
|
|
l_ThreadData.hThread,
|
|
INFINITE
|
|
);
|
|
_ASSERT(dwStatus == WAIT_OBJECT_0);
|
|
CloseHandle(l_ThreadData.hClose);
|
|
l_ThreadData.hClose = NULL;
|
|
CloseHandle(l_ThreadData.hThread);
|
|
l_ThreadData.hThread = NULL;
|
|
DebugPrint(("ScCertProp: Smart card certificate propagation stopped\n"));
|
|
}
|
|
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD WINAPI
|
|
SCardSuspendCertProp(
|
|
LPVOID lpvParam
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Suspens cert. propagation when the workstation will be locked
|
|
|
|
Arguments:
|
|
lpvParam - Winlogon notification info.
|
|
|
|
--*/
|
|
{
|
|
UNREFERENCED_PARAMETER(lpvParam);
|
|
|
|
// The suspended flag should take care of the following scenario:
|
|
// Winlogon generates lock notification each time the locked dialog appears
|
|
// (vs. only once when the wks is locked) and the thread would get suspended
|
|
// amny times. This happens when the screen saver is kicking on & off while
|
|
// the wks is locked (Bug 105852)
|
|
if ((NULL != l_ThreadData.hThread) && (!l_ThreadData.fSuspended)){
|
|
|
|
SuspendThread(l_ThreadData.hThread);
|
|
l_ThreadData.fSuspended = TRUE;
|
|
DebugPrint(("ScCertProp: Smart card certificate propagation suspended\n"));
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD WINAPI
|
|
SCardResumeCertProp(
|
|
LPVOID lpvParam
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Resumes cert. propagation after unlocking the workstation
|
|
|
|
Arguments:
|
|
lpvParam - Winlogon notification info.
|
|
|
|
--*/
|
|
{
|
|
UNREFERENCED_PARAMETER(lpvParam);
|
|
|
|
if (NULL != l_ThreadData.hThread) {
|
|
|
|
ResumeThread(l_ThreadData.hThread);
|
|
l_ThreadData.fSuspended = FALSE;
|
|
DebugPrint(("ScCertProp: Smart card certificate propagation resumed\n"));
|
|
}
|
|
return ERROR_SUCCESS;
|
|
}
|
|
|
|
DWORD WINAPI
|
|
SCardEnableCertProp(
|
|
BOOL On
|
|
)
|
|
/*++
|
|
|
|
Routine Description:
|
|
Allows cert. propagation to be turned off/on
|
|
|
|
Arguments:
|
|
On - TRUE turn on, else off
|
|
|
|
--*/
|
|
{
|
|
HKEY l_hKey;
|
|
LONG l_lResult;
|
|
|
|
if ((l_lResult = RegOpenKey(
|
|
HKEY_LOCAL_MACHINE,
|
|
REG_KEY,
|
|
&l_hKey)) == ERROR_SUCCESS) {
|
|
|
|
l_lResult = RegSetValueEx(
|
|
l_hKey,
|
|
"Enabled",
|
|
0,
|
|
REG_DWORD,
|
|
(PUCHAR) &On,
|
|
sizeof(DWORD)
|
|
);
|
|
|
|
RegCloseKey(l_hKey);
|
|
}
|
|
|
|
return l_lResult;
|
|
}
|
|
|
|
#ifdef test
|
|
__cdecl
|
|
main(
|
|
int argc,
|
|
char ** argv
|
|
)
|
|
{
|
|
EnableScCertProp(TRUE);
|
|
StartScCertProp(NULL);
|
|
getchar();
|
|
StopScCertProp(&l_ThreadData);
|
|
|
|
return 0;
|
|
}
|
|
#endif
|