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:
Werner Koch 2016-06-21 16:14:02 +02:00
parent 8997d88bf9
commit 32d4bbf5e3
No known key found for this signature in database
GPG Key ID: E3FDFF218E45B72B
3 changed files with 233 additions and 1 deletions

2
NEWS
View File

@ -18,6 +18,8 @@ Noteworthy changes in version 1.7.0 (unreleased) [C25/A14/R_]
GPGME_STATUS_TOFU_STATS NEW.
GPGME_STATUS_TOFU_STATS_LONG 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]

View File

@ -29,10 +29,238 @@
#include "util.h"
#include "parsetlv.h"
/* The size of the sample data we take for detection. */
#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
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
do any further plausibility checks but stupidly assume no CMS
armored data will follow. */
return GPGME_DATA_TYPE_UNKNOWN;
return pgp_binary_detection (data, datalen);
}
/* Now check whether there are armor lines. */

View File

@ -239,8 +239,10 @@ typedef enum
GPGME_DATA_TYPE_INVALID = 0, /* Not detected. */
GPGME_DATA_TYPE_UNKNOWN = 1,
GPGME_DATA_TYPE_PGP_SIGNED = 0x10,
GPGME_DATA_TYPE_PGP_ENCRYPTED= 0x11,
GPGME_DATA_TYPE_PGP_OTHER = 0x12,
GPGME_DATA_TYPE_PGP_KEY = 0x13,
GPGME_DATA_TYPE_PGP_SIGNATURE= 0x18, /* Detached signature */
GPGME_DATA_TYPE_CMS_SIGNED = 0x20,
GPGME_DATA_TYPE_CMS_ENCRYPTED= 0x21,
GPGME_DATA_TYPE_CMS_OTHER = 0x22,