/*++ 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; szExceptionRecord->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; }