Source code of Windows XP (NT5)
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.

1841 lines
56 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. #include <nt.h>
  20. #include <ntrtl.h>
  21. #include <nturtl.h>
  22. #include <ntlsa.h>
  23. #include <Windows.h>
  24. #define SECURITY_WIN32
  25. #define SECURITY_KERBEROS
  26. #include <security.h>
  27. #include <secint.h>
  28. #include <winsafer.h>
  29. #include <shellapi.h>
  30. #include <svcs.h>
  31. #include <userenv.h>
  32. #include <sddl.h>
  33. #include "seclogon.h"
  34. #include <stdio.h>
  35. #include "stringid.h"
  36. #include "dbgdef.h"
  37. //
  38. // must move to winbase.h soon!
  39. #define LOGON_WITH_PROFILE 0x00000001
  40. #define LOGON_NETCREDENTIALS_ONLY 0x00000002
  41. #define MAXIMUM_SECLOGON_PROCESSES MAXIMUM_WAIT_OBJECTS*4
  42. #define DESKTOP_ALL (DESKTOP_READOBJECTS | DESKTOP_CREATEWINDOW | \
  43. DESKTOP_CREATEMENU | DESKTOP_HOOKCONTROL | \
  44. DESKTOP_JOURNALRECORD | DESKTOP_JOURNALPLAYBACK | \
  45. DESKTOP_ENUMERATE | DESKTOP_WRITEOBJECTS | \
  46. DESKTOP_SWITCHDESKTOP | STANDARD_RIGHTS_REQUIRED)
  47. #define WINSTA_ALL (WINSTA_ENUMDESKTOPS | WINSTA_READATTRIBUTES | \
  48. WINSTA_ACCESSCLIPBOARD | WINSTA_CREATEDESKTOP | \
  49. WINSTA_WRITEATTRIBUTES | WINSTA_ACCESSGLOBALATOMS | \
  50. WINSTA_EXITWINDOWS | WINSTA_ENUMERATE | \
  51. WINSTA_READSCREEN | \
  52. STANDARD_RIGHTS_REQUIRED)
  53. struct SECL_STATE {
  54. SERVICE_STATUS serviceStatus;
  55. SERVICE_STATUS_HANDLE hServiceStatus;
  56. } g_state;
  57. typedef struct _SECONDARYLOGONINFOW {
  58. // First fields should all be quad-word types to avoid alignment errors:
  59. LPSTARTUPINFO lpStartupInfo;
  60. LPWSTR lpUsername;
  61. LPWSTR lpDomain;
  62. LPWSTR lpPassword;
  63. LPWSTR lpApplicationName;
  64. LPWSTR lpCommandLine;
  65. LPVOID lpEnvironment;
  66. LPCWSTR lpCurrentDirectory;
  67. // Next group of fields are double-word types:
  68. DWORD dwProcessId;
  69. ULONG LogonIdLowPart;
  70. LONG LogonIdHighPart;
  71. DWORD dwLogonFlags;
  72. DWORD dwCreationFlags;
  73. DWORD dwSeclogonFlags;
  74. HANDLE hWinsta;
  75. HANDLE hDesk;
  76. // Insert smaller types below:
  77. BOOL fFreeWinsta;
  78. BOOL fFreeDesk;
  79. } SECONDARYLOGONINFOW, *PSECONDARYLOGONINFOW;
  80. typedef struct _SECONDARYLOGONRETINFO {
  81. PROCESS_INFORMATION pi;
  82. DWORD dwErrorCode;
  83. } SECONDARYLOGONRETINFO, *PSECONDARYLOGONRETINFO;
  84. typedef struct _SECONDARYLOGINWATCHINFO {
  85. HANDLE hProcess;
  86. HANDLE hToken;
  87. HANDLE hProfile;
  88. LUID LogonId;
  89. PSECONDARYLOGONINFOW psli;
  90. } SECONDARYLOGONWATCHINFO, *PSECONDARYLOGONWATCHINFO;
  91. typedef struct _JOBINFO {
  92. HANDLE Job;
  93. LUID LogonId;
  94. } JOBINFO, *PJOBINFO;
  95. #define _JumpCondition(condition, label) \
  96. if (condition) \
  97. { \
  98. goto label; \
  99. } \
  100. else { }
  101. #define _JumpConditionWithExpr(condition, label, expr) \
  102. if (condition) \
  103. { \
  104. expr; \
  105. goto label; \
  106. } \
  107. else { }
  108. #define ARRAYSIZE(array) ((sizeof(array)) / (sizeof(array[0])))
  109. #define FIELDOFFSET(s,m) ((size_t)(ULONG_PTR)&(((s *)0)->m))
  110. HANDLE g_hThreadWatchdog;
  111. JOBINFO g_Jobs[MAXIMUM_SECLOGON_PROCESSES];
  112. HANDLE g_hProcess[MAXIMUM_SECLOGON_PROCESSES];
  113. HANDLE g_hToken[MAXIMUM_SECLOGON_PROCESSES];
  114. HANDLE g_hProfile[MAXIMUM_SECLOGON_PROCESSES];
  115. LUID g_LogonId[MAXIMUM_SECLOGON_PROCESSES];
  116. PSECONDARYLOGONINFOW g_psli[MAXIMUM_SECLOGON_PROCESSES];
  117. int g_nNumSecondaryLogonProcesses = 0;
  118. CRITICAL_SECTION csForProcessCount;
  119. CRITICAL_SECTION csForDesktop;
  120. BOOL g_fIsCsInitialized = FALSE;
  121. BOOL g_fTerminateSecondaryLogonService = FALSE;
  122. PSVCHOST_GLOBAL_DATA GlobalData;
  123. HANDLE g_hIOCP = NULL;
  124. BOOL g_fCleanupThreadActive = FALSE;
  125. //
  126. // function prototypes
  127. //
  128. void Free_SECONDARYLOGONINFOW(PSECONDARYLOGONINFOW psli);
  129. void FreeGlobalState();
  130. DWORD InitGlobalState();
  131. DWORD MySetServiceStatus(DWORD dwCurrentState, DWORD dwCheckPoint, DWORD dwWaitHint, DWORD dwExitCode);
  132. DWORD MySetServiceStopped(DWORD dwExitCode);
  133. DWORD SeclStartRpcServer();
  134. DWORD SeclStopRpcServer();
  135. VOID SecondaryLogonCleanupJob(LPVOID pvJobIndex, BOOL *pfLastJob);
  136. BOOL SlpLoadUserProfile(HANDLE hToken, PHANDLE hProfile);
  137. DWORD To_SECONDARYLOGONINFOW(PSECL_SLI pSeclSli, PSECONDARYLOGONINFOW *ppsli);
  138. DWORD To_SECL_SLRI(SECONDARYLOGONRETINFO *pslri, PSECL_SLRI pSeclSlri);
  139. void DbgPrintf( DWORD dwSubSysId, LPCSTR pszFormat , ...)
  140. {
  141. va_list args;
  142. CHAR pszBuffer[1024];
  143. va_start(args, pszFormat);
  144. _vsnprintf(pszBuffer, 1024, pszFormat, args);
  145. va_end(args);
  146. }
  147. BOOL
  148. IsSystemProcess(
  149. VOID
  150. )
  151. {
  152. PTOKEN_USER User;
  153. HANDLE Token;
  154. DWORD RetLen;
  155. PSID SystemSid = NULL;
  156. SID_IDENTIFIER_AUTHORITY SidAuthority = SECURITY_NT_AUTHORITY;
  157. BYTE Buffer[100];
  158. if(AllocateAndInitializeSid(&SidAuthority,1,SECURITY_LOCAL_SYSTEM_RID,
  159. 0,0,0,0,0,0,0,&SystemSid))
  160. {
  161. if(OpenThreadToken(GetCurrentThread(), MAXIMUM_ALLOWED, FALSE, &Token))
  162. {
  163. if(GetTokenInformation(Token, TokenUser, Buffer, 100, &RetLen))
  164. {
  165. User = (PTOKEN_USER)Buffer;
  166. CloseHandle(Token);
  167. if(EqualSid(User->User.Sid, SystemSid))
  168. {
  169. FreeSid(SystemSid);
  170. return TRUE;
  171. }
  172. }
  173. else
  174. CloseHandle(Token);
  175. }
  176. FreeSid(SystemSid);
  177. }
  178. return FALSE;
  179. }
  180. DWORD
  181. SlpGetClientLogonId(
  182. HANDLE Process,
  183. PLUID LogonId
  184. )
  185. {
  186. HANDLE Token;
  187. TOKEN_STATISTICS TokenStats;
  188. DWORD ReturnLength;
  189. //
  190. // Get handle to the process token.
  191. //
  192. if(OpenProcessToken(Process, MAXIMUM_ALLOWED, &Token))
  193. {
  194. if(GetTokenInformation (
  195. Token,
  196. TokenStatistics,
  197. (PVOID)&TokenStats,
  198. sizeof( TOKEN_STATISTICS ),
  199. &ReturnLength
  200. ))
  201. {
  202. *LogonId = TokenStats.AuthenticationId;
  203. CloseHandle(Token);
  204. return ERROR_SUCCESS;
  205. }
  206. CloseHandle(Token);
  207. }
  208. return GetLastError();
  209. }
  210. DWORD ModifyUserAccessToObject
  211. (IN HANDLE hObject,
  212. IN PSID pUserSid,
  213. IN DWORD dwAccessMask, // access mask to apply IF granting access
  214. IN BYTE bAceFlags, // flags to supply with Ace IF granting access
  215. IN BOOL fAdd // true if we're granting access
  216. )
  217. {
  218. ACL_SIZE_INFORMATION asiSize;
  219. PACCESS_ALLOWED_ACE pAce = NULL;
  220. PACCESS_ALLOWED_ACE pAceNew = NULL;
  221. BOOL fRemovedAccess = FALSE;
  222. BOOL fDaclDefaulted;
  223. BOOL fDaclPresent;
  224. DWORD dwIndex;
  225. DWORD dwNeeded;
  226. DWORD dwNewAclSize;
  227. DWORD dwResult;
  228. PACL pDaclNew = NULL;
  229. PACL pDaclReadOnly = NULL;
  230. SECURITY_DESCRIPTOR SdNew;
  231. PSECURITY_DESCRIPTOR pSdReadOnly = NULL;
  232. SECURITY_INFORMATION siRequested;
  233. // Initialize non-pointer data:
  234. ZeroMemory(&SdNew, sizeof(SdNew));
  235. // Query the security descriptor
  236. siRequested = DACL_SECURITY_INFORMATION;
  237. if (!GetUserObjectSecurity(hObject, &siRequested, pSdReadOnly, 0, &dwNeeded))
  238. {
  239. // allocate buffer large for returned SD and another ACE.
  240. pSdReadOnly = (PSECURITY_DESCRIPTOR) HeapAlloc (GetProcessHeap(), 0, dwNeeded + 100);
  241. if (NULL == pSdReadOnly)
  242. goto MemoryError;
  243. if (!GetUserObjectSecurity(hObject, &siRequested, pSdReadOnly, dwNeeded, &dwNeeded))
  244. goto GetUserObjectSecurityError;
  245. }
  246. else
  247. {
  248. // There's no security descriptor, not much we can do here!
  249. goto SuccessReturn;
  250. }
  251. if (!GetSecurityDescriptorDacl(pSdReadOnly, &fDaclPresent, &pDaclReadOnly, &fDaclDefaulted))
  252. goto GetSecurityDescriptorDaclError;
  253. // if Dacl is null, we don't need to do anything
  254. // because it gives WORLD full control...
  255. if (!fDaclPresent || NULL == pDaclReadOnly)
  256. goto SuccessReturn;
  257. // Compute the size for the new ACL, based on the size of the current
  258. // ACL, and the operation to be performed on it.
  259. if (!GetAclInformation(pDaclReadOnly, (PVOID)&asiSize, sizeof(asiSize), AclSizeInformation))
  260. goto GetAclInformationError;
  261. if (fAdd)
  262. dwNewAclSize = asiSize.AclBytesInUse + sizeof(ACCESS_ALLOWED_ACE) + GetLengthSid(pUserSid) + sizeof(DWORD);
  263. else
  264. dwNewAclSize = asiSize.AclBytesInUse - sizeof(ACCESS_ALLOWED_ACE) - GetLengthSid(pUserSid) + sizeof(DWORD);
  265. pDaclNew = (PACL)HeapAlloc(GetProcessHeap(), 0, dwNewAclSize);
  266. if (NULL == pDaclNew)
  267. goto MemoryError;
  268. if (!InitializeAcl(pDaclNew, dwNewAclSize, ACL_REVISION))
  269. goto InitializeAclError;
  270. if (fAdd) // We're granting the user access
  271. {
  272. if (!AddAccessAllowedAce(pDaclNew, ACL_REVISION, dwAccessMask, pUserSid))
  273. goto AddAccessAllowedAceError;
  274. if (!GetAce(pDaclNew, 0, &pAceNew))
  275. goto GetAceError;
  276. pAceNew->Header.AceFlags = bAceFlags;
  277. }
  278. // Copy over the ACEs that are still valid
  279. // (all if adding, all but the user's SID if removing).
  280. for (dwIndex = 0; dwIndex < asiSize.AceCount; dwIndex++)
  281. {
  282. if (!GetAce(pDaclReadOnly, dwIndex, (PVOID*)(&pAce)))
  283. goto GetAceError;
  284. // We're removing -- check if this ACE contains the user's SID
  285. if (!fAdd && !fRemovedAccess)
  286. {
  287. if ((((PACE_HEADER)pAce)->AceType) == ACCESS_ALLOWED_ACE_TYPE)
  288. {
  289. if (EqualSid(pUserSid, (PSID)(&(pAce->SidStart))))
  290. {
  291. // Don't add this ACE to the new ACL we're constructing.
  292. // NOTE: we only want to remove one ACCESS_ALLOWED_ACE. Otherwise, if the
  293. // user already had access to the desktop, we could
  294. // be removing the access they previously had!
  295. fRemovedAccess = TRUE;
  296. continue;
  297. }
  298. }
  299. }
  300. if (!AddAce(pDaclNew, ACL_REVISION, 0xFFFFFFFF, pAce, ((PACE_HEADER)pAce)->AceSize))
  301. goto AddAceError;
  302. }
  303. // Create the new security descriptor to assign to the object:
  304. if (!InitializeSecurityDescriptor(&SdNew, SECURITY_DESCRIPTOR_REVISION))
  305. goto InitializeSecurityDescriptorError;
  306. // Add the new DACL to the descriptor:
  307. if (!SetSecurityDescriptorDacl(&SdNew, TRUE, pDaclNew, fDaclDefaulted))
  308. goto SetSecurityDescriptorDaclError;
  309. // Finally, set the object security using our new security descriptor.
  310. siRequested = DACL_SECURITY_INFORMATION;
  311. if (!SetUserObjectSecurity(hObject, &siRequested, &SdNew))
  312. goto SetUserObjectSecurityError;
  313. SuccessReturn:
  314. dwResult = ERROR_SUCCESS;
  315. ErrorReturn:
  316. if (NULL != pSdReadOnly) { HeapFree(GetProcessHeap(), 0, pSdReadOnly); }
  317. if (NULL != pDaclNew) { HeapFree(GetProcessHeap(), 0, pDaclNew); }
  318. return dwResult;
  319. SET_DWRESULT(AddAceError, GetLastError());
  320. SET_DWRESULT(AddAccessAllowedAceError, GetLastError());
  321. SET_DWRESULT(GetAceError, GetLastError());
  322. SET_DWRESULT(GetAclInformationError, GetLastError());
  323. SET_DWRESULT(GetSecurityDescriptorDaclError, GetLastError());
  324. SET_DWRESULT(GetUserObjectSecurityError, GetLastError());
  325. SET_DWRESULT(InitializeAclError, GetLastError());
  326. SET_DWRESULT(InitializeSecurityDescriptorError, GetLastError());
  327. SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY);
  328. SET_DWRESULT(SetSecurityDescriptorDaclError, GetLastError());
  329. SET_DWRESULT(SetUserObjectSecurityError, GetLastError());
  330. }
  331. DWORD ModifyUserAccessToDesktop
  332. (IN HANDLE hWinsta,
  333. IN HANDLE hDesk,
  334. IN HANDLE hToken,
  335. IN BOOL fAdd)
  336. {
  337. BOOL fEnteredCriticalSection = FALSE;
  338. BYTE rgbSidBuff[256];
  339. DWORD dwResult = ERROR_SUCCESS;
  340. DWORD dwReturnedLen;
  341. PTOKEN_USER pTokenUser = NULL;
  342. // Get the SID from the token:
  343. pTokenUser = (PSID)&rgbSidBuff[0];
  344. if (!GetTokenInformation(hToken, TokenUser, pTokenUser, sizeof(rgbSidBuff), &dwReturnedLen))
  345. goto GetTokenInformationError;
  346. // We don't want any other threads messing with the desktop ACL
  347. EnterCriticalSection(&csForDesktop);
  348. fEnteredCriticalSection = TRUE;
  349. if (NULL != hWinsta)
  350. {
  351. dwResult = ModifyUserAccessToObject(hWinsta, pTokenUser->User.Sid, WINSTA_ALL, NO_PROPAGATE_INHERIT_ACE, fAdd);
  352. if (ERROR_SUCCESS != dwResult)
  353. goto ModifyUserAccessToObjectError;
  354. }
  355. if (NULL != hDesk)
  356. {
  357. dwResult = ModifyUserAccessToObject(hDesk, pTokenUser->User.Sid, DESKTOP_ALL, 0, fAdd);
  358. if (ERROR_SUCCESS != dwResult)
  359. goto ModifyUserAccessToObjectError;
  360. }
  361. dwResult = ERROR_SUCCESS;
  362. ErrorReturn:
  363. if (fEnteredCriticalSection) { LeaveCriticalSection(&csForDesktop); }
  364. return dwResult;
  365. SET_DWRESULT(GetTokenInformationError, GetLastError());
  366. TRACE_ERROR (ModifyUserAccessToObjectError);
  367. }
  368. DWORD
  369. WINAPI
  370. WaitForNextJobTermination(PVOID pvIgnored)
  371. {
  372. BOOL fResult;
  373. DWORD dwNumberOfBytes;
  374. DWORD dwResult;
  375. OVERLAPPED *po;
  376. ULONG_PTR ulptrCompletionKey;
  377. while (TRUE)
  378. {
  379. fResult = GetQueuedCompletionStatus(g_hIOCP, &dwNumberOfBytes, &ulptrCompletionKey, &po, INFINITE);
  380. if (!fResult) {
  381. // We've encountered an error. Shutdown our cleanup thread -- the next runas will queue another one.
  382. EnterCriticalSection(&csForProcessCount);
  383. g_fCleanupThreadActive = FALSE;
  384. LeaveCriticalSection(&csForProcessCount);
  385. goto GetQueuedCompletionStatusError;
  386. }
  387. // When waiting on job objects, the dwNumberOfBytes contains a message ID, indicating
  388. // the event which just occured.
  389. switch (dwNumberOfBytes)
  390. {
  391. case JOB_OBJECT_MSG_ACTIVE_PROCESS_ZERO:
  392. {
  393. BOOL fLastJob;
  394. // All of our processes have terminated. Call our cleanup function.
  395. SecondaryLogonCleanupJob((LPVOID)ulptrCompletionKey /*job index*/, &fLastJob);
  396. if (fLastJob)
  397. {
  398. // There are no more jobs -- we're done processing notification.
  399. goto CommonReturn;
  400. }
  401. else
  402. {
  403. // More jobs left to clean up. Keep processing...
  404. }
  405. }
  406. default:;
  407. // some message we don't care about. Try again.
  408. }
  409. }
  410. CommonReturn:
  411. dwResult = ERROR_SUCCESS;
  412. ErrorReturn:
  413. return dwResult;
  414. SET_DWRESULT(GetQueuedCompletionStatusError, GetLastError());
  415. }
  416. VOID
  417. SecondaryLogonCleanupJob(
  418. LPVOID pvJobIndex,
  419. BOOL *pfLastJob
  420. )
  421. /*++
  422. Routine Description:
  423. This routine is a process cleanup handler when one of the secondary
  424. logon process goes away.
  425. Arguments:
  426. dwProcessIndex -- the actual index to the process, the pointer is cast
  427. back to dword. THIS IS SAFE IN SUNDOWN.
  428. fWaitStatus -- status of the wait done by one of services.exe threads.
  429. Return Value:
  430. always 0.
  431. --*/
  432. {
  433. DWORD dwJobIndex = PtrToUlong(pvJobIndex);
  434. DWORD ProcessNum = dwJobIndex; // 1-to-1 mapping between jobs and runas'd processes
  435. DWORD dwResult;
  436. EnterCriticalSection(&csForProcessCount);
  437. // We've found another process in this job.
  438. if(g_psli[ProcessNum])
  439. {
  440. // we don't care about return value.
  441. ModifyUserAccessToDesktop(g_psli[ProcessNum]->hWinsta, g_psli[ProcessNum]->hDesk, g_hToken[ProcessNum], FALSE /*remove*/);
  442. Free_SECONDARYLOGONINFOW(g_psli[ProcessNum]);
  443. g_psli[ProcessNum] = NULL;
  444. }
  445. if(g_hProcess[ProcessNum])
  446. {
  447. CloseHandle(g_hProcess[ProcessNum]);
  448. g_hProcess[ProcessNum] = NULL;
  449. }
  450. if(g_hProfile[ProcessNum] != NULL)
  451. {
  452. UnloadUserProfile(g_hToken[ProcessNum], g_hProfile[ProcessNum]);
  453. g_hProfile[ProcessNum] = NULL;
  454. }
  455. if(g_hToken[ProcessNum])
  456. {
  457. CloseHandle(g_hToken[ProcessNum]);
  458. g_hToken[ProcessNum] = NULL;
  459. }
  460. // Close off this job:
  461. CloseHandle(g_Jobs[dwJobIndex].Job);
  462. g_Jobs[dwJobIndex].Job = NULL;
  463. *pfLastJob = --g_nNumSecondaryLogonProcesses == 0;
  464. // If it's the last job, the cleanup thread terminates:
  465. g_fCleanupThreadActive = !(*pfLastJob);
  466. // Update the service status to reflect whether there is a runas'd process alive.
  467. MySetServiceStatus(SERVICE_RUNNING, 0, 0, 0);
  468. LeaveCriticalSection(&csForProcessCount);
  469. return;
  470. }
  471. VOID
  472. APIENTRY
  473. SecondaryLogonProcessWatchdogNewProcess(
  474. PSECONDARYLOGONWATCHINFO dwParam
  475. )
  476. /*++
  477. Routine Description:
  478. This routine puts the secondary logon process created on the wait queue
  479. such that cleanup can be done after the process dies.
  480. Arguments:
  481. dwParam -- the pointer to the process information.
  482. Return Value:
  483. none.
  484. --*/
  485. {
  486. DWORD j, FirstFreeJob;
  487. unsigned __int3264 i;
  488. BOOL JobFound;
  489. JOBOBJECT_ASSOCIATE_COMPLETION_PORT joacp;
  490. LUID ProcessLogonId;
  491. ULONG_PTR ulptrJobIndex;
  492. if (dwParam != NULL) {
  493. PSECONDARYLOGONWATCHINFO pslwi = (PSECONDARYLOGONWATCHINFO) dwParam;
  494. EnterCriticalSection(&csForProcessCount);
  495. for(i=0;i<MAXIMUM_SECLOGON_PROCESSES;i++)
  496. {
  497. // if(g_hProcess[i] == LongToHandle(0xDEADBEEF)) break;
  498. if(g_hProcess[i] == NULL) break;
  499. }
  500. g_hProcess[i] = pslwi->hProcess;
  501. g_hToken[i] = pslwi->hToken;
  502. g_hProfile[i] = pslwi->hProfile;
  503. g_LogonId[i].LowPart = pslwi->LogonId.LowPart;
  504. g_LogonId[i].HighPart = pslwi->LogonId.HighPart;
  505. g_psli[i] = pslwi->psli;
  506. // Initialize this job with the logon ID of the client process.
  507. // If this is a recursive runas, we'll override this value in the following loop
  508. g_Jobs[i].LogonId.LowPart = g_LogonId[i].LowPart;
  509. g_Jobs[i].LogonId.HighPart = g_LogonId[i].HighPart;
  510. // Determine which logon session the new process should be associated with.
  511. for(j=0;j<MAXIMUM_SECLOGON_PROCESSES;j++)
  512. {
  513. if(g_Jobs[j].Job != NULL)
  514. {
  515. SlpGetClientLogonId(g_hProcess[j], &ProcessLogonId);
  516. if(ProcessLogonId.LowPart == g_LogonId[i].LowPart && ProcessLogonId.HighPart == g_LogonId[i].HighPart)
  517. {
  518. JobFound = TRUE;
  519. g_Jobs[i].LogonId.LowPart = g_Jobs[j].LogonId.LowPart;
  520. g_Jobs[i].LogonId.HighPart = g_Jobs[j].LogonId.HighPart;
  521. break;
  522. }
  523. }
  524. }
  525. // Increment the number of runas'd processes
  526. g_nNumSecondaryLogonProcesses++;
  527. // BUGBUG: we currently have no means of recovering from failures
  528. // in the functions below.
  529. // we have to create a new one;
  530. g_Jobs[i].Job = CreateJobObject(NULL, NULL);
  531. if (NULL != g_Jobs[i].Job)
  532. {
  533. if (AssignProcessToJobObject(g_Jobs[i].Job, g_hProcess[i]))
  534. {
  535. ulptrJobIndex = i;
  536. // Register our IO completion port to wait for events from this job:
  537. joacp.CompletionKey = (LPVOID)ulptrJobIndex;
  538. joacp.CompletionPort = g_hIOCP;
  539. if (SetInformationJobObject(g_Jobs[i].Job, JobObjectAssociateCompletionPortInformation, &joacp, sizeof(joacp)))
  540. {
  541. // If we don't already have a cleanup thread running, start one now:
  542. if (!g_fCleanupThreadActive)
  543. {
  544. g_fCleanupThreadActive = QueueUserWorkItem(WaitForNextJobTermination, NULL, WT_EXECUTELONGFUNCTION);
  545. }
  546. }
  547. }
  548. }
  549. // Update the service status to reflect that there is a runas'd process
  550. // This prevents the service from receiving SERVICE_STOP controls
  551. // while runas'd processes are alive.
  552. MySetServiceStatus(SERVICE_RUNNING, 0, 0, 0);
  553. LeaveCriticalSection(&csForProcessCount);
  554. } else {
  555. //
  556. // We were just awakened in order to terminate the service (nothing to do)
  557. //
  558. }
  559. }
  560. DWORD ServiceStop(BOOL fShutdown, DWORD dwExitCode)
  561. {
  562. DWORD dwCheckPoint = 0;
  563. DWORD dwIndex;
  564. DWORD dwResult;
  565. // Don't want the process count to change while we're shutting down the service!
  566. EnterCriticalSection(&csForProcessCount);
  567. // Only stop if we have no runas'd processes, or if we're shutting down
  568. if (fShutdown || 0 == g_nNumSecondaryLogonProcesses) {
  569. dwResult = MySetServiceStatus(SERVICE_STOP_PENDING, dwCheckPoint++, 0, 0);
  570. _JumpCondition(ERROR_SUCCESS != dwResult && !fShutdown, MySetServiceStatusError);
  571. // We shouldn't hold the critical section while we're shutting down the RPC server,
  572. // because RPC threads may be trying to acquire it.
  573. LeaveCriticalSection(&csForProcessCount);
  574. dwResult = SeclStopRpcServer();
  575. _JumpCondition(ERROR_SUCCESS != dwResult && !fShutdown, SeclStopRpcServerError);
  576. dwResult = MySetServiceStatus(SERVICE_STOP_PENDING, dwCheckPoint++, 0, 0);
  577. _JumpCondition(ERROR_SUCCESS != dwResult && !fShutdown, MySetServiceStatusError);
  578. g_fTerminateSecondaryLogonService = TRUE;
  579. if (g_fIsCsInitialized)
  580. {
  581. DeleteCriticalSection(&csForProcessCount);
  582. DeleteCriticalSection(&csForDesktop);
  583. g_fIsCsInitialized = FALSE;
  584. }
  585. if (NULL != g_hIOCP)
  586. {
  587. CloseHandle(g_hIOCP);
  588. g_hIOCP = NULL;
  589. }
  590. // Unlike MySetServiceStatus, this routine doesn't access any
  591. // global state which could have been freed:
  592. dwResult = MySetServiceStopped(dwExitCode);
  593. _JumpCondition(ERROR_SUCCESS != dwResult && !fShutdown, MySetServiceStopped);
  594. }
  595. dwResult = ERROR_SUCCESS;
  596. ErrorReturn:
  597. return dwResult;
  598. SET_DWRESULT(MySetServiceStatusError, dwResult);
  599. SET_DWRESULT(MySetServiceStopped, dwResult);
  600. SET_DWRESULT(SeclStopRpcServerError, dwResult);
  601. }
  602. void
  603. WINAPI
  604. ServiceHandler(
  605. DWORD fdwControl
  606. )
  607. /*++
  608. Routine Description:
  609. Service handler which wakes up the main service thread when ever
  610. service controller needs to send a message.
  611. Arguments:
  612. fdwControl -- the control from the service controller.
  613. Return Value:
  614. none.
  615. --*/
  616. {
  617. BOOL fResult;
  618. BOOL fCanStopService = TRUE;
  619. DWORD dwNextState = g_state.serviceStatus.dwCurrentState;
  620. DWORD dwResult;
  621. switch (fdwControl)
  622. {
  623. case SERVICE_CONTROL_CONTINUE:
  624. dwResult = MySetServiceStatus(SERVICE_CONTINUE_PENDING, 0, 0, 0);
  625. _JumpCondition(ERROR_SUCCESS != dwResult, MySetServiceStatusError);
  626. dwResult = SeclStartRpcServer();
  627. _JumpCondition(ERROR_SUCCESS != dwResult, StartRpcServerError);
  628. dwNextState = SERVICE_RUNNING;
  629. break;
  630. case SERVICE_CONTROL_INTERROGATE:
  631. break;
  632. case SERVICE_CONTROL_PAUSE:
  633. dwResult = MySetServiceStatus(SERVICE_PAUSE_PENDING, 0, 0, 0);
  634. _JumpCondition(ERROR_SUCCESS != dwResult, MySetServiceStatusError);
  635. dwResult = SeclStopRpcServer();
  636. _JumpCondition(ERROR_SUCCESS != dwResult, StopRpcServerError);
  637. dwNextState = SERVICE_PAUSED;
  638. break;
  639. case SERVICE_CONTROL_STOP:
  640. dwResult = ServiceStop(FALSE /*fShutdown*/, ERROR_SUCCESS);
  641. _JumpCondition(ERROR_SUCCESS != dwResult, ServiceStopError);
  642. return ; // All global state has been freed, just exit.
  643. case SERVICE_CONTROL_SHUTDOWN:
  644. dwResult = ServiceStop(TRUE /*fShutdown*/, ERROR_SUCCESS);
  645. _JumpCondition(ERROR_SUCCESS != dwResult, ServiceStopError);
  646. return ; // All global state has been freed, just exit.
  647. default:
  648. // Unhandled service control!
  649. goto ErrorReturn;
  650. }
  651. CommonReturn:
  652. // Restore the original state on error, set the new state on success.
  653. dwResult = MySetServiceStatus(dwNextState, 0, 0, 0);
  654. return;
  655. ErrorReturn:
  656. goto CommonReturn;
  657. SET_ERROR(MySetServiceStatusError, dwResult);
  658. SET_ERROR(ServiceStopError, dwResult);
  659. SET_ERROR(StartRpcServerError, dwResult);
  660. SET_ERROR(StopRpcServerError, dwResult);
  661. }
  662. VOID
  663. SlrCreateProcessWithLogon
  664. (IN RPC_BINDING_HANDLE hRPCBinding,
  665. IN PSECONDARYLOGONINFOW psli,
  666. OUT PSECONDARYLOGONRETINFO pslri)
  667. /*++
  668. Routine Description:
  669. The core routine -- it handles a client request to start a secondary
  670. logon process.
  671. Arguments:
  672. psli -- the input structure with client request information
  673. pslri -- the output structure with response back to the client.
  674. Return Value:
  675. none.
  676. --*/
  677. {
  678. BOOL fAccessWasAllowed = FALSE;
  679. HANDLE hCurrentThread = NULL;
  680. HANDLE hCurrentThreadToken = NULL;
  681. HANDLE hToken = NULL;
  682. HANDLE hProfile = NULL;
  683. HANDLE hProcessClient = NULL;
  684. PVOID pvEnvBlock = NULL, pvUserProfile = NULL;
  685. BOOL fCreatedEnvironmentBlock = FALSE;
  686. BOOL fIsImpersonatingRpcClient = FALSE;
  687. BOOL fIsImpersonatingClient = FALSE;
  688. BOOL fInheritHandles = FALSE;
  689. BOOL fOpenedSTDIN = FALSE;
  690. BOOL fOpenedSTDOUT = FALSE;
  691. BOOL fOpenedSTDERR = FALSE;
  692. PROFILEINFO pi;
  693. SECURITY_ATTRIBUTES sa;
  694. SECONDARYLOGONWATCHINFO slwi;
  695. DWORD dwResult;
  696. DWORD SessionId;
  697. DWORD dwLogonProvider;
  698. SECURITY_LOGON_TYPE LogonType;
  699. __try {
  700. //
  701. // Do some security checks:
  702. //
  703. // 1) We should impersonate the client and then try to open
  704. // the process so that we are assured that they didn't
  705. // give us some fake id.
  706. //
  707. dwResult = RpcImpersonateClient(hRPCBinding);
  708. _JumpCondition(RPC_S_OK != dwResult, leave_with_last_error);
  709. fIsImpersonatingRpcClient = TRUE;
  710. hProcessClient = OpenProcess(MAXIMUM_ALLOWED, FALSE, psli->dwProcessId);
  711. _JumpCondition(hProcessClient == NULL, leave_with_last_error);
  712. #if 0
  713. //
  714. // 2) Check that the client is not running from a restricted account.
  715. //
  716. hCurrentThread = GetCurrentThread(); // Doesn't need to be freed with CloseHandle().
  717. _JumpCondition(NULL == hCurrentThread, leave_with_last_error);
  718. _JumpCondition(FALSE == OpenThreadToken(hCurrentThread,
  719. TOKEN_QUERY | TOKEN_DUPLICATE,
  720. TRUE,
  721. &hCurrentThreadToken),
  722. leave_with_last_error);
  723. #endif
  724. dwResult = RpcRevertToSelfEx(hRPCBinding);
  725. if (RPC_S_OK != dwResult)
  726. {
  727. __leave;
  728. }
  729. fIsImpersonatingRpcClient = FALSE;
  730. #if 0
  731. if (TRUE == IsTokenUntrusted(hCurrentThreadToken))
  732. {
  733. dwResult = ERROR_ACCESS_DENIED;
  734. __leave;
  735. }
  736. #endif
  737. //
  738. // We should get the session id from process id
  739. // we will set this up in the token so that create process
  740. // happens on the correct session.
  741. //
  742. _JumpCondition(!ProcessIdToSessionId(psli->dwProcessId, &SessionId), leave_with_last_error);
  743. //
  744. // Get the unique logonId.
  745. // we will use this to cleanup any running processes
  746. // when the logoff happens.
  747. //
  748. dwResult = SlpGetClientLogonId(hProcessClient, &slwi.LogonId);
  749. if(dwResult != ERROR_SUCCESS)
  750. {
  751. __leave;
  752. }
  753. if ((psli->lpStartupInfo->dwFlags & STARTF_USESTDHANDLES) != 0)
  754. {
  755. _JumpCondition(!DuplicateHandle
  756. (hProcessClient,
  757. psli->lpStartupInfo->hStdInput,
  758. GetCurrentProcess(),
  759. &psli->lpStartupInfo->hStdInput,
  760. 0,
  761. TRUE, DUPLICATE_SAME_ACCESS),
  762. leave_with_last_error);
  763. fOpenedSTDIN = TRUE;
  764. _JumpCondition(!DuplicateHandle
  765. (hProcessClient,
  766. psli->lpStartupInfo->hStdOutput,
  767. GetCurrentProcess(),
  768. &psli->lpStartupInfo->hStdOutput,
  769. 0,
  770. TRUE,
  771. DUPLICATE_SAME_ACCESS),
  772. leave_with_last_error);
  773. fOpenedSTDOUT = TRUE;
  774. _JumpCondition(!DuplicateHandle
  775. (hProcessClient,
  776. psli->lpStartupInfo->hStdError,
  777. GetCurrentProcess(),
  778. &psli->lpStartupInfo->hStdError,
  779. 0,
  780. TRUE,
  781. DUPLICATE_SAME_ACCESS),
  782. leave_with_last_error);
  783. fOpenedSTDERR = TRUE;
  784. fInheritHandles = TRUE;
  785. }
  786. else
  787. {
  788. psli->lpStartupInfo->hStdInput = INVALID_HANDLE_VALUE;
  789. psli->lpStartupInfo->hStdOutput = INVALID_HANDLE_VALUE;
  790. psli->lpStartupInfo->hStdError = INVALID_HANDLE_VALUE;
  791. }
  792. if(psli->dwLogonFlags & LOGON_NETCREDENTIALS_ONLY)
  793. {
  794. LogonType = (SECURITY_LOGON_TYPE)LOGON32_LOGON_NEW_CREDENTIALS;
  795. dwLogonProvider = LOGON32_PROVIDER_WINNT50;
  796. }
  797. else
  798. {
  799. LogonType = (SECURITY_LOGON_TYPE) LOGON32_LOGON_INTERACTIVE;
  800. dwLogonProvider = LOGON32_PROVIDER_DEFAULT;
  801. }
  802. // Duplicate windowstation handles received from the client process
  803. // so that they're valid in this process.
  804. if (NULL != psli->hWinsta)
  805. {
  806. _JumpCondition(!DuplicateHandle(hProcessClient, psli->hWinsta, GetCurrentProcess(), &psli->hWinsta, 0, TRUE, DUPLICATE_SAME_ACCESS),
  807. leave_with_last_error);
  808. psli->fFreeWinsta = TRUE;
  809. }
  810. if (NULL != psli->hDesk)
  811. {
  812. _JumpCondition(!DuplicateHandle(hProcessClient, psli->hDesk, GetCurrentProcess(), &psli->hDesk, 0, TRUE, DUPLICATE_SAME_ACCESS),
  813. leave_with_last_error);
  814. psli->fFreeDesk = TRUE;
  815. }
  816. // LogonUser does not return profile information, we need to grab
  817. // that out of band after the logon has completed.
  818. //
  819. dwResult = RpcImpersonateClient(hRPCBinding);
  820. _JumpCondition(RPC_S_OK != dwResult, leave_with_last_error);
  821. fIsImpersonatingRpcClient = TRUE;
  822. _JumpCondition(!LogonUser(psli->lpUsername,
  823. psli->lpDomain,
  824. psli->lpPassword,
  825. LogonType,
  826. dwLogonProvider,
  827. &hToken),
  828. leave_with_last_error);
  829. if (0 == (SECLOGON_CALLER_SPECIFIED_DESKTOP & psli->dwSeclogonFlags))
  830. {
  831. // If the caller did not specify their own desktop, it is our responsibility
  832. // to grant the user access to the default desktop:
  833. dwResult = ModifyUserAccessToDesktop(psli->hWinsta, psli->hDesk, hToken, TRUE /*grant*/);
  834. if (ERROR_SUCCESS != dwResult)
  835. __leave;
  836. fAccessWasAllowed = TRUE;
  837. }
  838. dwResult = RpcRevertToSelfEx(hRPCBinding);
  839. if (RPC_S_OK != dwResult)
  840. {
  841. __leave;
  842. }
  843. fIsImpersonatingRpcClient = FALSE;
  844. // Let us set the SessionId in the Token.
  845. _JumpCondition(!SetTokenInformation(hToken, TokenSessionId, &SessionId, sizeof(DWORD)),
  846. leave_with_last_error);
  847. // Load the user's profile.
  848. // if this fails, we will not continue
  849. //
  850. if(psli->dwLogonFlags & LOGON_WITH_PROFILE)
  851. {
  852. _JumpCondition(!SlpLoadUserProfile(hToken, &hProfile), leave_with_last_error);
  853. }
  854. // we should now impersonate the user.
  855. //
  856. _JumpCondition(!ImpersonateLoggedOnUser(hToken), leave_with_last_error);
  857. fIsImpersonatingClient = TRUE;
  858. // Query Default Owner/ACL from token. Make SD with this stuff, pass for
  859. sa.nLength = sizeof(sa);
  860. sa.bInheritHandle = FALSE;
  861. sa.lpSecurityDescriptor = NULL;
  862. //
  863. // We should set the console control handler so CtrlC is correctly
  864. // handled by the new process.
  865. //
  866. // SetConsoleCtrlHandler(NULL, FALSE);
  867. //
  868. // if lpEnvironment is NULL, we create new one for this user
  869. // using CreateEnvironmentBlock
  870. //
  871. if(NULL == (psli->lpEnvironment))
  872. {
  873. if(FALSE == CreateEnvironmentBlock( &(psli->lpEnvironment), hToken, FALSE ))
  874. {
  875. psli->lpEnvironment = NULL;
  876. }
  877. else
  878. {
  879. // Successfully created environment block.
  880. fCreatedEnvironmentBlock = TRUE;
  881. }
  882. }
  883. _JumpCondition(!CreateProcessAsUser(hToken,
  884. psli->lpApplicationName,
  885. psli->lpCommandLine,
  886. &sa,
  887. &sa,
  888. fInheritHandles,
  889. psli->dwCreationFlags | CREATE_UNICODE_ENVIRONMENT,
  890. psli->lpEnvironment,
  891. psli->lpCurrentDirectory,
  892. psli->lpStartupInfo,
  893. &pslri->pi),
  894. leave_with_last_error);
  895. SetLastError(NO_ERROR);
  896. leave_with_last_error:
  897. dwResult = GetLastError();
  898. __leave;
  899. }
  900. __finally {
  901. pslri->dwErrorCode = dwResult;
  902. if (fCreatedEnvironmentBlock) { DestroyEnvironmentBlock(psli->lpEnvironment); }
  903. if (fIsImpersonatingClient) { RevertToSelf(); /* Ignore retval: nothing we can do on failure! */ }
  904. if (fIsImpersonatingRpcClient) { RpcRevertToSelfEx(hRPCBinding); /* Ignore retval: nothing we can do on failure! */ }
  905. if (fOpenedSTDIN) { CloseHandle(psli->lpStartupInfo->hStdInput); }
  906. if (fOpenedSTDOUT) { CloseHandle(psli->lpStartupInfo->hStdOutput); }
  907. if (fOpenedSTDERR) { CloseHandle(psli->lpStartupInfo->hStdError); }
  908. if(pslri->dwErrorCode != NO_ERROR)
  909. {
  910. if (NULL != hProfile) { UnloadUserProfile(hToken, hProfile); }
  911. if (fAccessWasAllowed) { ModifyUserAccessToDesktop(psli->hWinsta, psli->hDesk, hToken, FALSE /*remove*/); }
  912. if (NULL != hToken) { CloseHandle(hToken); }
  913. }
  914. else
  915. {
  916. // Start the watchdog process last so it won't delete psli before we're done with it.
  917. slwi.hProcess = pslri->pi.hProcess;
  918. slwi.hToken = hToken;
  919. slwi.hProfile = hProfile;
  920. // LogonId was already filled up.. right at the begining.
  921. slwi.psli = psli;
  922. SecondaryLogonProcessWatchdogNewProcess(&slwi);
  923. // SetConsoleCtrlHandler(NULL, TRUE);
  924. //
  925. // Have the watchdog watch this newly added process so that
  926. // cleanup will occur correctly when the process terminates.
  927. //
  928. // Set up the windowstation and desktop for the process
  929. DuplicateHandle(GetCurrentProcess(), pslri->pi.hProcess,
  930. hProcessClient, &pslri->pi.hProcess, 0, FALSE,
  931. DUPLICATE_SAME_ACCESS);
  932. DuplicateHandle(GetCurrentProcess(), pslri->pi.hThread, hProcessClient,
  933. &pslri->pi.hThread, 0, FALSE,
  934. DUPLICATE_SAME_ACCESS | DUPLICATE_CLOSE_SOURCE);
  935. }
  936. if (NULL != hProcessClient) { CloseHandle(hProcessClient); }
  937. if (NULL != hCurrentThreadToken) { CloseHandle(hCurrentThreadToken); }
  938. }
  939. }
  940. void
  941. WINAPI
  942. ServiceMain
  943. (IN DWORD dwArgc,
  944. IN WCHAR ** lpszArgv)
  945. /*++
  946. Routine Description:
  947. The main service handler thread routine.
  948. Arguments:
  949. Return Value:
  950. none.
  951. --*/
  952. {
  953. DWORD i, dwResult, dwWaitResult;
  954. HANDLE rghWait[4];
  955. for (i = 0; i < MAXIMUM_SECLOGON_PROCESSES; i++)
  956. {
  957. // g_hProcess[i] = LongToHandle(0xDEADBEEF);
  958. g_Jobs[i].Job = NULL;
  959. g_hProcess[i] = NULL;
  960. g_hProfile[i] = NULL;
  961. g_hToken[i] = NULL;
  962. }
  963. __try {
  964. InitializeCriticalSection(&csForProcessCount);
  965. InitializeCriticalSection(&csForDesktop);
  966. g_fIsCsInitialized = TRUE;
  967. }
  968. __except (EXCEPTION_EXECUTE_HANDLER) {
  969. return; // We can't do anything if we can't initialize this critsec
  970. }
  971. g_hIOCP = CreateIoCompletionPort(INVALID_HANDLE_VALUE, NULL, 0,0);
  972. _JumpCondition(NULL == g_hIOCP, CreateIoCompletionPortError);
  973. dwResult = InitGlobalState();
  974. _JumpCondition(ERROR_SUCCESS != dwResult, InitGlobalStateError);
  975. // NOTE: hSS does not have to be closed.
  976. g_state.hServiceStatus = RegisterServiceCtrlHandler(wszSvcName, ServiceHandler);
  977. _JumpCondition(NULL == g_state.hServiceStatus, RegisterServiceCtrlHandlerError);
  978. dwResult = SeclStartRpcServer();
  979. _JumpCondition(ERROR_SUCCESS != dwResult, StartRpcServerError);
  980. // Tell the SCM we're up and running:
  981. dwResult = MySetServiceStatus(SERVICE_RUNNING, 0, 0, ERROR_SUCCESS);
  982. _JumpCondition(ERROR_SUCCESS != dwResult, MySetServiceStatusError);
  983. SetLastError(ERROR_SUCCESS);
  984. ErrorReturn:
  985. // Shut down the service if we couldn't fully start:
  986. if (ERROR_SUCCESS != GetLastError()) {
  987. ServiceStop(TRUE /*fShutdown*/, GetLastError());
  988. }
  989. return;
  990. SET_ERROR(InitGlobalStateError, dwResult)
  991. SET_ERROR(MySetServiceStatusError, dwResult);
  992. SET_ERROR(RegisterServiceCtrlHandlerError, dwResult);
  993. SET_ERROR(StartRpcServerError, dwResult);
  994. TRACE_ERROR(CreateIoCompletionPortError);
  995. }
  996. DWORD
  997. InstallService()
  998. /*++
  999. Routine Description:
  1000. It installs the service with service controller, basically creating
  1001. the service object.
  1002. Arguments:
  1003. none.
  1004. Return Value:
  1005. several - as returned by the service controller.
  1006. --*/
  1007. {
  1008. // TCHAR *szModulePathname;
  1009. TCHAR AppName[MAX_PATH];
  1010. LPTSTR ptszAppName = NULL;
  1011. SC_HANDLE hService;
  1012. DWORD dw;
  1013. HANDLE hMod;
  1014. //
  1015. // Open the SCM on this machine.
  1016. //
  1017. SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CREATE_SERVICE);
  1018. if(hSCM == NULL) {
  1019. dw = GetLastError();
  1020. return dw;
  1021. }
  1022. //
  1023. // Let us give the service a useful description
  1024. // This is not earth shattering... if it works fine, if it
  1025. // doesn't it is just too bad :-)
  1026. //
  1027. hMod = GetModuleHandle(L"seclogon.dll");
  1028. //
  1029. // we'll try to get the localized name for the service,
  1030. // if it fails, we'll just put an english string...
  1031. //
  1032. if(hMod != NULL)
  1033. {
  1034. LoadString(hMod,
  1035. SECLOGON_STRING_NAME,
  1036. AppName,
  1037. MAX_PATH
  1038. );
  1039. ptszAppName = AppName;
  1040. }
  1041. else
  1042. ptszAppName = L"RunAs Service";
  1043. //
  1044. // Add this service to the SCM's database.
  1045. //
  1046. hService = CreateService
  1047. (hSCM,
  1048. wszSvcName,
  1049. ptszAppName,
  1050. SERVICE_ALL_ACCESS,
  1051. SERVICE_WIN32_SHARE_PROCESS,
  1052. SERVICE_AUTO_START,
  1053. SERVICE_ERROR_IGNORE,
  1054. L"%SystemRoot%\\system32\\svchost.exe -k netsvcs",
  1055. NULL,
  1056. NULL,
  1057. NULL,
  1058. NULL,
  1059. NULL);
  1060. if(hService == NULL) {
  1061. dw = GetLastError();
  1062. CloseServiceHandle(hSCM);
  1063. return dw;
  1064. }
  1065. if(hMod != NULL)
  1066. {
  1067. WCHAR DescString[500];
  1068. SERVICE_DESCRIPTION SvcDesc;
  1069. LoadString( hMod,
  1070. SECLOGON_STRING_DESCRIPTION,
  1071. DescString,
  1072. 500
  1073. );
  1074. SvcDesc.lpDescription = DescString;
  1075. ChangeServiceConfig2( hService,
  1076. SERVICE_CONFIG_DESCRIPTION,
  1077. &SvcDesc
  1078. );
  1079. }
  1080. //
  1081. // Close the service and the SCM
  1082. //
  1083. CloseServiceHandle(hService);
  1084. CloseServiceHandle(hSCM);
  1085. return S_OK;
  1086. }
  1087. DWORD
  1088. RemoveService()
  1089. /*++
  1090. Routine Description:
  1091. deinstalls the service.
  1092. Arguments:
  1093. none.
  1094. Return Value:
  1095. as returned by service controller apis.
  1096. --*/
  1097. {
  1098. DWORD dw;
  1099. SC_HANDLE hService;
  1100. //
  1101. // Open the SCM on this machine.
  1102. //
  1103. SC_HANDLE hSCM = OpenSCManager(NULL, NULL, SC_MANAGER_CONNECT);
  1104. if(hSCM == NULL) {
  1105. dw = GetLastError();
  1106. return dw;
  1107. }
  1108. //
  1109. // Open this service for DELETE access
  1110. //
  1111. hService = OpenService(hSCM, wszSvcName, DELETE);
  1112. if(hService == NULL) {
  1113. dw = GetLastError();
  1114. CloseServiceHandle(hSCM);
  1115. return dw;
  1116. }
  1117. //
  1118. // Remove this service from the SCM's database.
  1119. //
  1120. DeleteService(hService);
  1121. //
  1122. // Close the service and the SCM
  1123. //
  1124. CloseServiceHandle(hService);
  1125. CloseServiceHandle(hSCM);
  1126. return S_OK;
  1127. }
  1128. void SvchostPushServiceGlobals(PSVCHOST_GLOBAL_DATA pGlobalData) {
  1129. // this entry point is called by svchost.exe
  1130. GlobalData=pGlobalData;
  1131. }
  1132. void SvcEntry_Seclogon
  1133. (IN DWORD argc,
  1134. IN WCHAR **argv)
  1135. /*++
  1136. Routine Description:
  1137. Entry point for the service dll when running in svchost.exe
  1138. Arguments:
  1139. Return Value:
  1140. --*/
  1141. {
  1142. ServiceMain(0,NULL);
  1143. UNREFERENCED_PARAMETER(argc);
  1144. UNREFERENCED_PARAMETER(argv);
  1145. }
  1146. STDAPI
  1147. DllRegisterServer(void)
  1148. /*++
  1149. Routine Description:
  1150. Arguments:
  1151. Return Value:
  1152. --*/
  1153. {
  1154. return InstallService();
  1155. }
  1156. STDAPI
  1157. DllUnregisterServer(void)
  1158. /*++
  1159. Routine Description:
  1160. Arguments:
  1161. Return Value:
  1162. --*/
  1163. {
  1164. return RemoveService();
  1165. }
  1166. DWORD InitGlobalState() {
  1167. ZeroMemory(&g_state, sizeof(g_state));
  1168. return ERROR_SUCCESS;
  1169. }
  1170. DWORD MySetServiceStatus(DWORD dwCurrentState, DWORD dwCheckPoint, DWORD dwWaitHint, DWORD dwExitCode) {
  1171. BOOL fResult;
  1172. DWORD dwResult;
  1173. DWORD dwAcceptStop;
  1174. int nNumProcesses;
  1175. EnterCriticalSection(&csForProcessCount);
  1176. nNumProcesses = g_nNumSecondaryLogonProcesses;
  1177. dwAcceptStop = 0 == nNumProcesses ? SERVICE_ACCEPT_STOP : 0;
  1178. g_state.serviceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
  1179. g_state.serviceStatus.dwCurrentState = dwCurrentState;
  1180. switch (dwCurrentState)
  1181. {
  1182. case SERVICE_STOPPED:
  1183. case SERVICE_STOP_PENDING:
  1184. g_state.serviceStatus.dwControlsAccepted = 0;
  1185. break;
  1186. case SERVICE_RUNNING:
  1187. case SERVICE_PAUSED:
  1188. g_state.serviceStatus.dwControlsAccepted =
  1189. // SERVICE_ACCEPT_SHUTDOWN
  1190. SERVICE_ACCEPT_PAUSE_CONTINUE
  1191. | dwAcceptStop;
  1192. break;
  1193. case SERVICE_START_PENDING:
  1194. case SERVICE_CONTINUE_PENDING:
  1195. case SERVICE_PAUSE_PENDING:
  1196. g_state.serviceStatus.dwControlsAccepted =
  1197. // SERVICE_ACCEPT_SHUTDOWN
  1198. dwAcceptStop;
  1199. break;
  1200. }
  1201. g_state.serviceStatus.dwWin32ExitCode = dwExitCode;
  1202. g_state.serviceStatus.dwCheckPoint = dwCheckPoint;
  1203. g_state.serviceStatus.dwWaitHint = dwWaitHint;
  1204. fResult = SetServiceStatus(g_state.hServiceStatus, &g_state.serviceStatus);
  1205. _JumpCondition(FALSE == fResult, SetServiceStatusError);
  1206. dwResult = ERROR_SUCCESS;
  1207. CommonReturn:
  1208. LeaveCriticalSection(&csForProcessCount);
  1209. return dwResult;
  1210. ErrorReturn:
  1211. goto CommonReturn;
  1212. SET_DWRESULT(SetServiceStatusError, GetLastError());
  1213. }
  1214. DWORD MySetServiceStopped(DWORD dwExitCode) {
  1215. BOOL fResult;
  1216. DWORD dwResult;
  1217. g_state.serviceStatus.dwServiceType = SERVICE_WIN32_SHARE_PROCESS;
  1218. g_state.serviceStatus.dwCurrentState = SERVICE_STOPPED;
  1219. g_state.serviceStatus.dwControlsAccepted = 0;
  1220. g_state.serviceStatus.dwWin32ExitCode = dwExitCode;
  1221. g_state.serviceStatus.dwCheckPoint = 0;
  1222. g_state.serviceStatus.dwWaitHint = 0;
  1223. fResult = SetServiceStatus(g_state.hServiceStatus, &g_state.serviceStatus);
  1224. _JumpCondition(FALSE == fResult, SetServiceStatusError);
  1225. dwResult = ERROR_SUCCESS;
  1226. CommonReturn:
  1227. return dwResult;
  1228. ErrorReturn:
  1229. goto CommonReturn;
  1230. SET_DWRESULT(SetServiceStatusError, GetLastError());
  1231. }
  1232. ////////////////////////////////////////////////////////////////////////
  1233. //
  1234. // Implementation of RPC interface:
  1235. //
  1236. ////////////////////////////////////////////////////////////////////////
  1237. void WINAPI SeclCreateProcessWithLogonW
  1238. (IN handle_t hRPCBinding,
  1239. IN SECL_SLI *pSeclSli,
  1240. OUT SECL_SLRI *pSeclSlri)
  1241. {
  1242. BOOL fIsImpersonatingClient = FALSE;
  1243. DWORD dwResult;
  1244. HANDLE hHeap = NULL;
  1245. PSECONDARYLOGONINFOW psli = NULL;
  1246. SECL_SLRI SeclSlri;
  1247. SECONDARYLOGONRETINFO slri;
  1248. ZeroMemory(&SeclSlri, sizeof(SeclSlri));
  1249. ZeroMemory(&slri, sizeof(slri));
  1250. // We don't want the service to be stopped while we're creating a process.
  1251. EnterCriticalSection(&csForProcessCount);
  1252. // Service isn't running anymore ... don't create the process.
  1253. _JumpCondition(SERVICE_RUNNING != g_state.serviceStatus.dwCurrentState, ServiceStoppedError);
  1254. hHeap = GetProcessHeap();
  1255. _JumpCondition(NULL == hHeap, MemoryError);
  1256. __try {
  1257. dwResult = To_SECONDARYLOGONINFOW(pSeclSli, &psli);
  1258. _JumpCondition(ERROR_SUCCESS != dwResult, To_SECONDARYLOGONINFOW_Error);
  1259. if (psli->LogonIdHighPart != 0 || psli->LogonIdLowPart != 0)
  1260. {
  1261. // This is probably a notification from winlogon.exe that
  1262. // a client is logging off. If so, we must clean up all processes
  1263. // they've left running.
  1264. int i;
  1265. LUID LogonId;
  1266. //
  1267. // We should impersonate the client,
  1268. // check it is LocalSystem and only then proceed.
  1269. //
  1270. fIsImpersonatingClient = RPC_S_OK == RpcImpersonateClient((RPC_BINDING_HANDLE)hRPCBinding);
  1271. if(FALSE == fIsImpersonatingClient || FALSE == IsSystemProcess())
  1272. {
  1273. slri.dwErrorCode = ERROR_INVALID_PARAMETER;
  1274. ZeroMemory(&slri.pi, sizeof(slri.pi));
  1275. }
  1276. else
  1277. {
  1278. LogonId.HighPart = psli->LogonIdHighPart;
  1279. LogonId.LowPart = psli->LogonIdLowPart;
  1280. for(i=0;i<MAXIMUM_SECLOGON_PROCESSES;i++)
  1281. {
  1282. //
  1283. // Let us destroy all jobs associated with
  1284. // this user. There could be more than one.
  1285. //
  1286. if(g_Jobs[i].Job == NULL)
  1287. continue;
  1288. if(g_Jobs[i].LogonId.HighPart == LogonId.HighPart && g_Jobs[i].LogonId.LowPart == LogonId.LowPart)
  1289. {
  1290. TerminateJobObject(g_Jobs[i].Job, 0);
  1291. }
  1292. }
  1293. slri.dwErrorCode = ERROR_SUCCESS;
  1294. ZeroMemory(&slri.pi, sizeof(slri.pi));
  1295. }
  1296. if (fIsImpersonatingClient)
  1297. {
  1298. // Ignore error: nothing we can do on failure!
  1299. if (RPC_S_OK == RpcRevertToSelfEx((RPC_BINDING_HANDLE)hRPCBinding))
  1300. {
  1301. fIsImpersonatingClient = FALSE;
  1302. }
  1303. }
  1304. }
  1305. else
  1306. {
  1307. // Ok, this isn't notification from winlogon, it's really a user
  1308. // trying to use the service. Create a process for them.
  1309. //
  1310. DWORD currentNumber;
  1311. currentNumber = g_nNumSecondaryLogonProcesses;
  1312. if(currentNumber == MAXIMUM_SECLOGON_PROCESSES)
  1313. {
  1314. slri.dwErrorCode = ERROR_NO_SYSTEM_RESOURCES;
  1315. ZeroMemory(&slri.pi, sizeof(slri.pi));
  1316. }
  1317. else
  1318. {
  1319. SlrCreateProcessWithLogon((RPC_BINDING_HANDLE)hRPCBinding, psli, &slri);
  1320. }
  1321. }
  1322. // If we've errored out, jump to the error handler.
  1323. _JumpCondition(NO_ERROR != slri.dwErrorCode, UnspecifiedSeclogonError);
  1324. }
  1325. __except (EXCEPTION_EXECUTE_HANDLER) {
  1326. // If anything goes wrong, return the exception code to the client
  1327. dwResult = GetExceptionCode();
  1328. goto ExceptionError;
  1329. }
  1330. CommonReturn:
  1331. // Do not free slri: this will be freed by the watchdog!
  1332. SeclSlri.hProcess = (unsigned __int64)slri.pi.hProcess;
  1333. SeclSlri.hThread = (unsigned __int64)slri.pi.hThread;
  1334. SeclSlri.ulProcessId = slri.pi.dwProcessId;
  1335. SeclSlri.ulThreadId = slri.pi.dwThreadId;
  1336. SeclSlri.ulErrorCode = slri.dwErrorCode;
  1337. LeaveCriticalSection(&csForProcessCount);
  1338. // Assign the OUT parameter:
  1339. *pSeclSlri = SeclSlri;
  1340. return;
  1341. ErrorReturn:
  1342. ZeroMemory(&slri.pi, sizeof(slri.pi));
  1343. if (NULL != psli) { Free_SECONDARYLOGONINFOW(psli); }
  1344. slri.dwErrorCode = dwResult;
  1345. goto CommonReturn;
  1346. SET_DWRESULT(ExceptionError, dwResult);
  1347. SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY);
  1348. SET_DWRESULT(ServiceStoppedError, ERROR_SERVICE_NOT_ACTIVE);
  1349. SET_DWRESULT(To_SECONDARYLOGONINFOW_Error, dwResult);
  1350. SET_DWRESULT(UnspecifiedSeclogonError, slri.dwErrorCode);
  1351. }
  1352. ////////////////////////////////////////////////////////////////////////
  1353. //
  1354. // RPC Utility methods:
  1355. //
  1356. ////////////////////////////////////////////////////////////////////////
  1357. DWORD SeclStartRpcServer() {
  1358. DWORD dwResult;
  1359. RPC_STATUS RpcStatus;
  1360. EnterCriticalSection(&csForProcessCount);
  1361. if (NULL != GlobalData) {
  1362. // we are running under services.exe - we have to play nicely and share
  1363. // the process's RPC server. (The other services wouldn't be happy if we
  1364. // shut it down while they were still using it.)
  1365. RpcStatus = GlobalData->StartRpcServer(wszSeclogonSharedProcEndpointName, ISeclogon_v1_0_s_ifspec);
  1366. _JumpCondition(RPC_S_OK != RpcStatus, StartRpcServerError);
  1367. }
  1368. dwResult = ERROR_SUCCESS;
  1369. CommonReturn:
  1370. LeaveCriticalSection(&csForProcessCount);
  1371. return dwResult;
  1372. ErrorReturn:
  1373. goto CommonReturn;
  1374. SET_DWRESULT(StartRpcServerError, RpcStatus);
  1375. }
  1376. DWORD SeclStopRpcServer() {
  1377. DWORD dwResult;
  1378. RPC_STATUS RpcStatus;
  1379. EnterCriticalSection(&csForProcessCount);
  1380. if (NULL != GlobalData) {
  1381. // we are running under services.exe - we have to play nicely and share
  1382. // the process's RPC server. (The other services wouldn't be happy if we
  1383. // shut it down while they were still using it.)
  1384. RpcStatus = GlobalData->StopRpcServer(ISeclogon_v1_0_s_ifspec);
  1385. _JumpCondition(RPC_S_OK != RpcStatus, StopRpcServerError);
  1386. }
  1387. dwResult = ERROR_SUCCESS;
  1388. CommonReturn:
  1389. LeaveCriticalSection(&csForProcessCount);
  1390. return dwResult;
  1391. ErrorReturn:
  1392. goto CommonReturn;
  1393. SET_DWRESULT(StopRpcServerError, RpcStatus);
  1394. }
  1395. HRESULT To_LPWSTR(IN SECL_STRING *pss,
  1396. OUT LPWSTR *ppwsz)
  1397. {
  1398. DWORD dwResult;
  1399. HANDLE hHeap = NULL;
  1400. LPWSTR pwsz = NULL;
  1401. hHeap = GetProcessHeap();
  1402. _JumpCondition(NULL == hHeap, GetProcessHeapError);
  1403. __try {
  1404. if (pss->ccLength > 0) {
  1405. // Prevent large heap allocs:
  1406. _JumpCondition(pss->ccLength > 4096, InvalidArgError);
  1407. pwsz = (LPWSTR)HeapAlloc(hHeap, HEAP_ZERO_MEMORY, pss->ccLength * sizeof(WCHAR));
  1408. _JumpCondition(NULL == pwsz, MemoryError);
  1409. CopyMemory(pwsz, pss->pwsz, pss->ccLength * sizeof(WCHAR));
  1410. }
  1411. } __except (EXCEPTION_EXECUTE_HANDLER) {
  1412. dwResult = GetExceptionCode();
  1413. goto ExceptionError;
  1414. }
  1415. *ppwsz = pwsz;
  1416. dwResult = ERROR_SUCCESS;
  1417. CommonReturn:
  1418. return dwResult;
  1419. ErrorReturn:
  1420. if (NULL != pwsz) { HeapFree(hHeap, 0, pwsz); }
  1421. goto CommonReturn;
  1422. SET_DWRESULT(ExceptionError, dwResult);
  1423. SET_DWRESULT(GetProcessHeapError, GetLastError());
  1424. SET_DWRESULT(InvalidArgError, ERROR_INVALID_PARAMETER);
  1425. SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY);
  1426. }
  1427. void Free_SECONDARYLOGONINFOW(IN PSECONDARYLOGONINFOW psli) {
  1428. HANDLE hHeap = GetProcessHeap();
  1429. if (NULL == hHeap)
  1430. return;
  1431. if (NULL != psli) {
  1432. if (NULL != psli->lpStartupInfo) {
  1433. if (NULL != psli->lpStartupInfo->lpDesktop) { HeapFree(hHeap, 0, psli->lpStartupInfo->lpDesktop); }
  1434. if (NULL != psli->lpStartupInfo->lpTitle) { HeapFree(hHeap, 0, psli->lpStartupInfo->lpTitle); }
  1435. HeapFree(hHeap, 0, psli->lpStartupInfo);
  1436. }
  1437. if (NULL != psli->lpUsername) { HeapFree(hHeap, 0, psli->lpUsername); }
  1438. if (NULL != psli->lpDomain) { HeapFree(hHeap, 0, psli->lpDomain); }
  1439. if (NULL != psli->lpPassword) { HeapFree(hHeap, 0, psli->lpPassword); }
  1440. if (NULL != psli->lpApplicationName) { HeapFree(hHeap, 0, psli->lpApplicationName); }
  1441. if (NULL != psli->lpCommandLine) { HeapFree(hHeap, 0, psli->lpCommandLine); }
  1442. if (NULL != psli->lpCurrentDirectory) { HeapFree(hHeap, 0, (LPVOID)psli->lpCurrentDirectory); }
  1443. if (NULL != psli->hWinsta && psli->fFreeWinsta) { CloseHandle(psli->hWinsta); }
  1444. if (NULL != psli->hDesk && psli->fFreeDesk) { CloseHandle(psli->hDesk); }
  1445. HeapFree(hHeap, 0, psli);
  1446. }
  1447. }
  1448. DWORD To_SECONDARYLOGONINFOW(IN PSECL_SLI pSeclSli,
  1449. OUT PSECONDARYLOGONINFOW *ppsli)
  1450. {
  1451. DWORD dwAllocFlags = HEAP_ZERO_MEMORY;
  1452. DWORD dwIndex;
  1453. DWORD dwResult;
  1454. HANDLE hHeap = NULL;
  1455. PSECONDARYLOGONINFOW psli = NULL;
  1456. hHeap = GetProcessHeap();
  1457. _JumpCondition(NULL == hHeap, GetProcessHeapError);
  1458. psli = (PSECONDARYLOGONINFOW)HeapAlloc(hHeap, dwAllocFlags, sizeof(SECONDARYLOGONINFOW));
  1459. _JumpCondition(NULL == psli, MemoryError);
  1460. psli->lpStartupInfo = (LPSTARTUPINFO)HeapAlloc(hHeap, dwAllocFlags, sizeof(STARTUPINFO));
  1461. _JumpCondition(NULL == psli->lpStartupInfo, MemoryError);
  1462. __try {
  1463. {
  1464. struct {
  1465. SECL_STRING *pss;
  1466. LPWSTR *ppwsz;
  1467. } rg_StringsToMap[] = {
  1468. { &(pSeclSli->ssDesktop), /* Is mapped to ----> */ &(psli->lpStartupInfo->lpDesktop) },
  1469. { &(pSeclSli->ssTitle), /* Is mapped to ----> */ &(psli->lpStartupInfo->lpTitle) },
  1470. { &(pSeclSli->ssUsername), /* Is mapped to ----> */ &(psli->lpUsername) },
  1471. { &(pSeclSli->ssDomain), /* Is mapped to ----> */ &(psli->lpDomain) },
  1472. { &(pSeclSli->ssPassword), /* Is mapped to ----> */ &(psli->lpPassword) },
  1473. { &(pSeclSli->ssApplicationName), /* Is mapped to ----> */ &(psli->lpApplicationName) },
  1474. { &(pSeclSli->ssCommandLine), /* Is mapped to ----> */ &(psli->lpCommandLine) },
  1475. { &(pSeclSli->ssCurrentDirectory), /* Is mapped to ----> */ (LPWSTR *)&(psli->lpCurrentDirectory) }
  1476. };
  1477. for (dwIndex = 0; dwIndex < ARRAYSIZE(rg_StringsToMap); dwIndex++) {
  1478. dwResult = To_LPWSTR(rg_StringsToMap[dwIndex].pss, rg_StringsToMap[dwIndex].ppwsz);
  1479. _JumpCondition(ERROR_SUCCESS != dwResult, To_LPWSTR_Error);
  1480. }
  1481. }
  1482. if (pSeclSli->sbEnvironment.cb > 0) {
  1483. psli->lpEnvironment = HeapAlloc(hHeap, dwAllocFlags, pSeclSli->sbEnvironment.cb);
  1484. _JumpCondition(NULL == psli->lpEnvironment, MemoryError);
  1485. CopyMemory(psli->lpEnvironment, pSeclSli->sbEnvironment.pb, pSeclSli->sbEnvironment.cb);
  1486. }
  1487. } __except (EXCEPTION_EXECUTE_HANDLER) {
  1488. dwResult = GetExceptionCode();
  1489. goto ExceptionError;
  1490. }
  1491. psli->dwProcessId = pSeclSli->ulProcessId;
  1492. psli->LogonIdLowPart = pSeclSli->ulLogonIdLowPart;
  1493. psli->LogonIdHighPart = pSeclSli->lLogonIdHighPart;
  1494. psli->dwLogonFlags = pSeclSli->ulLogonFlags;
  1495. psli->dwCreationFlags = pSeclSli->ulCreationFlags;
  1496. psli->dwSeclogonFlags = pSeclSli->ulSeclogonFlags;
  1497. psli->hWinsta = (HANDLE)pSeclSli->hWinsta;
  1498. psli->hDesk = (HANDLE)pSeclSli->hDesk;
  1499. *ppsli = psli;
  1500. dwResult = ERROR_SUCCESS;
  1501. CommonReturn:
  1502. return dwResult;
  1503. ErrorReturn:
  1504. Free_SECONDARYLOGONINFOW(psli);
  1505. goto CommonReturn;
  1506. SET_DWRESULT(ExceptionError, dwResult);
  1507. SET_DWRESULT(GetProcessHeapError, GetLastError());
  1508. SET_DWRESULT(MemoryError, ERROR_NOT_ENOUGH_MEMORY);
  1509. SET_DWRESULT(To_LPWSTR_Error, dwResult);
  1510. }
  1511. //////////////////////////////// End Of File /////////////////////////////////