Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1093 lines
41 KiB

  1. //---------------------------------------------------------------------------
  2. // UPDTUPN.cpp
  3. //
  4. // Comment: This is a COM object extension for the MCS DCTAccountReplicator.
  5. // This object implements the IExtendAccountMigration interface.
  6. // The process method on this object updates the userPrincipalName
  7. // property on the user object.
  8. //
  9. // (c) Copyright 1995-1998, Mission Critical Software, Inc., All Rights Reserved
  10. //
  11. // Proprietary and confidential to Mission Critical Software, Inc.
  12. //---------------------------------------------------------------------------
  13. #include "stdafx.h"
  14. #include "ARExt.h"
  15. #include "ARExt_i.c"
  16. #include "UPNUpdt.h"
  17. #include "ErrDCT.hpp"
  18. #include "Names.hpp"
  19. #include "resstr.h"
  20. #include <GetDcName.h>
  21. #include <Array.h>
  22. #include "AdsiHelpers.h"
  23. //#import "\bin\NetEnum.tlb" no_namespace
  24. #import "NetEnum.tlb" no_namespace
  25. #include "UpdtUPN.h"
  26. TErrorDct err;
  27. /////////////////////////////////////////////////////////////////////////////
  28. // CUpdtUPN
  29. StringLoader gString;
  30. #define SEQUENCE_UPPER_BOUND 999
  31. //---------------------------------------------------------------------------
  32. // Get and set methods for the properties.
  33. //---------------------------------------------------------------------------
  34. STDMETHODIMP CUpdtUPN::get_sName(BSTR *pVal)
  35. {
  36. *pVal = m_sName;
  37. return S_OK;
  38. }
  39. STDMETHODIMP CUpdtUPN::put_sName(BSTR newVal)
  40. {
  41. m_sName = newVal;
  42. return S_OK;
  43. }
  44. STDMETHODIMP CUpdtUPN::get_sDesc(BSTR *pVal)
  45. {
  46. *pVal = m_sDesc;
  47. return S_OK;
  48. }
  49. STDMETHODIMP CUpdtUPN::put_sDesc(BSTR newVal)
  50. {
  51. m_sDesc = newVal;
  52. return S_OK;
  53. }
  54. //---------------------------------------------------------------------------
  55. // ProcessObject : This method doesn't do anything.
  56. //---------------------------------------------------------------------------
  57. STDMETHODIMP CUpdtUPN::PreProcessObject(
  58. IUnknown *pSource, //in- Pointer to the source AD object
  59. IUnknown *pTarget, //in- Pointer to the target AD object
  60. IUnknown *pMainSettings, //in- Varset filled with the settings supplied by user
  61. IUnknown **ppPropsToSet, //in,out - Varset filled with Prop-Value pairs that will be set
  62. // once all extension objects are executed.
  63. EAMAccountStats* pStats
  64. )
  65. {
  66. IVarSetPtr pVs = pMainSettings;
  67. _variant_t var;
  68. _bstr_t sTemp;
  69. _bstr_t sUPN;
  70. _bstr_t sPref;
  71. _bstr_t sSuff;
  72. IADs * pAds = NULL;
  73. IADs * pAdsSource = NULL;
  74. HRESULT hr;
  75. c_array<WCHAR> sTempUPN(7000);
  76. long ub, lb;
  77. _bstr_t sFull;
  78. _variant_t HUGEP * pDt;
  79. _bstr_t sAdsPath;
  80. _variant_t varDN;
  81. _bstr_t sIntraforest;
  82. _bstr_t sDomainDNS;
  83. _bstr_t sTargetOU;
  84. WCHAR fileName[MAX_PATH];
  85. bool bReplace = false;
  86. tstring sSAMName;
  87. tstring sUPNName;
  88. _bstr_t sOldUPN;
  89. bool bConflicted = false;
  90. SUPNStruc UPNStruc;
  91. // We need to process the user accounts only
  92. sTemp = pVs->get(GET_BSTR(DCTVS_CopiedAccount_Type));
  93. if (!sTemp.length())
  94. return HRESULT_FROM_WIN32(ERROR_NOT_ENOUGH_MEMORY);
  95. if (_wcsicmp((WCHAR*)sTemp,L"user") && _wcsicmp((WCHAR*)sTemp,L"inetOrgPerson"))
  96. return S_OK;
  97. //store the name of this user in the UPN list
  98. sSAMName = _bstr_t(pVs->get(GET_BSTR(DCTVS_CopiedAccount_SourceSam)));
  99. //get the target domain DNS name
  100. sDomainDNS = pVs->get(GET_BSTR(DCTVS_Options_TargetDomainDns));
  101. //get the target OU path
  102. sTargetOU = pVs->get(GET_BSTR(DCTVS_Options_OuPath));
  103. //if not retrieved yet, get the default UPN suffix for this domain
  104. if (m_sUPNSuffix.length() == 0)
  105. {
  106. //if failed, use the domain's DNS name
  107. if (!GetDefaultUPNSuffix(sDomainDNS, sTargetOU))
  108. m_sUPNSuffix = sDomainDNS;
  109. }
  110. // Get the Error log filename from the Varset
  111. wcscpy(fileName, (WCHAR*)(pVs->get(GET_BSTR(DCTVS_Options_Logfile)).bstrVal));
  112. // Open the error log
  113. err.LogOpen(fileName, 1);
  114. sPref = pVs->get(GET_BSTR(DCTVS_Options_Prefix));
  115. sSuff = pVs->get(GET_BSTR(DCTVS_Options_Suffix));
  116. sIntraforest = pVs->get(GET_BSTR(DCTVS_Options_IsIntraforest));
  117. sTemp = pVs->get(GET_BSTR(DCTVS_AccountOptions_ReplaceExistingAccounts));
  118. if (!UStrICmp(sTemp,GET_STRING(IDS_YES)))
  119. bReplace = true;
  120. sAdsPath = L"";
  121. if ( pSource )
  122. {
  123. // Get the UPN from the source domain
  124. hr = pSource->QueryInterface(IID_IADs, (void**) &pAdsSource);
  125. }
  126. if ( pAdsSource )
  127. {
  128. if ( SUCCEEDED(hr) )
  129. {
  130. hr = pAdsSource->GetEx(L"userPrincipalName", &var);
  131. if (SUCCEEDED(hr) )
  132. {
  133. SAFEARRAY * pArray = V_ARRAY(&var);
  134. hr = SafeArrayGetLBound(pArray, 1, &lb);
  135. hr = SafeArrayGetUBound(pArray, 1, &ub);
  136. hr = SafeArrayAccessData(pArray, (void HUGEP **) &pDt);
  137. if ( SUCCEEDED(hr) )
  138. {
  139. // translate all the UPNs to the target domain.
  140. for ( long x = lb; x <= ub; x++)
  141. {
  142. wcsncpy(sTempUPN, (WCHAR*) pDt[x].bstrVal, 5000);
  143. sTempUPN[4999] = 0;
  144. //Get the stuff before the LAST @ sign.
  145. WCHAR * ndx = NULL;
  146. WCHAR * tempNdx = sTempUPN;
  147. do
  148. {
  149. tempNdx = wcschr(tempNdx + 1, L'@');
  150. if ( tempNdx )
  151. ndx = tempNdx;
  152. } while (tempNdx);
  153. if (ndx) *ndx = L'\0';
  154. if ( sPref.length() )
  155. sFull = sPref + _bstr_t(sTempUPN);
  156. else if ( sSuff.length() )
  157. sFull = _bstr_t(sTempUPN) + sSuff;
  158. else
  159. sFull = sTempUPN;
  160. sTemp = sFull;
  161. sUPN = sTemp + _bstr_t(L"@");
  162. sUPN = sUPN + m_sUPNSuffix;
  163. //store UPN name as it enters
  164. sOldUPN = sUPN;
  165. sUPNName = sUPN;
  166. //
  167. // If able to verify that UPN is unique or a unique UPN
  168. // was generated then sUPN will contain the unique UPN
  169. // otherwise sUPN will be an empty string.
  170. //
  171. GetUniqueUPN(sUPN, pVs, false, sAdsPath);
  172. if (sUPN.length() > 0)
  173. {
  174. //see if the two UPN's differ. If they do, then we had a conflict
  175. if (_wcsicmp((WCHAR*)sOldUPN, sUPN) != 0)
  176. {
  177. sUPNName = sUPN;
  178. hr = ERROR_OBJECT_ALREADY_EXISTS;
  179. bConflicted = true;
  180. }
  181. }
  182. else
  183. {
  184. //
  185. // Unable to verify if UPN was unique therefore set UPN to be an
  186. // empty string which will cause the UPN attribute not to be set
  187. // in the process object method. Also must increment the error count.
  188. //
  189. sUPNName = sUPN;
  190. if (pStats != NULL)
  191. {
  192. pStats->errors.users++;
  193. }
  194. }
  195. pDt[x] = _variant_t(sUPN);
  196. }
  197. SafeArrayUnaccessData(pArray);
  198. }
  199. }
  200. else
  201. {
  202. sTemp = pVs->get(GET_BSTR(DCTVS_CopiedAccount_TargetSam));
  203. sUPN = sTemp + _bstr_t(L"@");
  204. sUPN = sUPN + m_sUPNSuffix;
  205. //store UPN name as it enters
  206. sOldUPN = sUPN;
  207. sUPNName = sUPN;
  208. //
  209. // If able to verify that UPN is unique or a unique UPN
  210. // was generated then sUPN will contain the unique UPN
  211. // otherwise sUPN will be an empty string.
  212. //
  213. GetUniqueUPN(sUPN, pVs, false, sAdsPath);
  214. if (sUPN.length() > 0)
  215. {
  216. //see if the two UPN's differ. If they do, then we had a conflict
  217. if (_wcsicmp((WCHAR*)sOldUPN, sUPN) != 0)
  218. {
  219. sUPNName = sUPN;
  220. hr = ERROR_OBJECT_ALREADY_EXISTS;
  221. bConflicted = true;
  222. }
  223. }
  224. else
  225. {
  226. //
  227. // Unable to verify if UPN was unique therefore set UPN to be an
  228. // empty string which will cause the UPN attribute not to be set
  229. // in the process object method. Also must increment the error count.
  230. //
  231. sUPNName = sUPN;
  232. if (pStats != NULL)
  233. {
  234. pStats->errors.users++;
  235. }
  236. }
  237. }
  238. }
  239. }
  240. else
  241. {
  242. sTemp = pVs->get(GET_BSTR(DCTVS_CopiedAccount_TargetSam));
  243. sUPN = sTemp + _bstr_t(L"@");
  244. sUPN = sUPN + m_sUPNSuffix;
  245. //store UPN name as it enters
  246. sOldUPN = sUPN;
  247. sUPNName = sUPN;
  248. //
  249. // If able to verify that UPN is unique or a unique UPN
  250. // was generated then sUPN will contain the unique UPN
  251. // otherwise sUPN will be an empty string.
  252. //
  253. GetUniqueUPN(sUPN, pVs, false, sAdsPath);
  254. if (sUPN.length() > 0)
  255. {
  256. //see if the two UPN's differ. If they do, then we had a conflict
  257. if (_wcsicmp((WCHAR*)sOldUPN, sUPN) != 0)
  258. {
  259. sUPNName = sUPN;
  260. hr = ERROR_OBJECT_ALREADY_EXISTS;
  261. bConflicted = true;
  262. }
  263. }
  264. else
  265. {
  266. //
  267. // Unable to verify if UPN was unique therefore set UPN to be an
  268. // empty string which will cause the UPN attribute not to be set
  269. // in the process object method. Also must increment the error count.
  270. //
  271. sUPNName = sUPN;
  272. if (pStats != NULL)
  273. {
  274. pStats->errors.users++;
  275. }
  276. }
  277. }
  278. if ( pAds ) pAds->Release();
  279. if (pAdsSource) pAdsSource->Release();
  280. UPNStruc.sName = sUPNName;
  281. UPNStruc.sOldName = sOldUPN;
  282. UPNStruc.bConflicted = bConflicted;
  283. //insert UPN into Map
  284. mUPNMap.insert(CUPNMap::value_type(sSAMName, UPNStruc));
  285. return hr;
  286. }
  287. //---------------------------------------------------------------------------
  288. // ProcessObject : This method updates the UPN property of the object. It
  289. // first sees if a E-Mail is specified then it will set UPN
  290. // to that otherwise it builds it from SAMAccountName and the
  291. // Domain name
  292. //---------------------------------------------------------------------------
  293. STDMETHODIMP CUpdtUPN::ProcessObject(
  294. IUnknown *pSource, //in- Pointer to the source AD object
  295. IUnknown *pTarget, //in- Pointer to the target AD object
  296. IUnknown *pMainSettings, //in- Varset filled with the settings supplied by user
  297. IUnknown **ppPropsToSet, //in,out - Varset filled with Prop-Value pairs that will be set
  298. // once all extension objects are executed.
  299. EAMAccountStats* pStats
  300. )
  301. {
  302. IVarSetPtr pVs = pMainSettings;
  303. _bstr_t sTemp;
  304. IADs * pAds = NULL;
  305. _variant_t var;
  306. HRESULT hr;
  307. WCHAR fileName[MAX_PATH];
  308. CUPNMap::iterator itUPNMap;
  309. tstring sSam;
  310. SUPNStruc UPNStruc;
  311. bool bReplace = false;
  312. _bstr_t sOldUPNSuffix;
  313. // We need to process the user accounts only
  314. sTemp = pVs->get(GET_BSTR(DCTVS_CopiedAccount_Type));
  315. if ( _wcsicmp((WCHAR*)sTemp,L"user") && _wcsicmp((WCHAR*)sTemp,L"inetOrgPerson") ) return S_OK;
  316. sTemp = pVs->get(GET_BSTR(DCTVS_AccountOptions_ReplaceExistingAccounts));
  317. if (!UStrICmp(sTemp,GET_STRING(IDS_YES)))
  318. bReplace = true;
  319. //get the target SAM name
  320. sSam = _bstr_t(pVs->get(GET_BSTR(DCTVS_CopiedAccount_SourceSam)));
  321. // Get the Error log filename from the Varset
  322. wcscpy(fileName, (WCHAR*)(pVs->get(GET_BSTR(DCTVS_Options_Logfile)).bstrVal));
  323. // Open the error log
  324. err.LogOpen(fileName, 1);
  325. // And only need to process the accounts copied to Win2k domain.
  326. if ( pTarget )
  327. {
  328. //Get the IADs pointer to manipulate properties
  329. hr = pTarget->QueryInterface(IID_IADs, (void**) &pAds);
  330. if (SUCCEEDED(hr))
  331. {
  332. //get the UPN name for this user from the list
  333. itUPNMap = mUPNMap.find(sSam);
  334. if (itUPNMap != mUPNMap.end())
  335. UPNStruc = itUPNMap->second;
  336. if (!UPNStruc.sName.empty())
  337. {
  338. bool bSame = false;
  339. //if replace mode, don't set UPN if same object we are replacing and
  340. //if not the same object, get it's current UPN Suffix
  341. if (bReplace)
  342. {
  343. hr = pAds->Get(L"userPrincipalName", &var);
  344. if (SUCCEEDED(hr))
  345. {
  346. //if replacing the object whose UPN conflicted, don't change it
  347. if (!UPNStruc.sOldName.compare(var.bstrVal))
  348. bSame = true;
  349. else //else, get the object's current UPN suffix for re-use
  350. sOldUPNSuffix = GetUPNSuffix(var.bstrVal);
  351. }
  352. }
  353. if (!bSame)
  354. {
  355. var = UPNStruc.sName.c_str();
  356. //if replacing an existing object, use it's old UPN suffix
  357. if ((bReplace) && (sOldUPNSuffix.length() != 0))
  358. {
  359. //change the suffix on the old name, since it may longer conflict
  360. _bstr_t sUPN = ChangeUPNSuffix(UPNStruc.sOldName.c_str(), sOldUPNSuffix);
  361. //if changed, make sure we don't still have a UPN conflict and save the
  362. //new UPN for setting
  363. if (sUPN.length() != 0)
  364. {
  365. _bstr_t sTempUPN = sUPN;
  366. //get unigue UPN on target, now that we could conflict
  367. GetUniqueUPN(sUPN, pVs, true, _bstr_t(L""));
  368. if (sUPN.length() > 0)
  369. {
  370. //if changed, set conflicted flag and names for error message
  371. if (sUPN != sTempUPN)
  372. {
  373. UPNStruc.sName = sUPN;
  374. UPNStruc.sOldName = sTempUPN;
  375. UPNStruc.bConflicted = true;
  376. }
  377. else
  378. UPNStruc.bConflicted = false;
  379. }
  380. var = sUPN;
  381. }
  382. }
  383. //
  384. // If unable to determine if UPN is unique then GetUniqueUPN will
  385. // set the UPN to an empty string. If this is the case then don't
  386. // set the UPN attribute.
  387. //
  388. if ((V_VT(&var) == VT_BSTR) && (SysStringLen(V_BSTR(&var)) > 0))
  389. {
  390. hr = pAds->Put(L"userPrincipalName", var);
  391. if (SUCCEEDED(hr))
  392. {
  393. hr = pAds->SetInfo();
  394. if (SUCCEEDED(hr))
  395. {
  396. // If we changed the UPN Name due to conflict, we need to log a
  397. //message indicating the fact that we changed it.
  398. if (UPNStruc.bConflicted)
  399. err.MsgWrite(1, DCT_MSG_CREATE_FAILED_UPN_CONF_SS,
  400. UPNStruc.sOldName.c_str(), UPNStruc.sName.c_str());
  401. }
  402. }
  403. }
  404. else
  405. {
  406. if (pStats != NULL)
  407. {
  408. pStats->errors.users++;
  409. }
  410. }
  411. }
  412. }
  413. }
  414. }
  415. if ( pAds ) pAds->Release();
  416. return hr;
  417. }
  418. //---------------------------------------------------------------------------
  419. // ProcessUndo : We are not going to undo this.
  420. //---------------------------------------------------------------------------
  421. STDMETHODIMP CUpdtUPN::ProcessUndo(
  422. IUnknown *pSource, //in- Pointer to the source AD object
  423. IUnknown *pTarget, //in- Pointer to the target AD object
  424. IUnknown *pMainSettings, //in- Varset filled with the settings supplied by user
  425. IUnknown **ppPropsToSet, //in,out - Varset filled with Prop-Value pairs that will be set
  426. // once all extension objects are executed.
  427. EAMAccountStats* pStats
  428. )
  429. {
  430. IVarSetPtr pVs = pMainSettings;
  431. _bstr_t sTemp, sSUPN;
  432. IADs * pAds = NULL;
  433. _variant_t var;
  434. HRESULT hr = S_OK;
  435. _bstr_t sAdsPath = L"";
  436. _bstr_t sTempUPN;
  437. // We need to process the user accounts only
  438. sTemp = pVs->get(GET_BSTR(DCTVS_CopiedAccount_Type));
  439. if ( _wcsicmp((WCHAR*)sTemp,L"user") || _wcsicmp((WCHAR*)sTemp,L"inetOrgPerson") ) return S_OK;
  440. // get the original source account's UPN
  441. sSUPN = pVs->get(GET_BSTR(DCTVS_CopiedAccount_SourceUPN));
  442. if (sSUPN.length())
  443. {
  444. sTempUPN = sSUPN;
  445. GetUniqueUPN(sTempUPN, pVs, true, sAdsPath);
  446. int len;
  447. WCHAR * ndx, * tempNdx = (WCHAR*)sTempUPN;
  448. do
  449. {
  450. tempNdx = wcschr(tempNdx + 1, L'@');
  451. if ( tempNdx )
  452. ndx = tempNdx;
  453. } while (tempNdx);
  454. if (ndx) len = ndx - sTempUPN;
  455. if (_wcsnicmp(sTempUPN, sSUPN, len) != 0)
  456. return S_OK;
  457. // And only need to process the accounts copied to Win2k domain.
  458. if ( pTarget )
  459. {
  460. //Get the IADs pointer to manipulate properties
  461. hr = pTarget->QueryInterface(IID_IADs, (void**) &pAds);
  462. if ( SUCCEEDED(hr) )
  463. {
  464. var = sSUPN;
  465. hr = pAds->Put(L"userPrincipalName", var);
  466. if (SUCCEEDED(hr))
  467. {
  468. hr = pAds->SetInfo();
  469. }
  470. }
  471. }
  472. if ( pAds ) pAds->Release();
  473. }
  474. return hr;
  475. }
  476. //---------------------------------------------------------------------------
  477. // GetUniqueUPN : This function checks if the UPN is unique if not then
  478. // appends a number starting with 0 and retries till a unique
  479. // UPN is found.
  480. //---------------------------------------------------------------------------
  481. void CUpdtUPN::GetUniqueUPN(_bstr_t &sUPN, IVarSetPtr pVs, bool bUsingSamName, _bstr_t sAdsPath)
  482. {
  483. // Here are the steps that we follow to get the unique UPN name
  484. // 1. Check if the current name is unique. If it is then return that.
  485. // 2. Append collision prefix and suffix if the sam account name has changed due to pref/suffix.
  486. // 3. Add a numeric suffix to the UPN and repeat till a unique UPN is found.
  487. c_array<WCHAR> sTempUPN(5000);
  488. c_array<WCHAR> sPath(5000);
  489. HRESULT hr = E_FAIL;
  490. LPWSTR pCols[] = { L"distinguishedName", L"sAMAccountName" };
  491. BSTR * pData = NULL;
  492. SAFEARRAY * pSaCols = NULL;
  493. SAFEARRAYBOUND bd = { 2, 0 };
  494. _bstr_t sSrcDomain = pVs->get(GET_BSTR(DCTVS_Options_SourceDomainDns));
  495. _bstr_t sTgtDomain = pVs->get(GET_BSTR(DCTVS_Options_TargetDomainDns));
  496. INetObjEnumeratorPtr pQuery(__uuidof(NetObjEnumerator));
  497. IEnumVARIANTPtr pEnum;
  498. DWORD fetched = 0;
  499. _variant_t var;
  500. bool bCollPrefSufProcessed = false;
  501. _bstr_t sSourceSam = pVs->get(GET_BSTR(DCTVS_CopiedAccount_SourceSam));
  502. _bstr_t sTargetSam = pVs->get(GET_BSTR(DCTVS_CopiedAccount_TargetSam));
  503. _bstr_t sPrefix = pVs->get(GET_BSTR(DCTVS_AccountOptions_Prefix));
  504. _bstr_t sSuffix = pVs->get(GET_BSTR(DCTVS_AccountOptions_Suffix));
  505. _bstr_t sPref = pVs->get(GET_BSTR(DCTVS_Options_Prefix));
  506. _bstr_t sSuff = pVs->get(GET_BSTR(DCTVS_Options_Suffix));
  507. int offset = 0;
  508. c_array<WCHAR> sTemp(5000);
  509. SAFEARRAY * psaPath = NULL;
  510. _bstr_t strDn;
  511. _bstr_t strSam;
  512. VARIANT * pVar;
  513. bool bReplace = false;
  514. WCHAR sTempSAM[MAX_PATH];
  515. _bstr_t sNewSAM;
  516. _bstr_t sUPNSuffix;
  517. _bstr_t sUPNPrefix;
  518. _bstr_t sReplace = pVs->get(GET_BSTR(DCTVS_AccountOptions_ReplaceExistingAccounts));
  519. if (!UStrICmp(sReplace,GET_STRING(IDS_YES)))
  520. bReplace = true;
  521. wcscpy(sTempSAM, (WCHAR*)sSourceSam);
  522. StripSamName(sTempSAM);
  523. if ( sPref.length() )
  524. sNewSAM = sPref + _bstr_t(sTempSAM);
  525. else if ( sSuff.length() )
  526. sNewSAM = _bstr_t(sTempSAM) + sSuff;
  527. else
  528. sNewSAM = sTempSAM;
  529. wcscpy(sTempUPN, (WCHAR*) sUPN);
  530. //Get the stuff before the LAST @ sign.
  531. WCHAR * ndx = NULL;
  532. WCHAR * tempNdx = sTempUPN;
  533. do
  534. {
  535. tempNdx = wcschr(tempNdx + 1, L'@');
  536. if ( tempNdx )
  537. ndx = tempNdx;
  538. } while (tempNdx);
  539. //
  540. // If UPN prefix and suffix terminate prefix portion
  541. // otherwise return empty UPN as an internal error
  542. // has occurred so do not generate UPN for this user.
  543. //
  544. if (ndx)
  545. {
  546. *ndx = L'\0';
  547. }
  548. else
  549. {
  550. err.SysMsgWrite(ErrE, E_FAIL, DCT_MSG_UNABLE_TO_GENERATE_UNIQUE_UPN_S, (PCWSTR)sUPN);
  551. sUPN = L"";
  552. return;
  553. }
  554. sUPNSuffix = ndx+1;
  555. sUPNPrefix = sTempUPN;
  556. //
  557. // User principal names (UPN) must be unique across the forest therefore
  558. // the name of a global catalog server in the target forest must be
  559. // obtained so that the entire forest may be queried for user principal names.
  560. //
  561. // If unable to obtain name of a global catalog server then log error message
  562. // and set UPN to an empty string which will cause the UPN attribute not to
  563. // be set.
  564. //
  565. _bstr_t strGlobalCatalogServer;
  566. DWORD dwError = GetGlobalCatalogServer5(sTgtDomain, strGlobalCatalogServer);
  567. if ((dwError == ERROR_SUCCESS) && (strGlobalCatalogServer.length() > 0))
  568. {
  569. wsprintf(sPath, L"GC://%s", (PCWSTR)strGlobalCatalogServer);
  570. }
  571. else
  572. {
  573. err.SysMsgWrite(ErrE, HRESULT_FROM_WIN32(dwError), DCT_MSG_UNABLE_TO_QUERY_UPN_IN_GLOBAL_CATALOG_SERVER_S, (PCWSTR)sUPN);
  574. sUPN = L"";
  575. return;
  576. }
  577. // setup the columns that we want the query to return to us.
  578. pSaCols = SafeArrayCreate(VT_BSTR, 1, &bd);
  579. if (pSaCols)
  580. {
  581. hr = SafeArrayAccessData(pSaCols, (void HUGEP **) &pData);
  582. if ( SUCCEEDED(hr) )
  583. {
  584. pData[0] = SysAllocString(pCols[0]);
  585. pData[1] = SysAllocString(pCols[1]);
  586. if (!pData[0] || !pData[1])
  587. {
  588. SafeArrayUnaccessData(pSaCols);
  589. sUPN = L"";
  590. return;
  591. }
  592. }
  593. hr = SafeArrayUnaccessData(pSaCols);
  594. }
  595. if ( SUCCEEDED(hr) )
  596. {
  597. // First we need to set up a query to find the UPN
  598. wcscpy(sTempUPN, (WCHAR*)sUPN);
  599. do
  600. {
  601. _bstr_t sQuery = L"(userPrincipalName=";
  602. sQuery += GetEscapedFilterValue(sTempUPN).c_str();
  603. sQuery += L")";
  604. hr = pQuery->raw_SetQuery(sPath, sTgtDomain, sQuery, ADS_SCOPE_SUBTREE, FALSE);
  605. if ( SUCCEEDED(hr) )
  606. hr = pQuery->raw_SetColumns(pSaCols);
  607. if ( SUCCEEDED(hr) )
  608. hr = pQuery->raw_Execute(&pEnum);
  609. if ( SUCCEEDED(hr) )
  610. {
  611. hr = pEnum->Next(1, &var, &fetched);
  612. while ( hr == S_OK )
  613. {
  614. if ( var.vt & VT_ARRAY )
  615. {
  616. psaPath = var.parray;
  617. hr = SafeArrayAccessData(psaPath, (void HUGEP**) &pVar);
  618. if ( SUCCEEDED(hr) )
  619. {
  620. //
  621. // Retrieve distinguishedName and sAMAccountName attributes.
  622. //
  623. if (V_VT(&pVar[0]) == VT_BSTR)
  624. {
  625. strDn = V_BSTR(&pVar[0]);
  626. }
  627. if (V_VT(&pVar[1]) == VT_BSTR)
  628. {
  629. strSam = V_BSTR(&pVar[1]);
  630. }
  631. }
  632. SafeArrayUnaccessData(psaPath);
  633. bool bContinue = false;
  634. //
  635. // If unable to query attributes generate error message and set UPN to empty string.
  636. //
  637. if (!strDn || !strSam)
  638. {
  639. err.SysMsgWrite(ErrE, E_FAIL, DCT_MSG_UNABLE_TO_QUERY_UPN_IN_GLOBAL_CATALOG_SERVER_S, (PCWSTR)sUPN);
  640. wcscpy(sTempUPN, L"");
  641. hr = S_FALSE;
  642. }
  643. else
  644. {
  645. //
  646. // If the object found is the source domain object continue.
  647. // This will be the case during intra-forest migrations.
  648. //
  649. if (sSrcDomain.length() && (_wcsicmp((wchar_t*)strSam, (wchar_t*)sSourceSam) == 0))
  650. {
  651. _bstr_t strDomain = GetDomainDNSFromPath(strDn);
  652. if (_wcsicmp((wchar_t*)strDomain, (wchar_t*)sSrcDomain) == 0)
  653. {
  654. bContinue = true;
  655. }
  656. }
  657. //
  658. // If the object found is the target domain object to be replaced continue.
  659. //
  660. if (!bContinue && bReplace && (_wcsicmp((wchar_t*)strSam, (wchar_t*)sNewSAM) == 0))
  661. {
  662. _bstr_t strDomain = GetDomainDNSFromPath(strDn);
  663. if (_wcsicmp((wchar_t*)strDomain, (wchar_t*)sTgtDomain) == 0)
  664. {
  665. bContinue = true;
  666. }
  667. }
  668. }
  669. // If the account found is the same as the account being processed then we
  670. // need to see if any other accounts have this UPN. if they do then we need
  671. // to change it other wise we do not need to process this any further.
  672. if (bContinue)
  673. {
  674. var.Clear();
  675. hr = pEnum->Next(1, &var, &fetched);
  676. }
  677. else
  678. {
  679. break;
  680. }
  681. }
  682. }
  683. if ( hr == S_OK )
  684. {
  685. // If we are here that means we have a collision So we need to update the UPN and try again
  686. // See if we have processed the Prefix/Suffix
  687. if ( !bCollPrefSufProcessed )
  688. {
  689. // See if we renamed the samAccountName with the prefix/suffix. If we are already using
  690. // sam name then there is no need to add the prefix/suffix.
  691. if ( !bUsingSamName && RenamedWithPrefixSuffix(sSourceSam, sTargetSam, sPrefix, sSuffix))
  692. {
  693. // Since we renamed the sam names we can rename the UPN
  694. if ( sPrefix.length() )
  695. wsprintf(sTempUPN, L"%s%s", (WCHAR*)sPrefix, (WCHAR*)sUPNPrefix);
  696. if ( sSuffix.length() )
  697. wsprintf(sTempUPN, L"%s%s",(WCHAR*)sUPNPrefix, (WCHAR*)sSuffix);
  698. sUPNPrefix = sTempUPN; // we want to apply the prefix/suffix in any case.
  699. }
  700. else
  701. {
  702. // just add a number to the end of the name.
  703. wsprintf(sTempUPN, L"%s%d", (WCHAR*)sUPNPrefix, offset);
  704. offset++;
  705. }
  706. bCollPrefSufProcessed = true;
  707. }
  708. else
  709. {
  710. //
  711. // Attempt sequence numbers only to some reasonable upper limit.
  712. //
  713. if (offset <= SEQUENCE_UPPER_BOUND)
  714. {
  715. // we went through prefix/suffix and still found a collision so we need to go by the count now.
  716. wsprintf(sTempUPN, L"%s%d", (WCHAR*)sUPNPrefix, offset);
  717. offset++;
  718. }
  719. else
  720. {
  721. //
  722. // If unable to find a unique UPN then must return empty UPN.
  723. //
  724. err.MsgWrite(ErrE, DCT_MSG_UNABLE_TO_GENERATE_UNIQUE_UPN_S, (PCWSTR)sUPN);
  725. wcscpy(sTempUPN, L"");
  726. break;
  727. }
  728. }
  729. if (wcslen(sTempUPN) > 0)
  730. {
  731. wcscpy(sTemp, sTempUPN);
  732. wsprintf(sTempUPN, L"%s@%s", (WCHAR*)sTemp, (WCHAR*)sUPNSuffix);
  733. }
  734. }
  735. var.Clear();
  736. }
  737. else
  738. {
  739. err.SysMsgWrite(ErrE, hr, DCT_MSG_UNABLE_TO_QUERY_UPN_IN_GLOBAL_CATALOG_SERVER_S, (PCWSTR)sUPN);
  740. }
  741. } while ( hr == S_OK );
  742. SafeArrayDestroy(pSaCols);
  743. }
  744. if (FAILED(hr))
  745. {
  746. sUPN = L"";
  747. }
  748. else
  749. {
  750. sUPN = sTempUPN;
  751. }
  752. }
  753. //---------------------------------------------------------------------------
  754. // RenamedWithPrefixSuffix : Checks to see if the Target sam name
  755. // was renamed with a prefix/suffix.
  756. //---------------------------------------------------------------------------
  757. bool CUpdtUPN::RenamedWithPrefixSuffix(_bstr_t sSourceSam, _bstr_t sTargetSam, _bstr_t sPrefix, _bstr_t sSuffix)
  758. {
  759. bool retVal = false;
  760. if ( sSourceSam != sTargetSam )
  761. {
  762. if ( sPrefix.length() )
  763. {
  764. if ( !wcsncmp((WCHAR*) sTargetSam, (WCHAR*) sPrefix, sPrefix.length()) )
  765. retVal = true;
  766. }
  767. if ( sSuffix.length() )
  768. {
  769. if ( !wcscmp((WCHAR*) sTargetSam + (sTargetSam.length() - sSuffix.length()), (WCHAR*) sSuffix ) )
  770. retVal = true;
  771. }
  772. }
  773. return retVal;
  774. }
  775. /*********************************************************************
  776. * *
  777. * Written by: Paul Thompson *
  778. * Date: 24 MAR 2001 *
  779. * *
  780. * This function is responsible for retrieving the default UPN *
  781. * suffix to be used in making UPN names. The suffix will be stored *
  782. * in a class member variable. *
  783. * First, using the given target OU path, see if the target OU *
  784. * has any UPN suffixes defined for it. If so, return store the *
  785. * last one enumerated. Otherwise, see if any UPN suffixes have *
  786. * been defined on the configuration's partition. If so, store the *
  787. * last one enumerated. If no success yet, use the forest root's *
  788. * domain DNS name. *
  789. * *
  790. *********************************************************************/
  791. //BEGIN GetDefaultUPNSuffix
  792. bool CUpdtUPN::GetDefaultUPNSuffix(_bstr_t sDomainDNS, _bstr_t sTargetOU)
  793. {
  794. /* local variables */
  795. IADs * pDSE = NULL;
  796. IADs * pCont = NULL;
  797. WCHAR sRoot[1000];
  798. HRESULT hr = S_OK;
  799. _variant_t var;
  800. _variant_t HUGEP * pVar;
  801. int nLast;
  802. /* function body */
  803. //check incoming parameters
  804. if ((sDomainDNS.length() == 0) || (sTargetOU.length() == 0))
  805. return false;
  806. /* first see if the target OU has UPN suffixes defined */
  807. //get a pointer to the target OU
  808. hr = ADsGetObject(sTargetOU,IID_IADs,(void**)&pCont);
  809. if ( SUCCEEDED(hr) )
  810. {
  811. //get any UPN suffixes defined
  812. hr = pCont->Get( L"uPNSuffixes", &var);
  813. if ( SUCCEEDED(hr) )
  814. {
  815. //if one, store it and return
  816. if ( var.vt == VT_BSTR )
  817. {
  818. m_sUPNSuffix = var.bstrVal; //store the suffix
  819. pCont->Release();
  820. return true;
  821. }
  822. //else if nore than one, get the first one, store it, and return
  823. else if ( var.vt & VT_ARRAY )
  824. {
  825. SAFEARRAY * multiVals = var.parray;
  826. SafeArrayAccessData(multiVals, (void HUGEP **) &pVar);
  827. nLast = multiVals->rgsabound->cElements - 1;
  828. m_sUPNSuffix = _bstr_t(V_BSTR(&pVar[nLast]));
  829. SafeArrayUnaccessData(multiVals);
  830. pCont->Release();
  831. return true;
  832. }
  833. }//end if suffixes defined on the partition
  834. pCont->Release();
  835. pCont = NULL;
  836. }//if got partition
  837. /* next try the UPN suffixes on the partition container or the root
  838. domain's DNS name */
  839. //get the root DSE container
  840. _snwprintf(sRoot, sizeof(sRoot) / sizeof(sRoot[0]), L"LDAP://%s/RootDSE", (WCHAR*)sDomainDNS);
  841. sRoot[sizeof(sRoot) / sizeof(sRoot[0]) - 1] = L'\0';
  842. hr = ADsGetObject(sRoot,IID_IADs,(void**)&pDSE);
  843. if ( SUCCEEDED(hr) )
  844. {
  845. //get the suffixes listed on the configuration partition
  846. hr = pDSE->Get(L"configurationNamingContext",&var);
  847. if ( SUCCEEDED(hr) )
  848. {
  849. swprintf(sRoot,L"LDAP://%ls/CN=Partitions,%ls", (WCHAR*)sDomainDNS, var.bstrVal);
  850. hr = ADsGetObject(sRoot,IID_IADs,(void**)&pCont);
  851. if ( SUCCEEDED(hr) )
  852. {
  853. //get any UPN suffixes defined
  854. hr = pCont->Get( L"uPNSuffixes", &var);
  855. if ( SUCCEEDED(hr) )
  856. {
  857. //if one, store it and return
  858. if ( var.vt == VT_BSTR )
  859. {
  860. m_sUPNSuffix = var.bstrVal; //store the suffix
  861. pDSE->Release();
  862. pCont->Release();
  863. return true;
  864. }
  865. //else if nore than one, get the first one, store it, and return
  866. else if ( var.vt & VT_ARRAY )
  867. {
  868. SAFEARRAY * multiVals = var.parray;
  869. SafeArrayAccessData(multiVals, (void HUGEP **) &pVar);
  870. nLast = multiVals->rgsabound->cElements - 1;
  871. m_sUPNSuffix = _bstr_t(V_BSTR(&pVar[nLast]));
  872. SafeArrayUnaccessData(multiVals);
  873. pDSE->Release();
  874. pCont->Release();
  875. return true;
  876. }
  877. }//end if suffixes defined on the partition
  878. pCont->Release();
  879. pCont = NULL;
  880. }//if got partition
  881. }//if got config naming context
  882. //since no UPN suffixes defined on the partition, try the root domain's
  883. //DNS name
  884. hr = pDSE->Get(L"RootDomainNamingContext",&var);
  885. if ( SUCCEEDED(hr) )
  886. {
  887. //convert the DN of the root domain to a DNS name, store it, and return
  888. m_sUPNSuffix = GetDomainDNSFromPath(_bstr_t(var.bstrVal));
  889. pDSE->Release();
  890. return true;
  891. }
  892. pDSE->Release();
  893. pDSE = NULL;
  894. }//if got rootDSE
  895. return false;
  896. }
  897. //END GetDefaultUPNSuffix
  898. /*********************************************************************
  899. * *
  900. * Written by: Paul Thompson *
  901. * Date: 26 MAR 2001 *
  902. * *
  903. * This function is responsible for extracting the UPN Suffix *
  904. * from a given UPN name and returning that suffix. *
  905. * *
  906. *********************************************************************/
  907. //BEGIN GetUPNSuffix
  908. _bstr_t CUpdtUPN::GetUPNSuffix(_bstr_t sUPNName)
  909. {
  910. /* local variables */
  911. _bstr_t sUPNSuffix = L"";
  912. WCHAR * pTemp;
  913. /* function body */
  914. //check incoming parameters
  915. if (sUPNName.length() == 0)
  916. return sUPNSuffix;
  917. //find the last '@'
  918. pTemp = wcsrchr((WCHAR*)sUPNName, L'@');
  919. //if found, copy the suffix to the return variable
  920. if (pTemp)
  921. sUPNSuffix = pTemp+1;
  922. return sUPNSuffix;
  923. }
  924. //END GetUPNSuffix
  925. /*********************************************************************
  926. * *
  927. * Written by: Paul Thompson *
  928. * Date: 26 MAR 2001 *
  929. * *
  930. * This function is responsible for replacing the UPN Suffix *
  931. * on a given UPN name with the given suffix and returning the new *
  932. * UPN name. *
  933. * *
  934. *********************************************************************/
  935. //BEGIN ChangeUPNSuffix
  936. _bstr_t CUpdtUPN::ChangeUPNSuffix(_bstr_t sUPNName, _bstr_t sNewSuffix)
  937. {
  938. /* local variables */
  939. _bstr_t sNewUPN = L"";
  940. WCHAR * pTemp;
  941. /* function body */
  942. //check incoming parameters
  943. if (sUPNName.length() == 0)
  944. return sNewUPN;
  945. //create a temporary buffer to hold the UPN Name
  946. WCHAR* sUPN = new WCHAR[sUPNName.length() + 1];
  947. if (!sUPN)
  948. return sNewUPN;
  949. //copy the UPN to this buffer
  950. wcscpy(sUPN, sUPNName);
  951. //find the last '@'
  952. pTemp = wcsrchr(sUPN, L'@');
  953. //if found, make the new UPN with the old Prefix and given suffix
  954. if (pTemp)
  955. {
  956. //end the string after the '@'
  957. *(pTemp+1) = L'\0';
  958. //copy the Prefix plus the new Suffix to the new UPN name
  959. sNewUPN = sUPN + sNewSuffix;
  960. }
  961. //delete the prefix string
  962. delete [] sUPN;
  963. return sNewUPN;
  964. }
  965. //END ChangeUPNSuffix