/*++ 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 #include #include #include #include #include #include #include // NewGenRandom() - Win2k and whistler #include #include #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 BOOLEAN ExportEfsCert = FALSE; // Export EFS certificate and keys to a PFX file 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 // The following swprintf should be safe. Any one component of the file path could not // be longer than MAX_PATH. That means lstrlen(FindData->cFileName) <= MAX_PATH // Buf holds 1024 TCHARs. // 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; 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; // // ForceOperation actually doesn't do anything. EncryptFile() will not encrypt an EFS file. // A legacy option from compact. // if ((FindData->dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) && !ForceOperation) { return TRUE; } if ( (0 == (FindData->dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) && (!DoFiles)) { // // Skip the files // return TRUE; } Success = EncryptFile( FileSpec ); if (!Success) { if (Quiet && IgnoreErrors) { return TRUE; } 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 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 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; Attributes = GetFileAttributes( DirectorySpec ); // // DirectorySpec could be passed in from the user. It could be bogus. // if (-1 == Attributes) { DisplayErr(DirectorySpec, GetLastError()); return FALSE; } 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 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 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; Attributes = GetFileAttributes( DirectorySpec ); // // DirectorySpec could be passed in from the user. It could be bogus. // if (-1 == Attributes) { DisplayErr(DirectorySpec, GetLastError()); return FALSE; } 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) { 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; RPC_STATUS RpcStatus = ERROR_SUCCESS; *pCertContext = NULL; *lpContainerName = NULL; *lpProviderName = NULL; // // Create a key pair // // // Container name // RpcStatus = UuidCreate(&guidContainerName); if ( (ERROR_SUCCESS != RpcStatus) && (RPC_S_UUID_LOCAL_ONLY != RpcStatus) ) { 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 // 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; MySetThreadUILanguage(0); 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 ); // // 5 -- .PFX or .CER plus 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 != UserChooseYes)) { 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 != UserChooseYes)) { 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 ); return GoodVersion; } PCRYPT_KEY_PROV_INFO GetKeyProvInfo( PCCERT_CONTEXT pCertContext ) { DWORD cbData = 0; BOOL b; PCRYPT_KEY_PROV_INFO pCryptKeyProvInfo = NULL; b = CertGetCertificateContextProperty( pCertContext, CERT_KEY_PROV_INFO_PROP_ID, NULL, &cbData ); if (b) { pCryptKeyProvInfo = (PCRYPT_KEY_PROV_INFO)malloc( cbData ); if (pCryptKeyProvInfo != NULL) { b = CertGetCertificateContextProperty( pCertContext, CERT_KEY_PROV_INFO_PROP_ID, pCryptKeyProvInfo, &cbData ); if (!b) { free( pCryptKeyProvInfo ); pCryptKeyProvInfo = NULL; } } else { SetLastError( ERROR_NOT_ENOUGH_MEMORY ); } } return ( pCryptKeyProvInfo ); } DWORD GetUserInput( IN DWORD MsgNum, IN OUT LPWSTR StrBuf, IN DWORD SizeInChars ) { int ii; DisplayMsg(MsgNum); if (!fgetws((LPWSTR)StrBuf, SizeInChars, stdin)) { // // Error or end of file. Just return. // return GetLastError(); } // // Strip off CR and LN // ii = wcslen(StrBuf) - 1; while (ii >=0 ) { if ((StrBuf[ii] == 0x0a) || (StrBuf[ii] == 0x0d)) { StrBuf[ii] = 0; ii--; } else { break; } } return ERROR_SUCCESS; } DWORD BackEfsCert( IN LPWSTR EfsFileName, IN LPWSTR PfxFileName ) /*++ Routine Description: Export EFS certificate and keys to a PFX file Arguments: EfsFileName - EFS file PfxFileName - PfxFileName Return Values: Win32 Error code --*/ { BOOLEAN EFSFileExist = FALSE; HCERTSTORE memStore = 0; HCERTSTORE myStore = 0; PCCERT_CONTEXT pCertContext = NULL; LPWSTR ContainerName = NULL; LPWSTR ProviderName = NULL; LPWSTR PfxPassword = NULL; PCRYPT_KEY_PROV_INFO pCryptKeyProvInfo = NULL; HCRYPTKEY hLocalKey = 0; HCRYPTPROV hLocalProv = 0; DWORD Attributes; DWORD RetCode = ERROR_SUCCESS; BOOLEAN ValidCertFound = FALSE; HKEY hRegKey = NULL; CRYPT_HASH_BLOB HashBlob; HANDLE hFile; if ((NULL == EfsFileName) || (NULL == PfxFileName)) { // // EfsFileName & PfxFileName have MAX_PATH available // return ERROR_INVALID_PARAMETER; } if (EfsFileName[0] != 0) { Attributes = GetFileAttributes( EfsFileName ); if (0xFFFFFFFF == Attributes) { // // Failed // return GetLastError(); } if (0 == (Attributes & FILE_ATTRIBUTE_ENCRYPTED)) { return ERROR_FILE_NOT_ENCRYPTED; } EFSFileExist = TRUE; } if (EFSFileExist) { // // Export the certs existing on the file // PENCRYPTION_CERTIFICATE_HASH_LIST pUsers = NULL; RetCode = QueryUsersOnEncryptedFile( EfsFileName, &pUsers ); if ( ERROR_SUCCESS == RetCode) { // // Create a memory cert store // DWORD nCerts = 0; memStore = CertOpenStore( CERT_STORE_PROV_MEMORY, 0, 0, CERT_STORE_MAXIMUM_ALLOWED_FLAG, NULL ); if (memStore) { myStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_REGISTRY_W, 0, // dwEncodingType 0, // hCryptProv, CERT_SYSTEM_STORE_CURRENT_USER, L"My" ); if (myStore) { // // Let's find the cert // nCerts = pUsers->nCert_Hash; while(nCerts){ // // We will export all the certs in MY store. Usually only one. // pCertContext = CertFindCertificateInStore( myStore, CRYPT_ASN_ENCODING, 0, CERT_FIND_HASH, (CRYPT_HASH_BLOB*) pUsers->pUsers[nCerts-1]->pHash, NULL ); if (pCertContext != NULL) { // // Let's try to see if keys are available // pCryptKeyProvInfo = GetKeyProvInfo( pCertContext ); if (pCryptKeyProvInfo) { if (CryptAcquireContext( &hLocalProv, pCryptKeyProvInfo->pwszContainerName, pCryptKeyProvInfo->pwszProvName, PROV_RSA_FULL, CRYPT_SILENT)) { if (CryptGetUserKey(hLocalProv, AT_KEYEXCHANGE, &hLocalKey)) { // // We found the key. Let's add the cert to Memory store // if (CertAddCertificateContextToStore(memStore, pCertContext, CERT_STORE_ADD_ALWAYS, NULL)){ // // Cert added. // ValidCertFound = TRUE; } CryptDestroyKey( hLocalKey ); } CryptReleaseContext( hLocalProv, 0 ); } free (pCryptKeyProvInfo); } CertFreeCertificateContext( pCertContext ); } nCerts--; } if (!ValidCertFound) { // // We didn't get good certs. Warn the user. // DisplayMsg(CIPHER_NO_LOCAL_CERT); } CertCloseStore(myStore, 0); myStore = 0; } else { CertCloseStore( memStore, 0 ); memStore = 0; RetCode = GetLastError(); } } if (pUsers) { FreeEncryptionCertificateHashList(pUsers); pUsers = NULL; } } else { return RetCode; } } else { // // EFS file not provided, try to export the current EFS cert // RetCode = RegOpenKeyEx( KEYPATHROOT, KEYPATH, 0, GENERIC_READ, &hRegKey ); if (RetCode == ERROR_SUCCESS) { DWORD Type; RetCode = RegQueryValueEx( hRegKey, CERT_HASH, NULL, &Type, NULL, &(HashBlob.cbData) ); if (RetCode == ERROR_SUCCESS) { // // Query out the thumbprint, find the cert, and return the key information. // if (HashBlob.pbData = (PBYTE)malloc( HashBlob.cbData )) { RetCode = RegQueryValueEx( hRegKey, CERT_HASH, NULL, &Type, HashBlob.pbData, &(HashBlob.cbData) ); if (RetCode == ERROR_SUCCESS) { // // We get the cert hash. Get the cert from MY to MemStore // memStore = CertOpenStore( CERT_STORE_PROV_MEMORY, 0, 0, CERT_STORE_MAXIMUM_ALLOWED_FLAG, NULL ); if (memStore) { myStore = CertOpenStore( CERT_STORE_PROV_SYSTEM_REGISTRY_W, 0, // dwEncodingType 0, // hCryptProv, CERT_SYSTEM_STORE_CURRENT_USER, L"My" ); if (myStore) { pCertContext = CertFindCertificateInStore( myStore, CRYPT_ASN_ENCODING, 0, CERT_FIND_HASH, (CRYPT_HASH_BLOB*) &HashBlob, NULL ); if (pCertContext != NULL) { // // Let's try to see if keys are available // pCryptKeyProvInfo = GetKeyProvInfo( pCertContext ); if (pCryptKeyProvInfo) { if (CryptAcquireContext( &hLocalProv, pCryptKeyProvInfo->pwszContainerName, pCryptKeyProvInfo->pwszProvName, PROV_RSA_FULL, CRYPT_SILENT)) { if (CryptGetUserKey(hLocalProv, AT_KEYEXCHANGE, &hLocalKey)) { // // We found the key. Let's add the cert to Memory store // if (CertAddCertificateContextToStore(memStore, pCertContext, CERT_STORE_ADD_ALWAYS, NULL)){ // // Cert added. // ValidCertFound = TRUE; } CryptDestroyKey( hLocalKey ); } CryptReleaseContext( hLocalProv, 0 ); } free (pCryptKeyProvInfo); } CertFreeCertificateContext( pCertContext ); } CertCloseStore(myStore, 0); myStore = 0; } } } free(HashBlob.pbData); } } RegCloseKey( hRegKey ); } } if (ValidCertFound && memStore && (RetCode == ERROR_SUCCESS)) { // // We got the cert ready to export. Warn the user with message box. // LPWSTR WarnText = NULL; LPWSTR WarnTitle= NULL; CRYPT_DATA_BLOB PFX; DWORD TextLen = 0; int ButtonID = 0; RetCode = GetResourceString(&WarnText, CIPHER_BACKUP_OK); if (ERROR_SUCCESS == RetCode) { RetCode = GetResourceString(&WarnTitle, CIPHER_BACKUP_WRN); } if (ERROR_SUCCESS == RetCode) { ButtonID = MessageBox(NULL, WarnText, WarnTitle, MB_OKCANCEL | MB_TOPMOST); LocalFree( WarnText ); LocalFree( WarnTitle ); } else { if (WarnText) { LocalFree( WarnText ); } if (WarnTitle) { LocalFree( WarnTitle ); } } if (ButtonID == IDOK) { if (PfxFileName[0] == 0 ) { // // Let's try to get the PFX file name // RetCode = GetUserInput( CIPHER_PFX_FILENAME_NEEDED, PfxFileName, MAX_PATH ); if (ERROR_SUCCESS != RetCode) { PfxFileName[0] = 0; } } // // Adding the .PFX if the file extension not provided // TextLen = wcslen(PfxFileName); if ((PfxFileName[0] != 0) && TextLen > 4) { if (lstricmp(&PfxFileName[ TextLen - 4], L".PFX")){ if (TextLen <= MAX_PATH - 5) { wcscpy(&PfxFileName[TextLen], L".PFX"); } else { RetCode = ERROR_BUFFER_OVERFLOW; PfxFileName[0] = 0; } } } else if (PfxFileName[0] != 0) { wcscpy(&PfxFileName[TextLen], L".PFX"); } if (PfxFileName[0]) { if (GetPassword( &PfxPassword )) { 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 // hFile = CreateFileW( PfxFileName, 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, &TextLen, NULL )){ RetCode = GetLastError(); DisplayMsg(CIPHER_CERTS_BACKUP_FAILED); } else { DisplayMsg(CIPHER_CERTS_BACKED); RetCode = ERROR_SUCCESS; } CloseHandle(hFile); } else { RetCode = GetLastError(); DisplayMsg(CIPHER_CERTS_BACKUP_FAILED); } } else { RetCode = GetLastError(); DisplayMsg(CIPHER_CERTS_BACKUP_FAILED); } free( PFX.pbData ); } else { RetCode = ERROR_NOT_ENOUGH_MEMORY; DisplayMsg(CIPHER_CERTS_BACKUP_FAILED); } } if (PfxPassword) { free(PfxPassword); PfxPassword = 0; } } else { RetCode = GetLastError(); } } else { RetCode = ERROR_INVALID_PARAMETER; DisplayMsg(CIPHER_CERTS_BACKUP_FAILED); } } } if (memStore) { CertCloseStore(memStore, 0); } return RetCode; } 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) { // // We need space for \ * and NULL. That is where 3 comes from. // if (wcslen(pch+1) > ((sizeof(StartingDirectory) / sizeof (TCHAR)) - 3)){ // // The dir is too long for us // DisplayErr(NULL, ERROR_BUFFER_OVERFLOW); return; } else { 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("/x"), 2)){ DWORD dLen; ExportEfsCert = TRUE; // // Using FileSpec for the source encrypted file. // Using DirectorySpec for the destination PFX file. // CIPHER /X[:EFSFILE] [PFXFILE] // DirectorySpec[0] = 0; FileSpec[0] = 0; dLen = wcslen(argv[i]); if ((dLen > 3) && argv[i][2] == ':') { // // Try to get the EFSFILE // if ( (dLen -3) >= MAX_PATH) { // // 3 is for /X: // DisplayMsg(CIPHER_INVALID_PATH, &(argv[i][3])); return; } else { lstrcpy( FileSpec, &(argv[i][3])); } } if ((i+1) < argc) { // // Try to get the target PFX file name // if (wcslen(argv[i+1]) >= MAX_PATH) { DisplayMsg(CIPHER_INVALID_PATH, argv[i+1]); return; } else { lstrcpy( DirectorySpec, argv[i+1]); } } break; } else if (0 == lstrnicmp(argv[i], TEXT("/r"), 2)){ PTCHAR pch; GenerateDRA = TRUE; pch = lstrchr(argv[i], ':'); if (NULL != pch) { // // We will append .CER (PFX) to the path. Plus the NULL. Extra 5 TCHARs are needed. // if (wcslen(pch+1) > ((sizeof(StartingDirectory) / sizeof (TCHAR)) - 5)){ // // The path is too long for us // DisplayErr(NULL, ERROR_BUFFER_OVERFLOW); return; }else { 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) { if (wcslen(pch+1) > ((sizeof(StartingDirectory) / sizeof (TCHAR)) - 1)){ // // The path is too long for us // DisplayErr(NULL, ERROR_BUFFER_OVERFLOW); return; }else { 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 (ExportEfsCert) { DWORD RetCode; RetCode = BackEfsCert(FileSpec, DirectorySpec); if (RetCode != ERROR_SUCCESS) { DisplayErr(NULL, RetCode); } 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])){ 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) { DWORD PathLen; // // 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 { PathLen = GetCurrentDirectory( MAX_PATH, StartingDirectory ); if (0 == PathLen) { DisplayErr(NULL, GetLastError()); return; } else if (PathLen >= MAX_PATH) { DisplayErr(NULL, ERROR_BUFFER_OVERFLOW); return; } } PathLen = GetFullPathName( TEXT("*"), MAX_PATH, DirectorySpec, &p ); if (0 == PathLen) { DisplayErr(NULL, GetLastError()); return; } if (PathLen >= MAX_PATH) { DisplayErr(NULL, ERROR_BUFFER_OVERFLOW); return; } // // If PathLen < MAX_PATH, than wcslen(P) < MAX_PATH // lstrcpy( FileSpec, p ); *p = '\0'; (VOID)(ActionRoutine)( DirectorySpec, FileSpec ); } else { // // Get our current directory because the action routines might move us // around // DWORD PathLen; if (!DoSubdirectories) { PathLen = GetCurrentDirectory( MAX_PATH, StartingDirectory ); if (0 == PathLen) { DisplayErr(NULL, GetLastError()); return; } else if (PathLen >= MAX_PATH) { DisplayErr(NULL, ERROR_BUFFER_OVERFLOW); return; } } 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; // // 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) || ( PathLen > (MAX_PATH - 1)) ){ 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]) { if (PathLen >= MAX_PATH - 2 ) { DisplayMsg(CIPHER_INVALID_PATH, argv[i]); break; } else { lstrcat(DirectorySpec, TEXT(".")); } } } if (p != NULL) { if (wcslen(p) >= (sizeof(FileSpec) / sizeof (TCHAR))) { DisplayMsg(CIPHER_INVALID_PATH, argv[i]); break; } else { 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)(); }