1474 lines
37 KiB
C++
1474 lines
37 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);
|
||
|
|
||
|
}
|