diff --git a/g10/keyid.c b/g10/keyid.c index d3fc29a98..23712e2a4 100644 --- a/g10/keyid.c +++ b/g10/keyid.c @@ -1,1087 +1,1090 @@ /* keyid.c - key ID and fingerprint handling * Copyright (C) 1998, 1999, 2000, 2001, 2003, * 2004, 2006, 2010 Free Software Foundation, Inc. * Copyright (C) 2014 Werner Koch * Copyright (C) 2016 g10 Code GmbH * * 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 . */ #include #include #include #include #include #include #include "gpg.h" #include "../common/util.h" #include "main.h" #include "packet.h" #include "options.h" #include "keydb.h" #include "../common/i18n.h" #include "rmd160.h" #include "../common/host2net.h" #define KEYID_STR_SIZE 19 #ifdef HAVE_UNSIGNED_TIME_T # define IS_INVALID_TIME_T(a) ((a) == (time_t)(-1)) #else /* Error or 32 bit time_t and value after 2038-01-19. */ # define IS_INVALID_TIME_T(a) ((a) < 0) #endif /* Return a letter describing the public key algorithms. */ int pubkey_letter( int algo ) { switch (algo) { case PUBKEY_ALGO_RSA: return 'R' ; case PUBKEY_ALGO_RSA_E: return 'r' ; case PUBKEY_ALGO_RSA_S: return 's' ; case PUBKEY_ALGO_ELGAMAL_E: return 'g' ; case PUBKEY_ALGO_ELGAMAL: return 'G' ; case PUBKEY_ALGO_DSA: return 'D' ; case PUBKEY_ALGO_ECDH: return 'e' ; /* ECC DH (encrypt only) */ case PUBKEY_ALGO_ECDSA: return 'E' ; /* ECC DSA (sign only) */ case PUBKEY_ALGO_EDDSA: return 'E' ; /* ECC EdDSA (sign only) */ default: return '?'; } } /* Return a string describing the public key algorithm and the keysize. For elliptic curves the function prints the name of the curve because the keysize is a property of the curve. The string is copied to the supplied buffer up a length of BUFSIZE-1. Examples for the output are: "rsa3072" - RSA with 3072 bit "elg1024" - Elgamal with 1024 bit "ed25519" - ECC using the curve Ed25519. "E_1.2.3.4" - ECC using the unsupported curve with OID "1.2.3.4". "E_1.3.6.1.4.1.11591.2.12242973" ECC with a bogus OID. "unknown_N" - Unknown OpenPGP algorithm N. If the option --legacy-list-mode is active, the output use the legacy format: "3072R" - RSA with 3072 bit "1024g" - Elgamal with 1024 bit "256E" - ECDSA using a curve with 256 bit The macro PUBKEY_STRING_SIZE may be used to allocate a buffer with a suitable size. Note that a more general version of this function exists as get_keyalgo_string. However, that has no special treatment for the old and unsupported Elgamal which we here print as xxxNNNN. */ char * pubkey_string (PKT_public_key *pk, char *buffer, size_t bufsize) { const char *prefix = NULL; if (opt.legacy_list_mode) { snprintf (buffer, bufsize, "%4u%c", nbits_from_pk (pk), pubkey_letter (pk->pubkey_algo)); return buffer; } switch (pk->pubkey_algo) { case PUBKEY_ALGO_RSA: case PUBKEY_ALGO_RSA_E: case PUBKEY_ALGO_RSA_S: prefix = "rsa"; break; case PUBKEY_ALGO_ELGAMAL_E: prefix = "elg"; break; case PUBKEY_ALGO_DSA: prefix = "dsa"; break; case PUBKEY_ALGO_ELGAMAL: prefix = "xxx"; break; case PUBKEY_ALGO_ECDH: case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_EDDSA: prefix = ""; break; } if (prefix && *prefix) snprintf (buffer, bufsize, "%s%u", prefix, nbits_from_pk (pk)); else if (prefix) { char *curve = openpgp_oid_to_str (pk->pkey[0]); const char *name = openpgp_oid_to_curve (curve, 0); if (name) snprintf (buffer, bufsize, "%s", name); else if (curve) snprintf (buffer, bufsize, "E_%s", curve); else snprintf (buffer, bufsize, "E_error"); xfree (curve); } else snprintf (buffer, bufsize, "unknown_%u", (unsigned int)pk->pubkey_algo); return buffer; } /* Hash a public key. This function is useful for v4 and v5 * fingerprints and for v3 or v4 key signing. */ void hash_public_key (gcry_md_hd_t md, PKT_public_key *pk) { unsigned int n; unsigned int nn[PUBKEY_MAX_NPKEY]; byte *pp[PUBKEY_MAX_NPKEY]; int i; unsigned int nbits; size_t nbytes; int npkey = pubkey_get_npkey (pk->pubkey_algo); int is_v5 = pk->version == 5; n = is_v5? 10 : 6; /* FIXME: We can avoid the extra malloc by calling only the first mpi_print here which computes the required length and calling the real mpi_print only at the end. The speed advantage would only be for ECC (opaque MPIs) or if we could implement an mpi_print variant with a callback handler to do the hashing. */ if (npkey==0 && pk->pkey[0] && gcry_mpi_get_flag (pk->pkey[0], GCRYMPI_FLAG_OPAQUE)) { pp[0] = gcry_mpi_get_opaque (pk->pkey[0], &nbits); nn[0] = (nbits+7)/8; n+=nn[0]; } else { for (i=0; i < npkey; i++ ) { if (!pk->pkey[i]) { /* This case may only happen if the parsing of the MPI failed but the key was anyway created. May happen during "gpg KEYFILE". */ pp[i] = NULL; nn[i] = 0; } else if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_OPAQUE)) { const void *p; int is_sos = 0; if (gcry_mpi_get_flag (pk->pkey[i], GCRYMPI_FLAG_USER2)) is_sos = 2; p = gcry_mpi_get_opaque (pk->pkey[i], &nbits); pp[i] = xmalloc ((nbits+7)/8 + is_sos); if (p) memcpy (pp[i] + is_sos, p, (nbits+7)/8); else pp[i] = NULL; if (is_sos) { pp[i][0] = (nbits >> 8); pp[i][1] = nbits; } nn[i] = (nbits+7)/8 + is_sos; n += nn[i]; } else { if (gcry_mpi_print (GCRYMPI_FMT_PGP, NULL, 0, &nbytes, pk->pkey[i])) BUG (); pp[i] = xmalloc (nbytes); if (gcry_mpi_print (GCRYMPI_FMT_PGP, pp[i], nbytes, &nbytes, pk->pkey[i])) BUG (); nn[i] = nbytes; n += nn[i]; } } } if (is_v5) { gcry_md_putc ( md, 0x9a ); /* ctb */ gcry_md_putc ( md, n >> 24 ); /* 4 byte length header */ gcry_md_putc ( md, n >> 16 ); gcry_md_putc ( md, n >> 8 ); gcry_md_putc ( md, n ); gcry_md_putc ( md, pk->version ); } else { gcry_md_putc ( md, 0x99 ); /* ctb */ gcry_md_putc ( md, n >> 8 ); /* 2 byte length header */ gcry_md_putc ( md, n ); gcry_md_putc ( md, pk->version ); } gcry_md_putc ( md, pk->timestamp >> 24 ); gcry_md_putc ( md, pk->timestamp >> 16 ); gcry_md_putc ( md, pk->timestamp >> 8 ); gcry_md_putc ( md, pk->timestamp ); gcry_md_putc ( md, pk->pubkey_algo ); if (is_v5) { n -= 10; gcry_md_putc ( md, n >> 24 ); gcry_md_putc ( md, n >> 16 ); gcry_md_putc ( md, n >> 8 ); gcry_md_putc ( md, n ); } if(npkey==0 && pk->pkey[0] && gcry_mpi_get_flag (pk->pkey[0], GCRYMPI_FLAG_OPAQUE)) { if (pp[0]) gcry_md_write (md, pp[0], nn[0]); } else { for(i=0; i < npkey; i++ ) { if (pp[i]) gcry_md_write ( md, pp[i], nn[i] ); xfree(pp[i]); } } } /* fixme: Check whether we can replace this function or if not describe why we need it. */ u32 v3_keyid (gcry_mpi_t a, u32 *ki) { byte *buffer, *p; size_t nbytes; if (gcry_mpi_print (GCRYMPI_FMT_USG, NULL, 0, &nbytes, a )) BUG (); /* fixme: allocate it on the stack */ buffer = xmalloc (nbytes); if (gcry_mpi_print( GCRYMPI_FMT_USG, buffer, nbytes, NULL, a )) BUG (); if (nbytes < 8) /* oops */ ki[0] = ki[1] = 0; else { p = buffer + nbytes - 8; ki[0] = buf32_to_u32 (p); p += 4; ki[1] = buf32_to_u32 (p); } xfree (buffer); return ki[1]; } /* Return PK's keyid. The memory is owned by PK. */ u32 * pk_keyid (PKT_public_key *pk) { keyid_from_pk (pk, NULL); /* Uncomment this for help tracking down bugs related to keyid or main_keyid not being set correctly. */ #if 0 if (! (pk->main_keyid[0] || pk->main_keyid[1])) log_bug ("pk->main_keyid not set!\n"); if (keyid_cmp (pk->keyid, pk->main_keyid) == 0 && ! pk->flags.primary) log_bug ("keyid and main_keyid are the same, but primary flag not set!\n"); if (keyid_cmp (pk->keyid, pk->main_keyid) != 0 && pk->flags.primary) log_bug ("keyid and main_keyid are different, but primary flag set!\n"); #endif return pk->keyid; } /* Return the keyid of the primary key associated with PK. The memory is owned by PK. */ u32 * pk_main_keyid (PKT_public_key *pk) { /* Uncomment this for help tracking down bugs related to keyid or main_keyid not being set correctly. */ #if 0 if (! (pk->main_keyid[0] || pk->main_keyid[1])) log_bug ("pk->main_keyid not set!\n"); #endif return pk->main_keyid; } /* Copy the keyid in SRC to DEST and return DEST. */ u32 * keyid_copy (u32 *dest, const u32 *src) { dest[0] = src[0]; dest[1] = src[1]; return dest; } char * format_keyid (u32 *keyid, int format, char *buffer, int len) { char tmp[KEYID_STR_SIZE]; if (! buffer) { buffer = tmp; len = sizeof (tmp); } if (format == KF_DEFAULT) format = opt.keyid_format; if (format == KF_DEFAULT) format = KF_NONE; switch (format) { case KF_NONE: if (len) *buffer = 0; break; case KF_SHORT: snprintf (buffer, len, "%08lX", (ulong)keyid[1]); break; case KF_LONG: snprintf (buffer, len, "%08lX%08lX", (ulong)keyid[0], (ulong)keyid[1]); break; case KF_0xSHORT: snprintf (buffer, len, "0x%08lX", (ulong)keyid[1]); break; case KF_0xLONG: snprintf (buffer, len, "0x%08lX%08lX", (ulong)keyid[0],(ulong)keyid[1]); break; default: BUG(); } if (buffer == tmp) return xstrdup (buffer); return buffer; } size_t keystrlen(void) { int format = opt.keyid_format; if (format == KF_DEFAULT) format = KF_NONE; switch(format) { case KF_NONE: return 0; case KF_SHORT: return 8; case KF_LONG: return 16; case KF_0xSHORT: return 10; case KF_0xLONG: return 18; default: BUG(); } } const char * keystr (u32 *keyid) { static char keyid_str[KEYID_STR_SIZE]; int format = opt.keyid_format; if (format == KF_DEFAULT) format = KF_NONE; if (format == KF_NONE) format = KF_LONG; return format_keyid (keyid, format, keyid_str, sizeof (keyid_str)); } /* This function returns the key id of the main and possible the * subkey as one string. It is used by error messages. */ const char * keystr_with_sub (u32 *main_kid, u32 *sub_kid) { static char buffer[KEYID_STR_SIZE+1+KEYID_STR_SIZE]; char *p; int format = opt.keyid_format; if (format == KF_NONE) format = KF_LONG; format_keyid (main_kid, format, buffer, KEYID_STR_SIZE); if (sub_kid) { p = buffer + strlen (buffer); *p++ = '/'; format_keyid (sub_kid, format, p, KEYID_STR_SIZE); } return buffer; } const char * keystr_from_pk(PKT_public_key *pk) { keyid_from_pk(pk,NULL); return keystr(pk->keyid); } const char * keystr_from_pk_with_sub (PKT_public_key *main_pk, PKT_public_key *sub_pk) { keyid_from_pk (main_pk, NULL); if (sub_pk) keyid_from_pk (sub_pk, NULL); return keystr_with_sub (main_pk->keyid, sub_pk? sub_pk->keyid:NULL); } /* Return PK's key id as a string using the default format. PK owns the storage. */ const char * pk_keyid_str (PKT_public_key *pk) { return keystr (pk_keyid (pk)); } const char * keystr_from_desc(KEYDB_SEARCH_DESC *desc) { switch(desc->mode) { case KEYDB_SEARCH_MODE_LONG_KID: case KEYDB_SEARCH_MODE_SHORT_KID: return keystr(desc->u.kid); case KEYDB_SEARCH_MODE_FPR: { u32 keyid[2]; if (desc->fprlen == 32) { keyid[0] = buf32_to_u32 (desc->u.fpr); keyid[1] = buf32_to_u32 (desc->u.fpr+4); } else if (desc->fprlen == 20) { keyid[0] = buf32_to_u32 (desc->u.fpr+12); keyid[1] = buf32_to_u32 (desc->u.fpr+16); } else if (desc->fprlen == 16) return "?v3 fpr?"; else /* oops */ return "?vx fpr?"; return keystr(keyid); } default: BUG(); } } /* Compute the fingerprint and keyid and store it in PK. */ static void compute_fingerprint (PKT_public_key *pk) { const byte *dp; gcry_md_hd_t md; size_t len; if (gcry_md_open (&md, pk->version == 5 ? GCRY_MD_SHA256 : GCRY_MD_SHA1, 0)) BUG (); hash_public_key (md, pk); gcry_md_final (md); dp = gcry_md_read (md, 0); len = gcry_md_get_algo_dlen (gcry_md_get_algo (md)); log_assert (len <= MAX_FINGERPRINT_LEN); memcpy (pk->fpr, dp, len); pk->fprlen = len; if (pk->version == 5) { pk->keyid[0] = buf32_to_u32 (dp); pk->keyid[1] = buf32_to_u32 (dp+4); } else { pk->keyid[0] = buf32_to_u32 (dp+12); pk->keyid[1] = buf32_to_u32 (dp+16); } gcry_md_close( md); } /* * Get the keyid from the public key PK and store it at KEYID unless * this is NULL. Returns the 32 bit short keyid. */ u32 keyid_from_pk (PKT_public_key *pk, u32 *keyid) { u32 dummy_keyid[2]; if (!keyid) keyid = dummy_keyid; if (!pk->fprlen) compute_fingerprint (pk); keyid[0] = pk->keyid[0]; keyid[1] = pk->keyid[1]; - return keyid[1]; /*FIXME:shortkeyid is different for v5*/ + if (pk->fprlen == 32) + return keyid[0]; + else + return keyid[1]; } /* * Get the keyid from the fingerprint. This function is simple for * most keys, but has to do a key lookup for old v3 keys where the * keyid is not part of the fingerprint. */ u32 keyid_from_fingerprint (ctrl_t ctrl, const byte *fprint, size_t fprint_len, u32 *keyid) { u32 dummy_keyid[2]; if( !keyid ) keyid = dummy_keyid; if (fprint_len != 20 && fprint_len != 32) { /* This is special as we have to lookup the key first. */ PKT_public_key pk; int rc; memset (&pk, 0, sizeof pk); rc = get_pubkey_byfprint (ctrl, &pk, NULL, fprint, fprint_len); if( rc ) { log_printhex (fprint, fprint_len, "Oops: keyid_from_fingerprint: no pubkey; fpr:"); keyid[0] = 0; keyid[1] = 0; } else keyid_from_pk (&pk, keyid); } else { const byte *dp = fprint; if (fprint_len == 20) /* v4 key */ { keyid[0] = buf32_to_u32 (dp+12); keyid[1] = buf32_to_u32 (dp+16); } else /* v5 key */ { keyid[0] = buf32_to_u32 (dp); keyid[1] = buf32_to_u32 (dp+4); } } return keyid[1]; } u32 keyid_from_sig (PKT_signature *sig, u32 *keyid) { if( keyid ) { keyid[0] = sig->keyid[0]; keyid[1] = sig->keyid[1]; } return sig->keyid[1]; /*FIXME:shortkeyid*/ } byte * namehash_from_uid (PKT_user_id *uid) { if (!uid->namehash) { uid->namehash = xmalloc (20); if (uid->attrib_data) rmd160_hash_buffer (uid->namehash, uid->attrib_data, uid->attrib_len); else rmd160_hash_buffer (uid->namehash, uid->name, uid->len); } return uid->namehash; } /* * Return the number of bits used in PK. */ unsigned int nbits_from_pk (PKT_public_key *pk) { return pubkey_nbits (pk->pubkey_algo, pk->pkey); } /* Convert an UTC TIMESTAMP into an UTC yyyy-mm-dd string. Return * that string. The caller should pass a buffer with at least a size * of MK_DATESTR_SIZE. */ char * mk_datestr (char *buffer, size_t bufsize, u32 timestamp) { time_t atime = timestamp; struct tm *tp; if (IS_INVALID_TIME_T (atime)) strcpy (buffer, "????" "-??" "-??"); /* Mark this as invalid. */ else { tp = gmtime (&atime); snprintf (buffer, bufsize, "%04d-%02d-%02d", 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday ); } return buffer; } /* * return a string with the creation date of the pk * Note: this is alloced in a static buffer. * Format is: yyyy-mm-dd */ const char * dateonlystr_from_pk (PKT_public_key *pk) { static char buffer[MK_DATESTR_SIZE]; return mk_datestr (buffer, sizeof buffer, pk->timestamp); } /* Same as dateonlystr_from_pk but with a global option a full iso * timestamp is returned. In this case it shares a static buffer with * isotimestamp(). */ const char * datestr_from_pk (PKT_public_key *pk) { if (opt.flags.full_timestrings) return isotimestamp (pk->timestamp); else return dateonlystr_from_pk (pk); } const char * dateonlystr_from_sig (PKT_signature *sig ) { static char buffer[MK_DATESTR_SIZE]; return mk_datestr (buffer, sizeof buffer, sig->timestamp); } const char * datestr_from_sig (PKT_signature *sig ) { if (opt.flags.full_timestrings) return isotimestamp (sig->timestamp); else return dateonlystr_from_sig (sig); } const char * expirestr_from_pk (PKT_public_key *pk) { static char buffer[MK_DATESTR_SIZE]; if (!pk->expiredate) return _("never "); if (opt.flags.full_timestrings) return isotimestamp (pk->expiredate); return mk_datestr (buffer, sizeof buffer, pk->expiredate); } const char * expirestr_from_sig (PKT_signature *sig) { static char buffer[MK_DATESTR_SIZE]; if (!sig->expiredate) return _("never "); if (opt.flags.full_timestrings) return isotimestamp (sig->expiredate); return mk_datestr (buffer, sizeof buffer, sig->expiredate); } const char * revokestr_from_pk( PKT_public_key *pk ) { static char buffer[MK_DATESTR_SIZE]; if(!pk->revoked.date) return _("never "); if (opt.flags.full_timestrings) return isotimestamp (pk->revoked.date); return mk_datestr (buffer, sizeof buffer, pk->revoked.date); } const char * usagestr_from_pk (PKT_public_key *pk, int fill) { static char buffer[10]; int i = 0; unsigned int use = pk->pubkey_usage; if ( use & PUBKEY_USAGE_SIG ) buffer[i++] = 'S'; if ( use & PUBKEY_USAGE_CERT ) buffer[i++] = 'C'; if ( use & PUBKEY_USAGE_ENC ) buffer[i++] = 'E'; if ( (use & PUBKEY_USAGE_AUTH) ) buffer[i++] = 'A'; while (fill && i < 4) buffer[i++] = ' '; buffer[i] = 0; return buffer; } const char * colon_strtime (u32 t) { static char buf[20]; if (!t) return ""; snprintf (buf, sizeof buf, "%lu", (ulong)t); return buf; } const char * colon_datestr_from_pk (PKT_public_key *pk) { static char buf[20]; snprintf (buf, sizeof buf, "%lu", (ulong)pk->timestamp); return buf; } const char * colon_datestr_from_sig (PKT_signature *sig) { static char buf[20]; snprintf (buf, sizeof buf, "%lu", (ulong)sig->timestamp); return buf; } const char * colon_expirestr_from_sig (PKT_signature *sig) { static char buf[20]; if (!sig->expiredate) return ""; snprintf (buf, sizeof buf,"%lu", (ulong)sig->expiredate); return buf; } /* * Return a byte array with the fingerprint for the given PK/SK * The length of the array is returned in ret_len. Caller must free * the array or provide an array of length MAX_FINGERPRINT_LEN. */ byte * fingerprint_from_pk (PKT_public_key *pk, byte *array, size_t *ret_len) { if (!pk->fprlen) compute_fingerprint (pk); if (!array) array = xmalloc (pk->fprlen); memcpy (array, pk->fpr, pk->fprlen); if (ret_len) *ret_len = pk->fprlen; return array; } /* Return an allocated buffer with the fingerprint of PK formatted as * a plain hexstring. If BUFFER is NULL the result is a malloc'd * string. If BUFFER is not NULL the result will be copied into this * buffer. In the latter case BUFLEN describes the length of the * buffer; if this is too short the function terminates the process. * Returns a malloc'ed string or BUFFER. A suitable length for BUFFER * is (2*MAX_FINGERPRINT_LEN + 1). */ char * hexfingerprint (PKT_public_key *pk, char *buffer, size_t buflen) { if (!pk->fprlen) compute_fingerprint (pk); if (!buffer) { buffer = xtrymalloc (2 * pk->fprlen + 1); if (!buffer) return NULL; } else if (buflen < 2 * pk->fprlen + 1) log_fatal ("%s: buffer too short (%zu)\n", __func__, buflen); bin2hex (pk->fpr, pk->fprlen, buffer); return buffer; } /* Pretty print a hex fingerprint. If BUFFER is NULL the result is a malloc'd string. If BUFFER is not NULL the result will be copied into this buffer. In the latter case BUFLEN describes the length of the buffer; if this is too short the function terminates the process. Returns a malloc'ed string or BUFFER. A suitable length for BUFFER is (MAX_FORMATTED_FINGERPRINT_LEN + 1). */ char * format_hexfingerprint (const char *fingerprint, char *buffer, size_t buflen) { int hexlen = strlen (fingerprint); int space; int i, j; if (hexlen == 40) /* v4 fingerprint */ { space = (/* The characters and the NUL. */ 40 + 1 /* After every fourth character, we add a space (except the last). */ + 40 / 4 - 1 /* Half way through we add a second space. */ + 1); } else if (hexlen == 64 || hexlen == 50) /* v5 fingerprint */ { /* The v5 fingerprint is commonly printed truncated to 25 * octets. We accept the truncated as well as the full hex * version here and format it like this: * 19347 BC987 24640 25F99 DF3EC 2E000 0ED98 84892 E1F7B 3EA4C */ hexlen = 50; space = 10 * 5 + 9 + 1; } else /* Other fingerprint versions - print as is. */ { /* We truncated here so that we do not need to provide a buffer * of a length which is in reality never used. */ if (hexlen > MAX_FORMATTED_FINGERPRINT_LEN - 1) hexlen = MAX_FORMATTED_FINGERPRINT_LEN - 1; space = hexlen + 1; } if (!buffer) buffer = xmalloc (space); else if (buflen < space) log_fatal ("%s: buffer too short (%zu)\n", __func__, buflen); if (hexlen == 40) /* v4 fingerprint */ { for (i = 0, j = 0; i < 40; i ++) { if (i && !(i % 4)) buffer[j ++] = ' '; if (i == 40 / 2) buffer[j ++] = ' '; buffer[j ++] = fingerprint[i]; } buffer[j ++] = 0; log_assert (j == space); } else if (hexlen == 50) /* v5 fingerprint */ { for (i=j=0; i < 50; i++) { if (i && !(i % 5)) buffer[j++] = ' '; buffer[j++] = fingerprint[i]; } buffer[j++] = 0; log_assert (j == space); } else { mem2str (buffer, fingerprint, space); } return buffer; } /* Return the so called KEYGRIP which is the SHA-1 hash of the public key parameters expressed as an canonical encoded S-Exp. ARRAY must be 20 bytes long. Returns 0 on success or an error code. */ gpg_error_t keygrip_from_pk (PKT_public_key *pk, unsigned char *array) { gpg_error_t err; gcry_sexp_t s_pkey; if (DBG_PACKET) log_debug ("get_keygrip for public key\n"); switch (pk->pubkey_algo) { case GCRY_PK_DSA: err = gcry_sexp_build (&s_pkey, NULL, "(public-key(dsa(p%m)(q%m)(g%m)(y%m)))", pk->pkey[0], pk->pkey[1], pk->pkey[2], pk->pkey[3]); break; case GCRY_PK_ELG: case GCRY_PK_ELG_E: err = gcry_sexp_build (&s_pkey, NULL, "(public-key(elg(p%m)(g%m)(y%m)))", pk->pkey[0], pk->pkey[1], pk->pkey[2]); break; case GCRY_PK_RSA: case GCRY_PK_RSA_S: case GCRY_PK_RSA_E: err = gcry_sexp_build (&s_pkey, NULL, "(public-key(rsa(n%m)(e%m)))", pk->pkey[0], pk->pkey[1]); break; case PUBKEY_ALGO_EDDSA: case PUBKEY_ALGO_ECDSA: case PUBKEY_ALGO_ECDH: { char *curve = openpgp_oid_to_str (pk->pkey[0]); if (!curve) err = gpg_error_from_syserror (); else { err = gcry_sexp_build (&s_pkey, NULL, pk->pubkey_algo == PUBKEY_ALGO_EDDSA? "(public-key(ecc(curve%s)(flags eddsa)(q%m)))": (pk->pubkey_algo == PUBKEY_ALGO_ECDH && openpgp_oid_is_cv25519 (pk->pkey[0]))? "(public-key(ecc(curve%s)(flags djb-tweak)(q%m)))": "(public-key(ecc(curve%s)(q%m)))", curve, pk->pkey[1]); xfree (curve); } } break; default: err = gpg_error (GPG_ERR_PUBKEY_ALGO); break; } if (err) return err; if (!gcry_pk_get_keygrip (s_pkey, array)) { char *hexfpr; hexfpr = hexfingerprint (pk, NULL, 0); log_info ("error computing keygrip (fpr=%s)\n", hexfpr); xfree (hexfpr); memset (array, 0, 20); err = gpg_error (GPG_ERR_GENERAL); } else { if (DBG_PACKET) log_printhex (array, 20, "keygrip="); /* FIXME: Save the keygrip in PK. */ } gcry_sexp_release (s_pkey); return err; } /* Store an allocated buffer with the keygrip of PK encoded as a hexstring at r_GRIP. Returns 0 on success. */ gpg_error_t hexkeygrip_from_pk (PKT_public_key *pk, char **r_grip) { gpg_error_t err; unsigned char grip[KEYGRIP_LEN]; *r_grip = NULL; err = keygrip_from_pk (pk, grip); if (!err) { char * buf = xtrymalloc (KEYGRIP_LEN * 2 + 1); if (!buf) err = gpg_error_from_syserror (); else { bin2hex (grip, KEYGRIP_LEN, buf); *r_grip = buf; } } return err; } diff --git a/g10/keyring.c b/g10/keyring.c index 992c280d2..9cd66db3a 100644 --- a/g10/keyring.c +++ b/g10/keyring.c @@ -1,1739 +1,1741 @@ /* keyring.c - keyring file handling * Copyright (C) 1998-2010 Free Software Foundation, Inc. * Copyright (C) 1997-2015 Werner Koch * * 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 . */ #include #include #include #include #include #include #include #include #include "gpg.h" #include "../common/util.h" #include "keyring.h" #include "packet.h" #include "keydb.h" #include "options.h" #include "main.h" /*for check_key_signature()*/ #include "../common/i18n.h" #include "../kbx/keybox.h" typedef struct keyring_resource *KR_RESOURCE; struct keyring_resource { struct keyring_resource *next; int read_only; dotlock_t lockhd; int is_locked; int did_full_scan; char fname[1]; }; typedef struct keyring_resource const * CONST_KR_RESOURCE; static KR_RESOURCE kr_resources; struct keyring_handle { CONST_KR_RESOURCE resource; struct { CONST_KR_RESOURCE kr; IOBUF iobuf; int eof; int error; } current; struct { CONST_KR_RESOURCE kr; off_t offset; size_t pk_no; size_t uid_no; unsigned int n_packets; /*used for delete and update*/ } found, saved_found; struct { char *name; char *pattern; } word_match; }; /* The number of extant handles. */ static int active_handles; static int do_copy (int mode, const char *fname, KBNODE root, off_t start_offset, unsigned int n_packets ); /* We keep a cache of entries that we have entered in the DB. This includes not only public keys, but also subkeys. Note: we'd like to keep the offset of the items that are present, however, this doesn't work, because another concurrent GnuPG process could modify the keyring. */ struct key_present { struct key_present *next; u32 kid[2]; }; /* For the hash table, we use separate chaining with linked lists. This means that we have an array of N linked lists (buckets), which is indexed by KEYID[1] mod N. Elements present in the keyring will be on the list; elements not present in the keyring will not be on the list. Note: since the hash table stores both present and not present information, it cannot be used until we complete a full scan of the keyring. This is indicated by key_present_hash_ready. */ typedef struct key_present **key_present_hash_t; static key_present_hash_t key_present_hash; static int key_present_hash_ready; #define KEY_PRESENT_HASH_BUCKETS 2048 /* Allocate a new value for a key present hash table. */ static struct key_present * key_present_value_new (void) { struct key_present *k; k = xmalloc_clear (sizeof *k); return k; } /* Allocate a new key present hash table. */ static key_present_hash_t key_present_hash_new (void) { struct key_present **tbl; tbl = xmalloc_clear (KEY_PRESENT_HASH_BUCKETS * sizeof *tbl); return tbl; } /* Return whether the value described by KID if it is in the hash table. Otherwise, return NULL. */ static struct key_present * key_present_hash_lookup (key_present_hash_t tbl, u32 *kid) { struct key_present *k; for (k = tbl[(kid[1] % (KEY_PRESENT_HASH_BUCKETS - 1))]; k; k = k->next) if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) return k; return NULL; } /* Add the key to the hash table TBL if it is not already present. */ static void key_present_hash_update (key_present_hash_t tbl, u32 *kid) { struct key_present *k; for (k = tbl[(kid[1] % (KEY_PRESENT_HASH_BUCKETS - 1))]; k; k = k->next) { if (k->kid[0] == kid[0] && k->kid[1] == kid[1]) return; } k = key_present_value_new (); k->kid[0] = kid[0]; k->kid[1] = kid[1]; k->next = tbl[(kid[1] % (KEY_PRESENT_HASH_BUCKETS - 1))]; tbl[(kid[1] % (KEY_PRESENT_HASH_BUCKETS - 1))] = k; } /* Add all the keys (public and subkeys) present in the keyblock to the hash TBL. */ static void key_present_hash_update_from_kb (key_present_hash_t tbl, KBNODE node) { for (; node; node = node->next) { if (node->pkt->pkttype == PKT_PUBLIC_KEY || node->pkt->pkttype == PKT_PUBLIC_SUBKEY) { u32 aki[2]; keyid_from_pk (node->pkt->pkt.public_key, aki); key_present_hash_update (tbl, aki); } } } /* * Register a filename for plain keyring files. ptr is set to a * pointer to be used to create a handles etc, or the already-issued * pointer if it has already been registered. The function returns 1 * if a new keyring was registered. */ int keyring_register_filename (const char *fname, int read_only, void **ptr) { KR_RESOURCE kr; if (active_handles) /* There are open handles. */ BUG (); for (kr=kr_resources; kr; kr = kr->next) { if (same_file_p (kr->fname, fname)) { /* Already registered. */ if (read_only) kr->read_only = 1; *ptr=kr; return 0; } } kr = xmalloc (sizeof *kr + strlen (fname)); strcpy (kr->fname, fname); kr->read_only = read_only; kr->lockhd = NULL; kr->is_locked = 0; kr->did_full_scan = 0; /* keep a list of all issued pointers */ kr->next = kr_resources; kr_resources = kr; /* create the offset table the first time a function here is used */ if (!key_present_hash) key_present_hash = key_present_hash_new (); *ptr=kr; return 1; } int keyring_is_writable (void *token) { KR_RESOURCE r = token; return r? (r->read_only || !access (r->fname, W_OK)) : 0; } /* Create a new handle for the resource associated with TOKEN. On error NULL is returned and ERRNO is set. The returned handle must be released using keyring_release (). */ KEYRING_HANDLE keyring_new (void *token) { KEYRING_HANDLE hd; KR_RESOURCE resource = token; log_assert (resource); hd = xtrycalloc (1, sizeof *hd); if (!hd) return hd; hd->resource = resource; active_handles++; return hd; } void keyring_release (KEYRING_HANDLE hd) { if (!hd) return; log_assert (active_handles > 0); active_handles--; xfree (hd->word_match.name); xfree (hd->word_match.pattern); iobuf_close (hd->current.iobuf); xfree (hd); } /* Save the current found state in HD for later retrieval by keybox_pop_found_state. Only one state may be saved. */ void keyring_push_found_state (KEYRING_HANDLE hd) { hd->saved_found = hd->found; hd->found.kr = NULL; } /* Restore the saved found state in HD. */ void keyring_pop_found_state (KEYRING_HANDLE hd) { hd->found = hd->saved_found; hd->saved_found.kr = NULL; } const char * keyring_get_resource_name (KEYRING_HANDLE hd) { if (!hd || !hd->resource) return NULL; return hd->resource->fname; } /* * Lock the keyring with the given handle, or unlock if YES is false. * We ignore the handle and lock all registered files. */ int keyring_lock (KEYRING_HANDLE hd, int yes) { KR_RESOURCE kr; int rc = 0; (void)hd; if (yes) { /* first make sure the lock handles are created */ for (kr=kr_resources; kr; kr = kr->next) { if (!keyring_is_writable(kr)) continue; if (!kr->lockhd) { kr->lockhd = dotlock_create (kr->fname, 0); if (!kr->lockhd) { log_info ("can't allocate lock for '%s'\n", kr->fname ); rc = GPG_ERR_GENERAL; } } } if (rc) return rc; /* and now set the locks */ for (kr=kr_resources; kr; kr = kr->next) { if (!keyring_is_writable(kr)) continue; if (kr->is_locked) continue; #ifdef HAVE_W32_SYSTEM /* Under Windows we need to CloseHandle the file before we * try to lock it. This is because another process might * have taken the lock and is using keybox_file_rename to * rename the base file. How if our dotlock_take below is * waiting for the lock but we have the base file still * open, keybox_file_rename will never succeed as we are * in a deadlock. */ iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)kr->fname); #endif /*HAVE_W32_SYSTEM*/ if (dotlock_take (kr->lockhd, -1) ) { log_info ("can't lock '%s'\n", kr->fname ); rc = GPG_ERR_GENERAL; } else kr->is_locked = 1; } } if (rc || !yes) { for (kr=kr_resources; kr; kr = kr->next) { if (!keyring_is_writable(kr)) continue; if (!kr->is_locked) continue; if (dotlock_release (kr->lockhd)) log_info ("can't unlock '%s'\n", kr->fname ); else kr->is_locked = 0; } } return rc; } /* * Return the last found keyblock. Caller must free it. * The returned keyblock has the kbode flag bit 0 set for the node with * the public key used to locate the keyblock or flag bit 1 set for * the user ID node. */ int keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb) { PACKET *pkt; struct parse_packet_ctx_s parsectx; int rc; KBNODE keyblock = NULL, node, lastnode; IOBUF a; int in_cert = 0; int pk_no = 0; int uid_no = 0; int save_mode; if (ret_kb) *ret_kb = NULL; if (!hd->found.kr) return -1; /* no successful search */ a = iobuf_open (hd->found.kr->fname); if (!a) { log_error(_("can't open '%s'\n"), hd->found.kr->fname); return GPG_ERR_KEYRING_OPEN; } if (iobuf_seek (a, hd->found.offset) ) { log_error ("can't seek '%s'\n", hd->found.kr->fname); iobuf_close(a); return GPG_ERR_KEYRING_OPEN; } pkt = xmalloc (sizeof *pkt); init_packet (pkt); init_parse_packet (&parsectx, a); hd->found.n_packets = 0; lastnode = NULL; save_mode = set_packet_list_mode(0); while ((rc=parse_packet (&parsectx, pkt)) != -1) { hd->found.n_packets = parsectx.n_parsed_packets; if (gpg_err_code (rc) == GPG_ERR_UNKNOWN_PACKET) { free_packet (pkt, &parsectx); init_packet (pkt); continue; } if (gpg_err_code (rc) == GPG_ERR_LEGACY_KEY) { if (in_cert) /* It is not this key that is problematic, but the following key. */ { rc = 0; hd->found.n_packets --; } else /* Upper layer needs to handle this. */ { } break; } if (rc) { log_error ("keyring_get_keyblock: read error: %s\n", gpg_strerror (rc) ); rc = GPG_ERR_INV_KEYRING; break; } /* Filter allowed packets. */ switch (pkt->pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_SECRET_KEY: case PKT_SECRET_SUBKEY: case PKT_USER_ID: case PKT_ATTRIBUTE: case PKT_SIGNATURE: break; /* Allowed per RFC. */ case PKT_RING_TRUST: case PKT_OLD_COMMENT: case PKT_COMMENT: case PKT_GPG_CONTROL: break; /* Allowed by us. */ default: log_info ("skipped packet of type %d in keyring\n", (int)pkt->pkttype); free_packet(pkt, &parsectx); init_packet(pkt); continue; } if (in_cert && (pkt->pkttype == PKT_PUBLIC_KEY || pkt->pkttype == PKT_SECRET_KEY)) { hd->found.n_packets--; /* fix counter */ break; /* ready */ } in_cert = 1; node = new_kbnode (pkt); if (!keyblock) keyblock = lastnode = node; else { lastnode->next = node; lastnode = node; } switch (pkt->pkttype) { case PKT_PUBLIC_KEY: case PKT_PUBLIC_SUBKEY: case PKT_SECRET_KEY: case PKT_SECRET_SUBKEY: if (++pk_no == hd->found.pk_no) node->flag |= 1; break; case PKT_USER_ID: if (++uid_no == hd->found.uid_no) node->flag |= 2; break; default: break; } pkt = xmalloc (sizeof *pkt); init_packet(pkt); } set_packet_list_mode(save_mode); if (rc == -1 && keyblock) rc = 0; /* got the entire keyblock */ if (rc || !ret_kb) release_kbnode (keyblock); else { *ret_kb = keyblock; } free_packet (pkt, &parsectx); deinit_parse_packet (&parsectx); xfree (pkt); iobuf_close(a); /* Make sure that future search operations fail immediately when * we know that we are working on a invalid keyring */ if (gpg_err_code (rc) == GPG_ERR_INV_KEYRING) hd->current.error = rc; return rc; } int keyring_update_keyblock (KEYRING_HANDLE hd, KBNODE kb) { int rc; if (!hd->found.kr) return -1; /* no successful prior search */ if (hd->found.kr->read_only) return gpg_error (GPG_ERR_EACCES); if (!hd->found.n_packets) { /* need to know the number of packets - do a dummy get_keyblock*/ rc = keyring_get_keyblock (hd, NULL); if (rc) { log_error ("re-reading keyblock failed: %s\n", gpg_strerror (rc)); return rc; } if (!hd->found.n_packets) BUG (); } /* The open iobuf isn't needed anymore and in fact is a problem when it comes to renaming the keyring files on some operating systems, so close it here */ iobuf_close(hd->current.iobuf); hd->current.iobuf = NULL; /* do the update */ rc = do_copy (3, hd->found.kr->fname, kb, hd->found.offset, hd->found.n_packets ); if (!rc) { if (key_present_hash) { key_present_hash_update_from_kb (key_present_hash, kb); } /* better reset the found info */ hd->found.kr = NULL; hd->found.offset = 0; } return rc; } int keyring_insert_keyblock (KEYRING_HANDLE hd, KBNODE kb) { int rc; const char *fname; if (!hd) fname = NULL; else if (hd->found.kr) { fname = hd->found.kr->fname; if (hd->found.kr->read_only) return gpg_error (GPG_ERR_EACCES); } else if (hd->current.kr) { fname = hd->current.kr->fname; if (hd->current.kr->read_only) return gpg_error (GPG_ERR_EACCES); } else fname = hd->resource? hd->resource->fname:NULL; if (!fname) return GPG_ERR_GENERAL; /* Close this one otherwise we will lose the position for * a next search. Fixme: it would be better to adjust the position * after the write operations. */ iobuf_close (hd->current.iobuf); hd->current.iobuf = NULL; /* do the insert */ rc = do_copy (1, fname, kb, 0, 0 ); if (!rc && key_present_hash) { key_present_hash_update_from_kb (key_present_hash, kb); } return rc; } int keyring_delete_keyblock (KEYRING_HANDLE hd) { int rc; if (!hd->found.kr) return -1; /* no successful prior search */ if (hd->found.kr->read_only) return gpg_error (GPG_ERR_EACCES); if (!hd->found.n_packets) { /* need to know the number of packets - do a dummy get_keyblock*/ rc = keyring_get_keyblock (hd, NULL); if (rc) { log_error ("re-reading keyblock failed: %s\n", gpg_strerror (rc)); return rc; } if (!hd->found.n_packets) BUG (); } /* close this one otherwise we will lose the position for * a next search. Fixme: it would be better to adjust the position * after the write operations. */ iobuf_close (hd->current.iobuf); hd->current.iobuf = NULL; /* do the delete */ rc = do_copy (2, hd->found.kr->fname, NULL, hd->found.offset, hd->found.n_packets ); if (!rc) { /* better reset the found info */ hd->found.kr = NULL; hd->found.offset = 0; /* Delete is a rare operations, so we don't remove the keys * from the offset table */ } return rc; } /* * Start the next search on this handle right at the beginning */ int keyring_search_reset (KEYRING_HANDLE hd) { log_assert (hd); iobuf_close (hd->current.iobuf); hd->current.iobuf = NULL; hd->current.eof = 0; hd->current.error = 0; hd->found.kr = NULL; hd->found.offset = 0; if (hd->current.kr) iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)hd->current.kr->fname); hd->current.kr = NULL; return 0; } static int prepare_search (KEYRING_HANDLE hd) { if (hd->current.error) { /* If the last key was a legacy key, we simply ignore the error so that we can easily use search_next. */ if (gpg_err_code (hd->current.error) == GPG_ERR_LEGACY_KEY) { if (DBG_LOOKUP) log_debug ("%s: last error was GPG_ERR_LEGACY_KEY, clearing\n", __func__); hd->current.error = 0; } else { if (DBG_LOOKUP) log_debug ("%s: returning last error: %s\n", __func__, gpg_strerror (hd->current.error)); return hd->current.error; /* still in error state */ } } if (hd->current.kr && !hd->current.eof) { if ( !hd->current.iobuf ) { if (DBG_LOOKUP) log_debug ("%s: missing iobuf!\n", __func__); return GPG_ERR_GENERAL; /* Position invalid after a modify. */ } return 0; /* okay */ } if (!hd->current.kr && hd->current.eof) { if (DBG_LOOKUP) log_debug ("%s: EOF!\n", __func__); return -1; /* still EOF */ } if (!hd->current.kr) { /* start search with first keyring */ hd->current.kr = hd->resource; if (!hd->current.kr) { if (DBG_LOOKUP) log_debug ("%s: keyring not available!\n", __func__); hd->current.eof = 1; return -1; /* keyring not available */ } log_assert (!hd->current.iobuf); } else { /* EOF */ if (DBG_LOOKUP) log_debug ("%s: EOF\n", __func__); iobuf_close (hd->current.iobuf); hd->current.iobuf = NULL; hd->current.kr = NULL; hd->current.eof = 1; return -1; } hd->current.eof = 0; hd->current.iobuf = iobuf_open (hd->current.kr->fname); if (!hd->current.iobuf) { hd->current.error = gpg_error_from_syserror (); log_error(_("can't open '%s'\n"), hd->current.kr->fname ); return hd->current.error; } return 0; } /* A map of the all characters valid used for word_match() * Valid characters are in this table converted to uppercase. * because the upper 128 bytes have special meaning, we assume * that they are all valid. * Note: We must use numerical values here in case that this program * will be converted to those little blue HAL9000s with their strange * EBCDIC character set (user ids are UTF-8). * wk 2000-04-13: Hmmm, does this really make sense, given the fact that * we can run gpg now on a S/390 running GNU/Linux, where the code * translation is done by the device drivers? */ static const byte word_match_chars[256] = { /* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 30 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, /* 38 */ 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, /* 40 */ 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 48 */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 50 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 58 */ 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 60 */ 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, /* 68 */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, /* 70 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, /* 78 */ 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00, /* 80 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, /* 88 */ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, /* 90 */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, /* 98 */ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, /* a0 */ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, /* a8 */ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, /* b0 */ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, /* b8 */ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, /* c0 */ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, /* c8 */ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, /* d0 */ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, /* d8 */ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, /* e0 */ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, /* e8 */ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, /* f0 */ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, /* f8 */ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; /**************** * Do a word match (original user id starts with a '+'). * The pattern is already tokenized to a more suitable format: * There are only the real words in it delimited by one space * and all converted to uppercase. * * Returns: 0 if all words match. * * Note: This algorithm is a straightforward one and not very * fast. It works for UTF-8 strings. The uidlen should * be removed but due to the fact that old versions of * pgp don't use UTF-8 we still use the length; this should * be fixed in parse-packet (and replace \0 by some special * UTF-8 encoding) */ static int word_match( const byte *uid, size_t uidlen, const byte *pattern ) { size_t wlen, n; const byte *p; const byte *s; for( s=pattern; *s; ) { do { /* skip leading delimiters */ while( uidlen && !word_match_chars[*uid] ) uid++, uidlen--; /* get length of the word */ n = uidlen; p = uid; while( n && word_match_chars[*p] ) p++, n--; wlen = p - uid; /* and compare against the current word from pattern */ for(n=0, p=uid; n < wlen && s[n] != ' ' && s[n] ; n++, p++ ) { if( word_match_chars[*p] != s[n] ) break; } if( n == wlen && (s[n] == ' ' || !s[n]) ) break; /* found */ uid += wlen; uidlen -= wlen; } while( uidlen ); if( !uidlen ) return -1; /* not found */ /* advance to next word in pattern */ for(; *s != ' ' && *s ; s++ ) ; if( *s ) s++ ; } return 0; /* found */ } /**************** * prepare word word_match; that is parse the name and * build the pattern. * caller has to free the returned pattern */ static char* prepare_word_match (const byte *name) { byte *pattern, *p; int c; /* the original length is always enough for the pattern */ p = pattern = xmalloc(strlen(name)+1); do { /* skip leading delimiters */ while( *name && !word_match_chars[*name] ) name++; /* copy as long as we don't have a delimiter and convert * to uppercase. * fixme: how can we handle utf8 uppercasing */ for( ; *name && (c=word_match_chars[*name]); name++ ) *p++ = c; *p++ = ' '; /* append pattern delimiter */ } while( *name ); p[-1] = 0; /* replace last pattern delimiter by EOS */ return pattern; } static int compare_name (int mode, const char *name, const char *uid, size_t uidlen) { int i; const char *s, *se; if (mode == KEYDB_SEARCH_MODE_EXACT) { for (i=0; name[i] && uidlen; i++, uidlen--) if (uid[i] != name[i]) break; if (!uidlen && !name[i]) return 0; /* found */ } else if (mode == KEYDB_SEARCH_MODE_SUBSTR) { if (ascii_memistr( uid, uidlen, name )) return 0; } else if ( mode == KEYDB_SEARCH_MODE_MAIL || mode == KEYDB_SEARCH_MODE_MAILSUB || mode == KEYDB_SEARCH_MODE_MAILEND) { int have_angles = 1; for (i=0, s= uid; i < uidlen && *s != '<'; s++, i++) ; if (i == uidlen) { /* The UID is a plain addr-spec (cf. RFC2822 section 4.3). */ have_angles = 0; s = uid; i = 0; } if (i < uidlen) { if (have_angles) { /* skip opening delim and one char and look for the closing one*/ s++; i++; for (se=s+1, i++; i < uidlen && *se != '>'; se++, i++) ; } else se = s + uidlen; if (i < uidlen) { i = se - s; if (mode == KEYDB_SEARCH_MODE_MAIL) { if( strlen(name)-2 == i && !ascii_memcasecmp( s, name+1, i) ) return 0; } else if (mode == KEYDB_SEARCH_MODE_MAILSUB) { if( ascii_memistr( s, i, name ) ) return 0; } else { /* email from end */ /* nyi */ } } } } else if (mode == KEYDB_SEARCH_MODE_WORDS) return word_match (uid, uidlen, name); else BUG(); return -1; /* not found */ } /* * Search through the keyring(s), starting at the current position, * for a keyblock which contains one of the keys described in the DESC array. */ int keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc, size_t ndesc, size_t *descindex, int ignore_legacy) { int rc; PACKET pkt; struct parse_packet_ctx_s parsectx; int save_mode; off_t offset, main_offset; size_t n; int need_uid, need_words, need_keyid, need_fpr, any_skip; int pk_no, uid_no; int initial_skip; int scanned_from_start; int use_key_present_hash; PKT_user_id *uid = NULL; PKT_public_key *pk = NULL; u32 aki[2]; /* figure out what information we need */ need_uid = need_words = need_keyid = need_fpr = any_skip = 0; for (n=0; n < ndesc; n++) { switch (desc[n].mode) { case KEYDB_SEARCH_MODE_EXACT: case KEYDB_SEARCH_MODE_SUBSTR: case KEYDB_SEARCH_MODE_MAIL: case KEYDB_SEARCH_MODE_MAILSUB: case KEYDB_SEARCH_MODE_MAILEND: need_uid = 1; break; case KEYDB_SEARCH_MODE_WORDS: need_uid = 1; need_words = 1; break; case KEYDB_SEARCH_MODE_SHORT_KID: case KEYDB_SEARCH_MODE_LONG_KID: need_keyid = 1; break; case KEYDB_SEARCH_MODE_FPR: need_fpr = 1; break; case KEYDB_SEARCH_MODE_FIRST: /* always restart the search in this mode */ keyring_search_reset (hd); break; default: break; } if (desc[n].skipfnc) { any_skip = 1; need_keyid = 1; } } if (DBG_LOOKUP) log_debug ("%s: need_uid = %d; need_words = %d; need_keyid = %d; need_fpr = %d; any_skip = %d\n", __func__, need_uid, need_words, need_keyid, need_fpr, any_skip); rc = prepare_search (hd); if (rc) { if (DBG_LOOKUP) log_debug ("%s: prepare_search failed: %s (%d)\n", __func__, gpg_strerror (rc), gpg_err_code (rc)); return rc; } use_key_present_hash = !!key_present_hash; if (!use_key_present_hash) { if (DBG_LOOKUP) log_debug ("%s: no offset table.\n", __func__); } else if (!key_present_hash_ready) { if (DBG_LOOKUP) log_debug ("%s: initializing offset table. (need_keyid: %d => 1)\n", __func__, need_keyid); need_keyid = 1; } else if (ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID) { struct key_present *oi; if (DBG_LOOKUP) log_debug ("%s: look up by long key id, checking cache\n", __func__); oi = key_present_hash_lookup (key_present_hash, desc[0].u.kid); if (!oi) { /* We know that we don't have this key */ if (DBG_LOOKUP) log_debug ("%s: cache says not present\n", __func__); hd->found.kr = NULL; hd->current.eof = 1; return -1; } /* We could now create a positive search status and return. * However the problem is that another instance of gpg may * have changed the keyring so that the offsets are not valid * anymore - therefore we don't do it */ } if (need_words) { const char *name = NULL; log_debug ("word search mode does not yet work\n"); /* FIXME: here is a long standing bug in our function and in addition we just use the first search description */ for (n=0; n < ndesc && !name; n++) { if (desc[n].mode == KEYDB_SEARCH_MODE_WORDS) name = desc[n].u.name; } log_assert (name); if ( !hd->word_match.name || strcmp (hd->word_match.name, name) ) { /* name changed */ xfree (hd->word_match.name); xfree (hd->word_match.pattern); hd->word_match.name = xstrdup (name); hd->word_match.pattern = prepare_word_match (name); } /* name = hd->word_match.pattern; */ } init_packet(&pkt); save_mode = set_packet_list_mode(0); hd->found.kr = NULL; main_offset = 0; pk_no = uid_no = 0; initial_skip = 1; /* skip until we see the start of a keyblock */ scanned_from_start = iobuf_tell (hd->current.iobuf) == 0; if (DBG_LOOKUP) log_debug ("%s: %ssearching from start of resource.\n", __func__, scanned_from_start ? "" : "not "); init_parse_packet (&parsectx, hd->current.iobuf); while (1) { byte afp[MAX_FINGERPRINT_LEN]; size_t an; rc = search_packet (&parsectx, &pkt, &offset, need_uid); if (ignore_legacy && gpg_err_code (rc) == GPG_ERR_LEGACY_KEY) { free_packet (&pkt, &parsectx); continue; } if (rc) break; if (pkt.pkttype == PKT_PUBLIC_KEY || pkt.pkttype == PKT_SECRET_KEY) { main_offset = offset; pk_no = uid_no = 0; initial_skip = 0; } if (initial_skip) { free_packet (&pkt, &parsectx); continue; } pk = NULL; uid = NULL; if ( pkt.pkttype == PKT_PUBLIC_KEY || pkt.pkttype == PKT_PUBLIC_SUBKEY || pkt.pkttype == PKT_SECRET_KEY || pkt.pkttype == PKT_SECRET_SUBKEY) { pk = pkt.pkt.public_key; ++pk_no; if (need_fpr) { fingerprint_from_pk (pk, afp, &an); while (an < 32) /* fill up to 32 bytes */ afp[an++] = 0; } if (need_keyid) keyid_from_pk (pk, aki); if (use_key_present_hash && !key_present_hash_ready && scanned_from_start) key_present_hash_update (key_present_hash, aki); } else if (pkt.pkttype == PKT_USER_ID) { uid = pkt.pkt.user_id; ++uid_no; } for (n=0; n < ndesc; n++) { switch (desc[n].mode) { case KEYDB_SEARCH_MODE_NONE: BUG (); break; case KEYDB_SEARCH_MODE_EXACT: case KEYDB_SEARCH_MODE_SUBSTR: case KEYDB_SEARCH_MODE_MAIL: case KEYDB_SEARCH_MODE_MAILSUB: case KEYDB_SEARCH_MODE_MAILEND: case KEYDB_SEARCH_MODE_WORDS: if ( uid && !compare_name (desc[n].mode, desc[n].u.name, uid->name, uid->len)) goto found; break; case KEYDB_SEARCH_MODE_SHORT_KID: - if (pk && desc[n].u.kid[1] == aki[1]) - goto found; + if (pk + && ((pk->fprlen == 32 && desc[n].u.kid[1] == aki[0]) + || (pk->fprlen != 32 && desc[n].u.kid[1] == aki[1]))) + goto found; break; case KEYDB_SEARCH_MODE_LONG_KID: if (pk && desc[n].u.kid[0] == aki[0] && desc[n].u.kid[1] == aki[1]) goto found; break; case KEYDB_SEARCH_MODE_FPR: if (pk && desc[n].fprlen >= 16 && desc[n].fprlen <= 32 && !memcmp (desc[n].u.fpr, afp, desc[n].fprlen)) goto found; break; case KEYDB_SEARCH_MODE_FIRST: if (pk) goto found; break; case KEYDB_SEARCH_MODE_NEXT: if (pk) goto found; break; default: rc = GPG_ERR_INV_ARG; goto found; } } free_packet (&pkt, &parsectx); continue; found: if (rc) goto real_found; if (DBG_LOOKUP) log_debug ("%s: packet starting at offset %lld matched descriptor %zu\n" , __func__, (long long)offset, n); /* Record which desc we matched on. Note this value is only meaningful if this function returns with no errors. */ if(descindex) *descindex=n; for (n=any_skip?0:ndesc; n < ndesc; n++) { if (desc[n].skipfnc && desc[n].skipfnc (desc[n].skipfncvalue, aki, uid_no)) { if (DBG_LOOKUP) log_debug ("%s: skipping match: desc %zd's skip function returned TRUE\n", __func__, n); break; } } if (n == ndesc) goto real_found; free_packet (&pkt, &parsectx); } real_found: if (!rc) { if (DBG_LOOKUP) log_debug ("%s: returning success\n", __func__); hd->found.offset = main_offset; hd->found.kr = hd->current.kr; hd->found.pk_no = pk? pk_no : 0; hd->found.uid_no = uid? uid_no : 0; } else if (rc == -1) { if (DBG_LOOKUP) log_debug ("%s: no matches (EOF)\n", __func__); hd->current.eof = 1; /* if we scanned all keyrings, we are sure that * all known key IDs are in our offtbl, mark that. */ if (use_key_present_hash && !key_present_hash_ready && scanned_from_start) { KR_RESOURCE kr; /* First set the did_full_scan flag for this keyring. */ for (kr=kr_resources; kr; kr = kr->next) { if (hd->resource == kr) { kr->did_full_scan = 1; break; } } /* Then check whether all flags are set and if so, mark the offtbl ready */ for (kr=kr_resources; kr; kr = kr->next) { if (!kr->did_full_scan) break; } if (!kr) key_present_hash_ready = 1; } } else { if (DBG_LOOKUP) log_debug ("%s: error encountered during search: %s (%d)\n", __func__, gpg_strerror (rc), rc); hd->current.error = rc; } free_packet (&pkt, &parsectx); deinit_parse_packet (&parsectx); set_packet_list_mode(save_mode); return rc; } static int create_tmp_file (const char *template, char **r_bakfname, char **r_tmpfname, IOBUF *r_fp) { gpg_error_t err; mode_t oldmask; err = keybox_tmp_names (template, 1, r_bakfname, r_tmpfname); if (err) return err; /* Create the temp file with limited access. Note that the umask call is not anymore needed because iobuf_create now takes care of it. However, it does not harm and thus we keep it. */ oldmask = umask (077); if (is_secured_filename (*r_tmpfname)) { *r_fp = NULL; gpg_err_set_errno (EPERM); } else *r_fp = iobuf_create (*r_tmpfname, 1); umask (oldmask); if (!*r_fp) { err = gpg_error_from_syserror (); log_error (_("can't create '%s': %s\n"), *r_tmpfname, gpg_strerror (err)); xfree (*r_tmpfname); *r_tmpfname = NULL; xfree (*r_bakfname); *r_bakfname = NULL; } return err; } static int rename_tmp_file (const char *bakfname, const char *tmpfname, const char *fname) { int rc = 0; int block = 0; /* Invalidate close caches. */ if (iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)tmpfname )) { rc = gpg_error_from_syserror (); goto fail; } iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)bakfname ); iobuf_ioctl (NULL, IOBUF_IOCTL_INVALIDATE_CACHE, 0, (char*)fname ); /* First make a backup file. */ block = 1; rc = gnupg_rename_file (fname, bakfname, &block); if (rc) goto fail; /* then rename the file */ rc = gnupg_rename_file (tmpfname, fname, NULL); if (block) { gnupg_unblock_all_signals (); block = 0; } if (rc) { register_secured_file (fname); goto fail; } /* Now make sure the file has the same permissions as the original */ #ifndef HAVE_DOSISH_SYSTEM { struct stat statbuf; statbuf.st_mode=S_IRUSR | S_IWUSR; if (!stat (bakfname, &statbuf) && !chmod (fname, statbuf.st_mode)) ; else log_error ("WARNING: unable to restore permissions to '%s': %s", fname, strerror(errno)); } #endif return 0; fail: if (block) gnupg_unblock_all_signals (); return rc; } static int write_keyblock (IOBUF fp, KBNODE keyblock) { KBNODE kbctx = NULL, node; int rc; while ( (node = walk_kbnode (keyblock, &kbctx, 0)) ) { if ( (rc = build_packet_and_meta (fp, node->pkt) )) { log_error ("build_packet(%d) failed: %s\n", node->pkt->pkttype, gpg_strerror (rc) ); return rc; } } return 0; } /* * Walk over all public keyrings, check the signatures and replace the * keyring with a new one where the signature cache is then updated. * This is only done for the public keyrings. */ int keyring_rebuild_cache (ctrl_t ctrl, void *token, int noisy) { KEYRING_HANDLE hd; KEYDB_SEARCH_DESC desc; KBNODE keyblock = NULL, node; const char *lastresname = NULL, *resname; IOBUF tmpfp = NULL; char *tmpfilename = NULL; char *bakfilename = NULL; int rc; ulong count = 0, sigcount = 0; hd = keyring_new (token); if (!hd) return gpg_error_from_syserror (); memset (&desc, 0, sizeof desc); desc.mode = KEYDB_SEARCH_MODE_FIRST; rc=keyring_lock (hd, 1); if(rc) goto leave; for (;;) { rc = keyring_search (hd, &desc, 1, NULL, 1 /* ignore_legacy */); if (rc) break; /* ready. */ desc.mode = KEYDB_SEARCH_MODE_NEXT; resname = keyring_get_resource_name (hd); if (lastresname != resname ) { /* we have switched to a new keyring - commit changes */ if (tmpfp) { if (iobuf_close (tmpfp)) { rc = gpg_error_from_syserror (); log_error ("error closing '%s': %s\n", tmpfilename, strerror (errno)); goto leave; } /* because we have switched resources, we can be sure that * the original file is closed */ tmpfp = NULL; } /* Static analyzer note: BAKFILENAME is never NULL here because it is controlled by LASTRESNAME. */ rc = lastresname? rename_tmp_file (bakfilename, tmpfilename, lastresname) : 0; xfree (tmpfilename); tmpfilename = NULL; xfree (bakfilename); bakfilename = NULL; if (rc) goto leave; lastresname = resname; if (noisy && !opt.quiet) log_info (_("caching keyring '%s'\n"), resname); rc = create_tmp_file (resname, &bakfilename, &tmpfilename, &tmpfp); if (rc) goto leave; } release_kbnode (keyblock); rc = keyring_get_keyblock (hd, &keyblock); if (rc) { if (gpg_err_code (rc) == GPG_ERR_LEGACY_KEY) continue; /* Skip legacy keys. */ log_error ("keyring_get_keyblock failed: %s\n", gpg_strerror (rc)); goto leave; } if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY) { /* We had a few reports about corrupted keyrings; if we have been called directly from the command line we delete such a keyblock instead of bailing out. */ log_error ("unexpected keyblock found (pkttype=%d)%s\n", keyblock->pkt->pkttype, noisy? " - deleted":""); if (noisy) continue; log_info ("Hint: backup your keys and try running '%s'\n", "gpg --rebuild-keydb-caches"); rc = gpg_error (GPG_ERR_INV_KEYRING); goto leave; } if (keyblock->pkt->pkt.public_key->version < 4) { /* We do not copy/cache v3 keys or any other unknown packets. It is better to remove them from the keyring. The code required to keep them in the keyring would be too complicated. Given that we do not touch the old secring.gpg a suitable backup for decryption of v3 stuff using an older gpg version will always be available. Note: This test is actually superfluous because we already acted upon GPG_ERR_LEGACY_KEY. */ } else { /* Check all signature to set the signature's cache flags. */ for (node=keyblock; node; node=node->next) { /* Note that this doesn't cache the result of a revocation issued by a designated revoker. This is because the pk in question does not carry the revkeys as we haven't merged the key and selfsigs. It is questionable whether this matters very much since there are very very few designated revoker revocation packets out there. */ if (node->pkt->pkttype == PKT_SIGNATURE) { PKT_signature *sig=node->pkt->pkt.signature; if(!opt.no_sig_cache && sig->flags.checked && sig->flags.valid && (openpgp_md_test_algo(sig->digest_algo) || openpgp_pk_test_algo(sig->pubkey_algo))) sig->flags.checked=sig->flags.valid=0; else check_key_signature (ctrl, keyblock, node, NULL); sigcount++; } } /* Write the keyblock to the temporary file. */ rc = write_keyblock (tmpfp, keyblock); if (rc) goto leave; if ( !(++count % 50) && noisy && !opt.quiet) log_info (ngettext("%lu keys cached so far (%lu signature)\n", "%lu keys cached so far (%lu signatures)\n", sigcount), count, sigcount); } } /* end main loop */ if (rc == -1) rc = 0; if (rc) { log_error ("keyring_search failed: %s\n", gpg_strerror (rc)); goto leave; } if (noisy || opt.verbose) { log_info (ngettext("%lu key cached", "%lu keys cached", count), count); log_printf (ngettext(" (%lu signature)\n", " (%lu signatures)\n", sigcount), sigcount); } if (tmpfp) { if (iobuf_close (tmpfp)) { rc = gpg_error_from_syserror (); log_error ("error closing '%s': %s\n", tmpfilename, strerror (errno)); goto leave; } /* because we have switched resources, we can be sure that * the original file is closed */ tmpfp = NULL; } rc = lastresname? rename_tmp_file (bakfilename, tmpfilename, lastresname) : 0; xfree (tmpfilename); tmpfilename = NULL; xfree (bakfilename); bakfilename = NULL; leave: if (tmpfp) iobuf_cancel (tmpfp); xfree (tmpfilename); xfree (bakfilename); release_kbnode (keyblock); keyring_lock (hd, 0); keyring_release (hd); return rc; } /**************** * Perform insert/delete/update operation. * mode 1 = insert * 2 = delete * 3 = update */ static int do_copy (int mode, const char *fname, KBNODE root, off_t start_offset, unsigned int n_packets ) { IOBUF fp, newfp; int rc=0; char *bakfname = NULL; char *tmpfname = NULL; /* Open the source file. Because we do a rename, we have to check the permissions of the file */ if (access (fname, W_OK)) return gpg_error_from_syserror (); fp = iobuf_open (fname); if (mode == 1 && !fp && errno == ENOENT) { /* insert mode but file does not exist: create a new file */ KBNODE kbctx, node; mode_t oldmask; oldmask=umask(077); if (is_secured_filename (fname)) { newfp = NULL; gpg_err_set_errno (EPERM); } else newfp = iobuf_create (fname, 1); umask(oldmask); if( !newfp ) { rc = gpg_error_from_syserror (); log_error (_("can't create '%s': %s\n"), fname, strerror(errno)); return rc; } if( !opt.quiet ) log_info(_("%s: keyring created\n"), fname ); kbctx=NULL; while ( (node = walk_kbnode( root, &kbctx, 0 )) ) { if( (rc = build_packet( newfp, node->pkt )) ) { log_error("build_packet(%d) failed: %s\n", node->pkt->pkttype, gpg_strerror (rc) ); iobuf_cancel(newfp); return rc; } } if( iobuf_close(newfp) ) { rc = gpg_error_from_syserror (); log_error ("%s: close failed: %s\n", fname, strerror(errno)); return rc; } return 0; /* ready */ } if( !fp ) { rc = gpg_error_from_syserror (); log_error(_("can't open '%s': %s\n"), fname, strerror(errno) ); goto leave; } /* Create the new file. */ rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp); if (rc) { iobuf_close(fp); goto leave; } if( mode == 1 ) { /* insert */ /* copy everything to the new file */ rc = copy_all_packets (fp, newfp); if( rc != -1 ) { log_error("%s: copy to '%s' failed: %s\n", fname, tmpfname, gpg_strerror (rc) ); iobuf_close(fp); iobuf_cancel(newfp); goto leave; } } if( mode == 2 || mode == 3 ) { /* delete or update */ /* copy first part to the new file */ rc = copy_some_packets( fp, newfp, start_offset ); if( rc ) { /* should never get EOF here */ log_error ("%s: copy to '%s' failed: %s\n", fname, tmpfname, gpg_strerror (rc) ); iobuf_close(fp); iobuf_cancel(newfp); goto leave; } /* skip this keyblock */ log_assert( n_packets ); rc = skip_some_packets( fp, n_packets ); if( rc ) { log_error("%s: skipping %u packets failed: %s\n", fname, n_packets, gpg_strerror (rc)); iobuf_close(fp); iobuf_cancel(newfp); goto leave; } } if( mode == 1 || mode == 3 ) { /* insert or update */ rc = write_keyblock (newfp, root); if (rc) { iobuf_close(fp); iobuf_cancel(newfp); goto leave; } } if( mode == 2 || mode == 3 ) { /* delete or update */ /* copy the rest */ rc = copy_all_packets( fp, newfp ); if( rc != -1 ) { log_error("%s: copy to '%s' failed: %s\n", fname, tmpfname, gpg_strerror (rc) ); iobuf_close(fp); iobuf_cancel(newfp); goto leave; } } /* close both files */ if( iobuf_close(fp) ) { rc = gpg_error_from_syserror (); log_error("%s: close failed: %s\n", fname, strerror(errno) ); goto leave; } if( iobuf_close(newfp) ) { rc = gpg_error_from_syserror (); log_error("%s: close failed: %s\n", tmpfname, strerror(errno) ); goto leave; } rc = rename_tmp_file (bakfname, tmpfname, fname); leave: xfree(bakfname); xfree(tmpfname); return rc; }