CASA/LoginCapture/windows/lcredmgr.cpp
2006-02-01 17:48:29 +00:00

1078 lines
27 KiB
C++

/***********************************************************************
*
* Copyright (C) 2005-2006 Novell, Inc. All Rights Reserved.
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; version 2.1
* of the License.
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this library; if not, Novell, Inc.
*
* To contact Novell about this file by physical or electronic mail,
* you may find current contact information at www.novell.com.
*
***********************************************************************/
/***********************************************************************
* Following are registry entries, which must be created in order
* to run this credential manager;
*
* [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\NetworkProvider\Order
* "ProviderOrder"=LCredMgr,(original string)
* [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LCredMgr\networkprovider
* "Class"=dword:00000002
* "Name"="LoginCapture Credential Provider"
* "ProviderPath"="
*
* Following are registry entries, which must be created in order
* to run this notification dll;
*
* [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify\LCredMgr]
* "Asynchronous"=dword:00000001
* "DLLName"="LCredMgr.dll"
* "Impersonate"=dword:00000001
* "Logoff"="NPLogoff"
*
***********************************************************************/
#include "lcredmgr.h"
#include <stdio.h>
//===[ External data ]=====================================================
//===[ Manifest constants ]================================================
//
// The authentication type is only used here in a Unicode context
//
#define MSV1_0_AUTH_TYPE L"MSV1_0:Interactive"
#define KERB_AUTH_TYPE L"Kerberos:Interactive"
#define WNNC_CRED_MANAGER 0xFFFF0000
#define WINDOWS_LOGON_ID 1
//===[ Type definitions ]==================================================
//===[ Function prototypes ]===============================================
BOOL WriteLogFile(LPTSTR String);
DWORD
APIENTRY
NPGetCaps (
DWORD ndex
);
DWORD
APIENTRY
NPLogonNotify (
PLUID lpLogonId,
LPCWSTR lpAuthentInfoType,
LPVOID lpAuthentInfo,
LPCWSTR lpPreviousAuthentInfoType,
LPVOID lpPreviousAuthentInfo,
LPWSTR lpStationName,
LPVOID StationHandle,
LPWSTR *lpLogonScript
);
DWORD
APIENTRY
NPPasswordChangeNotify (
LPCWSTR lpAuthentInfoType,
LPVOID lpAuthentInfo,
LPCWSTR lpPreviousAuthentInfoType,
LPVOID lpPreviousAuthentInfo,
LPWSTR lpStationName,
LPVOID StationHandle,
DWORD dwChangeInfo
);
//===[ Global variables ]==================================================
HMODULE g_hModule;
HANDLE g_hModuleMutex = NULL;
BOOLEAN g_bLibraryLoaded = FALSE;
HINSTANCE g_hCASALibrary;
PSETCREDENTIAL pCASASetCredential;
POPENCACHE pCASAOpenCache;
PCLOSECACHE pCASACloseCache;
#ifdef _DEBUG
void
DebugPrint
(
char *format,
...
)
{
va_list parameters; /* Pointer to parameters on the stack */
char newString[600] = {"=> LCredMgr: "};
/*
Get a pointer to the parameters
*/
va_start(parameters, format);
vsprintf( &newString[13], format, parameters );
OutputDebugStringA(newString);
}
#endif
//++=======================================================================
BOOL
APIENTRY
DllMain (
HANDLE hInst,
DWORD dwReason,
LPVOID lpReserved
)
//=======================================================================--
{
#ifdef _DEBUG
DebugPrint("DllMain called.\n");
#endif
switch (dwReason)
{
case DLL_PROCESS_ATTACH:
DisableThreadLibraryCalls((HINSTANCE)hInst);
g_hModule = (HMODULE)hInst;
g_hModuleMutex = CreateMutex(NULL, FALSE, NULL);
if (g_hModuleMutex == NULL)
{
return(FALSE);
}
InitLoginExtension();
//
// Always succeed, this allows us to be configured with regsvr32.exe
//
break;
case DLL_THREAD_ATTACH:
case DLL_THREAD_DETACH:
break;
case DLL_PROCESS_DETACH:
CloseHandle(g_hModuleMutex);
break;
}
return TRUE;
}
//++=======================================================================
DWORD
WINAPI
NPGetCaps (
DWORD nIndex
)
//
// PURPOSE: This entry point is called to query the provider. The parameter
// is an index representing the query. For a credential manager
// only the following index values apply:
//
// WNNC_SPEC_VERSION - What version of the provider specification
// was used in developing this provider? The return value is
// the version number.
//
// WNNC_DRIVER_VERSION - The version of the provider.
//
// WNNC_START - Will the provider start? When? The return values
// are:
//
// - 0 : Only return this if the provider will *not* start.
// - Estimated Start time in milliseconds : This is how
// long the provider is expected to take to start.
// - 0xFFFFFFFF : Time to start is unknown.
// - 1 : Provider is already started.
//
// A return value of 0 in other cases indicates that the query
// is not supported.
//=======================================================================--
{
DWORD dwRes;
#ifdef _DEBUG
DebugPrint("NPGetCaps called with nIndex %u.\n", nIndex);
#endif
switch (nIndex)
{
case WNNC_NET_TYPE:
dwRes = WNNC_CRED_MANAGER;
break;
case WNNC_SPEC_VERSION:
dwRes = WNNC_SPEC_VERSION51; // We are using version 5.1 of the spec.
break;
case WNNC_DRIVER_VERSION:
dwRes = 1; // This driver is version 1.
break;
case WNNC_START:
dwRes = 1; // We are already "started"
break;
default:
dwRes = 0; // We don't support anything else
break;
}
#ifdef _DEBUG
DebugPrint("NPGetCaps returning dwRes %u.\n", dwRes);
#endif
return dwRes;
}
//++=======================================================================
BOOLEAN
InitCASALibrary (
)
//
// PURPOSE: Loads the micasa dll entry points.
//=======================================================================--
{
BOOLEAN bLibraryLoaded = TRUE;
if (g_bLibraryLoaded == FALSE)
{
WaitForSingleObjectEx(g_hModuleMutex, INFINITE, FALSE);
bLibraryLoaded = FALSE;
if (g_bLibraryLoaded == FALSE)
{
g_hCASALibrary = LoadLibrary(L"micasa.dll");
if (g_hCASALibrary)
{
pCASASetCredential = (PSETCREDENTIAL)GetProcAddress(g_hCASALibrary, "miCASASetCredential");
pCASAOpenCache = (POPENCACHE)GetProcAddress(g_hCASALibrary, "miCASAOpenSecretStoreCache");
pCASACloseCache = (PCLOSECACHE)GetProcAddress(g_hCASALibrary, "miCASACloseSecretStoreCache");
bLibraryLoaded = TRUE;
g_bLibraryLoaded = TRUE;
}
#ifdef _DEBUG
else
{
DebugPrint("LoadLibrary for micasa.dll failed. %d\n", GetLastError());
}
#endif
}
ReleaseMutex(g_hModuleMutex);
}
return bLibraryLoaded;
}
//++=======================================================================
DWORD
WINAPI
NPLogonNotify (
PLUID lpLogonId,
LPCWSTR lpAuthentInfoType,
LPVOID lpAuthentInfo,
LPCWSTR lpPreviousAuthentInfoType,
LPVOID lpPreviousAuthentInfo,
LPWSTR lpStationName,
LPVOID StationHandle,
LPWSTR *lpLogonScript
)
//
// PURPOSE: This entry point is called when a user logs on. If the user
// authentication fails here, the user will still be logged on
// to the local machine.
//=======================================================================--
{
int ccode;
PMSV1_0_INTERACTIVE_LOGON pAuthInfo;
SSCS_BASIC_CREDENTIAL basicCredential;
SSCS_SECRET_ID_T desktopCredential;
SSCS_SECRET_ID_T domainCredential;
SSCS_EXT_T extension;
BOOLEAN bLibraryLoaded;
#ifdef _DEBUG
DebugPrint("NPLogonNotify called.\n");
#endif
SetLastError(NO_ERROR);
bLibraryLoaded = InitCASALibrary();
if (bLibraryLoaded == FALSE)
{
SetLastError(NO_ERROR);
return NO_ERROR;
}
//
// If the primary authenticator is not MSV1_0, return success.
// Why? Because this is the only auth info structure that we
// understand and we don't want to interact with other types.
//
if (!lstrcmpiW (MSV1_0_AUTH_TYPE, lpAuthentInfoType))
{
#ifdef _DEBUG
DebugPrint("NPLogonNotify auth type is MSV1_0_AUTH_TYPE.\n");
#endif
}
else if (!lstrcmpiW (KERB_AUTH_TYPE, lpAuthentInfoType))
{
#ifdef _DEBUG
DebugPrint("NPLogonNotify auth type is KERB_AUTH_TYPE.\n");
#endif
}
else
{
#ifdef _DEBUG
DebugPrint("NPLogonNotify auth type is UNSUPPORTED.\n");
#endif
SetLastError(NO_ERROR);
return NO_ERROR;
}
if ((wcscmp(L"SvcCtl", lpStationName)) == 0)
{
#ifdef _DEBUG
DebugPrint("NPLogonNotify stationname indicates a service has logged on.\n");
#endif
return NO_ERROR;
}
//
// Do something with the authentication information
//
pAuthInfo = (PMSV1_0_INTERACTIVE_LOGON) lpAuthentInfo;
basicCredential.unLen = WideCharToMultiByte(
CP_UTF8,
0,
pAuthInfo->UserName.Buffer,
-1,
(LPSTR)&basicCredential.username,
NSSCS_MAX_USERID_LEN,
NULL,
NULL);
if (basicCredential.unLen)
{
// basicCredential.unLen++;
basicCredential.pwordLen = WideCharToMultiByte(
CP_UTF8,
0,
pAuthInfo->Password.Buffer,
-1,
(LPSTR)&basicCredential.password,
NSSCS_MAX_PWORD_LEN,
NULL,
NULL);
if (basicCredential.pwordLen)
{
// basicCredential.pwordLen++;
strcpy((char *)&desktopCredential.id, "Desktop");
desktopCredential.len = (long)strlen((char *)&desktopCredential.id) + 1;
basicCredential.unFlags = USERNAME_TYPE_CN_F;
extension.extID = WINDOWS_LOGON_ID;
extension.version = 0x00010000; // 1.0.0
extension.ext = (void *)lpLogonId;
ccode = (*pCASASetCredential)(
0,
&desktopCredential,
NULL,
SSCS_CRED_TYPE_BASIC_F,
&basicCredential,
&extension);
#ifdef _DEBUG
if (!ccode)
{
DebugPrint("NSSCSSetCredential successful!.\n");
}
else
{
DebugPrint("NSSCSSetCredential failed 0x%X\n", ccode);
}
#endif
if (pAuthInfo->LogonDomainName.Length != 0)
{
domainCredential.len = WideCharToMultiByte(
CP_UTF8,
0,
pAuthInfo->LogonDomainName.Buffer,
-1,
(LPSTR)&domainCredential.id,
NSSCS_MAX_SECRET_ID_LEN,
NULL,
NULL);
if (domainCredential.len)
{
// domainCredential.len++;
#ifdef _DEBUG
DebugPrint("Domain exists - [%s], length %d\n", domainCredential.id, domainCredential.len);
#endif
ccode = (*pCASASetCredential)(
0,
&desktopCredential,
&domainCredential,
SSCS_CRED_TYPE_BASIC_F,
&basicCredential,
&extension);
#ifdef _DEBUG
if (!ccode)
{
DebugPrint("NSSCSSetCredential successful!.\n");
}
else
{
DebugPrint("NSSCSSetCredential failed 0x%X\n", ccode);
}
#endif
}
}
}
#ifdef _DEBUG
else
{
DebugPrint("WideCharToMultiByte for password failed.\n");
}
#endif
}
#ifdef _DEBUG
else
{
DebugPrint("WideCharToMultiByte for user name failed.\n");
}
#endif
return NO_ERROR;
}
//++=======================================================================
DWORD
WINAPI
NPPasswordChangeNotify (
LPCWSTR lpAuthentInfoType,
LPVOID lpAuthentInfo,
LPCWSTR lpPreviousAuthentInfoType,
LPVOID lpPreviousAuthentInfo,
LPWSTR lpStationName,
LPVOID StationHandle,
DWORD dwChangeInfo
)
//
// PURPOSE: This function is used to notify a credential manager provider
// of a password change (or, more accurately, an authentication
// information change) for an account.
//=======================================================================--
{
// return NO_ERROR;
#ifdef _DEBUG
DebugPrint("NPPasswordChangeNotify called.\n");
#endif
return WN_NOT_SUPPORTED;
}
//++=======================================================================
VOID
WINAPI
NPLogoff(
PWLX_NOTIFICATION_INFO pInfo
)
//=======================================================================--
{
DWORD dwSize;
TOKEN_STATISTICS tokenStats;
SSCS_SECRETSTORE_T ssId;
HANDLE context;
BOOLEAN bLibraryLoaded;
#ifdef _DEBUG
DebugPrint("NPLogoff called.\n");
#endif
dwSize = sizeof(tokenStats);
if (GetTokenInformation(
pInfo->hToken,
TokenStatistics,
&tokenStats,
dwSize,
&dwSize))
{
#ifdef _DEBUG
DebugPrint("GetTokenInformation for statistics returned LowPart 0x%X HighPart 0x%X\n", tokenStats.AuthenticationId.LowPart, tokenStats.AuthenticationId.HighPart);
#endif
}
#ifdef _DEBUG
else
{
DebugPrint("GetTokenInformation for TokenStatistics failed %u\n", GetLastError());
}
#endif
bLibraryLoaded = TRUE;
WaitForSingleObjectEx(g_hModuleMutex, INFINITE, FALSE);
if (g_bLibraryLoaded == FALSE)
{
#ifdef _DEBUG
DebugPrint("Library not loaded in NPLogoff.\n");
#endif
bLibraryLoaded = FALSE;
}
ReleaseMutex(g_hModuleMutex);
if (bLibraryLoaded == FALSE)
{
return;
}
ssId.version = NSSCS_VERSION_NUMBER;
strcpy((char *)ssId.ssName, (char *)SSCS_DEFAULT_SECRETSTORE_ID);
context = (*pCASAOpenCache)(
&ssId,
0,
NULL);
if (context)
{
//
// the SSFLAG_DESTROY_SESSION_F closes the session for this user
//
(*pCASACloseCache)(
context,
SSFLAG_DESTROY_SESSION_F,
NULL);
}
return;
}
//++=======================================================================
DWORD
AddSubString(
HKEY hKey
)
//=======================================================================--
{
LONG rc;
CHAR szValue[256],
szNewValue[256];
DWORD valueSize;
valueSize = sizeof(szValue);
rc = RegQueryValueExA(
hKey,
"ProviderOrder",
0,
NULL,
(PUCHAR)szValue,
&valueSize);
if (rc == S_OK)
{
strcpy (szNewValue, "LCredMgr,");
strcat (szNewValue, szValue);
rc = RegSetValueExA(
hKey,
"ProviderOrder",
0,
REG_SZ,
(PUCHAR)szNewValue,
(DWORD) strlen(szNewValue));
}
return rc;
}
//++=======================================================================
DWORD
RemoveSubString(
HKEY hKey
)
//=======================================================================--
{
LONG rc;
CHAR szValue[256],
szNewValue[256];
PCHAR subString, startString, endString;
DWORD valueSize;
valueSize = sizeof(szValue);
rc = RegQueryValueExA(
hKey,
"ProviderOrder",
0,
NULL,
(PUCHAR)szValue,
&valueSize);
if (rc == S_OK)
{
//
// Remove any leading whitespace
//
subString = szValue;
while (*subString != NULL && (*subString == ',' || *subString == ' '))
{
subString++;
}
startString = subString;
//
// Check to see if lcredmgr is in the string
//
if ((subString = strstr(startString, "LCredMgr")) != NULL)
{
if (subString == startString)
{
while (*subString != ',' && *subString != NULL)
{
subString++;
}
while ((*subString == ',' || *subString == ' ') && *subString != NULL)
{
subString++;
}
startString = subString;
}
else
{
endString = subString + 8;
//
// Back up past any whitespace or comma's.
//
for (subString--;;subString--)
{
if (*subString != ' ' && *subString != ',')
{
*(subString + 1) = '\0';
break;
}
}
//
// Get past any comma's
//
for (;;endString++)
{
if (*endString == NULL)
{
break;
}
if ((*endString != ',' && *endString != ' '))
{
//
// concat the two strings without lcredmgr
//
strcpy (szNewValue, startString);
strcat (szNewValue, endString);
startString = szNewValue;
break;
}
}
}
rc = RegSetValueExA(
hKey,
"ProviderOrder",
0,
REG_SZ,
(PUCHAR)startString,
(DWORD) strlen(startString));
}
}
return rc;
}
//++=======================================================================
HRESULT
DllInstall(
BOOL bInstall,
LPCWSTR pszCmdLine
)
//=======================================================================--
{
HKEY hKey;
LONG rc;
CHAR szModule[256];
DWORD value, valueSize;
#ifdef _DEBUG
DebugPrint("DllInstall called\n");
OutputDebugStringW(pszCmdLine);
#endif
if (bInstall == TRUE)
{
rc = RegCreateKeyExA(
HKEY_LOCAL_MACHINE,
"System\\CurrentControlSet\\Control\\NetworkProvider\\Order",
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey,
NULL);
if (rc == S_OK)
{
rc = RemoveSubString(hKey);
if (rc == S_OK)
{
rc = AddSubString(hKey);
}
RegCloseKey(hKey);
}
//
// Set up our service key
//
if (rc == S_OK)
{
rc = RegCreateKeyExA(
HKEY_LOCAL_MACHINE,
"System\\CurrentControlSet\\Services\\LCredMgr\\networkprovider",
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey,
NULL);
if (rc == S_OK)
{
value = 2;
rc = RegSetValueExA(
hKey,
"Class",
0,
REG_DWORD,
(BYTE *)&value,
sizeof(value));
if (rc != S_OK)
{
goto ErrorExit;
}
rc = RegSetValueExA(
hKey,
"Name",
0,
REG_SZ,
(BYTE *)"LoginCapture Credential Provider",
32);
if (rc != S_OK)
{
goto ErrorExit;
}
valueSize = GetModuleFileNameA((HMODULE)g_hModule, szModule, sizeof(szModule));
if (valueSize)
{
rc = RegSetValueExA(
hKey,
"ProviderPath",
0,
REG_SZ,
(BYTE *)szModule,
valueSize + 1);
}
else
{
rc = GetLastError();
}
RegCloseKey(hKey);
}
}
if (rc == S_OK)
{
rc = RegCreateKeyExA(
HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Notify\\LCredMgr",
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey,
NULL);
if (rc == S_OK)
{
value = 1;
rc = RegSetValueExA(
hKey,
"Asynchronous",
0,
REG_DWORD,
(BYTE *)&value,
sizeof(value));
if (rc != S_OK)
{
goto ErrorExit;
}
rc = RegSetValueExA(
hKey,
"DLLName",
0,
REG_SZ,
(BYTE *)szModule,
valueSize + 1);
if (rc != S_OK)
{
goto ErrorExit;
}
rc = RegSetValueExA(
hKey,
"Impersonate",
0,
REG_DWORD,
(BYTE *)&value,
sizeof(value));
if (rc != S_OK)
{
goto ErrorExit;
}
rc = RegSetValueExA(
hKey,
"Logoff",
0,
REG_SZ,
(BYTE *)"NPLogoff",
9);
RegCloseKey(hKey);
}
}
if (rc == S_OK)
{
rc = RegCreateKeyExA(
HKEY_LOCAL_MACHINE,
"SOFTWARE\\Novell\\Graphical Login\\NWLGE\\LCredMgr",
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey,
NULL);
if (rc == S_OK)
{
rc = RegSetValueExA(
hKey,
"LoginExtName",
0,
REG_SZ,
(BYTE *)szModule,
valueSize + 1);
if (rc != S_OK)
{
goto ErrorExit;
}
rc = RegSetValueExA(
hKey,
"LoginExtDesc",
0,
REG_SZ,
(BYTE *)"CASA Login Extension",
20);
if (rc != S_OK)
{
goto ErrorExit;
}
value = 0x00008002;
rc = RegSetValueExA(
hKey,
"LoginExtType",
0,
REG_DWORD,
(BYTE *)&value,
sizeof(value));
ErrorExit:
RegCloseKey(hKey);
}
}
}
else
{
rc = RegCreateKeyExA(
HKEY_LOCAL_MACHINE,
"System\\CurrentControlSet\\Control\\NetworkProvider\\Order",
0,
NULL,
REG_OPTION_NON_VOLATILE,
KEY_ALL_ACCESS,
NULL,
&hKey,
NULL);
if (rc == S_OK)
{
RemoveSubString(hKey);
RegCloseKey(hKey);
}
//
// Delete our service key
//
RegDeleteKeyA(
HKEY_LOCAL_MACHINE,
"System\\CurrentControlSet\\Services\\LCredMgr\\Enum");
RegDeleteKeyA(
HKEY_LOCAL_MACHINE,
"System\\CurrentControlSet\\Services\\LCredMgr\\networkprovider");
RegDeleteKeyA(
HKEY_LOCAL_MACHINE,
"System\\CurrentControlSet\\Services\\LCredMgr");
RegDeleteKeyA(
HKEY_LOCAL_MACHINE,
"SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Notify\\LCredMgr");
RegDeleteKeyA(
HKEY_LOCAL_MACHINE,
"SOFTWARE\\Novell\\Graphical Login\\NWLGE\\LCredMgr");
}
#ifdef _DEBUG
if (rc == S_OK)
{
DebugPrint("DllInstall successful.\n");
}
else
{
DebugPrint("DllInstall failed.\n");
}
#endif
return rc;
}
//++=======================================================================
STDAPI
DllRegisterServer(
void
)
//=======================================================================--
{
#ifdef _DEBUG
DebugPrint("DllRegisterServer called\n");
#endif
return S_OK;
}
//++=======================================================================
STDAPI
DllUnregisterServer(
void
)
//=======================================================================--
{
#ifdef _DEBUG
DebugPrint("DllUnregisterServer called\n");
#endif
return S_OK;
}
//=========================================================================
//=========================================================================