/*++ Copyright (c) 1997-1999 Microsoft Corporation Module Name: eserver.c Abstract: EFS RPC server code. Author: Robert Gu (RobertG) Aug, 1997 Environment: Revision History: --*/ #define UNICODE #include #include #include #include #include #include // prototypes for MIDL user functions #include #include #include #include #include "efssrv.hxx" #include #define PATHTOOLONG (5*1024) #define TOOMANYUSER (500) #define SDTOOBIG (260*1024) extern BOOLEAN EfsPersonalVer; extern BOOLEAN EfsDisabled; long GetLocalFileName( LPCWSTR FileName, LPWSTR *LocalFileName, WORD *Flag ); BOOL EfsShareDecline( LPCWSTR FileName, BOOL VerifyShareAccess, DWORD dwDesiredAccess ) /*++ Routine Description: Check to see if the FileName is a UNC name and if the user could access the share. Arguments: FileName -- File UNC name. VerifyShareAccess -- if we need to verify the access. dwDesiredAccess -- Desired access. Return Value: TRUE if the user can't access the file. --*/ { BOOL b = TRUE; DWORD FileNameLength = (DWORD) wcslen(FileName); if (FileNameLength >= 3) { if ((FileName[0] == L'\\') && (FileName[1] == L'\\' )) { // // Check if somebody play the trick \\?\ // if ((FileName[2] != L'?')) { // // This is a UNC name. If bad name passed in , we will catch later. // b = FALSE; } else { // // A RPC attack. Do not give good error, such as ERROR_INVALID_PARAMETER. // Just return ACCESS_DENIED. // SetLastError(ERROR_ACCESS_DENIED); } } else { SetLastError(ERROR_ACCESS_DENIED); } } else { SetLastError(ERROR_ACCESS_DENIED); } if (!b && VerifyShareAccess) { LPWSTR NetFileName = NULL; HANDLE hFile; if ( FileNameLength >= MAX_PATH ) { // // We need \\?\UNC\server\share\dir\file format to open the file. // NetFileName = LsapAllocateLsaHeap( (FileNameLength + 8) * sizeof (WCHAR) ); if (!NetFileName) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); return TRUE; } wcscpy(NetFileName, L"\\\\?\\UNC"); wcscat(NetFileName, &FileName[1]); } else { NetFileName = (LPWSTR) FileName; } // // Testing for access rights // hFile = CreateFile( NetFileName, dwDesiredAccess, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, // In case this is a directory NULL ); if (hFile != INVALID_HANDLE_VALUE) { CloseHandle( hFile ); } else { b = TRUE; } if (NetFileName != FileName) { LsapFreeLsaHeap( NetFileName ); } } return b; } BOOL EfsCheckForNetSession( ) /*++ Routine Description: Check to see if the session is from network Arguments: Return Value: TRUE if the session is network session. --*/ { NTSTATUS Status; HANDLE TokenHandle; ULONG ReturnLength; BOOL b = FALSE; BYTE PefBuffer[1024]; Status = NtOpenThreadToken( NtCurrentThread(), TOKEN_QUERY, TRUE, // OpenAsSelf &TokenHandle ); if (NT_SUCCESS( Status )) { Status = NtQueryInformationToken ( TokenHandle, TokenGroups, PefBuffer, sizeof (PefBuffer), &ReturnLength ); if (NT_SUCCESS( Status ) || (Status == STATUS_BUFFER_TOO_SMALL)) { PTOKEN_GROUPS pGroups = NULL; PTOKEN_GROUPS pAllocGroups = NULL; if ( NT_SUCCESS( Status ) ) { pGroups = (PTOKEN_GROUPS) PefBuffer; } else { pAllocGroups = (PTOKEN_GROUPS)LsapAllocateLsaHeap( ReturnLength ); if (pAllocGroups) { Status = NtQueryInformationToken ( TokenHandle, TokenGroups, pAllocGroups, ReturnLength, &ReturnLength ); if ( NT_SUCCESS( Status )) { pGroups = pAllocGroups; } } } if (pGroups) { // // Search the network SID. Looks like this SID tends to appear at the // end of the list. We search from back to the first. // int SidIndex; for ( SidIndex = (int)(pGroups->GroupCount - 1); SidIndex >= 0; SidIndex--) { if (RtlEqualSid(LsapNetworkSid, pGroups->Groups[SidIndex].Sid)) { b = TRUE; break; } } } else { // // Playing safe here. Any failure in this routine will assume net session. // b = TRUE; } if (pAllocGroups) { LsapFreeLsaHeap( pAllocGroups ); } } else { // // Playing safe here. Any failure in this routine will assume net session. // b = TRUE; } NtClose( TokenHandle ); } else { // // Playing safe here. Any failure in this routine will assume net session. // b = TRUE; } return( b ); } long EfsRpcOpenFileRaw( handle_t binding_h, PPEXIMPORT_CONTEXT_HANDLE pphContext, wchar_t __RPC_FAR *FileName, long Flags ) /*++ Routine Description: RPC Stub code for EFS Server EfsOpenFileRaw() Arguments: binding_h -- Binding handle. pphContext -- RPC context handle. FileName -- Target file name. Flags -- Flags of the open request. Return Value: Result of the operation. --*/ { DWORD hResult; LPWSTR LocalFileName; BOOL NetSession = TRUE; WORD WebDavPath; if (EfsPersonalVer) { return ERROR_NOT_SUPPORTED; } if (EfsDisabled) { return ERROR_EFS_DISABLED; } if ((pphContext == NULL) && (FileName == NULL)) { // // Possible RPC attack, don't tell the caller that the parameter is wrong. // //return ERROR_INVALID_PARAMETER; return ERROR_ACCESS_DENIED; } *pphContext = (PEXIMPORT_CONTEXT_HANDLE) NULL; hResult = GetLocalFileName( FileName, &LocalFileName, &WebDavPath ); if (hResult){ DebugLog((DEB_ERROR, "EfsRpcOpenFileRaw: GetLocalFileName failed, Error = (%x)\n" ,hResult )); return hResult; } hResult = RpcImpersonateClient( NULL ); if (hResult != RPC_S_OK) { LsapFreeLsaHeap( LocalFileName ); DebugLog((DEB_ERROR, "EfsRpcOpenFileRaw: RpcImpersonateClient failed, Error = (%x)\n" ,hResult )); return( hResult ); } if (NetSession = EfsCheckForNetSession()) { if (EfsShareDecline(FileName, FALSE, 0 )) { hResult = GetLastError(); LsapFreeLsaHeap( LocalFileName ); RpcRevertToSelf(); DebugLog((DEB_ERROR, "EfsRpcOpenFileRaw: EfsShareDecline failed, Error = (%x)\n" ,GetLastError() )); return hResult; } } hResult = EfsOpenFileRaw( FileName, LocalFileName, NetSession, Flags, pphContext ); RpcRevertToSelf(); LsapFreeLsaHeap( LocalFileName ); return hResult; } void EfsRpcCloseRaw( PPEXIMPORT_CONTEXT_HANDLE pphContext ) /*++ Routine Description: RPC Stub code for EFS Server EfsCloseRaw() Arguments: pphContext -- RPC context handle. Return Value: None. --*/ { if ( *pphContext && (((PEXPORT_CONTEXT) *pphContext)->ContextID == EFS_CONTEXT_ID)){ EfsCloseFileRaw( *pphContext ); *pphContext = NULL; } } void __RPC_USER PEXIMPORT_CONTEXT_HANDLE_rundown( PEXIMPORT_CONTEXT_HANDLE phContext ) /*++ Routine Description: Standard RPC Context Run Down Routine Arguments: phContext -- RPC context handle. Return Value: None. --*/ { EfsCloseFileRaw( phContext ); } long EfsRpcReadFileRaw( PEXIMPORT_CONTEXT_HANDLE phContext, EFS_EXIM_PIPE __RPC_FAR *EfsOutPipe ) /*++ Routine Description: RPC Stub code for EFS Server EfsReadFileRaw Arguments: phContext -- Context handle. EfsOutPipe -- Pipe handle. Return Value: The result of operation. --*/ { if ((EfsOutPipe == NULL) || (phContext == NULL)) { // // RPC Attack. // return ERROR_ACCESS_DENIED; } return (EfsReadFileRaw( phContext, EfsOutPipe ) ); } long EfsRpcWriteFileRaw( PEXIMPORT_CONTEXT_HANDLE phContext, EFS_EXIM_PIPE __RPC_FAR *EfsInPipe ) /*++ Routine Description: RPC Stub code for EFS Server EfsWriteFileRaw Arguments: phContext -- Context handle. EfsInPipe -- Pipe handle. Return Value: The result of operation. --*/ { long hResult; if ((EfsInPipe == NULL) || (phContext == NULL)) { // // RPC Attack. // return ERROR_ACCESS_DENIED; } hResult = RpcImpersonateClient( NULL ); if (hResult != RPC_S_OK) { DebugLog((DEB_ERROR, "EfsRpcWriteFileRaw: RpcImpersonateClient failed, Error = (%x)\n" ,hResult )); return( hResult ); } hResult = EfsWriteFileRaw( phContext, EfsInPipe ); RpcRevertToSelf(); return hResult; } DWORD EFSSendPipeData( char *DataBuf, ULONG DataLength, PVOID Context ) /*++ Routine Description: This is a wrapper routine for calling RPC pipe. The purposes of this routine and EfsRpcReadFileRaw() are to isolate efsapi.c from RPC details, and implemtation details from eserver.c. Arguments: DataBuf -- Data buffer. DataLength -- The length of data in bytes to be sent out to the client. Context -- Pipe handle. Return Value: The result of operation. --*/ { EFS_EXIM_PIPE __RPC_FAR *EfsOutPipe; DWORD HResult = NO_ERROR; // // Pass in parameter should not be 0. It is called by ourselves. // ASSERT( Context ); EfsOutPipe = ( EFS_EXIM_PIPE __RPC_FAR * )Context; RpcTryExcept { EfsOutPipe->push( EfsOutPipe->state, (unsigned char *) DataBuf, DataLength ); } RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) { HResult = RpcExceptionCode(); } RpcEndExcept; return (HResult); } DWORD EFSReceivePipeData( char *DataBuf, ULONG* DataLength, PVOID Context ) /*++ Routine Description: This is a wrapper routine for calling RPC pipe. The purposes of this routine and EfsRpcWriteFileRaw() are to isolate efsapi.c from RPC details, and implemtation details from eserver.c. Arguments: DataBuf -- Data buffer. DataLength -- The length of data in bytes to be got from the client. Context -- Pipe handle. Return Value: The result of operation. --*/ { EFS_EXIM_PIPE __RPC_FAR *EfsInPipe; DWORD HResult = NO_ERROR; char *WorkBuf; ULONG MoreDataBytes; ULONG BytesGot = 0; BOOLEAN GetMoreData = TRUE; // // Pass in parameter should not be 0. It is called by ourselves. // ASSERT( Context ); EfsInPipe = ( EFS_EXIM_PIPE __RPC_FAR * )Context; WorkBuf = DataBuf; MoreDataBytes = *DataLength; while ( GetMoreData ) { BytesGot = 0; RpcTryExcept { EfsInPipe->pull( EfsInPipe->state, (unsigned char *) WorkBuf, MoreDataBytes, &BytesGot ); } RpcExcept( I_RpcExceptionFilter( RpcExceptionCode()) ) { HResult = RpcExceptionCode(); GetMoreData = FALSE; } RpcEndExcept; if ( BytesGot && (BytesGot < MoreDataBytes)){ WorkBuf += BytesGot; MoreDataBytes -= BytesGot; } else { GetMoreData = FALSE; } } if (HResult == NO_ERROR){ *DataLength = (ULONG)(WorkBuf - DataBuf) + BytesGot; } return (HResult); } long EfsRpcEncryptFileSrv( handle_t binding_h, wchar_t __RPC_FAR *FileName ) /*++ Routine Description: RPC Stub code for EFS Server Encryption Arguments: binding_h -- RPC binding handle. FileName -- Target name. Return Value: The result of operation. --*/ { NTSTATUS Status = STATUS_SUCCESS; UNICODE_STRING DestFileName; DWORD hResult; UNICODE_STRING RootPath; HANDLE LogFile; LPWSTR LocalFileName; EFS_USER_INFO EfsUserInfo; DWORD FileAttributes; BOOL b; WORD WebDavPath; if (EfsPersonalVer) { return ERROR_NOT_SUPPORTED; } if (EfsDisabled) { return ERROR_EFS_DISABLED; } hResult = GetLocalFileName( FileName, &LocalFileName, &WebDavPath ); if (hResult){ DebugLog((DEB_ERROR, "EfsRpcEncryptFileSrv: GetLocalFileName failed, Error = (%x)\n" ,hResult )); return hResult; } hResult = RpcImpersonateClient( NULL ); if ((hResult == RPC_S_OK) && EfsCheckForNetSession()) { if (WebDavPath == WEBDAVPATH) { // // A RPC attacker. WEBDAV should be local session. // LsapFreeLsaHeap( LocalFileName ); RpcRevertToSelf(); return ERROR_ACCESS_DENIED; } if (EfsShareDecline(FileName, TRUE, FILE_READ_DATA | FILE_WRITE_DATA | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES )) { hResult = GetLastError(); LsapFreeLsaHeap( LocalFileName ); RpcRevertToSelf(); DebugLog((DEB_ERROR, "EfsRpcEncryptFileSrv: EfsShareDecline failed, Error = (%x)\n" ,GetLastError())); return hResult; } } if (hResult == RPC_S_OK) { if (WebDavPath == WEBDAVPATH) { // // This is a WEB DAV path. We will treat it specially. // FileAttributes = GetFileAttributes( LocalFileName ); if (FileAttributes == -1) { DWORD rc = GetLastError(); LsapFreeLsaHeap( LocalFileName ); RpcRevertToSelf(); DebugLog((DEB_ERROR, "EfsRpcEncryptFileSrv: GetFileAttributes failed on WEBDAV file, Error = (%x)\n" ,GetLastError())); return rc; } // // Mapping the attributes and fake the call of FileEncryptionStatus. // if (FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) { hResult = FILE_IS_ENCRYPTED; } else if (FileAttributes & FILE_ATTRIBUTE_READONLY) { hResult = FILE_READ_ONLY; } else { hResult = FILE_UNKNOWN; } b = TRUE; } else { b = FileEncryptionStatus(LocalFileName, &hResult); } } else { DebugLog((DEB_ERROR, "EfsRpcEncryptFileSrv: RpcImpersonateClient failed, Error = (%x)\n" ,hResult )); LsapFreeLsaHeap( LocalFileName ); return hResult; } RpcRevertToSelf(); if ( b ){ if ( (hResult != FILE_ENCRYPTABLE) && (hResult != FILE_UNKNOWN)){ // // No encryption is allowed or file is already encrypted // if ( hResult == FILE_IS_ENCRYPTED ){ HANDLE hSourceFile; hResult = RpcImpersonateClient( NULL ); if (hResult == RPC_S_OK) { FileAttributes = GetFileAttributes( LocalFileName ); if (FileAttributes != -1) { if ( FileAttributes & FILE_ATTRIBUTE_DIRECTORY ){ hResult = ERROR_SUCCESS; } else { hSourceFile = CreateFile( LocalFileName, FILE_READ_DATA | FILE_WRITE_DATA | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES, 0, NULL, OPEN_EXISTING, FILE_FLAG_OPEN_REPARSE_POINT, NULL ); if (hSourceFile == INVALID_HANDLE_VALUE) { hResult = GetLastError(); } else { CloseHandle( hSourceFile ); hResult = ERROR_SUCCESS; } } } else{ hResult = GetLastError(); } RpcRevertToSelf(); } } else if (hResult == FILE_DIR_DISALLOWED ) { hResult = ERROR_DIR_EFS_DISALLOWED; } else if ( hResult == FILE_READ_ONLY ){ hResult = ERROR_FILE_READ_ONLY; } else { hResult = ERROR_ACCESS_DENIED; } LsapFreeLsaHeap( LocalFileName ); return hResult; } } else { // // Error occured checking the status // DebugLog((DEB_TRACE_EFS, "EfsRpcEncryptFileSrv: FileEncryptionStatus() failed, Error = (%x)\n" ,hResult )); LsapFreeLsaHeap( LocalFileName ); return hResult; } hResult = RpcImpersonateClient( NULL ); if (hResult != RPC_S_OK) { DebugLog((DEB_ERROR, "EfsRpcEncryptFileSrv: RpcImpersonateClient failed, Error = (%x)\n" ,hResult )); LsapFreeLsaHeap( LocalFileName ); return( hResult ); } DestFileName.Length = (USHORT) (sizeof(WCHAR) * wcslen(LocalFileName)); DestFileName.MaximumLength = DestFileName.Length + sizeof (WCHAR); DestFileName.Buffer = LocalFileName; // // Get the rootname // if (WebDavPath == WEBDAVPATH){ // // Do not support LOGFILE for WEB DAV // LogFile = NULL; RpcRevertToSelf(); } else { hResult = GetVolumeRoot(&DestFileName, &RootPath); RpcRevertToSelf(); if (hResult != ERROR_SUCCESS) { DebugLog((DEB_ERROR, "EfsRpcEncryptFileSrv: GetVolumeRoot failed, Error = (%x)\n" ,hResult )); LsapFreeLsaHeap( LocalFileName ); return( hResult ); } Status = GetLogFile( &RootPath, &LogFile ); LsapFreeLsaHeap( RootPath.Buffer ); } if (NT_SUCCESS( Status )) { hResult = RpcImpersonateClient( NULL ); if (hResult == RPC_S_OK) { if (EfspGetUserInfo( &EfsUserInfo )) { if (EfspLoadUserProfile( &EfsUserInfo, FALSE )) { hResult = EncryptFileSrv( &EfsUserInfo, &DestFileName, LogFile ); EfspUnloadUserProfile( &EfsUserInfo ); } else { hResult = GetLastError(); } EfspFreeUserInfo( &EfsUserInfo ); } else{ hResult = GetLastError(); } RpcRevertToSelf(); } else { if (LogFile) { MarkFileForDelete( LogFile ); } } if (LogFile) { CloseHandle(LogFile); } } if (!NT_SUCCESS( Status )){ hResult = RtlNtStatusToDosError( Status ); // // Make sure the error was mapped // if (hResult == ERROR_MR_MID_NOT_FOUND) { DebugLog((DEB_WARN, "Unable to map NT Error (%x) to Win32 error, returning ERROR_ENCRYPTION_FAILED\n" , Status )); hResult = ERROR_ENCRYPTION_FAILED; } } LsapFreeLsaHeap( LocalFileName ); return( hResult ); } long EfsRpcDecryptFileSrv( handle_t binding_h, wchar_t __RPC_FAR *FileName, unsigned long OpenFlag ) /*++ Routine Description: RPC Stub code for EFS Server Decryption Arguments: binding_h -- RPC binding handle. FileName -- Target name. OpenFlag -- Open for recovery or decryption Return Value: The result of operation. --*/ { NTSTATUS Status = STATUS_SUCCESS; UNICODE_STRING DestFileName; DWORD hResult; UNICODE_STRING RootPath; HANDLE LogFile; LPWSTR LocalFileName; DWORD FileAttributes; WORD WebDavPath; if (EfsPersonalVer) { return ERROR_NOT_SUPPORTED; } if (EfsDisabled) { return ERROR_EFS_DISABLED; } hResult = GetLocalFileName( FileName, &LocalFileName, &WebDavPath ); if (hResult){ DebugLog((DEB_ERROR, "EfsRpcDecryptFileSrv: GetLocalFileName failed, Error = (%x)\n" ,hResult )); return hResult; } hResult = RpcImpersonateClient( NULL ); if (hResult != RPC_S_OK) { DebugLog((DEB_ERROR, "EfsRpcDecryptFileSrv: RpcImpersonateClient failed, Error = (%x)\n" ,hResult )); LsapFreeLsaHeap( LocalFileName ); return( hResult ); } if (EfsCheckForNetSession()) { if (WebDavPath == WEBDAVPATH) { // // A RPC attacker. WEBDAV should be local session. // LsapFreeLsaHeap( LocalFileName ); RpcRevertToSelf(); return ERROR_ACCESS_DENIED; } if (EfsShareDecline(FileName, TRUE, FILE_READ_DATA | FILE_WRITE_DATA | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES )) { hResult = GetLastError(); LsapFreeLsaHeap( LocalFileName ); RpcRevertToSelf(); DebugLog((DEB_ERROR, "EfsRpcDecryptFileSrv: EfsShareDecline failed, Error = (%x)\n" ,GetLastError() )); return hResult; } } FileAttributes = GetFileAttributes( LocalFileName ); if (-1 != FileAttributes){ if ( !(FileAttributes & FILE_ATTRIBUTE_ENCRYPTED) ){ // // No decryption is needed. // RpcRevertToSelf(); LsapFreeLsaHeap( LocalFileName ); return ERROR_SUCCESS; } } else { // // Error occured checking the status // hResult = GetLastError(); RpcRevertToSelf(); DebugLog((DEB_TRACE_EFS, "EfsRpcDecryptFileSrv: GetFileAttributes() failed, Error = (%x)\n" , hResult)); LsapFreeLsaHeap( LocalFileName ); return hResult; } DestFileName.Length = (USHORT) (sizeof(WCHAR) * wcslen(LocalFileName)); DestFileName.MaximumLength = DestFileName.Length + sizeof (WCHAR); DestFileName.Buffer = LocalFileName; // // Get the rootname // if (WebDavPath == WEBDAVPATH){ // // Do not support LOGFILE for WEB DAV // LogFile = NULL; RpcRevertToSelf(); } else { hResult = GetVolumeRoot(&DestFileName, &RootPath); RpcRevertToSelf(); if (hResult != ERROR_SUCCESS) { DebugLog((DEB_ERROR, "EfsRpcDecryptFileSrv: GetVolumeRoot failed, Error = (%x)\n" ,hResult )); LsapFreeLsaHeap( LocalFileName ); return( hResult ); } Status = GetLogFile( &RootPath, &LogFile ); LsapFreeLsaHeap( RootPath.Buffer ); } if (NT_SUCCESS( Status )) { hResult = RpcImpersonateClient( NULL ); if (hResult == RPC_S_OK) { hResult = DecryptFileSrv( &DestFileName, LogFile, OpenFlag ); RpcRevertToSelf(); } else { if (LogFile) { MarkFileForDelete( LogFile ); } } if (LogFile) { CloseHandle(LogFile); } } if (!NT_SUCCESS( Status )){ hResult = RtlNtStatusToDosError( Status ); // // Make sure the error was mapped // if (hResult == ERROR_MR_MID_NOT_FOUND) { DebugLog((DEB_WARN, "Unable to map NT Error (%x) to Win32 error, returning ERROR_DECRYPTION_FAILED\n" , Status )); hResult = ERROR_DECRYPTION_FAILED; } } LsapFreeLsaHeap( LocalFileName ); return( (long)hResult ); } long GetLocalFileName( LPCWSTR FileName, LPWSTR *LocalFileName, WORD *Flag ) /*++ Routine Description: Get the local file name from the UNC name Arguments: FileName -- Target UNC file name. LocalFileName -- Local file name. Flag -- Indicating special path, such as WEB DAV path. Return Value: The result of operation. --*/ { long RetCode = ERROR_SUCCESS; LPWSTR NetName; ULONG ii, jj; LPBYTE ShareInfo; DWORD PathLen; DWORD BufLen; BOOL SharePath = FALSE; BOOL LocalCheckLength = TRUE; *Flag = 0; if ( FileName == NULL ) { // // Possible RPC attack // return ERROR_ACCESS_DENIED; } PathLen = (DWORD) wcslen(FileName); if (PathLen >= PATHTOOLONG) { // // A possible RPC attack. Just return. // No need to return good error here. // return ERROR_ACCESS_DENIED; } BufLen = MAX_PATH >= PathLen + 1? MAX_PATH + 1: PathLen + 10; // // Check the WEB DAV path first // if (DAVHEADER == FileName[0]) { // // This is the WEB DAV path. Treat it as the local case. // Take whatever the user passed in. // *LocalFileName = (LPWSTR)LsapAllocateLsaHeap( PathLen * sizeof (WCHAR)); if (NULL == *LocalFileName) { return ERROR_NOT_ENOUGH_MEMORY; } *Flag = WEBDAVPATH; wcscpy(*LocalFileName, &FileName[1]); return ERROR_SUCCESS; } // // See if the pass in name is \\server\share // if ((PathLen > 4) && (FileName[0] == L'\\') && (FileName[1] == L'\\')) { if ((FileName[2] != L'?') && FileName[2] != L'.') { SharePath = TRUE; } else { if (FileName[3] != L'\\') { SharePath = TRUE; } else { // // path \\?\ or \\.\ // LocalCheckLength = FALSE; } } } *LocalFileName = (LPWSTR)LsapAllocateLsaHeap( BufLen * sizeof (WCHAR)); if ( NULL == *LocalFileName ){ return ERROR_NOT_ENOUGH_MEMORY; } if (!SharePath) { // // This is a local path. Just copy it. // if (LocalCheckLength && PathLen >= MAX_PATH) { // // This is for Win2K compatibility // wcscpy(*LocalFileName, L"\\\\?\\"); wcscat(*LocalFileName, FileName); } else { wcscpy(*LocalFileName, FileName); } return ERROR_SUCCESS; } NetName = (LPWSTR)LsapAllocateLsaHeap( PathLen * sizeof (WCHAR)); if ( NULL == NetName ){ LsapFreeLsaHeap( *LocalFileName ); *LocalFileName = NULL; return ERROR_NOT_ENOUGH_MEMORY; } // // Extract the net name // ii = jj = 0; while ( (FileName[jj]) && (FileName[jj] == L'\\') ){ jj++; } while ( (FileName[jj]) && (FileName[jj++] != L'\\') ); while ( (FileName[jj]) && (FileName[jj] != L'\\')){ NetName[ii++] = FileName[jj++]; } if ( !(FileName[jj]) ){ // // Invalid path name // LsapFreeLsaHeap( NetName ); LsapFreeLsaHeap( *LocalFileName ); *LocalFileName = NULL; return ERROR_BAD_NETPATH ; } NetName[ii] = 0; RetCode = NetShareGetInfo( NULL, NetName, 2, &ShareInfo ); if ( NERR_Success == RetCode ){ PathLen = (DWORD) (wcslen(((LPSHARE_INFO_2)ShareInfo)->shi2_path) + wcslen(&FileName[jj]) + 1); if ( PathLen >= MAX_PATH ){ if (PathLen + 5 > BufLen){ LsapFreeLsaHeap( *LocalFileName ); BufLen = PathLen + 5; *LocalFileName = (LPWSTR)LsapAllocateLsaHeap( BufLen * sizeof (WCHAR)); if ( NULL == *LocalFileName ){ NetApiBufferFree(ShareInfo); LsapFreeLsaHeap( NetName ); return ERROR_NOT_ENOUGH_MEMORY; } } } if (MAX_PATH <= PathLen){ // // Put in the \\?\. Buffer should be bigger enough. // wcscpy(*LocalFileName,L"\\\\?\\"); wcscat( *LocalFileName, ((LPSHARE_INFO_2)ShareInfo)->shi2_path ); } else { wcscpy( *LocalFileName, ((LPSHARE_INFO_2)ShareInfo)->shi2_path ); } wcscat(*LocalFileName, &FileName[jj]); NetApiBufferFree(ShareInfo); } else { // // Invalid path name // LsapFreeLsaHeap( *LocalFileName ); *LocalFileName = NULL; RetCode = ERROR_BAD_NETPATH ; } LsapFreeLsaHeap( NetName ); return RetCode; } DWORD EfsRpcQueryUsersOnFile( IN handle_t binding_h, IN LPCWSTR lpFileName, OUT PENCRYPTION_CERTIFICATE_HASH_LIST *pUsersList ) { DWORD hResult; PENCRYPTION_CERTIFICATE_HASH_LIST pHashList; LPWSTR LocalFileName; WORD WebDavPath; DebugLog((DEB_WARN, "Made it into EfsRpcQueryUsersOnFile\n" )); if (EfsPersonalVer) { return ERROR_NOT_SUPPORTED; } if (EfsDisabled) { return ERROR_EFS_DISABLED; } if (pUsersList == NULL) { return ERROR_INVALID_PARAMETER; } hResult = GetLocalFileName( lpFileName, &LocalFileName, &WebDavPath ); if (hResult){ DebugLog((DEB_ERROR, "EfsRpcQueryUsersOnFile: GetLocalFileName failed, Error = (%x)\n" ,hResult )); return hResult; } hResult = RpcImpersonateClient( NULL ); if (hResult != RPC_S_OK) { DebugLog((DEB_ERROR, "EfsRpcQueryUsersOnFile: RpcImpersonateClient failed, Error = (%x)\n" ,hResult )); LsapFreeLsaHeap( LocalFileName ); return( hResult ); } if (EfsCheckForNetSession()) { if (WebDavPath == WEBDAVPATH) { // // A RPC attacker. WEBDAV should be local session. // LsapFreeLsaHeap( LocalFileName ); RpcRevertToSelf(); return ERROR_ACCESS_DENIED; } if (EfsShareDecline(lpFileName, TRUE, FILE_READ_ATTRIBUTES )) { LsapFreeLsaHeap( LocalFileName ); RpcRevertToSelf(); DebugLog((DEB_ERROR, "EfsRpcQueryUsersOnFile: EfsShareDecline failed, Error = (%x)\n" ,GetLastError() )); return GetLastError(); } } // // Allocate the structure we're going to return // pHashList = (PENCRYPTION_CERTIFICATE_HASH_LIST)MIDL_user_allocate( sizeof( ENCRYPTION_CERTIFICATE_HASH_LIST )); *pUsersList = pHashList; if (pHashList) { hResult = QueryUsersOnFileSrv( LocalFileName, &pHashList->nCert_Hash, &pHashList->pUsers ); if (hResult != ERROR_SUCCESS) { // // Free the structure we allocated // MIDL_user_free( pHashList ); *pUsersList = NULL; // paranoia } } RpcRevertToSelf(); LsapFreeLsaHeap( LocalFileName ); return( hResult ); } DWORD EfsRpcQueryRecoveryAgents( IN handle_t binding_h, IN LPCWSTR lpFileName, OUT PENCRYPTION_CERTIFICATE_HASH_LIST * pRecoveryAgents ) { DWORD hResult; PENCRYPTION_CERTIFICATE_HASH_LIST pHashList; LPWSTR LocalFileName; WORD WebDavPath; DebugLog((DEB_WARN, "Made it into EfsRpcQueryRecoveryAgents\n" )); if (EfsPersonalVer) { return ERROR_NOT_SUPPORTED; } if (EfsDisabled) { return ERROR_EFS_DISABLED; } if (pRecoveryAgents == NULL) { return ERROR_INVALID_PARAMETER; } hResult = GetLocalFileName( lpFileName, &LocalFileName, &WebDavPath ); if (hResult){ DebugLog((DEB_ERROR, "EfsRpcQueryRecoveryAgents: GetLocalFileName failed, Error = (%x)\n" ,hResult )); return hResult; } hResult = RpcImpersonateClient( NULL ); if (hResult != RPC_S_OK) { DebugLog((DEB_ERROR, "EfsRpcQueryRecoveryAgents: RpcImpersonateClient failed, Error = (%x)\n" ,hResult )); LsapFreeLsaHeap( LocalFileName ); return( hResult ); } if (EfsCheckForNetSession()) { if (WebDavPath == WEBDAVPATH) { // // A RPC attacker. WEBDAV should be local session. // LsapFreeLsaHeap( LocalFileName ); RpcRevertToSelf(); return ERROR_ACCESS_DENIED; } if (EfsShareDecline(lpFileName, TRUE, FILE_READ_ATTRIBUTES )) { LsapFreeLsaHeap( LocalFileName ); RpcRevertToSelf(); DebugLog((DEB_ERROR, "EfsRpcQueryRecoveryAgents: EfsShareDecline failed, Error = (%x)\n" ,GetLastError() )); return GetLastError(); } } // // Allocate the structure we're going to return // pHashList = (PENCRYPTION_CERTIFICATE_HASH_LIST)MIDL_user_allocate( sizeof( ENCRYPTION_CERTIFICATE_HASH_LIST )); *pRecoveryAgents = pHashList; if (pHashList) { hResult = QueryRecoveryAgentsSrv( LocalFileName, &pHashList->nCert_Hash, &pHashList->pUsers ); if (hResult != ERROR_SUCCESS) { // // Free the structure we allocated // MIDL_user_free( pHashList ); *pRecoveryAgents = NULL; // paranoia } } RpcRevertToSelf(); LsapFreeLsaHeap( LocalFileName ); return( hResult ); } DWORD EfsRpcRemoveUsersFromFile( IN handle_t binding_h, IN LPCWSTR lpFileName, IN PENCRYPTION_CERTIFICATE_HASH_LIST pUsers ) { DWORD hResult; LPWSTR LocalFileName; EFS_USER_INFO EfsUserInfo; WORD WebDavPath; DebugLog((DEB_WARN, "Made it into EfsRpcRemoveUsersFromFile\n" )); if (EfsPersonalVer) { return ERROR_NOT_SUPPORTED; } if (EfsDisabled) { return ERROR_EFS_DISABLED; } if ((pUsers == NULL) || (lpFileName == NULL) || (pUsers->pUsers == NULL)) { return ERROR_INVALID_PARAMETER; } if (pUsers->nCert_Hash > TOOMANYUSER) { // // Possible RPC attack // return ERROR_ACCESS_DENIED; } hResult = GetLocalFileName( lpFileName, &LocalFileName, &WebDavPath ); if (hResult){ DebugLog((DEB_ERROR, "EfsRpcRemoveUsersFromFile: GetLocalFileName failed, Error = (%x)\n" ,hResult )); return hResult; } hResult = RpcImpersonateClient( NULL ); if (hResult != RPC_S_OK) { DebugLog((DEB_ERROR, "EfsRpcRemoveUsersFromFile: RpcImpersonateClient failed, Error = (%x)\n" ,hResult )); LsapFreeLsaHeap( LocalFileName ); return( hResult ); } if (EfsCheckForNetSession()) { if (WebDavPath == WEBDAVPATH) { // // A RPC attacker. WEBDAV should be local session. // LsapFreeLsaHeap( LocalFileName ); RpcRevertToSelf(); return ERROR_ACCESS_DENIED; } if (EfsShareDecline(lpFileName, TRUE, FILE_READ_ATTRIBUTES| FILE_WRITE_DATA )) { hResult = GetLastError(); LsapFreeLsaHeap( LocalFileName ); RpcRevertToSelf(); DebugLog((DEB_ERROR, "EfsRpcRemoveUsersFromFile: EfsShareDecline failed, Error = (%x)\n" ,hResult )); return hResult; } } if (EfspGetUserInfo( &EfsUserInfo )) { if (EfspLoadUserProfile( &EfsUserInfo, FALSE )) { // // The cert hash list could be garbage // __try{ hResult = RemoveUsersFromFileSrv( &EfsUserInfo, LocalFileName, pUsers->nCert_Hash, pUsers->pUsers ); } __except (EXCEPTION_EXECUTE_HANDLER) { hResult = ERROR_INVALID_PARAMETER; } EfspUnloadUserProfile( &EfsUserInfo ); } else { hResult = GetLastError(); } EfspFreeUserInfo( &EfsUserInfo ); } else { hResult = GetLastError(); } RpcRevertToSelf(); LsapFreeLsaHeap( LocalFileName ); return( hResult ); } DWORD EfsRpcAddUsersToFile( IN handle_t binding_h, IN LPCWSTR lpFileName, IN PENCRYPTION_CERTIFICATE_LIST pEncryptionCertificates ) { DWORD hResult; LPWSTR LocalFileName; EFS_USER_INFO EfsUserInfo; WORD WebDavPath; DebugLog((DEB_WARN, "Made it into EfsRpcAddUsersToFile\n" )); if (EfsPersonalVer) { return ERROR_NOT_SUPPORTED; } if (EfsDisabled) { return ERROR_EFS_DISABLED; } if (pEncryptionCertificates == NULL) { return ERROR_INVALID_PARAMETER; } if (pEncryptionCertificates->nUsers > TOOMANYUSER) { // // Possible RPC attack // We could make this bigger in the future if there is the need. // return ERROR_ACCESS_DENIED; } hResult = GetLocalFileName( lpFileName, &LocalFileName, &WebDavPath ); if (hResult){ DebugLog((DEB_ERROR, "EfsRpcAddUsersToFile: GetLocalFileName failed, Error = (%x)\n" ,hResult )); return hResult; } hResult = RpcImpersonateClient( NULL ); if (hResult != RPC_S_OK) { DebugLog((DEB_ERROR, "EfsRpcAddUsersToFile: RpcImpersonateClient failed, Error = (%x)\n" ,hResult )); LsapFreeLsaHeap( LocalFileName ); return( hResult ); } if (EfsCheckForNetSession()) { if (WebDavPath == WEBDAVPATH) { // // A RPC attacker. WEBDAV should be local session. // LsapFreeLsaHeap( LocalFileName ); RpcRevertToSelf(); return ERROR_ACCESS_DENIED; } if (EfsShareDecline(lpFileName, TRUE, FILE_READ_ATTRIBUTES| FILE_WRITE_DATA )) { hResult = GetLastError(); LsapFreeLsaHeap( LocalFileName ); RpcRevertToSelf(); DebugLog((DEB_ERROR, "EfsRpcAddUsersToFile: EfsShareDecline failed, Error = (%x)\n" , hResult )); return hResult; } } if (EfspGetUserInfo( &EfsUserInfo )) { if (EfspLoadUserProfile( &EfsUserInfo, FALSE )) { // // We may be passed in garbage for the cert list // However, RPC will guarantee that the first level reference will not cause AV. // TRY is used in AddUsersToFileSrv for better error handling. // hResult = AddUsersToFileSrv( &EfsUserInfo, LocalFileName, pEncryptionCertificates->nUsers, pEncryptionCertificates->pUsers ); EfspUnloadUserProfile( &EfsUserInfo ); } else { hResult = GetLastError(); } EfspFreeUserInfo( &EfsUserInfo ); } else { hResult = GetLastError(); } RpcRevertToSelf(); LsapFreeLsaHeap( LocalFileName ); return( hResult ); } DWORD EfsRpcSetFileEncryptionKey( IN handle_t binding_h, IN PENCRYPTION_CERTIFICATE pEncryptionCertificate ) { DWORD hResult; EFS_USER_INFO EfsUserInfo; DebugLog((DEB_WARN, "Made it into EfsRpcSetFileEncryptionKey\n" )); if (EfsPersonalVer) { return ERROR_NOT_SUPPORTED; } if (EfsDisabled) { return ERROR_EFS_DISABLED; } hResult = RpcImpersonateClient( NULL ); if (hResult != RPC_S_OK) { DebugLog((DEB_ERROR, "EfsRpcSetFileEncryptionKey: RpcImpersonateClient failed, Error = (%x)\n" ,hResult )); return( hResult ); } if (EfsCheckForNetSession()) { // // This call can only be done locally. // RpcRevertToSelf(); return ERROR_NOT_SUPPORTED; } if (EfspGetUserInfo( &EfsUserInfo )) { if (EfspLoadUserProfile( &EfsUserInfo, TRUE )) { hResult = SetFileEncryptionKeySrv( &EfsUserInfo, pEncryptionCertificate ); EfspUnloadUserProfile( &EfsUserInfo ); } else { hResult = GetLastError(); } EfspFreeUserInfo( &EfsUserInfo ); } RpcRevertToSelf(); return( hResult ); } DWORD EfsRpcDuplicateEncryptionInfoFile( IN handle_t binding_h, IN LPCWSTR lpSrcFileName, IN LPCWSTR lpDestFileName, IN DWORD dwCreationDistribution, IN DWORD dwAttributes, IN PEFS_RPC_BLOB pRelativeSD, IN BOOL bInheritHandle ) { DWORD hResult; LPWSTR LocalSrcFileName; LPWSTR LocalDestFileName; BOOLEAN NetSession=FALSE; WORD WebDavPathSrc; WORD WebDavPathDst; EFS_USER_INFO EfsUserInfo; DebugLog((DEB_WARN, "Made it into EfsRpcDuplicateEncryptionInfoFile\n" )); if (EfsPersonalVer) { return ERROR_NOT_SUPPORTED; } if (EfsDisabled) { return ERROR_EFS_DISABLED; } if (pRelativeSD && pRelativeSD->cbData > SDTOOBIG) { // // RPC attack // return ERROR_ACCESS_DENIED; } hResult = GetLocalFileName( lpSrcFileName, &LocalSrcFileName, &WebDavPathSrc ); if (hResult){ DebugLog((DEB_ERROR, "EfsRpcDuplicateEncryptionInfoFile: GetLocalFileName failed, Error = (%x)\n" ,hResult )); return hResult; } hResult = GetLocalFileName( lpDestFileName, &LocalDestFileName, &WebDavPathDst ); if (hResult){ DebugLog((DEB_ERROR, "EfsRpcDuplicateEncryptionInfoFile: GetLocalFileName failed, Error = (%x)\n" ,hResult )); LsapFreeLsaHeap( LocalSrcFileName ); return hResult; } hResult = RpcImpersonateClient( NULL ); if (hResult != RPC_S_OK) { DebugLog((DEB_ERROR, "EfsRpcDuplicateEncryptionInfoFile: RpcImpersonateClient failed, Error = (%x)\n" ,hResult )); LsapFreeLsaHeap( LocalDestFileName ); LsapFreeLsaHeap( LocalSrcFileName ); return ( hResult ); } if (EfsCheckForNetSession()) { if ((WebDavPathSrc == WEBDAVPATH) || (WebDavPathDst == WEBDAVPATH)) { // // A RPC attacker. WEBDAV should be local session. // LsapFreeLsaHeap( LocalDestFileName ); LsapFreeLsaHeap( LocalSrcFileName ); RpcRevertToSelf(); return ERROR_ACCESS_DENIED; } if (EfsShareDecline(lpSrcFileName, TRUE, FILE_READ_ATTRIBUTES )) { LsapFreeLsaHeap( LocalDestFileName ); LsapFreeLsaHeap( LocalSrcFileName ); RpcRevertToSelf(); DebugLog((DEB_ERROR, "EfsRpcDuplicateEncryptionInfoFile: EfsShareDecline failed, Error = (%x)\n" ,GetLastError() )); return GetLastError(); } if (EfsShareDecline(lpDestFileName, FALSE, 0 )) { LsapFreeLsaHeap( LocalDestFileName ); LsapFreeLsaHeap( LocalSrcFileName ); RpcRevertToSelf(); DebugLog((DEB_ERROR, "EfsRpcDuplicateEncryptionInfoFile: EfsShareDecline failed, Error = (%x)\n" ,GetLastError() )); return GetLastError(); } NetSession = TRUE; } if (EfspGetUserInfo( &EfsUserInfo )) { // // Load the profile so we can open the source file // if (EfspLoadUserProfile( &EfsUserInfo, FALSE )) { hResult = DuplicateEncryptionInfoFileSrv( &EfsUserInfo, LocalSrcFileName, LocalDestFileName, NetSession? lpDestFileName: NULL, dwCreationDistribution, dwAttributes, pRelativeSD, bInheritHandle ); EfspUnloadUserProfile( &EfsUserInfo ); } else { hResult = GetLastError(); } EfspFreeUserInfo( &EfsUserInfo ); } LsapFreeLsaHeap( LocalDestFileName ); LsapFreeLsaHeap( LocalSrcFileName ); RpcRevertToSelf(); return( hResult ); } DWORD EfsRpcFileKeyInfo( IN handle_t binding_h, IN LPCWSTR lpFileName, IN DWORD InfoClass, OUT PEFS_RPC_BLOB *KeyInfo ) { DWORD hResult; PEFS_RPC_BLOB pKeyInfo; LPWSTR LocalFileName; WORD WebDavPath; DebugLog((DEB_WARN, "Made it into EfsRpcFileKeyInfo\n" )); if (EfsPersonalVer) { return ERROR_NOT_SUPPORTED; } if (EfsDisabled) { return ERROR_EFS_DISABLED; } if (KeyInfo == NULL) { return ERROR_INVALID_PARAMETER; } hResult = GetLocalFileName( lpFileName, &LocalFileName, &WebDavPath ); if (hResult){ DebugLog((DEB_ERROR, "EfsRpcFileKeyInfo: GetLocalFileName failed, Error = (%x)\n" ,hResult )); return hResult; } hResult = RpcImpersonateClient( NULL ); if (hResult != RPC_S_OK) { DebugLog((DEB_ERROR, "EfsRpcFileKeyInfo: RpcImpersonateClient failed, Error = (%x)\n" ,hResult )); LsapFreeLsaHeap( LocalFileName ); return( hResult ); } if (EfsCheckForNetSession()) { if (WebDavPath == WEBDAVPATH) { // // A RPC attacker. WEBDAV should be local session. // LsapFreeLsaHeap( LocalFileName ); RpcRevertToSelf(); return ERROR_ACCESS_DENIED; } if (EfsShareDecline(lpFileName, TRUE, FILE_READ_ATTRIBUTES )) { LsapFreeLsaHeap( LocalFileName ); RpcRevertToSelf(); DebugLog((DEB_ERROR, "EfsRpcFileKeyInfo: EfsShareDecline failed, Error = (%x)\n" ,GetLastError() )); return GetLastError(); } } // // Allocate the structure we're going to return // pKeyInfo = (PEFS_RPC_BLOB)MIDL_user_allocate( sizeof( EFS_RPC_BLOB )); *KeyInfo = pKeyInfo; if (pKeyInfo) { hResult = EfsFileKeyInfoSrv( LocalFileName, InfoClass, &pKeyInfo->cbData, &pKeyInfo->pbData ); if (hResult != ERROR_SUCCESS) { // // Free the structure we allocated // if (pKeyInfo->pbData) { MIDL_user_free( pKeyInfo->pbData ); } MIDL_user_free( pKeyInfo ); *KeyInfo = NULL; // paranoia } else { if (NULL == pKeyInfo->pbData) { // // No data returned // MIDL_user_free( pKeyInfo ); *KeyInfo = NULL; } } } else { hResult = ERROR_NOT_ENOUGH_MEMORY; } RpcRevertToSelf(); LsapFreeLsaHeap( LocalFileName ); return( hResult ); } DWORD EfsRpcNotSupported( IN handle_t binding_h, IN LPCWSTR lpSrcFileName, IN LPCWSTR lpDestFileName, IN DWORD dwCreationDistribution, IN DWORD dwAttributes, IN PEFS_RPC_BLOB pRelativeSD, IN BOOL bInheritHandle ) { DebugLog((DEB_WARN, "Made it into EfsRpcNotSupported\n" )); return ERROR_NOT_SUPPORTED; }