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.

727 lines
21 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Microsoft Windows
  4. // Copyright(C) 2001 - 2002 Microsoft Corporation
  5. //
  6. // File: auditing.cxx
  7. //
  8. //----------------------------------------------------------------------------
  9. #include "..\pch\headers.hxx"
  10. #include "authzi.h"
  11. #include "msaudite.h"
  12. #include "security.hxx"
  13. #include "auditing.hxx"
  14. AUTHZ_RESOURCE_MANAGER_HANDLE ghRM = 0;
  15. AUTHZ_AUDIT_EVENT_TYPE_HANDLE ghAuditEventType = 0;
  16. HRESULT
  17. AuditATJob(const AT_INFO &AtInfo, LPCWSTR pwszFileName)
  18. {
  19. HRESULT hr = S_OK;
  20. //
  21. // Impersonate the caller
  22. //
  23. DWORD RpcStatus = RpcImpersonateClient(NULL);
  24. if (RpcStatus != RPC_S_OK)
  25. {
  26. hr = _HRESULT_FROM_WIN32(RpcStatus);
  27. return hr;
  28. }
  29. //
  30. // Open the thread token
  31. //
  32. HANDLE hThreadToken = NULL;
  33. BOOL bTokenOpened = OpenThreadToken(GetCurrentThread(),
  34. TOKEN_QUERY, // Desired access.
  35. TRUE, // Open as self.
  36. &hThreadToken);
  37. //
  38. // End impersonation.
  39. //
  40. if ((RpcStatus = RpcRevertToSelf()) != RPC_S_OK)
  41. {
  42. ERR_OUT("RpcRevertToSelf", RpcStatus);
  43. schAssert(!"RpcRevertToSelf failed");
  44. hr = _HRESULT_FROM_WIN32(RpcStatus);
  45. }
  46. if (SUCCEEDED(hr))
  47. {
  48. if (bTokenOpened)
  49. {
  50. DWORD cbAccountSid = MAX_SID_SIZE;
  51. BYTE pbTaskSid[MAX_SID_SIZE];
  52. hr = GetNSAccountSid(pbTaskSid, cbAccountSid);
  53. if (SUCCEEDED(hr))
  54. {
  55. hr = AuditJob(hThreadToken, pbTaskSid, pwszFileName);
  56. }
  57. CloseHandle(hThreadToken);
  58. }
  59. else
  60. {
  61. hr = _HRESULT_FROM_WIN32(GetLastError());
  62. CHECK_HRESULT(hr);
  63. }
  64. }
  65. return hr;
  66. }
  67. HRESULT
  68. AuditJob(
  69. HANDLE hThreadToken,
  70. PSID pTaskSid,
  71. LPCWSTR pwszFileName
  72. )
  73. {
  74. //
  75. // Caller has already been impersonated and thread token opened prior to calling of this function
  76. //
  77. HRESULT hr = S_OK;
  78. //
  79. // Get the user SID
  80. //
  81. BYTE rgbTokenInformation[USER_TOKEN_STACK_BUFFER_SIZE];
  82. TOKEN_USER* pTokenUser = (TOKEN_USER*) rgbTokenInformation;
  83. DWORD cbReturnLength;
  84. if (GetTokenInformation(hThreadToken,
  85. TokenUser,
  86. pTokenUser,
  87. USER_TOKEN_STACK_BUFFER_SIZE,
  88. &cbReturnLength))
  89. {
  90. //
  91. // Get the Authentication ID
  92. //
  93. BYTE rgbTokenStatistics[sizeof(TOKEN_STATISTICS)];
  94. TOKEN_STATISTICS* pTokenStatistics = (TOKEN_STATISTICS*) rgbTokenStatistics;
  95. if (GetTokenInformation(hThreadToken,
  96. TokenStatistics,
  97. pTokenStatistics,
  98. sizeof(TOKEN_STATISTICS),
  99. &cbReturnLength))
  100. {
  101. DWORD dwRet = GenerateJobCreatedAudit(pTokenUser->User.Sid,
  102. pTaskSid,
  103. &(pTokenStatistics->AuthenticationId),
  104. pwszFileName);
  105. hr = _HRESULT_FROM_WIN32(dwRet);
  106. }
  107. else
  108. {
  109. hr = _HRESULT_FROM_WIN32(GetLastError());
  110. }
  111. }
  112. else
  113. {
  114. hr = _HRESULT_FROM_WIN32(GetLastError());
  115. }
  116. return hr;
  117. }
  118. HRESULT
  119. GetJobAuditInfo(
  120. LPCWSTR pwszFileName,
  121. DWORD* pdwFlags,
  122. LPWSTR* ppwszCommandLine,
  123. LPWSTR* ppwszTriggers,
  124. FILETIME* pftNextRun
  125. )
  126. {
  127. if (!pwszFileName || pwszFileName[0] == L'\0' || !pftNextRun || !ppwszTriggers)
  128. {
  129. CHECK_HRESULT(E_INVALIDARG);
  130. return(E_INVALIDARG);
  131. }
  132. //
  133. // Init
  134. //
  135. *pdwFlags = 0;
  136. *ppwszCommandLine = NULL;
  137. *ppwszTriggers = NULL;
  138. //
  139. // Instantiate the job to get its run properties.
  140. //
  141. CJob* pJob = CJob::Create();
  142. if (!pJob)
  143. {
  144. return E_OUTOFMEMORY;
  145. }
  146. HRESULT hr = pJob->LoadP(pwszFileName, 0, FALSE, TRUE);
  147. if (FAILED(hr))
  148. {
  149. schDebugOut((DEB_ERROR, "GetTriggerAuditInfo: pJob->LoadP failed with error 0x%x\n", hr));
  150. }
  151. else
  152. {
  153. //
  154. // Get the flags
  155. //
  156. pJob->GetAllFlags(pdwFlags); // this call returns void
  157. //
  158. // Get the application
  159. //
  160. WCHAR* pwszApplicationName = NULL;
  161. hr = pJob->GetApplicationName(&pwszApplicationName);
  162. if (FAILED(hr))
  163. {
  164. schDebugOut((DEB_ERROR, "GetTriggerAuditInfo: pJob->GetApplicationName failed with error 0x%x\n", hr));
  165. }
  166. else
  167. {
  168. //
  169. // Get the parameters
  170. //
  171. WCHAR* pwszParameters = NULL;
  172. hr = pJob->GetParameters(&pwszParameters);
  173. if (FAILED(hr))
  174. {
  175. schDebugOut((DEB_ERROR, "GetTriggerAuditInfo: pJob->GetParameters failed with error 0x%x\n", hr));
  176. }
  177. else
  178. {
  179. //
  180. // Produce command line by concatenating application name and parameters
  181. //
  182. size_t cchBuff = lstrlenW(pwszApplicationName) + 1 + lstrlenW(pwszParameters) + 1;
  183. WCHAR* pwszCommandLine = new WCHAR[cchBuff];
  184. if (!pwszCommandLine)
  185. {
  186. hr = E_OUTOFMEMORY;
  187. }
  188. else
  189. {
  190. StringCchCopy(pwszCommandLine, cchBuff, pwszApplicationName);
  191. StringCchCat(pwszCommandLine, cchBuff, L" ");
  192. StringCchCat(pwszCommandLine, cchBuff, pwszParameters);
  193. //
  194. // Return pointer to the command line string.
  195. // This string will need to be deleted by the caller.
  196. //
  197. *ppwszCommandLine = pwszCommandLine;
  198. //
  199. // Zero out next run time return value in case of any failures.
  200. // Get the next run time, convert it to a local file time then to UTC.
  201. //
  202. //pftNextRun->dwLowDateTime = 0;
  203. //pftNextRun->dwHighDateTime = 0;
  204. //
  205. // due to temporary issues, use a different value than 0
  206. // 1/1/1700 00:00:00,000
  207. pftNextRun->dwLowDateTime = 0xAEC64000;
  208. pftNextRun->dwHighDateTime = 0x006EFDDD;
  209. SYSTEMTIME stNextRun;
  210. hr = pJob->GetNextRunTime(&stNextRun);
  211. if (S_OK == hr)
  212. {
  213. FILETIME ftNextRunLocal;
  214. if (SystemTimeToFileTime(&stNextRun, &ftNextRunLocal))
  215. {
  216. LocalFileTimeToFileTime(&ftNextRunLocal, pftNextRun);
  217. }
  218. }
  219. //
  220. // Now get the triggers.
  221. //
  222. // Build up a string consisting of formatted string
  223. // representations of all the triggers for the task.
  224. //
  225. WORD cTriggers;
  226. hr = pJob->GetTriggerCount(&cTriggers);
  227. if (FAILED(hr))
  228. {
  229. schDebugOut((DEB_ERROR, "GetTriggerAuditInfo: pJob->GetTriggerCount failed with error 0x%x\n", hr));
  230. }
  231. else
  232. {
  233. //
  234. // Allocate initial buffer for concatenation of all trigger strings
  235. //
  236. const DWORD INITIAL_BUFFER_SIZE = 256; // start with enough space for 256 chars including null
  237. const DWORD BUFFER_SIZE_INCREMENT = 256; // if necessary, grow buffer by this amount
  238. DWORD dwSize = INITIAL_BUFFER_SIZE;
  239. WCHAR* pwszTriggers = new WCHAR[dwSize]; // this will need to be deleted by the caller
  240. if (!pwszTriggers)
  241. {
  242. hr = E_OUTOFMEMORY;
  243. }
  244. else
  245. {
  246. pwszTriggers[0] = L'\0';
  247. DWORD dwSizeRequired;
  248. WCHAR* pwszTemp;
  249. WCHAR* pwszTriggerString;
  250. for (short nTrigger = 0; nTrigger < cTriggers && SUCCEEDED(hr); nTrigger++)
  251. {
  252. hr = pJob->GetTriggerString(nTrigger, &pwszTriggerString);
  253. if (FAILED(hr))
  254. {
  255. schDebugOut((DEB_ERROR, "GetTriggerAuditInfo: pJob->GetTriggerString failed with error 0x%x\n", hr));
  256. }
  257. else
  258. {
  259. //
  260. // If not big enough, create larger buffer, copy, and delete old
  261. //
  262. dwSizeRequired = lstrlenW(pwszTriggers)
  263. + 2 // two spaces
  264. + lstrlenW(pwszTriggerString)
  265. + 2; // period plus null terminator
  266. if (dwSizeRequired > dwSize)
  267. {
  268. dwSize += BUFFER_SIZE_INCREMENT;
  269. if (dwSizeRequired > dwSize) // if still not big enough (very unlikely),
  270. dwSize = dwSizeRequired; // make it big enough
  271. pwszTemp = new WCHAR[dwSize];
  272. if (!pwszTemp)
  273. {
  274. hr = E_OUTOFMEMORY;
  275. }
  276. else
  277. {
  278. StringCchCopy(pwszTemp, dwSize, pwszTriggers);
  279. delete [] pwszTriggers;
  280. pwszTriggers = pwszTemp;
  281. }
  282. }
  283. if (SUCCEEDED(hr))
  284. {
  285. if (lstrlenW(pwszTriggers) > 0)
  286. StringCchCat(pwszTriggers, dwSize, L" ");
  287. StringCchCat(pwszTriggers, dwSize, pwszTriggerString);
  288. StringCchCat(pwszTriggers, dwSize, L".");
  289. }
  290. CoTaskMemFree(pwszTriggerString);
  291. }
  292. }
  293. if (SUCCEEDED(hr))
  294. {
  295. //
  296. // If all was a success, return the pointer to the string of triggers.
  297. // This string will need to be deleted by the caller.
  298. //
  299. *ppwszTriggers = pwszTriggers;
  300. }
  301. else
  302. {
  303. //
  304. // If something went wrong, let's go ahead and clean up now.
  305. //
  306. if (pwszTriggers)
  307. delete [] pwszTriggers;
  308. }
  309. }
  310. }
  311. }
  312. CoTaskMemFree(pwszParameters);
  313. }
  314. CoTaskMemFree(pwszApplicationName);
  315. }
  316. }
  317. if (pJob)
  318. {
  319. pJob->Release();
  320. }
  321. return hr;
  322. }
  323. /***************************************************************************\
  324. * FUNCTION: StartupAuditing
  325. *
  326. * PURPOSE: Creates the resource manager and the audit event type handles.
  327. *
  328. * PARAMETERS:
  329. *
  330. * RETURNS: win32 error code
  331. *
  332. * History:
  333. * 12-05-2001 maxa Created
  334. *
  335. \***************************************************************************/
  336. DWORD
  337. StartupAuditing(
  338. )
  339. {
  340. DWORD dwError = ERROR_SUCCESS;
  341. BOOL fResult = FALSE;
  342. BOOL fWasEnabled = TRUE;
  343. if (ghRM && ghAuditEventType)
  344. {
  345. goto Cleanup;
  346. }
  347. dwError = EnableNamedPrivilege(
  348. L"SeAuditPrivilege",
  349. TRUE,
  350. &fWasEnabled);
  351. if (dwError != ERROR_SUCCESS)
  352. {
  353. goto Cleanup;
  354. }
  355. fResult = AuthzInitializeResourceManager(
  356. 0, // no special flags
  357. NULL, // PFN_AUTHZ_DYNAMIC_ACCESS_CHECK
  358. NULL, // PFN_AUTHZ_COMPUTE_DYNAMIC_GROUPS
  359. NULL, // PFN_AUTHZ_FREE_DYNAMIC_GROUPS
  360. L"Scheduler", // RM name
  361. &ghRM
  362. );
  363. if (!fResult)
  364. {
  365. dwError = GetLastError();
  366. goto Cleanup;
  367. }
  368. fResult = AuthziInitializeAuditEventType(
  369. 0,
  370. SE_CATEGID_DETAILED_TRACKING,
  371. SE_AUDITID_JOB_CREATED,
  372. 7,
  373. &ghAuditEventType
  374. );
  375. if (!fResult)
  376. {
  377. dwError = GetLastError();
  378. goto Cleanup;
  379. }
  380. Cleanup:
  381. if (!fWasEnabled)
  382. {
  383. EnableNamedPrivilege(
  384. L"SeAuditPrivilege",
  385. FALSE,
  386. &fWasEnabled);
  387. }
  388. if (dwError != ERROR_SUCCESS)
  389. {
  390. ShutdownAuditing();
  391. }
  392. return dwError;
  393. }
  394. /***************************************************************************\
  395. * FUNCTION: ShutdownAuditing
  396. *
  397. * PURPOSE: Deletes the resource manager and the audit event type handles.
  398. *
  399. * PARAMETERS:
  400. *
  401. * RETURNS:
  402. *
  403. * History:
  404. * 12-05-2001 maxa Created
  405. *
  406. \***************************************************************************/
  407. VOID
  408. ShutdownAuditing(
  409. )
  410. {
  411. if (ghAuditEventType)
  412. {
  413. AuthziFreeAuditEventType(ghAuditEventType);
  414. ghAuditEventType = 0;
  415. }
  416. if (ghRM)
  417. {
  418. AuthzFreeResourceManager(ghRM);
  419. ghRM = 0;
  420. }
  421. }
  422. /***************************************************************************\
  423. * FUNCTION: GenerateJobCreatedAudit
  424. *
  425. * PURPOSE: Generates an audit-event indicating that a job has
  426. * been created.
  427. *
  428. * PARAMETERS: pUserSid:
  429. * SID of the account that created the job.
  430. * pTaskSid:
  431. * SID of the account the job is to run as.
  432. * pLogonId:
  433. * LogonId of the account that created the job.
  434. * pszFileName:
  435. * File name of the newly created job in the Tasks folder.
  436. *
  437. * RETURNS: win32 error code
  438. *
  439. * History:
  440. * 10-01-2001 maxa Created
  441. * 11-07-2001 shbrown Updated for use with tasks rather than AT jobs
  442. *
  443. \***************************************************************************/
  444. DWORD
  445. GenerateJobCreatedAudit(
  446. IN PSID pUserSid,
  447. IN PSID pTaskSid,
  448. IN PLUID pLogonId,
  449. IN PCWSTR pwszFileName
  450. )
  451. {
  452. DWORD dwError = ERROR_SUCCESS;
  453. BOOL fResult = FALSE;
  454. AUTHZ_AUDIT_EVENT_HANDLE hAuditEvent = NULL;
  455. AUDIT_PARAMS AuditParams = {0};
  456. AUDIT_PARAM ParamArray[10] = {APT_None};
  457. PSID pDummySid = NULL;
  458. ASSERT(pUserSid);
  459. ASSERT(pTaskSid);
  460. ASSERT(pLogonId);
  461. ASSERT(pwszFileName && pwszFileName[0]);
  462. ASSERT(ghRM);
  463. ASSERT(ghAuditEventType);
  464. //
  465. // Get the job audit info
  466. //
  467. DWORD dwFlags;
  468. LPWSTR pwszCommandLine = NULL; // this will need to be deleted after use
  469. LPWSTR pwszTriggers = NULL; // this will need to be deleted after use
  470. FILETIME ftNextRun;
  471. HRESULT hr = GetJobAuditInfo(pwszFileName, &dwFlags, &pwszCommandLine, &pwszTriggers, &ftNextRun);
  472. if (FAILED(hr))
  473. {
  474. goto Cleanup;
  475. }
  476. AuditParams.Parameters = ParamArray;
  477. fResult = AuthziInitializeAuditParams(
  478. APF_AuditSuccess,
  479. &AuditParams,
  480. &pDummySid,
  481. L"Security",
  482. 7,
  483. APT_String | AP_Filespec, pwszFileName,
  484. APT_String, pwszCommandLine,
  485. APT_String, pwszTriggers,
  486. APT_Time, ftNextRun,
  487. APT_Ulong | AP_FormatHex, dwFlags,
  488. APT_Sid, pTaskSid,
  489. APT_LogonId, *pLogonId
  490. );
  491. if (!fResult)
  492. {
  493. goto Error;
  494. }
  495. //
  496. // this is ugly, but currently there is no other way
  497. // do we still need this?
  498. //
  499. ParamArray[0].Data0 = (ULONG_PTR)pUserSid;
  500. fResult = AuthziInitializeAuditEvent(
  501. 0, // flags
  502. ghRM, // resource manager
  503. ghAuditEventType,
  504. &AuditParams,
  505. NULL, // hAuditQueue
  506. INFINITE, // time out
  507. L"", L"", L"", L"", // obj access strings
  508. &hAuditEvent
  509. );
  510. if (!fResult)
  511. {
  512. goto Error;
  513. }
  514. fResult = AuthziLogAuditEvent(
  515. 0, // flags
  516. hAuditEvent,
  517. NULL // reserved
  518. );
  519. if (!fResult)
  520. {
  521. goto Error;
  522. }
  523. Cleanup:
  524. if (pwszCommandLine)
  525. {
  526. delete [] pwszCommandLine;
  527. }
  528. if (pwszTriggers)
  529. {
  530. delete [] pwszTriggers;
  531. }
  532. if (hAuditEvent)
  533. {
  534. AuthzFreeAuditEvent(hAuditEvent);
  535. }
  536. if (pDummySid)
  537. {
  538. LocalFree(pDummySid);
  539. }
  540. return dwError;
  541. Error:
  542. dwError = GetLastError();
  543. goto Cleanup;
  544. }
  545. /***************************************************************************\
  546. * FUNCTION: EnableNamedPrivilege
  547. *
  548. * PURPOSE: Enable or disable a privilege by its name.
  549. *
  550. * PARAMETERS: pszPrivName:
  551. * Name of privilege to enable.
  552. * fEnable:
  553. * Enable/Disable flag.
  554. * pfWasEnabled:
  555. * Optional pointer to flag to receive the previous state.
  556. *
  557. * RETURNS: win32 error code
  558. *
  559. * History:
  560. * 12-05-2001 maxa Created
  561. *
  562. \***************************************************************************/
  563. DWORD
  564. EnableNamedPrivilege(
  565. IN PCWSTR pszPrivName,
  566. IN BOOL fEnable,
  567. OUT PBOOL pfWasEnabled OPTIONAL
  568. )
  569. {
  570. DWORD dwError = ERROR_SUCCESS;
  571. BOOL fResult;
  572. HANDLE hToken = 0;
  573. DWORD dwSize = 0;
  574. TOKEN_PRIVILEGES newPriv;
  575. TOKEN_PRIVILEGES oldPriv;
  576. fResult = OpenProcessToken(
  577. GetCurrentProcess(),
  578. TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
  579. &hToken);
  580. if (!fResult)
  581. {
  582. dwError = GetLastError();
  583. goto Cleanup;
  584. }
  585. fResult = LookupPrivilegeValue(
  586. 0,
  587. pszPrivName,
  588. &newPriv.Privileges[0].Luid);
  589. if (!fResult)
  590. {
  591. dwError = GetLastError();
  592. goto Cleanup;
  593. }
  594. newPriv.Privileges[0].Attributes = fEnable ? SE_PRIVILEGE_ENABLED : 0;
  595. newPriv.PrivilegeCount = 1;
  596. fResult = AdjustTokenPrivileges(
  597. hToken,
  598. FALSE,
  599. &newPriv,
  600. sizeof oldPriv,
  601. &oldPriv,
  602. &dwSize);
  603. if (!fResult)
  604. {
  605. dwError = GetLastError();
  606. goto Cleanup;
  607. }
  608. if (pfWasEnabled)
  609. {
  610. if (oldPriv.PrivilegeCount == 0)
  611. {
  612. *pfWasEnabled = fEnable;
  613. }
  614. else
  615. {
  616. *pfWasEnabled =
  617. (oldPriv.Privileges[0].Attributes & SE_PRIVILEGE_ENABLED) ?
  618. TRUE : FALSE;
  619. }
  620. }
  621. Cleanup:
  622. if (hToken)
  623. {
  624. CloseHandle(hToken);
  625. }
  626. return dwError;
  627. }