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.

368 lines
10 KiB

  1. //
  2. // MODULE: RegistryPasswords.cpp
  3. //
  4. // PURPOSE: Handles the storing and retrieval of encrypted passwords in the registry.
  5. //
  6. // COMPANY: Saltmine Creative, Inc. (206)-284-7511 [email protected]
  7. //
  8. // AUTHOR: Randy Biley
  9. //
  10. // ORIGINAL DATE: 10-23-98
  11. //
  12. // NOTES: See RegistryPasswords.h
  13. //
  14. //
  15. // Version Date By Comments
  16. //--------------------------------------------------------------------
  17. // V3.0 10-23-98 RAB
  18. //
  19. #include "stdafx.h"
  20. #include "RegistryPasswords.h"
  21. #include "BaseException.h"
  22. #include "Event.h"
  23. #include "regutil.h"
  24. #ifndef CRYPT_MACHINE_KEYSET
  25. // This flag was exposed in Windows NT 4.0 Service Pack 2.
  26. #define CRYPT_MACHINE_KEYSET 0x00000020
  27. // By default, keys are stored in the HKEY_CURRENT_USER portion of the registry.
  28. // The CRYPT_MACHINE_KEYSET flag can be combined with all of the other flags,
  29. // indicating that the location for the key of interest is HKEY_LOCAL_MACHINE.
  30. // When combined with the CRYPT_NEW_KEYSET flag, the CRYPT_MACHINE_KEYSET flag
  31. // is useful when access is being performed from a service or user account that
  32. // did not log on interactively. This combination enables access to user specific
  33. // keys under HKEY_LOCAL_MACHINE.
  34. //
  35. // This setting is necessary in the the online troubleshooter in all
  36. // CryptAcquireContext() calls.
  37. #endif
  38. CRegistryPasswords::CRegistryPasswords(
  39. LPCTSTR szRegSoftwareLoc /* =REG_SOFTWARE_LOC */, // Registry Software Key location.
  40. LPCTSTR szRegThisProgram /* =REG_THIS_PROGRAM */, // Registry Program Name.
  41. LPCTSTR szKeyContainer /* =REG_THIS_PROGRAM */, // Key Container Name.
  42. LPCTSTR szHashString /* =HASH_SEED */ // Value used to seed the hash.
  43. )
  44. : m_hProv( NULL ), m_hHash( NULL ), m_hKey( NULL ), m_bAllValid( false )
  45. {
  46. try
  47. {
  48. m_strRegSoftwareLoc= szRegSoftwareLoc;
  49. m_strRegThisProgram= szRegThisProgram;
  50. // Attempt to acquire a handle to a particular key container.
  51. if (::CryptAcquireContext( &m_hProv, szKeyContainer,
  52. MS_DEF_PROV, // "Microsoft Base Cryptographic Provider v1.0"
  53. PROV_RSA_FULL, // This provider type supports both digital signatures
  54. // and data encryption, and is considered general purpose.
  55. // The RSA public-key algorithm is used for all public-key operations.
  56. CRYPT_MACHINE_KEYSET ) == FALSE)
  57. {
  58. // Attempt to create a particular key container and acquire handle
  59. if (::CryptAcquireContext( &m_hProv, szKeyContainer,
  60. MS_DEF_PROV,
  61. PROV_RSA_FULL,
  62. CRYPT_NEWKEYSET | CRYPT_MACHINE_KEYSET ) == FALSE)
  63. {
  64. throw CGenSysException( __FILE__, __LINE__, _T("AcquireContext"), ::GetLastError() );
  65. }
  66. }
  67. // Attempt to acquire a handle to a CSP hash object.
  68. /***
  69. Available hashing algorithms.
  70. CALG_HMACHMAC, a keyed hash algorithm
  71. CALG_MAC Message Authentication Code
  72. CALG_MD2 MD2
  73. CALG_MD5 MD5
  74. CALG_SHA US DSA Secure Hash Algorithm
  75. CALG_SHA1 Same as CALG_SHA
  76. CALG_SSL3_SHAMD5 SSL3 client authentication
  77. ***/
  78. if (::CryptCreateHash( m_hProv, CALG_SHA, 0, NULL, &m_hHash ) == FALSE)
  79. throw CGenSysException( __FILE__, __LINE__, _T("CreateHash"), ::GetLastError() );
  80. // Hash a string.
  81. if (::CryptHashData( m_hHash, (BYTE *) szHashString, _tcslen( szHashString ),
  82. NULL ) == FALSE)
  83. {
  84. throw CGenSysException( __FILE__, __LINE__, _T("HashData"), ::GetLastError() );
  85. }
  86. // Generate a cryptographic key derived from base data.
  87. if (::CryptDeriveKey( m_hProv,
  88. CALG_RC4, // RC4 stream encryption algorithm
  89. m_hHash, NULL, &m_hKey ) == FALSE)
  90. {
  91. throw CGenSysException( __FILE__, __LINE__, _T("DeriveKey"), ::GetLastError() );
  92. }
  93. // Toggle on flag to indicate that all Crypto handles have been initialized.
  94. m_bAllValid= true;
  95. }
  96. catch (CGenSysException& x)
  97. {
  98. // Log the error.
  99. LPVOID lpErrorMsgBuf;
  100. CString strErrorMsg;
  101. ::FormatMessage( FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM,
  102. NULL, x.GetErrorCode(),
  103. MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
  104. (LPTSTR) &lpErrorMsgBuf, 0, NULL );
  105. strErrorMsg.Format(_T("Encryption failure: %s"), (char *) lpErrorMsgBuf);
  106. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  107. CEvent::ReportWFEvent( x.GetSrcFileLineStr(),
  108. SrcLoc.GetSrcFileLineStr(),
  109. strErrorMsg, x.GetSystemErrStr(),
  110. EV_GTS_ERROR_ENCRYPTION );
  111. ::LocalFree(lpErrorMsgBuf);
  112. // Perform any cleanup.
  113. Destroy();
  114. }
  115. catch (...)
  116. {
  117. // Catch any other exceptions and do nothing.
  118. }
  119. }
  120. CRegistryPasswords::~CRegistryPasswords()
  121. {
  122. // Utilize function destroy to avoid potentially throwing an exception
  123. // within the destructor.
  124. Destroy();
  125. }
  126. bool CRegistryPasswords::WriteKey( const CString& RegKey, const CString& RegValue )
  127. {
  128. bool bRetVal= false;
  129. // Verify that the constructor worked properly.
  130. if (!m_bAllValid)
  131. return( bRetVal );
  132. // Verify that a key and a value were passed in.
  133. if ((!RegValue.IsEmpty()) && (!RegKey.IsEmpty()))
  134. {
  135. TCHAR *pBuffer;
  136. DWORD dwSize;
  137. if (EncryptKey( RegValue, &pBuffer, (LONG *)&dwSize ))
  138. {
  139. // Write the encrypted string to the registry.
  140. CRegUtil reg;
  141. bool was_created = false;
  142. if (reg.Create( HKEY_LOCAL_MACHINE, m_strRegSoftwareLoc, &was_created, KEY_QUERY_VALUE | KEY_WRITE))
  143. {
  144. if (reg.Create( m_strRegThisProgram, &was_created, KEY_READ | KEY_WRITE ))
  145. {
  146. if (reg.SetBinaryValue( RegKey, pBuffer, dwSize ))
  147. bRetVal= true;
  148. }
  149. }
  150. delete [] pBuffer;
  151. }
  152. }
  153. return( bRetVal );
  154. }
  155. // Note that if this returns true, *ppBuf will point to a new buffer on the heap.
  156. // The caller of this function is responsible for deleting that.
  157. bool CRegistryPasswords::EncryptKey( const CString& RegValue, char** ppBuf, long* plBufLen )
  158. {
  159. bool bRetVal= false;
  160. // Verify that the constructor worked properly.
  161. if (!m_bAllValid)
  162. return( bRetVal );
  163. if (!RegValue.IsEmpty())
  164. {
  165. BYTE* pData= NULL;
  166. DWORD dwSize= 0;
  167. // Set variable to length of data in buffer.
  168. dwSize= RegValue.GetLength();
  169. // Have API return us the required buffer size for encryption.
  170. if (::CryptEncrypt( m_hKey, 0, TRUE, NULL, NULL, &dwSize, dwSize ) == FALSE)
  171. {
  172. DWORD dwErr= ::GetLastError();
  173. CString strCryptErr;
  174. strCryptErr.Format( _T("%lu"), dwErr );
  175. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  176. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  177. SrcLoc.GetSrcFileLineStr(),
  178. RegValue, strCryptErr,
  179. EV_GTS_ERROR_ENCRYPTION );
  180. return( bRetVal );
  181. }
  182. // We now have a size for the output buffer, so create buffer.
  183. try
  184. {
  185. pData= new BYTE[ dwSize + 1 ];
  186. //[BC-03022001] - added check for NULL ptr to satisfy MS code analysis tool.
  187. if(!pData)
  188. throw bad_alloc();
  189. }
  190. catch (bad_alloc&)
  191. {
  192. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  193. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  194. SrcLoc.GetSrcFileLineStr(),
  195. _T("Failure to allocate"),
  196. _T("space to encrypt key"),
  197. EV_GTS_ERROR_ENCRYPTION );
  198. return( bRetVal );
  199. }
  200. memcpy( pData, RegValue, dwSize );
  201. pData[ dwSize ]= NULL;
  202. // Encrypt the passed in string.
  203. if (::CryptEncrypt( m_hKey, 0, TRUE, NULL, (BYTE *)pData, &dwSize, dwSize + 1 ) == FALSE)
  204. {
  205. // Log failure to encrypt.
  206. DWORD dwErr= ::GetLastError();
  207. CString strCryptErr;
  208. strCryptErr.Format( _T("%lu"), dwErr );
  209. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  210. CEvent::ReportWFEvent( SrcLoc.GetSrcFileLineStr(),
  211. SrcLoc.GetSrcFileLineStr(),
  212. RegValue, strCryptErr,
  213. EV_GTS_ERROR_ENCRYPTION );
  214. delete [] pData;
  215. }
  216. else
  217. {
  218. pData[ dwSize ]= 0;
  219. *ppBuf= (char*)pData;
  220. *plBufLen = dwSize;
  221. bRetVal= true;
  222. }
  223. }
  224. return( bRetVal );
  225. }
  226. bool CRegistryPasswords::KeyValidate( const CString& RegKey, const CString& RegValue )
  227. {
  228. bool bRetVal= false;
  229. // Verify that the constructor worked properly.
  230. if (!m_bAllValid)
  231. return( bRetVal );
  232. // Verify that a key and a value were passed in.
  233. if ((!RegValue.IsEmpty()) && (!RegKey.IsEmpty()))
  234. {
  235. CRegUtil reg;
  236. // Open up the desired key.
  237. if (reg.Open( HKEY_LOCAL_MACHINE, m_strRegSoftwareLoc, KEY_QUERY_VALUE ))
  238. {
  239. if (reg.Open( m_strRegThisProgram, KEY_QUERY_VALUE ))
  240. {
  241. TCHAR *pRegEncrypted;
  242. DWORD dwRegSize;
  243. TCHAR *pChkEncrypted;
  244. DWORD dwChkSize;
  245. // Attempt to read the current setting from the registry.
  246. if (reg.GetBinaryValue( RegKey, &pRegEncrypted, (LONG *)&dwRegSize ))
  247. {
  248. // Verify that the registry key had a previous value.
  249. if (dwRegSize < 1)
  250. {
  251. delete [] pRegEncrypted;
  252. return( bRetVal );
  253. }
  254. // Encrypt the passed in value.
  255. if (EncryptKey( RegValue, &pChkEncrypted, (LONG *)&dwChkSize ))
  256. {
  257. // Compare the two unencrypted strings.
  258. if (dwRegSize == dwChkSize)
  259. {
  260. if (!memcmp( pRegEncrypted, pChkEncrypted, dwRegSize ))
  261. bRetVal= true;
  262. }
  263. delete [] pChkEncrypted;
  264. }
  265. delete [] pRegEncrypted;
  266. }
  267. }
  268. }
  269. }
  270. return( bRetVal );
  271. }
  272. // This function is used to clean up from any potential exceptions thrown within the
  273. // ctor as well as standing in for the dtor.
  274. void CRegistryPasswords::Destroy()
  275. {
  276. try
  277. {
  278. // Toggle off flag that indicates valid Crypto handles.
  279. m_bAllValid= false;
  280. if (m_hKey)
  281. {
  282. if (::CryptDestroyKey( m_hKey ) == FALSE)
  283. throw CGenSysException( __FILE__, __LINE__,
  284. _T("Failure to destroy key"),
  285. EV_GTS_PASSWORD_EXCEPTION );
  286. m_hKey= NULL;
  287. }
  288. if (m_hHash)
  289. {
  290. if (::CryptDestroyHash( m_hHash ) == FALSE)
  291. throw CGenSysException( __FILE__, __LINE__,
  292. _T("Failure to destroy hash"),
  293. EV_GTS_PASSWORD_EXCEPTION );
  294. m_hHash= NULL;
  295. }
  296. if (m_hProv)
  297. {
  298. if (::CryptReleaseContext( m_hProv, 0 ) == FALSE)
  299. throw CGenSysException( __FILE__, __LINE__,
  300. _T("Failure to release context"),
  301. EV_GTS_PASSWORD_EXCEPTION );
  302. m_hProv= NULL;
  303. }
  304. }
  305. catch (CGenSysException& x)
  306. {
  307. CBuildSrcFileLinenoStr SrcLoc( __FILE__, __LINE__ );
  308. CEvent::ReportWFEvent( x.GetSrcFileLineStr(),
  309. SrcLoc.GetSrcFileLineStr(),
  310. x.GetErrorMsg(), x.GetSystemErrStr(),
  311. x.GetErrorCode() );
  312. }
  313. catch (...)
  314. {
  315. // Catch any other exceptions and do nothing.
  316. }
  317. return;
  318. }
  319. //
  320. // EOF.
  321. //