diff options
Diffstat (limited to 'g10/mailing-list.c')
-rw-r--r-- | g10/mailing-list.c | 1040 |
1 files changed, 1040 insertions, 0 deletions
diff --git a/g10/mailing-list.c b/g10/mailing-list.c new file mode 100644 index 000000000..c8b230fb0 --- /dev/null +++ b/g10/mailing-list.c @@ -0,0 +1,1040 @@ +/* mailing-list.c - Create a mailing list. + * Copyright (C) 2015 Neal H. Walfield <[email protected]> + * + * 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 <http://www.gnu.org/licenses/>. + */ + +#include <config.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <errno.h> +#include <assert.h> +#include <ctype.h> + +#include "gpg.h" +#include "options.h" +#include "packet.h" +#include "iobuf.h" +#include "keydb.h" +#include "util.h" +#include "main.h" +#include "ttyio.h" +#include "status.h" +#include "i18n.h" +#include "mailing-list.h" + +void +kbnode_dump (KBNODE kb) +{ + for (; kb; kb = kb->next) + { + switch (kb->pkt->pkttype) + { + case PKT_PUBLIC_KEY: + log_debug (" public key: %s%s\n", + keystr (kb->pkt->pkt.public_key->keyid), + kb->pkt->pkt.public_key->has_expired ? " (expired)" : ""); + break; + case PKT_PUBLIC_SUBKEY: + log_debug (" subkey: %s%s\n", + keystr (kb->pkt->pkt.public_key->keyid), + kb->pkt->pkt.public_key->has_expired ? " (expired)" : ""); + break; + case PKT_USER_ID: + log_debug (" user id: %s\n", + kb->pkt->pkt.user_id->name); + break; + case PKT_SIGNATURE: + { + PKT_signature *sig = kb->pkt->pkt.signature; + struct notation *notations = sig_to_notation (sig); + + log_debug (" sig by %s: class %x\n", + keystr (sig->keyid), sig->sig_class); + + if (notations) + { + struct notation *niter; + + log_debug (" Notations:\n"); + + for (niter = notations; niter; niter = niter->next) + { + log_debug (" %s=%s\n", + niter->name, niter->value); + } + + free_notation (notations); + } + } + break; + default: + log_debug (" unknown packet: %d\n", kb->pkt->pkttype); + break; + } + } +} + +/* Get a copy of all the session keys and store them in *DEKS and the + total count in *NDEKS. On success, the caller must xfree + deksp. */ +gpg_error_t +mailing_list_get_subscriber_list_session_keys (ctrl_t ctrl, KBNODE kb, + DEK **deksp, int *ndeksp) +{ + gpg_error_t err; + + PKT_public_key *pk = kb->pkt->pkt.public_key; + KBNODE n; + PKT_public_key *sk = NULL; + + /* We need to collect all of the keys before we can decrypt (in + order to access key_i, we need key_{i-1} and we aren't guaranteed + to read the keys in order). Thus, we save the raw key data in + this structure. */ + struct keydata + { + byte *data; + size_t blen; + }; + /* We grow this dynamically. */ + struct keydata *keydata = NULL; + int nkeydata = 0; + iobuf_t keydata_initial = iobuf_temp (); + + DEK *deks = NULL; + + int i; + int last = -1; + + for (n = kb; n; n = n->next) + if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY) + { + sk = n->pkt->pkt.public_key; + if (DBG_PACKET) + log_debug ("%s: Processing signatures for %s\n", + __func__, keystr (sk->keyid)); + } + else if (n->pkt->pkttype == PKT_SIGNATURE) + { + PKT_signature *sig = n->pkt->pkt.signature; + struct notation *notations; + struct notation *niter; + struct keydata k = { 0, 0 }; + /* The session key that this key was encrypted with. */ + int encrypted_with = -2; + + if (! sig->flags.notation) + /* Nothing to do. */ + continue; + + notations = sig_to_notation (sig); + for (niter = notations; niter; niter = niter->next) + { + if (strcmp ("[email protected]", + niter->name) == 0) + { + k.data = xmalloc (niter->blen); + memcpy (k.data, niter->bdat, niter->blen); + k.blen = niter->blen; + } + else if (strcmp ("[email protected]", + niter->name) == 0) + { + encrypted_with = atoi (niter->value); + } + else if (strcmp ("[email protected]", niter->name) == 0) + { + encrypted_with = -1; + } + else if (strcmp ("[email protected]", + niter->name) == 0) + /* An encrypted initial session key. Just append it to + KEYDATA_INITIAL. */ + { + if (DBG_PACKET) + log_debug ("%s: Adding subscriber-list-key for %s\n", + __func__, keystr (sk->keyid)); + + iobuf_write (keydata_initial, niter->bdat, niter->blen); + } + } + + if (k.blen) + { + /* ENCRYPTED_WITH is the index of the key that this key + was encrypted with. Thus, this key is index + ENCRYPTED_WITH+1. */ + int session_key_index = encrypted_with + 1; + if (session_key_index < 0) + log_bug ("Have subscriber-list-session-key, but no subscriber-list-session-key-encrypted-with notation!\n"); + + if (DBG_PACKET) + log_debug ("%s: Got subscriber-list-session-key %d\n", + __func__, session_key_index); + + if (session_key_index >= nkeydata) + { + int o = nkeydata; + nkeydata = 2 * (1 + nkeydata); + keydata = xrealloc (keydata, nkeydata * sizeof (*keydata)); + memset (&keydata[o], 0, (nkeydata - o) * sizeof (*keydata)); + } + + if (last < session_key_index) + last = session_key_index; + + if (keydata[session_key_index].blen) + log_bug ("Have multiple session keys with index %d?!?\n", + session_key_index); + + keydata[session_key_index] = k; + + if (session_key_index == 0) + /* Add the initial key to the keydata_initial set. */ + iobuf_write (keydata_initial, k.data, k.blen); + } + + free_notation (notations); + } + + if (! nkeydata) + { + log_error ("Malformed mailing list key: did not find any subscriber-list-session-key notations.\n"); + return gpg_error (GPG_ERR_INTERNAL); + } + + nkeydata = last + 1; + + if (DBG_PACKET) + log_debug ("%s: Found %d subscriber-list-session keys.\n", + __func__, nkeydata); + + deks = xmalloc_clear (nkeydata * sizeof (*deks)); + + last = -1; + for (i = 0; i < nkeydata; i ++) + if (keydata[i].blen) + { + iobuf_t input; + + if (last + 1 != i) + { + log_error ("Malformed mailing list key: missing subscriber-list-session-keys %d-%d\n", + last + 1, i - 1); + return gpg_error (GPG_ERR_INTERNAL); + } + last = i; + + if (DBG_PACKET) + log_debug ("%s: mailing list key %s: session key %d is %zd bytes\n", + __func__, keystr (pk->keyid), i, keydata[i].blen); + + if (i == 0) + { + input = + iobuf_temp_with_content (iobuf_get_temp_buffer (keydata_initial), + iobuf_get_temp_length (keydata_initial)); + iobuf_close (keydata_initial); + keydata_initial = NULL; + + err = proc_pubkey_packet (ctrl, input, &deks[i]); + if (err) + log_error ("unable to extract mailing list decryption key: %s. Try adding the key subscribed to the mailing list to --try-secret-key KEYID.\n", + gpg_strerror (err)); + } + else + { + input = iobuf_temp_with_content (keydata[i].data, keydata[i].blen); + if (! input) + log_bug ("Failed to create iobuf"); + + /* The encryption function (s2k) needs an ASCII password. + We just hex encode the session key and use that. */ + set_next_passphrase (bin2hex (deks[i - 1].key, deks[i - 1].keylen, + NULL)); + err = proc_symkey_packet (ctrl, input, &deks[i]); + + if (err) + log_error ("Failed to extract session key %d from subscriber-list-session-key notation: %s\n", + i, gpg_strerror (err)); + } + + if (err) + break; + } + + for (i = 0; i < nkeydata; i ++) + xfree (keydata[i].data); + xfree (keydata); + if (keydata_initial) + iobuf_close (keydata_initial); + + if (err) + xfree (deks); + else + { + *deksp = deks; + *ndeksp = last + 1; + } + + return err; +} + +gpg_error_t +mailing_list_add_subscriber (ctrl_t ctrl, KBNODE ml_kb, const char *sub) +{ + gpg_error_t err; + + /* The mailing list's primary key. */ + PKT_public_key *ml_pk = ml_kb->pkt->pkt.public_key; + /* The subscriber's keyblock. */ + KBNODE sub_kb = NULL; + /* The subscriber's primary key. */ + PKT_public_key *sub_pk = NULL; + /* The subscriber's encryption key. */ + PKT_public_key *sub_ek = NULL; + /* The modified copy of SUB_EK that we add to the mailing list's + keyblock. */ + PKT_public_key *ml_ek = NULL; + + /* The first session key. */ + DEK session_key_initial; + /* The current session key. */ + DEK session_key; + /* The index of the current session key. */ + int session_key_i; + + struct notation *notations = NULL; + + err = get_pubkey_byname (NULL, NULL, NULL, sub, &sub_kb, NULL, 0, 0); + if (err) + { + log_error (_("Looking up key '%s': %s\n"), + sub, gpg_strerror (err)); + goto out; + } + + sub_pk = sub_kb->pkt->pkt.public_key; + + { + char keyid_str[20]; + char subkeyid_str[20]; + + format_keyid (ml_pk->keyid, KF_DEFAULT, keyid_str, sizeof (keyid_str)); + format_keyid (sub_pk->keyid, KF_DEFAULT, + subkeyid_str, sizeof (subkeyid_str)); + + if (DBG_PACKET) + log_debug ("%s: addsub %s %s\n", __func__, keyid_str, subkeyid_str); + } + + /* Find the encryption key to add and save it in SUB_EK. */ + { + KBNODE n; + for (n = sub_kb; n; n = n->next) + if (n->pkt->pkttype == PKT_PUBLIC_KEY + || n->pkt->pkttype == PKT_PUBLIC_SUBKEY) + { + PKT_public_key *ek = n->pkt->pkt.public_key; + + /* Ignore invalid keys. */ + if (! (ek->pubkey_usage & PUBKEY_USAGE_ENC)) + { + if (DBG_PACKET) + log_debug ("%s: Ignoring subkey %s: no encryption capability.\n", + __func__, keystr (ek->keyid)); + continue; + } + + if (ek->flags.revoked) + { + if (DBG_PACKET) + log_debug ("%s: Ignoring subkey %s: revoked.\n", + __func__, keystr (ek->keyid)); + continue; + } + + if (ek->has_expired) + { + if (DBG_PACKET) + log_debug ("%s: Ignoring subkey %s: expired.\n", + __func__, keystr (ek->keyid)); + continue; + } + + if(ek->flags.maybe_revoked && !ek->flags.revoked) + log_info(_("WARNING: this key might be revoked (revocation key" + " not present)\n")); + + if (DBG_PACKET) + log_debug ("%s: subkey %s is a candidate.\n", + __func__, keystr (ek->keyid)); + + if (! sub_ek) + { + sub_ek = ek; + continue; + } + else + /* If there are multiple valid keys, then prefer the + newest one. */ + { + if (ek->timestamp > sub_ek->timestamp) + sub_ek = ek; + + if (DBG_PACKET) + log_debug ("%s: Preferring subkey %s (it is newer).\n", + __func__, keystr (sub_ek->keyid)); + } + } + + if (! sub_ek) + { + if (DBG_PACKET) + log_debug ("%s: Key does support encryption.\n", __func__); + err = gpg_error (GPG_ERR_UNUSABLE_PUBKEY); + goto out; + } + /* SUB_EK now holds the selected encryption key. */ + } + + /* Make sure we haven't already added this encryption key. Or, if + we have and it is unsubscriber, resubscribe it. */ + { + KBNODE n; + char sub_ek_fp[MAX_FINGERPRINT_LEN]; + size_t sub_ek_fplen; + + fingerprint_from_pk (sub_ek, sub_ek_fp, &sub_ek_fplen); + + for (n = ml_kb; n; n = n->next) + { + char fp[MAX_FINGERPRINT_LEN]; + size_t fplen; + + if (n->pkt->pkttype != PKT_PUBLIC_SUBKEY) + continue; + + fingerprint_from_pk (n->pkt->pkt.public_key, fp, &fplen); + if (sub_ek_fplen == fplen && memcmp (sub_ek_fp, fp, fplen) == 0) + /* Got a match! */ + break; + } + + if (n) + { + /* XXX: If SUB was a subscriber, but is currently + unsubscriber, readd. */ + log_error ("%s is already a subscriber.\n", sub); + goto out; + } + } + + + /* Get the initial session key (we need to grant the new subscriber + access to it) and the current session key (we need to encrypt the + new subscriber's parameters with it). */ + { + DEK *deks = NULL; + int ndeks; + err = mailing_list_get_subscriber_list_session_keys (ctrl, ml_kb, + &deks, &ndeks); + if (err) + { + log_error ("Failed to get session keys for mailing list: %s\n", + gpg_strerror (err)); + xfree (deks); + goto out; + } + + session_key_initial = deks[0]; + + session_key_i = ndeks - 1; + session_key = deks[session_key_i]; + + xfree (deks); + } + + /* Make a new subkey using the new subscriber's selected encryption + key. */ + { + PACKET *pkt; + + if (DBG_PACKET) + { + log_debug("%s: keyblock pre:\n", __func__); + kbnode_dump (ml_kb); + } + + pkt = xmalloc_clear (sizeof (*pkt)); + pkt->pkttype = PKT_PUBLIC_SUBKEY; + ml_ek = xmalloc_clear (sizeof (*ml_ek)); + pkt->pkt.public_key = ml_ek; + add_kbnode (ml_kb, new_kbnode (pkt)); + + /* First copy everything and then clear what we don't need. */ + /* XXX: It would be better to just copy the fields that we actually + need. */ + *ml_ek = *sub_ek; + ml_ek->main_keyid[0] = ml_pk->keyid[0]; + ml_ek->main_keyid[1] = ml_pk->keyid[1]; + ml_ek->pubkey_usage = PUBKEY_USAGE_ENC; + ml_ek->expiredate = 0; + ml_ek->max_expiredate = 0; + ml_ek->prefs = NULL; + ml_ek->user_id = NULL; + ml_ek->revkey = NULL; + ml_ek->numrevkeys = 0; + ml_ek->trust_regexp = NULL; + ml_ek->serialno = NULL; + ml_ek->seckey_info = NULL; + } + + /* Encrypt the parameters and the current time using the current + session key. */ + { + int i; + int n = pubkey_get_npkey (ml_ek->pubkey_algo); + + for (i = 0; i < n; i ++) + { + /* XXX: Finish me: actually encrypt the keys; don't just copy + them. */ + (void) session_key; + + ml_ek->pkey[i] = gcry_mpi_copy (ml_ek->pkey[i]); + } + + /* XXX: Encrypt the creation time. */ + + /* Recompute ml_ek->keyid. */ + { + char fp[MAX_FINGERPRINT_LEN]; + size_t fplen; + u32 keyid[2]; + + fingerprint_from_pk (ml_ek, fp, &fplen); + keyid_from_fingerprint (fp, fplen, keyid); + ml_ek->keyid[0] = keyid[0]; + ml_ek->keyid[1] = keyid[1]; + } + } + + /* Add the public-key-encrypted-with notation. */ + { + char *notation; + struct notation *notation_blob; + + /* The session key used to encrypt the public key parameters. */ + notation = xasprintf ("[email protected]=%d", + session_key_i); + notation_blob = string_to_notation (notation, 0); + if (! notation_blob) + { + log_bug ("Failed to create notation: %s\n", notation); + xfree (notation); + err = gpg_error (GPG_ERR_INTERNAL); + goto out; + } + xfree (notation); + + notation_blob->next = notations; + notations = notation_blob; + } + + /* Add the subscriber-list-key notation. */ + { + char *notation; + struct notation *notation_blob; + + struct pk_list pk_list; + /* The public key encrypted session key as a packet. */ + iobuf_t pk_esk; + char *buffer; + size_t len; + + + /* The initial session key encrypted with the new subscriber's + public key. */ + /* Initialize PK_LIST with just the encryption key. */ + pk_list.next = NULL; + pk_list.pk = sub_ek; + /* Throw the key id. */ + pk_list.flags = 1; + + pk_esk = iobuf_temp (); + if (! pk_esk) + { + log_bug ("Out of memory allocating pk_esk\n"); + err = gpg_error (GPG_ERR_INTERNAL); + goto out; + } + + err = write_pubkey_enc_from_list (ctrl, &pk_list, + &session_key_initial, pk_esk); + if (err) + { + log_bug ("Failed to generate PK-ESK packet: %s\n", + gpg_strerror (err)); + iobuf_close (pk_esk); + goto out; + } + + buffer = iobuf_get_temp_buffer (pk_esk); + len = iobuf_get_temp_length (pk_esk); + + /* XXX */ + if (DBG_PACKET) + { + char *fn = xasprintf ("/tmp/subscriber-list-key-%s", + keystr (sub_ek->keyid)); + FILE *fp = fopen (fn, "w"); + if (fp) + { + log_debug ("Writing subscriber-list-key to %s\n", fn); + fwrite (buffer, len, 1, fp); + fclose (fp); + } + xfree (fn); + } + + notation = "[email protected]"; + notation_blob = blob_to_notation (notation, buffer, len); + iobuf_close (pk_esk); + if (! notation_blob) + { + log_bug ("Failed to create notation: %s=<SE-ESK packet, %zd bytes>\n", + notation, len); + err = gpg_error (GPG_ERR_INTERNAL); + goto out; + } + + notation_blob->next = notations; + notations = notation_blob; + } + + /* Write the binding signature. */ + err = write_keybinding (ml_kb, ml_pk, NULL, ml_ek->pubkey_usage, + make_timestamp(), NULL, notations); + if (err) + { + log_error ("Error creating key binding: %s\n", gpg_strerror (err)); + goto out; + } + + if (DBG_PACKET) + { + log_debug("%s: keyblock after adding self-sig:\n", __func__); + kbnode_dump (ml_kb); + } + + + /* Save the updated keyblock. */ + { + KEYDB_HANDLE hd = keydb_new (); + err = keydb_update_keyblock (hd, ml_kb); + keydb_release (hd); + if (err) + { + log_error ("Error saving %s's keyblock.\n", + keystr (ml_pk->keyid)); + goto out; + } + } + + out: + free_notation (notations); + + if (sub_kb) + release_kbnode (sub_kb); + + if (err) + log_error (_("Key generation failed: %s\n"), gpg_strerror (err) ); + + return err; +} + +gpg_error_t +mailing_list_rm_subscriber (ctrl_t ctrl, KBNODE ml_kb, const char *sub_orig) +{ + gpg_error_t err; + + /* The mailing list's primary key. */ + PKT_public_key *ml_pk = ml_kb->pkt->pkt.public_key; + + DEK *deks = NULL; + int ndeks = 0; + + char *sub; + int i, j; + + PKT_public_key *ek; + struct notation *notations = NULL; + + /* Skip leading white space. */ + while (*sub_orig == ' ') + sub_orig ++; + /* Kill the leading 0x (if any). */ + if (sub_orig[0] == '0' && sub_orig[0] == 'x') + sub_orig += 2; + sub = xstrdup (sub_orig); + + if (DBG_PACKET) + log_debug ("%s: sub: '%s'\n", __func__, sub); + /* Remove any spaces and upcase the rest. */ + for (i = j = 0; sub[i]; i ++, j ++) + { + while (sub[i] == ' ') + i ++; + + if (i != j) + sub[j] = toupper (sub[i]); + } + sub[j] = 0; + if (DBG_PACKET && strcmp (sub_orig, sub) != 0) + log_debug ("%s: sub postprocessed: '%s'\n", __func__, sub); + + /* Make sure it is in the form of a keyid (short or long) or a + fingerprint. */ + if (strspn (sub, "0123456789ABCDEF") != strlen (sub) + || !(strlen (sub) == 8 || strlen (sub) != 16 || strlen (sub) != 40)) + { + log_error ("'%s' is not a valid key id or fingerprint.\n", sub_orig); + err = gpg_error (GPG_ERR_INV_VALUE); + goto out; + } + + err = mailing_list_get_subscriber_list_session_keys (ctrl, ml_kb, + &deks, &ndeks); + if (err) + { + log_error ("Failed to get session keys: %s\n", gpg_strerror (err)); + goto out; + } + + /* Iterate and decrypt all of the keys to get their real key ids. */ + { + KBNODE n; + + for (n = ml_kb; n; n = n->next) + if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY) + { + char id[41]; + int match = 0; + + ek = n->pkt->pkt.public_key; + + if (DBG_PACKET) + log_debug ("%s: considering subkey %s.\n", + __func__, keystr (ek->keyid)); + + switch (strlen (sub)) + { + case 8: + match = strcmp (format_keyid (ek->keyid, KF_SHORT, + id, sizeof (id)), + sub) == 0; + break; + case 16: + match = strcmp (format_keyid (ek->keyid, KF_LONG, + id, sizeof (id)), + sub) == 0; + break; + case 40: + match = strcmp (fingerprint_from_pk (ek, id, NULL), sub) == 0; + break; + default: + assert (! "Unhandled case."); + } + + if (match) + break; + } + + if (! n) + { + log_error ("No subkey matches %s\n", sub_orig); + err = gpg_error (GPG_ERR_NOT_FOUND); + goto out; + } + + if (DBG_PACKET) + log_debug ("%s: subkey %s matched.\n", + __func__, keystr (ek->keyid)); + + if (ek->has_expired) + { + log_error ("Subscriber %s was already removed.\n", sub_orig); + err = 0; + goto out; + } + + /* We need to generate a new session key. */ + { + const char *notation = "[email protected]"; + struct notation *notation_blob; + + STRING2KEY *symkey_s2k = NULL; + DEK *symkey_dek = NULL; + + DEK dek; + /* The symmetrically encrypted session key as a packet. */ + iobuf_t sk_esk; + + char *buffer; + size_t len; + + /* setup_symkey needs a passphrase. We have a static passphrase. + To communicate this to setup_symkey, we use the + set_next_passphrase function, which preloads the passphrase and + causes setup_symkey to not ask the user, which is exactly what + we want. */ + set_next_passphrase (bin2hex (deks[ndeks - 1].key, deks[ndeks - 1].keylen, + NULL)); + err = setup_symkey (&symkey_s2k, &symkey_dek); + if (err) + { + log_bug ("Failed to initialize s2k and dek buffers: %s\n", + gpg_strerror (err)); + return err; + } + + memset (&dek, 0, sizeof (dek)); + dek.algo = default_cipher_algo (); + make_session_key (&dek); + + sk_esk = iobuf_temp (); + if (! sk_esk) + { + log_bug ("Out of memory allocating sk_esk\n"); + return gpg_error (GPG_ERR_INTERNAL); + } + + err = write_symkey_enc (symkey_s2k, symkey_dek, &dek, sk_esk); + if (err) + { + log_bug ("Failed to generate a symmetric key: %s\n", + gpg_strerror (err)); + return err; + } + + buffer = iobuf_get_temp_buffer (sk_esk); + len = iobuf_get_temp_length (sk_esk); + + notation_blob = + blob_to_notation (notation, buffer, len); + if (! notation_blob) + { + log_bug ("Failed to create notation: %s=<SE-ESK packet, %zd bytes>\n", + notation, len); + return gpg_error (GPG_ERR_INTERNAL); + } + + { + char *fn = xasprintf ("/tmp/subscriber-list-session-key-%d", ndeks); + FILE *fp = fopen (fn, "w"); + xfree (fn); + fwrite (buffer, len, 1, fp); + fclose (fp); + } + + notation_blob->next = notations; + notations = notation_blob; + } + + /* Record the key that the notation was encrypted with. */ + { + char *notation + = xasprintf ("[email protected]=%d", + ndeks - 1); + struct notation *notation_blob; + + notation_blob = string_to_notation (notation, 0); + if (! notation_blob) + { + log_bug ("Failed to create notation: %s\n", notation); + return gpg_error (GPG_ERR_INTERNAL); + } + + notation_blob->next = notations; + notations = notation_blob; + } + + /* Add the notations and update the expiration time. */ + for (n = n->next; n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next) + { + PKT_signature *sig = n->pkt->pkt.signature; + + if (DBG_PACKET) + log_debug ("%s: sig: keyid: %s; class: %x; chosen: %d\n", + __func__, keystr (sig->keyid), sig->sig_class, + sig->flags.chosen_selfsig); + + if (ml_pk->keyid[0] == sig->keyid[0] && ml_pk->keyid[1] == sig->keyid[1] + && sig->sig_class == 0x18 + && sig->flags.chosen_selfsig) + break; + } + + if (!n || n->pkt->pkttype != PKT_SIGNATURE) + { + log_error ("subkey %s missing key binding signature!\n", sub_orig); + err = gpg_error (GPG_ERR_INV_DATA); + goto out; + } + + /* Modify the signature. */ + { + PKT_signature *sig = n->pkt->pkt.signature; + PKT_signature *newsig; + PACKET *newpkt; + KBNODE n2; + + ek->expiredate = make_timestamp(); + + err = update_keysig_packet (&newsig, sig, ml_pk, NULL, ek, + ml_pk, notations, + keygen_add_key_expire, ek); + if (err) + { + log_error ("make_keysig_packet failed: %s\n", + gpg_strerror (err)); + return 0; + } + + newpkt = xmalloc_clear (sizeof *newpkt); + newpkt->pkttype = PKT_SIGNATURE; + newpkt->pkt.signature = newsig; + + /* Add the packet. */ + n2 = new_kbnode (newpkt); + n2->next = n->next; + n->next = n2; + } + } + + if (DBG_PACKET) + { + log_debug ("%s: Keyblock after adding new signature marking %s expired:\n", + __func__, keystr (ek->keyid)); + kbnode_dump (ml_kb); + } + + { + KEYDB_HANDLE hd = keydb_new (); + err = keydb_update_keyblock (hd, ml_kb); + keydb_release (hd); + if (err) + log_error ("Error saving %s's keyblock.\n", + keystr (ml_pk->keyid)); + } + + + out: + free_notation (notations); + xfree (deks); + xfree (sub); + + return err; +} + +gpg_error_t +mailing_list_subscribers (ctrl_t ctrl, KBNODE kb, PK_LIST *pklistp) +{ + gpg_error_t err; + + DEK *deks = NULL; + int ndeks; + + PK_LIST pklist = NULL; + KBNODE n; + PKT_public_key *pk = NULL; + + err = mailing_list_get_subscriber_list_session_keys (ctrl, kb, + &deks, &ndeks); + + for (n = kb; n; n = n->next) + { + if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY) + { + pk = n->pkt->pkt.public_key; + if (pk->has_expired) + /* The subscriber was removed. */ + { + if (DBG_PACKET) + log_debug ("%s: Skipping subscriber %s who was unsubscribed.\n", + __func__, keystr (pk->keyid)); + pk = NULL; + } + } + else if (pk && n->pkt->pkttype == PKT_SIGNATURE) + { + PKT_signature *sig = n->pkt->pkt.signature; + struct notation *notations; + struct notation *x; + + notations = sig_to_notation (sig); + if (! notations) + continue; + + for (x = notations; x; x = x->next) + /* If the public key is encrypted, then this is a subscriber. */ + if (strcmp (x->name, "[email protected]") == 0) + { + PK_LIST r = xmalloc_clear (sizeof *r); + int i = atoi (x->value); + + if (i >= ndeks) + { + log_error ("Unable to decrypt subkey %s: session key %d not available.\n", + keystr (pk->keyid), i); + goto out; + } + + /* XXX: Decrypt the public key parameters using the + session key. */ + (void) i; + + r->pk = copy_public_key (NULL, pk); + r->next = pklist; + pklist = r; + } + + free_notation (notations); + } + else + { + if (pk) + { + log_info ("Warning: %s is not a valid subscriber (missing notations)\n", + keystr (pk->keyid)); + pk = NULL; + } + } + } + + out: + xfree (deks); + + if (err) + release_pk_list (pklist); + else + *pklistp = pklist; + + return err; +} |