/****************************** Module Header ******************************\ * Module Name: logoff.c * * Copyright (c) 1991, Microsoft Corporation * * Implements functions to allow a user to logoff the system. * * History: * 12-05-91 Davidc Created. \***************************************************************************/ #include "precomp.h" #pragma hdrstop #include #include // // Private prototypes // HANDLE ExecLogoffThread( PGLOBALS pGlobals, DWORD Flags ); DWORD LogoffThreadProc( LPVOID Parameter ); BOOL WINAPI ShutdownWaitDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ); BOOL WINAPI ShutdownDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ); BOOL DeleteNetworkConnections( PGLOBALS pGlobals ); BOOLEAN ShutdownThread( VOID ); BOOL DeleteRasConnections( PGLOBALS pGlobals ); BOOL WINAPI DeleteNetConnectionsDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ); BOOL ExitWindowsInProgress = FALSE ; HMODULE hRasApi; // // Bugbug: move to winlogon.h // #define IsShutdown(x) ((x == WLX_SAS_ACTION_SHUTDOWN) || \ (x == WLX_SAS_ACTION_SHUTDOWN_REBOOT) || \ (x == WLX_SAS_ACTION_SHUTDOWN_POWER_OFF) ) /***************************************************************************\ * FUNCTION: InitiateLogOff * * PURPOSE: Starts the procedure of logging off the user. * * RETURNS: DLG_SUCCESS - logoff was initiated successfully. * DLG_FAILURE - failed to initiate logoff. * * HISTORY: * * 12-09-91 Davidc Created. * \***************************************************************************/ int InitiateLogoff( PGLOBALS pGlobals, LONG Flags ) { BOOL IgnoreResult; HANDLE ThreadHandle; HANDLE Handle; PUSER_PROCESS_DATA UserProcessData; DWORD Result; // // If this is a shutdown operation, call ExitWindowsEx from // another thread. // if (Flags & (EWX_SHUTDOWN | EWX_REBOOT | EWX_POWEROFF)) { // // Exec a user thread to call ExitWindows // ThreadHandle = ExecLogoffThread(pGlobals, Flags); if (ThreadHandle == NULL) { DebugLog((DEB_ERROR, "Unable to create logoff thread")); return(DLG_FAILURE); } else { // // We don't need the thread handle // IgnoreResult = CloseHandle(ThreadHandle); ASSERT(IgnoreResult); } Result = DLG_SUCCESS; } else { // // Switch the thread to user context. We don't want // to start another thread to perform logoffs in // case the system is out of memory and unable to // create any more threads. // UserProcessData = &pGlobals->UserProcessData; Handle = ImpersonateUser(UserProcessData, GetCurrentThread()); if (Handle == NULL) { DebugLog((DEB_ERROR, "Failed to set user context on thread!")); } else { // // Let the thread run // if ((pGlobals->UserLoggedOn) && (pGlobals->LastGinaRet != WLX_SAS_ACTION_FORCE_LOGOFF) ) { SetActiveDesktop(&pGlobals->WindowStation, Desktop_Application); } Result = LogoffThreadProc((LPVOID)Flags); } RevertToSelf(); } // // ExitWindowsEx will cause one or more desktop switches to occur, // so we must invalidate our current desktop. // if ( (Flags & EWX_WINLOGON_API_SHUTDOWN) == 0 ) { pGlobals->WindowStation.PreviousDesktop = pGlobals->WindowStation.ActiveDesktop; pGlobals->WindowStation.ActiveDesktop = -1; } // // The reboot thread is off and running. We're finished. // return (Result); } /***************************************************************************\ * FUNCTION: ExecLogoffThread * * PURPOSE: Creates a user thread that calls ExitWindowsEx with the * passed flags. * * RETURNS: TRUE on success, FALSE on failure * * HISTORY: * * 05-05-92 Davidc Created. * \***************************************************************************/ HANDLE ExecLogoffThread( PGLOBALS pGlobals, DWORD Flags ) { HANDLE ThreadHandle; DWORD ThreadId; ThreadHandle = ExecUserThread( pGlobals, LogoffThreadProc, (LPVOID)Flags, 0, // Thread creation flags &ThreadId); if (ThreadHandle == NULL) { DebugLog((DEB_ERROR, "Failed to exec a user logoff thread")); } return (ThreadHandle); } /***************************************************************************\ * FUNCTION: LogoffThreadProc * * PURPOSE: The logoff thread procedure. Calls ExitWindowsEx with passed flags. * * RETURNS: Thread termination code is result of ExitWindowsEx call. * * HISTORY: * * 05-05-92 Davidc Created. * \***************************************************************************/ DWORD LogoffThreadProc( LPVOID Parameter ) { DWORD LogoffFlags = (DWORD)Parameter; BOOL Result = FALSE; // // If this logoff is a result of the InitiateSystemShutdown API, // put up a dialog warning the user. // if ( LogoffFlags & EWX_WINLOGON_API_SHUTDOWN ) { Result = ShutdownThread(); } else { Result = TRUE; } if ( Result ) { // // Enable shutdown privilege if we need it // if (LogoffFlags & (EWX_SHUTDOWN | EWX_REBOOT | EWX_POWEROFF)) { Result = EnablePrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE); if (!Result) { DebugLog((DEB_ERROR, "Logoff thread failed to enable shutdown privilege!\n")); } } // // Call ExitWindowsEx with the passed flags // if (Result) { DebugLog((DEB_TRACE, "Calling ExitWindowsEx(%#x, 0)\n", LogoffFlags)); // // Set global flag indicating an ExitWindows is in progress. // ExitWindowsInProgress = TRUE ; Result = ExitWindowsEx(LogoffFlags, 0); ExitWindowsInProgress = FALSE ; if (!Result) { DebugLog((DEB_ERROR, "Logoff thread call to ExitWindowsEx failed, error = %d\n", GetLastError())); } } } return(Result ? DLG_SUCCESS : DLG_FAILURE); } /***************************************************************************\ * FUNCTION: RebootMachine * * PURPOSE: Calls NtShutdown(Reboot) in current user's context. * * RETURNS: Should never return * * HISTORY: * * 05-09-92 Davidc Created. * \***************************************************************************/ VOID RebootMachine( PGLOBALS pGlobals ) { NTSTATUS Status; BOOL EnableResult, IgnoreResult; HANDLE UserHandle; // // Call windows to have it clear all data from video memory // // GdiEraseMemory(); DebugLog(( DEB_TRACE, "Rebooting machine\n" )); // // Impersonate the user for the shutdown call // UserHandle = ImpersonateUser( &pGlobals->UserProcessData, NULL ); ASSERT(UserHandle != NULL); // // Enable the shutdown privilege // This should always succeed - we are either system or a user who // successfully passed the privilege check in ExitWindowsEx. // EnableResult = EnablePrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE); ASSERT(EnableResult); // // Do the final system shutdown pass (reboot) // Status = NtShutdownSystem(ShutdownReboot); DebugLog((DEB_ERROR, "NtShutdownSystem failed, status = 0x%lx", Status)); ASSERT(NT_SUCCESS(Status)); // Should never get here // // We may get here if system is screwed up. // Try and clean up so they can at least log on again. // IgnoreResult = StopImpersonating(UserHandle); ASSERT(IgnoreResult); } /***************************************************************************\ * FUNCTION: PowerdownMachine * * PURPOSE: Calls NtShutdownSystem(ShutdownPowerOff) in current user's context. * * RETURNS: Should never return * * HISTORY: * * 08-09-93 TakaoK Created. * \***************************************************************************/ VOID PowerdownMachine( PGLOBALS pGlobals ) { NTSTATUS Status; BOOL EnableResult, IgnoreResult; HANDLE UserHandle; DebugLog(( DEB_TRACE, "Powering down machine\n" )); // // Impersonate the user for the shutdown call // UserHandle = ImpersonateUser( &pGlobals->UserProcessData, NULL ); ASSERT(UserHandle != NULL); // // Enable the shutdown privilege // This should always succeed - we are either system or a user who // successfully passed the privilege check in ExitWindowsEx. // EnableResult = EnablePrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE); ASSERT(EnableResult); // // Do the final system shutdown and powerdown pass // Status = NtShutdownSystem(ShutdownPowerOff); DebugLog((DEB_ERROR, "NtPowerdownSystem failed, status = 0x%lx", Status)); ASSERT(NT_SUCCESS(Status)); // Should never get here // // We may get here if system is screwed up. // Try and clean up so they can at least log on again. // IgnoreResult = StopImpersonating(UserHandle); ASSERT(IgnoreResult); } /***************************************************************************\ * FUNCTION: ShutdownMachine * * PURPOSE: Shutsdown and optionally reboots or powers off the machine. * * The shutdown is always done in the logged on user's context. * If no user is logged on then the shutdown happens in system context. * * RETURNS: FALSE if something went wrong, otherwise it never returns. * * HISTORY: * * 05-09-92 Davidc Created. * 10-04-93 Johannec Add poweroff option. * \***************************************************************************/ BOOL ShutdownMachine( PGLOBALS pGlobals, int Flags ) { int Result; HANDLE FoundDialogHandle; HANDLE LoadedDialogHandle = NULL; // // Preload the shutdown dialog so we don't have to fetch it after // the filesystem has been shutdown // FoundDialogHandle = FindResource(NULL, (LPTSTR) MAKEINTRESOURCE(IDD_SHUTDOWN), (LPTSTR) MAKEINTRESOURCE(RT_DIALOG)); if (FoundDialogHandle == NULL) { DebugLog((DEB_ERROR, "Failed to find shutdown dialog resource")); } else { LoadedDialogHandle = LoadResource(NULL, FoundDialogHandle); if (LoadedDialogHandle == NULL) { DebugLog((DEB_ERROR, "Ffailed to load shutdown dialog resource")); } } // // Notify the GINA of shutdown here. // #if DBG if (TEST_FLAG(GinaBreakFlags, BREAK_SHUTDOWN)) { DebugLog((DEB_TRACE, "About to call WlxShutdown(%#x)\n", pGlobals->pGina->pGinaContext)); DebugBreak(); } #endif WlxSetTimeout(pGlobals, 120); (void) pGlobals->pGina->pWlxShutdown(pGlobals->pGina->pGinaContext, Flags); // // If we haven't shut down already (via the Remote shutdown path), then // we start it here, and wait for it to complete. Otherwise, skip straight // down to the cool stuff. // if (pGlobals->WinlogonState != Winsta_Shutdown) { // // Call windows to do the windows part of shutdown // We make this a force operation so it is guaranteed to work // and can not be interrupted. // DebugLog(( DEB_TRACE, "Starting shutdown\n" )); Result = InitiateLogoff(pGlobals, EWX_SHUTDOWN | EWX_FORCE | ((Flags == WLX_SAS_ACTION_SHUTDOWN_REBOOT) ? EWX_REBOOT : 0) | ((Flags == WLX_SAS_ACTION_SHUTDOWN_POWER_OFF) ? EWX_POWEROFF : 0) ); ASSERT(Result == DLG_SUCCESS); // // Put up a dialog box to wait for the shutdown notification // from windows and make the first NtShutdownSystem call. // WlxSetTimeout(pGlobals, TIMEOUT_NONE); Result = WlxDialogBoxParam( pGlobals, pGlobals->hInstance, (LPTSTR)IDD_SHUTDOWN_WAIT, NULL, ShutdownWaitDlgProc, (LONG)pGlobals); } else { // // If we're here, it means that we were shut down from the remote path, // so user has cleaned up, now we have to call NtShutdown to flush out // mm, io, etc. // DebugLog(( DEB_TRACE, "Shutting down kernel\n" )); EnablePrivilege( SE_SHUTDOWN_PRIVILEGE, TRUE ); NtShutdownSystem( ShutdownNoReboot ); EnablePrivilege( SE_SHUTDOWN_PRIVILEGE, FALSE ); } // // if machine has powerdown capability and user want to turn it off, then // we down the system power. // if ( Flags == WLX_SAS_ACTION_SHUTDOWN_POWER_OFF) { PowerdownMachine(pGlobals); } // // If this is a shutdown request, then let the user know they can turn // off the power. Otherwise drop straight through and reboot. // if ( Flags != WLX_SAS_ACTION_SHUTDOWN_REBOOT) { DialogBoxIndirectParam( pGlobals->hInstance, (LPDLGTEMPLATE)LoadedDialogHandle, NULL, ShutdownDlgProc, (LONG)pGlobals); } // // If they got past that dialog it means they want to reboot // RebootMachine(pGlobals); ASSERT(!"RebootMachine failed"); // Should never get here return(FALSE); } /***************************************************************************\ * FUNCTION: ShutdownWaitDlgProc * * PURPOSE: Processes messages while we wait for windows to notify us of * a successful shutdown. When notification is received, do any * final processing and make the first call to NtShutdownSystem. * * RETURNS: * DLG_FAILURE - the dialog could not be displayed * DLG_SHUTDOWN() - the system has been shutdown, reboot wasn't requested * * HISTORY: * * 10-14-92 Davidc Created. * 10-04-93 Johannec Added Power off option. * \***************************************************************************/ BOOL WINAPI ShutdownWaitDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { PGLOBALS pGlobals = (PGLOBALS)GetWindowLong(hDlg, GWL_USERDATA); BOOL Success; NTSTATUS Status; HANDLE UserHandle; switch (message) { case WM_INITDIALOG: SetWindowLong(hDlg, GWL_USERDATA, lParam); CentreWindow(hDlg); return(TRUE); case WLX_WM_SAS: if (wParam != WLX_SAS_TYPE_USER_LOGOFF) { return(TRUE); } UpdateWindow(hDlg); // // Look at the public shutdown/reboot flags to determine what windows // has actually done. We may receive other logoff notifications here // but they will be only logoffs - the only place that winlogon actually // calls ExitWindowsEx to do a shutdown/reboot is right here. So wait // for the real shutdown/reboot notification. // if (pGlobals->LogoffFlags & (EWX_SHUTDOWN | EWX_REBOOT | EWX_POWEROFF)) { // // It's the notification we were waiting for. // Do any final processing required and make the first // call to NtShutdownSystem. // // // Do any dos-specific clean-up // ShutdownDOS(); // // Impersonate the user for the shutdown call // UserHandle = ImpersonateUser( &pGlobals->UserProcessData, NULL ); ASSERT(UserHandle != NULL); // // Enable the shutdown privilege // This should always succeed - we are either system or a user who // successfully passed the privilege check in ExitWindowsEx. // Success = EnablePrivilege(SE_SHUTDOWN_PRIVILEGE, TRUE); ASSERT(Success); // // Do the first pass at system shutdown (no reboot yet) // WaitForSystemProcesses(pGlobals); Status = NtShutdownSystem(ShutdownNoReboot); ASSERT(NT_SUCCESS(Status)); // // Revert to ourself // Success = StopImpersonating(UserHandle); ASSERT(Success); // // We've finished system shutdown, we're done // EndDialog(hDlg, LogoffFlagsToWlxCode(pGlobals->LogoffFlags) ); } return(TRUE); } // We didn't process this message return FALSE; } /***************************************************************************\ * FUNCTION: ShutdownDlgProc * * PURPOSE: Processes messages for the shutdown dialog - the one that says * it's safe to turn off the machine. * * RETURNS: DLG_SUCCESS if the user hits the restart button. * * HISTORY: * * 03-19-92 Davidc Created. * \***************************************************************************/ BOOL WINAPI ShutdownDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { PGLOBALS pGlobals = (PGLOBALS)GetWindowLong(hDlg, GWL_USERDATA); switch (message) { case WM_INITDIALOG: SetWindowLong(hDlg, GWL_USERDATA, lParam); SetupSystemMenu(hDlg); CentreWindow(hDlg); return(TRUE); case WM_COMMAND: EndDialog(hDlg, DLG_SUCCESS); return(TRUE); } // We didn't process this message return FALSE; } /***************************************************************************\ * FUNCTION: LogOff * * PURPOSE: Handles the post-user-application part of user logoff. This * routine is called after all the user apps have been closed down * It saves the user's profile, deletes network connections * and reboots/shutsdown the machine if that was requested. * * RETURNS: TRUE on success, FALSE on failure * * HISTORY: * * 12-09-91 Davidc Created. * \***************************************************************************/ BOOL Logoff( PGLOBALS pGlobals, int LoggedOnResult ) { NTSTATUS Status; LUID luidNone = { 0, 0 }; DebugLog((DEB_TRACE, "In Logoff()\n")); // // We expect to be at the winlogon desktop in all cases // // ASSERT(OpenInputDesktop(0, FALSE, MAXIMUM_ALLOWED) == pGlobals->hdeskWinlogon); // // Delete the user's network connections // Make sure we do this before deleting the user's profile // DeleteNetworkConnections(pGlobals); // // Remove any Messages Aliases added by the user. // DeleteMsgAliases(); // // Play the user's logoff sound // if (pGlobals->PlaySound || pGlobals->MigrateSoundEvents) { HANDLE uh; BOOL fBeep; // We AREN'T impersonating the user by default, so we MUST do so // otherwise we end up playing the default rather than the user // specified sound. if (OpenIniFileUserMapping(pGlobals)) { uh = ImpersonateUser(&pGlobals->UserProcessData, NULL); // // Whenever a user logs out, have WINMM.DLL check if there // were any sound events added to the [SOUNDS] section of // CONTROL.INI by a non-regstry-aware app. If there are, // migrate those schemes to their new home. If there aren't, // this is very quick. // if (pGlobals->MigrateSoundEvents) { (*(pGlobals->MigrateSoundEvents))(); } if (pGlobals->PlaySound) { if (!SystemParametersInfo(SPI_GETBEEP, 0, &fBeep, FALSE)) { // Failed to get hold of beep setting. Should we be // noisy or quiet? We have to choose one value... fBeep = TRUE; } if (fBeep) { // // Play synchronous // (*(pGlobals->PlaySound))((LPCSTR)SND_ALIAS_SYSTEMEXIT, NULL, SND_ALIAS_ID | SND_SYNC | SND_NODEFAULT); } } StopImpersonating(uh); CloseIniFileUserMapping(pGlobals); } } // // Call user to close the registry key for the NLS cache. // SetWindowStationUser(pGlobals->WindowStation.hwinsta, &luidNone, NULL, 0); // // Close the IniFileMapping that happened at logon time (LogonAttempt()). // CloseIniFileUserMapping(pGlobals); // // Save the user profile, this unloads the user's key in the registry // SaveUserProfile(pGlobals); // // Delete any remaining RAS connections. Make sure to do this after // the user profile gets copied up to the // DeleteRasConnections( pGlobals ); // // If the user logged off themselves (rather than a system logoff) // and wanted to reboot then do it now. // if (IsShutdown(LoggedOnResult) && (!(pGlobals->LogoffFlags & EWX_WINLOGON_OLD_SYSTEM))) { ShutdownMachine(pGlobals, LoggedOnResult); ASSERT(!"ShutdownMachine failed"); // Should never return } // // Set up security info for new user (system) - this clears out // the stuff for the old user. // SecurityChangeUser(pGlobals, NULL, NULL, pGlobals->WinlogonSid, FALSE); return(TRUE); } /***************************************************************************\ * FUNCTION: DeleteNetworkConnections * * PURPOSE: Calls WNetNukeConnections in the client context to delete * any connections they may have had. * * RETURNS: TRUE on success, FALSE on failure * * HISTORY: * * 04-15-92 Davidc Created. * \***************************************************************************/ BOOL DeleteNetworkConnections( PGLOBALS pGlobals ) { HANDLE ImpersonationHandle; DWORD WNetResult; BOOL Result = FALSE; // Default is failure TCHAR szMprDll[] = TEXT("mpr.dll"); CHAR szWNetNukeConn[] = "WNetClearConnections"; CHAR szWNetOpenEnum[] = "WNetOpenEnumW"; CHAR szWNetEnumResource[] = "WNetEnumResourceW"; CHAR szWNetCloseEnum[] = "WNetCloseEnum"; PWNETNUKECONN lpfnWNetNukeConn = NULL; PWNETOPENENUM lpfnWNetOpenEnum = NULL; PWNETENUMRESOURCE lpfnWNetEnumResource = NULL; PWNETCLOSEENUM lpfnWNetCloseEnum = NULL; HWND hNetDelDlg; HANDLE hEnum; BOOL bConnectionsExist = TRUE; NETRESOURCE NetRes; DWORD dwNumEntries = 1; DWORD dwEntrySize = sizeof (NETRESOURCE); // // Impersonate the user // ImpersonationHandle = ImpersonateUser(&pGlobals->UserProcessData, NULL); if (ImpersonationHandle == NULL) { DebugLog((DEB_ERROR, "DeleteNetworkConnections : Failed to impersonate user\n")); return(FALSE); } // // Load mpr if it wasn't already loaded. // if (!pGlobals->hMPR){ if (!(pGlobals->hMPR = LoadLibrary(szMprDll))) { DebugLog((DEB_ERROR, "DeleteNetworkConnections : Failed to load mpr.dll\n")); goto DNCExit; } } // // Get the function pointers // lpfnWNetOpenEnum = (PWNETOPENENUM) GetProcAddress(pGlobals->hMPR, (LPSTR)szWNetOpenEnum); lpfnWNetEnumResource = (PWNETENUMRESOURCE) GetProcAddress(pGlobals->hMPR, (LPSTR)szWNetEnumResource); lpfnWNetCloseEnum = (PWNETCLOSEENUM) GetProcAddress(pGlobals->hMPR, (LPSTR)szWNetCloseEnum); lpfnWNetNukeConn = (PWNETNUKECONN) GetProcAddress(pGlobals->hMPR, (LPSTR)szWNetNukeConn); // // Check for NULL return values // if ( !lpfnWNetOpenEnum || !lpfnWNetEnumResource || !lpfnWNetCloseEnum || !lpfnWNetNukeConn ) { DebugLog((DEB_ERROR, "DeleteNetworkConnections : Received a NULL pointer from GetProcAddress\n")); goto DNCExit; } // // Check for at least one network connection // if ( (*lpfnWNetOpenEnum)(RESOURCE_CONNECTED, RESOURCETYPE_ANY, 0, NULL, &hEnum) == NO_ERROR) { if ((*lpfnWNetEnumResource)(hEnum, &dwNumEntries, &NetRes, &dwEntrySize) == ERROR_NO_MORE_ITEMS) { bConnectionsExist = FALSE; } (*lpfnWNetCloseEnum)(hEnum); } // // If we don't have any connections, then we can exit. // if (!bConnectionsExist) { goto DNCExit; } // // Display the status dialog box to the user // hNetDelDlg = CreateDialog (pGlobals->hInstance, MAKEINTRESOURCE(IDD_WAIT_NET_DRIVES_DISCONNECT), NULL, DeleteNetConnectionsDlgProc); // // Delete the network connections. // WNetResult = 0; WNetResult = (*lpfnWNetNukeConn)(NULL); if (WNetResult != 0 && WNetResult != ERROR_CAN_NOT_COMPLETE) { DebugLog((DEB_ERROR, "DeleteNetworkConnections : WNetNukeConnections failed, error = %d\n", WNetResult)); } Result = (WNetResult == ERROR_SUCCESS); // // Close the dialog box // if (IsWindow (hNetDelDlg)) { DestroyWindow (hNetDelDlg); } DNCExit: // // Unload mpr.dll // if ( pGlobals->hMPR ) { FreeLibrary(pGlobals->hMPR); pGlobals->hMPR = NULL; } // // Revert to being 'ourself' // if (!StopImpersonating(ImpersonationHandle)) { DebugLog((DEB_ERROR, "DeleteNetworkConnections : Failed to revert to self\n")); } return(Result); } /***************************************************************************\ * FUNCTION: DeleteNetConnectionsDlgProc * * PURPOSE: Processes messages for the deleting net connections dialog * * RETURNS: Standard dialog box return values * * HISTORY: * * 04-26-92 EricFlo Created. * \***************************************************************************/ BOOL WINAPI DeleteNetConnectionsDlgProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam ) { switch (message) { case WM_INITDIALOG: CentreWindow(hDlg); return(TRUE); } // We didn't process this message return FALSE; } //+--------------------------------------------------------------------------- // // Function: DeleteRasConnections // // Synopsis: Delete RAS connections during logoff. // // Arguments: (none) // // History: 5-10-96 RichardW Created // // Notes: // //---------------------------------------------------------------------------- BOOL DeleteRasConnections( PGLOBALS pGlobals ) { HANDLE ImpersonationHandle; SC_HANDLE hServiceMgr, hService; SERVICE_STATUS status; RASCONN rasconn; LPRASCONN lprasconn = &rasconn; DWORD i, dwErr, dwcb, dwc; BOOL bRet; PRASENUMCONNECTIONSW pRasEnumConnectionsW; PRASHANGUPW pRasHangUpW; BOOL FreeThatRasConn = FALSE; if ( GetProfileInt( WINLOGON, KEEP_RAS_AFTER_LOGOFF, 0 ) ) { return( TRUE ); } // // Determine whether the rasman service is running. // hServiceMgr = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); if ( hServiceMgr == NULL ) { DebugLog((DEB_ERROR, "Unable to object service controller, %d\n", GetLastError())); return( FALSE ); } hService = OpenService( hServiceMgr, RASMAN_SERVICE_NAME, SERVICE_QUERY_STATUS); if (hService == NULL) { CloseServiceHandle(hServiceMgr); DebugLog((DEB_TRACE, "rasman not started, nothing to tear down\n")); return( TRUE ); } bRet = QueryServiceStatus( hService, &status ); CloseServiceHandle(hService); CloseServiceHandle(hServiceMgr); if (! bRet ) { // // Service in bad state, get out of here... // return( TRUE ); } if (status.dwCurrentState != SERVICE_RUNNING) { // // Service is not running // return( TRUE ); } // // Load the RASAPI DLL so we can make it do stuff // if ( !hRasApi ) { hRasApi = LoadLibrary( RASAPI32 ); if ( !hRasApi ) { return( FALSE ); } } pRasEnumConnectionsW = (PRASENUMCONNECTIONSW) GetProcAddress( hRasApi, "RasEnumConnectionsW" ); pRasHangUpW = (PRASHANGUPW) GetProcAddress( hRasApi, "RasHangUpW" ); if ( (!pRasEnumConnectionsW) || (!pRasHangUpW) ) { return( FALSE ); } // // Impersonate the user // ImpersonationHandle = ImpersonateUser(&pGlobals->UserProcessData, NULL); if (ImpersonationHandle == NULL) { DebugLog((DEB_ERROR, "DeleteNetworkConnections : Failed to impersonate user\n")); return(FALSE); } // // Enumerate the current RAS connections. // lprasconn->dwSize = sizeof (RASCONN); dwcb = sizeof (RASCONN); dwc = 1; dwErr = pRasEnumConnectionsW(lprasconn, &dwcb, &dwc); if (dwErr == ERROR_BUFFER_TOO_SMALL) { lprasconn = LocalAlloc(LPTR, dwcb); FreeThatRasConn = TRUE; if ( !lprasconn ) { return( FALSE ); } dwErr = pRasEnumConnectionsW(lprasconn, &dwcb, &dwc); if (dwErr) { if ( FreeThatRasConn ) { LocalFree( lprasconn ); } return( FALSE ); } } else if (dwErr) { return( FALSE ); } // // cycle through the connections, and kill them // for (i = 0; i < dwc; i++) { DebugLog((DEB_TRACE, "Hanging up connection to %ws\n", lprasconn[i].szEntryName)); (VOID) pRasHangUpW( lprasconn[i].hrasconn ); } if ( FreeThatRasConn ) { LocalFree( lprasconn ); } // // Revert to being 'ourself' // if (!StopImpersonating(ImpersonationHandle)) { DebugLog((DEB_ERROR, "DeleteNetworkConnections : Failed to revert to self\n")); } return( TRUE ); }