/* read-packet.c - Read OpenPGP packets * Copyright (C) 2001-2012 Free Software Foundation, Inc. * * Author: Timo Schulz * * This file is part of OpenCDK. * * The OpenCDK library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but * WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see * */ #ifdef HAVE_CONFIG_H #include #endif #include #include #include #include #include "opencdk.h" #include "main.h" #include "packet.h" #include "types.h" #include #include #include /* The version of the MDC packet considering the lastest OpenPGP draft. */ static int stream_read(cdk_stream_t s, void *buf, size_t buflen, size_t * r_nread) { int res = cdk_stream_read(s, buf, buflen); if (res > 0) { *r_nread = res; return 0; } else { return (cdk_stream_eof(s) ? EOF : _cdk_stream_get_errno(s)); } } /* Try to read 4 octets from the stream. */ static u32 read_32(cdk_stream_t s) { byte buf[4]; size_t nread = 0; assert(s != NULL); stream_read(s, buf, 4, &nread); if (nread != 4) return (u32) -1; return buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3]; } /* Try to read 2 octets from a stream. */ static u16 read_16(cdk_stream_t s) { byte buf[2]; size_t nread = 0; assert(s != NULL); stream_read(s, buf, 2, &nread); if (nread != 2) return (u16) - 1; return buf[0] << 8 | buf[1]; } /* read about S2K at http://tools.ietf.org/html/rfc4880#section-3.7.1 */ static cdk_error_t read_s2k(cdk_stream_t inp, cdk_s2k_t s2k) { size_t nread = 0; s2k->mode = cdk_stream_getc(inp); s2k->hash_algo = cdk_stream_getc(inp); if (s2k->mode == CDK_S2K_SIMPLE) return 0; else if (s2k->mode == CDK_S2K_SALTED || s2k->mode == CDK_S2K_ITERSALTED) { if (stream_read(inp, s2k->salt, DIM(s2k->salt), &nread)) return CDK_Inv_Packet; if (nread != DIM(s2k->salt)) return CDK_Inv_Packet; if (s2k->mode == CDK_S2K_ITERSALTED) s2k->count = cdk_stream_getc(inp); } else if (s2k->mode == CDK_S2K_GNU_EXT) { /* GNU extensions to the S2K : read DETAILS from gnupg */ return 0; } else return CDK_Not_Implemented; return 0; } static cdk_error_t read_mpi(cdk_stream_t inp, bigint_t * ret_m, int secure) { bigint_t m; int err; byte buf[MAX_MPI_BYTES + 2]; size_t nread, nbits; cdk_error_t rc; if (!inp || !ret_m) return CDK_Inv_Value; *ret_m = NULL; nbits = read_16(inp); nread = (nbits + 7) / 8; if (nbits > MAX_MPI_BITS || nbits == 0) { _gnutls_write_log("read_mpi: too large %d bits\n", (int) nbits); return gnutls_assert_val(CDK_MPI_Error); /* Sanity check */ } rc = stream_read(inp, buf + 2, nread, &nread); if (!rc && nread != ((nbits + 7) / 8)) { _gnutls_write_log("read_mpi: too short %d < %d\n", (int) nread, (int) ((nbits + 7) / 8)); return gnutls_assert_val(CDK_MPI_Error); } buf[0] = nbits >> 8; buf[1] = nbits >> 0; nread += 2; err = _gnutls_mpi_init_scan_pgp(&m, buf, nread); if (err < 0) return gnutls_assert_val(map_gnutls_error(err)); *ret_m = m; return rc; } /* Read the encoded packet length directly from the file object INP and return it. Reset RET_PARTIAL if this is the last packet in block mode. */ size_t _cdk_pkt_read_len(FILE * inp, size_t * ret_partial) { int c1, c2; size_t pktlen; c1 = fgetc(inp); if (c1 == EOF) return (size_t) EOF; if (c1 < 224 || c1 == 255) *ret_partial = 0; /* End of partial data */ if (c1 < 192) pktlen = c1; else if (c1 >= 192 && c1 <= 223) { c2 = fgetc(inp); if (c2 == EOF) return (size_t) EOF; pktlen = ((c1 - 192) << 8) + c2 + 192; } else if (c1 == 255) { pktlen = fgetc(inp) << 24; pktlen |= fgetc(inp) << 16; pktlen |= fgetc(inp) << 8; pktlen |= fgetc(inp) << 0; } else pktlen = 1 << (c1 & 0x1f); return pktlen; } static cdk_error_t read_pubkey_enc(cdk_stream_t inp, size_t pktlen, cdk_pkt_pubkey_enc_t pke) { size_t i, nenc; if (!inp || !pke) return CDK_Inv_Value; if (DEBUG_PKT) _gnutls_write_log("read_pubkey_enc: %d octets\n", (int) pktlen); if (pktlen < 12) return CDK_Inv_Packet; pke->version = cdk_stream_getc(inp); if (pke->version < 2 || pke->version > 3) return CDK_Inv_Packet; pke->keyid[0] = read_32(inp); pke->keyid[1] = read_32(inp); if (!pke->keyid[0] && !pke->keyid[1]) pke->throw_keyid = 1; /* RFC2440 "speculative" keyID */ pke->pubkey_algo = _pgp_pub_algo_to_cdk(cdk_stream_getc(inp)); nenc = cdk_pk_get_nenc(pke->pubkey_algo); if (!nenc) return CDK_Inv_Algo; for (i = 0; i < nenc; i++) { cdk_error_t rc = read_mpi(inp, &pke->mpi[i], 0); if (rc) return gnutls_assert_val(rc); } return 0; } static cdk_error_t read_mdc(cdk_stream_t inp, cdk_pkt_mdc_t mdc) { size_t n = 0; cdk_error_t rc; if (!inp || !mdc) return CDK_Inv_Value; if (DEBUG_PKT) _gnutls_write_log("read_mdc:\n"); rc = stream_read(inp, mdc->hash, DIM(mdc->hash), &n); if (rc) return rc; return n != DIM(mdc->hash) ? CDK_Inv_Packet : 0; } static cdk_error_t read_compressed(cdk_stream_t inp, size_t pktlen, cdk_pkt_compressed_t c) { if (!inp || !c) return CDK_Inv_Value; if (DEBUG_PKT) _gnutls_write_log("read_compressed: %d octets\n", (int) pktlen); c->algorithm = cdk_stream_getc(inp); if (c->algorithm > 3) return CDK_Inv_Packet; /* don't know the size, so we read until EOF */ if (!pktlen) { c->len = 0; c->buf = inp; } /* FIXME: Support partial bodies. */ return 0; } static cdk_error_t read_public_key(cdk_stream_t inp, size_t pktlen, cdk_pkt_pubkey_t pk) { size_t i, ndays, npkey; if (!inp || !pk) return CDK_Inv_Value; if (DEBUG_PKT) _gnutls_write_log("read_public_key: %d octets\n", (int) pktlen); pk->is_invalid = 1; /* default to detect missing self signatures */ pk->is_revoked = 0; pk->has_expired = 0; pk->version = cdk_stream_getc(inp); if (pk->version < 2 || pk->version > 4) return CDK_Inv_Packet_Ver; pk->timestamp = read_32(inp); if (pk->version < 4) { ndays = read_16(inp); if (ndays) pk->expiredate = pk->timestamp + ndays * 86400L; } pk->pubkey_algo = _pgp_pub_algo_to_cdk(cdk_stream_getc(inp)); npkey = cdk_pk_get_npkey(pk->pubkey_algo); if (!npkey) { gnutls_assert(); _gnutls_write_log("invalid public key algorithm %d\n", pk->pubkey_algo); return CDK_Inv_Algo; } for (i = 0; i < npkey; i++) { cdk_error_t rc = read_mpi(inp, &pk->mpi[i], 0); if (rc) return gnutls_assert_val(rc); } /* This value is just for the first run and will be replaced with the actual key flags from the self signature. */ pk->pubkey_usage = 0; return 0; } static cdk_error_t read_public_subkey(cdk_stream_t inp, size_t pktlen, cdk_pkt_pubkey_t pk) { if (!inp || !pk) return CDK_Inv_Value; return read_public_key(inp, pktlen, pk); } static cdk_error_t read_secret_key(cdk_stream_t inp, size_t pktlen, cdk_pkt_seckey_t sk) { size_t p1, p2, nread = 0; int i, nskey; int rc; if (!inp || !sk || !sk->pk) return CDK_Inv_Value; if (DEBUG_PKT) _gnutls_write_log("read_secret_key: %d octets\n", (int) pktlen); p1 = cdk_stream_tell(inp); rc = read_public_key(inp, pktlen, sk->pk); if (rc) return rc; sk->s2k_usage = cdk_stream_getc(inp); sk->protect.sha1chk = 0; if (sk->s2k_usage == 254 || sk->s2k_usage == 255) { sk->protect.sha1chk = (sk->s2k_usage == 254); sk->protect.algo = _pgp_cipher_to_gnutls(cdk_stream_getc(inp)); if (sk->protect.algo == GNUTLS_CIPHER_UNKNOWN) return gnutls_assert_val(CDK_Inv_Algo); sk->protect.s2k = cdk_calloc(1, sizeof *sk->protect.s2k); if (!sk->protect.s2k) return CDK_Out_Of_Core; rc = read_s2k(inp, sk->protect.s2k); if (rc) return rc; /* refer to --export-secret-subkeys in gpg(1) */ if (sk->protect.s2k->mode == CDK_S2K_GNU_EXT) sk->protect.ivlen = 0; else { sk->protect.ivlen = gnutls_cipher_get_block_size(sk->protect.algo); if (!sk->protect.ivlen) return CDK_Inv_Packet; rc = stream_read(inp, sk->protect.iv, sk->protect.ivlen, &nread); if (rc) return rc; if (nread != sk->protect.ivlen) return CDK_Inv_Packet; } } else sk->protect.algo = _pgp_cipher_to_gnutls(sk->s2k_usage); if (sk->protect.algo == GNUTLS_CIPHER_UNKNOWN) return gnutls_assert_val(CDK_Inv_Algo); else if (sk->protect.algo == GNUTLS_CIPHER_NULL) { sk->csum = 0; nskey = cdk_pk_get_nskey(sk->pk->pubkey_algo); if (!nskey) { gnutls_assert(); return CDK_Inv_Algo; } for (i = 0; i < nskey; i++) { rc = read_mpi(inp, &sk->mpi[i], 1); if (rc) return gnutls_assert_val(rc); } sk->csum = read_16(inp); sk->is_protected = 0; } else if (sk->pk->version < 4) { /* The length of each multiprecision integer is stored in plaintext. */ nskey = cdk_pk_get_nskey(sk->pk->pubkey_algo); if (!nskey) { gnutls_assert(); return CDK_Inv_Algo; } for (i = 0; i < nskey; i++) { rc = read_mpi(inp, &sk->mpi[i], 1); if (rc) return gnutls_assert_val(rc); } sk->csum = read_16(inp); sk->is_protected = 1; } else { /* We need to read the rest of the packet because we do not have any information how long the encrypted mpi's are */ p2 = cdk_stream_tell(inp); p2 -= p1; sk->enclen = pktlen - p2; if (sk->enclen < 2) return CDK_Inv_Packet; /* at least 16 bits for the checksum! */ sk->encdata = cdk_calloc(1, sk->enclen + 1); if (!sk->encdata) return CDK_Out_Of_Core; if (stream_read(inp, sk->encdata, sk->enclen, &nread)) return CDK_Inv_Packet; /* Handle the GNU S2K extensions we know (just gnu-dummy right now): */ if (sk->protect.s2k->mode == CDK_S2K_GNU_EXT) { unsigned char gnumode; if ((sk->enclen < strlen("GNU") + 1) || (0 != memcmp("GNU", sk->encdata, strlen("GNU")))) return CDK_Inv_Packet; gnumode = sk->encdata[strlen("GNU")]; /* we only handle gnu-dummy (mode 1). mode 2 should refer to external smart cards. */ if (gnumode != 1) return CDK_Inv_Packet; /* gnu-dummy should have no more data */ if (sk->enclen != strlen("GNU") + 1) return CDK_Inv_Packet; } nskey = cdk_pk_get_nskey(sk->pk->pubkey_algo); if (!nskey) { gnutls_assert(); return CDK_Inv_Algo; } /* We mark each MPI entry with NULL to indicate a protected key. */ for (i = 0; i < nskey; i++) sk->mpi[i] = NULL; sk->is_protected = 1; } sk->is_primary = 1; _cdk_copy_pk_to_sk(sk->pk, sk); return 0; } static cdk_error_t read_secret_subkey(cdk_stream_t inp, size_t pktlen, cdk_pkt_seckey_t sk) { cdk_error_t rc; if (!inp || !sk || !sk->pk) return CDK_Inv_Value; rc = read_secret_key(inp, pktlen, sk); sk->is_primary = 0; return rc; } #define ATTRIBUTE "[attribute]" static cdk_error_t read_attribute(cdk_stream_t inp, size_t pktlen, cdk_pkt_userid_t attr, int name_size) { const byte *p; byte *buf; size_t len, nread; cdk_error_t rc; if (!inp || !attr || !pktlen) return CDK_Inv_Value; if (DEBUG_PKT) _gnutls_write_log("read_attribute: %d octets\n", (int) pktlen); _gnutls_str_cpy(attr->name, name_size, ATTRIBUTE); attr->len = MIN(name_size, sizeof(ATTRIBUTE) - 1); buf = cdk_calloc(1, pktlen); if (!buf) return CDK_Out_Of_Core; rc = stream_read(inp, buf, pktlen, &nread); if (rc) { gnutls_assert(); rc = CDK_Inv_Packet; goto error; } p = buf; len = *p++; pktlen--; if (len == 255) { if (pktlen < 4) { gnutls_assert(); rc = CDK_Inv_Packet; goto error; } len = _cdk_buftou32(p); p += 4; pktlen -= 4; } else if (len >= 192) { if (pktlen < 2) { gnutls_assert(); rc = CDK_Inv_Packet; goto error; } len = ((len - 192) << 8) + *p + 192; p++; pktlen--; } if (!len || *p != 1) { /* Currently only 1, meaning an image, is defined. */ rc = CDK_Inv_Packet; goto error; } p++; len--; if (len >= pktlen) { rc = CDK_Inv_Packet; goto error; } attr->attrib_img = cdk_calloc(1, len); if (!attr->attrib_img) { rc = CDK_Out_Of_Core; goto error; } attr->attrib_len = len; memcpy(attr->attrib_img, p, len); cdk_free(buf); return rc; error: cdk_free(buf); return rc; } static cdk_error_t read_user_id(cdk_stream_t inp, size_t pktlen, cdk_pkt_userid_t user_id) { size_t nread; cdk_error_t rc; if (!inp || !user_id) return CDK_Inv_Value; if (!pktlen) return CDK_Inv_Packet; if (DEBUG_PKT) _gnutls_write_log("read_user_id: %lu octets\n", (unsigned long) pktlen); user_id->len = pktlen; rc = stream_read(inp, user_id->name, pktlen, &nread); if (rc) return rc; if (nread != pktlen) return CDK_Inv_Packet; user_id->name[nread] = '\0'; return rc; } static cdk_error_t read_subpkt(cdk_stream_t inp, cdk_subpkt_t * r_ctx, size_t * r_nbytes) { int c, c1; size_t size, nread = 0, n; cdk_subpkt_t node; cdk_error_t rc; if (!inp || !r_nbytes) return CDK_Inv_Value; if (DEBUG_PKT) _gnutls_write_log("read_subpkt:\n"); n = 0; *r_nbytes = 0; c = cdk_stream_getc(inp); n++; if (c == 255) { size = read_32(inp); if (size == (u32)-1) return CDK_Inv_Packet; n += 4; } else if (c >= 192 && c < 255) { c1 = cdk_stream_getc(inp); if (c1 == EOF) return CDK_Inv_Packet; n++; if (c1 == 0) return 0; size = ((c - 192) << 8) + c1 + 192; } else if (c < 192) size = c; else return CDK_Inv_Packet; node = cdk_subpkt_new(size); if (!node) return CDK_Out_Of_Core; node->size = size; node->type = cdk_stream_getc(inp); if (DEBUG_PKT) _gnutls_write_log(" %d octets %d type\n", node->size, node->type); n++; node->size--; rc = stream_read(inp, node->d, node->size, &nread); if (rc) { cdk_subpkt_free(node); return rc; } n += nread; *r_nbytes = n; if (!*r_ctx) *r_ctx = node; else cdk_subpkt_add(*r_ctx, node); return rc; } static cdk_error_t read_onepass_sig(cdk_stream_t inp, size_t pktlen, cdk_pkt_onepass_sig_t sig) { if (!inp || !sig) return CDK_Inv_Value; if (DEBUG_PKT) _gnutls_write_log("read_onepass_sig: %d octets\n", (int) pktlen); if (pktlen != 13) return CDK_Inv_Packet; sig->version = cdk_stream_getc(inp); if (sig->version != 3) return CDK_Inv_Packet_Ver; sig->sig_class = cdk_stream_getc(inp); sig->digest_algo = _pgp_hash_algo_to_gnutls(cdk_stream_getc(inp)); sig->pubkey_algo = _pgp_pub_algo_to_cdk(cdk_stream_getc(inp)); sig->keyid[0] = read_32(inp); sig->keyid[1] = read_32(inp); sig->last = cdk_stream_getc(inp); return 0; } static cdk_error_t parse_sig_subpackets(cdk_pkt_signature_t sig) { cdk_subpkt_t node; /* Setup the standard packet entries, so we can use V4 signatures similar to V3. */ for (node = sig->unhashed; node; node = node->next) { if (node->type == CDK_SIGSUBPKT_ISSUER && node->size >= 8) { sig->keyid[0] = _cdk_buftou32(node->d); sig->keyid[1] = _cdk_buftou32(node->d + 4); } else if (node->type == CDK_SIGSUBPKT_EXPORTABLE && node->d[0] == 0) { /* Sometimes this packet might be placed in the unhashed area */ sig->flags.exportable = 0; } } for (node = sig->hashed; node; node = node->next) { if (node->type == CDK_SIGSUBPKT_SIG_CREATED && node->size >= 4) sig->timestamp = _cdk_buftou32(node->d); else if (node->type == CDK_SIGSUBPKT_SIG_EXPIRE && node->size >= 4) { sig->expiredate = _cdk_buftou32(node->d); if (sig->expiredate > 0 && sig->expiredate < (u32) gnutls_time(NULL)) sig->flags.expired = 1; } else if (node->type == CDK_SIGSUBPKT_POLICY) sig->flags.policy_url = 1; else if (node->type == CDK_SIGSUBPKT_NOTATION) sig->flags.notation = 1; else if (node->type == CDK_SIGSUBPKT_REVOCABLE && node->d[0] == 0) sig->flags.revocable = 0; else if (node->type == CDK_SIGSUBPKT_EXPORTABLE && node->d[0] == 0) sig->flags.exportable = 0; } if (sig->sig_class == 0x1F) { cdk_desig_revoker_t r, rnode; for (node = sig->hashed; node; node = node->next) { if (node->type == CDK_SIGSUBPKT_REV_KEY) { if (node->size < 22) continue; rnode = cdk_calloc(1, sizeof *rnode); if (!rnode) return CDK_Out_Of_Core; rnode->r_class = node->d[0]; rnode->algid = node->d[1]; memcpy(rnode->fpr, node->d + 2, KEY_FPR_LEN); if (!sig->revkeys) sig->revkeys = rnode; else { for (r = sig->revkeys; r->next; r = r->next); r->next = rnode; } } } } return 0; } static cdk_error_t read_signature(cdk_stream_t inp, size_t pktlen, cdk_pkt_signature_t sig) { size_t nbytes; size_t i, nsig; ssize_t size; cdk_error_t rc; if (!inp || !sig) return gnutls_assert_val(CDK_Inv_Value); if (DEBUG_PKT) _gnutls_write_log("read_signature: %d octets\n", (int) pktlen); if (pktlen < 16) return gnutls_assert_val(CDK_Inv_Packet); sig->version = cdk_stream_getc(inp); if (sig->version < 2 || sig->version > 4) return gnutls_assert_val(CDK_Inv_Packet_Ver); sig->flags.exportable = 1; sig->flags.revocable = 1; if (sig->version < 4) { if (cdk_stream_getc(inp) != 5) return gnutls_assert_val(CDK_Inv_Packet); sig->sig_class = cdk_stream_getc(inp); sig->timestamp = read_32(inp); sig->keyid[0] = read_32(inp); sig->keyid[1] = read_32(inp); sig->pubkey_algo = _pgp_pub_algo_to_cdk(cdk_stream_getc(inp)); sig->digest_algo = _pgp_hash_algo_to_gnutls(cdk_stream_getc(inp)); sig->digest_start[0] = cdk_stream_getc(inp); sig->digest_start[1] = cdk_stream_getc(inp); nsig = cdk_pk_get_nsig(sig->pubkey_algo); if (!nsig) return gnutls_assert_val(CDK_Inv_Algo); for (i = 0; i < nsig; i++) { rc = read_mpi(inp, &sig->mpi[i], 0); if (rc) return gnutls_assert_val(rc); } } else { sig->sig_class = cdk_stream_getc(inp); sig->pubkey_algo = _pgp_pub_algo_to_cdk(cdk_stream_getc(inp)); sig->digest_algo = _pgp_hash_algo_to_gnutls(cdk_stream_getc(inp)); sig->hashed_size = read_16(inp); size = sig->hashed_size; sig->hashed = NULL; while (size > 0) { rc = read_subpkt(inp, &sig->hashed, &nbytes); if (rc) return gnutls_assert_val(rc); size -= nbytes; } sig->unhashed_size = read_16(inp); size = sig->unhashed_size; sig->unhashed = NULL; while (size > 0) { rc = read_subpkt(inp, &sig->unhashed, &nbytes); if (rc) return gnutls_assert_val(rc); size -= nbytes; } rc = parse_sig_subpackets(sig); if (rc) return gnutls_assert_val(rc); sig->digest_start[0] = cdk_stream_getc(inp); sig->digest_start[1] = cdk_stream_getc(inp); nsig = cdk_pk_get_nsig(sig->pubkey_algo); if (!nsig) return gnutls_assert_val(CDK_Inv_Algo); for (i = 0; i < nsig; i++) { rc = read_mpi(inp, &sig->mpi[i], 0); if (rc) return gnutls_assert_val(rc); } } return 0; } static cdk_error_t read_literal(cdk_stream_t inp, size_t pktlen, cdk_pkt_literal_t * ret_pt, int is_partial) { cdk_pkt_literal_t pt = *ret_pt; size_t nread = 0; cdk_error_t rc; if (!inp || !pt) return CDK_Inv_Value; if (DEBUG_PKT) _gnutls_write_log("read_literal: %d octets\n", (int) pktlen); pt->mode = cdk_stream_getc(inp); if (pt->mode != 0x62 && pt->mode != 0x74 && pt->mode != 0x75) return CDK_Inv_Packet; if (cdk_stream_eof(inp)) return CDK_Inv_Packet; pt->namelen = cdk_stream_getc(inp); if (pt->namelen > 0) { *ret_pt = pt = cdk_realloc(pt, sizeof *pt + pt->namelen + 2); if (!pt) return CDK_Out_Of_Core; pt->name = (char *) pt + sizeof(*pt); rc = stream_read(inp, pt->name, pt->namelen, &nread); if (rc) return rc; if ((int) nread != pt->namelen) return CDK_Inv_Packet; pt->name[pt->namelen] = '\0'; } pt->timestamp = read_32(inp); pktlen = pktlen - 6 - pt->namelen; if (is_partial) _cdk_stream_set_blockmode(inp, pktlen); pt->buf = inp; pt->len = pktlen; return 0; } /* Read an old packet CTB and return the length of the body. */ static void read_old_length(cdk_stream_t inp, int ctb, size_t * r_len, size_t * r_size) { int llen = ctb & 0x03; int c; if (llen == 0) { c = cdk_stream_getc(inp); if (c == EOF) goto fail; *r_len = c; (*r_size)++; } else if (llen == 1) { *r_len = read_16(inp); if (*r_len == (u16)-1) goto fail; (*r_size) += 2; } else if (llen == 2) { *r_len = read_32(inp); if (*r_len == (u32)-1) { goto fail; } (*r_size) += 4; } else { fail: *r_len = 0; *r_size = 0; } } /* Read a new CTB and decode the body length. */ static void read_new_length(cdk_stream_t inp, size_t * r_len, size_t * r_size, size_t * r_partial) { int c, c1; c = cdk_stream_getc(inp); if (c == EOF) return; (*r_size)++; if (c < 192) *r_len = c; else if (c >= 192 && c <= 223) { c1 = cdk_stream_getc(inp); if (c1 == EOF) return; (*r_size)++; *r_len = ((c - 192) << 8) + c1 + 192; } else if (c == 255) { c1 = read_32(inp); if (c1 == -1) { return; } *r_len = c1; (*r_size) += 4; } else { *r_len = 1 << (c & 0x1f); *r_partial = 1; } } /* Skip the current packet body. */ static cdk_error_t skip_packet(cdk_stream_t inp, size_t pktlen) { byte buf[BUFSIZE]; size_t nread = 0, buflen = DIM(buf); while (pktlen > 0) { cdk_error_t rc; rc = stream_read(inp, buf, pktlen > buflen ? buflen : pktlen, &nread); if (rc) return rc; pktlen -= nread; } assert(pktlen == 0); return 0; } #define MAX_PACKET_LEN (1<<24) /** * cdk_pkt_read: * @inp: the input stream * @pkt: allocated packet handle to store the packet * * Parse the next packet on the @inp stream and return its contents in @pkt. **/ cdk_error_t cdk_pkt_read(cdk_stream_t inp, cdk_packet_t pkt) { int ctb, is_newctb; int pkttype; size_t pktlen = 0, pktsize = 0, is_partial = 0; cdk_error_t rc; if (!inp || !pkt) return CDK_Inv_Value; ctb = cdk_stream_getc(inp); if (cdk_stream_eof(inp) || ctb == EOF) return CDK_EOF; else if (!ctb) return gnutls_assert_val(CDK_Inv_Packet); pktsize++; if (!(ctb & 0x80)) { _cdk_log_info("cdk_pkt_read: no openpgp data found. " "(ctb=%02X; fpos=%02X)\n", (int) ctb, (int) cdk_stream_tell(inp)); return gnutls_assert_val(CDK_Inv_Packet); } if (ctb & 0x40) { /* RFC2440 packet format. */ pkttype = ctb & 0x3f; is_newctb = 1; } else { /* the old RFC1991 packet format. */ pkttype = ctb & 0x3f; pkttype >>= 2; is_newctb = 0; } if (pkttype > 63) { _cdk_log_info("cdk_pkt_read: unknown type %d\n", pkttype); return gnutls_assert_val(CDK_Inv_Packet); } if (is_newctb) read_new_length(inp, &pktlen, &pktsize, &is_partial); else read_old_length(inp, ctb, &pktlen, &pktsize); /* enforce limits to ensure that the following calculations * do not overflow */ if (pktlen >= MAX_PACKET_LEN || pktsize >= MAX_PACKET_LEN) { _cdk_log_info("cdk_pkt_read: too long packet\n"); return gnutls_assert_val(CDK_Inv_Packet); } pkt->pkttype = pkttype; pkt->pktlen = pktlen; pkt->pktsize = pktsize + pktlen; pkt->old_ctb = is_newctb ? 0 : 1; switch (pkt->pkttype) { case CDK_PKT_ATTRIBUTE: #define NAME_SIZE (pkt->pktlen + 16 + 1) pkt->pkt.user_id = cdk_calloc(1, sizeof *pkt->pkt.user_id + NAME_SIZE); if (!pkt->pkt.user_id) return gnutls_assert_val(CDK_Out_Of_Core); pkt->pkt.user_id->name = (char *) pkt->pkt.user_id + sizeof(*pkt->pkt.user_id); rc = read_attribute(inp, pktlen, pkt->pkt.user_id, NAME_SIZE); pkt->pkttype = CDK_PKT_ATTRIBUTE; if (rc) return gnutls_assert_val(rc); break; case CDK_PKT_USER_ID: pkt->pkt.user_id = cdk_calloc(1, sizeof *pkt->pkt.user_id + pkt->pktlen + 1); if (!pkt->pkt.user_id) return gnutls_assert_val(CDK_Out_Of_Core); pkt->pkt.user_id->name = (char *) pkt->pkt.user_id + sizeof(*pkt->pkt.user_id); rc = read_user_id(inp, pktlen, pkt->pkt.user_id); if (rc) return gnutls_assert_val(rc); break; case CDK_PKT_PUBLIC_KEY: pkt->pkt.public_key = cdk_calloc(1, sizeof *pkt->pkt.public_key); if (!pkt->pkt.public_key) return gnutls_assert_val(CDK_Out_Of_Core); rc = read_public_key(inp, pktlen, pkt->pkt.public_key); if (rc) return gnutls_assert_val(rc); break; case CDK_PKT_PUBLIC_SUBKEY: pkt->pkt.public_key = cdk_calloc(1, sizeof *pkt->pkt.public_key); if (!pkt->pkt.public_key) return gnutls_assert_val(CDK_Out_Of_Core); rc = read_public_subkey(inp, pktlen, pkt->pkt.public_key); if (rc) return gnutls_assert_val(rc); break; case CDK_PKT_SECRET_KEY: pkt->pkt.secret_key = cdk_calloc(1, sizeof *pkt->pkt.secret_key); if (!pkt->pkt.secret_key) return gnutls_assert_val(CDK_Out_Of_Core); pkt->pkt.secret_key->pk = cdk_calloc(1, sizeof *pkt->pkt. secret_key->pk); if (!pkt->pkt.secret_key->pk) return gnutls_assert_val(CDK_Out_Of_Core); rc = read_secret_key(inp, pktlen, pkt->pkt.secret_key); if (rc) return gnutls_assert_val(rc); break; case CDK_PKT_SECRET_SUBKEY: pkt->pkt.secret_key = cdk_calloc(1, sizeof *pkt->pkt.secret_key); if (!pkt->pkt.secret_key) return gnutls_assert_val(CDK_Out_Of_Core); pkt->pkt.secret_key->pk = cdk_calloc(1, sizeof *pkt->pkt. secret_key->pk); if (!pkt->pkt.secret_key->pk) return gnutls_assert_val(CDK_Out_Of_Core); rc = read_secret_subkey(inp, pktlen, pkt->pkt.secret_key); if (rc) return gnutls_assert_val(rc); break; case CDK_PKT_LITERAL: pkt->pkt.literal = cdk_calloc(1, sizeof *pkt->pkt.literal); if (!pkt->pkt.literal) return gnutls_assert_val(CDK_Out_Of_Core); rc = read_literal(inp, pktlen, &pkt->pkt.literal, is_partial); if (rc) return gnutls_assert_val(rc); break; case CDK_PKT_ONEPASS_SIG: pkt->pkt.onepass_sig = cdk_calloc(1, sizeof *pkt->pkt.onepass_sig); if (!pkt->pkt.onepass_sig) return gnutls_assert_val(CDK_Out_Of_Core); rc = read_onepass_sig(inp, pktlen, pkt->pkt.onepass_sig); if (rc) return gnutls_assert_val(rc); break; case CDK_PKT_SIGNATURE: pkt->pkt.signature = cdk_calloc(1, sizeof *pkt->pkt.signature); if (!pkt->pkt.signature) return gnutls_assert_val(CDK_Out_Of_Core); rc = read_signature(inp, pktlen, pkt->pkt.signature); if (rc) return gnutls_assert_val(rc); break; case CDK_PKT_PUBKEY_ENC: pkt->pkt.pubkey_enc = cdk_calloc(1, sizeof *pkt->pkt.pubkey_enc); if (!pkt->pkt.pubkey_enc) return gnutls_assert_val(CDK_Out_Of_Core); rc = read_pubkey_enc(inp, pktlen, pkt->pkt.pubkey_enc); if (rc) return gnutls_assert_val(rc); break; case CDK_PKT_COMPRESSED: pkt->pkt.compressed = cdk_calloc(1, sizeof *pkt->pkt.compressed); if (!pkt->pkt.compressed) return gnutls_assert_val(CDK_Out_Of_Core); rc = read_compressed(inp, pktlen, pkt->pkt.compressed); if (rc) return gnutls_assert_val(rc); break; case CDK_PKT_MDC: pkt->pkt.mdc = cdk_calloc(1, sizeof *pkt->pkt.mdc); if (!pkt->pkt.mdc) return gnutls_assert_val(CDK_Out_Of_Core); rc = read_mdc(inp, pkt->pkt.mdc); if (rc) return gnutls_assert_val(rc); break; default: /* Skip all packets we don't understand */ rc = skip_packet(inp, pktlen); if (rc) return gnutls_assert_val(rc); break; } return rc; }