//+------------------------------------------------------------------------- // // Microsoft Windows // // Copyright (C) Microsoft Corporation, 1997 - 1999 // // File: newobjcr.cpp // //-------------------------------------------------------------------------- ///////////////////////////////////////////////////////////////////// // newobjcr.cpp // // This file contains implementation of functions to create // new ADs objects. // // HISTORY // 19-Aug-97 Dan Morin Creation. // ///////////////////////////////////////////////////////////////////// #include "stdafx.h" #include "newobj.h" #include "dlgcreat.h" #include "dscmn.h" // CrackName() #include "gencreat.h" #include "querysup.h" // CDSSearch extern "C" { #ifdef FRS_CREATE #include "dsquery.h" // CLSID_DsFindFrsMembers #endif // FRS_CREATE #include } #define BREAK_ON_TRUE(b) if (b) { ASSERT(FALSE); break; } #define BREAK_ON_FAIL BREAK_ON_TRUE(FAILED(hr)) #define RETURN_IF_FAIL if (FAILED(hr)) { ASSERT(FALSE); return hr; } // // The schedule block has been redefined to have 1 byte for every hour. // CODEWORK These should be defined in SCHEDULE.H. JonN 2/9/98 // #define INTERVAL_MASK 0x0F #define RESERVED 0xF0 #define FIRST_15_MINUTES 0x01 #define SECOND_15_MINUTES 0x02 #define THIRD_15_MINUTES 0x04 #define FORTH_15_MINUTES 0x08 // The dialog has one bit per hour, the DS schedule has one byte per hour #define cbDSScheduleArrayLength (24*7) #define HeadersSizeNum(NumberOfSchedules) \ (sizeof(SCHEDULE) + ((NumberOfSchedules)-1)*sizeof(SCHEDULE_HEADER)) inline ULONG HeadersSize(SCHEDULE* psched) { return HeadersSizeNum(psched->NumberOfSchedules); } ///////////////////////////////////////////////////////////////////// // HrCreateADsUser() // // Create a new user. // HRESULT HrCreateADsUser(INOUT CNewADsObjectCreateInfo * pNewADsObjectCreateInfo) { ASSERT(pNewADsObjectCreateInfo != NULL); #ifdef INETORGPERSON ASSERT(0 == lstrcmp(L"user", pNewADsObjectCreateInfo->m_pszObjectClass) || 0 == lstrcmp(L"inetOrgPerson", pNewADsObjectCreateInfo->m_pszObjectClass)); #else ASSERT(0 == lstrcmp(L"user", pNewADsObjectCreateInfo->m_pszObjectClass)); #endif CCreateNewUserWizard wiz(pNewADsObjectCreateInfo); return wiz.DoModal(); } // HrCreateADsUser() ///////////////////////////////////////////////////////////////////// // HrCreateADsVolume() // // Create a new volume. // HRESULT HrCreateADsVolume(INOUT CNewADsObjectCreateInfo * pNewADsObjectCreateInfo) { ASSERT(pNewADsObjectCreateInfo != NULL); ASSERT(0 == lstrcmp(L"volume", pNewADsObjectCreateInfo->m_pszObjectClass)); CCreateNewVolumeWizard wiz(pNewADsObjectCreateInfo); return wiz.DoModal(); } // HrCreateADsVolume() ///////////////////////////////////////////////////////////////////// // HrCreateADsComputer() // // Create a new computer. // HRESULT HrCreateADsComputer(INOUT CNewADsObjectCreateInfo * pNewADsObjectCreateInfo) { ASSERT(pNewADsObjectCreateInfo != NULL); ASSERT(0 == lstrcmp(L"computer", pNewADsObjectCreateInfo->m_pszObjectClass)); CCreateNewComputerWizard wiz(pNewADsObjectCreateInfo); return wiz.DoModal(); } // HrCreateADsComputer() ///////////////////////////////////////////////////////////////////// // HrCreateADsPrintQueue() // // Create a new print queue object. // HRESULT HrCreateADsPrintQueue(INOUT CNewADsObjectCreateInfo * pNewADsObjectCreateInfo) { ASSERT(pNewADsObjectCreateInfo != NULL); ASSERT(0 == lstrcmp(L"printQueue", pNewADsObjectCreateInfo->m_pszObjectClass)); CCreateNewPrintQWizard wiz(pNewADsObjectCreateInfo); return wiz.DoModal(); } // HrCreateADsPrintQueue() ///////////////////////////////////////////////////////////////////// // HrCreateADsNtDsConnection() // // Create a new NTDS-Connection object. Note that this is not supported // if the parent is an FRS object. // HRESULT HrCreateADsNtDsConnection(INOUT CNewADsObjectCreateInfo * pNewADsObjectCreateInfo) { ASSERT(pNewADsObjectCreateInfo != NULL); ASSERT(0 == lstrcmp(L"nTDSConnection", pNewADsObjectCreateInfo->m_pszObjectClass)); // do not allow this in the standalone case if (pNewADsObjectCreateInfo->IsStandaloneUI()) { ASSERT(FALSE); return E_INVALIDARG; } // determine whether this is an NTDS connection or an FRS connection // CODEWORK this code can probably be removed CPathCracker pathCracker; HRESULT hr = S_OK; CString strConfigPath; CComBSTR sbstrParentPath; bool fParentIsFrs = false; { // determine whether this is an FRS instance CComQIPtr spIADsParent( pNewADsObjectCreateInfo->m_pIADsContainer ); ASSERT( !!spIADsParent ); CComBSTR sbstrClass; hr = spIADsParent->get_ADsPath( &sbstrParentPath ); RETURN_IF_FAIL; hr = spIADsParent->get_Class( &sbstrClass ); RETURN_IF_FAIL; hr = DSPROP_IsFrsObject( sbstrClass, &fParentIsFrs ); RETURN_IF_FAIL; // Determine which subtree should be searched if (fParentIsFrs) { #ifndef FRS_CREATE // We shouldn't have seen the option to create a connection here ASSERT(FALSE); return S_FALSE; #else sbstrClass.Empty(); hr = spIADsParent->get_ADsPath( &sbstrClass ); RETURN_IF_FAIL; hr = DSPROP_RemoveX500LeafElements( 1, &sbstrClass ); RETURN_IF_FAIL; strConfigPath = sbstrClass; #endif // FRS_CREATE } else { pNewADsObjectCreateInfo->GetBasePathsInfo()->GetConfigPath(strConfigPath); } } CCreateNewObjectCnWizard wiz(pNewADsObjectCreateInfo); // Get the target server path from the user. The path is stored in a BSTR variant. CComBSTR sbstrTargetServer; #ifdef FRS_CREATE if (fParentIsFrs) { hr = DSPROP_DSQuery( pNewADsObjectCreateInfo->GetParentHwnd(), strConfigPath, const_cast(&CLSID_DsFindFrsMembers), &sbstrTargetServer ); } else #endif // FRS_CREATE { hr = DSPROP_PickNTDSDSA( pNewADsObjectCreateInfo->GetParentHwnd(), strConfigPath, &sbstrTargetServer ); } if (hr == S_FALSE) { // User canceled the dialog return S_FALSE; } RETURN_IF_FAIL; if (sbstrTargetServer == sbstrParentPath) { // 6231: Shouldn't be able to create a connection to "yourself" ReportMessageEx( pNewADsObjectCreateInfo->GetParentHwnd(), IDS_CONNECTION_TO_SELF ); return S_FALSE; } CComBSTR sbstrTargetServerX500DN; hr = pathCracker.Set( sbstrTargetServer, ADS_SETTYPE_FULL ); RETURN_IF_FAIL; hr = pathCracker.SetDisplayType( ADS_DISPLAY_FULL ); RETURN_IF_FAIL; hr = pathCracker.Retrieve( ADS_FORMAT_X500_DN, &sbstrTargetServerX500DN ); RETURN_IF_FAIL; // 33881: prevent duplicate connection objects { CDSSearch Search; Search.Init(sbstrParentPath); CString filter; filter.Format(L"(fromServer=%s)", sbstrTargetServerX500DN); Search.SetFilterString(const_cast((LPCTSTR) filter)); LPWSTR pAttrs[1] = { L"name" }; Search.SetAttributeList(pAttrs, 1); Search.SetSearchScope(ADS_SCOPE_SUBTREE); hr = Search.DoQuery(); if (SUCCEEDED(hr)) { hr = Search.GetNextRow(); if (SUCCEEDED(hr) && S_ADS_NOMORE_ROWS != hr) { DWORD dwRetval = ReportMessageEx( pNewADsObjectCreateInfo->GetParentHwnd(), IDS_DUPLICATE_CONNECTION, MB_YESNO | MB_DEFBUTTON2 | MB_ICONWARNING ); if (IDYES != dwRetval) return S_FALSE; } } } hr = pNewADsObjectCreateInfo->HrAddVariantBstr( L"fromServer", sbstrTargetServerX500DN, TRUE ); RETURN_IF_FAIL; { // NTDS: set default name to RDN of parent of target NTDS-DSA // FRS: set default name to RDN of target NTFRS-Member CComBSTR bstrDefaultRDN; hr = pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY); RETURN_IF_FAIL; hr = pathCracker.GetElement( (fParentIsFrs) ? 0 : 1, &bstrDefaultRDN ); RETURN_IF_FAIL; ASSERT( !!bstrDefaultRDN && TEXT('\0') != *bstrDefaultRDN ); pNewADsObjectCreateInfo->m_strDefaultObjectName = bstrDefaultRDN; hr = pathCracker.SetDisplayType(ADS_DISPLAY_FULL); RETURN_IF_FAIL; } // // Must do this before DoModal, OnOK will try to actually create the object // hr = pNewADsObjectCreateInfo->HrAddVariantLong(L"options", 0, TRUE); RETURN_IF_FAIL; hr = pNewADsObjectCreateInfo->HrAddVariantBoolean(L"enabledConnection", TRUE, TRUE); RETURN_IF_FAIL; { // // Store initial schedule // BYTE abyteSchedule[ HeadersSizeNum(1) + cbDSScheduleArrayLength ]; ZeroMemory( &abyteSchedule, sizeof(abyteSchedule) ); PSCHEDULE pNewScheduleBlock = (PSCHEDULE) abyteSchedule; pNewScheduleBlock->Size = sizeof(abyteSchedule); pNewScheduleBlock->NumberOfSchedules = 1; pNewScheduleBlock->Schedules[0].Type = SCHEDULE_INTERVAL; pNewScheduleBlock->Schedules[0].Offset = HeadersSizeNum(1); memset( ((BYTE*)pNewScheduleBlock)+pNewScheduleBlock->Schedules[0].Offset, INTERVAL_MASK, cbDSScheduleArrayLength ); // turn on all intervals CComVariant varSchedule; hr = BinaryToVariant( sizeof(abyteSchedule), abyteSchedule, &varSchedule ); RETURN_IF_FAIL; hr = pNewADsObjectCreateInfo->HrAddVariantCopyVar(L"schedule", IN varSchedule, TRUE); RETURN_IF_FAIL; } // CODEWORK: Need to set the dialog caption hr = wiz.DoModal(); return hr; } // HrCreateADsNtDsConnection() HRESULT HrCreateADsFixedName(INOUT CNewADsObjectCreateInfo * pNewADsObjectCreateInfo) { // Store the object name in the temporary storage LPCWSTR pcsz = reinterpret_cast(pNewADsObjectCreateInfo->QueryCreationParameter()); ASSERT( NULL != pcsz ); pNewADsObjectCreateInfo->HrCreateNew(pcsz); // Create and persist the object HRESULT hr = pNewADsObjectCreateInfo->HrSetInfo(TRUE /*fSilentError*/); if (SUCCEEDED(hr)) { CString csCaption, csMsg; csCaption.LoadString(IDS_CREATE_NEW_OBJECT_TITLE); csMsg.Format(IDS_s_CREATE_NEW_OBJECT_NOTICE, pNewADsObjectCreateInfo->GetName()); ::MessageBox( pNewADsObjectCreateInfo->GetParentHwnd(), csMsg, csCaption, MB_OK | MB_SETFOREGROUND | MB_ICONINFORMATION); } return hr; } HRESULT HrCreateADsSiteLink(INOUT CNewADsObjectCreateInfo * pNewADsObjectCreateInfo) { ASSERT(pNewADsObjectCreateInfo != NULL); ASSERT(0 == lstrcmp(gsz_siteLink, pNewADsObjectCreateInfo->m_pszObjectClass)); // load list of sites DSPROP_BSTR_BLOCK bstrblock; CComQIPtr container(pNewADsObjectCreateInfo->m_pIADsContainer); if (container) { CComBSTR container_path; container->get_ADsPath(&container_path); HRESULT hr = DSPROP_RemoveX500LeafElements( 2, &container_path ); if ( SUCCEEDED(hr) ) { hr = DSPROP_ShallowSearch( &bstrblock, container_path, L"site" ); } if ( FAILED(hr) ) { ReportErrorEx (pNewADsObjectCreateInfo->GetParentHwnd(), IDS_SITELINKERROR_READING_SITES, hr, MB_OK, NULL, 0); return S_FALSE; } } if ( 2 > bstrblock.QueryCount() ) { ReportMessageEx(pNewADsObjectCreateInfo->GetParentHwnd(), IDS_SITELINK_NOT_ENOUGH_SITES, MB_OK | MB_ICONSTOP); // allow wizard to continue, CODEWORK note that this // doesn't quite work right when zero sites are detected } // set default cost to 100 (by request of JeffParh) HRESULT hr = pNewADsObjectCreateInfo->HrAddVariantLong(L"cost", 100L, TRUE); RETURN_IF_FAIL; // set default replInterval to 180 (by request of JeffParh) hr = pNewADsObjectCreateInfo->HrAddVariantLong(L"replInterval", 180L, TRUE); RETURN_IF_FAIL; CCreateNewSiteLinkWizard wiz(pNewADsObjectCreateInfo,bstrblock); return wiz.DoModal(); } HRESULT HrCreateADsSiteLinkBridge(INOUT CNewADsObjectCreateInfo * pNewADsObjectCreateInfo) { ASSERT(pNewADsObjectCreateInfo != NULL); ASSERT(0 == lstrcmp(gsz_siteLinkBridge, pNewADsObjectCreateInfo->m_pszObjectClass)); // load list of site links DSPROP_BSTR_BLOCK bstrblock; CComQIPtr container(pNewADsObjectCreateInfo->m_pIADsContainer); if (container) { CComBSTR container_path; container->get_ADsPath(&container_path); HRESULT hr = DSPROP_ShallowSearch( &bstrblock, container_path, L"siteLink" ); if ( FAILED(hr) ) { ReportErrorEx (pNewADsObjectCreateInfo->GetParentHwnd(), IDS_SITELINKBRIDGEERROR_READING_SITELINKS, hr, MB_OK, NULL, 0); return S_FALSE; } } if ( 2 > bstrblock.QueryCount() ) { ReportMessageEx(pNewADsObjectCreateInfo->GetParentHwnd(), IDS_SITELINKBRIDGE_NOT_ENOUGH_SITELINKS, MB_OK | MB_ICONSTOP); return S_FALSE; // do not allow wizard to continue } CCreateNewSiteLinkBridgeWizard wiz(pNewADsObjectCreateInfo,bstrblock); return wiz.DoModal(); } #ifdef FRS_CREATE HRESULT HrCreateADsNtFrsMember(INOUT CNewADsObjectCreateInfo * pNewADsObjectCreateInfo) { ASSERT(pNewADsObjectCreateInfo != NULL); ASSERT(0 == lstrcmp(gsz_nTFRSMember, pNewADsObjectCreateInfo->m_pszObjectClass)); // // set up Frs-Computer-Reference attribute // CComBSTR sbstrComputerPath; // pNewADsObjectCreateInfo->m_strDefaultObjectName = sbstrComputerRDN; HRESULT hr = DSPROP_PickComputer( pNewADsObjectCreateInfo->GetParentHwnd(), &sbstrComputerPath ); RETURN_IF_FAIL; // Allow user to quit if user hit Cancel if (hr == S_FALSE) return S_FALSE; // set default name to RDN of target Computer hr = pathCracker.Set(sbstrComputerPath, ADS_SETTYPE_FULL); RETURN_IF_FAIL; hr = pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY); RETURN_IF_FAIL; sbstrComputerPath.Empty(); hr = pathCracker.GetElement( 0, &sbstrComputerPath ); RETURN_IF_FAIL; pNewADsObjectCreateInfo->m_strDefaultObjectName = sbstrComputerPath; hr = pathCracker.SetDisplayType(ADS_DISPLAY_FULL); RETURN_IF_FAIL; // set frsComputerReference for new object sbstrComputerPath.Empty(); hr = pathCracker.Retrieve( ADS_FORMAT_X500_DN, &sbstrComputerPath ); RETURN_IF_FAIL; hr = pNewADsObjectCreateInfo->HrAddVariantBstr( L"frsComputerReference", sbstrComputerPath, TRUE ); RETURN_IF_FAIL; hr = HrCreateADsSimpleObject(pNewADsObjectCreateInfo); return hr; } HRESULT HrCreateADsNtFrsSubscriber(INOUT CNewADsObjectCreateInfo * pNewADsObjectCreateInfo) { ASSERT(pNewADsObjectCreateInfo != NULL); ASSERT(0 == lstrcmp(gsz_nTFRSSubscriber, pNewADsObjectCreateInfo->m_pszObjectClass)); // User finds target nTFRSMember object CComBSTR sbstrTargetMember; HRESULT hr = DSPROP_DSQuery( pNewADsObjectCreateInfo->GetParentHwnd(), NULL, // any member const_cast(&CLSID_DsFindFrsMembers), &sbstrTargetMember ); if (hr == S_FALSE) { // User canceled the dialog return S_FALSE; } RETURN_IF_FAIL; // set default name of new nTFRSSubscriber to RDN of target nTFRSMember hr = pathCracker.Set( sbstrTargetMember, ADS_SETTYPE_FULL ); RETURN_IF_FAIL; hr = pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY); RETURN_IF_FAIL; sbstrTargetMember.Empty(); hr = pathCracker.GetElement( 0, &sbstrTargetMember ); RETURN_IF_FAIL; pNewADsObjectCreateInfo->m_strDefaultObjectName = sbstrTargetMember; hr = pathCracker.SetDisplayType(ADS_DISPLAY_FULL); RETURN_IF_FAIL; // set fRSMemberReference attribute to target nTFRSMember sbstrTargetMember.Empty(); hr = pathCracker.Retrieve( ADS_FORMAT_X500_DN, &sbstrTargetMember ); RETURN_IF_FAIL; hr = pNewADsObjectCreateInfo->HrAddVariantBstr( L"fRSMemberReference", sbstrTargetMember, TRUE ); RETURN_IF_FAIL; CCreateNewFrsSubscriberWizard wiz(pNewADsObjectCreateInfo); return wiz.DoModal(); } //+---------------------------------------------------------------------------- // // Function: CreateADsNtFrsSubscriptions // // Purpose: Create an NT-FRS-Subscriptions object and then grant its parent // (a computer object) full access. // //----------------------------------------------------------------------------- HRESULT CreateADsNtFrsSubscriptions(CNewADsObjectCreateInfo * pNewADsObjectCreateInfo) { LPCWSTR pcsz = reinterpret_cast(pNewADsObjectCreateInfo->QueryCreationParameter()); ASSERT( NULL != pcsz ); pNewADsObjectCreateInfo->HrCreateNew(pcsz); // // Create and persist the object. This must be done before attempting to modify // the Security Descriptor. // HRESULT hr = pNewADsObjectCreateInfo->HrSetInfo(); if (FAILED(hr)) { TRACE(_T("pNewADsObjectCreateInfo->HrSetInfo failed!\n")); return hr; } // // Create a new ACE on this object granting the parent full control. First, get the // parent's SID. // CComVariant varSID; CComPtr pADS; hr = pNewADsObjectCreateInfo->m_pIADsContainer->QueryInterface(IID_IADs, (PVOID*)&pADS); if (FAILED(hr)) { TRACE(_T("QueryInterface(IID_IADs) failed!\n")); return hr; } hr = pADS->Get(L"objectSid", &varSID); if (FAILED(hr)) { TRACE(_T("Get(\"objectSid\") failed!\n")); return hr; } ASSERT((varSID.vt & ~VT_ARRAY) == VT_UI1); // this better be an array of BYTEs. ASSERT(varSID.parray->cbElements && varSID.parray->pvData); // // Get this object's Security Descriptor. // CComPtr pDirObj; hr = pNewADsObjectCreateInfo->PGetIADsPtr()->QueryInterface(IID_IDirectoryObject, (PVOID*)&pDirObj); if (FAILED(hr)) { TRACE(_T("QueryInterface(IID_IDirectoryObject) failed!\n")); return hr; } const PWSTR wzSecDescriptor = L"nTSecurityDescriptor"; PADS_ATTR_INFO pAttrs = NULL; DWORD cAttrs = 0; LPWSTR rgpwzAttrNames[] = {wzSecDescriptor}; hr = pDirObj->GetObjectAttributes(rgpwzAttrNames, 1, &pAttrs, &cAttrs); if (FAILED(hr)) { TRACE(_T("GetObjectAttributes(wzSecDescriptor) failed!\n")); return hr; } ASSERT(cAttrs == 1); // SD is a required attribute. Blow chunks if missing. ASSERT(pAttrs != NULL); ASSERT(pAttrs->pADsValues != NULL); if (!pAttrs->pADsValues->SecurityDescriptor.lpValue || !pAttrs->pADsValues->SecurityDescriptor.dwLength) { TRACE(_T("IADS return bogus SD!\n")); FreeADsMem(pAttrs); return E_UNEXPECTED; } if (!IsValidSecurityDescriptor(pAttrs->pADsValues->SecurityDescriptor.lpValue)) { TRACE(_T("IsValidSecurityDescriptor failed!\n")); FreeADsMem(pAttrs); return HRESULT_FROM_WIN32(GetLastError()); } // // Can't modify a self-relative SD so convert it to an absolute one. // PSECURITY_DESCRIPTOR pAbsSD = NULL, pNewSD; PACL pDacl = NULL, pSacl = NULL; PSID pOwnerSid = NULL, pPriGrpSid = NULL; DWORD cbSD = 0, cbDacl = 0, cbSacl = 0, cbOwner = 0, cbPriGrp = 0; if (!MakeAbsoluteSD(pAttrs->pADsValues->SecurityDescriptor.lpValue, pAbsSD, &cbSD, pDacl, &cbDacl, pSacl, &cbSacl, pOwnerSid, &cbOwner, pPriGrpSid, &cbPriGrp)) { DWORD dwErr = GetLastError(); if (dwErr != ERROR_INSUFFICIENT_BUFFER) { TRACE(_T("MakeAbsoluteSD failed to return buffer sizes!\n")); FreeADsMem(pAttrs); return HRESULT_FROM_WIN32(GetLastError()); } } if (!cbDacl) { TRACE(_T("SD missing DACL!\n")); FreeADsMem(pAttrs); return E_UNEXPECTED; } WORD wSizeNeeded = (WORD)(sizeof(ACCESS_ALLOWED_ACE) + // the last element of GetLengthSid(varSID.parray->pvData) - // the ACE struct is the sizeof(DWORD)); // first DWORD of the SID. CSmartBytePtr spAbsSD(cbSD), spSacl(cbSacl); CSmartBytePtr spDacl(cbDacl + wSizeNeeded); CSmartBytePtr spOwnerSid(cbOwner), spPriGrpSid(cbPriGrp); pAbsSD = spAbsSD; pDacl = (PACL)(PBYTE)spDacl; pSacl = (PACL)(PBYTE)spSacl; pOwnerSid = spOwnerSid; pPriGrpSid = spPriGrpSid; if (!(pAbsSD && pDacl && pSacl && pOwnerSid && pPriGrpSid)) { TRACE(_T("SD allocation failed!\n")); FreeADsMem(pAttrs); return E_OUTOFMEMORY; } if (!MakeAbsoluteSD(pAttrs->pADsValues->SecurityDescriptor.lpValue, pAbsSD, &cbSD, pDacl, &cbDacl, pSacl, &cbSacl, pOwnerSid, &cbOwner, pPriGrpSid, &cbPriGrp)) { TRACE(_T("MakeAbsoluteSD failed!\n")); FreeADsMem(pAttrs); return HRESULT_FROM_WIN32(GetLastError()); } FreeADsMem(pAttrs); // // Add ACE. First tell the DACL that there is enough room. // ACL_SIZE_INFORMATION asi; if (!GetAclInformation(pDacl, &asi, sizeof(asi), AclSizeInformation)) { TRACE(_T("GetAclInformation failed!\n")); return HRESULT_FROM_WIN32(GetLastError()); } if (asi.AclBytesFree < wSizeNeeded) { pDacl->AclSize += wSizeNeeded; } if (!AddAccessAllowedAce(pDacl, ACL_REVISION_DS, STANDARD_RIGHTS_ALL | ACTRL_DS_OPEN | ACTRL_DS_CREATE_CHILD | ACTRL_DS_DELETE_CHILD | ACTRL_DS_LIST | ACTRL_DS_SELF | ACTRL_DS_READ_PROP | ACTRL_DS_WRITE_PROP | ACTRL_DS_DELETE_TREE | ACTRL_DS_LIST_OBJECT, varSID.parray->pvData)) { TRACE(_T("AddAccessAllowedAce failed!\n")); return HRESULT_FROM_WIN32(GetLastError()); } // // Put the SD back together again (sort of like Humpty Dumpty)... // SECURITY_DESCRIPTOR_CONTROL sdc; DWORD dwRev; if (!GetSecurityDescriptorControl(pAbsSD, &sdc, &dwRev)) { TRACE(_T("GetSecurityDescriptorControl failed!\n")); return HRESULT_FROM_WIN32(GetLastError()); } SECURITY_DESCRIPTOR sd; if (!InitializeSecurityDescriptor(&sd, dwRev)) { TRACE(_T("InitializeSecurityDescriptor failed!\n")); return HRESULT_FROM_WIN32(GetLastError()); } if (!SetSecurityDescriptorOwner(&sd, pOwnerSid, sdc & SE_OWNER_DEFAULTED)) { TRACE(_T("SetSecurityDescriptorOwner failed!\n")); return HRESULT_FROM_WIN32(GetLastError()); } if (!SetSecurityDescriptorGroup(&sd, pPriGrpSid, sdc & SE_GROUP_DEFAULTED)) { TRACE(_T("SetSecurityDescriptorOwner failed!\n")); return HRESULT_FROM_WIN32(GetLastError()); } if (!SetSecurityDescriptorSacl(&sd, sdc & SE_SACL_PRESENT, pSacl, sdc & SE_SACL_DEFAULTED)) { TRACE(_T("SetSecurityDescriptorOwner failed!\n")); return HRESULT_FROM_WIN32(GetLastError()); } if (!SetSecurityDescriptorDacl(&sd, sdc & SE_DACL_PRESENT, pDacl, sdc & SE_DACL_DEFAULTED)) { TRACE(_T("SetSecurityDescriptorOwner failed!\n")); return HRESULT_FROM_WIN32(GetLastError()); } DWORD dwSDlen = GetSecurityDescriptorLength(&sd); CSmartBytePtr spNewSD(dwSDlen); if (!spNewSD) { TRACE(_T("SD allocation failed!\n")); return E_OUTOFMEMORY; } pNewSD = (PSECURITY_DESCRIPTOR)spNewSD; if (!MakeSelfRelativeSD(&sd, pNewSD, &dwSDlen)) { DWORD dwErr = GetLastError(); if (dwErr != ERROR_INSUFFICIENT_BUFFER) { TRACE(_T("MakeSelfRelativeSD failed, err: %d!\n"), dwErr); return HRESULT_FROM_WIN32(GetLastError()); } if (!spNewSD.ReAlloc(dwSDlen)) { TRACE(_T("Unable to re-alloc SD buffer!\n")); return E_OUTOFMEMORY; } if (!MakeSelfRelativeSD(&sd, pNewSD, &dwSDlen)) { TRACE(_T("MakeSelfRelativeSD failed, err: %d!\n"), GetLastError()); return HRESULT_FROM_WIN32(GetLastError()); } } dwSDlen = GetSecurityDescriptorLength(pNewSD); if (dwSDlen < SECURITY_DESCRIPTOR_MIN_LENGTH) { TRACE(_T("Bad computer security descriptor length!\n")); return HRESULT_FROM_WIN32(GetLastError()); } if (!IsValidSecurityDescriptor(pNewSD)) { TRACE(_T("IsValidSecurityDescriptor failed!\n")); return HRESULT_FROM_WIN32(GetLastError()); } // // Save the modified SD back to this object. // DWORD cModified; ADSVALUE ADsValueSecurityDesc = {ADSTYPE_NT_SECURITY_DESCRIPTOR, NULL}; ADS_ATTR_INFO AttrInfoSecurityDesc = {wzSecDescriptor, ADS_ATTR_UPDATE, ADSTYPE_NT_SECURITY_DESCRIPTOR, &ADsValueSecurityDesc, 1}; ADsValueSecurityDesc.SecurityDescriptor.dwLength = dwSDlen; ADsValueSecurityDesc.SecurityDescriptor.lpValue = (PBYTE)pNewSD; ADS_ATTR_INFO rgAttrs[1]; rgAttrs[0] = AttrInfoSecurityDesc; hr = pDirObj->SetObjectAttributes(rgAttrs, 1, &cModified); if (FAILED(hr)) { TRACE(_T("SetObjectAttributes on SecurityDescriptor failed!\n")); return hr; } return S_OK; } #endif // FRS_CREATE HRESULT HrCreateADsSubnet(INOUT CNewADsObjectCreateInfo * pNewADsObjectCreateInfo) { ASSERT(pNewADsObjectCreateInfo != NULL); ASSERT(0 == lstrcmp(L"subnet", pNewADsObjectCreateInfo->m_pszObjectClass)); CreateNewSubnetWizard wiz(pNewADsObjectCreateInfo); return wiz.DoModal(); } // Note that this assumes that the site is the "grandparent" of the server. // If it isn't, the wrong name will appear in the site field. HRESULT ExtractServerAndSiteName( IN LPWSTR pwszServerDN, OUT BSTR* pbstrServerName, OUT BSTR* pbstrSiteName ) { CPathCracker pathCracker; *pbstrServerName = NULL; *pbstrSiteName = NULL; if ( NULL == pwszServerDN || L'\0' == *pwszServerDN ) return S_OK; HRESULT hr = pathCracker.Set( pwszServerDN, ADS_SETTYPE_DN ); RETURN_IF_FAIL; hr = pathCracker.SetDisplayType( ADS_DISPLAY_VALUE_ONLY ); RETURN_IF_FAIL; hr = pathCracker.GetElement( 0, pbstrServerName ); RETURN_IF_FAIL; hr = pathCracker.GetElement( 2, pbstrSiteName ); RETURN_IF_FAIL; hr = pathCracker.SetDisplayType( ADS_DISPLAY_FULL ); RETURN_IF_FAIL; return S_OK; } HRESULT HrCreateADsServer(INOUT CNewADsObjectCreateInfo * pNewADsObjectCreateInfo) { HRESULT hr = S_OK; #ifdef SERVER_COMPUTER_REFERENCE CComBSTR sbstrComputerPath; CComBSTR sbstrX500DN; CComBSTR sbstrComputerRDN; CComBSTR sbstrTemp; CComVariant svarServerReference; CComPtr spIADsComputer; bool fSkipComputerModify = false; do { hr = DSPROP_PickComputer( pNewADsObjectCreateInfo->GetParentHwnd(), &sbstrComputerPath ); BREAK_ON_FAIL; // Allow user to quit if user hit Cancel if (hr == S_FALSE) { DWORD dwRetval = ReportMessageEx( pNewADsObjectCreateInfo->GetParentHwnd(), IDS_SKIP_SERVER_REFERENCE, MB_YESNO | MB_DEFBUTTON2 | MB_ICONWARNING ); if (IDYES != dwRetval) { hr = S_FALSE; break; } fSkipComputerModify=true; } else { // prepare to modify computer object /* // Since the dialog was in single-select mode and the user was able // to hit OK, there should be exactly one selection. BREAK_ON_TRUE(1 != pSelection->cItems); */ // retrieve the ADsPath to the selected computer // hr = pathCracker.Set(pSelection->aDsSelection[0].pwzADsPath, ADS_SETTYPE_FULL); hr = pathCracker.Set(sbstrComputerPath, ADS_SETTYPE_FULL); sbstrComputerPath.Empty(); BREAK_ON_FAIL; // if this is a GC: path, the server might have a read-only copy of // this object. Change the path to an LDAP: path and remove the server. hr = pathCracker.Retrieve(ADS_FORMAT_PROVIDER,&sbstrTemp); BREAK_ON_FAIL; long lnFormatType = ADS_FORMAT_WINDOWS; if ( lstrcmp(sbstrTemp, TEXT("LDAP")) ) { ASSERT( !lstrcmp(sbstrTemp, TEXT("GC")) ); #error CODEWORK this usage of ADS_SETTYPE_PROVIDER will no longer work! JonN 2/12/99 hr = pathCracker.Set(TEXT("LDAP"),ADS_SETTYPE_PROVIDER); BREAK_ON_FAIL; lnFormatType = ADS_FORMAT_WINDOWS_NO_SERVER; } sbstrTemp.Empty(); hr = pathCracker.Retrieve(lnFormatType,&sbstrComputerPath); BREAK_ON_FAIL; // We preserve the servername in case Computer Picker returns one. // Extract the name of the computer object and make that the default name // of the new server object hr = pathCracker.SetDisplayType(ADS_DISPLAY_VALUE_ONLY); BREAK_ON_FAIL; hr = pathCracker.GetElement(0,&sbstrComputerRDN); BREAK_ON_FAIL; BREAK_ON_TRUE( !sbstrComputerRDN || TEXT('\0') == *sbstrComputerRDN ); pNewADsObjectCreateInfo->m_strDefaultObjectName = sbstrComputerRDN; hr = pathCracker.SetDisplayType(ADS_DISPLAY_FULL); BREAK_ON_FAIL; // Now check whether the computer already references a server. // Note that we may be using a serverless path, ADSI will try to find a // replica on the same domain as the computer object. hr = DSAdminOpenObject(sbstrComputerPath, IID_IADs, (PVOID*)&spIADsComputer, FALSE /*bServer*/ ); // DSAdminOpenObject might fail if the initial path chosen by Computer Picker // is a GC: path. The code above would munge the GC: path to an LDAP: // serverless path, and ADSI might choose a replica which hasn't // replicated this object yet. if ( SUCCEEDED(hr) ) { hr = spIADsComputer->Get( L"serverReference", &svarServerReference ); } if ( E_ADS_PROPERTY_NOT_FOUND == hr ) { hr = S_OK; } else if ( FAILED(hr) ) { PVOID apv[1] = { (BSTR)sbstrComputerRDN }; DWORD dwRetval = ReportErrorEx( pNewADsObjectCreateInfo->GetParentHwnd(), IDS_12_SERVER_REFERENCE_FAILED, hr, MB_YESNO | MB_DEFBUTTON2 | MB_ICONWARNING, apv, 1 ); if (IDYES != dwRetval) { hr = S_FALSE; break; } fSkipComputerModify=TRUE; hr = S_OK; } else { if ( VT_BSTR == V_VT(&svarServerReference) && NULL != V_BSTR(&svarServerReference) ) { CComBSTR sbstrServerName; CComBSTR sbstrSiteName; hr = ExtractServerAndSiteName( V_BSTR(&svarServerReference), &sbstrServerName, &sbstrSiteName ); BREAK_ON_FAIL; PVOID apv[3]; apv[0] = (BSTR)sbstrComputerRDN; apv[1] = (BSTR)sbstrServerName; apv[2] = (BSTR)sbstrSiteName; DWORD dwRetval = ReportMessageEx( pNewADsObjectCreateInfo->GetParentHwnd(), IDS_123_COMPUTER_OBJECT_ALREADY_USED, MB_YESNO | MB_DEFBUTTON2 | MB_ICONWARNING, apv, 3 ); if (IDYES != dwRetval) { hr = S_FALSE; break; } } } } // prepare to modify computer object #endif // SERVER_COMPUTER_REFERENCE // This is the standard UI to create a simple object hr = HrCreateADsSimpleObject(pNewADsObjectCreateInfo); #ifdef SERVER_COMPUTER_REFERENCE if ( FAILED(hr) || S_FALSE == hr ) { break; } // If an error occurs after the server was successfully created, we use a // special error message. do { // false loop if (fSkipComputerModify) break; // CODEWORK also display a fancy message? // Get the path to the new Server object in X500 format hr = pNewADsObjectCreateInfo->PGetIADsPtr()->get_ADsPath(&sbstrTemp); BREAK_ON_FAIL; hr = pathCracker.Set(sbstrTemp,ADS_SETTYPE_FULL); BREAK_ON_FAIL; hr = pathCracker.SetDisplayType(ADS_DISPLAY_FULL); BREAK_ON_FAIL; hr = pathCracker.Retrieve(ADS_FORMAT_X500_DN,&sbstrX500DN); BREAK_ON_FAIL; // Set the computer object's serverReference attribute // to point to the new Server object svarServerReference = sbstrX500DN; hr = spIADsComputer->Put( L"serverReference", svarServerReference ); BREAK_ON_FAIL; hr = spIADsComputer->SetInfo(); BREAK_ON_FAIL; } while (false); // false loop if ( FAILED(hr) ) { // The server was created but the computer could not be updated CComBSTR sbstrServerName; CComBSTR sbstrSiteName; (void) ExtractServerAndSiteName( V_BSTR(&svarServerReference), &sbstrServerName, &sbstrSiteName ); PVOID apv[3]; apv[0] = (BSTR)sbstrComputerRDN; apv[1] = (BSTR)sbstrServerName; apv[2] = (BSTR)sbstrSiteName; (void) ReportErrorEx( pNewADsObjectCreateInfo->GetParentHwnd(), IDS_1234_SERVER_REFERENCE_ERROR, hr, MB_OK | MB_ICONEXCLAMATION, apv, 3 ); hr = S_OK; } } while (false); // false loop #endif // SERVER_COMPUTER_REFERENCE // cleanup return hr; } HRESULT HrCreateADsSite(INOUT CNewADsObjectCreateInfo * pNewADsObjectCreateInfo) { HRESULT hr = CreateNewSiteWizard(pNewADsObjectCreateInfo).DoModal(); if ( !SUCCEEDED(hr) || S_FALSE == hr ) return hr; // need to create sub objects IADs* pIADs = pNewADsObjectCreateInfo->PGetIADsPtr(); ASSERT(pIADs != NULL); IADsContainer* pIADsContainer = NULL; hr = pIADs->QueryInterface(IID_IADsContainer, (void**)&pIADsContainer); ASSERT(SUCCEEDED(hr)); if (FAILED(hr)) { ASSERT(FALSE); return S_OK; // should never happen } LPCWSTR lpszAttrString = L"cn="; hr = HrCreateFixedNameHelper(gsz_nTDSSiteSettings, lpszAttrString, pIADsContainer); ASSERT(SUCCEEDED(hr)); hr = HrCreateFixedNameHelper(gsz_serversContainer, lpszAttrString, pIADsContainer); ASSERT(SUCCEEDED(hr)); hr = HrCreateFixedNameHelper(gsz_licensingSiteSettings, lpszAttrString, pIADsContainer); ASSERT(SUCCEEDED(hr)); pIADsContainer->Release(); LPCWSTR pcszSiteName = pNewADsObjectCreateInfo->GetName(); static bool g_DisplayedWarning = false; if (!g_DisplayedWarning) { g_DisplayedWarning = true; (void) ReportMessageEx( pNewADsObjectCreateInfo->GetParentHwnd(), IDS_NEW_SITE_INFO, MB_OK | MB_ICONINFORMATION | MB_HELP, (PVOID*)(&pcszSiteName), 1, 0, L"sag_ADsite_checklist_2.htm" ); } return S_OK; } HRESULT HrCreateADsOrganizationalUnit(INOUT CNewADsObjectCreateInfo * pNewADsObjectCreateInfo) { ASSERT(pNewADsObjectCreateInfo != NULL); ASSERT(0 == lstrcmp(L"organizationalUnit", pNewADsObjectCreateInfo->m_pszObjectClass)); CCreateNewOUWizard wiz(pNewADsObjectCreateInfo); return wiz.DoModal(); } HRESULT HrCreateADsGroup(INOUT CNewADsObjectCreateInfo * pNewADsObjectCreateInfo) { ASSERT(pNewADsObjectCreateInfo != NULL); ASSERT(0 == lstrcmp(L"group", pNewADsObjectCreateInfo->m_pszObjectClass)); CCreateNewGroupWizard wiz(pNewADsObjectCreateInfo); return wiz.DoModal(); } HRESULT HrCreateADsContact(INOUT CNewADsObjectCreateInfo * pNewADsObjectCreateInfo) { ASSERT(pNewADsObjectCreateInfo != NULL); ASSERT(0 == lstrcmp(L"contact", pNewADsObjectCreateInfo->m_pszObjectClass)); CCreateNewContactWizard wiz(pNewADsObjectCreateInfo); return wiz.DoModal(); } ///////////////////////////////////////////////////////////////////// // HrCreateADsSimpleObject() // // Create a simple object which "cn" is the // only mandatory attribute. // // IMPLEMENTATION NOTES // Invoke a dialog asking for the cannonical name. // HRESULT HrCreateADsSimpleObject(INOUT CNewADsObjectCreateInfo * pNewADsObjectCreateInfo) { ASSERT(pNewADsObjectCreateInfo != NULL); CCreateNewObjectCnWizard wiz(pNewADsObjectCreateInfo); return wiz.DoModal(); } // HrCreateADsSimpleObject() ///////////////////////////////////////////////////////////////////// // HrCreateADsObjectGenericWizard() // // Create an object invoking a "Generic Create" wizard. // The wizard will have as many pages as the number of mandatory attributes. // // INTERFACE NOTES // This routine must have the same interface as PFn_HrCreateADsObject(). // // IMPLEMENTATION NOTES // The wizard will look into the Directory Schema and determine what are // the mandatory attributes. // // REMARKS // Although the wizard is the most versatile tool to create a new // object, it is the least user-friendly way of doing so. The wizard // has no understanding how the attributes relates. Therefore it // is suggested to provide your own HrCreateADs*() routine to provide // a friendlier dialog to the user. // HRESULT HrCreateADsObjectGenericWizard(INOUT CNewADsObjectCreateInfo * pNewADsObjectCreateInfo) { ASSERT(pNewADsObjectCreateInfo != NULL); // cannot have a Generic Wizard when running as standalone object ASSERT(!pNewADsObjectCreateInfo->IsStandaloneUI()); if (pNewADsObjectCreateInfo->IsStandaloneUI()) return E_INVALIDARG; CCreateNewObjectGenericWizard dlg; if (dlg.FDoModal(INOUT pNewADsObjectCreateInfo)) return S_OK; return S_FALSE; } // HrCreateADsObjectGenericWizard() ///////////////////////////////////////////////////////////////////// // HrCreateADsObjectOverride() // // handler for object creation using a replacement dialog HRESULT HrCreateADsObjectOverride(INOUT CNewADsObjectCreateInfo* pNewADsObjectCreateInfo) { BOOL bHandled = FALSE; HRESULT hr = E_INVALIDARG; if (!pNewADsObjectCreateInfo->IsStandaloneUI()) { // try to create the dialog creation handler (full UI replacement) // this functionality is not exposed by the standalone UI IDsAdminCreateObj* pCreateObj = NULL; hr = ::CoCreateInstance(pNewADsObjectCreateInfo->GetCreateInfo()->clsidWizardPrimaryPage, NULL, CLSCTX_INPROC_SERVER, IID_IDsAdminCreateObj, (void**)&pCreateObj); if (SUCCEEDED(hr)) { // try to initialize handler hr = pCreateObj->Initialize(pNewADsObjectCreateInfo->m_pIADsContainer, pNewADsObjectCreateInfo->GetCopyFromObject(), pNewADsObjectCreateInfo->m_pszObjectClass); if (SUCCEEDED(hr)) { // execute call for creation IADs* pADsObj = NULL; bHandled = TRUE; hr = pCreateObj->CreateModal(pNewADsObjectCreateInfo->GetParentHwnd(), &pADsObj); // can have S_OK, S_FALSE, and error if ((hr == S_OK) && pADsObj != NULL) { // hold to the returned, newly created object pNewADsObjectCreateInfo->SetIADsPtr(pADsObj); // it will addref pADsObj->Release(); } } pCreateObj->Release(); } } // not standalone UI // check if the dialog creation handler was properly called if (bHandled) return hr; // try to create a primary extension handler (partial UI replacement) CCreateNewObjectWizardBase wiz(pNewADsObjectCreateInfo); hr = wiz.InitPrimaryExtension(); if (SUCCEEDED(hr)) { bHandled = TRUE; hr = wiz.DoModal(); } // check if the dialog creation handler was properly called if (bHandled) return hr; // The handler failed, need to recover, trying our internal creation UI PFn_HrCreateADsObject pfnCreateObject = NULL; PVOID pVoid = NULL; // we try to find a better handler than the generic wizard // by looking in our table if (!FindHandlerFunction(pNewADsObjectCreateInfo->m_pszObjectClass, &pfnCreateObject, &pVoid)) { // failed any match if (pNewADsObjectCreateInfo->IsStandaloneUI()) { // cannot have generic wizard on standalone UI return E_INVALIDARG; } else { // set the default to point to the "Generic Create" wizard ReportErrorEx(pNewADsObjectCreateInfo->GetParentHwnd(), IDS_NO_CREATION_WIZARD, S_OK, MB_OK | MB_ICONWARNING, NULL, 0); pfnCreateObject = HrCreateADsObjectGenericWizard; } } pNewADsObjectCreateInfo->SetCreationParameter(pVoid); ASSERT(pfnCreateObject != NULL); // call the function handler as last resort return pfnCreateObject(pNewADsObjectCreateInfo); } // HrCreateADsObjectOverride()