///////////////////////////////////////////////////////////////////////////// // // Copyright (c) 1996-2002 Microsoft Corporation // // Module Name: // EditAcl.cpp // // Abstract: // Implementation of ACL editor methods. // // Author: // David Potter (davidp) October 9, 1996 // From \nt\private\window\shell\lmui\ntshrui\acl.cxx // by BruceFo // // Revision History: // // Notes: // ///////////////////////////////////////////////////////////////////////////// #include "stdafx.h" #include extern "C" { #include } #include "EditAcl.h" #include "AclHelp.h" #include "resource.h" #ifdef _DEBUG #define new DEBUG_NEW #undef THIS_FILE static char THIS_FILE[] = __FILE__; #endif ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// #define ARRAYLEN(a) (sizeof(a) / sizeof((a)[0])) enum SED_PERM_TYPE{ SED_AUDITS, SED_ACCESSES, SED_OWNER }; enum MAP_DIRECTION { SPECIFIC_TO_GENERIC = 0, GENERIC_TO_SPECIFIC = 1 }; const DWORD LOCAL_ACCOUNTS_FILTERED = 2L; const BOOL bIsFile = 0; //#define MAPBITS BOOL MapBitsInSD(PSECURITY_DESCRIPTOR pSecDesc, MAP_DIRECTION direction); BOOL MapBitsInACL(PACL paclACL, MAP_DIRECTION direction); BOOL MapSpecificBitsInAce(PACCESS_ALLOWED_ACE pAce); BOOL MapGenericBitsInAce(PACCESS_ALLOWED_ACE pAce); typedef DWORD (*SedDiscretionaryAclEditorType)( HWND Owner, HANDLE Instance, LPWSTR Server, PSED_OBJECT_TYPE_DESCRIPTOR ObjectType, PSED_APPLICATION_ACCESSES ApplicationAccesses, LPWSTR ObjectName, PSED_FUNC_APPLY_SEC_CALLBACK ApplySecurityCallbackRoutine, ULONG CallbackContext, PSECURITY_DESCRIPTOR SecurityDescriptor, BOOLEAN CouldntReadDacl, BOOLEAN CantWriteDacl, LPDWORD SEDStatusReturn, DWORD Flags ); // NOTE: the SedDiscretionaryAclEditor string is used in GetProcAddress to // get the correct entrypoint. Since GetProcAddress is not UNICODE, this string // must be ANSI. #define ACLEDIT_DLL_STRING TEXT("acledit.dll") #define ACLEDIT_HELPFILENAME TEXT("ntshrui.hlp") #define SEDDISCRETIONARYACLEDITOR_STRING ("SedDiscretionaryAclEditor") // // Declare the callback routine based on typedef in sedapi.h. // DWORD SedCallback( HWND hwndParent, HANDLE hInstance, ULONG ulCallbackContext, PSECURITY_DESCRIPTOR pSecDesc, PSECURITY_DESCRIPTOR pSecDescNewObjects, BOOLEAN fApplyToSubContainers, BOOLEAN fApplyToSubObjects, LPDWORD StatusReturn ); // // Structure for callback function's usage. A pointer to this is passed as // ulCallbackContext. The callback functions sets bSecDescModified to TRUE // and makes a copy of the security descriptor. The caller of EditShareAcl // is responsible for deleting the memory in pSecDesc if bSecDescModified is // TRUE. This flag will be FALSE if the user hit CANCEL in the ACL editor. // struct SHARE_CALLBACK_INFO { BOOL bSecDescModified; PSECURITY_DESCRIPTOR pSecDesc; LPCTSTR pszClusterNameNode; }; // // Local function prototypes // VOID InitializeShareGenericMapping( IN OUT PGENERIC_MAPPING pSHAREGenericMapping ); PWSTR GetResourceString( IN DWORD dwId ); PWSTR NewDup( IN const WCHAR* psz ); // // The following two arrays define the permission names for NT Files. Note // that each index in one array corresponds to the index in the other array. // The second array will be modifed to contain a string pointer pointing to // a loaded string corresponding to the IDS_* in the first array. // DWORD g_dwSharePermNames[] = { IDS_ACLEDIT_PERM_GEN_NO_ACCESS, IDS_ACLEDIT_PERM_GEN_READ, IDS_ACLEDIT_PERM_GEN_MODIFY, IDS_ACLEDIT_PERM_GEN_ALL }; SED_APPLICATION_ACCESS g_SedAppAccessSharePerms[] = { { SED_DESC_TYPE_RESOURCE, FILE_PERM_NO_ACCESS, 0, NULL }, { SED_DESC_TYPE_RESOURCE, FILE_PERM_READ, 0, NULL }, { SED_DESC_TYPE_RESOURCE, FILE_PERM_MODIFY, 0, NULL }, { SED_DESC_TYPE_RESOURCE, FILE_PERM_ALL, 0, NULL } /* { SED_DESC_TYPE_RESOURCE, FILE_PERM_GEN_NO_ACCESS, 0, NULL }, { SED_DESC_TYPE_RESOURCE, FILE_PERM_GEN_READ, 0, NULL }, { SED_DESC_TYPE_RESOURCE, FILE_PERM_GEN_MODIFY, 0, NULL }, { SED_DESC_TYPE_RESOURCE, FILE_PERM_GEN_ALL, 0, NULL } */ }; ////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////// //+------------------------------------------------------------------------- // // Function: EditShareAcl // // Synopsis: Invokes the generic ACL editor, specifically for NT shares // // Arguments: [hwndParent] - Parent window handle // [pszServerName] - Name of server on which the object resides. // [pszShareName] - Fully qualified name of resource we will // edit, basically a share name. // [pSecDesc] - The initial security descriptor. If NULL, we will // create a default that is "World all" access. // [pbSecDescModified] - Set to TRUE if the security descriptor // was modified (i.e., the user hit "OK"), or FALSE if not // (i.e., the user hit "Cancel") // [ppSecDesc] - *ppSecDesc points to a new security descriptor // if *pbSecDescModified is TRUE. This memory must be freed // by the caller. // // History: // ChuckC 10-Aug-1992 Created. Culled from NTFS ACL code. // Yi-HsinS 09-Oct-1992 Added ulHelpContextBase // BruceFo 4-Apr-95 Stole and used in ntshrui.dll // DavidP 10-Oct-1996 Modified for use with CLUADMIN // //-------------------------------------------------------------------------- LONG EditShareAcl( IN HWND hwndParent, IN LPCTSTR pszServerName, IN LPCTSTR pszShareName, IN LPCTSTR pszClusterNameNode, IN PSECURITY_DESCRIPTOR pSecDesc, OUT BOOL * pbSecDescModified, OUT PSECURITY_DESCRIPTOR * ppSecDesc ) { ASSERT(pszShareName != NULL); ASSERT(pszClusterNameNode != NULL); TRACE(_T("EditShareAcl, share %ws\n"), pszShareName); ASSERT((pSecDesc == NULL) || IsValidSecurityDescriptor(pSecDesc)); ASSERT(pbSecDescModified != NULL); ASSERT(ppSecDesc != NULL); *pbSecDescModified = FALSE; LONG err = 0 ; PWSTR pszPermName; BOOL bCreatedDefaultSecDesc = FALSE; UINT idx; do // error breakout { /* * if pSecDesc is NULL, this is new file share or a file share with no * security descriptor. * we go and create a new (default) security descriptor. */ if ( pSecDesc == NULL ) { TRACE(_T("Security Descriptor is NULL. Grant everyone Full Control\n") ); LONG err = CreateDefaultSecDesc( &pSecDesc ); if (err != NERR_Success) { err = GetLastError(); TRACE(_T("CreateDefaultSecDesc failed, 0x%08lx\n"), err); break; } TRACE(_T("CreateDefaultSecDesc descriptor = 0x%08lx\n"), pSecDesc); bCreatedDefaultSecDesc = TRUE; } ASSERT(IsValidSecurityDescriptor(pSecDesc)); /* Retrieve the resource strings appropriate for the type of object we * are looking at */ CString strTypeName; CString strDefaultPermName; try { strTypeName.LoadString(IDS_ACLEDIT_TITLE); strDefaultPermName.LoadString(IDS_ACLEDIT_PERM_GEN_ALL); } // try catch (CMemoryException * pme) { pme->Delete(); } /* * other misc stuff we need pass to security editor */ SED_OBJECT_TYPE_DESCRIPTOR sedObjDesc ; SED_HELP_INFO sedHelpInfo ; GENERIC_MAPPING SHAREGenericMapping ; // setup mappings InitializeShareGenericMapping( &SHAREGenericMapping ) ; WCHAR szHelpFile[50] = ACLEDIT_HELPFILENAME; sedHelpInfo.pszHelpFileName = szHelpFile; sedHelpInfo.aulHelpContext[HC_MAIN_DLG] = HC_UI_SHELL_BASE + HC_NTSHAREPERMS ; sedHelpInfo.aulHelpContext[HC_ADD_USER_DLG] = HC_UI_SHELL_BASE + HC_SHAREADDUSER ; sedHelpInfo.aulHelpContext[HC_ADD_USER_MEMBERS_GG_DLG] = HC_UI_SHELL_BASE + HC_SHAREADDUSER_GLOBALGROUP ; sedHelpInfo.aulHelpContext[HC_ADD_USER_SEARCH_DLG] = HC_UI_SHELL_BASE + HC_SHAREADDUSER_FINDUSER ; // These are not used, set to zero sedHelpInfo.aulHelpContext[HC_SPECIAL_ACCESS_DLG] = 0 ; sedHelpInfo.aulHelpContext[HC_NEW_ITEM_SPECIAL_ACCESS_DLG] = 0 ; // setup the object description sedObjDesc.Revision = SED_REVISION1 ; sedObjDesc.IsContainer = FALSE ; sedObjDesc.AllowNewObjectPerms = FALSE ; sedObjDesc.MapSpecificPermsToGeneric = TRUE ; sedObjDesc.GenericMapping = &SHAREGenericMapping ; sedObjDesc.GenericMappingNewObjects = &SHAREGenericMapping ; sedObjDesc.ObjectTypeName = (LPWSTR) (LPCWSTR) strTypeName ; sedObjDesc.HelpInfo = &sedHelpInfo ; sedObjDesc.SpecialObjectAccessTitle = NULL ; /* Now we need to load the global arrays with the permission names * from the resource file. */ UINT cArrayItems = ARRAYLEN(g_SedAppAccessSharePerms); PSED_APPLICATION_ACCESS aSedAppAccess = g_SedAppAccessSharePerms ; /* Loop through each permission title retrieving the text from the * resource file and setting the pointer in the array. */ for ( idx = 0 ; idx < cArrayItems ; idx++ ) { pszPermName = GetResourceString(g_dwSharePermNames[ idx ]) ; if ( pszPermName == NULL ) { TRACE(_T("GetResourceString failed\n")); break ; } aSedAppAccess[ idx ].PermissionTitle = pszPermName; } if ( idx < cArrayItems ) { TRACE(_T("failed to get all share permission names\n")); break ; } SED_APPLICATION_ACCESSES sedAppAccesses ; sedAppAccesses.Count = cArrayItems ; sedAppAccesses.AccessGroup = aSedAppAccess ; sedAppAccesses.DefaultPermName = (LPWSTR) (LPCWSTR) strDefaultPermName; /* * pass this along so when the call back function is called, * we can set it. */ SHARE_CALLBACK_INFO callbackinfo ; callbackinfo.pSecDesc = NULL; callbackinfo.bSecDescModified = FALSE; callbackinfo.pszClusterNameNode = pszClusterNameNode; // // Now, load up the ACL editor and invoke it. We don't keep it around // because our DLL is loaded whenever the system is, so we don't want // the netui*.dll's hanging around as well... // HINSTANCE hInstanceAclEditor = NULL; SedDiscretionaryAclEditorType pAclEditor = NULL; hInstanceAclEditor = LoadLibrary(ACLEDIT_DLL_STRING); if ( hInstanceAclEditor == NULL ) { err = GetLastError(); TRACE(_T("LoadLibrary of acledit.dll failed, 0x%08lx\n"), err); break; } pAclEditor = (SedDiscretionaryAclEditorType) GetProcAddress( hInstanceAclEditor, SEDDISCRETIONARYACLEDITOR_STRING ); if ( pAclEditor == NULL ) { err = GetLastError(); TRACE(_T("GetProcAddress of SedDiscretionaryAclEditorType failed, 0x%08lx\n"), err); break; } #ifdef MAPBITS MapBitsInSD( pSecDesc, SPECIFIC_TO_GENERIC ); #endif DWORD dwSedReturnStatus ; ASSERT(pAclEditor != NULL); err = (*pAclEditor)( hwndParent, AfxGetInstanceHandle(), (LPTSTR) pszServerName, &sedObjDesc, &sedAppAccesses, (LPTSTR) pszShareName, SedCallback, (ULONG) &callbackinfo, pSecDesc, FALSE, // always can read FALSE, // if we can read, we can write (LPDWORD) &dwSedReturnStatus, 0 ); if (pSecDesc != NULL) { #ifdef MAPBITS MapBitsInSD( pSecDesc, GENERIC_TO_SPECIFIC ); #endif ASSERT(IsValidSecurityDescriptor(pSecDesc)); } // if: no security descriptor returned if (!FreeLibrary(hInstanceAclEditor)) { LONG err2 = GetLastError(); TRACE(_T("FreeLibrary of acledit.dll failed, 0x%08lx\n"), err2); // not fatal: continue... } if (0 != err) { TRACE(_T("SedDiscretionaryAclEditor failed, 0x%08lx\n"), err); break ; } *pbSecDescModified = callbackinfo.bSecDescModified ; if (*pbSecDescModified) { *ppSecDesc = callbackinfo.pSecDesc; #ifdef MAPBITS MapBitsInSD( *ppSecDesc, GENERIC_TO_SPECIFIC ); #endif TRACE(_T("After calling acl editor, *ppSecDesc = 0x%08lx\n"), *ppSecDesc); ASSERT(IsValidSecurityDescriptor(*ppSecDesc)); } } while (FALSE) ; // // Free memory... // UINT cArrayItems = ARRAYLEN(g_SedAppAccessSharePerms); PSED_APPLICATION_ACCESS aSedAppAccess = g_SedAppAccessSharePerms ; for ( UINT i = 0 ; i < cArrayItems ; i++ ) { pszPermName = aSedAppAccess[i].PermissionTitle; if ( pszPermName == NULL ) { // if we hit a NULL, that's it! break ; } delete[] pszPermName; } if (bCreatedDefaultSecDesc) { DeleteDefaultSecDesc(pSecDesc); } ASSERT(!*pbSecDescModified || IsValidSecurityDescriptor(*ppSecDesc)); if (0 != err) { CString strCaption; CString strMsg; try { strCaption.LoadString(IDS_MSGTITLE); strMsg.LoadString(IDS_NOACLEDITOR); MessageBox(hwndParent, strMsg, strCaption, MB_OK | MB_ICONSTOP); } // try catch (CException * pe) { pe->Delete(); }; } return err; } //*** EditShareAcl ////////////////////////////////////////////////////////////////////////////// //++ // // BLocalAccountInSD // // Description: // Determines if any ACEs for local accounts are in DACL stored in // Security Descriptor (pSD) after the ACL editor has been called // // Added this function in order to prevent users from selecting local // accounts in permissions dialog. // Rod Sharper 04/29/97 // // Arguments: // pSD - Security Descriptor to be checked. // pszClusterNameNode - // // Return Values: // TRUE if at least one ACE was removed from the DACL, False otherwise. // //-- ////////////////////////////////////////////////////////////////////////////// BOOL BLocalAccountsInSD(PSECURITY_DESCRIPTOR pSD, LPCTSTR pszClusterNameNode) { PACL paclDACL = NULL; BOOL bHasDACL = FALSE; BOOL bDaclDefaulted = FALSE; BOOL bLocalAccountInACL = FALSE; BOOL bRtn = FALSE; ACL_SIZE_INFORMATION asiAclSize; DWORD dwBufLength; DWORD dwACL_Index = 0L; ACCESS_ALLOWED_ACE * paaAllowedAce; TCHAR szUserName[128]; TCHAR szDomainName[128]; DWORD cbUser = 128; DWORD cbDomain = 128; SID_NAME_USE SidType; PUCHAR pnSubAuthorityCount; PULONG pnSubAuthority0; PULONG pnSubAuthority1; bRtn = IsValidSecurityDescriptor(pSD); ASSERT(bRtn); if ( bRtn == FALSE ) { goto Cleanup; } bRtn = GetSecurityDescriptorDacl( pSD, (LPBOOL)&bHasDACL, (PACL *)&paclDACL, (LPBOOL)&bDaclDefaulted); ASSERT(bRtn); if ( bRtn == FALSE ) { goto Cleanup; } if ( paclDACL == NULL ) { goto Cleanup; } bRtn = IsValidAcl(paclDACL); ASSERT(bRtn); if ( bRtn == FALSE ) { goto Cleanup; } dwBufLength = sizeof(asiAclSize); bRtn = GetAclInformation( paclDACL, (LPVOID)&asiAclSize, (DWORD)dwBufLength, (ACL_INFORMATION_CLASS) AclSizeInformation ); ASSERT(bRtn); if ( bRtn == FALSE ) { goto Cleanup; } // Search the ACL for local account ACEs // PSID pSID; while ( dwACL_Index < asiAclSize.AceCount ) { if (!GetAce(paclDACL, dwACL_Index, (LPVOID *)&paaAllowedAce)) { ASSERT(FALSE); goto Cleanup; } if((((PACE_HEADER)paaAllowedAce)->AceType) == ACCESS_ALLOWED_ACE_TYPE) { // //Get SID from ACE // pSID=(PSID)&((PACCESS_ALLOWED_ACE)paaAllowedAce)->SidStart; cbUser = 128; cbDomain = 128; if (LookupAccountSid(NULL, pSID, szUserName, &cbUser, szDomainName, &cbDomain, &SidType )) { if ( ClRtlStrNICmp( szDomainName, _T("BUILTIN"), RTL_NUMBER_OF( szDomainName ) ) == 0 ) { pnSubAuthorityCount = GetSidSubAuthorityCount( pSID ); if ( (pnSubAuthorityCount != NULL) && (*pnSubAuthorityCount == 2) ) { // Check to see if this is the local Administrators group. pnSubAuthority0 = GetSidSubAuthority( pSID, 0 ); pnSubAuthority1 = GetSidSubAuthority( pSID, 1 ); if ( (pnSubAuthority0 == NULL) || (pnSubAuthority1 == NULL) || ( (*pnSubAuthority0 != SECURITY_BUILTIN_DOMAIN_RID) && (*pnSubAuthority1 != SECURITY_BUILTIN_DOMAIN_RID)) || ( (*pnSubAuthority0 != DOMAIN_ALIAS_RID_ADMINS) && (*pnSubAuthority1 != DOMAIN_ALIAS_RID_ADMINS))) { bLocalAccountInACL = TRUE; break; } // if: not the local Administrators group } // if: exactly 2 sub-authorities else { bLocalAccountInACL = TRUE; break; } // else: unexpected # of sub-authorities } // if: built-in user or group else if ( ( ClRtlStrNICmp(szDomainName, pszClusterNameNode, RTL_NUMBER_OF( szDomainName ) ) == 0 ) && ( SidType != SidTypeDomain ) ) { // The domain name is the name of the node on which the // cluster name resource is online, so this is a local // user or group. bLocalAccountInACL = TRUE; break; } // else if: domain is cluster name resource node and not a Domain SID } // if: LookupAccountSid succeeded else { // If LookupAccountSid failed, assume that the SID is for // a user or group that is local to a machine to which we // don't have access. bLocalAccountInACL = TRUE; break; } // else: LookupAccountSid failed } dwACL_Index++; } Cleanup: return bLocalAccountInACL; } //*** BLocalAccountsInSD //+------------------------------------------------------------------------- // // Function: SedCallback // // Synopsis: Security Editor callback for the SHARE ACL Editor // // Arguments: See sedapi.h // // History: // ChuckC 10-Aug-1992 Created // BruceFo 4-Apr-95 Stole and used in ntshrui.dll // DavidP 10-Oct-1996 Modified for use with CLUADMIN // //-------------------------------------------------------------------------- DWORD SedCallback( HWND hwndParent, HANDLE hInstance, ULONG ulCallbackContext, PSECURITY_DESCRIPTOR pSecDesc, PSECURITY_DESCRIPTOR pSecDescNewObjects, BOOLEAN fApplyToSubContainers, BOOLEAN fApplyToSubObjects, LPDWORD StatusReturn ) { DWORD nStatus = NOERROR; SHARE_CALLBACK_INFO * pCallbackInfo = (SHARE_CALLBACK_INFO *)ulCallbackContext; TRACE(_T("SedCallback, got pSecDesc = 0x%08lx\n"), pSecDesc); ASSERT(pCallbackInfo != NULL); ASSERT(IsValidSecurityDescriptor(pSecDesc)); if ( BLocalAccountsInSD(pSecDesc, pCallbackInfo->pszClusterNameNode) ) { CString strMsg; strMsg.LoadString(IDS_LOCAL_ACCOUNTS_SPECIFIED); AfxMessageBox(strMsg, MB_OK | MB_ICONSTOP); nStatus = LOCAL_ACCOUNTS_FILTERED; goto Cleanup } // if: local users or groups were specified ASSERT(pCallbackInfo != NULL); delete[] (BYTE*)pCallbackInfo->pSecDesc; pCallbackInfo->pSecDesc = CopySecurityDescriptor(pSecDesc); pCallbackInfo->bSecDescModified = TRUE; ASSERT(IsValidSecurityDescriptor(pCallbackInfo->pSecDesc)); TRACE(_T("SedCallback, return pSecDesc = 0x%08lx\n"), pCallbackInfo->pSecDesc); Cleanup: return nStatus; } //*** SedCallback //+------------------------------------------------------------------------- // // Function: InitializeShareGenericMapping // // Synopsis: Initializes the passed generic mapping structure for shares. // // Arguments: [pSHAREGenericMapping] - Pointer to GENERIC_MAPPING to be init. // // History: // ChuckC 10-Aug-1992 Created. Culled from NTFS ACL code. // BruceFo 4-Apr-95 Stole and used in ntshrui.dll // DavidP 10-Oct-1996 Modified for use with CLUADMIN // //-------------------------------------------------------------------------- VOID InitializeShareGenericMapping( IN OUT PGENERIC_MAPPING pSHAREGenericMapping ) { TRACE(_T("InitializeShareGenericMapping\n")); pSHAREGenericMapping->GenericRead = GENERIC_READ; pSHAREGenericMapping->GenericWrite = GENERIC_WRITE; pSHAREGenericMapping->GenericExecute = GENERIC_EXECUTE; pSHAREGenericMapping->GenericAll = GENERIC_ALL; } //*** InitializeShareGenericMapping //+------------------------------------------------------------------------- // // Function: CreateDefaultSecDesc // // Synopsis: Create a default ACL for either a new share or for // a share that doesn't exist. // // Arguments: [ppSecDesc] - *ppSecDesc points to a "world all" access // security descriptor on exit. Caller is responsible for // freeing it. // // Returns: NERR_Success if OK, api error otherwise. // // History: // ChuckC 10-Aug-1992 Created. Culled from NTFS ACL code. // BruceFo 4-Apr-95 Stole and used in ntshrui.dll // DavidP 10-Oct-1996 Modified for use with CLUADMIN // //-------------------------------------------------------------------------- LONG CreateDefaultSecDesc( OUT PSECURITY_DESCRIPTOR* ppSecDesc ) { TRACE(_T("CreateDefaultSecDesc\n")); ASSERT(ppSecDesc != NULL) ; ASSERT(*ppSecDesc == NULL) ; LONG err = NERR_Success; PSECURITY_DESCRIPTOR pSecDesc = NULL; PACL pAcl = NULL; DWORD cbAcl; PSID pSid = NULL; *ppSecDesc = NULL; // First, create a world SID. Next, create an access allowed // ACE with "Generic All" access with the world SID. Put the ACE in // the ACL and the ACL in the security descriptor. SID_IDENTIFIER_AUTHORITY IDAuthorityWorld = SECURITY_WORLD_SID_AUTHORITY; if (!AllocateAndInitializeSid( &IDAuthorityWorld, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &pSid)) { err = GetLastError(); TRACE(_T("AllocateAndInitializeSid failed, 0x%08lx\n"), err); goto Cleanup; } ASSERT(IsValidSid(pSid)); cbAcl = sizeof(ACL) + (sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD)) + GetLengthSid(pSid) ; try { pAcl = (PACL) new BYTE[cbAcl]; } // try catch (CMemoryException * pme) { err = ERROR_OUTOFMEMORY; TRACE(_T("new ACL failed\n")); pme->Delete(); goto Cleanup; } // catch: CMemoryException if (!InitializeAcl(pAcl, cbAcl, ACL_REVISION2)) { err = GetLastError(); TRACE(_T("InitializeAcl failed, 0x%08lx\n"), err); goto Cleanup; } if (!AddAccessAllowedAce( pAcl, ACL_REVISION2, FILE_PERM_ALL, pSid)) { err = GetLastError(); TRACE(_T("AddAccessAllowedAce failed, 0x%08lx\n"), err); goto Cleanup; } ASSERT(IsValidAcl(pAcl)); try { pSecDesc = (PSECURITY_DESCRIPTOR) new BYTE[SECURITY_DESCRIPTOR_MIN_LENGTH]; } // try catch (CMemoryException * pme) { err = ERROR_OUTOFMEMORY; TRACE(_T("new SECURITY_DESCRIPTOR failed\n")); pme->Delete(); goto Cleanup; } // catch: CMemoryException if (!InitializeSecurityDescriptor( pSecDesc, SECURITY_DESCRIPTOR_REVISION1)) { err = GetLastError(); TRACE(_T("InitializeSecurityDescriptor failed, 0x%08lx\n"), err); goto Cleanup; } if (!SetSecurityDescriptorDacl( pSecDesc, TRUE, pAcl, FALSE)) { err = GetLastError(); TRACE(_T("SetSecurityDescriptorDacl failed, 0x%08lx\n"), err); goto Cleanup; } ASSERT(IsValidSecurityDescriptor(pSecDesc)); // Make the security descriptor self-relative DWORD dwLen = GetSecurityDescriptorLength(pSecDesc); TRACE(_T("SECURITY_DESCRIPTOR length = %d\n"), dwLen); PSECURITY_DESCRIPTOR pSelfSecDesc = NULL; try { pSelfSecDesc = (PSECURITY_DESCRIPTOR) new BYTE[dwLen]; } // try catch (CMemoryException * pme) { err = ERROR_OUTOFMEMORY; TRACE(_T("new SECURITY_DESCRIPTOR (2) failed\n")); pme->Delete(); goto Cleanup; } // catch: CMemoryException DWORD cbSelfSecDesc = dwLen; if (!MakeSelfRelativeSD(pSecDesc, pSelfSecDesc, &cbSelfSecDesc)) { err = GetLastError(); TRACE(_T("MakeSelfRelativeSD failed, 0x%08lx\n"), err); goto Cleanup; } ASSERT(IsValidSecurityDescriptor(pSelfSecDesc)); // // all done: set the security descriptor // *ppSecDesc = pSelfSecDesc; Cleanup: if (NULL != pSid) { FreeSid(pSid); } delete[] (BYTE*)pAcl; delete[] (BYTE*)pSecDesc; ASSERT(IsValidSecurityDescriptor(*ppSecDesc)); return err; } //*** CreateDefaultSecDesc //+------------------------------------------------------------------------- // // Function: DeleteDefaultSecDesc // // Synopsis: Delete a security descriptor that was created by // CreateDefaultSecDesc // // Arguments: [pSecDesc] - security descriptor to delete // // Returns: nothing // // History: // BruceFo 4-Apr-95 Created // DavidP 10-Oct-1996 Modified for use with CLUADMIN // //-------------------------------------------------------------------------- VOID DeleteDefaultSecDesc( IN PSECURITY_DESCRIPTOR pSecDesc ) { TRACE(_T("DeleteDefaultSecDesc\n")); delete[] (BYTE*)pSecDesc; } //*** DeleteDefaultSecDesc //+------------------------------------------------------------------------- // // Member: CopySecurityDescriptor, public // // Synopsis: Copy an NT security descriptor. The security descriptor must // be in self-relative (not absolute) form. Delete the result // using "delete[] (BYTE*)pSecDesc". // // History: 19-Apr-95 BruceFo Created // 10-Oct-1996 DavidP Modified for use with CLUADMIN // //-------------------------------------------------------------------------- PSECURITY_DESCRIPTOR CopySecurityDescriptor( IN PSECURITY_DESCRIPTOR pSecDesc ) { TRACE(_T("CopySecurityDescriptor, pSecDesc = 0x%08lx\n"), pSecDesc); ASSERT(IsValidSecurityDescriptor(pSecDesc)); size_t cbLen = GetSecurityDescriptorLength(pSecDesc); size_t cbSelfSecDesc = cbLen; PSECURITY_DESCRIPTOR pSelfSecDesc = NULL; if ( pSecDesc == NULL ) { goto Cleanup; } try { pSelfSecDesc = (PSECURITY_DESCRIPTOR) new BYTE[ cbLen ]; } catch (CMemoryException * pme) { TRACE(_T("new SECURITY_DESCRIPTOR (2) failed\n")); pme->Delete(); return NULL; // actually, should probably return an error } // catch: CMemoryException if (!MakeSelfRelativeSD(pSecDesc, pSelfSecDesc, &cbSelfSecDesc)) { TRACE(_T("MakeSelfRelativeSD failed, 0x%08lx\n"), GetLastError()); // assume it failed because it was already self-relative CopyMemory( pSelfSecDesc, pSecDesc, cbLen ); } ASSERT(IsValidSecurityDescriptor(pSelfSecDesc)); Cleanup: return pSelfSecDesc; } //*** CopySecurityDescriptor //+--------------------------------------------------------------------------- // // Function: GetResourceString // // Synopsis: Load a resource string, are return a "new"ed copy // // Arguments: [dwId] -- a resource string ID // // Returns: new memory copy of a string // // History: 5-Apr-95 BruceFo Created // 10-Oct-1996 DavidP Modified for CLUADMIN // //---------------------------------------------------------------------------- PWSTR GetResourceString( IN DWORD dwId ) { CString str; PWSTR pwsz = NULL; if (str.LoadString(dwId)) { pwsz = NewDup(str); } return pwsz; } //*** GetResourceString //+--------------------------------------------------------------------------- // // Function: NewDup // // Synopsis: Duplicate a string using '::new' // // History: 28-Dec-94 BruceFo Created // 10-Oct-1996 DavidP Modified for CLUADMIN // //---------------------------------------------------------------------------- PWSTR NewDup( IN const WCHAR* psz ) { PWSTR pszRet = NULL; size_t cch; HRESULT hr; if ( psz == NULL ) { TRACE(_T("Illegal string to duplicate: NULL\n")); goto Cleanup; } cch = wcslen( psz ) + 1; try { pszRet = new WCHAR[ cch ]; if ( pszRet == NULL ) { TRACE( _T("OUT OF MEMORY\n") ); goto Cleanup; } } catch (CMemoryException * pme) { TRACE(_T("OUT OF MEMORY\n")); pme->Delete(); pszRet = NULL; goto Cleanup; } // catch: CMemoryException hr = StringCchCopyW( pszRet, cch, psz ); ASSERT( SUCCEEDED( hr ) ); Cleanup: return pszRet; } //*** NewDup //+------------------------------------------------------------------------- // // Function: MapBitsInSD // // Synopsis: Maps Specific bits to Generic bit when MAP_DIRECTION is SPECIFIC_TO_GENERIC // Maps Generic bits to Specific bit when MAP_DIRECTION is GENERIC_TO_SPECIFIC // // Arguments: [pSecDesc] - SECURITY_DESCIRPTOR to be modified // [direction] - indicates whether bits are mapped from specific to generic // or generic to specific. // Author: // Roderick Sharper (rodsh) April 12, 1997 // // History: // //-------------------------------------------------------------------------- BOOL MapBitsInSD(PSECURITY_DESCRIPTOR pSecDesc, MAP_DIRECTION direction) { PACL paclDACL = NULL; BOOL bHasDACL = FALSE; BOOL bDaclDefaulted = FALSE; BOOL bRtn = FALSE; if (!IsValidSecurityDescriptor(pSecDesc)) { goto Cleanup; } if (!GetSecurityDescriptorDacl(pSecDesc, (LPBOOL)&bHasDACL, (PACL *)&paclDACL, (LPBOOL)&bDaclDefaulted )) { goto Cleanup; } if (paclDACL) { bRtn = MapBitsInACL(paclDACL, direction); } Cleanup: return bRtn; } //*** MapBitsInSD //+------------------------------------------------------------------------- // // Function: MapBitsInACL // // Synopsis: Maps Specific bits to Generic bit when MAP_DIRECTION is SPECIFIC_TO_GENERIC // Maps Generic bits to Specific bit when MAP_DIRECTION is GENERIC_TO_SPECIFIC // // // Arguments: [paclACL] - ACL (Access Control List) to be modified // [direction] - indicates whether bits are mapped from specific to generic // or generic to specific. // Author: // Roderick Sharper (rodsh) May 02, 1997 // // History: // //-------------------------------------------------------------------------- BOOL MapBitsInACL(PACL paclACL, MAP_DIRECTION direction) { ACL_SIZE_INFORMATION asiAclSize; BOOL bRtn = FALSE; DWORD dwBufLength; DWORD dwACL_Index; ACCESS_ALLOWED_ACE * paaAllowedAce; if (!IsValidAcl(paclACL)) { goto Cleanup; } dwBufLength = sizeof(asiAclSize); if (!GetAclInformation(paclACL, (LPVOID)&asiAclSize, (DWORD)dwBufLength, (ACL_INFORMATION_CLASS)AclSizeInformation)) { goto Cleanup; } for (dwACL_Index = 0; dwACL_Index < asiAclSize.AceCount; dwACL_Index++) { if (!GetAce(paclACL, dwACL_Index, (LPVOID *) &paaAllowedAce)) { goto Cleanup; } if ( direction == SPECIFIC_TO_GENERIC ) { bRtn = MapSpecificBitsInAce( paaAllowedAce ); } else if ( direction == GENERIC_TO_SPECIFIC ) { bRtn = MapGenericBitsInAce( paaAllowedAce ); } else { bRtn = FALSE; } } // for: each ACE Cleanup: return bRtn; } //*** MapBitsInACL //+------------------------------------------------------------------------- // // Function: MapSpecificBitsInAce // // Synopsis: Maps specific bits in ACE to generic bits // // Arguments: [paaAllowedAce] - ACE (Access Control Entry) to be modified // [direction] - indicates whether bits are mapped from specific to generic // or generic to specific. // Author: // Roderick Sharper (rodsh) May 02, 1997 // // History: // //-------------------------------------------------------------------------- BOOL MapSpecificBitsInAce(PACCESS_ALLOWED_ACE paaAllowedAce) { ACCESS_MASK amMask = paaAllowedAce->Mask; BOOL bRtn = FALSE; DWORD dwGenericBits; DWORD dwSpecificBits; dwSpecificBits = (amMask & SPECIFIC_RIGHTS_ALL); dwGenericBits = 0; switch( dwSpecificBits ) { case CLUSAPI_READ_ACCESS: dwGenericBits = GENERIC_READ; // GENERIC_READ == 0x80000000L bRtn = TRUE; break; case CLUSAPI_CHANGE_ACCESS: dwGenericBits = GENERIC_WRITE; // GENERIC_WRITE == 0x40000000L bRtn = TRUE; break; case CLUSAPI_NO_ACCESS: dwGenericBits = GENERIC_EXECUTE;// GENERIC_EXECUTE == 0x20000000L bRtn = TRUE; break; case CLUSAPI_ALL_ACCESS: dwGenericBits = GENERIC_ALL; // GENERIC_ALL == 0x10000000L bRtn = TRUE; break; default: dwGenericBits = 0x00000000L; // Invalid,assign no rights. bRtn = FALSE; break; } // switch: on specific bits amMask = dwGenericBits; paaAllowedAce->Mask = amMask; return bRtn; } //*** MapSpecificBitsInAce //+------------------------------------------------------------------------- // // Function: MapGenericBitsInAce // // Synopsis: Maps generic bits in ACE to specific bits // // Arguments: [paaAllowedAce] - ACE (Access Control Entry) to be modified // [direction] - indicates whether bits are mapped from specific to generic // or generic to specific. // Author: // Roderick Sharper (rodsh) May 02, 1997 // // History: // //-------------------------------------------------------------------------- BOOL MapGenericBitsInAce (PACCESS_ALLOWED_ACE paaAllowedAce) { #define GENERIC_RIGHTS_ALL_THE_BITS 0xF0000000L ACCESS_MASK amMask = paaAllowedAce->Mask; BOOL bRtn = FALSE; DWORD dwGenericBits; DWORD dwSpecificBits; dwSpecificBits = 0; dwGenericBits = (amMask & GENERIC_RIGHTS_ALL_THE_BITS); switch( dwGenericBits ) { case GENERIC_ALL: dwSpecificBits = CLUSAPI_ALL_ACCESS; // CLUSAPI_ALL_ACCESS == 3 bRtn = TRUE; break; case GENERIC_EXECUTE: dwSpecificBits = CLUSAPI_NO_ACCESS; // CLUSAPI_NO_ACCESS == 4 bRtn = TRUE; break; case GENERIC_WRITE: dwSpecificBits = CLUSAPI_CHANGE_ACCESS; // CLUSAPI_CHANGE_ACCESS == 2 bRtn = TRUE; break; case GENERIC_READ: dwSpecificBits = CLUSAPI_READ_ACCESS; // CLUSAPI_READ_ACCESS == 1 bRtn = TRUE; break; default: dwSpecificBits = 0x00000000L; // Invalid, assign no rights. bRtn = FALSE; break; } // switch: on generic bits amMask = dwSpecificBits; paaAllowedAce->Mask = amMask; return bRtn; } //*** MapGenericBitsInAce