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.

475 lines
13 KiB

  1. // Copyright (C) 2002 Microsoft Corporation
  2. //
  3. // answerfile reader object
  4. //
  5. // 5 April 2002 sburns
  6. #include "headers.hxx"
  7. #include "AnswerFile.hpp"
  8. #include "resource.h"
  9. static const String SECTION_NAME(L"DCInstall");
  10. const String AnswerFile::OPTION_ADMIN_PASSWORD (L"AdministratorPassword");
  11. const String AnswerFile::OPTION_ALLOW_ANON_ACCESS (L"AllowAnonymousAccess");
  12. const String AnswerFile::OPTION_AUTO_CONFIG_DNS (L"AutoConfigDNS");
  13. const String AnswerFile::OPTION_CHILD_NAME (L"ChildName");
  14. const String AnswerFile::OPTION_CRITICAL_REPLICATION_ONLY (L"CriticalReplicationOnly");
  15. const String AnswerFile::OPTION_DATABASE_PATH (L"DatabasePath");
  16. const String AnswerFile::OPTION_DISABLE_CANCEL_ON_DNS_INSTALL (L"DisableCancelForDnsInstall");
  17. const String AnswerFile::OPTION_DNS_ON_NET (L"DNSOnNetwork");
  18. const String AnswerFile::OPTION_GC_CONFIRM (L"ConfirmGc");
  19. const String AnswerFile::OPTION_IS_LAST_DC (L"IsLastDCInDomain");
  20. const String AnswerFile::OPTION_LOG_PATH (L"LogPath");
  21. const String AnswerFile::OPTION_NEW_DOMAIN (L"NewDomain");
  22. const String AnswerFile::OPTION_NEW_DOMAIN_NAME (L"NewDomainDNSName");
  23. const String AnswerFile::OPTION_NEW_DOMAIN_NETBIOS_NAME (L"DomainNetbiosName");
  24. const String AnswerFile::OPTION_PARENT_DOMAIN_NAME (L"ParentDomainDNSName");
  25. const String AnswerFile::OPTION_PASSWORD (L"Password");
  26. const String AnswerFile::OPTION_REBOOT (L"RebootOnSuccess");
  27. const String AnswerFile::OPTION_REMOVE_APP_PARTITIONS (L"RemoveApplicationPartitions");
  28. const String AnswerFile::OPTION_REPLICATION_SOURCE (L"ReplicationSourceDC");
  29. const String AnswerFile::OPTION_REPLICA_DOMAIN_NAME (L"ReplicaDomainDNSName");
  30. const String AnswerFile::OPTION_REPLICA_OR_MEMBER (L"ReplicaOrMember");
  31. const String AnswerFile::OPTION_REPLICA_OR_NEW_DOMAIN (L"ReplicaOrNewDomain");
  32. const String AnswerFile::OPTION_SAFE_MODE_ADMIN_PASSWORD (L"SafeModeAdminPassword");
  33. const String AnswerFile::OPTION_SET_FOREST_VERSION (L"SetForestVersion");
  34. const String AnswerFile::OPTION_SITE_NAME (L"SiteName");
  35. const String AnswerFile::OPTION_SOURCE_PATH (L"ReplicationSourcePath");
  36. const String AnswerFile::OPTION_SYSKEY (L"Syskey");
  37. const String AnswerFile::OPTION_SYSVOL_PATH (L"SYSVOLPath");
  38. const String AnswerFile::OPTION_USERNAME (L"UserName");
  39. const String AnswerFile::OPTION_USER_DOMAIN (L"UserDomain");
  40. const String AnswerFile::VALUE_CHILD (L"Child");
  41. const String AnswerFile::VALUE_DOMAIN (L"Domain");
  42. const String AnswerFile::VALUE_NO (L"No");
  43. const String AnswerFile::VALUE_NO_DONT_PROMPT (L"NoAndNoPromptEither");
  44. const String AnswerFile::VALUE_REPLICA (L"Replica");
  45. const String AnswerFile::VALUE_TREE (L"Tree");
  46. const String AnswerFile::VALUE_YES (L"Yes");
  47. static StringList PASSWORD_OPTIONS_LIST;
  48. static bool passwordOptionsListInitialized = false;
  49. void
  50. GetAllKeys(const String& filename, StringList& resultList)
  51. {
  52. LOG_FUNCTION(GetAllKeys);
  53. ASSERT(FS::IsValidPath(filename));
  54. resultList.clear();
  55. // our first call is with a large buffer, hoping that it will suffice...
  56. #ifdef DBG
  57. // on chk builds, use a small buffer size so that our growth algorithm
  58. // gets exercised
  59. unsigned bufSizeInCharacters = 3;
  60. #else
  61. unsigned bufSizeInCharacters = 1023;
  62. #endif
  63. PWSTR buffer = 0;
  64. do
  65. {
  66. buffer = new WCHAR[bufSizeInCharacters + 1];
  67. // REVIEWED-2002/02/22-sburns byte count correctly passed in
  68. ::ZeroMemory(buffer, (bufSizeInCharacters + 1) * sizeof WCHAR);
  69. DWORD result =
  70. // REVIEWED-2002/02/22-sburns call correctly passes size in characters.
  71. ::GetPrivateProfileString(
  72. SECTION_NAME.c_str(),
  73. 0,
  74. L"default",
  75. buffer,
  76. bufSizeInCharacters,
  77. filename.c_str());
  78. if (!result)
  79. {
  80. break;
  81. }
  82. // A value was found. check to see if it was truncated. Since lpKeyName
  83. // was null, check result against character count - 2
  84. if (result == bufSizeInCharacters - 2)
  85. {
  86. // buffer was too small, so the value was truncated. Resize the
  87. // buffer and try again.
  88. // no need to scribble out the buffer: we're retrieving key names,
  89. // not values.
  90. delete[] buffer;
  91. bufSizeInCharacters *= 2;
  92. if (bufSizeInCharacters > USHRT_MAX) // effectively ~32K max
  93. {
  94. // too big. way too big. We'll make do with the truncated value.
  95. ASSERT(false);
  96. break;
  97. }
  98. continue;
  99. }
  100. // copy out the strings results into list elements
  101. PWSTR p = buffer;
  102. while (*p)
  103. {
  104. resultList.push_back(p);
  105. // REVIEWED-2002/04/08-sburns wcslen is ok, since we arrange for
  106. // the buffer to be null terminated
  107. p += wcslen(p) + 1;
  108. }
  109. break;
  110. }
  111. //lint -e506 ok that this looks like "loop forever"
  112. while (true);
  113. delete[] buffer;
  114. }
  115. AnswerFile::AnswerFile(const String& filename_)
  116. :
  117. filename(filename_),
  118. isSafeModePasswordPresent(false)
  119. {
  120. LOG_CTOR(AnswerFile);
  121. // the caller is expected to have verified this
  122. ASSERT(FS::PathExists(filename));
  123. GetAllKeys(filename, keysPresent);
  124. isSafeModePasswordPresent = IsKeyPresent(OPTION_SAFE_MODE_ADMIN_PASSWORD);
  125. // remove read-only file attribute
  126. DWORD attrs = 0;
  127. HRESULT hr = Win::GetFileAttributes(filename, attrs);
  128. if (SUCCEEDED(hr) && attrs & FILE_ATTRIBUTE_READONLY)
  129. {
  130. LOG(L"Removing readonly attribute on " + filename);
  131. hr = Win::SetFileAttributes(filename, attrs & ~FILE_ATTRIBUTE_READONLY);
  132. // if this failed, well, we tried. The user runs the risk of cleartext
  133. // passwords left in his file.
  134. LOG_HRESULT(hr);
  135. }
  136. // Read all the password options into the encrypted value map, erasing
  137. // them as we go.
  138. if (!passwordOptionsListInitialized)
  139. {
  140. ASSERT(PASSWORD_OPTIONS_LIST.empty());
  141. PASSWORD_OPTIONS_LIST.clear();
  142. PASSWORD_OPTIONS_LIST.push_back(OPTION_PASSWORD);
  143. PASSWORD_OPTIONS_LIST.push_back(OPTION_ADMIN_PASSWORD);
  144. PASSWORD_OPTIONS_LIST.push_back(OPTION_SYSKEY);
  145. PASSWORD_OPTIONS_LIST.push_back(OPTION_SAFE_MODE_ADMIN_PASSWORD);
  146. passwordOptionsListInitialized = true;
  147. }
  148. String empty;
  149. for (
  150. StringList::iterator i = PASSWORD_OPTIONS_LIST.begin();
  151. i != PASSWORD_OPTIONS_LIST.end();
  152. ++i)
  153. {
  154. if (IsKeyPresent(*i))
  155. {
  156. ovMap[*i] = EncryptedReadKey(*i);
  157. hr = WriteKey(*i, empty);
  158. if (FAILED(hr))
  159. {
  160. popup.Error(
  161. Win::GetDesktopWindow(),
  162. hr,
  163. String::format(
  164. IDS_FAILED_PASSWORD_WRITE_TO_ANSWERFILE,
  165. i->c_str(),
  166. filename.c_str()));
  167. }
  168. }
  169. }
  170. }
  171. AnswerFile::~AnswerFile()
  172. {
  173. LOG_DTOR(AnswerFile);
  174. }
  175. String
  176. AnswerFile::ReadKey(const String& key) const
  177. {
  178. LOG_FUNCTION2(AnswerFile::ReadKey, key);
  179. ASSERT(!key.empty());
  180. String result =
  181. // REVIEWED-2002/02/22-sburns no cch/cb issue here.
  182. Win::GetPrivateProfileString(SECTION_NAME, key, String(), filename);
  183. // Don't log the value, as it may be a password.
  184. // LOG(L"value=" + result);
  185. return result.strip(String::BOTH);
  186. }
  187. EncryptedString
  188. AnswerFile::EncryptedReadKey(const String& key) const
  189. {
  190. LOG_FUNCTION2(AnswerFile::EncodedReadKey, key);
  191. ASSERT(!key.empty());
  192. EncryptedString retval;
  193. #ifdef DBG
  194. // on chk builds, use a small buffer size so that our growth algorithm
  195. // gets exercised
  196. unsigned bufSizeInCharacters = 3;
  197. #else
  198. unsigned bufSizeInCharacters = 1023;
  199. #endif
  200. PWSTR buffer = 0;
  201. do
  202. {
  203. // +1 for extra null-termination paranoia
  204. buffer = new WCHAR[bufSizeInCharacters + 1];
  205. // REVIEWED-2002/02/22-sburns byte count correctly passed in
  206. ::ZeroMemory(buffer, (bufSizeInCharacters + 1) * sizeof WCHAR);
  207. DWORD result =
  208. // REVIEWED-2002/02/22-sburns call correctly passes size in characters.
  209. ::GetPrivateProfileString(
  210. SECTION_NAME.c_str(),
  211. key.c_str(),
  212. L"",
  213. buffer,
  214. bufSizeInCharacters,
  215. filename.c_str());
  216. if (!result)
  217. {
  218. break;
  219. }
  220. // A value was found. check to see if it was truncated. neither
  221. // lpAppName nor lpKeyName were null, so check result against character
  222. // count - 1
  223. if (result == bufSizeInCharacters - 1)
  224. {
  225. // buffer was too small, so the value was truncated. Resize the
  226. // buffer and try again.
  227. // Since the buffer may have contained passwords, scribble it
  228. // out
  229. // REVIEWED-2002/02/22-sburns byte count correctly passed in
  230. ::SecureZeroMemory(buffer, (bufSizeInCharacters + 1) * sizeof WCHAR);
  231. delete[] buffer;
  232. bufSizeInCharacters *= 2;
  233. if (bufSizeInCharacters > USHRT_MAX) // effectively ~32K max
  234. {
  235. // too big. way too big. We'll make do with the truncated value.
  236. ASSERT(false);
  237. break;
  238. }
  239. continue;
  240. }
  241. // don't need to trim whitespace, GetPrivateProfileString does that
  242. // for us.
  243. retval.Encrypt(buffer);
  244. break;
  245. }
  246. while (true);
  247. // Since the buffer may have contained passwords, scribble it
  248. // out
  249. // REVIEWED-2002/02/22-sburns byte count correctly passed in
  250. ::SecureZeroMemory(buffer, (bufSizeInCharacters + 1) * sizeof WCHAR);
  251. delete[] buffer;
  252. // Don't log the value, as it may be a password.
  253. // LOG(L"value=" + result);
  254. return retval;
  255. }
  256. HRESULT
  257. AnswerFile::WriteKey(const String& key, const String& value) const
  258. {
  259. LOG_FUNCTION2(AnswerFile::WriteKey, key);
  260. ASSERT(!key.empty());
  261. HRESULT hr =
  262. Win::WritePrivateProfileString(SECTION_NAME, key, value, filename);
  263. return hr;
  264. }
  265. bool
  266. AnswerFile::IsKeyPresent(const String& key) const
  267. {
  268. LOG_FUNCTION2(AnswerFile::IsKeyPresent, key);
  269. ASSERT(!key.empty());
  270. bool result = false;
  271. // If GetAllKeys failed, then we won't find the option in the keys list
  272. // and will assume that the option is not specified. This is the best
  273. // we can do in the case where we can't read the keys.
  274. if (
  275. std::find(keysPresent.begin(), keysPresent.end(), key)
  276. != keysPresent.end() )
  277. {
  278. result = true;
  279. }
  280. LOG_BOOL(result);
  281. return result;
  282. }
  283. bool
  284. IsPasswordOption(const String& option)
  285. {
  286. ASSERT(passwordOptionsListInitialized);
  287. bool result = false;
  288. if (
  289. std::find(
  290. PASSWORD_OPTIONS_LIST.begin(),
  291. PASSWORD_OPTIONS_LIST.end(),
  292. option)
  293. != PASSWORD_OPTIONS_LIST.end() )
  294. {
  295. result = true;
  296. }
  297. return result;
  298. }
  299. String
  300. AnswerFile::GetOption(const String& option) const
  301. {
  302. LOG_FUNCTION2(AnswerFile::GetOption, option);
  303. String result = ReadKey(option);
  304. if (!IsPasswordOption(option))
  305. {
  306. LOG(result);
  307. }
  308. else
  309. {
  310. // should use GetEncryptedAnswerFileOption for passwords
  311. ASSERT(false);
  312. }
  313. return result;
  314. }
  315. EncryptedString
  316. AnswerFile::GetEncryptedOption(const String& option) const
  317. {
  318. LOG_FUNCTION2(AnswerFile::GetEncryptedOption, option);
  319. OptionEncryptedValueMap::const_iterator ci = ovMap.find(option);
  320. if (ci != ovMap.end())
  321. {
  322. return ci->second;
  323. }
  324. return EncryptedString();
  325. }
  326. bool
  327. AnswerFile::IsSafeModeAdminPwdOptionPresent() const
  328. {
  329. LOG_FUNCTION(AnswerFile::IsSafeModeAdminPwdOptionPresent);
  330. LOG_BOOL(isSafeModePasswordPresent);
  331. return isSafeModePasswordPresent;
  332. }