diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 09:59:15 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-27 09:59:15 +0000 |
commit | 8de1ee1b2b676b0d07586f0752750dd6b0fb7511 (patch) | |
tree | dd46fd7dc3863045696cd0e48032d8a36fa0daf5 /g10/build-packet.c | |
parent | Initial commit. (diff) | |
download | gnupg2-upstream.tar.xz gnupg2-upstream.zip |
Adding upstream version 2.2.27.upstream/2.2.27upstream
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'g10/build-packet.c')
-rw-r--r-- | g10/build-packet.c | 1802 |
1 files changed, 1802 insertions, 0 deletions
diff --git a/g10/build-packet.c b/g10/build-packet.c new file mode 100644 index 0000000..14e40a1 --- /dev/null +++ b/g10/build-packet.c @@ -0,0 +1,1802 @@ +/* build-packet.c - assemble packets and write them + * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005, + * 2006, 2010, 2011 Free Software Foundation, Inc. + * + * This file is part of GnuPG. + * + * GnuPG is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * GnuPG 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <https://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <ctype.h> + +#include "gpg.h" +#include "../common/util.h" +#include "packet.h" +#include "../common/status.h" +#include "../common/iobuf.h" +#include "../common/i18n.h" +#include "options.h" +#include "../common/host2net.h" + +static gpg_error_t do_ring_trust (iobuf_t out, PKT_ring_trust *rt); +static int do_user_id( IOBUF out, int ctb, PKT_user_id *uid ); +static int do_key (iobuf_t out, int ctb, PKT_public_key *pk); +static int do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc ); +static int do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ); +static u32 calc_plaintext( PKT_plaintext *pt ); +static int do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt ); +static int do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed ); +static int do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed ); +static int do_compressed( IOBUF out, int ctb, PKT_compressed *cd ); +static int do_signature( IOBUF out, int ctb, PKT_signature *sig ); +static int do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops ); + +static int calc_header_length( u32 len, int new_ctb ); +static int write_16(IOBUF inp, u16 a); +static int write_32(IOBUF inp, u32 a); +static int write_header( IOBUF out, int ctb, u32 len ); +static int write_sign_packet_header( IOBUF out, int ctb, u32 len ); +static int write_header2( IOBUF out, int ctb, u32 len, int hdrlen ); +static int write_new_header( IOBUF out, int ctb, u32 len, int hdrlen ); + +/* Returns 1 if CTB is a new format ctb and 0 if CTB is an old format + ctb. */ +static int +ctb_new_format_p (int ctb) +{ + /* Bit 7 must always be set. */ + log_assert ((ctb & (1 << 7))); + /* Bit 6 indicates whether the packet is a new format packet. */ + return (ctb & (1 << 6)); +} + +/* Extract the packet type from a CTB. */ +static int +ctb_pkttype (int ctb) +{ + if (ctb_new_format_p (ctb)) + /* Bits 0 through 5 are the packet type. */ + return (ctb & ((1 << 6) - 1)); + else + /* Bits 2 through 5 are the packet type. */ + return (ctb & ((1 << 6) - 1)) >> 2; +} + + +/* Build a packet and write it to the stream OUT. + * Returns: 0 on success or on an error code. */ +int +build_packet (IOBUF out, PACKET *pkt) +{ + int rc = 0; + int new_ctb = 0; + int ctb, pkttype; + + if (DBG_PACKET) + log_debug ("build_packet() type=%d\n", pkt->pkttype); + log_assert (pkt->pkt.generic); + + switch ((pkttype = pkt->pkttype)) + { + case PKT_PUBLIC_KEY: + if (pkt->pkt.public_key->seckey_info) + pkttype = PKT_SECRET_KEY; + break; + case PKT_PUBLIC_SUBKEY: + if (pkt->pkt.public_key->seckey_info) + pkttype = PKT_SECRET_SUBKEY; + break; + case PKT_PLAINTEXT: + new_ctb = pkt->pkt.plaintext->new_ctb; + break; + case PKT_ENCRYPTED: + case PKT_ENCRYPTED_MDC: + new_ctb = pkt->pkt.encrypted->new_ctb; + break; + case PKT_COMPRESSED: + new_ctb = pkt->pkt.compressed->new_ctb; + break; + case PKT_USER_ID: + if (pkt->pkt.user_id->attrib_data) + pkttype = PKT_ATTRIBUTE; + break; + default: + break; + } + + if (new_ctb || pkttype > 15) /* new format */ + ctb = (0xc0 | (pkttype & 0x3f)); + else + ctb = (0x80 | ((pkttype & 15)<<2)); + switch (pkttype) + { + case PKT_ATTRIBUTE: + case PKT_USER_ID: + rc = do_user_id (out, ctb, pkt->pkt.user_id); + break; + case PKT_OLD_COMMENT: + case PKT_COMMENT: + /* Ignore these. Theoretically, this will never be called as we + * have no way to output comment packets any longer, but just in + * case there is some code path that would end up outputting a + * comment that was written before comments were dropped (in the + * public key?) this is a no-op. */ + break; + case PKT_PUBLIC_SUBKEY: + case PKT_PUBLIC_KEY: + case PKT_SECRET_SUBKEY: + case PKT_SECRET_KEY: + rc = do_key (out, ctb, pkt->pkt.public_key); + break; + case PKT_SYMKEY_ENC: + rc = do_symkey_enc (out, ctb, pkt->pkt.symkey_enc); + break; + case PKT_PUBKEY_ENC: + rc = do_pubkey_enc (out, ctb, pkt->pkt.pubkey_enc); + break; + case PKT_PLAINTEXT: + rc = do_plaintext (out, ctb, pkt->pkt.plaintext); + break; + case PKT_ENCRYPTED: + rc = do_encrypted (out, ctb, pkt->pkt.encrypted); + break; + case PKT_ENCRYPTED_MDC: + rc = do_encrypted_mdc (out, ctb, pkt->pkt.encrypted); + break; + case PKT_COMPRESSED: + rc = do_compressed (out, ctb, pkt->pkt.compressed); + break; + case PKT_SIGNATURE: + rc = do_signature (out, ctb, pkt->pkt.signature); + break; + case PKT_ONEPASS_SIG: + rc = do_onepass_sig (out, ctb, pkt->pkt.onepass_sig); + break; + case PKT_RING_TRUST: + /* Ignore it (only written by build_packet_and_meta) */ + break; + case PKT_MDC: + /* We write it directly, so we should never see it here. */ + default: + log_bug ("invalid packet type in build_packet()\n"); + break; + } + + return rc; +} + + +/* Build a packet and write it to the stream OUT. This variant also + * writes the meta data using ring trust packets. Returns: 0 on + * success or on error code. */ +gpg_error_t +build_packet_and_meta (iobuf_t out, PACKET *pkt) +{ + gpg_error_t err; + PKT_ring_trust rt = {0}; + + err = build_packet (out, pkt); + if (err) + ; + else if (pkt->pkttype == PKT_SIGNATURE) + { + PKT_signature *sig = pkt->pkt.signature; + + rt.subtype = RING_TRUST_SIG; + /* Note: trustval is not yet used. */ + if (sig->flags.checked) + { + rt.sigcache = 1; + if (sig->flags.valid) + rt.sigcache |= 2; + } + err = do_ring_trust (out, &rt); + } + else if (pkt->pkttype == PKT_USER_ID + || pkt->pkttype == PKT_ATTRIBUTE) + { + PKT_user_id *uid = pkt->pkt.user_id; + + rt.subtype = RING_TRUST_UID; + rt.keyorg = uid->keyorg; + rt.keyupdate = uid->keyupdate; + rt.url = uid->updateurl; + err = do_ring_trust (out, &rt); + rt.url = NULL; + } + else if (pkt->pkttype == PKT_PUBLIC_KEY + || pkt->pkttype == PKT_SECRET_KEY) + { + PKT_public_key *pk = pkt->pkt.public_key; + + rt.subtype = RING_TRUST_KEY; + rt.keyorg = pk->keyorg; + rt.keyupdate = pk->keyupdate; + rt.url = pk->updateurl; + err = do_ring_trust (out, &rt); + rt.url = NULL; + + } + + return err; +} + + +/* + * Write the mpi A to OUT. + */ +gpg_error_t +gpg_mpi_write (iobuf_t out, gcry_mpi_t a) +{ + int rc; + + if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) + { + unsigned int nbits; + const unsigned char *p; + unsigned char lenhdr[2]; + + /* gcry_log_debugmpi ("a", a); */ + p = gcry_mpi_get_opaque (a, &nbits); + if (p) + { + /* Strip leading zero bits. */ + for (; nbits >= 8 && !*p; p++, nbits -= 8) + ; + if (nbits >= 8 && !(*p & 0x80)) + if (--nbits >= 7 && !(*p & 0x40)) + if (--nbits >= 6 && !(*p & 0x20)) + if (--nbits >= 5 && !(*p & 0x10)) + if (--nbits >= 4 && !(*p & 0x08)) + if (--nbits >= 3 && !(*p & 0x04)) + if (--nbits >= 2 && !(*p & 0x02)) + if (--nbits >= 1 && !(*p & 0x01)) + --nbits; + } + /* gcry_log_debug (" [%u bit]\n", nbits); */ + /* gcry_log_debughex (" ", p, (nbits+7)/8); */ + lenhdr[0] = nbits >> 8; + lenhdr[1] = nbits; + rc = iobuf_write (out, lenhdr, 2); + if (!rc && p) + rc = iobuf_write (out, p, (nbits+7)/8); + } + else + { + char buffer[(MAX_EXTERN_MPI_BITS+7)/8+2]; /* 2 is for the mpi length. */ + size_t nbytes; + + nbytes = DIM(buffer); + rc = gcry_mpi_print (GCRYMPI_FMT_PGP, buffer, nbytes, &nbytes, a ); + if( !rc ) + rc = iobuf_write( out, buffer, nbytes ); + else if (gpg_err_code(rc) == GPG_ERR_TOO_SHORT ) + { + log_info ("mpi too large (%u bits)\n", gcry_mpi_get_nbits (a)); + /* The buffer was too small. We better tell the user about the MPI. */ + rc = gpg_error (GPG_ERR_TOO_LARGE); + } + } + + return rc; +} + + +/* + * Write an opaque MPI to the output stream without length info. + */ +gpg_error_t +gpg_mpi_write_nohdr (iobuf_t out, gcry_mpi_t a) +{ + int rc; + + if (gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) + { + unsigned int nbits; + const void *p; + + p = gcry_mpi_get_opaque (a, &nbits); + rc = p ? iobuf_write (out, p, (nbits+7)/8) : 0; + } + else + rc = gpg_error (GPG_ERR_BAD_MPI); + + return rc; +} + + +/* Calculate the length of a packet described by PKT. */ +u32 +calc_packet_length( PACKET *pkt ) +{ + u32 n = 0; + int new_ctb = 0; + + log_assert (pkt->pkt.generic); + switch (pkt->pkttype) + { + case PKT_PLAINTEXT: + n = calc_plaintext (pkt->pkt.plaintext); + new_ctb = pkt->pkt.plaintext->new_ctb; + break; + case PKT_ATTRIBUTE: + case PKT_USER_ID: + case PKT_COMMENT: + case PKT_PUBLIC_KEY: + case PKT_SECRET_KEY: + case PKT_SYMKEY_ENC: + case PKT_PUBKEY_ENC: + case PKT_ENCRYPTED: + case PKT_SIGNATURE: + case PKT_ONEPASS_SIG: + case PKT_RING_TRUST: + case PKT_COMPRESSED: + default: + log_bug ("invalid packet type in calc_packet_length()"); + break; + } + + n += calc_header_length (n, new_ctb); + return n; +} + + +static gpg_error_t +write_fake_data (IOBUF out, gcry_mpi_t a) +{ + unsigned int n; + void *p; + + if (!a) + return 0; + if (!gcry_mpi_get_flag (a, GCRYMPI_FLAG_OPAQUE)) + return 0; /* e.g. due to generating a key with wrong usage. */ + p = gcry_mpi_get_opaque ( a, &n); + if (!p) + return 0; /* For example due to a read error in + parse-packet.c:read_rest. */ + return iobuf_write (out, p, (n+7)/8 ); +} + + +/* Write a ring trust meta packet. */ +static gpg_error_t +do_ring_trust (iobuf_t out, PKT_ring_trust *rt) +{ + unsigned int namelen = 0; + unsigned int pktlen = 6; + + if (rt->subtype == RING_TRUST_KEY || rt->subtype == RING_TRUST_UID) + { + if (rt->url) + namelen = strlen (rt->url); + pktlen += 1 + 4 + 1 + namelen; + } + + write_header (out, (0x80 | ((PKT_RING_TRUST & 15)<<2)), pktlen); + iobuf_put (out, rt->trustval); + iobuf_put (out, rt->sigcache); + iobuf_write (out, "gpg", 3); + iobuf_put (out, rt->subtype); + if (rt->subtype == RING_TRUST_KEY || rt->subtype == RING_TRUST_UID) + { + iobuf_put (out, rt->keyorg); + write_32 (out, rt->keyupdate); + iobuf_put (out, namelen); + if (namelen) + iobuf_write (out, rt->url, namelen); + } + + return 0; +} + + +/* Serialize the user id (RFC 4880, Section 5.11) or the user + * attribute UID (Section 5.12) and write it to OUT. + * + * CTB is the serialization's CTB. It specifies the header format and + * the packet's type. The header length must not be set. */ +static int +do_user_id( IOBUF out, int ctb, PKT_user_id *uid ) +{ + int rc; + int hdrlen; + + log_assert (ctb_pkttype (ctb) == PKT_USER_ID + || ctb_pkttype (ctb) == PKT_ATTRIBUTE); + + /* We need to take special care of a user ID with a length of 0: + * Without forcing HDRLEN to 2 in this case an indeterminate length + * packet would be written which is not allowed. Note that we are + * always called with a CTB indicating an old packet header format, + * so that forcing a 2 octet header works. We also check for the + * maximum allowed packet size by the parser using an arbitrary + * extra 10 bytes for header data. */ + if (uid->attrib_data) + { + if (uid->attrib_len > MAX_ATTR_PACKET_LENGTH - 10) + return gpg_error (GPG_ERR_TOO_LARGE); + hdrlen = uid->attrib_len? 0 : 2; + write_header2 (out, ctb, uid->attrib_len, hdrlen); + rc = iobuf_write( out, uid->attrib_data, uid->attrib_len ); + } + else + { + if (uid->len > MAX_UID_PACKET_LENGTH - 10) + return gpg_error (GPG_ERR_TOO_LARGE); + hdrlen = uid->len? 0 : 2; + write_header2 (out, ctb, uid->len, hdrlen); + rc = iobuf_write( out, uid->name, uid->len ); + } + return rc; +} + + +/* Serialize the key (RFC 4880, Section 5.5) described by PK and write + * it to OUT. + * + * This function serializes both primary keys and subkeys with or + * without a secret part. + * + * CTB is the serialization's CTB. It specifies the header format and + * the packet's type. The header length must not be set. + * + * PK->VERSION specifies the serialization format. A value of 0 means + * to use the default version. Currently, only version 4 packets are + * supported. + */ +static int +do_key (iobuf_t out, int ctb, PKT_public_key *pk) +{ + gpg_error_t err = 0; + /* The length of the body is stored in the packet's header, which + occurs before the body. Unfortunately, we don't know the length + of the packet's body until we've written all of the data! To + work around this, we first write the data into this temporary + buffer, then generate the header, and finally copy the contents + of this buffer to OUT. */ + iobuf_t a = iobuf_temp(); + int i, nskey, npkey; + + log_assert (pk->version == 0 || pk->version == 4); + log_assert (ctb_pkttype (ctb) == PKT_PUBLIC_KEY + || ctb_pkttype (ctb) == PKT_PUBLIC_SUBKEY + || ctb_pkttype (ctb) == PKT_SECRET_KEY + || ctb_pkttype (ctb) == PKT_SECRET_SUBKEY); + + /* Write the version number - if none is specified, use 4 */ + if ( !pk->version ) + iobuf_put ( a, 4 ); + else + iobuf_put ( a, pk->version ); + write_32 (a, pk->timestamp ); + + iobuf_put (a, pk->pubkey_algo ); + + /* Get number of secret and public parameters. They are held in one + array: the public ones followed by the secret ones. */ + nskey = pubkey_get_nskey (pk->pubkey_algo); + npkey = pubkey_get_npkey (pk->pubkey_algo); + + /* If we don't have any public parameters - which is for example the + case if we don't know the algorithm used - the parameters are + stored as one blob in a faked (opaque) MPI. */ + if (!npkey) + { + write_fake_data (a, pk->pkey[0]); + goto leave; + } + log_assert (npkey < nskey); + + for (i=0; i < npkey; i++ ) + { + if ( (pk->pubkey_algo == PUBKEY_ALGO_ECDSA && (i == 0)) + || (pk->pubkey_algo == PUBKEY_ALGO_EDDSA && (i == 0)) + || (pk->pubkey_algo == PUBKEY_ALGO_ECDH && (i == 0 || i == 2))) + err = gpg_mpi_write_nohdr (a, pk->pkey[i]); + else + err = gpg_mpi_write (a, pk->pkey[i]); + if (err) + goto leave; + } + + + if (pk->seckey_info) + { + /* This is a secret key packet. */ + struct seckey_info *ski = pk->seckey_info; + + /* Build the header for protected (encrypted) secret parameters. */ + if (ski->is_protected) + { + /* OpenPGP protection according to rfc2440. */ + iobuf_put (a, ski->sha1chk? 0xfe : 0xff); + iobuf_put (a, ski->algo); + if (ski->s2k.mode >= 1000) + { + /* These modes are not possible in OpenPGP, we use them + to implement our extensions, 101 can be viewed as a + private/experimental extension (this is not specified + in rfc2440 but the same scheme is used for all other + algorithm identifiers). */ + iobuf_put (a, 101); + iobuf_put (a, ski->s2k.hash_algo); + iobuf_write (a, "GNU", 3 ); + iobuf_put (a, ski->s2k.mode - 1000); + } + else + { + iobuf_put (a, ski->s2k.mode); + iobuf_put (a, ski->s2k.hash_algo); + } + + if (ski->s2k.mode == 1 || ski->s2k.mode == 3) + iobuf_write (a, ski->s2k.salt, 8); + + if (ski->s2k.mode == 3) + iobuf_put (a, ski->s2k.count); + + /* For our special modes 1001, 1002 we do not need an IV. */ + if (ski->s2k.mode != 1001 && ski->s2k.mode != 1002) + iobuf_write (a, ski->iv, ski->ivlen); + + } + else /* Not protected. */ + iobuf_put (a, 0 ); + + if (ski->s2k.mode == 1001) + ; /* GnuPG extension - don't write a secret key at all. */ + else if (ski->s2k.mode == 1002) + { + /* GnuPG extension - divert to OpenPGP smartcard. */ + /* Length of the serial number or 0 for no serial number. */ + iobuf_put (a, ski->ivlen ); + /* The serial number gets stored in the IV field. */ + iobuf_write (a, ski->iv, ski->ivlen); + } + else if (ski->is_protected) + { + /* The secret key is protected - write it out as it is. */ + byte *p; + unsigned int ndatabits; + + log_assert (gcry_mpi_get_flag (pk->pkey[npkey], GCRYMPI_FLAG_OPAQUE)); + p = gcry_mpi_get_opaque (pk->pkey[npkey], &ndatabits); + if (p) + iobuf_write (a, p, (ndatabits+7)/8 ); + } + else + { + /* Non-protected key. */ + for ( ; i < nskey; i++ ) + if ( (err = gpg_mpi_write (a, pk->pkey[i]))) + goto leave; + write_16 (a, ski->csum ); + } + } + + leave: + if (!err) + { + /* Build the header of the packet - which we must do after + writing all the other stuff, so that we know the length of + the packet */ + write_header2 (out, ctb, iobuf_get_temp_length(a), 0); + /* And finally write it out to the real stream. */ + err = iobuf_write_temp (out, a); + } + + iobuf_close (a); /* Close the temporary buffer */ + return err; +} + + +/* Serialize the symmetric-key encrypted session key packet (RFC 4880, + * 5.3) described by ENC and write it to OUT. + * + * CTB is the serialization's CTB. It specifies the header format and + * the packet's type. The header length must not be set. */ +static int +do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc ) +{ + int rc = 0; + IOBUF a = iobuf_temp(); + + log_assert (ctb_pkttype (ctb) == PKT_SYMKEY_ENC); + + /* The only acceptable version. */ + log_assert( enc->version == 4 ); + + /* RFC 4880, Section 3.7. */ + switch (enc->s2k.mode) + { + case 0: /* Simple S2K. */ + case 1: /* Salted S2K. */ + case 3: /* Iterated and salted S2K. */ + break; /* Reasonable values. */ + + default: + log_bug ("do_symkey_enc: s2k=%d\n", enc->s2k.mode); + } + iobuf_put( a, enc->version ); + iobuf_put( a, enc->cipher_algo ); + iobuf_put( a, enc->s2k.mode ); + iobuf_put( a, enc->s2k.hash_algo ); + if( enc->s2k.mode == 1 || enc->s2k.mode == 3 ) { + iobuf_write(a, enc->s2k.salt, 8 ); + if( enc->s2k.mode == 3 ) + iobuf_put(a, enc->s2k.count); + } + if( enc->seskeylen ) + iobuf_write(a, enc->seskey, enc->seskeylen ); + + write_header(out, ctb, iobuf_get_temp_length(a) ); + rc = iobuf_write_temp( out, a ); + + iobuf_close(a); + return rc; +} + + +/* Serialize the public-key encrypted session key packet (RFC 4880, + 5.1) described by ENC and write it to OUT. + + CTB is the serialization's CTB. It specifies the header format and + the packet's type. The header length must not be set. */ +static int +do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc ) +{ + int rc = 0; + int n, i; + IOBUF a = iobuf_temp(); + + log_assert (ctb_pkttype (ctb) == PKT_PUBKEY_ENC); + + iobuf_put (a, 3); /* Version. */ + + if ( enc->throw_keyid ) + { + write_32(a, 0 ); /* Don't tell Eve who can decrypt the message. */ + write_32(a, 0 ); + } + else + { + write_32(a, enc->keyid[0] ); + write_32(a, enc->keyid[1] ); + } + iobuf_put(a,enc->pubkey_algo ); + n = pubkey_get_nenc( enc->pubkey_algo ); + if ( !n ) + write_fake_data( a, enc->data[0] ); + + for (i=0; i < n && !rc ; i++ ) + { + if (enc->pubkey_algo == PUBKEY_ALGO_ECDH && i == 1) + rc = gpg_mpi_write_nohdr (a, enc->data[i]); + else + rc = gpg_mpi_write (a, enc->data[i]); + } + + if (!rc) + { + write_header (out, ctb, iobuf_get_temp_length(a) ); + rc = iobuf_write_temp (out, a); + } + iobuf_close(a); + return rc; +} + + +/* Calculate the length of the serialized plaintext packet PT (RFC + 4480, Section 5.9). */ +static u32 +calc_plaintext( PKT_plaintext *pt ) +{ + /* Truncate namelen to the maximum 255 characters. Note this means + that a function that calls build_packet with an illegal literal + packet will get it back legalized. */ + + if(pt->namelen>255) + pt->namelen=255; + + return pt->len? (1 + 1 + pt->namelen + 4 + pt->len) : 0; +} + +/* Serialize the plaintext packet (RFC 4880, 5.9) described by PT and + write it to OUT. + + The body of the message is stored in PT->BUF. The amount of data + to write is PT->LEN. (PT->BUF should be configured to return EOF + after this much data has been read.) If PT->LEN is 0 and CTB + indicates that this is a new format packet, then partial block mode + is assumed to have been enabled on OUT. On success, partial block + mode is disabled. + + If PT->BUF is NULL, the caller must write out the data. In + this case, if PT->LEN was 0, then partial body length mode was + enabled and the caller must disable it by calling + iobuf_set_partial_body_length_mode (out, 0). */ +static int +do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt ) +{ + int rc = 0; + size_t nbytes; + + log_assert (ctb_pkttype (ctb) == PKT_PLAINTEXT); + + write_header(out, ctb, calc_plaintext( pt ) ); + log_assert (pt->mode == 'b' || pt->mode == 't' || pt->mode == 'u' + || pt->mode == 'm' + || pt->mode == 'l' || pt->mode == '1'); + iobuf_put(out, pt->mode ); + iobuf_put(out, pt->namelen ); + iobuf_write (out, pt->name, pt->namelen); + rc = write_32(out, pt->timestamp ); + if (rc) + return rc; + + if (pt->buf) + { + nbytes = iobuf_copy (out, pt->buf); + if(ctb_new_format_p (ctb) && !pt->len) + /* Turn off partial body length mode. */ + iobuf_set_partial_body_length_mode (out, 0); + if( pt->len && nbytes != pt->len ) + log_error("do_plaintext(): wrote %lu bytes but expected %lu bytes\n", + (ulong)nbytes, (ulong)pt->len ); + } + + return rc; +} + + + +/* Serialize the symmetrically encrypted data packet (RFC 4880, + Section 5.7) described by ED and write it to OUT. + + Note: this only writes the packets header! The call must then + follow up and write the initial random data and the body to OUT. + (If you use the encryption iobuf filter (cipher_filter), then this + is done automatically.) */ +static int +do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed ) +{ + int rc = 0; + u32 n; + + log_assert (! ed->mdc_method); + log_assert (ctb_pkttype (ctb) == PKT_ENCRYPTED); + + n = ed->len ? (ed->len + ed->extralen) : 0; + write_header(out, ctb, n ); + + /* This is all. The caller has to write the real data */ + + return rc; +} + +/* Serialize the symmetrically encrypted integrity protected data + packet (RFC 4880, Section 5.13) described by ED and write it to + OUT. + + Note: this only writes the packet's header! The caller must then + follow up and write the initial random data, the body and the MDC + packet to OUT. (If you use the encryption iobuf filter + (cipher_filter), then this is done automatically.) */ +static int +do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed ) +{ + int rc = 0; + u32 n; + + log_assert (ed->mdc_method); + log_assert (ctb_pkttype (ctb) == PKT_ENCRYPTED_MDC); + + /* Take version number and the following MDC packet in account. */ + n = ed->len ? (ed->len + ed->extralen + 1 + 22) : 0; + write_header(out, ctb, n ); + iobuf_put(out, 1 ); /* version */ + + /* This is all. The caller has to write the real data */ + + return rc; +} + + +/* Serialize the compressed packet (RFC 4880, Section 5.6) described + by CD and write it to OUT. + + Note: this only writes the packet's header! The caller must then + follow up and write the body to OUT. */ +static int +do_compressed( IOBUF out, int ctb, PKT_compressed *cd ) +{ + int rc = 0; + + log_assert (ctb_pkttype (ctb) == PKT_COMPRESSED); + + /* We must use the old convention and don't use blockmode for the + sake of PGP 2 compatibility. However if the new_ctb flag was + set, CTB is already formatted as new style and write_header2 + does create a partial length encoding using new the new + style. */ + write_header2(out, ctb, 0, 0); + iobuf_put(out, cd->algorithm ); + + /* This is all. The caller has to write the real data */ + + return rc; +} + + +/**************** + * Delete all subpackets of type REQTYPE and return a bool whether a packet + * was deleted. + */ +int +delete_sig_subpkt (subpktarea_t *area, sigsubpkttype_t reqtype ) +{ + int buflen; + sigsubpkttype_t type; + byte *buffer, *bufstart; + size_t n; + size_t unused = 0; + int okay = 0; + + if( !area ) + return 0; + buflen = area->len; + buffer = area->data; + for(;;) { + if( !buflen ) { + okay = 1; + break; + } + bufstart = buffer; + n = *buffer++; buflen--; + if( n == 255 ) { + if( buflen < 4 ) + break; + n = buf32_to_size_t (buffer); + buffer += 4; + buflen -= 4; + } + else if( n >= 192 ) { + if( buflen < 2 ) + break; + n = (( n - 192 ) << 8) + *buffer + 192; + buffer++; + buflen--; + } + if( buflen < n ) + break; + + type = *buffer & 0x7f; + if( type == reqtype ) { + buffer++; + buflen--; + n--; + if( n > buflen ) + break; + buffer += n; /* point to next subpkt */ + buflen -= n; + memmove (bufstart, buffer, buflen); /* shift */ + unused += buffer - bufstart; + buffer = bufstart; + } + else { + buffer += n; buflen -=n; + } + } + + if (!okay) + log_error ("delete_subpkt: buffer shorter than subpacket\n"); + log_assert (unused <= area->len); + area->len -= unused; + return !!unused; +} + + +/**************** + * Create or update a signature subpacket for SIG of TYPE. This + * functions knows where to put the data (hashed or unhashed). The + * function may move data from the unhashed part to the hashed one. + * Note: All pointers into sig->[un]hashed (e.g. returned by + * parse_sig_subpkt) are not valid after a call to this function. The + * data to put into the subpaket should be in a buffer with a length + * of buflen. + */ +void +build_sig_subpkt (PKT_signature *sig, sigsubpkttype_t type, + const byte *buffer, size_t buflen ) +{ + byte *p; + int critical, hashed; + subpktarea_t *oldarea, *newarea; + size_t nlen, n, n0; + + critical = (type & SIGSUBPKT_FLAG_CRITICAL); + type &= ~SIGSUBPKT_FLAG_CRITICAL; + + /* Sanity check buffer sizes */ + if(parse_one_sig_subpkt(buffer,buflen,type)<0) + BUG(); + + switch(type) + { + case SIGSUBPKT_NOTATION: + case SIGSUBPKT_POLICY: + case SIGSUBPKT_REV_KEY: + case SIGSUBPKT_SIGNATURE: + /* we do allow multiple subpackets */ + break; + + default: + /* we don't allow multiple subpackets */ + delete_sig_subpkt(sig->hashed,type); + delete_sig_subpkt(sig->unhashed,type); + break; + } + + /* Any special magic that needs to be done for this type so the + packet doesn't need to be reparsed? */ + switch(type) + { + case SIGSUBPKT_NOTATION: + sig->flags.notation=1; + break; + + case SIGSUBPKT_POLICY: + sig->flags.policy_url=1; + break; + + case SIGSUBPKT_PREF_KS: + sig->flags.pref_ks=1; + break; + + case SIGSUBPKT_EXPORTABLE: + if(buffer[0]) + sig->flags.exportable=1; + else + sig->flags.exportable=0; + break; + + case SIGSUBPKT_REVOCABLE: + if(buffer[0]) + sig->flags.revocable=1; + else + sig->flags.revocable=0; + break; + + case SIGSUBPKT_TRUST: + sig->trust_depth=buffer[0]; + sig->trust_value=buffer[1]; + break; + + case SIGSUBPKT_REGEXP: + sig->trust_regexp=buffer; + break; + + /* This should never happen since we don't currently allow + creating such a subpacket, but just in case... */ + case SIGSUBPKT_SIG_EXPIRE: + if(buf32_to_u32(buffer)+sig->timestamp<=make_timestamp()) + sig->flags.expired=1; + else + sig->flags.expired=0; + break; + + default: + break; + } + + if( (buflen+1) >= 8384 ) + nlen = 5; /* write 5 byte length header */ + else if( (buflen+1) >= 192 ) + nlen = 2; /* write 2 byte length header */ + else + nlen = 1; /* just a 1 byte length header */ + + switch( type ) + { + /* The issuer being unhashed is a historical oddity. It + should work equally as well hashed. Of course, if even an + unhashed issuer is tampered with, it makes it awfully hard + to verify the sig... */ + case SIGSUBPKT_ISSUER: + case SIGSUBPKT_SIGNATURE: + hashed = 0; + break; + default: + hashed = 1; + break; + } + + if( critical ) + type |= SIGSUBPKT_FLAG_CRITICAL; + + oldarea = hashed? sig->hashed : sig->unhashed; + + /* Calculate new size of the area and allocate */ + n0 = oldarea? oldarea->len : 0; + n = n0 + nlen + 1 + buflen; /* length, type, buffer */ + if (oldarea && n <= oldarea->size) { /* fits into the unused space */ + newarea = oldarea; + /*log_debug ("updating area for type %d\n", type );*/ + } + else if (oldarea) { + newarea = xrealloc (oldarea, sizeof (*newarea) + n - 1); + newarea->size = n; + /*log_debug ("reallocating area for type %d\n", type );*/ + } + else { + newarea = xmalloc (sizeof (*newarea) + n - 1); + newarea->size = n; + /*log_debug ("allocating area for type %d\n", type );*/ + } + newarea->len = n; + + p = newarea->data + n0; + if (nlen == 5) { + *p++ = 255; + *p++ = (buflen+1) >> 24; + *p++ = (buflen+1) >> 16; + *p++ = (buflen+1) >> 8; + *p++ = (buflen+1); + *p++ = type; + memcpy (p, buffer, buflen); + } + else if (nlen == 2) { + *p++ = (buflen+1-192) / 256 + 192; + *p++ = (buflen+1-192) % 256; + *p++ = type; + memcpy (p, buffer, buflen); + } + else { + *p++ = buflen+1; + *p++ = type; + memcpy (p, buffer, buflen); + } + + if (hashed) + sig->hashed = newarea; + else + sig->unhashed = newarea; +} + +/* + * Put all the required stuff from SIG into subpackets of sig. + * PKSK is the signing key. + * Hmmm, should we delete those subpackets which are in a wrong area? + */ +void +build_sig_subpkt_from_sig (PKT_signature *sig, PKT_public_key *pksk) +{ + u32 u; + byte buf[1+MAX_FINGERPRINT_LEN]; + size_t fprlen; + + /* For v4 keys we need to write the ISSUER subpacket. We do not + * want that for a future v5 format. */ + if (pksk->version < 5) + { + u = sig->keyid[0]; + buf[0] = (u >> 24) & 0xff; + buf[1] = (u >> 16) & 0xff; + buf[2] = (u >> 8) & 0xff; + buf[3] = u & 0xff; + u = sig->keyid[1]; + buf[4] = (u >> 24) & 0xff; + buf[5] = (u >> 16) & 0xff; + buf[6] = (u >> 8) & 0xff; + buf[7] = u & 0xff; + build_sig_subpkt (sig, SIGSUBPKT_ISSUER, buf, 8); + } + + /* Write the new ISSUER_FPR subpacket. */ + fingerprint_from_pk (pksk, buf+1, &fprlen); + if (fprlen == 20) + { + buf[0] = pksk->version; + build_sig_subpkt (sig, SIGSUBPKT_ISSUER_FPR, buf, 21); + } + + /* Write the timestamp. */ + u = sig->timestamp; + buf[0] = (u >> 24) & 0xff; + buf[1] = (u >> 16) & 0xff; + buf[2] = (u >> 8) & 0xff; + buf[3] = u & 0xff; + build_sig_subpkt( sig, SIGSUBPKT_SIG_CREATED, buf, 4 ); + + if(sig->expiredate) + { + if(sig->expiredate>sig->timestamp) + u=sig->expiredate-sig->timestamp; + else + u=1; /* A 1-second expiration time is the shortest one + OpenPGP has */ + + buf[0] = (u >> 24) & 0xff; + buf[1] = (u >> 16) & 0xff; + buf[2] = (u >> 8) & 0xff; + buf[3] = u & 0xff; + + /* Mark this CRITICAL, so if any implementation doesn't + understand sigs that can expire, it'll just disregard this + sig altogether. */ + + build_sig_subpkt( sig, SIGSUBPKT_SIG_EXPIRE | SIGSUBPKT_FLAG_CRITICAL, + buf, 4 ); + } +} + +void +build_attribute_subpkt(PKT_user_id *uid,byte type, + const void *buf,u32 buflen, + const void *header,u32 headerlen) +{ + byte *attrib; + int idx; + + if(1+headerlen+buflen>8383) + idx=5; + else if(1+headerlen+buflen>191) + idx=2; + else + idx=1; + + /* realloc uid->attrib_data to the right size */ + + uid->attrib_data=xrealloc(uid->attrib_data, + uid->attrib_len+idx+1+headerlen+buflen); + + attrib=&uid->attrib_data[uid->attrib_len]; + + if(idx==5) + { + attrib[0]=255; + attrib[1]=(1+headerlen+buflen) >> 24; + attrib[2]=(1+headerlen+buflen) >> 16; + attrib[3]=(1+headerlen+buflen) >> 8; + attrib[4]=1+headerlen+buflen; + } + else if(idx==2) + { + attrib[0]=(1+headerlen+buflen-192) / 256 + 192; + attrib[1]=(1+headerlen+buflen-192) % 256; + } + else + attrib[0]=1+headerlen+buflen; /* Good luck finding a JPEG this small! */ + + attrib[idx++]=type; + + /* Tack on our data at the end */ + + if(headerlen>0) + memcpy(&attrib[idx],header,headerlen); + memcpy(&attrib[idx+headerlen],buf,buflen); + uid->attrib_len+=idx+headerlen+buflen; +} + +/* Returns a human-readable string corresponding to the notation. + This ignores notation->value. The caller must free the result. */ +static char * +notation_value_to_human_readable_string (struct notation *notation) +{ + if(notation->bdat) + /* Binary data. */ + { + size_t len = notation->blen; + int i; + char preview[20]; + + for (i = 0; i < len && i < sizeof (preview) - 1; i ++) + if (isprint (notation->bdat[i])) + preview[i] = notation->bdat[i]; + else + preview[i] = '?'; + preview[i] = 0; + + return xasprintf (_("[ not human readable (%zu bytes: %s%s) ]"), + len, preview, i < len ? "..." : ""); + } + else + /* The value is human-readable. */ + return xstrdup (notation->value); +} + +/* Turn the notation described by the string STRING into a notation. + + STRING has the form: + + - -name - Delete the notation. + - name@domain.name=value - Normal notation + - !name@domain.name=value - Notation with critical bit set. + + The caller must free the result using free_notation(). */ +struct notation * +string_to_notation(const char *string,int is_utf8) +{ + const char *s; + int saw_at=0; + struct notation *notation; + + notation=xmalloc_clear(sizeof(*notation)); + + if(*string=='-') + { + notation->flags.ignore=1; + string++; + } + + if(*string=='!') + { + notation->flags.critical=1; + string++; + } + + /* If and when the IETF assigns some official name tags, we'll have + to add them here. */ + + for( s=string ; *s != '='; s++ ) + { + if( *s=='@') + saw_at++; + + /* -notationname is legal without an = sign */ + if(!*s && notation->flags.ignore) + break; + + if( !*s || !isascii (*s) || (!isgraph(*s) && !isspace(*s)) ) + { + log_error(_("a notation name must have only printable characters" + " or spaces, and end with an '='\n") ); + goto fail; + } + } + + notation->name=xmalloc((s-string)+1); + memcpy(notation->name,string,s-string); + notation->name[s-string]='\0'; + + if(!saw_at && !opt.expert) + { + log_error(_("a user notation name must contain the '@' character\n")); + goto fail; + } + + if (saw_at > 1) + { + log_error(_("a notation name must not contain more than" + " one '@' character\n")); + goto fail; + } + + if(*s) + { + const char *i=s+1; + int highbit=0; + + /* we only support printable text - therefore we enforce the use + of only printable characters (an empty value is valid) */ + for(s++; *s ; s++ ) + { + if ( !isascii (*s) ) + highbit=1; + else if (iscntrl(*s)) + { + log_error(_("a notation value must not use any" + " control characters\n")); + goto fail; + } + } + + if(!highbit || is_utf8) + notation->value=xstrdup(i); + else + notation->value=native_to_utf8(i); + } + + return notation; + + fail: + free_notation(notation); + return NULL; +} + +/* Like string_to_notation, but store opaque data rather than human + readable data. */ +struct notation * +blob_to_notation(const char *name, const char *data, size_t len) +{ + const char *s; + int saw_at=0; + struct notation *notation; + + notation=xmalloc_clear(sizeof(*notation)); + + if(*name=='-') + { + notation->flags.ignore=1; + name++; + } + + if(*name=='!') + { + notation->flags.critical=1; + name++; + } + + /* If and when the IETF assigns some official name tags, we'll have + to add them here. */ + + for( s=name ; *s; s++ ) + { + if( *s=='@') + saw_at++; + + /* -notationname is legal without an = sign */ + if(!*s && notation->flags.ignore) + break; + + if (*s == '=') + { + log_error(_("a notation name may not contain an '=' character\n")); + goto fail; + } + + if (!isascii (*s) || (!isgraph(*s) && !isspace(*s))) + { + log_error(_("a notation name must have only printable characters" + " or spaces\n") ); + goto fail; + } + } + + notation->name=xstrdup (name); + + if(!saw_at && !opt.expert) + { + log_error(_("a user notation name must contain the '@' character\n")); + goto fail; + } + + if (saw_at > 1) + { + log_error(_("a notation name must not contain more than" + " one '@' character\n")); + goto fail; + } + + notation->bdat = xmalloc (len); + memcpy (notation->bdat, data, len); + notation->blen = len; + + notation->value = notation_value_to_human_readable_string (notation); + + return notation; + + fail: + free_notation(notation); + return NULL; +} + +struct notation * +sig_to_notation(PKT_signature *sig) +{ + const byte *p; + size_t len; + int seq = 0; + int crit; + notation_t list = NULL; + + /* See RFC 4880, 5.2.3.16 for the format of notation data. In + short, a notation has: + + - 4 bytes of flags + - 2 byte name length (n1) + - 2 byte value length (n2) + - n1 bytes of name data + - n2 bytes of value data + */ + while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION,&len,&seq,&crit))) + { + int n1,n2; + struct notation *n=NULL; + + if(len<8) + { + log_info(_("WARNING: invalid notation data found\n")); + continue; + } + + /* name length. */ + n1=(p[4]<<8)|p[5]; + /* value length. */ + n2=(p[6]<<8)|p[7]; + + if(8+n1+n2!=len) + { + log_info(_("WARNING: invalid notation data found\n")); + continue; + } + + n=xmalloc_clear(sizeof(*n)); + n->name=xmalloc(n1+1); + + memcpy(n->name,&p[8],n1); + n->name[n1]='\0'; + + if(p[0]&0x80) + /* The value is human-readable. */ + { + n->value=xmalloc(n2+1); + memcpy(n->value,&p[8+n1],n2); + n->value[n2]='\0'; + n->flags.human = 1; + } + else + /* Binary data. */ + { + n->bdat=xmalloc(n2); + n->blen=n2; + memcpy(n->bdat,&p[8+n1],n2); + + n->value = notation_value_to_human_readable_string (n); + } + + n->flags.critical=crit; + + n->next=list; + list=n; + } + + return list; +} + +/* Release the resources associated with the *list* of notations. To + release a single notation, make sure that notation->next is + NULL. */ +void +free_notation(struct notation *notation) +{ + while(notation) + { + struct notation *n=notation; + + xfree(n->name); + xfree(n->value); + xfree(n->altvalue); + xfree(n->bdat); + notation=n->next; + xfree(n); + } +} + +/* Serialize the signature packet (RFC 4880, Section 5.2) described by + SIG and write it to OUT. */ +static int +do_signature( IOBUF out, int ctb, PKT_signature *sig ) +{ + int rc = 0; + int n, i; + IOBUF a = iobuf_temp(); + + log_assert (ctb_pkttype (ctb) == PKT_SIGNATURE); + + if ( !sig->version || sig->version == 3) + { + iobuf_put( a, 3 ); + + /* Version 3 packets don't support subpackets. */ + log_assert (! sig->hashed); + log_assert (! sig->unhashed); + } + else + iobuf_put( a, sig->version ); + if ( sig->version < 4 ) + iobuf_put (a, 5 ); /* Constant */ + iobuf_put (a, sig->sig_class ); + if ( sig->version < 4 ) + { + write_32(a, sig->timestamp ); + write_32(a, sig->keyid[0] ); + write_32(a, sig->keyid[1] ); + } + iobuf_put(a, sig->pubkey_algo ); + iobuf_put(a, sig->digest_algo ); + if ( sig->version >= 4 ) + { + size_t nn; + /* Timestamp and keyid must have been packed into the subpackets + prior to the call of this function, because these subpackets + are hashed. */ + nn = sig->hashed? sig->hashed->len : 0; + write_16(a, nn); + if (nn) + iobuf_write( a, sig->hashed->data, nn ); + nn = sig->unhashed? sig->unhashed->len : 0; + write_16(a, nn); + if (nn) + iobuf_write( a, sig->unhashed->data, nn ); + } + iobuf_put(a, sig->digest_start[0] ); + iobuf_put(a, sig->digest_start[1] ); + n = pubkey_get_nsig( sig->pubkey_algo ); + if ( !n ) + write_fake_data( a, sig->data[0] ); + for (i=0; i < n && !rc ; i++ ) + rc = gpg_mpi_write (a, sig->data[i] ); + + if (!rc) + { + if ( is_RSA(sig->pubkey_algo) && sig->version < 4 ) + write_sign_packet_header(out, ctb, iobuf_get_temp_length(a) ); + else + write_header(out, ctb, iobuf_get_temp_length(a) ); + rc = iobuf_write_temp( out, a ); + } + + iobuf_close(a); + return rc; +} + + +/* Serialize the one-pass signature packet (RFC 4880, Section 5.4) + described by OPS and write it to OUT. */ +static int +do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops ) +{ + log_assert (ctb_pkttype (ctb) == PKT_ONEPASS_SIG); + + write_header(out, ctb, 4 + 8 + 1); + + iobuf_put (out, 3); /* Version. */ + iobuf_put(out, ops->sig_class ); + iobuf_put(out, ops->digest_algo ); + iobuf_put(out, ops->pubkey_algo ); + write_32(out, ops->keyid[0] ); + write_32(out, ops->keyid[1] ); + iobuf_put(out, ops->last ); + + return 0; +} + + +/* Write a 16-bit quantity to OUT in big endian order. */ +static int +write_16(IOBUF out, u16 a) +{ + iobuf_put(out, a>>8); + if( iobuf_put(out,a) ) + return -1; + return 0; +} + +/* Write a 32-bit quantity to OUT in big endian order. */ +static int +write_32(IOBUF out, u32 a) +{ + iobuf_put(out, a>> 24); + iobuf_put(out, a>> 16); + iobuf_put(out, a>> 8); + return iobuf_put(out, a); +} + + +/**************** + * calculate the length of a header. + * + * LEN is the length of the packet's body. NEW_CTB is whether we are + * using a new or old format packet. + * + * This function does not handle indeterminate lengths or partial body + * lengths. (If you pass LEN as 0, then this function assumes you + * really mean an empty body.) + */ +static int +calc_header_length( u32 len, int new_ctb ) +{ + if( new_ctb ) { + if( len < 192 ) + return 2; + if( len < 8384 ) + return 3; + else + return 6; + } + if( len < 256 ) + return 2; + if( len < 65536 ) + return 3; + + return 5; +} + +/**************** + * Write the CTB and the packet length + */ +static int +write_header( IOBUF out, int ctb, u32 len ) +{ + return write_header2( out, ctb, len, 0 ); +} + + +static int +write_sign_packet_header (IOBUF out, int ctb, u32 len) +{ + (void)ctb; + + /* Work around a bug in the pgp read function for signature packets, + which are not correctly coded and silently assume at some point 2 + byte length headers.*/ + iobuf_put (out, 0x89 ); + iobuf_put (out, len >> 8 ); + return iobuf_put (out, len) == -1 ? -1:0; +} + +/**************** + * Write a packet header to OUT. + * + * CTB is the ctb. It determines whether a new or old format packet + * header should be written. The length field is adjusted, but the + * CTB is otherwise written out as is. + * + * LEN is the length of the packet's body. + * + * If HDRLEN is set, then we don't necessarily use the most efficient + * encoding to store LEN, but the specified length. (If this is not + * possible, this is a bug.) In this case, LEN=0 means a 0 length + * packet. Note: setting HDRLEN is only supported for old format + * packets! + * + * If HDRLEN is not set, then the shortest encoding is used. In this + * case, LEN=0 means the body has an indeterminate length and a + * partial body length header (if a new format packet) or an + * indeterminate length header (if an old format packet) is written + * out. Further, if using partial body lengths, this enables partial + * body length mode on OUT. + */ +static int +write_header2( IOBUF out, int ctb, u32 len, int hdrlen ) +{ + if (ctb_new_format_p (ctb)) + return write_new_header( out, ctb, len, hdrlen ); + + /* An old format packet. Refer to RFC 4880, Section 4.2.1 to + understand how lengths are encoded in this case. */ + + /* The length encoding is stored in the two least significant bits. + Make sure they are cleared. */ + log_assert ((ctb & 3) == 0); + + log_assert (hdrlen == 0 || hdrlen == 2 || hdrlen == 3 || hdrlen == 5); + + if (hdrlen) + /* Header length is given. */ + { + if( hdrlen == 2 && len < 256 ) + /* 00 => 1 byte length. */ + ; + else if( hdrlen == 3 && len < 65536 ) + /* 01 => 2 byte length. If len < 256, this is not the most + compact encoding, but it is a correct encoding. */ + ctb |= 1; + else if (hdrlen == 5) + /* 10 => 4 byte length. If len < 65536, this is not the most + compact encoding, but it is a correct encoding. */ + ctb |= 2; + else + log_bug ("Can't encode length=%d in a %d byte header!\n", + len, hdrlen); + } + else + { + if( !len ) + /* 11 => Indeterminate length. */ + ctb |= 3; + else if( len < 256 ) + /* 00 => 1 byte length. */ + ; + else if( len < 65536 ) + /* 01 => 2 byte length. */ + ctb |= 1; + else + /* 10 => 4 byte length. */ + ctb |= 2; + } + + if( iobuf_put(out, ctb ) ) + return -1; + + if( len || hdrlen ) + { + if( ctb & 2 ) + { + if(iobuf_put(out, len >> 24 )) + return -1; + if(iobuf_put(out, len >> 16 )) + return -1; + } + + if( ctb & 3 ) + if(iobuf_put(out, len >> 8 )) + return -1; + + if( iobuf_put(out, len ) ) + return -1; + } + + return 0; +} + + +/* Write a new format header to OUT. + + CTB is the ctb. + + LEN is the length of the packet's body. If LEN is 0, then enables + partial body length mode (i.e., the body is of an indeterminant + length) on OUT. Note: this function cannot be used to generate a + header for a zero length packet. + + HDRLEN is the length of the packet's header. If HDRLEN is 0, the + shortest encoding is chosen based on the length of the packet's + body. Currently, values other than 0 are not supported. + + Returns 0 on success. */ +static int +write_new_header( IOBUF out, int ctb, u32 len, int hdrlen ) +{ + if( hdrlen ) + log_bug("can't cope with hdrlen yet\n"); + + if( iobuf_put(out, ctb ) ) + return -1; + if( !len ) { + iobuf_set_partial_body_length_mode(out, 512 ); + } + else { + if( len < 192 ) { + if( iobuf_put(out, len ) ) + return -1; + } + else if( len < 8384 ) { + len -= 192; + if( iobuf_put( out, (len / 256) + 192) ) + return -1; + if( iobuf_put( out, (len % 256) ) ) + return -1; + } + else { + if( iobuf_put( out, 0xff ) ) + return -1; + if( iobuf_put( out, (len >> 24)&0xff ) ) + return -1; + if( iobuf_put( out, (len >> 16)&0xff ) ) + return -1; + if( iobuf_put( out, (len >> 8)&0xff ) ) + return -1; + if( iobuf_put( out, len & 0xff ) ) + return -1; + } + } + return 0; +} |