the-backdoor-factory/osslsigncode/osslsigncode.c
2015-12-19 08:38:39 -05:00

3458 lines
97 KiB
C

/*
OpenSSL based Authenticode signing for PE/MSI/Java CAB files.
Copyright (C) 2005-2015 Per Allansson <pallansson@gmail.com>
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
In addition, as a special exception, the copyright holders give
permission to link the code of portions of this program with the
OpenSSL library under certain conditions as described in each
individual source file, and distribute linked combinations
including the two.
You must obey the GNU General Public License in all respects
for all of the code used other than OpenSSL. If you modify
file(s) with this exception, you may extend this exception to your
version of the file(s), but you are not obligated to do so. If you
do not wish to do so, delete this exception statement from your
version. If you delete this exception statement from all source
files in the program, then also delete it here.
*/
static const char *rcsid = "$Id: osslsigncode.c,v 1.7.1 2014/07/11 14:14:14 mfive Exp $";
/*
Implemented with good help from:
* Peter Gutmann's analysis of Authenticode:
http://www.cs.auckland.ac.nz/~pgut001/pubs/authenticode.txt
* MS CAB SDK documentation
http://msdn.microsoft.com/library/default.asp?url=/library/en-us/dncabsdk/html/cabdl.asp
* MS PE/COFF documentation
http://www.microsoft.com/whdc/system/platform/firmware/PECOFF.mspx
* MS Windows Authenticode PE Signature Format
http://msdn.microsoft.com/en-US/windows/hardware/gg463183
(Although the part of how the actual checksumming is done is not
how it is done inside Windows. The end result is however the same
on all "normal" PE files.)
* tail -c, tcpdump, mimencode & openssl asn1parse :)
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#ifdef HAVE_WINDOWS_H
#define NOCRYPT
#define WIN32_LEAN_AND_MEAN
#include <windows.h>
typedef unsigned char u_char;
#endif
#include <stdio.h>
#include <stdlib.h>
#ifndef _WIN32
#include <unistd.h>
#endif
#include <string.h>
#include <time.h>
#include <ctype.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#ifndef _WIN32
#ifdef HAVE_SYS_MMAN_H
#include <sys/mman.h>
#endif
#ifdef HAVE_TERMIOS_H
#include <termios.h>
#endif
#endif
#ifdef WITH_GSF
#include <gsf/gsf-infile-msole.h>
#include <gsf/gsf-infile.h>
#include <gsf/gsf-input-stdio.h>
#include <gsf/gsf-outfile-msole.h>
#include <gsf/gsf-outfile.h>
#include <gsf/gsf-output-stdio.h>
#include <gsf/gsf-utils.h>
#endif
#include <openssl/err.h>
#include <openssl/objects.h>
#include <openssl/evp.h>
#include <openssl/x509.h>
#include <openssl/pkcs7.h>
#include <openssl/pkcs12.h>
#include <openssl/pem.h>
#include <openssl/asn1t.h>
#include <openssl/conf.h>
#ifndef OPENSSL_NO_ENGINE
#include <openssl/engine.h>
#endif
#ifdef ENABLE_CURL
#ifdef __CYGWIN__
#ifndef SOCKET
#define SOCKET UINT_PTR
#endif
#endif
#include <curl/curl.h>
#define MAX_TS_SERVERS 256
#endif
#ifndef FALSE
#define FALSE 0
#endif
#ifndef TRUE
#define TRUE 1
#endif
#if defined (HAVE_TERMIOS_H) || defined (HAVE_GETPASS)
#define PROVIDE_ASKPASS 1
#endif
/* MS Authenticode object ids */
#define SPC_INDIRECT_DATA_OBJID "1.3.6.1.4.1.311.2.1.4"
#define SPC_STATEMENT_TYPE_OBJID "1.3.6.1.4.1.311.2.1.11"
#define SPC_SP_OPUS_INFO_OBJID "1.3.6.1.4.1.311.2.1.12"
#define SPC_INDIVIDUAL_SP_KEY_PURPOSE_OBJID "1.3.6.1.4.1.311.2.1.21"
#define SPC_COMMERCIAL_SP_KEY_PURPOSE_OBJID "1.3.6.1.4.1.311.2.1.22"
#define SPC_MS_JAVA_SOMETHING "1.3.6.1.4.1.311.15.1"
#define SPC_PE_IMAGE_DATA_OBJID "1.3.6.1.4.1.311.2.1.15"
#define SPC_CAB_DATA_OBJID "1.3.6.1.4.1.311.2.1.25"
#define SPC_TIME_STAMP_REQUEST_OBJID "1.3.6.1.4.1.311.3.2.1"
#define SPC_SIPINFO_OBJID "1.3.6.1.4.1.311.2.1.30"
#define SPC_PE_IMAGE_PAGE_HASHES_V1 "1.3.6.1.4.1.311.2.3.1" /* Page hash using SHA1 */
#define SPC_PE_IMAGE_PAGE_HASHES_V2 "1.3.6.1.4.1.311.2.3.2" /* Page hash using SHA256 */
#define SPC_NESTED_SIGNATURE_OBJID "1.3.6.1.4.1.311.2.4.1"
#define SPC_RFC3161_OBJID "1.3.6.1.4.1.311.3.3.1"
/* 1.3.6.1.4.1.311.4... MS Crypto 2.0 stuff... */
#define WIN_CERT_REVISION_2 0x0200
#define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002
/*
ASN.1 definitions (more or less from official MS Authenticode docs)
*/
typedef struct {
int type;
union {
ASN1_BMPSTRING *unicode;
ASN1_IA5STRING *ascii;
} value;
} SpcString;
DECLARE_ASN1_FUNCTIONS(SpcString)
ASN1_CHOICE(SpcString) = {
ASN1_IMP_OPT(SpcString, value.unicode, ASN1_BMPSTRING , 0),
ASN1_IMP_OPT(SpcString, value.ascii, ASN1_IA5STRING, 1)
} ASN1_CHOICE_END(SpcString)
IMPLEMENT_ASN1_FUNCTIONS(SpcString)
typedef struct {
ASN1_OCTET_STRING *classId;
ASN1_OCTET_STRING *serializedData;
} SpcSerializedObject;
DECLARE_ASN1_FUNCTIONS(SpcSerializedObject)
ASN1_SEQUENCE(SpcSerializedObject) = {
ASN1_SIMPLE(SpcSerializedObject, classId, ASN1_OCTET_STRING),
ASN1_SIMPLE(SpcSerializedObject, serializedData, ASN1_OCTET_STRING)
} ASN1_SEQUENCE_END(SpcSerializedObject)
IMPLEMENT_ASN1_FUNCTIONS(SpcSerializedObject)
typedef struct {
int type;
union {
ASN1_IA5STRING *url;
SpcSerializedObject *moniker;
SpcString *file;
} value;
} SpcLink;
DECLARE_ASN1_FUNCTIONS(SpcLink)
ASN1_CHOICE(SpcLink) = {
ASN1_IMP_OPT(SpcLink, value.url, ASN1_IA5STRING, 0),
ASN1_IMP_OPT(SpcLink, value.moniker, SpcSerializedObject, 1),
ASN1_EXP_OPT(SpcLink, value.file, SpcString, 2)
} ASN1_CHOICE_END(SpcLink)
IMPLEMENT_ASN1_FUNCTIONS(SpcLink)
typedef struct {
SpcString *programName;
SpcLink *moreInfo;
} SpcSpOpusInfo;
DECLARE_ASN1_FUNCTIONS(SpcSpOpusInfo)
ASN1_SEQUENCE(SpcSpOpusInfo) = {
ASN1_EXP_OPT(SpcSpOpusInfo, programName, SpcString, 0),
ASN1_EXP_OPT(SpcSpOpusInfo, moreInfo, SpcLink, 1)
} ASN1_SEQUENCE_END(SpcSpOpusInfo)
IMPLEMENT_ASN1_FUNCTIONS(SpcSpOpusInfo)
typedef struct {
ASN1_OBJECT *type;
ASN1_TYPE *value;
} SpcAttributeTypeAndOptionalValue;
DECLARE_ASN1_FUNCTIONS(SpcAttributeTypeAndOptionalValue)
ASN1_SEQUENCE(SpcAttributeTypeAndOptionalValue) = {
ASN1_SIMPLE(SpcAttributeTypeAndOptionalValue, type, ASN1_OBJECT),
ASN1_OPT(SpcAttributeTypeAndOptionalValue, value, ASN1_ANY)
} ASN1_SEQUENCE_END(SpcAttributeTypeAndOptionalValue)
IMPLEMENT_ASN1_FUNCTIONS(SpcAttributeTypeAndOptionalValue)
typedef struct {
ASN1_OBJECT *algorithm;
ASN1_TYPE *parameters;
} AlgorithmIdentifier;
DECLARE_ASN1_FUNCTIONS(AlgorithmIdentifier)
ASN1_SEQUENCE(AlgorithmIdentifier) = {
ASN1_SIMPLE(AlgorithmIdentifier, algorithm, ASN1_OBJECT),
ASN1_OPT(AlgorithmIdentifier, parameters, ASN1_ANY)
} ASN1_SEQUENCE_END(AlgorithmIdentifier)
IMPLEMENT_ASN1_FUNCTIONS(AlgorithmIdentifier)
typedef struct {
AlgorithmIdentifier *digestAlgorithm;
ASN1_OCTET_STRING *digest;
} DigestInfo;
DECLARE_ASN1_FUNCTIONS(DigestInfo)
ASN1_SEQUENCE(DigestInfo) = {
ASN1_SIMPLE(DigestInfo, digestAlgorithm, AlgorithmIdentifier),
ASN1_SIMPLE(DigestInfo, digest, ASN1_OCTET_STRING)
} ASN1_SEQUENCE_END(DigestInfo)
IMPLEMENT_ASN1_FUNCTIONS(DigestInfo)
typedef struct {
SpcAttributeTypeAndOptionalValue *data;
DigestInfo *messageDigest;
} SpcIndirectDataContent;
DECLARE_ASN1_FUNCTIONS(SpcIndirectDataContent)
ASN1_SEQUENCE(SpcIndirectDataContent) = {
ASN1_SIMPLE(SpcIndirectDataContent, data, SpcAttributeTypeAndOptionalValue),
ASN1_SIMPLE(SpcIndirectDataContent, messageDigest, DigestInfo)
} ASN1_SEQUENCE_END(SpcIndirectDataContent)
IMPLEMENT_ASN1_FUNCTIONS(SpcIndirectDataContent)
typedef struct {
ASN1_BIT_STRING* flags;
SpcLink *file;
} SpcPeImageData;
DECLARE_ASN1_FUNCTIONS(SpcPeImageData)
ASN1_SEQUENCE(SpcPeImageData) = {
ASN1_SIMPLE(SpcPeImageData, flags, ASN1_BIT_STRING),
ASN1_EXP_OPT(SpcPeImageData, file, SpcLink, 0)
} ASN1_SEQUENCE_END(SpcPeImageData)
IMPLEMENT_ASN1_FUNCTIONS(SpcPeImageData)
typedef struct {
ASN1_INTEGER *a;
ASN1_OCTET_STRING *string;
ASN1_INTEGER *b;
ASN1_INTEGER *c;
ASN1_INTEGER *d;
ASN1_INTEGER *e;
ASN1_INTEGER *f;
} SpcSipInfo;
DECLARE_ASN1_FUNCTIONS(SpcSipInfo)
ASN1_SEQUENCE(SpcSipInfo) = {
ASN1_SIMPLE(SpcSipInfo, a, ASN1_INTEGER),
ASN1_SIMPLE(SpcSipInfo, string, ASN1_OCTET_STRING),
ASN1_SIMPLE(SpcSipInfo, b, ASN1_INTEGER),
ASN1_SIMPLE(SpcSipInfo, c, ASN1_INTEGER),
ASN1_SIMPLE(SpcSipInfo, d, ASN1_INTEGER),
ASN1_SIMPLE(SpcSipInfo, e, ASN1_INTEGER),
ASN1_SIMPLE(SpcSipInfo, f, ASN1_INTEGER),
} ASN1_SEQUENCE_END(SpcSipInfo)
IMPLEMENT_ASN1_FUNCTIONS(SpcSipInfo)
#ifdef ENABLE_CURL
typedef struct {
ASN1_OBJECT *type;
ASN1_OCTET_STRING *signature;
} TimeStampRequestBlob;
DECLARE_ASN1_FUNCTIONS(TimeStampRequestBlob)
ASN1_SEQUENCE(TimeStampRequestBlob) = {
ASN1_SIMPLE(TimeStampRequestBlob, type, ASN1_OBJECT),
ASN1_EXP_OPT(TimeStampRequestBlob, signature, ASN1_OCTET_STRING, 0)
} ASN1_SEQUENCE_END(TimeStampRequestBlob)
IMPLEMENT_ASN1_FUNCTIONS(TimeStampRequestBlob)
typedef struct {
ASN1_OBJECT *type;
TimeStampRequestBlob *blob;
} TimeStampRequest;
DECLARE_ASN1_FUNCTIONS(TimeStampRequest)
ASN1_SEQUENCE(TimeStampRequest) = {
ASN1_SIMPLE(TimeStampRequest, type, ASN1_OBJECT),
ASN1_SIMPLE(TimeStampRequest, blob, TimeStampRequestBlob)
} ASN1_SEQUENCE_END(TimeStampRequest)
IMPLEMENT_ASN1_FUNCTIONS(TimeStampRequest)
/* RFC3161 Time stamping */
typedef struct {
ASN1_INTEGER *status;
STACK_OF(ASN1_UTF8STRING) *statusString;
ASN1_BIT_STRING *failInfo;
} PKIStatusInfo;
DECLARE_ASN1_FUNCTIONS(PKIStatusInfo)
ASN1_SEQUENCE(PKIStatusInfo) = {
ASN1_SIMPLE(PKIStatusInfo, status, ASN1_INTEGER),
ASN1_SEQUENCE_OF_OPT(PKIStatusInfo, statusString, ASN1_UTF8STRING),
ASN1_OPT(PKIStatusInfo, failInfo, ASN1_BIT_STRING)
} ASN1_SEQUENCE_END(PKIStatusInfo)
IMPLEMENT_ASN1_FUNCTIONS(PKIStatusInfo)
typedef struct {
PKIStatusInfo *status;
PKCS7 *token;
} TimeStampResp;
DECLARE_ASN1_FUNCTIONS(TimeStampResp)
ASN1_SEQUENCE(TimeStampResp) = {
ASN1_SIMPLE(TimeStampResp, status, PKIStatusInfo),
ASN1_OPT(TimeStampResp, token, PKCS7)
} ASN1_SEQUENCE_END(TimeStampResp)
IMPLEMENT_ASN1_FUNCTIONS(TimeStampResp)
typedef struct {
AlgorithmIdentifier *digestAlgorithm;
ASN1_OCTET_STRING *digest;
} MessageImprint;
DECLARE_ASN1_FUNCTIONS(MessageImprint)
ASN1_SEQUENCE(MessageImprint) = {
ASN1_SIMPLE(MessageImprint, digestAlgorithm, AlgorithmIdentifier),
ASN1_SIMPLE(MessageImprint, digest, ASN1_OCTET_STRING)
} ASN1_SEQUENCE_END(MessageImprint)
IMPLEMENT_ASN1_FUNCTIONS(MessageImprint)
typedef struct {
ASN1_INTEGER *version;
MessageImprint *messageImprint;
ASN1_OBJECT *reqPolicy;
ASN1_INTEGER *nonce;
ASN1_BOOLEAN *certReq;
STACK_OF(X509_EXTENSION) *extensions;
} TimeStampReq;
DECLARE_ASN1_FUNCTIONS(TimeStampReq)
ASN1_SEQUENCE(TimeStampReq) = {
ASN1_SIMPLE(TimeStampReq, version, ASN1_INTEGER),
ASN1_SIMPLE(TimeStampReq, messageImprint, MessageImprint),
ASN1_OPT (TimeStampReq, reqPolicy, ASN1_OBJECT),
ASN1_OPT (TimeStampReq, nonce, ASN1_INTEGER),
ASN1_SIMPLE(TimeStampReq, certReq, ASN1_BOOLEAN),
ASN1_IMP_SEQUENCE_OF_OPT(TimeStampReq, extensions, X509_EXTENSION, 0)
} ASN1_SEQUENCE_END(TimeStampReq)
IMPLEMENT_ASN1_FUNCTIONS(TimeStampReq)
#endif /* ENABLE_CURL */
static SpcSpOpusInfo* createOpus(const char *desc, const char *url)
{
SpcSpOpusInfo *info = SpcSpOpusInfo_new();
if (desc) {
info->programName = SpcString_new();
info->programName->type = 1;
info->programName->value.ascii = M_ASN1_IA5STRING_new();
ASN1_STRING_set((ASN1_STRING *)info->programName->value.ascii,
(const unsigned char*)desc, strlen(desc));
}
if (url) {
info->moreInfo = SpcLink_new();
info->moreInfo->type = 0;
info->moreInfo->value.url = M_ASN1_IA5STRING_new();
ASN1_STRING_set((ASN1_STRING *)info->moreInfo->value.url,
(const unsigned char*)url, strlen(url));
}
return info;
}
static unsigned int asn1_simple_hdr_len(const unsigned char *p, unsigned int len) {
if (len <= 2 || p[0] > 0x31)
return 0;
return (p[1]&0x80) ? (2 + (p[1]&0x7f)) : 2;
}
static void tohex(const unsigned char *v, unsigned char *b, int len)
{
int i;
for(i=0; i<len; i++)
sprintf((char*)b+i*2, "%02X", v[i]);
b[i*2] = 0x00;
}
static int add_unauthenticated_blob(PKCS7 *sig)
{
u_char *p = NULL;
int len = 1024+4;
char prefix[] = "\x0c\x82\x04\x00---BEGIN_BLOB---"; // Length data for ASN1 attribute plus prefix
char postfix[] = "---END_BLOB---";
PKCS7_SIGNER_INFO *si = sk_PKCS7_SIGNER_INFO_value(sig->d.sign->signer_info, 0);
p = OPENSSL_malloc(len);
memset(p, 0, len);
memcpy(p, prefix, sizeof(prefix));
memcpy(p+len-sizeof(postfix), postfix, sizeof(postfix));
ASN1_STRING *astr = ASN1_STRING_new();
ASN1_STRING_set(astr, p, len);
int nid = OBJ_create("1.3.6.1.4.1.42921.1.2.1",
"unauthenticatedData",
"unauthenticatedData");
PKCS7_add_attribute (si, nid, V_ASN1_SEQUENCE, astr);
OPENSSL_free(p);
return 0;
}
static int g_verbose = 0;
#ifdef ENABLE_CURL
static int blob_has_nl = 0;
static size_t curl_write( void *ptr, size_t sz, size_t nmemb, void *stream)
{
if (sz*nmemb > 0 && !blob_has_nl) {
if (memchr(ptr, '\n', sz*nmemb))
blob_has_nl = 1;
}
return BIO_write((BIO*)stream, ptr, sz*nmemb);
}
static void print_timestamp_error(const char *url, long http_code)
{
if (http_code != -1) {
fprintf(stderr, "Failed to convert timestamp reply from %s; "
"HTTP status %ld\n", url, http_code);
} else {
fprintf(stderr, "Failed to convert timestamp reply from %s; "
"no HTTP status available", url);
}
ERR_print_errors_fp(stderr);
}
/*
A timestamp request looks like this:
POST <someurl> HTTP/1.1
Content-Type: application/octet-stream
Content-Length: ...
Accept: application/octet-stream
User-Agent: Transport
Host: ...
Cache-Control: no-cache
<base64encoded blob>
.. and the blob has the following ASN1 structure:
0:d=0 hl=4 l= 291 cons: SEQUENCE
4:d=1 hl=2 l= 10 prim: OBJECT :1.3.6.1.4.1.311.3.2.1
16:d=1 hl=4 l= 275 cons: SEQUENCE
20:d=2 hl=2 l= 9 prim: OBJECT :pkcs7-data
31:d=2 hl=4 l= 260 cons: cont [ 0 ]
35:d=3 hl=4 l= 256 prim: OCTET STRING
<signature>
.. and it returns a base64 encoded PKCS#7 structure.
*/
static int add_timestamp(PKCS7 *sig, char *url, char *proxy, int rfc3161, const EVP_MD *md, int verbose, int noverifypeer)
{
CURL *curl;
struct curl_slist *slist = NULL;
CURLcode c;
BIO *bout, *bin, *b64;
u_char *p = NULL;
int len = 0;
PKCS7_SIGNER_INFO *si =
sk_PKCS7_SIGNER_INFO_value
(sig->d.sign->signer_info, 0);
if (!url) return -1;
curl = curl_easy_init();
if (proxy) {
curl_easy_setopt(curl, CURLOPT_PROXY, proxy);
if (!strncmp("http:", proxy, 5))
curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_HTTP);
if (!strncmp("socks:", proxy, 6))
curl_easy_setopt(curl, CURLOPT_PROXYTYPE, CURLPROXY_SOCKS5);
}
curl_easy_setopt(curl, CURLOPT_URL, url);
/* curl_easy_setopt(curl, CURLOPT_VERBOSE, 42); */
if (noverifypeer)
curl_easy_setopt(curl, CURLOPT_SSL_VERIFYPEER, FALSE);
if (rfc3161) {
slist = curl_slist_append(slist, "Content-Type: application/timestamp-query");
slist = curl_slist_append(slist, "Accept: application/timestamp-reply");
} else {
slist = curl_slist_append(slist, "Content-Type: application/octet-stream");
slist = curl_slist_append(slist, "Accept: application/octet-stream");
}
slist = curl_slist_append(slist, "User-Agent: Transport");
slist = curl_slist_append(slist, "Cache-Control: no-cache");
curl_easy_setopt(curl, CURLOPT_HTTPHEADER, slist);
if (rfc3161) {
unsigned char mdbuf[EVP_MAX_MD_SIZE];
EVP_MD_CTX mdctx;
EVP_MD_CTX_init(&mdctx);
EVP_DigestInit(&mdctx, md);
EVP_DigestUpdate(&mdctx, si->enc_digest->data, si->enc_digest->length);
EVP_DigestFinal(&mdctx, mdbuf, NULL);
TimeStampReq *req = TimeStampReq_new();
ASN1_INTEGER_set(req->version, 1);
req->messageImprint->digestAlgorithm->algorithm = OBJ_nid2obj(EVP_MD_nid(md));
req->messageImprint->digestAlgorithm->parameters = ASN1_TYPE_new();
req->messageImprint->digestAlgorithm->parameters->type = V_ASN1_NULL;
M_ASN1_OCTET_STRING_set(req->messageImprint->digest, mdbuf, EVP_MD_size(md));
req->certReq = (void*)0x1;
len = i2d_TimeStampReq(req, NULL);
p = OPENSSL_malloc(len);
len = i2d_TimeStampReq(req, &p);
p -= len;
TimeStampReq_free(req);
} else {
TimeStampRequest *req = TimeStampRequest_new();
req->type = OBJ_txt2obj(SPC_TIME_STAMP_REQUEST_OBJID, 1);
req->blob->type = OBJ_nid2obj(NID_pkcs7_data);
req->blob->signature = si->enc_digest;
len = i2d_TimeStampRequest(req, NULL);
p = OPENSSL_malloc(len);
len = i2d_TimeStampRequest(req, &p);
p -= len;
req->blob->signature = NULL;
TimeStampRequest_free(req);
}
bout = BIO_new(BIO_s_mem());
if (!rfc3161) {
b64 = BIO_new(BIO_f_base64());
bout = BIO_push(b64, bout);
}
BIO_write(bout, p, len);
(void)BIO_flush(bout);
OPENSSL_free(p);
p = NULL;
len = BIO_get_mem_data(bout, &p);
curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, len);
curl_easy_setopt(curl, CURLOPT_POSTFIELDS, (char*)p);
bin = BIO_new(BIO_s_mem());
BIO_set_mem_eof_return(bin, 0);
curl_easy_setopt(curl, CURLOPT_POST, 1);
curl_easy_setopt(curl, CURLOPT_WRITEDATA, bin);
curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, curl_write);
c = curl_easy_perform(curl);
curl_slist_free_all(slist);
BIO_free_all(bout);
if (c) {
BIO_free_all(bin);
if (verbose)
fprintf(stderr, "CURL failure: %s\n", curl_easy_strerror(c));
} else {
(void)BIO_flush(bin);
long http_code = -1;
curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &http_code);
/*
* At this point we could also look at the response body (and perhaps
* log it if we fail to decode the response):
*
* char *resp_body = NULL;
* long resp_body_len = BIO_get_mem_data(bin, &resp_body);
*/
if (rfc3161) {
TimeStampResp *reply;
(void)BIO_flush(bin);
reply = ASN1_item_d2i_bio(ASN1_ITEM_rptr(TimeStampResp), bin, NULL);
BIO_free_all(bin);
if (!reply) {
if (verbose)
print_timestamp_error(url, http_code);
return -1;
}
if (ASN1_INTEGER_get(reply->status->status) != 0) {
if (verbose)
fprintf(stderr, "Timestamping failed: %ld\n", ASN1_INTEGER_get(reply->status->status));
TimeStampResp_free(reply);
return -1;
}
if (((len = i2d_PKCS7(reply->token, NULL)) <= 0) ||
(p = OPENSSL_malloc(len)) == NULL) {
if (verbose) {
fprintf(stderr, "Failed to convert pkcs7: %d\n", len);
ERR_print_errors_fp(stderr);
}
TimeStampResp_free(reply);
return -1;
}
len = i2d_PKCS7(reply->token, &p);
p -= len;
TimeStampResp_free(reply);
STACK_OF(X509_ATTRIBUTE) *attrs = sk_X509_ATTRIBUTE_new_null();
attrs = X509at_add1_attr_by_txt
(&attrs, SPC_RFC3161_OBJID, V_ASN1_SET, p, len);
OPENSSL_free(p);
PKCS7_set_attributes(si, attrs);
sk_X509_ATTRIBUTE_pop_free(attrs, X509_ATTRIBUTE_free);
} else {
int i;
PKCS7 *p7;
PKCS7_SIGNER_INFO *info;
ASN1_STRING *astr;
BIO* b64_bin;
b64 = BIO_new(BIO_f_base64());
if (!blob_has_nl)
BIO_set_flags(b64, BIO_FLAGS_BASE64_NO_NL);
b64_bin = BIO_push(b64, bin);
p7 = d2i_PKCS7_bio(b64_bin, NULL);
if (p7 == NULL) {
BIO_free_all(b64_bin);
if (verbose)
print_timestamp_error(url, http_code);
return -1;
}
BIO_free_all(b64_bin);
for(i = sk_X509_num(p7->d.sign->cert)-1; i>=0; i--)
PKCS7_add_certificate(sig, sk_X509_value(p7->d.sign->cert, i));
info = sk_PKCS7_SIGNER_INFO_value(p7->d.sign->signer_info, 0);
if (((len = i2d_PKCS7_SIGNER_INFO(info, NULL)) <= 0) ||
(p = OPENSSL_malloc(len)) == NULL) {
if (verbose) {
fprintf(stderr, "Failed to convert signer info: %d\n", len);
ERR_print_errors_fp(stderr);
}
PKCS7_free(p7);
return -1;
}
len = i2d_PKCS7_SIGNER_INFO(info, &p);
p -= len;
astr = ASN1_STRING_new();
ASN1_STRING_set(astr, p, len);
OPENSSL_free(p);
PKCS7_add_attribute
(si, NID_pkcs9_countersignature,
V_ASN1_SEQUENCE, astr);
PKCS7_free(p7);
}
}
curl_easy_cleanup(curl);
curl_global_cleanup();
return (int)c;
}
static int add_timestamp_authenticode(PKCS7 *sig, char **url, int nurls, char *proxy, int noverifypeer)
{
int i;
for (i=0; i<nurls; i++) {
int res = add_timestamp(sig, url[i], proxy, 0, NULL, g_verbose || nurls == 1, noverifypeer);
if (!res) return 0;
}
return -1;
}
static int add_timestamp_rfc3161(PKCS7 *sig, char **url, int nurls, char *proxy, const EVP_MD *md, int noverifypeer)
{
int i;
for (i=0; i<nurls; i++) {
int res = add_timestamp(sig, url[i], proxy, 1, md, g_verbose || nurls == 1, noverifypeer);
if (!res) return 0;
}
return -1;
}
#endif /* ENABLE_CURL */
#ifdef WITH_GSF
static int gsf_initialized = 0;
#endif
static void cleanup_lib_state(void)
{
#ifdef WITH_GSF
if (gsf_initialized)
gsf_shutdown();
#endif
OBJ_cleanup();
#ifndef OPENSSL_NO_ENGINE
ENGINE_cleanup();
#endif
EVP_cleanup();
CONF_modules_free();
CRYPTO_cleanup_all_ex_data();
#if OPENSSL_VERSION_NUMBER > 0x10000000
ERR_remove_thread_state(NULL);
#endif
ERR_free_strings();
}
static void usage(const char *argv0)
{
fprintf(stderr,
"Usage: %s\n\n\t[ --version | -v ]\n\n"
"\t[ sign ]\n"
"\t\t( -certs <certfile> -key <keyfile> | -pkcs12 <pkcs12file> |\n"
"\t\t -pkcs11engine <engine> -pkcs11module <module> -certs <certfile> -key <pkcs11 key id>)\n"
"\t\t[ -pass <password> ] "
#ifdef PROVIDE_ASKPASS
"[ -askpass ]"
#endif
"[ -readpass <file> ]\n"
"\t\t[ -ac <crosscertfile> ]\n"
"\t\t[ -h {md5,sha1,sha2(56),sha384,sha512} ]\n"
"\t\t[ -n <desc> ] [ -i <url> ] [ -jp <level> ] [ -comm ]\n"
"\t\t[ -ph ]\n"
#ifdef ENABLE_CURL
"\t\t[ -t <timestampurl> [ -t ... ] [ -p <proxy> ] [ -noverifypeer ] ]\n"
"\t\t[ -ts <timestampurl> [ -ts ... ] [ -p <proxy> ] [ -noverifypeer ] ]\n"
#endif
"\t\t[ -addUnauthenticatedBlob ]\n\n"
"\t\t[ -nest ]\n\n"
"\t\t[ -verbose ]\n\n"
"\t\tMSI specific:\n"
"\t\t[ -add-msi-dse ]\n\n"
"\t\t[ -in ] <infile> [-out ] <outfile>\n\n"
"\textract-signature [ -pem ] [ -in ] <infile> [ -out ] <outfile>\n\n"
"\tremove-signature [ -in ] <infile> [ -out ] <outfile>\n\n"
"\tattach-signature [ -sigin ] <sigfile> [ -in ] <infile> [ -out ] <outfile>\n\n"
"\tverify [ -in ] <infile>\n"
"\t\t[ -require-leaf-hash {md5,sha1,sha2(56),sha384,sha512}:XXXXXXXXXXXX... ]\n\n"
"\tadd [-addUnauthenticatedBlob] [ -in ] <infile> [ -out ] <outfile>\n"
#ifdef ENABLE_CURL
"\t\t[ -t <timestampurl> [ -t ... ] [ -p <proxy> ] [ -noverifypeer ] ]\n"
"\t\t[ -ts <timestampurl> [ -ts ... ] [ -p <proxy> ] [ -noverifypeer ] ]\n"
#endif
"\n"
"",
argv0);
cleanup_lib_state();
exit(-1);
}
#define DO_EXIT_0(x) { fputs(x, stderr); goto err_cleanup; }
#define DO_EXIT_1(x, y) { fprintf(stderr, x, y); goto err_cleanup; }
#define DO_EXIT_2(x, y, z) { fprintf(stderr, x, y, z); goto err_cleanup; }
#define GET_UINT16_LE(p) (((u_char*)(p))[0] | (((u_char*)(p))[1]<<8))
#define GET_UINT32_LE(p) (((u_char*)(p))[0] | (((u_char*)(p))[1]<<8) | \
(((u_char*)(p))[2]<<16) | (((u_char*)(p))[3]<<24))
#define PUT_UINT16_LE(i,p) \
((u_char*)(p))[0] = (i) & 0xff; \
((u_char*)(p))[1] = ((i)>>8) & 0xff
#define PUT_UINT32_LE(i,p) \
((u_char*)(p))[0] = (i) & 0xff; \
((u_char*)(p))[1] = ((i)>>8) & 0xff; \
((u_char*)(p))[2] = ((i)>>16) & 0xff; \
((u_char*)(p))[3] = ((i)>>24) & 0xff
typedef enum {
FILE_TYPE_CAB,
FILE_TYPE_PE,
FILE_TYPE_MSI,
} file_type_t;
typedef enum {
CMD_SIGN,
CMD_EXTRACT,
CMD_REMOVE,
CMD_VERIFY,
CMD_ADD,
CMD_ATTACH,
} cmd_type_t;
static SpcLink *get_obsolete_link(void)
{
static const unsigned char obsolete[] = {
0x00, 0x3c, 0x00, 0x3c, 0x00, 0x3c, 0x00, 0x4f, 0x00, 0x62,
0x00, 0x73, 0x00, 0x6f, 0x00, 0x6c, 0x00, 0x65, 0x00, 0x74,
0x00, 0x65, 0x00, 0x3e, 0x00, 0x3e, 0x00, 0x3e
};
SpcLink *link = SpcLink_new();
link->type = 2;
link->value.file = SpcString_new();
link->value.file->type = 0;
link->value.file->value.unicode = ASN1_BMPSTRING_new();
ASN1_STRING_set(link->value.file->value.unicode, obsolete, sizeof(obsolete));
return link;
}
static const unsigned char classid_page_hash[] = {
0xA6, 0xB5, 0x86, 0xD5, 0xB4, 0xA1, 0x24, 0x66,
0xAE, 0x05, 0xA2, 0x17, 0xDA, 0x8E, 0x60, 0xD6
};
static unsigned char *calc_page_hash(char *indata, unsigned int peheader, int pe32plus,
unsigned int sigpos, int phtype, unsigned int *phlen);
DECLARE_STACK_OF(ASN1_OCTET_STRING)
#ifndef sk_ASN1_OCTET_STRING_new_null
#define sk_ASN1_OCTET_STRING_new_null() SKM_sk_new_null(ASN1_OCTET_STRING)
#define sk_ASN1_OCTET_STRING_free(st) SKM_sk_free(ASN1_OCTET_STRING, (st))
#define sk_ASN1_OCTET_STRING_push(st, val) SKM_sk_push(ASN1_OCTET_STRING, (st), (val))
#define i2d_ASN1_SET_OF_ASN1_OCTET_STRING(st, pp, i2d_func, ex_tag, ex_class, is_set) \
SKM_ASN1_SET_OF_i2d(ASN1_OCTET_STRING, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set))
#endif
DECLARE_STACK_OF(SpcAttributeTypeAndOptionalValue)
#ifndef sk_SpcAttributeTypeAndOptionalValue_new_null
#define sk_SpcAttributeTypeAndOptionalValue_new_null() SKM_sk_new_null(SpcAttributeTypeAndOptionalValue)
#define sk_SpcAttributeTypeAndOptionalValue_free(st) SKM_sk_free(SpcAttributeTypeAndOptionalValue, (st))
#define sk_SpcAttributeTypeAndOptionalValue_push(st, val) SKM_sk_push(SpcAttributeTypeAndOptionalValue, (st), (val))
#define i2d_SpcAttributeTypeAndOptionalValue(st, pp, i2d_func, ex_tag, ex_class, is_set) \
SKM_ASN1_SET_OF_i2d(SpcAttributeTypeAndOptionalValue, (st), (pp), (i2d_func), (ex_tag), (ex_class), (is_set))
#endif
static SpcLink *get_page_hash_link(int phtype, char *indata, unsigned int peheader, int pe32plus, unsigned int sigpos)
{
unsigned int phlen;
unsigned char *ph = calc_page_hash(indata, peheader, pe32plus, sigpos, phtype, &phlen);
if (!ph) {
fprintf(stderr, "Failed to calculate page hash\n");
exit(-1);
}
ASN1_OCTET_STRING *ostr = M_ASN1_OCTET_STRING_new();
M_ASN1_OCTET_STRING_set(ostr, ph, phlen);
free(ph);
STACK_OF(ASN1_OCTET_STRING) *oset = sk_ASN1_OCTET_STRING_new_null();
sk_ASN1_OCTET_STRING_push(oset, ostr);
unsigned char *p, *tmp;
unsigned int l;
l = i2d_ASN1_SET_OF_ASN1_OCTET_STRING(oset, NULL, i2d_ASN1_OCTET_STRING,
V_ASN1_SET, V_ASN1_UNIVERSAL, IS_SET);
tmp = p = OPENSSL_malloc(l);
i2d_ASN1_SET_OF_ASN1_OCTET_STRING(oset, &tmp, i2d_ASN1_OCTET_STRING,
V_ASN1_SET, V_ASN1_UNIVERSAL, IS_SET);
ASN1_OCTET_STRING_free(ostr);
sk_ASN1_OCTET_STRING_free(oset);
SpcAttributeTypeAndOptionalValue *aval = SpcAttributeTypeAndOptionalValue_new();
aval->type = OBJ_txt2obj((phtype == NID_sha1) ? SPC_PE_IMAGE_PAGE_HASHES_V1 : SPC_PE_IMAGE_PAGE_HASHES_V2, 1);
aval->value = ASN1_TYPE_new();
aval->value->type = V_ASN1_SET;
aval->value->value.set = ASN1_STRING_new();
ASN1_STRING_set(aval->value->value.set, p, l);
OPENSSL_free(p);
STACK_OF(SpcAttributeTypeAndOptionalValue) *aset = sk_SpcAttributeTypeAndOptionalValue_new_null();
sk_SpcAttributeTypeAndOptionalValue_push(aset, aval);
l = i2d_SpcAttributeTypeAndOptionalValue(aset, NULL, i2d_SpcAttributeTypeAndOptionalValue,
V_ASN1_SET, V_ASN1_UNIVERSAL, IS_SET);
tmp = p = OPENSSL_malloc(l);
l = i2d_SpcAttributeTypeAndOptionalValue(aset, &tmp, i2d_SpcAttributeTypeAndOptionalValue,
V_ASN1_SET, V_ASN1_UNIVERSAL, IS_SET);
sk_SpcAttributeTypeAndOptionalValue_free(aset);
SpcAttributeTypeAndOptionalValue_free(aval);
SpcSerializedObject *so = SpcSerializedObject_new();
M_ASN1_OCTET_STRING_set(so->classId, classid_page_hash, sizeof(classid_page_hash));
M_ASN1_OCTET_STRING_set(so->serializedData, p, l);
OPENSSL_free(p);
SpcLink *link = SpcLink_new();
link->type = 1;
link->value.moniker = so;
return link;
}
static void get_indirect_data_blob(u_char **blob, int *len, const EVP_MD *md, file_type_t type,
int pagehash, char *indata, unsigned int peheader, int pe32plus,
unsigned int sigpos)
{
static const unsigned char msistr[] = {
0xf1, 0x10, 0x0c, 0x00, 0x00, 0x00, 0x00, 0x00,
0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x46
};
u_char *p;
int hashlen, l;
void *hash;
ASN1_OBJECT *dtype;
SpcIndirectDataContent *idc = SpcIndirectDataContent_new();
idc->data->value = ASN1_TYPE_new();
idc->data->value->type = V_ASN1_SEQUENCE;
idc->data->value->value.sequence = ASN1_STRING_new();
if (type == FILE_TYPE_CAB) {
SpcLink *link = get_obsolete_link();
l = i2d_SpcLink(link, NULL);
p = OPENSSL_malloc(l);
i2d_SpcLink(link, &p);
p -= l;
dtype = OBJ_txt2obj(SPC_CAB_DATA_OBJID, 1);
SpcLink_free(link);
} else if (type == FILE_TYPE_PE) {
SpcPeImageData *pid = SpcPeImageData_new();
ASN1_BIT_STRING_set(pid->flags, (unsigned char*)"0", 0);
if (pagehash) {
int phtype = NID_sha1;
if (EVP_MD_size(md) > EVP_MD_size(EVP_sha1()))
phtype = NID_sha256;
pid->file = get_page_hash_link(phtype, indata, peheader, pe32plus, sigpos);
} else {
pid->file = get_obsolete_link();
}
l = i2d_SpcPeImageData(pid, NULL);
p = OPENSSL_malloc(l);
i2d_SpcPeImageData(pid, &p);
p -= l;
dtype = OBJ_txt2obj(SPC_PE_IMAGE_DATA_OBJID, 1);
SpcPeImageData_free(pid);
} else if (type == FILE_TYPE_MSI) {
SpcSipInfo *si = SpcSipInfo_new();
ASN1_INTEGER_set(si->a, 1);
ASN1_INTEGER_set(si->b, 0);
ASN1_INTEGER_set(si->c, 0);
ASN1_INTEGER_set(si->d, 0);
ASN1_INTEGER_set(si->e, 0);
ASN1_INTEGER_set(si->f, 0);
M_ASN1_OCTET_STRING_set(si->string, msistr, sizeof(msistr));
l = i2d_SpcSipInfo(si, NULL);
p = OPENSSL_malloc(l);
i2d_SpcSipInfo(si, &p);
p -= l;
dtype = OBJ_txt2obj(SPC_SIPINFO_OBJID, 1);
SpcSipInfo_free(si);
} else {
fprintf(stderr, "Unexpected file type: %d\n", type);
exit(1);
}
idc->data->type = dtype;
idc->data->value->value.sequence->data = p;
idc->data->value->value.sequence->length = l;
idc->messageDigest->digestAlgorithm->algorithm = OBJ_nid2obj(EVP_MD_nid(md));
idc->messageDigest->digestAlgorithm->parameters = ASN1_TYPE_new();
idc->messageDigest->digestAlgorithm->parameters->type = V_ASN1_NULL;
hashlen = EVP_MD_size(md);
hash = OPENSSL_malloc(hashlen);
memset(hash, 0, hashlen);
M_ASN1_OCTET_STRING_set(idc->messageDigest->digest, hash, hashlen);
OPENSSL_free(hash);
*len = i2d_SpcIndirectDataContent(idc, NULL);
*blob = OPENSSL_malloc(*len);
p = *blob;
i2d_SpcIndirectDataContent(idc, &p);
SpcIndirectDataContent_free(idc);
}
static unsigned int calc_pe_checksum(BIO *bio, unsigned int peheader)
{
unsigned int checkSum = 0;
unsigned short val;
unsigned int size = 0;
unsigned short *buf;
int nread;
/* recalc checksum. */
buf = (unsigned short*)malloc(sizeof(unsigned short)*32768);
(void)BIO_seek(bio, 0);
while ((nread = BIO_read(bio, buf, sizeof(unsigned short)*32768)) > 0) {
int i;
for (i = 0; i < nread / 2; i++) {
val = buf[i];
if (size == peheader + 88 || size == peheader + 90)
val = 0;
checkSum += val;
checkSum = 0xffff & (checkSum + (checkSum >> 0x10));
size += 2;
}
}
free(buf);
checkSum = 0xffff & (checkSum + (checkSum >> 0x10));
checkSum += size;
return checkSum;
}
static void recalc_pe_checksum(BIO *bio, unsigned int peheader)
{
unsigned int checkSum = calc_pe_checksum(bio, peheader);
char buf[4];
/* write back checksum. */
(void)BIO_seek(bio, peheader + 88);
PUT_UINT32_LE(checkSum, buf);
BIO_write(bio, buf, 4);
}
static unsigned char nib2val(unsigned char c) {
if (c >= '0' && c <= '9') {
return c - '0';
} else if (c >= 'a' && c <= 'f') {
return c - 'a' + 10;
} else if (c >= 'A' && c <= 'F') {
return c - 'A' + 10;
}
printf("Illegal hex value: '%x'\n", c);
return 0;
}
static int verify_leaf_hash(X509 *leaf, const char *leafhash) {
char *lhdup = NULL;
char *orig = NULL;
char *mdid = NULL;
char *hash = NULL;
int ret = 0;
lhdup = strdup(leafhash);
orig = lhdup;
mdid = lhdup;
while (lhdup != NULL && *lhdup != '\0') {
if (*lhdup == ':') {
*lhdup = '\0';
++lhdup;
hash = lhdup;
break;
}
++lhdup;
}
lhdup = orig;
if (hash == NULL) {
printf("Unable to parse -require-leaf-hash parameter: %s\n\n", orig);
ret = 1;
goto out;
}
const EVP_MD *md = EVP_get_digestbyname(mdid);
if (md == NULL) {
printf("Unable to lookup digest by name '%s'\n", mdid);
ret = 1;
goto out;
}
unsigned long sz = EVP_MD_size(md);
unsigned long actual = strlen(hash);
if (actual%2 != 0) {
printf("Hash length mismatch: length is uneven.\n");
ret = 1;
goto out;
}
actual /= 2;
if (actual != sz) {
printf("Hash length mismatch: '%s' digest must be %lu bytes long (got %lu bytes)\n", mdid, sz, actual);
ret = 1;
goto out;
}
unsigned char mdbuf[EVP_MAX_MD_SIZE];
unsigned char cmdbuf[EVP_MAX_MD_SIZE];
int i = 0, j = 0;
while (i < sz*2) {
unsigned char x;
x = nib2val(hash[i+1]);
x |= nib2val(hash[i]) << 4;
mdbuf[j] = x;
i += 2;
j += 1;
}
unsigned long certlen = i2d_X509(leaf, NULL);
unsigned char *certbuf = malloc(certlen);
unsigned char *tmp = certbuf;
i2d_X509(leaf, &tmp);
EVP_MD_CTX *ctx = EVP_MD_CTX_create();
EVP_DigestInit_ex(ctx, md, NULL);
EVP_DigestUpdate(ctx, certbuf, certlen);
EVP_DigestFinal_ex(ctx, cmdbuf, NULL);
EVP_MD_CTX_destroy(ctx);
free(certbuf);
if (memcmp(mdbuf, cmdbuf, EVP_MD_size(md))) {
ret = 1;
goto out;
}
out:
free(lhdup);
return ret;
}
// pkcs7_get_nested_signature exctracts a nested signature from p7.
// The caller is responsible for freeing the returned object.
//
// If has_sig is provided, it will be set to either 1 if there is a
// SPC_NESTED_SIGNATURE attribute in p7 at all or 0 if not.
// This allows has_sig to be used to distinguish two possible scenarios
// when the functon returns NULL: if has_sig is 1, it means d2i_PKCS7
// failed to decode the nested signature. However, if has_sig is 0, it
// simply means the given p7 does not have a nested signature.
static PKCS7 *pkcs7_get_nested_signature(PKCS7 *p7, int *has_sig) {
PKCS7 *ret = NULL;
PKCS7_SIGNER_INFO *si = sk_PKCS7_SIGNER_INFO_value(p7->d.sign->signer_info, 0);
ASN1_TYPE *nestedSignature = PKCS7_get_attribute(si, OBJ_txt2nid(SPC_NESTED_SIGNATURE_OBJID));
if (nestedSignature) {
ASN1_STRING *astr = nestedSignature->value.sequence;
const unsigned char *p = astr->data;
ret = d2i_PKCS7(NULL, &p, astr->length);
}
if (has_sig)
*has_sig = (nestedSignature != NULL);
return ret;
}
// pkcs7_set_nested_signature adds the p7nest signature to p7
// as a nested signature (SPC_NESTED_SIGNATURE).
static int pkcs7_set_nested_signature(PKCS7 *p7, PKCS7 *p7nest) {
u_char *p = NULL;
int len = 0;
if (((len = i2d_PKCS7(p7nest, NULL)) <= 0) ||
(p = OPENSSL_malloc(len)) == NULL)
return 0;
i2d_PKCS7(p7nest, &p);
p -= len;
ASN1_STRING *astr = ASN1_STRING_new();
ASN1_STRING_set(astr, p, len);
OPENSSL_free(p);
PKCS7_SIGNER_INFO *si = sk_PKCS7_SIGNER_INFO_value(p7->d.sign->signer_info, 0);
if (PKCS7_add_attribute(si, OBJ_txt2nid(SPC_NESTED_SIGNATURE_OBJID), V_ASN1_SEQUENCE, astr) == 0)
return 0;
return 1;
}
#ifdef WITH_GSF
static gint msi_base64_decode(gint x)
{
if (x < 10)
return x + '0';
if (x < (10 + 26))
return x - 10 + 'A';
if (x < (10 + 26 + 26))
return x - 10 - 26 + 'a';
if (x == (10 + 26 + 26))
return '.';
return 1;
}
static void msi_decode(const guint8 *in, gchar *out)
{
guint count = 0;
guint8 *q = (guint8 *)out;
/* utf-8 encoding of 0x4840 */
if (in[0] == 0xe4 && in[1] == 0xa1 && in[2] == 0x80)
in += 3;
while (*in) {
guint8 ch = *in;
if ((ch == 0xe3 && in[1] >= 0xa0) || (ch == 0xe4 && in[1] < 0xa0)) {
*q++ = msi_base64_decode(in[2] & 0x7f);
*q++ = msi_base64_decode(in[1] ^ 0xa0);
in += 3;
count += 2;
continue;
}
if (ch == 0xe4 && in[1] == 0xa0) {
*q++ = msi_base64_decode(in[2] & 0x7f);
in += 3;
count++;
continue;
}
*q++ = *in++;
if (ch >= 0xc1)
*q++ = *in++;
if (ch >= 0xe0)
*q++ = *in++;
if (ch >= 0xf0)
*q++ = *in++;
count++;
}
*q = 0;
}
/*
* Sorry if this code looks a bit silly, but that seems
* to be the best solution so far...
*/
static gint msi_cmp(gpointer a, gpointer b)
{
glong anc = 0, bnc = 0;
gchar *pa = (gchar*)g_utf8_to_utf16(a, -1, NULL, &anc, NULL);
gchar *pb = (gchar*)g_utf8_to_utf16(b, -1, NULL, &bnc, NULL);
gint diff;
diff = memcmp(pa, pb, MIN(2*anc, 2*bnc));
/* apparently the longer wins */
if (diff == 0)
return 2*anc > 2*bnc ? 1 : -1;
g_free(pa);
g_free(pb);
return diff;
}
/*
* msi_sorted_infile_children returns a sorted list of all
* of the children of the given infile. The children are
* sorted according to the msi_cmp.
*
* The returned list must be freed with g_slist_free.
*/
static GSList *msi_sorted_infile_children(GsfInfile *infile)
{
GSList *sorted = NULL;
gchar decoded[0x40];
int i;
for (i = 0; i < gsf_infile_num_children(infile); i++) {
GsfInput *child = gsf_infile_child_by_index(infile, i);
const guint8 *name = (const guint8*)gsf_input_name(child);
msi_decode(name, decoded);
if (!g_strcmp0(decoded, "\05DigitalSignature"))
continue;
if (!g_strcmp0(decoded, "\05MsiDigitalSignatureEx"))
continue;
sorted = g_slist_insert_sorted(sorted, (gpointer)name, (GCompareFunc)msi_cmp);
}
return sorted;
}
/*
* msi_prehash_utf16_name converts an UTF-8 representation of
* an MSI filename to its on-disk UTF-16 representation and
* writes it to the hash BIO. It is used when calculating the
* pre-hash used for MsiDigitalSignatureEx signatures in MSI files.
*/
static gboolean msi_prehash_utf16_name(gchar *name, BIO *hash)
{
glong chars_written = 0;
gchar *u16name = (gchar*)g_utf8_to_utf16(name, -1, NULL, &chars_written, NULL);
if (u16name == NULL) {
return FALSE;
}
BIO_write(hash, u16name, 2*chars_written);
g_free(u16name);
return TRUE;
}
/*
* msi_prehash calculates the pre-hash used for 'MsiDigitalSignatureEx'
* signatures in MSI files. The pre-hash hashes only metadata (file names,
* file sizes, creation times and modification times), whereas the basic
* 'DigitalSignature' MSI signature only hashes file content.
*
* The hash is written to the hash BIO.
*/
static gboolean msi_prehash(GsfInfile *infile, gchar *dirname, BIO *hash)
{
GSList *sorted = NULL;
guint8 classid[16];
guint8 zeroes[8];
memset(&zeroes, 0, sizeof(zeroes));
gsf_infile_msole_get_class_id(GSF_INFILE_MSOLE(infile), classid);
if (dirname != NULL) {
if (!msi_prehash_utf16_name(dirname, hash))
return FALSE;
}
BIO_write(hash, classid, sizeof(classid));
BIO_write(hash, zeroes, 4);
if (dirname != NULL) {
/*
* Creation time and modification time for the root directory.
* These are always zero. The ctime and mtime of the actual
* file itself takes precedence.
*/
BIO_write(hash, zeroes, 8); // ctime as Windows FILETIME.
BIO_write(hash, zeroes, 8); // mtime as Windows FILETIME.
}
sorted = msi_sorted_infile_children(infile);
for (; sorted; sorted = sorted->next) {
gchar *name = (gchar*)sorted->data;
GsfInput *child = gsf_infile_child_by_name(infile, name);
if (child == NULL)
continue;
gboolean is_dir = GSF_IS_INFILE(child) && gsf_infile_num_children(GSF_INFILE(child)) > 0;
if (is_dir) {
if (!msi_prehash(GSF_INFILE(child), name, hash))
return FALSE;
} else {
if (!msi_prehash_utf16_name(name, hash))
return FALSE;
/*
* File size.
*/
gsf_off_t size = gsf_input_remaining(child);
guint32 sizebuf = GUINT32_TO_LE((guint32)size);
BIO_write(hash, &sizebuf, sizeof(sizebuf));
/*
* Reserved - must be 0. Corresponds to
* offset 0x7c..0x7f in the CDFv2 file.
*/
BIO_write(hash, zeroes, 4);
/*
* Creation time and modification time
* as Windows FILETIMEs. We keep them
* zeroed, because libgsf doesn't seem
* to support outputting them.
*/
BIO_write(hash, zeroes, 8); // ctime as a Windows FILETIME
BIO_write(hash, zeroes, 8); // mtime as a Windows FILETIME
}
}
g_slist_free(sorted);
return TRUE;
}
/**
* msi_handle_dir performs a direct copy of the input MSI file in infile to a new
* output file in outfile. While copying, it also writes all file content to the
* hash BIO in order to calculate a 'basic' hash that can be used for an MSI
* 'DigitalSignature' hash.
*
* msi_handle_dir is hierarchy aware: if any subdirectories are found, they will be
* visited, copied and hashed as well.
*/
static gboolean msi_handle_dir(GsfInfile *infile, GsfOutfile *outole, BIO *hash)
{
guint8 classid[16];
GSList *sorted = NULL;
gsf_infile_msole_get_class_id(GSF_INFILE_MSOLE(infile), classid);
if (outole != NULL)
gsf_outfile_msole_set_class_id(GSF_OUTFILE_MSOLE(outole), classid);
sorted = msi_sorted_infile_children(infile);
for (; sorted; sorted = sorted->next) {
gchar *name = (gchar*)sorted->data;
GsfInput *child = gsf_infile_child_by_name(infile, name);
if (child == NULL)
continue;
gboolean is_dir = GSF_IS_INFILE(child) && gsf_infile_num_children(GSF_INFILE(child)) > 0;
GsfOutput *outchild = NULL;
if (outole != NULL)
outchild = gsf_outfile_new_child(outole, name, is_dir);
if (is_dir) {
if (!msi_handle_dir(GSF_INFILE(child), GSF_OUTFILE(outchild), hash)) {
return FALSE;
}
} else {
while (gsf_input_remaining(child) > 0) {
gsf_off_t size = MIN(gsf_input_remaining(child), 4096);
guint8 const *data = gsf_input_read(child, size, NULL);
BIO_write(hash, data, size);
if (outchild != NULL && !gsf_output_write(outchild, size, data)) {
return FALSE;
}
}
}
if (outchild != NULL) {
gsf_output_close(outchild);
g_object_unref(outchild);
}
}
BIO_write(hash, classid, sizeof(classid));
g_slist_free(sorted);
return TRUE;
}
/*
* msi_verify_pkcs7 is a helper function for msi_verify_file.
* It exists to make it easier to implement verification of nested signatures.
*/
static int msi_verify_pkcs7(PKCS7 *p7, GsfInfile *infile, unsigned char *exdata, unsigned int exlen, char *leafhash, int allownest) {
int i = 0;
int ret = 0;
X509_STORE *store = NULL;
int mdtype = -1;
unsigned char mdbuf[EVP_MAX_MD_SIZE];
unsigned char cmdbuf[EVP_MAX_MD_SIZE];
#ifdef GSF_CAN_READ_MSI_METADATA
unsigned char cexmdbuf[EVP_MAX_MD_SIZE];
#endif
unsigned char hexbuf[EVP_MAX_MD_SIZE*2+1];
BIO *bio = NULL;
ASN1_OBJECT *indir_objid = OBJ_txt2obj(SPC_INDIRECT_DATA_OBJID, 1);
if (p7 && PKCS7_type_is_signed(p7) && !OBJ_cmp(p7->d.sign->contents->type, indir_objid) && p7->d.sign->contents->d.other->type == V_ASN1_SEQUENCE) {
ASN1_STRING *astr = p7->d.sign->contents->d.other->value.sequence;
const unsigned char *p = astr->data;
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, astr->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, idc->messageDigest->digest->length);
}
SpcIndirectDataContent_free(idc);
}
ASN1_OBJECT_free(indir_objid);
}
if (mdtype == -1) {
printf("Failed to extract current message digest\n\n");
ret = 1;
goto out;
}
if (p7 == NULL) {
printf("Failed to read PKCS7 signature from DigitalSignature section\n\n");
ret = 1;
goto out;
}
printf("Message digest algorithm : %s\n", OBJ_nid2sn(mdtype));
const EVP_MD *md = EVP_get_digestbynid(mdtype);
BIO *hash = BIO_new(BIO_f_md());
BIO_set_md(hash, md);
BIO_push(hash, BIO_new(BIO_s_null()));
if (exdata) {
/*
* Until libgsf can read more MSI metadata, we can't
* really verify them by plowing through the file.
* Verifying files signed by osslsigncode itself works,
* though!
*
* For now, the compromise is to use the hash given
* by the file, which is equivalent to verifying a
* non-MsiDigitalSignatureEx signature from a security
* pespective, because we'll only be calculating the
* file content hashes ourselves.
*/
#ifdef GSF_CAN_READ_MSI_METADATA
BIO *prehash = BIO_new(BIO_f_md());
BIO_set_md(prehash, md);
BIO_push(prehash, BIO_new(BIO_s_null()));
if (!msi_prehash(infile, NULL, prehash)) {
ret = 1;
goto out;
}
BIO_gets(prehash, (char*)cexmdbuf, EVP_MAX_MD_SIZE);
BIO_write(hash, (char*)cexmdbuf, EVP_MD_size(md));
#else
BIO_write(hash, (char *)exdata, EVP_MD_size(md));
#endif
}
if (!msi_handle_dir(infile, NULL, hash)) {
ret = 1;
goto out;
}
BIO_gets(hash, (char*)cmdbuf, EVP_MAX_MD_SIZE);
tohex(cmdbuf, hexbuf, EVP_MD_size(md));
int mdok = !memcmp(mdbuf, cmdbuf, EVP_MD_size(md));
if (!mdok) ret = 1;
printf("Calculated DigitalSignature : %s", hexbuf);
if (mdok) {
printf("\n");
} else {
tohex(mdbuf, hexbuf, EVP_MD_size(md));
printf(" MISMATCH!!! FILE HAS %s\n", hexbuf);
}
#ifdef GSF_CAN_READ_MSI_METADATA
if (exdata) {
tohex(cexmdbuf, hexbuf, EVP_MD_size(md));
int exok = !memcmp(exdata, cexmdbuf, MIN(EVP_MD_size(md), exlen));
if (!exok) ret = 1;
printf("Calculated MsiDigitalSignatureEx : %s", hexbuf);
if (exok) {
printf("\n");
} else {
tohex(exdata, hexbuf, EVP_MD_size(md));
printf(" MISMATCH!!! FILE HAS %s\n", hexbuf);
}
}
#endif
printf("\n");
int seqhdrlen = asn1_simple_hdr_len(p7->d.sign->contents->d.other->value.sequence->data,
p7->d.sign->contents->d.other->value.sequence->length);
bio = BIO_new_mem_buf(p7->d.sign->contents->d.other->value.sequence->data + seqhdrlen,
p7->d.sign->contents->d.other->value.sequence->length - seqhdrlen);
store = X509_STORE_new();
int verok = PKCS7_verify(p7, p7->d.sign->cert, store, bio, NULL, PKCS7_NOVERIFY);
BIO_free(bio);
/* XXX: add more checks here (attributes, timestamp, etc) */
printf("Signature verification: %s\n\n", verok ? "ok" : "failed");
if (!verok) {
ERR_print_errors_fp(stdout);
ret = 1;
}
int leafok = 0;
STACK_OF(X509) *signers = PKCS7_get0_signers(p7, NULL, 0);
printf("Number of signers: %d\n", sk_X509_num(signers));
for (i=0; i<sk_X509_num(signers); i++) {
X509 *cert = sk_X509_value(signers, i);
char *subject = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
char *issuer = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0);
printf("\tSigner #%d:\n\t\tSubject : %s\n\t\tIssuer : %s\n", i, subject, issuer);
OPENSSL_free(subject);
OPENSSL_free(issuer);
if (leafhash != NULL && leafok == 0) {
leafok = verify_leaf_hash(cert, leafhash) == 0;
}
}
sk_X509_free(signers);
printf("\nNumber of certificates: %d\n", sk_X509_num(p7->d.sign->cert));
for (i=0; i<sk_X509_num(p7->d.sign->cert); i++) {
X509 *cert = sk_X509_value(p7->d.sign->cert, i);
char *subject = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
char *issuer = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0);
printf("\tCert #%d:\n\t\tSubject : %s\n\t\tIssuer : %s\n", i, subject, issuer);
OPENSSL_free(subject);
OPENSSL_free(issuer);
}
printf("\n");
if (leafhash != NULL) {
printf("Leaf hash match: %s\n\n", leafok ? "ok" : "failed");
if (!leafok)
ret = 1;
}
if (allownest) {
int has_sig = 0;
PKCS7 *p7nest = pkcs7_get_nested_signature(p7, &has_sig);
if (p7nest) {
printf("\n");
int nest_ret = msi_verify_pkcs7(p7nest, infile, exdata, exlen, leafhash, 0);
if (ret == 0)
ret = nest_ret;
PKCS7_free(p7nest);
} else if (!p7nest && has_sig) {
printf("\nFailed to decode nested signature!\n");
ret = 1;
} else
printf("\n");
} else
printf("\n");
out:
if (store)
X509_STORE_free(store);
return ret;
}
/*
* msi_verify_file checks whether or not the signature of infile is valid.
*/
static int msi_verify_file(GsfInfile *infile, char *leafhash) {
GsfInput *sig = NULL;
GsfInput *exsig = NULL;
unsigned char *exdata = NULL;
unsigned char *indata = NULL;
gchar decoded[0x40];
int i, ret = 0;
PKCS7 *p7 = NULL;
for (i = 0; i < gsf_infile_num_children(infile); i++) {
GsfInput *child = gsf_infile_child_by_index(infile, i);
const guint8 *name = (const guint8*)gsf_input_name(child);
msi_decode(name, decoded);
if (!g_strcmp0(decoded, "\05DigitalSignature"))
sig = child;
if (!g_strcmp0(decoded, "\05MsiDigitalSignatureEx"))
exsig = child;
}
if (sig == NULL) {
printf("MSI file has no signature\n\n");
ret = 1;
goto out;
}
unsigned long inlen = (unsigned long) gsf_input_remaining(sig);
indata = malloc(inlen);
if (gsf_input_read(sig, inlen, indata) == NULL) {
ret = 1;
goto out;
}
unsigned long exlen = 0;
if (exsig != NULL) {
exlen = (unsigned long) gsf_input_remaining(exsig);
exdata = malloc(exlen);
if (gsf_input_read(exsig, exlen, exdata) == NULL) {
ret = 1;
goto out;
}
}
const unsigned char *blob = (unsigned char *)indata;
p7 = d2i_PKCS7(NULL, &blob, inlen);
ret = msi_verify_pkcs7(p7, infile, exdata, exlen, leafhash, 1);
out:
free(indata);
free(exdata);
if (p7)
PKCS7_free(p7);
return ret;
}
static int msi_extract_dse(GsfInfile *infile, unsigned char **dsebuf, unsigned long *dselen, int *has_dse) {
GsfInput *exsig = NULL;
gchar decoded[0x40];
u_char *buf = NULL;
gsf_off_t size = 0;
int i, ret = 0;
for (i = 0; i < gsf_infile_num_children(infile); i++) {
GsfInput *child = gsf_infile_child_by_index(infile, i);
const guint8 *name = (const guint8*)gsf_input_name(child);
msi_decode(name, decoded);
if (!g_strcmp0(decoded, "\05MsiDigitalSignatureEx"))
exsig = child;
}
if (exsig == NULL) {
ret = 1;
goto out;
}
if (has_dse != NULL) {
*has_dse = 1;
}
size = gsf_input_remaining(exsig);
if (dselen != NULL) {
*dselen = (unsigned long) size;
}
if (dsebuf != NULL) {
buf = malloc(size);
if (gsf_input_read(exsig, size, buf) == NULL) {
ret = 1;
goto out;
}
*dsebuf = (unsigned char *) buf;
}
out:
return ret;
}
/*
* msi_extract_signature_to_file extracts the MSI DigitalSignaure from infile
* to a file at the path given by outfile.
*/
static int msi_extract_signature_to_file(GsfInfile *infile, char *outfile) {
unsigned char hexbuf[EVP_MAX_MD_SIZE*2+1];
GsfInput *sig = NULL;
GsfInput *exsig = NULL;
unsigned char *exdata = NULL;
unsigned long exlen = 0;
BIO *outdata = NULL;
gchar decoded[0x40];
int i, ret = 0;
for (i = 0; i < gsf_infile_num_children(infile); i++) {
GsfInput *child = gsf_infile_child_by_index(infile, i);
const guint8 *name = (const guint8*)gsf_input_name(child);
msi_decode(name, decoded);
if (!g_strcmp0(decoded, "\05DigitalSignature"))
sig = child;
if (!g_strcmp0(decoded, "\05MsiDigitalSignatureEx"))
exsig = child;
}
if (sig == NULL) {
printf("MSI file has no signature\n\n");
ret = 1;
goto out;
}
/* Create outdata file */
outdata = BIO_new_file(outfile, "w+b");
if (outdata == NULL) {
printf("Unable to open %s\n\n", outfile);
ret = 1;
goto out;
}
while (gsf_input_remaining(sig) > 0) {
gsf_off_t size = MIN(gsf_input_remaining(sig), 4096);
guint8 const *data = gsf_input_read(sig, size, NULL);
BIO_write(outdata, data, size);
}
if (exsig != NULL) {
exlen = (unsigned long) gsf_input_remaining(exsig);
if (exlen > EVP_MAX_MD_SIZE) {
printf("MsiDigitalSignatureEx is larger than EVP_MAX_MD_SIZE. Aborting...\n\n");
ret = 1;
goto out;
}
exdata = malloc(exlen);
if (gsf_input_read(exsig, exlen, exdata) == NULL) {
printf("Unable to read MsiDigitalSignatureEx\n\n");
ret = 1;
goto out;
}
tohex(exdata, hexbuf, exlen);
printf("Note: MSI includes a MsiDigitalSignatureEx section.\n");
printf("MsiDigitalSignatureEx pre-hash: %s\n\n", hexbuf);
}
out:
free(exdata);
if (outdata)
BIO_free_all(outdata);
return ret;
}
static PKCS7 *msi_extract_signature_to_pkcs7(GsfInfile *infile) {
GsfInput *sig = NULL;
gchar decoded[0x40];
PKCS7 *p7 = NULL;
u_char *buf = NULL;
gsf_off_t size = 0;
int i = 0;
for (i = 0; i < gsf_infile_num_children(infile); i++) {
GsfInput *child = gsf_infile_child_by_index(infile, i);
const guint8 *name = (const guint8*)gsf_input_name(child);
msi_decode(name, decoded);
if (!g_strcmp0(decoded, "\05DigitalSignature"))
sig = child;
}
if (sig == NULL) {
goto out;
}
size = gsf_input_remaining(sig);
buf = malloc(size);
if (gsf_input_read(sig, size, buf) == NULL) {
goto out;
}
const unsigned char *p7buf = buf;
p7 = d2i_PKCS7(NULL, &p7buf, size);
out:
free(buf);
return p7;
}
#endif
static void calc_pe_digest(BIO *bio, const EVP_MD *md, unsigned char *mdbuf,
unsigned int peheader, int pe32plus, unsigned int fileend)
{
static unsigned char bfb[16*1024*1024];
EVP_MD_CTX mdctx;
EVP_MD_CTX_init(&mdctx);
EVP_DigestInit(&mdctx, md);
memset(mdbuf, 0, EVP_MAX_MD_SIZE);
(void)BIO_seek(bio, 0);
BIO_read(bio, bfb, peheader + 88);
EVP_DigestUpdate(&mdctx, bfb, peheader + 88);
BIO_read(bio, bfb, 4);
BIO_read(bio, bfb, 60+pe32plus*16);
EVP_DigestUpdate(&mdctx, bfb, 60+pe32plus*16);
BIO_read(bio, bfb, 8);
unsigned int n = peheader + 88 + 4 + 60+pe32plus*16 + 8;
while (n < fileend) {
int want = fileend - n;
if (want > sizeof(bfb))
want = sizeof(bfb);
int l = BIO_read(bio, bfb, want);
if (l <= 0)
break;
EVP_DigestUpdate(&mdctx, bfb, l);
n += l;
}
EVP_DigestFinal(&mdctx, mdbuf, NULL);
}
static void extract_page_hash (SpcAttributeTypeAndOptionalValue *obj,
unsigned char **ph, unsigned int *phlen, int *phtype)
{
*phlen = 0;
const unsigned char *blob = obj->value->value.sequence->data;
SpcPeImageData *id = d2i_SpcPeImageData(NULL, &blob, obj->value->value.sequence->length);
if (id == NULL)
return;
if (id->file->type != 1) {
SpcPeImageData_free(id);
return;
}
SpcSerializedObject *so = id->file->value.moniker;
if (so->classId->length != sizeof(classid_page_hash) ||
memcmp(so->classId->data, classid_page_hash, sizeof (classid_page_hash))) {
SpcPeImageData_free(id);
return;
}
/* skip ASN.1 SET hdr */
unsigned int l = asn1_simple_hdr_len(so->serializedData->data, so->serializedData->length);
blob = so->serializedData->data + l;
obj = d2i_SpcAttributeTypeAndOptionalValue(NULL, &blob, so->serializedData->length - l);
SpcPeImageData_free(id);
if (!obj)
return;
char buf[128];
*phtype = 0;
buf[0] = 0x00;
OBJ_obj2txt(buf, sizeof(buf), obj->type, 1);
if (!strcmp(buf, SPC_PE_IMAGE_PAGE_HASHES_V1)) {
*phtype = NID_sha1;
} else if (!strcmp(buf, SPC_PE_IMAGE_PAGE_HASHES_V2)) {
*phtype = NID_sha256;
} else {
SpcAttributeTypeAndOptionalValue_free(obj);
return;
}
/* Skip ASN.1 SET hdr */
unsigned int l2 = asn1_simple_hdr_len(obj->value->value.sequence->data, obj->value->value.sequence->length);
/* Skip ASN.1 OCTET STRING hdr */
l = asn1_simple_hdr_len(obj->value->value.sequence->data + l2, obj->value->value.sequence->length - l2);
l += l2;
*phlen = obj->value->value.sequence->length - l;
*ph = malloc(*phlen);
memcpy(*ph, obj->value->value.sequence->data + l, *phlen);
SpcAttributeTypeAndOptionalValue_free(obj);
}
static unsigned char *calc_page_hash(char *indata, unsigned int peheader, int pe32plus,
unsigned int sigpos, int phtype, unsigned int *rphlen)
{
unsigned short nsections = GET_UINT16_LE(indata + peheader + 6);
unsigned int pagesize = GET_UINT32_LE(indata + peheader + 56);
unsigned int hdrsize = GET_UINT32_LE(indata + peheader + 84);
const EVP_MD *md = EVP_get_digestbynid(phtype);
int pphlen = 4 + EVP_MD_size(md);
int phlen = pphlen * (3 + nsections + sigpos / pagesize);
unsigned char *res = malloc(phlen);
unsigned char *zeroes = calloc(pagesize, 1);
EVP_MD_CTX mdctx;
EVP_MD_CTX_init(&mdctx);
EVP_DigestInit(&mdctx, md);
EVP_DigestUpdate(&mdctx, indata, peheader + 88);
EVP_DigestUpdate(&mdctx, indata + peheader + 92, 60 + pe32plus*16);
EVP_DigestUpdate(&mdctx, indata + peheader + 160 + pe32plus*16, hdrsize - (peheader + 160 + pe32plus*16));
EVP_DigestUpdate(&mdctx, zeroes, pagesize - hdrsize);
memset(res, 0, 4);
EVP_DigestFinal(&mdctx, res + 4, NULL);
unsigned short sizeofopthdr = GET_UINT16_LE(indata + peheader + 20);
char *sections = indata + peheader + 24 + sizeofopthdr;
int i, pi = 1;
unsigned int lastpos = 0;
for (i=0; i<nsections; i++) {
unsigned int rs = GET_UINT32_LE(sections + 16);
unsigned int ro = GET_UINT32_LE(sections + 20);
unsigned int l;
for (l=0; l < rs; l+=pagesize, pi++) {
PUT_UINT32_LE(ro + l, res + pi*pphlen);
EVP_DigestInit(&mdctx, md);
if (rs - l < pagesize) {
EVP_DigestUpdate(&mdctx, indata + ro + l, rs - l);
EVP_DigestUpdate(&mdctx, zeroes, pagesize - (rs - l));
} else {
EVP_DigestUpdate(&mdctx, indata + ro + l, pagesize);
}
EVP_DigestFinal(&mdctx, res + pi*pphlen + 4, NULL);
}
lastpos = ro + rs;
sections += 40;
}
PUT_UINT32_LE(lastpos, res + pi*pphlen);
memset(res + pi*pphlen + 4, 0, EVP_MD_size(md));
pi++;
free(zeroes);
*rphlen = pi*pphlen;
return res;
}
static int verify_pe_pkcs7(PKCS7 *p7, char *indata, unsigned int peheader, int pe32plus,
unsigned int sigpos, unsigned int siglen, char *leafhash,
int allownest)
{
int ret = 0;
int mdtype = -1, phtype = -1;
unsigned char mdbuf[EVP_MAX_MD_SIZE];
unsigned char cmdbuf[EVP_MAX_MD_SIZE];
unsigned char hexbuf[EVP_MAX_MD_SIZE*2+1];
unsigned char *ph = NULL;
unsigned int phlen = 0;
BIO *bio = NULL;
ASN1_OBJECT *indir_objid = OBJ_txt2obj(SPC_INDIRECT_DATA_OBJID, 1);
if (PKCS7_type_is_signed(p7) &&
!OBJ_cmp(p7->d.sign->contents->type, indir_objid) &&
p7->d.sign->contents->d.other->type == V_ASN1_SEQUENCE) {
ASN1_STRING *astr = p7->d.sign->contents->d.other->value.sequence;
const unsigned char *p = astr->data;
SpcIndirectDataContent *idc = d2i_SpcIndirectDataContent(NULL, &p, astr->length);
if (idc) {
extract_page_hash (idc->data, &ph, &phlen, &phtype);
if (idc->messageDigest && idc->messageDigest->digest && idc->messageDigest->digestAlgorithm) {
mdtype = OBJ_obj2nid(idc->messageDigest->digestAlgorithm->algorithm);
memcpy(mdbuf, idc->messageDigest->digest->data, idc->messageDigest->digest->length);
}
SpcIndirectDataContent_free(idc);
}
}
ASN1_OBJECT_free(indir_objid);
if (mdtype == -1) {
printf("Failed to extract current message digest\n\n");
return -1;
}
printf("Message digest algorithm : %s\n", OBJ_nid2sn(mdtype));
const EVP_MD *md = EVP_get_digestbynid(mdtype);
tohex(mdbuf, hexbuf, EVP_MD_size(md));
printf("Current message digest : %s\n", hexbuf);
bio = BIO_new_mem_buf(indata, sigpos + siglen);
calc_pe_digest(bio, md, cmdbuf, peheader, pe32plus, sigpos);
BIO_free(bio);
tohex(cmdbuf, hexbuf, EVP_MD_size(md));
int mdok = !memcmp(mdbuf, cmdbuf, EVP_MD_size(md));
if (!mdok) ret = 1;
printf("Calculated message digest : %s%s\n\n", hexbuf, mdok?"":" MISMATCH!!!");
if (phlen > 0) {
printf("Page hash algorithm : %s\n", OBJ_nid2sn(phtype));
tohex(ph, hexbuf, (phlen < 32) ? phlen : 32);
printf("Page hash : %s ...\n", hexbuf);
unsigned int cphlen = 0;
unsigned char *cph = calc_page_hash(indata, peheader, pe32plus, sigpos, phtype, &cphlen);
tohex(cph, hexbuf, (cphlen < 32) ? cphlen : 32);
printf("Calculated page hash : %s ...%s\n\n", hexbuf,
((phlen != cphlen) || memcmp(ph, cph, phlen)) ? " MISMATCH!!!":"");
free(ph);
free(cph);
}
int seqhdrlen = asn1_simple_hdr_len(p7->d.sign->contents->d.other->value.sequence->data,
p7->d.sign->contents->d.other->value.sequence->length);
bio = BIO_new_mem_buf(p7->d.sign->contents->d.other->value.sequence->data + seqhdrlen,
p7->d.sign->contents->d.other->value.sequence->length - seqhdrlen);
X509_STORE *store = X509_STORE_new();
int verok = PKCS7_verify(p7, p7->d.sign->cert, store, bio, NULL, PKCS7_NOVERIFY);
BIO_free(bio);
/* XXX: add more checks here (attributes, timestamp, etc) */
printf("Signature verification: %s\n\n", verok ? "ok" : "failed");
if (!verok) {
ERR_print_errors_fp(stdout);
ret = 1;
}
int i;
int leafok = 0;
STACK_OF(X509) *signers = PKCS7_get0_signers(p7, NULL, 0);
printf("Number of signers: %d\n", sk_X509_num(signers));
for (i=0; i<sk_X509_num(signers); i++) {
X509 *cert = sk_X509_value(signers, i);
char *subject = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
char *issuer = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0);
BIGNUM *serialbn = ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), NULL);
char *serial = BN_bn2hex(serialbn);
if (i > 0)
printf("\t------------------\n");
printf("\tSigner #%d:\n\t\tSubject: %s\n\t\tIssuer : %s\n\t\tSerial : %s\n",
i, subject, issuer, serial);
OPENSSL_free(subject);
OPENSSL_free(issuer);
OPENSSL_free(serial);
BN_free(serialbn);
if (leafhash != NULL && leafok == 0) {
leafok = verify_leaf_hash(cert, leafhash) == 0;
}
}
sk_X509_free(signers);
printf("\nNumber of certificates: %d\n", sk_X509_num(p7->d.sign->cert));
for (i=0; i<sk_X509_num(p7->d.sign->cert); i++) {
X509 *cert = sk_X509_value(p7->d.sign->cert, i);
char *subject = X509_NAME_oneline(X509_get_subject_name(cert), NULL, 0);
char *issuer = X509_NAME_oneline(X509_get_issuer_name(cert), NULL, 0);
BIGNUM *serialbn = ASN1_INTEGER_to_BN(X509_get_serialNumber(cert), NULL);
char *serial = BN_bn2hex(serialbn);
if (i > 0)
printf("\t------------------\n");
printf("\tCert #%d:\n\t\tSubject: %s\n\t\tIssuer : %s\n\t\tSerial : %s\n",
i, subject, issuer, serial);
OPENSSL_free(subject);
OPENSSL_free(issuer);
OPENSSL_free(serial);
BN_free(serialbn);
}
if (leafhash != NULL) {
printf("\nLeaf hash match: %s\n", leafok ? "ok" : "failed");
if (!leafok)
ret = 1;
}
if (allownest) {
int has_sig = 0;
PKCS7 *p7nest = pkcs7_get_nested_signature(p7, &has_sig);
if (p7nest) {
printf("\n");
int nest_ret = verify_pe_pkcs7(p7nest, indata, peheader, pe32plus, sigpos, siglen, leafhash, 0);
if (ret == 0)
ret = nest_ret;
PKCS7_free(p7nest);
} else if (!p7nest && has_sig) {
printf("\nFailed to decode nested signature!\n");
ret = 1;
} else
printf("\n");
} else
printf("\n");
X509_STORE_free(store);
return ret;
}
static int verify_pe_file(char *indata, unsigned int peheader, int pe32plus,
unsigned int sigpos, unsigned int siglen, char *leafhash)
{
int ret = 0;
unsigned int pe_checksum = GET_UINT32_LE(indata + peheader + 88);
printf("Current PE checksum : %08X\n", pe_checksum);
BIO *bio = BIO_new_mem_buf(indata, sigpos + siglen);
unsigned int real_pe_checksum = calc_pe_checksum(bio, peheader);
BIO_free(bio);
if (pe_checksum && pe_checksum != real_pe_checksum)
ret = 1;
printf("Calculated PE checksum: %08X%s\n\n", real_pe_checksum,
ret ? " MISMATCH!!!!" : "");
if (siglen == 0) {
printf("No signature found.\n\n");
return ret;
}
unsigned int pos = 0;
PKCS7 *p7 = NULL;
while (pos < siglen) {
unsigned int l = GET_UINT32_LE(indata + sigpos + pos);
unsigned short certrev = GET_UINT16_LE(indata + sigpos + pos + 4);
unsigned short certtype = GET_UINT16_LE(indata + sigpos + pos + 6);
if (certrev == WIN_CERT_REVISION_2 && certtype == WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
const unsigned char *blob = (unsigned char*)indata + sigpos + pos + 8;
p7 = d2i_PKCS7(NULL, &blob, l - 8);
}
if (l%8)
l += (8 - l%8);
pos += l;
}
if (p7 == NULL) {
printf("Failed to extract PKCS7 data\n\n");
return -1;
}
ret = verify_pe_pkcs7(p7, indata, peheader, pe32plus, sigpos, siglen, leafhash, 1);
PKCS7_free(p7);
return ret;
}
// extract_existing_pe_pkcs7 retreives a decoded PKCS7 struct corresponding to the
// existing signature of the PE file.
static PKCS7 *extract_existing_pe_pkcs7(char *indata, unsigned int peheader, int pe32plus,
unsigned int sigpos, unsigned int siglen)
{
unsigned int pos = 0;
PKCS7 *p7 = NULL;
while (pos < siglen) {
unsigned int l = GET_UINT32_LE(indata + sigpos + pos);
unsigned short certrev = GET_UINT16_LE(indata + sigpos + pos + 4);
unsigned short certtype = GET_UINT16_LE(indata + sigpos + pos + 6);
if (certrev == WIN_CERT_REVISION_2 && certtype == WIN_CERT_TYPE_PKCS_SIGNED_DATA) {
const unsigned char *blob = (unsigned char*)indata + sigpos + pos + 8;
p7 = d2i_PKCS7(NULL, &blob, l - 8);
}
if (l%8)
l += (8 - l%8);
pos += l;
}
return p7;
}
static STACK_OF(X509) *PEM_read_certs_with_pass(BIO *bin, char *certpass)
{
STACK_OF(X509) *certs = sk_X509_new_null();
X509 *x509;
(void)BIO_seek(bin, 0);
while((x509 = PEM_read_bio_X509(bin, NULL, NULL, certpass)))
sk_X509_push(certs, x509);
if (!sk_X509_num(certs)) {
sk_X509_free(certs);
return NULL;
}
return certs;
}
static STACK_OF(X509) *PEM_read_certs(BIO *bin, char *certpass)
{
STACK_OF(X509) *certs = PEM_read_certs_with_pass(bin, certpass);
if (!certs)
certs = PEM_read_certs_with_pass(bin, NULL);
return certs;
}
static off_t get_file_size(const char *infile)
{
int ret;
#ifdef _WIN32
struct _stat st;
ret = _stat(infile, &st);
#else
struct stat st;
ret = stat(infile, &st);
#endif
if (ret)
{
fprintf(stderr, "Failed to open file: %s\n", infile);
return 0;
}
if (st.st_size < 4) {
fprintf(stderr, "Unrecognized file type - file is too short: %s\n", infile);
return 0;
}
return st.st_size;
}
static char* map_file(const char *infile, const off_t size)
{
char *indata = NULL;
#ifdef WIN32
HANDLE fh, fm;
fh = CreateFile(infile, GENERIC_READ, FILE_SHARE_READ , NULL, OPEN_EXISTING, 0, NULL);
if (fh == INVALID_HANDLE_VALUE)
return NULL;
fm = CreateFileMapping(fh, NULL, PAGE_READONLY, 0, 0, NULL);
if (fm == NULL)
return NULL;
indata = MapViewOfFile(fm, FILE_MAP_READ, 0, 0, 0);
#else
int fd = open(infile, O_RDONLY);
if (fd < 0)
return NULL;
indata = mmap(0, size, PROT_READ, MAP_PRIVATE, fd, 0);
if (indata == MAP_FAILED)
return NULL;
#endif
return indata;
}
#ifdef PROVIDE_ASKPASS
char *getpassword(const char *prompt)
{
#ifdef HAVE_TERMIOS_H
struct termios ofl, nfl;
char *p, passbuf[1024];
fputs(prompt, stdout);
tcgetattr(fileno(stdin), &ofl);
nfl = ofl;
nfl.c_lflag &= ~ECHO;
nfl.c_lflag |= ECHONL;
if (tcsetattr(fileno(stdin), TCSANOW, &nfl) != 0) {
fprintf(stderr, "Failed to set terminal attributes\n");
return NULL;
}
p = fgets(passbuf, sizeof(passbuf), stdin);
if (tcsetattr(fileno(stdin), TCSANOW, &ofl) != 0)
fprintf(stderr, "Failed to restore terminal attributes\n");
if (!p) {
fprintf(stderr, "Failed to read password\n");
return NULL;
}
passbuf[strlen(passbuf)-1] = 0x00;
char *pass = strdup(passbuf);
memset(passbuf, 0, sizeof(passbuf));
return pass;
#else
return getpass(prompt);
#endif
}
#endif
int main(int argc, char **argv)
{
BIO *btmp, *sigbio, *hash, *outdata;
PKCS12 *p12;
PKCS7 *p7 = NULL, *cursig = NULL, *outsig = NULL, *sig, *p7x = NULL;
X509 *cert = NULL;
STACK_OF(X509) *certs = NULL, *xcerts = NULL;
EVP_PKEY *pkey = NULL;
PKCS7_SIGNER_INFO *si;
ASN1_STRING *astr;
const EVP_MD *md;
const char *argv0 = argv[0];
static char buf[64*1024];
char *xcertfile, *certfile, *keyfile, *pvkfile, *pkcs12file, *infile, *outfile, *sigfile, *desc, *url, *indata, *insigdata, *outdataverify;
char *p11engine, *p11module;
char *pass = NULL, *readpass = NULL;
int output_pkcs7 = 0;
int askpass = 0;
char *leafhash = NULL;
#ifdef ENABLE_CURL
char *turl[MAX_TS_SERVERS], *proxy = NULL, *tsurl[MAX_TS_SERVERS];
int noverifypeer = 0;
#endif
int nest = 0;
int add_msi_dse = 0;
int nturl = 0, ntsurl = 0;
int addBlob = 0;
u_char *p = NULL;
int ret = 0, i, len = 0, jp = -1, pe32plus = 0, comm = 0, pagehash = 0;
unsigned int tmp, peheader = 0, padlen = 0;
off_t filesize, fileend, sigfilesize, sigfileend, outdatasize;
file_type_t type;
cmd_type_t cmd = CMD_SIGN;
char *failarg = NULL;
static u_char purpose_ind[] = {
0x30, 0x0c,
0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x15
};
static u_char purpose_comm[] = {
0x30, 0x0c,
0x06, 0x0a, 0x2b, 0x06, 0x01, 0x04, 0x01, 0x82, 0x37, 0x02, 0x01, 0x16
};
static u_char msi_signature[] = {
0xd0, 0xcf, 0x11, 0xe0, 0xa1, 0xb1, 0x1a, 0xe1
};
#ifdef WITH_GSF
GsfOutfile *outole = NULL;
GsfOutput *sink = NULL;
u_char *p_msiex = NULL;
int len_msiex = 0;
#endif
xcertfile = certfile = keyfile = pvkfile = pkcs12file = p11module = p11engine = infile = outfile = sigfile = desc = url = NULL;
hash = outdata = NULL;
/* Set up OpenSSL */
ERR_load_crypto_strings();
OPENSSL_add_all_algorithms_conf();
/* create some MS Authenticode OIDS we need later on */
if (!OBJ_create(SPC_STATEMENT_TYPE_OBJID, NULL, NULL) ||
!OBJ_create(SPC_MS_JAVA_SOMETHING, NULL, NULL) ||
!OBJ_create(SPC_SP_OPUS_INFO_OBJID, NULL, NULL) ||
!OBJ_create(SPC_NESTED_SIGNATURE_OBJID, NULL, NULL))
DO_EXIT_0("Failed to add objects\n");
md = EVP_sha1();
if (argc > 1) {
if (!strcmp(argv[1], "sign")) {
cmd = CMD_SIGN;
argv++;
argc--;
} else if (!strcmp(argv[1], "extract-signature")) {
cmd = CMD_EXTRACT;
argv++;
argc--;
} else if (!strcmp(argv[1], "attach-signature")) {
cmd = CMD_ATTACH;
argv++;
argc--;
} else if (!strcmp(argv[1], "remove-signature")) {
cmd = CMD_REMOVE;
argv++;
argc--;
} else if (!strcmp(argv[1], "verify")) {
cmd = CMD_VERIFY;
argv++;
argc--;
} else if (!strcmp(argv[1], "add")) {
cmd = CMD_ADD;
argv++;
argc--;
}
}
for (argc--,argv++; argc >= 1; argc--,argv++) {
if (!strcmp(*argv, "-in")) {
if (--argc < 1) usage(argv0);
infile = *(++argv);
} else if (!strcmp(*argv, "-out")) {
if (--argc < 1) usage(argv0);
outfile = *(++argv);
} else if (!strcmp(*argv, "-sigin")) {
if (--argc < 1) usage(argv0);
sigfile = *(++argv);
} else if ((cmd == CMD_SIGN) && (!strcmp(*argv, "-spc") || !strcmp(*argv, "-certs"))) {
if (--argc < 1) usage(argv0);
certfile = *(++argv);
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-ac")) {
if (--argc < 1) usage(argv0);
xcertfile = *(++argv);
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-key")) {
if (--argc < 1) usage(argv0);
keyfile = *(++argv);
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-pkcs12")) {
if (--argc < 1) usage(argv0);
pkcs12file = *(++argv);
} else if ((cmd == CMD_EXTRACT) && !strcmp(*argv, "-pem")) {
output_pkcs7 = 1;
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-pkcs11engine")) {
if (--argc < 1) usage(argv0);
p11engine = *(++argv);
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-pkcs11module")) {
if (--argc < 1) usage(argv0);
p11module = *(++argv);
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-pass")) {
if (askpass || readpass) usage(argv0);
if (--argc < 1) usage(argv0);
pass = strdup(*(++argv));
memset(*argv, 0, strlen(*argv));
#ifdef PROVIDE_ASKPASS
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-askpass")) {
if (pass || readpass) usage(argv0);
askpass = 1;
#endif
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-readpass")) {
if (askpass || pass) usage(argv0);
if (--argc < 1) usage(argv0);
readpass = *(++argv);
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-comm")) {
comm = 1;
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-ph")) {
pagehash = 1;
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-n")) {
if (--argc < 1) usage(argv0);
desc = *(++argv);
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-h")) {
if (--argc < 1) usage(argv0);
++argv;
if (!strcmp(*argv, "md5")) {
md = EVP_md5();
} else if (!strcmp(*argv, "sha1")) {
md = EVP_sha1();
} else if (!strcmp(*argv, "sha2") || !strcmp(*argv, "sha256")) {
md = EVP_sha256();
} else if (!strcmp(*argv, "sha384")) {
md = EVP_sha384();
} else if (!strcmp(*argv, "sha512")) {
md = EVP_sha512();
} else {
usage(argv0);
}
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-i")) {
if (--argc < 1) usage(argv0);
url = *(++argv);
#ifdef ENABLE_CURL
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-t")) {
if (--argc < 1) usage(argv0);
turl[nturl++] = *(++argv);
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-ts")) {
if (--argc < 1) usage(argv0);
tsurl[ntsurl++] = *(++argv);
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-p")) {
if (--argc < 1) usage(argv0);
proxy = *(++argv);
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-noverifypeer")) {
noverifypeer = 1;
#endif
} else if ((cmd == CMD_SIGN || cmd == CMD_ADD) && !strcmp(*argv, "-addUnauthenticatedBlob")) {
addBlob = 1;
} else if ((cmd == CMD_SIGN || cmd == CMD_ATTACH) && !strcmp(*argv, "-nest")) {
nest = 1;
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-verbose")) {
g_verbose = 1;
} else if ((cmd == CMD_SIGN) && !strcmp(*argv, "-add-msi-dse")) {
add_msi_dse = 1;
} else if ((cmd == CMD_VERIFY) && !strcmp(*argv, "-require-leaf-hash")) {
if (--argc < 1) usage(argv0);
leafhash = (*++argv);
} else if (!strcmp(*argv, "-v") || !strcmp(*argv, "--version")) {
printf(PACKAGE_STRING ", using:\n\t%s\n\t%s\n",
SSLeay_version(SSLEAY_VERSION),
#ifdef ENABLE_CURL
curl_version()
#else
"no libcurl available"
#endif
);
printf(
#ifdef WITH_GSF
"\tlibgsf %d.%d.%d\n",
libgsf_major_version,
libgsf_minor_version,
libgsf_micro_version
#else
"\tno libgsf available\n"
#endif
);
printf("\nPlease send bug-reports to "
PACKAGE_BUGREPORT
"\n\n");
} else if (!strcmp(*argv, "-jp")) {
char *ap;
if (--argc < 1) usage(argv0);
ap = *(++argv);
for (i=0; ap[i]; i++) ap[i] = tolower((int)ap[i]);
if (!strcmp(ap, "low")) {
jp = 0;
} else if (!strcmp(ap, "medium")) {
jp = 1;
} else if (!strcmp(ap, "high")) {
jp = 2;
}
if (jp != 0) usage(argv0); /* XXX */
} else {
failarg = *argv;
break;
}
}
if (!infile && argc > 0) {
infile = *(argv++);
argc--;
}
if (cmd != CMD_VERIFY && (!outfile && argc > 0)) {
if (!strcmp(*argv, "-out")) {
argv++;
argc--;
}
if (argc > 0) {
outfile = *(argv++);
argc--;
}
}
if (argc > 0 || (nturl && ntsurl) || !infile ||
(cmd != CMD_VERIFY && !outfile) ||
(cmd == CMD_SIGN && !((certfile && keyfile) || pkcs12file || (p11engine && p11module)))) {
if (failarg)
fprintf(stderr, "Unknown option: %s\n", failarg);
usage(argv0);
}
if (readpass) {
char passbuf[4096];
int passfd = open(readpass, O_RDONLY);
if (passfd < 0)
DO_EXIT_1("Failed to open password file: %s\n", readpass);
int passlen = read(passfd, passbuf, sizeof(passbuf)-1);
close(passfd);
if (passlen <= 0)
DO_EXIT_1("Failed to read password from file: %s\n", readpass);
passbuf[passlen] = 0x00;
pass = strdup(passbuf);
memset(passbuf, 0, sizeof(passbuf));
#ifdef PROVIDE_ASKPASS
} else if (askpass) {
pass = getpassword("Password: ");
#endif
}
if (cmd == CMD_SIGN) {
/* Read certificate and key */
if (keyfile && !p11engine && (btmp = BIO_new_file(keyfile, "rb")) != NULL) {
unsigned char magic[4];
unsigned char pvkhdr[4] = { 0x1e, 0xf1, 0xb5, 0xb0 };
magic[0] = 0x00;
BIO_read(btmp, magic, 4);
if (!memcmp(magic, pvkhdr, 4)) {
pvkfile = keyfile;
keyfile = NULL;
}
BIO_free(btmp);
}
if (pkcs12file != NULL) {
if ((btmp = BIO_new_file(pkcs12file, "rb")) == NULL ||
(p12 = d2i_PKCS12_bio(btmp, NULL)) == NULL)
DO_EXIT_1("Failed to read PKCS#12 file: %s\n", pkcs12file);
BIO_free(btmp);
if (!PKCS12_parse(p12, pass ? pass : "", &pkey, &cert, &certs))
DO_EXIT_1("Failed to parse PKCS#12 file: %s (Wrong password?)\n", pkcs12file);
PKCS12_free(p12);
} else if (pvkfile != NULL) {
#if OPENSSL_VERSION_NUMBER > 0x10000000
if ((btmp = BIO_new_file(certfile, "rb")) == NULL ||
((p7 = d2i_PKCS7_bio(btmp, NULL)) == NULL &&
(certs = PEM_read_certs(btmp, "")) == NULL))
DO_EXIT_1("Failed to read certificate file: %s\n", certfile);
BIO_free(btmp);
if ((btmp = BIO_new_file(pvkfile, "rb")) == NULL ||
( (pkey = b2i_PVK_bio(btmp, NULL, pass ? pass : "")) == NULL &&
(BIO_seek(btmp, 0) == 0) &&
(pkey = b2i_PVK_bio(btmp, NULL, NULL)) == NULL))
DO_EXIT_1("Failed to read PVK file: %s\n", pvkfile);
BIO_free(btmp);
#else
DO_EXIT_1("Can not read keys from PVK files, must compile against a newer version of OpenSSL: %s\n", pvkfile);
#endif
} else if (p11engine != NULL && p11module != NULL) {
const int CMD_MANDATORY = 0;
ENGINE_load_dynamic();
ENGINE * dyn = ENGINE_by_id( "dynamic" );
if ( ! dyn )
DO_EXIT_0( "Failed to load 'dynamic' engine");
if ( 1 != ENGINE_ctrl_cmd_string( dyn, "SO_PATH", p11engine, CMD_MANDATORY ) )
DO_EXIT_1( "Failed to set dyn SO_PATH to '%s'", p11engine);
if ( 1 != ENGINE_ctrl_cmd_string( dyn, "ID", "pkcs11", CMD_MANDATORY ) )
DO_EXIT_0( "Failed to set dyn ID to 'pkcs11'" );
if ( 1 != ENGINE_ctrl_cmd( dyn, "LIST_ADD", 1, NULL, NULL, CMD_MANDATORY ) )
DO_EXIT_0( "Failed to set dyn LIST_ADD to '1'" );
if ( 1 != ENGINE_ctrl_cmd( dyn, "LOAD", 1, NULL, NULL, CMD_MANDATORY ) )
DO_EXIT_0( "Failed to set dyn LOAD to '1'" );
ENGINE * pkcs11 = ENGINE_by_id( "pkcs11" );
if ( ! pkcs11 )
DO_EXIT_0( "Failed to find and load pkcs11 engine" );
if ( 1 != ENGINE_ctrl_cmd_string( pkcs11, "MODULE_PATH", p11module, CMD_MANDATORY ) )
DO_EXIT_1( "Failed to set pkcs11 engine MODULE_PATH to '%s'", p11module );
if (pass != NULL) {
if ( 1 != ENGINE_ctrl_cmd_string( pkcs11, "PIN", pass, CMD_MANDATORY ) )
DO_EXIT_0( "Failed to set pkcs11 PIN" );
}
if ( 1 != ENGINE_init( pkcs11 ) )
DO_EXIT_0( "Failed to initialized pkcs11 engine" );
pkey = ENGINE_load_private_key( pkcs11, keyfile, NULL, NULL );
if (pkey == NULL)
DO_EXIT_1("Failed to load private key %s", keyfile);
if ((btmp = BIO_new_file(certfile, "rb")) == NULL ||
((p7 = d2i_PKCS7_bio(btmp, NULL)) == NULL &&
(certs = PEM_read_certs(btmp, "")) == NULL))
DO_EXIT_1("Failed to read certificate file: %s\n", certfile);
BIO_free(btmp);
} else {
if ((btmp = BIO_new_file(certfile, "rb")) == NULL ||
((p7 = d2i_PKCS7_bio(btmp, NULL)) == NULL &&
(certs = PEM_read_certs(btmp, "")) == NULL))
DO_EXIT_1("Failed to read certificate file: %s\n", certfile);
BIO_free(btmp);
if ((btmp = BIO_new_file(keyfile, "rb")) == NULL ||
( (pkey = d2i_PrivateKey_bio(btmp, NULL)) == NULL &&
(BIO_seek(btmp, 0) == 0) &&
(pkey = PEM_read_bio_PrivateKey(btmp, NULL, NULL, pass ? pass : "")) == NULL &&
(BIO_seek(btmp, 0) == 0) &&
(pkey = PEM_read_bio_PrivateKey(btmp, NULL, NULL, NULL)) == NULL))
DO_EXIT_1("Failed to read private key file: %s (Wrong password?)\n", keyfile);
BIO_free(btmp);
}
if (xcertfile) {
if ((btmp = BIO_new_file(xcertfile, "rb")) == NULL ||
((p7x = d2i_PKCS7_bio(btmp, NULL)) == NULL &&
(xcerts = PEM_read_certs(btmp, "")) == NULL))
DO_EXIT_1("Failed to read cross certificate file: %s\n", xcertfile);
BIO_free(btmp);
}
if (pass) {
memset (pass, 0, strlen(pass));
pass = NULL;
}
}
if (certs == NULL && p7 != NULL)
certs = p7->d.sign->cert;
/* Check if indata is cab or pe */
filesize = get_file_size(infile);
if (filesize == 0)
goto err_cleanup;
indata = map_file(infile, filesize);
if (indata == NULL)
DO_EXIT_1("Failed to open file: %s\n", infile);
fileend = filesize;
if (!memcmp(indata, "MSCF", 4)) {
type = FILE_TYPE_CAB;
} else if (!memcmp(indata, "MZ", 2)) {
type = FILE_TYPE_PE;
} else if (!memcmp(indata, msi_signature, sizeof(msi_signature))) {
type = FILE_TYPE_MSI;
#ifdef WITH_GSF
gsf_init();
gsf_initialized = 1;
#endif
} else {
DO_EXIT_1("Unrecognized file type: %s\n", infile);
}
if (cmd != CMD_SIGN && !(type == FILE_TYPE_PE || type == FILE_TYPE_MSI))
DO_EXIT_1("Command is not supported for non-PE/non-MSI files: %s\n", infile);
hash = BIO_new(BIO_f_md());
BIO_set_md(hash, md);
if (type == FILE_TYPE_CAB) {
if (filesize < 44)
DO_EXIT_1("Corrupt cab file - too short: %s\n", infile);
if (indata[0x1e] != 0x00 || indata[0x1f] != 0x00)
DO_EXIT_0("Cannot sign cab files with flag bits set!\n"); /* XXX */
} else if (type == FILE_TYPE_PE) {
if (filesize < 64)
DO_EXIT_1("Corrupt DOS file - too short: %s\n", infile);
peheader = GET_UINT32_LE(indata+60);
if (filesize < peheader + 160)
DO_EXIT_1("Corrupt PE file - too short: %s\n", infile);
if (memcmp(indata+peheader, "PE\0\0", 4))
DO_EXIT_1("Unrecognized DOS file type: %s\n", infile);
} else if (type == FILE_TYPE_MSI) {
#ifdef WITH_GSF
GsfInput *src;
GsfInfile *ole;
BIO_push(hash, BIO_new(BIO_s_null()));
src = gsf_input_stdio_new(infile, NULL);
if (!src)
DO_EXIT_1("Error opening file %s", infile);
ole = gsf_infile_msole_new(src, NULL);
if (cmd == CMD_EXTRACT) {
if(output_pkcs7) {
sig = msi_extract_signature_to_pkcs7(ole);
if (!sig)
DO_EXIT_0("Unable to extract existing signature.");
outdata = BIO_new_file(outfile, "w+b");
if (outdata == NULL)
DO_EXIT_1("Unable to open %s\n\n", outfile);
ret = !PEM_write_bio_PKCS7(outdata, sig);
BIO_free_all(outdata);
}
else
ret = msi_extract_signature_to_file(ole, outfile);
goto skip_signing;
} else if (cmd == CMD_VERIFY) {
ret = msi_verify_file(ole, leafhash);
goto skip_signing;
} else if (cmd == CMD_SIGN || cmd == CMD_ADD || cmd == CMD_ATTACH) {
if (nest || cmd == CMD_ADD) {
// Perform a sanity check for the MsiDigitalSignatureEx section.
// If the file we're attempting to sign has an MsiDigitalSignatureEx
// section, we can't add a nested signature of a different MD type
// without breaking the initial signature.
{
unsigned long dselen = 0;
int has_dse = 0;
if (msi_extract_dse(ole, NULL, &dselen, &has_dse) != 0 && has_dse) {
DO_EXIT_0("Unable to extract MsiDigitalSigantureEx section.\n");
}
if (has_dse) {
int mdlen = EVP_MD_size(md);
if (dselen != (unsigned long)mdlen) {
DO_EXIT_0("Unable to add nested signature with a different MD type (-h parameter) "
"than what exists in the MSI file already. This is due to the presence of "
"MsiDigitalSigantureEx (-add-msi-dse parameter).\n");
}
}
}
cursig = msi_extract_signature_to_pkcs7(ole);
if (cursig == NULL) {
DO_EXIT_0("Unable to extract existing signature in -nest mode");
}
if (cmd == CMD_ADD) {
sig = cursig;
}
}
}
sink = gsf_output_stdio_new(outfile, NULL);
if (!sink)
DO_EXIT_1("Error opening output file %s", outfile);
outole = gsf_outfile_msole_new(sink);
/*
* MsiDigitalSignatureEx is an enhanced signature type that
* can be used when signing MSI files. In addition to
* file content, it also hashes some file metadata, specifically
* file names, file sizes, creation times and modification times.
*
* The file content hashing part stays the same, so the
* msi_handle_dir() function can be used across both variants.
*
* When an MsiDigitalSigntaureEx section is present in an MSI file,
* the meaning of the DigitalSignature section changes: Instead
* of being merely a file content hash (as what is output by the
* msi_handle_dir() function), it is now hashes both content
* and metadata.
*
* Here is how it works:
*
* First, a "pre-hash" is calculated. This is the "metadata" hash.
* It iterates over the files in the MSI in the same order as the
* file content hashing method would - but it only processes the
* metadata.
*
* Once the pre-hash is calculated, a new hash is created for
* calculating the hash of the file content. The output of the
* pre-hash is added as the first element of the file content hash.
*
* After the pre-hash is written, what follows is the "regular"
* stream of data that would normally be written when performing
* file content hashing.
*
* The output of this hash, which combines both metadata and file
* content, is what will be output in signed form to the
* DigitalSignature section when in 'MsiDigitalSignatureEx' mode.
*
* As mentioned previously, this new mode of operation is signalled
* by the presence of a 'MsiDigitalSignatureEx' section in the MSI
* file. This section must come after the 'DigitalSignature'
* section, and its content must be the output of the pre-hash
* ("metadata") hash.
*/
if (add_msi_dse) {
BIO *prehash = BIO_new(BIO_f_md());
BIO_set_md(prehash, md);
BIO_push(prehash, BIO_new(BIO_s_null()));
if (!msi_prehash(ole, NULL, prehash))
DO_EXIT_0("unable to calculate MSI pre-hash ('metadata') hash.\n");
p_msiex = malloc(EVP_MAX_MD_SIZE);
len_msiex = BIO_gets(prehash, (char*)p_msiex, EVP_MAX_MD_SIZE);
BIO_write(hash, p_msiex, len_msiex);
}
if (!msi_handle_dir(ole, outole, hash)) {
DO_EXIT_0("unable to msi_handle_dir()\n");
}
if (cmd == CMD_REMOVE) {
gsf_output_close(GSF_OUTPUT(outole));
g_object_unref(sink);
}
#else
DO_EXIT_1("libgsf is not available, msi support is disabled: %s\n", infile);
#endif
}
if (type == FILE_TYPE_CAB || type == FILE_TYPE_PE) {
if (cmd != CMD_VERIFY) {
/* Create outdata file */
outdata = BIO_new_file(outfile, "w+b");
if (outdata == NULL)
DO_EXIT_1("Failed to create file: %s\n", outfile);
BIO_push(hash, outdata);
}
}
if (type == FILE_TYPE_CAB) {
unsigned short nfolders;
u_char cabsigned[] = {
0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
0xde, 0xad, 0xbe, 0xef, /* size of cab file */
0xde, 0xad, 0xbe, 0xef, /* size of asn1 blob */
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
BIO_write(hash, indata, 4);
BIO_write(outdata, indata+4, 4);
tmp = GET_UINT32_LE(indata+8) + 24;
PUT_UINT32_LE(tmp, buf);
BIO_write(hash, buf, 4);
BIO_write(hash, indata+12, 4);
tmp = GET_UINT32_LE(indata+16) + 24;
PUT_UINT32_LE(tmp, buf+4);
BIO_write(hash, buf+4, 4);
memcpy(buf+4, indata+20, 14);
buf[4+10] = 0x04; /* RESERVE_PRESENT */
BIO_write(hash, buf+4, 14);
BIO_write(outdata, indata+34, 2);
memcpy(cabsigned+8, buf, 4);
BIO_write(outdata, cabsigned, 20);
BIO_write(hash, cabsigned+20, 4); /* ??? or possibly the previous 4 bytes instead? */
nfolders = indata[26] | (indata[27] << 8);
for (i = 36; nfolders; nfolders--, i+=8) {
tmp = GET_UINT32_LE(indata+i);
tmp += 24;
PUT_UINT32_LE(tmp, buf);
BIO_write(hash, buf, 4);
BIO_write(hash, indata+i+4, 4);
}
/* Write what's left */
BIO_write(hash, indata+i, filesize-i);
} else if (type == FILE_TYPE_PE) {
unsigned int sigpos, siglen, nrvas;
unsigned short magic;
if (jp >= 0)
fprintf(stderr, "Warning: -jp option is only valid "
"for CAB files.\n");
magic = GET_UINT16_LE(indata + peheader + 24);
if (magic == 0x20b) {
pe32plus = 1;
} else if (magic == 0x10b) {
pe32plus = 0;
} else {
DO_EXIT_2("Corrupt PE file - found unknown magic %x: %s\n", magic, infile);
}
nrvas = GET_UINT32_LE(indata + peheader + 116 + pe32plus*16);
if (nrvas < 5)
DO_EXIT_1("Can not handle PE files without certificate table resource: %s\n", infile);
sigpos = GET_UINT32_LE(indata + peheader + 152 + pe32plus*16);
siglen = GET_UINT32_LE(indata + peheader + 152 + pe32plus*16 + 4);
/* Since fix for MS Bulletin MS12-024 we can really assume
that signature should be last part of file */
if (sigpos > 0 && sigpos + siglen != filesize)
DO_EXIT_1("Corrupt PE file - current signature not at end of file: %s\n", infile);
if ((cmd == CMD_REMOVE || cmd == CMD_EXTRACT) && sigpos == 0)
DO_EXIT_1("PE file does not have any signature: %s\n", infile);
if (cmd == CMD_EXTRACT) {
/* A lil' bit of ugliness. Reset stream, write signature and skip forward */
(void)BIO_reset(outdata);
if(output_pkcs7) {
sig = extract_existing_pe_pkcs7(indata, peheader, pe32plus, sigpos ? sigpos : fileend, siglen);
if (!sig)
DO_EXIT_0("Unable to extract existing signature.");
PEM_write_bio_PKCS7(outdata, sig);
}
else
BIO_write(outdata, indata + sigpos, siglen);
goto skip_signing;
}
if ((cmd == CMD_SIGN && nest) || (cmd == CMD_ATTACH && nest) || cmd == CMD_ADD) {
cursig = extract_existing_pe_pkcs7(indata, peheader, pe32plus, sigpos ? sigpos : fileend, siglen);
if (cursig == NULL) {
DO_EXIT_0("Unable to extract existing signature in -nest mode");
}
if (cmd == CMD_ADD) {
sig = cursig;
}
}
if (cmd == CMD_VERIFY) {
ret = verify_pe_file(indata, peheader, pe32plus, sigpos ? sigpos : fileend, siglen, leafhash);
goto skip_signing;
}
if (sigpos > 0) {
/* Strip current signature */
fileend = sigpos;
}
BIO_write(hash, indata, peheader + 88);
i = peheader + 88;
memset(buf, 0, 4);
BIO_write(outdata, buf, 4); /* zero out checksum */
i += 4;
BIO_write(hash, indata + i, 60+pe32plus*16);
i += 60+pe32plus*16;
memset(buf, 0, 8);
BIO_write(outdata, buf, 8); /* zero out sigtable offset + pos */
i += 8;
BIO_write(hash, indata + i, fileend - i);
/* pad (with 0's) pe file to 8 byte boundary */
len = 8 - fileend % 8;
if (len > 0 && len != 8) {
memset(buf, 0, len);
BIO_write(hash, buf, len);
fileend += len;
}
}
if (cmd == CMD_ADD)
goto add_only;
if(cmd == CMD_ATTACH)
{
const char pemhdr[] = "-----BEGIN PKCS7-----";
sigfilesize = get_file_size(sigfile);
if(!sigfilesize)
goto err_cleanup;
insigdata = map_file(sigfile, sigfilesize);
if (insigdata == NULL)
DO_EXIT_1("Failed to open file: %s\n", infile);
if (sigfilesize >= sizeof(pemhdr) && !memcmp(insigdata, pemhdr, sizeof(pemhdr)-1))
{
sigbio = BIO_new_mem_buf(insigdata, sigfilesize);
sig = PEM_read_bio_PKCS7(sigbio, NULL, NULL, NULL);
BIO_free_all(sigbio);
}
else {
if (type == FILE_TYPE_PE) {
sig = extract_existing_pe_pkcs7(insigdata, peheader, pe32plus, 0, sigfilesize);
}
else if (type == FILE_TYPE_MSI) {
#ifdef WITH_GSF
const unsigned char *p = (unsigned char*)insigdata;
sig = d2i_PKCS7(NULL, &p, sigfilesize);
#else
DO_EXIT_1("libgsf is not available, msi support is disabled: %s\n", infile);
#endif
}
}
if (!sig)
DO_EXIT_0("No valid signature found.");
goto add_only;
}
if (cmd != CMD_SIGN)
goto skip_signing;
sig = PKCS7_new();
PKCS7_set_type(sig, NID_pkcs7_signed);
si = NULL;
if (cert != NULL)
si = PKCS7_add_signature(sig, cert, pkey, md);
if (si == NULL) {
for (i=0; i<sk_X509_num(certs); i++) {
X509 *signcert = sk_X509_value(certs, i);
/* X509_print_fp(stdout, signcert); */
si = PKCS7_add_signature(sig, signcert, pkey, md);
if (si != NULL) break;
}
}
EVP_PKEY_free(pkey);
pkey = NULL;
if (si == NULL)
DO_EXIT_0("Signing failed(PKCS7_add_signature)\n");
PKCS7_add_signed_attribute
(si, NID_pkcs9_contentType,
V_ASN1_OBJECT, OBJ_txt2obj(SPC_INDIRECT_DATA_OBJID, 1));
if (type == FILE_TYPE_CAB && jp >= 0) {
const u_char *attrs = NULL;
static const u_char java_attrs_low[] = {
0x30, 0x06, 0x03, 0x02, 0x00, 0x01, 0x30, 0x00
};
switch (jp) {
case 0:
attrs = java_attrs_low;
len = sizeof(java_attrs_low);
break;
case 1:
/* XXX */
case 2:
/* XXX */
default:
break;
}
if (attrs) {
astr = ASN1_STRING_new();
ASN1_STRING_set(astr, attrs, len);
PKCS7_add_signed_attribute
(si, OBJ_txt2nid(SPC_MS_JAVA_SOMETHING),
V_ASN1_SEQUENCE, astr);
}
}
astr = ASN1_STRING_new();
if (comm) {
ASN1_STRING_set(astr, purpose_comm, sizeof(purpose_comm));
} else {
ASN1_STRING_set(astr, purpose_ind, sizeof(purpose_ind));
}
PKCS7_add_signed_attribute(si, OBJ_txt2nid(SPC_STATEMENT_TYPE_OBJID),
V_ASN1_SEQUENCE, astr);
if (desc || url) {
SpcSpOpusInfo *opus = createOpus(desc, url);
if ((len = i2d_SpcSpOpusInfo(opus, NULL)) <= 0 ||
(p = OPENSSL_malloc(len)) == NULL)
DO_EXIT_0("Couldn't allocate memory for opus info\n");
i2d_SpcSpOpusInfo(opus, &p);
p -= len;
astr = ASN1_STRING_new();
ASN1_STRING_set(astr, p, len);
OPENSSL_free(p);
PKCS7_add_signed_attribute(si, OBJ_txt2nid(SPC_SP_OPUS_INFO_OBJID),
V_ASN1_SEQUENCE, astr);
SpcSpOpusInfo_free(opus);
}
PKCS7_content_new(sig, NID_pkcs7_data);
if (cert != NULL) {
PKCS7_add_certificate(sig, cert);
X509_free(cert);
cert = NULL;
}
if (xcerts) {
for(i = sk_X509_num(xcerts)-1; i>=0; i--)
PKCS7_add_certificate(sig, sk_X509_value(xcerts, i));
}
for(i = sk_X509_num(certs)-1; i>=0; i--)
PKCS7_add_certificate(sig, sk_X509_value(certs, i));
if (p7 == NULL || certs != p7->d.sign->cert) {
sk_X509_free(certs);
certs = NULL;
}
if (p7) {
PKCS7_free(p7);
p7 = NULL;
}
if (xcerts) {
sk_X509_free(xcerts);
xcerts = NULL;
}
if (p7x) {
PKCS7_free(p7x);
p7x = NULL;
}
get_indirect_data_blob(&p, &len, md, type, pagehash, indata, peheader, pe32plus, fileend);
len -= EVP_MD_size(md);
memcpy(buf, p, len);
OPENSSL_free(p);
unsigned char mdbuf[EVP_MAX_MD_SIZE];
int mdlen = BIO_gets(hash, (char*)mdbuf, EVP_MAX_MD_SIZE);
memcpy(buf+len, mdbuf, mdlen);
int seqhdrlen = asn1_simple_hdr_len((unsigned char*)buf, len);
if ((sigbio = PKCS7_dataInit(sig, NULL)) == NULL)
DO_EXIT_0("Signing failed(PKCS7_dataInit)\n");
BIO_write(sigbio, buf+seqhdrlen, len-seqhdrlen+mdlen);
(void)BIO_flush(sigbio);
if (!PKCS7_dataFinal(sig, sigbio))
DO_EXIT_0("Signing failed(PKCS7_dataFinal)\n");
BIO_free_all(sigbio);
/* replace the data part with the MS Authenticode
spcIndirectDataContext blob */
PKCS7 *td7 = PKCS7_new();
td7->type = OBJ_txt2obj(SPC_INDIRECT_DATA_OBJID, 1);
td7->d.other = ASN1_TYPE_new();
td7->d.other->type = V_ASN1_SEQUENCE;
td7->d.other->value.sequence = ASN1_STRING_new();
ASN1_STRING_set(td7->d.other->value.sequence, buf, len+mdlen);
PKCS7_set_content(sig, td7);
add_only:
#ifdef ENABLE_CURL
/* add counter-signature/timestamp */
if (nturl && add_timestamp_authenticode(sig, turl, nturl, proxy, noverifypeer))
DO_EXIT_0("authenticode timestamping failed\n");
if (ntsurl && add_timestamp_rfc3161(sig, tsurl, ntsurl, proxy, md, noverifypeer))
DO_EXIT_0("RFC 3161 timestamping failed\n");
#endif
if (addBlob && add_unauthenticated_blob(sig))
DO_EXIT_0("Adding unauthenticated blob failed\n");
#if 0
if (!PEM_write_PKCS7(stdout, sig))
DO_EXIT_0("PKCS7 output failed\n");
#endif
if (nest) {
if (cursig == NULL) {
DO_EXIT_0("no 'cursig' was extracted. this points to a bug in the code. aborting...\n")
}
if (pkcs7_set_nested_signature(cursig, sig) == 0)
DO_EXIT_0("unable to append the nested signature to the current signature\n");
outsig = cursig;
} else {
outsig = sig;
}
/* Append signature to outfile */
if (((len = i2d_PKCS7(outsig, NULL)) <= 0) ||
(p = OPENSSL_malloc(len)) == NULL)
DO_EXIT_1("i2d_PKCS - memory allocation failed: %d\n", len);
i2d_PKCS7(outsig, &p);
p -= len;
padlen = (8 - len%8) % 8;
if (type == FILE_TYPE_PE) {
PUT_UINT32_LE(len+8+padlen, buf);
PUT_UINT16_LE(WIN_CERT_REVISION_2, buf + 4);
PUT_UINT16_LE(WIN_CERT_TYPE_PKCS_SIGNED_DATA, buf + 6);
BIO_write(outdata, buf, 8);
}
if (type == FILE_TYPE_PE || type == FILE_TYPE_CAB) {
BIO_write(outdata, p, len);
/* pad (with 0's) asn1 blob to 8 byte boundary */
if (padlen > 0) {
memset(p, 0, padlen);
BIO_write(outdata, p, padlen);
}
#ifdef WITH_GSF
} else if (type == FILE_TYPE_MSI) {
/* Only output signatures if we're signing. */
if (cmd == CMD_SIGN || cmd == CMD_ADD || cmd == CMD_ATTACH) {
GsfOutput *child = gsf_outfile_new_child(outole, "\05DigitalSignature", FALSE);
if (!gsf_output_write(child, len, p))
DO_EXIT_1("Failed to write MSI 'DigitalSignature' signature to %s", infile);
gsf_output_close(child);
if (p_msiex != NULL) {
child = gsf_outfile_new_child(outole, "\05MsiDigitalSignatureEx", FALSE);
if (!gsf_output_write(child, len_msiex, p_msiex)) {
DO_EXIT_1("Failed to write MSI 'MsiDigitalSignatureEx' signature to %s", infile);
}
gsf_output_close(child);
}
gsf_output_close(GSF_OUTPUT(outole));
g_object_unref(sink);
}
#endif
}
PKCS7_free(sig);
OPENSSL_free(p);
skip_signing:
if (type == FILE_TYPE_PE) {
if (cmd == CMD_SIGN || cmd == CMD_ADD || cmd == CMD_ATTACH) {
/* Update signature position and size */
(void)BIO_seek(outdata, peheader+152+pe32plus*16);
PUT_UINT32_LE(fileend, buf); /* Previous file end = signature table start */
BIO_write(outdata, buf, 4);
PUT_UINT32_LE(len+8+padlen, buf);
BIO_write(outdata, buf, 4);
}
if (cmd == CMD_SIGN || cmd == CMD_REMOVE || cmd == CMD_ADD || cmd == CMD_ATTACH)
recalc_pe_checksum(outdata, peheader);
} else if (type == FILE_TYPE_CAB) {
(void)BIO_seek(outdata, 0x30);
PUT_UINT32_LE(len+padlen, buf);
BIO_write(outdata, buf, 4);
}
BIO_free_all(hash);
hash = outdata = NULL;
if (cmd == CMD_ATTACH) {
if (type == FILE_TYPE_PE) {
outdatasize = get_file_size(outfile);
if (!outdatasize)
DO_EXIT_0("Error verifying result.\n");
outdataverify = map_file(outfile, outdatasize);
if (!outdataverify)
DO_EXIT_0("Error verifying result.\n");
int sigpos = GET_UINT32_LE(outdataverify + peheader + 152 + pe32plus*16);
int siglen = GET_UINT32_LE(outdataverify + peheader + 152 + pe32plus*16 + 4);
ret = verify_pe_file(outdataverify, peheader, pe32plus, sigpos, siglen, leafhash);
if (ret) {
DO_EXIT_0("Signature mismatch.\n");
}
}
else if (type == FILE_TYPE_MSI)
{
#ifdef WITH_GSF
GsfInput *src;
GsfInfile *ole;
src = gsf_input_stdio_new(outfile, NULL);
if (!src)
DO_EXIT_1("Error opening file %s", outfile);
ole = gsf_infile_msole_new(src, NULL);
g_object_unref(src);
ret = msi_verify_file(ole, leafhash);
g_object_unref(ole);
if (ret) {
DO_EXIT_0("Signature mismatch.\n");
}
#else
DO_EXIT_1("libgsf is not available, msi support is disabled: %s\n", infile);
#endif
}
else
{
DO_EXIT_1("Unknown input type for file: %s\n", infile);
}
printf("Signature successfully attached.\n");
}
else
printf(ret ? "Failed\n" : "Succeeded\n");
cleanup_lib_state();
return ret;
err_cleanup:
if (pass) {
memset (pass, 0, strlen(pass));
pass = NULL;
}
ERR_print_errors_fp(stderr);
if (certs && (p7 == NULL || certs != p7->d.sign->cert))
sk_X509_free(certs);
if (p7)
PKCS7_free(p7);
if (p7x)
PKCS7_free(p7x);
if (xcerts)
sk_X509_free(xcerts);
if (cert)
X509_free(cert);
if (pkey)
EVP_PKEY_free(pkey);
if (hash)
BIO_free_all(hash);
if (outfile)
unlink(outfile);
fprintf(stderr, "\nFailed\n");
cleanup_lib_state();
return -1;
}
/*
Local Variables:
c-basic-offset: 4
tab-width: 4
indent-tabs-mode: t
End:
vim: set ts=4 :
*/