Source code of Windows XP (NT5)
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.

529 lines
13 KiB

  1. #include "StdAfx.h"
  2. #include "AdmtAccount.h"
  3. #include <LM.h>
  4. #include <NtSecApi.h>
  5. #include <ActiveDS.h>
  6. #include <PWGen.hpp>
  7. #include <AdsiHelpers.h>
  8. #include <ResStr.h>
  9. #pragma comment(lib, "NetApi32.lib")
  10. #pragma comment(lib, "ActiveDS.lib")
  11. #pragma comment(lib, "adsiid.lib")
  12. //#pragma comment(lib, "ntdsapi.lib")
  13. #ifndef STATUS_SUCCESS
  14. #define STATUS_SUCCESS ((NTSTATUS)0x00000000L)
  15. #endif
  16. using namespace _com_util;
  17. namespace nsAdmtAccount
  18. {
  19. bool __stdcall IsComputerDomainController();
  20. CADsContainer __stdcall GetAccountContainer(bool bLocal);
  21. _bstr_t __stdcall GetAccountName();
  22. _bstr_t __stdcall CreateAccountPassword();
  23. CADsUser __stdcall GetAccount(bool bLocal, CADsContainer& adsContainer, _bstr_t strName);
  24. CADsUser __stdcall CreateAccount(bool bLocal, CADsContainer& adsContainer, _bstr_t strName, _bstr_t strPassword);
  25. _bstr_t __stdcall LoadPasswordFromPrivateData();
  26. void __stdcall SavePasswordToPrivateData(LPCTSTR pszPassword);
  27. inline _bstr_t __stdcall GetAccountPassword()
  28. {
  29. return LoadPasswordFromPrivateData();
  30. }
  31. void _cdecl Trace(LPCTSTR pszFormat, ...)
  32. {
  33. #ifdef _DEBUG
  34. if (pszFormat)
  35. {
  36. _TCHAR szMessage[2048];
  37. va_list args;
  38. va_start(args, pszFormat);
  39. _vsntprintf(szMessage, 2048, pszFormat, args);
  40. va_end(args);
  41. OutputDebugString(szMessage);
  42. }
  43. #endif
  44. }
  45. }
  46. using namespace nsAdmtAccount;
  47. //---------------------------------------------------------------------------
  48. // GetOptionsCredentials Method
  49. //
  50. // Retrieves the options credentials.
  51. //---------------------------------------------------------------------------
  52. HRESULT __stdcall GetOptionsCredentials(_bstr_t& strDomain, _bstr_t& strUserName, _bstr_t& strPassword)
  53. {
  54. HRESULT hr = S_OK;
  55. try
  56. {
  57. // use local account if computer is not a domain controller
  58. bool bLocal = IsComputerDomainController() ? false : true;
  59. // get account container
  60. CADsContainer adsContainer = GetAccountContainer(bLocal);
  61. // get account name
  62. strUserName = GetAccountName();
  63. // get account password
  64. bool bNewPassword = false;
  65. strPassword = GetAccountPassword();
  66. if (!strPassword)
  67. {
  68. strPassword = CreateAccountPassword();
  69. bNewPassword = true;
  70. }
  71. // get account
  72. CADsUser adsUser = GetAccount(bLocal, adsContainer, strUserName);
  73. // if account exists...
  74. if (adsUser)
  75. {
  76. // if new password...
  77. // this may occur if administrator or system deletes
  78. // the private data key where the password is stored
  79. if (bNewPassword)
  80. {
  81. // set new password
  82. adsUser.SetPassword(strPassword);
  83. }
  84. }
  85. else
  86. {
  87. // create account
  88. CreateAccount(bLocal, adsContainer, strUserName, strPassword);
  89. }
  90. // get account domain (NetBIOS name)
  91. if (bLocal)
  92. {
  93. _TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 1];
  94. DWORD cbComputerName = MAX_COMPUTERNAME_LENGTH + 1;
  95. if (GetComputerName(szComputerName, &cbComputerName))
  96. {
  97. strDomain = szComputerName;
  98. }
  99. else
  100. {
  101. _com_issue_error(HRESULT_FROM_WIN32(GetLastError()));
  102. }
  103. }
  104. else
  105. {
  106. strDomain = CADsADSystemInfo().GetDomainShortName();
  107. }
  108. }
  109. catch (_com_error& ce)
  110. {
  111. hr = ce.Error();
  112. }
  113. catch (...)
  114. {
  115. hr = E_FAIL;
  116. }
  117. return hr;
  118. }
  119. namespace nsAdmtAccount
  120. {
  121. //---------------------------------------------------------------------------
  122. // IsComputerDomainController Function
  123. //
  124. // Determines whether the local computer is a domain controller or not.
  125. //---------------------------------------------------------------------------
  126. bool __stdcall IsComputerDomainController()
  127. {
  128. Trace(_T("E IsComputerDomainController()\n"));
  129. bool bIs = true;
  130. PSERVER_INFO_101 psiInfo = NULL;
  131. NET_API_STATUS nasStatus = NetServerGetInfo(NULL, 101, (LPBYTE*)&psiInfo);
  132. if (nasStatus == ERROR_SUCCESS)
  133. {
  134. // if either the domain controller or backup domain controller type
  135. // bit is set then the local computer is a domain controller
  136. if (psiInfo->sv101_type & (SV_TYPE_DOMAIN_CTRL|SV_TYPE_DOMAIN_BAKCTRL))
  137. {
  138. bIs = true;
  139. }
  140. else
  141. {
  142. bIs = false;
  143. }
  144. NetApiBufferFree(psiInfo);
  145. }
  146. else
  147. {
  148. _com_issue_error(HRESULT_FROM_WIN32(nasStatus));
  149. }
  150. Trace(_T("L IsComputerDomainController() : %s\n"), bIs ? _T("true") : _T("false"));
  151. return bIs;
  152. }
  153. //---------------------------------------------------------------------------
  154. // GetAccountContainer Function
  155. //
  156. // Retrieves container interface either to local computer or the computer's
  157. // domain Users container.
  158. // The WinNT: provider is used for access to the local computer's local
  159. // security authority.
  160. // The LDAP: provider is used for access to the domain.
  161. //---------------------------------------------------------------------------
  162. CADsContainer __stdcall GetAccountContainer(bool bLocal)
  163. {
  164. Trace(_T("E GetAccountContainer(bLocal=%s)\n"), bLocal ? _T("true") : _T("false"));
  165. CADsContainer adsContainer;
  166. // if creating a local account...
  167. if (bLocal)
  168. {
  169. // get container interface to computer
  170. _TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 1];
  171. DWORD cbComputerName = MAX_COMPUTERNAME_LENGTH + 1;
  172. if (GetComputerName(szComputerName, &cbComputerName))
  173. {
  174. // build path to local computer
  175. _bstr_t strPath = _bstr_t(_T("WinNT://")) + szComputerName + _bstr_t(_T(",computer"));
  176. // get container interface
  177. adsContainer = CADsContainer(LPCTSTR(strPath));
  178. }
  179. else
  180. {
  181. _com_issue_error(HRESULT_FROM_WIN32(GetLastError()));
  182. }
  183. }
  184. else
  185. {
  186. // retrieve default naming context for computer's domain
  187. CADs adsRootDSE(_T("LDAP://rootDSE"));
  188. _bstr_t strDefaultNamingContext = adsRootDSE.Get(_T("defaultNamingContext"));
  189. // build path to Users container in the domain
  190. CADsPathName adsPathName(_T("LDAP"), ADS_SETTYPE_PROVIDER);
  191. adsPathName.Set(strDefaultNamingContext, ADS_SETTYPE_DN);
  192. adsPathName.AddLeafElement(_T("CN=Users"));
  193. // get container interface
  194. adsContainer = CADsContainer(LPCTSTR(adsPathName.Retrieve(ADS_FORMAT_X500)));
  195. }
  196. Trace(_T("L GetAccountContainer() : %p\n"), adsContainer.operator IADsContainer*());
  197. return adsContainer;
  198. }
  199. //---------------------------------------------------------------------------
  200. // GetAccountName Function
  201. //
  202. // Generates account name based on the local computer's name.
  203. //---------------------------------------------------------------------------
  204. _bstr_t __stdcall GetAccountName()
  205. {
  206. Trace(_T("E GetAccountName()\n"));
  207. _bstr_t strName;
  208. _TCHAR szComputerName[MAX_COMPUTERNAME_LENGTH + 1];
  209. DWORD cbComputerName = MAX_COMPUTERNAME_LENGTH + 1;
  210. if (GetComputerName(szComputerName, &cbComputerName))
  211. {
  212. strName = _T("ADMT_");
  213. strName += szComputerName;
  214. }
  215. else
  216. {
  217. _com_issue_error(HRESULT_FROM_WIN32(GetLastError()));
  218. }
  219. Trace(_T("L GetAccountName() : '%s'\n"), LPCTSTR(strName));
  220. return strName;
  221. }
  222. //---------------------------------------------------------------------------
  223. // CreateAccountPassword Function
  224. //
  225. // Creates complex password, stores it in the local security authority
  226. // private data and returns the password.
  227. //---------------------------------------------------------------------------
  228. _bstr_t __stdcall CreateAccountPassword()
  229. {
  230. Trace(_T("E CreateAccountPassword()\n"));
  231. // generate complex password
  232. WCHAR szPassword[32];
  233. EaPasswordGenerate(3, 3, 3, 3, 2, 12, szPassword, sizeof(szPassword) / sizeof(szPassword[0]));
  234. SavePasswordToPrivateData(szPassword);
  235. Trace(_T("L CreateAccountPassword() : '%s'\n"), szPassword);
  236. return szPassword;
  237. }
  238. //---------------------------------------------------------------------------
  239. // GetAccount Function
  240. //
  241. // Gets local or domain user account in the specified container.
  242. //---------------------------------------------------------------------------
  243. CADsUser __stdcall GetAccount(bool bLocal, CADsContainer& adsContainer, _bstr_t strName)
  244. {
  245. Trace(_T("E GetAccount(bLocal=%s, adsContainer=%p, strName='%s')\n"), bLocal ? _T("true") : _T("false"), adsContainer.operator IADsContainer*(), LPCTSTR(strName));
  246. IDispatchPtr spDispatch;
  247. try
  248. {
  249. spDispatch = adsContainer.GetObject(_T("user"), bLocal ? strName : _T("CN=") + strName);
  250. }
  251. catch (_com_error& ce)
  252. {
  253. switch (ce.Error())
  254. {
  255. case HRESULT_FROM_WIN32(ERROR_DS_NO_SUCH_OBJECT):
  256. case HRESULT_FROM_WIN32(NERR_UserNotFound):
  257. break;
  258. default:
  259. throw;
  260. break;
  261. }
  262. }
  263. Trace(_T("L GetAccount() : %p\n"), spDispatch.GetInterfacePtr());
  264. return spDispatch;
  265. }
  266. //---------------------------------------------------------------------------
  267. // CreateAccount Function
  268. //
  269. // Creates local or domain user account in the specified container.
  270. // WinNT provider is used to create a local account whereas the LDAP provider
  271. // is used to create a domain account.
  272. //---------------------------------------------------------------------------
  273. CADsUser __stdcall CreateAccount(bool bLocal, CADsContainer& adsContainer, _bstr_t strName, _bstr_t strPassword)
  274. {
  275. Trace(_T("E CreateAccount(bLocal=%s, adsContainer=%p, strName='%s', strPassword='%s')\n"), bLocal ? _T("true") : _T("false"), adsContainer.operator IADsContainer*(), LPCTSTR(strName), LPCTSTR(strPassword));
  276. CADsUser adsUser;
  277. // the relative name must be prefixed only for LDAP provider
  278. adsUser = adsContainer.Create(_T("user"), bLocal ? strName : _T("CN=") + strName);
  279. // if not local account...
  280. if (bLocal == false)
  281. {
  282. // the SAM account name attribute is mandatory in active directory
  283. adsUser.Put(_T("sAMAccountName"), strName);
  284. }
  285. adsUser.SetDescription(GET_BSTR(IDS_ADMT_ACCOUNT_DESCRIPTION));
  286. // active directory account must exist first before password can be set
  287. // whereas local accounts may be created with the password attribute
  288. // this becomes important if password policies are in effect because
  289. // the WinNT provider will fail to create the account if the password
  290. // does not meet password policy
  291. // the LDAP provider creates the account disabled by default and therefore
  292. // the password does not need to meet password policy
  293. if (bLocal == false)
  294. {
  295. adsUser.SetInfo();
  296. }
  297. // the password can only be set after the user account is created
  298. adsUser.SetPassword(strPassword);
  299. // enable account after setting the account password
  300. // set password cannot be changed to remind the administrator not to change the password
  301. // note that the administrator can still set the password even with this flag set
  302. // set do not expire password as we do not want the password to expire
  303. adsUser.Put(
  304. bLocal ? _T("UserFlags") : _T("userAccountControl"),
  305. long(ADS_UF_NORMAL_ACCOUNT|ADS_UF_PASSWD_CANT_CHANGE|ADS_UF_DONT_EXPIRE_PASSWD)
  306. );
  307. adsUser.SetInfo();
  308. Trace(_T("L CreateAccount() : %p\n"), adsUser.operator IADs*());
  309. return adsUser;
  310. }
  311. // LSA private data key name for password storage
  312. const WCHAR c_wszKeyName[] = L"L$4480F273-39D1-4FFA-BEB4-44DF6C848364";
  313. const USHORT c_usKeyNameLength = sizeof(c_wszKeyName) - sizeof(c_wszKeyName[0]);
  314. //---------------------------------------------------------------------------
  315. // SavePasswordToPrivateData Method
  316. //
  317. // Stores password in local security authority private data (LSA Secret).
  318. //---------------------------------------------------------------------------
  319. void __stdcall SavePasswordToPrivateData(LPCTSTR pszPassword)
  320. {
  321. LSA_HANDLE hPolicy = NULL;
  322. LSA_OBJECT_ATTRIBUTES loa = { sizeof(LSA_OBJECT_ATTRIBUTES), NULL, NULL, 0, NULL, NULL };
  323. NTSTATUS ntsStatus = LsaOpenPolicy(NULL, &loa, POLICY_CREATE_SECRET, &hPolicy);
  324. if (ntsStatus == STATUS_SUCCESS)
  325. {
  326. USHORT usPasswordLength = pszPassword ? wcslen(pszPassword) * sizeof(WCHAR) : 0;
  327. LSA_UNICODE_STRING lusKeyName = { c_usKeyNameLength, c_usKeyNameLength, const_cast<PWSTR>(c_wszKeyName) };
  328. LSA_UNICODE_STRING lusPrivateData = { usPasswordLength, usPasswordLength, const_cast<PWSTR>(pszPassword) };
  329. ntsStatus = LsaStorePrivateData(hPolicy, &lusKeyName, &lusPrivateData);
  330. LsaClose(hPolicy);
  331. if (ntsStatus != ERROR_SUCCESS)
  332. {
  333. _com_issue_error(HRESULT_FROM_WIN32(LsaNtStatusToWinError(ntsStatus)));
  334. }
  335. }
  336. else
  337. {
  338. _com_issue_error(HRESULT_FROM_WIN32(LsaNtStatusToWinError(ntsStatus)));
  339. }
  340. }
  341. //---------------------------------------------------------------------------
  342. // LoadPasswordFromPrivateData Function
  343. //
  344. // Retrieves password from local security authority private data (LSA Secret).
  345. //---------------------------------------------------------------------------
  346. _bstr_t __stdcall LoadPasswordFromPrivateData()
  347. {
  348. _bstr_t strPassword;
  349. LSA_HANDLE hPolicy = NULL;
  350. try
  351. {
  352. // open local security authority policy
  353. LSA_OBJECT_ATTRIBUTES loa = { sizeof(LSA_OBJECT_ATTRIBUTES), NULL, NULL, 0, NULL, NULL };
  354. NTSTATUS ntsStatus = LsaOpenPolicy(NULL, &loa, POLICY_GET_PRIVATE_INFORMATION, &hPolicy);
  355. if (ntsStatus == STATUS_SUCCESS)
  356. {
  357. // retrieve password from private data
  358. LSA_UNICODE_STRING lusKeyName = { c_usKeyNameLength, c_usKeyNameLength, const_cast<PWSTR>(c_wszKeyName) };
  359. PLSA_UNICODE_STRING plusPrivateData;
  360. ntsStatus = LsaRetrievePrivateData(hPolicy, &lusKeyName, &plusPrivateData);
  361. if (ntsStatus == STATUS_SUCCESS)
  362. {
  363. DWORD dwLength = plusPrivateData->Length / sizeof(WCHAR);
  364. WCHAR szPassword[64];
  365. wcsncpy(szPassword, plusPrivateData->Buffer, dwLength);
  366. szPassword[dwLength] = L'\0';
  367. strPassword = szPassword;
  368. LsaFreeMemory(plusPrivateData);
  369. }
  370. else
  371. {
  372. DWORD dwError = LsaNtStatusToWinError(ntsStatus);
  373. if (dwError != ERROR_FILE_NOT_FOUND)
  374. {
  375. _com_issue_error(HRESULT_FROM_WIN32(dwError));
  376. }
  377. }
  378. // close local security authority policy
  379. LsaClose(hPolicy);
  380. }
  381. else
  382. {
  383. _com_issue_error(HRESULT_FROM_WIN32(LsaNtStatusToWinError(ntsStatus)));
  384. }
  385. }
  386. catch (...)
  387. {
  388. if (hPolicy)
  389. {
  390. LsaClose(hPolicy);
  391. }
  392. throw;
  393. }
  394. return strPassword;
  395. }
  396. } // namespace nsAdmtAccount