/*+ * * Microsoft Windows * Copyright (C) Microsoft Corporation, 1997 - 1998. * * Name : cseclogn.cxx * Author:Jeffrey Richter (v-jeffrr) * * Abstract: * This is the client side for Secondary Logon Service * implemented as CreateProcessWithLogon API * in advapi32.dll * * Revision History: * PraeritG 10/8/97 To integrate this in to services.exe * -*/ #define UNICODE #define SECURITY_WIN32 #include #include #include #include #include #include #include #include #include #include "seclogon.h" #include "security.h" #include "dbgdef.h" // // must move to winbase.h soon! #define LOGON_WITH_PROFILE 0x00000001 #define LOGON_NETCREDENTIALS_ONLY 0x00000002 //////////////////////////////////////////////////////////////////////// // // Function prototypes: // //////////////////////////////////////////////////////////////////////// BOOL CreateProcessWithLogonCommonW( HANDLE hToken, LPCWSTR lpUsername, LPCWSTR lpDomain, LPCWSTR lpPassword, DWORD dwLogonFlags, LPCWSTR lpApplicationName, LPWSTR lpCommandLine, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation); DWORD c_SeclCreateProcessWithLogonW(IN SECL_SLI *psli, OUT SECL_SLRI *pslri); DWORD To_SECL_BLOB_A(IN LPVOID lpEnvironment, OUT SECL_BLOB *psb); DWORD To_SECL_BLOB_W(IN LPVOID lpEnvironment, OUT SECL_BLOB *psb); ///////////////////////////////////////////////////////////////////////// // // Useful macros: // ///////////////////////////////////////////////////////////////////////// #define ARRAYSIZE(array) ((sizeof(array)) / (sizeof(array[0]))) // // Fill out a string structure to send to the seclogon RPC server. // We'll use the convention that the client must allocate an extra byte // which the server can use to NULL-terminate the input string // #define ASSIGN_SECL_STRING(ss, wsz) \ { \ ss.pwsz = wsz; \ if (NULL != wsz) { \ size_t len; \ LastError = StringCchLength(wsz, 0xFFFF-1, &len); \ if (FAILED(LastError)) { \ __leave; \ } \ ss.ccLength = (WORD)len; \ ss.ccSize = (WORD)len+1; \ } \ else { \ ss.ccLength = ss.ccSize = 0; \ } \ } #define GET_RTL_ENCRYPT_PADDING_LENGTH(dwLen, dwUnits) \ ((RTL_ENCRYPT_MEMORY_SIZE - (((dwLen)*(dwUnits)) % RTL_ENCRYPT_MEMORY_SIZE)) / (dwUnits)) #define IS_RTL_MEMORY_BLOCK_MULTIPLE(dwLen, dwUnits) \ ((((dwLen)*(dwUnits)) % RTL_ENCRYPT_MEMORY_SIZE) == 0) #define GET_RTL_ENCRYPT_BLOCK_LENGTH(dwLen, dwUnits) \ ((IS_RTL_MEMORY_BLOCK_MULTIPLE((dwLen), (dwUnits))) ? (dwLen) : ((dwLen) + (GET_RTL_ENCRYPT_PADDING_LENGTH((dwLen), (dwUnits))))) //////////////////////////////////////////////////////////////////////// // // Module implementation: // ////////////////////////////////////////////////////////////////////////// extern "C" void *__cdecl _alloca(size_t); BOOL WINAPI CreateProcessWithLogonW( LPCWSTR lpUsername, LPCWSTR lpDomain, LPCWSTR lpPassword, DWORD dwLogonFlags, LPCWSTR lpApplicationName, LPWSTR lpCommandLine, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ) { return CreateProcessWithLogonCommonW( NULL, lpUsername, lpDomain, lpPassword, dwLogonFlags, lpApplicationName, lpCommandLine, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation ); } BOOL WINAPI CreateProcessWithTokenW( HANDLE hToken, DWORD dwLogonFlags, LPCWSTR lpApplicationName, LPWSTR lpCommandLine, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation ) { LPWSTR szEmpty = L""; return CreateProcessWithLogonCommonW( hToken, szEmpty, szEmpty, szEmpty, dwLogonFlags, lpApplicationName, lpCommandLine, dwCreationFlags, lpEnvironment, lpCurrentDirectory, lpStartupInfo, lpProcessInformation ); } BOOL CreateProcessWithLogonCommonW( HANDLE hToken, LPCWSTR lpUsername, LPCWSTR lpDomain, LPCWSTR lpPassword, DWORD dwLogonFlags, LPCWSTR lpApplicationName, LPWSTR lpCommandLine, DWORD dwCreationFlags, LPVOID lpEnvironment, LPCWSTR lpCurrentDirectory, LPSTARTUPINFOW lpStartupInfo, LPPROCESS_INFORMATION lpProcessInformation) /*++ Routine Description: Arguments: Return Value: --*/ { BOOL fOk = FALSE; BOOL fRevertWinsta = FALSE; DWORD ccCmdLine; DWORD ccPszApplName; DWORD ccEncryptedPassword; DWORD LastError = ERROR_SUCCESS; HANDLE hheap = GetProcessHeap(); HDESK hDesk = NULL; HRESULT hr; HWINSTA hWinsta = NULL; HWINSTA hWinstaSave = NULL; LPWSTR pszApplName = NULL; LPWSTR pszCmdLine = NULL; LPWSTR pwszEmptyString = L""; NTSTATUS ntStatus; SECL_SLI sli; SECL_SLRI slri; UNICODE_STRING uszEncryptedPassword; WCHAR wszDesktopName[2*MAX_PATH + 2]; WCHAR wszEncryptedPassword[GET_RTL_ENCRYPT_BLOCK_LENGTH(CREDUI_MAX_PASSWORD_LENGTH, sizeof(WCHAR)) + 1]; ZeroMemory(&sli, sizeof(sli)); ZeroMemory(&slri, sizeof(slri)); ZeroMemory(&uszEncryptedPassword, sizeof(uszEncryptedPassword)); ZeroMemory(&wszDesktopName, sizeof(wszDesktopName)); ZeroMemory(&wszEncryptedPassword[0], sizeof(wszEncryptedPassword)); __try { sli.hToken = (unsigned __int64)hToken; if (NULL != lpPassword && L'\0' != lpPassword[0]) { // BUG 547683: we need to encrypt the password across the LPC boundary to prevent // password theft. NOTE: we won't actually touch the user's passwd buffer -- // this behavior would be confusing, and would have minimal gain. // // The RtlEncryptMemory() routine requires that the // memory buffer's size be a multiple of the encryption block length. // Copy the password over and pad it with some zeros: // hr = StringCchCopy(wszEncryptedPassword, ARRAYSIZE(wszEncryptedPassword), lpPassword); if (FAILED(hr)) { LastError = (DWORD)hr; __leave; } // Calculate the size of the encrypted password ccEncryptedPassword = wcslen(wszEncryptedPassword); ccEncryptedPassword = GET_RTL_ENCRYPT_BLOCK_LENGTH(ccEncryptedPassword, sizeof(WCHAR)); assert(ccEncryptedPassword < ARRAYSIZE(wszEncryptedPassword)); // perform the encryption ntStatus = RtlEncryptMemory((PVOID)wszEncryptedPassword, sizeof(WCHAR)*ccEncryptedPassword, RTL_ENCRYPT_OPTION_SAME_LOGON); if (!NT_SUCCESS(ntStatus)) { LastError = RtlNtStatusToDosError(ntStatus); __leave; } // assign the encrypted password to a SECL_STRING. // NOTE: we can't use ASSIGN_SECL_STRING, as the encrypted password may have // '\0' characters in the middle sli.ssPassword.ccLength = (WORD)(ccEncryptedPassword); sli.ssPassword.ccSize = (WORD)(ccEncryptedPassword+1); sli.ssPassword.pwsz = wszEncryptedPassword; } else { lpPassword = L""; ASSIGN_SECL_STRING(sli.ssPassword, (LPWSTR)lpPassword); } // // JMR: Do these flags work: CREATE_SEPARATE_WOW_VDM, // CREATE_SHARED_WOW_VDM // Valid flags: CREATE_SUSPENDED, CREATE_UNICODE_ENVIRONMENT, // *_PRIORITY_CLASS // // The following flags are illegal. Fail the call if any are specified. // if ((dwCreationFlags & (DEBUG_PROCESS | DEBUG_ONLY_THIS_PROCESS | DETACHED_PROCESS)) != 0) { LastError = ERROR_INVALID_PARAMETER; __leave; } if(dwLogonFlags & ~(LOGON_WITH_PROFILE | LOGON_NETCREDENTIALS_ONLY)) { LastError = ERROR_INVALID_PARAMETER; __leave; } // // Turn on the flags that MUST be turned on // // We are overloading CREATE_NEW_CONSOLE to // CREATE_WITH_NETWORK_LOGON // dwCreationFlags |= CREATE_DEFAULT_ERROR_MODE | CREATE_NEW_CONSOLE | CREATE_NEW_PROCESS_GROUP; // // If no priority class explicitly specified and this process is IDLE, force IDLE (See CreateProcess documentation) // if ((dwCreationFlags & (NORMAL_PRIORITY_CLASS | IDLE_PRIORITY_CLASS | HIGH_PRIORITY_CLASS | REALTIME_PRIORITY_CLASS)) == 0) { if (GetPriorityClass(GetCurrentProcess()) == IDLE_PRIORITY_CLASS) dwCreationFlags |= IDLE_PRIORITY_CLASS; } ccPszApplName = MAX_PATH; pszApplName = (LPWSTR) HeapAlloc(hheap, 0, sizeof(WCHAR) * ccPszApplName); // // Lookup the fullpathname of the specified executable // ccCmdLine = MAX_PATH + lstrlenW(lpCommandLine); pszCmdLine = (LPWSTR) HeapAlloc(hheap, 0, sizeof(WCHAR) * ccCmdLine); if(pszApplName == NULL || pszCmdLine == NULL) { LastError = ERROR_INVALID_PARAMETER; __leave; } if(lpApplicationName == NULL) { if(lpCommandLine != NULL) { // // Commandline contains the name, we should parse it out and get // the full path so that correct executable is invoked. // DWORD Length; DWORD fileattr; WCHAR TempChar = L'\0'; LPWSTR TempApplName = NULL; LPWSTR TempRemainderString = NULL; LPWSTR WhiteScan = NULL; BOOL SearchRetry = TRUE; DWORD ccApplName = lstrlenW(lpCommandLine)+1; LPWSTR ApplName = (LPWSTR) HeapAlloc( hheap, 0, sizeof(WCHAR) * ccApplName); DWORD ccNameBuffer = MAX_PATH+1; LPWSTR NameBuffer = (LPWSTR) HeapAlloc( hheap, 0, sizeof(WCHAR) * ccNameBuffer); if (ApplName == NULL || NameBuffer == NULL) { LastError = ERROR_NOT_ENOUGH_MEMORY; __leave; } hr = StringCchCopy(ApplName, ccApplName, lpCommandLine); if (FAILED(hr)) { LastError = (DWORD)hr; __leave; } WhiteScan = ApplName; // // if there is a leading quote // if(*WhiteScan == L'\"') { // we will NOT retry search, as app name is quoted. SearchRetry = FALSE; WhiteScan++; TempApplName = WhiteScan; while(*WhiteScan) { if( *WhiteScan == L'\"') { TempChar = *WhiteScan; *WhiteScan = L'\0'; TempRemainderString = WhiteScan; break; } WhiteScan++; } } else { // skip to the first non-white char while(*WhiteScan) { if( *WhiteScan == L' ' || *WhiteScan == L'\t') { WhiteScan++; } else break; } TempApplName = WhiteScan; while(*WhiteScan) { if( *WhiteScan == L' ' || *WhiteScan == L'\t') { TempChar = *WhiteScan; *WhiteScan = L'\0'; TempRemainderString = WhiteScan; break; } WhiteScan++; } } RetrySearch: Length = SearchPathW( NULL, TempApplName, (PWSTR)L".exe", MAX_PATH, NameBuffer, NULL ); if(!Length || Length > MAX_PATH) { if(LastError) SetLastError(LastError); else LastError = GetLastError(); CoverForDirectoryCase: // // If we still have command line left, then keep going // the point is to march through the command line looking // for whitespace so we can try to find an image name // launches of things like: // c:\word 95\winword.exe /embedding -automation // require this. Our first iteration will // stop at c:\word, our next // will stop at c:\word 95\winword.exe // if(TempRemainderString) { *TempRemainderString = TempChar; WhiteScan++; } if(*WhiteScan & SearchRetry) { // again skip to the first non-white char while(*WhiteScan) { if( *WhiteScan == L' ' || *WhiteScan == L'\t') { WhiteScan++; } else break; } while(*WhiteScan) { if( *WhiteScan == L' ' || *WhiteScan == L'\t') { TempChar = *WhiteScan; *WhiteScan = L'\0'; TempRemainderString = WhiteScan; break; } WhiteScan++; } // we'll do one last try of the whole string. if(!WhiteScan) SearchRetry = FALSE; goto RetrySearch; } // // otherwise we have failed. // if(NameBuffer) HeapFree(hheap, 0, NameBuffer); if(ApplName) HeapFree(hheap, 0, ApplName); // we should let CreateProcess do its job. if (pszApplName) { HeapFree(hheap, 0, pszApplName); pszApplName = NULL; } hr = StringCchCopy(pszCmdLine, ccCmdLine, lpCommandLine); if (FAILED(hr)) { LastError = (DWORD)hr; __leave; } } else { // searchpath succeeded. // but it can find a directory! fileattr = GetFileAttributesW(NameBuffer); if ( fileattr != 0xffffffff && (fileattr & FILE_ATTRIBUTE_DIRECTORY) ) { Length = 0; goto CoverForDirectoryCase; } // // so it is not a directory.. it must be the real thing! // hr = StringCchCopy(pszApplName, ccPszApplName, NameBuffer); if (FAILED(hr)) { LastError = (DWORD)hr; __leave; } hr = StringCchCopy(pszCmdLine, ccCmdLine, lpCommandLine); if (FAILED(hr)) { LastError = (DWORD)hr; __leave; } HeapFree(hheap, 0, ApplName); HeapFree(hheap, 0, NameBuffer); } } else { LastError = ERROR_INVALID_PARAMETER; __leave; } } else { // // If ApplicationName is not null, we need to handle // one case here -- the application name is present in // current directory. All other cases will be handled by // CreateProcess in the server side anyway. // // // let us get a FullPath relative to current directory // and try to open it. If it succeeds, then the full path // is what we'll give as app name.. otherwise will just // pass what we got from caller and let CreateProcess deal with it. LPWSTR lpFilePart; DWORD cchFullPath = GetFullPathName( lpApplicationName, MAX_PATH, pszApplName, &lpFilePart ); if(cchFullPath) { HANDLE hFile; // // let us try to open it. // if it works, pszApplName is already setup correctly. // just close the handle. hFile = CreateFile(lpApplicationName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL ); if(hFile == INVALID_HANDLE_VALUE) { // otherwise, keep what the caller gave us. hr = StringCchCopy(pszApplName, ccPszApplName, lpApplicationName); if (FAILED(hr)) { LastError = (DWORD)hr; __leave; } } else CloseHandle(hFile); } else { // lets keep what the caller gave us. hr = StringCchCopy(pszApplName, ccPszApplName, lpApplicationName); if (FAILED(hr)) { LastError = (DWORD)hr; __leave; } } // // Commandline should be kept as is. // if(lpCommandLine != NULL) { hr = StringCchCopy(pszCmdLine, ccCmdLine, lpCommandLine); if (FAILED(hr)) { LastError = (DWORD)hr; __leave; } } else { HeapFree(hheap, 0, pszCmdLine); pszCmdLine = NULL; } } #if 0 if(lpApplicationName != NULL) lstrcpy(pszApplName,lpApplicationName); else { HeapFree(hheap, 0, pszApplName); pszApplName = NULL; } if(lpCommandLine != NULL) lstrcpy(pszCmdLine, lpCommandLine); else { HeapFree(hheap, 0, pszCmdLine); pszCmdLine = NULL; } #endif // Construct a memory block will all the info that needs to go to the server sli.lLogonIdHighPart = 0; sli.ulLogonIdLowPart = 0; sli.ulLogonFlags = dwLogonFlags; sli.ulProcessId = GetCurrentProcessId(); sli.ulCreationFlags = dwCreationFlags; sli.hWinsta = 0; // no longer used sli.hDesk = 0; // no longer used ASSIGN_SECL_STRING(sli.ssUsername, (LPWSTR) lpUsername); ASSIGN_SECL_STRING(sli.ssDomain, (LPWSTR) lpDomain); ASSIGN_SECL_STRING(sli.ssApplicationName, pszApplName); ASSIGN_SECL_STRING(sli.ssCommandLine, pszCmdLine); ASSIGN_SECL_STRING(sli.ssCurrentDirectory, (LPWSTR)lpCurrentDirectory); ASSIGN_SECL_STRING(sli.ssDesktop, lpStartupInfo->lpDesktop); ASSIGN_SECL_STRING(sli.ssTitle, lpStartupInfo->lpTitle); if (0 != (sli.ulCreationFlags & CREATE_UNICODE_ENVIRONMENT)) { LastError = To_SECL_BLOB_W(lpEnvironment, &(sli.sbEnvironment)); } else { LastError = To_SECL_BLOB_A(lpEnvironment, &(sli.sbEnvironment)); } if (ERROR_SUCCESS != LastError) { __leave; } // If the caller hasn't specified their own desktop, we'll do it for // them (the seclogon service will take care of granting access // to the desktop). if (sli.ssDesktop.pwsz == NULL || sli.ssDesktop.pwsz[0] == L'\0') { DWORD Length; HWINSTA Winsta = GetProcessWindowStation(); HDESK Desk = GetThreadDesktop(GetCurrentThreadId()); // Send seclogon the name of the current windowstation and desktop. // Default to empty string if we can't get the name: ASSIGN_SECL_STRING(sli.ssDesktop, pwszEmptyString); if (GetUserObjectInformation(Winsta, UOI_NAME, wszDesktopName, (MAX_PATH*sizeof(WCHAR)), &Length)) { Length = (DWORD)wcslen(wszDesktopName); wszDesktopName[Length++] = L'\\'; if(GetUserObjectInformation(Desk, UOI_NAME, &wszDesktopName[Length], (MAX_PATH*sizeof(WCHAR)), &Length)) { // sli.ssDesktop now contains "windowstation\desktop" ASSIGN_SECL_STRING(sli.ssDesktop, wszDesktopName); } } } else { // The caller specified their own desktop sli.ulSeclogonFlags |= SECLOGON_CALLER_SPECIFIED_DESKTOP; } // Perform the RPC call to the seclogon service: LastError = c_SeclCreateProcessWithLogonW(&sli, &slri); if (ERROR_SUCCESS != LastError) __leave; fOk = (slri.ulErrorCode == NO_ERROR); // This function succeeds if the server's function succeeds if (!fOk) { // // If the server function failed, set the server's // returned eror code as this thread's error code // LastError = slri.ulErrorCode; SetLastError(slri.ulErrorCode); } else { // // The server function succeeded, return the // PROCESS_INFORMATION info // lpProcessInformation->hProcess = (HANDLE)slri.hProcess; lpProcessInformation->hThread = (HANDLE)slri.hThread; lpProcessInformation->dwProcessId = slri.ulProcessId; lpProcessInformation->dwThreadId = slri.ulThreadId; LastError = ERROR_SUCCESS; } } __finally { if (NULL != pszCmdLine) HeapFree(hheap, 0, pszCmdLine); if (NULL != pszApplName) HeapFree(hheap, 0, pszApplName); if (fRevertWinsta) SetProcessWindowStation(hWinstaSave); if (NULL != hWinsta) CloseWindowStation(hWinsta); if (NULL != hDesk) CloseDesktop(hDesk); SetLastError(LastError); } return(fOk); } //////////////////////////////////////////////////////////////////////// // // RPC Utility methods: // //////////////////////////////////////////////////////////////////////// DWORD To_SECL_BLOB_W(IN LPVOID lpEnvironment, OUT SECL_BLOB *psb) { DWORD cb = 0; DWORD dwResult = NULL; HANDLE hHeap = NULL; HRESULT hr; LPBYTE pb = NULL; LPWSTR pwsz = NULL; size_t totalLen = 0; size_t nextLen; hHeap = GetProcessHeap(); _JumpCondition(NULL == hHeap, GetProcessHeapError); if (NULL != lpEnvironment) { for (pwsz = (LPWSTR)lpEnvironment; pwsz[0] != L'\0'; pwsz += nextLen + 1) { // 10K chars is the most we allow for the environment block: hr = StringCchLengthW(pwsz, (10*1024)-totalLen, &nextLen); if (FAILED(hr)) goto StringCchLengthWError; totalLen += nextLen + 1; } cb = sizeof(WCHAR) * ((DWORD)(1 + totalLen)); pb = (LPBYTE)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, cb); _JumpCondition(NULL == pb, MemoryError); CopyMemory(pb, (LPBYTE)lpEnvironment, cb); } psb->cb = cb; psb->pb = pb; dwResult = ERROR_SUCCESS; CommonReturn: return dwResult; ErrorReturn: if (NULL != pb) { HeapFree(hHeap, 0, pb); } goto CommonReturn; SET_DWRESULT(GetProcessHeapError, GetLastError()); SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY); SET_DWRESULT(StringCchLengthWError, (DWORD)hr); } DWORD To_SECL_BLOB_A(IN LPVOID lpEnvironment, OUT SECL_BLOB *psb) { DWORD cb = 0; DWORD dwResult; HANDLE hHeap = NULL; HRESULT hr; LPBYTE pb = NULL; LPSTR psz = NULL; size_t totalLen = 0; size_t nextLen; hHeap = GetProcessHeap(); _JumpCondition(NULL == hHeap, GetProcessHeapError); if (NULL != lpEnvironment) { for (psz = (LPSTR)lpEnvironment; psz[0] != '\0'; psz += nextLen + 1) { // 10K chars is the most we allow for the environment block: hr = StringCchLengthA(psz, (10*1024)-totalLen, &nextLen); if (FAILED(hr)) goto StringCchLengthAError; totalLen += nextLen + 1; } cb = (DWORD)(1 + totalLen); pb = (LPBYTE)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, cb); _JumpCondition(NULL == pb, MemoryError); CopyMemory(pb, (LPBYTE)lpEnvironment, cb); } psb->cb = cb; psb->pb = pb; dwResult = ERROR_SUCCESS; CommonReturn: return dwResult; ErrorReturn: if (NULL != pb) { HeapFree(hHeap, 0, pb); } goto CommonReturn; SET_DWRESULT(GetProcessHeapError, GetLastError()); SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY); SET_DWRESULT(StringCchLengthAError, (DWORD)hr); } DWORD StartSeclogonService() { BOOL fResult; DWORD dwInitialCount; DWORD dwResult; SC_HANDLE hSCM = NULL; SC_HANDLE hService = NULL; SERVICE_STATUS sSvcStatus; hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT); _JumpCondition(hSCM == NULL, OpenSCManagerError); hService = OpenService(hSCM, wszSvcName, SERVICE_START | SERVICE_QUERY_STATUS); _JumpCondition(NULL == hService, OpenServiceError); fResult = StartService(hService, NULL, NULL); _JumpCondition(FALSE == fResult, StartServiceError); // Wait until the service has actually started. // Set timeout to 20 seconds. dwInitialCount = GetTickCount(); // Keep polling to see if the service has started ... for (;;) { fResult = QueryServiceStatus(hService, &sSvcStatus); _JumpCondition(FALSE == fResult, QueryServiceStatusError); // The service is running. We can stop waiting for it. if (sSvcStatus.dwCurrentState == SERVICE_RUNNING) break; // Check to see if we've timed out. If GetTickCount() rolls over, // then at worst we time out early. _JumpCondition((GetTickCount() - dwInitialCount) > 20000, ServiceTimeoutError); // Don't hose the service. SleepEx(100, FALSE); } // Ok, the service has successfully started. dwResult = ERROR_SUCCESS; CommonReturn: if (NULL != hSCM) { CloseServiceHandle(hSCM); } if (NULL != hService) { CloseServiceHandle(hService); } return dwResult; ErrorReturn: goto CommonReturn; SET_DWRESULT(OpenSCManagerError, GetLastError()); SET_DWRESULT(OpenServiceError, GetLastError()); SET_DWRESULT(QueryServiceStatusError, GetLastError()); SET_DWRESULT(StartServiceError, GetLastError()); SET_DWRESULT(ServiceTimeoutError, ERROR_SERVICE_REQUEST_TIMEOUT); } DWORD SetupLocalRPCSecurity(handle_t hRPCBinding) { CHAR szDomainName[128]; CHAR szName[128]; DWORD cbDomainName; DWORD cbName; DWORD dwResult; PSID pSid = NULL; RPC_SECURITY_QOS SecurityQOS; SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY; SID_NAME_USE SidNameUse; // We're doing LRPC -- we need to get the account name of the service to do mutual auth if (!AllocateAndInitializeSid(&SidAuthority, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &pSid)) goto AllocateAndInitializeSidError; cbName = sizeof(szName); cbDomainName = sizeof(szDomainName); if (!LookupAccountSidA(NULL, pSid, szName, &cbName, szDomainName, &cbDomainName, &SidNameUse)) goto LookupAccountSidAError; // Specify quality of service parameters. SecurityQOS.ImpersonationType = RPC_C_IMP_LEVEL_IMPERSONATE; // the server will need to impersonate us SecurityQOS.Version = RPC_C_SECURITY_QOS_VERSION; SecurityQOS.Capabilities = RPC_C_QOS_CAPABILITIES_MUTUAL_AUTH; // we need mutual auth SecurityQOS.IdentityTracking = RPC_C_QOS_IDENTITY_STATIC; // calls go to the server under the identity that created the binding handle dwResult = RpcBindingSetAuthInfoExA(hRPCBinding, (unsigned char *)szName, RPC_C_AUTHN_LEVEL_PKT_PRIVACY, RPC_C_AUTHN_WINNT, NULL, 0, &SecurityQOS); if (RPC_S_OK != dwResult) goto RpcBindingSetAuthInfoExAError; dwResult = ERROR_SUCCESS; ErrorReturn: if (NULL != pSid) { FreeSid(pSid); } return dwResult; SET_DWRESULT(AllocateAndInitializeSidError, GetLastError()); SET_DWRESULT(LookupAccountSidAError, GetLastError()); SET_DWRESULT(RpcBindingSetAuthInfoExAError, dwResult); } DWORD c_SeclCreateProcessWithLogonW (IN SECL_SLI *psli, OUT SECL_SLRI *pslri) { DWORD dwResult; LPWSTR pwszBinding = NULL; RPC_BINDING_HANDLE hRPCBinding = NULL; dwResult = RpcStringBindingCompose (NULL, (USHORT *)L"ncalrpc", NULL, (USHORT *)wszSeclogonSharedProcEndpointName, (USHORT *)L"Security=impersonation static false", (USHORT **)&pwszBinding); _JumpCondition(RPC_S_OK != dwResult, RpcStringBindingComposeError); dwResult = RpcBindingFromStringBinding((USHORT *)pwszBinding, &hRPCBinding); _JumpCondition(0 != dwResult, RpcBindingFromStringBindingError); dwResult = SetupLocalRPCSecurity(hRPCBinding); _JumpCondition(ERROR_SUCCESS != dwResult, SetupLocalRpcSecurityError); // Perform the RPC call to the seclogon service. If the call fails because the // service was not started, try again. If the call still fails, give up. for (BOOL fFirstTry = TRUE; /*TRUE*/; fFirstTry = FALSE) { __try { SeclCreateProcessWithLogonW(hRPCBinding, psli, pslri); break; } __except(EXCEPTION_EXECUTE_HANDLER) { dwResult = RpcExceptionCode(); if ((RPC_S_SERVER_UNAVAILABLE == dwResult || RPC_S_UNKNOWN_IF == dwResult) && (TRUE == fFirstTry)) { // Ok, the seclogon service is probably just not started. // Attempt to start it up and try again. dwResult = StartSeclogonService(); _JumpCondition(ERROR_SUCCESS != dwResult, SeclCreateProcessWithLogonWError); } else { goto SeclCreateProcessWithLogonWError; } } } dwResult = ERROR_SUCCESS; CommonReturn: if (NULL != pwszBinding) { RpcStringFree((USHORT **)&pwszBinding); } if (NULL != hRPCBinding) { RpcBindingFree(&hRPCBinding); } return dwResult; ErrorReturn: goto CommonReturn; SET_DWRESULT(RpcBindingFromStringBindingError, dwResult); SET_DWRESULT(RpcStringBindingComposeError, dwResult); SET_DWRESULT(SeclCreateProcessWithLogonWError, dwResult); SET_DWRESULT(SetupLocalRpcSecurityError, dwResult); } void DbgPrintf( DWORD /*dwSubSysId*/, LPCSTR /*pszFormat*/ , ...) { } //////////////////////////////// End Of File /////////////////////////////////