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.

738 lines
17 KiB

  1. /*++
  2. Copyright (c) 1999 Microsoft Corporation
  3. Module Name:
  4. apicli.c
  5. Abstract:
  6. Windows File Protection client side APIs.
  7. Author:
  8. Wesley Witt (wesw) 27-May-1999
  9. Revision History:
  10. Andrew Ritz (andrewr) 5-Jul-1999 : added comments
  11. --*/
  12. #include "sfcp.h"
  13. #pragma hdrstop
  14. //
  15. // global RPC binding handle because some client API's don't require you to
  16. // specify an RPC handle
  17. //
  18. HANDLE _pRpcHandle;
  19. //
  20. // global boolean variable that tracks how the global RPC binding handle was
  21. // established (via explicit or implicit call to SfcConnectToServer), where
  22. // TRUE indicates that the connection was established implicitly
  23. static BOOL InternalClient;
  24. //
  25. // these macros are used by each client side api to
  26. // ensure that we have a valid rpc handle. if the
  27. // calling application chooses to not call SfcConnectToServer
  28. // they connect to the local server and save the handle
  29. // in a global for future use.
  30. //
  31. #define EnsureGoodConnectionHandleStatus(_h)\
  32. if (_h == NULL) {\
  33. if (_pRpcHandle == NULL) {\
  34. BOOL ic = InternalClient;\
  35. _pRpcHandle = SfcConnectToServer( NULL );\
  36. if (_pRpcHandle == NULL) {\
  37. return RPC_S_SERVER_UNAVAILABLE;\
  38. }\
  39. InternalClient = ic;\
  40. }\
  41. _h = _pRpcHandle;\
  42. }
  43. #define EnsureGoodConnectionHandleBool(_h)\
  44. if (_h == NULL) {\
  45. if (_pRpcHandle == NULL) {\
  46. BOOL ic = InternalClient;\
  47. _pRpcHandle = SfcConnectToServer( NULL );\
  48. if (_pRpcHandle == NULL) {\
  49. SetLastError(RPC_S_SERVER_UNAVAILABLE);\
  50. return FALSE;\
  51. }\
  52. InternalClient = ic;\
  53. }\
  54. _h = _pRpcHandle;\
  55. }
  56. void
  57. ClientApiInit(
  58. void
  59. )
  60. {
  61. #ifndef _WIN64
  62. SfcInitPathTranslator();
  63. #endif // _WIN64
  64. }
  65. void
  66. ClientApiCleanup(
  67. void
  68. )
  69. /*++
  70. Routine Description:
  71. RPC cleanup wrapper routine called by client side when done with server side
  72. connection that was previously established with SfcConnectToServer().
  73. Arguments:
  74. None
  75. Return Value:
  76. none.
  77. --*/
  78. {
  79. if (_pRpcHandle) {
  80. SfcClose( _pRpcHandle );
  81. }
  82. #ifndef _WIN64
  83. SfcCleanupPathTranslator(TRUE);
  84. #endif // _WIN64
  85. }
  86. HANDLE
  87. WINAPI
  88. SfcConnectToServer(
  89. IN PCWSTR ServerName
  90. )
  91. /*++
  92. Routine Description:
  93. RPC attachment routine.
  94. Arguments:
  95. ServerName - NULL terminated unicode string specifying server to connect to
  96. Return Value:
  97. an RPC binding handle on success, else NULL.
  98. --*/
  99. {
  100. RPC_BINDING_HANDLE RpcHandle;
  101. NTSTATUS Status;
  102. #ifndef SFC_REMOTE_CLIENT_SUPPORT
  103. //
  104. // Note: We don't want to support remote calls for now.
  105. // This call should really allow the local computer name or synonyms
  106. // thereof, but since this is a private interface, we don't worry
  107. // about that.
  108. if (ServerName) {
  109. SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
  110. return(NULL);
  111. }
  112. #endif
  113. //
  114. // connect to the RPC server
  115. //
  116. Status = RpcpBindRpc(
  117. ServerName ? (PWSTR) ServerName : L".",
  118. L"SfcApi",
  119. 0,
  120. &RpcHandle
  121. );
  122. if (!NT_SUCCESS(Status)) {
  123. return NULL;
  124. }
  125. //
  126. // record this as an internal client connection
  127. //
  128. InternalClient = TRUE;
  129. return RpcHandle;
  130. }
  131. VOID
  132. SfcClose(
  133. IN HANDLE RpcHandle
  134. )
  135. /*++
  136. Routine Description:
  137. RPC cleanup routine.
  138. Arguments:
  139. RpcHandle - RPC binding handle to the SFC server
  140. Return Value:
  141. None.
  142. --*/
  143. {
  144. RpcpUnbindRpc( RpcHandle );
  145. InternalClient = FALSE;
  146. _pRpcHandle = NULL;
  147. }
  148. DWORD
  149. WINAPI
  150. SfcFileException(
  151. IN HANDLE RpcHandle,
  152. IN PCWSTR FileName,
  153. IN DWORD ExpectedChangeType
  154. )
  155. /*++
  156. Routine Description:
  157. Routine to exempt a given file from the specified file change. This
  158. routine is used by certain clients to allow files to be deleted from
  159. the system, etc.
  160. Arguments:
  161. RpcHandle - RPC binding handle to the SFC server
  162. FileName - NULL terminated unicode string specifying full filename of the
  163. file to be exempted
  164. ExpectedChangeType - SFC_ACTION_* mask listing the file changes to exempt
  165. Return Value:
  166. Win32 error code indicating outcome.
  167. --*/
  168. {
  169. #ifndef _WIN64
  170. DWORD dwError = ERROR_SUCCESS;
  171. UNICODE_STRING Path = { 0 };
  172. NTSTATUS Status;
  173. EnsureGoodConnectionHandleStatus( RpcHandle );
  174. Status = SfcRedirectPath(FileName, &Path);
  175. if(!NT_SUCCESS(Status))
  176. {
  177. dwError = RtlNtStatusToDosError(Status);
  178. goto exit;
  179. }
  180. ASSERT(Path.Buffer != NULL);
  181. dwError = SfcCli_FileException( RpcHandle, Path.Buffer, ExpectedChangeType );
  182. exit:
  183. MemFree(Path.Buffer);
  184. return dwError;
  185. #else // _WIN64
  186. EnsureGoodConnectionHandleStatus( RpcHandle );
  187. return SfcCli_FileException( RpcHandle, FileName, ExpectedChangeType );
  188. #endif // _WIN64
  189. }
  190. DWORD
  191. WINAPI
  192. SfcInitiateScan(
  193. IN HANDLE RpcHandle,
  194. IN DWORD ScanWhen
  195. )
  196. /*++
  197. Routine Description:
  198. Routine to start some sort scan on the system.
  199. Arguments:
  200. RpcHandle - RPC binding handle to the SFC server
  201. ScanWhen - flag indicating when to scan. This parameter is currently
  202. unused.
  203. Return Value:
  204. Win32 error code indicating outcome.
  205. --*/
  206. {
  207. UNREFERENCED_PARAMETER(ScanWhen);
  208. EnsureGoodConnectionHandleStatus( RpcHandle );
  209. return SfcCli_InitiateScan( RpcHandle, ScanWhen );
  210. }
  211. BOOL
  212. WINAPI
  213. SfcInstallProtectedFiles(
  214. IN HANDLE RpcHandle,
  215. IN PCWSTR FileNames,
  216. IN BOOL AllowUI,
  217. IN PCWSTR ClassName,
  218. IN PCWSTR WindowName,
  219. IN PSFCNOTIFICATIONCALLBACK SfcNotificationCallback,
  220. IN DWORD_PTR Context OPTIONAL
  221. )
  222. /*++
  223. Routine Description:
  224. Routine to install one or more protected system files onto the system at
  225. the protected location. A client can use this API to request that WFP
  226. install the specified operating system files as appropriate (instead of the
  227. client redistributing the operating system files!) The caller specifies a
  228. callback routine and a context structure that is called once per file.
  229. Arguments:
  230. RpcHandle - RPC binding handle to the SFC server
  231. FileNames - a list of NULL seperated unicode strings, terminated by two
  232. NULL characters
  233. AllowUI - a BOOL indicating whether UI is allowed or not. If this value
  234. is TRUE, then any prompts for UI cause the API call to fail.
  235. ClassName - NULL terminated unicode string indicating the window classname
  236. for the parent window
  237. WindowName - NULL terminated unicode string indicating the window name for
  238. the parent window for any UI that may be displayed
  239. SfcNotificationCallback - pointer to a callback routine that is called once
  240. per file.
  241. Context - opaque pointer to caller defined context structure that is
  242. passed through to the callback routine.
  243. Return Value:
  244. TRUE for success, FALSE for error. last error code contains a Win32 error
  245. code on failure.
  246. --*/
  247. {
  248. DWORD rVal = ERROR_SUCCESS;
  249. PCWSTR fname;
  250. ULONG cnt = 0, cntold = 0;
  251. ULONG sz = 0;
  252. PFILEINSTALL_STATUS cs = NULL;
  253. DWORD StatusSize = 0;
  254. UNICODE_STRING Path = { 0 };
  255. #ifndef _WIN64
  256. //
  257. // must translate the paths
  258. //
  259. PWSTR szTranslatedFiles = NULL;
  260. #endif
  261. //
  262. // parameter validation
  263. //
  264. if((SfcNotificationCallback == NULL) ||
  265. (FileNames == NULL)) {
  266. rVal = ERROR_INVALID_PARAMETER;
  267. goto exit;
  268. }
  269. //
  270. // 1. if a windowname is specified, a classname should be specified
  271. // 2. if a classname is specified, a windowname should be specified
  272. // 3. if we don't allow UI, then windowname and classname should both be
  273. // NULL.
  274. //
  275. if ((WindowName && !ClassName)
  276. || (ClassName && !WindowName)
  277. || (!AllowUI && (ClassName || WindowName))) {
  278. rVal = ERROR_INVALID_PARAMETER;
  279. goto exit;
  280. }
  281. //
  282. // validate RPC handle
  283. //
  284. EnsureGoodConnectionHandleBool( RpcHandle );
  285. //
  286. // check out how large of a buffer to send over
  287. //
  288. try {
  289. #ifdef _WIN64
  290. for(fname = FileNames; *fname; ++cntold) {
  291. DWORD StringLength;
  292. StringLength = wcslen(fname) + 1;
  293. sz += StringLength * sizeof(WCHAR);
  294. fname += StringLength;
  295. }
  296. #else
  297. //
  298. // must translate paths before calling the server
  299. //
  300. PWSTR szNewBuf = NULL;
  301. for(fname = FileNames; *fname; fname += wcslen(fname) + 1, ++cntold) {
  302. NTSTATUS Status;
  303. Status = SfcRedirectPath(fname, &Path);
  304. if(!NT_SUCCESS(Status))
  305. {
  306. rVal = RtlNtStatusToDosError(Status);
  307. goto exit;
  308. }
  309. if(NULL == szTranslatedFiles)
  310. {
  311. szNewBuf = (PWSTR) MemAlloc(Path.Length + 2 * sizeof(WCHAR));
  312. }
  313. else
  314. {
  315. szNewBuf = (PWSTR) MemReAlloc(sz + Path.Length + 2 * sizeof(WCHAR), szTranslatedFiles);
  316. }
  317. if(szNewBuf != NULL)
  318. {
  319. szTranslatedFiles = szNewBuf;
  320. RtlCopyMemory((PCHAR) szTranslatedFiles + sz, Path.Buffer, Path.Length + sizeof(WCHAR));
  321. sz += Path.Length + sizeof(WCHAR);
  322. }
  323. MemFree(Path.Buffer);
  324. RtlZeroMemory(&Path, sizeof(Path));
  325. if(NULL == szNewBuf)
  326. {
  327. rVal = ERROR_NOT_ENOUGH_MEMORY;
  328. goto exit;
  329. }
  330. }
  331. //
  332. //set the last null
  333. //
  334. if(szTranslatedFiles != NULL)
  335. {
  336. szTranslatedFiles[sz / sizeof(WCHAR)] = L'\0';
  337. }
  338. #endif
  339. } except (EXCEPTION_EXECUTE_HANDLER) {
  340. rVal = RtlNtStatusToDosError(GetExceptionCode());
  341. goto exit;
  342. }
  343. if(0 == cntold)
  344. {
  345. //
  346. // not files to install
  347. //
  348. rVal = ERROR_INVALID_PARAMETER;
  349. goto exit;
  350. }
  351. //
  352. // for terminating NULL
  353. //
  354. sz+=sizeof(WCHAR);
  355. //
  356. // make the RPC call to install the files
  357. //
  358. rVal = SfcCli_InstallProtectedFiles(
  359. RpcHandle,
  360. #ifdef _WIN64
  361. (LPBYTE)FileNames,
  362. #else
  363. (LPBYTE)szTranslatedFiles,
  364. #endif
  365. sz,
  366. (LPBYTE*)&cs,
  367. &StatusSize,
  368. &cnt,
  369. AllowUI,
  370. ClassName,
  371. WindowName
  372. );
  373. if (rVal != ERROR_SUCCESS) {
  374. goto exit;
  375. }
  376. //
  377. // we should have gotten back the same amount of status information as the
  378. // number of files that we passed in
  379. //
  380. ASSERT(cnt == cntold);
  381. //
  382. // call the callback function once for each file, now that we've completed
  383. // copying the files in the list. We pass the caller a structure which
  384. // indicates the success of copying each individual file in the list.
  385. //
  386. for (fname = FileNames, sz=0; sz<cnt; sz++, fname += wcslen(fname) + 1) {
  387. LPEXCEPTION_POINTERS ExceptionPointers = NULL;
  388. try {
  389. NTSTATUS Status;
  390. BOOL b;
  391. //
  392. // don't use the (possibly reditected) file names returned from the server
  393. //
  394. Status = SfcAllocUnicodeStringFromPath(fname, &Path);
  395. if(!NT_SUCCESS(Status))
  396. {
  397. rVal = RtlNtStatusToDosError(Status);
  398. goto exit;
  399. }
  400. cs[sz].FileName = Path.Buffer;
  401. b = SfcNotificationCallback( &cs[sz], Context );
  402. MemFree(Path.Buffer);
  403. RtlZeroMemory(&Path, sizeof(Path));
  404. if (!b) {
  405. //
  406. // return FALSE if the callback fails for any reason
  407. //
  408. rVal = ERROR_CANCELLED;
  409. goto exit;
  410. }
  411. } except (ExceptionPointers = GetExceptionInformation(),
  412. EXCEPTION_EXECUTE_HANDLER) {
  413. //
  414. // we hit an exception calling the callback...return exception code
  415. //
  416. DebugPrint3( LVL_VERBOSE,
  417. L"SIPF hit exception %x while calling callback routine %x at address %x\n",
  418. ExceptionPointers->ExceptionRecord->ExceptionCode,
  419. SfcNotificationCallback,
  420. ExceptionPointers->ExceptionRecord->ExceptionAddress
  421. );
  422. rVal = RtlNtStatusToDosError(ExceptionPointers->ExceptionRecord->ExceptionCode);
  423. goto exit;
  424. }
  425. }
  426. exit:
  427. MemFree(Path.Buffer);
  428. if(cs != NULL)
  429. {
  430. midl_user_free( cs );
  431. }
  432. #ifndef _WIN64
  433. MemFree(szTranslatedFiles);
  434. #endif
  435. SetLastError(rVal);
  436. return rVal == ERROR_SUCCESS;
  437. }
  438. BOOL
  439. WINAPI
  440. SfcGetNextProtectedFile(
  441. IN HANDLE RpcHandle,
  442. IN PPROTECTED_FILE_DATA ProtFileData
  443. )
  444. /*++
  445. Routine Description:
  446. Routine to retrieve the next protected file in the list.
  447. Arguments:
  448. RpcHandle - RPC binding handle to the SFC server
  449. ProtFileData - pointer to a PROTECTED_FILE_DATA structure to be filled
  450. in by function.
  451. Return Value:
  452. TRUE for success, FALSE for failure. If there are no more files, the last
  453. error code will be set to ERROR_NO_MORE_FILES.
  454. --*/
  455. {
  456. DWORD rVal;
  457. LPWSTR FileName = NULL;
  458. DWORD FileNameSize = 0;
  459. BOOL bReturn = FALSE;
  460. DWORD FileNumber;
  461. //
  462. // validate parameters
  463. //
  464. if (ProtFileData == NULL) {
  465. SetLastError(ERROR_INVALID_PARAMETER);
  466. return(FALSE);
  467. }
  468. try {
  469. FileNumber = ProtFileData->FileNumber;
  470. } except (EXCEPTION_EXECUTE_HANDLER) {
  471. SetLastError(ERROR_INVALID_DATA);
  472. return(FALSE);
  473. }
  474. //
  475. // If this is not an internal client, then RpcHandle must be NULL.
  476. //
  477. if (InternalClient == FALSE) {
  478. if (RpcHandle != NULL) {
  479. SetLastError(ERROR_INVALID_HANDLE);
  480. return(FALSE);
  481. }
  482. }
  483. EnsureGoodConnectionHandleBool( RpcHandle );
  484. //
  485. // call the server API
  486. //
  487. rVal = SfcCli_GetNextProtectedFile(
  488. RpcHandle,
  489. FileNumber,
  490. (LPBYTE*)&FileName,
  491. &FileNameSize
  492. );
  493. if (rVal != ERROR_SUCCESS) {
  494. SetLastError(rVal);
  495. goto exit;
  496. }
  497. bReturn = TRUE;
  498. //
  499. // copy into the caller supplied buffer
  500. //
  501. try {
  502. wcscpy( ProtFileData->FileName, FileName );
  503. ProtFileData->FileNumber += 1;
  504. } except (EXCEPTION_EXECUTE_HANDLER) {
  505. SetLastError(RtlNtStatusToDosError(GetExceptionCode()));
  506. bReturn = FALSE;
  507. }
  508. midl_user_free( FileName );
  509. exit:
  510. return(bReturn);
  511. }
  512. BOOL
  513. WINAPI
  514. SfcIsFileProtected(
  515. IN HANDLE RpcHandle,
  516. IN LPCWSTR ProtFileName
  517. )
  518. /*++
  519. Routine Description:
  520. Routine to determine if the specified file is protected.
  521. Arguments:
  522. RpcHandle - RPC binding handle to the SFC server
  523. ProtFileName - NULL terminated unicode string indicating fully qualified
  524. filename to query
  525. Return Value:
  526. TRUE if file is protected, FALSE if it isn't. last error code contains a
  527. Win32 error code on failure.
  528. --*/
  529. {
  530. DWORD rVal;
  531. DWORD dwAttributes, dwSize;
  532. WCHAR Buffer[MAX_PATH];
  533. //
  534. // parameter validation
  535. //
  536. if (ProtFileName == NULL) {
  537. SetLastError(ERROR_INVALID_PARAMETER);
  538. return FALSE;
  539. }
  540. //
  541. // if this is not an internal client, then RpcHandle must be NULL.
  542. //
  543. if (InternalClient == FALSE) {
  544. if (RpcHandle != NULL) {
  545. SetLastError(ERROR_INVALID_HANDLE);
  546. return FALSE;
  547. }
  548. }
  549. EnsureGoodConnectionHandleBool( RpcHandle );
  550. //
  551. // check whether this file is sxs-wfp first, which could be done on client-side only
  552. //
  553. //
  554. // check whether it begins with "%SystemRoot%\\WinSxS\\"
  555. //
  556. dwSize = ExpandEnvironmentStrings( L"%SystemRoot%\\WinSxS\\", Buffer, UnicodeChars(Buffer));
  557. if(0 == dwSize)
  558. {
  559. DebugPrint1( LVL_MINIMAL, L"SFC : ExpandEnvironmentStrings failed with lastError = 0x%x", GetLastError());
  560. return FALSE;
  561. }
  562. --dwSize;
  563. try {
  564. if ((wcslen(ProtFileName) > dwSize) &&
  565. (_wcsnicmp(Buffer, ProtFileName, dwSize) == 0)) // if they're equal, this could be a protected file
  566. {
  567. dwAttributes = GetFileAttributesW(ProtFileName);
  568. if (dwAttributes == 0xFFFFFFFF)
  569. return FALSE;
  570. if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) {
  571. SetLastError(ERROR_INVALID_PARAMETER);
  572. return FALSE;
  573. }
  574. return TRUE;
  575. }
  576. } except(EXCEPTION_EXECUTE_HANDLER) {
  577. SetLastError(ERROR_INVALID_PARAMETER);
  578. return FALSE;
  579. }
  580. //
  581. // call server to determine if file is protected
  582. //
  583. rVal = SfcCli_IsFileProtected( RpcHandle, (PWSTR)ProtFileName );
  584. if (rVal != ERROR_SUCCESS) {
  585. SetLastError(rVal);
  586. return FALSE;
  587. }
  588. return TRUE;
  589. }