3840 lines
96 KiB
C
3840 lines
96 KiB
C
/*++
|
||
|
||
Copyright (c) 1997-1999 Microsoft Corporation
|
||
|
||
Module Name:
|
||
|
||
cipher.c
|
||
|
||
Abstract:
|
||
|
||
This module implements the encryption utility for encrypted
|
||
NTFS files.
|
||
|
||
Author:
|
||
|
||
Robert Reichel [RobertRe] 28-Feb-1997
|
||
Robert Gu [RobertG] 24-Mar-1998
|
||
|
||
Revision History:
|
||
|
||
Code reused from compact.exe, file compression utility
|
||
|
||
|
||
--*/
|
||
|
||
//
|
||
// Include the standard header files.
|
||
//
|
||
|
||
//#define UNICODE
|
||
//#define _UNICODE
|
||
|
||
#include <windows.h>
|
||
#include <stdio.h>
|
||
|
||
#include <winioctl.h>
|
||
#include <shellapi.h>
|
||
#include <winefs.h>
|
||
#include <malloc.h>
|
||
|
||
#include <rc4.h>
|
||
#include <randlib.h> // NewGenRandom() - Win2k and whistler
|
||
#include <rpc.h>
|
||
#include <wincrypt.h>
|
||
|
||
#include "support.h"
|
||
#include "msg.h"
|
||
|
||
#define lstrchr wcschr
|
||
#define lstricmp _wcsicmp
|
||
#define lstrnicmp _wcsnicmp
|
||
|
||
//
|
||
// FIRST_COLUMN_WIDTH - When encrypting files, the width of the output
|
||
// column which displays the file name
|
||
//
|
||
|
||
#define FIRST_COLUMN_WIDTH (20)
|
||
#define ENUMPATHLENGTH (4096)
|
||
#define DosDriveLimitCount (26)
|
||
|
||
#define PASSWORDLEN 1024
|
||
#define UserChooseYes 0
|
||
#define UserChooseNo 1
|
||
#define ChoiceNotDefined 3
|
||
|
||
#define KEYPATH TEXT("Software\\Microsoft\\Windows NT\\CurrentVersion\\EFS\\CurrentKeys")
|
||
#define KEYPATHROOT HKEY_CURRENT_USER
|
||
#define CERT_HASH TEXT("CertificateHash")
|
||
|
||
#define WIPING_DIR TEXT("EFSTMPWP\\")
|
||
|
||
#define RANDOM_BYTES(pv, cb) NewGenRandom(NULL, NULL, pv, cb)
|
||
|
||
#define YEARCOUNT (LONGLONG) 10000000*3600*24*365 // One Year's tick count
|
||
|
||
//
|
||
// Local data structure
|
||
//
|
||
|
||
typedef struct _CIPHER_VOLUME_INFO {
|
||
LPWSTR VolumeName[DosDriveLimitCount];
|
||
LPWSTR DosDeviceName[DosDriveLimitCount];
|
||
} CIPHER_VOLUME_INFO, *PCIPHER_VOLUME_INFO;
|
||
|
||
//
|
||
//
|
||
// definitions for initializing and working with random fill data.
|
||
//
|
||
|
||
typedef struct {
|
||
RC4_KEYSTRUCT Key;
|
||
CRITICAL_SECTION Lock;
|
||
BOOL LockValid;
|
||
PBYTE pbRandomFill;
|
||
DWORD cbRandomFill;
|
||
LONG cbFilled;
|
||
BOOLEAN fRandomFill; // is fill randomized?
|
||
} SECURE_FILL_INFO, *PSECURE_FILL_INFO;
|
||
|
||
// Local procedure types
|
||
//
|
||
|
||
typedef BOOLEAN (*PACTION_ROUTINE) (
|
||
IN PTCHAR DirectorySpec,
|
||
IN PTCHAR FileSpec
|
||
);
|
||
|
||
typedef VOID (*PFINAL_ACTION_ROUTINE) (
|
||
);
|
||
|
||
|
||
//
|
||
// Declare global variables to hold the command line information
|
||
//
|
||
|
||
BOOLEAN DoSubdirectories = FALSE; // recurse
|
||
BOOLEAN IgnoreErrors = FALSE; // keep going despite errs
|
||
BOOLEAN UserSpecifiedFileSpec = FALSE;
|
||
BOOLEAN ForceOperation = FALSE; // encrypt/decrypt even if already so
|
||
BOOLEAN Quiet = FALSE; // be less verbose
|
||
BOOLEAN DisplayAllFiles = FALSE; // dsply hidden, system?
|
||
BOOLEAN DoFiles = FALSE; // operation for files "/a"
|
||
BOOLEAN SetUpNewUserKey = FALSE; // Set up new user key
|
||
BOOLEAN RefreshUserKeyOnFiles = FALSE; // Refresh User Key on EFS files
|
||
BOOLEAN DisplayFilesOnly = FALSE; // Do not refresh $EFS, just display the file names
|
||
BOOLEAN FillUnusedSpace = FALSE; // Fill unused disk space with random data
|
||
BOOLEAN GenerateDRA = FALSE; // Generate Data Recovery Certificate files
|
||
TCHAR StartingDirectory[MAX_PATH]; // parameter to "/s"
|
||
ULONG AttributesNoDisplay = FILE_ATTRIBUTE_SYSTEM | FILE_ATTRIBUTE_HIDDEN;
|
||
|
||
BOOLEAN DisplayUseOptionW = FALSE;
|
||
|
||
//
|
||
// Declare global variables to hold encryption statistics
|
||
//
|
||
|
||
LARGE_INTEGER TotalDirectoryCount;
|
||
LARGE_INTEGER TotalFileCount;
|
||
|
||
TCHAR Buf[1024]; // for displaying stuff
|
||
|
||
SECURE_FILL_INFO GlobalSecureFill;
|
||
BOOLEAN GlobalSecureFillInitialized;
|
||
|
||
#if 0
|
||
#define TestOutPut
|
||
#endif
|
||
|
||
//
|
||
// Now do the routines to list the encryption state and size of
|
||
// a file and/or directory
|
||
//
|
||
|
||
BOOLEAN
|
||
DisplayFile (
|
||
IN PTCHAR FileSpec,
|
||
IN PWIN32_FIND_DATA FindData
|
||
)
|
||
{
|
||
TCHAR PrintState;
|
||
|
||
|
||
//
|
||
// Decide if the file is compressed and if so then
|
||
// get the compressed file size.
|
||
//
|
||
|
||
if (FindData->dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) {
|
||
|
||
PrintState = 'E';
|
||
|
||
} else {
|
||
|
||
PrintState = 'U';
|
||
}
|
||
|
||
//
|
||
// Print out the encryption state and file name
|
||
//
|
||
|
||
if (!Quiet &&
|
||
(DisplayAllFiles ||
|
||
(0 == (FindData->dwFileAttributes & AttributesNoDisplay)))) {
|
||
|
||
swprintf(Buf, TEXT("%c %s",), PrintState, FindData->cFileName);
|
||
DisplayMsg(CIPHER_THROW_NL, Buf);
|
||
}
|
||
|
||
TotalFileCount.QuadPart += 1;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
|
||
BOOLEAN
|
||
DoListAction (
|
||
IN PTCHAR DirectorySpec,
|
||
IN PTCHAR FileSpec
|
||
)
|
||
|
||
{
|
||
PTCHAR DirectorySpecEnd;
|
||
|
||
//
|
||
// So that we can keep on appending names to the directory spec
|
||
// get a pointer to the end of its string
|
||
//
|
||
|
||
DirectorySpecEnd = DirectorySpec + lstrlen(DirectorySpec);
|
||
|
||
//
|
||
// List the encryption attribute for the directory
|
||
//
|
||
|
||
{
|
||
ULONG Attributes;
|
||
|
||
if (!Quiet || Quiet) {
|
||
|
||
Attributes = GetFileAttributes( DirectorySpec );
|
||
|
||
if (0xFFFFFFFF == Attributes) {
|
||
|
||
if (!Quiet || !IgnoreErrors) {
|
||
|
||
//
|
||
// Refrain from displaying error only when in quiet
|
||
// mode *and* we're ignoring errors.
|
||
//
|
||
|
||
DisplayErr(DirectorySpec, GetLastError());
|
||
}
|
||
|
||
if (!IgnoreErrors) {
|
||
return FALSE;
|
||
}
|
||
} else {
|
||
|
||
if (Attributes & FILE_ATTRIBUTE_ENCRYPTED) {
|
||
DisplayMsg(CIPHER_LIST_EDIR, DirectorySpec);
|
||
} else {
|
||
DisplayMsg(CIPHER_LIST_UDIR, DirectorySpec);
|
||
}
|
||
}
|
||
}
|
||
|
||
TotalDirectoryCount.QuadPart += 1;
|
||
}
|
||
|
||
//
|
||
// Now for every file in the directory that matches the file spec we will
|
||
// will open the file and list its encryption state
|
||
//
|
||
|
||
{
|
||
HANDLE FindHandle;
|
||
WIN32_FIND_DATA FindData;
|
||
|
||
//
|
||
// setup the template for findfirst/findnext
|
||
//
|
||
|
||
//
|
||
// Make sure we don't try any paths that are too long for us
|
||
// to deal with.
|
||
//
|
||
|
||
if (((DirectorySpecEnd - DirectorySpec) + lstrlen( FileSpec )) <
|
||
MAX_PATH) {
|
||
|
||
lstrcpy( DirectorySpecEnd, FileSpec );
|
||
|
||
FindHandle = FindFirstFile( DirectorySpec, &FindData );
|
||
|
||
if (INVALID_HANDLE_VALUE != FindHandle) {
|
||
|
||
do {
|
||
|
||
//
|
||
// append the found file to the directory spec and open the
|
||
// file
|
||
//
|
||
|
||
if (0 == lstrcmp(FindData.cFileName, TEXT("..")) ||
|
||
0 == lstrcmp(FindData.cFileName, TEXT("."))) {
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// Make sure we don't try any paths that are too long for us
|
||
// to deal with.
|
||
//
|
||
|
||
if ((DirectorySpecEnd - DirectorySpec) +
|
||
lstrlen( FindData.cFileName ) >= MAX_PATH ) {
|
||
|
||
continue;
|
||
}
|
||
|
||
lstrcpy( DirectorySpecEnd, FindData.cFileName );
|
||
|
||
//
|
||
// Now print out the state of the file
|
||
//
|
||
|
||
DisplayFile( DirectorySpec, &FindData );
|
||
|
||
} while ( FindNextFile( FindHandle, &FindData ));
|
||
|
||
FindClose( FindHandle );
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// For if we are to do subdirectores then we will look for every
|
||
// subdirectory and recursively call ourselves to list the subdirectory
|
||
//
|
||
|
||
if (DoSubdirectories) {
|
||
|
||
HANDLE FindHandle;
|
||
|
||
WIN32_FIND_DATA FindData;
|
||
|
||
//
|
||
// Setup findfirst/findnext to search the entire directory
|
||
//
|
||
|
||
if (((DirectorySpecEnd - DirectorySpec) + lstrlen( TEXT("*") )) <
|
||
MAX_PATH) {
|
||
|
||
lstrcpy( DirectorySpecEnd, TEXT("*") );
|
||
|
||
FindHandle = FindFirstFile( DirectorySpec, &FindData );
|
||
|
||
if (INVALID_HANDLE_VALUE != FindHandle) {
|
||
|
||
do {
|
||
|
||
//
|
||
// Now skip over the . and .. entries otherwise we'll recurse
|
||
// like mad
|
||
//
|
||
|
||
if (0 == lstrcmp(&FindData.cFileName[0], TEXT(".")) ||
|
||
0 == lstrcmp(&FindData.cFileName[0], TEXT(".."))) {
|
||
|
||
continue;
|
||
|
||
} else {
|
||
|
||
//
|
||
// If the entry is for a directory then we'll tack on the
|
||
// subdirectory name to the directory spec and recursively
|
||
// call otherselves
|
||
//
|
||
|
||
if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||
|
||
//
|
||
// Make sure we don't try any paths that are too long for us
|
||
// to deal with.
|
||
//
|
||
|
||
if ((DirectorySpecEnd - DirectorySpec) +
|
||
lstrlen( TEXT("\\") ) +
|
||
lstrlen( FindData.cFileName ) >= MAX_PATH ) {
|
||
|
||
continue;
|
||
}
|
||
|
||
lstrcpy( DirectorySpecEnd, FindData.cFileName );
|
||
lstrcat( DirectorySpecEnd, TEXT("\\") );
|
||
|
||
if (!DoListAction( DirectorySpec, FileSpec )) {
|
||
|
||
FindClose( FindHandle );
|
||
return FALSE || IgnoreErrors;
|
||
}
|
||
}
|
||
}
|
||
|
||
} while ( FindNextFile( FindHandle, &FindData ));
|
||
|
||
FindClose( FindHandle );
|
||
}
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
VOID
|
||
DoFinalListAction (
|
||
)
|
||
{
|
||
return;
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
EncryptAFile (
|
||
IN PTCHAR FileSpec,
|
||
IN PWIN32_FIND_DATA FindData
|
||
)
|
||
|
||
{
|
||
USHORT State = 1;
|
||
ULONG i;
|
||
BOOL Success;
|
||
double f = 1.0;
|
||
|
||
if ((FindData->dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
|
||
!ForceOperation) {
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
// Success = DeviceIoControl(Handle, FSCTL_SET_COMPRESSION, &State,
|
||
// sizeof(USHORT), NULL, 0, &Length, FALSE );
|
||
|
||
if ( (0 == (FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) && (!DoFiles)) {
|
||
|
||
//
|
||
// Skip the files
|
||
//
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
Success = EncryptFile( FileSpec );
|
||
|
||
if (!Success) {
|
||
|
||
if (Quiet && IgnoreErrors) {
|
||
return FALSE || IgnoreErrors;
|
||
}
|
||
|
||
swprintf(Buf, TEXT("%s "), FindData->cFileName);
|
||
DisplayMsg(CIPHER_THROW, Buf);
|
||
|
||
for (i = lstrlen(FindData->cFileName) + 1; i < FIRST_COLUMN_WIDTH; ++i) {
|
||
swprintf(Buf, TEXT("%c"), ' ');
|
||
DisplayMsg(CIPHER_THROW, Buf);
|
||
}
|
||
|
||
DisplayMsg(CIPHER_ERR);
|
||
|
||
if (!Quiet && !IgnoreErrors) {
|
||
if (ERROR_INVALID_FUNCTION == GetLastError()) {
|
||
|
||
// This error is caused by doing the fsctl on a
|
||
// non-encrypting volume.
|
||
|
||
DisplayMsg(CIPHER_WRONG_FILE_SYSTEM, FindData->cFileName);
|
||
|
||
} else {
|
||
DisplayErr(FindData->cFileName, GetLastError());
|
||
}
|
||
}
|
||
|
||
return FALSE || IgnoreErrors;
|
||
}
|
||
|
||
if (!DisplayUseOptionW && ( 0 == (FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY))){
|
||
|
||
DisplayUseOptionW = TRUE;
|
||
|
||
}
|
||
|
||
if (!Quiet &&
|
||
(DisplayAllFiles ||
|
||
(0 == (FindData->dwFileAttributes & AttributesNoDisplay)))) {
|
||
swprintf(Buf, TEXT("%s "), FindData->cFileName);
|
||
DisplayMsg(CIPHER_THROW, Buf);
|
||
|
||
for (i = lstrlen(FindData->cFileName) + 1; i < FIRST_COLUMN_WIDTH; ++i) {
|
||
swprintf(Buf, TEXT("%c"), ' ');
|
||
DisplayMsg(CIPHER_THROW, Buf);
|
||
}
|
||
|
||
DisplayMsg(CIPHER_OK);
|
||
}
|
||
|
||
TotalFileCount.QuadPart += 1;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOLEAN
|
||
DoEncryptAction (
|
||
IN PTCHAR DirectorySpec,
|
||
IN PTCHAR FileSpec
|
||
)
|
||
|
||
{
|
||
PTCHAR DirectorySpecEnd;
|
||
|
||
//
|
||
// If the file spec is null then we'll set the encryption bit for the
|
||
// the directory spec and get out.
|
||
//
|
||
|
||
if (lstrlen(FileSpec) == 0) {
|
||
|
||
USHORT State = 1;
|
||
ULONG Length;
|
||
|
||
DisplayMsg(CIPHER_ENCRYPT_DIR, DirectorySpec);
|
||
|
||
if (!EncryptFile( DirectorySpec )) {
|
||
|
||
if (!Quiet || !IgnoreErrors) {
|
||
DisplayMsg(CIPHER_ERR);
|
||
}
|
||
if (!Quiet && !IgnoreErrors) {
|
||
DisplayErr(DirectorySpec, GetLastError());
|
||
}
|
||
return FALSE || IgnoreErrors;
|
||
}
|
||
|
||
if (!Quiet) {
|
||
DisplayMsg(CIPHER_OK);
|
||
}
|
||
|
||
TotalDirectoryCount.QuadPart += 1;
|
||
TotalFileCount.QuadPart += 1;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// So that we can keep on appending names to the directory spec
|
||
// get a pointer to the end of its string
|
||
//
|
||
|
||
DirectorySpecEnd = DirectorySpec + lstrlen( DirectorySpec );
|
||
|
||
//
|
||
// List the directory that we will be encrypting within and say what its
|
||
// current encryption attribute is.
|
||
//
|
||
|
||
{
|
||
ULONG Attributes;
|
||
|
||
if (!Quiet || Quiet) {
|
||
|
||
Attributes = GetFileAttributes( DirectorySpec );
|
||
|
||
if ( DoFiles ) {
|
||
|
||
if (Attributes & FILE_ATTRIBUTE_ENCRYPTED) {
|
||
|
||
|
||
DisplayMsg(CIPHER_ENCRYPT_EDIR, DirectorySpec);
|
||
|
||
} else {
|
||
|
||
DisplayMsg(CIPHER_ENCRYPT_UDIR, DirectorySpec);
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
if (Attributes & FILE_ATTRIBUTE_ENCRYPTED) {
|
||
|
||
|
||
DisplayMsg(CIPHER_ENCRYPT_EDIR_NF, DirectorySpec);
|
||
|
||
} else {
|
||
|
||
DisplayMsg(CIPHER_ENCRYPT_UDIR_NF, DirectorySpec);
|
||
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
TotalDirectoryCount.QuadPart += 1;
|
||
}
|
||
|
||
//
|
||
// Now for every file in the directory that matches the file spec we will
|
||
// will open the file and encrypt it
|
||
//
|
||
|
||
{
|
||
HANDLE FindHandle;
|
||
HANDLE FileHandle;
|
||
|
||
WIN32_FIND_DATA FindData;
|
||
|
||
//
|
||
// setup the template for findfirst/findnext
|
||
//
|
||
|
||
if (((DirectorySpecEnd - DirectorySpec) + lstrlen( FileSpec )) <
|
||
MAX_PATH) {
|
||
|
||
lstrcpy( DirectorySpecEnd, FileSpec );
|
||
|
||
FindHandle = FindFirstFile( DirectorySpec, &FindData );
|
||
|
||
if (INVALID_HANDLE_VALUE != FindHandle) {
|
||
|
||
do {
|
||
|
||
//
|
||
// Now skip over the . and .. entries
|
||
//
|
||
|
||
if (0 == lstrcmp(&FindData.cFileName[0], TEXT(".")) ||
|
||
0 == lstrcmp(&FindData.cFileName[0], TEXT(".."))) {
|
||
|
||
continue;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Make sure we don't try any paths that are too long for us
|
||
// to deal with.
|
||
//
|
||
|
||
if ( (DirectorySpecEnd - DirectorySpec) +
|
||
lstrlen( FindData.cFileName ) >= MAX_PATH ) {
|
||
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// append the found file to the directory spec and open
|
||
// the file
|
||
//
|
||
|
||
|
||
lstrcpy( DirectorySpecEnd, FindData.cFileName );
|
||
|
||
//
|
||
// Hack hack, kludge kludge. Refrain from compressing
|
||
// files named "\NTDLR" to help users avoid hosing
|
||
// themselves.
|
||
//
|
||
|
||
if (IsNtldr(DirectorySpec)) {
|
||
|
||
if (!Quiet) {
|
||
DisplayMsg(CIPHER_SKIPPING, DirectorySpecEnd);
|
||
}
|
||
|
||
continue;
|
||
}
|
||
|
||
EncryptAFile( DirectorySpec, &FindData );
|
||
|
||
}
|
||
|
||
} while ( FindNextFile( FindHandle, &FindData ));
|
||
|
||
FindClose( FindHandle );
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we are to do subdirectores then we will look for every subdirectory
|
||
// and recursively call ourselves to list the subdirectory
|
||
//
|
||
|
||
if (DoSubdirectories) {
|
||
|
||
HANDLE FindHandle;
|
||
|
||
WIN32_FIND_DATA FindData;
|
||
|
||
//
|
||
// Setup findfirst/findnext to search the entire directory
|
||
//
|
||
|
||
if (((DirectorySpecEnd - DirectorySpec) + lstrlen( TEXT("*") )) <
|
||
MAX_PATH) {
|
||
|
||
lstrcpy( DirectorySpecEnd, TEXT("*") );
|
||
|
||
FindHandle = FindFirstFile( DirectorySpec, &FindData );
|
||
|
||
if (INVALID_HANDLE_VALUE != FindHandle) {
|
||
|
||
do {
|
||
|
||
//
|
||
// Now skip over the . and .. entries otherwise we'll recurse
|
||
// like mad
|
||
//
|
||
|
||
if (0 == lstrcmp(&FindData.cFileName[0], TEXT(".")) ||
|
||
0 == lstrcmp(&FindData.cFileName[0], TEXT(".."))) {
|
||
|
||
continue;
|
||
|
||
} else {
|
||
|
||
//
|
||
// If the entry is for a directory then we'll tack on the
|
||
// subdirectory name to the directory spec and recursively
|
||
// call otherselves
|
||
//
|
||
|
||
if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||
|
||
//
|
||
// Make sure we don't try any paths that are too long for us
|
||
// to deal with.
|
||
//
|
||
|
||
if ((DirectorySpecEnd - DirectorySpec) +
|
||
lstrlen( TEXT("\\") ) +
|
||
lstrlen( FindData.cFileName ) >= MAX_PATH ) {
|
||
|
||
continue;
|
||
}
|
||
|
||
lstrcpy( DirectorySpecEnd, FindData.cFileName );
|
||
lstrcat( DirectorySpecEnd, TEXT("\\") );
|
||
|
||
if (!DoEncryptAction( DirectorySpec, FileSpec )) {
|
||
FindClose( FindHandle );
|
||
return FALSE || IgnoreErrors;
|
||
}
|
||
}
|
||
}
|
||
|
||
} while ( FindNextFile( FindHandle, &FindData ));
|
||
|
||
FindClose( FindHandle );
|
||
}
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
VOID
|
||
DoFinalEncryptAction (
|
||
)
|
||
{
|
||
TCHAR FileCount[32];
|
||
TCHAR DirectoryCount[32];
|
||
|
||
FormatFileSize(&TotalFileCount, 0, FileCount, FALSE);
|
||
FormatFileSize(&TotalDirectoryCount, 0, DirectoryCount, FALSE);
|
||
|
||
if ( DoFiles ) {
|
||
DisplayMsg(CIPHER_ENCRYPT_SUMMARY, FileCount, DirectoryCount);
|
||
if (DisplayUseOptionW) {
|
||
DisplayMsg(CIPHER_USE_W);
|
||
}
|
||
} else {
|
||
DisplayMsg(CIPHER_ENCRYPT_SUMMARY_NF, FileCount, DirectoryCount);
|
||
}
|
||
return;
|
||
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
DecryptAFile (
|
||
IN PTCHAR FileSpec,
|
||
IN PWIN32_FIND_DATA FindData
|
||
)
|
||
{
|
||
USHORT State = 0;
|
||
ULONG Length;
|
||
|
||
if (!(FindData->dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) &&
|
||
!ForceOperation) {
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
if ( (0 == (FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) && (!DoFiles)) {
|
||
|
||
//
|
||
// Skip the files
|
||
//
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
if (!DecryptFile(FileSpec, 0L )) {
|
||
|
||
if (!Quiet || !IgnoreErrors) {
|
||
|
||
swprintf(Buf, TEXT("%s "), FindData->cFileName);
|
||
DisplayMsg(CIPHER_THROW, Buf);
|
||
|
||
DisplayMsg(CIPHER_ERR);
|
||
|
||
if (!Quiet && !IgnoreErrors) {
|
||
|
||
if (ERROR_INVALID_FUNCTION == GetLastError()) {
|
||
|
||
// This error is caused by doing the fsctl on a
|
||
// non-compressing volume.
|
||
|
||
DisplayMsg(CIPHER_WRONG_FILE_SYSTEM, FindData->cFileName);
|
||
|
||
} else {
|
||
DisplayErr(FindData->cFileName, GetLastError());
|
||
}
|
||
}
|
||
}
|
||
return FALSE || IgnoreErrors;
|
||
}
|
||
|
||
if (!Quiet &&
|
||
(DisplayAllFiles ||
|
||
(0 == (FindData->dwFileAttributes & AttributesNoDisplay)))) {
|
||
swprintf(Buf, TEXT("%s "), FindData->cFileName);
|
||
DisplayMsg(CIPHER_THROW, Buf);
|
||
|
||
DisplayMsg(CIPHER_OK);
|
||
}
|
||
|
||
//
|
||
// Increment our running total
|
||
//
|
||
|
||
TotalFileCount.QuadPart += 1;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
BOOLEAN
|
||
DoDecryptAction (
|
||
IN PTCHAR DirectorySpec,
|
||
IN PTCHAR FileSpec
|
||
)
|
||
|
||
{
|
||
PTCHAR DirectorySpecEnd;
|
||
|
||
//
|
||
// If the file spec is null then we'll clear the encryption bit for the
|
||
// the directory spec and get out.
|
||
//
|
||
|
||
if (lstrlen(FileSpec) == 0) {
|
||
|
||
HANDLE FileHandle;
|
||
USHORT State = 0;
|
||
ULONG Length;
|
||
|
||
DisplayMsg(CIPHER_DECRYPT_DIR, DirectorySpec);
|
||
|
||
if (!DecryptFile( DirectorySpec, 0L )) {
|
||
|
||
if (!Quiet || !IgnoreErrors) {
|
||
DisplayMsg(CIPHER_ERR);
|
||
|
||
}
|
||
if (!Quiet && !IgnoreErrors) {
|
||
DisplayErr(DirectorySpec, GetLastError());
|
||
}
|
||
|
||
return FALSE || IgnoreErrors;
|
||
}
|
||
|
||
if (!Quiet) {
|
||
DisplayMsg(CIPHER_OK);
|
||
}
|
||
|
||
|
||
TotalDirectoryCount.QuadPart += 1;
|
||
TotalFileCount.QuadPart += 1;
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// So that we can keep on appending names to the directory spec
|
||
// get a pointer to the end of its string
|
||
//
|
||
|
||
DirectorySpecEnd = DirectorySpec + lstrlen( DirectorySpec );
|
||
|
||
//
|
||
// List the directory that we will be uncompressing within and say what its
|
||
// current compress attribute is
|
||
//
|
||
|
||
{
|
||
ULONG Attributes;
|
||
|
||
if (!Quiet || Quiet) {
|
||
|
||
Attributes = GetFileAttributes( DirectorySpec );
|
||
|
||
if ( DoFiles) {
|
||
|
||
if (Attributes & FILE_ATTRIBUTE_ENCRYPTED) {
|
||
|
||
DisplayMsg(CIPHER_DECRYPT_EDIR, DirectorySpec);
|
||
|
||
} else {
|
||
|
||
DisplayMsg(CIPHER_DECRYPT_UDIR, DirectorySpec);
|
||
}
|
||
|
||
} else {
|
||
|
||
if (Attributes & FILE_ATTRIBUTE_ENCRYPTED) {
|
||
|
||
DisplayMsg(CIPHER_DECRYPT_EDIR_NF, DirectorySpec);
|
||
|
||
} else {
|
||
|
||
DisplayMsg(CIPHER_DECRYPT_UDIR_NF, DirectorySpec);
|
||
}
|
||
|
||
}
|
||
}
|
||
|
||
TotalDirectoryCount.QuadPart += 1;
|
||
}
|
||
|
||
//
|
||
// Now for every file in the directory that matches the file spec we will
|
||
// will open the file and uncompress it
|
||
//
|
||
|
||
{
|
||
HANDLE FindHandle;
|
||
|
||
WIN32_FIND_DATA FindData;
|
||
|
||
//
|
||
// setup the template for findfirst/findnext
|
||
//
|
||
|
||
if (((DirectorySpecEnd - DirectorySpec) + lstrlen( FileSpec )) <
|
||
MAX_PATH) {
|
||
|
||
lstrcpy( DirectorySpecEnd, FileSpec );
|
||
|
||
FindHandle = FindFirstFile( DirectorySpec, &FindData );
|
||
|
||
if (INVALID_HANDLE_VALUE != FindHandle) {
|
||
|
||
do {
|
||
|
||
//
|
||
// Now skip over the . and .. entries
|
||
//
|
||
|
||
if (0 == lstrcmp(&FindData.cFileName[0], TEXT(".")) ||
|
||
0 == lstrcmp(&FindData.cFileName[0], TEXT(".."))) {
|
||
|
||
continue;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Make sure we don't try any paths that are too long for us
|
||
// to deal with.
|
||
//
|
||
|
||
if ((DirectorySpecEnd - DirectorySpec) +
|
||
lstrlen( FindData.cFileName ) >= MAX_PATH ) {
|
||
|
||
continue;
|
||
}
|
||
|
||
//
|
||
// append the found file to the directory spec and open
|
||
// the file
|
||
//
|
||
|
||
lstrcpy( DirectorySpecEnd, FindData.cFileName );
|
||
|
||
//
|
||
// Now decrypt the file
|
||
//
|
||
|
||
DecryptAFile( DirectorySpec, &FindData );
|
||
|
||
}
|
||
|
||
} while ( FindNextFile( FindHandle, &FindData ));
|
||
|
||
FindClose( FindHandle );
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// If we are to do subdirectores then we will look for every subdirectory
|
||
// and recursively call ourselves to list the subdirectory
|
||
//
|
||
|
||
if (DoSubdirectories) {
|
||
|
||
HANDLE FindHandle;
|
||
|
||
WIN32_FIND_DATA FindData;
|
||
|
||
//
|
||
// Setup findfirst/findnext to search the entire directory
|
||
//
|
||
|
||
if (((DirectorySpecEnd - DirectorySpec) + lstrlen( TEXT("*") )) <
|
||
MAX_PATH) {
|
||
|
||
lstrcpy( DirectorySpecEnd, TEXT("*") );
|
||
|
||
FindHandle = FindFirstFile( DirectorySpec, &FindData );
|
||
if (INVALID_HANDLE_VALUE != FindHandle) {
|
||
|
||
do {
|
||
|
||
//
|
||
// Now skip over the . and .. entries otherwise we'll recurse
|
||
// like mad
|
||
//
|
||
|
||
if (0 == lstrcmp(&FindData.cFileName[0], TEXT(".")) ||
|
||
0 == lstrcmp(&FindData.cFileName[0], TEXT(".."))) {
|
||
|
||
continue;
|
||
|
||
} else {
|
||
|
||
//
|
||
// If the entry is for a directory then we'll tack on the
|
||
// subdirectory name to the directory spec and recursively
|
||
// call otherselves
|
||
//
|
||
|
||
if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||
|
||
//
|
||
// Make sure we don't try any paths that are too long for us
|
||
// to deal with.
|
||
//
|
||
|
||
if ((DirectorySpecEnd - DirectorySpec) +
|
||
lstrlen( TEXT("\\") ) +
|
||
lstrlen( FindData.cFileName ) >= MAX_PATH ) {
|
||
|
||
continue;
|
||
}
|
||
|
||
lstrcpy( DirectorySpecEnd, FindData.cFileName );
|
||
lstrcat( DirectorySpecEnd, TEXT("\\") );
|
||
|
||
if (!DoDecryptAction( DirectorySpec, FileSpec )) {
|
||
FindClose( FindHandle );
|
||
return FALSE || IgnoreErrors;
|
||
}
|
||
}
|
||
}
|
||
|
||
} while ( FindNextFile( FindHandle, &FindData ));
|
||
|
||
FindClose( FindHandle );
|
||
}
|
||
}
|
||
}
|
||
|
||
return TRUE;
|
||
}
|
||
|
||
VOID
|
||
DoFinalDecryptAction (
|
||
)
|
||
|
||
{
|
||
TCHAR FileCount[32];
|
||
TCHAR DirectoryCount[32];
|
||
|
||
FormatFileSize(&TotalFileCount, 0, FileCount, FALSE);
|
||
FormatFileSize(&TotalDirectoryCount, 0, DirectoryCount, FALSE);
|
||
|
||
if (DoFiles) {
|
||
DisplayMsg(CIPHER_DECRYPT_SUMMARY, FileCount, DirectoryCount);
|
||
} else {
|
||
DisplayMsg(CIPHER_DECRYPT_SUMMARY_NF, FileCount, DirectoryCount);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
VOID
|
||
CipherConvertHashToStr(
|
||
IN PBYTE pHashData,
|
||
IN DWORD cbData,
|
||
OUT LPWSTR OutHashStr
|
||
)
|
||
{
|
||
|
||
DWORD Index = 0;
|
||
BOOLEAN NoLastZero = FALSE;
|
||
|
||
for (; Index < cbData; Index+=2) {
|
||
|
||
BYTE HashByteLow = pHashData[Index] & 0x0f;
|
||
BYTE HashByteHigh = (pHashData[Index] & 0xf0) >> 4;
|
||
|
||
OutHashStr[Index * 5/2] = HashByteHigh > 9 ? (WCHAR)(HashByteHigh - 9 + 0x40): (WCHAR)(HashByteHigh + 0x30);
|
||
OutHashStr[Index * 5/2 + 1] = HashByteLow > 9 ? (WCHAR)(HashByteLow - 9 + 0x40): (WCHAR)(HashByteLow + 0x30);
|
||
|
||
if (Index + 1 < cbData) {
|
||
HashByteLow = pHashData[Index+1] & 0x0f;
|
||
HashByteHigh = (pHashData[Index+1] & 0xf0) >> 4;
|
||
|
||
OutHashStr[Index * 5/2 + 2] = HashByteHigh > 9 ? (WCHAR)(HashByteHigh - 9 + 0x40): (WCHAR)(HashByteHigh + 0x30);
|
||
OutHashStr[Index * 5/2 + 3] = HashByteLow > 9 ? (WCHAR)(HashByteLow - 9 + 0x40): (WCHAR)(HashByteLow + 0x30);
|
||
|
||
OutHashStr[Index * 5/2 + 4] = L' ';
|
||
|
||
} else {
|
||
OutHashStr[Index * 5/2 + 2] = 0;
|
||
NoLastZero = TRUE;
|
||
}
|
||
|
||
}
|
||
|
||
if (!NoLastZero) {
|
||
OutHashStr[Index*5/2] = 0;
|
||
}
|
||
|
||
}
|
||
|
||
VOID
|
||
CipherDisplayCrntEfsHash(
|
||
)
|
||
{
|
||
|
||
DWORD rc;
|
||
HKEY hRegKey = NULL;
|
||
PBYTE pbHash;
|
||
DWORD cbHash;
|
||
|
||
DWORD nSize = MAX_COMPUTERNAME_LENGTH + 1;
|
||
WCHAR LocalComputerName[MAX_COMPUTERNAME_LENGTH + 1];
|
||
|
||
if (!GetComputerName ( LocalComputerName, &nSize )){
|
||
|
||
//
|
||
// This is not likely to happen.
|
||
//
|
||
|
||
return;
|
||
}
|
||
|
||
rc = RegOpenKeyEx(
|
||
KEYPATHROOT,
|
||
KEYPATH,
|
||
0,
|
||
GENERIC_READ,
|
||
&hRegKey
|
||
);
|
||
|
||
if (rc == ERROR_SUCCESS) {
|
||
|
||
DWORD Type;
|
||
|
||
rc = RegQueryValueEx(
|
||
hRegKey,
|
||
CERT_HASH,
|
||
NULL,
|
||
&Type,
|
||
NULL,
|
||
&cbHash
|
||
);
|
||
|
||
if (rc == ERROR_SUCCESS) {
|
||
|
||
//
|
||
// Query out the thumbprint, find the cert, and return the key information.
|
||
//
|
||
|
||
if (pbHash = (PBYTE)malloc( cbHash )) {
|
||
|
||
rc = RegQueryValueEx(
|
||
hRegKey,
|
||
CERT_HASH,
|
||
NULL,
|
||
&Type,
|
||
pbHash,
|
||
&cbHash
|
||
);
|
||
|
||
|
||
if (rc == ERROR_SUCCESS) {
|
||
|
||
LPWSTR OutHash;
|
||
|
||
|
||
OutHash = (LPWSTR) malloc(((((cbHash + 1)/2) * 5)+1) * sizeof(WCHAR));
|
||
if (OutHash) {
|
||
|
||
CipherConvertHashToStr(pbHash, cbHash, OutHash);
|
||
DisplayMsg(CIPHER_CURRENT_CERT, LocalComputerName, OutHash);
|
||
free(OutHash);
|
||
|
||
}
|
||
}
|
||
free(pbHash);
|
||
}
|
||
}
|
||
RegCloseKey( hRegKey );
|
||
}
|
||
return;
|
||
}
|
||
|
||
BOOL
|
||
CipherConvertToDriveLetter(
|
||
IN OUT LPWSTR VolBuffer,
|
||
IN PCIPHER_VOLUME_INFO VolumeInfo
|
||
)
|
||
{
|
||
WCHAR DeviceName[MAX_PATH];
|
||
WORD DriveIndex = 0;
|
||
|
||
while (DriveIndex < DosDriveLimitCount) {
|
||
if (VolumeInfo->VolumeName[DriveIndex]) {
|
||
if (!wcscmp(VolBuffer, VolumeInfo->VolumeName[DriveIndex])) {
|
||
lstrcpy(VolBuffer, TEXT("A:\\"));
|
||
VolBuffer[0] += DriveIndex;
|
||
return TRUE;
|
||
}
|
||
|
||
VolBuffer[48] = 0;
|
||
if (VolumeInfo->DosDeviceName[DriveIndex] && QueryDosDevice( &(VolBuffer[4]), DeviceName, MAX_PATH)) {
|
||
|
||
if (!wcscmp(DeviceName, VolumeInfo->DosDeviceName[DriveIndex])) {
|
||
lstrcpy(VolBuffer, TEXT("A:\\"));
|
||
VolBuffer[0] += DriveIndex;
|
||
return TRUE;
|
||
}
|
||
|
||
|
||
}
|
||
}
|
||
DriveIndex++;
|
||
}
|
||
|
||
return FALSE;
|
||
|
||
}
|
||
|
||
VOID
|
||
CipherTouchDirFiles(
|
||
IN WCHAR *DirPath,
|
||
IN PCIPHER_VOLUME_INFO VolumeInfo
|
||
)
|
||
{
|
||
|
||
PTCHAR DirectorySpecEnd;
|
||
HANDLE FindHandle;
|
||
WIN32_FIND_DATA FindData;
|
||
HANDLE hFile;
|
||
|
||
|
||
//
|
||
// So that we can keep on appending names to the directory spec
|
||
// get a pointer to the end of its string
|
||
//
|
||
|
||
|
||
DirectorySpecEnd = DirPath + lstrlen( DirPath );
|
||
|
||
|
||
|
||
//
|
||
// setup the template for findfirst/findnext
|
||
//
|
||
|
||
if ((DirectorySpecEnd - DirPath) < ENUMPATHLENGTH - 2* sizeof(WCHAR)) {
|
||
|
||
lstrcpy( DirectorySpecEnd, TEXT("*") );
|
||
|
||
FindHandle = FindFirstFile( DirPath, &FindData );
|
||
|
||
if (INVALID_HANDLE_VALUE != FindHandle) {
|
||
|
||
do {
|
||
|
||
//
|
||
// Now skip over the . and .. entries
|
||
//
|
||
|
||
if (0 == lstrcmp(&FindData.cFileName[0], TEXT(".")) ||
|
||
0 == lstrcmp(&FindData.cFileName[0], TEXT(".."))) {
|
||
|
||
continue;
|
||
|
||
} else {
|
||
|
||
//
|
||
// Make sure we don't try any paths that are too long for us
|
||
// to deal with.
|
||
//
|
||
|
||
if ((DirectorySpecEnd - DirPath) +
|
||
lstrlen( FindData.cFileName ) >= ENUMPATHLENGTH ) {
|
||
|
||
continue;
|
||
}
|
||
|
||
if ( !(FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) &&
|
||
(FindData.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED)) {
|
||
|
||
//
|
||
// append the found file to the directory spec and open
|
||
// the file
|
||
//
|
||
|
||
lstrcpy( DirectorySpecEnd, FindData.cFileName );
|
||
|
||
//
|
||
// Now touch the file
|
||
//
|
||
|
||
if (DisplayFilesOnly) {
|
||
//swprintf(Buf, TEXT("%s",), DirPath);
|
||
DisplayMsg(CIPHER_THROW_NL, DirPath);
|
||
} else {
|
||
|
||
hFile = CreateFileW(
|
||
DirPath,
|
||
GENERIC_READ,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE,
|
||
NULL,
|
||
OPEN_EXISTING,
|
||
0,
|
||
NULL
|
||
);
|
||
|
||
if ( INVALID_HANDLE_VALUE != hFile ){
|
||
|
||
DisplayMsg(CIPHER_TOUCH_OK, DirPath);
|
||
|
||
CloseHandle(hFile);
|
||
|
||
} else {
|
||
|
||
DisplayErr(DirPath, GetLastError());
|
||
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
}
|
||
|
||
} while ( FindNextFile( FindHandle, &FindData ));
|
||
|
||
FindClose( FindHandle );
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Setup findfirst/findnext to search the sub directory
|
||
//
|
||
|
||
if ((DirectorySpecEnd - DirPath) < ENUMPATHLENGTH - 2* sizeof(WCHAR)) {
|
||
|
||
lstrcpy( DirectorySpecEnd, TEXT("*") );
|
||
|
||
FindHandle = FindFirstFile( DirPath, &FindData );
|
||
if (INVALID_HANDLE_VALUE != FindHandle) {
|
||
|
||
do {
|
||
|
||
//
|
||
// Now skip over the . and .. entries otherwise we'll recurse
|
||
// like mad
|
||
//
|
||
|
||
if (0 == lstrcmp(&FindData.cFileName[0], TEXT(".")) ||
|
||
0 == lstrcmp(&FindData.cFileName[0], TEXT(".."))) {
|
||
|
||
continue;
|
||
|
||
} else {
|
||
|
||
//
|
||
// If the entry is for a directory then we'll tack on the
|
||
// subdirectory name to the directory spec and recursively
|
||
// call otherselves
|
||
//
|
||
|
||
if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
|
||
|
||
BOOL b;
|
||
WCHAR MountVolName[MAX_PATH];
|
||
|
||
//
|
||
// Make sure we don't try any paths that are too long for us
|
||
// to deal with.
|
||
//
|
||
|
||
if ((DirectorySpecEnd - DirPath) +
|
||
lstrlen( TEXT("\\") ) +
|
||
lstrlen( FindData.cFileName ) >= ENUMPATHLENGTH ) {
|
||
|
||
continue;
|
||
}
|
||
|
||
lstrcpy( DirectorySpecEnd, FindData.cFileName );
|
||
lstrcat( DirectorySpecEnd, TEXT("\\") );
|
||
|
||
//
|
||
// Check if this DIR point to another volume
|
||
//
|
||
|
||
|
||
b = GetVolumeNameForVolumeMountPoint(DirPath, MountVolName, MAX_PATH);
|
||
if (b) {
|
||
if (CipherConvertToDriveLetter(MountVolName, VolumeInfo)){
|
||
continue;
|
||
}
|
||
}
|
||
|
||
CipherTouchDirFiles(DirPath, VolumeInfo);
|
||
|
||
}
|
||
}
|
||
|
||
} while ( FindNextFile( FindHandle, &FindData ));
|
||
|
||
FindClose( FindHandle );
|
||
}
|
||
}
|
||
|
||
}
|
||
|
||
DWORD
|
||
CipherTouchEncryptedFiles(
|
||
)
|
||
{
|
||
|
||
WCHAR VolBuffer[MAX_PATH];
|
||
WCHAR *SearchPath = NULL;
|
||
HANDLE SearchHandle;
|
||
BOOL SearchNext = TRUE;
|
||
CIPHER_VOLUME_INFO VolumeInfo;
|
||
LPWSTR VolumeNames;
|
||
LPWSTR VolumeNamesCrnt;
|
||
LPWSTR DosDeviceNames;
|
||
LPWSTR DosDeviceNamesCrnt;
|
||
DWORD DriveIndex = 0;
|
||
WCHAR TmpChar;
|
||
BOOL b;
|
||
|
||
VolumeNames = (LPWSTR) malloc ( DosDriveLimitCount * MAX_PATH * sizeof(WCHAR) );
|
||
DosDeviceNames = (LPWSTR) malloc ( DosDriveLimitCount * MAX_PATH * sizeof(WCHAR) );
|
||
|
||
if ( !VolumeNames || !DosDeviceNames) {
|
||
if (VolumeNames) {
|
||
free(VolumeNames);
|
||
}
|
||
if (DosDeviceNames) {
|
||
free(DosDeviceNames);
|
||
}
|
||
return ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
//
|
||
// Don't popup when query floopy and etc.
|
||
//
|
||
|
||
SetErrorMode(SEM_FAILCRITICALERRORS);
|
||
lstrcpy(VolBuffer, TEXT("A:\\"));
|
||
VolumeNamesCrnt = VolumeNames;
|
||
DosDeviceNamesCrnt = DosDeviceNames;
|
||
|
||
//
|
||
// Get all the volume and device names which has a drive letter assigned
|
||
//
|
||
|
||
while (DriveIndex < DosDriveLimitCount) {
|
||
|
||
b = GetVolumeNameForVolumeMountPoint( VolBuffer,
|
||
VolumeNamesCrnt,
|
||
(DWORD)(VolumeNames + DosDriveLimitCount * MAX_PATH - VolumeNamesCrnt));
|
||
if (!b) {
|
||
VolumeInfo.VolumeName[DriveIndex] = NULL;
|
||
VolumeInfo.DosDeviceName[DriveIndex++] = NULL;
|
||
VolBuffer[0]++;
|
||
continue;
|
||
}
|
||
|
||
VolumeInfo.VolumeName[DriveIndex] = VolumeNamesCrnt;
|
||
VolumeNamesCrnt += lstrlen(VolumeNamesCrnt) + 1;
|
||
|
||
//
|
||
// The number 48 is copied from utils\mountvol\mountvol.c
|
||
//
|
||
|
||
TmpChar = VolumeInfo.VolumeName[DriveIndex][48];
|
||
VolumeInfo.VolumeName[DriveIndex][48] = 0;
|
||
if (QueryDosDevice( &(VolumeInfo.VolumeName[DriveIndex][4]),
|
||
DosDeviceNamesCrnt,
|
||
(DWORD)(DosDeviceNames + DosDriveLimitCount * MAX_PATH - DosDeviceNamesCrnt))) {
|
||
|
||
VolumeInfo.DosDeviceName[DriveIndex] = DosDeviceNamesCrnt;
|
||
DosDeviceNamesCrnt += lstrlen(DosDeviceNamesCrnt) + 1;
|
||
|
||
} else {
|
||
VolumeInfo.DosDeviceName[DriveIndex] = NULL;
|
||
}
|
||
|
||
VolumeInfo.VolumeName[DriveIndex][48] = TmpChar;
|
||
VolBuffer[0]++;
|
||
DriveIndex++;
|
||
|
||
}
|
||
|
||
|
||
SearchPath = (WCHAR *) malloc( ENUMPATHLENGTH * sizeof(WCHAR) );
|
||
if (!SearchPath) {
|
||
return ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
SearchHandle = FindFirstVolume(VolBuffer, MAX_PATH);
|
||
|
||
if ( INVALID_HANDLE_VALUE != SearchHandle ) {
|
||
|
||
if (DisplayFilesOnly) {
|
||
|
||
DisplayMsg(CIPHER_ENCRYPTED_FILES, NULL);
|
||
|
||
}
|
||
|
||
while ( SearchNext ) {
|
||
|
||
if (CipherConvertToDriveLetter(VolBuffer, &VolumeInfo)){
|
||
|
||
//
|
||
// Check if this volume is a NTFS volume
|
||
//
|
||
|
||
if(GetVolumeInformation(
|
||
VolBuffer, // Current root directory.
|
||
NULL, // Volume name.
|
||
0, // Volume name length.
|
||
NULL, // Serial number.
|
||
NULL, // Maximum length.
|
||
NULL,
|
||
SearchPath, // File system type.
|
||
MAX_PATH
|
||
)){
|
||
if(!wcscmp(SearchPath, TEXT("NTFS"))){
|
||
|
||
lstrcpy( SearchPath, VolBuffer );
|
||
CipherTouchDirFiles(SearchPath, &VolumeInfo);
|
||
|
||
}
|
||
}
|
||
}
|
||
SearchNext = FindNextVolume(SearchHandle, VolBuffer, MAX_PATH);
|
||
|
||
}
|
||
FindVolumeClose(SearchHandle);
|
||
}
|
||
|
||
free(SearchPath);
|
||
free(VolumeNames);
|
||
free(DosDeviceNames);
|
||
return ERROR_SUCCESS;
|
||
}
|
||
|
||
|
||
BOOL
|
||
EncodeAndAlloc(
|
||
DWORD dwEncodingType,
|
||
LPCSTR lpszStructType,
|
||
const void * pvStructInfo,
|
||
PBYTE * pbEncoded,
|
||
PDWORD pcbEncoded
|
||
)
|
||
{
|
||
BOOL b = FALSE;
|
||
|
||
if (CryptEncodeObject(
|
||
dwEncodingType,
|
||
lpszStructType,
|
||
pvStructInfo,
|
||
NULL,
|
||
pcbEncoded )) {
|
||
|
||
*pbEncoded = (PBYTE)malloc( *pcbEncoded );
|
||
|
||
if (*pbEncoded) {
|
||
|
||
if (CryptEncodeObject(
|
||
dwEncodingType,
|
||
lpszStructType,
|
||
pvStructInfo,
|
||
*pbEncoded,
|
||
pcbEncoded )) {
|
||
|
||
b = TRUE;
|
||
|
||
} else {
|
||
|
||
free( *pbEncoded );
|
||
*pbEncoded = NULL;
|
||
}
|
||
|
||
} else {
|
||
|
||
SetLastError( ERROR_NOT_ENOUGH_MEMORY );
|
||
}
|
||
}
|
||
|
||
return( b );
|
||
}
|
||
|
||
|
||
BOOLEAN
|
||
CreateSelfSignedRecoveryCertificate(
|
||
OUT PCCERT_CONTEXT * pCertContext,
|
||
OUT LPWSTR *lpContainerName,
|
||
OUT LPWSTR *lpProviderName
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
This routine sets up and creates a self-signed certificate.
|
||
|
||
Arguments:
|
||
|
||
|
||
Return Value:
|
||
|
||
TRUE on success, FALSE on failure. Call GetLastError() for more details.
|
||
|
||
--*/
|
||
|
||
{
|
||
BOOLEAN fReturn = FALSE;
|
||
DWORD rc = ERROR_SUCCESS;
|
||
|
||
PBYTE pbHash = NULL;
|
||
LPWSTR lpDisplayInfo = NULL;
|
||
|
||
HCRYPTKEY hKey = 0;
|
||
HCRYPTPROV hProv = 0;
|
||
GUID guidContainerName;
|
||
LPWSTR TmpContainerName;
|
||
|
||
*pCertContext = NULL;
|
||
*lpContainerName = NULL;
|
||
*lpProviderName = NULL;
|
||
|
||
//
|
||
// Create a key pair
|
||
//
|
||
|
||
//
|
||
// Container name
|
||
//
|
||
|
||
|
||
|
||
|
||
if ( ERROR_SUCCESS != UuidCreate(&guidContainerName) ) {
|
||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||
return(fReturn);
|
||
}
|
||
|
||
|
||
if (ERROR_SUCCESS == UuidToStringW(&guidContainerName, (unsigned short **)lpContainerName )) {
|
||
|
||
//
|
||
// Copy the container name into LSA heap memory
|
||
//
|
||
|
||
*lpProviderName = MS_DEF_PROV;
|
||
|
||
//
|
||
// Create the key container
|
||
//
|
||
|
||
if (CryptAcquireContext(&hProv, *lpContainerName, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_NEWKEYSET )) {
|
||
|
||
if (CryptGenKey(hProv, AT_KEYEXCHANGE, RSA1024BIT_KEY | CRYPT_EXPORTABLE, &hKey)) {
|
||
|
||
DWORD NameLength = 64;
|
||
LPWSTR AgentName = NULL;
|
||
|
||
//
|
||
// Construct the subject name information
|
||
//
|
||
|
||
//lpDisplayInfo = MakeDNName();
|
||
|
||
AgentName = (LPWSTR)malloc(NameLength * sizeof(WCHAR));
|
||
if (AgentName){
|
||
if (!GetUserName(AgentName, &NameLength)){
|
||
free(AgentName);
|
||
AgentName = (LPWSTR)malloc(NameLength * sizeof(WCHAR));
|
||
|
||
//
|
||
// Try again with big buffer
|
||
//
|
||
|
||
if ( AgentName ){
|
||
|
||
if (!GetUserName(AgentName, &NameLength)){
|
||
rc = GetLastError();
|
||
free(AgentName);
|
||
AgentName = NULL;
|
||
}
|
||
|
||
} else {
|
||
rc = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
}
|
||
} else {
|
||
rc = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
if (AgentName) {
|
||
|
||
LPCWSTR DNNameTemplate = L"CN=%ws,L=EFS,OU=EFS File Encryption Certificate";
|
||
DWORD cbDNName = 0;
|
||
|
||
cbDNName = (wcslen( DNNameTemplate ) + 1) * sizeof( WCHAR ) + (wcslen( AgentName ) + 1) * sizeof( WCHAR );
|
||
lpDisplayInfo = (LPWSTR)malloc( cbDNName );
|
||
if (lpDisplayInfo) {
|
||
swprintf( lpDisplayInfo, DNNameTemplate, AgentName );
|
||
} else {
|
||
rc = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
free(AgentName);
|
||
AgentName = NULL;
|
||
|
||
}
|
||
|
||
if (lpDisplayInfo) {
|
||
|
||
//
|
||
// Use this piece of code to create the PCERT_NAME_BLOB going into CertCreateSelfSignCertificate()
|
||
//
|
||
|
||
CERT_NAME_BLOB SubjectName;
|
||
|
||
SubjectName.cbData = 0;
|
||
|
||
if(CertStrToNameW(
|
||
CRYPT_ASN_ENCODING,
|
||
lpDisplayInfo,
|
||
0,
|
||
NULL,
|
||
NULL,
|
||
&SubjectName.cbData,
|
||
NULL)) {
|
||
|
||
SubjectName.pbData = (BYTE *) malloc(SubjectName.cbData);
|
||
|
||
if (SubjectName.pbData) {
|
||
|
||
if (CertStrToNameW(
|
||
CRYPT_ASN_ENCODING,
|
||
lpDisplayInfo,
|
||
0,
|
||
NULL,
|
||
SubjectName.pbData,
|
||
&SubjectName.cbData,
|
||
NULL) ) {
|
||
|
||
//
|
||
// Make the enhanced key usage
|
||
//
|
||
|
||
CERT_ENHKEY_USAGE certEnhKeyUsage;
|
||
LPSTR lpstr;
|
||
CERT_EXTENSION certExt;
|
||
|
||
lpstr = szOID_EFS_RECOVERY;
|
||
certEnhKeyUsage.cUsageIdentifier = 1;
|
||
certEnhKeyUsage.rgpszUsageIdentifier = &lpstr;
|
||
|
||
// now call CryptEncodeObject to encode the enhanced key usage into the extension struct
|
||
|
||
certExt.Value.cbData = 0;
|
||
certExt.Value.pbData = NULL;
|
||
certExt.fCritical = FALSE;
|
||
certExt.pszObjId = szOID_ENHANCED_KEY_USAGE;
|
||
|
||
//
|
||
// Encode it
|
||
//
|
||
|
||
if (EncodeAndAlloc(
|
||
CRYPT_ASN_ENCODING,
|
||
X509_ENHANCED_KEY_USAGE,
|
||
&certEnhKeyUsage,
|
||
&certExt.Value.pbData,
|
||
&certExt.Value.cbData
|
||
)) {
|
||
|
||
//
|
||
// finally, set up the array of extensions in the certInfo struct
|
||
// any further extensions need to be added to this array.
|
||
//
|
||
|
||
CERT_EXTENSIONS certExts;
|
||
CRYPT_KEY_PROV_INFO KeyProvInfo;
|
||
SYSTEMTIME StartTime;
|
||
FILETIME FileTime;
|
||
LARGE_INTEGER TimeData;
|
||
SYSTEMTIME EndTime;
|
||
|
||
certExts.cExtension = 1;
|
||
certExts.rgExtension = &certExt;
|
||
|
||
|
||
memset( &KeyProvInfo, 0, sizeof( CRYPT_KEY_PROV_INFO ));
|
||
|
||
KeyProvInfo.pwszContainerName = *lpContainerName;
|
||
KeyProvInfo.pwszProvName = *lpProviderName;
|
||
KeyProvInfo.dwProvType = PROV_RSA_FULL;
|
||
KeyProvInfo.dwKeySpec = AT_KEYEXCHANGE;
|
||
|
||
|
||
GetSystemTime(&StartTime);
|
||
SystemTimeToFileTime(&StartTime, &FileTime);
|
||
TimeData.LowPart = FileTime.dwLowDateTime;
|
||
TimeData.HighPart = (LONG) FileTime.dwHighDateTime;
|
||
TimeData.QuadPart += YEARCOUNT * 100;
|
||
FileTime.dwLowDateTime = TimeData.LowPart;
|
||
FileTime.dwHighDateTime = (DWORD) TimeData.HighPart;
|
||
|
||
FileTimeToSystemTime(&FileTime, &EndTime);
|
||
|
||
*pCertContext = CertCreateSelfSignCertificate(
|
||
hProv,
|
||
&SubjectName,
|
||
0,
|
||
&KeyProvInfo,
|
||
NULL,
|
||
&StartTime,
|
||
&EndTime,
|
||
&certExts
|
||
);
|
||
|
||
if (*pCertContext) {
|
||
|
||
fReturn = TRUE;
|
||
|
||
} else {
|
||
|
||
rc = GetLastError();
|
||
}
|
||
|
||
free( certExt.Value.pbData );
|
||
|
||
} else {
|
||
|
||
rc = GetLastError();
|
||
}
|
||
|
||
} else {
|
||
|
||
rc = GetLastError();
|
||
}
|
||
|
||
free( SubjectName.pbData );
|
||
|
||
} else {
|
||
|
||
rc = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
} else {
|
||
|
||
rc = GetLastError();
|
||
}
|
||
|
||
free( lpDisplayInfo );
|
||
|
||
} else {
|
||
|
||
rc = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
CryptDestroyKey( hKey );
|
||
|
||
} else {
|
||
|
||
rc = GetLastError();
|
||
}
|
||
|
||
CryptReleaseContext( hProv, 0 );
|
||
hProv = 0;
|
||
if (ERROR_SUCCESS != rc) {
|
||
|
||
//
|
||
// Creating cert failed. Let's delete the key container.
|
||
//
|
||
|
||
CryptAcquireContext(&hProv, *lpContainerName, MS_DEF_PROV, PROV_RSA_FULL, CRYPT_DELETEKEYSET | CRYPT_SILENT );
|
||
}
|
||
|
||
} else {
|
||
|
||
rc = GetLastError();
|
||
}
|
||
|
||
if (ERROR_SUCCESS != rc) {
|
||
RpcStringFree( (unsigned short **)lpContainerName );
|
||
*lpContainerName = NULL;
|
||
}
|
||
} else {
|
||
|
||
rc = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
|
||
if (!fReturn) {
|
||
|
||
if (*pCertContext) {
|
||
CertFreeCertificateContext( *pCertContext );
|
||
*pCertContext = NULL;
|
||
}
|
||
}
|
||
|
||
SetLastError( rc );
|
||
return( fReturn );
|
||
}
|
||
|
||
BOOLEAN
|
||
GetPassword(
|
||
OUT LPWSTR *PasswordStr
|
||
)
|
||
/*++
|
||
|
||
Routine Description:
|
||
|
||
Input a string from stdin in the Console code page.
|
||
|
||
We can't use fgetws since it uses the wrong code page.
|
||
|
||
Arguments:
|
||
|
||
Buffer - Buffer to put the read string into.
|
||
The Buffer will be zero terminated and will have any traing CR/LF removed
|
||
|
||
Return Values:
|
||
|
||
None.
|
||
|
||
--*/
|
||
{
|
||
int size;
|
||
LPSTR MbcsBuffer = NULL;
|
||
LPSTR Result;
|
||
DWORD Mode;
|
||
DWORD MbcsSize;
|
||
DWORD MbcsLength;
|
||
|
||
//
|
||
// Allocate a local buffer to read the string into
|
||
// Include room for the trimmed CR/LF
|
||
//
|
||
|
||
MbcsSize = (PASSWORDLEN+2) * sizeof(WCHAR);
|
||
MbcsBuffer = (LPSTR) malloc((PASSWORDLEN+2) * sizeof(WCHAR));
|
||
*PasswordStr = (LPWSTR) malloc((PASSWORDLEN+1) * sizeof(WCHAR));
|
||
|
||
if ( (MbcsBuffer == NULL) || (*PasswordStr == NULL) ) {
|
||
|
||
if (MbcsBuffer) {
|
||
free (MbcsBuffer);
|
||
}
|
||
if (*PasswordStr) {
|
||
free (*PasswordStr);
|
||
}
|
||
DisplayMsg(CIPHER_NO_MEMORY);
|
||
SetLastError(ERROR_NOT_ENOUGH_MEMORY);
|
||
return FALSE;
|
||
}
|
||
|
||
DisplayMsg(CIPHER_PROMPT_PASSWORD);
|
||
|
||
// turn off echo
|
||
GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &Mode);
|
||
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE),
|
||
(~(ENABLE_ECHO_INPUT)) & Mode);
|
||
|
||
Result = fgets( MbcsBuffer, MbcsSize, stdin );
|
||
|
||
if ( Result == NULL ) {
|
||
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), Mode);
|
||
free(MbcsBuffer);
|
||
free (*PasswordStr);
|
||
*PasswordStr = NULL;
|
||
return TRUE;
|
||
}
|
||
|
||
DisplayMsg(CIPHER_CONFIRM_PASSWORD);
|
||
Result = fgets( (LPSTR)*PasswordStr, (PASSWORDLEN+1) * sizeof(WCHAR), stdin );
|
||
|
||
// turn echo back on
|
||
SetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), Mode);
|
||
_putws ( L"\n" );
|
||
|
||
if (strcmp( (LPSTR) *PasswordStr, MbcsBuffer)){
|
||
|
||
//
|
||
// Password not match.
|
||
//
|
||
|
||
DisplayMsg(CIPHER_PASSWORD_NOMATCH);
|
||
free(MbcsBuffer);
|
||
free (*PasswordStr);
|
||
SetLastError(ERROR_INVALID_PASSWORD);
|
||
*PasswordStr = NULL;
|
||
return FALSE;
|
||
}
|
||
|
||
if ( Result == NULL ) {
|
||
free(MbcsBuffer);
|
||
free (*PasswordStr);
|
||
*PasswordStr = NULL;
|
||
return TRUE;
|
||
}
|
||
|
||
//
|
||
// Trim any trailing CR or LF char from the string
|
||
//
|
||
|
||
MbcsLength = lstrlenA( MbcsBuffer );
|
||
if ( MbcsLength == 0 ) {
|
||
free(MbcsBuffer);
|
||
free (*PasswordStr);
|
||
*PasswordStr = NULL;
|
||
return TRUE;
|
||
}
|
||
|
||
if ( MbcsBuffer[MbcsLength-1] == '\n' || MbcsBuffer[MbcsLength-1] == '\r' ) {
|
||
MbcsBuffer[MbcsLength-1] = '\0';
|
||
MbcsLength --;
|
||
}
|
||
|
||
|
||
//
|
||
// Convert the string to UNICODE
|
||
//
|
||
size = MultiByteToWideChar( GetConsoleOutputCP(),
|
||
0,
|
||
MbcsBuffer,
|
||
MbcsLength+1, // Include trailing zero
|
||
*PasswordStr,
|
||
PASSWORDLEN );
|
||
free(MbcsBuffer);
|
||
|
||
if ( size == 0 ) {
|
||
DisplayErr(NULL, GetLastError());
|
||
free (*PasswordStr);
|
||
*PasswordStr = NULL;
|
||
return FALSE;
|
||
}
|
||
return TRUE;
|
||
|
||
}
|
||
|
||
DWORD
|
||
PromtUserYesNo(
|
||
IN LPWSTR FileName,
|
||
OUT DWORD *UserChoice
|
||
)
|
||
{
|
||
BOOLEAN Continue = TRUE;
|
||
LPWSTR Result;
|
||
LPWSTR Yesnotext;
|
||
DWORD TextLen;
|
||
|
||
//
|
||
// File exists
|
||
//
|
||
|
||
*UserChoice = ChoiceNotDefined;
|
||
|
||
TextLen = FormatMessage(FORMAT_MESSAGE_FROM_HMODULE | FORMAT_MESSAGE_ALLOCATE_BUFFER,
|
||
NULL, CIPHER_YESNOANSWER, 0, (LPVOID)&Yesnotext, 0, NULL);
|
||
|
||
if (TextLen && Yesnotext) {
|
||
|
||
while (TRUE) {
|
||
|
||
WCHAR FirstChar;
|
||
|
||
DisplayMsg(CIPHER_FILE_EXISTS, FileName);
|
||
Result = fgetws((LPWSTR)Buf, sizeof(Buf)/sizeof (WCHAR), stdin);
|
||
if (!Result) {
|
||
|
||
//
|
||
// Error or end of file. Just return.
|
||
//
|
||
|
||
LocalFree(Yesnotext);
|
||
return GetLastError();
|
||
|
||
}
|
||
|
||
//
|
||
// Trim any trailing CR or LF char from the string
|
||
//
|
||
|
||
FirstChar = towupper(Buf[0]);
|
||
if (Yesnotext[0] == FirstChar) {
|
||
*UserChoice = UserChooseYes;
|
||
break;
|
||
} else if (Yesnotext[1] == FirstChar) {
|
||
*UserChoice = UserChooseNo;
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
LocalFree(Yesnotext);
|
||
|
||
} else {
|
||
|
||
return GetLastError();
|
||
|
||
}
|
||
|
||
return ERROR_SUCCESS;
|
||
|
||
}
|
||
|
||
|
||
DWORD
|
||
GenerateCertFiles(
|
||
IN LPWSTR StartingDirectory
|
||
)
|
||
{
|
||
HCERTSTORE memStore;
|
||
DWORD dwLastError = ERROR_SUCCESS;
|
||
PCCERT_CONTEXT pCertContext;
|
||
LPWSTR ContainerName;
|
||
LPWSTR ProviderName;
|
||
LPWSTR CertFileName;
|
||
LPWSTR PfxPassword;
|
||
|
||
|
||
if (!GetPassword( &PfxPassword )){
|
||
return GetLastError();
|
||
}
|
||
|
||
memStore = CertOpenStore(
|
||
CERT_STORE_PROV_MEMORY,
|
||
0,
|
||
0,
|
||
CERT_STORE_MAXIMUM_ALLOWED_FLAG,
|
||
NULL
|
||
);
|
||
|
||
CertFileName = (LPWSTR)malloc((wcslen(StartingDirectory)+5) * sizeof(WCHAR));
|
||
|
||
if (memStore && CertFileName) {
|
||
|
||
//
|
||
// Let's check if the files exist or not
|
||
//
|
||
|
||
wcscpy(CertFileName, StartingDirectory);
|
||
wcscat(CertFileName, L".PFX");
|
||
if (GetFileAttributes(CertFileName) != -1) {
|
||
|
||
DWORD UserChoice;
|
||
|
||
if (((dwLastError = PromtUserYesNo(CertFileName, &UserChoice)) != ERROR_SUCCESS) ||
|
||
(UserChoice != 0)) {
|
||
|
||
free(CertFileName);
|
||
CertCloseStore( memStore, 0 );
|
||
return dwLastError;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
wcscpy(CertFileName, StartingDirectory);
|
||
wcscat(CertFileName, L".CER");
|
||
if (GetFileAttributes(CertFileName) != -1) {
|
||
|
||
DWORD UserChoice;
|
||
|
||
if (((dwLastError = PromtUserYesNo(CertFileName, &UserChoice)) != ERROR_SUCCESS) ||
|
||
(UserChoice != 0)) {
|
||
|
||
free(CertFileName);
|
||
CertCloseStore( memStore, 0 );
|
||
return dwLastError;
|
||
|
||
}
|
||
|
||
}
|
||
|
||
//
|
||
// Generate the cert first
|
||
//
|
||
|
||
if (CreateSelfSignedRecoveryCertificate(&pCertContext, &ContainerName, &ProviderName)){
|
||
|
||
HANDLE hFile;
|
||
HCRYPTPROV hProv = 0;
|
||
DWORD BytesWritten = 0;
|
||
|
||
//
|
||
// We got the certificate. Let's generate the CER file first
|
||
//
|
||
|
||
hFile = CreateFileW(
|
||
CertFileName,
|
||
GENERIC_WRITE,
|
||
0,
|
||
NULL,
|
||
CREATE_ALWAYS,
|
||
FILE_ATTRIBUTE_NORMAL,
|
||
NULL
|
||
);
|
||
if ( INVALID_HANDLE_VALUE != hFile) {
|
||
|
||
//
|
||
// Let's write out the CER file
|
||
//
|
||
|
||
|
||
if(!WriteFile(
|
||
hFile,
|
||
pCertContext->pbCertEncoded,
|
||
pCertContext->cbCertEncoded,
|
||
&BytesWritten,
|
||
NULL
|
||
)){
|
||
|
||
dwLastError = GetLastError();
|
||
} else {
|
||
DisplayMsg(CIPHER_CER_CREATED);
|
||
}
|
||
|
||
CloseHandle(hFile);
|
||
|
||
} else {
|
||
|
||
dwLastError = GetLastError();
|
||
|
||
}
|
||
|
||
if (CertAddCertificateContextToStore(memStore, pCertContext, CERT_STORE_ADD_ALWAYS, NULL)){
|
||
|
||
CRYPT_DATA_BLOB PFX;
|
||
|
||
memset( &PFX, 0, sizeof( CRYPT_DATA_BLOB ));
|
||
|
||
//
|
||
// Asking password
|
||
//
|
||
|
||
if (PFXExportCertStoreEx(
|
||
memStore,
|
||
&PFX,
|
||
PfxPassword,
|
||
NULL,
|
||
EXPORT_PRIVATE_KEYS | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY | REPORT_NO_PRIVATE_KEY)){
|
||
|
||
PFX.pbData = (BYTE *) malloc(PFX.cbData);
|
||
|
||
if (PFX.pbData) {
|
||
|
||
if (PFXExportCertStoreEx(
|
||
memStore,
|
||
&PFX,
|
||
PfxPassword,
|
||
NULL,
|
||
EXPORT_PRIVATE_KEYS | REPORT_NOT_ABLE_TO_EXPORT_PRIVATE_KEY | REPORT_NO_PRIVATE_KEY)){
|
||
|
||
//
|
||
// Write out the PFX file
|
||
//
|
||
wcscpy(CertFileName, StartingDirectory);
|
||
wcscat(CertFileName, L".PFX");
|
||
|
||
hFile = CreateFileW(
|
||
CertFileName,
|
||
GENERIC_WRITE,
|
||
0,
|
||
NULL,
|
||
CREATE_ALWAYS,
|
||
FILE_ATTRIBUTE_NORMAL,
|
||
NULL
|
||
);
|
||
|
||
if ( INVALID_HANDLE_VALUE != hFile) {
|
||
|
||
//
|
||
// Let's write out the CER file
|
||
//
|
||
|
||
|
||
if(!WriteFile(
|
||
hFile,
|
||
PFX.pbData,
|
||
PFX.cbData,
|
||
&BytesWritten,
|
||
NULL
|
||
)){
|
||
|
||
dwLastError = GetLastError();
|
||
} else {
|
||
DisplayMsg(CIPHER_PFX_CREATED);
|
||
}
|
||
|
||
|
||
CloseHandle(hFile);
|
||
|
||
} else {
|
||
|
||
dwLastError = GetLastError();
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
dwLastError = GetLastError();
|
||
|
||
}
|
||
|
||
free( PFX.pbData );
|
||
|
||
} else {
|
||
|
||
dwLastError = ERROR_NOT_ENOUGH_MEMORY;
|
||
|
||
}
|
||
|
||
} else {
|
||
|
||
dwLastError = GetLastError();
|
||
|
||
}
|
||
}
|
||
|
||
|
||
//
|
||
// Let's delete the key
|
||
//
|
||
|
||
CertFreeCertificateContext(pCertContext);
|
||
RpcStringFree( (unsigned short **)&ContainerName );
|
||
CryptAcquireContext(&hProv, ContainerName, ProviderName, PROV_RSA_FULL, CRYPT_DELETEKEYSET | CRYPT_SILENT );
|
||
|
||
} else {
|
||
dwLastError = GetLastError();
|
||
}
|
||
|
||
//
|
||
// Close Store and free the
|
||
//
|
||
|
||
free(CertFileName);
|
||
CertCloseStore( memStore, 0 );
|
||
|
||
} else {
|
||
dwLastError = ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
if (PfxPassword){
|
||
free(PfxPassword);
|
||
}
|
||
|
||
if (ERROR_SUCCESS != dwLastError) {
|
||
|
||
DisplayErr(NULL, dwLastError);
|
||
|
||
}
|
||
|
||
return dwLastError;
|
||
}
|
||
|
||
DWORD
|
||
SecureInitializeRandomFill(
|
||
IN OUT PSECURE_FILL_INFO pSecureFill,
|
||
IN ULONG FillSize,
|
||
IN PBYTE FillValue OPTIONAL
|
||
)
|
||
/*++
|
||
|
||
FillValue = NULL
|
||
Use Random fill and random mixing logic.
|
||
|
||
FillValue = valid pointer to fill byte
|
||
Fill region with specified value, with no random mixing.
|
||
|
||
|
||
--*/
|
||
|
||
{
|
||
DWORD dwLastError;
|
||
|
||
__try {
|
||
|
||
//
|
||
// allocate the critical section.
|
||
//
|
||
|
||
InitializeCriticalSection( &pSecureFill->Lock );
|
||
} __except (EXCEPTION_EXECUTE_HANDLER )
|
||
{
|
||
return ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
pSecureFill->LockValid = TRUE;
|
||
|
||
pSecureFill->cbRandomFill = FillSize;
|
||
pSecureFill->pbRandomFill = VirtualAlloc(
|
||
NULL,
|
||
FillSize,
|
||
MEM_COMMIT,
|
||
PAGE_READWRITE
|
||
);
|
||
|
||
if( pSecureFill->pbRandomFill != NULL )
|
||
{
|
||
BYTE RandomFill[256];
|
||
|
||
|
||
if( FillValue != NULL )
|
||
{
|
||
memset( pSecureFill->pbRandomFill, *FillValue, pSecureFill->cbRandomFill );
|
||
pSecureFill->fRandomFill = FALSE;
|
||
return ERROR_SUCCESS;
|
||
}
|
||
|
||
//
|
||
// initialize the region with initial random pad.
|
||
//
|
||
|
||
pSecureFill->fRandomFill = TRUE;
|
||
|
||
RANDOM_BYTES( RandomFill, sizeof(RandomFill) );
|
||
|
||
rc4_key( &pSecureFill->Key, sizeof(RandomFill), RandomFill );
|
||
rc4( &pSecureFill->Key, pSecureFill->cbRandomFill, pSecureFill->pbRandomFill );
|
||
|
||
//
|
||
// initialize the key.
|
||
//
|
||
|
||
RANDOM_BYTES( RandomFill, sizeof(RandomFill) );
|
||
rc4_key( &pSecureFill->Key, sizeof(RandomFill), RandomFill );
|
||
|
||
ZeroMemory( RandomFill, sizeof(RandomFill) );
|
||
|
||
return ERROR_SUCCESS;
|
||
}
|
||
|
||
|
||
dwLastError = GetLastError();
|
||
|
||
DeleteCriticalSection( &pSecureFill->Lock );
|
||
pSecureFill->LockValid = FALSE;
|
||
|
||
return dwLastError;
|
||
}
|
||
|
||
VOID
|
||
SecureMixRandomFill(
|
||
IN OUT PSECURE_FILL_INFO pSecureFill,
|
||
IN ULONG cbBytesThisFill
|
||
)
|
||
{
|
||
LONG Result;
|
||
LONG Compare;
|
||
|
||
if( !pSecureFill->fRandomFill )
|
||
{
|
||
return;
|
||
}
|
||
|
||
//
|
||
// update the fill once it has been used 8 times.
|
||
//
|
||
|
||
Compare = (LONG)(8 * pSecureFill->cbRandomFill);
|
||
|
||
Result = InterlockedExchangeAdd(
|
||
&pSecureFill->cbFilled,
|
||
cbBytesThisFill
|
||
);
|
||
|
||
if( (Result+Compare) > Compare )
|
||
{
|
||
Result = 0;
|
||
|
||
//
|
||
// if there was a race condition, only one thread will update the random fill.
|
||
//
|
||
|
||
if( TryEnterCriticalSection( &pSecureFill->Lock ) )
|
||
{
|
||
rc4( &pSecureFill->Key, pSecureFill->cbRandomFill, pSecureFill->pbRandomFill );
|
||
|
||
LeaveCriticalSection( &pSecureFill->Lock );
|
||
}
|
||
}
|
||
}
|
||
|
||
DWORD
|
||
SecureDeleteRandomFill(
|
||
IN PSECURE_FILL_INFO pSecureFill
|
||
)
|
||
{
|
||
if( pSecureFill->pbRandomFill != NULL )
|
||
{
|
||
VirtualFree( pSecureFill->pbRandomFill, pSecureFill->cbRandomFill, MEM_RELEASE );
|
||
}
|
||
|
||
if( pSecureFill->LockValid )
|
||
{
|
||
DeleteCriticalSection( &pSecureFill->Lock );
|
||
}
|
||
|
||
ZeroMemory( pSecureFill, sizeof(*pSecureFill) );
|
||
|
||
return ERROR_SUCCESS;
|
||
}
|
||
|
||
#define MaxFileNum 100000000
|
||
#define MaxDigit 9
|
||
|
||
HANDLE
|
||
CreateMyTempFile(
|
||
LPWSTR TempPath
|
||
)
|
||
{
|
||
static DWORD TempIndex = 0;
|
||
WCHAR TempFileName[MAX_PATH];
|
||
WCHAR TempIndexString[MaxDigit+2];
|
||
DWORD TempPathLength;
|
||
HANDLE TempHandle;
|
||
BOOLEAN ContinueSearch = TRUE;
|
||
DWORD RetCode;
|
||
|
||
if (wcslen(TempPath) >= (MAX_PATH - 3 - MaxDigit)) {
|
||
|
||
//
|
||
// Path too long. This should not happen as the TempPath should be the root of the volume
|
||
//
|
||
|
||
SetLastError(ERROR_LABEL_TOO_LONG);
|
||
|
||
return INVALID_HANDLE_VALUE;
|
||
|
||
}
|
||
|
||
wcscpy(TempFileName, TempPath);
|
||
TempPathLength = wcslen(TempPath);
|
||
|
||
while ( (TempIndex <= MaxFileNum) && ContinueSearch ) {
|
||
|
||
wsprintf(TempIndexString, L"%ld", TempIndex);
|
||
wcscat(TempFileName, TempIndexString);
|
||
wcscat(TempFileName, L".E");
|
||
TempHandle = CreateFileW(
|
||
TempFileName,
|
||
GENERIC_WRITE,
|
||
0,
|
||
NULL,
|
||
CREATE_NEW,
|
||
FILE_ATTRIBUTE_NORMAL |
|
||
FILE_FLAG_DELETE_ON_CLOSE,
|
||
NULL
|
||
);
|
||
if (TempHandle != INVALID_HANDLE_VALUE) {
|
||
return TempHandle;
|
||
}
|
||
|
||
RetCode = GetLastError();
|
||
|
||
switch (RetCode) {
|
||
case ERROR_INVALID_PARAMETER :
|
||
case ERROR_WRITE_PROTECT :
|
||
case ERROR_FILE_NOT_FOUND :
|
||
case ERROR_BAD_PATHNAME :
|
||
case ERROR_INVALID_NAME :
|
||
case ERROR_PATH_NOT_FOUND :
|
||
case ERROR_NETWORK_ACCESS_DENIED :
|
||
case ERROR_DISK_CORRUPT :
|
||
case ERROR_FILE_CORRUPT :
|
||
case ERROR_DISK_FULL :
|
||
|
||
ContinueSearch = FALSE;
|
||
|
||
break;
|
||
default:
|
||
|
||
TempFileName[TempPathLength] = 0;
|
||
break;
|
||
|
||
}
|
||
|
||
TempIndex++;
|
||
|
||
}
|
||
|
||
|
||
//
|
||
// We got the filename.
|
||
//
|
||
|
||
return TempHandle;
|
||
}
|
||
|
||
DWORD
|
||
SecureProcessMft(
|
||
IN LPWSTR DriveLetter,
|
||
IN HANDLE hTempFile
|
||
)
|
||
{
|
||
NTFS_VOLUME_DATA_BUFFER VolumeData;
|
||
DWORD cbOutput;
|
||
|
||
__int64 TotalMftEntries;
|
||
PHANDLE pHandleArray = NULL;
|
||
DWORD FreeMftEntries;
|
||
DWORD i;
|
||
DWORD dwLastError = ERROR_SUCCESS;
|
||
|
||
|
||
//
|
||
// get the count of MFT records. This will fail if not NTFS, so bail in that case.
|
||
//
|
||
|
||
if(!DeviceIoControl(
|
||
hTempFile,
|
||
FSCTL_GET_NTFS_VOLUME_DATA, // dwIoControlCode
|
||
NULL,
|
||
0,
|
||
&VolumeData,
|
||
sizeof(VolumeData),
|
||
&cbOutput,
|
||
NULL
|
||
))
|
||
{
|
||
return GetLastError();
|
||
}
|
||
|
||
TotalMftEntries = VolumeData.MftValidDataLength.QuadPart / VolumeData.BytesPerFileRecordSegment;
|
||
if( TotalMftEntries > (0xFFFFFFFF/sizeof(HANDLE)) )
|
||
{
|
||
return ERROR_INVALID_PARAMETER;
|
||
}
|
||
|
||
FreeMftEntries = (DWORD)TotalMftEntries;
|
||
|
||
pHandleArray = HeapAlloc(GetProcessHeap(), 0 , FreeMftEntries*sizeof(HANDLE));
|
||
if( pHandleArray == NULL )
|
||
{
|
||
return ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
ZeroMemory( pHandleArray, FreeMftEntries * sizeof(HANDLE) );
|
||
|
||
|
||
for( i=0;i< FreeMftEntries ; i++ )
|
||
{
|
||
WCHAR szTempPath[ MAX_PATH + 1 ];
|
||
DWORD FillIndex;
|
||
|
||
pHandleArray[i] = CreateMyTempFile(DriveLetter);
|
||
if( pHandleArray[i] == INVALID_HANDLE_VALUE )
|
||
{
|
||
dwLastError = GetLastError();
|
||
break;
|
||
}
|
||
|
||
//
|
||
// for each file created, write at most BytesPerFileRecordSegment data to it.
|
||
//
|
||
|
||
for( FillIndex = 0 ; FillIndex < (VolumeData.BytesPerFileRecordSegment/8) ; FillIndex++ )
|
||
{
|
||
DWORD dwBytesWritten;
|
||
|
||
if(!WriteFile(
|
||
pHandleArray[i],
|
||
GlobalSecureFill.pbRandomFill,
|
||
8,
|
||
&dwBytesWritten,
|
||
NULL
|
||
))
|
||
{
|
||
break;
|
||
}
|
||
|
||
}
|
||
|
||
if (i && !(i % 200)) {
|
||
|
||
//
|
||
// Keep users informed for every 50 files we created.
|
||
//
|
||
|
||
printf(".");
|
||
}
|
||
}
|
||
|
||
if( dwLastError == ERROR_DISK_FULL )
|
||
{
|
||
dwLastError = ERROR_SUCCESS;
|
||
}
|
||
|
||
|
||
#ifdef TestOutPut
|
||
printf("\nmft error=%lu entries created=%lu total = %I64u\n", dwLastError, i, TotalMftEntries);
|
||
#endif
|
||
|
||
|
||
for (i=0;i < FreeMftEntries;i++) {
|
||
if( pHandleArray[i] != INVALID_HANDLE_VALUE &&
|
||
pHandleArray[i] != NULL
|
||
)
|
||
{
|
||
CloseHandle( pHandleArray[i] );
|
||
}
|
||
}
|
||
|
||
|
||
if( pHandleArray != NULL )
|
||
{
|
||
HeapFree(GetProcessHeap(), 0, pHandleArray );
|
||
}
|
||
|
||
return dwLastError;
|
||
}
|
||
|
||
|
||
DWORD
|
||
SecureProcessFreeClusters(
|
||
IN LPWSTR DrivePath,
|
||
IN HANDLE hTempFile
|
||
)
|
||
{
|
||
HANDLE hVolume = INVALID_HANDLE_VALUE;
|
||
|
||
WCHAR VolumeName[100]; // 50 should be enough. 100 is more than enough.
|
||
|
||
NTFS_VOLUME_DATA_BUFFER VolumeData;
|
||
STARTING_LCN_INPUT_BUFFER LcnInput;
|
||
VOLUME_BITMAP_BUFFER *pBitmap = NULL;
|
||
MOVE_FILE_DATA MoveFile;
|
||
__int64 cbBitmap;
|
||
DWORD cbOutput;
|
||
|
||
unsigned __int64 ClusterLocation;
|
||
unsigned __int64 Lcn;
|
||
BYTE Mask;
|
||
|
||
unsigned __int64 Free = 0;
|
||
DWORD Fail = 0;
|
||
|
||
#ifdef TestOutPut
|
||
DWORD dwStart, dwStop;
|
||
#endif
|
||
|
||
__int64 ClusterIndex;
|
||
DWORD dwLastError = ERROR_SUCCESS;
|
||
|
||
//
|
||
// first, find out if there are free or reserved clusters.
|
||
// this will fail if the volume is not NTFS.
|
||
//
|
||
|
||
if (!GetVolumeNameForVolumeMountPoint(
|
||
DrivePath,
|
||
VolumeName,
|
||
sizeof(VolumeName)/sizeof(WCHAR) )){
|
||
|
||
return GetLastError();
|
||
|
||
}
|
||
|
||
VolumeName[wcslen(VolumeName)-1] = 0; // Truncate the trailing slash
|
||
|
||
if(!DeviceIoControl(
|
||
hTempFile,
|
||
FSCTL_GET_NTFS_VOLUME_DATA, // dwIoControlCode
|
||
NULL,
|
||
0,
|
||
&VolumeData,
|
||
sizeof(VolumeData),
|
||
&cbOutput,
|
||
NULL
|
||
))
|
||
{
|
||
dwLastError = GetLastError();
|
||
goto Cleanup;
|
||
}
|
||
|
||
if( VolumeData.FreeClusters.QuadPart == 0 &&
|
||
VolumeData.TotalReserved.QuadPart == 0 )
|
||
{
|
||
return ERROR_SUCCESS;
|
||
}
|
||
|
||
hVolume = CreateFileW( VolumeName,
|
||
FILE_READ_ATTRIBUTES | GENERIC_WRITE,
|
||
FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE,
|
||
NULL,
|
||
OPEN_EXISTING,
|
||
FILE_FLAG_NO_BUFFERING, // no buffering
|
||
NULL
|
||
);
|
||
|
||
if( hVolume == INVALID_HANDLE_VALUE )
|
||
{
|
||
dwLastError = GetLastError();
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// allocate space for the volume bitmap.
|
||
//
|
||
|
||
cbBitmap = sizeof(VOLUME_BITMAP_BUFFER) + (VolumeData.TotalClusters.QuadPart / 8);
|
||
if( cbBitmap > 0xFFFFFFFF )
|
||
{
|
||
dwLastError = ERROR_INVALID_PARAMETER;
|
||
goto Cleanup;
|
||
}
|
||
|
||
pBitmap = HeapAlloc(GetProcessHeap(), 0, (DWORD)cbBitmap);
|
||
if( pBitmap == NULL )
|
||
{
|
||
dwLastError = ERROR_NOT_ENOUGH_MEMORY;
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// grab the volume bitmap.
|
||
//
|
||
|
||
LcnInput.StartingLcn.QuadPart = 0;
|
||
|
||
ZeroMemory( &MoveFile, sizeof(MoveFile) );
|
||
|
||
MoveFile.FileHandle = hTempFile;
|
||
MoveFile.StartingVcn.QuadPart = 0;
|
||
MoveFile.ClusterCount = 1;
|
||
|
||
#ifdef TestOutPut
|
||
dwStart = GetTickCount();
|
||
#endif
|
||
|
||
|
||
if(!DeviceIoControl(
|
||
hVolume,
|
||
FSCTL_GET_VOLUME_BITMAP,
|
||
&LcnInput,
|
||
sizeof(LcnInput),
|
||
pBitmap,
|
||
(DWORD)cbBitmap,
|
||
&cbOutput,
|
||
NULL
|
||
))
|
||
{
|
||
dwLastError = GetLastError();
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
//
|
||
// insure file is only bytes per cluster in length.
|
||
// this will shrink the file if necessary. We waited until after we fetched the
|
||
// volume bitmap to insure we only process the free clusters that existed prior to
|
||
// the shrink operation.
|
||
//
|
||
|
||
if(SetFilePointer(
|
||
hTempFile,
|
||
(LONG)VolumeData.BytesPerCluster,
|
||
NULL,
|
||
FILE_BEGIN
|
||
) == INVALID_SET_FILE_POINTER)
|
||
{
|
||
dwLastError = GetLastError();
|
||
goto Cleanup;
|
||
}
|
||
|
||
if(!SetEndOfFile( hTempFile ))
|
||
{
|
||
dwLastError = GetLastError();
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
Mask = 1;
|
||
Lcn = pBitmap->StartingLcn.QuadPart;
|
||
|
||
for(ClusterIndex = 0 ; ClusterIndex < VolumeData.TotalClusters.QuadPart ; ClusterIndex++)
|
||
{
|
||
if( (pBitmap->Buffer[ClusterIndex/8] & Mask) == 0 )
|
||
{
|
||
DWORD dwMoveError = ERROR_SUCCESS;
|
||
|
||
//
|
||
// move a single cluster from the temp file to the free cluster.
|
||
//
|
||
|
||
MoveFile.StartingLcn.QuadPart = Lcn;
|
||
|
||
if(!DeviceIoControl(
|
||
hVolume,
|
||
FSCTL_MOVE_FILE, // dwIoControlCode
|
||
&MoveFile,
|
||
sizeof(MoveFile),
|
||
NULL,
|
||
0,
|
||
&cbOutput,
|
||
NULL
|
||
))
|
||
{
|
||
dwMoveError = GetLastError();
|
||
}
|
||
|
||
//
|
||
// if it succeeded, or the cluster was in use, mark it used in the bitmap.
|
||
//
|
||
|
||
if( dwMoveError == ERROR_SUCCESS || dwMoveError == ERROR_ACCESS_DENIED )
|
||
{
|
||
pBitmap->Buffer[ClusterIndex/8] |= Mask;
|
||
} else {
|
||
Fail++;
|
||
}
|
||
|
||
Free++;
|
||
if ( !(Free % 200) ) {
|
||
|
||
//
|
||
// Keep users informed for every 50 files we created.
|
||
//
|
||
printf(".");
|
||
|
||
}
|
||
}
|
||
|
||
Lcn ++;
|
||
|
||
Mask <<= 1;
|
||
|
||
if(Mask == 0)
|
||
{
|
||
Mask = 1;
|
||
}
|
||
}
|
||
|
||
#ifdef TestOutPut
|
||
dwStop = GetTickCount();
|
||
|
||
printf("\nFreeCount = %I64x fail = %lu elapsed = %lu\n", Free, Fail, dwStop-dwStart);
|
||
#endif
|
||
|
||
Cleanup:
|
||
|
||
if( pBitmap != NULL )
|
||
{
|
||
HeapFree( GetProcessHeap(), 0, pBitmap );
|
||
}
|
||
|
||
if( hVolume != INVALID_HANDLE_VALUE )
|
||
{
|
||
CloseHandle( hVolume );
|
||
}
|
||
|
||
return dwLastError;
|
||
}
|
||
|
||
|
||
DWORD
|
||
SecureDeleteFreeSpace(
|
||
IN LPWSTR Directory
|
||
)
|
||
/*++
|
||
|
||
This routine fills the disk specified by the input Directory parameter with random fill.
|
||
Input is of the form "C:\", for instance.
|
||
|
||
Notes on approaches not employed here:
|
||
|
||
Alternate method would use defrag API to move random fill around the
|
||
free cluster map. Requires admin priviliges to the volume. Slower than filling volume with
|
||
a new file.
|
||
|
||
Variant on alternate method: fill volume 80% with file, grab free cluster map,
|
||
delete file associated with 80% fill, then use defrag API to fill the free cluster map
|
||
mentioned previously.
|
||
|
||
Does not fill cluster slack space for each file on the system. Could do this by
|
||
enumerating all files, and then extending+fill to slack boundry+restore original
|
||
EOF.
|
||
|
||
Does not fill $LOG. Queried file system folks on whether this is possible by creating
|
||
many small temporary files containing random fill.
|
||
|
||
--*/
|
||
{
|
||
UINT DriveType;
|
||
DWORD DirNameLength;
|
||
DWORD BufferLength;
|
||
LPWSTR PathName = NULL;
|
||
LPWSTR TempDirName = NULL;
|
||
BOOL b;
|
||
BOOL DirCreated = FALSE;
|
||
DWORD Attributes;
|
||
|
||
DWORD SectorsPerCluster;
|
||
DWORD BytesPerSector;
|
||
|
||
WCHAR TempFileName[ MAX_PATH + 1 ];
|
||
HANDLE hTempFile = INVALID_HANDLE_VALUE;
|
||
DWORD dwWriteBytes;
|
||
|
||
unsigned __int64 TotalBytesWritten;
|
||
unsigned __int64 NotifyBytesWritten;
|
||
unsigned __int64 NotifyInterval;
|
||
ULARGE_INTEGER TotalFreeBytes;
|
||
|
||
PBYTE pbFillBuffer = NULL;
|
||
ULONG cbFillBuffer;
|
||
|
||
NTFS_VOLUME_DATA_BUFFER VolumeData;
|
||
__int64 MftEntries = 0;
|
||
BOOLEAN ClustersRemaining = FALSE;
|
||
DWORD cbOutput;
|
||
|
||
DWORD dwLastError = ERROR_SUCCESS;
|
||
DWORD dwTestError;
|
||
|
||
#ifdef TestOutPut
|
||
|
||
ULARGE_INTEGER StartTime;
|
||
ULARGE_INTEGER StopTime;
|
||
|
||
#endif
|
||
|
||
|
||
//
|
||
// collect information about the disk in question.
|
||
//
|
||
|
||
|
||
DirNameLength = wcslen(Directory);
|
||
|
||
BufferLength = (DirNameLength + 1) <= MAX_PATH ?
|
||
(MAX_PATH + 1) * sizeof(WCHAR) : (DirNameLength + 1) * sizeof (WCHAR);
|
||
PathName = (LPWSTR) malloc(BufferLength);
|
||
if ( !PathName ) {
|
||
return ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
TempDirName = (LPWSTR) malloc(BufferLength + wcslen(WIPING_DIR) * sizeof (WCHAR));
|
||
if ( !TempDirName ) {
|
||
free(PathName);
|
||
return ERROR_NOT_ENOUGH_MEMORY;
|
||
}
|
||
|
||
b = GetVolumePathNameW(
|
||
Directory,
|
||
PathName,
|
||
BufferLength
|
||
);
|
||
|
||
if (!b) {
|
||
dwLastError = GetLastError();
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
DriveType = GetDriveTypeW( PathName );
|
||
|
||
|
||
if( DriveType == DRIVE_REMOTE ||
|
||
DriveType == DRIVE_CDROM )
|
||
{
|
||
dwLastError = ERROR_NOT_SUPPORTED;
|
||
goto Cleanup;
|
||
}
|
||
|
||
if(!GetDiskFreeSpaceW(
|
||
PathName,
|
||
&SectorsPerCluster,
|
||
&BytesPerSector,
|
||
NULL,
|
||
NULL
|
||
))
|
||
{
|
||
dwLastError = GetLastError();
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// allocate memory chunk to accomodate cluster size data
|
||
//
|
||
|
||
|
||
cbFillBuffer = GlobalSecureFill.cbRandomFill;
|
||
pbFillBuffer = GlobalSecureFill.pbRandomFill;
|
||
|
||
|
||
//
|
||
// determine how many bytes free space on the disk to enable notification of
|
||
// overall progress.
|
||
//
|
||
|
||
if(!GetDiskFreeSpaceExW(
|
||
PathName,
|
||
NULL,
|
||
NULL,
|
||
&TotalFreeBytes
|
||
))
|
||
{
|
||
dwLastError = GetLastError();
|
||
goto Cleanup;
|
||
}
|
||
|
||
//
|
||
// Let's Create the temp directory
|
||
//
|
||
|
||
wcscpy(TempDirName, PathName);
|
||
wcscat(TempDirName, WIPING_DIR);
|
||
if (!CreateDirectory(TempDirName, NULL)){
|
||
|
||
//
|
||
// Could not create our temp directory. Quit.
|
||
//
|
||
|
||
if ((dwLastError = GetLastError()) != ERROR_ALREADY_EXISTS){
|
||
goto Cleanup;
|
||
}
|
||
|
||
}
|
||
|
||
DirCreated = TRUE;
|
||
|
||
//
|
||
// generate temporary file.
|
||
//
|
||
|
||
if( GetTempFileNameW(
|
||
TempDirName,
|
||
L"fil",
|
||
0,
|
||
TempFileName
|
||
) == 0 )
|
||
{
|
||
dwLastError = GetLastError();
|
||
goto Cleanup;
|
||
}
|
||
|
||
Attributes = GetFileAttributes(TempFileName);
|
||
if (0xFFFFFFFF == Attributes) {
|
||
dwLastError = GetLastError();
|
||
goto Cleanup;
|
||
}
|
||
|
||
if (Attributes & FILE_ATTRIBUTE_ENCRYPTED) {
|
||
|
||
if (!DecryptFile(TempFileName, 0)){
|
||
dwLastError = GetLastError();
|
||
goto Cleanup;
|
||
}
|
||
|
||
}
|
||
|
||
hTempFile = CreateFileW(
|
||
TempFileName,
|
||
GENERIC_WRITE,
|
||
0, // exclusive access
|
||
NULL,
|
||
OPEN_EXISTING,
|
||
FILE_ATTRIBUTE_NORMAL |
|
||
FILE_FLAG_NO_BUFFERING | // no buffering
|
||
FILE_FLAG_DELETE_ON_CLOSE, // delete file when it closes.
|
||
NULL
|
||
);
|
||
|
||
if( hTempFile == INVALID_HANDLE_VALUE )
|
||
{
|
||
dwLastError = GetLastError();
|
||
goto Cleanup;
|
||
}
|
||
|
||
|
||
if (Attributes & FILE_ATTRIBUTE_COMPRESSED) {
|
||
|
||
USHORT State = COMPRESSION_FORMAT_NONE;
|
||
|
||
//
|
||
// Uncompress the directory first
|
||
//
|
||
|
||
b = DeviceIoControl(hTempFile,
|
||
FSCTL_SET_COMPRESSION,
|
||
&State,
|
||
sizeof(USHORT),
|
||
NULL,
|
||
0,
|
||
&BufferLength,
|
||
FALSE
|
||
);
|
||
|
||
if ( !b ){
|
||
dwLastError = GetLastError();
|
||
goto Cleanup;
|
||
}
|
||
|
||
}
|
||
|
||
|
||
TotalBytesWritten = 0;
|
||
|
||
//
|
||
// tell the user something happened for each 1% processed.
|
||
//
|
||
|
||
NotifyInterval = (TotalFreeBytes.QuadPart / 100);
|
||
NotifyBytesWritten = NotifyInterval;
|
||
|
||
dwWriteBytes = cbFillBuffer;
|
||
|
||
|
||
#ifdef TestOutPut
|
||
GetSystemTimeAsFileTime( (FILETIME*)&StartTime );
|
||
#endif
|
||
|
||
while( TRUE )
|
||
{
|
||
DWORD BytesWritten;
|
||
|
||
if( TotalBytesWritten >= NotifyBytesWritten )
|
||
{
|
||
printf(".");
|
||
|
||
NotifyBytesWritten += NotifyInterval;
|
||
}
|
||
|
||
|
||
//
|
||
// mix random fill.
|
||
//
|
||
|
||
SecureMixRandomFill( &GlobalSecureFill, dwWriteBytes );
|
||
|
||
if(!WriteFile(
|
||
hTempFile,
|
||
pbFillBuffer,
|
||
dwWriteBytes,
|
||
&BytesWritten,
|
||
NULL
|
||
))
|
||
{
|
||
if( GetLastError() == ERROR_DISK_FULL )
|
||
{
|
||
dwLastError = ERROR_SUCCESS;
|
||
|
||
//
|
||
// if the attempted write failed, enter a retry mode with downgraded
|
||
// buffersize to catch the last bits of slop.
|
||
//
|
||
|
||
if( dwWriteBytes > BytesPerSector )
|
||
{
|
||
dwWriteBytes = BytesPerSector;
|
||
continue;
|
||
}
|
||
} else {
|
||
dwLastError = GetLastError();
|
||
}
|
||
|
||
break;
|
||
}
|
||
|
||
TotalBytesWritten += BytesWritten;
|
||
}
|
||
|
||
#ifdef TestOutPut
|
||
GetSystemTimeAsFileTime( (FILETIME*)&StopTime );
|
||
|
||
{
|
||
ULARGE_INTEGER ElapsedTime;
|
||
SYSTEMTIME st;
|
||
|
||
ElapsedTime.QuadPart = (StopTime.QuadPart - StartTime.QuadPart);
|
||
|
||
FileTimeToSystemTime( (FILETIME*)&ElapsedTime, &st );
|
||
|
||
printf("\nTotalWritten = %I64u time = %02u:%02u:%02u.%02u\n",
|
||
TotalBytesWritten,
|
||
st.wHour,
|
||
st.wMinute,
|
||
st.wSecond,
|
||
st.wMilliseconds
|
||
);
|
||
|
||
}
|
||
#endif
|
||
|
||
|
||
|
||
//
|
||
// at this point, the disk should be full.
|
||
// If the disk is NTFS:
|
||
// 1. Fill the MFT.
|
||
// 2. Fill any free/reserved clusters.
|
||
//
|
||
|
||
dwTestError = SecureProcessMft( TempDirName, hTempFile );
|
||
// dwTestError = SecureProcessMft( PathName, hTempFile );
|
||
|
||
#ifdef TestOutPut
|
||
if (ERROR_SUCCESS != dwTestError) {
|
||
printf("\nWriting NTFS MFT & LOG. Error:");
|
||
DisplayErr(NULL, dwTestError);
|
||
}
|
||
#endif
|
||
|
||
dwTestError = SecureProcessFreeClusters( PathName, hTempFile );
|
||
|
||
#ifdef TestOutPut
|
||
if (ERROR_SUCCESS != dwTestError) {
|
||
printf("\nWriting NTFS reserved clusters. Error:");
|
||
DisplayErr(NULL, dwTestError);
|
||
}
|
||
#endif
|
||
|
||
Cleanup:
|
||
|
||
if (hTempFile != INVALID_HANDLE_VALUE) {
|
||
//
|
||
// flush the buffers. Likely has no effect if we used FILE_FLAG_NO_BUFFERING
|
||
//
|
||
//Sleep(INFINITE);
|
||
FlushFileBuffers( hTempFile );
|
||
CloseHandle( hTempFile );
|
||
}
|
||
|
||
if (DirCreated && TempDirName) {
|
||
RemoveDirectory(TempDirName);
|
||
}
|
||
|
||
if( PathName != NULL ){
|
||
free(PathName);
|
||
}
|
||
|
||
if ( TempDirName != NULL ) {
|
||
free(TempDirName);
|
||
}
|
||
|
||
return dwLastError;
|
||
}
|
||
|
||
BOOL CheckMinVersion ()
|
||
{
|
||
OSVERSIONINFOEX osvi;
|
||
DWORDLONG dwlConditionMask = 0;
|
||
BOOL GoodVersion;
|
||
|
||
// Initialize the OSVERSIONINFOEX structure.
|
||
|
||
ZeroMemory(&osvi, sizeof(OSVERSIONINFOEX));
|
||
osvi.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX);
|
||
osvi.dwMajorVersion = 5;
|
||
osvi.dwMinorVersion = 0;
|
||
osvi.wServicePackMajor = 3;
|
||
|
||
// Initialize the condition mask.
|
||
|
||
VER_SET_CONDITION( dwlConditionMask, VER_MAJORVERSION,
|
||
VER_GREATER_EQUAL );
|
||
VER_SET_CONDITION( dwlConditionMask, VER_MINORVERSION,
|
||
VER_GREATER_EQUAL );
|
||
VER_SET_CONDITION( dwlConditionMask, VER_SERVICEPACKMAJOR,
|
||
VER_GREATER_EQUAL );
|
||
|
||
// Perform the test.
|
||
|
||
GoodVersion = VerifyVersionInfo(
|
||
&osvi,
|
||
VER_MAJORVERSION | VER_MINORVERSION | VER_SERVICEPACKMAJOR,
|
||
dwlConditionMask
|
||
);
|
||
|
||
//
|
||
// Check HotFix here
|
||
//
|
||
// if (!GoodVersion) {
|
||
// GoodVersion = WeHaveTheHotFixVersion();
|
||
// }
|
||
//
|
||
|
||
|
||
return GoodVersion;
|
||
}
|
||
|
||
|
||
VOID
|
||
__cdecl
|
||
main()
|
||
{
|
||
PTCHAR *argv;
|
||
ULONG argc;
|
||
|
||
ULONG i;
|
||
|
||
PACTION_ROUTINE ActionRoutine = NULL;
|
||
PFINAL_ACTION_ROUTINE FinalActionRoutine = NULL;
|
||
|
||
TCHAR DirectorySpec[MAX_PATH];
|
||
TCHAR FileSpec[MAX_PATH];
|
||
PTCHAR p;
|
||
BOOL b;
|
||
|
||
InitializeIoStreams();
|
||
|
||
|
||
argv = CommandLineToArgvW(GetCommandLine(), &argc);
|
||
if (NULL == argv) {
|
||
DisplayErr(NULL, GetLastError());
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Scan through the arguments looking for switches
|
||
//
|
||
|
||
for (i = 1; i < argc; i += 1) {
|
||
|
||
if (argv[i][0] == '/') {
|
||
|
||
if (0 == lstricmp(argv[i], TEXT("/e"))) {
|
||
|
||
if (ActionRoutine != NULL && ActionRoutine != DoEncryptAction) {
|
||
|
||
DisplayMsg(CIPHER_USAGE, NULL);
|
||
return;
|
||
}
|
||
|
||
ActionRoutine = DoEncryptAction;
|
||
FinalActionRoutine = DoFinalEncryptAction;
|
||
|
||
} else if (0 == lstricmp(argv[i], TEXT("/d"))) {
|
||
|
||
if (ActionRoutine != NULL && ActionRoutine != DoListAction) {
|
||
|
||
DisplayMsg(CIPHER_USAGE, NULL);
|
||
return;
|
||
}
|
||
|
||
ActionRoutine = DoDecryptAction;
|
||
FinalActionRoutine = DoFinalDecryptAction;
|
||
|
||
} else if (0 == lstricmp(argv[i], TEXT("/a"))){
|
||
|
||
DoFiles = TRUE;
|
||
|
||
} else if (0 == lstricmp(argv[i], TEXT("/q"))) {
|
||
|
||
Quiet = TRUE;
|
||
|
||
} else if (0 == lstricmp(argv[i], TEXT("/k"))){
|
||
|
||
SetUpNewUserKey = TRUE;
|
||
|
||
} else if (0 == lstricmp(argv[i], TEXT("/u"))){
|
||
|
||
RefreshUserKeyOnFiles = TRUE;
|
||
|
||
} else if (0 == lstricmp(argv[i], TEXT("/n"))){
|
||
|
||
DisplayFilesOnly = TRUE;
|
||
|
||
} else if (0 == lstricmp(argv[i], TEXT("/h"))){
|
||
|
||
DisplayAllFiles = TRUE;
|
||
|
||
} else if (0 == lstrnicmp(argv[i], TEXT("/s"), 2)) {
|
||
|
||
PTCHAR pch;
|
||
|
||
DoSubdirectories = TRUE;
|
||
|
||
pch = lstrchr(argv[i], ':');
|
||
if (NULL != pch) {
|
||
lstrcpy(StartingDirectory, pch + 1);
|
||
} else {
|
||
|
||
//
|
||
// We require an explicit directory to be passed.
|
||
//
|
||
|
||
DisplayMsg(CIPHER_USAGE, NULL);
|
||
return;
|
||
}
|
||
|
||
} else if (0 == lstricmp(argv[i], TEXT("/i"))) {
|
||
|
||
IgnoreErrors = TRUE;
|
||
|
||
} else if (0 == lstricmp(argv[i], TEXT("/f"))) {
|
||
|
||
ForceOperation = TRUE;
|
||
|
||
} else if (0 == lstrnicmp(argv[i], TEXT("/r"), 2)){
|
||
|
||
PTCHAR pch;
|
||
|
||
GenerateDRA = TRUE;
|
||
|
||
pch = lstrchr(argv[i], ':');
|
||
if (NULL != pch) {
|
||
lstrcpy(StartingDirectory, pch + 1);
|
||
} else {
|
||
|
||
//
|
||
// We require an explicit file to be passed.
|
||
//
|
||
|
||
DisplayMsg(CIPHER_USAGE, NULL);
|
||
return;
|
||
}
|
||
|
||
} else if (0 == lstrnicmp(argv[i], TEXT("/w"), 2)){
|
||
|
||
PTCHAR pch;
|
||
|
||
FillUnusedSpace = TRUE;
|
||
|
||
pch = lstrchr(argv[i], ':');
|
||
if (NULL != pch) {
|
||
lstrcpy(StartingDirectory, pch + 1);
|
||
} else {
|
||
|
||
//
|
||
// We require an explicit directory to be passed.
|
||
//
|
||
|
||
DisplayMsg(CIPHER_USAGE, NULL);
|
||
return;
|
||
}
|
||
|
||
} else {
|
||
|
||
DisplayMsg(CIPHER_USAGE, NULL);
|
||
return;
|
||
}
|
||
|
||
} else {
|
||
|
||
UserSpecifiedFileSpec = TRUE;
|
||
}
|
||
}
|
||
|
||
if (SetUpNewUserKey) {
|
||
|
||
DWORD RetCode;
|
||
|
||
//
|
||
// Set up new user key here
|
||
//
|
||
|
||
RetCode = SetUserFileEncryptionKey(NULL);
|
||
if ( ERROR_SUCCESS != RetCode ) {
|
||
|
||
//
|
||
// Display error info.
|
||
//
|
||
|
||
DisplayErr(NULL, GetLastError());
|
||
|
||
|
||
} else {
|
||
|
||
//
|
||
// Get the new hash and display it.
|
||
//
|
||
|
||
CipherDisplayCrntEfsHash();
|
||
|
||
}
|
||
|
||
//
|
||
// Create user key should not be used with other options.
|
||
// We will ignore other options if user do.
|
||
//
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
if (RefreshUserKeyOnFiles) {
|
||
|
||
DWORD RetCode;
|
||
|
||
RetCode = CipherTouchEncryptedFiles();
|
||
|
||
if (RetCode != ERROR_SUCCESS) {
|
||
DisplayErr(NULL, RetCode);
|
||
}
|
||
|
||
return;
|
||
|
||
}
|
||
|
||
if (GenerateDRA) {
|
||
|
||
DWORD RetCode;
|
||
|
||
RetCode = GenerateCertFiles(StartingDirectory);
|
||
return;
|
||
|
||
}
|
||
|
||
if (FillUnusedSpace) {
|
||
|
||
BYTE FillByte[2] = { 0x00, 0xFF };
|
||
DWORD WriteValue[3] = {CIPHER_WRITE_ZERO, CIPHER_WRITE_FF, CIPHER_WRITE_RANDOM};
|
||
PBYTE pFillByte[3] = {&FillByte[0], &FillByte[1], NULL};
|
||
LPWSTR WriteChars;
|
||
DWORD RetCode;
|
||
|
||
if (!CheckMinVersion()) {
|
||
DisplayErr(NULL, ERROR_OLD_WIN_VERSION);
|
||
return;
|
||
}
|
||
|
||
//
|
||
// We are going to erase the disks
|
||
//
|
||
|
||
|
||
DisplayMsg(CIPHER_WIPE_WARNING, NULL);
|
||
|
||
for (i = 0; i < 3; i++) {
|
||
RetCode = SecureInitializeRandomFill( &GlobalSecureFill, 4096 * 128, pFillByte[i] );
|
||
if (RetCode != ERROR_SUCCESS) {
|
||
SecureDeleteRandomFill(&GlobalSecureFill);
|
||
break;
|
||
}
|
||
|
||
if ( ERROR_SUCCESS == GetResourceString(&WriteChars, WriteValue[i])){
|
||
//LoadStringW(0, WriteValue[i], WriteChars, sizeof(WriteChars)/sizeof(WCHAR));
|
||
DisplayMsg(CIPHER_WIPE_PROGRESS, WriteChars);
|
||
LocalFree(WriteChars);
|
||
}
|
||
RetCode = SecureDeleteFreeSpace(StartingDirectory);
|
||
printf("\n");
|
||
SecureDeleteRandomFill( &GlobalSecureFill );
|
||
if (RetCode != ERROR_SUCCESS) {
|
||
break;
|
||
}
|
||
}
|
||
|
||
if (RetCode != ERROR_SUCCESS) {
|
||
DisplayErr(NULL, RetCode);
|
||
}
|
||
|
||
return;
|
||
}
|
||
|
||
|
||
//
|
||
// If the use didn't specify an action then set the default to do a listing
|
||
//
|
||
|
||
if (ActionRoutine == NULL) {
|
||
|
||
ActionRoutine = DoListAction;
|
||
FinalActionRoutine = DoFinalListAction;
|
||
}
|
||
|
||
|
||
|
||
//
|
||
// If the user didn't specify a file spec then we'll do just "*"
|
||
//
|
||
|
||
if (!UserSpecifiedFileSpec) {
|
||
|
||
//
|
||
// Get our current directory because the action routines might move us
|
||
// around
|
||
//
|
||
|
||
if (DoSubdirectories) {
|
||
if (ActionRoutine != DoListAction) {
|
||
(VOID)(ActionRoutine)( StartingDirectory, TEXT("") );
|
||
}
|
||
if (!SetCurrentDirectory( StartingDirectory )) {
|
||
DisplayErr(StartingDirectory, GetLastError());
|
||
return;
|
||
}
|
||
} else {
|
||
GetCurrentDirectory( MAX_PATH, StartingDirectory );
|
||
}
|
||
|
||
|
||
(VOID)GetFullPathName( TEXT("*"), MAX_PATH, DirectorySpec, &p );
|
||
|
||
lstrcpy( FileSpec, p ); *p = '\0';
|
||
|
||
(VOID)(ActionRoutine)( DirectorySpec, FileSpec );
|
||
|
||
} else {
|
||
|
||
//
|
||
// Get our current directory because the action routines might move us
|
||
// around
|
||
//
|
||
|
||
if (!DoSubdirectories) {
|
||
GetCurrentDirectory( MAX_PATH, StartingDirectory );
|
||
} else if (!SetCurrentDirectory( StartingDirectory )) {
|
||
DisplayErr(StartingDirectory, GetLastError());
|
||
return;
|
||
}
|
||
|
||
//
|
||
// Now scan the arguments again looking for non-switches
|
||
// and this time do the action, but before calling reset
|
||
// the current directory so that things work again
|
||
//
|
||
|
||
for (i = 1; i < argc; i += 1) {
|
||
|
||
if (argv[i][0] != '/') {
|
||
|
||
SetCurrentDirectory( StartingDirectory );
|
||
|
||
//
|
||
// Handle a command with "." as the file argument specially,
|
||
// since it doesn't make good sense and the results without
|
||
// this code are surprising.
|
||
//
|
||
|
||
if ('.' == argv[i][0] && '\0' == argv[i][1]) {
|
||
argv[i] = TEXT("*");
|
||
GetFullPathName(argv[i], MAX_PATH, DirectorySpec, &p);
|
||
*p = '\0';
|
||
p = NULL;
|
||
} else {
|
||
|
||
PWCHAR pwch;
|
||
DWORD PathLen;
|
||
|
||
//
|
||
// We need to deal with path longer than MAX_PATH later.
|
||
// This code is based on Compact. They have the same problem
|
||
// as we do. So far, we have not heard any one complaining about this.
|
||
// Let's track this in the RAID.
|
||
//
|
||
|
||
PathLen = GetFullPathName(argv[i], MAX_PATH, DirectorySpec, &p);
|
||
if ( 0 == PathLen ){
|
||
DisplayMsg(CIPHER_INVALID_PARAMETER, argv[i]);
|
||
break;
|
||
}
|
||
|
||
//
|
||
// We want to treat "foobie:xxx" as an invalid drive name,
|
||
// rather than as a name identifying a stream. If there's
|
||
// a colon, there should be only a single character before
|
||
// it.
|
||
//
|
||
|
||
pwch = wcschr(argv[i], ':');
|
||
if (NULL != pwch && pwch - argv[i] != 1) {
|
||
DisplayMsg(CIPHER_INVALID_PATH, argv[i]);
|
||
break;
|
||
}
|
||
|
||
//
|
||
// GetFullPathName strips trailing dots, but we want
|
||
// to save them so that "*." will work correctly.
|
||
//
|
||
|
||
if ('.' == argv[i][lstrlen(argv[i]) - 1]) {
|
||
lstrcat(DirectorySpec, TEXT("."));
|
||
}
|
||
}
|
||
|
||
if (p != NULL) {
|
||
lstrcpy( FileSpec, p ); *p = '\0';
|
||
} else {
|
||
FileSpec[0] = '\0';
|
||
}
|
||
|
||
if (!(ActionRoutine)( DirectorySpec, FileSpec ) &&
|
||
!IgnoreErrors) {
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
//
|
||
// Reset our current directory back
|
||
//
|
||
|
||
SetCurrentDirectory( StartingDirectory );
|
||
|
||
//
|
||
// And do the final action routine that will print out the final
|
||
// statistics of what we've done
|
||
//
|
||
|
||
(FinalActionRoutine)();
|
||
}
|