/*++

Copyright (c) 1997-1999  Microsoft Corporation

Module Name:

    efs.c

Abstract:

    EFS (Encrypting File System) API Interfaces

Author:

    Robert Reichel      (RobertRe)
    Robert Gu           (RobertG)

Environment:

Revision History:

--*/

#undef WIN32_LEAN_AND_MEAN

#include "advapi.h"
#include <windows.h>
#include <feclient.h>

#define FE_CLIENT_DLL      L"feclient.dll"


//
// Global Variables
//

LPFE_CLIENT_INFO    FeClientInfo   = NULL;
HMODULE             FeClientModule = NULL;
CRITICAL_SECTION    FeClientLoadCritical;


LPWSTR
GetFeClientDll(
    VOID
    )
/*++

Routine Description:

    This routine obtains the name of the currently installed client
    encryption dll (which is currently hardcoded).

Arguments:

    None.

Return Value:

    Returns the name of the current DLL, or NULL on error.

--*/

{
    return( FE_CLIENT_DLL );
}


BOOL
LoadAndInitFeClient(
    VOID
    )

/*++

Routine Description:

    This routine finds the name of the proper client dll (by some as of
    yet unspecified means) and proceeds to load it and initialize it.

Arguments:

    None.

Return Value:

    TRUE on success, FALSE on failure.  Callers may call GetLastError()
    for more error information.

--*/
{
    LPWSTR FeClientDllName;
    LPFEAPI_CLIENT_INITIALIZE ClientInitRoutine;
    BOOL Inited;

    //
    // GetFeClientDll returns a hard coded name.
    // If we get this name dynamically later, we will
    // need to free FeClientDllName.
    //


    FeClientDllName = GetFeClientDll();

    EnterCriticalSection(&FeClientLoadCritical);
    if (FeClientInfo) {
       LeaveCriticalSection(&FeClientLoadCritical);
       return( TRUE );
    }
    if (FeClientDllName) {
        FeClientModule = LoadLibraryW( FeClientDllName );
        if (FeClientModule == NULL) {
            DbgPrint("Unable to load client dll, error = %d\n",GetLastError());
            LeaveCriticalSection(&FeClientLoadCritical);
            return( FALSE );
        }
    }

    ClientInitRoutine = (LPFEAPI_CLIENT_INITIALIZE) GetProcAddress( FeClientModule, (LPCSTR)"FeClientInitialize");



    if (NULL == ClientInitRoutine) {
        FreeLibrary( FeClientModule );
        DbgPrint("Unable to locate init routine, error = %d\n",GetLastError());
        LeaveCriticalSection(&FeClientLoadCritical);
        return( FALSE );
    }

    Inited = (*ClientInitRoutine)( FE_REVISION_1_0, &FeClientInfo );

    LeaveCriticalSection(&FeClientLoadCritical);
    if (!Inited) {
        FreeLibrary( FeClientModule );
        return( FALSE );
    }

    return( TRUE );
}

BOOL
WINAPI
EncryptFileA (
    LPCSTR lpFileName
    )
/*++

Routine Description:

    ANSI Stub to EncryptFileW

Arguments:

    lpFileName - The name of the file to be encrypted.

Return Value:

    TRUE on success, FALSE on failure.  Callers may call GetLastError()
    for more information.

--*/
{
    UNICODE_STRING Unicode;
    WCHAR UnicodeBuffer[STATIC_UNICODE_BUFFER_LENGTH];
    ANSI_STRING AnsiString;
    NTSTATUS Status;

    Unicode.Length = 0;
    Unicode.MaximumLength = STATIC_UNICODE_BUFFER_LENGTH * sizeof( WCHAR );
    Unicode.Buffer = UnicodeBuffer;

    RtlInitAnsiString(&AnsiString,lpFileName);
    Status = RtlAnsiStringToUnicodeString(&Unicode,&AnsiString,FALSE);

    if ( !NT_SUCCESS(Status) ) {
        if ( Status == STATUS_BUFFER_OVERFLOW ) {
            SetLastError(ERROR_FILENAME_EXCED_RANGE);
        } else {
            BaseSetLastNTError(Status);
        }
        return FALSE;
    }

    return ( EncryptFileW( Unicode.Buffer ));

}


BOOL
WINAPI
EncryptFileW (
    LPCWSTR lpFileName
    )
/*++

Routine Description:

    Win32 EncryptFile API

Arguments:

    lpFileName - Supplies the name of the file to be encrypted.

Return Value:

    TRUE on success, FALSE on failure.  Callers may call GetLastError()
    for more information.

--*/
{
    BOOL rc;
    DWORD Result;

    //
    // See if the module has been loaded, and if not, load it into this
    // process.
    //

    if (FeClientInfo == NULL) {
        rc = LoadAndInitFeClient();
        if (!rc) {
            return(rc);
        }
    }

    Result = FeClientInfo->lpServices->EncryptFile( lpFileName );

    if (ERROR_SUCCESS != Result) {
        SetLastError( Result );
        return( FALSE );
    }

    return( TRUE );
}

BOOL
WINAPI
DecryptFileA (
    IN LPCSTR lpFileName,
    IN DWORD  dwRecovery
    )
/*++

Routine Description:

    ANSI Stub for the DecryptFileW API

Arguments:

    lpFileName - Supplies the name of the file to be decrypted.

    dwRecover - Supplies whether this is a recovery operation or a
        normal decryption operation.

Return Value:

    TRUE on success, FALSE on failure.  Callers may call GetLastError()
    for more information.

--*/
{
    UNICODE_STRING Unicode;
    WCHAR UnicodeBuffer[STATIC_UNICODE_BUFFER_LENGTH];
    ANSI_STRING AnsiString;
    NTSTATUS Status;

    Unicode.Length = 0;
    Unicode.MaximumLength = STATIC_UNICODE_BUFFER_LENGTH * sizeof( WCHAR );
    Unicode.Buffer = UnicodeBuffer;

    RtlInitAnsiString(&AnsiString,lpFileName);
    Status = RtlAnsiStringToUnicodeString(&Unicode,&AnsiString,FALSE);

    if ( !NT_SUCCESS(Status) ) {
        if ( Status == STATUS_BUFFER_OVERFLOW ) {
            SetLastError(ERROR_FILENAME_EXCED_RANGE);
        } else {
            BaseSetLastNTError(Status);
        }
        return FALSE;
    }

    return ( DecryptFileW( Unicode.Buffer, dwRecovery ));
}


BOOL
WINAPI
DecryptFileW (
    IN LPCWSTR lpFileName,
    IN DWORD   dwRecovery
    )
/*++

Routine Description:

    Win32 DecryptFile API

Arguments:

    lpFileName - Supplies the name of the file to be encrypted.

Return Value:

    TRUE on success, FALSE on failure.  Callers may call GetLastError()
    for more information.

--*/
{
    BOOL rc;
    DWORD Result;

    //
    // See if the module has been loaded, and if not, load it into this
    // process.
    //

    if (FeClientInfo == NULL) {
        rc = LoadAndInitFeClient();
        if (!rc) {
            return(rc);
        }
    }

    Result = FeClientInfo->lpServices->DecryptFile( lpFileName, dwRecovery );

    if (ERROR_SUCCESS != Result) {
        SetLastError( Result );
        return( FALSE );
    }

    return( TRUE );

}

BOOL
WINAPI
FileEncryptionStatusA (
    LPCSTR    lpFileName,
    LPDWORD   lpStatus
    )
/*++

Routine Description:

    ANSI Stub to FileEncryptionStatusW

Arguments:

    lpFileName - The name of the file to be checked.
    lpStatus - The status of the file.

Return Value:

    TRUE on success, FALSE on failure. Callers may call GetLastError() for more information.

--*/
{
    ANSI_STRING AnsiString;
    NTSTATUS Status;
    UNICODE_STRING Unicode;
    WCHAR UnicodeBuffer[STATIC_UNICODE_BUFFER_LENGTH];

    Unicode.Length = 0;
    Unicode.MaximumLength = STATIC_UNICODE_BUFFER_LENGTH * sizeof( WCHAR );
    Unicode.Buffer = UnicodeBuffer;

    RtlInitAnsiString(&AnsiString,lpFileName);
    Status = RtlAnsiStringToUnicodeString(&Unicode,&AnsiString,FALSE);

    if ( !NT_SUCCESS(Status) ) {
        if ( Status == STATUS_BUFFER_OVERFLOW ) {
            SetLastError(ERROR_FILENAME_EXCED_RANGE);
        } else {
            BaseSetLastNTError(Status);
        }
        return FALSE;
    }

    return ( FileEncryptionStatusW( Unicode.Buffer, lpStatus ));

}

BOOL
WINAPI
FileEncryptionStatusW (
    LPCWSTR    lpFileName,
    LPDWORD    lpStatus
    )
/*++

Routine Description:

    Win32 FileEncryptionStatus API

Arguments:

    lpFileName - Supplies the name of the file to be encrypted.
    lpStatus - The status of the file.

Return Value:

    TRUE on success, FALSE on failure. Callers may call GetLastError()
    for more information.

--*/
{

    BOOL rc;
    DWORD Result;

    //
    // See if the module has been loaded, and if not, load it into this
    // process.
    //

    if (FeClientInfo == NULL) {
        rc = LoadAndInitFeClient();
        if (!rc) {
            return(rc);
        }
    }

    return (FeClientInfo->lpServices->FileEncryptionStatus( lpFileName, lpStatus ));

}

DWORD
WINAPI
OpenEncryptedFileRawA(
    LPCSTR          lpFileName,
    ULONG           Flags,
    PVOID *         Context
    )
{
    ANSI_STRING AnsiString;
    NTSTATUS Status;
    UNICODE_STRING Unicode;
    WCHAR UnicodeBuffer[STATIC_UNICODE_BUFFER_LENGTH];

    Unicode.Length = 0;
    Unicode.MaximumLength = STATIC_UNICODE_BUFFER_LENGTH * sizeof( WCHAR );
    Unicode.Buffer = UnicodeBuffer;

    RtlInitAnsiString(&AnsiString,lpFileName);
    Status = RtlAnsiStringToUnicodeString(&Unicode,&AnsiString,FALSE);

    if ( !NT_SUCCESS(Status) ) {
        if ( Status == STATUS_BUFFER_OVERFLOW ) {
            SetLastError(ERROR_FILENAME_EXCED_RANGE);
        } else {
            BaseSetLastNTError(Status);
        }
        return FALSE;
    }

    return ( OpenEncryptedFileRawW( Unicode.Buffer, Flags, Context ));
}




DWORD
WINAPI
OpenEncryptedFileRawW(
    LPCWSTR         lpFileName,
    ULONG           Flags,
    PVOID *         Context
    )
{
    BOOL rc;
    DWORD Result;

    //
    // See if the module has been loaded, and if not, load it into this
    // process.
    //

    if (FeClientInfo == NULL) {
        rc = LoadAndInitFeClient();
        if (!rc) {
            return(GetLastError());
        }
    }

    return (FeClientInfo->lpServices->OpenFileRaw( lpFileName, Flags, Context ));
}


DWORD
WINAPI
ReadEncryptedFileRaw(
    PFE_EXPORT_FUNC ExportCallback,
    PVOID           CallbackContext,
    PVOID           Context
    )
{
    //
    // It doesn't make sense to call this before calling OpenRaw, so don't
    // bother checking to see if the module is loaded or not.  We'll fault
    // in the user process if it isn't.
    //

    return (FeClientInfo->lpServices->ReadFileRaw( ExportCallback, CallbackContext, Context ));
}

DWORD
WINAPI
WriteEncryptedFileRaw(
    PFE_IMPORT_FUNC ImportCallback,
    PVOID           CallbackContext,
    PVOID           Context
    )
{
    //
    // It doesn't make sense to call this before calling OpenRaw, so don't
    // bother checking to see if the module is loaded or not.  We'll fault
    // in the user process if it isn't.
    //

    return (FeClientInfo->lpServices->WriteFileRaw( ImportCallback, CallbackContext, Context ));
}

VOID
WINAPI
CloseEncryptedFileRaw(
    PVOID           Context
    )
{
    FeClientInfo->lpServices->CloseFileRaw( Context );

    return;
}


DWORD
QueryUsersOnEncryptedFile(
    IN  LPCWSTR lpFileName,
    OUT PENCRYPTION_CERTIFICATE_HASH_LIST * pUsers
    )
/*++

Routine Description:

    Win32 interface for adding users to an encrypted file.

Arguments:

    lpFileName - Supplies the name of the file to be modified.

    pUsers - Returns a list of users on the file.  This parameter
        must be passed to FreeEncryptionCertificateHashList() when
        no longer needed.

Return Value:

    Win32 error.

--*/

{
    DWORD rc;

    //
    // See if the module has been loaded, and if not, load it into this
    // process.
    //

    if (FeClientInfo == NULL) {
        rc = LoadAndInitFeClient();
        if (!rc) {
            return(rc);
        }
    }

    if ((lpFileName != NULL) && (pUsers != NULL)) {
        return(FeClientInfo->lpServices->QueryUsers( lpFileName, pUsers ));
    } else {
        return( ERROR_INVALID_PARAMETER );
    }
}


VOID
FreeEncryptionCertificateHashList(
    IN PENCRYPTION_CERTIFICATE_HASH_LIST pUsers
    )
/*++

Routine Description:

    Frees a certificate hash list as returned by QueryUsersOnEncryptedFile()
    and QueryRecoveryAgentsOnEncryptedFile().

Arguments:

    Supplies a list of users returned from QueryUsersOnEncryptedFile().

Return Value:

    Win32 error.

--*/
{

    //
    // It is probably safe to assume that feclient.dll is loaded,
    // since we wouldn't have one of these structures to free
    // if it weren't.
    //

    if (pUsers != NULL) {
        FeClientInfo->lpServices->FreeCertificateHashList( pUsers );
    } else {

        //
        // nothing to do
        //
    }

    return;
}


DWORD
QueryRecoveryAgentsOnEncryptedFile(
    IN  LPCWSTR lpFileName,
    OUT PENCRYPTION_CERTIFICATE_HASH_LIST * pRecoveryAgents
    )
/*++

Routine Description:

    This routine returns a list of recovery agents on an encrypted
    file.

Arguments:

    lpFileName - Supplies the name of the file to be examined.

    pRecoveryAgents - Returns a list of recovery agents, represented
        by certificate hashes on the file.  This list should be freed
        by calling FreeEncryptionCertificateHashList().

Return Value:

    return-value - Description of conditions needed to return value. - or -
    None.

--*/
{
    DWORD rc;

    //
    // See if the module has been loaded, and if not, load it into this
    // process.
    //

    if (FeClientInfo == NULL) {
        rc = LoadAndInitFeClient();
        if (!rc) {
            return(rc);
        }
    }

    if ((lpFileName != NULL) && (pRecoveryAgents != NULL)) {
        return(FeClientInfo->lpServices->QueryRecoveryAgents( lpFileName, pRecoveryAgents ));
    } else {
        return( ERROR_INVALID_PARAMETER );
    }
}


DWORD
RemoveUsersFromEncryptedFile(
    IN LPCWSTR lpFileName,
    IN PENCRYPTION_CERTIFICATE_HASH_LIST pHashes
    )
/*++

Routine Description:

    Takes a list of certificate hashes to be removed
    from the passed file.  Any that are found are removed,
    the rest are ignored with no error return.

Arguments:

    lpFileName - Supplies the name of the file to be modified.

    pHashes - Supplies the list of hashes to be removed.

Return Value:

    Win32 Error

--*/
{
    DWORD rc;

    //
    // See if the module has been loaded, and if not, load it into this
    // process.
    //

    if (FeClientInfo == NULL) {
        rc = LoadAndInitFeClient();
        if (!rc) {
            return(rc);
        }
    }

    if ((lpFileName != NULL) && (pHashes != NULL)) {
        return(FeClientInfo->lpServices->RemoveUsers( lpFileName, pHashes ));
    } else {
        return( ERROR_INVALID_PARAMETER );
    }
}

DWORD
AddUsersToEncryptedFile(
    IN LPCWSTR lpFileName,
    IN PENCRYPTION_CERTIFICATE_LIST pEncryptionCertificates
    )
/*++

Routine Description:

    This routine adds user keys to the passed encrypted file.

Arguments:

    lpFileName - Supplies the name of the file to be encrypted.

    pEncryptionCertificates - Supplies the list of certificates for
        new users to be added to the file.

Return Value:

    Win32 Error

--*/
{
    DWORD rc;

    //
    // See if the module has been loaded, and if not, load it into this
    // process.
    //

    if (FeClientInfo == NULL) {
        rc = LoadAndInitFeClient();
        if (!rc) {
            return(rc);
        }
    }

    if ((lpFileName != NULL) && (pEncryptionCertificates != NULL)) {
        return(FeClientInfo->lpServices->AddUsers( lpFileName, pEncryptionCertificates ));
    } else {
        return( ERROR_INVALID_PARAMETER );
    }
}

DWORD
SetUserFileEncryptionKey(
    PENCRYPTION_CERTIFICATE pEncryptionCertificate
    )
/*++

Routine Description:

    This routine will set the user's current EFS key to the one
    contained in the passed certificate.  If no certificate is
    passed, a new key will be generated automatically.

Arguments:

    pEncryptionCertificate - Optionally supplies the certificate
        containing the new public key.

Return Value:

    Win32 error

--*/
{
    DWORD rc;

    //
    // See if the module has been loaded, and if not, load it into this
    // process.
    //

    if (FeClientInfo == NULL) {
        rc = LoadAndInitFeClient();
        if (!rc) {
            return(rc);
        }
    }

    return(FeClientInfo->lpServices->SetKey( pEncryptionCertificate ));

    /*
    if (pEncryptionCertificate != NULL) {
        return(FeClientInfo->lpServices->SetKey( pEncryptionCertificate ));
    } else {
        return( ERROR_INVALID_PARAMETER );
    }*/
}

DWORD
DuplicateEncryptionInfoFile(
     IN LPCWSTR SrcFileName,
     IN LPCWSTR DstFileName, 
     IN DWORD dwCreationDistribution, 
     IN DWORD dwAttributes, 
     IN LPSECURITY_ATTRIBUTES lpSecurityAttributes
     )
/*++

Routine Description:

    This routine duplicates the encryption information from the source file to the
    destination file. Destination file will be created if not existing.

    The destination file is overwritten.

Arguments:

    SrcFileName - Supplies the source of the encryption information.

    DstFileName - Supplies the target file, exclusive open is required on this file.
    
    dwCreationDistribution - Create options. 
                             If dwCreationDistribution != CREATE_NEW, dwCreationDistribution = CREATE_ALWAYS
                             
    dwAttributes - File attributes.
    
    lpSecurityAttributes - Security attributes.
    
    

Return Value:

    Win32 error on failure.

--*/

{
    DWORD rc;

    if (FeClientModule == NULL) {
        rc = LoadAndInitFeClient();
        if (!rc) {
            return(rc);
        }
    }

    if (SrcFileName && DstFileName) {
        return(FeClientInfo->lpServices->DuplicateEncryptionInfo( SrcFileName, 
                                                                  DstFileName, 
                                                                  dwCreationDistribution,
                                                                  dwAttributes,
                                                                  lpSecurityAttributes
                                                                 ));
    } else {
        return( ERROR_INVALID_PARAMETER );
    }
}


BOOL
WINAPI
EncryptionDisable(
    IN LPCWSTR DirPath,
    IN BOOL Disable
    )

/*++

Routine Description:

    This routine disable and enable EFS in the directory DirPath.
        
Arguments:

    DirPath - Directory path.

    Disable - TRUE to disable
    

Return Value:

    TRUE for SUCCESS

--*/
{
    DWORD rc;

    //
    // See if the module has been loaded, and if not, load it into this
    // process.
    //

    if (FeClientInfo == NULL) {
        rc = LoadAndInitFeClient();
        if (!rc) {
            return(rc);
        }
    }

    return(FeClientInfo->lpServices->DisableDir( DirPath, Disable ));

}


WINADVAPI
DWORD
WINAPI
EncryptedFileKeyInfo(
    IN  LPCWSTR lpFileName,
    IN  DWORD   InfoClass,
    OUT PEFS_RPC_BLOB * KeyInfo
    )
/*++

Routine Description:

    Win32 interface for adding users to an encrypted file.

Arguments:

    lpFileName - Supplies the name of the file to be modified.
    
    InfoClass  - Information requested. Only support 1 for now.

    KeyInfo - Returns Key info
    
Return Value:

    Win32 error.

--*/

{
    DWORD rc;

    //
    // See if the module has been loaded, and if not, load it into this
    // process.
    //

    if (FeClientInfo == NULL) {
        rc = LoadAndInitFeClient();
        if (!rc) {
            return(rc);
        }
    }

    if ((lpFileName != NULL) && (KeyInfo != NULL)) {
        return(FeClientInfo->lpServices->GetKeyInfo( lpFileName, InfoClass, KeyInfo ));
    } else {
        return( ERROR_INVALID_PARAMETER );
    }
}



WINADVAPI
VOID
WINAPI
FreeEncryptedFileKeyInfo(
    IN PEFS_RPC_BLOB pKeyInfo
    )
/*++

Routine Description:

    Frees a KeyInfo as returned by EncryptedFileKeyInfo();
    
Arguments:

    pKeyInfo - Supplies a KeyInfo returned from EncryptedFileKeyInfo().

Return Value:

    No.

--*/
{

    //
    // It is probably safe to assume that feclient.dll is loaded,
    // since we wouldn't have one of these structures to free
    // if it weren't.
    //

    if (pKeyInfo != NULL) {
        FeClientInfo->lpServices->FreeKeyInfo( pKeyInfo );
    } else {

        //
        // nothing to do
        //
    }

    return;
}
