/* new-packet.c - packet handling (freeing, copying, ...) * 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 "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); if (!pkt->pkt.secret_key) return CDK_Out_Of_Core; pkt->pkt.secret_key->pk = cdk_calloc(1, sizeof *pkt->pkt.secret_key->pk); if (!pkt->pkt.secret_key->pk) { cdk_free(pkt->pkt.secret_key); pkt->pkt.secret_key = NULL; 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 = (byte *) 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) { nbytes -= 192; buf[n++] = nbytes / 256 + 192; buf[n++] = nbytes & 0xff; } 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; }