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 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]

View File

@ -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. */

View File

@ -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,