diff options
author | Neal H. Walfield <[email protected]> | 2016-02-01 08:49:16 +0000 |
---|---|---|
committer | Neal H. Walfield <[email protected]> | 2016-02-07 23:31:55 +0000 |
commit | 28fe48cf9222bfe6fef8625821c658f3f4cc90da (patch) | |
tree | cf2f03b80c69e89f4685c5b6933418444a116d41 | |
parent | gpg: Fix format_keyid when dynamically allocating the buffer. (diff) | |
download | gnupg-28fe48cf9222bfe6fef8625821c658f3f4cc90da.tar.gz gnupg-28fe48cf9222bfe6fef8625821c658f3f4cc90da.zip |
Initial commit.
-rw-r--r-- | g10/Makefile.am | 3 | ||||
-rw-r--r-- | g10/build-packet.c | 88 | ||||
-rw-r--r-- | g10/card-util.c | 2 | ||||
-rw-r--r-- | g10/encrypt.c | 159 | ||||
-rw-r--r-- | g10/getkey.c | 37 | ||||
-rw-r--r-- | g10/gpg.c | 178 | ||||
-rw-r--r-- | g10/keyedit.c | 166 | ||||
-rw-r--r-- | g10/keygen.c | 159 | ||||
-rw-r--r-- | g10/mailing-list.c | 1040 | ||||
-rw-r--r-- | g10/mailing-list.h | 44 | ||||
-rw-r--r-- | g10/main.h | 16 | ||||
-rw-r--r-- | g10/mainproc.c | 99 | ||||
-rw-r--r-- | g10/packet.h | 8 | ||||
-rw-r--r-- | g10/revoke.c | 4 | ||||
-rw-r--r-- | g10/sign.c | 9 |
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(¬ation->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]; @@ -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); |