/*++ Copyright (c) 1994 Microsoft Corporation Module Name: aclfuncs.c Abstract: This module contians the functions to process the items from the files containing the ACL entries for registry keys and files Author: Bob Watson (a-robw) Dec-94 Revision History: --*/ #ifdef TEST_MODE #undef TEST_MODE #endif //#define TEST_MODE 1 #include #include #include #include #include #include "c2acls.h" #include "c2aclres.h" #include "strings.h" #define FILE_ENTRY 1 #define DIR_ENTRY 2 // internal flag #define FILE_PATH_SUBDIRS 8 #define NTFS_NOT_STANDARD 0x00000000 #define NTFS_FULL_CONTROL 0x00010001 #define NTFS_CHANGE 0x00010002 #define NTFS_ADD_READ 0x00010003 #define NTFS_READ 0x00010004 #define NTFS_ADD 0x00010005 #define NTFS_LIST 0x00010006 #define NTFS_NO_ACCESS 0x00010007 #define NTFS_NONE 0x00000008 #define NTFS_ALL 0x00000009 #define NTFS_FILE_ALL_ACCESS (FILE_ALL_ACCESS) #define NTFS_FILE_CHANGE (FILE_GENERIC_READ | FILE_GENERIC_WRITE | \ FILE_GENERIC_EXECUTE | DELETE) #define NTFS_FILE_ADD_READ (FILE_GENERIC_READ | FILE_GENERIC_WRITE | \ FILE_GENERIC_EXECUTE) #define NTFS_FILE_READ (FILE_GENERIC_READ | FILE_GENERIC_EXECUTE) #define NTFS_FILE_ADD (FILE_GENERIC_WRITE | FILE_GENERIC_EXECUTE) #define NTFS_FILE_LIST (FILE_GENERIC_READ | FILE_GENERIC_EXECUTE) #define NTFS_FILE_NO_ACCESS (FILE_ALL_ACCESS) /* access denied ace */ #define NTFS_DIR_ALL_ACCESS (GENERIC_ALL) #define NTFS_DIR_CHANGE (GENERIC_READ | GENERIC_WRITE | \ GENERIC_EXECUTE | DELETE) #define NTFS_DIR_ADD_READ (GENERIC_READ | GENERIC_EXECUTE) #define NTFS_DIR_READ (GENERIC_READ | GENERIC_EXECUTE) #define NTFS_DIR_ADD (GENERIC_WRITE | GENERIC_EXECUTE) #define NTFS_DIR_LIST (GENERIC_READ | GENERIC_EXECUTE) #define NTFS_DIR_NO_ACCESS (GENERIC_ALL) /* access denied ace */ #define NTFS_DIR_DIR_FLAGS (INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE) #define NTFS_DIR_FILE_FLAGS (CONTAINER_INHERIT_ACE) #define NTFS_FILE_FILE_FLAGS 0x00 typedef struct _ACCESS_DECODE { LPTSTR szString; DWORD dwMask; } ACCESS_DECODE, *PACCESS_DECODE; #ifdef TEST_MODE LONG DumpAclData ( IN LPCTSTR szFileName, IN PSECURITY_DESCRIPTOR psdFile ); #endif static ACCESS_DECODE adRegistryDecodeList[] = { // specific access {TEXT("QV"), (DWORD)KEY_QUERY_VALUE}, {TEXT("SV"), (DWORD)KEY_SET_VALUE}, {TEXT("CS"), (DWORD)KEY_CREATE_SUB_KEY}, {TEXT("ES"), (DWORD)KEY_ENUMERATE_SUB_KEYS}, {TEXT("NT"), (DWORD)KEY_NOTIFY}, {TEXT("CL"), (DWORD)KEY_CREATE_LINK}, // standard rights {TEXT("DE"), (DWORD)DELETE}, {TEXT("RC"), (DWORD)READ_CONTROL}, {TEXT("WD"), (DWORD)WRITE_DAC}, {TEXT("WO"), (DWORD)WRITE_OWNER}, // predefined groups {TEXT("NONE"), (DWORD)0}, {TEXT("FULL"), (DWORD)KEY_ALL_ACCESS}, {TEXT("READ"), (DWORD)KEY_READ}, // terminating entry {NULL, (DWORD)0} }; static ACCESS_DECODE adNtfsDecodeList[] = { // predefined "combination" access keywords {TEXT("FullControl"), (DWORD)NTFS_FULL_CONTROL}, {TEXT("Change"), (DWORD)NTFS_CHANGE}, {TEXT("AddRead"), (DWORD)NTFS_ADD_READ}, {TEXT("Read"), (DWORD)NTFS_READ}, {TEXT("Add"), (DWORD)NTFS_ADD}, {TEXT("List"), (DWORD)NTFS_LIST}, {TEXT("NoAccess"), (DWORD)NTFS_NO_ACCESS}, // predefined single ACE entries {TEXT("NONE"), (DWORD)NTFS_NONE}, {TEXT("ALL"), (DWORD)NTFS_ALL}, // terminating entry {NULL, (DWORD)0} }; static BOOL DotOrDotDotDir ( IN LPCTSTR szFileName ) /*++ Routine Description: examines the filename in the parameter list to see if it is one of the default subdirectories (e.g. the . or the .. dirs). This routine assumes that the argument is a filename only. (i.e. NO PATH!) Arguments: Filename to compare Return Value: TRUE if filename is either the . or the .. dir FALSE if it is any other string --*/ { if (szFileName != NULL) { // check for null parameter if (lstrcmp(szFileName, cszDot) == 0) { return TRUE; // it's the . dir } else if (lstrcmp(szFileName, cszDotDot) == 0) { return TRUE; // it's the .. dir } else { return FALSE; // it's neither } } else { return FALSE; // null filename, so not a . or .. dir } } static BOOL IsDirectory ( IN LPCTSTR szFilePath ) { DWORD dwAttrib; dwAttrib = QuietGetFileAttributes (szFilePath); if (dwAttrib != 0xFFFFFFFF) { return ((dwAttrib & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY); } else { return FALSE; } } static ACCESS_MASK ReadNtfsPermissionString ( IN LPCTSTR szPerms, IN BOOL bDirectory ) { LPCTSTR szThisChar; ACCESS_MASK amReturn = 0; for (szThisChar = szPerms; *szThisChar != 0; szThisChar++) { switch (*szThisChar) { case TEXT('R'): case TEXT('r'): amReturn |= (bDirectory ? GENERIC_READ : FILE_GENERIC_READ); break; case TEXT('W'): case TEXT('w'): amReturn |= (bDirectory ? GENERIC_WRITE : FILE_GENERIC_WRITE); break; case TEXT('X'): case TEXT('x'): amReturn |= (bDirectory ? GENERIC_EXECUTE : FILE_GENERIC_EXECUTE); break; case TEXT('D'): case TEXT('d'): amReturn |= DELETE; break; case TEXT('P'): case TEXT('p'): amReturn |= WRITE_DAC; break; case TEXT('O'): case TEXT('o'): amReturn |= WRITE_OWNER; break; default: // no change if unrecognized break; } } return amReturn; } HKEY GetRootKey ( IN LPCTSTR szKeyPath ) { TCHAR szLocalPath[MAX_PATH]; LPTSTR szEndOfRoot; lstrcpy (szLocalPath, szKeyPath); szEndOfRoot = _tcschr(szLocalPath, cBackslash); if (szEndOfRoot != NULL) { *szEndOfRoot = 0; // null terminate here and use just the chars to the left if (lstrcmpi(szLocalPath, cszLocalMachine) == 0) { return HKEY_LOCAL_MACHINE; } else if (lstrcmpi(szLocalPath, cszClassesRoot) == 0) { return HKEY_CLASSES_ROOT; } else if (lstrcmpi(szLocalPath, cszCurrentUser) == 0) { return HKEY_CURRENT_USER; } else if (lstrcmpi(szLocalPath, cszUsers) == 0) { return HKEY_USERS; } else { return NULL; } } else { return NULL; } } LPCTSTR GetKeyPath ( IN LPCTSTR szKeyPath, OUT LPBOOL pbDoSubKeys ) { static TCHAR szLocalPath[MAX_PATH]; LPTSTR szEndOfRoot; BOOL bReturn = FALSE; szEndOfRoot = _tcschr(szKeyPath, cBackslash); if (szEndOfRoot != NULL) { // path found szEndOfRoot++; // go to first char after '\' // save in local static buffer lstrcpy (szLocalPath, szEndOfRoot); // now see if this is a recursive key szEndOfRoot = _tcschr(szLocalPath, cAsterisk); if (szEndOfRoot != NULL) { // then this is a recursive path bReturn = TRUE; szEndOfRoot--; // go back to backslash *szEndOfRoot = 0; // and terminate there } if (pbDoSubKeys != NULL) { *pbDoSubKeys = bReturn; } return szLocalPath; } else { // unable to find correct syntax so just return entire string if (pbDoSubKeys != NULL) { *pbDoSubKeys = FALSE; } return szKeyPath; // or beginning of path after ROOT key } } LPCTSTR GetFilePathFromHeader ( IN LPCTSTR szHeaderPath, OUT LPDWORD pdwFlags ) { static TCHAR szLocalPath[MAX_PATH]; TCHAR szLocalWorkPath[MAX_PATH]; LPTSTR szEndOfPath; DWORD dwLastError = ERROR_SUCCESS; DWORD dwReturn = FILE_PATH_NORMAL; lstrcpy (szLocalWorkPath, szHeaderPath); szEndOfPath = _tcsrchr(szLocalWorkPath, cBang); if (szEndOfPath != NULL) { // wild card found so remember this and truncate szEndOfPath--; // go to first char before * *szEndOfPath = 0; // and terminate there dwReturn = FILE_PATH_ALL; // remember to do sub-dirs } else { // check for wildcard path syntax characters szEndOfPath = _tcschr (szLocalWorkPath, cAsterisk); if (szEndOfPath == NULL) { // check for the other wild card char szEndOfPath = _tcschr (szLocalWorkPath, cAsterisk); } if (szEndOfPath != NULL) { // keep the path intact but set the flag dwReturn = FILE_PATH_WILD; } // no wildcard, so assume the path is in the valid syntax } dwLastError = GetExpandedFileName( szLocalWorkPath, sizeof(szLocalPath)/sizeof(TCHAR), szLocalPath, NULL); if (dwLastError != ERROR_SUCCESS) { SetLastError (dwLastError); // record error szLocalPath[0] = 0; // and clear buffer } if (pdwFlags != NULL) { *pdwFlags = dwReturn; // update the sub dir flag } return (LPCTSTR)&szLocalPath[0]; } static DWORD DecodeRegAccessEntry ( IN LPCTSTR szAccessEntry ) { PACCESS_DECODE pEnt; for (pEnt = &adRegistryDecodeList[0]; pEnt->szString != NULL; pEnt++) { if (lstrcmpi (szAccessEntry, pEnt->szString) == 0) { return pEnt->dwMask; } } return 0; // no entry found } static DWORD DecodeNtfsAccessEntry ( IN LPCTSTR szAccessEntry ) { PACCESS_DECODE pEnt; for (pEnt = &adNtfsDecodeList[0]; pEnt->szString != NULL; pEnt++) { if (lstrcmpi (szAccessEntry, pEnt->szString) == 0) { return pEnt->dwMask; } } return NTFS_NOT_STANDARD; // no entry found } static BOOL FillAce ( IN PACCESS_ALLOWED_ACE paaAce, IN ACCESS_MASK amAccess, IN BYTE Type, IN BYTE Flags, IN PSID pSid ) { paaAce->Header.AceType = Type; paaAce->Header.AceFlags = Flags; paaAce->Mask = amAccess; CopySid ( (DWORD)(paaAce->Header.AceSize - (sizeof (ACCESS_ALLOWED_ACE) - sizeof(DWORD))), (PSID)(&paaAce->SidStart), pSid); paaAce->Header.AceSize = (WORD)(GetLengthSid(pSid) + (sizeof (ACCESS_ALLOWED_ACE) - sizeof(DWORD))); return TRUE; } static BOOL MakeAccessDeniedAce ( IN PACCESS_DENIED_ACE padAce, IN ACCESS_MASK amAccess, IN BYTE Flags, IN PSID pSid ) { padAce->Header.AceType = ACCESS_DENIED_ACE_TYPE; padAce->Header.AceFlags = Flags; padAce->Mask = amAccess; CopySid ( (DWORD)(padAce->Header.AceSize - (sizeof (ACCESS_ALLOWED_ACE) - sizeof(DWORD))), (PSID)(&padAce->SidStart), pSid); padAce->Header.AceSize = (WORD)(GetLengthSid(pSid) + (sizeof (ACCESS_ALLOWED_ACE) - sizeof(DWORD))); return TRUE; } static LONG MakeAceFromRegEntry ( IN LPCTSTR szAccessList, IN PSID psidUser, IN OUT PACCESS_ALLOWED_ACE pAce ) /*++ Routine Description: interprets the access string list and returns the corresponding access mask Arguments: list of access strings to include: --*/ { DWORD dwIndex = 1; LPCTSTR szItem; // see if this is an "inherit" ACE or not if (lstrcmpi ((LPTSTR)GetItemFromIniEntry(szAccessList, dwIndex), cszInherit) == 0) { pAce->Header.AceFlags = CONTAINER_INHERIT_ACE | INHERIT_ONLY_ACE; dwIndex++; // go to next entry } else { pAce->Header.AceFlags = 0; } // walk down the list to set bits as they are defined. szItem = GetItemFromIniEntry (szAccessList, dwIndex); while (lstrlen(szItem) > 0) { pAce->Mask |= DecodeRegAccessEntry(szItem); dwIndex++; szItem = (LPTSTR)GetItemFromIniEntry (szAccessList, dwIndex); } // now to add the sid and set the ace size CopySid ( (DWORD)(pAce->Header.AceSize - sizeof(ACE_HEADER) - sizeof (ACCESS_MASK)), (PSID)(&pAce->SidStart), psidUser); pAce->Header.AceSize = (WORD)GetLengthSid(psidUser) + sizeof(ACE_HEADER) + sizeof(ACCESS_MASK); return ERROR_SUCCESS; } LONG MakeAclFromRegSection ( IN LPTSTR mszSection, OUT PACL pAcl ) /*++ Routine Description: interprets the section string as a list of ACE information in the form of: DOMAIN\ACCOUNT = Access information and adds the ACE information to the initialized ACL passed in Arguments: mszSection msz Buffer of ACE information pAcl pointer to intialized ACL buffer Return Value: WIN32 Error status of function --*/ { LPTSTR szThisEntry; TCHAR szDomain[MAX_PATH]; DWORD dwSidBuffSize; DWORD dwDomainSize; SID_NAME_USE snu; PSID psidAccount = NULL; PACCESS_ALLOWED_ACE pAce = NULL; LONG lStatus; for (szThisEntry = mszSection; *szThisEntry != 0; szThisEntry += lstrlen(szThisEntry)+1) { psidAccount = GLOBAL_ALLOC (SMALL_BUFFER_SIZE); pAce = GLOBAL_ALLOC (MAX_PATH); // this should be plenty big if ((psidAccount != NULL) && (pAce != NULL)){ dwSidBuffSize = SMALL_BUFFER_SIZE; dwDomainSize = sizeof(szDomain) / sizeof(TCHAR); if (LookupAccountName ( NULL, // look up on local machine GetKeyFromIniEntry(szThisEntry), psidAccount, &dwSidBuffSize, szDomain, &dwDomainSize, &snu)) { // account found so make an ACE with it pAce->Header.AceType = ACCESS_ALLOWED_ACE_TYPE; pAce->Header.AceFlags = 0; pAce->Header.AceSize = MAX_PATH; // initial buffer size pAce->Mask = 0; // the mask will be added in the next fn. pAce->SidStart = 0; // the sid will be added in the next fn. lStatus = MakeAceFromRegEntry ( szThisEntry, psidAccount, pAce); AddAce ( pAcl, ACL_REVISION, MAXDWORD, // append this ACE to the end of the list (LPVOID)pAce, (DWORD)pAce->Header.AceSize); } else { // unable to look up name } } else { // unable to allocate SID buffer } GLOBAL_FREE_IF_ALLOC (psidAccount); GLOBAL_FREE_IF_ALLOC (pAce); } // end of entry loop return ERROR_SUCCESS; } static BOOL MakeDirAce ( IN PACCESS_ALLOWED_ACE pAce, IN PSID pSid, IN DWORD dwAccessValue ) { BOOL bAddAce = TRUE; ACCESS_MASK amAce = 0; BYTE bType = ACCESS_ALLOWED_ACE_TYPE; switch (dwAccessValue) { case NTFS_FULL_CONTROL: amAce = NTFS_DIR_ALL_ACCESS; break; case NTFS_CHANGE: amAce = NTFS_DIR_CHANGE; break; case NTFS_ADD_READ: amAce = NTFS_DIR_ADD_READ; break; case NTFS_READ: amAce = NTFS_DIR_READ; break; case NTFS_ADD: bAddAce = FALSE; break; case NTFS_LIST: bAddAce = FALSE; break; case NTFS_NO_ACCESS: amAce = NTFS_DIR_NO_ACCESS; bType = ACCESS_DENIED_ACE_TYPE; break; default: bAddAce = FALSE; break; } if (bAddAce) { bAddAce = FillAce ( pAce, amAce, bType, NTFS_DIR_DIR_FLAGS, pSid); } return bAddAce; } static BOOL MakeFileAce ( IN PACCESS_ALLOWED_ACE pAce, IN PSID pSid, IN DWORD dwAccessValue, IN BOOL bDirectory ) { BOOL bAddAce = TRUE; ACCESS_MASK amAce = 0; BYTE bType = ACCESS_ALLOWED_ACE_TYPE; switch (dwAccessValue) { case NTFS_FULL_CONTROL: amAce = NTFS_FILE_ALL_ACCESS; break; case NTFS_CHANGE: amAce = NTFS_FILE_CHANGE; break; case NTFS_ADD_READ: if (bDirectory) { amAce = NTFS_FILE_ADD_READ; } else { bAddAce = FALSE; // not allowed for files } break; case NTFS_READ: amAce = NTFS_FILE_READ; break; case NTFS_ADD: amAce = NTFS_FILE_ADD; break; case NTFS_LIST: amAce = NTFS_FILE_LIST; break; case NTFS_NO_ACCESS: amAce = NTFS_FILE_NO_ACCESS; bType = ACCESS_DENIED_ACE_TYPE; break; default: bAddAce = FALSE; break; } if (bAddAce) { bAddAce = FillAce ( pAce, amAce, bType, (BYTE)(bDirectory ? NTFS_DIR_FILE_FLAGS : NTFS_FILE_FILE_FLAGS), pSid); } return bAddAce; } static LONG InterpretNtfsAccessEntry ( IN LPCTSTR szThisEntry, IN BOOL bDirectory, IN PSID psidAccount, OUT PACL pAcl ) { LONG lStatus = ERROR_SUCCESS; LPCTSTR szAccessString; DWORD dwAccessValue; BOOL bCombinationEntry; // true if access string makes 2 ace's PACCESS_ALLOWED_ACE pAce; ACCESS_MASK amAce; BYTE bAceType; BYTE bAceFlags; DWORD dwAllowedEntries; DWORD dwThisEntry = 1; szAccessString = GetItemFromIniEntry (szThisEntry, dwThisEntry); dwAccessValue = DecodeNtfsAccessEntry (szAccessString); bCombinationEntry = (BOOL)(HIWORD(dwAccessValue) == 1); if (bCombinationEntry) { // this is a combination entry so if this is a directory // we'll make 2 ACE's one for dir access and one to be inherited // for file access. If this is a file, then only the file access // ace will be created. if (bDirectory) { // make the directory access ACE for the directory entry pAce = (PACCESS_ALLOWED_ACE)GLOBAL_ALLOC(MAX_PATH); if (pAce != NULL) { if (MakeDirAce (pAce, psidAccount, dwAccessValue)) { // then add ace to acl AddAce ( pAcl, ACL_REVISION, MAXDWORD, (LPVOID)pAce, (DWORD)pAce->Header.AceSize); } } GLOBAL_FREE_IF_ALLOC (pAce); // free this one } // create the File Access entry pAce = (PACCESS_ALLOWED_ACE)GLOBAL_ALLOC(MAX_PATH); if (pAce != NULL) { if (MakeFileAce (pAce, psidAccount, dwAccessValue, bDirectory)) { // then add ace to acl AddAce ( pAcl, ACL_REVISION, MAXDWORD, (LPVOID)pAce, (DWORD)pAce->Header.AceSize); } } GLOBAL_FREE_IF_ALLOC (pAce); // free this one } else { // specific permissions have been specified so process them // this is the first entry in the list so it's either the "dir" // entry of a directory or a File entry. dwAllowedEntries = (bDirectory ? 2 : 1); while (dwThisEntry <= dwAllowedEntries) { if (lstrlen(szAccessString) > 0) { bAceFlags = (bDirectory ? (dwThisEntry == DIR_ENTRY ? NTFS_DIR_DIR_FLAGS : NTFS_DIR_FILE_FLAGS) : NTFS_FILE_FILE_FLAGS); bAceType = ACCESS_ALLOWED_ACE_TYPE; switch (dwAccessValue) { case NTFS_NONE: amAce = (bDirectory ? NTFS_DIR_NO_ACCESS : NTFS_FILE_NO_ACCESS); bAceType = ACCESS_DENIED_ACE_TYPE; break; case NTFS_ALL: amAce = (bDirectory ? NTFS_DIR_NO_ACCESS : NTFS_FILE_ALL_ACCESS); break; default: amAce = ReadNtfsPermissionString (szAccessString, bDirectory); break; } pAce = (PACCESS_ALLOWED_ACE)GLOBAL_ALLOC(MAX_PATH); if (pAce != NULL) { FillAce ( pAce, amAce, bAceType, bAceFlags, psidAccount); AddAce ( pAcl, ACL_REVISION, MAXDWORD, (LPVOID)pAce, (DWORD)pAce->Header.AceSize); } GLOBAL_FREE_IF_ALLOC (pAce); } if (++dwThisEntry <= dwAllowedEntries) { // get next entry from string szAccessString = GetItemFromIniEntry (szThisEntry, dwThisEntry); } } } return lStatus; } LONG MakeAclFromNtfsSection ( IN LPTSTR mszSection, IN BOOL bDirectory, // true if this is for a directory OUT PACL pAcl ) /*++ Routine Description: interprets the section string as a list of ACE information in the form of: DOMAIN\ACCOUNT = Access information and adds the ACE information to the initialized ACL passed in Arguments: mszSection msz Buffer of ACE information pAcl pointer to intialized ACL buffer Return Value: WIN32 Error status of function --*/ { LPTSTR szThisEntry; TCHAR szDomain[MAX_PATH]; DWORD dwSidBuffSize; DWORD dwDomainSize; SID_NAME_USE snu; PSID psidAccount = NULL; LONG lStatus; for (szThisEntry = mszSection; *szThisEntry != 0; szThisEntry += lstrlen(szThisEntry)+1) { psidAccount = GLOBAL_ALLOC (SMALL_BUFFER_SIZE); if (psidAccount != NULL) { dwSidBuffSize = SMALL_BUFFER_SIZE; dwDomainSize = sizeof(szDomain) / sizeof(TCHAR); if (LookupAccountName ( NULL, // look up on local machine GetKeyFromIniEntry(szThisEntry), psidAccount, &dwSidBuffSize, szDomain, &dwDomainSize, &snu)) { lStatus = InterpretNtfsAccessEntry ( szThisEntry, bDirectory, psidAccount, pAcl); } else { // unable to look up name } } else { // unable to allocate SID buffer } GLOBAL_FREE_IF_ALLOC (psidAccount); } // end of entry loop return ERROR_SUCCESS; } LONG SetRegistryKeySecurity ( IN HKEY hkeyRootKey, IN LPCTSTR szKeyPath, IN BOOL bDoSubKeys, IN PSECURITY_DESCRIPTOR psdSecurity ) /*++ Routine Description: opens the key in "KeyPath" and set's the key's security. If the "bDoSubKeys" flag is set, then all sub keys are set to the same security using this routine recursively. Return Value: WIN32 status value of function --*/ { LONG lStatus; HKEY hkeyThisKey; DWORD dwKeyIndex; DWORD dwSubKeyLen; TCHAR szSubKeyName[MAX_PATH]; FILETIME FileTime; lStatus = RegOpenKeyEx ( hkeyRootKey, szKeyPath, 0L, KEY_ALL_ACCESS, &hkeyThisKey); if (lStatus == ERROR_SUCCESS) { _tprintf (GetStringResource (GetDllInstance(), IDS_REG_DISPLAY_FORMAT), szKeyPath); lStatus = RegSetKeySecurity ( hkeyThisKey, DACL_SECURITY_INFORMATION, psdSecurity); if (bDoSubKeys) { dwKeyIndex = 0; dwSubKeyLen = sizeof(szSubKeyName) / sizeof(TCHAR); while (RegEnumKeyEx ( hkeyThisKey, dwKeyIndex, szSubKeyName, &dwSubKeyLen, NULL, NULL, // don't care about the class NULL, // no class buffer &FileTime) == ERROR_SUCCESS) { // subkey found so set subkey security lStatus = SetRegistryKeySecurity ( hkeyThisKey, szSubKeyName, bDoSubKeys, psdSecurity); // set variables for next call dwKeyIndex++; dwSubKeyLen = sizeof(szSubKeyName) / sizeof(TCHAR); } } RegCloseKey (hkeyThisKey); } else { // unable to open key so return ERROR } return lStatus; } LONG SetNtfsFileSecurity ( IN LPCTSTR szPath, IN DWORD dwFlags, IN PSECURITY_DESCRIPTOR pSdDir, IN PSECURITY_DESCRIPTOR pSdFile ) { LONG lReturn = ERROR_SUCCESS; LONG lStatus = ERROR_SUCCESS; WIN32_FIND_DATA fdThisFile; HANDLE hFileSearch; BOOL bFileFound = FALSE; DWORD dwFlagsToPass; LPTSTR szWildPath; TCHAR szWildFilePath[MAX_PATH]; LPTSTR szWildFileName; if ((dwFlags & FILE_PATH_NORMAL) == FILE_PATH_NORMAL) { // then just set the security on this path if (FileExists(szPath)) { if (IsDirectory(szPath)) { // then apply directory SD #ifdef TEST_MODE _tprintf (TEXT("\n_DIR: %s"), szPath); DumpAclData (szPath, pSdDir); lReturn = ERROR_SUCCESS; #else if (!SetFileSecurity(szPath, DACL_SECURITY_INFORMATION, pSdDir)) { lReturn = GetLastError(); } #endif } else { // it's not a dir so apply file SD #ifdef TEST_MODE _tprintf (TEXT("\nFILE: %s"), szPath); DumpAclData (szPath, pSdFile); lReturn = ERROR_SUCCESS; #else if (!SetFileSecurity(szPath, DACL_SECURITY_INFORMATION, pSdFile)) { lReturn = GetLastError(); } #endif } } else { #ifdef TEST_MODE _tprintf (TEXT("\n_ERR: %s"), szPath); #endif lReturn = ERROR_FILE_NOT_FOUND; } } else if ((dwFlags & FILE_PATH_WILD) == FILE_PATH_WILD) { // the path is (presumably) a wild-card path spec so walk the list // of matching files // make a local copy of the path preceeding the wildcard chars // to build local paths with lstrcpy (szWildFilePath, szPath); szWildFileName = _tcsrchr(szWildFilePath, cBackslash); // find last backslash char if (szWildFileName == NULL) { // no backslash found so add one to the end of the path string lstrcat (szWildFilePath, cszBackslash); szWildFileName = szWildFilePath + lstrlen(szWildFilePath); } else { // backslash found so move pointer past it. szWildFileName++; } hFileSearch = FindFirstFile (szPath, &fdThisFile); if (hFileSearch != INVALID_HANDLE_VALUE) bFileFound = TRUE; while (bFileFound) { if (fdThisFile.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) { if (!DotOrDotDotDir (fdThisFile.cFileName)) { // then if this is dir, do see if we should do all the sub dirs if ((dwFlags & FILE_PATH_SUBDIRS) == FILE_PATH_SUBDIRS) { dwFlagsToPass = FILE_PATH_ALL; } else { dwFlagsToPass = 0; // do not do in this case } } else { // this is a . or a .. dir so ignore dwFlagsToPass = 0; } } else { // just a file so process as a normal file dwFlagsToPass = FILE_PATH_NORMAL; } if (dwFlagsToPass != 0) { // make into a full path string lstrcpy (szWildFileName, fdThisFile.cFileName); // set the file security of this and any other files lStatus = SetNtfsFileSecurity (szWildFilePath, dwFlagsToPass, pSdDir, pSdFile); if (lStatus != ERROR_SUCCESS) { // save last "non-success" error for return lReturn = lStatus; } } bFileFound = FindNextFile (hFileSearch, &fdThisFile); } FindClose (hFileSearch); } else if ((dwFlags & FILE_PATH_ALL) == FILE_PATH_ALL) { // set the security of this path lStatus = SetNtfsFileSecurity (szPath, FILE_PATH_NORMAL, pSdDir, pSdFile); if (IsDirectory(szPath)) { // make a wild card path an pass it do all the files and sub-dirs // of this path szWildPath = (LPTSTR)GLOBAL_ALLOC(MAX_PATH_BYTES); if (szWildPath != NULL) { lstrcpy (szWildPath, szPath); if (szWildPath[lstrlen(szWildPath)-1] != cBackslash) { lstrcat (szWildPath, cszBackslash); } lstrcat (szWildPath, cszStarDotStar); // make wild // call this routine with both WILD and ALL flags set // so all files and sub-dirs will get set. lStatus = SetNtfsFileSecurity (szWildPath, (FILE_PATH_WILD | FILE_PATH_SUBDIRS), pSdDir, pSdFile); lReturn = lStatus; } else { lReturn = ERROR_OUTOFMEMORY; } GLOBAL_FREE_IF_ALLOC (szWildPath); } else { // path not a dir so just skip the wildcard stuff } } else { // unrecognized flag lReturn = ERROR_INVALID_PARAMETER; } return lReturn; }