core: Enhance gpgme_data_identify to detect binary PGP messages.
* src/gpgme.h.in (GPGME_DATA_TYPE_PGP_ENCRYPTED): New. (GPGME_DATA_TYPE_PGP_SIGNATURE): New. * src/data-identify.c: Add enum for OpenPGP packet types. (buf32_to_ulong): New. (next_openpgp_packet): New. Based on the gnupg/kbx/keybox-openpgp.c implementation and relicensed to LGPL by g10 Code. (pgp_binary_detection): New. (basic_detection): Call pgp_binary_detection instead of returning unknown. Signed-off-by: Werner Koch <wk@gnupg.org>
This commit is contained in:
parent
8997d88bf9
commit
32d4bbf5e3
2
NEWS
2
NEWS
@ -18,6 +18,8 @@ Noteworthy changes in version 1.7.0 (unreleased) [C25/A14/R_]
|
|||||||
GPGME_STATUS_TOFU_STATS NEW.
|
GPGME_STATUS_TOFU_STATS NEW.
|
||||||
GPGME_STATUS_TOFU_STATS_LONG NEW.
|
GPGME_STATUS_TOFU_STATS_LONG NEW.
|
||||||
GPGME_STATUS_NOTATION_FLAGS NEW.
|
GPGME_STATUS_NOTATION_FLAGS NEW.
|
||||||
|
GPGME_DATA_TYPE_PGP_ENCRYPTED NEW.
|
||||||
|
GPGME_DATA_TYPE_PGP_SIGNATURE NEW.
|
||||||
|
|
||||||
|
|
||||||
Noteworthy changes in version 1.6.0 (2015-08-26) [C25/A14/R0]
|
Noteworthy changes in version 1.6.0 (2015-08-26) [C25/A14/R0]
|
||||||
|
@ -29,10 +29,238 @@
|
|||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "parsetlv.h"
|
#include "parsetlv.h"
|
||||||
|
|
||||||
|
|
||||||
/* The size of the sample data we take for detection. */
|
/* The size of the sample data we take for detection. */
|
||||||
#define SAMPLE_SIZE 2048
|
#define SAMPLE_SIZE 2048
|
||||||
|
|
||||||
|
|
||||||
|
/* OpenPGP packet types. */
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
PKT_NONE = 0,
|
||||||
|
PKT_PUBKEY_ENC = 1, /* Public key encrypted packet. */
|
||||||
|
PKT_SIGNATURE = 2, /* Secret key encrypted packet. */
|
||||||
|
PKT_SYMKEY_ENC = 3, /* Session key packet. */
|
||||||
|
PKT_ONEPASS_SIG = 4, /* One pass sig packet. */
|
||||||
|
PKT_SECRET_KEY = 5, /* Secret key. */
|
||||||
|
PKT_PUBLIC_KEY = 6, /* Public key. */
|
||||||
|
PKT_SECRET_SUBKEY = 7, /* Secret subkey. */
|
||||||
|
PKT_COMPRESSED = 8, /* Compressed data packet. */
|
||||||
|
PKT_ENCRYPTED = 9, /* Conventional encrypted data. */
|
||||||
|
PKT_MARKER = 10, /* Marker packet. */
|
||||||
|
PKT_PLAINTEXT = 11, /* Literal data packet. */
|
||||||
|
PKT_RING_TRUST = 12, /* Keyring trust packet. */
|
||||||
|
PKT_USER_ID = 13, /* User id packet. */
|
||||||
|
PKT_PUBLIC_SUBKEY = 14, /* Public subkey. */
|
||||||
|
PKT_OLD_COMMENT = 16, /* Comment packet from an OpenPGP draft. */
|
||||||
|
PKT_ATTRIBUTE = 17, /* PGP's attribute packet. */
|
||||||
|
PKT_ENCRYPTED_MDC = 18, /* Integrity protected encrypted data. */
|
||||||
|
PKT_MDC = 19, /* Manipulation detection code packet. */
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
static inline unsigned long
|
||||||
|
buf32_to_ulong (const void *buffer)
|
||||||
|
{
|
||||||
|
const unsigned char *p = buffer;
|
||||||
|
|
||||||
|
return (((unsigned long)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Parse the next openpgp packet. This function assumes a valid
|
||||||
|
* OpenPGP packet at the address pointed to by BUFPTR which has a
|
||||||
|
* maximum length as stored at BUFLEN. Return the header information
|
||||||
|
* of that packet and advance the pointer stored at BUFPTR to the next
|
||||||
|
* packet; also adjust the length stored at BUFLEN to match the
|
||||||
|
* remaining bytes. If there are no more packets, store NULL at
|
||||||
|
* BUFPTR. Return an non-zero error code on failure or the following
|
||||||
|
* data on success:
|
||||||
|
*
|
||||||
|
* R_PKTTYPE = The packet type.
|
||||||
|
* R_NTOTAL = The total number of bytes of this packet
|
||||||
|
*
|
||||||
|
* If GPG_ERR_TRUNCATED is returned, a packet type is anyway stored at
|
||||||
|
* R_PKTTYPE but R_NOTAL won't have a usable value,
|
||||||
|
*/
|
||||||
|
static gpg_error_t
|
||||||
|
next_openpgp_packet (unsigned char const **bufptr, size_t *buflen,
|
||||||
|
int *r_pkttype, size_t *r_ntotal)
|
||||||
|
{
|
||||||
|
const unsigned char *buf = *bufptr;
|
||||||
|
size_t len = *buflen;
|
||||||
|
int c, ctb, pkttype;
|
||||||
|
unsigned long pktlen;
|
||||||
|
|
||||||
|
if (!len)
|
||||||
|
return gpg_error (GPG_ERR_NO_DATA);
|
||||||
|
|
||||||
|
ctb = *buf++; len--;
|
||||||
|
if ( !(ctb & 0x80) )
|
||||||
|
return gpg_error (GPG_ERR_INV_PACKET); /* Invalid CTB. */
|
||||||
|
|
||||||
|
if ((ctb & 0x40)) /* New style (OpenPGP) CTB. */
|
||||||
|
{
|
||||||
|
pkttype = (ctb & 0x3f);
|
||||||
|
if (!len)
|
||||||
|
return gpg_error (GPG_ERR_INV_PACKET); /* No 1st length byte. */
|
||||||
|
c = *buf++; len--;
|
||||||
|
if ( c < 192 )
|
||||||
|
pktlen = c;
|
||||||
|
else if ( c < 224 )
|
||||||
|
{
|
||||||
|
pktlen = (c - 192) * 256;
|
||||||
|
if (!len)
|
||||||
|
return gpg_error (GPG_ERR_INV_PACKET); /* No 2nd length byte. */
|
||||||
|
c = *buf++; len--;
|
||||||
|
pktlen += c + 192;
|
||||||
|
}
|
||||||
|
else if (c == 255)
|
||||||
|
{
|
||||||
|
if (len < 4)
|
||||||
|
return gpg_error (GPG_ERR_INV_PACKET); /* No length bytes. */
|
||||||
|
pktlen = buf32_to_ulong (buf);
|
||||||
|
buf += 4;
|
||||||
|
len -= 4;
|
||||||
|
}
|
||||||
|
else /* Partial length encoding is not allowed for key packets. */
|
||||||
|
return gpg_error (GPG_ERR_UNEXPECTED);
|
||||||
|
}
|
||||||
|
else /* Old style CTB. */
|
||||||
|
{
|
||||||
|
int lenbytes;
|
||||||
|
|
||||||
|
pktlen = 0;
|
||||||
|
pkttype = (ctb>>2)&0xf;
|
||||||
|
lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3));
|
||||||
|
if (!lenbytes) /* Not allowed in key packets. */
|
||||||
|
return gpg_error (GPG_ERR_UNEXPECTED);
|
||||||
|
if (len < lenbytes)
|
||||||
|
return gpg_error (GPG_ERR_INV_PACKET); /* Not enough length bytes. */
|
||||||
|
for (; lenbytes; lenbytes--)
|
||||||
|
{
|
||||||
|
pktlen <<= 8;
|
||||||
|
pktlen |= *buf++; len--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Do some basic sanity check. */
|
||||||
|
switch (pkttype)
|
||||||
|
{
|
||||||
|
case PKT_PUBKEY_ENC:
|
||||||
|
case PKT_SIGNATURE:
|
||||||
|
case PKT_SYMKEY_ENC:
|
||||||
|
case PKT_ONEPASS_SIG:
|
||||||
|
case PKT_SECRET_KEY:
|
||||||
|
case PKT_PUBLIC_KEY:
|
||||||
|
case PKT_SECRET_SUBKEY:
|
||||||
|
case PKT_COMPRESSED:
|
||||||
|
case PKT_ENCRYPTED:
|
||||||
|
case PKT_MARKER:
|
||||||
|
case PKT_PLAINTEXT:
|
||||||
|
case PKT_RING_TRUST:
|
||||||
|
case PKT_USER_ID:
|
||||||
|
case PKT_PUBLIC_SUBKEY:
|
||||||
|
case PKT_OLD_COMMENT:
|
||||||
|
case PKT_ATTRIBUTE:
|
||||||
|
case PKT_ENCRYPTED_MDC:
|
||||||
|
case PKT_MDC:
|
||||||
|
break; /* Okay these are allowed packets. */
|
||||||
|
default:
|
||||||
|
return gpg_error (GPG_ERR_UNEXPECTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pktlen > len)
|
||||||
|
{
|
||||||
|
/* Packet length header too long. This is possible because we
|
||||||
|
* may have only a truncated image. */
|
||||||
|
*r_pkttype = pkttype;
|
||||||
|
*r_ntotal = 0;
|
||||||
|
*bufptr = NULL;
|
||||||
|
return gpg_error (GPG_ERR_TRUNCATED);
|
||||||
|
}
|
||||||
|
|
||||||
|
*r_pkttype = pkttype;
|
||||||
|
*r_ntotal = (buf - *bufptr) + pktlen;
|
||||||
|
|
||||||
|
*bufptr = buf + pktlen;
|
||||||
|
*buflen = len - pktlen;
|
||||||
|
if (!*buflen)
|
||||||
|
*bufptr = NULL;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Detection of PGP binary data. This function parses an OpenPGP
|
||||||
|
* message. This parser is robust enough to work on a truncated
|
||||||
|
* version. Returns a GPGME_DATA_TYPE_. */
|
||||||
|
static gpgme_data_type_t
|
||||||
|
pgp_binary_detection (const void *image_arg, size_t imagelen)
|
||||||
|
{
|
||||||
|
gpg_error_t err = 0;
|
||||||
|
const unsigned char *image = image_arg;
|
||||||
|
size_t n;
|
||||||
|
int pkttype;
|
||||||
|
int anypacket = 0;
|
||||||
|
int allsignatures = 0;
|
||||||
|
|
||||||
|
while (!err && image)
|
||||||
|
{
|
||||||
|
err = next_openpgp_packet (&image, &imagelen, &pkttype, &n);
|
||||||
|
if (gpg_err_code (err) == GPG_ERR_TRUNCATED)
|
||||||
|
;
|
||||||
|
else if (err)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (pkttype == PKT_SIGNATURE)
|
||||||
|
{
|
||||||
|
if (!anypacket)
|
||||||
|
allsignatures = 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
allsignatures = 0;
|
||||||
|
anypacket = 1;
|
||||||
|
|
||||||
|
switch (pkttype)
|
||||||
|
{
|
||||||
|
case PKT_SIGNATURE:
|
||||||
|
break; /* We decide later. */
|
||||||
|
|
||||||
|
case PKT_PLAINTEXT:
|
||||||
|
/* Old style signature format: {sig}+,plaintext */
|
||||||
|
if (allsignatures)
|
||||||
|
return GPGME_DATA_TYPE_PGP_SIGNED;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case PKT_ONEPASS_SIG:
|
||||||
|
return GPGME_DATA_TYPE_PGP_SIGNED;
|
||||||
|
|
||||||
|
case PKT_SECRET_KEY:
|
||||||
|
case PKT_PUBLIC_KEY:
|
||||||
|
return GPGME_DATA_TYPE_PGP_KEY;
|
||||||
|
|
||||||
|
case PKT_SECRET_SUBKEY:
|
||||||
|
case PKT_PUBLIC_SUBKEY:
|
||||||
|
return GPGME_DATA_TYPE_PGP_OTHER;
|
||||||
|
case PKT_PUBKEY_ENC:
|
||||||
|
case PKT_SYMKEY_ENC:
|
||||||
|
return GPGME_DATA_TYPE_PGP_ENCRYPTED;
|
||||||
|
|
||||||
|
case PKT_MARKER:
|
||||||
|
break; /* Skip this packet. */
|
||||||
|
|
||||||
|
default:
|
||||||
|
return GPGME_DATA_TYPE_PGP_OTHER;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (allsignatures)
|
||||||
|
return GPGME_DATA_TYPE_PGP_SIGNATURE;
|
||||||
|
|
||||||
|
return GPGME_DATA_TYPE_UNKNOWN;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Note that DATA may be binary but a final nul is required so that
|
/* Note that DATA may be binary but a final nul is required so that
|
||||||
string operations will find a terminator.
|
string operations will find a terminator.
|
||||||
@ -167,7 +395,7 @@ basic_detection (const char *data, size_t datalen)
|
|||||||
at all defined and in any case it is uncommon. Thus we don't
|
at all defined and in any case it is uncommon. Thus we don't
|
||||||
do any further plausibility checks but stupidly assume no CMS
|
do any further plausibility checks but stupidly assume no CMS
|
||||||
armored data will follow. */
|
armored data will follow. */
|
||||||
return GPGME_DATA_TYPE_UNKNOWN;
|
return pgp_binary_detection (data, datalen);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Now check whether there are armor lines. */
|
/* Now check whether there are armor lines. */
|
||||||
|
@ -239,8 +239,10 @@ typedef enum
|
|||||||
GPGME_DATA_TYPE_INVALID = 0, /* Not detected. */
|
GPGME_DATA_TYPE_INVALID = 0, /* Not detected. */
|
||||||
GPGME_DATA_TYPE_UNKNOWN = 1,
|
GPGME_DATA_TYPE_UNKNOWN = 1,
|
||||||
GPGME_DATA_TYPE_PGP_SIGNED = 0x10,
|
GPGME_DATA_TYPE_PGP_SIGNED = 0x10,
|
||||||
|
GPGME_DATA_TYPE_PGP_ENCRYPTED= 0x11,
|
||||||
GPGME_DATA_TYPE_PGP_OTHER = 0x12,
|
GPGME_DATA_TYPE_PGP_OTHER = 0x12,
|
||||||
GPGME_DATA_TYPE_PGP_KEY = 0x13,
|
GPGME_DATA_TYPE_PGP_KEY = 0x13,
|
||||||
|
GPGME_DATA_TYPE_PGP_SIGNATURE= 0x18, /* Detached signature */
|
||||||
GPGME_DATA_TYPE_CMS_SIGNED = 0x20,
|
GPGME_DATA_TYPE_CMS_SIGNED = 0x20,
|
||||||
GPGME_DATA_TYPE_CMS_ENCRYPTED= 0x21,
|
GPGME_DATA_TYPE_CMS_ENCRYPTED= 0x21,
|
||||||
GPGME_DATA_TYPE_CMS_OTHER = 0x22,
|
GPGME_DATA_TYPE_CMS_OTHER = 0x22,
|
||||||
|
Loading…
Reference in New Issue
Block a user