42 Commits

Author SHA1 Message Date
olszomal bf209e0fc8 Refactor SpcIndirectDataContent parsing helpers 2026-05-26 17:27:46 +02:00
olszomal 68a6826cd1 Use ASN1_STRING and ASN1_INTEGER APIs instead of direct struct access 2026-05-26 17:27:46 +02:00
olszomal ea5d15862d cmake: add documentation generation, GNU install dirs, and uninstall target 2026-05-26 17:27:46 +02:00
olszomal bdde95635f attach-signature flow moved to digest-only verification semantics
attach-signature now uses digest-only verification instead of full
signature validation; output file is not kept if digest verification fails
2026-04-02 17:43:43 +02:00
olszomal d0ef178a9a Remove redundant NULL assignments, CID 1669015 2026-02-25 14:07:58 +01:00
olszomal e126ab3e4a Fix memory leak of compressedData on error paths, CID 1669014 2026-02-25 14:07:58 +01:00
Michał Trojnara a1fb6600fb Fix handling OpenSSL initialization errors 2026-02-11 16:23:57 +01:00
Michał Trojnara 8227c68ceb Do not export osslsigncode.c consts 2026-02-11 15:50:25 +01:00
Michał Trojnara c988b48063 Refactor UI method cleanup 2026-02-11 15:48:17 +01:00
Michał Trojnara 6cf70b4af2 Initial 2.14-dev commit 2026-02-10 23:17:56 +01:00
Michał Trojnara 97a9ade6ec Release 2.13
Signed-off-by: Michał Trojnara <Michal.Trojnara@stunnel.org>
2026-02-10 22:51:25 +01:00
Michał Trojnara f2f33bb131 Move providers_cleanup() back 2026-02-10 19:10:00 +01:00
Michał Trojnara 202b2c2866 Separate OpenSSL and functional initialization 2026-02-10 18:37:48 +01:00
olszomal 2a5409b7c4 Fix header bounds validation in PE page hash calculation 2026-02-09 16:15:21 +01:00
olszomal 87bce8e372 Fix memory leaks on error paths in PE signing 2026-02-09 16:15:21 +01:00
olszomal f7ace57c81 Validate PE attribute certificate table bounds 2026-02-09 16:15:21 +01:00
olszomal 92f8761b47 Fix heap corruption in PE page hash calculation 2026-02-06 15:41:01 +01:00
Antoni Klajn 09d3312fd9 Fixed integer overflow, integer underflow and Out-of-Bounds Read 2026-02-06 11:39:49 +01:00
olszomal 9d02a20aec Fix unsafe ZIP size handling and allocation checks 2026-02-04 10:13:03 +01:00
olszomal f190ec5d87 Fix double free in ZIP local header 2026-02-04 10:13:03 +01:00
Michał Trojnara 4b30d6be28 Initial 2.12-dev commit 2026-02-03 17:20:29 +01:00
Michał Trojnara fac8164622 Release 2.12
Signed-off-by: Michał Trojnara <Michal.Trojnara@stunnel.org>
2026-02-02 17:53:25 +01:00
Michał Trojnara cbee1e723c Fixed buffer overflow while extracting msg digest
Reported and fixed by Antoni Klajn, Opera
2026-02-02 17:37:45 +01:00
Michał Trojnara f90327df09 Initial 2.12-dev commit 2026-01-21 00:08:58 +01:00
Michał Trojnara f3a590be69 Release 2.11
Signed-off-by: Michał Trojnara <Michal.Trojnara@stunnel.org>
2026-01-20 23:53:33 +01:00
Michał Trojnara 6631a5f10b Update trusted certificate list 2026-01-20 23:21:33 +01:00
Michał Trojnara 9924f0c085 Minor style improvements
- Reorder checks by corresponding RFC 5280 section numbers.
- Simplify comments.
2026-01-20 23:21:33 +01:00
olszomal 7d85ac5f04 Add GitHub issue and PR templates 2026-01-09 14:41:02 +01:00
olszomal feebbcd4d9 Print current CRL during certificate verification 2026-01-07 13:36:43 +01:00
olszomal d787541107 tests: add digitalSignature keyUsage to leaf certificate 2025-12-31 11:38:21 +01:00
olszomal 6390ae2746 Add keyUsage digitalSignature validation for signer certificate 2025-12-31 11:38:21 +01:00
Michał Trojnara a472d7fbff Comment returned CRL Content-Type 2025-12-22 19:26:21 +01:00
Chris Thibodeaux 27172a07ca Patch CRL fetch failure from expected file type
`application/octet-stream` response types caused CRL/TSA-CRL fetch failures
2025-12-22 19:22:01 +01:00
olszomal d77ddb9443 Disable keep-alive and remove the shutdown workaround.
Some RFC 3161 TSA servers (e.g. time.certum.pl) advertise
"Connection: close" but delay closing the connection,
when keep-alive was requested. The client waited for EOF and
attempted to work around this by explicitly shutting down the socket.
2025-12-19 15:11:12 +01:00
olszomal 988f72249b Simplify ASLR/PIE flags check for macOS 2025-12-16 06:42:01 +01:00
olszomal c23f92ca68 Improve PKCS#11 and CNG usage instructions in README 2025-09-22 11:16:29 +02:00
olszomal 842bd94aaf Remove duplicate certs from sorted X509 chain 2025-09-19 17:02:21 +02:00
olszomal 1d72c3da8c Improve key/cert loading logic and standardize usage file argument names 2025-09-19 17:02:21 +02:00
olszomal d792e8d0db Use bio_new_file() wrapper instead of BIO_new_file() for consistent file handling 2025-09-19 17:01:02 +02:00
olszomal bbdfc1d98a Avoid undefined behavior with BIO_get_fp by replacing BIO_new_file with fopen + BIO_new_fp 2025-09-19 17:01:02 +02:00
olszomal 5ac11e9f58 Fix -Wsign-conversion warning in x509_name_to_utf8() 2025-07-01 12:21:44 +02:00
Michał Trojnara 55541c6ace Initial 2.11-dev commit 2025-06-23 21:42:26 +02:00
28 changed files with 3355 additions and 1151 deletions
+8
View File
@@ -0,0 +1,8 @@
<!--
Please use one of the available issue templates.
Bug reports without required information may be closed.
-->
If you are reporting a bug or crash, please use the appropriate issue template.
For questions or support, use please use [Discussions](<https://github.com/mtrojnar/osslsigncode/discussions>).
+74
View File
@@ -0,0 +1,74 @@
---
name: Crash report
about: Report a segmentation fault or other crash
labels: crash
---
<!--
Thank you for your crash report.
Note: Please search to see if an issue already exists for the bug you encountered.
-->
### Segmentation Fault / Crash Details
<!--
Provide exact, reproducible steps.
Include the complete command, exactly as executed.
-->
- Signal / exit code: <!-- SIGSEGV, SIGABRT -->
- Reproducibility: <!-- always / sometimes / once -->
- Affected command or operation: <!-- e.g. `osslsigncode sign`, `osslsigncode verify` -->
- First observed version:
- Last known working version (if any):
#### Backtrace
<!--
Provide a backtrace from gdb or lldb.
Build with debug symbols if possible. Use `bt full` if possible.
Crash reports without a backtrace may be closed without investigation.
-->
- `(gdb) bt`
#### Memory / Sanitizers
<!--
Attach relevant output if available.
-->
- [ ] Valgrind
- [ ] ASan / UBSan
- [ ] Other tools
#### Crash Context
<!--
Anything that may be relevant:
- OpenSSL provider / engine in use
- PKCS#11 modules
- Custom OpenSSL configuration
- Threading or concurrency
-->
### Environment
- Operating system and version (e.g. Ubuntu 24.04):
- Architecture (x86_64, arm64, etc.):
### Versions
<!--
Please verify that the issue is reproducible with the current upstream master.
-->
- osslsigncode built from:
- [ ] upstream master
- [ ] upstream release (tag):
- [ ] distribution package (name and version):
- `openssl version -a`
- `osslsigncode --version`
### Configuration / Settings
<!--
Anything that could affect signing or verification:
- Custom OpenSSL configuration
- Engine / provider settings
- Environment variables (OPENSSL_CONF, etc.)
-->
### Anything else
<!--
Links, references, related issues, workarounds or additional observations.
-->
@@ -0,0 +1,26 @@
---
name: Documentation
about: Report an error in (or missing) documentation
labels: documentation
---
<!--
Thank you for taking the time to report a documentation issue.
-->
### Documentation Location
<!--
Where is the problem located?
Provide a link, file path, or section name.
-->
### Issue Description
<!--
Describe what is wrong or missing.
-->
### Suggested Improvement (optional)
<!--
If you know how it should be fixed, describe it here.
Proposed wording or examples are especially helpful.
-->
@@ -0,0 +1,30 @@
---
name: Feature request
about: Suggest a new feature or improvement
labels: feature
---
<!--
Thank you for your feature request.
Please describe the use case and motivation as clearly as possible.
-->
### Use Case / Motivation
<!--
What problem are you trying to solve?
Why is this feature needed?
-->
### Proposed Change
<!--
Describe the feature or improvement you are proposing.
High-level description is sufficient.
-->
### Additional Notes (optional)
<!--
Anything else that may help:
- examples
- references
- related issues
-->
+17
View File
@@ -0,0 +1,17 @@
---
name: Questions / Support
about: Please use Q&A in Discussions instead
labels: question
---
### Questions and Support
Please do **not** use GitHub issues for general questions or support requests.
For:
- usage questions
- "how do I..." questions
please use [Q&A category in Discussions](<https://github.com/mtrojnar/osslsigncode/discussions/new?category=q-a>)
Bug reports and crashes should be reported using the appropriate issue templates.
+77
View File
@@ -0,0 +1,77 @@
---
name: Other bug report
about: Report a bug
labels: bug
---
<!--
Thank you for your bug report.
Note: Please search to see if an issue already exists for the bug you encountered.
-->
### Current Behavior
<!--
A concise description of what is happening.
Include error messages or incorrect results.
-->
### Expected Behavior
<!--
A concise description of what you expected to happen instead.
-->
### Steps To Reproduce & Observed Output
<!--
Provide exact, reproducible steps together with full stdout/stderr for each.
-->
1. Signing with osslsigncode
<!--
Full `osslsigncode sign` command and complete stdout/stderr output.
-->
2. Verification with osslsigncode
<!--
Full `osslsigncode verify` command and complete stdout/stderr output.
-->
3. Signing / verification with Windows signtool (if applicable)
<!--
Full signtool command (`signtool verify /pa /v`) and complete stdout/stderr output.
-->
### Environment
- Operating system and version (e.g. Ubuntu 24.04):
- Architecture (x86_64, arm64, etc.):
### Versions
<!--
Please verify that the issue is reproducible with the current upstream master.
-->
- osslsigncode built from:
- [ ] upstream master
- [ ] upstream release (tag):
- [ ] distribution package (name and version):
- `openssl version -a`
- `osslsigncode --version`
### Files
<!--
Attach files if possible, or mention that you will share them privately.
-->
- [ ] unsigned file
- [ ] file signed with osslsigncode
- [ ] file signed with signtool or the other tool (for comparison)
- [ ] certificate chain used for verification (PEM format)
### Configuration / Settings
<!--
Anything that could affect signing or verification:
- Custom OpenSSL configuration
- Engine / provider settings
- Environment variables (OPENSSL_CONF, etc.)
-->
### Anything else
<!--
Links, references, related issues, workarounds or additional observations.
-->
+65
View File
@@ -0,0 +1,65 @@
<!--
Thank you for your pull request.
Provide a concise summary of the changes in the PR title.
-->
### Pull Request Type
<!--
Limit this PR to a single type. If necessary, split changes into multiple PRs.
-->
- [ ] Bug fix
- [ ] New feature
- [ ] Code style / formatting / renaming
- [ ] Refactoring (no functional or API changes)
- [ ] Build / CI related changes
- [ ] Documentation
- [ ] Other (please describe):
### Related Issue
<!--
If this fixes a GitHub issue, make sure to have a line saying 'Fixes #XXXX' (without quotes) in the commit message.
-->
Issue number: N/A
### Current Behavior
<!--
Describe the current behavior or limitation this PR addresses.
Include error messages or crash symptoms if relevant.
-->
### New Behavior
<!--
Describe the new or changed behavior introduced by this PR.
-->
### Scope of Changes
<!--
Briefly describe what was changed and why.
Focus on relevant parts only.
-->
### Testing
<!--
Describe how the changes were tested.
Include commands, environments, or platforms if relevant.
-->
- [ ] Existing tests
- [ ] New tests added
- [ ] Manual testing
### Additional Notes
<!--
Any additional information relevant for reviewers:
- design decisions
- backward compatibility
- known limitations
-->
## License Declaration
<!--
All contributions to this project are licensed under the project's license.
By submitting this pull request, you confirm that you have the right to submit
the code and agree to license it accordingly.
-->
- [ ] I hereby agree to license my contribution under the project's license.
+1 -1
View File
@@ -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.10 version: osslsigncode-2.14-dev
jobs: jobs:
build: build:
+25 -5
View File
@@ -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.10 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
+43
View File
@@ -1,5 +1,48 @@
# 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)
**CRITICAL SECURITY VULNERABILITY**
This release fixes a critical memory corruption vulnerability. A malicious
attacker could create a signed file, which, when verified with osslsigncode,
triggers arbitrary code execution. Any previous version of osslsigncode should
be immediately upgraded if the tool is used for verification of untrusted
files.
- fixed a buffer overflow while extracting message digests
(reported and fixed by Antoni Klajn, Opera)
### 2.11 (2026.01.20)
- added keyUsage validation for signer certificate
(thanks to Hanqing Zhao and Zi-Quan You for reporting the issue)
- added printing CRL details during signature verification
- implemented a workaround for CRL servers returning the HTTP Content-Type
header other than application/pkix-crl (thanks to Chris Thibodeaux)
- fixed HTTP keep-alive handling
- fixed macOS compiler and linker flags
- fixed undefined BIO_get_fp() behavior with BIO_FLAGS_UPLINK_INTERNAL
### 2.10 (2025.06.23) ### 2.10 (2025.06.23)
- added JavaScript signing - added JavaScript signing
+33 -16
View File
@@ -133,8 +133,9 @@ To sign a CAB file containing Java class files:
``` ```
Only the 'low' parameter is currently supported. Only the 'low' parameter is currently supported.
If you want to use a PKCS#11 token, you should specify the PKCS#11 engine and module. ### Using the PKCS#11 Engine with osslsigncode
An example of using osslsigncode with SoftHSM: If you want to use a PKCS#11 token, specify the PKCS#11 engine and module.
Example usage with SoftHSM:
``` ```
osslsigncode sign \ osslsigncode sign \
-engine /usr/lib64/engines-1.1/pkcs11.so \ -engine /usr/lib64/engines-1.1/pkcs11.so \
@@ -144,8 +145,10 @@ An example of using osslsigncode with SoftHSM:
-in yourapp.exe -out yourapp-signed.exe -in yourapp.exe -out yourapp-signed.exe
``` ```
Since OpenSSL 3.0, you can use a PKCS#11 token with the PKCS#11 provider. ### Using the PKCS#11 Provider with osslsigncode (OpenSSL 3.x only)
An example of using osslsigncode with OpenSC: OpenSSL 3.0 introduced a new provider-based architecture. To use a PKCS#11 token
with `osslsigncode`, specify the PKCS#11 provider and module.
Example usage with OpenSC:
``` ```
osslsigncode sign \ osslsigncode sign \
-provider /usr/lib64/ossl-modules/pkcs11prov.so \ -provider /usr/lib64/ossl-modules/pkcs11prov.so \
@@ -155,26 +158,40 @@ An example of using osslsigncode with OpenSC:
-in yourapp.exe -out yourapp-signed.exe -in yourapp.exe -out yourapp-signed.exe
``` ```
You can use a certificate and key stored in the Windows Certificate Store with ### Using the CNG Engine with osslsigncode (Windows only)
the CNG engine version 1.1 or later. For more information, refer to The CNG engine allows using certificates and keys stored in the Windows
Certificate Store. It requires CNG engine version 1.1 or later. For more
information, refer to
https://www.stunnel.org/cng-engine.html https://www.stunnel.org/cng-engine.html
A non-commercial edition of CNG engine is available for testing, personal, A non-commercial edition of CNG engine is available for testing, personal,
educational, or research purposes. educational, or research purposes.
To use the CNG engine with osslsigncode, ensure that the `cng.dll` library is To ensure `osslsigncode` can locate and load the CNG engine module (`cng.dll`)
placed in the same directory as the `osslsigncode.exe` executable. even when it is not installed in the default system engine directory, you can:
Below is an example of how to use osslsigncode with the CNG engine: - Specify the full or relative path to `cng.dll`:
``` ```
osslsigncode sign \ osslsigncode sign -engine C:\my\engines\cng.dll ...
-engine cng \ ```
-pkcs11cert osslsigncode_cert \ - Or set the `OPENSSL_ENGINES` environment variable to the directory containing
-key osslsigncode_cert \ `cng.dll`, and refer to the engine by its ID:
-engineCtrl store_flags:0 \ ```
-engineCtrl store_name:MY \ set OPENSSL_ENGINES=C:\my\engines
-engineCtrl PIN:yourpass \ osslsigncode sign -engine cng ...
```
Below is an example of how to use `osslsigncode` with the CNG engine on Windows:
```
set OPENSSL_ENGINES=C:\my\engines
osslsigncode sign ^
-engine cng ^
-pkcs11cert osslsigncode_cert ^
-key osslsigncode_cert ^
-engineCtrl store_flags:0 ^
-engineCtrl store_name:MY ^
-engineCtrl PIN:yourpass ^
-in yourapp.exe -out yourapp-signed.exe -in yourapp.exe -out yourapp-signed.exe
``` ```
+84 -34
View File
@@ -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) {
@@ -2744,7 +2791,10 @@ static int get_current_position(BIO *bio, uint64_t *offset)
FILE *file = NULL; FILE *file = NULL;
int64_t pos; int64_t pos;
BIO_get_fp(bio, &file); if (BIO_get_fp(bio, &file) != 1 || file == NULL) {
fprintf(stderr, "BIO_get_fp() failed\n");
return 0; /* FAILED */
}
pos = ftello(file); pos = ftello(file);
if (pos < 0) { if (pos < 0) {
return 0; /* FAILED */ return 0; /* FAILED */
+1 -13
View File
@@ -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 (idc->messageDigest && idc->messageDigest->digest && idc->messageDigest->digestAlgorithm) {
mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm);
memcpy(mdbuf, idc->messageDigest->digest->data, (size_t)idc->messageDigest->digest->length);
}
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 */
} }
+28 -21
View File
@@ -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,27 +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 (idc->messageDigest && idc->messageDigest->digest && idc->messageDigest->digestAlgorithm) {
/* get a digest algorithm a message digest of the file from the content */ if (spc_indirect_data_content_get_digest(idc, mdbuf, &mdtype) < 0) {
mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm); fprintf(stderr, "Failed to extract message digest from signature\n\n");
memcpy(mdbuf, idc->messageDigest->digest->data, (size_t)idc->messageDigest->digest->length); SpcIndirectDataContent_free(idc);
}
SpcIndirectDataContent_free(idc);
if (mdtype == -1) {
fprintf(stderr, "Failed to extract current message digest\n\n");
return 0; /* FAILED */ return 0; /* FAILED */
} }
SpcIndirectDataContent_free(idc);
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 */
} }
+44
View File
@@ -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
View File
@@ -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}")
+4 -9
View File
@@ -96,16 +96,11 @@ function(add_compile_flags target)
message(WARNING "No stack protection supported") message(WARNING "No stack protection supported")
endif(HAVE_STACK_PROTECTOR) endif(HAVE_STACK_PROTECTOR)
endif(HAVE_STACK_PROTECTOR_ALL) endif(HAVE_STACK_PROTECTOR_ALL)
# Support address space layout randomization (ASLR) # Support address space layout randomization (ASLR) / PIE
if(NOT (MINGW OR CYGWIN OR CMAKE_C_COMPILER_ID STREQUAL "AppleClang" if(UNIX AND NOT APPLE)
OR ((CMAKE_SYSTEM_NAME MATCHES Darwin) AND (CMAKE_C_COMPILER_ID MATCHES Clang))))
target_compile_options(${target} PRIVATE -fPIE) target_compile_options(${target} PRIVATE -fPIE)
target_link_options(${target} PRIVATE -fPIE -pie) target_link_options(${target} PRIVATE -fPIE -pie -Wl,-z,relro,-z,now,-z,noexecstack)
target_link_options(${target} PRIVATE -Wl,-z,relro) endif(UNIX AND NOT APPLE)
target_link_options(${target} PRIVATE -Wl,-z,now)
target_link_options(${target} PRIVATE -Wl,-z,noexecstack)
endif(NOT (MINGW OR CYGWIN OR CMAKE_C_COMPILER_ID STREQUAL "AppleClang"
OR ((CMAKE_SYSTEM_NAME MATCHES Darwin) AND (CMAKE_C_COMPILER_ID MATCHES Clang))))
target_link_options(${target} PRIVATE -fstack-check) target_link_options(${target} PRIVATE -fstack-check)
add_compile_flag_to_targets(${target}) add_compile_flag_to_targets(${target})
endif(MSVC) endif(MSVC)
+24
View File
@@ -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()
+1341 -638
View File
File diff suppressed because it is too large Load Diff
+18 -7
View File
@@ -10,6 +10,7 @@ from csv import reader
from requests import get from requests import get
from requests.exceptions import RequestException from requests.exceptions import RequestException
from concurrent.futures import ThreadPoolExecutor from concurrent.futures import ThreadPoolExecutor
from re import search
def download_cert(hash): def download_cert(hash):
for attempt in range(10): for attempt in range(10):
@@ -17,23 +18,33 @@ def download_cert(hash):
sleep(10) sleep(10)
try: try:
creds = f'{attempt}{hash}:{attempt}{hash}' creds = f'{attempt}{hash}:{attempt}{hash}'
resp = get(f'https://crt.sh/?d={hash}', proxies = dict(https=f'socks5://{creds}@127.0.0.1:9050')
proxies=dict(https=f'socks5://{creds}@127.0.0.1:9050'))
url = f'https://crt.sh/?sha1={hash}&match=='
resp = get(url, proxies=proxies)
resp.raise_for_status() resp.raise_for_status()
m = search(r'\bid=(\d+)\b', resp.content.decode('ascii', 'replace'))
id = m.group(1)
url = f'https://crt.sh/?d={id}'
resp = get(url, proxies=proxies)
resp.raise_for_status()
print('.', file=stderr, end='') print('.', file=stderr, end='')
stderr.flush() stderr.flush()
return resp.content.decode('utf-8') return resp.content.decode('utf-8', 'replace')
except RequestException as e: except Exception as e:
print(f'\nAttempt {attempt}: {e}', file=stderr) print(f'\n{url} attempt {attempt}: {e}', file=stderr)
print('\nGiving up on', hash, file=stderr) print('\nGiving up on', hash, file=stderr)
resp = get('https://ccadb-public.secure.force.com/microsoft/IncludedCACertificateReportForMSFTCSV') resp = get('https://ccadb.my.salesforce-sites.com/microsoft/IncludedCACertificateReportForMSFTCSV')
resp.raise_for_status() resp.raise_for_status()
lines = resp.content.decode('utf-8').splitlines()[1:] lines = resp.content.decode('utf-8').splitlines()[1:]
hashes = [row[4] for row in reader(lines) hashes = [row[4] for row in reader(lines)
if row[0] != 'Disabled' if row[0] != 'Disabled'
or row[4] == 'F38406E540D7A9D90CB4A9479299640FFB6DF9E224ECC7A01C0D9558D8DAD77D'] or row[4] == 'F38406E540D7A9D90CB4A9479299640FFB6DF9E224ECC7A01C0D9558D8DAD77D']
with ThreadPoolExecutor(max_workers=20) as executor: with ThreadPoolExecutor(max_workers=10) as executor:
certs = executor.map(download_cert, hashes) certs = executor.map(download_cert, hashes)
for cert in certs: for cert in certs:
if cert is not None: if cert is not None:
+164 -2
View File
@@ -17,6 +17,7 @@ static int pkcs7_signer_info_add_purpose(PKCS7_SIGNER_INFO *si, FILE_FORMAT_CTX
static int pkcs7_signer_info_add_sequence_number(PKCS7_SIGNER_INFO *si, FILE_FORMAT_CTX *ctx); static int pkcs7_signer_info_add_sequence_number(PKCS7_SIGNER_INFO *si, FILE_FORMAT_CTX *ctx);
static STACK_OF(X509) *X509_chain_get_sorted(FILE_FORMAT_CTX *ctx, int signer); static STACK_OF(X509) *X509_chain_get_sorted(FILE_FORMAT_CTX *ctx, int signer);
static int X509_compare(const X509 *const *a, const X509 *const *b); static int X509_compare(const X509 *const *a, const X509 *const *b);
static void sk_X509_remove_duplicates(STACK_OF(X509) *chain);
/* /*
* Common functions * Common functions
@@ -340,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
@@ -562,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
*/ */
@@ -617,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;
@@ -763,6 +893,9 @@ static STACK_OF(X509) *X509_chain_get_sorted(FILE_FORMAT_CTX *ctx, int signer)
} }
/* sort certificate chain using the supplied comparison function */ /* sort certificate chain using the supplied comparison function */
sk_X509_sort(chain); sk_X509_sort(chain);
/* remove duplicates */
sk_X509_remove_duplicates(chain);
return chain; return chain;
} }
@@ -814,6 +947,35 @@ static int X509_compare(const X509 *const *a, const X509 *const *b)
return ret; return ret;
} }
/*
* Remove duplicate certificates from a sorted STACK_OF(X509).
*
* This function assumes the stack is sorted according to X.690-compliant
* certificate comparison, so duplicate certificates appear consecutively.
* It iterates through the stack and removes any duplicate certificates
* by comparing each element with its immediate predecessor.
* The stack is modified in place.
*/
static void sk_X509_remove_duplicates(STACK_OF(X509) *chain)
{
int i, n = sk_X509_num(chain);
if (n < 2)
return;
/* start from the second element */
for (i = 1; i < n; ) {
if (!X509_cmp(sk_X509_value(chain, i - 1), sk_X509_value(chain, i))) {
/* duplicate found: remove the certificate at index i */
(void)sk_X509_delete(chain, i);
n--; /* reduce stack size since one element was removed */
/* do not increment i, as next element shifts into index i */
} else {
i++; /* advance only if no removal was done */
}
}
}
/* /*
Local Variables: Local Variables:
c-basic-offset: 4 c-basic-offset: 4
+4
View File
@@ -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);
@@ -25,6 +28,7 @@ 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 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:
+1 -13
View File
@@ -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 (idc->messageDigest && idc->messageDigest->digest && idc->messageDigest->digestAlgorithm) {
mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm);
memcpy(mdbuf, idc->messageDigest->digest->data, (size_t)idc->messageDigest->digest->length);
}
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 */
} }
+375 -282
View File
File diff suppressed because it is too large Load Diff
+554
View File
@@ -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/>
+299 -82
View File
@@ -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 (idc->messageDigest && idc->messageDigest->digest && idc->messageDigest->digestAlgorithm) {
mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm);
memcpy(mdbuf, idc->messageDigest->digest->data, (size_t)idc->messageDigest->digest->length);
}
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) {
+1 -14
View File
@@ -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 (idc->messageDigest && idc->messageDigest->digest && idc->messageDigest->digestAlgorithm) {
mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm);
memcpy(mdbuf, idc->messageDigest->digest->data, (size_t)idc->messageDigest->digest->length);
}
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 */
} }
+12
View File
@@ -338,6 +338,17 @@ class LeafCertificate(X509Extensions):
authority_key = AuthorityKeyIdentifier.from_issuer_subject_key_identifier( authority_key = AuthorityKeyIdentifier.from_issuer_subject_key_identifier(
self.issuer_cert.extensions.get_extension_for_class(SubjectKeyIdentifier).value self.issuer_cert.extensions.get_extension_for_class(SubjectKeyIdentifier).value
) )
key_usage = KeyUsage(
digital_signature=True,
content_commitment=False,
key_encipherment=False,
data_encipherment=False,
key_agreement=False,
key_cert_sign=False,
crl_sign=False,
encipher_only=False,
decipher_only=False
)
extended_key_usage = ExtendedKeyUsage( extended_key_usage = ExtendedKeyUsage(
[ExtendedKeyUsageOID.CODE_SIGNING] [ExtendedKeyUsageOID.CODE_SIGNING]
) )
@@ -352,6 +363,7 @@ class LeafCertificate(X509Extensions):
.add_extension(BasicConstraints(ca=False, path_length=None), critical=False) .add_extension(BasicConstraints(ca=False, path_length=None), critical=False)
.add_extension(SubjectKeyIdentifier.from_public_key(public_key), critical=False) .add_extension(SubjectKeyIdentifier.from_public_key(public_key), critical=False)
.add_extension(authority_key, critical=False) .add_extension(authority_key, critical=False)
.add_extension(key_usage, critical=False)
.add_extension(extended_key_usage, critical=False) .add_extension(extended_key_usage, critical=False)
.add_extension(self.create_x509_crldp(), critical=False) .add_extension(self.create_x509_crldp(), critical=False)
.sign(self.issuer_key, SHA256()) .sign(self.issuer_key, SHA256())