--- trunk/Src/wptKeyCache.cpp 2006/05/03 14:34:08 209 +++ trunk/Src/wptKeyCache.cpp 2011/11/27 13:15:07 340 @@ -1,5 +1,5 @@ /* wptKeyCache.cpp- Caching for the pub- and the secring - * Copyright (C) 2001-2006 Timo Schulz + * Copyright (C) 2001-2006, 2009 Timo Schulz * * This file is part of WinPT. * @@ -12,10 +12,6 @@ * 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, write to the Free Software - * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA */ #ifdef HAVE_CONFIG_H @@ -25,7 +21,6 @@ #include #include #include -#include #include #include #include @@ -43,6 +38,8 @@ #include "wptUTF8.h" +gpgme_error_t parse_keyserver_url (char **r_keyserver, unsigned short *r_port); + /* Attribute list which holds the image data. */ struct attr_list_s { struct attr_list_s *next; @@ -54,15 +51,27 @@ typedef struct attr_list_s *attr_list_t; +static unsigned char* +safe_uchar_alloc (size_t n) +{ + unsigned char *p = new unsigned char[n]; + if (!p) + BUG (0); + return p; +} + + /* Free attribute list @ctx. */ -void +static void free_attr_list (attr_list_t ctx) { attr_list_t n; + while (ctx) { n = ctx->next; - safe_free (ctx->fpr); - safe_free (ctx->d); + free_if_alloc (ctx->fpr); + free_if_alloc (ctx->d); + free_if_alloc (ctx); ctx = n; } } @@ -86,14 +95,16 @@ continue; buffer = buf+9+10; pos = 0; - c = (attr_list_t)calloc (1, sizeof *c); + c = new attr_list_s; if (!c) BUG (0); + memset (c, 0, sizeof *c); + p = strtok (buffer, " "); while (p != NULL) { switch (pos) { case 0: - c->fpr = strdup (p); + c->fpr = m_strdup (p); break; case 1: @@ -117,9 +128,7 @@ ; t->next = c; } - c->d = (unsigned char*)malloc (c->octets); - if (!c->d) - BUG (0); + c->d = safe_uchar_alloc (c->octets); memcpy (c->d, data, c->octets); data += c->octets; datlen -= c->octets; @@ -149,13 +158,14 @@ fwrite (status, 1, strlen (status), tmp); fflush (tmp); rewind (tmp); - ndata = parse_attr_list (tmp, data, ndata, list); - fclose (tmp); - DeleteFile (tmpnam); } else *list = NULL; + if (tmp != NULL) { + fclose (tmp); + DeleteFile (tmpnam); + } safe_free (status); safe_free (data); @@ -198,8 +208,8 @@ goto next; c->gloflags.is_protected = sk->is_protected; c->gloflags.divert_to_card = sk->protect.s2k.mode==1002? 1 : 0; - if (c->pubpart != NULL) { - c->pubpart->gloflags.is_protected = sk->is_protected; + if (c->pubpart != NULL) { + c->pubpart->gloflags.is_protected = sk->is_protected; c->pubpart->gloflags.divert_to_card = sk->protect.s2k.mode==1002? 1 : 0; } } @@ -223,12 +233,10 @@ gpg_keycache_find_key2 (ctx, fpr, 0, &key, &fnd); if (!fnd) return gpg_error (GPG_ERR_NOT_FOUND); - safe_free (fnd->attrib.d); + free_if_alloc (fnd->attrib.d); fnd->attrib.flags = dat->flags; fnd->attrib.len = dat->octets; - fnd->attrib.d = (unsigned char*)malloc (dat->octets); - if (!fnd->attrib.d) - BUG (0); + fnd->attrib.d = safe_uchar_alloc (dat->octets); memcpy (fnd->attrib.d, dat->d, dat->octets); return 0; } @@ -254,23 +262,24 @@ } -static void +void keycache_decode_uid (struct keycache_s *ctx) { gpgme_user_id_t u; struct native_uid_s *n, *t; for (u = ctx->key->uids; u; u = u->next) { - n = (struct native_uid_s*)calloc (1, sizeof *n); + n = new native_uid_s; if (!n) BUG (0); + memset (n, 0, sizeof *n); if (is_8bit_string (u->uid)) { n->malloced = 1; n->uid = utf8_to_native (u->uid); if (u->name != NULL) n->name = utf8_to_native (u->name); if (u->email != NULL) - n->email = strdup (u->email); + n->email = m_strdup (u->email); if (u->comment != NULL) n->comment = utf8_to_native (u->comment); } @@ -315,13 +324,12 @@ while (n != NULL) { t = n->next; if (n->malloced) { - safe_free (n->uid); - safe_free (n->name); - safe_free (n->comment); - safe_free (n->email); - safe_free (n->uid); + free_if_alloc (n->uid); + free_if_alloc (n->name); + free_if_alloc (n->comment); + free_if_alloc (n->email); } - safe_free (n); + free_if_alloc (n); n = t; } *r_n = NULL; @@ -339,7 +347,7 @@ gpg_iobuf_t inp; PACKET *pkt; struct keycache_s *c; - const byte *sym_prefs; + const BYTE *sym_prefs; char keyid[16+1]; int key_seen = 0; size_t nsym =0; @@ -377,8 +385,6 @@ c->gloflags.has_desig_rev = 1; } if (pkt->pkttype == PKT_SIGNATURE && key_seen == 1 && c != NULL) { - if (c->sym_prefs) /* only use the prefs from the primary uid. */ - goto next; sym_prefs = gpg_parse_sig_subpkt (pkt->pkt.signature->hashed, SIGSUBPKT_PREF_SYM, &nsym); if (!sym_prefs) @@ -388,12 +394,13 @@ if (kid && strcmp (kid, keyid) != 0) goto next; err = gpg_keycache_find_key2 (ctx, keyid, 0, &key, &c); - if (err) + if (err || !c) + goto next; + if (c->sym_prefs) // only use the prefs from the primary uid. goto next; else if (nsym > 0) { - c->sym_prefs = (unsigned char*)calloc (1, nsym+1); - if (!c->sym_prefs) - BUG (0); + c->sym_prefs = safe_uchar_alloc (nsym+1); + memset (c->sym_prefs, 0, nsym+1); memcpy (c->sym_prefs, sym_prefs, nsym); } } @@ -433,9 +440,10 @@ if (!r_ctx) return gpg_error (GPG_ERR_INV_ARG); - ctx = (gpg_keycache_t)calloc (1, sizeof *ctx); + ctx = new gpg_keycache_s; if (!ctx) BUG (0); + memset (ctx, 0, sizeof *ctx); ctx->secret = 0; ctx->pos = 0; *r_ctx = ctx; @@ -443,6 +451,24 @@ } +void +gpg_keycache_item_release (struct keycache_s *c) +{ + if (c->key) + gpgme_key_release (c->key); + c->key = NULL; + if (c->rev != NULL) + gpg_desig_rev_release (c->rev); + c->rev = NULL; + free_if_alloc (c->pref_keyserver); + free_if_alloc (c->sym_prefs); + free_if_alloc (c->attrib.d); + free_if_alloc (c->card_type); + free_native_uids (&c->uids); + free_if_alloc (c); +} + + /* Release keycache object @ctx. */ void gpg_keycache_release (gpg_keycache_t ctx) @@ -454,19 +480,9 @@ for (c = ctx->item; c; c = c2) { c2 = c->next; - gpgme_key_release (c->key); - c->key = NULL; - if (c->rev != NULL) - gpg_desig_rev_release (c->rev); - c->rev = NULL; - safe_free (c->pref_keyserver); - safe_free (c->sym_prefs); - safe_free (c->attrib.d); - safe_free (c->card_type); - free_native_uids (&c->uids); - safe_free (c); + gpg_keycache_item_release (c); } - safe_free (ctx); + free_if_alloc (ctx); } @@ -498,9 +514,10 @@ if (!ctx) return gpg_error (GPG_ERR_INV_ARG); - c = (struct keycache_s*)calloc (1, sizeof *c); + c = new keycache_s; if (!c) BUG (0); + memset (c, 0, sizeof *c); c->gloflags.is_protected = 1; /*default: assume protection. */ c->key = key; if (!ctx->item) @@ -614,7 +631,7 @@ if (c->flags != 0) { *r_status = c->flags; *r_obj = c; - c->flags = 0; + c->flags = 0; /* reset update flag. */ return 0; } } @@ -622,23 +639,65 @@ } +/* Helper to retrieve a GPG key. */ +static gpgme_error_t +get_gpg_key (const char *keyid, int is_sec, gpgme_key_t *r_key) +{ + gpgme_ctx_t ctx; + gpgme_error_t err; + err = gpgme_new (&ctx); + if (err) + return err; + gpgme_set_keylist_mode (ctx, GPGME_KEYLIST_MODE_SIGS); + err = gpgme_get_key (ctx, keyid, r_key, is_sec); + gpgme_release (ctx); + return err; +} + + +/* Fetch a key directly from gpg but without adding + it to the key cache. Caller must free @r_ctx. */ +gpgme_error_t +gpg_keycache_fetch_key (const char *keyid, int is_sec, + gpgme_key_t *r_key, struct keycache_s **r_c) +{ + gpgme_error_t err; + gpgme_key_t key; + struct keycache_s *c; + + *r_key = NULL; + *r_c = NULL; + err = get_gpg_key (keyid, is_sec, &key); + if (err) + return err; + + c = new keycache_s; + if (!c) + BUG (0); + memset (c, 0, sizeof *c); + c->gloflags.is_protected = 1; /*default: assume protection. */ + c->key = key; + keycache_decode_uid (c); + *r_key = key; + *r_c = c; + return 0; +} + + +/* Update the key with the keyid @key in the key cache. + If the key does not exist, it is added otherwise all + parts are first freed and then replaced with the updated data. */ gpgme_error_t gpg_keycache_update_key (gpg_keycache_t ctx, int is_sec, void *opaque, const char *keyid) -{ - struct keycache_s *c = NULL, *c_new=NULL; +{ gpgme_key_t key=NULL, fndkey=NULL; gpgme_error_t err; - gpgme_ctx_t gctx; + struct keycache_s *c = NULL, *c_new=NULL; gpg_keycache_t pub = (gpg_keycache_t)opaque; - err = gpgme_new (&gctx); - if (err) - return err; - gpgme_set_keylist_mode (gctx, GPGME_KEYLIST_MODE_SIGS/*|GPGME_KEYLIST_MODE_SIG_NOTATIONS*/); - err = gpgme_get_key (gctx, keyid, &key, is_sec); - gpgme_release (gctx); + err = get_gpg_key (keyid, is_sec, &key); if (err) return err; err = gpg_keycache_find_key2 (ctx, keyid, 0, &fndkey, &c); @@ -669,13 +728,12 @@ c->gloflags.divert_to_card = c_new->gloflags.divert_to_card; } } - if (c) + if (c != NULL) c->flags = KC_FLAG_ADD; - } /* refresh utf8 user ID list. */ - if (c != NULL) { + if (c != NULL && c->key) { free_native_uids (&c->uids); keycache_decode_uid (c); } @@ -700,9 +758,15 @@ return rc; c = ctx->item; - if (c->next == NULL) { - gpgme_key_release (itm->key); - safe_free (itm); + if (!c) /* empty */ + return 0; + else if (c->next == NULL) { + if (itm->key) + gpgme_key_release (itm->key); + itm->key = NULL; + free_if_alloc (itm); + /* the cache has no other items, so we set the context to NULL + to indicate that the entire cache is empty. */ ctx->item = NULL; } else { @@ -712,8 +776,10 @@ } assert (c != NULL); /* XXX: sometimes access violation. */ c->next = c->next->next; - gpgme_key_release (itm->key); - safe_free (itm); + if (itm->key) + gpgme_key_release (itm->key); + itm->key = NULL; + free_if_alloc (itm); } return 0; } @@ -738,7 +804,7 @@ return err; /* XXX: GPGME_KEYLIST_MODE_SIG_NOTATIONS causes an internal error! */ - gpgme_set_keylist_mode (c, GPGME_KEYLIST_MODE_SIGS/*|GPGME_KEYLIST_MODE_SIG_NOTATIONS*/); + gpgme_set_keylist_mode (c, GPGME_KEYLIST_MODE_SIGS); err = gpgme_op_keylist_start (c, pattern, secret); while(!err) { err = gpgme_op_keylist_next (c, &key); @@ -752,28 +818,34 @@ err = gpg_error (GPG_ERR_NO_ERROR); keycache_update_photos (ctx); keycache_decode_uids (ctx); - /* XXX: make sure the progress dialog is closed. */ gpgme_op_keylist_end (c); gpgme_release (c); return err; } -/* XXX: kludge to see if the key is stored on a card. */ +/* Return 1 if we can assume that the actual private key is + stored on a smart card. This is not bullet proof, but the + card provides 3 keys (RSA) and each key for a different purpose. */ static int key_divert_to_card (gpgme_key_t key) { gpgme_subkey_t k; - int n=0, n_alg=0, can_auth = 0; + int n=0; + int can_auth = 0, can_encr = 0; for (k = key->subkeys; k; k = k->next) { n++; - if (k->pubkey_algo == GPGME_PK_RSA && k->length == 1024) - n_alg++; + if (k->pubkey_algo != GPGME_PK_RSA || k->length != 1024) { + return 0; + break; + } if (k->can_authenticate) can_auth++; + if (k->can_encrypt) + can_encr++; } - if (n == 3 && n_alg == 3 && can_auth == 1) + if (n >= 3 && can_auth >= 1 && can_encr >= 1) return 1; return 0; } @@ -787,14 +859,14 @@ while (prefs[pos] != 0) pos++; - p = (unsigned char*)calloc (1, pos+1); - if (!p) - BUG (0); + p = safe_uchar_alloc (pos+1); + memset (p, 0, pos+1); memcpy (p, prefs, pos); return p; } +/* Sync the secret and the public key cache information. */ gpgme_error_t gpg_keycache_sync (gpg_keycache_t pub, gpg_keycache_t sec) { @@ -804,8 +876,9 @@ if (!pub || !sec) return gpg_error (GPG_ERR_INV_ARG); - for (c=sec->item; c; c=c->next) { - if (!gpg_keycache_find_key2 (pub, c->key->subkeys->keyid, 0, &key, &c_sec)) { + for (c=sec->item; c; c=c->next) { + if (!gpg_keycache_find_key2 (pub, c->key->subkeys->keyid, 0, + &key, &c_sec)) { c_sec->gloflags.is_protected = c->gloflags.is_protected; c_sec->gloflags.divert_to_card = c->gloflags.divert_to_card; if (!c->gloflags.divert_to_card) @@ -862,12 +935,13 @@ } if (ctx->tmp->flags != 0) ctx->tmp->flags = 0; /* reset the 'updated' status. */ + /* it might be possible there is no public key. */ if (flags && ctx->tmp->pubpart == NULL) flags = 0; *r_key = flags? ctx->tmp->pubpart->key : ctx->tmp->key; *c = ctx->tmp; - ctx->tmp = ctx->tmp->next; + ctx->tmp = ctx->tmp->next; ctx->pos++; return 0; @@ -887,6 +961,7 @@ return err; } + gpgme_error_t gpg_keycache_next_key2 (gpg_keycache_t ctx, int flags, struct keycache_s **c, gpgme_key_t *r_key) @@ -937,13 +1012,16 @@ } +/* FIXME: rewrite the subpacket part */ +void unhexify_buffer (const char *in, char **r_out); + static gpgme_error_t decode_subpacket (const char *subpkt_data, int *type, char **out, WORD *outlen) { char tmp[128], *val; - char *enc = NULL; - size_t pos = 0, i=0; + char *enc = NULL; + size_t pos = 0; /* example: spk:24:1:21:http%3A//subkeys.pgp.de */ *outlen = 0; @@ -952,7 +1030,8 @@ if (strncmp (subpkt_data, "spk:", 4)) return gpg_error (GPG_ERR_NO_DATA); - strncpy (tmp, subpkt_data, 62); + /* XXX: do not use static buffer sizes. */ + strncpy (tmp, subpkt_data, DIM (tmp)-4); val = strtok (tmp, ":"); while (val != NULL) { switch (pos++) { @@ -972,31 +1051,15 @@ break; case 4: - enc = strdup (val); + enc = m_strdup (val); break; } val = strtok (NULL, ":"); } if (!enc) - return gpg_error (GPG_ERR_NO_DATA);; - *out = (char*)calloc (1, strlen (enc)+1); - if (!*out) - BUG (0); - for (pos = 0; pos < strlen (enc); pos++) { - if (enc[pos] == '%' && enc[pos+1] == '%') - (*out)[i++] = '%'; - else if (enc[pos] == '%') { - char temp[3]; - temp[0] = enc[++pos]; - temp[1] = enc[++pos]; - temp[2] = 0; - (*out)[i++] = (char)strtoul (temp, NULL, 16); - } - else - (*out)[i++] = enc[pos]; - } - (*out)[i] = 0; - safe_free (enc); + return gpg_error (GPG_ERR_NO_DATA); + unhexify_buffer (enc, out); + free_if_alloc (enc); return 0; } @@ -1015,7 +1078,7 @@ case KC_ATTR_PREFSYM: if (!force && item->sym_prefs) break; - safe_free (item->sym_prefs); + free_if_alloc (item->sym_prefs); err = gpg_find_key_subpacket (item->key->subkeys->keyid+8, attr, &val); if (!err && val != NULL) err = decode_subpacket (val, NULL, (char**)&item->sym_prefs, &n); @@ -1024,10 +1087,13 @@ case KC_ATTR_PREFKSERV: if (!force && item->pref_keyserver) break; - safe_free (item->pref_keyserver); + free_if_alloc (item->pref_keyserver); err = gpg_find_key_subpacket (item->key->subkeys->keyid+8, attr, &val); if (!err && val != NULL) err = decode_subpacket (val, NULL, &item->pref_keyserver, &n); + if (!err && item->pref_keyserver) + err = parse_keyserver_url (&item->pref_keyserver, + &item->pref_keyserver_port); break; } safe_free (val);