Skip to content

Commit

Permalink
Added /Locate Operation
Browse files Browse the repository at this point in the history
- Added /Locate operation.  This non-security operation performs a file
name search and records the name and file metadata to a CSV file.
- Changed all output files to be UTF-8 since it is smaller in most cases
and allows Excel to recognize CSV file formats automatically.
- Corrected issue where /RestoreSecurity was prematurely freeing
security descriptor memory.
- Changed /BackupSecurity and /RestoreSecurity to use a pipe '|'
delimiter character since '=' was an allowed character in file names.
  • Loading branch information
NoMoreFood committed Sep 18, 2017
1 parent c08256a commit 193758d
Show file tree
Hide file tree
Showing 16 changed files with 256 additions and 53 deletions.
Binary file removed .DS_Store
Binary file not shown.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -186,6 +186,7 @@ ClientBin/
*.publishsettings
node_modules/
orleans.codegen.cs
.DS_Store

# RIA/Silverlight projects
Generated_Code/
Expand Down
2 changes: 2 additions & 0 deletions Functions.h
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,7 @@ std::wstring GenerateAccessMask(DWORD iCurrentMask);
std::wstring GenerateInheritanceFlags(DWORD iCurrentFlags);
HANDLE RegisterFileHandle(HANDLE hFile, std::wstring sOperation);
bool CheckIfAntivirusIsActive();
std::wstring FileTimeToString(LPFILETIME tFileTime);
BOOL WriteToFile(std::wstring & sStringToWrite, HANDLE hFile);


48 changes: 47 additions & 1 deletion Helpers.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -365,7 +365,8 @@ HANDLE RegisterFileHandle(HANDLE hFile, std::wstring sOperation)
sPath.resize(iSize + 1);

// get the full name
if (GetFinalPathNameByHandle(hFile, (LPWSTR)sPath.data(), (DWORD)sPath.capacity(), VOLUME_NAME_NT) == 0)
if (GetFinalPathNameByHandle(hFile, (LPWSTR)sPath.data(), (DWORD)sPath.capacity(),
FILE_NAME_NORMALIZED | VOLUME_NAME_NT) == 0)
{
wprintf(L"ERROR: The true path to the specified file could not be determined.\n");
exit(-1);
Expand Down Expand Up @@ -457,4 +458,49 @@ bool CheckIfAntivirusIsActive()
// return status
PtrProductList->Release();
return bIsInstalled;
}

std::wstring FileTimeToString(LPFILETIME tFileTime)
{
// the date format function require system time structure
SYSTEMTIME tTime;
FileTimeToSystemTime(tFileTime, &tTime);

// convert the date to a string and return
WCHAR sTime[24];
GetDateFormatEx(LOCALE_NAME_INVARIANT, LOCALE_USE_CP_ACP, NULL,
L"yyyy'-'MM'-'dd ", sTime, _countof(sTime), NULL);
GetTimeFormatEx(LOCALE_NAME_INVARIANT, LOCALE_USE_CP_ACP, NULL,
L"HH':'mm':'ss", sTime + wcslen(sTime), (int)
(_countof(sTime) - wcslen(sTime)));
return std::wstring(sTime);
}

BOOL WriteToFile(std::wstring & sStringToWrite, HANDLE hFile)
{
// see how many characters we need to store as utf-8
int iChars = WideCharToMultiByte(CP_UTF8, 0,
sStringToWrite.c_str(), (int) sStringToWrite.length(),
NULL, 0, NULL, NULL);
if (iChars == 0)
{
return FALSE;
}

// allocate and do the conversion
BYTE * sString = (BYTE *) malloc(iChars);
iChars = WideCharToMultiByte(CP_UTF8, 0,
sStringToWrite.c_str(), (int) sStringToWrite.length(),
(LPSTR) sString, iChars, NULL, NULL);
if (iChars == 0)
{
free(sString);
return FALSE;
}

// write to file, free memory, and return result
DWORD iBytes = 0;
BOOL bResult = WriteFile(hFile, sString, iChars, &iBytes, NULL);
free(sString);
return bResult;
}
17 changes: 11 additions & 6 deletions Main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,10 @@ void AnalyzeSecurity(ObjectEntry & oEntry)
PACL tAclSacl = NULL;
PSID tOwnerSid = NULL;
PSID tGroupSid = NULL;
PSECURITY_DESCRIPTOR tDesc;
PSECURITY_DESCRIPTOR tDesc = NULL;
DWORD iError = 0;
if ((iError = GetNamedSecurityInfo(oEntry.Name.c_str(), SE_FILE_OBJECT,
if (iInformationToLookup != 0 &&
(iError = GetNamedSecurityInfo(oEntry.Name.c_str(), SE_FILE_OBJECT,
iInformationToLookup, (bFetchOwner) ? &tOwnerSid : NULL, (bFetchGroup) ? &tGroupSid : NULL,
(bFetchDacl) ? &tAclDacl : NULL, (bFetchSacl) ? &tAclSacl : NULL, &tDesc)) != ERROR_SUCCESS)
{
Expand All @@ -94,7 +95,7 @@ void AnalyzeSecurity(ObjectEntry & oEntry)
bool bSaclCleanupRequired = false;
bool bOwnerCleanupRequired = false;
bool bGroupCleanupRequired = false;
bool bDescCleanupRequired = true;
bool bDescCleanupRequired = (tDesc != NULL);

// used for one-shot operations like reset children or inheritance
DWORD iSpecialCommitMergeFlags = 0;
Expand All @@ -112,7 +113,11 @@ void AnalyzeSecurity(ObjectEntry & oEntry)

// merge any special commit flags
iSpecialCommitMergeFlags |= (*oOperation)->SpecialCommitFlags;


if ((*oOperation)->AppliesToObject)
{
(*oOperation)->ProcessObjectAction(oEntry);
}
if ((*oOperation)->AppliesToDacl)
{
bDaclIsDirty |= (*oOperation)->ProcessAclAction(L"DACL", oEntry, tAclDacl, bDaclCleanupRequired);
Expand All @@ -138,7 +143,6 @@ void AnalyzeSecurity(ObjectEntry & oEntry)
if (bSaclCleanupRequired) { LocalFree(tAclDacl); bSaclCleanupRequired = false; };
if (bOwnerCleanupRequired) { LocalFree(tAclDacl); bOwnerCleanupRequired = false; };
if (bGroupCleanupRequired) { LocalFree(tAclDacl); bGroupCleanupRequired = false; };
if (bDescCleanupRequired) { LocalFree(tDesc); bDescCleanupRequired = false; };

// extract the elements from the raw security descriptor
BOOL bItemPresent = FALSE;
Expand Down Expand Up @@ -470,7 +474,8 @@ int wmain(int iArgs, WCHAR * aArgs[])
oOperation->AppliesToSacl ||
oOperation->AppliesToOwner ||
oOperation->AppliesToGroup ||
oOperation->AppliesToSd)
oOperation->AppliesToSd ||
oOperation->AppliesToObject)
{
// do exclusivity check and error
if (bExclusiveOperation && oOperation->ExclusiveOperation)
Expand Down
1 change: 1 addition & 0 deletions Operation.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,7 @@ void Operation::ProcessGranularTargetting(std::wstring sScope)
AppliesToSacl = false;
AppliesToOwner = false;
AppliesToGroup = false;
AppliesToObject = false;

for (std::vector<std::wstring>::iterator oScope = sScopeOpts.begin();
oScope != sScopeOpts.end(); oScope++)
Expand Down
2 changes: 2 additions & 0 deletions Operation.h
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@ class Operation
bool AppliesToOwner = false;
bool AppliesToGroup = false;
bool AppliesToSd = false;
bool AppliesToObject = false;

bool AppliesToRootOnly = false;
bool AppliesToChildrenOnly = false;
Expand All @@ -86,6 +87,7 @@ class Operation
virtual bool ProcessAclAction(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PACL & tCurrentAcl, bool & bAclReplacement);
virtual bool ProcessSidAction(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PSID & tCurrentSid, bool & bSidReplacement);
virtual SidActionResult DetermineSid(WCHAR * const sSdPart, ObjectEntry & tObjectEntry, PSID const tCurrentSid, PSID & tResultantSid) { return SidActionResult::Nothing; }
virtual void ProcessObjectAction(ObjectEntry & tObjectEntry) { return; }

Operation(std::queue<std::wstring> & oArgList);
};
Expand Down
11 changes: 5 additions & 6 deletions OperationBackupSecurity.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,11 @@ OperationBackupSecurity::OperationBackupSecurity(std::queue<std::wstring> & oArg
}

// write out the file type marker
USHORT hHeader = 0xFEFF;
BYTE hHeader[] = { 0xEF,0xBB,0xBF };
DWORD iBytes = 0;
if (WriteFile(hFile, &hHeader, sizeof(USHORT), &iBytes, NULL) == 0)
if (WriteFile(hFile, &hHeader, _countof(hHeader), &iBytes, NULL) == 0)
{
wprintf(L"ERROR: Could not write out report file type marker '%s'.\n", GetCommand().c_str());
wprintf(L"ERROR: Could not write out file type marker '%s'.\n", GetCommand().c_str());
exit(-1);
}

Expand All @@ -52,9 +52,8 @@ bool OperationBackupSecurity::ProcessSdAction(std::wstring & sFileName, ObjectEn
}

// write the string to a file
DWORD iBytes = 0;
std::wstring sToWrite = sFileName + L"=" + sInfo + L"\r\n";
if (WriteFile(hFile, sToWrite.c_str(), (DWORD)sToWrite.size() * sizeof(WCHAR), &iBytes, NULL) == 0)
std::wstring sToWrite = sFileName + L"|" + sInfo + L"\r\n";
if (WriteToFile(sToWrite, hFile) == 0)
{
LocalFree(sInfo);
InputOutput::AddError(L"ERROR: Unable to write security descriptor to file.");
Expand Down
63 changes: 35 additions & 28 deletions OperationHelp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,23 @@ Commands That Do Not Alter Security
Reports any instance of a null ACL. A null ACL, unlike an empty ACL, allows
all access (i.e., similar to an ACE with 'Everyone' with 'Full Control')
/Locate <FileName> <RegularExpression>
This command will write a comma separated value file with the fields of
filename, creation time, file modified time, file size and file attributes.
The regular expression will perform a case insensitive regular expression
search against file name or directory name. To report all data, pass .*
as the regular expression.
/Report <FileName> <RegularExpression>
This command will write a comma separated value file with the fields of
filename, security descriptor part (e.g., DACL), account name, permissions,
and inheritance flags. The regular expression will perform a case
insensitive regular expression search against the account name in
DOMAIN\user format. To report all data, pass .* as the regular expression.
An optional qualifier after regular expression can be specified after the
regular expression to refine what part of the security descriptor to scan.
See Other Notes & Limitations section for more information.
Commands That Can Alter Security (When /WhatIf Is Not Present)
--------------------------------
/AddAccountIfMissing <Name|Sid>
Expand All @@ -149,29 +166,29 @@ Commands That Can Alter Security (When /WhatIf Is Not Present)
degradation.
/CopyDomain <SourceDomainName>:<TargetDomainName>
This command is identical to /MoveDomain except that the original
entry referring the SourceDomainName is retained instead of replaced.
This command only applies to the SACL and the DACL. If this command is
used multiple times, it is recommended to use /Compact to ensure there
are not any redundant access control entries.
/MoveDomain <SourceDomainName>:<TargetDomainName>
This command will look to see whether any account in <SourceDomain>
has an identically-named account in <TargetDomain>. If so, any entires
are converted to use the new domain. For example,
'OldDomain\Domain Admins' would become 'NewDomain\Domain Admins'. Since
this operation relies on the names being resolvable, specifying a SID
instead of domain name for this command does not work.
This command is identical to /MoveDomain except that the original
entry referring the SourceDomainName is retained instead of replaced.
This command only applies to the SACL and the DACL. If this command is
used multiple times, it is recommended to use /Compact to ensure there
are not any redundant access control entries.
)";

std::wcout <<
LR"(
/MoveDomain <SourceDomainName>:<TargetDomainName>
This command will look to see whether any account in <SourceDomain>
has an identically-named account in <TargetDomain>. If so, any entires
are converted to use the new domain. For example,
'OldDomain\Domain Admins' would become 'NewDomain\Domain Admins'. Since
this operation relies on the names being resolvable, specifying a SID
instead of domain name for this command does not work.
/RemoveAccount <Name|Sid>
Will remove <Name> from the security descriptor. If the specified name
is found as the file owner, the owner is replaced by the builtin
Administrators group. If the specified name is found as the group owner
(a defunct attribute that has no function in terms of security), it is
also replace with the built-in Administrators group.
Will remove <Name> from the security descriptor. If the specified name
is found as the file owner, the owner is replaced by the builtin
Administrators group. If the specified name is found as the group owner
(a defunct attribute that has no function in terms of security), it is
also replace with the built-in Administrators group.
/RemoveOrphans <Domain|Sid>
Remove any account whose SID is derived from the <Domain> specified
Expand All @@ -187,16 +204,6 @@ LR"(
/ReplaceAccount <SearchAccount>:<ReplaceAccount>
Search for an account and replace it with another account.
/Report <FileName> <RegularExpression>
This command will write a comma separated value file with the fields of
filename, security descriptor part (e.g., DACL), account name, permissions,
and inheritance flags. The regular expression will perform a case
insensitive regular expression search against the account name in
DOMAIN\user format. To report all data, pass .* as the regular expression.
An optional qualifier after regular expression can be specified after the
regular expression to refine what part of the security descriptor to scan.
See Other Notes & Limitations section for more information.
/RestoreSecurity <FileName>
The reverse operation of /BackupSecurity. Takes the file name and security
descriptors specified in the file specified and applies them to the file
Expand Down
108 changes: 108 additions & 0 deletions OperationLocate.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
#include "OperationLocate.h"
#include "InputOutput.h"
#include "Functions.h"

ClassFactory<OperationLocate> * OperationLocate::RegisteredFactory =
new ClassFactory<OperationLocate>(GetCommand());

#define Q(x) L"\"" + x + L"\""

OperationLocate::OperationLocate(std::queue<std::wstring> & oArgList) : Operation(oArgList)
{
// exit if there are not enough arguments to part
std::vector<std::wstring> sReportFile = ProcessAndCheckArgs(1, oArgList, L"\\0");
std::vector<std::wstring> sMatchAndArgs = ProcessAndCheckArgs(1, oArgList, L"\\0");

// fetch params
HANDLE hFile = CreateFile(sReportFile[0].c_str(), GENERIC_WRITE,
FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);

// see if names could be resolved
if (hFile == INVALID_HANDLE_VALUE)
{
// complain
wprintf(L"ERROR: Could not create file '%s' specified for parameter '%s'.\n", sReportFile[0].c_str(), GetCommand().c_str());
exit(-1);
}

// register the file handle
hReportFile = RegisterFileHandle(hFile, GetCommand());

// if this is the first handle using this file, write out a header
if (hFile == hReportFile)
{
// write out the file type marker
BYTE hHeader[] = { 0xEF,0xBB,0xBF };
DWORD iBytes = 0;
if (WriteFile(hFile, &hHeader, _countof(hHeader), &iBytes, NULL) == 0)
{
wprintf(L"ERROR: Could not write out file type marker '%s'.\n", GetCommand().c_str());
exit(-1);
}

// write out the header
std::wstring sToWrite = std::wstring(L"") + Q(L"Path") + L"," + Q(L"Creation Time") + L"," +
Q(L"Modified Time") + L"," + Q(L"Size") + L"," + Q(L"Attributes") + L"\r\n";
if (WriteToFile(sToWrite, hReportFile) == 0)
{
wprintf(L"ERROR: Could not write header to report file for parameter '%s'.\n", GetCommand().c_str());
exit(-1);
}
}

// only flag this to apply to the core object with the file name
AppliesToObject = true;

// compile the regular expression
try
{
tRegex = std::wregex(sMatchAndArgs[0], std::wregex::icase | std::wregex::optimize);
}
catch (const std::regex_error &)
{
wprintf(L"ERROR: Invalid regular expression '%s' specified for parameter '%s'.\n", sMatchAndArgs[0].c_str(), GetCommand().c_str());
exit(-1);
}
}

void OperationLocate::ProcessObjectAction(ObjectEntry & tObjectEntry)
{
// skip any file names that do not match the regex
const WCHAR * sFileName = tObjectEntry.Name.c_str();
if (wcschr(sFileName, '\\') != NULL) sFileName = wcschr(sFileName, '\\');
if (!std::regex_match(sFileName, tRegex)) return;

// fetch file attribute data
WIN32_FILE_ATTRIBUTE_DATA tData;
GetFileAttributesExW(tObjectEntry.Name.c_str(), GetFileExInfoStandard, &tData);

// convert the file size to a string
WCHAR sSize[32];
ULARGE_INTEGER iFileSize;
iFileSize.LowPart = tData.nFileSizeLow;
iFileSize.HighPart = tData.nFileSizeHigh;
setlocale(LC_NUMERIC, "");
wsprintf(sSize, L"%I64u", iFileSize.QuadPart);

// decode attributes
std::wstring sAttributes = L"";
if (tData.dwFileAttributes & FILE_ATTRIBUTE_READONLY) sAttributes += L"R";
if (tData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) sAttributes += L"H";
if (tData.dwFileAttributes & FILE_ATTRIBUTE_SYSTEM) sAttributes += L"S";
if (tData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) sAttributes += L"D";
if (tData.dwFileAttributes & FILE_ATTRIBUTE_ARCHIVE) sAttributes += L"A";
if (tData.dwFileAttributes & FILE_ATTRIBUTE_TEMPORARY) sAttributes += L"T";
if (tData.dwFileAttributes & FILE_ATTRIBUTE_COMPRESSED) sAttributes += L"C";
if (tData.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) sAttributes += L"O";
if (tData.dwFileAttributes & FILE_ATTRIBUTE_NOT_CONTENT_INDEXED) sAttributes += L"N";
if (tData.dwFileAttributes & FILE_ATTRIBUTE_ENCRYPTED) sAttributes += L"E";

// write the string to a file
std::wstring sToWrite = std::wstring(L"") + Q(tObjectEntry.Name) + L"," +
Q(FileTimeToString(&tData.ftCreationTime)) + L"," + Q(FileTimeToString(&tData.ftLastWriteTime)) +
L"," + Q(sSize) + L"," + Q(sAttributes) + L"\r\n";
if (WriteToFile(sToWrite, hReportFile) == 0)
{
InputOutput::AddError(L"ERROR: Unable to write security information to report file.");
}
}
Loading

0 comments on commit 193758d

Please sign in to comment.