/* * ReactOS Control ACLs Program * Copyright (C) 2006 Thomas Weidenmueller * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ #include "precomp.h" /* command line options */ BOOL OptionT = FALSE, OptionE = FALSE, OptionC = FALSE; BOOL OptionG = FALSE, OptionR = FALSE, OptionP = FALSE, OptionD = FALSE; LPCTSTR GUser, GPerm, RUser, PUser, PPerm, DUser; static GENERIC_MAPPING FileGenericMapping = { FILE_GENERIC_READ, FILE_GENERIC_WRITE, FILE_GENERIC_EXECUTE, FILE_ALL_ACCESS }; static VOID PrintError(DWORD dwError) { if (dwError == ERROR_SUCCESS) return; ConMsgPuts(StdErr, FORMAT_MESSAGE_FROM_SYSTEM, NULL, dwError, LANG_USER_DEFAULT); } static BOOL PrintFileDacl(IN LPTSTR FilePath, IN LPTSTR FileName) { SIZE_T Length; PSECURITY_DESCRIPTOR SecurityDescriptor; DWORD SDSize = 0; TCHAR FullFileName[MAX_PATH + 1]; BOOL Error = FALSE, Ret = FALSE; Length = _tcslen(FilePath) + _tcslen(FileName); if (Length > MAX_PATH) { /* file name too long */ SetLastError(ERROR_FILE_NOT_FOUND); return FALSE; } _tcscpy(FullFileName, FilePath); _tcscat(FullFileName, FileName); /* find out how much memory we need */ if (!GetFileSecurity(FullFileName, DACL_SECURITY_INFORMATION, NULL, 0, &SDSize) && GetLastError() != ERROR_INSUFFICIENT_BUFFER) { return FALSE; } SecurityDescriptor = (PSECURITY_DESCRIPTOR)HeapAlloc(GetProcessHeap(), 0, SDSize); if (SecurityDescriptor != NULL) { if (GetFileSecurity(FullFileName, DACL_SECURITY_INFORMATION, SecurityDescriptor, SDSize, &SDSize)) { PACL Dacl; BOOL DaclPresent; BOOL DaclDefaulted; if (GetSecurityDescriptorDacl(SecurityDescriptor, &DaclPresent, &Dacl, &DaclDefaulted)) { if (DaclPresent) { PACCESS_ALLOWED_ACE Ace; DWORD AceIndex = 0; /* dump the ACL */ while (GetAce(Dacl, AceIndex, (PVOID*)&Ace)) { SID_NAME_USE Use; DWORD NameSize = 0; DWORD DomainSize = 0; LPTSTR Name = NULL; LPTSTR Domain = NULL; LPTSTR SidString = NULL; DWORD IndentAccess; DWORD AccessMask = Ace->Mask; PSID Sid = (PSID)&Ace->SidStart; /* attempt to translate the SID into a readable string */ if (!LookupAccountSid(NULL, Sid, Name, &NameSize, Domain, &DomainSize, &Use)) { if (GetLastError() == ERROR_NONE_MAPPED || NameSize == 0) { goto BuildSidString; } else { if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) { Error = TRUE; break; } Name = (LPTSTR)HeapAlloc(GetProcessHeap(), 0, (NameSize + DomainSize) * sizeof(TCHAR)); if (Name == NULL) { SetLastError(ERROR_NOT_ENOUGH_MEMORY); Error = TRUE; break; } Domain = Name + NameSize; Name[0] = _T('\0'); if (DomainSize != 0) Domain[0] = _T('\0'); if (!LookupAccountSid(NULL, Sid, Name, &NameSize, Domain, &DomainSize, &Use)) { HeapFree(GetProcessHeap(), 0, Name); Name = NULL; goto BuildSidString; } } } else { BuildSidString: if (!ConvertSidToStringSid(Sid, &SidString)) { Error = TRUE; break; } } /* print the file name or space */ ConPrintf(StdOut, L"%s ", FullFileName); /* attempt to map the SID to a user name */ if (AceIndex == 0) { DWORD i = 0; /* overwrite the full file name with spaces so we only print the file name once */ while (FullFileName[i] != _T('\0')) FullFileName[i++] = _T(' '); } /* print the domain and/or user if possible, or the SID string */ if (Name != NULL && Domain[0] != _T('\0')) { ConPrintf(StdOut, L"%s\\%s:", Domain, Name); IndentAccess = (DWORD)_tcslen(Domain) + _tcslen(Name); } else { LPTSTR DisplayString = (Name != NULL ? Name : SidString); ConPrintf(StdOut, L"%s:", DisplayString); IndentAccess = (DWORD)_tcslen(DisplayString); } /* print the ACE Flags */ if (Ace->Header.AceFlags & CONTAINER_INHERIT_ACE) { IndentAccess += ConResPuts(StdOut, IDS_ABBR_CI); } if (Ace->Header.AceFlags & OBJECT_INHERIT_ACE) { IndentAccess += ConResPuts(StdOut, IDS_ABBR_OI); } if (Ace->Header.AceFlags & INHERIT_ONLY_ACE) { IndentAccess += ConResPuts(StdOut, IDS_ABBR_IO); } IndentAccess += 2; /* print the access rights */ MapGenericMask(&AccessMask, &FileGenericMapping); if (Ace->Header.AceType & ACCESS_DENIED_ACE_TYPE) { if (AccessMask == FILE_ALL_ACCESS) { ConResPuts(StdOut, IDS_ABBR_NONE); } else { ConResPuts(StdOut, IDS_DENY); goto PrintSpecialAccess; } } else { if (AccessMask == FILE_ALL_ACCESS) { ConResPuts(StdOut, IDS_ABBR_FULL); } else if (!(Ace->Mask & (GENERIC_READ | GENERIC_EXECUTE)) && AccessMask == (FILE_GENERIC_READ | FILE_EXECUTE)) { ConResPuts(StdOut, IDS_ABBR_READ); } else if (AccessMask == (FILE_GENERIC_READ | FILE_GENERIC_WRITE | FILE_EXECUTE | DELETE)) { ConResPuts(StdOut, IDS_ABBR_CHANGE); } else if (AccessMask == FILE_GENERIC_WRITE) { ConResPuts(StdOut, IDS_ABBR_WRITE); } else { DWORD x, x2; static const struct { DWORD Access; UINT uID; } AccessRights[] = { {FILE_WRITE_ATTRIBUTES, IDS_FILE_WRITE_ATTRIBUTES}, {FILE_READ_ATTRIBUTES, IDS_FILE_READ_ATTRIBUTES}, {FILE_DELETE_CHILD, IDS_FILE_DELETE_CHILD}, {FILE_EXECUTE, IDS_FILE_EXECUTE}, {FILE_WRITE_EA, IDS_FILE_WRITE_EA}, {FILE_READ_EA, IDS_FILE_READ_EA}, {FILE_APPEND_DATA, IDS_FILE_APPEND_DATA}, {FILE_WRITE_DATA, IDS_FILE_WRITE_DATA}, {FILE_READ_DATA, IDS_FILE_READ_DATA}, {FILE_GENERIC_EXECUTE, IDS_FILE_GENERIC_EXECUTE}, {FILE_GENERIC_WRITE, IDS_FILE_GENERIC_WRITE}, {FILE_GENERIC_READ, IDS_FILE_GENERIC_READ}, {GENERIC_ALL, IDS_GENERIC_ALL}, {GENERIC_EXECUTE, IDS_GENERIC_EXECUTE}, {GENERIC_WRITE, IDS_GENERIC_WRITE}, {GENERIC_READ, IDS_GENERIC_READ}, {MAXIMUM_ALLOWED, IDS_MAXIMUM_ALLOWED}, {ACCESS_SYSTEM_SECURITY, IDS_ACCESS_SYSTEM_SECURITY}, {SPECIFIC_RIGHTS_ALL, IDS_SPECIFIC_RIGHTS_ALL}, {STANDARD_RIGHTS_REQUIRED, IDS_STANDARD_RIGHTS_REQUIRED}, {SYNCHRONIZE, IDS_SYNCHRONIZE}, {WRITE_OWNER, IDS_WRITE_OWNER}, {WRITE_DAC, IDS_WRITE_DAC}, {READ_CONTROL, IDS_READ_CONTROL}, {DELETE, IDS_DELETE}, {STANDARD_RIGHTS_ALL, IDS_STANDARD_RIGHTS_ALL}, }; ConResPuts(StdOut, IDS_ALLOW); PrintSpecialAccess: ConResPuts(StdOut, IDS_SPECIAL_ACCESS); /* print the special access rights */ x = ARRAYSIZE(AccessRights); while (x-- != 0) { if ((Ace->Mask & AccessRights[x].Access) == AccessRights[x].Access) { ConPrintf(StdOut, L"\n%s ", FullFileName); for (x2 = 0; x2 < IndentAccess; x2++) { ConPuts(StdOut, L" "); } ConResPuts(StdOut, AccessRights[x].uID); } } ConPuts(StdOut, L"\n"); } } ConPuts(StdOut, L"\n"); /* free up all resources */ if (Name != NULL) { HeapFree(GetProcessHeap(), 0, Name); } if (SidString != NULL) { LocalFree((HLOCAL)SidString); } AceIndex++; } if (!Error) Ret = TRUE; } else { SetLastError(ERROR_NO_SECURITY_ON_OBJECT); } } } HeapFree(GetProcessHeap(), 0, SecurityDescriptor); } else { SetLastError(ERROR_NOT_ENOUGH_MEMORY); } return Ret; } /* add a backslash at end to a path string if necessary */ static VOID AddBackslash(LPTSTR FilePath) { INT len = lstrlen(FilePath); LPTSTR pch = CharPrev(FilePath, FilePath + len); if (*pch != _T('\\')) lstrcat(pch, _T("\\")); } static BOOL GetPathOfFile(LPTSTR FilePath, LPCTSTR pszFiles) { TCHAR FullPath[MAX_PATH]; LPTSTR pch; DWORD attrs; lstrcpyn(FilePath, pszFiles, MAX_PATH); pch = _tcsrchr(FilePath, _T('\\')); if (pch != NULL) { *pch = 0; if (!GetFullPathName(FilePath, MAX_PATH, FullPath, NULL)) { PrintError(GetLastError()); return FALSE; } lstrcpyn(FilePath, FullPath, MAX_PATH); attrs = GetFileAttributes(FilePath); if (attrs == 0xFFFFFFFF || !(attrs & FILE_ATTRIBUTE_DIRECTORY)) { PrintError(ERROR_DIRECTORY); return FALSE; } } else GetCurrentDirectory(MAX_PATH, FilePath); AddBackslash(FilePath); return TRUE; } static BOOL PrintDaclsOfFiles(LPCTSTR pszFiles) { TCHAR FilePath[MAX_PATH]; WIN32_FIND_DATA FindData; HANDLE hFind; DWORD LastError; /* * get the file path */ if (!GetPathOfFile(FilePath, pszFiles)) return FALSE; /* * search for the files */ hFind = FindFirstFile(pszFiles, &FindData); if (hFind == INVALID_HANDLE_VALUE) return FALSE; do { if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue; if (!PrintFileDacl(FilePath, FindData.cFileName)) { LastError = GetLastError(); if (LastError == ERROR_ACCESS_DENIED) { PrintError(LastError); if (!OptionC) { FindClose(hFind); return FALSE; } } else { break; } } else { ConPuts(StdOut, L"\n"); } } while(FindNextFile(hFind, &FindData)); LastError = GetLastError(); FindClose(hFind); if (LastError != ERROR_NO_MORE_FILES) { PrintError(LastError); return FALSE; } return TRUE; } static BOOL GrantUserAccessRights(LPCTSTR FilePath, LPCTSTR File, LPCTSTR User, TCHAR Perm) { /* TODO & FIXME */ switch(Perm) { case _T('R'): // Read break; case _T('W'): // Write break; case _T('C'): // Change (write) break; case _T('F'): // Full control break; default: break; } return FALSE; } static BOOL ReplaceUserAccessRights( LPCTSTR FilePath, LPCTSTR File, LPCTSTR User, TCHAR Perm) { /* TODO & FIXME */ switch(Perm) { case _T('N'): // None break; case _T('R'): // Read break; case _T('W'): // Write break; case _T('C'): // Change (write) break; case _T('F'): // Full control break; default: break; } return FALSE; } static BOOL EditUserAccessRights( LPCTSTR FilePath, LPCTSTR File, LPCTSTR User, TCHAR Perm) { /* TODO & FIXME */ switch(Perm) { case _T('N'): // None break; case _T('R'): // Read break; case _T('W'): // Write break; case _T('C'): // Change (write) break; case _T('F'): // Full control break; default: break; } return FALSE; } static BOOL DenyUserAccess(LPCTSTR FilePath, LPCTSTR File, LPCTSTR User) { /* TODO & FIXME */ return FALSE; } static BOOL RevokeUserAccessRights(LPCTSTR FilePath, LPCTSTR File, LPCTSTR User) { /* TODO & FIXME */ return FALSE; } static BOOL ChangeFileACL(LPCTSTR FilePath, LPCTSTR File) { if (OptionG) { /* Grant specified user access rights. */ GrantUserAccessRights(FilePath, File, GUser, *GPerm); } if (OptionP) { if (!OptionE) { /* Replace specified user's access rights. */ ReplaceUserAccessRights(FilePath, File, PUser, *PPerm); } else { /* Edit ACL instead of replacing it. */ EditUserAccessRights(FilePath, File, PUser, *PPerm); } } if (OptionD) { /* Deny specified user access. */ DenyUserAccess(FilePath, File, DUser); } if (OptionR) { /* Revoke specified user's access rights. */ RevokeUserAccessRights(FilePath, File, RUser); } return TRUE; } static BOOL ChangeACLsOfFiles(LPCTSTR pszFiles) { TCHAR FilePath[MAX_PATH]; HANDLE hFind; WIN32_FIND_DATA FindData; DWORD LastError; /* * get the file path */ if (!GetPathOfFile(FilePath, pszFiles)) return FALSE; /* * search for files in current directory */ hFind = FindFirstFile(pszFiles, &FindData); if (hFind == INVALID_HANDLE_VALUE) return FALSE; do { if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue; if (!ChangeFileACL(FilePath, FindData.cFileName)) { LastError = GetLastError(); if (LastError == ERROR_ACCESS_DENIED) { PrintError(LastError); if (!OptionC) { FindClose(hFind); return FALSE; } } else break; } } while(FindNextFile(hFind, &FindData)); LastError = GetLastError(); FindClose(hFind); if (LastError != ERROR_NO_MORE_FILES) { PrintError(LastError); return FALSE; } return TRUE; } static BOOL ChangeACLsOfFilesInCurDir(LPCTSTR pszFiles) { HANDLE hFind; WIN32_FIND_DATA FindData; TCHAR szCurDir[MAX_PATH]; DWORD LastError; /* * get the file path (current directory) */ GetCurrentDirectory(MAX_PATH, szCurDir); AddBackslash(szCurDir); /* * search for files in current directory */ hFind = FindFirstFile(pszFiles, &FindData); if (hFind == INVALID_HANDLE_VALUE) return FALSE; do { if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) continue; if (!ChangeFileACL(szCurDir, FindData.cFileName)) { LastError = GetLastError(); if (LastError == ERROR_ACCESS_DENIED) { PrintError(LastError); if (!OptionC) { FindClose(hFind); return FALSE; } } else break; } } while(FindNextFile(hFind, &FindData)); LastError = GetLastError(); FindClose(hFind); if (LastError != ERROR_NO_MORE_FILES) { PrintError(LastError); return FALSE; } /* * search for subdirectory in current directory */ hFind = FindFirstFile(_T("*"), &FindData); if (hFind == INVALID_HANDLE_VALUE) return FALSE; do { if (_tcscmp(FindData.cFileName, _T(".")) == 0 || _tcscmp(FindData.cFileName, _T("..")) == 0) continue; if (FindData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { GetCurrentDirectory(MAX_PATH, szCurDir); if (SetCurrentDirectory(FindData.cFileName)) { ChangeACLsOfFilesInCurDir(pszFiles); SetCurrentDirectory(szCurDir); } else { LastError = GetLastError(); if (LastError == ERROR_ACCESS_DENIED) { PrintError(LastError); if (!OptionC) { FindClose(hFind); return FALSE; } } else break; } } } while(FindNextFile(hFind, &FindData)); LastError = GetLastError(); FindClose(hFind); if (LastError != ERROR_NO_MORE_FILES) { PrintError(LastError); return FALSE; } return TRUE; } int _tmain(int argc, const TCHAR *argv[]) { INT i; LPTSTR pch; BOOL InvalidParameter = FALSE; /* Initialize the Console Standard Streams */ ConInitStdStreams(); if (argc <= 1) { ConResPuts(StdOut, IDS_HELP); return 0; } // FIXME: Convert to proper parsing, with support for /? /* * parse command line options */ for (i = 2; i < argc; i++) { if (lstrcmpi(argv[i], _T("/T")) == 0) { OptionT = TRUE; } else if (lstrcmpi(argv[i], _T("/E")) == 0) { OptionE = TRUE; } else if (lstrcmpi(argv[i], _T("/C")) == 0) { OptionC = TRUE; } else if (lstrcmpi(argv[i], _T("/G")) == 0) { if (i + 1 < argc) { pch = _tcschr(argv[++i], _T(':')); if (pch != NULL) { OptionG = TRUE; *pch = 0; GUser = argv[i]; GPerm = pch + 1; continue; } } InvalidParameter = TRUE; break; } else if (lstrcmpi(argv[i], _T("/R")) == 0) { if (i + 1 < argc) { RUser = argv[++i]; OptionR = TRUE; continue; } InvalidParameter = TRUE; break; } else if (lstrcmpi(argv[i], _T("/P")) == 0) { if (i + 1 < argc) { pch = _tcschr(argv[++i], _T(':')); if (pch != NULL) { OptionP = TRUE; *pch = 0; PUser = argv[i]; PPerm = pch + 1; continue; } } InvalidParameter = TRUE; break; } else if (lstrcmpi(argv[i], _T("/D")) == 0) { if (i + 1 < argc) { OptionD = TRUE; DUser = argv[++i]; continue; } InvalidParameter = TRUE; break; } else { InvalidParameter = TRUE; break; } } if (InvalidParameter) { PrintError(ERROR_INVALID_PARAMETER); ConResPuts(StdOut, IDS_HELP); return 1; } /* /R is only valid with /E */ if (OptionR && !OptionE) { OptionR = FALSE; } PrintDaclsOfFiles(argv[1]); if (OptionT) { ChangeACLsOfFilesInCurDir(argv[1]); } else { ChangeACLsOfFiles(argv[1]); } return 0; }