modify and verify CAB header

This commit is contained in:
olszomal 2020-03-04 13:28:46 +01:00
parent 73cf4e9540
commit 150d14b57c

View File

@ -166,6 +166,24 @@ typedef unsigned char u_char;
#define WIN_CERT_REVISION_2 0x0200 #define WIN_CERT_REVISION_2 0x0200
#define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002 #define WIN_CERT_TYPE_PKCS_SIGNED_DATA 0x0002
/*
* FLAG_PREV_CABINET is set if the cabinet file is not the first in a set
* of cabinet files. When this bit is set, the szCabinetPrev and szDiskPrev
* fields are present in this CFHEADER.
*/
#define FLAG_PREV_CABINET 0x0001
/*
* FLAG_NEXT_CABINET is set if the cabinet file is not the last in a set of
* cabinet files. When this bit is set, the szCabinetNext and szDiskNext
* fields are present in this CFHEADER.
*/
#define FLAG_NEXT_CABINET 0x0002
/*
* FLAG_RESERVE_PRESENT is set if the cabinet file contains any reserved
* fields. When this bit is set, the cbCFHeader, cbCFFolder, and cbCFData
* fields are present in this CFHEADER.
*/
#define FLAG_RESERVE_PRESENT 0x0004
/* /*
ASN.1 definitions (more or less from official MS Authenticode docs) ASN.1 definitions (more or less from official MS Authenticode docs)
@ -1154,6 +1172,8 @@ static void help_for(const char *argv0, const char *cmd) {
#define DO_EXIT_1(x, y) { fprintf(stderr, x, y); 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 DO_EXIT_2(x, y, z) { fprintf(stderr, x, y, z); goto err_cleanup; }
#define GET_UINT8_LE(p) ((u_char*)(p))[0]
#define GET_UINT16_LE(p) (((u_char*)(p))[0] | (((u_char*)(p))[1]<<8)) #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) | \ #define GET_UINT32_LE(p) (((u_char*)(p))[0] | (((u_char*)(p))[1]<<8) | \
@ -2904,11 +2924,75 @@ static void sign_pe_file(char *indata, size_t i , int pe32plus, size_t *fileend,
* CAB file support * CAB file support
* https://www.file-recovery.com/cab-signature-format.htm * https://www.file-recovery.com/cab-signature-format.htm
*/ */
static int verify_cab_header(char *indata, char *infile, cmd_type_t cmd,
size_t filesize, size_t *header_size, size_t *sigpos, size_t *siglen)
{
int ret = 1;
size_t reserved, flags = 0;
if (filesize < 44) {
printf("Corrupt cab file - too short: %s\n", infile);
ret = 0; /* FAILED */
}
reserved = GET_UINT32_LE(indata + 4);
if (reserved) {
printf("Reserved1: 0x%08lX\n", reserved);
ret = 0; /* FAILED */
}
/* flags specify bit-mapped values that indicate the presence of optional data */
flags = GET_UINT16_LE(indata + 30);
#if 1
if (flags & FLAG_PREV_CABINET) {
/* FLAG_NEXT_CABINET works */
printf("Multivolume cabinet file is unsupported: flags 0x%04lX\n", flags);
ret = 0; /* FAILED */
}
#endif
if (!(flags & FLAG_RESERVE_PRESENT) &&
(cmd == CMD_REMOVE || cmd == CMD_EXTRACT)) {
printf("CAB file does not have any signature: %s\n", infile);
ret = 0; /* FAILED */
}
if (flags & FLAG_RESERVE_PRESENT) {
/*
* Additional headers is located at offset 36 (cbCFHeader, cbCFFolder, cbCFData);
* size of header (4 bytes, little-endian order) must be 20 (checkpoint).
*/
*header_size = GET_UINT32_LE(indata + 36);
if (*header_size != 20) {
printf("Additional header size: 0x%08lX\n", *header_size);
ret = 0; /* FAILED */
}
reserved = GET_UINT32_LE(indata + 40);
if (reserved != 0x00100000) {
printf("abReserved: 0x%08lX\n", reserved);
ret = 0; /* FAILED */
}
/*
* File size is defined at offset 8, however if additional header exists, this size is not valid.
* sigpos - additional data offset is located at offset 44 (from file beginning)
* and consist of 4 bytes (little-endian order)
* siglen - additional data size is located at offset 48 (from file beginning)
* and consist of 4 bytes (little-endian order)
* If there are additional headers, size of the CAB archive file is calcualted
* as additional data offset plus additional data size.
*/
*sigpos = GET_UINT32_LE(indata + 44);
*siglen = GET_UINT32_LE(indata + 48);
if (*sigpos < filesize && *sigpos + *siglen != filesize) {
printf("Additional data offset:\t%lu bytes\nAdditional data size:\t%lu bytes\n", *sigpos, *siglen);
printf("File size:\t\t%lu bytes\n", filesize);
ret = 0; /* FAILED */
}
}
return ret;
}
static int calc_cab_digest(BIO *bio, const EVP_MD *md, unsigned char *mdbuf, size_t offset) static int calc_cab_digest(BIO *bio, const EVP_MD *md, unsigned char *mdbuf, size_t offset)
{ {
int ret = 0; size_t coffFiles, nfolders, flags;
unsigned int i;
size_t folders, flags;
static unsigned char bfb[16*1024*1024]; static unsigned char bfb[16*1024*1024];
EVP_MD_CTX *mdctx; EVP_MD_CTX *mdctx;
mdctx = EVP_MD_CTX_new(); mdctx = EVP_MD_CTX_new();
@ -2925,28 +3009,31 @@ static int calc_cab_digest(BIO *bio, const EVP_MD *md, unsigned char *mdbuf, siz
/* /*
* u4 cbCabinet - size of this cabinet file in bytes: 8-11 * u4 cbCabinet - size of this cabinet file in bytes: 8-11
* u4 reserved2 00000000: 12-15 * u4 reserved2 00000000: 12-15
* u4 coffFiles - offset of the first CFFILE entry: 16-19 */
BIO_read(bio, bfb, 8);
EVP_DigestUpdate(mdctx, bfb, 8);
/* u4 coffFiles - offset of the first CFFILE entry: 16-19 */
BIO_read(bio, bfb, 4);
coffFiles = GET_UINT32_LE(bfb);
EVP_DigestUpdate(mdctx, bfb, 4);
/*
* u4 reserved3 00000000: 20-23 * u4 reserved3 00000000: 20-23
* u1 versionMinor 03: 24 * u1 versionMinor 03: 24
* u1 versionMajor 01: 25 * u1 versionMajor 01: 25
*/ */
BIO_read(bio, bfb, 18); BIO_read(bio, bfb, 6);
EVP_DigestUpdate(mdctx, bfb, 18); EVP_DigestUpdate(mdctx, bfb, 6);
/* u2 cFolders - number of CFFOLDER entries in this cabinet: 26-27 */ /* u2 cFolders - number of CFFOLDER entries in this cabinet: 26-27 */
BIO_read(bio, bfb, 2); BIO_read(bio, bfb, 2);
nfolders = GET_UINT16_LE(bfb);
EVP_DigestUpdate(mdctx, bfb, 2); EVP_DigestUpdate(mdctx, bfb, 2);
folders = GET_UINT16_LE(bfb);
/* u2 cFiles - number of CFFILE entries in this cabinet: 28-29 */ /* u2 cFiles - number of CFFILE entries in this cabinet: 28-29 */
BIO_read(bio, bfb, 2); BIO_read(bio, bfb, 2);
EVP_DigestUpdate(mdctx, bfb, 2); EVP_DigestUpdate(mdctx, bfb, 2);
/* u2 flags 0400 FLAG_RESERVE_PRESENT: 30-31 */ /* u2 flags: 30-31 */
BIO_read(bio, bfb, 2); BIO_read(bio, bfb, 2);
EVP_DigestUpdate(mdctx, bfb, 2);
flags = GET_UINT16_LE(bfb); flags = GET_UINT16_LE(bfb);
if (flags != 4) { EVP_DigestUpdate(mdctx, bfb, 2);
printf("Checkpoint error: flags 0x%04lX\n", flags);
ret = 1; /* FAILED */
}
/* u2 setID must be the same for all cabinets in a set: 32-33 */ /* u2 setID must be the same for all cabinets in a set: 32-33 */
BIO_read(bio, bfb, 2); BIO_read(bio, bfb, 2);
EVP_DigestUpdate(mdctx, bfb, 2); EVP_DigestUpdate(mdctx, bfb, 2);
@ -2963,30 +3050,57 @@ static int calc_cab_digest(BIO *bio, const EVP_MD *md, unsigned char *mdbuf, siz
/* u22 abReserve: 56-59 */ /* u22 abReserve: 56-59 */
BIO_read(bio, bfb, 4); BIO_read(bio, bfb, 4);
EVP_DigestUpdate(mdctx, bfb, 4); EVP_DigestUpdate(mdctx, bfb, 4);
/* TODO */
if (flags & FLAG_PREV_CABINET) {
/* szCabinetPrev */
do {
BIO_read(bio, bfb, 1);
EVP_DigestUpdate(mdctx, bfb, 1);
} while (bfb[0]);
/* szDiskPrev */
do {
BIO_read(bio, bfb, 1);
EVP_DigestUpdate(mdctx, bfb, 1);
} while (bfb[0]);
}
if (flags & FLAG_NEXT_CABINET) {
/* szCabinetNext */
do {
BIO_read(bio, bfb, 1);
EVP_DigestUpdate(mdctx, bfb, 1);
} while (bfb[0]);
/* szDiskNext */
do {
BIO_read(bio, bfb, 1);
EVP_DigestUpdate(mdctx, bfb, 1);
} while (bfb[0]);
}
/* /*
* (u8 * cFolders) CFFOLDER - structure contains information about * (u8 * cFolders) CFFOLDER - structure contains information about
* one of the folders or partial folders stored in this cabinet file * one of the folders or partial folders stored in this cabinet file
*/ */
for (i = 60; folders; folders--, i+=8) { while (nfolders) {
BIO_read(bio, bfb, 8); BIO_read(bio, bfb, 8);
EVP_DigestUpdate(mdctx, bfb, 8); EVP_DigestUpdate(mdctx, bfb, 8);
nfolders--;
} }
/* (variable) ab - the compressed data bytes */ /* (variable) ab - the compressed data bytes */
while (i < offset) { while (coffFiles < offset) {
size_t want = offset - i; size_t want = offset - coffFiles;
if (want > sizeof(bfb)) if (want > sizeof(bfb))
want = sizeof(bfb); want = sizeof(bfb);
int l = BIO_read(bio, bfb, want); int l = BIO_read(bio, bfb, want);
if (l <= 0) if (l <= 0)
break; break;
EVP_DigestUpdate(mdctx, bfb, l); EVP_DigestUpdate(mdctx, bfb, l);
i += l; coffFiles += l;
} }
EVP_DigestFinal(mdctx, mdbuf, NULL); EVP_DigestFinal(mdctx, mdbuf, NULL);
EVP_MD_CTX_free(mdctx); EVP_MD_CTX_free(mdctx);
return ret; return 0; /* OK */
} }
static int verify_cab_pkcs7(PKCS7 *p7, char *indata, size_t sigpos, char *leafhash, static int verify_cab_pkcs7(PKCS7 *p7, char *indata, size_t sigpos, char *leafhash,
@ -3068,13 +3182,13 @@ static PKCS7 *extract_existing_cab_pkcs7(char *indata, size_t sigpos, size_t sig
return p7; return p7;
} }
static int verify_cab_file(char *indata, size_t sigpos, size_t siglen, static int verify_cab_file(char *indata, size_t header_size, size_t sigpos, size_t siglen,
char *leafhash, char *cafile, char *crlfile, char *untrusted) char *leafhash, char *cafile, char *crlfile, char *untrusted)
{ {
PKCS7 *p7; PKCS7 *p7;
int ret = 0; int ret = 0;
if (siglen == 0) { if (header_size != 20) {
printf("No signature found\n\n"); printf("No signature found\n\n");
return 1; /* FAILED */ return 1; /* FAILED */
} }
@ -3110,11 +3224,52 @@ static int extract_cab_file(char *indata, size_t sigpos, size_t siglen, BIO *out
return ret; return ret;
} }
static void write_optional_names(size_t flags, char *indata, BIO *outdata, int *len)
{
int i;
i = *len;
/* TODO */
if (flags & FLAG_PREV_CABINET) {
/* szCabinetPrev */
while (GET_UINT8_LE(indata+i)) {
BIO_write(outdata, indata+i, 1);
i++;
}
BIO_write(outdata, indata+i, 1);
i++;
/* szDiskPrev */
while (GET_UINT8_LE(indata+i)) {
BIO_write(outdata, indata+i, 1);
i++;
}
BIO_write(outdata, indata+i, 1);
i++;
}
if (flags & FLAG_NEXT_CABINET) {
/* szCabinetNext */
while (GET_UINT8_LE(indata+i)) {
BIO_write(outdata, indata+i, 1);
i++;
}
BIO_write(outdata, indata+i, 1);
i++;
/* szDiskNext */
while (GET_UINT8_LE(indata+i)) {
BIO_write(outdata, indata+i, 1);
i++;
}
BIO_write(outdata, indata+i, 1);
i++;
}
*len = i;
}
static void remove_cab_file(char *indata, size_t siglen, size_t filesize, BIO *outdata) static void remove_cab_file(char *indata, size_t siglen, size_t filesize, BIO *outdata)
{ {
int i; int i;
unsigned short nfolders; unsigned short nfolders;
size_t tmp; size_t tmp, flags;
static char buf[64*1024]; static char buf[64*1024];
/* /*
@ -3129,7 +3284,8 @@ static void remove_cab_file(char *indata, size_t siglen, size_t filesize, BIO *o
/* u4 reserved2 00000000: 12-15 */ /* u4 reserved2 00000000: 12-15 */
BIO_write(outdata, indata+12, 4); BIO_write(outdata, indata+12, 4);
/* u4 coffFiles - offset of the first CFFILE entry: 16-19 */ /* u4 coffFiles - offset of the first CFFILE entry: 16-19 */
PUT_UINT32_LE(44, buf); tmp = GET_UINT32_LE(indata+16) - 24;
PUT_UINT32_LE(tmp, buf);
BIO_write(outdata, buf, 4); BIO_write(outdata, buf, 4);
/* /*
* u4 reserved3 00000000: 20-23 * u4 reserved3 00000000: 20-23
@ -3139,34 +3295,41 @@ static void remove_cab_file(char *indata, size_t siglen, size_t filesize, BIO *o
* u2 cFiles - number of CFFILE entries in this cabinet: 28-29 * u2 cFiles - number of CFFILE entries in this cabinet: 28-29
*/ */
BIO_write(outdata, indata+20, 10); BIO_write(outdata, indata+20, 10);
/* u2 flags 0000 FLAG_RESERVE_PRESENT: 30-31 */ /* u2 flags: 30-31 */
PUT_UINT16_LE(0, buf); flags = GET_UINT16_LE(indata+30);
PUT_UINT32_LE(flags & (FLAG_PREV_CABINET | FLAG_NEXT_CABINET), buf);
BIO_write(outdata, buf, 2); BIO_write(outdata, buf, 2);
/* /*
* u2 setID must be the same for all cabinets in a set: 32-33 * u2 setID must be the same for all cabinets in a set: 32-33
* u2 iCabinet - number of this cabinet file in a set: 34-35 * u2 iCabinet - number of this cabinet file in a set: 34-35
*/ */
BIO_write(outdata, indata+32, 4); BIO_write(outdata, indata+32, 4);
i = 60;
write_optional_names(flags, indata, outdata, &i);
/* /*
* (u8 * cFolders) CFFOLDER - structure contains information about * (u8 * cFolders) CFFOLDER - structure contains information about
* one of the folders or partial folders stored in this cabinet file * one of the folders or partial folders stored in this cabinet file
*/ */
nfolders = GET_UINT16_LE(indata + 26); nfolders = GET_UINT16_LE(indata + 26);
for (i = 60; nfolders; nfolders--, i+=8) { while (nfolders) {
tmp = GET_UINT32_LE(indata+i); tmp = GET_UINT32_LE(indata+i);
tmp -= 24; tmp -= 24;
PUT_UINT32_LE(tmp, buf); PUT_UINT32_LE(tmp, buf);
BIO_write(outdata, buf, 4); BIO_write(outdata, buf, 4);
BIO_write(outdata, indata+i+4, 4); BIO_write(outdata, indata+i+4, 4);
i+=8;
nfolders--;
} }
/* Write what's left - the compressed data bytes */ /* Write what's left - the compressed data bytes */
BIO_write(outdata, indata+i, filesize-siglen-i); BIO_write(outdata, indata+i, filesize-siglen-i);
} }
static void add_cab_file(char *indata, size_t fileend, BIO *hash, BIO *outdata) static void modify_cab_header(char *indata, size_t fileend, BIO *hash, BIO *outdata)
{ {
int i; int i;
unsigned short nfolders; unsigned short nfolders;
size_t flags;
static char buf[64*1024];
/* u1 signature[4] 4643534D MSCF: 0-3 */ /* u1 signature[4] 4643534D MSCF: 0-3 */
BIO_write(hash, indata, 4); BIO_write(hash, indata, 4);
@ -3181,39 +3344,48 @@ static void add_cab_file(char *indata, size_t fileend, BIO *hash, BIO *outdata)
* u1 versionMajor 01: 25 * u1 versionMajor 01: 25
* u2 cFolders - number of CFFOLDER entries in this cabinet: 26-27 * u2 cFolders - number of CFFOLDER entries in this cabinet: 26-27
* u2 cFiles - number of CFFILE entries in this cabinet: 28-29 * u2 cFiles - number of CFFILE entries in this cabinet: 28-29
* u2 flags 0400 FLAG_RESERVE_PRESENT: 30-31
* u2 setID must be the same for all cabinets in a set: 32-33
*/ */
BIO_write(hash, indata+8, 26); BIO_write(hash, indata+8, 22);
/* u2 flags: 30-31 */
flags = GET_UINT16_LE(indata+30);
PUT_UINT32_LE(flags, buf);
BIO_write(hash, buf, 2);
/* u2 setID must be the same for all cabinets in a set: 32-33 */
BIO_write(hash, indata+32, 2);
/* /*
* u2 iCabinet - number of this cabinet file in a set: 34-35 * u2 iCabinet - number of this cabinet file in a set: 34-35
* u2 cbCFHeader: 36-37 * u2 cbCFHeader: 36-37
* u1 cbCFFolder: 38 * u1 cbCFFolder: 38
* u1 cbCFData: 39 * u1 cbCFData: 39
* u22 abReserve: 40-55 * u22 abReserve: 40-43
* - Additional data offset: 44-47 * - Additional data offset: 44-47
* - Additional data size: 48-51 * - Additional data size: 48-51
*/ */
BIO_write(outdata, indata+34, 22); BIO_write(outdata, indata+34, 22);
/* u22 abReserve: 56-59 */ /* u24 abReserve: 52-59 */
BIO_write(hash, indata+56, 4); BIO_write(hash, indata+56, 4);
i = 60;
write_optional_names(flags, indata, hash, &i);
/* /*
* (u8 * cFolders) CFFOLDER - structure contains information about * (u8 * cFolders) CFFOLDER - structure contains information about
* one of the folders or partial folders stored in this cabinet file * one of the folders or partial folders stored in this cabinet file
*/ */
nfolders = GET_UINT16_LE(indata + 26); nfolders = GET_UINT16_LE(indata + 26);
for (i = 60; nfolders; nfolders--, i+=8) { while (nfolders) {
BIO_write(hash, indata+i, 8); BIO_write(hash, indata+i, 8);
i+=8;
nfolders--;
} }
/* Write what's left - the compressed data bytes */ /* Write what's left - the compressed data bytes */
BIO_write(hash, indata+i, fileend-i); BIO_write(hash, indata+i, fileend-i);
} }
static void sign_cab_file(char *indata, size_t fileend, BIO *hash, BIO *outdata) static void add_cab_header(char *indata, size_t fileend, BIO *hash, BIO *outdata)
{ {
int i; int i;
unsigned short nfolders; unsigned short nfolders;
size_t tmp; size_t tmp, flags;
static char buf[64*1024]; static char buf[64*1024];
u_char cabsigned[] = { u_char cabsigned[] = {
0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00,
@ -3224,6 +3396,7 @@ static void sign_cab_file(char *indata, size_t fileend, BIO *hash, BIO *outdata)
/* u1 signature[4] 4643534D MSCF: 0-3 */ /* u1 signature[4] 4643534D MSCF: 0-3 */
BIO_write(hash, indata, 4); BIO_write(hash, indata, 4);
/* u4 reserved1 00000000: 4-7 */
BIO_write(outdata, indata+4, 4); BIO_write(outdata, indata+4, 4);
/* u4 cbCabinet - size of this cabinet file in bytes: 8-11 */ /* u4 cbCabinet - size of this cabinet file in bytes: 8-11 */
tmp = GET_UINT32_LE(indata+8) + 24; tmp = GET_UINT32_LE(indata+8) + 24;
@ -3241,29 +3414,35 @@ static void sign_cab_file(char *indata, size_t fileend, BIO *hash, BIO *outdata)
* u1 versionMajor 01: 25 * u1 versionMajor 01: 25
* u2 cFolders - number of CFFOLDER entries in this cabinet: 26-27 * u2 cFolders - number of CFFOLDER entries in this cabinet: 26-27
* u2 cFiles - number of CFFILE entries in this cabinet: 28-29 * u2 cFiles - number of CFFILE entries in this cabinet: 28-29
* u2 flags 0000 FLAG_RESERVE_PRESENT: 30-31
* u2 setID must be the same for all cabinets in a set: 32-33
*/ */
memcpy(buf+4, indata+20, 14); memcpy(buf+4, indata+20, 10);
/* u2 flags 0400 FLAG_RESERVE_PRESENT */ flags = GET_UINT16_LE(indata+30);
buf[4+10] = 0x04; buf[4+10] = flags | FLAG_RESERVE_PRESENT;
/* u2 setID must be the same for all cabinets in a set: 32-33 */
memcpy(buf+16, indata+32, 2);
BIO_write(hash, buf+4, 14); BIO_write(hash, buf+4, 14);
/* u2 iCabinet - number of this cabinet file in a set: 34-35 */ /* u2 iCabinet - number of this cabinet file in a set: 34-35 */
BIO_write(outdata, indata+34, 2); BIO_write(outdata, indata+34, 2);
memcpy(cabsigned+8, buf, 4); memcpy(cabsigned+8, buf, 4);
BIO_write(outdata, cabsigned, 20); BIO_write(outdata, cabsigned, 20);
BIO_write(hash, cabsigned+20, 4); BIO_write(hash, cabsigned+20, 4);
i = 36;
write_optional_names(flags, indata, hash, &i);
/* /*
* (u8 * cFolders) CFFOLDER - structure contains information about * (u8 * cFolders) CFFOLDER - structure contains information about
* one of the folders or partial folders stored in this cabinet file * one of the folders or partial folders stored in this cabinet file
*/ */
nfolders = GET_UINT16_LE(indata + 26); nfolders = GET_UINT16_LE(indata + 26);
for (i = 36; nfolders; nfolders--, i+=8) { while (nfolders) {
tmp = GET_UINT32_LE(indata+i); tmp = GET_UINT32_LE(indata+i);
tmp += 24; tmp += 24;
PUT_UINT32_LE(tmp, buf); PUT_UINT32_LE(tmp, buf);
BIO_write(hash, buf, 4); BIO_write(hash, buf, 4);
BIO_write(hash, indata+i+4, 4); BIO_write(hash, indata+i+4, 4);
i+=8;
nfolders--;
} }
/* Write what's left - the compressed data bytes */ /* Write what's left - the compressed data bytes */
BIO_write(hash, indata+i, fileend-i); BIO_write(hash, indata+i, fileend-i);
@ -3442,10 +3621,10 @@ int main(int argc, char **argv) {
int ret = 0, i, len = 0, jp = -1, pe32plus = 0, comm = 0, pagehash = 0; int ret = 0, i, len = 0, jp = -1, pe32plus = 0, comm = 0, pagehash = 0;
size_t peheader = 0, padlen = 0; size_t peheader = 0, padlen = 0;
size_t filesize, fileend, sigfilesize, outdatasize; size_t filesize, fileend, sigfilesize, outdatasize;
size_t reserved1, flags, sigpos, siglen, header_size;
file_type_t type; file_type_t type;
cmd_type_t cmd = CMD_SIGN; cmd_type_t cmd = CMD_SIGN;
char *failarg = NULL; char *failarg = NULL;
size_t header_size = 0, sigpos = 0, siglen = 0;
static u_char purpose_ind[] = { static u_char purpose_ind[] = {
0x30, 0x0c, 0x30, 0x0c,
@ -3878,41 +4057,8 @@ int main(int argc, char **argv) {
if (add_msi_dse == 1) if (add_msi_dse == 1)
fprintf(stderr, "Warning: -add-msi-dse option is only valid for MSI files\n"); fprintf(stderr, "Warning: -add-msi-dse option is only valid for MSI files\n");
#endif #endif
if (filesize < 44) if (!verify_cab_header(indata, infile, cmd, filesize, &header_size, &sigpos, &siglen))
DO_EXIT_1("Corrupt cab file - too short: %s\n", infile); goto err_cleanup;
reserved1 = GET_UINT32_LE(indata + 4);
if (reserved1 != 0) {
DO_EXIT_1("Checkpoint error: reserved1 0x%08lX\n", reserved1);
}
/*
* Two bytes at offset 30 is a flag defining whether additional headers exist, or not.
* FLAG_RESERVE_PRESENT
*/
flags = GET_UINT16_LE(indata + 30);
if (flags != 0 && flags != 4)
DO_EXIT_1("Checkpoint error: FLAG_RESERVE_PRESENT 0x%04lx\n", flags);
if (flags == 0 && (cmd == CMD_REMOVE || cmd == CMD_EXTRACT))
DO_EXIT_1("CAB file does not have any signature: %s\n", infile);
/*
* File size is defined at offset 8, however if additional header exists, this size is not valid.
* sigpos - additional data offset is located at offset 44 (from file beginning)
* and consist of 4 bytes (little-endian order)
* siglen - additional data size is located at offset 48 (from file beginning)
* and consist of 4 bytes (little-endian order)
* If there are additional headers, size of the CAB archive file is calcualted
* as additional data offset plus additional data size.
*/
sigpos = GET_UINT32_LE(indata + 44);
siglen = GET_UINT32_LE(indata + 48);
if (flags == 4 && sigpos < filesize && sigpos + siglen != filesize)
DO_EXIT_2("Checkpoint error:\n\tadditional data offset 0x%08lX\n\tadditional data size 0x%08lX\n", sigpos, siglen);
/*
* Additional headers is located at offset 36 (cbCFHeader, cbCFFolder, cbCFData);
* size of header (4 bytes, little-endian order) must be 20 (checkpoint).
*/
header_size = GET_UINT32_LE(indata + 36);
if (flags == 4 && header_size != 20)
DO_EXIT_1("Checkpoint error: additional header size 0x%08lX\n", header_size);
} else if (type == FILE_TYPE_PE) { } else if (type == FILE_TYPE_PE) {
if (filesize < 64) if (filesize < 64)
@ -4082,18 +4228,21 @@ int main(int argc, char **argv) {
goto skip_signing; goto skip_signing;
} }
if (cmd == CMD_VERIFY) { if (cmd == CMD_VERIFY) {
ret = verify_cab_file(indata, sigpos, siglen, leafhash, cafile, crlfile, untrusted); ret = verify_cab_file(indata, header_size, sigpos, siglen, leafhash, cafile, crlfile, untrusted);
goto skip_signing; goto skip_signing;
} }
if ((cmd == CMD_SIGN && nest) || (cmd == CMD_ATTACH && nest) || cmd == CMD_ADD) { if ((cmd == CMD_SIGN && nest) || (cmd == CMD_ATTACH && nest) || cmd == CMD_ADD) {
cursig = extract_existing_cab_pkcs7(indata, sigpos, siglen); cursig = extract_existing_cab_pkcs7(indata, sigpos, siglen);
if (!cursig) if (!cursig)
DO_EXIT_0("Unable to extract existing signature\n"); DO_EXIT_0("Unable to extract existing signature\n");
add_cab_file(indata, sigpos, hash, outdata);
if (cmd == CMD_ADD) if (cmd == CMD_ADD)
sig = cursig; sig = cursig;
} else }
sign_cab_file(indata, fileend, hash, outdata); if (header_size == 20)
/* Strip current signature and modify header */
modify_cab_header(indata, sigpos, hash, outdata);
else
add_cab_header(indata, fileend, hash, outdata);
} else if (type == FILE_TYPE_PE) { } else if (type == FILE_TYPE_PE) {
size_t sigpos, siglen; size_t sigpos, siglen;
@ -4453,17 +4602,9 @@ skip_signing:
outdataverify = map_file(outfile, outdatasize); outdataverify = map_file(outfile, outdatasize);
if (!outdataverify) if (!outdataverify)
DO_EXIT_0("Error verifying result\n"); DO_EXIT_0("Error verifying result\n");
flags = GET_UINT16_LE(outdataverify + 30); /* FLAG_RESERVE_PRESENT */ if (!verify_cab_header(outdataverify, outfile, cmd, outdatasize, &header_size, &sigpos, &siglen))
if (flags != 4) goto err_cleanup;
DO_EXIT_1("Checkpoint error: FLAG_RESERVE_PRESENT 0x%04lx\n", flags); ret = verify_cab_file(outdataverify, header_size, sigpos, siglen, leafhash, cafile, crlfile, untrusted);
sigpos = GET_UINT32_LE(outdataverify + 44);
siglen = GET_UINT32_LE(outdataverify + 48);
if (sigpos < filesize && sigpos + siglen != filesize)
DO_EXIT_2("Checkpoint error:\n\tadditional data offset %lu bytes\n\tadditional data size %lu bytes\n", sigpos, siglen);
header_size = GET_UINT32_LE(outdataverify + 36);
if (flags == 4 && header_size != 20)
DO_EXIT_1("Checkpoint error: additional header size 0x%08lX\n", header_size);
ret = verify_cab_file(outdataverify, sigpos, siglen, leafhash, cafile, crlfile, untrusted);
if (ret) { if (ret) {
DO_EXIT_0("Signature mismatch\n"); DO_EXIT_0("Signature mismatch\n");
} }