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.
 
 
 
 
 
 

2216 lines
55 KiB

/*++
Copyright (c) 1997 Microsoft Corporation
Module Name:
eclient.c
Abstract:
EFS RPC client code.
Author:
Robert Gu (RobertG) Aug, 1997
Environment:
Revision History:
--*/
#include <string.h>
#include <nt.h>
#include <ntrtl.h>
#include <nturtl.h>
#include <windows.h>
#include <ntrpcp.h> // prototypes for MIDL user functions
#include <wincrypt.h>
#include <efsrpc.h>
#include <efsstruc.h>
#include <dfsfsctl.h>
#include <rpcasync.h>
#define DAVHEADER 0x01
//
// Internal prototypes
//
void __RPC_FAR
EfsPipeAlloc(
char __RPC_FAR * State,
unsigned long ReqSize,
unsigned char __RPC_FAR * __RPC_FAR * Buf,
unsigned long __RPC_FAR * RealSize
);
void __RPC_FAR
EfsPipeRead (
char __RPC_FAR * State,
unsigned char __RPC_FAR * DataBuf,
unsigned long ByteCount
);
void __RPC_FAR
EfsPipeWrite (
char __RPC_FAR * State,
unsigned char __RPC_FAR * DataBuf,
unsigned long ByteRequested,
unsigned long *ByteFromCaller
);
DWORD
GetFullName(
LPCWSTR FileName,
LPWSTR *FullName,
LPWSTR *ServerName,
ULONG Flags,
DWORD *dwCreationDistribution,
DWORD dwAttributes,
PSECURITY_DESCRIPTOR pRelativeSD,
BOOL bInheritHandle
);
DWORD
EnablePrivilege(
ULONG Flags,
HANDLE *TokenHandle,
PTOKEN_PRIVILEGES *OldPrivs
);
VOID
RestorePrivilege(
HANDLE *TokenHandle,
PTOKEN_PRIVILEGES *OldPrivs
);
DWORD
EnablePrivilege(
ULONG Flags,
HANDLE *TokenHandle,
PTOKEN_PRIVILEGES *OldPrivs
)
{
TOKEN_PRIVILEGES Privs;
DWORD RetCode = ERROR_SUCCESS;
BOOL b;
DWORD ReturnLength;
*TokenHandle = NULL;
*OldPrivs = NULL;
*OldPrivs = ( TOKEN_PRIVILEGES *) RtlAllocateHeap(
RtlProcessHeap(),
0,
sizeof( TOKEN_PRIVILEGES )
);
if ( *OldPrivs == NULL ){
return ERROR_NOT_ENOUGH_MEMORY;
}
//
// We're impersonating, use the thread token.
//
b = OpenThreadToken(
GetCurrentThread(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
FALSE,
TokenHandle
);
if (!b) {
b = OpenProcessToken(
GetCurrentProcess(),
TOKEN_ADJUST_PRIVILEGES | TOKEN_QUERY,
TokenHandle
);
}
if ( b ) {
//
// We've got a token handle
//
//
// If we're doing a create for import, enable restore privilege,
// otherwise enable backup privilege.
//
Privs.PrivilegeCount = 1;
Privs.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
if ( !(Flags & CREATE_FOR_IMPORT) ){
Privs.Privileges[0].Luid = RtlConvertLongToLuid(SE_BACKUP_PRIVILEGE);
} else {
Privs.Privileges[0].Luid = RtlConvertLongToLuid(SE_RESTORE_PRIVILEGE);
}
ReturnLength = sizeof( TOKEN_PRIVILEGES );
(VOID) AdjustTokenPrivileges (
*TokenHandle,
FALSE,
&Privs,
sizeof( TOKEN_PRIVILEGES ),
*OldPrivs,
&ReturnLength
);
if ( ERROR_SUCCESS != (RetCode = GetLastError()) ) {
//
// Privilege adjust failed
//
CloseHandle( *TokenHandle );
*TokenHandle = NULL;
RtlFreeHeap( RtlProcessHeap(), 0, *OldPrivs );
*OldPrivs = NULL;
}
} else {
*TokenHandle = NULL;
RtlFreeHeap( RtlProcessHeap(), 0, *OldPrivs );
*OldPrivs = NULL;
}
return RetCode;
}
VOID
RestorePrivilege(
HANDLE *TokenHandle,
PTOKEN_PRIVILEGES *OldPrivs
)
{
if (!TokenHandle || !OldPrivs || !(*TokenHandle) || !(*OldPrivs)) {
return;
}
(VOID) AdjustTokenPrivileges (
*TokenHandle,
FALSE,
*OldPrivs,
0,
NULL,
NULL
);
CloseHandle( *TokenHandle );
*TokenHandle = 0;
RtlFreeHeap( RtlProcessHeap(), 0, *OldPrivs );
*OldPrivs = NULL;
}
DWORD
EfsOpenFileRawRPCClient(
IN LPCWSTR FileName,
IN ULONG Flags,
OUT PVOID * Context
)
/*++
Routine Description:
This routine is the client side of EfsOpenFileRaw. It establishes the
connection to the server. And then call the server to finish the task.
Arguments:
FileName -- File name of the file to be exported
Flags -- Indicating if open for export or import; for directory or file.
Context - Export context to be used by READ operation later. Caller should
pass this back in ReadRaw().
Return Value:
Result of the operation.
--*/
{
DWORD RetCode;
handle_t binding_h;
NTSTATUS Status;
PEXIMPORT_CONTEXT_HANDLE RawContext;
LPWSTR FullName;
LPWSTR Server;
HANDLE TokenHandle;
PTOKEN_PRIVILEGES OldPrivs;
*Context = NULL;
RetCode = GetFullName(
FileName,
&FullName,
&Server,
Flags,
NULL,
0,
NULL,
FALSE
);
if ( RetCode == ERROR_SUCCESS ){
(VOID) EnablePrivilege(
Flags,
&TokenHandle,
&OldPrivs
);
Status = RpcpBindRpc (
Server,
L"lsarpc",
L"security=Impersonation static true",
&binding_h
);
if (NT_SUCCESS(Status)){
RpcTryExcept {
RetCode = EfsRpcOpenFileRaw(
binding_h,
&RawContext,
FullName,
Flags
);
if ( ERROR_SUCCESS == RetCode ){
//
// Send the context handle back to the user
//
if (RawContext) {
*Context = (PVOID) RawContext;
} else {
//
// The server is hacked?
//
RetCode = ERROR_DEV_NOT_EXIST;
}
}
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
RetCode = RpcExceptionCode();
} RpcEndExcept;
//
// Free the binding handle
//
RpcpUnbindRpc( binding_h );
} else {
RetCode = RtlNtStatusToDosError( Status );
}
RestorePrivilege(
&TokenHandle,
&OldPrivs
);
RtlFreeHeap( RtlProcessHeap(), 0, FullName );
RtlFreeHeap( RtlProcessHeap(), 0, Server );
}
return RetCode;
}
VOID
EfsCloseFileRawRPCClient(
IN PVOID Context
)
/*++
Routine Description:
This routine is the client side of EfsCloseFileRaw.
Arguments:
Context - Export/Import context used by READ/WRITE raw data.
Return Value:
None.
--*/
{
PEXIMPORT_CONTEXT_HANDLE phContext;
phContext = (PEXIMPORT_CONTEXT_HANDLE) Context;
RpcTryExcept {
EfsRpcCloseRaw(
&phContext
);
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
} RpcEndExcept;
}
DWORD
EfsReadFileRawRPCClient(
IN PFE_EXPORT_FUNC ExportCallback,
IN PVOID CallbackContext,
IN PVOID Context
)
/*++
Routine Description:
This routine is the client side of EfsReadFileRaw.
Arguments:
ExportCallback - Caller provided callback function.
CallbackContext - Caller's context.
Context - Export context used by READ raw data.
Return Value:
None.
*/
{
PEXIMPORT_CONTEXT_HANDLE phContext;
EFS_EXIM_STATE Pipe_State;
EFS_EXIM_PIPE ExportPipe;
DWORD RetCode;
if ( NULL == Context){
return ERROR_ACCESS_DENIED;
}
phContext = ( PEXIMPORT_CONTEXT_HANDLE ) Context;
//
// Try to allocate a reasonable size buffer. The size can be fine tuned later, but should
// at least one page plus 4K. FSCTL_OUTPUT_LESS_LENGTH should be n * page size.
// FSCTL_OUTPUT_MIN_LENGTH can be fine tuned later. It should be at least one page
// plus 4K.
//
Pipe_State.BufLength = FSCTL_OUTPUT_INITIAL_LENGTH;
Pipe_State.WorkBuf = NULL;
while ( !Pipe_State.WorkBuf &&
(Pipe_State.BufLength >= FSCTL_OUTPUT_MIN_LENGTH)
){
Pipe_State.WorkBuf = RtlAllocateHeap(
RtlProcessHeap(),
0,
Pipe_State.BufLength
);
if ( !Pipe_State.WorkBuf ){
//
// Memory allocation failed.
// Try smaller allocation.
//
Pipe_State.BufLength -= FSCTL_OUTPUT_LESS_LENGTH;
}
}
if (!Pipe_State.WorkBuf){
return ERROR_OUTOFMEMORY;
}
Pipe_State.ExImCallback = (PVOID) ExportCallback;
Pipe_State.CallbackContext = CallbackContext;
Pipe_State.Status = NO_ERROR;
ExportPipe.state = (char *) &Pipe_State;
ExportPipe.alloc = EfsPipeAlloc;
ExportPipe.pull = NULL;
ExportPipe.push = EfsPipeRead;
RpcTryExcept{
RetCode = EfsRpcReadFileRaw(
phContext,
&ExportPipe
);
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
if ( NO_ERROR == Pipe_State.Status ){
RetCode = RpcExceptionCode();
} else {
RetCode = Pipe_State.Status;
}
} RpcEndExcept;
RtlFreeHeap( RtlProcessHeap(), 0, Pipe_State.WorkBuf );
return RetCode;
}
DWORD
EfsWriteFileRawRPCClient(
IN PFE_IMPORT_FUNC ImportCallback,
IN PVOID CallbackContext,
IN PVOID Context
)
/*++
Routine Description:
This routine is the client side of EfsWriteFileRaw.
Arguments:
ImportCallback - Caller provided callback function.
CallbackContext - Caller's context.
Context - Import context used by WRITE raw data.
Return Value:
None.
*/
{
PEXIMPORT_CONTEXT_HANDLE phContext;
EFS_EXIM_STATE Pipe_State;
EFS_EXIM_PIPE ImportPipe;
DWORD RetCode;
HANDLE TokenHandle;
PTOKEN_PRIVILEGES OldPrivs;
if ( NULL == Context){
return ERROR_ACCESS_DENIED;
}
phContext = ( PEXIMPORT_CONTEXT_HANDLE ) Context;
//
// Try to allocate a reasonable size buffer. The size can be fine tuned later, but should
// at least one page plus 4K. FSCTL_OUTPUT_LESS_LENGTH should be n * page size.
// FSCTL_OUTPUT_MIN_LENGTH can be fine tuned later. It should be at least one page
// plus 4K.
//
Pipe_State.BufLength = FSCTL_OUTPUT_INITIAL_LENGTH;
Pipe_State.WorkBuf = RtlAllocateHeap(
RtlProcessHeap(),
0,
Pipe_State.BufLength
);
if (!Pipe_State.WorkBuf){
return ERROR_OUTOFMEMORY;
}
Pipe_State.ExImCallback = (PVOID) ImportCallback;
Pipe_State.CallbackContext = CallbackContext;
Pipe_State.Status = NO_ERROR;
ImportPipe.state = (char *) &Pipe_State;
ImportPipe.alloc = EfsPipeAlloc;
ImportPipe.pull = EfsPipeWrite;
ImportPipe.push = NULL;
(VOID) EnablePrivilege(
CREATE_FOR_IMPORT,
&TokenHandle,
&OldPrivs
);
RpcTryExcept{
RetCode = EfsRpcWriteFileRaw(
phContext,
&ImportPipe
);
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
if ( NO_ERROR == Pipe_State.Status ){
RetCode = RpcExceptionCode();
} else {
RetCode = Pipe_State.Status;
}
} RpcEndExcept;
RestorePrivilege(
&TokenHandle,
&OldPrivs
);
RtlFreeHeap( RtlProcessHeap(), 0, Pipe_State.WorkBuf );
return RetCode;
}
void __RPC_FAR
EfsPipeAlloc(
char __RPC_FAR * State,
unsigned long ReqSize,
unsigned char __RPC_FAR * __RPC_FAR * Buf,
unsigned long __RPC_FAR * RealSize
)
/*++
Routine Description:
This routine is required by the RPC pipe. It allocates the memory
for the push and pull routines.
Arguments:
State - Pipe status.
ReqSize - Required buffer sixe in bytes.
Buf - Buffer pointer.
RealSize - Size of allocated buffer in bytes.
Return Value:
None.
*/
{
PEFS_EXIM_STATE Pipe_State = (PEFS_EXIM_STATE) State;
//
// If error had occured, this is the chance to tell the RPC LIB to
// stop the pipe work.
//
if ( NO_ERROR != Pipe_State->Status){
*RealSize = 0;
*Buf = NULL;
} else {
if ( ReqSize > Pipe_State->BufLength ){
*RealSize = Pipe_State->BufLength;
} else {
*RealSize = ReqSize;
}
*Buf = Pipe_State->WorkBuf;
}
}
void __RPC_FAR
EfsPipeRead (
char __RPC_FAR * State,
unsigned char __RPC_FAR * DataBuf,
unsigned long ByteCount
)
/*++
Routine Description:
This routine is called by the RPC pipe. It send the exported data to the caller.
Arguments:
State - Pipe status.
DataBuf - Buffer pointer.
ByteCount - Number of bytes to be sent out.
Return Value:
None.
*/
{
DWORD HResult;
PEFS_EXIM_STATE Pipe_State = (PEFS_EXIM_STATE) State;
PFE_EXPORT_FUNC ExportCallback;
PVOID CallbackContext;
ExportCallback = Pipe_State->ExImCallback;
CallbackContext = Pipe_State->CallbackContext;
HResult = (*ExportCallback)( DataBuf, CallbackContext, ByteCount);
if ( NO_ERROR != HResult ){
Pipe_State->Status = HResult;
}
}
void __RPC_FAR
EfsPipeWrite (
char __RPC_FAR * State,
unsigned char __RPC_FAR * DataBuf,
unsigned long ByteRequested,
unsigned long *ByteFromCaller
)
/*++
Routine Description:
This routine is called by the RPC pipe. It requests the imported data from the caller.
Arguments:
State - Pipe status.
DataBuf - Buffer pointer.
ByteRequested - Number of bytes requested to write to the pipe.
ByteFromCaller - Number of bytes available for writing to the pipe.
Return Value:
None.
*/
{
DWORD HResult;
PEFS_EXIM_STATE Pipe_State = (PEFS_EXIM_STATE) State;
PFE_IMPORT_FUNC ImportCallback;
PVOID CallbackContext;
ImportCallback = Pipe_State->ExImCallback;
CallbackContext = Pipe_State->CallbackContext;
*ByteFromCaller = ByteRequested;
HResult = (*ImportCallback)( DataBuf, CallbackContext, ByteFromCaller);
if ( NO_ERROR != HResult ){
Pipe_State->Status = HResult;
}
}
DWORD
EfsEncryptFileRPCClient(
UNICODE_STRING *FullFileNameU
)
/*++
Routine Description:
This routine is the client side of Encryption API. It establishes the
connection to the server. And then call the server to finish the task.
Arguments:
FullFileNameU - Supplies the name of the file to be encrypted.
Return Value:
ERROR_SUCCESS on success, other on failure.
--*/
{
DWORD RetCode;
handle_t binding_h;
NTSTATUS Status;
LPWSTR FullName;
LPWSTR Server;
RetCode = GetFullName(
FullFileNameU->Buffer,
&FullName,
&Server,
0,
NULL,
0,
NULL,
FALSE
);
if ( RetCode == ERROR_SUCCESS ){
Status = RpcpBindRpc (
Server,
L"lsarpc",
0,
&binding_h
);
if (NT_SUCCESS(Status)){
RpcTryExcept {
RetCode = EfsRpcEncryptFileSrv(
binding_h,
FullName
);
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
RetCode = RpcExceptionCode();
} RpcEndExcept;
//
// Free the binding handle
//
RpcpUnbindRpc( binding_h );
} else {
RetCode = RtlNtStatusToDosError( Status );
}
RtlFreeHeap( RtlProcessHeap(), 0, FullName );
RtlFreeHeap( RtlProcessHeap(), 0, Server );
}
return RetCode;
}
DWORD
EfsDecryptFileRPCClient(
UNICODE_STRING *FullFileNameU,
DWORD dwRecovery
)
/*++
Routine Description:
This routine is the client side of Decryption API. It establishes the
connection to the server. And then call the server to finish the task.
Arguments:
FullFileNameU - Supplies the name of the file to be encrypted.
Return Value:
ERROR_SUCCESS on success, other on failure.
--*/
{
DWORD RetCode;
handle_t binding_h;
NTSTATUS Status;
LPWSTR FullName;
LPWSTR Server;
RetCode = GetFullName(
FullFileNameU->Buffer,
&FullName,
&Server,
0,
NULL,
0,
NULL,
FALSE
);
if ( RetCode == ERROR_SUCCESS ){
Status = RpcpBindRpc (
Server,
L"lsarpc",
0,
&binding_h
);
if (NT_SUCCESS(Status)){
RpcTryExcept {
RetCode = EfsRpcDecryptFileSrv(
binding_h,
FullName,
dwRecovery
);
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
RetCode = RpcExceptionCode();
} RpcEndExcept;
//
// Free the binding handle
//
RpcpUnbindRpc( binding_h );
} else {
RetCode = RtlNtStatusToDosError( Status );
}
RtlFreeHeap( RtlProcessHeap(), 0, FullName );
RtlFreeHeap( RtlProcessHeap(), 0, Server );
}
return RetCode;
}
DWORD
GetFullName(
LPCWSTR FileName,
LPWSTR *FullName,
LPWSTR *ServerName,
ULONG Flags,
DWORD *dwCreationDistribution,
DWORD dwAttributes,
PSECURITY_DESCRIPTOR pRelativeSD,
BOOL bInheritHandle
)
/*++
Routine Description:
This routine will extract the server name and the file UNC name from the
passed in file name.
Arguments:
FileName - Supplies the name of the file to be parsed.
FullName - File name used on the server.
ServerName - The server machine name where the file lives.
Flags - Indicates if the object is a directory or a file. CREATE_FOR_DIR for directory.
dwCreationDistribution - How the file should be created.
dwAtrributes - The attributes for creating a new object.
pRelativeSD - Security Descriptor.
bInheritHandle - If the file to be created should inherit the security.
Return Value:
ERROR_SUCCESS on success, other on failure.
--*/
{
HANDLE FileHdl = 0;
HANDLE DriverHandle;
UNICODE_STRING DfsDriverName;
DWORD RetCode = ERROR_SUCCESS;
LPWSTR TmpFullName;
DWORD FullNameLength;
DWORD FileNameLength;
OBJECT_ATTRIBUTES Obja;
IO_STATUS_BLOCK IoStatusBlock;
UNICODE_STRING FileNtName;
NTSTATUS NtStatus;
BOOL b = TRUE;
DWORD FileAttributes = 0;
DWORD CreationDistribution = 0;
DWORD CreateOptions = 0;
ULONG ii, jj;
BOOL GotRoot;
WCHAR *PathName;
UINT DriveType;
PFILE_NAME_INFORMATION FileNameInfo;
WCHAR WorkBuffer[MAX_PATH+4];
DWORD BufSize;
DWORD BufferLength;
NETRESOURCEW RemotePathResource;
NETRESOURCEW *pNetInfo;
FileNameLength = wcslen(FileName);
BufferLength = (FileNameLength + 1) <= MAX_PATH ?
(MAX_PATH + 1) * sizeof(WCHAR) : (FileNameLength + 1) * sizeof (WCHAR);
PathName = (WCHAR *) RtlAllocateHeap(
RtlProcessHeap(),
0,
BufferLength
);
if ( !PathName ) {
return ERROR_NOT_ENOUGH_MEMORY;
}
GotRoot = GetVolumePathNameW(
FileName,
PathName,
BufferLength / sizeof(WCHAR)
);
if (!GotRoot) {
RetCode = GetLastError();
RtlFreeHeap( RtlProcessHeap(), 0, PathName );
return RetCode;
}
DriveType = GetDriveTypeW(PathName);
RtlFreeHeap( RtlProcessHeap(), 0, PathName );
if (DriveType == DRIVE_REMOTE){
if ((Flags & CREATE_FOR_IMPORT) || (dwAttributes !=0) ) {
//
// Called from OpenRaw or DuplicateInfo.
// Use NtCreateFile()
//
FileAttributes = GetFileAttributesW( FileName );
if (dwAttributes) {
//
// From dup
//
if (-1 != FileAttributes) {
//
// File existed
//
if ( dwCreationDistribution && (*dwCreationDistribution == CREATE_NEW) ){
return ERROR_FILE_EXISTS;
}
CreationDistribution = FILE_OPEN;
if (FileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
if ((Flags & CREATE_FOR_DIR) == 0) {
return ERROR_DS_SRC_AND_DST_OBJECT_CLASS_MISMATCH;
}
CreateOptions |= FILE_DIRECTORY_FILE;
}
} else {
//
// Destination not existing
//
CreationDistribution = FILE_CREATE;
if (dwCreationDistribution && (*dwCreationDistribution == CREATE_NEW) ) {
*dwCreationDistribution = CREATE_ALWAYS;
}
if (Flags & CREATE_FOR_DIR) {
CreateOptions |= FILE_DIRECTORY_FILE;
dwAttributes |= FILE_ATTRIBUTE_DIRECTORY;
} else {
CreateOptions |= FILE_NO_COMPRESSION;
}
}
} else {
//
// From OpenRaw import
//
dwAttributes = FILE_ATTRIBUTE_NORMAL;
CreateOptions = FILE_OPEN_FOR_BACKUP_INTENT;
if (-1 == FileAttributes) {
CreationDistribution = FILE_CREATE;
if (Flags & CREATE_FOR_DIR) {
CreateOptions |= FILE_DIRECTORY_FILE;
dwAttributes |= FILE_ATTRIBUTE_DIRECTORY;
} else {
CreateOptions |= FILE_NO_COMPRESSION;
}
} else {
//
// File already existing
//
CreationDistribution = FILE_OPEN;
if (Flags & CREATE_FOR_DIR) {
CreateOptions |= FILE_DIRECTORY_FILE;
dwAttributes |= FILE_ATTRIBUTE_DIRECTORY;
}
}
}
RtlInitUnicodeString(
&FileNtName,
NULL
);
b = RtlDosPathNameToNtPathName_U(
FileName,
&FileNtName,
NULL,
NULL
);
if (b) {
dwAttributes &= ~(FILE_ATTRIBUTE_ENCRYPTED | FILE_ATTRIBUTE_READONLY);
InitializeObjectAttributes(
&Obja,
&FileNtName,
bInheritHandle ? OBJ_INHERIT | OBJ_CASE_INSENSITIVE : OBJ_CASE_INSENSITIVE,
0,
pRelativeSD? ((PEFS_RPC_BLOB)pRelativeSD)->pbData:NULL
);
NtStatus = NtCreateFile(
&FileHdl,
FILE_WRITE_ATTRIBUTES | SYNCHRONIZE,
&Obja,
&IoStatusBlock,
NULL,
dwAttributes,
0,
CreationDistribution,
CreateOptions,
NULL,
0
);
RtlFreeHeap(
RtlProcessHeap(),
0,
FileNtName.Buffer
);
if (!NT_SUCCESS(NtStatus)) {
return (RtlNtStatusToDosError( NtStatus ));
}
} else {
return ERROR_PATH_NOT_FOUND;
}
} else {
FileHdl = CreateFileW(
FileName,
FILE_READ_ATTRIBUTES,
FILE_SHARE_READ,
NULL,
OPEN_EXISTING,
FILE_FLAG_BACKUP_SEMANTICS,
NULL
);
if (INVALID_HANDLE_VALUE == FileHdl) {
RetCode = GetLastError();
return RetCode;
}
}
FileNameInfo = (PFILE_NAME_INFORMATION) WorkBuffer;
BufSize = sizeof (WorkBuffer);
do {
NtStatus = NtQueryInformationFile(
FileHdl,
&IoStatusBlock,
FileNameInfo,
BufSize,
FileNameInformation
);
if ( NtStatus == STATUS_BUFFER_OVERFLOW || NtStatus == STATUS_BUFFER_TOO_SMALL ) {
BufSize *= 2;
if (FileNameInfo != (PFILE_NAME_INFORMATION)WorkBuffer) {
RtlFreeHeap( RtlProcessHeap(), 0, FileNameInfo );
}
FileNameInfo = (PFILE_NAME_INFORMATION) RtlAllocateHeap(
RtlProcessHeap(),
0,
BufSize
);
if (!FileNameInfo) {
CloseHandle(FileHdl);
return ERROR_NOT_ENOUGH_MEMORY;
}
}
} while (NtStatus == STATUS_BUFFER_OVERFLOW || NtStatus == STATUS_BUFFER_TOO_SMALL);
CloseHandle(FileHdl);
if (!NT_SUCCESS(NtStatus)) {
if (FileNameInfo != (PFILE_NAME_INFORMATION)WorkBuffer) {
RtlFreeHeap( RtlProcessHeap(), 0, FileNameInfo );
}
return RtlNtStatusToDosError(NtStatus);
} else {
ASSERT((FileNameInfo->FileName)[ 0 ] == L'\\');
}
//
// We got the UNC name
//
*FullName = RtlAllocateHeap(
RtlProcessHeap(),
0,
FileNameInfo->FileNameLength+2*sizeof (WCHAR)
);
*ServerName = RtlAllocateHeap(
RtlProcessHeap(),
0,
( MAX_PATH + 1) * sizeof (WCHAR)
);
if ( (NULL == *FullName) || (NULL == *ServerName) ){
if ( *FullName ){
RtlFreeHeap( RtlProcessHeap(), 0, *FullName );
*FullName = NULL;
}
if ( *ServerName ){
RtlFreeHeap( RtlProcessHeap(), 0, *ServerName );
*ServerName = NULL;
}
if (FileNameInfo != (PFILE_NAME_INFORMATION)WorkBuffer) {
RtlFreeHeap( RtlProcessHeap(), 0, FileNameInfo );
}
return ERROR_NOT_ENOUGH_MEMORY;
}
} else {
//
// The path is local
//
*FullName = RtlAllocateHeap(
RtlProcessHeap(),
0,
(FileNameLength + 1) * sizeof (WCHAR)
);
*ServerName = RtlAllocateHeap(
RtlProcessHeap(),
0,
8 * sizeof (WCHAR)
);
//
// Use . for local case.
//
if ( (NULL == *FullName) || (NULL == *ServerName) ){
if ( *FullName ){
RtlFreeHeap( RtlProcessHeap(), 0, *FullName );
*FullName = NULL;
}
if ( *ServerName ){
RtlFreeHeap( RtlProcessHeap(), 0, *ServerName );
*ServerName = NULL;
}
return ERROR_NOT_ENOUGH_MEMORY;
}
wcscpy ( *ServerName, L".");
wcscpy ( *FullName, FileName);
return ERROR_SUCCESS;
}
//
// Let's get the UNC server and path name
//
FullNameLength = FileNameInfo->FileNameLength;
ii = jj = 0;
while ( (FileNameInfo->FileName)[ jj ] == L'\\' ) {
jj ++;
}
while ( jj < FullNameLength/sizeof(WCHAR) && ((FileNameInfo->FileName)[ jj ] != L'\\') ){
(*ServerName)[ii++] = (FileNameInfo->FileName)[ jj++ ];
}
(*ServerName)[ii] = 0;
if (FileNameInfo->FileName[0] == L'\\' && FileNameInfo->FileName[1] != L'\\' ) {
//
// NtQueryInformationFile returns \server\share\...
//
(*FullName)[0] = L'\\';
wcsncpy( &((*FullName)[1]), &FileNameInfo->FileName[0], FullNameLength/sizeof(WCHAR) );
(*FullName)[1+FullNameLength/sizeof(WCHAR)] = 0;
} else{
//
// Just in case we get \\server\share\...
//
wcsncpy( &((*FullName)[0]), &FileNameInfo->FileName[0], FullNameLength/sizeof(WCHAR) );
(*FullName)[FullNameLength/sizeof(WCHAR)] = 0;
}
//
// WorkBuffer is freed here. It can be reused from here.
//
if (FileNameInfo != (PFILE_NAME_INFORMATION)WorkBuffer) {
RtlFreeHeap( RtlProcessHeap(), 0, FileNameInfo );
}
//
// This is a workaround to test DFS path.
// Let's see if the path could be a DFS path or not
//
RtlInitUnicodeString(&DfsDriverName, DFS_DRIVER_NAME);
InitializeObjectAttributes(
&Obja,
&DfsDriverName,
OBJ_CASE_INSENSITIVE,
NULL,
NULL
);
NtStatus = NtCreateFile(
&DriverHandle,
SYNCHRONIZE,
&Obja,
&IoStatusBlock,
NULL,
FILE_ATTRIBUTE_NORMAL,
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
FILE_OPEN_IF,
FILE_CREATE_TREE_CONNECTION | FILE_SYNCHRONOUS_IO_NONALERT,
NULL,
0
);
if ( NT_SUCCESS( NtStatus ) ){
//
// DfsDriver opened successfully
//
TmpFullName = RtlAllocateHeap(
RtlProcessHeap(),
0,
FullNameLength + 2*sizeof (WCHAR)
);
if (TmpFullName) {
NtStatus = NtFsControlFile(
DriverHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
FSCTL_DFS_GET_SERVER_NAME,
*FullName,
FullNameLength + 2*sizeof (WCHAR) ,
TmpFullName,
FullNameLength + 2* sizeof (WCHAR)
);
if ( STATUS_BUFFER_OVERFLOW == NtStatus ){
ULONG OldFullNameLength = FullNameLength;
FullNameLength = *(ULONG *)TmpFullName + sizeof (WCHAR);
RtlFreeHeap( RtlProcessHeap(), 0, TmpFullName );
TmpFullName = RtlAllocateHeap(
RtlProcessHeap(),
0,
FullNameLength
);
if (NULL == TmpFullName){
//
// Remember this is just a workaround.
// Let's assume this is not DFS path. If it is, it will fail later anyway.
//
NtClose( DriverHandle );
DriverHandle = NULL;
} else {
NtStatus = NtFsControlFile(
DriverHandle,
NULL,
NULL,
NULL,
&IoStatusBlock,
FSCTL_DFS_GET_SERVER_NAME,
*FullName,
OldFullNameLength + 2*sizeof (WCHAR) ,
TmpFullName,
FullNameLength
);
}
}
if (TmpFullName) {
if ( NT_SUCCESS( NtStatus ) ){
//
// The name is a DFS file name. Use the name in the TmpFullName
//
RtlFreeHeap( RtlProcessHeap(), 0, *FullName );
*FullName = TmpFullName;
//
// Reset the server name
//
ii = jj = 0;
while ( (*FullName)[ jj ] == L'\\' ) {
jj ++;
}
while ( ((*FullName)[ jj ]) && ((*FullName)[ jj ] != L'\\') ){
(*ServerName)[ii++] = (*FullName)[ jj++ ];
}
(*ServerName)[ii] = 0;
} else {
//
// Not a DFS name
//
RtlFreeHeap( RtlProcessHeap(), 0, TmpFullName );
}
}
}
if (DriverHandle) {
NtClose( DriverHandle );
}
}
//
// Let's see if the path is a WEB DAV path or not
//
BufSize = 1024; //If not enough, we will allocate more
pNetInfo = (NETRESOURCEW *) RtlAllocateHeap(
RtlProcessHeap(),
0,
BufSize
);
//
// If we can't decide if the path is WEBDAV path, we assume not.
// Error will be returned later if it turns out to be a WEBDAV Share.
//
if (pNetInfo) {
LPWSTR lpSysName;
RemotePathResource.dwScope = RESOURCE_CONNECTED;
RemotePathResource.dwType = RESOURCETYPE_DISK;
RemotePathResource.dwDisplayType = RESOURCEDISPLAYTYPE_GENERIC;
RemotePathResource.dwUsage = 0;
RemotePathResource.lpLocalName = NULL;
RemotePathResource.lpRemoteName = *FullName;
RemotePathResource.lpComment = NULL;
RemotePathResource.lpProvider = NULL;
RetCode = WNetGetResourceInformationW (
&RemotePathResource, // network resource
(LPVOID) pNetInfo, // information buffer
(LPDWORD) &BufSize, // size of information buffer
&lpSysName
);
if (RetCode == ERROR_MORE_DATA) {
//
// This is not likely to happen
//
RtlFreeHeap( RtlProcessHeap(), 0, pNetInfo );
pNetInfo = (NETRESOURCEW *) RtlAllocateHeap(
RtlProcessHeap(),
0,
BufSize
);
if (pNetInfo) {
RetCode = WNetGetResourceInformationW (
&RemotePathResource, // network resource
(LPVOID) pNetInfo, // information buffer
(LPDWORD) &BufSize, // size of information buffer
&lpSysName
);
} else {
RetCode = ERROR_NOT_ENOUGH_MEMORY;
}
}
if (ERROR_SUCCESS == RetCode) {
WCHAR *WebDavPath;
DWORD DavNameLength;
WebDavPath = WorkBuffer;
DavNameLength = sizeof(WorkBuffer) / sizeof (WCHAR);
RetCode = WNetGetProviderNameW(
WNNC_NET_DAV,
WebDavPath,
&DavNameLength
);
if (ERROR_SUCCESS != RetCode) {
if ( ERROR_MORE_DATA == RetCode) {
WebDavPath = RtlAllocateHeap(
RtlProcessHeap(),
0,
DavNameLength * sizeof (WCHAR)
);
if (WebDavPath) {
RetCode = WNetGetProviderNameW(
WNNC_NET_DAV,
WebDavPath,
&DavNameLength
);
} else {
RetCode = ERROR_NOT_ENOUGH_MEMORY;
}
}
}
//
// Check to see if the provider is WEBDAV
//
if ((ERROR_SUCCESS == RetCode) && !wcscmp(WebDavPath, pNetInfo->lpProvider)){
//
// This is the WEBDAV. Let's redo the name.
//
RtlFreeHeap( RtlProcessHeap(), 0, pNetInfo );
RtlFreeHeap( RtlProcessHeap(), 0, *FullName );
if (WebDavPath && (WebDavPath != WorkBuffer)) {
RtlFreeHeap( RtlProcessHeap(), 0, WebDavPath );
}
*FullName = RtlAllocateHeap(
RtlProcessHeap(),
0,
(FileNameLength + 3) * sizeof (WCHAR)
);
//
// Use . for local case.
//
if (*FullName) {
wcscpy ( *ServerName, L".");
(*FullName)[0] = DAVHEADER;
(*FullName)[1] = 0;
wcscat ( *FullName, FileName);
return ERROR_SUCCESS;
} else {
//
// Out of memory
//
RtlFreeHeap( RtlProcessHeap(), 0, *ServerName );
*ServerName = NULL;
return ERROR_NOT_ENOUGH_MEMORY;
}
}
if (WebDavPath && (WebDavPath != WorkBuffer)) {
RtlFreeHeap( RtlProcessHeap(), 0, WebDavPath );
}
}
if (pNetInfo) {
RtlFreeHeap( RtlProcessHeap(), 0, pNetInfo );
}
}
return ERROR_SUCCESS;
}
//
// Beta 2 API
//
DWORD
EfsAddUsersRPCClient(
IN LPCWSTR lpFileName,
IN PENCRYPTION_CERTIFICATE_LIST pEncryptionCertificates
)
/*++
Routine Description:
description-of-function.
Arguments:
argument-name - Supplies | Returns description of argument.
.
.
Return Value:
return-value - Description of conditions needed to return value. - or -
None.
--*/
{
DWORD RetCode;
handle_t binding_h;
NTSTATUS Status;
LPWSTR FullName;
LPWSTR Server;
RetCode = GetFullName(
lpFileName,
&FullName,
&Server,
0,
NULL,
0,
NULL,
FALSE
);
if ( RetCode == ERROR_SUCCESS ) {
Status = RpcpBindRpc (
Server,
L"lsarpc",
0,
&binding_h
);
if (NT_SUCCESS(Status)){
RpcTryExcept {
RetCode = EfsRpcAddUsersToFile(
binding_h,
FullName,
pEncryptionCertificates
);
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
RetCode = RpcExceptionCode();
} RpcEndExcept;
//
// Free the binding handle
//
RpcpUnbindRpc( binding_h );
} else {
RetCode = RtlNtStatusToDosError( Status );
}
RtlFreeHeap( RtlProcessHeap(), 0, FullName );
RtlFreeHeap( RtlProcessHeap(), 0, Server );
}
return RetCode;
}
DWORD
EfsRemoveUsersRPCClient(
IN LPCWSTR lpFileName,
IN PENCRYPTION_CERTIFICATE_HASH_LIST pHashes
)
/*++
Routine Description:
description-of-function.
Arguments:
argument-name - Supplies | Returns description of argument.
.
.
Return Value:
return-value - Description of conditions needed to return value. - or -
None.
--*/
{
DWORD RetCode;
handle_t binding_h;
NTSTATUS Status;
LPWSTR FullName;
LPWSTR Server;
RetCode = GetFullName(
lpFileName,
&FullName,
&Server,
0,
NULL,
0,
NULL,
FALSE
);
if ( RetCode == ERROR_SUCCESS ){
Status = RpcpBindRpc (
Server,
L"lsarpc",
0,
&binding_h
);
if (NT_SUCCESS(Status)){
RpcTryExcept {
RetCode = EfsRpcRemoveUsersFromFile(
binding_h,
FullName,
pHashes
);
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
RetCode = RpcExceptionCode();
} RpcEndExcept;
//
// Free the binding handle
//
RpcpUnbindRpc( binding_h );
} else {
RetCode = RtlNtStatusToDosError( Status );
}
RtlFreeHeap( RtlProcessHeap(), 0, FullName );
RtlFreeHeap( RtlProcessHeap(), 0, Server );
}
return RetCode;
}
DWORD
EfsQueryRecoveryAgentsRPCClient(
IN LPCWSTR lpFileName,
OUT PENCRYPTION_CERTIFICATE_HASH_LIST * pRecoveryAgents
)
/*++
Routine Description:
description-of-function.
Arguments:
argument-name - Supplies | Returns description of argument.
.
.
Return Value:
return-value - Description of conditions needed to return value. - or -
None.
--*/
{
DWORD RetCode;
handle_t binding_h;
NTSTATUS Status;
LPWSTR FullName;
LPWSTR Server;
//
// Clear out this parameter, or RPC will choke on the server
// side.
//
*pRecoveryAgents = NULL;
RetCode = GetFullName(
lpFileName,
&FullName,
&Server,
0,
NULL,
0,
NULL,
FALSE
);
if ( RetCode == ERROR_SUCCESS ){
Status = RpcpBindRpc (
Server,
L"lsarpc",
0,
&binding_h
);
if (NT_SUCCESS(Status)){
RpcTryExcept {
RetCode = EfsRpcQueryRecoveryAgents(
binding_h,
FullName,
pRecoveryAgents
);
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
RetCode = RpcExceptionCode();
} RpcEndExcept;
//
// Free the binding handle
//
RpcpUnbindRpc( binding_h );
} else {
RetCode = RtlNtStatusToDosError( Status );
}
RtlFreeHeap( RtlProcessHeap(), 0, FullName );
RtlFreeHeap( RtlProcessHeap(), 0, Server );
}
return RetCode;
}
DWORD
EfsQueryUsersRPCClient(
IN LPCWSTR lpFileName,
OUT PENCRYPTION_CERTIFICATE_HASH_LIST * pUsers
)
/*++
Routine Description:
description-of-function.
Arguments:
argument-name - Supplies | Returns description of argument.
.
.
Return Value:
return-value - Description of conditions needed to return value. - or -
None.
--*/
{
DWORD RetCode;
handle_t binding_h;
NTSTATUS Status;
LPWSTR FullName;
LPWSTR Server;
//
// Clear out this parameter, or RPC will choke on the server
// side.
//
*pUsers = NULL;
RetCode = GetFullName(
lpFileName,
&FullName,
&Server,
0,
NULL,
0,
NULL,
FALSE
);
if ( RetCode == ERROR_SUCCESS ){
Status = RpcpBindRpc (
Server,
L"lsarpc",
0,
&binding_h
);
if (NT_SUCCESS(Status)){
RpcTryExcept {
RetCode = EfsRpcQueryUsersOnFile(
binding_h,
FullName,
pUsers
);
if ((ERROR_SUCCESS == RetCode) && !(*pUsers)) {
//
// The server is hacked? There should always be one user on the file.
//
RetCode = ERROR_DEV_NOT_EXIST;
}
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
RetCode = RpcExceptionCode();
} RpcEndExcept;
//
// Free the binding handle
//
RpcpUnbindRpc( binding_h );
} else {
RetCode = RtlNtStatusToDosError( Status );
}
RtlFreeHeap( RtlProcessHeap(), 0, FullName );
RtlFreeHeap( RtlProcessHeap(), 0, Server );
}
return RetCode;
}
DWORD
EfsSetEncryptionKeyRPCClient(
IN PENCRYPTION_CERTIFICATE pEncryptionCertificate
)
{
DWORD RetCode;
handle_t binding_h;
NTSTATUS Status;
WCHAR ServerName[3 ];
wcscpy(&ServerName[0], L".");
Status = RpcpBindRpc (
&ServerName[0],
L"lsarpc",
0,
&binding_h
);
if (NT_SUCCESS(Status)){
RpcTryExcept {
RetCode = EfsRpcSetFileEncryptionKey(
binding_h,
pEncryptionCertificate
);
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
RetCode = RpcExceptionCode();
} RpcEndExcept;
//
// Free the binding handle
//
RpcpUnbindRpc( binding_h );
} else {
RetCode = RtlNtStatusToDosError( Status );
}
return RetCode;
}
DWORD
EfsDuplicateEncryptionInfoRPCClient(
IN LPCWSTR lpSrcFileName,
IN LPCWSTR lpDestFileName,
IN DWORD dwCreationDistribution,
IN DWORD dwAttributes,
IN PEFS_RPC_BLOB pRelativeSD,
IN BOOL bInheritHandle
)
{
DWORD RetCode;
handle_t binding_h;
NTSTATUS Status;
LPWSTR SrcServer;
LPWSTR DestServer;
LPWSTR FullSrcName;
LPWSTR FullDestName;
DWORD FileAttribute;
DWORD MyCreationDistribution = dwCreationDistribution;
DWORD Flags = 0;
RetCode = GetFullName(
lpSrcFileName,
&FullSrcName,
&SrcServer,
0,
NULL,
0,
NULL,
FALSE
);
if (RetCode == ERROR_SUCCESS) {
FileAttribute = GetFileAttributesW(lpSrcFileName);
if (-1 != FileAttribute) {
if (FileAttribute & FILE_ATTRIBUTE_DIRECTORY) {
Flags = CREATE_FOR_DIR;
}
}
if (dwAttributes == 0) {
FileAttribute = FILE_ATTRIBUTE_NORMAL;
} else {
FileAttribute = dwAttributes;
}
RetCode = GetFullName(
lpDestFileName,
&FullDestName,
&DestServer,
Flags,
&MyCreationDistribution,
FileAttribute,
pRelativeSD,
bInheritHandle
);
if (RetCode == ERROR_SUCCESS) {
BOOL SamePC = TRUE;
//
// Only do this if they're on the same server.
//
SamePC = (_wcsicmp( SrcServer, DestServer ) == 0);
if (!SamePC) {
//
// Check loopback case.
//
if ((wcscmp( SrcServer, L".") == 0) || (wcscmp( DestServer, L".") == 0)){
WCHAR MyComputerName[( MAX_COMPUTERNAME_LENGTH + 1) * sizeof (WCHAR)];
DWORD WorkBufferLength = MAX_COMPUTERNAME_LENGTH + 1;
BOOL b;
b = GetComputerNameW(
MyComputerName,
&WorkBufferLength
);
if (b) {
if (wcscmp( SrcServer, L".") == 0) {
SamePC = (_wcsicmp( MyComputerName, DestServer ) == 0);
} else {
SamePC = (_wcsicmp( MyComputerName, SrcServer ) == 0);
}
}
}
}
if (SamePC) {
Status = RpcpBindRpc (
SrcServer,
L"lsarpc",
0,
&binding_h
);
if (NT_SUCCESS(Status)){
RpcTryExcept {
RetCode = EfsRpcDuplicateEncryptionInfoFile(
binding_h,
FullSrcName,
FullDestName,
MyCreationDistribution,
dwAttributes,
pRelativeSD,
bInheritHandle
);
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
RetCode = RpcExceptionCode();
} RpcEndExcept;
//
// Free the binding handle
//
RpcpUnbindRpc( binding_h );
} else {
RetCode = RtlNtStatusToDosError( Status );
}
} else {
RetCode = ERROR_INVALID_PARAMETER;
}
RtlFreeHeap( RtlProcessHeap(), 0, FullDestName );
RtlFreeHeap( RtlProcessHeap(), 0, DestServer );
}
if ((RetCode != ERROR_SUCCESS) && (RetCode != ERROR_FILE_EXISTS) && (CREATE_NEW == dwCreationDistribution)) {
//
// Let's delete the file. This is the best effort. No return code is to be
// checked.
//
DeleteFileW(lpDestFileName);
}
RtlFreeHeap( RtlProcessHeap(), 0, FullSrcName );
RtlFreeHeap( RtlProcessHeap(), 0, SrcServer );
}
return RetCode;
}
DWORD
EfsFileKeyInfoRPCClient(
IN LPCWSTR lpFileName,
IN DWORD InfoClass,
OUT PEFS_RPC_BLOB *KeyInfo
)
{
DWORD RetCode;
handle_t binding_h;
NTSTATUS Status;
LPWSTR FullName;
LPWSTR Server;
//
// Clear out this parameter, or RPC will choke on the server
// side.
//
if (KeyInfo) {
*KeyInfo = NULL;
}
RetCode = GetFullName(
lpFileName,
&FullName,
&Server,
0,
NULL,
0,
NULL,
FALSE
);
if ( RetCode == ERROR_SUCCESS ){
Status = RpcpBindRpc (
Server,
L"lsarpc",
0,
&binding_h
);
if (NT_SUCCESS(Status)){
RpcTryExcept {
RetCode = EfsRpcFileKeyInfo(
binding_h,
FullName,
InfoClass,
KeyInfo
);
} RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) {
RetCode = RpcExceptionCode();
} RpcEndExcept;
//
// Free the binding handle
//
RpcpUnbindRpc( binding_h );
} else {
RetCode = RtlNtStatusToDosError( Status );
}
RtlFreeHeap( RtlProcessHeap(), 0, FullName );
RtlFreeHeap( RtlProcessHeap(), 0, Server );
}
return RetCode;
}