/*********************************************************************** * * Copyright (C) 2005-2006 Novell, Inc. * * 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 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, write to the Free * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * * 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 //===[ 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; } //========================================================================= //=========================================================================