mirror of
https://github.com/mtrojnar/osslsigncode
synced 2026-06-08 16:13:39 +00:00
Compare commits
21 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| bf209e0fc8 | |||
| 68a6826cd1 | |||
| ea5d15862d | |||
| bdde95635f | |||
| d0ef178a9a | |||
| e126ab3e4a | |||
| a1fb6600fb | |||
| 8227c68ceb | |||
| c988b48063 | |||
| 6cf70b4af2 | |||
| 97a9ade6ec | |||
| f2f33bb131 | |||
| 202b2c2866 | |||
| 2a5409b7c4 | |||
| 87bce8e372 | |||
| f7ace57c81 | |||
| 92f8761b47 | |||
| 09d3312fd9 | |||
| 9d02a20aec | |||
| f190ec5d87 | |||
| 4b30d6be28 |
@@ -7,7 +7,7 @@ on:
|
|||||||
env:
|
env:
|
||||||
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
# Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.)
|
||||||
BUILD_TYPE: Release
|
BUILD_TYPE: Release
|
||||||
version: osslsigncode-2.12
|
version: osslsigncode-2.14-dev
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|||||||
+25
-5
@@ -10,13 +10,13 @@ set(BUILTIN_SOCKET ON CACHE BOOL "") # for static Python
|
|||||||
|
|
||||||
# configure basic project information
|
# configure basic project information
|
||||||
project(osslsigncode
|
project(osslsigncode
|
||||||
VERSION 2.12
|
VERSION 2.14
|
||||||
DESCRIPTION "OpenSSL based Authenticode signing for PE, CAB, CAT, MSI, APPX and script files"
|
DESCRIPTION "OpenSSL based Authenticode signing for PE, CAB, CAT, MSI, APPX and script files"
|
||||||
HOMEPAGE_URL "https://github.com/mtrojnar/osslsigncode"
|
HOMEPAGE_URL "https://github.com/mtrojnar/osslsigncode"
|
||||||
LANGUAGES C)
|
LANGUAGES C)
|
||||||
|
|
||||||
# force nonstandard version format for development packages
|
# force nonstandard version format for development packages
|
||||||
set(DEV "")
|
set(DEV "-dev")
|
||||||
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}${DEV}")
|
set(PROJECT_VERSION "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}${DEV}")
|
||||||
|
|
||||||
# version and contact information
|
# version and contact information
|
||||||
@@ -97,14 +97,25 @@ set_target_properties(osslsigncode PROPERTIES INSTALL_RPATH_USE_LINK_PATH TRUE)
|
|||||||
# testing with CTest
|
# testing with CTest
|
||||||
include(CMakeTest)
|
include(CMakeTest)
|
||||||
|
|
||||||
|
# documentation with Pandoc
|
||||||
|
include(CMakeDoc)
|
||||||
|
|
||||||
# installation rules for a project
|
# installation rules for a project
|
||||||
set(BINDIR "${CMAKE_INSTALL_PREFIX}/bin")
|
include(GNUInstallDirs)
|
||||||
install(TARGETS osslsigncode RUNTIME DESTINATION ${BINDIR})
|
|
||||||
|
install(TARGETS osslsigncode RUNTIME DESTINATION ${CMAKE_INSTALL_BINDIR})
|
||||||
|
|
||||||
|
install(FILES
|
||||||
|
"${PROJECT_SOURCE_DIR}/README.md"
|
||||||
|
"${PROJECT_SOURCE_DIR}/NEWS.md"
|
||||||
|
DESTINATION "${CMAKE_INSTALL_DOCDIR}")
|
||||||
|
|
||||||
if(UNIX)
|
if(UNIX)
|
||||||
include(CMakeDist)
|
include(CMakeDist)
|
||||||
else(UNIX)
|
else(UNIX)
|
||||||
install(
|
install(
|
||||||
DIRECTORY ${PROJECT_BINARY_DIR}/ DESTINATION ${BINDIR}
|
DIRECTORY ${PROJECT_BINARY_DIR}/
|
||||||
|
DESTINATION ${CMAKE_INSTALL_BINDIR}
|
||||||
FILES_MATCHING
|
FILES_MATCHING
|
||||||
PATTERN "*.dll"
|
PATTERN "*.dll"
|
||||||
PATTERN "vcpkg_installed" EXCLUDE
|
PATTERN "vcpkg_installed" EXCLUDE
|
||||||
@@ -112,6 +123,15 @@ else(UNIX)
|
|||||||
PATTERN "Testing" EXCLUDE)
|
PATTERN "Testing" EXCLUDE)
|
||||||
endif(UNIX)
|
endif(UNIX)
|
||||||
|
|
||||||
|
# uninstall target
|
||||||
|
configure_file(
|
||||||
|
"${PROJECT_SOURCE_DIR}/cmake/cmake_uninstall.cmake.in"
|
||||||
|
"${PROJECT_BINARY_DIR}/cmake_uninstall.cmake"
|
||||||
|
IMMEDIATE @ONLY)
|
||||||
|
|
||||||
|
add_custom_target(uninstall
|
||||||
|
COMMAND ${CMAKE_COMMAND} -P "${PROJECT_BINARY_DIR}/cmake_uninstall.cmake")
|
||||||
|
|
||||||
#[[
|
#[[
|
||||||
Local Variables:
|
Local Variables:
|
||||||
c-basic-offset: 4
|
c-basic-offset: 4
|
||||||
|
|||||||
@@ -1,5 +1,25 @@
|
|||||||
# osslsigncode change log
|
# osslsigncode change log
|
||||||
|
|
||||||
|
### 2.14 (unreleased)
|
||||||
|
|
||||||
|
- attach-signature now uses digest-only verification instead of full signature
|
||||||
|
validation; output file is not kept if digest verification fails
|
||||||
|
|
||||||
|
### 2.13 (2026.02.10)
|
||||||
|
|
||||||
|
**MULTIPLE SECURITY VULNERABILITIES**
|
||||||
|
|
||||||
|
This release includes important security fixes. Users are strongly encouraged
|
||||||
|
to upgrade, as the issues below may be exploitable when processing untrusted
|
||||||
|
files.
|
||||||
|
|
||||||
|
- fixed integer overflows when processing APPX compressed data streams
|
||||||
|
(by Małgorzata Olszówka)
|
||||||
|
- fixed double-free vulnerabilities in APPX file processing
|
||||||
|
(by Małgorzata Olszówka)
|
||||||
|
- fixed multiple memory corruption issues in PE page hash computation
|
||||||
|
(by Antoni Klajn (Opera) and Małgorzata Olszówka)
|
||||||
|
|
||||||
### 2.12 (2026.02.02)
|
### 2.12 (2026.02.02)
|
||||||
|
|
||||||
**CRITICAL SECURITY VULNERABILITY**
|
**CRITICAL SECURITY VULNERABILITY**
|
||||||
|
|||||||
@@ -470,32 +470,33 @@ static int appx_hash_length_get(FILE_FORMAT_CTX *ctx)
|
|||||||
*/
|
*/
|
||||||
static int appx_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
|
static int appx_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
|
||||||
{
|
{
|
||||||
if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) {
|
SpcIndirectDataContent *idc;
|
||||||
ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence;
|
BIO *hashes;
|
||||||
const u_char *p = content_val->data;
|
|
||||||
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
|
|
||||||
|
|
||||||
if (idc) {
|
idc = pkcs7_get_indirect_data_content(p7);
|
||||||
BIO *hashes;
|
if (!idc)
|
||||||
if (!appx_extract_hashes(ctx, idc)) {
|
return 1; /* OK - no SpcIndirectDataContent */
|
||||||
fprintf(stderr, "Failed to extract hashes from the signature\n");
|
|
||||||
SpcIndirectDataContent_free(idc);
|
if (!appx_extract_hashes(ctx, idc)) {
|
||||||
return 0; /* FAILED */
|
fprintf(stderr, "Failed to extract hashes from the signature\n");
|
||||||
}
|
SpcIndirectDataContent_free(idc);
|
||||||
hashes = appx_calculate_hashes(ctx);
|
return 0; /* FAILED */
|
||||||
if (!hashes) {
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
return 0; /* FAILED */
|
|
||||||
}
|
|
||||||
BIO_free_all(hashes);
|
|
||||||
if (!appx_compare_hashes(ctx)) {
|
|
||||||
fprintf(stderr, "Signature hash verification failed\n");
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
return 0; /* FAILED */
|
|
||||||
}
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
hashes = appx_calculate_hashes(ctx);
|
||||||
|
if (!hashes) {
|
||||||
|
SpcIndirectDataContent_free(idc);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
BIO_free_all(hashes);
|
||||||
|
|
||||||
|
if (!appx_compare_hashes(ctx)) {
|
||||||
|
fprintf(stderr, "Signature hash verification failed\n");
|
||||||
|
SpcIndirectDataContent_free(idc);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
|
||||||
|
SpcIndirectDataContent_free(idc);
|
||||||
return 1; /* OK */
|
return 1; /* OK */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1077,13 +1078,13 @@ static int appx_extract_hashes(FILE_FORMAT_CTX *ctx, SpcIndirectDataContent *con
|
|||||||
AppxSpcSipInfo_free(si);
|
AppxSpcSipInfo_free(si);
|
||||||
BIO_free_all(stdbio);
|
BIO_free_all(stdbio);
|
||||||
#endif
|
#endif
|
||||||
int length = content->messageDigest->digest->length;
|
int len = ASN1_STRING_length(content->messageDigest->digest);
|
||||||
uint8_t *data = content->messageDigest->digest->data;
|
const uint8_t *data = ASN1_STRING_get0_data(content->messageDigest->digest);
|
||||||
int mdlen = EVP_MD_size(ctx->appx_ctx->md);
|
int mdlen = EVP_MD_size(ctx->appx_ctx->md);
|
||||||
int pos = 4;
|
int pos = 4;
|
||||||
|
|
||||||
/* we are expecting at least 4 hashes + 4 byte header */
|
/* we are expecting at least 4 hashes + 4 byte header */
|
||||||
if (length < 4 * mdlen + 4) {
|
if (len < 4 * mdlen + 4) {
|
||||||
fprintf(stderr, "Hash too short\n");
|
fprintf(stderr, "Hash too short\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
@@ -1091,7 +1092,7 @@ static int appx_extract_hashes(FILE_FORMAT_CTX *ctx, SpcIndirectDataContent *con
|
|||||||
fprintf(stderr, "Hash signature does not match\n");
|
fprintf(stderr, "Hash signature does not match\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
while (pos + mdlen + 4 <= length) {
|
while (pos + mdlen + 4 <= len) {
|
||||||
if (!memcmp(data + pos, AXPC_SIGNATURE, 4)) {
|
if (!memcmp(data + pos, AXPC_SIGNATURE, 4)) {
|
||||||
ctx->appx_ctx->existingDataHash = OPENSSL_malloc((size_t)mdlen);
|
ctx->appx_ctx->existingDataHash = OPENSSL_malloc((size_t)mdlen);
|
||||||
memcpy(ctx->appx_ctx->existingDataHash, data + pos + 4, (size_t)mdlen);
|
memcpy(ctx->appx_ctx->existingDataHash, data + pos + 4, (size_t)mdlen);
|
||||||
@@ -1503,6 +1504,7 @@ static int zipAppendSignatureFile(BIO *bio, ZIP_FILE *zip, uint8_t *data, uint64
|
|||||||
if (!get_current_position(bio, &offset)) {
|
if (!get_current_position(bio, &offset)) {
|
||||||
fprintf(stderr, "Unable to get offset\n");
|
fprintf(stderr, "Unable to get offset\n");
|
||||||
OPENSSL_free(header.fileName);
|
OPENSSL_free(header.fileName);
|
||||||
|
header.fileName = NULL;
|
||||||
OPENSSL_free(dataToWrite);
|
OPENSSL_free(dataToWrite);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
@@ -1513,6 +1515,7 @@ static int zipAppendSignatureFile(BIO *bio, ZIP_FILE *zip, uint8_t *data, uint64
|
|||||||
if (!BIO_write_ex(bio, dataToWrite + written, toWrite, &check)
|
if (!BIO_write_ex(bio, dataToWrite + written, toWrite, &check)
|
||||||
|| check != toWrite) {
|
|| check != toWrite) {
|
||||||
OPENSSL_free(header.fileName);
|
OPENSSL_free(header.fileName);
|
||||||
|
header.fileName = NULL;
|
||||||
OPENSSL_free(dataToWrite);
|
OPENSSL_free(dataToWrite);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
@@ -1685,6 +1688,8 @@ static int zipRewriteData(ZIP_FILE *zip, ZIP_CENTRAL_DIRECTORY_ENTRY *entry, BIO
|
|||||||
out:
|
out:
|
||||||
OPENSSL_free(header.fileName);
|
OPENSSL_free(header.fileName);
|
||||||
OPENSSL_free(header.extraField);
|
OPENSSL_free(header.extraField);
|
||||||
|
header.fileName = NULL;
|
||||||
|
header.extraField = NULL;
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1852,6 +1857,11 @@ static size_t zipReadFileData(ZIP_FILE *zip, uint8_t **pData, ZIP_CENTRAL_DIRECT
|
|||||||
}
|
}
|
||||||
if (entry->overrideData) {
|
if (entry->overrideData) {
|
||||||
compressedSize = entry->overrideData->compressedSize;
|
compressedSize = entry->overrideData->compressedSize;
|
||||||
|
/* Validate sizes for safe allocation */
|
||||||
|
if (compressedSize > (uint64_t)(SIZE_MAX - 1)) {
|
||||||
|
fprintf(stderr, "Corrupted compressedSize : %" PRIu64"\n", compressedSize);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
uncompressedSize = entry->overrideData->uncompressedSize;
|
uncompressedSize = entry->overrideData->uncompressedSize;
|
||||||
compressedData = OPENSSL_zalloc(compressedSize + 1);
|
compressedData = OPENSSL_zalloc(compressedSize + 1);
|
||||||
memcpy(compressedData, entry->overrideData->data, compressedSize);
|
memcpy(compressedData, entry->overrideData->data, compressedSize);
|
||||||
@@ -1863,6 +1873,8 @@ static size_t zipReadFileData(ZIP_FILE *zip, uint8_t **pData, ZIP_CENTRAL_DIRECT
|
|||||||
if (!zipReadLocalHeader(&header, zip, compressedSize)) {
|
if (!zipReadLocalHeader(&header, zip, compressedSize)) {
|
||||||
OPENSSL_free(header.fileName);
|
OPENSSL_free(header.fileName);
|
||||||
OPENSSL_free(header.extraField);
|
OPENSSL_free(header.extraField);
|
||||||
|
header.fileName = NULL;
|
||||||
|
header.extraField = NULL;
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
if (header.fileNameLen != entry->fileNameLen
|
if (header.fileNameLen != entry->fileNameLen
|
||||||
@@ -1873,14 +1885,20 @@ static size_t zipReadFileData(ZIP_FILE *zip, uint8_t **pData, ZIP_CENTRAL_DIRECT
|
|||||||
fprintf(stderr, "Local header does not match central directory entry\n");
|
fprintf(stderr, "Local header does not match central directory entry\n");
|
||||||
OPENSSL_free(header.fileName);
|
OPENSSL_free(header.fileName);
|
||||||
OPENSSL_free(header.extraField);
|
OPENSSL_free(header.extraField);
|
||||||
|
header.fileName = NULL;
|
||||||
|
header.extraField = NULL;
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
/* we don't really need those */
|
/* we don't really need those */
|
||||||
OPENSSL_free(header.fileName);
|
OPENSSL_free(header.fileName);
|
||||||
OPENSSL_free(header.extraField);
|
OPENSSL_free(header.extraField);
|
||||||
|
header.fileName = NULL;
|
||||||
|
header.extraField = NULL;
|
||||||
|
|
||||||
if (compressedSize > (uint64_t)zip->fileSize - entry->offsetOfLocalHeader) {
|
/* Validate sizes for safe allocation */
|
||||||
fprintf(stderr, "Corrupted compressedSize : 0x%08" PRIX64 "\n", entry->compressedSize);
|
if (compressedSize > (uint64_t)(SIZE_MAX - 1)
|
||||||
|
|| compressedSize > (uint64_t)zip->fileSize - entry->offsetOfLocalHeader) {
|
||||||
|
fprintf(stderr, "Corrupted compressedSize : %" PRIu64"\n", compressedSize);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
compressedData = OPENSSL_zalloc(compressedSize + 1);
|
compressedData = OPENSSL_zalloc(compressedSize + 1);
|
||||||
@@ -1899,11 +1917,26 @@ static size_t zipReadFileData(ZIP_FILE *zip, uint8_t **pData, ZIP_CENTRAL_DIRECT
|
|||||||
*pData = compressedData;
|
*pData = compressedData;
|
||||||
dataSize = compressedSize;
|
dataSize = compressedSize;
|
||||||
} else if (entry->compression == COMPRESSION_DEFLATE) {
|
} else if (entry->compression == COMPRESSION_DEFLATE) {
|
||||||
uint8_t *uncompressedData = OPENSSL_zalloc(uncompressedSize + 1);
|
uint8_t *uncompressedData;
|
||||||
uint64_t destLen = uncompressedSize;
|
uint64_t destLen, sourceLen;
|
||||||
uint64_t sourceLen = compressedSize;
|
|
||||||
int ret;
|
int ret;
|
||||||
|
|
||||||
|
/* Validate sizes for safe allocation */
|
||||||
|
if (uncompressedSize > (uint64_t)(SIZE_MAX - 1)) {
|
||||||
|
fprintf(stderr, "Corrupted uncompressedSize : %" PRIu64"\n", uncompressedSize);
|
||||||
|
OPENSSL_free(compressedData);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
/* Detect suspicious compression ratio (zip bomb protection) */
|
||||||
|
if (uncompressedSize > 1024 * 1024 && uncompressedSize / 100 >= compressedSize) {
|
||||||
|
fprintf(stderr, "Error: suspicious compression ratio\n");
|
||||||
|
OPENSSL_free(compressedData);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
uncompressedData = OPENSSL_zalloc(uncompressedSize + 1);
|
||||||
|
destLen = uncompressedSize;
|
||||||
|
sourceLen = compressedSize;
|
||||||
|
|
||||||
ret = zipInflate(uncompressedData, &destLen, compressedData, (uLong *)&sourceLen);
|
ret = zipInflate(uncompressedData, &destLen, compressedData, (uLong *)&sourceLen);
|
||||||
OPENSSL_free(compressedData);
|
OPENSSL_free(compressedData);
|
||||||
|
|
||||||
@@ -1970,6 +2003,8 @@ static int zipReadLocalHeader(ZIP_LOCAL_HEADER *header, ZIP_FILE *zip, uint64_t
|
|||||||
header->extraFieldLen = fileGetU16(file);
|
header->extraFieldLen = fileGetU16(file);
|
||||||
/* file name (variable size) */
|
/* file name (variable size) */
|
||||||
if (header->fileNameLen > 0) {
|
if (header->fileNameLen > 0) {
|
||||||
|
/* fileNameLen is uint16_t (ZIP spec, 2-byte field),
|
||||||
|
* so fileNameLen + 1 cannot overflow size_t */
|
||||||
header->fileName = OPENSSL_zalloc(header->fileNameLen + 1);
|
header->fileName = OPENSSL_zalloc(header->fileNameLen + 1);
|
||||||
size = fread(header->fileName, 1, header->fileNameLen, file);
|
size = fread(header->fileName, 1, header->fileNameLen, file);
|
||||||
if (size != header->fileNameLen) {
|
if (size != header->fileNameLen) {
|
||||||
@@ -1981,6 +2016,8 @@ static int zipReadLocalHeader(ZIP_LOCAL_HEADER *header, ZIP_FILE *zip, uint64_t
|
|||||||
}
|
}
|
||||||
/* extra field (variable size) */
|
/* extra field (variable size) */
|
||||||
if (header->extraFieldLen > 0) {
|
if (header->extraFieldLen > 0) {
|
||||||
|
/* extraFieldLen is uint16_t (ZIP spec, 2-byte field),
|
||||||
|
* so extraFieldLen + 1 cannot overflow size_t */
|
||||||
header->extraField = OPENSSL_zalloc(header->extraFieldLen + 1);
|
header->extraField = OPENSSL_zalloc(header->extraFieldLen + 1);
|
||||||
size = fread(header->extraField, 1, header->extraFieldLen, file);
|
size = fread(header->extraField, 1, header->extraFieldLen, file);
|
||||||
if (size != header->extraFieldLen) {
|
if (size != header->extraFieldLen) {
|
||||||
@@ -2011,6 +2048,8 @@ static int zipReadLocalHeader(ZIP_LOCAL_HEADER *header, ZIP_FILE *zip, uint64_t
|
|||||||
fprintf(stderr, "The input file is not a valid zip file - flags indicate data descriptor, but data descriptor signature does not match\n");
|
fprintf(stderr, "The input file is not a valid zip file - flags indicate data descriptor, but data descriptor signature does not match\n");
|
||||||
OPENSSL_free(header->fileName);
|
OPENSSL_free(header->fileName);
|
||||||
OPENSSL_free(header->extraField);
|
OPENSSL_free(header->extraField);
|
||||||
|
header->fileName = NULL;
|
||||||
|
header->extraField = NULL;
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
header->crc32 = fileGetU32(file);
|
header->crc32 = fileGetU32(file);
|
||||||
@@ -2477,6 +2516,8 @@ static ZIP_CENTRAL_DIRECTORY_ENTRY *zipReadNextCentralDirectoryEntry(FILE *file)
|
|||||||
entry->offsetOfLocalHeader = fileGetU32(file);
|
entry->offsetOfLocalHeader = fileGetU32(file);
|
||||||
/* file name (variable size) */
|
/* file name (variable size) */
|
||||||
if (entry->fileNameLen > 0) {
|
if (entry->fileNameLen > 0) {
|
||||||
|
/* fileNameLen is uint16_t (ZIP spec, 2-byte field),
|
||||||
|
* so fileNameLen + 1 cannot overflow size_t */
|
||||||
entry->fileName = OPENSSL_zalloc(entry->fileNameLen + 1);
|
entry->fileName = OPENSSL_zalloc(entry->fileNameLen + 1);
|
||||||
size = fread(entry->fileName, 1, entry->fileNameLen, file);
|
size = fread(entry->fileName, 1, entry->fileNameLen, file);
|
||||||
if (size != entry->fileNameLen) {
|
if (size != entry->fileNameLen) {
|
||||||
@@ -2487,6 +2528,8 @@ static ZIP_CENTRAL_DIRECTORY_ENTRY *zipReadNextCentralDirectoryEntry(FILE *file)
|
|||||||
}
|
}
|
||||||
/* extra field (variable size) */
|
/* extra field (variable size) */
|
||||||
if (entry->extraFieldLen > 0) {
|
if (entry->extraFieldLen > 0) {
|
||||||
|
/* extraFieldLen is uint16_t (ZIP spec, 2-byte field),
|
||||||
|
* so extraFieldLen + 1 cannot overflow size_t */
|
||||||
entry->extraField = OPENSSL_zalloc(entry->extraFieldLen + 1);
|
entry->extraField = OPENSSL_zalloc(entry->extraFieldLen + 1);
|
||||||
size = fread(entry->extraField, 1, entry->extraFieldLen, file);
|
size = fread(entry->extraField, 1, entry->extraFieldLen, file);
|
||||||
if (size != entry->extraFieldLen) {
|
if (size != entry->extraFieldLen) {
|
||||||
@@ -2497,6 +2540,8 @@ static ZIP_CENTRAL_DIRECTORY_ENTRY *zipReadNextCentralDirectoryEntry(FILE *file)
|
|||||||
}
|
}
|
||||||
/* file comment (variable size) */
|
/* file comment (variable size) */
|
||||||
if (entry->fileCommentLen > 0) {
|
if (entry->fileCommentLen > 0) {
|
||||||
|
/* fileCommentLen is uint16_t (ZIP spec, 2-byte field),
|
||||||
|
* so fileCommentLen + 1 cannot overflow size_t */
|
||||||
entry->fileComment = OPENSSL_zalloc(entry->fileCommentLen + 1);
|
entry->fileComment = OPENSSL_zalloc(entry->fileCommentLen + 1);
|
||||||
size = fread(entry->fileComment, 1, entry->fileCommentLen, file);
|
size = fread(entry->fileComment, 1, entry->fileCommentLen, file);
|
||||||
if (size != entry->fileCommentLen) {
|
if (size != entry->fileCommentLen) {
|
||||||
@@ -2635,6 +2680,8 @@ static int readZipEOCDR(ZIP_EOCDR *eocdr, FILE *file)
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
if (eocdr->commentLen > 0) {
|
if (eocdr->commentLen > 0) {
|
||||||
|
/* ZIP_EOCDR commentLen is uint16_t (ZIP spec, 2-byte field),
|
||||||
|
* so fileCommentLen + 1 cannot overflow size_t */
|
||||||
eocdr->comment = OPENSSL_zalloc(eocdr->commentLen + 1);
|
eocdr->comment = OPENSSL_zalloc(eocdr->commentLen + 1);
|
||||||
size = fread(eocdr->comment, 1, eocdr->commentLen, file);
|
size = fread(eocdr->comment, 1, eocdr->commentLen, file);
|
||||||
if (size != eocdr->commentLen) {
|
if (size != eocdr->commentLen) {
|
||||||
|
|||||||
@@ -337,19 +337,7 @@ static int cab_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
|
|||||||
u_char mdbuf[EVP_MAX_MD_SIZE];
|
u_char mdbuf[EVP_MAX_MD_SIZE];
|
||||||
u_char *cmdbuf;
|
u_char *cmdbuf;
|
||||||
|
|
||||||
if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) {
|
if (!pkcs7_get_content_digest(p7, mdbuf, &mdtype)) {
|
||||||
ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence;
|
|
||||||
const u_char *p = content_val->data;
|
|
||||||
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
|
|
||||||
if (idc) {
|
|
||||||
if (spc_extract_digest_safe(idc, mdbuf, &mdtype) < 0) {
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
return 0; /* FAILED */
|
|
||||||
}
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mdtype == -1) {
|
|
||||||
fprintf(stderr, "Failed to extract current message digest\n\n");
|
fprintf(stderr, "Failed to extract current message digest\n\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -297,27 +297,39 @@ static int cat_add_content_type(PKCS7 *p7, PKCS7 *cursig)
|
|||||||
*/
|
*/
|
||||||
static int cat_sign_content(PKCS7 *p7, PKCS7 *contents)
|
static int cat_sign_content(PKCS7 *p7, PKCS7 *contents)
|
||||||
{
|
{
|
||||||
u_char *content;
|
const unsigned char *sequence_data;
|
||||||
int seqhdrlen, content_length;
|
const unsigned char *content;
|
||||||
|
ASN1_STRING *sequence;
|
||||||
|
int seqhdrlen, sequence_len, content_length;
|
||||||
|
|
||||||
if (!contents->d.other || !contents->d.other->value.sequence
|
if (!contents->d.other || !contents->d.other->value.sequence) {
|
||||||
|| !contents->d.other->value.sequence->data) {
|
|
||||||
fprintf(stderr, "Failed to get content value\n");
|
fprintf(stderr, "Failed to get content value\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
seqhdrlen = asn1_simple_hdr_len(contents->d.other->value.sequence->data,
|
|
||||||
contents->d.other->value.sequence->length);
|
sequence = contents->d.other->value.sequence;
|
||||||
content = contents->d.other->value.sequence->data + seqhdrlen;
|
sequence_data = ASN1_STRING_get0_data(sequence);
|
||||||
content_length = contents->d.other->value.sequence->length - seqhdrlen;
|
sequence_len = ASN1_STRING_length(sequence);
|
||||||
|
|
||||||
|
if (!sequence_data) {
|
||||||
|
fprintf(stderr, "Failed to get content value\n");
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
|
||||||
|
seqhdrlen = asn1_simple_hdr_len(sequence_data, sequence_len);
|
||||||
|
content = (const unsigned char *)sequence_data + seqhdrlen;
|
||||||
|
content_length = sequence_len - seqhdrlen;
|
||||||
|
|
||||||
if (!pkcs7_sign_content(p7, content, content_length)) {
|
if (!pkcs7_sign_content(p7, content, content_length)) {
|
||||||
fprintf(stderr, "Failed to sign content\n");
|
fprintf(stderr, "Failed to sign content\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!PKCS7_set_content(p7, PKCS7_dup(contents))) {
|
if (!PKCS7_set_content(p7, PKCS7_dup(contents))) {
|
||||||
fprintf(stderr, "PKCS7_set_content failed\n");
|
fprintf(stderr, "PKCS7_set_content failed\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
|
|
||||||
return 1; /* OK */
|
return 1; /* OK */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -381,26 +393,22 @@ static int cat_print_content_member_digest(ASN1_TYPE *content)
|
|||||||
{
|
{
|
||||||
SpcIndirectDataContent *idc;
|
SpcIndirectDataContent *idc;
|
||||||
u_char mdbuf[EVP_MAX_MD_SIZE];
|
u_char mdbuf[EVP_MAX_MD_SIZE];
|
||||||
const u_char *data ;
|
|
||||||
int mdtype = -1;
|
int mdtype = -1;
|
||||||
ASN1_STRING *value;
|
|
||||||
|
|
||||||
value = content->value.sequence;
|
idc = asn1_type_get_indirect_data_content(content);
|
||||||
data = ASN1_STRING_get0_data(value);
|
|
||||||
idc = d2i_SpcIndirectDataContent(NULL, &data, ASN1_STRING_length(value));
|
|
||||||
if (!idc)
|
if (!idc)
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
if (spc_extract_digest_safe(idc, mdbuf, &mdtype) < 0) {
|
|
||||||
|
if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) {
|
||||||
|
fprintf(stderr, "Failed to extract message digest from signature\n\n");
|
||||||
SpcIndirectDataContent_free(idc);
|
SpcIndirectDataContent_free(idc);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
SpcIndirectDataContent_free(idc);
|
SpcIndirectDataContent_free(idc);
|
||||||
if (mdtype == -1) {
|
|
||||||
fprintf(stderr, "Failed to extract current message digest\n\n");
|
|
||||||
return 0; /* FAILED */
|
|
||||||
}
|
|
||||||
printf("\tHash algorithm: %s\n", OBJ_nid2sn(mdtype));
|
printf("\tHash algorithm: %s\n", OBJ_nid2sn(mdtype));
|
||||||
print_hash("\tMessage digest", "", mdbuf, EVP_MD_size(EVP_get_digestbynid(mdtype)));
|
print_hash("\tMessage digest", "", mdbuf, EVP_MD_size(EVP_get_digestbynid(mdtype)));
|
||||||
|
|
||||||
return 1; /* OK */
|
return 1; /* OK */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,44 @@
|
|||||||
|
# documentation with Pandoc
|
||||||
|
# cmake --build .
|
||||||
|
|
||||||
|
find_program(PANDOC pandoc)
|
||||||
|
|
||||||
|
if(NOT PANDOC)
|
||||||
|
message(WARNING "CMakeDoc: pandoc not found, documentation disabled")
|
||||||
|
return()
|
||||||
|
endif(NOT PANDOC)
|
||||||
|
|
||||||
|
set(DOC_MD "${PROJECT_SOURCE_DIR}/osslsigncode.md")
|
||||||
|
|
||||||
|
if(NOT EXISTS "${DOC_MD}")
|
||||||
|
message(WARNING "CMakeDoc: markdown source not found: ${DOC_MD}")
|
||||||
|
return()
|
||||||
|
endif(NOT EXISTS "${DOC_MD}")
|
||||||
|
|
||||||
|
set(MAN_PAGE "${PROJECT_BINARY_DIR}/osslsigncode.1")
|
||||||
|
set(HTML_PAGE "${PROJECT_BINARY_DIR}/osslsigncode.html")
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${MAN_PAGE}"
|
||||||
|
COMMAND "${PANDOC}" -s "${DOC_MD}" -t man -o "${MAN_PAGE}"
|
||||||
|
DEPENDS "${DOC_MD}"
|
||||||
|
COMMENT "CMakeDoc: generating man page"
|
||||||
|
VERBATIM)
|
||||||
|
|
||||||
|
add_custom_command(
|
||||||
|
OUTPUT "${HTML_PAGE}"
|
||||||
|
COMMAND "${PANDOC}" -s --toc --toc-depth=2 "${DOC_MD}" -t html -o "${HTML_PAGE}"
|
||||||
|
DEPENDS "${DOC_MD}"
|
||||||
|
COMMENT "CMakeDoc: generating HTML documentation"
|
||||||
|
VERBATIM)
|
||||||
|
|
||||||
|
add_custom_target(docs ALL DEPENDS "${MAN_PAGE}" "${HTML_PAGE}")
|
||||||
|
|
||||||
|
#[[
|
||||||
|
Local Variables:
|
||||||
|
c-basic-offset: 4
|
||||||
|
tab-width: 4
|
||||||
|
indent-tabs-mode: nil
|
||||||
|
End:
|
||||||
|
vim: set ts=4 expandtab:
|
||||||
|
]]
|
||||||
+2
-14
@@ -196,18 +196,12 @@ if(Python3_FOUND AND NOT cryptography_error)
|
|||||||
foreach(format ${formats})
|
foreach(format ${formats})
|
||||||
add_test(NAME "attached_${format}_${ext}"
|
add_test(NAME "attached_${format}_${ext}"
|
||||||
COMMAND ${Python3_EXECUTABLE} ${EXEC} ${OSSLSIGNCODE} "attach-signature"
|
COMMAND ${Python3_EXECUTABLE} ${EXEC} ${OSSLSIGNCODE} "attach-signature"
|
||||||
# sign options
|
|
||||||
"-add-msi-dse"
|
"-add-msi-dse"
|
||||||
"-h" "sha512"
|
"-h" "sha512"
|
||||||
"-nest"
|
"-nest"
|
||||||
"-sigin" "${FILES}/${ext}.${format}"
|
"-sigin" "${FILES}/${ext}.${format}"
|
||||||
"-in" "${FILES}/signed.${ext}"
|
"-in" "${FILES}/signed.${ext}"
|
||||||
"-out" "${FILES}/attached_${format}.${ext}"
|
"-out" "${FILES}/attached_${format}.${ext}")
|
||||||
# verify options
|
|
||||||
"-require-leaf-hash" "FILE ${CERTS}/leafhash.txt"
|
|
||||||
"-time" "1567296000" # Signature verification time: Sep 1 00:00:00 2019 GMT
|
|
||||||
"-CAfile" "${CERTS}/CACert.pem"
|
|
||||||
"-CRLfile" "${CERTS}/CACertCRL.pem")
|
|
||||||
set_tests_properties("attached_${format}_${ext}" PROPERTIES
|
set_tests_properties("attached_${format}_${ext}" PROPERTIES
|
||||||
DEPENDS "signed_${ext};extract_pem_${ext};extract_der_${ext}")
|
DEPENDS "signed_${ext};extract_pem_${ext};extract_der_${ext}")
|
||||||
list(APPEND ALL_TESTS "attached_${format}_${ext}")
|
list(APPEND ALL_TESTS "attached_${format}_${ext}")
|
||||||
@@ -413,17 +407,11 @@ if(Python3_FOUND AND NOT cryptography_error)
|
|||||||
foreach(format ${formats})
|
foreach(format ${formats})
|
||||||
add_test(NAME "attached_data_${ext}_${data_format}_${format}"
|
add_test(NAME "attached_data_${ext}_${data_format}_${format}"
|
||||||
COMMAND ${Python3_EXECUTABLE} ${EXEC} ${OSSLSIGNCODE} "attach-signature"
|
COMMAND ${Python3_EXECUTABLE} ${EXEC} ${OSSLSIGNCODE} "attach-signature"
|
||||||
# sign options
|
|
||||||
"-add-msi-dse"
|
"-add-msi-dse"
|
||||||
"-h" "sha384"
|
"-h" "sha384"
|
||||||
"-sigin" "${FILES}/signed_data_${ext}_${data_format}.${format}"
|
"-sigin" "${FILES}/signed_data_${ext}_${data_format}.${format}"
|
||||||
"-in" "${FILES}/unsigned.${ext}"
|
"-in" "${FILES}/unsigned.${ext}"
|
||||||
"-out" "${FILES}/attached_data_${data_format}_${format}.${ext}"
|
"-out" "${FILES}/attached_data_${data_format}_${format}.${ext}")
|
||||||
# verify options
|
|
||||||
"-require-leaf-hash" "FILE ${CERTS}/leafhash.txt"
|
|
||||||
"-time" "1567296000" # Signature verification time: Sep 1 00:00:00 2019 GMT
|
|
||||||
"-CAfile" "${CERTS}/CACert.pem"
|
|
||||||
"-CRLfile" "${CERTS}/CACertCRL.pem")
|
|
||||||
set_tests_properties("attached_data_${ext}_${data_format}_${format}" PROPERTIES
|
set_tests_properties("attached_data_${ext}_${data_format}_${format}" PROPERTIES
|
||||||
DEPENDS "signed_data_${ext}_${data_format};signed_data_pem_${ext}_${data_format}")
|
DEPENDS "signed_data_${ext}_${data_format};signed_data_pem_${ext}_${data_format}")
|
||||||
list(APPEND ALL_TESTS "attached_data_${ext}_${data_format}_${format}")
|
list(APPEND ALL_TESTS "attached_data_${ext}_${data_format}_${format}")
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
# uninstall target
|
||||||
|
#
|
||||||
|
# CMake does not provide a built-in uninstall target.
|
||||||
|
# This target removes files listed in install_manifest.txt,
|
||||||
|
# generated by the install step.
|
||||||
|
#
|
||||||
|
# cmake --build . --target uninstall
|
||||||
|
|
||||||
|
if(NOT EXISTS "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt")
|
||||||
|
message(FATAL_ERROR "Cannot find install manifest")
|
||||||
|
endif()
|
||||||
|
|
||||||
|
file(READ "@CMAKE_CURRENT_BINARY_DIR@/install_manifest.txt" files)
|
||||||
|
string(REPLACE "\n" ";" files "${files}")
|
||||||
|
|
||||||
|
foreach(file ${files})
|
||||||
|
message(STATUS "Removing ${file}")
|
||||||
|
|
||||||
|
if(EXISTS "${file}" OR IS_SYMLINK "${file}")
|
||||||
|
file(REMOVE "${file}")
|
||||||
|
else()
|
||||||
|
message(STATUS "File does not exist: ${file}")
|
||||||
|
endif()
|
||||||
|
endforeach()
|
||||||
@@ -341,6 +341,91 @@ PKCS7 *pkcs7_set_content(ASN1_OCTET_STRING *content)
|
|||||||
return p7;
|
return p7;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Retrieve the message digest and digest algorithm from PKCS7
|
||||||
|
* SpcIndirectDataContent.
|
||||||
|
*
|
||||||
|
* [in] p7: PKCS7 structure containing SPC_INDIRECT_DATA_OBJID content
|
||||||
|
* [out] mdbuf: message digest buffer, at least EVP_MAX_MD_SIZE bytes
|
||||||
|
* [out] mdtype: OpenSSL NID of the digest algorithm
|
||||||
|
* [returns] 0 on error or 1 on success
|
||||||
|
*/
|
||||||
|
int pkcs7_get_content_digest(PKCS7 *p7, u_char *mdbuf, int *mdtype)
|
||||||
|
{
|
||||||
|
SpcIndirectDataContent *idc;
|
||||||
|
|
||||||
|
if (!mdbuf || !mdtype)
|
||||||
|
return 0; /* FAILED */
|
||||||
|
|
||||||
|
*mdtype = -1;
|
||||||
|
|
||||||
|
idc = pkcs7_get_indirect_data_content(p7);
|
||||||
|
if (!idc) {
|
||||||
|
fprintf(stderr, "Failed to decode SpcIndirectDataContent\n\n");
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
if (spc_indirect_data_content_get_digest(idc, mdbuf, mdtype) < 0) {
|
||||||
|
fprintf(stderr, "Failed to extract message digest from signature\n\n");
|
||||||
|
SpcIndirectDataContent_free(idc);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
SpcIndirectDataContent_free(idc);
|
||||||
|
if (*mdtype == -1) {
|
||||||
|
fprintf(stderr, "Failed to extract current message digest\n\n");
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
return 1; /* OK */
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decode SpcIndirectDataContent from a PKCS7 signedData content.
|
||||||
|
*
|
||||||
|
* [in] p7: PKCS7 structure containing SPC_INDIRECT_DATA_OBJID content
|
||||||
|
* [returns] newly allocated SpcIndirectDataContent, or NULL on error
|
||||||
|
*
|
||||||
|
* The caller is responsible for freeing the returned object with
|
||||||
|
* SpcIndirectDataContent_free().
|
||||||
|
*/
|
||||||
|
SpcIndirectDataContent *pkcs7_get_indirect_data_content(PKCS7 *p7)
|
||||||
|
{
|
||||||
|
if (!is_content_type(p7, SPC_INDIRECT_DATA_OBJID))
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (!p7->d.sign || !p7->d.sign->contents || !p7->d.sign->contents->d.other)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
return asn1_type_get_indirect_data_content(p7->d.sign->contents->d.other);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Decode SpcIndirectDataContent from an ASN1_TYPE object.
|
||||||
|
* The ASN1_TYPE is expected to contain a V_ASN1_SEQUENCE value.
|
||||||
|
*
|
||||||
|
* [in] content: ASN1_TYPE containing DER-encoded SpcIndirectDataContent
|
||||||
|
* [returns] newly allocated SpcIndirectDataContent, or NULL on error
|
||||||
|
*
|
||||||
|
* The caller is responsible for freeing the returned object with
|
||||||
|
* SpcIndirectDataContent_free().
|
||||||
|
*/
|
||||||
|
SpcIndirectDataContent *asn1_type_get_indirect_data_content(ASN1_TYPE *content)
|
||||||
|
{
|
||||||
|
ASN1_STRING *value;
|
||||||
|
const unsigned char *data;
|
||||||
|
int len;
|
||||||
|
|
||||||
|
if (!content || content->type != V_ASN1_SEQUENCE)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
value = content->value.sequence;
|
||||||
|
if (!value)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
data = ASN1_STRING_get0_data(value);
|
||||||
|
len = ASN1_STRING_length(value);
|
||||||
|
|
||||||
|
return d2i_SpcIndirectDataContent(NULL, &data, len);
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Return spcIndirectDataContent.
|
* Return spcIndirectDataContent.
|
||||||
* [in] hash: message digest BIO
|
* [in] hash: message digest BIO
|
||||||
@@ -548,33 +633,6 @@ SpcLink *spc_link_obsolete_get(void)
|
|||||||
return link;
|
return link;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* Safely extract digest from SpcIndirectDataContent
|
|
||||||
* [in] idc: parsed SpcIndirectDataContent
|
|
||||||
* [out] mdbuf: output buffer (must be EVP_MAX_MD_SIZE bytes)
|
|
||||||
* [out] mdtype: digest algorithm's NID
|
|
||||||
* [returns] -1 on error or digest length on success
|
|
||||||
*/
|
|
||||||
int spc_extract_digest_safe(SpcIndirectDataContent *idc,
|
|
||||||
u_char *mdbuf, int *mdtype)
|
|
||||||
{
|
|
||||||
int digest_len;
|
|
||||||
|
|
||||||
if (!idc || !idc->messageDigest || !idc->messageDigest->digest ||
|
|
||||||
!idc->messageDigest->digestAlgorithm) {
|
|
||||||
fprintf(stderr, "Missing digest data\n");
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
digest_len = idc->messageDigest->digest->length;
|
|
||||||
if (digest_len <= 0 || digest_len > EVP_MAX_MD_SIZE) {
|
|
||||||
fprintf(stderr, "Invalid digest length: %d\n", digest_len);
|
|
||||||
return -1;
|
|
||||||
}
|
|
||||||
memcpy(mdbuf, idc->messageDigest->digest->data, (size_t)digest_len);
|
|
||||||
*mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm);
|
|
||||||
return digest_len;
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* [in] mdbuf, cmdbuf: message digests
|
* [in] mdbuf, cmdbuf: message digests
|
||||||
* [in] mdtype: message digest algorithm type
|
* [in] mdtype: message digest algorithm type
|
||||||
@@ -590,6 +648,42 @@ int compare_digests(u_char *mdbuf, u_char *cmdbuf, int mdtype)
|
|||||||
return mdok;
|
return mdok;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Safely extract digest from SpcIndirectDataContent with bounds checking.
|
||||||
|
* This function validates that the digest length from the ASN.1 structure
|
||||||
|
* does not exceed the destination buffer size, preventing buffer overflows
|
||||||
|
* from maliciously crafted signatures.
|
||||||
|
* [in] idc: parsed SpcIndirectDataContent structure
|
||||||
|
* [out] mdbuf: output buffer (must be at least EVP_MAX_MD_SIZE bytes)
|
||||||
|
* [out] mdtype: digest algorithm NID
|
||||||
|
* [returns] digest length on success, -1 on error
|
||||||
|
*/
|
||||||
|
int spc_indirect_data_content_get_digest(SpcIndirectDataContent *idc, u_char *mdbuf, int *mdtype)
|
||||||
|
{
|
||||||
|
ASN1_OCTET_STRING *digest_asn1;
|
||||||
|
const unsigned char *digest_data;
|
||||||
|
int digest_len;
|
||||||
|
|
||||||
|
if (!idc || !idc->messageDigest || !idc->messageDigest->digest ||
|
||||||
|
!idc->messageDigest->digestAlgorithm) {
|
||||||
|
return -1; /* FAILED */
|
||||||
|
}
|
||||||
|
digest_asn1 = idc->messageDigest->digest;
|
||||||
|
digest_len = ASN1_STRING_length((ASN1_STRING *)digest_asn1);
|
||||||
|
|
||||||
|
/* Validate digest length to prevent buffer overflow */
|
||||||
|
if (digest_len <= 0 || digest_len > EVP_MAX_MD_SIZE) {
|
||||||
|
fprintf(stderr, "Invalid digest length in signature: %d (expected 1-%d)\n",
|
||||||
|
digest_len, EVP_MAX_MD_SIZE);
|
||||||
|
return -1; /* FAILED */
|
||||||
|
}
|
||||||
|
|
||||||
|
digest_data = ASN1_STRING_get0_data((ASN1_STRING *)digest_asn1);
|
||||||
|
*mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm);
|
||||||
|
memcpy(mdbuf, digest_data, (size_t)digest_len);
|
||||||
|
return digest_len; /* OK */
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Helper functions
|
* Helper functions
|
||||||
*/
|
*/
|
||||||
@@ -645,8 +739,16 @@ static int spc_indirect_data_content_create(u_char **blob, int *len, FILE_FORMAT
|
|||||||
idc->data->value->type = V_ASN1_SEQUENCE;
|
idc->data->value->type = V_ASN1_SEQUENCE;
|
||||||
idc->data->value->value.sequence = ASN1_STRING_new();
|
idc->data->value->value.sequence = ASN1_STRING_new();
|
||||||
idc->data->type = ctx->format->data_blob_get(&p, &l, ctx);
|
idc->data->type = ctx->format->data_blob_get(&p, &l, ctx);
|
||||||
idc->data->value->value.sequence->data = p;
|
if (!idc->data->type) {
|
||||||
idc->data->value->value.sequence->length = l;
|
SpcIndirectDataContent_free(idc);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
if (!ASN1_STRING_set(idc->data->value->value.sequence, p, l)) {
|
||||||
|
OPENSSL_free(p);
|
||||||
|
SpcIndirectDataContent_free(idc);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
OPENSSL_free(p);
|
||||||
idc->messageDigest->digestAlgorithm->algorithm = OBJ_nid2obj(mdtype);
|
idc->messageDigest->digestAlgorithm->algorithm = OBJ_nid2obj(mdtype);
|
||||||
idc->messageDigest->digestAlgorithm->parameters = ASN1_TYPE_new();
|
idc->messageDigest->digestAlgorithm->parameters = ASN1_TYPE_new();
|
||||||
idc->messageDigest->digestAlgorithm->parameters->type = V_ASN1_NULL;
|
idc->messageDigest->digestAlgorithm->parameters->type = V_ASN1_NULL;
|
||||||
|
|||||||
@@ -15,6 +15,9 @@ PKCS7 *pkcs7_create(FILE_FORMAT_CTX *ctx);
|
|||||||
int add_indirect_data_object(PKCS7 *p7);
|
int add_indirect_data_object(PKCS7 *p7);
|
||||||
int sign_spc_indirect_data_content(PKCS7 *p7, ASN1_OCTET_STRING *content);
|
int sign_spc_indirect_data_content(PKCS7 *p7, ASN1_OCTET_STRING *content);
|
||||||
PKCS7 *pkcs7_set_content(ASN1_OCTET_STRING *content);
|
PKCS7 *pkcs7_set_content(ASN1_OCTET_STRING *content);
|
||||||
|
int pkcs7_get_content_digest(PKCS7 *p7, u_char *mdbuf, int *mdtype);
|
||||||
|
SpcIndirectDataContent *pkcs7_get_indirect_data_content(PKCS7 *p7);
|
||||||
|
SpcIndirectDataContent *asn1_type_get_indirect_data_content(ASN1_TYPE *content);
|
||||||
ASN1_OCTET_STRING *spc_indirect_data_content_get(BIO *hash, FILE_FORMAT_CTX *ctx);
|
ASN1_OCTET_STRING *spc_indirect_data_content_get(BIO *hash, FILE_FORMAT_CTX *ctx);
|
||||||
int pkcs7_sign_content(PKCS7 *p7, const u_char *data, int len);
|
int pkcs7_sign_content(PKCS7 *p7, const u_char *data, int len);
|
||||||
int asn1_simple_hdr_len(const u_char *p, int len);
|
int asn1_simple_hdr_len(const u_char *p, int len);
|
||||||
@@ -24,9 +27,8 @@ int is_content_type(PKCS7 *p7, const char *objid);
|
|||||||
MsCtlContent *ms_ctl_content_get(PKCS7 *p7);
|
MsCtlContent *ms_ctl_content_get(PKCS7 *p7);
|
||||||
ASN1_TYPE *catalog_content_get(CatalogAuthAttr *attribute);
|
ASN1_TYPE *catalog_content_get(CatalogAuthAttr *attribute);
|
||||||
SpcLink *spc_link_obsolete_get(void);
|
SpcLink *spc_link_obsolete_get(void);
|
||||||
int spc_extract_digest_safe(SpcIndirectDataContent *idc,
|
|
||||||
u_char *mdbuf, int *mdtype);
|
|
||||||
int compare_digests(u_char *mdbuf, u_char *cmdbuf, int mdtype);
|
int compare_digests(u_char *mdbuf, u_char *cmdbuf, int mdtype);
|
||||||
|
int spc_indirect_data_content_get_digest(SpcIndirectDataContent *idc, u_char *mdbuf, int *mdtype);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Local Variables:
|
Local Variables:
|
||||||
|
|||||||
@@ -414,19 +414,7 @@ static int msi_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
|
|||||||
const EVP_MD *md;
|
const EVP_MD *md;
|
||||||
BIO *hash;
|
BIO *hash;
|
||||||
|
|
||||||
if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) {
|
if (!pkcs7_get_content_digest(p7, mdbuf, &mdtype)) {
|
||||||
ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence;
|
|
||||||
const u_char *p = content_val->data;
|
|
||||||
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
|
|
||||||
if (idc) {
|
|
||||||
if (spc_extract_digest_safe(idc, mdbuf, &mdtype) < 0) {
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
return 0; /* FAILED */
|
|
||||||
}
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mdtype == -1) {
|
|
||||||
fprintf(stderr, "Failed to extract current message digest\n\n");
|
fprintf(stderr, "Failed to extract current message digest\n\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
|
|||||||
+278
-197
@@ -67,7 +67,7 @@
|
|||||||
* 2:d=1 hl=2 l= 2 prim: BIT STRING
|
* 2:d=1 hl=2 l= 2 prim: BIT STRING
|
||||||
* 6:d=1 hl=2 l= 0 cons: SEQUENCE
|
* 6:d=1 hl=2 l= 0 cons: SEQUENCE
|
||||||
*/
|
*/
|
||||||
const u_char java_attrs_low[] = {
|
static const u_char java_attrs_low[] = {
|
||||||
0x30, 0x06, 0x03, 0x02, 0x00, 0x01, 0x30, 0x00
|
0x30, 0x06, 0x03, 0x02, 0x00, 0x01, 0x30, 0x00
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -76,7 +76,7 @@ const u_char java_attrs_low[] = {
|
|||||||
* 0:d=0 hl=2 l= 12 cons: SEQUENCE
|
* 0:d=0 hl=2 l= 12 cons: SEQUENCE
|
||||||
* 2:d=1 hl=2 l= 10 prim: OBJECT :Microsoft Individual Code Signing
|
* 2:d=1 hl=2 l= 10 prim: OBJECT :Microsoft Individual Code Signing
|
||||||
*/
|
*/
|
||||||
const u_char purpose_ind[] = {
|
static const u_char purpose_ind[] = {
|
||||||
0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04,
|
0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04,
|
||||||
0x01, 0x82, 0x37, 0x02, 0x01, 0x15
|
0x01, 0x82, 0x37, 0x02, 0x01, 0x15
|
||||||
};
|
};
|
||||||
@@ -86,11 +86,13 @@ const u_char purpose_ind[] = {
|
|||||||
* 0:d=0 hl=2 l= 12 cons: SEQUENCE
|
* 0:d=0 hl=2 l= 12 cons: SEQUENCE
|
||||||
* 2:d=1 hl=2 l= 10 prim: OBJECT :Microsoft Commercial Code Signing
|
* 2:d=1 hl=2 l= 10 prim: OBJECT :Microsoft Commercial Code Signing
|
||||||
*/
|
*/
|
||||||
const u_char purpose_comm[] = {
|
static const u_char purpose_comm[] = {
|
||||||
0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04,
|
0x30, 0x0c, 0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04,
|
||||||
0x01, 0x82, 0x37, 0x02, 0x01, 0x16
|
0x01, 0x82, 0x37, 0x02, 0x01, 0x16
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static UI_METHOD *ui_method;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* ASN.1 definitions (more or less from official MS Authenticode docs)
|
* ASN.1 definitions (more or less from official MS Authenticode docs)
|
||||||
*/
|
*/
|
||||||
@@ -230,7 +232,7 @@ static ASN1_INTEGER *create_nonce(int bits);
|
|||||||
static char *clrdp_url_get_x509(X509 *cert);
|
static char *clrdp_url_get_x509(X509 *cert);
|
||||||
static time_t time_t_get_asn1_time(const ASN1_TIME *s);
|
static time_t time_t_get_asn1_time(const ASN1_TIME *s);
|
||||||
static time_t time_t_get_si_time(PKCS7_SIGNER_INFO *si);
|
static time_t time_t_get_si_time(PKCS7_SIGNER_INFO *si);
|
||||||
static ASN1_UTCTIME *asn1_time_get_si_time(PKCS7_SIGNER_INFO *si);
|
static const ASN1_UTCTIME *asn1_time_get_si_time(PKCS7_SIGNER_INFO *si);
|
||||||
static time_t time_t_get_cms_time(CMS_ContentInfo *cms);
|
static time_t time_t_get_cms_time(CMS_ContentInfo *cms);
|
||||||
static CMS_ContentInfo *cms_get_timestamp(PKCS7_SIGNED *p7_signed,
|
static CMS_ContentInfo *cms_get_timestamp(PKCS7_SIGNED *p7_signed,
|
||||||
PKCS7_SIGNER_INFO *countersignature);
|
PKCS7_SIGNER_INFO *countersignature);
|
||||||
@@ -241,6 +243,7 @@ static int X509_attribute_chain_append_object(STACK_OF(X509_ATTRIBUTE) **unauth_
|
|||||||
static STACK_OF(PKCS7) *signature_list_create(PKCS7 *p7);
|
static STACK_OF(PKCS7) *signature_list_create(PKCS7 *p7);
|
||||||
static int PKCS7_compare(const PKCS7 *const *a, const PKCS7 *const *b);
|
static int PKCS7_compare(const PKCS7 *const *a, const PKCS7 *const *b);
|
||||||
static PKCS7 *pkcs7_get_sigfile(FILE_FORMAT_CTX *ctx);
|
static PKCS7 *pkcs7_get_sigfile(FILE_FORMAT_CTX *ctx);
|
||||||
|
static STACK_OF(PKCS7) *get_signature_list(FILE_FORMAT_CTX *ctx, GLOBAL_OPTIONS *options);
|
||||||
static void print_cert(X509 *cert, int i);
|
static void print_cert(X509 *cert, int i);
|
||||||
static int x509_store_load_crlfile(X509_STORE *store, char *cafile, char *crlfile);
|
static int x509_store_load_crlfile(X509_STORE *store, char *cafile, char *crlfile);
|
||||||
static void load_objects_from_store(const char *url, char *pass, EVP_PKEY **pkey, STACK_OF(X509) *certs, STACK_OF(X509_CRL) *crls);
|
static void load_objects_from_store(const char *url, char *pass, EVP_PKEY **pkey, STACK_OF(X509) *certs, STACK_OF(X509_CRL) *crls);
|
||||||
@@ -293,7 +296,8 @@ static BIO *bio_encode_rfc3161_request(PKCS7 *p7, const EVP_MD *md)
|
|||||||
TS_REQ *req = NULL;
|
TS_REQ *req = NULL;
|
||||||
BIO *bout = NULL, *bhash = NULL;
|
BIO *bout = NULL, *bhash = NULL;
|
||||||
u_char *p;
|
u_char *p;
|
||||||
int len;
|
const u_char *digest;
|
||||||
|
int digest_len, len;
|
||||||
|
|
||||||
signer_info = PKCS7_get_signer_info(p7);
|
signer_info = PKCS7_get_signer_info(p7);
|
||||||
if (!signer_info)
|
if (!signer_info)
|
||||||
@@ -303,6 +307,9 @@ static BIO *bio_encode_rfc3161_request(PKCS7 *p7, const EVP_MD *md)
|
|||||||
if (!si)
|
if (!si)
|
||||||
goto out;
|
goto out;
|
||||||
|
|
||||||
|
digest = ASN1_STRING_get0_data(si->enc_digest);
|
||||||
|
digest_len = ASN1_STRING_length(si->enc_digest);
|
||||||
|
|
||||||
bhash = BIO_new(BIO_f_md());
|
bhash = BIO_new(BIO_f_md());
|
||||||
#if defined(__GNUC__)
|
#if defined(__GNUC__)
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
@@ -316,7 +323,7 @@ static BIO *bio_encode_rfc3161_request(PKCS7 *p7, const EVP_MD *md)
|
|||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
BIO_push(bhash, BIO_new(BIO_s_null()));
|
BIO_push(bhash, BIO_new(BIO_s_null()));
|
||||||
BIO_write(bhash, si->enc_digest->data, si->enc_digest->length);
|
BIO_write(bhash, digest, digest_len);
|
||||||
BIO_gets(bhash, (char*)mdbuf, EVP_MD_size(md));
|
BIO_gets(bhash, (char*)mdbuf, EVP_MD_size(md));
|
||||||
|
|
||||||
req = TS_REQ_new();
|
req = TS_REQ_new();
|
||||||
@@ -374,6 +381,7 @@ static ASN1_INTEGER *create_nonce(int bits)
|
|||||||
{
|
{
|
||||||
unsigned char buf[20];
|
unsigned char buf[20];
|
||||||
ASN1_INTEGER *nonce = NULL;
|
ASN1_INTEGER *nonce = NULL;
|
||||||
|
BIGNUM *bn = NULL;
|
||||||
int len = (bits - 1) / 8 + 1;
|
int len = (bits - 1) / 8 + 1;
|
||||||
int i;
|
int i;
|
||||||
|
|
||||||
@@ -388,15 +396,21 @@ static ASN1_INTEGER *create_nonce(int bits)
|
|||||||
/* Find the first non-zero byte and creating ASN1_INTEGER object. */
|
/* Find the first non-zero byte and creating ASN1_INTEGER object. */
|
||||||
for (i = 0; i < len && !buf[i]; ++i) {
|
for (i = 0; i < len && !buf[i]; ++i) {
|
||||||
}
|
}
|
||||||
nonce = ASN1_INTEGER_new();
|
|
||||||
|
bn = BN_bin2bn(buf + i, len - i, NULL);
|
||||||
|
if (!bn) {
|
||||||
|
fprintf(stderr, "Could not create nonce BIGNUM\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
nonce = BN_to_ASN1_INTEGER(bn, NULL);
|
||||||
|
BN_free(bn);
|
||||||
|
|
||||||
if (!nonce) {
|
if (!nonce) {
|
||||||
fprintf(stderr, "Could not create nonce\n");
|
fprintf(stderr, "Could not create nonce\n");
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
OPENSSL_free(nonce->data);
|
|
||||||
nonce->length = len - i;
|
|
||||||
nonce->data = OPENSSL_malloc((size_t)nonce->length + 1);
|
|
||||||
memcpy(nonce->data, buf + i, (size_t)nonce->length);
|
|
||||||
return nonce;
|
return nonce;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1621,7 +1635,7 @@ static int X509_attribute_chain_append_object(STACK_OF(X509_ATTRIBUTE) **unauth_
|
|||||||
u_char *p, int len, const char *oid)
|
u_char *p, int len, const char *oid)
|
||||||
{
|
{
|
||||||
X509_ATTRIBUTE *attr = NULL;
|
X509_ATTRIBUTE *attr = NULL;
|
||||||
ASN1_OBJECT *object;
|
const ASN1_OBJECT *object;
|
||||||
char object_txt[128];
|
char object_txt[128];
|
||||||
|
|
||||||
if (*unauth_attr == NULL) {
|
if (*unauth_attr == NULL) {
|
||||||
@@ -1632,7 +1646,7 @@ static int X509_attribute_chain_append_object(STACK_OF(X509_ATTRIBUTE) **unauth_
|
|||||||
int i;
|
int i;
|
||||||
for (i = 0; i < X509at_get_attr_count(*unauth_attr); i++) {
|
for (i = 0; i < X509at_get_attr_count(*unauth_attr); i++) {
|
||||||
attr = X509at_get_attr(*unauth_attr, i);
|
attr = X509at_get_attr(*unauth_attr, i);
|
||||||
object = X509_ATTRIBUTE_get0_object(attr);
|
object = (const ASN1_OBJECT *)X509_ATTRIBUTE_get0_object(attr);
|
||||||
if (object == NULL)
|
if (object == NULL)
|
||||||
continue;
|
continue;
|
||||||
object_txt[0] = 0x00;
|
object_txt[0] = 0x00;
|
||||||
@@ -2196,18 +2210,22 @@ static int verify_timestamp_token(PKCS7 *p7, CMS_ContentInfo *timestamp)
|
|||||||
/* get the embedded content */
|
/* get the embedded content */
|
||||||
pos = CMS_get0_content(timestamp);
|
pos = CMS_get0_content(timestamp);
|
||||||
if (pos != NULL && *pos != NULL) {
|
if (pos != NULL && *pos != NULL) {
|
||||||
const u_char *p = (*pos)->data;
|
const u_char *p = ASN1_STRING_get0_data(*pos);
|
||||||
TS_TST_INFO *token = d2i_TS_TST_INFO(NULL, &p, (*pos)->length);
|
int len = ASN1_STRING_length(*pos);
|
||||||
|
TS_TST_INFO *token = d2i_TS_TST_INFO(NULL, &p, len);
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
BIO *bhash;
|
BIO *bhash;
|
||||||
u_char mdbuf[EVP_MAX_MD_SIZE];
|
u_char mdbuf[EVP_MAX_MD_SIZE];
|
||||||
ASN1_OCTET_STRING *hash;
|
ASN1_OCTET_STRING *hash;
|
||||||
const ASN1_OBJECT *aoid;
|
const ASN1_OBJECT *aoid;
|
||||||
int md_nid;
|
const u_char *hash_data;
|
||||||
|
int hash_len, md_nid;
|
||||||
const EVP_MD *md;
|
const EVP_MD *md;
|
||||||
TS_MSG_IMPRINT *msg_imprint = TS_TST_INFO_get_msg_imprint(token);
|
TS_MSG_IMPRINT *msg_imprint = TS_TST_INFO_get_msg_imprint(token);
|
||||||
const X509_ALGOR *alg = TS_MSG_IMPRINT_get_algo(msg_imprint);
|
const X509_ALGOR *alg = TS_MSG_IMPRINT_get_algo(msg_imprint);
|
||||||
|
const u_char *digest = ASN1_STRING_get0_data(si->enc_digest);
|
||||||
|
int digest_len = ASN1_STRING_length(si->enc_digest);
|
||||||
|
|
||||||
X509_ALGOR_get0(&aoid, NULL, NULL, alg);
|
X509_ALGOR_get0(&aoid, NULL, NULL, alg);
|
||||||
md_nid = OBJ_obj2nid(aoid);
|
md_nid = OBJ_obj2nid(aoid);
|
||||||
@@ -2229,17 +2247,19 @@ static int verify_timestamp_token(PKCS7 *p7, CMS_ContentInfo *timestamp)
|
|||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
BIO_push(bhash, BIO_new(BIO_s_null()));
|
BIO_push(bhash, BIO_new(BIO_s_null()));
|
||||||
BIO_write(bhash, si->enc_digest->data, si->enc_digest->length);
|
BIO_write(bhash, digest, digest_len);
|
||||||
BIO_gets(bhash, (char*)mdbuf, EVP_MD_size(md));
|
BIO_gets(bhash, (char*)mdbuf, EVP_MD_size(md));
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
|
|
||||||
/* compare the provided hash against the computed hash */
|
/* compare the provided hash against the computed hash */
|
||||||
hash =TS_MSG_IMPRINT_get_msg(msg_imprint);
|
hash =TS_MSG_IMPRINT_get_msg(msg_imprint);
|
||||||
if (memcmp(mdbuf, hash->data, (size_t)hash->length)) {
|
hash_data = ASN1_STRING_get0_data(hash);
|
||||||
|
hash_len = ASN1_STRING_length(hash);
|
||||||
|
if (memcmp(mdbuf, hash_data, (size_t)hash_len)) {
|
||||||
printf("Hash value mismatch:\n\tMessage digest algorithm: %s\n",
|
printf("Hash value mismatch:\n\tMessage digest algorithm: %s\n",
|
||||||
(md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(md_nid));
|
(md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2ln(md_nid));
|
||||||
print_hash("\tComputed message digest", "", mdbuf, EVP_MD_size(md));
|
print_hash("\tComputed message digest", "", mdbuf, EVP_MD_size(md));
|
||||||
print_hash("\tReceived message digest", "", hash->data, hash->length);
|
print_hash("\tReceived message digest", "", hash_data, hash_len);
|
||||||
printf("\nFile's message digest verification: failed\n");
|
printf("\nFile's message digest verification: failed\n");
|
||||||
TS_TST_INFO_free(token);
|
TS_TST_INFO_free(token);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
@@ -2413,7 +2433,7 @@ static int verify_pkcs7_data(PKCS7 *p7, X509_STORE *store)
|
|||||||
&& (contents->d.other->value.sequence->length > 0)) {
|
&& (contents->d.other->value.sequence->length > 0)) {
|
||||||
if (contents->d.other->type == V_ASN1_SEQUENCE) {
|
if (contents->d.other->type == V_ASN1_SEQUENCE) {
|
||||||
/* only verify the content of the sequence */
|
/* only verify the content of the sequence */
|
||||||
const unsigned char *data = contents->d.other->value.sequence->data;
|
const u_char *data = contents->d.other->value.sequence->data;
|
||||||
long len;
|
long len;
|
||||||
int inf, tag, class;
|
int inf, tag, class;
|
||||||
|
|
||||||
@@ -2685,11 +2705,12 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
|
|||||||
{
|
{
|
||||||
STACK_OF(PKCS7_SIGNER_INFO) *signer_info;
|
STACK_OF(PKCS7_SIGNER_INFO) *signer_info;
|
||||||
PKCS7_SIGNER_INFO *si;
|
PKCS7_SIGNER_INFO *si;
|
||||||
int md_nid, i;
|
int md_nid, i, len;
|
||||||
STACK_OF(X509_ATTRIBUTE) *auth_attr, *unauth_attr;
|
STACK_OF(X509_ATTRIBUTE) *auth_attr, *unauth_attr;
|
||||||
X509_ATTRIBUTE *attr;
|
X509_ATTRIBUTE *attr;
|
||||||
ASN1_OBJECT *object;
|
const ASN1_OBJECT *object;
|
||||||
ASN1_STRING *value;
|
const ASN1_STRING *value;
|
||||||
|
const u_char *data;
|
||||||
char object_txt[128];
|
char object_txt[128];
|
||||||
time_t time = INVALID_TIME;
|
time_t time = INVALID_TIME;
|
||||||
|
|
||||||
@@ -2708,24 +2729,24 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
|
|||||||
printf("\nAuthenticated attributes:\n");
|
printf("\nAuthenticated attributes:\n");
|
||||||
for (i=0; i<X509at_get_attr_count(auth_attr); i++) {
|
for (i=0; i<X509at_get_attr_count(auth_attr); i++) {
|
||||||
attr = X509at_get_attr(auth_attr, i);
|
attr = X509at_get_attr(auth_attr, i);
|
||||||
object = X509_ATTRIBUTE_get0_object(attr);
|
object = (const ASN1_OBJECT *)X509_ATTRIBUTE_get0_object(attr);
|
||||||
if (object == NULL)
|
if (object == NULL)
|
||||||
continue;
|
continue;
|
||||||
object_txt[0] = 0x00;
|
object_txt[0] = 0x00;
|
||||||
OBJ_obj2txt(object_txt, sizeof object_txt, object, 1);
|
OBJ_obj2txt(object_txt, sizeof object_txt, object, 1);
|
||||||
if (!strcmp(object_txt, PKCS9_MESSAGE_DIGEST)) {
|
if (!strcmp(object_txt, PKCS9_MESSAGE_DIGEST)) {
|
||||||
/* PKCS#9 message digest - Policy OID: 1.2.840.113549.1.9.4 */
|
/* PKCS#9 message digest - Policy OID: 1.2.840.113549.1.9.4 */
|
||||||
const u_char *mdbuf;
|
value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_OCTET_STRING, NULL);
|
||||||
int len;
|
if (value == NULL)
|
||||||
ASN1_STRING *digest = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_OCTET_STRING, NULL);
|
|
||||||
if (digest == NULL)
|
|
||||||
continue;
|
continue;
|
||||||
mdbuf = ASN1_STRING_get0_data(digest);
|
data = ASN1_STRING_get0_data(value);
|
||||||
len = ASN1_STRING_length(digest);
|
len = ASN1_STRING_length(value);
|
||||||
print_hash("\tMessage digest", "", mdbuf, len);
|
print_hash("\tMessage digest", "", data, len);
|
||||||
} else if (!strcmp(object_txt, PKCS9_SIGNING_TIME)) {
|
} else if (!strcmp(object_txt, PKCS9_SIGNING_TIME)) {
|
||||||
/* PKCS#9 signing time - Policy OID: 1.2.840.113549.1.9.5 */
|
/* PKCS#9 signing time - Policy OID: 1.2.840.113549.1.9.5 */
|
||||||
ASN1_UTCTIME *signtime = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTCTIME, NULL);
|
const ASN1_UTCTIME *signtime;
|
||||||
|
|
||||||
|
signtime = (const ASN1_UTCTIME *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTCTIME, NULL);
|
||||||
if (signtime == NULL)
|
if (signtime == NULL)
|
||||||
continue;
|
continue;
|
||||||
printf("\tSigning time: ");
|
printf("\tSigning time: ");
|
||||||
@@ -2733,30 +2754,39 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
|
|||||||
} else if (!strcmp(object_txt, SPC_SP_OPUS_INFO_OBJID)) {
|
} else if (!strcmp(object_txt, SPC_SP_OPUS_INFO_OBJID)) {
|
||||||
/* Microsoft OID: 1.3.6.1.4.1.311.2.1.12 */
|
/* Microsoft OID: 1.3.6.1.4.1.311.2.1.12 */
|
||||||
SpcSpOpusInfo *opus;
|
SpcSpOpusInfo *opus;
|
||||||
const u_char *data;
|
|
||||||
value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
|
value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
|
||||||
if (value == NULL)
|
if (value == NULL)
|
||||||
continue;
|
continue;
|
||||||
data = ASN1_STRING_get0_data(value);
|
data = ASN1_STRING_get0_data(value);
|
||||||
opus = d2i_SpcSpOpusInfo(NULL, &data, ASN1_STRING_length(value));
|
len = ASN1_STRING_length(value);
|
||||||
|
opus = d2i_SpcSpOpusInfo(NULL, &data, len);
|
||||||
if (opus == NULL)
|
if (opus == NULL)
|
||||||
continue;
|
continue;
|
||||||
if (opus->moreInfo && opus->moreInfo->type == 0) {
|
if (opus->moreInfo && opus->moreInfo->type == 0) {
|
||||||
char *url = OPENSSL_strdup((char *)opus->moreInfo->value.url->data);
|
ASN1_IA5STRING *url_asn1 = opus->moreInfo->value.url;
|
||||||
printf("\tURL description: %s\n", url);
|
const u_char *url_data = ASN1_STRING_get0_data((ASN1_STRING *)url_asn1);
|
||||||
OPENSSL_free(url);
|
int url_length = ASN1_STRING_length((ASN1_STRING *)url_asn1);
|
||||||
|
|
||||||
|
printf("\tURL description: %.*s\n", url_length, url_data);
|
||||||
}
|
}
|
||||||
if (opus->programName) {
|
if (opus->programName) {
|
||||||
char *desc = NULL;
|
char *desc = NULL;
|
||||||
|
|
||||||
if (opus->programName->type == 0) {
|
if (opus->programName->type == 0) {
|
||||||
u_char *opusdata;
|
u_char *opus_data;
|
||||||
int len = ASN1_STRING_to_UTF8(&opusdata, opus->programName->value.unicode);
|
int opus_len = ASN1_STRING_to_UTF8(&opus_data, opus->programName->value.unicode);
|
||||||
if (len >= 0) {
|
|
||||||
desc = OPENSSL_strndup((char *)opusdata, (size_t)len);
|
if (opus_len >= 0) {
|
||||||
OPENSSL_free(opusdata);
|
desc = OPENSSL_strndup((char *)opus_data, (size_t)opus_len);
|
||||||
|
OPENSSL_free(opus_data);
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
desc = OPENSSL_strdup((char *)opus->programName->value.ascii->data);
|
ASN1_IA5STRING *desc_asn1 = opus->programName->value.ascii;
|
||||||
|
const u_char *desc_data = ASN1_STRING_get0_data((ASN1_STRING *)desc_asn1);
|
||||||
|
int desc_len = ASN1_STRING_length((ASN1_STRING *)desc_asn1);
|
||||||
|
|
||||||
|
desc = OPENSSL_strndup((const char *)desc_data, (size_t)desc_len);
|
||||||
}
|
}
|
||||||
if (desc) {
|
if (desc) {
|
||||||
printf("\tText description: %s\n", desc);
|
printf("\tText description: %s\n", desc);
|
||||||
@@ -2766,31 +2796,31 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
|
|||||||
SpcSpOpusInfo_free(opus);
|
SpcSpOpusInfo_free(opus);
|
||||||
} else if (!strcmp(object_txt, SPC_STATEMENT_TYPE_OBJID)) {
|
} else if (!strcmp(object_txt, SPC_STATEMENT_TYPE_OBJID)) {
|
||||||
/* Microsoft OID: 1.3.6.1.4.1.311.2.1.11 */
|
/* Microsoft OID: 1.3.6.1.4.1.311.2.1.11 */
|
||||||
const u_char *purpose;
|
value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
|
||||||
value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
|
|
||||||
if (value == NULL)
|
if (value == NULL)
|
||||||
continue;
|
continue;
|
||||||
purpose = ASN1_STRING_get0_data(value);
|
data = ASN1_STRING_get0_data(value);
|
||||||
if (!memcmp(purpose, purpose_comm, sizeof purpose_comm))
|
if (!memcmp(data, purpose_comm, sizeof purpose_comm))
|
||||||
printf("\tMicrosoft Commercial Code Signing purpose\n");
|
printf("\tMicrosoft Commercial Code Signing purpose\n");
|
||||||
else if (!memcmp(purpose, purpose_ind, sizeof purpose_ind))
|
else if (!memcmp(data, purpose_ind, sizeof purpose_ind))
|
||||||
printf("\tMicrosoft Individual Code Signing purpose\n");
|
printf("\tMicrosoft Individual Code Signing purpose\n");
|
||||||
else
|
else
|
||||||
printf("\tUnrecognized Code Signing purpose\n");
|
printf("\tUnrecognized Code Signing purpose\n");
|
||||||
} else if (!strcmp(object_txt, MS_JAVA_SOMETHING)) {
|
} else if (!strcmp(object_txt, MS_JAVA_SOMETHING)) {
|
||||||
/* Microsoft OID: 1.3.6.1.4.1.311.15.1 */
|
/* Microsoft OID: 1.3.6.1.4.1.311.15.1 */
|
||||||
const u_char *level;
|
value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
|
||||||
value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
|
|
||||||
if (value == NULL)
|
if (value == NULL)
|
||||||
continue;
|
continue;
|
||||||
level = ASN1_STRING_get0_data(value);
|
data = ASN1_STRING_get0_data(value);
|
||||||
if (!memcmp(level, java_attrs_low, sizeof java_attrs_low))
|
if (!memcmp(data, java_attrs_low, sizeof java_attrs_low))
|
||||||
printf("\tLow level of permissions in Microsoft Internet Explorer 4.x for CAB files\n");
|
printf("\tLow level of permissions in Microsoft Internet Explorer 4.x for CAB files\n");
|
||||||
else
|
else
|
||||||
printf("\tUnrecognized level of permissions in Microsoft Internet Explorer 4.x for CAB files\n");
|
printf("\tUnrecognized level of permissions in Microsoft Internet Explorer 4.x for CAB files\n");
|
||||||
} else if (!strcmp(object_txt, PKCS9_SEQUENCE_NUMBER)) {
|
} else if (!strcmp(object_txt, PKCS9_SEQUENCE_NUMBER)) {
|
||||||
/* PKCS#9 sequence number - Policy OID: 1.2.840.113549.1.9.25.4 */
|
/* PKCS#9 sequence number - Policy OID: 1.2.840.113549.1.9.25.4 */
|
||||||
ASN1_INTEGER *number = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_INTEGER, NULL);
|
const ASN1_INTEGER *number;
|
||||||
|
|
||||||
|
number = (const ASN1_INTEGER *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_INTEGER, NULL);
|
||||||
if (number == NULL)
|
if (number == NULL)
|
||||||
continue;
|
continue;
|
||||||
printf("\tSequence number: %ld\n", ASN1_INTEGER_get(number));
|
printf("\tSequence number: %ld\n", ASN1_INTEGER_get(number));
|
||||||
@@ -2801,17 +2831,17 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
|
|||||||
unauth_attr = PKCS7_get_attributes(si); /* cont[1] */
|
unauth_attr = PKCS7_get_attributes(si); /* cont[1] */
|
||||||
for (i=0; i<X509at_get_attr_count(unauth_attr); i++) {
|
for (i=0; i<X509at_get_attr_count(unauth_attr); i++) {
|
||||||
attr = X509at_get_attr(unauth_attr, i);
|
attr = X509at_get_attr(unauth_attr, i);
|
||||||
object = X509_ATTRIBUTE_get0_object(attr);
|
object = (const ASN1_OBJECT *)X509_ATTRIBUTE_get0_object(attr);
|
||||||
if (object == NULL)
|
if (object == NULL)
|
||||||
continue;
|
continue;
|
||||||
object_txt[0] = 0x00;
|
object_txt[0] = 0x00;
|
||||||
OBJ_obj2txt(object_txt, sizeof object_txt, object, 1);
|
OBJ_obj2txt(object_txt, sizeof object_txt, object, 1);
|
||||||
if (!strcmp(object_txt, PKCS9_COUNTER_SIGNATURE)) {
|
if (!strcmp(object_txt, PKCS9_COUNTER_SIGNATURE)) {
|
||||||
/* Authenticode Timestamp - Policy OID: 1.2.840.113549.1.9.6 */
|
/* Authenticode Timestamp - Policy OID: 1.2.840.113549.1.9.6 */
|
||||||
const u_char *data;
|
|
||||||
CMS_ContentInfo *cms;
|
CMS_ContentInfo *cms;
|
||||||
PKCS7_SIGNER_INFO *countersi;
|
PKCS7_SIGNER_INFO *countersi;
|
||||||
value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
|
|
||||||
|
value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
|
||||||
if (value == NULL)
|
if (value == NULL)
|
||||||
continue;
|
continue;
|
||||||
data = ASN1_STRING_get0_data(value);
|
data = ASN1_STRING_get0_data(value);
|
||||||
@@ -2841,9 +2871,9 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
|
|||||||
}
|
}
|
||||||
} else if (!strcmp(object_txt, SPC_RFC3161_OBJID)) {
|
} else if (!strcmp(object_txt, SPC_RFC3161_OBJID)) {
|
||||||
/* RFC3161 Timestamp - Policy OID: 1.3.6.1.4.1.311.3.3.1 */
|
/* RFC3161 Timestamp - Policy OID: 1.3.6.1.4.1.311.3.3.1 */
|
||||||
const u_char *data;
|
|
||||||
CMS_ContentInfo *cms;
|
CMS_ContentInfo *cms;
|
||||||
value = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
|
|
||||||
|
value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_SEQUENCE, NULL);
|
||||||
if (value == NULL)
|
if (value == NULL)
|
||||||
continue;
|
continue;
|
||||||
data = ASN1_STRING_get0_data(value);
|
data = ASN1_STRING_get0_data(value);
|
||||||
@@ -2869,27 +2899,35 @@ static time_t time_t_timestamp_get_attributes(CMS_ContentInfo **timestamp, PKCS7
|
|||||||
}
|
}
|
||||||
} else if (!strcmp(object_txt, SPC_UNAUTHENTICATED_DATA_BLOB_OBJID)) {
|
} else if (!strcmp(object_txt, SPC_UNAUTHENTICATED_DATA_BLOB_OBJID)) {
|
||||||
/* Unauthenticated Data Blob - Policy OID: 1.3.6.1.4.1.42921.1.2.1 */
|
/* Unauthenticated Data Blob - Policy OID: 1.3.6.1.4.1.42921.1.2.1 */
|
||||||
ASN1_STRING *blob = X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTF8STRING, NULL);
|
value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTF8STRING, NULL);
|
||||||
if (blob == NULL) {
|
if (value == NULL) {
|
||||||
printf("Warning: Unauthenticated Data Blob could not be decoded correctly\n");
|
printf("Warning: Unauthenticated Data Blob could not be decoded correctly\n");
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
len = ASN1_STRING_length(value);
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
char *data_blob = OPENSSL_buf2hexstr(blob->data, blob->length);
|
char *data_blob;
|
||||||
|
|
||||||
|
data = ASN1_STRING_get0_data(value);
|
||||||
|
data_blob = OPENSSL_buf2hexstr(data, len);
|
||||||
|
|
||||||
printf("\nUnauthenticated Data Blob:\n%s\n", data_blob);
|
printf("\nUnauthenticated Data Blob:\n%s\n", data_blob);
|
||||||
OPENSSL_free(data_blob);
|
OPENSSL_free(data_blob);
|
||||||
} else {
|
} else {
|
||||||
printf("\nUnauthenticated Data Blob length: %d bytes\n", blob->length);
|
printf("\nUnauthenticated Data Blob length: %d bytes\n", len);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Signature */
|
/* Signature */
|
||||||
if (verbose) {
|
if (verbose) {
|
||||||
|
data = ASN1_STRING_get0_data(si->enc_digest);
|
||||||
|
len = ASN1_STRING_length(si->enc_digest);
|
||||||
|
|
||||||
md_nid = OBJ_obj2nid(si->digest_enc_alg->algorithm);
|
md_nid = OBJ_obj2nid(si->digest_enc_alg->algorithm);
|
||||||
printf("\nDigest encryption algorithm: %s\n",
|
printf("\nDigest encryption algorithm: %s\n",
|
||||||
(md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2sn(md_nid));
|
(md_nid == NID_undef) ? "UNKNOWN" : OBJ_nid2sn(md_nid));
|
||||||
print_hash("Signature", "", ASN1_STRING_get0_data(si->enc_digest), ASN1_STRING_length(si->enc_digest));
|
print_hash("Signature", "", data, len);
|
||||||
}
|
}
|
||||||
|
|
||||||
return time;
|
return time;
|
||||||
@@ -2925,7 +2963,7 @@ static time_t time_t_get_asn1_time(const ASN1_TIME *s)
|
|||||||
*/
|
*/
|
||||||
static time_t time_t_get_si_time(PKCS7_SIGNER_INFO *si)
|
static time_t time_t_get_si_time(PKCS7_SIGNER_INFO *si)
|
||||||
{
|
{
|
||||||
ASN1_UTCTIME *time = asn1_time_get_si_time(si);
|
const ASN1_UTCTIME *time = asn1_time_get_si_time(si);
|
||||||
|
|
||||||
if (time == NULL)
|
if (time == NULL)
|
||||||
return INVALID_TIME; /* FAILED */
|
return INVALID_TIME; /* FAILED */
|
||||||
@@ -2937,7 +2975,7 @@ static time_t time_t_get_si_time(PKCS7_SIGNER_INFO *si)
|
|||||||
* [in] si: PKCS7_SIGNER_INFO structure
|
* [in] si: PKCS7_SIGNER_INFO structure
|
||||||
* [returns] NULL on error or ASN1_UTCTIME on success
|
* [returns] NULL on error or ASN1_UTCTIME on success
|
||||||
*/
|
*/
|
||||||
static ASN1_UTCTIME *asn1_time_get_si_time(PKCS7_SIGNER_INFO *si)
|
static const ASN1_UTCTIME *asn1_time_get_si_time(PKCS7_SIGNER_INFO *si)
|
||||||
{
|
{
|
||||||
STACK_OF(X509_ATTRIBUTE) *auth_attr = PKCS7_get_signed_attributes(si);
|
STACK_OF(X509_ATTRIBUTE) *auth_attr = PKCS7_get_signed_attributes(si);
|
||||||
if (auth_attr) {
|
if (auth_attr) {
|
||||||
@@ -2947,7 +2985,7 @@ static ASN1_UTCTIME *asn1_time_get_si_time(PKCS7_SIGNER_INFO *si)
|
|||||||
X509_ATTRIBUTE *attr = X509at_get_attr(auth_attr, i);
|
X509_ATTRIBUTE *attr = X509at_get_attr(auth_attr, i);
|
||||||
if (OBJ_obj2nid(X509_ATTRIBUTE_get0_object(attr)) == nid) {
|
if (OBJ_obj2nid(X509_ATTRIBUTE_get0_object(attr)) == nid) {
|
||||||
/* PKCS#9 signing time - Policy OID: 1.2.840.113549.1.9.5 */
|
/* PKCS#9 signing time - Policy OID: 1.2.840.113549.1.9.5 */
|
||||||
return X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTCTIME, NULL);
|
return (const ASN1_UTCTIME *)X509_ATTRIBUTE_get0_data(attr, 0, V_ASN1_UTCTIME, NULL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -2987,10 +3025,13 @@ static time_t time_t_get_cms_time(CMS_ContentInfo *cms)
|
|||||||
ASN1_OCTET_STRING **pos = CMS_get0_content(cms);
|
ASN1_OCTET_STRING **pos = CMS_get0_content(cms);
|
||||||
|
|
||||||
if (pos != NULL && *pos != NULL) {
|
if (pos != NULL && *pos != NULL) {
|
||||||
const u_char *p = (*pos)->data;
|
const u_char *p = ASN1_STRING_get0_data(*pos);
|
||||||
TS_TST_INFO *token = d2i_TS_TST_INFO(NULL, &p, (*pos)->length);
|
int len = ASN1_STRING_length(*pos);
|
||||||
|
TS_TST_INFO *token = d2i_TS_TST_INFO(NULL, &p, len);
|
||||||
|
|
||||||
if (token) {
|
if (token) {
|
||||||
const ASN1_GENERALIZEDTIME *asn1_time = TS_TST_INFO_get_time(token);
|
const ASN1_GENERALIZEDTIME *asn1_time = TS_TST_INFO_get_time(token);
|
||||||
|
|
||||||
posix_time = time_t_get_asn1_time(asn1_time);
|
posix_time = time_t_get_asn1_time(asn1_time);
|
||||||
TS_TST_INFO_free(token);
|
TS_TST_INFO_free(token);
|
||||||
}
|
}
|
||||||
@@ -3082,12 +3123,8 @@ static int verify_content_member_digest(FILE_FORMAT_CTX *ctx, ASN1_TYPE *content
|
|||||||
fprintf(stderr, "Failed to extract SpcIndirectDataContent data\n");
|
fprintf(stderr, "Failed to extract SpcIndirectDataContent data\n");
|
||||||
return 1; /* FAILED */
|
return 1; /* FAILED */
|
||||||
}
|
}
|
||||||
if (spc_extract_digest_safe(idc, mdbuf, &mdtype) < 0) {
|
if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) {
|
||||||
SpcIndirectDataContent_free(idc);
|
fprintf(stderr, "Failed to extract message digest from signature\n\n");
|
||||||
return 1; /* FAILED */
|
|
||||||
}
|
|
||||||
if (mdtype == -1) {
|
|
||||||
fprintf(stderr, "Failed to extract current message digest\n\n");
|
|
||||||
SpcIndirectDataContent_free(idc);
|
SpcIndirectDataContent_free(idc);
|
||||||
return 1; /* FAILED */
|
return 1; /* FAILED */
|
||||||
}
|
}
|
||||||
@@ -3241,91 +3278,67 @@ static int verify_signature(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* [in] ctx: structure holds input and output data
|
* Verifies signatures in a file.
|
||||||
|
* [in] ctx: file format context (input/output data)
|
||||||
|
* [in] options: verification options
|
||||||
|
* [in] verify_signature_flag:
|
||||||
|
* - non-zero: verify both digest and cryptographic signature
|
||||||
|
* - zero: verify only digest/content consistency (no signature validation)
|
||||||
* [returns] 1 on error or 0 on success
|
* [returns] 1 on error or 0 on success
|
||||||
*/
|
*/
|
||||||
static int verify_signed_file(FILE_FORMAT_CTX *ctx, GLOBAL_OPTIONS *options)
|
static int verify_signed_file(FILE_FORMAT_CTX *ctx, GLOBAL_OPTIONS *options,
|
||||||
|
int verify_signature_flag)
|
||||||
{
|
{
|
||||||
int i, ret = 1, verified = 0;
|
int i, ret = 1, verified = 0;
|
||||||
PKCS7 *p7;
|
|
||||||
STACK_OF(PKCS7) *signatures = NULL;
|
|
||||||
int detached = options->catalog ? 1 : 0;
|
int detached = options->catalog ? 1 : 0;
|
||||||
|
STACK_OF(PKCS7) *signatures = NULL;
|
||||||
|
|
||||||
if (detached) {
|
signatures = get_signature_list(ctx, options);
|
||||||
GLOBAL_OPTIONS *cat_options;
|
if (!signatures)
|
||||||
FILE_FORMAT_CTX *cat_ctx;
|
|
||||||
|
|
||||||
if (!ctx->format->is_detaching_supported || !ctx->format->is_detaching_supported()) {
|
|
||||||
fprintf(stderr, "This format does not support detached PKCS#7 signature\n");
|
|
||||||
return 1; /* FAILED */
|
|
||||||
}
|
|
||||||
printf("Checking the specified catalog file\n\n");
|
|
||||||
cat_options = OPENSSL_memdup(options, sizeof(GLOBAL_OPTIONS));
|
|
||||||
if (!cat_options) {
|
|
||||||
fprintf(stderr, "OPENSSL_memdup error.\n");
|
|
||||||
return 1; /* FAILED */
|
|
||||||
}
|
|
||||||
cat_options->infile = options->catalog;
|
|
||||||
cat_options->cmd = CMD_EXTRACT;
|
|
||||||
cat_ctx = file_format_cat.ctx_new(cat_options, NULL, NULL);
|
|
||||||
if (!cat_ctx) {
|
|
||||||
fprintf(stderr, "CAT file initialization error\n");
|
|
||||||
return 1; /* FAILED */
|
|
||||||
}
|
|
||||||
if (!cat_ctx->format->pkcs7_extract) {
|
|
||||||
fprintf(stderr, "Unsupported command: extract-signature\n");
|
|
||||||
return 1; /* FAILED */
|
|
||||||
}
|
|
||||||
p7 = cat_ctx->format->pkcs7_extract(cat_ctx);
|
|
||||||
cat_ctx->format->ctx_cleanup(cat_ctx);
|
|
||||||
OPENSSL_free(cat_options);
|
|
||||||
} else {
|
|
||||||
if (!ctx->format->pkcs7_extract) {
|
|
||||||
fprintf(stderr, "Unsupported command: extract-signature\n");
|
|
||||||
return 1; /* FAILED */
|
|
||||||
}
|
|
||||||
p7 = ctx->format->pkcs7_extract(ctx);
|
|
||||||
}
|
|
||||||
if (!p7) {
|
|
||||||
fprintf(stderr, "Unable to extract existing signature\n");
|
|
||||||
return 1; /* FAILED */
|
return 1; /* FAILED */
|
||||||
}
|
|
||||||
signatures = signature_list_create(p7);
|
if (!detached && !ctx->format->verify_digests) {
|
||||||
if (!signatures) {
|
fprintf(stderr, "Unsupported method: verify_digests\n");
|
||||||
fprintf(stderr, "Failed to create signature list\n\n");
|
|
||||||
sk_PKCS7_pop_free(signatures, PKCS7_free);
|
sk_PKCS7_pop_free(signatures, PKCS7_free);
|
||||||
return 1; /* FAILED */
|
return 1; /* FAILED */
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = 0; i < sk_PKCS7_num(signatures); i++) {
|
for (i = 0; i < sk_PKCS7_num(signatures); i++) {
|
||||||
PKCS7 *sig;
|
PKCS7 *sig;
|
||||||
|
int digest_ok = 0;
|
||||||
|
|
||||||
if (options->index >= 0 && options->index != i) {
|
if (options->index >= 0 && options->index != i) {
|
||||||
printf("Warning: signature verification at index %d was skipped\n", i);
|
printf("Warning: signature verification at index %d was skipped\n", i);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
sig = sk_PKCS7_value(signatures, i);
|
sig = sk_PKCS7_value(signatures, i);
|
||||||
|
printf("\nSignature Index: %d %s\n\n", i, i == 0 ? " (Primary Signature)" : "");
|
||||||
|
|
||||||
if (detached) {
|
if (detached) {
|
||||||
if (!verify_content(ctx, sig)) {
|
digest_ok = (verify_content(ctx, sig) == 0);
|
||||||
ret &= verify_signature(ctx, sig);
|
if (!digest_ok)
|
||||||
} else {
|
|
||||||
printf("Catalog verification: failed\n\n");
|
printf("Catalog verification: failed\n\n");
|
||||||
}
|
} else {
|
||||||
verified++;
|
digest_ok = (ctx->format->verify_digests(ctx, sig) != 0);
|
||||||
} else if (ctx->format->verify_digests) {
|
}
|
||||||
printf("\nSignature Index: %d %s\n\n", i, i==0 ? " (Primary Signature)" : "");
|
if (digest_ok) {
|
||||||
if (ctx->format->verify_digests(ctx, sig)) {
|
if (!verify_signature_flag) {
|
||||||
|
/* success: at least one digest matches */
|
||||||
|
ret = 0;
|
||||||
|
} else {
|
||||||
|
/* success if at least one valid signature is found */
|
||||||
ret &= verify_signature(ctx, sig);
|
ret &= verify_signature(ctx, sig);
|
||||||
}
|
}
|
||||||
verified++;
|
|
||||||
} else {
|
|
||||||
fprintf(stderr, "Unsupported method: verify_digests\n");
|
|
||||||
return 1; /* FAILED */
|
|
||||||
}
|
}
|
||||||
|
verified++;
|
||||||
}
|
}
|
||||||
printf("Number of verified signatures: %d\n", verified);
|
printf("Number of verified signatures: %d\n", verified);
|
||||||
sk_PKCS7_pop_free(signatures, PKCS7_free);
|
sk_PKCS7_pop_free(signatures, PKCS7_free);
|
||||||
|
|
||||||
if (ret)
|
if (ret)
|
||||||
ERR_print_errors_fp(stderr);
|
ERR_print_errors_fp(stderr);
|
||||||
|
|
||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -3367,11 +3380,11 @@ static STACK_OF(PKCS7) *signature_list_create(PKCS7 *p7)
|
|||||||
int j;
|
int j;
|
||||||
|
|
||||||
for (j=0; j<X509_ATTRIBUTE_count(attr); j++) {
|
for (j=0; j<X509_ATTRIBUTE_count(attr); j++) {
|
||||||
ASN1_STRING *value;
|
const ASN1_STRING *value;
|
||||||
const u_char *data;
|
const u_char *data;
|
||||||
PKCS7 *nested;
|
PKCS7 *nested;
|
||||||
|
|
||||||
value = X509_ATTRIBUTE_get0_data(attr, j, V_ASN1_SEQUENCE, NULL);
|
value = (const ASN1_STRING *)X509_ATTRIBUTE_get0_data(attr, j, V_ASN1_SEQUENCE, NULL);
|
||||||
if (value == NULL)
|
if (value == NULL)
|
||||||
continue;
|
continue;
|
||||||
data = ASN1_STRING_get0_data(value);
|
data = ASN1_STRING_get0_data(value);
|
||||||
@@ -3502,8 +3515,72 @@ static PKCS7 *pkcs7_get_sigfile(FILE_FORMAT_CTX *ctx)
|
|||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
* Extracts PKCS#7 object and creates a list of signatures
|
||||||
|
* [in] ctx: file format context
|
||||||
|
* [in] options: structure holds input parameters
|
||||||
|
* [returns] STACK_OF(PKCS7) on success or NULL on error
|
||||||
|
*/
|
||||||
|
static STACK_OF(PKCS7) *get_signature_list(FILE_FORMAT_CTX *ctx, GLOBAL_OPTIONS *options)
|
||||||
|
{
|
||||||
|
PKCS7 *p7;
|
||||||
|
STACK_OF(PKCS7) *signatures;
|
||||||
|
|
||||||
|
if (options->catalog) {
|
||||||
|
GLOBAL_OPTIONS *cat_options;
|
||||||
|
FILE_FORMAT_CTX *cat_ctx;
|
||||||
|
|
||||||
|
if (!ctx->format->is_detaching_supported || !ctx->format->is_detaching_supported()) {
|
||||||
|
fprintf(stderr, "This format does not support detached PKCS#7 signature\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
printf("Checking the specified catalog file\n\n");
|
||||||
|
cat_options = OPENSSL_memdup(options, sizeof(GLOBAL_OPTIONS));
|
||||||
|
if (!cat_options) {
|
||||||
|
fprintf(stderr, "OPENSSL_memdup error\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
cat_options->infile = options->catalog;
|
||||||
|
cat_options->cmd = CMD_EXTRACT;
|
||||||
|
cat_ctx = file_format_cat.ctx_new(cat_options, NULL, NULL);
|
||||||
|
if (!cat_ctx) {
|
||||||
|
fprintf(stderr, "CAT file initialization error\n");
|
||||||
|
OPENSSL_free(cat_options);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (!cat_ctx->format->pkcs7_extract) {
|
||||||
|
fprintf(stderr, "Unsupported command: extract-signature\n");
|
||||||
|
cat_ctx->format->ctx_cleanup(cat_ctx);
|
||||||
|
OPENSSL_free(cat_options);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
p7 = cat_ctx->format->pkcs7_extract(cat_ctx);
|
||||||
|
cat_ctx->format->ctx_cleanup(cat_ctx);
|
||||||
|
OPENSSL_free(cat_options);
|
||||||
|
} else {
|
||||||
|
if (!ctx->format->pkcs7_extract) {
|
||||||
|
fprintf(stderr, "Unsupported command: extract-signature\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
p7 = ctx->format->pkcs7_extract(ctx);
|
||||||
|
}
|
||||||
|
if (!p7) {
|
||||||
|
fprintf(stderr, "Unable to extract existing signature\n");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
signatures = signature_list_create(p7);
|
||||||
|
if (!signatures) {
|
||||||
|
fprintf(stderr, "Failed to create signature list\n\n");
|
||||||
|
PKCS7_free(p7);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return signatures;
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Verifies integrity of data after attaching a signature.
|
||||||
* [in] options: structure holds the input data
|
* [in] options: structure holds the input data
|
||||||
* [returns] 1 on error or 0 on success
|
* [returns] 0 on success, 1 on error or mismatch
|
||||||
*/
|
*/
|
||||||
static int check_attached_data(GLOBAL_OPTIONS *options)
|
static int check_attached_data(GLOBAL_OPTIONS *options)
|
||||||
{
|
{
|
||||||
@@ -3534,7 +3611,8 @@ static int check_attached_data(GLOBAL_OPTIONS *options)
|
|||||||
OPENSSL_free(tmp_options);
|
OPENSSL_free(tmp_options);
|
||||||
return 1; /* FAILED */
|
return 1; /* FAILED */
|
||||||
}
|
}
|
||||||
if (verify_signed_file(ctx, tmp_options)) {
|
/* Check that attached data matches PKCS#7 digest (no signature validation) */
|
||||||
|
if (verify_signed_file(ctx, tmp_options, 0)) {
|
||||||
fprintf(stderr, "Signature mismatch\n");
|
fprintf(stderr, "Signature mismatch\n");
|
||||||
ctx->format->ctx_cleanup(ctx);
|
ctx->format->ctx_cleanup(ctx);
|
||||||
OPENSSL_free(tmp_options);
|
OPENSSL_free(tmp_options);
|
||||||
@@ -3665,13 +3743,7 @@ static void usage(const char *argv0, const char *cmd)
|
|||||||
}
|
}
|
||||||
if (on_list(cmd, cmds_attach)) {
|
if (on_list(cmd, cmds_attach)) {
|
||||||
printf("%1sattach-signature [ -sigin ] <file>\n", "");
|
printf("%1sattach-signature [ -sigin ] <file>\n", "");
|
||||||
printf("%12s[ -CAfile <file> ]\n", "");
|
|
||||||
printf("%12s[ -CRLfile <file> ]\n", "");
|
|
||||||
printf("%12s[ -TSA-CAfile <file> ]\n", "");
|
|
||||||
printf("%12s[ -TSA-CRLfile <file> ]\n", "");
|
|
||||||
printf("%12s[ -time <unix-time> ]\n", "");
|
|
||||||
printf("%12s[ -h {md5,sha1,sha2(56),sha384,sha512} ]\n", "");
|
printf("%12s[ -h {md5,sha1,sha2(56),sha384,sha512} ]\n", "");
|
||||||
printf("%12s[ -require-leaf-hash {md5,sha1,sha2(56),sha384,sha512}:XXXXXXXXXXXX... ]\n", "");
|
|
||||||
printf("%12s[ -nest ]\n", "");
|
printf("%12s[ -nest ]\n", "");
|
||||||
printf("%12s[ -add-msi-dse ]\n", "");
|
printf("%12s[ -add-msi-dse ]\n", "");
|
||||||
printf("%12s[ -in ] <file> [ -out ] <file>\n\n", "");
|
printf("%12s[ -in ] <file> [ -out ] <file>\n\n", "");
|
||||||
@@ -3722,13 +3794,13 @@ static void help_for(const char *argv0, const char *cmd)
|
|||||||
#ifdef PROVIDE_ASKPASS
|
#ifdef PROVIDE_ASKPASS
|
||||||
const char *cmds_askpass[] = {"sign", NULL};
|
const char *cmds_askpass[] = {"sign", NULL};
|
||||||
#endif /* PROVIDE_ASKPASS */
|
#endif /* PROVIDE_ASKPASS */
|
||||||
const char *cmds_CAfile[] = {"attach-signature", "verify", NULL};
|
const char *cmds_CAfile[] = {"verify", NULL};
|
||||||
const char *cmds_catalog[] = {"verify", NULL};
|
const char *cmds_catalog[] = {"verify", NULL};
|
||||||
const char *cmds_certs[] = {"sign", NULL};
|
const char *cmds_certs[] = {"sign", NULL};
|
||||||
const char *cmds_comm[] = {"sign", NULL};
|
const char *cmds_comm[] = {"sign", NULL};
|
||||||
const char *cmds_CRLfile[] = {"attach-signature", "verify", NULL};
|
const char *cmds_CRLfile[] = {"verify", NULL};
|
||||||
const char *cmds_CRLfileHTTPS[] = {"add", "sign", "verify", NULL};
|
const char *cmds_CRLfileHTTPS[] = {"add", "sign", "verify", NULL};
|
||||||
const char *cmds_CRLfileTSA[] = {"attach-signature", "verify", NULL};
|
const char *cmds_CRLfileTSA[] = {"verify", NULL};
|
||||||
const char *cmds_h[] = {"add", "attach-signature", "sign", "extract-data", NULL};
|
const char *cmds_h[] = {"add", "attach-signature", "sign", "extract-data", NULL};
|
||||||
const char *cmds_i[] = {"sign", NULL};
|
const char *cmds_i[] = {"sign", NULL};
|
||||||
const char *cmds_in[] = {"add", "attach-signature", "extract-signature",
|
const char *cmds_in[] = {"add", "attach-signature", "extract-signature",
|
||||||
@@ -3762,16 +3834,16 @@ static void help_for(const char *argv0, const char *cmd)
|
|||||||
#endif /* OPENSSL_NO_ENGINE */
|
#endif /* OPENSSL_NO_ENGINE */
|
||||||
const char *cmds_pkcs12[] = {"sign", NULL};
|
const char *cmds_pkcs12[] = {"sign", NULL};
|
||||||
const char *cmds_readpass[] = {"sign", NULL};
|
const char *cmds_readpass[] = {"sign", NULL};
|
||||||
const char *cmds_require_leaf_hash[] = {"attach-signature", "verify", NULL};
|
const char *cmds_require_leaf_hash[] = {"verify", NULL};
|
||||||
const char *cmds_sigin[] = {"attach-signature", NULL};
|
const char *cmds_sigin[] = {"attach-signature", NULL};
|
||||||
const char *cmds_time[] = {"attach-signature", "sign", "verify", NULL};
|
const char *cmds_time[] = {"sign", "verify", NULL};
|
||||||
const char *cmds_ignore_timestamp[] = {"verify", NULL};
|
const char *cmds_ignore_timestamp[] = {"verify", NULL};
|
||||||
const char *cmds_ignore_cdp[] = {"verify", NULL};
|
const char *cmds_ignore_cdp[] = {"verify", NULL};
|
||||||
const char *cmds_ignore_crl[] = {"verify", NULL};
|
const char *cmds_ignore_crl[] = {"verify", NULL};
|
||||||
const char *cmds_t[] = {"add", "sign", NULL};
|
const char *cmds_t[] = {"add", "sign", NULL};
|
||||||
const char *cmds_ts[] = {"add", "sign", NULL};
|
const char *cmds_ts[] = {"add", "sign", NULL};
|
||||||
const char *cmds_CAfileHTTPS[] = {"add", "sign", "verify", NULL};
|
const char *cmds_CAfileHTTPS[] = {"add", "sign", "verify", NULL};
|
||||||
const char *cmds_CAfileTSA[] = {"attach-signature", "verify", NULL};
|
const char *cmds_CAfileTSA[] = {"verify", NULL};
|
||||||
const char *cmds_certsTSA[] = {"add", "sign", NULL};
|
const char *cmds_certsTSA[] = {"add", "sign", NULL};
|
||||||
const char *cmds_keyTSA[] = {"add", "sign", NULL};
|
const char *cmds_keyTSA[] = {"add", "sign", NULL};
|
||||||
const char *cmds_timeTSA[] = {"add", "sign", NULL};
|
const char *cmds_timeTSA[] = {"add", "sign", NULL};
|
||||||
@@ -3791,6 +3863,7 @@ static void help_for(const char *argv0, const char *cmd)
|
|||||||
printf("%-22s = add an unauthenticated blob or a timestamp to a previously-signed file\n", "add");
|
printf("%-22s = add an unauthenticated blob or a timestamp to a previously-signed file\n", "add");
|
||||||
printf("%-22s = sign file using a given signature\n", "attach-signature");
|
printf("%-22s = sign file using a given signature\n", "attach-signature");
|
||||||
printf("%-22s = extract signature from a previously-signed file\n", "extract-signature");
|
printf("%-22s = extract signature from a previously-signed file\n", "extract-signature");
|
||||||
|
printf("%-22s = extract a data content to be signed\n", "extract-data");
|
||||||
printf("%-22s = remove sections of the embedded signature on a file\n", "remove-signature");
|
printf("%-22s = remove sections of the embedded signature on a file\n", "remove-signature");
|
||||||
printf("%-22s = digitally sign a file\n", "sign");
|
printf("%-22s = digitally sign a file\n", "sign");
|
||||||
printf("%-22s = verifies the digital signature of a file\n\n", "verify");
|
printf("%-22s = verifies the digital signature of a file\n\n", "verify");
|
||||||
@@ -3802,8 +3875,6 @@ static void help_for(const char *argv0, const char *cmd)
|
|||||||
}
|
}
|
||||||
if (on_list(cmd, cmds_attach)) {
|
if (on_list(cmd, cmds_attach)) {
|
||||||
printf("\nUse the \"attach-signature\" command to attach the signature stored in the \"sigin\" file.\n");
|
printf("\nUse the \"attach-signature\" command to attach the signature stored in the \"sigin\" file.\n");
|
||||||
printf("In order to verify this signature you should specify how to find needed CA or TSA\n");
|
|
||||||
printf("certificates, if appropriate.\n\n");
|
|
||||||
printf("Options:\n");
|
printf("Options:\n");
|
||||||
}
|
}
|
||||||
if (on_list(cmd, cmds_extract)) {
|
if (on_list(cmd, cmds_extract)) {
|
||||||
@@ -4433,19 +4504,15 @@ static int ui_read(UI *ui, UI_STRING *uis)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static UI_METHOD *ui_osslsigncode(void) {
|
static UI_METHOD *ui_osslsigncode(void) {
|
||||||
static UI_METHOD *ui_method=NULL;
|
UI_METHOD *ui = UI_create_method("osslsigncode UI");
|
||||||
|
|
||||||
if (ui_method) /* already initialized */
|
if (ui) {
|
||||||
return ui_method;
|
UI_method_set_opener(ui, UI_method_get_opener(UI_OpenSSL()));
|
||||||
ui_method = UI_create_method("osslsigncode UI");
|
UI_method_set_writer(ui, UI_method_get_writer(UI_OpenSSL()));
|
||||||
if (!ui_method) {
|
UI_method_set_reader(ui, ui_read);
|
||||||
return NULL;
|
UI_method_set_closer(ui, UI_method_get_closer(UI_OpenSSL()));
|
||||||
}
|
}
|
||||||
UI_method_set_opener(ui_method, UI_method_get_opener(UI_OpenSSL()));
|
return ui;
|
||||||
UI_method_set_writer(ui_method, UI_method_get_writer(UI_OpenSSL()));
|
|
||||||
UI_method_set_reader(ui_method, ui_read);
|
|
||||||
UI_method_set_closer(ui_method, UI_method_get_closer(UI_OpenSSL()));
|
|
||||||
return ui_method;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* store_type == 0 means here multiple types of credentials are to be loaded */
|
/* store_type == 0 means here multiple types of credentials are to be loaded */
|
||||||
@@ -4456,7 +4523,7 @@ static void load_objects_from_store(const char *url, char *pass, EVP_PKEY **pkey
|
|||||||
if (!url)
|
if (!url)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
store_ctx = OSSL_STORE_open(url, ui_osslsigncode(), pass, NULL, NULL);
|
store_ctx = OSSL_STORE_open(url, ui_method, pass, NULL, NULL);
|
||||||
if (!store_ctx)
|
if (!store_ctx)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
@@ -4557,7 +4624,6 @@ static void providers_cleanup(void)
|
|||||||
{
|
{
|
||||||
sk_OSSL_PROVIDER_pop_free(providers, provider_free);
|
sk_OSSL_PROVIDER_pop_free(providers, provider_free);
|
||||||
providers = NULL;
|
providers = NULL;
|
||||||
UI_destroy_method(ui_osslsigncode());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static int provider_load(const char *pname)
|
static int provider_load(const char *pname)
|
||||||
@@ -4662,7 +4728,7 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options)
|
|||||||
if (cmd == CMD_HELP) {
|
if (cmd == CMD_HELP) {
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
if (cmd == CMD_SIGN || cmd == CMD_VERIFY || cmd == CMD_ATTACH) {
|
if (cmd == CMD_SIGN || cmd == CMD_VERIFY) {
|
||||||
options->cafile = get_cafile();
|
options->cafile = get_cafile();
|
||||||
options->https_cafile = get_cafile();
|
options->https_cafile = get_cafile();
|
||||||
options->tsa_cafile = get_cafile();
|
options->tsa_cafile = get_cafile();
|
||||||
@@ -4827,7 +4893,7 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options)
|
|||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
options->url = *(++argv);
|
options->url = *(++argv);
|
||||||
} else if ((cmd == CMD_ATTACH || cmd == CMD_SIGN || cmd == CMD_VERIFY)
|
} else if ((cmd == CMD_SIGN || cmd == CMD_VERIFY)
|
||||||
&& (!strcmp(*argv, "-time") || !strcmp(*argv, "-st"))) {
|
&& (!strcmp(*argv, "-time") || !strcmp(*argv, "-st"))) {
|
||||||
if (--argc < 1) {
|
if (--argc < 1) {
|
||||||
usage(argv0, "all");
|
usage(argv0, "all");
|
||||||
@@ -4896,14 +4962,14 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options)
|
|||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
options->catalog = *(++argv);
|
options->catalog = *(++argv);
|
||||||
} else if ((cmd == CMD_VERIFY || cmd == CMD_ATTACH) && !strcmp(*argv, "-CAfile")) {
|
} else if (cmd == CMD_VERIFY && !strcmp(*argv, "-CAfile")) {
|
||||||
if (--argc < 1) {
|
if (--argc < 1) {
|
||||||
usage(argv0, "all");
|
usage(argv0, "all");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
OPENSSL_free(options->cafile);
|
OPENSSL_free(options->cafile);
|
||||||
options->cafile = OPENSSL_strdup(*++argv);
|
options->cafile = OPENSSL_strdup(*++argv);
|
||||||
} else if ((cmd == CMD_VERIFY || cmd == CMD_ATTACH) && !strcmp(*argv, "-CRLfile")) {
|
} else if (cmd == CMD_VERIFY && !strcmp(*argv, "-CRLfile")) {
|
||||||
if (--argc < 1) {
|
if (--argc < 1) {
|
||||||
usage(argv0, "all");
|
usage(argv0, "all");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
@@ -4924,20 +4990,20 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options)
|
|||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
options->https_crlfile = OPENSSL_strdup(*++argv);
|
options->https_crlfile = OPENSSL_strdup(*++argv);
|
||||||
} else if ((cmd == CMD_VERIFY || cmd == CMD_ATTACH) && (!strcmp(*argv, "-untrusted") || !strcmp(*argv, "-TSA-CAfile"))) {
|
} else if (cmd == CMD_VERIFY && (!strcmp(*argv, "-untrusted") || !strcmp(*argv, "-TSA-CAfile"))) {
|
||||||
if (--argc < 1) {
|
if (--argc < 1) {
|
||||||
usage(argv0, "all");
|
usage(argv0, "all");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
OPENSSL_free(options->tsa_cafile);
|
OPENSSL_free(options->tsa_cafile);
|
||||||
options->tsa_cafile = OPENSSL_strdup(*++argv);
|
options->tsa_cafile = OPENSSL_strdup(*++argv);
|
||||||
} else if ((cmd == CMD_VERIFY || cmd == CMD_ATTACH) && (!strcmp(*argv, "-CRLuntrusted") || !strcmp(*argv, "-TSA-CRLfile"))) {
|
} else if (cmd == CMD_VERIFY && (!strcmp(*argv, "-CRLuntrusted") || !strcmp(*argv, "-TSA-CRLfile"))) {
|
||||||
if (--argc < 1) {
|
if (--argc < 1) {
|
||||||
usage(argv0, "all");
|
usage(argv0, "all");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
options->tsa_crlfile = OPENSSL_strdup(*++argv);
|
options->tsa_crlfile = OPENSSL_strdup(*++argv);
|
||||||
} else if ((cmd == CMD_VERIFY || cmd == CMD_ATTACH) && !strcmp(*argv, "-require-leaf-hash")) {
|
} else if (cmd == CMD_VERIFY && !strcmp(*argv, "-require-leaf-hash")) {
|
||||||
if (--argc < 1) {
|
if (--argc < 1) {
|
||||||
usage(argv0, "all");
|
usage(argv0, "all");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
@@ -5054,7 +5120,7 @@ static int main_configure(int argc, char **argv, GLOBAL_OPTIONS *options)
|
|||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
#ifndef WIN32
|
#ifndef WIN32
|
||||||
if ((cmd == CMD_VERIFY || cmd == CMD_ATTACH) && access(options->cafile, R_OK)) {
|
if (cmd == CMD_VERIFY && access(options->cafile, R_OK)) {
|
||||||
printf("Use the \"-CAfile\" option to add one or more trusted CA certificates to verify the signature.\n");
|
printf("Use the \"-CAfile\" option to add one or more trusted CA certificates to verify the signature.\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
@@ -5082,7 +5148,7 @@ static void engine_control_set(GLOBAL_OPTIONS *options, const char *arg)
|
|||||||
}
|
}
|
||||||
#endif /* OPENSSL_NO_ENGINE */
|
#endif /* OPENSSL_NO_ENGINE */
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
static int main_execute(int argc, char **argv)
|
||||||
{
|
{
|
||||||
FILE_FORMAT_CTX *ctx = NULL;
|
FILE_FORMAT_CTX *ctx = NULL;
|
||||||
GLOBAL_OPTIONS options;
|
GLOBAL_OPTIONS options;
|
||||||
@@ -5094,24 +5160,6 @@ int main(int argc, char **argv)
|
|||||||
/* reset options */
|
/* reset options */
|
||||||
memset(&options, 0, sizeof(GLOBAL_OPTIONS));
|
memset(&options, 0, sizeof(GLOBAL_OPTIONS));
|
||||||
|
|
||||||
/* Set up OpenSSL */
|
|
||||||
if (!OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS
|
|
||||||
| OPENSSL_INIT_ADD_ALL_CIPHERS
|
|
||||||
| OPENSSL_INIT_ADD_ALL_DIGESTS
|
|
||||||
| OPENSSL_INIT_LOAD_CONFIG, NULL))
|
|
||||||
DO_EXIT_0("Failed to init crypto\n");
|
|
||||||
|
|
||||||
/* create some MS Authenticode OIDS we need later on */
|
|
||||||
if (!OBJ_create(SPC_STATEMENT_TYPE_OBJID, NULL, NULL)
|
|
||||||
/* PKCS9_COUNTER_SIGNATURE exists as OpenSSL OBJ_pkcs9_countersignature */
|
|
||||||
|| !OBJ_create(MS_JAVA_SOMETHING, NULL, NULL)
|
|
||||||
|| !OBJ_create(SPC_SP_OPUS_INFO_OBJID, NULL, NULL)
|
|
||||||
|| !OBJ_create(SPC_NESTED_SIGNATURE_OBJID, NULL, NULL)
|
|
||||||
|| !OBJ_create(SPC_UNAUTHENTICATED_DATA_BLOB_OBJID, NULL, NULL)
|
|
||||||
|| !OBJ_create(SPC_RFC3161_OBJID, NULL, NULL)
|
|
||||||
|| !OBJ_create(PKCS9_SEQUENCE_NUMBER, NULL, NULL))
|
|
||||||
DO_EXIT_0("Failed to create objects\n");
|
|
||||||
|
|
||||||
/* commands and options initialization */
|
/* commands and options initialization */
|
||||||
if (!main_configure(argc, argv, &options))
|
if (!main_configure(argc, argv, &options))
|
||||||
goto err_cleanup;
|
goto err_cleanup;
|
||||||
@@ -5166,7 +5214,7 @@ int main(int argc, char **argv)
|
|||||||
DO_EXIT_0("Initialization error or unsupported input file type.\n");
|
DO_EXIT_0("Initialization error or unsupported input file type.\n");
|
||||||
}
|
}
|
||||||
if (options.cmd == CMD_VERIFY) {
|
if (options.cmd == CMD_VERIFY) {
|
||||||
ret = verify_signed_file(ctx, &options);
|
ret = verify_signed_file(ctx, &options, 1);
|
||||||
goto skip_signing;
|
goto skip_signing;
|
||||||
} else if (options.cmd == CMD_EXTRACT_DATA) {
|
} else if (options.cmd == CMD_EXTRACT_DATA) {
|
||||||
if (!ctx->format->pkcs7_contents_get) {
|
if (!ctx->format->pkcs7_contents_get) {
|
||||||
@@ -5313,13 +5361,15 @@ skip_signing:
|
|||||||
}
|
}
|
||||||
if (!ret && options.cmd == CMD_ATTACH) {
|
if (!ret && options.cmd == CMD_ATTACH) {
|
||||||
ret = check_attached_data(&options);
|
ret = check_attached_data(&options);
|
||||||
if (!ret)
|
if (!ret) {
|
||||||
printf("Signature successfully attached\n");
|
printf("Signature successfully attached\n");
|
||||||
/* else
|
} else {
|
||||||
* the new PKCS#7 signature has been successfully appended to the outfile
|
/*
|
||||||
* but only its verification failed (incorrect verification parameters?)
|
* The output file was created, but its content does not match
|
||||||
* so the output file is not deleted
|
* the attached PKCS#7 digest, so it is treated as invalid and removed.
|
||||||
*/
|
*/
|
||||||
|
remove_file(options.outfile);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
err_cleanup:
|
err_cleanup:
|
||||||
@@ -5359,6 +5409,37 @@ err_cleanup:
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int main(int argc, char **argv)
|
||||||
|
{
|
||||||
|
int ret = -1;
|
||||||
|
|
||||||
|
/* one-time OpenSSL initialization */
|
||||||
|
if (!OPENSSL_init_crypto(OPENSSL_INIT_LOAD_CRYPTO_STRINGS
|
||||||
|
| OPENSSL_INIT_ADD_ALL_CIPHERS
|
||||||
|
| OPENSSL_INIT_ADD_ALL_DIGESTS
|
||||||
|
| OPENSSL_INIT_LOAD_CONFIG, NULL))
|
||||||
|
DO_EXIT_0("Failed to init crypto\n");
|
||||||
|
|
||||||
|
/* create some MS Authenticode OIDS we need later on */
|
||||||
|
if (!OBJ_create(SPC_STATEMENT_TYPE_OBJID, NULL, NULL)
|
||||||
|
/* PKCS9_COUNTER_SIGNATURE exists as OpenSSL OBJ_pkcs9_countersignature */
|
||||||
|
|| !OBJ_create(MS_JAVA_SOMETHING, NULL, NULL)
|
||||||
|
|| !OBJ_create(SPC_SP_OPUS_INFO_OBJID, NULL, NULL)
|
||||||
|
|| !OBJ_create(SPC_NESTED_SIGNATURE_OBJID, NULL, NULL)
|
||||||
|
|| !OBJ_create(SPC_UNAUTHENTICATED_DATA_BLOB_OBJID, NULL, NULL)
|
||||||
|
|| !OBJ_create(SPC_RFC3161_OBJID, NULL, NULL)
|
||||||
|
|| !OBJ_create(PKCS9_SEQUENCE_NUMBER, NULL, NULL))
|
||||||
|
DO_EXIT_0("Failed to create objects\n");
|
||||||
|
|
||||||
|
/* perform the requested operation */
|
||||||
|
ui_method = ui_osslsigncode();
|
||||||
|
ret = main_execute(argc, argv);
|
||||||
|
UI_destroy_method(ui_method);
|
||||||
|
|
||||||
|
err_cleanup:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
Local Variables:
|
Local Variables:
|
||||||
c-basic-offset: 4
|
c-basic-offset: 4
|
||||||
|
|||||||
+554
@@ -0,0 +1,554 @@
|
|||||||
|
---
|
||||||
|
title: osslsigncode
|
||||||
|
lang: en-US
|
||||||
|
---
|
||||||
|
|
||||||
|
# NAME
|
||||||
|
|
||||||
|
osslsigncode - Authenticode signing, timestamping, extraction, attachment, removal, and verification tool
|
||||||
|
|
||||||
|
# SYNOPSIS
|
||||||
|
|
||||||
|
`osslsigncode` [`--help`] [`--version`]
|
||||||
|
|
||||||
|
`osslsigncode` `sign`
|
||||||
|
[`-certs` *file* | `-spc` *file* | `-pkcs12` *file*]
|
||||||
|
[`-key` *file-or-URI*]
|
||||||
|
[`-ac` *file*]
|
||||||
|
[`-pass` *password* | `-readpass` *file* | `-askpass`]
|
||||||
|
[`-pkcs11module` *module*] [`-pkcs11cert` *URI*]
|
||||||
|
[`-engine` *engine*] [`-provider` *provider*]
|
||||||
|
[`-login`] [`-engineCtrl` *command*[:*parameter*]]
|
||||||
|
[`-h` *digest*]
|
||||||
|
[`-n` *description*] [`-i` *URL*]
|
||||||
|
[`-jp` `low`] [`-comm`] [`-ph`]
|
||||||
|
[`-t` *URL* ... | `-ts` *URL* ...]
|
||||||
|
[`-TSA-certs` *file* `-TSA-key` *file-or-URI* [`-TSA-time` *unix-time*]]
|
||||||
|
[`-HTTPS-CAfile` *file*] [`-HTTPS-CRLfile` *file*]
|
||||||
|
[`-time` *unix-time*]
|
||||||
|
[`-addUnauthenticatedBlob` [`-blobFile` *file*]]
|
||||||
|
[`-nest`] [`-add-msi-dse`] [`-verbose`] [`-pem`]
|
||||||
|
`-in` *input* `-out` *output*
|
||||||
|
|
||||||
|
`osslsigncode` `extract-data`
|
||||||
|
[`-pem`] [`-h` *digest*] [`-ph`] [`-add-msi-dse`]
|
||||||
|
`-in` *input* `-out` *output*
|
||||||
|
|
||||||
|
`osslsigncode` `add`
|
||||||
|
[`-addUnauthenticatedBlob` [`-blobFile` *file*]]
|
||||||
|
[`-t` *URL* ... | `-ts` *URL* ...]
|
||||||
|
[`-TSA-certs` *file* `-TSA-key` *file-or-URI* [`-TSA-time` *unix-time*]]
|
||||||
|
[`-HTTPS-CAfile` *file*] [`-HTTPS-CRLfile` *file*]
|
||||||
|
[`-h` *digest*] [`-index` *n*] [`-verbose`] [`-add-msi-dse`]
|
||||||
|
`-in` *input* `-out` *output*
|
||||||
|
|
||||||
|
`osslsigncode` `attach-signature`
|
||||||
|
`-sigin` *signature*
|
||||||
|
[`-h` *digest*] [`-nest`] [`-add-msi-dse`]
|
||||||
|
`-in` *input* `-out` *output*
|
||||||
|
|
||||||
|
`osslsigncode` `extract-signature`
|
||||||
|
[`-pem`]
|
||||||
|
`-in` *input* `-out` *output*
|
||||||
|
|
||||||
|
`osslsigncode` `remove-signature`
|
||||||
|
`-in` *input* `-out` *output*
|
||||||
|
|
||||||
|
`osslsigncode` `verify`
|
||||||
|
`-in` *input*
|
||||||
|
[`-c` | `-catalog` *catalog-file*]
|
||||||
|
[`-CAfile` *file*] [`-CRLfile` *file*]
|
||||||
|
[`-HTTPS-CAfile` *file*] [`-HTTPS-CRLfile` *file*]
|
||||||
|
[`-TSA-CAfile` *file*] [`-TSA-CRLfile` *file*]
|
||||||
|
[`-p` *proxy*] [`-index` *n*]
|
||||||
|
[`-ignore-timestamp`] [`-ignore-cdp`] [`-ignore-crl`]
|
||||||
|
[`-time` *unix-time*]
|
||||||
|
[`-require-leaf-hash` *alg*:*hex*]
|
||||||
|
[`-verbose`]
|
||||||
|
|
||||||
|
# DESCRIPTION
|
||||||
|
|
||||||
|
`osslsigncode` signs and verifies Microsoft Authenticode signatures on
|
||||||
|
supported file formats. It can also extract data for detached signing,
|
||||||
|
attach an externally produced signature, add timestamps or unauthenticated
|
||||||
|
blobs to an existing signature, and remove an embedded signature.
|
||||||
|
|
||||||
|
Supported input formats include PE files such as EXE, DLL, and SYS, CAB,
|
||||||
|
CAT, MSI, APPX, and several script file types, including `.ps1`, `.ps1xml`,
|
||||||
|
`.psc1`, `.psd1`, `.psm1`, `.cdxml`, `.mof`, and `.js`.
|
||||||
|
|
||||||
|
The program supports these common workflows:
|
||||||
|
|
||||||
|
- direct signing of an unsigned file
|
||||||
|
- detached signing via `extract-data`, `sign`, and `attach-signature`
|
||||||
|
- post-sign timestamping with `add`
|
||||||
|
- verification of embedded signatures or catalog signatures with `verify`
|
||||||
|
|
||||||
|
If no subcommand is given, `sign` is assumed.
|
||||||
|
|
||||||
|
# FORMATS
|
||||||
|
|
||||||
|
Support is not identical across all file formats.
|
||||||
|
|
||||||
|
In particular, detached-signature workflows, nested signatures, catalog-based
|
||||||
|
verification, and signature removal are format-dependent features. A command
|
||||||
|
that is valid for one supported file type may be unsupported for another.
|
||||||
|
|
||||||
|
CAT files are a special case. They are detached catalog containers for
|
||||||
|
hashes of other files, not ordinary embedded-signature payloads. A CAT
|
||||||
|
file is itself a PKCS#7 structure containing authenticated entries for one
|
||||||
|
or more external files. In practice, the catalog signs file digests
|
||||||
|
recorded in the catalog, rather than embedding a signature into each
|
||||||
|
covered file.
|
||||||
|
|
||||||
|
Because of this, CAT files behave differently from embedded-signature
|
||||||
|
formats. They do not support `attach-signature`, `remove-signature`,
|
||||||
|
`extract-data`, or nested signatures.
|
||||||
|
|
||||||
|
MSI files are also a special case. They support an extended signature mode
|
||||||
|
controlled by `-add-msi-dse`. In this mode, the MSI signature covers file
|
||||||
|
metadata as well as file content. Detached-signing workflows and any later
|
||||||
|
re-signing or nesting operations must use a mode consistent with the MSI
|
||||||
|
file's existing signature structure.
|
||||||
|
|
||||||
|
# COMMANDS
|
||||||
|
|
||||||
|
## `sign`
|
||||||
|
|
||||||
|
Create a new Authenticode signature.
|
||||||
|
|
||||||
|
This command can sign a normal unsigned file, or it can sign PKCS#7 data
|
||||||
|
previously produced by `extract-data`.
|
||||||
|
|
||||||
|
## `extract-data`
|
||||||
|
|
||||||
|
Extract the PKCS#7 content to be signed later. This is used for detached
|
||||||
|
signing workflows.
|
||||||
|
|
||||||
|
## `add`
|
||||||
|
|
||||||
|
Add unauthenticated attributes to an existing signature, typically an
|
||||||
|
Authenticode timestamp, an RFC 3161 timestamp, or an unauthenticated blob.
|
||||||
|
|
||||||
|
With `-index`, the selected signature in a multi-signature file is updated.
|
||||||
|
|
||||||
|
## `attach-signature`
|
||||||
|
|
||||||
|
Attach a detached PKCS#7 signature to an input file.
|
||||||
|
|
||||||
|
With `-nest`, the new signature is attached as a nested signature instead of
|
||||||
|
replacing the primary one, if the file format supports nested signatures.
|
||||||
|
|
||||||
|
## `extract-signature`
|
||||||
|
|
||||||
|
Extract the embedded PKCS#7 signature from a signed file.
|
||||||
|
|
||||||
|
## `remove-signature`
|
||||||
|
|
||||||
|
Remove the embedded signature from a signed file.
|
||||||
|
|
||||||
|
## `verify`
|
||||||
|
|
||||||
|
Verify an embedded signature or a catalog signature.
|
||||||
|
|
||||||
|
Verification may include digest consistency, certificate chain validation,
|
||||||
|
certificate revocation checking, timestamp validation, and optional checking
|
||||||
|
of the signer's leaf certificate hash.
|
||||||
|
|
||||||
|
When verifying that a file is covered by a catalog, use `verify -catalog
|
||||||
|
catalog.cat -in file`. Verifying the CAT file by itself validates the
|
||||||
|
catalog signature; verifying with `-catalog` checks whether the specified
|
||||||
|
input file is covered by that catalog.
|
||||||
|
|
||||||
|
# OPTIONS
|
||||||
|
|
||||||
|
Some options are available only in particular builds or OpenSSL versions.
|
||||||
|
In particular, `-askpass` is build-dependent, `-provider` and `-nolegacy`
|
||||||
|
require OpenSSL 3, and engine-related options depend on engine support in the
|
||||||
|
build.
|
||||||
|
|
||||||
|
## General options
|
||||||
|
|
||||||
|
`--help`
|
||||||
|
: Show help text. With a subcommand, show help for that subcommand.
|
||||||
|
|
||||||
|
`-v`, `--version`
|
||||||
|
: Show version information.
|
||||||
|
|
||||||
|
`-in` *file*
|
||||||
|
: Input file.
|
||||||
|
|
||||||
|
`-out` *file*
|
||||||
|
: Output file. Required for all commands except `verify`.
|
||||||
|
|
||||||
|
`-verbose`
|
||||||
|
: Produce more detailed diagnostic output.
|
||||||
|
|
||||||
|
## Signing material
|
||||||
|
|
||||||
|
`-pkcs12` *file*
|
||||||
|
: Read the signing certificate and private key from a PKCS#12 container.
|
||||||
|
|
||||||
|
`-certs`, `-spc` *file*
|
||||||
|
: Read the signing certificate chain. The historical alias `-spc` is accepted.
|
||||||
|
|
||||||
|
`-key` *file-or-URI*
|
||||||
|
: Read the private key. This may also be a store or PKCS#11 URI.
|
||||||
|
|
||||||
|
`-ac` *file*
|
||||||
|
: Add extra certificates to the signature block.
|
||||||
|
|
||||||
|
`-pass` *password*
|
||||||
|
: Password or PIN for the key, token, or PKCS#12 container.
|
||||||
|
|
||||||
|
`-readpass` *file*
|
||||||
|
: Read the password or PIN from *file*. Use `-` to read from standard input.
|
||||||
|
|
||||||
|
`-askpass`
|
||||||
|
: Prompt for the password interactively.
|
||||||
|
|
||||||
|
## PKCS#11, engines, and providers
|
||||||
|
|
||||||
|
`-pkcs11module` *module*
|
||||||
|
: Path to a PKCS#11 module.
|
||||||
|
|
||||||
|
`-pkcs11cert` *URI*
|
||||||
|
: PKCS#11 URI identifying the certificate object.
|
||||||
|
|
||||||
|
`-provider` *provider*
|
||||||
|
: OpenSSL 3 provider to load. This is the preferred modern interface for
|
||||||
|
provider-based PKCS#11 use.
|
||||||
|
|
||||||
|
`-engine`, `-pkcs11engine` *engine*
|
||||||
|
: OpenSSL engine identifier or path to a dynamic engine module. This
|
||||||
|
interface is retained for compatibility with builds and deployments that
|
||||||
|
still support engines.
|
||||||
|
|
||||||
|
`-login`
|
||||||
|
: Force login to the token for engine-based PKCS#11 use.
|
||||||
|
|
||||||
|
`-engineCtrl` *command*[:*parameter*]
|
||||||
|
: Pass a control command to the selected engine.
|
||||||
|
|
||||||
|
`-nolegacy`
|
||||||
|
: On OpenSSL 3 builds, do not automatically load the legacy provider.
|
||||||
|
|
||||||
|
## Signature contents and digest control
|
||||||
|
|
||||||
|
`-h` `md5` | `sha1` | `sha2` | `sha256` | `sha384` | `sha512`
|
||||||
|
: Select the digest algorithm. The default is `sha256`. `sha2` and
|
||||||
|
`sha256` are equivalent.
|
||||||
|
|
||||||
|
`-n` *description*
|
||||||
|
: Description of the signed content.
|
||||||
|
|
||||||
|
`-i` *URL*
|
||||||
|
: Informational URL associated with the signed content.
|
||||||
|
|
||||||
|
`-comm`
|
||||||
|
: Use Microsoft Commercial Code Signing purpose instead of the default
|
||||||
|
individual purpose.
|
||||||
|
|
||||||
|
`-jp` `low`
|
||||||
|
: Add the Java CAB permission attribute. Only `low` is currently supported.
|
||||||
|
|
||||||
|
`-ph`
|
||||||
|
: Generate page hashes for executable files.
|
||||||
|
|
||||||
|
`-add-msi-dse`
|
||||||
|
: For MSI files, enable the `MsiDigitalSignatureEx` signing mode. In this
|
||||||
|
mode, the signature covers MSI metadata as well as file content. The
|
||||||
|
metadata portion includes stream names, sizes, and selected timestamps in
|
||||||
|
the MSI structure. This option changes the MSI signature format and should
|
||||||
|
be used consistently in any detached-signing workflow involving
|
||||||
|
`extract-data`, `sign`, `attach-signature`, or `add`.
|
||||||
|
|
||||||
|
For a newly signed MSI, this mode is generally preferred because it extends
|
||||||
|
signing coverage beyond file content alone. For an already signed MSI,
|
||||||
|
however, the chosen mode must match the file's existing signature
|
||||||
|
structure. Switching between basic MSI signing and `MsiDigitalSignatureEx`
|
||||||
|
during re-signing or nested-signature operations can invalidate the
|
||||||
|
existing signature.
|
||||||
|
|
||||||
|
`-pem`
|
||||||
|
: Write PKCS#7 output in PEM format instead of DER.
|
||||||
|
|
||||||
|
## Timestamping and network options
|
||||||
|
|
||||||
|
The following timestamping modes are **mutually exclusive** within a single
|
||||||
|
`sign` or `add` invocation:
|
||||||
|
|
||||||
|
- Authenticode timestamping with `-t`
|
||||||
|
- RFC 3161 timestamping with `-ts`
|
||||||
|
- built-in RFC 3161 timestamp generation with `-TSA-certs` and `-TSA-key`
|
||||||
|
|
||||||
|
`-t` *URL*
|
||||||
|
: Add an Authenticode timestamp from the specified URL. May be repeated.
|
||||||
|
|
||||||
|
`-ts` *URL*
|
||||||
|
: Add an RFC 3161 timestamp from the specified URL. May be repeated.
|
||||||
|
|
||||||
|
`-p` *proxy*
|
||||||
|
: Proxy used for timestamp or CRL retrieval.
|
||||||
|
|
||||||
|
`-noverifypeer`
|
||||||
|
: Do not verify the TLS certificate of the remote timestamp service.
|
||||||
|
|
||||||
|
`-HTTPS-CAfile` *file*
|
||||||
|
: PEM bundle used to verify HTTPS peers contacted by `osslsigncode`.
|
||||||
|
|
||||||
|
`-HTTPS-CRLfile` *file*
|
||||||
|
: PEM CRL file used while verifying HTTPS peers.
|
||||||
|
|
||||||
|
`-TSA-certs` *file*
|
||||||
|
: PEM certificate chain for locally generated RFC 3161 timestamps.
|
||||||
|
|
||||||
|
`-TSA-key` *file-or-URI*
|
||||||
|
: Private key for locally generated RFC 3161 timestamps.
|
||||||
|
|
||||||
|
`-TSA-time` *unix-time*
|
||||||
|
: Timestamp time for locally generated RFC 3161 responses.
|
||||||
|
|
||||||
|
## Nested signatures and indexed operations
|
||||||
|
|
||||||
|
`-nest`
|
||||||
|
: Add a nested signature instead of replacing the primary signature.
|
||||||
|
|
||||||
|
`-index` *n*
|
||||||
|
: Select a signature by index for `add` or `verify`. Index 0 is the primary
|
||||||
|
signature.
|
||||||
|
|
||||||
|
## Unauthenticated blob options
|
||||||
|
|
||||||
|
`-addUnauthenticatedBlob`
|
||||||
|
: Add an unauthenticated blob to the signature.
|
||||||
|
|
||||||
|
`-blobFile` *file*
|
||||||
|
: Read blob contents from *file*. If omitted, a placeholder blob is created.
|
||||||
|
|
||||||
|
## Verification options
|
||||||
|
|
||||||
|
`-c`, `-catalog` *file*
|
||||||
|
: Verify the input file against the specified catalog file.
|
||||||
|
|
||||||
|
`-CAfile` *file*
|
||||||
|
: PEM bundle of trusted CA certificates for signer validation.
|
||||||
|
|
||||||
|
`-CRLfile` *file*
|
||||||
|
: PEM file containing CRLs for signer validation.
|
||||||
|
|
||||||
|
`-TSA-CAfile`, `-untrusted` *file*
|
||||||
|
: PEM bundle of trusted CA certificates for timestamp validation.
|
||||||
|
|
||||||
|
`-TSA-CRLfile`, `-CRLuntrusted` *file*
|
||||||
|
: PEM file containing CRLs for timestamp validation.
|
||||||
|
|
||||||
|
`-time`, `-st` *unix-time*
|
||||||
|
: Verification time. If a valid timestamp is present and used, chain
|
||||||
|
validation is normally performed at the timestamp time.
|
||||||
|
|
||||||
|
`-ignore-timestamp`
|
||||||
|
: Skip verification of the timestamp signature.
|
||||||
|
|
||||||
|
`-ignore-cdp`
|
||||||
|
: Do not fetch CRLs from CRL Distribution Points.
|
||||||
|
|
||||||
|
`-ignore-crl`
|
||||||
|
: Disable CRL retrieval and CRL validation.
|
||||||
|
|
||||||
|
`-require-leaf-hash` *alg*:*hex*
|
||||||
|
: Require the signer's leaf certificate to hash to the specified value.
|
||||||
|
The hash is computed over the DER encoding of the leaf certificate.
|
||||||
|
|
||||||
|
# EXIT STATUS
|
||||||
|
|
||||||
|
`0`
|
||||||
|
: Success.
|
||||||
|
|
||||||
|
non-zero
|
||||||
|
: Failure.
|
||||||
|
|
||||||
|
# DIAGNOSTICS
|
||||||
|
|
||||||
|
Common causes of failure include:
|
||||||
|
|
||||||
|
missing CA trust bundle
|
||||||
|
: On Unix-like systems, `verify` expects a readable CA bundle, either from
|
||||||
|
`-CAfile` or from a detected system default.
|
||||||
|
|
||||||
|
detached-signing mismatch
|
||||||
|
: `extract-data`, `sign`, and `attach-signature` must use compatible
|
||||||
|
digest-affecting options such as `-h`, and where relevant `-ph` and
|
||||||
|
`-add-msi-dse`.
|
||||||
|
|
||||||
|
unsupported format feature
|
||||||
|
: Some file formats do not support every subcommand or every signature mode.
|
||||||
|
|
||||||
|
missing TSA trust chain
|
||||||
|
: Timestamp verification may fail unless the appropriate TSA trust anchors
|
||||||
|
are supplied with `-TSA-CAfile`, and where needed `-TSA-CRLfile`.
|
||||||
|
|
||||||
|
conflicting timestamp modes
|
||||||
|
: `-t`, `-ts`, and built-in TSA signing cannot be combined in one command.
|
||||||
|
|
||||||
|
MSI signature mode mismatch
|
||||||
|
: Re-signing or nesting an MSI signature must be consistent with whether the
|
||||||
|
file already uses `MsiDigitalSignatureEx`. Mixing modes may invalidate the
|
||||||
|
existing signature.
|
||||||
|
|
||||||
|
# ENVIRONMENT
|
||||||
|
|
||||||
|
`HTTP_PROXY`, `http_proxy`
|
||||||
|
: Default proxy for HTTP access if `-p` is not given.
|
||||||
|
|
||||||
|
`HTTPS_PROXY`, `https_proxy`
|
||||||
|
: Default proxy for HTTPS access if `-p` is not given.
|
||||||
|
|
||||||
|
`OPENSSL_ENGINES`
|
||||||
|
: May help OpenSSL find engine modules.
|
||||||
|
|
||||||
|
# FILES
|
||||||
|
|
||||||
|
On Unix-like systems, `osslsigncode` tries common CA bundle locations for
|
||||||
|
its default `-CAfile`, including:
|
||||||
|
|
||||||
|
- `/etc/ssl/certs/ca-certificates.crt`
|
||||||
|
- `/etc/pki/tls/certs/ca-bundle.crt`
|
||||||
|
- `/usr/share/ssl/certs/ca-bundle.crt`
|
||||||
|
- `/usr/local/share/certs/ca-root-nss.crt`
|
||||||
|
- `/etc/ssl/cert.pem`
|
||||||
|
|
||||||
|
If no readable CA bundle is available, `verify` may require an explicit
|
||||||
|
`-CAfile`.
|
||||||
|
|
||||||
|
# NOTES
|
||||||
|
|
||||||
|
Use `extract-data` when you need to create a new detached signature object.
|
||||||
|
Use `extract-signature` when you need to copy an existing embedded PKCS#7
|
||||||
|
signature out of a file.
|
||||||
|
|
||||||
|
For safer secret handling, prefer `-readpass` or `-askpass` over `-pass`.
|
||||||
|
|
||||||
|
Data added with `-addUnauthenticatedBlob` is not protected by the signature
|
||||||
|
and must not be treated as trusted.
|
||||||
|
|
||||||
|
For new MSI signatures, `-add-msi-dse` is generally preferred because it
|
||||||
|
extends signing coverage to MSI metadata as well as file content. However,
|
||||||
|
it is format-affecting rather than cosmetic, so existing signed MSI files
|
||||||
|
should be re-signed only in a mode consistent with their current signature
|
||||||
|
structure.
|
||||||
|
|
||||||
|
Output files are not overwritten.
|
||||||
|
|
||||||
|
# EXAMPLES
|
||||||
|
|
||||||
|
## Sign and verify a file
|
||||||
|
|
||||||
|
```sh
|
||||||
|
osslsigncode sign \
|
||||||
|
-pkcs12 signer.p12 \
|
||||||
|
-readpass p12-pass.txt \
|
||||||
|
-n "Example Application" \
|
||||||
|
-i "https://example.com/" \
|
||||||
|
-ts "https://tsa.example.net/" \
|
||||||
|
-in app.exe \
|
||||||
|
-out app-signed.exe
|
||||||
|
|
||||||
|
osslsigncode verify \
|
||||||
|
-CAfile ca-bundle.pem \
|
||||||
|
-TSA-CAfile tsa-ca-bundle.pem \
|
||||||
|
-in app-signed.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
## Detached signing workflow
|
||||||
|
|
||||||
|
```sh
|
||||||
|
osslsigncode extract-data \
|
||||||
|
-h sha384 \
|
||||||
|
-ph \
|
||||||
|
-in app.exe \
|
||||||
|
-out app-data.der
|
||||||
|
|
||||||
|
osslsigncode sign \
|
||||||
|
-pkcs12 signer.p12 \
|
||||||
|
-readpass p12-pass.txt \
|
||||||
|
-h sha384 \
|
||||||
|
-in app-data.der \
|
||||||
|
-out app-sig.der
|
||||||
|
|
||||||
|
osslsigncode attach-signature \
|
||||||
|
-h sha384 \
|
||||||
|
-sigin app-sig.der \
|
||||||
|
-in app.exe \
|
||||||
|
-out app-signed.exe
|
||||||
|
|
||||||
|
osslsigncode verify \
|
||||||
|
-CAfile ca-bundle.pem \
|
||||||
|
-in app-signed.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
## Sign a new MSI with extended MSI metadata coverage
|
||||||
|
|
||||||
|
```sh
|
||||||
|
osslsigncode sign \
|
||||||
|
-pkcs12 signer.p12 \
|
||||||
|
-readpass p12-pass.txt \
|
||||||
|
-add-msi-dse \
|
||||||
|
-in installer.msi \
|
||||||
|
-out installer-signed.msi
|
||||||
|
```
|
||||||
|
|
||||||
|
## Use a PKCS#11 provider
|
||||||
|
|
||||||
|
```sh
|
||||||
|
osslsigncode sign \
|
||||||
|
-provider /path/to/pkcs11prov.so \
|
||||||
|
-pkcs11module /path/to/opensc-pkcs11.so \
|
||||||
|
-pkcs11cert 'pkcs11:token=my-token;object=cert' \
|
||||||
|
-key 'pkcs11:token=my-token;object=key' \
|
||||||
|
-readpass pin.txt \
|
||||||
|
-in app.exe \
|
||||||
|
-out app-signed.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
## Add a timestamp to an already signed file
|
||||||
|
|
||||||
|
```sh
|
||||||
|
osslsigncode add \
|
||||||
|
-ts "https://tsa.example.net/" \
|
||||||
|
-in app-signed.exe \
|
||||||
|
-out app-signed-ts.exe
|
||||||
|
```
|
||||||
|
|
||||||
|
## Verify that a file is covered by a catalog
|
||||||
|
|
||||||
|
```sh
|
||||||
|
osslsigncode verify \
|
||||||
|
-catalog drivers.cat \
|
||||||
|
-CAfile ca-bundle.pem \
|
||||||
|
-CRLfile ca-crl.pem \
|
||||||
|
-in driver.sys
|
||||||
|
```
|
||||||
|
|
||||||
|
# REPORTING BUGS
|
||||||
|
|
||||||
|
Report bugs and suspected issues via the project issue tracker:
|
||||||
|
|
||||||
|
<https://github.com/mtrojnar/osslsigncode/issues>
|
||||||
|
|
||||||
|
# AUTHORS
|
||||||
|
|
||||||
|
Originally written by Per Allansson.
|
||||||
|
|
||||||
|
Maintained and extended by Michał Trojnara.
|
||||||
|
|
||||||
|
Major contributions by Małgorzata Olszówka.
|
||||||
|
|
||||||
|
Additional contributions by other project contributors.
|
||||||
|
|
||||||
|
# SEE ALSO
|
||||||
|
|
||||||
|
**OpenSSL** Library
|
||||||
|
|
||||||
|
<https://openssl-library.org/>
|
||||||
|
|
||||||
@@ -87,6 +87,7 @@ static uint32_t pe_calc_checksum(BIO *bio, uint32_t header_size);
|
|||||||
static uint32_t pe_calc_realchecksum(FILE_FORMAT_CTX *ctx);
|
static uint32_t pe_calc_realchecksum(FILE_FORMAT_CTX *ctx);
|
||||||
static int pe_modify_header(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata);
|
static int pe_modify_header(FILE_FORMAT_CTX *ctx, BIO *hash, BIO *outdata);
|
||||||
static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md);
|
static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md);
|
||||||
|
static int pkcs7_get_page_hash(PKCS7 *p7, u_char **ph, int *phlen, int *phtype);
|
||||||
static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype, SpcAttributeTypeAndOptionalValue *obj);
|
static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype, SpcAttributeTypeAndOptionalValue *obj);
|
||||||
static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype);
|
static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype);
|
||||||
static int pe_verify_page_hash(FILE_FORMAT_CTX *ctx, u_char *ph, int phlen, int phtype);
|
static int pe_verify_page_hash(FILE_FORMAT_CTX *ctx, u_char *ph, int phlen, int phtype);
|
||||||
@@ -163,8 +164,10 @@ static ASN1_OBJECT *pe_spc_image_data_get(u_char **p, int *plen, FILE_FORMAT_CTX
|
|||||||
if (EVP_MD_size(ctx->options->md) > EVP_MD_size(EVP_sha1()))
|
if (EVP_MD_size(ctx->options->md) > EVP_MD_size(EVP_sha1()))
|
||||||
phtype = NID_sha256;
|
phtype = NID_sha256;
|
||||||
link = pe_page_hash_link_get(ctx, phtype);
|
link = pe_page_hash_link_get(ctx, phtype);
|
||||||
if (!link)
|
if (!link) {
|
||||||
|
SpcPeImageData_free(pid);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
pid->file = link;
|
pid->file = link;
|
||||||
} else {
|
} else {
|
||||||
pid->file = spc_link_obsolete_get();
|
pid->file = spc_link_obsolete_get();
|
||||||
@@ -245,49 +248,34 @@ static int pe_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
|
|||||||
u_char *cmdbuf = NULL;
|
u_char *cmdbuf = NULL;
|
||||||
u_char *ph = NULL;
|
u_char *ph = NULL;
|
||||||
|
|
||||||
if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) {
|
if (!pkcs7_get_content_digest(p7, mdbuf, &mdtype)) {
|
||||||
ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence;
|
|
||||||
const u_char *p = content_val->data;
|
|
||||||
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
|
|
||||||
if (idc) {
|
|
||||||
if (!pe_page_hash_get(&ph, &phlen, &phtype, idc->data)) {
|
|
||||||
fprintf(stderr, "Failed to extract a page hash\n\n");
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
return 0; /* FAILED */
|
|
||||||
}
|
|
||||||
if (spc_extract_digest_safe(idc, mdbuf, &mdtype) < 0) {
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
return 0; /* FAILED */
|
|
||||||
}
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mdtype == -1) {
|
|
||||||
fprintf(stderr, "Failed to extract current message digest\n\n");
|
fprintf(stderr, "Failed to extract current message digest\n\n");
|
||||||
OPENSSL_free(ph);
|
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
md = EVP_get_digestbynid(mdtype);
|
md = EVP_get_digestbynid(mdtype);
|
||||||
cmdbuf = pe_digest_calc(ctx, md);
|
cmdbuf = pe_digest_calc(ctx, md);
|
||||||
if (!cmdbuf) {
|
if (!cmdbuf) {
|
||||||
fprintf(stderr, "Failed to calculate message digest\n\n");
|
fprintf(stderr, "Failed to calculate message digest\n\n");
|
||||||
OPENSSL_free(ph);
|
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
if (!compare_digests(mdbuf, cmdbuf, mdtype)) {
|
if (!compare_digests(mdbuf, cmdbuf, mdtype)) {
|
||||||
fprintf(stderr, "Signature verification: failed\n\n");
|
fprintf(stderr, "Signature verification: failed\n\n");
|
||||||
OPENSSL_free(ph);
|
|
||||||
OPENSSL_free(cmdbuf);
|
OPENSSL_free(cmdbuf);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
|
OPENSSL_free(cmdbuf);
|
||||||
|
|
||||||
|
if (!pkcs7_get_page_hash(p7, &ph, &phlen, &phtype)) {
|
||||||
|
fprintf(stderr, "Failed to extract page hash\n\n");
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
if (!pe_verify_page_hash(ctx, ph, phlen, phtype)) {
|
if (!pe_verify_page_hash(ctx, ph, phlen, phtype)) {
|
||||||
fprintf(stderr, "Signature verification: failed\n\n");
|
fprintf(stderr, "Signature verification: failed\n\n");
|
||||||
OPENSSL_free(ph);
|
OPENSSL_free(ph);
|
||||||
OPENSSL_free(cmdbuf);
|
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
OPENSSL_free(ph);
|
OPENSSL_free(ph);
|
||||||
OPENSSL_free(cmdbuf);
|
|
||||||
return 1; /* OK */
|
return 1; /* OK */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -402,6 +390,7 @@ static PKCS7 *pe_pkcs7_signature_new(FILE_FORMAT_CTX *ctx, BIO *hash)
|
|||||||
content = spc_indirect_data_content_get(hash, ctx);
|
content = spc_indirect_data_content_get(hash, ctx);
|
||||||
if (!content) {
|
if (!content) {
|
||||||
fprintf(stderr, "Failed to get spcIndirectDataContent\n");
|
fprintf(stderr, "Failed to get spcIndirectDataContent\n");
|
||||||
|
PKCS7_free(p7);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
if (!sign_spc_indirect_data_content(p7, content)) {
|
if (!sign_spc_indirect_data_content(p7, content)) {
|
||||||
@@ -834,6 +823,36 @@ static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md)
|
|||||||
* Page hash support
|
* Page hash support
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Retrieve a page hash from PKCS7 SPC_INDIRECT_DATA structure.
|
||||||
|
* [in] p7: PKCS7 signature
|
||||||
|
* [out] ph: page hash
|
||||||
|
* [out] phlen: page hash length
|
||||||
|
* [out] phtype: NID_sha1 or NID_sha256
|
||||||
|
* [returns] 0 on error or 1 on success
|
||||||
|
*/
|
||||||
|
static int pkcs7_get_page_hash(PKCS7 *p7, u_char **ph, int *phlen, int *phtype)
|
||||||
|
{
|
||||||
|
SpcIndirectDataContent *idc = pkcs7_get_indirect_data_content(p7);
|
||||||
|
|
||||||
|
if (!idc) {
|
||||||
|
fprintf(stderr, "Failed to decode SpcIndirectDataContent\n\n");
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
if (!idc->data) {
|
||||||
|
fprintf(stderr, "Missing SpcIndirectDataContent data\n\n");
|
||||||
|
SpcIndirectDataContent_free(idc);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
if (!pe_page_hash_get(ph, phlen, phtype, idc->data)) {
|
||||||
|
fprintf(stderr, "Failed to extract a page hash\n\n");
|
||||||
|
SpcIndirectDataContent_free(idc);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
SpcIndirectDataContent_free(idc);
|
||||||
|
return 1; /* OK */
|
||||||
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Retrieve a page hash from SPC_INDIRECT_DATA structure.
|
* Retrieve a page hash from SPC_INDIRECT_DATA structure.
|
||||||
* [out] ph: page hash
|
* [out] ph: page hash
|
||||||
@@ -842,43 +861,71 @@ static BIO *pe_digest_calc_bio(FILE_FORMAT_CTX *ctx, const EVP_MD *md)
|
|||||||
* [in] obj: SPC_INDIRECT_DATA OID: 1.3.6.1.4.1.311.2.1.4 containing page hash
|
* [in] obj: SPC_INDIRECT_DATA OID: 1.3.6.1.4.1.311.2.1.4 containing page hash
|
||||||
* [returns] 0 on error or 1 on success
|
* [returns] 0 on error or 1 on success
|
||||||
*/
|
*/
|
||||||
static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype, SpcAttributeTypeAndOptionalValue *obj)
|
static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype,
|
||||||
|
SpcAttributeTypeAndOptionalValue *obj)
|
||||||
{
|
{
|
||||||
const u_char *blob;
|
const unsigned char *blob;
|
||||||
|
const unsigned char *sequence_data;
|
||||||
|
const unsigned char *classid_data;
|
||||||
|
const unsigned char *serialized_data;
|
||||||
SpcPeImageData *id;
|
SpcPeImageData *id;
|
||||||
SpcSerializedObject *so;
|
SpcSerializedObject *so;
|
||||||
int l, l2;
|
int sequence_len, classid_len, serialized_len, l, l2;
|
||||||
char buf[128];
|
char buf[128];
|
||||||
|
|
||||||
|
/* Validate input object */
|
||||||
if (!obj || !obj->value)
|
if (!obj || !obj->value)
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
blob = obj->value->value.sequence->data;
|
|
||||||
id = d2i_SpcPeImageData(NULL, &blob, obj->value->value.sequence->length);
|
/* Decode SpcPeImageData from ASN.1 sequence */
|
||||||
if (!id) {
|
sequence_data = ASN1_STRING_get0_data(obj->value->value.sequence);
|
||||||
|
sequence_len = ASN1_STRING_length(obj->value->value.sequence);
|
||||||
|
|
||||||
|
/* d2i_* modifies the input pointer, so use a temporary variable */
|
||||||
|
blob = sequence_data;
|
||||||
|
id = d2i_SpcPeImageData(NULL, &blob, sequence_len);
|
||||||
|
if (!id)
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
|
||||||
|
/* Validate SpcPeImageData contents */
|
||||||
if (!id->file) {
|
if (!id->file) {
|
||||||
SpcPeImageData_free(id);
|
SpcPeImageData_free(id);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Type 1 means SpcSerializedObject */
|
||||||
if (id->file->type != 1) {
|
if (id->file->type != 1) {
|
||||||
SpcPeImageData_free(id);
|
SpcPeImageData_free(id);
|
||||||
return 1; /* OK - This is not SpcSerializedObject structure that contains page hashes */
|
return 1; /* OK - no page hashes present */
|
||||||
}
|
}
|
||||||
|
|
||||||
so = id->file->value.moniker;
|
so = id->file->value.moniker;
|
||||||
if (so->classId->length != sizeof classid_page_hash ||
|
|
||||||
memcmp(so->classId->data, classid_page_hash, sizeof classid_page_hash)) {
|
/* Validate serialized object class ID */
|
||||||
|
classid_data = ASN1_STRING_get0_data((ASN1_STRING *)so->classId);
|
||||||
|
classid_len = ASN1_STRING_length((ASN1_STRING *)so->classId);
|
||||||
|
|
||||||
|
if (classid_len != sizeof classid_page_hash ||
|
||||||
|
memcmp(classid_data, classid_page_hash, sizeof classid_page_hash)) {
|
||||||
SpcPeImageData_free(id);
|
SpcPeImageData_free(id);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
/* skip ASN.1 SET hdr */
|
|
||||||
l = asn1_simple_hdr_len(so->serializedData->data, so->serializedData->length);
|
/*Get serialized ASN.1 blob */
|
||||||
blob = so->serializedData->data + l;
|
serialized_data = ASN1_STRING_get0_data((ASN1_STRING *)so->serializedData);
|
||||||
obj = d2i_SpcAttributeTypeAndOptionalValue(NULL, &blob, so->serializedData->length - l);
|
serialized_len = ASN1_STRING_length((ASN1_STRING *)so->serializedData);
|
||||||
|
|
||||||
|
/* Skip ASN.1 SET header */
|
||||||
|
l = asn1_simple_hdr_len(serialized_data, serialized_len);
|
||||||
|
blob = serialized_data + l;
|
||||||
|
|
||||||
|
/* Decode nested SpcAttributeTypeAndOptionalValue */
|
||||||
|
obj = d2i_SpcAttributeTypeAndOptionalValue(NULL, &blob, serialized_len - l);
|
||||||
SpcPeImageData_free(id);
|
SpcPeImageData_free(id);
|
||||||
if (!obj)
|
if (!obj)
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
|
|
||||||
|
/* Determine page hash algorithm */
|
||||||
*phtype = 0;
|
*phtype = 0;
|
||||||
buf[0] = 0x00;
|
buf[0] = 0x00;
|
||||||
OBJ_obj2txt(buf, sizeof buf, obj->type, 1);
|
OBJ_obj2txt(buf, sizeof buf, obj->type, 1);
|
||||||
@@ -890,15 +937,30 @@ static int pe_page_hash_get(u_char **ph, int *phlen, int *phtype, SpcAttributeTy
|
|||||||
SpcAttributeTypeAndOptionalValue_free(obj);
|
SpcAttributeTypeAndOptionalValue_free(obj);
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
/* Skip ASN.1 SET hdr */
|
|
||||||
l2 = asn1_simple_hdr_len(obj->value->value.sequence->data, obj->value->value.sequence->length);
|
/* IMPORTANT:
|
||||||
/* Skip ASN.1 OCTET STRING hdr */
|
* obj now points to the newly decoded structure,
|
||||||
l = asn1_simple_hdr_len(obj->value->value.sequence->data + l2, obj->value->value.sequence->length - l2);
|
* so refresh sequence_data/sequence_len */
|
||||||
|
sequence_data = ASN1_STRING_get0_data(obj->value->value.sequence);
|
||||||
|
sequence_len = ASN1_STRING_length(obj->value->value.sequence);
|
||||||
|
|
||||||
|
/* Skip ASN.1 SET header */
|
||||||
|
l2 = asn1_simple_hdr_len(sequence_data, sequence_len);
|
||||||
|
|
||||||
|
/* Skip ASN.1 OCTET STRING header */
|
||||||
|
l = asn1_simple_hdr_len(sequence_data + l2, sequence_len - l2);
|
||||||
l += l2;
|
l += l2;
|
||||||
*phlen = obj->value->value.sequence->length - l;
|
|
||||||
|
/* Extract raw page hash blob */
|
||||||
|
*phlen = sequence_len - l;
|
||||||
*ph = OPENSSL_malloc((size_t)*phlen);
|
*ph = OPENSSL_malloc((size_t)*phlen);
|
||||||
memcpy(*ph, obj->value->value.sequence->data + l, (size_t)*phlen);
|
if (!*ph) {
|
||||||
|
SpcAttributeTypeAndOptionalValue_free(obj);
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
memcpy(*ph, sequence_data + l, (size_t)*phlen);
|
||||||
SpcAttributeTypeAndOptionalValue_free(obj);
|
SpcAttributeTypeAndOptionalValue_free(obj);
|
||||||
|
|
||||||
return 1; /* OK */
|
return 1; /* OK */
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -914,12 +976,25 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
|
|||||||
uint16_t nsections, opthdr_size;
|
uint16_t nsections, opthdr_size;
|
||||||
uint32_t alignment, pagesize, hdrsize;
|
uint32_t alignment, pagesize, hdrsize;
|
||||||
uint32_t rs, ro, l, lastpos = 0;
|
uint32_t rs, ro, l, lastpos = 0;
|
||||||
int pphlen, phlen, i, pi = 1;
|
int mdlen, pphlen, phlen, i, pi = 1;
|
||||||
size_t written;
|
size_t written, off, sect_off, sect_tbl, need;
|
||||||
u_char *res, *zeroes;
|
u_char *res = NULL, *zeroes = NULL;
|
||||||
char *sections;
|
char *sections;
|
||||||
const EVP_MD *md = EVP_get_digestbynid(phtype);
|
const EVP_MD *md = EVP_get_digestbynid(phtype);
|
||||||
BIO *bhash;
|
BIO *bhash = NULL;
|
||||||
|
uint32_t filebound;
|
||||||
|
size_t pphlen_sz, sections_factor;
|
||||||
|
|
||||||
|
if (rphlen == NULL || ctx == NULL || ctx->options == NULL || ctx->pe_ctx == NULL
|
||||||
|
|| ctx->options->indata == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
if (md == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
mdlen = EVP_MD_size(md);
|
||||||
|
if (mdlen <= 0)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
/* NumberOfSections indicates the size of the section table,
|
/* NumberOfSections indicates the size of the section table,
|
||||||
* which immediately follows the headers, can be up to 65535 under Vista and later */
|
* which immediately follows the headers, can be up to 65535 under Vista and later */
|
||||||
@@ -961,10 +1036,46 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
|
|||||||
fprintf(stderr, "Corrupted optional header size: 0x%08X\n", opthdr_size);
|
fprintf(stderr, "Corrupted optional header size: 0x%08X\n", opthdr_size);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
pphlen = 4 + EVP_MD_size(md);
|
/* Validate that pagesize >= hdrsize to prevent integer underflow */
|
||||||
phlen = pphlen * (3 + (int)nsections + (int)(ctx->pe_ctx->fileend / pagesize));
|
if (pagesize < hdrsize) {
|
||||||
|
fprintf(stderr, "Page size (0x%08X) is smaller than header size (0x%08X)\n",
|
||||||
|
pagesize, hdrsize);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
|
pphlen = 4 + mdlen;
|
||||||
|
|
||||||
|
/* Compute an upper bound for result size and guard overflow */
|
||||||
|
pphlen_sz = (size_t)pphlen;
|
||||||
|
sections_factor = 3 + (size_t)nsections + ((size_t)ctx->pe_ctx->fileend / pagesize);
|
||||||
|
if (sections_factor > SIZE_MAX / pphlen_sz) {
|
||||||
|
fprintf(stderr, "Page hash allocation size would overflow\n");
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
|
phlen = (int)(pphlen_sz * sections_factor);
|
||||||
|
/* Sanity limit - page hash shouldn't exceed reasonable size (16 MB) */
|
||||||
|
if (phlen < 0 || (size_t)phlen > SIZE_16M) {
|
||||||
|
fprintf(stderr, "Page hash size exceeds limit: %d\n", phlen);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Determine the file boundary for section data validation */
|
||||||
|
filebound = ctx->pe_ctx->sigpos ? ctx->pe_ctx->sigpos : ctx->pe_ctx->fileend;
|
||||||
|
|
||||||
|
/* Validate section table bounds before reading section headers */
|
||||||
|
sect_off = (size_t)ctx->pe_ctx->header_size + 24u + (size_t)opthdr_size;
|
||||||
|
sect_tbl = (size_t)nsections * 40u;
|
||||||
|
|
||||||
|
if (sect_off > (size_t)filebound || sect_tbl > (size_t)filebound - sect_off) {
|
||||||
|
fprintf(stderr, "Section table out of bounds: off=%zu size=%zu filebound=%u\n",
|
||||||
|
sect_off, sect_tbl, filebound);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
|
sections = (char *)ctx->options->indata + sect_off;
|
||||||
|
|
||||||
bhash = BIO_new(BIO_f_md());
|
bhash = BIO_new(BIO_f_md());
|
||||||
|
if (bhash == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
#if defined(__GNUC__)
|
#if defined(__GNUC__)
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wcast-qual"
|
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||||
@@ -977,7 +1088,10 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
|
|||||||
#if defined(__GNUC__)
|
#if defined(__GNUC__)
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
BIO_push(bhash, BIO_new(BIO_s_null()));
|
if (BIO_push(bhash, BIO_new(BIO_s_null())) == NULL) {
|
||||||
|
BIO_free_all(bhash);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
if (!BIO_write_ex(bhash, ctx->options->indata, ctx->pe_ctx->header_size + 88, &written)
|
if (!BIO_write_ex(bhash, ctx->options->indata, ctx->pe_ctx->header_size + 88, &written)
|
||||||
|| written != ctx->pe_ctx->header_size + 88) {
|
|| written != ctx->pe_ctx->header_size + 88) {
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
@@ -989,36 +1103,83 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
|
|||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
if (!BIO_write_ex(bhash,
|
off = ctx->pe_ctx->header_size + 160 + (size_t)ctx->pe_ctx->pe32plus * 16;
|
||||||
ctx->options->indata + ctx->pe_ctx->header_size + 160 + ctx->pe_ctx->pe32plus*16,
|
if (hdrsize < off || hdrsize > filebound) {
|
||||||
hdrsize - (ctx->pe_ctx->header_size + 160 + ctx->pe_ctx->pe32plus*16), &written)
|
BIO_free_all(bhash);
|
||||||
|| written != hdrsize - (ctx->pe_ctx->header_size + 160 + ctx->pe_ctx->pe32plus*16)) {
|
return NULL; /* FAILED: header too small */
|
||||||
|
}
|
||||||
|
if (!BIO_write_ex(bhash, ctx->options->indata + off, (size_t)hdrsize - off, &written)
|
||||||
|
|| written != hdrsize - off) {
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
|
if (pagesize < hdrsize) {
|
||||||
|
BIO_free_all(bhash);
|
||||||
|
return NULL; /* FAILED: header larger than page */
|
||||||
|
}
|
||||||
zeroes = OPENSSL_zalloc((size_t)pagesize);
|
zeroes = OPENSSL_zalloc((size_t)pagesize);
|
||||||
if (!BIO_write_ex(bhash, zeroes, pagesize - hdrsize, &written)
|
if (zeroes == NULL) {
|
||||||
|| written != pagesize - hdrsize) {
|
BIO_free_all(bhash);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
|
if (!BIO_write_ex(bhash, zeroes, (size_t)pagesize - (size_t)hdrsize, &written)
|
||||||
|
|| written != (size_t)pagesize - (size_t)hdrsize) {
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
OPENSSL_free(zeroes);
|
OPENSSL_free(zeroes);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
res = OPENSSL_malloc((size_t)phlen);
|
res = OPENSSL_malloc((size_t)phlen);
|
||||||
|
if (res == NULL) {
|
||||||
|
BIO_free_all(bhash);
|
||||||
|
OPENSSL_free(zeroes);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
memset(res, 0, 4);
|
memset(res, 0, 4);
|
||||||
BIO_gets(bhash, (char*)res + 4, EVP_MD_size(md));
|
if (BIO_gets(bhash, (char *)res + 4, mdlen) != mdlen) {
|
||||||
|
BIO_free_all(bhash);
|
||||||
|
OPENSSL_free(zeroes);
|
||||||
|
OPENSSL_free(res);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
sections = ctx->options->indata + ctx->pe_ctx->header_size + 24 + opthdr_size;
|
|
||||||
for (i=0; i<nsections; i++) {
|
for (i = 0; i < (int)nsections; i++) {
|
||||||
/* Resource Table address and size */
|
/* SizeOfRawData and PointerToRawData from section header */
|
||||||
rs = GET_UINT32_LE(sections + 16);
|
rs = GET_UINT32_LE(sections + 16);
|
||||||
ro = GET_UINT32_LE(sections + 20);
|
ro = GET_UINT32_LE(sections + 20);
|
||||||
if (rs == 0 || rs >= UINT32_MAX) {
|
if (rs == 0) {
|
||||||
sections += 40;
|
sections += 40;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
for (l=0; l<rs; l+=pagesize, pi++) {
|
/* Validate section bounds against file size to prevent OOB read */
|
||||||
PUT_UINT32_LE(ro + l, res + pi*pphlen);
|
if (ro >= filebound || rs > filebound - ro) {
|
||||||
|
fprintf(stderr, "Section %d has invalid bounds: offset=0x%08X, size=0x%08X, fileend=0x%08X\n",
|
||||||
|
i, ro, rs, filebound);
|
||||||
|
OPENSSL_free(zeroes);
|
||||||
|
OPENSSL_free(res);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
|
for (l = 0; l < rs; l += pagesize, pi++) {
|
||||||
|
need = (size_t)(pi + 1) * (size_t)pphlen;
|
||||||
|
|
||||||
|
/* Prevent OOB write into res if pi grows beyond allocated factor */
|
||||||
|
if (need > (size_t)phlen) {
|
||||||
|
fprintf(stderr, "Page hash buffer overflow prevented: pi=%d need=%zu phlen=%d\n",
|
||||||
|
pi, need, phlen);
|
||||||
|
OPENSSL_free(zeroes);
|
||||||
|
OPENSSL_free(res);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
|
|
||||||
|
PUT_UINT32_LE(ro + l, res + (size_t)pi * (size_t)pphlen);
|
||||||
|
|
||||||
bhash = BIO_new(BIO_f_md());
|
bhash = BIO_new(BIO_f_md());
|
||||||
|
if (bhash == NULL) {
|
||||||
|
OPENSSL_free(zeroes);
|
||||||
|
OPENSSL_free(res);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(__GNUC__)
|
#if defined(__GNUC__)
|
||||||
#pragma GCC diagnostic push
|
#pragma GCC diagnostic push
|
||||||
#pragma GCC diagnostic ignored "-Wcast-qual"
|
#pragma GCC diagnostic ignored "-Wcast-qual"
|
||||||
@@ -1033,17 +1194,24 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
|
|||||||
#if defined(__GNUC__)
|
#if defined(__GNUC__)
|
||||||
#pragma GCC diagnostic pop
|
#pragma GCC diagnostic pop
|
||||||
#endif
|
#endif
|
||||||
BIO_push(bhash, BIO_new(BIO_s_null()));
|
if (BIO_push(bhash, BIO_new(BIO_s_null())) == NULL) {
|
||||||
if (rs - l < pagesize) {
|
BIO_free_all(bhash);
|
||||||
if (!BIO_write_ex(bhash, ctx->options->indata + ro + l, rs - l, &written)
|
OPENSSL_free(zeroes);
|
||||||
|| written != rs - l) {
|
OPENSSL_free(res);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
if (l < rs && rs - l < pagesize) {
|
||||||
|
size_t tail = (size_t)(rs - l);
|
||||||
|
|
||||||
|
if (!BIO_write_ex(bhash, ctx->options->indata + ro + l, tail, &written)
|
||||||
|
|| written != tail) {
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
OPENSSL_free(zeroes);
|
OPENSSL_free(zeroes);
|
||||||
OPENSSL_free(res);
|
OPENSSL_free(res);
|
||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
if (!BIO_write_ex(bhash, zeroes, pagesize - (rs - l), &written)
|
if (!BIO_write_ex(bhash, zeroes, pagesize - tail, &written)
|
||||||
|| written != pagesize - (rs - l)) {
|
|| written != pagesize - tail) {
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
OPENSSL_free(zeroes);
|
OPENSSL_free(zeroes);
|
||||||
OPENSSL_free(res);
|
OPENSSL_free(res);
|
||||||
@@ -1058,17 +1226,34 @@ static u_char *pe_page_hash_calc(int *rphlen, FILE_FORMAT_CTX *ctx, int phtype)
|
|||||||
return NULL; /* FAILED */
|
return NULL; /* FAILED */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
BIO_gets(bhash, (char*)res + pi*pphlen + 4, EVP_MD_size(md));
|
if (BIO_gets(bhash, (char *)res + (size_t)pi * (size_t)pphlen + 4, mdlen) != mdlen) {
|
||||||
|
BIO_free_all(bhash);
|
||||||
|
OPENSSL_free(zeroes);
|
||||||
|
OPENSSL_free(res);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
BIO_free_all(bhash);
|
BIO_free_all(bhash);
|
||||||
}
|
}
|
||||||
lastpos = ro + rs;
|
lastpos = ro + rs;
|
||||||
sections += 40;
|
sections += 40;
|
||||||
}
|
}
|
||||||
PUT_UINT32_LE(lastpos, res + pi*pphlen);
|
/* Final entry */
|
||||||
memset(res + pi*pphlen + 4, 0, (size_t)EVP_MD_size(md));
|
need = (size_t)(pi + 1) * (size_t)pphlen;
|
||||||
|
|
||||||
|
if (need > (size_t)phlen) {
|
||||||
|
fprintf(stderr, "Page hash buffer overflow prevented at final entry: pi=%d need=%zu phlen=%d\n",
|
||||||
|
pi, need, phlen);
|
||||||
|
OPENSSL_free(zeroes);
|
||||||
|
OPENSSL_free(res);
|
||||||
|
return NULL; /* FAILED */
|
||||||
|
}
|
||||||
|
|
||||||
|
PUT_UINT32_LE(lastpos, res + (size_t)pi * (size_t)pphlen);
|
||||||
|
memset(res + (size_t)pi * (size_t)pphlen + 4, 0, (size_t)mdlen);
|
||||||
pi++;
|
pi++;
|
||||||
|
|
||||||
OPENSSL_free(zeroes);
|
OPENSSL_free(zeroes);
|
||||||
*rphlen = pi*pphlen;
|
*rphlen = pi * pphlen;
|
||||||
return res;
|
return res;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1088,6 +1273,10 @@ static int pe_verify_page_hash(FILE_FORMAT_CTX *ctx, u_char *ph, int phlen, int
|
|||||||
if (!ph)
|
if (!ph)
|
||||||
return 1; /* OK */
|
return 1; /* OK */
|
||||||
cph = pe_page_hash_calc(&cphlen, ctx, phtype);
|
cph = pe_page_hash_calc(&cphlen, ctx, phtype);
|
||||||
|
if (!cph) {
|
||||||
|
fprintf(stderr, "Page hash verification failed: could not calculate page hash\n");
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
mdok = (phlen == cphlen) && !memcmp(ph, cph, (size_t)phlen);
|
mdok = (phlen == cphlen) && !memcmp(ph, cph, (size_t)phlen);
|
||||||
printf("Page hash algorithm : %s\n", OBJ_nid2sn(phtype));
|
printf("Page hash algorithm : %s\n", OBJ_nid2sn(phtype));
|
||||||
if (ctx->options->verbose) {
|
if (ctx->options->verbose) {
|
||||||
@@ -1190,7 +1379,8 @@ static int pe_check_file(FILE_FORMAT_CTX *ctx)
|
|||||||
{
|
{
|
||||||
uint32_t real_pe_checksum, sum = 0;
|
uint32_t real_pe_checksum, sum = 0;
|
||||||
|
|
||||||
if (!ctx) {
|
if (ctx == NULL || ctx->pe_ctx == NULL || ctx->options == NULL
|
||||||
|
|| ctx->options->indata == NULL) {
|
||||||
fprintf(stderr, "Init error\n");
|
fprintf(stderr, "Init error\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
@@ -1202,25 +1392,52 @@ static int pe_check_file(FILE_FORMAT_CTX *ctx)
|
|||||||
printf("Calculated PE checksum: %08X\n", real_pe_checksum);
|
printf("Calculated PE checksum: %08X\n", real_pe_checksum);
|
||||||
printf("Warning: invalid PE checksum\n");
|
printf("Warning: invalid PE checksum\n");
|
||||||
}
|
}
|
||||||
|
/* Signature directory bounds */
|
||||||
if (ctx->pe_ctx->sigpos == 0 || ctx->pe_ctx->siglen == 0
|
if (ctx->pe_ctx->sigpos == 0 || ctx->pe_ctx->siglen == 0
|
||||||
|| ctx->pe_ctx->sigpos > ctx->pe_ctx->fileend) {
|
|| ctx->pe_ctx->sigpos > ctx->pe_ctx->fileend
|
||||||
|
|| ctx->pe_ctx->siglen > ctx->pe_ctx->fileend - ctx->pe_ctx->sigpos) {
|
||||||
fprintf(stderr, "No signature found\n");
|
fprintf(stderr, "No signature found\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
|
* Validate WIN_CERTIFICATE chain.
|
||||||
* If the sum of the rounded dwLength values does not equal the Size value,
|
* If the sum of the rounded dwLength values does not equal the Size value,
|
||||||
* then either the attribute certificate table or the Size field is corrupted.
|
* then either the attribute certificate table or the Size field is corrupted.
|
||||||
*/
|
*/
|
||||||
while (sum < ctx->pe_ctx->siglen) {
|
while (sum < ctx->pe_ctx->siglen) {
|
||||||
uint32_t len = GET_UINT32_LE(ctx->options->indata + ctx->pe_ctx->sigpos + sum);
|
uint32_t len, off;
|
||||||
if (ctx->pe_ctx->siglen - len > 8) {
|
|
||||||
|
/* Prevent overflow in sigpos + sum */
|
||||||
|
if (sum > UINT32_MAX - ctx->pe_ctx->sigpos) {
|
||||||
fprintf(stderr, "Corrupted attribute certificate table\n");
|
fprintf(stderr, "Corrupted attribute certificate table\n");
|
||||||
fprintf(stderr, "Attribute certificate table size : %08X\n", ctx->pe_ctx->siglen);
|
|
||||||
fprintf(stderr, "Attribute certificate entry length: %08X\n\n", len);
|
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
/* quadword align data */
|
off = ctx->pe_ctx->sigpos + sum;
|
||||||
len += len % 8 ? 8 - len % 8 : 0;
|
|
||||||
|
/* Need at least 4 bytes to read dwLength */
|
||||||
|
if (off > ctx->pe_ctx->fileend || ctx->pe_ctx->fileend - off < 4) {
|
||||||
|
fprintf(stderr, "Corrupted attribute certificate table\n");
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
len = GET_UINT32_LE(ctx->options->indata + off);
|
||||||
|
|
||||||
|
/* dwLength must include the 8-byte WIN_CERTIFICATE header */
|
||||||
|
if (len < 8 || len > ctx->pe_ctx->siglen - sum || len > ctx->pe_ctx->fileend - off) {
|
||||||
|
fprintf(stderr, "Corrupted attribute certificate table\n");
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Quadword align data */
|
||||||
|
if (len % 8) {
|
||||||
|
uint32_t pad = 8 - (len % 8);
|
||||||
|
|
||||||
|
/* Ensure quadword alignment does not overflow or exceed remaining table size */
|
||||||
|
if (pad > ctx->pe_ctx->siglen - sum - len) {
|
||||||
|
fprintf(stderr, "Corrupted attribute certificate table\n");
|
||||||
|
return 0; /* FAILED */
|
||||||
|
}
|
||||||
|
len += pad;
|
||||||
|
}
|
||||||
sum += len;
|
sum += len;
|
||||||
}
|
}
|
||||||
if (sum != ctx->pe_ctx->siglen) {
|
if (sum != ctx->pe_ctx->siglen) {
|
||||||
|
|||||||
@@ -288,20 +288,7 @@ static int script_verify_digests(FILE_FORMAT_CTX *ctx, PKCS7 *p7)
|
|||||||
const EVP_MD *md;
|
const EVP_MD *md;
|
||||||
BIO *bhash;
|
BIO *bhash;
|
||||||
|
|
||||||
/* FIXME: this shared code most likely belongs in osslsigncode.c */
|
if (!pkcs7_get_content_digest(p7, mdbuf, &mdtype)) {
|
||||||
if (is_content_type(p7, SPC_INDIRECT_DATA_OBJID)) {
|
|
||||||
ASN1_STRING *content_val = p7->d.sign->contents->d.other->value.sequence;
|
|
||||||
const u_char *p = content_val->data;
|
|
||||||
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, content_val->length);
|
|
||||||
if (idc) {
|
|
||||||
if (spc_extract_digest_safe(idc, mdbuf, &mdtype) < 0) {
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
return 0; /* FAILED */
|
|
||||||
}
|
|
||||||
SpcIndirectDataContent_free(idc);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (mdtype == -1) {
|
|
||||||
fprintf(stderr, "Failed to extract current message digest\n\n");
|
fprintf(stderr, "Failed to extract current message digest\n\n");
|
||||||
return 0; /* FAILED */
|
return 0; /* FAILED */
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user