windows-nt/Source/XPSP1/NT/base/subsys/sm/sfc/dll/apicli.c
2020-09-26 16:20:57 +08:00

739 lines
17 KiB
C

/*++
Copyright (c) 1999 Microsoft Corporation
Module Name:
apicli.c
Abstract:
Windows File Protection client side APIs.
Author:
Wesley Witt (wesw) 27-May-1999
Revision History:
Andrew Ritz (andrewr) 5-Jul-1999 : added comments
--*/
#include "sfcp.h"
#pragma hdrstop
//
// global RPC binding handle because some client API's don't require you to
// specify an RPC handle
//
HANDLE _pRpcHandle;
//
// global boolean variable that tracks how the global RPC binding handle was
// established (via explicit or implicit call to SfcConnectToServer), where
// TRUE indicates that the connection was established implicitly
static BOOL InternalClient;
//
// these macros are used by each client side api to
// ensure that we have a valid rpc handle. if the
// calling application chooses to not call SfcConnectToServer
// they connect to the local server and save the handle
// in a global for future use.
//
#define EnsureGoodConnectionHandleStatus(_h)\
if (_h == NULL) {\
if (_pRpcHandle == NULL) {\
BOOL ic = InternalClient;\
_pRpcHandle = SfcConnectToServer( NULL );\
if (_pRpcHandle == NULL) {\
return RPC_S_SERVER_UNAVAILABLE;\
}\
InternalClient = ic;\
}\
_h = _pRpcHandle;\
}
#define EnsureGoodConnectionHandleBool(_h)\
if (_h == NULL) {\
if (_pRpcHandle == NULL) {\
BOOL ic = InternalClient;\
_pRpcHandle = SfcConnectToServer( NULL );\
if (_pRpcHandle == NULL) {\
SetLastError(RPC_S_SERVER_UNAVAILABLE);\
return FALSE;\
}\
InternalClient = ic;\
}\
_h = _pRpcHandle;\
}
void
ClientApiInit(
void
)
{
#ifndef _WIN64
SfcInitPathTranslator();
#endif // _WIN64
}
void
ClientApiCleanup(
void
)
/*++
Routine Description:
RPC cleanup wrapper routine called by client side when done with server side
connection that was previously established with SfcConnectToServer().
Arguments:
None
Return Value:
none.
--*/
{
if (_pRpcHandle) {
SfcClose( _pRpcHandle );
}
#ifndef _WIN64
SfcCleanupPathTranslator(TRUE);
#endif // _WIN64
}
HANDLE
WINAPI
SfcConnectToServer(
IN PCWSTR ServerName
)
/*++
Routine Description:
RPC attachment routine.
Arguments:
ServerName - NULL terminated unicode string specifying server to connect to
Return Value:
an RPC binding handle on success, else NULL.
--*/
{
RPC_BINDING_HANDLE RpcHandle;
NTSTATUS Status;
#ifndef SFC_REMOTE_CLIENT_SUPPORT
//
// Note: We don't want to support remote calls for now.
// This call should really allow the local computer name or synonyms
// thereof, but since this is a private interface, we don't worry
// about that.
if (ServerName) {
SetLastError( ERROR_CALL_NOT_IMPLEMENTED );
return(NULL);
}
#endif
//
// connect to the RPC server
//
Status = RpcpBindRpc(
ServerName ? (PWSTR) ServerName : L".",
L"SfcApi",
0,
&RpcHandle
);
if (!NT_SUCCESS(Status)) {
return NULL;
}
//
// record this as an internal client connection
//
InternalClient = TRUE;
return RpcHandle;
}
VOID
SfcClose(
IN HANDLE RpcHandle
)
/*++
Routine Description:
RPC cleanup routine.
Arguments:
RpcHandle - RPC binding handle to the SFC server
Return Value:
None.
--*/
{
RpcpUnbindRpc( RpcHandle );
InternalClient = FALSE;
_pRpcHandle = NULL;
}
DWORD
WINAPI
SfcFileException(
IN HANDLE RpcHandle,
IN PCWSTR FileName,
IN DWORD ExpectedChangeType
)
/*++
Routine Description:
Routine to exempt a given file from the specified file change. This
routine is used by certain clients to allow files to be deleted from
the system, etc.
Arguments:
RpcHandle - RPC binding handle to the SFC server
FileName - NULL terminated unicode string specifying full filename of the
file to be exempted
ExpectedChangeType - SFC_ACTION_* mask listing the file changes to exempt
Return Value:
Win32 error code indicating outcome.
--*/
{
#ifndef _WIN64
DWORD dwError = ERROR_SUCCESS;
UNICODE_STRING Path = { 0 };
NTSTATUS Status;
EnsureGoodConnectionHandleStatus( RpcHandle );
Status = SfcRedirectPath(FileName, &Path);
if(!NT_SUCCESS(Status))
{
dwError = RtlNtStatusToDosError(Status);
goto exit;
}
ASSERT(Path.Buffer != NULL);
dwError = SfcCli_FileException( RpcHandle, Path.Buffer, ExpectedChangeType );
exit:
MemFree(Path.Buffer);
return dwError;
#else // _WIN64
EnsureGoodConnectionHandleStatus( RpcHandle );
return SfcCli_FileException( RpcHandle, FileName, ExpectedChangeType );
#endif // _WIN64
}
DWORD
WINAPI
SfcInitiateScan(
IN HANDLE RpcHandle,
IN DWORD ScanWhen
)
/*++
Routine Description:
Routine to start some sort scan on the system.
Arguments:
RpcHandle - RPC binding handle to the SFC server
ScanWhen - flag indicating when to scan. This parameter is currently
unused.
Return Value:
Win32 error code indicating outcome.
--*/
{
UNREFERENCED_PARAMETER(ScanWhen);
EnsureGoodConnectionHandleStatus( RpcHandle );
return SfcCli_InitiateScan( RpcHandle, ScanWhen );
}
BOOL
WINAPI
SfcInstallProtectedFiles(
IN HANDLE RpcHandle,
IN PCWSTR FileNames,
IN BOOL AllowUI,
IN PCWSTR ClassName,
IN PCWSTR WindowName,
IN PSFCNOTIFICATIONCALLBACK SfcNotificationCallback,
IN DWORD_PTR Context OPTIONAL
)
/*++
Routine Description:
Routine to install one or more protected system files onto the system at
the protected location. A client can use this API to request that WFP
install the specified operating system files as appropriate (instead of the
client redistributing the operating system files!) The caller specifies a
callback routine and a context structure that is called once per file.
Arguments:
RpcHandle - RPC binding handle to the SFC server
FileNames - a list of NULL seperated unicode strings, terminated by two
NULL characters
AllowUI - a BOOL indicating whether UI is allowed or not. If this value
is TRUE, then any prompts for UI cause the API call to fail.
ClassName - NULL terminated unicode string indicating the window classname
for the parent window
WindowName - NULL terminated unicode string indicating the window name for
the parent window for any UI that may be displayed
SfcNotificationCallback - pointer to a callback routine that is called once
per file.
Context - opaque pointer to caller defined context structure that is
passed through to the callback routine.
Return Value:
TRUE for success, FALSE for error. last error code contains a Win32 error
code on failure.
--*/
{
DWORD rVal = ERROR_SUCCESS;
PCWSTR fname;
ULONG cnt = 0, cntold = 0;
ULONG sz = 0;
PFILEINSTALL_STATUS cs = NULL;
DWORD StatusSize = 0;
UNICODE_STRING Path = { 0 };
#ifndef _WIN64
//
// must translate the paths
//
PWSTR szTranslatedFiles = NULL;
#endif
//
// parameter validation
//
if((SfcNotificationCallback == NULL) ||
(FileNames == NULL)) {
rVal = ERROR_INVALID_PARAMETER;
goto exit;
}
//
// 1. if a windowname is specified, a classname should be specified
// 2. if a classname is specified, a windowname should be specified
// 3. if we don't allow UI, then windowname and classname should both be
// NULL.
//
if ((WindowName && !ClassName)
|| (ClassName && !WindowName)
|| (!AllowUI && (ClassName || WindowName))) {
rVal = ERROR_INVALID_PARAMETER;
goto exit;
}
//
// validate RPC handle
//
EnsureGoodConnectionHandleBool( RpcHandle );
//
// check out how large of a buffer to send over
//
try {
#ifdef _WIN64
for(fname = FileNames; *fname; ++cntold) {
DWORD StringLength;
StringLength = wcslen(fname) + 1;
sz += StringLength * sizeof(WCHAR);
fname += StringLength;
}
#else
//
// must translate paths before calling the server
//
PWSTR szNewBuf = NULL;
for(fname = FileNames; *fname; fname += wcslen(fname) + 1, ++cntold) {
NTSTATUS Status;
Status = SfcRedirectPath(fname, &Path);
if(!NT_SUCCESS(Status))
{
rVal = RtlNtStatusToDosError(Status);
goto exit;
}
if(NULL == szTranslatedFiles)
{
szNewBuf = (PWSTR) MemAlloc(Path.Length + 2 * sizeof(WCHAR));
}
else
{
szNewBuf = (PWSTR) MemReAlloc(sz + Path.Length + 2 * sizeof(WCHAR), szTranslatedFiles);
}
if(szNewBuf != NULL)
{
szTranslatedFiles = szNewBuf;
RtlCopyMemory((PCHAR) szTranslatedFiles + sz, Path.Buffer, Path.Length + sizeof(WCHAR));
sz += Path.Length + sizeof(WCHAR);
}
MemFree(Path.Buffer);
RtlZeroMemory(&Path, sizeof(Path));
if(NULL == szNewBuf)
{
rVal = ERROR_NOT_ENOUGH_MEMORY;
goto exit;
}
}
//
//set the last null
//
if(szTranslatedFiles != NULL)
{
szTranslatedFiles[sz / sizeof(WCHAR)] = L'\0';
}
#endif
} except (EXCEPTION_EXECUTE_HANDLER) {
rVal = RtlNtStatusToDosError(GetExceptionCode());
goto exit;
}
if(0 == cntold)
{
//
// not files to install
//
rVal = ERROR_INVALID_PARAMETER;
goto exit;
}
//
// for terminating NULL
//
sz+=sizeof(WCHAR);
//
// make the RPC call to install the files
//
rVal = SfcCli_InstallProtectedFiles(
RpcHandle,
#ifdef _WIN64
(LPBYTE)FileNames,
#else
(LPBYTE)szTranslatedFiles,
#endif
sz,
(LPBYTE*)&cs,
&StatusSize,
&cnt,
AllowUI,
ClassName,
WindowName
);
if (rVal != ERROR_SUCCESS) {
goto exit;
}
//
// we should have gotten back the same amount of status information as the
// number of files that we passed in
//
ASSERT(cnt == cntold);
//
// call the callback function once for each file, now that we've completed
// copying the files in the list. We pass the caller a structure which
// indicates the success of copying each individual file in the list.
//
for (fname = FileNames, sz=0; sz<cnt; sz++, fname += wcslen(fname) + 1) {
LPEXCEPTION_POINTERS ExceptionPointers = NULL;
try {
NTSTATUS Status;
BOOL b;
//
// don't use the (possibly reditected) file names returned from the server
//
Status = SfcAllocUnicodeStringFromPath(fname, &Path);
if(!NT_SUCCESS(Status))
{
rVal = RtlNtStatusToDosError(Status);
goto exit;
}
cs[sz].FileName = Path.Buffer;
b = SfcNotificationCallback( &cs[sz], Context );
MemFree(Path.Buffer);
RtlZeroMemory(&Path, sizeof(Path));
if (!b) {
//
// return FALSE if the callback fails for any reason
//
rVal = ERROR_CANCELLED;
goto exit;
}
} except (ExceptionPointers = GetExceptionInformation(),
EXCEPTION_EXECUTE_HANDLER) {
//
// we hit an exception calling the callback...return exception code
//
DebugPrint3( LVL_VERBOSE,
L"SIPF hit exception %x while calling callback routine %x at address %x\n",
ExceptionPointers->ExceptionRecord->ExceptionCode,
SfcNotificationCallback,
ExceptionPointers->ExceptionRecord->ExceptionAddress
);
rVal = RtlNtStatusToDosError(ExceptionPointers->ExceptionRecord->ExceptionCode);
goto exit;
}
}
exit:
MemFree(Path.Buffer);
if(cs != NULL)
{
midl_user_free( cs );
}
#ifndef _WIN64
MemFree(szTranslatedFiles);
#endif
SetLastError(rVal);
return rVal == ERROR_SUCCESS;
}
BOOL
WINAPI
SfcGetNextProtectedFile(
IN HANDLE RpcHandle,
IN PPROTECTED_FILE_DATA ProtFileData
)
/*++
Routine Description:
Routine to retrieve the next protected file in the list.
Arguments:
RpcHandle - RPC binding handle to the SFC server
ProtFileData - pointer to a PROTECTED_FILE_DATA structure to be filled
in by function.
Return Value:
TRUE for success, FALSE for failure. If there are no more files, the last
error code will be set to ERROR_NO_MORE_FILES.
--*/
{
DWORD rVal;
LPWSTR FileName = NULL;
DWORD FileNameSize = 0;
BOOL bReturn = FALSE;
DWORD FileNumber;
//
// validate parameters
//
if (ProtFileData == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return(FALSE);
}
try {
FileNumber = ProtFileData->FileNumber;
} except (EXCEPTION_EXECUTE_HANDLER) {
SetLastError(ERROR_INVALID_DATA);
return(FALSE);
}
//
// If this is not an internal client, then RpcHandle must be NULL.
//
if (InternalClient == FALSE) {
if (RpcHandle != NULL) {
SetLastError(ERROR_INVALID_HANDLE);
return(FALSE);
}
}
EnsureGoodConnectionHandleBool( RpcHandle );
//
// call the server API
//
rVal = SfcCli_GetNextProtectedFile(
RpcHandle,
FileNumber,
(LPBYTE*)&FileName,
&FileNameSize
);
if (rVal != ERROR_SUCCESS) {
SetLastError(rVal);
goto exit;
}
bReturn = TRUE;
//
// copy into the caller supplied buffer
//
try {
wcscpy( ProtFileData->FileName, FileName );
ProtFileData->FileNumber += 1;
} except (EXCEPTION_EXECUTE_HANDLER) {
SetLastError(RtlNtStatusToDosError(GetExceptionCode()));
bReturn = FALSE;
}
midl_user_free( FileName );
exit:
return(bReturn);
}
BOOL
WINAPI
SfcIsFileProtected(
IN HANDLE RpcHandle,
IN LPCWSTR ProtFileName
)
/*++
Routine Description:
Routine to determine if the specified file is protected.
Arguments:
RpcHandle - RPC binding handle to the SFC server
ProtFileName - NULL terminated unicode string indicating fully qualified
filename to query
Return Value:
TRUE if file is protected, FALSE if it isn't. last error code contains a
Win32 error code on failure.
--*/
{
DWORD rVal;
DWORD dwAttributes, dwSize;
WCHAR Buffer[MAX_PATH];
//
// parameter validation
//
if (ProtFileName == NULL) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
//
// if this is not an internal client, then RpcHandle must be NULL.
//
if (InternalClient == FALSE) {
if (RpcHandle != NULL) {
SetLastError(ERROR_INVALID_HANDLE);
return FALSE;
}
}
EnsureGoodConnectionHandleBool( RpcHandle );
//
// check whether this file is sxs-wfp first, which could be done on client-side only
//
//
// check whether it begins with "%SystemRoot%\\WinSxS\\"
//
dwSize = ExpandEnvironmentStrings( L"%SystemRoot%\\WinSxS\\", Buffer, UnicodeChars(Buffer));
if(0 == dwSize)
{
DebugPrint1( LVL_MINIMAL, L"SFC : ExpandEnvironmentStrings failed with lastError = 0x%x", GetLastError());
return FALSE;
}
--dwSize;
try {
if ((wcslen(ProtFileName) > dwSize) &&
(_wcsnicmp(Buffer, ProtFileName, dwSize) == 0)) // if they're equal, this could be a protected file
{
dwAttributes = GetFileAttributesW(ProtFileName);
if (dwAttributes == 0xFFFFFFFF)
return FALSE;
if (dwAttributes & FILE_ATTRIBUTE_DIRECTORY) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
return TRUE;
}
} except(EXCEPTION_EXECUTE_HANDLER) {
SetLastError(ERROR_INVALID_PARAMETER);
return FALSE;
}
//
// call server to determine if file is protected
//
rVal = SfcCli_IsFileProtected( RpcHandle, (PWSTR)ProtFileName );
if (rVal != ERROR_SUCCESS) {
SetLastError(rVal);
return FALSE;
}
return TRUE;
}