aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNeal H. Walfield <[email protected]>2016-02-01 08:49:16 +0000
committerNeal H. Walfield <[email protected]>2016-02-07 23:31:55 +0000
commit28fe48cf9222bfe6fef8625821c658f3f4cc90da (patch)
treecf2f03b80c69e89f4685c5b6933418444a116d41
parentgpg: Fix format_keyid when dynamically allocating the buffer. (diff)
downloadgnupg-28fe48cf9222bfe6fef8625821c658f3f4cc90da.tar.gz
gnupg-28fe48cf9222bfe6fef8625821c658f3f4cc90da.zip
Initial commit.
-rw-r--r--g10/Makefile.am3
-rw-r--r--g10/build-packet.c88
-rw-r--r--g10/card-util.c2
-rw-r--r--g10/encrypt.c159
-rw-r--r--g10/getkey.c37
-rw-r--r--g10/gpg.c178
-rw-r--r--g10/keyedit.c166
-rw-r--r--g10/keygen.c159
-rw-r--r--g10/mailing-list.c1040
-rw-r--r--g10/mailing-list.h44
-rw-r--r--g10/main.h16
-rw-r--r--g10/mainproc.c99
-rw-r--r--g10/packet.h8
-rw-r--r--g10/revoke.c4
-rw-r--r--g10/sign.c9
15 files changed, 1866 insertions, 146 deletions
diff --git a/g10/Makefile.am b/g10/Makefile.am
index 10714f6af..d1314e192 100644
--- a/g10/Makefile.am
+++ b/g10/Makefile.am
@@ -133,7 +133,8 @@ gpg2_SOURCES = gpg.c \
call-agent.c call-agent.h \
trust.c $(trust_source) $(tofu_source) \
$(card_source) \
- exec.c exec.h
+ exec.c exec.h \
+ mailing-list.c mailing-list.h
gpgv2_SOURCES = gpgv.c \
$(common_source) \
diff --git a/g10/build-packet.c b/g10/build-packet.c
index 269c63ca9..37c96f843 100644
--- a/g10/build-packet.c
+++ b/g10/build-packet.c
@@ -1038,6 +1038,87 @@ string_to_notation(const char *string,int is_utf8)
}
struct notation *
+blob_to_notation(const char *name, const char *data, size_t len)
+{
+ const char *s;
+ int saw_at=0;
+ struct notation *notation;
+
+ notation=xmalloc_clear(sizeof(*notation));
+
+ if(*name=='-')
+ {
+ notation->flags.ignore=1;
+ name++;
+ }
+
+ if(*name=='!')
+ {
+ notation->flags.critical=1;
+ name++;
+ }
+
+ /* If and when the IETF assigns some official name tags, we'll have
+ to add them here. */
+
+ for( s=name ; *s; s++ )
+ {
+ if( *s=='@')
+ saw_at++;
+
+ /* -notationname is legal without an = sign */
+ if(!*s && notation->flags.ignore)
+ break;
+
+ if (*s == '=')
+ {
+ log_error(_("a notation name may not contain an '=' character\n"));
+ goto fail;
+ }
+
+ if (!isascii (*s) || (!isgraph(*s) && !isspace(*s)))
+ {
+ log_error(_("a notation name must have only printable characters"
+ " or spaces\n") );
+ goto fail;
+ }
+ }
+
+ notation->name=xstrdup (name);
+
+ if(!saw_at && !opt.expert)
+ {
+ log_error(_("a user notation name must contain the '@' character\n"));
+ goto fail;
+ }
+
+ if (saw_at > 1)
+ {
+ log_error(_("a notation name must not contain more than"
+ " one '@' character\n"));
+ goto fail;
+ }
+
+ notation->bdat = xmalloc (len);
+ memcpy (notation->bdat, data, len);
+ notation->blen = len;
+
+ /* The maximum size of a notation is 65k, i.e., we need 5 bytes of
+ space. */
+ notation->value=xmalloc(2+strlen(_("not human readable (65000 bytes)"))+2+1);
+ strcpy(notation->value,"[ ");
+ sprintf(&notation->value[strlen (notation->value)],
+ _("not human readable (%zd bytes)"), len);
+ strcat(notation->value," ]");
+
+ return notation;
+
+ fail:
+ free_notation(notation);
+ return NULL;
+}
+
+struct notation *
sig_to_notation(PKT_signature *sig)
{
const byte *p;
@@ -1083,9 +1164,12 @@ sig_to_notation(PKT_signature *sig)
n->blen=n2;
memcpy(n->bdat,&p[8+n1],n2);
- n->value=xmalloc(2+strlen(_("not human readable"))+2+1);
+ /* The maximum size of a notation is 65k, i.e., we need 5
+ bytes of space. */
+ n->value=xmalloc(2+strlen(_("not human readable (65000 bytes)"))+2+1);
strcpy(n->value,"[ ");
- strcat(n->value,_("not human readable"));
+ sprintf(&n->value[strlen (n->value)],
+ _("not human readable (%d bytes)"), n2);
strcat(n->value," ]");
}
diff --git a/g10/card-util.c b/g10/card-util.c
index b48705bd4..49682010d 100644
--- a/g10/card-util.c
+++ b/g10/card-util.c
@@ -1431,7 +1431,7 @@ generate_card_keys (ctrl_t ctrl)
the serialnumber and thus it won't harm. */
}
- generate_keypair (ctrl, 1, NULL, info.serialno, want_backup);
+ generate_keypair (ctrl, 1, 0, NULL, info.serialno, want_backup);
leave:
agent_release_card_info (&info);
diff --git a/g10/encrypt.c b/g10/encrypt.c
index abd800258..d606a5c72 100644
--- a/g10/encrypt.c
+++ b/g10/encrypt.c
@@ -38,10 +38,10 @@
#include "i18n.h"
#include "status.h"
#include "pkglue.h"
+#include "mailing-list.h"
static int encrypt_simple( const char *filename, int mode, int use_seskey );
-static int write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, iobuf_t out );
/****************
* Encrypt FILENAME with only the symmetric cipher. Take input from
@@ -419,7 +419,8 @@ setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek)
}
-static int
+/* DEK is initialized. */
+int
write_symkey_enc (STRING2KEY *symkey_s2k, DEK *symkey_dek, DEK *dek,
iobuf_t out)
{
@@ -628,7 +629,7 @@ encrypt_crypt (ctrl_t ctrl, int filefd, const char *filename,
if (DBG_CRYPTO)
log_printhex ("DEK is: ", cfx.dek->key, cfx.dek->keylen );
- rc = write_pubkey_enc_from_list (pk_list, cfx.dek, out);
+ rc = write_pubkey_enc_from_list (ctrl, pk_list, cfx.dek, out);
if (rc)
goto leave;
@@ -822,7 +823,8 @@ encrypt_filter (void *opaque, int control,
if (DBG_CRYPTO)
log_printhex ("DEK is: ", efx->cfx.dek->key, efx->cfx.dek->keylen);
- rc = write_pubkey_enc_from_list (efx->pk_list, efx->cfx.dek, a);
+ rc = write_pubkey_enc_from_list (efx->ctrl,
+ efx->pk_list, efx->cfx.dek, a);
if (rc)
return rc;
@@ -854,75 +856,114 @@ encrypt_filter (void *opaque, int control,
}
+static int
+write_pubkey_enc (PKT_public_key *pk, int flags, DEK *dek, iobuf_t out)
+{
+ int rc;
+ PKT_pubkey_enc *enc;
+ gcry_mpi_t frame;
+ PACKET pkt;
+
+ print_pubkey_algo_note ( pk->pubkey_algo );
+ enc = xmalloc_clear ( sizeof *enc );
+ enc->pubkey_algo = pk->pubkey_algo;
+ keyid_from_pk( pk, enc->keyid );
+ enc->throw_keyid = (opt.throw_keyids || (flags&1));
+
+ if (opt.throw_keyids && (PGP6 || PGP7 || PGP8))
+ {
+ log_info(_("you may not use %s while in %s mode\n"),
+ "--throw-keyids",compliance_option_string());
+ compliance_failure();
+ }
+
+ /* Okay, what's going on: We have the session key somewhere in
+ * the structure DEK and want to encode this session key in an
+ * integer value of n bits. pubkey_nbits gives us the number of
+ * bits we have to use. We then encode the session key in some
+ * way and we get it back in the big intger value FRAME. Then
+ * we use FRAME, the public key PK->PKEY and the algorithm
+ * number PK->PUBKEY_ALGO and pass it to pubkey_encrypt which
+ * returns the encrypted value in the array ENC->DATA. This
+ * array has a size which depends on the used algorithm (e.g. 2
+ * for Elgamal). We don't need frame anymore because we have
+ * everything now in enc->data which is the passed to
+ * build_packet(). */
+ frame = encode_session_key (pk->pubkey_algo, dek,
+ pubkey_nbits (pk->pubkey_algo, pk->pkey));
+ rc = pk_encrypt (pk->pubkey_algo, enc->data, frame, pk, pk->pkey);
+ gcry_mpi_release (frame);
+ if (rc)
+ log_error ("pubkey_encrypt failed: %s\n", gpg_strerror (rc) );
+ else
+ {
+ if ( opt.verbose )
+ {
+ char *ustr = get_user_id_string_native (enc->keyid);
+ log_info (_("%s/%s encrypted for: \"%s\"\n"),
+ openpgp_pk_algo_name (enc->pubkey_algo),
+ openpgp_cipher_algo_name (dek->algo),
+ ustr );
+ xfree (ustr);
+ }
+ /* And write it. */
+ init_packet (&pkt);
+ pkt.pkttype = PKT_PUBKEY_ENC;
+ pkt.pkt.pubkey_enc = enc;
+ rc = build_packet (out, &pkt);
+ if (rc)
+ log_error ("build_packet(pubkey_enc) failed: %s\n",
+ gpg_strerror (rc));
+ }
+ free_pubkey_enc(enc);
+ return rc;
+}
+
/*
* Write pubkey-enc packets from the list of PKs to OUT.
*/
-static int
-write_pubkey_enc_from_list (PK_LIST pk_list, DEK *dek, iobuf_t out)
+int
+write_pubkey_enc_from_list (ctrl_t ctrl, PK_LIST pk_list, DEK *dek, iobuf_t out)
{
- PACKET pkt;
PKT_public_key *pk;
- PKT_pubkey_enc *enc;
- int rc;
+ int rc = 0;
for ( ; pk_list; pk_list = pk_list->next )
{
- gcry_mpi_t frame;
-
pk = pk_list->pk;
- print_pubkey_algo_note ( pk->pubkey_algo );
- enc = xmalloc_clear ( sizeof *enc );
- enc->pubkey_algo = pk->pubkey_algo;
- keyid_from_pk( pk, enc->keyid );
- enc->throw_keyid = (opt.throw_keyids || (pk_list->flags&1));
-
- if (opt.throw_keyids && (PGP6 || PGP7 || PGP8))
+ if (pk->flags.mailing_list)
{
- log_info(_("you may not use %s while in %s mode\n"),
- "--throw-keyids",compliance_option_string());
- compliance_failure();
- }
+ KBNODE kb = get_pubkeyblock (pk->main_keyid);
+ PK_LIST subscribers;
+ PK_LIST subscriber;
- /* Okay, what's going on: We have the session key somewhere in
- * the structure DEK and want to encode this session key in an
- * integer value of n bits. pubkey_nbits gives us the number of
- * bits we have to use. We then encode the session key in some
- * way and we get it back in the big intger value FRAME. Then
- * we use FRAME, the public key PK->PKEY and the algorithm
- * number PK->PUBKEY_ALGO and pass it to pubkey_encrypt which
- * returns the encrypted value in the array ENC->DATA. This
- * array has a size which depends on the used algorithm (e.g. 2
- * for Elgamal). We don't need frame anymore because we have
- * everything now in enc->data which is the passed to
- * build_packet(). */
- frame = encode_session_key (pk->pubkey_algo, dek,
- pubkey_nbits (pk->pubkey_algo, pk->pkey));
- rc = pk_encrypt (pk->pubkey_algo, enc->data, frame, pk, pk->pkey);
- gcry_mpi_release (frame);
- if (rc)
- log_error ("pubkey_encrypt failed: %s\n", gpg_strerror (rc) );
- else
- {
- if ( opt.verbose )
+ if (! kb)
{
- char *ustr = get_user_id_string_native (enc->keyid);
- log_info (_("%s/%s encrypted for: \"%s\"\n"),
- openpgp_pk_algo_name (enc->pubkey_algo),
- openpgp_cipher_algo_name (dek->algo),
- ustr );
- xfree (ustr);
- }
- /* And write it. */
- init_packet (&pkt);
- pkt.pkttype = PKT_PUBKEY_ENC;
- pkt.pkt.pubkey_enc = enc;
- rc = build_packet (out, &pkt);
+ log_error (_("failed to find keyblock corresponding to %s\n"),
+ keystr (pk->main_keyid));
+ return gpg_error (GPG_ERR_INTERNAL);
+ }
+
+ rc = mailing_list_subscribers (ctrl, kb, &subscribers);
+ release_kbnode (kb);
if (rc)
- log_error ("build_packet(pubkey_enc) failed: %s\n",
- gpg_strerror (rc));
- }
- free_pubkey_enc(enc);
+ {
+ log_error (_("failed to list subscribers for %s\n"),
+ keystr (pk->main_keyid));
+ return rc;
+ }
+
+ for (subscriber = subscribers;
+ ! rc && subscriber;
+ subscriber = subscriber->next)
+ rc = write_pubkey_enc (subscriber->pk, subscriber->flags, dek, out);
+
+ release_pk_list (subscribers);
+ }
+ else
+ rc = write_pubkey_enc (pk, pk_list->flags, dek, out);
+
if (rc)
return rc;
}
diff --git a/g10/getkey.c b/g10/getkey.c
index 74fa7530a..9cd41c0fa 100644
--- a/g10/getkey.c
+++ b/g10/getkey.c
@@ -2318,6 +2318,24 @@ merge_selfsigs_main (KBNODE keyblock, int *r_revoked,
}
}
+
+ if (sig->flags.notation && ! pk->flags.mailing_list)
+ /* If the mailing-list notation appears on any
+ self-sig (not only the latest one), we consider
+ the key to be a mailing list key. */
+ {
+ struct notation *notations = sig_to_notation (sig);
+ struct notation *n;
+
+ for (n = notations; n; n = n->next)
+ if (strcmp (n->name, "[email protected]") == 0)
+ {
+ pk->flags.mailing_list = 1;
+ break;
+ }
+
+ free_notation (notations);
+ }
}
}
}
@@ -2467,6 +2485,24 @@ merge_selfsigs_main (KBNODE keyblock, int *r_revoked,
if (sig->version > sigversion)
sigversion = sig->version;
}
+
+ if (sig->flags.notation && ! pk->flags.mailing_list)
+ /* If the mailing-list notation appears on any
+ self-sig (not only the latest one), we consider
+ the key to be a mailing list key. */
+ {
+ struct notation *notations = sig_to_notation (sig);
+ struct notation *n;
+
+ for (n = notations; n; n = n->next)
+ if (strcmp (n->name, "[email protected]") == 0)
+ {
+ pk->flags.mailing_list = 1;
+ break;
+ }
+
+ free_notation (notations);
+ }
}
}
}
@@ -2777,6 +2813,7 @@ merge_selfsigs_subkey (KBNODE keyblock, KBNODE subnode)
subpk->flags.valid = 0;
subpk->flags.exact = 0;
+ subpk->flags.mailing_list = mainpk->flags.mailing_list;
subpk->main_keyid[0] = mainpk->main_keyid[0];
subpk->main_keyid[1] = mainpk->main_keyid[1];
diff --git a/g10/gpg.c b/g10/gpg.c
index 330d5a3fa..7b637f9f8 100644
--- a/g10/gpg.c
+++ b/g10/gpg.c
@@ -60,6 +60,7 @@
#include "asshelp.h"
#include "call-dirmngr.h"
#include "tofu.h"
+#include "mailing-list.h"
#include "../common/init.h"
#include "../common/shareddefs.h"
@@ -110,6 +111,8 @@ enum cmd_and_opt_values
aQuickKeygen,
aFullKeygen,
aKeygen,
+ aMailingListKeygen,
+ aQuickMailingListKeygen,
aSignEncr,
aSignEncrSym,
aSignSym,
@@ -118,6 +121,9 @@ enum cmd_and_opt_values
aQuickSignKey,
aQuickLSignKey,
aQuickAddUid,
+ aMailingListAddSub,
+ aMailingListRMSub,
+ aMailingListSubs,
aListConfig,
aListGcryptConfig,
aGPGConfList,
@@ -422,10 +428,20 @@ static ARGPARSE_OPTS opts[] = {
ARGPARSE_c (aListSecretKeys, "list-secret-keys", N_("list secret keys")),
ARGPARSE_c (aKeygen, "gen-key",
N_("generate a new key pair")),
+ ARGPARSE_c (aMailingListKeygen, "gen-mailing-list-key",
+ N_("generate a new key pair for a mailing list")),
ARGPARSE_c (aQuickKeygen, "quick-gen-key" ,
N_("quickly generate a new key pair")),
+ ARGPARSE_c (aQuickMailingListKeygen, "quick-gen-mailing-list-key" ,
+ N_("quickly generate a new mailing list key pair")),
ARGPARSE_c (aQuickAddUid, "quick-adduid",
N_("quickly add a new user-id")),
+ ARGPARSE_c (aMailingListAddSub, "mailing-list-add-sub",
+ N_("add a subscriber to a mailing list key")),
+ ARGPARSE_c (aMailingListRMSub, "mailing-list-rm-sub",
+ N_("remove a subscriber from a mailing list key")),
+ ARGPARSE_c (aMailingListSubs, "mailing-list-subs",
+ N_("list a mailing list's current subscribers")),
ARGPARSE_c (aFullKeygen, "full-gen-key" ,
N_("full featured key pair generation")),
ARGPARSE_c (aGenRevoke, "gen-revoke",N_("generate a revocation certificate")),
@@ -2427,6 +2443,9 @@ main (int argc, char **argv)
case aStore:
case aQuickKeygen:
case aQuickAddUid:
+ case aMailingListAddSub:
+ case aMailingListRMSub:
+ case aMailingListSubs:
case aExportOwnerTrust:
case aImportOwnerTrust:
case aRebuildKeydbCaches:
@@ -2434,6 +2453,8 @@ main (int argc, char **argv)
break;
case aKeygen:
+ case aMailingListKeygen:
+ case aQuickMailingListKeygen:
case aFullKeygen:
case aEditKey:
case aDeleteSecretKeys:
@@ -3765,8 +3786,13 @@ main (int argc, char **argv)
case aDeleteSecretAndPublicKeys:
case aQuickKeygen:
case aQuickAddUid:
+ case aMailingListAddSub:
+ case aMailingListRMSub:
+ case aMailingListSubs:
case aFullKeygen:
case aKeygen:
+ case aQuickMailingListKeygen:
+ case aMailingListKeygen:
case aImport:
case aExportSecret:
case aExportSecretSub:
@@ -4082,33 +4108,65 @@ main (int argc, char **argv)
break;
case aQuickKeygen:
- if (argc != 1 )
- wrong_args("--gen-key user-id");
- username = make_username (fname);
- quick_generate_keypair (ctrl, username);
- xfree (username);
+ case aQuickMailingListKeygen:
+ {
+ int mailing_list = cmd == aQuickMailingListKeygen;
+ const char *option_help;
+ if (mailing_list)
+ option_help = "--quick-gen-mailing-list-key user-id";
+ else
+ option_help = "--quick-gen-key user-id";
+
+ if (argc != 1 )
+ wrong_args(option_help);
+ username = make_username (fname);
+ printf ("'%s'\n", username);
+ quick_generate_keypair (ctrl, username, mailing_list);
+ xfree (username);
+ }
break;
case aKeygen: /* generate a key */
- if( opt.batch ) {
- if( argc > 1 )
- wrong_args("--gen-key [parameterfile]");
- generate_keypair (ctrl, 0, argc? *argv : NULL, NULL, 0);
- }
- else {
- if (opt.command_fd != -1 && argc)
- {
- if( argc > 1 )
- wrong_args("--gen-key [parameterfile]");
+ case aMailingListKeygen:
+ {
+ int mailing_list = cmd == aMailingListKeygen;
+ const char *option_help;
+ const char *option_help_short;
+ if (mailing_list)
+ {
+ option_help = "--gen-mailing-list-key [parameterfile]";
+ option_help_short = "--gen-mailing-list-key";
+ }
+ else
+ {
+ option_help = "--gen-key [parameterfile]";
+ option_help_short = "--gen-key";
+ }
- opt.batch = 1;
- generate_keypair (ctrl, 0, argc? *argv : NULL, NULL, 0);
- }
- else if (argc)
- wrong_args ("--gen-key");
- else
- generate_keypair (ctrl, 0, NULL, NULL, 0);
- }
+ if( opt.batch )
+ {
+ if( argc > 1 )
+ wrong_args(option_help);
+ generate_keypair (ctrl, 0, mailing_list,
+ argc? *argv : NULL, NULL, 0);
+ }
+ else
+ {
+ if (opt.command_fd != -1 && argc)
+ {
+ if( argc > 1 )
+ wrong_args(option_help);
+
+ opt.batch = 1;
+ generate_keypair (ctrl, 0, mailing_list,
+ argc? *argv : NULL, NULL, 0);
+ }
+ else if (argc)
+ wrong_args (option_help_short);
+ else
+ generate_keypair (ctrl, 0, mailing_list, NULL, NULL, 0);
+ }
+ }
break;
case aFullKeygen: /* Generate a key with all options. */
@@ -4116,13 +4174,13 @@ main (int argc, char **argv)
{
if (argc > 1)
wrong_args ("--full-gen-key [parameterfile]");
- generate_keypair (ctrl, 1, argc? *argv : NULL, NULL, 0);
+ generate_keypair (ctrl, 1, 0, argc? *argv : NULL, NULL, 0);
}
else
{
if (argc)
wrong_args("--full-gen-key");
- generate_keypair (ctrl, 1, NULL, NULL, 0);
+ generate_keypair (ctrl, 1, 0, NULL, NULL, 0);
}
break;
@@ -4138,6 +4196,76 @@ main (int argc, char **argv)
}
break;
+ case aMailingListAddSub:
+ case aMailingListRMSub:
+ {
+ int add = cmd == aMailingListAddSub;
+ const char *option_help;
+ KBNODE kb;
+
+ if (add)
+ option_help = "--mailing-list-add-sub mailing-list-keyid subcriber-keyid...";
+ else
+ option_help = "--mailing-list-rm-sub mailing-list-keyid subcriber-keyid...";
+
+ if (argc < 2)
+ wrong_args (option_help);
+
+ rc = get_pubkey_byname (ctrl, NULL, NULL, argv[0], &kb, NULL, 1, 1);
+ if (rc)
+ log_error (_("key \"%s\" not found: %s\n"),
+ argv[0], gpg_strerror (rc));
+ else
+ {
+ int i;
+
+ for (i = 1; i < argc; i ++)
+ if (add)
+ mailing_list_add_subscriber (ctrl, kb, argv[i]);
+ else
+ mailing_list_rm_subscriber (ctrl, kb, argv[i]);
+ }
+
+ break;
+ }
+
+ case aMailingListSubs:
+ {
+ KBNODE kb = NULL;
+ PKT_public_key *pk;
+ PK_LIST pklist;
+ PK_LIST i;
+ int count = 0;
+
+ if (argc != 1)
+ wrong_args ("--mailing-list-subs mailing-list-keyid");
+
+ rc = get_pubkey_byname (ctrl, NULL, NULL, argv[0], &kb, NULL, 1, 1);
+ if (rc)
+ log_error (_("key \"%s\" not found: %s\n"),
+ argv[0], gpg_strerror (rc));
+ else
+ {
+ pk = kb->pkt->pkt.public_key;
+
+ rc = mailing_list_subscribers (ctrl, kb, &pklist);
+ if (rc)
+ log_error ("Failed to list %s's subscribers.\n",
+ keystr (pk->keyid));
+ else
+ {
+ for (i = pklist; i; i = i->next, count ++)
+ es_printf ("%s\n", keystr (i->pk->keyid));
+
+ es_printf (" %d subscribers.\n", count);
+
+ release_pk_list (pklist);
+ }
+ }
+
+ break;
+ }
+
case aFastImport:
opt.import_options |= IMPORT_FAST;
case aImport:
diff --git a/g10/keyedit.c b/g10/keyedit.c
index 30f52a4b0..064b8722c 100644
--- a/g10/keyedit.c
+++ b/g10/keyedit.c
@@ -49,6 +49,7 @@
#include "call-agent.h"
#include "host2net.h"
#include "tofu.h"
+#include "mailing-list.h"
static void show_prefs (PKT_user_id * uid, PKT_signature * selfsig,
int verbose);
@@ -92,6 +93,8 @@ static int menu_revsubkey (KBNODE pub_keyblock);
static int enable_disable_key (KBNODE keyblock, int disable);
#endif /*!NO_TRUST_MODELS*/
static void menu_showphoto (KBNODE keyblock);
+static int menu_addsub (ctrl_t ctrl, KBNODE keyblock, const char *sub);
+static int menu_rmsub (ctrl_t ctrl, KBNODE keyblock, const char *sub);
static int update_trust = 0;
@@ -1089,7 +1092,7 @@ sign_uids (ctrl_t ctrl, estream_t fp,
node->pkt->pkt.user_id,
NULL,
pk,
- 0x13, 0, 0, 0,
+ 0x13, 0, 0, 0, NULL,
keygen_add_std_prefs, primary_pk,
NULL);
else
@@ -1098,7 +1101,7 @@ sign_uids (ctrl_t ctrl, estream_t fp,
NULL,
pk,
class, 0,
- timestamp, duration,
+ timestamp, duration, NULL,
sign_mk_attrib, &attrib,
NULL);
if (rc)
@@ -1369,7 +1372,7 @@ enum cmdids
cmdSHOWPREF,
cmdSETPREF, cmdPREFKS, cmdNOTATION, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST,
cmdCHKTRUST, cmdADDCARDKEY, cmdKEYTOCARD, cmdBKUPTOCARD,
- cmdCLEAN, cmdMINIMIZE, cmdGRIP, cmdNOP
+ cmdCLEAN, cmdMINIMIZE, cmdGRIP, cmdADDSUB, cmdRMSUB, cmdNOP
};
static struct
@@ -1463,6 +1466,10 @@ static struct
N_("compact unusable user IDs and remove unusable signatures from key")},
{ "minimize", cmdMINIMIZE, KEYEDIT_NOT_SK,
N_("compact unusable user IDs and remove all signatures from key")},
+ { "addsub", cmdADDSUB, KEYEDIT_NEED_SK | KEYEDIT_ONLY_SK,
+ N_("add a subscriber to a mailing list")},
+ { "rmsub", cmdRMSUB, KEYEDIT_NEED_SK | KEYEDIT_ONLY_SK,
+ N_("add a subscriber to a mailing list")},
{ NULL, cmdNONE, 0, NULL}
};
@@ -2182,6 +2189,7 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
show_names (NULL, keyblock, keyblock->pkt->pkt.public_key,
count ? NODFLG_SELUID : 0, 2);
+ kbnode_dump (keyblock);
}
break;
@@ -2269,6 +2277,23 @@ keyedit_menu (ctrl_t ctrl, const char *username, strlist_t locusr,
redisplay = modified = 1;
break;
+ case cmdADDSUB:
+ if (menu_addsub (ctrl, keyblock, arg_string))
+ {
+ redisplay = 1;
+ modified = 1;
+ merge_keys_and_selfsig (keyblock);
+ }
+ break;
+ case cmdRMSUB:
+ if (menu_rmsub (ctrl, keyblock, arg_string))
+ {
+ redisplay = 1;
+ modified = 1;
+ merge_keys_and_selfsig (keyblock);
+ }
+ break;
+
case cmdQUIT:
if (have_commands)
goto leave;
@@ -2639,6 +2664,17 @@ tty_print_notations (int indent, PKT_signature * sig)
}
+static void
+show_notations (PKT_signature * selfsig)
+{
+ if (selfsig->flags.notation)
+ {
+ tty_printf (" ");
+ tty_printf (_("Notations: "));
+ tty_print_notations (5 + strlen (_("Notations: ")), selfsig);
+ }
+}
+
/*
* Show preferences of a public keyblock.
*/
@@ -2777,12 +2813,7 @@ show_prefs (PKT_user_id * uid, PKT_signature * selfsig, int verbose)
tty_printf ("\n");
}
- if (selfsig->flags.notation)
- {
- tty_printf (" ");
- tty_printf (_("Notations: "));
- tty_print_notations (5 + strlen (_("Notations: ")), selfsig);
- }
+ show_notations (selfsig);
}
}
else
@@ -2974,21 +3005,56 @@ show_key_with_all_names_colon (ctrl_t ctrl, estream_t fp, kbnode_t keyblock)
}
}
-
static void
show_names (estream_t fp,
KBNODE keyblock, PKT_public_key * pk, unsigned int flag,
int with_prefs)
{
KBNODE node;
- int i = 0;
+ int userids = 0;
+ int keys = 0;
for (node = keyblock; node; node = node->next)
{
- if (node->pkt->pkttype == PKT_USER_ID && !is_deleted_kbnode (node))
+ if ((node->pkt->pkttype == PKT_PUBLIC_KEY
+ || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
+ && !is_deleted_kbnode (node))
+ {
+ PKT_public_key *pk2 = node->pkt->pkt.public_key;
+ ++ keys;
+
+ if (with_prefs && pk)
+ {
+ if (pk->version > 3 || pk2->selfsigversion > 3)
+ {
+ KBNODE signode;
+
+ for (signode = node->next;
+ signode && signode->pkt->pkttype == PKT_SIGNATURE;
+ signode = signode->next)
+ {
+ PKT_signature *selfsig = signode->pkt->pkt.signature;
+
+ if (selfsig->flags.notation)
+ {
+ tty_printf (" Self-sig from %s on %s %s:\n",
+ isotimestamp (pk->timestamp),
+ node == keyblock
+ ? "primary key" : "subkey",
+ keystr (pk2->keyid));
+ show_notations (selfsig);
+ }
+ }
+ }
+ else
+ tty_fprintf (fp, _("There are no preferences on a"
+ " PGP 2.x-style KEY.\n"));
+ }
+ }
+ else if (node->pkt->pkttype == PKT_USER_ID && !is_deleted_kbnode (node))
{
PKT_user_id *uid = node->pkt->pkt.user_id;
- ++i;
+ ++userids;
if (!flag || (flag && (node->flag & flag)))
{
if (!(flag & NODFLG_MARK_A) && pk)
@@ -2997,11 +3063,11 @@ show_names (estream_t fp,
if (flag & NODFLG_MARK_A)
tty_fprintf (fp, " ");
else if (node->flag & NODFLG_SELUID)
- tty_fprintf (fp, "(%d)* ", i);
+ tty_fprintf (fp, "(%d)* ", userids);
else if (uid->is_primary)
- tty_fprintf (fp, "(%d). ", i);
+ tty_fprintf (fp, "(%d). ", userids);
else
- tty_fprintf (fp, "(%d) ", i);
+ tty_fprintf (fp, "(%d) ", userids);
tty_print_utf8_string2 (fp, uid->name, uid->len, 0);
tty_fprintf (fp, "\n");
if (with_prefs && pk)
@@ -3590,7 +3656,7 @@ menu_adduid (kbnode_t pub_keyblock, int photo, const char *photo_name,
return 0;
}
- err = make_keysig_packet (&sig, pk, uid, NULL, pk, 0x13, 0, 0, 0,
+ err = make_keysig_packet (&sig, pk, uid, NULL, pk, 0x13, 0, 0, 0, NULL,
keygen_add_std_prefs, pk, NULL);
if (err)
{
@@ -3973,7 +4039,7 @@ menu_addrevoker (ctrl_t ctrl, kbnode_t pub_keyblock, int sensitive)
break;
}
- rc = make_keysig_packet (&sig, pk, NULL, NULL, pk, 0x1F, 0, 0, 0,
+ rc = make_keysig_packet (&sig, pk, NULL, NULL, pk, 0x1F, 0, 0, 0, NULL,
keygen_add_revkey, &revkey, NULL);
if (rc)
{
@@ -4080,12 +4146,13 @@ menu_expire (KBNODE pub_keyblock)
if (mainkey)
rc = update_keysig_packet (&newsig, sig, main_pk, uid, NULL,
- main_pk, keygen_add_key_expire,
- main_pk);
+ main_pk, NULL,
+ keygen_add_key_expire, main_pk);
else
rc =
update_keysig_packet (&newsig, sig, main_pk, NULL, sub_pk,
- main_pk, keygen_add_key_expire, sub_pk);
+ main_pk, NULL,
+ keygen_add_key_expire, sub_pk);
if (rc)
{
log_error ("make_keysig_packet failed: %s\n",
@@ -4188,7 +4255,7 @@ menu_backsign (KBNODE pub_keyblock)
PACKET *newpkt;
rc = update_keysig_packet (&newsig, sig_pk->pkt->pkt.signature,
- main_pk, NULL, sub_pk, main_pk,
+ main_pk, NULL, sub_pk, main_pk, NULL,
NULL, NULL);
if (!rc)
{
@@ -4341,7 +4408,7 @@ menu_set_primary_uid (KBNODE pub_keyblock)
{
int rc = update_keysig_packet (&newsig, sig,
main_pk, uid, NULL,
- main_pk,
+ main_pk, NULL,
change_primary_uid_cb,
action > 0 ? "x" : NULL);
if (rc)
@@ -4431,7 +4498,7 @@ menu_set_preferences (KBNODE pub_keyblock)
int rc;
rc = update_keysig_packet (&newsig, sig,
- main_pk, uid, NULL, main_pk,
+ main_pk, uid, NULL, main_pk, NULL,
keygen_upd_std_prefs, NULL);
if (rc)
{
@@ -4566,7 +4633,7 @@ menu_set_keyserver_url (const char *url, KBNODE pub_keyblock)
rc = update_keysig_packet (&newsig, sig,
main_pk, uid, NULL,
- main_pk,
+ main_pk, NULL,
keygen_add_keyserver_url, uri);
if (rc)
{
@@ -4765,7 +4832,7 @@ menu_set_notation (const char *string, KBNODE pub_keyblock)
rc = update_keysig_packet (&newsig, sig,
main_pk, uid, NULL,
- main_pk,
+ main_pk, NULL,
keygen_add_notations, notation);
if (rc)
{
@@ -5358,7 +5425,7 @@ reloop: /* (must use this, because we are modifing the list) */
}
rc = make_keysig_packet (&sig, primary_pk,
unode->pkt->pkt.user_id,
- NULL, signerkey, 0x30, 0, 0, 0,
+ NULL, signerkey, 0x30, 0, 0, 0, NULL,
sign_mk_attrib, &attrib, NULL);
free_public_key (signerkey);
if (rc)
@@ -5451,7 +5518,7 @@ menu_revuid (KBNODE pub_keyblock)
node->flag &= ~NODFLG_SELUID;
rc = make_keysig_packet (&sig, pk, uid, NULL, pk, 0x30, 0,
- timestamp, 0,
+ timestamp, 0, NULL,
sign_mk_attrib, &attrib, NULL);
if (rc)
{
@@ -5516,7 +5583,7 @@ menu_revkey (KBNODE pub_keyblock)
return 0;
rc = make_keysig_packet (&sig, pk, NULL, NULL, pk,
- 0x20, 0, 0, 0,
+ 0x20, 0, 0, 0, NULL,
revocation_reason_build_cb, reason, NULL);
if (rc)
{
@@ -5578,8 +5645,8 @@ menu_revsubkey (KBNODE pub_keyblock)
node->flag &= ~NODFLG_SELKEY;
rc = make_keysig_packet (&sig, mainpk, NULL, subpk, mainpk,
- 0x28, 0, 0, 0, sign_mk_attrib, &attrib,
- NULL);
+ 0x28, 0, 0, 0, NULL,
+ sign_mk_attrib, &attrib, NULL);
if (rc)
{
write_status_error ("keysig", rc);
@@ -5676,3 +5743,40 @@ menu_showphoto (KBNODE keyblock)
}
}
}
+
+static int
+menu_addsub (ctrl_t ctrl, KBNODE keyblock, const char *sub)
+{
+ if (! sub || ! *sub)
+ /* XXX: testing hack, remove. */
+ // sub = "72DC07B5";
+ sub = "32C5067D";
+
+ if (! sub || ! *sub)
+ {
+ tty_printf (_("Usage: addsub KEYID\n"));
+ return 0;
+ }
+
+ return mailing_list_add_subscriber (ctrl, keyblock, sub) == 0;
+}
+
+
+static int
+menu_rmsub (ctrl_t ctrl, KBNODE keyblock, const char *sub)
+{
+ PKT_public_key *pk = keyblock->pkt->pkt.public_key;
+
+ if (! sub || ! *sub)
+ sub = "E3AC040E";
+
+ if (! sub || ! *sub)
+ {
+ tty_printf (_("Usage: rmsub KEYID\n"));
+ return 0;
+ }
+
+ printf ("rmsub %s %s\n", keystr (pk->keyid), sub);
+
+ return mailing_list_rm_subscriber (ctrl, keyblock, sub) == 0;
+}
diff --git a/g10/keygen.c b/g10/keygen.c
index 0f7a6a0aa..665a32c30 100644
--- a/g10/keygen.c
+++ b/g10/keygen.c
@@ -89,7 +89,8 @@ enum para_name {
pSERIALNO,
pCARDBACKUPKEY,
pHANDLE,
- pKEYSERVER
+ pKEYSERVER,
+ pMAILINGLIST
};
struct para_data_s {
@@ -102,6 +103,7 @@ struct para_data_s {
unsigned int usage;
struct revocation_key revkey;
char value[1];
+ int is_mailing_list;
} u;
};
@@ -807,7 +809,7 @@ make_backsig (PKT_signature *sig, PKT_public_key *pk,
cache_public_key (sub_pk);
err = make_keysig_packet (&backsig, pk, NULL, sub_pk, sub_psk, 0x19,
- 0, timestamp, 0, NULL, NULL, cache_nonce);
+ 0, timestamp, 0, NULL, NULL, NULL, cache_nonce);
if (err)
log_error ("make_keysig_packet failed for backsig: %s\n",
gpg_strerror (err));
@@ -915,7 +917,7 @@ write_direct_sig (KBNODE root, PKT_public_key *psk,
/* Make the signature. */
err = make_keysig_packet (&sig, pk, NULL,NULL, psk, 0x1F,
- 0, timestamp, 0,
+ 0, timestamp, 0, NULL,
keygen_add_revkey, revkey, cache_nonce);
if (err)
{
@@ -931,13 +933,13 @@ write_direct_sig (KBNODE root, PKT_public_key *psk,
}
-
/* Write a self-signature to the first user id in ROOT using the key
PSK. USE and TIMESTAMP give the extra data we need for the
signature. */
static gpg_error_t
write_selfsigs (KBNODE root, PKT_public_key *psk,
- unsigned int use, u32 timestamp, const char *cache_nonce)
+ unsigned int use, u32 timestamp,
+ int mailing_list, const char *cache_nonce)
{
gpg_error_t err;
PACKET *pkt;
@@ -945,6 +947,7 @@ write_selfsigs (KBNODE root, PKT_public_key *psk,
PKT_user_id *uid;
KBNODE node;
PKT_public_key *pk;
+ struct notation *notations = NULL;
if (opt.verbose)
log_info (_("writing self signature\n"));
@@ -968,10 +971,95 @@ write_selfsigs (KBNODE root, PKT_public_key *psk,
signature creation is able to retrieve the public key. */
cache_public_key (pk);
+ if (mailing_list)
+ /* Add the mailing-list notation. */
+ {
+ char *notation = "[email protected]=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;
+ }
+
+ if (mailing_list)
+ /* Add the subscriber-list-session-key notation. */
+ {
+ char *notation = "[email protected]";
+ struct pk_list pk_list;
+ /* The public key encrypted session key as a packet. */
+ iobuf_t pk_esk;
+ DEK session_key_initial;
+ char *buffer;
+ size_t len;
+ struct notation *notation_blob;
+
+ /* 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 = pk;
+ /* Don't throw the key id. */
+ pk_list.flags = 0;
+
+ pk_esk = iobuf_temp ();
+ if (! pk_esk)
+ {
+ log_bug ("Out of memory allocating pk_esk\n");
+ return gpg_error (GPG_ERR_INTERNAL);
+ }
+
+ memset (&session_key_initial, 0, sizeof (session_key_initial));
+ session_key_initial.algo = default_cipher_algo ();
+ make_session_key (&session_key_initial);
+
+ /* We don't need ctrl: we are certain that pk_list doesn't
+ contain a mailing list key, which is the only thing that
+ write_pubkey_enc_from_list needs ctrl for. */
+ err = write_pubkey_enc_from_list (NULL, &pk_list,
+ &session_key_initial, pk_esk);
+ if (err)
+ {
+ log_bug ("Failed to generate PK-ESK packet: %s\n",
+ gpg_strerror (err));
+ return err;
+ }
+
+ buffer = iobuf_get_temp_buffer (pk_esk);
+ len = iobuf_get_temp_length (pk_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);
+ }
+
+ {
+ FILE *fp = fopen ("/tmp/subscriber-list-session-key", "w");
+ fwrite (buffer, len, 1, fp);
+ fclose (fp);
+ }
+
+ notation_blob->next = notations;
+ notations = notation_blob;
+ }
+
+
/* Make the signature. */
+ /* We pass a callback (keygen_add_std_prefs) to add some extra data
+ to the self-signature. */
err = make_keysig_packet (&sig, pk, uid, NULL, psk, 0x13,
- 0, timestamp, 0,
+ 0, timestamp, 0, notations,
keygen_add_std_prefs, pk, cache_nonce);
+ free_notation (notations);
if (err)
{
log_error ("make_keysig_packet failed: %s\n", gpg_strerror (err));
@@ -991,9 +1079,10 @@ write_selfsigs (KBNODE root, PKT_public_key *psk,
signature creation time. PRI_PSK is the key use for signing.
SUB_PSK is a key used to create a back-signature; that one is only
used if USE has the PUBKEY_USAGE_SIG capability. */
-static int
+int
write_keybinding (KBNODE root, PKT_public_key *pri_psk, PKT_public_key *sub_psk,
- unsigned int use, u32 timestamp, const char *cache_nonce)
+ unsigned int use, u32 timestamp, const char *cache_nonce,
+ struct notation *notations)
{
gpg_error_t err;
PACKET *pkt;
@@ -1029,7 +1118,7 @@ write_keybinding (KBNODE root, PKT_public_key *pri_psk, PKT_public_key *sub_psk,
oduap.usage = use;
oduap.pk = sub_pk;
err = make_keysig_packet (&sig, pri_pk, NULL, sub_pk, pri_psk, 0x18,
- 0, timestamp, 0,
+ 0, timestamp, 0, notations,
keygen_add_key_flags_and_expire, &oduap,
cache_nonce);
if (err)
@@ -2471,7 +2560,7 @@ uid_already_in_keyblock (kbnode_t keyblock, const char *uid)
the function prevents the creation of an already existing user
ID. IF FULL is not set some prompts are not shown. */
static char *
-ask_user_id (int mode, int full, KBNODE keyblock)
+ask_user_id (int mode, int full, int mailing_list, KBNODE keyblock)
{
char *answer;
char *aname, *acomment, *amail, *uid;
@@ -2557,7 +2646,10 @@ ask_user_id (int mode, int full, KBNODE keyblock)
}
}
if (!acomment) {
- if (full) {
+ if (mailing_list) {
+ xfree (acomment);
+ acomment = xstrdup ("mailing list");
+ } else if (full) {
for(;;) {
xfree(acomment);
acomment = cpr_get("keygen.comment",_("Comment: "));
@@ -2766,7 +2858,7 @@ generate_user_id (KBNODE keyblock, const char *uidstr)
}
else
{
- p = ask_user_id (1, 1, keyblock);
+ p = ask_user_id (1, 1, 0, keyblock);
if (!p)
return NULL; /* Canceled. */
uid = uid_from_string (p);
@@ -2974,6 +3066,8 @@ get_parameter_u32( struct para_data_s *para, enum para_name key )
return r->u.expire;
if( r->key == pKEYUSAGE || r->key == pSUBKEYUSAGE )
return r->u.usage;
+ if( r->key == pMAILINGLIST )
+ return r->u.is_mailing_list;
return (unsigned int)strtoul( r->u.value, NULL, 10 );
}
@@ -3436,13 +3530,14 @@ quickgen_set_para (struct para_data_s *para, int for_subkey,
* Unattended generation of a standard key.
*/
void
-quick_generate_keypair (ctrl_t ctrl, const char *uid)
+quick_generate_keypair (ctrl_t ctrl, const char *uid, int mailing_list)
{
gpg_error_t err;
struct para_data_s *para = NULL;
struct para_data_s *r;
struct output_control_s outctrl;
int use_tty;
+ const char *mailing_list_comment = " (mailing list)";
memset (&outctrl, 0, sizeof outctrl);
@@ -3452,9 +3547,12 @@ quick_generate_keypair (ctrl_t ctrl, const char *uid)
&& gnupg_isatty (fileno (stdout))
&& gnupg_isatty (fileno (stderr)));
- r = xmalloc_clear (sizeof *r + strlen (uid));
+ r = xmalloc_clear (sizeof *r + strlen (uid)
+ + (mailing_list ? strlen (mailing_list_comment) : 0));
r->key = pUSERID;
strcpy (r->u.value, uid);
+ if (mailing_list_comment)
+ strcat (r->u.value, mailing_list_comment);
r->next = para;
para = r;
@@ -3476,6 +3574,15 @@ quick_generate_keypair (ctrl_t ctrl, const char *uid)
goto leave;
}
+ if (mailing_list)
+ {
+ r = xcalloc (1, sizeof *r );
+ r->key = pMAILINGLIST;
+ r->u.is_mailing_list = 1;
+ r->next = para;
+ para = r;
+ }
+
/* Check whether such a user ID already exists. */
{
KEYDB_HANDLE kdbhd;
@@ -3545,7 +3652,7 @@ quick_generate_keypair (ctrl_t ctrl, const char *uid)
* mode).
*/
void
-generate_keypair (ctrl_t ctrl, int full, const char *fname,
+generate_keypair (ctrl_t ctrl, int full, int mailing_list, const char *fname,
const char *card_serialno, int card_backup_key)
{
unsigned int nbits;
@@ -3577,6 +3684,15 @@ generate_keypair (ctrl_t ctrl, int full, const char *fname,
return;
}
+ if (mailing_list)
+ {
+ r = xcalloc (1, sizeof *r );
+ r->key = pMAILINGLIST;
+ r->u.is_mailing_list = 1;
+ r->next = para;
+ para = r;
+ }
+
if (card_serialno)
{
#ifdef ENABLE_CARD_SUPPORT
@@ -3787,7 +3903,7 @@ generate_keypair (ctrl_t ctrl, int full, const char *fname,
r->next = para;
para = r;
- uid = ask_user_id (0, full, NULL);
+ uid = ask_user_id (0, full, mailing_list, NULL);
if (!uid)
{
log_error(_("Key generation canceled.\n"));
@@ -4071,6 +4187,7 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
write_uid (pub_root, s );
err = write_selfsigs (pub_root, pri_psk,
get_parameter_uint (para, pKEYUSAGE), timestamp,
+ get_parameter_uint (para, pMAILINGLIST),
cache_nonce);
}
@@ -4088,7 +4205,8 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
get_parameter_u32 (para, pKEYEXPIRE));
if (!err)
err = write_keybinding (pub_root, pri_psk, NULL,
- PUBKEY_USAGE_AUTH, timestamp, cache_nonce);
+ PUBKEY_USAGE_AUTH, timestamp, cache_nonce,
+ NULL);
}
if (!err && get_parameter (para, pSUBKEYTYPE))
@@ -4129,7 +4247,7 @@ do_generate_keypair (ctrl_t ctrl, struct para_data_s *para,
if (!err)
err = write_keybinding (pub_root, pri_psk, sub_psk,
get_parameter_uint (para, pSUBKEYUSAGE),
- timestamp, cache_nonce);
+ timestamp, cache_nonce, NULL);
did_sub = 1;
}
@@ -4334,7 +4452,8 @@ generate_subkeypair (ctrl_t ctrl, kbnode_t keyblock)
sub_psk = node->pkt->pkt.public_key;
/* Write the binding signature. */
- err = write_keybinding (keyblock, pri_psk, sub_psk, use, cur_time, NULL);
+ err = write_keybinding (keyblock, pri_psk, sub_psk, use, cur_time,
+ NULL, NULL);
if (err)
goto leave;
@@ -4437,7 +4556,7 @@ generate_card_subkeypair (kbnode_t pub_keyblock,
sub_pk = node->pkt->pkt.public_key;
assert (sub_pk);
err = write_keybinding (pub_keyblock, pri_pk, sub_pk,
- use, cur_time, NULL);
+ use, cur_time, NULL, NULL);
}
leave:
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;
+}
diff --git a/g10/mailing-list.h b/g10/mailing-list.h
new file mode 100644
index 000000000..7cdaf1876
--- /dev/null
+++ b/g10/mailing-list.h
@@ -0,0 +1,44 @@
+/* mailing-list.h - Manage an encrypted 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/>.
+ */
+
+#ifndef G10_MAILING_LIST_H
+#define G10_MAILING_LIST_H
+
+#include "types.h"
+#include "util.h"
+#include "dek.h"
+
+void kbnode_dump (KBNODE kb);
+
+/* 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 mailing_list_add_subscriber (ctrl_t ctrl,
+ KBNODE ml_kb, const char *sub);
+
+gpg_error_t mailing_list_rm_subscriber (ctrl_t ctrl, KBNODE ml_kb,
+ const char *sub_orig);
+
+gpg_error_t mailing_list_subscribers (ctrl_t ctrl, KBNODE kb,
+ PK_LIST *pklistp);
+
+#endif
diff --git a/g10/main.h b/g10/main.h
index ec2442661..b6d9376f2 100644
--- a/g10/main.h
+++ b/g10/main.h
@@ -59,6 +59,7 @@ typedef struct
DEK *symkey_dek;
STRING2KEY *symkey_s2k;
cipher_filter_context_t cfx;
+ ctrl_t ctrl;
} encrypt_filter_context_t;
@@ -221,6 +222,8 @@ void display_online_help( const char *keyword );
/*-- encode.c --*/
int setup_symkey (STRING2KEY **symkey_s2k,DEK **symkey_dek);
+int write_symkey_enc (STRING2KEY *symkey_s2k, DEK *symkey_dek, DEK *dek,
+ iobuf_t out);
int use_mdc (pk_list_t pk_list,int algo);
int encrypt_symmetric (const char *filename );
int encrypt_store (const char *filename );
@@ -231,7 +234,11 @@ void encrypt_crypt_files (ctrl_t ctrl,
int nfiles, char **files, strlist_t remusr);
int encrypt_filter (void *opaque, int control,
iobuf_t a, byte *buf, size_t *ret_len);
+int write_pubkey_enc_from_list(ctrl_t ctrl, PK_LIST pk_list, DEK *dek, iobuf_t out );
+gpg_error_t symmetric_encrypt_buffer (DEK *dek, const char *password,
+ char *inbuffer, size_t inlen,
+ char **outbuffer, size_t *outlen);
/*-- sign.c --*/
int complete_sig (PKT_signature *sig, PKT_public_key *pksk, gcry_md_hd_t md,
@@ -280,10 +287,15 @@ void show_basic_key_info (KBNODE keyblock);
u32 parse_expire_string(const char *string);
u32 ask_expire_interval(int object,const char *def_expire);
u32 ask_expiredate(void);
-void quick_generate_keypair (ctrl_t ctrl, const char *uid);
-void generate_keypair (ctrl_t ctrl, int full, const char *fname,
+void quick_generate_keypair (ctrl_t ctrl, const char *uid, int mailing_list);
+void generate_keypair (ctrl_t ctrl, int full, int mailing_list,
+ const char *fname,
const char *card_serialno, int card_backup_key);
int keygen_set_std_prefs (const char *string,int personal);
+int write_keybinding (KBNODE root,
+ PKT_public_key *pri_psk, PKT_public_key *sub_psk,
+ unsigned int use, u32 timestamp, const char *cache_nonce,
+ struct notation *notation);
PKT_user_id *keygen_get_std_prefs (void);
int keygen_add_key_expire( PKT_signature *sig, void *opaque );
int keygen_add_std_prefs( PKT_signature *sig, void *opaque );
diff --git a/g10/mainproc.c b/g10/mainproc.c
index 5e6b40b3b..11e73cc62 100644
--- a/g10/mainproc.c
+++ b/g10/mainproc.c
@@ -69,6 +69,8 @@ struct mainproc_context
md_filter_context_t mfx;
int sigs_only; /* Process only signatures and reject all other stuff. */
int encrypt_only; /* Process only encryption messages. */
+ int symkey_only; /* Just process a SK-ESK packet. */
+ int pubkey_only; /* Just process a PK-ESK packet. */
/* Name of the file with the complete signature or the file with the
detached signature. This is currently only used to deduce the
@@ -140,8 +142,11 @@ release_list( CTX c )
c->any.data = 0;
c->any.uncompress_failed = 0;
c->last_was_session_key = 0;
- xfree (c->dek);
- c->dek = NULL;
+ if (! (c->symkey_only || c->pubkey_only))
+ {
+ xfree (c->dek);
+ c->dek = NULL;
+ }
}
@@ -1242,6 +1247,49 @@ proc_signature_packets (ctrl_t ctrl, void *anchor, iobuf_t a,
return rc;
}
+int
+proc_symkey_packet (ctrl_t ctrl, iobuf_t a, DEK *dek)
+{
+ CTX c = xmalloc_clear (sizeof *c);
+ int rc;
+
+ c->ctrl = ctrl;
+ c->symkey_only = 1;
+
+ rc = do_proc_packets (ctrl, c, a);
+ if (! rc && ! c->dek)
+ rc = gpg_error (GPG_ERR_BAD_KEY);
+
+ if (c->dek)
+ *dek = *c->dek;
+
+ xfree (c->dek);
+ xfree (c);
+
+ return rc;
+}
+
+int
+proc_pubkey_packet (ctrl_t ctrl, iobuf_t a, DEK *dek)
+{
+ CTX c = xmalloc_clear (sizeof *c);
+ int rc;
+
+ c->ctrl = ctrl;
+ c->pubkey_only = 1;
+
+ rc = do_proc_packets (ctrl, c, a);
+ if (! rc && ! c->dek)
+ rc = gpg_error (GPG_ERR_BAD_KEY);
+
+ if (c->dek)
+ *dek = *c->dek;
+
+ xfree (c->dek);
+ xfree (c);
+
+ return rc;
+}
int
proc_signature_packets_by_fd (ctrl_t ctrl,
@@ -1361,6 +1409,50 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a)
default: newpkt = 0; break;
}
}
+ else if (c->symkey_only)
+ {
+ switch (pkt->pkttype)
+ {
+ case PKT_PUBLIC_KEY:
+ case PKT_SECRET_KEY:
+ case PKT_USER_ID:
+ case PKT_SIGNATURE:
+ case PKT_PUBKEY_ENC:
+ case PKT_ENCRYPTED_MDC:
+ case PKT_PLAINTEXT:
+ case PKT_COMPRESSED:
+ case PKT_ONEPASS_SIG:
+ case PKT_GPG_CONTROL:
+ write_status_text (STATUS_UNEXPECTED, "0");
+ rc = GPG_ERR_UNEXPECTED;
+ goto leave;
+
+ case PKT_SYMKEY_ENC: proc_symkey_enc (c, pkt); break;
+ default: newpkt = 0; break;
+ }
+ }
+ else if (c->pubkey_only)
+ {
+ switch (pkt->pkttype)
+ {
+ case PKT_PUBLIC_KEY:
+ case PKT_SECRET_KEY:
+ case PKT_USER_ID:
+ case PKT_SIGNATURE:
+ case PKT_SYMKEY_ENC:
+ case PKT_ENCRYPTED_MDC:
+ case PKT_PLAINTEXT:
+ case PKT_COMPRESSED:
+ case PKT_ONEPASS_SIG:
+ case PKT_GPG_CONTROL:
+ write_status_text (STATUS_UNEXPECTED, "0");
+ rc = GPG_ERR_UNEXPECTED;
+ goto leave;
+
+ case PKT_PUBKEY_ENC: proc_pubkey_enc (ctrl, c, pkt); break;
+ default: newpkt = 0; break;
+ }
+ }
else if (c->sigs_only)
{
switch (pkt->pkttype)
@@ -1472,7 +1564,8 @@ do_proc_packets (ctrl_t ctrl, CTX c, iobuf_t a)
leave:
release_list (c);
- xfree(c->dek);
+ if (! (c->symkey_only || c->pubkey_only))
+ xfree(c->dek);
free_packet (pkt);
xfree (pkt);
free_md_filter_context (&c->mfx);
diff --git a/g10/packet.h b/g10/packet.h
index 16524f801..dd967666e 100644
--- a/g10/packet.h
+++ b/g10/packet.h
@@ -299,6 +299,7 @@ typedef struct
unsigned int backsig:2; /* 0=none, 1=bad, 2=good. */
unsigned int serialno_valid:1;/* SERIALNO below is valid. */
unsigned int exact:1; /* Found via exact (!) search. */
+ unsigned int mailing_list:1;
} flags;
PKT_user_id *user_id; /* If != NULL: found by that uid. */
struct revocation_key *revkey;
@@ -417,6 +418,9 @@ void reset_literals_seen(void);
int proc_packets (ctrl_t ctrl, void *ctx, iobuf_t a );
int proc_signature_packets (ctrl_t ctrl, void *ctx, iobuf_t a,
strlist_t signedfiles, const char *sigfile );
+int proc_symkey_packet (ctrl_t ctrl, iobuf_t a, DEK *dek);
+int proc_pubkey_packet (ctrl_t ctrl, iobuf_t a, DEK *dek);
+
int proc_signature_packets_by_fd (ctrl_t ctrl,
void *anchor, IOBUF a, int signed_data_fd );
int proc_encryption_packets (ctrl_t ctrl, void *ctx, iobuf_t a);
@@ -615,6 +619,8 @@ void build_attribute_subpkt(PKT_user_id *uid,byte type,
const void *buf,u32 buflen,
const void *header,u32 headerlen);
struct notation *string_to_notation(const char *string,int is_utf8);
+struct notation *blob_to_notation(const char *name,
+ const char *data, size_t len);
struct notation *sig_to_notation(PKT_signature *sig);
void free_notation(struct notation *notation);
@@ -676,6 +682,7 @@ int make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk,
PKT_user_id *uid, PKT_public_key *subpk,
PKT_public_key *pksk, int sigclass, int digest_algo,
u32 timestamp, u32 duration,
+ struct notation *notations,
int (*mksubpkt)(PKT_signature *, void *),
void *opaque,
const char *cache_nonce);
@@ -685,6 +692,7 @@ gpg_error_t update_keysig_packet (PKT_signature **ret_sig,
PKT_user_id *uid,
PKT_public_key *subpk,
PKT_public_key *pksk,
+ struct notation *notation,
int (*mksubpkt)(PKT_signature *, void *),
void *opaque );
diff --git a/g10/revoke.c b/g10/revoke.c
index a8f765856..feac4f29b 100644
--- a/g10/revoke.c
+++ b/g10/revoke.c
@@ -346,7 +346,7 @@ gen_desig_revoke (ctrl_t ctrl, const char *uname, strlist_t locusr)
/* create it */
rc = make_keysig_packet( &sig, pk, NULL, NULL, pk2, 0x20, 0,
- 0, 0,
+ 0, 0, NULL,
revocation_reason_build_cb, reason,
NULL);
if( rc ) {
@@ -473,7 +473,7 @@ create_revocation (const char *filename,
push_armor_filter (afx, out);
rc = make_keysig_packet (&sig, psk, NULL, NULL, psk, 0x20, 0,
- 0, 0,
+ 0, 0, NULL,
revocation_reason_build_cb, reason, cache_nonce);
if (rc)
{
diff --git a/g10/sign.c b/g10/sign.c
index c3ae028ea..3a4d880b0 100644
--- a/g10/sign.c
+++ b/g10/sign.c
@@ -747,6 +747,7 @@ sign_file (ctrl_t ctrl, strlist_t filenames, int detached, strlist_t locusr,
memset( &zfx, 0, sizeof zfx);
memset( &mfx, 0, sizeof mfx);
memset( &efx, 0, sizeof efx);
+ efx.ctrl = ctrl;
init_packet( &pkt );
if( filenames ) {
@@ -1369,6 +1370,7 @@ make_keysig_packet (PKT_signature **ret_sig, PKT_public_key *pk,
PKT_public_key *pksk,
int sigclass, int digest_algo,
u32 timestamp, u32 duration,
+ struct notation *notations,
int (*mksubpkt)(PKT_signature *, void *), void *opaque,
const char *cache_nonce)
{
@@ -1444,6 +1446,9 @@ make_keysig_packet (PKT_signature **ret_sig, PKT_public_key *pk,
sig->expiredate=sig->timestamp+duration;
sig->sig_class = sigclass;
+ if (notations)
+ keygen_add_notations (sig, notations);
+
build_sig_subpkt_from_sig( sig );
mk_notation_policy_etc (sig, pk, pksk);
@@ -1485,6 +1490,7 @@ update_keysig_packet( PKT_signature **ret_sig,
PKT_user_id *uid,
PKT_public_key *subpk,
PKT_public_key *pksk,
+ struct notation *notations,
int (*mksubpkt)(PKT_signature *, void *),
void *opaque)
{
@@ -1552,6 +1558,9 @@ update_keysig_packet( PKT_signature **ret_sig,
if (mksubpkt)
rc = (*mksubpkt)(sig, opaque);
+ if (!rc && notations)
+ keygen_add_notations (sig, notations);
+
if (!rc) {
hash_sigversion_to_magic (md, sig);
gcry_md_final (md);