Blame src/data-identify.c

Packit d7e8d0
/* data-identify.c - Try to identify the data
Packit Service 30b792
 * Copyright (C) 2013, 2016 g10 Code GmbH
Packit Service 30b792
 *
Packit Service 30b792
 * This file is part of GPGME.
Packit Service 30b792
 *
Packit Service 30b792
 * GPGME is free software; you can redistribute it and/or modify it
Packit Service 30b792
 * under the terms of the GNU Lesser General Public License as
Packit Service 30b792
 * published by the Free Software Foundation; either version 2.1 of
Packit Service 30b792
 * the License, or (at your option) any later version.
Packit Service 30b792
 *
Packit Service 30b792
 * GPGME is distributed in the hope that it will be useful, but
Packit Service 30b792
 * WITHOUT ANY WARRANTY; without even the implied warranty of
Packit Service 30b792
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
Packit Service 30b792
 * Lesser General Public License for more details.
Packit Service 30b792
 *
Packit Service 30b792
 * You should have received a copy of the GNU Lesser General Public
Packit Service 30b792
 * License along with this program; if not, see <https://gnu.org/licenses/>.
Packit Service 30b792
 * SPDX-License-Identifier: LGPL-2.1-or-later
Packit d7e8d0
 */
Packit d7e8d0
Packit d7e8d0
#if HAVE_CONFIG_H
Packit d7e8d0
# include <config.h>
Packit d7e8d0
#endif
Packit d7e8d0
Packit d7e8d0
#include <stdlib.h>
Packit d7e8d0
#include <string.h>
Packit d7e8d0
Packit d7e8d0
#include "gpgme.h"
Packit d7e8d0
#include "data.h"
Packit d7e8d0
#include "util.h"
Packit d7e8d0
#include "parsetlv.h"
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* The size of the sample data we take for detection.  */
Packit d7e8d0
#define SAMPLE_SIZE 2048
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* OpenPGP packet types.  */
Packit d7e8d0
enum
Packit d7e8d0
  {
Packit d7e8d0
    PKT_NONE	      = 0,
Packit d7e8d0
    PKT_PUBKEY_ENC    = 1,  /* Public key encrypted packet. */
Packit d7e8d0
    PKT_SIGNATURE     = 2,  /* Secret key encrypted packet. */
Packit d7e8d0
    PKT_SYMKEY_ENC    = 3,  /* Session key packet. */
Packit d7e8d0
    PKT_ONEPASS_SIG   = 4,  /* One pass sig packet. */
Packit d7e8d0
    PKT_SECRET_KEY    = 5,  /* Secret key. */
Packit d7e8d0
    PKT_PUBLIC_KEY    = 6,  /* Public key. */
Packit d7e8d0
    PKT_SECRET_SUBKEY = 7,  /* Secret subkey. */
Packit d7e8d0
    PKT_COMPRESSED    = 8,  /* Compressed data packet. */
Packit d7e8d0
    PKT_ENCRYPTED     = 9,  /* Conventional encrypted data. */
Packit d7e8d0
    PKT_MARKER	      = 10, /* Marker packet. */
Packit d7e8d0
    PKT_PLAINTEXT     = 11, /* Literal data packet. */
Packit d7e8d0
    PKT_RING_TRUST    = 12, /* Keyring trust packet. */
Packit d7e8d0
    PKT_USER_ID	      = 13, /* User id packet. */
Packit d7e8d0
    PKT_PUBLIC_SUBKEY = 14, /* Public subkey. */
Packit d7e8d0
    PKT_OLD_COMMENT   = 16, /* Comment packet from an OpenPGP draft. */
Packit d7e8d0
    PKT_ATTRIBUTE     = 17, /* PGP's attribute packet. */
Packit d7e8d0
    PKT_ENCRYPTED_MDC = 18, /* Integrity protected encrypted data. */
Packit d7e8d0
    PKT_MDC 	      = 19, /* Manipulation detection code packet. */
Packit d7e8d0
  };
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
static inline unsigned long
Packit d7e8d0
buf32_to_ulong (const void *buffer)
Packit d7e8d0
{
Packit d7e8d0
  const unsigned char *p = buffer;
Packit d7e8d0
Packit d7e8d0
  return (((unsigned long)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Parse the next openpgp packet.  This function assumes a valid
Packit d7e8d0
 * OpenPGP packet at the address pointed to by BUFPTR which has a
Packit d7e8d0
 * maximum length as stored at BUFLEN.  Return the header information
Packit d7e8d0
 * of that packet and advance the pointer stored at BUFPTR to the next
Packit d7e8d0
 * packet; also adjust the length stored at BUFLEN to match the
Packit d7e8d0
 * remaining bytes. If there are no more packets, store NULL at
Packit d7e8d0
 * BUFPTR.  Return an non-zero error code on failure or the following
Packit d7e8d0
 * data on success:
Packit d7e8d0
 *
Packit d7e8d0
 *  R_PKTTYPE = The packet type.
Packit d7e8d0
 *  R_NTOTAL  = The total number of bytes of this packet
Packit d7e8d0
 *
Packit d7e8d0
 * If GPG_ERR_TRUNCATED is returned, a packet type is anyway stored at
Packit d7e8d0
 * R_PKTTYPE but R_NOTAL won't have a usable value,
Packit d7e8d0
 */
Packit d7e8d0
static gpg_error_t
Packit d7e8d0
next_openpgp_packet (unsigned char const **bufptr, size_t *buflen,
Packit d7e8d0
                     int *r_pkttype, size_t *r_ntotal)
Packit d7e8d0
{
Packit d7e8d0
  const unsigned char *buf = *bufptr;
Packit d7e8d0
  size_t len = *buflen;
Packit d7e8d0
  int c, ctb, pkttype;
Packit d7e8d0
  unsigned long pktlen;
Packit d7e8d0
Packit d7e8d0
  if (!len)
Packit d7e8d0
    return gpg_error (GPG_ERR_NO_DATA);
Packit d7e8d0
Packit d7e8d0
  /* First some blacklisting.  */
Packit d7e8d0
  if (len >= 4 && !memcmp (buf, "\x89PNG", 4))
Packit d7e8d0
    return gpg_error (GPG_ERR_INV_PACKET); /* This is a PNG file.  */
Packit d7e8d0
Packit d7e8d0
  /* Start parsing.  */
Packit d7e8d0
  ctb = *buf++; len--;
Packit d7e8d0
  if ( !(ctb & 0x80) )
Packit d7e8d0
    return gpg_error (GPG_ERR_INV_PACKET); /* Invalid CTB. */
Packit d7e8d0
Packit d7e8d0
  if ((ctb & 0x40))  /* New style (OpenPGP) CTB.  */
Packit d7e8d0
    {
Packit d7e8d0
      pkttype = (ctb & 0x3f);
Packit d7e8d0
      if (!len)
Packit d7e8d0
        return gpg_error (GPG_ERR_INV_PACKET); /* No 1st length byte. */
Packit d7e8d0
      c = *buf++; len--;
Packit d7e8d0
      if ( c < 192 )
Packit d7e8d0
        pktlen = c;
Packit d7e8d0
      else if ( c < 224 )
Packit d7e8d0
        {
Packit d7e8d0
          pktlen = (c - 192) * 256;
Packit d7e8d0
          if (!len)
Packit d7e8d0
            return gpg_error (GPG_ERR_INV_PACKET); /* No 2nd length byte. */
Packit d7e8d0
          c = *buf++; len--;
Packit d7e8d0
          pktlen += c + 192;
Packit d7e8d0
        }
Packit d7e8d0
      else if (c == 255)
Packit d7e8d0
        {
Packit d7e8d0
          if (len < 4)
Packit d7e8d0
            return gpg_error (GPG_ERR_INV_PACKET); /* No length bytes. */
Packit d7e8d0
          pktlen = buf32_to_ulong (buf);
Packit d7e8d0
          buf += 4;
Packit d7e8d0
          len -= 4;
Packit d7e8d0
        }
Packit d7e8d0
      else /* Partial length encoding. */
Packit d7e8d0
        {
Packit d7e8d0
          pktlen = 0;
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
  else /* Old style CTB.  */
Packit d7e8d0
    {
Packit d7e8d0
      int lenbytes;
Packit d7e8d0
Packit d7e8d0
      pktlen = 0;
Packit d7e8d0
      pkttype = (ctb>>2)&0xf;
Packit d7e8d0
      lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3));
Packit d7e8d0
      if (len < lenbytes)
Packit d7e8d0
        return gpg_error (GPG_ERR_INV_PACKET); /* Not enough length bytes.  */
Packit d7e8d0
      for (; lenbytes; lenbytes--)
Packit d7e8d0
        {
Packit d7e8d0
          pktlen <<= 8;
Packit d7e8d0
          pktlen |= *buf++; len--;
Packit d7e8d0
	}
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  /* Do some basic sanity check.  */
Packit d7e8d0
  switch (pkttype)
Packit d7e8d0
    {
Packit d7e8d0
    case PKT_PUBKEY_ENC:
Packit d7e8d0
    case PKT_SIGNATURE:
Packit d7e8d0
    case PKT_SYMKEY_ENC:
Packit d7e8d0
    case PKT_ONEPASS_SIG:
Packit d7e8d0
    case PKT_SECRET_KEY:
Packit d7e8d0
    case PKT_PUBLIC_KEY:
Packit d7e8d0
    case PKT_SECRET_SUBKEY:
Packit d7e8d0
    case PKT_COMPRESSED:
Packit d7e8d0
    case PKT_ENCRYPTED:
Packit d7e8d0
    case PKT_MARKER:
Packit d7e8d0
    case PKT_PLAINTEXT:
Packit d7e8d0
    case PKT_RING_TRUST:
Packit d7e8d0
    case PKT_USER_ID:
Packit d7e8d0
    case PKT_PUBLIC_SUBKEY:
Packit d7e8d0
    case PKT_OLD_COMMENT:
Packit d7e8d0
    case PKT_ATTRIBUTE:
Packit d7e8d0
    case PKT_ENCRYPTED_MDC:
Packit d7e8d0
    case PKT_MDC:
Packit d7e8d0
      break; /* Okay these are allowed packets. */
Packit d7e8d0
    default:
Packit d7e8d0
      return gpg_error (GPG_ERR_UNEXPECTED);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (pktlen > len)
Packit d7e8d0
    {
Packit d7e8d0
      /* Packet length header too long.  This is possible because we
Packit d7e8d0
       * may have only a truncated image.  */
Packit d7e8d0
      *r_pkttype = pkttype;
Packit d7e8d0
      *r_ntotal = 0;
Packit d7e8d0
      *bufptr = NULL;
Packit d7e8d0
      return gpg_error (GPG_ERR_TRUNCATED);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  *r_pkttype = pkttype;
Packit d7e8d0
  *r_ntotal = (buf - *bufptr) + pktlen;
Packit d7e8d0
Packit d7e8d0
  *bufptr = buf + pktlen;
Packit d7e8d0
  *buflen = len - pktlen;
Packit d7e8d0
  if (!*buflen)
Packit d7e8d0
    *bufptr = NULL;
Packit d7e8d0
Packit d7e8d0
  return 0;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Detection of PGP binary data.  This function parses an OpenPGP
Packit d7e8d0
 * message.  This parser is robust enough to work on a truncated
Packit d7e8d0
 * version.  Returns a GPGME_DATA_TYPE_.  */
Packit d7e8d0
static gpgme_data_type_t
Packit d7e8d0
pgp_binary_detection (const void *image_arg, size_t imagelen)
Packit d7e8d0
{
Packit d7e8d0
  gpg_error_t err = 0;
Packit d7e8d0
  const unsigned char *image = image_arg;
Packit d7e8d0
  size_t n;
Packit d7e8d0
  int pkttype;
Packit d7e8d0
  int anypacket = 0;
Packit d7e8d0
  int allsignatures = 0;
Packit d7e8d0
Packit d7e8d0
  while (!err && image)
Packit d7e8d0
    {
Packit d7e8d0
      err = next_openpgp_packet (&image, &imagelen, &pkttype, &n);
Packit d7e8d0
      if (gpg_err_code (err) == GPG_ERR_TRUNCATED)
Packit d7e8d0
        ;
Packit d7e8d0
      else if (err)
Packit d7e8d0
        break;
Packit d7e8d0
Packit d7e8d0
      /* Skip all leading marker packets.  */
Packit d7e8d0
      if (!anypacket && pkttype == PKT_MARKER)
Packit d7e8d0
        continue;
Packit d7e8d0
Packit d7e8d0
      if (pkttype == PKT_SIGNATURE)
Packit d7e8d0
        {
Packit d7e8d0
          if (!anypacket)
Packit d7e8d0
            allsignatures = 1;
Packit d7e8d0
        }
Packit d7e8d0
      else
Packit d7e8d0
        allsignatures = 0;
Packit d7e8d0
Packit d7e8d0
      switch (pkttype)
Packit d7e8d0
        {
Packit d7e8d0
        case PKT_SIGNATURE:
Packit d7e8d0
          break;  /* We decide later.  */
Packit d7e8d0
Packit d7e8d0
        case PKT_PLAINTEXT:
Packit d7e8d0
          /* Old style signature format: {sig}+,plaintext */
Packit d7e8d0
          if (allsignatures)
Packit d7e8d0
            return GPGME_DATA_TYPE_PGP_SIGNED;
Packit d7e8d0
          break;
Packit d7e8d0
Packit d7e8d0
        case PKT_ONEPASS_SIG:
Packit d7e8d0
          return GPGME_DATA_TYPE_PGP_SIGNED;
Packit d7e8d0
Packit d7e8d0
        case PKT_SECRET_KEY:
Packit d7e8d0
        case PKT_PUBLIC_KEY:
Packit d7e8d0
          return GPGME_DATA_TYPE_PGP_KEY;
Packit d7e8d0
Packit d7e8d0
        case PKT_SECRET_SUBKEY:
Packit d7e8d0
        case PKT_PUBLIC_SUBKEY:
Packit d7e8d0
          return GPGME_DATA_TYPE_PGP_OTHER;
Packit d7e8d0
        case PKT_PUBKEY_ENC:
Packit d7e8d0
        case PKT_SYMKEY_ENC:
Packit d7e8d0
          return GPGME_DATA_TYPE_PGP_ENCRYPTED;
Packit d7e8d0
Packit d7e8d0
        case PKT_COMPRESSED:
Packit d7e8d0
          /* If this is the first packet we assume that that a signed
Packit d7e8d0
           * packet follows.  We do not want to uncompress it here due
Packit Service 30b792
           * to the need of a lot of code and the potential DoS. */
Packit d7e8d0
          if (!anypacket)
Packit d7e8d0
            return GPGME_DATA_TYPE_PGP_SIGNED;
Packit d7e8d0
          return GPGME_DATA_TYPE_PGP_OTHER;
Packit d7e8d0
Packit d7e8d0
        default:
Packit d7e8d0
          return GPGME_DATA_TYPE_PGP_OTHER;
Packit d7e8d0
        }
Packit d7e8d0
      anypacket = 1;
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  if (allsignatures)
Packit d7e8d0
    return  GPGME_DATA_TYPE_PGP_SIGNATURE;
Packit d7e8d0
Packit d7e8d0
  return GPGME_DATA_TYPE_UNKNOWN;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* This is probably an armored "PGP MESSAGE" which can encode
Packit d7e8d0
 * different PGP data types.  STRING is modified after a call to this
Packit d7e8d0
 * function. */
Packit d7e8d0
static gpgme_data_type_t
Packit d7e8d0
inspect_pgp_message (char *string)
Packit d7e8d0
{
Packit d7e8d0
  struct b64state state;
Packit d7e8d0
  size_t nbytes;
Packit d7e8d0
Packit d7e8d0
  if (_gpgme_b64dec_start (&state, ""))
Packit d7e8d0
    return GPGME_DATA_TYPE_INVALID; /* oops */
Packit d7e8d0
Packit d7e8d0
  if (_gpgme_b64dec_proc (&state, string, strlen (string), &nbytes))
Packit d7e8d0
    {
Packit d7e8d0
      _gpgme_b64dec_finish (&state);
Packit d7e8d0
      return GPGME_DATA_TYPE_UNKNOWN; /* bad encoding etc. */
Packit d7e8d0
    }
Packit d7e8d0
  _gpgme_b64dec_finish (&state);
Packit d7e8d0
  string[nbytes] = 0; /* Better append a Nul. */
Packit d7e8d0
Packit d7e8d0
  return pgp_binary_detection (string, nbytes);
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Note that DATA may be binary but a final nul is required so that
Packit d7e8d0
   string operations will find a terminator.
Packit d7e8d0
Packit d7e8d0
   Returns: GPGME_DATA_TYPE_xxxx */
Packit d7e8d0
static gpgme_data_type_t
Packit d7e8d0
basic_detection (char *data, size_t datalen)
Packit d7e8d0
{
Packit d7e8d0
  tlvinfo_t ti;
Packit d7e8d0
  const char *s;
Packit d7e8d0
  size_t n;
Packit d7e8d0
  int maybe_p12 = 0;
Packit d7e8d0
Packit d7e8d0
  if (datalen < 24) /* Object is probably too short for detection.  */
Packit d7e8d0
    return GPGME_DATA_TYPE_UNKNOWN;
Packit d7e8d0
Packit d7e8d0
  /* This is a common example of a CMS object - it is obvious that we
Packit d7e8d0
     only need to read a few bytes to get to the OID:
Packit d7e8d0
  30 82 0B 59 06 09 2A 86 48 86 F7 0D 01 07 02 A0 82 0B 4A 30 82 0B 46 02
Packit d7e8d0
  ----------- ++++++++++++++++++++++++++++++++
Packit d7e8d0
  SEQUENCE    OID (signedData)
Packit d7e8d0
  (2 byte len)
Packit d7e8d0
Packit d7e8d0
    A PKCS#12 message is:
Packit d7e8d0
Packit d7e8d0
  30 82 08 59 02 01 03 30 82 08 1F 06 09 2A 86 48 86 F7 0D 01 07 01 A0 82
Packit d7e8d0
  ----------- ++++++++ ----------- ++++++++++++++++++++++++++++++++
Packit d7e8d0
  SEQUENCE    INTEGER  SEQUENCE    OID (data)
Packit d7e8d0
Packit d7e8d0
    A X.509 certificate is:
Packit d7e8d0
Packit d7e8d0
  30 82 05 B8 30 82 04 A0 A0 03 02 01 02 02 07 15 46 A0 BF 30 07 39 30 0D
Packit d7e8d0
  ----------- +++++++++++ ----- ++++++++ --------------------------
Packit d7e8d0
  SEQUENCE    SEQUENCE    [0]   INTEGER  INTEGER                    SEQU
Packit d7e8d0
              (tbs)            (version) (s/n)                      (Algo)
Packit d7e8d0
Packit d7e8d0
    Thus we need to read at least 22 bytes, we add 2 bytes to cope with
Packit d7e8d0
    length headers stored with 4 bytes.
Packit d7e8d0
  */
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
  s = data;
Packit d7e8d0
  n = datalen;
Packit d7e8d0
Packit d7e8d0
  if (parse_tlv (&s, &n, &ti))
Packit d7e8d0
    goto try_pgp; /* Not properly BER encoded.  */
Packit d7e8d0
  if (!(ti.cls == ASN1_CLASS_UNIVERSAL && ti.tag == ASN1_TAG_SEQUENCE
Packit d7e8d0
        && ti.is_cons))
Packit d7e8d0
    goto try_pgp; /* A CMS object always starts with a sequence.  */
Packit d7e8d0
Packit d7e8d0
  if (parse_tlv (&s, &n, &ti))
Packit d7e8d0
    goto try_pgp; /* Not properly BER encoded.  */
Packit d7e8d0
  if (ti.cls == ASN1_CLASS_UNIVERSAL && ti.tag == ASN1_TAG_SEQUENCE
Packit d7e8d0
      && ti.is_cons && n >= ti.length)
Packit d7e8d0
    {
Packit d7e8d0
      if (parse_tlv (&s, &n, &ti))
Packit d7e8d0
        goto try_pgp;
Packit d7e8d0
      if (!(ti.cls == ASN1_CLASS_CONTEXT && ti.tag == 0
Packit d7e8d0
            && ti.is_cons && ti.length == 3 && n >= ti.length))
Packit d7e8d0
        goto try_pgp;
Packit d7e8d0
Packit d7e8d0
      if (parse_tlv (&s, &n, &ti))
Packit d7e8d0
        goto try_pgp;
Packit d7e8d0
      if (!(ti.cls == ASN1_CLASS_UNIVERSAL && ti.tag == ASN1_TAG_INTEGER
Packit d7e8d0
            && !ti.is_cons && ti.length == 1 && n && (*s == 1 || *s == 2)))
Packit d7e8d0
        goto try_pgp;
Packit d7e8d0
      s++;
Packit d7e8d0
      n--;
Packit d7e8d0
      if (!(ti.cls == ASN1_CLASS_UNIVERSAL && ti.tag == ASN1_TAG_INTEGER
Packit d7e8d0
            && !ti.is_cons))
Packit d7e8d0
        goto try_pgp;
Packit d7e8d0
      /* Because the now following S/N may be larger than the sample
Packit d7e8d0
         data we have, we stop parsing here and don't check for the
Packit d7e8d0
         algorithm ID.  */
Packit d7e8d0
      return GPGME_DATA_TYPE_X509_CERT;
Packit d7e8d0
    }
Packit d7e8d0
  if (ti.cls == ASN1_CLASS_UNIVERSAL && ti.tag == ASN1_TAG_INTEGER
Packit d7e8d0
      && !ti.is_cons && ti.length == 1 && n && *s == 3)
Packit d7e8d0
    {
Packit d7e8d0
      maybe_p12 = 1;
Packit d7e8d0
      s++;
Packit d7e8d0
      n--;
Packit d7e8d0
      if (parse_tlv (&s, &n, &ti))
Packit d7e8d0
        goto try_pgp;
Packit d7e8d0
      if (!(ti.cls == ASN1_CLASS_UNIVERSAL && ti.tag == ASN1_TAG_SEQUENCE
Packit d7e8d0
            && ti.is_cons))
Packit d7e8d0
        goto try_pgp;
Packit d7e8d0
      if (parse_tlv (&s, &n, &ti))
Packit d7e8d0
        goto try_pgp;
Packit d7e8d0
    }
Packit d7e8d0
  if (ti.cls == ASN1_CLASS_UNIVERSAL && ti.tag == ASN1_TAG_OBJECT_ID
Packit d7e8d0
      && !ti.is_cons && ti.length && n >= ti.length)
Packit d7e8d0
    {
Packit d7e8d0
      if (ti.length == 9)
Packit d7e8d0
        {
Packit d7e8d0
          if (!memcmp (s, "\x2A\x86\x48\x86\xF7\x0D\x01\x07\x01", 9))
Packit d7e8d0
            {
Packit d7e8d0
              /* Data.  */
Packit d7e8d0
              return (maybe_p12 ? GPGME_DATA_TYPE_PKCS12
Packit d7e8d0
                      /*     */ : GPGME_DATA_TYPE_CMS_OTHER);
Packit d7e8d0
            }
Packit d7e8d0
          if (!memcmp (s, "\x2A\x86\x48\x86\xF7\x0D\x01\x07\x02", 9))
Packit d7e8d0
            {
Packit d7e8d0
              /* Signed Data.  */
Packit d7e8d0
              return (maybe_p12 ? GPGME_DATA_TYPE_PKCS12
Packit d7e8d0
                      /*     */ : GPGME_DATA_TYPE_CMS_SIGNED);
Packit d7e8d0
            }
Packit d7e8d0
          if (!memcmp (s, "\x2A\x86\x48\x86\xF7\x0D\x01\x07\x03", 9))
Packit d7e8d0
            return GPGME_DATA_TYPE_CMS_ENCRYPTED; /* Enveloped Data.  */
Packit d7e8d0
          if (!memcmp (s, "\x2A\x86\x48\x86\xF7\x0D\x01\x07\x05", 9))
Packit d7e8d0
            return GPGME_DATA_TYPE_CMS_OTHER; /* Digested Data.  */
Packit d7e8d0
          if (!memcmp (s, "\x2A\x86\x48\x86\xF7\x0D\x01\x07\x06", 9))
Packit d7e8d0
            return GPGME_DATA_TYPE_CMS_OTHER; /* Encrypted Data.  */
Packit d7e8d0
        }
Packit d7e8d0
      else if (ti.length == 11)
Packit d7e8d0
        {
Packit d7e8d0
          if (!memcmp (s, "\x2A\x86\x48\x86\xF7\x0D\x01\x09\x10\x01\x02", 11))
Packit d7e8d0
            return GPGME_DATA_TYPE_CMS_OTHER; /* Auth Data.  */
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
 try_pgp:
Packit d7e8d0
  /* Check whether this might be a non-armored PGP message.  We need
Packit d7e8d0
     to do this before checking for armor lines, so that we don't get
Packit d7e8d0
     fooled by armored messages inside a signed binary PGP message.  */
Packit d7e8d0
  if ((data[0] & 0x80))
Packit d7e8d0
    {
Packit d7e8d0
      /* That might be a binary PGP message.  At least it is not plain
Packit d7e8d0
         ASCII.  Of course this might be certain lead-in text of
Packit d7e8d0
         armored CMS messages.  However, I am not sure whether this is
Packit d7e8d0
         at all defined and in any case it is uncommon.  Thus we don't
Packit d7e8d0
         do any further plausibility checks but stupidly assume no CMS
Packit d7e8d0
         armored data will follow.  */
Packit d7e8d0
      return pgp_binary_detection (data, datalen);
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  /* Now check whether there are armor lines.  */
Packit d7e8d0
  for (s = data; s && *s; s = (*s=='\n')?(s+1):((s=strchr (s,'\n'))?(s+1):s))
Packit d7e8d0
    {
Packit d7e8d0
      if (!strncmp (s, "-----BEGIN ", 11))
Packit d7e8d0
        {
Packit d7e8d0
          if (!strncmp (s+11, "SIGNED ", 7))
Packit d7e8d0
            return GPGME_DATA_TYPE_CMS_SIGNED;
Packit d7e8d0
          if (!strncmp (s+11, "ENCRYPTED ", 10))
Packit d7e8d0
            return GPGME_DATA_TYPE_CMS_ENCRYPTED;
Packit d7e8d0
          if (!strncmp (s+11, "PGP ", 4))
Packit d7e8d0
            {
Packit d7e8d0
              if (!strncmp (s+15, "SIGNATURE", 9))
Packit d7e8d0
                return GPGME_DATA_TYPE_PGP_SIGNATURE;
Packit d7e8d0
              if (!strncmp (s+15, "SIGNED MESSAGE", 14))
Packit d7e8d0
                return GPGME_DATA_TYPE_PGP_SIGNED;
Packit d7e8d0
              if (!strncmp (s+15, "PUBLIC KEY BLOCK", 16))
Packit d7e8d0
                return GPGME_DATA_TYPE_PGP_KEY;
Packit d7e8d0
              if (!strncmp (s+15, "PRIVATE KEY BLOCK", 17))
Packit d7e8d0
                return GPGME_DATA_TYPE_PGP_KEY;
Packit d7e8d0
              if (!strncmp (s+15, "SECRET KEY BLOCK", 16))
Packit d7e8d0
                return GPGME_DATA_TYPE_PGP_KEY;
Packit d7e8d0
              if (!strncmp (s+15, "ARMORED FILE", 12))
Packit d7e8d0
                return GPGME_DATA_TYPE_UNKNOWN;
Packit d7e8d0
Packit d7e8d0
              return inspect_pgp_message (data);
Packit d7e8d0
            }
Packit d7e8d0
          if (!strncmp (s+11, "CERTIFICATE", 11))
Packit d7e8d0
            return GPGME_DATA_TYPE_X509_CERT;
Packit d7e8d0
          if (!strncmp (s+11, "PKCS12", 6))
Packit d7e8d0
            return GPGME_DATA_TYPE_PKCS12;
Packit d7e8d0
          return GPGME_DATA_TYPE_CMS_OTHER; /* Not PGP, thus we assume CMS.  */
Packit d7e8d0
        }
Packit d7e8d0
    }
Packit d7e8d0
Packit d7e8d0
  return GPGME_DATA_TYPE_UNKNOWN;
Packit d7e8d0
}
Packit d7e8d0
Packit d7e8d0
Packit d7e8d0
/* Try to detect the type of the data.  Note that this function works
Packit d7e8d0
   only on seekable data objects.  The function tries to reset the
Packit d7e8d0
   file pointer but there is no guarantee that it will work.
Packit d7e8d0
Packit d7e8d0
   FIXME: We may want to add internal buffering so that this function
Packit Service 30b792
   can be implemented for almost all kind of data objects.
Packit d7e8d0
 */
Packit d7e8d0
gpgme_data_type_t
Packit d7e8d0
gpgme_data_identify (gpgme_data_t dh, int reserved)
Packit d7e8d0
{
Packit d7e8d0
  gpgme_data_type_t result;
Packit d7e8d0
  char *sample;
Packit d7e8d0
  int n;
Packit d7e8d0
  gpgme_off_t off;
Packit d7e8d0
Packit d7e8d0
  (void)reserved;
Packit d7e8d0
Packit d7e8d0
  /* Check whether we can seek the data object.  */
Packit d7e8d0
  off = gpgme_data_seek (dh, 0, SEEK_CUR);
Packit d7e8d0
  if (off == (gpgme_off_t)(-1))
Packit d7e8d0
    return GPGME_DATA_TYPE_INVALID;
Packit d7e8d0
Packit d7e8d0
  /* Allocate a buffer and read the data. */
Packit d7e8d0
  sample = malloc (SAMPLE_SIZE);
Packit d7e8d0
  if (!sample)
Packit d7e8d0
    return GPGME_DATA_TYPE_INVALID; /* Ooops.  */
Packit d7e8d0
  n = gpgme_data_read (dh, sample, SAMPLE_SIZE - 1);
Packit d7e8d0
  if (n < 0)
Packit d7e8d0
    {
Packit d7e8d0
      free (sample);
Packit d7e8d0
      return GPGME_DATA_TYPE_INVALID; /* Ooops.  */
Packit d7e8d0
    }
Packit d7e8d0
  sample[n] = 0;  /* (Required for our string functions.)  */
Packit d7e8d0
Packit d7e8d0
  result = basic_detection (sample, n);
Packit d7e8d0
  free (sample);
Packit d7e8d0
  gpgme_data_seek (dh, off, SEEK_SET);
Packit d7e8d0
Packit d7e8d0
  return result;
Packit d7e8d0
}