/* new-packet.c - packet handling (freeing, copying, ...) * Copyright (C) 2001, 2002, 2003, 2007, 2008, 2010 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 library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, * USA * */ #ifdef HAVE_CONFIG_H # include #endif #include #include #include "opencdk.h" #include "main.h" #include "packet.h" /* Release an array of MPI values. */ void _cdk_free_mpibuf (size_t n, bigint_t * array) { while (n--) { _gnutls_mpi_release (&array[n]); } } /** * cdk_pkt_new: * @r_pkt: the new packet * * Allocate a new packet. **/ cdk_error_t cdk_pkt_new (cdk_packet_t * r_pkt) { cdk_packet_t pkt; if (!r_pkt) return CDK_Inv_Value; pkt = cdk_calloc (1, sizeof *pkt); if (!pkt) return CDK_Out_Of_Core; *r_pkt = pkt; return 0; } static void free_pubkey_enc (cdk_pkt_pubkey_enc_t enc) { size_t nenc; if (!enc) return; nenc = cdk_pk_get_nenc (enc->pubkey_algo); _cdk_free_mpibuf (nenc, enc->mpi); cdk_free (enc); } static void free_literal (cdk_pkt_literal_t pt) { if (!pt) return; /* The buffer which is referenced in this packet is closed elsewhere. To close it here would cause a double close. */ cdk_free (pt); } void _cdk_free_userid (cdk_pkt_userid_t uid) { if (!uid) return; cdk_free (uid->prefs); uid->prefs = NULL; cdk_free (uid->attrib_img); uid->attrib_img = NULL; cdk_free (uid); } void _cdk_free_signature (cdk_pkt_signature_t sig) { cdk_desig_revoker_t r; size_t nsig; if (!sig) return; nsig = cdk_pk_get_nsig (sig->pubkey_algo); _cdk_free_mpibuf (nsig, sig->mpi); cdk_subpkt_free (sig->hashed); sig->hashed = NULL; cdk_subpkt_free (sig->unhashed); sig->unhashed = NULL; while (sig->revkeys) { r = sig->revkeys->next; cdk_free (sig->revkeys); sig->revkeys = r; } cdk_free (sig); } void cdk_pk_release (cdk_pubkey_t pk) { size_t npkey; if (!pk) return; npkey = cdk_pk_get_npkey (pk->pubkey_algo); _cdk_free_userid (pk->uid); pk->uid = NULL; cdk_free (pk->prefs); pk->prefs = NULL; _cdk_free_mpibuf (npkey, pk->mpi); cdk_free (pk); } void cdk_sk_release (cdk_seckey_t sk) { size_t nskey; if (!sk) return; nskey = cdk_pk_get_nskey (sk->pubkey_algo); _cdk_free_mpibuf (nskey, sk->mpi); cdk_free (sk->encdata); sk->encdata = NULL; cdk_pk_release (sk->pk); sk->pk = NULL; cdk_s2k_free (sk->protect.s2k); sk->protect.s2k = NULL; cdk_free (sk); } /* Detach the openpgp packet from the packet structure and release the packet structure itself. */ void _cdk_pkt_detach_free (cdk_packet_t pkt, int *r_pkttype, void **ctx) { /* For now we just allow this for keys. */ switch (pkt->pkttype) { case CDK_PKT_PUBLIC_KEY: case CDK_PKT_PUBLIC_SUBKEY: *ctx = pkt->pkt.public_key; break; case CDK_PKT_SECRET_KEY: case CDK_PKT_SECRET_SUBKEY: *ctx = pkt->pkt.secret_key; break; default: *r_pkttype = 0; return; } /* The caller might expect a specific packet type and is not interested to store it for later use. */ if (r_pkttype) *r_pkttype = pkt->pkttype; cdk_free (pkt); } void cdk_pkt_free (cdk_packet_t pkt) { if (!pkt) return; switch (pkt->pkttype) { case CDK_PKT_ATTRIBUTE: case CDK_PKT_USER_ID: _cdk_free_userid (pkt->pkt.user_id); break; case CDK_PKT_PUBLIC_KEY: case CDK_PKT_PUBLIC_SUBKEY: cdk_pk_release (pkt->pkt.public_key); break; case CDK_PKT_SECRET_KEY: case CDK_PKT_SECRET_SUBKEY: cdk_sk_release (pkt->pkt.secret_key); break; case CDK_PKT_SIGNATURE: _cdk_free_signature (pkt->pkt.signature); break; case CDK_PKT_PUBKEY_ENC: free_pubkey_enc (pkt->pkt.pubkey_enc); break; case CDK_PKT_MDC: cdk_free (pkt->pkt.mdc); break; case CDK_PKT_ONEPASS_SIG: cdk_free (pkt->pkt.onepass_sig); break; case CDK_PKT_LITERAL: free_literal (pkt->pkt.literal); break; case CDK_PKT_COMPRESSED: cdk_free (pkt->pkt.compressed); break; default: break; } /* Reset the packet type to avoid, when cdk_pkt_release() will be used, that the second cdk_pkt_free() call will double free the data. */ pkt->pkttype = 0; } /** * cdk_pkt_release: * @pkt: the packet * * Free the contents of the given package and * release the memory of the structure. **/ void cdk_pkt_release (cdk_packet_t pkt) { if (!pkt) return; cdk_pkt_free (pkt); cdk_free (pkt); } /** * cdk_pkt_alloc: * @r_pkt: output is the new packet * @pkttype: the requested packet type * * Allocate a new packet structure with the given packet type. **/ cdk_error_t cdk_pkt_alloc (cdk_packet_t * r_pkt, cdk_packet_type_t pkttype) { cdk_packet_t pkt; int rc; if (!r_pkt) return CDK_Inv_Value; rc = cdk_pkt_new (&pkt); if (rc) return rc; switch (pkttype) { case CDK_PKT_USER_ID: pkt->pkt.user_id = cdk_calloc (1, sizeof pkt->pkt.user_id); if (!pkt->pkt.user_id) return CDK_Out_Of_Core; pkt->pkt.user_id->name = NULL; break; case CDK_PKT_PUBLIC_KEY: case CDK_PKT_PUBLIC_SUBKEY: pkt->pkt.public_key = cdk_calloc (1, sizeof *pkt->pkt.public_key); if (!pkt->pkt.public_key) return CDK_Out_Of_Core; break; case CDK_PKT_SECRET_KEY: case CDK_PKT_SECRET_SUBKEY: pkt->pkt.secret_key = cdk_calloc (1, sizeof *pkt->pkt.secret_key); pkt->pkt.secret_key->pk = cdk_calloc (1, sizeof *pkt->pkt.secret_key->pk); if (!pkt->pkt.secret_key || !pkt->pkt.secret_key->pk) return CDK_Out_Of_Core; break; case CDK_PKT_SIGNATURE: pkt->pkt.signature = cdk_calloc (1, sizeof *pkt->pkt.signature); if (!pkt->pkt.signature) return CDK_Out_Of_Core; break; case CDK_PKT_PUBKEY_ENC: pkt->pkt.pubkey_enc = cdk_calloc (1, sizeof *pkt->pkt.pubkey_enc); if (!pkt->pkt.pubkey_enc) return CDK_Out_Of_Core; break; case CDK_PKT_MDC: pkt->pkt.mdc = cdk_calloc (1, sizeof *pkt->pkt.mdc); if (!pkt->pkt.mdc) return CDK_Out_Of_Core; break; case CDK_PKT_ONEPASS_SIG: pkt->pkt.onepass_sig = cdk_calloc (1, sizeof *pkt->pkt.onepass_sig); if (!pkt->pkt.onepass_sig) return CDK_Out_Of_Core; break; case CDK_PKT_LITERAL: /* FIXME: We would need the size of the file name to allocate extra bytes, otherwise the result would be useless. */ pkt->pkt.literal = cdk_calloc (1, sizeof *pkt->pkt.literal); if (!pkt->pkt.literal) return CDK_Out_Of_Core; pkt->pkt.literal->name = NULL; break; default: return CDK_Not_Implemented; } pkt->pkttype = pkttype; *r_pkt = pkt; return 0; } cdk_prefitem_t _cdk_copy_prefs (const cdk_prefitem_t prefs) { size_t n = 0; struct cdk_prefitem_s *new_prefs; if (!prefs) return NULL; for (n = 0; prefs[n].type; n++) ; new_prefs = cdk_calloc (1, sizeof *new_prefs * (n + 1)); if (!new_prefs) return NULL; for (n = 0; prefs[n].type; n++) { new_prefs[n].type = prefs[n].type; new_prefs[n].value = prefs[n].value; } new_prefs[n].type = CDK_PREFTYPE_NONE; new_prefs[n].value = 0; return new_prefs; } cdk_error_t _cdk_copy_userid (cdk_pkt_userid_t * dst, cdk_pkt_userid_t src) { cdk_pkt_userid_t u; if (!dst || !src) return CDK_Inv_Value; *dst = NULL; u = cdk_calloc (1, sizeof *u + strlen (src->name) + 2); if (!u) return CDK_Out_Of_Core; u->name = (char *) u + sizeof (*u); memcpy (u, src, sizeof *u); memcpy (u->name, src->name, strlen (src->name)); u->prefs = _cdk_copy_prefs (src->prefs); if (src->selfsig) _cdk_copy_signature (&u->selfsig, src->selfsig); *dst = u; return 0; } cdk_error_t _cdk_copy_pubkey (cdk_pkt_pubkey_t * dst, cdk_pkt_pubkey_t src) { cdk_pkt_pubkey_t k; int i; if (!dst || !src) return CDK_Inv_Value; *dst = NULL; k = cdk_calloc (1, sizeof *k); if (!k) return CDK_Out_Of_Core; memcpy (k, src, sizeof *k); if (src->uid) _cdk_copy_userid (&k->uid, src->uid); if (src->prefs) k->prefs = _cdk_copy_prefs (src->prefs); for (i = 0; i < cdk_pk_get_npkey (src->pubkey_algo); i++) k->mpi[i] = _gnutls_mpi_copy (src->mpi[i]); *dst = k; return 0; } cdk_error_t _cdk_copy_seckey (cdk_pkt_seckey_t * dst, cdk_pkt_seckey_t src) { cdk_pkt_seckey_t k; int i; if (!dst || !src) return CDK_Inv_Value; *dst = NULL; k = cdk_calloc (1, sizeof *k); if (!k) return CDK_Out_Of_Core; memcpy (k, src, sizeof *k); _cdk_copy_pubkey (&k->pk, src->pk); if (src->encdata) { k->encdata = cdk_calloc (1, src->enclen + 1); if (!k->encdata) return CDK_Out_Of_Core; memcpy (k->encdata, src->encdata, src->enclen); } _cdk_s2k_copy (&k->protect.s2k, src->protect.s2k); for (i = 0; i < cdk_pk_get_nskey (src->pubkey_algo); i++) { k->mpi[i] = _gnutls_mpi_copy (src->mpi[i]); } *dst = k; return 0; } cdk_error_t _cdk_copy_pk_to_sk (cdk_pkt_pubkey_t pk, cdk_pkt_seckey_t sk) { if (!pk || !sk) return CDK_Inv_Value; sk->version = pk->version; sk->expiredate = pk->expiredate; sk->pubkey_algo = _pgp_pub_algo_to_cdk (pk->pubkey_algo); sk->has_expired = pk->has_expired; sk->is_revoked = pk->is_revoked; sk->main_keyid[0] = pk->main_keyid[0]; sk->main_keyid[1] = pk->main_keyid[1]; sk->keyid[0] = pk->keyid[0]; sk->keyid[1] = pk->keyid[1]; return 0; } cdk_error_t _cdk_copy_signature (cdk_pkt_signature_t * dst, cdk_pkt_signature_t src) { cdk_pkt_signature_t s; if (!dst || !src) return CDK_Inv_Value; *dst = NULL; s = cdk_calloc (1, sizeof *s); if (!s) return CDK_Out_Of_Core; memcpy (s, src, sizeof *src); _cdk_subpkt_copy (&s->hashed, src->hashed); _cdk_subpkt_copy (&s->unhashed, src->unhashed); /* FIXME: Copy MPI parts */ *dst = s; return 0; } cdk_error_t _cdk_pubkey_compare (cdk_pkt_pubkey_t a, cdk_pkt_pubkey_t b) { int na, nb, i; if (a->timestamp != b->timestamp || a->pubkey_algo != b->pubkey_algo) return -1; if (a->version < 4 && a->expiredate != b->expiredate) return -1; na = cdk_pk_get_npkey (a->pubkey_algo); nb = cdk_pk_get_npkey (b->pubkey_algo); if (na != nb) return -1; for (i = 0; i < na; i++) { if (_gnutls_mpi_cmp (a->mpi[i], b->mpi[i])) return -1; } return 0; } /** * cdk_subpkt_free: * @ctx: the sub packet node to free * * Release the context. **/ void cdk_subpkt_free (cdk_subpkt_t ctx) { cdk_subpkt_t s; while (ctx) { s = ctx->next; cdk_free (ctx); ctx = s; } } /** * cdk_subpkt_find: * @ctx: the sub packet node * @type: the packet type to find * * Find the given packet type in the node. If no packet with this * type was found, return null otherwise pointer to the node. **/ cdk_subpkt_t cdk_subpkt_find (cdk_subpkt_t ctx, size_t type) { return cdk_subpkt_find_nth (ctx, type, 0); } /** * cdk_subpkt_type_count: * @ctx: The sub packet context * @type: The sub packet type. * * Return the amount of sub packets with this type. **/ size_t cdk_subpkt_type_count (cdk_subpkt_t ctx, size_t type) { cdk_subpkt_t s; size_t count; count = 0; for (s = ctx; s; s = s->next) { if (s->type == type) count++; } return count; } /** * cdk_subpkt_find_nth: * @ctx: The sub packet context * @type: The sub packet type * @index: The nth packet to retrieve, 0 means the first * * Return the nth sub packet of the given type. **/ cdk_subpkt_t cdk_subpkt_find_nth (cdk_subpkt_t ctx, size_t type, size_t idx) { cdk_subpkt_t s; size_t pos; pos = 0; for (s = ctx; s; s = s->next) { if (s->type == type && pos++ == idx) return s; } return NULL; } /** * cdk_subpkt_new: * @size: the size of the new context * * Create a new sub packet node with the size of @size. **/ cdk_subpkt_t cdk_subpkt_new (size_t size) { cdk_subpkt_t s; if (!size) return NULL; s = cdk_calloc (1, sizeof *s + size + 2); if (!s) return NULL; s->d = (char *) s + sizeof (*s); return s; } /** * cdk_subpkt_get_data: * @ctx: the sub packet node * @r_type: pointer store the packet type * @r_nbytes: pointer to store the packet size * * Extract the data from the given sub packet. The type is returned * in @r_type and the size in @r_nbytes. **/ const byte * cdk_subpkt_get_data (cdk_subpkt_t ctx, size_t * r_type, size_t * r_nbytes) { if (!ctx || !r_nbytes) return NULL; if (r_type) *r_type = ctx->type; *r_nbytes = ctx->size; return ctx->d; } /** * cdk_subpkt_add: * @root: the root node * @node: the node to add * * Add the node in @node to the root node @root. **/ cdk_error_t cdk_subpkt_add (cdk_subpkt_t root, cdk_subpkt_t node) { cdk_subpkt_t n1; if (!root) return CDK_Inv_Value; for (n1 = root; n1->next; n1 = n1->next) ; n1->next = node; return 0; } byte * _cdk_subpkt_get_array (cdk_subpkt_t s, int count, size_t * r_nbytes) { cdk_subpkt_t list; byte *buf; size_t n, nbytes; if (!s) { if (r_nbytes) *r_nbytes = 0; return NULL; } for (n = 0, list = s; list; list = list->next) { n++; /* type */ n += list->size; if (list->size < 192) n++; else if (list->size < 8384) n += 2; else n += 5; } buf = cdk_calloc (1, n + 1); if (!buf) return NULL; n = 0; for (list = s; list; list = list->next) { nbytes = 1 + list->size; /* type */ if (nbytes < 192) buf[n++] = nbytes; else if (nbytes < 8384) { buf[n++] = nbytes / 256 + 192; buf[n++] = nbytes % 256; } else { buf[n++] = 0xFF; buf[n++] = nbytes >> 24; buf[n++] = nbytes >> 16; buf[n++] = nbytes >> 8; buf[n++] = nbytes; } buf[n++] = list->type; memcpy (buf + n, list->d, list->size); n += list->size; } if (count) { cdk_free (buf); buf = NULL; } if (r_nbytes) *r_nbytes = n; return buf; } cdk_error_t _cdk_subpkt_copy (cdk_subpkt_t * r_dst, cdk_subpkt_t src) { cdk_subpkt_t root, p, node; if (!src || !r_dst) return CDK_Inv_Value; root = NULL; for (p = src; p; p = p->next) { node = cdk_subpkt_new (p->size); if (node) { memcpy (node->d, p->d, p->size); node->type = p->type; node->size = p->size; } if (!root) root = node; else cdk_subpkt_add (root, node); } *r_dst = root; return 0; } /** * cdk_subpkt_init: * @node: the sub packet node * @type: type of the packet which data should be initialized * @buf: the buffer with the actual data * @buflen: the size of the data * * Set the packet data of the given root and set the type of it. **/ void cdk_subpkt_init (cdk_subpkt_t node, size_t type, const void *buf, size_t buflen) { if (!node) return; node->type = type; node->size = buflen; memcpy (node->d, buf, buflen); } /* FIXME: We need to think of a public interface for it. */ const byte * cdk_key_desig_revoker_walk (cdk_desig_revoker_t root, cdk_desig_revoker_t * ctx, int *r_class, int *r_algid) { cdk_desig_revoker_t n; if (!*ctx) { *ctx = root; n = root; } else { n = (*ctx)->next; *ctx = n; } if (n && r_class && r_algid) { *r_class = n->r_class; *r_algid = n->algid; } return n ? n->fpr : NULL; } /** * cdk_subpkt_find_next: * @root: the base where to begin the iteration * @type: the type to find or 0 for the next node. * * Try to find the next node after @root with type. * If type is 0, the next node will be returned. **/ cdk_subpkt_t cdk_subpkt_find_next (cdk_subpkt_t root, size_t type) { cdk_subpkt_t node; for (node = root->next; node; node = node->next) { if (!type) return node; else if (node->type == type) return node; } return NULL; }