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.

1205 lines
32 KiB

  1. //+---------------------------------------------------------------------------
  2. //
  3. // Scheduling Agent Service
  4. //
  5. // Microsoft Windows
  6. // Copyright (C) Microsoft Corporation, 1992 - 1996.
  7. //
  8. // File: netsch.cxx
  9. //
  10. // Contents: Server-side Net Scheduler RPC implementation.
  11. //
  12. // Classes: None.
  13. //
  14. // RPC: NetrJobAdd
  15. // NetrJobDel
  16. // NetrJobEnum
  17. // NetrJobGetInfo
  18. //
  19. // Functions: CreateAtJobPath
  20. // GetAtJobIdFromFileName
  21. // InitializeNetScheduleApi
  22. // UninitializeNetScheduleApi
  23. //
  24. // History: 11-Nov-95 MarkBl Created.
  25. // 02-Feb-01 JBenton Fixed BUG 303146 - 64bit pointer alignment problem
  26. //
  27. //----------------------------------------------------------------------------
  28. #include "..\pch\headers.hxx"
  29. #pragma hdrstop
  30. #include "debug.hxx"
  31. #include <align.h>
  32. #include <apperr.h>
  33. #include <lmerr.h>
  34. #include <netevent.h>
  35. extern "C" {
  36. #include <netlib.h>
  37. }
  38. #include "atsvc.h"
  39. #include "..\inc\resource.h"
  40. #include "globals.hxx"
  41. #include "svc_core.hxx"
  42. #include "atsec.hxx"
  43. #include "proto.hxx"
  44. //
  45. // Manifests below taken from the existing At service. Values must *not*
  46. // change to maintain compatibility.
  47. //
  48. #define MAXIMUM_COMMAND_LENGTH (MAX_PATH - 1)
  49. #define MAXIMUM_JOB_TIME (24 * 60 * 60 * 1000 - 1)
  50. #define DAYS_OF_WEEK 0x7F // 7 bits for 7 days.
  51. #define DAYS_OF_MONTH 0x7FFFFFFF // 31 bits for 31 days.
  52. // This is not localized - it is a registry key (indirectly) from the At service
  53. #define SCHEDULE_EVENTLOG_NAME TEXT("Schedule")
  54. //
  55. // Converts an HRESULT to a WIN32 status code. Masks off everything but
  56. // the error code.
  57. //
  58. // BUGBUG : Review.
  59. //
  60. #define WIN32_FROM_HRESULT(x) (HRESULT_CODE(x))
  61. //
  62. // Minimum and maximum buffer size returned in an enumeration.
  63. //
  64. // 02/05/01-jbenton : this macro is used to a unicode buffer so must be even
  65. // to avoid alignment problems (bug 303146).
  66. #define BUFFER_LENGTH_MINIMUM (sizeof(AT_ENUM) + (MAXIMUM_COMMAND_LENGTH+1)*sizeof(WCHAR))
  67. #define BUFFER_LENGTH_MAXIMUM 65536
  68. //
  69. // Ballpark maximum command string length.
  70. //
  71. // BUGBUG : Review this value.
  72. //
  73. #define COMMAND_STRING_LENGTH_APPROX (((MAX_PATH / 4) + 1) * sizeof(WCHAR))
  74. #define ASTERISK_STR L"*"
  75. #define BACKSLASH_STR L"\\"
  76. void CreateAtJobPath(DWORD, WCHAR *, size_t);
  77. DWORD GetAtJobIdFromFileName(WCHAR *);
  78. void GetNextAtID(LPDWORD);
  79. WCHAR * gpwszAtJobPathTemplate = NULL;
  80. //+---------------------------------------------------------------------------
  81. //
  82. // RPC: NetrJobAdd
  83. //
  84. // Synopsis: Add a single At job.
  85. //
  86. // Arguments: [ServerName] -- Unused.
  87. // [pAtInfo] -- New job information.
  88. // [pJobId] -- Returned job id.
  89. //
  90. // Returns: BUGBUG : Problem mapping a HRESULT to WIN32. Masking off the
  91. // facility & error bits is insufficient.
  92. //
  93. // Notes: None.
  94. //
  95. //----------------------------------------------------------------------------
  96. NET_API_STATUS
  97. NetrJobAdd(ATSVC_HANDLE ServerName, LPAT_INFO pAtInfo, LPDWORD pJobId)
  98. {
  99. schDebugOut((DEB_ITRACE,
  100. "NetrJobAdd ServerName(%ws), pAtInfo(0x%x)\n",
  101. (ServerName != NULL) ? ServerName : L"(local)",
  102. pAtInfo));
  103. UNREFERENCED_PARAMETER(ServerName);
  104. NET_API_STATUS Status = NERR_Success;
  105. Status = AtCheckSecurity(AT_JOB_ADD);
  106. if (Status != NERR_Success)
  107. {
  108. return ERROR_ACCESS_DENIED;
  109. }
  110. //
  111. // Validate arguments.
  112. //
  113. if ( (pAtInfo->Command == NULL) ||
  114. (wcslen(pAtInfo->Command) > MAXIMUM_COMMAND_LENGTH) ||
  115. (pAtInfo->JobTime > MAXIMUM_JOB_TIME) ||
  116. (pAtInfo->DaysOfWeek & ~DAYS_OF_WEEK) ||
  117. (pAtInfo->DaysOfMonth & ~DAYS_OF_MONTH) ||
  118. (pAtInfo->Flags & ~JOB_INPUT_FLAGS))
  119. {
  120. return(ERROR_INVALID_PARAMETER);
  121. }
  122. //
  123. // TBD : Logic to punt the submission if the service is paused.
  124. //
  125. EnterCriticalSection(&gcsNetScheduleCritSection);
  126. //
  127. // Have the global schedule instance add the At job.
  128. //
  129. HRESULT hr = g_pSched->m_pSch->AddAtJobWithHash(*pAtInfo, pJobId);
  130. if (FAILED(hr))
  131. {
  132. //
  133. // Convert the HRESULT to a WIN32 status code.
  134. //
  135. Status = WIN32_FROM_HRESULT(hr);
  136. }
  137. LeaveCriticalSection(&gcsNetScheduleCritSection);
  138. return(Status);
  139. }
  140. //+---------------------------------------------------------------------------
  141. //
  142. // RPC: NetrJobDel
  143. //
  144. // Synopsis: Delete the At jobs in the range specified.
  145. //
  146. // Arguments: [ServerName] -- Unused.
  147. // [MinJobId] -- Range lower bound, inclusive.
  148. // [MaxJobId] -- Range upper bound, inclusive.
  149. //
  150. // Returns: NERR_Sucess
  151. // ERROR_INVALID_PARAMETER
  152. // APE_AT_ID_NOT_FOUND
  153. //
  154. // Notes: None.
  155. //
  156. //----------------------------------------------------------------------------
  157. NET_API_STATUS
  158. NetrJobDel(ATSVC_HANDLE ServerName, DWORD MinJobId, DWORD MaxJobId)
  159. {
  160. schDebugOut((DEB_ITRACE,
  161. "NetrJobDel ServerName(%ws), MinJobId(%d), MaxJobId(%d)\n",
  162. (ServerName != NULL) ? ServerName : L"(local)",
  163. MinJobId,
  164. MaxJobId));
  165. UNREFERENCED_PARAMETER(ServerName);
  166. NET_API_STATUS Status;
  167. Status = AtCheckSecurity(AT_JOB_DEL);
  168. if (Status != NERR_Success)
  169. {
  170. return ERROR_ACCESS_DENIED;
  171. }
  172. //
  173. // Validate range.
  174. //
  175. if (MinJobId > MaxJobId)
  176. {
  177. return(ERROR_INVALID_PARAMETER);
  178. }
  179. EnterCriticalSection(&gcsNetScheduleCritSection);
  180. //
  181. // Delete the indicated At job objects from storage.
  182. //
  183. // NB : To maintain compatibility with the existing AT service, if at
  184. // least one job is deleted successfully, return success; otherwise,
  185. // return APE_ID_NOT_FOUND.
  186. //
  187. WCHAR wszPath[MAX_PATH + 1];
  188. BOOL fJobDeleted = FALSE;
  189. HRESULT hr;
  190. BOOL fDeleteAll = FALSE;
  191. //
  192. // Test for delete-all; signaled by passing a MaxJobId of 0xffffffff.
  193. //
  194. if (MaxJobId == 0xffffffff)
  195. {
  196. //
  197. // Get the actual maximum ID value (this fixes bug 55839).
  198. //
  199. GetNextAtID(&MaxJobId);
  200. fDeleteAll = TRUE;
  201. }
  202. CJob * pJob = CJob::Create();
  203. if (pJob)
  204. {
  205. for (DWORD i = MinJobId; i <= MaxJobId; i++)
  206. {
  207. CreateAtJobPath(i, wszPath, MAX_PATH + 1);
  208. //
  209. // Make sure this is really an AT job, and not one that's just
  210. // named like one. Just load the fixed-length data and check for
  211. // the at flag.
  212. //
  213. hr = pJob->LoadP(wszPath, 0, FALSE, FALSE);
  214. if (SUCCEEDED(hr))
  215. {
  216. DWORD rgFlags;
  217. pJob->GetAllFlags(&rgFlags);
  218. if (rgFlags & JOB_I_FLAG_NET_SCHEDULE)
  219. {
  220. if (DeleteFile(wszPath))
  221. {
  222. fJobDeleted = TRUE;
  223. }
  224. }
  225. }
  226. else
  227. {
  228. schDebugOut((DEB_IWARN, "LoadP(%S) hr=0x%x\n", wszPath, hr));
  229. }
  230. }
  231. pJob->Release();
  232. }
  233. //
  234. // If the user asked to delete all at jobs, reset the next id to 1
  235. //
  236. if (fDeleteAll)
  237. {
  238. (void) g_pSched->m_pSch->ResetAtID();
  239. }
  240. LeaveCriticalSection(&gcsNetScheduleCritSection);
  241. Status = fJobDeleted ? NERR_Success : APE_AT_ID_NOT_FOUND;
  242. return(Status);
  243. }
  244. //+---------------------------------------------------------------------------
  245. //
  246. // RPC: NetrJobEnum
  247. //
  248. // Synopsis: Enumerate At jobs.
  249. //
  250. // Arguments: [ServerName] -- Unused.
  251. // [pEnumContainer] -- Returned enumeration (AT_JOB_INFO
  252. // array and size).
  253. // [PreferredMaximumLength] -- Preferred buffer size maximum. If
  254. // -1, allocate as needed.
  255. // [pTotalEntries] -- Returns the total number of
  256. // entries available.
  257. // [pResumeHandle] -- Enumeration context. Indexes the
  258. // the At jobs directory.
  259. //
  260. // Returns: BUGBUG : Problem here too with HRESULTs mapped to WIN32 status
  261. // codes.
  262. //
  263. // Notes: None.
  264. //
  265. //----------------------------------------------------------------------------
  266. NET_API_STATUS
  267. NetrJobEnum(
  268. ATSVC_HANDLE ServerName,
  269. LPAT_ENUM_CONTAINER pEnumContainer,
  270. DWORD PreferredMaximumLength,
  271. LPDWORD pTotalEntries,
  272. LPDWORD pResumeHandle)
  273. {
  274. schDebugOut((DEB_ITRACE,
  275. "NetrJobEnum ServerName(%ws), pEnumContainer(0x%x), " \
  276. "PreferredMaximumLength(%d)\n",
  277. (ServerName != NULL) ? ServerName : L"(local)",
  278. pEnumContainer,
  279. PreferredMaximumLength));
  280. UNREFERENCED_PARAMETER(ServerName);
  281. WCHAR wszCommand[MAX_PATH + 1];
  282. WIN32_FIND_DATA fd;
  283. NET_API_STATUS Status;
  284. HANDLE hFileFindContext;
  285. LPBYTE pbBuffer;
  286. LPBYTE pbStringsOffset;
  287. PAT_ENUM pAtEnum;
  288. DWORD cbBufferSize;
  289. DWORD cbCommandSize;
  290. DWORD cJobsEnumerated;
  291. DWORD iEnumContext;
  292. DWORD i;
  293. DWORD rgFlags;
  294. HRESULT hr;
  295. Status = NERR_Success;
  296. pbBuffer = NULL;
  297. cJobsEnumerated = 0;
  298. i = 0;
  299. //
  300. // pEnumContainer is defined in the IDL file as [in,out] though it
  301. // should only be [out]. This can't be changed in the IDL file for
  302. // backwards compatibility, so check it here. Without this check,
  303. // we'll leak memory if the user gives a non-NULL buffer.
  304. //
  305. if (pEnumContainer->Buffer != NULL)
  306. {
  307. return ERROR_INVALID_PARAMETER;
  308. }
  309. Status = AtCheckSecurity(AT_JOB_ENUM);
  310. if (Status != NERR_Success)
  311. {
  312. return ERROR_ACCESS_DENIED;
  313. }
  314. if (pResumeHandle != NULL)
  315. {
  316. iEnumContext = *pResumeHandle;
  317. }
  318. else
  319. {
  320. iEnumContext = 0;
  321. }
  322. //
  323. // Allocate one job object that will be reused.
  324. //
  325. CJob * pJob = CJob::Create();
  326. if (pJob == NULL)
  327. {
  328. return ERROR_NOT_ENOUGH_MEMORY;
  329. }
  330. EnterCriticalSection(&gcsNetScheduleCritSection);
  331. //
  332. // Compute the total number of At jobs (i.e., the number of At jobs in
  333. // the At subdirectory). This number is used to update the pTotalEntries
  334. // argument, and may be used for enumeration buffer size computation.
  335. //
  336. DWORD cAtJobTotal = 0;
  337. hFileFindContext = FindFirstFile(g_wszAtJobSearchPath, &fd);
  338. if (hFileFindContext == INVALID_HANDLE_VALUE)
  339. {
  340. //
  341. // Nothing to enumerate.
  342. //
  343. *pTotalEntries = 0;
  344. goto EnumExit;
  345. }
  346. do
  347. {
  348. //
  349. // If somebody renamed an At job, don't enumerate it. This is to
  350. // prevent us from returning duplicate IDs as a result of finding jobs
  351. // like At1.job and At01.job.
  352. //
  353. if (!IsValidAtFilename(fd.cFileName))
  354. {
  355. continue;
  356. }
  357. hr = LoadAtJob(pJob, fd.cFileName);
  358. if (FAILED(hr))
  359. {
  360. // we don't want to return the job,
  361. // but failing altogether is a little drastic
  362. // ERR_OUT("NetrJobEnum: pJob->Load", hr);
  363. // Status = WIN32_FROM_HRESULT(hr);
  364. // FindClose(hFileFindContext);
  365. // goto EnumExit;
  366. continue; // skip it and go on
  367. }
  368. pJob->GetAllFlags(&rgFlags);
  369. if (rgFlags & JOB_I_FLAG_NET_SCHEDULE)
  370. {
  371. cAtJobTotal++;
  372. }
  373. } while (FindNextFile(hFileFindContext, &fd));
  374. FindClose(hFileFindContext);
  375. if (!cAtJobTotal)
  376. {
  377. //
  378. // Nothing to enumerate.
  379. //
  380. *pTotalEntries = 0;
  381. goto EnumExit;
  382. }
  383. //
  384. // Get buffer size.
  385. //
  386. if (PreferredMaximumLength != -1)
  387. {
  388. //
  389. // Caller has specified a preferred buffer size.
  390. //
  391. // 02/05/01-jbenton : buffer size must be even to avoid
  392. // alignment errors. (bug 303146).
  393. cbBufferSize = ROUND_DOWN_COUNT(PreferredMaximumLength, ALIGN_WCHAR);
  394. }
  395. else
  396. {
  397. //
  398. // Compute a "best-guess" buffer size to return all of the data.
  399. // If we underestimate the buffer size, we'll return as much data
  400. // as the buffer allows, plus a return code of ERROR_MORE_DATA.
  401. //
  402. cbBufferSize = (sizeof(AT_ENUM) + COMMAND_STRING_LENGTH_APPROX) *
  403. cAtJobTotal;
  404. }
  405. //
  406. // Restrict buffer size.
  407. //
  408. cbBufferSize = (DWORD)max(cbBufferSize, BUFFER_LENGTH_MINIMUM);
  409. cbBufferSize = min(cbBufferSize, BUFFER_LENGTH_MAXIMUM);
  410. //
  411. // The enumeration context is utilized as an index in the find first/next
  412. // file result. If non-zero, enumerate the directory until the number
  413. // of AT jobs enumerated equals the caller's enumeration context.
  414. //
  415. // BUGBUG : This is quite a departure from the existing At service, but
  416. // I'm confident it should not present a problem. Note for
  417. // review.
  418. //
  419. // Seek to the enumeration context index.
  420. //
  421. hFileFindContext = FindFirstFile(g_wszAtJobSearchPath, &fd);
  422. if (hFileFindContext == INVALID_HANDLE_VALUE)
  423. {
  424. //
  425. // Nothing to enumerate.
  426. //
  427. *pTotalEntries = 0;
  428. goto EnumExit;
  429. }
  430. i = 0;
  431. do
  432. {
  433. if (!IsValidAtFilename(fd.cFileName))
  434. {
  435. continue;
  436. }
  437. hr = LoadAtJob(pJob, fd.cFileName);
  438. if (FAILED(hr))
  439. {
  440. // we don't want to return the job,
  441. // but failing altogether is a little drastic
  442. // ERR_OUT("NetrJobEnum: pJob->Load", hr);
  443. // Status = WIN32_FROM_HRESULT(hr);
  444. // FindClose(hFileFindContext);
  445. // goto EnumExit;
  446. continue; // skip it and go on
  447. }
  448. pJob->GetAllFlags(&rgFlags);
  449. if (rgFlags & JOB_I_FLAG_NET_SCHEDULE)
  450. {
  451. i++;
  452. if (i > iEnumContext)
  453. {
  454. break;
  455. }
  456. }
  457. } while (FindNextFile(hFileFindContext, &fd));
  458. if (i <= iEnumContext)
  459. {
  460. //
  461. // The above enumeration seek failed to find any more AT jobs
  462. // beyond the Resume handle count. Thus, the enumeration is
  463. // complete. Nothing else to enumerate.
  464. //
  465. FindClose(hFileFindContext);
  466. *pTotalEntries = 0;
  467. goto EnumExit;
  468. }
  469. //
  470. // Update pTotalEntries argument. It is the difference between the total
  471. // number of jobs and the number of jobs previously enumerated.
  472. //
  473. *pTotalEntries = cAtJobTotal - i + 1;
  474. pbBuffer = (LPBYTE)MIDL_user_allocate(cbBufferSize);
  475. if (pbBuffer == NULL)
  476. {
  477. Status = ERROR_NOT_ENOUGH_MEMORY;
  478. CHECK_HRESULT(HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY));
  479. goto EnumExit;
  480. }
  481. //
  482. // Begin the enumeration.
  483. //
  484. pbStringsOffset = pbBuffer + cbBufferSize;
  485. pAtEnum = (PAT_ENUM)pbBuffer;
  486. //
  487. // To have arrived here, the resume handle seek above will have left us
  488. // a valid AT job object in pJob and the corresponding rgFlags.
  489. //
  490. do
  491. {
  492. if (rgFlags & JOB_I_FLAG_NET_SCHEDULE)
  493. {
  494. if (pbStringsOffset <= (LPBYTE)pAtEnum + sizeof(AT_ENUM))
  495. {
  496. //
  497. // Buffer full.
  498. //
  499. Status = ERROR_MORE_DATA;
  500. break;
  501. }
  502. //
  503. // Get At job information.
  504. //
  505. DWORD CommandSize = MAX_PATH + 1;
  506. AT_INFO AtInfo;
  507. hr = pJob->GetAtInfo(&AtInfo, wszCommand, &CommandSize);
  508. if (SUCCEEDED(hr))
  509. {
  510. //
  511. // Copy fixed portion.
  512. //
  513. pAtEnum->JobId = GetAtJobIdFromFileName(fd.cFileName);
  514. pAtEnum->JobTime = AtInfo.JobTime;
  515. pAtEnum->DaysOfMonth = AtInfo.DaysOfMonth;
  516. pAtEnum->DaysOfWeek = AtInfo.DaysOfWeek;
  517. pAtEnum->Flags = AtInfo.Flags;
  518. //
  519. // Copy variable data.
  520. //
  521. // whack one off of CommandSize
  522. // because NetpCopyStringToBuffer doesn't want the NULL counted
  523. BOOL fRet = NetpCopyStringToBuffer(
  524. wszCommand,
  525. CommandSize -1,
  526. (LPBYTE)(pAtEnum + 1),
  527. (LPWSTR *)&pbStringsOffset,
  528. &pAtEnum->Command);
  529. if (!fRet)
  530. {
  531. Status = ERROR_MORE_DATA;
  532. break;
  533. }
  534. pAtEnum++; cJobsEnumerated++; iEnumContext++;
  535. }
  536. }
  537. //
  538. // Get the next filename, skipping any that have been renamed
  539. //
  540. BOOL fFoundAnotherAtJob = FALSE;
  541. do
  542. {
  543. while (fFoundAnotherAtJob = FindNextFile(hFileFindContext, &fd))
  544. {
  545. if (IsValidAtFilename(fd.cFileName))
  546. {
  547. break;
  548. }
  549. }
  550. if (!fFoundAnotherAtJob)
  551. {
  552. //
  553. // No more files.
  554. //
  555. break;
  556. }
  557. hr = LoadAtJob(pJob, fd.cFileName);
  558. }
  559. // we wish to continue doing this until we find a good one
  560. while (FAILED(hr));
  561. // but if we broke out without finding a good job above, we're done
  562. if (!fFoundAnotherAtJob)
  563. break;
  564. pJob->GetAllFlags(&rgFlags);
  565. } while (TRUE);
  566. FindClose(hFileFindContext);
  567. //
  568. // Reset enumeration context if everything has been read.
  569. //
  570. if (Status == NERR_Success)
  571. {
  572. iEnumContext = 0;
  573. }
  574. EnumExit:
  575. LeaveCriticalSection(&gcsNetScheduleCritSection);
  576. if (pJob)
  577. {
  578. pJob->Release();
  579. }
  580. pEnumContainer->EntriesRead = cJobsEnumerated;
  581. if (cJobsEnumerated == 0 && pbBuffer != NULL)
  582. {
  583. MIDL_user_free(pbBuffer);
  584. pbBuffer = NULL;
  585. }
  586. pEnumContainer->Buffer = (LPAT_ENUM)pbBuffer;
  587. if (pResumeHandle != NULL)
  588. {
  589. *pResumeHandle = iEnumContext;
  590. }
  591. return(Status);
  592. }
  593. //+---------------------------------------------------------------------------
  594. //
  595. // RPC: NetrJobGetInfo
  596. //
  597. // Synopsis: Get information on an At job.
  598. //
  599. // Arguments: [ServerName] -- Unused.
  600. // [JobId] -- Target At job.
  601. // [ppAtInfo] -- Returned information.
  602. //
  603. // Returns: BUGBUG : Problem here too with HRESULTs mapped to WIN32 status
  604. // codes.
  605. //
  606. // Notes: None.
  607. //
  608. //----------------------------------------------------------------------------
  609. NET_API_STATUS
  610. NetrJobGetInfo(ATSVC_HANDLE ServerName, DWORD JobId, LPAT_INFO * ppAtInfo)
  611. {
  612. schDebugOut((DEB_ITRACE,
  613. "NetrJobGetInfo ServerName(%ws), JobId(%d)\n",
  614. (ServerName != NULL) ? ServerName : L"(local)",
  615. JobId));
  616. UNREFERENCED_PARAMETER(ServerName);
  617. AT_INFO AtInfo;
  618. PAT_INFO pAtInfo;
  619. NET_API_STATUS Status;
  620. WCHAR wszPath[MAX_PATH + 1];
  621. WCHAR wszCommand[MAX_PATH + 1];
  622. WCHAR wszJobId[10 + 1];
  623. DWORD CommandSize;
  624. HRESULT hr;
  625. Status = NERR_Success;
  626. pAtInfo = NULL;
  627. Status = AtCheckSecurity(AT_JOB_GET_INFO);
  628. if (Status != NERR_Success)
  629. {
  630. return ERROR_ACCESS_DENIED;
  631. }
  632. //
  633. // Create the file name from the ID.
  634. //
  635. CreateAtJobPath(JobId, wszPath, MAX_PATH + 1);
  636. schDebugOut((DEB_ITRACE, "At job name: %S\n", wszPath));
  637. EnterCriticalSection(&gcsNetScheduleCritSection);
  638. //
  639. // Ensure the job object exists in storage.
  640. //
  641. if (GetFileAttributes(wszPath) == -1 &&
  642. GetLastError() == ERROR_FILE_NOT_FOUND)
  643. {
  644. //
  645. // Job object does not exist.
  646. //
  647. Status = APE_AT_ID_NOT_FOUND;
  648. CHECK_HRESULT(HRESULT_FROM_WIN32(Status));
  649. goto GetInfoExit;
  650. }
  651. //
  652. // Command size. A character count throughout the call to GetAtJob;
  653. // a byte count thereafter.
  654. //
  655. CommandSize = MAX_PATH + 1;
  656. //
  657. // Get At job information.
  658. //
  659. hr = g_pSched->m_pSch->GetAtJob(wszPath, &AtInfo, wszCommand, &CommandSize);
  660. if (FAILED(hr))
  661. {
  662. //
  663. // Convert the HRESULT to a WIN32 status code.
  664. //
  665. Status = WIN32_FROM_HRESULT(hr);
  666. if (Status == ERROR_FILE_NOT_FOUND)
  667. {
  668. Status = APE_AT_ID_NOT_FOUND;
  669. }
  670. goto GetInfoExit;
  671. }
  672. CommandSize *= sizeof(WCHAR); // Character count -> Byte count
  673. pAtInfo = (PAT_INFO)MIDL_user_allocate(sizeof(AT_INFO) + CommandSize);
  674. if (pAtInfo == NULL)
  675. {
  676. Status = ERROR_NOT_ENOUGH_MEMORY;
  677. CHECK_HRESULT(HRESULT_FROM_WIN32(Status));
  678. goto GetInfoExit;
  679. }
  680. *pAtInfo = AtInfo;
  681. pAtInfo->Command = (LPWSTR)(pAtInfo + 1);
  682. CopyMemory(pAtInfo->Command, wszCommand, CommandSize);
  683. GetInfoExit:
  684. LeaveCriticalSection(&gcsNetScheduleCritSection);
  685. *ppAtInfo = pAtInfo;
  686. return(Status);
  687. }
  688. //+---------------------------------------------------------------------------
  689. //
  690. // Function: GetAtJobIdFromFileName
  691. //
  692. // Synopsis: Return the DWORD At job id from an At filename. At filenames
  693. // are named according the following convention: "At<nnnn>.Job".
  694. // The "<nnnn>" portion is the At job id in string form.
  695. // eg: "At132.Job"
  696. //
  697. // Arguments: [pwszAtFileName] -- At path/filename.
  698. //
  699. // Returns: Non-zero At job id.
  700. // Zero if the filename is not recognized as an At filename.
  701. //
  702. // Notes: None.
  703. //
  704. //----------------------------------------------------------------------------
  705. DWORD
  706. GetAtJobIdFromFileName(WCHAR * pwszAtFileName)
  707. {
  708. static ULONG ccAtJobFilenamePrefix = 0;
  709. schAssert(pwszAtFileName != NULL);
  710. if (ccAtJobFilenamePrefix == 0)
  711. {
  712. ccAtJobFilenamePrefix = ARRAY_LEN(TSZ_AT_JOB_PREFIX) - 1;
  713. }
  714. //
  715. // Refer to the last (right-most) path element.
  716. //
  717. WCHAR * pwsz = wcsrchr(pwszAtFileName, L'\\');
  718. if (pwsz == NULL)
  719. {
  720. pwsz = pwszAtFileName;
  721. }
  722. //
  723. // Skip past the "At" filename portion.
  724. //
  725. if (_wcsnicmp(pwsz, TSZ_AT_JOB_PREFIX, ccAtJobFilenamePrefix) == 0)
  726. {
  727. pwsz += ccAtJobFilenamePrefix;
  728. }
  729. else
  730. {
  731. //
  732. // Unknown filename. At least, it's known if this is an At job.
  733. // Proceed no further.
  734. //
  735. return(0);
  736. }
  737. //
  738. // Isolate the At job Id portion of the path. Do so by temporarilly
  739. // replacing the extension period character with a null character.
  740. //
  741. WCHAR * pwszExt = wcsrchr(pwsz, L'.');
  742. if (pwszExt != NULL)
  743. {
  744. *pwszExt = L'\0';
  745. }
  746. //
  747. // Convert the Id to integer from string form.
  748. //
  749. DWORD AtJobId = _wtol(pwsz);
  750. //
  751. // Restore period character.
  752. //
  753. if (pwszExt != NULL)
  754. {
  755. *pwszExt = L'.';
  756. }
  757. return(AtJobId);
  758. }
  759. //+---------------------------------------------------------------------------
  760. //
  761. // Function: CreateAtJobPath
  762. //
  763. // Synopsis: Constructs a path in the form:
  764. // "...\Jobs\At_Jobs\At<nnnn>.job"
  765. // where <nnnn> is the At job id.
  766. //
  767. // Arguments: [JobId] -- At job Id.
  768. // [pwszPath] -- Returned path.
  769. // [cchBuff] -- size of path buffer
  770. //
  771. // Returns: None.
  772. //
  773. // Notes: None.
  774. //
  775. //----------------------------------------------------------------------------
  776. void
  777. CreateAtJobPath(DWORD JobId, WCHAR* pwszPath, size_t cchBuff)
  778. {
  779. WCHAR wszJobId[10 + 1];
  780. StringCchPrintf(wszJobId, 10 + 1, L"%d", JobId);
  781. StringCchCopy(pwszPath, cchBuff, gpwszAtJobPathTemplate);
  782. StringCchCat(pwszPath, cchBuff, wszJobId);
  783. StringCchCat(pwszPath, cchBuff, TSZ_DOTJOB);
  784. }
  785. //+---------------------------------------------------------------------------
  786. //
  787. // Function: InitializeNetScheduleApi
  788. //
  789. // Synopsis: Initializes globals used by the server-side NetScheduleXXX.
  790. // Associated globals:
  791. //
  792. // gpwszAtJobPathTemplate -- Used to construct full paths to
  793. // At jobs in the At jobs directory.
  794. // (eg: "...\At_Jobs\At")
  795. // gcsNetScheduleCritSection -- Used to serialize thread access
  796. // to server-side NetScheduleXXX
  797. // RPC.
  798. //
  799. // Arguments: None.
  800. //
  801. // Returns: S_OK
  802. // E_OUTOFMEMORY
  803. //
  804. // Notes: None.
  805. //
  806. //----------------------------------------------------------------------------
  807. HRESULT
  808. InitializeNetScheduleApi(void)
  809. {
  810. WCHAR wszBuffer[MAX_PATH + 1];
  811. ULONG ccAtJobPathTemplate;
  812. HRESULT hr;
  813. NET_API_STATUS Status;
  814. Status = AtCreateSecurityObject();
  815. if (Status != NERR_Success)
  816. {
  817. hr = Status;
  818. CHECK_HRESULT(hr);
  819. goto InitializeError;
  820. }
  821. schAssert(g_TasksFolderInfo.ptszPath);
  822. ULONG ccFolderPath;
  823. ccFolderPath = wcslen(g_TasksFolderInfo.ptszPath);
  824. //
  825. // Create the At job path template. For use in NetScheduleJobAdd/Del.
  826. // Example: "<Job Folder Path>\At". To which the Job Id (string form) +
  827. // the ".job" extension is appended.
  828. //
  829. ccAtJobPathTemplate = wcslen(g_TasksFolderInfo.ptszPath) +
  830. ARRAY_LEN(TSZ_AT_JOB_PREFIX) +
  831. 1; // '\' + null terminator
  832. gpwszAtJobPathTemplate = new WCHAR[ccAtJobPathTemplate];
  833. if (gpwszAtJobPathTemplate == NULL)
  834. {
  835. hr = E_OUTOFMEMORY;
  836. CHECK_HRESULT(hr);
  837. goto InitializeError;
  838. }
  839. StringCchCopy(gpwszAtJobPathTemplate, ccAtJobPathTemplate, g_TasksFolderInfo.ptszPath);
  840. StringCchCat(gpwszAtJobPathTemplate, ccAtJobPathTemplate, BACKSLASH_STR TSZ_AT_JOB_PREFIX);
  841. //
  842. // Register the Event Source, which is used to report NetSchedule
  843. // errors in the event log - for NT4 ATSVC compatibility
  844. //
  845. g_hAtEventSource = RegisterEventSource(NULL, SCHEDULE_EVENTLOG_NAME);
  846. if (g_hAtEventSource == NULL)
  847. {
  848. hr = HRESULT_FROM_WIN32(GetLastError());
  849. CHECK_HRESULT(hr);
  850. goto InitializeError;
  851. }
  852. return(S_OK);
  853. InitializeError:
  854. if (gpwszAtJobPathTemplate != NULL)
  855. {
  856. delete gpwszAtJobPathTemplate;
  857. gpwszAtJobPathTemplate = NULL;
  858. }
  859. return(hr);
  860. }
  861. //+---------------------------------------------------------------------------
  862. //
  863. // Function: UninitializeNetScheduleApi
  864. //
  865. // Synopsis: Un-does work done in InitializeNetScheduleApi.
  866. //
  867. // Arguments: None.
  868. //
  869. // Returns: None.
  870. //
  871. // Notes: None.
  872. //
  873. //----------------------------------------------------------------------------
  874. void
  875. UninitializeNetScheduleApi(void)
  876. {
  877. //
  878. // Clean up the event logging for downlevel jobs
  879. //
  880. if (g_hAtEventSource != NULL)
  881. {
  882. DeregisterEventSource(g_hAtEventSource);
  883. g_hAtEventSource = NULL;
  884. }
  885. if (gpwszAtJobPathTemplate != NULL)
  886. {
  887. delete gpwszAtJobPathTemplate;
  888. gpwszAtJobPathTemplate = NULL;
  889. }
  890. AtDeleteSecurityObject();
  891. }
  892. //+---------------------------------------------------------------------------
  893. //
  894. // Function: IsAdminFileOwner
  895. //
  896. // Synopsis: Ensure the file owner is an adminstrator. Currently used to
  897. // determine if AT jobs are owned by administrators. Local system
  898. // ownership is allowed as well.
  899. //
  900. // Arguments: [pwszFile] -- Checked file.
  901. //
  902. // Returns: TRUE -- The owner is an admin or local system.
  903. // FALSE -- The owner isn't an admin or local system, or the
  904. // attempt to confirm ownership identity failed.
  905. //
  906. // Notes: None.
  907. //
  908. //----------------------------------------------------------------------------
  909. BOOL
  910. IsAdminFileOwner(LPCWSTR pwszFile)
  911. {
  912. #define SECDESCR_STACK_BUFFER_SIZE 512
  913. BYTE rgbBuffer[SECDESCR_STACK_BUFFER_SIZE];
  914. PSECURITY_DESCRIPTOR pOwnerSecDescr = rgbBuffer;
  915. DWORD cbSize = SECDESCR_STACK_BUFFER_SIZE;
  916. DWORD cbSizeNeeded = 0;
  917. BOOL fAllocatedBuffer;
  918. if (GetFileSecurity(pwszFile,
  919. OWNER_SECURITY_INFORMATION,
  920. pOwnerSecDescr,
  921. cbSize,
  922. &cbSizeNeeded))
  923. {
  924. //
  925. // The information fit within the stack-allocated buffer.
  926. // This should cover 90% of the cases.
  927. //
  928. }
  929. else if (GetLastError() == ERROR_INSUFFICIENT_BUFFER && cbSizeNeeded)
  930. {
  931. //
  932. // Too much data. We'll need to allocate memory on the heap.
  933. //
  934. fAllocatedBuffer = TRUE;
  935. pOwnerSecDescr = (SECURITY_DESCRIPTOR *)new BYTE[cbSizeNeeded];
  936. if (pOwnerSecDescr == NULL)
  937. {
  938. return FALSE;
  939. }
  940. if (!GetFileSecurity(pwszFile,
  941. OWNER_SECURITY_INFORMATION,
  942. pOwnerSecDescr,
  943. cbSizeNeeded,
  944. &cbSizeNeeded))
  945. {
  946. delete pOwnerSecDescr;
  947. return FALSE;
  948. }
  949. }
  950. else
  951. {
  952. //
  953. // An unexpected error occurred. Disallow access.
  954. //
  955. return FALSE;
  956. }
  957. //
  958. // Get the owner sid.
  959. //
  960. PSID pOwnerSid;
  961. BOOL fOwnerDefaulted;
  962. BOOL fRet = FALSE;
  963. if (GetSecurityDescriptorOwner(pOwnerSecDescr, &pOwnerSid,
  964. &fOwnerDefaulted))
  965. {
  966. if (IsValidSid(pOwnerSid))
  967. {
  968. //
  969. // Enumerate the subauthorities to check for the admin RID.
  970. //
  971. for (DWORD i = *GetSidSubAuthorityCount(pOwnerSid); i; i--)
  972. {
  973. DWORD SubAuthority = *GetSidSubAuthority(pOwnerSid, i);
  974. if (SubAuthority == DOMAIN_ALIAS_RID_ADMINS ||
  975. SubAuthority == SECURITY_LOCAL_SYSTEM_RID)
  976. {
  977. //
  978. // Done. Owner is an admin or local system.
  979. //
  980. fRet = TRUE;
  981. break;
  982. }
  983. }
  984. }
  985. }
  986. if (pOwnerSecDescr != rgbBuffer)
  987. {
  988. delete pOwnerSecDescr;
  989. }
  990. return fRet;
  991. }