From 9b7f1f69761bc99512674a79cf3f63e4cb12cf3e Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Mon, 17 Oct 2005 17:21:15 +0000 Subject: [PATCH] exported subkeys are now merged into one output keyblock --- NEWS | 3 + THANKS | 1 + doc/ChangeLog | 4 + doc/gpg.texi | 86 +++++++++++------ doc/gpgv.sgml | 22 ++--- doc/gpgv.texi | 22 ++--- g10/ChangeLog | 9 ++ g10/export.c | 250 +++++++++++++++++++++++++++++++++++--------------- 8 files changed, 272 insertions(+), 125 deletions(-) diff --git a/NEWS b/NEWS index 9fe4ca9b9..404b3b528 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,9 @@ Noteworthy changes in version 1.4.3 associate a mail address with an OpenPGP key. See: XXXX for a description. + * When exporting subkeys, those specified with a key ID or + fingerpint and the '!' suffix are now merged into one keyblock. + * Added "gpg-zip", a program to create encrypted archives that can interoperate with PGP Zip. diff --git a/THANKS b/THANKS index c3b1a536c..4817792b9 100644 --- a/THANKS +++ b/THANKS @@ -145,6 +145,7 @@ Michael Roth mroth@nessie.de Michael Sobolev mss@despair.transas.com Michael Tokarev mjt@tls.msk.ru Nicolas Graner Nicolas.Graner@cri.u-psud.fr +Mike Dowling ML.Dowling at tu-bs.de Mike McEwan mike@lotusland.demon.co.uk Neal H Walfield neal@cs.uml.edu Nelson H. F. Beebe beebe@math.utah.edu diff --git a/doc/ChangeLog b/doc/ChangeLog index 8b39cd134..e4677e843 100644 --- a/doc/ChangeLog +++ b/doc/ChangeLog @@ -1,3 +1,7 @@ +2005-10-07 Werner Koch + + * gpgv.sgml: Small spelling corrections by Mike Dowling. + 2005-09-21 David Shaw * gpg.sgml: Note that --display-charset is just for display and diff --git a/doc/gpg.texi b/doc/gpg.texi index a9fe10fdd..5dccd70b9 100644 --- a/doc/gpg.texi +++ b/doc/gpg.texi @@ -322,21 +322,15 @@ the preferences in effect by including the implied preferences of are not already included in the preference list. @item setpref @code{string} -Set the list of user ID preferences to @code{string}, this should be a -string similar to the one printed by "pref". Using an empty string -will set the default preference string, using "none" will remove the -preferences. Use "gpg --version" to get a list of available -algorithms. This command just initializes an internal list and does -not change anything unless another command (such as "updpref") which -changes the self-signatures is used. - -@item updpref -Change the preferences of all user IDs (or just of the selected ones -to the current list of preferences. The timestamp of all affected -self-signatures will be advanced by one second. Note that while you -can change the preferences on an attribute user ID (aka "photo ID"), -GnuPG does not select keys via attribute user IDs so these preferences -will not be used by GnuPG. +Set the list of user ID preferences to @code{string} for all (or just +the selected) user IDs. Calling setpref with no arguments sets the +preference list to the default (either built-in or set via +--default-preference-list), and calling setpref with "none" as the +argument sets an empty preference list. Use "gpg --version" to get a +list of available algorithms. Note that while you can change the +preferences on an attribute user ID (aka "photo ID"), GnuPG does not +select keys via attribute user IDs so these preferences will not be +used by GnuPG. @item keyserver Set a preferred keyserver for the specified user ID(s). This allows @@ -506,6 +500,10 @@ keyservers set (see --keyserver-option honor-keyserver-url). Search the keyserver for the given names. Multiple names given here will be joined together to create the search string for the keyserver. Option --keyserver must be used to give the name of this keyserver. +Keyservers that support different search methods allow using the +syntax specified in "How to specify a user ID" below. Note that +different keyserver types support different search methods. Currently +only LDAP supports them all. @item --update-trustdb Do trust database maintenance. This command iterates over all keys @@ -935,6 +933,9 @@ sigs" after import. Defaults to no. After import, compact (remove all signatures from) any user IDs from the new key that are not usable. This is the same as running the --edit-key command "clean uids" after import. Defaults to no. + +@item import-clean +Identical to "import-clean-sigs import-clean-uids". @end table @item --export-options @code{parameters} @@ -975,8 +976,8 @@ the --edit-key command "clean uids" before export. Defaults to no. @item export-reset-subkey-passwd When using the "--export-secret-subkeys" command, this option resets the passphrases for all exported subkeys to empty. This is useful -when the exported subkey is to be used on an unattended amchine where -a passphrase won't make sense. Defaults to no. +when the exported subkey is to be used on an unattended machine where +a passphrase doesn't necessarily make sense. Defaults to no. @end table @item --list-options @code{parameters} @@ -1160,10 +1161,12 @@ found. @item --display-charset @code{name} Set the name of the native character set. This is used to convert -some informational strings like user IDs to the proper UTF-8 -encoding. If this option is not used, the default character set is -determined from the current locale. A verbosity level of 3 shows the -chosen set. Valid values for @code{name} are: +some informational strings like user IDs to the proper UTF-8 encoding. +Note that this has nothing to do with the character set of data to be +encrypted or signed; GnuPG does not recode user supplied data. If +this option is not used, the default character set is determined from +the current locale. A verbosity level of 3 shows the chosen set. +Valid values for @code{name} are: @table @asis @@ -1231,15 +1234,27 @@ There is a slight performance overhead using it. Write special status strings to the file descriptor @code{n}. See the file DETAILS in the documentation for a listing of them. +@item --status-file @code{file} +Same as --status-fd, except the status data is written to file +@code{file}. + @item --logger-fd @code{n} Write log output to file descriptor @code{n} and not to stderr. +@item --logger-file @code{file} +Same as --logger-fd, except the logger data is written to file +@code{file}. + @item --attribute-fd @code{n} Write attribute subpackets to the file descriptor @code{n}. This is most useful for use with --status-fd, since the status messages are needed to separate out the various subpackets from the stream delivered to the file descriptor. +@item --attribute-file @code{file} +Same as --attribute-fd, except the attribute data is written to file +@code{file}. + @item --comment @code{string} @itemx --no-comments Use @code{string} as a comment string in clear text signatures and @@ -1475,6 +1490,17 @@ Read the passphrase from file descriptor @code{n}. If you use can only be used if only one passphrase is supplied. Don't use this option if you can avoid it. +@item --passphrase-file @code{file} +Read the passphrase from file @code{file}. This can only be used if +only one passphrase is supplied. Obviously, a passphrase stored in a +file is of questionable security. Don't use this option if you can +avoid it. + +@item --passphrase @code{string} +Use @code{string} as the passphrase. This can only be used if only one +passphrase is supplied. Obviously, this is of very questionable +security. Don't use this option if you can avoid it. + @item --command-fd @code{n} This is a replacement for the deprecated shared-memory IPC mode. If this option is enabled, user input on questions is not expected @@ -1482,6 +1508,10 @@ from the TTY but from the given file descriptor. It should be used together with --status-fd. See the file doc/DETAILS in the source distribution for details on how to use it. +@item --command-file @code{file} +Same as --command-fd, except the commands are read out of file +@code{file} + @item --use-agent @itemx --no-use-agent Try to use the GnuPG-Agent. Please note that this agent is still under @@ -1871,10 +1901,9 @@ preferences. The most highly ranked algorithm in this list is also used when there are no recipient keys to consider (e.g. --symmetric). @item --default-preference-list @code{string} -Set the list of default preferences to @code{string}, this list should -be a string similar to the one printed by the command "pref" in the -edit menu. This affects both key generation and "updpref" in the edit -menu. +Set the list of default preferences to @code{string}. This preference +list is used for new keys and becomes the default for "setpref" in the +edit menu. @item --list-config Display various internal configuration parameters of GnuPG. This @@ -1921,10 +1950,9 @@ Using an exact to match string. The equal sign indicates this. Using the email address part which must match exactly. The left angle bracket indicates this email address mode. -@item +Heinrich Heine duesseldorf -All words must match exactly (not case sensitive) but can appear in -any order in the user ID. Words are any sequences of letters, -digits, the underscore and all characters with bit 7 set. +@item @@heinrichh +Match within the part of a user ID. The at sign +indicates this email address mode. @item Heine @itemx *Heine diff --git a/doc/gpgv.sgml b/doc/gpgv.sgml index 6b865e013..ee16d0e94 100644 --- a/doc/gpgv.sgml +++ b/doc/gpgv.sgml @@ -70,16 +70,16 @@ -This program is a stripped down version of - -v, --verbose -Give more information during processing. If used +Gives more information during processing. If used twice, the input data is listed in detail. @@ -127,8 +127,8 @@ The filename may be prefixed with a scheme: --homedir &ParmDir; Set the name of the home directory to &ParmDir; If this -option is not used it defaults to "~/.gnupg". It does -not make sense to use this in a options file. This +option is not used, it defaults to "~/.gnupg". It does +not make sense to use this in an options file. This also overrides the environment variable "GNUPGHOME". @@ -154,7 +154,7 @@ Write log output to file descriptor &ParmN; and not to stderr. GnuPG normally checks that the timestamps associated with keys and signatures have plausible values. However, sometimes a signature seems to be older than the key due to clock problems. This option makes these -checks just a warning. +checks just warnings. diff --git a/doc/gpgv.texi b/doc/gpgv.texi index 721f2e703..2985a5dbb 100644 --- a/doc/gpgv.texi +++ b/doc/gpgv.texi @@ -22,15 +22,15 @@ gpgv @code{gpgv} is the OpenPGP signature checking tool. -This program is a stripped down version of @code{gpg} which is only -able -to check signatures. It is somewhat smaller than the full blown -@code{gpg} and uses a different (and more simple way) to check that -the public keys used to made the signature are trustworth. There is +This program is a stripped down version of @code{gpg} which is able +only +to check signatures. It is somewhat smaller than the fully blown +@code{gpg} and uses a different (and simpler) way to check that +the public keys used to make the signature are trustworthy. There are no options files and only very few options are implemented. -@code{gpgv} assumes that all keys in the keyring are trustworty. -It uses by default a keyring named @file{trustedkeys.gpg} which is +@code{gpgv} assumes that all keys in the keyring are trustworthy. +By default it uses a keyring named @file{trustedkeys.gpg} which is assumed to be in the home directory as defined by GnuPG or set by an option or an environment variable. An option may be used to specify another keyring or even multiple keyrings. @@ -41,7 +41,7 @@ another keyring or even multiple keyrings. @table @asis @item -v, --verbose -Give more information during processing. If used +Gives more information during processing. If used twice, the input data is listed in detail. @item -q, --quiet @@ -59,8 +59,8 @@ The filename may be prefixed with a scheme: @item --homedir @code{directory} Set the name of the home directory to @code{directory} If this -option is not used it defaults to "~/.gnupg". It does -not make sense to use this in a options file. This +option is not used, it defaults to "~/.gnupg". It does +not make sense to use this in an options file. This also overrides the environment variable "GNUPGHOME". @item --status-fd @code{n} @@ -74,7 +74,7 @@ Write log output to file descriptor @code{n} and not to stderr. GnuPG normally checks that the timestamps associated with keys and signatures have plausible values. However, sometimes a signature seems to be older than the key due to clock problems. This option makes these -checks just a warning. +checks just warnings. @end table @chapheading RETURN VALUE diff --git a/g10/ChangeLog b/g10/ChangeLog index aff9c6bee..272df32ca 100644 --- a/g10/ChangeLog +++ b/g10/ChangeLog @@ -1,3 +1,12 @@ +2005-10-17 Werner Koch + + * export.c (do_export_stream): Factored some code out to ... + (skip_subkey_p): .. new. + (subkey_in_list_p, release_subkey_list): New. + (new_subkey_list_item): New. + (do_export_stream): Export exactly specified subkeys into one + keyblock. + 2005-10-13 David Shaw * keyedit.c (keyedit_menu, menu_backsign): New "backsign" command diff --git a/g10/export.c b/g10/export.c index a8ac6d8cf..ab5bd0f28 100644 --- a/g10/export.c +++ b/g10/export.c @@ -37,6 +37,17 @@ #include "i18n.h" #include "trustdb.h" + +/* An object to keep track of subkeys. */ +struct subkey_list_s +{ + struct subkey_list_s *next; + u32 kid[2]; +}; +typedef struct subkey_list_s *subkey_list_t; + + + static int do_export( STRLIST users, int secret, unsigned int options ); static int do_export_stream( IOBUF out, STRLIST users, int secret, KBNODE *keyblock_out, unsigned int options, @@ -77,6 +88,7 @@ parse_export_options(char *str,unsigned int *options,int noisy) return parse_options(str,options,export_opts,noisy); } + /**************** * Export the public keys (to standard out or --output). * Depending on opt.armor the output is armored. @@ -147,6 +159,124 @@ do_export( STRLIST users, int secret, unsigned int options ) } + +/* Release an entire subkey list. */ +static void +release_subkey_list (subkey_list_t list) +{ + while (list) + { + subkey_list_t tmp = list->next;; + xfree (list); + list = tmp; + } +} + + +/* Returns true if NODE is a subkey and contained in LIST. */ +static int +subkey_in_list_p (subkey_list_t list, KBNODE node) +{ + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY + || node->pkt->pkttype == PKT_SECRET_SUBKEY ) + { + u32 kid[2]; + + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + keyid_from_pk (node->pkt->pkt.public_key, kid); + else + keyid_from_sk (node->pkt->pkt.secret_key, kid); + + for (; list; list = list->next) + if (list->kid[0] == kid[0] && list->kid[1] == kid[1]) + return 1; + } + return 0; +} + +/* Allocate a new subkey list item from NODE. */ +static subkey_list_t +new_subkey_list_item (KBNODE node) +{ + subkey_list_t list = xcalloc (1, sizeof *list); + + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + keyid_from_pk (node->pkt->pkt.public_key, list->kid); + else if (node->pkt->pkttype == PKT_SECRET_SUBKEY) + keyid_from_sk (node->pkt->pkt.secret_key, list->kid); + + return list; +} + + +/* Helper function to check whether the subkey at NODE actually + matches the description at DESC. The function returns true if the + key under question has been specified by an exact specification + (keyID or fingerprint) and does match the one at NODE. It is + assumed that the packet at NODE is either a public or secret + subkey. */ +static int +exact_subkey_match_p (KEYDB_SEARCH_DESC *desc, KBNODE node) +{ + u32 kid[2]; + byte fpr[MAX_FINGERPRINT_LEN]; + size_t fprlen; + int result = 0; + + switch(desc->mode) + { + case KEYDB_SEARCH_MODE_SHORT_KID: + case KEYDB_SEARCH_MODE_LONG_KID: + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + keyid_from_pk (node->pkt->pkt.public_key, kid); + else + keyid_from_sk (node->pkt->pkt.secret_key, kid); + break; + + case KEYDB_SEARCH_MODE_FPR16: + case KEYDB_SEARCH_MODE_FPR20: + case KEYDB_SEARCH_MODE_FPR: + if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) + fingerprint_from_pk (node->pkt->pkt.public_key, fpr,&fprlen); + else + fingerprint_from_sk (node->pkt->pkt.secret_key, fpr,&fprlen); + break; + + default: + break; + } + + switch(desc->mode) + { + case KEYDB_SEARCH_MODE_SHORT_KID: + if (desc->u.kid[1] == kid[1]) + result = 1; + break; + + case KEYDB_SEARCH_MODE_LONG_KID: + if (desc->u.kid[0] == kid[0] && desc->u.kid[1] == kid[1]) + result = 1; + break; + + case KEYDB_SEARCH_MODE_FPR16: + if (!memcmp (desc->u.fpr, fpr, 16)) + result = 1; + break; + + case KEYDB_SEARCH_MODE_FPR20: + case KEYDB_SEARCH_MODE_FPR: + if (!memcmp (desc->u.fpr, fpr, 20)) + result = 1; + break; + + default: + break; + } + + return result; +} + + /* If keyblock_out is non-NULL, AND the exit code is zero, then it contains a pointer to the first keyblock found and exported. No other keyblocks are exported. The caller must free it. */ @@ -160,6 +290,7 @@ do_export_stream( IOBUF out, STRLIST users, int secret, KBNODE kbctx, node; size_t ndesc, descindex; KEYDB_SEARCH_DESC *desc = NULL; + subkey_list_t subkey_list = NULL; /* Track alreay processed subkeys. */ KEYDB_HANDLE kdbhd; STRLIST sl; u32 keyid[2]; @@ -170,7 +301,7 @@ do_export_stream( IOBUF out, STRLIST users, int secret, if (!users) { ndesc = 1; - desc = xmalloc_clear ( ndesc * sizeof *desc); + desc = xcalloc ( ndesc, sizeof *desc ); desc[0].mode = KEYDB_SEARCH_MODE_FIRST; } else { @@ -186,7 +317,7 @@ do_export_stream( IOBUF out, STRLIST users, int secret, sl->d, g10_errstr (G10ERR_INV_USER_ID)); } - /* it would be nice to see which of the given users did + /* It would be nice to see which of the given users did actually match one in the keyring. To implement this we need to have a found flag for each entry in desc and to set this we must check all those entries after a match to mark @@ -209,7 +340,7 @@ do_export_stream( IOBUF out, STRLIST users, int secret, if (!users) desc[0].mode = KEYDB_SEARCH_MODE_NEXT; - /* read the keyblock */ + /* Read the keyblock. */ rc = keydb_get_keyblock (kdbhd, &keyblock ); if( rc ) { log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) ); @@ -222,7 +353,7 @@ do_export_stream( IOBUF out, STRLIST users, int secret, keyid_from_sk(sk,sk_keyid); - /* we can't apply GNU mode 1001 on an unprotected key */ + /* We can't apply GNU mode 1001 on an unprotected key. */ if( secret == 2 && !sk->is_protected ) { log_info(_("key %s: not protected - skipped\n"), @@ -230,7 +361,7 @@ do_export_stream( IOBUF out, STRLIST users, int secret, continue; } - /* no v3 keys with GNU mode 1001 */ + /* No v3 keys with GNU mode 1001. */ if( secret == 2 && sk->version == 3 ) { log_info(_("key %s: PGP 2.x style key - skipped\n"), @@ -251,7 +382,7 @@ do_export_stream( IOBUF out, STRLIST users, int secret, } else { - /* It's a public key export */ + /* It's a public key export. */ if((options&EXPORT_MINIMAL) && (node=find_kbnode(keyblock,PKT_PUBLIC_KEY))) keyid_from_pk(node->pkt->pkt.public_key,keyid); @@ -260,7 +391,7 @@ do_export_stream( IOBUF out, STRLIST users, int secret, clean_uids_from_key(keyblock,opt.verbose); } - /* and write it */ + /* And write it. */ for( kbctx=NULL; (node = walk_kbnode( keyblock, &kbctx, 0 )); ) { if( skip_until_subkey ) { @@ -277,89 +408,59 @@ do_export_stream( IOBUF out, STRLIST users, int secret, if( node->pkt->pkttype == PKT_COMMENT ) continue; - /* make sure that ring_trust packets never get exported */ + /* Make sure that ring_trust packets never get exported. */ if (node->pkt->pkttype == PKT_RING_TRUST) continue; /* If exact is set, then we only export what was requested (plus the primary key, if the user didn't specifically - request it) */ + request it). */ if(desc[descindex].exact && (node->pkt->pkttype==PKT_PUBLIC_SUBKEY || node->pkt->pkttype==PKT_SECRET_SUBKEY)) { - u32 kid[2]; - byte fpr[MAX_FINGERPRINT_LEN]; - size_t fprlen; - - switch(desc[descindex].mode) - { - case KEYDB_SEARCH_MODE_SHORT_KID: - case KEYDB_SEARCH_MODE_LONG_KID: - if(node->pkt->pkttype==PKT_PUBLIC_SUBKEY) - keyid_from_pk(node->pkt->pkt.public_key,kid); - else - keyid_from_sk(node->pkt->pkt.secret_key,kid); - break; - - case KEYDB_SEARCH_MODE_FPR16: - case KEYDB_SEARCH_MODE_FPR20: - case KEYDB_SEARCH_MODE_FPR: - if(node->pkt->pkttype==PKT_PUBLIC_SUBKEY) - fingerprint_from_pk(node->pkt->pkt.public_key, - fpr,&fprlen); - else - fingerprint_from_sk(node->pkt->pkt.secret_key, - fpr,&fprlen); - break; - - default: - break; - } - - switch(desc[descindex].mode) - { - case KEYDB_SEARCH_MODE_SHORT_KID: - if (desc[descindex].u.kid[1] != kid[1]) - skip_until_subkey=1; - break; - case KEYDB_SEARCH_MODE_LONG_KID: - if (desc[descindex].u.kid[0] != kid[0] - || desc[descindex].u.kid[1] != kid[1]) - skip_until_subkey=1; - break; - case KEYDB_SEARCH_MODE_FPR16: - if (memcmp (desc[descindex].u.fpr, fpr, 16)) - skip_until_subkey=1; - break; - case KEYDB_SEARCH_MODE_FPR20: - case KEYDB_SEARCH_MODE_FPR: - if (memcmp (desc[descindex].u.fpr, fpr, 20)) - skip_until_subkey=1; - break; - default: - break; - } - - /* XXX: before skipping a subkey, check whether any - other description wants an exact macth on a subkey - and include that subkey into the output too. Need - to add this subkey to a list so that it won't get - processed a second time. + if (!exact_subkey_match_p (desc+descindex, node)) + { + /* Before skipping this subkey, check whether any + other description wants an exact match on a + subkey and include that subkey into the output + too. Need to add this subkey to a list so that + it won't get processed a second time. - So the first step here is to check that list and - skip in any case if the key is in that list. + So the first step here is to check that list and + skip in any case if the key is in that list. - We need this whole mess becuase the import fnction - is not able to merge secret key and thus it is not - possible to output them as two keys and have import - merge them. - */ + We need this whole mess because the import + function is not able to merge secret keys and + thus it is useless to output them as two + separate keys and have import merge them. */ + if (subkey_in_list_p (subkey_list, node)) + skip_until_subkey = 1; /* Already processed this one. */ + else + { + size_t j; + + for (j=0; j < ndesc; j++) + if (j != descindex && desc[j].exact + && exact_subkey_match_p (desc+j, node)) + break; + if (!(j < ndesc)) + skip_until_subkey = 1; /* No other one matching. */ + } + } if(skip_until_subkey) continue; + + /* Mark this one as processed. */ + { + subkey_list_t tmp = new_subkey_list_item (node); + tmp->next = subkey_list; + subkey_list = tmp; + } } + if(node->pkt->pkttype==PKT_USER_ID) { /* Run clean_sigs_from_uid against each uid if @@ -510,6 +611,7 @@ do_export_stream( IOBUF out, STRLIST users, int secret, rc = 0; leave: + release_subkey_list (subkey_list); xfree(desc); keydb_release (kdbhd); if(rc || keyblock_out==NULL)