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.

534 lines
16 KiB

  1. //+-------------------------------------------------------------------------
  2. // Microsoft Windows
  3. //
  4. // Copyright (C) Microsoft Corporation, 2001 - 2001
  5. //
  6. // File: verfile.cpp
  7. //
  8. // Contents: Minimal Cryptographic functions to hash files and verify
  9. // Authenticode signed files.
  10. // message
  11. //
  12. // Functions: MinCryptHashFile
  13. // MinCryptVerifySignedFile
  14. //
  15. // History: 21-Jan-01 philh created
  16. //--------------------------------------------------------------------------
  17. #include "global.hxx"
  18. #include <md5.h>
  19. #include <sha.h>
  20. #define PE_EXE_HEADER_TAG "MZ"
  21. #define MIN_PE_FILE_LEN 4
  22. #define MAX_SIGNED_FILE_AUTH_ATTR_CNT 10
  23. typedef struct _DIGEST_DATA {
  24. ALG_ID AlgId;
  25. void *pvSHA1orMD5Ctx;
  26. } DIGEST_DATA, *PDIGEST_DATA;
  27. // #define SPC_INDIRECT_DATA_OBJID "1.3.6.1.4.1.311.2.1.4"
  28. const BYTE rgbSPC_INDIRECT_DATA_OBJID[] =
  29. {0x2B, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x04};
  30. BOOL
  31. WINAPI
  32. I_DigestFunction(
  33. DIGEST_HANDLE refdata,
  34. PBYTE pbData,
  35. DWORD cbData
  36. )
  37. {
  38. PDIGEST_DATA pDigestData = (PDIGEST_DATA) refdata;
  39. switch (pDigestData->AlgId)
  40. {
  41. case CALG_MD5:
  42. MD5Update((MD5_CTX *)pDigestData->pvSHA1orMD5Ctx, pbData, cbData);
  43. return(TRUE);
  44. case CALG_SHA1:
  45. A_SHAUpdate((A_SHA_CTX *)pDigestData->pvSHA1orMD5Ctx, pbData,
  46. cbData);
  47. return(TRUE);
  48. }
  49. return FALSE;
  50. }
  51. BOOL
  52. WINAPI
  53. I_IsNtPe32File(
  54. IN PCRYPT_DATA_BLOB pFileBlob
  55. )
  56. {
  57. const BYTE *pbFile = pFileBlob->pbData;
  58. DWORD cbFile = pFileBlob->cbData;
  59. if (MIN_PE_FILE_LEN > cbFile)
  60. return FALSE;
  61. if (0 != memcmp(&pbFile[0], PE_EXE_HEADER_TAG, strlen(PE_EXE_HEADER_TAG)))
  62. return FALSE;
  63. // Make sure it is a 32 bit PE
  64. if (sizeof(IMAGE_DOS_HEADER) > cbFile)
  65. return FALSE;
  66. else {
  67. IMAGE_DOS_HEADER *pDosHead = (IMAGE_DOS_HEADER *) pbFile;
  68. if (pDosHead->e_magic != IMAGE_DOS_SIGNATURE)
  69. return FALSE;
  70. if (cbFile < (sizeof(IMAGE_DOS_HEADER) + pDosHead->e_lfanew))
  71. return FALSE;
  72. else {
  73. IMAGE_NT_HEADERS *pNTHead =
  74. (IMAGE_NT_HEADERS *)((ULONG_PTR)pDosHead + pDosHead->e_lfanew);
  75. if (pNTHead->Signature != IMAGE_NT_SIGNATURE)
  76. return FALSE;
  77. }
  78. }
  79. return TRUE;
  80. }
  81. //+-------------------------------------------------------------------------
  82. // Hashes the file according to the Hash ALG_ID.
  83. //
  84. // According to dwFileType, pvFile can be a pwszFilename, hFile or pFileBlob.
  85. // Only requires READ access.
  86. //
  87. // dwFileType:
  88. // MINCRYPT_FILE_NAME : pvFile - LPCWSTR pwszFilename
  89. // MINCRYPT_FILE_HANDLE : pvFile - HANDLE hFile
  90. // MINCRYPT_FILE_BLOB : pvFile - PCRYPT_DATA_BLOB pFileBlob
  91. //
  92. // rgbHash is updated with the resultant hash. *pcbHash is updated with
  93. // the length associated with the hash algorithm.
  94. //
  95. // If the function succeeds, the return value is ERROR_SUCCESS. Otherwise,
  96. // a nonzero error code is returned.
  97. //
  98. // Only CALG_SHA1 and CALG_MD5 are supported.
  99. //
  100. // If a NT PE 32 bit file format, hashed according to imagehlp rules, ie, skip
  101. // section containing potential signature, ... . Otherwise, the entire file
  102. // is hashed.
  103. //--------------------------------------------------------------------------
  104. LONG
  105. WINAPI
  106. MinCryptHashFile(
  107. IN DWORD dwFileType,
  108. IN const VOID *pvFile,
  109. IN ALG_ID HashAlgId,
  110. OUT BYTE rgbHash[MINCRYPT_MAX_HASH_LEN],
  111. OUT DWORD *pcbHash
  112. )
  113. {
  114. LONG lErr;
  115. CRYPT_DATA_BLOB FileBlob = {0, NULL};
  116. DIGEST_DATA DigestData;
  117. A_SHA_CTX ShaCtx;
  118. MD5_CTX Md5Ctx;
  119. __try {
  120. lErr = I_MinCryptMapFile(
  121. dwFileType,
  122. pvFile,
  123. &FileBlob
  124. );
  125. if (ERROR_SUCCESS != lErr)
  126. goto ErrorReturn;
  127. if (!I_IsNtPe32File(&FileBlob)) {
  128. // Hash the entire file
  129. lErr = MinCryptHashMemory(
  130. HashAlgId,
  131. 1, // cBlob
  132. &FileBlob,
  133. rgbHash,
  134. pcbHash
  135. );
  136. goto CommonReturn;
  137. }
  138. DigestData.AlgId = HashAlgId;
  139. switch (HashAlgId) {
  140. case CALG_MD5:
  141. DigestData.pvSHA1orMD5Ctx = &Md5Ctx;
  142. MD5Init(&Md5Ctx);
  143. break;
  144. case CALG_SHA1:
  145. DigestData.pvSHA1orMD5Ctx = &ShaCtx;
  146. A_SHAInit(&ShaCtx);
  147. break;
  148. default:
  149. goto InvalidHashAlgId;
  150. }
  151. if (!imagehack_ImageGetDigestStream(
  152. &FileBlob,
  153. 0, // DigestLevel, ignored
  154. I_DigestFunction,
  155. &DigestData
  156. ))
  157. goto DigestStreamError;
  158. else {
  159. DWORD dwPadBeforeCerts;
  160. dwPadBeforeCerts = (FileBlob.cbData + 7) & ~7;
  161. dwPadBeforeCerts -= FileBlob.cbData;
  162. if (0 < dwPadBeforeCerts) {
  163. BYTE rgb[8];
  164. // imagehlp put nulls before the signature!
  165. memset(rgb, 0x00, dwPadBeforeCerts);
  166. if (!I_DigestFunction(&DigestData, rgb, dwPadBeforeCerts))
  167. goto DigestFunctionError;
  168. }
  169. }
  170. switch (HashAlgId) {
  171. case CALG_MD5:
  172. MD5Final(&Md5Ctx);
  173. memcpy(rgbHash, Md5Ctx.digest, MD5DIGESTLEN);
  174. *pcbHash = MINCRYPT_MD5_HASH_LEN;
  175. break;
  176. case CALG_SHA1:
  177. A_SHAFinal(&ShaCtx, rgbHash);
  178. *pcbHash = MINCRYPT_SHA1_HASH_LEN;
  179. break;
  180. default:
  181. goto InvalidHashAlgId;
  182. }
  183. } __except(EXCEPTION_EXECUTE_HANDLER) {
  184. lErr = GetExceptionCode();
  185. if (ERROR_SUCCESS == lErr)
  186. lErr = E_UNEXPECTED;
  187. goto ErrorReturn;
  188. }
  189. lErr = ERROR_SUCCESS;
  190. CommonReturn:
  191. //**********************************************************************
  192. // WARNING!!!!
  193. //
  194. // UnmapViewOfFile is in another DLL, kernel32.dll.
  195. // lErr and the return hash in rgbHash[] must be protected.
  196. //
  197. //**********************************************************************
  198. if (MINCRYPT_FILE_BLOB != dwFileType && NULL != FileBlob.pbData)
  199. UnmapViewOfFile(FileBlob.pbData);
  200. return lErr;
  201. ErrorReturn:
  202. *pcbHash = 0;
  203. goto CommonReturn;
  204. InvalidHashAlgId:
  205. lErr = NTE_BAD_ALGID;
  206. goto ErrorReturn;
  207. DigestStreamError:
  208. DigestFunctionError:
  209. lErr = NTE_BAD_HASH;
  210. goto ErrorReturn;
  211. }
  212. // Only called when cAttrOID != 0
  213. LONG
  214. WINAPI
  215. I_GetAuthAttributes(
  216. IN PCRYPT_DER_BLOB pAttrsValueBlob,
  217. IN DWORD cAttrOID,
  218. IN CRYPT_DER_BLOB rgAttrEncodedOIDBlob[],
  219. // CRYPT_DER_BLOB rgAttrBlob[cAttrOID] header is at beginning
  220. // with the bytes pointed to immediately following
  221. OUT OPTIONAL CRYPT_DER_BLOB *rgAttrValueBlob,
  222. IN OUT DWORD *pcbAttr
  223. )
  224. {
  225. LONG lErr;
  226. DWORD i;
  227. LONG lRemainExtra;
  228. BYTE *pbExtra;
  229. DWORD cbAttr;
  230. CRYPT_DER_BLOB rgrgAttrBlob[MAX_SIGNED_FILE_AUTH_ATTR_CNT][MINASN1_ATTR_BLOB_CNT];
  231. DWORD cAttr;
  232. assert(0 != cAttrOID);
  233. if (rgAttrValueBlob)
  234. cbAttr = *pcbAttr;
  235. else
  236. cbAttr = 0;
  237. lRemainExtra = cbAttr - sizeof(CRYPT_DER_BLOB) * cAttrOID;
  238. if (0 <= lRemainExtra) {
  239. memset(rgAttrValueBlob, 0, sizeof(CRYPT_DER_BLOB) * cAttrOID);
  240. pbExtra = (BYTE *) &rgAttrValueBlob[cAttrOID];
  241. } else
  242. pbExtra = NULL;
  243. // Parse the authenticated attributes
  244. cAttr = MAX_SIGNED_FILE_AUTH_ATTR_CNT;
  245. if (0 >= MinAsn1ParseAttributes(
  246. pAttrsValueBlob,
  247. &cAttr,
  248. rgrgAttrBlob))
  249. cAttr = 0;
  250. for (i = 0; i < cAttrOID; i++) {
  251. PCRYPT_DER_BLOB rgFindAttrBlob;
  252. rgFindAttrBlob = MinAsn1FindAttribute(
  253. &rgAttrEncodedOIDBlob[i],
  254. cAttr,
  255. rgrgAttrBlob
  256. );
  257. if (rgFindAttrBlob) {
  258. PCRYPT_DER_BLOB pFindAttrValue =
  259. &rgFindAttrBlob[MINASN1_ATTR_VALUE_IDX];
  260. const BYTE *pbFindValue = pFindAttrValue->pbData;
  261. DWORD cbFindValue = pFindAttrValue->cbData;
  262. if (0 < cbFindValue) {
  263. lRemainExtra -= cbFindValue;
  264. if (0 <= lRemainExtra) {
  265. rgAttrValueBlob[i].pbData = pbExtra;
  266. rgAttrValueBlob[i].cbData = cbFindValue;
  267. memcpy(pbExtra, pbFindValue, cbFindValue);
  268. pbExtra += cbFindValue;
  269. }
  270. }
  271. }
  272. }
  273. if (0 <= lRemainExtra) {
  274. *pcbAttr = cbAttr - (DWORD) lRemainExtra;
  275. lErr = ERROR_SUCCESS;
  276. } else {
  277. *pcbAttr = cbAttr + (DWORD) -lRemainExtra;
  278. lErr = ERROR_INSUFFICIENT_BUFFER;
  279. }
  280. return lErr;
  281. }
  282. //+-------------------------------------------------------------------------
  283. // Verifies a previously signed file.
  284. //
  285. // According to dwFileType, pvFile can be a pwszFilename, hFile or pFileBlob.
  286. // Only requires READ access.
  287. //
  288. // dwFileType:
  289. // MINCRYPT_FILE_NAME : pvFile - LPCWSTR pwszFilename
  290. // MINCRYPT_FILE_HANDLE : pvFile - HANDLE hFile
  291. // MINCRYPT_FILE_BLOB : pvFile - PCRYPT_DATA_BLOB pFileBlob
  292. //
  293. // Checks if the file has an embedded PKCS #7 Signed Data message containing
  294. // Indirect Data. The PKCS #7 is verified via MinCryptVerifySignedData().
  295. // The Indirect Data is parsed via MinAsn1ParseIndirectData() to get the
  296. // HashAlgId and the file hash. MinCryptHashFile() is called to hash the
  297. // file. The returned hash is compared against the Indirect Data's hash.
  298. //
  299. // The caller can request one or more signer authenticated attribute values
  300. // to be returned. The still encoded values are returned in the
  301. // caller allocated memory. The beginning of this returned memory will
  302. // be set to an array of attribute value blobs pointing to these
  303. // encoded values. The caller should make every attempt to allow for a
  304. // single pass call. The necessary memory size is:
  305. // (cAttrOID * sizeof(CRYPT_DER_BLOB)) +
  306. // total length of encoded attribute values.
  307. //
  308. // *pcbAttr will be updated with the number of bytes required to contain
  309. // the attribute blobs and values. If the input memory is insufficient,
  310. // ERROR_INSUFFICIENT_BUFFER will be returned if no other error.
  311. //
  312. // For the multi-valued attributes, only the first value is returned.
  313. //
  314. // If the function succeeds, the return value is ERROR_SUCCESS. Otherwise,
  315. // a nonzero error code is returned.
  316. //
  317. // Only NT, PE 32 bit file formats are supported.
  318. //--------------------------------------------------------------------------
  319. LONG
  320. WINAPI
  321. MinCryptVerifySignedFile(
  322. IN DWORD dwFileType,
  323. IN const VOID *pvFile,
  324. IN OPTIONAL DWORD cAttrOID,
  325. IN OPTIONAL CRYPT_DER_BLOB rgAttrEncodedOIDBlob[],
  326. // CRYPT_DER_BLOB rgAttrBlob[cAttrOID] header is at beginning
  327. // with the bytes pointed to immediately following
  328. OUT OPTIONAL CRYPT_DER_BLOB *rgAttrValueBlob,
  329. IN OUT OPTIONAL DWORD *pcbAttr
  330. )
  331. {
  332. LONG lErr;
  333. CRYPT_DATA_BLOB FileBlob = {0, NULL};
  334. __try {
  335. LPWIN_CERTIFICATE pCertHdr = NULL;
  336. const BYTE *pbEncodedSignedData;
  337. DWORD cbEncodedSignedData;
  338. CRYPT_DER_BLOB rgVerSignedDataBlob[MINCRYPT_VER_SIGNED_DATA_BLOB_CNT];
  339. CRYPT_DER_BLOB rgIndirectDataBlob[MINASN1_INDIRECT_DATA_BLOB_CNT];
  340. ALG_ID HashAlgId;
  341. BYTE rgbHash[MINCRYPT_MAX_HASH_LEN];
  342. DWORD cbHash;
  343. lErr = I_MinCryptMapFile(
  344. dwFileType,
  345. pvFile,
  346. &FileBlob
  347. );
  348. if (ERROR_SUCCESS != lErr)
  349. goto ErrorReturn;
  350. if (!I_IsNtPe32File(&FileBlob))
  351. goto NotNtPe32File;
  352. if (!imagehack_ImageGetCertificateData(
  353. &FileBlob,
  354. 0, // CertificateIndex
  355. &pCertHdr
  356. ))
  357. goto NoSignature;
  358. if (WIN_CERT_REVISION_2_0 != pCertHdr->wRevision ||
  359. WIN_CERT_TYPE_PKCS_SIGNED_DATA != pCertHdr->wCertificateType)
  360. goto UnsupportedSignature;
  361. if (offsetof(WIN_CERTIFICATE, bCertificate) > pCertHdr->dwLength)
  362. goto InvalidSignature;
  363. cbEncodedSignedData = pCertHdr->dwLength -
  364. offsetof(WIN_CERTIFICATE, bCertificate);
  365. pbEncodedSignedData = pCertHdr->bCertificate;
  366. lErr = MinCryptVerifySignedData(
  367. pbEncodedSignedData,
  368. cbEncodedSignedData,
  369. rgVerSignedDataBlob
  370. );
  371. if (ERROR_SUCCESS != lErr)
  372. goto ErrorReturn;
  373. // The data content should be Indirect Data
  374. if (sizeof(rgbSPC_INDIRECT_DATA_OBJID) !=
  375. rgVerSignedDataBlob[
  376. MINCRYPT_VER_SIGNED_DATA_CONTENT_OID_IDX].cbData
  377. ||
  378. 0 != memcmp(rgbSPC_INDIRECT_DATA_OBJID,
  379. rgVerSignedDataBlob[
  380. MINCRYPT_VER_SIGNED_DATA_CONTENT_OID_IDX].pbData,
  381. sizeof(rgbSPC_INDIRECT_DATA_OBJID)))
  382. goto NotIndirectDataOID;
  383. if (0 >= MinAsn1ParseIndirectData(
  384. &rgVerSignedDataBlob[MINCRYPT_VER_SIGNED_DATA_CONTENT_DATA_IDX],
  385. rgIndirectDataBlob
  386. ))
  387. goto ParseIndirectDataError;
  388. HashAlgId = MinCryptDecodeHashAlgorithmIdentifier(
  389. &rgIndirectDataBlob[MINASN1_INDIRECT_DATA_DIGEST_ALGID_IDX]
  390. );
  391. if (0 == HashAlgId)
  392. goto UnknownHashAlgId;
  393. lErr = MinCryptHashFile(
  394. MINCRYPT_FILE_BLOB,
  395. (const VOID *) &FileBlob,
  396. HashAlgId,
  397. rgbHash,
  398. &cbHash
  399. );
  400. if (ERROR_SUCCESS != lErr)
  401. goto ErrorReturn;
  402. // Check that the hash in the indirect data matches the file hash
  403. if (cbHash !=
  404. rgIndirectDataBlob[MINASN1_INDIRECT_DATA_DIGEST_IDX].cbData
  405. ||
  406. 0 != memcmp(rgbHash,
  407. rgIndirectDataBlob[MINASN1_INDIRECT_DATA_DIGEST_IDX].pbData,
  408. cbHash))
  409. goto InvalidFileHash;
  410. if (cAttrOID)
  411. lErr = I_GetAuthAttributes(
  412. &rgVerSignedDataBlob[MINCRYPT_VER_SIGNED_DATA_AUTH_ATTRS_IDX],
  413. cAttrOID,
  414. rgAttrEncodedOIDBlob,
  415. rgAttrValueBlob,
  416. pcbAttr
  417. );
  418. else
  419. lErr = ERROR_SUCCESS;
  420. } __except(EXCEPTION_EXECUTE_HANDLER) {
  421. lErr = GetExceptionCode();
  422. if (ERROR_SUCCESS == lErr)
  423. lErr = E_UNEXPECTED;
  424. goto ErrorReturn;
  425. }
  426. CommonReturn:
  427. //**********************************************************************
  428. // WARNING!!!!
  429. //
  430. // UnmapViewOfFile is in another DLL, kernel32.dll.
  431. // lErr must be protected.
  432. //
  433. //**********************************************************************
  434. if (MINCRYPT_FILE_BLOB != dwFileType && NULL != FileBlob.pbData)
  435. UnmapViewOfFile(FileBlob.pbData);
  436. return lErr;
  437. ErrorReturn:
  438. assert(ERROR_SUCCESS != lErr);
  439. if (ERROR_INSUFFICIENT_BUFFER == lErr)
  440. // This error can only be set when we determine that the attribute
  441. // buffer isn't big enough.
  442. lErr = E_UNEXPECTED;
  443. goto CommonReturn;
  444. NotNtPe32File:
  445. lErr = ERROR_NOT_SUPPORTED;
  446. goto ErrorReturn;
  447. NoSignature:
  448. UnsupportedSignature:
  449. InvalidSignature:
  450. lErr = TRUST_E_NOSIGNATURE;
  451. goto ErrorReturn;
  452. NotIndirectDataOID:
  453. ParseIndirectDataError:
  454. lErr = CRYPT_E_BAD_MSG;
  455. goto ErrorReturn;
  456. UnknownHashAlgId:
  457. lErr = CRYPT_E_UNKNOWN_ALGO;
  458. goto ErrorReturn;
  459. InvalidFileHash:
  460. lErr = CRYPT_E_HASH_VALUE;
  461. goto ErrorReturn;
  462. }