WindowsXP-SP1/admin/snapin/dsadmin/copyobj.cpp
2020-09-30 16:53:49 +02:00

1474 lines
39 KiB
C++

//+-------------------------------------------------------------------------
//
// Microsoft Windows
//
// Copyright (C) Microsoft Corporation, 1999 - 1999
//
// File: copyobj.cpp
//
//--------------------------------------------------------------------------
/////////////////////////////////////////////////////////////////////
// copyobj.cpp
//
/////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "resource.h"
#include "util.h"
#include "dsutil.h"
#include "newobj.h"
#include "copyobj.h"
#include "dscmn.h" // CrackName()
#include "querysup.h"
// attributes for "intelligent" copy user
static const PWSTR g_szProfilePath = L"profilePath";
static const PWSTR g_szHomeDir = L"homeDirectory";
/////////////////////////////////////////////////////////////////////
// global functions
//+----------------------------------------------------------------------------
//
// Function: _GetDomainScope
//
// Synopsis: Returns the full LDAP DN of the domain of the given object.
//
//-----------------------------------------------------------------------------
HRESULT _GetDomainScope(IADs* pIADs, CString& szDomainLDAPPath)
{
// get the DN of the current object
// get the SID of the object
CComVariant varObjectDistinguishedName;
HRESULT hr = pIADs->Get(L"distinguishedName", &varObjectDistinguishedName);
if (FAILED(hr))
{
TRACE(L"pIADs->Get(distinguishedName,...) failed with hr = 0x%x\n", hr);
return hr;
}
TRACE(L"Retrieved distinguishedName = <%s>\n", varObjectDistinguishedName.bstrVal);
// obtain the FQDN of the domain the object is in
LPWSTR pwzDomainDN = NULL;
hr = CrackName(varObjectDistinguishedName.bstrVal, &pwzDomainDN, GET_FQDN_DOMAIN_NAME);
if (FAILED(hr))
{
TRACE(L"CrackName(%s) failed with hr = 0x%x\n", varObjectDistinguishedName.bstrVal, hr);
return hr;
}
TRACE(L"CrackName(%s) returned <%s>\n", varObjectDistinguishedName.bstrVal, pwzDomainDN);
// retrieve the server name the object is bound to
CString szServer;
hr = GetADSIServerName(OUT szServer, IN pIADs);
if (FAILED(hr))
{
TRACE(L"GetADSIServerName() failed with hr = 0x%x\n", hr);
return hr;
}
TRACE(L"GetADSIServerName() returned <%s>\n", (LPCWSTR)szServer);
// build the full LDAP path of the domain
CPathCracker pathCracker;
hr = pathCracker.SetDisplayType(ADS_DISPLAY_FULL);
hr = pathCracker.Set((LPWSTR)((LPCWSTR)szServer), ADS_SETTYPE_SERVER);
hr = pathCracker.Set(pwzDomainDN, ADS_SETTYPE_DN);
LocalFreeStringW(&pwzDomainDN);
CComBSTR bstrDomainPath;
hr = pathCracker.Retrieve(ADS_FORMAT_X500, &bstrDomainPath);
if (FAILED(hr))
{
TRACE(L"PathCracker() failed to build LDAP path. hr = 0x%x\n", hr);
return hr;
}
szDomainLDAPPath = bstrDomainPath;
TRACE(L"Object's domain is: <%s>\n", (LPCWSTR)szDomainLDAPPath);
return S_OK;
}
//+----------------------------------------------------------------------------
//
// Method: _ConvertRIDtoName
//
// Synopsis: Convert the RID to the object DN.
//
//-----------------------------------------------------------------------------
HRESULT _ConvertRIDtoName(IN LPCWSTR lpszDomainLDAPPath,
IN PSID pObjSID,
IN DWORD priGroupRID,
OUT CString& szGroupPath)
{
PWSTR g_wzADsPath = L"ADsPath";
if ((pObjSID == NULL) || (priGroupRID == 0))
{
return E_INVALIDARG;
}
HRESULT hr = S_OK;
UCHAR * psaCount, i;
PSID pSID = NULL;
PSID_IDENTIFIER_AUTHORITY psia;
DWORD rgRid[8];
psaCount = GetSidSubAuthorityCount(pObjSID);
if (psaCount == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
TRACE(L"GetSidSubAuthorityCount() failed, hr = 0x%x\n", hr);
return hr;
}
ASSERT(*psaCount <= 8);
if (*psaCount > 8)
{
return E_FAIL;
}
for (i = 0; i < (*psaCount - 1); i++)
{
PDWORD pRid = GetSidSubAuthority(pObjSID, (DWORD)i);
if (pRid == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
TRACE(L"GetSidSubAuthority() failed, hr = 0x%x\n", hr);
return hr;
}
rgRid[i] = *pRid;
}
rgRid[*psaCount - 1] = priGroupRID;
for (i = *psaCount; i < 8; i++)
{
rgRid[i] = 0;
}
psia = GetSidIdentifierAuthority(pObjSID);
if (psia == NULL)
{
hr = HRESULT_FROM_WIN32(GetLastError());
TRACE(L"GetSidIdentifierAuthority() failed, hr = 0x%x\n", hr);
return hr;
}
if (!AllocateAndInitializeSid(psia, *psaCount, rgRid[0], rgRid[1],
rgRid[2], rgRid[3], rgRid[4],
rgRid[5], rgRid[6], rgRid[7], &pSID))
{
hr = HRESULT_FROM_WIN32(GetLastError());
TRACE(L"AllocateAndInitializeSid() failed, hr = 0x%x\n", hr);
return hr;
}
PWSTR rgpwzAttrNames[] = {g_wzADsPath};
const WCHAR wzSearchFormat[] = L"(&(objectCategory=group)(objectSid=%s))";
PWSTR pwzSID;
CString strSearchFilter;
hr = ADsEncodeBinaryData((PBYTE)pSID, GetLengthSid(pSID), &pwzSID);
if (FAILED(hr))
{
TRACE(L"ADsEncodeBinaryData() failed, hr = 0x%x\n", hr);
return hr;
}
strSearchFilter.Format(wzSearchFormat, pwzSID);
FreeADsMem(pwzSID);
CDSSearch Search;
hr = Search.Init(lpszDomainLDAPPath);
if (FAILED(hr))
{
TRACE(L"Search.Init(%s) failed, hr = 0x%x\n", lpszDomainLDAPPath, hr);
return hr;
}
Search.SetFilterString(const_cast<LPWSTR>((LPCTSTR)strSearchFilter));
Search.SetAttributeList(rgpwzAttrNames, 1);
Search.SetSearchScope(ADS_SCOPE_SUBTREE);
hr = Search.DoQuery();
if (FAILED(hr))
{
TRACE(L"Search.DoQuery() failed, hr = 0x%x\n", hr);
return hr;
}
hr = Search.GetNextRow();
if (hr == S_ADS_NOMORE_ROWS)
{
hr = S_OK;
szGroupPath = L"";
TRACE(L"Search. returned S_ADS_NOMORE_ROWS, we failed to find the primary group object, hr = 0x%x\n", hr);
return hr;
}
if (FAILED(hr))
{
TRACE(L"Search.GetNextRow() failed, hr = 0x%x\n", hr);
return hr;
}
ADS_SEARCH_COLUMN Column;
hr = Search.GetColumn(g_wzADsPath, &Column);
if (FAILED(hr))
{
TRACE(L"Search.GetColumn(%s) failed, hr = 0x%x\n", g_wzADsPath, hr);
return hr;
}
szGroupPath = Column.pADsValues->CaseIgnoreString;
Search.FreeColumn(&Column);
return hr;
}
/////////////////////////////////////////////////////////////////////
// CCopyableAttributesHolder
HRESULT CCopyableAttributesHolder::LoadFromSchema(MyBasePathsInfo* pBasePathsInfo)
{
TRACE(L"CCopyableAttributesHolder::LoadFromSchema()\n");
// build the LDAP path for the schema class
LPCWSTR lpszPhysicalSchemaNamingContext = pBasePathsInfo->GetSchemaNamingContext();
CString szPhysicalSchemaPath;
pBasePathsInfo->ComposeADsIPath(szPhysicalSchemaPath,lpszPhysicalSchemaNamingContext);
CDSSearch search;
HRESULT hr = search.Init(szPhysicalSchemaPath);
if (FAILED(hr))
{
TRACE(L"search.Init(%s) failed with hr = 0x%x\n", (LPCWSTR)szPhysicalSchemaPath, hr);
return hr;
}
// the query string filters out the attribute classes that have the
// "searchFlags" attribute with the 5th bit set (16 == 2^4)
static LPCWSTR lpszFilterFormat =
L"(&(objectCategory=CN=Attribute-Schema,%s)(searchFlags:1.2.840.113556.1.4.803:=16))";
int nFmtLen = lstrlen(lpszFilterFormat);
int nSchemaContextLen = lstrlen(lpszPhysicalSchemaNamingContext);
WCHAR* pszFilter = new WCHAR[nFmtLen+nSchemaContextLen+1];
if (!pszFilter)
{
TRACE(L"Could not allocate enough space for filter string\n");
return E_OUTOFMEMORY;
}
wsprintf(pszFilter, lpszFilterFormat, lpszPhysicalSchemaNamingContext);
static const int cAttrs = 1;
static LPCWSTR pszAttribsArr[cAttrs] =
{
L"lDAPDisplayName", // e.g. "accountExpires"
};
search.SetFilterString(pszFilter);
search.SetSearchScope(ADS_SCOPE_ONELEVEL);
search.SetAttributeList((LPWSTR*)pszAttribsArr, cAttrs);
hr = search.DoQuery();
if (FAILED(hr))
{
TRACE(L"search.DoQuery() failed with hr = 0x%x\n", hr);
return hr;
}
TRACE(L"\n*** Query Results BEGIN ***\n\n");
ADS_SEARCH_COLUMN Column;
hr = search.GetNextRow();
while (hr != S_ADS_NOMORE_ROWS)
{
if (FAILED(hr))
{
continue;
}
HRESULT hr0 = search.GetColumn((LPWSTR)pszAttribsArr[0], &Column);
if (FAILED(hr0))
{
continue;
}
LPCWSTR lpszAttr = Column.pADsValues->CaseIgnoreString;
TRACE(L"Attribute = %s", lpszAttr);
// screen against attributes we want to skip anyway
if (!_FindInNotCopyableHardwiredList(lpszAttr))
{
TRACE(L" can be copied");
m_attributeNameList.AddTail(lpszAttr);
}
TRACE(L"\n");
search.FreeColumn(&Column);
hr = search.GetNextRow();
} // while
TRACE(L"\n*** Query Results END ***\n\n");
if (pszFilter)
{
delete[] pszFilter;
pszFilter = 0;
}
return hr;
}
BOOL CCopyableAttributesHolder::CanCopy(LPCWSTR lpszAttributeName)
{
for (POSITION pos = m_attributeNameList.GetHeadPosition(); pos != NULL; )
{
CString& strRef = m_attributeNameList.GetNext(pos);
if (_wcsicmp(lpszAttributeName, strRef) == 0)
return TRUE;
}
return FALSE;
}
// function to screen out attributes that will not
// be copied (or not copied in bulk) no matter what the
// schema setting are
BOOL CCopyableAttributesHolder::_FindInNotCopyableHardwiredList(LPCWSTR lpszAttributeName)
{
static LPCWSTR _lpszNoCopyArr[] =
{
// we skip the toxic waste dump, no matter what
L"userParameters",
// userAccountControl handled separately after commit
L"userAccountControl",
// group membership (to be handled after commit)
L"primaryGroupID",
L"memberOf",
NULL // end of table marker
};
for (int k = 0; _lpszNoCopyArr[k] != NULL; k++)
{
if (_wcsicmp(lpszAttributeName, _lpszNoCopyArr[k]) == 0)
return TRUE;
}
return FALSE;
}
/////////////////////////////////////////////////////////////////////
// CCopyObjectHandlerBase
/////////////////////////////////////////////////////////////////////
// CSid
HRESULT CSid::Init(IADs* pIADs)
{
static LPWSTR g_wzObjectSID = L"objectSID";
CComPtr<IDirectoryObject> spDirObj;
HRESULT hr = pIADs->QueryInterface(IID_IDirectoryObject, (void**)&spDirObj);
if (FAILED(hr))
{
return hr;
}
PWSTR rgpwzAttrNames[] = {g_wzObjectSID};
DWORD cAttrs = 1;
Smart_PADS_ATTR_INFO spAttrs;
hr = spDirObj->GetObjectAttributes(rgpwzAttrNames, cAttrs, &spAttrs, &cAttrs);
if (FAILED(hr))
{
return hr;
}
if (_wcsicmp(spAttrs[0].pszAttrName, g_wzObjectSID) != 0)
{
return E_FAIL;
}
if (!Init(spAttrs[0].pADsValues->OctetString.dwLength,
spAttrs[0].pADsValues->OctetString.lpValue))
{
return E_OUTOFMEMORY;
}
return S_OK;
}
/////////////////////////////////////////////////////////////////////
// CGroupMembershipHolder
HRESULT CGroupMembershipHolder::Read(IADs* pIADs)
{
if (pIADs == NULL)
return E_INVALIDARG;
// hang on to the ADSI pointer
m_spIADs = pIADs;
// get the SID of the object
HRESULT hr = m_objectSid.Init(m_spIADs);
if (FAILED(hr))
{
TRACE(L"Failed to retrieve object SID, hr = 0x%x\n", hr);
return hr;
}
// get the info about the domain we are in
hr = _GetDomainScope(m_spIADs, m_szDomainLDAPPath);
if (FAILED(hr))
{
TRACE(L"_GetDomainScope() failed, hr = 0x%x\n", hr);
return hr;
}
hr = _ReadPrimaryGroupInfo();
if (FAILED(hr))
{
TRACE(L"_ReadPrimaryGroupInfo() failed, hr = 0x%x\n", hr);
return hr;
}
hr = _ReadNonPrimaryGroupInfo();
if (FAILED(hr))
{
TRACE(L"_ReadNonPrimaryGroupInfo() failed, hr = 0x%x\n", hr);
return hr;
}
#ifdef DBG
m_entryList.Trace(L"Group Membership List:");
#endif // DBG
return hr;
}
HRESULT CGroupMembershipHolder::CopyFrom(CGroupMembershipHolder* pSource)
{
// Add all the elements that are in the source
// but not yet in the destination
CGroupMembershipEntryList* pSourceList = &(pSource->m_entryList);
//
// Copy the state of the primary group
//
m_bPrimaryGroupFound = pSource->m_bPrimaryGroupFound;
CGroupMembershipEntryList additionsEntryList;
for (POSITION pos = pSourceList->GetHeadPosition(); pos != NULL; )
{
CGroupMembershipEntry* pCurrSourceEntry = pSourceList->GetNext(pos);
// look if the the source item is already in the current list
CGroupMembershipEntry* pEntry = m_entryList.FindByDN(pCurrSourceEntry->GetDN());
if (pEntry == NULL)
{
if (_wcsicmp(pCurrSourceEntry->GetDN(), L"") != 0)
{
// not found in the entry list, need to add
pEntry = new CGroupMembershipEntry(pCurrSourceEntry->GetRID(), pCurrSourceEntry->GetDN());
pEntry->MarkAdd();
additionsEntryList.AddTail(pEntry);
TRACE(L"add: RID %d, DN = <%s>\n", pEntry->GetRID(), pEntry->GetDN());
}
}
} // for
// find all the elements that are in the destination
// but not in the source (slated for deletion)
for (pos = m_entryList.GetHeadPosition(); pos != NULL; )
{
CGroupMembershipEntry* pCurrDestEntry = m_entryList.GetNext(pos);
// look if the the source item is in the source list
CGroupMembershipEntry* pEntry = pSourceList->FindByDN(pCurrDestEntry->GetDN());
if (pEntry == NULL)
{
// not found in the entry list, need to mark for deletion
pCurrDestEntry->MarkRemove();
TRACE(L"remove: RID %d, DN = <%s>\n", pCurrDestEntry->GetRID(), pCurrDestEntry->GetDN());
}
} // for
// catenate the list of additions to the entry list
m_entryList.Merge(&additionsEntryList);
return S_OK;
}
HRESULT _EditGroupMembership(LPCWSTR lpszServer, LPCWSTR lpszUserPath, LPCWSTR lpszGroupDN, BOOL bAdd)
{
// build the full LDAP path of the domain
CPathCracker pathCracker;
HRESULT hr = pathCracker.Set((LPWSTR)lpszServer, ADS_SETTYPE_SERVER);
hr = pathCracker.Set((LPWSTR)lpszGroupDN, ADS_SETTYPE_DN);
CComBSTR bstrGroupPath;
hr = pathCracker.Retrieve(ADS_FORMAT_X500, &bstrGroupPath);
if (FAILED(hr))
{
TRACE(L"PathCracker() failed to build LDAP path. hr = 0x%x\n", hr);
return hr;
}
CComPtr<IADs> spIADs;
hr = DSAdminOpenObject(bstrGroupPath,
IID_IADs,
(void **)&spIADs,
TRUE /*bServer*/);
if (FAILED(hr))
{
TRACE(L"DSAdminOpenObject(%s) on group failed, hr = 0x%x\n", bstrGroupPath, hr);
return hr;
}
hr = spIADs->GetInfo();
CComPtr<IADsGroup> spIADsGroup;
hr = spIADs->QueryInterface(IID_IADsGroup, (void**)&spIADsGroup);
if (bAdd)
{
hr = spIADsGroup->Add((LPWSTR)lpszUserPath);
if (FAILED(hr))
{
TRACE(L"spIADsGroup->Add(%s) on group failed, hr = 0x%x\n", lpszUserPath, hr);
return hr;
}
}
else
{
hr = spIADsGroup->Remove((LPWSTR)lpszUserPath);
if (FAILED(hr))
{
TRACE(L"spIADsGroup->Remove(%s) on group failed, hr = 0x%x\n", lpszUserPath, hr);
return hr;
}
}
hr = spIADsGroup->SetInfo();
if (FAILED(hr))
{
TRACE(L"spIADsGroup->SetInfo() on group failed, hr = 0x%x\n", hr);
return hr;
}
return hr;
}
HRESULT CGroupMembershipHolder::Write()
{
TRACE(L"CGroupMembershipHolder::Write()\n");
#ifdef DBG
m_entryList.Trace(L"Group Membership List:");
#endif // DBG
// get the path of the user object
CComBSTR bstrObjPath;
HRESULT hr = m_spIADs->get_ADsPath(&bstrObjPath);
if (FAILED(hr))
{
TRACE(L"m_spIADs->get_ADsPath() failed with hr = 0x%x\n", hr);
return hr;
}
TRACE(L"bstrPath = %s\n", (LPCWSTR)bstrObjPath);
// retrieve the server name the object is bound to
CString szServer;
hr = GetADSIServerName(OUT szServer, IN m_spIADs);
if (FAILED(hr))
{
TRACE(L"GetADSIServerName() failed with hr = 0x%x\n", hr);
return hr;
}
// first do all the additions
// also remember if we added a primary group
CGroupMembershipEntry* pNewPrimaryGroupEntry = NULL;
TRACE(L"\nfirst do all the additions\n\n");
for (POSITION pos = m_entryList.GetHeadPosition(); pos != NULL; )
{
CGroupMembershipEntry* pCurrEntry = m_entryList.GetNext(pos);
if (pCurrEntry->GetActionType() == CGroupMembershipEntry::add)
{
TRACE(L"add: RID %d, DN = <%s>\n", pCurrEntry->GetRID(), pCurrEntry->GetDN());
pCurrEntry->m_hr = _EditGroupMembership(szServer, bstrObjPath, pCurrEntry->GetDN(), TRUE /*bAdd*/);
if (SUCCEEDED(pCurrEntry->m_hr) && (pCurrEntry->IsPrimaryGroup()))
{
ASSERT(pNewPrimaryGroupEntry == NULL);
pNewPrimaryGroupEntry = pCurrEntry;
}
}
} // for
if (m_bPrimaryGroupFound)
{
// second, do the primary group change
TRACE(L"\ndo the primary group change\n\n");
if (pNewPrimaryGroupEntry != NULL)
{
TRACE(L"new primary: RID %d, DN = <%s>\n",
pNewPrimaryGroupEntry->GetRID(), pNewPrimaryGroupEntry->GetDN());
CComVariant varPrimaryGroupID;
varPrimaryGroupID.vt = VT_I4;
varPrimaryGroupID.lVal = pNewPrimaryGroupEntry->GetRID();
hr = m_spIADs->Put(L"primaryGroupID", varPrimaryGroupID);
if (FAILED(hr))
{
TRACE(L"m_spIADs->Put(primaryGroupID) failed with hr = 0x%x\n", hr);
return hr;
}
hr = m_spIADs->SetInfo();
if (FAILED(hr))
{
TRACE(L"m_spIADs->SetInfo() failed with hr = 0x%x\n", hr);
return hr;
}
}
}
// finally do the deletes
TRACE(L"\ndo the deletes\n\n");
for (pos = m_entryList.GetHeadPosition(); pos != NULL; )
{
CGroupMembershipEntry* pCurrEntry = m_entryList.GetNext(pos);
if (pCurrEntry->GetActionType() == CGroupMembershipEntry::remove)
{
TRACE(L"remove: RID %d, DN = <%s>\n", pCurrEntry->GetRID(), pCurrEntry->GetDN());
pCurrEntry->m_hr = _EditGroupMembership(szServer, bstrObjPath, pCurrEntry->GetDN(), FALSE /*bAdd*/);
}
} // for
return S_OK;
}
void CGroupMembershipHolder::ProcessFailures(HRESULT& hr, CString& szFailureString, BOOL* pPrimaryGroupFound)
{
// reset variables
hr = S_OK;
szFailureString.Empty();
BOOL bFirstOne = TRUE;
BOOL bGotAccessDenied = FALSE;
*pPrimaryGroupFound = m_bPrimaryGroupFound;
// compose the best error code. If one code is access denied,
// return it. If none is access denied, use the first error code
for (POSITION pos = m_entryList.GetHeadPosition(); pos != NULL; )
{
CGroupMembershipEntry* pCurrEntry = m_entryList.GetNext(pos);
if (FAILED(pCurrEntry->m_hr))
{
if (pCurrEntry->m_hr == E_ACCESSDENIED)
{
bGotAccessDenied = TRUE;
}
if (bFirstOne)
{
bFirstOne = FALSE;
hr = pCurrEntry->m_hr;
}
else
{
szFailureString += L"\n";
}
LPWSTR pszCanonical = NULL;
HRESULT hrCanonical =
CrackName((LPWSTR)pCurrEntry->GetDN(), &pszCanonical, GET_OBJ_CAN_NAME, NULL);
if ((S_OK == hrCanonical) && (pszCanonical != NULL))
{
szFailureString += pszCanonical;
LocalFreeStringW(&pszCanonical);
}
else
{
szFailureString += pCurrEntry->GetDN();
}
}
} // for
if (bGotAccessDenied)
{
// override any error we have
hr = E_ACCESSDENIED;
}
}
HRESULT CGroupMembershipHolder::_ReadPrimaryGroupInfo()
{
// from the object SID and the primary group RID, get the full
// primary group info
// read the RID for the primary group
CComVariant varPrimaryGroupID;
HRESULT hr = m_spIADs->Get(L"primaryGroupID", &varPrimaryGroupID);
if (FAILED(hr))
{
TRACE(L"m_spIADs->Get(primaryGroupID, ...) failed, hr = 0x%x\n", hr);
return hr;
}
ASSERT(varPrimaryGroupID.vt == VT_I4);
TRACE(L"primaryGroupID = %d\n", varPrimaryGroupID.lVal);
// now need to map it into actual group information
CString szGroupPath;
PSID pObjSID = m_objectSid.GetSid();
DWORD priGroupRID = varPrimaryGroupID.lVal;
hr = _ConvertRIDtoName(IN m_szDomainLDAPPath,
IN pObjSID,
IN priGroupRID,
OUT szGroupPath);
if (FAILED(hr))
{
TRACE(L"_ConvertRIDtoName() failed, hr = 0x%x\n", hr);
return hr;
}
CComBSTR bstrGroupDN;
if (szGroupPath != L"")
{
m_bPrimaryGroupFound = TRUE;
// from the LDAP path, retrieve the DN
CPathCracker pathCracker;
hr = pathCracker.Set((LPWSTR)(LPCWSTR)szGroupPath, ADS_SETTYPE_FULL);
hr = pathCracker.Retrieve(ADS_FORMAT_X500_DN, &bstrGroupDN);
if (FAILED(hr))
{
TRACE(L"PathCracker() failed to build primary group DN path. hr = 0x%x\n", hr);
return hr;
}
}
else
{
bstrGroupDN = szGroupPath;
}
// create a new entry
CGroupMembershipEntry* pEntry = new CGroupMembershipEntry(priGroupRID, bstrGroupDN);
m_entryList.AddTail(pEntry);
TRACE(L"CGroupMembershipEntry(%d,%s) added to list\n", priGroupRID, bstrGroupDN);
return hr;
}
HRESULT CGroupMembershipHolder::_ReadNonPrimaryGroupInfo()
{
// copy group membership
CComVariant varMemberOf;
HRESULT hr = m_spIADs->Get(L"memberOf", &varMemberOf);
if (hr == E_ADS_PROPERTY_NOT_FOUND)
{
TRACE(L"_ReadNonPrimaryGroupInfo(): memberOf not set\n");
return S_OK;
}
if (FAILED(hr))
{
return hr;
}
CStringList groupList;
hr = HrVariantToStringList(IN varMemberOf, groupList);
if (FAILED(hr))
{
TRACE(L"HrVariantToStringList() failed with hr = 0x%x\n", hr);
return hr;
}
CComBSTR bstrPath;
hr = m_spIADs->get_ADsPath(&bstrPath);
if (FAILED(hr))
{
TRACE(L"m_spIADs->get_ADsPath() failed with hr = 0x%x\n", hr);
return hr;
}
TRACE(L"bstrPath = %s\n", (LPCWSTR)bstrPath);
for (POSITION pos = groupList.GetHeadPosition(); pos != NULL; )
{
CString szGroupDN = groupList.GetNext(pos);
TRACE(_T("szGroupDN: %s\n"), (LPCWSTR)szGroupDN);
CGroupMembershipEntry* pEntry = new CGroupMembershipEntry(0x0, szGroupDN);
m_entryList.AddTail(pEntry);
}
return hr;
}
/////////////////////////////////////////////////////////////////////
// CCopyUserHandler
HRESULT CCopyUserHandler::Init(MyBasePathsInfo* pBasePathsInfo, IADs* pIADsCopyFrom)
{
HRESULT hr = CCopyObjectHandlerBase::Init(pBasePathsInfo, pIADsCopyFrom);
if (FAILED(hr))
{
return hr;
}
// read list of copyable attributes form the schema
hr = m_copyableAttributesHolder.LoadFromSchema(pBasePathsInfo);
if (FAILED(hr))
{
return hr;
}
// read group membership information
hr = m_sourceMembershipHolder.Read(m_spIADsCopyFrom);
if (FAILED(hr))
{
return hr;
}
hr = _ReadPasswordCannotChange();
if (FAILED(hr))
{
return hr;
}
hr = _ReadPasswordMustChange();
return hr;
}
HRESULT CCopyUserHandler::Copy(IADs* pIADsCopyTo, BOOL bPostCommit,
HWND hWnd, LPCWSTR lpszObjectName)
{
HRESULT hr = S_OK;
if (bPostCommit)
{
hr = _CopyGroupMembership(pIADsCopyTo);
if (SUCCEEDED(hr))
{
// might have failed to add to some group(s)
_ShowGroupMembershipWarnings(hWnd, lpszObjectName);
}
if (FAILED(hr))
{
// something went really wrong, need to bail out
return hr;
}
if (m_bNeedToCreateHomeDir)
{
// it might fail under unforeseen circumstances
hr = _CreateHomeDirectory(pIADsCopyTo, lpszObjectName, hWnd);
}
}
else
{
hr = _UpdatePaths(pIADsCopyTo);
}
return hr;
}
HRESULT CCopyUserHandler::_ReadPasswordCannotChange()
{
CChangePasswordPrivilegeAction ChangePasswordPrivilegeAction;
HRESULT hr = ChangePasswordPrivilegeAction.Load(GetCopyFrom());
if (FAILED(hr))
{
TRACE(L"ChangePasswordPrivilegeAction.Load() failed with hr = 0x%x\n", hr);
return hr;
}
hr = ChangePasswordPrivilegeAction.Read(&m_bPasswordCannotChange);
if (FAILED(hr))
{
TRACE(L"ChangePasswordPrivilegeAction.Read() failed with hr = 0x%x\n", hr);
return hr;
}
return S_OK;
}
HRESULT CCopyUserHandler::_ReadPasswordMustChange()
{
CComPtr<IDirectoryObject> spDirObj;
HRESULT hr = GetCopyFrom()->QueryInterface(IID_IDirectoryObject, (void**)&spDirObj);
if (FAILED(hr))
{
return hr;
}
PWSTR rgpwzAttrNames[] = {L"pwdLastSet"};
DWORD cAttrs = 1;
Smart_PADS_ATTR_INFO spAttrs;
hr = spDirObj->GetObjectAttributes(rgpwzAttrNames, cAttrs, &spAttrs, &cAttrs);
if (FAILED(hr))
{
return hr;
}
if ( (_wcsicmp(spAttrs[0].pszAttrName, L"pwdLastSet") != 0) ||
(spAttrs[0].dwADsType != ADSTYPE_LARGE_INTEGER) )
{
return E_FAIL;
}
m_bPasswordMustChange = (spAttrs[0].pADsValues->LargeInteger.QuadPart == 0);
return S_OK;
}
HRESULT CCopyUserHandler::_CopyAttributes(IADs* pIADsCopyTo)
{
ASSERT(pIADsCopyTo != NULL);
ASSERT(m_spIADsCopyFrom != NULL);
HRESULT hr = S_OK;
CComPtr<IADsPropertyList> spIADsPropertyList;
// get a property list interface from the object we want to copy from
hr = m_spIADsCopyFrom->QueryInterface(IID_IADsPropertyList, (void**)&spIADsPropertyList);
if (FAILED(hr))
{
return hr;
}
// ignore the return value and try to continue even if it didn't reset
hr = spIADsPropertyList->Reset();
// loop thru the list of set attributes
CComVariant varProperty;
do
{
hr = spIADsPropertyList->Next(&varProperty);
if (SUCCEEDED(hr))
{
ASSERT(varProperty.vt == VT_DISPATCH);
if (varProperty.pdispVal != NULL)
{
CComPtr<IADsPropertyEntry> spIADsPropertyEntry;
hr = (varProperty.pdispVal)->QueryInterface(IID_IADsPropertyEntry, (void**)&spIADsPropertyEntry);
if (SUCCEEDED(hr))
{
CComBSTR bstrName;
hr = spIADsPropertyEntry->get_Name(&bstrName);
if (SUCCEEDED(hr))
{
TRACE(L" Property Name = <%s>", bstrName);
if (m_copyableAttributesHolder.CanCopy(bstrName))
{
TRACE(L" Can copy: ");
CComVariant varData;
hr = m_spIADsCopyFrom->Get(bstrName, &varData);
if (SUCCEEDED(hr))
{
HRESULT hr1 = pIADsCopyTo->Put(bstrName, varData);
if (SUCCEEDED(hr1))
{
TRACE(L"Added");
}
else
{
TRACE(L"Failed: 0x%x", hr1);
}
}
}
TRACE(L"\n");
}
}
}
}
varProperty.Clear();
}
while (hr == S_OK);
return S_OK;
}
// Given an IADs* of an object, retrieves a string attrubute
// in a variant
HRESULT _GetStringAttribute(IN IADs* pIADs, IN LPCWSTR lpszAttribute, OUT CComVariant& var)
{
TRACE(L"_GetStringAttribute(_, %s, _)\n", lpszAttribute);
HRESULT hr = pIADs->Get((LPWSTR)lpszAttribute, &var);
if (FAILED(hr))
{
TRACE(L"_GetStringAttribute(): pIADs->Get() failed with hr = 0x%x\n", hr);
return hr;
}
if (var.vt != VT_BSTR)
{
TRACE(L"_GetStringAttribute(): failed because var.vt != VT_BSTR\n");
return E_INVALIDARG;
}
return S_OK;
}
BOOL _ChangePathUsingSAMAccountName(IN LPCWSTR lpszSAMAccountNameSource,
IN LPCWSTR lpszSAMAccountDestination,
INOUT CComVariant& varValPath)
{
// NOTICE: we do the substitution only if we find
// something like:
// \\myhost\myshare\JoeB
// i.e. the last token in the path is the source SAM account name
// we change into \\myhost\myshare\FrankM
TRACE(L"_ChangePathUsingSAMAccountName(%s, %s, _)\n",
lpszSAMAccountNameSource, lpszSAMAccountDestination);
ASSERT(lpszSAMAccountNameSource != NULL);
ASSERT(lpszSAMAccountDestination != NULL);
// invalid string
if ( (varValPath.vt != VT_BSTR) || (varValPath.bstrVal == NULL))
{
TRACE(L"returning FALSE, varValPath of wrong type or NULL\n");
return FALSE;
}
CString szSourcePath = varValPath.bstrVal;
TRACE(L"Input value for varValPath.bstrVal = %s\n", varValPath.bstrVal);
// look for a \ as a separator
int iLastSlash = szSourcePath.ReverseFind(L'\\');
if (iLastSlash == -1)
{
//
// No slashes were found
//
TRACE(L"returning FALSE, could not find the \\ at the end of the string\n");
return FALSE;
}
CString szSAMName = szSourcePath.Right(szSourcePath.GetLength() - iLastSlash - 1);
ASSERT(!szSAMName.IsEmpty());
// compare what is after the \ with the source SAM account name
if (szSAMName.CompareNoCase(lpszSAMAccountNameSource) != 0)
{
TRACE(L"returning FALSE, lpszLeaf = %s does not match source SAM account name\n", szSAMName);
return FALSE;
}
CString szBasePath = szSourcePath.Left(iLastSlash + 1);
CString szNewPath = szBasePath + lpszSAMAccountDestination;
// replace old value in variant
::SysFreeString(varValPath.bstrVal);
varValPath.bstrVal = ::SysAllocString(szNewPath);
TRACE(L"returning TRUE, new varValPath.bstrVal = %s\n", varValPath.bstrVal);
return TRUE; // we did a replacement
}
HRESULT _UpdatePathAttribute(IN LPCWSTR lpszAttributeName,
IN LPCWSTR lpszSAMAccountNameSource,
IN LPCWSTR lpszSAMAccountDestination,
IN IADs* pIADsCopySource,
IN IADs* pIADsCopyTo,
OUT BOOL* pbDirChanged)
{
TRACE(L"_UpdatePathAttribute(%s, %s, %s, _, _, _)\n",
lpszAttributeName, lpszSAMAccountNameSource, lpszSAMAccountDestination);
*pbDirChanged = FALSE;
// get the value of the source attribute
CComVariant varVal;
HRESULT hr = pIADsCopySource->Get((LPWSTR)lpszAttributeName, &varVal);
// if attribute not set, nothing to do
if (hr == E_ADS_PROPERTY_NOT_FOUND)
{
TRACE(L"attribute not set, just returning\n");
return E_ADS_PROPERTY_NOT_FOUND;
}
// handle other unexpected failures
if (FAILED(hr))
{
TRACE(L"pIADsCopySource->Get(%s,_) failed with hr = 0x%x\n", lpszAttributeName, hr);
return hr;
}
if (varVal.vt == VT_EMPTY)
{
TRACE(L"just returning because varVal.vt == VT_EMPTY\n");
return E_ADS_PROPERTY_NOT_FOUND;
}
if (varVal.vt != VT_BSTR)
{
TRACE(L"failed because var.vt != VT_BSTR\n");
return E_INVALIDARG;
}
// synthesize the new value of the path, if appropriate
if (_ChangePathUsingSAMAccountName(lpszSAMAccountNameSource, lpszSAMAccountDestination, varVal))
{
// the path got updated, need to update the destination object
hr = pIADsCopyTo->Put((LPWSTR)lpszAttributeName, varVal);
TRACE(L"pIADsCopyTo->Put(%s,_) returned hr = 0x%x\n", lpszAttributeName, hr);
if (SUCCEEDED(hr))
{
*pbDirChanged = TRUE;
}
}
TRACE(L"*pbDirChanged = %d\n", *pbDirChanged);
// we should fail only in really exceptional circumstances
ASSERT(SUCCEEDED(hr));
return hr;
}
HRESULT CCopyUserHandler::_UpdatePaths(IADs* pIADsCopyTo)
{
// NOTICE: we assume that, if the paths are copyable, they have been
// bulk copied when the temporary object was created.
// If the paths have to be adjusted, we overwrite the copy.
TRACE(L"CCopyUserHandler::_UpdatePaths()\n");
// reset the flag for post commit
m_bNeedToCreateHomeDir = FALSE;
BOOL bCopyHomeDir = m_copyableAttributesHolder.CanCopy(g_szHomeDir);
BOOL bCopyProfilePath = m_copyableAttributesHolder.CanCopy(g_szProfilePath);
TRACE(L"bCopyHomeDir = %d, bCopyProfilePath = %d\n", bCopyHomeDir, bCopyProfilePath);
if (!bCopyHomeDir && !bCopyProfilePath)
{
TRACE(L"no need to update anything, bail out\n");
return S_OK;
}
// retrieve the SAM account names of source and destination
// to synthesize the new paths
IADs* pIADsCopySource = GetCopyFrom();
CComVariant varSAMNameSource;
HRESULT hr = _GetStringAttribute(pIADsCopySource, gsz_samAccountName, varSAMNameSource);
if (FAILED(hr))
{
TRACE(L"_GetStringAttribute() failed on source SAM account name\n");
return hr;
}
CComVariant varSAMNameDestination;
hr = _GetStringAttribute(pIADsCopyTo, gsz_samAccountName, varSAMNameDestination);
if (FAILED(hr))
{
TRACE(L"_GetStringAttribute() failed on destination SAM account name\n");
return hr;
}
if (bCopyHomeDir)
{
BOOL bDummy;
hr = _UpdatePathAttribute(g_szHomeDir, varSAMNameSource.bstrVal,
varSAMNameDestination.bstrVal,
pIADsCopySource,
pIADsCopyTo,
&bDummy);
if (hr == E_ADS_PROPERTY_NOT_FOUND)
{
// not set, just clear the HRESULT
hr = S_OK;
}
else
{
// the home directory was set, verify it is a UNC path
CComVariant varDestinationHomeDir;
hr = _GetStringAttribute(pIADsCopyTo, g_szHomeDir, varDestinationHomeDir);
if (FAILED(hr))
{
TRACE(L"_GetStringAttribute() failed on homeDir hr = 0x%x\n", hr);
return hr;
}
m_bNeedToCreateHomeDir = DSPROP_IsValidUNCPath(varDestinationHomeDir.bstrVal);
TRACE(L"DSPROP_IsValidUNCPath(%s) returned = %d\n",
varDestinationHomeDir.bstrVal, m_bNeedToCreateHomeDir);
}
if (FAILED(hr))
{
TRACE(L"_UpdatePathAttribute() failed on homeDir hr = 0x%x\n", hr);
return hr;
}
}
if (bCopyProfilePath)
{
BOOL bDummy;
hr = _UpdatePathAttribute(g_szProfilePath, varSAMNameSource.bstrVal,
varSAMNameDestination.bstrVal,
pIADsCopySource,
pIADsCopyTo,
&bDummy);
if (hr == E_ADS_PROPERTY_NOT_FOUND)
{
// not set, just clear the HRESULT
hr = S_OK;
}
if (FAILED(hr))
{
TRACE(L"_UpdatePathAttribute() failed on profilePath hr = 0x%x\n", hr);
return hr;
}
}
// failure expected only under exceptional circumstances
return S_OK;
}
HRESULT CCopyUserHandler::_CreateHomeDirectory(IADs* pIADsCopyTo,
LPCWSTR lpszObjectName, HWND hWnd)
{
TRACE(L"CCopyUserHandler::_CreateHomeDirectory()\n");
ASSERT(m_bNeedToCreateHomeDir);
// retrieve the home directory attribute
CComVariant VarHomeDir;
HRESULT hr = pIADsCopyTo->Get(g_szHomeDir, &VarHomeDir);
if (FAILED(hr))
{
TRACE(L"pIADsCopyTo->Get(%s,_) failed, hr = 0x%x\n", g_szHomeDir, hr);
return hr;
}
if (VarHomeDir.vt != VT_BSTR)
{
TRACE(L"failing because varVal.vt != VT_BSTR\n");
return E_INVALIDARG;
}
// retrieve the SID of the newly created object
CSid destinationObjectSid;
hr = destinationObjectSid.Init(pIADsCopyTo);
if (FAILED(hr))
{
TRACE(L"destinationObjectSid.Init() failed, , hr = 0x%x\n", hr);
// unforeseen error
PVOID apv[1] = {(LPWSTR)(lpszObjectName) };
ReportErrorEx(hWnd, IDS_CANT_READ_HOME_DIR_SID, hr,
MB_OK | MB_ICONWARNING, apv, 1);
// we cannot proceed further, but we return success because
// we already displayed the error message and we want to treat the
// failure as a warning
return S_OK;
}
// make call to helper function to create directory and set ACL's on it
DWORD dwErr = ::DSPROP_CreateHomeDirectory(destinationObjectSid.GetSid(), VarHomeDir.bstrVal);
TRACE(L"DSPROP_CreateHomeDirectory(%s, pSid) returned dwErr = 0x%x\n", VarHomeDir.bstrVal, dwErr);
if (dwErr != 0)
{
// treat as a warning, display a message and continue
PVOID apv[1] = {VarHomeDir.bstrVal};
UINT nMsgID = 0;
switch (dwErr)
{
case ERROR_ALREADY_EXISTS:
nMsgID = IDS_HOME_DIR_EXISTS;
break;
case ERROR_PATH_NOT_FOUND:
nMsgID = IDS_HOME_DIR_CREATE_FAILED;
break;
case ERROR_LOGON_FAILURE:
case ERROR_NOT_AUTHENTICATED:
case ERROR_INVALID_PASSWORD:
case ERROR_PASSWORD_EXPIRED:
case ERROR_ACCOUNT_DISABLED:
case ERROR_ACCOUNT_LOCKED_OUT:
nMsgID = IDS_HOME_DIR_CREATE_NO_ACCESS;
break;
default:
nMsgID = IDS_HOME_DIR_CREATE_FAIL;
} // switch
HRESULT hrTemp = HRESULT_FROM_WIN32(dwErr);
ReportErrorEx(hWnd, nMsgID, hrTemp,
MB_OK|MB_ICONWARNING , apv, 1);
}
return S_OK;
}
HRESULT CCopyUserHandler::_CopyGroupMembership(IADs* pIADsCopyTo)
{
if (pIADsCopyTo == NULL)
return E_INVALIDARG;
HRESULT hr = pIADsCopyTo->GetInfo();
if (FAILED(hr))
{
TRACE(L"pIADsCopyTo->GetInfo() failed with hr = 0x%x\n", hr);
return hr;
}
CGroupMembershipHolder destinationMembership;
hr = destinationMembership.Read(pIADsCopyTo);
if (FAILED(hr))
{
TRACE(L"destinationMembership.Read(pIADsCopyTo) failed with hr = 0x%x\n", hr);
return hr;
}
hr = destinationMembership.CopyFrom(&m_sourceMembershipHolder);
if (FAILED(hr))
{
TRACE(L"destinationMembership.CopyFrom() failed with hr = 0x%x\n", hr);
return hr;
}
hr = destinationMembership.Write();
if (FAILED(hr))
{
// something unexpected failed and we are going to percolate
// it to the wizards for a generic warning message
TRACE(L"destinationMembership.Write() failed with hr = 0x%x\n", hr);
return hr;
}
// there can be failures related to acccess denied on some groups
// we handle them with a cumulative warning
destinationMembership.ProcessFailures(m_hrFailure, m_szFailureString, &m_bPrimaryGroupFound);
return S_OK;
}
void CCopyUserHandler::_ShowGroupMembershipWarnings(HWND hWnd, LPCWSTR lpszObjectName)
{
if (!m_bPrimaryGroupFound)
{
// Some message box
ReportMessageEx(hWnd, IDS_123_CANT_COPY_PRIMARY_GROUP_NOT_FOUND,
MB_OK | MB_ICONWARNING);
}
if (m_szFailureString.IsEmpty())
{
// we have nothing
return;
}
// we have an HRESULT we can use
ASSERT(FAILED(m_hrFailure));
UINT nMsgID = (m_hrFailure == E_ACCESSDENIED) ?
IDS_123_CANT_COPY_SOME_GROUP_MEMBERSHIP_ACCESS_DENIED :
IDS_123_CANT_COPY_SOME_GROUP_MEMBERSHIP;
PVOID apv[2] = {(LPWSTR)(lpszObjectName), (LPWSTR)(LPCWSTR)m_szFailureString };
ReportErrorEx(hWnd,nMsgID, m_hrFailure,
MB_OK | MB_ICONERROR, apv, 2);
}