Leaked source code of windows server 2003
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.

2321 lines
77 KiB

  1. /*+
  2. *
  3. * Microsoft Windows
  4. * Copyright (C) Microsoft Corporation, 1997 - 1998.
  5. *
  6. * Name : seclogon.cxx
  7. * Author:Jeffrey Richter (v-jeffrr)
  8. *
  9. * Abstract:
  10. * This is the service DLL for Secondary Logon Service
  11. * This service supports the CreateProcessWithLogon API implemented
  12. * in advapi32.dll
  13. *
  14. * Revision History:
  15. * PraeritG 10/8/97 To integrate this in to services.exe
  16. *
  17. -*/
  18. #define STRICT
  19. // Disable some warnings we don't care about:
  20. #pragma warning(disable:4115) // '': named type definition in parentheses
  21. #pragma warning(disable:4201) // nonstandard extension used : nameless struct/union
  22. #pragma warning(disable:4204) // nonstandard extension used : non-constant aggregate initializer
  23. #include <nt.h>
  24. #include <ntrtl.h>
  25. #include <nturtl.h>
  26. #include <ntlsa.h>
  27. #include <Windows.h>
  28. #define SECURITY_WIN32
  29. #define SECURITY_KERBEROS
  30. #include <security.h>
  31. #include <secint.h>
  32. #include <winsafer.h>
  33. #include <shellapi.h>
  34. #include <svcs.h>
  35. #include <userenv.h>
  36. #include <sddl.h>
  37. #include <rpcdcep.h>
  38. #include <crypt.h>
  39. #include <lm.h>
  40. #include <strsafe.h>
  41. #include "seclogon.h"
  42. #include <stdio.h>
  43. #include "stringid.h"
  44. #include "dbgdef.h"
  45. //
  46. // must move to winbase.h soon!
  47. #define LOGON_WITH_PROFILE 0x00000001
  48. #define LOGON_NETCREDENTIALS_ONLY 0x00000002
  49. #define MAXIMUM_SECLOGON_PROCESSES MAXIMUM_WAIT_OBJECTS*4
  50. struct SECL_STATE {
  51. SERVICE_STATUS serviceStatus;
  52. SERVICE_STATUS_HANDLE hServiceStatus;
  53. HANDLE hLSA;
  54. ULONG hMSVPackage;
  55. ULONG hKerbPackage;
  56. BOOL fRPCServerActive;
  57. LIST_ENTRY JobListHead;
  58. } g_state;
  59. typedef struct _SECONDARYLOGONINFOW {
  60. // First fields should all be quad-word types to avoid alignment errors:
  61. LPSTARTUPINFO lpStartupInfo;
  62. LPWSTR lpUsername;
  63. LPWSTR lpDomain;
  64. LPWSTR lpApplicationName;
  65. LPWSTR lpCommandLine;
  66. LPVOID lpEnvironment;
  67. LPCWSTR lpCurrentDirectory;
  68. UNICODE_STRING uszPassword; // use UNICODE_STRING for the passwd (easier to work with Rtl(De/En)cryptMemory())
  69. // Next group of fields are double-word types:
  70. DWORD dwProcessId;
  71. ULONG LogonIdLowPart;
  72. LONG LogonIdHighPart;
  73. DWORD dwLogonFlags;
  74. DWORD dwCreationFlags;
  75. DWORD dwSeclogonFlags;
  76. // Insert smaller types below:
  77. HANDLE hToken; // client access token handle, for CreateProcessWithToken
  78. } SECONDARYLOGONINFOW, *PSECONDARYLOGONINFOW;
  79. typedef struct _SECONDARYLOGONRETINFO {
  80. PROCESS_INFORMATION pi;
  81. DWORD dwErrorCode;
  82. } SECONDARYLOGONRETINFO, *PSECONDARYLOGONRETINFO;
  83. typedef struct _SECONDARYLOGINWATCHINFO {
  84. HANDLE hProcess;
  85. HANDLE hToken;
  86. HANDLE hProfile;
  87. LUID LogonId;
  88. DWORD dwClientSessionId;
  89. PSECONDARYLOGONINFOW psli;
  90. } SECONDARYLOGONWATCHINFO, *PSECONDARYLOGONWATCHINFO;
  91. //
  92. // The SecondaryLogonJob structure contains all of the information needed to cleanup when
  93. //
  94. // a) a secondary logon job group terminates (non-TS case)
  95. // OR b) a secondary logon process terminates (TS case)
  96. //
  97. typedef struct _SecondaryLogonJob {
  98. HANDLE hJob; // The Job to watch for (case 'a')
  99. HANDLE hProcess; // The Process to watch for (case 'b')
  100. HANDLE hRegisteredProcessTerminated; // Used to deregister wait on hProcess (case 'b')
  101. HANDLE hToken; // Used to unload profile on cleanup
  102. HANDLE hProfile; // Used to unload profile on cleanup
  103. LUID RootLogonId; // The root logon session which this process/job is associated with
  104. // BUGBUG: psli no longer has to be preserved until cleanup. However, there's no reason to
  105. // destabilze the codebase by fixing it now.
  106. PSECONDARYLOGONINFOW psli; // More data to free when the process/job goes away
  107. LIST_ENTRY list; // Links this struct to other active jobs
  108. BOOL fHeapAllocated; // TRUE if this job was HeapAlloc'd (must be freed on cleanup).
  109. } SecondaryLogonJob;
  110. #define _JumpCondition(condition, label) \
  111. if (condition) \
  112. { \
  113. goto label; \
  114. } \
  115. else { }
  116. #define _JumpConditionWithExpr(condition, label, expr) \
  117. if (condition) \
  118. { \
  119. expr; \
  120. goto label; \
  121. } \
  122. else { }
  123. #define ARRAYSIZE(array) ((sizeof(array)) / (sizeof(array[0])))
  124. #define FIELDOFFSET(s,m) ((size_t)(ULONG_PTR)&(((s *)0)->m))
  125. CRITICAL_SECTION csForProcessCount;
  126. BOOL g_fIsCsInitialized = FALSE;
  127. PSVCHOST_GLOBAL_DATA GlobalData;
  128. HANDLE g_hIOCP = NULL;
  129. BOOL g_fCleanupThreadActive = FALSE;
  130. //
  131. // function prototypes
  132. //
  133. void Free_SECONDARYLOGONINFOW(PSECONDARYLOGONINFOW psli);
  134. void FreeGlobalState();
  135. DWORD InitGlobalState();
  136. DWORD MySetServiceStatus(DWORD dwCurrentState, DWORD dwCheckPoint, DWORD dwWaitHint, DWORD dwExitCode);
  137. DWORD MySetServiceStopped(DWORD dwExitCode);
  138. DWORD SeclStartRpcServer();
  139. DWORD SeclStopRpcServer();
  140. void WINAPI TS_SecondaryLogonCleanupProcess(PVOID pvIgnored, BOOLEAN bIgnored);
  141. VOID SecondaryLogonCleanupJob(LPVOID pvJobIndex, BOOL *pfLastJob);
  142. BOOL SlpLoadUserProfile(HANDLE hToken, PHANDLE hProfile);
  143. DWORD To_SECONDARYLOGONINFOW(PSECL_SLI pSeclSli, PSECONDARYLOGONINFOW *ppsli);
  144. DWORD To_SECL_SLRI(SECONDARYLOGONRETINFO *pslri, PSECL_SLRI pSeclSlri);
  145. void DbgPrintf( DWORD dwSubSysId, LPCSTR pszFormat , ...)
  146. {
  147. #if DBG
  148. va_list args;
  149. CHAR pszBuffer[1024];
  150. HRESULT hr;
  151. va_start(args, pszFormat);
  152. hr = StringCchVPrintfA(pszBuffer, 1024, pszFormat, args);
  153. if (FAILED(hr))
  154. return;
  155. va_end(args);
  156. OutputDebugStringA(pszBuffer);
  157. #else
  158. UNREFERENCED_PARAMETER(pszFormat);
  159. #endif // #if DBG
  160. UNREFERENCED_PARAMETER(dwSubSysId);
  161. }
  162. BOOL
  163. IsSystemProcess(
  164. VOID
  165. )
  166. {
  167. PTOKEN_USER User;
  168. HANDLE Token;
  169. DWORD RetLen;
  170. PSID SystemSid = NULL;
  171. SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY;
  172. BYTE Buffer[100];
  173. if(AllocateAndInitializeSid(&SidAuthority,1,SECURITY_LOCAL_SYSTEM_RID,
  174. 0,0,0,0,0,0,0,&SystemSid))
  175. {
  176. if(OpenThreadToken(GetCurrentThread(), MAXIMUM_ALLOWED, FALSE, &Token))
  177. {
  178. if(GetTokenInformation(Token, TokenUser, Buffer, 100, &RetLen))
  179. {
  180. User = (PTOKEN_USER)Buffer;
  181. CloseHandle(Token);
  182. if(EqualSid(User->User.Sid, SystemSid))
  183. {
  184. FreeSid(SystemSid);
  185. return TRUE;
  186. }
  187. }
  188. else
  189. CloseHandle(Token);
  190. }
  191. FreeSid(SystemSid);
  192. }
  193. return FALSE;
  194. }
  195. DWORD SlpGetClientSessionId(HANDLE hProcess, DWORD *pdwSessionId)
  196. {
  197. DWORD dwResult;
  198. DWORD dwReturnLen;
  199. HANDLE hToken = NULL;
  200. if (!OpenProcessToken(hProcess, TOKEN_QUERY, &hToken)) {
  201. goto OpenProcessTokenError;
  202. }
  203. if (!GetTokenInformation (hToken, TokenSessionId, pdwSessionId, sizeof(DWORD), &dwReturnLen)) {
  204. goto GetTokenInformationError;
  205. }
  206. dwResult = ERROR_SUCCESS;
  207. ErrorReturn:
  208. if (NULL != hToken) {
  209. CloseHandle(hToken);
  210. }
  211. return dwResult;
  212. SET_DWRESULT(OpenProcessTokenError, GetLastError());
  213. SET_DWRESULT(GetTokenInformationError, GetLastError());
  214. }
  215. DWORD
  216. SlpGetClientLogonId(
  217. HANDLE Process,
  218. PLUID LogonId
  219. )
  220. {
  221. HANDLE Token;
  222. TOKEN_STATISTICS TokenStats;
  223. DWORD ReturnLength;
  224. //
  225. // Get handle to the process token.
  226. //
  227. if(OpenProcessToken(Process, MAXIMUM_ALLOWED, &Token))
  228. {
  229. if(GetTokenInformation (
  230. Token,
  231. TokenStatistics,
  232. (PVOID)&TokenStats,
  233. sizeof( TOKEN_STATISTICS ),
  234. &ReturnLength
  235. ))
  236. {
  237. *LogonId = TokenStats.AuthenticationId;
  238. CloseHandle(Token);
  239. return ERROR_SUCCESS;
  240. }
  241. CloseHandle(Token);
  242. }
  243. return GetLastError();
  244. }
  245. DWORD GetLogonSid(PSID *ppSid)
  246. {
  247. DWORD dwIndex;
  248. DWORD dwResult;
  249. DWORD dwReturnLen = 0;
  250. DWORD dwSidLen;
  251. HANDLE hToken = NULL;
  252. PSID *pSid = NULL;
  253. TOKEN_GROUPS *pTokenGroups = NULL;
  254. // Get the current thread's token
  255. if (!OpenThreadToken(GetCurrentThread(), TOKEN_QUERY, TRUE, &hToken))
  256. goto OpenThreadTokenError;
  257. // Compute the size of this token's groups:
  258. GetTokenInformation(hToken, TokenGroups, NULL, 0, &dwReturnLen);
  259. if (0 == dwReturnLen)
  260. goto GetTokenInformationSizeError;
  261. pTokenGroups = (TOKEN_GROUPS *)HeapAlloc(GetProcessHeap(), 0, dwReturnLen);
  262. if (NULL == pTokenGroups)
  263. goto MemoryError;
  264. // Get this token's groups:
  265. if (!GetTokenInformation(hToken, TokenGroups, pTokenGroups, dwReturnLen, &dwReturnLen))
  266. goto GetTokenInformationError;
  267. // Find the logon sid in this token's groups:
  268. for (dwIndex = 0; dwIndex < pTokenGroups->GroupCount; dwIndex++) {
  269. if (SE_GROUP_LOGON_ID & pTokenGroups->Groups[dwIndex].Attributes ) {
  270. // we've found it. Copy it to a new sid:
  271. dwSidLen = RtlLengthSid(pTokenGroups->Groups[dwIndex].Sid);
  272. pSid = (PSID)HeapAlloc(GetProcessHeap(), 0, dwSidLen);
  273. if (NULL == pSid)
  274. goto MemoryError;
  275. RtlCopySid(dwSidLen, pSid, pTokenGroups->Groups[dwIndex].Sid);
  276. break;
  277. }
  278. }
  279. // Did we find the logon sid?
  280. if (NULL == pSid)
  281. goto NoLogonSidError; // No: this token doesn't have one.
  282. *ppSid = pSid;
  283. pSid = NULL;
  284. dwResult = ERROR_SUCCESS;
  285. ErrorReturn:
  286. if (NULL != hToken)
  287. CloseHandle(hToken);
  288. if (NULL != pTokenGroups)
  289. HeapFree(GetProcessHeap(), 0, pTokenGroups);
  290. if (NULL != pSid)
  291. HeapFree(GetProcessHeap(), 0, pSid);
  292. return dwResult;
  293. SET_DWRESULT(GetTokenInformationError, GetLastError());
  294. SET_DWRESULT(GetTokenInformationSizeError, GetLastError());
  295. SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY);
  296. SET_DWRESULT(NoLogonSidError, ERROR_ACCESS_DENIED);
  297. SET_DWRESULT(OpenThreadTokenError, GetLastError());
  298. }
  299. DWORD DecryptString(UNICODE_STRING usz)
  300. {
  301. DWORD dwResult;
  302. NTSTATUS ntStatus;
  303. // We want the MaximumLength to be greater than Length because we'll want to add a NULL
  304. // termination character to prevent buffer overruns.
  305. if (usz.Length >= usz.MaximumLength)
  306. goto InvalidParameterError;
  307. // We've got the empty string, nothing to decrypt
  308. if (0 == usz.Length)
  309. goto done;
  310. // We should've been passed an encrypted string that's a multiple of the block size
  311. if (0 != ((sizeof(WCHAR)*usz.Length) % RTL_ENCRYPT_MEMORY_SIZE))
  312. goto InvalidParameterError;
  313. // Attempt to decrypt the password
  314. ntStatus = RtlDecryptMemory(usz.Buffer, sizeof(WCHAR)*usz.Length, RTL_ENCRYPT_OPTION_SAME_LOGON);
  315. if (!NT_SUCCESS(ntStatus))
  316. goto RtlDecryptMemoryError;
  317. done:
  318. // Terminate the buffer to prevent overrun attacks (probably already done)
  319. // This is OK because we've already checked that MaximumLength is > Length
  320. usz.Buffer[usz.Length] = L'\0';
  321. dwResult = ERROR_SUCCESS;
  322. ErrorReturn:
  323. return dwResult;
  324. SET_DWRESULT(InvalidParameterError, ERROR_INVALID_PARAMETER);
  325. SET_DWRESULT(RtlDecryptMemoryError, RtlNtStatusToDosError(ntStatus));
  326. }
  327. void DestroyAuthInfo(BOOL bUseNTLM, PVOID pvAuthInfo)
  328. {
  329. UNICODE_STRING *pustrPasswd;
  330. if (bUseNTLM)
  331. {
  332. pustrPasswd = &(((MSV1_0_INTERACTIVE_LOGON *)pvAuthInfo)->Password);
  333. } else
  334. {
  335. pustrPasswd = &(((KERB_INTERACTIVE_LOGON *)pvAuthInfo)->Password);
  336. }
  337. // this was allocated with HEAP_ZERO_MEMORY, so this check is valid
  338. if (NULL != pustrPasswd->Buffer)
  339. {
  340. // will the compiler optimize this out?
  341. SecureZeroMemory(pustrPasswd->Buffer, pustrPasswd->Length);
  342. }
  343. HeapFree(GetProcessHeap(), 0, pvAuthInfo);
  344. }
  345. DWORD MakeAuthInfo(BOOL bUseNTLM, LPWSTR pwszUserName, LPWSTR pwszDomainName, LPWSTR pwszPasswd, ULONG *pulAuthPackage, PVOID *ppvAuthInfo, ULONG *pulAuthInfoLength)
  346. {
  347. DWORD dwResult;
  348. HRESULT hr;
  349. LPBYTE pbCurrent;
  350. LPBYTE pbEnd;
  351. PVOID pvAuthInfo = NULL;
  352. ULONG cbAuthInfoStrings;
  353. ULONG ulAuthInfoLength;
  354. ULONG ulAuthPackage;
  355. UNICODE_STRING *pustrUserName;
  356. UNICODE_STRING *pustrDomainName;
  357. UNICODE_STRING *pustrPasswd;
  358. if (bUseNTLM)
  359. {
  360. ulAuthPackage = g_state.hMSVPackage;
  361. ulAuthInfoLength = sizeof(MSV1_0_INTERACTIVE_LOGON);
  362. } else
  363. {
  364. ulAuthPackage = g_state.hKerbPackage;
  365. ulAuthInfoLength = sizeof(KERB_INTERACTIVE_LOGON);
  366. }
  367. ulAuthInfoLength += (ULONG)(sizeof(WCHAR) * (wcslen(pwszUserName)));
  368. ulAuthInfoLength += (ULONG)(sizeof(WCHAR) * (wcslen(pwszDomainName)));
  369. ulAuthInfoLength += (ULONG)(sizeof(WCHAR) * (wcslen(pwszPasswd)+1));
  370. // zero out the allocated memory (checked by DestroyAuthInfo)
  371. pvAuthInfo = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, ulAuthInfoLength);
  372. if (NULL == pvAuthInfo)
  373. goto MemoryError;
  374. // fill out the auth info structure:
  375. if (bUseNTLM)
  376. {
  377. ((MSV1_0_INTERACTIVE_LOGON *)pvAuthInfo)->MessageType = MsV1_0InteractiveLogon;
  378. pustrUserName = &(((MSV1_0_INTERACTIVE_LOGON *)pvAuthInfo)->UserName);
  379. pustrDomainName = &(((MSV1_0_INTERACTIVE_LOGON *)pvAuthInfo)->LogonDomainName);
  380. pustrPasswd = &(((MSV1_0_INTERACTIVE_LOGON *)pvAuthInfo)->Password);
  381. pbCurrent = ((LPBYTE)pvAuthInfo) + sizeof(MSV1_0_INTERACTIVE_LOGON);
  382. cbAuthInfoStrings = ulAuthInfoLength - sizeof(MSV1_0_INTERACTIVE_LOGON);
  383. } else
  384. {
  385. ((KERB_INTERACTIVE_LOGON *)pvAuthInfo)->MessageType = KerbInteractiveLogon;
  386. pustrUserName = &(((KERB_INTERACTIVE_LOGON *)pvAuthInfo)->UserName);
  387. pustrDomainName = &(((KERB_INTERACTIVE_LOGON *)pvAuthInfo)->LogonDomainName);
  388. pustrPasswd = &(((KERB_INTERACTIVE_LOGON *)pvAuthInfo)->Password);
  389. pbCurrent = ((LPBYTE)pvAuthInfo) + sizeof(KERB_INTERACTIVE_LOGON);
  390. cbAuthInfoStrings = ulAuthInfoLength - sizeof(KERB_INTERACTIVE_LOGON);
  391. }
  392. // Initialize a pointer to the end of our buffer.
  393. pbEnd = pbCurrent + cbAuthInfoStrings;
  394. // Copy the auth info strings into our allocated buffer
  395. hr = StringCbCopy((WCHAR *)pbCurrent, pbEnd-pbCurrent, pwszUserName);
  396. if (FAILED(hr))
  397. goto StringCchCopyError;
  398. RtlInitUnicodeString(pustrUserName, (LPWSTR)pbCurrent);
  399. pbCurrent += sizeof(WCHAR) * (wcslen(pwszUserName));
  400. hr = StringCbCopy((WCHAR *)pbCurrent, pbEnd-pbCurrent, pwszDomainName);
  401. if (FAILED(hr))
  402. goto StringCchCopyError;
  403. RtlInitUnicodeString(pustrDomainName, (LPWSTR)pbCurrent);
  404. pbCurrent += sizeof(WCHAR) * (wcslen(pwszDomainName));
  405. hr = StringCbCopy((WCHAR *)pbCurrent, pbEnd-pbCurrent, pwszPasswd);
  406. if (FAILED(hr))
  407. goto StringCchCopyError;
  408. RtlInitUnicodeString(pustrPasswd, (LPWSTR)pbCurrent);
  409. pbCurrent += sizeof(WCHAR) * (wcslen(pwszPasswd));
  410. *pulAuthPackage = ulAuthPackage;
  411. *ppvAuthInfo = pvAuthInfo;
  412. pvAuthInfo = NULL;
  413. *pulAuthInfoLength = ulAuthInfoLength;
  414. dwResult = ERROR_SUCCESS;
  415. ErrorReturn:
  416. if (NULL != pvAuthInfo)
  417. DestroyAuthInfo(bUseNTLM, pvAuthInfo);
  418. return dwResult;
  419. SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY);
  420. SET_DWRESULT(StringCchCopyError, (DWORD)hr);
  421. }
  422. DWORD LogonUserWrap(LPWSTR pwszUserName,
  423. LPWSTR pwszDomainName,
  424. UNICODE_STRING uszPassword,
  425. DWORD dwLogonType,
  426. DWORD dwLogonProvider,
  427. PSID pLogonSid,
  428. HANDLE *phToken,
  429. LPWSTR *ppwszProfilePath)
  430. {
  431. BOOL bUseNTLM = FALSE;
  432. DWORD cTokenGroups;
  433. DWORD dwLengthSid;
  434. DWORD dwResult;
  435. HRESULT hr;
  436. KERB_INTERACTIVE_LOGON kerbLogon;
  437. LPWSTR pwszProfilePath = NULL;
  438. MSV1_0_INTERACTIVE_LOGON msv10Logon;
  439. NTSTATUS ntStatus;
  440. PSID pLocalSid = NULL;
  441. PUNICODE_STRING puszProfilePath = NULL; SID_IDENTIFIER_AUTHORITY LocalSidAuthority = SECURITY_LOCAL_SID_AUTHORITY;
  442. // Declare the parameters to LsaLogonUser:
  443. LSA_STRING lsastr_OriginName;
  444. SECURITY_LOGON_TYPE sltLogonType;
  445. ULONG ulAuthPackage;
  446. PVOID pvAuthInfo = NULL;
  447. ULONG ulAuthInfoLength;
  448. TOKEN_GROUPS *pTokenGroups = NULL;
  449. TOKEN_SOURCE sourceContext;
  450. PVOID pvProfileBuffer = NULL;
  451. ULONG ulProfileBufferLength;
  452. LUID LogonId;
  453. QUOTA_LIMITS quotas;
  454. NTSTATUS ntSubStatus;
  455. ZeroMemory(&kerbLogon, sizeof(kerbLogon));
  456. ZeroMemory(&msv10Logon, sizeof(msv10Logon));
  457. // Map NULLs to the empty strings so we don't have to deal with them:
  458. if (NULL == pwszUserName)
  459. pwszUserName = L"";
  460. if (NULL == pwszDomainName)
  461. pwszDomainName = L"";
  462. if (NULL == uszPassword.Buffer)
  463. {
  464. uszPassword.Buffer = L"";
  465. uszPassword.Length = 0;
  466. uszPassword.MaximumLength = 1;
  467. }
  468. // Check if we use NTLM or not:
  469. if (NULL == pwszDomainName || L'\0' == pwszDomainName[0])
  470. {
  471. if (NULL == wcschr(pwszUserName, L'@'))
  472. {
  473. bUseNTLM = TRUE;
  474. }
  475. }
  476. RtlInitString(&lsastr_OriginName, "seclogon");
  477. sltLogonType = dwLogonType;
  478. // The password is current encrypted. Need to decrypt it before we can use it:
  479. dwResult = DecryptString(uszPassword);
  480. if (ERROR_SUCCESS != dwResult)
  481. goto DecryptStringError;
  482. // Get the values of the auth-package dependendent LsaLogonUser parameters.
  483. dwResult = MakeAuthInfo(bUseNTLM, pwszUserName, pwszDomainName, uszPassword.Buffer, &ulAuthPackage, &pvAuthInfo, &ulAuthInfoLength);
  484. if (ERROR_SUCCESS != dwResult)
  485. goto GetAuthInfoError;
  486. // We don't need the password anymore. Zero it out:
  487. SecureZeroMemory(uszPassword.Buffer, sizeof(WCHAR)*uszPassword.Length);
  488. // If the caller specified a logon sid, cram it into a TOKEN_GROUPS structure
  489. if (NULL != pLogonSid)
  490. {
  491. // BUG 522969: If we specify a token groups to LsaLogonUser, it's not going to add the local sid to the token groups.
  492. // So, we have to do that ourselves:
  493. dwLengthSid = RtlLengthRequiredSid(1);
  494. pLocalSid = (PSID)HeapAlloc(GetProcessHeap(), 0, dwLengthSid);
  495. if (NULL == pLocalSid)
  496. goto MemoryError;
  497. RtlInitializeSid(pLocalSid, &LocalSidAuthority, 1);
  498. *(RtlSubAuthoritySid(pLocalSid, 0)) = SECURITY_LOCAL_RID;
  499. // Initialize a TOKEN_GROUPS structure
  500. cTokenGroups = 2; // the local sid, and the logon sid
  501. pTokenGroups = (TOKEN_GROUPS *)HeapAlloc(GetProcessHeap(), 0, sizeof(TOKEN_GROUPS) + ((cTokenGroups - ANYSIZE_ARRAY) * sizeof(SID_AND_ATTRIBUTES)));
  502. if (NULL == pTokenGroups)
  503. goto MemoryError;
  504. pTokenGroups->GroupCount = cTokenGroups;
  505. pTokenGroups->Groups[0].Sid = pLogonSid;
  506. pTokenGroups->Groups[0].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT | SE_GROUP_LOGON_ID;
  507. pTokenGroups->Groups[1].Sid = pLocalSid;
  508. pTokenGroups->Groups[1].Attributes = SE_GROUP_MANDATORY | SE_GROUP_ENABLED | SE_GROUP_ENABLED_BY_DEFAULT;
  509. }
  510. strncpy(sourceContext.SourceName, "seclogon ", sizeof(sourceContext.SourceName));
  511. ntStatus = LsaLogonUser
  512. (g_state.hLSA,
  513. &lsastr_OriginName,
  514. sltLogonType,
  515. ulAuthPackage,
  516. pvAuthInfo,
  517. ulAuthInfoLength,
  518. pTokenGroups,
  519. &sourceContext,
  520. &pvProfileBuffer,
  521. &ulProfileBufferLength,
  522. &LogonId,
  523. phToken,
  524. &quotas,
  525. &ntSubStatus);
  526. if (!NT_SUCCESS(ntStatus))
  527. goto LsaLogonUserError;
  528. if (!NT_SUCCESS(ntSubStatus)) {
  529. ntStatus = ntSubStatus;
  530. goto LsaLogonUserError;
  531. }
  532. if (NULL != pvProfileBuffer) {
  533. puszProfilePath = &(((MSV1_0_INTERACTIVE_PROFILE *)pvProfileBuffer)->ProfilePath);
  534. if (NULL != puszProfilePath->Buffer) {
  535. pwszProfilePath = (LPWSTR)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(WCHAR)*(puszProfilePath->Length+1));
  536. if (NULL == pwszProfilePath)
  537. goto MemoryError;
  538. hr = StringCchCopy(pwszProfilePath, puszProfilePath->Length+1, puszProfilePath->Buffer);
  539. if (FAILED(hr))
  540. goto StringCchCopyError;
  541. }
  542. }
  543. *ppwszProfilePath = pwszProfilePath;
  544. pwszProfilePath = NULL;
  545. dwResult = ERROR_SUCCESS;
  546. ErrorReturn:
  547. if (NULL != pvAuthInfo)
  548. DestroyAuthInfo(bUseNTLM, pvAuthInfo);
  549. if (NULL != pLocalSid)
  550. HeapFree(GetProcessHeap(), 0, pLocalSid);
  551. if (NULL != pTokenGroups)
  552. HeapFree(GetProcessHeap(), 0, pTokenGroups);
  553. if (NULL != pvProfileBuffer)
  554. LsaFreeReturnBuffer(pvProfileBuffer);
  555. if (NULL != pwszProfilePath)
  556. HeapFree(GetProcessHeap(), 0, pwszProfilePath);
  557. return dwResult;
  558. SET_DWRESULT(DecryptStringError, dwResult);
  559. SET_DWRESULT(GetAuthInfoError, dwResult);
  560. SET_DWRESULT(LsaLogonUserError, RtlNtStatusToDosError(ntStatus));
  561. SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY);
  562. SET_DWRESULT(StringCchCopyError, (DWORD)hr);
  563. UNREFERENCED_PARAMETER(dwLogonProvider);
  564. }
  565. DWORD
  566. WINAPI
  567. WaitForNextJobTermination(PVOID pvIgnored)
  568. {
  569. BOOL fResult;
  570. DWORD dwNumberOfBytes;
  571. DWORD dwResult;
  572. OVERLAPPED *po;
  573. ULONG_PTR ulptrCompletionKey;
  574. for (;;)
  575. {
  576. fResult = GetQueuedCompletionStatus(g_hIOCP, &dwNumberOfBytes, &ulptrCompletionKey, &po, INFINITE);
  577. if (!fResult) {
  578. // We've encountered an error. Shutdown our cleanup thread -- the next runas will queue another one.
  579. EnterCriticalSection(&csForProcessCount);
  580. g_fCleanupThreadActive = FALSE;
  581. LeaveCriticalSection(&csForProcessCount);
  582. goto GetQueuedCompletionStatusError;
  583. }
  584. // When waiting on job objects, the dwNumberOfBytes contains a message ID, indicating
  585. // the event which just occured.
  586. switch (dwNumberOfBytes)
  587. {
  588. case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
  589. {
  590. BOOL fLastJob;
  591. // All of our processes have terminated. Call our cleanup function.
  592. SecondaryLogonCleanupJob((LPVOID)ulptrCompletionKey /*job index*/, &fLastJob);
  593. if (fLastJob)
  594. {
  595. // There are no more jobs -- we're done processing notification.
  596. goto CommonReturn;
  597. }
  598. else
  599. {
  600. // More jobs left to clean up. Keep processing...
  601. }
  602. }
  603. default:;
  604. // some message we don't care about. Try again.
  605. }
  606. }
  607. CommonReturn:
  608. dwResult = ERROR_SUCCESS;
  609. ErrorReturn:
  610. return dwResult;
  611. SET_DWRESULT(GetQueuedCompletionStatusError, GetLastError());
  612. UNREFERENCED_PARAMETER(pvIgnored);
  613. }
  614. VOID
  615. SecondaryLogonCleanupJob(
  616. LPVOID pvslj,
  617. BOOL *pfLastJob
  618. )
  619. /*++
  620. Routine Description:
  621. This routine is a process cleanup handler when one of the secondary
  622. logon process goes away.
  623. Arguments:
  624. dwProcessIndex -- the actual index to the process, the pointer is cast
  625. back to dword. THIS IS SAFE IN SUNDOWN.
  626. fWaitStatus -- status of the wait done by one of services.exe threads.
  627. Return Value:
  628. always 0.
  629. --*/
  630. {
  631. SecondaryLogonJob *pslj = (SecondaryLogonJob *)pvslj;
  632. EnterCriticalSection(&csForProcessCount);
  633. if (NULL != pslj->hJob) {
  634. CloseHandle(pslj->hJob);
  635. }
  636. if (NULL != pslj->hRegisteredProcessTerminated) {
  637. UnregisterWaitEx(pslj->hRegisteredProcessTerminated, 0 /*don't wait*/);
  638. }
  639. if (NULL != pslj->hProcess) {
  640. CloseHandle(pslj->hProcess);
  641. }
  642. if (NULL != pslj->hProfile) {
  643. UnloadUserProfile(pslj->hToken, pslj->hProfile);
  644. }
  645. if (NULL != pslj->hToken) {
  646. CloseHandle(pslj->hToken);
  647. }
  648. if (NULL != pslj->psli) {
  649. Free_SECONDARYLOGONINFOW(pslj->psli);
  650. }
  651. // Unlink us from the list of jobs
  652. RemoveEntryList(&pslj->list);
  653. // Free the list element, if it was allocated.
  654. if (pslj->fHeapAllocated) {
  655. HeapFree(GetProcessHeap(), 0, pslj);
  656. }
  657. // If the list is empty, we don't need the cleanup thread anymore.
  658. *pfLastJob = IsListEmpty(&g_state.JobListHead);
  659. // If it's the last job, the cleanup thread terminates:
  660. g_fCleanupThreadActive = !(*pfLastJob) && g_fCleanupThreadActive;
  661. // Update the service status to reflect whether there is a runas'd process alive.
  662. MySetServiceStatus(SERVICE_RUNNING, 0, 0, 0);
  663. LeaveCriticalSection(&csForProcessCount);
  664. return;
  665. }
  666. void
  667. WINAPI
  668. TS_SecondaryLogonCleanupProcess(PVOID pvIndex, BOOLEAN bIgnored)
  669. {
  670. BOOL bIgnored2;
  671. SecondaryLogonCleanupJob(pvIndex, &bIgnored2);
  672. UNREFERENCED_PARAMETER(bIgnored);
  673. }
  674. DWORD
  675. APIENTRY
  676. SecondaryLogonProcessWatchdogNewProcess(
  677. PSECONDARYLOGONWATCHINFO dwParam
  678. )
  679. /*++
  680. Routine Description:
  681. This routine puts the secondary logon process created on the wait queue
  682. such that cleanup can be done after the process dies.
  683. Arguments:
  684. dwParam -- the pointer to the process information.
  685. Return Value:
  686. none.
  687. --*/
  688. {
  689. BOOL fFailedAllocation = FALSE;
  690. BOOL fEnteredCriticalSection = FALSE;
  691. DWORD dwResult;
  692. JOBOBJECT_ASSOCIATE_COMPLETION_PORT joacp;
  693. LUID ProcessLogonId;
  694. PLIST_ENTRY ple;
  695. SecondaryLogonJob *psljNew = NULL;
  696. SecondaryLogonJob sljDummy;
  697. ZeroMemory(&sljDummy, sizeof(sljDummy));
  698. __try {
  699. if (dwParam != NULL) {
  700. PSECONDARYLOGONWATCHINFO pslwi = (PSECONDARYLOGONWATCHINFO) dwParam;
  701. EnterCriticalSection(&csForProcessCount);
  702. fEnteredCriticalSection = TRUE;
  703. psljNew = (SecondaryLogonJob *)HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(SecondaryLogonJob));
  704. if (NULL == psljNew) {
  705. // Couldn't allocate a new SecondaryLogonJob object in which to hold cleanup info. Use a dummy
  706. // stack object and perform the cleanup immediately.
  707. fFailedAllocation = TRUE;
  708. psljNew = &sljDummy;
  709. }
  710. // Insert the new SecondaryLogonJob into the list of jobs to clean up. If we fail to fully register
  711. // for cleanup, we'll remove it from the list ourselves
  712. InsertTailList(&g_state.JobListHead, &psljNew->list);
  713. psljNew->hProcess = pslwi->hProcess;
  714. psljNew->hToken = pslwi->hToken;
  715. psljNew->hProfile = pslwi->hProfile;
  716. psljNew->psli = pslwi->psli;
  717. psljNew->fHeapAllocated = !fFailedAllocation;
  718. if (fFailedAllocation) {
  719. goto MemoryError;
  720. }
  721. // Initialize this job with the logon ID of the client process.
  722. // If this is a recursive runas, we'll override this value in the following loop
  723. psljNew->RootLogonId.LowPart = pslwi->LogonId.LowPart;
  724. psljNew->RootLogonId.HighPart = pslwi->LogonId.HighPart;
  725. // Search this list of active jobs and determine which logon session the new process should be associated with.
  726. for (ple = g_state.JobListHead.Flink; ple != &g_state.JobListHead; ple = ple->Flink)
  727. {
  728. SecondaryLogonJob *pslj = CONTAINING_RECORD(ple, SecondaryLogonJob, list);
  729. SlpGetClientLogonId(pslj->hProcess, &ProcessLogonId);
  730. if(ProcessLogonId.LowPart == pslwi->LogonId.LowPart && ProcessLogonId.HighPart == pslwi->LogonId.HighPart)
  731. {
  732. psljNew->RootLogonId.LowPart = pslj->RootLogonId.LowPart;
  733. psljNew->RootLogonId.HighPart = pslj->RootLogonId.HighPart;
  734. break;
  735. }
  736. }
  737. // Now determine what kind of cleanup we're going to do. In the non-TS (session 0) case, we'll
  738. // add the process to a job and cleanup when the process count goes to 0. In the TS case,
  739. // we'll wait on the process handle and cleanup when it terminates. This is the best we can do,
  740. // as there is no support for cross-session jobs.
  741. if (0 == pslwi->dwClientSessionId) {
  742. // The console login case
  743. psljNew->hJob = CreateJobObject(NULL, NULL);
  744. if (NULL == psljNew->hJob)
  745. goto CreateJobObjectError;
  746. if (!AssignProcessToJobObject(psljNew->hJob, psljNew->hProcess))
  747. goto AssignProcessToJobObjectError;
  748. // Register our IO completion port to wait for events from this job:
  749. joacp.CompletionKey = (LPVOID)psljNew;
  750. joacp.CompletionPort = g_hIOCP;
  751. if (!SetInformationJobObject(psljNew->hJob, JobObjectAssociateCompletionPortInformation, &joacp, sizeof(joacp)))
  752. goto SetInformationJobObjectError;
  753. // If we don't already have a cleanup thread running, start one now:
  754. if (!g_fCleanupThreadActive)
  755. {
  756. // NOTE: it's acceptable for this to fail -- we'll just cleanup on the next runas
  757. g_fCleanupThreadActive = QueueUserWorkItem(WaitForNextJobTermination, NULL, WT_EXECUTELONGFUNCTION);
  758. }
  759. } else {
  760. // The TS case
  761. // We can't perform cleanup if we can't add the process to a job. The best we can do
  762. // for now is to hope that this is a termsrv client (highly likely), and that csrss will
  763. // do the cleanup for us.
  764. //
  765. // csrss won't unload user profiles, so we'll still have to do that. Wait until the
  766. // process terminates and unload then. We might unload profiles when an application doesn't
  767. // want it unloaded, but they can prevent this by holding open a key in the hive.
  768. //
  769. // This must be doc'd in CPWL documentation.
  770. //
  771. if (!RegisterWaitForSingleObject
  772. (&psljNew->hRegisteredProcessTerminated,
  773. psljNew->hProcess,
  774. TS_SecondaryLogonCleanupProcess,
  775. (PVOID)psljNew,
  776. INFINITE,
  777. WT_EXECUTEONLYONCE)) {
  778. goto RegisterWaitForSingleObjectError;
  779. }
  780. }
  781. // we've registered cleanup for this, we don't need to free it here anymore
  782. psljNew = NULL;
  783. // Update the service status to reflect that there is a runas'd process
  784. // This prevents the service from receiving SERVICE_STOP controls
  785. // while runas'd processes are alive.
  786. // NOTE: this will only be correct if called *AFTER* InsertTailList. It bases its
  787. // decision as to whether to allow a SERVICE_STOP control on whether there are any active
  788. // processes in this list.
  789. MySetServiceStatus(SERVICE_RUNNING, 0, 0, 0);
  790. } else {
  791. //
  792. // We were just awakened in order to terminate the service (nothing to do)
  793. //
  794. }
  795. } __except (EXCEPTION_EXECUTE_HANDLER) {
  796. dwResult = ERROR_INVALID_DATA; // don't give an attacker exception codes
  797. goto ExceptionError;
  798. }
  799. dwResult = ERROR_SUCCESS;
  800. ErrorReturn:
  801. if (NULL != psljNew) {
  802. BOOL bIgnored;
  803. // An error has occured. Terminate the process and cleanup after ourselves.
  804. TerminateProcess(psljNew->hProcess, dwResult);
  805. // NOTE: we want to make this call while holding the critsec, in case winlogon tries to shut us down while we're
  806. // performing this cleanup.
  807. SecondaryLogonCleanupJob(psljNew, &bIgnored);
  808. }
  809. if (fEnteredCriticalSection) {
  810. LeaveCriticalSection(&csForProcessCount);
  811. }
  812. return dwResult;
  813. SET_DWRESULT(AssignProcessToJobObjectError, GetLastError());
  814. SET_DWRESULT(CreateJobObjectError, GetLastError());
  815. SET_DWRESULT(ExceptionError, dwResult);
  816. SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY);
  817. SET_DWRESULT(RegisterWaitForSingleObjectError, GetLastError());
  818. SET_DWRESULT(SetInformationJobObjectError, GetLastError());
  819. }
  820. DWORD ServiceStop(BOOL fShutdown, DWORD dwExitCode)
  821. {
  822. DWORD dwCheckPoint = 0;
  823. DWORD dwResult;
  824. // Don't want the process count to change while we're shutting down the service!
  825. EnterCriticalSection(&csForProcessCount);
  826. // Only stop if we have no runas'd processes, or if we're shutting down
  827. if (fShutdown || IsListEmpty(&g_state.JobListHead)) {
  828. dwResult = MySetServiceStatus(SERVICE_STOP_PENDING, dwCheckPoint++, 0, 0);
  829. _JumpCondition(ERROR_SUCCESS != dwResult && !fShutdown, MySetServiceStatusError);
  830. // We shouldn't hold the critical section while we're shutting down the RPC server,
  831. // because RPC threads may be trying to acquire it.
  832. LeaveCriticalSection(&csForProcessCount);
  833. dwResult = SeclStopRpcServer();
  834. _JumpCondition(ERROR_SUCCESS != dwResult && !fShutdown, SeclStopRpcServerError);
  835. dwResult = MySetServiceStatus(SERVICE_STOP_PENDING, dwCheckPoint++, 0, 0);
  836. _JumpCondition(ERROR_SUCCESS != dwResult && !fShutdown, MySetServiceStatusError);
  837. if (g_fIsCsInitialized)
  838. {
  839. DeleteCriticalSection(&csForProcessCount);
  840. g_fIsCsInitialized = FALSE;
  841. }
  842. if (NULL != g_hIOCP)
  843. {
  844. CloseHandle(g_hIOCP);
  845. g_hIOCP = NULL;
  846. }
  847. // Unlike MySetServiceStatus, this routine doesn't access any
  848. // global state which could have been freed:
  849. dwResult = MySetServiceStopped(dwExitCode);
  850. _JumpCondition(ERROR_SUCCESS != dwResult && !fShutdown, MySetServiceStopped);
  851. }
  852. dwResult = ERROR_SUCCESS;
  853. ErrorReturn:
  854. return dwResult;
  855. SET_DWRESULT(MySetServiceStatusError, dwResult);
  856. SET_DWRESULT(MySetServiceStopped, dwResult);
  857. SET_DWRESULT(SeclStopRpcServerError, dwResult);
  858. }
  859. void
  860. WINAPI
  861. ServiceHandler(
  862. DWORD fdwControl
  863. )
  864. /*++
  865. Routine Description:
  866. Service handler which wakes up the main service thread when ever
  867. service controller needs to send a message.
  868. Arguments:
  869. fdwControl -- the control from the service controller.
  870. Return Value:
  871. none.
  872. --*/
  873. {
  874. DWORD dwNextState = g_state.serviceStatus.dwCurrentState;
  875. DWORD dwResult;
  876. switch (fdwControl)
  877. {
  878. case SERVICE_CONTROL_CONTINUE:
  879. dwResult = MySetServiceStatus(SERVICE_CONTINUE_PENDING, 0, 0, 0);
  880. _JumpCondition(ERROR_SUCCESS != dwResult, MySetServiceStatusError);
  881. dwResult = SeclStartRpcServer();
  882. _JumpCondition(ERROR_SUCCESS != dwResult, StartRpcServerError);
  883. dwNextState = SERVICE_RUNNING;
  884. break;
  885. case SERVICE_CONTROL_INTERROGATE:
  886. break;
  887. case SERVICE_CONTROL_PAUSE:
  888. dwResult = MySetServiceStatus(SERVICE_PAUSE_PENDING, 0, 0, 0);
  889. _JumpCondition(ERROR_SUCCESS != dwResult, MySetServiceStatusError);
  890. dwResult = SeclStopRpcServer();
  891. _JumpCondition(ERROR_SUCCESS != dwResult, StopRpcServerError);
  892. dwNextState = SERVICE_PAUSED;
  893. break;
  894. case SERVICE_CONTROL_STOP:
  895. dwResult = ServiceStop(FALSE /*fShutdown*/, ERROR_SUCCESS);
  896. _JumpCondition(ERROR_SUCCESS != dwResult, ServiceStopError);
  897. return ; // All global state has been freed, just exit.
  898. case SERVICE_CONTROL_SHUTDOWN:
  899. dwResult = ServiceStop(TRUE /*fShutdown*/, ERROR_SUCCESS);
  900. _JumpCondition(ERROR_SUCCESS != dwResult, ServiceStopError);
  901. return ; // All global state has been freed, just exit.
  902. default:
  903. // Unhandled service control!
  904. goto ErrorReturn;
  905. }
  906. CommonReturn:
  907. // Restore the original state on error, set the new state on success.
  908. dwResult = MySetServiceStatus(dwNextState, 0, 0, 0);
  909. return;
  910. ErrorReturn:
  911. goto CommonReturn;
  912. SET_ERROR(MySetServiceStatusError, dwResult);
  913. SET_ERROR(ServiceStopError, dwResult);
  914. SET_ERROR(StartRpcServerError, dwResult);
  915. SET_ERROR(StopRpcServerError, dwResult);
  916. }
  917. VOID
  918. SlrCreateProcessWithLogon
  919. (IN RPC_BINDING_HANDLE hRPCBinding,
  920. IN PSECONDARYLOGONINFOW *ppsli,
  921. OUT PSECONDARYLOGONRETINFO pslri)
  922. /*++
  923. Routine Description:
  924. The core routine -- it handles a client request to start a secondary
  925. logon process.
  926. Arguments:
  927. psli -- the input structure with client request information
  928. pslri -- the output structure with response back to the client.
  929. Return Value:
  930. none.
  931. --*/
  932. {
  933. HANDLE hCurrentThreadToken = NULL;
  934. HANDLE hToken = NULL;
  935. HANDLE hProfile = NULL;
  936. HANDLE hProcessClient = NULL;
  937. BOOL fCreatedEnvironmentBlock = FALSE;
  938. BOOL fIsImpersonatingRpcClient = FALSE;
  939. BOOL fIsImpersonatingClient = FALSE;
  940. BOOL fInheritHandles = FALSE;
  941. BOOL fOpenedSTDIN = FALSE;
  942. BOOL fOpenedSTDOUT = FALSE;
  943. BOOL fOpenedSTDERR = FALSE;
  944. SECURITY_ATTRIBUTES sa;
  945. PSECONDARYLOGONINFOW psli = *ppsli;
  946. SECONDARYLOGONWATCHINFO slwi;
  947. DWORD dwResult = ERROR_INVALID_PARAMETER; // Correct error code if we fail immediately
  948. DWORD SessionId;
  949. DWORD dwLogonProvider;
  950. SECURITY_LOGON_TYPE LogonType;
  951. PSID pLogonSid = NULL;
  952. LPWSTR pwszProfilePath = NULL;
  953. PROFILEINFO pi;
  954. WCHAR szTemp [ UNLEN + 1 ];
  955. LPWSTR pszUserName = NULL;
  956. HANDLE hClientProcessToken ;
  957. PRIVILEGE_SET ImpersonatePrivilege ;
  958. BOOL PrivilegeTest ;
  959. ZeroMemory(&pi, sizeof(pi));
  960. __try {
  961. //
  962. // Do some security checks:
  963. //
  964. // 1) We should impersonate the client and then try to open
  965. // the process so that we are assured that they didn't
  966. // give us some fake id.
  967. //
  968. dwResult = RpcImpersonateClient(hRPCBinding);
  969. _JumpCondition(RPC_S_OK != dwResult, leave_with_last_error);
  970. fIsImpersonatingRpcClient = TRUE;
  971. hProcessClient = OpenProcess(PROCESS_ALL_ACCESS, FALSE, psli->dwProcessId);
  972. _JumpCondition(hProcessClient == NULL, leave_with_last_error);
  973. #if 0
  974. //
  975. // 2) Check that the client is not running from a restricted account.
  976. //
  977. hCurrentThread = GetCurrentThread(); // Doesn't need to be freed with CloseHandle().
  978. _JumpCondition(NULL == hCurrentThread, leave_with_last_error);
  979. _JumpCondition(FALSE == OpenThreadToken(hCurrentThread,
  980. TOKEN_QUERY | TOKEN_DUPLICATE,
  981. TRUE,
  982. &hCurrentThreadToken),
  983. leave_with_last_error);
  984. #endif
  985. dwResult = RpcRevertToSelfEx(hRPCBinding);
  986. if (RPC_S_OK != dwResult)
  987. {
  988. __leave;
  989. }
  990. fIsImpersonatingRpcClient = FALSE;
  991. #if 0
  992. if (TRUE == IsTokenUntrusted(hCurrentThreadToken))
  993. {
  994. dwResult = ERROR_ACCESS_DENIED;
  995. __leave;
  996. }
  997. #endif
  998. //
  999. // We should get the session id from process id
  1000. // we will set this up in the token so that create process
  1001. // happens on the correct session.
  1002. //
  1003. _JumpCondition(!ProcessIdToSessionId(psli->dwProcessId, &SessionId), leave_with_last_error);
  1004. //
  1005. // Get the unique logonId.
  1006. // we will use this to cleanup any running processes
  1007. // when the logoff happens.
  1008. //
  1009. dwResult = SlpGetClientLogonId(hProcessClient, &slwi.LogonId);
  1010. if(dwResult != ERROR_SUCCESS)
  1011. {
  1012. __leave;
  1013. }
  1014. dwResult = SlpGetClientSessionId(hProcessClient, &slwi.dwClientSessionId);
  1015. if (dwResult != ERROR_SUCCESS)
  1016. {
  1017. __leave;
  1018. }
  1019. if ((psli->lpStartupInfo->dwFlags & STARTF_USESTDHANDLES) != 0)
  1020. {
  1021. _JumpCondition(!DuplicateHandle
  1022. (hProcessClient,
  1023. psli->lpStartupInfo->hStdInput,
  1024. GetCurrentProcess(),
  1025. &psli->lpStartupInfo->hStdInput,
  1026. 0,
  1027. TRUE, DUPLICATE_SAME_ACCESS),
  1028. leave_with_last_error);
  1029. fOpenedSTDIN = TRUE;
  1030. _JumpCondition(!DuplicateHandle
  1031. (hProcessClient,
  1032. psli->lpStartupInfo->hStdOutput,
  1033. GetCurrentProcess(),
  1034. &psli->lpStartupInfo->hStdOutput,
  1035. 0,
  1036. TRUE,
  1037. DUPLICATE_SAME_ACCESS),
  1038. leave_with_last_error);
  1039. fOpenedSTDOUT = TRUE;
  1040. _JumpCondition(!DuplicateHandle
  1041. (hProcessClient,
  1042. psli->lpStartupInfo->hStdError,
  1043. GetCurrentProcess(),
  1044. &psli->lpStartupInfo->hStdError,
  1045. 0,
  1046. TRUE,
  1047. DUPLICATE_SAME_ACCESS),
  1048. leave_with_last_error);
  1049. fOpenedSTDERR = TRUE;
  1050. fInheritHandles = TRUE;
  1051. }
  1052. else
  1053. {
  1054. psli->lpStartupInfo->hStdInput = INVALID_HANDLE_VALUE;
  1055. psli->lpStartupInfo->hStdOutput = INVALID_HANDLE_VALUE;
  1056. psli->lpStartupInfo->hStdError = INVALID_HANDLE_VALUE;
  1057. }
  1058. if(psli->dwLogonFlags & LOGON_NETCREDENTIALS_ONLY)
  1059. {
  1060. LogonType = (SECURITY_LOGON_TYPE)LOGON32_LOGON_NEW_CREDENTIALS;
  1061. dwLogonProvider = LOGON32_PROVIDER_WINNT50;
  1062. }
  1063. else
  1064. {
  1065. LogonType = (SECURITY_LOGON_TYPE) LOGON32_LOGON_INTERACTIVE;
  1066. dwLogonProvider = LOGON32_PROVIDER_DEFAULT;
  1067. }
  1068. // LogonUser does not return profile information, we need to grab
  1069. // that out of band after the logon has completed.
  1070. //
  1071. dwResult = RpcImpersonateClient(hRPCBinding);
  1072. _JumpCondition(RPC_S_OK != dwResult, leave_with_last_error);
  1073. fIsImpersonatingRpcClient = TRUE;
  1074. if (0 == (SECLOGON_CALLER_SPECIFIED_DESKTOP & psli->dwSeclogonFlags))
  1075. {
  1076. // BUG 477613:
  1077. // If the caller did not specify their own desktop, it is our responsibility
  1078. // to grant the user access to the default desktop. We'll do this by
  1079. // adding the logon sid
  1080. dwResult = GetLogonSid(&pLogonSid);
  1081. if (ERROR_SUCCESS != dwResult)
  1082. __leave;
  1083. }
  1084. if( psli->hToken != NULL )
  1085. {
  1086. //
  1087. // Caller has supplied a token. Verify that the caller can impersonate
  1088. // before proceeding
  1089. //
  1090. if ( OpenProcessToken(hProcessClient, TOKEN_QUERY | TOKEN_IMPERSONATE, &hClientProcessToken ) )
  1091. {
  1092. ImpersonatePrivilege.PrivilegeCount = 1;
  1093. ImpersonatePrivilege.Privilege[ 0 ].Luid.HighPart = 0;
  1094. ImpersonatePrivilege.Privilege[ 0 ].Luid.LowPart = SE_IMPERSONATE_PRIVILEGE ;
  1095. if ( !PrivilegeCheck( hClientProcessToken,&ImpersonatePrivilege,&PrivilegeTest ) )
  1096. {
  1097. PrivilegeTest = FALSE ;
  1098. }
  1099. CloseHandle( hClientProcessToken );
  1100. if ( !PrivilegeTest )
  1101. {
  1102. dwResult = ERROR_PRIVILEGE_NOT_HELD ;
  1103. __leave ;
  1104. }
  1105. }
  1106. else
  1107. {
  1108. dwResult = GetLastError() ;
  1109. __leave ;
  1110. }
  1111. //
  1112. // duplicate the token from the caller, and use that.
  1113. //
  1114. if(!DuplicateHandle(
  1115. hProcessClient,
  1116. psli->hToken,
  1117. GetCurrentProcess(),
  1118. &hToken,
  1119. 0,
  1120. FALSE,
  1121. DUPLICATE_SAME_ACCESS
  1122. ))
  1123. {
  1124. dwResult = GetLastError();
  1125. __leave;
  1126. }
  1127. dwResult = RpcRevertToSelfEx(hRPCBinding);
  1128. if (RPC_S_OK != dwResult)
  1129. {
  1130. __leave;
  1131. }
  1132. fIsImpersonatingRpcClient = FALSE;
  1133. if(psli->dwLogonFlags & LOGON_WITH_PROFILE)
  1134. {
  1135. DWORD cchUserName = sizeof(szTemp) / sizeof(WCHAR);
  1136. pszUserName = szTemp;
  1137. //
  1138. // impersonate the token to get the username, for the profile path.
  1139. //
  1140. if(!ImpersonateLoggedOnUser( hToken ))
  1141. {
  1142. dwResult = GetLastError();
  1143. __leave;
  1144. }
  1145. if(!GetUserNameW(szTemp, &cchUserName))
  1146. {
  1147. dwResult = GetLastError();
  1148. }
  1149. RevertToSelf();
  1150. if( ERROR_SUCCESS != dwResult )
  1151. {
  1152. __leave;
  1153. }
  1154. //
  1155. // TODO: pwszProfilePath ?
  1156. //
  1157. }
  1158. dwResult = ERROR_SUCCESS;
  1159. } else {
  1160. pszUserName = psli->lpUsername;
  1161. dwResult = LogonUserWrap(
  1162. psli->lpUsername,
  1163. psli->lpDomain,
  1164. psli->uszPassword,
  1165. LogonType,
  1166. dwLogonProvider,
  1167. pLogonSid,
  1168. &hToken,
  1169. &pwszProfilePath);
  1170. if (ERROR_SUCCESS != dwResult)
  1171. __leave;
  1172. dwResult = RpcRevertToSelfEx(hRPCBinding);
  1173. if (RPC_S_OK != dwResult)
  1174. {
  1175. __leave;
  1176. }
  1177. fIsImpersonatingRpcClient = FALSE;
  1178. }
  1179. if(psli->dwLogonFlags & LOGON_WITH_PROFILE)
  1180. {
  1181. // Load the user's profile:
  1182. pi.dwSize = sizeof(pi);
  1183. pi.lpUserName = pszUserName;
  1184. pi.lpProfilePath = pwszProfilePath;
  1185. if (!LoadUserProfile(hToken, &pi))
  1186. goto leave_with_last_error;
  1187. // Save the profile handle so we can unload it later
  1188. hProfile = pi.hProfile;
  1189. }
  1190. // Let us set the SessionId in the Token.
  1191. _JumpCondition(!SetTokenInformation(hToken, TokenSessionId, &SessionId, sizeof(DWORD)),
  1192. leave_with_last_error);
  1193. // we should now impersonate the user.
  1194. //
  1195. _JumpCondition(!ImpersonateLoggedOnUser(hToken), leave_with_last_error);
  1196. fIsImpersonatingClient = TRUE;
  1197. // Query Default Owner/ACL from token. Make SD with this stuff, pass for
  1198. sa.nLength = sizeof(sa);
  1199. sa.bInheritHandle = FALSE;
  1200. sa.lpSecurityDescriptor = NULL;
  1201. //
  1202. // We should set the console control handler so CtrlC is correctly
  1203. // handled by the new process.
  1204. //
  1205. // SetConsoleCtrlHandler(NULL, FALSE);
  1206. //
  1207. // if lpEnvironment is NULL, we create new one for this user
  1208. // using CreateEnvironmentBlock
  1209. //
  1210. if(NULL == (psli->lpEnvironment))
  1211. {
  1212. if(FALSE == CreateEnvironmentBlock( &(psli->lpEnvironment), hToken, FALSE ))
  1213. {
  1214. psli->lpEnvironment = NULL;
  1215. }
  1216. else
  1217. {
  1218. // Successfully created environment block.
  1219. fCreatedEnvironmentBlock = TRUE;
  1220. }
  1221. }
  1222. // Create process.
  1223. // NOTE: we want the primary thread to be suspended until we
  1224. // register the thread for cleanup. In case the caller didn't specify it,
  1225. // mask CREATE_SUSPENDED in.
  1226. _JumpCondition(!CreateProcessAsUser(hToken,
  1227. psli->lpApplicationName,
  1228. psli->lpCommandLine,
  1229. &sa,
  1230. &sa,
  1231. fInheritHandles,
  1232. psli->dwCreationFlags | (fCreatedEnvironmentBlock ? CREATE_UNICODE_ENVIRONMENT : 0) | CREATE_SUSPENDED,
  1233. psli->lpEnvironment,
  1234. psli->lpCurrentDirectory,
  1235. psli->lpStartupInfo,
  1236. &pslri->pi),
  1237. leave_with_last_error);
  1238. SetLastError(NO_ERROR);
  1239. leave_with_last_error:
  1240. dwResult = GetLastError();
  1241. __leave;
  1242. }
  1243. __finally {
  1244. pslri->dwErrorCode = dwResult;
  1245. if (fCreatedEnvironmentBlock) { DestroyEnvironmentBlock(psli->lpEnvironment); }
  1246. if (fIsImpersonatingClient) { RevertToSelf(); /* Ignore retval: nothing we can do on failure! */ }
  1247. if (fIsImpersonatingRpcClient) { RpcRevertToSelfEx(hRPCBinding); /* Ignore retval: nothing we can do on failure! */ }
  1248. if (fOpenedSTDIN) { CloseHandle(psli->lpStartupInfo->hStdInput); }
  1249. if (fOpenedSTDOUT) { CloseHandle(psli->lpStartupInfo->hStdOutput); }
  1250. if (fOpenedSTDERR) { CloseHandle(psli->lpStartupInfo->hStdError); }
  1251. if (NULL != pLogonSid) { HeapFree(GetProcessHeap(), 0, pLogonSid); }
  1252. if (NULL != pwszProfilePath) { HeapFree(GetProcessHeap(), 0, pwszProfilePath); }
  1253. if(pslri->dwErrorCode != NO_ERROR)
  1254. {
  1255. if (NULL != hProfile) { UnloadUserProfile(hToken, hProfile); }
  1256. if (NULL != hToken) { CloseHandle(hToken); }
  1257. }
  1258. else
  1259. {
  1260. // Start the watchdog process last so it won't delete psli before we're done with it.
  1261. slwi.hProcess = pslri->pi.hProcess;
  1262. slwi.hToken = hToken;
  1263. slwi.hProfile = hProfile;
  1264. // LogonId was already filled up.. right at the begining.
  1265. slwi.psli = psli;
  1266. // Register for cleanup: SecondaryLogonProcessWatchdogNewProcess MUST BE IN an EH!
  1267. // The cleanup method will cleanup all process info, and terminate the process, on failure.
  1268. dwResult = SecondaryLogonProcessWatchdogNewProcess(&slwi);
  1269. if (ERROR_SUCCESS == dwResult)
  1270. {
  1271. if (0 == (psli->dwCreationFlags & CREATE_SUSPENDED))
  1272. {
  1273. // The caller doesn't want to create the process suspended. Resume the primary thread:
  1274. ResumeThread(pslri->pi.hThread);
  1275. }
  1276. // SetConsoleCtrlHandler(NULL, TRUE);
  1277. //
  1278. // Have the watchdog watch this newly added process so that
  1279. // cleanup will occur correctly when the process terminates.
  1280. //
  1281. // Set up the windowstation and desktop for the process
  1282. DuplicateHandle(GetCurrentProcess(), pslri->pi.hProcess,
  1283. hProcessClient, &pslri->pi.hProcess, 0, FALSE,
  1284. DUPLICATE_SAME_ACCESS);
  1285. DuplicateHandle(GetCurrentProcess(), pslri->pi.hThread, hProcessClient,
  1286. &pslri->pi.hThread, 0, FALSE,
  1287. DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
  1288. }
  1289. else
  1290. {
  1291. pslri->dwErrorCode = dwResult; // return the error to the caller
  1292. }
  1293. *ppsli = NULL; // cleanup method will clean this up now.
  1294. }
  1295. if (NULL != hProcessClient) { CloseHandle(hProcessClient); }
  1296. if (NULL != hCurrentThreadToken) { CloseHandle(hCurrentThreadToken); }
  1297. }
  1298. }
  1299. void
  1300. WINAPI
  1301. ServiceMain
  1302. (IN DWORD dwArgc,
  1303. IN WCHAR ** lpszArgv)
  1304. /*++
  1305. Routine Description:
  1306. The main service handler thread routine.
  1307. Arguments:
  1308. Return Value:
  1309. none.
  1310. --*/
  1311. {
  1312. DWORD dwResult;
  1313. __try {
  1314. InitializeCriticalSection(&csForProcessCount);
  1315. g_fIsCsInitialized = TRUE;
  1316. }
  1317. __except (EXCEPTION_EXECUTE_HANDLER) {
  1318. return; // We can't do anything if we can't initialize this critsec
  1319. }
  1320. g_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0,0);
  1321. _JumpCondition(NULL == g_hIOCP, CreateIoCompletionPortError);
  1322. dwResult = InitGlobalState();
  1323. _JumpCondition(ERROR_SUCCESS != dwResult, InitGlobalStateError);
  1324. // NOTE: hSS does not have to be closed.
  1325. g_state.hServiceStatus = RegisterServiceCtrlHandler(wszSvcName, ServiceHandler);
  1326. _JumpCondition(NULL == g_state.hServiceStatus, RegisterServiceCtrlHandlerError);
  1327. dwResult = SeclStartRpcServer();
  1328. _JumpCondition(ERROR_SUCCESS != dwResult, StartRpcServerError);
  1329. // Tell the SCM we're up and running:
  1330. dwResult = MySetServiceStatus(SERVICE_RUNNING, 0, 0, ERROR_SUCCESS);
  1331. _JumpCondition(ERROR_SUCCESS != dwResult, MySetServiceStatusError);
  1332. SetLastError(ERROR_SUCCESS);
  1333. ErrorReturn:
  1334. // Shut down the service if we couldn't fully start:
  1335. if (ERROR_SUCCESS != GetLastError()) {
  1336. ServiceStop(TRUE /*fShutdown*/, GetLastError());
  1337. }
  1338. return;
  1339. SET_ERROR(InitGlobalStateError, dwResult)
  1340. SET_ERROR(MySetServiceStatusError, dwResult);
  1341. SET_ERROR(RegisterServiceCtrlHandlerError, dwResult);
  1342. SET_ERROR(StartRpcServerError, dwResult);
  1343. TRACE_ERROR(CreateIoCompletionPortError);
  1344. UNREFERENCED_PARAMETER(dwArgc);
  1345. UNREFERENCED_PARAMETER(lpszArgv);
  1346. }
  1347. DWORD
  1348. InstallService()
  1349. /*++
  1350. Routine Description:
  1351. It installs the service with service controller, basically creating
  1352. the service object.
  1353. Arguments:
  1354. none.
  1355. Return Value:
  1356. several - as returned by the service controller.
  1357. --*/
  1358. {
  1359. // TCHAR *szModulePathname;
  1360. TCHAR AppName[MAX_PATH];
  1361. LPTSTR ptszAppName = NULL;
  1362. SC_HANDLE hService;
  1363. DWORD dw;
  1364. HANDLE hMod;
  1365. //
  1366. // Open the SCM on this machine.
  1367. //
  1368. SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
  1369. if(hSCM == NULL) {
  1370. dw = GetLastError();
  1371. return dw;
  1372. }
  1373. //
  1374. // Let us give the service a useful description
  1375. // This is not earth shattering... if it works fine, if it
  1376. // doesn't it is just too bad :-)
  1377. //
  1378. hMod = GetModuleHandle(L"seclogon.dll");
  1379. //
  1380. // we'll try to get the localized name for the service,
  1381. // if it fails, we'll just put an english string...
  1382. //
  1383. if(hMod != NULL)
  1384. {
  1385. LoadString(hMod,
  1386. SECLOGON_STRING_NAME,
  1387. AppName,
  1388. MAX_PATH
  1389. );
  1390. ptszAppName = AppName;
  1391. }
  1392. else
  1393. ptszAppName = L"RunAs Service";
  1394. //
  1395. // Add this service to the SCM's database.
  1396. //
  1397. hService = CreateService
  1398. (hSCM,
  1399. wszSvcName,
  1400. ptszAppName,
  1401. SERVICE_ALL_ACCESS,
  1402. SERVICE_WIN32_SHARE_PROCESS,
  1403. SERVICE_AUTO_START,
  1404. SERVICE_ERROR_IGNORE,
  1405. L"%SystemRoot%\\system32\\svchost.exe -k netsvcs",
  1406. NULL,
  1407. NULL,
  1408. NULL,
  1409. NULL,
  1410. NULL);
  1411. if(hService == NULL) {
  1412. dw = GetLastError();
  1413. CloseServiceHandle(hSCM);
  1414. return dw;
  1415. }
  1416. if(hMod != NULL)
  1417. {
  1418. WCHAR DescString[500];
  1419. SERVICE_DESCRIPTION SvcDesc;
  1420. LoadString( hMod,
  1421. SECLOGON_STRING_DESCRIPTION,
  1422. DescString,
  1423. 500
  1424. );
  1425. SvcDesc.lpDescription = DescString;
  1426. ChangeServiceConfig2( hService,
  1427. SERVICE_CONFIG_DESCRIPTION,
  1428. &SvcDesc
  1429. );
  1430. }
  1431. //
  1432. // Close the service and the SCM
  1433. //
  1434. CloseServiceHandle(hService);
  1435. CloseServiceHandle(hSCM);
  1436. return S_OK;
  1437. }
  1438. DWORD
  1439. RemoveService()
  1440. /*++
  1441. Routine Description:
  1442. deinstalls the service.
  1443. Arguments:
  1444. none.
  1445. Return Value:
  1446. as returned by service controller apis.
  1447. --*/
  1448. {
  1449. DWORD dw;
  1450. SC_HANDLE hService;
  1451. //
  1452. // Open the SCM on this machine.
  1453. //
  1454. SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
  1455. if(hSCM == NULL) {
  1456. dw = GetLastError();
  1457. return dw;
  1458. }
  1459. //
  1460. // Open this service for DELETE access
  1461. //
  1462. hService = OpenService(hSCM, wszSvcName, DELETE);
  1463. if(hService == NULL) {
  1464. dw = GetLastError();
  1465. CloseServiceHandle(hSCM);
  1466. return dw;
  1467. }
  1468. //
  1469. // Remove this service from the SCM's database.
  1470. //
  1471. DeleteService(hService);
  1472. //
  1473. // Close the service and the SCM
  1474. //
  1475. CloseServiceHandle(hService);
  1476. CloseServiceHandle(hSCM);
  1477. return S_OK;
  1478. }
  1479. void SvchostPushServiceGlobals(PSVCHOST_GLOBAL_DATA pGlobalData) {
  1480. // this entry point is called by svchost.exe
  1481. GlobalData=pGlobalData;
  1482. }
  1483. void SvcEntry_Seclogon
  1484. (IN DWORD argc,
  1485. IN WCHAR **argv)
  1486. /*++
  1487. Routine Description:
  1488. Entry point for the service dll when running in svchost.exe
  1489. Arguments:
  1490. Return Value:
  1491. --*/
  1492. {
  1493. ServiceMain(0,NULL);
  1494. UNREFERENCED_PARAMETER(argc);
  1495. UNREFERENCED_PARAMETER(argv);
  1496. }
  1497. STDAPI
  1498. DllRegisterServer(void)
  1499. /*++
  1500. Routine Description:
  1501. Arguments:
  1502. Return Value:
  1503. --*/
  1504. {
  1505. return InstallService();
  1506. }
  1507. STDAPI
  1508. DllUnregisterServer(void)
  1509. /*++
  1510. Routine Description:
  1511. Arguments:
  1512. Return Value:
  1513. --*/
  1514. {
  1515. return RemoveService();
  1516. }
  1517. DWORD InitGlobalState() {
  1518. BOOLEAN bWasEnabled;
  1519. DWORD dwResult;
  1520. LSA_STRING lsastr_LogonProcessName;
  1521. LSA_STRING lsastr_PackageName;
  1522. NTSTATUS ntStatus;
  1523. ULONG ulSecurityMode;
  1524. ZeroMemory(&g_state, sizeof(g_state));
  1525. // LsaRegisterLogonProcess() requires TCB privilege
  1526. RtlAdjustPrivilege(SE_TCB_PRIVILEGE, TRUE, FALSE, &bWasEnabled);
  1527. RtlInitString(&lsastr_LogonProcessName, "Secondary Logon Service");
  1528. ntStatus = LsaRegisterLogonProcess(&lsastr_LogonProcessName, &g_state.hLSA, &ulSecurityMode);
  1529. if (!NT_SUCCESS(ntStatus))
  1530. goto LsaRegisterLogonProcessError;
  1531. RtlAdjustPrivilege(SE_TCB_PRIVILEGE, bWasEnabled, FALSE, &bWasEnabled);
  1532. RtlInitString(&lsastr_PackageName, "MICROSOFT_AUTHENTICATION_PACKAGE_V1_0");
  1533. ntStatus = LsaLookupAuthenticationPackage(g_state.hLSA, &lsastr_PackageName, &g_state.hMSVPackage);
  1534. if (!NT_SUCCESS(ntStatus))
  1535. goto LsaLookupAuthenticationPackageError;
  1536. RtlInitString(&lsastr_PackageName, NEGOSSP_NAME_A);
  1537. ntStatus = LsaLookupAuthenticationPackage(g_state.hLSA, &lsastr_PackageName, &g_state.hKerbPackage);
  1538. if (!NT_SUCCESS(ntStatus))
  1539. goto LsaLookupAuthenticationPackageError;
  1540. // Initialize the list of seclogon processes:
  1541. InitializeListHead(&g_state.JobListHead);
  1542. dwResult = ERROR_SUCCESS;
  1543. ErrorReturn:
  1544. return dwResult;
  1545. SET_DWRESULT(LsaLookupAuthenticationPackageError, RtlNtStatusToDosError(ntStatus));
  1546. SET_DWRESULT(LsaRegisterLogonProcessError, RtlNtStatusToDosError(ntStatus));
  1547. }
  1548. DWORD MySetServiceStatus(DWORD dwCurrentState, DWORD dwCheckPoint, DWORD dwWaitHint, DWORD dwExitCode) {
  1549. BOOL fResult;
  1550. DWORD dwResult;
  1551. DWORD dwAcceptStop;
  1552. EnterCriticalSection(&csForProcessCount);
  1553. dwAcceptStop = IsListEmpty(&g_state.JobListHead) ? SERVICE_ACCEPT_STOP : 0;
  1554. g_state.serviceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
  1555. g_state.serviceStatus.dwCurrentState = dwCurrentState;
  1556. switch (dwCurrentState)
  1557. {
  1558. case SERVICE_STOPPED:
  1559. case SERVICE_STOP_PENDING:
  1560. g_state.serviceStatus.dwControlsAccepted = 0;
  1561. break;
  1562. case SERVICE_RUNNING:
  1563. case SERVICE_PAUSED:
  1564. g_state.serviceStatus.dwControlsAccepted =
  1565. // SERVICE_ACCEPT_SHUTDOWN
  1566. SERVICE_ACCEPT_PAUSE_CONTINUE
  1567. | dwAcceptStop;
  1568. break;
  1569. case SERVICE_START_PENDING:
  1570. case SERVICE_CONTINUE_PENDING:
  1571. case SERVICE_PAUSE_PENDING:
  1572. g_state.serviceStatus.dwControlsAccepted =
  1573. // SERVICE_ACCEPT_SHUTDOWN
  1574. dwAcceptStop;
  1575. break;
  1576. }
  1577. g_state.serviceStatus.dwWin32ExitCode = dwExitCode;
  1578. g_state.serviceStatus.dwCheckPoint = dwCheckPoint;
  1579. g_state.serviceStatus.dwWaitHint = dwWaitHint;
  1580. fResult = SetServiceStatus(g_state.hServiceStatus, &g_state.serviceStatus);
  1581. _JumpCondition(FALSE == fResult, SetServiceStatusError);
  1582. dwResult = ERROR_SUCCESS;
  1583. CommonReturn:
  1584. LeaveCriticalSection(&csForProcessCount);
  1585. return dwResult;
  1586. ErrorReturn:
  1587. goto CommonReturn;
  1588. SET_DWRESULT(SetServiceStatusError, GetLastError());
  1589. }
  1590. DWORD MySetServiceStopped(DWORD dwExitCode) {
  1591. BOOL fResult;
  1592. DWORD dwResult;
  1593. g_state.serviceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
  1594. g_state.serviceStatus.dwCurrentState = SERVICE_STOPPED;
  1595. g_state.serviceStatus.dwControlsAccepted = 0;
  1596. g_state.serviceStatus.dwWin32ExitCode = dwExitCode;
  1597. g_state.serviceStatus.dwCheckPoint = 0;
  1598. g_state.serviceStatus.dwWaitHint = 0;
  1599. fResult = SetServiceStatus(g_state.hServiceStatus, &g_state.serviceStatus);
  1600. _JumpCondition(FALSE == fResult, SetServiceStatusError);
  1601. dwResult = ERROR_SUCCESS;
  1602. CommonReturn:
  1603. return dwResult;
  1604. ErrorReturn:
  1605. goto CommonReturn;
  1606. SET_DWRESULT(SetServiceStatusError, GetLastError());
  1607. }
  1608. ////////////////////////////////////////////////////////////////////////
  1609. //
  1610. // Implementation of RPC interface:
  1611. //
  1612. ////////////////////////////////////////////////////////////////////////
  1613. // Make sure that the caller is local (we don't want to allow remote machines to call us!)
  1614. // This should be done as soon as possible (so we don't waste resources on hackers).
  1615. // Therefore, we put this call in a security callback -- if the callback fails, the RPC
  1616. // runtime won't allocate memory for the hacker's parameters.
  1617. //
  1618. long __stdcall SeclSecurityCallback(void *Interface, void *Context)
  1619. {
  1620. RPC_STATUS rpcStatus;
  1621. unsigned int fClientIsLocal;
  1622. rpcStatus = I_RpcBindingIsClientLocal(NULL, &fClientIsLocal);
  1623. if (RPC_S_OK != rpcStatus)
  1624. goto error;
  1625. if (!fClientIsLocal) {
  1626. rpcStatus = RPC_S_ACCESS_DENIED;
  1627. goto error;
  1628. }
  1629. rpcStatus = RPC_S_OK;
  1630. error:
  1631. return rpcStatus;
  1632. UNREFERENCED_PARAMETER(Interface);
  1633. UNREFERENCED_PARAMETER(Context);
  1634. }
  1635. void WINAPI SeclCreateProcessWithLogonW
  1636. (IN handle_t hRPCBinding,
  1637. IN SECL_SLI *pSeclSli,
  1638. OUT SECL_SLRI *pSeclSlri)
  1639. {
  1640. BOOL fEnteredCriticalSection = FALSE;
  1641. BOOL fIsImpersonatingClient = FALSE;
  1642. DWORD dwResult;
  1643. HANDLE hHeap = NULL;
  1644. PLIST_ENTRY ple;
  1645. PSECONDARYLOGONINFOW psli = NULL;
  1646. SECL_SLRI SeclSlri;
  1647. SECONDARYLOGONRETINFO slri;
  1648. ZeroMemory(&SeclSlri, sizeof(SeclSlri));
  1649. ZeroMemory(&slri, sizeof(slri));
  1650. // We don't want the service to be stopped while we're creating a process.
  1651. EnterCriticalSection(&csForProcessCount);
  1652. fEnteredCriticalSection = TRUE;
  1653. // Service isn't running anymore ... don't create the process.
  1654. _JumpCondition(SERVICE_RUNNING != g_state.serviceStatus.dwCurrentState, ServiceStoppedError);
  1655. hHeap = GetProcessHeap();
  1656. _JumpCondition(NULL == hHeap, MemoryError);
  1657. __try {
  1658. dwResult = To_SECONDARYLOGONINFOW(pSeclSli, &psli);
  1659. _JumpCondition(ERROR_SUCCESS != dwResult, To_SECONDARYLOGONINFOW_Error);
  1660. if (psli->LogonIdHighPart != 0 || psli->LogonIdLowPart != 0)
  1661. {
  1662. // This is probably a notification from winlogon.exe that
  1663. // a client is logging off. If so, we must clean up all processes
  1664. // they've left running.
  1665. LUID LogonId;
  1666. //
  1667. // We should impersonate the client,
  1668. // check it is LocalSystem and only then proceed.
  1669. //
  1670. fIsImpersonatingClient = RPC_S_OK == RpcImpersonateClient((RPC_BINDING_HANDLE)hRPCBinding);
  1671. if(FALSE == fIsImpersonatingClient || FALSE == IsSystemProcess())
  1672. {
  1673. slri.dwErrorCode = ERROR_INVALID_PARAMETER;
  1674. ZeroMemory(&slri.pi, sizeof(slri.pi));
  1675. }
  1676. else
  1677. {
  1678. LogonId.HighPart = psli->LogonIdHighPart;
  1679. LogonId.LowPart = psli->LogonIdLowPart;
  1680. // Loop over the list of active jobs and find the ones associated with the terminating logon session.
  1681. // NOTE: there could be more than one.
  1682. for (ple = g_state.JobListHead.Flink; ple != &g_state.JobListHead; ple = ple->Flink)
  1683. {
  1684. SecondaryLogonJob *pslj = CONTAINING_RECORD(ple, SecondaryLogonJob, list);
  1685. if(pslj->RootLogonId.HighPart == LogonId.HighPart && pslj->RootLogonId.LowPart == LogonId.LowPart)
  1686. {
  1687. // This will be NULL in the terminal services case (we don't use job objects in this case).
  1688. // That's OK though, as csrss will clean things up for us.
  1689. if (NULL != pslj->hJob)
  1690. {
  1691. TerminateJobObject(pslj->hJob, 0);
  1692. }
  1693. }
  1694. }
  1695. slri.dwErrorCode = ERROR_SUCCESS;
  1696. ZeroMemory(&slri.pi, sizeof(slri.pi));
  1697. }
  1698. if (fIsImpersonatingClient)
  1699. {
  1700. // Ignore error: nothing we can do on failure!
  1701. if (RPC_S_OK == RpcRevertToSelfEx((RPC_BINDING_HANDLE)hRPCBinding))
  1702. {
  1703. fIsImpersonatingClient = FALSE;
  1704. }
  1705. }
  1706. if (NULL != psli)
  1707. {
  1708. Free_SECONDARYLOGONINFOW(psli);
  1709. psli = NULL;
  1710. }
  1711. }
  1712. else
  1713. {
  1714. // Ok, this isn't notification from winlogon, it's really a user
  1715. // trying to use the service. Create a process for them.
  1716. //
  1717. SlrCreateProcessWithLogon((RPC_BINDING_HANDLE)hRPCBinding, &psli, &slri);
  1718. }
  1719. // If we've errored out, jump to the error handler.
  1720. _JumpCondition(NO_ERROR != slri.dwErrorCode, UnspecifiedSeclogonError);
  1721. }
  1722. __except (EXCEPTION_EXECUTE_HANDLER) {
  1723. // If anything goes wrong, return the exception code to the client
  1724. dwResult = GetExceptionCode();
  1725. goto ExceptionError;
  1726. }
  1727. CommonReturn:
  1728. // Do not free slri: this will be freed by the watchdog!
  1729. SeclSlri.hProcess = (unsigned __int64)slri.pi.hProcess;
  1730. SeclSlri.hThread = (unsigned __int64)slri.pi.hThread;
  1731. SeclSlri.ulProcessId = slri.pi.dwProcessId;
  1732. SeclSlri.ulThreadId = slri.pi.dwThreadId;
  1733. SeclSlri.ulErrorCode = slri.dwErrorCode;
  1734. if (fEnteredCriticalSection)
  1735. LeaveCriticalSection(&csForProcessCount);
  1736. // Assign the OUT parameter:
  1737. *pSeclSlri = SeclSlri;
  1738. return;
  1739. ErrorReturn:
  1740. ZeroMemory(&slri.pi, sizeof(slri.pi));
  1741. if (NULL != psli) { Free_SECONDARYLOGONINFOW(psli); }
  1742. slri.dwErrorCode = dwResult;
  1743. goto CommonReturn;
  1744. SET_DWRESULT(ExceptionError, dwResult);
  1745. SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY);
  1746. SET_DWRESULT(ServiceStoppedError, ERROR_SERVICE_NOT_ACTIVE);
  1747. SET_DWRESULT(To_SECONDARYLOGONINFOW_Error, dwResult);
  1748. SET_DWRESULT(UnspecifiedSeclogonError, slri.dwErrorCode);
  1749. }
  1750. ////////////////////////////////////////////////////////////////////////
  1751. //
  1752. // RPC Utility methods:
  1753. //
  1754. ////////////////////////////////////////////////////////////////////////
  1755. DWORD SeclStartRpcServer() {
  1756. DWORD dwResult;
  1757. RPC_STATUS RpcStatus;
  1758. EnterCriticalSection(&csForProcessCount);
  1759. if (!g_state.fRPCServerActive) {
  1760. RpcStatus = RpcServerUseProtseqEpW(L"ncalrpc", RPC_C_PROTSEQ_MAX_REQS_DEFAULT, wszSeclogonSharedProcEndpointName, NULL);
  1761. if (RPC_S_DUPLICATE_ENDPOINT == RpcStatus)
  1762. RpcStatus = RPC_S_OK;
  1763. if (RPC_S_OK != RpcStatus)
  1764. goto RpcServerUseProtseqEpWError;
  1765. RpcStatus = RpcServerRegisterIfEx(ISeclogon_v1_0_s_ifspec, NULL, NULL, RPC_IF_AUTOLISTEN | RPC_IF_ALLOW_SECURE_ONLY, RPC_C_PROTSEQ_MAX_REQS_DEFAULT, SeclSecurityCallback);
  1766. if (RPC_S_OK != RpcStatus)
  1767. goto StartRpcServerError;
  1768. g_state.fRPCServerActive = TRUE;
  1769. }
  1770. dwResult = ERROR_SUCCESS;
  1771. CommonReturn:
  1772. LeaveCriticalSection(&csForProcessCount);
  1773. return dwResult;
  1774. ErrorReturn:
  1775. goto CommonReturn;
  1776. SET_DWRESULT(RpcServerUseProtseqEpWError, RpcStatus);
  1777. SET_DWRESULT(StartRpcServerError, RpcStatus);
  1778. }
  1779. DWORD SeclStopRpcServer() {
  1780. DWORD dwResult;
  1781. RPC_STATUS RpcStatus;
  1782. EnterCriticalSection(&csForProcessCount);
  1783. if (g_state.fRPCServerActive) {
  1784. RpcStatus = RpcServerUnregisterIf(ISeclogon_v1_0_s_ifspec, 0, 0);
  1785. if (RPC_S_OK != RpcStatus)
  1786. goto RpcServerUnregisterIfError;
  1787. g_state.fRPCServerActive = FALSE;
  1788. }
  1789. dwResult = ERROR_SUCCESS;
  1790. CommonReturn:
  1791. LeaveCriticalSection(&csForProcessCount);
  1792. return dwResult;
  1793. ErrorReturn:
  1794. goto CommonReturn;
  1795. SET_DWRESULT(RpcServerUnregisterIfError, RpcStatus);
  1796. }
  1797. void Free_SECONDARYLOGONINFOW(IN PSECONDARYLOGONINFOW psli) {
  1798. HANDLE hHeap = GetProcessHeap();
  1799. if (NULL == hHeap)
  1800. return;
  1801. if (NULL != psli) {
  1802. if (NULL != psli->lpStartupInfo) {
  1803. HeapFree(hHeap, 0, psli->lpStartupInfo);
  1804. }
  1805. HeapFree(hHeap, 0, psli);
  1806. }
  1807. }
  1808. DWORD To_LPWSTR(IN SECL_STRING *pss,
  1809. OUT LPWSTR *ppwsz)
  1810. {
  1811. DWORD dwResult;
  1812. if (NULL != pss->pwsz) {
  1813. // Ensure that the string is NULL-terminated where the caller says it is:
  1814. if (pss->ccSize <= pss->ccLength) {
  1815. goto InvalidParameterError;
  1816. }
  1817. // NULL-terminate the string ourself
  1818. pss->pwsz[pss->ccLength] = L'\0';
  1819. }
  1820. *ppwsz = pss->pwsz;
  1821. dwResult = ERROR_SUCCESS;
  1822. ErrorReturn:
  1823. return dwResult;
  1824. SET_DWRESULT(InvalidParameterError, ERROR_INVALID_PARAMETER);
  1825. }
  1826. DWORD To_SECONDARYLOGONINFOW(IN PSECL_SLI pSeclSli,
  1827. OUT PSECONDARYLOGONINFOW *ppsli)
  1828. {
  1829. DWORD dwAllocFlags = HEAP_ZERO_MEMORY;
  1830. DWORD dwIndex;
  1831. DWORD dwResult;
  1832. HANDLE hHeap = NULL;
  1833. PSECONDARYLOGONINFOW psli = NULL;
  1834. hHeap = GetProcessHeap();
  1835. _JumpCondition(NULL == hHeap, GetProcessHeapError);
  1836. psli = (PSECONDARYLOGONINFOW)HeapAlloc(hHeap, dwAllocFlags, sizeof(SECONDARYLOGONINFOW));
  1837. _JumpCondition(NULL == psli, MemoryError);
  1838. psli->lpStartupInfo = (LPSTARTUPINFO)HeapAlloc(hHeap, dwAllocFlags, sizeof(STARTUPINFO));
  1839. _JumpCondition(NULL == psli->lpStartupInfo, MemoryError);
  1840. __try {
  1841. {
  1842. struct {
  1843. SECL_STRING *pss;
  1844. LPWSTR *ppwsz;
  1845. } rg_StringsToMap[] = {
  1846. { &(pSeclSli->ssDesktop), /* Is mapped to ----> */ &(psli->lpStartupInfo->lpDesktop) },
  1847. { &(pSeclSli->ssTitle), /* Is mapped to ----> */ &(psli->lpStartupInfo->lpTitle) },
  1848. { &(pSeclSli->ssUsername), /* Is mapped to ----> */ &(psli->lpUsername) },
  1849. { &(pSeclSli->ssDomain), /* Is mapped to ----> */ &(psli->lpDomain) },
  1850. { &(pSeclSli->ssApplicationName), /* Is mapped to ----> */ &(psli->lpApplicationName) },
  1851. { &(pSeclSli->ssCommandLine), /* Is mapped to ----> */ &(psli->lpCommandLine) },
  1852. { &(pSeclSli->ssCurrentDirectory), /* Is mapped to ----> */ (LPWSTR *)&(psli->lpCurrentDirectory) }
  1853. };
  1854. for (dwIndex = 0; dwIndex < ARRAYSIZE(rg_StringsToMap); dwIndex++) {
  1855. dwResult = To_LPWSTR(rg_StringsToMap[dwIndex].pss, rg_StringsToMap[dwIndex].ppwsz);
  1856. _JumpCondition(ERROR_SUCCESS != dwResult, To_LPWSTR_Error);
  1857. }
  1858. }
  1859. // Get the (possibly encrypted) passwd:
  1860. psli->uszPassword.Buffer = pSeclSli->ssPassword.pwsz;
  1861. psli->uszPassword.Length = pSeclSli->ssPassword.ccLength;
  1862. psli->uszPassword.MaximumLength = pSeclSli->ssPassword.ccSize;
  1863. if (NULL != psli->uszPassword.Buffer && psli->uszPassword.MaximumLength > 0)
  1864. {
  1865. psli->uszPassword.Buffer[psli->uszPassword.MaximumLength-1] = L'\0';
  1866. }
  1867. // Grab the environment from the SECL_SLI block:
  1868. psli->lpEnvironment = pSeclSli->sbEnvironment.pb;
  1869. // Ensure that our environment parameter is NULL-terminated:
  1870. if (NULL != psli->lpEnvironment)
  1871. {
  1872. // The environment block is terminated by 2 NULL chars.
  1873. DWORD cbTerm = (CREATE_UNICODE_ENVIRONMENT & pSeclSli->ulCreationFlags) ? sizeof(WCHAR)*2 : sizeof(CHAR)*2;
  1874. // ensure we have a large enough buffer to contain the NULL termination chars:
  1875. if (pSeclSli->sbEnvironment.cb < cbTerm)
  1876. goto InvalidParameterError;
  1877. // add terminated NULLs to the end of the environment block
  1878. for (; cbTerm > 0; cbTerm--)
  1879. {
  1880. pSeclSli->sbEnvironment.pb[pSeclSli->sbEnvironment.cb - cbTerm] = 0;
  1881. }
  1882. }
  1883. } __except (EXCEPTION_EXECUTE_HANDLER) {
  1884. // Don't give the caller too much information: the input was bad, that's all they need to know.
  1885. dwResult = ERROR_INVALID_PARAMETER;
  1886. goto ExceptionError;
  1887. }
  1888. psli->dwProcessId = pSeclSli->ulProcessId;
  1889. psli->LogonIdLowPart = pSeclSli->ulLogonIdLowPart;
  1890. psli->LogonIdHighPart = pSeclSli->lLogonIdHighPart;
  1891. psli->dwLogonFlags = pSeclSli->ulLogonFlags;
  1892. psli->dwCreationFlags = pSeclSli->ulCreationFlags;
  1893. psli->dwSeclogonFlags = pSeclSli->ulSeclogonFlags;
  1894. psli->hToken = (HANDLE)((ULONG_PTR)pSeclSli->hToken);
  1895. *ppsli = psli;
  1896. dwResult = ERROR_SUCCESS;
  1897. CommonReturn:
  1898. return dwResult;
  1899. ErrorReturn:
  1900. Free_SECONDARYLOGONINFOW(psli);
  1901. goto CommonReturn;
  1902. SET_DWRESULT(ExceptionError, dwResult);
  1903. SET_DWRESULT(GetProcessHeapError, GetLastError());
  1904. SET_DWRESULT(InvalidParameterError, ERROR_INVALID_PARAMETER);
  1905. SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY);
  1906. SET_DWRESULT(To_LPWSTR_Error, dwResult);
  1907. }
  1908. //////////////////////////////// End Of File /////////////////////////////////