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.

400 lines
9.8 KiB

  1. #include "stdinc.h"
  2. #define SHA1_HASH_SIZE_BYTES ( 160 / 8 )
  3. #define HASHFLAG_AUTODETECT ( 0x0001 )
  4. #define HASHFLAG_STRAIGHT_HASH ( 0x0002 )
  5. #define HASHFLAG_PROCESS_IMAGE ( 0x0004 )
  6. const wstring XML_ATTRIBUTE_NAME = (L"name");
  7. HRESULT FreshenFileNodeHashes( PVOID, int argc, WCHAR *argv[] );
  8. HRESULT ProcessSingleFileNode( ISXSManifestPtr pDocument, IXMLDOMNode *pDocNode );
  9. BOOL
  10. SxspCreateFileHash(
  11. DWORD dwFlags,
  12. ALG_ID PreferredAlgorithm,
  13. wstring pwsFileName,
  14. wstring &HashBytes
  15. );
  16. const wstring p_bstHashAlgName = (L"SHA1");
  17. wstring
  18. ConvertHashToString( std::vector<BYTE> Hash )
  19. {
  20. wstringstream output;
  21. output << hex;
  22. output.fill(L'0');
  23. output.width(2);
  24. for ( std::vector<BYTE>::const_iterator it = Hash.begin(); it != Hash.end(); it++ )
  25. {
  26. output << *it;
  27. }
  28. return output.str();
  29. }
  30. HRESULT
  31. ProcessSingleFileNode(
  32. ISXSManifestPtr Document,
  33. ::ATL::CComPtr<IXMLDOMNode> DocNode
  34. )
  35. {
  36. //
  37. // Here, we're dealing with a single file. So, we have to go and see if
  38. // there's a Verification subtag of this file, and process it properly.
  39. //
  40. wstring bstFileName;
  41. wstring bstNamespace, bstPrefix;
  42. ::ATL::CComPtr<IXMLDOMNamedNodeMap> Attributes;
  43. ::ATL::CComPtr<IXMLDOMNode> Dump;
  44. HRESULT hr;
  45. wstring Hash;
  46. //
  47. // So we get the attributes of this node, which should contain the file
  48. // name and hash information.
  49. //
  50. if ( FAILED( hr = DocNode->get_attributes( &Attributes ) ) )
  51. goto lblGetOut;
  52. //
  53. // Now just the values out
  54. //
  55. SxspSimplifyGetAttribute( Attributes, XML_ATTRIBUTE_NAME, bstFileName, ASM_NAMESPACE_URI );
  56. //
  57. // Now we use this to gather information about the file, and to fix
  58. // the values in the hash entry if need be.
  59. //
  60. if (::SxspCreateFileHash(HASHFLAG_AUTODETECT, CALG_SHA1, bstFileName, Hash))
  61. {
  62. //
  63. // Write the data back into the node, don't change the file name at all
  64. //
  65. ::ATL::CComPtr<IXMLDOMNode> Dump;
  66. Attributes->removeNamedItem( _bstr_t(L"hash"), &Dump );
  67. Attributes->removeNamedItem( _bstr_t(L"hashalg"), &Dump );
  68. SxspSimplifyPutAttribute( Document, Attributes, L"hash", Hash );
  69. SxspSimplifyPutAttribute( Document, Attributes, L"hashalg", p_bstHashAlgName );
  70. {
  71. wstringstream ss;
  72. ss << bstFileName.c_str() << wstring(L" hashed to ") << Hash.c_str();
  73. ReportError( ErrorSpew, ss );
  74. }
  75. }
  76. else
  77. {
  78. wstringstream ss;
  79. ss << wstring(L"Unable to create hash for file ") << bstFileName.c_str();
  80. ReportError( ErrorWarning, ss );
  81. goto lblGetOut;
  82. }
  83. lblGetOut:
  84. return hr;
  85. }
  86. wstring AssemblyFileXSLPattern = (L"/assembly/file");
  87. bool UpdateManifestHashes( const CPostbuildProcessListEntry& item )
  88. {
  89. ISXSManifestPtr document;
  90. ::ATL::CComPtr<IXMLDOMElement> rootElement;
  91. ::ATL::CComPtr<IXMLDOMNodeList> fileTags;
  92. ::ATL::CComPtr<IXMLDOMNode> fileNode;
  93. HRESULT hr = S_OK;
  94. if ( FAILED(hr = ConstructXMLDOMObject( item.getManifestFullPath(), document )) )
  95. {
  96. wstringstream ss;
  97. ss << wstring(L"Failed opening the manifest ") << item.getManifestFullPath()
  98. << wstring(L" for input under the DOM.");
  99. ReportError( ErrorFatal, ss );
  100. return false;
  101. }
  102. if ( FAILED(document->get_documentElement( &rootElement ) ) )
  103. {
  104. wstringstream ss;
  105. ss << wstring(L"The manifest ") << item.getManifestFullPath() << wstring(L" may be malformed,")
  106. << wstring(L" as we were unable to load the root element!");
  107. ReportError( ErrorFatal, ss );
  108. return false;
  109. }
  110. //
  111. // Now, let's select all the 'file' nodes under 'assembly' tags:
  112. //
  113. hr = document->selectNodes( _bstr_t(AssemblyFileXSLPattern.c_str()), &fileTags );
  114. if ( FAILED(hr) )
  115. {
  116. wstringstream ss;
  117. ss << wstring(L"Unable to select the file nodes under this assembly tag, can't proceed.");
  118. ReportError( ErrorFatal, ss );
  119. }
  120. long length;
  121. fileTags->get_length( &length );
  122. //
  123. // And for each, process it
  124. //
  125. fileTags->reset();
  126. while ( SUCCEEDED(fileTags->nextNode( &fileNode ) ) )
  127. {
  128. //
  129. // All done
  130. //
  131. if ( fileNode == NULL )
  132. {
  133. break;
  134. }
  135. SetCurrentDirectoryW( item.getManifestPathOnly().c_str() );
  136. ProcessSingleFileNode( document, fileNode );
  137. }
  138. if ( FAILED( hr = document->save( _variant_t(_bstr_t(item.getManifestFullPath().c_str())) ) ) )
  139. {
  140. wstringstream ss;
  141. ss << wstring(L"Unable to save manifest ") << item.getManifestFullPath()
  142. << wstring(L" back after updating hashes! Changes will be lost.");
  143. ReportError( ErrorFatal, ss );
  144. }
  145. return true;
  146. }
  147. //
  148. // HUGE HACKHACK
  149. //
  150. // There has to be some nice way of including this code (which otherwise lives
  151. // in hashfile.cpp in the fusion\dll\whistler tree) other than just glomping
  152. // it here.
  153. //
  154. BOOL
  155. ImageDigesterFunc(
  156. DIGEST_HANDLE hSomething,
  157. PBYTE pbDataBlock,
  158. DWORD dwLength
  159. )
  160. {
  161. return CryptHashData( (HCRYPTHASH)hSomething, pbDataBlock, dwLength, 0 );
  162. }
  163. BOOL
  164. pSimpleHashRoutine(
  165. HCRYPTHASH hHash,
  166. HANDLE hFile
  167. )
  168. {
  169. static const DWORD FILE_BUFFER = 64 * 1024;
  170. BYTE pbBuffer[FILE_BUFFER];
  171. DWORD dwDataRead;
  172. BOOL b = FALSE;
  173. BOOL bKeepReading = TRUE;
  174. while ( bKeepReading )
  175. {
  176. b = ReadFile( hFile, pbBuffer, FILE_BUFFER, &dwDataRead, NULL );
  177. if ( b && ( dwDataRead == 0 ) )
  178. {
  179. bKeepReading = FALSE;
  180. b = TRUE;
  181. continue;
  182. }
  183. else if ( !b )
  184. {
  185. WCHAR ws[8192];
  186. FormatMessageW(
  187. FORMAT_MESSAGE_FROM_SYSTEM,
  188. NULL,
  189. ::GetLastError(),
  190. MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ),
  191. ws,
  192. 0,
  193. NULL
  194. );
  195. bKeepReading = FALSE;
  196. continue;
  197. }
  198. if ( CryptHashData( hHash, pbBuffer, dwDataRead, 0 ) == 0 )
  199. {
  200. b = FALSE;
  201. break;
  202. }
  203. }
  204. return b;
  205. }
  206. BOOL
  207. pImageHashRoutine(
  208. HCRYPTHASH hHash,
  209. HANDLE hFile
  210. )
  211. {
  212. return ImageGetDigestStream(
  213. hFile,
  214. CERT_PE_IMAGE_DIGEST_ALL_IMPORT_INFO,
  215. ImageDigesterFunc,
  216. (DIGEST_HANDLE)hHash
  217. );
  218. }
  219. BOOL
  220. SxspCreateFileHash(DWORD dwFlags,
  221. ALG_ID PreferredAlgorithm,
  222. wstring pwsFileName,
  223. wstring &HashBytes
  224. )
  225. {
  226. BOOL fSuccessCode = FALSE;
  227. HCRYPTPROV hProvider;
  228. HCRYPTHASH hCurrentHash;
  229. HANDLE hFile;
  230. // Initialization
  231. hProvider = (HCRYPTPROV)INVALID_HANDLE_VALUE;
  232. hCurrentHash = (HCRYPTHASH)INVALID_HANDLE_VALUE;
  233. hFile = INVALID_HANDLE_VALUE;
  234. //
  235. // First try and open the file. No sense in doing anything else if we
  236. // can't get to the data to start with. Use a very friendly set of
  237. // rights to check the file. Future users might want to be sure that
  238. // you're in the right security context before doing this - system
  239. // level to check system files, etc.
  240. //
  241. hFile = CreateFileW(
  242. pwsFileName.c_str(),
  243. GENERIC_READ,
  244. FILE_SHARE_READ,
  245. NULL,
  246. OPEN_EXISTING,
  247. FILE_FLAG_SEQUENTIAL_SCAN,
  248. NULL
  249. );
  250. if ( hFile == INVALID_HANDLE_VALUE ) {
  251. return FALSE;
  252. }
  253. //
  254. // Create a cryptological provider that supports everything RSA needs.
  255. //
  256. if (!CryptAcquireContextW(&hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
  257. goto lblCleanup;
  258. //
  259. // We'll be using SHA1 for the file hash
  260. //
  261. if ( !CryptCreateHash( hProvider, PreferredAlgorithm, 0, 0, &hCurrentHash ) )
  262. goto lblCleanup;
  263. //
  264. // So first try hashing it via the image, and if that fails, try the
  265. // normal file-reading hash routine instead.
  266. //
  267. if ( dwFlags & HASHFLAG_AUTODETECT )
  268. {
  269. if ( !pImageHashRoutine( hCurrentHash, hFile ) )
  270. {
  271. //
  272. // Oops, the image-hasher wasn't happy. Let's try the straight
  273. // hasher instead.
  274. //
  275. if ( !pSimpleHashRoutine( hCurrentHash, hFile ) )
  276. {
  277. goto lblCleanup;
  278. }
  279. else
  280. {
  281. fSuccessCode = TRUE;
  282. }
  283. }
  284. else
  285. {
  286. fSuccessCode = TRUE;
  287. }
  288. }
  289. else if ( dwFlags & HASHFLAG_STRAIGHT_HASH )
  290. {
  291. fSuccessCode = pSimpleHashRoutine( hCurrentHash, hFile );
  292. }
  293. else if ( dwFlags & HASHFLAG_PROCESS_IMAGE )
  294. {
  295. fSuccessCode = pImageHashRoutine( hCurrentHash, hFile );
  296. }
  297. else
  298. {
  299. ::SetLastError( ERROR_INVALID_PARAMETER );
  300. goto lblCleanup;
  301. }
  302. //
  303. // We know the buffer is the right size, so we just call down to the hash parameter
  304. // getter, which will be smart and bop out (setting the pdwDestinationSize parameter)
  305. // if the user passed an invalid parameter.
  306. //
  307. if ( fSuccessCode )
  308. {
  309. wstringstream ss;
  310. DWORD dwSize, dwDump;
  311. BYTE *pb = NULL;
  312. fSuccessCode = CryptGetHashParam( hCurrentHash, HP_HASHSIZE, (BYTE*)&dwSize, &(dwDump=sizeof(dwSize)), 0 );
  313. if ( !fSuccessCode || ( pb = new BYTE[dwSize] ) == NULL ) {
  314. goto lblCleanup;
  315. }
  316. fSuccessCode = CryptGetHashParam( hCurrentHash, HP_HASHVAL, pb, &dwSize, 0);
  317. if ( !fSuccessCode ) {
  318. delete[] pb;
  319. goto lblCleanup;
  320. }
  321. for ( dwDump = 0; dwDump < dwSize; dwDump++ ) {
  322. ss << hex;
  323. ss.fill('0');
  324. ss.width(2);
  325. ss << (unsigned int)pb[dwDump];
  326. }
  327. HashBytes = ss.str();
  328. delete[] pb;
  329. }
  330. lblCleanup:
  331. DWORD dwLastError = ::GetLastError();
  332. if ( hFile != INVALID_HANDLE_VALUE )
  333. {
  334. CloseHandle( hFile );
  335. }
  336. //
  337. // We just destroy the hash and the crypto context blindly. If they were
  338. // invalid before, the release and destroy would just return with a failure,
  339. // not an exception or fault.
  340. //
  341. CryptDestroyHash( hCurrentHash );
  342. CryptReleaseContext( hProvider, 0 );
  343. ::SetLastError( dwLastError );
  344. return fSuccessCode;
  345. }