summaryrefslogtreecommitdiff
path: root/g10
diff options
context:
space:
mode:
Diffstat (limited to 'g10')
-rw-r--r--g10/ChangeLog12964
-rw-r--r--g10/Makefile.am156
-rw-r--r--g10/Makefile.in801
-rw-r--r--g10/apdu.c3013
-rw-r--r--g10/apdu.h123
-rw-r--r--g10/app-common.h197
-rw-r--r--g10/app-openpgp.c2528
-rw-r--r--g10/armor.c1474
-rw-r--r--g10/build-packet.c1308
-rw-r--r--g10/card-util.c1610
-rw-r--r--g10/cardglue.c1432
-rw-r--r--g10/cardglue.h211
-rw-r--r--g10/ccid-driver.c2745
-rw-r--r--g10/ccid-driver.h108
-rw-r--r--g10/cipher.c153
-rw-r--r--g10/compress-bz2.c242
-rw-r--r--g10/compress.c365
-rw-r--r--g10/dearmor.c137
-rw-r--r--g10/decrypt.c191
-rw-r--r--g10/delkey.c221
-rw-r--r--g10/encode.c877
-rw-r--r--g10/encr-data.c320
-rw-r--r--g10/exec.c626
-rw-r--r--g10/exec.h52
-rw-r--r--g10/export.c600
-rw-r--r--g10/filter.h167
-rw-r--r--g10/free-packet.c569
-rw-r--r--g10/getkey.c3003
-rw-r--r--g10/global.h30
-rw-r--r--g10/gpg.c4207
-rw-r--r--g10/gpgv.c434
-rw-r--r--g10/helptext.c300
-rw-r--r--g10/import.c2396
-rw-r--r--g10/iso7816.c685
-rw-r--r--g10/iso7816.h81
-rw-r--r--g10/kbnode.c399
-rw-r--r--g10/keydb.c807
-rw-r--r--g10/keydb.h317
-rw-r--r--g10/keyedit.c5094
-rw-r--r--g10/keygen.c3707
-rw-r--r--g10/keyid.c753
-rw-r--r--g10/keylist.c1638
-rw-r--r--g10/keyring.c1624
-rw-r--r--g10/keyring.h46
-rw-r--r--g10/keyserver-internal.h54
-rw-r--r--g10/keyserver.c2132
-rw-r--r--g10/main.h300
-rw-r--r--g10/mainproc.c2083
-rw-r--r--g10/mdfilter.c77
-rw-r--r--g10/misc.c1284
-rw-r--r--g10/openfile.c430
-rw-r--r--g10/options.h335
-rw-r--r--g10/options.skel241
-rw-r--r--g10/packet.h569
-rw-r--r--g10/parse-packet.c2411
-rw-r--r--g10/passphrase.c1084
-rw-r--r--g10/photoid.c385
-rw-r--r--g10/photoid.h35
-rw-r--r--g10/pipemode.c318
-rw-r--r--g10/pkclist.c1448
-rw-r--r--g10/plaintext.c589
-rw-r--r--g10/progress.c118
-rw-r--r--g10/pubkey-enc.c355
-rw-r--r--g10/revoke.c738
-rw-r--r--g10/seckey-cert.c427
-rw-r--r--g10/seskey.c271
-rw-r--r--g10/sig-check.c616
-rw-r--r--g10/sign.c1578
-rw-r--r--g10/signal.c238
-rw-r--r--g10/skclist.c223
-rw-r--r--g10/status.c790
-rw-r--r--g10/status.h150
-rw-r--r--g10/tdbdump.c233
-rw-r--r--g10/tdbio.c1633
-rw-r--r--g10/tdbio.h118
-rw-r--r--g10/textfilter.c251
-rw-r--r--g10/tlv.c306
-rw-r--r--g10/tlv.h109
-rw-r--r--g10/trustdb.c2368
-rw-r--r--g10/trustdb.h98
-rw-r--r--g10/verify.c211
81 files changed, 83317 insertions, 0 deletions
diff --git a/g10/ChangeLog b/g10/ChangeLog
new file mode 100644
index 0000000..20d3ec1
--- /dev/null
+++ b/g10/ChangeLog
@@ -0,0 +1,12964 @@
+2006-12-04 Werner Koch <wk@g10code.com>
+
+ * filter.h (armor_filter_context_t): New field REFCOUNT.
+ * armor.c (new_armor_context, release_armor_context)
+ (push_armor_filter): New.
+ (armor_filter): Call releae_armor_context for IOBUFCTRL_FREE.
+ * import.c (import): Use the new function here instead of the
+ old hack using the iobuf_push_filter2.
+ * keyserver.c (keyserver_spawn): Ditto.
+
+2006-12-03 Werner Koch <wk@g10code.com>
+
+ * keyedit.c (menu_clean): Made strings translatable.
+
+2006-12-03 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (menu_clean): Show "already minimized" rather than
+ "already clean" when a minimized key is minimized again. From
+ Dirk Traulsen.
+
+2006-12-02 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, gpg.c (main), passphrase.c (passphrase_to_dek): Add
+ --passphrase-repeat option to control how many times gpg will
+ re-prompt for a passphrase to ensure the user has typed it
+ correctly. Defaults to 1.
+
+2006-11-27 Werner Koch <wk@g10code.com>
+
+ * openfile.c (ask_outfile_name): Fixed buffer overflow occurring
+ if make_printable_string returns a longer string. Fixes bug 728.
+
+2006-10-23 Werner Koch <wk@g10code.com>
+
+ * gpg.c (main): New command --gpgconf-list.
+
+2006-10-12 David Shaw <dshaw@jabberwocky.com>
+
+ * parse-packet.c (parse_symkeyenc): Show the unpacked as well as
+ the packed s2k iteration count.
+
+ * main.h, options.h, gpg.c (encode_s2k_iterations, main),
+ passphrase.c (hash_passphrase): Add --s2k-count option to specify
+ the number of s2k hash iterations.
+
+2006-10-06 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_spawn): Write the 16-digit keyid rather
+ than whatever key selector the user used on the command line.
+
+2006-10-02 Werner Koch <wk@g10code.com>
+
+ * encr-data.c (decrypt_data, mdc_decode_filter): Check the MDC
+ right here and don't let parse-packet handle the MDC.
+
+2006-08-21 Werner Koch <wk@g10code.com>
+
+ * skclist.c (is_insecure): Also test for uppercase version of the
+ insecure string.
+
+2006-07-31 Werner Koch <wk@g10code.com>
+
+ * openfile.c (open_outfile) [USE_ONLY_8DOT3]: Search backwards for
+ the dot. Fixes bug 654.
+
+ * passphrase.c (agent_open): Use log_info instead of log_error to
+ allow a fallback without having gpg return an error code. Fixes
+ bug #655.
+
+ * encode.c (encode_crypt_files): Invalidate the whole fd cache.
+ This is a workaround for problems in iobuf's stupid fd cache.
+ * decrypt.c (decrypt_messages): Ditto.
+ * verify.c (verify_files): Ditto.
+
+2006-07-26 Werner Koch <wk@g10code.com>
+
+ * keygen.c (gen_card_key_with_backup): Initialize sk_{un}protected.
+
+ * import.c (import): Initialize KEYBLOCK.
+
+ * pkclist.c (edit_ownertrust): Intialize trust to avoid gcc
+ warning.
+
+ * parse-packet.c (parse_comment): Cap comments at 65k.
+ (parse_gpg_control): Skip too large control packets.
+
+2006-06-28 David Shaw <dshaw@jabberwocky.com>
+
+ * keydb.h, pkclist.c (select_algo_from_prefs, algo_available):
+ Pass a union for preference hints rather than doing void * games.
+
+ * sign.c (sign_file): Use it here.
+
+ * sign.c (sign_file): When signing with multiple DSA keys, one
+ being DSA1 and one being DSA2 and encrypting at the same time, if
+ the recipient preferences give a hash that can work with the DSA2
+ key, then allow the DSA1 key to be promoted rather than giving up
+ and using hash_for().
+
+ * pkclist.c (algo_available): Automatically enable DSA2 mode when
+ handling a key that clearly isn't DSA1 (i.e. q!=160).
+
+2006-06-28 Werner Koch <wk@g10code.com>
+
+ * import.c (check_prefs_warning): Fix change for better
+ translatability.
+
+ * app-openpgp.c (do_writekey): Fixed computation of memmove
+ length. This led to garbled keys if E was larger than one byte.
+ Thanks to Achim Pietig for hinting at the garbled E.
+
+2006-06-27 Werner Koch <wk@g10code.com>
+
+ * gpg.c (reopen_std) [HAVE_W32_SYSTEM]: Do not use it.
+
+2006-06-22 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, gpg.c (main), keygen.c (keygen_upd_std_prefs,
+ keygen_add_std_prefs, proc_parameter_file): Add
+ --default-keyserver-url to specify a keyserver URL at key
+ generation time, and "Keyserver:" keyword for doing the same
+ through a batch file.
+
+ * sign.c (do_sign): Accept a truncated hash even for DSA1 keys (be
+ liberal in what you accept, etc).
+
+2006-06-12 David Shaw <dshaw@jabberwocky.com>
+
+ * import.c (import_one): Add a flag (from_sk) so we don't check
+ prefs on an autoconverted public key. The check should only
+ happen on the sk side. Noted by Dirk Traulsen.
+
+2006-06-09 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (gen_card_key): Add optional argument to return a
+ pointer (not a copy) of the stub secret key for the secret key we
+ just generated on the card.
+ (generate_card_subkeypair): Use it here so that the signing key on
+ the card can use the card to generate the 0x19 backsig on the
+ primary key. Noted by Janko Heilgeist and Jonas Oberg.
+
+ * parse-packet.c (parse_user_id): Cap the user ID size at 2048
+ bytes. This prevents a memory allocation attack with a very large
+ user ID. A very large packet length could even cause the
+ allocation (a u32) to wrap around to a small number. Noted by
+ Evgeny Legerov on full-disclosure.
+
+2006-05-25 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (gen_dsa): Allow generating DSA2 keys
+ (allow specifying sizes > 1024 when --enable-dsa2 is set). The
+ size of q is set automatically based on the key size.
+ (ask_keysize, generate_keypair): Ask for DSA size when
+ --enable-dsa2 is set.
+
+2006-05-24 David Shaw <dshaw@jabberwocky.com>
+
+ * exec.c (make_tempdir): Fix bug with a temporary directory on
+ Win32 that is over 256 bytes long. Noted by Israel G. Lugo.
+
+2006-05-23 David Shaw <dshaw@jabberwocky.com>
+
+ * gpg.c (reopen_std): New function to reopen fd 0, 1, or 2 if we
+ are called with them closed. This is to protect our
+ keyring/trustdb files from corruption if they get attached to one
+ of the standard fds. Print a warning if possible that this has
+ happened, and fail completely if we cannot reopen (should never
+ happen).
+ (main): Call it here.
+
+2006-05-22 David Shaw <dshaw@jabberwocky.com>
+
+ * parse-packet.c (dump_sig_subpkt, parse_signature),
+ build-packet.c (build_sig_subpkt_from_sig), getkey.c
+ (fixup_uidnode, merge_selfsigs_main, merge_selfsigs_subkey),
+ keygen.c (keygen_add_key_expire): Fix meaning of key expiration
+ and sig expiration subpackets - zero means "never expire"
+ according to 2440, not "expire instantly".
+
+ * getkey.c (get_pubkey_byname), import.c (import_one): Fix key
+ selection problem when auto-key-locate returns a list of keys, not
+ all of which are usable (revoked, expired, etc). Noted by Simon
+ Josefsson.
+
+2006-04-26 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (direct_uri_map): New.
+ (keyserver_spawn): Used here to add "_uri" to certain gpgkeys_xxx
+ helpers when the meaning is different if a path is provided
+ (i.e. ldap).
+ (keyserver_import_cert): Show warning if there is a CERT
+ fingerprint, but no --keyserver set.
+
+2006-04-22 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c: Fix build problem with platforms that stick libcurl
+ in a place not in the regular include search path.
+
+2006-04-20 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, gpg.c (main): Add --enable-dsa2 and --disable-dsa2.
+ Defaults to disable.
+
+ * pkclist.c (algo_available): If --enable-dsa2 is set, we're
+ allowed to truncate hashes to fit DSA keys.
+
+ * sign.c (match_dsa_hash): New. Return the best match hash for a
+ given q size.
+ (do_sign, hash_for, sign_file): When signing with a DSA key, if it
+ has q==160, assume it is an old DSA key and don't allow truncation
+ unless --enable-dsa2 is also set. q!=160 always allows truncation
+ since they must be DSA2 keys.
+ (make_keysig_packet): If the user doesn't specify a
+ --cert-digest-algo, use match_dsa_hash to pick the best hash for
+ key signatures.
+
+2006-04-19 David Shaw <dshaw@jabberwocky.com>
+
+ * gpg.c (print_mds), armor.c (armor_filter, parse_hash_header):
+ Add SHA-224.
+
+ * sign.c (write_plaintext_packet), encode.c (encode_simple):
+ Factor common literal packet setup code from here, to...
+
+ * main.h, plaintext.c (setup_plaintext_name): Here. New. Make sure
+ the literal packet filename field is UTF-8 encoded.
+
+ * options.h, gpg.c (main): Make sure --set-filename is UTF-8
+ encoded and note when filenames are already UTF-8.
+
+2006-04-18 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (menu_backsign): Give some more verbose errors when we
+ have no need to backsign.
+
+2006-04-11 David Shaw <dshaw@jabberwocky.com>
+
+ * options.skel, photoid.c (get_default_photo_command): Find an
+ image viewer at runtime. Seems FC5 doesn't have xloadimage.
+
+2006-04-08 David Shaw <dshaw@jabberwocky.com>
+
+ * getkey.c (parse_auto_key_locate): Fix dupe-removal code.
+
+ * keyedit.c (menu_backsign): Allow backsigning even if the secret
+ subkey doesn't have a binding signature.
+
+ * armor.c (radix64_read): Don't report EOF when reading only a pad
+ (=) character. The EOF actually starts after the pad.
+
+ * gpg.c (main): Make --export, --send-keys, --recv-keys,
+ --refresh-keys, and --fetch-keys follow their arguments from left
+ to right. Suggested by Peter Palfrader.
+
+2006-04-08 Werner Koch <wk@g10code.com>
+
+ * mainproc.c (list_node): Print ring trust value only if not empty
+ and --with-colons has been given.
+
+2006-04-05 Werner Koch <wk@g10code.com>
+
+ * getkey.c (user_id_not_found_utf8): New.
+ (get_primary_uid, get_user_id): Use it. Fixes Debian bug #205028
+ in the right way.
+
+2006-04-03 Werner Koch <wk@g10code.com>
+
+ * import.c (check_prefs_warning): Merged strings for better
+ translation.
+
+ * gpg.c (main) [__GLIBC__]: Default to libpcsclite.so.1.
+
+ * status.h, status.c (STATUS_BEGIN_SIGNING): New. Suggested by
+ Daiki Ueno.
+ * textfilter.c (copy_clearsig_text): Issue new status code.
+ * sign.c (sign_file, sign_symencrypt_file): Ditto.
+
+2006-03-31 David Shaw <dshaw@jabberwocky.com>
+
+ * getkey.c (get_pubkey_byname): Fix missing auto_key_retrieve
+ unlock. Fix strings to not start with a capital letter as per
+ convention.
+
+2006-03-30 David Shaw <dshaw@jabberwocky.com>
+
+ * main.h, seskey.c (encode_md_value): Modify to allow a q size
+ greater than 160 bits as per DSA2. This will allow us to verify
+ and issue DSA2 signatures for some backwards compatibility once we
+ start generating DSA2 keys.
+ * sign.c (do_sign), sig-check.c (do_check): Change all callers.
+
+ * sign.c (do_sign): Enforce the 160-bit check for new signatures
+ here since encode_md_value can handle non-160-bit digests now.
+ This will need to come out once the standard for DSA2 is firmed
+ up.
+
+2006-03-28 Werner Koch <wk@g10code.com>
+
+ * openfile.c (overwrite_filep): Fix small cpr issue. Noted by
+ Daiki Ueno.
+
+2006-03-22 David Shaw <dshaw@jabberwocky.com>
+
+ * getkey.c (parse_auto_key_locate): Silently strip out duplicates
+ rather than causing an error.
+
+2006-03-22 Werner Koch <wk@g10code.com>
+
+ * sig-check.c (signature_check2): Changed warning URL to include faq.
+ * misc.c (idea_cipher_warn): Ditto.
+
+2006-03-22 David Shaw <dshaw@jabberwocky.com>
+
+ * mainproc.c (get_pka_address): Fix bug introduced as part of
+ sig_to_notation conversion. Noted by Peter Palfradrer.
+
+2006-03-21 Werner Koch <wk@g10code.com>
+
+ * cardglue.c (agent_scd_pksign): Allow the use of ripemd-160 along
+ with scdaemon.
+
+2006-03-16 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_import_cert): Handle the IPGP CERT type
+ for both the fingerprint alone, and fingerprint+URL cases.
+
+ * getkey.c (get_pubkey_byname): Minor cleanup.
+
+2006-03-13 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver-internal.h, keyserver.c (keyserver_import_pka): Use
+ the same API as the other auto-key-locate fetchers.
+
+ * getkey.c (get_pubkey_byname): Use the fingerprint of the key
+ that we actually fetched. This helps prevent problems where the
+ key that we fetched doesn't have the same name that we used to
+ fetch it. In the case of CERT and PKA, this is an actual security
+ requirement as the URL might point to a key put in by an attacker.
+ By forcing the use of the fingerprint, we won't use the attacker's
+ key here.
+
+ * keyserver-internal.h, keyserver.c (keyserver_spawn,
+ keyserver_work, keyserver_import_cert, keyserver_import_name,
+ keyserver_import_ldap): Pass fingerprint info through.
+
+ * main.h, import.c (import_one): Optionally return the fingerprint
+ of the key being imported.
+ (import_keys_internal, import_keys_stream, import): Change all
+ callers.
+
+2006-03-12 David Shaw <dshaw@jabberwocky.com>
+
+ * sig-check.c (signature_check2): Print the backsig warning when
+ there is no backsig present. Give a URL for more information.
+
+ * keyedit.c (menu_backsign): Small tweak to work properly with
+ keys originally generated with older GnuPGs that included comments
+ in the secret keys.
+
+2006-03-10 Werner Koch <wk@g10code.com>
+
+ * card-util.c (get_manufacturer): Added Vendor 3
+
+2006-03-09 David Shaw <dshaw@jabberwocky.com>
+
+ * build-packet.c (string_to_notation): Add ability to indicate a
+ notation to be deleted with a '-' prefix.
+
+ * keyedit.c (menu_set_notation): Use it here to allow deleting a
+ notation marked with '-'. This works with either "-notation" or
+ "-notation=value".
+
+2006-03-08 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (menu_set_notation): New function to set notations on
+ self-signatures.
+ (keyedit_menu): Call it here.
+ (tty_print_notations): Helper.
+ (show_prefs): Show notations in "showpref".
+
+ * mainproc.c (get_pka_address)
+ * keylist.c (show_notation): Remove
+ duplicate code by using notation functions.
+
+ * packet.h, build-packet.c (sig_to_notation)
+ * keygen.c (keygen_add_notations): Provide printable text for
+ non-human-readable notation values.
+
+ * packet.h, build-packet.c (sig_to_notation)
+ * keygen.c (keygen_add_notations): Tweak to handle non-human-readable
+ notation values.
+
+ * options.h, sign.c (mk_notation_policy_etc)
+ * gpg.c (add_notation_data): Use it here for the various notation
+ commands.
+
+ * packet.h, main.h, keygen.c (keygen_add_notations)
+ * build-packet.c (string_to_notation, sig_to_notation)
+ (free_notation): New "one stop shopping" functions to handle
+ notations and start removing some code duplication.
+
+2006-03-08 Werner Koch <wk@g10code.com>
+
+ * mainproc.c (do_check_sig): Use log_error for standalone revocations.
+
+2006-03-07 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, mainproc.c (check_sig_and_print), gpg.c (main):
+ pka-lookups, not pka-lookup.
+
+ * options.h, gpg.c (main), keyedit.c [cmds], sig-check.c
+ (signature_check2): Rename "backsign" to "cross-certify" as a more
+ accurate name.
+
+ * options.h, gpg.c (main, parse_trust_model), pkclist.c
+ (check_signatures_trust), mainproc.c (check_sig_and_print,
+ pka_uri_from_sig), trustdb.c (init_trustdb): Some tweaks to PKA so
+ that it is a verify-option now.
+
+2006-03-07 Werner Koch <wk@g10code.com>
+
+ * mainproc.c (proc_signature_packets): Return any_sig_seen to caller.
+ (check_sig_and_print): Option to partly allow the old behaviour.
+ * gpg.c: New option --allow-multisig-verification.
+
+2006-03-06 David Shaw <dshaw@jabberwocky.com>
+
+ * sign.c (make_keysig_packet): Don't use MD5 for a RSA_S key as
+ that is not a PGP 2.x algorithm.
+
+ * mainproc.c (proc_compressed): "Uncompressed" is not a valid
+ compression algorithm.
+
+2006-03-06 Werner Koch <wk@g10code.com>
+
+ * mainproc.c (check_sig_and_print): Made the composition test more
+ tight. This is due to another bug report by Tavis Ormandy.
+ (add_onepass_sig): Simplified.
+
+2006-03-05 Werner Koch <wk@g10code.com>
+
+ * plaintext.c (handle_plaintext): Replace assert by explict error
+ conflict message. Reported by Tavis Ormandy.
+
+2006-03-02 Werner Koch <wk@g10code.com>
+
+ * cardglue.c (check_card_serialno): Don't ask in batch mode.
+
+2006-03-01 David Shaw <dshaw@jabberwocky.com>
+
+ * getkey.c (parse_auto_key_locate): Error if the user selects
+ "cert" or "pka" when those features are disabled.
+
+ * misc.c (has_invalid_email_chars): Fix some C syntax that broke
+ the compilers on SGI IRIX MIPS and Compaq/DEC OSF/1 Alpha. Noted
+ by Nelson H. F. Beebe.
+
+2006-02-27 David Shaw <dshaw@jabberwocky.com>
+
+ * options.skel: Document auto-key-locate and give a pointer to
+ Simon Josefsson's page for CERT.
+
+2006-02-24 David Shaw <dshaw@jabberwocky.com>
+
+ * keydb.h, getkey.c (release_akl), gpg.c (main): Add
+ --no-auto-key-locate.
+
+ * options.h, gpg.c (main): Keep track of each keyserver registered
+ so we can match on them later.
+
+ * keyserver-internal.h, keyserver.c (cmp_keyserver_spec,
+ keyserver_match), gpgv.c: New. Find a keyserver that matches ours
+ and return its spec.
+
+ * getkey.c (get_pubkey_byname): Use it here to get the
+ per-keyserver options from an earlier keyserver.
+
+2006-02-23 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (parse_keyserver_options): Only change max_cert if
+ it is used.
+
+ * options.c, gpg.c (main), keyserver.c (keyserver_spawn): No
+ special treatment of include-revoked, include-subkeys, and
+ try-dns-srv. These are keyserver features, and GPG shouldn't get
+ involved here.
+
+ * keyserver.c (parse_keyserver_uri, add_canonical_option): Always
+ append options to the list, as ordering may be significant to the
+ user.
+
+ * gpg.c (add_notation_data): Fix reversed logic for isascii check
+ when adding notations. Noted by Christian Biere.
+
+ * options.h, keyserver.c (add_canonical_option): New.
+ (parse_keyserver_options): Moved from here.
+ (parse_keyserver_uri): Use it here so each keyserver can have some
+ private options in addition to the main keyserver-options
+ (e.g. per-keyserver auth).
+
+2006-02-22 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, keyserver-internal.h, keyserver.c
+ (keyserver_import_name), getkey.c (free_akl,
+ parse_auto_key_locate, get_pubkey_byname): The obvious next step:
+ allow arbitrary keyservers in the auto-key-locate list.
+
+ * options.h, keyserver.c (parse_keyserver_options): Remove
+ auto-cert-retrieve as it is no longer meaningful. Add
+ max-cert-size to allow users to pick a max key size retrieved via
+ CERT.
+
+ * options.h, gpg.c (main), mainproc.c (check_sig_and_print),
+ keyserver.c (keyserver_opts): Rename auto-pka-retrieve to
+ honor-pka-record to be consistent with honor-keyserver-url.
+
+ * options.h, keydb.h, g10.c (main), getkey.c
+ (parse_auto_key_locate): Parse a list of key access methods.
+ (get_pubkey_byname): Walk the list here to try and retrieve keys
+ we don't have locally.
+
+2006-02-21 David Shaw <dshaw@jabberwocky.com>
+
+ * getkey.c (get_pubkey_byname): Fix minor security problem with
+ PKA when importing at -r time. The URL in the PKA record may
+ point to a key put in by an attacker. Fix is to use the
+ fingerprint from the PKA record as the recipient. This ensures
+ that the PKA record is followed.
+
+ * keyserver-internal.h, keyserver.c (keyserver_import_pka): Return
+ the fingerprint we requested.
+
+ * gpgv.c: Stub keyserver_import_ldap.
+
+ * keyserver-internal.h, keyserver.c (keyserver_import_ldap):
+ Import using the PGP Universal trick of asking
+ ldap://keys.(maildomain) for the key.
+
+2006-02-20 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (parse_keyserver_uri): Include the scheme in the uri
+ even when we've assumed "hkp" when there was no scheme.
+
+2006-02-20 Werner Koch <wk@g10code.com>
+
+ * apdu.c (open_pcsc_reader): As a precaution set LIST to NULL
+ after free.
+
+2006-02-14 Werner Koch <wk@gnupg.org>
+
+ * verify.c (verify_signatures): Print warning also for NO_DATA.
+
+ * mainproc.c (struct mainproc_context): New field any_sig_seen.
+ (add_signature): Set it.
+ (proc_signature_packets): Test and return NO_DATA.
+
+2006-02-09 Werner Koch <wk@g10code.com>
+
+ * gpg.c (main) <oLockNever>: Disable random locking.
+
+2006-02-06 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c, ccid-driver.h: Updated from GnuPG 1.9. Changes:
+ * ccid-driver.h (CCID_DRIVER_ERR_NO_KEYPAD): New.
+ * ccid-driver.c (send_escape_cmd): New args RESULT, RESULTLEN and
+ RESULTMAX. Changed all callers.
+ (ccid_transceive_escape): New.
+ * ccid-driver.c (special_transport): New
+ (ccid_open_reader, do_close_reader, ccid_shutdown_reader)
+ (bulk_out, bulk_in): Add support for CardMan 4040 reader.
+ * ccid-driver.c (scan_or_find_devices): Factored most code out to
+ (scan_or_find_usb_device): .. new.
+ (make_reader_id): Fixed vendor mask.
+
+2006-01-24 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (parse_keyserver_uri): If there is a path present,
+ set the direct_uri flag so the right keyserver helper is run.
+
+2006-01-22 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_spawn): Include the EXEEXT so we can find
+ keyserver helpers on systems that use extensions.
+
+ * misc.c (path_access) [HAVE_DRIVE_LETTERS]: Do the right thing
+ with drive letter systems.
+
+2006-01-17 David Shaw <dshaw@jabberwocky.com>
+
+ * keydb.h, passphrase.c (next_to_last_passphrase): New. "Touch" a
+ passphrase as if it was used (move from next_pw to last_pw).
+
+ * pubkey-enc.c (get_session_key): Use it here to handle the case
+ where a passphrase happens to be correct for a secret key, but yet
+ that key isn't the anonymous recipient (i.e. the secret key could
+ be decrypted, but not the session key). This also handles the
+ case where a secret key is located on a card and a secret key with
+ no passphrase. Note this does not fix bug 594 (anonymous
+ recipients on smartcard do not work) - it just prevents the
+ anonymous search from stopping when the card is encountered.
+
+2006-01-07 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_refresh): Fix problem when more than one
+ key in a refresh batch has a preferred keyserver set. Noted by
+ Nicolas Rachinsky.
+
+2006-01-01 David Shaw <dshaw@jabberwocky.com>
+
+ * mainproc.c (check_sig_and_print), keyserver.c
+ (keyserver_import_pka), card-util.c (fetch_url): Always require a
+ scheme:// for keyserver URLs except when used as part of the
+ --keyserver command for backwards compatibility.
+
+ * sign.c (write_signature_packets): Lost a digest_algo line.
+
+ * sign.c (hash_for): Add code to detect if the sk lives on a smart
+ card. If it does, only allow 160-bit hashes, a la DSA. This
+ involves passing the *sk in, so change all callers. This is
+ correct for today, given the current 160-bit q in DSA, and the
+ current SHA-1/RIPEMD160 support in the openpgp card. It will
+ almost certainly need changing down the road.
+
+ * app-openpgp.c (do_sign): Give user error if hash algorithm is
+ not supported by the card.
+
+2005-12-23 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_import_pka): New. Moved from
+ getkey.c:get_pubkey_byname which was getting crowded.
+
+ * keyserver.c (keyserver_import_cert): Import a key found in DNS
+ via CERT records. Can handle both the PGP (actual key) and IPGP
+ (URL) CERT types.
+
+ * getkey.c (get_pubkey_byname): Call them both here.
+
+ * options.h, keyserver.c (parse_keyserver_options): Add
+ "auto-cert-retrieve" option with optional max size argument.
+
+ * gpgv.c: Stubs.
+
+ * keyserver-internal.h, keyserver.c (keyserver_spawn,
+ keyserver_work, keygerver_getname): New keyserver_getname function
+ to fetch keys by name.
+
+ * getkey.c (get_pubkey_byname): Call it here to enable locating
+ keys by full mailbox from a keyserver a la PKA. Try PKA first,
+ though, as it is likely to be faster.
+
+2005-12-20 Werner Koch <wk@g10code.com>
+
+ * gpg.c: New option --allow-pka-lookup.
+ (parse_trust_model): Add "+pka" variants.
+ (main): Make KEYSERVER_AUTO_PKA_RETRIEVE teh default.
+ * options.h (opt): New fields PKA_TRUST_INCREASE and
+ ALLOW_PKA_LOOKUP.
+ * status.h (STATUS_PKA_TRUST_BAD, STATUS_PKA_TRUST_GOOD): New.
+ * pkclist.c (check_signatures_trust): Increase trust due to valid
+ PKA only if that new option has been set. Issue new status lines.
+ * trustdb.c (init_trustdb): Print info if this option is active.
+ * getkey.c (get_pubkey_byname): Honor allow-pka-lookup.
+ * mainproc.c (pka_uri_from_sig): Ditto.
+
+ * trustdb.c (validate_keys): Print no "ultimately trusted keys
+ found" only in non-quiet mode.
+
+2005-12-19 David Shaw <dshaw@jabberwocky.com>
+
+ * getkey.c (merge_selfsigs_main): All primary keys can certify.
+
+2005-12-18 David Shaw <dshaw@jabberwocky.com>
+
+ * gpg.c (main): Restore convert-sk-to-pk as programs rely on it.
+
+ * keyid.c (usagestr_from_pk): Remove special PUBKEY_USAGE_CERT
+ flag. It's no longer needed.
+
+2005-12-14 David Shaw <dshaw@jabberwocky.com>
+
+ * gpg.c (main): Don't default to import-options convert-sk-to-pk.
+ It causes confusing warning messages when importing a PGP-exported
+ key that contains a secret key without selfsigs followed by the
+ public key.
+
+2005-12-08 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_fetch): Switch on fast-import before we
+ --fetch-keys so we don't rebuild the trustdb after each fetch.
+
+2005-12-08 Werner Koch <wk@g10code.com>
+
+ * gpg.c (main): Check for DBCS lead byte when converting the
+ homedir. By Kazuyoshi Kakihara. Fixes PR561.
+
+ * keyserver.c (keyserver_fetch): Made strings translatable.
+
+2005-12-08 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, keyserver.c (curl_cant_handle, keyserver_spawn)
+ (keyserver_fetch): Set a flag to indicate that we're doing a direct
+ URI fetch so we can differentiate between a keyserver operation
+ and a URI fetch for protocols like LDAP that can do either.
+
+2005-12-07 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_spawn): Don't print "searching for key
+ 00000000" when fetching a URI.
+
+ * keyserver-internal.h, keyserver.c (keyserver_fetch): New. Fetch
+ an arbitrary URI using the keyserver helpers.
+
+ * gpg.c (main): Call it from here for --fetch-keys.
+
+2005-12-07 Werner Koch <wk@g10code.com>
+
+ * pkclist.c (do_we_trust): Add NOTREACHED comment.
+
+2005-11-20 David Shaw <dshaw@jabberwocky.com>
+
+ * main.h, keylist.c (print_revokers): New. Print the "rvk"
+ designated revoker record. Moved from
+ keyedit.c:show_key_with_all_names_colon.
+
+ * keylist.c (list_keyblock_colon): Use it here ...
+
+ * keyedit.c (show_key_with_all_names_colon): ... and here.
+
+2005-11-19 David Shaw <dshaw@jabberwocky.com>
+
+ * free-packet.c (copy_secret_key): Copy secret key into secure
+ memory since we may unprotect it.
+
+ * main.h, g10.c (main), revoke.c (gen_desig_revoke): Add local
+ user support so users can use -u with --desig-revoke. This
+ bypasses the interactive walk over the revocation keys.
+
+2005-11-17 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (keyedit_menu, menu_clean): Simplify clean options to
+ just "clean", and add "minimize".
+
+ * import.c (parse_import_options): Make help text match the export
+ versions of the options.
+
+ * options.h, export.c (parse_export_options, do_export_stream):
+ Reduce clean options to two: clean and minimize.
+
+ * trustdb.h, trustdb.c (clean_one_uid): New function that joins
+ uid and sig cleaning into one for a simple API outside trustdb.
+
+2005-11-13 David Shaw <dshaw@jabberwocky.com>
+
+ * armor.c (parse_header_line): A fussy bit of 2440: header lines
+ are delimited with a colon-space pair. Therefore a line such as
+ "Comment: " (with a trailing space) is actually legal, albeit not
+ particularly useful.
+
+2005-11-11 David Shaw <dshaw@jabberwocky.com>
+
+ * trustdb.h, trustdb.c (clean_key): New function to handle key
+ cleaning from one convenient place.
+
+ * options.h, import.c (parse_import_options,
+ clean_sigs_from_all_uids, import_one): Reduce clean options to
+ two: clean and minimize.
+
+ * parse-packet.c (setup_user_id): Remove.
+ (parse_user_id, parse_attribute): Just use xmalloc_clear instead.
+
+ * trustdb.c (clean_uid_from_key, clean_uids_from_key):
+ Significantly simpler implementation.
+
+2005-11-10 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (keyedit_menu, menu_clean_sigs_from_uids): Add
+ "minimize" command.
+
+ * packet.h, keyedit.c (menu_clean_uids_from_key), trustdb.c
+ (clean_uids_from_key): Fix display bug where sigs cleaned for
+ other reasons caused a uid to appear as if it had been compacted.
+
+ * packet.h: Move some flags to a bitfield. Change all callers.
+
+ * options.h, import.c (parse_import_options,
+ clean_sigs_from_all_uids, import_one): Add import-minimal option.
+ Similar to export-minimal, except it works on the way in.
+
+ * trustdb.h, trustdb.c (clean_sigs_from_uid): Add flag to remove
+ all non-selfsigs from key during cleaning. Change all callers.
+
+ * export.c (do_export_stream): Use it here for export-minimal so
+ we don't need additional minimize code in the export path.
+
+2005-11-06 David Shaw <dshaw@jabberwocky.com>
+
+ * options.skel: Add a section for --encrypt-to. This is Debian
+ bug 336211 by Javier Fernández-Sanguino Peña.
+
+2005-11-05 David Shaw <dshaw@jabberwocky.com>
+
+ * Makefile.am: Include @LIBUSB_CPPFLAGS@ in our CPPFLAGS.
+ Strictly speaking this should be only in gpg_CPPFLAGS, but then we
+ have to compile everything twice for gpg and gpgv.
+
+ * apdu.c (open_pcsc_reader): Fix double free.
+
+ * gpg.c (main) [__APPLE__]: Default the PCSC driver to the OS X
+ location. Suggested by Patty A. Hardy.
+
+2005-11-02 David Shaw <dshaw@jabberwocky.com>
+
+ * trustdb.c (clean_sigs_from_uid): Include sigs from unavailable
+ keys in the sigs that are cleaned. Suggested by Dirk Traulsen and
+ many others.
+
+2005-11-01 David Shaw <dshaw@jabberwocky.com>
+
+ * import.c (import_one): Do collapse_uids() before we do any
+ cleaning so keyserver mangled keys with doubled user IDs can be
+ properly cleaned - possibly sigs on the different user IDs cancel
+ each other out.
+
+ * import.c (parse_import_options), export.c
+ (parse_export_options): List "xxx-clean" before the longer options
+ so we don't end up with a partial match on the longer options.
+
+ * trustdb.c (clean_uids_from_key): Return proper number of cleaned
+ user IDs. Don't count user IDs as cleaned unless we actually
+ delete something.
+
+2005-10-27 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (menu_addrevoker), getkey.c (finish_lookup): Fix
+ problem with adding a cert-only designated revoker. Code was
+ looking for a key with sign ability, and not cert ability. Noted
+ by Timo Schulz.
+
+2005-10-27 Werner Koch <wk@g10code.com>
+
+ * gpg.c [__CYGWIN__]: Set default driver to winscard.dll.
+
+ * apdu.c, apdu.h: Updated from gnupg 1.9. Changes are:
+ * apdu.c [__CYGWIN__]: Make cygwin environment similar to _WIN32.
+ Suggested by John P. Clizbe.
+ * apdu.h (SW_HOST_NO_KEYPAD): New.
+ * apdu.c (host_sw_string): Support new code.
+ (reader_table_s): New field CHECK_KEYPAD.
+ (new_reader_slot, open_ct_reader, open_pcsc_reader)
+ (open_ccid_reader, open_rapdu_reader): Initialize it.
+ (check_ccid_keypad): New.
+ (apdu_check_keypad): New.
+ (apdu_send_le): Factored all code out to ...
+ (send_le): .. new. Takes an additional arg; changed all callers
+ of the orginal function to use this one with a NULL for the new
+ arg.
+ (apdu_send_simple_kp): New.
+ (ct_send_apdu, pcsc_send_apdu, my_rapdu_send_apdu)
+ (send_apdu_ccid): New arg PININFO.
+ (send_apdu_ccid): Use the new arg.
+
+2005-10-26 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (proc_parameter_file): Default key and subkey usage
+ flags to algo capabilities if parameter file doesn't specify them.
+ Noted by Timo Schulz.
+
+2005-10-18 Werner Koch <wk@g10code.com>
+
+ * cardglue.c (pin_cb): Fixed prompt for repeated PIN. Return
+ G10ERR_CANCELED and not just -1.
+ (status_sc_op_failure): New. Use it where we issue that status.
+ (pin_cb): Append serial number to the need-pin status message.
+ (agent_scd_change_pin): Add arg SERIALNO. Changed all callers.
+ (agent_scd_writekey): Ditto.
+ (agent_scd_setattr): Ditto.
+ (agent_scd_genkey): Ditto.
+ (agent_scd_checkpin): Pass serialno to the pin_cb.
+
+ * keygen.c (parse_expire_string): Allow setting the expire
+ interval using a "seconds=<n>" syntax. This is useful for
+ debugging.
+
+2005-10-17 Werner Koch <wk@g10code.com>
+
+ * 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 <dshaw@jabberwocky.com>
+
+ * keyedit.c (keyedit_menu, menu_backsign): New "backsign" command
+ to add 0x19 backsigs to old keys that don't have them.
+
+ * misc.c (parse_options): Fix build warning.
+
+ * main.h, keygen.c (make_backsig): Make public.
+
+2005-10-12 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, getkey.c (merge_selfsigs_subkey), gpg.c (main),
+ sig-check.c (signature_check2): Add --require-backsigs and
+ --no-require-backsigs. Currently defaults to
+ --no-require-backsigs.
+
+2005-10-11 David Shaw <dshaw@jabberwocky.com>
+
+ * getkey.c (merge_selfsigs_subkey), sig-check.c
+ (signature_check2), keygen.c (make_backsig): Did some backsig
+ interop testing with the PGP folks. All is well, so I'm turning
+ generation of backsigs on for new keys. Checking for backsigs on
+ verification is still off.
+
+2005-10-05 Werner Koch <wk@g10code.com>
+
+ * g10.c: Renamed to ..
+ * gpg.c: ..this.
+ * Makefile.am: Adjusted accordingly.
+
+2005-09-22 Werner Koch <wk@g10code.com>
+
+ * sign.c (write_plaintext_packet): Don't print an empty file
+ warning if the file is actually too large.
+ * encode.c (encode_simple,encode_crypt): Ditto.
+ * progress.c (handle_progress): Adjusted for iobuf_get_filelength
+ change.
+ * photoid.c (generate_photo_id): Ditto.
+
+2005-09-20 Werner Koch <wk@g10code.com>
+
+ * mainproc.c (proc_symkey_enc): Take care of a canceled passphrase
+ prompt.
+
+2005-09-19 David Shaw <dshaw@jabberwocky.com>
+
+ * keylist.c (reorder_keyblock, do_reorder_keyblock): Reorder
+ attribute IDs as well as regular text IDs.
+
+ * plaintext.c (ask_for_detached_datafile): Use make_filename() on
+ filename so tilde expansion works.
+
+2005-09-14 David Shaw <dshaw@jabberwocky.com>
+
+ * main.h, misc.c (parse_options): Add the ability to have help
+ strings in xxx-options commands.
+
+ * keyserver.c (keyserver_opts), import.c (parse_import_options),
+ export.c (parse_export_options), g10.c (parse_list_options, main):
+ Add help strings to xxx-options.
+
+2005-09-10 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (show_names): Moved name display code out from
+ show_key_with_all_names.
+ (keyedit_menu): Call it here for pref and showpref so they can
+ show only the selected user ID. Suggested by Timo Schulz.
+
+2005-09-07 Werner Koch <wk@g10code.com>
+
+ * cardglue.h (GPG_ERR_TOO_LARGE): New.
+
+ * apdu.c, apdu.h, iso7816.c, iso7816.h
+ * ccid-driver.c, ccid-driver.h: Updated from GnuPG 1.9 source.
+ Changes are:
+ * iso7816.c (iso7816_select_path): New.
+ * iso7816.c (iso7816_read_binary): Use Le=0 when reading all
+ data. Handle 6C00 error and take 6B00 as indication for EOF.
+ * apdu.h (SW_EXACT_LENGTH_P): New.
+ * apdu.c (new_reader_slot, reset_pcsc_reader, pcsc_get_status)
+ (open_pcsc_reader): Set new reader state IS_T0.
+ (apdu_send_le): When doing T=0 make sure not to send Lc and Le.
+ Problem reported by Carl Meijer.
+ (apdu_send_direct): Initialize RESULTLEN.
+
+ * misc.c (parse_options): Allow meta option "help" to list all
+ options and to exit the program.
+
+2005-09-02 David Shaw <dshaw@jabberwocky.com>
+
+ * parse-packet.c (enum_sig_subpkt, parse_signature,
+ parse_attribute_subpkts): Make a number of warnings verbose items.
+ These fire on many slightly mangled keys in the field, so the
+ warning is becoming burdensome.
+
+2005-09-01 David Shaw <dshaw@jabberwocky.com>
+
+ * photoid.h, photoid.c (generate_photo_id): Allow passing in a
+ suggested filename.
+
+ * keyedit.c (keyedit_menu, menu_adduid): Call it here so "addphoto
+ filename" works.
+
+2005-08-31 David Shaw <dshaw@jabberwocky.com>
+
+ * photoid.c (generate_photo_id): Enable readline completion and
+ tilde expansion for the JPEG prompt.
+
+2005-08-30 Werner Koch <wk@g10code.com>
+
+ * passphrase.c (agent_open): Print a warning and not an error in
+ case of a missing agent. Should fix Debian bug #325578.
+
+2005-08-26 David Shaw <dshaw@jabberwocky.com>
+
+ * misc.c (openpgp_pk_algo_usage): Default to allowing CERT for
+ signing algorithms.
+
+ * keyedit.c (sign_uids): Don't request a signing key to make a
+ certification.
+
+ * keygen.c (do_add_key_flags): Force the certify flag on for all
+ primary keys, as the spec requires primary keys must be able to
+ certify (if nothing else, which key is going to issue the user ID
+ signature?)
+ (print_key_flags): Show certify flag.
+ (ask_key_flags, ask_algo): Don't allow setting the C flag for
+ subkeys.
+
+ * keyid.c (usagestr_from_pk), getkey.c (parse_key_usage):
+ Distinguish between a sign/certify key and a certify-only key.
+
+ * keyedit.c (ask_revoke_sig): Add a revsig --with-colons mode.
+ Suggested by Michael Schierl.
+
+2005-08-21 David Shaw <dshaw@jabberwocky.com>
+
+ * Makefile.am: No need to link with curl any longer.
+
+ * main.h, misc.c (path_access): New. Same as access() but does a
+ PATH search like execlp.
+
+ * keyserver.c (curl_can_handle): Removed. Replaced by...
+ (curl_cant_handle): We are now relying on curl as the handler of
+ last resort. This is necessary because PGP LDAP and curl LDAP are
+ apples and oranges.
+ (keyserver_typemap): Only test for ldap and ldaps.
+ (keyserver_spawn): If a given handler is unusable (as determined
+ by path_access()) then try gpgkeys_curl.
+
+ * exec.h, exec.c (make_tempdir, expand_args, exec_write,
+ exec_read): Minor cleanup to use bitfield flags instead of a bunch
+ of integers.
+
+2005-08-20 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): Add aliases sign-with->local-user and
+ user->recipient to make switching from PGP command line to GPG
+ easier.
+
+2005-08-19 David Shaw <dshaw@jabberwocky.com>
+
+ * options.skel: Remove the surfnet LDAP keyserver from the list of
+ samples since it is being shut down.
+
+ * getkey.c (classify_user_id): Disable the '.' and '+' search
+ modes since they aren't supported yet.
+
+2005-08-05 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main), passphrase.c (set_passphrase_from_string): New
+ --passphrase command line option. Only useful in very special
+ circumstances.
+
+2005-08-05 Werner Koch <wk@g10code.com>
+
+ * gpgv.c (keyserver_import_fprint): New stub.
+
+ * keygen.c (ask_user_id): Moved email checking code out to ..
+ * misc.c (is_valid_mailbox): .. new.
+ * mainproc.c (get_pka_address): Use it here.
+ * getkey.c (get_pubkey_byname): Add falback to auto-retrieve a key
+ via the PKA mechanism.
+
+ * options.h (KEYSERVER_AUTO_PKA_RETRIEVE): New.
+ * keyserver.c (keyserver_opts): Ditto.
+ * mainproc.c (check_sig_and_print): Use it here to retrieve keys
+ from a PKA DNS record.
+
+ * pkclist.c (build_pk_list): Add comments to this function;
+ re-indented it.
+
+2005-08-04 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (proc_parameter_file): Sanity check items in keygen
+ batch file. Noted by Michael Schierl.
+
+ * pkclist.c (do_edit_ownertrust): Don't allow ownertrust level 0.
+ Noted by Michael Schierl.
+
+ * keygen.c (write_keyblock): Don't try and build deleted kbnodes
+ since we start our tree with one.
+
+2005-08-04 Werner Koch <wk@g10code.com>
+
+ * export.c (do_export_stream): Skip on-card keys when only subkeys
+ are to be exported. It does not make sense to replace the on-card
+ key stub by a no-key stub.
+
+ * revoke.c (gen_revoke): Check for non-online keys.
+
+ * seckey-cert.c (is_secret_key_protected): Return -3 for
+ non-online key stubs. The old code assumes that a protection
+ algorithm is still set but in some cases this one is 0 and thus it
+ won't be possible to decide whether it is unprotected or
+ protected.
+
+2005-07-28 Werner Koch <wk@g10code.com>
+
+ * Makefile.am (other_libs): Add SRVLIBS.
+
+ * parse-packet.c (can_handle_critical_notation): We know about
+ pka-address@gnupg.org.
+ * packet.h (PKT_signature): New fields PKA_INFO and PKA_TRIED.
+ (pka_info_t): New.
+ * free-packet.c (cp_pka_info): New.
+ (free_seckey_enc, copy_signature): Support new fields.
+ * mainproc.c (get_pka_address, pka_uri_from_sig): New.
+ (check_sig_and_print): Try to get the keyserver from the PKA
+ record.
+ * pkclist.c (check_signatures_trust): Adjust the trust based on
+ the PKA.
+ * gpgv.c (parse_keyserver_uri): New stub.
+
+ * keygen.c (has_invalid_email_chars): Moved to ..
+ * misc.c (has_invalid_email_chars): .. here and made global.
+
+2005-07-27 Werner Koch <wk@g10code.com>
+
+ * export.c (do_export_stream): Make two strings translatable.
+
+2005-07-26 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_typemap): Special-case LDAP since curl
+ will report that it can handle it, and we don't want it to.
+
+2005-07-26 Werner Koch <wk@g10code.com>
+
+ * passphrase.c (agent_get_passphrase): Make sure to release the
+ saved codeset.
+ (agent_open): Add arg ORIG_CODESET and switch back to it in case
+ of error. Changed all callers.
+
+2005-07-22 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (sign_uids): Don't prompt for setting signature expiry
+ to match key expiry unless --ask-cert-expire is set. Suggested by
+ Peter Palfrader.
+
+2005-07-22 Werner Koch <wk@g10code.com>
+
+ * g10.c, options.h: New option --exit-on-status-write-error.
+ * status.c (write_status_text): Make use of this option.
+
+2005-07-22 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main): Removed option --no-interactive-selection.
+ * keyedit.c (keyedit_menu): Use --interactive to enable the uid
+ walking when signing a key with no uids specified to sign.
+
+ * keylist.c (list_keyblock_print): Fix silly typo. Noted by Greg
+ Sabino Mullane.
+
+2005-07-20 Werner Koch <wk@g10code.com>
+
+ * openfile.c (open_outfile): Disable FD caching for created files.
+ * encode.c (encode_simple, encode_crypt): Disable FD caching for
+ input files.
+ * verify.c (verify_one_file): Ditto.
+ * decrypt.c (decrypt_messages): Ditto. This is bug #479.
+
+ * misc.c (get_libexecdir) [W32]: Changed to return the value of
+ program used to create the process.
+ * keyserver.c (keyserver_spawn) [DISABLE_KEYSERVER_PATH]: Don't
+ change the exec-path at all.
+
+2005-07-20 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (curl_can_handle): New. Do a runtime check against
+ libcurl to see if it can handle a particular protocol.
+ (keyserver_typemap): Call it here.
+
+ * Makefile.am: Pull in libcurl for curl_version_info() if used.
+
+2005-07-19 Werner Koch <wk@g10code.com>
+
+ * g10.c, options.h: New option --limit-card-insert-tries.
+ * cardglue.c (open_card): Use it.
+
+ * export.c (parse_export_options): New option
+ export-reset-subkey-passwd.
+ (do_export_stream): Implement it.
+
+ * misc.c (get_libexecdir): New.
+ * keyserver.c (keyserver_spawn): Use it
+
+2005-07-18 Werner Koch <wk@g10code.com>
+
+ * tdbio.c (open_db): Check for EROFS. Suggested by Bryce Nichols.
+
+2005-07-08 David Shaw <dshaw@jabberwocky.com>
+
+ * trustdb.c (clean_uids_from_key): Don't keep a valid selfsig
+ around when compacting a uid. There is no reason to make an
+ attacker's job easier - this way they only have a revocation which
+ is useless in bringing the uid back.
+
+ * keydb.h, kbnode.c (undelete_kbnode): Removed. No longer needed.
+
+ * import.c (chk_self_sigs): Allow a uid revocation to be enough to
+ allow importing a particular uid (no self sig needed). This
+ allows importing compacted uids.
+
+2005-06-20 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (save_unprotected_key_to_card): Better fix for gcc4
+ warning.
+
+2005-06-20 Werner Koch <wk@g10code.com>
+
+ * g10.c, options.h: New option --no-interactive-selection.
+ * keyedit.c (keyedit_menu): Use it.
+
+2005-06-18 Werner Koch <wk@g10code.com>
+
+ * parse-packet.c (parse_signature): Use log_info for messages
+ about missing timestamp or keyid. In case we don't use that key
+ there won't be no further error and thus gpg does not need to
+ return with an error.
+
+2005-06-13 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (save_unprotected_key_to_card): Fix gcc4 warning.
+
+ * options.h, import.c (parse_import_options, import_one): Add
+ import-clean-uids option to automatically compact unusable uids
+ when importing. Like import-clean-sigs, this may nodify the local
+ keyring.
+
+ * trustdb.c (clean_uids_from_key): Only allow selfsigs to be a
+ candidate for re-inclusion.
+
+2005-06-12 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, import.c (parse_import_options,
+ clean_sigs_from_all_uids, import_one): Add import-clean-sigs
+ option to automatically clean a key when importing. Note that
+ when importing a key that is already on the local keyring, the
+ clean applies to the merged key - i.e. existing superceded or
+ invalid signatures are removed.
+
+ * getkey.c (merge_selfsigs_main, merge_selfsigs_subkey): Make sure
+ that even after keys may be merged together, we only have one
+ chosen selfsig.
+
+2005-06-09 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, import.c (parse_import_options, delete_inv_parts):
+ import-unusable-sigs is now a noop.
+
+ * options.h, export.c (do_export_stream), keyedit.c (keyedit_menu,
+ menu_clean_subkeys_from_key), trustdb.h, trustdb.c
+ (clean_subkeys_from_key): Remove subkey cleaning function. It is
+ of very limited usefulness since it cannot be used on any subkey
+ that can sign, and can only affect multiple selfsigs on
+ encryption-only subkeys.
+
+ * keydb.h, kbnode.c (undelete_kbnode): New function to undelete a
+ kbnode.
+
+ * trustdb.c (clean_uids_from_key): Further tweak the algorithm so
+ that the last good selfsig is kept when the chosen selfsig is a
+ revocation.
+
+2005-06-08 David Shaw <dshaw@jabberwocky.com>
+
+ * trustdb.c (clean_uids_from_key), keyedit.c
+ (menu_clean_uids_from_key): Tweak algorithm to preserve the last
+ selfsig which helps prevent uid resurrections.
+
+ * getkey.c (fixup_uidnode, merge_selfsigs_main): Handle both
+ expired and revoked uids in fixup_uidnode(). No need to special
+ case in merge_selfsigs_main(). This also means that an expired
+ uid will have its selfsig tagged with chosen_selfsig.
+
+2005-06-07 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), export.c (parse_export_options,
+ do_export_stream): Add export-options export-clean-sigs,
+ export-clean-uids, export-clean-subkeys, and export-clean which is
+ all of the above. Export-minimal is the same except it also
+ removes all non-selfsigs. export-unusable-sigs is now a noop.
+
+2005-06-06 Werner Koch <wk@g10code.com>
+
+ * cardglue.c (open_card): Emit new CARDCTRL status 5 for no reader
+ available.
+
+2005-06-02 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (do_writekey): Typo fix.
+
+ * status.c, status.h: Removed STATUS_BAD_PASSPHRASE_PIN.
+
+2005-06-01 David Shaw <dshaw@jabberwocky.com>
+
+ * signal.c [HAVE_DOSISH_SYSTEM]: Fix unused function warnings on
+ mingw32. Noted by Joe Vender.
+
+ * passphrase.c [_WIN32]: Remove unused variables.
+
+2005-05-31 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (menu_clean_uids_from_key,
+ menu_clean_subkeys_from_key), trustdb.c (clean_uids_from_key,
+ clean_subkeys_from_key): Fix mingw32 build warnings. Noted by Joe
+ Vender.
+
+2005-05-31 Werner Koch <wk@g10code.com>
+
+ * keydb.h [!ENABLE_AGENT_SUPPORT]: Define dummy types.
+
+ * cardglue.c (assuan_strerror, assuan_transact): Dummy functions
+ if not build with agent support.
+
+ * armor.c (check_input): Don't bail out on invalid header lines
+ unless in struict rfc2440 mode. Suggested by Richard Patterson.
+
+2005-05-30 Werner Koch <wk@g10code.com>
+
+ * tlv.c: Add hack to compile without gpg-error.h.
+
+2005-05-30 David Shaw <dshaw@jabberwocky.com>
+
+ * trustdb.h, trustdb.c (clean_subkeys_from_key): New. Walk
+ through the subkeys on a key, and mark any that aren't usable for
+ deletion. Note that a signing subkey is never marked for deletion
+ since these keys are still useful after expiration or revocation.
+
+ * keyedit.c (menu_clean_subkeys_from_key): New function to call
+ clean_subkeys_from_key() on a key. Note that the strings here are
+ not marked for translation yet. The UI is still in flux, and
+ there is no point in annoying the translators twice.
+ (keyedit_menu): Call it here as part of the "clean" command.
+
+2005-05-29 David Shaw <dshaw@jabberwocky.com>
+
+ * trustdb.h, trustdb.c (clean_uids_from_key): New. Walk through
+ the user IDs on a key, and mark any that aren't valid for
+ deletion.
+
+ * keyedit.c (menu_clean_uids_from_key): New function to call
+ clean_uids_from_key() on a key.
+ (keyedit_menu): Call it from here as part of the "clean" command.
+
+2005-05-26 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): Default {export|import}-unusable-sigs to off until
+ the "clean" UI can be finished.
+
+2005-05-24 Werner Koch <wk@g10code.com>
+
+ * passphrase.c (ask_passphrase): Unescape the description string.
+ * cardglue.c (unescape_status_string): Removed. Changed all
+ caller to use ...
+ * misc.c (unescape_percent_string): New.
+
+ * g10.c (add_notation_data): Check number of at-signs.
+
+2005-05-23 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c, app-common.h: Again updated from gnupg 1.9 CVS.
+
+ * cardglue.c (open_card): Check USE_AGENT.
+ (agent_scd_checkpin): Implemented Assuan part.
+ (agent_scd_change_pin): Ditto.
+
+ * g10.c (main): Option --debug-ccid-driver may now be given
+ several times increase the debug level.
+
+ * ccid-driver.c (parse_ccid_descriptor): Mark SCR335 FW version
+ 5.14 as good.
+ (do_close_reader): Never do a reset. The caller should instead
+ make sure that the reader has been closed properly. The new retry
+ code in ccid_slot_status will make sure that the readersatrts up
+ fine even if the last process didn't closed the USB connection
+ properly.
+ (ccid_get_atr): For certain readers try switching to ISO mode.
+ Thanks to Ludovic Rousseau for this hint and the magic numbers.
+ (print_command_failed): New.
+ (bulk_in): Use it here. Add new arg NO_DEBUG.
+ (ccid_slot_status): Disabled debugging.
+
+2005-05-21 Werner Koch <wk@g10code.com>
+
+ * cardglue.c (send_status_info): Make CTRL optional.
+ (agent_scd_writekey, inq_writekey_parms): New.
+ (agent_openpgp_storekey): Removed.
+ * cardglue.h: Add a few more error code mappings.
+ * keygen.c (copy_mpi): Removed.
+ (save_unprotected_key_to_card): Changed to use agent_scd_writekey.
+ * app-common.h, app-openpgp.c, tlv.c, tlv.h: Updated from newer
+ version in gnupg 1.9 CVS.
+
+2005-05-20 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (ccid_transceive): Arghhh. The seqno is another
+ bit in the R-block than in the I block, this was wrong at one
+ place. Fixes bug #419 and hopefully several others.
+
+2005-05-19 Werner Koch <wk@g10code.com>
+
+ * app-common.h, app-openpgp.c, tlv.c, tlv.h: Updated from newer
+ version in gnupg 1.9 CVS.
+
+2005-05-18 Werner Koch <wk@g10code.com>
+
+ * passphrase.c (agent_open): Made global and add arg TRY.
+ (agent_close): Made global.
+
+ * app-common.h (app_t): Add a field to store the Assuan context.
+
+2005-05-13 David Shaw <dshaw@jabberwocky.com>
+
+ * build-packet.c (do_comment): Removed.
+ (build_packet): Ignore comment packets.
+
+ * export.c (do_export_stream): Don't export comment packets any
+ longer.
+
+ * options.h, g10.c (main): Remove --sk-comments and
+ --no-sk-comments options, and replace with no-op.
+
+2005-05-11 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (write_selfsigs): Rename from write_selfsig. Write the
+ same selfsig into both the pk and sk, so that someone importing
+ their sk (which will get an autoconvert to the pk) won't end up
+ with two selfsigs.
+ (do_generate_keypair): Call it from here.
+
+ * parse-packet.c (can_handle_critical_notation): New. Check for
+ particular notation tags that we will accept when critical.
+ Currently, that's only preferred-email-encoding@pgp.com, since we
+ know how to handle it (pass it through to a mail program).
+ (can_handle_critical): Call it from here.
+ (parse_one_sig_subpkt): Sanity check that notations are
+ well-formed in that the internal lengths add up to the size of the
+ subpacket.
+
+2005-05-07 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (do_close_reader): Don't do a reset before close.
+ Some folks reported that it makes the SCR335 hang less often.
+ Look at the source on how to re-enable it.
+
+2005-05-06 David Shaw <dshaw@jabberwocky.com>
+
+ * main.h, keygen.c (parse_expire_string, ask_expire_interval),
+ sign.c (sign_file, clearsign_file, sign_symencrypt_file), g10.c
+ (main), keyedit.c (sign_uids): Use seconds rather than days
+ internally to calculate expiration. We no longer need the
+ day-based code as we don't generate v3 keys.
+
+ * sign.c (sign_file, clearsign_file, sign_symencrypt_file): Use
+ the default sig expire value when signing in batchmode.
+
+2005-05-05 David Shaw <dshaw@jabberwocky.com>
+
+ * Makefile.am, packet.h, main.h, comment.c: Remove comment.c. We
+ don't use any of these functions any longer.
+
+ * keygen.c (start_tree): New function to "prime" a KBNODE list.
+ (do_generate_keypair): Use it here rather than creating and
+ deleting a comment packet.
+
+ * keygen.c (gen_elg, gen_dsa): Do not put public factors in secret
+ key as a comment.
+
+ * options.h, encode.c (encode_simple, encode_crypt), keygen.c
+ (do_create): Remove disabled comment packet code.
+
+ * keygen.c (keygen_set_std_prefs): Add SHA256 and BZip2 to default
+ preferences.
+
+ * options.h, g10.c (main): Add new --default-sig-expire and
+ --default-cert-expire options. Suggested by Florian Weimer.
+
+ * main.h, keygen.c (parse_expire_string, ask_expire_interval): Use
+ defaults passed in, or "0" to control what default expiration is.
+
+ * keyedit.c (sign_uids), sign.c (sign_file, clearsign_file,
+ sign_symencrypt_file): Call them here, so that default expiration
+ is used when --ask-xxxxx-expire is off.
+
+2005-05-03 Werner Koch <wk@g10code.com>
+
+ * passphrase.c (agent_get_passphrase): Add new arg CACHEID.
+ Changed all callers.
+ (ask_passphrase): Add new arg CACHEID and use it in agent mode.
+ Changed all callers.
+ (passphrase_clear_cache): New arg CACHEID. Changed all callers.
+ * cardglue.c (format_cacheid): New.
+ (pin_cb): Compute a cache ID.
+ (agent_scd_pksign, agent_scd_pkdecrypt): Use it.
+ (agent_clear_pin_cache): New.
+ * card-util.c (change_pin): Clear the PIN cache.
+ (check_pin_for_key_operation): Ditto.
+
+2005-04-24 David Shaw <dshaw@jabberwocky.com>
+
+ * trustdb.h, trustdb.c (mark_usable_uid_certs): Add flags for the
+ no-pubkey and chosen revocation cases.
+ (clean_uid): New function to clean a user ID of unusable (as
+ defined by mark_usable_uid_certs) certs.
+
+ * keyedit.c (keyedit_menu, menu_clean_uids): Call it here for new
+ "clean" command that removes unusable sigs from a key.
+
+ * trustdb.h, keyedit.c (keyedit_menu, menu_select_uid_namehash):
+ Allow specifying user ID via the namehash from --with-colons
+ --fixed-list-mode --list-keys. Suggested by Peter Palfrader.
+
+2005-04-21 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (sign_uids, keyedit_menu): When the user requests to
+ sign a key without specifying which user IDs to sign, and declines
+ to sign all user IDs, walk through the set of user IDs and prompt
+ for which to sign.
+
+ * mainproc.c (symkey_decrypt_seskey): There is no need to have an
+ extra check for a bad passphrase and/or unknown cipher algorithm
+ here. We'll fail quite happily later, and usually with a better
+ error message to boot.
+
+2005-04-20 Werner Koch <wk@g10code.com>
+
+ * sign.c (sign_file, sign_symencrypt_file): Allow for hash
+ debugging.
+
+2005-04-16 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_spawn): Free some memory.
+
+ * sign.c (hash_for): Comments.
+
+2005-04-11 Werner Koch <wk@g10code.com>
+
+ * g10.c (main, add_notation_data, add_policy_url)
+ (add_keyserver_url): Use isascii() to protect the isfoo macros and
+ to replace direct tests. Possible problems noted by Christian
+ Biere.
+ * keyserver.c (parse_keyserver_uri): Ditto.
+
+2005-04-07 Werner Koch <wk@g10code.com>
+
+ * g10.c (main): Declare --pipemode deprecated.
+ * misc.c (deprecated_command): New.
+
+ * ccid-driver.c (ccid_slot_status): Fixed debug messages.
+
+ * card-util.c (card_edit): Add command "verify". Enhanced admin
+ command to allow optional arguments "on", "off" and "verify".
+ (card_status): Print private DOs in colon mode.
+ * app-openpgp.c (do_check_pin): Add hack to allow verification of
+ CHV3.
+
+2005-04-01 Werner Koch <wk@g10code.com>
+
+ * keygen.c (keygen_set_std_prefs): Explain the chosen order of
+ AES key sizes.
+
+2005-04-01 David Shaw <dshaw@jabberwocky.com>
+
+ * mainproc.c (proc_plaintext): Properly handle SIG+LITERAL
+ (old-style PGP) signatures that use hashes other than SHA-1,
+ RIPEMD160, or MD5.
+
+2005-03-31 David Shaw <dshaw@jabberwocky.com>
+
+ * exec.h, exec.c (set_exec_path): Remove some dead code and change
+ all callers. We no longer need to append to $PATH.
+
+2005-03-31 Werner Koch <wk@g10code.com>
+
+ * passphrase.c (agent_open): Dropped support for W32 - is was
+ never actually used. Removed support for the old non-assuan
+ protocol; there has never been a matured implementation and
+ gpg-agent is now arround for quite some time. Rewritten to make
+ use of the Assuan code from ../util.
+ (gpga_protocol_codes): Removed.
+ (readn): Removed.
+ (agent_close): Simplified for use with Assuan.
+ (agent_get_passphrase, passphrase_clear_cache): Removed support
+ for old protocol. Use only with ENABLE_CARD_SUPPORT defined.
+ (agent_send_all_options): Take assuan context instead of a file
+ descriptor.
+ (agent_send_option): Likewise. Use assuan_transact.
+ * passphrase.c (writen, readaline): Removed.
+
+ * g10.c (main): Print a warning if --use-agent has been used but
+ it has not been build with support for it.
+
+ * keydb.c (keydb_add_resource): Clarify meaning of flags. Add new
+ flag 4. Use log_info for errors registering the default secret key.
+ * g10.c (main): Flag the default keyrings.
+
+2005-03-30 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_spawn): Don't mess about with the $PATH.
+ Rather, call keyserver helpers with the full path. This fixes
+ some PATH-inspired DLL problems on W32. Noted by Carlo Luciano
+ Bianco.
+
+2005-03-30 Werner Koch <wk@g10code.com>
+
+ * cardglue.c (pin_cb): Print a warning if the info string hack is
+ not there. This may happen due to typos in the translation.
+
+2005-03-22 Werner Koch <wk@g10code.com>
+
+ * misc.c (w32_shgetfolderpath) [W32]: Changed declaration of
+ function ptr. Noted by Tim Costello.
+ * apdu.c [W32]: Changed declaration of dlopened function pointers.
+
+2005-03-21 David Shaw <dshaw@jabberwocky.com>
+
+ * gpgv.c: Stubs for tty_enable_completion() &
+ tty_disable_completion().
+
+ * openfile.c (ask_outfile_name): Enable readline completion when
+ prompting for an output filename.
+
+ * plaintext.c (ask_for_detached_datafile): Enable readline
+ completion when prompting for a detached sig datafile.
+
+2005-03-21 Werner Koch <wk@g10code.com>
+
+ * keyedit.c (command_generator, keyedit_completion): Changed
+ indentation.
+ * card-util.c (command_generator, card_edit_completion): Ditto.
+
+2005-03-19 David Shaw <dshaw@jabberwocky.com>
+
+ * card-util.c (command_generator, card_edit_completion)
+ [GNUPG_MAJOR_VERSION==1 && HAVE_LIBREADLINE]: New functions to
+ enable command completion in the --card-edit menu.
+ (card_edit): Call them here.
+
+2005-03-18 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (command_generator, keyedit_completion)
+ [HAVE_LIBREADLINE]: New functions to enable command completion in
+ the --edit-key menu.
+ (keyedit_menu): Call them here.
+
+2005-03-17 David Shaw <dshaw@jabberwocky.com>
+
+ * getkey.c (get_seckey_byname2): If no explicit default key is
+ set, don't pick a disabled default. Noted by David Crick.
+
+ * Makefile.am: Calculate GNUPG_LIBEXECDIR directly. Do not
+ redefine $libexecdir.
+
+ * options.h, keyserver.c (parse_keyserver_options)
+ (keyserver_spawn): Don't treat 'verbose' and 'include-disabled' as
+ special. Just pass them through silently to the keyserver helper.
+
+2005-03-16 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (parse_ccid_descriptor): Make SCM workaround
+ reader type specific.
+ (scan_or_find_devices): Do not check the interface subclass in the
+ SPR532 kludge, as this depends on the firmware version.
+ (ccid_get_atr): Get the Slot status first. This solves the
+ problem with readers hanging on recent Linux 2.6.x.
+ (bulk_in): Add argument TIMEOUT and changed all callers to pass an
+ appropriate one. Change the standard timeout from 10 to 5 seconds.
+ (ccid_slot_status): Add a retry code with an initial short timeout.
+ (do_close_reader): Do an usb_reset before closing the reader.
+
+2005-03-14 Werner Koch <wk@g10code.com>
+
+ * card-util.c (card_status): Use isotimestamp and not the
+ localized asctimestamp to match the timezone used in the key
+ information.
+
+ * cardglue.c (pin_cb): Disable debug output.
+
+2005-03-11 Werner Koch <wk@g10code.com>
+
+ * keygen.c (gen_card_key_with_backup): Write status line with the
+ backup filename.
+
+ * status.h, status.h (STATUS_BACKUP_KEY_CREATED): New.
+
+2005-03-10 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (parse_keyserver_options): Accept honor-http-proxy
+ as an alias for http-proxy.
+
+ * delkey.c (do_delete_key, delete_keys): Fix problem with --expert
+ preventing --delete-secret-and-public-keys from deleting secret
+ keys.
+
+2005-03-10 Werner Koch <wk@g10code.com>
+
+ * keyedit.c (keyedit_menu) [W32]: Run the trustdb stale check
+ earlier.
+
+2005-03-07 Werner Koch <wk@g10code.com>
+
+ * cardglue.c (agent_scd_pkdecrypt, agent_scd_pksign)
+ (agent_scd_genkey, agent_scd_setattr, agent_scd_change_pin)
+ (agent_scd_checkpin, agent_openpgp_storekey): Make sure to send a
+ SC_OP_FAILURE after card operations which might change data.
+ * card-util.c (change_pin): Send a SC_OP_SUCCESS after a PIN has
+ been changed.
+ (change_name): Removed a debug output.
+ * status.h, status.c: New codes BAD_PASSPHRASE_PIN, SC_OP_FAILURE
+ and SC_OP_SUCCESS.
+
+2005-02-24 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (keyedit_menu): Only print the key signing hint when
+ signing from a place where it is useful (i.e. --edit-key and not
+ --sign-key).
+
+2005-02-16 Werner Koch <wk@g10code.com>
+
+ * card-util.c (fetch_url): Fetch the key from the default
+ keyserver if no URL is available.
+
+2005-02-15 Werner Koch <wk@g10code.com>
+
+ * passphrase.c (agent_get_passphrase): Don't call free_public_key
+ if PK is NULL.
+ (passphrase_clear_cache): Ditto. Removed debug output.
+ (passphrase_to_dek): Ditto.
+
+2005-02-13 Werner Koch <wk@g10code.com>
+
+ * keyedit.c (cmds): Limit code to 80 columns. Add command
+ BKUPTOCARD.
+
+2005-02-09 David Shaw <dshaw@jabberwocky.com>
+
+ * encr-data.c (decrypt_data): Use it here to turn off the "quick
+ check" bytes for PK decryptions. This is in regards to the Mister
+ and Zuccherato attack on OpenPGP CFB mode.
+
+ * mainproc.c (proc_symkey_enc): Set a flag to indicate that a
+ particular session key came from a passphrase and not a PK.
+
+2005-02-08 Werner Koch <wk@g10code.com>
+
+ * misc.c (w32_shgetfolderpath): New.
+ (default_homedir): Use it to avoid problems under Windows95.
+
+2005-02-06 David Shaw <dshaw@jabberwocky.com>
+
+ * trustdb.h, trustdb.c (trustdb_check_or_update): New. If the
+ trustdb is dirty and --interactive is set, do an --update-trustdb.
+ If not interactive, do a --check_trustdb unless
+ --no-auto-check-trustdb is set.
+
+ * import.c (import_keys_internal): Moved from here.
+
+ * keyserver.c (keyserver_refresh): Call it here after all
+ refreshing has happened so that we don't rebuild after each
+ preferred keyserver set of imports, but do one big rebuild at the
+ end. This is Debian bug #293816, noted by Kurt Roeckx.
+
+2005-02-04 David Shaw <dshaw@jabberwocky.com>
+
+ * getkey.c (merge_selfsigs_subkey): Merged away definition from
+ the backsigs code.
+
+2005-01-31 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (do_generate_keypair): Write the auth key to the card
+ before the encryption key. This is a partial workaround for a PGP
+ bug (as of this writing, all versions including 8.1), that causes
+ it to try and encrypt to the most recent subkey regardless of
+ whether that subkey is actually an encryption type. In this case,
+ the auth key is an RSA key so it succeeds.
+
+2005-01-27 David Shaw <dshaw@jabberwocky.com>
+
+ * keyid.c (keyid_from_sk, keyid_from_pk): Use 0xFFFFFFFFFFFFFFFF
+ instead of 0x0000000000000000 for the invalid key ID since
+ all-zeroes is reserved for the anonymous recipient.
+
+ * keyedit.c (change_passphrase), keygen.c (generate_subkeypair):
+ Fix a string ;)
+
+2005-01-27 Werner Koch <wk@g10code.com>
+
+ * parse-packet.c (listfp): New.
+ (set_packet_list_mode): Intialize it to stdout or stderr depending
+ on a global option. Made all printing in list mode use LISTFP.
+
+ * keygen.c (generate_subkeypair): Detect primary key on-card and
+ ask for the passphrase. Return an error if the primary key is a
+ plain stub.
+
+ * keyedit.c (change_passphrase): Don't ever change any stub key.
+ Print a note if a key consists of only stub keys. Reported by
+ Dany Nativel. These are bugs #401 and #402.
+
+2005-01-26 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (parse_ccid_descriptor): Need the CSM workaround
+ also for newer firmware versions. Need to get a list of fixed
+ firmware versions and use that.
+
+2005-01-26 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (parse_keyserver_uri): Allow RFC-2732 IPv6 [literal
+ address] syntax in keyserver URLs.
+ (keyserver_typemap): Map ftps if we are supporting it.
+
+2005-01-25 Werner Koch <wk@g10code.com>
+
+ * keygen.c (do_generate_keypair): Don't continue after an error;
+ fixed at two places. Why at all didn't I used a goto to cleanup,
+ tsss?
+
+ * app-openpgp.c (get_cached_data): New arg GET_IMMEDIATE to bypass
+ the cache. Changed all callers.
+ (get_one_do): Bypass the cache if the value would have been read
+ directly for v1.1 cards. It makes things a bit slower but only for
+ 1.0 cards and there are not that many cards out in the wild. This
+ is required to fix a caching bug when generating new keys; as a
+ side effect of the retrieval of the the C4 DO from the 6E DO the
+ chaced fingerprint will get updated to the old value and later
+ when signing the generated key the checking of the fingerprint
+ fails becuase it won't match the new one. Thanks to Moritz for
+ analyzing this problem.
+ (verify_chv3): Removed the CHV status reread logic because we
+ won't cache the C4 DO anymore.
+
+2005-01-21 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (free_keyserver_spec): Fix small leak.
+ (keyserver_typemap): Map https if we are supporting it.
+
+2005-01-20 Werner Koch <wk@g10code.com>
+
+ * cardglue.c (open_card): Issue new CARDCTRL(4) status.
+
+ * gpgv.c (tty_fprintf): New stub.
+
+ * card-util.c (card_status): Create a secret key stub on the fly
+ and print more information about a card key.
+ * import.c (pub_to_sec_keyblock, auto_create_card_key_stub): New.
+ * getkey.c (get_seckeyblock_byfprint): New.
+ * keylist.c (print_card_key_info): New.
+
+ * g10.c (i18n_init) [W32]: Pass registry key to gettext
+ initialization.
+ * gpgv.c (i18n_init) [W32]: Ditto.
+
+2005-01-18 Werner Koch <wk@g10code.com>
+
+ * misc.c (default_homedir): New. Taken from gnupg 1.9.15.
+ * g10.c (main): Use it.
+ * gpgv.c (main): Ditto.
+
+ * keylist.c (public_key_list): Do a trustdb staleness check before
+ opening the keyring.
+ (secret_key_list): Ditto.
+
+2005-01-10 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (keyedit_menu): Move command strings outside the
+ function to get ready for the readline completion code.
+
+ * passphrase.c (readline, agent_send_option, agent_open,
+ agent_get_passphrase, passphrase_clear_cache): Rename readline()
+ to readaline() to keep readline library namespace clear.
+
+2005-01-06 David Shaw <dshaw@jabberwocky.com>
+
+ * filter.h, armor.c (armor_filter): Use the eol string from the
+ armor filter context instead of hardcoding '\n' or '\r\n'. If no
+ eol string is provided, default to '\n' or '\r\n' as appropriate.
+ (is_armor_header): Trim tabs in armor header lines as well.
+
+ * keyserver.c (keyserver_spawn): Use it here to force '\n' line
+ endings since the keyserver output file gets a LF->CRLF expansion
+ on win32.
+
+2005-01-05 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): Typo.
+
+ * armor.c (is_armor_header): Allow CR and LF (not just actual
+ spaces) in an armor header line (-----BEGIN etc). This is needed
+ due to CRLF issues on win32. As before, --openpgp makes it
+ strict.
+
+2005-01-03 David Shaw <dshaw@jabberwocky.com>
+
+ * Makefile.am: Use @LIBUSB@ instead of @LIBUSB_LIBS@
+
+ * import.c (delete_inv_parts): Comments on import-unusable-sigs.
+
+2005-01-01 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, import.c (parse_import_options, delete_inv_parts):
+ Add import-unusable-sigs flag to enable importing unusable
+ (currently: expired) sigs.
+
+ * options.h, export.c (parse_export_options, do_export_stream):
+ Add export-unusable-sigs flag to enable exporting unusable
+ (currently: expired) sigs.
+
+2004-12-29 David Shaw <dshaw@jabberwocky.com>
+
+ * packet.h, getkey.c (merge_selfsigs_main, sig_to_revoke_info),
+ keyid.c (revokestr_from_pk), keyedit.c (show_key_with_all_names):
+ Show who revoked a key (either the same key or a designated
+ revoker) and when.
+
+2004-12-28 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (find_endpoint): New.
+ (scan_or_find_devices): Add new args to return endpoint info and
+ interface number.
+ (ccid_open_reader, ccid_shutdown_reader): Take care of these new
+ args.
+ (bulk_in, bulk_out): Use the correct endpoints.
+ (ccid_transceive_apdu_level): New.
+ (ccid_transceive): Divert to above.
+ (parse_ccid_descriptor): Allow APDU level exchange mode.
+ (do_close_reader): Pass the interface number to usb_release_interface.
+
+2004-12-24 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_typemap): Only map HTTP and FTP if
+ libcurl has specifically been selected to handle them.
+
+2004-12-22 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, keyserver.c (parse_keyserver_uri): Properly parse
+ auth data from URLs and pass to keyserver helpers.
+
+ * keyserver.c (keyserver_typemap): New. Map certain keyserver
+ types to a common type (e.g. ldaps -> ldap). If we are building
+ with curl, map both http and ftp to curl.
+
+ * build-packet.c (build_sig_subpkt): Only allow one preferred
+ keyserver subpacket at a time.
+
+2004-12-21 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (menu_set_keyserver_url): Make sure we only operate on
+ the chosen selfsig so we don't accidentally promote an older
+ selfsig to chosen. Discovered by Simon Josefsson and 'Todd'.
+
+ * keygen.c (ask_expire_interval): Fix typo.
+
+2004-12-20 David Shaw <dshaw@jabberwocky.com>
+
+ * keylist.c (list_keyblock_print): Secret key listings should
+ always show everything (expired UIDs, revoked subkeys, etc, etc).
+
+ * keyedit.c (keyedit_menu): Add additional help for the "sign"
+ flags.
+
+2004-12-20 Werner Koch <wk@g10code.com>
+
+ * keygen.c (ask_expire_interval): For better translations chnage 2
+ strings.
+
+ * seckey-cert.c (do_check): Handle case when checksum was okay but
+ passphrase still wrong. Roman Pavlik found such a case.
+
+2004-12-20 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (keyedit_menu): Invisible alias "passwd" as
+ "password".
+
+ * passphrase.c: Don't check for __CYGWIN__, so it is treated as a
+ unix-like system.
+
+ * options.h, g10.c (main), textfilter.c (standard): Use new option
+ --rfc2440-text to determine whether to filter "<space>\t\r\n" or
+ just "\r\n" before canonicalizing text line endings. Default to
+ "<space>\t\r\n".
+
+2004-12-19 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (keygen_get_std_prefs): Set reference count when
+ creating the temporary user ID.
+
+ * keyedit.c (keyedit_menu): Merge updpref and setpref. Keep
+ updpref as an invisible alias. Add invisible alias for revphoto.
+ Fix small memory leak when using "setpref" (not all of the uid was
+ freed).
+ (menu_revkey): Trigger a trust rebuild after revoking a key.
+ Don't allow revoking an already-revoked whole key.
+ (menu_revsubkey): Don't allow revoking an already-revoked subkey.
+
+2004-12-18 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (menu_revkey): Rename to menu_revsubkey.
+ (menu_revkey): New. Revoke a whole key.
+ (keyedit_menu): Call it here for when 'revkey' is used without any
+ subkeys selected. This is to be consistent with the other
+ functions which are "selected part if selected, whole key if not".
+
+ * signal.c: Use only HAVE_LIBREADLINE to detect readline
+ availability.
+
+ * Makefile.am: Link with readline where necessary.
+
+2004-12-17 Werner Koch <wk@g10code.com>
+
+ * passphrase.c (agent_get_passphrase): Define NREAD locally as
+ size_t or int.
+
+ * keylist.c (list_keyblock_print): Make field width an int.
+ * keyedit.c (show_key_with_all_names): Ditto.
+
+2004-12-16 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): Add --require-secmem/--no-require-secmem to cause
+ gpg to exit if it cannot lock memory. Also remove --nrsign-key
+ and --nrlsign-key since this can better be done via --edit-key.
+
+2004-12-15 David Shaw <dshaw@jabberwocky.com>
+
+ * apdu.c (apdu_send_le, apdu_send_direct), keylist.c
+ (status_one_subpacket, print_one_subpacket): Fix some compiler
+ warnings.
+
+ * g10.c (main): Fix --compression-algo to take a string argument
+ like --compress-algo.
+
+ * trustdb.c (uid_trust_string_fixed): For safety, check for a pk.
+
+2004-12-14 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (keyedit_menu): Re-remove the N_() markers.
+
+ * trustdb.c (uid_trust_string_fixed): Show uids as revoked if the
+ key is revoked.
+
+ * keyedit.c (show_key_with_all_names): Don't show validity for
+ secret key UIDs.
+
+ * keyedit.c (parse_sign_type): New. Figure out the flags (local,
+ nonrevoke, trust) for a signature.
+ (keyedit_menu): Call it here so we can mix and match flags, and
+ don't need "nrltsign", "ltsign", "tnrsign", etc, etc, etc.
+
+2004-12-14 Werner Koch <wk@g10code.com>
+
+ * passphrase.c (agent_get_passphrase): Removed debug output
+
+ * keyserver.c (keyserver_work, keyserver_spawn): Map ldaps to ldap.
+
+ * keyedit.c (keyedit_menu): Removed the N_() markers from the
+ command names.
+ * card-util.c (card_edit): Ditto.
+
+2004-12-13 Werner Koch <wk@g10code.com>
+
+ * passphrase.c (read_passphrase_from_fd): Fixed memory leak.
+ Noted by Andrei Darashenka.
+
+2004-12-11 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (parse_preferred_keyserver): Force preferred
+ keyserver subpackets to have a URI scheme specified.
+
+2004-12-10 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), textfilter.c (standard): Use --rfc2440
+ or --openpgp directly to determine the end of line hashing rule.
+
+ * trustdb.c (uid_trust_string_fixed): Show uids as expired if the
+ key is expired.
+
+2004-12-10 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (send_fprtime_if_not_null): New.
+ (do_getattr): Add KEY_TIME.
+ (do_learn_status): Print KEY_TIME.
+ * cardglue.c (learn_status_cb): Parse KEY-TIME.
+ * card-util.c (card_status): Print creation time if available.
+
+2004-12-09 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), textfilter.c (len_without_trailing_ws):
+ Removed (not used).
+ (standard): 2440 says that textmode hashes should canonicalize
+ line endings to CRLF and remove spaces and tabs. 2440bis-12 says
+ to just canonicalize to CRLF. So, we default to the 2440bis-12
+ behavior, but revert to the strict 2440 behavior if the user
+ specifies --rfc2440. In practical terms this makes no difference
+ to any signatures in the real world except for a textmode detached
+ signature.
+
+2004-12-09 Werner Koch <wk@g10code.com>
+
+ * passphrase.c (agent_get_passphrase): New args CUSTOM_PROMPT and
+ CUSTOM_DESCRIPTION. Changed all callers.
+
+ * app-openpgp.c (do_getattr, do_learn_status, do_setattr): Support
+ the new private DOs.
+ (do_change_pin): Add a "N" prefix to the strings so that the
+ callback can act accordingly for a new PIN. Unfortunately this
+ breaks existing translations but I see no wother way to overvome
+ this.
+
+ * cardglue.c (learn_status_cb): Ditto.
+ (agent_release_card_info): Ditto.
+ (struct pin_cb_info_s): Removed and changed all users.
+ (pin_cb): Reworked.
+
+ * card-util.c (card_status): Print them
+ (card_edit): New command PRIVATEDO.
+ (change_private_do): New.
+
+2004-12-09 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (ask_algo): Add a choose-your-own-capabilities option
+ for DSA.
+
+2004-12-07 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (ask_keysize): Change strings to always use %u instead
+ of hardcoding key sizes. Bump default to 2048. Bump minimum down
+ to 512, where possible, but require --expert to get there. DSA is
+ always 1024 unless --expert is given.
+
+2004-11-29 David Shaw <dshaw@jabberwocky.com>
+
+ * getkey.c (parse_key_usage): New function to parse out key usage
+ flags. Set PUBKEY_USAGE_UNKNOWN to handle flags that we don't
+ understand.
+ (fixup_uidnode, merge_selfsigs_main, merge_selfsigs_subkey): Call
+ it from here to remove duplicate code.
+
+2004-11-26 David Shaw <dshaw@jabberwocky.com>
+
+ * export.c (do_export_stream): Allow export-minimal to work with
+ secret keys, even though a non-selfsig secret key signature is
+ rare.
+
+ * options.h, export.c (parse_export_options, do_export_stream),
+ import.c (parse_import_options, import_keys_internal): Make the
+ import-options and export-options distinct since they can be mixed
+ together as part of keyserver-options.
+
+2004-11-24 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, export.c (parse_export_options, do_export_stream):
+ Add "export-minimal" option to disregard any sigs except selfsigs.
+
+ * trustdb.c (uid_trust_string_fixed): Use a string that can be
+ atoi-ed, but also has a comment for the translator.
+
+ * trustdb.h, trustdb.c (uid_trust_string_fixed): New. Return a
+ fixed-size translatable string similar to trust_value_to_string.
+ This allows for easier lining up of displays.
+
+ * keyedit.c (show_key_with_all_names), keylist.c
+ (list_keyblock_print): Use it here to print validity strings.
+
+ * gpgv.c: Stub.
+
+2004-11-18 Werner Koch <wk@g10code.com>
+
+ * g10.c (S_IRGRP) [HAVE_DOSISH_SYSTEM]: Define to 0.
+
+2004-11-17 Werner Koch <wk@g10code.com>
+
+ * g10.c (open_info_file): New.
+ (main): Unconditionally implement --status-file, --logger-file,
+ --attribute-file, --passphrase-file, --command-file. This is not
+ generally useful but easy to support and might make scripting
+ under Windows easier.
+
+2004-11-11 Werner Koch <wk@g10code.com>
+
+ * passphrase.c (readn): Fixed test against EINTR.
+
+2004-11-05 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c: Made more strings translatable.
+ (verify_chv3, do_change_pin): Add a special prefix to the prompt
+ of the Admin PIN prompts.
+ * passphrase.c (ask_passphrase): Add arg TRYAGAIN_TEXT. Changed
+ call callers.
+ * cardglue.c (pin_cb): Make use of the OPAQUE arg to pass
+ arguments to the PIN callback. Use this to implement a way to
+ check for correct PIN repetition. Changed all callers to pass an
+ opaque argument. Improved detection of Admin PIN prompts.
+
+2004-11-04 David Shaw <dshaw@jabberwocky.com>
+
+ * plaintext.c (handle_plaintext): Don't try and create a
+ zero-length filename when using --use-embedded-filename with input
+ that has no filename (clearsigned or message generated from a
+ pipe).
+
+ * encode.c (encode_simple, encode_crypt), progress.c
+ (handle_progress), sign.c (write_plaintext_packet): Fix a few
+ inconsistent calls (NULL filename means a pipe here, so don't
+ bother to check it twice).
+
+2004-11-03 David Shaw <dshaw@jabberwocky.com>
+
+ * misc.c (print_digest_algo_note): The latest 2440bis drafts
+ deprecates MD5, so give a warning.
+ (print_pubkey_algo_note, print_cipher_algo_note,
+ print_digest_algo_note): Give the algorithm name in the
+ experimental algo warning.
+
+2004-11-03 Timo Schulz <twoaday@g10code.com>
+
+ * passphrase.c (readn, writen): Use w32_strerror instead
+ of just showing the error number.
+ * misc.c [_WIN32]: Fix warning about missing prototypes.
+
+2004-10-28 David Shaw <dshaw@jabberwocky.com>
+
+ * skclist.c (build_sk_list): Don't need to warn about
+ PGP-generated Elgamal signing keys since we no longer support any
+ Elgamal signing keys.
+
+ * sign.c (sign_file, clearsign_file): Use "writing to" instead of
+ "writing to file" to match other strings.
+
+ * pkclist.c (check_signatures_trust): Fix typo. Noted by Moray
+ Allan. This is Debian bug #278708.
+
+ * passphrase.c (ask_passphrase, passphrase_to_dek): "password" ->
+ "passphrase".
+
+ * keyedit.c (show_key_with_all_names): Show designated revoker as
+ part of translatable string.
+
+2004-10-28 Werner Koch <wk@g10code.com>
+
+ * Makefile.am (other_libs): New. Also include LIBICONV. Noted by
+ Tim Mooney.
+
+2004-10-28 Werner Koch <wk@g10code.com>
+
+ * apdu.c (open_pcsc_reader): Removed bad free in error handler.
+
+2004-10-27 David Shaw <dshaw@jabberwocky.com>
+
+ * card-util.c, delkey.c, keygen.c, plaintext.c, keyedit.c,
+ passphrase.c, revoke.c: Collapse the two different "can't do that
+ in batch mode" strings into one.
+
+ * keylist.c (status_one_subpacket): New. Send the subpacket data
+ to the --status interface.
+
+ * card-util.c (card_edit): Show when admin is enabled or not.
+
+ * status.h, status.c: New STATUS_SIG_SUBPACKET type.
+
+ * build-packet.c (build_sig_subpkt): Multiple keyserver URLs are
+ allowed.
+
+ * keyring.c: Make some strings translatable.
+
+ * exec.c, sign.c: Change "can't open file" to "can't open" and
+ "can't create file" to "can't create" to be consistent with other
+ strings so we don't have to translate both.
+
+ * delkey.c, export.c, keyedit.c, pkclist.c, revoke.c, skclist.c:
+ Fix a few missed possible \"username\" quotes.
+
+2004-10-26 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (verify_chv3): The minimum length for CHV3 is
+ 8. Changed string to match the other ones.
+
+ * passphrase.c (agent_send_all_options): Try to deduce the ttyname
+ from stdin.
+
+2004-10-22 Werner Koch <wk@g10code.com>
+
+ * card-util.c (fetch_url): Disable for gnupg 1.9
+ (card_generate_subkey): Ditto.
+ (card_store_subkey): Ditto.
+
+2004-10-21 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), mainproc.c (check_sig_and_print):
+ Rename verify-option show-validity to show-uid-validity to match
+ the similar list-option.
+
+ * app-openpgp.c (verify_chv3): Fix typo.
+
+2004-10-21 Werner Koch <wk@g10code.com>
+
+ * app-common.h (app_openpgp_storekey): Add prototype.
+
+ * app-openpgp.c (do_sign): Replace asprintf by direct allocation.
+ This avoids problems with missing vasprintf implementations.
+
+ * card-util.c (generate_card_keys): Add a #warning for gnupg 1.9
+ and use the same string there.
+
+2004-10-20 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (parse_list_options): Fix non-constant initializer so we
+ can build with C89.
+
+2004-10-17 David Shaw <dshaw@jabberwocky.com>
+
+ * keylist.c (print_one_subpacket): The flags field should be hex.
+
+2004-10-17 Werner Koch <wk@g10code.com>
+
+ * passphrase.c (agent_get_passphrase): Cast UIDLEN to int. Noted
+ by Christian Cornelssen.
+
+2004-10-16 David Shaw <dshaw@jabberwocky.com>
+
+ * parse-packet.c (parse_one_sig_subpkt, enum_sig_subpkt): Don't
+ BUG() on unknown subpackets. Rather, just return them silently.
+
+2004-10-15 Werner Koch <wk@g10code.com>
+
+ * status.h (STATUS_NEED_PASSPHRASE_PIN): New.
+ * status.c (get_status_string): Added.
+ * passphrase.c (ask_passphrase): Moved status printing to ..
+ * cardglue.c (pin_cb): .. here and issue new status message.
+
+ * keyedit.c (sign_uids): Don't include the leading LF in the
+ translatable string but print them separately.
+
+ * apdu.c (apdu_open_remote_reader) [_WIN32]: We don't have ENOSYS.
+
+ * app-openpgp.c (parse_login_data): New.
+ (app_select_openpgp): Call it.
+ (do_setattr): Reparse it after change.
+
+ * pkclist.c (do_edit_ownertrust): Add a note to translators.
+ * keygen.c (ask_user_id): Ditto.
+
+ * helptext.c: Typo fix.
+
+2004-10-14 David Shaw <dshaw@jabberwocky.com>
+
+ * keylist.c (list_keyblock_print): Show the fingerprint after the
+ key, not after the first user ID.
+
+ * keyedit.c (show_key_with_all_names): Don't show validity if
+ we're just printing user IDs for signing.
+
+ * armor.c (fake_packet): Properly handle the case where the line
+ is dash-space (i.e. a blank line that was quoted). Give a warning
+ for bad dash escaping.
+
+2004-10-14 Werner Koch <wk@g10code.com>
+
+ * export.c (do_export_stream) [ENABLE_SELINUX_HACKS]: Don't allow
+ secret key export.
+ * import.c (import_secret_one) [ENABLE_SELINUX_HACKS]: Likewise
+
+ * misc.c (is_secured_filename): New.
+ * keydb.c (maybe_create_keyring)
+ * tdbio.c (tdbio_set_dbname)
+ * plaintext.c (handle_plaintext)
+ * openfile.c (copy_options_file, open_outfile)
+ * exec.c (exec_write)
+ * keygen.c (do_generate_keypair, gen_card_key_with_backup)
+
+ * sign.c (sign_file, clearsign_file)
+ * keyring.c (create_tmp_file, do_copy): Check for secured files
+ before creating them.
+
+ * keygen.c (print_status_key_created, read_parameter_file):
+ s/unsigned char/byte/ due to a strange typedef for RISC OS. Noted
+ by Stefan.
+
+2004-10-13 David Shaw <dshaw@jabberwocky.com>
+
+ * armor.c (fake_packet): Allow arbitrary dash-escaped lines as per
+ 2440bis-10. This is bug #158.
+
+ * keyserver.c (keyserver_work): Handle keyserver timeouts.
+
+ * pkclist.c (do_edit_ownertrust): Different prompt when we're
+ using direct trust since the meaning is different.
+
+ * keyedit.c (trustsig_prompt): Change the strings to match the
+ ones in pkclist.c:do_edit_ownertrust to make translation easier.
+
+ * trustdb.c (trust_model_string, get_validity): Add direct trust
+ model which applies to the key as a whole and not per-uid.
+
+ * options.h, g10.c (parse_trust_model): New.
+ (main): Call it from here to do string-to-trust-model.
+
+2004-10-13 Werner Koch <wk@g10code.com>
+
+ * tdbdump.c (import_ownertrust): Removed all log_error_f and
+ reworded the messages.
+
+ * dermor.c: Include i18n.h. Made 2 strings translatable.
+
+ * misc.c (register_secured_file, is_secured_file)
+ (unregister_secured_file): New.
+ * keyring.c (do_copy, rename_tmp_file): Implement the SELinux hacks.
+ (keyring_register_filename): Ditto.
+ * tdbio.c (open_db): Ditto.
+ * openfile.c (copy_options_file, open_sigfile): Ditto.
+ * verify.c (verify_signatures, verify_one_file): Ditto.
+ * photoid.c (generate_photo_id): Ditto.
+ * keygen.c (read_parameter_file): Ditto.
+ * import.c (import_keys_internal): Ditto.
+ * decrypt.c (decrypt_message, decrypt_messages): Ditto.
+ * dearmor.c (dearmor_file, enarmor_file): Ditto.
+ * g10.c (main, print_mds): Ditto.
+ * exec.c (exec_write, exec_read): Ditto.
+ * card-util.c (change_login): Ditto.
+ * encode.c (encode_simple, encode_crypt): Ditto.
+
+ * openfile.c (overwrite_filep, make_outfile_name, open_outfile)
+ (open_sigfile): Use iobuf_is_pipe_filename to check for pipes so
+ that special filesnames are taken into account. This is bug 327.
+
+ * tdbdump.c (import_ownertrust): Ditto.
+
+ * sign.c (write_plaintext_packet): Ditto.
+ (sign_file, clearsign_file, sign_symencrypt_file):
+
+ * progress.c (handle_progress): Ditto.
+ * plaintext.c (handle_plaintext): Ditto.
+ (ask_for_detached_datafile, hash_datafiles):
+
+ * encode.c (encode_simple, encode_crypt): Ditto.
+
+2004-10-12 Werner Koch <wk@g10code.com>
+
+ * keygen.c (read_parameter_file): Changed to use iobuf based file
+ reading to allow the special file name feature to work.
+
+ * keygen.c (read_parameter_file): New keyword "Handle". This is
+ bug 287.
+ (print_status_key_not_created): New.
+ (print_status_key_created): Add new arg HANDLE.
+ (do_generate_keypair): Print not created status.
+ * status.c, tatus.h (STATUS_KEY_NOT_CREATED): New.
+
+2004-10-11 David Shaw <dshaw@jabberwocky.com>
+
+ * pkclist.c (do_edit_ownertrust): Use the same translated string
+ for showing the user ID as mainproc.c:print_pkenc_list.
+
+ * mainproc.c (print_pkenc_list): Allow translating the quotes
+ around the user ID.
+
+ * card-util.c, g10.c, photoid.c, trustdb.c: The last of the \"%s\"
+ -> `%s' quoting for things that aren't user IDs.
+
+ * keyserver.c (keyserver_spawn): If there is no keyserver host,
+ print the whole URI since it is self-contained.
+
+2004-10-11 Werner Koch <wk@g10code.com>
+
+ * keyserver.c (keyserver_spawn): Print an empty string in log_info
+ if the host is not set (e.g. finger).
+
+2004-10-10 David Shaw <dshaw@jabberwocky.com>
+
+ * card-util.c, keyedit.c, openfile.c, pkclist.c, delkey.c,
+ keygen.c, photoid.c, revoke.c: Some yes-or-no prompts end in
+ "(y/n)". Some don't. Consistently use y/n everywhere.
+
+ * keygen.c (ask_key_flags): New.
+ (ask_algo): Call it here in --expert mode so we don't need to
+ specify each possible variation of RSA capabilities.
+
+ * keygen.c (do_add_key_flags): The spec says that all primary keys
+ MUST be able to certify. Force the certify flag on for primaries
+ (and off for subkeys).
+
+ * keygen.c (generate_keypair): Fix generating keys with the auth
+ flag.
+
+2004-10-08 David Shaw <dshaw@jabberwocky.com>
+
+ * encr-data.c (decrypt_data): Give a warning with a weak key, but
+ still allow to decrypt the message.
+
+2004-10-07 David Shaw <dshaw@jabberwocky.com>
+
+ * pkclist.c (build_pk_list): Keystrify.
+
+ * mainproc.c (check_sig_and_print), pkclist.c
+ (do_edit_ownertrust): Improve translatability of user ID prompts.
+
+2004-10-06 David Shaw <dshaw@jabberwocky.com>
+
+ * helptext.c, pkclist.c (do_we_trust): It is not possible to get
+ here with a revoked or expired key, so BUG() that case. Remove
+ question about overriding revoked/expired. Also
+ --keyid-format-ify.
+ (do_we_trust_pre): Use print_pubkey_info() instead of printing the
+ info ourselves.
+
+ * passphrase.c (passphrase_to_dek): Improve translatability of
+ user ID prompts.
+
+ * keylist.c (print_pubkey_info): Use the user ID the pk was
+ selected by, if any.
+
+ * keyedit.c (sign_uids, ask_revoke_sig): Improve translatability
+ of user ID prompts.
+ (ask_revoke_sig, menu_revsig): Try and use common strings for
+ these two functions so they don't need to be translated twice.
+
+ * keyedit.c, keylist.c, keyserver.c, mainproc.c: The
+ revoked/expired/expires string change of 2004-09-29 was too
+ simple. Use two styles for each tag.
+
+2004-10-06 Werner Koch <wk@g10code.com>
+
+ * ccid-driver.c (ccid_open_reader): Store the vendor ID.
+ (ccid_transceive_secure): New.
+ (parse_ccid_descriptor): Workaround for an SCM reader problem.
+ (send_escape_cmd): New.
+
+2004-10-05 David Shaw <dshaw@jabberwocky.com>
+
+ * passphrase.c (agent_get_passphrase): Use keystrs for agent
+ strings, and fix sprintf warnings.
+
+ * keyserver.c (keyserver_spawn): Fix BUG() with certain sets of
+ mixed regular and preferred keyserver refreshes. Noted by
+ Sebastian Wiesinger.
+
+ * keyedit.c (show_key_with_all_names): Show uid validity in menu.
+
+2004-10-03 Timo Schulz <twoaday@g10code.de>
+
+ * apdu.c (apdu_open_remote_reader) [_WIN32]: Do not set ENOSYS.
+
+2004-10-03 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (print_and_check_one_sig_colon): Fix bad keyids in
+ colon delsig output. Noted by Peter Palfrader.
+ (show_prefs): Do not reference missing selfsig. Noted by Alex
+ Moroz.
+
+2004-10-01 Werner Koch <wk@g10code.com>
+
+ * gpgv.c (i18n_init): Always use LC_ALL.
+
+2004-09-30 Werner Koch <wk@g10code.com>
+
+ * app-openpgp.c (verify_chv3) [GNUPG_MAJOR_VERSION!=1]: Typo fix.
+
+2004-09-30 David Shaw <dshaw@jabberwocky.com>
+
+ * gpgv.c, keydb.c (keydb_add_resource): Factored keyring creation
+ out to ..
+ (maybe_create_keyring): .. new. Make sure that we do the checks
+ in a locked state. Problem reported by Stefan Haller. Try to
+ create the home directory before acquiring a lock for the keyring.
+ From Werner on stable branch.
+
+ * g10.c (main): Blow up if we didn't lose setuid. From Werner on
+ stable branch.
+
+2004-09-29 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c, keylist.c, keyserver.c, mainproc.c: Reduce the many
+ variations of "revoked" ("revoked", "[revoked]", " [revoked]",
+ "[revoked] ") "and" expired down to two to simplify translation.
+
+2004-09-28 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (print_and_check_one_sig): Account for the extra space
+ that show-sig-expire takes up so we do not wrap lines.
+ (show_key_with_all_names): No need to show subkey revocations as a
+ seperate line since we now show revocation date in the main subkey
+ line.
+
+ * signal.c (got_fatal_signal): HAVE_DECL_SYS_SIGLIST is defined,
+ but zero if not found. Noted by John Clizbe.
+
+ * keyserver.c (parse_keyrec): Fix problem with non-expiring keys
+ appearing expired in --search-keys results.
+
+2004-09-27 Werner Koch <wk@g10code.com>
+
+ * card-util.c (card_edit): Take admin only status from the table.
+
+ * app-openpgp.c: Made all strings translatable.
+ (verify_chv3) [GNUPG_MAJOR_VERSION]: Make opt.allow_admin
+ available for use in gnupg 2.
+ (verify_chv3): Reimplemented countdown showing to use only
+ functions from this module. Flush the CVH status cache on a
+ successful read.
+ (get_one_do): Hack to bypass the cache for cards versions > 1.0.
+ (store_fpr): Store the creation date for card version > 1.0.
+
+2004-09-25 David Shaw <dshaw@jabberwocky.com>
+
+ * main.h, g10.c (main), card-util.c (change_pin): If "admin" has
+ not been issued, skip right to the CHV1/CHV2 PIN change. No need
+ to show the unblock or admin PIN change option.
+ (card_edit): Add "admin" command to add admin commands to the
+ menu. Do not allow admin commands until "admin" is given.
+
+ * app-openpgp.c (verify_chv3): Show a countdown of how many wrong
+ admin PINs can be entered before the card is locked.
+
+ * options.h, g10.c (main), app-openpgp.c (verify_chv3): Remove
+ --allow-admin.
+
+2004-09-24 David Shaw <dshaw@jabberwocky.com>
+
+ * main.h: Create S2K_DIGEST_ALGO macro so we do not need to always
+ set opt.s2k_digest_algo. This helps fix a problem with PGP 2.x
+ encrypted symmetric messages. Change all callers (encode.c,
+ g10.c, keyedit.c, keygen.c, passphrase.c, sign.c).
+
+ * armor.c, cardglue.c, getkey.c, import.c, keygen.c: Be consistent
+ in some more quoted strings. Always use 'user ID', not 'user id',
+ "quotes" for user IDs, etc.
+
+ * keyedit.c (keyedit_menu), gpgv.c (agent_scd_getattr (stub)),
+ keygen.c (copy_mpi, generate_raw_key): Fix a compile problem and a
+ few warnings when building without card support.
+
+2004-09-23 Werner Koch <wk@g10code.com>
+
+ * card_util.c (generate_card_keys): ask whether backup should be
+ created.
+ (card_store_subkey): Factored some code out to ..
+ * keygen.c (save_unprotected_key_to_card): .. new function.
+ (gen_card_key_with_backup): New.
+ (generate_raw_key): New.
+ (generate_keypair): New arg BACKUP_ENCRYPTION_DIR. Changed all
+ callers.
+ (do_generate_keypair): Divert to gen_card_key_with_backup when
+ desired.
+
+ * apdu.c (open_pcsc_reader): Do not print empty reader string.
+
+ * keygen.c (ask_algo): Allow creation of AUTH keys.
+
+ * keyid.c (usagestr_from_pk): New.
+
+ * app-openpgp.c (app_openpgp_storekey): Call flush_cache.
+ (get_cached_data): Move local data initialization to ..
+ (app_select_openpgp): .. here. Read some flags for later use.
+ (do_getattr): New read-only attribute EXTCAP.
+
+ * keyedit.c (keyedit_menu): New command "keytocard"
+ (keyedit_menu): Bad hack for the not_with_sk element.
+ (show_key_with_all_names): Print the usage.
+ (find_pk_from_sknode): New.
+
+ * card-util.c (card_store_subkey): New.
+ (copy_mpi): New.
+
+ * cardglue.c (agent_openpgp_storekey): New.
+
+2004-09-22 Werner Koch <wk@g10code.com>
+
+ * card-util.c (card_generate_subkey, generate_card_keys): Factored
+ common code out to ...
+ (get_info_for_key_operation, check_pin_for_key_operation)
+ (restore_forced_chv1, replace_existing_key_p)
+ (show_card_key_info): ... new functions.
+
+2004-09-21 David Shaw <dshaw@jabberwocky.com>
+
+ * mainproc.c (check_sig_and_print), keyedit.c (show_prefs,
+ menu_set_keyserver_url): Make sure that keyserver URLs with
+ control characters inside are printed properly. In fact, handle
+ them as UTF8.
+
+ * keyedit.c (keyedit_menu): Don't show "addcardkey" in the menu if
+ we do not have card support.
+
+ * keydb.h, keyserver.c (print_keyrec, keyserver_spawn): fpr is an
+ array of unsigned bytes.
+
+2004-09-20 Werner Koch <wk@g10code.com>
+
+ * g10.c: Make -K an alias for --list-secret-keys.
+
+ * keylist.c (print_card_serialno): New. Taken from gnupg 1.9.11.
+ (list_keyblock_print): Make use of it.
+ * keyedit.c (show_key_with_all_names): Print the card S/N.
+
+ * keyedit.c (keyedit_menu): New command ADDCARDKEY.
+ * card-util.c (card_generate_subkey): New.
+ * keygen.c (generate_card_subkeypair): New.
+ (gen_card_key): New arg IS_PRIMARY; changed all callers.
+
+ * cardglue.c (open_card): Use shutdown code if possible.
+ (check_card_serialno): Ditto.
+
+ * ccid-driver.c (do_close_reader): Factored some code out from ...
+ (ccid_close_reader): ..here.
+ (ccid_shutdown_reader): New.
+
+ * apdu.c (apdu_shutdown_reader): New.
+ (shutdown_ccid_reader): New.
+
+2004-09-17 Werner Koch <wk@g10code.com>
+
+ * g10.c (list_config): New config option ccid-reader-id.
+ (gpgconf_list): Add "reader-port".
+
+ * apdu.c (open_ccid_reader): New arg PORTSTR. Pass it to
+ ccid_open_reader.
+ (apdu_open_reader): Pass portstr to open_ccid_reader.
+ (apdu_open_reader): No fallback if a full CCID reader id has been
+ given.
+
+ * ccid-driver.c (ccid_get_reader_list): New.
+ (ccid_open_reader): Changed API to take a string for the reader.
+ Removed al the cruft for the libusb development vesion which seems
+ not to be maintained anymore and there are no packages anyway.
+ The stable library works just fine.
+ (struct ccid_reader_id_s): Deleted and replaced everywhere by a
+ simple string.
+ (usb_get_string_simple): Removed.
+ (bulk_in): Do valgrind hack here and not just everywhere.
+
+2004-09-16 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (show_key_with_all_names, show_prefs): Show preferred
+ keyserver(s) in "showpref" output.
+
+ * keygen.c (keygen_add_keyserver_url), keyedit.c
+ (menu_set_keyserver_url): Allow setting a keyserver URL of "none"
+ to remove an existing keyserver URL.
+
+ * keyedit.c (menu_set_keyserver_url): Confirm replacement of a
+ keyserver URL before overwriting the old one.
+
+2004-09-15 David Shaw <dshaw@jabberwocky.com>
+
+ * gpgv.c (agent_scd_getattr): Stub.
+
+ * misc.c (get_signature_count): New. Get the signature count from
+ a smartcard.
+ (pct_expando): Call it here so the %c expando becomes the number
+ of signatures issued. This allows for notations or the like with
+ an automatic signature count.
+
+ * ccid-driver.c (usb_get_string_simple): Replacement function to
+ work with older libusb.
+
+2004-09-15 Werner Koch <wk@g10code.com>
+
+ * g10.c [HAVE_LIBUSB]: New option --debug-ccid-driver.
+
+ * ccid-driver.c (read_device_info): Removed.
+ (make_reader_id, scan_or_find_devices): New.
+ (ccid_open_reader): Simplified by make use of the new functions.
+ (ccid_set_debug_level): New. Changed the macros to make use of
+ it. It has turned out that it is often useful to enable debugging
+ at runtime so I added this option.
+
+2004-09-13 David Shaw <dshaw@jabberwocky.com>
+
+ * getkey.c (premerge_public_with_secret): Fix subkey<->binding sig
+ mismatch when some secret subkeys are missing. Discovered by
+ Michael Roth.
+
+ * main.h, keylist.c (print_subpackets_colon): Make a public
+ function.
+
+ * keyedit.c (print_and_check_one_sig_colon): New. Print a
+ with-colons version of the sig record.
+ (menu_delsig): Call it here for a with-colons delsig.
+
+2004-09-12 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, keylist.c (print_one_subpacket,
+ print_subpackets_colon): Print a spk record for each request
+ subpacket.
+ (list_keyblock_colon): Call them here.
+
+ * g10.c (parse_subpacket_list, parse_list_options): New. Make the
+ list of subpackets we are going to print.
+ (main): Call them here.
+
+2004-09-11 David Shaw <dshaw@jabberwocky.com>
+
+ * card-util.c (fetch_url, card_edit): Use the pubkey URL stored on
+ the card to fetch an updated copy. Works with either straight
+ URLs or HKP or LDAP keyservers.
+
+ * keyserver-internal.h, keyserver.c (keyserver_import_fprint),
+ import.c (revocation_present): Use a keyserver_spec so the caller
+ can pass in whatever keyserver they like.
+
+2004-09-10 David Shaw <dshaw@jabberwocky.com>
+
+ * app-openpgp.c (get_cached_data): Avoid mallocing zero since it
+ breaks us when using --enable-m-guard.
+
+ * ccid-driver.c (read_device_info): Fix segfault when usb device
+ is not accessible.
+ (ccid_open_reader): Allow working with an even older version of
+ libusb (usb_busses global instead of usb_get_busses()).
+
+2004-09-09 Werner Koch <wk@g10code.com>
+
+ * cardglue.h: Add members for CA fingerprints.
+ * cardglue.c (agent_release_card_info): Invalid them.
+ (learn_status_cb): Store them.
+
+ * app-common.h, app-openpgp.c, iso7816.c, iso7816.h
+ * apdu.c, apdu.h, ccid-driver.c, ccid-driver.h
+ * card-util.c: Updated from current gnupg-1.9.
+
+ Changes are:
+
+ * ccid-driver.h (CCID_DRIVER_ERR_ABORTED): New.
+ * ccid-driver.c (ccid_open_reader): Support the stable 0.1 version
+ of libusb.
+ (ccid_get_atr): Handle short messages.
+ * apdu.c (my_rapdu_get_status): Implemented.
+ * apdu.c: Include <signal.h>.
+ * apdu.c (reader_table_s): Add function pointers for the backends.
+ (apdu_close_reader, apdu_get_status, apdu_activate)
+ (send_apdu): Make use of them.
+ (new_reader_slot): Intialize them to NULL.
+ (dump_ccid_reader_status, ct_dump_reader_status): New.
+ (dump_pcsc_reader_status): New.
+ (open_ct_reader, open_pcsc_reader, open_ccid_reader)
+ (open_osc_reader, open_rapdu_reader): Intialize function pointers.
+ (ct_activate_card, ct_send_apdu, pcsc_send_apdu, osc_send_apdu)
+ (error_string): Removed. Replaced by apdu_strerror.
+ (get_ccid_error_string): Removed.
+ (ct_activate_card): Remove the unused loop.
+ (reset_ct_reader): Implemented.
+ (ct_send_apdu): Activate the card if not yet done.
+ (pcsc_send_apdu): Ditto.
+ * ccid-driver.h: Add error codes.
+ * ccid-driver.c: Implement more or less proper error codes all
+ over the place.
+ * apdu.c (apdu_send_direct): New.
+ (get_ccid_error_string): Add some error code mappings.
+ (send_apdu): Pass error codes along for drivers already supporting
+ them.
+ (host_sw_string): New.
+ (get_ccid_error_string): Use above.
+ (send_apdu_ccid): Reset the reader if it has not yet been done.
+ (open_ccid_reader): Don't care if the ATR can't be read.
+ (apdu_activate_card): New.
+ (apdu_strerror): New.
+ (dump_reader_status): Only enable it with opt.VERBOSE.
+ * iso7816.c (map_sw): Add mappings for the new error codes.
+ * apdu.c (open_ct_reader, open_pcsc_reader, open_ccid_reader)
+ (reset_ccid_reader, open_osc_reader): Call dump_reader_status only
+ in verbose mode.
+ * app-openpgp.c (do_getattr): Fix for sending CA-FPR.
+ * app-openpgp.c (app_openpgp_readkey): Fixed check for valid
+ exponent.
+ * app-openpgp.c (do_setattr): Sync FORCE_CHV1.
+ * card-util.c (change_login): Kludge to allow reading data from a
+ file.
+ (card_edit): Pass ARG_STRING to change_login.
+ (card_status): Print CA fingerprints.
+ (change_cafpr): New.
+ (card_edit): New command CAFPR.
+
+2004-04-30 Werner Koch <wk@gnupg.org>
+
+ * g10.c (main) <gpgconf>: Use gpg.conf and not /dev/null as
+ default filename.
+
+2004-04-28 Werner Koch <wk@gnupg.org>
+
+ * card-util.c (card_edit): Remove PIN verification.
+ (generate_card_keys): New arg SERIALNO. Do PIN verification here
+ after resetting forced_chv1.
+
+
+2004-09-09 Werner Koch <wk@g10code.com>
+
+ * signal.c (got_fatal_signal): Do readline cleanup. Print signal
+ number if we can't print the name. Use new autoconf macro
+ HAVE_DECL_SYS_SIGLIST.
+ (get_signal_name): Removed.
+
+ * photoid.c: Include ttyio.h.
+
+ * parse-packet.c (skip_rest): Removed. Changed all callers to use
+ the new iobuf_skip_reset. Orginal patch by Florian Weimer.
+
+2004-09-07 Werner Koch <wk@g10code.de>
+
+ * photoid.c (generate_photo_id): Use tty_printf and not just
+ printf. Put _() around one string.
+
+2004-09-03 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (parse_keyrec): Force the 'e'xpired flag on as soon
+ as we know the key is definitely expired. Some translatable
+ string cleanup.
+
+2004-08-27 David Shaw <dshaw@jabberwocky.com>
+
+ * encode.c, exec.c, g10.c, sign.c: Some translatable string
+ cleanup. Change some "this" to `this'.
+
+2004-08-23 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_spawn): Show log line for what keyserver
+ action we are taking.
+
+ * keyid.c (keystr): If printing a keyid that lacks the high 4
+ bytes, print the low 4 alone.
+ (keystr_from_desc): Handle short keyids and warn on v3
+ fingerprints.
+
+ * keydb.h, getkey.c (get_user_id_printable,
+ get_user_id_string_printable): Rename to get_user_id_native and
+ get_user_id_string_native and remove the printable stuff since
+ we're print-ifying valid utf8 characters. Change all callers in
+ import.c, sign.c, keylist.c, and encode.c.
+
+ * keyserver.c (keyserver_search_prompt): Make sure the search
+ string is converted from UTF-8 before display.
+
+2004-08-19 Werner Koch <wk@g10code.de>
+
+ * seskey.c (encode_session_key): Changed the zero random byte
+ substituting code to actually do clever things. Thanks to
+ Matthias Urlichs for noting the implementation problem.
+
+2004-08-18 Marcus Brinkmann <marcus@g10code.de>
+
+ * passphrase.c (agent_get_passphrase): Fix detection of gpg-agent
+ cancellation.
+
+2004-08-08 David Shaw <dshaw@jabberwocky.com>
+
+ * plaintext.c (handle_plaintext): Bigger buffer for extra safety.
+
+ * g10.c (main): New alias --throw-keyid for --throw-keyids, so
+ that it continues to work in old configuration files. Noted by
+ Jens Adam.
+
+ * pkclist.c (algo_available): --pgp8 now allows blowfish, zlib,
+ and bzip2.
+
+ * status.c (do_get_from_fd): Flush stdout if status isn't flushing
+ it for us. This guarantees that any menus that were displayed
+ before the prompt don't get stuck in a buffer. Noted by Peter
+ Palfrader. This is Debian bug #254072.
+
+ * sign.c (update_keysig_packet): Revert change of 2004-05-18. It
+ is not appropriate to strip policy and notations when remaking a
+ sig. That should only happen when specifically requested by the
+ user.
+
+2004-08-05 David Shaw <dshaw@jabberwocky.com>
+
+ * armor.c (radix64_read): No armor CRC is legal according to the
+ spec (the CRC is a MAY).
+
+2004-07-28 David Shaw <dshaw@jabberwocky.com>
+
+ * misc.c (argsplit): Properly split quoted args from the keyword
+ and trim whitespace afterwards.
+
+2004-07-27 David Shaw <dshaw@jabberwocky.com>
+
+ * misc.c (optsep): Add the ability to understand keyword="quoted
+ arg with spaces" type options.
+
+2004-07-16 David Shaw <dshaw@jabberwocky.com>
+
+ * keylist.c (list_keyblock_print): Always use the new listing
+ format where uids are always on a line for themselves. Mark
+ expired secret keys as expired.
+
+ * options.h, g10.c (main): Rename list show-validity to
+ show-uid-validity as it only shows for uids.
+
+ * armor.c (armor_filter): Do not use padding to get us to 8 bytes
+ of header. Rather, use 2+4 as two different chunks. This avoids
+ a fake filename of "is".
+
+2004-07-15 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (sign_uids): Properly handle remaking a self-sig on
+ revoked or expired user IDs. Also, once we've established that a
+ given uid cannot or will not be signed, don't continue to ask
+ about each sig.
+
+ * mainproc.c (proc_symkey_enc), seckey-cert.c (do_check): Check
+ the S2K hash algorithm before we try to generate a passphrase
+ using it. This prevents hitting BUG() when generating a
+ passphrase using a hash that we don't have.
+
+ * sign.c (sign_symencrypt_file): Allow using --force-mdc in --sign
+ --symmetric messages.
+
+ * g10.c (main): Alias --charset as --display-charset to help avoid
+ the continuing confusion and make room for possible changes in
+ devel.
+
+ * parse-packet.c (parse_plaintext): Show the hex value for the
+ literal packet mode since it may not be printable.
+
+ * keygen.c (make_backsig): Make sure that the backsig was built
+ successfully before we try and use it.
+
+ * status.h, status.c (get_status_string), plaintext.c
+ (handle_plaintext): New status tags PLAINTEXT and
+ PLAINTEXT_LENGTH.
+
+2004-06-16 Werner Koch <wk@gnupg.org>
+
+ * free-packet.c (copy_secret_key): Get last fix right.
+
+2004-06-16 Werner Koch <wk@gnupg.org>
+
+ * free-packet.c (copy_secret_key): Fixed memory leak when D is not
+ NULL.
+
+ * passphrase.c (passphrase_to_dek): Added a few comments to the
+ code.
+
+2004-05-26 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_refresh): Keep track of keys already
+ fetched so we don't do a regular keyserver fetch if the preferred
+ keyserver fetch has exhausted the list.
+
+2004-05-23 David Shaw <dshaw@jabberwocky.com>
+
+ * verify.c (verify_signatures): Verify multiple files in the same
+ order in which we hashed them when issuing the signature. Noted
+ by Nicholas Cole.
+
+ * pkclist.c (do_edit_ownertrust): Fix a kbnode leak and do another
+ keyid-format conversion.
+
+2004-05-22 Werner Koch <wk@gnupg.org>
+
+ * trustdb.c (check_regexp): s/EXP/EXPR/.
+
+ * keyedit.c (trustsig_prompt): Remoev useless range check.
+
+ * options.h: Renamed ctrl to glo_ctrl. Changed all users.
+
+ * ccid-driver.c (ccid_open_reader): Print a warning when CCID
+ can't be used.
+
+2004-05-21 David Shaw <dshaw@jabberwocky.com>
+
+ * mainproc.c (check_sig_and_print): If we're honoring preferred
+ keyservers, and auto-key-retrieve is set, try and get a missing
+ key from the preferred keyserver subpacket when we verify the sig.
+
+ * gpgv.c (parse_preferred_keyserver, free_keyserver_spec): Stubs.
+
+ * keyserver.c (keyidlist): Use new parse_preferred_keyserver
+ function.
+ (keyserver_work): Use the passed-in keyserver spec rather than the
+ options global one.
+
+ * keyserver-internal.h, keyserver.c (parse_preferred_keyserver):
+ New function to take a sig and return a split out keyserver_spec.
+ (keyserver_import_keyid): Now takes a keyserver_spec.
+
+ * keyserver.c (keyidlist): Go back to the old fast keyid lister.
+ Only merge selfsigs if we have to for honor-keyserver-url.
+ (keyserver_refresh): Keyserver URL handler moved here.
+ (calculate_keyid_fpr): Removed.
+
+ * keydb.h, keyid.c (keystr_from_desc): Calculate a key string from
+ a KEYDB_SEARCH_DESC.
+
+ * keyserver.c (keyserver_spawn): Fix keyserver options on tempfile
+ only platforms. Noted by Roger Sondermann.
+
+2004-05-20 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_work): Allow --refresh-keys with a
+ preferred keyserver to happen even if there is no global keyserver
+ set.
+
+ * sig-check.c (do_check_messages): No need to check for Elgamal
+ signatures any longer.
+ (do_check_messages, do_check, check_key_signature2):
+ --keyid-format conversion.
+
+ * pkclist.c (show_paths, edit_ownertrust): Remove some unused
+ code.
+
+ * options.h (ctrl): New for member IN_AUTO_KEY_RETRIEVE.
+
+ * mainproc.c (check_sig_and_print): track whether we are
+ retrieving a key.
+
+ * status.c (status_currently_allowed): New.
+ (write_status_text, write_status_text_and_buffer): Use it here.
+
+ * g10.c: New command --gpgconf-list.
+ (gpgconf_list): New. From Werner on stable branch.
+
+2004-05-19 David Shaw <dshaw@jabberwocky.com>
+
+ * pubkey-enc.c (get_session_key, get_it), keyedit.c
+ (show_key_with_all_names, show_basic_key_info): --keyid-format
+ conversion.
+
+2004-05-18 David Shaw <dshaw@jabberwocky.com>
+
+ * sign.c (update_keysig_packet): Policies and notations should be
+ stripped out when remaking a self-signature. Noted by Atom
+ Smasher.
+
+ * keyserver.c (parse_keyserver_uri): Fix compiler warnings.
+
+2004-05-11 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, keyserver-internal.h, keyserver.c
+ (parse_keyserver_uri): Improved URI parser that keeps track of the
+ path information and doesn't modify the input string.
+ (keyserver_spawn): Tell keyserver plugins about the path.
+
+2004-05-11 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (show_policy_url, show_keyserver_url, show_notation)
+ (list_one): Use const char* for i18n string helpers.
+
+ * keygen.c (do_generate_keypair, read_parameter_file): Really
+ close the files.
+ (do_generate_keypair): Create the secret key file using safe
+ permissions. Noted by Atom Smasher.
+
+2004-05-10 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, mainproc.c (symkey_decrypt_seskey), keyserver.c
+ (struct keyrec, parse_keyrec, keyserver_search_prompt), keyedit.c
+ (keyedit_menu), g10.c (add_keyserver_url, add_policy_url): Fix
+ some compiler warnings.
+
+2004-05-08 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (keyedit_menu, menu_set_keyserver_url): Allow passing
+ preferred keyserver on "keyserver" command line. Sanity check
+ keyserver URL before accepting it.
+
+ * keyserver-internal.h, g10.c (main), keyserver.c
+ (parse_keyserver_uri): Add an option to require the scheme:// and
+ change all callers.
+ (free_keyserver_spec): Make public.
+
+2004-05-07 Werner Koch <wk@gnupg.org>
+
+ * sign.c (write_plaintext_packet): Fixed the detection of too
+ large files in the same way as in encode.c.
+
+2004-05-04 David Shaw <dshaw@jabberwocky.com>
+
+ * keylist.c (show_notation): Use bits to select which sort of
+ notation to show. Don't allow a not-shown notation to prevent us
+ from issuing the proper --status-fd message.
+
+ * options.h, g10.c (main): Add show-std/standard-notations and
+ show-user-notations. show-notations is both. Default is to show
+ standard notations only during verify. Change all callers.
+
+2004-04-28 David Shaw <dshaw@jabberwocky.com>
+
+ * main.h, keylist.c (show_notation): Add argument to show only
+ user notations, only standard notations, or both. Change all
+ callers.
+
+ * keyserver.c (keyserver_spawn): We still need EXEC_TEMPFILE_ONLY.
+
+2004-04-28 Werner Koch <wk@gnupg.org>
+
+ * card-util.c (card_edit): Require PIN only for generate.
+
+ * app-openpgp.c (do_setattr): Sync FORCE_CHV1.
+
+2004-04-27 Werner Koch <wk@gnupg.org>
+
+ * keyserver.c (keyserver_spawn) [EXEC_TEMPFILE_ONLY]: Removed
+ setting use_temp_file because this option has been removed.
+
+ * g10.c: New commands --allow-admin and --deny-admin.
+ * options.h (opt): Add member ALLOW_ADMIN.
+
+ * tlv.h, tlv.c: New. Copied from gnupg-1.9.
+ * cardglue.c (open_card): The serialno is now set internally by
+ app_select_openpgp; changed invocation.
+ * cardglue.h (app_t, ctrl_t): New.
+ (GPG_ERR_EBUSY, GPG_ERR_ENOENT, GPG_ERR_NOT_FOUND, GPG_ERR_BUG)
+ (GPG_ERR_NOT_IMPLEMENTED, GPG_ERR_EACCESS): New.
+ (gpg_err_code_from_errno): New.
+
+ * app-common.h, app-openpgp.c, iso7816.c, iso7816.h
+ * apdu.c, apdu.h, ccid-driver.c, ccid-driver.h
+ * card-util.c: Updated from current gnupg-1.9.
+
+ Changes are:
+
+ * app-common.h: New members FNC.DEINIT and APP_LOCAL.
+ * app-openpgp.c (do_deinit): New.
+ (get_cached_data, flush_cache_item, flush_cache_after_error)
+ (flush_cache): New.
+ (get_one_do): Replaced arg SLOT by APP. Make used of cached data.
+ (verify_chv2, verify_chv3): Flush some cache item after error.
+ (do_change_pin): Ditto.
+ (do_sign): Ditto.
+ (do_setattr): Flush cache item.
+ (do_genkey): Flush the entire cache.
+ (compare_fingerprint): Use cached data.
+
+ * apdu.c (apdu_send_le): Reinitialize RESULTLEN. Handle
+ SW_EOF_REACHED like SW_SUCCESS.
+
+ * ccid-driver.c (parse_ccid_descriptor): Store some of the reader
+ features away. New arg HANDLE
+ (read_device_info): New arg HANDLE. Changed caller.
+ (bulk_in): Handle time extension requests.
+ (ccid_get_atr): Setup parameters and the IFSD.
+ (compute_edc): New. Factored out code.
+ (ccid_transceive): Use default NADs when required.
+
+ * apdu.h: New pseudo stati SW_HOST_NOT_SUPPORTED,
+ SW_HOST_LOCKING_FAILED and SW_HOST_BUSY.
+ * iso7816.c (map_sw): Map it.
+
+ * ccid-driver.c (ccid_slot_status): Add arg STATUSBITS.
+ * apdu.c (apdu_get_status): New.
+ (ct_get_status, pcsc_get_status, ocsc_get_status): New stubs.
+ (get_status_ccid): New.
+ (apdu_reset): New.
+ (reset_ct_reader, reset_pcsc_reader, reset_osc_reader): New stubs.
+ (reset_ccid_reader): New.
+ (apdu_enum_reader): New.
+
+ * apdu.c (lock_slot, trylock_slot, unlock_slot): New helpers.
+ (new_reader_slot) [USE_GNU_PTH]: Init mutex.
+ (apdu_reset, apdu_get_status, apdu_send_le): Run functions
+ in locked mode.
+
+2004-04-25 David Shaw <dshaw@jabberwocky.com>
+
+ * getkey.c (get_seckey_byname2): Significantly simplify this
+ function by using key_byname to do the heavy lifting. Note that
+ this also fixes an old problem when the first key on the secret
+ keyring has an unusable stub primary, but is still chosen.
+
+ * getkey.c (key_byname): If namelist is NULL, return the first key
+ in the keyring.
+
+2004-04-22 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (make_backsig): If DO_BACKSIGS is not defined, do not
+ create backsigs.
+
+ * getkey.c (merge_selfsigs_subkey): Find 0x19 backsigs on subkey
+ selfsigs and verify they are valid. If DO_BACKSIGS is not
+ defined, fake this as always valid.
+
+ * packet.h, parse-packet.c (parse_signature): Make parse_signature
+ non-static so we can parse 0x19s in self-sigs.
+
+ * main.h, sig-check.c (check_backsig): Check a 0x19 signature.
+ (signature_check2): Give a backsig warning if there is no or a bad
+ 0x19 with signatures from a subkey.
+
+2004-04-21 David Shaw <dshaw@jabberwocky.com>
+
+ * parse-packet.c (dump_sig_subpkt, parse_one_sig_subpkt,
+ can_handle_critical): Parse and display 0x19 signatures.
+
+2004-04-20 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (parse_keyserver_uri): Do not accept "http" as an
+ alias for "hkp". They are not the same thing.
+
+2004-04-19 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main): Add keyserver-option
+ honor-keyserver-url. parse_keyserver_options now returns a
+ success code.
+
+ * keyserver.c (parse_keyserver_options): Return error on failure
+ to parse. Currently there is no way to fail as any unrecognized
+ options get saved to be sent to the keyserver plugins later.
+ Check length of keyserver option tokens since with =arguments we
+ must only match the prefix.
+ (free_keyserver_spec): Moved code from parse_keyserver_url.
+ (keyserver_work, keyserver_spawn): Pass in a struct keyserver_spec
+ rather than using the global keyserver option.
+ (calculate_keyid_fpr): New. Fills in a KEYDB_SEARCH_DESC for a
+ key.
+ (keyidlist): New implementation using get_pubkey_bynames rather
+ than searching the keydb directly. If honor-keyserver-url is set,
+ make up a keyserver_spec and try and fetch that key directly. Do
+ not include it in the returned keyidlist in that case.
+
+2004-04-16 David Shaw <dshaw@jabberwocky.com>
+
+ * plaintext.c (handle_plaintext): Accept 'u' as a plaintext mode
+ that requires end of line conversion. This is being considered
+ for a UTF8 text packet. If this doesn't take place, no major harm
+ done. If it does take place, we'll get a jump on starting the
+ changeover.
+
+ * g10.c (main): --no-use-embedded-filename.
+
+ * build-packet.c (calc_plaintext, do_plaintext): Do not create
+ illegal (packet header indicates a size larger than the actual
+ packet) encrypted data packets when not compressing and using a
+ filename longer than 255 characters.
+
+ * keyedit.c (no_primary_warning): Cleanup. (menu_expire): Don't
+ give primary warning for subkey expiration changes. These cannot
+ reorder primaries.
+
+ * keygen.c (gen_elg, gen_dsa, gen_rsa, do_create,
+ do_generate_keypair, generate_subkeypair): New is_subkey argument
+ to set whether a generated key is a subkey. Do not overload the
+ ret_sk. This is some early cleanup to do backsigs for signing
+ subkeys.
+
+ * keygen.c (write_keybinding, do_generate_keypair,
+ generate_subkeypair): Keep track of the unprotected subkey secret
+ key so we can make a backsig with it.
+
+ * keygen.c (make_backsig): New function to add a backsig to a
+ binding sig of signing subkeys. Currently disabled.
+ (write_keybinding): Call it here, for signing subkeys only.
+
+ * sign.c (make_keysig_packet): Allow generating 0x19 signatures
+ (same as 0x18 or 0x28, but used for backsigs).
+
+ * packet.h, build-packet.c (build_sig_subpkt): Add new
+ SIGSUBPKT_SIGNATURE type for embedded signatures.
+
+ * main.h, misc.c (optsep, argsplit, optlen, parse_options):
+ Simplify code and properly handle a partial match against an
+ option with an argument.
+
+ * keyserver-internal.h, keyserver.c (parse_keyserver_options): Use
+ new optsep and argsplit functions.
+
+2004-04-15 David Shaw <dshaw@jabberwocky.com>
+
+ * main.h, misc.c (argsplit): Refactor argsep into argsplit and
+ argsep so they can be called separately.
+
+ * options.h, keyserver.c (parse_keyserver_options): Remove
+ duplicate code from parse_keyserver_options by calling the generic
+ parse_options.
+
+ * keyserver.c (keyserver_spawn, keyserver_refresh), g10.c (main),
+ gpgv.c (main), mainproc.c (check_sig_and_print), import.c
+ (revocation_present): Change all callers.
+
+2004-04-14 David Shaw <dshaw@jabberwocky.com>
+
+ * packet.h, getkey.c (fixup_uidnode, merge_selfsigs_subkey): Keep
+ track of which self-sig we actually chose.
+
+ * keyedit.c (menu_expire, menu_set_primary_uid,
+ menu_set_preferences): Use it here to avoid updating non-used
+ self-sigs and possibly promoting an old self-sig into
+ consideration again.
+
+ * options.h, import.c, keyserver-internal.h, g10.c, mainproc.c,
+ keyserver.c (parse_keyserver_uri): Parse keyserver URI into a
+ structure. Cleanup for new "guess my keyserver" functionality, as
+ well as refreshing via a preferred keyserver subpacket.
+
+ * options.h: Encapsulate keyserver details. Change all callers.
+
+2004-04-05 Werner Koch <wk@gnupg.org>
+
+ * status.h (STATUS_NEWSIG): New.
+ * status.c (get_status_string): Add it.
+
+2004-03-27 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (keyedit_menu): Request a trustdb update when adding a
+ new user ID so the new ID gets validity set. Reported by Owen
+ Taylor.
+
+2004-03-25 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), compress-bz2.c (init_uncompress):
+ Rename --bzip2-compress-lowmem to --bzip2-decompress-lowmem since
+ it applies to decompression, not compression.
+
+2004-03-24 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (sign_uids, show_key_and_fingerprint, ask_revoke_sig,
+ menu_revsig, menu_showphoto): --keyid-format conversion.
+ (menu_addrevoker): Use print_pubkey_info() rather than duplicating
+ code.
+
+2004-03-19 David Shaw <dshaw@jabberwocky.com>
+
+ * trustdb.c (update_min_ownertrust, validate_keys): Do not use
+ keystr functions in log_debug.
+
+ * import.c (import_one): Try and collapse user IDs when importing
+ a key for the first time.
+
+ * keyedit.c (menu_addrevoker): Allow appointing a subkey as a
+ designated revoker if the user forces it via keyid!, so long as
+ the subkey can certify. Also use the proper date string when
+ prompting for confirmation.
+
+ * g10.c (main): Maintain ordering of multiple Comment lines.
+ Requested by Peter Hyman.
+
+2004-03-17 David Shaw <dshaw@jabberwocky.com>
+
+ * mainproc.c (proc_pubkey_enc, print_pkenc_list, list_node):
+ --keyid-format conversion.
+
+2004-03-16 David Shaw <dshaw@jabberwocky.com>
+
+ * getkey.c (skip_unusable, merge_selfsigs_main,
+ premerge_public_with_secret, lookup, get_user_id_string):
+ --keyid-format conversion.
+
+2004-03-15 David Shaw <dshaw@jabberwocky.com>
+
+ * trustdb.c (add_utk, verify_own_keys, update_min_ownertrust,
+ get_validity, ask_ownertrust, validate_keys): --keyid-format
+ conversion.
+
+ * import.c (check_prefs_warning, check_prefs): --keyid-format
+ conversion and a little better text.
+ (import_one, import_secret_one, import_revoke_cert, chk_self_sigs,
+ delete_inv_parts, merge_blocks): Still more --keyid-format
+ conversions.
+
+2004-03-06 David Shaw <dshaw@jabberwocky.com>
+
+ * keylist.c (print_seckey_info, print_pubkey_info): --keyid-format
+ conversion.
+ (list_keyblock_print): 0xshort should not push us into the new
+ list format since it is not much longer than regular 8-character
+ short keyids.
+
+ * keydb.h, keyid.c (keystr_from_pk, keystr_from_sk): New functions
+ to pull a key string from a key in one step. This isn't faster
+ than before, but makes for neater code.
+
+ * keylist.c (list_keyblock_print): Use keystr_from_xx here.
+ (print_key_data): No need to pass a keyid in.
+
+2004-03-05 David Shaw <dshaw@jabberwocky.com>
+
+ * keyid.c (keyid_from_sk): Minor performance boost by caching
+ secret key keyids so we don't have to calculate them each time.
+
+ * getkey.c (merge_selfsigs_subkey): Do not mark subkeys valid if
+ we do not support their pk algorithm. This allows for early
+ (during get_*) rejection of a subkey, and selection of another.
+
+ * passphrase.c (passphrase_to_dek): Give a little more information
+ when we have room to do so.
+
+2004-03-04 David Shaw <dshaw@jabberwocky.com>
+
+ * revoke.c (export_minimal_pk), export.c (do_export_stream),
+ passphrase.c (passphrase_to_dek), keyserver.c (print_keyrec): A
+ few more places to use --keyid-format.
+
+ * options.h, g10.c (main), export.c (parse_export_options,
+ do_export_stream): Remove --export-all and the "include-non-rfc"
+ export-option as they are no longer meaningful with the removal of
+ v3 Elgamal keys.
+
+ * armor.c (fake_packet, armor_filter): Use the 2440 partial length
+ encoding for the faked plaintext packet.
+
+2004-03-03 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), mainproc.c (check_sig_and_print):
+ Remove verify-option show-long-keyids and replace with
+ the more general keyid-format.
+
+ * build-packet.c (write_header2): Remove call to start old gpg
+ partial length mode and change all callers.
+ (do_plaintext): Turn off partial length encoding now that we're
+ done writing the packet.
+ (do_comment, do_user_id): Try for a headerlen of 2 since that's
+ the smallest and most likely encoding for these packets.
+
+ * parse-packet.c (parse): Remove call to start old gpg partial
+ length mode.
+
+2004-03-02 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main): Add a more flexible --keyid-format
+ option to replace the list-option (and eventually verify-option)
+ show-long-keyids. The format can be short, long, 0xshort, and
+ 0xlong.
+
+ * keydb.h, keyid.c (keystr, keystrlen): New functions to generate
+ a printable keyid.
+
+ * keyedit.c (print_and_check_one_sig, show_key_with_all_names),
+ keylist.c (list_keyblock_print): Use new keystr() function here to
+ print keyids.
+
+ * packet.h, free-packet.c (free_encrypted, free_plaintext),
+ parse-packet.c (copy_packet, skip_packet, skip_rest, read_rest,
+ parse_plaintext, parse_encrypted, parse_gpg_control): Use a flag
+ to indicate partial or indeterminate encoding. This is the first
+ step in some minor surgery to remove the old gpg partial length
+ encoding.
+
+2004-03-01 David Shaw <dshaw@jabberwocky.com>
+
+ * parse-packet.c (parse): Only data-type packets are allowed to
+ use OpenPGP partial length encoding.
+
+2004-02-25 David Shaw <dshaw@jabberwocky.com>
+
+ * delkey.c (do_delete_key): Allow deleting a public key with a
+ secret present if --expert is set.
+
+ * plaintext.c (handle_plaintext): Make bytecount static so it
+ works with multiple literal packets inside a message.
+
+ * encode.c, helptext.c (keygen.algo, keygen.algo.elg_se), keygen.c
+ (ask_algo), sig-check.c (do_check_messages), skclist.c
+ (build_sk_list): Rename "ElGamal" to "Elgamal" as that is the
+ proper spelling nowadays. Suggested by Jon Callas.
+
+2004-02-24 David Shaw <dshaw@jabberwocky.com>
+
+ * plaintext.c: Copyright.
+
+ * encode.c (encode_simple): Show cipher with --verbose.
+
+ * options.h, g10.c (main), keyedit.c (sign_keys): Add
+ --ask-cert-level option to enable cert level prompts during
+ sigs. Defaults to on. Simplify --default-cert-check-level to
+ --default-cert-level. If ask-cert-level is off, or batch is on,
+ use the default-cert-level as the cert level.
+
+ * options.h, g10.c (main), trustdb.c (mark_usable_uid_certs):
+ Simplify --min-cert-check-level to --min-cert-level.
+
+2004-02-22 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), trustdb.c (mark_usable_uid_certs): Add
+ --min-cert-check-level option to specify minimum cert check level.
+ Defaults to 2 (so 0x11 sigs are ignored). 0x10 sigs cannot be
+ ignored.
+
+2004-02-21 David Shaw <dshaw@jabberwocky.com>
+
+ * plaintext.c (handle_plaintext): Properly handle a --max-output
+ of zero (do not limit output at all).
+
+ * keyserver.c (keyserver_spawn): Use the full 64-bit keyid in the
+ INFO header lines, and include "sig:" records for the benefit of
+ people who store their keys in LDAP servers. It makes it easy to
+ do queries for things like "all keys signed by Isabella".
+
+ * main.h, misc.c (hextobyte): Removed. It's in libutil.a now.
+
+2004-02-20 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_export): Disallow user strings that
+ aren't key IDs.
+ (keyserver_import): Clarify error message.
+ (keyserver_spawn): Properly handle 8 bit characters in user IDs in
+ the info lines during SEND.
+
+ * mkdtemp.c: Removed.
+
+ * Makefile.am: We get mkdtemp.c from libutil.a now, so don't link
+ with @LIBOBJS@.
+
+ * keyserver.c (keyserver_spawn): Pass the scheme to the keyserver
+ helper.
+
+2004-02-18 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), plaintext.c (handle_plaintext): Add
+ --max-output option to help people deal with decompression bombs.
+
+2004-02-15 David Shaw <dshaw@jabberwocky.com>
+
+ * build-packet.c (do_user_id): Do not force a header for attribute
+ packets as they require a new CTB, and we don't support forced
+ headers for new CTBs yet.
+
+2004-02-14 David Shaw <dshaw@jabberwocky.com>
+
+ * build-packet.c (write_header2): If a suggested header length is
+ provided along with a zero length, interpret this as an actual
+ zero length packet and not as an indeterminate length packet.
+ (do_comment, do_user_id): Use it here as these packets might be
+ naturally zero length.
+
+ * parse-packet.c (parse): Show packet type when failing due to an
+ indeterminate length packet.
+
+ * misc.c (parse_options): Only provide args for the true (i.e. not
+ "no-xxx") form of options.
+
+2004-02-13 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (argsep): Move to misc.c.
+
+ * main.h, misc.c (parse_options), export.c (parse_export_options),
+ import.c (parse_import_options), g10.c (main): Use it here to
+ allow for options with optional arguments. Change all callers.
+
+ * import.c (check_prefs): Some language fixes.
+ (sec_to_pub_keyblock, import_secret_one): Without knowing the
+ number of MPIs there are, we cannot try and sk-to-pk-ize a key.
+
+2004-02-12 David Shaw <dshaw@jabberwocky.com>
+
+ * import.c (check_prefs): New function to check preferences on a
+ public key to ensure that it does not advertise any that we cannot
+ fulfill. Use the keyedit command list function to optionally
+ rewrite the prefs.
+ (import_one, import_secret_one): Use it here when importing a
+ public key that we have the secret half of, or when importing a
+ secret key that we have the public half of.
+
+ * main.h, keyedit.c (keyedit_menu): Remove sign_mode and enhance
+ the more general command list functionality to replace it.
+
+ * g10.c (main): Use the general command functionality to implement
+ --sign-key, --lsign-key, --nrsign-key, and --nrlsign-key.
+
+ * import.c (import_one): Do the revocation check even in the case
+ when a key, a revocation key set in a direct key signature, and a
+ revocation from that revocation key, all arrive piecemeal.
+ Needless to say, this is pretty obscure.
+
+2004-02-11 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), keylist.c (list_keyblock_print): Add
+ "show-unusable-subkeys" list-option to show revoked and/or expired
+ subkeys.
+
+2004-02-10 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (keyedit_menu): Prompt for subkey removal for both
+ secret and public subkeys.
+
+ * keylist.c (list_keyblock_print), keyedit.c
+ (show_key_with_all_names): Show the revocation date of a
+ key/subkey, and general formatting work.
+
+ * packet.h, getkey.c (merge_selfsigs_main, merge_selfsigs_subkey,
+ merge_selfsigs): Keep track of the revocation date of a key.
+
+ * keydb.h, keyid.c (revokestr_from_pk): New function to print the
+ revocation date of a key.
+
+ * keygen.c (keygen_set_std_prefs): Build the default preferences
+ list at runtime as it properly handles algorithms disabled at
+ build or run time.
+
+ * getkey.c (merge_selfsigs_main): Properly handle expired user IDs
+ when the expired self-sig is not the only self-sig.
+
+ * misc.c (compress_algo_to_string): Return NULL on failure like
+ all of the other xxxx_algo_to_string() functions.
+
+ * mainproc.c (list_node): Minor spacing tweak to match --list-keys
+ output.
+
+ * keylist.c (list_keyblock_print), mainproc.c (list_node): Mark
+ revoked subkeys as revoked. Requested by Matthew Wilcox. Revoked
+ overrides expiration when both apply.
+
+ * keyedit.c (show_prefs): Use compress algo constants.
+ (show_basic_key_info): Make revoked and expired tags translatable.
+
+ * g10.c (rm_group): Properly ungroup from a list of groups.
+
+2004-01-30 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main, rm_group): Add --ungroup command to remove a
+ particular group.
+ (add_group): When adding a group with the same name as an already
+ existing group, merge the two groups.
+ (list_config): Show an error message when listing a config item
+ that doesn't exist.
+ (main): Replace -z0 trick for no compression.
+
+ * packet.h, keyedit.c (show_key_with_all_names_colon), keylist.c
+ (list_keyblock_colon), mainproc.c (list_node, proc_tree): Minor
+ cleanup to remove local_id, which is no longer used.
+
+2004-01-27 David Shaw <dshaw@jabberwocky.com>
+
+ * getkey.c: Set MAX_PK_CACHE_ENTRIES and MAX_UID_CACHE_ENTRIES to
+ PK_UID_CACHE_SIZE (set in ./configure).
+
+ * getkey.c (get_pubkey): When reading key data into the cache,
+ properly handle keys that are partially (pk, no UIDs) cached
+ already. This is Debian bug #176425 and #229549.
+
+ * compress.c (init_compress, push_compress_filter2): Do the right
+ thing (i.e. nothing) with compress algo 0.
+
+ * main.h, decrypt.c (decrypt_messages): Accept filenames to
+ decrypt on stdin. This is bug #253.
+
+2004-01-23 David Shaw <dshaw@jabberwocky.com>
+
+ * mainproc.c (list_node): Show sigs with --verbose.
+
+ * options.h, g10.c (set_screen_dimensions): New function to look
+ at COLUMNS and LINES.
+
+ * keyserver.c (parse_keyrec, keyserver_search_prompt), keyedit.c
+ (print_and_check_one_sig): Use new screen dimension variables.
+
+2004-01-21 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (list_config): New function to dump config options to
+ stdout. Currently requires --with-colons.
+ (collapse_args): New function to turn argc/argv into a single
+ string.
+ (main): Use it here to pass list_config() more than one argument
+ as a single string.
+ (print_algo_numbers): Helper to print algorithm number for
+ --list-config "pubkey", "cipher", "hash"/"digest", and "compress"
+ config options.
+
+ * packet.h, getkey.c (merge_selfsigs, merge_selfsigs_main),
+ pkclist.c (check_signatures_trust): Indicate who has revoked a key
+ (the owner or a designated revoker). If a key was revoked by
+ both, prefer the owner. If a subkey is already revoked by the
+ owner, don't allow a designated revokation of the whole key to
+ override this. We're still revoked either way, of course.
+
+ * keyedit.c (print_and_check_one_sig, keyedit_menu): Use the
+ COLUMNS environment variable (if any) to hint how wide the
+ terminal is. Disabled on _WIN32. Suggested by Janusz
+ A. Urbanowicz.
+
+2004-01-20 David Shaw <dshaw@jabberwocky.com>
+
+ * keylist.c (set_attrib_fd): Open attribute fd in binary
+ mode. This isn't meaningful on POSIX systems, but the Mingw builds
+ aren't exactly POSIX.
+
+ * trustdb.c (reset_trust_records): New, faster, implementation
+ that doesn't involve a keyring scan.
+ (clear_validity): Removed.
+
+ * g10.c (main), keydb.h, keydb.c (keydb_rebuild_caches),
+ keyring.h, keyring.c (keyring_rebuild_cache): Add "noisy" flag so
+ cache rebuilds can remain noisy when called for itself, and quiet
+ when called as part of the trustdb rebuild.
+
+ * trustdb.c (validate_keys): Rebuild the sig caches before
+ building the trustdb. Note that this is going to require some
+ architectual re-thinking, as it is agonizingly slow.
+
+2004-01-19 David Shaw <dshaw@jabberwocky.com>
+
+ * sig-check.c (check_key_signature2): Comments.
+
+ * keyring.c (keyring_rebuild_cache): Clear sig cache for any
+ signatures that we can no longer process (say, if the user removed
+ support for a necessary pubkey or digest algorithm).
+
+2004-01-16 David Shaw <dshaw@jabberwocky.com>
+
+ * misc.c (print_cipher_algo_note): May as well call Rijndael AES
+ at this point.
+
+ * keygen.c (do_create), misc.c (openpgp_pk_algo_usage): Remove the
+ last bits of Elgamal type 20 support.
+
+2004-01-03 Stefan Bellon <sbellon@sbellon.de>
+
+ * compress.c [__riscos__]: Only use RISC OS' own ZLib module if
+ configured to use it.
+
+2003-12-30 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), import.c (parse_import_options,
+ import_one, import_secret_one), keyserver.c (keyserver_refresh):
+ Change --merge-only to --import-option merge-only. Deprecate
+ --merge-only.
+
+2003-12-29 David Shaw <dshaw@jabberwocky.com>
+
+ * misc.c (pull_in_libs): Dead code. Removed.
+
+ * sig-check.c (check_revocation_keys): Comments.
+
+ * getkey.c (merge_selfsigs_main): Don't bother to check designated
+ revoker sigs if the key is already revoked.
+
+ * packet.h, getkey.c (merge_selfsigs_main): New "maybe_revoked"
+ flag on PKs. It is set when there is a revocation signature from
+ a valid revocation key, but the revocation key is not present to
+ verify the signature.
+
+ * pkclist.c (check_signatures_trust): Use it here to give a
+ warning when showing key trust.
+
+ * compress-bz2.c: Include stdio.h. Solaris 9 has a very old bzip2
+ library and we can at least guarantee that it won't fail because
+ of the lack of stdio.h.
+
+ * tdbio.c: Fixed format string bugs related to the use of DB_NAME.
+ Reported by Florian Weimer.
+
+2003-12-28 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), keyserver.c (keyserver_opts,
+ parse_keyserver_uri): honor-http-proxy is no longer an option
+ since we can do the same thing with http-proxy with no arguments.
+ Also remove broken-http-proxy since it can be better handled in
+ the HTTP helper.
+
+ * keyserver.c (argsep): New variation on strsep that knows about
+ optional arguments.
+ (parse_keyserver_options): Use it here for optional arguments.
+
+2003-12-28 Stefan Bellon <sbellon@sbellon.de>
+
+ * plaintext.c (handle_plaintext) [__riscos__]: Don't mangle
+ filename if the user specified it.
+
+ * g10.c, gpgv.c [__riscos__]: Removal of unnecessary #ifdef
+ __riscos__ sections.
+
+2003-12-27 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (strip_leading_space, get_arg): New.
+ (parse_keyserver_options): Use them here to allow arguments to
+ keyserver-options. Since none of our options need arguments yet,
+ just pass them through whole to the keyserver helper.
+
+ * main.h, misc.c (parse_options): Add a "noisy" flag to enable and
+ disable the messages about which option didn't match or matched
+ ambiguously. Change all callers (g10.c, keyserver.c).
+
+ * main.h, import.c (import_options), export.c (export_options):
+ Pass the noisy flag through.
+
+2003-12-17 David Shaw <dshaw@jabberwocky.com>
+
+ * build-packet.c (write_fake_data, do_secret_key), seckey-cert.c
+ (do_check): Use an unsigned length for mpi_get_opaque.
+
+ * options.h: It's impolite to assign -1 to an unsigned
+ opt.force_ownertrust.
+
+ * sig-check.c (cmp_help, do_check), sign.c (do_sign): Remove old
+ unused code.
+
+ * keyid.c (keyid_from_sk): Make sure lowbits is initialized.
+
+2003-12-12 David Shaw <dshaw@jabberwocky.com>
+
+ * sig-check.c (do_check): Move the signing algo and hash checks
+ from here...
+ (signature_check2): ... to here.
+ (check_key_signature2): ... and here. This is a minor
+ optimization to avoid fetching a key (which can be expensive,
+ especially if it is not self-signed, and there are many key
+ signatures on it which need to be checked for ultimate trust) if
+ the signature would have failed anyway because of algorithm or
+ hash problems.
+
+2003-12-10 David Shaw <dshaw@jabberwocky.com>
+
+ * packet.h, build-packet.c (hash_public_key): Remove function ...
+
+ * keydb.h, keyid.c (hash_public_key, do_fingerprint_md): ... and
+ make a new one here that shares code with the fingerprint
+ calculations. This removes some duplicated functionality, and is
+ also around 14% faster. (Every bit helps).
+
+ * import.c (import_one): No longer need the Elgamal import
+ warning.
+
+ * getkey.c (get_pubkey_fast): This one is sort of obscure.
+ get_pubkey_fast returns the primary key when requesting a subkey,
+ so if a user has a key signed by a subkey (we don't do this, but
+ used to), AND that key is not self-signed, AND the algorithm of
+ the subkey in question is not present in GnuPG, AND the algorithm
+ of the primary key that owns the subkey in question is present in
+ GnuPG, then we will try and verify the subkey signature using the
+ primary key algorithm and hit a BUG(). The fix is to not return a
+ hit if the keyid is not the primary. All other users of
+ get_pubkey_fast already expect a primary only.
+
+2003-12-09 David Shaw <dshaw@jabberwocky.com>
+
+ * keyid.c (do_fingerprint_md): Remove the rules to hash the old v3
+ Elgamal keys. They are no longer needed.
+
+ * keyid.c (keyid_from_sk, keyid_from_pk, fingerprint_from_pk,
+ fingerprint_from_sk): Enforce the v3-is-only-RSA rule. Anything
+ that isn't RSA gets a zero keyid and fingerprint.
+
+ * keyid.c (do_fingerprint_md): Properly handle hashing of keys
+ that we don't know the structure of by using the opaque MPI.
+ (do_fingerprint_md_sk): We cannot calculate the fingerprint from a
+ secret key unless we know the structure (since we can't leave off
+ the secret key parts), so fail early.....
+ (keyid_from_sk, fingerprint_from_sk): .... and return all zeroes.
+
+2003-12-03 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (strusage, main): Show development version warning in
+ --version output.
+ (main): Set --bzip2-compress-level to the default value at
+ startup. Remove --emulate-checksum-bug noop.
+
+ * options.h, g10.c (main), main.h, seskey.c (do_encode_md,
+ encode_md_value), sig-check.c (do_check), sign.c (do_sign): Remove
+ --emulate-md-encode-bug as it only applied to Elgamal signatures,
+ which are going away.
+
+2003-11-30 David Shaw <dshaw@jabberwocky.com>
+
+ * mainproc.c (proc_symkey_enc, proc_encrypted): Add ability to use
+ --override-session-key on --symmetric messages (new-style or
+ old-style).
+ (proc_pubkey_enc): Move code to show session key from here to
+ proc_encrypted() so it can work with any type of message.
+ Suggested by Michael Young.
+
+2003-11-29 David Shaw <dshaw@jabberwocky.com>
+
+ * trustdb.c (validate_keys): Reset the trustdb before checking if
+ we have any ultimately trusted keys. This ensures that if we lose
+ all our ultimately trusted keys, we don't leave behind the old
+ validity calculations. Noted by Peter Palfrader.
+
+ * revoke.c (gen_desig_revoke): Specify in the comment when a
+ designated revocation is generated.
+
+ * getkey.c (merge_selfsigs_main, merge_selfsigs_subkey,
+ get_seckey_byname2): Remove Elgamal check since we are removing
+ type 20 keys altogether.
+
+2003-11-27 David Shaw <dshaw@jabberwocky.com>
+
+ * pkclist.c (build_pk_list): Do not allow an empty PK list in
+ interactive mode.
+
+ * keygen.c (ask_algo): Remove ability to generate Elgamal
+ sign+encrypt keys.
+
+ * getkey.c (merge_selfsigs_main, merge_selfsigs_subkey,
+ get_seckey_byname2): Disallow use of sign+encrypt Elgamal keys.
+
+2003-11-20 David Shaw <dshaw@jabberwocky.com>
+
+ * seskey.c (do_encode_md): Comment about earlier (pre-PGP 2.3)
+ encodings.
+
+ * misc.c (compress_algo_to_string): Translate "Uncompressed".
+ Requested by Tommi Vainikainen.
+ (string_to_compress_algo): Include multi-string for
+ "uncompressed|none".
+
+2003-11-17 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), compress-bz2.c (init_uncompress): Add
+ --bz2-compress-lowmem to set bzlib "small" flag for low memory
+ (but slow) decompression.
+
+2003-11-15 David Shaw <dshaw@jabberwocky.com>
+
+ * compress.c (init_compress): Remove compress level 10 trick,
+ since it is no longer needed.
+
+ * g10.c: Fix typoed option name.
+
+ * compress-bz2.c (init_compress): Compression level 0 is not
+ meaningful for bzip2.
+
+ * options.h, g10.c (main), compress.c (init_compress),
+ compress-bz2.c (init_compress): Add --compress-level and
+ --bzip2-compress-level. -z sets them both. Change various
+ callers.
+
+ * encode.c (encode_simple), sign.c (sign_symencrypt_file):
+ Properly use default_compress_algo (--compress-algo, followed by
+ the highest --personal-compress-preference, followed by ZIP) to
+ get the algorithm.
+
+2003-11-14 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, trustdb.c (trust_model_string, init_trustdb): Add
+ support for "external" trust model, where the user can provide a
+ pregenerated trustdb.
+
+ * keyedit.c (keyedit_menu): Do not allow editing ownertrust with
+ an external trust model trustdb.
+
+2003-11-13 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c, keyedit.c, keylist.c, mainproc.c: Clarify the
+ plurarility (or not) of various list and verify options.
+
+2003-11-12 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): Add --symmetric --sign --encrypt.
+
+ * main.h, encode.c (setup_symkey): New. Prompt for a passphrase
+ and create a DEK for symmetric encryption.
+ (write_symkey_enc): New. Write out symmetrically encrypted
+ session keys.
+ (encode_crypt, encrypt_filter): Use them here here when creating a
+ message that can be decrypted with a passphrase or a pk.
+
+ * sign.c (sign_file): Call setup_symkey if we are doing a
+ --symmetric --sign --encrypt.
+
+2003-11-09 David Shaw <dshaw@jabberwocky.com>
+
+ * mainproc.c (proc_symkey_enc): Don't show algorithm information
+ when --quiet is set. Suggested by Duncan Harris. Also don't fail
+ with BUG() when processing a --symmetric message with a cipher we
+ don't have.
+
+ * g10.c: Alias --personal-xxx-prefs to --personal-xxx-preferences.
+
+ * pkclist.c (build_pk_list): When adding recipients interactively,
+ allow the user to stop at any point.
+
+2003-10-31 David Shaw <dshaw@jabberwocky.com>
+
+ * trustdb.h, trustdb.c (register_trusted_keyid): New. Adds a
+ keyid to the list of ultimately trusted keys.
+
+ * keygen.c (do_generate_keypair): Use it here so that the ultimate
+ ownertrust happens before the trustdb (might be) rebuilt. Also
+ fix an error where the newly generated pk is thought to be a
+ subkey by the trustdb.
+
+ * g10.c (main): Fix --export-all do actually do something
+ different than --export.
+
+ * pkclist.c (build_pk_list): Show all recipients rather than
+ showing each recipient as they are added.
+
+ * mainproc.c (proc_symkey_enc, proc_encrypted): Keep a count of
+ the number of passphrases that can decrypt a symmetric or mixed
+ symmetric/pk message and include it in the list of keys shown to
+ the user.
+
+2003-10-30 David Shaw <dshaw@jabberwocky.com>
+
+ * misc.c (compress_algo_to_string, string_to_compress_algo,
+ check_compress_algo): Add bzip2.
+
+ * compress.c (compress_filter): Make static to help force the use
+ of push_compress_filter. Remove default algorithm setting since
+ that is done in push_compress_filter now.
+
+ * main.h: Use named algorithm.
+
+ * filter.h, compress.c (push_compress_filter,
+ push_compress_filter2): New. Figure out which is the appropriate
+ compression filter to use, and push it into place.
+
+ * compress.c (handle_compressed), encode.c (encode_simple,
+ encode_crypt), sign.c (sign_file, sign_symencrypt_file), import.c
+ (read_block), export.c (do_export): Use push_compress_filter
+ instead of pushing the compression filter ourselves.
+
+ * compress-bz2.c: New. Bzlib versions of the compression filter
+ routines.
+
+ * Makefile.am: Include compress-bz2.c if bz2lib is available.
+
+2003-10-30 Werner Koch <wk@gnupg.org>
+
+ * apdu.c (close_ct_reader, close_pcsc_reader): Implemented.
+ (get_ccid_error_string): New. Not very useful messages, though.
+
+2003-10-29 Werner Koch <wk@gnupg.org>
+
+ * cardglue.c (open_card): Ask for card insertion.
+ (check_card_serialno): New.
+ (agent_scd_pksign, agent_scd_pkdecrypt): Use it here.
+ * cardglue.c (open_card): Issue insertion status message.
+ * status.h, status.c (STATUS_CARDCTRL): New.
+
+ * status.c (cpr_get_answer_okay_cancel): New.
+
+2003-10-28 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (list_keyblock_print): Denote secrets keys stored on a
+ card with an '>'. Print the '#' also for subkeys.
+ (list_keyblock_colon): Introduce new field 15 for sec/ssb to print
+ the serial number.
+
+2003-10-26 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): Enhance the version-specific config file code to
+ try for more specific matches before giving up (e.g. 1.3.3-cvs,
+ 1.3.3, 1.3, 1).
+
+2003-10-25 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): Add --symmetric --encrypt command. This generates
+ a message that can be decrypted via a passphrase or public key
+ system.
+
+ * main.h, encode.c (encode_seskey): Allow passing in an
+ already-created session key dek.
+ (encode_simple): Use the actual symmetric cipher when encrypting a
+ session key for a symmetric message.
+ (encode_crypt): Add a flag to trigger a hybrid mode that can be
+ decrypted via a passphrase or a pk. Change all callers.
+
+ * mainproc.c (symkey_decrypt_sesskey): There is no way to tell the
+ difference here between a bad passphrase and a cipher algorithm
+ that we don't have, so use a error message that makes that clear.
+ Use the actual list of ciphers when checking whether a cipher is
+ invalid. Return error if the decrypted cipher algorithm is
+ invalid.
+ (proc_symkey_enc): In a mixed passphrase/pk message, if a valid
+ dek already exists from decrypting via pk, do not try to process
+ the passphrase.
+ (proc_symkey_enc): Indicate when we're decrypting a session key as
+ opposed to decrypting data. If a passphrase is invalid, discard
+ the dek so we'll keep trying.
+
+2003-10-25 Werner Koch <wk@gnupg.org>
+
+ * ccid-driver.c (ccid_open_reader): Return an error if no USB
+ devices are found.
+
+ * Makefile.am: Replaced INTLLIBS by LIBINTL.
+
+ * g10.c (main) [ENABLE_CARD_SUPPORT]: Add a default for
+ --pcsc-driver.
+
+ * cardglue.c (learn_status_cb): Fixed faulty use of !space.
+
+2003-10-24 Werner Koch <wk@gnupg.org>
+
+ * apdu.c (apdu_open_reader): Hacks for PC/SC under Windows.
+
+2003-10-21 Werner Koch <wk@gnupg.org>
+
+ * passphrase.c (ask_passphrase): Add optional promptid arg.
+ Changed all callers.
+ * cardglue.c (pin_cb): Use it here, so the machine interface can
+ tell whether the Admin PIN is requested.
+
+ * cardglue.c (agent_scd_checkpin): New.
+
+ * misc.c (openpgp_pk_algo_usage): Added AUTH usage.
+
+ * app-openpgp.c (check_against_given_fingerprint): New. Factored
+ out that code elsewhere.
+ (do_check_pin): New.
+ * card-util.c (card_edit): New command "passwd". Add logic to
+ check the PIN in advance.
+ (card_status): Add new args to return the serial number. Changed
+ all callers.
+
+2003-10-14 David Shaw <dshaw@jabberwocky.com>
+
+ * import.c (import_one): Show the keyid when giving the Elgamal
+ slow import warning.
+
+ * g10.c (main): Older versions used --comment "" to indicate no
+ comment. Don't add an empty comment.
+
+2003-10-13 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (show_key_with_all_names): Ownertrust is only
+ meaningful for the PGP or classic trust models. Both validity and
+ ownertrust are not meaningful for the always trust model.
+
+2003-10-11 Werner Koch <wk@gnupg.org>
+
+ * keygen.c: Always enable the gen_card_key prototype.
+
+2003-10-10 Werner Koch <wk@gnupg.org>
+
+ * cardglue.c (card_close): New.
+ (agent_scd_change_pin): Implemented.
+
+ * ccid-driver.c (ccid_close_reader): New.
+ * apdu.c (close_ccid_reader, close_ct_reader, close_csc_reader)
+ (close_osc_reader, apdu_close_reader): New. Not all are properly
+ implemented yet.
+ * g10.c (g10_exit): Use close_card.
+
+2003-10-09 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): Give a deprecated option warning for
+ --show-keyring, --show-photos, --show-policy-url, --show-notation,
+ and their respective no- forms.
+
+ * options.skel: Remove show-photos and replace with
+ list/verify-options show-photos. Remove no-mangle-dos-filenames.
+
+ * misc.c (parse_options): Allow for incomplete (but unambiguous)
+ options.
+
+2003-10-09 Werner Koch <wk@gnupg.org>
+
+ * ccid-driver.c (ccid_transceive): Add T=1 chaining for sending.
+
+ * sign.c (do_sign) [!ENABLE_CARD_SUPPORT]: Return an error for
+ card keys.
+
+ * cardglue.c (agent_scd_pkdecrypt): Implemented.
+ * pubkey-enc.c (get_it) [ENABLE_CARD_SUPPORT]: Divert decryption
+ to card
+
+2003-10-08 Werner Koch <wk@gnupg.org>
+
+ * cardglue.c (pin_cb): Detect whether an admin or regular PIN is
+ requested.
+ (genkey_status_cb): New.
+ (agent_scd_genkey): Implemented.
+
+ * keygen.c (generate_keypair): New arg CARD_SERIALNO and prepare
+ parameters for on card key generation. Changed all callers.
+ (do_generate_keypair): Add new arg card and merged casrd specific
+ changes from 1.9.
+ (proc_parameter_file): New arg card, apss it down to
+ do_generate_keypair and changed all callers.
+ (gen_card_key): New.
+
+ * g10.c: Include cardclue.h.
+ (main): s/app_set_default_reader_port/card_set_reader_port/.
+ * cardglue.c (card_set_reader_port): New to address include file
+ issues.
+
+2003-10-02 Werner Koch <wk@gnupg.org>
+
+ * cardglue.c (learn_status_cb): Release values before assignment
+ so that it can be used by getattr to update the structure.
+ (agent_scd_getattr): New.
+
+ * keylist.c (print_pubkey_info): Add FP arg for optional printing
+ to a stream. Changed all callers.
+
+2003-10-01 Werner Koch <wk@gnupg.org>
+
+ * app-common.h, app-openpgp.c, iso7816.c, iso7816.h, apdu.c
+ * apdu.h, ccid-driver.c, ccid-driver.h, card-util.c: Updated
+ from current GnuPG 1.9. Changes over there are:
+ * card-util.c: Tweaked to use this source also under 1.3.
+ (card_edit): New command "forcesig".
+ * card-util.c (print_name, print_isoname): Use 0 and not LF fro
+ the max_n arg of tty_print_utf8_string2.
+ * card-util.c (change_pin): Simplified. We now have only a PIN
+ and an Admin PIN.
+ * ccid-driver.c: Detect GnuPG 1.3 and include appropriate files.
+ * apdu.c: Ditto.
+ * app-openpgp.c: Ditto.
+ * iso7816.c: Ditto.
+ (generate_keypair): Renamed to ..
+ (do_generate_keypair): .. this.
+ * app-common.h [GNUPG_MAJOR_VERSION]: New.
+ * iso7816.h [GNUPG_MAJOR_VERSION]: Include cardglue.h
+ * app-openpgp.c (do_change_pin): Make sure CVH1 and CHV2 are
+ always synced.
+ (verify_chv2, verify_chv3): New. Factored out common code.
+ (do_setattr, do_sign, do_auth, do_decipher): Change the names of
+ the prompts to match that we have only 2 different PINs.
+ (app_select_openpgp): Check whether the card enforced CHV1.
+ (convert_sig_counter_value): New. Factor out code from
+ get_sig_counter.
+
+ * Makefile.am (card_support_source): Depend on new AM conditional
+ to get the ifdef ENABLE_CARD_SUPPORT off the way from source
+ copied files.
+ (update-source-from-gnupg-2): Maintainer helper.
+
+2003-10-01 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): Add --no-groups to zero --group list.
+
+ * encode.c (encode_simple): Allow for 32 bytes (256 bits) of
+ symmetrically encrypted session key. Use --s2k-cipher-algo to
+ choose cipher, rather than the default cipher.
+
+ * parse-packet.c (parse_subkeyenc): Give a warning if an
+ symmetrically encrypted session key is seen without salt. Show in
+ --list-packets if a symetrically encrypted session key is present.
+
+ * pubkey-enc.c (get_it): Always show cipher-not-in-prefs warning
+ unless --quiet is set. Use text name of cipher in warning.
+
+2003-09-30 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), mainproc.c (check_sig_and_print): Add
+ --verify-option show-unusable-uids.
+
+ * gpgv.c (check_trustdb_stale): Stub.
+
+ * trustdb.c (get_validity): Move the up-to-date check to
+ check_trustdb_stale (new), so that it can be called before
+ validity is checked.
+
+ * keylist.c (list_keyblock_print): Disable the overall key
+ validity display until it can be thought about more. Use
+ check_trustdb_stale here to avoid putting the check warning in the
+ middle of a listed key.
+
+ * trustdb.c (init_trustdb): Only verify_own_keys() for those trust
+ models that it applies to (i.e. classic and OpenPGP).
+
+2003-09-29 Werner Koch <wk@gnupg.org>
+
+ * keygen.c (do_add_key_flags, parse_parameter_usage): Add support
+ the proposed AUTH key flag.
+ * getkey.c (fixup_uidnode, merge_selfsigs_main)
+ (merge_selfsigs_subkey, premerge_public_with_secret): Ditto.
+ * keylist.c (print_capabilities): Ditto.
+
+ * parse-packet.c (parse_key): Allow to parse the divert-to-card
+ S2K mode.
+ * build-packet.c (do_secret_key): Handle divert-to-card S2K
+ * seckey-cert.c (is_secret_key_protected): Ditto.
+ (check_secret_key): Ditto.
+
+ * keygen.c (do_ask_passphrase): Renamed from ask_passphrase.
+ * passphrase.c (ask_passphrase): New.
+
+2003-09-28 Werner Koch <wk@gnupg.org>
+
+ * g10.c (main): New commands --card-edit, --card-status and
+ --change-pin. New options --ctapi-driver, --pcsc-driver and
+ --disable-ccid
+ * options.h (DBG_CARD_IO): New.
+ * cardglue.c, cardclue.h: Enhanced.
+ * card-util.c: New. Taken from current the gnupg 1.9 branch.
+ * app-common.h, app-openpgp.c, iso7816.c, iso7816.h, apdu.c
+ * apdu.h, ccid-driver.c, ccid-driver.h: New. Takem from the current
+ gnupg 1.9 branch withy minor changes to include directives.
+ * Makefile.am: Added these files.
+
+2003-09-27 Werner Koch <wk@gnupg.org>
+
+ * sign.c (do_sign) [ENABLE_CARD_SUPPORT]: Divert to card.
+ * cardglue.c, cardglue.h: New.
+ * Makefile.am (gpg_LDADD): Added.
+ (card_support_sources): New.
+
+2003-09-25 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), keylist.c (list_keyblock_print): Add
+ "show-unusable-uids" list-option to show revoked and/or expired
+ user IDs.
+
+2003-09-24 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (show_key_with_all_names): Show names a little neater
+ by putting the [revoked] or [expired] in the space used for the
+ [validity]. There is also no point in showing "[unknown]
+ [revoked]".
+
+2003-09-23 David Shaw <dshaw@jabberwocky.com>
+
+ * sign.c (mk_notation_policy_etc): Capitalize "URL".
+
+ * trustdb.c (validate_keys): Give a little more information while
+ rebuilding trustdb.
+
+ * pkclist.c (do_edit_ownertrust): Clarify "don't know".
+
+ * g10.c (main): Default to --no-mangle-dos-filenames.
+
+ * keydb.h, keyring.c (keyring_search), trustdb.c (search_skipfnc):
+ Expand the skipfnc to include a pointer to the user ID that
+ matched.
+
+ * getkey.c (skip_disabled): Rename to skip_unusable, and add
+ checks for expired or revoked user IDs.
+
+2003-09-22 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): Deprecate --default-comment in favor of
+ --no-comments.
+
+ * options.h, g10.c (main), armor.c (armor_filter): Allow using
+ --comment multiple times to get multiple Comment: header lines.
+ --no-comments resets list.
+
+2003-09-11 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): Trim --help to commonly used options. Remove -f.
+
+2003-09-08 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): Error out if --multifile is used with the commands
+ that don't support it yet (--sign, --clearsign, --detach-sign,
+ --symmetric, and --store).
+
+ * g10.c (main): Add --multifile as an alias to turn --encrypt into
+ --encrypt-files (plus --verify-files, --decrypt-files).
+
+ * encode.c (use_mdc), g10.c (main): Use RFC1991 and RFC2440
+ directly to check for MDC usability. Do not set the force_mdc or
+ disable_mdc flags since there is no point any longer.
+
+2003-09-04 David Shaw <dshaw@jabberwocky.com>
+
+ * armor.c (parse_hash_header, armor_filter), g10.c (print_hex,
+ print_mds), pkclist.c (algo_available): Drop TIGER/192 support.
+
+2003-09-03 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (show_key_with_all_names): Fix assertion failure when
+ using toggle to see a secret key. Reported by Maxim Britov.
+
+2003-08-31 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (add_keyserver_url), keyedit.c (keyedit_menu), sign.c
+ (mk_notation_policy_etc): Clarify a few strings. It's a
+ "preferred keyserver URL".
+
+ * g10.c (main): Use "keyserver-url" instead of
+ "preferred-keyserver" for the sake of short and simple commands.
+
+2003-08-30 David Shaw <dshaw@jabberwocky.com>
+
+ * main.h, keygen.c (keygen_add_keyserver_url): Signature callback
+ for adding a keyserver URL.
+
+ * keyedit.c (keyedit_menu, menu_set_keyserver_url): New command to
+ set preferred keyserver to specified (or all) user IDs.
+
+ * build-packet.c (build_sig_subpkt): Set preferred keyserver flag
+ while building a preferred keyserver subpacket.
+
+ * keylist.c (show_policy_url, show_keyserver_url): URLs might be
+ UTF8.
+
+ * keyedit.c (menu_addrevoker): Fix leaking a few bytes.
+
+2003-08-29 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (show_key_with_all_names): Use list-option
+ show-long-keyid in main --edit-key display.
+
+ * keyedit.c (print_and_check_one_sig): Use list-option
+ show-long-keyid in --edit-key "check" function.
+
+2003-08-28 David Shaw <dshaw@jabberwocky.com>
+
+ * passphrase.c (agent_send_all_options): Make use of $GPG_TTY.
+
+ * g10.c (main): Disable use-agent if passphrase-fd is given
+ later. Suggested by Kurt Garloff.
+
+ * exec.c, g10.c, gpgv.c, passphrase.c, photoid.c:
+ s/__MINGW32__/_WIN32/ to help building on native Windows
+ compilers. Requested by Brian Gladman. From Werner on stable
+ branch.
+
+2003-08-25 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main): Add list-option
+ list-preferred-keyserver.
+
+ * keyedit.c (change_passphrase): When responding 'no' to the blank
+ passphrase question, re-prompt for a new passphrase. This is bug
+ #202.
+
+ * mainproc.c (check_sig_and_print): Use two different preferred
+ keyserver displays - one if the key is not present (to tell the
+ user where to get the key), the other if it is present (to tell
+ the user where the key can be refreshed).
+
+ * packet.h, parse-packet.c (parse_signature): Set flag if a
+ preferred keyserver is present.
+
+ * keylist.c (list_keyblock_print): Show keyserver url in listings
+ with list-option show-keyserver-url.
+
+2003-08-24 David Shaw <dshaw@jabberwocky.com>
+
+ * Makefile.am: Use NETLIBS instead of EGDLIBS.
+
+ * mainproc.c (check_sig_and_print): Get the uid validity before
+ printing any sig results to avoid munging the output with trustdb
+ warnings.
+
+ * g10.c (main): Don't include --show-keyring in --help as it is
+ deprecated.
+
+2003-08-21 David Shaw <dshaw@jabberwocky.com>
+
+ * gpgv.c: Remove extra semicolon (typo).
+
+ * options.skel: Note that keyserver.pgp.com isn't synchronized,
+ and explain the roundrobin a bit better.
+
+ * sig-check.c (check_key_signature2), import.c (import_one,
+ import_revoke_cert, chk_self_sigs, delete_inv_parts,
+ collapse_uids, merge_blocks): Make much quieter during import of
+ slightly munged, but recoverable, keys. Use log_error for
+ unrecoverable import failures.
+
+ * keyring.c (keyring_rebuild_cache): Comment.
+
+ * sign.c (mk_notation_and_policy): Making a v3 signature with
+ notations or policy urls is an error, not an info (i.e. increment
+ the errorcount). Don't print the notation or policy url to stdout
+ since it can be mixed into the output stream when piping and munge
+ the stream.
+
+2003-08-12 David Shaw <dshaw@jabberwocky.com>
+
+ * packet.h, sig-check.c (signature_check2, do_check,
+ do_check_messages): Provide a signing-key-is-revoked flag. Change
+ all callers.
+
+ * status.h, status.c (get_status_string): New REVKEYSIG status tag
+ for a good signature from a revoked key.
+
+ * mainproc.c (do_check_sig, check_sig_and_print): Use it here.
+
+ * import.c (import_revoke_cert, merge_blocks, merge_sigs): Compare
+ actual signatures on import rather than using keyid or class
+ matching. This does not change actual behavior with a key, but
+ does mean that all sigs are imported whether they will be used or
+ not.
+
+ * parse-packet.c (parse_signature): Don't give "signature packet
+ without xxxx" warnings for experimental pk algorithms. An
+ experimental algorithm may not have a notion of (for example) a
+ keyid (i.e. PGP's x.509 stuff).
+
+2003-08-02 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), keylist.c (list_keyblock_print),
+ keyedit.c (print_and_check_one_sig): New "show-sig-expire"
+ list-option to show signature expiration dates (if any).
+
+2003-07-24 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main, add_keyserver_url): Add
+ --sig-preferred-keyserver to implant a "where to get my key"
+ subpacket into a signature.
+
+ * sign.c (mk_notation_and_policy): Rename to
+ mk_notation_policy_etc and add preferred keyserver support for
+ signatures.
+
+2003-07-21 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (do_add_key_flags): Don't set the certify flag for
+ subkeys.
+ (ask_algo): Provide key flags for DSA, Elgamal_e, and Elgamal
+ subkeys.
+ (generate_keypair): Provide key flags for the default DSA/Elgamal
+ keys.
+
+ * sig-check.c (signature_check, signature_check2,
+ check_key_signature, check_key_signature2): Allow passing NULLs
+ for unused parameters in the x2 form of each function to avoid the
+ need for dummy variables. getkey.c, mainproc.c: Change all
+ callers.
+
+ * trustdb.h, trustdb.c (read_trust_options): New. Returns items
+ from the trustdb version record.
+
+ * keylist.c (public_key_list): Use it here for the new "tru"
+ record.
+
+ * gpgv.c (read_trust_options): Stub.
+
+2003-07-20 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (show_key_with_all_names): Use list-option
+ show-validity in --edit-key interface as well.
+
+2003-07-19 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), mainproc.c (check_sig_and_print): Add
+ verify-options "show-validity" and "show-long-keyid" to show
+ trustdb validity and long keyids during (file) signature
+ verification.
+
+ * packet.h, main.h, sig-check.c (signature_check2,
+ check_key_signature2, do_check): If ret_pk is set, fill in the pk
+ used to verify the signature. Change all callers in getkey.c,
+ mainproc.c, and sig-check.c.
+
+ * keylist.c (list_keyblock_colon): Use the ret_pk from above to
+ put the fingerprint of the signing key in "sig" records during a
+ --with-colons --check-sigs. This requires --no-sig-cache as well
+ since we don't cache fingerprints.
+
+2003-07-10 David Shaw <dshaw@jabberwocky.com>
+
+ * parse-packet.c (parse_signature): No need to reserve 8 bytes for
+ the unhashed signature cache any longer.
+
+ * misc.c (pct_expando): Add two new expandos - signer's
+ fingerprint (%g), and signer's primary fingerprint (%p).
+
+ * Makefile.am: Include W32LIBS where appropriate.
+
+ * g10.c (main): Add --rfc2440 alias for --openpgp since in a few
+ months, they won't be the same thing.
+
+ * keyserver.c (parse_keyserver_uri): Accept "http" as an alias for
+ "hkp", since it is occasionally written that way.
+ (keyserver_spawn): Use ascii_isspace to avoid locale issues.
+
+ * keygen.c (ask_user_id): Make --allow-freeform-uid apply to the
+ email field as well as the name field, and allow mixing fields
+ when it is set.
+
+ * options.skel: Use subkeys.pgp.net as the default keyserver.
+
+ * trustdb.c (validate_one_keyblock): Certifications on revoked or
+ expired uids do not count in the web of trust.
+
+ * signal.c (init_one_signal, pause_on_sigusr, do_block): Only use
+ sigprocmask() if we have sigset_t, and only use sigaction() if we
+ have struct sigaction. This is for Forte c89 on Solaris which
+ seems to define only the function call half of the two pairs by
+ default.
+ (pause_on_sigusr): Typo.
+ (do_block): If we can't use sigprocmask() and sigset_t, try to get
+ the number of signals from NSIG as well as MAXSIG, and if we
+ can't, fail with an explanation.
+
+ * signal.c, tdbio.c: Comment out the transaction code. It was not
+ used in this version, and was causing some build problems on
+ quasi-posix platforms (Solaris and Forte c89).
+
+ * keylist.c (list_keyblock_colon): Don't include validity values
+ when listing secret keys since they can be incorrect and/or
+ misleading. This is a temporary kludge, and will be handled
+ properly in 1.9/2.0.
+
+ * mainproc.c (check_sig_and_print): Only show the "key available
+ from" preferred keyserver line if the key is not currently
+ present.
+
+ * keyedit.c (sign_uids): Do not sign expired uids without --expert
+ (same behavior as revoked uids). Do not allow signing a user ID
+ without a self-signature. --expert overrides. Add additional
+ prompt to the signature level question.
+ (menu_expire): When changing expiration dates, don't replace
+ selfsigs on revoked uids since this would effectively unrevoke
+ them. There is also no point in replacing expired selfsigs. This
+ is bug #181
+
+2003-07-10 David Shaw <dshaw@jabberwocky.com> (from Werner on stable branch)
+
+ * g10.c (add_notation_data): Make sure that only ascii is passed
+ to iscntrl. Noted by Christian Biere.
+ * getkey.c (classify_user_id2): Replaced isspace by spacep
+ * keygen.c (ask_user_id): Ditto.
+ (get_parameter_algo): Ditto.
+ * keyedit.c (keyedit_menu): Ditto.
+ * tdbdump.c (import_ownertrust): Ditto. s/isxdigit/hexdigitp/.
+ * revoke.c (ask_revocation_reason):
+ * keyserver.c (keyserver_spawn): Dito.
+
+2003-06-10 Werner Koch <wk@gnupg.org>
+
+ * parse-packet.c (parse): Disallow old style partial length for
+ all key material packets to avoid possible corruption of keyrings.
+
+2003-06-08 Werner Koch <wk@gnupg.org>
+
+ * import.c (import_keys_internal): Invalidate the cache so that
+ the file descriptor gets closed. Fixes bug reported by Juan
+ F. Codagnone.
+
+2003-06-04 David Shaw <dshaw@jabberwocky.com>
+
+ * options.skel: Use new hkp://subkeys.pgp.net as sample keyserver
+ since they at least handle subkeys correctly.
+
+ * options.h, g10.c (main), main.h, keylist.c (show_keyserver_url),
+ mainproc.c (check_sig_and_print), parse-packet.c (dump_sig_subpkt,
+ parse_one_sig_subpkt, can_handle_critical): Add read-only support
+ for preferred keyserver subpackets. They're basically policy URLs
+ with a different name. Add a verify-option
+ "show-preferred-keyserver" to turn them on and off (on by default,
+ as per stable branch).
+
+ * g10.c (main): Add "--set-notation" as alias to "--notation-data"
+ this is to make things consistent with --set-policy-url meaning
+ both sigs and certs.
+
+2003-06-03 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), keylist.c (list_keyblock_print): Add
+ "show-validity" and "show-long-keyid" list-options.
+
+ * gpgv.c (get_validity, trust_value_to_string): Stubs.
+
+ * g10.c (main): Use SAFE_VERSION instead of VERSION in the
+ version-specific gpg.conf file so it can be overridden on RISCOS.
+
+2003-06-01 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main), keylist.c (show_policy_url, show_notation),
+ mainproc.c (check_sig_and_print): Emulate the old policy and
+ notation behavior (display by default). Send to status-fd whether
+ it is displayed on the screen or not.
+
+ * g10.c (main): Since we now have some options in devel that won't
+ work in a stable branch gpg.conf file, try for a version-specific
+ gpg.conf-VERSION file before falling back to gpg.conf.
+
+ * main.h, options.h: Move various option flags to options.h.
+
+2003-05-31 David Shaw <dshaw@jabberwocky.com>
+
+ * mainproc.c (check_sig_and_print), main.h, keylist.c
+ (show_policy, show_notation): Collapse the old print_notation_data
+ into show_policy() and show_notation() so there is only one
+ function to print notations and policy URLs.
+
+ * options.h, main.h, g10.c (main), keyedit.c
+ (print_and_check_one_sig), keylist.c (list_one,
+ list_keyblock_print), pkclist.c (do_edit_ownertrust), sign.c
+ (mk_notation_and_policy): New "list-options" and "verify-options"
+ commands. These replace the existing
+ --show-photos/--no-show-photos,
+ --show-notation/--no-show-notation,
+ --show-policy-url/--no-show-policy-url, and --show-keyring
+ options. The new method is more flexible since a user can specify
+ (for example) showing photos during sig verification, but not in
+ key listings. The old options are emulated.
+
+ * main.h, misc.c (parse_options): New general option line
+ parser. Fix the bug in the old version that did not handle report
+ syntax errors after a valid entry.
+
+ * import.c (parse_import_options), export.c
+ (parse_export_options): Call it here instead of duplicating the
+ code.
+
+2003-05-30 David Shaw <dshaw@jabberwocky.com>
+
+ * keylist.c (list_one): Don't show the keyring filename when in
+ --with-colons mode. Actually translate "Keyring" string.
+
+ * mainproc.c (proc_tree): We can't currently handle multiple
+ signatures of different classes or digests (we'd pretty much have
+ to run a different hash context for each), but if they are all the
+ same, make an exception. This is Debian bug #194292.
+
+ * sig-check.c (check_key_signature2): Make string translatable.
+
+ * packet.h, getkey.c (fixup_uidnode): Mark real primary uids
+ differently than assumed primaries.
+
+ * keyedit.c (no_primary_warning): Use the differently marked
+ primaries here in a new function to warn when an --edit-key
+ command might rearrange the self-sig dates enough to change which
+ uid is primary.
+ (menu_expire, menu_set_preferences): Use no_primary_warning()
+ here.
+
+ * Makefile.am: Use @DLLIBS@ for -ldl.
+
+2003-05-26 David Shaw <dshaw@jabberwocky.com>
+
+ * getkey.c (premerge_public_with_secret): Made "no secret subkey
+ for" warning a verbose item and translatable. (From wk on stable
+ branch)
+
+ * sig-check.c (check_key_signature2): Made "no subkey for subkey
+ binding packet" a verbose item instead of a !quiet one. There are
+ too many garbled keys out in the wild. (From wk on stable branch)
+
+ * filter.h: Remove const from WHAT. (From wk on stable branch)
+
+ * progress.c (handle_progress): Store a copy of
+ NAME. (progress_filter): Release WHAT, make sure not to print a
+ NULL WHAT. (From wk on stable branch)
+
+ * openfile.c (open_sigfile): Adjust free for new progress
+ semantics. (From wk on stable branch)
+
+ * plaintext.c (ask_for_detached_datafile): Don't dealloc
+ pfx->WHAT. (From wk on stable branch)
+
+ * seckey-cert.c (do_check): Issue the RSA_OR_IDEA status when the
+ cipher algo is IDEA to make it easier to track down the
+ problem. (From twoaday on stable branch)
+
+2003-05-24 David Shaw <dshaw@jabberwocky.com>
+
+ * armor.c, g10.c, kbnode.c, misc.c, pkclist.c, sign.c,
+ build-packet.c, getkey.c, keydb.c, openfile.c, plaintext.c,
+ status.c, gpgv.c, keygen.c, options.h, sig-check.c, tdbio.h,
+ encode.c, mainproc.c, parse-packet.c, signal.c, textfilter.c: Edit
+ all preprocessor instructions to remove whitespace before the '#'.
+ This is not required by C89, but there are some compilers out
+ there that don't like it.
+
+2003-05-21 David Shaw <dshaw@jabberwocky.com>
+
+ * trustdb.h, trustdb.c (is_disabled), gpgv.c (is_disabled): Rename
+ is_disabled to cache_disabled_value, which now takes a pk and not
+ just the keyid. This is for speed since there is no need to
+ re-fetch a key when we already have that key handy. Cache the
+ result of the check so we don't need to hit the trustdb more than
+ once.
+
+ * getkey.c (skip_disabled): New function to get a pk and call
+ is_disabled on it. (key_byname): Use it here.
+
+ * packet.h, getkey.c (skip_disabled), keylist.c
+ (print_capabilities): New "pk_is_disabled" macro to retrieve the
+ cached disabled value if available, and fill it in via
+ cache_disabled_value if not available.
+
+ * trustdb.c (get_validity): Cache the disabled value since we have
+ it handy and it might be useful later.
+
+ * parse-packet.c (parse_key): Clear disabled flag when parsing a
+ new key. Just in case someone forgets to clear the whole key.
+
+ * getkey.c (merge_selfsigs_main): Add an "if all else fails" path
+ for setting a single user ID primary when there are multiple set
+ primaries all at the same second, or no primaries set and the most
+ recent user IDs are at the same second, or no signed user IDs at
+ all. This is arbitrary, but deterministic.
+
+ * exec.h, photoid.h: Add copyright message.
+
+ * keylist.c (list_keyblock_print): Don't dump attribs for
+ revoked/expired/etc uids for non-colon key listings. This is for
+ consistency with --show-photos.
+
+ * main.h, keylist.c (dump_attribs), mainproc.c
+ (check_sig_and_print): Dump attribs if --attrib-fd is set when
+ verifying signatures.
+
+ * g10.c (main): New --gnupg option to disable the various
+ --openpgp, --pgpX, etc. options. This is the same as --no-XXXX
+ for those options.
+
+ * revoke.c (ask_revocation_reason): Clear old reason if user
+ elects to repeat question. This is bug 153.
+
+ * keyedit.c (sign_uids): Show keyid of the key making the
+ signature.
+
+2003-05-21 Werner Koch <wk@gnupg.org>
+
+ * progress.c (handle_progress)
+ * sign.c (write_plaintext_packet)
+ * encode.c (encode_simple,encode_crypt): Make sure that a filename
+ of "-" is considered to be stdin so that iobuf_get_filelength
+ won't get called. This fixes bug 156 reported by Gregery Barton.
+
+2003-05-02 David Shaw <dshaw@jabberwocky.com>
+
+ * packet.h, build-packet.c (build_sig_subpkt), export.c
+ (do_export_stream), import.c (remove_bad_stuff, import),
+ parse-packet.c (dump_sig_subpkt, parse_one_sig_subpkt): Remove
+ vestigal code for the old sig cache subpacket. This wasn't
+ completely harmless as it caused subpacket 101 to disappear on
+ import and export.
+
+ * options.h, armor.c, cipher.c, g10.c, keyedit.c, pkclist.c,
+ sign.c, encode.c, getkey.c, revoke.c: The current flags for
+ different levels of PGP-ness are massively complex. This is step
+ one in simplifying them. No functional change yet, just use a
+ macro to check for compliance level.
+
+ * sign.c (sign_file): Fix bug that causes spurious compression
+ preference warning.
+
+ * sign.c (clearsign_file): Fix bug that prevents proper warning
+ message from appearing when clearsigning in --pgp2 mode with a
+ non-v3 RSA key.
+
+ * main.h, misc.c (compliance_option_string, compliance_string,
+ compliance_failure), pkclist.c (build_pk_list), sign.c (sign_file,
+ clearsign_file), encode.c (encode_crypt,
+ write_pubkey_enc_from_list): New functions to put the "this
+ message may not be usable...." warning in one place.
+
+ * options.h, g10.c (main): Part two of the simplification. Use a
+ single enum to indicate what we are compliant to (1991, 2440,
+ PGPx, etc.)
+
+ * g10.c (main): Show errors for failure in export, send-keys,
+ recv-keys, and refresh-keys.
+
+ * options.h, g10.c (main): Give algorithm warnings for algorithms
+ chosen against the --pgpX and --openpgp rules.
+
+ * keydb.h, pkclist.c (algo_available): Make TIGER192 invalid in
+ --openpgp mode.
+
+ * sign.c (sign_file), pkclist.c (algo_available): Allow passing a
+ hint of 0.
+
+2003-05-01 David Shaw <dshaw@jabberwocky.com>
+
+ * tdbio.c (create_version_record): Only create new trustdbs with
+ TM_CLASSIC or TM_PGP.
+
+ * trustdb.h, trustdb.c (trust_string, get_ownertrust_string,
+ get_validity_string, ask_ownertrust, validate_keys), pkclist.c
+ (do_edit_ownertrust): Rename trust_string to trust_value_to_string
+ for naming consistency.
+
+ * trustdb.h, trustdb.c (string_to_trust_value): New function to
+ translate a string to a trust value.
+
+ * g10.c (main): Use string_to_trust_value here for
+ --force-ownertrust.
+
+ * options.h, g10.c (main), trustdb.c (trust_model_string,
+ init_trustdb, check_trustdb, update_trustdb, get_validity,
+ validate_one_keyblock): An "OpenPGP" trust model is misleading
+ since there is no official OpenPGP trust model. Use "PGP"
+ instead.
+
+2003-04-30 David Shaw <dshaw@jabberwocky.com>
+
+ * build-packet.c (build_sig_subpkt): Comments.
+
+ * exec.c (exec_write): Cast NULL to void* to properly terminate
+ varargs list.
+
+ * keyedit.c (show_key_with_all_names): Just for safety, catch an
+ invalid pk algorithm.
+
+ * sign.c (make_keysig_packet): Crucial that the call to mksubpkt
+ comes LAST before the calls to finalize the sig as that makes it
+ possible for the mksubpkt function to get a reliable pointer to
+ the subpacket area.
+
+ * pkclist.c (do_we_trust_pre): If an untrusted key was chosen by a
+ particular user ID, use that ID as the one to ask about when
+ prompting whether to use the key anyway.
+ (build_pk_list): Similar change here when adding keys to the
+ recipient list.
+
+ * trustdb.c (update_validity): Fix bug that prevented more than
+ one validity record per trust record.
+ (get_validity): When retrieving validity for a (user) supplied
+ user ID, return the validity for that user ID only, and do not
+ fall back to the general key validity.
+ (validate_one_keyblock): Some commentary on whether
+ non-self-signed user IDs belong in the web of trust (arguably,
+ they do).
+
+2003-04-27 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): Add --no-textmode.
+
+ * export.c (do_export_stream), keyedit.c (show_key_with_all_names,
+ menu_addrevoker), mainproc.c (check_sig_and_print), photoid.c
+ (show_photos), sign.c (mk_notation_and_policy), trustdb.c
+ (get_validity, reset_trust_records, validate_keys): Make some
+ strings translatable.
+
+ * mainproc.c (check_sig_and_print): Show digest algorithm and sig
+ class when verifying a sig with --verbose on, and add version, pk
+ and hash algorithms and sig class to VALIDSIG.
+
+ * parse-packet.c (enum_sig_subpkt): Make a warning message a
+ --verbose warning message since we don't need to warn every time
+ we see an unknown critical (we only need to invalidate the
+ signature).
+
+ * trustdb.c (init_trustdb): Check the trustdb options even with
+ TM_AUTO since the auto may become TM_CLASSIC or TM_OPENPGP.
+
+2003-04-26 David Shaw <dshaw@jabberwocky.com>
+
+ * sign.c (do_sign): Show the hash used when making a signature in
+ verbose mode.
+
+ * tdbio.h, tdbio.c (tdbio_read_model): New function to return the
+ trust model used in a given trustdb.
+
+ * options.h, g10.c (main), trustdb.c (init_trustdb, check_trustdb,
+ update_trustdb): Use tdbio_read_model to implement an "auto" trust
+ model which is set via the trustdb.
+
+2003-04-23 David Shaw <dshaw@jabberwocky.com>
+
+ * import.c (import_revoke_cert): Remove ultimate trust when
+ revoking an ultimately trusted key.
+
+ * keyedit.c (sign_uids): Allow replacing expired signatures.
+ Allow duplicate signatures with --expert.
+
+ * pkclist.c (check_signatures_trust): Don't display a null
+ fingerprint when checking a signature with --always-trust enabled.
+
+ * filter.h (progress_filter_context_t), progress.c
+ (handle_progress), plaintext.c (ask_for_detached_datafile,
+ hash_datafiles): Fix compiler warnings. Make "what" constant.
+
+ * build-packet.c (do_plaintext): Do not create invalid literal
+ packets with >255-byte names.
+
+2003-04-15 Werner Koch <wk@gnupg.org>
+
+ * Makefile.am (AM_CFLAGS): Make use of AM_CFLAGS and AM_LDFLAGS.
+
+ * g10.c, options.h: New option --enable-progress-filter.
+ * progress.c (handle_progress): Make use of it.
+
+2003-04-15 Marcus Brinkmann <marcus@g10code.de>
+
+ * progress.c: New file.
+ * Makefile.am (common_source): Add progress.c.
+ * filter.h (progress_filter_context_t): New type.
+ (progress_filter, handle_progress): New prototypes.
+ * main.h (open_sigfile): New argument for prototype.
+ * openfile.c (open_sigfile): New argument to install progress
+ filter.
+ * encode.c (encode_simple): New variable PFX. Register
+ progress filter. Install text_filter after that.
+ (encode_crypt): Likewise.
+ * sign.c (sign_file): Likewise.
+ (clearsign_file): Likewise.
+ * decrypt.c (decrypt_message): Likewise.
+ (decrypt_messages): Likewise.
+ * verify.c (verify_signatures): Likewise.
+ (verify_one_file): Likewise.
+ * plaintext.c (hash_datafiles): Likewise.
+ (ask_for_detached_datafile): Likewise.
+
+2003-04-10 Werner Koch <wk@gnupg.org>
+
+ * passphrase.c (read_passphrase_from_fd): Do a dummy read if the
+ agent is to be used. Noted by Ingo Klöcker.
+ (agent_get_passphrase): Inhibit caching when we have no
+ fingerprint. This is required for key generation as well as for
+ symmetric only encryption.
+
+ * passphrase .c (agent_get_passphrase): New arg CANCELED.
+ (passphrase_to_dek): Ditto. Passed to above. Changed all
+ callers to pass NULL.
+ * seckey-cert.c (do_check): New arg CANCELED.
+ (check_secret_key): Terminate loop when canceled.
+
+ * keyedit.c (change_passphrase): Pass ERRTEXT untranslated to
+ passphrase_to_dek and translate where appropriate.
+ * seckey-cert.c (check_secret_key): Ditto.
+ * keygen.c (ask_passphrase): Ditto.
+ * passphrase.c (agent_get_passphrase): Translate the TRYAGAIN_TEXT.
+ Switch the codeset to utf-8.
+
+2003-04-09 Werner Koch <wk@gnupg.org>
+
+ * decrypt.c (decrypt_messages): Fixed error handling; the function
+ used to re-loop with same file after an error. Reported by Joseph
+ Walton.
+
+2003-04-08 David Shaw <dshaw@jabberwocky.com>
+
+ * main.h, g10.c (main), import.c (parse_import_options,
+ fix_pks_corruption): It's really PKS corruption, not HKP
+ corruption. Keep the old repair-hkp-subkey-bug command as an
+ alias.
+
+ * g10.c (main): Rename --no-version to --no-emit-version for
+ consistency. Keep --no-version as an alias.
+
+2003-04-04 David Shaw <dshaw@jabberwocky.com>
+
+ * pkclist.c (algo_available): PGP 8 can use the SHA-256 hash.
+
+ * sign.c (sign_file, clearsign_file, sign_symencrypt_file): Remove
+ unused code.
+
+2003-04-01 Werner Koch <wk@gnupg.org>
+
+ * mainproc.c (check_sig_and_print): Add primary key fpr to VALIDSIG
+ status.
+
+2003-03-24 David Shaw <dshaw@jabberwocky.com>
+
+ * keydb.h: Err on the side of making an unknown signature a SIG
+ rather than a CERT.
+
+ * import.c (delete_inv_parts): Discard any key signatures that
+ aren't key types (i.e. 0x00, 0x01, etc.)
+
+ * g10.c (main): Add deprecated option warning for
+ --list-ownertrust. Add --compression-algo alias for
+ --compress-algo. Change --version output strings to match
+ "showpref" strings, and make translatable.
+
+ * status.c (do_get_from_fd): Accept 'y' as well as 'Y' for
+ --command-fd boolean input.
+
+ * trustdb.c: Fix typo (DISABLE_REGEXP -> DISABLE_REGEX)
+
+ * keyedit.c (show_key_with_all_names_colon): Show no-ks-modify
+ flag.
+
+2003-03-11 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), keyserver.c (kopts): Add "try-dns-srv"
+ keyserver option. Defaults to on.
+
+ * passphrase.c (agent_get_passphrase): Fix memory leak with
+ symmetric messages. Fix segfault with symmetric messages. Fix
+ incorrect prompt with symmetric messages.
+
+2003-03-10 Werner Koch <wk@gnupg.org>
+
+ * compress.c (init_uncompress): Use a 15 bit window size so that
+ the output of implementations which don't run for PGP 2
+ compatibility won't get garbled.
+
+2003-03-04 David Shaw <dshaw@jabberwocky.com>
+
+ * trustdb.c (validate_keys): Mask the ownertrust when building the
+ list of fully valid keys so that disabled keys are still counted
+ in the web of trust.
+ (get_ownertrust_with_min): Do the same for the minimum ownertrust
+ calculation.
+
+ * parse-packet.c (dump_sig_subpkt): Show the notation names for
+ not-human-readable notations. Fix cosmetic off-by-one length
+ counter.
+
+ * options.skel: Add explantion and commented-out
+ "no-mangle-dos-filenames".
+
+ * mainproc.c (proc_encrypted): Make string translatable.
+
+ * keyserver.c (keyserver_spawn): Quote ':', '%', and any 8-bit
+ characters in the uid strings sent to the keyserver helper.
+
+ * keyring.c (keyring_rebuild_cache): Lock the keyring while
+ rebuilding the signature caches to prevent another gpg from
+ tampering with the temporary copy.
+
+ * keygen.c (keygen_set_std_prefs): Include AES192 and AES256 in
+ default prefs.
+
+ * keyedit.c (show_prefs): Make strings translatable.
+
+ * keydb.c: Double the maximum number of keyrings to 40.
+
+ * gpgv.c (main): Fix bug #113 - gpgv should accept the
+ --ignore-time-conflict option.
+
+ * g10.c (main): --openpgp disables --pgpX. Double the amount of
+ secure memory to 32k (keys are getting bigger these days).
+
+ * Makefile.am: Makefile.am: Use @CAPLIBS@ to link in -lcap if we
+ are using capabilities.
+
+2003-02-26 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_spawn): Include various pieces of
+ information about the key in the data sent to the keyserver
+ helper. This allows the helper to use it in instructing a remote
+ server which may not have any actual OpenPGP smarts in parsing
+ keys.
+
+ * main.h, export.c (export_pubkeys_stream, do_export_stream): Add
+ ability to return only the first match in an exported keyblock for
+ keyserver usage. This should be replaced at some point with a
+ more flexible solution where each key can be armored seperately.
+
+2003-02-22 David Shaw <dshaw@jabberwocky.com>
+
+ * sign.c (sign_file): Do not push textmode filter onto an unopened
+ IOBUF (segfault). Noted by Marcus Brinkmann. Push and
+ reinitialize textmode filter for each file in a multiple file
+ list.
+
+ * packet.h, getkey.c (fixup_uidnode), keyedit.c (show_prefs): Set
+ and show the keyserver no-modify flag.
+
+ * keygen.c (add_keyserver_modify): New.
+ (keygen_upd_std_prefs): Call it here.
+ (keygen_set_std_prefs): Accept "ks-modify" and "no-ks-modify" as
+ prefs to set and unset keyserver modify flag.
+
+ * g10.c (main): Accept "s1" in addition to "idea" to match the
+ other ciphers.
+
+ * main.h, misc.c (idea_cipher_warn): We don't need this if IDEA
+ has been disabled.
+
+2003-02-21 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (keygen_set_std_prefs): Don't put AES or CAST5 in
+ default prefs if they are disabled.
+
+ * g10.c (main): Use 3DES instead of CAST5 if we don't have CAST5
+ support. Use 3DES for the s2k cipher in --openpgp mode.
+ (print_mds): #ifdef all of the optional digest algorithms.
+
+2003-02-12 David Shaw <dshaw@jabberwocky.com>
+
+ * keydb.h, getkey.c (classify_user_id, classify_user_id2): Make
+ 'exact' a per-desc item. Merge into one function since
+ 'force_exact' is no longer needed.
+ (key_byname): Use new classify_user_id function, and new exact
+ flag in KEYDB_SEARCH_DESC.
+
+ * keyring.h, keyring.c (keyring_search): Return an optional index
+ to show which KEYDB_SEARCH_DESC was the matching one.
+
+ * keydb.h, keydb.c (keydb_search): Rename to keydb_search2, and
+ pass the optional index to keyring_search. Add a macro version of
+ keydb_search that calls this new function.
+
+ * export.c (do_export_stream): If the keyid! syntax is used,
+ export only that specified key. If the key in question is a
+ subkey, export the primary plus that subkey only.
+
+2003-02-11 David Shaw <dshaw@jabberwocky.com>
+
+ * exec.c (set_exec_path): Add debugging line.
+
+ * g10.c (print_hex, print_mds): Print long hash strings a lot
+ neater. This assumes at least an 80-character display, as there
+ are a few other similar assumptions here and there. Users who
+ need unformatted hashes can still use with-colons. Check that
+ SHA384 and 512 are available before using them as they are no
+ longer always available.
+
+ * Makefile.am: Use a local copy of libexecdir along with @PACKAGE@
+ as GNUPG_LIBEXECDIR so it can be easily overridden at make time.
+
+2003-02-04 David Shaw <dshaw@jabberwocky.com>
+
+ * armor.c (parse_hash_header, armor_filter): Accept the new SHAs
+ in the armor Hash: header.
+
+ * g10.c (print_hex): Print long hash strings a little neater.
+ (print_mds): Add the new SHAs to the hash list.
+
+2003-02-02 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (menu_revuid): Properly handle a nonselfsigned uid on
+ a v4 key (treat as a v4 revocation).
+
+ * import.c (print_import_check): Do not re-utf8 convert user IDs.
+
+2003-01-27 David Shaw <dshaw@jabberwocky.com>
+
+ * mainproc.c (list_node): Show signature expiration date in
+ with-colons sig records.
+
+ * keylist.c (list_keyblock_colon), mainproc.c (list_node): Show
+ trust sig information in with-colons sig records.
+
+2003-01-16 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (add_group): Trim whitespace after a group name so it does
+ not matter where the user puts the = sign.
+
+ * options.skel: Comment out the first three lines in case someone
+ manually copies the skel file to their homedir.
+
+ * sign.c (clearsign_file): Only use pgp2mode with v3 keys and
+ MD5. This matches what we do when decoding such messages and
+ prevents creating a message (v3+RIPEMD/160) that we can't verify.
+
+ * sig-check.c (signature_check2): Use G10ERR_GENERAL as the error
+ for signature digest conflict. BAD_SIGN implies that a signature
+ was checked and we may try and print out a user ID for a key that
+ doesn't exist.
+
+2003-01-15 David Shaw <dshaw@jabberwocky.com>
+
+ * trustdb.c (init_trustdb, get_validity): Don't use a changed
+ trust model to indicate a dirty trustdb, and never auto-rebuild a
+ dirty trustdb with the "always" trust model.
+
+ * g10.c (add_group): Last commit missed the \t ;)
+
+2003-01-14 David Shaw <dshaw@jabberwocky.com>
+
+ * packet.h, parse-packet.c (setup_user_id), free-packet.c
+ (free_user_id), keydb.h, keyid.c (namehash_from_uid): New function
+ to rmd160-hash the contents of a user ID packet and cache it in
+ the uid object.
+
+ * keylist.c (list_keyblock_colon): Use namehash in field 8 of
+ uids. Show dates for creation (selfsig date), and expiration in
+ fields 6 and 7.
+
+ * trustdb.c (get_validity, get_validity_counts, update_validity):
+ Use new namehash function rather than hashing it locally.
+
+2003-01-14 Werner Koch <wk@gnupg.org>
+
+ * g10.c (add_group): Fixed group parsing to allow more than one
+ delimiter in a row and also allow tab as delimiter.
+
+2003-01-12 David Shaw <dshaw@jabberwocky.com>
+
+ * tdbio.c (tdbio_set_dbname): Fix assertion failure with
+ non-fully-qualified trustdb names.
+
+2003-01-11 David Shaw <dshaw@jabberwocky.com>
+
+ * trustdb.c (get_validity_info, get_ownertrust_info,
+ trust_letter): Simplify by returning a ? for error directly.
+
+ * keyedit.c (show_key_with_all_names): Use get_validity_string and
+ get_ownertrust_string to show full word versions of trust
+ (i.e. "full" instead of 'f').
+
+ * trustdb.h, trustdb.c (get_ownertrust_string,
+ get_validity_string): Same as get_ownertrust_info, and
+ get_validity_info, except returns a full string.
+
+ * trustdb.c (get_ownertrust_with_min): New. Same as
+ 'get_ownertrust' but takes the min_ownertrust value into account.
+
+2003-01-10 David Shaw <dshaw@jabberwocky.com>
+
+ * armor.c (armor_filter): Comment about PGP's end of line tab
+ problem.
+
+ * trustdb.h, trustdb.c (trust_letter): Make
+ static. (get_ownertrust_info, get_validity_info): Don't mask the
+ trust level twice.
+
+ * trustdb.h, gpgv.c, trustdb.c (get_validity, get_validity_info),
+ keylist.c (list_keyblock_colon), keyedit.c
+ (show_key_with_all_names_colon, menu_revuid): Pass a user ID in
+ rather than a namehash, so we only have to do the hashing in one
+ place.
+
+ * packet.h, pkclist.c (build_pk_list), free-packet.c
+ (release_public_key_parts): Remove unused namehash element for
+ public keys.
+
+2003-01-07 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (keygen_set_std_prefs): Warn when setting an IDEA
+ preference when IDEA is not available.
+
+2003-01-06 David Shaw <dshaw@jabberwocky.com>
+
+ * trustdb.c (get_validity_info): 'd' for disabled is not a
+ validity value any more.
+
+ * packet.h, tdbio.h, tdbio.c (tdbio_read_record,
+ tdbio_write_record), trustdb.c (update_validity): Store temporary
+ full & marginal counts in the trustdb.
+ (clear_validity, get_validity_counts): Return and clear temp
+ counts.
+ (store_validation_status): Keep track of which keyids have been
+ stored.
+ (validate_one_keyblock, validate_key_list): Use per-uid copies of
+ the full & marginal counts so they can be recalled for multiple
+ levels.
+ (validate_keys): Only use unused keys for each new round.
+ (reset_unconnected_keys): Rename to reset_trust_records, and only
+ skip specifically excluded records.
+
+ * keylist.c (print_capabilities): Show 'D' for disabled keys in
+ capabilities section.
+
+ * trustdb.c (is_disabled): Remove incorrect comment.
+
+2003-01-03 David Shaw <dshaw@jabberwocky.com>
+
+ * import.c (import_one): Only do the work to create the status
+ display for interactive import if status is enabled.
+
+ * keyring.c (keyring_search): skipfnc didn't work properly with
+ non-keyid searches. Noted by Stefan Bellon.
+
+ * getkey.c (merge_selfsigs_main): Remove some unused code and make
+ sure that the pk selfsigversion member accounts for 1F direct
+ sigs.
+
+2003-01-02 Werner Koch <wk@gnupg.org>
+
+ * keydb.c (keydb_add_resource): Don't assume that try_make_homedir
+ terminates but check again for the existence of the directory and
+ continue then.
+ * openfile.c (copy_options_file): Print a warning if the skeleton
+ file has active options.
+
+2002-12-29 David Shaw <dshaw@jabberwocky.com>
+
+ * getkey.c (merge_selfsigs_main), main.h, sig-check.c
+ (check_key_signature2): Pass the ultimately trusted pk directly to
+ check_key_signature2 to avoid going through the key selection
+ mechanism. This prevents a deadly embrace when two keys without
+ selfsigs each sign the other.
+
+2002-12-27 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_refresh): Don't print the "refreshing..."
+ line if there are no keys to refresh or if there is no keyserver
+ set.
+
+ * getkey.c (merge_selfsigs_main): Any valid user ID should make a
+ key valid, not just the last one. This also fixes Debian bug
+ #174276.
+
+2002-12-27 Stefan Bellon <sbellon@sbellon.de>
+
+ * import.c (print_import_check): Changed int to size_t.
+
+2002-12-27 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (keyedit_menu, menu_revuid): Add "revuid" feature to
+ revoke a user ID. This is the same as issuing a revocation for
+ the self-signature, but a much simpler interface to do it.
+
+2002-12-26 David Shaw <dshaw@jabberwocky.com>
+
+ * keydb.h, getkey.c (key_byname): Flag to enable or disable
+ including disabled keys. Keys specified via keyid (i.e. 0x...)
+ are always included.
+
+ * getkey.c (get_pubkey_byname, get_seckey_byname2,
+ get_seckey_bynames), keyedit.c (keyedit_menu, menu_addrevoker):
+ Include disabled keys in these functions.
+
+ * pkclist.c (build_pk_list): Do not include disabled keys for -r
+ or the key prompt. Do include disabled keys for the default key
+ and --encrypt-to.
+
+ * trustdb.h, trustdb.c (is_disabled): New skipfnc for skipping
+ disabled keys.
+
+ * gpgv.c (is_disabled): Stub.
+
+ * keygen.c (keygen_add_key_expire): Properly handle updating a key
+ expiration to a no-expiration value.
+
+ * keyedit.c (enable_disable_key): Comment.
+
+ * import.c (import_one): When in interactive mode and --verbose,
+ don't repeat some key information twice.
+
+2002-12-22 Timo Schulz <ts@winpt.org>
+
+ * import.c (print_import_check): New.
+ (import_one): Use it here.
+ Use merge_keys_and_selfsig in the interactive mode to avoid
+ wrong key information.
+ * status.h: Add new status code.
+ * status.c: Ditto.
+
+2002-12-13 David Shaw <dshaw@jabberwocky.com>
+
+ * pkclist.c (do_we_trust): Tweak language to refer to the "named
+ user" rather than "owner". Noted by Stefan Bellon.
+
+ * trustdb.h, trustdb.c (trustdb_pending_check): New function to
+ check if the trustdb needs a check.
+
+ * import.c (import_keys_internal): Used here so we don't rebuild
+ the trustdb if it is still clean.
+ (import_one, chk_self_sigs): Only mark trustdb dirty if the key
+ that is being imported has any sigs other than self-sigs.
+ Suggested by Adrian von Bidder.
+
+ * options.skel: Include the required '=' sign in the sample
+ 'group' option. Noted by Stefan Bellon.
+
+ * import.c (chk_self_sigs): Don't try and check a subkey as if it
+ was a signature.
+
+2002-12-11 David Shaw <dshaw@jabberwocky.com>
+
+ * tdbio.c (tdbio_read_record, tdbio_write_record): Compact the
+ RECTYPE_TRUST records a bit.
+
+ * g10.c (main): Comment out --list-trust-path until it can be
+ implemented.
+
+ * import.c (import_one): Warn when importing an Elgamal primary
+ that this may take some time (to verify self-sigs).
+ (chk_self_sigs): Try and cache all self-sigs so the keyblock is
+ written to the keyring with a good rich cache.
+
+ * keygen.c (ask_algo): Make the Elgamal sign+encrypt warning
+ stronger, and remove the RSA sign+encrypt warning.
+
+2002-12-06 Stefan Bellon <sbellon@sbellon.de>
+
+ * options.h: Fixed typo (mangle_dos_names instead of
+ mangle_dos_filenames).
+
+2002-12-05 Werner Koch <wk@gnupg.org>
+
+ * g10.c: New options --[no-]mangle-dos-filenames.
+ * options.h (opt): Added mangle-dos-filenames.
+ * openfile.c (open_outfile) [USE_ONLY_8DOT3]: Truncate the
+ filename only when this option is set; this is the default.
+
+2002-12-04 David Shaw <dshaw@jabberwocky.com>
+
+ * main.h, keyedit.c, keygen.c: Back out previous (2002-12-01)
+ change. Minimal isn't always best.
+
+ * sign.c (update_keysig_packet): Use the current time rather then
+ a modification of the original signature time. Make sure that
+ this doesn't cause a time warp.
+
+ * keygen.c (keygen_add_key_expire): Properly handle a key
+ expiration date in the past (use a duration of 0).
+
+ * keyedit.c (menu_expire): Use update_keysig_packet so any sig
+ subpackets are maintained during the update.
+
+ * build-packet.c (build_sig_subpkt): Mark sig expired or unexpired
+ when the sig expiration subpacket is added.
+ (build_sig_subpkt_from_sig): Handle making an expiration subpacket
+ from a sig that has already expired (use a duration of 0).
+
+ * packet.h, sign.c (update_keysig_packet), keyedit.c
+ (menu_set_primary_uid, menu_set_preferences): Add ability to issue
+ 0x18 subkey binding sigs to update_keysig_packet and change all
+ callers.
+
+ * trustdb.c (validate_keys): Show trust parameters when building
+ the trustdb, and make sure that the version record update was
+ successful.
+ (init_trustdb): If the current parameters aren't what was used for
+ building the trustdb, the trustdb is invalid.
+
+ * tbio.c (tdbio_db_matches_options): Update to work with new
+ trustdbs.
+
+2002-12-03 David Shaw <dshaw@jabberwocky.com>
+
+ * tdbio.h, tdbio.c (tdbio_read_record, tdbio_write_record): Store
+ trust model in the trustdb version record.
+ (tdbio_update_version_record): New function to update version
+ record values during a trustdb check or update.
+ (tdbio_dump_record): Show trust model in dump.
+
+ * trustdb.c (validate_keys): Call tdbio_update_version_record on
+ success so that the correct options are stored in the trustdb.
+
+ * options.h: rearrange trust models so that CLASSIC is 0 and
+ OPENPGP is 1.
+
+ * options.h, g10.c (main), encode.c (write_pubkey_enc_from_list),
+ pkclist.c (algo_available), revoke.c (gen_revoke): Add --pgp8
+ mode. This is basically identical to --pgp7 in all ways except
+ that signing subkeys, v4 data sigs (including expiration), and SK
+ comments are allowed.
+
+ * getkey.c (finish_lookup): Comment.
+
+ * main.h, keylist.c (reorder_keyblock), keyedit.c (keyedit_menu):
+ Reorder user ID display in the --edit-key menu to match that of
+ the --list-keys display.
+
+ * g10.c (add_notation_data): Fix initialization.
+
+2002-12-01 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (menu_expire): Don't lose key flags when changing the
+ expiration date of a subkey. This is not the most optimal
+ solution, but it is minimal change on the stable branch.
+
+ * main.h, keygen.c (do_copy_key_flags): New function to copy key
+ flags, if any, from one sig to another.
+ (do_add_key_expire): New function to add key expiration to a sig.
+ (keygen_copy_flags_add_expire): New version of
+ keygen_add_key_expire that also copies key flags.
+ (keygen_add_key_flags_and_expire): Use do_add_key_expire.
+
+ * import.c (fix_hkp_corruption): Comment.
+
+2002-11-25 Stefan Bellon <sbellon@sbellon.de>
+
+ * plaintext.c (handle_plaintext) [__riscos__]: If nooutput is set,
+ no filetype is needed obviously.
+
+2002-11-24 David Shaw <dshaw@jabberwocky.com>
+
+ * main.h, misc.c (default_cipher_algo, default_compress_algo):
+ New. Return the default algorithm by trying
+ --cipher-algo/--compress-algo, then the first item in the pref
+ list, then s2k-cipher-algo or ZIP.
+
+ * sign.c (sign_file, sign_symencrypt_file), encode.c
+ (encode_simple, encode_crypt): Call default_cipher_algo and
+ default_compress_algo to get algorithms.
+
+ * g10.c (main): Allow pref selection for compress algo with
+ --openpgp.
+
+ * mainproc.c (proc_encrypted): Use --s2k-digest-algo for
+ passphrase mangling rather than --digest-algo.
+
+ * sign.c (hash_for): If --digest-algo is not set, but
+ --personal-digest-preferences is, then use the first hash
+ algorithm in the personal list. If the signing algorithm is DSA,
+ then use the first 160-bit hash algorithm in the personal list.
+ If --pgp2 is set and it's a v3 RSA key, use MD5.
+
+ * g10.c (main), keydb.c (keydb_add_resource,
+ keydb_locate_writable): Rename --default-keyring as
+ --primary-keyring. Stefan wins the naming contest.
+
+2002-11-23 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (add_notation_data): Disallow notation names that do not
+ contain a '@', unless --expert is set. This is to help prevent
+ people from polluting the (as yet unused) IETF namespace.
+
+ * main.h: Comments about default algorithms.
+
+ * photoid.c (image_type_to_string): Comments about 3-letter file
+ extensions.
+
+ * encode.c (encode_simple), passphrase.c (passphrase_to_dek),
+ sign.c (sign_symencrypt_file): Use --s2k-digest-algo for
+ passphrase mangling rather than --digest-algo.
+
+2002-11-21 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (keygen_set_std_prefs): Properly handle an empty
+ preference string.
+
+ * misc.c (string_to_compress_algo): "none" is a bad choice since
+ it conflicts with the "none" in setpref.
+
+2002-11-14 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): Allow compression algorithm names as the argument
+ to --compress-algo. The old algorithm names still work for
+ backwards compatibility.
+
+ * misc.c (string_to_compress_algo): Allow "none" as an alias for
+ "uncompressed".
+
+2002-11-13 Stefan Bellon <sbellon@sbellon.de>
+
+ * getkey.c (get_pubkey_byfprint_fast): Fixed type incompatibility,
+ was unsigned char instead of byte.
+
+2002-11-13 David Shaw <dshaw@jabberwocky.com>
+
+ * encode.c (encode_simple): Make sure that files larger than about
+ 4G use partial length encoding. This is required because OpenPGP
+ allows only for 32 bit length fields. From Werner on stable
+ branch.
+
+ * getkey.c (get_pubkey_direct): Renamed to...
+ (get_pubkey_fast): this and made extern.
+ (get_pubkey_byfprint_fast): New. From Werner on stable branch.
+
+ * keydb.h, import.c (import_one): Use get_pubkey_fast instead of
+ get_pubkey. We don't need a merged key and actually this might
+ lead to recursions.
+ (revocation_present): Likewise for search by fingerprint. From
+ Werner on stable branch.
+
+ * g10.c (main): Try to create the trustdb even for non-colon-mode
+ list-key operations. This is required because getkey needs to
+ know whether a a key is ultimately trusted. From Werner on stable
+ branch.
+
+ * exec.c [__CYGWIN32__]: Keep cygwin separate from Mingw32;
+ we don't need it here as it behaves more like a Posix system.
+ From Werner on stable branch.
+
+ * passphrase.c (agent_get_passphrase): Ditto. From Werner on
+ stable branch.
+
+ * tdbio.c (MY_O_BINARY): Need binary mode with Cygwin. From
+ Werner on stable branch.
+
+ * g10.c, gpgv.c (main) [__CYGWIN32__]: Don't get the homedir from
+ the registry. From Werner on stable branch.
+
+ * keyedit.c (show_key_with_all_names_colon): Make --with-colons
+ --edit display match the validity and trust of --with-colons
+ --list-keys.
+
+ * passphrase.c (agent_send_all_options): Fix compile warning.
+
+ * keylist.c (list_keyblock_colon): Validity for subkeys should
+ match that of the primary key, and not that of the last user ID.
+
+ * getkey.c (merge_selfsigs): Revoked/expired/invalid primary keys
+ carry these facts onto all their subkeys, but only after the
+ subkey has a chance to be marked valid. This is to fix an
+ incorrect "invalid public key" error verifying a signature made by
+ a revoked signing subkey, with a valid unrevoked primary key.
+
+2002-11-09 Werner Koch <wk@gnupg.org>
+
+ * passphrase.c (agent_send_all_options): Use tty_get_ttyname to
+ get the default ttyname.
+
+2002-11-07 David Shaw <dshaw@jabberwocky.com>
+
+ * keyring.h, keyring.c (keyring_register_filename): Return the
+ pointer if a given keyring is registered twice.
+
+ * keydb.h, keydb.c (keydb_add_resource): Use flags to indicate a
+ default keyring.
+ (keydb_locate_writable): Prefer the default keyring if possible.
+
+ * g10.c (main): Add --default-keyring option.
+
+2002-11-06 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), trustdb.c (ask_ownertrust): Add
+ --force-ownertrust option for debugging purposes. This allows
+ setting a whole keyring to a given trust during an
+ --update-trustdb. Not for normal use - it's just easier than
+ hitting "4" all the time to test a large trustdb.
+
+ * pubkey-enc.c (get_session_key): With hidden recipients or try a
+ given passphrase against all secret keys rather than trying all
+ secret keys in turn. Don't if --try-all-secrets or --status-fd is
+ enabled.
+
+ * passphrase.c (passphrase_to_dek): Mode 1 means do a regular
+ passphrase query, but don't prompt with the key info.
+
+ * seckey-cert.c (do_check, check_secret_key): A negative ask count
+ means to enable passphrase mode 1.
+
+ * keydb.h, getkey.c (enum_secret_keys): Add flag to include
+ secret-parts-missing keys (or not) in the list.
+
+2002-11-05 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_search_prompt): When --with-colons is
+ enabled, don't try and fit the search output to the screen size -
+ just dump the whole list.
+
+2002-11-04 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_search_prompt): When --with-colons is
+ enabled, just dump the raw keyserver protocol to stdout and don't
+ print the menu.
+
+ * keyserver.c (show_prompt): Don't show a prompt when command-fd
+ is being used.
+
+ * trustdb.c (trust_model_string, check_trustdb, update_trustdb,
+ validate_one_keyblock): It's not clear what a trustdb rebuild or
+ check means with a trust model other than "classic" or "openpgp",
+ so disallow this.
+
+2002-11-03 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main): Add --trust-model option. Current
+ models are "openpgp" which is classic+trustsigs, "classic" which
+ is classic only, and "always" which is the same as the current
+ option --always-trust (which still works). Default is "openpgp".
+
+ * trustdb.c (validate_one_keyblock): Use "openpgp" trust model to
+ enable trust sigs.
+
+ * gpgv.c (main), mainproc.c (check_sig_and_print), pkclist.c
+ (do_we_trust, do_we_trust_pre, check_signatures_trust): Use new
+ --trust-model option in place of --always-trust.
+
+ * keyedit.c (sign_mk_attrib, trustsig_prompt, sign_uids,
+ keyedit_menu): Prompt for and create a trust signature with
+ "tsign". This is functional, but needs better UI text.
+
+ * build-packet.c (build_sig_subpkt): Able to build trust and
+ regexp subpackets.
+
+ * pkclist.c (do_edit_ownertrust): Comment.
+
+2002-11-02 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (set_one_pref, keygen_set_std_prefs): Allow using the
+ full algorithm name (CAST5, SHA1) rather than the short form (S3,
+ H2).
+
+ * main.h, keygen.c (keygen_get_std_prefs), keyedit.c
+ (keyedit_menu): Return and use a fake uid packet rather than a
+ string since we already have a nice parser/printer in
+ keyedit.c:show_prefs.
+
+ * main.h, misc.c (string_to_compress_algo): New.
+
+2002-11-01 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): Add --no-throw-keyid.
+
+ * keydb.h, encode.c (write_pubkey_enc_from_list), g10.c (main),
+ pkclist.c (build_pk_list): Add --hidden-recipient (-R) and
+ --hidden-encrypt-to, which do a single-user variation on
+ --throw-keyid. The "hide this key" flag is carried in bit 0 of
+ the pk_list flags field.
+
+ * keyserver.c (parse_keyrec): Fix shadowing warning.
+
+2002-10-31 Stefan Bellon <sbellon@sbellon.de>
+
+ * compress.c (init_compress) [__riscos__]: Use
+ riscos_load_module() to load ZLib module.
+
+ * g10.c (main) [__riscos__]: Renames due to changes in riscos.c
+ (e.g. prefixes all RISC OS specific functions with riscos_*).
+ * photoid.c (show_photos) [__riscos__]: Likewise.
+ * signal.c (got_fatal_signal) [__riscos__]: Likewise.
+
+ * trustdb.c (check_regexp) [__riscos__]: Branch to RISC OS RegEx
+ handling.
+
+2002-10-31 David Shaw <dshaw@jabberwocky.com>
+
+ * build-packet.c (do_plaintext), encode.c (encode_sesskey,
+ encode_simple, encode_crypt), sign.c (write_plaintext_packet): Use
+ wipememory() instead of memset() to wipe sensitive memory as the
+ memset() might be optimized away.
+
+2002-10-30 David Shaw <dshaw@jabberwocky.com>
+
+ * trustdb.c (check_regexp): Modern regexps require REG_EXTENDED.
+
+2002-10-29 David Shaw <dshaw@jabberwocky.com>
+
+ * packet.h, trustdb.h, trustdb.c (trust_string): New. Return a
+ string like "fully trusted", "marginally trusted", etc.
+ (get_min_ownertrust): New. Return minimum ownertrust.
+ (update_min_ownertrust): New. Set minimum ownertrust.
+ (check_regexp): New. Check a regular epression against a user ID.
+ (ask_ownertrust): Allow specifying a minimum value.
+ (get_ownertrust_info): Follow the minimum ownertrust when
+ returning a letter.
+ (clear_validity): Remove minimum ownertrust when a key becomes
+ invalid.
+ (release_key_items): Release regexp along with the rest of the
+ info.
+ (validate_one_keyblock, validate_keys): Build a trust sig chain
+ while validating. Call check_regexp for regexps. Use the minimum
+ ownertrust if the user does not specify a genuine ownertrust.
+
+ * pkclist.c (do_edit_ownertrust): Only allow user to select a
+ trust level greater than the minimum value.
+
+ * parse-packet.c (can_handle_critical): Can handle critical trust
+ and regexp subpackets.
+
+ * trustdb.h, trustdb.c (clear_ownertrusts), delkey.c
+ (do_delete_key), import.c (import_one): Rename clear_ownertrust to
+ clear_ownertrusts and have it clear the min_ownertrust value as
+ well.
+
+ * keylist.c (list_keyblock_print): Indent uid to match pub and
+ sig.
+
+ * keyedit.c (print_and_check_one_sig, show_key_and_fingerprint,
+ menu_addrevoker), keylist.c (list_keyblock_print,
+ print_fingerprint): Show "T" or the trust depth for trust
+ signatures, and add spaces to some strings to make room for it.
+
+ * packet.h, parse-packet.c (dump_sig_subpkt, parse_one_sig_subpkt,
+ parse_signature): Parse trust signature values.
+
+ * tdbio.h, tdbio.c (tdbio_read_record, tdbio_write_record):
+ Reserve a byte for the minimum ownertrust value (for use with
+ trust signatures).
+
+2002-10-29 Stefan Bellon <sbellon@sbellon.de>
+
+ * build-packet.c (calc_plaintext, do_plaintext): Removed RISC OS
+ specific filetype parts (it's now done in make_basename()).
+
+ * plaintext.c (handle_plaintext): Tidied up RISC OS specific
+ filetype parts.
+
+ * encode.c (encode_simple, encode_crypt): Added argument to
+ make_basename() call.
+
+ * sign.c (write_plaintext_packet): Added argument to
+ make_basename() call.
+
+2002-10-28 Stefan Bellon <sbellon@sbellon.de>
+
+ * build-packet.c (calc_plaintext, do_plaintext): Added filetype
+ handling for RISC OS' file types.
+
+ * plaintext.c (handle_plaintext) [__riscos__]: Added filetype
+ handling for RISC OS' file types.
+
+2002-10-23 David Shaw <dshaw@jabberwocky.com>
+
+ * main.h, import.c (sec_to_pub_keyblock, import_secret_one,
+ parse_import_options), g10.c (main): New import-option
+ "convert-sk-to-pk" to convert a secret key into a public key
+ during import. It is on by default.
+
+2002-10-23 Werner Koch <wk@gnupg.org>
+
+ * pubkey-enc.c (get_it): Fix segv, test for revoked only when PK
+ has been assigned.
+
+2002-10-18 Timo Schulz <ts@winpt.org>
+
+ * keylist.c: (print_pubkey_info): New.
+ (print_seckey_info): New.
+ * main.h: Prototypes for the new functions.
+ * delkey.c (do_delete_key): Use it here.
+ * revoke.c (gen_desig_revoke): Ditto.
+
+2002-10-17 Werner Koch <wk@gnupg.org>
+
+ * pkclist.c (do_edit_ownertrust): Show all user IDs. This should
+ be enhanced to also show the current trust level. Suggested by
+ Florian Weimer.
+
+2002-10-17 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): Handle --strict and --no-strict from the command
+ line before the options file is loaded.
+
+2002-10-15 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): Disable --textmode when encrypting (symmetric or
+ pk) in --pgp2 mode as PGP 2 can't handle the unknown length
+ literal packet. Reported by Michael Richardson.
+
+2002-10-14 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver-internal.h, keyserver.c (print_keyrec, parse_keyrec,
+ show_prompt, keyserver_search_prompt, keyserver_spawn): Go to
+ version 1 of the keyserver protocol. This is a better design,
+ similar to --with-colons, that allows for keys with multiple user
+ IDs rather than using multiple keys. It also matches the machine
+ readable pksd format. Also use a prettier --search-keys listing
+ format that can fill different size windows (currently set at 24
+ lines).
+
+2002-10-12 Werner Koch <wk@gnupg.org>
+
+ * keygen.c (print_status_key_created): New.
+ (do_generate_keypair): Use it to print the fingerprint.
+ (generate_subkeypair): Likewise.
+
+2002-10-11 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (menu_addrevoker): Properly back out if the signature
+ fails. Also, do not allow appointing the same revoker twice, and
+ report ALREADY_SIGNED if the user tries it.
+
+2002-10-07 David Shaw <dshaw@jabberwocky.com>
+
+ * import.c (import_keys_internal): Missed one s/inp/inp2/.
+
+ * keylist.c (print_capabilities): Properly indicate per-key
+ capabilities of sign&encrypt primary keys that have
+ secret-parts-missing (i.e. no capabilities at all)
+
+ * mainproc.c (symkey_decrypt_sesskey): Fix compiler warning.
+
+2002-10-04 David Shaw <dshaw@jabberwocky.com>
+
+ * getkey.c (get_pubkey_direct): Don't cache keys retrieved via
+ this function as they may not have all their fields filled in.
+
+ * sig-check.c (signature_check2): Use new is_primary flag to check
+ rather than comparing main_keyid with keyid as this still works in
+ the case of a not fully filled in pk.
+
+2002-10-04 Werner Koch <wk@gnupg.org>
+
+ * import.c (import_keys_internal): s/inp/inp2/ to avoid shadowing
+ warning.
+
+ * passphrase.c (agent_get_passphrase): Fixed signed/unsigned char
+ problem in %-escaping. Noted by Ingo Klöcker.
+
+2002-10-03 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main): Add --strict and --no-strict to switch
+ the log_warning severity level from info to error.
+
+ * keylist.c (print_capabilities): Secret-parts-missing keys should
+ show that fact in the capabilities, and only primary signing keys
+ can certify other keys.
+
+ * packet.h, parse_packet.c (parse_key): Add is_primary flag for
+ public keys (it already exists for secret keys).
+
+2002-10-02 David Shaw <dshaw@jabberwocky.com>
+
+ * import.c (import_secret_one): Check for an illegal (>110)
+ protection cipher when importing a secret key.
+
+ * keylist.c (list_keyblock_print): Show a '#' for a
+ secret-parts-missing key.
+
+ * parse_packet.c (parse_key): Some comments.
+
+ * revoke.c (gen_revoke): Remove some debugging code.
+
+ * trustdb.c (verify_own_keys): Make trusted-key a non-deprecated
+ option again.
+
+ * seckey-cert.c (do_check): Don't give the IDEA warning unless the
+ cipher in question is in fact IDEA.
+
+2002-10-01 David Shaw <dshaw@jabberwocky.com>
+
+ * import.c (import_one): Make sure that a newly imported key
+ starts with a clean ownertrust.
+
+2002-10-01 Werner Koch <wk@gnupg.org>
+
+ * getkey.c (get_pubkey_direct): New.
+ (merge_selfsigs_main): Use it here to look for an ultimately
+ trusted key. Using the full get_pubkey might lead to an
+ infinitive recursion.
+
+2002-09-29 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (parse_keyserver_uri): Force the keyserver URI
+ scheme to lowercase to be case-insensitive.
+
+2002-09-28 David Shaw <dshaw@jabberwocky.com>
+
+ * export.c (do_export_stream): Comment.
+
+ * sig-check.c (check_key_signature2): Properly handle a
+ non-designated revocation import.
+
+2002-09-26 Werner Koch <wk@gnupg.org>
+
+ * g10.c (set_homedir): New. Changed all direct assignments to use
+ this.
+ * gpgv.c (set_homedir): Ditto.
+
+2002-09-25 David Shaw <dshaw@jabberwocky.com>
+
+ * Makefile.am: Link gpg with EGDLIBS (i.e. NETLIBS) as EGD uses
+ sockets. Remove the old NETLIBS variable since the keyserver
+ stuff is no longer internal.
+
+2002-09-24 David Shaw <dshaw@jabberwocky.com>
+
+ * import.c (import_keys_stream): Fix compiler type warning.
+
+ * keyring.c (keyring_rebuild_cache), sig-check.c
+ (check_key_signature2), import.c (import, chk_self_sigs): Minor
+ language cleanups.
+
+2002-09-23 Stefan Bellon <sbellon@sbellon.de>
+
+ * main.h: Introduced fast-import as import option. Removed
+ fast as separate option from prototypes.
+ * import.c (parse_import_options): Added fast-import option.
+ (import_*): Removed fast as separate option.
+ * g10.c (main): Added option fast-import, removed old fast
+ as separate argument.
+ * keyserver.c (keyserver_spawn): Removed old fast as separate
+ argument.
+
+2002-09-22 Stefan Bellon <sbellon@sbellon.de>
+
+ * import.c (import_keys, import_keys_stream,
+ import_keys_internal): Added trustdb update/check to key import if
+ not fast-import and interactive set/no-auto-check-trustdb unset.
+ Avoided function clone by introducing import_keys_internal.
+
+2002-09-19 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_spawn): Properly handle line truncation.
+ Don't leak memory (~10-20 bytes) on searches.
+ (keyserver_search_prompt): Cleanup.
+
+ * keylist.c (list_keyblock_colon): Show 1F direct key signatures
+ in --with-colons listing.
+
+2002-09-16 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (menu_addrevoker): The direct key signature for
+ revocation keys must be at least v4 to carry the revocation key
+ subpacket. Add a PGP 2.x warning for revocation keys.
+
+2002-09-14 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (check_permissions): Rearrange strings to make translating
+ easier (don't incorporate string parts).
+
+ * keyedit.c (sign_uids): Make strings translatable.
+
+ * sig-check.c (check_key_signature2): Make string translatable.
+
+2002-09-13 David Shaw <dshaw@jabberwocky.com>
+
+ * getkey.c (check_revocation_keys): Move....
+ * main.h, sig-check.c (check_revocation_keys): to here. Also
+ return the signature_check error code rather than 0/1 and cache
+ the sig result.
+
+ * sig-check.c (check_key_signature2): Divert to
+ check_revocation_keys if a revocation sig is made by someone other
+ than the pk owner.
+
+ * getkey.c (merge_selfsigs_main): Tidy.
+
+2002-09-13 Werner Koch <wk@gnupg.org>
+
+ * g10.c (main) [__MINGW32__]: Activate oLoadExtension.
+
+2002-09-12 David Shaw <dshaw@jabberwocky.com>
+
+ * Makefile.am, hkp.c, hkp.h, keyserver.c (keyserver_work): Remove
+ internal HKP support.
+
+ * keyserver.c (keyserver_spawn): Remove whitespace after keyserver
+ commands.
+
+2002-09-10 David Shaw <dshaw@jabberwocky.com>
+
+ * exec.c (expand_args): Remove loop left over from earlier
+ implementation.
+ (exec_write): Missed one tick.
+
+2002-09-10 Werner Koch <wk@gnupg.org>
+
+ * g10.c, options.h: Removed option --emulate-checksum-bug.
+ * misc.c (checksum_u16_nobug): Removed.
+ (checksum_u16): Removed the bug emulation.
+ (checksum_mpi): Ditto.
+ (checksum_mpi_counted_nbits): Removed and replaced all calls
+ with checksum_mpi.
+
+ * parse-packet.c (read_protected_v3_mpi): New.
+ (parse_key): Use it here to store it as an opaque MPI.
+ * seckey-cert.c (do_check): Changed the v3 unprotection to the new
+ why to store these keys.
+ (protect_secret_key): Likewise.
+ * build-packet.c (do_secret_key): And changed the writing.
+
+ * tdbio.c (tdbio_set_dbname, open_db): Use new macro MY_O_BINARY
+ to avoid silly ifdefs.
+ (open_db): Fallback to RDONLY so that gpg may be used from a
+ RO-medium.
+
+ * encode.c (encode_simple): Make sure we don't use an ESK packet
+ when we don't have a salt in the S2K.
+
+ * misc.c (pct_expando) <case f>: Make sure that LEN is initialized.
+
+ * exec.c (exec_finish): Use ticks to denote filenames in messages.
+ (make_tempdir, exec_write): Changed format of messages.
+
+ * keyserver.c (print_keyinfo): Release USERID in on error.
+ (keyserver_work) [!DISABLE_KEYSERVER_HELPERS]: Exclude the unused
+ code.
+
+2002-09-09 Werner Koch <wk@gnupg.org>
+
+ * parse-packet.c (make_attribute_uidname): Add new ar MAX_NAMELEN
+ for sanity checks. Changed both callers. Limit the size of an %s.
+
+ * options.skel: Comment lock-once out, so that this file does not
+ change anything when copied to a new home directory.
+ * openfile.c (try_make_homedir): Don't exit after copying the
+ option skeleton.
+
+ * options.h: Don't use a comma when declaring variables over more
+ than one line.
+
+ * mainproc.c (symkey_decrypt_sesskey): Check length of the session
+ key.
+
+ * hkp.c (dehtmlize): Use ascii_tolower to protect against weird
+ locales. Cast the argument for isspace for the sake of broken
+ HP/UXes.
+ (parse_hkp_index): s/ascii_memcasecmp/ascii_strncasecmp/.
+
+ * g10.c: Removed option --emulate-3des-s2k-bug.
+
+ * passphrase.c (hash_passphrase): Was used here.
+
+ * export.c (parse_export_options)
+ * keyserver.c (parse_keyserver_options)
+ * import.c (parse_import_options)
+ * g10.c (check_permissions): s/ascii_memcasecmp/ascii_strncasecmp/.
+
+2002-09-09 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (add_group): Use '=' to separate group name from group
+ members. Use a better error message for when no = is found.
+
+ * hkp.c (hkp_export): Use CRLF in headers.
+
+2002-09-03 David Shaw <dshaw@jabberwocky.com>
+
+ * mainproc.c (print_pkenc_list): Don't increment the error counter
+ when printing the list of keys a message was encrypted to. This
+ would make gpg give a non-zero exit code even for completely valid
+ messages if the message was encrypted to more than one key that
+ the user owned.
+
+2002-09-02 Werner Koch <wk@gnupg.org>
+
+ * g10.c (main): Try to set a default character set. Print the
+ used one in verbosity level 3.
+ * gpgv.c (main): Try to set a default character set.
+
+ * status.c, status.h (STATUS_IMPORT_OK): New.
+ * import.c (import_one,import_secret_one): Print new status.
+
+2002-08-30 David Shaw <dshaw@jabberwocky.com>
+
+ * pkclist.c (build_pk_list): Add new status code to indicate an
+ untrusted user. This (or a disabled key) fail with "unavailable
+ pubkey" (G10ERR_UNU_PUBKEY).
+
+ * pkclist.c (build_pk_list): Fail if any recipient keys are
+ unusable.
+
+ * options.skel: The PGP LDAP keyserver is back. Use MIT keyserver
+ as a sample rather than cryptnet as cryptnet does not support
+ searching yet.
+
+ * keyedit.c (show_key_with_all_names): Fix error message
+ (preferences are userid/selfsig and not key specific).
+
+2002-08-30 Werner Koch <wk@gnupg.org>
+
+ * pkclist.c (do_we_trust_pre): Changed the wording of a warning.
+
+ * encode.c (encode_simple,encode_crypt): Use new style CTB for
+ compressssed packets when using MDC. We need to do this so that
+ concatenated messages are properly decrypted. Old style
+ compression assumes that it is the last packet; given that we
+ can't determine the length in advance, the uncompressor does not
+ know where to start. Actually we should use the new CTB always
+ but this would break PGP 2 compatibility.
+
+ * parse-packet.c (parse): Special treatment for new style CTB
+ compressed packets.
+
+ * build-packet.c (do_mdc): Removed. Was not used.
+ (do_encrypted_mdc): Count in the version number and the MDC packet.
+
+2002-08-28 David Shaw <dshaw@jabberwocky.com>
+
+ * sig-check.c (do_check_messages, do_check): Show keyid in error
+ messages.
+
+ * keyserver.c (print_keyinfo): More readable key listings for
+ --search-keys responses.
+
+2002-08-26 David Shaw <dshaw@jabberwocky.com>
+
+ * hkp.c (parse_hkp_index, dehtmlize): Move HTML functionality into
+ new "dehtmlize" function. Remove HTML before trying to parse each
+ line from the keyserver. If the keyserver provides key type
+ information in the listing, use it.
+
+2002-08-23 David Shaw <dshaw@jabberwocky.com>
+
+ * sig-check.c (do_check, do_check_messages): Emit the usual sig
+ warnings even for cached sigs. This also serves to protect
+ against missing a sig expiring while cached.
+
+ * getkey.c (merge_selfsigs_main): Don't check UID self-sigs twice.
+
+2002-08-22 David Shaw <dshaw@jabberwocky.com>
+
+ * import.c (clean_subkeys, chk_self_sigs): Merge clean_subkeys
+ into chk_self_sigs. This improves efficiency as the same
+ signatures are not checked multiple times. Clarify when a subkey
+ is revoked (any revocation signature, even if it is dated before
+ the binding signature).
+
+ * getkey.c (merge_selfsigs_subkey): Subkey revocation comments.
+
+ * keylist.c (list_one): Stats are only for public key listings.
+
+ * g10.c (main), options.skel: Default should be include-revoked
+ for keyserver operations.
+
+2002-08-21 Werner Koch <wk@gnupg.org>
+
+ * import.c (import_print_stats): Print new non_imported counter
+ which is currently not used because we terminate on errors.
+
+2002-08-20 David Shaw <dshaw@jabberwocky.com>
+
+ * options.skel: Document no-include-attributes for
+ keyserver-options.
+
+ * keylist.c, keyedit.c, keyserver.c, sign.c: Some TODOs and
+ comments.
+
+ * export.c (do_export_stream): Fix noop bug in exporting sensitive
+ revocation keys.
+
+ * pkclist.c (do_edit_ownertrust): Comment out the option for
+ showing trust paths until it can be implemented.
+
+2002-08-19 Werner Koch <wk@gnupg.org>
+
+ * getkey.c (get_user_id_native): Renamed to ..
+ (get_user_id_printable): this. Filter out all dangerous
+ characters. Checked all usages.
+ (get_user_id_string_native): Renamed to..
+ (get_user_id_string_printable): this. Filter out all dangerous
+ characters. Checked all usages.
+ * keyedit.c (show_basic_key_info): New.
+ * keylist.c (print_fingerprint): New mode 3.
+ * import.c (import_one): Use new function to display the user ID.
+
+2002-08-16 Timo Schulz <ts@winpt.org>
+
+ * g10.c (main): Enable opt.interactive.
+
+ * import.c (import_one): Ask the user if the key shall be
+ imported when the interactive mode is used. Useful to extract
+ selected keys from a file.
+
+2002-08-16 Werner Koch <wk@gnupg.org>
+
+ * seckey-cert.c: Workaround to allow decryption of v3 keys created
+ with a bug in the mpi_get_secure_buffer.
+
+2002-08-14 David Shaw <dshaw@jabberwocky.com>
+
+ * hkp.c (parse_hkp_index): Properly handle really large keys
+ (5 digit key length) in HKP searches.
+
+2002-08-13 David Shaw <dshaw@jabberwocky.com>
+
+ * encode.c (encode_simple): Fix problem with using compression
+ algo 2 and symmetric compressed files.
+
+ * encode.c (encode_simple, encode_crypt): If we are not using a
+ MDC, compress even if a file is already compressed. This is to
+ help against the chosen ciphertext attack.
+
+ * pkclist.c (select_algo_from_prefs): Fix requested algorithm bug
+ so the request succeeds even if the requested algorithm is not the
+ first found.
+
+ * cipher.c (write_header), encode.c (use_mdc, encode_simple,
+ encode_crypt, encrypt_filter), g10.c (main): Be more eager to use
+ a MDC. We use a MDC if the keys directly support it, if the keys
+ list AES (any) or TWOFISH anywhere in the prefs, or if the cipher
+ chosen does not have a 64 bit blocksize.
+
+2002-08-08 David Shaw <dshaw@jabberwocky.com>
+
+ * options.skel: Some language tweaks, and remove the
+ load-extension section for random gatherers.
+
+ * keyring.c (create_tmp_file, rename_tmp_file): Create tmp files
+ with user-only permissions, but restore the original permissions
+ if the user has something special set.
+
+ * openfile.c (copy_options_file): Create new options file
+ (gpg.conf) with user-only permissions.
+
+ * keydb.c (keydb_add_resource): Create new keyrings with user-only
+ permissions.
+
+ * tdbio.c (tdbio_set_dbname): Create new trustdbs with user-only
+ permissions.
+
+2002-08-07 David Shaw <dshaw@jabberwocky.com>
+
+ * sig-check.c (signature_check2): Sanity check that the md has a
+ context for the hash that the sig is expecting. This can happen
+ if a onepass sig header does not match the actual sig, and also if
+ the clearsign "Hash:" header is missing or does not match the
+ actual sig.
+
+ * keyedit.c (menu_revsig): Properly show a uid is revoked without
+ restarting gpg. This is Debian bug 124219, though their supplied
+ patch will not do the right thing.
+
+ * main.h, tdbio.c (tdbio_set_dbname), misc.c (removed
+ check_permissions), keydb.c (keydb_add_resource), g10.c (main,
+ check_permissions): Significant reworking of the permission check
+ mechanism. The new behavior is to check everything in the homedir
+ by checking the homedir itself. If the user wants to put
+ (possibly shared) keyrings outside the homedir, they are not
+ checked. The options file and any extension files are checked
+ wherever they are, as well as their enclosing directories. This
+ is Debian bug 147760.
+
+2002-08-06 Stefan Bellon <sbellon@sbellon.de>
+
+ * g10.c (main): Use of EXTSEP_S in new gpg.conf string.
+ * openfile.c (copy_options_file): Ditto.
+
+2002-08-06 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), mainproc.c (proc_encrypted):
+ --ignore-mdc-error option to turn a MDC check error into a
+ warning.
+
+ * encode.c (encode_crypt), g10.c (main), sign.c (sign_file,
+ clearsign_file): Use the same --pgpX warning string everywhere to
+ ease translations.
+
+ * encode.c (write_pubkey_enc_from_list): Warn when using
+ --throw-keyid with --pgpX. Noted by Vedaal Nistar.
+
+ * revoke.c (export_minimal_pk, gen_desig_revoke, gen_revoke):
+ Export a minimal pk along with the revocation cert when in --pgpX
+ mode so that PGP can import it.
+
+2002-08-06 Werner Koch <wk@gnupg.org>
+
+ * options.skel: Changed comments.
+
+ * g10.c (main): Try to use "gpg.conf" as default option file.
+ * openfile.c (copy_options_file): Changed name of created file.
+
+2002-08-02 Werner Koch <wk@gnupg.org>
+
+ * Makefile.am (LDFLAGS): Removed DYNLINK_LDFLAGS.
+
+2002-07-30 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), mainproc.c (proc_encrypted): Return a
+ decryption failed error if a MDC does not verify. Warn if a MDC
+ is not present (can disable via --no-mdc-warning).
+
+ * exec.c (exec_write), g10.c (main), keyserver.c
+ (keyserver_spawn): Use new DISABLE_KEYSERVER_PATH rather than
+ FIXED_EXEC_PATH.
+
+2002-07-28 David Shaw <dshaw@jabberwocky.com>
+
+ * sig-check.c (do_check): Properly validate v4 sigs with no hashed
+ section at all.
+
+2002-07-25 Werner Koch <wk@gnupg.org>
+
+ * delkey.c (do_delete_key): Always allow to delete a key in batch mode
+ when specified by fingerprint. Suggested by Enzo Michelangeli.
+
+2002-07-25 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (menu_revsig): Change "revsig" to honor selected uids
+ so the user can revoke sigs from particular uids only.
+
+ * keylist.c (list_keyblock_print): Don't display expired uids in
+ --list-keys unless -v and not --list-sigs (just like revoked
+ uids).
+
+ * exec.c, export.c, import.c, keyedit.c, keyserver.c, misc.c:
+ "Warning" -> "WARNING"
+
+2002-07-24 David Shaw <dshaw@jabberwocky.com>
+
+ * main.h, import.c (parse_import_options, fix_hkp_corruption,
+ import_one, delete_inv_parts), g10.c (main): New import-option
+ "repair-hkp-subkey-bug", which repairs as much as possible the HKP
+ mangling multiple subkeys bug. It is on by default for keyserver
+ receives, and off by default for regular --import.
+
+ * main.h, import.c (import, import_one, delete_inv_parts), hkp.c
+ (hkp_ask_import), keyserver.c (keyserver_spawn): Use keyserver
+ import options when doing keyserver receives.
+
+ * options.h, exec.h, exec.c (set_exec_path, exec_write), g10.c
+ (main), keyserver.c (keyserver_spawn): If the user does not use
+ "exec-path", completely replace $PATH with GNUPG_LIBEXECDIR before
+ calling the keyserver helper. If the user does use "exec-path",
+ append GNUPG_LIBEXECDIR after the specified path.
+
+2002-07-23 David Shaw <dshaw@jabberwocky.com>
+
+ * import.c (parse_import_options), export.c
+ (parse_export_options): Fix offset problem with reversed ("no-")
+ meanings.
+
+ * import.c (delete_inv_parts): Discard subkey signatures (0x18 and
+ 0x28) if found in the userid section of the key.
+
+ * sig-check.c (signature_check2): Signatures made by invalid
+ subkeys (bad/missing binding sig) are also invalid.
+
+ * keylist.c (print_fingerprint): Show the primary as well as the
+ secondary key fingerprint in modes 1 & 2.
+
+2002-07-22 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, main.h, g10.c (main), import.c
+ (parse_import_options, delete_inv_parts), keyserver.c
+ (parse_keyserver_options): add new --import-options option. The
+ only current flag is "allow-local-sigs".
+
+ * g10.c (main): Don't disable MDC in pgp7 mode.
+
+ * options.h, g10.c (main), keyserver.c (parse_keyserver_options):
+ Remove old keyserver-option include-attributes now that there is
+ an export-option for the same thing.
+
+ * options.h, main.h, export.c (parse_export_options,
+ do_export_stream), g10.c (main): add new --export-options option.
+ Current flags are "include-non-rfc", "include-local-sigs",
+ "include-attributes", and "include-sensitive-revkeys".
+
+ * options.h, hkp.c (hkp_export), keyserver.c
+ (parse_keyserver_options, keyserver_spawn): try passing unknown
+ keyserver options to export options, and if successful, use them
+ when doing a keyserver --send-key.
+
+ * build-packet.c (build_sig_subpkt): We do not generate
+ SIGSUBPKT_PRIV_VERIFY_CACHE anymore.
+
+ * revoke.c (gen_desig_revoke): Lots more comments about including
+ sensitive revkeys along with the revocation sig itself.
+
+ * keyserver.c (parse_keyserver_options): Simpler implementation
+ that can skip one pass over the options.
+
+2002-07-18 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (keyedit_menu, menu_addrevoker): Allow specifying
+ "sensitive" as an argument to an addrevoker command. This sets
+ the 0x40 sensitive revoker flag.
+
+ * revoke.c (gen_desig_revoke): When generating a designated
+ revocation, include the direct key sig that contains the
+ designated revoker subpacket. This allows sensitive designated
+ revocation subpackets to be exported. Also indicate which
+ revokers are sensitive in the first place.
+
+2002-07-17 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (show_key_with_all_names_colon): The 0x40 class bit in
+ a designated revoker means "sensitive", not "local". It's
+ exportable under the right circumstances.
+
+ * main.h, options.h, export.c (do_export_stream), g10.c (main),
+ hkp.c (hkp_export), keyserver.c (keyserver_spawn: Add a flag to
+ skip attribute packets and their signatures while exporting. This
+ is to accomodate keyservers (pksd again) that choke on attributes.
+ Use keyserver-option "include-attributes" to control it. This
+ defaults to ON (i.e. don't skip).
+
+2002-07-09 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, keyserver.c (parse_keyserver_uri, keyserver_spawn,
+ keyserver_work), hkp.c (hkp_ask_import, hkp_export, hkp_search):
+ Use a much more strict reading of RFC-2396 for the keyserver URIs.
+ Specifically, don't try and be smart about checking the value of
+ ":port" so long as it is all digits, and properly handle opaque
+ data (those scheme specific parts that do not start with "//").
+
+2002-07-04 David Shaw <dshaw@jabberwocky.com>
+
+ * photoid.c (get_default_photo_command, show_photos): Honor
+ FIXED_PHOTO_VIEWER and DISABLE_PHOTO_VIEWER.
+
+ * mainproc.c (check_sig_and_print): Use --show-photos to show
+ photos when verifying a sig made by a key with a photo.
+
+ * keyserver.c (parse_keyserver_uri): Properly parse a URI with no
+ :port section and an empty file path, but with a terminating '/'.
+ (keyserver_work): Honor DISABLE_KEYSERVER_HELPERS.
+
+ * hkp.c (hkp_ask_import): Display keyserver URI as a URI, but only
+ if verbose.
+
+ * exec.c, g10.c: USE_EXEC_PATH -> FIXED_EXEC_PATH
+
+2002-07-03 David Shaw <dshaw@jabberwocky.com>
+
+ * exec.h, exec.c (set_exec_path, exec_write), g10.c (main): If
+ USE_EXEC_PATH is defined at compile time, use it to lock the
+ exec-path and not allow the user to change it.
+
+2002-07-02 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), keyserver.c (keyserver_refresh):
+ Maintain and use the original keyserver URI for cosmetics rather
+ than trying to recreate it when needed.
+
+ * mainproc.c (check_sig_and_print): Properly disregard expired
+ uids. Make sure that the first uid listed is a real uid and not
+ an attribute (attributes should only be listed in the "aka"
+ section). When there are no valid textual userids, try for an
+ invalid textual userid before using any attribute uid.
+
+2002-07-01 David Shaw <dshaw@jabberwocky.com>
+
+ * options.skel: Fix a few typos, clarify "group", and remove
+ sample photo viewers for Win32 since they are the defaults now.
+
+ * parse-packet.c (make_attribute_uidname), keylist.c
+ (dump_attribs): Fix two typecast warnings.
+
+ * packet.h, build-packet.c (build_attribute_subpkt), exec.c
+ (expand_args), mkdtemp.c (mkdtemp), photoid.c
+ (parse_image_header): Fix some signedness compiler warnings.
+
+2002-07-01 Werner Koch <wk@gnupg.org>
+
+ * photoid.c (get_default_photo_command): Also use __MINGW32__
+ instead of HAVE_DOSISH_SYSTEM.
+
+ * encode.c (encode_symmetric): Do not use the new encryption code.
+
+2002-06-30 Werner Koch <wk@gnupg.org>
+
+ * photoid.c: Use __MINGW32__ to include windows because
+ HAVE_DOSISH_SYSTEM is also set for OS/2 and plain DOS. Provide
+ constant missing in older mingw installations.
+
+2002-06-21 Stefan Bellon <sbellon@sbellon.de>
+
+ * g10.c [__riscos__]: Moved RISC OS specific stuff to util/riscos.c
+ and include/util.h.
+
+ * gpgv.c [__riscos__]: Likewise.
+
+2002-06-20 David Shaw <dshaw@jabberwocky.com>
+
+ * keydb.h, pkclist.c (select_algo_from_prefs): Allow passing a
+ suggested algorithm which will be used if available.
+
+ * encode.c (encode_crypt, encrypt_filter), sign.c (sign_file): Use
+ new select_algo_from_prefs feature to check if forcing an
+ algorithm would violate the recipient preferences.
+
+ * photoid.c (get_default_photo_command, show_photos): Use
+ different default viewers on different platforms. Currently we
+ have Win 9x, Win NT (2k, xp), Mac OSX, RISC OS, and "everybody
+ else". These are #ifdefs as much as possible to avoid clutter.
+
+ * g10.c (strusage, build_list), keyedit.c (show_prefs), main.h,
+ misc.c (compress_algo_to_string, check_compress_algo), pkclist.c
+ (algo_available), keygen.c (keygen_set_std_prefs): New
+ algo_to_string and check functions for compress algorithms.
+
+2002-06-20 Werner Koch <wk@gnupg.org>
+
+ * misc.c (setsysinfo): Removed a #warning for Alpha's uniligedn
+ trap disabling - it is quite possible that this is a debug relict.
+
+2002-06-20 Stefan Bellon <sbellon@sbellon.de>
+
+ * g10.c [__riscos__]: Added image file system feature.
+
+ * gpgv.c [__riscos__]: Added image file system feature.
+
+ * photoid.c (show_photos) [__riscos__]: Set RISC OS filetype of
+ photo id according to MIME type.
+
+2002-06-19 David Shaw <dshaw@jabberwocky.com>
+
+ * hkp.c (parse_hkp_index): Don't leak memory when failing out of a
+ bad HKP keyserver.
+
+ * g10.c (add_notation_data): Relax slightly the rules as to what
+ can go into a notation name - 2440 allows "@", for example.
+
+2002-06-17 David Shaw <dshaw@jabberwocky.com>
+
+ * import.c (clean_subkeys, import_one): Only allow at most 1
+ binding sig and at most 1 revocation sig on a subkey, as per
+ 2440:11.1.
+
+ * hkp.c (parse_hkp_index, hkp_search): Error if the keyserver
+ returns an unparseable HKP response.
+
+2002-06-15 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (show_key_with_all_names), keylist.c
+ (list_keyblock_print): Show "[expired]" before expired uids.
+
+ * keyedit.c (show_key_with_all_names_colon), mainproc.c
+ (list_node), keylist.c (list_keyblock_colon): Show flag 'e' for
+ expired user ids. Use "uat" for user attribute packets instead of
+ "uid". Also use '<count> <length>' rather than the fake user id
+ string on attributes.
+
+ * keygen.c (keygen_add_revkey): Remove unused code.
+
+ * misc.c (check_permissions): Check directory permissions
+ properly - they are not special files.
+
+ * pkclist.c (expand_id, expand_group, build_pk_list): When
+ expanding groups before building a pk list, inherit flags from the
+ original pre-expanded string.
+
+ * pubkey-enc.c (is_algo_in_prefs): Don't use prefs from expired
+ uids.
+
+2002-06-14 David Shaw <dshaw@jabberwocky.com>
+
+ * free-packet.c (copy_signature): Properly copy a signature that
+ carries a revocation key on it.
+
+ * pkclist.c (expand_id, expand_group, build_pk_list): Groups now
+ work properly when used in the "Enter the user ID" prompt.
+
+2002-06-14 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (show_key_with_all_names): Display warning if a user
+ tries to show prefs on a v3 key with a v3 selfsig.
+
+ * kbnode.c (dump_kbnode): Show if a uid is expired.
+
+ * import.c (merge_blocks, import_revoke_cert): Show user ID
+ receiving a revocation certificate.
+
+ * free-packet.c (cmp_user_ids): Properly compare attribute ids.
+
+ * pkclist.c (expand_groups): Maintain the strlist flags while
+ expanding. Members of an expansion inherit their flags from the
+ expansion key.
+
+ * options.h, cipher.c (write_header), g10.c (main), keygen.c
+ (keygen_set_std_prefs): remove the personal_mdc flag. It no
+ longer serves a purpose now that the personal preference lists are
+ split into cipher/digest/zip.
+
+2002-06-14 Timo Schulz <ts@winpt.org>
+
+ * skclist.c (is_insecure): Implemented.
+
+2002-06-12 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_spawn): Properly handle PROGRAM responses
+ when they have a CRLF ending. Noted by Keith Ray.
+
+ * keyserver.c (keyserver_spawn): Handle CRLF endings from
+ keyserver helpers. Also don't leak the last line worth of memory
+ from the keyserver response.
+
+ * main.h, misc.c (deprecated_warning): New function to warn about
+ deprecated options and commands.
+
+ * g10.c (main), keyserver-internal.h, keyserver.c
+ (parse_keyserver_uri): Use new deprecated function to warn about
+ honor-http-proxy, auto-key-retrieve, and x-broken-hkp.
+
+2002-06-11 David Shaw <dshaw@jabberwocky.com>
+
+ * Makefile.am: link gpg with NETLIBS for the built-in HKP access.
+
+2002-06-10 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, keyserver.c (keyserver_opts), g10.c (main): New
+ keyserver option "include-subkeys". This feature already existed,
+ but now can be turned off. It defaults to on.
+
+ * options.h, keyserver.c (parse_keyserver_options,
+ keyserver_spawn): There are now enough options to justify making a
+ structure for the keyserver options rather than a page of
+ if-then-else-if-then-etc.
+
+ * getkey.c (merge_keys_and_selfsig, merge_selfsigs_main): Fix bug
+ in calculating key expiration dates.
+
+2002-06-09 David Shaw <dshaw@jabberwocky.com>
+
+ * keydb.h, getkey.c (get_user_id_native), import.c (import_one):
+ Display user ID while importing a key. Note this applies to both
+ --import and keyserver --recv-keys.
+
+ * exec.c (exec_finish): Log unnatural exit (core dump, killed
+ manually, etc) for fork/exec/pipe child processes.
+
+2002-06-08 Timo Schulz <ts@winpt.org>
+
+ * encode.c (encode_symmetric): Disable the compat flag
+ when the expert mode is enabled.
+
+2002-06-07 David Shaw <dshaw@jabberwocky.com>
+
+ * options.skel, options.h, main.h, keydb.h, pkclist.c
+ (build_pk_list, expand_groups), g10.c (main, add_group): Add new
+ "group" command to allow one name to expand into multiple keys.
+ For simplicity, and to avoid potential loops, we only expand once
+ - you can't make an alias that points to an alias.
+
+ * main.h, g10.c (main), keygen.c (build_personal_digest_list):
+ Simplify the default digest list - there is really no need for the
+ other hashes since they will never be used after SHA-1 in the
+ list.
+
+ * options.skel, options.h, g10.c (main), hkp.c (hkp_ask_import,
+ hkp_export, hkp_search), keyserver.c (parse_keyserver_options,
+ parse_keyserver_uri, keyserver_work, keyserver_refresh): Make the
+ "x-broken-hkp" keyserver scheme into keyserver-option
+ "broken-http-proxy". Move honor_http_proxy into
+ keyserver_options. Canonicalize the three variations of "hkp",
+ "x-hkp", and "x-broken-hkp" into "hkp".
+
+2002-06-07 Stefan Bellon <sbellon@sbellon.de>
+
+ * g10.c [__riscos__]: Added --attribute-file to do the same as
+ --attribute-fd, but with a filename not a fd as argument.
+ Added magic symbol for RISC OS to use different memory management.
+
+ * gpgv.c [__riscos__]: Added magic symbol for RISC OS to use
+ different memory management.
+
+2002-06-06 David Shaw <dshaw@jabberwocky.com>
+
+ * main.h, g10.c (main), keygen.c (build_personal_digest_list): Put
+ in a default digest preference list consisting of SHA-1, followed
+ by every other installed digest except MD5. Note this is the same
+ as having no digest preference at all except for SHA-1 being
+ favored.
+
+ * options.h, g10.c (main), keygen.c (keygen_set_std_prefs),
+ pkclist.c (select_algo_from_prefs): Split
+ --personal-preference-list into three:
+ --personal-{cipher|digest|compress}-preferences. This allows a
+ user to set one without affecting another (i.e. setting only a
+ digest pref doesn't imply an empty cipher pref).
+
+ * exec.c (exec_read): This is a safer way of guessing the return
+ value of system(). Noted by Stefan Bellon.
+
+2002-06-05 David Shaw <dshaw@jabberwocky.com>
+
+ * hkp.c (parse_hkp_index): Be more robust with keyservers
+ returning very unparseable responses.
+
+ * exec.c (exec_read): Catch and display an error when the remote
+ process exits unnaturally (i.e. segfault) so the user knows what
+ happened. Also fix exec_write stub which has a different number
+ of arguments now.
+
+2002-06-05 Timo Schulz <ts@winpt.org>
+
+ * encode.c (encode_simple): Ignore the new mode for RFC1991.
+ * mainproc.c (symkey_decrypt_sesskey): Better check for weird
+ keysizes.
+
+2002-06-05 Timo Schulz <ts@winpt.org>
+
+ * encode.c (encode_sesskey): New.
+ (encode_simple): Use it here. But by default we use the compat
+ mode which supress to generate encrypted session keys.
+
+2002-06-05 Timo Schulz <ts@winpt.org>
+
+ * mainproc.c (symkey_decrypt_sesskey): New.
+ (proc_symkey_enc): Support for encrypted session keys.
+
+2002-06-04 David Shaw <dshaw@jabberwocky.com>
+
+ * sign.c (hash_for, sign_file): When encrypting and signing at the
+ same time, consult the various hash prefs to pick a hash algorithm
+ to use. Pass in a 160-bit hint if any of the signing keys are
+ DSA.
+
+ * keydb.h, pkclist.c (select_algo_from_prefs, algo_available):
+ Pass a "hints" opaque pointer in to let the caller give hints as
+ to what algorithms would be acceptable. The only current hint is
+ for PREFTYPE_HASH to require a 160-bit hash for DSA. Change all
+ callers in encode.c (encode_crypt, encrypt_filter) and sign.c
+ (sign_file). If we settle on MD5 as the best algorithm based
+ solely on recepient keys and SHA1 is also a possibility, use SHA1
+ unless the user intentionally chose MD5. This is as per 2440:13.
+
+ * exec.c (make_tempdir): Fix duplicated filename problem.
+
+2002-06-03 David Shaw <dshaw@jabberwocky.com>
+
+ * packet.h, parse-packet.c (enum_sig_subpkt): Report back from
+ enum_sig_subpkt when a subpacket is critical and change all
+ callers in keylist.c (show_policy_url, show_notation), mainproc.c
+ (print_notation_data), and pkclist.c (do_show_revocation_reason).
+
+ * keylist.c (show_policy_url, show_notation): Display if the
+ policy or notation is critical.
+
+2002-06-03 David Shaw <dshaw@jabberwocky.com>
+
+ * main.h, g10.c (main), keylist.c (dump_attribs, set_attrib_fd,
+ list_keyblock_print, list_keyblock_colon), status.h, status.c
+ (get_status_string): New --attribute-fd feature to dump the
+ contents of attribute subpackets for frontends. If --status-fd is
+ also used, then a new status tag ATTRIBUTE is provided for each
+ subpacket.
+
+ * packet.h, getkey.c (fixup_uidnode, merge_selfsigs_main,
+ merge_selfsigs_subkey), parse-packet.c (setup_user_id): Keep track
+ of the expiration time of a user ID, and while we're at it, use
+ the expired flag from the selfsig rather than reparsing the
+ SIG_EXPIRE subpacket.
+
+ * photoid.c (generate_photo_id): When adding a new photo ID,
+ showing the photo for confirmation is not safe when noninteractive
+ since the "user" may not be able to dismiss a viewer window.
+ Noted by Timo Schulz.
+
+2002-06-03 David Shaw <dshaw@jabberwocky.com>
+
+ * options.skel: Sample photo viewers for Win32.
+
+ * misc.c (pct_expando): Use the seckey for %k/%K if the pubkey is
+ not available.
+
+ * photoid.h, photoid.c (show_photos): Include the seckey in case a
+ user tries to view a photo on a secret key, and change all callers
+ in keyedit.c (menu_showphoto), keylist.c (list_keyblock_print),
+ and photoid.c (generate_photo_id).
+
+2002-06-02 David Shaw <dshaw@jabberwocky.com>
+
+ * photoid.c (show_photos): Work properly when not called with a
+ public key.
+
+2002-05-31 David Shaw <dshaw@jabberwocky.com>
+
+ * sign.c (mk_notation_and_policy): Free unneeded buffer.
+
+ * hkp.c (parse_hkp_index): Properly handle the '&' character
+ (i.e. "&amp;") in HKP responses.
+
+ * getkey.c (merge_selfsigs_main): Fix reversed expiration time
+ check with self-sigs.
+
+ * keyedit.c (sign_uids): When making a new self-sig on a v3 key,
+ make a v3 self-sig unless it is currently a v3 self-sig being
+ promoted to v4.
+
+2002-05-31 Timo Schulz <ts@winpt.org>
+
+ * pkclist.c (do_show_revocation_reason): Don't use capital
+ letters for non-interactive output.
+ (show_revocation_reason): Now it is global.
+ * pubkey-enc.c (get_it): Show if the key has been revoked.
+
+2002-05-30 David Shaw <dshaw@jabberwocky.com>
+
+ * sign.c (write_signature_packets, sign_file, clearsign_file,
+ sign_symencrypt_file): Make a v4 signature if a policy URL or
+ notation is set, unless v3 sigs are forced via rfc1991 or
+ force-v3-sigs. Also remove some doubled code and clarify an error
+ message (we don't sign in PGP2 mode - just detach-sign).
+
+ * parse-packet.c (parse_one_sig_subpkt): Add KS_FLAGS to the "any
+ size" section.
+
+2002-05-29 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (keygen_set_std_prefs, add_feature_mdc): Use "mdc" and
+ "no-mdc" in the prefs string to allow switching on and off the MDC
+ feature. This is needed to properly export a key from GnuPG for
+ use on PGP which does not support MDC - without this, MDC-capable
+ implementations will still try and generate MDCs which will break
+ PGP.
+
+ * keygen.c (keygen_get_std_prefs): Show "[mdc]" in prefs string if
+ it is enabled.
+
+ * options.h, g10.c (main), cipher.c (write_header), keygen.c
+ (keygen_set_std_prefs): For consistency, allow the user to specify
+ mdc/no-mdc in the --personal-preference-list. If disabled, it
+ acts just like --disable-mdc.
+
+2002-05-29 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, exec.c: Add some debugging info, using the 1024 debug
+ flag.
+
+ * exec.c (win_system): New system()-like function for win32 that
+ does not return until the child process terminates. Of course,
+ this doesn't help if the process itself exits before it is
+ finished.
+
+2002-05-29 Werner Koch <wk@gnupg.org>
+
+ * encode.c (encode_simple): Intialize PKT when --no-literal is used.
+
+ * keyedit.c (show_key_with_all_names_colon): Renamed the record
+ for revocation keys to "rvk".
+
+2002-05-27 Werner Koch <wk@gnupg.org>
+
+ * keyedit.c (show_key_with_all_names_colon): New.
+ (show_key_with_all_names): Divert to new function when required.
+ Sanitize printing of revoker name.
+
+2002-05-27 David Shaw <dshaw@jabberwocky.com>
+
+ * build-packet.c (build_sig_subpkt): Handle setting sig flags for
+ certain subpacket types (notation, policy url, exportable,
+ revocable). keyedit.c (sign_mk_attrib): Flags no longer need to
+ be set here.
+
+ * packet.h, parse-packet.c (parse_one_sig_subpkt), build-packet.c
+ (build_sig_subpkt): Call parse_one_sig_subpkt to sanity check
+ buffer lengths before building a sig subpacket.
+
+2002-05-26 David Shaw <dshaw@jabberwocky.com>
+
+ * sign.c (mk_notation_and_policy): Include secret key to enable %s
+ expandos, and pass notations through pct_expando as well.
+
+ * main.h, misc.c (pct_expando): Add %s and %S expandos for
+ signer's keyid.
+
+2002-05-25 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (strusage, build_list): Add compress algorithms to
+ --version list. Show algorithm numbers when --verbose --version
+ is done.
+
+2002-05-22 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, main.h, keygen.c (keygen_set_set_prefs,
+ keygen_get_std_prefs, keygen_upd_std_prefs), keyedit.c
+ (keyedit_menu), g10.c (main), pkclist.c (select_algo_from_prefs):
+ Add --personal-preference-list which allows the user to factor in
+ their own preferred algorithms when the preference lists are
+ consulted. Obviously, this does not let the user violate a
+ recepient's preferences (and the RFC) - this only influences the
+ ranking of the agreed-on (and available) algorithms from the
+ recepients. Suggested by David Hollenberg.
+
+ * options.h, keygen.c (keygen_set_std_prefs), g10.c (main): Rename
+ --preference-list to --default-preference-list (as that is what it
+ really is), and make it a true default in that if the user selects
+ "default" they get this list and not the compiled-in list.
+
+2002-05-22 Werner Koch <wk@gnupg.org>
+
+ * g10.c (main): Add missing LF in a info printout and made it
+ translatable. Noted by Michael Tokarev.
+
+2002-05-21 Werner Koch <wk@gnupg.org>
+
+ * g10.c (main): Removed the undef of USE_SHM_COPROCESSING which
+ was erroneously introduced on 2002-01-09.
+
+ * signal.c (got_fatal_signal): Don't write the Nul to stderr.
+ Reported by David Hollenberg.
+
+2002-05-18 David Shaw <dshaw@jabberwocky.com>
+
+ * main.h, g10.c (main), revoke.c (gen_desig_revoke): Generate a
+ designated revocation via --desig-revoke
+
+ * keyedit.c (keyedit_menu, menu_addrevoker): New "addrevoker"
+ command to add a designated revoker to a key.
+
+2002-05-17 David Shaw <dshaw@jabberwocky.com>
+
+ * gpgv.c: Add stub for get_ownertrust().
+
+ * g10.c (main): --allow-freeform-uid should be implied by
+ OpenPGP. Add --no-allow-freeform-uid.
+
+ * keyedit.c (sign_uids): Issue a warning when signing a
+ non-selfsigned uid.
+
+ * getkey.c (merge_selfsigs_main): If a key has no selfsigs, and
+ allow-non-selfsigned-uid is not set, still try and make the key
+ valid by checking all uids for a signature from an ultimately
+ trusted key.
+
+2002-05-16 David Shaw <dshaw@jabberwocky.com>
+
+ * main.h, keygen.c (keygen_add_revkey): Add revocation key
+ subpackets to a signature (callable by
+ make_keysig_packet). (write_direct_sig): Write a 1F direct key
+ signature. (parse_revocation_key): Parse a string in
+ algo:fpr:sensitive format into a revocation
+ key. (get_parameter_revkey, do_generate_keypair): Call above
+ functions when prompted from a batch key generation file.
+
+ * build-packet.c (build_sig_subpkt): Allow multiple revocation key
+ subpackets in a single sig.
+
+ * keydb.h, getkey.c (get_seckey_byfprint): Same as
+ get_pubkey_byfprint, except for secret keys. We only know the
+ fingerprint of a revocation key, so this is needed to retrieve the
+ secret key needed to issue a revokation.
+
+ * packet.h, parse-packet.c (parse_signature, parse_revkeys): Split
+ revkey parsing off into a new function that can be used to reparse
+ after manipulating the revkey list.
+
+ * sign.c (make_keysig_packet): Ability to make 1F direct key
+ signatures.
+
+2002-05-15 David Shaw <dshaw@jabberwocky.com>
+
+ * options.skel: keyserver.pgp.com is gone, so list pgp.surfnet.nl
+ as a sample LDAP server instead.
+
+ * getkey.c (merge_selfsigs_main): Properly handle multiple
+ revocation keys in a single packet. Properly handle revocation
+ keys that are in out-of-order packets. Remove duplicates in
+ revocation key list.
+
+2002-05-14 Timo Schulz <ts@winpt.org>
+
+ * exec.c (make_tempdir) [MINGW32]: Added missing '\'.
+
+2002-05-14 Stefan Bellon <sbellon@sbellon.de>
+
+ * exec.c (make_tempdir): Make use of EXTSEP_S instead of hardcoded
+ dot as extension separator.
+
+2002-05-13 David Shaw <dshaw@jabberwocky.com>
+
+ * photoid.c (show_photos): Use the long keyid as the filename for
+ the photo. Use the short keyid as the filename on 8.3 systems.
+
+ * exec.h, exec.c (make_tempdir, exec_write, exec_finish): Allow
+ caller to specify filename. This should make things easier on
+ windows and macs where the file extension is required, but a whole
+ filename is even better.
+
+ * keyedit.c (show_key_with_all_names, show_prefs): Show proper
+ prefs for a v4 key uid with no selfsig at all.
+
+ * misc.c (check_permissions): Don't check permissions on
+ non-normal files (pipes, character devices, etc.)
+
+2002-05-11 Werner Koch <wk@gnupg.org>
+
+ * mainproc.c (proc_symkey_enc): Avoid segv in case the parser
+ encountered an invalid packet.
+
+ * keyserver.c (keyserver_export): Get confirmation before sending
+ all keys.
+
+2002-05-10 Stefan Bellon <sbellon@sbellon.de>
+
+ * g10.c, hkp.c, keyedit.c, keyserver.c: Replaced all occurrances
+ of strcasecmp with ascii_strcasecmp and all occurrances of
+ strncasecmp with ascii_memcasecmp.
+
+2002-05-10 David Shaw <dshaw@jabberwocky.com>
+
+ * packet.h, getkey.c (fixup_uidnode), keyedit.c (show_prefs): Show
+ assumed prefs for hash and compression as well as the cipher pref.
+ Show assumed prefs if there are no prefs at all on a v4
+ self-signed key.
+
+ * options.h, g10.c (main), sign.c (make_keysig_packet): New
+ --cert-digest-algo function to override the default key signing
+ hash algorithm.
+
+2002-05-09 David Shaw <dshaw@jabberwocky.com>
+
+ * getkey.c (merge_selfsigs_main): Make sure the revocation key
+ list starts clean as this function may be called more than once
+ (e.g. from functions in --edit).
+
+ * g10.c, encode.c (encode_crypt), sign.c (sign_file,
+ sign_symencrypt_file): Make --compress-algo work like the
+ documentation says. It should be like --cipher-algo and
+ --digest-algo in that it can override the preferences calculation
+ and impose the setting the user wants. No --compress-algo setting
+ allows the usual preferences calculation to take place.
+
+ * main.h, compress.c (compress_filter): use new
+ DEFAULT_COMPRESS_ALGO define, and add a sanity check for compress
+ algo value.
+
+2002-05-08 David Shaw <dshaw@jabberwocky.com>
+
+ * pkclist.c (select_algo_from_prefs): There is an assumed
+ compression preference for uncompressed data.
+
+2002-05-07 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), getkey.c (finish_lookup), pkclist.c
+ (algo_available): --pgp7, identical to --pgp6 except that it
+ permits a few algorithms that PGP 7 added: AES128, AES192, AES256,
+ and TWOFISH. Any more of these --pgpX flags, and it'll be time to
+ start looking at a generic --emulate-pgp X option.
+
+ * export.c (do_export_stream): Warn the user when exporting a
+ secret key if it or any of its secret subkeys are protected with
+ SHA1 while simple_sk_checksum is set.
+
+ * parse-packet.c (parse_key): Show when the SHA1 protection is
+ used in --list-packets.
+
+ * options.h, build-packet.c (do_comment), g10.c (main): Rename
+ --no-comment as --sk-comments/--no-sk-comments (--no-comment still
+ works) and make the default be --no-sk-comments.
+
+2002-05-07 Werner Koch <wk@gnupg.org>
+
+ * keygen.c (get_parameter_algo): Never allow generation of the
+ deprecated RSA-E or RSA-S flavors of PGP RSA.
+ (ask_algo): Allow generation of RSA sign and encrypt in expert
+ mode. Don't allow ElGamal S+E unless in expert mode.
+ * helptext.c: Added entry keygen.algo.rsa_se.
+
+2002-05-07 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (sign_uids): If --expert is set, allow re-signing a
+ uid to promote a v3 self-sig to a v4 one. This essentially
+ deletes the old v3 self-sig and replaces it with a v4 one.
+
+ * packet.h, parse-packet.c (parse_key), getkey.c
+ (merge_keys_and_selfsig, merge_selfsigs_main): a v3 key with a v4
+ self-sig must never let the v4 self-sig express a key expiration
+ time that extends beyond the original v3 expiration time.
+
+2002-05-06 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (sign_uids): When making a self-signature via "sign"
+ don't ask about sig level or expiration, and include the usual
+ preferences and such for v4 self-sigs. (menu_set_preferences):
+ Convert uids from UTF8 to native before printing.
+
+ * keyedit.c (sign_uids): Convert uids from UTF8 to native before
+ printing. (menu_set_primary_uid): Show error if the user tries to
+ make a uid with a v3 self-sig primary.
+
+2002-05-05 David Shaw <dshaw@jabberwocky.com>
+
+ * import.c (import_one): When merging with a key we already have,
+ don't let a key conflict (same keyid but different key) stop the
+ import: just skip the bad key and continue.
+
+ * exec.c (make_tempdir): Under Win32, don't try environment
+ variables for temp directories - GetTempDir tries environment
+ variables internally, and it's better not to second-guess it in
+ case MS adds some sort of temp dir handling to Windows at some
+ point.
+
+2002-05-05 Timo Schulz <ts@winpt.org>
+
+ * mainproc.c (proc_symkey_enc): Don't ask for a passphrase
+ in the list only mode.
+
+2002-05-05 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_refresh): --refresh-keys implies
+ --merge-only so as not to import keys with keyids that match the
+ ones being refreshed. Noted by Florian Weimer.
+
+2002-05-04 Stefan Bellon <sbellon@sbellon.de>
+
+ * free-packet.c (copy_public_key): Don't call m_alloc(0), therefore
+ added consistency check for revkey and numrefkeys.
+
+ * getkey.c (check_revocation_keys): Added consistency check for
+ revkey and numrefkeys.
+
+ * keyedit.c (show_key_with_all_names): Likewise.
+
+2002-05-03 David Shaw <dshaw@jabberwocky.com>
+
+ * photoid.c: Provide default image viewer for Win32.
+
+ * misc.c (pct_expando): %t means extension, not name ("jpg", not
+ "jpeg").
+
+ * keyserver.c (keyserver_spawn), photoid.c (show_photos), exec.h,
+ exec.c: Allow the caller to determine the temp file extension when
+ starting an exec_write and change all callers.
+
+ * keyedit.c (sign_uids): Nonrevocable key signatures cause an
+ automatic promotion to v4.
+
+ * exec.c: Provide stubs for exec_ functions when NO_EXEC is
+ defined.
+
+2002-05-02 David Shaw <dshaw@jabberwocky.com>
+
+ * photoid.h, photoid.c (parse_image_header, image_type_to_string):
+ Useful functions to return data about an image.
+
+ * packet.h, parse-packet.c (make_attribute_uidname,
+ parse_attribute_subpkts, parse_attribute), photoid.h, photoid.c
+ (show_photos): Handle multiple images in a single attribute
+ packet.
+
+ * main.h, misc.c (pct_expando), sign.c (mk_notation_and_policy),
+ photoid.c (show_photos): Simpler expando code that does not
+ require using compile-time string sizes. Call
+ image_type_to_string to get image strings (i.e. "jpg",
+ "image/jpeg"). Change all callers.
+
+ * keyedit.c (menu_showphoto), keylist.c (list_keyblock_print):
+ Allow viewing multiple images within a single attribute packet.
+
+ * gpgv.c: Various stubs for link happiness.
+
+2002-05-02 David Shaw <dshaw@jabberwocky.com>
+
+ * build-packet.c (build_sig_subpkt), keyedit.c (sign_uids),
+ options.h, sign.c (mk_notation_and_policy), g10.c (main,
+ add_notation_data, add_policy_url (new), check_policy_url
+ (removed)): Allow multiple policy URLs on a given signature.
+ Split "--notation-data" into "--cert-notation" and
+ "--sig-notation" so the user can set different policies for key
+ and data signing. For backwards compatibility, "--notation-data"
+ sets both, as before.
+
+2002-05-02 Werner Koch <wk@gnupg.org>
+
+ * options.skel: Removed the comment on trusted-keys because this
+ option is now deprecated.
+
+2002-05-01 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (menu_adduid): 2440bis04 says that multiple attribute
+ packets on a given key are legal.
+
+ * keyserver.c (keyserver_refresh): the fake v3 keyid hack applies
+ to "mailto" URLs as well since they are also served by pksd.
+
+2002-04-29 Werner Koch <wk@gnupg.org>
+
+ Added a copyright year for files changed this year.
+
+2002-04-25 Werner Koch <wk@gnupg.org>
+
+ * g10.c, options.h: New options --display, --ttyname, --ttytype,
+ --lc-ctype, --lc-messages to be used with future versions of the
+ gpg-agent.
+ * passphrase.c (agent_send_option,agent_send_all_options): New.
+ (agent_open): Send options to the agent.
+
+ * trustdb.c (update_ownertrust, clear_ownertrust): Do an explicit
+ do_sync because revalidation_mark does it only if when the
+ timestamp actually changes.
+
+2002-04-23 David Shaw <dshaw@jabberwocky.com>
+
+ * main.h, keygen.c (do_generate_keypair), keylist.c
+ (print_signature_stats, list_all, list_one, list_keyblock,
+ list_keyblock_print, list_keyblock_colon): After generating a new
+ key, show the key information (name, keyid, fingerprint, etc.)
+ Also do not print uncheckable signatures (missing key..) in
+ --check-sigs. Print statistics (N missing keys, etc.) after
+ --check-sigs.
+
+ * keyedit.c (sign_uids): When signing a key with an expiration
+ date on it, the "Do you want your signature to expire at the same
+ time?" question should default to YES.
+
+2002-04-22 David Shaw <dshaw@jabberwocky.com>
+
+ * parse-packet.c (parse_plaintext), packet.h, plaintext.c
+ (handle_plaintext): Fix bug in handling literal packets with
+ zero-length data (no data was being confused with partial body
+ length).
+
+ * misc.c (pct_expando), options.skel: %t means extension ("jpg").
+ %T means MIME type ("image/jpeg").
+
+ * import.c (import_one): Only trigger trust update if the keyring
+ is actually changed.
+
+ * export.c (do_export_stream): Missing a m_free.
+
+2002-04-22 Stefan Bellon <sbellon@sbellon.de>
+
+ * keyid.c (expirestr_from_sk, expirestr_from_sig): Added _() to
+ string constant.
+
+ * exec.c (make_tempdir) [__riscos__]: Better placement of
+ temporary file.
+
+2002-04-20 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (generate_subkeypair): 2440bis04 adds that creating
+ subkeys on v3 keys is a MUST NOT.
+
+ * getkey.c (finish_lookup): The --pgp6 "use the primary key"
+ behavior should only apply while data signing and not encryption.
+ Noted by Roger Sondermann.
+
+2002-04-19 Werner Koch <wk@gnupg.org>
+
+ * keygen.c (keygen_set_std_prefs): Put back 3DES because the RFC
+ says it is good form to do so.
+
+2002-04-19 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (menu_deluid): Only cause a trust update if we delete
+ a non-revoked user id.
+
+ * hkp.c (hkp_ask_import), keyserver.c (parse_keyserver_options,
+ keyserver_spawn), options.h: Remove fast-import keyserver option
+ (no longer meaningful).
+
+ * g10.c (main), keyedit.c (sign_uids), options.h: Change
+ --default-check-level to --default-cert-check-level as it makes
+ clear what it operates on.
+
+ * g10.c (main): --pgp6 also implies --no-ask-sig-expire.
+
+ * delkey.c (do_delete_key): Comment.
+
+ * keyedit.c (sign_uids, keyedit_menu, menu_deluid, menu_delsig,
+ menu_expire, menu_revsig, menu_revkey): Only force a trustdb check
+ if we did something that changes it.
+
+ * g10.c: add "--auto-check-trustdb" to override a
+ "--no-auto-check-trustdb"
+
+2002-04-19 Werner Koch <wk@gnupg.org>
+
+ * tdbio.c (tdbio_write_nextcheck): Return a status whether the
+ stamp was actually changed.
+ * trustdb.c (revalidation_mark): Sync the changes. Removed the
+ sync operation done by its callers.
+ (get_validity): Add logic for maintaining a pending_check flag.
+ (clear_ownertrust): New.
+
+ * keyedit.c (sign_uids): Don't call revalidation_mark depending on
+ primary_pk.
+ (keyedit_menu): Call revalidation_mark after "trust".
+ (show_key_with_all_names): Print a warning on the wrong listed key
+ validity.
+
+ * delkey.c (do_delete_key): Clear the owenertrust information when
+ deleting a public key.
+
+2002-04-18 Werner Koch <wk@gnupg.org>
+
+ * seskey.c (encode_md_value): Print an error message if a wrong
+ digest algorithm is used with DSA. Changed all callers to cope
+ with a NULL return. Problem noted by Imad R. Faiad.
+
+2002-04-18 David Shaw <dshaw@jabberwocky.com>
+
+ * trustdb.c (mark_usable_uid_certs): Properly handle nonrevocable
+ signatures that can expire. In short, the only thing that can
+ override an unexpired nonrevocable signature is another unexpired
+ nonrevocable signature.
+
+ * getkey.c (finish_lookup): Always use primary signing key for
+ signatures when --pgp6 is on since pgp6 and 7 do not understand
+ signatures made by signing subkeys.
+
+2002-04-18 Werner Koch <wk@gnupg.org>
+
+ * trustdb.c (validate_keys): Never schedule a nextcheck into the
+ past.
+ (validate_key_list): New arg curtime use it to set next_expire.
+ (validate_one_keyblock): Take the current time from the caller.
+ (clear_validity, reset_unconnected_keys): New.
+ (validate_keys): Reset all unconnected keys.
+
+ * getkey.c (premerge_public_with_secret): Fixed 0x12345678! syntax
+ for use with secret keys.
+ (lookup): Advance the searchmode after a search FIRST.
+
+ * seckey-cert.c (do_check): Always calculate the old checksum for
+ use after unprotection.
+
+ * g10.c, options.skel: New option --no-escape-from. Made
+ --escape-from and --force-v3-sigs the default and removed them
+ from the options skeleton.
+
+2002-04-16 Werner Koch <wk@gnupg.org>
+
+ * parse-packet.c (parse_key): Support a SHA1 checksum as per
+ draft-rfc2440-bis04.
+ * packet.h (PKT_secret_key): Add field sha1chk.
+ * seckey-cert.c (do_check): Check the SHA1 checksum
+ (protect_secret_key): And create it.
+ * build-packet.c (do_secret_key): Mark it as sha-1 protected.
+ * g10.c, options.h: New option --simple-sk-checksum.
+
+2002-04-13 David Shaw <dshaw@jabberwocky.com>
+
+ * parse-packet.c (parse_signature): Minor fix - signatures should
+ expire at their expiration time and not one second later.
+
+ * keygen.c (proc_parameter_file): Allow specifying preferences
+ string (i.e. "s5 s2 z1 z2", etc) in a batchmode key generation
+ file.
+
+ * keyedit.c (keyedit_menu): Print standard error message when
+ signing a revoked key (no new translation).
+
+ * getkey.c (merge_selfsigs): Get the default set of key prefs from
+ the real (not attribute) primary uid.
+
+2002-04-12 David Shaw <dshaw@jabberwocky.com>
+
+ * pkclist.c (build_pk_list): Fix bug that allowed a key to be
+ selected twice in batch mode if one instance was the default
+ recipient and the other was an encrypt-to. Noted by Stefan
+ Bellon.
+
+ * parse-packet.c (dump_sig_subpkt): Show data in trust and regexp
+ sig subpackets.
+
+ * keyedit.c (keyedit_menu): Use new function real_uids_left to
+ prevent deleting the last real (i.e. non-attribute) uid. Again,
+ according to the attribute draft. (menu_showphoto): Make another
+ string translatable.
+
+2002-04-11 David Shaw <dshaw@jabberwocky.com>
+
+ * build-packet.c (build_sig_subpkt): Delete subpackets from both
+ hashed and unhashed area on update. (find_subpkt): No longer
+ needed.
+
+ * keyedit.c (sign_uids): With --pgp2 on, refuse to sign a v3 key
+ with a v4 signature. As usual, --expert overrides. Try to tweak
+ some strings to a closer match so they can all be translated in
+ one place. Use different helptext keys to allow different help
+ text for different questions.
+
+ * keygen.c (keygen_upd_std_prefs): Remove preferences from both
+ hashed and unhashed areas if they are not going to be used.
+
+2002-04-10 David Shaw <dshaw@jabberwocky.com>
+
+ * misc.c (pct_expando), options.skel: Use %t to indicate type of a
+ photo ID (in this version, it's always "jpeg"). Also tweak string
+ expansion loop to minimize reallocs.
+
+ * mainproc.c (do_check_sig): Variable type fix.
+
+ * keyedit.c (menu_set_primary_uid): Differentiate between true
+ user IDs and attribute user IDs when making one of them primary.
+ That is, if we are making a user ID primary, we alter user IDs.
+ If we are making an attribute packet primary, we alter attribute
+ packets. This matches the language in the latest attribute packet
+ draft.
+
+ * keyedit.c (sign_uids): No need for the empty string hack.
+
+ * getkey.c (fixup_uidnode): Only accept preferences from the
+ hashed segment of the self-sig.
+
+2002-04-10 Werner Koch <wk@gnupg.org>
+
+ * tdbio.c (migrate_from_v2): Fixed the offset to read the old
+ ownertrust value and only add entries to the table if we really
+ have a value.
+
+2002-04-08 David Shaw <dshaw@jabberwocky.com>
+
+ * status.h, status.c (get_status_string): Add KEYEXPIRED, EXPSIG,
+ and EXPKEYSIG. Add "deprecated-use-keyexpired-instead" to
+ SIGEXPIRED.
+
+ * sig-check.c (do_check): Start transition from SIGEXPIRED to
+ KEYEXPIRED, since the actual event is signature verification by an
+ expired key and not an expired signature. (do_signature_check,
+ packet.h): Rename as signature_check2, make public, and change all
+ callers.
+
+ * mainproc.c (check_sig_and_print, do_check_sig): Use status
+ EXPSIG for an expired, but good, signature. Add the expiration
+ time (or 0) to the VALIDSIG status line. Use status KEYEXPSIG for
+ a good signature from an expired key.
+
+ * g10.c (main): remove checks for no arguments now that argparse
+ does it.
+
+2002-04-06 Werner Koch <wk@gnupg.org>
+
+ * keyring.c (keyring_get_keyblock): Disable the keylist mode here.
+
+ * encode.c (encode_simple, encode_crypt): Only test on compressed
+ files if a compress level was not explicity set.
+
+ * keygen.c (keygen_set_std_prefs): Removed Blowfish and Twofish
+ from the list of default preferences, swapped the preferences of
+ RMD160 and SHA1. Don't include a preference to 3DES unless the
+ IDEA kludge gets used.
+
+ * free-packet.c (free_packet): call free_encrypted also for
+ PKT_ENCRYPTED_MDC.
+
+ * compress.c (release_context): New.
+ (handle_compressed): Allocate the context and setup a closure to
+ release the context. This is required because there is no
+ guarabntee that the filter gets popped from the chain at the end
+ of the function. Problem noted by Timo and probably also the
+ cause for a couple of other reports.
+ (compress_filter): Use the release function if set.
+
+ * tdbio.c [__CYGWIN32__]: Don't rename ftruncate. Noted by
+ Disastry.
+
+ * parse-packet.c (parse_signature): Put parens around a bit test.
+
+ * exec.c (make_tempdir): Double backslash for TMP directory
+ creation under Windows. Better strlen the DIRSEP_S constants for
+ allocation measurements.
+
+ * decrypt.c (decrypt_messages): Release the passphrase aquired
+ by get_last_passphrase.
+
+2002-04-02 Werner Koch <wk@gnupg.org>
+
+ * Makefile.am (EXTRA_DIST): Removed OPTIONS an pubring.asc - they
+ are no longer of any use.
+
+2002-04-03 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (parse_keyserver_options): fix auto-key-retrieve to
+ actually work as a keyserver-option (noted by Roger Sondermann).
+
+ * keylist.c (reorder_keyblock): do not reorder the primary
+ attribute packet - the first user ID must be a genuine one.
+
+2002-03-31 David Shaw <dshaw@jabberwocky.com>
+
+ * keylist.c (list_keyblock_colon): Fix ownertrust display with
+ --with-colons.
+
+ * keygen.c (generate_user_id), photoid.c (generate_photo_id):
+ Properly initialize the user ID refcount. A few more "y/n" ->
+ "y/N" in photoid.c.
+
+ * keyedit.c (ask_revoke_sig): Warn the user if they are about to
+ revoke an expired sig (not a problem, but they should know). Also
+ tweak a few prompts to change "y/n" to "y/N", which is how most
+ other prompts are written.
+
+ * keyserver.c (keyserver_search_prompt): Control-d escapes the
+ keyserver search prompt.
+
+ * pkclist.c (show_revocation_reason & callers): If a subkey is
+ considered revoked solely because the parent key is revoked, print
+ the revocation reason from the parent key.
+
+ * trustdb.c (get_validity): Allow revocation/expiration to apply
+ to a uid/key with no entry in the trustdb.
+
+2002-03-29 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (printunquoted): unquote backslashes from keyserver
+ searches
+
+ * hkp.c (write_quoted): quote backslashes from keyserver searches
+
+2002-03-26 Werner Koch <wk@gnupg.org>
+
+ * keygen.c (ask_keysize): Removed the warning for key sizes > 1536.
+
+2002-03-25 Werner Koch <wk@gnupg.org>
+
+ * keyedit.c (sign_uids): Use 2 strings and not a %s so that
+ translations can be done the right way.
+ * helptext.c: Fixed small typo.
+
+2002-03-23 David Shaw <dshaw@jabberwocky.com>
+
+ * import.c (append_uid, merge_sigs): it is okay to import
+ completely non-signed uids now (with --allow-non-selfsigned-uid).
+
+ * getkey.c (get_primary_uid, merge_selfsigs_main): do not choose
+ an attribute packet (i.e. photo) as primary uid. This prevents
+ oddities like "Good signature from [image of size 2671]". This is
+ still not perfect (one can still select an attribute packet as
+ primary in --edit), but is closer to the way the draft is going.
+
+ * g10.c (build_list): algorithms should include 110.
+
+ * g10.c (main): --pgp2 implies --no-ask-sig-expire and
+ --no-ask-cert-expire as those would cause a v4 sig/cert.
+
+ * armor.c (is_armor_header): be more lenient in what constitutes a
+ valid armor header (i.e. -----BEGIN blah blah-----) as some
+ Windows programs seem to add spaces at the end. --openpgp makes
+ it strict again.
+
+2002-03-18 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_search_prompt): Properly handle a "no
+ keys found" case from the internal HKP code (external HKP is ok).
+ Also, make a COUNT -1 (i.e. streamed) keyserver response a little
+ more efficient.
+
+ * g10.c (main): Add --no-allow-non-selfsigned-uid
+
+2002-03-17 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): --openpgp implies --allow-non-selfsigned-uid.
+
+ * getkey.c (merge_selfsigs_main): If none of the uids are primary
+ (because none are valid) then pick the first to be primary (but
+ still invalid). This is for cosmetics in case some display needs
+ to print a user ID from a non-selfsigned key. Also use
+ --allow-non-selfsigned-uid to make such a key valid and not
+ --always-trust. The key is *not* automatically trusted via
+ --allow-non-selfsigned-uid.
+
+ * mainproc.c (check_sig_and_print): Make sure non-selfsigned uids
+ print [uncertain] on verification even though one is primary now.
+
+ * getkey.c (merge_selfsigs): If the main key is not valid, then
+ neither are the subkeys.
+
+ * import.c (import_one): Allow --allow-non-selfsigned-uid to work
+ on completely unsigned keys. Print the uids in UTF8. Remove
+ mark_non_selfsigned_uids_valid().
+
+ * keyedit.c (show_key_with_all_names): Show revocation key as
+ UTF8.
+
+ * sign.c (clearsign_file): Allow --not-dash-escaped to work with
+ v3 keys.
+
+2002-03-14 Werner Koch <wk@gnupg.org>
+
+ * main.h: Changed the default algorithms to CAST5 and SHA1.
+
+2002-03-13 David Shaw <dshaw@jabberwocky.com>
+
+ * import.c (chk_self_sigs): Show which user ID a bad self-sig
+ (invald sig or unsupported public key algorithm) resides on.
+
+ * import.c (chk_self_sigs): any valid self-sig should mark a user
+ ID or subkey as valid - otherwise, an attacker could DoS the user
+ by inventing a bogus invalid self-signature.
+
+2002-03-07 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): make a few more strings translatable.
+
+ * options.h, options.skel, g10.c (main), gpgv.c, mainproc.c
+ (check_sig_and_print), keyserver.c (parse_keyserver_options):
+ --auto-key-retrieve should really be a keyserver-option variable.
+
+ * import.c (revocation_present): new function to print a warning
+ if a key is imported that has been revoked by designated revoker,
+ but the designated revoker is not present to verify the
+ revocation. If keyserver-options auto-key-retrieve is set, try
+ and fetch the designated revoker from the keyserver.
+
+ * import.c (import_one): call revocation_present after importing a
+ new key. Note that this applies to --import, --recv-keys, and
+ --search-keys.
+
+ * keyserver-internal.h, keyserver.c (keyserver_import_fprint):
+ import via fingerprint (for revocation keys).
+
+ * keyserver.c (keyserver_import_keyid): much simpler
+ implementation now that we're using KEYDB_SEARCH_DESC internally.
+
+2002-03-04 David Shaw <dshaw@jabberwocky.com>
+
+ * revoke.c (gen_revoke): do not prompt for revocation reason for
+ v3 revocations (unless force-v4-certs is on) since they wouldn't
+ be used anyway.
+
+ * keyedit.c (menu_revsig): show the status of the sigs
+ (exportable? revocable?) to the user before prompting for which
+ sig to revoke. Also, make sure that local signatures get local
+ revocations.
+
+ * keyedit.c (ask_revoke_sig): remind the user which sigs are
+ local.
+
+ * g10.c (main): Add "exec-path" variable to override PATH for
+ execing programs.
+
+ * export.c (do_export_stream): properly check return code from
+ classify_user_id to catch unclassifiable keys.
+
+2002-03-03 David Shaw <dshaw@jabberwocky.com>
+
+ * parse-packet.c (parse_signature): variable type tweak for RISC
+ OS (from Stefan)
+
+2002-02-28 David Shaw <dshaw@jabberwocky.com>
+
+ * getkey.c (check_revocation_keys): New function to check a
+ revocation against a list of potential revocation keys. Note the
+ loop-breaking code here. This is to prevent blowing up if A is
+ B's revocation key, while B is also A's. Note also that this is
+ written so that a revoked revoker can still issue revocations:
+ i.e. If A revokes B, but A is revoked, B is still revoked. I'm
+ not completely convinced this is the proper behavior, but it
+ matches how PGP does it. It does at least have the advantage of
+ much simpler code - my first version of this had lots of loop
+ maintaining code so you could chain revokers many levels deep and
+ if D was revoked, C was not, which meant that B was, and so on.
+ It was sort of scary, actually.
+
+ * getkey.c (merge_selfsigs_main): Add any revocation keys onto the
+ pk. This is particularly interesting since we normally only get
+ data from the most recent 1F signature, but you need multiple 1F
+ sigs to properly handle revocation keys (PGP does it this way, and
+ a revocation key could be marked "sensitive" and hence in a
+ different signature). Also, if a pk has a revocation key set,
+ check for revocation sigs that were not made by us - if made by a
+ valid revocation key, mark the pk revoked.
+
+ * packet.h, getkey.c (cache_public_key): do not cache key if
+ "dont_cache" is set. This allows the revocation key code to look
+ up a key and return information that may be inaccurate to prevent
+ loops without caching the fake data.
+
+ * packet.h, sig-check.c (do_signature_check): Record if a
+ signature was made by a revoked pk.
+
+ * packet.h, parse-packet.c (parse_one_sig_subpkt,
+ can_handle_critical, parse_signature): Get revocation key
+ information out of direct sigs.
+
+ * keylist.c (list_keyblock_print): don't assume that the presence
+ of a 0x20 signature means the key is revoked. With revocation
+ keys, this may not be true if the revocation key is not around to
+ verify it or if verification failed. Also, 0x1F should get listed
+ as "sig", and not "unexpected signature class".
+
+ * keyedit.c (show_key_with_all_names): Add a flag for printing
+ revoker information and change all callers.
+
+ * import.c (merge_blocks): merge in any new direct key (0x1F)
+ sigs.
+
+ * import.c (import_revoke_cert): don't keep processing after a
+ revocation is rejected.
+
+ * import.c (delete_inv_parts): Allow importing a revocation
+ signature even if it was not issued by the key. This allows a
+ revocation key to issue it. Of course, the sig still needs to be
+ checked before we trust it.
+
+ * free-packet.c (copy_public_key): Include a new copy of the
+ revocation keys when duping a pk.
+
+ * free-packet.c (free_seckey_enc, release_public_key_parts): Free
+ any revocation keys that are attached to a sig or pk.
+
+ * export.c (do_export_stream): Do not export signatures with
+ "sensitive" revocation keys in them.
+
+2002-02-27 David Shaw <dshaw@jabberwocky.com>
+
+ * export.c (do_export_stream): Do not include v3 keys in a
+ --export-secret-subkeys export.
+
+ * getkey.c (merge_selfsigs_main): If a key isn't valid (say,
+ because of no self-signature), allow --always-trust to force it
+ valid so it can be trusted.
+
+2002-02-25 David Shaw <dshaw@jabberwocky.com>
+
+ * hkp.c (hkp_ask_import), hkp.h, keyserver.c (all): treat key
+ lists internally as fingerprints when possible. All this is via
+ KEYDB_SEARCH_DESC - no point in reinventing the wheel. This allows
+ the helper program to search the keyserver by fingerprint if
+ desired (and the keyserver supports it). Note that automatic
+ fingerprint promotion during refresh only applies to v4 keys as a
+ v4 fingerprint can be easily changed into a long or short key id,
+ and a v3 cannot.
+
+ * pubkey-enc.c, getkey.c, misc.c, main.h: Take two copies of
+ hextobyte() from pubkey-enc.c and getkey.c and make them into one
+ copy in misc.c.
+
+2002-02-22 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_search_prompt): Detect a "no keys found"
+ case even if the helper program does not explicitly say how many
+ keys were found.
+
+ * hkp.c (parse_hkp_index): Bug fix - don't report non-revoked keys
+ as revoked in HKP key searches.
+
+2002-02-19 Werner Koch <wk@gnupg.org>
+
+ * parse-packet.c (parse_trust): Made parsing more robust.
+
+2002-02-19 David Shaw <dshaw@jabberwocky.com>
+
+ * hkp.c (parse_hkp_index): Catch corruption in HKP index lines
+ (can be caused by broken or malicious keyservers).
+
+ * keyserver.c (keyserver_work): Add KEYSERVER_NOT_SUPPORTED for
+ unsupported actions (say, a keyserver that has no way to search,
+ or a readonly keyserver that has no way to add). Also add a
+ USE_EXTERNAL_HKP define to disable the internal HKP keyserver
+ code.
+
+2002-02-14 Werner Koch <wk@gnupg.org>
+
+ * g10.c: New option --no-use-agent.
+
+ * pkclist.c (check_signatures_trust): Always print the warning for
+ unknown and undefined trust. Removed the did_add cruft. Reported
+ by Janusz A. Urbanowicz.
+
+2002-02-11 David Shaw <dshaw@jabberwocky.com>
+
+ * hkp.c (parse_hkp_index): Bug fix - properly handle user IDs with
+ colons (":") in them while HKP searching.
+
+2002-02-09 David Shaw <dshaw@jabberwocky.com>
+
+ * misc.c (pct_expando): More comments.
+
+ * keydb.h, sign.c (mk_notation_and_policy): Clarify what is a sig
+ and what is a cert. A sig has sigclass 0x00, 0x01, 0x02, or 0x40,
+ and everything else is a cert.
+
+ * g10.c (main), keyedit.c (keyedit_menu): Add a "nrlsign" for
+ nonrevocable and local key signatures.
+
+ * g10.c (main): Add a --no-force-mdc to undo --force-mdc.
+
+ * options.h, g10.c (main), cipher.c (write_header): Add a knob to
+ --disable-mdc/--no-disable-mdc. Off by default, of course, but is
+ used in --pgp2 and --pgp6 modes.
+
+ * pkclist.c (build_pk_list): Allow specifying multiple users in
+ the "Enter the user ID" loop. Enter a blank line to stop. Show
+ each key+id as it is added.
+
+ * keylist.c (show_policy_url), mainproc.c (print_notation_data):
+ It is not illegal (though possibly silly) to have multiple policy
+ URLs in a given signature, so print all that are present.
+
+ * hkp.c (hkp_search): More efficient implementation of URL-ifying
+ code.
+
+2002-02-04 David Shaw <dshaw@jabberwocky.com>
+
+ * main.h, misc.c (pct_expando): New function to generalize
+ %-expando processing in any arbitrary string.
+
+ * photoid.c (show_photo): Call the new pct_expando function rather
+ than expand strings internally.
+
+ * sign.c (mk_notation_and_policy): Show policy URLs and notations
+ when making a signature if show-policy/show-notation is on.
+ %-expand policy URLs during generation. This lets the user have
+ policy URLs of the form "http://notary.jabberwocky.com/keysign/%K"
+ which will generate a per-signature policy URL.
+
+ * main.h, keylist.c (show_policy_url, show_notation): Add amount
+ to indent so the same function can be used in key listings as well
+ as during sig generation. Change all callers.
+
+2002-02-04 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c, options.h (parse_keyserver_options, keyidlist):
+ Workaround for the pksd and OKS keyserver bug that calculates v4
+ RSA keyids as if they were v3. The workaround/hack is to fetch
+ both the v4 (e.g. 99242560) and v3 (e.g. 68FDDBC7) keyids. This
+ only happens for key refresh while using the HKP scheme and the
+ refresh-add-fake-v3-keyids keyserver option must be set. This
+ should stay off by default.
+
+2002-02-03 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_spawn): Bug fix - do not append keys to
+ each other when --sending more than one.
+
+2002-02-02 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c (main), keyedit.c (sign_uids), sign.c
+ (mk_notation_and_policy): Split "--set-policy-url" into
+ "--cert-policy-url" and "--sig-policy-url" so the user can set
+ different policies for key and data signing. For backwards
+ compatibility, "--set-policy-url" sets both, as before.
+
+2002-01-30 Werner Koch <wk@gnupg.org>
+
+ * g10.c (main): --gen-random --armor does now output a base64
+ encoded string.
+
+2002-01-28 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main), options.h, pkclist.c (algo_available): --pgp6
+ flag. This is not nearly as involved as --pgp2. In short, it
+ turns off force_mdc, turns on no_comment, escape_from, and
+ force_v3_sigs, and sets compression to 1. It also restricts the
+ user to IDEA (if present), 3DES, CAST5, MD5, SHA1, and RIPEMD160.
+ See the comments above algo_available() for lots of discussion on
+ why you would want to do this.
+
+2002-01-27 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (keygen_set_std_prefs): Comment
+
+ * keyedit.c (sign_uids): Bug fix - when signing with multiple
+ secret keys at the same time, make sure each key gets the sigclass
+ prompt.
+
+ * exec.c (exec_finish): Close the iobuf and FILE before trying to
+ waitpid, so the remote process will get a SIGPIPE and exit. This
+ is only a factor when using a pipe to communicate.
+
+ * exec.c (exec_write): Disable cache-on-close of the fd iobuf (is
+ this right? Why is a fd iobuf cached at all?)
+
+2002-01-26 Werner Koch <wk@gnupg.org>
+
+ * g10.c, options.h: New option --gpg-agent-info
+ * passphrase.c (agent_open): Let it override the environment info.
+ * seckey-cert.c (check_secret_key): Always try 3 times when the
+ agent is enabled.
+ * options.skel: Describe --use-agent.
+
+2002-01-24 David Shaw <dshaw@jabberwocky.com>
+
+ * pubkey-enc.c (is_algo_in_prefs, get_it): Only check preferences
+ against keys with v4 self sigs - there is really little point in
+ warning for every single non-IDEA message encrypted to an old key.
+
+ * pkclist.c (select_algo_from_prefs): Only put in the fake IDEA
+ preference if --pgp2 is on.
+
+ * mainproc.c (check_sig_and_print): Print "Expired" for expired
+ but good signatures (this still prints "BAD" for expired but bad
+ signatures).
+
+2002-01-23 David Shaw <dshaw@jabberwocky.com>
+
+ * keygen.c (ask_keysize): Cosmetic: don't present a RSA signing
+ key as a "keypair" which can be 768 bits long (as RSA minimum is
+ 1024).
+
+ * pubkey-enc.c (is_algo_in_prefs): Allow IDEA as a fake preference
+ for v3 keys with v3 selfsigs.
+
+2002-01-22 David Shaw <dshaw@jabberwocky.com>
+
+ * packet.h, getkey.c (merge_selfsigs_main), pkclist.c
+ (select_algo_from_prefs): Implement the fake IDEA preference as
+ per RFC2440:12.1. This doesn't mean that IDEA will be used (the
+ plugin may not be present), but it does mean that a v3 key with a
+ v3 selfsig has an implicit IDEA preference instead of 3DES. v3
+ keys with v4 selfsigs use preferences as normal.
+
+ * encode.c (encode_crypt): if select_algo_from_prefs fails, this
+ means that we could not find a cipher that both keys like. Since
+ all v4 keys have an implicit 3DES preference, this means there is
+ a v3 key with a v3 selfsig in the list. Use 3DES in this case as
+ it is the safest option (we know the v4 key can handle it, and
+ we'll just hope the v3 key is being used in an implementation that
+ can handle it). If --pgp2 is on, warn the user what we're doing
+ since it'll probably break PGP2 compatibility.
+
+ * g10.c (main): Do not force using IDEA for encrypted files in
+ --pgp2 mode - let the fake IDEA preference choose this for us for
+ better compatibility when encrypting to multiple keys, only some
+ of which are v3.
+
+ * keygen.c (keygen_set_std_prefs): Put 3DES on the end of the
+ default cipher pref list (RFC2440: "...it is good form to place it
+ there explicitly."). If the user has the IDEA plugin installed,
+ put a preference for IDEA *after* 3DES to effectively disable its
+ use for everything except encrypting along with v3 keys.
+
+ * encode.c, g10.c, sign.c: Change the PGP2 warning line from
+ "... will not be usable ..." to "... may not be usable ..." as the
+ user could be using one of the enhanced PGP2 variations.
+
+ * helptext.c: Revise the sign_uid.class help text as suggested by
+ Stefan.
+
+2002-01-20 Werner Koch <wk@gnupg.org>
+
+ * passphrase.c (passphrase_to_dek): Add tryagain_text arg to be
+ used with the agent. Changed all callers.
+ (agent_get_passphrase): Likewise and send it to the agent
+ * seckey-cert.c (do_check): New arg tryagain_text.
+ (check_secret_key): Pass the string to do_check.
+ * keygen.c (ask_passphrase): Set the error text is required.
+ * keyedit.c (change_passphrase): Ditto.
+
+ * passphrase.c (agent_open): Disable opt.use_agent in case of a
+ problem with the agent.
+ (agent_get_passphrase): Ditto.
+ (passphrase_clear_cache): Ditto.
+
+2002-01-19 Werner Koch <wk@gnupg.org>
+
+ * passphrase.c (agent_open): Add support for the new Assuan based
+ gpg-agent. New arg to return the used protocol version.
+ (agent_get_passphrase): Implemented new protocol here.
+ (passphrase_clear_cache): Ditto.
+ (readline): New.
+
+2002-01-15 Timo Schulz <ts@winpt.org>
+
+ * encode.c (encode_crypt_files): Fail if --output is used.
+
+ * g10.c: New command --decrypt-files.
+
+ * decrypt.c (decrypt_messages): New.
+
+2002-01-09 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c, misc.c, gpgv.c: move idea_cipher_warn to misc.c so gpgv.c
+ doesn't need a stub for it any longer.
+
+ * g10.c (get_temp_dir), main.h: no longer used (it's in exec.c now)
+
+ * g10.c (main), delkey.c (delete_keys), main.h : Allow
+ --delete-key (now --delete-keys, though --delete-key still works,
+ of course) to delete multiple keys in one go. This applies to
+ --delete-secret-key(s) and --delete-secret-and-public-key(s) as
+ well.
+
+2002-01-09 Timo Schulz <ts@winpt.org>
+
+ * encode.c (encode_crypt_files): Now it behaves like verify_files.
+
+ * g10.c (main): We don't need to check argc for encode_crypt_files
+ any longer.
+
+2002-01-09 Timo Schulz <ts@winpt.org>
+
+ * exec.c: Include windows.h for dosish systems.
+
+2002-01-08 Timo Schulz <ts@winpt.org>
+
+ * g10.c (main): New description for --encrypt-files.
+
+2002-01-08 Werner Koch <wk@gnupg.org>
+
+ * g10.c (main): Must register the secring for encryption because
+ it is needed to figure out the default recipient. Reported by
+ Roger Sondermann.
+
+2002-01-05 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (menu_adduid): Require --expert before adding a photo
+ ID to a v3 key, and before adding a second photo ID to any key.
+
+ * keyedit.c (keyedit_menu): Don't allow adding photo IDs in
+ rfc1991 or pgp2 mode.
+
+ * getkey.c (merge_selfsigs_subkey): Permit v3 subkeys. Believe it
+ or not, this is allowed by rfc 2440, and both PGP 6 and PGP 7 work
+ fine with them.
+
+ * g10.c, options.h, keyedit.c, sign.c: Move the "ask for
+ expiration" switch off of --expert, which was getting quite
+ overloaded, and onto ask-sig-expire and ask-cert-expire. Both
+ default to off.
+
+ * g10.c (main): Change the default compression algo to 1, to be
+ more OpenPGP compliant (PGP also uses this, so it'll help with
+ interoperability problems as well).
+
+ * encode.c (encode_crypt): Handle compression algo 2, since the
+ default is now 1.
+
+ * build-packet.c (build_attribute_subpkt): Fix off-by-one error.
+
+2002-01-05 Werner Koch <wk@gnupg.org>
+
+ * g10.c (main): Do not register the secret keyrings for certain
+ commands.
+
+ * keydb.c (keydb_add_resource): Use access to test for keyring
+ existence. This avoids cached opened files which are bad under
+ RISC OS.
+
+2002-01-04 David Shaw <dshaw@jabberwocky.com>
+
+ * sign.c (sign_file, sign_symencrypt_file): always use one-pass
+ packets unless rfc1991 is enabled. This allows a signature made
+ with a v3 key to work in PGP 6 and 7. Signatures made with v4
+ keys are unchanged.
+
+ * g10.c (main): Disallow non-detached signatures in PGP2 mode.
+ Move the "you must use files and not pipes" PGP2 warning up so all
+ the PGP2 stuff is together.
+
+ * encode.c (encode_simple): Use the actual filesize instead of
+ partial length packets in the internal literal packet from a
+ symmetric message. This breaks PGP5(?), but fixes PGP2, 6, and 7.
+ It's a decent tradeoff. Note there was only an issue with
+ old-style RFC1991 symmetric messages. 2440-style messages in 6
+ and 7 work with or without partial length packets.
+
+2002-01-03 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): Removed --no-default-check-level option, as it is
+ not consistent with other "default" options. Plus, it is the same
+ as saying --default-check-level 0.
+
+ * exec.c (exec_read): Disallow caching tempfile from child
+ process, as this keeps the file handle open and can cause unlink
+ problems on some platforms.
+
+ * keyserver.c (keyserver_search_prompt): Minor tweak - don't
+ bother to transform keyids into textual form if they're just going
+ to be transformed back to numbers.
+
+2002-01-03 Timo Schulz <ts@winpt.org>
+
+ * g10.c: New command --encrypt-files.
+
+ * verify.c (print_file_status): Removed the static because
+ encode_crypt_files also uses this function.
+
+ * main.h (print_files_status): New.
+ (encode_crypt_files): New.
+
+ * encode.c (encode_crypt_files): New.
+
+2002-01-02 Stefan Bellon <sbellon@sbellon.de>
+
+ * keyserver.c: Moved util.h include down in order to avoid
+ redefinition problems on RISC OS.
+
+ * keyring.c (keyring_lock): Only lock keyrings that are writable.
+
+ * keyring.c (keyring_update_keyblock): Close unused iobuf.
+
+ * hkp.c (parse_hkp_index, hkp_search) [__riscos__]: Changed
+ unsigned char* to char* because of compiler issues.
+
+ * exec.c (exec_finish) [__riscos__]: Invalidate close cache so
+ that file can be unlinked.
+
+2001-12-28 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (main): Use a different strlist to check extensions since
+ they need to be handled seperately now.
+
+ * misc.c,main.h (check_permissions): Properly handle permission
+ and ownership checks on files in the lib directory
+ (e.g. /usr/local/lib/gnupg), which are owned by root and are
+ world-readable, and change all callers to specify extension or
+ per-user file.
+
+ * photoid.c (show_photo), keyserver.c (keyserver_spawn): Bug fix -
+ don't call exec_finish if exec_write fails.
+
+ * keyserver.c (keyserver_spawn): Look for OPTIONS from the
+ keyserver helper - specifically, a "OUTOFBAND" option for the
+ email keyserver.
+
+ * mainproc.c (list_node), keylist.c (list_keyblock_colon),
+ import.c (delete_inv_parts), export.c (do_export_stream): Use
+ signature flags for exportability check rather than re-parsing the
+ subpacket.
+
+ * keyid.c, keydb.h (get_lsign_letter): No longer needed.
+
+2001-12-27 David Shaw <dshaw@jabberwocky.com>
+
+ * exec.c (exec_finish): Show errors when temp files cannot be
+ deleted for whatever reason.
+
+ * exec.c (exec_read): Don't rely on WEXITSTATUS being present.
+
+ * exec.c (make_tempdir): Add temp file creator for win32. Don't
+ create an incoming temp file if the exec is write-only.
+
+ * keyserver.c (keyserver_spawn): Clean up error handling, for when
+ the spawn fails.
+
+ * photoid.c (show_photo): Clean up error handling.
+
+ * misc.c (check_permissions): Neaten.
+
+2001-12-25 David Shaw <dshaw@jabberwocky.com>
+
+ * mkdtemp.c (mkdtemp): Add copyleft info and tweak the 'X' counter
+ to be a bit simpler.
+
+ * keyserver.c, photoid.c: Remove unused headers left over from
+ when the exec functions lived there.
+
+2001-12-23 Timo Schulz <ts@winpt.org>
+
+ * misc.c (check_permissions): Do not use it for W32 systems.
+
+ * tdbio.c (migrate_from_v2): Define ftruncate as chsize() for W32.
+
+ * mkdtemp.c: W32 support.
+
+ * photoid.c: Ditto.
+
+ * exec.c: Ditto.
+
+2001-12-22 David Shaw <dshaw@jabberwocky.com>
+
+ * exec.c (make_tempdir): avoid compiler warning with const
+
+ * mkdtemp.c (mkdtemp): catch the empty ("") string case in case
+ someone repurposes mkdtemp at some point.
+
+ * photoid.c (generate_photo_id, show_photo): some type changes
+ from Stefan Bellon.
+
+ * exec.c (make_tempdir): handle Win32 systems, suggested by Timo
+ Schulz.
+
+2001-12-22 Werner Koch <wk@gnupg.org>
+
+ * encode.c (encode_simple, encode_crypt): i18n 2 strings.
+
+2001-12-22 Timo Schulz <ts@winpt.org>
+
+ * encode.c (encode_simple, encode_crypt): Use is_file_compressed
+ to avoid to compress compressed files.
+
+2001-12-22 Werner Koch <wk@gnupg.org>
+
+ * keyserver.c (keyserver_spawn): Removed some variables
+ declaration due to shadowing warnings.
+
+ * build-packet.c (build_attribute_subpkt): s/index/idx/ to avoid
+ compiler warnig due to index(3).
+
+ * getkey.c (get_ctx_handle): Use KEYDB_HANDLE as return value.
+ * keylist.c (list_one): Made resname const.
+
+ * keyedit.c (keyedit_menu): Allow "addphoto" only when --openpgp is
+ not used.
+
+ * options.skel: Changed one example photo viewer to qiv.
+
+2001-12-21 David Shaw <dshaw@jabberwocky.com>
+
+ * Makefile.am: add exec.c, exec.h, photoid.c, and photoid.h
+
+ * build-packet.c (build_attribute_subpkt): new function to build
+ the raw attribute subpacket. Note that attribute subpackets have
+ the same format as signature subpackets.
+
+ * exec.c: new file with generic exec-a-program functionality.
+ Used by both photo IDs and keyserver helpers. This is pretty much
+ the same code that used to be keyserver specific, with some
+ changes to be usable generically.
+
+ * free-packet.c (free_attributes (new)): function to free an
+ attribute packet.
+
+ * gpgv.c: added stub show_photo
+
+ * keyedit.c (keyedit_menu, menu_adduid, menu_showphoto): can add a
+ photo (calls generate_photo_id), or display a photo (calls
+ show_photo) from the --edit menu. New commands are "addphoto",
+ and "delphoto" (same as "deluid").
+
+ * keylist.c (list_keyblock_print): show photos during key list if
+ --show-photos enabled.
+
+ * keyserver.c (keyserver_spawn): use the generic exec_xxx
+ functions to call keyserver helper.
+
+ * g10.c, options.h: three new options - --{no-}show-photos, and
+ --photo-viewer to give the command line to display a picture.
+
+ * options.skel: instructions for the photo viewer
+
+ * parse-packet.c (parse_user_id, setup_user_id (new)): common code
+ for both user IDs and attribute IDs moved to setup_user_id.
+
+ * parse-packet.c (make_attribute_uidname (new)): constructs a fake
+ "name" for attribute packets (e.g. "[image of size ...]")
+
+ * parse-packet.c (parse_attribute (replaces parse_photo_id),
+ parse_attribute_subpkts): Builds an array of individual
+ attributes. Currently only handles attribute image / type jpeg
+ subpackets.
+
+ * sign.c (hash_uid): Fix bug in signing attribute (formerly
+ photo_id) packets.
+
+ * packet.h, and callers: globally change "photo_id" to "attribute"
+ and add structures for attributes. The packet format is generic
+ attributes, even though the only attribute type thus far defined
+ is jpeg.
+
+2001-12-21 David Shaw <dshaw@jabberwocky.com>
+
+ * parse-packet.c (can_handle_critical): Can handle critical
+ revocation subpackets now.
+
+ * trustdb.c (mark_usable_uid_certs): Disregard revocations for
+ nonrevocable sigs. Note that this allows a newer revocable
+ signature to override an older nonrevocable signature.
+
+ * sign.c (make_keysig_packet): add a duration field and change all
+ callers. This makes make_keysig_packet closer to
+ write_signature_packets and removes some duplicated expiration
+ code.
+
+ * keyedit.c (keyedit_menu, menu_revsig, sign_uids,
+ sign_mk_attrib): Add nrsign command, don't allow revoking a
+ nonrevocable signature,
+
+ * g10.c (main): Add --nrsign option to nonrevocably sign a key
+ from the command line.
+
+ * build-packet.c (build_sig_subpkt_from_sig): Comment to explain
+ the use of CRITICAL.
+
+2001-12-21 Werner Koch <wk@gnupg.org>
+
+ * g10.c. options.h : New option --show-keyring
+ * getkey.c (get_ctx_handle): New.
+ * keylist.c (list_one): Implement option here. By David Champion.
+
+2001-12-20 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (keyserver_spawn): Use mkdtemp() to make temp
+ directory.
+
+ * mkdtemp.c: replacement function for those platforms that don't
+ have mkdtemp (make a temp directory securely).
+
+2001-12-19 David Shaw <dshaw@jabberwocky.com>
+
+ * misc.c (check_permissions): New function to stat() and ensure
+ the permissions of GNUPGHOME and the files have safe permissions.
+
+ * keydb.c (keydb_add_resource): Check keyring permissions.
+
+ * tdbio.c (tdbio_set_dbname): Check permissions of trustdb.gpg
+
+ * keyserver.c (keyserver_spawn): Disable keyserver schemes that
+ involve running external programs if the options file has unsafe
+ permissions or ownership.
+
+ * g10.c, options.h: New option --no-permission-warning to disable
+ the permission warning message(s). This also permits use of the
+ keyserver if it had been disabled (see above). Also check the
+ permissions/ownership of random_seed.
+
+ * keyserver.c (keyserver_spawn): The new glibc prints a warning
+ when using mktemp() (the code was already secure, but the warning
+ was bound to cause confusion). Use a different implementation
+ based on get_random_bits() instead. Also try a few times to get
+ the temp dir before giving up.
+
+2001-12-19 Werner Koch <wk@gnupg.org>
+
+ * g10.c, passphrase.c [CYGWIN32]: Allow this as an alias for MINGW32.
+
+2001-12-18 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c (idea_cipher_warn): Add a flag to show the warning always
+ or once per session and change all callers (show always except for
+ the secret key protection and unknown cipher from an encrypted
+ message errors). Also make the strings translatable.
+
+ * pubkey-enc.c (get_it): Add the IDEA cipher warning if the user
+ tries to decrypt an IDEA encrypted message without the IDEA
+ plugin.
+
+ * keyserver.c (parse_keyserver_uri): More strict checking of the
+ keyserver URI. Specifically, fail if the ":port" section is
+ anything except a number between 1 and 65535.
+
+2001-12-17 David Shaw <dshaw@jabberwocky.com>
+
+ * keyserver.c (print_keyinfo): No need to check for
+ control/illegal characters, as utf8_to_native does this for us.
+
+ * mainproc.c (proc_encrypted): Use generic IDEA warning.
+
+ * gpgv.c: add stub for idea_cipher_warn
+
+ * g10.c, hkp.c, keyserver.c: Fix capitalization and plural issues.
+
+ * encode.c (encode_crypt), sign.c (sign_file, clearsign_file):
+ disable pgp2 mode after the message is no longer pgp2 compatible.
+
+ * g10.c (main): Tweak the PGP2.x IDEA warning to use the generic
+ warning, and not merely fail if the IDEA plugin isn't there.
+
+ * g10.c (main, idea_cipher_warn), keygen.c (set_one_pref),
+ seckey-cert.c (do_check): Add a generic IDEA warning for when the
+ IDEA plugin is not present. This pops up when the user uses
+ "--cipher-algo idea", when setpref is used to set a "S1"
+ preference, and when a secret key protected with IDEA is used.
+
+2001-12-15 Werner Koch <wk@gnupg.org>
+
+ * keyserver.c (keyserver_spawn): Assert that we have dropped privs.
+
+2001-12-13 Werner Koch <wk@gnupg.org>
+
+ * pubkey-enc.c (get_session_key): Check that the public key
+ algorithm is indeed usable for en/decryption. This avoid a
+ strange error message from pubkey_decrypt if for some reasons a
+ bad algorithm indentifier is passed.
+
+2001-12-12 David Shaw <dshaw@jabberwocky.com>
+
+ * Fixed some types for portability. Noted by Stefan Bellon.
+
+2001-12-11 Werner Koch <wk@gnupg.org>
+
+ * hkp.c (hkp_export): Do not print possible control characters
+ from a keyserver response.
+ (parse_hkp_index): Made uid an unsigned char* because it is passed to
+ isspace().
+ (hkp_search): Ditto for the char* vars.
+
+ * g10.c (main): Print the IDEA warning also for -c and -se.
+
+ * g10.c (get_temp_dir): Assert that we have dropped privs
+
+ * encode.c (encode_crypt): Include the first key into the --pgp2
+ check.
+
+2001-12-07 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c, options.h: New option --pgp2. This is identical to
+ "--rfc1991 --cipher-algo idea --compress-algo 1 --digest-algo md5
+ --force_v3_sigs" with the addition of an warning to advise the
+ user not to use a pipe (which would break pgp2 compatibility).
+
+ * encode.c (encode_crypt): warn if the user tries to encrypt to
+ any key that is not RSA and <= 2048 bits when the --pgp2 option is
+ used.
+
+ * sign.c (sign_file, clearsign_file): When using --pgp2, make a v3
+ sig, and warn if the signature is made with a non-v3 key.
+
+2001-12-05 David Shaw <dshaw@jabberwocky.com>
+
+ * sign.c (sign_file, clearsign_file, sign_symencrypt_file): Prompt
+ for sig expiration if --expert is set and --force-v3-sigs is not
+ set (v3 sigs cannot expire).
+
+ * mainproc.c (check_sig_and_print): After checking a sig, print
+ expiration status. This causes a error return if the sig is
+ expired.
+
+ * build-packet.c (build_sig_subpkt_from_sig): Include a critical
+ sig expiration subpacket if the sig is to expire.
+
+ * keyedit.c (sign_uids): Do not sign an expired key unless
+ --expert is set, in which case prompt. Also, offer to expire a
+ signature when the key the user is signing expires.
+
+ * keygen.c (ask_expire_interval): Add a value to determine whether
+ to prompt for a key or sig expiration and change all callers.
+
+ * keyid.c: New functions: expirestr_from_sig and
+ colon_expirestr_from_sig.
+
+ * keylist.c (list_keyblock_colon): Show sig expiration date in the
+ --with-colons listing.
+
+ * sign.c (make_keysig_packet, write_signature_packets): Pass in an
+ optional timestamp for the signature packet, and change all
+ callers.
+
+ * keyedit.c (sign_mk_attrib): Include a critical expiration
+ subpacket in the signature if an expiration date is given.
+
+2001-12-04 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (sign_uids): If the user tries to sign a
+ locally-signed key, allow the cert to be promoted to a full
+ exportable signature. This essentially deletes the old
+ non-exportable sig, and replaces it with a new exportable one.
+
+2001-12-04 David Shaw <dshaw@jabberwocky.com>
+
+ * keyedit.c (keyedit_menu): Do not allow signing a revoked key
+ unless --expert is set, and ask even then.
+
+ * keyedit.c (sign_uids): Do not allow signing a revoked UID unless
+ --expert is set, and ask even then.
+
+ * g10.c, options.h : New option --expert
+
+2001-11-16 David Shaw <dshaw@jabberwocky.com>
+
+ * Allow the user to select no compression via "--compress-algo 0"
+ on the command line.
+
+ * keyedit.c (show_prefs): Show compression preferences in the
+ long-form "showpref" style.
+
+ * keygen.c (set_one_pref): Permit setting a no-compression ("Z0")
+ preference.
+
+ * getkey.c (fixup_uidnode): Fix compression preference corruption
+ bug.
+
+2001-12-02 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c: Add advisory --for-your-eyes-only option as per section
+ 5.9 of 2440.
+
+2001-12-05 David Shaw <dshaw@jabberwocky.com>
+
+ * Force a V4 sig if the user has a notation or policy URL set.
+
+2001-12-04 David Shaw <dshaw@jabberwocky.com>
+
+ * g10.c: Add options --keyserver-options, --temp-directory, and
+ auto-key-retrieve (the opposite of no-auto-key-retrieve).
+
+ * hkp.c (hkp_search): New function to handle searching a HKP
+ keyserver for a key
+
+ * hkp.c (hkp_ask_import, hkp_export): Pretty large changes to make
+ them communicate via the generic functions in keyserver.c
+
+ * keyserver.c: new file with generic keyserver routines for
+ getting keys from a keyserver, sending keys to a keyserver, and
+ searching for keys on a keyserver. Calls the internal HKP stuff
+ in hkp.c for HKP keyserver functions. Other calls are handled by
+ an external program which is spawned and written to and read from
+ via pipes. Platforms that don't have pipes use temp files.
+
+2001-11-20 David Shaw <dshaw@jabberwocky.com>
+
+ * options.h, g10.c: New options show-notation, no-show-notation,
+ default-check-level, no-default-check-level, show-policy-url,
+ no-show-policy-url.
+
+ * packet.h, sign.c (make_keysig_packet), parse-packet.c
+ (parse_signature), free-packet.c (free_seckey_enc): Fill in
+ structures for notation, policy, sig class, exportability, etc.
+
+ * keyedit.c, keylist.c (print_and_check_one_sig,
+ list_keyblock_print): Show flags in signature display for cert
+ details (class, local, notation, policy, revocable). If selected,
+ show the notation and policy url.
+
+ * keyedit.c (sign_uids): Prompt for and use different key sig
+ classes.
+
+ * helptext.c (helptexts): Add help text to explain different
+ key signature classes
+
+2001-11-26 David Shaw <dshaw@jabberwocky.com>
+
+ * trustdb.c (mark_usable_uid_certs): Fix segfault from bad
+ initialization and fix reversed key signature expiration check.
+
+2001-11-09 Werner Koch <wk@gnupg.org>
+
+ * export.c (do_export_stream): Put all given names into a search
+ description and change the loop so that all matching names are
+ returned.
+
+2001-11-08 Werner Koch <wk@gnupg.org>
+
+ * pubkey-enc.c (get_it): To reduce the number of questions on the
+ MLs print the the name of cipher algorithm 1 with the error message.
+
+ * mainproc.c: Changed the way old rfc1991 encryption cipher is
+ selected. Based on a patch by W Lewis.
+
+ * pkclist.c (do_edit_ownertrust): Allow to skip over keys, the non
+ working "show info" is now assigned to "i"
+ * trustdb.c (ask_ownertrust, validate_keys): Implement a real quit
+ here. Both are by David Shaw.
+
+ * trustdb.c (validate_keys): Make sure next_exipire is initialized.
+
+ * sign.c (make_keysig_packet): Use SHA-1 with v4 RSA keys.
+
+ * g10.c, options.h : New option --[no-]froce-v4-certs.
+ * sign.c (make_keysig_packet): Create v4 sigs on v4 keys even with
+ a v3 key. Use that new option. By David Shaw
+
+ * revoke.c (ask_revocation_reason): Allow to select "no reason".
+ By David Shaw.
+
+ * keyid.c (fingerprint_from_sk): Calculation of an v3 fpr was
+ plain wrong - nearly the same code in fingerprint_from_pk is correct.
+
+ * build-packet.c (do_secret_key): Added a few comments to the code.
+
+2001-11-07 Werner Koch <wk@gnupg.org>
+
+ * g10.c (main): Print a warning when -r is used w/o encryption.
+ Suggested by Pascal Scheffers.
+
+2001-10-23 Werner Koch <wk@gnupg.org>
+
+ * keyedit.c (keyedit_menu): Changed helptext for showpref
+ command. Suggested by Reinhard Wobst.
+
+ * keyring.c (keyring_search): When marking the offtbl ready, take
+ into account that we may have more than one keyring.
+
+2001-10-22 Werner Koch <wk@gnupg.org>
+
+ * Makefile.am: Do not use OMIT_DEPENDENCIES
+
+ * build-packet.c (build_sig_subpkt): Default is now to put all
+ types of subpackets into the hashed area and only list those which
+ should go into the unhashed area.
+
+2001-10-18 Werner Koch <wk@gnupg.org>
+
+ * keydb.c (keydb_add_resource): Rearranged the way we keep track
+ of the resource. There will now be an entry for each keyring here
+ and not in keyring.c itself. Store a token to allow creation of a
+ keyring handle. Changed all functions to utilize this new design.
+ (keydb_locate_writable): Make a real implementation.
+ * keyring.c (next_kr): Removed and changed all callers to set the
+ resource directly from the one given with the handle.
+ (keyring_is_writable): New.
+ (keyring_rebuild_cache): Add an arg to pass the token from keydb.
+
+2001-10-17 Werner Koch <wk@gnupg.org>
+
+ * keyring.c (keyring_search): Enabled word search mode but print a
+ warning that it is buggy.
+
+2001-10-11 Werner Koch <wk@gnupg.org>
+
+ * hkp.c (hkp_ask_import): No more need to set the port number for
+ the x-hkp scheme.
+ (hkp_export): Ditto.
+
+2001-10-06 Stefan Bellon <sbellon@sbellon.de>
+
+ * passphrase.c [__riscos__]: Disabled agent specific stuff.
+ * g10.c: New option --no-force-v3-sigs.
+
+2001-10-04 Werner Koch <wk@gnupg.org>
+
+ * export.c (do_export_stream): Do not push the compress filter
+ here because the context would run out of scope due to the
+ iobuf_close done by the caller.
+ (do_export): Do it here instead.
+
+2001-09-28 Werner Koch <wk@gnupg.org>
+
+ * keyedit.c (sign_uids): Always use the primary key to sign keys.
+ * getkey.c (finish_lookup): Hack to return only the primary key if
+ a certification key has been requested.
+
+ * trustdb.c (cmp_kid_for_make_key_array): Renamed to
+ (validate_one_keyblock): this and changed arg for direct calling.
+ (make_key_array): Renamed to
+ (validate_one_keyblock): this and changed args for direct calling.
+ (mark_usable_uid_certs, validate_one_keyblock)
+ (validate_key_list): Add next_expire arg to keep track of
+ expiration times.
+ (validate_keys): Ditto for UTKs and write the stamp.
+
+ * tdbio.c (migrate_from_v2): Check return code of tbdio_sync.
+
+ * tdbdump.c (import_ownertrust): Do a tdbio_sync().
+
+ * keyring.c: Made the offtbl an global object.
+
+2001-09-27 Werner Koch <wk@gnupg.org>
+
+ * pkclist.c (do_edit_ownertrust): Allow settin of ultimate trust.
+
+ * trustdb.c (mark_keyblock_seen): New.
+ (make_key_array): Use it to mark the subkeys too.
+ (validate_keys): Store validity for ultimatly trusted keys.
+
+2001-09-26 Werner Koch <wk@gnupg.org>
+
+ * pkclist.c (check_signatures_trust, do_we_trust): Removed the
+ invocation of add_ownertrust. Minor changes to the wording.
+ (add_ownertrust, add_ownertrust_cb): Removed.
+
+ * trustdb.c (get_validity): Allow to lookup the validity using a
+ subkey.
+
+ * trustdb.c (new_key_hash_table): Increased the table size to 1024
+ and changed the masks accordingly.
+ (validate): Changed stats printing.
+ (mark_usable_uid_certs): New.
+ (cmp_kid_for_make_key_array): Does now check the signatures and
+ figures out a usable one.
+
+2001-09-25 Werner Koch <wk@gnupg.org>
+
+ * keyring.c (new_offset_item,release_offset_items)
+ (new_offset_hash_table, lookup_offset_hash_table)
+ (update_offset_hash_table, update_offset_hash_table_from_kb): New.
+ (keyring_search): Use a offset table to optimize search for
+ unknown keys.
+ (keyring_update_keyblock, keyring_insert_keyblock): Insert new
+ offsets.
+ * getkey.c (MAX_UNK_CACHE_ENTRIES): Removed the unknown keys
+ caching code.
+
+ * g10.c, options.h, import.c: Removed the entire
+ allow-secret-key-import stuff because the validity is now
+ controlled by other means.
+
+ * g10.c: New command --rebuild-keydb-caches.
+ * keydb.c (keydb_rebuild_caches): New.
+ * keyring.c (do_copy): Moved some code to
+ (create_tmp_file, rename_tmp_file, write_keyblock): new functions.
+ (keyring_rebuild_cache): New.
+
+ * packet.h (PKT_ring_trust): Add sigcache field.
+ * parse-packet.c (parse_trust): Parse sigcache.
+ * keyring.c (do_copy): Always insert a sigcache packet.
+ (keyring_get_keyblock): Copy the sigcache packet to the signature.
+ * sig-check.c (cache_sig_result): Renamed from
+ cache_selfsig_result. Changed implementation to use the flag bits
+ and changed all callers.
+ (mdc_kludge_check): Removed this unused code.
+ (do_check): Do not set the sig flags here.
+
+ * import.c (read_block): Make sure that ring_trust packets are
+ never imported.
+ * export.c (do_export_stream): and never export them.
+
+ * trustdb.c (make_key_array): Skip revoked and expired keys.
+
+2001-09-24 Werner Koch <wk@gnupg.org>
+
+ * g10.c, options.h: New option --no-auto-check-trustdb.
+
+ * keygen.c (do_generate_keypair): Set newly created keys to
+ ultimately trusted.
+
+ * tdbio.h, tdbio.c: Removed all support for records DIR, KEY, UID,
+ PREF, SIG, SDIR and CACH. Changed migration function to work
+ direct on the file.
+ (tdbio_read_nextcheck): New.
+ (tdbio_write_nextcheck): New.
+
+2001-09-21 Werner Koch <wk@gnupg.org>
+
+ Revamped the entire key validation system.
+ * trustdb.c: Complete rewrite. No more validation on demand,
+ removed some functions, adjusted to all callers to use the new
+ and much simpler interface. Does not use the LID anymore.
+ * tdbio.c, tdbio.h: Add new record types trust and valid. Wrote a
+ migration function to convert to the new trustdb layout.
+ * getkey.c (classify_user_id2): Do not allow the use of the "#"
+ prefix.
+ * keydb.h: Removed the TDBIDX mode add a skipfnc to the
+ descriptor.
+ * keyring.c (keyring_search): Implemented skipfnc.
+
+ * passphrase.c (agent_open): Add missing bracket. Include windows.h.
+
+2001-09-19 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (print_fingerprint): Renamed from fingerprint, made
+ global available. Added new arg to control the print style.
+ * mainproc.c (print_fingerprint): Removed.
+ * pkclist.c (print_fpr, fpr_info): Removed and changed callers to
+ use print_fingerprint.
+ * keyedit.c (show_fingerprint): Ditto.
+
+ * passphrase.c (writen, readn)
+ (agent_open, agent_close)
+ (agent_get_passphrase)
+ (passphrase_clear_cache): Support for W32. Contributed by Timo.
+
+ * import.c (import_one): Release keydb handles at 2 more places.
+
+ * keyring.c (keyring_release): Close the iobuf.
+ (keyring_get_keyblock): Init ret_kb to NULL and store error contidion.
+
+ * import.c (import_new_stats_handle): New.
+ (import_release_stats_handle): New.
+ (import_print_stats): Renamed from static fnc print_stats.
+ (import_keys, import_keys_stream): Add an optional status handle
+ arg and changed all callers.
+ * hkp.c (hkp_ask_import): Add an stats_handle arg and changed all
+ callers.
+
+ * mainproc.c (print_pkenc_list): Use print_utf8_string2().
+
+2001-09-18 Werner Koch <wk@gnupg.org>
+
+ * g10.c: New command --refresh-keys.
+ * hkp.c (hkp_refresh_keys): New. Contributed by Timo Schulz.
+
+ * parse-packet.c (parse): Stop on impossible packet lengths.
+
+2001-09-17 Werner Koch <wk@gnupg.org>
+
+ * mainproc.c (print_notation_data): Wrap notation data status lines
+ after 50 chars.
+
+ * mainproc.c (proc_pubkey_enc): Make option try-all-secrets work.
+ By disastry@saiknes.lv.
+
+2001-09-14 Werner Koch <wk@gnupg.org>
+
+ * parse-packet.c (dump_sig_subpkt): List key server preferences
+ and show the revocable flag correctly. Contributed by David Shaw.
+
+2001-09-09 Werner Koch <wk@gnupg.org>
+
+ * keyedit.c (keyedit_menu): No need to define another p.
+
+ * keylist.c (print_capabilities): s/used/use/ so that it
+ does not shadow a global.
+ * sign.c (sign_file): Renamed arg encrypt to encryptflag
+ * keygen.c: Replaced all "usage" by "use".
+ * misc.c (openpgp_pk_algo_usage): Ditto.
+
+ * pubkey-enc.c (get_it): Renamed arg k to enc so that the later
+ defined k does not shadow it.
+
+ * parse-packet.c (parse_gpg_control): No need to define another i.
+
+ * getkey.c (get_pubkey_byfprint): Must use the enum values and not
+ the fprint_len.
+ * keyring.c (keyring_search): Removed a non-sense break. Both
+ bugs pointed out by Stefan.
+
+2001-09-07 Werner Koch <wk@gnupg.org>
+
+ * status.c, status.h: Added NO_RECP and ALREADY_SIGNED.
+ * pkclist.c (build_pk_list): Issue NO_RECP.
+ * keyedit.c (sign_uids): Added experimental ALREADY_SIGNED
+
+ * hkp.c (hkp_import): Use log_error. Bug reported by Neal H
+ Walfield.
+
+ * getkey.c (classify_user_id2): Change args to take the desc union
+ direct. It was a stupid idea to pass the individual fields of an
+ union to this function. Changed all callers.
+ (classify_user_id): Ditto and allow to pass NULL as the description.
+
+2001-09-06 Werner Koch <wk@gnupg.org>
+
+ * getkey.c (fixup_uidnode): Features flag is now a bit vector.
+ * keygen.c (add_feature_mdc): Ditto.
+
+ Revamped the entire key I/O code to be prepared for other ways of
+ key storages and to get rid of the existing shit. GDBM support has
+ gone.
+ * keydb.c: New
+ * keyring.c, keyring.h: New.
+ * ringedit.c: Removed. Moved some stuff to keyring.c
+ * getkey.c: Changed everything related to the key retrieving
+ functions which are now using the keydb_ functions.
+ (prepare_search, word_match_chars, word_match)
+ (prepare_word_match, compare_name): Moved to keyring.c
+ (get_pubkey_byname): Removed ctx arg and add ret_kdbhd
+ arg. Changed all callers.
+ (key_byname): Use get_pubkey_end to release the context and take
+ new ret_kbdhd arg. Changed all callers.
+ (classify_user_id2): Fill the 16 byte fingerprint up with 4 null
+ bytes not with zero bytes of value 4, tsss.
+ * import.c (import_one): Updated to use the new keydb interface.
+ (import_secret_one): Ditto.
+ (import_revoke_cert): Ditto.
+ * delkey.c (do_delete_key): Ditto.
+ * keyedit.c (keyedit_menu): Ditto.
+ (get_keyblock_byname): Removed.
+ * revoke.c (gen_revoke): Ditto.
+ * export.c (do_export_stream): Ditto.
+ * trustdb.c (update_trustdb): Ditto.
+ * g10.c, gpgv.c (main): Renamed add_keyblock_resource to
+ keydb_add_resource.
+ * Makefile.am: Added and removed files.
+
+ * keydb.h: Moved KBNODE typedef and MAX_FINGERPRINT_LEN to
+ * global.h: this new header.
+
+2001-09-03 Werner Koch <wk@gnupg.org>
+
+ * passphrase.c (agent_get_passphrase): Changed nread to size_t.
+ (passphrase_clear_cache): Ditto.
+
+ * keyid.c (mk_datestr): Avoid trigraphs.
+ (fingerprint_from_pk): Cache the keyid in the pk.
+
+ * options.h: Add opt.with_fingerprint so that we know whether the
+ corresponding options was used.
+ * g10.c (main): Set it here.
+ * pkclist.c (check_signatures_trust): Always print fingerprint
+ when this option is used. Mixed a minor memory leak.
+
+ * status.c, status.h: New status INV_RECP.
+ * pkclist.c (build_pk_list): Issue this status.
+
+2001-08-31 Werner Koch <wk@gnupg.org>
+
+ * parse-packet.c (parse_key,parse_pubkeyenc)
+ (parse_signature): Return error on reading bad MPIs.
+
+ * mainproc.c (check_sig_and_print): Always print the user ID even
+ if it is not bound by a signature. Use the primary UID in the
+ status messages and encode them in UTF-8
+ * status.c (write_status_text_and_buffer): New.
+
+2001-08-30 Werner Koch <wk@gnupg.org>
+
+ * packet.h (sigsubpkttype_t): Add SIGSUBPKT_FEATURES.
+ (PKT_public_key, PKT_user_id): Add a flag for it.
+ * parse-packet.c, build-packet.c: Add support for them.
+ * getkey.c (fixup_uidnode, merge_selfsigs): Set the MDC flags.
+ * keygen.c (add_feature_mdc): New.
+ (keygen_upd_std_prefs): Always set the MDC feature.
+ * keyedit.c (show_prefs): List the MDC flag
+ * pkclist.c (select_mdc_from_pklist): New.
+ * encode.c (encode_crypt, encrypt_filter): Test whether MDC
+ should be used.
+ * cipher.c (write_header): Set MDC use depending on the above test.
+ Print more status info.
+
+ * delkey.c (do_delete_key): Kludge to delete a secret key with no
+ public key available.
+
+ * ringedit.c (find_secret_keyblock_direct): New.
+ * getkey.c (seckey_available): Simplified.
+
+ * ringedit.c (cmp_seckey): Now compares the secret key against the
+ public key while ignoring all secret parts.
+ (keyring_search): Use a public key packet as arg. Allow to search
+ for subnkeys
+ (search): Likewise. Changed all callers.
+ (find_secret_keyblock_bypk): New.
+ (find_secret_keyblock_byname): First locate the pubkey and then
+ find the correponding secret key.
+ * parse-packet.c (parse): Renamed pkttype arg to onlykeypkts and
+ changed code accordingly. Changed all callers.
+ (search_packet): Removed pkttype arg.
+ * keyedit.c (keyedit_menu): First locate the public key and then
+ try to locate a secret key.
+
+ * ringedit.c (locate_keyblock_by_fpr): Removed.
+ (locate_keyblock_by_keyid): Removed.
+ (find_keyblock_bysk): Removed.
+
+ * sig-check.c (check_key_signature2): Print the keyid along with
+ the wrong sig class errors.
+
+2001-08-24 Werner Koch <wk@gnupg.org>
+
+ * sign.c (sign_file): Stripped the disabled comment packet code.
+ (sign_file, sign_symencrypt_file): Moved common code to ..
+ (write_onepass_sig_packets): .. this new function.
+ (sign_file, clearsign_file, sign_symencrypt_file): Moved common
+ code to
+ (write_signature_packets): this new function.
+ (write_signature_packets, make_keysig_packet)
+ (update_keysig_packet): Moved common code to
+ (hash_uid, hash_sigclass_to_magic): these new functions
+ (sign_file, sign_symencrypt_file): Moved common code to
+ (write_plaintext_packet): this new function.
+
+2001-08-21 Stefan Bellon <sbellon@sbellon.de>
+
+ * trustdb.c (query_trust_info): Changed trustlevel to signed int.
+ * g10.c [__riscos__]: Fixed handling of --use-agent --lock-multiple.
+
+2001-08-20 Werner Koch <wk@gnupg.org>
+
+ * encr-data.c (decrypt_data): Keep track on whether we already
+ printed information about the used algorithm.
+ * mainproc.c (proc_encrypted): Removed the non-working IDEA hack
+ and print a message about the assumed algorithm.
+ * passphrase.c (passphrase_to_dek): Use the same algorithm as above.
+ (proc_symkey_enc): Print the algorithm, so that the user knows it
+ before entering the passphrase.
+ (proc_pubkey_enc, proc_pubkey_enc): Zero the DEK out.
+ * encode.c (encode_crypt, encrypt_filter): Ditto.
+
+ * g10.c: Allow for --sign --symmetric.
+ * sign.c (sign_and_symencrypt): New.
+
+ Applied patches from Stefan Bellon <sbellon@sbellon.de> to support
+ RISC OS. Nearly all of these patches are identified by the
+ __riscos__ macro.
+ * compress.c: Added a couple of casts.
+ * g10.c [__riscos__]: Some patches and new options foo-file similar
+ to all foo-fd options.
+ * gpgv.c, openfile.c, ringedit.c, tdbio.c: Minor fixes. Mainly
+ replaced hardcoded path separators with EXTSEP_S like macros.
+ * passprase.c [__riscos__]: Disabled agent stuff
+ * trustdb.c (check_trust): Changed r_trustlevel to signed int to
+ avoid mismatch problems in pkclist.c
+ * pkclist.c (add_ownertrust): Ditto.
+ * plaintext.c (handle_plaintext) [__riscos__]: Print a note when
+ file can't be created.
+ * options.h [__riscos__]: Use an extern unless included from the
+ main module.
+ * signal.c (got_fatal_signal) [__riscos__]: Close all files.
+
+2001-08-14 Werner Koch <wk@gnupg.org>
+
+ * keygen.c (ask_algo): New arg r_usage. Allow for RSA keys.
+ (gen_rsa): Enabled the code.
+ (do_create): Enabled RSA branch.
+ (parse_parameter_usage): New.
+ (proc_parameter_file): Handle usage parameter.
+ (read_parameter_file): Ditto.
+ (generate_keypair): Ditto.
+ (generate_subkeypair): Ditto.
+ (do_generate_keypair): Ditto.
+ (do_add_key_flags): New.
+ (keygen_add_std_prefs): Use the new function.
+ (keygen_add_key_flags_and_expire): New.
+ (write_selfsig, write_keybinding): Handle new usage arg.
+ * build-packet.c (build_sig_subpkt): Make sure that key flags go
+ into the hashed area.
+
+ * keygen.c (write_uid): Initialize the reference cunter.
+
+ * keyedit.c (keyedit_menu): No more need to update the trustdb for
+ preferences. Added calls to merge keblock.
+
+ * kbnode.c (dump_kbnode): Print some more flags.
+
+2001-08-10 Werner Koch <wk@gnupg.org>
+
+ Revamped the preference handling.
+
+ * packet.h (prefitem_t, preftype_t): New.
+ (PKT_public_key): Added a uid field.
+ (PKT_user_id): Added field to store preferences and a reference
+ counter.
+ * parse-packet.c (parse_user_id,parse_photo_id): Initialize them
+ * free-packet.c (free_user_id): Free them.
+ (copy_user_id): Removed.
+ (scopy_user_id): New.
+ (cmp_user_ids): Optimized for identical pointers.
+ (release_public_key_parts): Release the uid.
+ (copy_public_key_with_new_namehash): Removed.
+ (copy_prefs): New.
+ * keyedit.c (menu_adduid): Use the new shallow copy user id.
+ (show_prefs): Adjusted implementation.
+ (keyedit_menu): No more need to update the trustdb after changing
+ preferences.
+ * getkey.c (fixup_uidnode): Store preferences.
+ (find_by_name): Return a user id packet and remove namehash stuff.
+ (lookup): Removed the unused namehash stuff.
+ (finish_lookup): Added foundu arg.
+ (pk_from_block): Removed the namehash arg and changed all callers.
+ (merge_selfsigs): Copy prefs to all keys.
+ * trustdb.c (get_pref_data): Removed.
+ (is_algo_in_prefs): Removed.
+ (make_pref_record): Deleted and removed all class.
+ * pkclist.c (select_algo_from_prefs): Adjusted for the new
+ preference implementation.
+ * pubkey-enc.c (is_algo_in_prefs): New.
+ (get_it): Use that new function.
+
+2001-08-09 Werner Koch <wk@gnupg.org>
+
+ * build-packet.c (build_sig_subpkt): Fixed calculation of
+ newarea->size.
+
+ * g10.c (main): New option "--preference-list"
+ * keyedit.c (keyedit_menu): New commands "setpref" and "updpref".
+ (menu_set_preferences): New.
+ * keygen.c (keygen_set_std_prefs): New.
+ (set_one_pref): New.
+ (check_zip_algo): New.
+ (keygen_get_std_prefs): New.
+ (keygen_upd_std_prefs): New
+ (keygen_add_std_prefs): Move the pref setting code into the above fnc.
+ * build-packet.c (build_sig_subpkt): Updated the list of allowed
+ to update subpackets.
+
+2001-08-08 Werner Koch <wk@gnupg.org>
+
+ * packet.h (subpktarea_t): New.
+ (PKT_signature): Use that type for hashed_data and unhashed_data and
+ removed the _data prefix from those fields. Changed all users.
+ * parse-packet.c (parse_signature): Changed allocation for that.
+ (parse_sig_subpkt): Changed declaration
+ (enum_sig_subpkt): Ditto and changed implementation accordingly.
+ * free-packet.c (cp_subpktarea): Renamed from cp_data_block and
+ adjusted implementation. Changed caller.
+ * sig-check.c (mdc_kludge_check): Adjusted the hashing.
+ (do_check): Ditto.
+ * sign.c (sign_file, clearsign_file, make_keysig_packet,
+ update_keysig_packet): Ditto.
+ * build-packet.c (build_sig_subpkt): Partial rewrite.
+ (find_subpkt): Adjusted and made static.
+ (delete_sig_subpkt): Adjusted.
+ (do_signature): Ditto.
+
+ * keygen.c (ask_keysize): Do not print the notes about suggested
+ key sizes if just a DSA key is generated.
+
+ * trustdb.c (add_ultimate_key): s/log_error/log_info/ for
+ duplicated inserted trusted keys.
+
+2001-08-07 Werner Koch <wk@gnupg.org>
+
+ * sign.c (sleep): Redefine for W32.
+
+ * g10.c, options.h: Set new flag opt.no_homedir_creation when
+ --no-options is given.
+ * openfile.c (try_make_homedir): Don't create the homedir in that case.
+
+2001-08-03 Werner Koch <wk@gnupg.org>
+
+ * armor.c (armor_filter): Removed the default comment string
+ because it could get us in trouble due to translations using non
+ ascii characters.
+
+2001-08-01 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (list_keyblock_print): Do not list revoked UIDs unless
+ in verbose mode and we do no signature listing.
+
+ * getkey.c (finish_lookup): Skip subkeys which are not yet valid.
+ * g10.c, options.h: New option --ignore-valid-from.
+
+ * sign.c (make_keysig_packet): Added new sigversion argument to
+ allow the caller to force generation of required signature
+ version. Changed all callers. Suggested by Thomas Roessler.
+
+ * keyedit.c (sign_uids): Force v4 signature generation for local
+ sigs. Removed the check for local signature and pre-v4 keys.
+
+2001-07-27 Werner Koch <wk@gnupg.org>
+
+ * keyedit.c (sign_uids): Check that we are not trying to to a
+ lsign with a pre-v4 key. Bug noticed by Thomas Roessler.
+
+2001-07-26 Werner Koch <wk@gnupg.org>
+
+ * parse-packet.c (parse_photo_id): Reset all variables.
+ * getkey.c (merge_selfsigs_main): Removed checks on PHOTO_ID
+ because this is handled identically to a user ID.
+
+2001-07-06 Werner Koch <wk@gnupg.org>
+
+ * cipher.c (write_header): Don't use MDC with --rfc1991. Suggested
+ by disastry@saiknes.lv.
+
+2001-07-05 Werner Koch <wk@gnupg.org>
+
+ * g10.c, options.h: New option --preserve-permissions.
+ * ringedit.c (add_keyblock_resource): Use it here
+ (keyring_copy): and here.
+
+ * trustdb.c (verify_own_keys): Be more silent on --quiet.
+ Suggested by Thomas Roessler.
+ * sig-check.c (check_key_signature2): Ditto.
+ * mainproc.c (proc_encrypted, proc_tree): Ditto
+ * getkey.c (lookup): Ditto.
+
+2001-07-04 Werner Koch <wk@gnupg.org>
+
+ * ringedit.c (add_keyblock_resource): Restore filename in case of error.
+
+2001-06-25 Werner Koch <wk@gnupg.org>
+
+ * kbnode.c (dump_kbnode): Print the signature timestamp.
+
+ * keyedit.c (keyedit_menu): New menu point "primary".
+ (change_primary_uid_cb): New.
+ (menu_set_primary_uid): New.
+ * sign.c (update_keysig_packet): New.
+ * build-packet.c (build_sig_subpkt): Put the primary UID flag into
+ the hashed area. Allow update of some more packets.
+
+2001-06-15 Werner Koch <wk@gnupg.org>
+
+ * getkey.c (merge_selfsigs): Exit gracefully when a secret key is
+ encountered. May happen if a secret key is in public keyring.
+ Reported by Francesco Potorti.
+
+2001-06-12 Werner Koch <wk@gnupg.org>
+
+ * getkey.c (compare_name): Use ascii_memistr(), ascii_memcasecmp()
+ * keyedit.c (keyedit_menu): Use ascii_strcasecmp().
+ * armor.c (radix64_read): Use ascii_toupper().
+ * ringedit.c (do_bm_search): Ditto.
+ * keygen.c (read_parameter_file): Ditto.
+ * openfile.c (CMP_FILENAME): Ditto.
+ * g10.c (i18n_init): We can now use just LC_ALL.
+
+2001-05-29 Werner Koch <wk@gnupg.org>
+
+ * keygen.c (generate_subkeypair): Print a warning if a subkey is
+ created on a v3 key. Suggested by Brian M. Carlson.
+
+2001-05-27 Werner Koch <wk@gnupg.org>
+
+ * keyid.c (get_lsign_letter): New.
+ * keylist.c (list_keyblock_colon): Use it here.
+ * mainproc.c (list_node): and here.
+
+ * getkey.c, packet.h, free-packet.c: Removed that useless key
+ created field; I dunno why I introducded this at all - the
+ creation time is always bound to the key packet and subject to
+ fingerprint calculation etc.
+
+ * getkey.c (fixup_uidnode): Add keycreated arg and use this
+ instead of the signature timestamp to calculate the
+ help_key_expire. Bug reported by David R. Bergstein.
+ (merge_selfsigs_main): Correct key expiration time calculation.
+ (merge_selfsigs_subkey): Ditto.
+
+2001-05-25 Werner Koch <wk@gnupg.org>
+
+ * revoke.c (gen_revoke): Add a cast to a tty_printf arg.
+ * delkey.c (do_delete_key): Ditto.
+ * keyedit.c (print_and_check_one_sig): Ditto.
+ (ask_revoke_sig): Ditto.
+ (menu_revsig): Ditto.
+ (check_all_keysigs): Removed unused arg.
+
+2001-05-23 Werner Koch <wk@gnupg.org>
+
+ * g10.c (opts): Typo fix by Robert C. Ames.
+
+2001-05-06 Werner Koch <wk@gnupg.org>
+
+ * revoke.c: Small typo fix
+
+2001-05-04 Werner Koch <wk@gnupg.org>
+
+ * passphrase.c (passphrase_clear_cache): Shortcut if agent usage
+ is not enabled.
+
+2001-05-01 Werner Koch <wk@gnupg.org>
+
+ * passphrase.c (writen): Replaced ssize_t by int. Thanks to
+ to Robert Joop for reporting that SunOS 4.1.4 does not have it.
+
+2001-04-28 Werner Koch <wk@gnupg.org>
+
+ * getkey.c (merge_public_with_secret): pkttype was not set to subkey.
+
+2001-04-27 Werner Koch <wk@gnupg.org>
+
+ * skclist.c (build_sk_list): Changed one log_debug to log_info.
+
+2001-04-25 Werner Koch <wk@gnupg.org>
+
+ * keyedit.c (show_prefs): Add a verbose mode.
+ (show_key_with_all_names): Pass verbose flag for special value of
+ with_pref.
+ (keyedit_menu): New command "showpref"
+ (show_key_with_all_names): Mark revoked uids and the primary key.
+
+2001-04-24 Werner Koch <wk@gnupg.org>
+
+ * getkey.c (get_primary_uid): Return a different string in case of
+ error and made it translatable.
+
+ * build-packet.c (do_secret_key): Ugly, we wrote a zero
+ instead of the computed ndays. Thanks to M Taylor for complaining
+ about a secret key import problem.
+
+2001-04-23 Werner Koch <wk@gnupg.org>
+
+ * hkp.c (hkp_ask_import): Allow to specify a port number for the
+ keyserver. Add a kudge to set the no_shutdown flag.
+ (hkp_export): Ditto.
+ * options.skel: Document the changes
+
+2001-04-20 Werner Koch <wk@gnupg.org>
+
+ * options.skel: Add some more comments.
+
+2001-04-19 Werner Koch <wk@gnupg.org>
+
+ * keyid.c (mk_datestr): New. Handles negative times. We must do
+ this because Windoze segvs on negative times passed to gmtime().
+ Changed all datestr_from function to use this one.
+
+ * keyid.c, keyid.h (colon_strtime): New. To implement the
+ fixed-list-mode.
+ (colon_datestr_from_pk): New.
+ (colon_datestr_from_sk): New.
+ (colon_datestr_from_sig): New.
+ * keylist.c (list_keyblock_colon): Use these functions here.
+ * mainproc.c (list_node): Ditto.
+
+2001-04-18 Werner Koch <wk@gnupg.org>
+
+ * openfile.c (open_sigfile): Fixed the handling of ".sign".
+ * mainproc.c (proc_tree): Use iobuf_get_real_fname.
+ Both are by Vincent Broman.
+
+2001-04-14 Werner Koch <wk@gnupg.org>
+
+ * getkey.c (fixup_uidnode): Removed check for !sig which is
+ pointless here. Thanks to Jan Niehusmann.
+
+2001-04-10 Werner Koch <wk@gnupg.org>
+
+ * sig-check.c (check_key_signature2): Use log_info instead of
+ log_error so that messed up keys do not let gpg return an error.
+ Suggested by Christian Kurz.
+
+ * getkey.c (merge_selfsigs_main): Do a fixup_uidnode only if we
+ have both, uid and sig. Thanks to M Taylor.
+
+2001-04-05 Werner Koch <wk@gnupg.org>
+
+ * armor.c (unarmor_pump_new,unarmor_pump_release): New.
+ (unarmor_pump): New.
+ * pipemode.c (pipemode_filter): Use the unarmor_pump to handle
+ armored or non-armored detached signatures. We can't use the
+ regular armor_filter becuase this does only chack for armored
+ signatures the very first time. In pipemode we may have a mix of
+ armored and binary detached signatures.
+ * mainproc.c (proc_tree): Do not print the "old style" notice when
+ this is a pipemode processes detached signature.
+ (proc_plaintext): Special handling of pipemode detached sigs.
+
+ * packet.h (CTRLPKT_PLAINTEXT_MARK): New.
+ * parse-packet.c (create_gpg_control): New.
+ * kbnode.c (dump_kbnode): Support it here.
+ * mainproc.c (check_sig_and_print): Fixed the check for bad
+ sequences of multiple signatures.
+ (proc_plaintext): Add the marker packet.
+ (proc_tree): We can now check multiple detached signatures.
+
+2001-04-02 Werner Koch <wk@gnupg.org>
+
+ The length of encrypted packets for blocksizes != 8 was not
+ correct encoded. I think this is a minor problem, because we
+ usually use partial length packets. Kudos to Kahil D. Jallad for
+ pointing this out.
+ * packet.h: Add extralen to PKT_encrypted.
+ * cipher.c (write_header): Set extralen.
+ * build-packet.c (do_encrypted): Use extralen instead of const 10.
+ (do_encrypted_mdc): Ditto.
+ * parse-packet.c (parse_encrypted): Set extralen to 0 because we
+ don't know it here.
+
+2001-03-30 Werner Koch <wk@gnupg.org>
+
+ * getkey.c (premerge_public_with_secret): Changed wording an add
+ the keyID to the info message.
+
+2001-03-29 Werner Koch <wk@gnupg.org>
+
+ * getkey.c (premerge_public_with_secret): Use log_info instead of
+ log_error when no secret key was found for a public one.
+ Fix the usage if the secret parts of a key are not available.
+
+ * openfile.c (ask_outfile_name): Trim spaces.
+ (open_outfile): Allow to enter an alternate filename. Thanks to
+ Stefan Bellon.
+ * plaintext.c (handle_plaintext): Ditto.
+
+2001-03-28 Werner Koch <wk@gnupg.org>
+
+ * mainproc.c (do_check_sig): Allow direct key and subkey
+ revocation signature.
+ * sig-check.c (check_key_signature2): Check direct key signatures.
+ Print the signature class along with an error.
+
+2001-03-27 Werner Koch <wk@gnupg.org>
+
+ * packet.h: Add a missing typedef to an enum. Thanks to Stefan Bellon.
+
+ * g10.c: New option --no-sig-create-check.
+ * sign.c (do_sign): Implement it here.
+ * g10.c: New option --no-sig-cache.
+ * sig-check.c (check_key_signature2): Implement it here.
+ (cache_selfsig_result): and here.
+
+ * keylist.c (list_keyblock): Removed debugging stuff.
+
+ * getkey.c (cache_public_key): Made global.
+ * keygen.c (write_selfsig, write_keybinding): Cache the new key.
+
+ * getkey.c (key_byname): Add new arg secmode and changed all
+ callers to request explicitly the mode. Deriving this information
+ from the other supplied parameters does not work if neither pk nor
+ sk are supplied.
+
+2001-03-25 Werner Koch <wk@gnupg.org>
+
+ * packet.h (ctrlpkttype_t): New.
+ * mainproc.c (add_gpg_control,proc_plaintext,proc_tree): Use the
+ new enum values.
+ * pipemode.c (make_control): Ditto.
+ * armor.c (armor_filter): Ditto.
+
+2001-03-24 Werner Koch <wk@gnupg.org>
+
+ * sign.c (do_sign): Verify the signature right after creation.
+
+2001-03-23 Werner Koch <wk@gnupg.org>
+
+ * status.c, status.h (STATUS_UNEXPECTED): New.
+ * mainproc.c (do_proc_packets): And emit it here.
+
+2001-03-21 Werner Koch <wk@gnupg.org>
+
+ * status.c: Add sys/types.h so that it runs on Ultrix. Reported
+ by Georg Schwarz.x
+
+ * build-packet.c (build_sig_subpkt): Fixed generaton of packet
+ length header in case where 2 bytes headers are needed. Thanks to
+ Piotr Krukowiecki.
+
+2001-03-19 Werner Koch <wk@gnupg.org>
+
+ * g10.c (main): the default keyring is no always used unless
+ --no-default-keyring is given.
+
+ * ringedit.c (add_keyblock_resource): invalidate cache after file
+ creation.
+
+2001-03-15 Werner Koch <wk@gnupg.org>
+
+ * keygen.c (ask_algo): Changed the warning of the ElGamal S+E Algo.
+
+ * keylist.c (print_capabilities): New.
+ (list_keyblock_colon): and use it here.
+
+2001-03-13 Werner Koch <wk@gnupg.org>
+
+ * main.c, options.h: New option --fixed_list_mode.
+ * keylist.c (list_keyblock_colon): use it here.
+
+ * getkey.c (merge_keys_and_selfsig): Divert merging of public keys
+ to the function used in key selection..
+ * keylist.c (is_uid_valid): Removed.
+ (list_keyblock): Splitted into ..
+ (list_keyblock_print, list_keyblock_colon): .. these.
+ functions. Changed them to use the flags set in the key lookup code.
+ (reorder_keyblock): New, so that primary user IDs are listed first.
+
+ * ringedit.c (keyring_copy): flush the new iobuf chaces before
+ rename or remove operations. This is mainly needed for W32.
+
+ * hkp.c [HAVE_DOSISH_SYSTEM]: Removed the disabled code because we
+ have now W32 socket support in ../util/http.c
+
+ * skclist.c (key_present_in_sk_list): New.
+ (is_duplicated_entry): New.
+ (build_sk_list): Check for duplicates and do that before unlocking.
+
+2001-03-12 Werner Koch <wk@gnupg.org>
+
+ * armor.c (parse_header_line): Removed double empty line check.
+ (parse_header_line): Replaced trim_trailing_ws with a counting
+ function so that we can adjust for the next read.
+
+ * options.skel: Fixed 3 typos. By Thomas Klausner. Replaced the
+ keyserver example by a better working server.
+
+ * parse-packet.c (parse_symkeyenc): Return Invalid_Packet on error.
+ (parse_pubkeyenc): Ditto.
+ (parse_onepass_sig): Ditto.
+ (parse_plaintext): Ditto.
+ (parse_encrypted): Ditto.
+ (parse_signature): Return error at other places too.
+ (parse_key): Ditto.
+ * g10.c (main): Set opt.list_packets to another value when invoked
+ with the --list-packets command.
+ * mainproc.c (do_proc_packets): Don's stop processing when running
+ under --list-packets command.
+
+ * signal.c (do_sigaction): Removed.
+ (init_one_signal): New to replace the above. Needed to support
+ systems without sigactions. Suggested by Dave Dykstra.
+ (got_fatal_signal,init_signals): Use the above here.
+ (do_block): Use sigset() if sigprocmask() is not available.
+
+ * armor.c (parse_hash_header): Test on TIGER192, which is the
+ correct value as per rfc2440. By Edwin Woudt.
+
+2001-03-08 Werner Koch <wk@gnupg.org>
+
+ * misc.c: Include time.h. By James Troup.
+
+ * getkey.c: Re-enabled the unknown user Id and PK caches and
+ increased their sizes.
+
+ * getkey.c (merge_selfsigs_main): Set expire date and continue
+ processing even if we found a revoked key.
+ (merge_selfsigs_subkeys): Ditto.
+
+ * packet.h: Add an is_revoked flag to the user_id packet.
+ * getkey.c (fixup_uidnode): Set that flag here.
+ (merge_selfsigs_main): Fix so that the latest signature is used to
+ find the self-signature for an UID.
+ * parse-packet.c (parse_user_id): Zero out all fields.
+ * mainproc.c (check_sig_and_print): Print the primary user ID
+ according the the node flag and then all other non-revoked user IDs.
+ (is_uid_revoked): Removed; it is now handled by the key selection code.
+
+ Changed the year list of all copyright notices.
+
+2001-03-07 Werner Koch <wk@gnupg.org>
+
+ * getkey.c (finish_lookup): Print an info message only in verbose mode.
+
+2001-03-05 Werner Koch <wk@gnupg.org>
+
+ * packet.h: Replaced sigsubpkt_t value 101 by PRIV_VERIFY_CACHE.
+ We have never used the old value, so we can do this without any harm.
+ * parse-packet.c (dump_sig_subpkt): Ditto.
+ (parse_one_sig_subpkt): Parse that new sub packet.
+ * build-packet.c (build_sig_subpkt): Removed the old one from the
+ hashed area.
+ (delete_sig_subpkt): New.
+ (build_sig_subpkt): Allow an update of that new subpkt.
+ * sig-check.c (check_key_signature2): Add verification caching
+ (cache_selfsig_result): New.
+ * export.c (do_export_stream): Delete that sig subpkt before exporting.
+ * import.c (remove_bad_stuff): New.
+ (import): Apply that function to all imported data
+
+2001-03-03 Werner Koch <wk@gnupg.org>
+
+ * getkey.c: Introduced a new lookup context flag "exact" and used
+ it in all place where we once used primary.
+ (classify_user_id2): Replaced the old function and add an extra
+ argument to return whether an exact keyID has been requested.
+ (key_byname): Removed the unused ctx.primary flag
+ (get_seckey_byname2): Ditto.
+ (finish_lookup): Changed debugging output.
+
+2001-03-02 Werner Koch <wk@gnupg.org>
+
+ * keylist.c (list_one): Remove the merge key calls.
+
+2001-03-01 Werner Koch <wk@gnupg.org>
+
+ * getkey.c (finish_lookup): Don't use it if we no specific usage
+ has been requested.
+ (merge_selfsigs_main): fix UID only if we have an signature.
+ (lookup): Return UNU_PUBKEY etc. instead of NO_PUBKEY if we found
+ a key but the requested usage does not allow this key.
+ * import.c (import_one): Take UNU_PUBKEY into account.
+ * mainproc.c (list_node): Ditto.
+ * keylist.c (list_keyblock): Ditto.
+ * keyedit.c (print_and_check_one_sig): Ditto.
+
+2001-02-09 Werner Koch <wk@gnupg.org>
+
+ * delkey.c (delete_key): Removed that silly assert which rendered
+ the whole new stuff meaningless.
+
+2001-02-08 Werner Koch <wk@gnupg.org>
+
+ * getkey.c (key_byname): It can happen that we have both, sk and pk
+ NULL, fix for that.
+
+ * parse-packet.c (parse_one_sig_subpkt): Add support for
+ primary_uid and key_flags.
+ (can_handle_critical): Ditto
+
+ * parse-packet.c (parse_encrypted): Fixed listing of pktlen for
+ MDC packets.
+
+ * getkey.c: Backported the version of this file from gpg 1.1. this
+ involved some changes in other files too.
+ * parse-packet.c (parse_key): Clear req_usage.
+ * skclist.c (build_sk_list): Use req_usage to pass the usage
+ information to the lookup function.
+ * pkclist.c (build_pk_list): Ditto.
+ * free-packet.c (copy_public_parts_to_secret_key): New.
+ * keydb.h: Add IS_* macros to check the sig_class.
+ * misc.c (openpgp_cipher_test_algo): New.
+ (openpgp_pk_test_algo): New.
+ (openpgp_pk_algo_usage): New.
+ (openpgp_md_test_algo): New.
+ * packet.h: Add a few fields to PKT_{public,secret}_key and
+ PKT_user_id.
+ * seckey-cert.c (do_check): Use the new main_keyid field.
+
+2001-02-04 Werner Koch <wk@gnupg.org>
+
+ * encr-data.c (decrypt_data): Catch error when we had problems to
+ parse the encrypted packet. By Timo.
+
+2001-01-29 Werner Koch <wk@gnupg.org>
+
+ * g10.c (main): --batch does now set nogreeting.
+
+ * delkey.c (do_delete_key): Fixed delete-both functionality.
+
+2001-01-22 Werner Koch <wk@gnupg.org>
+
+ * g10.c: New command --delete-secret-and-public-key.
+ * delkey.c (delete_key): Add new arg allow_both.
+ (do_delete_key): Move most stuff from above to this new function.
+
+2001-01-12 Werner Koch <wk@gnupg.org>
+
+ * passphrase.c (passphrase_to_dek): Use MD5 when IDEA is installed
+ and we have no S2K.
+ * mainproc.c (proc_encrypted): Likewise
+
+2001-01-11 Werner Koch <wk@gnupg.org>
+
+ * sig-check.c (do_check): Print the signature key expire message
+ only in verbose mode and added the keyID.
+
+2001-01-09 Werner Koch <wk@gnupg.org>
+
+ * status.c, status.h: New status USERID_HINT.
+ (write_status_text): Replace LF and CR int text by C-escape sequence.
+
+ * passphrase.c (passphrase_to_dek): Fixed the NEED_PASSPHRASE
+ output. It does now always print 2 keyIDs. Emit the new
+ USERID_HINT.
+
+2001-01-08 Werner Koch <wk@gnupg.org>
+
+ * g10.c, options.h: New option --no-expensive-trust-checks.
+ * keylist.c (list_keyblock): Act on this option.
+
+2001-01-04 Werner Koch <wk@gnupg.org>
+
+ * g10.c (main): Set homedir only in the pre-parsing phase and
+ replace backslashes in the W32 version.
+
+2001-01-03 Werner Koch <wk@gnupg.org>
+
+ * status.c, status.h : New status KEY_CREATED
+ * keygen.c (do_generate_keypair,generate_subkeypair): Emit it.
+
+2000-12-28 Werner Koch <wk@gnupg.org>
+
+ * signal.c (got_fatal_signal): Remove lockfiles here because the
+ atexit stuff does not work due to the use of raise. Suggested by
+ Peter Fales.
+ * gpgv.c (remove_lockfiles): New stub.
+
+2000-12-19 Werner Koch <wk@gnupg.org>
+
+ * status.c, status.h (cpr_get_no_help): New.
+ * keyedit.c (keyedit_menu): Use it here because we have our own
+ help list here.
+
+2000-12-18 Werner Koch <wk@gnupg.org>
+
+ * mainproc.c (print_failed_pkenc): Don't print the sometimes
+ confusing message about unavailabe secret key. Renamed ...
+ (print_pkenc_list): ... to this and introduced failed arg.
+ (proc_encrypted): Print the failed encryption keys and then
+ the one to be used.
+ (proc_pubkey_enc): Store also the key we are going to use.
+
+ * mainproc.c (check_sig_and_print): Don't list revoked user IDs.
+ (is_uid_revoked): New.
+
+2000-12-08 Werner Koch <wk@gnupg.org>
+
+ * pipemode.c: Made the command work. Currently only for
+ non-armored detached signatures.
+ * mainproc.c (release_list): Reset the new pipemode vars.
+ (add_gpg_control): Handle the control packets for pipemode
+ * status.c, status.h: New stati {BEGIN,END}_STREAM.
+
+2000-12-07 Werner Koch <wk@gnupg.org>
+
+ * g10.c: New option --allow-secret-key-import.
+ * import.c (import_keys,import_keys_stream): Honor this option.
+ (import): New arg allow_secret and pass that arg down to ...
+ (import_secret_one): to this and print a warning if secret key
+ importing is not allowed.
+
+2000-12-05 Werner Koch <wk@gnupg.org>
+
+ * cipher.c (cipher_filter): Moved the end_encryption status ...
+ * encode.c (encode_simple,encode_crypt): to here
+ * sign.c (sign_file): and here.
+
+ * status.c (mywrite): Removed.
+ (get_status_string): Removed the LFs from the strings.
+ (set_status_fd,is_status_enabed,write_status_text,
+ write_status_buffer): Replaced all mywrite by stdio calls and use
+ fdopen to create a strem. This is needed to make things smoother
+ in the W32 version.
+
+2000-12-04 Werner Koch <wk@gnupg.org>
+
+ * import.c (merge_blocks): Increment n_sigs for revocations.
+
+2000-11-30 Werner Koch <wk@gnupg.org>
+
+ * g10.c (main): Use iobuf_translate_file_handle for all options
+ with filehandles as arguments. This is function does some magic
+ for the W32 API.
+
+ * verify.c (verify_signatures): Add a comment rant about the
+ detached signature problem.
+ * mainproc.c (proc_tree): Issue an error if a detached signature
+ is assumed but a standard one was found.
+ * plaintext.c (hash_datafiles): Don't fall back to read signature
+ from stdin.
+ * openfile.c (open_sigfile): Print verbose message only if the
+ file could be accessed.
+
+2000-11-24 Werner Koch <wk@gnupg.org>
+
+ * passphrase.c [HAVE_DOSISH_SYSTEM]: Disabled all the agent stuff.
+
+2000-11-16 Werner Koch <wk@gnupg.org>
+
+ * g10.c: New option --use-agent
+ * passphrase.c (agent_open,agent_close): New.
+ (agent_get_passphrase,agent_clear_passphrase): New.
+ (passphrase_clear_cache): New.
+ (passphrase_to_dek): Use the agent here.
+ * seckey-cert.c (do_check): Clear cached passphrases.
+
+2000-11-15 Werner Koch <wk@gnupg.org>
+
+ * status.c (write_status_text): Moved the big switch to ...
+ (get_status_string): ... new function.
+ (write_status_buffer): New.
+
+ * status.c (mywrite): New and replaced all write() by this.
+
+ * status.c, status.h: Add 3 status lcodes for notaions and policy.
+ * mainproc.c (print_notation_data): Do status output of notations.
+
+2000-11-13 Werner Koch <wk@gnupg.org>
+
+ * sign.c (clearsign_file): Use LF macro to print linefeed.
+
+2000-11-11 Paul Eggert <eggert@twinsun.com>
+
+ Clean up the places in the code that incorrectly use "long" or
+ "unsigned long" for file offsets. The correct type to use is
+ "off_t". The difference is important on large-file hosts,
+ where "off_t" is longer than "long".
+
+ * keydb.h (struct keyblock_pos_struct.offset):
+ Use off_t, not ulong, for file offsets.
+ * packet.h (dbg_search_packet, dbg_copy_some_packets,
+ search_packet, copy_some_packets): Likewise.
+ * parse-packet.c (parse, dbg_search_packet, search_packet,
+ dbg_copy_some_packets, copy_some_packets): Likewise.
+ * ringedit.c (keyring_search): Likewise.
+
+ * parse-packet.c (parse): Do not use %lu to report file
+ offsets in error diagnostics; it's not portable.
+ * ringedit.c (keyring_search): Likewise.
+
+2000-11-09 Werner Koch <wk@gnupg.org>
+
+ * g10.c (main): New option --enable-special-filenames.
+
+2000-11-07 Werner Koch <wk@gnupg.org>
+
+ * g10.c (main): New command --pipemode.
+ * pipemode.c: New.
+
+2000-10-23 Werner Koch <wk@gnupg.org>
+
+ * armor.c (armor_filter): Changed output of hdrlines, so that a CR
+ is emitted for DOS systems.
+
+ * keygen.c (read_parameter_file): Add a cast for isspace().
+
+ * status.c (myread): Use SIGINT instead of SIGHUP for DOS.
+
+2000-10-19 Werner Koch <wk@gnupg.org>
+
+ * g10.c: New option --ignore-crc-error
+ * armor.c (invalid_crc): New.
+ (radix64_read): Act on new option.
+
+ * openfile.c (try_make_homedir): Klaus Singvogel fixed a stupid
+ error introduced on Sep 6th.
+
+2000-10-18 Werner Koch <wk@gnupg.org>
+
+ * misc.c (print_cipher_algo_note): Don't print the note for AES.
+ Changed wording.
+
+2000-10-16 Werner Koch <wk@gnupg.org>
+
+ * mainproc.c (do_proc_packets): Hack to fix the problem that
+ signatures are not detected when there is a MDC packet but no
+ compression packet.
+
+ * g10.c (print_hashline): New.
+ (print_mds): Use above func with --with-colons.
+
+ * mainproc.c (check_sig_and_print): Detect multiple signatures
+ and don't verify them.
+
+2000-10-14 Werner Koch <wk@gnupg.org>
+
+ * mainproc.c (add_onepass_sig): There is an easier solution to the
+ error fixed yesterday; just check that we only have onepass
+ packets. However, the other solution provides an cleaner
+ interface and opens the path to get access to other information
+ from the armore headers.
+ (release_list): Reset some more variables.
+
+2000-10-13 Werner Koch <wk@gnupg.org>
+
+ * mainproc.c (add_gpg_control): New.
+ (do_proc_packets): use it.
+ (proc_plaintext): Changed logic to detect clearsigns.
+ (proc_tree): Check the cleartext sig with some new code.
+
+ * packet.h: New packet PKT_GPG_CONTROL.
+ * parse-packet.c (parse_gpg_control): New.
+ * misc.c (get_session_marker): New.
+ * armor.c (armor_filter): Replaced the faked 1-pass packet by the
+ new control packet.
+
+ * keyedit.c (keyedit_menu): Allow batchmode with a command_fd.
+ * status.c (my_read): New.
+ (do_get_from_fd): use it.
+
+2000-10-12 Werner Koch <wk@gnupg.org>
+
+ * keygen.c (keygen_add_std_prefs): Add Rijndael to the prefs.
+
+2000-10-07 Werner Koch <wk@gnupg.org>
+
+ * gpgv.c: Add more stubs for ununsed code to make the binary smaller.
+
+Wed Oct 4 15:50:18 CEST 2000 Werner Koch <wk@openit.de>
+
+ * sign.c (hash_for): New arg to take packet version in account, changed
+ call callers.
+
+ * gpgv.c: New.
+ * Makefile.am: Rearranged source files so that gpgv can be build with
+ at least files as possible.
+
+Mon Sep 18 12:13:52 CEST 2000 Werner Koch <wk@openit.de>
+
+ * hkp.c (not_implemented): Print a notice for W32
+
+Fri Sep 15 18:40:36 CEST 2000 Werner Koch <wk@openit.de>
+
+ * keygen.c (keygen_add_std_prefs): Changed order of preferences to
+ twofish, cast5, blowfish.
+
+ * pkclist.c (algo_available): Removed hack to disable Twofish.
+
+Thu Sep 14 17:45:11 CEST 2000 Werner Koch <wk@openit.de>
+
+ * parse-packet.c (dump_sig_subpkt): Dump key flags. Print special
+ warning in case of faked ARRs.
+
+ * getkey.c (finsih_lookup): Hack so that for v4 RSA keys the subkey
+ is used for encryption.
+
+Thu Sep 14 14:20:38 CEST 2000 Werner Koch <wk@openit.de>
+
+ * g10.c (main): Default S2K algorithms are now SHA1 and CAST5 - this
+ should solve a lot of compatibility problems with other OpenPGP
+ apps because those algorithms are SHOULD and not optional. The old
+ way to force it was by using the --openpgp option whith the drawback
+ that this would disable a couple of workarounds for PGP.
+
+ * g10.c (main): Don't set --quite along with --no-tty. By Frank Tobin.
+
+ * misc.c (disable_core_dump): Don't display a warning here but a return
+ a status value and ...
+ * g10.c (main): ...print warnining here. Suggested by Sam Roberts.
+
+Wed Sep 13 18:12:34 CEST 2000 Werner Koch <wk@openit.de>
+
+ * keyedit.c (keyedit_menu): Allow to use "debug" on the secret key.
+
+ * ringedit.c (cmp_seckey): Fix for v4 RSA keys.
+ * seckey-cert.c (do_check): Workaround for PGP 7 bug.
+
+Wed Sep 6 17:55:47 CEST 2000 Werner Koch <wk@openit.de>
+
+ * misc.c (print_pubkey_algo_note): Do not print the RSA notice.
+ * sig-check.c (do_signature_check): Do not emit the RSA status message.
+ * pubkey-enc.c (get_session_key): Ditto.
+
+ * encode.c (encode_simple, encode_crypt): Fix for large files.
+ * sign.c (sign_file): Ditto.
+
+Wed Sep 6 14:59:09 CEST 2000 Werner Koch <wk@openit.de>
+
+ * passphrase.c (hash_passphrase): Removed funny assert. Reported by
+ David Mathog.
+
+ * openfile.c (try_make_homedir): Changes for non-Posix systems.
+ * g10.c (main): Take the default homedir from macro.
+
+ * g10.c: The --trusted-key option is back.
+ * trustdb.c (verify_own_key): Handle this option.
+ (add_ultimate_key): Moved stuff from verify_own_key to this new func.
+ (register_trusted_key): New.
+
+Fri Aug 25 16:05:38 CEST 2000 Werner Koch <wk@openit.de>
+
+ * parse-packet.c (dump_sig_subpkt): Print info about the ARR.
+
+ * openfile.c (overwrite_filep): Always return okay if the file is
+ called /dev/null.
+ (make_outfile_name): Add ".sign" to the list of know extensions.
+ (open_sigfile): Ditto.
+
+Wed Aug 23 19:52:51 CEST 2000 Werner Koch <wk@openit.de>
+
+ * g10.c: New option --allow-freeform-uid. By Jeroen C. van Gelderen.
+ * keygen.c (ask_user_id): Implemented here.
+
+Fri Aug 4 14:23:05 CEST 2000 Werner Koch <wk@openit.de>
+
+ * status.c (do_get_from_fd): Ooops, we used fd instead of opt.command_fd.
+ Thanks to Michael Tokarev.
+
+Tue Aug 1 20:06:23 CEST 2000 Werner Koch <wk@openit.de>
+
+ * g10.c: New opttion --try-all-secrets on suggestion from Matthias Urlichs.
+ * pubkey-enc.c (get_session_key): Quite easy to implement here.
+
+Thu Jul 27 17:33:04 CEST 2000 Werner Koch <wk@openit.de>
+
+ * g10.c: New option --merge-only. Suggested by Brendan O'Dea.
+ * import.c (import_one): Implemented it here
+ (import_secret_one): Ditto.
+ (print_stats): and give some stats.
+
+Thu Jul 27 12:01:00 CEST 2000 Werner Koch <wk@openit.de>
+
+ * g10.c: New options --show-session-key and --override-session-key
+ * pubkey-enc.c (hextobyte): New.
+ (get_override_session_key): New.
+ * mainproc.c (proc_pubkey_enc): Add session-key stuff.
+ * status.h, status.c (STATUS_SESSION_KEY): New.
+
+Thu Jul 27 10:02:38 CEST 2000 Werner Koch <wk@openit.de>
+
+ * g10.c (main): Use setmode(O_BINARY) for MSDOS while generating random bytes
+ (print_mds): Likewise for stdin.
+ * plaintext.c (handle_plaintext): Likewise for stdout.
+
+Mon Jul 24 10:30:17 CEST 2000 Werner Koch <wk@openit.de>
+
+ * keyedit.c (menu_expire): expire date for primary key can be set again.
+
+Wed Jul 19 11:26:43 CEST 2000 Werner Koch <wk@openit.de>
+
+ * keylist.c (is_uid_valid): New.
+ (list_keyblock): Print validity information for all user IDs. Note, this
+ has to be done at other places too; for now we have only minimal support.
+
+Wed Jul 12 13:32:06 CEST 2000 Werner Koch <wk@openit.de>
+
+ * helptext.c, pkclist.c: s/superseeded/superseded/
+
+Mon Jul 10 16:08:57 CEST 2000 Werner Koch <wk@openit.de>
+
+ * parse-packet.c (enum_sig_subpkt): Fixed testing on crtitical bit in case
+ of a NULL buffer. Reported by Peter Marschall.
+
+Wed Jul 5 13:28:45 CEST 2000 Werner Koch <wk@openit.de>
+
+ * keyedit.c, keyid.c: Add some _()
+
+ * argparse.c: Changed the flag to suppress --version handling to also
+ suppress --help.
+
+Wed Jun 28 11:54:44 CEST 2000 Werner Koch <wk@openit.de>
+
+ * armor.c (armor_filter): Set sigclass to 0 in case of non-dash-escaped
+ clearsig. This makes this mode work again.
+
+ * mainproc.c (proc_tree): Fixed handling of one-pass-sig packets in textmode.
+ Disabled the ugly workaround for PGP 5 - let's see whether thi breaks less
+ cases. Found by Ted Cabeen.
+
+ * options.h (DBG_HASHING): New. All commented md_start_debug are now
+ controlled by this debug option.
+
+ * sign.c (print_status_sig_created): New and called from 2 places.
+
+ * keygen.c (gen_rsa): New, but commented.
+ (ask_algo): Commented support for RSA.
+
+ * seckey-cert.c (protect_secret_key): Started to fix the code for v4 RSA
+ keys - it is not solved yet. However, we have time until, Sep 20th ;)
+
+Wed Jun 14 12:27:09 CEST 2000 Werner Koch <wk@openit.de>
+
+ * status.c (init_shm_coprocessing): Changed the sequence of the get,attach
+ to cope with the changes in newer Linux kernels. This bug has been found
+ by <dmitri@advantrix.com> who also proposed this solution. Hopefully
+ this does not break gpg on to many systems.
+
+ * cipher.c (write_header): Protect the IV with the MDC too.
+ * encr-data.c (decrypt_data): Likewise.
+
+Fri Jun 9 10:09:52 CEST 2000 Werner Koch <wk@openit.de>
+
+ * g10.c: New options --no-auto-key-retrieve
+ * options.h (auto_key_retrieve): New.
+ * mainproc.c (check_sig_and_print): Implemented that.
+
+Wed Jun 7 19:19:09 CEST 2000 Werner Koch <wk@openit.de>
+
+ * sig-check.c (do_check): Use EMULATE_MDENCODE also on v4 packets.
+
+Wed Jun 7 17:25:38 CEST 2000 Werner Koch <wk@openit.de>
+
+ * cipher.c (write_header): Use plain CFB mode for MDC encrypted packets.
+ * encr-data.c (decrypt_data): Ditto.
+
+Mon Jun 5 23:41:54 CEST 2000 Werner Koch <wk@openit.de>
+
+ * seskey.c (do_encode_md, encode_md_value): Add new arg v3compathack to work
+ around a bug in old versions.
+ * sig-check.c (do_check): use the aboved workaround when enabled.
+ * g10.c: New option --emulate-md-decode-bug
+
+Mon Jun 5 12:37:43 CEST 2000 Werner Koch <wk@openit.de>
+
+ * build-packet.c (do_mdc): New.
+ (do_encrypted_mdc): Changed for the new proposal.
+ * parse-packet.c (parse_mdc): New.
+ (parse_encrypted): Fixed for the new proposal.
+ * packet.h (PKT_MDC): New.
+ * cipher.c (cipher_filter): Build the MDC packet here.
+ * g10.c (main): Enable --force-mdc.
+ * encr-data.c (mdc_decode_filter): Fixed for new MDC method
+
+ * options.h(rfc2440): New.
+ * g10.c (main): Changed the selected values for --openpgp to not include
+ optional algorithms.
+
+Thu May 18 11:38:54 CEST 2000 Werner Koch <wk@openit.de>
+
+ * keyedit.c (keyedit_menu): Add a keyword arg to the prompt.
+
+ * status.c, status.h: Added 3 new status tokens.
+ * status.c (do_get_from_fd): New.
+ (cpr_enabled,cpr_get,cpr_get_hidden,cpr_kill_prompt,
+ cpr_get_answer_is_yes,cpr_get_answer_yes_no_quit): Modified to work
+ with the new function.
+ * g10.c: Add new option --command-fd.
+
+ * status.c (progress_cb): New.
+ (set_status_fd): Register progress functions
+
+Fri May 12 14:01:20 CEST 2000 Werner Koch <wk@openit.de>
+
+ * delkey.c (delete_key): Add 2 new status messages
+ * status.c, status.h (STATUS_DELETE_PROBLEM): New.
+
+ Fixed years of copyright in all source files.
+
+Mon May 1 17:08:14 CEST 2000 Werner Koch <wk@openit.de>
+
+ * trustdb.c (propagate_validity): Fixed the bug that only one uid
+ gets fully trusted even when all are signed by an ultimate key.
+
+Mon May 1 15:38:04 CEST 2000 Werner Koch <wk@openit.de>
+
+ * getkey.c (key_byname): Always returned a defined context. Fixed
+ a segv for invalid user id specifications. Reported by Walter Koch.
+
+ * getkey.c (get_user_id): I18ned "no user id" string. By Walter.
+
+ * pkclist.c (do_show_revocation_reason): Typo fixes.
+ * helptext.c: Ditto.
+
+ * armor.c (armor_filter): Fixed some CRLF issues. By Mike McEwan.
+
+Fri Apr 14 19:37:08 CEST 2000 Werner Koch <wk@openit.de>
+
+ * pkclist.c (do_show_revocation_reason): New.
+ (show_revocation_reason): New and called at various places.
+
+ * g10.c (main): Fixed small typo.
+
+ * pkclist.c (do_we_trust): Act on always_trust but not for revoked
+ keys. Suggested by Chip Salzenberg.
+
+ * g10.c: New option --lock-never.
+
+ * ringedit.c (get_writable_keyblock_file): New.
+ * keygen.c (do_generate_keypair): Use this instead of the hardwired one.
+
+ * keygen.c (ask_user_id): Check that the email address is in the
+ correct field. Suggested by Christian Kurz.
+
+Mon Apr 10 13:34:19 CEST 2000 Werner Koch <wk@openit.de>
+
+ * keyedit.c (show_key_with_all_names): s/sbb/ssb/
+
+Tue Mar 28 14:26:58 CEST 2000 Werner Koch <wk@openit.de>
+
+ * trustdb.c (verify_own_keys): Do not print warning about unprotected
+ key when in quiet mode.
+
+Wed Mar 22 13:50:24 CET 2000 Werner Koch <wk@openit.de>
+
+ * mainproc.c (print_userid): Do UTF8 conversion before printing.
+ * import.c (import_one): Ditto.
+ (import_secret_one): Ditto.
+ (delete_inv_parts): Ditto.
+
+Thu Mar 16 16:20:23 CET 2000 Werner Koch <wk@openit.de>
+
+ * keylist.c (print_key_data): Handle a NULL pk gracefully.
+
+ * getkey.c (merge_one_pk_and_selfsig): Fixed silly code for
+ getting the primary keys keyID but kept using the one from the
+ subkey.
+ * pubkey-enc.c (get_it): Print a note for expired subkeys.
+
+ * getkey.c (has_expired): New.
+ (subkeys_expiretime): New.
+ (finish_lookup): Check for expired subkeys needed for encryption.
+ (merge_keys_and_selfsig): Fixed expiration date merging for subkeys.
+
+ * keylist.c (list_keyblock): Print expiration time for "sub".
+ (list_one): Add missing merging for public keys.
+ * mainproc.c (list_node): Ditto.
+
+2000-03-14 13:49:38 Werner Koch (wk@habibti.openit.de)
+
+ * keygen.c (keyedit_menu): Do not allow to use certain commands
+ while the secret key is selected.
+
+2000-03-09 12:53:09 Werner Koch (wk@habibti.openit.de)
+
+ * keygen.c (ask_expire_interval): Movede parsig to ...
+ (parse_expire_string): ... this new function. And some new control
+ commands.
+ (proc_parameter_file): Add expire date parsing.
+ (do_generate_keypair): Allow the use of specified output files.
+
+2000-03-08 10:38:38 Werner Koch (wk@habibti.openit.de)
+
+ * keygen.c (ask_algo): Removed is_v4 return value and the commented
+ code to create Elg keys in a v3 packet. Removed the rounding
+ of key sizes here.
+ (do_create): Likewise removed arg v4_packet.
+ (gen_elg): Likewise removed arg version. Now rounding keysizes here.
+ (gen_dsa): Rounding keysize now here.
+ (release_parameter_list): New
+ (get_parameter*): New.
+ (proc_parameter_file): New.
+ (read_parameter_file): New.
+ (generate_keypair): Splitted. Now uses read_parameter_file when in
+ batch mode. Additional argument to specify a parameter file.
+ (do_generate_keypair): Main bulk of above fucntion and uses the
+ parameter list.
+ (do_create): Don't print long notice in batch mode.
+ * g10.c (main): Allow batched key generation.
+
+Thu Mar 2 15:37:46 CET 2000 Werner Koch <wk@gnupg.de>
+
+ * pubkey-enc.c (get_it): Print a note about unknown cipher algos.
+
+ * g10.c (opts): Add a note to the help listing about the man page
+ and removed some options from the help listing.
+
+ * keyedit.c (print_and_check_one_sig): Use a new function to truncate
+ the output of the user ID. Suggested by Jan-Benedict Glaw.
+
+Wed Feb 23 10:07:57 CET 2000 Werner Koch <wk@gnupg.de>
+
+ * helptext.c: typo fix.
+
+Thu Feb 17 13:39:32 CET 2000 Werner Koch <wk@gnupg.de>
+
+ * revoke.c: Removed a bunch of commented code.
+
+ * packet.h (SIGSUBPKT_REVOC_REASON): New.
+ * build-packet.c (build_sig_subpkt): Support new sub packet.
+ * parse-packet.c (parse_one_sig_subpkt): Ditto.
+ (dump_sig_subpkt): Ditto.
+ * revoke.c (ask_revocation_reason): New.
+ (release_revocation_reason_info): New.
+ (revocation_reason_build_cb): New.
+ (gen_revoke): Ask for reason.
+ * main.h (struct revocation_reason_info): Add declaration.
+ * keyedit.c (menu_revsig): Add support for revocation reason.
+ (menu_revkey): Ditto.
+ (sign_uid_mk_attrib): Renamed to ...
+ (sign_mk_attrib): ... this, made static and add support for reasons.
+
+Tue Feb 15 08:48:13 CET 2000 Werner Koch <wk@gnupg.de>
+
+ * build-packet.c (build_packet): Fixed fixing of old comment packets.
+
+ * import.c (import_keys): Fixed importing from stdin when called with
+ nnames set to zero as it normally happens.
+
+Mon Feb 14 14:30:20 CET 2000 Werner Koch <wk@gnupg.de>
+
+ * sig-check.c (check_key_signature2): Add new arg r_expired.
+ (do_signature_check): New arg to pass it down to ...
+ (do_check): New arg r-expire which is set when the signature
+ has expired.
+ * trustdb.c (check_sig_record): Set SIGF_EXPIRED flag and set
+ the expiretime to zero so that thi signature will not be checked
+ anymore.
+
+Fri Feb 11 17:44:40 CET 2000 Werner Koch <wk@gnupg.de>
+
+ * g10.c (g10_exit): Update the random seed_file.
+ (main): Set the random seed file. New option --no-random-seed-file.
+
+Thu Feb 10 17:39:44 CET 2000 Werner Koch <wk@gnupg.de>
+
+ * keyedit.c (menu_expire): Fixed segv due to unitialized sub_pk.
+ By Rémi.
+
+Thu Feb 10 11:39:41 CET 2000 Werner Koch <wk@gnupg.de>
+
+ * keylist.c (list_keyblock): Don't print warnings in the middle of
+ regulat output lines. By Rémi.
+
+ * sig-check.c: Include options.h
+
+Wed Feb 9 15:33:44 CET 2000 Werner Koch <wk@gnupg.de>
+
+ * gpg.c: New option --ignore-time-conflict
+ * sig-check.c (do_check): Implemented this option.
+ * trustdb.c (check_trust): Ditto.
+ * sign.c (do_sign): Ditto.
+ * keygen.c (generate_subkeypair): Ditto.
+
+ * encode.c (encode_simple): use iobuf_cancel after open failure.
+ Reported by Huy Le.
+
+Fri Jan 14 18:32:01 CET 2000 Werner Koch <wk@gnupg.de>
+
+ * packet.h (STRING2KEY): Changed mode from byte to int.
+ * parse-packet.c (parse_key): Add the special GNU protection stuff
+ * build-packet.c (so_secret_key): Ditto.
+ * seckey-cert.c (do_check): Ditto.
+ * keyedit.c (change_passphrase): Ditto.
+ * export.c (export_secsubkeys): New.
+ (do_export_stream): Hack to export the primary key using mode 1001.
+ * g10.c: New command --export-secret-subkeys
+
+Thu Jan 13 19:31:58 CET 2000 Werner Koch <wk@gnupg.de>
+
+ * armor.c (is_armored): Check for 1-pass-sig packets. Reported by
+ David Hallinan <hallinan@rtd.com>.
+ (armor_filter): Replaced one LF by the LF macro. Reported by
+ Wolfgang Redtenbacher.
+
+Wed Jan 5 11:51:17 CET 2000 Werner Koch <wk@gnupg.de>
+
+ * g10.c (main): Reset new global flag opt.pgp2_workarounds
+ when --openpgp is used.
+ * mainproc.c (proc_plaintext): Do the PGP2,5 workarounds only
+ when the global flag is set.
+ (proc_tree): Ditto.
+ * textfilter.c (copy_clearsig_text): Ditto.
+ * armor.c (armor_filter): Ditto.
+
+ * g10.c: New option --list-only
+ * mainproc.c (proc_tree): Don't do it if opt.list_only is active.
+ (proc_pubkey_enc): Implement option.
+
+ * status.h, status.c ({BEGIN,END}_{EN,DE}CRYPTION): New.
+ * cipher.c (cipher_filter): New status outputs.
+ * mainproc.c (proc_encrypted): New status outputs.
+
+Fri Dec 31 14:08:15 CET 1999 Werner Koch <wk@gnupg.de>
+
+ * armor.c (armor_filter): Made the "Comment:" header translatable.
+
+ * hkp.c (hkp_import): Make sure that the program does not return
+ success when there is a connection problem. Reported by Phillip Jones.
+
+Sun Dec 19 15:22:26 CET 1999 Werner Koch <wk@gnupg.de>
+
+ * armor.c (LF): Use this new macro at all places where a line LF
+ is needed. This way DOSish textfiles should be created when the
+ input data is also in dos mode.
+ * sign.c (LF): Ditto.
+ * textfilter.c (LF): Ditto.
+ (copy_clearsig_text): Disabled the forcing of CR,LF sequences
+ for DOS systems.
+
+ * plaintext.c (handle_plaintext): Fixes for line endings on DOS.
+ and react on a LF in cleartext.
+ * armor.c (fake_packet): Restore the original line ending after
+ removing trailing spaces.
+
+ * signal.c (got_fatal_signal): DOS fix.
+
+Thu Dec 16 10:07:58 CET 1999 Werner Koch <wk@gnupg.de>
+
+ * mainproc.c (print_failed_pkenc): Fix for unknown algorithm.
+ Found by fygrave@epr0.org.
+
+Thu Dec 9 10:31:05 CET 1999 Werner Koch <wk@gnupg.de>
+
+ * hkp.c: i18n the strings.
+
+Sat Dec 4 15:32:20 CET 1999 Werner Koch <wk@gnupg.de>
+
+ * trustdb.c (verify_key): Shortcut for ultimately trusted keys.
+
+Sat Dec 4 12:30:28 CET 1999 Werner Koch <wk@gnupg.de>
+
+ * pkclist.c (build_pk_list): Validate the trust using the namehash
+ if this one has been set by the key lookup.
+
+ * g10.c: Add --delete-secret-key to the help page.
+
+ * openfile.c (copy_options_file): Made static.
+ (try_make_homedir): New.
+ * ringedit.c (add_keyblock_resource): Use the try_make_hoemdir logic.
+ * tdbio.c (tdbio_set_dbname): Likewise.
+
+ * keygen.c (generate_user_id): Use m_alloc_clear() here. We should
+ better use an allocation function specific to the user_id packet.
+
+ * keygen.c (keygen_add_std_prefs): Changed symmetric preferences
+ to include Blowfish again. This is due to it's better speed compared
+ to CAST5.
+
+ * g10.c (strusage): Print the home directory.
+
+ * armor.c (armor_filter): Take action on the cancel control msg.
+ * filter.h (armor_filter_context_t): Add cancel flag.
+
+Mon Nov 29 21:52:11 CET 1999 Werner Koch <wk@gnupg.de>
+
+ * g10.c: New option --fast-list-mode ..
+ * keylist.c (list_keyblock): .. and implemented.
+ * mainproc.c (list_node): Ditto.
+
+ * import.c (mark_non_selfsigned_uids_valid): Fixed the case that there
+ is a uid without any packet following.
+
+Mon Nov 22 11:14:53 CET 1999 Werner Koch <wk@gnupg.de>
+
+ * mainproc.c (proc_plaintext): Never enable the hash processing
+ when skip_verify is active.
+
+ * armor.c (parse_header_line): Stop parsing on a WS line too.
+ Suggested by Aric Cyr.
+
+ * tdbdump.c (HEXTOBIN): Changed the name of the argument, so that
+ traditional cpp don't mess up the macros. Suggested by Jos Backus.
+
+ * mainproc.c (list_node): Print the PK algo in the --with-colon mode.
+ * keylist.c (list_keyblock): Ditto.
+
+ * signal.c (got_fatal_signal): Found the reason why exit(8) did not
+ work - it is better to set the disposition back to default before
+ raising the signal. Print the notice on stderr always.
+
+Fri Nov 12 20:33:19 CET 1999 Werner Koch <wk@gnupg.de>
+
+ * g10.c (make_username): Swapped the logic.
+ * keylist.c (public_key_list): Now takes a STRLIST as arg and moved
+ the creation ot this list to the caller, so that he can copy with
+ UTF-conversion of user IDs. Changed all callers.
+ (secret_key_list): Likewise.
+
+ * getkey.c (get_user_id_string_native): New and ...
+ * encode.c (write_pubkey_enc_from_list): ... use it here.
+
+ * pubring.asc: Updated.
+
+ * packet.h (PKT_PHOTO_ID): New.
+ * parse-packet.c (parse_photo_id): New.
+ * build-packet.c (do_user_id: Handle photo IDs.
+ (build_packet): Change CTB for photo IDs
+ * free-packet.c (free_user_id): Release memory used for photo IDs
+ * sig-check.c (hash_uid_node): Handle photo IDs too.
+ * trustdb.c (print_uid_from_keyblock): Hash photo ID.
+ (make_uid_records): Ditto.
+ * getkey.c (find_by_name): Ditto.
+ * keyedit.c (show_prefs): Ditto.
+ * keylist.c (list_keyblock): Ditto.
+
+Thu Oct 28 16:08:20 CEST 1999 Werner Koch <wk@gnupg.de>
+
+ * keygen.c (ask_expire_interval): Print a warning for systems
+ with a signed 32 time_t if the exiration time is beyoind 2038.
+
+Fri Oct 8 20:40:50 CEST 1999 Werner Koch <wk@gnupg.de>
+
+ * ringedit.c (enum_keyblocks): The last fix way really stupid;
+ reverted and set rt to Unknown.
+
+Fri Oct 8 20:32:01 CEST 1999 Werner Koch <wk@gnupg.de>
+
+ * ringedit.c (enum_keyblocks): Zero the entire kbpos out on open.
+
+ * g10.c (oEntropyDLL): Removed option.
+ (main): Made the warning on development versions more verbose.
+
+ * g10.c (oHonorHttpProxy): New option.
+ * hkp.c (hkp_ask_import,hkp_export): Implement this option.
+ * options.skel: Enable this option for new installations
+
+Mon Oct 4 21:23:04 CEST 1999 Werner Koch <wk@gnupg.de>
+
+ * import.c (import_keys): Changed calling interface, adjusted caller.
+ (import): Moved printing of stats out ...
+ (print_stats): New. ... to here.
+ (import_keys_stream): Call stats print here.
+ (import_keys): Print stats as totals for all files.
+
+ * tdbio.h (DIRF_NEWKEYS): New
+ * tdbio.c (tdbio_dump_record): Print the new flag.
+ * trustdb.c (check_trust_record): New arg sigs_only. Adapted all
+ callers.
+ (do_update_trust_record): Removed recheck arg and add a new sigs_only
+ do we can later improve on the performance. Changed all callers too.
+ (check_trustdb): Evalutate the new flag and add a status output.
+ Do a check when the dir record has not been checked.
+ (build_cert_tree): Evaluate the new flag.
+ (check_trust): Ditto. Do a trust_record check, when the dir record
+ is not marked as checked.
+ (mark_fresh_keys): New.
+ (clear_lid_table): New.
+ (sync_trustdb): New.
+ * import.c (import_keys): Call sync_trustdb() after processing.
+ (import_keys_stream): Ditto.
+ * tdbdump.c (import_ownertrust): Ditto.
+
+ * import.c (import_revoke_cert): Notify the trust DB.
+ (do_update_trust_record): Use |= to set the REVOKED bit and not &=;
+ shame on me for this bad copy+paste introduced bug.
+ (do_we_trust): Add trustmask to allow revoked key override to work.
+ Chnaged are to allow return of a mofified trustlevel. Adapted the
+ one caller.
+
+ * g10.c: New options --emulate-3des-s2k-bug
+ * passphrase.c (hash_passphrase): Implemented above.
+
+ * mainproc.c (proc_tree): Check for standalone signatures.
+ (do_check_sig): Print a notice for a standalone revocation
+ (check_sig_and_print): Do not print an error for unchecked standalone
+ revocations.
+
+Tue Sep 28 20:54:37 CEST 1999 Werner Koch <wk@gnupg.de>
+
+ * encode.c (encode_simple): Use new CTB when we don't have the
+ length of the file. This is somewhat strange as the comment above
+ indicates that this part is actually fixed for PGP 5 - maybe I simply
+ lost the source line, tsss.
+
+ * armor.c (armor_filter): Set a flag if no OpenPGP data has been found.
+ * verify.c (verify_signatures): Add an error helptext.
+
+Thu Sep 23 19:24:30 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * openfile.c (open_outfile): Fixed the 8dot3 handling.
+
+ * passphrase.c (passphrase_to_dek): Print uid using utf8 func.
+ * delkey.c (delete_key): Ditto.
+ * pkclist.c (show_paths,do_edit_ownertrust,do_we_trust): Ditto
+ (do_we_trust_pre): Ditto.
+ * trustdb.c (print_user_id,check_uidsigs): Ditto.
+ * revoke.c (gen_revoke,ask_revoke_sig): Ditto.
+
+Thu Sep 23 09:52:58 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * verify.c (print_file_status): New.
+ (verify_one_file): Moved status print to th new fnc. Add error status.
+ * status.c, status.h (STATUS_FILE_ERROR): New
+
+Wed Sep 22 10:14:17 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * openfile.c (make_outfile_name): Use case-insenstive compare for
+ DOS systems. Add ".pgp" to the list of know extensions.
+ (open_outfile): For DOS systems try to replace the suffiy instead of
+ appending it.
+
+ * status.c, status.h: Add STATUS_FILE_{START,DONE}.
+ * verify.c (verify_one_file): Emit these new stati.
+
+ * sign.c (clearsign_file): Avoid duplicated Entries in the "Hash:"
+ line. Those headers are now only _not_ printed when there are
+ only old-style keys _and_ all hashs are MD5.
+
+Mon Sep 20 12:24:41 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+
+ * verify.c (verify_files, ferify_one_file): New.
+ * g10.c: New command --verify-files
+
+Fri Sep 17 12:56:42 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * g10.c: Add UK spelling as alias for armor options ;-)
+
+ * import.c (append_uid): Fixed a SEGV when there is no selfsig and
+ no subkey.
+ (merge_sigs): Ditto. Removed the assertion.
+
+Wed Sep 15 16:22:17 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * g10.c: New option --entropy-dll-name
+
+Mon Sep 13 10:51:29 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * signal.c (got_fatal_signal): Print message using write(2) and
+ only for development versions.
+
+Mon Sep 6 19:59:08 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * tdbio.c (tdbio_set_dbname): Use mkdir macro
+ * ringedit.c (add_keyblock_resource): Ditto.
+
+Fri Sep 3 10:04:45 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * pkclist.c (build_pk_list): Skip keys set with --encrypt-to also
+ when asking for a key.
+
+ * plaintext.c (handle_plaintext): Make sure that we don't read a
+ second EOF in the read loop for partial length packets.
+
+ * mainproc.c (check_sig_and_print): print user ID as utf-8.
+
+Thu Sep 2 16:40:55 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * import.c (merge_blocks): First add new subkeys, then merge subkey
+ certificates.
+ (merge_sigs): Don't merge subkey signatures here.
+
+Wed Sep 1 15:30:44 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * keygen.c (ask_expire_interval): Fixed bug related to cpr_xx (tnx
+ Francis J. Lacoste).
+
+Tue Aug 31 17:20:44 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * plaintext.c (do_hash): Hash CR,LF for a single CR.
+ (ask_for_detached_datafile): Changed arguments to be closer to
+ those of hash_datafiles and cleanup the code a bit.
+ * mainproc.c (proc_tree): Workaround for pgp5 textmode detached
+ signatures. Changed behavior of asking for data file to be the same
+ as with provided data files.
+
+ * keylist.c (list_keyblock): Use UTF8 print functions.
+
+Mon Aug 30 20:38:33 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * import.c (chk_self_sigs): some s/log_error/log_info/ so that gpg
+ does not return an error if a key has some invalid packets.
+
+ * helptext.c: Fixed some typos and changed the way the
+ translation works. The english text is now the keyword for gettext
+ and not anymore the keyword supplied to the function. Done after
+ some discussion with Walter who thinks this is much easier for the
+ translators.
+
+ * misc.c (disable_core_dumps): Don't do it for DOSish systems.
+
+ * signal.c (signal_name): Bounds check on signum.
+
+Wed Aug 4 10:34:18 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * pubring.asc: Updated.
+
+ * pkclist.c (do_we_trust_pre,check_signatures_trust): Do not print
+ the warning about --always_trust when --quiet is used.
+
+ * pkclist.c (fpr_info): New and called at several places.
+
+ * parse-packet.c (dump_sig_subpkt): List revocation key contents.
+
+Mon Jul 26 09:34:46 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * pkclist.c (build_pk_list): Fixed typo in format string.
+
+ * trustdb.c (create_shadow_dir): Don't translate the error string.
+
+ * g10.c (main): Fixed spelling of user-id.
+ * getkey.c (find_by_name_pk,find_by_name_sk,
+ find_by_keyid,find_by_keyid_sk): Ditto and translate it.
+ * import.c (mark_non_selfsigned_uids_valid,delete_inv_parts): Ditto.
+
+
+Mon Jul 26 01:01:39 CEST 1999 Michael Roth <mroth@nessie.de>
+
+ * g10.c, options.h: New options --no-literal and --set-filesize
+
+ * encode.c (encode_simple, encode_crypt): Support for the options
+ --no-literal and --set-filesize.
+
+ * sign.c (sign_file): ditto.
+
+Fri Jul 23 13:53:03 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+
+ * ringedit.c (enum_keyblocks): Removed annoying error message in cases
+ when we have no keyring at all to enum.
+
+ * getkey.c (classify_user_id): Rewrote to relax the recognition of
+ keyIDs and fingerprints (Michael).
+
+ * mainproc.c (check_sig_and_print): Print status NO_PUBKEY.
+ (print_failed_pkenc): Print status NO_SECKEY.
+
+ * import.c (mark_non_selfsigned_uids_valid): New.
+ * g10.c: New option --allow-non-selfsigned-uid.
+
+ * pkclist.c (print_fpr): New.
+ (do_we_trust_pre): Print the fpr before asking whether to use the key
+ anyway.
+ (do_edit_ownertrust): Likewise.
+
+Thu Jul 22 20:03:03 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+
+ * ringedit.c (enum_keyblocks): Removed annoying error message in cases
+ when we have no keyring at all to enum.
+
+ * getkey.c (classify_user_id): Rewrote to relax the recognition of
+ keyIDs and fingerprints (Michael).
+
+ * mainproc.c (check_sig_and_print): Print status NO_PUBKEY.
+ (print_failed_pkenc): Print status NO_SECKEY.
+
+ * import.c (mark_non_selfsigned_uids_valid): New.
+ * g10.c: New option --allow-non-selfsigned-uid.
+
+Thu Jul 15 10:15:35 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * g10.c: New options --disable-{cipher,pubkey}-algo.
+
+Wed Jul 14 19:42:08 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * status.h (STATUS_IMPORTED): New.
+ * import.c (import): Print some status information (Holger Schurig).
+
+ * g10.c (main): Make --no-greeting work again. Add a warning when
+ --force-mds is used.
+
+Tue Jul 13 17:39:25 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * pkclist.c (do_edit_ownertrust): Changed the way help works.
+ (build_pk_list): Implemented default recipient stuff.
+ * g10.c: New options --default-recipient[-self]
+ (main): Suppress greeting in most cases, entering a passphrase or
+ a missing value is not considered to be interactive use.
+ Merged --print-md and --print-mds; the latter is now obsolete.
+ Changed the way --gen-random works and documented it.
+ Changed the way --gen-prime works and add a man entry.
+ * g10.c (MAINTAINER_OPTIONS): Removed.
+
+Mon Jul 12 18:45:57 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * keyedit.c (keyedit_menu): Add arg sign_mode and changed callers
+ * g10.c (main): New command --lsign-key.
+
+Mon Jul 12 14:55:34 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * mainproc.c (kidlist_item): New.
+ (release_list): Release failed pk-enc-list.
+ (print_failed_pkenc): New
+ (proc_encrypted): Print info about failed PK enc.
+
+ * openfile.c (make_outfile_name): s/error/info/
+
+ * passphrase.c (passphrase_to_dek): Return an empty passphrase when
+ in batch mode and don't make the warning message fatal
+ * seckey-cert.c (check_secret_key): Try only once when in batch mode.
+
+ * g10.c (make_username): New.
+
+Thu Jul 8 16:21:27 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+
+ * packet.h (PKT_ring_trust): New
+ * parse-packet.c (parse_trust): Store trust value
+ * build-packet (build_packet): Ignore ring trust packets.
+ * mainproc.c (add_ring_trust): New.
+ (list_node): Print "rtv" records.
+ * g10.c: New option --with-fingerprint.
+
+ * trustdb.c (verify_own_keys): Don't insert if we are dry running
+ (check_trust): Ditto.
+
+Wed Jul 7 13:08:40 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * Makefile.am: Support for libtool.
+
+ * keygen.c (ask_expire_interval): Hack to allow for an expire date.
+
+ * trustdb.c (do_update_trust_record,update_trust_record): Splitted.
+ (check_trust_record): New.
+ (check_trust,build_cert_tree): Check the dir record as needed.
+ (upd_pref_record): Removed.
+ (make_pref_record): New.
+ (propagate_validity): Stop as soon as we have enough validity.
+
+ * tbdio.c (MAX_CACHE_ENTRIES_HARD): Increased the limit.
+
+
+Fri Jul 2 11:45:54 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * g10.c (g10_exit): Dump random stats.
+
+ * sig-check.c (check_key_signature,check_key_signature2): Enhanced
+ version and wrapper for old function.
+ (do_signature_check,signature_check): Ditto.
+
+Thu Jul 1 12:47:31 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+
+ * keyedit.c (show_key_with_all_names): Print a notice for disabled keys.
+ (enable_disable_keys): Add functionality
+ * pkclist.c (edit_ownertrust): preserve disabled state.
+ (build_pk_list): Skip disabled keys.
+ * trustdb.c (upd_one_ownertrust): Ditto.
+ (build_cert_tree): Mask the ownertrust.
+ (trust_letter): Mask the value.
+ (do_check): Take disabled flag into account.
+
+ * passphrase.c (passphrase_to_dek): Add a pubkey_algo arg and changed
+ all callers.
+
+ * g10.c (utf8_strings): 2 new options.
+
+ * trustdb.c (insert_trust_record_by_pk): New, replaces the next one.
+ (insert_trust_record): Now takes a keyblock as arg. Changed all
+ callers to use the appropritae function.
+
+ * openfile.c (ask_outfile_name): New.
+ * plaintext.c (handle_plaintext): Ask for filename if there is
+ no valid syntax. Don't use fname varbatim but filter it.
+
+Tue Jun 29 21:44:25 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+
+ * trustdb.h (TRUST_FLAG_DISABLED): New.
+
+ * status.c (USE_CAPABILITIES): Capabilities support (Remi).
+
+ * tdbio.c : Added new fields to the DIR record.
+ (tdbio_write_record): Fixed the update of the hash tables.
+ (tdbio_delete_record): Drop the record from the hash tables.
+ (drop_from_hashtbl): New.
+
+ * status.c (cpr_get): Special online help mode.
+ * helptext.c ("keyedit.cmd"): Removed.
+ * keyedit.c (keyedit_menu): Use only help system.
+ (enable_disable_key): New bit doies not yet work.
+
+Sat Jun 26 12:15:59 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+
+ * dearmor.c (enarmor_file): Fixed comment string.
+ * tdbdump.c (export_ownertrust): Text fix.
+ * tbio.c (tdbio_invalid): Ditto.
+
+ * parse-packet.c (parse_key): Made temp buffer larger.
+
+ * Makefile.am (install-data-local): Add missing backslashes
+
+Tue Jun 15 12:21:08 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * g10.c (main): Made iterated+salted the default S2K method.
+
+ * Makefile.am (install-data-local): Use DESTDIR.
+
+ * passphrase.c (passphrase_to_dek): Emit missing-passphrase while in
+ batchmode.
+
+ * parse-packet.c (parse_pubkeyenc): Fixed a SEGV.
+
+Mon Jun 14 21:18:54 CEST 1999 Michael Roth <mroth@nessie.de>
+
+ * g10.c: New options --openpgp, --no-tty, --emit-version,
+ --default-comment and --lock-multiple
+
+Thu Jun 10 14:18:23 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * free-packet.c (free_encrypted): Fixed EOF case (Remi).
+ (free_plaintext): Ditto.
+
+ * helptext.c (keyedit.delsig.unknown): New (Remi).
+ * keyedit.c (print_and_check_one_sig): Add arg print_without_key and
+ changed all callers to make use of it (Remi):
+
+Tue Jun 8 13:36:25 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * keylist.c (print_key_data): New and called elsewhere.
+ * g10.c: New option --with-key-data
+
+Wed Jun 2 14:17:19 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * mainproc.c (proc_tree): Yet another bad hack to cope with
+ broken pgp2 created detached messages in textmode.
+
+Tue Jun 1 16:01:46 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * openfile.c (make_outfile_name): New.
+ * plaintext.c (handle_plaintext): Outputfile is now the inputfile
+ without the suffix.
+ * g10.c: New option --use-embedded-filename
+
+Mon May 31 19:41:10 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * g10.c (main): Fix for SHM init (Michael).
+
+ * compress.c, encr-data.c, mdfilter.c,
+ plaintext.c, free-packet.c: Speed patches (Rémi).
+
+Thu May 27 09:40:55 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * status.c (cpr_get_answer_yes_no_quit): New.
+ * keyedit.c (menu_delsig): New.
+ (check_all_keysigs): Splitted.
+ (print_and_check_one_sig): New.
+
+Wed May 26 14:36:29 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * build-packet.c (build_sig_subpkt): Support large packets.
+ * parse-packet.c (enum_sig_subpkt): Replaces parse_sig_subpkt.
+ * mainproc.c (print_notation_data): Print all notation packets.
+ * g10.c (add_notation_data): Add a way to specify the critical flag.
+ (main): Add option --set-policy-url.
+ (check_policy_url): Basic checks.
+ * sign.c (mk_notation_and_policy): Replaces mk_notation.
+
+ * parse-packet.c (can_handle_critical): Moved decision whether we can
+ handle critical subpacket to an extra function.
+
+Tue May 25 19:50:32 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * sign.c (sign_file): Always use compression algo 1 for signed
+ onyl file becuase we can´ be sure the the verifier supports other
+ algorithms.
+
+ * build-packet.c (build_sig_subpkt): Support for notation data.
+ * sign.c (sign_file,clearsign_file,make_keysig_packet): Ditto.
+ (mk_notation): New.
+ * g10.c (add_notation_data): New and add option -N
+ * mainproc.c (print_notation_data): New.
+ (check_sig_and_print): Print any notation data of the signed text.
+
+Sun May 23 14:20:22 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * pkclist.c (check_signatures_trust): Print a warning and return
+ immediateley if opt.always_trust is true.
+
+ * g10.c (main): Corrected handling of no-default-keyring
+
+ * pkclist.c (algo_available): Disable Twofish until we have settled
+ how to do the MDC.
+
+ * hkp.c: Disable everything for mingw32
+
+Sat May 22 22:47:26 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * mainproc.c (check_sig_and_print): Add sig creation time to the
+ VALIDSIG status output. Add more info to the ERRSIG output.
+ * sig-check.c (signature_check): Add sig time after epoch to SIG_ID.
+
+ * import.c (import_one): Merge duplicate user IDs.
+ (collapse_uids): New.
+ * kbnode.c (move_kbnode): New.
+ (remove_kbnode): New.
+ * keyedit.c (keyedit_menu): Call collapse_uids.
+
+ * g10.c: new option --logger-fd.
+
+ * import.c: s/log_*_f/log_*/
+
+Thu May 20 14:04:08 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * misc.c (pull_in_libs): do the volatile only for gcc
+
+ * sig-check (signature_check): Emit SIG_iD only for classes 0 and 1.
+
+ * armor.c (armor_filter): Add detection of PGP2 created clearsigs.
+ (fake_packet): A tab is not a WS for pgp2 - handle this.
+ * textfilter.c (len_without_trailing_chars): New.
+ (copy_clearsig_text): Add pgp2mode arg.
+ * sign.c (clearsign_file): pass old_style to the above fnc.
+
+
+Wed May 19 16:04:30 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * g10.c: New option --interactive.
+
+ * mainproc.c (proc_plaintext): Add workaround for pgp2 bug
+ (do_check_sig): Ditto.
+ (proc_tree): Ditto.
+ * plaintext.c (do_hash): Ditto.
+ (hash_datafiles): Ditto, add an arg, changed all callers.
+ * mdfilter.c (md_filter): Add support for the alternate hash context.
+
+Mon May 17 21:54:43 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * parse-packet.c (parse_encrypted): Support for PKT_ENCRYPTED_MDC.
+ * build-packet.c (do_encrypted_mdc): Ditto.
+ * cipher.c (write_header): Add mdc hashing.
+ (cipher_filter): write out the hash.
+ * mainproc.c (do_proc_packets): Add PKT_ENCRYPTED_MDC.
+ * encr-data.c (decrypt_data): Add mdc hashing.
+ (mdc_decode_filter): New.
+
+ * parse-packet.c (parse_sig_subpkt): Fixed stupid bug for subpkt
+ length calculation
+ (parse_signature): Fixed even more stupid bug.
+
+Sat May 8 19:28:08 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * build-packet.c (do_signature): Removed MDC hack.
+ * encode.c (encode_crypt_mdc): Removed.
+ * mainproc.c (do_check_sig): Removed MDC hack.
+ (check_sig_and_print): Ditto.
+ * parse-packet.c (parse_signature): Ditto.
+ * sig-check.c (mdc_kludge_check): Ditto.
+ * free-packte.c (copy_signature, free_seckey_enc): Ditto.
+
+ * parse-packet.c (parse_signature,parse_key): Store data of
+ unknown algorithms with mpi_set_opaque inseatd of the old
+ faked data stuff.
+ (read_rest): Removed.
+ (read_rest2): Renamed to read_rest
+ * build-packet.c (write_fake_data): Use mpi_get_opaque.
+ * free-packet.c (cp_fake_data): Removed and cahnged all callers
+ to use mpi_copy.
+ (free_pubkey_enc,free_seckey_enc,release_public_key_parts,
+ release_secret_key_parts): Use mpi_free for opaque data.
+
+Thu May 6 14:18:17 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * trustdb.c (check_trust): Check for revoked subkeys.
+ * pkclist.c (do_we_trust): Handled revoked subkeys.
+ (do_we_trust_pre): Ditto.
+ (check_signatures_trust): Ditto.
+
+ * build-packet.c (hash_public_key): Fix for ancient g10 keys.
+
+ * mainproc.c (do_proc_packets): Return EOF if no data has been read.
+ * g10.c (main): Catch errors for default operation.
+
+Thu Apr 29 12:29:22 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * sign.c (sign_file): Fixed hashing in case of no subpackets.
+ (clearsign_file): Ditto.
+ (make_keysig_packet): Ditto.
+
+Wed Apr 28 13:03:03 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * keyedit.c (keyedit_menu): Add new command revkey.
+ * (menu_revkey): New.
+
+
+Mon Apr 26 17:48:15 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * parse-packet.c (parse_signature): Add the MDC hack.
+ * build-packet.c (do_signature): Ditto.
+ * free-packet.c (free_seckey_enc,copy_signature,cmp_signatures): Ditto.
+ * mainproc.c (do_check_sig): Ditto.
+ * sig-check.c (mdc_kludge_check): New.
+ * encode.c (encrypt_mdc_file): New.
+
+ * keyedit.c (check_all_keysigs): List revocations.
+ * (menu_revsig): New.
+ * sign (make_keysig_packet): Support for class 0x30.
+
+Sun Apr 18 20:48:15 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * pkclist.c (select_algo_from_prefs): Fixed the case that one key
+ has no preferences (Remi Guyomarch).
+
+ keylist.c (list_keyblock): ulti_hack to propagate trust to all uids.
+
+Sun Apr 18 10:11:28 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * seckey-cert.c (do_check): Use real IV instead of a 0 one, so that
+ it works even if the length of the IV doesn't match the blocksize.
+ Removed the save_iv stuff.
+ (protect_secret_key): Likewise. Create the IV here.
+ * packet.h (PKT_secret_key): Increased size of IV field and add a
+ ivlen field.
+ * parse-packet.c (parse_key): Use the len protect.ivlen.
+ * build-packet.c (do_secret_key). Ditto.
+
+ * getkey.c (key_byname): Close keyblocks.
+
+ * Makefile.am (gpgm): Removed this
+ * g10.c: Merged gpg and gpgm
+
+ * import.c (import): Utilize option quiet.
+ * tdbio.c (tdbio_set_dbname): Ditto.
+ * ringedit.c (add_keyblock_resource,keyring_copy): Ditto.
+
+ * keyedit.c (sign_uids): Add some batch support.
+
+ * g10.c (main): add call to tty_batchmode.
+
+Fri Apr 9 12:26:25 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * status.c (write_status_text): Some more status codes.
+ * passphrase_to_dek (passphrase_to_dek): add a status code.
+ * seckey_cert.c (check_secret_key): Likewise.
+
+ * encr-data.c (decrypt_data): Reverse the last changes
+ * cipher.c (write_header): Ditto.
+
+ * parse-packet.c (parse_key): Dropped kludge for ancient blowfish mode.
+
+Thu Apr 8 09:35:53 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * mainproc.c (proc_encrypted): Add a new status output
+ * passphrase.c (passphrase_to_dek): Ditto.
+ * status.h status.c: Add new status tokens.
+
+Wed Apr 7 20:51:39 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * encr-data.c (decrypt_data): Fixes for 128 bit blocksize
+ * cipher.c (write_header): Ditto.
+ * seckey-cert.c (do_check): Ditto.
+ (protect_secret_key). Ditto.
+ * misc.c (print_cipher_algo_note): Twofish is now a standard algo.
+
+ * keygen.c (do_create): Fixed spelling (Gaël Quéri)
+ (ask_keysize): Only allow keysizes up to 4096
+
+ * ringedit.c (add_keyblock_resource): chmod newly created secrings.
+
+ * import.c (delete_inv_parts): Fixed accidently deleted subkeys.
+
+Tue Apr 6 19:58:12 CEST 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * armor.c: Removed duped include (John Bley)
+ * mainproc.c: Ditto.
+
+ * build-packet.c (hash_public_key): Fixed hashing of the header.
+
+ * import.c (delete_inv_parts): Allow import of own non-exportable sigs.
+
+Sat Mar 20 13:59:47 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * armor.c (fake_packet): Fix for not not-dash-escaped
+
+Sat Mar 20 11:44:21 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * g10.c (main): Added command --recv-keys
+ * hkp.c (hkp_import): New.
+
+Wed Mar 17 13:09:03 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * trustdb.c (check_trust): add new arg add_fnc and changed all callers.
+ (do_check): Ditto.
+ (verify_key): Ditto.
+ (propagate_validity): Use the new add_fnc arg.
+ (print_user_id): Add the FILE arg.
+ (propagate_ownertrust): New.
+ * pkclist.c (add_ownertrust_cb): New and changed the add_ownertrust
+ logic.
+
+ * getkey.c (get_keyblock_bylid): New.
+ * trustdb.c (print_uid_from_keyblock): New.
+ (dump_tn_tree_with_colons): New.
+ (list_trust_path): Add colon print mode.
+
+ * trustdb.c (insert_trust_record): Always use the primary key.
+
+ * encode.c (encode_simple): Added text_mode filter (Rémi Guyomarch)
+ (encode_crypt): Ditto.
+
+ * mainproc.c (proc_pubkey_enc): Added status ENC_TO.
+ * armor.c (armor_filter): Added status NODATA.
+ * passphrase.c (passphrase_to_dek): Always print NEED_PASSPHRASE
+ * seckey_cert.c (check_secret_key): Added BAD_PASS status.
+
+ * g10.c (main): Set g10_opt_homedir.
+
+Sun Mar 14 19:34:36 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * keygen.c (do_create): Changed wording of the note (Hugh Daniel)
+
+Thu Mar 11 16:39:46 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * tdbdump.c: New
+
+ * trustdb.c (walk_sigrecs,do_list_sigs,list_sigs,
+ list_records,list_trustdb,export_ownertrust,import_ownertrust): Moved
+ to tdbdump.c
+ (init_trustdb): renamed to setup_trustdb. Changed all callers.
+ (do_init_trustdb): renamed to init_trustdb().
+ * trustdb.c (die_invalid_db): replaced by tdbio_invalid.
+ * tdbio.c (tdbio_invalid): New.
+
+ * import.c (delete_inv_parts): Skip non exportable signatures.
+ * keyedit.c (sign_uid_mk_attrib): New.
+ (sign_uids): Add the local argument.
+ (keyedit_menu): New "lsign" command.
+ * trustdb.c (register_trusted_key): Removed this and all related stuff.
+ * g10.c (oTrustedKey): Removed option.
+
+ * tdbio.h (dir.valcheck): New trustdb field.
+ * tdbio.c: Add support for this field
+ (tdbio_read_modify_stamp): New.
+ (tdbio_write_modify_stamp): New.
+ * trustdb.c (do_check): Check against this field. Removed cache update.
+ (verify_key): Add cache update.
+ (upd_uid_record): Some functional changes.
+ (upd_cert_record): Ditto
+
+Wed Mar 10 11:26:18 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * keylist.c (list_keyblock): Fixed segv in uid. Print 'u' as
+ validity of sks.
+
+Mon Mar 8 20:47:17 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * getkey.c (classify_user_id): Add new mode 12 (#<lid>).
+
+ * seckey-cert.c (check_secret_key): replaced error by info.
+
+ * trustdb.c (query_trust_info): Add another arg, changed all callers.
+ (check_trust): Ditto.
+ (do_check): Ditto.
+ (verify_key): Handle namehash.
+ * keylist.c (list_keyblock): print trust info for user ids.
+
+ * sig-check.c (signature_check): Add sig-created to status output.
+
+Tue Mar 2 16:44:57 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * textfilter.c (copy_clearsig_text): New.
+ (clearsign): Removed.
+ * sign.c (clearsign_file): does not use textfiler anymore.
+
+ * keygen.c (ask_user_id): print a note about the used charset.
+
+Tue Mar 2 10:38:42 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * sig-check.c (signature_check): sig-id now works for all algos.
+
+ * armor.c (armor_filter): Fixed armor bypassing.
+
+Sun Feb 28 19:11:00 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * keygen.c (ask_user_id): Don't change the case of email addresses.
+ (has_invalid_email_chars): Adjusted.
+
+ * keylist.c (list_one): Really list serect keys (Remi Guyomarch)
+
+ * keyedit.c (menu_select_uid): Add some braces to make egcs happy.
+ (menu_select_key): Ditto.
+
+ * mainproc.c (do_proc_packets): List sym-enc packets (Remi Guyomarch)
+
+Fri Feb 26 17:55:41 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * pkclist.c (build_pk_list): Return error if there are no recipients.
+
+ * sig-check.c (signature_check): New signature id feature.
+ * armor.c (make_radic64_string): New.
+
+ * mainproc.c (proc_pubkey_enc): early check for seckey availability.
+
+ * pkclist.c (do_we_trust_pre): print user id before asking.
+
+ * ringedit.c (add_keyblock_resource,get_keyblock_handle): Cleaner
+ handling of default resource.
+
+
+Thu Feb 25 18:47:39 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * pkclist.c (algo_available): New.
+ (select_algo_from_prefs): Check whether algo is available.
+
+ * ringedit.c (keyring_copy): Take care of opt.dry_run.
+ (do_gdbm_store): Ditto.
+ * openfile.c (open_outfile). Ditto.
+ (copy_options_file): Ditto.
+ * trustdb.c (update_trustdb): Ditto.
+ (clear_trust_checked_flag): Ditto.
+ (update_trust_record): Ditto.
+ (insert_trust_record): Ditto.
+
+Wed Feb 24 11:07:27 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * keylist.c (secret_key_list): Now really list the secret key.
+
+ * trustdb.c (do_init_trustdb): New. Init is now deferred.
+
+Mon Feb 22 20:04:00 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * getkey.c (lookup_sk): Return G10ERR_NO_SECKEY and not x_PUBKEY.
+
+Fri Feb 19 15:49:15 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * pkclist.c (select_algo_from_prefs): retrieve LID if not there.
+
+ * armor.c (fake_packet): Replaced ugly lineending handling.
+
+ * g10.c (oNoEncryptTo): New.
+ * pkclist.c (build_pk_list): Implemented this option.
+
+ * g10.c (main): Greeting is now printed to stderr and not to tty.
+ Use add_to_strlist() instead of direct coding.
+
+ * import.c (import): Use iobuf_push_filter2.
+
+ * mainproc.c (check_sig_and_print): Print all user ids
+ for good signatures.
+ * getkey.c (get_pubkeyblock): New.
+
+ * import.c (chk_self_sigs): Fixed SEGV for unbounded class 0x18 keys.
+ (delete_inv_parts): Delete special marked packets.
+
+Tue Feb 16 14:10:02 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * g10.c (main): New option --encrypt-to
+
+ * pkclist.c (build_pk_list): Implemented encrypt-to.
+
+ * parse-packet.c (parse_user_id): Removed the hack to work with
+ utf-8 strings.
+
+ * g10.c (main): Install lockfile cleanup handler.
+ * tdbio.c (cleanup): Removed: this is now handled by dotlock.
+
+Sat Feb 13 14:13:04 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * tdbio.c (tdbio_set_dbname): Init lockhandle for a new trustdb
+
+Wed Feb 10 17:15:39 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * g10.c (main): check for development version now in configure
+
+ * tdbio.c (tdbio_write_record): Add uid.validity
+ (tdbio_read_record) : Ditto.
+ (tdbio_dump_record) : Ditto.
+
+ * keygen.c (keygen_add_std_prefs): Replaced Blowfish by Twofish,
+ removed MD5 and Tiger.
+ * pubkey-enc.c (get_it): Suppress warning about missing Blowfish
+ in preferences in certain cases.
+
+ * ringedit.c (lock_rentry,unlock_rentry): New.
+
+ * getkey.c (key_byname): Pass ret_kb down to lookup_xx.
+
+ * armor.c (armor_filter): No output of of empty comment lines.
+ Add option --no-version to suppress the output of the version string.
+
+ * getkey.c: Release the getkey context for auto context variables.
+
+Sun Jan 24 18:16:26 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * getkey.c: Changed the internal design to allow simultaneous
+ lookup of multible user ids
+ (get_pubkey_bynames): New.
+ (get_seckey_bynames): New.
+ (get_seckey_next): New.
+ (get_seckey_end): New.
+ * keylist.c (list_one): Use the new functions.
+
+ * keylist.c (list_keyblock): add a newline for normal listings.
+
+ * g10.c (--recipient): New option name to replace --remote-user
+
+
+Wed Jan 20 18:59:49 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * textfilter.c: Mostly rewritten
+ * plaintext.c (handle_plaintext): Use now text_filter semantics.
+
+Tue Jan 19 19:34:58 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * export.c (export_pubkeys_stream): New.
+ (do_export_stream): New.
+ * g10.c (aSendKeys): New command.
+ * hkp.c (hkp_export): New.
+
+ * compress.c (do_uncompress): Hack for algo 1 and 1.1.3
+
+Sun Jan 17 11:04:33 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * textfilter.c (text_filter): Now uses iobuf_read_line().
+ (read_line): Removed.
+
+ * armor.c (trim_trailing_spaces): Removed and replaced
+ by trim_trailing_ws from libutil
+
+Sat Jan 16 12:03:27 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * hkp.c (hkp_ask_import): Use only the short keyid
+
+Sat Jan 16 09:27:30 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * import.c (import_key_stream): New
+ (import): New, moved most of import_keys here.
+ * g10.c: New option --keyserver
+ * mainproc.c (check_sig_and_print): Hook to import a pubkey.
+
+ * pref.c pref.h : Removed
+
+ * hkp.c hkp.h: New
+
+Wed Jan 13 14:10:15 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * armor.c (radix64_read): Print an error if a bad armor was detected.
+
+Wed Jan 13 12:49:36 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * armor.c (radix64_read): Now handles malformed armors produced
+ by some buggy MUAs.
+
+Tue Jan 12 11:17:18 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * ringedit.c (find_keyblock_bysk): New.
+
+ * skc_list.c (is_insecure): New.
+ (build_sk_list): usage check for insecure keys.
+
+ * import.c (chk_self_sigs): Add handling for subkeys.
+ (delete_inv_parts): Skip unsigned subkeys
+
+ * sig-check.c (do_check): Print info if the signature is older
+ than the key.
+ * keygen.c (generate_subkeypair): Fail on time warp.
+ * sign.c (do_sign): Ditto.
+
+Sun Jan 10 15:10:02 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * armor.c (fake_packet): Fixed not-dash-escaped bug.
+
+Sat Jan 9 16:02:23 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * sig-check.c (do_check): Output time diff on error
+
+ * status.c (STATUS_VALIDSIG): New.
+ (is_status_enabled): New.
+ * mainproc.c (check_sig_and_print): Issue that status message.
+
+ * plaintext.c (special_md_putc): Removed
+
+ * armor.c (armor_filter): print error for truncated lines.
+
+ * free-packet.c (free_encrypted): Revomed call to set_block_mode.
+ (free_plaintext): Ditto.
+
+Thu Jan 7 18:00:58 CET 1999 Werner Koch <wk@isil.d.shuttle.de>
+
+ * pkclist.c (add_ownertrust): Fixed return value.
+
+ * encr-data.c (decrypt_data): Disabled iobuf_set_limit and
+ iobuf_pop_filter stuff.
+ * compress.c (handle_compressed): Disabled iobuf_pop_filter.
+
+ * packet.h (PKT_secret_key): Add is_primary flag.
+ * parse-packet.c (parse_key): Set this flag.
+ * passphrase.c (passphrase_to_dek): Kludge to print the primary
+ keyid - changed the API: keyid must now hold 2 keyids.
+ * getkey.c (get_primary_seckey): New.
+ * seckey-cert.c (do_check): pass primary keyid to passphrase query
+
+ * tbdio.c (open_db): removed the atexit
+ (tdbio_set_dbname): and moved it to here.
+
+ * armor.c: Rewrote large parts.
+
+Tue Dec 29 19:55:38 CET 1998 Werner Koch <wk@isil.d.shuttle.de>
+
+ * revoke.c (gen_revoke): Removed compression.
+
+ * pkclist.c (do_we_trust_pre): special check for revoked keys
+
+ * trustdb.c (update_trust_record): Fixed revoke flag.
+
+Tue Dec 29 14:41:47 CET 1998 Werner Koch <wk@isil.d.shuttle.de>
+
+ * misc.c (disable_core_dumps): Check for EINVAL (Atari)
+
+ * getkey (merge_one_pk_and_selfsig): Fixed search of expiredate.
+ (merge_keys_and_selfsig): Ditto.
+
+ * free-packet.c (cmp_public_keys): cmp expire only for v3 packets
+ (cmp_secret_keys): Ditto.
+ (cmp_public_secret_key): Ditto.
+
+Wed Dec 23 17:12:24 CET 1998 Werner Koch <wk@isil.d.shuttle.de>
+
+ * armor.c (find_header): Reset not_dashed at every header
+
+Wed Dec 23 13:18:14 CET 1998 Werner Koch <wk@isil.d.shuttle.de>
+
+ * pkclist.c (add_ownertrust): Refresh validity values.
+
+ * trustdb.c (enum_cert_paths_print): New arg refresh.
+
+ * ringedit.c: Fixed problems fix keyrings
+ * parse-packet.c (dbg_parse_packet): New debug functions.
+
+ * getkey.c (getkey_disable_caches): New.
+ * import.c (import_keys): Disable caches.
+
+Thu Dec 17 18:31:15 CET 1998 Werner Koch <wk@isil.d.shuttle.de>
+
+ * misc.c (trap_unaligned): Only for glibc 1
+
+ * sign.c (write_dash_escaped): Now escapes "From " lines
+ * g10.c: New option --escape-from-lines
+
+ * trustdb.c (sort_tsl_list): New
+ (list_trust_path): Now prints sorted list.
+ (enum_cert_paths): Likewise.
+ (enum_cert_paths_print): New.
+ (print_paths): New printing format.
+ * pkclist.c (add_ownertrust): New arg quit.
+ (edit_ownertrust): New quit selection and does not query
+ the recipients ownertrust anymore.
+ (add_ownertrust): Print the ceritficate path.
+
+
+Mon Dec 14 21:18:49 CET 1998 Werner Koch <wk@isil.d.shuttle.de>
+
+ * parse-packet.c (parse_signature): Now checks for critical bit
+ (parse_sig_subpkt): Splitted.
+ (parse_one_sig_subpkt): New.
+ * sig-check.c (do_check): handle critical bit.
+
+Sun Dec 13 14:10:56 CET 1998 Werner Koch <wk@isil.d.shuttle.de>
+
+ * pcklist.c (select_algo_from_prefs): Preferences should
+ now work (lost the != ? )
+
+Thu Dec 10 20:15:36 CET 1998 Werner Koch <wk@isil.d.shuttle.de>
+
+ * ringedit.c (gdbm_store): Fix for inserts
+
+ * g10.c (main): New option --export-all
+ * export.c (export_pubkeys): New arg.
+ (do_export): Now may skip old keys.
+
+ * status.c: Minor patches for Sun's cc
+
+ * keygen.c (ask_algo): Disabled v3 ElGamal choice, rearranged
+ the numbers. Add a warning question when a sign+encrypt key
+ is selected.
+
+ * g10.c (do_not_use_RSA): Removed.
+ * misc.c (print_pubkey_algo_note): New as replacement for the
+ do_not_use_RSA() and chnaged all callers.
+ (print_cipher_algo_note): New.
+ (print_hash_algo_note): New.
+
+ * cipher.c (write_header): Add a call to print_cipher_algo_note.
+ * seckey-cert.c (protect_secret_key): Ditto
+ * sign.c (do_sign): Add a call to print_digest_algo_note.
+
+ * getkey.c (get_long_user_id_string): New.
+ * mainproc.c (check_sig_and_print): Changed the format of the
+ status output.
+
+ * encrypt.c (write_pubkey_enc_from_list): print used symmetric cipher.
+
+ * pkclist.c (do_we_trust): Changed a message.
+
+Wed Dec 9 13:41:06 CET 1998 Werner Koch <wk@isil.d.shuttle.de>
+
+ * misc.c (trap_unaligned) [ALPHA]: Only if UAC_SIGBUS is defined.
+
+ * sign.c (write_dash_escaped): Add the forgotten patch by Brian Moore.
+
+ * compress.c (do_uncompress): Fixed the inflating bug.
+
+
+Tue Dec 8 13:15:16 CET 1998 Werner Koch <wk@isil.d.shuttle.de>
+
+ * trustdb.c (upd_uid_record): Now uses the newest self-signature
+ (insert_trust_record): Now calls update with recheck set to true.
+ (register_trusted_key): New.
+ (verify_own_keys): Enhanced by list of trusted keys.
+
+ * g10.c (main): Print a warning when a devel version is used.
+ (main): New option --trusted-key
+
+ * import.c (merge_blocks): Fixed merging of new user ids and
+ added merging of subkeys.
+ (append_uid): Ditto.
+ (merge_keysig): New.
+ (append_key): New.
+ * getkey.c (merge_one_pk_and_selfsig): Get the expiration time
+ from the newest self-signature.
+ (merge_keys_and_selfsig): Ditto.
+
+ * free-packet.c (cmp_secret_key): New.
+
+
+Fri Nov 27 21:37:41 CET 1998 Werner Koch <wk@isil.d.shuttle.de>
+
+ * g10.c: New option --lock-once
+ * tdbio.c (open_db): Add an atexit
+ (cleanup): New.
+ (tdbio_sync): Add locking.
+ (tdbio_end_transaction): Ditto.
+ (put_record_into_cache): Ditto.
+ * ringedit.c (keyring_copy): Ditto.
+ (cleanup): New.
+ (add_keyblock_resource): Add an atexit.
+
+Fri Nov 27 15:30:24 CET 1998 Werner Koch <wk@isil.d.shuttle.de>
+
+ * armor.c (find_header): Another fix for clearsigs.
+
+Fri Nov 27 12:39:29 CET 1998 Werner Koch <wk@isil.d.shuttle.de>
+
+
+ * status.c (display_help): Removed.
+ * helptext.c: New and removed the N_() from all cpr_gets.
+
+
+Fri Nov 20 16:54:52 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c (main): New option --not-dash-escaped
+ * sign.c (write_dashed_escaped): Ditto.
+ * armor.c (find_header): Support for NotDashEscaped header.
+
+ * getkey.c: print "disabled cache.." only if verbose is used.
+
+Thu Nov 19 07:17:31 1998 Werner Koch <werner.koch@guug.de>
+
+ * parse-packet.c (dump_sig_subpkt): Fixed expire listing
+ * getkey.c (merge_keys_and_selfsig): Fixed expire calculation.
+ (merge_one_pk_and_selfsig): Ditto.
+ * keyedit.c (menu_expire). Ditto.
+ * keygen.c (keygen_add_key_expire): Ditto.
+ (ask_expire_interval): New and changed all local function to use
+ this instead.
+ (keygen_add_key_expire): Opaque should now be a public key;
+ changed all callers.
+
+ * parse.packet.c (parse): use skip_rest to skip packets.
+
+ * keyedit.c (keyedit_menu): New arg for cmdline cmds.
+
+Wed Nov 18 20:33:50 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * trustdb.c (check_trustdb): Now rechecks all gived userids.
+ (collect_paths): Some fixes.
+ (upd_pref_records): Skips empty items, evaluate all items.
+
+ * parse-packet.c (dump_sig_subpkt): Better listing of prefs.
+ (skip_packet): Now knows about marker packet
+
+ * g10.c: removed cmd "--edit-sig".
+
+ * pubring.asc: Updated.
+
+Sat Nov 14 14:01:29 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c (main): Changed syntax of --list-trust-path
+ * trustdb.c (list_trust_path): Replaced max_depth by
+ opt.max_cert_depth
+
+Fri Nov 13 07:39:58 1998 Werner Koch <werner.koch@guug.de>
+
+ * trustdb.c (collect_paths): Removed a warning message.
+ (enum_trust_web): Removed.
+ (enum_cert_paths): New.
+ * pkclist.c (add_ownertrust): Changed to use enum_cert_paths.
+ (edit_ownertrust): Now list ceritficates on request.
+ (show_paths): New.
+
+Wed Nov 11 18:05:44 1998 Werner Koch <werner.koch@guug.de>
+
+ * g10.c (main): New option --max-cert-depth
+ * tdbio.h: add new fields to ver and dir record.
+ * tdbio.c: read/write/dump of these fields.
+ (tdbio_db_matches_options): New.
+ * trustdb.c: replaced MAC_CERT_DEPTH by opt.max_cert_depth.
+ (do_check): cache validity and changed other functions
+ to reset the cached value.
+
+ * keylist.c (list_one): Now lists the ownertrust.
+ * mainproc.c (list_node): Ditto.
+
+Tue Nov 10 10:08:59 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c (g10_exit): Now looks at the new g10_errors_seen.
+ * mainproc.c (check_sig_and_print): Sets g10_errors_seen.
+
+ * *.c : i18n many more strings.
+
+ * ringedit.c (locate_keyblock_by_keyid): Add HAVE_LIBGDBM
+ (locate_keyblock_by_fpr): Ditto.
+
+ * g10.c (main): removed unsused "int errors".
+ (main): Add new option --charset.
+
+ * g10.c (main): special message for the unix newbie.
+
+Mon Nov 9 07:17:42 1998 Werner Koch <werner.koch@guug.de>
+
+ * getkey.c (finish_lookup): Kludge to prefere algo 16.
+
+ * trustdb.c (new_lid_table): Clear cached item.
+
+ * status.c (cpr_get_utf8): New.
+ * pkclist.c (build_pk_list): Uses this.
+
+Sun Nov 8 17:20:39 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * mainproc.c (check_sig_and_print): Why did I use strlen()-1
+ in the printf? - This truncated the TZ.
+
+Sat Nov 7 15:57:28 1998 me,,, (wk@tobold)
+
+ * getkey.c (lookup): Changes to support a read_next.
+ (get_pubkey): Fixed a memory leak.
+
+ * keylist.c (list_one): Now lists all matching user IDs.
+
+Tue Nov 3 16:19:21 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * keygen.c (ask_user_id): Now converted to UTF-8
+
+ * g10.c (main): Kludge for pgp clearsigs and textmode.
+
+Fri Oct 30 16:40:39 1998 me,,, (wk@tobold)
+
+ * signal.c (block_all_signals): New.
+ (unblock_all_signals): New
+ * tdbio.c (tdbio_end_transaction): Now blocks all signals.
+
+ * trustdb.c (new_lid_table): Changed the representation of the
+ former local_lid_info stuff.
+
+ * trustdb.c (update_trust_record): Reorganized the whole thing.
+ * sig-check.c (check_key_signature): Now handles class 0x28
+
+
+Wed Oct 28 18:56:33 1998 me,,, (wk@tobold)
+
+ * export.c (do_export): Takes care of the exportable sig flag.
+
+Tue Oct 27 14:53:04 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * trustdb.c (update_trust_record): New "fast" parameter.
+
+Sun Oct 25 19:32:05 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * openfile.c (copy_options_File): New.
+ * ringedit.c (add_keyblock_resource): Creates options file
+ * tdbio.c (tdbio_set_dbname): Ditto.
+
+Sat Oct 24 14:10:53 1998 brian moore <bem@cmc.net>
+
+ * mainproc.c (proc_pubkey_enc): Don't release the DEK
+ (do_proc_packets): Ditto.
+
+Fri Oct 23 06:49:38 1998 me,,, (wk@tobold)
+
+ * keyedit.c (keyedit_menu): Comments are now allowed
+
+ * trustdb.c: Rewrote large parts.
+
+
+Thu Oct 22 15:56:45 1998 Michael Roth (mroth@nessie.de)
+
+ * encode.c: (encode_simple): Only the plain filename without
+ a given directory is stored in generated packets.
+ (encode_crypt): Ditto.
+
+ * sign.c: (sign_file) Ditto.
+
+
+Thu Oct 22 10:53:41 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * trustdb.c (update_trust_record): Add new optional arg.
+
+ * import.c (import_keys): Add statistics output
+ * trustdb.c (update_trustdb): Ditto.
+ (insert_trustdb): Ditto.
+
+ * tdbio.c (tdbio_begin_transaction): New.
+ (tdbio_end_transaction): New.
+ (tdbio_cancel_transaction): New.
+
+ * g10.c (main): New option --quit.
+
+ * trustdb.c (check_hint_sig): No tests for user-id w/o sig.
+ This caused an assert while checking the sigs.
+
+ * trustdb.c (upd_sig_record): Splitted into several functions.
+
+ * import.c (import_keys): New arg "fast".
+ * g10.c (main): New command --fast-import.
+
+Wed Oct 21 18:19:36 1998 Michael Roth <mroth@nessie.de>
+
+ * ringedit.c (add_keyblock_resource): Directory is now created.
+ * tdbio.c (tdbio_set_dbname): New info message.
+
+Wed Oct 21 11:52:04 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * trustdb.c (update_trustdb): released keyblock in loop.
+
+ * keylist.c (list_block): New.
+ (list_all): Changed to use list_block.
+
+ * trustdb.c: Completed support for GDBM
+
+ * sign.c (only_old_style): Changed the way force_v3 is handled
+ (sign_file): Ditto.
+ (clearsign_file): Ditto.
+
+ * keygen.c (has_invalid_email_chars): Splitted into mailbox and
+ host part.
+
+ * keylist.c (list_one): Add a merge_keys_and_selfsig.
+ * mainproc.c (proc_tree): Ditto.
+
+Sun Oct 18 11:49:03 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * sign.c (only_old_style): Add option force_v3_sigs
+ (sign_file): Fixed a bug in sig->version
+ (clearsign_file): Ditto.
+
+ * parse-packet.c (dump_sig_subpkt): New
+
+ * keyedit.c (menu_expire): New.
+ * free-packet.c (cmp_signatures): New
+
+
+Sat Oct 17 10:22:39 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * armor.c: changed output line length from 72 to 64.
+
+ * keyedit.c (fix_keyblock): New.
+
+Fri Oct 16 10:24:47 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * trustdb.c: Rewrote most.
+ * tdbio.c: Add cache and generalized hash tables.
+
+ * options.h (ENABLE_COMMENT_PACKETS): New but undef'ed.
+ * encode.c, sign.c, keygen.c: Disabled comment packets.
+ * export.c (do_export): Comment packets are never exported,
+ except for those in the secret keyring.
+
+ * g10.c (main): Removed option do-no-export-rsa; should be
+ be replaced by a secpial tool.
+ * export.c (do_export): Removed the code for the above option.
+
+ * armor.c (find_header): Support for new only_keyblocks.
+ * import.c (import_keys): Only looks for keyblock armors.
+
+ * packet.h: replaced valid_days by expiredate and changed all users.
+ * build-packet.c (do_public_key): calculates valid-days
+ (do_secret_key): Ditto.
+ * parse-packet.c (parse_key): expiredate is calucated from the
+ valid_period in v3 packets.
+ * keyid.c (do_fingerprint_md): calculates valid_dates.
+
+ * keygen.c (add_key_expire): fixed key expiration time for v4 packets.
+
+ * armor.c (find_header): A LF in the first 28 bytes
+ was skipped for non-armored data.
+
+Thu Oct 8 11:35:51 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * armor.c (is_armored): Add test on old comment packets.
+
+ * tdbio.c (tdbio_search_dir_bypk): fixed memory leak.
+
+ * getkey.c: Changed the caching algorithms.
+
+Wed Oct 7 19:33:28 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * kbnodes.c (unused_nodes): New.
+
+Wed Oct 7 11:15:36 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * keyedit.c (sign_uids): Fixed a problem with SK which could caused
+ a save of an unprotected key.
+ (menu_adduid): Ditto.
+
+ * keyedit.c (keyedit_menu): Prefs are now correctly listed for
+ new user ids.
+
+ * trustdb.c (update_trust_record): New.
+ (insert_trust_record): Now makes use of update_trust_record.
+
+Tue Oct 6 16:18:03 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * trustdb.c (read_record): replaces most of the tdbio_read_records.
+ (write_record): Ditto.
+
+Sat Oct 3 11:01:21 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * keygen.c (ask_alogo): enable ElGamal enc-only only for addmode.
+
+Wed Sep 30 10:15:33 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * import.c (import_one): Fixed update of wrong keyblock.
+
+Tue Sep 29 08:32:08 1998 me,,, (wk@tobold)
+
+ * mainproc.c (proc_plaintext): Display note for special filename.
+ * plaintext.c (handle_plaintext): Suppress output of special file.
+
+Mon Sep 28 12:57:12 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c (verify_own_keys): Add warning if a key is not protected.
+
+ * passphrase (hash_passphrase): Fixed iterated+salted mode and
+ setup for keysizes > hashsize.
+
+ * g10.c (main): New options: --s2k-{cipher,digest,mode}.
+
+Fri Sep 25 09:34:23 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c: Chnaged some help texts.
+
+Tue Sep 22 19:34:39 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * passphrase.c (read_passphrase_from_fd): fixed bug for long
+ passphrases.
+
+Mon Sep 21 11:28:05 1998 Werner Koch (wk@(none))
+
+ * getkey.c (lookup): Add code to use the sub key if the primary one
+ does not match the usage.
+
+ * armor.c (armor_filter): New error message: no valid data found.
+ (radix64_read): Changes to support multiple messages.
+ (i18n.h): New.
+ * mainproc.c (add_onepass_sig): bug fix.
+
+Mon Sep 21 08:03:16 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * pkclist.c (do_we_trust): Add keyid to most messages.
+
+ * passphrase.c (read_passphrase_from_fd): New.
+ (have_static_passphrase): New
+ (get_passphrase_fd): Removed.
+ (set_passphrase_fd): Removed.
+ * g10.c (main): passphrase is now read here.
+
+ * keyedit.c (keyedit_menu): "help" texts should now translate fine.
+
+Mon Sep 21 06:40:02 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * encode.c (encode_simple): Now disables compression
+ when --rfc1991 is used.
+ (encode_crypt): Ditto.
+
+Fri Sep 18 16:50:32 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * getkey.c (merge_key_and_selfsig): New.
+
+Fri Sep 18 10:20:11 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * pkclist.c (select_algo_from_prefs): Removed 3DES kludge.
+
+ * seskey.c (make_session_key): Fixed SERIOUS bug introduced
+ by adding the weak key detection code.
+
+ * sign.c (sign_file): Changed aremor header in certain cases.
+
+Tue Sep 15 17:52:55 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * mainproc.c (check_sig_and_print): Replaced ascime by asctimestamp.
+
+Mon Sep 14 11:40:52 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * seskey.c (make_session_key): Now detects weak keys.
+
+ * trustdb (clear_trust_checked_flag): New.
+
+ * plaintext.c (handle_plaintext): Does no anymore suppress CR from
+ cleartext signed messages.
+
+Sun Sep 13 12:54:29 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * trustdb.c (insert_trust_record): Fixed a stupid bug in the free
+ liunked list loops.
+
+Sat Sep 12 15:49:16 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * status.c (remove_shmid): New.
+ (init_shm_comprocess): Now sets permission to the real uid.
+
+Wed Sep 9 11:15:03 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * packet.h (PKT_pubkey_enc): New flah throw_keyid, and add logic to
+ implement it.
+ * g10.c (main): New Option --throw-keyid
+
+ * getkey.c (enum_secret_keys): Add new ar and changed all callers.
+
+Tue Sep 8 20:04:09 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * delkey.c (delete_key): Moved from keyedit.c.
+
+Mon Sep 7 16:37:52 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * build-packet.c (calc_length_header): New arg new_ctb to correctly
+ calculate the length of new style packets.
+
+ * armor.c (is_armored): Checks for symkey_enc packets.
+
+ * pkclist.c (select_algo_from_prefs): 3DEs substitute is now CAST5.
+
+Tue Aug 11 17:54:50 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * build-packet.c (do_secret_key): Fixed handling of old keys.
+
+ * getkey.c (compare_name): Fixed exact and email matching
+
+ * openfile.c (open_outfile): Changed arguments and all callers.
+
+Tue Aug 11 09:14:35 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * encode.c (encode_simple): Applied option set-filename and comment.
+ (encode_crypt): Ditto.
+ * sign.c (sign_file): Ditto.
+ * armor.c (armor_filter): Applied option comment.
+
+ * encode.c (encode_crypt): Moved init_packet to the begin.
+ (encode_simple): add an init_packet().
+
+ * comment (write_comment): Now enforces a hash sign as the 1st byte.
+
+ * import.c (import_one): Add explanation for "no user ids".
+
+ * compress.c (do_uncompress): Applied Brian Warner's patch to support
+ zlib 1.1.3 etc.
+
+ * trustdb.c (check_trust): Fixed a problem after inserting new keys.
+
+ * getkey (lookup): do not return the primary key if usage is given
+ (lookup_sk): Ditto and take usage into account.
+
+ * status.c (cpr_get_answer_is_yes): add display_help.
+
+Mon Aug 10 10:11:28 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * getkey.c (lookup_sk): Now always returns the primary if arg
+ primary is true.
+ (lookup): Likewise.
+ (get_pubkey_byname): Now returns the primary key
+ (get_seckey_byname): Ditto.
+
+
+Mon Aug 10 08:34:03 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * keyid.c (pubkey_letter): ELG_E is now a small g.
+
+Sat Aug 8 17:26:12 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * openfile (overwrite_filep): Changed semantics and all callers.
+
+Sat Aug 8 12:17:07 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * status.c (display_help): New.
+
+Thu Aug 6 16:30:41 1998 Werner Koch,mobil,,, (wk@tobold)
+
+ * seskey.c (encode_session_key): Now uses get_random_bits().
+
+Thu Aug 6 07:34:56 1998 Werner Koch,mobil,,, (wk@tobold)
+
+ * ringedit.c (keyring_copy): No more backupfiles for
+ secret keyrings and add additional warning in case of
+ a failed secret keyring operation.
+
+Wed Aug 5 11:54:37 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c (check_opts): Moved to main. Changed def_cipher_algo
+ semantics and chnaged all users.
+
+ * pubkey-enc.c (get_sssion_key): New informational output
+ about preferences.
+
+ * parse-packet.c (parse_symkeyenc): Fixed salted+iterated S2K
+ (parse_key): Ditto.
+ * build-packet.c (do_secret_key): Ditto.
+ (do_symkey_enc): Ditto.
+
+Tue Aug 4 08:59:10 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * getkey.c (enum_secret_keys): Now returns only primary keys.
+
+ * getkey (lookup): Now sets the new namehash field.
+
+ * parse-packet.c (parse_sig_subpkt2): New.
+
+ * sign.c (sign_file): one-pass sigs are now emiited reverse.
+ Preference data is considered when selecting the compress algo.
+
+Wed Jul 29 12:53:03 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * free-packet.c (copy_signature): New.
+
+ * keygen.c (generate_subkeypair): rewritten
+ * g10.c (aKeyadd): Removed option --add-key
+
+Mon Jul 27 10:37:28 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * seckey-cert.c (do_check): Additional check on cipher blocksize.
+ (protect_secret_key): Ditto.
+ * encr-data.c: Support for other blocksizes.
+ * cipher.c (write_header): Ditto.
+
+Fri Jul 24 16:47:59 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * kbnode.c (insert_kbnode): Changed semantics and all callers.
+ * keyedit.c : More or less a complete rewrite
+
+Wed Jul 22 17:10:04 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * build-packet.c (write_sign_packet_header): New.
+
+Tue Jul 21 14:37:09 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * import.c (import_one): Now creates a trustdb record.
+
+ * g10.c (main): New command --check-trustdb
+
+Mon Jul 20 11:15:07 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * genkey.c (generate_keypair): Default key is now DSA with
+ encryption only ElGamal subkey.
+
+Thu Jul 16 10:58:33 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * keyid.c (keyid_from_fingerprint): New.
+ * getkey.c (get_pubkey_byfprint): New.
+
+Tue Jul 14 18:09:51 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * keyid.c (fingerprint_from_pk): Add argument and changed all callers.
+ (fingerprint_from_sk): Ditto.
+
+Tue Jul 14 10:10:03 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * plaintext.c (handle_plaintext): Now returns create error if
+ the file could not be created or the user responded not to overwrite
+ the file.
+ * mainproc.c (proc_plaintext): Tries again if the file could not
+ be created to check the signature without output.
+
+ * misc.c (disable_core_dumps): New.
+ * g10.c (main): disable coredumps for gpg
+
+ * g10.c (MAINTAINER_OPTIONS): New to disable some options
+
+Mon Jul 13 16:47:54 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * plaintext.c (hash_datafiles): New arg for better support of
+ detached sigs. Changed all callers.
+ * mainproc.c (proc_signature_packets): Ditto.
+
+ * g10.c (main): New option "compress-sigs"
+ * sig.c (sign_file): detached signatures are not anymore compressed
+ unless the option --compress-sigs is used.
+
+Thu Jul 9 19:54:54 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * armor.c: Fixes to allow zero length cleartext signatures
+
+Thu Jul 9 14:52:47 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c (build_list): Now drops setuid.
+ (main): Changed the way keyrings and algorithms are registered .
+
+Wed Jul 8 14:17:30 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * packet.h (PKT_public_key): Add field keyid.
+ * parse-packet.c (parse_key): Reset the above field.
+ * keyid.c (keyid_from_pk): Use above field as cache.
+
+ * tdbio.c, tdbio.h: New
+ * trustdb.c: Moved some functions to tdbio.c.
+ (print_keyid): New.
+
+ * pkclist.c (check_signatures_trust): New.
+
+Wed Jul 8 10:45:28 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * plaintext.c (special_md_putc): New.
+ (handle_plaintext): add clearsig argument
+ * mainproc.c (proc_plaintext): detection of clearsig
+ * sign.c (write_dased_escaped): Changed clearsig format
+
+Tue Jul 7 18:56:19 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * armor.c (find_header): Now makes sure that there is only one
+ empty line for clearsigs, as this is what OP now says.
+
+Mon Jul 6 13:09:07 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c (main): New option default-secret-key
+ * getkey.c (get_seckey_byname): support for this option.
+
+Mon Jul 6 09:03:49 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * getkey.c (add_keyring): Keyrings are now added to end of the
+ list of keyrings. The first added keyringwill be created.
+ (add_secret_keyring): Likewise.
+
+ * ringedit.c (add_keyblock_resource): Files are created here.
+
+ * g10.c (aNOP): Removed
+
+ * getkey.c (lookup): Add checking of usage for name lookups
+ * packet.h (pubkey_usage): Add a field which may be used to store
+ usage capabilities.
+ * pkclist.c (build_pk_list): getkey now called with usage arg.
+ * skclist.c (build_sk_list): Ditto.
+
+ * sign.c (clearsign_file): Fixed "Hash:" headers
+
+Sat Jul 4 13:33:31 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * trustdb.c (list_ownertrust): New.
+ * g10.c (aListOwnerTrust): New.
+
+ * g10.c (def_pubkey_algo): Removed.
+
+ * trustdb.c (verify_private_data): Removed and also the call to it.
+ (sign_private_data): Removed.
+
+Fri Jul 3 13:26:10 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c (aEditKey): was aEditSig. Changed usage msg.
+
+ * keyedit.c: Done some i18n stuff.
+
+ * g10.c (do_not_use_RSA): New.
+ * sign.c (do_sign): Add call to above function.
+ * encode.c (write_pubkey_enc_from_list): Ditto.
+
+Thu Jul 2 21:01:25 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * parse-packet.c: Now is able sto store data of unknown
+ algorithms.
+ * free-packet.c: Support for this.
+ * build-packet.c: Can write data of packet with unknown algos.
+
+Thu Jul 2 11:46:36 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * parse-packet.c (parse): fixed 4 byte length header
+
+Wed Jul 1 12:36:55 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * packet.h (new_ctb): New field for some packets
+ * build-packet.c (build_packet): Support for new_ctb
+ * parse-packet.c (parse): Ditto.
+
+Mon Jun 29 12:54:45 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * packet.h: changed all "_cert" to "_key", "subcert" to "subkey".
+
+ * free-packet.c (free_packet): Removed memory leak for subkeys.
+
+Sun Jun 28 18:32:27 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * import.c (import_keys): Renamed from import_pubkeys.
+ (import_secret_one): New.
+
+ * g10.c (aExportSecret): New.
+
+ * export.c (export_seckeys): New.
+
+ * parse-packet.c (parse_certificate): Cleaned up.
+ (parse_packet): Trust packets are now considered as unknown.
+ (parse_pubkey_warning): New.
+
+Fri Jun 26 10:37:35 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * keygen.c (has_invalid_email_chars): New.
+
+Wed Jun 24 16:40:22 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * armor.c (armor_filter): Now creates valid onepass_sig packets
+ with all detected hash algorithms.
+ * mainproc.c (proc_plaintext): Now uses the hash algos as specified
+ in the onepass_sig packets (if there are any)
+
+Mon Jun 22 11:54:08 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * plaintext.c (handle_plaintext): add arg to disable outout
+ * mainproc.c (proc_plaintext): disable output when in sigs_only mode.
+
+Thu Jun 18 13:17:27 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * keygen.c: Removed all rsa packet stuff, chnaged defaults
+ for key generation.
+
+Sun Jun 14 21:28:31 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * misc.c (checksum_u16): Fixed a stupid bug which caused a
+ wrong checksum calculation for the secret key protection and
+ add a backward compatibility option.
+ * g10.c (main): Add option --emulate-checksum-bug.
+
+Thu Jun 11 13:26:44 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * packet.h: Major changes to the structure of public key material
+ which is now stored in an array and not anaymore in a union of
+ algorithm specific structures. These is needed to make the system
+ more extendable and makes a lot of stuff much simpler. Changed
+ all over the system.
+
+ * dsa.c, rsa.c, elg.c: Removed.
+
+Wed Jun 10 07:22:02 1998 Werner Koch,mobil,,, (wk@tobold)
+
+ * g10.c ("load-extension"): New option.
+
+Mon Jun 8 22:23:37 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * seckey-cert.c (do_check): Removed cipher constants
+ (protect_secret_key): Ditto.
+
+Fri May 29 10:00:28 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * trustdb.c (query_trust_info): New.
+ * keylist.c (list_one): Add output of trust info
+ * mainproc (list_node): ditto.
+ * g10.c (main): full trustdb init if -with-colons and any of the
+ key list modes.
+
+Thu May 28 10:34:42 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * status.c (STATUS_RSA_OR_IDEA): New.
+ * sig-check.c (check_signature): Output special status message.
+ * pubkey-enc.c (get_session_key): Ditto.
+
+ * mainproc.c (check_sig_and_print): Changed format of output.
+ * passpharse.c (passphrase_to_dek): Likewise.
+
+Wed May 27 13:46:48 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c (aListSecretKeys): New option --list-secret-keys
+ * keylist.c (std_key_list): Renamed to public_key_list.
+ (secret_key_list): New
+ (list_one, list_all): Add support for secret keys.
+ * getkey.c (get_secret_keyring): New.
+ * mainproc.c (list_node): Add option --with-colons for secret keys
+
+ * sig-check.c (check_key_signature): detection of selfsigs
+ * mainproc.c (list_node): fixed listing.
+
+ * g10.c (aListSecretKeys): New option --always-trust
+ * pkclist.c (do_we_trust): Override per option added
+
+ * status.c (write_status_text): Add a prefix to every output line.
+
+Wed May 27 07:49:21 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10 (--compress-keys): New.
+ * options.h (compress_keys): New.
+ * export.c (export_pubkeys): Only compresses with the new option.
+
+Tue May 26 11:24:33 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * passphrase.c (get_last_passphrase): New
+ (set_next_passphrase): New.
+ (passphrase_to_dek): add support for the above functions.
+ * keyedit.c (make_keysig_packet): Add sigclass 0x18,
+ changed all callers due to a new argument.
+ * keygen.c (write_keybinding): New
+ (generate_subkeypair): Add functionality
+ (ask_algo, ask_keysize, ask_valid_days): Broke out of generate_keypair
+ (ask_user_id, ask_passphrase): Ditto.
+
+Thu May 21 11:26:13 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c,gpgd.c (main): Does now return an int, so that egcs does
+ not complain.
+
+ * armor.c (fake_packet): Removed erro message and add a noticed
+ that this part should be fixed.
+
+ * sign.c (sign_file): Compression now comes in front of encryption.
+ * encode.c (encode_simple): Ditto.
+ (encode_crypt): Ditto.
+
+Tue May 19 16:18:19 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * armor.c (fake_packet): Changed assertion to log_error
+
+Sat May 16 16:02:06 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * build-packet.c (build_packet): Add SUBKEY packets.
+
+Fri May 15 17:57:23 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * sign.c (hash_for): New and used in all places here.
+ * main.h (DEFAULT_): new macros.
+ * g10.c (opt.def_digest_algo): Now set to 0
+
+ * compress.c (init_compress): Add support for algo 1
+ * options.h (def_compress_algo): New
+ * g10.c (main): New option --compress-algo
+
+Fri May 15 13:23:59 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c (print_mds): New feature to print only one hash,
+ chnaged formatting.
+
+Thu May 14 15:36:24 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * misc.c (trap_unaligned) [__alpha__]: New
+ * g10.c (trap_unaligned): Add call to this to track down SIGBUS
+ on Alphas (to avoid the slow emulation code).
+
+Wed May 13 11:48:27 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * build-packet.c (do_signature): Support for v4 pakets.
+ * keyedit.c (make_keysig_packet): Ditto.
+ * build-packet.c (build_sig_subpkt_from_sig): New.
+ (build_sig_subpkt): New.
+
+ * elg.c (g10_elg_sign): removed keyid_from_skc.
+ * dsa.c (g10_dsa_sign): Ditto.
+ * rsa.c (g10_rsa_sign): Ditto.
+ * keyedit.c (make_keysig_packet): Add call to keyid_from_skc
+
+ * sign.c (clearsign_file): Support for v4 signatures.
+ (sign_file): Ditto.
+
+Wed May 6 09:31:24 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * parse-packet.c (do_parse): add support for 5 byte length leader.
+ (parse_subpkt): Ditto.
+ * build-packet.c (write_new_header): Ditto.
+
+ * packet.h (SIGSUBPKT_): New constants.
+ * parse-packet.c (parse_sig_subpkt): Changed name, made global,
+ and arg to return packet length, chnaged all callers
+
+
+Tue May 5 22:11:59 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * keygen.c (gen_dsa): New.
+ * build_packet.c (do_secret_cert): Support for DSA
+
+Mon May 4 19:01:25 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * compress.c: doubled buffer sizes
+ * parse-packet.c (do_plaintext): now uses iobuf_read/write.
+
+Mon May 4 09:35:53 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * seskey.c (encode_md_value): Add optional argument hash_algo,
+ changed all callers.
+
+ * passphrase.c (make_dek_from_passphrase): Removed
+ * (get_passhrase_hash): Changed name to passphrase_to_dek, add arg,
+ changed all callers.
+
+ * all: Introduced the new ELG identifier and added support for the
+ encryption only one (which is okay to use by GNUPG for signatures).
+
+Sun May 3 17:50:26 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * packet.h (PKT_OLD_COMMENT): New name for type 16.
+ * parse-packet.c (parse_comment): Now uses type 61
+
+Fri May 1 12:44:39 1998 Werner Koch,mobil,,, (wk@tobold)
+
+ * packet.h (count): Chnaged s2k count from byte to u32.
+ * seckey-cert.c (do_check): Changed s2k algo 3 to 4, changed
+ reading of count.
+ * build-packet.c (do_secret_cert): ditto.
+ * parse-packet.c (parse_certificate): ditto.
+
+ * parse-packet.c (parse_symkeyenc): New.
+ * build-packet.c (do_symkey_enc): New.
+
+Thu Apr 30 16:33:34 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * sign.c (clearsign_file): Fixed "Hash: " armor line.
+
+Tue Apr 28 14:27:42 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * parse-packet.c (parse_subpkt): Some new types.
+
+Mon Apr 27 12:53:59 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c (main): Add option --skip-verify.
+ * mainproc.c (check_sig_and_print): Ditto.
+
+ * g10.c (print_mds): Add output for Tiger.
+
+ * sign.c (sign_file): Now uses partial length headers if used
+ in canonical textmode (kludge to fix a bug).
+
+ * parse-packet.c (parse_certificate): Changed BLOWFISH id.
+ * pubkey-enc.c (get_session_key): Ditto.
+ * seskey.c (make_session_key): Ditto.
+ * seckey-cert.c (protect_secret_key,do_check): Add BLOWFISH160.
+
+Fri Apr 24 17:38:48 1998 Werner Koch,mobil,,, (wk@tobold)
+
+ * sig-check.c (check_key_signature): Add sig-class 0x14..0x17
+ * keyedit.c (sign-key): Some changes to start with support of
+ the above new sig-classes.
+
+Wed Apr 22 09:01:57 1998 Werner Koch,mobil,,, (wk@tobold)
+
+ * getkey.c (compare_name): add email matching
+
+Tue Apr 21 16:17:12 1998 Werner Koch,mobil,,, (wk@tobold)
+
+ * armor.c (armor_filter): fixed missing last LF before CSUM.
+
+Thu Apr 9 11:35:22 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * seckey-cert.c (do_check): New; combines all the check functions
+ into one.
+
+ * sign.c: removed all key management functions
+ * keyedit.c: New.
+
+Thu Apr 9 09:49:36 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * import.c (chk_self_sigs): Changed an error message.
+
+Wed Apr 8 16:19:39 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * packet.h: packet structs now uses structs from the pubkey,
+ removed all copy operations from packet to pubkey structs.
+
+Wed Apr 8 13:40:33 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * trustdb.c (verify_own_certs): Fixed "public key not found".
+
+ * getkey.c (key_byname): New, combines public and secret key search.
+
+ * pkclist.c (build_pkc_list): Add new arg usage, changed all callers.
+ * skclist.c (build_skc_list): Likewise.
+
+ * ringedit.c (find_keyblock, keyring_search2): Removed.
+
+Wed Apr 8 09:47:21 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * sig-check.c (do_check): Applied small fix from Ulf Möller.
+
+Tue Apr 7 19:28:07 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * cipher.c, encr-data.c, seckey-cert.c: Now uses cipher_xxxx
+ functions instead of blowfish_xxx or cast_xxx
+
+Tue Apr 7 11:04:02 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * Makefile.am (g10maint.o): Changed the way it is created.
+
+Mon Apr 6 11:17:08 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * misc.c: New.
+ * keygen.c (checksum,checksum_u16,checksum_mpi): Moved to misc.c
+ * seckey-cert.c: Kludge for wrong ELG checksum implementation.
+
+Sat Apr 4 20:07:01 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * cipher.c (cipher_filter): Support for CAST5
+ * encr-data.c (decode_filter): Ditto.
+ (decrypt_data): Ditto.
+ * seskey.c (make_session_key): Ditto.
+ * seckey-cert.c (check_elg, check_dsa): Ditto,
+ (protect_secret_key): Ditto.
+ * pubkey-enc.c (get_session_key): Ditto.
+ * passphrase.c (hash_passphrase): Ditto.
+
+Thu Apr 2 20:22:35 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * gpgd.c: New
+
+Thu Apr 2 10:38:16 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * keygen.c (generate_keypair): Add valid_days stuff.
+ * trustdb.c (check_trust): Add check for valid_days.
+
+Wed Apr 1 16:15:58 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * keygen.c (generate_keypair): Addional question whether the
+ selected large keysize is really needed.
+
+Wed Apr 1 15:56:33 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * seckey-cert.c (protect_secret_key): merged protect_xxx to here.
+
+Wed Apr 1 10:34:46 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * Makefile.am (g10maint.c): Changed creation rule, so that it works
+ on FreeBSD (missing CFLAGS).
+
+ * parse-packet.c (parse_subkey): Removed.
+
+Thu Mar 19 15:22:36 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * ringedit.c (keyring_enum): Fixed problem with reading too
+ many packets. Add support to read secret keyrings.
+
+ * getkey.c (scan_keyring): Removed
+ (lookup): New to replace scan_keyring.
+ (scan_secret_keyring): Removed.
+ (lookup_skc): New.
+
+Wed Mar 18 11:47:34 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * ringedit.c (enum_keyblocks): New read mode 11.
+
+ * keyid.c (elg_fingerprint_md): New and changed all other functions
+ to call this if the packet version is 4 or above.
+
+Tue Mar 17 20:46:16 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * parse-packet.c (parse_certificate): Add listing support for subkeys.
+
+Tue Mar 17 20:32:22 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * armor.c (is_armored): Allow marker packet.
+
+Thu Mar 12 13:36:49 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * trustdb.c (check_trust): Checks timestamp of pubkey.
+ * sig-check. (do_check): Compares timestamps.
+
+Tue Mar 10 17:01:56 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c (main): Add call to init_signals.
+ * signal.c: New.
+
+Mon Mar 9 12:43:42 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * dsa.c: New
+ * packet.h, free-packet.c, parse-packet.c : Add support for DSA
+ * sig-check.c, getkey.c, keyid.c, ringedit.c: Ditto.
+ * seckey-cert.c: Ditto.
+
+ * packet.h : Moved .digest_algo of signature packets to outer
+ structure. Changed all references
+
+Sun Mar 8 13:06:42 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * openfile.c : Support for stdout filename "-".
+
+ * mainproc.c (check_sig_and_print): Enhanced status output:
+ * status.c (write_status_text): New.
+
+Fri Mar 6 16:10:54 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * kbnode.c (clone_kbnode): Fixed private_flag.
+
+ * mainproc.c (list_node): Output of string "Revoked" as user-id.
+
+Fri Mar 6 14:26:39 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c (main): Add userids to "-kv" and cleaned up this stuff.
+
+Fri Mar 6 12:45:58 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c (main): Changed semantics of the list-... commands
+ and added a new one. Removed option "-d"
+
+ * decrypt.c: New.
+
+ * trustdb.c (init_trustdb): Autocreate directory only if it ends
+ in "/.gnupg".
+
+Thu Mar 5 12:12:11 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * mainproc.c (do_proc_packets): New. Common part of proc_packet.
+ (proc_signature_packets): special version to handle signature data.
+ * verify.c: New.
+ * g10.c (aVerify): New.
+ * plaintext.c (hash_datafiles): New.
+ * compress.c (handle_compressed): Add callback arg, changed caller.
+
+Thu Mar 5 10:20:06 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c: Is nom the common source for gpg and gpgm
+ * g10maint.c: Removed
+ * Makefile.am: Add rule to build g10maint.c
+
+Thu Mar 5 08:43:59 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c (main): Changed the way clear text sigs are faked.
+
+Wed Mar 4 19:47:37 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10maint.c (aMuttKeyList): New
+ * keylist.c: New.
+
+Wed Mar 4 17:20:33 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * getkey.c (get_pubkey_byname): Kludge to allow 0x prefix.
+
+Tue Mar 3 13:46:55 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10maint.c (main): New option --gen-random.
+
+Tue Mar 3 09:50:08 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c (aDeleteSecretKey): New.
+ (aEditSig): Add option "--edit-key" as synonym for "--edit-sig".
+ (aDeleteSecretKey): New.
+ * getkey.c (seckey_available): New.
+ * sign.c (delete_key): Enhanced to delete secret keys, changed all
+ callers.
+
+Mon Mar 2 21:23:48 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * pkc_list.c (build_pkc_list): Add interactive input of user ID.
+
+Mon Mar 2 20:54:05 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * pkclist.c (do_we_trust_pre): New.
+ (add_ownertrust): Add message.
+ * trustdb.c (enum_trust_web): Quick fix.
+
+Mon Mar 2 13:50:53 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c (main): New action aDeleteKey
+ * sign.c (delete_key): New.
+
+Sun Mar 1 16:38:58 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * trustdb.c (do_check): No returns TRUST_UNDEFINED instead of
+ eof error.
+
+Fri Feb 27 18:14:03 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * armor.c (find_header): Removed trailing CR on headers.
+
+Fri Feb 27 18:02:48 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * ringedit.c (keyring_search) [MINGW32]: Open and close file here
+ because rename does not work on open files. Chnaged callers.
+
+Fri Feb 27 16:43:11 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * sig-check.c (do_check): Add an md_enable.
+ * mainproc.c (do_check_sig): Use md_open in case of detached sig
+ (proc_tree): Take detached sigs into account.
+
+Fri Feb 27 15:22:46 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c (main): Make use of GNUPGHOME envvar.
+ * g10main.c (main): Ditto.
+
+Wed Feb 25 11:40:04 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * plaintext.c (ask_for_detached_datafile): add opt.verbose to
+ info output.
+
+ * openfile.c (open_sigfile): Try also name ending in ".asc"
+
+Wed Feb 25 08:41:00 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * keygen.c (generate_keypair): Fixed memory overflow.
+
+Tue Feb 24 15:51:55 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * parse-packet.c (parse_certificate): Support for S2K.
+ * build-packet.c (do_secret_cert): Ditto.
+ * keygen.c (gen_elg): Ditto.
+ * seckey-cert.c (check_elg): Ditto
+ (protect_elg): Ditto.
+ * sign.c (chnage_passphrase): Ditto.
+ * passphrase.c (get_passphrase_hash): Support for a salt and
+ changed all callers.
+ (make_dek_from_passphrase): Ditto.
+
+Tue Feb 24 12:30:56 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * build-packet.c (hash_public_cert): Disabled debug output.
+
+Fri Feb 20 17:22:28 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * trustdb.c (init_trustdb) [MINGW32]: Removed 2nd mkdir arg.
+ (keyring_copy) [MINGW32]: Add a remove prior to the renames.
+
+Wed Feb 18 18:39:02 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * Makefile.am (OMIT_DEPENDENCIES): New.
+
+ * rsa.c: Replaced log_bug by BUG.
+
+Wed Feb 18 13:35:58 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * mainproc.c (do_check_sig): Now uses hash_public_cert.
+ * parse-packet.c (parse_certificate): Removed hashing.
+ * packet.h (public_cert): Removed hash variable.
+ * free-packet.c (copy_public_cert, free_public_cert): Likewise.
+
+ * sig-check.c (check_key_signatures): Changed semantics.
+
+Wed Feb 18 12:11:28 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * trustdb.c (do_check): Add handling for revocation certificates.
+ (build_sigrecs): Ditto.
+ (check_sigs): Ditto.
+
+Wed Feb 18 09:31:04 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * armor.c (armor_filter): Add afx->hdrlines.
+ * revoke.c (gen_revoke): Add comment line.
+ * dearmor.c (enarmor_file): Ditto.
+
+ * sig-check.c (check_key_signature): Add handling for class 0x20.
+ * mainproc.c : Ditto.
+
+Tue Feb 17 21:24:17 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * armor.c : Add header lines "...ARMORED FILE .."
+ * dearmor.c (enarmor_file): New.
+ * g10maint.c (main): New option "--enarmor"
+
+Tue Feb 17 19:03:33 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * mainproc.c : Changed a lot, because the packets are now stored
+ a simple linlked list and not anymore in a complicatd tree structure.
+
+Tue Feb 17 10:14:48 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * free_packet.c (cmp_public_certs): New.
+ (cmp_user_ids): New.
+
+ * kbnode.c (clone_kbnode): New.
+ (release_kbnode): Add clone support.
+
+ * ringedit.c (find_keyblock_bypkc): New.
+
+ * sign.c (remove_keysigs): Self signatures are now skipped,
+ changed arguments and all callers.
+
+ * import.c : Add functionality.
+
+Tue Feb 17 09:31:40 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * options.h (homedir): New option.
+ * g10.c, g10maint.c, getkey.c, keygen.c, trustdb.c (opt.homedir): New.
+
+ * trustdb.c (init_trustdb): mkdir for hoem directory
+ (sign_private_data): Renamed "sig" to "g10.sig"
+
+Mon Feb 16 20:02:03 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * kbnode.c (commit_kbnode): New.
+ (delete_kbnode): removed unused first arg. Changed all Callers.
+
+ * ringedit.c (keyblock_resource_name): New.
+ (get_keyblock_handle): NULL for filename returns default resource.
+
+Mon Feb 16 19:38:48 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * sig-check.s (check_key_signature): Now uses the supplied
+ public key to check the signature and not any more the one
+ from the getkey.c
+ (do_check): New.
+ (check_signature): Most work moved to do_check.
+
+Mon Feb 16 14:48:57 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * armor.c (find_header): Fixed another bug.
+
+Mon Feb 16 12:18:34 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * getkey.c (scan_keyring): Add handling of compressed keyrings.
+
+Mon Feb 16 10:44:51 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c, g10maint.c (strusage): Rewrote.
+ (build_list): New
+
+Mon Feb 16 08:58:41 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * armor.c (use_armor): New.
+
+Sat Feb 14 14:30:57 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * mainproc.c (proc_tree): Sigclass fix.
+
+Sat Feb 14 14:16:33 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * armor.c (armor_filter): Changed version and comment string.
+ * encode.c, sign.c, keygen.c: Changed all comment packet strings.
+
+Sat Feb 14 12:39:24 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c (aGenRevoke): New command.
+ * revoke.c: New.
+ * sign.c (make_keysig_packet): Add support for sigclass 0x20.
+
+Fri Feb 13 20:18:14 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * ringedit.c (enum_keyblocks, keyring_enum): New.
+
+Fri Feb 13 19:33:40 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * export.c: Add functionality.
+
+ * keygen.c (generate_keypair): Moved the leading comment behind the
+ key packet.
+ * kbnode.c (walk_kbnode): Fixed.
+
+ * g10.c (main): listing armored keys now work.
+
+Fri Feb 13 16:17:43 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * parse-packet.c (parse_publickey, parse_signature): Fixed calls
+ to mpi_read used for ELG b.
+
+Fri Feb 13 15:13:23 1998 Werner Koch (wk@isil.d.shuttle.de)
+
+ * g10.c (main): changed formatting of help output.
+
+Thu Feb 12 22:24:42 1998 Werner Koch (wk@frodo)
+
+ * pubkey-enc.c (get_session_key): rewritten
+
+
+ Copyright 1998, 1999, 2000, 2001, 2002, 2003,
+ 2004, 2005, 2006 Free Software Foundation, Inc.
+
+ This file is free software; as a special exception the author gives
+ unlimited permission to copy and/or distribute it, with or without
+ modifications, as long as this notice is preserved.
+
+ This file is distributed in the hope that it will be useful, but
+ WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+ implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
diff --git a/g10/Makefile.am b/g10/Makefile.am
new file mode 100644
index 0000000..fa4e0b5
--- /dev/null
+++ b/g10/Makefile.am
@@ -0,0 +1,156 @@
+# Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005 Free Software Foundation, Inc.
+#
+# 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 2 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+## Process this file with automake to produce Makefile.in
+
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/intl @LIBUSB_CPPFLAGS@
+
+EXTRA_DIST = options.skel
+# it seems that we can't use this with automake 1.5
+#OMIT_DEPENDENCIES = zlib.h zconf.h
+
+if ! HAVE_DOSISH_SYSTEM
+AM_CPPFLAGS += -DGNUPG_LIBEXECDIR="\"$(libexecdir)/@PACKAGE@\""
+endif
+
+needed_libs = ../cipher/libcipher.a ../mpi/libmpi.a ../util/libutil.a
+other_libs = $(LIBICONV) $(DNSLIBS) $(LIBINTL) $(CAPLIBS)
+
+bin_PROGRAMS = gpg gpgv
+
+if ENABLE_BZIP2_SUPPORT
+bzip2_source = compress-bz2.c
+else
+bzip2_source =
+endif
+
+common_source = \
+ global.h \
+ build-packet.c \
+ compress.c \
+ $(bzip2_source) \
+ filter.h \
+ free-packet.c \
+ getkey.c \
+ keydb.c keydb.h \
+ keyring.c keyring.h \
+ seskey.c \
+ kbnode.c \
+ main.h \
+ mainproc.c \
+ armor.c \
+ mdfilter.c \
+ textfilter.c \
+ progress.c \
+ misc.c \
+ options.h \
+ openfile.c \
+ keyid.c \
+ packet.h \
+ parse-packet.c \
+ status.c \
+ status.h \
+ plaintext.c \
+ sig-check.c \
+ keylist.c \
+ signal.c
+
+if ENABLE_CARD_SUPPORT
+card_support_source_gpg = card-util.c
+card_support_source_scd = \
+ app-common.h \
+ app-openpgp.c \
+ iso7816.c iso7816.h \
+ apdu.c apdu.h \
+ ccid-driver.c ccid-driver.h
+card_support_source_local = cardglue.c cardglue.h tlv.c tlv.h
+else
+card_support_source_gpg =
+card_support_source_scd =
+card_support_source_local =
+endif
+
+card_support_source = $(card_support_source_local) $(card_support_source_gpg) $(card_support_source_scd)
+
+gpg_SOURCES = gpg.c \
+ $(common_source) \
+ $(card_support_source) \
+ pkclist.c \
+ skclist.c \
+ pubkey-enc.c \
+ passphrase.c \
+ seckey-cert.c \
+ encr-data.c \
+ cipher.c \
+ encode.c \
+ sign.c \
+ verify.c \
+ revoke.c \
+ decrypt.c \
+ keyedit.c \
+ dearmor.c \
+ import.c \
+ export.c \
+ trustdb.c \
+ trustdb.h \
+ tdbdump.c \
+ tdbio.c \
+ tdbio.h \
+ delkey.c \
+ keygen.c \
+ pipemode.c \
+ helptext.c \
+ keyserver.c \
+ keyserver-internal.h \
+ photoid.c photoid.h \
+ exec.c exec.h
+
+gpgv_SOURCES = gpgv.c \
+ $(common_source) \
+ verify.c
+
+LDADD = $(needed_libs) $(other_libs) @ZLIBS@ @W32LIBS@ @LIBREADLINE@
+gpg_LDADD = $(LDADD) @DLLIBS@ @NETLIBS@ @LIBUSB@
+
+$(PROGRAMS): $(needed_libs)
+
+install-data-local:
+ $(mkinstalldirs) $(DESTDIR)$(pkgdatadir)
+ $(INSTALL_DATA) $(srcdir)/options.skel \
+ $(DESTDIR)$(pkgdatadir)/options.skel
+ @set -e;\
+ if test -f $(DESTDIR)$(bindir)/gpgm ; then \
+ echo "removing obsolete gpgm binary" ; \
+ rm $(DESTDIR)$(bindir)/gpgm ; \
+ fi
+
+# Helper to update some source files.
+update-source-from-gnupg-2:
+ @set -e; \
+ if test -d ../../gnupg-1.9/scd; then dir="../../gnupg-1.9"; \
+ elif test -d ../../gnupg/scd; then dir="../../gnupg"; \
+ else exit 1; \
+ fi; \
+ for i in $(card_support_source_scd); do \
+ cp $$dir/scd/$$i $$i; echo $$i; \
+ done ;\
+ for i in $(card_support_source_gpg); do \
+ cp $$dir/g10/$$i $$i; echo $$i; \
+ done ; \
+ echo "Please remember to update the ChangeLog accordingly!"
diff --git a/g10/Makefile.in b/g10/Makefile.in
new file mode 100644
index 0000000..20d7899
--- /dev/null
+++ b/g10/Makefile.in
@@ -0,0 +1,801 @@
+# Makefile.in generated by automake 1.9.6 from Makefile.am.
+# @configure_input@
+
+# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002,
+# 2003, 2004, 2005 Free Software Foundation, Inc.
+# This Makefile.in is free software; the Free Software Foundation
+# gives unlimited permission to copy and/or distribute it,
+# with or without modifications, as long as this notice is preserved.
+
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY, to the extent permitted by law; without
+# even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+# PARTICULAR PURPOSE.
+
+@SET_MAKE@
+
+# Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+# 2005 Free Software Foundation, Inc.
+#
+# 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 2 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, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
+
+srcdir = @srcdir@
+top_srcdir = @top_srcdir@
+VPATH = @srcdir@
+pkgdatadir = $(datadir)/@PACKAGE@
+pkglibdir = $(libdir)/@PACKAGE@
+pkgincludedir = $(includedir)/@PACKAGE@
+top_builddir = ..
+am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd
+INSTALL = @INSTALL@
+install_sh_DATA = $(install_sh) -c -m 644
+install_sh_PROGRAM = $(install_sh) -c
+install_sh_SCRIPT = $(install_sh) -c
+INSTALL_HEADER = $(INSTALL_DATA)
+transform = $(program_transform_name)
+NORMAL_INSTALL = :
+PRE_INSTALL = :
+POST_INSTALL = :
+NORMAL_UNINSTALL = :
+PRE_UNINSTALL = :
+POST_UNINSTALL = :
+build_triplet = @build@
+host_triplet = @host@
+# it seems that we can't use this with automake 1.5
+#OMIT_DEPENDENCIES = zlib.h zconf.h
+@HAVE_DOSISH_SYSTEM_FALSE@am__append_1 = -DGNUPG_LIBEXECDIR="\"$(libexecdir)/@PACKAGE@\""
+bin_PROGRAMS = gpg$(EXEEXT) gpgv$(EXEEXT)
+subdir = g10
+DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in ChangeLog
+ACLOCAL_M4 = $(top_srcdir)/aclocal.m4
+am__aclocal_m4_deps = $(top_srcdir)/m4/autobuild.m4 \
+ $(top_srcdir)/m4/codeset.m4 $(top_srcdir)/m4/gettext.m4 \
+ $(top_srcdir)/m4/glibc21.m4 $(top_srcdir)/m4/iconv.m4 \
+ $(top_srcdir)/m4/intdiv0.m4 $(top_srcdir)/m4/intmax.m4 \
+ $(top_srcdir)/m4/inttypes-pri.m4 $(top_srcdir)/m4/inttypes.m4 \
+ $(top_srcdir)/m4/inttypes_h.m4 $(top_srcdir)/m4/isc-posix.m4 \
+ $(top_srcdir)/m4/lcmessage.m4 $(top_srcdir)/m4/ldap.m4 \
+ $(top_srcdir)/m4/lib-ld.m4 $(top_srcdir)/m4/lib-link.m4 \
+ $(top_srcdir)/m4/lib-prefix.m4 $(top_srcdir)/m4/libcurl.m4 \
+ $(top_srcdir)/m4/libusb.m4 $(top_srcdir)/m4/longdouble.m4 \
+ $(top_srcdir)/m4/longlong.m4 $(top_srcdir)/m4/nls.m4 \
+ $(top_srcdir)/m4/noexecstack.m4 $(top_srcdir)/m4/po.m4 \
+ $(top_srcdir)/m4/printf-posix.m4 $(top_srcdir)/m4/progtest.m4 \
+ $(top_srcdir)/m4/readline.m4 $(top_srcdir)/m4/signed.m4 \
+ $(top_srcdir)/m4/size_max.m4 $(top_srcdir)/m4/stdint_h.m4 \
+ $(top_srcdir)/m4/tar-ustar.m4 $(top_srcdir)/m4/uintmax_t.m4 \
+ $(top_srcdir)/m4/ulonglong.m4 $(top_srcdir)/m4/wchar_t.m4 \
+ $(top_srcdir)/m4/wint_t.m4 $(top_srcdir)/m4/xsize.m4 \
+ $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac
+am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \
+ $(ACLOCAL_M4)
+mkinstalldirs = $(SHELL) $(top_srcdir)/scripts/mkinstalldirs
+CONFIG_HEADER = $(top_builddir)/config.h
+CONFIG_CLEAN_FILES =
+am__installdirs = "$(DESTDIR)$(bindir)"
+binPROGRAMS_INSTALL = $(INSTALL_PROGRAM)
+PROGRAMS = $(bin_PROGRAMS)
+am__gpg_SOURCES_DIST = gpg.c global.h build-packet.c compress.c \
+ compress-bz2.c filter.h free-packet.c getkey.c keydb.c keydb.h \
+ keyring.c keyring.h seskey.c kbnode.c main.h mainproc.c \
+ armor.c mdfilter.c textfilter.c progress.c misc.c options.h \
+ openfile.c keyid.c packet.h parse-packet.c status.c status.h \
+ plaintext.c sig-check.c keylist.c signal.c cardglue.c \
+ cardglue.h tlv.c tlv.h card-util.c app-common.h app-openpgp.c \
+ iso7816.c iso7816.h apdu.c apdu.h ccid-driver.c ccid-driver.h \
+ pkclist.c skclist.c pubkey-enc.c passphrase.c seckey-cert.c \
+ encr-data.c cipher.c encode.c sign.c verify.c revoke.c \
+ decrypt.c keyedit.c dearmor.c import.c export.c trustdb.c \
+ trustdb.h tdbdump.c tdbio.c tdbio.h delkey.c keygen.c \
+ pipemode.c helptext.c keyserver.c keyserver-internal.h \
+ photoid.c photoid.h exec.c exec.h
+@ENABLE_BZIP2_SUPPORT_TRUE@am__objects_1 = compress-bz2.$(OBJEXT)
+am__objects_2 = build-packet.$(OBJEXT) compress.$(OBJEXT) \
+ $(am__objects_1) free-packet.$(OBJEXT) getkey.$(OBJEXT) \
+ keydb.$(OBJEXT) keyring.$(OBJEXT) seskey.$(OBJEXT) \
+ kbnode.$(OBJEXT) mainproc.$(OBJEXT) armor.$(OBJEXT) \
+ mdfilter.$(OBJEXT) textfilter.$(OBJEXT) progress.$(OBJEXT) \
+ misc.$(OBJEXT) openfile.$(OBJEXT) keyid.$(OBJEXT) \
+ parse-packet.$(OBJEXT) status.$(OBJEXT) plaintext.$(OBJEXT) \
+ sig-check.$(OBJEXT) keylist.$(OBJEXT) signal.$(OBJEXT)
+@ENABLE_CARD_SUPPORT_TRUE@am__objects_3 = cardglue.$(OBJEXT) \
+@ENABLE_CARD_SUPPORT_TRUE@ tlv.$(OBJEXT)
+@ENABLE_CARD_SUPPORT_TRUE@am__objects_4 = card-util.$(OBJEXT)
+@ENABLE_CARD_SUPPORT_TRUE@am__objects_5 = app-openpgp.$(OBJEXT) \
+@ENABLE_CARD_SUPPORT_TRUE@ iso7816.$(OBJEXT) apdu.$(OBJEXT) \
+@ENABLE_CARD_SUPPORT_TRUE@ ccid-driver.$(OBJEXT)
+am__objects_6 = $(am__objects_3) $(am__objects_4) $(am__objects_5)
+am_gpg_OBJECTS = gpg.$(OBJEXT) $(am__objects_2) $(am__objects_6) \
+ pkclist.$(OBJEXT) skclist.$(OBJEXT) pubkey-enc.$(OBJEXT) \
+ passphrase.$(OBJEXT) seckey-cert.$(OBJEXT) encr-data.$(OBJEXT) \
+ cipher.$(OBJEXT) encode.$(OBJEXT) sign.$(OBJEXT) \
+ verify.$(OBJEXT) revoke.$(OBJEXT) decrypt.$(OBJEXT) \
+ keyedit.$(OBJEXT) dearmor.$(OBJEXT) import.$(OBJEXT) \
+ export.$(OBJEXT) trustdb.$(OBJEXT) tdbdump.$(OBJEXT) \
+ tdbio.$(OBJEXT) delkey.$(OBJEXT) keygen.$(OBJEXT) \
+ pipemode.$(OBJEXT) helptext.$(OBJEXT) keyserver.$(OBJEXT) \
+ photoid.$(OBJEXT) exec.$(OBJEXT)
+gpg_OBJECTS = $(am_gpg_OBJECTS)
+am__DEPENDENCIES_1 = ../cipher/libcipher.a ../mpi/libmpi.a \
+ ../util/libutil.a
+am__DEPENDENCIES_2 =
+am__DEPENDENCIES_3 = $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_2) \
+ $(am__DEPENDENCIES_2) $(am__DEPENDENCIES_2)
+am__DEPENDENCIES_4 = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_3)
+gpg_DEPENDENCIES = $(am__DEPENDENCIES_4)
+am__gpgv_SOURCES_DIST = gpgv.c global.h build-packet.c compress.c \
+ compress-bz2.c filter.h free-packet.c getkey.c keydb.c keydb.h \
+ keyring.c keyring.h seskey.c kbnode.c main.h mainproc.c \
+ armor.c mdfilter.c textfilter.c progress.c misc.c options.h \
+ openfile.c keyid.c packet.h parse-packet.c status.c status.h \
+ plaintext.c sig-check.c keylist.c signal.c verify.c
+am_gpgv_OBJECTS = gpgv.$(OBJEXT) $(am__objects_2) verify.$(OBJEXT)
+gpgv_OBJECTS = $(am_gpgv_OBJECTS)
+gpgv_LDADD = $(LDADD)
+gpgv_DEPENDENCIES = $(am__DEPENDENCIES_1) $(am__DEPENDENCIES_3)
+DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir)
+depcomp = $(SHELL) $(top_srcdir)/scripts/depcomp
+am__depfiles_maybe = depfiles
+COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \
+ $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS)
+CCLD = $(CC)
+LINK = $(CCLD) $(AM_CFLAGS) $(CFLAGS) $(AM_LDFLAGS) $(LDFLAGS) -o $@
+SOURCES = $(gpg_SOURCES) $(gpgv_SOURCES)
+DIST_SOURCES = $(am__gpg_SOURCES_DIST) $(am__gpgv_SOURCES_DIST)
+ETAGS = etags
+CTAGS = ctags
+DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST)
+ACLOCAL = @ACLOCAL@
+ALLOCA = @ALLOCA@
+AMDEP_FALSE = @AMDEP_FALSE@
+AMDEP_TRUE = @AMDEP_TRUE@
+AMTAR = @AMTAR@
+AR = @AR@
+AUTOCONF = @AUTOCONF@
+AUTOHEADER = @AUTOHEADER@
+AUTOMAKE = @AUTOMAKE@
+AWK = @AWK@
+BUILD_INCLUDED_LIBINTL = @BUILD_INCLUDED_LIBINTL@
+CAPLIBS = @CAPLIBS@
+CATOBJEXT = @CATOBJEXT@
+CC = @CC@
+CCAS = @CCAS@
+CCASFLAGS = @CCASFLAGS@
+CCDEPMODE = @CCDEPMODE@
+CC_FOR_BUILD = @CC_FOR_BUILD@
+CFLAGS = @CFLAGS@
+CPP = @CPP@
+CPPFLAGS = @CPPFLAGS@
+CROSS_COMPILING_FALSE = @CROSS_COMPILING_FALSE@
+CROSS_COMPILING_TRUE = @CROSS_COMPILING_TRUE@
+CYGPATH_W = @CYGPATH_W@
+DATADIRNAME = @DATADIRNAME@
+DEFS = @DEFS@
+DEPDIR = @DEPDIR@
+DLLIBS = @DLLIBS@
+DNSLIBS = @DNSLIBS@
+DOCBOOK_TO_MAN = @DOCBOOK_TO_MAN@
+DOCBOOK_TO_TEXI = @DOCBOOK_TO_TEXI@
+ECHO_C = @ECHO_C@
+ECHO_N = @ECHO_N@
+ECHO_T = @ECHO_T@
+EGREP = @EGREP@
+ENABLE_AGENT_SUPPORT_FALSE = @ENABLE_AGENT_SUPPORT_FALSE@
+ENABLE_AGENT_SUPPORT_TRUE = @ENABLE_AGENT_SUPPORT_TRUE@
+ENABLE_BZIP2_SUPPORT_FALSE = @ENABLE_BZIP2_SUPPORT_FALSE@
+ENABLE_BZIP2_SUPPORT_TRUE = @ENABLE_BZIP2_SUPPORT_TRUE@
+ENABLE_CARD_SUPPORT_FALSE = @ENABLE_CARD_SUPPORT_FALSE@
+ENABLE_CARD_SUPPORT_TRUE = @ENABLE_CARD_SUPPORT_TRUE@
+ENABLE_LOCAL_ZLIB_FALSE = @ENABLE_LOCAL_ZLIB_FALSE@
+ENABLE_LOCAL_ZLIB_TRUE = @ENABLE_LOCAL_ZLIB_TRUE@
+EXEEXT = @EXEEXT@
+FAKE_CURL_FALSE = @FAKE_CURL_FALSE@
+FAKE_CURL_TRUE = @FAKE_CURL_TRUE@
+FAQPROG = @FAQPROG@
+GENCAT = @GENCAT@
+GETOPT = @GETOPT@
+GLIBC21 = @GLIBC21@
+GMSGFMT = @GMSGFMT@
+GPGKEYS_CURL = @GPGKEYS_CURL@
+GPGKEYS_FINGER = @GPGKEYS_FINGER@
+GPGKEYS_HKP = @GPGKEYS_HKP@
+GPGKEYS_LDAP = @GPGKEYS_LDAP@
+GPGKEYS_MAILTO = @GPGKEYS_MAILTO@
+GREP = @GREP@
+HAVE_ASPRINTF = @HAVE_ASPRINTF@
+HAVE_DOCBOOK_TO_MAN_FALSE = @HAVE_DOCBOOK_TO_MAN_FALSE@
+HAVE_DOCBOOK_TO_MAN_TRUE = @HAVE_DOCBOOK_TO_MAN_TRUE@
+HAVE_DOCBOOK_TO_TEXI_FALSE = @HAVE_DOCBOOK_TO_TEXI_FALSE@
+HAVE_DOCBOOK_TO_TEXI_TRUE = @HAVE_DOCBOOK_TO_TEXI_TRUE@
+HAVE_DOSISH_SYSTEM_FALSE = @HAVE_DOSISH_SYSTEM_FALSE@
+HAVE_DOSISH_SYSTEM_TRUE = @HAVE_DOSISH_SYSTEM_TRUE@
+HAVE_POSIX_PRINTF = @HAVE_POSIX_PRINTF@
+HAVE_SNPRINTF = @HAVE_SNPRINTF@
+HAVE_USTAR_FALSE = @HAVE_USTAR_FALSE@
+HAVE_USTAR_TRUE = @HAVE_USTAR_TRUE@
+HAVE_W32_SYSTEM_FALSE = @HAVE_W32_SYSTEM_FALSE@
+HAVE_W32_SYSTEM_TRUE = @HAVE_W32_SYSTEM_TRUE@
+HAVE_WPRINTF = @HAVE_WPRINTF@
+IDEA_O = @IDEA_O@
+INSTALL_DATA = @INSTALL_DATA@
+INSTALL_PROGRAM = @INSTALL_PROGRAM@
+INSTALL_SCRIPT = @INSTALL_SCRIPT@
+INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@
+INSTOBJEXT = @INSTOBJEXT@
+INTLBISON = @INTLBISON@
+INTLLIBS = @INTLLIBS@
+INTLOBJS = @INTLOBJS@
+INTL_LIBTOOL_SUFFIX_PREFIX = @INTL_LIBTOOL_SUFFIX_PREFIX@
+LDAPLIBS = @LDAPLIBS@
+LDAP_CPPFLAGS = @LDAP_CPPFLAGS@
+LDFLAGS = @LDFLAGS@
+LIBCURL = @LIBCURL@
+LIBCURL_CPPFLAGS = @LIBCURL_CPPFLAGS@
+LIBICONV = @LIBICONV@
+LIBINTL = @LIBINTL@
+LIBOBJS = @LIBOBJS@
+LIBREADLINE = @LIBREADLINE@
+LIBS = @LIBS@
+LIBUSB = @LIBUSB@
+LIBUSB_CPPFLAGS = @LIBUSB_CPPFLAGS@
+LTLIBICONV = @LTLIBICONV@
+LTLIBINTL = @LTLIBINTL@
+LTLIBOBJS = @LTLIBOBJS@
+MAINT = @MAINT@
+MAINTAINER_MODE_FALSE = @MAINTAINER_MODE_FALSE@
+MAINTAINER_MODE_TRUE = @MAINTAINER_MODE_TRUE@
+MAKEINFO = @MAKEINFO@
+MKINSTALLDIRS = @MKINSTALLDIRS@
+MPI_EXTRA_ASM_OBJS = @MPI_EXTRA_ASM_OBJS@
+MPI_OPT_FLAGS = @MPI_OPT_FLAGS@
+MPI_SFLAGS = @MPI_SFLAGS@
+MSGFMT = @MSGFMT@
+MSGMERGE = @MSGMERGE@
+NETLIBS = @NETLIBS@
+NM = @NM@
+NOEXECSTACK_FLAGS = @NOEXECSTACK_FLAGS@
+OBJEXT = @OBJEXT@
+PACKAGE = @PACKAGE@
+PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@
+PACKAGE_NAME = @PACKAGE_NAME@
+PACKAGE_STRING = @PACKAGE_STRING@
+PACKAGE_TARNAME = @PACKAGE_TARNAME@
+PACKAGE_VERSION = @PACKAGE_VERSION@
+PATH_SEPARATOR = @PATH_SEPARATOR@
+PERL = @PERL@
+POSUB = @POSUB@
+RANLIB = @RANLIB@
+SENDMAIL = @SENDMAIL@
+SET_MAKE = @SET_MAKE@
+SHELL = @SHELL@
+STRIP = @STRIP@
+TAR = @TAR@
+USE_DNS_SRV_FALSE = @USE_DNS_SRV_FALSE@
+USE_DNS_SRV_TRUE = @USE_DNS_SRV_TRUE@
+USE_INCLUDED_LIBINTL = @USE_INCLUDED_LIBINTL@
+USE_INTERNAL_REGEX_FALSE = @USE_INTERNAL_REGEX_FALSE@
+USE_INTERNAL_REGEX_TRUE = @USE_INTERNAL_REGEX_TRUE@
+USE_NLS = @USE_NLS@
+USE_RNDEGD_FALSE = @USE_RNDEGD_FALSE@
+USE_RNDEGD_TRUE = @USE_RNDEGD_TRUE@
+USE_RNDLINUX_FALSE = @USE_RNDLINUX_FALSE@
+USE_RNDLINUX_TRUE = @USE_RNDLINUX_TRUE@
+USE_RNDUNIX_FALSE = @USE_RNDUNIX_FALSE@
+USE_RNDUNIX_TRUE = @USE_RNDUNIX_TRUE@
+USE_RNDW32_FALSE = @USE_RNDW32_FALSE@
+USE_RNDW32_TRUE = @USE_RNDW32_TRUE@
+USE_SHA512_FALSE = @USE_SHA512_FALSE@
+USE_SHA512_TRUE = @USE_SHA512_TRUE@
+USE_SIMPLE_GETTEXT_FALSE = @USE_SIMPLE_GETTEXT_FALSE@
+USE_SIMPLE_GETTEXT_TRUE = @USE_SIMPLE_GETTEXT_TRUE@
+VERSION = @VERSION@
+W32LIBS = @W32LIBS@
+WORKING_FAQPROG_FALSE = @WORKING_FAQPROG_FALSE@
+WORKING_FAQPROG_TRUE = @WORKING_FAQPROG_TRUE@
+XGETTEXT = @XGETTEXT@
+ZLIBS = @ZLIBS@
+_libcurl_config = @_libcurl_config@
+_usb_config = @_usb_config@
+ac_ct_CC = @ac_ct_CC@
+am__fastdepCC_FALSE = @am__fastdepCC_FALSE@
+am__fastdepCC_TRUE = @am__fastdepCC_TRUE@
+am__include = @am__include@
+am__leading_dot = @am__leading_dot@
+am__quote = @am__quote@
+am__tar = @am__tar@
+am__untar = @am__untar@
+bindir = @bindir@
+build = @build@
+build_alias = @build_alias@
+build_cpu = @build_cpu@
+build_os = @build_os@
+build_vendor = @build_vendor@
+datadir = @datadir@
+datarootdir = @datarootdir@
+docdir = @docdir@
+dvidir = @dvidir@
+exec_prefix = @exec_prefix@
+host = @host@
+host_alias = @host_alias@
+host_cpu = @host_cpu@
+host_os = @host_os@
+host_vendor = @host_vendor@
+htmldir = @htmldir@
+includedir = @includedir@
+infodir = @infodir@
+install_sh = @install_sh@
+libdir = @libdir@
+libexecdir = @libexecdir@
+localedir = @localedir@
+localstatedir = @localstatedir@
+mandir = @mandir@
+mkdir_p = @mkdir_p@
+oldincludedir = @oldincludedir@
+pdfdir = @pdfdir@
+prefix = @prefix@
+program_transform_name = @program_transform_name@
+psdir = @psdir@
+sbindir = @sbindir@
+sharedstatedir = @sharedstatedir@
+sysconfdir = @sysconfdir@
+target_alias = @target_alias@
+AM_CPPFLAGS = -I$(top_srcdir)/include -I$(top_srcdir)/intl \
+ @LIBUSB_CPPFLAGS@ $(am__append_1)
+EXTRA_DIST = options.skel
+needed_libs = ../cipher/libcipher.a ../mpi/libmpi.a ../util/libutil.a
+other_libs = $(LIBICONV) $(DNSLIBS) $(LIBINTL) $(CAPLIBS)
+@ENABLE_BZIP2_SUPPORT_FALSE@bzip2_source =
+@ENABLE_BZIP2_SUPPORT_TRUE@bzip2_source = compress-bz2.c
+common_source = \
+ global.h \
+ build-packet.c \
+ compress.c \
+ $(bzip2_source) \
+ filter.h \
+ free-packet.c \
+ getkey.c \
+ keydb.c keydb.h \
+ keyring.c keyring.h \
+ seskey.c \
+ kbnode.c \
+ main.h \
+ mainproc.c \
+ armor.c \
+ mdfilter.c \
+ textfilter.c \
+ progress.c \
+ misc.c \
+ options.h \
+ openfile.c \
+ keyid.c \
+ packet.h \
+ parse-packet.c \
+ status.c \
+ status.h \
+ plaintext.c \
+ sig-check.c \
+ keylist.c \
+ signal.c
+
+@ENABLE_CARD_SUPPORT_FALSE@card_support_source_gpg =
+@ENABLE_CARD_SUPPORT_TRUE@card_support_source_gpg = card-util.c
+@ENABLE_CARD_SUPPORT_FALSE@card_support_source_scd =
+@ENABLE_CARD_SUPPORT_TRUE@card_support_source_scd = \
+@ENABLE_CARD_SUPPORT_TRUE@ app-common.h \
+@ENABLE_CARD_SUPPORT_TRUE@ app-openpgp.c \
+@ENABLE_CARD_SUPPORT_TRUE@ iso7816.c iso7816.h \
+@ENABLE_CARD_SUPPORT_TRUE@ apdu.c apdu.h \
+@ENABLE_CARD_SUPPORT_TRUE@ ccid-driver.c ccid-driver.h
+
+@ENABLE_CARD_SUPPORT_FALSE@card_support_source_local =
+@ENABLE_CARD_SUPPORT_TRUE@card_support_source_local = cardglue.c cardglue.h tlv.c tlv.h
+card_support_source = $(card_support_source_local) $(card_support_source_gpg) $(card_support_source_scd)
+gpg_SOURCES = gpg.c \
+ $(common_source) \
+ $(card_support_source) \
+ pkclist.c \
+ skclist.c \
+ pubkey-enc.c \
+ passphrase.c \
+ seckey-cert.c \
+ encr-data.c \
+ cipher.c \
+ encode.c \
+ sign.c \
+ verify.c \
+ revoke.c \
+ decrypt.c \
+ keyedit.c \
+ dearmor.c \
+ import.c \
+ export.c \
+ trustdb.c \
+ trustdb.h \
+ tdbdump.c \
+ tdbio.c \
+ tdbio.h \
+ delkey.c \
+ keygen.c \
+ pipemode.c \
+ helptext.c \
+ keyserver.c \
+ keyserver-internal.h \
+ photoid.c photoid.h \
+ exec.c exec.h
+
+gpgv_SOURCES = gpgv.c \
+ $(common_source) \
+ verify.c
+
+LDADD = $(needed_libs) $(other_libs) @ZLIBS@ @W32LIBS@ @LIBREADLINE@
+gpg_LDADD = $(LDADD) @DLLIBS@ @NETLIBS@ @LIBUSB@
+all: all-am
+
+.SUFFIXES:
+.SUFFIXES: .c .o .obj
+$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.am $(am__configure_deps)
+ @for dep in $?; do \
+ case '$(am__configure_deps)' in \
+ *$$dep*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh \
+ && exit 0; \
+ exit 1;; \
+ esac; \
+ done; \
+ echo ' cd $(top_srcdir) && $(AUTOMAKE) --gnu g10/Makefile'; \
+ cd $(top_srcdir) && \
+ $(AUTOMAKE) --gnu g10/Makefile
+.PRECIOUS: Makefile
+Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status
+ @case '$?' in \
+ *config.status*) \
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \
+ *) \
+ echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \
+ cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \
+ esac;
+
+$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+
+$(top_srcdir)/configure: @MAINTAINER_MODE_TRUE@ $(am__configure_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+$(ACLOCAL_M4): @MAINTAINER_MODE_TRUE@ $(am__aclocal_m4_deps)
+ cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh
+install-binPROGRAMS: $(bin_PROGRAMS)
+ @$(NORMAL_INSTALL)
+ test -z "$(bindir)" || $(mkdir_p) "$(DESTDIR)$(bindir)"
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ p1=`echo $$p|sed 's/$(EXEEXT)$$//'`; \
+ if test -f $$p \
+ ; then \
+ f=`echo "$$p1" | sed 's,^.*/,,;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) '$$p' '$(DESTDIR)$(bindir)/$$f'"; \
+ $(INSTALL_PROGRAM_ENV) $(binPROGRAMS_INSTALL) "$$p" "$(DESTDIR)$(bindir)/$$f" || exit 1; \
+ else :; fi; \
+ done
+
+uninstall-binPROGRAMS:
+ @$(NORMAL_UNINSTALL)
+ @list='$(bin_PROGRAMS)'; for p in $$list; do \
+ f=`echo "$$p" | sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ echo " rm -f '$(DESTDIR)$(bindir)/$$f'"; \
+ rm -f "$(DESTDIR)$(bindir)/$$f"; \
+ done
+
+clean-binPROGRAMS:
+ -test -z "$(bin_PROGRAMS)" || rm -f $(bin_PROGRAMS)
+
+installcheck-binPROGRAMS: $(bin_PROGRAMS)
+ bad=0; pid=$$$$; list="$(bin_PROGRAMS)"; for p in $$list; do \
+ case ' $(AM_INSTALLCHECK_STD_OPTIONS_EXEMPT) ' in \
+ *" $$p "* | *" $(srcdir)/$$p "*) continue;; \
+ esac; \
+ f=`echo "$$p" | \
+ sed 's,^.*/,,;s/$(EXEEXT)$$//;$(transform);s/$$/$(EXEEXT)/'`; \
+ for opt in --help --version; do \
+ if "$(DESTDIR)$(bindir)/$$f" $$opt >c$${pid}_.out \
+ 2>c$${pid}_.err </dev/null \
+ && test -n "`cat c$${pid}_.out`" \
+ && test -z "`cat c$${pid}_.err`"; then :; \
+ else echo "$$f does not support $$opt" 1>&2; bad=1; fi; \
+ done; \
+ done; rm -f c$${pid}_.???; exit $$bad
+gpg$(EXEEXT): $(gpg_OBJECTS) $(gpg_DEPENDENCIES)
+ @rm -f gpg$(EXEEXT)
+ $(LINK) $(gpg_LDFLAGS) $(gpg_OBJECTS) $(gpg_LDADD) $(LIBS)
+gpgv$(EXEEXT): $(gpgv_OBJECTS) $(gpgv_DEPENDENCIES)
+ @rm -f gpgv$(EXEEXT)
+ $(LINK) $(gpgv_LDFLAGS) $(gpgv_OBJECTS) $(gpgv_LDADD) $(LIBS)
+
+mostlyclean-compile:
+ -rm -f *.$(OBJEXT)
+
+distclean-compile:
+ -rm -f *.tab.c
+
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apdu.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/app-openpgp.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/armor.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/build-packet.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/card-util.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cardglue.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/ccid-driver.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/cipher.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compress-bz2.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/compress.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dearmor.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/decrypt.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/delkey.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encode.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encr-data.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/exec.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/export.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/free-packet.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/getkey.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpg.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/gpgv.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/helptext.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/import.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/iso7816.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/kbnode.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keydb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keyedit.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keygen.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keyid.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keylist.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keyring.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/keyserver.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mainproc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mdfilter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/misc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/openfile.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/parse-packet.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/passphrase.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/photoid.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pipemode.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pkclist.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/plaintext.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/progress.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/pubkey-enc.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/revoke.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/seckey-cert.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/seskey.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sig-check.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sign.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/signal.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/skclist.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/status.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tdbdump.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tdbio.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/textfilter.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tlv.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/trustdb.Po@am__quote@
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/verify.Po@am__quote@
+
+.c.o:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ $<; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c $<
+
+.c.obj:
+@am__fastdepCC_TRUE@ if $(COMPILE) -MT $@ -MD -MP -MF "$(DEPDIR)/$*.Tpo" -c -o $@ `$(CYGPATH_W) '$<'`; \
+@am__fastdepCC_TRUE@ then mv -f "$(DEPDIR)/$*.Tpo" "$(DEPDIR)/$*.Po"; else rm -f "$(DEPDIR)/$*.Tpo"; exit 1; fi
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepCC_FALSE@ DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepCC_FALSE@ $(COMPILE) -c `$(CYGPATH_W) '$<'`
+uninstall-info-am:
+
+ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES)
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ mkid -fID $$unique
+tags: TAGS
+
+TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ if test -z "$(ETAGS_ARGS)$$tags$$unique"; then :; else \
+ test -n "$$unique" || unique=$$empty_fix; \
+ $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \
+ $$tags $$unique; \
+ fi
+ctags: CTAGS
+CTAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \
+ $(TAGS_FILES) $(LISP)
+ tags=; \
+ here=`pwd`; \
+ list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \
+ unique=`for i in $$list; do \
+ if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \
+ done | \
+ $(AWK) ' { files[$$0] = 1; } \
+ END { for (i in files) print i; }'`; \
+ test -z "$(CTAGS_ARGS)$$tags$$unique" \
+ || $(CTAGS) $(CTAGSFLAGS) $(AM_CTAGSFLAGS) $(CTAGS_ARGS) \
+ $$tags $$unique
+
+GTAGS:
+ here=`$(am__cd) $(top_builddir) && pwd` \
+ && cd $(top_srcdir) \
+ && gtags -i $(GTAGS_ARGS) $$here
+
+distclean-tags:
+ -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH tags
+
+distdir: $(DISTFILES)
+ @srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; \
+ topsrcdirstrip=`echo "$(top_srcdir)" | sed 's|.|.|g'`; \
+ list='$(DISTFILES)'; for file in $$list; do \
+ case $$file in \
+ $(srcdir)/*) file=`echo "$$file" | sed "s|^$$srcdirstrip/||"`;; \
+ $(top_srcdir)/*) file=`echo "$$file" | sed "s|^$$topsrcdirstrip/|$(top_builddir)/|"`;; \
+ esac; \
+ if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \
+ dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \
+ if test "$$dir" != "$$file" && test "$$dir" != "."; then \
+ dir="/$$dir"; \
+ $(mkdir_p) "$(distdir)$$dir"; \
+ else \
+ dir=''; \
+ fi; \
+ if test -d $$d/$$file; then \
+ if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \
+ cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \
+ fi; \
+ cp -pR $$d/$$file $(distdir)$$dir || exit 1; \
+ else \
+ test -f $(distdir)/$$file \
+ || cp -p $$d/$$file $(distdir)/$$file \
+ || exit 1; \
+ fi; \
+ done
+check-am: all-am
+check: check-am
+all-am: Makefile $(PROGRAMS)
+installdirs:
+ for dir in "$(DESTDIR)$(bindir)"; do \
+ test -z "$$dir" || $(mkdir_p) "$$dir"; \
+ done
+install: install-am
+install-exec: install-exec-am
+install-data: install-data-am
+uninstall: uninstall-am
+
+install-am: all-am
+ @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am
+
+installcheck: installcheck-am
+install-strip:
+ $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \
+ install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \
+ `test -z '$(STRIP)' || \
+ echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install
+mostlyclean-generic:
+
+clean-generic:
+
+distclean-generic:
+ -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES)
+
+maintainer-clean-generic:
+ @echo "This command is intended for maintainers to use"
+ @echo "it deletes files that may require special tools to rebuild."
+clean: clean-am
+
+clean-am: clean-binPROGRAMS clean-generic mostlyclean-am
+
+distclean: distclean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+distclean-am: clean-am distclean-compile distclean-generic \
+ distclean-tags
+
+dvi: dvi-am
+
+dvi-am:
+
+html: html-am
+
+info: info-am
+
+info-am:
+
+install-data-am: install-data-local
+
+install-exec-am: install-binPROGRAMS
+
+install-info: install-info-am
+
+install-man:
+
+installcheck-am: installcheck-binPROGRAMS
+
+maintainer-clean: maintainer-clean-am
+ -rm -rf ./$(DEPDIR)
+ -rm -f Makefile
+maintainer-clean-am: distclean-am maintainer-clean-generic
+
+mostlyclean: mostlyclean-am
+
+mostlyclean-am: mostlyclean-compile mostlyclean-generic
+
+pdf: pdf-am
+
+pdf-am:
+
+ps: ps-am
+
+ps-am:
+
+uninstall-am: uninstall-binPROGRAMS uninstall-info-am
+
+.PHONY: CTAGS GTAGS all all-am check check-am clean clean-binPROGRAMS \
+ clean-generic ctags distclean distclean-compile \
+ distclean-generic distclean-tags distdir dvi dvi-am html \
+ html-am info info-am install install-am install-binPROGRAMS \
+ install-data install-data-am install-data-local install-exec \
+ install-exec-am install-info install-info-am install-man \
+ install-strip installcheck installcheck-am \
+ installcheck-binPROGRAMS installdirs maintainer-clean \
+ maintainer-clean-generic mostlyclean mostlyclean-compile \
+ mostlyclean-generic pdf pdf-am ps ps-am tags uninstall \
+ uninstall-am uninstall-binPROGRAMS uninstall-info-am
+
+
+$(PROGRAMS): $(needed_libs)
+
+install-data-local:
+ $(mkinstalldirs) $(DESTDIR)$(pkgdatadir)
+ $(INSTALL_DATA) $(srcdir)/options.skel \
+ $(DESTDIR)$(pkgdatadir)/options.skel
+ @set -e;\
+ if test -f $(DESTDIR)$(bindir)/gpgm ; then \
+ echo "removing obsolete gpgm binary" ; \
+ rm $(DESTDIR)$(bindir)/gpgm ; \
+ fi
+
+# Helper to update some source files.
+update-source-from-gnupg-2:
+ @set -e; \
+ if test -d ../../gnupg-1.9/scd; then dir="../../gnupg-1.9"; \
+ elif test -d ../../gnupg/scd; then dir="../../gnupg"; \
+ else exit 1; \
+ fi; \
+ for i in $(card_support_source_scd); do \
+ cp $$dir/scd/$$i $$i; echo $$i; \
+ done ;\
+ for i in $(card_support_source_gpg); do \
+ cp $$dir/g10/$$i $$i; echo $$i; \
+ done ; \
+ echo "Please remember to update the ChangeLog accordingly!"
+# Tell versions [3.59,3.63) of GNU make to not export all variables.
+# Otherwise a system limit (for SysV at least) may be exceeded.
+.NOEXPORT:
diff --git a/g10/apdu.c b/g10/apdu.c
new file mode 100644
index 0000000..5df9ebf
--- /dev/null
+++ b/g10/apdu.c
@@ -0,0 +1,3013 @@
+/* apdu.c - ISO 7816 APDU functions and low level I/O
+ * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ * $Id: apdu.c 4034 2006-03-05 15:13:18Z wk $
+ */
+
+/* NOTE: This module is also used by other software, thus the use of
+ the macro USE_GNU_PTH is mandatory. For GnuPG this macro is
+ guaranteed to be defined true. */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <signal.h>
+#ifdef USE_GNU_PTH
+# include <pth.h>
+# include <unistd.h>
+# include <fcntl.h>
+#endif
+
+
+/* If requested include the definitions for the remote APDU protocol
+ code. */
+#ifdef USE_G10CODE_RAPDU
+#include "rapdu.h"
+#endif /*USE_G10CODE_RAPDU*/
+
+#if defined(GNUPG_SCD_MAIN_HEADER)
+#include GNUPG_SCD_MAIN_HEADER
+#elif GNUPG_MAJOR_VERSION == 1
+/* This is used with GnuPG version < 1.9. The code has been source
+ copied from the current GnuPG >= 1.9 and is maintained over
+ there. */
+#include "options.h"
+#include "errors.h"
+#include "memory.h"
+#include "util.h"
+#include "i18n.h"
+#include "cardglue.h"
+#else /* GNUPG_MAJOR_VERSION != 1 */
+#include "scdaemon.h"
+#endif /* GNUPG_MAJOR_VERSION != 1 */
+
+#include "apdu.h"
+#include "dynload.h"
+#include "ccid-driver.h"
+
+
+/* Due to conflicting use of threading libraries we usually can't link
+ against libpcsclite. Instead we use a wrapper program. */
+#ifdef USE_GNU_PTH
+#if !defined(HAVE_W32_SYSTEM) && !defined(__CYGWIN__)
+#define NEED_PCSC_WRAPPER 1
+#endif
+#endif
+
+
+#define MAX_READER 4 /* Number of readers we support concurrently. */
+
+
+#if defined(_WIN32) || defined(__CYGWIN__)
+#define DLSTDCALL __stdcall
+#else
+#define DLSTDCALL
+#endif
+
+#ifdef _POSIX_OPEN_MAX
+#define MAX_OPEN_FDS _POSIX_OPEN_MAX
+#else
+#define MAX_OPEN_FDS 20
+#endif
+
+/* Helper to pass patrameters related to keypad based operations. */
+struct pininfo_s
+{
+ int mode;
+ int minlen;
+ int maxlen;
+ int padlen;
+};
+
+/* A structure to collect information pertaining to one reader
+ slot. */
+struct reader_table_s {
+ int used; /* True if slot is used. */
+ unsigned short port; /* Port number: 0 = unused, 1 - dev/tty */
+
+ /* Function pointers intialized to the various backends. */
+ int (*close_reader)(int);
+ int (*shutdown_reader)(int);
+ int (*reset_reader)(int);
+ int (*get_status_reader)(int, unsigned int *);
+ int (*send_apdu_reader)(int,unsigned char *,size_t,
+ unsigned char *, size_t *, struct pininfo_s *);
+ int (*check_keypad)(int, int, int, int, int, int);
+ void (*dump_status_reader)(int);
+
+ struct {
+ ccid_driver_t handle;
+ } ccid;
+ struct {
+ unsigned long context;
+ unsigned long card;
+ unsigned long protocol;
+#ifdef NEED_PCSC_WRAPPER
+ int req_fd;
+ int rsp_fd;
+ pid_t pid;
+#endif /*NEED_PCSC_WRAPPER*/
+ } pcsc;
+#ifdef USE_G10CODE_RAPDU
+ struct {
+ rapdu_t handle;
+ } rapdu;
+#endif /*USE_G10CODE_RAPDU*/
+ char *rdrname; /* Name of the connected reader or NULL if unknown. */
+ int last_status;
+ int status;
+ int is_t0; /* True if we know that we are running T=0. */
+ unsigned char atr[33];
+ size_t atrlen; /* A zero length indicates that the ATR has
+ not yet been read; i.e. the card is not
+ ready for use. */
+ unsigned int change_counter;
+#ifdef USE_GNU_PTH
+ int lock_initialized;
+ pth_mutex_t lock;
+#endif
+};
+typedef struct reader_table_s *reader_table_t;
+
+/* A global table to keep track of active readers. */
+static struct reader_table_s reader_table[MAX_READER];
+
+
+/* ct API function pointer. */
+static char (* DLSTDCALL CT_init) (unsigned short ctn, unsigned short Pn);
+static char (* DLSTDCALL CT_data) (unsigned short ctn, unsigned char *dad,
+ unsigned char *sad, unsigned short lc,
+ unsigned char *cmd, unsigned short *lr,
+ unsigned char *rsp);
+static char (* DLSTDCALL CT_close) (unsigned short ctn);
+
+/* PC/SC constants and function pointer. */
+#define PCSC_SCOPE_USER 0
+#define PCSC_SCOPE_TERMINAL 1
+#define PCSC_SCOPE_SYSTEM 2
+#define PCSC_SCOPE_GLOBAL 3
+
+#define PCSC_PROTOCOL_T0 1
+#define PCSC_PROTOCOL_T1 2
+#define PCSC_PROTOCOL_RAW 4
+
+#define PCSC_SHARE_EXCLUSIVE 1
+#define PCSC_SHARE_SHARED 2
+#define PCSC_SHARE_DIRECT 3
+
+#define PCSC_LEAVE_CARD 0
+#define PCSC_RESET_CARD 1
+#define PCSC_UNPOWER_CARD 2
+#define PCSC_EJECT_CARD 3
+
+#define PCSC_UNKNOWN 0x0001
+#define PCSC_ABSENT 0x0002 /* Card is absent. */
+#define PCSC_PRESENT 0x0004 /* Card is present. */
+#define PCSC_SWALLOWED 0x0008 /* Card is present and electrical connected. */
+#define PCSC_POWERED 0x0010 /* Card is powered. */
+#define PCSC_NEGOTIABLE 0x0020 /* Card is awaiting PTS. */
+#define PCSC_SPECIFIC 0x0040 /* Card is ready for use. */
+
+#define PCSC_STATE_UNAWARE 0x0000 /* Want status. */
+#define PCSC_STATE_IGNORE 0x0001 /* Ignore this reader. */
+#define PCSC_STATE_CHANGED 0x0002 /* State has changed. */
+#define PCSC_STATE_UNKNOWN 0x0004 /* Reader unknown. */
+#define PCSC_STATE_UNAVAILABLE 0x0008 /* Status unavailable. */
+#define PCSC_STATE_EMPTY 0x0010 /* Card removed. */
+#define PCSC_STATE_PRESENT 0x0020 /* Card inserted. */
+#define PCSC_STATE_ATRMATCH 0x0040 /* ATR matches card. */
+#define PCSC_STATE_EXCLUSIVE 0x0080 /* Exclusive Mode. */
+#define PCSC_STATE_INUSE 0x0100 /* Shared mode. */
+#define PCSC_STATE_MUTE 0x0200 /* Unresponsive card. */
+
+/* Some PC/SC error codes. */
+#define PCSC_E_CANCELLED 0x80100002
+#define PCSC_E_CANT_DISPOSE 0x8010000E
+#define PCSC_E_INSUFFICIENT_BUFFER 0x80100008
+#define PCSC_E_INVALID_ATR 0x80100015
+#define PCSC_E_INVALID_HANDLE 0x80100003
+#define PCSC_E_INVALID_PARAMETER 0x80100004
+#define PCSC_E_INVALID_TARGET 0x80100005
+#define PCSC_E_INVALID_VALUE 0x80100011
+#define PCSC_E_NO_MEMORY 0x80100006
+#define PCSC_E_UNKNOWN_READER 0x80100009
+#define PCSC_E_TIMEOUT 0x8010000A
+#define PCSC_E_SHARING_VIOLATION 0x8010000B
+#define PCSC_E_NO_SMARTCARD 0x8010000C
+#define PCSC_E_UNKNOWN_CARD 0x8010000D
+#define PCSC_E_PROTO_MISMATCH 0x8010000F
+#define PCSC_E_NOT_READY 0x80100010
+#define PCSC_E_SYSTEM_CANCELLED 0x80100012
+#define PCSC_E_NOT_TRANSACTED 0x80100016
+#define PCSC_E_READER_UNAVAILABLE 0x80100017
+#define PCSC_W_REMOVED_CARD 0x80100069
+
+
+struct pcsc_io_request_s
+{
+ unsigned long protocol;
+ unsigned long pci_len;
+};
+
+typedef struct pcsc_io_request_s *pcsc_io_request_t;
+
+struct pcsc_readerstate_s
+{
+ const char *reader;
+ void *user_data;
+ unsigned long current_state;
+ unsigned long event_state;
+ unsigned long atrlen;
+ unsigned char atr[33];
+};
+
+typedef struct pcsc_readerstate_s *pcsc_readerstate_t;
+
+long (* DLSTDCALL pcsc_establish_context) (unsigned long scope,
+ const void *reserved1,
+ const void *reserved2,
+ unsigned long *r_context);
+long (* DLSTDCALL pcsc_release_context) (unsigned long context);
+long (* DLSTDCALL pcsc_list_readers) (unsigned long context,
+ const char *groups,
+ char *readers, unsigned long*readerslen);
+long (* DLSTDCALL pcsc_get_status_change) (unsigned long context,
+ unsigned long timeout,
+ pcsc_readerstate_t readerstates,
+ unsigned long nreaderstates);
+long (* DLSTDCALL pcsc_connect) (unsigned long context,
+ const char *reader,
+ unsigned long share_mode,
+ unsigned long preferred_protocols,
+ unsigned long *r_card,
+ unsigned long *r_active_protocol);
+long (* DLSTDCALL pcsc_reconnect) (unsigned long card,
+ unsigned long share_mode,
+ unsigned long preferred_protocols,
+ unsigned long initialization,
+ unsigned long *r_active_protocol);
+long (* DLSTDCALL pcsc_disconnect) (unsigned long card,
+ unsigned long disposition);
+long (* DLSTDCALL pcsc_status) (unsigned long card,
+ char *reader, unsigned long *readerlen,
+ unsigned long *r_state,
+ unsigned long *r_protocol,
+ unsigned char *atr, unsigned long *atrlen);
+long (* DLSTDCALL pcsc_begin_transaction) (unsigned long card);
+long (* DLSTDCALL pcsc_end_transaction) (unsigned long card);
+long (* DLSTDCALL pcsc_transmit) (unsigned long card,
+ const pcsc_io_request_t send_pci,
+ const unsigned char *send_buffer,
+ unsigned long send_len,
+ pcsc_io_request_t recv_pci,
+ unsigned char *recv_buffer,
+ unsigned long *recv_len);
+long (* DLSTDCALL pcsc_set_timeout) (unsigned long context,
+ unsigned long timeout);
+
+
+/* Prototypes. */
+static int pcsc_get_status (int slot, unsigned int *status);
+
+
+
+/*
+ Helper
+ */
+
+
+/* Find an unused reader slot for PORTSTR and put it into the reader
+ table. Return -1 on error or the index into the reader table. */
+static int
+new_reader_slot (void)
+{
+ int i, reader = -1;
+
+ for (i=0; i < MAX_READER; i++)
+ {
+ if (!reader_table[i].used && reader == -1)
+ reader = i;
+ }
+ if (reader == -1)
+ {
+ log_error ("new_reader_slot: out of slots\n");
+ return -1;
+ }
+#ifdef USE_GNU_PTH
+ if (!reader_table[reader].lock_initialized)
+ {
+ if (!pth_mutex_init (&reader_table[reader].lock))
+ {
+ log_error ("error initializing mutex: %s\n", strerror (errno));
+ return -1;
+ }
+ reader_table[reader].lock_initialized = 1;
+ }
+#endif /*USE_GNU_PTH*/
+ reader_table[reader].close_reader = NULL;
+ reader_table[reader].shutdown_reader = NULL;
+ reader_table[reader].reset_reader = NULL;
+ reader_table[reader].get_status_reader = NULL;
+ reader_table[reader].send_apdu_reader = NULL;
+ reader_table[reader].check_keypad = NULL;
+ reader_table[reader].dump_status_reader = NULL;
+
+ reader_table[reader].used = 1;
+ reader_table[reader].last_status = 0;
+ reader_table[reader].is_t0 = 1;
+#ifdef NEED_PCSC_WRAPPER
+ reader_table[reader].pcsc.req_fd = -1;
+ reader_table[reader].pcsc.rsp_fd = -1;
+ reader_table[reader].pcsc.pid = (pid_t)(-1);
+#endif
+
+ return reader;
+}
+
+
+static void
+dump_reader_status (int slot)
+{
+ if (!opt.verbose)
+ return;
+
+ if (reader_table[slot].dump_status_reader)
+ reader_table[slot].dump_status_reader (slot);
+
+ if (reader_table[slot].status != -1
+ && reader_table[slot].atrlen)
+ {
+ log_info ("slot %d: ATR=", slot);
+ log_printhex ("", reader_table[slot].atr, reader_table[slot].atrlen);
+ }
+}
+
+
+
+static const char *
+host_sw_string (long err)
+{
+ switch (err)
+ {
+ case 0: return "okay";
+ case SW_HOST_OUT_OF_CORE: return "out of core";
+ case SW_HOST_INV_VALUE: return "invalid value";
+ case SW_HOST_NO_DRIVER: return "no driver";
+ case SW_HOST_NOT_SUPPORTED: return "not supported";
+ case SW_HOST_LOCKING_FAILED: return "locking failed";
+ case SW_HOST_BUSY: return "busy";
+ case SW_HOST_NO_CARD: return "no card";
+ case SW_HOST_CARD_INACTIVE: return "card inactive";
+ case SW_HOST_CARD_IO_ERROR: return "card I/O error";
+ case SW_HOST_GENERAL_ERROR: return "general error";
+ case SW_HOST_NO_READER: return "no reader";
+ case SW_HOST_ABORTED: return "aborted";
+ case SW_HOST_NO_KEYPAD: return "no keypad";
+ default: return "unknown host status error";
+ }
+}
+
+
+const char *
+apdu_strerror (int rc)
+{
+ switch (rc)
+ {
+ case SW_EOF_REACHED : return "eof reached";
+ case SW_EEPROM_FAILURE : return "eeprom failure";
+ case SW_WRONG_LENGTH : return "wrong length";
+ case SW_CHV_WRONG : return "CHV wrong";
+ case SW_CHV_BLOCKED : return "CHV blocked";
+ case SW_USE_CONDITIONS : return "use conditions not satisfied";
+ case SW_BAD_PARAMETER : return "bad parameter";
+ case SW_NOT_SUPPORTED : return "not supported";
+ case SW_FILE_NOT_FOUND : return "file not found";
+ case SW_RECORD_NOT_FOUND:return "record not found";
+ case SW_REF_NOT_FOUND : return "reference not found";
+ case SW_BAD_P0_P1 : return "bad P0 or P1";
+ case SW_INS_NOT_SUP : return "instruction not supported";
+ case SW_CLA_NOT_SUP : return "class not supported";
+ case SW_SUCCESS : return "success";
+ default:
+ if ((rc & ~0x00ff) == SW_MORE_DATA)
+ return "more data available";
+ if ( (rc & 0x10000) )
+ return host_sw_string (rc);
+ return "unknown status error";
+ }
+}
+
+
+
+/*
+ ct API Interface
+ */
+
+static const char *
+ct_error_string (long err)
+{
+ switch (err)
+ {
+ case 0: return "okay";
+ case -1: return "invalid data";
+ case -8: return "ct error";
+ case -10: return "transmission error";
+ case -11: return "memory allocation error";
+ case -128: return "HTSI error";
+ default: return "unknown CT-API error";
+ }
+}
+
+
+static void
+ct_dump_reader_status (int slot)
+{
+ log_info ("reader slot %d: %s\n", slot,
+ reader_table[slot].status == 1? "Processor ICC present" :
+ reader_table[slot].status == 0? "Memory ICC present" :
+ "ICC not present" );
+}
+
+
+/* Wait for the card in SLOT and activate it. Return a status word
+ error or 0 on success. */
+static int
+ct_activate_card (int slot)
+{
+ int rc;
+ unsigned char dad[1], sad[1], cmd[11], buf[256];
+ unsigned short buflen;
+
+ /* Check whether card has been inserted. */
+ dad[0] = 1; /* Destination address: CT. */
+ sad[0] = 2; /* Source address: Host. */
+
+ cmd[0] = 0x20; /* Class byte. */
+ cmd[1] = 0x13; /* Request status. */
+ cmd[2] = 0x00; /* From kernel. */
+ cmd[3] = 0x80; /* Return card's DO. */
+ cmd[4] = 0x00;
+
+ buflen = DIM(buf);
+
+ rc = CT_data (slot, dad, sad, 5, cmd, &buflen, buf);
+ if (rc || buflen < 2 || buf[buflen-2] != 0x90)
+ {
+ log_error ("ct_activate_card: can't get status of reader %d: %s\n",
+ slot, ct_error_string (rc));
+ return SW_HOST_CARD_IO_ERROR;
+ }
+
+ /* Connected, now activate the card. */
+ dad[0] = 1; /* Destination address: CT. */
+ sad[0] = 2; /* Source address: Host. */
+
+ cmd[0] = 0x20; /* Class byte. */
+ cmd[1] = 0x12; /* Request ICC. */
+ cmd[2] = 0x01; /* From first interface. */
+ cmd[3] = 0x01; /* Return card's ATR. */
+ cmd[4] = 0x00;
+
+ buflen = DIM(buf);
+
+ rc = CT_data (slot, dad, sad, 5, cmd, &buflen, buf);
+ if (rc || buflen < 2 || buf[buflen-2] != 0x90)
+ {
+ log_error ("ct_activate_card(%d): activation failed: %s\n",
+ slot, ct_error_string (rc));
+ if (!rc)
+ log_printhex (" received data:", buf, buflen);
+ return SW_HOST_CARD_IO_ERROR;
+ }
+
+ /* Store the type and the ATR. */
+ if (buflen - 2 > DIM (reader_table[0].atr))
+ {
+ log_error ("ct_activate_card(%d): ATR too long\n", slot);
+ return SW_HOST_CARD_IO_ERROR;
+ }
+
+ reader_table[slot].status = buf[buflen - 1];
+ memcpy (reader_table[slot].atr, buf, buflen - 2);
+ reader_table[slot].atrlen = buflen - 2;
+ return 0;
+}
+
+
+static int
+close_ct_reader (int slot)
+{
+ CT_close (slot);
+ reader_table[slot].used = 0;
+ return 0;
+}
+
+static int
+reset_ct_reader (int slot)
+{
+ /* FIXME: Check is this is sufficient do do a reset. */
+ return ct_activate_card (slot);
+}
+
+
+static int
+ct_get_status (int slot, unsigned int *status)
+{
+ *status = 1|2|4; /* FIXME */
+ return 0;
+
+ return SW_HOST_NOT_SUPPORTED;
+}
+
+/* Actually send the APDU of length APDULEN to SLOT and return a
+ maximum of *BUFLEN data in BUFFER, the actual retruned size will be
+ set to BUFLEN. Returns: CT API error code. */
+static int
+ct_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
+ unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo)
+{
+ int rc;
+ unsigned char dad[1], sad[1];
+ unsigned short ctbuflen;
+
+ /* If we don't have an ATR, we need to reset the reader first. */
+ if (!reader_table[slot].atrlen
+ && (rc = reset_ct_reader (slot)))
+ return rc;
+
+ dad[0] = 0; /* Destination address: Card. */
+ sad[0] = 2; /* Source address: Host. */
+ ctbuflen = *buflen;
+ if (DBG_CARD_IO)
+ log_printhex (" CT_data:", apdu, apdulen);
+ rc = CT_data (slot, dad, sad, apdulen, apdu, &ctbuflen, buffer);
+ *buflen = ctbuflen;
+
+ return rc? SW_HOST_CARD_IO_ERROR: 0;
+}
+
+
+
+/* Open a reader and return an internal handle for it. PORT is a
+ non-negative value with the port number of the reader. USB readers
+ do have port numbers starting at 32769. */
+static int
+open_ct_reader (int port)
+{
+ int rc, reader;
+
+ if (port < 0 || port > 0xffff)
+ {
+ log_error ("open_ct_reader: invalid port %d requested\n", port);
+ return -1;
+ }
+ reader = new_reader_slot ();
+ if (reader == -1)
+ return reader;
+ reader_table[reader].port = port;
+
+ rc = CT_init (reader, (unsigned short)port);
+ if (rc)
+ {
+ log_error ("apdu_open_ct_reader failed on port %d: %s\n",
+ port, ct_error_string (rc));
+ reader_table[reader].used = 0;
+ return -1;
+ }
+
+ /* Only try to activate the card. */
+ rc = ct_activate_card (reader);
+ if (rc)
+ {
+ reader_table[reader].atrlen = 0;
+ rc = 0;
+ }
+
+ reader_table[reader].close_reader = close_ct_reader;
+ reader_table[reader].reset_reader = reset_ct_reader;
+ reader_table[reader].get_status_reader = ct_get_status;
+ reader_table[reader].send_apdu_reader = ct_send_apdu;
+ reader_table[reader].check_keypad = NULL;
+ reader_table[reader].dump_status_reader = ct_dump_reader_status;
+
+ dump_reader_status (reader);
+ return reader;
+}
+
+
+/*
+ PC/SC Interface
+ */
+
+#ifdef NEED_PCSC_WRAPPER
+static int
+writen (int fd, const void *buf, size_t nbytes)
+{
+ size_t nleft = nbytes;
+ int nwritten;
+
+/* log_printhex (" writen:", buf, nbytes); */
+
+ while (nleft > 0)
+ {
+#ifdef USE_GNU_PTH
+ nwritten = pth_write (fd, buf, nleft);
+#else
+ nwritten = write (fd, buf, nleft);
+#endif
+ if (nwritten < 0 && errno == EINTR)
+ continue;
+ if (nwritten < 0)
+ return -1;
+ nleft -= nwritten;
+ buf = (const char*)buf + nwritten;
+ }
+ return 0;
+}
+
+/* Read up to BUFLEN bytes from FD and return the number of bytes
+ actually read in NREAD. Returns -1 on error or 0 on success. */
+static int
+readn (int fd, void *buf, size_t buflen, size_t *nread)
+{
+ size_t nleft = buflen;
+ int n;
+/* void *orig_buf = buf; */
+
+ while (nleft > 0)
+ {
+#ifdef USE_GNU_PTH
+ n = pth_read (fd, buf, nleft);
+#else
+ n = read (fd, buf, nleft);
+#endif
+ if (n < 0 && errno == EINTR)
+ continue;
+ if (n < 0)
+ return -1; /* read error. */
+ if (!n)
+ break; /* EOF */
+ nleft -= n;
+ buf = (char*)buf + n;
+ }
+ if (nread)
+ *nread = buflen - nleft;
+
+/* log_printhex (" readn:", orig_buf, *nread); */
+
+ return 0;
+}
+#endif /*NEED_PCSC_WRAPPER*/
+
+static const char *
+pcsc_error_string (long err)
+{
+ const char *s;
+
+ if (!err)
+ return "okay";
+ if ((err & 0x80100000) != 0x80100000)
+ return "invalid PC/SC error code";
+ err &= 0xffff;
+ switch (err)
+ {
+ case 0x0002: s = "cancelled"; break;
+ case 0x000e: s = "can't dispose"; break;
+ case 0x0008: s = "insufficient buffer"; break;
+ case 0x0015: s = "invalid ATR"; break;
+ case 0x0003: s = "invalid handle"; break;
+ case 0x0004: s = "invalid parameter"; break;
+ case 0x0005: s = "invalid target"; break;
+ case 0x0011: s = "invalid value"; break;
+ case 0x0006: s = "no memory"; break;
+ case 0x0013: s = "comm error"; break;
+ case 0x0001: s = "internal error"; break;
+ case 0x0014: s = "unknown error"; break;
+ case 0x0007: s = "waited too long"; break;
+ case 0x0009: s = "unknown reader"; break;
+ case 0x000a: s = "timeout"; break;
+ case 0x000b: s = "sharing violation"; break;
+ case 0x000c: s = "no smartcard"; break;
+ case 0x000d: s = "unknown card"; break;
+ case 0x000f: s = "proto mismatch"; break;
+ case 0x0010: s = "not ready"; break;
+ case 0x0012: s = "system cancelled"; break;
+ case 0x0016: s = "not transacted"; break;
+ case 0x0017: s = "reader unavailable"; break;
+ case 0x0065: s = "unsupported card"; break;
+ case 0x0066: s = "unresponsive card"; break;
+ case 0x0067: s = "unpowered card"; break;
+ case 0x0068: s = "reset card"; break;
+ case 0x0069: s = "removed card"; break;
+ case 0x006a: s = "inserted card"; break;
+ case 0x001f: s = "unsupported feature"; break;
+ case 0x0019: s = "PCI too small"; break;
+ case 0x001a: s = "reader unsupported"; break;
+ case 0x001b: s = "duplicate reader"; break;
+ case 0x001c: s = "card unsupported"; break;
+ case 0x001d: s = "no service"; break;
+ case 0x001e: s = "service stopped"; break;
+ default: s = "unknown PC/SC error code"; break;
+ }
+ return s;
+}
+
+/* Map PC/SC error codes to our special host status words. */
+static int
+pcsc_error_to_sw (long ec)
+{
+ int rc;
+
+ switch (ec)
+ {
+ case 0: rc = 0; break;
+
+ case PCSC_E_CANCELLED: rc = SW_HOST_ABORTED; break;
+ case PCSC_E_NO_MEMORY: rc = SW_HOST_OUT_OF_CORE; break;
+ case PCSC_E_TIMEOUT: rc = SW_HOST_CARD_IO_ERROR; break;
+ case PCSC_E_SHARING_VIOLATION: rc = SW_HOST_LOCKING_FAILED; break;
+ case PCSC_E_NO_SMARTCARD: rc = SW_HOST_NO_CARD; break;
+ case PCSC_W_REMOVED_CARD: rc = SW_HOST_NO_CARD; break;
+
+ case PCSC_E_INVALID_TARGET:
+ case PCSC_E_INVALID_VALUE:
+ case PCSC_E_INVALID_HANDLE:
+ case PCSC_E_INVALID_PARAMETER:
+ case PCSC_E_INSUFFICIENT_BUFFER: rc = SW_HOST_INV_VALUE; break;
+
+ default: rc = SW_HOST_GENERAL_ERROR; break;
+ }
+
+ return rc;
+}
+
+static void
+dump_pcsc_reader_status (int slot)
+{
+ log_info ("reader slot %d: active protocol:", slot);
+ if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T0))
+ log_printf (" T0");
+ else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
+ log_printf (" T1");
+ else if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_RAW))
+ log_printf (" raw");
+ log_printf ("\n");
+}
+
+
+/* Send an PC/SC reset command and return a status word on error or 0
+ on success. */
+static int
+reset_pcsc_reader (int slot)
+{
+#ifdef NEED_PCSC_WRAPPER
+ long err;
+ reader_table_t slotp;
+ size_t len;
+ int i, n;
+ unsigned char msgbuf[9];
+ unsigned int dummy_status;
+ int sw = SW_HOST_CARD_IO_ERROR;
+
+ slotp = reader_table + slot;
+
+ if (slotp->pcsc.req_fd == -1
+ || slotp->pcsc.rsp_fd == -1
+ || slotp->pcsc.pid == (pid_t)(-1) )
+ {
+ log_error ("pcsc_get_status: pcsc-wrapper not running\n");
+ return sw;
+ }
+
+ msgbuf[0] = 0x05; /* RESET command. */
+ len = 0;
+ msgbuf[1] = (len >> 24);
+ msgbuf[2] = (len >> 16);
+ msgbuf[3] = (len >> 8);
+ msgbuf[4] = (len );
+ if ( writen (slotp->pcsc.req_fd, msgbuf, 5) )
+ {
+ log_error ("error sending PC/SC RESET request: %s\n",
+ strerror (errno));
+ goto command_failed;
+ }
+
+ /* Read the response. */
+ if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
+ {
+ log_error ("error receiving PC/SC RESET response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
+ if (msgbuf[0] != 0x81 || len < 4)
+ {
+ log_error ("invalid response header from PC/SC received\n");
+ goto command_failed;
+ }
+ len -= 4; /* Already read the error code. */
+ if (len > DIM (slotp->atr))
+ {
+ log_error ("PC/SC returned a too large ATR (len=%lx)\n",
+ (unsigned long)len);
+ sw = SW_HOST_GENERAL_ERROR;
+ goto command_failed;
+ }
+ err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+ if (err)
+ {
+ log_error ("PC/SC RESET failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ /* If the error code is no smart card, we should not considere
+ this a major error and close the wrapper. */
+ sw = pcsc_error_to_sw (err);
+ if (err == PCSC_E_NO_SMARTCARD)
+ return sw;
+ goto command_failed;
+ }
+
+ /* The open function may return a zero for the ATR length to
+ indicate that no card is present. */
+ n = len;
+ if (n)
+ {
+ if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
+ {
+ log_error ("error receiving PC/SC RESET response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ }
+ slotp->atrlen = len;
+
+ /* Read the status so that IS_T0 will be set. */
+ pcsc_get_status (slot, &dummy_status);
+
+ return 0;
+
+ command_failed:
+ close (slotp->pcsc.req_fd);
+ close (slotp->pcsc.rsp_fd);
+ slotp->pcsc.req_fd = -1;
+ slotp->pcsc.rsp_fd = -1;
+ kill (slotp->pcsc.pid, SIGTERM);
+ slotp->pcsc.pid = (pid_t)(-1);
+ slotp->used = 0;
+ return sw;
+
+#else /* !NEED_PCSC_WRAPPER */
+ long err;
+ char reader[250];
+ unsigned long nreader, atrlen;
+ unsigned long card_state, card_protocol;
+
+ if (reader_table[slot].pcsc.card)
+ {
+ err = pcsc_disconnect (reader_table[slot].pcsc.card, PCSC_LEAVE_CARD);
+ if (err)
+ {
+ log_error ("pcsc_disconnect failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ return SW_HOST_CARD_IO_ERROR;
+ }
+ reader_table[slot].pcsc.card = 0;
+ }
+
+ err = pcsc_connect (reader_table[slot].pcsc.context,
+ reader_table[slot].rdrname,
+ PCSC_SHARE_EXCLUSIVE,
+ PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
+ &reader_table[slot].pcsc.card,
+ &reader_table[slot].pcsc.protocol);
+ if (err)
+ {
+ log_error ("pcsc_connect failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ reader_table[slot].pcsc.card = 0;
+ return pcsc_error_to_sw (err);
+ }
+
+
+ atrlen = 33;
+ nreader = sizeof reader - 1;
+ err = pcsc_status (reader_table[slot].pcsc.card,
+ reader, &nreader,
+ &card_state, &card_protocol,
+ reader_table[slot].atr, &atrlen);
+ if (err)
+ {
+ log_error ("pcsc_status failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ reader_table[slot].atrlen = 0;
+ return pcsc_error_to_sw (err);
+ }
+ if (atrlen >= DIM (reader_table[0].atr))
+ log_bug ("ATR returned by pcsc_status is too large\n");
+ reader_table[slot].atrlen = atrlen;
+ reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0);
+
+ return 0;
+#endif /* !NEED_PCSC_WRAPPER */
+}
+
+
+static int
+pcsc_get_status (int slot, unsigned int *status)
+{
+#ifdef NEED_PCSC_WRAPPER
+ long err;
+ reader_table_t slotp;
+ size_t len, full_len;
+ int i, n;
+ unsigned char msgbuf[9];
+ unsigned char buffer[16];
+ int sw = SW_HOST_CARD_IO_ERROR;
+
+ slotp = reader_table + slot;
+
+ if (slotp->pcsc.req_fd == -1
+ || slotp->pcsc.rsp_fd == -1
+ || slotp->pcsc.pid == (pid_t)(-1) )
+ {
+ log_error ("pcsc_get_status: pcsc-wrapper not running\n");
+ return sw;
+ }
+
+ msgbuf[0] = 0x04; /* STATUS command. */
+ len = 0;
+ msgbuf[1] = (len >> 24);
+ msgbuf[2] = (len >> 16);
+ msgbuf[3] = (len >> 8);
+ msgbuf[4] = (len );
+ if ( writen (slotp->pcsc.req_fd, msgbuf, 5) )
+ {
+ log_error ("error sending PC/SC STATUS request: %s\n",
+ strerror (errno));
+ goto command_failed;
+ }
+
+ /* Read the response. */
+ if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
+ {
+ log_error ("error receiving PC/SC STATUS response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
+ if (msgbuf[0] != 0x81 || len < 4)
+ {
+ log_error ("invalid response header from PC/SC received\n");
+ goto command_failed;
+ }
+ len -= 4; /* Already read the error code. */
+ err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+ if (err)
+ {
+ log_error ("pcsc_status failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ /* This is a proper error code, so return immediately. */
+ return pcsc_error_to_sw (err);
+ }
+
+ full_len = len;
+
+ /* The current version returns 3 words but we allow also for old
+ versions returning only 2 words. */
+ n = 12 < len ? 12 : len;
+ if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len))
+ || (len != 8 && len != 12))
+ {
+ log_error ("error receiving PC/SC STATUS response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+
+ slotp->is_t0 = (len == 12 && !!(buffer[11] & PCSC_PROTOCOL_T0));
+
+
+ full_len -= len;
+ /* Newer versions of the wrapper might send more status bytes.
+ Read them. */
+ while (full_len)
+ {
+ unsigned char dummybuf[128];
+
+ n = full_len < DIM (dummybuf) ? full_len : DIM (dummybuf);
+ if ((i=readn (slotp->pcsc.rsp_fd, dummybuf, n, &len)) || len != n)
+ {
+ log_error ("error receiving PC/SC TRANSMIT response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ full_len -= n;
+ }
+
+ /* We are lucky: The wrapper already returns the data in the
+ required format. */
+ *status = buffer[3];
+
+ return 0;
+
+ command_failed:
+ close (slotp->pcsc.req_fd);
+ close (slotp->pcsc.rsp_fd);
+ slotp->pcsc.req_fd = -1;
+ slotp->pcsc.rsp_fd = -1;
+ kill (slotp->pcsc.pid, SIGTERM);
+ slotp->pcsc.pid = (pid_t)(-1);
+ slotp->used = 0;
+ return sw;
+
+#else /*!NEED_PCSC_WRAPPER*/
+
+ long err;
+ struct pcsc_readerstate_s rdrstates[1];
+
+ memset (rdrstates, 0, sizeof *rdrstates);
+ rdrstates[0].reader = reader_table[slot].rdrname;
+ rdrstates[0].current_state = PCSC_STATE_UNAWARE;
+ err = pcsc_get_status_change (reader_table[slot].pcsc.context,
+ 0,
+ rdrstates, 1);
+ if (err == PCSC_E_TIMEOUT)
+ err = 0; /* Timeout is no error error here. */
+ if (err)
+ {
+ log_error ("pcsc_get_status_change failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ return pcsc_error_to_sw (err);
+ }
+
+
+ /* log_debug */
+ /* ("pcsc_get_status_change: %s%s%s%s%s%s%s%s%s%s\n", */
+ /* (rdrstates[0].event_state & PCSC_STATE_IGNORE)? " ignore":"", */
+ /* (rdrstates[0].event_state & PCSC_STATE_CHANGED)? " changed":"", */
+ /* (rdrstates[0].event_state & PCSC_STATE_UNKNOWN)? " unknown":"", */
+ /* (rdrstates[0].event_state & PCSC_STATE_UNAVAILABLE)?" unavail":"", */
+ /* (rdrstates[0].event_state & PCSC_STATE_EMPTY)? " empty":"", */
+ /* (rdrstates[0].event_state & PCSC_STATE_PRESENT)? " present":"", */
+ /* (rdrstates[0].event_state & PCSC_STATE_ATRMATCH)? " atr":"", */
+ /* (rdrstates[0].event_state & PCSC_STATE_EXCLUSIVE)? " excl":"", */
+ /* (rdrstates[0].event_state & PCSC_STATE_INUSE)? " unuse":"", */
+ /* (rdrstates[0].event_state & PCSC_STATE_MUTE)? " mute":"" ); */
+
+ *status = 0;
+ if ( (rdrstates[0].event_state & PCSC_STATE_PRESENT) )
+ *status |= 2;
+ if ( !(rdrstates[0].event_state & PCSC_STATE_MUTE) )
+ *status |= 4;
+ /* We indicate a useful card if it is not in use by another
+ application. This is because we only use exclusive access
+ mode. */
+ if ( (*status & 6) == 6
+ && !(rdrstates[0].event_state & PCSC_STATE_INUSE) )
+ *status |= 1;
+
+ return 0;
+#endif /*!NEED_PCSC_WRAPPER*/
+}
+
+
+/* Actually send the APDU of length APDULEN to SLOT and return a
+ maximum of *BUFLEN data in BUFFER, the actual returned size will be
+ set to BUFLEN. Returns: CT API error code. */
+static int
+pcsc_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
+ unsigned char *buffer, size_t *buflen,
+ struct pininfo_s *pininfo)
+{
+#ifdef NEED_PCSC_WRAPPER
+ long err;
+ reader_table_t slotp;
+ size_t len, full_len;
+ int i, n;
+ unsigned char msgbuf[9];
+ int sw = SW_HOST_CARD_IO_ERROR;
+
+ if (!reader_table[slot].atrlen
+ && (err = reset_pcsc_reader (slot)))
+ return err;
+
+ if (DBG_CARD_IO)
+ log_printhex (" PCSC_data:", apdu, apdulen);
+
+ slotp = reader_table + slot;
+
+ if (slotp->pcsc.req_fd == -1
+ || slotp->pcsc.rsp_fd == -1
+ || slotp->pcsc.pid == (pid_t)(-1) )
+ {
+ log_error ("pcsc_send_apdu: pcsc-wrapper not running\n");
+ return sw;
+ }
+
+ msgbuf[0] = 0x03; /* TRANSMIT command. */
+ len = apdulen;
+ msgbuf[1] = (len >> 24);
+ msgbuf[2] = (len >> 16);
+ msgbuf[3] = (len >> 8);
+ msgbuf[4] = (len );
+ if ( writen (slotp->pcsc.req_fd, msgbuf, 5)
+ || writen (slotp->pcsc.req_fd, apdu, len))
+ {
+ log_error ("error sending PC/SC TRANSMIT request: %s\n",
+ strerror (errno));
+ goto command_failed;
+ }
+
+ /* Read the response. */
+ if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
+ {
+ log_error ("error receiving PC/SC TRANSMIT response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
+ if (msgbuf[0] != 0x81 || len < 4)
+ {
+ log_error ("invalid response header from PC/SC received\n");
+ goto command_failed;
+ }
+ len -= 4; /* Already read the error code. */
+ err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+ if (err)
+ {
+ log_error ("pcsc_transmit failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ return pcsc_error_to_sw (err);
+ }
+
+ full_len = len;
+
+ n = *buflen < len ? *buflen : len;
+ if ((i=readn (slotp->pcsc.rsp_fd, buffer, n, &len)) || len != n)
+ {
+ log_error ("error receiving PC/SC TRANSMIT response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ *buflen = n;
+
+ full_len -= len;
+ if (full_len)
+ {
+ log_error ("pcsc_send_apdu: provided buffer too short - truncated\n");
+ err = SW_HOST_INV_VALUE;
+ }
+ /* We need to read any rest of the response, to keep the
+ protocol runnng. */
+ while (full_len)
+ {
+ unsigned char dummybuf[128];
+
+ n = full_len < DIM (dummybuf) ? full_len : DIM (dummybuf);
+ if ((i=readn (slotp->pcsc.rsp_fd, dummybuf, n, &len)) || len != n)
+ {
+ log_error ("error receiving PC/SC TRANSMIT response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ full_len -= n;
+ }
+
+ return err;
+
+ command_failed:
+ close (slotp->pcsc.req_fd);
+ close (slotp->pcsc.rsp_fd);
+ slotp->pcsc.req_fd = -1;
+ slotp->pcsc.rsp_fd = -1;
+ kill (slotp->pcsc.pid, SIGTERM);
+ slotp->pcsc.pid = (pid_t)(-1);
+ slotp->used = 0;
+ return sw;
+
+#else /*!NEED_PCSC_WRAPPER*/
+
+ long err;
+ struct pcsc_io_request_s send_pci;
+ unsigned long recv_len;
+
+ if (!reader_table[slot].atrlen
+ && (err = reset_pcsc_reader (slot)))
+ return err;
+
+ if (DBG_CARD_IO)
+ log_printhex (" PCSC_data:", apdu, apdulen);
+
+ if ((reader_table[slot].pcsc.protocol & PCSC_PROTOCOL_T1))
+ send_pci.protocol = PCSC_PROTOCOL_T1;
+ else
+ send_pci.protocol = PCSC_PROTOCOL_T0;
+ send_pci.pci_len = sizeof send_pci;
+ recv_len = *buflen;
+ err = pcsc_transmit (reader_table[slot].pcsc.card,
+ &send_pci, apdu, apdulen,
+ NULL, buffer, &recv_len);
+ *buflen = recv_len;
+ if (err)
+ log_error ("pcsc_transmit failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+
+ return pcsc_error_to_sw (err);
+#endif /*!NEED_PCSC_WRAPPER*/
+}
+
+
+static int
+close_pcsc_reader (int slot)
+{
+#ifdef NEED_PCSC_WRAPPER
+ long err;
+ reader_table_t slotp;
+ size_t len;
+ int i;
+ unsigned char msgbuf[9];
+
+ slotp = reader_table + slot;
+
+ if (slotp->pcsc.req_fd == -1
+ || slotp->pcsc.rsp_fd == -1
+ || slotp->pcsc.pid == (pid_t)(-1) )
+ {
+ log_error ("close_pcsc_reader: pcsc-wrapper not running\n");
+ return 0;
+ }
+
+ msgbuf[0] = 0x02; /* CLOSE command. */
+ len = 0;
+ msgbuf[1] = (len >> 24);
+ msgbuf[2] = (len >> 16);
+ msgbuf[3] = (len >> 8);
+ msgbuf[4] = (len );
+ if ( writen (slotp->pcsc.req_fd, msgbuf, 5) )
+ {
+ log_error ("error sending PC/SC CLOSE request: %s\n",
+ strerror (errno));
+ goto command_failed;
+ }
+
+ /* Read the response. */
+ if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
+ {
+ log_error ("error receiving PC/SC CLOSE response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
+ if (msgbuf[0] != 0x81 || len < 4)
+ {
+ log_error ("invalid response header from PC/SC received\n");
+ goto command_failed;
+ }
+ len -= 4; /* Already read the error code. */
+ err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+ if (err)
+ log_error ("pcsc_close failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+
+ /* We will close the wrapper in any case - errors are merely
+ informational. */
+
+ command_failed:
+ close (slotp->pcsc.req_fd);
+ close (slotp->pcsc.rsp_fd);
+ slotp->pcsc.req_fd = -1;
+ slotp->pcsc.rsp_fd = -1;
+ kill (slotp->pcsc.pid, SIGTERM);
+ slotp->pcsc.pid = (pid_t)(-1);
+ slotp->used = 0;
+ return 0;
+
+#else /*!NEED_PCSC_WRAPPER*/
+
+ pcsc_release_context (reader_table[slot].pcsc.context);
+ xfree (reader_table[slot].rdrname);
+ reader_table[slot].rdrname = NULL;
+ reader_table[slot].used = 0;
+ return 0;
+#endif /*!NEED_PCSC_WRAPPER*/
+}
+
+/* Note: It is a pitty that we can't return proper error codes. */
+static int
+open_pcsc_reader (const char *portstr)
+{
+#ifdef NEED_PCSC_WRAPPER
+/* Open the PC/SC reader using the pcsc_wrapper program. This is
+ needed to cope with different thread models and other peculiarities
+ of libpcsclite. */
+ int slot;
+ reader_table_t slotp;
+ int fd, rp[2], wp[2];
+ int n, i;
+ pid_t pid;
+ size_t len;
+ unsigned char msgbuf[9];
+ int err;
+ unsigned int dummy_status;
+ int sw = SW_HOST_CARD_IO_ERROR;
+
+ slot = new_reader_slot ();
+ if (slot == -1)
+ return -1;
+ slotp = reader_table + slot;
+
+ /* Fire up the pcsc wrapper. We don't use any fork/exec code from
+ the common directy but implement it direclty so that this file
+ may still be source copied. */
+
+ if (pipe (rp) == -1)
+ {
+ log_error ("error creating a pipe: %s\n", strerror (errno));
+ slotp->used = 0;
+ return -1;
+ }
+ if (pipe (wp) == -1)
+ {
+ log_error ("error creating a pipe: %s\n", strerror (errno));
+ close (rp[0]);
+ close (rp[1]);
+ slotp->used = 0;
+ return -1;
+ }
+
+ pid = fork ();
+ if (pid == -1)
+ {
+ log_error ("error forking process: %s\n", strerror (errno));
+ close (rp[0]);
+ close (rp[1]);
+ close (wp[0]);
+ close (wp[1]);
+ slotp->used = 0;
+ return -1;
+ }
+ slotp->pcsc.pid = pid;
+
+ if (!pid)
+ { /*
+ === Child ===
+ */
+
+ /* Double fork. */
+ pid = fork ();
+ if (pid == -1)
+ _exit (31);
+ if (pid)
+ _exit (0); /* Immediate exit this parent, so that the child
+ gets cleaned up by the init process. */
+
+ /* Connect our pipes. */
+ if (wp[0] != 0 && dup2 (wp[0], 0) == -1)
+ log_fatal ("dup2 stdin failed: %s\n", strerror (errno));
+ if (rp[1] != 1 && dup2 (rp[1], 1) == -1)
+ log_fatal ("dup2 stdout failed: %s\n", strerror (errno));
+
+ /* Send stderr to the bit bucket. */
+ fd = open ("/dev/null", O_WRONLY);
+ if (fd == -1)
+ log_fatal ("can't open `/dev/null': %s", strerror (errno));
+ if (fd != 2 && dup2 (fd, 2) == -1)
+ log_fatal ("dup2 stderr failed: %s\n", strerror (errno));
+
+ /* Close all other files. */
+ n = sysconf (_SC_OPEN_MAX);
+ if (n < 0)
+ n = MAX_OPEN_FDS;
+ for (i=3; i < n; i++)
+ close(i);
+ errno = 0;
+
+ execl (GNUPG_LIBDIR "/pcsc-wrapper",
+ "pcsc-wrapper",
+ "--",
+ "1", /* API version */
+ opt.pcsc_driver, /* Name of the PC/SC library. */
+ NULL);
+ _exit (31);
+ }
+
+ /*
+ === Parent ===
+ */
+ close (wp[0]);
+ close (rp[1]);
+ slotp->pcsc.req_fd = wp[1];
+ slotp->pcsc.rsp_fd = rp[0];
+
+ /* Wait for the intermediate child to terminate. */
+#ifdef USE_GNU_PTH
+#define WAIT pth_waitpid
+#else
+#define WAIT waitpid
+#endif
+ while ( (i=WAIT (pid, NULL, 0)) == -1 && errno == EINTR)
+ ;
+#undef X
+
+ /* Now send the open request. */
+ msgbuf[0] = 0x01; /* OPEN command. */
+ len = portstr? strlen (portstr):0;
+ msgbuf[1] = (len >> 24);
+ msgbuf[2] = (len >> 16);
+ msgbuf[3] = (len >> 8);
+ msgbuf[4] = (len );
+ if ( writen (slotp->pcsc.req_fd, msgbuf, 5)
+ || (portstr && writen (slotp->pcsc.req_fd, portstr, len)))
+ {
+ log_error ("error sending PC/SC OPEN request: %s\n",
+ strerror (errno));
+ goto command_failed;
+ }
+ /* Read the response. */
+ if ((i=readn (slotp->pcsc.rsp_fd, msgbuf, 9, &len)) || len != 9)
+ {
+ log_error ("error receiving PC/SC OPEN response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ len = (msgbuf[1] << 24) | (msgbuf[2] << 16) | (msgbuf[3] << 8 ) | msgbuf[4];
+ if (msgbuf[0] != 0x81 || len < 4)
+ {
+ log_error ("invalid response header from PC/SC received\n");
+ goto command_failed;
+ }
+ len -= 4; /* Already read the error code. */
+ if (len > DIM (slotp->atr))
+ {
+ log_error ("PC/SC returned a too large ATR (len=%lx)\n",
+ (unsigned long)len);
+ goto command_failed;
+ }
+ err = (msgbuf[5] << 24) | (msgbuf[6] << 16) | (msgbuf[7] << 8 ) | msgbuf[8];
+ if (err)
+ {
+ log_error ("PC/SC OPEN failed: %s\n", pcsc_error_string (err));
+ sw = pcsc_error_to_sw (err);
+ goto command_failed;
+ }
+
+ slotp->last_status = 0;
+
+ /* The open request may return a zero for the ATR length to
+ indicate that no card is present. */
+ n = len;
+ if (n)
+ {
+ if ((i=readn (slotp->pcsc.rsp_fd, slotp->atr, n, &len)) || len != n)
+ {
+ log_error ("error receiving PC/SC OPEN response: %s\n",
+ i? strerror (errno) : "premature EOF");
+ goto command_failed;
+ }
+ /* If we got to here we know that a card is present
+ and usable. Thus remember this. */
+ slotp->last_status = (1|2|4| 0x8000);
+ }
+ slotp->atrlen = len;
+
+ reader_table[slot].close_reader = close_pcsc_reader;
+ reader_table[slot].reset_reader = reset_pcsc_reader;
+ reader_table[slot].get_status_reader = pcsc_get_status;
+ reader_table[slot].send_apdu_reader = pcsc_send_apdu;
+ reader_table[slot].check_keypad = NULL;
+ reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
+
+ /* Read the status so that IS_T0 will be set. */
+ pcsc_get_status (slot, &dummy_status);
+
+ dump_reader_status (slot);
+ return slot;
+
+ command_failed:
+ close (slotp->pcsc.req_fd);
+ close (slotp->pcsc.rsp_fd);
+ slotp->pcsc.req_fd = -1;
+ slotp->pcsc.rsp_fd = -1;
+ kill (slotp->pcsc.pid, SIGTERM);
+ slotp->pcsc.pid = (pid_t)(-1);
+ slotp->used = 0;
+ /* There is no way to return SW. */
+ return -1;
+
+#else /*!NEED_PCSC_WRAPPER */
+ long err;
+ int slot;
+ char *list = NULL;
+ unsigned long nreader, listlen, atrlen;
+ char *p;
+ unsigned long card_state, card_protocol;
+
+ slot = new_reader_slot ();
+ if (slot == -1)
+ return -1;
+
+ err = pcsc_establish_context (PCSC_SCOPE_SYSTEM, NULL, NULL,
+ &reader_table[slot].pcsc.context);
+ if (err)
+ {
+ log_error ("pcsc_establish_context failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ reader_table[slot].used = 0;
+ return -1;
+ }
+
+ err = pcsc_list_readers (reader_table[slot].pcsc.context,
+ NULL, NULL, &nreader);
+ if (!err)
+ {
+ list = xtrymalloc (nreader+1); /* Better add 1 for safety reasons. */
+ if (!list)
+ {
+ log_error ("error allocating memory for reader list\n");
+ pcsc_release_context (reader_table[slot].pcsc.context);
+ reader_table[slot].used = 0;
+ return -1 /*SW_HOST_OUT_OF_CORE*/;
+ }
+ err = pcsc_list_readers (reader_table[slot].pcsc.context,
+ NULL, list, &nreader);
+ }
+ if (err)
+ {
+ log_error ("pcsc_list_readers failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ pcsc_release_context (reader_table[slot].pcsc.context);
+ reader_table[slot].used = 0;
+ xfree (list);
+ return -1 /*pcsc_error_to_sw (err)*/;
+ }
+
+ listlen = nreader;
+ p = list;
+ while (nreader)
+ {
+ if (!*p && !p[1])
+ break;
+ if (*p)
+ log_info ("detected reader `%s'\n", p);
+ if (nreader < (strlen (p)+1))
+ {
+ log_error ("invalid response from pcsc_list_readers\n");
+ break;
+ }
+ nreader -= strlen (p)+1;
+ p += strlen (p) + 1;
+ }
+
+ reader_table[slot].rdrname = xtrymalloc (strlen (portstr? portstr : list)+1);
+ if (!reader_table[slot].rdrname)
+ {
+ log_error ("error allocating memory for reader name\n");
+ pcsc_release_context (reader_table[slot].pcsc.context);
+ reader_table[slot].used = 0;
+ return -1 /*SW_HOST_OUT_OF_CORE*/;
+ }
+ strcpy (reader_table[slot].rdrname, portstr? portstr : list);
+ xfree (list);
+ list = NULL;
+
+ err = pcsc_connect (reader_table[slot].pcsc.context,
+ reader_table[slot].rdrname,
+ PCSC_SHARE_EXCLUSIVE,
+ PCSC_PROTOCOL_T0|PCSC_PROTOCOL_T1,
+ &reader_table[slot].pcsc.card,
+ &reader_table[slot].pcsc.protocol);
+ if (err == PCSC_E_NO_SMARTCARD)
+ reader_table[slot].pcsc.card = 0;
+ else if (err)
+ {
+ log_error ("pcsc_connect failed: %s (0x%lx)\n",
+ pcsc_error_string (err), err);
+ pcsc_release_context (reader_table[slot].pcsc.context);
+ xfree (reader_table[slot].rdrname);
+ reader_table[slot].rdrname = NULL;
+ reader_table[slot].used = 0;
+ return -1 /*pcsc_error_to_sw (err)*/;
+ }
+
+ reader_table[slot].atrlen = 0;
+ reader_table[slot].last_status = 0;
+ if (!err)
+ {
+ char reader[250];
+ unsigned long readerlen;
+
+ atrlen = 32;
+ readerlen = sizeof reader -1 ;
+ err = pcsc_status (reader_table[slot].pcsc.card,
+ reader, &readerlen,
+ &card_state, &card_protocol,
+ reader_table[slot].atr, &atrlen);
+ if (err)
+ log_error ("pcsc_status failed: %s (0x%lx) %lu\n",
+ pcsc_error_string (err), err, readerlen);
+ else
+ {
+ if (atrlen >= DIM (reader_table[0].atr))
+ log_bug ("ATR returned by pcsc_status is too large\n");
+ reader_table[slot].atrlen = atrlen;
+ /* If we got to here we know that a card is present
+ and usable. Thus remember this. */
+ reader_table[slot].last_status = (1|2|4| 0x8000);
+ reader_table[slot].is_t0 = !!(card_protocol & PCSC_PROTOCOL_T0);
+ }
+ }
+
+ reader_table[slot].close_reader = close_pcsc_reader;
+ reader_table[slot].reset_reader = reset_pcsc_reader;
+ reader_table[slot].get_status_reader = pcsc_get_status;
+ reader_table[slot].send_apdu_reader = pcsc_send_apdu;
+ reader_table[slot].check_keypad = NULL;
+ reader_table[slot].dump_status_reader = dump_pcsc_reader_status;
+
+/* log_debug ("state from pcsc_status: 0x%lx\n", card_state); */
+/* log_debug ("protocol from pcsc_status: 0x%lx\n", card_protocol); */
+
+ dump_reader_status (slot);
+ return slot;
+#endif /*!NEED_PCSC_WRAPPER */
+}
+
+
+
+
+#ifdef HAVE_LIBUSB
+/*
+ Internal CCID driver interface.
+ */
+
+
+static void
+dump_ccid_reader_status (int slot)
+{
+ log_info ("reader slot %d: using ccid driver\n", slot);
+}
+
+static int
+close_ccid_reader (int slot)
+{
+ ccid_close_reader (reader_table[slot].ccid.handle);
+ reader_table[slot].used = 0;
+ return 0;
+}
+
+
+static int
+shutdown_ccid_reader (int slot)
+{
+ ccid_shutdown_reader (reader_table[slot].ccid.handle);
+ return 0;
+}
+
+
+static int
+reset_ccid_reader (int slot)
+{
+ int err;
+ reader_table_t slotp = reader_table + slot;
+ unsigned char atr[33];
+ size_t atrlen;
+
+ err = ccid_get_atr (slotp->ccid.handle, atr, sizeof atr, &atrlen);
+ if (err)
+ return err;
+ /* If the reset was successful, update the ATR. */
+ assert (sizeof slotp->atr >= sizeof atr);
+ slotp->atrlen = atrlen;
+ memcpy (slotp->atr, atr, atrlen);
+ dump_reader_status (slot);
+ return 0;
+}
+
+
+static int
+get_status_ccid (int slot, unsigned int *status)
+{
+ int rc;
+ int bits;
+
+ rc = ccid_slot_status (reader_table[slot].ccid.handle, &bits);
+ if (rc)
+ return -1;
+
+ if (bits == 0)
+ *status = 1|2|4;
+ else if (bits == 1)
+ *status = 2;
+ else
+ *status = 0;
+
+ return 0;
+}
+
+
+/* Actually send the APDU of length APDULEN to SLOT and return a
+ maximum of *BUFLEN data in BUFFER, the actual returned size will be
+ set to BUFLEN. Returns: Internal CCID driver error code. */
+static int
+send_apdu_ccid (int slot, unsigned char *apdu, size_t apdulen,
+ unsigned char *buffer, size_t *buflen,
+ struct pininfo_s *pininfo)
+{
+ long err;
+ size_t maxbuflen;
+
+ /* If we don't have an ATR, we need to reset the reader first. */
+ if (!reader_table[slot].atrlen
+ && (err = reset_ccid_reader (slot)))
+ return err;
+
+ if (DBG_CARD_IO)
+ log_printhex (" APDU_data:", apdu, apdulen);
+
+ maxbuflen = *buflen;
+ if (pininfo)
+ err = ccid_transceive_secure (reader_table[slot].ccid.handle,
+ apdu, apdulen,
+ pininfo->mode,
+ pininfo->minlen,
+ pininfo->maxlen,
+ pininfo->padlen,
+ buffer, maxbuflen, buflen);
+ else
+ err = ccid_transceive (reader_table[slot].ccid.handle,
+ apdu, apdulen,
+ buffer, maxbuflen, buflen);
+ if (err)
+ log_error ("ccid_transceive failed: (0x%lx)\n",
+ err);
+
+ return err;
+}
+
+
+/* Check whether the CCID reader supports the ISO command code COMMAND
+ on the keypad. Return 0 on success. For a description of the pin
+ parameters, see ccid-driver.c */
+static int
+check_ccid_keypad (int slot, int command, int pin_mode,
+ int pinlen_min, int pinlen_max, int pin_padlen)
+{
+ unsigned char apdu[] = { 0, 0, 0, 0x81 };
+
+ apdu[1] = command;
+ return ccid_transceive_secure (reader_table[slot].ccid.handle,
+ apdu, sizeof apdu,
+ pin_mode, pinlen_min, pinlen_max, pin_padlen,
+ NULL, 0, NULL);
+}
+
+
+/* Open the reader and try to read an ATR. */
+static int
+open_ccid_reader (const char *portstr)
+{
+ int err;
+ int slot;
+ reader_table_t slotp;
+
+ slot = new_reader_slot ();
+ if (slot == -1)
+ return -1;
+ slotp = reader_table + slot;
+
+ err = ccid_open_reader (&slotp->ccid.handle, portstr);
+ if (err)
+ {
+ slotp->used = 0;
+ return -1;
+ }
+
+ err = ccid_get_atr (slotp->ccid.handle,
+ slotp->atr, sizeof slotp->atr, &slotp->atrlen);
+ if (err)
+ {
+ slotp->atrlen = 0;
+ err = 0;
+ }
+ else
+ {
+ /* If we got to here we know that a card is present
+ and usable. Thus remember this. */
+ reader_table[slot].last_status = (1|2|4| 0x8000);
+ }
+
+ reader_table[slot].close_reader = close_ccid_reader;
+ reader_table[slot].shutdown_reader = shutdown_ccid_reader;
+ reader_table[slot].reset_reader = reset_ccid_reader;
+ reader_table[slot].get_status_reader = get_status_ccid;
+ reader_table[slot].send_apdu_reader = send_apdu_ccid;
+ reader_table[slot].check_keypad = check_ccid_keypad;
+ reader_table[slot].dump_status_reader = dump_ccid_reader_status;
+
+ dump_reader_status (slot);
+ return slot;
+}
+
+
+
+#endif /* HAVE_LIBUSB */
+
+
+
+#ifdef USE_G10CODE_RAPDU
+/*
+ The Remote APDU Interface.
+
+ This uses the Remote APDU protocol to contact a reader.
+
+ The port number is actually an index into the list of ports as
+ returned via the protocol.
+ */
+
+
+static int
+rapdu_status_to_sw (int status)
+{
+ int rc;
+
+ switch (status)
+ {
+ case RAPDU_STATUS_SUCCESS: rc = 0; break;
+
+ case RAPDU_STATUS_INVCMD:
+ case RAPDU_STATUS_INVPROT:
+ case RAPDU_STATUS_INVSEQ:
+ case RAPDU_STATUS_INVCOOKIE:
+ case RAPDU_STATUS_INVREADER: rc = SW_HOST_INV_VALUE; break;
+
+ case RAPDU_STATUS_TIMEOUT: rc = SW_HOST_CARD_IO_ERROR; break;
+ case RAPDU_STATUS_CARDIO: rc = SW_HOST_CARD_IO_ERROR; break;
+ case RAPDU_STATUS_NOCARD: rc = SW_HOST_NO_CARD; break;
+ case RAPDU_STATUS_CARDCHG: rc = SW_HOST_NO_CARD; break;
+ case RAPDU_STATUS_BUSY: rc = SW_HOST_BUSY; break;
+ case RAPDU_STATUS_NEEDRESET: rc = SW_HOST_CARD_INACTIVE; break;
+
+ default: rc = SW_HOST_GENERAL_ERROR; break;
+ }
+
+ return rc;
+}
+
+
+
+static int
+close_rapdu_reader (int slot)
+{
+ rapdu_release (reader_table[slot].rapdu.handle);
+ reader_table[slot].used = 0;
+ return 0;
+}
+
+
+static int
+reset_rapdu_reader (int slot)
+{
+ int err;
+ reader_table_t slotp;
+ rapdu_msg_t msg = NULL;
+
+ slotp = reader_table + slot;
+
+ err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_RESET);
+ if (err)
+ {
+ log_error ("sending rapdu command RESET failed: %s\n",
+ err < 0 ? strerror (errno): rapdu_strerror (err));
+ rapdu_msg_release (msg);
+ return rapdu_status_to_sw (err);
+ }
+ err = rapdu_read_msg (slotp->rapdu.handle, &msg);
+ if (err)
+ {
+ log_error ("receiving rapdu message failed: %s\n",
+ err < 0 ? strerror (errno): rapdu_strerror (err));
+ rapdu_msg_release (msg);
+ return rapdu_status_to_sw (err);
+ }
+ if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
+ {
+ int sw = rapdu_status_to_sw (msg->cmd);
+ log_error ("rapdu command RESET failed: %s\n",
+ rapdu_strerror (msg->cmd));
+ rapdu_msg_release (msg);
+ return sw;
+ }
+ if (msg->datalen >= DIM (slotp->atr))
+ {
+ log_error ("ATR returned by the RAPDU layer is too large\n");
+ rapdu_msg_release (msg);
+ return SW_HOST_INV_VALUE;
+ }
+ slotp->atrlen = msg->datalen;
+ memcpy (slotp->atr, msg->data, msg->datalen);
+
+ rapdu_msg_release (msg);
+ return 0;
+}
+
+
+static int
+my_rapdu_get_status (int slot, unsigned int *status)
+{
+ int err;
+ reader_table_t slotp;
+ rapdu_msg_t msg = NULL;
+ int oldslot;
+
+ slotp = reader_table + slot;
+
+ oldslot = rapdu_set_reader (slotp->rapdu.handle, slot);
+ err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_GET_STATUS);
+ rapdu_set_reader (slotp->rapdu.handle, oldslot);
+ if (err)
+ {
+ log_error ("sending rapdu command GET_STATUS failed: %s\n",
+ err < 0 ? strerror (errno): rapdu_strerror (err));
+ return rapdu_status_to_sw (err);
+ }
+ err = rapdu_read_msg (slotp->rapdu.handle, &msg);
+ if (err)
+ {
+ log_error ("receiving rapdu message failed: %s\n",
+ err < 0 ? strerror (errno): rapdu_strerror (err));
+ rapdu_msg_release (msg);
+ return rapdu_status_to_sw (err);
+ }
+ if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
+ {
+ int sw = rapdu_status_to_sw (msg->cmd);
+ log_error ("rapdu command GET_STATUS failed: %s\n",
+ rapdu_strerror (msg->cmd));
+ rapdu_msg_release (msg);
+ return sw;
+ }
+ *status = msg->data[0];
+
+ rapdu_msg_release (msg);
+ return 0;
+}
+
+
+/* Actually send the APDU of length APDULEN to SLOT and return a
+ maximum of *BUFLEN data in BUFFER, the actual returned size will be
+ set to BUFLEN. Returns: APDU error code. */
+static int
+my_rapdu_send_apdu (int slot, unsigned char *apdu, size_t apdulen,
+ unsigned char *buffer, size_t *buflen,
+ struct pininfo_s *pininfo)
+{
+ int err;
+ reader_table_t slotp;
+ rapdu_msg_t msg = NULL;
+ size_t maxlen = *buflen;
+
+ slotp = reader_table + slot;
+
+ *buflen = 0;
+ if (DBG_CARD_IO)
+ log_printhex (" APDU_data:", apdu, apdulen);
+
+ if (apdulen < 4)
+ {
+ log_error ("rapdu_send_apdu: APDU is too short\n");
+ return SW_HOST_INV_VALUE;
+ }
+
+ err = rapdu_send_apdu (slotp->rapdu.handle, apdu, apdulen);
+ if (err)
+ {
+ log_error ("sending rapdu command APDU failed: %s\n",
+ err < 0 ? strerror (errno): rapdu_strerror (err));
+ rapdu_msg_release (msg);
+ return rapdu_status_to_sw (err);
+ }
+ err = rapdu_read_msg (slotp->rapdu.handle, &msg);
+ if (err)
+ {
+ log_error ("receiving rapdu message failed: %s\n",
+ err < 0 ? strerror (errno): rapdu_strerror (err));
+ rapdu_msg_release (msg);
+ return rapdu_status_to_sw (err);
+ }
+ if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
+ {
+ int sw = rapdu_status_to_sw (msg->cmd);
+ log_error ("rapdu command APDU failed: %s\n",
+ rapdu_strerror (msg->cmd));
+ rapdu_msg_release (msg);
+ return sw;
+ }
+
+ if (msg->datalen > maxlen)
+ {
+ log_error ("rapdu response apdu too large\n");
+ rapdu_msg_release (msg);
+ return SW_HOST_INV_VALUE;
+ }
+
+ *buflen = msg->datalen;
+ memcpy (buffer, msg->data, msg->datalen);
+
+ rapdu_msg_release (msg);
+ return 0;
+}
+
+static int
+open_rapdu_reader (int portno,
+ const unsigned char *cookie, size_t length,
+ int (*readfnc) (void *opaque,
+ void *buffer, size_t size),
+ void *readfnc_value,
+ int (*writefnc) (void *opaque,
+ const void *buffer, size_t size),
+ void *writefnc_value,
+ void (*closefnc) (void *opaque),
+ void *closefnc_value)
+{
+ int err;
+ int slot;
+ reader_table_t slotp;
+ rapdu_msg_t msg = NULL;
+
+ slot = new_reader_slot ();
+ if (slot == -1)
+ return -1;
+ slotp = reader_table + slot;
+
+ slotp->rapdu.handle = rapdu_new ();
+ if (!slotp->rapdu.handle)
+ {
+ slotp->used = 0;
+ return -1;
+ }
+
+ rapdu_set_reader (slotp->rapdu.handle, portno);
+
+ rapdu_set_iofunc (slotp->rapdu.handle,
+ readfnc, readfnc_value,
+ writefnc, writefnc_value,
+ closefnc, closefnc_value);
+ rapdu_set_cookie (slotp->rapdu.handle, cookie, length);
+
+ /* First try to get the current ATR, but if the card is inactive
+ issue a reset instead. */
+ err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_GET_ATR);
+ if (err == RAPDU_STATUS_NEEDRESET)
+ err = rapdu_send_cmd (slotp->rapdu.handle, RAPDU_CMD_RESET);
+ if (err)
+ {
+ log_info ("sending rapdu command GET_ATR/RESET failed: %s\n",
+ err < 0 ? strerror (errno): rapdu_strerror (err));
+ goto failure;
+ }
+ err = rapdu_read_msg (slotp->rapdu.handle, &msg);
+ if (err)
+ {
+ log_info ("receiving rapdu message failed: %s\n",
+ err < 0 ? strerror (errno): rapdu_strerror (err));
+ goto failure;
+ }
+ if (msg->cmd != RAPDU_STATUS_SUCCESS || !msg->datalen)
+ {
+ log_info ("rapdu command GET ATR failed: %s\n",
+ rapdu_strerror (msg->cmd));
+ goto failure;
+ }
+ if (msg->datalen >= DIM (slotp->atr))
+ {
+ log_error ("ATR returned by the RAPDU layer is too large\n");
+ goto failure;
+ }
+ slotp->atrlen = msg->datalen;
+ memcpy (slotp->atr, msg->data, msg->datalen);
+
+ reader_table[slot].close_reader = close_rapdu_reader;
+ reader_table[slot].reset_reader = reset_rapdu_reader;
+ reader_table[slot].get_status_reader = my_rapdu_get_status;
+ reader_table[slot].send_apdu_reader = my_rapdu_send_apdu;
+ reader_table[slot].check_keypad = NULL;
+ reader_table[slot].dump_status_reader = NULL;
+
+ dump_reader_status (slot);
+ rapdu_msg_release (msg);
+ return slot;
+
+ failure:
+ rapdu_msg_release (msg);
+ rapdu_release (slotp->rapdu.handle);
+ slotp->used = 0;
+ return -1;
+}
+
+#endif /*USE_G10CODE_RAPDU*/
+
+
+
+/*
+ Driver Access
+ */
+
+
+static int
+lock_slot (int slot)
+{
+#ifdef USE_GNU_PTH
+ if (!pth_mutex_acquire (&reader_table[slot].lock, 0, NULL))
+ {
+ log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
+ return SW_HOST_LOCKING_FAILED;
+ }
+#endif /*USE_GNU_PTH*/
+ return 0;
+}
+
+static int
+trylock_slot (int slot)
+{
+#ifdef USE_GNU_PTH
+ if (!pth_mutex_acquire (&reader_table[slot].lock, TRUE, NULL))
+ {
+ if (errno == EBUSY)
+ return SW_HOST_BUSY;
+ log_error ("failed to acquire apdu lock: %s\n", strerror (errno));
+ return SW_HOST_LOCKING_FAILED;
+ }
+#endif /*USE_GNU_PTH*/
+ return 0;
+}
+
+static void
+unlock_slot (int slot)
+{
+#ifdef USE_GNU_PTH
+ if (!pth_mutex_release (&reader_table[slot].lock))
+ log_error ("failed to release apdu lock: %s\n", strerror (errno));
+#endif /*USE_GNU_PTH*/
+}
+
+
+/* Open the reader and return an internal slot number or -1 on
+ error. If PORTSTR is NULL we default to a suitable port (for ctAPI:
+ the first USB reader. For PC/SC the first listed reader). */
+int
+apdu_open_reader (const char *portstr)
+{
+ static int pcsc_api_loaded, ct_api_loaded;
+
+#ifdef HAVE_LIBUSB
+ if (!opt.disable_ccid)
+ {
+ int slot, i;
+ const char *s;
+
+ slot = open_ccid_reader (portstr);
+ if (slot != -1)
+ return slot; /* got one */
+
+ /* If a CCID reader specification has been given, the user does
+ not want a fallback to other drivers. */
+ if (portstr)
+ for (s=portstr, i=0; *s; s++)
+ if (*s == ':' && (++i == 3))
+ return -1;
+ }
+
+#endif /* HAVE_LIBUSB */
+
+ if (opt.ctapi_driver && *opt.ctapi_driver)
+ {
+ int port = portstr? atoi (portstr) : 32768;
+
+ if (!ct_api_loaded)
+ {
+ void *handle;
+
+ handle = dlopen (opt.ctapi_driver, RTLD_LAZY);
+ if (!handle)
+ {
+ log_error ("apdu_open_reader: failed to open driver: %s\n",
+ dlerror ());
+ return -1;
+ }
+ CT_init = dlsym (handle, "CT_init");
+ CT_data = dlsym (handle, "CT_data");
+ CT_close = dlsym (handle, "CT_close");
+ if (!CT_init || !CT_data || !CT_close)
+ {
+ log_error ("apdu_open_reader: invalid CT-API driver\n");
+ dlclose (handle);
+ return -1;
+ }
+ ct_api_loaded = 1;
+ }
+ return open_ct_reader (port);
+ }
+
+
+ /* No ctAPI configured, so lets try the PC/SC API */
+ if (!pcsc_api_loaded)
+ {
+#ifndef NEED_PCSC_WRAPPER
+ void *handle;
+
+ handle = dlopen (opt.pcsc_driver, RTLD_LAZY);
+ if (!handle)
+ {
+ log_error ("apdu_open_reader: failed to open driver `%s': %s\n",
+ opt.pcsc_driver, dlerror ());
+ return -1;
+ }
+
+ pcsc_establish_context = dlsym (handle, "SCardEstablishContext");
+ pcsc_release_context = dlsym (handle, "SCardReleaseContext");
+ pcsc_list_readers = dlsym (handle, "SCardListReaders");
+#if defined(_WIN32) || defined(__CYGWIN__)
+ if (!pcsc_list_readers)
+ pcsc_list_readers = dlsym (handle, "SCardListReadersA");
+#endif
+ pcsc_get_status_change = dlsym (handle, "SCardGetStatusChange");
+#if defined(_WIN32) || defined(__CYGWIN__)
+ if (!pcsc_get_status_change)
+ pcsc_get_status_change = dlsym (handle, "SCardGetStatusChangeA");
+#endif
+ pcsc_connect = dlsym (handle, "SCardConnect");
+#if defined(_WIN32) || defined(__CYGWIN__)
+ if (!pcsc_connect)
+ pcsc_connect = dlsym (handle, "SCardConnectA");
+#endif
+ pcsc_reconnect = dlsym (handle, "SCardReconnect");
+#if defined(_WIN32) || defined(__CYGWIN__)
+ if (!pcsc_reconnect)
+ pcsc_reconnect = dlsym (handle, "SCardReconnectA");
+#endif
+ pcsc_disconnect = dlsym (handle, "SCardDisconnect");
+ pcsc_status = dlsym (handle, "SCardStatus");
+#if defined(_WIN32) || defined(__CYGWIN__)
+ if (!pcsc_status)
+ pcsc_status = dlsym (handle, "SCardStatusA");
+#endif
+ pcsc_begin_transaction = dlsym (handle, "SCardBeginTransaction");
+ pcsc_end_transaction = dlsym (handle, "SCardEndTransaction");
+ pcsc_transmit = dlsym (handle, "SCardTransmit");
+ pcsc_set_timeout = dlsym (handle, "SCardSetTimeout");
+
+ if (!pcsc_establish_context
+ || !pcsc_release_context
+ || !pcsc_list_readers
+ || !pcsc_get_status_change
+ || !pcsc_connect
+ || !pcsc_reconnect
+ || !pcsc_disconnect
+ || !pcsc_status
+ || !pcsc_begin_transaction
+ || !pcsc_end_transaction
+ || !pcsc_transmit
+ /* || !pcsc_set_timeout */)
+ {
+ /* Note that set_timeout is currently not used and also not
+ available under Windows. */
+ log_error ("apdu_open_reader: invalid PC/SC driver "
+ "(%d%d%d%d%d%d%d%d%d%d%d%d)\n",
+ !!pcsc_establish_context,
+ !!pcsc_release_context,
+ !!pcsc_list_readers,
+ !!pcsc_get_status_change,
+ !!pcsc_connect,
+ !!pcsc_reconnect,
+ !!pcsc_disconnect,
+ !!pcsc_status,
+ !!pcsc_begin_transaction,
+ !!pcsc_end_transaction,
+ !!pcsc_transmit,
+ !!pcsc_set_timeout );
+ dlclose (handle);
+ return -1;
+ }
+#endif /*!NEED_PCSC_WRAPPER*/
+ pcsc_api_loaded = 1;
+ }
+
+ return open_pcsc_reader (portstr);
+}
+
+
+/* Open an remote reader and return an internal slot number or -1 on
+ error. This function is an alternative to apdu_open_reader and used
+ with remote readers only. Note that the supplied CLOSEFNC will
+ only be called once and the slot will not be valid afther this.
+
+ If PORTSTR is NULL we default to the first availabe port.
+*/
+int
+apdu_open_remote_reader (const char *portstr,
+ const unsigned char *cookie, size_t length,
+ int (*readfnc) (void *opaque,
+ void *buffer, size_t size),
+ void *readfnc_value,
+ int (*writefnc) (void *opaque,
+ const void *buffer, size_t size),
+ void *writefnc_value,
+ void (*closefnc) (void *opaque),
+ void *closefnc_value)
+{
+#ifdef USE_G10CODE_RAPDU
+ return open_rapdu_reader (portstr? atoi (portstr) : 0,
+ cookie, length,
+ readfnc, readfnc_value,
+ writefnc, writefnc_value,
+ closefnc, closefnc_value);
+#else
+#ifdef _WIN32
+ errno = ENOENT;
+#else
+ errno = ENOSYS;
+#endif
+ return -1;
+#endif
+}
+
+
+int
+apdu_close_reader (int slot)
+{
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+ if (reader_table[slot].close_reader)
+ return reader_table[slot].close_reader (slot);
+ return SW_HOST_NOT_SUPPORTED;
+}
+
+/* Shutdown a reader; that is basically the same as a close but keeps
+ the handle ready for later use. A apdu_reset_header should be used
+ to get it active again. */
+int
+apdu_shutdown_reader (int slot)
+{
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+ if (reader_table[slot].shutdown_reader)
+ return reader_table[slot].shutdown_reader (slot);
+ return SW_HOST_NOT_SUPPORTED;
+}
+
+/* Enumerate all readers and return information on whether this reader
+ is in use. The caller should start with SLOT set to 0 and
+ increment it with each call until an error is returned. */
+int
+apdu_enum_reader (int slot, int *used)
+{
+ if (slot < 0 || slot >= MAX_READER)
+ return SW_HOST_NO_DRIVER;
+ *used = reader_table[slot].used;
+ return 0;
+}
+
+/* Do a reset for the card in reader at SLOT. */
+int
+apdu_reset (int slot)
+{
+ int sw;
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if ((sw = lock_slot (slot)))
+ return sw;
+
+ reader_table[slot].last_status = 0;
+ if (reader_table[slot].reset_reader)
+ sw = reader_table[slot].reset_reader (slot);
+
+ if (!sw)
+ {
+ /* If we got to here we know that a card is present
+ and usable. Thus remember this. */
+ reader_table[slot].last_status = (1|2|4| 0x8000);
+ }
+
+ unlock_slot (slot);
+ return sw;
+}
+
+
+/* Activate a card if it has not yet been done. This is a kind of
+ reset-if-required. It is useful to test for presence of a card
+ before issuing a bunch of apdu commands. It does not wait on a
+ locked card. */
+int
+apdu_activate (int slot)
+{
+ int sw;
+ unsigned int s;
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if ((sw = trylock_slot (slot)))
+ return sw;
+
+ if (reader_table[slot].get_status_reader)
+ sw = reader_table[slot].get_status_reader (slot, &s);
+
+ if (!sw)
+ {
+ if (!(s & 2)) /* Card not present. */
+ sw = SW_HOST_NO_CARD;
+ else if ( ((s & 2) && !(s & 4))
+ || !reader_table[slot].atrlen )
+ {
+ /* We don't have an ATR or a card is present though inactive:
+ do a reset now. */
+ if (reader_table[slot].reset_reader)
+ {
+ reader_table[slot].last_status = 0;
+ sw = reader_table[slot].reset_reader (slot);
+ if (!sw)
+ {
+ /* If we got to here we know that a card is present
+ and usable. Thus remember this. */
+ reader_table[slot].last_status = (1|2|4| 0x8000);
+ }
+ }
+ }
+ }
+
+ unlock_slot (slot);
+ return sw;
+}
+
+
+
+unsigned char *
+apdu_get_atr (int slot, size_t *atrlen)
+{
+ unsigned char *buf;
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return NULL;
+
+ buf = xtrymalloc (reader_table[slot].atrlen);
+ if (!buf)
+ return NULL;
+ memcpy (buf, reader_table[slot].atr, reader_table[slot].atrlen);
+ *atrlen = reader_table[slot].atrlen;
+ return buf;
+}
+
+
+
+/* Retrieve the status for SLOT. The function does only wait for the
+ card to become available if HANG is set to true. On success the
+ bits in STATUS will be set to
+
+ bit 0 = card present and usable
+ bit 1 = card present
+ bit 2 = card active
+ bit 3 = card access locked [not yet implemented]
+
+ For must application, testing bit 0 is sufficient.
+
+ CHANGED will receive the value of the counter tracking the number
+ of card insertions. This value may be used to detect a card
+ change.
+*/
+int
+apdu_get_status (int slot, int hang,
+ unsigned int *status, unsigned int *changed)
+{
+ int sw;
+ unsigned int s;
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if ((sw = hang? lock_slot (slot) : trylock_slot (slot)))
+ return sw;
+
+ if (reader_table[slot].get_status_reader)
+ sw = reader_table[slot].get_status_reader (slot, &s);
+
+ unlock_slot (slot);
+
+ if (sw)
+ {
+ reader_table[slot].last_status = 0;
+ return sw;
+ }
+
+ /* Keep track of changes. We use one extra bit to test whether we
+ have checked the status at least once. */
+ if ( s != (reader_table[slot].last_status & 0x07ff)
+ || !reader_table[slot].last_status )
+ {
+ reader_table[slot].change_counter++;
+ /* Make sure that the ATR is invalid so that a reset will be by
+ activate. */
+ reader_table[slot].atrlen = 0;
+ }
+ reader_table[slot].last_status = (s | 0x8000);
+
+ if (status)
+ *status = s;
+ if (changed)
+ *changed = reader_table[slot].change_counter;
+ return 0;
+}
+
+
+/* Check whether the reader supports the ISO command code COMMAND on
+ the keypad. Return 0 on success. For a description of the pin
+ parameters, see ccid-driver.c */
+int
+apdu_check_keypad (int slot, int command, int pin_mode,
+ int pinlen_min, int pinlen_max, int pin_padlen)
+{
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if (reader_table[slot].check_keypad)
+ return reader_table[slot].check_keypad (slot, command,
+ pin_mode, pinlen_min, pinlen_max,
+ pin_padlen);
+ else
+ return SW_HOST_NOT_SUPPORTED;
+}
+
+
+/* Dispatcher for the actual send_apdu function. Note, that this
+ function should be called in locked state. */
+static int
+send_apdu (int slot, unsigned char *apdu, size_t apdulen,
+ unsigned char *buffer, size_t *buflen, struct pininfo_s *pininfo)
+{
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if (reader_table[slot].send_apdu_reader)
+ return reader_table[slot].send_apdu_reader (slot,
+ apdu, apdulen,
+ buffer, buflen, pininfo);
+ else
+ return SW_HOST_NOT_SUPPORTED;
+}
+
+
+/* Core APDU trabceiver function. Parameters are described at
+ apdu_send_le with the exception of PININFO which indicates keypad
+ related operations if not NULL. */
+static int
+send_le (int slot, int class, int ins, int p0, int p1,
+ int lc, const char *data, int le,
+ unsigned char **retbuf, size_t *retbuflen,
+ struct pininfo_s *pininfo)
+{
+#define RESULTLEN 256
+ unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
+ the driver. */
+ size_t resultlen;
+ unsigned char apdu[5+256+1];
+ size_t apdulen;
+ int sw;
+ long rc; /* we need a long here due to PC/SC. */
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if (DBG_CARD_IO)
+ log_debug ("send apdu: c=%02X i=%02X p0=%02X p1=%02X lc=%d le=%d\n",
+ class, ins, p0, p1, lc, le);
+
+ if (lc != -1 && (lc > 255 || lc < 0))
+ return SW_WRONG_LENGTH;
+ if (le != -1 && (le > 256 || le < 0))
+ return SW_WRONG_LENGTH;
+ if ((!data && lc != -1) || (data && lc == -1))
+ return SW_HOST_INV_VALUE;
+
+ if ((sw = lock_slot (slot)))
+ return sw;
+
+ apdulen = 0;
+ apdu[apdulen++] = class;
+ apdu[apdulen++] = ins;
+ apdu[apdulen++] = p0;
+ apdu[apdulen++] = p1;
+ if (lc != -1)
+ {
+ apdu[apdulen++] = lc;
+ memcpy (apdu+apdulen, data, lc);
+ apdulen += lc;
+ /* T=0 does not allow the use of Lc together with Le; thus
+ disable Le in this case. */
+ if (reader_table[slot].is_t0)
+ le = -1;
+ }
+ if (le != -1)
+ apdu[apdulen++] = le; /* Truncation is okay because 0 means 256. */
+ assert (sizeof (apdu) >= apdulen);
+ /* As safeguard don't pass any garbage from the stack to the driver. */
+ memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
+ resultlen = RESULTLEN;
+ rc = send_apdu (slot, apdu, apdulen, result, &resultlen, pininfo);
+ if (rc || resultlen < 2)
+ {
+ log_error ("apdu_send_simple(%d) failed: %s\n",
+ slot, apdu_strerror (rc));
+ unlock_slot (slot);
+ return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+ }
+ sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ /* store away the returned data but strip the statusword. */
+ resultlen -= 2;
+ if (DBG_CARD_IO)
+ {
+ log_debug (" response: sw=%04X datalen=%d\n",
+ sw, (unsigned int)resultlen);
+ if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA))
+ log_printhex (" dump: ", result, resultlen);
+ }
+
+ if (sw == SW_SUCCESS || sw == SW_EOF_REACHED)
+ {
+ if (retbuf)
+ {
+ *retbuf = xtrymalloc (resultlen? resultlen : 1);
+ if (!*retbuf)
+ {
+ unlock_slot (slot);
+ return SW_HOST_OUT_OF_CORE;
+ }
+ *retbuflen = resultlen;
+ memcpy (*retbuf, result, resultlen);
+ }
+ }
+ else if ((sw & 0xff00) == SW_MORE_DATA)
+ {
+ unsigned char *p = NULL, *tmp;
+ size_t bufsize = 4096;
+
+ /* It is likely that we need to return much more data, so we
+ start off with a large buffer. */
+ if (retbuf)
+ {
+ *retbuf = p = xtrymalloc (bufsize);
+ if (!*retbuf)
+ {
+ unlock_slot (slot);
+ return SW_HOST_OUT_OF_CORE;
+ }
+ assert (resultlen < bufsize);
+ memcpy (p, result, resultlen);
+ p += resultlen;
+ }
+
+ do
+ {
+ int len = (sw & 0x00ff);
+
+ if (DBG_CARD_IO)
+ log_debug ("apdu_send_simple(%d): %d more bytes available\n",
+ slot, len);
+ apdulen = 0;
+ apdu[apdulen++] = class;
+ apdu[apdulen++] = 0xC0;
+ apdu[apdulen++] = 0;
+ apdu[apdulen++] = 0;
+ apdu[apdulen++] = len;
+ memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
+ resultlen = RESULTLEN;
+ rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
+ if (rc || resultlen < 2)
+ {
+ log_error ("apdu_send_simple(%d) for get response failed: %s\n",
+ slot, apdu_strerror (rc));
+ unlock_slot (slot);
+ return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+ }
+ sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ resultlen -= 2;
+ if (DBG_CARD_IO)
+ {
+ log_debug (" more: sw=%04X datalen=%d\n",
+ sw, (unsigned int)resultlen);
+ if (!retbuf && (sw==SW_SUCCESS || (sw&0xff00)==SW_MORE_DATA))
+ log_printhex (" dump: ", result, resultlen);
+ }
+
+ if ((sw & 0xff00) == SW_MORE_DATA
+ || sw == SW_SUCCESS
+ || sw == SW_EOF_REACHED )
+ {
+ if (retbuf && resultlen)
+ {
+ if (p - *retbuf + resultlen > bufsize)
+ {
+ bufsize += resultlen > 4096? resultlen: 4096;
+ tmp = xtryrealloc (*retbuf, bufsize);
+ if (!tmp)
+ {
+ unlock_slot (slot);
+ return SW_HOST_OUT_OF_CORE;
+ }
+ p = tmp + (p - *retbuf);
+ *retbuf = tmp;
+ }
+ memcpy (p, result, resultlen);
+ p += resultlen;
+ }
+ }
+ else
+ log_info ("apdu_send_simple(%d) "
+ "got unexpected status %04X from get response\n",
+ slot, sw);
+ }
+ while ((sw & 0xff00) == SW_MORE_DATA);
+
+ if (retbuf)
+ {
+ *retbuflen = p - *retbuf;
+ tmp = xtryrealloc (*retbuf, *retbuflen);
+ if (tmp)
+ *retbuf = tmp;
+ }
+ }
+
+ unlock_slot (slot);
+
+ if (DBG_CARD_IO && retbuf && sw == SW_SUCCESS)
+ log_printhex (" dump: ", *retbuf, *retbuflen);
+
+ return sw;
+#undef RESULTLEN
+}
+
+/* Send an APDU to the card in SLOT. The APDU is created from all
+ given parameters: CLASS, INS, P0, P1, LC, DATA, LE. A value of -1
+ for LC won't sent this field and the data field; in this case DATA
+ must also be passed as NULL. The return value is the status word
+ or -1 for an invalid SLOT or other non card related error. If
+ RETBUF is not NULL, it will receive an allocated buffer with the
+ returned data. The length of that data will be put into
+ *RETBUFLEN. The caller is reponsible for releasing the buffer even
+ in case of errors. */
+int
+apdu_send_le(int slot, int class, int ins, int p0, int p1,
+ int lc, const char *data, int le,
+ unsigned char **retbuf, size_t *retbuflen)
+{
+ return send_le (slot, class, ins, p0, p1,
+ lc, data, le,
+ retbuf, retbuflen,
+ NULL);
+}
+
+
+/* Send an APDU to the card in SLOT. The APDU is created from all
+ given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for
+ LC won't sent this field and the data field; in this case DATA must
+ also be passed as NULL. The return value is the status word or -1
+ for an invalid SLOT or other non card related error. If RETBUF is
+ not NULL, it will receive an allocated buffer with the returned
+ data. The length of that data will be put into *RETBUFLEN. The
+ caller is reponsible for releasing the buffer even in case of
+ errors. */
+int
+apdu_send (int slot, int class, int ins, int p0, int p1,
+ int lc, const char *data, unsigned char **retbuf, size_t *retbuflen)
+{
+ return send_le (slot, class, ins, p0, p1, lc, data, 256,
+ retbuf, retbuflen, NULL);
+}
+
+/* Send an APDU to the card in SLOT. The APDU is created from all
+ given parameters: CLASS, INS, P0, P1, LC, DATA. A value of -1 for
+ LC won't sent this field and the data field; in this case DATA must
+ also be passed as NULL. The return value is the status word or -1
+ for an invalid SLOT or other non card related error. No data will be
+ returned. */
+int
+apdu_send_simple (int slot, int class, int ins, int p0, int p1,
+ int lc, const char *data)
+{
+ return send_le (slot, class, ins, p0, p1, lc, data, -1, NULL, NULL, NULL);
+}
+
+
+/* Same as apdu_send_simple but uses the keypad of the reader. */
+int
+apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
+ int lc, const char *data,
+ int pin_mode,
+ int pinlen_min, int pinlen_max, int pin_padlen)
+{
+ struct pininfo_s pininfo;
+
+ pininfo.mode = pin_mode;
+ pininfo.minlen = pinlen_min;
+ pininfo.maxlen = pinlen_max;
+ pininfo.padlen = pin_padlen;
+ return send_le (slot, class, ins, p0, p1, lc, data, -1,
+ NULL, NULL, &pininfo);
+}
+
+
+/* This is a more generic version of the apdu sending routine. It
+ takes an already formatted APDU in APDUDATA or length APDUDATALEN
+ and returns the with the APDU including the status word. With
+ HANDLE_MORE set to true this function will handle the MORE DATA
+ status and return all APDUs concatenated with one status word at
+ the end. The function does not return a regular status word but 0
+ on success. If the slot is locked, the fucntion returns
+ immediately.*/
+int
+apdu_send_direct (int slot, const unsigned char *apdudata, size_t apdudatalen,
+ int handle_more,
+ unsigned char **retbuf, size_t *retbuflen)
+{
+#define RESULTLEN 256
+ unsigned char apdu[5+256+1];
+ size_t apdulen;
+ unsigned char result[RESULTLEN+10]; /* 10 extra in case of bugs in
+ the driver. */
+ size_t resultlen;
+ int sw;
+ long rc; /* we need a long here due to PC/SC. */
+ int class;
+
+ if (slot < 0 || slot >= MAX_READER || !reader_table[slot].used )
+ return SW_HOST_NO_DRIVER;
+
+ if ((sw = trylock_slot (slot)))
+ return sw;
+
+ /* We simply trunctate a too long APDU. */
+ if (apdudatalen > sizeof apdu)
+ apdudatalen = sizeof apdu;
+ apdulen = apdudatalen;
+ memcpy (apdu, apdudata, apdudatalen);
+ class = apdulen? *apdu : 0;
+
+ resultlen = RESULTLEN;
+ rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
+ if (rc || resultlen < 2)
+ {
+ log_error ("apdu_send_direct(%d) failed: %s\n",
+ slot, apdu_strerror (rc));
+ unlock_slot (slot);
+ return rc? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+ }
+ sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ /* Store away the returned data but strip the statusword. */
+ resultlen -= 2;
+ if (DBG_CARD_IO)
+ {
+ log_debug (" response: sw=%04X datalen=%d\n",
+ sw, (unsigned int)resultlen);
+ if ( !retbuf && (sw == SW_SUCCESS || (sw & 0xff00) == SW_MORE_DATA))
+ log_printhex (" dump: ", result, resultlen);
+ }
+
+ if (handle_more && (sw & 0xff00) == SW_MORE_DATA)
+ {
+ unsigned char *p = NULL, *tmp;
+ size_t bufsize = 4096;
+
+ /* It is likely that we need to return much more data, so we
+ start off with a large buffer. */
+ if (retbuf)
+ {
+ *retbuf = p = xtrymalloc (bufsize + 2);
+ if (!*retbuf)
+ {
+ unlock_slot (slot);
+ return SW_HOST_OUT_OF_CORE;
+ }
+ assert (resultlen < bufsize);
+ memcpy (p, result, resultlen);
+ p += resultlen;
+ }
+
+ do
+ {
+ int len = (sw & 0x00ff);
+
+ if (DBG_CARD_IO)
+ log_debug ("apdu_send_direct(%d): %d more bytes available\n",
+ slot, len);
+ apdulen = 0;
+ apdu[apdulen++] = class;
+ apdu[apdulen++] = 0xC0;
+ apdu[apdulen++] = 0;
+ apdu[apdulen++] = 0;
+ apdu[apdulen++] = len;
+ memset (apdu+apdulen, 0, sizeof (apdu) - apdulen);
+ resultlen = RESULTLEN;
+ rc = send_apdu (slot, apdu, apdulen, result, &resultlen, NULL);
+ if (rc || resultlen < 2)
+ {
+ log_error ("apdu_send_direct(%d) for get response failed: %s\n",
+ slot, apdu_strerror (rc));
+ unlock_slot (slot);
+ return rc ? rc : SW_HOST_INCOMPLETE_CARD_RESPONSE;
+ }
+ sw = (result[resultlen-2] << 8) | result[resultlen-1];
+ resultlen -= 2;
+ if (DBG_CARD_IO)
+ {
+ log_debug (" more: sw=%04X datalen=%d\n",
+ sw, (unsigned int)resultlen);
+ if (!retbuf && (sw==SW_SUCCESS || (sw&0xff00)==SW_MORE_DATA))
+ log_printhex (" dump: ", result, resultlen);
+ }
+
+ if ((sw & 0xff00) == SW_MORE_DATA
+ || sw == SW_SUCCESS
+ || sw == SW_EOF_REACHED )
+ {
+ if (retbuf && resultlen)
+ {
+ if (p - *retbuf + resultlen > bufsize)
+ {
+ bufsize += resultlen > 4096? resultlen: 4096;
+ tmp = xtryrealloc (*retbuf, bufsize + 2);
+ if (!tmp)
+ {
+ unlock_slot (slot);
+ return SW_HOST_OUT_OF_CORE;
+ }
+ p = tmp + (p - *retbuf);
+ *retbuf = tmp;
+ }
+ memcpy (p, result, resultlen);
+ p += resultlen;
+ }
+ }
+ else
+ log_info ("apdu_send_sdirect(%d) "
+ "got unexpected status %04X from get response\n",
+ slot, sw);
+ }
+ while ((sw & 0xff00) == SW_MORE_DATA);
+
+ if (retbuf)
+ {
+ *retbuflen = p - *retbuf;
+ tmp = xtryrealloc (*retbuf, *retbuflen + 2);
+ if (tmp)
+ *retbuf = tmp;
+ }
+ }
+ else
+ {
+ if (retbuf)
+ {
+ *retbuf = xtrymalloc ((resultlen? resultlen : 1)+2);
+ if (!*retbuf)
+ {
+ unlock_slot (slot);
+ return SW_HOST_OUT_OF_CORE;
+ }
+ *retbuflen = resultlen;
+ memcpy (*retbuf, result, resultlen);
+ }
+ }
+
+ unlock_slot (slot);
+
+ /* Append the status word - we reseved the two extra bytes while
+ allocating the buffer. */
+ if (retbuf)
+ {
+ (*retbuf)[(*retbuflen)++] = (sw >> 8);
+ (*retbuf)[(*retbuflen)++] = sw;
+ }
+
+ if (DBG_CARD_IO && retbuf)
+ log_printhex (" dump: ", *retbuf, *retbuflen);
+
+ return 0;
+#undef RESULTLEN
+}
diff --git a/g10/apdu.h b/g10/apdu.h
new file mode 100644
index 0000000..d8e3b36
--- /dev/null
+++ b/g10/apdu.h
@@ -0,0 +1,123 @@
+/* apdu.h - ISO 7816 APDU functions and low level I/O
+ * Copyright (C) 2003 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ * $Id: apdu.h 3916 2005-10-27 09:14:27Z wk $
+ */
+
+#ifndef APDU_H
+#define APDU_H
+
+/* ISO 7816 values for the statusword are defined here because they
+ should not be visible to the users of the actual ISO command
+ API. */
+enum {
+ SW_MORE_DATA = 0x6100, /* Note: that the low byte must be
+ masked of.*/
+ SW_EOF_REACHED = 0x6282,
+ SW_EEPROM_FAILURE = 0x6581,
+ SW_WRONG_LENGTH = 0x6700,
+ SW_CHV_WRONG = 0x6982,
+ SW_CHV_BLOCKED = 0x6983,
+ SW_USE_CONDITIONS = 0x6985,
+ SW_BAD_PARAMETER = 0x6a80, /* (in the data field) */
+ SW_NOT_SUPPORTED = 0x6a81,
+ SW_FILE_NOT_FOUND = 0x6a82,
+ SW_RECORD_NOT_FOUND = 0x6a83,
+ SW_REF_NOT_FOUND = 0x6a88,
+ SW_BAD_P0_P1 = 0x6b00,
+ SW_EXACT_LENGTH = 0x6c00,
+ SW_INS_NOT_SUP = 0x6d00,
+ SW_CLA_NOT_SUP = 0x6e00,
+ SW_SUCCESS = 0x9000,
+
+ /* The follwoing statuswords are no real ones but used to map host
+ OS errors into status words. A status word is 16 bit so that
+ those values can't be issued by a card. */
+ SW_HOST_OUT_OF_CORE = 0x10001, /* No way yet to differentiate
+ between errnos on a failed malloc. */
+ SW_HOST_INV_VALUE = 0x10002,
+ SW_HOST_INCOMPLETE_CARD_RESPONSE = 0x10003,
+ SW_HOST_NO_DRIVER = 0x10004,
+ SW_HOST_NOT_SUPPORTED = 0x10005,
+ SW_HOST_LOCKING_FAILED= 0x10006,
+ SW_HOST_BUSY = 0x10007,
+ SW_HOST_NO_CARD = 0x10008,
+ SW_HOST_CARD_INACTIVE = 0x10009,
+ SW_HOST_CARD_IO_ERROR = 0x1000a,
+ SW_HOST_GENERAL_ERROR = 0x1000b,
+ SW_HOST_NO_READER = 0x1000c,
+ SW_HOST_ABORTED = 0x1000d,
+ SW_HOST_NO_KEYPAD = 0x1000e
+};
+
+
+#define SW_EXACT_LENGTH_P(a) (((a)&~0xff) == SW_EXACT_LENGTH)
+
+
+/* Note , that apdu_open_reader returns no status word but -1 on error. */
+int apdu_open_reader (const char *portstr);
+int apdu_open_remote_reader (const char *portstr,
+ const unsigned char *cookie, size_t length,
+ int (*readfnc) (void *opaque,
+ void *buffer, size_t size),
+ void *readfnc_value,
+ int (*writefnc) (void *opaque,
+ const void *buffer, size_t size),
+ void *writefnc_value,
+ void (*closefnc) (void *opaque),
+ void *closefnc_value);
+int apdu_shutdown_reader (int slot);
+int apdu_close_reader (int slot);
+int apdu_enum_reader (int slot, int *used);
+unsigned char *apdu_get_atr (int slot, size_t *atrlen);
+
+const char *apdu_strerror (int rc);
+
+
+/* These apdu functions do return status words. */
+
+int apdu_activate (int slot);
+int apdu_reset (int slot);
+int apdu_get_status (int slot, int hang,
+ unsigned int *status, unsigned int *changed);
+int apdu_check_keypad (int slot, int command, int pin_mode,
+ int pinlen_min, int pinlen_max, int pin_padlen);
+int apdu_send_simple (int slot, int class, int ins, int p0, int p1,
+ int lc, const char *data);
+int apdu_send_simple_kp (int slot, int class, int ins, int p0, int p1,
+ int lc, const char *data,
+ int pin_mode,
+ int pinlen_min, int pinlen_max, int pin_padlen);
+int apdu_send (int slot, int class, int ins, int p0, int p1,
+ int lc, const char *data,
+ unsigned char **retbuf, size_t *retbuflen);
+int apdu_send_le (int slot, int class, int ins, int p0, int p1,
+ int lc, const char *data, int le,
+ unsigned char **retbuf, size_t *retbuflen);
+int apdu_send_direct (int slot,
+ const unsigned char *apdudata, size_t apdudatalen,
+ int handle_more,
+ unsigned char **retbuf, size_t *retbuflen);
+
+
+#endif /*APDU_H*/
+
+
+
diff --git a/g10/app-common.h b/g10/app-common.h
new file mode 100644
index 0000000..d517425
--- /dev/null
+++ b/g10/app-common.h
@@ -0,0 +1,197 @@
+/* app-common.h - Common declarations for all card applications
+ * Copyright (C) 2003, 2005 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ * $Id: app-common.h 3754 2005-05-31 10:11:01Z wk $
+ */
+
+#ifndef GNUPG_SCD_APP_COMMON_H
+#define GNUPG_SCD_APP_COMMON_H
+
+#if GNUPG_MAJOR_VERSION == 1
+# ifdef ENABLE_AGENT_SUPPORT
+# include "assuan.h"
+# endif
+#else
+# include <ksba.h>
+#endif
+
+
+struct app_local_s; /* Defined by all app-*.c. */
+
+struct app_ctx_s {
+ int initialized; /* The application has been initialied and the
+ function pointers may be used. Note that for
+ unsupported operations the particular
+ function pointer is set to NULL */
+ int slot; /* Used reader. */
+
+ /* If this is used by GnuPG 1.4 we need to know the assuan context
+ in case we need to divert the operation to an already running
+ agent. This if ASSUAN_CTX is not NULL we take this as indication
+ that all operations are diverted to gpg-agent. */
+#if GNUPG_MAJOR_VERSION == 1
+ assuan_context_t assuan_ctx;
+#endif /*GNUPG_MAJOR_VERSION == 1*/
+
+ unsigned char *serialno; /* Serialnumber in raw form, allocated. */
+ size_t serialnolen; /* Length in octets of serialnumber. */
+ const char *apptype;
+ unsigned int card_version;
+ int did_chv1;
+ int force_chv1; /* True if the card does not cache CHV1. */
+ int did_chv2;
+ int did_chv3;
+ struct app_local_s *app_local; /* Local to the application. */
+ struct {
+ void (*deinit) (app_t app);
+ gpg_error_t (*learn_status) (app_t app, ctrl_t ctrl);
+ gpg_error_t (*readcert) (app_t app, const char *certid,
+ unsigned char **cert, size_t *certlen);
+ gpg_error_t (*readkey) (app_t app, const char *certid,
+ unsigned char **pk, size_t *pklen);
+ gpg_error_t (*getattr) (app_t app, ctrl_t ctrl, const char *name);
+ gpg_error_t (*setattr) (app_t app, const char *name,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *value, size_t valuelen);
+ gpg_error_t (*sign) (app_t app,
+ const char *keyidstr, int hashalgo,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen );
+ gpg_error_t (*auth) (app_t app, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen);
+ gpg_error_t (*decipher) (app_t app, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen);
+ gpg_error_t (*writekey) (app_t app, ctrl_t ctrl,
+ const char *certid, unsigned int flags,
+ gpg_error_t (*pincb)(void*,const char *,char **),
+ void *pincb_arg,
+ const unsigned char *pk, size_t pklen);
+ gpg_error_t (*genkey) (app_t app, ctrl_t ctrl,
+ const char *keynostr, unsigned int flags,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg);
+ gpg_error_t (*change_pin) (app_t app, ctrl_t ctrl,
+ const char *chvnostr, int reset_mode,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg);
+ gpg_error_t (*check_pin) (app_t app, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg);
+ } fnc;
+
+};
+
+#if GNUPG_MAJOR_VERSION == 1
+gpg_error_t app_select_openpgp (app_t app);
+gpg_error_t app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp);
+gpg_error_t app_openpgp_storekey (app_t app, int keyno,
+ unsigned char *template, size_t template_len,
+ time_t created_at,
+ const unsigned char *m, size_t mlen,
+ const unsigned char *e, size_t elen,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg);
+#else
+/*-- app-help.c --*/
+gpg_error_t app_help_get_keygrip_string (ksba_cert_t cert, char *hexkeygrip);
+size_t app_help_read_length_of_cert (int slot, int fid, size_t *r_certoff);
+
+
+/*-- app.c --*/
+gpg_error_t select_application (ctrl_t ctrl, int slot, const char *name,
+ app_t *r_app);
+void release_application (app_t app);
+gpg_error_t app_munge_serialno (app_t app);
+gpg_error_t app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp);
+gpg_error_t app_write_learn_status (app_t app, ctrl_t ctrl);
+gpg_error_t app_readcert (app_t app, const char *certid,
+ unsigned char **cert, size_t *certlen);
+gpg_error_t app_readkey (app_t app, const char *keyid,
+ unsigned char **pk, size_t *pklen);
+gpg_error_t app_getattr (app_t app, ctrl_t ctrl, const char *name);
+gpg_error_t app_setattr (app_t app, const char *name,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *value, size_t valuelen);
+gpg_error_t app_sign (app_t app, const char *keyidstr, int hashalgo,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen );
+gpg_error_t app_auth (app_t app, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen);
+gpg_error_t app_decipher (app_t app, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen );
+gpg_error_t app_writekey (app_t app, ctrl_t ctrl,
+ const char *keyidstr, unsigned int flags,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *keydata, size_t keydatalen);
+gpg_error_t app_genkey (app_t app, ctrl_t ctrl,
+ const char *keynostr, unsigned int flags,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg);
+gpg_error_t app_get_challenge (app_t app, size_t nbytes,
+ unsigned char *buffer);
+gpg_error_t app_change_pin (app_t app, ctrl_t ctrl,
+ const char *chvnostr, int reset_mode,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg);
+gpg_error_t app_check_pin (app_t app, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg);
+
+
+/*-- app-openpgp.c --*/
+gpg_error_t app_select_openpgp (app_t app);
+
+/*-- app-nks.c --*/
+gpg_error_t app_select_nks (app_t app);
+
+/*-- app-dinsig.c --*/
+gpg_error_t app_select_dinsig (app_t app);
+
+/*-- app-p15.c --*/
+gpg_error_t app_select_p15 (app_t app);
+
+
+#endif
+
+
+
+#endif /*GNUPG_SCD_APP_COMMON_H*/
+
+
+
diff --git a/g10/app-openpgp.c b/g10/app-openpgp.c
new file mode 100644
index 0000000..1d83963
--- /dev/null
+++ b/g10/app-openpgp.c
@@ -0,0 +1,2528 @@
+/* app-openpgp.c - The OpenPGP card application.
+ * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ * $Id: app-openpgp.c 4178 2006-06-28 09:37:42Z wk $
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+
+#if GNUPG_MAJOR_VERSION == 1
+/* This is used with GnuPG version < 1.9. The code has been source
+ copied from the current GnuPG >= 1.9 and is maintained over
+ there. */
+#include "options.h"
+#include "errors.h"
+#include "memory.h"
+#include "util.h"
+#include "cardglue.h"
+#else /* GNUPG_MAJOR_VERSION != 1 */
+#include "scdaemon.h"
+#endif /* GNUPG_MAJOR_VERSION != 1 */
+
+#include "i18n.h"
+#include "iso7816.h"
+#include "app-common.h"
+#include "tlv.h"
+
+
+static struct {
+ int tag;
+ int constructed;
+ int get_from; /* Constructed DO with this DO or 0 for direct access. */
+ int binary;
+ int dont_cache;
+ int flush_on_error;
+ int get_immediate_in_v11; /* Enable a hack to bypass the cache of
+ this data object if it is used in 1.1
+ and later versions of the card. This
+ does not work with composite DO and is
+ currently only useful for the CHV
+ status bytes. */
+ char *desc;
+} data_objects[] = {
+ { 0x005E, 0, 0, 1, 0, 0, 0, "Login Data" },
+ { 0x5F50, 0, 0, 0, 0, 0, 0, "URL" },
+ { 0x0065, 1, 0, 1, 0, 0, 0, "Cardholder Related Data"},
+ { 0x005B, 0, 0x65, 0, 0, 0, 0, "Name" },
+ { 0x5F2D, 0, 0x65, 0, 0, 0, 0, "Language preferences" },
+ { 0x5F35, 0, 0x65, 0, 0, 0, 0, "Sex" },
+ { 0x006E, 1, 0, 1, 0, 0, 0, "Application Related Data" },
+ { 0x004F, 0, 0x6E, 1, 0, 0, 0, "AID" },
+ { 0x0073, 1, 0, 1, 0, 0, 0, "Discretionary Data Objects" },
+ { 0x0047, 0, 0x6E, 1, 1, 0, 0, "Card Capabilities" },
+ { 0x00C0, 0, 0x6E, 1, 1, 0, 0, "Extended Card Capabilities" },
+ { 0x00C1, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Signature" },
+ { 0x00C2, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Decryption" },
+ { 0x00C3, 0, 0x6E, 1, 1, 0, 0, "Algorithm Attributes Authentication" },
+ { 0x00C4, 0, 0x6E, 1, 0, 1, 1, "CHV Status Bytes" },
+ { 0x00C5, 0, 0x6E, 1, 0, 0, 0, "Fingerprints" },
+ { 0x00C6, 0, 0x6E, 1, 0, 0, 0, "CA Fingerprints" },
+ { 0x00CD, 0, 0x6E, 1, 0, 0, 0, "Generation time" },
+ { 0x007A, 1, 0, 1, 0, 0, 0, "Security Support Template" },
+ { 0x0093, 0, 0x7A, 1, 1, 0, 0, "Digital Signature Counter" },
+ { 0x0101, 0, 0, 0, 0, 0, 0, "Private DO 1"},
+ { 0x0102, 0, 0, 0, 0, 0, 0, "Private DO 2"},
+ { 0x0103, 0, 0, 0, 0, 0, 0, "Private DO 3"},
+ { 0x0104, 0, 0, 0, 0, 0, 0, "Private DO 4"},
+ { 0 }
+};
+
+
+/* One cache item for DOs. */
+struct cache_s {
+ struct cache_s *next;
+ int tag;
+ size_t length;
+ unsigned char data[1];
+};
+
+
+/* Object with application (i.e. OpenPGP card) specific data. */
+struct app_local_s {
+ /* A linked list with cached DOs. */
+ struct cache_s *cache;
+
+ /* Keep track of the public keys. */
+ struct
+ {
+ int read_done; /* True if we have at least tried to read them. */
+ unsigned char *key; /* This is a malloced buffer with a canonical
+ encoded S-expression encoding a public
+ key. Might be NULL if key is not
+ available. */
+ size_t keylen; /* The length of the above S-expression. Thsi
+ is usullay only required for corss checks
+ because the length of an S-expression is
+ implicitly available. */
+ } pk[3];
+
+ /* Keep track of card capabilities. */
+ struct
+ {
+ unsigned int get_challenge:1;
+ unsigned int key_import:1;
+ unsigned int change_force_chv:1;
+ unsigned int private_dos:1;
+ } extcap;
+
+ /* Flags used to control the application. */
+ struct
+ {
+ unsigned int no_sync:1; /* Do not sync CHV1 and CHV2 */
+ unsigned int def_chv2:1; /* Use 123456 for CHV2. */
+ } flags;
+};
+
+
+
+/***** Local prototypes *****/
+static unsigned long convert_sig_counter_value (const unsigned char *value,
+ size_t valuelen);
+static unsigned long get_sig_counter (app_t app);
+
+
+
+
+
+/* Deconstructor. */
+static void
+do_deinit (app_t app)
+{
+ if (app && app->app_local)
+ {
+ struct cache_s *c, *c2;
+ int i;
+
+ for (c = app->app_local->cache; c; c = c2)
+ {
+ c2 = c->next;
+ xfree (c);
+ }
+
+ for (i=0; i < DIM (app->app_local->pk); i++)
+ {
+ xfree (app->app_local->pk[i].key);
+ app->app_local->pk[i].read_done = 0;
+ }
+ xfree (app->app_local);
+ app->app_local = NULL;
+ }
+}
+
+
+/* Wrapper around iso7816_get_data which first tries to get the data
+ from the cache. With GET_IMMEDIATE passed as true, the cache is
+ bypassed. */
+static gpg_error_t
+get_cached_data (app_t app, int tag,
+ unsigned char **result, size_t *resultlen,
+ int get_immediate)
+{
+ gpg_error_t err;
+ int i;
+ unsigned char *p;
+ size_t len;
+ struct cache_s *c;
+
+ *result = NULL;
+ *resultlen = 0;
+
+ if (!get_immediate)
+ {
+ for (c=app->app_local->cache; c; c = c->next)
+ if (c->tag == tag)
+ {
+ if(c->length)
+ {
+ p = xtrymalloc (c->length);
+ if (!p)
+ return gpg_error (gpg_err_code_from_errno (errno));
+ memcpy (p, c->data, c->length);
+ *result = p;
+ }
+
+ *resultlen = c->length;
+
+ return 0;
+ }
+ }
+
+ err = iso7816_get_data (app->slot, tag, &p, &len);
+ if (err)
+ return err;
+ *result = p;
+ *resultlen = len;
+
+ /* Check whether we should cache this object. */
+ if (get_immediate)
+ return 0;
+
+ for (i=0; data_objects[i].tag; i++)
+ if (data_objects[i].tag == tag)
+ {
+ if (data_objects[i].dont_cache)
+ return 0;
+ break;
+ }
+
+ /* Okay, cache it. */
+ for (c=app->app_local->cache; c; c = c->next)
+ assert (c->tag != tag);
+
+ c = xtrymalloc (sizeof *c + len);
+ if (c)
+ {
+ memcpy (c->data, p, len);
+ c->length = len;
+ c->tag = tag;
+ c->next = app->app_local->cache;
+ app->app_local->cache = c;
+ }
+
+ return 0;
+}
+
+/* Remove DO at TAG from the cache. */
+static void
+flush_cache_item (app_t app, int tag)
+{
+ struct cache_s *c, *cprev;
+ int i;
+
+ if (!app->app_local)
+ return;
+
+ for (c=app->app_local->cache, cprev=NULL; c ; cprev=c, c = c->next)
+ if (c->tag == tag)
+ {
+ if (cprev)
+ cprev->next = c->next;
+ else
+ app->app_local->cache = c->next;
+ xfree (c);
+
+ for (c=app->app_local->cache; c ; c = c->next)
+ {
+ assert (c->tag != tag); /* Oops: duplicated entry. */
+ }
+ return;
+ }
+
+ /* Try again if we have an outer tag. */
+ for (i=0; data_objects[i].tag; i++)
+ if (data_objects[i].tag == tag && data_objects[i].get_from
+ && data_objects[i].get_from != tag)
+ flush_cache_item (app, data_objects[i].get_from);
+}
+
+/* Flush all entries from the cache which might be out of sync after
+ an error. */
+static void
+flush_cache_after_error (app_t app)
+{
+ int i;
+
+ for (i=0; data_objects[i].tag; i++)
+ if (data_objects[i].flush_on_error)
+ flush_cache_item (app, data_objects[i].tag);
+}
+
+
+/* Flush the entire cache. */
+static void
+flush_cache (app_t app)
+{
+ if (app && app->app_local)
+ {
+ struct cache_s *c, *c2;
+
+ for (c = app->app_local->cache; c; c = c2)
+ {
+ c2 = c->next;
+ xfree (c);
+ }
+ app->app_local->cache = NULL;
+ }
+}
+
+
+/* Get the DO identified by TAG from the card in SLOT and return a
+ buffer with its content in RESULT and NBYTES. The return value is
+ NULL if not found or a pointer which must be used to release the
+ buffer holding value. */
+static void *
+get_one_do (app_t app, int tag, unsigned char **result, size_t *nbytes,
+ int *r_rc)
+{
+ int rc, i;
+ unsigned char *buffer;
+ size_t buflen;
+ unsigned char *value;
+ size_t valuelen;
+ int dummyrc;
+
+ if (!r_rc)
+ r_rc = &dummyrc;
+
+ *result = NULL;
+ *nbytes = 0;
+ *r_rc = 0;
+ for (i=0; data_objects[i].tag && data_objects[i].tag != tag; i++)
+ ;
+
+ if (app->card_version > 0x0100 && data_objects[i].get_immediate_in_v11)
+ {
+ rc = iso7816_get_data (app->slot, tag, &buffer, &buflen);
+ if (rc)
+ {
+ *r_rc = rc;
+ return NULL;
+ }
+ *result = buffer;
+ *nbytes = buflen;
+ return buffer;
+ }
+
+ value = NULL;
+ rc = -1;
+ if (data_objects[i].tag && data_objects[i].get_from)
+ {
+ rc = get_cached_data (app, data_objects[i].get_from,
+ &buffer, &buflen,
+ (data_objects[i].dont_cache
+ || data_objects[i].get_immediate_in_v11));
+ if (!rc)
+ {
+ const unsigned char *s;
+
+ s = find_tlv_unchecked (buffer, buflen, tag, &valuelen);
+ if (!s)
+ value = NULL; /* not found */
+ else if (valuelen > buflen - (s - buffer))
+ {
+ log_error ("warning: constructed DO too short\n");
+ value = NULL;
+ xfree (buffer); buffer = NULL;
+ }
+ else
+ value = buffer + (s - buffer);
+ }
+ }
+
+ if (!value) /* Not in a constructed DO, try simple. */
+ {
+ rc = get_cached_data (app, tag, &buffer, &buflen,
+ (data_objects[i].dont_cache
+ || data_objects[i].get_immediate_in_v11));
+ if (!rc)
+ {
+ value = buffer;
+ valuelen = buflen;
+ }
+ }
+
+ if (!rc)
+ {
+ *nbytes = valuelen;
+ *result = value;
+ return buffer;
+ }
+ *r_rc = rc;
+ return NULL;
+}
+
+
+static void
+dump_all_do (int slot)
+{
+ int rc, i, j;
+ unsigned char *buffer;
+ size_t buflen;
+
+ for (i=0; data_objects[i].tag; i++)
+ {
+ if (data_objects[i].get_from)
+ continue;
+
+ rc = iso7816_get_data (slot, data_objects[i].tag, &buffer, &buflen);
+ if (gpg_err_code (rc) == GPG_ERR_NO_OBJ)
+ ;
+ else if (rc)
+ log_info ("DO `%s' not available: %s\n",
+ data_objects[i].desc, gpg_strerror (rc));
+ else
+ {
+ if (data_objects[i].binary)
+ {
+ log_info ("DO `%s': ", data_objects[i].desc);
+ log_printhex ("", buffer, buflen);
+ }
+ else
+ log_info ("DO `%s': `%.*s'\n",
+ data_objects[i].desc,
+ (int)buflen, buffer); /* FIXME: sanitize */
+
+ if (data_objects[i].constructed)
+ {
+ for (j=0; data_objects[j].tag; j++)
+ {
+ const unsigned char *value;
+ size_t valuelen;
+
+ if (j==i || data_objects[i].tag != data_objects[j].get_from)
+ continue;
+ value = find_tlv_unchecked (buffer, buflen,
+ data_objects[j].tag, &valuelen);
+ if (!value)
+ ; /* not found */
+ else if (valuelen > buflen - (value - buffer))
+ log_error ("warning: constructed DO too short\n");
+ else
+ {
+ if (data_objects[j].binary)
+ {
+ log_info ("DO `%s': ", data_objects[j].desc);
+ log_printhex ("", value, valuelen);
+ }
+ else
+ log_info ("DO `%s': `%.*s'\n",
+ data_objects[j].desc,
+ (int)valuelen, value); /* FIXME: sanitize */
+ }
+ }
+ }
+ }
+ xfree (buffer); buffer = NULL;
+ }
+}
+
+
+/* Count the number of bits, assuming the A represents an unsigned big
+ integer of length LEN bytes. */
+static unsigned int
+count_bits (const unsigned char *a, size_t len)
+{
+ unsigned int n = len * 8;
+ int i;
+
+ for (; len && !*a; len--, a++, n -=8)
+ ;
+ if (len)
+ {
+ for (i=7; i && !(*a & (1<<i)); i--)
+ n--;
+ }
+ return n;
+}
+
+/* GnuPG makes special use of the login-data DO, this fucntion parses
+ the login data to store the flags for later use. It may be called
+ at any time and should be called after changing the login-data DO.
+
+ Everything up to a LF is considered a mailbox or account name. If
+ the first LF is followed by DC4 (0x14) control sequence are
+ expected up to the next LF. Control sequences are separated by FS
+ (0x28) and consist of key=value pairs. There is one key defined:
+
+ F=<flags>
+
+ Were FLAGS is a plain hexadecimal number representing flag values.
+ The lsb is here the rightmost bit. Defined flags bits are:
+
+ Bit 0 = CHV1 and CHV2 are not syncronized
+ Bit 1 = CHV2 has been been set to the default PIN of "123456"
+ (this implies that bit 0 is also set).
+
+*/
+static void
+parse_login_data (app_t app)
+{
+ unsigned char *buffer, *p;
+ size_t buflen, len;
+ void *relptr;
+
+ /* Set defaults. */
+ app->app_local->flags.no_sync = 0;
+ app->app_local->flags.def_chv2 = 0;
+
+ /* Read the DO. */
+ relptr = get_one_do (app, 0x005E, &buffer, &buflen, NULL);
+ if (!relptr)
+ return; /* Ooops. */
+ for (; buflen; buflen--, buffer++)
+ if (*buffer == '\n')
+ break;
+ if (buflen < 2 || buffer[1] != '\x14')
+ return; /* No control sequences. */
+ buflen--;
+ buffer++;
+ do
+ {
+ buflen--;
+ buffer++;
+ if (buflen > 1 && *buffer == 'F' && buffer[1] == '=')
+ {
+ /* Flags control sequence found. */
+ int lastdig = 0;
+
+ /* For now we are only interested in the last digit, so skip
+ any leading digits but bail out on invalid characters. */
+ for (p=buffer+2, len = buflen-2; len && hexdigitp (p); p++, len--)
+ lastdig = xtoi_1 (p);
+ if (len && !(*p == '\n' || *p == '\x18'))
+ goto next; /* Invalid characters in field. */
+ app->app_local->flags.no_sync = !!(lastdig & 1);
+ app->app_local->flags.def_chv2 = (lastdig & 3) == 3;
+ }
+ next:
+ for (; buflen && *buffer != '\x18'; buflen--, buffer++)
+ if (*buffer == '\n')
+ buflen = 1;
+ }
+ while (buflen);
+
+ xfree (relptr);
+}
+
+/* Note, that FPR must be at least 20 bytes. */
+static gpg_error_t
+store_fpr (int slot, int keynumber, u32 timestamp,
+ const unsigned char *m, size_t mlen,
+ const unsigned char *e, size_t elen,
+ unsigned char *fpr, unsigned int card_version)
+{
+ unsigned int n, nbits;
+ unsigned char *buffer, *p;
+ int rc;
+
+ for (; mlen && !*m; mlen--, m++) /* strip leading zeroes */
+ ;
+ for (; elen && !*e; elen--, e++) /* strip leading zeroes */
+ ;
+
+ n = 6 + 2 + mlen + 2 + elen;
+ p = buffer = xtrymalloc (3 + n);
+ if (!buffer)
+ return gpg_error_from_errno (errno);
+
+ *p++ = 0x99; /* ctb */
+ *p++ = n >> 8; /* 2 byte length header */
+ *p++ = n;
+ *p++ = 4; /* key packet version */
+ *p++ = timestamp >> 24;
+ *p++ = timestamp >> 16;
+ *p++ = timestamp >> 8;
+ *p++ = timestamp;
+ *p++ = 1; /* RSA */
+ nbits = count_bits (m, mlen);
+ *p++ = nbits >> 8;
+ *p++ = nbits;
+ memcpy (p, m, mlen); p += mlen;
+ nbits = count_bits (e, elen);
+ *p++ = nbits >> 8;
+ *p++ = nbits;
+ memcpy (p, e, elen); p += elen;
+
+ gcry_md_hash_buffer (GCRY_MD_SHA1, fpr, buffer, n+3);
+
+ xfree (buffer);
+
+ rc = iso7816_put_data (slot, (card_version > 0x0007? 0xC7 : 0xC6)
+ + keynumber, fpr, 20);
+ if (rc)
+ log_error (_("failed to store the fingerprint: %s\n"),gpg_strerror (rc));
+
+ if (!rc && card_version > 0x0100)
+ {
+ unsigned char buf[4];
+
+ buf[0] = timestamp >> 24;
+ buf[1] = timestamp >> 16;
+ buf[2] = timestamp >> 8;
+ buf[3] = timestamp;
+
+ rc = iso7816_put_data (slot, 0xCE + keynumber, buf, 4);
+ if (rc)
+ log_error (_("failed to store the creation date: %s\n"),
+ gpg_strerror (rc));
+ }
+
+ return rc;
+}
+
+
+static void
+send_fpr_if_not_null (ctrl_t ctrl, const char *keyword,
+ int number, const unsigned char *fpr)
+{
+ int i;
+ char buf[41];
+ char numbuf[25];
+
+ for (i=0; i < 20 && !fpr[i]; i++)
+ ;
+ if (i==20)
+ return; /* All zero. */
+ for (i=0; i< 20; i++)
+ sprintf (buf+2*i, "%02X", fpr[i]);
+ if (number == -1)
+ *numbuf = 0; /* Don't print the key number */
+ else
+ sprintf (numbuf, "%d", number);
+ send_status_info (ctrl, keyword,
+ numbuf, (size_t)strlen(numbuf),
+ buf, (size_t)strlen (buf), NULL, 0);
+}
+
+static void
+send_fprtime_if_not_null (ctrl_t ctrl, const char *keyword,
+ int number, const unsigned char *stamp)
+{
+ char numbuf1[50], numbuf2[50];
+ unsigned long value;
+
+ value = (stamp[0] << 24) | (stamp[1]<<16) | (stamp[2]<<8) | stamp[3];
+ if (!value)
+ return;
+ sprintf (numbuf1, "%d", number);
+ sprintf (numbuf2, "%lu", value);
+ send_status_info (ctrl, keyword,
+ numbuf1, (size_t)strlen(numbuf1),
+ numbuf2, (size_t)strlen(numbuf2), NULL, 0);
+}
+
+static void
+send_key_data (ctrl_t ctrl, const char *name,
+ const unsigned char *a, size_t alen)
+{
+ char *p, *buf = xmalloc (alen*2+1);
+
+ for (p=buf; alen; a++, alen--, p += 2)
+ sprintf (p, "%02X", *a);
+
+ send_status_info (ctrl, "KEY-DATA",
+ name, (size_t)strlen(name),
+ buf, (size_t)strlen (buf),
+ NULL, 0);
+ xfree (buf);
+}
+
+/* Implement the GETATTR command. This is similar to the LEARN
+ command but returns just one value via the status interface. */
+static gpg_error_t
+do_getattr (app_t app, ctrl_t ctrl, const char *name)
+{
+ static struct {
+ const char *name;
+ int tag;
+ int special;
+ } table[] = {
+ { "DISP-NAME", 0x005B },
+ { "LOGIN-DATA", 0x005E },
+ { "DISP-LANG", 0x5F2D },
+ { "DISP-SEX", 0x5F35 },
+ { "PUBKEY-URL", 0x5F50 },
+ { "KEY-FPR", 0x00C5, 3 },
+ { "KEY-TIME", 0x00CD, 4 },
+ { "CA-FPR", 0x00C6, 3 },
+ { "CHV-STATUS", 0x00C4, 1 },
+ { "SIG-COUNTER", 0x0093, 2 },
+ { "SERIALNO", 0x004F, -1 },
+ { "AID", 0x004F },
+ { "EXTCAP", 0x0000, -2 },
+ { "PRIVATE-DO-1", 0x0101 },
+ { "PRIVATE-DO-2", 0x0102 },
+ { "PRIVATE-DO-3", 0x0103 },
+ { "PRIVATE-DO-4", 0x0104 },
+ { NULL, 0 }
+ };
+ int idx, i, rc;
+ void *relptr;
+ unsigned char *value;
+ size_t valuelen;
+
+ for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++)
+ ;
+ if (!table[idx].name)
+ return gpg_error (GPG_ERR_INV_NAME);
+
+ if (table[idx].special == -1)
+ {
+ /* The serial number is very special. We could have used the
+ AID DO to retrieve it, but we have it already in the app
+ context and the stamp argument is required anyway which we
+ can't by other means. The AID DO is available anyway but not
+ hex formatted. */
+ char *serial;
+ time_t stamp;
+ char tmp[50];
+
+ if (!app_get_serial_and_stamp (app, &serial, &stamp))
+ {
+ sprintf (tmp, "%lu", (unsigned long)stamp);
+ send_status_info (ctrl, "SERIALNO",
+ serial, strlen (serial),
+ tmp, strlen (tmp),
+ NULL, 0);
+ xfree (serial);
+ }
+ return 0;
+ }
+ if (table[idx].special == -2)
+ {
+ char tmp[50];
+
+ sprintf (tmp, "gc=%d ki=%d fc=%d pd=%d",
+ app->app_local->extcap.get_challenge,
+ app->app_local->extcap.key_import,
+ app->app_local->extcap.change_force_chv,
+ app->app_local->extcap.private_dos);
+ send_status_info (ctrl, table[idx].name, tmp, strlen (tmp), NULL, 0);
+ return 0;
+ }
+
+ relptr = get_one_do (app, table[idx].tag, &value, &valuelen, &rc);
+ if (relptr)
+ {
+ if (table[idx].special == 1)
+ {
+ char numbuf[7*23];
+
+ for (i=0,*numbuf=0; i < valuelen && i < 7; i++)
+ sprintf (numbuf+strlen (numbuf), " %d", value[i]);
+ send_status_info (ctrl, table[idx].name,
+ numbuf, strlen (numbuf), NULL, 0);
+ }
+ else if (table[idx].special == 2)
+ {
+ char numbuf[50];
+
+ sprintf (numbuf, "%lu", convert_sig_counter_value (value, valuelen));
+ send_status_info (ctrl, table[idx].name,
+ numbuf, strlen (numbuf), NULL, 0);
+ }
+ else if (table[idx].special == 3)
+ {
+ if (valuelen >= 60)
+ for (i=0; i < 3; i++)
+ send_fpr_if_not_null (ctrl, table[idx].name, i+1, value+i*20);
+ }
+ else if (table[idx].special == 4)
+ {
+ if (valuelen >= 12)
+ for (i=0; i < 3; i++)
+ send_fprtime_if_not_null (ctrl, table[idx].name, i+1, value+i*4);
+ }
+ else
+ send_status_info (ctrl, table[idx].name, value, valuelen, NULL, 0);
+
+ xfree (relptr);
+ }
+ return rc;
+}
+
+/* Retrieve the fingerprint from the card inserted in SLOT and write
+ the according hex representation to FPR. Caller must have provide
+ a buffer at FPR of least 41 bytes. Returns 0 on success or an
+ error code. */
+#if GNUPG_MAJOR_VERSION > 1
+static gpg_error_t
+retrieve_fpr_from_card (app_t app, int keyno, char *fpr)
+{
+ gpg_error_t err = 0;
+ void *relptr;
+ unsigned char *value;
+ size_t valuelen;
+ int i;
+
+ assert (keyno >=0 && keyno <= 2);
+
+ relptr = get_one_do (app, 0x00C5, &value, &valuelen, NULL);
+ if (relptr && valuelen >= 60)
+ {
+ for (i = 0; i < 20; i++)
+ sprintf (fpr + (i * 2), "%02X", value[(keyno*20)+i]);
+ }
+ else
+ err = gpg_error (GPG_ERR_NOT_FOUND);
+ xfree (relptr);
+ return err;
+}
+#endif /*GNUPG_MAJOR_VERSION > 1*/
+
+
+/* Retrieve the public key material for the RSA key, whose fingerprint
+ is FPR, from gpg output, which can be read through the stream FP.
+ The RSA modulus will be stored at the address of M and MLEN, the
+ public exponent at E and ELEN. Returns zero on success, an error
+ code on failure. Caller must release the allocated buffers at M
+ and E if the function returns success. */
+#if GNUPG_MAJOR_VERSION > 1
+static gpg_error_t
+retrieve_key_material (FILE *fp, const char *hexkeyid,
+ const unsigned char **m, size_t *mlen,
+ const unsigned char **e, size_t *elen)
+{
+ gcry_error_t err = 0;
+ char *line = NULL; /* read_line() buffer. */
+ size_t line_size = 0; /* Helper for for read_line. */
+ int found_key = 0; /* Helper to find a matching key. */
+ unsigned char *m_new = NULL;
+ unsigned char *e_new = NULL;
+ size_t m_new_n = 0;
+ size_t e_new_n = 0;
+
+ /* Loop over all records until we have found the subkey
+ corresponsing to the fingerprint. Inm general the first record
+ should be the pub record, but we don't rely on that. Given that
+ we only need to look at one key, it is sufficient to compare the
+ keyid so that we don't need to look at "fpr" records. */
+ for (;;)
+ {
+ char *p;
+ char *fields[6];
+ int nfields;
+ size_t max_length;
+ gcry_mpi_t mpi;
+ int i;
+
+ max_length = 4096;
+ i = read_line (fp, &line, &line_size, &max_length);
+ if (!i)
+ break; /* EOF. */
+ if (i < 0)
+ {
+ err = gpg_error_from_errno (errno);
+ goto leave; /* Error. */
+ }
+ if (!max_length)
+ {
+ err = gpg_error (GPG_ERR_TRUNCATED);
+ goto leave; /* Line truncated - we better stop processing. */
+ }
+
+ /* Parse the line into fields. */
+ for (nfields=0, p=line; p && nfields < DIM (fields); nfields++)
+ {
+ fields[nfields] = p;
+ p = strchr (p, ':');
+ if (p)
+ *(p++) = 0;
+ }
+ if (!nfields)
+ continue; /* No fields at all - skip line. */
+
+ if (!found_key)
+ {
+ if ( (!strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") )
+ && nfields > 4 && !strcmp (fields[4], hexkeyid))
+ found_key = 1;
+ continue;
+ }
+
+ if ( !strcmp (fields[0], "sub") || !strcmp (fields[0], "pub") )
+ break; /* Next key - stop. */
+
+ if ( strcmp (fields[0], "pkd") )
+ continue; /* Not a key data record. */
+ i = 0; /* Avoid erroneous compiler warning. */
+ if ( nfields < 4 || (i = atoi (fields[1])) < 0 || i > 1
+ || (!i && m_new) || (i && e_new))
+ {
+ err = gpg_error (GPG_ERR_GENERAL);
+ goto leave; /* Error: Invalid key data record or not an RSA key. */
+ }
+
+ err = gcry_mpi_scan (&mpi, GCRYMPI_FMT_HEX, fields[3], 0, NULL);
+ if (err)
+ mpi = NULL;
+ else if (!i)
+ err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &m_new, &m_new_n, mpi);
+ else
+ err = gcry_mpi_aprint (GCRYMPI_FMT_STD, &e_new, &e_new_n, mpi);
+ gcry_mpi_release (mpi);
+ if (err)
+ goto leave;
+ }
+
+ if (m_new && e_new)
+ {
+ *m = m_new;
+ *mlen = m_new_n;
+ m_new = NULL;
+ *e = e_new;
+ *elen = e_new_n;
+ e_new = NULL;
+ }
+ else
+ err = gpg_error (GPG_ERR_GENERAL);
+
+ leave:
+ xfree (m_new);
+ xfree (e_new);
+ xfree (line);
+ return err;
+}
+#endif /*GNUPG_MAJOR_VERSION > 1*/
+
+
+/* Get the public key for KEYNO and store it as an S-expresion with
+ the APP handle. On error that field gets cleared. If we already
+ know about the public key we will just return. Note that this does
+ not mean a key is available; this is soley indicated by the
+ presence of the app->app_local->pk[KEYNO-1].key field.
+
+ Note that GnuPG 1.x does not need this and it would be too time
+ consuming to send it just for the fun of it. However, given that we
+ use the same code in gpg 1.4, we can't use the gcry S-expresion
+ here but need to open encode it. */
+#if GNUPG_MAJOR_VERSION > 1
+static gpg_error_t
+get_public_key (app_t app, int keyno)
+{
+ gpg_error_t err = 0;
+ unsigned char *buffer;
+ const unsigned char *keydata, *m, *e;
+ size_t buflen, keydatalen, mlen, elen;
+ unsigned char *mbuf = NULL;
+ unsigned char *ebuf = NULL;
+ unsigned char *keybuf = NULL;
+ unsigned char *keybuf_p;
+
+ if (keyno < 1 || keyno > 3)
+ return gpg_error (GPG_ERR_INV_ID);
+ keyno--;
+
+ /* Already cached? */
+ if (app->app_local->pk[keyno].read_done)
+ return 0;
+
+ xfree (app->app_local->pk[keyno].key);
+ app->app_local->pk[keyno].key = NULL;
+ app->app_local->pk[keyno].keylen = 0;
+
+ if (app->card_version > 0x0100)
+ {
+ /* We may simply read the public key out of these cards. */
+ err = iso7816_read_public_key (app->slot,
+ keyno == 0? "\xB6" :
+ keyno == 1? "\xB8" : "\xA4",
+ 2,
+ &buffer, &buflen);
+ if (err)
+ {
+ log_error (_("reading public key failed: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+
+ keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
+ if (!keydata)
+ {
+ err = gpg_error (GPG_ERR_CARD);
+ log_error (_("response does not contain the public key data\n"));
+ goto leave;
+ }
+
+ m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
+ if (!m)
+ {
+ err = gpg_error (GPG_ERR_CARD);
+ log_error (_("response does not contain the RSA modulus\n"));
+ goto leave;
+ }
+
+
+ e = find_tlv (keydata, keydatalen, 0x0082, &elen);
+ if (!e)
+ {
+ err = gpg_error (GPG_ERR_CARD);
+ log_error (_("response does not contain the RSA public exponent\n"));
+ goto leave;
+ }
+
+ /* Prepend numbers with a 0 if needed. */
+ if (mlen && (*m & 0x80))
+ {
+ mbuf = xtrymalloc ( mlen + 1);
+ if (!mbuf)
+ {
+ err = gpg_error_from_errno (errno);
+ goto leave;
+ }
+ *mbuf = 0;
+ memcpy (mbuf+1, m, mlen);
+ mlen++;
+ m = mbuf;
+ }
+ if (elen && (*e & 0x80))
+ {
+ ebuf = xtrymalloc ( elen + 1);
+ if (!ebuf)
+ {
+ err = gpg_error_from_errno (errno);
+ goto leave;
+ }
+ *ebuf = 0;
+ memcpy (ebuf+1, e, elen);
+ elen++;
+ e = ebuf;
+ }
+
+ }
+ else
+ {
+ /* Due to a design problem in v1.0 cards we can't get the public
+ key out of these cards without doing a verify on CHV3.
+ Clearly that is not an option and thus we try to locate the
+ key using an external helper.
+
+ The helper we use here is gpg itself, which should know about
+ the key in any case. */
+
+ char fpr[41];
+ char *hexkeyid;
+ char *command = NULL;
+ FILE *fp;
+ int ret;
+
+ buffer = NULL; /* We don't need buffer. */
+
+ err = retrieve_fpr_from_card (app, keyno, fpr);
+ if (err)
+ {
+ log_error ("error while retrieving fpr from card: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+ hexkeyid = fpr + 24;
+
+ ret = asprintf (&command,
+ "gpg --list-keys --with-colons --with-key-data '%s'",
+ fpr);
+ if (ret < 0)
+ {
+ err = gpg_error_from_errno (errno);
+ goto leave;
+ }
+
+ fp = popen (command, "r");
+ free (command);
+ if (!fp)
+ {
+ err = gpg_error_from_errno (errno);
+ log_error ("running gpg failed: %s\n", gpg_strerror (err));
+ goto leave;
+ }
+
+ err = retrieve_key_material (fp, hexkeyid, &m, &mlen, &e, &elen);
+ fclose (fp);
+ if (err)
+ {
+ log_error ("error while retrieving key material through pipe: %s\n",
+ gpg_strerror (err));
+ goto leave;
+ }
+ }
+
+ /* Allocate a buffer to construct the S-expression. */
+ /* FIXME: We should provide a generalized S-expression creation
+ mechanism. */
+ keybuf = xtrymalloc (50 + 2*35 + mlen + elen + 1);
+ if (!keybuf)
+ {
+ err = gpg_error_from_errno (errno);
+ goto leave;
+ }
+
+ sprintf (keybuf, "(10:public-key(3:rsa(1:n%u:", (unsigned int) mlen);
+ keybuf_p = keybuf + strlen (keybuf);
+ memcpy (keybuf_p, m, mlen);
+ keybuf_p += mlen;
+ sprintf (keybuf_p, ")(1:e%u:", (unsigned int)elen);
+ keybuf_p += strlen (keybuf_p);
+ memcpy (keybuf_p, e, elen);
+ keybuf_p += elen;
+ strcpy (keybuf_p, ")))");
+ keybuf_p += strlen (keybuf_p);
+
+ app->app_local->pk[keyno].key = keybuf;
+ app->app_local->pk[keyno].keylen = (keybuf_p - keybuf);
+
+ leave:
+ /* Set a flag to indicate that we tried to read the key. */
+ app->app_local->pk[keyno].read_done = 1;
+
+ xfree (buffer);
+ xfree (mbuf);
+ xfree (ebuf);
+ return 0;
+}
+#endif /* GNUPG_MAJOR_VERSION > 1 */
+
+
+
+/* Send the KEYPAIRINFO back. KEYNO needs to be in the range [1,3].
+ This is used by the LEARN command. */
+static gpg_error_t
+send_keypair_info (app_t app, ctrl_t ctrl, int keyno)
+{
+ gpg_error_t err = 0;
+ /* Note that GnuPG 1.x does not need this and it would be too time
+ consuming to send it just for the fun of it. */
+#if GNUPG_MAJOR_VERSION > 1
+ unsigned char grip[20];
+ char gripstr[41];
+ char idbuf[50];
+ int i;
+
+ err = get_public_key (app, keyno);
+ if (err)
+ goto leave;
+
+ assert (keyno >= 1 && keyno <= 3);
+ if (!app->app_local->pk[keyno-1].key)
+ goto leave; /* No such key - ignore. */
+
+ err = keygrip_from_canon_sexp (app->app_local->pk[keyno-1].key,
+ app->app_local->pk[keyno-1].keylen,
+ grip);
+ if (err)
+ goto leave;
+
+ for (i=0; i < 20; i++)
+ sprintf (gripstr+i*2, "%02X", grip[i]);
+
+ sprintf (idbuf, "OPENPGP.%d", keyno);
+ send_status_info (ctrl, "KEYPAIRINFO",
+ gripstr, 40,
+ idbuf, strlen (idbuf),
+ NULL, (size_t)0);
+
+ leave:
+#endif /* GNUPG_MAJOR_VERSION > 1 */
+
+ return err;
+}
+
+
+/* Handle the LEARN command for OpenPGP. */
+static gpg_error_t
+do_learn_status (app_t app, ctrl_t ctrl)
+{
+ do_getattr (app, ctrl, "EXTCAP");
+ do_getattr (app, ctrl, "DISP-NAME");
+ do_getattr (app, ctrl, "DISP-LANG");
+ do_getattr (app, ctrl, "DISP-SEX");
+ do_getattr (app, ctrl, "PUBKEY-URL");
+ do_getattr (app, ctrl, "LOGIN-DATA");
+ do_getattr (app, ctrl, "KEY-FPR");
+ if (app->card_version > 0x0100)
+ do_getattr (app, ctrl, "KEY-TIME");
+ do_getattr (app, ctrl, "CA-FPR");
+ do_getattr (app, ctrl, "CHV-STATUS");
+ do_getattr (app, ctrl, "SIG-COUNTER");
+ if (app->app_local->extcap.private_dos)
+ {
+ do_getattr (app, ctrl, "PRIVATE-DO-1");
+ do_getattr (app, ctrl, "PRIVATE-DO-2");
+ if (app->did_chv2)
+ do_getattr (app, ctrl, "PRIVATE-DO-3");
+ if (app->did_chv3)
+ do_getattr (app, ctrl, "PRIVATE-DO-4");
+ }
+ send_keypair_info (app, ctrl, 1);
+ send_keypair_info (app, ctrl, 2);
+ send_keypair_info (app, ctrl, 3);
+ return 0;
+}
+
+
+/* Handle the READKEY command for OpenPGP. On success a canonical
+ encoded S-expression with the public key will get stored at PK and
+ its length (for assertions) at PKLEN; the caller must release that
+ buffer. On error PK and PKLEN are not changed and an error code is
+ returned. */
+static gpg_error_t
+do_readkey (app_t app, const char *keyid, unsigned char **pk, size_t *pklen)
+{
+#if GNUPG_MAJOR_VERSION > 1
+ gpg_error_t err;
+ int keyno;
+ unsigned char *buf;
+
+ if (!strcmp (keyid, "OPENPGP.1"))
+ keyno = 1;
+ else if (!strcmp (keyid, "OPENPGP.2"))
+ keyno = 2;
+ else if (!strcmp (keyid, "OPENPGP.3"))
+ keyno = 3;
+ else
+ return gpg_error (GPG_ERR_INV_ID);
+
+ err = get_public_key (app, keyno);
+ if (err)
+ return err;
+
+ buf = app->app_local->pk[keyno-1].key;
+ if (!buf)
+ return gpg_error (GPG_ERR_NO_PUBKEY);
+ *pk = buf;
+ *pklen = app->app_local->pk[keyno-1].keylen;;
+ return 0;
+#else
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+#endif
+}
+
+
+
+/* Verify CHV2 if required. Depending on the configuration of the
+ card CHV1 will also be verified. */
+static gpg_error_t
+verify_chv2 (app_t app,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ int rc = 0;
+
+ if (!app->did_chv2)
+ {
+ char *pinvalue;
+
+ rc = pincb (pincb_arg, "PIN", &pinvalue);
+ if (rc)
+ {
+ log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc));
+ return rc;
+ }
+
+ if (strlen (pinvalue) < 6)
+ {
+ log_error (_("PIN for CHV%d is too short;"
+ " minimum length is %d\n"), 2, 6);
+ xfree (pinvalue);
+ return gpg_error (GPG_ERR_BAD_PIN);
+ }
+
+ rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue));
+ if (rc)
+ {
+ log_error (_("verify CHV%d failed: %s\n"), 2, gpg_strerror (rc));
+ xfree (pinvalue);
+ flush_cache_after_error (app);
+ return rc;
+ }
+ app->did_chv2 = 1;
+
+ if (!app->did_chv1 && !app->force_chv1)
+ {
+ rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
+ if (gpg_err_code (rc) == GPG_ERR_BAD_PIN)
+ rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
+ if (rc)
+ {
+ log_error (_("verify CHV%d failed: %s\n"), 1, gpg_strerror (rc));
+ xfree (pinvalue);
+ flush_cache_after_error (app);
+ return rc;
+ }
+ app->did_chv1 = 1;
+ }
+ xfree (pinvalue);
+ }
+ return rc;
+}
+
+/* Verify CHV3 if required. */
+static gpg_error_t
+verify_chv3 (app_t app,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ int rc = 0;
+
+#if GNUPG_MAJOR_VERSION != 1
+ if (!opt.allow_admin)
+ {
+ log_info (_("access to admin commands is not configured\n"));
+ return gpg_error (GPG_ERR_EACCES);
+ }
+#endif
+
+ if (!app->did_chv3)
+ {
+ char *pinvalue;
+ void *relptr;
+ unsigned char *value;
+ size_t valuelen;
+
+ relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
+ if (!relptr || valuelen < 7)
+ {
+ log_error (_("error retrieving CHV status from card\n"));
+ xfree (relptr);
+ return gpg_error (GPG_ERR_CARD);
+ }
+ if (value[6] == 0)
+ {
+ log_info (_("card is permanently locked!\n"));
+ xfree (relptr);
+ return gpg_error (GPG_ERR_BAD_PIN);
+ }
+
+ log_info(_("%d Admin PIN attempts remaining before card"
+ " is permanently locked\n"), value[6]);
+ xfree (relptr);
+
+ /* TRANSLATORS: Do not translate the "|A|" prefix but
+ keep it at the start of the string. We need this elsewhere
+ to get some infos on the string. */
+ rc = pincb (pincb_arg, _("|A|Admin PIN"), &pinvalue);
+ if (rc)
+ {
+ log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc));
+ return rc;
+ }
+
+ if (strlen (pinvalue) < 8)
+ {
+ log_error (_("PIN for CHV%d is too short;"
+ " minimum length is %d\n"), 3, 8);
+ xfree (pinvalue);
+ return gpg_error (GPG_ERR_BAD_PIN);
+ }
+
+ rc = iso7816_verify (app->slot, 0x83, pinvalue, strlen (pinvalue));
+ xfree (pinvalue);
+ if (rc)
+ {
+ log_error (_("verify CHV%d failed: %s\n"), 3, gpg_strerror (rc));
+ flush_cache_after_error (app);
+ return rc;
+ }
+ app->did_chv3 = 1;
+ }
+ return rc;
+}
+
+
+/* Handle the SETATTR operation. All arguments are already basically
+ checked. */
+static gpg_error_t
+do_setattr (app_t app, const char *name,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *value, size_t valuelen)
+{
+ gpg_error_t rc;
+ int idx;
+ static struct {
+ const char *name;
+ int tag;
+ int need_chv;
+ int special;
+ } table[] = {
+ { "DISP-NAME", 0x005B, 3 },
+ { "LOGIN-DATA", 0x005E, 3, 2 },
+ { "DISP-LANG", 0x5F2D, 3 },
+ { "DISP-SEX", 0x5F35, 3 },
+ { "PUBKEY-URL", 0x5F50, 3 },
+ { "CHV-STATUS-1", 0x00C4, 3, 1 },
+ { "CA-FPR-1", 0x00CA, 3 },
+ { "CA-FPR-2", 0x00CB, 3 },
+ { "CA-FPR-3", 0x00CC, 3 },
+ { "PRIVATE-DO-1", 0x0101, 2 },
+ { "PRIVATE-DO-2", 0x0102, 3 },
+ { "PRIVATE-DO-3", 0x0103, 2 },
+ { "PRIVATE-DO-4", 0x0104, 3 },
+ { NULL, 0 }
+ };
+
+
+ for (idx=0; table[idx].name && strcmp (table[idx].name, name); idx++)
+ ;
+ if (!table[idx].name)
+ return gpg_error (GPG_ERR_INV_NAME);
+
+ switch (table[idx].need_chv)
+ {
+ case 2:
+ rc = verify_chv2 (app, pincb, pincb_arg);
+ break;
+ case 3:
+ rc = verify_chv3 (app, pincb, pincb_arg);
+ break;
+ default:
+ rc = 0;
+ }
+ if (rc)
+ return rc;
+
+ /* Flush the cache before writing it, so that the next get operation
+ will reread the data from the card and thus get synced in case of
+ errors (e.g. data truncated by the card). */
+ flush_cache_item (app, table[idx].tag);
+ rc = iso7816_put_data (app->slot, table[idx].tag, value, valuelen);
+ if (rc)
+ log_error ("failed to set `%s': %s\n", table[idx].name, gpg_strerror (rc));
+
+ if (table[idx].special == 1)
+ app->force_chv1 = (valuelen && *value == 0);
+ else if (table[idx].special == 2)
+ parse_login_data (app);
+
+ return rc;
+}
+
+
+/* Handle the PASSWD command. */
+static gpg_error_t
+do_change_pin (app_t app, ctrl_t ctrl, const char *chvnostr, int reset_mode,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ int rc = 0;
+ int chvno = atoi (chvnostr);
+ char *pinvalue;
+
+ if (reset_mode && chvno == 3)
+ {
+ rc = gpg_error (GPG_ERR_INV_ID);
+ goto leave;
+ }
+ else if (reset_mode || chvno == 3)
+ {
+ /* we always require that the PIN is entered. */
+ app->did_chv3 = 0;
+ rc = verify_chv3 (app, pincb, pincb_arg);
+ if (rc)
+ goto leave;
+ }
+ else if (chvno == 1 || chvno == 2)
+ {
+ /* CHV1 and CVH2 should always have the same value, thus we
+ enforce it here. */
+ int save_force = app->force_chv1;
+
+ app->force_chv1 = 0;
+ app->did_chv1 = 0;
+ app->did_chv2 = 0;
+ rc = verify_chv2 (app, pincb, pincb_arg);
+ app->force_chv1 = save_force;
+ if (rc)
+ goto leave;
+ }
+ else
+ {
+ rc = gpg_error (GPG_ERR_INV_ID);
+ goto leave;
+ }
+
+ if (chvno == 3)
+ app->did_chv3 = 0;
+ else
+ app->did_chv1 = app->did_chv2 = 0;
+
+ /* TRANSLATORS: Do not translate the "|*|" prefixes but
+ keep it at the start of the string. We need this elsewhere
+ to get some infos on the string. */
+ rc = pincb (pincb_arg, chvno == 3? _("|AN|New Admin PIN") : _("|N|New PIN"),
+ &pinvalue);
+ if (rc)
+ {
+ log_error (_("error getting new PIN: %s\n"), gpg_strerror (rc));
+ goto leave;
+ }
+
+ if (reset_mode)
+ {
+ rc = iso7816_reset_retry_counter (app->slot, 0x81,
+ pinvalue, strlen (pinvalue));
+ if (!rc)
+ rc = iso7816_reset_retry_counter (app->slot, 0x82,
+ pinvalue, strlen (pinvalue));
+ }
+ else
+ {
+ if (chvno == 1 || chvno == 2)
+ {
+ rc = iso7816_change_reference_data (app->slot, 0x81, NULL, 0,
+ pinvalue, strlen (pinvalue));
+ if (!rc)
+ rc = iso7816_change_reference_data (app->slot, 0x82, NULL, 0,
+ pinvalue, strlen (pinvalue));
+ }
+ else
+ rc = iso7816_change_reference_data (app->slot, 0x80 + chvno, NULL, 0,
+ pinvalue, strlen (pinvalue));
+ }
+ xfree (pinvalue);
+ if (rc)
+ flush_cache_after_error (app);
+
+ leave:
+ return rc;
+}
+
+
+/* Check whether a key already exists. KEYIDX is the index of the key
+ (0..2). If FORCE is TRUE a diagnositic will be printed but no
+ error returned if the key already exists. */
+static gpg_error_t
+does_key_exist (app_t app, int keyidx, int force)
+{
+ const unsigned char *fpr;
+ unsigned char *buffer;
+ size_t buflen, n;
+ int i;
+
+ assert (keyidx >=0 && keyidx <= 2);
+
+ if (iso7816_get_data (app->slot, 0x006E, &buffer, &buflen))
+ {
+ log_error (_("error reading application data\n"));
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ fpr = find_tlv (buffer, buflen, 0x00C5, &n);
+ if (!fpr || n < 60)
+ {
+ log_error (_("error reading fingerprint DO\n"));
+ xfree (buffer);
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ fpr += 20*keyidx;
+ for (i=0; i < 20 && !fpr[i]; i++)
+ ;
+ xfree (buffer);
+ if (i!=20 && !force)
+ {
+ log_error (_("key already exists\n"));
+ return gpg_error (GPG_ERR_EEXIST);
+ }
+ else if (i!=20)
+ log_info (_("existing key will be replaced\n"));
+ else
+ log_info (_("generating new key\n"));
+ return 0;
+}
+
+
+
+/* Handle the WRITEKEY command for OpenPGP. This function expects a
+ canonical encoded S-expression with the secret key in KEYDATA and
+ its length (for assertions) in KEYDATALEN. KEYID needs to be the
+ usual keyid which for OpenPGP is the string "OPENPGP.n" with
+ n=1,2,3. Bit 0 of FLAGS indicates whether an existing key shall
+ get overwritten. PINCB and PINCB_ARG are the usual arguments for
+ the pinentry callback. */
+static gpg_error_t
+do_writekey (app_t app, ctrl_t ctrl,
+ const char *keyid, unsigned int flags,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const unsigned char *keydata, size_t keydatalen)
+{
+ gpg_error_t err;
+ int force = (flags & 1);
+ int keyno;
+ const unsigned char *buf, *tok;
+ size_t buflen, toklen;
+ int depth, last_depth1, last_depth2;
+ const unsigned char *rsa_n = NULL;
+ const unsigned char *rsa_e = NULL;
+ const unsigned char *rsa_p = NULL;
+ const unsigned char *rsa_q = NULL;
+ size_t rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len;
+ unsigned int nbits;
+ unsigned char *template = NULL;
+ unsigned char *tp;
+ size_t template_len;
+ unsigned char fprbuf[20];
+ u32 created_at = 0;
+
+ if (!strcmp (keyid, "OPENPGP.1"))
+ keyno = 0;
+ else if (!strcmp (keyid, "OPENPGP.2"))
+ keyno = 1;
+ else if (!strcmp (keyid, "OPENPGP.3"))
+ keyno = 2;
+ else
+ return gpg_error (GPG_ERR_INV_ID);
+
+ err = does_key_exist (app, keyno, force);
+ if (err)
+ return err;
+
+
+ /*
+ Parse the S-expression
+ */
+ buf = keydata;
+ buflen = keydatalen;
+ depth = 0;
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (!tok || toklen != 11 || memcmp ("private-key", tok, toklen))
+ {
+ if (!tok)
+ ;
+ else if (toklen == 21 && !memcmp ("protected-private-key", tok, toklen))
+ log_info ("protected-private-key passed to writekey\n");
+ else if (toklen == 20 && !memcmp ("shadowed-private-key", tok, toklen))
+ log_info ("shadowed-private-key passed to writekey\n");
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (!tok || toklen != 3 || memcmp ("rsa", tok, toklen))
+ {
+ err = gpg_error (GPG_ERR_WRONG_PUBKEY_ALGO);
+ goto leave;
+ }
+ last_depth1 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth1)
+ {
+ if (tok)
+ {
+ err = gpg_error (GPG_ERR_UNKNOWN_SEXP);
+ goto leave;
+ }
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (tok && toklen == 1)
+ {
+ const unsigned char **mpi;
+ size_t *mpi_len;
+
+ switch (*tok)
+ {
+ case 'n': mpi = &rsa_n; mpi_len = &rsa_n_len; break;
+ case 'e': mpi = &rsa_e; mpi_len = &rsa_e_len; break;
+ case 'p': mpi = &rsa_p; mpi_len = &rsa_p_len; break;
+ case 'q': mpi = &rsa_q; mpi_len = &rsa_q_len;break;
+ default: mpi = NULL; mpi_len = NULL; break;
+ }
+ if (mpi && *mpi)
+ {
+ err = gpg_error (GPG_ERR_DUP_VALUE);
+ goto leave;
+ }
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (tok && mpi)
+ {
+ /* Strip off leading zero bytes and save. */
+ for (;toklen && !*tok; toklen--, tok++)
+ ;
+ *mpi = tok;
+ *mpi_len = toklen;
+ }
+ }
+ /* Skip until end of list. */
+ last_depth2 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth2)
+ ;
+ if (err)
+ goto leave;
+ }
+ /* Parse other attributes. */
+ last_depth1 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth1)
+ {
+ if (tok)
+ {
+ err = gpg_error (GPG_ERR_UNKNOWN_SEXP);
+ goto leave;
+ }
+ if ((err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen)))
+ goto leave;
+ if (tok && toklen == 10 && !memcmp ("created-at", tok, toklen))
+ {
+ if ((err = parse_sexp (&buf,&buflen,&depth,&tok,&toklen)))
+ goto leave;
+ if (tok)
+ {
+ for (created_at=0; toklen && *tok && *tok >= '0' && *tok <= '9';
+ tok++, toklen--)
+ created_at = created_at*10 + (*tok - '0');
+ }
+ }
+ /* Skip until end of list. */
+ last_depth2 = depth;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth && depth >= last_depth2)
+ ;
+ if (err)
+ goto leave;
+ }
+
+
+ /* Check that we have all parameters and that they match the card
+ description. */
+ if (!created_at)
+ {
+ log_error (_("creation timestamp missing\n"));
+ err = gpg_error (GPG_ERR_INV_VALUE);
+ goto leave;
+ }
+ nbits = rsa_n? count_bits (rsa_n, rsa_n_len) : 0;
+ if (nbits != 1024)
+ {
+ log_error (_("RSA modulus missing or not of size %d bits\n"), 1024);
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+ nbits = rsa_e? count_bits (rsa_e, rsa_e_len) : 0;
+ if (nbits < 2 || nbits > 32)
+ {
+ log_error (_("RSA public exponent missing or larger than %d bits\n"),
+ 32);
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+ nbits = rsa_p? count_bits (rsa_p, rsa_p_len) : 0;
+ if (nbits != 512)
+ {
+ log_error (_("RSA prime %s missing or not of size %d bits\n"), "P", 512);
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+ nbits = rsa_q? count_bits (rsa_q, rsa_q_len) : 0;
+ if (nbits != 512)
+ {
+ log_error (_("RSA prime %s missing or not of size %d bits\n"), "Q", 512);
+ err = gpg_error (GPG_ERR_BAD_SECKEY);
+ goto leave;
+ }
+
+
+ /* Build the private key template as described in section 4.3.3.6 of
+ the OpenPGP card specs:
+ 0xC0 <length> public exponent
+ 0xC1 <length> prime p
+ 0xC2 <length> prime q
+ */
+ assert (rsa_e_len <= 4);
+ template_len = (1 + 1 + 4
+ + 1 + 1 + rsa_p_len
+ + 1 + 1 + rsa_q_len);
+ template = tp = xtrymalloc_secure (template_len);
+ if (!template)
+ {
+ err = gpg_error_from_errno (errno);
+ goto leave;
+ }
+ *tp++ = 0xC0;
+ *tp++ = 4;
+ memcpy (tp, rsa_e, rsa_e_len);
+ if (rsa_e_len < 4)
+ {
+ /* Right justify E. */
+ memmove (tp+4-rsa_e_len, tp, rsa_e_len);
+ memset (tp, 0, 4-rsa_e_len);
+ }
+ tp += 4;
+
+ *tp++ = 0xC1;
+ *tp++ = rsa_p_len;
+ memcpy (tp, rsa_p, rsa_p_len);
+ tp += rsa_p_len;
+
+ *tp++ = 0xC2;
+ *tp++ = rsa_q_len;
+ memcpy (tp, rsa_q, rsa_q_len);
+ tp += rsa_q_len;
+
+ assert (tp - template == template_len);
+
+
+ /* Obviously we need to remove the cached public key. */
+ xfree (app->app_local->pk[keyno].key);
+ app->app_local->pk[keyno].key = NULL;
+ app->app_local->pk[keyno].keylen = 0;
+ app->app_local->pk[keyno].read_done = 0;
+
+ /* Prepare for storing the key. */
+ err = verify_chv3 (app, pincb, pincb_arg);
+ if (err)
+ goto leave;
+
+ /* Store the key. */
+ err = iso7816_put_data (app->slot,
+ (app->card_version > 0x0007? 0xE0 : 0xE9) + keyno,
+ template, template_len);
+ if (err)
+ {
+ log_error (_("failed to store the key: %s\n"), gpg_strerror (err));
+ goto leave;
+ }
+
+ err = store_fpr (app->slot, keyno, created_at,
+ rsa_n, rsa_n_len, rsa_e, rsa_e_len,
+ fprbuf, app->card_version);
+ if (err)
+ goto leave;
+
+
+ leave:
+ xfree (template);
+ return err;
+}
+
+
+/* Handle the GENKEY command. */
+static gpg_error_t
+do_genkey (app_t app, ctrl_t ctrl, const char *keynostr, unsigned int flags,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ int rc;
+ char numbuf[30];
+ unsigned char fprbuf[20];
+ const unsigned char *keydata, *m, *e;
+ unsigned char *buffer = NULL;
+ size_t buflen, keydatalen, mlen, elen;
+ time_t created_at;
+ int keyno = atoi (keynostr);
+ int force = (flags & 1);
+ time_t start_at;
+
+ if (keyno < 1 || keyno > 3)
+ return gpg_error (GPG_ERR_INV_ID);
+ keyno--;
+
+ /* We flush the cache to increase the traffic before a key
+ generation. This _might_ help a card to gather more entropy. */
+ flush_cache (app);
+
+ /* Obviously we need to remove the cached public key. */
+ xfree (app->app_local->pk[keyno].key);
+ app->app_local->pk[keyno].key = NULL;
+ app->app_local->pk[keyno].keylen = 0;
+ app->app_local->pk[keyno].read_done = 0;
+
+ /* Check whether a key already exists. */
+ rc = does_key_exist (app, keyno, force);
+ if (rc)
+ return rc;
+
+ /* Prepare for key generation by verifying the ADmin PIN. */
+ rc = verify_chv3 (app, pincb, pincb_arg);
+ if (rc)
+ goto leave;
+
+#if 1
+ log_info (_("please wait while key is being generated ...\n"));
+ start_at = time (NULL);
+ rc = iso7816_generate_keypair
+#else
+#warning key generation temporary replaced by reading an existing key.
+ rc = iso7816_read_public_key
+#endif
+ (app->slot,
+ keyno == 0? "\xB6" :
+ keyno == 1? "\xB8" : "\xA4",
+ 2,
+ &buffer, &buflen);
+ if (rc)
+ {
+ rc = gpg_error (GPG_ERR_CARD);
+ log_error (_("generating key failed\n"));
+ goto leave;
+ }
+ log_info (_("key generation completed (%d seconds)\n"),
+ (int)(time (NULL) - start_at));
+ keydata = find_tlv (buffer, buflen, 0x7F49, &keydatalen);
+ if (!keydata)
+ {
+ rc = gpg_error (GPG_ERR_CARD);
+ log_error (_("response does not contain the public key data\n"));
+ goto leave;
+ }
+
+ m = find_tlv (keydata, keydatalen, 0x0081, &mlen);
+ if (!m)
+ {
+ rc = gpg_error (GPG_ERR_CARD);
+ log_error (_("response does not contain the RSA modulus\n"));
+ goto leave;
+ }
+/* log_printhex ("RSA n:", m, mlen); */
+ send_key_data (ctrl, "n", m, mlen);
+
+ e = find_tlv (keydata, keydatalen, 0x0082, &elen);
+ if (!e)
+ {
+ rc = gpg_error (GPG_ERR_CARD);
+ log_error (_("response does not contain the RSA public exponent\n"));
+ goto leave;
+ }
+/* log_printhex ("RSA e:", e, elen); */
+ send_key_data (ctrl, "e", e, elen);
+
+ created_at = gnupg_get_time ();
+ sprintf (numbuf, "%lu", (unsigned long)created_at);
+ send_status_info (ctrl, "KEY-CREATED-AT",
+ numbuf, (size_t)strlen(numbuf), NULL, 0);
+
+ rc = store_fpr (app->slot, keyno, (u32)created_at,
+ m, mlen, e, elen, fprbuf, app->card_version);
+ if (rc)
+ goto leave;
+ send_fpr_if_not_null (ctrl, "KEY-FPR", -1, fprbuf);
+
+
+ leave:
+ xfree (buffer);
+ return rc;
+}
+
+
+static unsigned long
+convert_sig_counter_value (const unsigned char *value, size_t valuelen)
+{
+ unsigned long ul;
+
+ if (valuelen == 3 )
+ ul = (value[0] << 16) | (value[1] << 8) | value[2];
+ else
+ {
+ log_error (_("invalid structure of OpenPGP card (DO 0x93)\n"));
+ ul = 0;
+ }
+ return ul;
+}
+
+static unsigned long
+get_sig_counter (app_t app)
+{
+ void *relptr;
+ unsigned char *value;
+ size_t valuelen;
+ unsigned long ul;
+
+ relptr = get_one_do (app, 0x0093, &value, &valuelen, NULL);
+ if (!relptr)
+ return 0;
+ ul = convert_sig_counter_value (value, valuelen);
+ xfree (relptr);
+ return ul;
+}
+
+static gpg_error_t
+compare_fingerprint (app_t app, int keyno, unsigned char *sha1fpr)
+{
+ const unsigned char *fpr;
+ unsigned char *buffer;
+ size_t buflen, n;
+ int rc, i;
+
+ assert (keyno >= 1 && keyno <= 3);
+
+ rc = get_cached_data (app, 0x006E, &buffer, &buflen, 0);
+ if (rc)
+ {
+ log_error (_("error reading application data\n"));
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ fpr = find_tlv (buffer, buflen, 0x00C5, &n);
+ if (!fpr || n != 60)
+ {
+ xfree (buffer);
+ log_error (_("error reading fingerprint DO\n"));
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+ fpr += (keyno-1)*20;
+ for (i=0; i < 20; i++)
+ if (sha1fpr[i] != fpr[i])
+ {
+ xfree (buffer);
+ return gpg_error (GPG_ERR_WRONG_SECKEY);
+ }
+ xfree (buffer);
+ return 0;
+}
+
+
+ /* If a fingerprint has been specified check it against the one on
+ the card. This is allows for a meaningful error message in case
+ the key on the card has been replaced but the shadow information
+ known to gpg was not updated. If there is no fingerprint we
+ assume that this is okay. */
+static gpg_error_t
+check_against_given_fingerprint (app_t app, const char *fpr, int keyno)
+{
+ unsigned char tmp[20];
+ const char *s;
+ int n;
+
+ for (s=fpr, n=0; hexdigitp (s); s++, n++)
+ ;
+ if (n != 40)
+ return gpg_error (GPG_ERR_INV_ID);
+ else if (!*s)
+ ; /* okay */
+ else
+ return gpg_error (GPG_ERR_INV_ID);
+
+ for (s=fpr, n=0; n < 20; s += 2, n++)
+ tmp[n] = xtoi_2 (s);
+ return compare_fingerprint (app, keyno, tmp);
+}
+
+
+
+/* Compute a digital signature on INDATA which is expected to be the
+ raw message digest. For this application the KEYIDSTR consists of
+ the serialnumber and the fingerprint delimited by a slash.
+
+ Note that this fucntion may return the error code
+ GPG_ERR_WRONG_CARD to indicate that the card currently present does
+ not match the one required for the requested action (e.g. the
+ serial number does not match). */
+static gpg_error_t
+do_sign (app_t app, const char *keyidstr, int hashalgo,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen )
+{
+ static unsigned char sha1_prefix[15] = /* Object ID is 1.3.14.3.2.26 */
+ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x0e, 0x03,
+ 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14 };
+ static unsigned char rmd160_prefix[15] = /* Object ID is 1.3.36.3.2.1 */
+ { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b, 0x24, 0x03,
+ 0x02, 0x01, 0x05, 0x00, 0x04, 0x14 };
+ int rc;
+ unsigned char data[35];
+ unsigned char tmp_sn[20]; /* actually 16 but we use it also for the fpr. */
+ const char *s;
+ int n;
+ const char *fpr = NULL;
+ unsigned long sigcount;
+
+ if (!keyidstr || !*keyidstr)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (indatalen == 20)
+ ;
+ else if (indatalen == (15 + 20) && hashalgo == GCRY_MD_SHA1
+ && !memcmp (indata, sha1_prefix, 15))
+ ;
+ else if (indatalen == (15 + 20) && hashalgo == GCRY_MD_RMD160
+ && !memcmp (indata, rmd160_prefix, 15))
+ ;
+ else
+ {
+ log_error(_("card does not support digest algorithm %s\n"),
+ digest_algo_to_string(hashalgo));
+ return gpg_error (GPG_ERR_INV_VALUE);
+ }
+
+ /* Check whether an OpenPGP card of any version has been requested. */
+ if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
+ return gpg_error (GPG_ERR_INV_ID);
+
+ for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
+ ;
+ if (n != 32)
+ return gpg_error (GPG_ERR_INV_ID);
+ else if (!*s)
+ ; /* no fingerprint given: we allow this for now. */
+ else if (*s == '/')
+ fpr = s + 1;
+ else
+ return gpg_error (GPG_ERR_INV_ID);
+
+ for (s=keyidstr, n=0; n < 16; s += 2, n++)
+ tmp_sn[n] = xtoi_2 (s);
+
+ if (app->serialnolen != 16)
+ return gpg_error (GPG_ERR_INV_CARD);
+ if (memcmp (app->serialno, tmp_sn, 16))
+ return gpg_error (GPG_ERR_WRONG_CARD);
+
+ /* If a fingerprint has been specified check it against the one on
+ the card. This is allows for a meaningful error message in case
+ the key on the card has been replaced but the shadow information
+ known to gpg was not updated. If there is no fingerprint, gpg
+ will detect a bogus signature anyway due to the
+ verify-after-signing feature. */
+ rc = fpr? check_against_given_fingerprint (app, fpr, 1) : 0;
+ if (rc)
+ return rc;
+
+ if (hashalgo == GCRY_MD_SHA1)
+ memcpy (data, sha1_prefix, 15);
+ else if (hashalgo == GCRY_MD_RMD160)
+ memcpy (data, rmd160_prefix, 15);
+ else
+ return gpg_error (GPG_ERR_UNSUPPORTED_ALGORITHM);
+ memcpy (data+15, indata, indatalen);
+
+ sigcount = get_sig_counter (app);
+ log_info (_("signatures created so far: %lu\n"), sigcount);
+
+ if (!app->did_chv1 || app->force_chv1 )
+ {
+ char *pinvalue;
+
+ {
+ char *prompt;
+#define PROMPTSTRING _("||Please enter the PIN%%0A[sigs done: %lu]")
+
+ prompt = malloc (strlen (PROMPTSTRING) + 50);
+ if (!prompt)
+ return gpg_error_from_errno (errno);
+ sprintf (prompt, PROMPTSTRING, sigcount);
+ rc = pincb (pincb_arg, prompt, &pinvalue);
+ free (prompt);
+#undef PROMPTSTRING
+ }
+ if (rc)
+ {
+ log_info (_("PIN callback returned error: %s\n"), gpg_strerror (rc));
+ return rc;
+ }
+
+ if (strlen (pinvalue) < 6)
+ {
+ log_error (_("PIN for CHV%d is too short;"
+ " minimum length is %d\n"), 1, 6);
+ xfree (pinvalue);
+ return gpg_error (GPG_ERR_BAD_PIN);
+ }
+
+ rc = iso7816_verify (app->slot, 0x81, pinvalue, strlen (pinvalue));
+ if (rc)
+ {
+ log_error (_("verify CHV%d failed: %s\n"), 1, gpg_strerror (rc));
+ xfree (pinvalue);
+ flush_cache_after_error (app);
+ return rc;
+ }
+ app->did_chv1 = 1;
+ if (!app->did_chv2)
+ {
+ /* We should also verify CHV2. */
+ rc = iso7816_verify (app->slot, 0x82, pinvalue, strlen (pinvalue));
+ if (gpg_err_code (rc) == GPG_ERR_BAD_PIN)
+ rc = gpg_error (GPG_ERR_PIN_NOT_SYNCED);
+ if (rc)
+ {
+ log_error (_("verify CHV%d failed: %s\n"), 2, gpg_strerror (rc));
+ xfree (pinvalue);
+ flush_cache_after_error (app);
+ return rc;
+ }
+ app->did_chv2 = 1;
+ }
+ xfree (pinvalue);
+ }
+
+ rc = iso7816_compute_ds (app->slot, data, 35, outdata, outdatalen);
+ return rc;
+}
+
+/* Compute a digital signature using the INTERNAL AUTHENTICATE command
+ on INDATA which is expected to be the raw message digest. For this
+ application the KEYIDSTR consists of the serialnumber and the
+ fingerprint delimited by a slash. Optionally the id OPENPGP.3 may
+ be given.
+
+ Note that this fucntion may return the error code
+ GPG_ERR_WRONG_CARD to indicate that the card currently present does
+ not match the one required for the requested action (e.g. the
+ serial number does not match). */
+static gpg_error_t
+do_auth (app_t app, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen )
+{
+ int rc;
+ unsigned char tmp_sn[20]; /* actually 16 but we use it also for the fpr. */
+ const char *s;
+ int n;
+ const char *fpr = NULL;
+
+ if (!keyidstr || !*keyidstr)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ if (indatalen > 50) /* For a 1024 bit key. */
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* Check whether an OpenPGP card of any version has been requested. */
+ if (!strcmp (keyidstr, "OPENPGP.3"))
+ ;
+ else if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
+ return gpg_error (GPG_ERR_INV_ID);
+ else
+ {
+ for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
+ ;
+ if (n != 32)
+ return gpg_error (GPG_ERR_INV_ID);
+ else if (!*s)
+ ; /* no fingerprint given: we allow this for now. */
+ else if (*s == '/')
+ fpr = s + 1;
+ else
+ return gpg_error (GPG_ERR_INV_ID);
+
+ for (s=keyidstr, n=0; n < 16; s += 2, n++)
+ tmp_sn[n] = xtoi_2 (s);
+
+ if (app->serialnolen != 16)
+ return gpg_error (GPG_ERR_INV_CARD);
+ if (memcmp (app->serialno, tmp_sn, 16))
+ return gpg_error (GPG_ERR_WRONG_CARD);
+ }
+
+ /* If a fingerprint has been specified check it against the one on
+ the card. This is allows for a meaningful error message in case
+ the key on the card has been replaced but the shadow information
+ known to gpg was not updated. If there is no fingerprint, gpg
+ will detect a bogus signature anyway due to the
+ verify-after-signing feature. */
+ rc = fpr? check_against_given_fingerprint (app, fpr, 3) : 0;
+ if (rc)
+ return rc;
+
+ rc = verify_chv2 (app, pincb, pincb_arg);
+ if (!rc)
+ rc = iso7816_internal_authenticate (app->slot, indata, indatalen,
+ outdata, outdatalen);
+ return rc;
+}
+
+
+static gpg_error_t
+do_decipher (app_t app, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg,
+ const void *indata, size_t indatalen,
+ unsigned char **outdata, size_t *outdatalen )
+{
+ int rc;
+ unsigned char tmp_sn[20]; /* actually 16 but we use it also for the fpr. */
+ const char *s;
+ int n;
+ const char *fpr = NULL;
+
+ if (!keyidstr || !*keyidstr || !indatalen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* Check whether an OpenPGP card of any version has been requested. */
+ if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
+ return gpg_error (GPG_ERR_INV_ID);
+
+ for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
+ ;
+ if (n != 32)
+ return gpg_error (GPG_ERR_INV_ID);
+ else if (!*s)
+ ; /* no fingerprint given: we allow this for now. */
+ else if (*s == '/')
+ fpr = s + 1;
+ else
+ return gpg_error (GPG_ERR_INV_ID);
+
+ for (s=keyidstr, n=0; n < 16; s += 2, n++)
+ tmp_sn[n] = xtoi_2 (s);
+
+ if (app->serialnolen != 16)
+ return gpg_error (GPG_ERR_INV_CARD);
+ if (memcmp (app->serialno, tmp_sn, 16))
+ return gpg_error (GPG_ERR_WRONG_CARD);
+
+ /* If a fingerprint has been specified check it against the one on
+ the card. This is allows for a meaningful error message in case
+ the key on the card has been replaced but the shadow information
+ known to gpg was not updated. If there is no fingerprint, the
+ decryption will won't produce the right plaintext anyway. */
+ rc = fpr? check_against_given_fingerprint (app, fpr, 2) : 0;
+ if (rc)
+ return rc;
+
+ rc = verify_chv2 (app, pincb, pincb_arg);
+ if (!rc)
+ rc = iso7816_decipher (app->slot, indata, indatalen, 0,
+ outdata, outdatalen);
+ return rc;
+}
+
+
+/* Perform a simple verify operation for CHV1 and CHV2, so that
+ further operations won't ask for CHV2 and it is possible to do a
+ cheap check on the PIN: If there is something wrong with the PIN
+ entry system, only the regular CHV will get blocked and not the
+ dangerous CHV3. KEYIDSTR is the usual card's serial number; an
+ optional fingerprint part will be ignored.
+
+ There is a special mode if the keyidstr is "<serialno>[CHV3]" with
+ the "[CHV3]" being a literal string: The Admin Pin is checked if
+ and only if the retry counter is still at 3. */
+static gpg_error_t
+do_check_pin (app_t app, const char *keyidstr,
+ gpg_error_t (*pincb)(void*, const char *, char **),
+ void *pincb_arg)
+{
+ unsigned char tmp_sn[20];
+ const char *s;
+ int n;
+ int admin_pin = 0;
+
+ if (!keyidstr || !*keyidstr)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ /* Check whether an OpenPGP card of any version has been requested. */
+ if (strlen (keyidstr) < 32 || strncmp (keyidstr, "D27600012401", 12))
+ return gpg_error (GPG_ERR_INV_ID);
+
+ for (s=keyidstr, n=0; hexdigitp (s); s++, n++)
+ ;
+ if (n != 32)
+ return gpg_error (GPG_ERR_INV_ID);
+ else if (!*s)
+ ; /* No fingerprint given: we allow this for now. */
+ else if (*s == '/')
+ ; /* We ignore a fingerprint. */
+ else if (!strcmp (s, "[CHV3]") )
+ admin_pin = 1;
+ else
+ return gpg_error (GPG_ERR_INV_ID);
+
+ for (s=keyidstr, n=0; n < 16; s += 2, n++)
+ tmp_sn[n] = xtoi_2 (s);
+
+ if (app->serialnolen != 16)
+ return gpg_error (GPG_ERR_INV_CARD);
+ if (memcmp (app->serialno, tmp_sn, 16))
+ return gpg_error (GPG_ERR_WRONG_CARD);
+
+ /* Yes, there is a race conditions: The user might pull the card
+ right here and we won't notice that. However this is not a
+ problem and the check above is merely for a graceful failure
+ between operations. */
+
+ if (admin_pin)
+ {
+ void *relptr;
+ unsigned char *value;
+ size_t valuelen;
+ int count;
+
+ relptr = get_one_do (app, 0x00C4, &value, &valuelen, NULL);
+ if (!relptr || valuelen < 7)
+ {
+ log_error (_("error retrieving CHV status from card\n"));
+ xfree (relptr);
+ return gpg_error (GPG_ERR_CARD);
+ }
+ count = value[6];
+ xfree (relptr);
+
+ if (!count)
+ {
+ log_info (_("card is permanently locked!\n"));
+ return gpg_error (GPG_ERR_BAD_PIN);
+ }
+ else if (value[6] < 3)
+ {
+ log_info (_("verification of Admin PIN is currently prohibited "
+ "through this command\n"));
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+
+ app->did_chv3 = 0; /* Force verification. */
+ return verify_chv3 (app, pincb, pincb_arg);
+ }
+ else
+ return verify_chv2 (app, pincb, pincb_arg);
+}
+
+
+
+
+/* Select the OpenPGP application on the card in SLOT. This function
+ must be used before any other OpenPGP application functions. */
+gpg_error_t
+app_select_openpgp (app_t app)
+{
+ static char const aid[] = { 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01 };
+ int slot = app->slot;
+ int rc;
+ unsigned char *buffer;
+ size_t buflen;
+ void *relptr;
+
+ rc = iso7816_select_application (slot, aid, sizeof aid);
+ if (!rc)
+ {
+ unsigned int manufacturer;
+
+ app->apptype = "OPENPGP";
+
+ app->did_chv1 = 0;
+ app->did_chv2 = 0;
+ app->did_chv3 = 0;
+ app->app_local = NULL;
+
+ /* The OpenPGP card returns the serial number as part of the
+ AID; because we prefer to use OpenPGP serial numbers, we
+ replace a possibly already set one from a EF.GDO with this
+ one. Note, that for current OpenPGP cards, no EF.GDO exists
+ and thus it won't matter at all. */
+ rc = iso7816_get_data (slot, 0x004F, &buffer, &buflen);
+ if (rc)
+ goto leave;
+ if (opt.verbose)
+ {
+ log_info ("AID: ");
+ log_printhex ("", buffer, buflen);
+ }
+
+ app->card_version = buffer[6] << 8;
+ app->card_version |= buffer[7];
+ manufacturer = (buffer[8]<<8 | buffer[9]);
+
+ xfree (app->serialno);
+ app->serialno = buffer;
+ app->serialnolen = buflen;
+ buffer = NULL;
+ app->app_local = xtrycalloc (1, sizeof *app->app_local);
+ if (!app->app_local)
+ {
+ rc = gpg_error (gpg_err_code_from_errno (errno));
+ goto leave;
+ }
+
+ relptr = get_one_do (app, 0x00C4, &buffer, &buflen, NULL);
+ if (!relptr)
+ {
+ log_error (_("can't access %s - invalid OpenPGP card?\n"),
+ "CHV Status Bytes");
+ goto leave;
+ }
+ app->force_chv1 = (buflen && *buffer == 0);
+ xfree (relptr);
+
+ relptr = get_one_do (app, 0x00C0, &buffer, &buflen, NULL);
+ if (!relptr)
+ {
+ log_error (_("can't access %s - invalid OpenPGP card?\n"),
+ "Extended Capability Flags" );
+ goto leave;
+ }
+ if (buflen)
+ {
+ app->app_local->extcap.get_challenge = !!(*buffer & 0x40);
+ app->app_local->extcap.key_import = !!(*buffer & 0x20);
+ app->app_local->extcap.change_force_chv = !!(*buffer & 0x10);
+ app->app_local->extcap.private_dos = !!(*buffer & 0x08);
+ }
+ xfree (relptr);
+
+ /* Some of the first cards accidently don't set the
+ CHANGE_FORCE_CHV bit but allow it anyway. */
+ if (app->card_version <= 0x0100 && manufacturer == 1)
+ app->app_local->extcap.change_force_chv = 1;
+
+ parse_login_data (app);
+
+ if (opt.verbose > 1)
+ dump_all_do (slot);
+
+ app->fnc.deinit = do_deinit;
+ app->fnc.learn_status = do_learn_status;
+ app->fnc.readkey = do_readkey;
+ app->fnc.getattr = do_getattr;
+ app->fnc.setattr = do_setattr;
+ app->fnc.writekey = do_writekey;
+ app->fnc.genkey = do_genkey;
+ app->fnc.sign = do_sign;
+ app->fnc.auth = do_auth;
+ app->fnc.decipher = do_decipher;
+ app->fnc.change_pin = do_change_pin;
+ app->fnc.check_pin = do_check_pin;
+ }
+
+leave:
+ if (rc)
+ do_deinit (app);
+ return rc;
+}
+
+
+
diff --git a/g10/armor.c b/g10/armor.c
new file mode 100644
index 0000000..65804a7
--- /dev/null
+++ b/g10/armor.c
@@ -0,0 +1,1474 @@
+/* armor.c - Armor flter
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "errors.h"
+#include "iobuf.h"
+#include "memory.h"
+#include "util.h"
+#include "filter.h"
+#include "packet.h"
+#include "options.h"
+#include "main.h"
+#include "status.h"
+#include "i18n.h"
+
+#define MAX_LINELEN 20000
+
+#define CRCINIT 0xB704CE
+#define CRCPOLY 0X864CFB
+#define CRCUPDATE(a,c) do { \
+ a = ((a) << 8) ^ crc_table[((a)&0xff >> 16) ^ (c)]; \
+ a &= 0x00ffffff; \
+ } while(0)
+static u32 crc_table[256];
+static byte bintoasc[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
+ "abcdefghijklmnopqrstuvwxyz"
+ "0123456789+/";
+static byte asctobin[256]; /* runtime initialized */
+static int is_initialized;
+
+
+typedef enum {
+ fhdrHASArmor = 0,
+ fhdrNOArmor,
+ fhdrINIT,
+ fhdrINITCont,
+ fhdrINITSkip,
+ fhdrCHECKBegin,
+ fhdrWAITHeader,
+ fhdrWAITClearsig,
+ fhdrSKIPHeader,
+ fhdrCLEARSIG,
+ fhdrREADClearsig,
+ fhdrNullClearsig,
+ fhdrEMPTYClearsig,
+ fhdrCHECKClearsig,
+ fhdrCHECKClearsig2,
+ fhdrCHECKDashEscaped,
+ fhdrCHECKDashEscaped2,
+ fhdrCHECKDashEscaped3,
+ fhdrREADClearsigNext,
+ fhdrENDClearsig,
+ fhdrENDClearsigHelp,
+ fhdrTESTSpaces,
+ fhdrCLEARSIGSimple,
+ fhdrCLEARSIGSimpleNext,
+ fhdrTEXT,
+ fhdrTEXTSimple,
+ fhdrERROR,
+ fhdrERRORShow,
+ fhdrEOF
+} fhdr_state_t;
+
+
+/* if we encounter this armor string with this index, go
+ * into a mode which fakes packets and wait for the next armor */
+#define BEGIN_SIGNATURE 2
+#define BEGIN_SIGNED_MSG_IDX 3
+static char *head_strings[] = {
+ "BEGIN PGP MESSAGE",
+ "BEGIN PGP PUBLIC KEY BLOCK",
+ "BEGIN PGP SIGNATURE",
+ "BEGIN PGP SIGNED MESSAGE",
+ "BEGIN PGP ARMORED FILE", /* gnupg extension */
+ "BEGIN PGP PRIVATE KEY BLOCK",
+ "BEGIN PGP SECRET KEY BLOCK", /* only used by pgp2 */
+ NULL
+};
+static char *tail_strings[] = {
+ "END PGP MESSAGE",
+ "END PGP PUBLIC KEY BLOCK",
+ "END PGP SIGNATURE",
+ "END dummy",
+ "END PGP ARMORED FILE",
+ "END PGP PRIVATE KEY BLOCK",
+ "END PGP SECRET KEY BLOCK",
+ NULL
+};
+
+
+/* Create a new context for armor filters. */
+armor_filter_context_t *
+new_armor_context (void)
+{
+ armor_filter_context_t *afx;
+
+ afx = xcalloc (1, sizeof *afx);
+ afx->refcount = 1;
+
+ return afx;
+}
+
+/* Release an armor filter context. Passing NULL is explicitly
+ allowed and a no-op. */
+void
+release_armor_context (armor_filter_context_t *afx)
+{
+ if (!afx)
+ return;
+
+ /* In contrast to 2.0, we use in 1.4 heap based contexts only in a
+ very few places and in general keep the stack based contexts. A
+ REFCOUNT of 0 indicates a stack based context and thus we don't
+ do anything in this case. */
+ if (!afx->refcount)
+ return;
+
+ if ( --afx->refcount )
+ return;
+ xfree (afx);
+}
+
+/* Push the armor filter onto the iobuf stream IOBUF. */
+int
+push_armor_filter (armor_filter_context_t *afx, iobuf_t iobuf)
+{
+ int rc;
+
+ if (!afx->refcount)
+ return iobuf_push_filter (iobuf, armor_filter, afx);
+
+ afx->refcount++;
+ rc = iobuf_push_filter (iobuf, armor_filter, afx);
+ if (rc)
+ afx->refcount--;
+ return rc;
+}
+
+
+
+
+
+static void
+initialize(void)
+{
+ int i, j;
+ u32 t;
+ byte *s;
+
+ /* init the crc lookup table */
+ crc_table[0] = 0;
+ for(i=j=0; j < 128; j++ ) {
+ t = crc_table[j];
+ if( t & 0x00800000 ) {
+ t <<= 1;
+ crc_table[i++] = t ^ CRCPOLY;
+ crc_table[i++] = t;
+ }
+ else {
+ t <<= 1;
+ crc_table[i++] = t;
+ crc_table[i++] = t ^ CRCPOLY;
+ }
+ }
+ /* build the helptable for radix64 to bin conversion */
+ for(i=0; i < 256; i++ )
+ asctobin[i] = 255; /* used to detect invalid characters */
+ for(s=bintoasc,i=0; *s; s++,i++ )
+ asctobin[*s] = i;
+
+ is_initialized=1;
+}
+
+/****************
+ * Check whether this is an armored file or not See also
+ * parse-packet.c for details on this code For unknown historic
+ * reasons we use a string here but only the first byte will be used.
+ * Returns: True if it seems to be armored
+ */
+static int
+is_armored( const byte *buf )
+{
+ int ctb, pkttype;
+
+ ctb = *buf;
+ if( !(ctb & 0x80) )
+ return 1; /* invalid packet: assume it is armored */
+ pkttype = ctb & 0x40 ? (ctb & 0x3f) : ((ctb>>2)&0xf);
+ switch( pkttype ) {
+ case PKT_MARKER:
+ case PKT_SYMKEY_ENC:
+ case PKT_ONEPASS_SIG:
+ case PKT_PUBLIC_KEY:
+ case PKT_SECRET_KEY:
+ case PKT_PUBKEY_ENC:
+ case PKT_SIGNATURE:
+ case PKT_COMMENT:
+ case PKT_OLD_COMMENT:
+ case PKT_PLAINTEXT:
+ case PKT_COMPRESSED:
+ case PKT_ENCRYPTED:
+ return 0; /* seems to be a regular packet: not armored */
+ }
+
+ return 1;
+}
+
+
+/****************
+ * Try to check whether the iobuf is armored
+ * Returns true if this may be the case; the caller should use the
+ * filter to do further processing.
+ */
+int
+use_armor_filter( IOBUF a )
+{
+ byte buf[1];
+ int n;
+
+ /* fixme: there might be a problem with iobuf_peek */
+ n = iobuf_peek(a, buf, 1 );
+ if( n == -1 )
+ return 0; /* EOF, doesn't matter whether armored or not */
+ if( !n )
+ return 1; /* can't check it: try armored */
+ return is_armored(buf);
+}
+
+
+
+
+static void
+invalid_armor(void)
+{
+ write_status(STATUS_BADARMOR);
+ g10_exit(1); /* stop here */
+}
+
+
+/****************
+ * check whether the armor header is valid on a signed message.
+ * this is for security reasons: the header lines are not included in the
+ * hash and by using some creative formatting rules, Mallory could fake
+ * any text at the beginning of a document; assuming it is read with
+ * a simple viewer. We only allow the Hash Header.
+ */
+static int
+parse_hash_header( const char *line )
+{
+ const char *s, *s2;
+ unsigned found = 0;
+
+ if( strlen(line) < 6 || strlen(line) > 60 )
+ return 0; /* too short or too long */
+ if( memcmp( line, "Hash:", 5 ) )
+ return 0; /* invalid header */
+ s = line+5;
+ for(s=line+5;;s=s2) {
+ for(; *s && (*s==' ' || *s == '\t'); s++ )
+ ;
+ if( !*s )
+ break;
+ for(s2=s+1; *s2 && *s2!=' ' && *s2 != '\t' && *s2 != ','; s2++ )
+ ;
+ if( !strncmp( s, "RIPEMD160", s2-s ) )
+ found |= 1;
+ else if( !strncmp( s, "SHA1", s2-s ) )
+ found |= 2;
+ else if( !strncmp( s, "MD5", s2-s ) )
+ found |= 4;
+ else if( !strncmp( s, "SHA224", s2-s ) )
+ found |= 8;
+ else if( !strncmp( s, "SHA256", s2-s ) )
+ found |= 16;
+ else if( !strncmp( s, "SHA384", s2-s ) )
+ found |= 32;
+ else if( !strncmp( s, "SHA512", s2-s ) )
+ found |= 64;
+ else
+ return 0;
+ for(; *s2 && (*s2==' ' || *s2 == '\t'); s2++ )
+ ;
+ if( *s2 && *s2 != ',' )
+ return 0;
+ if( *s2 )
+ s2++;
+ }
+ return found;
+}
+
+
+
+/****************
+ * Check whether this is a armor line.
+ * returns: -1 if it is not a armor header or the index number of the
+ * armor header.
+ */
+static int
+is_armor_header( byte *line, unsigned len )
+{
+ const char *s;
+ byte *save_p, *p;
+ int save_c;
+ int i;
+
+ if( len < 15 )
+ return -1; /* too short */
+ if( memcmp( line, "-----", 5 ) )
+ return -1; /* no */
+ p = strstr( line+5, "-----");
+ if( !p )
+ return -1;
+ save_p = p;
+ p += 5;
+
+ /* Some Windows environments seem to add whitespace to the end of
+ the line, so we strip it here. This becomes strict if
+ --rfc2440 is set since 2440 reads "The header lines, therefore,
+ MUST start at the beginning of a line, and MUST NOT have text
+ following them on the same line." It is unclear whether "text"
+ refers to all text or just non-whitespace text. */
+
+ if(RFC2440)
+ {
+ if( *p == '\r' )
+ p++;
+ if( *p == '\n' )
+ p++;
+ }
+ else
+ while(*p==' ' || *p=='\r' || *p=='\n' || *p=='\t')
+ p++;
+
+ if( *p )
+ return -1; /* garbage after dashes */
+ save_c = *save_p; *save_p = 0;
+ p = line+5;
+ for(i=0; (s=head_strings[i]); i++ )
+ if( !strcmp(s, p) )
+ break;
+ *save_p = save_c;
+ if( !s )
+ return -1; /* unknown armor line */
+
+ if( opt.verbose > 1 )
+ log_info(_("armor: %s\n"), head_strings[i]);
+ return i;
+}
+
+
+
+/****************
+ * Parse a header lines
+ * Return 0: Empty line (end of header lines)
+ * -1: invalid header line
+ * >0: Good header line
+ */
+static int
+parse_header_line( armor_filter_context_t *afx, byte *line, unsigned int len )
+{
+ byte *p;
+ int hashes=0;
+ unsigned int len2;
+
+ len2 = check_trailing_ws( line, len );
+ if( !len2 ) {
+ afx->buffer_pos = len2; /* (it is not the fine way to do it here) */
+ return 0; /* WS only: same as empty line */
+ }
+
+ /*
+ This is fussy. The spec says that a header line is delimited
+ with a colon-space pair. This means that a line such as
+ "Comment: " (with nothing else) is actually legal as an empty
+ string comment. However, email and cut-and-paste being what it
+ is, that trailing space may go away. Therefore, we accept empty
+ headers delimited with only a colon. --rfc2440, as always,
+ makes this strict and enforces the colon-space pair. -dms
+ */
+
+ p = strchr( line, ':');
+ if( !p || (RFC2440 && p[1]!=' ')
+ || (!RFC2440 && p[1]!=' ' && p[1]!='\n' && p[1]!='\r'))
+ {
+ log_error(_("invalid armor header: "));
+ print_string( stderr, line, len, 0 );
+ putc('\n', stderr);
+ return -1;
+ }
+
+ /* Chop off the whitespace we detected before */
+ len=len2;
+ line[len2]='\0';
+
+ if( opt.verbose ) {
+ log_info(_("armor header: "));
+ print_string( stderr, line, len, 0 );
+ putc('\n', stderr);
+ }
+
+ if( afx->in_cleartext ) {
+ if( (hashes=parse_hash_header( line )) )
+ afx->hashes |= hashes;
+ else if( strlen(line) > 15 && !memcmp( line, "NotDashEscaped:", 15 ) )
+ afx->not_dash_escaped = 1;
+ else {
+ log_error(_("invalid clearsig header\n"));
+ return -1;
+ }
+ }
+ return 1;
+}
+
+
+
+/* figure out whether the data is armored or not */
+static int
+check_input( armor_filter_context_t *afx, IOBUF a )
+{
+ int rc = 0;
+ int i;
+ byte *line;
+ unsigned len;
+ unsigned maxlen;
+ int hdr_line = -1;
+
+ /* read the first line to see whether this is armored data */
+ maxlen = MAX_LINELEN;
+ len = afx->buffer_len = iobuf_read_line( a, &afx->buffer,
+ &afx->buffer_size, &maxlen );
+ line = afx->buffer;
+ if( !maxlen ) {
+ /* line has been truncated: assume not armored */
+ afx->inp_checked = 1;
+ afx->inp_bypass = 1;
+ return 0;
+ }
+
+ if( !len ) {
+ return -1; /* eof */
+ }
+
+ /* (the line is always a C string but maybe longer) */
+ if( *line == '\n' || ( len && (*line == '\r' && line[1]=='\n') ) )
+ ;
+ else if( !is_armored( line ) ) {
+ afx->inp_checked = 1;
+ afx->inp_bypass = 1;
+ return 0;
+ }
+
+ /* find the armor header */
+ while(len) {
+ i = is_armor_header( line, len );
+ if( i >= 0 && !(afx->only_keyblocks && i != 1 && i != 5 && i != 6 )) {
+ hdr_line = i;
+ if( hdr_line == BEGIN_SIGNED_MSG_IDX ) {
+ if( afx->in_cleartext ) {
+ log_error(_("nested clear text signatures\n"));
+ rc = G10ERR_INVALID_ARMOR;
+ }
+ afx->in_cleartext = 1;
+ }
+ break;
+ }
+ /* read the next line (skip all truncated lines) */
+ do {
+ maxlen = MAX_LINELEN;
+ afx->buffer_len = iobuf_read_line( a, &afx->buffer,
+ &afx->buffer_size, &maxlen );
+ line = afx->buffer;
+ len = afx->buffer_len;
+ } while( !maxlen );
+ }
+
+ /* Parse the header lines. */
+ while(len) {
+ /* Read the next line (skip all truncated lines). */
+ do {
+ maxlen = MAX_LINELEN;
+ afx->buffer_len = iobuf_read_line( a, &afx->buffer,
+ &afx->buffer_size, &maxlen );
+ line = afx->buffer;
+ len = afx->buffer_len;
+ } while( !maxlen );
+
+ i = parse_header_line( afx, line, len );
+ if( i <= 0 ) {
+ if (i && RFC2440)
+ rc = G10ERR_INVALID_ARMOR;
+ break;
+ }
+ }
+
+
+ if( rc )
+ invalid_armor();
+ else if( afx->in_cleartext )
+ afx->faked = 1;
+ else {
+ afx->inp_checked = 1;
+ afx->crc = CRCINIT;
+ afx->idx = 0;
+ afx->radbuf[0] = 0;
+ }
+
+ return rc;
+}
+
+#define PARTIAL_CHUNK 512
+#define PARTIAL_POW 9
+
+/****************
+ * Fake a literal data packet and wait for the next armor line
+ * fixme: empty line handling and null length clear text signature are
+ * not implemented/checked.
+ */
+static int
+fake_packet( armor_filter_context_t *afx, IOBUF a,
+ size_t *retn, byte *buf, size_t size )
+{
+ int rc = 0;
+ size_t len = 0;
+ int lastline = 0;
+ unsigned maxlen, n;
+ byte *p;
+ byte tempbuf[PARTIAL_CHUNK];
+ size_t tempbuf_len=0;
+
+ while( !rc && size-len>=(PARTIAL_CHUNK+1)) {
+ /* copy what we have in the line buffer */
+ if( afx->faked == 1 )
+ afx->faked++; /* skip the first (empty) line */
+ else
+ {
+ /* It's full, so write this partial chunk */
+ if(tempbuf_len==PARTIAL_CHUNK)
+ {
+ buf[len++]=0xE0+PARTIAL_POW;
+ memcpy(&buf[len],tempbuf,PARTIAL_CHUNK);
+ len+=PARTIAL_CHUNK;
+ tempbuf_len=0;
+ continue;
+ }
+
+ while( tempbuf_len < PARTIAL_CHUNK
+ && afx->buffer_pos < afx->buffer_len )
+ tempbuf[tempbuf_len++] = afx->buffer[afx->buffer_pos++];
+ if( tempbuf_len==PARTIAL_CHUNK )
+ continue;
+ }
+
+ /* read the next line */
+ maxlen = MAX_LINELEN;
+ afx->buffer_pos = 0;
+ afx->buffer_len = iobuf_read_line( a, &afx->buffer,
+ &afx->buffer_size, &maxlen );
+ if( !afx->buffer_len ) {
+ rc = -1; /* eof (should not happen) */
+ continue;
+ }
+ if( !maxlen )
+ afx->truncated++;
+
+ p = afx->buffer;
+ n = afx->buffer_len;
+
+ /* Armor header or dash-escaped line? */
+ if(p[0]=='-')
+ {
+ /* 2440bis-10: When reversing dash-escaping, an
+ implementation MUST strip the string "- " if it occurs
+ at the beginning of a line, and SHOULD warn on "-" and
+ any character other than a space at the beginning of a
+ line. */
+
+ if(p[1]==' ' && !afx->not_dash_escaped)
+ {
+ /* It's a dash-escaped line, so skip over the
+ escape. */
+ afx->buffer_pos = 2;
+ }
+ else if(p[1]=='-' && p[2]=='-' && p[3]=='-' && p[4]=='-')
+ {
+ /* Five dashes in a row mean it's probably armor
+ header. */
+ int type = is_armor_header( p, n );
+ if( afx->not_dash_escaped && type != BEGIN_SIGNATURE )
+ ; /* this is okay */
+ else
+ {
+ if( type != BEGIN_SIGNATURE )
+ {
+ log_info(_("unexpected armor: "));
+ print_string( stderr, p, n, 0 );
+ putc('\n', stderr);
+ }
+
+ lastline = 1;
+ rc = -1;
+ }
+ }
+ else if(!afx->not_dash_escaped)
+ {
+ /* Bad dash-escaping. */
+ log_info(_("invalid dash escaped line: "));
+ print_string( stderr, p, n, 0 );
+ putc('\n', stderr);
+ }
+ }
+
+ /* Now handle the end-of-line canonicalization */
+ if( !afx->not_dash_escaped )
+ {
+ int crlf = n > 1 && p[n-2] == '\r' && p[n-1]=='\n';
+
+ /* PGP2 does not treat a tab as white space character */
+ afx->buffer_len=
+ trim_trailing_chars( &p[afx->buffer_pos], n-afx->buffer_pos,
+ afx->pgp2mode ? " \r\n" : " \t\r\n");
+ afx->buffer_len+=afx->buffer_pos;
+ /* the buffer is always allocated with enough space to append
+ * the removed [CR], LF and a Nul
+ * The reason for this complicated procedure is to keep at least
+ * the original type of lineending - handling of the removed
+ * trailing spaces seems to be impossible in our method
+ * of faking a packet; either we have to use a temporary file
+ * or calculate the hash here in this module and somehow find
+ * a way to send the hash down the processing line (well, a special
+ * faked packet could do the job).
+ */
+ if( crlf )
+ afx->buffer[afx->buffer_len++] = '\r';
+ afx->buffer[afx->buffer_len++] = '\n';
+ afx->buffer[afx->buffer_len] = '\0';
+ }
+ }
+
+ if( lastline ) { /* write last (ending) length header */
+ if(tempbuf_len<192)
+ buf[len++]=tempbuf_len;
+ else
+ {
+ buf[len++]=((tempbuf_len-192)/256) + 192;
+ buf[len++]=(tempbuf_len-192) % 256;
+ }
+ memcpy(&buf[len],tempbuf,tempbuf_len);
+ len+=tempbuf_len;
+
+ rc = 0;
+ afx->faked = 0;
+ afx->in_cleartext = 0;
+ /* and now read the header lines */
+ afx->buffer_pos = 0;
+ for(;;) {
+ int i;
+
+ /* read the next line (skip all truncated lines) */
+ do {
+ maxlen = MAX_LINELEN;
+ afx->buffer_len = iobuf_read_line( a, &afx->buffer,
+ &afx->buffer_size, &maxlen );
+ } while( !maxlen );
+ p = afx->buffer;
+ n = afx->buffer_len;
+ if( !n ) {
+ rc = -1;
+ break; /* eof */
+ }
+ i = parse_header_line( afx, p , n );
+ if( i <= 0 ) {
+ if( i )
+ invalid_armor();
+ break;
+ }
+ }
+ afx->inp_checked = 1;
+ afx->crc = CRCINIT;
+ afx->idx = 0;
+ afx->radbuf[0] = 0;
+ }
+
+ *retn = len;
+ return rc;
+}
+
+
+static int
+invalid_crc(void)
+{
+ if ( opt.ignore_crc_error )
+ return 0;
+ log_inc_errorcount();
+ return G10ERR_INVALID_ARMOR;
+}
+
+
+static int
+radix64_read( armor_filter_context_t *afx, IOBUF a, size_t *retn,
+ byte *buf, size_t size )
+{
+ byte val;
+ int c=0, c2; /*init c because gcc is not clever enough for the continue*/
+ int checkcrc=0;
+ int rc = 0;
+ size_t n = 0;
+ int idx, i, onlypad=0;
+ u32 crc;
+
+ crc = afx->crc;
+ idx = afx->idx;
+ val = afx->radbuf[0];
+ for( n=0; n < size; ) {
+
+ if( afx->buffer_pos < afx->buffer_len )
+ c = afx->buffer[afx->buffer_pos++];
+ else { /* read the next line */
+ unsigned maxlen = MAX_LINELEN;
+ afx->buffer_pos = 0;
+ afx->buffer_len = iobuf_read_line( a, &afx->buffer,
+ &afx->buffer_size, &maxlen );
+ if( !maxlen )
+ afx->truncated++;
+ if( !afx->buffer_len )
+ break; /* eof */
+ continue;
+ }
+
+ again:
+ if( c == '\n' || c == ' ' || c == '\r' || c == '\t' )
+ continue;
+ else if( c == '=' ) { /* pad character: stop */
+ /* some mailers leave quoted-printable encoded characters
+ * so we try to workaround this */
+ if( afx->buffer_pos+2 < afx->buffer_len ) {
+ int cc1, cc2, cc3;
+ cc1 = afx->buffer[afx->buffer_pos];
+ cc2 = afx->buffer[afx->buffer_pos+1];
+ cc3 = afx->buffer[afx->buffer_pos+2];
+ if( isxdigit(cc1) && isxdigit(cc2)
+ && strchr( "=\n\r\t ", cc3 )) {
+ /* well it seems to be the case - adjust */
+ c = isdigit(cc1)? (cc1 - '0'): (ascii_toupper(cc1)-'A'+10);
+ c <<= 4;
+ c |= isdigit(cc2)? (cc2 - '0'): (ascii_toupper(cc2)-'A'+10);
+ afx->buffer_pos += 2;
+ afx->qp_detected = 1;
+ goto again;
+ }
+ }
+ else if(n==0)
+ onlypad=1;
+
+ if( idx == 1 )
+ buf[n++] = val;
+ checkcrc++;
+ break;
+ }
+ else if( (c = asctobin[(c2=c)]) == 255 ) {
+ log_error(_("invalid radix64 character %02X skipped\n"), c2);
+ continue;
+ }
+ switch(idx) {
+ case 0: val = c << 2; break;
+ case 1: val |= (c>>4)&3; buf[n++]=val;val=(c<<4)&0xf0;break;
+ case 2: val |= (c>>2)&15; buf[n++]=val;val=(c<<6)&0xc0;break;
+ case 3: val |= c&0x3f; buf[n++] = val; break;
+ }
+ idx = (idx+1) % 4;
+ }
+
+ for(i=0; i < n; i++ )
+ crc = (crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ buf[i]];
+ crc &= 0x00ffffff;
+ afx->crc = crc;
+ afx->idx = idx;
+ afx->radbuf[0] = val;
+
+ if( checkcrc ) {
+ afx->any_data = 1;
+ afx->inp_checked=0;
+ afx->faked = 0;
+ for(;;) { /* skip lf and pad characters */
+ if( afx->buffer_pos < afx->buffer_len )
+ c = afx->buffer[afx->buffer_pos++];
+ else { /* read the next line */
+ unsigned maxlen = MAX_LINELEN;
+ afx->buffer_pos = 0;
+ afx->buffer_len = iobuf_read_line( a, &afx->buffer,
+ &afx->buffer_size, &maxlen );
+ if( !maxlen )
+ afx->truncated++;
+ if( !afx->buffer_len )
+ break; /* eof */
+ continue;
+ }
+ if( c == '\n' || c == ' ' || c == '\r'
+ || c == '\t' || c == '=' )
+ continue;
+ break;
+ }
+ if( c == -1 )
+ log_error(_("premature eof (no CRC)\n"));
+ else {
+ u32 mycrc = 0;
+ idx = 0;
+ do {
+ if( (c = asctobin[c]) == 255 )
+ break;
+ switch(idx) {
+ case 0: val = c << 2; break;
+ case 1: val |= (c>>4)&3; mycrc |= val << 16;val=(c<<4)&0xf0;break;
+ case 2: val |= (c>>2)&15; mycrc |= val << 8;val=(c<<6)&0xc0;break;
+ case 3: val |= c&0x3f; mycrc |= val; break;
+ }
+ for(;;) {
+ if( afx->buffer_pos < afx->buffer_len )
+ c = afx->buffer[afx->buffer_pos++];
+ else { /* read the next line */
+ unsigned maxlen = MAX_LINELEN;
+ afx->buffer_pos = 0;
+ afx->buffer_len = iobuf_read_line( a, &afx->buffer,
+ &afx->buffer_size,
+ &maxlen );
+ if( !maxlen )
+ afx->truncated++;
+ if( !afx->buffer_len )
+ break; /* eof */
+ continue;
+ }
+ break;
+ }
+ if( !afx->buffer_len )
+ break; /* eof */
+ } while( ++idx < 4 );
+ if( c == -1 ) {
+ log_info(_("premature eof (in CRC)\n"));
+ rc = invalid_crc();
+ }
+ else if( idx == 0 ) {
+ /* No CRC at all is legal ("MAY") */
+ rc=0;
+ }
+ else if( idx != 4 ) {
+ log_info(_("malformed CRC\n"));
+ rc = invalid_crc();
+ }
+ else if( mycrc != afx->crc ) {
+ log_info (_("CRC error; %06lX - %06lX\n"),
+ (ulong)afx->crc, (ulong)mycrc);
+ rc = invalid_crc();
+ }
+ else {
+ rc = 0;
+ /* FIXME: Here we should emit another control packet,
+ * so that we know in mainproc that we are processing
+ * a clearsign message */
+#if 0
+ for(rc=0;!rc;) {
+ rc = 0 /*check_trailer( &fhdr, c )*/;
+ if( !rc ) {
+ if( (c=iobuf_get(a)) == -1 )
+ rc = 2;
+ }
+ }
+ if( rc == -1 )
+ rc = 0;
+ else if( rc == 2 ) {
+ log_error(_("premature eof (in trailer)\n"));
+ rc = G10ERR_INVALID_ARMOR;
+ }
+ else {
+ log_error(_("error in trailer line\n"));
+ rc = G10ERR_INVALID_ARMOR;
+ }
+#endif
+ }
+ }
+ }
+
+ if( !n && !onlypad )
+ rc = -1;
+
+ *retn = n;
+ return rc;
+}
+
+/****************
+ * This filter is used to handle the armor stuff
+ */
+int
+armor_filter( void *opaque, int control,
+ IOBUF a, byte *buf, size_t *ret_len)
+{
+ size_t size = *ret_len;
+ armor_filter_context_t *afx = opaque;
+ int rc=0, i, c;
+ byte radbuf[3];
+ int idx, idx2;
+ size_t n=0;
+ u32 crc;
+#if 0
+ static FILE *fp ;
+
+ if( !fp ) {
+ fp = fopen("armor.out", "w");
+ assert(fp);
+ }
+#endif
+
+ if( DBG_FILTER )
+ log_debug("armor-filter: control: %d\n", control );
+ if( control == IOBUFCTRL_UNDERFLOW && afx->inp_bypass ) {
+ n = 0;
+ if( afx->buffer_len ) {
+ for(; n < size && afx->buffer_pos < afx->buffer_len; n++ )
+ buf[n++] = afx->buffer[afx->buffer_pos++];
+ if( afx->buffer_pos >= afx->buffer_len )
+ afx->buffer_len = 0;
+ }
+ for(; n < size; n++ ) {
+ if( (c=iobuf_get(a)) == -1 )
+ break;
+ buf[n] = c & 0xff;
+ }
+ if( !n )
+ rc = -1;
+ *ret_len = n;
+ }
+ else if( control == IOBUFCTRL_UNDERFLOW ) {
+ /* We need some space for the faked packet. The minmum
+ * required size is the PARTIAL_CHUNK size plus a byte for the
+ * length itself */
+ if( size < PARTIAL_CHUNK+1 )
+ BUG(); /* supplied buffer too short */
+
+ if( afx->faked )
+ rc = fake_packet( afx, a, &n, buf, size );
+ else if( !afx->inp_checked ) {
+ rc = check_input( afx, a );
+ if( afx->inp_bypass ) {
+ for(n=0; n < size && afx->buffer_pos < afx->buffer_len; )
+ buf[n++] = afx->buffer[afx->buffer_pos++];
+ if( afx->buffer_pos >= afx->buffer_len )
+ afx->buffer_len = 0;
+ if( !n )
+ rc = -1;
+ }
+ else if( afx->faked ) {
+ unsigned int hashes = afx->hashes;
+ const byte *sesmark;
+ size_t sesmarklen;
+
+ sesmark = get_session_marker( &sesmarklen );
+ if ( sesmarklen > 20 )
+ BUG();
+
+ /* the buffer is at least 15+n*15 bytes long, so it
+ * is easy to construct the packets */
+
+ hashes &= 1|2|4|8|16|32|64;
+ if( !hashes ) {
+ hashes |= 4; /* default to MD 5 */
+ /* This is non-ideal since PGP 5-8 have the same
+ end-of-line bugs as PGP 2. However, we only
+ enable pgp2mode if there is no Hash: header. */
+ if( opt.pgp2_workarounds )
+ afx->pgp2mode = 1;
+ }
+ n=0;
+ /* First a gpg control packet... */
+ buf[n++] = 0xff; /* new format, type 63, 1 length byte */
+ n++; /* see below */
+ memcpy(buf+n, sesmark, sesmarklen ); n+= sesmarklen;
+ buf[n++] = CTRLPKT_CLEARSIGN_START;
+ buf[n++] = afx->not_dash_escaped? 0:1; /* sigclass */
+ if( hashes & 1 )
+ buf[n++] = DIGEST_ALGO_RMD160;
+ if( hashes & 2 )
+ buf[n++] = DIGEST_ALGO_SHA1;
+ if( hashes & 4 )
+ buf[n++] = DIGEST_ALGO_MD5;
+ if( hashes & 8 )
+ buf[n++] = DIGEST_ALGO_SHA224;
+ if( hashes & 16 )
+ buf[n++] = DIGEST_ALGO_SHA256;
+ if( hashes & 32 )
+ buf[n++] = DIGEST_ALGO_SHA384;
+ if( hashes & 64 )
+ buf[n++] = DIGEST_ALGO_SHA512;
+ buf[1] = n - 2;
+
+ /* ...followed by an invented plaintext packet.
+ Amusingly enough, this packet is not compliant with
+ 2440 as the initial partial length is less than 512
+ bytes. Of course, we'll accept it anyway ;) */
+
+ buf[n++] = 0xCB; /* new packet format, type 11 */
+ buf[n++] = 0xE1; /* 2^1 == 2 bytes */
+ buf[n++] = 't'; /* canonical text mode */
+ buf[n++] = 0; /* namelength */
+ buf[n++] = 0xE2; /* 2^2 == 4 more bytes */
+ memset(buf+n, 0, 4); /* timestamp */
+ n += 4;
+ }
+ else if( !rc )
+ rc = radix64_read( afx, a, &n, buf, size );
+ }
+ else
+ rc = radix64_read( afx, a, &n, buf, size );
+#if 0
+ if( n )
+ if( fwrite(buf, n, 1, fp ) != 1 )
+ BUG();
+#endif
+ *ret_len = n;
+ }
+ else if( control == IOBUFCTRL_FLUSH && !afx->cancel ) {
+ if( !afx->status ) { /* write the header line */
+ const char *s;
+ STRLIST comment=opt.comments;
+
+ if( afx->what >= DIM(head_strings) )
+ log_bug("afx->what=%d", afx->what);
+ iobuf_writestr(a, "-----");
+ iobuf_writestr(a, head_strings[afx->what] );
+ iobuf_writestr(a, "-----" );
+ iobuf_writestr(a,afx->eol);
+ if( !opt.no_version )
+ {
+ iobuf_writestr(a, "Version: GnuPG v" VERSION " ("
+ PRINTABLE_OS_NAME ")" );
+ iobuf_writestr(a,afx->eol);
+ }
+
+ /* write the comment strings */
+ for(s=comment->d;comment;comment=comment->next,s=comment->d)
+ {
+ iobuf_writestr(a, "Comment: " );
+ for( ; *s; s++ )
+ {
+ if( *s == '\n' )
+ iobuf_writestr(a, "\\n" );
+ else if( *s == '\r' )
+ iobuf_writestr(a, "\\r" );
+ else if( *s == '\v' )
+ iobuf_writestr(a, "\\v" );
+ else
+ iobuf_put(a, *s );
+ }
+
+ iobuf_writestr(a,afx->eol);
+ }
+
+ if ( afx->hdrlines ) {
+ for ( s = afx->hdrlines; *s; s++ ) {
+#ifdef HAVE_DOSISH_SYSTEM
+ if ( *s == '\n' )
+ iobuf_put( a, '\r');
+#endif
+ iobuf_put(a, *s );
+ }
+ }
+
+ iobuf_writestr(a,afx->eol);
+ afx->status++;
+ afx->idx = 0;
+ afx->idx2 = 0;
+ afx->crc = CRCINIT;
+
+ }
+ crc = afx->crc;
+ idx = afx->idx;
+ idx2 = afx->idx2;
+ for(i=0; i < idx; i++ )
+ radbuf[i] = afx->radbuf[i];
+
+ for(i=0; i < size; i++ )
+ crc = (crc << 8) ^ crc_table[((crc >> 16)&0xff) ^ buf[i]];
+ crc &= 0x00ffffff;
+
+ for( ; size; buf++, size-- ) {
+ radbuf[idx++] = *buf;
+ if( idx > 2 ) {
+ idx = 0;
+ c = bintoasc[(*radbuf >> 2) & 077];
+ iobuf_put(a, c);
+ c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
+ iobuf_put(a, c);
+ c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
+ iobuf_put(a, c);
+ c = bintoasc[radbuf[2]&077];
+ iobuf_put(a, c);
+ if( ++idx2 >= (64/4) )
+ { /* pgp doesn't like 72 here */
+ iobuf_writestr(a,afx->eol);
+ idx2=0;
+ }
+ }
+ }
+ for(i=0; i < idx; i++ )
+ afx->radbuf[i] = radbuf[i];
+ afx->idx = idx;
+ afx->idx2 = idx2;
+ afx->crc = crc;
+ }
+ else if( control == IOBUFCTRL_INIT )
+ {
+ if( !is_initialized )
+ initialize();
+
+ /* Figure out what we're using for line endings if the caller
+ didn't specify. */
+ if(afx->eol[0]==0)
+ {
+#ifdef HAVE_DOSISH_SYSTEM
+ afx->eol[0]='\r';
+ afx->eol[1]='\n';
+#else
+ afx->eol[0]='\n';
+#endif
+ }
+ }
+ else if( control == IOBUFCTRL_CANCEL ) {
+ afx->cancel = 1;
+ }
+ else if( control == IOBUFCTRL_FREE ) {
+ if( afx->cancel )
+ ;
+ else if( afx->status ) { /* pad, write cecksum, and bottom line */
+ crc = afx->crc;
+ idx = afx->idx;
+ idx2 = afx->idx2;
+ for(i=0; i < idx; i++ )
+ radbuf[i] = afx->radbuf[i];
+ if( idx ) {
+ c = bintoasc[(*radbuf>>2)&077];
+ iobuf_put(a, c);
+ if( idx == 1 ) {
+ c = bintoasc[((*radbuf << 4) & 060) & 077];
+ iobuf_put(a, c);
+ iobuf_put(a, '=');
+ iobuf_put(a, '=');
+ }
+ else { /* 2 */
+ c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1]>>4)&017))&077];
+ iobuf_put(a, c);
+ c = bintoasc[((radbuf[1] << 2) & 074) & 077];
+ iobuf_put(a, c);
+ iobuf_put(a, '=');
+ }
+ if( ++idx2 >= (64/4) )
+ { /* pgp doesn't like 72 here */
+ iobuf_writestr(a,afx->eol);
+ idx2=0;
+ }
+ }
+ /* may need a linefeed */
+ if( idx2 )
+ iobuf_writestr(a,afx->eol);
+ /* write the CRC */
+ iobuf_put(a, '=');
+ radbuf[0] = crc >>16;
+ radbuf[1] = crc >> 8;
+ radbuf[2] = crc;
+ c = bintoasc[(*radbuf >> 2) & 077];
+ iobuf_put(a, c);
+ c = bintoasc[(((*radbuf<<4)&060)|((radbuf[1] >> 4)&017))&077];
+ iobuf_put(a, c);
+ c = bintoasc[(((radbuf[1]<<2)&074)|((radbuf[2]>>6)&03))&077];
+ iobuf_put(a, c);
+ c = bintoasc[radbuf[2]&077];
+ iobuf_put(a, c);
+ iobuf_writestr(a,afx->eol);
+ /* and the the trailer */
+ if( afx->what >= DIM(tail_strings) )
+ log_bug("afx->what=%d", afx->what);
+ iobuf_writestr(a, "-----");
+ iobuf_writestr(a, tail_strings[afx->what] );
+ iobuf_writestr(a, "-----" );
+ iobuf_writestr(a,afx->eol);
+ }
+ else if( !afx->any_data && !afx->inp_bypass ) {
+ log_error(_("no valid OpenPGP data found.\n"));
+ afx->no_openpgp_data = 1;
+ write_status_text( STATUS_NODATA, "1" );
+ }
+ if( afx->truncated )
+ log_info(_("invalid armor: line longer than %d characters\n"),
+ MAX_LINELEN );
+ /* issue an error to enforce dissemination of correct software */
+ if( afx->qp_detected )
+ log_error(_("quoted printable character in armor - "
+ "probably a buggy MTA has been used\n") );
+ xfree( afx->buffer );
+ afx->buffer = NULL;
+ release_armor_context (afx);
+ }
+ else if( control == IOBUFCTRL_DESC )
+ *(char**)buf = "armor_filter";
+ return rc;
+}
+
+
+/****************
+ * create a radix64 encoded string.
+ */
+char *
+make_radix64_string( const byte *data, size_t len )
+{
+ char *buffer, *p;
+
+ buffer = p = xmalloc( (len+2)/3*4 + 1 );
+ for( ; len >= 3 ; len -= 3, data += 3 ) {
+ *p++ = bintoasc[(data[0] >> 2) & 077];
+ *p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
+ *p++ = bintoasc[(((data[1]<<2)&074)|((data[2]>>6)&03))&077];
+ *p++ = bintoasc[data[2]&077];
+ }
+ if( len == 2 ) {
+ *p++ = bintoasc[(data[0] >> 2) & 077];
+ *p++ = bintoasc[(((data[0] <<4)&060)|((data[1] >> 4)&017))&077];
+ *p++ = bintoasc[((data[1]<<2)&074)];
+ }
+ else if( len == 1 ) {
+ *p++ = bintoasc[(data[0] >> 2) & 077];
+ *p++ = bintoasc[(data[0] <<4)&060];
+ }
+ *p = 0;
+ return buffer;
+}
+
+
+/***********************************************
+ * For the pipemode command we can't use the armor filter for various
+ * reasons, so we use this new unarmor_pump stuff to remove the armor
+ */
+
+enum unarmor_state_e {
+ STA_init = 0,
+ STA_bypass,
+ STA_wait_newline,
+ STA_wait_dash,
+ STA_first_dash,
+ STA_compare_header,
+ STA_found_header_wait_newline,
+ STA_skip_header_lines,
+ STA_skip_header_lines_non_ws,
+ STA_read_data,
+ STA_wait_crc,
+ STA_read_crc,
+ STA_ready
+};
+
+struct unarmor_pump_s {
+ enum unarmor_state_e state;
+ byte val;
+ int checkcrc;
+ int pos; /* counts from 0..3 */
+ u32 crc;
+ u32 mycrc; /* the one store in the data */
+};
+
+
+
+UnarmorPump
+unarmor_pump_new (void)
+{
+ UnarmorPump x;
+
+ if( !is_initialized )
+ initialize();
+ x = xmalloc_clear (sizeof *x);
+ return x;
+}
+
+void
+unarmor_pump_release (UnarmorPump x)
+{
+ xfree (x);
+}
+
+/*
+ * Get the next character from the ascii armor taken from the IOBUF
+ * created earlier by unarmor_pump_new().
+ * Return: c = Character
+ * 256 = ignore this value
+ * -1 = End of current armor
+ * -2 = Premature EOF (not used)
+ * -3 = Invalid armor
+ */
+int
+unarmor_pump (UnarmorPump x, int c)
+{
+ int rval = 256; /* default is to ignore the return value */
+
+ switch (x->state) {
+ case STA_init:
+ {
+ byte tmp[1];
+ tmp[0] = c;
+ if ( is_armored (tmp) )
+ x->state = c == '-'? STA_first_dash : STA_wait_newline;
+ else {
+ x->state = STA_bypass;
+ return c;
+ }
+ }
+ break;
+ case STA_bypass:
+ return c; /* return here to avoid crc calculation */
+ case STA_wait_newline:
+ if (c == '\n')
+ x->state = STA_wait_dash;
+ break;
+ case STA_wait_dash:
+ x->state = c == '-'? STA_first_dash : STA_wait_newline;
+ break;
+ case STA_first_dash: /* just need for initalization */
+ x->pos = 0;
+ x->state = STA_compare_header;
+ case STA_compare_header:
+ if ( "-----BEGIN PGP SIGNATURE-----"[++x->pos] == c ) {
+ if ( x->pos == 28 )
+ x->state = STA_found_header_wait_newline;
+ }
+ else
+ x->state = c == '\n'? STA_wait_dash : STA_wait_newline;
+ break;
+ case STA_found_header_wait_newline:
+ /* to make CR,LF issues easier we simply allow for white space
+ behind the 5 dashes */
+ if ( c == '\n' )
+ x->state = STA_skip_header_lines;
+ else if ( c != '\r' && c != ' ' && c != '\t' )
+ x->state = STA_wait_dash; /* garbage after the header line */
+ break;
+ case STA_skip_header_lines:
+ /* i.e. wait for one empty line */
+ if ( c == '\n' ) {
+ x->state = STA_read_data;
+ x->crc = CRCINIT;
+ x->val = 0;
+ x->pos = 0;
+ }
+ else if ( c != '\r' && c != ' ' && c != '\t' )
+ x->state = STA_skip_header_lines_non_ws;
+ break;
+ case STA_skip_header_lines_non_ws:
+ /* like above but we already encountered non white space */
+ if ( c == '\n' )
+ x->state = STA_skip_header_lines;
+ break;
+ case STA_read_data:
+ /* fixme: we don't check for the trailing dash lines but rely
+ * on the armor stop characters */
+ if( c == '\n' || c == ' ' || c == '\r' || c == '\t' )
+ break; /* skip all kind of white space */
+
+ if( c == '=' ) { /* pad character: stop */
+ if( x->pos == 1 ) /* in this case val has some value */
+ rval = x->val;
+ x->state = STA_wait_crc;
+ break;
+ }
+
+ {
+ int c2;
+ if( (c = asctobin[(c2=c)]) == 255 ) {
+ log_error(_("invalid radix64 character %02X skipped\n"), c2);
+ break;
+ }
+ }
+
+ switch(x->pos) {
+ case 0:
+ x->val = c << 2;
+ break;
+ case 1:
+ x->val |= (c>>4)&3;
+ rval = x->val;
+ x->val = (c<<4)&0xf0;
+ break;
+ case 2:
+ x->val |= (c>>2)&15;
+ rval = x->val;
+ x->val = (c<<6)&0xc0;
+ break;
+ case 3:
+ x->val |= c&0x3f;
+ rval = x->val;
+ break;
+ }
+ x->pos = (x->pos+1) % 4;
+ break;
+ case STA_wait_crc:
+ if( c == '\n' || c == ' ' || c == '\r' || c == '\t' || c == '=' )
+ break; /* skip ws and pad characters */
+ /* assume that we are at the next line */
+ x->state = STA_read_crc;
+ x->pos = 0;
+ x->mycrc = 0;
+ case STA_read_crc:
+ if( (c = asctobin[c]) == 255 ) {
+ rval = -1; /* ready */
+ if( x->crc != x->mycrc ) {
+ log_info (_("CRC error; %06lX - %06lX\n"),
+ (ulong)x->crc, (ulong)x->mycrc);
+ if ( invalid_crc() )
+ rval = -3;
+ }
+ x->state = STA_ready; /* not sure whether this is correct */
+ break;
+ }
+
+ switch(x->pos) {
+ case 0:
+ x->val = c << 2;
+ break;
+ case 1:
+ x->val |= (c>>4)&3;
+ x->mycrc |= x->val << 16;
+ x->val = (c<<4)&0xf0;
+ break;
+ case 2:
+ x->val |= (c>>2)&15;
+ x->mycrc |= x->val << 8;
+ x->val = (c<<6)&0xc0;
+ break;
+ case 3:
+ x->val |= c&0x3f;
+ x->mycrc |= x->val;
+ break;
+ }
+ x->pos = (x->pos+1) % 4;
+ break;
+ case STA_ready:
+ rval = -1;
+ break;
+ }
+
+ if ( !(rval & ~255) ) { /* compute the CRC */
+ x->crc = (x->crc << 8) ^ crc_table[((x->crc >> 16)&0xff) ^ rval];
+ x->crc &= 0x00ffffff;
+ }
+
+ return rval;
+}
diff --git a/g10/build-packet.c b/g10/build-packet.c
new file mode 100644
index 0000000..a7ac5d8
--- /dev/null
+++ b/g10/build-packet.c
@@ -0,0 +1,1308 @@
+/* build-packet.c - assemble packets and write them
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "packet.h"
+#include "errors.h"
+#include "iobuf.h"
+#include "mpi.h"
+#include "util.h"
+#include "cipher.h"
+#include "memory.h"
+#include "i18n.h"
+#include "options.h"
+
+static int do_user_id( IOBUF out, int ctb, PKT_user_id *uid );
+static int do_public_key( IOBUF out, int ctb, PKT_public_key *pk );
+static int do_secret_key( IOBUF out, int ctb, PKT_secret_key *pk );
+static int do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc );
+static int do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc );
+static u32 calc_plaintext( PKT_plaintext *pt );
+static int do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt );
+static int do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed );
+static int do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed );
+static int do_compressed( IOBUF out, int ctb, PKT_compressed *cd );
+static int do_signature( IOBUF out, int ctb, PKT_signature *sig );
+static int do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops );
+
+static int calc_header_length( u32 len, int new_ctb );
+static int write_16(IOBUF inp, u16 a);
+static int write_32(IOBUF inp, u32 a);
+static int write_header( IOBUF out, int ctb, u32 len );
+static int write_sign_packet_header( IOBUF out, int ctb, u32 len );
+static int write_header2( IOBUF out, int ctb, u32 len, int hdrlen );
+static int write_new_header( IOBUF out, int ctb, u32 len, int hdrlen );
+static int write_version( IOBUF out, int ctb );
+
+/****************
+ * Build a packet and write it to INP
+ * Returns: 0 := okay
+ * >0 := error
+ * Note: Caller must free the packet
+ */
+int
+build_packet( IOBUF out, PACKET *pkt )
+{
+ int new_ctb=0, rc=0, ctb;
+ int pkttype;
+
+ if( DBG_PACKET )
+ log_debug("build_packet() type=%d\n", pkt->pkttype );
+ assert( pkt->pkt.generic );
+
+ switch( (pkttype = pkt->pkttype) )
+ {
+ case PKT_PLAINTEXT: new_ctb = pkt->pkt.plaintext->new_ctb; break;
+ case PKT_ENCRYPTED:
+ case PKT_ENCRYPTED_MDC: new_ctb = pkt->pkt.encrypted->new_ctb; break;
+ case PKT_COMPRESSED:new_ctb = pkt->pkt.compressed->new_ctb; break;
+ case PKT_USER_ID:
+ if( pkt->pkt.user_id->attrib_data )
+ pkttype = PKT_ATTRIBUTE;
+ break;
+ default: break;
+ }
+
+ if( new_ctb || pkttype > 15 ) /* new format */
+ ctb = 0xc0 | (pkttype & 0x3f);
+ else
+ ctb = 0x80 | ((pkttype & 15)<<2);
+ switch( pkttype )
+ {
+ case PKT_ATTRIBUTE:
+ case PKT_USER_ID:
+ rc = do_user_id( out, ctb, pkt->pkt.user_id );
+ break;
+ case PKT_OLD_COMMENT:
+ case PKT_COMMENT:
+ /*
+ Ignore these. Theoretically, this will never be called as
+ we have no way to output comment packets any longer, but
+ just in case there is some code path that would end up
+ outputting a comment that was written before comments were
+ dropped (in the public key?) this is a no-op.
+ */
+ break;
+ case PKT_PUBLIC_SUBKEY:
+ case PKT_PUBLIC_KEY:
+ rc = do_public_key( out, ctb, pkt->pkt.public_key );
+ break;
+ case PKT_SECRET_SUBKEY:
+ case PKT_SECRET_KEY:
+ rc = do_secret_key( out, ctb, pkt->pkt.secret_key );
+ break;
+ case PKT_SYMKEY_ENC:
+ rc = do_symkey_enc( out, ctb, pkt->pkt.symkey_enc );
+ break;
+ case PKT_PUBKEY_ENC:
+ rc = do_pubkey_enc( out, ctb, pkt->pkt.pubkey_enc );
+ break;
+ case PKT_PLAINTEXT:
+ rc = do_plaintext( out, ctb, pkt->pkt.plaintext );
+ break;
+ case PKT_ENCRYPTED:
+ rc = do_encrypted( out, ctb, pkt->pkt.encrypted );
+ break;
+ case PKT_ENCRYPTED_MDC:
+ rc = do_encrypted_mdc( out, ctb, pkt->pkt.encrypted );
+ break;
+ case PKT_COMPRESSED:
+ rc = do_compressed( out, ctb, pkt->pkt.compressed );
+ break;
+ case PKT_SIGNATURE:
+ rc = do_signature( out, ctb, pkt->pkt.signature );
+ break;
+ case PKT_ONEPASS_SIG:
+ rc = do_onepass_sig( out, ctb, pkt->pkt.onepass_sig );
+ break;
+ case PKT_RING_TRUST:
+ break; /* ignore it (keyring.c does write it directly)*/
+ case PKT_MDC: /* we write it directly, so we should never see it here. */
+ default:
+ log_bug("invalid packet type in build_packet()\n");
+ break;
+ }
+
+ return rc;
+}
+
+/****************
+ * calculate the length of a packet described by PKT
+ */
+u32
+calc_packet_length( PACKET *pkt )
+{
+ u32 n=0;
+ int new_ctb = 0;
+
+ assert( pkt->pkt.generic );
+ switch( pkt->pkttype ) {
+ case PKT_PLAINTEXT:
+ n = calc_plaintext( pkt->pkt.plaintext );
+ new_ctb = pkt->pkt.plaintext->new_ctb;
+ break;
+ case PKT_ATTRIBUTE:
+ case PKT_USER_ID:
+ case PKT_COMMENT:
+ case PKT_PUBLIC_KEY:
+ case PKT_SECRET_KEY:
+ case PKT_SYMKEY_ENC:
+ case PKT_PUBKEY_ENC:
+ case PKT_ENCRYPTED:
+ case PKT_SIGNATURE:
+ case PKT_ONEPASS_SIG:
+ case PKT_RING_TRUST:
+ case PKT_COMPRESSED:
+ default:
+ log_bug("invalid packet type in calc_packet_length()");
+ break;
+ }
+
+ n += calc_header_length(n, new_ctb);
+ return n;
+}
+
+static void
+write_fake_data( IOBUF out, MPI a )
+{
+ if( a ) {
+ unsigned int i;
+ void *p;
+
+ p = mpi_get_opaque( a, &i );
+ iobuf_write( out, p, i );
+ }
+}
+
+static int
+do_user_id( IOBUF out, int ctb, PKT_user_id *uid )
+{
+ if( uid->attrib_data )
+ {
+ write_header(out, ctb, uid->attrib_len);
+ if( iobuf_write( out, uid->attrib_data, uid->attrib_len ) )
+ return G10ERR_WRITE_FILE;
+ }
+ else
+ {
+ write_header2( out, ctb, uid->len, 2 );
+ if( iobuf_write( out, uid->name, uid->len ) )
+ return G10ERR_WRITE_FILE;
+ }
+ return 0;
+}
+
+static int
+do_public_key( IOBUF out, int ctb, PKT_public_key *pk )
+{
+ int rc = 0;
+ int n, i;
+ IOBUF a = iobuf_temp();
+
+ if( !pk->version )
+ iobuf_put( a, 3 );
+ else
+ iobuf_put( a, pk->version );
+ write_32(a, pk->timestamp );
+ if( pk->version < 4 ) {
+ u16 ndays;
+ if( pk->expiredate )
+ ndays = (u16)((pk->expiredate - pk->timestamp) / 86400L);
+ else
+ ndays = 0;
+ write_16(a, ndays );
+ }
+ iobuf_put(a, pk->pubkey_algo );
+ n = pubkey_get_npkey( pk->pubkey_algo );
+ if( !n )
+ write_fake_data( a, pk->pkey[0] );
+ for(i=0; i < n; i++ )
+ mpi_write(a, pk->pkey[i] );
+
+ write_header2(out, ctb, iobuf_get_temp_length(a), pk->hdrbytes);
+ if( iobuf_write_temp( out, a ) )
+ rc = G10ERR_WRITE_FILE;
+
+ iobuf_close(a);
+ return rc;
+}
+
+
+static int
+do_secret_key( IOBUF out, int ctb, PKT_secret_key *sk )
+{
+ int rc = 0;
+ int i, nskey, npkey;
+ IOBUF a = iobuf_temp(); /* build in a self-enlarging buffer */
+
+ /* Write the version number - if none is specified, use 3 */
+ if( !sk->version )
+ iobuf_put( a, 3 );
+ else
+ iobuf_put( a, sk->version );
+ write_32(a, sk->timestamp );
+
+ /* v3 needs the expiration time */
+ if( sk->version < 4 ) {
+ u16 ndays;
+ if( sk->expiredate )
+ ndays = (u16)((sk->expiredate - sk->timestamp) / 86400L);
+ else
+ ndays = 0;
+ write_16(a, ndays);
+ }
+
+ iobuf_put(a, sk->pubkey_algo );
+
+ /* get number of secret and public parameters. They are held in
+ one array first the public ones, then the secret ones */
+ nskey = pubkey_get_nskey( sk->pubkey_algo );
+ npkey = pubkey_get_npkey( sk->pubkey_algo );
+
+ /* If we don't have any public parameters - which is the case if
+ we don't know the algorithm used - the parameters are stored as
+ one blob in a faked (opaque) MPI */
+ if( !npkey ) {
+ write_fake_data( a, sk->skey[0] );
+ goto leave;
+ }
+ assert( npkey < nskey );
+
+ /* Writing the public parameters is easy */
+ for(i=0; i < npkey; i++ )
+ mpi_write(a, sk->skey[i] );
+
+ /* build the header for protected (encrypted) secret parameters */
+ if( sk->is_protected ) {
+ if( is_RSA(sk->pubkey_algo) && sk->version < 4
+ && !sk->protect.s2k.mode ) {
+ /* the simple rfc1991 (v3) way */
+ iobuf_put(a, sk->protect.algo );
+ iobuf_write(a, sk->protect.iv, sk->protect.ivlen );
+ }
+ else {
+ /* OpenPGP protection according to rfc2440 */
+ iobuf_put(a, sk->protect.sha1chk? 0xfe : 0xff );
+ iobuf_put(a, sk->protect.algo );
+ if( sk->protect.s2k.mode >= 1000 ) {
+ /* These modes are not possible in OpenPGP, we use them
+ to implement our extensions, 101 can be seen as a
+ private/experimental extension (this is not
+ specified in rfc2440 but the same scheme is used
+ for all other algorithm identifiers) */
+ iobuf_put(a, 101 );
+ iobuf_put(a, sk->protect.s2k.hash_algo );
+ iobuf_write(a, "GNU", 3 );
+ iobuf_put(a, sk->protect.s2k.mode - 1000 );
+ }
+ else {
+ iobuf_put(a, sk->protect.s2k.mode );
+ iobuf_put(a, sk->protect.s2k.hash_algo );
+ }
+ if( sk->protect.s2k.mode == 1
+ || sk->protect.s2k.mode == 3 )
+ iobuf_write(a, sk->protect.s2k.salt, 8 );
+ if( sk->protect.s2k.mode == 3 )
+ iobuf_put(a, sk->protect.s2k.count );
+
+ /* For out special modes 1001, 1002 we do not need an IV */
+ if( sk->protect.s2k.mode != 1001
+ && sk->protect.s2k.mode != 1002 )
+ iobuf_write(a, sk->protect.iv, sk->protect.ivlen );
+ }
+ }
+ else
+ iobuf_put(a, 0 );
+
+ if( sk->protect.s2k.mode == 1001 )
+ ; /* GnuPG extension - don't write a secret key at all */
+ else if( sk->protect.s2k.mode == 1002 )
+ { /* GnuPG extension - divert to OpenPGP smartcard. */
+ iobuf_put(a, sk->protect.ivlen ); /* length of the serial
+ number or 0 for no serial
+ number. */
+ /* The serial number gets stored in the IV field. */
+ iobuf_write(a, sk->protect.iv, sk->protect.ivlen);
+ }
+ else if( sk->is_protected && sk->version >= 4 ) {
+ /* The secret key is protected - write it out as it is */
+ byte *p;
+ unsigned int ndata;
+
+ assert( mpi_is_opaque( sk->skey[npkey] ) );
+ p = mpi_get_opaque( sk->skey[npkey], &ndata );
+ iobuf_write(a, p, ndata );
+ }
+ else if( sk->is_protected ) {
+ /* The secret key is protected te old v4 way. */
+ for( ; i < nskey; i++ ) {
+ byte *p;
+ unsigned int ndata;
+
+ assert (mpi_is_opaque (sk->skey[i]));
+ p = mpi_get_opaque (sk->skey[i], &ndata);
+ iobuf_write (a, p, ndata);
+ }
+ write_16(a, sk->csum );
+ }
+ else {
+ /* non-protected key */
+ for( ; i < nskey; i++ )
+ mpi_write(a, sk->skey[i] );
+ write_16(a, sk->csum );
+ }
+
+ leave:
+ /* Build the header of the packet - which we must do after writing all
+ the other stuff, so that we know the length of the packet */
+ write_header2(out, ctb, iobuf_get_temp_length(a), sk->hdrbytes);
+ /* And finally write it out the real stream */
+ if( iobuf_write_temp( out, a ) )
+ rc = G10ERR_WRITE_FILE;
+
+ iobuf_close(a); /* close the remporary buffer */
+ return rc;
+}
+
+static int
+do_symkey_enc( IOBUF out, int ctb, PKT_symkey_enc *enc )
+{
+ int rc = 0;
+ IOBUF a = iobuf_temp();
+
+ assert( enc->version == 4 );
+ switch( enc->s2k.mode ) {
+ case 0: case 1: case 3: break;
+ default: log_bug("do_symkey_enc: s2k=%d\n", enc->s2k.mode );
+ }
+ iobuf_put( a, enc->version );
+ iobuf_put( a, enc->cipher_algo );
+ iobuf_put( a, enc->s2k.mode );
+ iobuf_put( a, enc->s2k.hash_algo );
+ if( enc->s2k.mode == 1 || enc->s2k.mode == 3 ) {
+ iobuf_write(a, enc->s2k.salt, 8 );
+ if( enc->s2k.mode == 3 )
+ iobuf_put(a, enc->s2k.count);
+ }
+ if( enc->seskeylen )
+ iobuf_write(a, enc->seskey, enc->seskeylen );
+
+ write_header(out, ctb, iobuf_get_temp_length(a) );
+ if( iobuf_write_temp( out, a ) )
+ rc = G10ERR_WRITE_FILE;
+
+ iobuf_close(a);
+ return rc;
+}
+
+
+static int
+do_pubkey_enc( IOBUF out, int ctb, PKT_pubkey_enc *enc )
+{
+ int rc = 0;
+ int n, i;
+ IOBUF a = iobuf_temp();
+
+ write_version( a, ctb );
+ if( enc->throw_keyid ) {
+ write_32(a, 0 ); /* don't tell Eve who can decrypt the message */
+ write_32(a, 0 );
+ }
+ else {
+ write_32(a, enc->keyid[0] );
+ write_32(a, enc->keyid[1] );
+ }
+ iobuf_put(a,enc->pubkey_algo );
+ n = pubkey_get_nenc( enc->pubkey_algo );
+ if( !n )
+ write_fake_data( a, enc->data[0] );
+ for(i=0; i < n; i++ )
+ mpi_write(a, enc->data[i] );
+
+ write_header(out, ctb, iobuf_get_temp_length(a) );
+ if( iobuf_write_temp( out, a ) )
+ rc = G10ERR_WRITE_FILE;
+
+ iobuf_close(a);
+ return rc;
+}
+
+
+static u32
+calc_plaintext( PKT_plaintext *pt )
+{
+ /* Truncate namelen to the maximum 255 characters. Note this means
+ that a function that calls build_packet with an illegal literal
+ packet will get it back legalized. */
+
+ if(pt->namelen>255)
+ pt->namelen=255;
+
+ return pt->len? (1 + 1 + pt->namelen + 4 + pt->len) : 0;
+}
+
+static int
+do_plaintext( IOBUF out, int ctb, PKT_plaintext *pt )
+{
+ int i, rc = 0;
+ u32 n;
+ byte buf[1000]; /* this buffer has the plaintext! */
+ int nbytes;
+
+ write_header(out, ctb, calc_plaintext( pt ) );
+ iobuf_put(out, pt->mode );
+ iobuf_put(out, pt->namelen );
+ for(i=0; i < pt->namelen; i++ )
+ iobuf_put(out, pt->name[i] );
+ if( write_32(out, pt->timestamp ) )
+ rc = G10ERR_WRITE_FILE;
+
+ n = 0;
+ while( (nbytes=iobuf_read(pt->buf, buf, 1000)) != -1 ) {
+ if( iobuf_write(out, buf, nbytes) == -1 ) {
+ rc = G10ERR_WRITE_FILE;
+ break;
+ }
+ n += nbytes;
+ }
+ wipememory(buf,1000); /* burn the buffer */
+ if( (ctb&0x40) && !pt->len )
+ iobuf_set_partial_block_mode(out, 0 ); /* turn off partial */
+ if( pt->len && n != pt->len )
+ log_error("do_plaintext(): wrote %lu bytes but expected %lu bytes\n",
+ (ulong)n, (ulong)pt->len );
+
+ return rc;
+}
+
+
+
+static int
+do_encrypted( IOBUF out, int ctb, PKT_encrypted *ed )
+{
+ int rc = 0;
+ u32 n;
+
+ n = ed->len ? (ed->len + ed->extralen) : 0;
+ write_header(out, ctb, n );
+
+ /* This is all. The caller has to write the real data */
+
+ return rc;
+}
+
+static int
+do_encrypted_mdc( IOBUF out, int ctb, PKT_encrypted *ed )
+{
+ int rc = 0;
+ u32 n;
+
+ assert( ed->mdc_method );
+
+ /* Take version number and the following MDC packet in account. */
+ n = ed->len ? (ed->len + ed->extralen + 1 + 22) : 0;
+ write_header(out, ctb, n );
+ iobuf_put(out, 1 ); /* version */
+
+ /* This is all. The caller has to write the real data */
+
+ return rc;
+}
+
+
+static int
+do_compressed( IOBUF out, int ctb, PKT_compressed *cd )
+{
+ int rc = 0;
+
+ /* We must use the old convention and don't use blockmode for tyhe
+ sake of PGP 2 compatibility. However if the new_ctb flag was
+ set, CTB is already formatted as new style and write_header2
+ does create a partial length encoding using new the new
+ style. */
+ write_header2(out, ctb, 0, 0);
+ iobuf_put(out, cd->algorithm );
+
+ /* This is all. The caller has to write the real data */
+
+ return rc;
+}
+
+
+/****************
+ * Delete all subpackets of type REQTYPE and return a bool whether a packet
+ * was deleted.
+ */
+int
+delete_sig_subpkt (subpktarea_t *area, sigsubpkttype_t reqtype )
+{
+ int buflen;
+ sigsubpkttype_t type;
+ byte *buffer, *bufstart;
+ size_t n;
+ size_t unused = 0;
+ int okay = 0;
+
+ if( !area )
+ return 0;
+ buflen = area->len;
+ buffer = area->data;
+ for(;;) {
+ if( !buflen ) {
+ okay = 1;
+ break;
+ }
+ bufstart = buffer;
+ n = *buffer++; buflen--;
+ if( n == 255 ) {
+ if( buflen < 4 )
+ break;
+ n = (buffer[0] << 24) | (buffer[1] << 16)
+ | (buffer[2] << 8) | buffer[3];
+ buffer += 4;
+ buflen -= 4;
+ }
+ else if( n >= 192 ) {
+ if( buflen < 2 )
+ break;
+ n = (( n - 192 ) << 8) + *buffer + 192;
+ buffer++;
+ buflen--;
+ }
+ if( buflen < n )
+ break;
+
+ type = *buffer & 0x7f;
+ if( type == reqtype ) {
+ buffer++;
+ buflen--;
+ n--;
+ if( n > buflen )
+ break;
+ buffer += n; /* point to next subpkt */
+ buflen -= n;
+ memmove (bufstart, buffer, buflen); /* shift */
+ unused += buffer - bufstart;
+ buffer = bufstart;
+ }
+ else {
+ buffer += n; buflen -=n;
+ }
+ }
+
+ if (!okay)
+ log_error ("delete_subpkt: buffer shorter than subpacket\n");
+ assert (unused <= area->len);
+ area->len -= unused;
+ return !!unused;
+}
+
+
+/****************
+ * Create or update a signature subpacket for SIG of TYPE. This
+ * functions knows where to put the data (hashed or unhashed). The
+ * function may move data from the unhashed part to the hashed one.
+ * Note: All pointers into sig->[un]hashed (e.g. returned by
+ * parse_sig_subpkt) are not valid after a call to this function. The
+ * data to put into the subpaket should be in a buffer with a length
+ * of buflen.
+ */
+void
+build_sig_subpkt (PKT_signature *sig, sigsubpkttype_t type,
+ const byte *buffer, size_t buflen )
+{
+ byte *p;
+ int critical, hashed;
+ subpktarea_t *oldarea, *newarea;
+ size_t nlen, n, n0;
+
+ critical = (type & SIGSUBPKT_FLAG_CRITICAL);
+ type &= ~SIGSUBPKT_FLAG_CRITICAL;
+
+ /* Sanity check buffer sizes */
+ if(parse_one_sig_subpkt(buffer,buflen,type)<0)
+ BUG();
+
+ switch(type)
+ {
+ case SIGSUBPKT_NOTATION:
+ case SIGSUBPKT_POLICY:
+ case SIGSUBPKT_REV_KEY:
+ case SIGSUBPKT_SIGNATURE:
+ /* we do allow multiple subpackets */
+ break;
+
+ default:
+ /* we don't allow multiple subpackets */
+ delete_sig_subpkt(sig->hashed,type);
+ delete_sig_subpkt(sig->unhashed,type);
+ break;
+ }
+
+ /* Any special magic that needs to be done for this type so the
+ packet doesn't need to be reparsed? */
+ switch(type)
+ {
+ case SIGSUBPKT_NOTATION:
+ sig->flags.notation=1;
+ break;
+
+ case SIGSUBPKT_POLICY:
+ sig->flags.policy_url=1;
+ break;
+
+ case SIGSUBPKT_PREF_KS:
+ sig->flags.pref_ks=1;
+ break;
+
+ case SIGSUBPKT_EXPORTABLE:
+ if(buffer[0])
+ sig->flags.exportable=1;
+ else
+ sig->flags.exportable=0;
+ break;
+
+ case SIGSUBPKT_REVOCABLE:
+ if(buffer[0])
+ sig->flags.revocable=1;
+ else
+ sig->flags.revocable=0;
+ break;
+
+ case SIGSUBPKT_TRUST:
+ sig->trust_depth=buffer[0];
+ sig->trust_value=buffer[1];
+ break;
+
+ case SIGSUBPKT_REGEXP:
+ sig->trust_regexp=buffer;
+ break;
+
+ /* This should never happen since we don't currently allow
+ creating such a subpacket, but just in case... */
+ case SIGSUBPKT_SIG_EXPIRE:
+ if(buffer_to_u32(buffer)+sig->timestamp<=make_timestamp())
+ sig->flags.expired=1;
+ else
+ sig->flags.expired=0;
+ break;
+
+ default:
+ break;
+ }
+
+ if( (buflen+1) >= 8384 )
+ nlen = 5; /* write 5 byte length header */
+ else if( (buflen+1) >= 192 )
+ nlen = 2; /* write 2 byte length header */
+ else
+ nlen = 1; /* just a 1 byte length header */
+
+ switch( type )
+ {
+ /* The issuer being unhashed is a historical oddity. It
+ should work equally as well hashed. Of course, if even an
+ unhashed issuer is tampered with, it makes it awfully hard
+ to verify the sig... */
+ case SIGSUBPKT_ISSUER:
+ case SIGSUBPKT_SIGNATURE:
+ hashed = 0;
+ break;
+ default:
+ hashed = 1;
+ break;
+ }
+
+ if( critical )
+ type |= SIGSUBPKT_FLAG_CRITICAL;
+
+ oldarea = hashed? sig->hashed : sig->unhashed;
+
+ /* Calculate new size of the area and allocate */
+ n0 = oldarea? oldarea->len : 0;
+ n = n0 + nlen + 1 + buflen; /* length, type, buffer */
+ if (oldarea && n <= oldarea->size) { /* fits into the unused space */
+ newarea = oldarea;
+ /*log_debug ("updating area for type %d\n", type );*/
+ }
+ else if (oldarea) {
+ newarea = xrealloc (oldarea, sizeof (*newarea) + n - 1);
+ newarea->size = n;
+ /*log_debug ("reallocating area for type %d\n", type );*/
+ }
+ else {
+ newarea = xmalloc (sizeof (*newarea) + n - 1);
+ newarea->size = n;
+ /*log_debug ("allocating area for type %d\n", type );*/
+ }
+ newarea->len = n;
+
+ p = newarea->data + n0;
+ if (nlen == 5) {
+ *p++ = 255;
+ *p++ = (buflen+1) >> 24;
+ *p++ = (buflen+1) >> 16;
+ *p++ = (buflen+1) >> 8;
+ *p++ = (buflen+1);
+ *p++ = type;
+ memcpy (p, buffer, buflen);
+ }
+ else if (nlen == 2) {
+ *p++ = (buflen+1-192) / 256 + 192;
+ *p++ = (buflen+1-192) % 256;
+ *p++ = type;
+ memcpy (p, buffer, buflen);
+ }
+ else {
+ *p++ = buflen+1;
+ *p++ = type;
+ memcpy (p, buffer, buflen);
+ }
+
+ if (hashed)
+ sig->hashed = newarea;
+ else
+ sig->unhashed = newarea;
+}
+
+/****************
+ * Put all the required stuff from SIG into subpackets of sig.
+ * Hmmm, should we delete those subpackets which are in a wrong area?
+ */
+void
+build_sig_subpkt_from_sig( PKT_signature *sig )
+{
+ u32 u;
+ byte buf[8];
+
+ u = sig->keyid[0];
+ buf[0] = (u >> 24) & 0xff;
+ buf[1] = (u >> 16) & 0xff;
+ buf[2] = (u >> 8) & 0xff;
+ buf[3] = u & 0xff;
+ u = sig->keyid[1];
+ buf[4] = (u >> 24) & 0xff;
+ buf[5] = (u >> 16) & 0xff;
+ buf[6] = (u >> 8) & 0xff;
+ buf[7] = u & 0xff;
+ build_sig_subpkt( sig, SIGSUBPKT_ISSUER, buf, 8 );
+
+ u = sig->timestamp;
+ buf[0] = (u >> 24) & 0xff;
+ buf[1] = (u >> 16) & 0xff;
+ buf[2] = (u >> 8) & 0xff;
+ buf[3] = u & 0xff;
+ build_sig_subpkt( sig, SIGSUBPKT_SIG_CREATED, buf, 4 );
+
+ if(sig->expiredate)
+ {
+ if(sig->expiredate>sig->timestamp)
+ u=sig->expiredate-sig->timestamp;
+ else
+ u=1; /* A 1-second expiration time is the shortest one
+ OpenPGP has */
+
+ buf[0] = (u >> 24) & 0xff;
+ buf[1] = (u >> 16) & 0xff;
+ buf[2] = (u >> 8) & 0xff;
+ buf[3] = u & 0xff;
+
+ /* Mark this CRITICAL, so if any implementation doesn't
+ understand sigs that can expire, it'll just disregard this
+ sig altogether. */
+
+ build_sig_subpkt( sig, SIGSUBPKT_SIG_EXPIRE | SIGSUBPKT_FLAG_CRITICAL,
+ buf, 4 );
+ }
+}
+
+void
+build_attribute_subpkt(PKT_user_id *uid,byte type,
+ const void *buf,u32 buflen,
+ const void *header,u32 headerlen)
+{
+ byte *attrib;
+ int idx;
+
+ if(1+headerlen+buflen>8383)
+ idx=5;
+ else if(1+headerlen+buflen>191)
+ idx=2;
+ else
+ idx=1;
+
+ /* realloc uid->attrib_data to the right size */
+
+ uid->attrib_data=xrealloc(uid->attrib_data,
+ uid->attrib_len+idx+1+headerlen+buflen);
+
+ attrib=&uid->attrib_data[uid->attrib_len];
+
+ if(idx==5)
+ {
+ attrib[0]=255;
+ attrib[1]=(1+headerlen+buflen) >> 24;
+ attrib[2]=(1+headerlen+buflen) >> 16;
+ attrib[3]=(1+headerlen+buflen) >> 8;
+ attrib[4]=1+headerlen+buflen;
+ }
+ else if(idx==2)
+ {
+ attrib[0]=(1+headerlen+buflen-192) / 256 + 192;
+ attrib[1]=(1+headerlen+buflen-192) % 256;
+ }
+ else
+ attrib[0]=1+headerlen+buflen; /* Good luck finding a JPEG this small! */
+
+ attrib[idx++]=type;
+
+ /* Tack on our data at the end */
+
+ if(headerlen>0)
+ memcpy(&attrib[idx],header,headerlen);
+ memcpy(&attrib[idx+headerlen],buf,buflen);
+ uid->attrib_len+=idx+headerlen+buflen;
+}
+
+struct notation *
+string_to_notation(const char *string,int is_utf8)
+{
+ const char *s;
+ int saw_at=0;
+ struct notation *notation;
+
+ notation=xmalloc_clear(sizeof(*notation));
+
+ if(*string=='-')
+ {
+ notation->flags.ignore=1;
+ string++;
+ }
+
+ if(*string=='!')
+ {
+ notation->flags.critical=1;
+ string++;
+ }
+
+ /* If and when the IETF assigns some official name tags, we'll have
+ to add them here. */
+
+ for( s=string ; *s != '='; s++ )
+ {
+ if( *s=='@')
+ saw_at++;
+
+ /* -notationname is legal without an = sign */
+ if(!*s && notation->flags.ignore)
+ break;
+
+ if( !*s || !isascii (*s) || (!isgraph(*s) && !isspace(*s)) )
+ {
+ log_error(_("a notation name must have only printable characters"
+ " or spaces, and end with an '='\n") );
+ goto fail;
+ }
+ }
+
+ notation->name=xmalloc((s-string)+1);
+ strncpy(notation->name,string,s-string);
+ notation->name[s-string]='\0';
+
+ 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;
+ }
+
+ if(*s)
+ {
+ const char *i=s+1;
+ int highbit=0;
+
+ /* we only support printable text - therefore we enforce the use
+ of only printable characters (an empty value is valid) */
+ for(s++; *s ; s++ )
+ {
+ if ( !isascii (*s) )
+ highbit=1;
+ else if (iscntrl(*s))
+ {
+ log_error(_("a notation value must not use any"
+ " control characters\n"));
+ goto fail;
+ }
+ }
+
+ if(!highbit || is_utf8)
+ notation->value=xstrdup(i);
+ else
+ notation->value=native_to_utf8(i);
+ }
+
+ return notation;
+
+ fail:
+ free_notation(notation);
+ return NULL;
+}
+
+struct notation *
+sig_to_notation(PKT_signature *sig)
+{
+ const byte *p;
+ size_t len;
+ int seq=0,crit;
+ struct notation *list=NULL;
+
+ while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION,&len,&seq,&crit)))
+ {
+ int n1,n2;
+ struct notation *n=NULL;
+
+ if(len<8)
+ {
+ log_info(_("WARNING: invalid notation data found\n"));
+ continue;
+ }
+
+ n1=(p[4]<<8)|p[5];
+ n2=(p[6]<<8)|p[7];
+
+ if(8+n1+n2!=len)
+ {
+ log_info(_("WARNING: invalid notation data found\n"));
+ continue;
+ }
+
+ n=xmalloc_clear(sizeof(*n));
+ n->name=xmalloc(n1+1);
+
+ memcpy(n->name,&p[8],n1);
+ n->name[n1]='\0';
+
+ if(p[0]&0x80)
+ {
+ n->value=xmalloc(n2+1);
+ memcpy(n->value,&p[8+n1],n2);
+ n->value[n2]='\0';
+ }
+ else
+ {
+ n->bdat=xmalloc(n2);
+ n->blen=n2;
+ memcpy(n->bdat,&p[8+n1],n2);
+
+ n->value=xmalloc(2+strlen(_("not human readable"))+2+1);
+ strcpy(n->value,"[ ");
+ strcat(n->value,_("not human readable"));
+ strcat(n->value," ]");
+ }
+
+ n->flags.critical=crit;
+
+ n->next=list;
+ list=n;
+ }
+
+ return list;
+}
+
+void
+free_notation(struct notation *notation)
+{
+ while(notation)
+ {
+ struct notation *n=notation;
+
+ xfree(n->name);
+ xfree(n->value);
+ xfree(n->altvalue);
+ xfree(n->bdat);
+ notation=n->next;
+ xfree(n);
+ }
+}
+
+static int
+do_signature( IOBUF out, int ctb, PKT_signature *sig )
+{
+ int rc = 0;
+ int n, i;
+ IOBUF a = iobuf_temp();
+
+ if( !sig->version )
+ iobuf_put( a, 3 );
+ else
+ iobuf_put( a, sig->version );
+ if( sig->version < 4 )
+ iobuf_put(a, 5 ); /* constant */
+ iobuf_put(a, sig->sig_class );
+ if( sig->version < 4 ) {
+ write_32(a, sig->timestamp );
+ write_32(a, sig->keyid[0] );
+ write_32(a, sig->keyid[1] );
+ }
+ iobuf_put(a, sig->pubkey_algo );
+ iobuf_put(a, sig->digest_algo );
+ if( sig->version >= 4 ) {
+ size_t nn;
+ /* timestamp and keyid must have been packed into the
+ * subpackets prior to the call of this function, because
+ * these subpackets are hashed */
+ nn = sig->hashed? sig->hashed->len : 0;
+ write_16(a, nn);
+ if( nn )
+ iobuf_write( a, sig->hashed->data, nn );
+ nn = sig->unhashed? sig->unhashed->len : 0;
+ write_16(a, nn);
+ if( nn )
+ iobuf_write( a, sig->unhashed->data, nn );
+ }
+ iobuf_put(a, sig->digest_start[0] );
+ iobuf_put(a, sig->digest_start[1] );
+ n = pubkey_get_nsig( sig->pubkey_algo );
+ if( !n )
+ write_fake_data( a, sig->data[0] );
+ for(i=0; i < n; i++ )
+ mpi_write(a, sig->data[i] );
+
+ if( is_RSA(sig->pubkey_algo) && sig->version < 4 )
+ write_sign_packet_header(out, ctb, iobuf_get_temp_length(a) );
+ else
+ write_header(out, ctb, iobuf_get_temp_length(a) );
+ if( iobuf_write_temp( out, a ) )
+ rc = G10ERR_WRITE_FILE;
+
+ iobuf_close(a);
+ return rc;
+}
+
+
+static int
+do_onepass_sig( IOBUF out, int ctb, PKT_onepass_sig *ops )
+{
+ int rc = 0;
+ IOBUF a = iobuf_temp();
+
+ write_version( a, ctb );
+ iobuf_put(a, ops->sig_class );
+ iobuf_put(a, ops->digest_algo );
+ iobuf_put(a, ops->pubkey_algo );
+ write_32(a, ops->keyid[0] );
+ write_32(a, ops->keyid[1] );
+ iobuf_put(a, ops->last );
+
+ write_header(out, ctb, iobuf_get_temp_length(a) );
+ if( iobuf_write_temp( out, a ) )
+ rc = G10ERR_WRITE_FILE;
+
+ iobuf_close(a);
+ return rc;
+}
+
+
+static int
+write_16(IOBUF out, u16 a)
+{
+ iobuf_put(out, a>>8);
+ if( iobuf_put(out,a) )
+ return -1;
+ return 0;
+}
+
+static int
+write_32(IOBUF out, u32 a)
+{
+ iobuf_put(out, a>> 24);
+ iobuf_put(out, a>> 16);
+ iobuf_put(out, a>> 8);
+ if( iobuf_put(out, a) )
+ return -1;
+ return 0;
+}
+
+
+/****************
+ * calculate the length of a header
+ */
+static int
+calc_header_length( u32 len, int new_ctb )
+{
+ if( !len )
+ return 1; /* only the ctb */
+
+ if( new_ctb ) {
+ if( len < 192 )
+ return 2;
+ if( len < 8384 )
+ return 3;
+ else
+ return 6;
+ }
+ if( len < 256 )
+ return 2;
+ if( len < 65536 )
+ return 3;
+
+ return 5;
+}
+
+/****************
+ * Write the CTB and the packet length
+ */
+static int
+write_header( IOBUF out, int ctb, u32 len )
+{
+ return write_header2( out, ctb, len, 0 );
+}
+
+
+static int
+write_sign_packet_header( IOBUF out, int ctb, u32 len )
+{
+ /* work around a bug in the pgp read function for signature packets,
+ * which are not correctly coded and silently assume at some
+ * point 2 byte length headers.*/
+ iobuf_put(out, 0x89 );
+ iobuf_put(out, len >> 8 );
+ return iobuf_put(out, len ) == -1 ? -1:0;
+}
+
+/****************
+ * If HDRLEN is > 0, try to build a header of this length. We need
+ * this so that we can hash packets without reading them again. If
+ * len is 0, write a partial or indeterminate length header, unless
+ * hdrlen is specified in which case write an actual zero length
+ * (using the specified hdrlen).
+ */
+static int
+write_header2( IOBUF out, int ctb, u32 len, int hdrlen )
+{
+ if( ctb & 0x40 )
+ return write_new_header( out, ctb, len, hdrlen );
+
+ if( hdrlen )
+ {
+ if( hdrlen == 2 && len < 256 )
+ ;
+ else if( hdrlen == 3 && len < 65536 )
+ ctb |= 1;
+ else
+ ctb |= 2;
+ }
+ else
+ {
+ if( !len )
+ ctb |= 3;
+ else if( len < 256 )
+ ;
+ else if( len < 65536 )
+ ctb |= 1;
+ else
+ ctb |= 2;
+ }
+
+ if( iobuf_put(out, ctb ) )
+ return -1;
+
+ if( len || hdrlen )
+ {
+ if( ctb & 2 )
+ {
+ if(iobuf_put(out, len >> 24 ))
+ return -1;
+ if(iobuf_put(out, len >> 16 ))
+ return -1;
+ }
+
+ if( ctb & 3 )
+ if(iobuf_put(out, len >> 8 ))
+ return -1;
+
+ if( iobuf_put(out, len ) )
+ return -1;
+ }
+
+ return 0;
+}
+
+
+static int
+write_new_header( IOBUF out, int ctb, u32 len, int hdrlen )
+{
+ if( hdrlen )
+ log_bug("can't cope with hdrlen yet\n");
+
+ if( iobuf_put(out, ctb ) )
+ return -1;
+ if( !len ) {
+ iobuf_set_partial_block_mode(out, 512 );
+ }
+ else {
+ if( len < 192 ) {
+ if( iobuf_put(out, len ) )
+ return -1;
+ }
+ else if( len < 8384 ) {
+ len -= 192;
+ if( iobuf_put( out, (len / 256) + 192) )
+ return -1;
+ if( iobuf_put( out, (len % 256) ) )
+ return -1;
+ }
+ else {
+ if( iobuf_put( out, 0xff ) )
+ return -1;
+ if( iobuf_put( out, (len >> 24)&0xff ) )
+ return -1;
+ if( iobuf_put( out, (len >> 16)&0xff ) )
+ return -1;
+ if( iobuf_put( out, (len >> 8)&0xff ) )
+ return -1;
+ if( iobuf_put( out, len & 0xff ) )
+ return -1;
+ }
+ }
+ return 0;
+}
+
+static int
+write_version( IOBUF out, int ctb )
+{
+ if( iobuf_put( out, 3 ) )
+ return -1;
+ return 0;
+}
diff --git a/g10/card-util.c b/g10/card-util.c
new file mode 100644
index 0000000..0c83654
--- /dev/null
+++ b/g10/card-util.c
@@ -0,0 +1,1610 @@
+/* card-util.c - Utility functions for the OpenPGP card.
+ * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#if GNUPG_MAJOR_VERSION != 1
+#include "gpg.h"
+#endif /*GNUPG_MAJOR_VERSION != 1*/
+#include "util.h"
+#include "i18n.h"
+#include "ttyio.h"
+#include "status.h"
+#include "options.h"
+#include "main.h"
+#include "keyserver-internal.h"
+#if GNUPG_MAJOR_VERSION == 1
+#ifdef HAVE_LIBREADLINE
+#include <stdio.h>
+#include <readline/readline.h>
+#endif /*HAVE_LIBREADLINE*/
+#include "cardglue.h"
+#else /*GNUPG_MAJOR_VERSION!=1*/
+#include "call-agent.h"
+#endif /*GNUPG_MAJOR_VERSION!=1*/
+
+#define CONTROL_D ('D' - 'A' + 1)
+
+
+/* Change the PIN of a an OpenPGP card. This is an interactive
+ function. */
+void
+change_pin (int chvno, int allow_admin)
+{
+ struct agent_card_info_s info;
+ int rc;
+
+ rc = agent_learn (&info);
+ if (rc)
+ {
+ log_error (_("OpenPGP card not available: %s\n"),
+ gpg_strerror (rc));
+ return;
+ }
+
+ log_info (_("OpenPGP card no. %s detected\n"),
+ info.serialno? info.serialno : "[none]");
+
+ agent_clear_pin_cache (info.serialno);
+
+ if (opt.batch)
+ {
+ agent_release_card_info (&info);
+ log_error (_("can't do this in batch mode\n"));
+ return;
+ }
+
+ if(!allow_admin)
+ {
+ rc = agent_scd_change_pin (1, info.serialno);
+ if (rc)
+ tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
+ else
+ {
+ write_status (STATUS_SC_OP_SUCCESS);
+ tty_printf ("PIN changed.\n");
+ }
+ }
+ else
+ for (;;)
+ {
+ char *answer;
+
+ tty_printf ("\n");
+ tty_printf ("1 - change PIN\n"
+ "2 - unblock PIN\n"
+ "3 - change Admin PIN\n"
+ "Q - quit\n");
+ tty_printf ("\n");
+
+ answer = cpr_get("cardutil.change_pin.menu",_("Your selection? "));
+ cpr_kill_prompt();
+ if (strlen (answer) != 1)
+ continue;
+
+ rc = 0;
+ if (*answer == '1')
+ {
+ rc = agent_scd_change_pin (1, info.serialno);
+ if (rc)
+ tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
+ else
+ {
+ write_status (STATUS_SC_OP_SUCCESS);
+ tty_printf ("PIN changed.\n");
+ }
+ }
+ else if (*answer == '2')
+ {
+ rc = agent_scd_change_pin (101, info.serialno);
+ if (rc)
+ tty_printf ("Error unblocking the PIN: %s\n", gpg_strerror (rc));
+ else
+ {
+ write_status (STATUS_SC_OP_SUCCESS);
+ tty_printf ("PIN unblocked and new PIN set.\n");
+ }
+ }
+ else if (*answer == '3')
+ {
+ rc = agent_scd_change_pin (3, info.serialno);
+ if (rc)
+ tty_printf ("Error changing the PIN: %s\n", gpg_strerror (rc));
+ else
+ {
+ write_status (STATUS_SC_OP_SUCCESS);
+ tty_printf ("PIN changed.\n");
+ }
+ }
+ else if (*answer == 'q' || *answer == 'Q')
+ {
+ break;
+ }
+ }
+
+ agent_release_card_info (&info);
+}
+
+static const char *
+get_manufacturer (unsigned int no)
+{
+ /* Note: Make sure that there is no colon or linefeed in the string. */
+ switch (no)
+ {
+ case 0:
+ case 0xffff: return "test card";
+ case 0x0001: return "PPC Card Systems";
+ case 0x0002: return "Prism";
+ case 0x0003: return "OpenFortress";
+ default: return "unknown";
+ }
+}
+
+
+static void
+print_sha1_fpr (FILE *fp, const unsigned char *fpr)
+{
+ int i;
+
+ if (fpr)
+ {
+ for (i=0; i < 20 ; i+=2, fpr += 2 )
+ {
+ if (i == 10 )
+ tty_fprintf (fp, " ");
+ tty_fprintf (fp, " %02X%02X", *fpr, fpr[1]);
+ }
+ }
+ else
+ tty_fprintf (fp, " [none]");
+ tty_fprintf (fp, "\n");
+}
+
+
+static void
+print_sha1_fpr_colon (FILE *fp, const unsigned char *fpr)
+{
+ int i;
+
+ if (fpr)
+ {
+ for (i=0; i < 20 ; i++, fpr++)
+ fprintf (fp, "%02X", *fpr);
+ }
+ putc (':', fp);
+}
+
+
+static void
+print_name (FILE *fp, const char *text, const char *name)
+{
+ tty_fprintf (fp, "%s", text);
+
+ /* FIXME: tty_printf_utf8_string2 eats everything after and
+ including an @ - e.g. when printing an url. */
+ if (name && *name)
+ {
+ if (fp)
+ print_utf8_string2 (fp, name, strlen (name), '\n');
+ else
+ tty_print_utf8_string2 (name, strlen (name), 0);
+ }
+ else
+ tty_fprintf (fp, _("[not set]"));
+ tty_fprintf (fp, "\n");
+}
+
+static void
+print_isoname (FILE *fp, const char *text, const char *tag, const char *name)
+{
+ if (opt.with_colons)
+ fprintf (fp, "%s:", tag);
+ else
+ tty_fprintf (fp, "%s", text);
+
+ if (name && *name)
+ {
+ char *p, *given, *buf = xstrdup (name);
+
+ given = strstr (buf, "<<");
+ for (p=buf; *p; p++)
+ if (*p == '<')
+ *p = ' ';
+ if (given && given[2])
+ {
+ *given = 0;
+ given += 2;
+ if (opt.with_colons)
+ print_string (fp, given, strlen (given), ':');
+ else if (fp)
+ print_utf8_string2 (fp, given, strlen (given), '\n');
+ else
+ tty_print_utf8_string2 (given, strlen (given), 0);
+
+ if (opt.with_colons)
+ putc (':', fp);
+ else if (*buf)
+ tty_fprintf (fp, " ");
+ }
+
+ if (opt.with_colons)
+ print_string (fp, buf, strlen (buf), ':');
+ else if (fp)
+ print_utf8_string2 (fp, buf, strlen (buf), '\n');
+ else
+ tty_print_utf8_string2 (buf, strlen (buf), 0);
+ xfree (buf);
+ }
+ else
+ {
+ if (opt.with_colons)
+ putc (':', fp);
+ else
+ tty_fprintf (fp, _("[not set]"));
+ }
+
+ if (opt.with_colons)
+ fputs (":\n", fp);
+ else
+ tty_fprintf (fp, "\n");
+}
+
+/* Return true if the SHA1 fingerprint FPR consists only of zeroes. */
+static int
+fpr_is_zero (const char *fpr)
+{
+ int i;
+
+ for (i=0; i < 20 && !fpr[i]; i++)
+ ;
+ return (i == 20);
+}
+
+
+/* Print all available information about the current card. */
+void
+card_status (FILE *fp, char *serialno, size_t serialnobuflen)
+{
+ struct agent_card_info_s info;
+ PKT_public_key *pk = xcalloc (1, sizeof *pk);
+ int rc;
+ unsigned int uval;
+ const unsigned char *thefpr;
+ int i;
+
+ if (serialno && serialnobuflen)
+ *serialno = 0;
+
+ rc = agent_learn (&info);
+ if (rc)
+ {
+ if (opt.with_colons)
+ fputs ("AID:::\n", fp);
+ log_error (_("OpenPGP card not available: %s\n"),
+ gpg_strerror (rc));
+ xfree (pk);
+ return;
+ }
+
+ if (opt.with_colons)
+ fprintf (fp, "AID:%s:", info.serialno? info.serialno : "");
+ else
+ tty_fprintf (fp, "Application ID ...: %s\n",
+ info.serialno? info.serialno : "[none]");
+ if (!info.serialno || strncmp (info.serialno, "D27600012401", 12)
+ || strlen (info.serialno) != 32 )
+ {
+ if (opt.with_colons)
+ fputs ("unknown:\n", fp);
+ log_info ("not an OpenPGP card\n");
+ agent_release_card_info (&info);
+ xfree (pk);
+ return;
+ }
+
+ if (!serialno)
+ ;
+ else if (strlen (serialno)+1 > serialnobuflen)
+ log_error ("serial number longer than expected\n");
+ else
+ strcpy (serialno, info.serialno);
+
+ if (opt.with_colons)
+ fputs ("openpgp-card:\n", fp);
+
+
+ if (opt.with_colons)
+ {
+ fprintf (fp, "version:%.4s:\n", info.serialno+12);
+ uval = xtoi_2(info.serialno+16)*256 + xtoi_2 (info.serialno+18);
+ fprintf (fp, "vendor:%04x:%s:\n", uval, get_manufacturer (uval));
+ fprintf (fp, "serial:%.8s:\n", info.serialno+20);
+
+ print_isoname (fp, "Name of cardholder: ", "name", info.disp_name);
+
+ fputs ("lang:", fp);
+ if (info.disp_lang)
+ print_string (fp, info.disp_lang, strlen (info.disp_lang), ':');
+ fputs (":\n", fp);
+
+ fprintf (fp, "sex:%c:\n", (info.disp_sex == 1? 'm':
+ info.disp_sex == 2? 'f' : 'u'));
+
+ fputs ("url:", fp);
+ if (info.pubkey_url)
+ print_string (fp, info.pubkey_url, strlen (info.pubkey_url), ':');
+ fputs (":\n", fp);
+
+ fputs ("login:", fp);
+ if (info.login_data)
+ print_string (fp, info.login_data, strlen (info.login_data), ':');
+ fputs (":\n", fp);
+
+ fprintf (fp, "forcepin:%d:::\n", !info.chv1_cached);
+ fprintf (fp, "maxpinlen:%d:%d:%d:\n",
+ info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]);
+ fprintf (fp, "pinretry:%d:%d:%d:\n",
+ info.chvretry[0], info.chvretry[1], info.chvretry[2]);
+ fprintf (fp, "sigcount:%lu:::\n", info.sig_counter);
+
+ for (i=0; i < 4; i++)
+ {
+ if (info.private_do[i])
+ {
+ fprintf (fp, "private_do:%d:", i+1);
+ print_string (fp, info.private_do[i],
+ strlen (info.private_do[i]), ':');
+ fputs (":\n", fp);
+ }
+ }
+
+ fputs ("cafpr:", fp);
+ print_sha1_fpr_colon (fp, info.cafpr1valid? info.cafpr1:NULL);
+ print_sha1_fpr_colon (fp, info.cafpr2valid? info.cafpr2:NULL);
+ print_sha1_fpr_colon (fp, info.cafpr3valid? info.cafpr3:NULL);
+ putc ('\n', fp);
+ fputs ("fpr:", fp);
+ print_sha1_fpr_colon (fp, info.fpr1valid? info.fpr1:NULL);
+ print_sha1_fpr_colon (fp, info.fpr2valid? info.fpr2:NULL);
+ print_sha1_fpr_colon (fp, info.fpr3valid? info.fpr3:NULL);
+ putc ('\n', fp);
+ fprintf (fp, "fprtime:%lu:%lu:%lu:\n",
+ (unsigned long)info.fpr1time, (unsigned long)info.fpr2time,
+ (unsigned long)info.fpr3time);
+ }
+ else
+ {
+ tty_fprintf (fp, "Version ..........: %.1s%c.%.1s%c\n",
+ info.serialno[12] == '0'?"":info.serialno+12,
+ info.serialno[13],
+ info.serialno[14] == '0'?"":info.serialno+14,
+ info.serialno[15]);
+ tty_fprintf (fp, "Manufacturer .....: %s\n",
+ get_manufacturer (xtoi_2(info.serialno+16)*256
+ + xtoi_2 (info.serialno+18)));
+ tty_fprintf (fp, "Serial number ....: %.8s\n", info.serialno+20);
+
+ print_isoname (fp, "Name of cardholder: ", "name", info.disp_name);
+ print_name (fp, "Language prefs ...: ", info.disp_lang);
+ tty_fprintf (fp, "Sex ..............: %s\n",
+ info.disp_sex == 1? _("male"):
+ info.disp_sex == 2? _("female") : _("unspecified"));
+ print_name (fp, "URL of public key : ", info.pubkey_url);
+ print_name (fp, "Login data .......: ", info.login_data);
+ if (info.private_do[0])
+ print_name (fp, "Private DO 1 .....: ", info.private_do[0]);
+ if (info.private_do[1])
+ print_name (fp, "Private DO 2 .....: ", info.private_do[1]);
+ if (info.private_do[2])
+ print_name (fp, "Private DO 3 .....: ", info.private_do[2]);
+ if (info.private_do[3])
+ print_name (fp, "Private DO 4 .....: ", info.private_do[3]);
+ if (info.cafpr1valid)
+ {
+ tty_fprintf (fp, "CA fingerprint %d .:", 1);
+ print_sha1_fpr (fp, info.cafpr1);
+ }
+ if (info.cafpr2valid)
+ {
+ tty_fprintf (fp, "CA fingerprint %d .:", 2);
+ print_sha1_fpr (fp, info.cafpr2);
+ }
+ if (info.cafpr3valid)
+ {
+ tty_fprintf (fp, "CA fingerprint %d .:", 3);
+ print_sha1_fpr (fp, info.cafpr3);
+ }
+ tty_fprintf (fp, "Signature PIN ....: %s\n",
+ info.chv1_cached? _("not forced"): _("forced"));
+ tty_fprintf (fp, "Max. PIN lengths .: %d %d %d\n",
+ info.chvmaxlen[0], info.chvmaxlen[1], info.chvmaxlen[2]);
+ tty_fprintf (fp, "PIN retry counter : %d %d %d\n",
+ info.chvretry[0], info.chvretry[1], info.chvretry[2]);
+ tty_fprintf (fp, "Signature counter : %lu\n", info.sig_counter);
+ tty_fprintf (fp, "Signature key ....:");
+ print_sha1_fpr (fp, info.fpr1valid? info.fpr1:NULL);
+ if (info.fpr1valid && info.fpr1time)
+ tty_fprintf (fp, " created ....: %s\n",
+ isotimestamp (info.fpr1time));
+ tty_fprintf (fp, "Encryption key....:");
+ print_sha1_fpr (fp, info.fpr2valid? info.fpr2:NULL);
+ if (info.fpr2valid && info.fpr2time)
+ tty_fprintf (fp, " created ....: %s\n",
+ isotimestamp (info.fpr2time));
+ tty_fprintf (fp, "Authentication key:");
+ print_sha1_fpr (fp, info.fpr3valid? info.fpr3:NULL);
+ if (info.fpr3valid && info.fpr3time)
+ tty_fprintf (fp, " created ....: %s\n",
+ isotimestamp (info.fpr3time));
+ tty_fprintf (fp, "General key info..: ");
+
+ thefpr = (info.fpr1valid? info.fpr1 : info.fpr2valid? info.fpr2 :
+ info.fpr3valid? info.fpr3 : NULL);
+ if ( thefpr && !get_pubkey_byfprint (pk, thefpr, 20))
+ {
+ KBNODE keyblock = NULL;
+
+ print_pubkey_info (fp, pk);
+
+ if ( !get_seckeyblock_byfprint (&keyblock, thefpr, 20) )
+ print_card_key_info (fp, keyblock);
+ else if ( !get_keyblock_byfprint (&keyblock, thefpr, 20) )
+ {
+ release_kbnode (keyblock);
+ keyblock = NULL;
+
+ if (!auto_create_card_key_stub (info.serialno,
+ info.fpr1valid? info.fpr1:NULL,
+ info.fpr2valid? info.fpr2:NULL,
+ info.fpr3valid? info.fpr3:NULL))
+ {
+ if ( !get_seckeyblock_byfprint (&keyblock, thefpr, 20) )
+ print_card_key_info (fp, keyblock);
+ }
+ }
+
+ release_kbnode (keyblock);
+ }
+ else
+ tty_fprintf (fp, "[none]\n");
+ }
+
+ free_public_key (pk);
+ agent_release_card_info (&info);
+}
+
+
+static char *
+get_one_name (const char *prompt1, const char *prompt2)
+{
+ char *name;
+ int i;
+
+ for (;;)
+ {
+ name = cpr_get (prompt1, prompt2);
+ if (!name)
+ return NULL;
+ trim_spaces (name);
+ cpr_kill_prompt ();
+ for (i=0; name[i] && name[i] >= ' ' && name[i] <= 126; i++)
+ ;
+
+ /* The name must be in Latin-1 and not UTF-8 - lacking the code
+ to ensure this we restrict it to ASCII. */
+ if (name[i])
+ tty_printf (_("Error: Only plain ASCII is currently allowed.\n"));
+ else if (strchr (name, '<'))
+ tty_printf (_("Error: The \"<\" character may not be used.\n"));
+ else if (strstr (name, " "))
+ tty_printf (_("Error: Double spaces are not allowed.\n"));
+ else
+ return name;
+ xfree (name);
+ }
+}
+
+
+
+static int
+change_name (void)
+{
+ char *surname = NULL, *givenname = NULL;
+ char *isoname, *p;
+ int rc;
+
+ surname = get_one_name ("keygen.smartcard.surname",
+ _("Cardholder's surname: "));
+ givenname = get_one_name ("keygen.smartcard.givenname",
+ _("Cardholder's given name: "));
+ if (!surname || !givenname || (!*surname && !*givenname))
+ {
+ xfree (surname);
+ xfree (givenname);
+ return -1; /*canceled*/
+ }
+
+ isoname = xmalloc ( strlen (surname) + 2 + strlen (givenname) + 1);
+ strcpy (stpcpy (stpcpy (isoname, surname), "<<"), givenname);
+ xfree (surname);
+ xfree (givenname);
+ for (p=isoname; *p; p++)
+ if (*p == ' ')
+ *p = '<';
+
+ if (strlen (isoname) > 39 )
+ {
+ tty_printf (_("Error: Combined name too long "
+ "(limit is %d characters).\n"), 39);
+ xfree (isoname);
+ return -1;
+ }
+
+ rc = agent_scd_setattr ("DISP-NAME", isoname, strlen (isoname), NULL );
+ if (rc)
+ log_error ("error setting Name: %s\n", gpg_strerror (rc));
+
+ xfree (isoname);
+ return rc;
+}
+
+
+static int
+change_url (void)
+{
+ char *url;
+ int rc;
+
+ url = cpr_get ("cardedit.change_url", _("URL to retrieve public key: "));
+ if (!url)
+ return -1;
+ trim_spaces (url);
+ cpr_kill_prompt ();
+
+ if (strlen (url) > 254 )
+ {
+ tty_printf (_("Error: URL too long "
+ "(limit is %d characters).\n"), 254);
+ xfree (url);
+ return -1;
+ }
+
+ rc = agent_scd_setattr ("PUBKEY-URL", url, strlen (url), NULL );
+ if (rc)
+ log_error ("error setting URL: %s\n", gpg_strerror (rc));
+ xfree (url);
+ return rc;
+}
+
+
+/* Fetch the key from the URL given on the card or try to get it from
+ the default keyserver. */
+static int
+fetch_url(void)
+{
+#if GNUPG_MAJOR_VERSION == 1
+ int rc;
+ struct agent_card_info_s info;
+
+ memset(&info,0,sizeof(info));
+
+ rc=agent_scd_getattr("PUBKEY-URL",&info);
+ if(rc)
+ log_error("error retrieving URL from card: %s\n",gpg_strerror(rc));
+ else
+ {
+ struct keyserver_spec *spec=NULL;
+
+ rc=agent_scd_getattr("KEY-FPR",&info);
+ if(rc)
+ log_error("error retrieving key fingerprint from card: %s\n",
+ gpg_strerror(rc));
+ else if (info.pubkey_url && *info.pubkey_url)
+ {
+ spec=parse_keyserver_uri(info.pubkey_url,1,NULL,0);
+ if(spec && info.fpr1valid)
+ {
+ /* This is not perfectly right. Currently, all card
+ fingerprints are 20 digits, but what about
+ fingerprints for a future v5 key? We should get the
+ length from somewhere lower in the code. In any
+ event, the fpr/keyid is not meaningful for straight
+ HTTP fetches, but using it allows the card to point
+ to HKP and LDAP servers as well. */
+ rc=keyserver_import_fprint(info.fpr1,20,spec);
+ free_keyserver_spec(spec);
+ }
+ }
+ else if (info.fpr1valid)
+ {
+ rc = keyserver_import_fprint (info.fpr1, 20, opt.keyserver);
+ }
+ }
+
+ return rc;
+#else
+ return 0;
+#endif
+}
+
+
+static int
+change_login (const char *args)
+{
+ char *data;
+ int n;
+ int rc;
+
+ if (args && *args == '<') /* Read it from a file */
+ {
+ FILE *fp;
+
+ for (args++; spacep (args); args++)
+ ;
+ fp = fopen (args, "rb");
+#if GNUPG_MAJOR_VERSION == 1
+ if (fp && is_secured_file (fileno (fp)))
+ {
+ fclose (fp);
+ fp = NULL;
+ errno = EPERM;
+ }
+#endif
+ if (!fp)
+ {
+ tty_printf (_("can't open `%s': %s\n"), args, strerror (errno));
+ return -1;
+ }
+
+ data = xmalloc (254);
+ n = fread (data, 1, 254, fp);
+ fclose (fp);
+ if (n < 0)
+ {
+ tty_printf (_("error reading `%s': %s\n"), args, strerror (errno));
+ xfree (data);
+ return -1;
+ }
+ }
+ else
+ {
+ data = cpr_get ("cardedit.change_login",
+ _("Login data (account name): "));
+ if (!data)
+ return -1;
+ trim_spaces (data);
+ cpr_kill_prompt ();
+ n = strlen (data);
+ }
+
+ if (n > 254 )
+ {
+ tty_printf (_("Error: Login data too long "
+ "(limit is %d characters).\n"), 254);
+ xfree (data);
+ return -1;
+ }
+
+ rc = agent_scd_setattr ("LOGIN-DATA", data, n, NULL );
+ if (rc)
+ log_error ("error setting login data: %s\n", gpg_strerror (rc));
+ xfree (data);
+ return rc;
+}
+
+static int
+change_private_do (const char *args, int nr)
+{
+ char do_name[] = "PRIVATE-DO-X";
+ char *data;
+ int n;
+ int rc;
+
+ assert (nr >= 1 && nr <= 4);
+ do_name[11] = '0' + nr;
+
+ if (args && (args = strchr (args, '<'))) /* Read it from a file */
+ {
+ FILE *fp;
+
+ /* Fixme: Factor this duplicated code out. */
+ for (args++; spacep (args); args++)
+ ;
+ fp = fopen (args, "rb");
+#if GNUPG_MAJOR_VERSION == 1
+ if (fp && is_secured_file (fileno (fp)))
+ {
+ fclose (fp);
+ fp = NULL;
+ errno = EPERM;
+ }
+#endif
+ if (!fp)
+ {
+ tty_printf (_("can't open `%s': %s\n"), args, strerror (errno));
+ return -1;
+ }
+
+ data = xmalloc (254);
+ n = fread (data, 1, 254, fp);
+ fclose (fp);
+ if (n < 0)
+ {
+ tty_printf (_("error reading `%s': %s\n"), args, strerror (errno));
+ xfree (data);
+ return -1;
+ }
+ }
+ else
+ {
+ data = cpr_get ("cardedit.change_private_do",
+ _("Private DO data: "));
+ if (!data)
+ return -1;
+ trim_spaces (data);
+ cpr_kill_prompt ();
+ n = strlen (data);
+ }
+
+ if (n > 254 )
+ {
+ tty_printf (_("Error: Private DO too long "
+ "(limit is %d characters).\n"), 254);
+ xfree (data);
+ return -1;
+ }
+
+ rc = agent_scd_setattr (do_name, data, n, NULL );
+ if (rc)
+ log_error ("error setting private DO: %s\n", gpg_strerror (rc));
+ xfree (data);
+ return rc;
+}
+
+static int
+change_lang (void)
+{
+ char *data, *p;
+ int rc;
+
+ data = cpr_get ("cardedit.change_lang",
+ _("Language preferences: "));
+ if (!data)
+ return -1;
+ trim_spaces (data);
+ cpr_kill_prompt ();
+
+ if (strlen (data) > 8 || (strlen (data) & 1))
+ {
+ tty_printf (_("Error: invalid length of preference string.\n"));
+ xfree (data);
+ return -1;
+ }
+
+ for (p=data; *p && *p >= 'a' && *p <= 'z'; p++)
+ ;
+ if (*p)
+ {
+ tty_printf (_("Error: invalid characters in preference string.\n"));
+ xfree (data);
+ return -1;
+ }
+
+ rc = agent_scd_setattr ("DISP-LANG", data, strlen (data), NULL );
+ if (rc)
+ log_error ("error setting lang: %s\n", gpg_strerror (rc));
+ xfree (data);
+ return rc;
+}
+
+
+static int
+change_sex (void)
+{
+ char *data;
+ const char *str;
+ int rc;
+
+ data = cpr_get ("cardedit.change_sex",
+ _("Sex ((M)ale, (F)emale or space): "));
+ if (!data)
+ return -1;
+ trim_spaces (data);
+ cpr_kill_prompt ();
+
+ if (!*data)
+ str = "9";
+ else if ((*data == 'M' || *data == 'm') && !data[1])
+ str = "1";
+ else if ((*data == 'F' || *data == 'f') && !data[1])
+ str = "2";
+ else
+ {
+ tty_printf (_("Error: invalid response.\n"));
+ xfree (data);
+ return -1;
+ }
+
+ rc = agent_scd_setattr ("DISP-SEX", str, 1, NULL );
+ if (rc)
+ log_error ("error setting sex: %s\n", gpg_strerror (rc));
+ xfree (data);
+ return rc;
+}
+
+
+static int
+change_cafpr (int fprno)
+{
+ char *data;
+ const char *s;
+ int i, c, rc;
+ unsigned char fpr[20];
+
+ data = cpr_get ("cardedit.change_cafpr", _("CA fingerprint: "));
+ if (!data)
+ return -1;
+ trim_spaces (data);
+ cpr_kill_prompt ();
+
+ for (i=0, s=data; i < 20 && *s; )
+ {
+ while (spacep(s))
+ s++;
+ if (*s == ':')
+ s++;
+ while (spacep(s))
+ s++;
+ c = hextobyte (s);
+ if (c == -1)
+ break;
+ fpr[i++] = c;
+ s += 2;
+ }
+ xfree (data);
+ if (i != 20 || *s)
+ {
+ tty_printf (_("Error: invalid formatted fingerprint.\n"));
+ return -1;
+ }
+
+ rc = agent_scd_setattr (fprno==1?"CA-FPR-1":
+ fprno==2?"CA-FPR-2":
+ fprno==3?"CA-FPR-3":"x", fpr, 20, NULL );
+ if (rc)
+ log_error ("error setting cafpr: %s\n", gpg_strerror (rc));
+ return rc;
+}
+
+
+
+static void
+toggle_forcesig (void)
+{
+ struct agent_card_info_s info;
+ int rc;
+ int newstate;
+
+ memset (&info, 0, sizeof info);
+ rc = agent_scd_getattr ("CHV-STATUS", &info);
+ if (rc)
+ {
+ log_error ("error getting current status: %s\n", gpg_strerror (rc));
+ return;
+ }
+ newstate = !info.chv1_cached;
+ agent_release_card_info (&info);
+
+ rc = agent_scd_setattr ("CHV-STATUS-1", newstate? "\x01":"", 1, NULL);
+ if (rc)
+ log_error ("error toggling signature PIN flag: %s\n", gpg_strerror (rc));
+}
+
+
+/* Helper for the key generation/edit functions. */
+static int
+get_info_for_key_operation (struct agent_card_info_s *info)
+{
+ int rc;
+
+ memset (info, 0, sizeof *info);
+ rc = agent_scd_getattr ("SERIALNO", info);
+ if (rc || !info->serialno || strncmp (info->serialno, "D27600012401", 12)
+ || strlen (info->serialno) != 32 )
+ {
+ log_error (_("key operation not possible: %s\n"),
+ rc ? gpg_strerror (rc) : _("not an OpenPGP card"));
+ return rc? rc: -1;
+ }
+ rc = agent_scd_getattr ("KEY-FPR", info);
+ if (!rc)
+ rc = agent_scd_getattr ("CHV-STATUS", info);
+ if (!rc)
+ rc = agent_scd_getattr ("DISP-NAME", info);
+ if (rc)
+ log_error (_("error getting current key info: %s\n"), gpg_strerror (rc));
+ return rc;
+}
+
+
+/* Helper for the key generation/edit functions. */
+static int
+check_pin_for_key_operation (struct agent_card_info_s *info, int *forced_chv1)
+{
+ int rc = 0;
+
+ agent_clear_pin_cache (info->serialno);
+
+ *forced_chv1 = !info->chv1_cached;
+ if (*forced_chv1)
+ { /* Switch of the forced mode so that during key generation we
+ don't get bothered with PIN queries for each
+ self-signature. */
+ rc = agent_scd_setattr ("CHV-STATUS-1", "\x01", 1, info->serialno);
+ if (rc)
+ {
+ log_error ("error clearing forced signature PIN flag: %s\n",
+ gpg_strerror (rc));
+ *forced_chv1 = 0;
+ }
+ }
+
+ if (!rc)
+ {
+ /* Check the PIN now, so that we won't get asked later for each
+ binding signature. */
+ rc = agent_scd_checkpin (info->serialno);
+ if (rc)
+ log_error ("error checking the PIN: %s\n", gpg_strerror (rc));
+ }
+ return rc;
+}
+
+/* Helper for the key generation/edit functions. */
+static void
+restore_forced_chv1 (int *forced_chv1)
+{
+ int rc;
+
+ if (*forced_chv1)
+ { /* Switch back to forced state. */
+ rc = agent_scd_setattr ("CHV-STATUS-1", "", 1, NULL);
+ if (rc)
+ {
+ log_error ("error setting forced signature PIN flag: %s\n",
+ gpg_strerror (rc));
+ }
+ }
+}
+
+#if GNUPG_MAJOR_VERSION == 1
+/* Helper for the key generation/edit functions. */
+static void
+show_card_key_info (struct agent_card_info_s *info)
+{
+ tty_fprintf (NULL, "Signature key ....:");
+ print_sha1_fpr (NULL, info->fpr1valid? info->fpr1:NULL);
+ tty_fprintf (NULL, "Encryption key....:");
+ print_sha1_fpr (NULL, info->fpr2valid? info->fpr2:NULL);
+ tty_fprintf (NULL, "Authentication key:");
+ print_sha1_fpr (NULL, info->fpr3valid? info->fpr3:NULL);
+ tty_printf ("\n");
+}
+#endif
+
+#if GNUPG_MAJOR_VERSION == 1
+/* Helper for the key generation/edit functions. */
+static int
+replace_existing_key_p (struct agent_card_info_s *info, int keyno)
+{
+ assert (keyno >= 0 && keyno <= 3);
+
+ if ((keyno == 1 && info->fpr1valid)
+ || (keyno == 2 && info->fpr2valid)
+ || (keyno == 3 && info->fpr3valid))
+ {
+ tty_printf ("\n");
+ log_info ("WARNING: such a key has already been stored on the card!\n");
+ tty_printf ("\n");
+ if ( !cpr_get_answer_is_yes( "cardedit.genkeys.replace_key",
+ _("Replace existing key? (y/N) ")))
+ return -1;
+ }
+ return 0;
+}
+#endif
+
+
+static void
+generate_card_keys (const char *serialno)
+{
+ struct agent_card_info_s info;
+ int forced_chv1;
+ int want_backup;
+
+ if (get_info_for_key_operation (&info))
+ return;
+
+#if GNUPG_MAJOR_VERSION == 1
+ {
+ char *answer=cpr_get("cardedit.genkeys.backup_enc",
+ _("Make off-card backup of encryption key? (Y/n) "));
+
+ want_backup=answer_is_yes_no_default(answer,1);
+ cpr_kill_prompt();
+ xfree(answer);
+ }
+#else
+ want_backup = cpr_get_answer_is_yes
+ ( "cardedit.genkeys.backup_enc",
+ _("Make off-card backup of encryption key? (Y/n) "));
+ /*FIXME: we need answer_is_yes_no_default()*/
+#endif
+
+ if ( (info.fpr1valid && !fpr_is_zero (info.fpr1))
+ || (info.fpr2valid && !fpr_is_zero (info.fpr2))
+ || (info.fpr3valid && !fpr_is_zero (info.fpr3)))
+ {
+ tty_printf ("\n");
+ log_info ("NOTE: keys are already stored on the card!\n");
+ tty_printf ("\n");
+ if ( !cpr_get_answer_is_yes( "cardedit.genkeys.replace_keys",
+ _("Replace existing keys? (y/N) ")))
+ {
+ agent_release_card_info (&info);
+ return;
+ }
+ }
+ else if (!info.disp_name || !*info.disp_name)
+ {
+ tty_printf ("\n");
+ tty_printf (_("Please note that the factory settings of the PINs are\n"
+ " PIN = `%s' Admin PIN = `%s'\n"
+ "You should change them using the command --change-pin\n"),
+ "123456", "12345678");
+ tty_printf ("\n");
+ }
+
+ if (check_pin_for_key_operation (&info, &forced_chv1))
+ goto leave;
+
+#if GNUPG_MAJOR_VERSION == 1
+ generate_keypair (NULL, info.serialno,
+ want_backup? opt.homedir:NULL);
+#else
+ generate_keypair (NULL, info.serialno);
+#endif
+
+ leave:
+ agent_release_card_info (&info);
+ restore_forced_chv1 (&forced_chv1);
+}
+
+
+/* This function is used by the key edit menu to generate an arbitrary
+ subkey. */
+int
+card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock)
+{
+#if GNUPG_MAJOR_VERSION == 1
+ struct agent_card_info_s info;
+ int okay = 0;
+ int forced_chv1 = 0;
+ int keyno;
+
+ if (get_info_for_key_operation (&info))
+ return 0;
+
+ show_card_key_info (&info);
+
+ tty_printf (_("Please select the type of key to generate:\n"));
+
+ tty_printf (_(" (1) Signature key\n"));
+ tty_printf (_(" (2) Encryption key\n"));
+ tty_printf (_(" (3) Authentication key\n"));
+
+ for (;;)
+ {
+ char *answer = cpr_get ("cardedit.genkeys.subkeytype",
+ _("Your selection? "));
+ cpr_kill_prompt();
+ if (*answer == CONTROL_D)
+ {
+ xfree (answer);
+ goto leave;
+ }
+ keyno = *answer? atoi(answer): 0;
+ xfree(answer);
+ if (keyno >= 1 && keyno <= 3)
+ break; /* Okay. */
+ tty_printf(_("Invalid selection.\n"));
+ }
+
+ if (replace_existing_key_p (&info, keyno))
+ goto leave;
+
+ if (check_pin_for_key_operation (&info, &forced_chv1))
+ goto leave;
+
+ okay = generate_card_subkeypair (pub_keyblock, sec_keyblock,
+ keyno, info.serialno);
+
+ leave:
+ agent_release_card_info (&info);
+ restore_forced_chv1 (&forced_chv1);
+ return okay;
+#else
+ return 0;
+#endif
+}
+
+
+/* Store the key at NODE into the smartcard and modify NODE to
+ carry the serialno stuff instead of the actual secret key
+ parameters. USE is the usage for that key; 0 means any
+ usage. */
+int
+card_store_subkey (KBNODE node, int use)
+{
+#if GNUPG_MAJOR_VERSION == 1
+ struct agent_card_info_s info;
+ int okay = 0;
+ int rc;
+ int keyno, i;
+ PKT_secret_key *copied_sk = NULL;
+ PKT_secret_key *sk;
+ size_t n;
+ const char *s;
+ int allow_keyno[3];
+
+ assert (node->pkt->pkttype == PKT_SECRET_KEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY);
+ sk = node->pkt->pkt.secret_key;
+
+ if (get_info_for_key_operation (&info))
+ return 0;
+
+ show_card_key_info (&info);
+
+ if (!is_RSA (sk->pubkey_algo) || nbits_from_sk (sk) != 1024 )
+ {
+ tty_printf ("You may only store a 1024 bit RSA key on the card\n");
+ tty_printf ("\n");
+ goto leave;
+ }
+
+ allow_keyno[0] = (!use || (use & (PUBKEY_USAGE_SIG)));
+ allow_keyno[1] = (!use || (use & (PUBKEY_USAGE_ENC)));
+ allow_keyno[2] = (!use || (use & (PUBKEY_USAGE_SIG|PUBKEY_USAGE_AUTH)));
+
+ tty_printf (_("Please select where to store the key:\n"));
+
+ if (allow_keyno[0])
+ tty_printf (_(" (1) Signature key\n"));
+ if (allow_keyno[1])
+ tty_printf (_(" (2) Encryption key\n"));
+ if (allow_keyno[2])
+ tty_printf (_(" (3) Authentication key\n"));
+
+ for (;;)
+ {
+ char *answer = cpr_get ("cardedit.genkeys.storekeytype",
+ _("Your selection? "));
+ cpr_kill_prompt();
+ if (*answer == CONTROL_D || !*answer)
+ {
+ xfree (answer);
+ goto leave;
+ }
+ keyno = *answer? atoi(answer): 0;
+ xfree(answer);
+ if (keyno >= 1 && keyno <= 3 && allow_keyno[keyno-1])
+ break; /* Okay. */
+ tty_printf(_("Invalid selection.\n"));
+ }
+
+ if (replace_existing_key_p (&info, keyno))
+ goto leave;
+
+ /* Unprotect key. */
+ switch (is_secret_key_protected (sk) )
+ {
+ case 0: /* Not protected. */
+ break;
+ case -1:
+ log_error (_("unknown key protection algorithm\n"));
+ goto leave;
+ default:
+ if (sk->protect.s2k.mode == 1001)
+ {
+ log_error (_("secret parts of key are not available\n"));
+ goto leave;
+ }
+ if (sk->protect.s2k.mode == 1002)
+ {
+ log_error (_("secret key already stored on a card\n"));
+ goto leave;
+ }
+ /* We better copy the key before we unprotect it. */
+ copied_sk = sk = copy_secret_key (NULL, sk);
+ rc = check_secret_key (sk, 0);
+ if (rc)
+ goto leave;
+ }
+
+ rc = save_unprotected_key_to_card (sk, keyno);
+ if (rc)
+ goto leave;
+
+ /* Get back to the maybe protected original secret key. */
+ if (copied_sk)
+ {
+ free_secret_key (copied_sk);
+ copied_sk = NULL;
+ }
+ sk = node->pkt->pkt.secret_key;
+
+ /* Get rid of the secret key parameters and store the serial numer. */
+ n = pubkey_get_nskey (sk->pubkey_algo);
+ for (i=pubkey_get_npkey (sk->pubkey_algo); i < n; i++)
+ {
+ mpi_free (sk->skey[i]);
+ sk->skey[i] = NULL;
+ }
+ i = pubkey_get_npkey (sk->pubkey_algo);
+ sk->skey[i] = mpi_set_opaque (NULL, xstrdup ("dummydata"), 10);
+ sk->is_protected = 1;
+ sk->protect.s2k.mode = 1002;
+ s = info.serialno;
+ for (sk->protect.ivlen=0; sk->protect.ivlen < 16 && *s && s[1];
+ sk->protect.ivlen++, s += 2)
+ sk->protect.iv[sk->protect.ivlen] = xtoi_2 (s);
+
+ okay = 1;
+
+ leave:
+ if (copied_sk)
+ free_secret_key (copied_sk);
+ agent_release_card_info (&info);
+ return okay;
+#else
+ return 0;
+#endif
+}
+
+
+
+/* Data used by the command parser. This needs to be outside of the
+ function scope to allow readline based command completion. */
+enum cmdids
+ {
+ cmdNOP = 0,
+ cmdQUIT, cmdADMIN, cmdHELP, cmdLIST, cmdDEBUG, cmdVERIFY,
+ cmdNAME, cmdURL, cmdFETCH, cmdLOGIN, cmdLANG, cmdSEX, cmdCAFPR,
+ cmdFORCESIG, cmdGENERATE, cmdPASSWD, cmdPRIVATEDO,
+ cmdINVCMD
+ };
+
+static struct
+{
+ const char *name;
+ enum cmdids id;
+ int admin_only;
+ const char *desc;
+} cmds[] =
+ {
+ { "quit" , cmdQUIT , 0, N_("quit this menu")},
+ { "q" , cmdQUIT , 0, NULL },
+ { "admin" , cmdADMIN , 0, N_("show admin commands")},
+ { "help" , cmdHELP , 0, N_("show this help")},
+ { "?" , cmdHELP , 0, NULL },
+ { "list" , cmdLIST , 0, N_("list all available data")},
+ { "l" , cmdLIST , 0, NULL },
+ { "debug" , cmdDEBUG , 0, NULL },
+ { "name" , cmdNAME , 1, N_("change card holder's name")},
+ { "url" , cmdURL , 1, N_("change URL to retrieve key")},
+ { "fetch" , cmdFETCH , 0, N_("fetch the key specified in the card URL")},
+ { "login" , cmdLOGIN , 1, N_("change the login name")},
+ { "lang" , cmdLANG , 1, N_("change the language preferences")},
+ { "sex" , cmdSEX , 1, N_("change card holder's sex")},
+ { "cafpr" , cmdCAFPR , 1, N_("change a CA fingerprint")},
+ { "forcesig", cmdFORCESIG, 1, N_("toggle the signature force PIN flag")},
+ { "generate", cmdGENERATE, 1, N_("generate new keys")},
+ { "passwd" , cmdPASSWD, 0, N_("menu to change or unblock the PIN")},
+ { "verify" , cmdVERIFY, 0, N_("verify the PIN and list all data")},
+ /* Note, that we do not announce this command yet. */
+ { "privatedo", cmdPRIVATEDO, 0, NULL },
+ { NULL, cmdINVCMD, 0, NULL }
+ };
+
+
+#if GNUPG_MAJOR_VERSION == 1 && defined (HAVE_LIBREADLINE)
+
+/* These two functions are used by readline for command completion. */
+
+static char *
+command_generator(const char *text,int state)
+{
+ static int list_index,len;
+ const char *name;
+
+ /* If this is a new word to complete, initialize now. This includes
+ saving the length of TEXT for efficiency, and initializing the
+ index variable to 0. */
+ if(!state)
+ {
+ list_index=0;
+ len=strlen(text);
+ }
+
+ /* Return the next partial match */
+ while((name=cmds[list_index].name))
+ {
+ /* Only complete commands that have help text */
+ if(cmds[list_index++].desc && strncmp(name,text,len)==0)
+ return strdup(name);
+ }
+
+ return NULL;
+}
+
+static char **
+card_edit_completion(const char *text, int start, int end)
+{
+ /* If we are at the start of a line, we try and command-complete.
+ If not, just do nothing for now. */
+
+ if(start==0)
+ return rl_completion_matches(text,command_generator);
+
+ rl_attempted_completion_over=1;
+
+ return NULL;
+}
+#endif /* GNUPG_MAJOR_VERSION == 1 && HAVE_LIBREADLINE */
+
+/* Menu to edit all user changeable values on an OpenPGP card. Only
+ Key creation is not handled here. */
+void
+card_edit (STRLIST commands)
+{
+ enum cmdids cmd = cmdNOP;
+ int have_commands = !!commands;
+ int redisplay = 1;
+ char *answer = NULL;
+ int did_checkpin = 0, allow_admin=0;
+ char serialnobuf[50];
+
+
+ if (opt.command_fd != -1)
+ ;
+ else if (opt.batch && !have_commands)
+ {
+ log_error(_("can't do this in batch mode\n"));
+ goto leave;
+ }
+
+ for (;;)
+ {
+ int arg_number;
+ const char *arg_string = "";
+ char *p;
+ int i;
+ int cmd_admin_only;
+
+ tty_printf("\n");
+ if (redisplay )
+ {
+ if (opt.with_colons)
+ {
+ card_status (stdout, serialnobuf, DIM (serialnobuf));
+ fflush (stdout);
+ }
+ else
+ {
+ card_status (NULL, serialnobuf, DIM (serialnobuf));
+ tty_printf("\n");
+ }
+ redisplay = 0;
+ }
+
+ do
+ {
+ xfree (answer);
+ if (have_commands)
+ {
+ if (commands)
+ {
+ answer = xstrdup (commands->d);
+ commands = commands->next;
+ }
+ else if (opt.batch)
+ {
+ answer = xstrdup ("quit");
+ }
+ else
+ have_commands = 0;
+ }
+
+ if (!have_commands)
+ {
+#if GNUPG_MAJOR_VERSION == 1
+ tty_enable_completion (card_edit_completion);
+#endif
+ answer = cpr_get_no_help("cardedit.prompt", _("Command> "));
+ cpr_kill_prompt();
+#if GNUPG_MAJOR_VERSION == 1
+ tty_disable_completion ();
+#endif
+ }
+ trim_spaces(answer);
+ }
+ while ( *answer == '#' );
+
+ arg_number = 0; /* Yes, here is the init which egcc complains about */
+ cmd_admin_only = 0;
+ if (!*answer)
+ cmd = cmdLIST; /* Default to the list command */
+ else if (*answer == CONTROL_D)
+ cmd = cmdQUIT;
+ else
+ {
+ if ((p=strchr (answer,' ')))
+ {
+ *p++ = 0;
+ trim_spaces (answer);
+ trim_spaces (p);
+ arg_number = atoi(p);
+ arg_string = p;
+ }
+
+ for (i=0; cmds[i].name; i++ )
+ if (!ascii_strcasecmp (answer, cmds[i].name ))
+ break;
+
+ cmd = cmds[i].id;
+ cmd_admin_only = cmds[i].admin_only;
+ }
+
+ if (!allow_admin && cmd_admin_only)
+ {
+ tty_printf ("\n");
+ tty_printf (_("Admin-only command\n"));
+ continue;
+ }
+
+ switch (cmd)
+ {
+ case cmdHELP:
+ for (i=0; cmds[i].name; i++ )
+ if(cmds[i].desc
+ && (!cmds[i].admin_only || (cmds[i].admin_only && allow_admin)))
+ tty_printf("%-10s %s\n", cmds[i].name, _(cmds[i].desc) );
+ break;
+
+ case cmdADMIN:
+ if ( !strcmp (arg_string, "on") )
+ allow_admin = 1;
+ else if ( !strcmp (arg_string, "off") )
+ allow_admin = 0;
+ else if ( !strcmp (arg_string, "verify") )
+ {
+ /* Force verification of the Admin Command. However,
+ this is only done if the retry counter is at initial
+ state. */
+ char *tmp = xmalloc (strlen (serialnobuf) + 6 + 1);
+ strcpy (stpcpy (tmp, serialnobuf), "[CHV3]");
+ allow_admin = !agent_scd_checkpin (tmp);
+ xfree (tmp);
+ }
+ else /* Toggle. */
+ allow_admin=!allow_admin;
+ if(allow_admin)
+ tty_printf(_("Admin commands are allowed\n"));
+ else
+ tty_printf(_("Admin commands are not allowed\n"));
+ break;
+
+ case cmdVERIFY:
+ agent_scd_checkpin (serialnobuf);
+ redisplay = 1;
+ break;
+
+ case cmdLIST:
+ redisplay = 1;
+ break;
+
+ case cmdNAME:
+ change_name ();
+ break;
+
+ case cmdURL:
+ change_url ();
+ break;
+
+ case cmdFETCH:
+ fetch_url();
+ break;
+
+ case cmdLOGIN:
+ change_login (arg_string);
+ break;
+
+ case cmdLANG:
+ change_lang ();
+ break;
+
+ case cmdSEX:
+ change_sex ();
+ break;
+
+ case cmdCAFPR:
+ if ( arg_number < 1 || arg_number > 3 )
+ tty_printf ("usage: cafpr N\n"
+ " 1 <= N <= 3\n");
+ else
+ change_cafpr (arg_number);
+ break;
+
+ case cmdPRIVATEDO:
+ if ( arg_number < 1 || arg_number > 4 )
+ tty_printf ("usage: privatedo N\n"
+ " 1 <= N <= 4\n");
+ else
+ change_private_do (arg_string, arg_number);
+ break;
+
+ case cmdFORCESIG:
+ toggle_forcesig ();
+ break;
+
+ case cmdGENERATE:
+ generate_card_keys (serialnobuf);
+ break;
+
+ case cmdPASSWD:
+ change_pin (0, allow_admin);
+ did_checkpin = 0; /* Need to reset it of course. */
+ break;
+
+ case cmdQUIT:
+ goto leave;
+
+ case cmdNOP:
+ break;
+
+ case cmdINVCMD:
+ default:
+ tty_printf ("\n");
+ tty_printf (_("Invalid command (try \"help\")\n"));
+ break;
+ } /* End command switch. */
+ } /* End of main menu loop. */
+
+ leave:
+ xfree (answer);
+}
+
diff --git a/g10/cardglue.c b/g10/cardglue.c
new file mode 100644
index 0000000..1bfb9e4
--- /dev/null
+++ b/g10/cardglue.c
@@ -0,0 +1,1432 @@
+/* cardglue.c - mainly dispatcher for card related functions.
+ * Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#ifndef ENABLE_CARD_SUPPORT
+#error not configured for card support.
+#endif
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <stdarg.h>
+#include <assert.h>
+#include "options.h"
+#include "packet.h"
+#include "errors.h"
+#include "memory.h"
+#include "util.h"
+#include "main.h"
+#include "status.h"
+#include "ttyio.h"
+#include "i18n.h"
+
+#include "cardglue.h"
+#include "apdu.h"
+#include "app-common.h"
+
+
+
+struct ctrl_ctx_s
+{
+ assuan_error_t (*status_cb)(void *opaque, const char *line);
+ void *status_cb_arg;
+};
+
+
+struct pincb_parm_s
+{
+ const char *sn;
+};
+
+
+struct writekey_parm_s
+{
+ assuan_context_t ctx;
+ const unsigned char *keydata;
+ size_t keydatalen;
+};
+
+
+
+static char *default_reader_port;
+static app_t current_app;
+
+
+/* Local prototypes. */
+static assuan_error_t learn_status_cb (void *opaque, const char *line);
+
+
+/* To avoid cluttering the code with bunches of ifdefs we use a few
+ dummy functions instead and defines. */
+#ifndef ENABLE_AGENT_SUPPORT
+
+#define ASSUAN_LINELENGTH 100
+
+static assuan_context_t
+agent_open (int try, const char *orig_codeset)
+{
+ return NULL;
+}
+
+void
+agent_close (assuan_context_t ctx)
+{
+}
+
+const char *
+assuan_strerror (assuan_error_t err)
+{
+ return "no Assuan support";
+}
+
+assuan_error_t
+assuan_transact (assuan_context_t ctx,
+ const char *command,
+ assuan_error_t (*data_cb)(void *, const void *, size_t),
+ void *data_cb_arg,
+ assuan_error_t (*inquire_cb)(void*, const char *),
+ void *inquire_cb_arg,
+ assuan_error_t (*status_cb)(void*, const char *),
+ void *status_cb_arg)
+{
+ return 100; /* ASSUAN_NOT_IMPLEMENTED */
+}
+assuan_error_t
+assuan_send_data (assuan_context_t ctx, const void *buffer, size_t length)
+{
+ return 100; /* ASSUAN_NOT_IMPLEMENTED */
+}
+#endif /*!ENABLE_AGENT_SUPPORT*/
+
+
+/* Create a serialno/fpr string from the serial number and the secret
+ key. caller must free the returned string. There is no error
+ return. [Taken from 1.9's keyid.c]*/
+char *
+serialno_and_fpr_from_sk (const unsigned char *sn, size_t snlen,
+ PKT_secret_key *sk)
+{
+ unsigned char fpr[MAX_FINGERPRINT_LEN];
+ size_t fprlen;
+ char *buffer, *p;
+ int i;
+
+ fingerprint_from_sk (sk, fpr, &fprlen);
+ buffer = p = xmalloc (snlen*2 + 1 + fprlen*2 + 1);
+ for (i=0; i < snlen; i++, p+=2)
+ sprintf (p, "%02X", sn[i]);
+ *p++ = '/';
+ for (i=0; i < fprlen; i++, p+=2)
+ sprintf (p, "%02X", fpr[i]);
+ *p = 0;
+ return buffer;
+}
+
+
+/* Send a line with status information via assuan and escape all given
+ buffers. The variable elements are pairs of (char *, size_t),
+ terminated with a (NULL, 0). */
+void
+send_status_info (ctrl_t ctrl, const char *keyword, ...)
+{
+ va_list arg_ptr;
+ const unsigned char *value;
+ size_t valuelen;
+ char buf[950], *p;
+ size_t n;
+
+ va_start (arg_ptr, keyword);
+
+ p = buf;
+ n = 0;
+ valuelen = strlen (keyword);
+ for ( ; valuelen && n < DIM (buf)-2; n++, valuelen--, keyword++)
+ *p++ = *keyword;
+
+ while ( (value = va_arg (arg_ptr, const unsigned char *)) )
+ {
+ valuelen = va_arg (arg_ptr, size_t);
+ if (!valuelen)
+ continue; /* empty buffer */
+ if (n)
+ {
+ *p++ = ' ';
+ n++;
+ }
+ for ( ; valuelen && n < DIM (buf)-2; n++, valuelen--, value++)
+ {
+ if (*value < ' ' || *value == '+')
+ {
+ sprintf (p, "%%%02X", *value);
+ p += 3;
+ }
+ else if (*value == ' ')
+ *p++ = '+';
+ else
+ *p++ = *value;
+ }
+ }
+ *p = 0;
+ if (ctrl && ctrl->status_cb)
+ ctrl->status_cb (ctrl->status_cb_arg, buf);
+
+ va_end (arg_ptr);
+}
+
+
+/* Replacement function of the Libgcrypt onewhich is used in gnupg
+ 1.9. Thus function computes the digest of ALGO from the data in
+ BUFFER of LENGTH. ALGO must be supported. */
+void
+gcry_md_hash_buffer (int algo, void *digest,
+ const void *buffer, size_t length)
+{
+ MD_HANDLE h = md_open (algo, 0);
+ if (!h)
+ BUG();
+ md_write (h, (byte *) buffer, length);
+ md_final (h);
+ memcpy (digest, md_read (h, algo), md_digest_length (algo));
+ md_close (h);
+}
+
+
+/* This is a limited version of the one in 1.9 but it should be
+ sufficient here. */
+void
+log_printf (const char *fmt, ...)
+{
+ va_list arg_ptr;
+
+ va_start (arg_ptr, fmt);
+ vfprintf (log_stream (), fmt, arg_ptr);
+ va_end (arg_ptr);
+}
+
+
+
+/* Print a hexdump of BUFFER. With TEXT of NULL print just the raw
+ dump, with TEXT just an empty string, print a trailing linefeed,
+ otherwise print an entire debug line. */
+void
+log_printhex (const char *text, const void *buffer, size_t length)
+{
+ if (text && *text)
+ log_debug ("%s ", text);
+ if (length)
+ {
+ const unsigned char *p = buffer;
+ log_printf ("%02X", *p);
+ for (length--, p++; length--; p++)
+ log_printf (" %02X", *p);
+ }
+ if (text)
+ log_printf ("\n");
+}
+
+
+
+void
+app_set_default_reader_port (const char *portstr)
+{
+ xfree (default_reader_port);
+ default_reader_port = portstr? xstrdup (portstr): NULL;
+}
+
+
+void
+card_set_reader_port (const char *portstr)
+{
+ app_set_default_reader_port (portstr);
+}
+
+
+/* Retrieve the serial number and the time of the last update of the
+ card. The serial number is returned as a malloced string (hex
+ encoded) in SERIAL and the time of update is returned in STAMP. If
+ no update time is available the returned value is 0. Caller must
+ free SERIAL unless the function returns an error. */
+int
+app_get_serial_and_stamp (app_t app, char **serial, time_t *stamp)
+{
+ unsigned char *buf, *p;
+ int i;
+
+ if (!app || !serial || !stamp)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ *serial = NULL;
+ *stamp = 0; /* not available */
+
+ buf = xtrymalloc (app->serialnolen * 2 + 1);
+ if (!buf)
+ return gpg_error_from_errno (errno);
+ for (p=buf, i=0; i < app->serialnolen; p +=2, i++)
+ sprintf (p, "%02X", app->serialno[i]);
+ *p = 0;
+ *serial = buf;
+ return 0;
+}
+
+
+
+/* Release the card info structure. */
+void
+agent_release_card_info (struct agent_card_info_s *info)
+{
+ int i;
+
+ if (!info)
+ return;
+
+ xfree (info->serialno); info->serialno = NULL;
+ xfree (info->disp_name); info->disp_name = NULL;
+ xfree (info->disp_lang); info->disp_lang = NULL;
+ xfree (info->pubkey_url); info->pubkey_url = NULL;
+ xfree (info->login_data); info->login_data = NULL;
+ info->fpr1valid = info->fpr2valid = info->fpr3valid = 0;
+ info->cafpr1valid = info->cafpr2valid = info->cafpr3valid = 0;
+ for (i=0; i < 4; i++)
+ {
+ xfree (info->private_do[i]);
+ info->private_do[i] = NULL;
+ }
+}
+
+
+/* Print an error message for a failed assuan_transact and return a
+ gpg error code. No error is printed if RC is 0. */
+static gpg_error_t
+test_transact (int rc, const char *command)
+{
+ if (!rc)
+ return 0;
+ log_error ("sending command `%s' to agent failed: %s\n",
+ command, assuan_strerror (rc));
+ return gpg_error (GPG_ERR_CARD);
+}
+
+
+/* Try to open a card using an already running agent. Prepare a
+ proper application context and return it. */
+static app_t
+open_card_via_agent (int *scd_available)
+{
+ assuan_context_t ctx;
+ app_t app;
+ struct agent_card_info_s info;
+ int rc;
+
+ *scd_available = 0;
+ ctx = agent_open (1, NULL);
+ if (!ctx)
+ return NULL;
+
+ /* Request the serialbnumber of the card. If we get
+ NOT_SUPPORTED or NO_SCDAEMON back, the gpg-agent either has
+ disabled scdaemon or it can't be used. We close the connection
+ in this case and use our own code. This may happen if just the
+ gpg-agent has been installed for the sake of passphrase
+ caching. */
+ memset (&info, 0, sizeof info);
+ rc = assuan_transact (ctx, "SCD SERIALNO openpgp",
+ NULL, NULL, NULL, NULL,
+ learn_status_cb, &info);
+ if (rc)
+ {
+ if ((rc & 0xffff) == 60 || (rc & 0xffff) == 119)
+ ; /* No scdaemon available to gpg-agent. */
+ else
+ {
+ write_status_text (STATUS_CARDCTRL, "4");
+ log_info ("selecting openpgp failed: %s\n", assuan_strerror (rc));
+ *scd_available = 1;
+ }
+ agent_release_card_info (&info);
+ agent_close (ctx);
+ return NULL;
+ }
+
+ app = xcalloc (1, sizeof *app);
+ app->assuan_ctx = ctx;
+
+ return app;
+}
+
+
+
+/* Open the current card and select the openpgp application. Return
+ an APP context handle to be used for further procesing or NULL on
+ error or if no OpenPGP application exists.*/
+static app_t
+open_card (void)
+{
+ int slot = -1;
+ int rc;
+ app_t app;
+ int did_shutdown = 0;
+ int retry_count = 0;
+
+ /* First check whether we can contact a gpg-agent and divert all
+ operation to it. This is required because gpg as well as the
+ agent require exclusive access to the reader. */
+ if (opt.use_agent)
+ {
+ int scd_available;
+
+ app = open_card_via_agent (&scd_available);
+ if (app)
+ goto ready; /* Yes, there is a agent with a usable card, go that way. */
+ if (scd_available)
+ return NULL; /* agent avilabale but card problem. */
+ }
+
+
+ /* No agent or usable agent, thus we do it on our own. */
+ card_close ();
+
+ retry:
+ if (did_shutdown)
+ apdu_reset (slot);
+ else
+ {
+ slot = apdu_open_reader (default_reader_port);
+ if (slot == -1)
+ {
+ write_status_text (STATUS_CARDCTRL, "5");
+ log_error (_("card reader not available\n"));
+ return NULL;
+ }
+ }
+
+ app = xcalloc (1, sizeof *app);
+ app->slot = slot;
+ rc = app_select_openpgp (app);
+ if (opt.limit_card_insert_tries
+ && ++retry_count >= opt.limit_card_insert_tries)
+ ;
+ else if (rc && !opt.batch)
+ {
+ write_status_text (STATUS_CARDCTRL, "1");
+
+ did_shutdown = !!apdu_shutdown_reader (slot);
+
+ if ( cpr_get_answer_okay_cancel ("cardctrl.insert_card.okay",
+ _("Please insert the card and hit return or enter 'c' to cancel: "),
+ 1) )
+ {
+ if (!did_shutdown)
+ apdu_close_reader (slot);
+ xfree (app);
+ goto retry;
+ }
+ }
+ if (rc)
+ {
+ write_status_text (STATUS_CARDCTRL, "4");
+ log_info (_("selecting openpgp failed: %s\n"), gpg_strerror (rc));
+ apdu_close_reader (slot);
+ xfree (app);
+ return NULL;
+ }
+
+ ready:
+ app->initialized = 1;
+ current_app = app;
+ if (is_status_enabled () )
+ {
+ int i;
+ char *p, *buf;
+
+ buf = xmalloc (5 + app->serialnolen * 2 + 1);
+ p = stpcpy (buf, "3 ");
+ for (i=0; i < app->serialnolen; p +=2, i++)
+ sprintf (p, "%02X", app->serialno[i]);
+ write_status_text (STATUS_CARDCTRL, buf);
+ xfree (buf);
+ }
+
+ return app;
+}
+
+
+void
+card_close (void)
+{
+ if (current_app)
+ {
+ app_t app = current_app;
+ current_app = NULL;
+
+ if (app->assuan_ctx)
+ agent_close (app->assuan_ctx);
+ else
+ apdu_close_reader (app->slot);
+ xfree (app);
+ }
+}
+
+
+/* Format a cache ID from the serialnumber in SN and return it as an
+ allocated string. In case of an error NULL is returned. */
+static char *
+format_cacheid (const char *sn)
+{
+ const char *s;
+ size_t snlen;
+ char *cacheid = NULL;
+
+ /* The serialnumber we use for a card is "CARDSN:serialno". Where
+ serialno is the BCD string (i.e. hex string) with the full
+ number. The serial number expect here constsis of hexdigits
+ followed by other characters, we cut off these other
+ characters. */
+ if (sn)
+ {
+ for (s=sn,snlen=0; hexdigitp (s); s++, snlen++)
+ ;
+ if (snlen == 32)
+ {
+ /* Yes, this looks indeed like an OpenPGP card S/N. */
+ cacheid = xtrymalloc (7+snlen+1);
+ if (cacheid)
+ {
+ memcpy (cacheid, "CARDSN:", 7);
+ memcpy (cacheid+7, sn, snlen);
+ cacheid[7+snlen] = 0;
+ }
+ }
+ }
+ return cacheid;
+}
+
+
+/* If RC is not 0, write an appropriate status message. */
+static void
+status_sc_op_failure (int rc)
+{
+ if (rc == G10ERR_CANCELED)
+ write_status_text (STATUS_SC_OP_FAILURE, "1");
+ else if (rc == G10ERR_BAD_PASS)
+ write_status_text (STATUS_SC_OP_FAILURE, "2");
+ else if (rc)
+ write_status (STATUS_SC_OP_FAILURE);
+}
+
+
+/* Check that the serial number of the current card (as described by
+ APP) matches SERIALNO. If there is no match and we are not in
+ batch mode, present a prompt to insert the desired card. The
+ function returnd 0 if the present card is okay, -1 if the user
+ selected to insert a new card or an error value. Note that the
+ card context will be closed in all cases except for 0 as return
+ value and if it was possible to merely shutdown the reader. */
+static int
+check_card_serialno (app_t app, const char *serialno)
+{
+ const char *s;
+ int ask = 0;
+ int n;
+
+ for (s = serialno, n=0; *s != '/' && hexdigitp (s); s++, n++)
+ ;
+ if (n != 32)
+ {
+ log_error ("invalid serial number in keyring detected\n");
+ return gpg_error (GPG_ERR_INV_ID);
+ }
+ if (app->serialnolen != 16)
+ ask = 1;
+ for (s = serialno, n=0; !ask && n < 16; s += 2, n++)
+ if (app->serialno[n] != xtoi_2 (s))
+ ask = 1;
+ if (ask)
+ {
+ char buf[5+32+1];
+ int did_shutdown = 0;
+
+ if (current_app && !apdu_shutdown_reader (current_app->slot))
+ did_shutdown = 1;
+ else
+ card_close ();
+
+ if (!opt.batch)
+ tty_printf (_("Please remove the current card and "
+ "insert the one with serial number:\n"
+ " %.*s\n"), 32, serialno);
+
+ sprintf (buf, "1 %.32s", serialno);
+ write_status_text (STATUS_CARDCTRL, buf);
+
+ if ( !opt.batch
+ && cpr_get_answer_okay_cancel ("cardctrl.change_card.okay",
+ _("Hit return when ready "
+ "or enter 'c' to cancel: "),
+ 1) )
+ {
+ card_close ();
+ return -1;
+ }
+ if (did_shutdown)
+ apdu_reset (current_app->slot);
+ else
+ card_close ();
+ return gpg_error (GPG_ERR_INV_ID);
+ }
+ return 0;
+}
+
+
+/* Take a 20 byte hexencoded string and put it into the the provided
+ 20 byte buffer FPR in binary format. */
+static int
+unhexify_fpr (const char *hexstr, unsigned char *fpr)
+{
+ const char *s;
+ int n;
+
+ for (s=hexstr, n=0; hexdigitp (s); s++, n++)
+ ;
+ if (*s || (n != 40))
+ return 0; /* no fingerprint (invalid or wrong length). */
+ n /= 2;
+ for (s=hexstr, n=0; *s; s += 2, n++)
+ fpr[n] = xtoi_2 (s);
+ return 1; /* okay */
+}
+
+/* Take the serial number from LINE and return it verbatim in a newly
+ allocated string. We make sure that only hex characters are
+ returned. */
+static char *
+store_serialno (const char *line)
+{
+ const char *s;
+ char *p;
+
+ for (s=line; hexdigitp (s); s++)
+ ;
+ p = xmalloc (s + 1 - line);
+ memcpy (p, line, s-line);
+ p[s-line] = 0;
+ return p;
+}
+
+
+
+static assuan_error_t
+learn_status_cb (void *opaque, const char *line)
+{
+ struct agent_card_info_s *parm = opaque;
+ const char *keyword = line;
+ int keywordlen;
+ int i;
+
+/* log_debug ("got status line `%s'\n", line); */
+ for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
+ ;
+ while (spacep (line))
+ line++;
+
+ if (keywordlen == 8 && !memcmp (keyword, "SERIALNO", keywordlen))
+ {
+ xfree (parm->serialno);
+ parm->serialno = store_serialno (line);
+ }
+ else if (keywordlen == 9 && !memcmp (keyword, "DISP-NAME", keywordlen))
+ {
+ xfree (parm->disp_name);
+ parm->disp_name = unescape_percent_string (line);
+ }
+ else if (keywordlen == 9 && !memcmp (keyword, "DISP-LANG", keywordlen))
+ {
+ xfree (parm->disp_lang);
+ parm->disp_lang = unescape_percent_string (line);
+ }
+ else if (keywordlen == 8 && !memcmp (keyword, "DISP-SEX", keywordlen))
+ {
+ parm->disp_sex = *line == '1'? 1 : *line == '2' ? 2: 0;
+ }
+ else if (keywordlen == 10 && !memcmp (keyword, "PUBKEY-URL", keywordlen))
+ {
+ xfree (parm->pubkey_url);
+ parm->pubkey_url = unescape_percent_string (line);
+ }
+ else if (keywordlen == 10 && !memcmp (keyword, "LOGIN-DATA", keywordlen))
+ {
+ xfree (parm->login_data);
+ parm->login_data = unescape_percent_string (line);
+ }
+ else if (keywordlen == 11 && !memcmp (keyword, "SIG-COUNTER", keywordlen))
+ {
+ parm->sig_counter = strtoul (line, NULL, 0);
+ }
+ else if (keywordlen == 10 && !memcmp (keyword, "CHV-STATUS", keywordlen))
+ {
+ char *p, *buf;
+
+ buf = p = unescape_percent_string (line);
+ if (buf)
+ {
+ while (spacep (p))
+ p++;
+ parm->chv1_cached = atoi (p);
+ while (*p && !spacep (p))
+ p++;
+ while (spacep (p))
+ p++;
+ for (i=0; *p && i < 3; i++)
+ {
+ parm->chvmaxlen[i] = atoi (p);
+ while (*p && !spacep (p))
+ p++;
+ while (spacep (p))
+ p++;
+ }
+ for (i=0; *p && i < 3; i++)
+ {
+ parm->chvretry[i] = atoi (p);
+ while (*p && !spacep (p))
+ p++;
+ while (spacep (p))
+ p++;
+ }
+ xfree (buf);
+ }
+ }
+ else if (keywordlen == 7 && !memcmp (keyword, "KEY-FPR", keywordlen))
+ {
+ int no = atoi (line);
+ while (* line && !spacep (line))
+ line++;
+ while (spacep (line))
+ line++;
+ if (no == 1)
+ parm->fpr1valid = unhexify_fpr (line, parm->fpr1);
+ else if (no == 2)
+ parm->fpr2valid = unhexify_fpr (line, parm->fpr2);
+ else if (no == 3)
+ parm->fpr3valid = unhexify_fpr (line, parm->fpr3);
+ }
+ else if (keywordlen == 8 && !memcmp (keyword, "KEY-TIME", keywordlen))
+ {
+ int no = atoi (line);
+ while (* line && !spacep (line))
+ line++;
+ while (spacep (line))
+ line++;
+ if (no == 1)
+ parm->fpr1time = strtoul (line, NULL, 10);
+ else if (no == 2)
+ parm->fpr2time = strtoul (line, NULL, 10);
+ else if (no == 3)
+ parm->fpr3time = strtoul (line, NULL, 10);
+ }
+ else if (keywordlen == 6 && !memcmp (keyword, "CA-FPR", keywordlen))
+ {
+ int no = atoi (line);
+ while (*line && !spacep (line))
+ line++;
+ while (spacep (line))
+ line++;
+ if (no == 1)
+ parm->cafpr1valid = unhexify_fpr (line, parm->cafpr1);
+ else if (no == 2)
+ parm->cafpr2valid = unhexify_fpr (line, parm->cafpr2);
+ else if (no == 3)
+ parm->cafpr3valid = unhexify_fpr (line, parm->cafpr3);
+ }
+ else if (keywordlen == 12 && !memcmp (keyword, "PRIVATE-DO-", 11)
+ && strchr ("1234", keyword[11]))
+ {
+ int no = keyword[11] - '1';
+ assert (no >= 0 && no <= 3);
+ xfree (parm->private_do[no]);
+ parm->private_do[no] = unescape_percent_string (line);
+ }
+
+ return 0;
+}
+
+
+/* Return card info. */
+int
+agent_learn (struct agent_card_info_s *info)
+{
+ app_t app;
+ int rc;
+ struct ctrl_ctx_s ctrl;
+ time_t stamp;
+ char *serial;
+
+ app = current_app? current_app : open_card ();
+ if (!app)
+ return gpg_error (GPG_ERR_CARD);
+
+ memset (info, 0, sizeof *info);
+
+ if (app->assuan_ctx)
+ {
+ rc = assuan_transact (app->assuan_ctx, "SCD LEARN --force",
+ NULL, NULL, NULL, NULL,
+ learn_status_cb, info);
+ rc = test_transact (rc, "SCD LEARN");
+ }
+ else
+ {
+ memset (&ctrl, 0, sizeof ctrl);
+ ctrl.status_cb = learn_status_cb;
+ ctrl.status_cb_arg = info;
+
+ rc = app_get_serial_and_stamp (app, &serial, &stamp);
+ if (!rc)
+ {
+ send_status_info (&ctrl, "SERIALNO",
+ serial, strlen(serial), NULL, 0);
+ xfree (serial);
+ rc = app->fnc.learn_status (app, &ctrl);
+ }
+ }
+
+ return rc;
+}
+
+
+/* Get an attribute from the card. Make sure info is initialized. */
+int
+agent_scd_getattr (const char *name, struct agent_card_info_s *info)
+{
+ int rc;
+ app_t app;
+ struct ctrl_ctx_s ctrl;
+
+ app = current_app? current_app : open_card ();
+ if (!app)
+ return gpg_error (GPG_ERR_CARD);
+
+ if (app->assuan_ctx)
+ {
+ char line[ASSUAN_LINELENGTH];
+
+ /* We assume that NAME does not need escaping. */
+ if (12 + strlen (name) > DIM(line)-1)
+ return gpg_error (GPG_ERR_CARD);
+ stpcpy (stpcpy (line, "SCD GETATTR "), name);
+
+ rc = test_transact (assuan_transact (app->assuan_ctx, line,
+ NULL, NULL, NULL, NULL,
+ learn_status_cb, info),
+ "SCD GETATTR");
+ }
+ else
+ {
+ ctrl.status_cb = learn_status_cb;
+ ctrl.status_cb_arg = info;
+ rc = app->fnc.getattr (app, &ctrl, name);
+ }
+
+ return rc;
+}
+
+
+
+static int
+pin_cb (void *opaque, const char *info, char **retstr)
+{
+ struct pincb_parm_s *parm = opaque;
+ char *value;
+ int canceled;
+ int isadmin = 0;
+ int newpin = 0;
+ const char *again_text = NULL;
+ const char *ends, *s;
+ char *cacheid = NULL;
+
+ *retstr = NULL;
+ /* log_debug ("asking for PIN '%s'\n", info); */
+
+ /* We use a special prefix to check whether the Admin PIN has been
+ requested. */
+ if (info && *info =='|' && (ends=strchr (info+1, '|')))
+ {
+ for (s=info+1; s < ends; s++)
+ {
+ if (*s == 'A')
+ isadmin = 1;
+ else if (*s == 'N')
+ newpin = 1;
+ }
+ info = ends+1;
+ }
+ else if (info && *info == '|')
+ log_debug ("pin_cb called without proper PIN info hack\n");
+
+ /* If we are not requesting a new PIN and we are not requesting an
+ AdminPIN, compute a string to be used as the cacheID for
+ gpg-agent. */
+ if (!newpin && !isadmin && parm)
+ {
+ cacheid = format_cacheid (parm->sn);
+ }
+ else if (newpin && parm)
+ {
+ /* Make really sure that it is not cached anymore. */
+ agent_clear_pin_cache (parm->sn);
+ }
+
+
+ again:
+ if (is_status_enabled())
+ {
+ if (parm && parm->sn && *parm->sn)
+ {
+ char *buf = xmalloc ( 10 + strlen (parm->sn) + 1);
+ strcpy (stpcpy (buf, isadmin? "OPENPGP 3 ":"OPENPGP 1 "), parm->sn);
+ write_status_text (STATUS_NEED_PASSPHRASE_PIN, buf);
+ xfree (buf);
+ }
+ else
+ write_status_text (STATUS_NEED_PASSPHRASE_PIN,
+ isadmin? "OPENPGP 3" : "OPENPGP 1");
+ }
+
+ value = ask_passphrase (info, again_text,
+ newpin && isadmin? "passphrase.adminpin.new.ask" :
+ newpin? "passphrase.pin.new.ask" :
+ isadmin? "passphrase.adminpin.ask" :
+ "passphrase.pin.ask",
+ newpin && isadmin? _("Enter New Admin PIN: ") :
+ newpin? _("Enter New PIN: ") :
+ isadmin? _("Enter Admin PIN: ")
+ : _("Enter PIN: "),
+ cacheid,
+ &canceled);
+ xfree (cacheid);
+ cacheid = NULL;
+ again_text = NULL;
+ if (!value && canceled)
+ return G10ERR_CANCELED;
+ else if (!value)
+ return G10ERR_GENERAL;
+
+ if (newpin)
+ {
+ char *value2;
+
+ value2 = ask_passphrase (info, NULL,
+ "passphrase.pin.repeat",
+ _("Repeat this PIN: "),
+ NULL,
+ &canceled);
+ if (!value2 && canceled)
+ {
+ xfree (value);
+ return G10ERR_CANCELED;
+ }
+ else if (!value2)
+ {
+ xfree (value);
+ return G10ERR_GENERAL;
+ }
+ if (strcmp (value, value2))
+ {
+ again_text = N_("PIN not correctly repeated; try again");
+ xfree (value2);
+ xfree (value);
+ value = NULL;
+ goto again;
+ }
+ xfree (value2);
+ }
+
+ *retstr = value;
+ return 0;
+}
+
+
+
+/* Send a SETATTR command to the SCdaemon. */
+int
+agent_scd_setattr (const char *name,
+ const unsigned char *value, size_t valuelen,
+ const char *serialno)
+{
+ app_t app;
+ int rc;
+ struct pincb_parm_s parm;
+
+ memset (&parm, 0, sizeof parm);
+ parm.sn = serialno;
+
+ app = current_app? current_app : open_card ();
+ if (!app)
+ return gpg_error (GPG_ERR_CARD);
+
+ if (app->assuan_ctx)
+ {
+ char line[ASSUAN_LINELENGTH];
+ char *p;
+
+ /* We assume that NAME does not need escaping. */
+ if (12 + strlen (name) > DIM(line)-1)
+ return gpg_error (GPG_ERR_CARD);
+ p = stpcpy (stpcpy (line, "SCD SETATTR "), name);
+ *p++ = ' ';
+ for (; valuelen; value++, valuelen--)
+ {
+ if (p >= line + DIM(line)-5 )
+ return gpg_error (GPG_ERR_CARD);
+ if (*value < ' ' || *value == '+' || *value == '%')
+ {
+ sprintf (p, "%%%02X", *value);
+ p += 3;
+ }
+ else if (*value == ' ')
+ *p++ = '+';
+ else
+ *p++ = *value;
+ }
+ *p = 0;
+
+ rc = test_transact (assuan_transact (app->assuan_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL),
+ "SCD SETATTR");
+ }
+ else
+ {
+ rc = app->fnc.setattr (app, name, pin_cb, &parm, value, valuelen);
+ }
+
+ status_sc_op_failure (rc);
+ return rc;
+}
+
+
+/* Handle a KEYDATA inquiry. Note, we only send the data,
+ assuan_transact takes care of flushing and writing the end */
+static assuan_error_t
+inq_writekey_parms (void *opaque, const char *keyword)
+{
+ struct writekey_parm_s *parm = opaque;
+
+ return assuan_send_data (parm->ctx, parm->keydata, parm->keydatalen);
+}
+
+
+/* Send a WRITEKEY command to the SCdaemon. */
+int
+agent_scd_writekey (int keyno, const char *serialno,
+ const unsigned char *keydata, size_t keydatalen)
+{
+ app_t app;
+ int rc;
+ char line[ASSUAN_LINELENGTH];
+ struct pincb_parm_s parm;
+
+ memset (&parm, 0, sizeof parm);
+ parm.sn = serialno;
+
+ app = current_app? current_app : open_card ();
+ if (!app)
+ return gpg_error (GPG_ERR_CARD);
+
+ if (app->assuan_ctx)
+ {
+ struct writekey_parm_s parms;
+
+ snprintf (line, DIM(line)-1, "SCD WRITEKEY --force OPENPGP.%d", keyno);
+ line[DIM(line)-1] = 0;
+ parms.ctx = app->assuan_ctx;
+ parms.keydata = keydata;
+ parms.keydatalen = keydatalen;
+ rc = test_transact (assuan_transact (app->assuan_ctx, line,
+ NULL, NULL,
+ inq_writekey_parms, &parms,
+ NULL, NULL),
+ "SCD WRITEKEY");
+ }
+ else
+ {
+ snprintf (line, DIM(line)-1, "OPENPGP.%d", keyno);
+ line[DIM(line)-1] = 0;
+ rc = app->fnc.writekey (app, NULL, line, 0x0001,
+ pin_cb, &parm,
+ keydata, keydatalen);
+ }
+
+ status_sc_op_failure (rc);
+ return rc;
+}
+
+
+
+static assuan_error_t
+genkey_status_cb (void *opaque, const char *line)
+{
+ struct agent_card_genkey_s *parm = opaque;
+ const char *keyword = line;
+ int keywordlen;
+
+/* log_debug ("got status line `%s'\n", line); */
+ for (keywordlen=0; *line && !spacep (line); line++, keywordlen++)
+ ;
+ while (spacep (line))
+ line++;
+
+ if (keywordlen == 7 && !memcmp (keyword, "KEY-FPR", keywordlen))
+ {
+ parm->fprvalid = unhexify_fpr (line, parm->fpr);
+ }
+ if (keywordlen == 8 && !memcmp (keyword, "KEY-DATA", keywordlen))
+ {
+ MPI a;
+ const char *name = line;
+ char *buf;
+
+ while (*line && !spacep (line))
+ line++;
+ while (spacep (line))
+ line++;
+
+ buf = xmalloc ( 2 + strlen (line) + 1);
+ strcpy (stpcpy (buf, "0x"), line);
+ a = mpi_alloc (300);
+ if( mpi_fromstr (a, buf) )
+ log_error ("error parsing received key data\n");
+ else if (*name == 'n' && spacep (name+1))
+ parm->n = a;
+ else if (*name == 'e' && spacep (name+1))
+ parm->e = a;
+ else
+ {
+ log_info ("unknown parameter name in received key data\n");
+ mpi_free (a);
+ }
+ xfree (buf);
+ }
+ else if (keywordlen == 14 && !memcmp (keyword,"KEY-CREATED-AT", keywordlen))
+ {
+ parm->created_at = (u32)strtoul (line, NULL, 10);
+ }
+
+ return 0;
+}
+
+/* Send a GENKEY command to the SCdaemon. */
+int
+agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force,
+ const char *serialno)
+{
+ app_t app;
+ char line[ASSUAN_LINELENGTH];
+ struct ctrl_ctx_s ctrl;
+ int rc;
+ struct pincb_parm_s parm;
+
+ memset (&parm, 0, sizeof parm);
+ parm.sn = serialno;
+
+ app = current_app? current_app : open_card ();
+ if (!app)
+ return gpg_error (GPG_ERR_CARD);
+
+ memset (info, 0, sizeof *info);
+
+ if (app->assuan_ctx)
+ {
+ snprintf (line, DIM(line)-1, "SCD GENKEY %s%d",
+ force? "--force ":"", keyno);
+ line[DIM(line)-1] = 0;
+ rc = test_transact (assuan_transact (app->assuan_ctx, line,
+ NULL, NULL, NULL, NULL,
+ genkey_status_cb, info),
+ "SCD GENKEY");
+ }
+ else
+ {
+ snprintf (line, DIM(line)-1, "%d", keyno);
+ ctrl.status_cb = genkey_status_cb;
+ ctrl.status_cb_arg = info;
+ rc = app->fnc.genkey (app, &ctrl, line,
+ force? 1:0,
+ pin_cb, &parm);
+ }
+
+ status_sc_op_failure (rc);
+ return rc;
+}
+
+
+static assuan_error_t
+membuf_data_cb (void *opaque, const void *buffer, size_t length)
+{
+ membuf_t *data = opaque;
+
+ if (buffer)
+ put_membuf (data, buffer, length);
+ return 0;
+}
+
+
+/* Send a PKSIGN command to the SCdaemon. */
+int
+agent_scd_pksign (const char *serialno, int hashalgo,
+ const unsigned char *indata, size_t indatalen,
+ unsigned char **r_buf, size_t *r_buflen)
+{
+ struct pincb_parm_s parm;
+ app_t app;
+ int rc;
+
+ *r_buf = NULL;
+ *r_buflen = 0;
+ memset (&parm, 0, sizeof parm);
+ parm.sn = serialno;
+ retry:
+ app = current_app? current_app : open_card ();
+ if (!app)
+ return gpg_error (GPG_ERR_CARD);
+
+ if (app->assuan_ctx)
+ {
+ char *p, line[ASSUAN_LINELENGTH];
+ membuf_t data;
+ size_t len;
+ int i;
+
+ if (indatalen*2 + 50 > DIM(line))
+ return gpg_error (GPG_ERR_GENERAL);
+
+ p = stpcpy (line, "SCD SETDATA ");
+ for (i=0; i < indatalen ; i++, p += 2 )
+ sprintf (p, "%02X", indata[i]);
+ rc = test_transact (assuan_transact (app->assuan_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL),
+ "SCD SETDATA");
+ if (!rc)
+ {
+ init_membuf (&data, 1024);
+ snprintf (line, DIM(line)-1, "SCD PKSIGN %s%s",
+ hashalgo == GCRY_MD_RMD160? "--hash=rmd160 ": "",
+ serialno);
+ line[DIM(line)-1] = 0;
+ rc = test_transact (assuan_transact (app->assuan_ctx, line,
+ membuf_data_cb, &data,
+ NULL, NULL, NULL, NULL),
+ "SCD PKSIGN");
+ if (rc)
+ xfree (get_membuf (&data, &len));
+ else
+ *r_buf = get_membuf (&data, r_buflen);
+ }
+ }
+ else
+ {
+ /* Check that the card's serialnumber is as required.*/
+ rc = check_card_serialno (app, serialno);
+ if (rc == -1)
+ goto retry;
+
+ if (!rc)
+ rc = app->fnc.sign (app, serialno, hashalgo,
+ pin_cb, &parm,
+ indata, indatalen,
+ r_buf, r_buflen);
+ }
+
+ if (rc)
+ {
+ status_sc_op_failure (rc);
+ if (!app->assuan_ctx)
+ agent_clear_pin_cache (serialno);
+ }
+ return rc;
+}
+
+
+/* Send a PKDECRYPT command to the SCdaemon. */
+int
+agent_scd_pkdecrypt (const char *serialno,
+ const unsigned char *indata, size_t indatalen,
+ unsigned char **r_buf, size_t *r_buflen)
+{
+ struct pincb_parm_s parm;
+ app_t app;
+ int rc;
+
+ *r_buf = NULL;
+ *r_buflen = 0;
+ memset (&parm, 0, sizeof parm);
+ parm.sn = serialno;
+ retry:
+ app = current_app? current_app : open_card ();
+ if (!app)
+ return gpg_error (GPG_ERR_CARD);
+
+ if (app->assuan_ctx)
+ {
+ char *p, line[ASSUAN_LINELENGTH];
+ membuf_t data;
+ size_t len;
+ int i;
+
+ if (indatalen*2 + 50 > DIM(line))
+ return gpg_error (GPG_ERR_GENERAL);
+
+ p = stpcpy (line, "SCD SETDATA ");
+ for (i=0; i < indatalen ; i++, p += 2 )
+ sprintf (p, "%02X", indata[i]);
+ rc = test_transact (assuan_transact (app->assuan_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL),
+ "SCD SETDATA");
+ if (!rc)
+ {
+ init_membuf (&data, 1024);
+ snprintf (line, DIM(line)-1, "SCD PKDECRYPT %s", serialno);
+ line[DIM(line)-1] = 0;
+ rc = test_transact (assuan_transact (app->assuan_ctx, line,
+ membuf_data_cb, &data,
+ NULL, NULL, NULL, NULL),
+ "SCD PKDECRYPT");
+ if (rc)
+ xfree (get_membuf (&data, &len));
+ else
+ *r_buf = get_membuf (&data, r_buflen);
+ }
+ }
+ else
+ {
+ /* Check that the card's serialnumber is as required.*/
+ rc = check_card_serialno (app, serialno);
+ if (rc == -1)
+ goto retry;
+
+ if (!rc)
+ rc = app->fnc.decipher (app, serialno,
+ pin_cb, &parm,
+ indata, indatalen,
+ r_buf, r_buflen);
+ }
+
+ if (rc)
+ {
+ status_sc_op_failure (rc);
+ if (!app->assuan_ctx)
+ agent_clear_pin_cache (serialno);
+ }
+ return rc;
+}
+
+/* Change the PIN of an OpenPGP card or reset the retry
+ counter. SERIALNO may be NULL or a hex string finally passed to the
+ passphrase callback. */
+int
+agent_scd_change_pin (int chvno, const char *serialno)
+{
+ app_t app;
+ int reset = 0;
+ int rc;
+ struct pincb_parm_s parm;
+
+ memset (&parm, 0, sizeof parm);
+ parm.sn = serialno;
+
+ reset = (chvno >= 100);
+ chvno %= 100;
+
+ app = current_app? current_app : open_card ();
+ if (!app)
+ return gpg_error (GPG_ERR_CARD);
+
+ if (app->assuan_ctx)
+ {
+ char line[ASSUAN_LINELENGTH];
+
+ snprintf (line, DIM(line)-1, "SCD PASSWD%s %d",
+ reset? " --reset":"", chvno);
+ line[DIM(line)-1] = 0;
+ rc = test_transact (assuan_transact (app->assuan_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL),
+ "SCD PASSWD");
+ }
+ else
+ {
+ char chvnostr[50];
+
+ sprintf (chvnostr, "%d", chvno);
+ rc = app->fnc.change_pin (app, NULL, chvnostr, reset,
+ pin_cb, &parm);
+ }
+
+ status_sc_op_failure (rc);
+ return rc;
+}
+
+/* Perform a CHECKPIN operation. SERIALNO should be the serial
+ number of the card - optionally followed by the fingerprint;
+ however the fingerprint is ignored here. */
+int
+agent_scd_checkpin (const char *serialnobuf)
+{
+ app_t app;
+ int rc;
+ struct pincb_parm_s parm;
+
+ memset (&parm, 0, sizeof parm);
+ parm.sn = serialnobuf;
+
+ app = current_app? current_app : open_card ();
+ if (!app)
+ return gpg_error (GPG_ERR_CARD);
+
+ if (app->assuan_ctx)
+ {
+ char line[ASSUAN_LINELENGTH];
+
+ if (15 + strlen (serialnobuf) > DIM(line)-1)
+ return gpg_error (GPG_ERR_CARD);
+ stpcpy (stpcpy (line, "SCD CHECKPIN "), serialnobuf);
+ rc = test_transact (assuan_transact (app->assuan_ctx, line,
+ NULL, NULL, NULL, NULL, NULL, NULL),
+ "SCD CHECKPIN");
+ }
+ else
+ {
+ rc = app->fnc.check_pin (app, serialnobuf, pin_cb, &parm);
+ }
+
+ status_sc_op_failure (rc);
+ return rc;
+}
+
+
+
+void
+agent_clear_pin_cache (const char *sn)
+{
+ char *cacheid = format_cacheid (sn);
+ if (cacheid)
+ {
+ passphrase_clear_cache (NULL, cacheid, 0);
+ xfree (cacheid);
+ }
+}
diff --git a/g10/cardglue.h b/g10/cardglue.h
new file mode 100644
index 0000000..b1f2811
--- /dev/null
+++ b/g10/cardglue.h
@@ -0,0 +1,211 @@
+/* cardglue.h - Divert operations to the agent
+ * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+#ifndef GNUPG_G10_CARDGLUE_H
+#define GNUPG_G10_CARDGLUE_H
+
+#ifdef ENABLE_CARD_SUPPORT
+/*
+ Note, that most card related code has been taken from 1.9.x branch
+ and is maintained over there if at all possible. Thus, if you make
+ changes here, please check that a similar change has been commited
+ to the 1.9.x branch.
+*/
+
+
+struct agent_card_info_s {
+ int error; /* private. */
+ char *serialno; /* malloced hex string. */
+ char *disp_name; /* malloced. */
+ char *disp_lang; /* malloced. */
+ int disp_sex; /* 0 = unspecified, 1 = male, 2 = female */
+ char *pubkey_url; /* malloced. */
+ char *login_data; /* malloced. */
+ char *private_do[4]; /* malloced. */
+ char cafpr1valid;
+ char cafpr2valid;
+ char cafpr3valid;
+ char cafpr1[20];
+ char cafpr2[20];
+ char cafpr3[20];
+ char fpr1valid;
+ char fpr2valid;
+ char fpr3valid;
+ char fpr1[20];
+ char fpr2[20];
+ char fpr3[20];
+ u32 fpr1time;
+ u32 fpr2time;
+ u32 fpr3time;
+ unsigned long sig_counter;
+ int chv1_cached; /* True if a PIN is not required for each
+ signing. Note that the gpg-agent might cache
+ it anyway. */
+ int chvmaxlen[3]; /* Maximum allowed length of a CHV. */
+ int chvretry[3]; /* Allowed retries for the CHV; 0 = blocked. */
+};
+
+struct agent_card_genkey_s {
+ char fprvalid;
+ char fpr[20];
+ u32 created_at;
+ MPI n;
+ MPI e;
+};
+
+
+struct app_ctx_s;
+struct ctrl_ctx_s;
+
+typedef struct app_ctx_s *APP; /* deprecated. */
+typedef struct app_ctx_s *app_t;
+typedef struct ctrl_ctx_s *CTRL; /* deprecated. */
+typedef struct ctrl_ctx_s *ctrl_t;
+
+
+#define GPG_ERR_GENERAL G10ERR_GENERAL
+#define GPG_ERR_BAD_PIN G10ERR_BAD_PASS
+#define GPG_ERR_BAD_KEY G10ERR_BAD_KEY
+#define GPG_ERR_CARD G10ERR_GENERAL
+#define GPG_ERR_EEXIST G10ERR_FILE_EXISTS
+#define GPG_ERR_ENOMEM G10ERR_RESOURCE_LIMIT
+#define GPG_ERR_GENERAL G10ERR_GENERAL
+#define GPG_ERR_HARDWARE G10ERR_GENERAL
+#define GPG_ERR_INV_CARD G10ERR_GENERAL
+#define GPG_ERR_INV_ID G10ERR_GENERAL
+#define GPG_ERR_INV_NAME G10ERR_GENERAL
+#define GPG_ERR_INV_VALUE G10ERR_INV_ARG
+#define GPG_ERR_INV_SEXP G10ERR_INV_ARG
+#define GPG_ERR_NOT_SUPPORTED G10ERR_UNSUPPORTED
+#define GPG_ERR_NO_OBJ G10ERR_GENERAL
+#define GPG_ERR_PIN_BLOCKED G10ERR_PASSPHRASE
+#define GPG_ERR_UNSUPPORTED_ALGORITHM G10ERR_PUBKEY_ALGO
+#define GPG_ERR_USE_CONDITIONS G10ERR_GENERAL
+#define GPG_ERR_WRONG_CARD G10ERR_GENERAL
+#define GPG_ERR_WRONG_SECKEY G10ERR_WRONG_SECKEY
+#define GPG_ERR_PIN_NOT_SYNCED G10ERR_GENERAL
+#define GPG_ERR_NOT_FOUND G10ERR_GENERAL
+#define GPG_ERR_BUG G10ERR_GENERAL
+#define GPG_ERR_NOT_IMPLEMENTED G10ERR_GENERAL
+#define GPG_ERR_BAD_BER G10ERR_GENERAL
+#define GPG_ERR_EOF (-1)
+#define GPG_ERR_CARD_NOT_PRESENT G10ERR_NO_CARD
+#define GPG_ERR_CARD_RESET G10ERR_GENERAL
+#define GPG_ERR_WRONG_PUBKEY_ALGO G10ERR_PUBKEY_ALGO
+#define GPG_ERR_UNKNOWN_SEXP G10ERR_INV_ARG
+#define GPG_ERR_DUP_VALUE G10ERR_INV_ARG
+#define GPG_ERR_BAD_SECKEY G10ERR_BAD_SECKEY
+#define GPG_ERR_TOO_LARGE G10ERR_GENERAL
+
+#define GPG_ERR_EBUSY G10ERR_GENERAL
+#define GPG_ERR_ENOENT G10ERR_OPEN_FILE
+#define GPG_ERR_EACCES G10ERR_UNSUPPORTED
+#define GPG_ERR_EIO G10ERR_GENERAL
+#define GPG_ERR_ENODEV G10ERR_GENERAL
+#define GPG_ERR_CANCELED G10ERR_CANCELED
+
+typedef int gpg_error_t;
+typedef int gpg_err_code_t;
+
+#define gpg_error(n) (n)
+#define gpg_err_code(n) (n)
+#define gpg_strerror(n) g10_errstr ((n))
+#define gpg_error_from_errno(n) (G10ERR_GENERAL) /*FIXME*/
+#define gpg_err_code_from_errno(n) (G10ERR_GENERAL)
+
+/* We are not using it in a library, so we even let xtrymalloc
+ abort. Because we won't never return from these malloc functions,
+ we also don't need the out_of_core function, we simply define it to
+ return -1 */
+#define xtrymalloc(n) xmalloc((n))
+#define xtrycalloc(n,m) xcalloc((n),(m))
+#define xtryrealloc(n,m) xrealloc((n),(m))
+#define xtrymalloc_secure(n) xmalloc_secure((n))
+#define out_of_core() (-1)
+
+#define gnupg_get_time() make_timestamp ()
+
+
+void card_set_reader_port (const char *portstr);
+
+char *serialno_and_fpr_from_sk (const unsigned char *sn, size_t snlen,
+ PKT_secret_key *sk);
+void send_status_info (ctrl_t ctrl, const char *keyword, ...);
+void gcry_md_hash_buffer (int algo, void *digest,
+ const void *buffer, size_t length);
+void log_printf (const char *fmt, ...);
+void log_printhex (const char *text, const void *buffer, size_t length);
+
+
+#define GCRY_MD_SHA1 DIGEST_ALGO_SHA1
+#define GCRY_MD_RMD160 DIGEST_ALGO_RMD160
+
+void card_close (void);
+
+
+/* Release the card info structure. */
+void agent_release_card_info (struct agent_card_info_s *info);
+
+/* Return card info. */
+int agent_learn (struct agent_card_info_s *info);
+
+/* Check whether the secret key for the key identified by HEXKEYGRIP
+ is available. Return 0 for yes or an error code. */
+int agent_havekey (const char *hexkeygrip);
+
+/* Return card info. */
+int agent_scd_getattr (const char *name, struct agent_card_info_s *info);
+
+/* Send a SETATTR command to the SCdaemon. */
+int agent_scd_setattr (const char *name,
+ const unsigned char *value, size_t valuelen,
+ const char *serialno);
+
+/* Send a WRITEKEY command to the SCdaemon. */
+int agent_scd_writekey (int keyno, const char *serialno,
+ const unsigned char *keydata, size_t keydatalen);
+
+/* Send a GENKEY command to the SCdaemon. */
+int agent_scd_genkey (struct agent_card_genkey_s *info, int keyno, int force,
+ const char *serialno);
+
+/* Send a PKSIGN command to the SCdaemon. */
+int agent_scd_pksign (const char *keyid, int hashalgo,
+ const unsigned char *indata, size_t indatalen,
+ unsigned char **r_buf, size_t *r_buflen);
+
+/* Send a PKDECRYPT command to the SCdaemon. */
+int agent_scd_pkdecrypt (const char *serialno,
+ const unsigned char *indata, size_t indatalen,
+ unsigned char **r_buf, size_t *r_buflen);
+
+/* Change the PIN of an OpenPGP card or reset the retry counter. */
+int agent_scd_change_pin (int chvno, const char *serialno);
+
+/* Send a CHECKPIN command. */
+int agent_scd_checkpin (const char *serialnobuf);
+
+/* Clear a cached PIN. */
+void agent_clear_pin_cache (const char *sn);
+
+
+#endif /*ENABLE_CARD_SUPPORT*/
+#endif /*GNUPG_G10_CARDGLUE_H*/
+
diff --git a/g10/ccid-driver.c b/g10/ccid-driver.c
new file mode 100644
index 0000000..da8edb0
--- /dev/null
+++ b/g10/ccid-driver.c
@@ -0,0 +1,2745 @@
+/* ccid-driver.c - USB ChipCardInterfaceDevices driver
+ * Copyright (C) 2003, 2004, 2005, 2006 Free Software Foundation, Inc.
+ * Written by Werner Koch.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ * ALTERNATIVELY, this file may be distributed under the terms of the
+ * following license, in which case the provisions of this license are
+ * required INSTEAD OF the GNU General Public License. If you wish to
+ * allow use of your version of this file only under the terms of the
+ * GNU General Public License, and not to allow others to use your
+ * version of this file under the terms of the following license,
+ * indicate your decision by deleting this paragraph and the license
+ * below.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Date: 2006-02-14 17:28:34 +0100 (Tue, 14 Feb 2006) $
+ */
+
+
+/* CCID (ChipCardInterfaceDevices) is a specification for accessing
+ smartcard via a reader connected to the USB.
+
+ This is a limited driver allowing to use some CCID drivers directly
+ without any other specila drivers. This is a fallback driver to be
+ used when nothing else works or the system should be kept minimal
+ for security reasons. It makes use of the libusb library to gain
+ portable access to USB.
+
+ This driver has been tested with the SCM SCR335 and SPR532
+ smartcard readers and requires that a reader implements APDU or
+ TPDU level exchange and does fully automatic initialization.
+*/
+
+#ifdef HAVE_CONFIG_H
+# include <config.h>
+#endif
+
+#if defined(HAVE_LIBUSB) || defined(TEST)
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include <usb.h>
+
+#include "ccid-driver.h"
+
+#define DRVNAME "ccid-driver: "
+
+
+/* Depending on how this source is used we either define our error
+ output to go to stderr or to the jnlib based logging functions. We
+ use the latter when GNUPG_MAJOR_VERSION is defines or when both,
+ GNUPG_SCD_MAIN_HEADER and HAVE_JNLIB_LOGGING are defined.
+*/
+#if defined(GNUPG_MAJOR_VERSION) \
+ || (defined(GNUPG_SCD_MAIN_HEADER) && defined(HAVE_JNLIB_LOGGING))
+
+#if defined(GNUPG_SCD_MAIN_HEADER)
+# include GNUPG_SCD_MAIN_HEADER
+#elif GNUPG_MAJOR_VERSION == 1 /* GnuPG Version is < 1.9. */
+# include "options.h"
+# include "util.h"
+# include "memory.h"
+# include "cardglue.h"
+# else /* This is the modularized GnuPG 1.9 or later. */
+# include "scdaemon.h"
+#endif
+
+
+# define DEBUGOUT(t) do { if (debug_level) \
+ log_debug (DRVNAME t); } while (0)
+# define DEBUGOUT_1(t,a) do { if (debug_level) \
+ log_debug (DRVNAME t,(a)); } while (0)
+# define DEBUGOUT_2(t,a,b) do { if (debug_level) \
+ log_debug (DRVNAME t,(a),(b)); } while (0)
+# define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \
+ log_debug (DRVNAME t,(a),(b),(c));} while (0)
+# define DEBUGOUT_4(t,a,b,c,d) do { if (debug_level) \
+ log_debug (DRVNAME t,(a),(b),(c),(d));} while (0)
+# define DEBUGOUT_CONT(t) do { if (debug_level) \
+ log_printf (t); } while (0)
+# define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \
+ log_printf (t,(a)); } while (0)
+# define DEBUGOUT_CONT_2(t,a,b) do { if (debug_level) \
+ log_printf (t,(a),(b)); } while (0)
+# define DEBUGOUT_CONT_3(t,a,b,c) do { if (debug_level) \
+ log_printf (t,(a),(b),(c)); } while (0)
+# define DEBUGOUT_LF() do { if (debug_level) \
+ log_printf ("\n"); } while (0)
+
+#else /* Other usage of this source - don't use gnupg specifics. */
+
+# define DEBUGOUT(t) do { if (debug_level) \
+ fprintf (stderr, DRVNAME t); } while (0)
+# define DEBUGOUT_1(t,a) do { if (debug_level) \
+ fprintf (stderr, DRVNAME t, (a)); } while (0)
+# define DEBUGOUT_2(t,a,b) do { if (debug_level) \
+ fprintf (stderr, DRVNAME t, (a), (b)); } while (0)
+# define DEBUGOUT_3(t,a,b,c) do { if (debug_level) \
+ fprintf (stderr, DRVNAME t, (a), (b), (c)); } while (0)
+# define DEBUGOUT_4(t,a,b,c,d) do { if (debug_level) \
+ fprintf (stderr, DRVNAME t, (a), (b), (c), (d));} while(0)
+# define DEBUGOUT_CONT(t) do { if (debug_level) \
+ fprintf (stderr, t); } while (0)
+# define DEBUGOUT_CONT_1(t,a) do { if (debug_level) \
+ fprintf (stderr, t, (a)); } while (0)
+# define DEBUGOUT_CONT_2(t,a,b) do { if (debug_level) \
+ fprintf (stderr, t, (a), (b)); } while (0)
+# define DEBUGOUT_CONT_3(t,a,b,c) do { if (debug_level) \
+ fprintf (stderr, t, (a), (b), (c)); } while (0)
+# define DEBUGOUT_LF() do { if (debug_level) \
+ putc ('\n', stderr); } while (0)
+
+#endif /* This source not used by scdaemon. */
+
+
+
+enum {
+ RDR_to_PC_NotifySlotChange= 0x50,
+ RDR_to_PC_HardwareError = 0x51,
+
+ PC_to_RDR_SetParameters = 0x61,
+ PC_to_RDR_IccPowerOn = 0x62,
+ PC_to_RDR_IccPowerOff = 0x63,
+ PC_to_RDR_GetSlotStatus = 0x65,
+ PC_to_RDR_Secure = 0x69,
+ PC_to_RDR_T0APDU = 0x6a,
+ PC_to_RDR_Escape = 0x6b,
+ PC_to_RDR_GetParameters = 0x6c,
+ PC_to_RDR_ResetParameters = 0x6d,
+ PC_to_RDR_IccClock = 0x6e,
+ PC_to_RDR_XfrBlock = 0x6f,
+ PC_to_RDR_Mechanical = 0x71,
+ PC_to_RDR_Abort = 0x72,
+ PC_to_RDR_SetDataRate = 0x73,
+
+ RDR_to_PC_DataBlock = 0x80,
+ RDR_to_PC_SlotStatus = 0x81,
+ RDR_to_PC_Parameters = 0x82,
+ RDR_to_PC_Escape = 0x83,
+ RDR_to_PC_DataRate = 0x84
+};
+
+
+/* Two macro to detect whether a CCID command has failed and to get
+ the error code. These macros assume that we can access the
+ mandatory first 10 bytes of a CCID message in BUF. */
+#define CCID_COMMAND_FAILED(buf) ((buf)[7] & 0x40)
+#define CCID_ERROR_CODE(buf) (((unsigned char *)(buf))[8])
+
+
+/* We need to know the vendor to do some hacks. */
+enum {
+ VENDOR_CHERRY = 0x046a,
+ VENDOR_SCM = 0x04e6,
+ VENDOR_OMNIKEY= 0x076b,
+ VENDOR_GEMPC = 0x08e6
+};
+
+/* A list and a table with special transport descriptions. */
+enum {
+ TRANSPORT_USB = 0, /* Standard USB transport. */
+ TRANSPORT_CM4040 = 1 /* As used by the Cardman 4040. */
+};
+
+static struct
+{
+ char *name; /* Device name. */
+ int type;
+
+} transports[] = {
+ { "/dev/cmx0", TRANSPORT_CM4040 },
+ { "/dev/cmx1", TRANSPORT_CM4040 },
+ { NULL },
+};
+
+
+/* Store information on the driver's state. A pointer to such a
+ structure is used as handle for most functions. */
+struct ccid_driver_s
+{
+ usb_dev_handle *idev;
+ char *rid;
+ int dev_fd; /* -1 for USB transport or file descriptor of the
+ transport device. */
+ unsigned short id_vendor;
+ unsigned short id_product;
+ unsigned short bcd_device;
+ int ifc_no;
+ int ep_bulk_out;
+ int ep_bulk_in;
+ int ep_intr;
+ int seqno;
+ unsigned char t1_ns;
+ unsigned char t1_nr;
+ int nonnull_nad;
+ int auto_ifsd;
+ int max_ifsd;
+ int ifsd;
+ int powered_off;
+ int has_pinpad;
+ int apdu_level; /* Reader supports short APDU level exchange. */
+};
+
+
+static int initialized_usb; /* Tracks whether USB has been initialized. */
+static int debug_level; /* Flag to control the debug output.
+ 0 = No debugging
+ 1 = USB I/O info
+ 2 = T=1 protocol tracing
+ */
+
+
+static unsigned int compute_edc (const unsigned char *data, size_t datalen,
+ int use_crc);
+static int bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen);
+static int bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
+ size_t *nread, int expected_type, int seqno, int timeout,
+ int no_debug);
+
+/* Convert a little endian stored 4 byte value into an unsigned
+ integer. */
+static unsigned int
+convert_le_u32 (const unsigned char *buf)
+{
+ return buf[0] | (buf[1] << 8) | (buf[2] << 16) | (buf[3] << 24);
+}
+
+static void
+set_msg_len (unsigned char *msg, unsigned int length)
+{
+ msg[1] = length;
+ msg[2] = length >> 8;
+ msg[3] = length >> 16;
+ msg[4] = length >> 24;
+}
+
+
+/* Pint an error message for a failed CCID command including a textual
+ error code. MSG is shall be the CCID message of at least 10 bytes. */
+static void
+print_command_failed (const unsigned char *msg)
+{
+ const char *t;
+ char buffer[100];
+ int ec;
+
+ if (!debug_level)
+ return;
+
+ ec = CCID_ERROR_CODE (msg);
+ switch (ec)
+ {
+ case 0x00: t = "Command not supported"; break;
+
+ case 0xE0: t = "Slot busy"; break;
+ case 0xEF: t = "PIN cancelled"; break;
+ case 0xF0: t = "PIN timeout"; break;
+
+ case 0xF2: t = "Automatic sequence ongoing"; break;
+ case 0xF3: t = "Deactivated Protocol"; break;
+ case 0xF4: t = "Procedure byte conflict"; break;
+ case 0xF5: t = "ICC class not supported"; break;
+ case 0xF6: t = "ICC protocol not supported"; break;
+ case 0xF7: t = "Bad checksum in ATR"; break;
+ case 0xF8: t = "Bad TS in ATR"; break;
+
+ case 0xFB: t = "An all inclusive hardware error occurred"; break;
+ case 0xFC: t = "Overrun error while talking to the ICC"; break;
+ case 0xFD: t = "Parity error while talking to the ICC"; break;
+ case 0xFE: t = "CCID timed out while talking to the ICC"; break;
+ case 0xFF: t = "Host aborted the current activity"; break;
+
+ default:
+ if (ec > 0 && ec < 128)
+ sprintf (buffer, "Parameter error at offset %d", ec);
+ else
+ sprintf (buffer, "Error code %02X", ec);
+ t = buffer;
+ break;
+ }
+ DEBUGOUT_1 ("CCID command failed: %s\n", t);
+}
+
+
+/* Given a handle used for special transport prepare it for use. In
+ particular setup all information in way that resembles what
+ parse_cccid_descriptor does. */
+static void
+prepare_special_transport (ccid_driver_t handle)
+{
+ assert (!handle->id_vendor);
+
+ handle->nonnull_nad = 0;
+ handle->auto_ifsd = 0;
+ handle->max_ifsd = 32;
+ handle->ifsd = 0;
+ handle->has_pinpad = 0;
+ handle->apdu_level = 0;
+ switch (handle->id_product)
+ {
+ case TRANSPORT_CM4040:
+ DEBUGOUT ("setting up transport for CardMan 4040\n");
+ handle->apdu_level = 1;
+ break;
+
+ default: assert (!"transport not defined");
+ }
+}
+
+/* Parse a CCID descriptor, optionally print all available features
+ and test whether this reader is usable by this driver. Returns 0
+ if it is usable.
+
+ Note, that this code is based on the one in lsusb.c of the
+ usb-utils package, I wrote on 2003-09-01. -wk. */
+static int
+parse_ccid_descriptor (ccid_driver_t handle,
+ const unsigned char *buf, size_t buflen)
+{
+ unsigned int i;
+ unsigned int us;
+ int have_t1 = 0, have_tpdu=0, have_auto_conf = 0;
+
+
+ handle->nonnull_nad = 0;
+ handle->auto_ifsd = 0;
+ handle->max_ifsd = 32;
+ handle->ifsd = 0;
+ handle->has_pinpad = 0;
+ handle->apdu_level = 0;
+ DEBUGOUT_3 ("idVendor: %04X idProduct: %04X bcdDevice: %04X\n",
+ handle->id_vendor, handle->id_product, handle->bcd_device);
+ if (buflen < 54 || buf[0] < 54)
+ {
+ DEBUGOUT ("CCID device descriptor is too short\n");
+ return -1;
+ }
+
+ DEBUGOUT ("ChipCard Interface Descriptor:\n");
+ DEBUGOUT_1 (" bLength %5u\n", buf[0]);
+ DEBUGOUT_1 (" bDescriptorType %5u\n", buf[1]);
+ DEBUGOUT_2 (" bcdCCID %2x.%02x", buf[3], buf[2]);
+ if (buf[3] != 1 || buf[2] != 0)
+ DEBUGOUT_CONT(" (Warning: Only accurate for version 1.0)");
+ DEBUGOUT_LF ();
+
+ DEBUGOUT_1 (" nMaxSlotIndex %5u\n", buf[4]);
+ DEBUGOUT_2 (" bVoltageSupport %5u %s\n",
+ buf[5], (buf[5] == 1? "5.0V" : buf[5] == 2? "3.0V"
+ : buf[5] == 3? "1.8V":"?"));
+
+ us = convert_le_u32 (buf+6);
+ DEBUGOUT_1 (" dwProtocols %5u ", us);
+ if ((us & 1))
+ DEBUGOUT_CONT (" T=0");
+ if ((us & 2))
+ {
+ DEBUGOUT_CONT (" T=1");
+ have_t1 = 1;
+ }
+ if ((us & ~3))
+ DEBUGOUT_CONT (" (Invalid values detected)");
+ DEBUGOUT_LF ();
+
+ us = convert_le_u32(buf+10);
+ DEBUGOUT_1 (" dwDefaultClock %5u\n", us);
+ us = convert_le_u32(buf+14);
+ DEBUGOUT_1 (" dwMaxiumumClock %5u\n", us);
+ DEBUGOUT_1 (" bNumClockSupported %5u\n", buf[18]);
+ us = convert_le_u32(buf+19);
+ DEBUGOUT_1 (" dwDataRate %7u bps\n", us);
+ us = convert_le_u32(buf+23);
+ DEBUGOUT_1 (" dwMaxDataRate %7u bps\n", us);
+ DEBUGOUT_1 (" bNumDataRatesSupp. %5u\n", buf[27]);
+
+ us = convert_le_u32(buf+28);
+ DEBUGOUT_1 (" dwMaxIFSD %5u\n", us);
+ handle->max_ifsd = us;
+
+ us = convert_le_u32(buf+32);
+ DEBUGOUT_1 (" dwSyncProtocols %08X ", us);
+ if ((us&1))
+ DEBUGOUT_CONT ( " 2-wire");
+ if ((us&2))
+ DEBUGOUT_CONT ( " 3-wire");
+ if ((us&4))
+ DEBUGOUT_CONT ( " I2C");
+ DEBUGOUT_LF ();
+
+ us = convert_le_u32(buf+36);
+ DEBUGOUT_1 (" dwMechanical %08X ", us);
+ if ((us & 1))
+ DEBUGOUT_CONT (" accept");
+ if ((us & 2))
+ DEBUGOUT_CONT (" eject");
+ if ((us & 4))
+ DEBUGOUT_CONT (" capture");
+ if ((us & 8))
+ DEBUGOUT_CONT (" lock");
+ DEBUGOUT_LF ();
+
+ us = convert_le_u32(buf+40);
+ DEBUGOUT_1 (" dwFeatures %08X\n", us);
+ if ((us & 0x0002))
+ {
+ DEBUGOUT (" Auto configuration based on ATR\n");
+ have_auto_conf = 1;
+ }
+ if ((us & 0x0004))
+ DEBUGOUT (" Auto activation on insert\n");
+ if ((us & 0x0008))
+ DEBUGOUT (" Auto voltage selection\n");
+ if ((us & 0x0010))
+ DEBUGOUT (" Auto clock change\n");
+ if ((us & 0x0020))
+ DEBUGOUT (" Auto baud rate change\n");
+ if ((us & 0x0040))
+ DEBUGOUT (" Auto parameter negotation made by CCID\n");
+ else if ((us & 0x0080))
+ DEBUGOUT (" Auto PPS made by CCID\n");
+ else if ((us & (0x0040 | 0x0080)))
+ DEBUGOUT (" WARNING: conflicting negotation features\n");
+
+ if ((us & 0x0100))
+ DEBUGOUT (" CCID can set ICC in clock stop mode\n");
+ if ((us & 0x0200))
+ {
+ DEBUGOUT (" NAD value other than 0x00 accepted\n");
+ handle->nonnull_nad = 1;
+ }
+ if ((us & 0x0400))
+ {
+ DEBUGOUT (" Auto IFSD exchange\n");
+ handle->auto_ifsd = 1;
+ }
+
+ if ((us & 0x00010000))
+ {
+ DEBUGOUT (" TPDU level exchange\n");
+ have_tpdu = 1;
+ }
+ else if ((us & 0x00020000))
+ {
+ DEBUGOUT (" Short APDU level exchange\n");
+ handle->apdu_level = 1;
+ }
+ else if ((us & 0x00040000))
+ {
+ DEBUGOUT (" Short and extended APDU level exchange\n");
+ handle->apdu_level = 1;
+ }
+ else if ((us & 0x00070000))
+ DEBUGOUT (" WARNING: conflicting exchange levels\n");
+
+ us = convert_le_u32(buf+44);
+ DEBUGOUT_1 (" dwMaxCCIDMsgLen %5u\n", us);
+
+ DEBUGOUT ( " bClassGetResponse ");
+ if (buf[48] == 0xff)
+ DEBUGOUT_CONT ("echo\n");
+ else
+ DEBUGOUT_CONT_1 (" %02X\n", buf[48]);
+
+ DEBUGOUT ( " bClassEnvelope ");
+ if (buf[49] == 0xff)
+ DEBUGOUT_CONT ("echo\n");
+ else
+ DEBUGOUT_CONT_1 (" %02X\n", buf[48]);
+
+ DEBUGOUT ( " wlcdLayout ");
+ if (!buf[50] && !buf[51])
+ DEBUGOUT_CONT ("none\n");
+ else
+ DEBUGOUT_CONT_2 ("%u cols %u lines\n", buf[50], buf[51]);
+
+ DEBUGOUT_1 (" bPINSupport %5u ", buf[52]);
+ if ((buf[52] & 1))
+ {
+ DEBUGOUT_CONT ( " verification");
+ handle->has_pinpad |= 1;
+ }
+ if ((buf[52] & 2))
+ {
+ DEBUGOUT_CONT ( " modification");
+ handle->has_pinpad |= 2;
+ }
+ DEBUGOUT_LF ();
+
+ DEBUGOUT_1 (" bMaxCCIDBusySlots %5u\n", buf[53]);
+
+ if (buf[0] > 54) {
+ DEBUGOUT (" junk ");
+ for (i=54; i < buf[0]-54; i++)
+ DEBUGOUT_CONT_1 (" %02X", buf[i]);
+ DEBUGOUT_LF ();
+ }
+
+ if (!have_t1 || !(have_tpdu || handle->apdu_level) || !have_auto_conf)
+ {
+ DEBUGOUT ("this drivers requires that the reader supports T=1, "
+ "TPDU or APDU level exchange and auto configuration - "
+ "this is not available\n");
+ return -1;
+ }
+
+
+ /* SCM drivers get stuck in their internal USB stack if they try to
+ send a frame of n*wMaxPacketSize back to us. Given that
+ wMaxPacketSize is 64 for these readers we set the IFSD to a value
+ lower than that:
+ 64 - 10 CCID header - 4 T1frame - 2 reserved = 48
+ Product Ids:
+ 0xe001 - SCR 331
+ 0x5111 - SCR 331-DI
+ 0x5115 - SCR 335
+ 0xe003 - SPR 532
+ */
+ if (handle->id_vendor == VENDOR_SCM
+ && handle->max_ifsd > 48
+ && ( (handle->id_product == 0xe001 && handle->bcd_device < 0x0516)
+ ||(handle->id_product == 0x5111 && handle->bcd_device < 0x0620)
+ ||(handle->id_product == 0x5115 && handle->bcd_device < 0x0514)
+ ||(handle->id_product == 0xe003 && handle->bcd_device < 0x0504)
+ ))
+ {
+ DEBUGOUT ("enabling workaround for buggy SCM readers\n");
+ handle->max_ifsd = 48;
+ }
+
+
+ return 0;
+}
+
+
+static char *
+get_escaped_usb_string (usb_dev_handle *idev, int idx,
+ const char *prefix, const char *suffix)
+{
+ int rc;
+ unsigned char buf[280];
+ unsigned char *s;
+ unsigned int langid;
+ size_t i, n, len;
+ char *result;
+
+ if (!idx)
+ return NULL;
+
+ /* Fixme: The next line is for the current Valgrid without support
+ for USB IOCTLs. */
+ memset (buf, 0, sizeof buf);
+
+ /* First get the list of supported languages and use the first one.
+ If we do don't find it we try to use English. Note that this is
+ all in a 2 bute Unicode encoding using little endian. */
+ rc = usb_control_msg (idev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR,
+ (USB_DT_STRING << 8), 0,
+ (char*)buf, sizeof buf, 1000 /* ms timeout */);
+ if (rc < 4)
+ langid = 0x0409; /* English. */
+ else
+ langid = (buf[3] << 8) | buf[2];
+
+ rc = usb_control_msg (idev, USB_ENDPOINT_IN, USB_REQ_GET_DESCRIPTOR,
+ (USB_DT_STRING << 8) + idx, langid,
+ (char*)buf, sizeof buf, 1000 /* ms timeout */);
+ if (rc < 2 || buf[1] != USB_DT_STRING)
+ return NULL; /* Error or not a string. */
+ len = buf[0];
+ if (len > rc)
+ return NULL; /* Larger than our buffer. */
+
+ for (s=buf+2, i=2, n=0; i+1 < len; i += 2, s += 2)
+ {
+ if (s[1])
+ n++; /* High byte set. */
+ else if (*s <= 0x20 || *s >= 0x7f || *s == '%' || *s == ':')
+ n += 3 ;
+ else
+ n++;
+ }
+
+ result = malloc (strlen (prefix) + n + strlen (suffix) + 1);
+ if (!result)
+ return NULL;
+
+ strcpy (result, prefix);
+ n = strlen (prefix);
+ for (s=buf+2, i=2; i+1 < len; i += 2, s += 2)
+ {
+ if (s[1])
+ result[n++] = '\xff'; /* High byte set. */
+ else if (*s <= 0x20 || *s >= 0x7f || *s == '%' || *s == ':')
+ {
+ sprintf (result+n, "%%%02X", *s);
+ n += 3;
+ }
+ else
+ result[n++] = *s;
+ }
+ strcpy (result+n, suffix);
+
+ return result;
+}
+
+/* This function creates an reader id to be used to find the same
+ physical reader after a reset. It returns an allocated and possibly
+ percent escaped string or NULL if not enough memory is available. */
+static char *
+make_reader_id (usb_dev_handle *idev,
+ unsigned int vendor, unsigned int product,
+ unsigned char serialno_index)
+{
+ char *rid;
+ char prefix[20];
+
+ sprintf (prefix, "%04X:%04X:", (vendor & 0xffff), (product & 0xffff));
+ rid = get_escaped_usb_string (idev, serialno_index, prefix, ":0");
+ if (!rid)
+ {
+ rid = malloc (strlen (prefix) + 3 + 1);
+ if (!rid)
+ return NULL;
+ strcpy (rid, prefix);
+ strcat (rid, "X:0");
+ }
+ return rid;
+}
+
+
+/* Helper to find the endpoint from an interface descriptor. */
+static int
+find_endpoint (struct usb_interface_descriptor *ifcdesc, int mode)
+{
+ int no;
+ int want_bulk_in = 0;
+
+ if (mode == 1)
+ want_bulk_in = 0x80;
+ for (no=0; no < ifcdesc->bNumEndpoints; no++)
+ {
+ struct usb_endpoint_descriptor *ep = ifcdesc->endpoint + no;
+ if (ep->bDescriptorType != USB_DT_ENDPOINT)
+ ;
+ else if (mode == 2
+ && ((ep->bmAttributes & USB_ENDPOINT_TYPE_MASK)
+ == USB_ENDPOINT_TYPE_INTERRUPT)
+ && (ep->bEndpointAddress & 0x80))
+ return (ep->bEndpointAddress & 0x0f);
+ else if (((ep->bmAttributes & USB_ENDPOINT_TYPE_MASK)
+ == USB_ENDPOINT_TYPE_BULK)
+ && (ep->bEndpointAddress & 0x80) == want_bulk_in)
+ return (ep->bEndpointAddress & 0x0f);
+ }
+ /* Should never happen. */
+ return mode == 2? 0x83 : mode == 1? 0x82 :1;
+}
+
+
+/* Helper for scan_or_find_devices. This function returns true if a
+ requested device has been found or the caller should stop scanning
+ for other reasons. */
+static int
+scan_or_find_usb_device (int scan_mode,
+ int *readerno, int *count, char **rid_list,
+ const char *readerid,
+ struct usb_device *dev,
+ char **r_rid,
+ struct usb_device **r_dev,
+ usb_dev_handle **r_idev,
+ unsigned char **ifcdesc_extra,
+ size_t *ifcdesc_extra_len,
+ int *interface_number,
+ int *ep_bulk_out, int *ep_bulk_in, int *ep_intr)
+{
+ int cfg_no;
+ int ifc_no;
+ int set_no;
+ struct usb_config_descriptor *config;
+ struct usb_interface *interface;
+ struct usb_interface_descriptor *ifcdesc;
+ char *rid;
+ usb_dev_handle *idev;
+
+ *r_idev = NULL;
+
+ for (cfg_no=0; cfg_no < dev->descriptor.bNumConfigurations; cfg_no++)
+ {
+ config = dev->config + cfg_no;
+ if(!config)
+ continue;
+
+ for (ifc_no=0; ifc_no < config->bNumInterfaces; ifc_no++)
+ {
+ interface = config->interface + ifc_no;
+ if (!interface)
+ continue;
+
+ for (set_no=0; set_no < interface->num_altsetting; set_no++)
+ {
+ ifcdesc = (interface->altsetting + set_no);
+ /* The second condition is for older SCM SPR 532 who did
+ not know about the assigned CCID class. Instead of
+ trying to interpret the strings we simply check the
+ product ID. */
+ if (ifcdesc && ifcdesc->extra
+ && ((ifcdesc->bInterfaceClass == 11
+ && ifcdesc->bInterfaceSubClass == 0
+ && ifcdesc->bInterfaceProtocol == 0)
+ || (ifcdesc->bInterfaceClass == 255
+ && dev->descriptor.idVendor == VENDOR_SCM
+ && dev->descriptor.idProduct == 0xe003)))
+ {
+ idev = usb_open (dev);
+ if (!idev)
+ {
+ DEBUGOUT_1 ("usb_open failed: %s\n",
+ strerror (errno));
+ continue; /* with next setting. */
+ }
+
+ rid = make_reader_id (idev,
+ dev->descriptor.idVendor,
+ dev->descriptor.idProduct,
+ dev->descriptor.iSerialNumber);
+ if (rid)
+ {
+ if (scan_mode)
+ {
+ char *p;
+
+ /* We are collecting infos about all
+ available CCID readers. Store them and
+ continue. */
+ DEBUGOUT_2 ("found CCID reader %d (ID=%s)\n",
+ *count, rid );
+ p = malloc ((*rid_list? strlen (*rid_list):0) + 1
+ + strlen (rid) + 1);
+ if (p)
+ {
+ *p = 0;
+ if (*rid_list)
+ {
+ strcat (p, *rid_list);
+ free (*rid_list);
+ }
+ strcat (p, rid);
+ strcat (p, "\n");
+ *rid_list = p;
+ }
+ else /* Out of memory. */
+ free (rid);
+
+ rid = NULL;
+ ++*count;
+ }
+ else if (!*readerno
+ || (*readerno < 0
+ && readerid
+ && !strcmp (readerid, rid)))
+ {
+ /* We found the requested reader. */
+ if (ifcdesc_extra && ifcdesc_extra_len)
+ {
+ *ifcdesc_extra = malloc (ifcdesc
+ ->extralen);
+ if (!*ifcdesc_extra)
+ {
+ usb_close (idev);
+ free (rid);
+ return 1; /* Out of core. */
+ }
+ memcpy (*ifcdesc_extra, ifcdesc->extra,
+ ifcdesc->extralen);
+ *ifcdesc_extra_len = ifcdesc->extralen;
+ }
+
+ if (interface_number)
+ *interface_number = (ifcdesc->bInterfaceNumber);
+
+ if (ep_bulk_out)
+ *ep_bulk_out = find_endpoint (ifcdesc, 0);
+ if (ep_bulk_in)
+ *ep_bulk_in = find_endpoint (ifcdesc, 1);
+ if (ep_intr)
+ *ep_intr = find_endpoint (ifcdesc, 2);
+
+ if (r_dev)
+ *r_dev = dev;
+ if (r_rid)
+ {
+ *r_rid = rid;
+ rid = NULL;
+ }
+ else
+ free (rid);
+
+ *r_idev = idev;
+ return 1; /* Found requested device. */
+ }
+ else
+ {
+ /* This is not yet the reader we want.
+ fixme: We should avoid the extra usb_open
+ in this case. */
+ if (*readerno >= 0)
+ --*readerno;
+ }
+ free (rid);
+ }
+
+ usb_close (idev);
+ idev = NULL;
+ return 0;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
+/* Combination function to either scan all CCID devices or to find and
+ open one specific device.
+
+ The function returns 0 if a reader has been found or when a scan
+ returned without error.
+
+ With READERNO = -1 and READERID is NULL, scan mode is used and
+ R_RID should be the address where to store the list of reader_ids
+ we found. If on return this list is empty, no CCID device has been
+ found; otherwise it points to an allocated linked list of reader
+ IDs. Note that in this mode the function always returns NULL.
+
+ With READERNO >= 0 or READERID is not NULL find mode is used. This
+ uses the same algorithm as the scan mode but stops and returns at
+ the entry number READERNO and return the handle for the the opened
+ USB device. If R_RID is not NULL it will receive the reader ID of
+ that device. If R_DEV is not NULL it will the device pointer of
+ that device. If IFCDESC_EXTRA is NOT NULL it will receive a
+ malloced copy of the interfaces "extra: data filed;
+ IFCDESC_EXTRA_LEN receive the length of this field. If there is
+ no reader with number READERNO or that reader is not usable by our
+ implementation NULL will be returned. The caller must close a
+ returned USB device handle and free (if not passed as NULL) the
+ returned reader ID info as well as the IFCDESC_EXTRA. On error
+ NULL will get stored at R_RID, R_DEV, IFCDESC_EXTRA and
+ IFCDESC_EXTRA_LEN. With READERID being -1 the function stops if
+ the READERID was found.
+
+ If R_FD is not -1 on return the device is not using USB for
+ transport but the device associated with that file descriptor. In
+ this case INTERFACE will receive the transport type and the other
+ USB specific return values are not used; the return value is
+ (void*)(1).
+
+ Note that the first entry of the returned reader ID list in scan mode
+ corresponds with a READERNO of 0 in find mode.
+*/
+static int
+scan_or_find_devices (int readerno, const char *readerid,
+ char **r_rid,
+ struct usb_device **r_dev,
+ unsigned char **ifcdesc_extra,
+ size_t *ifcdesc_extra_len,
+ int *interface_number,
+ int *ep_bulk_out, int *ep_bulk_in, int *ep_intr,
+ usb_dev_handle **r_idev,
+ int *r_fd)
+{
+ char *rid_list = NULL;
+ int count = 0;
+ struct usb_bus *busses, *bus;
+ struct usb_device *dev = NULL;
+ usb_dev_handle *idev = NULL;
+ int scan_mode = (readerno == -1 && !readerid);
+ int i;
+
+ /* Set return values to a default. */
+ if (r_rid)
+ *r_rid = NULL;
+ if (r_dev)
+ *r_dev = NULL;
+ if (ifcdesc_extra)
+ *ifcdesc_extra = NULL;
+ if (ifcdesc_extra_len)
+ *ifcdesc_extra_len = 0;
+ if (interface_number)
+ *interface_number = 0;
+ if (r_idev)
+ *r_idev = NULL;
+ if (r_fd)
+ *r_fd = -1;
+
+ /* See whether we want scan or find mode. */
+ if (scan_mode)
+ {
+ assert (r_rid);
+ }
+
+ usb_find_busses();
+ usb_find_devices();
+
+#ifdef HAVE_USB_GET_BUSSES
+ busses = usb_get_busses();
+#else
+ busses = usb_busses;
+#endif
+
+ for (bus = busses; bus; bus = bus->next)
+ {
+ for (dev = bus->devices; dev; dev = dev->next)
+ {
+ if (scan_or_find_usb_device (scan_mode, &readerno, &count, &rid_list,
+ readerid,
+ dev,
+ r_rid,
+ r_dev,
+ &idev,
+ ifcdesc_extra,
+ ifcdesc_extra_len,
+ interface_number,
+ ep_bulk_out, ep_bulk_in, ep_intr))
+ {
+ /* Found requested device or out of core. */
+ if (!idev)
+ {
+ free (rid_list);
+ return -1; /* error */
+ }
+ *r_idev = idev;
+ return 0;
+ }
+ }
+ }
+
+ /* Now check whether there are any devices with special transport types. */
+ for (i=0; transports[i].name; i++)
+ {
+ int fd;
+ char *rid, *p;
+
+ fd = open (transports[i].name, O_RDWR);
+ if (fd == -1)
+ continue;
+
+ rid = malloc (strlen (transports[i].name) + 30 + 10);
+ if (!rid)
+ {
+ close (fd);
+ free (rid_list);
+ return -1; /* Error. */
+ }
+ sprintf (rid, "0000:%04X:%s:0", transports[i].type, transports[i].name);
+ if (scan_mode)
+ {
+ DEBUGOUT_2 ("found CCID reader %d (ID=%s)\n", count, rid);
+ p = malloc ((rid_list? strlen (rid_list):0) + 1 + strlen (rid) + 1);
+ if (!p)
+ {
+ close (fd);
+ free (rid_list);
+ free (rid);
+ return -1; /* Error. */
+ }
+ *p = 0;
+ if (rid_list)
+ {
+ strcat (p, rid_list);
+ free (rid_list);
+ }
+ strcat (p, rid);
+ strcat (p, "\n");
+ rid_list = p;
+ ++count;
+ }
+ else if (!readerno ||
+ (readerno < 0 && readerid && !strcmp (readerid, rid)))
+ {
+ /* Found requested device. */
+ if (interface_number)
+ *interface_number = transports[i].type;
+ if (r_rid)
+ *r_rid = rid;
+ else
+ free (rid);
+ if (r_fd)
+ *r_fd = fd;
+ return 0; /* Okay, found device */
+ }
+ else /* This is not yet the reader we want. */
+ {
+ if (readerno >= 0)
+ --readerno;
+ }
+ free (rid);
+ close (fd);
+ }
+
+ if (scan_mode)
+ {
+ *r_rid = rid_list;
+ return 0;
+ }
+ else
+ return -1;
+}
+
+
+/* Set the level of debugging to LEVEL and return the old level. -1
+ just returns the old level. A level of 0 disables debugging, 1
+ enables debugging, 2 enables additional tracing of the T=1
+ protocol, other values are not yet defined. */
+int
+ccid_set_debug_level (int level)
+{
+ int old = debug_level;
+ if (level != -1)
+ debug_level = level;
+ return old;
+}
+
+
+char *
+ccid_get_reader_list (void)
+{
+ char *reader_list;
+
+ if (!initialized_usb)
+ {
+ usb_init ();
+ initialized_usb = 1;
+ }
+
+ if (scan_or_find_devices (-1, NULL, &reader_list, NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL, NULL))
+ return NULL; /* Error. */
+ return reader_list;
+}
+
+
+/* Open the reader with the internal number READERNO and return a
+ pointer to be used as handle in HANDLE. Returns 0 on success. */
+int
+ccid_open_reader (ccid_driver_t *handle, const char *readerid)
+{
+ int rc = 0;
+ struct usb_device *dev = NULL;
+ usb_dev_handle *idev = NULL;
+ int dev_fd = -1;
+ char *rid = NULL;
+ unsigned char *ifcdesc_extra = NULL;
+ size_t ifcdesc_extra_len;
+ int readerno;
+ int ifc_no, ep_bulk_out, ep_bulk_in, ep_intr;
+
+ *handle = NULL;
+
+ if (!initialized_usb)
+ {
+ usb_init ();
+ initialized_usb = 1;
+ }
+
+ /* See whether we want to use the reader ID string or a reader
+ number. A readerno of -1 indicates that the reader ID string is
+ to be used. */
+ if (readerid && strchr (readerid, ':'))
+ readerno = -1; /* We want to use the readerid. */
+ else if (readerid)
+ {
+ readerno = atoi (readerid);
+ if (readerno < 0)
+ {
+ DEBUGOUT ("no CCID readers found\n");
+ rc = CCID_DRIVER_ERR_NO_READER;
+ goto leave;
+ }
+ }
+ else
+ readerno = 0; /* Default. */
+
+ if (scan_or_find_devices (readerno, readerid, &rid, &dev,
+ &ifcdesc_extra, &ifcdesc_extra_len,
+ &ifc_no, &ep_bulk_out, &ep_bulk_in, &ep_intr,
+ &idev, &dev_fd) )
+ {
+ if (readerno == -1)
+ DEBUGOUT_1 ("no CCID reader with ID %s\n", readerid );
+ else
+ DEBUGOUT_1 ("no CCID reader with number %d\n", readerno );
+ rc = CCID_DRIVER_ERR_NO_READER;
+ goto leave;
+ }
+
+ /* Okay, this is a CCID reader. */
+ *handle = calloc (1, sizeof **handle);
+ if (!*handle)
+ {
+ DEBUGOUT ("out of memory\n");
+ rc = CCID_DRIVER_ERR_OUT_OF_CORE;
+ goto leave;
+ }
+ (*handle)->rid = rid;
+ if (idev) /* Regular USB transport. */
+ {
+ (*handle)->idev = idev;
+ (*handle)->dev_fd = -1;
+ (*handle)->id_vendor = dev->descriptor.idVendor;
+ (*handle)->id_product = dev->descriptor.idProduct;
+ (*handle)->bcd_device = dev->descriptor.bcdDevice;
+ (*handle)->ifc_no = ifc_no;
+ (*handle)->ep_bulk_out = ep_bulk_out;
+ (*handle)->ep_bulk_in = ep_bulk_in;
+ (*handle)->ep_intr = ep_intr;
+ }
+ else if (dev_fd != -1) /* Device transport. */
+ {
+ (*handle)->idev = NULL;
+ (*handle)->dev_fd = dev_fd;
+ (*handle)->id_vendor = 0; /* Magic vendor for special transport. */
+ (*handle)->id_product = ifc_no; /* Transport type */
+ prepare_special_transport (*handle);
+ }
+ else
+ {
+ assert (!"no transport"); /* Bug. */
+ }
+
+ DEBUGOUT_2 ("using CCID reader %d (ID=%s)\n", readerno, rid );
+
+ if (idev)
+ {
+ if (parse_ccid_descriptor (*handle, ifcdesc_extra, ifcdesc_extra_len))
+ {
+ DEBUGOUT ("device not supported\n");
+ rc = CCID_DRIVER_ERR_NO_READER;
+ goto leave;
+ }
+
+ rc = usb_claim_interface (idev, ifc_no);
+ if (rc)
+ {
+ DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
+ rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
+ goto leave;
+ }
+ }
+
+ leave:
+ free (ifcdesc_extra);
+ if (rc)
+ {
+ free (rid);
+ if (idev)
+ usb_close (idev);
+ if (dev_fd != -1)
+ close (dev_fd);
+ free (*handle);
+ *handle = NULL;
+ }
+
+ return rc;
+}
+
+
+static void
+do_close_reader (ccid_driver_t handle)
+{
+ int rc;
+ unsigned char msg[100];
+ size_t msglen;
+ unsigned char seqno;
+
+ if (!handle->powered_off)
+ {
+ msg[0] = PC_to_RDR_IccPowerOff;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 0; /* RFU */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ set_msg_len (msg, 0);
+ msglen = 10;
+
+ rc = bulk_out (handle, msg, msglen);
+ if (!rc)
+ bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus,
+ seqno, 2000, 0);
+ handle->powered_off = 1;
+ }
+ if (handle->idev)
+ {
+ usb_release_interface (handle->idev, handle->ifc_no);
+ usb_close (handle->idev);
+ handle->idev = NULL;
+ }
+ if (handle->dev_fd != -1)
+ {
+ close (handle->dev_fd);
+ handle->dev_fd = -1;
+ }
+}
+
+
+/* Reset a reader on HANDLE. This is useful in case a reader has been
+ plugged of and inserted at a different port. By resetting the
+ handle, the same reader will be get used. Note, that on error the
+ handle won't get released.
+
+ This does not return an ATR, so ccid_get_atr should be called right
+ after this one.
+*/
+int
+ccid_shutdown_reader (ccid_driver_t handle)
+{
+ int rc = 0;
+ struct usb_device *dev = NULL;
+ usb_dev_handle *idev = NULL;
+ unsigned char *ifcdesc_extra = NULL;
+ size_t ifcdesc_extra_len;
+ int ifc_no, ep_bulk_out, ep_bulk_in, ep_intr;
+
+ if (!handle || !handle->rid)
+ return CCID_DRIVER_ERR_INV_VALUE;
+
+ do_close_reader (handle);
+
+ if (scan_or_find_devices (-1, handle->rid, NULL, &dev,
+ &ifcdesc_extra, &ifcdesc_extra_len,
+ &ifc_no, &ep_bulk_out, &ep_bulk_in, &ep_intr,
+ &idev, NULL) || !idev)
+ {
+ DEBUGOUT_1 ("no CCID reader with ID %s\n", handle->rid);
+ return CCID_DRIVER_ERR_NO_READER;
+ }
+
+ if (idev)
+ {
+ handle->idev = idev;
+ handle->ifc_no = ifc_no;
+ handle->ep_bulk_out = ep_bulk_out;
+ handle->ep_bulk_in = ep_bulk_in;
+ handle->ep_intr = ep_intr;
+
+ if (parse_ccid_descriptor (handle, ifcdesc_extra, ifcdesc_extra_len))
+ {
+ DEBUGOUT ("device not supported\n");
+ rc = CCID_DRIVER_ERR_NO_READER;
+ goto leave;
+ }
+
+ rc = usb_claim_interface (idev, ifc_no);
+ if (rc)
+ {
+ DEBUGOUT_1 ("usb_claim_interface failed: %d\n", rc);
+ rc = CCID_DRIVER_ERR_CARD_IO_ERROR;
+ goto leave;
+ }
+ }
+
+ leave:
+ free (ifcdesc_extra);
+ if (rc)
+ {
+ if (handle->idev)
+ usb_close (handle->idev);
+ handle->idev = NULL;
+ if (handle->dev_fd != -1)
+ close (handle->dev_fd);
+ handle->dev_fd = -1;
+ }
+
+ return rc;
+
+}
+
+
+/* Close the reader HANDLE. */
+int
+ccid_close_reader (ccid_driver_t handle)
+{
+ if (!handle || (!handle->idev && handle->dev_fd == -1))
+ return 0;
+
+ do_close_reader (handle);
+ free (handle->rid);
+ free (handle);
+ return 0;
+}
+
+
+/* Return False if a card is present and powered. */
+int
+ccid_check_card_presence (ccid_driver_t handle)
+{
+
+ return -1;
+}
+
+
+/* Write NBYTES of BUF to file descriptor FD. */
+static int
+writen (int fd, const void *buf, size_t nbytes)
+{
+ size_t nleft = nbytes;
+ int nwritten;
+
+ while (nleft > 0)
+ {
+ nwritten = write (fd, buf, nleft);
+ if (nwritten < 0)
+ {
+ if (errno == EINTR)
+ nwritten = 0;
+ else
+ return -1;
+ }
+ nleft -= nwritten;
+ buf = (const char*)buf + nwritten;
+ }
+
+ return 0;
+}
+
+
+/* Write a MSG of length MSGLEN to the designated bulk out endpoint.
+ Returns 0 on success. */
+static int
+bulk_out (ccid_driver_t handle, unsigned char *msg, size_t msglen)
+{
+ int rc;
+
+ if (handle->idev)
+ {
+ rc = usb_bulk_write (handle->idev,
+ handle->ep_bulk_out,
+ (char*)msg, msglen,
+ 1000 /* ms timeout */);
+ if (rc == msglen)
+ return 0;
+ if (rc == -1)
+ DEBUGOUT_1 ("usb_bulk_write error: %s\n", strerror (errno));
+ else
+ DEBUGOUT_1 ("usb_bulk_write failed: %d\n", rc);
+ }
+ else
+ {
+ rc = writen (handle->dev_fd, msg, msglen);
+ if (!rc)
+ return 0;
+ DEBUGOUT_2 ("writen to %d failed: %s\n",
+ handle->dev_fd, strerror (errno));
+
+ }
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+}
+
+
+/* Read a maximum of LENGTH bytes from the bulk in endpoint into
+ BUFFER and return the actual read number if bytes in NREAD. SEQNO
+ is the sequence number used to send the request and EXPECTED_TYPE
+ the type of message we expect. Does checks on the ccid
+ header. TIMEOUT is the timeout value in ms. NO_DEBUG may be set to
+ avoid debug messages in case of no error. Returns 0 on success. */
+static int
+bulk_in (ccid_driver_t handle, unsigned char *buffer, size_t length,
+ size_t *nread, int expected_type, int seqno, int timeout,
+ int no_debug)
+{
+ int i, rc;
+ size_t msglen;
+
+ /* Fixme: The next line for the current Valgrind without support
+ for USB IOCTLs. */
+ memset (buffer, 0, length);
+ retry:
+ if (handle->idev)
+ {
+ rc = usb_bulk_read (handle->idev,
+ handle->ep_bulk_in,
+ (char*)buffer, length,
+ timeout);
+ if (rc < 0)
+ {
+ DEBUGOUT_1 ("usb_bulk_read error: %s\n", strerror (errno));
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ *nread = msglen = rc;
+ }
+ else
+ {
+ rc = read (handle->dev_fd, buffer, length);
+ if (rc < 0)
+ {
+ DEBUGOUT_2 ("read from %d failed: %s\n",
+ handle->dev_fd, strerror (errno));
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ *nread = msglen = rc;
+ }
+
+
+ if (msglen < 10)
+ {
+ DEBUGOUT_1 ("bulk-in msg too short (%u)\n", (unsigned int)msglen);
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+ if (buffer[0] != expected_type)
+ {
+ DEBUGOUT_1 ("unexpected bulk-in msg type (%02x)\n", buffer[0]);
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+ if (buffer[5] != 0)
+ {
+ DEBUGOUT_1 ("unexpected bulk-in slot (%d)\n", buffer[5]);
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+ if (buffer[6] != seqno)
+ {
+ DEBUGOUT_2 ("bulk-in seqno does not match (%d/%d)\n",
+ seqno, buffer[6]);
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+
+ if ( !(buffer[7] & 0x03) && (buffer[7] & 0xC0) == 0x80)
+ {
+ /* Card present and active, time extension requested. */
+ DEBUGOUT_2 ("time extension requested (%02X,%02X)\n",
+ buffer[7], buffer[8]);
+ goto retry;
+ }
+
+ if (!no_debug)
+ {
+ DEBUGOUT_3 ("status: %02X error: %02X octet[9]: %02X\n"
+ " data:", buffer[7], buffer[8], buffer[9] );
+ for (i=10; i < msglen; i++)
+ DEBUGOUT_CONT_1 (" %02X", buffer[i]);
+ DEBUGOUT_LF ();
+ }
+ if (CCID_COMMAND_FAILED (buffer))
+ print_command_failed (buffer);
+
+ /* Check whether a card is at all available. Note: If you add new
+ error codes here, check whether they need to be ignored in
+ send_escape_cmd. */
+ switch ((buffer[7] & 0x03))
+ {
+ case 0: /* no error */ break;
+ case 1: return CCID_DRIVER_ERR_CARD_INACTIVE;
+ case 2: return CCID_DRIVER_ERR_NO_CARD;
+ case 3: /* RFU */ break;
+ }
+ return 0;
+}
+
+
+/* Note that this function won't return the error codes NO_CARD or
+ CARD_INACTIVE. IF RESULT is not NULL, the result from the
+ operation will get returned in RESULT and its length in RESULTLEN.
+ If the response is larger than RESULTMAX, an error is returned and
+ the required buffer length returned in RESULTLEN. */
+static int
+send_escape_cmd (ccid_driver_t handle,
+ const unsigned char *data, size_t datalen,
+ unsigned char *result, size_t resultmax, size_t *resultlen)
+{
+ int i, rc;
+ unsigned char msg[100];
+ size_t msglen;
+ unsigned char seqno;
+
+ if (resultlen)
+ *resultlen = 0;
+
+ if (datalen > sizeof msg - 10)
+ return CCID_DRIVER_ERR_INV_VALUE; /* Escape data too large. */
+
+ msg[0] = PC_to_RDR_Escape;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 0; /* RFU */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ memcpy (msg+10, data, datalen);
+ msglen = 10 + datalen;
+ set_msg_len (msg, datalen);
+
+ DEBUGOUT ("sending");
+ for (i=0; i < msglen; i++)
+ DEBUGOUT_CONT_1 (" %02X", msg[i]);
+ DEBUGOUT_LF ();
+ rc = bulk_out (handle, msg, msglen);
+ if (rc)
+ return rc;
+ rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Escape,
+ seqno, 5000, 0);
+ if (result)
+ switch (rc)
+ {
+ /* We need to ignore certain errorcode here. */
+ case 0:
+ case CCID_DRIVER_ERR_CARD_INACTIVE:
+ case CCID_DRIVER_ERR_NO_CARD:
+ {
+ if (msglen > resultmax)
+ rc = CCID_DRIVER_ERR_INV_VALUE; /* Response too large. */
+ else
+ {
+ memcpy (result, msg, msglen);
+ *resultlen = msglen;
+ }
+ rc = 0;
+ }
+ break;
+ default:
+ break;
+ }
+
+ return rc;
+}
+
+
+int
+ccid_transceive_escape (ccid_driver_t handle,
+ const unsigned char *data, size_t datalen,
+ unsigned char *resp, size_t maxresplen, size_t *nresp)
+{
+ return send_escape_cmd (handle, data, datalen, resp, maxresplen, nresp);
+}
+
+
+
+/* experimental */
+int
+ccid_poll (ccid_driver_t handle)
+{
+ int rc;
+ unsigned char msg[10];
+ size_t msglen;
+ int i, j;
+
+ if (handle->idev)
+ {
+ rc = usb_bulk_read (handle->idev,
+ handle->ep_intr,
+ (char*)msg, sizeof msg,
+ 0 /* ms timeout */ );
+ if (rc < 0 && errno == ETIMEDOUT)
+ return 0;
+ }
+ else
+ return 0;
+
+ if (rc < 0)
+ {
+ DEBUGOUT_1 ("usb_intr_read error: %s\n", strerror (errno));
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+
+ msglen = rc;
+ rc = 0;
+
+ if (msglen < 1)
+ {
+ DEBUGOUT ("intr-in msg too short\n");
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+
+ if (msg[0] == RDR_to_PC_NotifySlotChange)
+ {
+ DEBUGOUT ("notify slot change:");
+ for (i=1; i < msglen; i++)
+ for (j=0; j < 4; j++)
+ DEBUGOUT_CONT_3 (" %d:%c%c",
+ (i-1)*4+j,
+ (msg[i] & (1<<(j*2)))? 'p':'-',
+ (msg[i] & (2<<(j*2)))? '*':' ');
+ DEBUGOUT_LF ();
+ }
+ else if (msg[0] == RDR_to_PC_HardwareError)
+ {
+ DEBUGOUT ("hardware error occured\n");
+ }
+ else
+ {
+ DEBUGOUT_1 ("unknown intr-in msg of type %02X\n", msg[0]);
+ }
+
+ return 0;
+}
+
+
+/* Note that this fucntion won't return the error codes NO_CARD or
+ CARD_INACTIVE */
+int
+ccid_slot_status (ccid_driver_t handle, int *statusbits)
+{
+ int rc;
+ unsigned char msg[100];
+ size_t msglen;
+ unsigned char seqno;
+ int retries = 0;
+
+ retry:
+ msg[0] = PC_to_RDR_GetSlotStatus;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 0; /* RFU */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ set_msg_len (msg, 0);
+
+ rc = bulk_out (handle, msg, 10);
+ if (rc)
+ return rc;
+ /* Note that we set the NO_DEBUG flag here, so that the logs won't
+ get cluttered up by a ticker function checking for the slot
+ status and debugging enabled. */
+ rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_SlotStatus,
+ seqno, retries? 1000 : 200, 1);
+ if (rc == CCID_DRIVER_ERR_CARD_IO_ERROR && retries < 3)
+ {
+ if (!retries)
+ {
+ DEBUGOUT ("USB: CALLING USB_CLEAR_HALT\n");
+ usb_clear_halt (handle->idev, handle->ep_bulk_in);
+ usb_clear_halt (handle->idev, handle->ep_bulk_out);
+ }
+ else
+ DEBUGOUT ("USB: RETRYING bulk_in AGAIN\n");
+ retries++;
+ goto retry;
+ }
+ if (rc && rc != CCID_DRIVER_ERR_NO_CARD
+ && rc != CCID_DRIVER_ERR_CARD_INACTIVE)
+ return rc;
+ *statusbits = (msg[7] & 3);
+
+ return 0;
+}
+
+
+int
+ccid_get_atr (ccid_driver_t handle,
+ unsigned char *atr, size_t maxatrlen, size_t *atrlen)
+{
+ int rc;
+ int statusbits;
+ unsigned char msg[100];
+ unsigned char *tpdu;
+ size_t msglen, tpdulen;
+ unsigned char seqno;
+ int use_crc = 0;
+ unsigned int edc;
+ int i;
+ int tried_iso = 0;
+ int got_param;
+
+ /* First check whether a card is available. */
+ rc = ccid_slot_status (handle, &statusbits);
+ if (rc)
+ return rc;
+ if (statusbits == 2)
+ return CCID_DRIVER_ERR_NO_CARD;
+
+
+ /* For an inactive and also for an active card, issue the PowerOn
+ command to get the ATR. */
+ again:
+ msg[0] = PC_to_RDR_IccPowerOn;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 0; /* power select (0=auto, 1=5V, 2=3V, 3=1.8V) */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ set_msg_len (msg, 0);
+ msglen = 10;
+
+ rc = bulk_out (handle, msg, msglen);
+ if (rc)
+ return rc;
+ rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_DataBlock,
+ seqno, 5000, 0);
+ if (rc)
+ return rc;
+ if (!tried_iso && CCID_COMMAND_FAILED (msg) && CCID_ERROR_CODE (msg) == 0xbb
+ && ((handle->id_vendor == VENDOR_CHERRY
+ && handle->id_product == 0x0005)
+ || (handle->id_vendor == VENDOR_GEMPC
+ && handle->id_product == 0x4433)
+ ))
+ {
+ tried_iso = 1;
+ /* Try switching to ISO mode. */
+ if (!send_escape_cmd (handle, (const unsigned char*)"\xF1\x01", 2,
+ NULL, 0, NULL))
+ goto again;
+ }
+ else if (CCID_COMMAND_FAILED (msg))
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+
+
+ handle->powered_off = 0;
+
+ if (atr)
+ {
+ size_t n = msglen - 10;
+
+ if (n > maxatrlen)
+ n = maxatrlen;
+ memcpy (atr, msg+10, n);
+ *atrlen = n;
+ }
+
+ got_param = 0;
+ msg[0] = PC_to_RDR_GetParameters;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 0; /* RFU */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ set_msg_len (msg, 0);
+ msglen = 10;
+ rc = bulk_out (handle, msg, msglen);
+ if (!rc)
+ rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters,
+ seqno, 2000, 0);
+ if (rc)
+ DEBUGOUT ("GetParameters failed\n");
+ else
+ {
+ DEBUGOUT ("GetParametes returned");
+ for (i=0; i < msglen; i++)
+ DEBUGOUT_CONT_1 (" %02X", msg[i]);
+ DEBUGOUT_LF ();
+ if (msglen >= 10)
+ {
+ DEBUGOUT_1 (" protocol ..........: T=%d\n", msg[9]);
+ if (msglen == 17 && msg[9] == 1)
+ {
+ DEBUGOUT_1 (" bmFindexDindex ....: %02X\n", msg[10]);
+ DEBUGOUT_1 (" bmTCCKST1 .........: %02X\n", msg[11]);
+ DEBUGOUT_1 (" bGuardTimeT1 ......: %02X\n", msg[12]);
+ DEBUGOUT_1 (" bmWaitingIntegersT1: %02X\n", msg[13]);
+ DEBUGOUT_1 (" bClockStop ........: %02X\n", msg[14]);
+ DEBUGOUT_1 (" bIFSC .............: %d\n", msg[15]);
+ DEBUGOUT_1 (" bNadValue .........: %d\n", msg[16]);
+ got_param = 1;
+ }
+ }
+ }
+
+ /* Setup parameters to select T=1. */
+ msg[0] = PC_to_RDR_SetParameters;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 1; /* Select T=1. */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+
+ if (!got_param)
+ {
+ /* FIXME: Get those values from the ATR. */
+ msg[10]= 0x01; /* Fi/Di */
+ msg[11]= 0x10; /* LRC, direct convention. */
+ msg[12]= 0; /* Extra guardtime. */
+ msg[13]= 0x41; /* BWI/CWI */
+ msg[14]= 0; /* No clock stoppping. */
+ msg[15]= 254; /* IFSC */
+ msg[16]= 0; /* Does not support non default NAD values. */
+ }
+ set_msg_len (msg, 7);
+ msglen = 10 + 7;
+
+ DEBUGOUT ("sending");
+ for (i=0; i < msglen; i++)
+ DEBUGOUT_CONT_1 (" %02X", msg[i]);
+ DEBUGOUT_LF ();
+
+ rc = bulk_out (handle, msg, msglen);
+ if (rc)
+ return rc;
+ rc = bulk_in (handle, msg, sizeof msg, &msglen, RDR_to_PC_Parameters,
+ seqno, 5000, 0);
+ if (rc)
+ DEBUGOUT ("SetParameters failed (ignored)\n");
+
+ handle->t1_ns = 0;
+ handle->t1_nr = 0;
+
+ /* Send an S-Block with our maximum IFSD to the CCID. */
+ if (!handle->apdu_level && !handle->auto_ifsd)
+ {
+ tpdu = msg+10;
+ /* NAD: DAD=1, SAD=0 */
+ tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+ tpdu[1] = (0xc0 | 0 | 1); /* S-block request: change IFSD */
+ tpdu[2] = 1;
+ tpdu[3] = handle->max_ifsd? handle->max_ifsd : 32;
+ tpdulen = 4;
+ edc = compute_edc (tpdu, tpdulen, use_crc);
+ if (use_crc)
+ tpdu[tpdulen++] = (edc >> 8);
+ tpdu[tpdulen++] = edc;
+
+ msg[0] = PC_to_RDR_XfrBlock;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 0;
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ set_msg_len (msg, tpdulen);
+ msglen = 10 + tpdulen;
+
+ DEBUGOUT ("sending");
+ for (i=0; i < msglen; i++)
+ DEBUGOUT_CONT_1 (" %02X", msg[i]);
+ DEBUGOUT_LF ();
+
+ if (debug_level > 1)
+ DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n",
+ ((msg[11] & 0xc0) == 0x80)? 'R' :
+ (msg[11] & 0x80)? 'S' : 'I',
+ ((msg[11] & 0x80)? !!(msg[11]& 0x10)
+ : !!(msg[11] & 0x40)),
+ (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
+
+ rc = bulk_out (handle, msg, msglen);
+ if (rc)
+ return rc;
+
+
+ rc = bulk_in (handle, msg, sizeof msg, &msglen,
+ RDR_to_PC_DataBlock, seqno, 5000, 0);
+ if (rc)
+ return rc;
+
+ tpdu = msg + 10;
+ tpdulen = msglen - 10;
+
+ if (tpdulen < 4)
+ return CCID_DRIVER_ERR_ABORTED;
+
+ if (debug_level > 1)
+ DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n",
+ ((msg[11] & 0xc0) == 0x80)? 'R' :
+ (msg[11] & 0x80)? 'S' : 'I',
+ ((msg[11] & 0x80)? !!(msg[11]& 0x10)
+ : !!(msg[11] & 0x40)),
+ ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0,
+ (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
+
+ if ((tpdu[1] & 0xe0) != 0xe0 || tpdu[2] != 1)
+ {
+ DEBUGOUT ("invalid response for S-block (Change-IFSD)\n");
+ return -1;
+ }
+ DEBUGOUT_1 ("IFSD has been set to %d\n", tpdu[3]);
+ }
+
+ return 0;
+}
+
+
+
+
+static unsigned int
+compute_edc (const unsigned char *data, size_t datalen, int use_crc)
+{
+ if (use_crc)
+ {
+ return 0x42; /* Not yet implemented. */
+ }
+ else
+ {
+ unsigned char crc = 0;
+
+ for (; datalen; datalen--)
+ crc ^= *data++;
+ return crc;
+ }
+}
+
+
+/* Helper for ccid_transceive used for APDU level exchanges. */
+static int
+ccid_transceive_apdu_level (ccid_driver_t handle,
+ const unsigned char *apdu_buf, size_t apdu_buflen,
+ unsigned char *resp, size_t maxresplen,
+ size_t *nresp)
+{
+ int rc;
+ unsigned char send_buffer[10+259], recv_buffer[10+259];
+ const unsigned char *apdu;
+ size_t apdulen;
+ unsigned char *msg;
+ size_t msglen;
+ unsigned char seqno;
+ int i;
+
+ msg = send_buffer;
+
+ apdu = apdu_buf;
+ apdulen = apdu_buflen;
+ assert (apdulen);
+
+ if (apdulen > 254)
+ return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
+
+ msg[0] = PC_to_RDR_XfrBlock;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 4; /* bBWI */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ memcpy (msg+10, apdu, apdulen);
+ set_msg_len (msg, apdulen);
+ msglen = 10 + apdulen;
+
+ DEBUGOUT ("sending");
+ for (i=0; i < msglen; i++)
+ DEBUGOUT_CONT_1 (" %02X", msg[i]);
+ DEBUGOUT_LF ();
+
+ rc = bulk_out (handle, msg, msglen);
+ if (rc)
+ return rc;
+
+ msg = recv_buffer;
+ rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen,
+ RDR_to_PC_DataBlock, seqno, 5000, 0);
+ if (rc)
+ return rc;
+
+ apdu = msg + 10;
+ apdulen = msglen - 10;
+
+ if (resp)
+ {
+ if (apdulen > maxresplen)
+ {
+ DEBUGOUT_2 ("provided buffer too short for received data "
+ "(%u/%u)\n",
+ (unsigned int)apdulen, (unsigned int)maxresplen);
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+
+ memcpy (resp, apdu, apdulen);
+ *nresp = apdulen;
+ }
+
+ return 0;
+}
+
+
+
+/*
+ Protocol T=1 overview
+
+ Block Structure:
+ Prologue Field:
+ 1 byte Node Address (NAD)
+ 1 byte Protocol Control Byte (PCB)
+ 1 byte Length (LEN)
+ Information Field:
+ 0-254 byte APDU or Control Information (INF)
+ Epilogue Field:
+ 1 byte Error Detection Code (EDC)
+
+ NAD:
+ bit 7 unused
+ bit 4..6 Destination Node Address (DAD)
+ bit 3 unused
+ bit 2..0 Source Node Address (SAD)
+
+ If node adresses are not used, SAD and DAD should be set to 0 on
+ the first block sent to the card. If they are used they should
+ have different values (0 for one is okay); that first block sets up
+ the addresses of the nodes.
+
+ PCB:
+ Information Block (I-Block):
+ bit 7 0
+ bit 6 Sequence number (yep, that is modulo 2)
+ bit 5 Chaining flag
+ bit 4..0 reserved
+ Received-Ready Block (R-Block):
+ bit 7 1
+ bit 6 0
+ bit 5 0
+ bit 4 Sequence number
+ bit 3..0 0 = no error
+ 1 = EDC or parity error
+ 2 = other error
+ other values are reserved
+ Supervisory Block (S-Block):
+ bit 7 1
+ bit 6 1
+ bit 5 clear=request,set=response
+ bit 4..0 0 = resyncronisation request
+ 1 = information field size request
+ 2 = abort request
+ 3 = extension of BWT request
+ 4 = VPP error
+ other values are reserved
+
+*/
+
+int
+ccid_transceive (ccid_driver_t handle,
+ const unsigned char *apdu_buf, size_t apdu_buflen,
+ unsigned char *resp, size_t maxresplen, size_t *nresp)
+{
+ int rc;
+ unsigned char send_buffer[10+259], recv_buffer[10+259];
+ const unsigned char *apdu;
+ size_t apdulen;
+ unsigned char *msg, *tpdu, *p;
+ size_t msglen, tpdulen, last_tpdulen, n;
+ unsigned char seqno;
+ int i;
+ unsigned int edc;
+ int use_crc = 0;
+ size_t dummy_nresp;
+ int next_chunk = 1;
+ int sending = 1;
+ int retries = 0;
+
+ if (!nresp)
+ nresp = &dummy_nresp;
+ *nresp = 0;
+
+ /* Smarter readers allow to send APDUs directly; divert here. */
+ if (handle->apdu_level)
+ return ccid_transceive_apdu_level (handle, apdu_buf, apdu_buflen,
+ resp, maxresplen, nresp);
+
+ /* The other readers we support require sending TPDUs. */
+
+ tpdulen = 0; /* Avoid compiler warning about no initialization. */
+ msg = send_buffer;
+ for (;;)
+ {
+ if (next_chunk)
+ {
+ next_chunk = 0;
+
+ apdu = apdu_buf;
+ apdulen = apdu_buflen;
+ assert (apdulen);
+
+ /* Construct an I-Block. */
+ if (apdulen > 254)
+ return CCID_DRIVER_ERR_INV_VALUE; /* Invalid length. */
+
+ tpdu = msg+10;
+ /* NAD: DAD=1, SAD=0 */
+ tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+ tpdu[1] = ((handle->t1_ns & 1) << 6); /* I-block */
+ if (apdulen > 128 /* fixme: replace by ifsc */)
+ {
+ apdulen = 128;
+ apdu_buf += 128;
+ apdu_buflen -= 128;
+ tpdu[1] |= (1 << 5); /* Set more bit. */
+ }
+ tpdu[2] = apdulen;
+ memcpy (tpdu+3, apdu, apdulen);
+ tpdulen = 3 + apdulen;
+ edc = compute_edc (tpdu, tpdulen, use_crc);
+ if (use_crc)
+ tpdu[tpdulen++] = (edc >> 8);
+ tpdu[tpdulen++] = edc;
+ }
+
+ msg[0] = PC_to_RDR_XfrBlock;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 4; /* bBWI */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ set_msg_len (msg, tpdulen);
+ msglen = 10 + tpdulen;
+ last_tpdulen = tpdulen;
+
+ DEBUGOUT ("sending");
+ for (i=0; i < msglen; i++)
+ DEBUGOUT_CONT_1 (" %02X", msg[i]);
+ DEBUGOUT_LF ();
+
+ if (debug_level > 1)
+ DEBUGOUT_3 ("T=1: put %c-block seq=%d%s\n",
+ ((msg[11] & 0xc0) == 0x80)? 'R' :
+ (msg[11] & 0x80)? 'S' : 'I',
+ ((msg[11] & 0x80)? !!(msg[11]& 0x10)
+ : !!(msg[11] & 0x40)),
+ (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
+
+ rc = bulk_out (handle, msg, msglen);
+ if (rc)
+ return rc;
+
+ msg = recv_buffer;
+ rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen,
+ RDR_to_PC_DataBlock, seqno, 5000, 0);
+ if (rc)
+ return rc;
+
+ tpdu = msg + 10;
+ tpdulen = msglen - 10;
+
+ if (tpdulen < 4)
+ {
+ usb_clear_halt (handle->idev, handle->ep_bulk_in);
+ return CCID_DRIVER_ERR_ABORTED;
+ }
+
+ if (debug_level > 1)
+ DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n",
+ ((msg[11] & 0xc0) == 0x80)? 'R' :
+ (msg[11] & 0x80)? 'S' : 'I',
+ ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)),
+ ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0,
+ (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
+
+ if (!(tpdu[1] & 0x80))
+ { /* This is an I-block. */
+ retries = 0;
+ if (sending)
+ { /* last block sent was successful. */
+ handle->t1_ns ^= 1;
+ sending = 0;
+ }
+
+ if (!!(tpdu[1] & 0x40) != handle->t1_nr)
+ { /* Reponse does not match our sequence number. */
+ msg = send_buffer;
+ tpdu = msg+10;
+ /* NAD: DAD=1, SAD=0 */
+ tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+ tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4 | 2); /* R-block */
+ tpdu[2] = 0;
+ tpdulen = 3;
+ edc = compute_edc (tpdu, tpdulen, use_crc);
+ if (use_crc)
+ tpdu[tpdulen++] = (edc >> 8);
+ tpdu[tpdulen++] = edc;
+
+ continue;
+ }
+
+ handle->t1_nr ^= 1;
+
+ p = tpdu + 3; /* Skip the prologue field. */
+ n = tpdulen - 3 - 1; /* Strip the epilogue field. */
+ /* fixme: verify the checksum. */
+ if (resp)
+ {
+ if (n > maxresplen)
+ {
+ DEBUGOUT_2 ("provided buffer too short for received data "
+ "(%u/%u)\n",
+ (unsigned int)n, (unsigned int)maxresplen);
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+
+ memcpy (resp, p, n);
+ resp += n;
+ *nresp += n;
+ maxresplen -= n;
+ }
+
+ if (!(tpdu[1] & 0x20))
+ return 0; /* No chaining requested - ready. */
+
+ msg = send_buffer;
+ tpdu = msg+10;
+ /* NAD: DAD=1, SAD=0 */
+ tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+ tpdu[1] = (0x80 | (handle->t1_nr & 1) << 4); /* R-block */
+ tpdu[2] = 0;
+ tpdulen = 3;
+ edc = compute_edc (tpdu, tpdulen, use_crc);
+ if (use_crc)
+ tpdu[tpdulen++] = (edc >> 8);
+ tpdu[tpdulen++] = edc;
+ }
+ else if ((tpdu[1] & 0xc0) == 0x80)
+ { /* This is a R-block. */
+ if ( (tpdu[1] & 0x0f))
+ { /* Error: repeat last block */
+ if (++retries > 3)
+ {
+ DEBUGOUT ("3 failed retries\n");
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ msg = send_buffer;
+ tpdulen = last_tpdulen;
+ }
+ else if (sending && !!(tpdu[1] & 0x10) == handle->t1_ns)
+ { /* Response does not match our sequence number. */
+ DEBUGOUT ("R-block with wrong seqno received on more bit\n");
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ else if (sending)
+ { /* Send next chunk. */
+ retries = 0;
+ msg = send_buffer;
+ next_chunk = 1;
+ handle->t1_ns ^= 1;
+ }
+ else
+ {
+ DEBUGOUT ("unexpected ACK R-block received\n");
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ }
+ else
+ { /* This is a S-block. */
+ retries = 0;
+ DEBUGOUT_2 ("T=1 S-block %s received cmd=%d\n",
+ (tpdu[1] & 0x20)? "response": "request",
+ (tpdu[1] & 0x1f));
+ if ( !(tpdu[1] & 0x20) && (tpdu[1] & 0x1f) == 3 && tpdu[2])
+ { /* Wait time extension request. */
+ unsigned char bwi = tpdu[3];
+ msg = send_buffer;
+ tpdu = msg+10;
+ /* NAD: DAD=1, SAD=0 */
+ tpdu[0] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+ tpdu[1] = (0xc0 | 0x20 | 3); /* S-block response */
+ tpdu[2] = 1;
+ tpdu[3] = bwi;
+ tpdulen = 4;
+ edc = compute_edc (tpdu, tpdulen, use_crc);
+ if (use_crc)
+ tpdu[tpdulen++] = (edc >> 8);
+ tpdu[tpdulen++] = edc;
+ DEBUGOUT_1 ("T=1 waittime extension of bwi=%d\n", bwi);
+ }
+ else
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ } /* end T=1 protocol loop. */
+
+ return 0;
+}
+
+
+/* Send the CCID Secure command to the reader. APDU_BUF should
+ contain the APDU template. PIN_MODE defines how the pin gets
+ formatted:
+
+ 1 := The PIN is ASCII encoded and of variable length. The
+ length of the PIN entered will be put into Lc by the reader.
+ The APDU should me made up of 4 bytes without Lc.
+
+ PINLEN_MIN and PINLEN_MAX define the limits for the pin length. 0
+ may be used t enable reasonable defaults. PIN_PADLEN should be 0.
+
+ When called with RESP and NRESP set to NULL, the function will
+ merely check whether the reader supports the secure command for the
+ given APDU and PIN_MODE. */
+int
+ccid_transceive_secure (ccid_driver_t handle,
+ const unsigned char *apdu_buf, size_t apdu_buflen,
+ int pin_mode, int pinlen_min, int pinlen_max,
+ int pin_padlen,
+ unsigned char *resp, size_t maxresplen, size_t *nresp)
+{
+ int rc;
+ unsigned char send_buffer[10+259], recv_buffer[10+259];
+ unsigned char *msg, *tpdu, *p;
+ size_t msglen, tpdulen, n;
+ unsigned char seqno;
+ int i;
+ size_t dummy_nresp;
+ int testmode;
+
+ testmode = !resp && !nresp;
+
+ if (!nresp)
+ nresp = &dummy_nresp;
+ *nresp = 0;
+
+ if (apdu_buflen >= 4 && apdu_buf[1] == 0x20 && (handle->has_pinpad & 1))
+ ;
+ else if (apdu_buflen >= 4 && apdu_buf[1] == 0x24 && (handle->has_pinpad & 2))
+ return CCID_DRIVER_ERR_NOT_SUPPORTED; /* Not yet by our code. */
+ else
+ return CCID_DRIVER_ERR_NO_KEYPAD;
+
+ if (pin_mode != 1)
+ return CCID_DRIVER_ERR_NOT_SUPPORTED;
+
+ if (pin_padlen != 0)
+ return CCID_DRIVER_ERR_NOT_SUPPORTED;
+
+ if (!pinlen_min)
+ pinlen_min = 1;
+ if (!pinlen_max)
+ pinlen_max = 25;
+
+ /* Note that the 25 is the maximum value the SPR532 allows. */
+ if (pinlen_min < 1 || pinlen_min > 25
+ || pinlen_max < 1 || pinlen_max > 25
+ || pinlen_min > pinlen_max)
+ return CCID_DRIVER_ERR_INV_VALUE;
+
+ /* We have only tested this with an SCM reader so better don't risk
+ anything and do not allow the use with other readers. */
+ if (handle->id_vendor != VENDOR_SCM)
+ return CCID_DRIVER_ERR_NOT_SUPPORTED;
+
+ if (testmode)
+ return 0; /* Success */
+
+ msg = send_buffer;
+ if (handle->id_vendor == VENDOR_SCM)
+ {
+ DEBUGOUT ("sending escape sequence to switch to a case 1 APDU\n");
+ rc = send_escape_cmd (handle, (const unsigned char*)"\x80\x02\x00", 3,
+ NULL, 0, NULL);
+ if (rc)
+ return rc;
+ }
+
+ msg[0] = PC_to_RDR_Secure;
+ msg[5] = 0; /* slot */
+ msg[6] = seqno = handle->seqno++;
+ msg[7] = 4; /* bBWI */
+ msg[8] = 0; /* RFU */
+ msg[9] = 0; /* RFU */
+ msg[10] = 0; /* Perform PIN verification. */
+ msg[11] = 0; /* Timeout in seconds. */
+ msg[12] = 0x82; /* bmFormatString: Byte, pos=0, left, ASCII. */
+ if (handle->id_vendor == VENDOR_SCM)
+ {
+ /* For the SPR532 the next 2 bytes need to be zero. We do this
+ for all SCM product. Kudos to Martin Paljak for this
+ hint. */
+ msg[13] = msg[14] = 0;
+ }
+ else
+ {
+ msg[13] = 0x00; /* bmPINBlockString:
+ 0 bits of pin length to insert.
+ 0 bytes of PIN block size. */
+ msg[14] = 0x00; /* bmPINLengthFormat:
+ Units are bytes, position is 0. */
+ }
+ msg[15] = pinlen_min; /* wPINMaxExtraDigit-Minimum. */
+ msg[16] = pinlen_max; /* wPINMaxExtraDigit-Maximum. */
+ msg[17] = 0x02; /* bEntryValidationCondition:
+ Validation key pressed */
+ if (pinlen_min && pinlen_max && pinlen_min == pinlen_max)
+ msg[17] |= 0x01; /* Max size reached. */
+ msg[18] = 0xff; /* bNumberMessage: Default. */
+ msg[19] = 0x04; /* wLangId-High. */
+ msg[20] = 0x09; /* wLangId-Low: English FIXME: use the first entry. */
+ msg[21] = 0; /* bMsgIndex. */
+ /* bTeoProlog follows: */
+ msg[22] = handle->nonnull_nad? ((1 << 4) | 0): 0;
+ msg[23] = ((handle->t1_ns & 1) << 6); /* I-block */
+ msg[24] = 4; /* apdulen. */
+ /* APDU follows: */
+ msg[25] = apdu_buf[0]; /* CLA */
+ msg[26] = apdu_buf[1]; /* INS */
+ msg[27] = apdu_buf[2]; /* P1 */
+ msg[28] = apdu_buf[3]; /* P2 */
+ msglen = 29;
+ set_msg_len (msg, msglen - 10);
+
+ DEBUGOUT ("sending");
+ for (i=0; i < msglen; i++)
+ DEBUGOUT_CONT_1 (" %02X", msg[i]);
+ DEBUGOUT_LF ();
+
+ rc = bulk_out (handle, msg, msglen);
+ if (rc)
+ return rc;
+
+ msg = recv_buffer;
+ rc = bulk_in (handle, msg, sizeof recv_buffer, &msglen,
+ RDR_to_PC_DataBlock, seqno, 5000, 0);
+ if (rc)
+ return rc;
+
+ tpdu = msg + 10;
+ tpdulen = msglen - 10;
+
+ if (tpdulen < 4)
+ {
+ usb_clear_halt (handle->idev, handle->ep_bulk_in);
+ return CCID_DRIVER_ERR_ABORTED;
+ }
+ if (debug_level > 1)
+ DEBUGOUT_4 ("T=1: got %c-block seq=%d err=%d%s\n",
+ ((msg[11] & 0xc0) == 0x80)? 'R' :
+ (msg[11] & 0x80)? 'S' : 'I',
+ ((msg[11] & 0x80)? !!(msg[11]& 0x10) : !!(msg[11] & 0x40)),
+ ((msg[11] & 0xc0) == 0x80)? (msg[11] & 0x0f) : 0,
+ (!(msg[11] & 0x80) && (msg[11] & 0x20)? " [more]":""));
+
+ if (!(tpdu[1] & 0x80))
+ { /* This is an I-block. */
+ /* Last block sent was successful. */
+ handle->t1_ns ^= 1;
+
+ if (!!(tpdu[1] & 0x40) != handle->t1_nr)
+ { /* Reponse does not match our sequence number. */
+ DEBUGOUT ("I-block with wrong seqno received\n");
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+
+ handle->t1_nr ^= 1;
+
+ p = tpdu + 3; /* Skip the prologue field. */
+ n = tpdulen - 3 - 1; /* Strip the epilogue field. */
+ /* fixme: verify the checksum. */
+ if (resp)
+ {
+ if (n > maxresplen)
+ {
+ DEBUGOUT_2 ("provided buffer too short for received data "
+ "(%u/%u)\n",
+ (unsigned int)n, (unsigned int)maxresplen);
+ return CCID_DRIVER_ERR_INV_VALUE;
+ }
+
+ memcpy (resp, p, n);
+ resp += n;
+ *nresp += n;
+ maxresplen -= n;
+ }
+
+ if (!(tpdu[1] & 0x20))
+ return 0; /* No chaining requested - ready. */
+
+ DEBUGOUT ("chaining requested but not supported for Secure operation\n");
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ else if ((tpdu[1] & 0xc0) == 0x80)
+ { /* This is a R-block. */
+ if ( (tpdu[1] & 0x0f))
+ { /* Error: repeat last block */
+ DEBUGOUT ("No retries supported for Secure operation\n");
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ else if (!!(tpdu[1] & 0x10) == handle->t1_ns)
+ { /* Reponse does not match our sequence number. */
+ DEBUGOUT ("R-block with wrong seqno received on more bit\n");
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ else
+ { /* Send next chunk. */
+ DEBUGOUT ("chaining not supported on Secure operation\n");
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+ }
+ else
+ { /* This is a S-block. */
+ DEBUGOUT_2 ("T=1 S-block %s received cmd=%d for Secure operation\n",
+ (tpdu[1] & 0x20)? "response": "request",
+ (tpdu[1] & 0x1f));
+ return CCID_DRIVER_ERR_CARD_IO_ERROR;
+ }
+
+ return 0;
+}
+
+
+
+
+#ifdef TEST
+
+
+static void
+print_error (int err)
+{
+ const char *p;
+ char buf[50];
+
+ switch (err)
+ {
+ case 0: p = "success";
+ case CCID_DRIVER_ERR_OUT_OF_CORE: p = "out of core"; break;
+ case CCID_DRIVER_ERR_INV_VALUE: p = "invalid value"; break;
+ case CCID_DRIVER_ERR_NO_DRIVER: p = "no driver"; break;
+ case CCID_DRIVER_ERR_NOT_SUPPORTED: p = "not supported"; break;
+ case CCID_DRIVER_ERR_LOCKING_FAILED: p = "locking failed"; break;
+ case CCID_DRIVER_ERR_BUSY: p = "busy"; break;
+ case CCID_DRIVER_ERR_NO_CARD: p = "no card"; break;
+ case CCID_DRIVER_ERR_CARD_INACTIVE: p = "card inactive"; break;
+ case CCID_DRIVER_ERR_CARD_IO_ERROR: p = "card I/O error"; break;
+ case CCID_DRIVER_ERR_GENERAL_ERROR: p = "general error"; break;
+ case CCID_DRIVER_ERR_NO_READER: p = "no reader"; break;
+ case CCID_DRIVER_ERR_ABORTED: p = "aborted"; break;
+ default: sprintf (buf, "0x%05x", err); p = buf; break;
+ }
+ fprintf (stderr, "operation failed: %s\n", p);
+}
+
+static void
+print_data (const unsigned char *data, size_t length)
+{
+ if (length >= 2)
+ {
+ fprintf (stderr, "operation status: %02X%02X\n",
+ data[length-2], data[length-1]);
+ length -= 2;
+ }
+ if (length)
+ {
+ fputs (" returned data:", stderr);
+ for (; length; length--, data++)
+ fprintf (stderr, " %02X", *data);
+ putc ('\n', stderr);
+ }
+}
+
+static void
+print_result (int rc, const unsigned char *data, size_t length)
+{
+ if (rc)
+ print_error (rc);
+ else if (data)
+ print_data (data, length);
+}
+
+int
+main (int argc, char **argv)
+{
+ int rc;
+ ccid_driver_t ccid;
+ unsigned int slotstat;
+ unsigned char result[512];
+ size_t resultlen;
+ int no_pinpad = 0;
+ int verify_123456 = 0;
+ int did_verify = 0;
+ int no_poll = 0;
+
+ if (argc)
+ {
+ argc--;
+ argv++;
+ }
+
+ while (argc)
+ {
+ if ( !strcmp (*argv, "--list"))
+ {
+ char *p;
+ p = ccid_get_reader_list ();
+ if (!p)
+ return 1;
+ fputs (p, stderr);
+ free (p);
+ return 0;
+ }
+ else if ( !strcmp (*argv, "--debug"))
+ {
+ ccid_set_debug_level (1);
+ argc--; argv++;
+ }
+ else if ( !strcmp (*argv, "--no-poll"))
+ {
+ no_poll = 1;
+ argc--; argv++;
+ }
+ else if ( !strcmp (*argv, "--no-pinpad"))
+ {
+ no_pinpad = 1;
+ argc--; argv++;
+ }
+ else if ( !strcmp (*argv, "--verify-123456"))
+ {
+ verify_123456 = 1;
+ argc--; argv++;
+ }
+ else
+ break;
+ }
+
+ rc = ccid_open_reader (&ccid, argc? *argv:NULL);
+ if (rc)
+ return 1;
+
+ if (!no_poll)
+ ccid_poll (ccid);
+ fputs ("getting ATR ...\n", stderr);
+ rc = ccid_get_atr (ccid, NULL, 0, NULL);
+ if (rc)
+ {
+ print_error (rc);
+ return 1;
+ }
+
+ if (!no_poll)
+ ccid_poll (ccid);
+ fputs ("getting slot status ...\n", stderr);
+ rc = ccid_slot_status (ccid, &slotstat);
+ if (rc)
+ {
+ print_error (rc);
+ return 1;
+ }
+
+ if (!no_poll)
+ ccid_poll (ccid);
+
+ fputs ("selecting application OpenPGP ....\n", stderr);
+ {
+ static unsigned char apdu[] = {
+ 0, 0xA4, 4, 0, 6, 0xD2, 0x76, 0x00, 0x01, 0x24, 0x01};
+ rc = ccid_transceive (ccid,
+ apdu, sizeof apdu,
+ result, sizeof result, &resultlen);
+ print_result (rc, result, resultlen);
+ }
+
+
+ if (!no_poll)
+ ccid_poll (ccid);
+
+ fputs ("getting OpenPGP DO 0x65 ....\n", stderr);
+ {
+ static unsigned char apdu[] = { 0, 0xCA, 0, 0x65, 254 };
+ rc = ccid_transceive (ccid, apdu, sizeof apdu,
+ result, sizeof result, &resultlen);
+ print_result (rc, result, resultlen);
+ }
+
+ if (!no_pinpad)
+ {
+ }
+
+ if (!no_pinpad)
+ {
+ static unsigned char apdu[] = { 0, 0x20, 0, 0x81 };
+
+
+ if (ccid_transceive_secure (ccid,
+ apdu, sizeof apdu,
+ 1, 0, 0, 0,
+ NULL, 0, NULL))
+ fputs ("can't verify using a PIN-Pad reader\n", stderr);
+ else
+ {
+ fputs ("verifying CHV1 using the PINPad ....\n", stderr);
+
+ rc = ccid_transceive_secure (ccid,
+ apdu, sizeof apdu,
+ 1, 0, 0, 0,
+ result, sizeof result, &resultlen);
+ print_result (rc, result, resultlen);
+ did_verify = 1;
+ }
+ }
+
+ if (verify_123456 && !did_verify)
+ {
+ fputs ("verifying that CHV1 is 123456....\n", stderr);
+ {
+ static unsigned char apdu[] = {0, 0x20, 0, 0x81,
+ 6, '1','2','3','4','5','6'};
+ rc = ccid_transceive (ccid, apdu, sizeof apdu,
+ result, sizeof result, &resultlen);
+ print_result (rc, result, resultlen);
+ }
+ }
+
+ if (!rc)
+ {
+ fputs ("getting OpenPGP DO 0x5E ....\n", stderr);
+ {
+ static unsigned char apdu[] = { 0, 0xCA, 0, 0x5E, 254 };
+ rc = ccid_transceive (ccid, apdu, sizeof apdu,
+ result, sizeof result, &resultlen);
+ print_result (rc, result, resultlen);
+ }
+ }
+
+ ccid_close_reader (ccid);
+
+ return 0;
+}
+
+/*
+ * Local Variables:
+ * compile-command: "gcc -DTEST -Wall -I/usr/local/include -lusb -g ccid-driver.c"
+ * End:
+ */
+#endif /*TEST*/
+#endif /*HAVE_LIBUSB*/
diff --git a/g10/ccid-driver.h b/g10/ccid-driver.h
new file mode 100644
index 0000000..54adffc
--- /dev/null
+++ b/g10/ccid-driver.h
@@ -0,0 +1,108 @@
+/* ccid-driver.c - USB ChipCardInterfaceDevices driver
+ * Copyright (C) 2003 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ * ALTERNATIVELY, this file may be distributed under the terms of the
+ * following license, in which case the provisions of this license are
+ * required INSTEAD OF the GNU General Public License. If you wish to
+ * allow use of your version of this file only under the terms of the
+ * GNU General Public License, and not to allow others to use your
+ * version of this file under the terms of the following license,
+ * indicate your decision by deleting this paragraph and the license
+ * below.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, and the entire permission notice in its entirety,
+ * including the disclaimer of warranties.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. The name of the author may not be used to endorse or promote
+ * products derived from this software without specific prior
+ * written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
+ * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT,
+ * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * $Id: ccid-driver.h 3995 2006-02-06 16:34:20Z wk $
+ */
+
+#ifndef CCID_DRIVER_H
+#define CCID_DRIVER_H
+
+/* The CID driver returns the same error codes as the status words
+ used by GnuPG's apdu.h. For ease of maintenance they should always
+ match. */
+#define CCID_DRIVER_ERR_OUT_OF_CORE 0x10001
+#define CCID_DRIVER_ERR_INV_VALUE 0x10002
+#define CCID_DRIVER_ERR_INCOMPLETE_CARD_RESPONSE = 0x10003
+#define CCID_DRIVER_ERR_NO_DRIVER 0x10004
+#define CCID_DRIVER_ERR_NOT_SUPPORTED 0x10005
+#define CCID_DRIVER_ERR_LOCKING_FAILED 0x10006
+#define CCID_DRIVER_ERR_BUSY 0x10007
+#define CCID_DRIVER_ERR_NO_CARD 0x10008
+#define CCID_DRIVER_ERR_CARD_INACTIVE 0x10009
+#define CCID_DRIVER_ERR_CARD_IO_ERROR 0x1000a
+#define CCID_DRIVER_ERR_GENERAL_ERROR 0x1000b
+#define CCID_DRIVER_ERR_NO_READER 0x1000c
+#define CCID_DRIVER_ERR_ABORTED 0x1000d
+#define CCID_DRIVER_ERR_NO_KEYPAD 0x1000e
+
+struct ccid_driver_s;
+typedef struct ccid_driver_s *ccid_driver_t;
+
+int ccid_set_debug_level (int level);
+char *ccid_get_reader_list (void);
+int ccid_open_reader (ccid_driver_t *handle, const char *readerid);
+int ccid_shutdown_reader (ccid_driver_t handle);
+int ccid_close_reader (ccid_driver_t handle);
+int ccid_get_atr (ccid_driver_t handle,
+ unsigned char *atr, size_t maxatrlen, size_t *atrlen);
+int ccid_slot_status (ccid_driver_t handle, int *statusbits);
+int ccid_transceive (ccid_driver_t handle,
+ const unsigned char *apdu, size_t apdulen,
+ unsigned char *resp, size_t maxresplen, size_t *nresp);
+int ccid_transceive_secure (ccid_driver_t handle,
+ const unsigned char *apdu, size_t apdulen,
+ int pin_mode,
+ int pinlen_min, int pinlen_max, int pin_padlen,
+ unsigned char *resp, size_t maxresplen, size_t *nresp);
+int ccid_transceive_escape (ccid_driver_t handle,
+ const unsigned char *data, size_t datalen,
+ unsigned char *resp, size_t maxresplen,
+ size_t *nresp);
+
+
+
+#endif /*CCID_DRIVER_H*/
+
+
+
diff --git a/g10/cipher.c b/g10/cipher.c
new file mode 100644
index 0000000..f7332ba
--- /dev/null
+++ b/g10/cipher.c
@@ -0,0 +1,153 @@
+/* cipher.c - En-/De-ciphering filter
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "errors.h"
+#include "iobuf.h"
+#include "memory.h"
+#include "util.h"
+#include "filter.h"
+#include "packet.h"
+#include "options.h"
+#include "main.h"
+#include "status.h"
+
+
+#define MIN_PARTIAL_SIZE 512
+
+
+static void
+write_header( cipher_filter_context_t *cfx, IOBUF a )
+{
+ PACKET pkt;
+ PKT_encrypted ed;
+ byte temp[18];
+ unsigned blocksize;
+ unsigned nprefix;
+
+ blocksize = cipher_get_blocksize( cfx->dek->algo );
+ if( blocksize < 8 || blocksize > 16 )
+ log_fatal("unsupported blocksize %u\n", blocksize );
+
+ memset( &ed, 0, sizeof ed );
+ ed.len = cfx->datalen;
+ ed.extralen = blocksize+2;
+ ed.new_ctb = !ed.len && !RFC1991;
+ if( cfx->dek->use_mdc ) {
+ ed.mdc_method = DIGEST_ALGO_SHA1;
+ cfx->mdc_hash = md_open( DIGEST_ALGO_SHA1, 0 );
+ if ( DBG_HASHING )
+ md_start_debug( cfx->mdc_hash, "creatmdc" );
+ }
+
+ {
+ char buf[20];
+
+ sprintf (buf, "%d %d", ed.mdc_method, cfx->dek->algo);
+ write_status_text (STATUS_BEGIN_ENCRYPTION, buf);
+ }
+
+ init_packet( &pkt );
+ pkt.pkttype = cfx->dek->use_mdc? PKT_ENCRYPTED_MDC : PKT_ENCRYPTED;
+ pkt.pkt.encrypted = &ed;
+ if( build_packet( a, &pkt ))
+ log_bug("build_packet(ENCR_DATA) failed\n");
+ nprefix = blocksize;
+ randomize_buffer( temp, nprefix, 1 );
+ temp[nprefix] = temp[nprefix-2];
+ temp[nprefix+1] = temp[nprefix-1];
+ print_cipher_algo_note( cfx->dek->algo );
+ cfx->cipher_hd = cipher_open( cfx->dek->algo,
+ cfx->dek->use_mdc? CIPHER_MODE_CFB
+ : CIPHER_MODE_AUTO_CFB, 1 );
+/* log_hexdump( "thekey", cfx->dek->key, cfx->dek->keylen );*/
+ cipher_setkey( cfx->cipher_hd, cfx->dek->key, cfx->dek->keylen );
+ cipher_setiv( cfx->cipher_hd, NULL, 0 );
+/* log_hexdump( "prefix", temp, nprefix+2 ); */
+ if( cfx->mdc_hash ) /* hash the "IV" */
+ md_write( cfx->mdc_hash, temp, nprefix+2 );
+ cipher_encrypt( cfx->cipher_hd, temp, temp, nprefix+2);
+ cipher_sync( cfx->cipher_hd );
+ iobuf_write(a, temp, nprefix+2);
+ cfx->header=1;
+}
+
+
+
+/****************
+ * This filter is used to en/de-cipher data with a conventional algorithm
+ */
+int
+cipher_filter( void *opaque, int control,
+ IOBUF a, byte *buf, size_t *ret_len)
+{
+ size_t size = *ret_len;
+ cipher_filter_context_t *cfx = opaque;
+ int rc=0;
+
+ if( control == IOBUFCTRL_UNDERFLOW ) { /* decrypt */
+ rc = -1; /* not yet used */
+ }
+ else if( control == IOBUFCTRL_FLUSH ) { /* encrypt */
+ assert(a);
+ if( !cfx->header ) {
+ write_header( cfx, a );
+ }
+ if( cfx->mdc_hash )
+ md_write( cfx->mdc_hash, buf, size );
+ cipher_encrypt( cfx->cipher_hd, buf, buf, size);
+ if( iobuf_write( a, buf, size ) )
+ rc = G10ERR_WRITE_FILE;
+ }
+ else if( control == IOBUFCTRL_FREE ) {
+ if( cfx->mdc_hash ) {
+ byte *hash;
+ int hashlen = md_digest_length( md_get_algo( cfx->mdc_hash ) );
+ byte temp[22];
+
+ assert( hashlen == 20 );
+ /* we must hash the prefix of the MDC packet here */
+ temp[0] = 0xd3;
+ temp[1] = 0x14;
+ md_putc( cfx->mdc_hash, temp[0] );
+ md_putc( cfx->mdc_hash, temp[1] );
+
+ md_final( cfx->mdc_hash );
+ hash = md_read( cfx->mdc_hash, 0 );
+ memcpy(temp+2, hash, 20);
+ cipher_encrypt( cfx->cipher_hd, temp, temp, 22 );
+ md_close( cfx->mdc_hash ); cfx->mdc_hash = NULL;
+ if( iobuf_write( a, temp, 22 ) )
+ log_error("writing MDC packet failed\n" );
+ }
+ cipher_close(cfx->cipher_hd);
+ }
+ else if( control == IOBUFCTRL_DESC ) {
+ *(char**)buf = "cipher_filter";
+ }
+ return rc;
+}
diff --git a/g10/compress-bz2.c b/g10/compress-bz2.c
new file mode 100644
index 0000000..7a8075a
--- /dev/null
+++ b/g10/compress-bz2.c
@@ -0,0 +1,242 @@
+/* compress.c - bzip2 compress filter
+ * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <string.h>
+#include <stdio.h> /* Early versions of bzlib (1.0) require stdio.h */
+#include <bzlib.h>
+
+#include "util.h"
+#include "memory.h"
+#include "packet.h"
+#include "filter.h"
+#include "main.h"
+#include "options.h"
+
+/* Note that the code in compress.c is nearly identical to the code
+ here, so if you fix a bug here, look there to see if a matching bug
+ needs to be fixed. I tried to have one set of functions that could
+ do ZIP, ZLIB, and BZIP2, but it became dangerously unreadable with
+ #ifdefs and if(algo) -dshaw */
+
+static void
+init_compress( compress_filter_context_t *zfx, bz_stream *bzs )
+{
+ int rc;
+ int level;
+
+ if( opt.bz2_compress_level >= 1 && opt.bz2_compress_level <= 9 )
+ level = opt.bz2_compress_level;
+ else if( opt.bz2_compress_level == -1 )
+ level = 6; /* no particular reason, but it seems reasonable */
+ else
+ {
+ log_error("invalid compression level; using default level\n");
+ level = 6;
+ }
+
+ if((rc=BZ2_bzCompressInit(bzs,level,0,0))!=BZ_OK)
+ log_fatal("bz2lib problem: %d\n",rc);
+
+ zfx->outbufsize = 8192;
+ zfx->outbuf = xmalloc( zfx->outbufsize );
+}
+
+static int
+do_compress(compress_filter_context_t *zfx, bz_stream *bzs, int flush, IOBUF a)
+{
+ int zrc;
+ unsigned n;
+
+ do
+ {
+ bzs->next_out = zfx->outbuf;
+ bzs->avail_out = zfx->outbufsize;
+ if( DBG_FILTER )
+ log_debug("enter bzCompress: avail_in=%u, avail_out=%u, flush=%d\n",
+ (unsigned)bzs->avail_in, (unsigned)bzs->avail_out, flush );
+ zrc = BZ2_bzCompress( bzs, flush );
+ if( zrc == BZ_STREAM_END && flush == BZ_FINISH )
+ ;
+ else if( zrc != BZ_RUN_OK && zrc != BZ_FINISH_OK )
+ log_fatal("bz2lib deflate problem: rc=%d\n", zrc );
+
+ n = zfx->outbufsize - bzs->avail_out;
+ if( DBG_FILTER )
+ log_debug("leave bzCompress:"
+ " avail_in=%u, avail_out=%u, n=%u, zrc=%d\n",
+ (unsigned)bzs->avail_in, (unsigned)bzs->avail_out,
+ (unsigned)n, zrc );
+
+ if( iobuf_write( a, zfx->outbuf, n ) )
+ {
+ log_debug("bzCompress: iobuf_write failed\n");
+ return G10ERR_WRITE_FILE;
+ }
+ }
+ while( bzs->avail_in || (flush == BZ_FINISH && zrc != BZ_STREAM_END) );
+
+ return 0;
+}
+
+static void
+init_uncompress( compress_filter_context_t *zfx, bz_stream *bzs )
+{
+ int rc;
+
+ if((rc=BZ2_bzDecompressInit(bzs,0,opt.bz2_decompress_lowmem))!=BZ_OK)
+ log_fatal("bz2lib problem: %d\n",rc);
+
+ zfx->inbufsize = 2048;
+ zfx->inbuf = xmalloc( zfx->inbufsize );
+ bzs->avail_in = 0;
+}
+
+static int
+do_uncompress( compress_filter_context_t *zfx, bz_stream *bzs,
+ IOBUF a, size_t *ret_len )
+{
+ int zrc;
+ int rc=0;
+ size_t n;
+ int nread, count;
+ int refill = !bzs->avail_in;
+
+ if( DBG_FILTER )
+ log_debug("begin bzDecompress: avail_in=%u, avail_out=%u, inbuf=%u\n",
+ (unsigned)bzs->avail_in, (unsigned)bzs->avail_out,
+ (unsigned)zfx->inbufsize );
+ do
+ {
+ if( bzs->avail_in < zfx->inbufsize && refill )
+ {
+ n = bzs->avail_in;
+ if( !n )
+ bzs->next_in = zfx->inbuf;
+ count = zfx->inbufsize - n;
+ nread = iobuf_read( a, zfx->inbuf + n, count );
+ if( nread == -1 ) nread = 0;
+ n += nread;
+ bzs->avail_in = n;
+ }
+
+ refill = 1;
+
+ if( DBG_FILTER )
+ log_debug("enter bzDecompress: avail_in=%u, avail_out=%u\n",
+ (unsigned)bzs->avail_in, (unsigned)bzs->avail_out);
+
+ zrc=BZ2_bzDecompress(bzs);
+ if( DBG_FILTER )
+ log_debug("leave bzDecompress: avail_in=%u, avail_out=%u, zrc=%d\n",
+ (unsigned)bzs->avail_in, (unsigned)bzs->avail_out, zrc);
+ if( zrc == BZ_STREAM_END )
+ rc = -1; /* eof */
+ else if( zrc != BZ_OK && zrc != BZ_PARAM_ERROR )
+ log_fatal("bz2lib inflate problem: rc=%d\n", zrc );
+ }
+ while( bzs->avail_out && zrc != BZ_STREAM_END && zrc != BZ_PARAM_ERROR );
+
+ /* I'm not completely happy with the two uses of BZ_PARAM_ERROR
+ here. The corresponding zlib function is Z_BUF_ERROR, which
+ covers a narrower scope than BZ_PARAM_ERROR. -dshaw */
+
+ *ret_len = zfx->outbufsize - bzs->avail_out;
+ if( DBG_FILTER )
+ log_debug("do_uncompress: returning %u bytes\n", (unsigned)*ret_len );
+ return rc;
+}
+
+int
+compress_filter_bz2( void *opaque, int control,
+ IOBUF a, byte *buf, size_t *ret_len)
+{
+ size_t size = *ret_len;
+ compress_filter_context_t *zfx = opaque;
+ bz_stream *bzs = zfx->opaque;
+ int rc=0;
+
+ if( control == IOBUFCTRL_UNDERFLOW )
+ {
+ if( !zfx->status )
+ {
+ bzs = zfx->opaque = xmalloc_clear( sizeof *bzs );
+ init_uncompress( zfx, bzs );
+ zfx->status = 1;
+ }
+
+ bzs->next_out = buf;
+ bzs->avail_out = size;
+ zfx->outbufsize = size; /* needed only for calculation */
+ rc = do_uncompress( zfx, bzs, a, ret_len );
+ }
+ else if( control == IOBUFCTRL_FLUSH )
+ {
+ if( !zfx->status )
+ {
+ PACKET pkt;
+ PKT_compressed cd;
+
+ if( zfx->algo != COMPRESS_ALGO_BZIP2 )
+ BUG();
+ memset( &cd, 0, sizeof cd );
+ cd.len = 0;
+ cd.algorithm = zfx->algo;
+ init_packet( &pkt );
+ pkt.pkttype = PKT_COMPRESSED;
+ pkt.pkt.compressed = &cd;
+ if( build_packet( a, &pkt ))
+ log_bug("build_packet(PKT_COMPRESSED) failed\n");
+ bzs = zfx->opaque = xmalloc_clear( sizeof *bzs );
+ init_compress( zfx, bzs );
+ zfx->status = 2;
+ }
+
+ bzs->next_in = buf;
+ bzs->avail_in = size;
+ rc = do_compress( zfx, bzs, BZ_RUN, a );
+ }
+ else if( control == IOBUFCTRL_FREE )
+ {
+ if( zfx->status == 1 )
+ {
+ BZ2_bzDecompressEnd(bzs);
+ xfree(bzs);
+ zfx->opaque = NULL;
+ xfree(zfx->outbuf); zfx->outbuf = NULL;
+ }
+ else if( zfx->status == 2 )
+ {
+ bzs->next_in = buf;
+ bzs->avail_in = 0;
+ do_compress( zfx, bzs, BZ_FINISH, a );
+ BZ2_bzCompressEnd(bzs);
+ xfree(bzs);
+ zfx->opaque = NULL;
+ xfree(zfx->outbuf); zfx->outbuf = NULL;
+ }
+ if (zfx->release)
+ zfx->release (zfx);
+ }
+ else if( control == IOBUFCTRL_DESC )
+ *(char**)buf = "compress_filter";
+ return rc;
+}
diff --git a/g10/compress.c b/g10/compress.c
new file mode 100644
index 0000000..0cee5ae
--- /dev/null
+++ b/g10/compress.c
@@ -0,0 +1,365 @@
+/* compress.c - compress filter
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002,
+ * 2003 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* Note that the code in compress-bz2.c is nearly identical to the
+ code here, so if you fix a bug here, look there to see if a
+ matching bug needs to be fixed. I tried to have one set of
+ functions that could do ZIP, ZLIB, and BZIP2, but it became
+ dangerously unreadable with #ifdefs and if(algo) -dshaw */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#include <errno.h>
+#include <zlib.h>
+#if defined(__riscos__) && defined(USE_ZLIBRISCOS)
+# include "zlib-riscos.h"
+#endif
+
+#include "util.h"
+#include "memory.h"
+#include "packet.h"
+#include "filter.h"
+#include "main.h"
+#include "options.h"
+
+int compress_filter_bz2( void *opaque, int control,
+ IOBUF a, byte *buf, size_t *ret_len);
+
+static void
+init_compress( compress_filter_context_t *zfx, z_stream *zs )
+{
+ int rc;
+ int level;
+
+#if defined(__riscos__) && defined(USE_ZLIBRISCOS)
+ static int zlib_initialized = 0;
+
+ if (!zlib_initialized)
+ zlib_initialized = riscos_load_module("ZLib", zlib_path, 1);
+#endif
+
+ if( opt.compress_level >= 1 && opt.compress_level <= 9 )
+ level = opt.compress_level;
+ else if( opt.compress_level == -1 )
+ level = Z_DEFAULT_COMPRESSION;
+ else {
+ log_error("invalid compression level; using default level\n");
+ level = Z_DEFAULT_COMPRESSION;
+ }
+
+ if( (rc = zfx->algo == 1? deflateInit2( zs, level, Z_DEFLATED,
+ -13, 8, Z_DEFAULT_STRATEGY)
+ : deflateInit( zs, level )
+ ) != Z_OK ) {
+ log_fatal("zlib problem: %s\n", zs->msg? zs->msg :
+ rc == Z_MEM_ERROR ? "out of core" :
+ rc == Z_VERSION_ERROR ? "invalid lib version" :
+ "unknown error" );
+ }
+
+ zfx->outbufsize = 8192;
+ zfx->outbuf = xmalloc( zfx->outbufsize );
+}
+
+static int
+do_compress( compress_filter_context_t *zfx, z_stream *zs, int flush, IOBUF a )
+{
+ int zrc;
+ unsigned n;
+
+ do {
+#ifndef __riscos__
+ zs->next_out = zfx->outbuf;
+#else /* __riscos__ */
+ zs->next_out = (Bytef *) zfx->outbuf;
+#endif /* __riscos__ */
+ zs->avail_out = zfx->outbufsize;
+ if( DBG_FILTER )
+ log_debug("enter deflate: avail_in=%u, avail_out=%u, flush=%d\n",
+ (unsigned)zs->avail_in, (unsigned)zs->avail_out, flush );
+ zrc = deflate( zs, flush );
+ if( zrc == Z_STREAM_END && flush == Z_FINISH )
+ ;
+ else if( zrc != Z_OK ) {
+ if( zs->msg )
+ log_fatal("zlib deflate problem: %s\n", zs->msg );
+ else
+ log_fatal("zlib deflate problem: rc=%d\n", zrc );
+ }
+ n = zfx->outbufsize - zs->avail_out;
+ if( DBG_FILTER )
+ log_debug("leave deflate: "
+ "avail_in=%u, avail_out=%u, n=%u, zrc=%d\n",
+ (unsigned)zs->avail_in, (unsigned)zs->avail_out,
+ (unsigned)n, zrc );
+
+ if( iobuf_write( a, zfx->outbuf, n ) ) {
+ log_debug("deflate: iobuf_write failed\n");
+ return G10ERR_WRITE_FILE;
+ }
+ } while( zs->avail_in || (flush == Z_FINISH && zrc != Z_STREAM_END) );
+ return 0;
+}
+
+static void
+init_uncompress( compress_filter_context_t *zfx, z_stream *zs )
+{
+ int rc;
+
+ /****************
+ * PGP uses a windowsize of 13 bits. Using a negative value for
+ * it forces zlib not to expect a zlib header. This is a
+ * undocumented feature Peter Gutmann told me about.
+ *
+ * We must use 15 bits for the inflator because CryptoEx uses 15
+ * bits thus the output would get scrambled w/o error indication
+ * if we would use 13 bits. For the uncompressing this does not
+ * matter at all.
+ */
+ if( (rc = zfx->algo == 1? inflateInit2( zs, -15)
+ : inflateInit( zs )) != Z_OK ) {
+ log_fatal("zlib problem: %s\n", zs->msg? zs->msg :
+ rc == Z_MEM_ERROR ? "out of core" :
+ rc == Z_VERSION_ERROR ? "invalid lib version" :
+ "unknown error" );
+ }
+
+ zfx->inbufsize = 2048;
+ zfx->inbuf = xmalloc( zfx->inbufsize );
+ zs->avail_in = 0;
+}
+
+static int
+do_uncompress( compress_filter_context_t *zfx, z_stream *zs,
+ IOBUF a, size_t *ret_len )
+{
+ int zrc;
+ int rc=0;
+ size_t n;
+ int nread, count;
+ int refill = !zs->avail_in;
+
+ if( DBG_FILTER )
+ log_debug("begin inflate: avail_in=%u, avail_out=%u, inbuf=%u\n",
+ (unsigned)zs->avail_in, (unsigned)zs->avail_out,
+ (unsigned)zfx->inbufsize );
+ do {
+ if( zs->avail_in < zfx->inbufsize && refill ) {
+ n = zs->avail_in;
+ if( !n )
+#ifndef __riscos__
+ zs->next_in = zfx->inbuf;
+#else /* __riscos__ */
+ zs->next_in = (Bytef *) zfx->inbuf;
+#endif /* __riscos__ */
+ count = zfx->inbufsize - n;
+ nread = iobuf_read( a, zfx->inbuf + n, count );
+ if( nread == -1 ) nread = 0;
+ n += nread;
+ /* If we use the undocumented feature to suppress
+ * the zlib header, we have to give inflate an
+ * extra dummy byte to read */
+ if( nread < count && zfx->algo == 1 ) {
+ *(zfx->inbuf + n) = 0xFF; /* is it really needed ? */
+ zfx->algo1hack = 1;
+ n++;
+ }
+ zs->avail_in = n;
+ }
+ refill = 1;
+ if( DBG_FILTER )
+ log_debug("enter inflate: avail_in=%u, avail_out=%u\n",
+ (unsigned)zs->avail_in, (unsigned)zs->avail_out);
+#ifdef Z_SYNC_FLUSH
+ zrc = inflate( zs, Z_SYNC_FLUSH );
+#else
+ zrc = inflate( zs, Z_PARTIAL_FLUSH );
+#endif
+ if( DBG_FILTER )
+ log_debug("leave inflate: avail_in=%u, avail_out=%u, zrc=%d\n",
+ (unsigned)zs->avail_in, (unsigned)zs->avail_out, zrc);
+ if( zrc == Z_STREAM_END )
+ rc = -1; /* eof */
+ else if( zrc != Z_OK && zrc != Z_BUF_ERROR ) {
+ if( zs->msg )
+ log_fatal("zlib inflate problem: %s\n", zs->msg );
+ else
+ log_fatal("zlib inflate problem: rc=%d\n", zrc );
+ }
+ } while( zs->avail_out && zrc != Z_STREAM_END && zrc != Z_BUF_ERROR );
+ *ret_len = zfx->outbufsize - zs->avail_out;
+ if( DBG_FILTER )
+ log_debug("do_uncompress: returning %u bytes\n", (unsigned)*ret_len );
+ return rc;
+}
+
+static int
+compress_filter( void *opaque, int control,
+ IOBUF a, byte *buf, size_t *ret_len)
+{
+ size_t size = *ret_len;
+ compress_filter_context_t *zfx = opaque;
+ z_stream *zs = zfx->opaque;
+ int rc=0;
+
+ if( control == IOBUFCTRL_UNDERFLOW ) {
+ if( !zfx->status ) {
+ zs = zfx->opaque = xmalloc_clear( sizeof *zs );
+ init_uncompress( zfx, zs );
+ zfx->status = 1;
+ }
+
+#ifndef __riscos__
+ zs->next_out = buf;
+#else /* __riscos__ */
+ zs->next_out = (Bytef *) buf;
+#endif /* __riscos__ */
+ zs->avail_out = size;
+ zfx->outbufsize = size; /* needed only for calculation */
+ rc = do_uncompress( zfx, zs, a, ret_len );
+ }
+ else if( control == IOBUFCTRL_FLUSH ) {
+ if( !zfx->status ) {
+ PACKET pkt;
+ PKT_compressed cd;
+ if(zfx->algo != COMPRESS_ALGO_ZIP
+ && zfx->algo != COMPRESS_ALGO_ZLIB)
+ BUG();
+ memset( &cd, 0, sizeof cd );
+ cd.len = 0;
+ cd.algorithm = zfx->algo;
+ init_packet( &pkt );
+ pkt.pkttype = PKT_COMPRESSED;
+ pkt.pkt.compressed = &cd;
+ if( build_packet( a, &pkt ))
+ log_bug("build_packet(PKT_COMPRESSED) failed\n");
+ zs = zfx->opaque = xmalloc_clear( sizeof *zs );
+ init_compress( zfx, zs );
+ zfx->status = 2;
+ }
+
+#ifndef __riscos__
+ zs->next_in = buf;
+#else /* __riscos__ */
+ zs->next_in = (Bytef *) buf;
+#endif /* __riscos__ */
+ zs->avail_in = size;
+ rc = do_compress( zfx, zs, Z_NO_FLUSH, a );
+ }
+ else if( control == IOBUFCTRL_FREE ) {
+ if( zfx->status == 1 ) {
+ inflateEnd(zs);
+ xfree(zs);
+ zfx->opaque = NULL;
+ xfree(zfx->outbuf); zfx->outbuf = NULL;
+ }
+ else if( zfx->status == 2 ) {
+#ifndef __riscos__
+ zs->next_in = buf;
+#else /* __riscos__ */
+ zs->next_in = (Bytef *) buf;
+#endif /* __riscos__ */
+ zs->avail_in = 0;
+ do_compress( zfx, zs, Z_FINISH, a );
+ deflateEnd(zs);
+ xfree(zs);
+ zfx->opaque = NULL;
+ xfree(zfx->outbuf); zfx->outbuf = NULL;
+ }
+ if (zfx->release)
+ zfx->release (zfx);
+ }
+ else if( control == IOBUFCTRL_DESC )
+ *(char**)buf = "compress_filter";
+ return rc;
+}
+
+
+static void
+release_context (compress_filter_context_t *ctx)
+{
+ xfree (ctx);
+}
+
+/****************
+ * Handle a compressed packet
+ */
+int
+handle_compressed( void *procctx, PKT_compressed *cd,
+ int (*callback)(IOBUF, void *), void *passthru )
+{
+ compress_filter_context_t *cfx;
+ int rc;
+
+ if(check_compress_algo(cd->algorithm))
+ return G10ERR_COMPR_ALGO;
+ cfx = xmalloc_clear (sizeof *cfx);
+ cfx->release = release_context;
+ cfx->algo = cd->algorithm;
+ push_compress_filter(cd->buf,cfx,cd->algorithm);
+ if( callback )
+ rc = callback(cd->buf, passthru );
+ else
+ rc = proc_packets(procctx, cd->buf);
+ cd->buf = NULL;
+ return rc;
+}
+
+void
+push_compress_filter(IOBUF out,compress_filter_context_t *zfx,int algo)
+{
+ push_compress_filter2(out,zfx,algo,0);
+}
+
+void
+push_compress_filter2(IOBUF out,compress_filter_context_t *zfx,
+ int algo,int rel)
+{
+ if(algo>=0)
+ zfx->algo=algo;
+ else
+ zfx->algo=DEFAULT_COMPRESS_ALGO;
+
+ switch(zfx->algo)
+ {
+ case COMPRESS_ALGO_NONE:
+ break;
+
+ case COMPRESS_ALGO_ZIP:
+ case COMPRESS_ALGO_ZLIB:
+ iobuf_push_filter2(out,compress_filter,zfx,rel);
+ break;
+
+#ifdef HAVE_BZIP2
+ case COMPRESS_ALGO_BZIP2:
+ iobuf_push_filter2(out,compress_filter_bz2,zfx,rel);
+ break;
+#endif
+
+ default:
+ BUG();
+ }
+}
diff --git a/g10/dearmor.c b/g10/dearmor.c
new file mode 100644
index 0000000..8d7c60a
--- /dev/null
+++ b/g10/dearmor.c
@@ -0,0 +1,137 @@
+/* dearmor.c - Armor utility
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "errors.h"
+#include "iobuf.h"
+#include "memory.h"
+#include "util.h"
+#include "filter.h"
+#include "packet.h"
+#include "options.h"
+#include "main.h"
+#include "i18n.h"
+
+/****************
+ * Take an armor file and write it out without armor
+ */
+int
+dearmor_file( const char *fname )
+{
+ armor_filter_context_t afx;
+ IOBUF inp = NULL, out = NULL;
+ int rc = 0;
+ int c;
+
+ memset( &afx, 0, sizeof afx);
+
+ /* prepare iobufs */
+ inp = iobuf_open(fname);
+ if (inp && is_secured_file (iobuf_get_fd (inp)))
+ {
+ iobuf_close (inp);
+ inp = NULL;
+ errno = EPERM;
+ }
+ if (!inp) {
+ log_error(_("can't open `%s': %s\n"), fname? fname: "[stdin]",
+ strerror(errno) );
+ rc = G10ERR_OPEN_FILE;
+ goto leave;
+ }
+
+ iobuf_push_filter( inp, armor_filter, &afx );
+
+ if( (rc = open_outfile( fname, 0, &out )) )
+ goto leave;
+
+
+
+ while( (c = iobuf_get(inp)) != -1 )
+ iobuf_put( out, c );
+
+
+ leave:
+ if( rc )
+ iobuf_cancel(out);
+ else
+ iobuf_close(out);
+ iobuf_close(inp);
+ return rc;
+}
+
+
+/****************
+ * Take file and write it out with armor
+ */
+int
+enarmor_file( const char *fname )
+{
+ armor_filter_context_t afx;
+ IOBUF inp = NULL, out = NULL;
+ int rc = 0;
+ int c;
+
+ memset( &afx, 0, sizeof afx);
+
+ /* prepare iobufs */
+ inp = iobuf_open(fname);
+ if (inp && is_secured_file (iobuf_get_fd (inp)))
+ {
+ iobuf_close (inp);
+ inp = NULL;
+ errno = EPERM;
+ }
+ if (!inp) {
+ log_error(_("can't open `%s': %s\n"), fname? fname: "[stdin]",
+ strerror(errno) );
+ rc = G10ERR_OPEN_FILE;
+ goto leave;
+ }
+
+
+ if( (rc = open_outfile( fname, 1, &out )) )
+ goto leave;
+
+ afx.what = 4;
+ afx.hdrlines = "Comment: Use \"gpg --dearmor\" for unpacking\n";
+ iobuf_push_filter( out, armor_filter, &afx );
+
+ while( (c = iobuf_get(inp)) != -1 )
+ iobuf_put( out, c );
+
+
+ leave:
+ if( rc )
+ iobuf_cancel(out);
+ else
+ iobuf_close(out);
+ iobuf_close(inp);
+ return rc;
+}
+
+
diff --git a/g10/decrypt.c b/g10/decrypt.c
new file mode 100644
index 0000000..171e265
--- /dev/null
+++ b/g10/decrypt.c
@@ -0,0 +1,191 @@
+/* decrypt.c - verify signed data
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003
+ * 2004 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "options.h"
+#include "packet.h"
+#include "errors.h"
+#include "iobuf.h"
+#include "keydb.h"
+#include "memory.h"
+#include "util.h"
+#include "main.h"
+#include "status.h"
+#include "i18n.h"
+
+
+
+/****************
+ * Assume that the input is an encrypted message and decrypt
+ * (and if signed, verify the signature on) it.
+ * This command differs from the default operation, as it never
+ * writes to the filename which is included in the file and it
+ * rejects files which don't begin with an encrypted message.
+ */
+
+int
+decrypt_message( const char *filename )
+{
+ IOBUF fp;
+ armor_filter_context_t afx;
+ progress_filter_context_t pfx;
+ int rc;
+ int no_out=0;
+
+ /* open the message file */
+ fp = iobuf_open(filename);
+ if (fp && is_secured_file (iobuf_get_fd (fp)))
+ {
+ iobuf_close (fp);
+ fp = NULL;
+ errno = EPERM;
+ }
+ if( !fp ) {
+ log_error(_("can't open `%s'\n"), print_fname_stdin(filename));
+ return G10ERR_OPEN_FILE;
+ }
+
+ handle_progress (&pfx, fp, filename);
+
+ if( !opt.no_armor ) {
+ if( use_armor_filter( fp ) ) {
+ memset( &afx, 0, sizeof afx);
+ iobuf_push_filter( fp, armor_filter, &afx );
+ }
+ }
+
+ if( !opt.outfile ) {
+ no_out = 1;
+ opt.outfile = "-";
+ }
+ rc = proc_encryption_packets( NULL, fp );
+ if( no_out )
+ opt.outfile = NULL;
+ iobuf_close(fp);
+ return rc;
+}
+
+void
+decrypt_messages(int nfiles, char *files[])
+{
+ IOBUF fp;
+ armor_filter_context_t afx;
+ progress_filter_context_t pfx;
+ char *p, *output = NULL;
+ int rc=0,use_stdin=0;
+ unsigned int lno=0;
+
+ if (opt.outfile)
+ {
+ log_error(_("--output doesn't work for this command\n"));
+ return;
+
+ }
+
+ if(!nfiles)
+ use_stdin=1;
+
+ for(;;)
+ {
+ char line[2048];
+ char *filename=NULL;
+
+ if(use_stdin)
+ {
+ if(fgets(line, DIM(line), stdin))
+ {
+ lno++;
+ if (!*line || line[strlen(line)-1] != '\n')
+ log_error("input line %u too long or missing LF\n", lno);
+ else
+ {
+ line[strlen(line)-1] = '\0';
+ filename=line;
+ }
+ }
+ }
+ else
+ {
+ if(nfiles)
+ {
+ filename=*files;
+ nfiles--;
+ files++;
+ }
+ }
+
+ if(filename==NULL)
+ break;
+
+ print_file_status(STATUS_FILE_START, filename, 3);
+ output = make_outfile_name(filename);
+ if (!output)
+ goto next_file;
+ fp = iobuf_open(filename);
+ if (fp)
+ iobuf_ioctl (fp,3,1,NULL); /* disable fd caching */
+ if (fp && is_secured_file (iobuf_get_fd (fp)))
+ {
+ iobuf_close (fp);
+ fp = NULL;
+ errno = EPERM;
+ }
+ if (!fp)
+ {
+ log_error(_("can't open `%s'\n"), print_fname_stdin(filename));
+ goto next_file;
+ }
+
+ handle_progress (&pfx, fp, filename);
+
+ if (!opt.no_armor)
+ {
+ if (use_armor_filter(fp))
+ {
+ memset(&afx, 0, sizeof afx);
+ iobuf_push_filter(fp, armor_filter, &afx);
+ }
+ }
+ rc = proc_packets(NULL, fp);
+ iobuf_close(fp);
+ if (rc)
+ log_error("%s: decryption failed: %s\n", print_fname_stdin(filename),
+ g10_errstr(rc));
+ p = get_last_passphrase();
+ set_next_passphrase(p);
+ xfree (p);
+
+ next_file:
+ /* Note that we emit file_done even after an error. */
+ write_status( STATUS_FILE_DONE );
+ iobuf_ioctl( NULL, 2, 0, NULL); /* Invalidate entire cache. */
+ xfree(output);
+ }
+
+ set_next_passphrase(NULL);
+}
diff --git a/g10/delkey.c b/g10/delkey.c
new file mode 100644
index 0000000..354851d
--- /dev/null
+++ b/g10/delkey.c
@@ -0,0 +1,221 @@
+/* delkey.c - delete keys
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2004,
+ * 2005 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "options.h"
+#include "packet.h"
+#include "errors.h"
+#include "iobuf.h"
+#include "keydb.h"
+#include "memory.h"
+#include "util.h"
+#include "main.h"
+#include "trustdb.h"
+#include "filter.h"
+#include "ttyio.h"
+#include "status.h"
+#include "i18n.h"
+
+
+/****************
+ * Delete a public or secret key from a keyring.
+ * r_sec_avail will be set if a secret key is available and the public
+ * key can't be deleted for that reason.
+ */
+static int
+do_delete_key( const char *username, int secret, int force, int *r_sec_avail )
+{
+ int rc = 0;
+ KBNODE keyblock = NULL;
+ KBNODE node;
+ KEYDB_HANDLE hd = keydb_new (secret);
+ PKT_public_key *pk = NULL;
+ PKT_secret_key *sk = NULL;
+ u32 keyid[2];
+ int okay=0;
+ int yes;
+ KEYDB_SEARCH_DESC desc;
+ int exactmatch;
+
+ *r_sec_avail = 0;
+
+ /* search the userid */
+ classify_user_id (username, &desc);
+ exactmatch = (desc.mode == KEYDB_SEARCH_MODE_FPR
+ || desc.mode == KEYDB_SEARCH_MODE_FPR16
+ || desc.mode == KEYDB_SEARCH_MODE_FPR20);
+ rc = desc.mode? keydb_search (hd, &desc, 1):G10ERR_INV_USER_ID;
+ if (rc) {
+ log_error (_("key \"%s\" not found: %s\n"), username, g10_errstr (rc));
+ write_status_text( STATUS_DELETE_PROBLEM, "1" );
+ goto leave;
+ }
+
+ /* read the keyblock */
+ rc = keydb_get_keyblock (hd, &keyblock );
+ if (rc) {
+ log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) );
+ goto leave;
+ }
+
+ /* get the keyid from the keyblock */
+ node = find_kbnode( keyblock, secret? PKT_SECRET_KEY:PKT_PUBLIC_KEY );
+ if( !node ) {
+ log_error("Oops; key not found anymore!\n");
+ rc = G10ERR_GENERAL;
+ goto leave;
+ }
+
+ if( secret )
+ {
+ sk = node->pkt->pkt.secret_key;
+ keyid_from_sk( sk, keyid );
+ }
+ else
+ {
+ /* public */
+ pk = node->pkt->pkt.public_key;
+ keyid_from_pk( pk, keyid );
+
+ if(!force)
+ {
+ rc = seckey_available( keyid );
+ if( !rc )
+ {
+ *r_sec_avail = 1;
+ rc = -1;
+ goto leave;
+ }
+ else if( rc != G10ERR_NO_SECKEY )
+ log_error("%s: get secret key: %s\n", username, g10_errstr(rc) );
+ else
+ rc = 0;
+ }
+ }
+
+ if( rc )
+ rc = 0;
+ else if (opt.batch && exactmatch)
+ okay++;
+ else if( opt.batch && secret )
+ {
+ log_error(_("can't do this in batch mode\n"));
+ log_info (_("(unless you specify the key by fingerprint)\n"));
+ }
+ else if( opt.batch && opt.answer_yes )
+ okay++;
+ else if( opt.batch )
+ {
+ log_error(_("can't do this in batch mode without \"--yes\"\n"));
+ log_info (_("(unless you specify the key by fingerprint)\n"));
+ }
+ else {
+ if( secret )
+ print_seckey_info( sk );
+ else
+ print_pubkey_info(NULL, pk );
+ tty_printf( "\n" );
+
+ yes = cpr_get_answer_is_yes( secret? "delete_key.secret.okay"
+ : "delete_key.okay",
+ _("Delete this key from the keyring? (y/N) "));
+ if( !cpr_enabled() && secret && yes ) {
+ /* I think it is not required to check a passphrase; if
+ * the user is so stupid as to let others access his secret keyring
+ * (and has no backup) - it is up him to read some very
+ * basic texts about security.
+ */
+ yes = cpr_get_answer_is_yes("delete_key.secret.okay",
+ _("This is a secret key! - really delete? (y/N) "));
+ }
+ if( yes )
+ okay++;
+ }
+
+
+ if( okay ) {
+ rc = keydb_delete_keyblock (hd);
+ if (rc) {
+ log_error (_("deleting keyblock failed: %s\n"), g10_errstr(rc) );
+ goto leave;
+ }
+
+ /* Note that the ownertrust being cleared will trigger a
+ revalidation_mark(). This makes sense - only deleting keys
+ that have ownertrust set should trigger this. */
+
+ if (!secret && pk && clear_ownertrusts (pk)) {
+ if (opt.verbose)
+ log_info (_("ownertrust information cleared\n"));
+ }
+ }
+
+ leave:
+ keydb_release (hd);
+ release_kbnode (keyblock);
+ return rc;
+}
+
+/****************
+ * Delete a public or secret key from a keyring.
+ */
+int
+delete_keys( STRLIST names, int secret, int allow_both )
+{
+ int rc, avail, force=(!allow_both && !secret && opt.expert);
+
+ /* Force allows us to delete a public key even if a secret key
+ exists. */
+
+ for(;names;names=names->next) {
+ rc = do_delete_key (names->d, secret, force, &avail );
+ if ( rc && avail ) {
+ if ( allow_both ) {
+ rc = do_delete_key (names->d, 1, 0, &avail );
+ if ( !rc )
+ rc = do_delete_key (names->d, 0, 0, &avail );
+ }
+ else {
+ log_error(_(
+ "there is a secret key for public key \"%s\"!\n"),names->d);
+ log_info(_(
+ "use option \"--delete-secret-keys\" to delete it first.\n"));
+ write_status_text( STATUS_DELETE_PROBLEM, "2" );
+ return rc;
+ }
+ }
+
+ if(rc) {
+ log_error("%s: delete key failed: %s\n", names->d, g10_errstr(rc) );
+ return rc;
+ }
+ }
+
+ return 0;
+}
diff --git a/g10/encode.c b/g10/encode.c
new file mode 100644
index 0000000..251ea3e
--- /dev/null
+++ b/g10/encode.c
@@ -0,0 +1,877 @@
+/* encode.c - encode data
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "options.h"
+#include "packet.h"
+#include "errors.h"
+#include "iobuf.h"
+#include "keydb.h"
+#include "memory.h"
+#include "util.h"
+#include "main.h"
+#include "filter.h"
+#include "trustdb.h"
+#include "i18n.h"
+#include "status.h"
+
+static int encode_simple( const char *filename, int mode, int use_seskey );
+static int write_pubkey_enc_from_list( PK_LIST pk_list, DEK *dek, IOBUF out );
+
+/****************
+ * Encode FILENAME with only the symmetric cipher. Take input from
+ * stdin if FILENAME is NULL.
+ */
+int
+encode_symmetric( const char *filename )
+{
+ return encode_simple( filename, 1, 0 );
+}
+
+/****************
+ * Encode FILENAME as a literal data packet only. Take input from
+ * stdin if FILENAME is NULL.
+ */
+int
+encode_store( const char *filename )
+{
+ return encode_simple( filename, 0, 0 );
+}
+
+static void
+encode_seskey( DEK *dek, DEK **seskey, byte *enckey )
+{
+ CIPHER_HANDLE hd;
+ byte buf[33];
+
+ assert ( dek->keylen <= 32 );
+ if(!*seskey)
+ {
+ *seskey=xmalloc_clear(sizeof(DEK));
+ (*seskey)->keylen=dek->keylen;
+ (*seskey)->algo=dek->algo;
+ make_session_key(*seskey);
+ /*log_hexdump( "thekey", c->key, c->keylen );*/
+ }
+
+ buf[0] = (*seskey)->algo;
+ memcpy( buf + 1, (*seskey)->key, (*seskey)->keylen );
+
+ hd = cipher_open( dek->algo, CIPHER_MODE_CFB, 1 );
+ cipher_setkey( hd, dek->key, dek->keylen );
+ cipher_setiv( hd, NULL, 0 );
+ cipher_encrypt( hd, buf, buf, (*seskey)->keylen + 1 );
+ cipher_close( hd );
+
+ memcpy( enckey, buf, (*seskey)->keylen + 1 );
+ wipememory( buf, sizeof buf ); /* burn key */
+}
+
+/* We try very hard to use a MDC */
+static int
+use_mdc(PK_LIST pk_list,int algo)
+{
+ /* RFC-1991 and 2440 don't have MDC */
+ if(RFC1991 || RFC2440)
+ return 0;
+
+ /* --force-mdc overrides --disable-mdc */
+ if(opt.force_mdc)
+ return 1;
+
+ if(opt.disable_mdc)
+ return 0;
+
+ /* Do the keys really support MDC? */
+
+ if(select_mdc_from_pklist(pk_list))
+ return 1;
+
+ /* The keys don't support MDC, so now we do a bit of a hack - if any
+ of the AESes or TWOFISH are in the prefs, we assume that the user
+ can handle a MDC. This is valid for PGP 7, which can handle MDCs
+ though it will not generate them. 2440bis allows this, by the
+ way. */
+
+ if(select_algo_from_prefs(pk_list,PREFTYPE_SYM,
+ CIPHER_ALGO_AES,NULL)==CIPHER_ALGO_AES)
+ return 1;
+
+ if(select_algo_from_prefs(pk_list,PREFTYPE_SYM,
+ CIPHER_ALGO_AES192,NULL)==CIPHER_ALGO_AES192)
+ return 1;
+
+ if(select_algo_from_prefs(pk_list,PREFTYPE_SYM,
+ CIPHER_ALGO_AES256,NULL)==CIPHER_ALGO_AES256)
+ return 1;
+
+ if(select_algo_from_prefs(pk_list,PREFTYPE_SYM,
+ CIPHER_ALGO_TWOFISH,NULL)==CIPHER_ALGO_TWOFISH)
+ return 1;
+
+ /* Last try. Use MDC for the modern ciphers. */
+
+ if(cipher_get_blocksize(algo)!=8)
+ return 1;
+
+ return 0; /* No MDC */
+}
+
+/* We don't want to use use_seskey yet because older gnupg versions
+ can't handle it, and there isn't really any point unless we're
+ making a message that can be decrypted by a public key or
+ passphrase. */
+static int
+encode_simple( const char *filename, int mode, int use_seskey )
+{
+ IOBUF inp, out;
+ PACKET pkt;
+ PKT_plaintext *pt = NULL;
+ STRING2KEY *s2k = NULL;
+ byte enckey[33];
+ int rc = 0;
+ int seskeylen = 0;
+ u32 filesize;
+ cipher_filter_context_t cfx;
+ armor_filter_context_t afx;
+ compress_filter_context_t zfx;
+ text_filter_context_t tfx;
+ progress_filter_context_t pfx;
+ int do_compress = !RFC1991 && default_compress_algo();
+
+ memset( &cfx, 0, sizeof cfx);
+ memset( &afx, 0, sizeof afx);
+ memset( &zfx, 0, sizeof zfx);
+ memset( &tfx, 0, sizeof tfx);
+ init_packet(&pkt);
+
+ /* prepare iobufs */
+ inp = iobuf_open(filename);
+ if (inp)
+ iobuf_ioctl (inp,3,1,NULL); /* disable fd caching */
+ if (inp && is_secured_file (iobuf_get_fd (inp)))
+ {
+ iobuf_close (inp);
+ inp = NULL;
+ errno = EPERM;
+ }
+ if( !inp ) {
+ log_error(_("can't open `%s': %s\n"), filename? filename: "[stdin]",
+ strerror(errno) );
+ return G10ERR_OPEN_FILE;
+ }
+
+ handle_progress (&pfx, inp, filename);
+
+ if( opt.textmode )
+ iobuf_push_filter( inp, text_filter, &tfx );
+
+ /* Due the the fact that we use don't use an IV to encrypt the
+ session key we can't use the new mode with RFC1991 because
+ it has no S2K salt. RFC1991 always uses simple S2K. */
+ if ( RFC1991 && use_seskey )
+ use_seskey = 0;
+
+ cfx.dek = NULL;
+ if( mode ) {
+ s2k = xmalloc_clear( sizeof *s2k );
+ s2k->mode = RFC1991? 0:opt.s2k_mode;
+ s2k->hash_algo=S2K_DIGEST_ALGO;
+ cfx.dek = passphrase_to_dek( NULL, 0,
+ default_cipher_algo(), s2k, 2,
+ NULL, NULL);
+ if( !cfx.dek || !cfx.dek->keylen ) {
+ rc = G10ERR_PASSPHRASE;
+ xfree(cfx.dek);
+ xfree(s2k);
+ iobuf_close(inp);
+ log_error(_("error creating passphrase: %s\n"), g10_errstr(rc) );
+ return rc;
+ }
+ if (use_seskey && s2k->mode != 1 && s2k->mode != 3) {
+ use_seskey = 0;
+ log_info (_("can't use a symmetric ESK packet "
+ "due to the S2K mode\n"));
+ }
+
+ if ( use_seskey )
+ {
+ DEK *dek = NULL;
+ seskeylen = cipher_get_keylen( default_cipher_algo() ) / 8;
+ encode_seskey( cfx.dek, &dek, enckey );
+ xfree( cfx.dek ); cfx.dek = dek;
+ }
+
+ if(opt.verbose)
+ log_info(_("using cipher %s\n"),
+ cipher_algo_to_string(cfx.dek->algo));
+
+ cfx.dek->use_mdc=use_mdc(NULL,cfx.dek->algo);
+ }
+
+ if (do_compress && cfx.dek && cfx.dek->use_mdc
+ && is_file_compressed(filename, &rc))
+ {
+ if (opt.verbose)
+ log_info(_("`%s' already compressed\n"), filename);
+ do_compress = 0;
+ }
+
+ if( rc || (rc = open_outfile( filename, opt.armor? 1:0, &out )) ) {
+ iobuf_cancel(inp);
+ xfree(cfx.dek);
+ xfree(s2k);
+ return rc;
+ }
+
+ if( opt.armor )
+ iobuf_push_filter( out, armor_filter, &afx );
+
+ if( s2k && !RFC1991 ) {
+ PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc + seskeylen + 1 );
+ enc->version = 4;
+ enc->cipher_algo = cfx.dek->algo;
+ enc->s2k = *s2k;
+ if ( use_seskey && seskeylen ) {
+ enc->seskeylen = seskeylen + 1; /* algo id */
+ memcpy( enc->seskey, enckey, seskeylen + 1 );
+ }
+ pkt.pkttype = PKT_SYMKEY_ENC;
+ pkt.pkt.symkey_enc = enc;
+ if( (rc = build_packet( out, &pkt )) )
+ log_error("build symkey packet failed: %s\n", g10_errstr(rc) );
+ xfree(enc);
+ }
+
+ if (!opt.no_literal)
+ pt=setup_plaintext_name(filename,inp);
+
+ /* Note that PGP 5 has problems decrypting symmetrically encrypted
+ data if the file length is in the inner packet. It works when
+ only partial length headers are use. In the past, we always
+ used partial body length here, but since PGP 2, PGP 6, and PGP
+ 7 need the file length, and nobody should be using PGP 5
+ nowadays anyway, this is now set to the file length. Note also
+ that this only applies to the RFC-1991 style symmetric
+ messages, and not the RFC-2440 style. PGP 6 and 7 work with
+ either partial length or fixed length with the new style
+ messages. */
+
+ if ( !iobuf_is_pipe_filename (filename) && *filename && !opt.textmode )
+ {
+ off_t tmpsize;
+ int overflow;
+
+ if ( !(tmpsize = iobuf_get_filelength(inp, &overflow))
+ && !overflow )
+ log_info(_("WARNING: `%s' is an empty file\n"), filename );
+ /* We can't encode the length of very large files because
+ OpenPGP uses only 32 bit for file sizes. So if the the
+ size of a file is larger than 2^32 minus some bytes for
+ packet headers, we switch to partial length encoding. */
+ if ( tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
+ filesize = tmpsize;
+ else
+ filesize = 0;
+ }
+ else
+ filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */
+
+ if (!opt.no_literal) {
+ pt->timestamp = make_timestamp();
+ pt->mode = opt.textmode? 't' : 'b';
+ pt->len = filesize;
+ pt->new_ctb = !pt->len && !RFC1991;
+ pt->buf = inp;
+ pkt.pkttype = PKT_PLAINTEXT;
+ pkt.pkt.plaintext = pt;
+ cfx.datalen = filesize && !do_compress ? calc_packet_length( &pkt ) : 0;
+ }
+ else
+ {
+ cfx.datalen = filesize && !do_compress ? filesize : 0;
+ pkt.pkttype = 0;
+ pkt.pkt.generic = NULL;
+ }
+
+ /* register the cipher filter */
+ if( mode )
+ iobuf_push_filter( out, cipher_filter, &cfx );
+ /* register the compress filter */
+ if( do_compress )
+ {
+ if (cfx.dek && cfx.dek->use_mdc)
+ zfx.new_ctb = 1;
+ push_compress_filter(out,&zfx,default_compress_algo());
+ }
+
+ /* do the work */
+ if (!opt.no_literal) {
+ if( (rc = build_packet( out, &pkt )) )
+ log_error("build_packet failed: %s\n", g10_errstr(rc) );
+ }
+ else {
+ /* user requested not to create a literal packet,
+ * so we copy the plain data */
+ byte copy_buffer[4096];
+ int bytes_copied;
+ while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1)
+ if (iobuf_write(out, copy_buffer, bytes_copied) == -1) {
+ rc = G10ERR_WRITE_FILE;
+ log_error("copying input to output failed: %s\n", g10_errstr(rc) );
+ break;
+ }
+ wipememory(copy_buffer, 4096); /* burn buffer */
+ }
+
+ /* finish the stuff */
+ iobuf_close(inp);
+ if (rc)
+ iobuf_cancel(out);
+ else {
+ iobuf_close(out); /* fixme: check returncode */
+ if (mode)
+ write_status( STATUS_END_ENCRYPTION );
+ }
+ if (pt)
+ pt->buf = NULL;
+ free_packet(&pkt);
+ xfree(cfx.dek);
+ xfree(s2k);
+ return rc;
+}
+
+int
+setup_symkey(STRING2KEY **symkey_s2k,DEK **symkey_dek)
+{
+ *symkey_s2k=xmalloc_clear(sizeof(STRING2KEY));
+ (*symkey_s2k)->mode = opt.s2k_mode;
+ (*symkey_s2k)->hash_algo = S2K_DIGEST_ALGO;
+
+ *symkey_dek=passphrase_to_dek(NULL,0,opt.s2k_cipher_algo,
+ *symkey_s2k,2,NULL,NULL);
+ if(!*symkey_dek || !(*symkey_dek)->keylen)
+ {
+ xfree(*symkey_dek);
+ xfree(*symkey_s2k);
+ return G10ERR_PASSPHRASE;
+ }
+
+ return 0;
+}
+
+static int
+write_symkey_enc(STRING2KEY *symkey_s2k,DEK *symkey_dek,DEK *dek,IOBUF out)
+{
+ int rc,seskeylen=cipher_get_keylen(dek->algo)/8;
+
+ PKT_symkey_enc *enc;
+ byte enckey[33];
+ PACKET pkt;
+
+ enc=xmalloc_clear(sizeof(PKT_symkey_enc)+seskeylen+1);
+ encode_seskey(symkey_dek,&dek,enckey);
+
+ enc->version = 4;
+ enc->cipher_algo = opt.s2k_cipher_algo;
+ enc->s2k = *symkey_s2k;
+ enc->seskeylen = seskeylen + 1; /* algo id */
+ memcpy( enc->seskey, enckey, seskeylen + 1 );
+
+ pkt.pkttype = PKT_SYMKEY_ENC;
+ pkt.pkt.symkey_enc = enc;
+
+ if((rc=build_packet(out,&pkt)))
+ log_error("build symkey_enc packet failed: %s\n",g10_errstr(rc));
+
+ xfree(enc);
+ return rc;
+}
+
+/****************
+ * Encrypt the file with the given userids (or ask if none
+ * is supplied).
+ */
+int
+encode_crypt( const char *filename, STRLIST remusr, int use_symkey )
+{
+ IOBUF inp = NULL, out = NULL;
+ PACKET pkt;
+ PKT_plaintext *pt = NULL;
+ DEK *symkey_dek = NULL;
+ STRING2KEY *symkey_s2k = NULL;
+ int rc = 0, rc2 = 0;
+ u32 filesize;
+ cipher_filter_context_t cfx;
+ armor_filter_context_t afx;
+ compress_filter_context_t zfx;
+ text_filter_context_t tfx;
+ progress_filter_context_t pfx;
+ PK_LIST pk_list,work_list;
+ int do_compress = opt.compress_algo && !RFC1991;
+
+ memset( &cfx, 0, sizeof cfx);
+ memset( &afx, 0, sizeof afx);
+ memset( &zfx, 0, sizeof zfx);
+ memset( &tfx, 0, sizeof tfx);
+ init_packet(&pkt);
+
+ if(use_symkey
+ && (rc=setup_symkey(&symkey_s2k,&symkey_dek)))
+ return rc;
+
+ if( (rc=build_pk_list( remusr, &pk_list, PUBKEY_USAGE_ENC)) )
+ return rc;
+
+ if(PGP2) {
+ for(work_list=pk_list; work_list; work_list=work_list->next)
+ if(!(is_RSA(work_list->pk->pubkey_algo) &&
+ nbits_from_pk(work_list->pk)<=2048))
+ {
+ log_info(_("you can only encrypt to RSA keys of 2048 bits or "
+ "less in --pgp2 mode\n"));
+ compliance_failure();
+ break;
+ }
+ }
+
+ /* prepare iobufs */
+ inp = iobuf_open(filename);
+ if (inp)
+ iobuf_ioctl (inp,3,1,NULL); /* disable fd caching */
+ if (inp && is_secured_file (iobuf_get_fd (inp)))
+ {
+ iobuf_close (inp);
+ inp = NULL;
+ errno = EPERM;
+ }
+ if( !inp ) {
+ log_error(_("can't open `%s': %s\n"), filename? filename: "[stdin]",
+ strerror(errno) );
+ rc = G10ERR_OPEN_FILE;
+ goto leave;
+ }
+ else if( opt.verbose )
+ log_info(_("reading from `%s'\n"), filename? filename: "[stdin]");
+
+ handle_progress (&pfx, inp, filename);
+
+ if( opt.textmode )
+ iobuf_push_filter( inp, text_filter, &tfx );
+
+ if( (rc = open_outfile( filename, opt.armor? 1:0, &out )) )
+ goto leave;
+
+ if( opt.armor )
+ iobuf_push_filter( out, armor_filter, &afx );
+
+ /* create a session key */
+ cfx.dek = xmalloc_secure_clear (sizeof *cfx.dek);
+ if( !opt.def_cipher_algo ) { /* try to get it from the prefs */
+ cfx.dek->algo = select_algo_from_prefs(pk_list,PREFTYPE_SYM,-1,NULL);
+ /* The only way select_algo_from_prefs can fail here is when
+ mixing v3 and v4 keys, as v4 keys have an implicit
+ preference entry for 3DES, and the pk_list cannot be empty.
+ In this case, use 3DES anyway as it's the safest choice -
+ perhaps the v3 key is being used in an OpenPGP
+ implementation and we know that the implementation behind
+ any v4 key can handle 3DES. */
+ if( cfx.dek->algo == -1 ) {
+ cfx.dek->algo = CIPHER_ALGO_3DES;
+
+ if( PGP2 ) {
+ log_info(_("unable to use the IDEA cipher for all of the keys "
+ "you are encrypting to.\n"));
+ compliance_failure();
+ }
+ }
+ }
+ else {
+ if(!opt.expert &&
+ select_algo_from_prefs(pk_list,PREFTYPE_SYM,
+ opt.def_cipher_algo,NULL)!=opt.def_cipher_algo)
+ log_info(_("WARNING: forcing symmetric cipher %s (%d)"
+ " violates recipient preferences\n"),
+ cipher_algo_to_string(opt.def_cipher_algo),
+ opt.def_cipher_algo);
+
+ cfx.dek->algo = opt.def_cipher_algo;
+ }
+
+ cfx.dek->use_mdc=use_mdc(pk_list,cfx.dek->algo);
+
+ /* Only do the is-file-already-compressed check if we are using a
+ MDC. This forces compressed files to be re-compressed if we do
+ not have a MDC to give some protection against chosen
+ ciphertext attacks. */
+
+ if (do_compress && cfx.dek->use_mdc && is_file_compressed(filename, &rc2) )
+ {
+ if (opt.verbose)
+ log_info(_("`%s' already compressed\n"), filename);
+ do_compress = 0;
+ }
+ if (rc2)
+ {
+ rc = rc2;
+ goto leave;
+ }
+
+ make_session_key( cfx.dek );
+ if( DBG_CIPHER )
+ log_hexdump("DEK is: ", cfx.dek->key, cfx.dek->keylen );
+
+ rc = write_pubkey_enc_from_list( pk_list, cfx.dek, out );
+ if( rc )
+ goto leave;
+
+ /* We put the passphrase (if any) after any public keys as this
+ seems to be the most useful on the recipient side - there is no
+ point in prompting a user for a passphrase if they have the
+ secret key needed to decrypt. */
+ if(use_symkey && (rc=write_symkey_enc(symkey_s2k,symkey_dek,cfx.dek,out)))
+ goto leave;
+
+ if (!opt.no_literal) {
+ /* setup the inner packet */
+ if( filename || opt.set_filename ) {
+ char *s = make_basename( opt.set_filename ? opt.set_filename
+ : filename,
+ iobuf_get_real_fname( inp ) );
+ pt = xmalloc( sizeof *pt + strlen(s) - 1 );
+ pt->namelen = strlen(s);
+ memcpy(pt->name, s, pt->namelen );
+ xfree(s);
+ }
+ else { /* no filename */
+ pt = xmalloc( sizeof *pt - 1 );
+ pt->namelen = 0;
+ }
+ }
+
+ if (!iobuf_is_pipe_filename (filename) && *filename && !opt.textmode )
+ {
+ off_t tmpsize;
+ int overflow;
+
+ if ( !(tmpsize = iobuf_get_filelength(inp, &overflow))
+ && !overflow )
+ log_info(_("WARNING: `%s' is an empty file\n"), filename );
+ /* We can't encode the length of very large files because
+ OpenPGP uses only 32 bit for file sizes. So if the the
+ size of a file is larger than 2^32 minus some bytes for
+ packet headers, we switch to partial length encoding. */
+ if (tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
+ filesize = tmpsize;
+ else
+ filesize = 0;
+ }
+ else
+ filesize = opt.set_filesize ? opt.set_filesize : 0; /* stdin */
+
+ if (!opt.no_literal) {
+ pt->timestamp = make_timestamp();
+ pt->mode = opt.textmode ? 't' : 'b';
+ pt->len = filesize;
+ pt->new_ctb = !pt->len && !RFC1991;
+ pt->buf = inp;
+ pkt.pkttype = PKT_PLAINTEXT;
+ pkt.pkt.plaintext = pt;
+ cfx.datalen = filesize && !do_compress? calc_packet_length( &pkt ) : 0;
+ }
+ else
+ cfx.datalen = filesize && !do_compress ? filesize : 0;
+
+ /* register the cipher filter */
+ iobuf_push_filter( out, cipher_filter, &cfx );
+
+ /* register the compress filter */
+ if( do_compress ) {
+ int compr_algo = opt.compress_algo;
+
+ if(compr_algo==-1)
+ {
+ if((compr_algo=
+ select_algo_from_prefs(pk_list,PREFTYPE_ZIP,-1,NULL))==-1)
+ compr_algo=DEFAULT_COMPRESS_ALGO;
+ /* Theoretically impossible to get here since uncompressed
+ is implicit. */
+ }
+ else if(!opt.expert &&
+ select_algo_from_prefs(pk_list,PREFTYPE_ZIP,
+ compr_algo,NULL)!=compr_algo)
+ log_info(_("WARNING: forcing compression algorithm %s (%d)"
+ " violates recipient preferences\n"),
+ compress_algo_to_string(compr_algo),compr_algo);
+
+ /* algo 0 means no compression */
+ if( compr_algo )
+ {
+ if (cfx.dek && cfx.dek->use_mdc)
+ zfx.new_ctb = 1;
+ push_compress_filter(out,&zfx,compr_algo);
+ }
+ }
+
+ /* do the work */
+ if (!opt.no_literal) {
+ if( (rc = build_packet( out, &pkt )) )
+ log_error("build_packet failed: %s\n", g10_errstr(rc) );
+ }
+ else {
+ /* user requested not to create a literal packet, so we copy
+ the plain data */
+ byte copy_buffer[4096];
+ int bytes_copied;
+ while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1)
+ if (iobuf_write(out, copy_buffer, bytes_copied) == -1) {
+ rc = G10ERR_WRITE_FILE;
+ log_error("copying input to output failed: %s\n",
+ g10_errstr(rc) );
+ break;
+ }
+ wipememory(copy_buffer, 4096); /* burn buffer */
+ }
+
+ /* finish the stuff */
+ leave:
+ iobuf_close(inp);
+ if( rc )
+ iobuf_cancel(out);
+ else {
+ iobuf_close(out); /* fixme: check returncode */
+ write_status( STATUS_END_ENCRYPTION );
+ }
+ if( pt )
+ pt->buf = NULL;
+ free_packet(&pkt);
+ xfree(cfx.dek);
+ xfree(symkey_dek);
+ xfree(symkey_s2k);
+ release_pk_list( pk_list );
+ return rc;
+}
+
+
+
+
+/****************
+ * Filter to do a complete public key encryption.
+ */
+int
+encrypt_filter( void *opaque, int control,
+ IOBUF a, byte *buf, size_t *ret_len)
+{
+ size_t size = *ret_len;
+ encrypt_filter_context_t *efx = opaque;
+ int rc=0;
+
+ if( control == IOBUFCTRL_UNDERFLOW ) { /* decrypt */
+ BUG(); /* not used */
+ }
+ else if( control == IOBUFCTRL_FLUSH ) { /* encrypt */
+ if( !efx->header_okay ) {
+ efx->cfx.dek = xmalloc_secure_clear( sizeof *efx->cfx.dek );
+
+ if( !opt.def_cipher_algo ) { /* try to get it from the prefs */
+ efx->cfx.dek->algo =
+ select_algo_from_prefs(efx->pk_list,PREFTYPE_SYM,-1,NULL);
+ if( efx->cfx.dek->algo == -1 ) {
+ /* because 3DES is implicitly in the prefs, this can only
+ * happen if we do not have any public keys in the list */
+ efx->cfx.dek->algo = DEFAULT_CIPHER_ALGO;
+ }
+ }
+ else {
+ if(!opt.expert &&
+ select_algo_from_prefs(efx->pk_list,PREFTYPE_SYM,
+ opt.def_cipher_algo,
+ NULL)!=opt.def_cipher_algo)
+ log_info(_("forcing symmetric cipher %s (%d) "
+ "violates recipient preferences\n"),
+ cipher_algo_to_string(opt.def_cipher_algo),
+ opt.def_cipher_algo);
+
+ efx->cfx.dek->algo = opt.def_cipher_algo;
+ }
+
+ efx->cfx.dek->use_mdc = use_mdc(efx->pk_list,efx->cfx.dek->algo);
+
+ make_session_key( efx->cfx.dek );
+ if( DBG_CIPHER )
+ log_hexdump("DEK is: ",
+ efx->cfx.dek->key, efx->cfx.dek->keylen );
+
+ rc = write_pubkey_enc_from_list( efx->pk_list, efx->cfx.dek, a );
+ if( rc )
+ return rc;
+
+ if(efx->symkey_s2k && efx->symkey_dek)
+ {
+ rc=write_symkey_enc(efx->symkey_s2k,efx->symkey_dek,
+ efx->cfx.dek,a);
+ if(rc)
+ return rc;
+ }
+
+ iobuf_push_filter( a, cipher_filter, &efx->cfx );
+
+ efx->header_okay = 1;
+ }
+ rc = iobuf_write( a, buf, size );
+
+ }
+ else if( control == IOBUFCTRL_FREE )
+ {
+ xfree(efx->symkey_dek);
+ xfree(efx->symkey_s2k);
+ }
+ else if( control == IOBUFCTRL_DESC ) {
+ *(char**)buf = "encrypt_filter";
+ }
+ 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 out )
+{
+ PACKET pkt;
+ PKT_public_key *pk;
+ PKT_pubkey_enc *enc;
+ int rc;
+
+ for( ; pk_list; pk_list = pk_list->next ) {
+ MPI 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_keyid || (pk_list->flags&1));
+
+ if(opt.throw_keyid && (PGP2 || PGP6 || PGP7 || PGP8))
+ {
+ log_info(_("you may not use %s while in %s mode\n"),
+ "--throw-keyid",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( dek, pubkey_nbits( pk->pubkey_algo,
+ pk->pkey ) );
+ rc = pubkey_encrypt( pk->pubkey_algo, enc->data, frame, pk->pkey );
+ mpi_free( frame );
+ if( rc )
+ log_error("pubkey_encrypt failed: %s\n", g10_errstr(rc) );
+ else {
+ if( opt.verbose ) {
+ char *ustr = get_user_id_string_native (enc->keyid);
+ log_info(_("%s/%s encrypted for: \"%s\"\n"),
+ pubkey_algo_to_string(enc->pubkey_algo),
+ cipher_algo_to_string(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", g10_errstr(rc));
+ }
+ free_pubkey_enc(enc);
+ if( rc )
+ return rc;
+ }
+ return 0;
+}
+
+void
+encode_crypt_files(int nfiles, char **files, STRLIST remusr)
+{
+ int rc = 0;
+
+ if (opt.outfile)
+ {
+ log_error(_("--output doesn't work for this command\n"));
+ return;
+ }
+
+ if (!nfiles)
+ {
+ char line[2048];
+ unsigned int lno = 0;
+ while ( fgets(line, DIM(line), stdin) )
+ {
+ lno++;
+ if (!*line || line[strlen(line)-1] != '\n')
+ {
+ log_error("input line %u too long or missing LF\n", lno);
+ return;
+ }
+ line[strlen(line)-1] = '\0';
+ print_file_status(STATUS_FILE_START, line, 2);
+ if ( (rc = encode_crypt(line, remusr, 0)) )
+ log_error("encryption of `%s' failed: %s\n",
+ print_fname_stdin(line), g10_errstr(rc) );
+ write_status( STATUS_FILE_DONE );
+ iobuf_ioctl( NULL, 2, 0, NULL); /* Invalidate entire cache. */
+ }
+ }
+ else
+ {
+ while (nfiles--)
+ {
+ print_file_status(STATUS_FILE_START, *files, 2);
+ if ( (rc = encode_crypt(*files, remusr, 0)) )
+ log_error("encryption of `%s' failed: %s\n",
+ print_fname_stdin(*files), g10_errstr(rc) );
+ write_status( STATUS_FILE_DONE );
+ iobuf_ioctl( NULL, 2, 0, NULL); /* Invalidate entire cache. */
+ files++;
+ }
+ }
+}
diff --git a/g10/encr-data.c b/g10/encr-data.c
new file mode 100644
index 0000000..acb090a
--- /dev/null
+++ b/g10/encr-data.c
@@ -0,0 +1,320 @@
+/* encr-data.c - process an encrypted data packet
+ * Copyright (C) 1998, 1999, 2000, 2001, 2005,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "util.h"
+#include "memory.h"
+#include "packet.h"
+#include "mpi.h"
+#include "cipher.h"
+#include "options.h"
+#include "i18n.h"
+
+
+static int mdc_decode_filter( void *opaque, int control, IOBUF a,
+ byte *buf, size_t *ret_len);
+static int decode_filter( void *opaque, int control, IOBUF a,
+ byte *buf, size_t *ret_len);
+
+typedef struct {
+ CIPHER_HANDLE cipher_hd;
+ MD_HANDLE mdc_hash;
+ char defer[22];
+ int defer_filled;
+ int eof_seen;
+ int refcount;
+} *decode_filter_ctx_t;
+
+
+/* Helper to release the decode context. */
+static void
+release_dfx_context (decode_filter_ctx_t dfx)
+{
+ if (!dfx)
+ return;
+
+ assert (dfx->refcount);
+ if ( !--dfx->refcount )
+ {
+ cipher_close (dfx->cipher_hd);
+ dfx->cipher_hd = NULL;
+ md_close (dfx->mdc_hash);
+ dfx->mdc_hash = NULL;
+ xfree (dfx);
+ }
+}
+
+
+/****************
+ * Decrypt the data, specified by ED with the key DEK.
+ */
+int
+decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek )
+{
+ decode_filter_ctx_t dfx;
+ byte *p;
+ int rc=0, c, i;
+ byte temp[32];
+ unsigned blocksize;
+ unsigned nprefix;
+
+
+ dfx = xcalloc (1, sizeof *dfx);
+ dfx->refcount = 1;
+
+ if( opt.verbose && !dek->algo_info_printed ) {
+ const char *s = cipher_algo_to_string( dek->algo );
+ if( s )
+ log_info(_("%s encrypted data\n"), s );
+ else
+ log_info(_("encrypted with unknown algorithm %d\n"), dek->algo );
+ dek->algo_info_printed = 1;
+ }
+ if( (rc=check_cipher_algo(dek->algo)) )
+ goto leave;
+ blocksize = cipher_get_blocksize(dek->algo);
+ if( !blocksize || blocksize > 16 )
+ log_fatal("unsupported blocksize %u\n", blocksize );
+ nprefix = blocksize;
+ if( ed->len && ed->len < (nprefix+2) )
+ BUG();
+
+ if( ed->mdc_method ) {
+ dfx->mdc_hash = md_open ( ed->mdc_method, 0 );
+ if ( DBG_HASHING )
+ md_start_debug (dfx->mdc_hash, "checkmdc");
+ }
+ dfx->cipher_hd = cipher_open ( dek->algo,
+ ed->mdc_method? CIPHER_MODE_CFB
+ : CIPHER_MODE_AUTO_CFB, 1 );
+ /* log_hexdump( "thekey", dek->key, dek->keylen );*/
+ rc = cipher_setkey ( dfx->cipher_hd, dek->key, dek->keylen );
+ if( rc == G10ERR_WEAK_KEY )
+ {
+ log_info(_("WARNING: message was encrypted with"
+ " a weak key in the symmetric cipher.\n"));
+ rc=0;
+ }
+ else if( rc )
+ {
+ log_error("key setup failed: %s\n", g10_errstr(rc) );
+ goto leave;
+
+ }
+ if (!ed->buf) {
+ log_error(_("problem handling encrypted packet\n"));
+ goto leave;
+ }
+
+ cipher_setiv ( dfx->cipher_hd, NULL, 0 );
+
+ if( ed->len ) {
+ for(i=0; i < (nprefix+2) && ed->len; i++, ed->len-- ) {
+ if( (c=iobuf_get(ed->buf)) == -1 )
+ break;
+ else
+ temp[i] = c;
+ }
+ }
+ else {
+ for(i=0; i < (nprefix+2); i++ )
+ if( (c=iobuf_get(ed->buf)) == -1 )
+ break;
+ else
+ temp[i] = c;
+ }
+ cipher_decrypt ( dfx->cipher_hd, temp, temp, nprefix+2);
+ cipher_sync ( dfx->cipher_hd );
+ p = temp;
+/* log_hexdump( "prefix", temp, nprefix+2 ); */
+ if(dek->symmetric
+ && (p[nprefix-2] != p[nprefix] || p[nprefix-1] != p[nprefix+1]) )
+ {
+ rc = G10ERR_BAD_KEY;
+ goto leave;
+ }
+
+ if ( dfx->mdc_hash )
+ md_write ( dfx->mdc_hash, temp, nprefix+2 );
+
+ dfx->refcount++;
+ if ( ed->mdc_method )
+ iobuf_push_filter( ed->buf, mdc_decode_filter, dfx );
+ else
+ iobuf_push_filter( ed->buf, decode_filter, dfx );
+
+ proc_packets( procctx, ed->buf );
+ ed->buf = NULL;
+ if( ed->mdc_method && dfx->eof_seen == 2 )
+ rc = G10ERR_INVALID_PACKET;
+ else if( ed->mdc_method ) { /* check the mdc */
+ /* We used to let parse-packet.c handle the MDC packet but
+ this turned out to be a problem with compressed packets:
+ With old style packets there is no length information
+ available and the decompressor uses an implicit end.
+ However we can't know this implicit end beforehand (:-) and
+ thus may feed the decompressor with more bytes than
+ actually needed. It would be possible to unread the extra
+ bytes but due to our weird iobuf system any unread is non
+ reliable due to filters already popped off. The easy and
+ sane solution is to care about the MDC packet only here and
+ never pass it to the packet parser. Fortunatley the
+ OpenPGP spec requires a strict format for the MDC packet so
+ that we know that 22 bytes are appended. */
+ int datalen = md_digest_length( ed->mdc_method );
+
+ assert (dfx->cipher_hd);
+ assert (dfx->mdc_hash);
+ cipher_decrypt ( dfx->cipher_hd, dfx->defer, dfx->defer, 22);
+ md_write ( dfx->mdc_hash, dfx->defer, 2);
+ md_final ( dfx->mdc_hash );
+ if (dfx->defer[0] != '\xd3' || dfx->defer[1] != '\x14' ) {
+ log_error("mdc_packet with invalid encoding\n");
+ rc = G10ERR_INVALID_PACKET;
+ }
+ else if ( datalen != 20
+ || memcmp(md_read( dfx->mdc_hash, 0 ), dfx->defer+2, datalen) )
+ rc = G10ERR_BAD_SIGN;
+ /*log_hexdump("MDC calculated:",md_read( dfx->mdc_hash, 0), datalen);*/
+ /*log_hexdump("MDC message :", dfx->defer, 20);*/
+ }
+
+
+ leave:
+ release_dfx_context (dfx);
+ return rc;
+}
+
+
+
+/* I think we should merge this with cipher_filter */
+static int
+mdc_decode_filter( void *opaque, int control, IOBUF a,
+ byte *buf, size_t *ret_len)
+{
+ decode_filter_ctx_t dfx = opaque;
+ size_t n, size = *ret_len;
+ int rc = 0;
+ int c;
+
+ if( control == IOBUFCTRL_UNDERFLOW && dfx->eof_seen ) {
+ *ret_len = 0;
+ rc = -1;
+ }
+ else if( control == IOBUFCTRL_UNDERFLOW ) {
+ assert(a);
+ assert( size > 44 );
+
+ /* get at least 20 bytes and put it somewhere ahead in the buffer */
+ for(n=22; n < 44 ; n++ ) {
+ if( (c = iobuf_get(a)) == -1 )
+ break;
+ buf[n] = c;
+ }
+ if( n == 44 ) {
+ /* we have enough stuff - flush the deferred stuff */
+ /* (we have asserted that the buffer is large enough) */
+ if( !dfx->defer_filled ) { /* the first time */
+ memcpy(buf, buf+22, 22 );
+ n = 22;
+ }
+ else {
+ memcpy(buf, dfx->defer, 22 );
+ }
+ /* now fill up */
+ for(; n < size; n++ ) {
+ if( (c = iobuf_get(a)) == -1 )
+ break;
+ buf[n] = c;
+ }
+ /* Move the last 22 bytes back to the defer buffer. */
+ /* (okay, we are wasting 22 bytes of supplied buffer) */
+ n -= 22;
+ memcpy( dfx->defer, buf+n, 22 );
+ dfx->defer_filled = 1;
+ }
+ else if( !dfx->defer_filled ) { /* eof seen buf empty defer */
+ /* this is bad because there is an incomplete hash */
+ n -= 22;
+ memcpy(buf, buf+22, n );
+ dfx->eof_seen = 2; /* eof with incomplete hash */
+ }
+ else { /* eof seen */
+ memcpy (buf, dfx->defer, 22 );
+ n -= 22;
+ memcpy( dfx->defer, buf+n, 22 );
+ dfx->eof_seen = 1; /* normal eof */
+ }
+
+ if( n ) {
+ if (dfx->cipher_hd)
+ cipher_decrypt( dfx->cipher_hd, buf, buf, n);
+ if (dfx->mdc_hash)
+ md_write( dfx->mdc_hash, buf, n );
+ }
+ else {
+ assert( dfx->eof_seen );
+ rc = -1; /* eof */
+ }
+ *ret_len = n;
+ }
+ else if ( control == IOBUFCTRL_FREE ) {
+ release_dfx_context (dfx);
+ }
+ else if( control == IOBUFCTRL_DESC ) {
+ *(char**)buf = "mdc_decode_filter";
+ }
+ return rc;
+}
+
+static int
+decode_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len)
+{
+ decode_filter_ctx_t fc = opaque;
+ size_t n, size = *ret_len;
+ int rc = 0;
+
+ if( control == IOBUFCTRL_UNDERFLOW ) {
+ assert(a);
+ n = iobuf_read( a, buf, size );
+ if( n == -1 ) n = 0;
+ if( n ) {
+ if (fc->cipher_hd)
+ cipher_decrypt( fc->cipher_hd, buf, buf, n);
+ }
+ else
+ rc = -1; /* eof */
+ *ret_len = n;
+ }
+ else if ( control == IOBUFCTRL_FREE ) {
+ release_dfx_context (fc);
+ }
+ else if( control == IOBUFCTRL_DESC ) {
+ *(char**)buf = "decode_filter";
+ }
+ return rc;
+}
+
diff --git a/g10/exec.c b/g10/exec.c
new file mode 100644
index 0000000..d99bb5e
--- /dev/null
+++ b/g10/exec.c
@@ -0,0 +1,626 @@
+/* exec.c - generic call-a-program code
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <stdio.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#ifndef EXEC_TEMPFILE_ONLY
+#include <sys/wait.h>
+#endif
+#ifdef HAVE_DOSISH_SYSTEM
+#include <windows.h>
+#endif
+#include <fcntl.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include "options.h"
+#include "memory.h"
+#include "i18n.h"
+#include "iobuf.h"
+#include "util.h"
+#include "exec.h"
+
+#ifdef NO_EXEC
+int exec_write(struct exec_info **info,const char *program,
+ const char *args_in,const char *name,int writeonly,int binary)
+{
+ log_error(_("no remote program execution supported\n"));
+ return G10ERR_GENERAL;
+}
+
+int exec_read(struct exec_info *info) { return G10ERR_GENERAL; }
+int exec_finish(struct exec_info *info) { return G10ERR_GENERAL; }
+int set_exec_path(const char *path) { return G10ERR_GENERAL; }
+
+#else /* ! NO_EXEC */
+
+#ifndef HAVE_MKDTEMP
+char *mkdtemp(char *template);
+#endif
+
+#if defined (_WIN32)
+/* This is a nicer system() for windows that waits for programs to
+ return before returning control to the caller. I hate helpful
+ computers. */
+static int win_system(const char *command)
+{
+ PROCESS_INFORMATION pi;
+ STARTUPINFO si;
+ char *string;
+
+ /* We must use a copy of the command as CreateProcess modifies this
+ argument. */
+ string=xstrdup(command);
+
+ memset(&pi,0,sizeof(pi));
+ memset(&si,0,sizeof(si));
+ si.cb=sizeof(si);
+
+ if(!CreateProcess(NULL,string,NULL,NULL,FALSE,0,NULL,NULL,&si,&pi))
+ return -1;
+
+ /* Wait for the child to exit */
+ WaitForSingleObject(pi.hProcess,INFINITE);
+
+ CloseHandle(pi.hProcess);
+ CloseHandle(pi.hThread);
+ xfree(string);
+
+ return 0;
+}
+#endif
+
+/* Replaces current $PATH */
+int set_exec_path(const char *path)
+{
+ char *p;
+
+ p=xmalloc(5+strlen(path)+1);
+ strcpy(p,"PATH=");
+ strcat(p,path);
+
+ if(DBG_EXTPROG)
+ log_debug("set_exec_path: %s\n",p);
+
+ /* Notice that path is never freed. That is intentional due to the
+ way putenv() works. This leaks a few bytes if we call
+ set_exec_path multiple times. */
+
+ if(putenv(p)!=0)
+ return G10ERR_GENERAL;
+ else
+ return 0;
+}
+
+/* Makes a temp directory and filenames */
+static int make_tempdir(struct exec_info *info)
+{
+ char *tmp=opt.temp_dir,*namein=info->name,*nameout;
+
+ if(!namein)
+ namein=info->flags.binary?"tempin" EXTSEP_S "bin":"tempin" EXTSEP_S "txt";
+
+ nameout=info->flags.binary?"tempout" EXTSEP_S "bin":"tempout" EXTSEP_S "txt";
+
+ /* Make up the temp dir and files in case we need them */
+
+ if(tmp==NULL)
+ {
+#if defined (_WIN32)
+ int err;
+
+ tmp=xmalloc(MAX_PATH);
+ err=GetTempPath(MAX_PATH,tmp);
+ if(err==0 || err>MAX_PATH)
+ strcpy(tmp,"c:\\windows\\temp");
+ else
+ {
+ int len=strlen(tmp);
+
+ /* GetTempPath may return with \ on the end */
+ while(len>0 && tmp[len-1]=='\\')
+ {
+ tmp[len-1]='\0';
+ len--;
+ }
+ }
+#else /* More unixish systems */
+ tmp=getenv("TMPDIR");
+ if(tmp==NULL)
+ {
+ tmp=getenv("TMP");
+ if(tmp==NULL)
+ {
+#ifdef __riscos__
+ tmp="<Wimp$ScrapDir>.GnuPG";
+ mkdir(tmp,0700); /* Error checks occur later on */
+#else
+ tmp="/tmp";
+#endif
+ }
+ }
+#endif
+ }
+
+ info->tempdir=xmalloc(strlen(tmp)+strlen(DIRSEP_S)+10+1);
+
+ sprintf(info->tempdir,"%s" DIRSEP_S "gpg-XXXXXX",tmp);
+
+#if defined (_WIN32)
+ xfree(tmp);
+#endif
+
+ if(mkdtemp(info->tempdir)==NULL)
+ log_error(_("can't create directory `%s': %s\n"),
+ info->tempdir,strerror(errno));
+ else
+ {
+ info->flags.madedir=1;
+
+ info->tempfile_in=xmalloc(strlen(info->tempdir)+
+ strlen(DIRSEP_S)+strlen(namein)+1);
+ sprintf(info->tempfile_in,"%s" DIRSEP_S "%s",info->tempdir,namein);
+
+ if(!info->flags.writeonly)
+ {
+ info->tempfile_out=xmalloc(strlen(info->tempdir)+
+ strlen(DIRSEP_S)+strlen(nameout)+1);
+ sprintf(info->tempfile_out,"%s" DIRSEP_S "%s",info->tempdir,nameout);
+ }
+ }
+
+ return info->flags.madedir?0:G10ERR_GENERAL;
+}
+
+/* Expands %i and %o in the args to the full temp files within the
+ temp directory. */
+static int expand_args(struct exec_info *info,const char *args_in)
+{
+ const char *ch=args_in;
+ unsigned int size,len;
+
+ info->flags.use_temp_files=0;
+ info->flags.keep_temp_files=0;
+
+ if(DBG_EXTPROG)
+ log_debug("expanding string \"%s\"\n",args_in);
+
+ size=100;
+ info->command=xmalloc(size);
+ len=0;
+ info->command[0]='\0';
+
+ while(*ch!='\0')
+ {
+ if(*ch=='%')
+ {
+ char *append=NULL;
+
+ ch++;
+
+ switch(*ch)
+ {
+ case 'O':
+ info->flags.keep_temp_files=1;
+ /* fall through */
+
+ case 'o': /* out */
+ if(!info->flags.madedir)
+ {
+ if(make_tempdir(info))
+ goto fail;
+ }
+ append=info->tempfile_out;
+ info->flags.use_temp_files=1;
+ break;
+
+ case 'I':
+ info->flags.keep_temp_files=1;
+ /* fall through */
+
+ case 'i': /* in */
+ if(!info->flags.madedir)
+ {
+ if(make_tempdir(info))
+ goto fail;
+ }
+ append=info->tempfile_in;
+ info->flags.use_temp_files=1;
+ break;
+
+ case '%':
+ append="%";
+ break;
+ }
+
+ if(append)
+ {
+ size_t applen=strlen(append);
+
+ if(applen+len>size-1)
+ {
+ if(applen<100)
+ applen=100;
+
+ size+=applen;
+ info->command=xrealloc(info->command,size);
+ }
+
+ strcat(info->command,append);
+ len+=strlen(append);
+ }
+ }
+ else
+ {
+ if(len==size-1) /* leave room for the \0 */
+ {
+ size+=100;
+ info->command=xrealloc(info->command,size);
+ }
+
+ info->command[len++]=*ch;
+ info->command[len]='\0';
+ }
+
+ ch++;
+ }
+
+ if(DBG_EXTPROG)
+ log_debug("args expanded to \"%s\", use %u, keep %u\n",info->command,
+ info->flags.use_temp_files,info->flags.keep_temp_files);
+
+ return 0;
+
+ fail:
+
+ xfree(info->command);
+ info->command=NULL;
+
+ return G10ERR_GENERAL;
+}
+
+/* Either handles the tempfile creation, or the fork/exec. If it
+ returns ok, then info->tochild is a FILE * that can be written to.
+ The rules are: if there are no args, then it's a fork/exec/pipe.
+ If there are args, but no tempfiles, then it's a fork/exec/pipe via
+ shell -c. If there are tempfiles, then it's a system. */
+
+int exec_write(struct exec_info **info,const char *program,
+ const char *args_in,const char *name,int writeonly,int binary)
+{
+ int ret=G10ERR_GENERAL;
+
+ if(opt.exec_disable && !opt.no_perm_warn)
+ {
+ log_info(_("external program calls are disabled due to unsafe "
+ "options file permissions\n"));
+
+ return ret;
+ }
+
+#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
+ /* There should be no way to get to this spot while still carrying
+ setuid privs. Just in case, bomb out if we are. */
+ if(getuid()!=geteuid())
+ BUG();
+#endif
+
+ if(program==NULL && args_in==NULL)
+ BUG();
+
+ *info=xmalloc_clear(sizeof(struct exec_info));
+
+ if(name)
+ (*info)->name=xstrdup(name);
+ (*info)->flags.binary=binary;
+ (*info)->flags.writeonly=writeonly;
+
+ /* Expand the args, if any */
+ if(args_in && expand_args(*info,args_in))
+ goto fail;
+
+#ifdef EXEC_TEMPFILE_ONLY
+ if(!(*info)->flags.use_temp_files)
+ {
+ log_error(_("this platform requires temporary files when calling"
+ " external programs\n"));
+ goto fail;
+ }
+
+#else /* !EXEC_TEMPFILE_ONLY */
+
+ /* If there are no args, or there are args, but no temp files, we
+ can use fork/exec/pipe */
+ if(args_in==NULL || (*info)->flags.use_temp_files==0)
+ {
+ int to[2],from[2];
+
+ if(pipe(to)==-1)
+ goto fail;
+
+ if(pipe(from)==-1)
+ {
+ close(to[0]);
+ close(to[1]);
+ goto fail;
+ }
+
+ if(((*info)->child=fork())==-1)
+ {
+ close(to[0]);
+ close(to[1]);
+ close(from[0]);
+ close(from[1]);
+ goto fail;
+ }
+
+ if((*info)->child==0)
+ {
+ char *shell=getenv("SHELL");
+
+ if(shell==NULL)
+ shell="/bin/sh";
+
+ /* I'm the child */
+
+ /* If the program isn't going to respond back, they get to
+ keep their stdout/stderr */
+ if(!(*info)->flags.writeonly)
+ {
+ /* implied close of STDERR */
+ if(dup2(STDOUT_FILENO,STDERR_FILENO)==-1)
+ _exit(1);
+
+ /* implied close of STDOUT */
+ close(from[0]);
+ if(dup2(from[1],STDOUT_FILENO)==-1)
+ _exit(1);
+ }
+
+ /* implied close of STDIN */
+ close(to[1]);
+ if(dup2(to[0],STDIN_FILENO)==-1)
+ _exit(1);
+
+ if(args_in==NULL)
+ {
+ if(DBG_EXTPROG)
+ log_debug("execlp: %s\n",program);
+
+ execlp(program,program,(void *)NULL);
+ }
+ else
+ {
+ if(DBG_EXTPROG)
+ log_debug("execlp: %s -c %s\n",shell,(*info)->command);
+
+ execlp(shell,shell,"-c",(*info)->command,(void *)NULL);
+ }
+
+ /* If we get this far the exec failed. Clean up and return. */
+
+ if(args_in==NULL)
+ log_error(_("unable to execute program `%s': %s\n"),
+ program,strerror(errno));
+ else
+ log_error(_("unable to execute shell `%s': %s\n"),
+ shell,strerror(errno));
+
+ /* This mimics the POSIX sh behavior - 127 means "not found"
+ from the shell. */
+ if(errno==ENOENT)
+ _exit(127);
+
+ _exit(1);
+ }
+
+ /* I'm the parent */
+
+ close(to[0]);
+
+ (*info)->tochild=fdopen(to[1],binary?"wb":"w");
+ if((*info)->tochild==NULL)
+ {
+ close(to[1]);
+ ret=G10ERR_WRITE_FILE;
+ goto fail;
+ }
+
+ close(from[1]);
+
+ (*info)->fromchild=iobuf_fdopen(from[0],"r");
+ if((*info)->fromchild==NULL)
+ {
+ close(from[0]);
+ ret=G10ERR_READ_FILE;
+ goto fail;
+ }
+
+ /* fd iobufs are cached?! */
+ iobuf_ioctl((*info)->fromchild,3,1,NULL);
+
+ return 0;
+ }
+#endif /* !EXEC_TEMPFILE_ONLY */
+
+ if(DBG_EXTPROG)
+ log_debug("using temp file `%s'\n",(*info)->tempfile_in);
+
+ /* It's not fork/exec/pipe, so create a temp file */
+ if( is_secured_filename ((*info)->tempfile_in) )
+ {
+ (*info)->tochild = NULL;
+ errno = EPERM;
+ }
+ else
+ (*info)->tochild=fopen((*info)->tempfile_in,binary?"wb":"w");
+ if((*info)->tochild==NULL)
+ {
+ log_error(_("can't create `%s': %s\n"),
+ (*info)->tempfile_in,strerror(errno));
+ ret=G10ERR_WRITE_FILE;
+ goto fail;
+ }
+
+ ret=0;
+
+ fail:
+ return ret;
+}
+
+int exec_read(struct exec_info *info)
+{
+ int ret=G10ERR_GENERAL;
+
+ fclose(info->tochild);
+ info->tochild=NULL;
+
+ if(info->flags.use_temp_files)
+ {
+ if(DBG_EXTPROG)
+ log_debug("system() command is %s\n",info->command);
+
+#if defined (_WIN32)
+ info->progreturn=win_system(info->command);
+#else
+ info->progreturn=system(info->command);
+#endif
+
+ if(info->progreturn==-1)
+ {
+ log_error(_("system error while calling external program: %s\n"),
+ strerror(errno));
+ info->progreturn=127;
+ goto fail;
+ }
+
+#if defined(WIFEXITED) && defined(WEXITSTATUS)
+ if(WIFEXITED(info->progreturn))
+ info->progreturn=WEXITSTATUS(info->progreturn);
+ else
+ {
+ log_error(_("unnatural exit of external program\n"));
+ info->progreturn=127;
+ goto fail;
+ }
+#else
+ /* If we don't have the macros, do the best we can. */
+ info->progreturn = (info->progreturn & 0xff00) >> 8;
+#endif
+
+ /* 127 is the magic value returned from system() to indicate
+ that the shell could not be executed, or from /bin/sh to
+ indicate that the program could not be executed. */
+
+ if(info->progreturn==127)
+ {
+ log_error(_("unable to execute external program\n"));
+ goto fail;
+ }
+
+ if(!info->flags.writeonly)
+ {
+ info->fromchild=iobuf_open(info->tempfile_out);
+ if (info->fromchild
+ && is_secured_file (iobuf_get_fd (info->fromchild)))
+ {
+ iobuf_close (info->fromchild);
+ info->fromchild = NULL;
+ errno = EPERM;
+ }
+ if(info->fromchild==NULL)
+ {
+ log_error(_("unable to read external program response: %s\n"),
+ strerror(errno));
+ ret=G10ERR_READ_FILE;
+ goto fail;
+ }
+
+ /* Do not cache this iobuf on close */
+ iobuf_ioctl(info->fromchild,3,1,NULL);
+ }
+ }
+
+ ret=0;
+
+ fail:
+ return ret;
+}
+
+int exec_finish(struct exec_info *info)
+{
+ int ret=info->progreturn;
+
+ if(info->fromchild)
+ iobuf_close(info->fromchild);
+
+ if(info->tochild)
+ fclose(info->tochild);
+
+#ifndef EXEC_TEMPFILE_ONLY
+ if(info->child>0)
+ {
+ if(waitpid(info->child,&info->progreturn,0)!=0 &&
+ WIFEXITED(info->progreturn))
+ ret=WEXITSTATUS(info->progreturn);
+ else
+ {
+ log_error(_("unnatural exit of external program\n"));
+ ret=127;
+ }
+ }
+#endif
+
+ if(info->flags.madedir && !info->flags.keep_temp_files)
+ {
+ if(info->tempfile_in)
+ {
+ if(unlink(info->tempfile_in)==-1)
+ log_info(_("WARNING: unable to remove tempfile (%s) `%s': %s\n"),
+ "in",info->tempfile_in,strerror(errno));
+ }
+
+ if(info->tempfile_out)
+ {
+ if(unlink(info->tempfile_out)==-1)
+ log_info(_("WARNING: unable to remove tempfile (%s) `%s': %s\n"),
+ "out",info->tempfile_out,strerror(errno));
+ }
+
+ if(rmdir(info->tempdir)==-1)
+ log_info(_("WARNING: unable to remove temp directory `%s': %s\n"),
+ info->tempdir,strerror(errno));
+ }
+
+ xfree(info->command);
+ xfree(info->name);
+ xfree(info->tempdir);
+ xfree(info->tempfile_in);
+ xfree(info->tempfile_out);
+ xfree(info);
+
+ return ret;
+}
+#endif /* ! NO_EXEC */
diff --git a/g10/exec.h b/g10/exec.h
new file mode 100644
index 0000000..b4a6f0e
--- /dev/null
+++ b/g10/exec.h
@@ -0,0 +1,52 @@
+/* exec.h
+ * Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef _EXEC_H_
+#define _EXEC_H_
+
+#include <unistd.h>
+#include <stdio.h>
+#include "iobuf.h"
+
+struct exec_info
+{
+ int progreturn;
+ struct
+ {
+ unsigned int binary:1;
+ unsigned int writeonly:1;
+ unsigned int madedir:1;
+ unsigned int use_temp_files:1;
+ unsigned int keep_temp_files:1;
+ } flags;
+ pid_t child;
+ FILE *tochild;
+ IOBUF fromchild;
+ char *command,*name,*tempdir,*tempfile_in,*tempfile_out;
+};
+
+int exec_write(struct exec_info **info,const char *program,
+ const char *args_in,const char *name,int writeonly,int binary);
+int exec_read(struct exec_info *info);
+int exec_finish(struct exec_info *info);
+int set_exec_path(const char *path);
+
+#endif /* !_EXEC_H_ */
diff --git a/g10/export.c b/g10/export.c
new file mode 100644
index 0000000..626b7d0
--- /dev/null
+++ b/g10/export.c
@@ -0,0 +1,600 @@
+/* export.c
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ * 2005 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "options.h"
+#include "packet.h"
+#include "errors.h"
+#include "keydb.h"
+#include "memory.h"
+#include "util.h"
+#include "main.h"
+#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,
+ int *any );
+
+int
+parse_export_options(char *str,unsigned int *options,int noisy)
+{
+ struct parse_options export_opts[]=
+ {
+ {"export-local-sigs",EXPORT_LOCAL_SIGS,NULL,
+ N_("export signatures that are marked as local-only")},
+ {"export-attributes",EXPORT_ATTRIBUTES,NULL,
+ N_("export attribute user IDs (generally photo IDs)")},
+ {"export-sensitive-revkeys",EXPORT_SENSITIVE_REVKEYS,NULL,
+ N_("export revocation keys marked as \"sensitive\"")},
+ {"export-reset-subkey-passwd",EXPORT_RESET_SUBKEY_PASSWD,NULL,
+ N_("remove the passphrase from exported subkeys")},
+ {"export-clean",EXPORT_CLEAN,NULL,
+ N_("remove unusable parts from key during export")},
+ {"export-minimal",EXPORT_MINIMAL|EXPORT_CLEAN,NULL,
+ N_("remove as much as possible from key during export")},
+ /* Aliases for backward compatibility */
+ {"include-local-sigs",EXPORT_LOCAL_SIGS,NULL,NULL},
+ {"include-attributes",EXPORT_ATTRIBUTES,NULL,NULL},
+ {"include-sensitive-revkeys",EXPORT_SENSITIVE_REVKEYS,NULL,NULL},
+ /* dummy */
+ {"export-unusable-sigs",0,NULL,NULL},
+ {"export-clean-sigs",0,NULL,NULL},
+ {"export-clean-uids",0,NULL,NULL},
+ {NULL,0,NULL,NULL}
+ /* add tags for include revoked and disabled? */
+ };
+
+ 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.
+ * options are defined in main.h.
+ * If USERS is NULL, the complete ring will be exported. */
+int
+export_pubkeys( STRLIST users, unsigned int options )
+{
+ return do_export( users, 0, options );
+}
+
+/****************
+ * Export to an already opened stream; return -1 if no keys have
+ * been exported
+ */
+int
+export_pubkeys_stream( IOBUF out, STRLIST users,
+ KBNODE *keyblock_out, unsigned int options )
+{
+ int any, rc;
+
+ rc = do_export_stream( out, users, 0, keyblock_out, options, &any );
+ if( !rc && !any )
+ rc = -1;
+ return rc;
+}
+
+int
+export_seckeys( STRLIST users )
+{
+ return do_export( users, 1, 0 );
+}
+
+int
+export_secsubkeys( STRLIST users )
+{
+ return do_export( users, 2, 0 );
+}
+
+static int
+do_export( STRLIST users, int secret, unsigned int options )
+{
+ IOBUF out = NULL;
+ int any, rc;
+ armor_filter_context_t afx;
+ compress_filter_context_t zfx;
+
+ memset( &afx, 0, sizeof afx);
+ memset( &zfx, 0, sizeof zfx);
+
+ rc = open_outfile( NULL, 0, &out );
+ if( rc )
+ return rc;
+
+ if( opt.armor ) {
+ afx.what = secret?5:1;
+ iobuf_push_filter( out, armor_filter, &afx );
+ }
+ if( opt.compress_keys )
+ push_compress_filter(out,&zfx,default_compress_algo());
+
+ rc = do_export_stream( out, users, secret, NULL, options, &any );
+ if( rc || !any )
+ iobuf_cancel(out);
+ else
+ iobuf_close(out);
+ return rc;
+}
+
+
+
+/* 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. */
+static int
+do_export_stream( IOBUF out, STRLIST users, int secret,
+ KBNODE *keyblock_out, unsigned int options, int *any )
+{
+ int rc = 0;
+ PACKET pkt;
+ KBNODE keyblock = NULL;
+ 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;
+
+ *any = 0;
+ init_packet( &pkt );
+ kdbhd = keydb_new (secret);
+
+ if (!users) {
+ ndesc = 1;
+ desc = xcalloc ( ndesc, sizeof *desc );
+ desc[0].mode = KEYDB_SEARCH_MODE_FIRST;
+ }
+ else {
+ for (ndesc=0, sl=users; sl; sl = sl->next, ndesc++)
+ ;
+ desc = xmalloc ( ndesc * sizeof *desc);
+
+ for (ndesc=0, sl=users; sl; sl = sl->next) {
+ if (classify_user_id (sl->d, desc+ndesc))
+ ndesc++;
+ else
+ log_error (_("key \"%s\" not found: %s\n"),
+ sl->d, g10_errstr (G10ERR_INV_USER_ID));
+ }
+
+ /* 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
+ all matched one - currently we stop at the first match. To
+ do this we need an extra flag to enable this feature so */
+ }
+
+#ifdef ENABLE_SELINUX_HACKS
+ if (secret) {
+ log_error (_("exporting secret keys not allowed\n"));
+ rc = G10ERR_GENERAL;
+ goto leave;
+ }
+#endif
+
+ while (!(rc = keydb_search2 (kdbhd, desc, ndesc, &descindex))) {
+ int sha1_warned=0,skip_until_subkey=0;
+ u32 sk_keyid[2];
+
+ if (!users)
+ desc[0].mode = KEYDB_SEARCH_MODE_NEXT;
+
+ /* Read the keyblock. */
+ rc = keydb_get_keyblock (kdbhd, &keyblock );
+ if( rc ) {
+ log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) );
+ goto leave;
+ }
+
+ if((node=find_kbnode(keyblock,PKT_SECRET_KEY)))
+ {
+ PKT_secret_key *sk=node->pkt->pkt.secret_key;
+
+ keyid_from_sk(sk,sk_keyid);
+
+ /* 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"),
+ keystr(sk_keyid));
+ continue;
+ }
+
+ /* No v3 keys with GNU mode 1001. */
+ if( secret == 2 && sk->version == 3 )
+ {
+ log_info(_("key %s: PGP 2.x style key - skipped\n"),
+ keystr(sk_keyid));
+ continue;
+ }
+
+ /* It does not make sense to export a key with a primary
+ key on card using a non-key stub. We simply skip those
+ keys when used with --export-secret-subkeys. */
+ if (secret == 2 && sk->is_protected
+ && sk->protect.s2k.mode == 1002 )
+ {
+ log_info(_("key %s: key material on-card - skipped\n"),
+ keystr(sk_keyid));
+ continue;
+ }
+ }
+ else
+ {
+ /* It's a public key export, so do the cleaning if
+ requested. Note that both export-clean and
+ export-minimal only apply to UID sigs (0x10, 0x11,
+ 0x12, and 0x13). A designated revocation is never
+ stripped, even with export-minimal set. */
+
+ if(options&EXPORT_CLEAN)
+ clean_key(keyblock,opt.verbose,options&EXPORT_MINIMAL,NULL,NULL);
+ }
+
+ /* And write it. */
+ for( kbctx=NULL; (node = walk_kbnode( keyblock, &kbctx, 0 )); ) {
+ if( skip_until_subkey )
+ {
+ if(node->pkt->pkttype==PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype==PKT_SECRET_SUBKEY)
+ skip_until_subkey=0;
+ else
+ continue;
+ }
+
+ /* We used to use comment packets, but not any longer. In
+ case we still have comments on a key, strip them here
+ before we call build_packet(). */
+ if( node->pkt->pkttype == PKT_COMMENT )
+ continue;
+
+ /* 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). */
+ if(desc[descindex].exact
+ && (node->pkt->pkttype==PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype==PKT_SECRET_SUBKEY))
+ {
+ 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.
+
+ 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_SIGNATURE)
+ {
+ /* do not export packets which are marked as not
+ exportable */
+ if(!(options&EXPORT_LOCAL_SIGS)
+ && !node->pkt->pkt.signature->flags.exportable)
+ continue; /* not exportable */
+
+ /* Do not export packets with a "sensitive" revocation
+ key unless the user wants us to. Note that we do
+ export these when issuing the actual revocation
+ (see revoke.c). */
+ if(!(options&EXPORT_SENSITIVE_REVKEYS)
+ && node->pkt->pkt.signature->revkey)
+ {
+ int i;
+
+ for(i=0;i<node->pkt->pkt.signature->numrevkeys;i++)
+ if(node->pkt->pkt.signature->revkey[i]->class & 0x40)
+ break;
+
+ if(i<node->pkt->pkt.signature->numrevkeys)
+ continue;
+ }
+ }
+
+ /* Don't export attribs? */
+ if( !(options&EXPORT_ATTRIBUTES) &&
+ node->pkt->pkttype == PKT_USER_ID &&
+ node->pkt->pkt.user_id->attrib_data ) {
+ /* Skip until we get to something that is not an attrib
+ or a signature on an attrib */
+ while(kbctx->next && kbctx->next->pkt->pkttype==PKT_SIGNATURE) {
+ kbctx=kbctx->next;
+ }
+
+ continue;
+ }
+
+ if( secret == 2 && node->pkt->pkttype == PKT_SECRET_KEY )
+ {
+ /* We don't want to export the secret parts of the
+ * primary key, this is done by using GNU protection mode 1001
+ */
+ int save_mode = node->pkt->pkt.secret_key->protect.s2k.mode;
+ node->pkt->pkt.secret_key->protect.s2k.mode = 1001;
+ rc = build_packet( out, node->pkt );
+ node->pkt->pkt.secret_key->protect.s2k.mode = save_mode;
+ }
+ else if (secret == 2 && node->pkt->pkttype == PKT_SECRET_SUBKEY
+ && (opt.export_options&EXPORT_RESET_SUBKEY_PASSWD))
+ {
+ /* If the subkey is protected reset the passphrase to
+ export an unprotected subkey. This feature is
+ useful in cases of a subkey copied to an unattended
+ machine where a passphrase is not required. */
+ PKT_secret_key *sk_save, *sk;
+
+ sk_save = node->pkt->pkt.secret_key;
+ sk = copy_secret_key (NULL, sk_save);
+ node->pkt->pkt.secret_key = sk;
+
+ log_info (_("about to export an unprotected subkey\n"));
+ switch (is_secret_key_protected (sk))
+ {
+ case -1:
+ rc = G10ERR_PUBKEY_ALGO;
+ break;
+ case 0:
+ break;
+ default:
+ if (sk->protect.s2k.mode == 1001)
+ ; /* No secret parts. */
+ else if( sk->protect.s2k.mode == 1002 )
+ ; /* Card key stub. */
+ else
+ {
+ rc = check_secret_key( sk, 0 );
+ }
+ break;
+ }
+ if (rc)
+ {
+ node->pkt->pkt.secret_key = sk_save;
+ free_secret_key (sk);
+ log_error (_("failed to unprotect the subkey: %s\n"),
+ g10_errstr (rc));
+ goto leave;
+ }
+
+ rc = build_packet (out, node->pkt);
+
+ node->pkt->pkt.secret_key = sk_save;
+ free_secret_key (sk);
+ }
+ else
+ {
+ /* Warn the user if the secret key or any of the secret
+ subkeys are protected with SHA1 and we have
+ simple_sk_checksum set. */
+ if(!sha1_warned && opt.simple_sk_checksum &&
+ (node->pkt->pkttype==PKT_SECRET_KEY ||
+ node->pkt->pkttype==PKT_SECRET_SUBKEY) &&
+ node->pkt->pkt.secret_key->protect.sha1chk)
+ {
+ /* I hope this warning doesn't confuse people. */
+ log_info(_("WARNING: secret key %s does not have a "
+ "simple SK checksum\n"),keystr(sk_keyid));
+
+ sha1_warned=1;
+ }
+
+ rc = build_packet( out, node->pkt );
+ }
+
+ if( rc ) {
+ log_error("build_packet(%d) failed: %s\n",
+ node->pkt->pkttype, g10_errstr(rc) );
+ rc = G10ERR_WRITE_FILE;
+ goto leave;
+ }
+ }
+ ++*any;
+ if(keyblock_out)
+ {
+ *keyblock_out=keyblock;
+ break;
+ }
+ }
+ if( rc == -1 )
+ rc = 0;
+
+ leave:
+ release_subkey_list (subkey_list);
+ xfree(desc);
+ keydb_release (kdbhd);
+ if(rc || keyblock_out==NULL)
+ release_kbnode( keyblock );
+ if( !*any )
+ log_info(_("WARNING: nothing exported\n"));
+ return rc;
+}
diff --git a/g10/filter.h b/g10/filter.h
new file mode 100644
index 0000000..c9fe872
--- /dev/null
+++ b/g10/filter.h
@@ -0,0 +1,167 @@
+/* filter.h
+ * Copyright (C) 1998, 1999, 2000, 2001, 2003,
+ * 2005 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+#ifndef G10_FILTER_H
+#define G10_FILTER_H
+
+#include "types.h"
+#include "cipher.h"
+
+typedef struct {
+ MD_HANDLE md; /* catch all */
+ MD_HANDLE md2; /* if we want to calculate an alternate hash */
+ size_t maxbuf_size;
+} md_filter_context_t;
+
+typedef struct {
+ int refcount; /* Reference counter. If 0 this structure
+ is not allocated on the heap. */
+
+ /* these fields may be initialized */
+ int what; /* what kind of armor headers to write */
+ int only_keyblocks; /* skip all headers but ".... key block" */
+ const char *hdrlines; /* write these headerlines */
+
+ /* these fileds must be initialized to zero */
+ int no_openpgp_data; /* output flag: "No valid OpenPGP data found" */
+
+ /* the following fields must be initialized to zero */
+ int inp_checked; /* set if the input has been checked */
+ int inp_bypass; /* set if the input is not armored */
+ int in_cleartext; /* clear text message */
+ int not_dash_escaped; /* clear text is not dash escaped */
+ int hashes; /* detected hash algorithms */
+ int faked; /* we are faking a literal data packet */
+ int truncated; /* number of truncated lines */
+ int qp_detected;
+ int pgp2mode;
+ byte eol[3]; /* The end of line characters as a
+ zero-terminated string. Defaults
+ (eol[0]=='\0') to whatever the local
+ platform uses. */
+
+ byte *buffer; /* malloced buffer */
+ unsigned buffer_size; /* and size of this buffer */
+ unsigned buffer_len; /* used length of the buffer */
+ unsigned buffer_pos; /* read position */
+
+ byte radbuf[4];
+ int idx, idx2;
+ u32 crc;
+
+ int status; /* an internal state flag */
+ int cancel;
+ int any_data; /* any valid armored data seen */
+ int pending_lf; /* used together with faked */
+} armor_filter_context_t;
+
+struct unarmor_pump_s;
+typedef struct unarmor_pump_s *UnarmorPump;
+
+
+struct compress_filter_context_s {
+ int status;
+ void *opaque; /* (used for z_stream) */
+ byte *inbuf;
+ unsigned inbufsize;
+ byte *outbuf;
+ unsigned outbufsize;
+ int algo; /* compress algo */
+ int algo1hack;
+ int new_ctb;
+ void (*release)(struct compress_filter_context_s*);
+};
+typedef struct compress_filter_context_s compress_filter_context_t;
+
+
+typedef struct {
+ DEK *dek;
+ u32 datalen;
+ CIPHER_HANDLE cipher_hd;
+ int header;
+ MD_HANDLE mdc_hash;
+ byte enchash[20];
+ int create_mdc; /* flag will be set by the cipher filter */
+} cipher_filter_context_t;
+
+
+
+typedef struct {
+ byte *buffer; /* malloced buffer */
+ unsigned buffer_size; /* and size of this buffer */
+ unsigned buffer_len; /* used length of the buffer */
+ unsigned buffer_pos; /* read position */
+ int truncated; /* number of truncated lines */
+ int not_dash_escaped;
+ int escape_from;
+ MD_HANDLE md;
+ int pending_lf;
+ int pending_esc;
+} text_filter_context_t;
+
+
+typedef struct {
+ char *what; /* description */
+ u32 last_time; /* last time reported */
+ unsigned long last; /* last amount reported */
+ unsigned long offset; /* current amount */
+ unsigned long total; /* total amount */
+} progress_filter_context_t;
+
+/* encrypt_filter_context_t defined in main.h */
+
+/*-- mdfilter.c --*/
+int md_filter( void *opaque, int control, IOBUF a, byte *buf, size_t *ret_len);
+void free_md_filter_context( md_filter_context_t *mfx );
+
+/*-- armor.c --*/
+armor_filter_context_t *new_armor_context (void);
+void release_armor_context (armor_filter_context_t *afx);
+int push_armor_filter (armor_filter_context_t *afx, IOBUF iobuf);
+int use_armor_filter( IOBUF a );
+int armor_filter( void *opaque, int control,
+ IOBUF chain, byte *buf, size_t *ret_len);
+UnarmorPump unarmor_pump_new (void);
+void unarmor_pump_release (UnarmorPump x);
+int unarmor_pump (UnarmorPump x, int c);
+
+/*-- compress.c --*/
+void push_compress_filter(IOBUF out,compress_filter_context_t *zfx,int algo);
+void push_compress_filter2(IOBUF out,compress_filter_context_t *zfx,
+ int algo,int rel);
+
+/*-- cipher.c --*/
+int cipher_filter( void *opaque, int control,
+ IOBUF chain, byte *buf, size_t *ret_len);
+
+/*-- textfilter.c --*/
+int text_filter( void *opaque, int control,
+ IOBUF chain, byte *buf, size_t *ret_len);
+int copy_clearsig_text( IOBUF out, IOBUF inp, MD_HANDLE md,
+ int escape_dash, int escape_from, int pgp2mode );
+
+/*-- progress.c --*/
+int progress_filter (void *opaque, int control,
+ IOBUF a, byte *buf, size_t *ret_len);
+void handle_progress (progress_filter_context_t *pfx,
+ IOBUF inp, const char *name);
+
+#endif /*G10_FILTER_H*/
diff --git a/g10/free-packet.c b/g10/free-packet.c
new file mode 100644
index 0000000..be49bb5
--- /dev/null
+++ b/g10/free-packet.c
@@ -0,0 +1,569 @@
+/* free-packet.c - cleanup stuff for packets
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003,
+ * 2005 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "packet.h"
+#include "iobuf.h"
+#include "mpi.h"
+#include "util.h"
+#include "cipher.h"
+#include "memory.h"
+#include "options.h"
+
+void
+free_symkey_enc( PKT_symkey_enc *enc )
+{
+ xfree(enc);
+}
+
+void
+free_pubkey_enc( PKT_pubkey_enc *enc )
+{
+ int n, i;
+ n = pubkey_get_nenc( enc->pubkey_algo );
+ if( !n )
+ mpi_free(enc->data[0]);
+ for(i=0; i < n; i++ )
+ mpi_free( enc->data[i] );
+ xfree(enc);
+}
+
+void
+free_seckey_enc( PKT_signature *sig )
+{
+ int n, i;
+
+ n = pubkey_get_nsig( sig->pubkey_algo );
+ if( !n )
+ mpi_free(sig->data[0]);
+ for(i=0; i < n; i++ )
+ mpi_free( sig->data[i] );
+
+ xfree(sig->revkey);
+ xfree(sig->hashed);
+ xfree(sig->unhashed);
+
+ if (sig->pka_info)
+ {
+ xfree (sig->pka_info->uri);
+ xfree (sig->pka_info);
+ }
+
+ xfree(sig);
+}
+
+
+void
+release_public_key_parts( PKT_public_key *pk )
+{
+ int n, i;
+ n = pubkey_get_npkey( pk->pubkey_algo );
+ if( !n )
+ mpi_free(pk->pkey[0]);
+ for(i=0; i < n; i++ ) {
+ mpi_free( pk->pkey[i] );
+ pk->pkey[i] = NULL;
+ }
+ if (pk->prefs) {
+ xfree (pk->prefs);
+ pk->prefs = NULL;
+ }
+ if (pk->user_id) {
+ free_user_id (pk->user_id);
+ pk->user_id = NULL;
+ }
+ if (pk->revkey) {
+ xfree(pk->revkey);
+ pk->revkey=NULL;
+ pk->numrevkeys=0;
+ }
+}
+
+
+void
+free_public_key( PKT_public_key *pk )
+{
+ release_public_key_parts( pk );
+ xfree(pk);
+}
+
+
+static subpktarea_t *
+cp_subpktarea (subpktarea_t *s )
+{
+ subpktarea_t *d;
+
+ if( !s )
+ return NULL;
+ d = xmalloc (sizeof (*d) + s->size - 1 );
+ d->size = s->size;
+ d->len = s->len;
+ memcpy (d->data, s->data, s->len);
+ return d;
+}
+
+/*
+ * Return a copy of the preferences
+ */
+prefitem_t *
+copy_prefs (const prefitem_t *prefs)
+{
+ size_t n;
+ prefitem_t *new;
+
+ if (!prefs)
+ return NULL;
+
+ for (n=0; prefs[n].type; n++)
+ ;
+ new = xmalloc ( sizeof (*new) * (n+1));
+ for (n=0; prefs[n].type; n++) {
+ new[n].type = prefs[n].type;
+ new[n].value = prefs[n].value;
+ }
+ new[n].type = PREFTYPE_NONE;
+ new[n].value = 0;
+
+ return new;
+}
+
+
+PKT_public_key *
+copy_public_key ( PKT_public_key *d, PKT_public_key *s)
+{
+ int n, i;
+
+ if( !d )
+ d = xmalloc(sizeof *d);
+ memcpy( d, s, sizeof *d );
+ d->user_id = scopy_user_id (s->user_id);
+ d->prefs = copy_prefs (s->prefs);
+ n = pubkey_get_npkey( s->pubkey_algo );
+ if( !n )
+ d->pkey[0] = mpi_copy(s->pkey[0]);
+ else {
+ for(i=0; i < n; i++ )
+ d->pkey[i] = mpi_copy( s->pkey[i] );
+ }
+ if( !s->revkey && s->numrevkeys )
+ BUG();
+ if( s->numrevkeys ) {
+ d->revkey = xmalloc(sizeof(struct revocation_key)*s->numrevkeys);
+ memcpy(d->revkey,s->revkey,sizeof(struct revocation_key)*s->numrevkeys);
+ }
+ else
+ d->revkey = NULL;
+ return d;
+}
+
+/****************
+ * Replace all common parts of a sk by the one from the public key.
+ * This is a hack and a better solution will be to just store the real secret
+ * parts somewhere and don't duplicate all the other stuff.
+ */
+void
+copy_public_parts_to_secret_key( PKT_public_key *pk, PKT_secret_key *sk )
+{
+ sk->expiredate = pk->expiredate;
+ sk->pubkey_algo = pk->pubkey_algo;
+ sk->pubkey_usage= pk->pubkey_usage;
+ sk->req_usage = pk->req_usage;
+ sk->req_algo = pk->req_algo;
+ sk->has_expired = pk->has_expired;
+ sk->is_revoked = pk->is_revoked;
+ sk->is_valid = pk->is_valid;
+ sk->main_keyid[0]= pk->main_keyid[0];
+ sk->main_keyid[1]= pk->main_keyid[1];
+ sk->keyid[0] = pk->keyid[0];
+ sk->keyid[1] = pk->keyid[1];
+}
+
+
+static pka_info_t *
+cp_pka_info (const pka_info_t *s)
+{
+ pka_info_t *d = xmalloc (sizeof *s + strlen (s->email));
+
+ d->valid = s->valid;
+ d->checked = s->checked;
+ d->uri = s->uri? xstrdup (s->uri):NULL;
+ memcpy (d->fpr, s->fpr, sizeof s->fpr);
+ strcpy (d->email, s->email);
+ return d;
+}
+
+
+PKT_signature *
+copy_signature( PKT_signature *d, PKT_signature *s )
+{
+ int n, i;
+
+ if( !d )
+ d = xmalloc(sizeof *d);
+ memcpy( d, s, sizeof *d );
+ n = pubkey_get_nsig( s->pubkey_algo );
+ if( !n )
+ d->data[0] = mpi_copy(s->data[0]);
+ else {
+ for(i=0; i < n; i++ )
+ d->data[i] = mpi_copy( s->data[i] );
+ }
+ d->pka_info = s->pka_info? cp_pka_info (s->pka_info) : NULL;
+ d->hashed = cp_subpktarea (s->hashed);
+ d->unhashed = cp_subpktarea (s->unhashed);
+ if(s->numrevkeys)
+ {
+ d->revkey=NULL;
+ d->numrevkeys=0;
+ parse_revkeys(d);
+ }
+ return d;
+}
+
+
+/*
+ * shallow copy of the user ID
+ */
+PKT_user_id *
+scopy_user_id (PKT_user_id *s)
+{
+ if (s)
+ s->ref++;
+ return s;
+}
+
+
+
+void
+release_secret_key_parts( PKT_secret_key *sk )
+{
+ int n, i;
+
+ n = pubkey_get_nskey( sk->pubkey_algo );
+ if( !n )
+ mpi_free(sk->skey[0]);
+ for(i=0; i < n; i++ ) {
+ mpi_free( sk->skey[i] );
+ sk->skey[i] = NULL;
+ }
+}
+
+void
+free_secret_key( PKT_secret_key *sk )
+{
+ release_secret_key_parts( sk );
+ xfree(sk);
+}
+
+PKT_secret_key *
+copy_secret_key( PKT_secret_key *d, PKT_secret_key *s )
+{
+ int n, i;
+
+ if( !d )
+ d = xmalloc_secure(sizeof *d);
+ else
+ release_secret_key_parts (d);
+ memcpy( d, s, sizeof *d );
+ n = pubkey_get_nskey( s->pubkey_algo );
+ if( !n )
+ d->skey[0] = mpi_copy(s->skey[0]);
+ else {
+ for(i=0; i < n; i++ )
+ d->skey[i] = mpi_copy( s->skey[i] );
+ }
+
+ return d;
+}
+
+void
+free_comment( PKT_comment *rem )
+{
+ xfree(rem);
+}
+
+void
+free_attributes(PKT_user_id *uid)
+{
+ xfree(uid->attribs);
+ xfree(uid->attrib_data);
+
+ uid->attribs=NULL;
+ uid->attrib_data=NULL;
+ uid->attrib_len=0;
+}
+
+void
+free_user_id (PKT_user_id *uid)
+{
+ assert (uid->ref > 0);
+ if (--uid->ref)
+ return;
+
+ free_attributes(uid);
+ xfree (uid->prefs);
+ xfree (uid->namehash);
+ xfree (uid);
+}
+
+void
+free_compressed( PKT_compressed *zd )
+{
+ if( zd->buf ) { /* have to skip some bytes */
+ /* don't have any information about the length, so
+ * we assume this is the last packet */
+ while( iobuf_read( zd->buf, NULL, 1<<30 ) != -1 )
+ ;
+ }
+ xfree(zd);
+}
+
+void
+free_encrypted( PKT_encrypted *ed )
+{
+ if( ed->buf ) { /* have to skip some bytes */
+ if( ed->is_partial ) {
+ while( iobuf_read( ed->buf, NULL, 1<<30 ) != -1 )
+ ;
+ }
+ else {
+ while( ed->len ) { /* skip the packet */
+ int n = iobuf_read( ed->buf, NULL, ed->len );
+ if( n == -1 )
+ ed->len = 0;
+ else
+ ed->len -= n;
+ }
+ }
+ }
+ xfree(ed);
+}
+
+
+void
+free_plaintext( PKT_plaintext *pt )
+{
+ if( pt->buf ) { /* have to skip some bytes */
+ if( pt->is_partial ) {
+ while( iobuf_read( pt->buf, NULL, 1<<30 ) != -1 )
+ ;
+ }
+ else {
+ while( pt->len ) { /* skip the packet */
+ int n = iobuf_read( pt->buf, NULL, pt->len );
+ if( n == -1 )
+ pt->len = 0;
+ else
+ pt->len -= n;
+ }
+ }
+ }
+ xfree(pt);
+}
+
+/****************
+ * Free the packet in pkt.
+ */
+void
+free_packet( PACKET *pkt )
+{
+ if( !pkt || !pkt->pkt.generic )
+ return;
+
+ if( DBG_MEMORY )
+ log_debug("free_packet() type=%d\n", pkt->pkttype );
+
+ switch( pkt->pkttype ) {
+ case PKT_SIGNATURE:
+ free_seckey_enc( pkt->pkt.signature );
+ break;
+ case PKT_PUBKEY_ENC:
+ free_pubkey_enc( pkt->pkt.pubkey_enc );
+ break;
+ case PKT_SYMKEY_ENC:
+ free_symkey_enc( pkt->pkt.symkey_enc );
+ break;
+ case PKT_PUBLIC_KEY:
+ case PKT_PUBLIC_SUBKEY:
+ free_public_key( pkt->pkt.public_key );
+ break;
+ case PKT_SECRET_KEY:
+ case PKT_SECRET_SUBKEY:
+ free_secret_key( pkt->pkt.secret_key );
+ break;
+ case PKT_COMMENT:
+ free_comment( pkt->pkt.comment );
+ break;
+ case PKT_USER_ID:
+ free_user_id( pkt->pkt.user_id );
+ break;
+ case PKT_COMPRESSED:
+ free_compressed( pkt->pkt.compressed);
+ break;
+ case PKT_ENCRYPTED:
+ case PKT_ENCRYPTED_MDC:
+ free_encrypted( pkt->pkt.encrypted );
+ break;
+ case PKT_PLAINTEXT:
+ free_plaintext( pkt->pkt.plaintext );
+ break;
+ default:
+ xfree( pkt->pkt.generic );
+ break;
+ }
+ pkt->pkt.generic = NULL;
+}
+
+/****************
+ * returns 0 if they match.
+ */
+int
+cmp_public_keys( PKT_public_key *a, PKT_public_key *b )
+{
+ int n, i;
+
+ if( a->timestamp != b->timestamp )
+ return -1;
+ if( a->version < 4 && a->expiredate != b->expiredate )
+ return -1;
+ if( a->pubkey_algo != b->pubkey_algo )
+ return -1;
+
+ n = pubkey_get_npkey( b->pubkey_algo );
+ if( !n )
+ return -1; /* can't compare due to unknown algorithm */
+ for(i=0; i < n; i++ ) {
+ if( mpi_cmp( a->pkey[i], b->pkey[i] ) )
+ return -1;
+ }
+
+ return 0;
+}
+
+/****************
+ * Returns 0 if they match.
+ * We only compare the public parts.
+ */
+int
+cmp_secret_keys( PKT_secret_key *a, PKT_secret_key *b )
+{
+ int n, i;
+
+ if( a->timestamp != b->timestamp )
+ return -1;
+ if( a->version < 4 && a->expiredate != b->expiredate )
+ return -1;
+ if( a->pubkey_algo != b->pubkey_algo )
+ return -1;
+
+ n = pubkey_get_npkey( b->pubkey_algo );
+ if( !n )
+ return -1; /* can't compare due to unknown algorithm */
+ for(i=0; i < n; i++ ) {
+ if( mpi_cmp( a->skey[i], b->skey[i] ) )
+ return -1;
+ }
+
+ return 0;
+}
+
+/****************
+ * Returns 0 if they match.
+ */
+int
+cmp_public_secret_key( PKT_public_key *pk, PKT_secret_key *sk )
+{
+ int n, i;
+
+ if( pk->timestamp != sk->timestamp )
+ return -1;
+ if( pk->version < 4 && pk->expiredate != sk->expiredate )
+ return -1;
+ if( pk->pubkey_algo != sk->pubkey_algo )
+ return -1;
+
+ n = pubkey_get_npkey( pk->pubkey_algo );
+ if( !n )
+ return -1; /* can't compare due to unknown algorithm */
+ for(i=0; i < n; i++ ) {
+ if( mpi_cmp( pk->pkey[i] , sk->skey[i] ) )
+ return -1;
+ }
+ return 0;
+}
+
+
+
+int
+cmp_signatures( PKT_signature *a, PKT_signature *b )
+{
+ int n, i;
+
+ if( a->keyid[0] != b->keyid[0] )
+ return -1;
+ if( a->keyid[1] != b->keyid[1] )
+ return -1;
+ if( a->pubkey_algo != b->pubkey_algo )
+ return -1;
+
+ n = pubkey_get_nsig( a->pubkey_algo );
+ if( !n )
+ return -1; /* can't compare due to unknown algorithm */
+ for(i=0; i < n; i++ ) {
+ if( mpi_cmp( a->data[i] , b->data[i] ) )
+ return -1;
+ }
+ return 0;
+}
+
+
+/****************
+ * Returns: true if the user ids do not match
+ */
+int
+cmp_user_ids( PKT_user_id *a, PKT_user_id *b )
+{
+ int res=1;
+
+ if( a == b )
+ return 0;
+
+ if( a->attrib_data && b->attrib_data )
+ {
+ res = a->attrib_len - b->attrib_len;
+ if( !res )
+ res = memcmp( a->attrib_data, b->attrib_data, a->attrib_len );
+ }
+ else if( !a->attrib_data && !b->attrib_data )
+ {
+ res = a->len - b->len;
+ if( !res )
+ res = memcmp( a->name, b->name, a->len );
+ }
+
+ return res;
+}
diff --git a/g10/getkey.c b/g10/getkey.c
new file mode 100644
index 0000000..74c0a74
--- /dev/null
+++ b/g10/getkey.c
@@ -0,0 +1,3003 @@
+/* getkey.c - Get a key from the database
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <ctype.h>
+#include "util.h"
+#include "packet.h"
+#include "memory.h"
+#include "iobuf.h"
+#include "keydb.h"
+#include "options.h"
+#include "main.h"
+#include "trustdb.h"
+#include "i18n.h"
+#include "keyserver-internal.h"
+
+#define MAX_PK_CACHE_ENTRIES PK_UID_CACHE_SIZE
+#define MAX_UID_CACHE_ENTRIES PK_UID_CACHE_SIZE
+
+#if MAX_PK_CACHE_ENTRIES < 2
+#error We need the cache for key creation
+#endif
+
+struct getkey_ctx_s {
+ int exact;
+ KBNODE keyblock;
+ KBPOS kbpos;
+ KBNODE found_key; /* pointer into some keyblock */
+ int last_rc;
+ int req_usage;
+ int req_algo;
+ KEYDB_HANDLE kr_handle;
+ int not_allocated;
+ int nitems;
+ KEYDB_SEARCH_DESC items[1];
+};
+
+#if 0
+static struct {
+ int any;
+ int okay_count;
+ int nokey_count;
+ int error_count;
+} lkup_stats[21];
+#endif
+
+typedef struct keyid_list {
+ struct keyid_list *next;
+ u32 keyid[2];
+} *keyid_list_t;
+
+
+#if MAX_PK_CACHE_ENTRIES
+ typedef struct pk_cache_entry {
+ struct pk_cache_entry *next;
+ u32 keyid[2];
+ PKT_public_key *pk;
+ } *pk_cache_entry_t;
+ static pk_cache_entry_t pk_cache;
+ static int pk_cache_entries; /* number of entries in pk cache */
+ static int pk_cache_disabled;
+#endif
+
+#if MAX_UID_CACHE_ENTRIES < 5
+#error we really need the userid cache
+#endif
+typedef struct user_id_db {
+ struct user_id_db *next;
+ keyid_list_t keyids;
+ int len;
+ char name[1];
+} *user_id_db_t;
+static user_id_db_t user_id_db;
+static int uid_cache_entries; /* number of entries in uid cache */
+
+static void merge_selfsigs( KBNODE keyblock );
+static int lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode );
+
+#if 0
+static void
+print_stats()
+{
+ int i;
+ for(i=0; i < DIM(lkup_stats); i++ ) {
+ if( lkup_stats[i].any )
+ fprintf(stderr,
+ "lookup stats: mode=%-2d ok=%-6d nokey=%-6d err=%-6d\n",
+ i,
+ lkup_stats[i].okay_count,
+ lkup_stats[i].nokey_count,
+ lkup_stats[i].error_count );
+ }
+}
+#endif
+
+
+void
+cache_public_key( PKT_public_key *pk )
+{
+#if MAX_PK_CACHE_ENTRIES
+ pk_cache_entry_t ce;
+ u32 keyid[2];
+
+ if( pk_cache_disabled )
+ return;
+
+ if( pk->dont_cache )
+ return;
+
+ if( is_ELGAMAL(pk->pubkey_algo)
+ || pk->pubkey_algo == PUBKEY_ALGO_DSA
+ || is_RSA(pk->pubkey_algo) ) {
+ keyid_from_pk( pk, keyid );
+ }
+ else
+ return; /* don't know how to get the keyid */
+
+ for( ce = pk_cache; ce; ce = ce->next )
+ if( ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1] ) {
+ if( DBG_CACHE )
+ log_debug("cache_public_key: already in cache\n");
+ return;
+ }
+
+ if( pk_cache_entries >= MAX_PK_CACHE_ENTRIES ) {
+ /* fixme: use another algorithm to free some cache slots */
+ pk_cache_disabled=1;
+ if( opt.verbose > 1 )
+ log_info(_("too many entries in pk cache - disabled\n"));
+ return;
+ }
+ pk_cache_entries++;
+ ce = xmalloc( sizeof *ce );
+ ce->next = pk_cache;
+ pk_cache = ce;
+ ce->pk = copy_public_key( NULL, pk );
+ ce->keyid[0] = keyid[0];
+ ce->keyid[1] = keyid[1];
+#endif
+}
+
+
+/* Return a const utf-8 string with the text "[User ID not found]".
+ This fucntion is required so that we don't need to switch gettext's
+ encoding temporary. */
+static const char *
+user_id_not_found_utf8 (void)
+{
+ static char *text;
+
+ if (!text)
+ text = native_to_utf8 (_("[User ID not found]"));
+ return text;
+}
+
+
+
+/*
+ * Return the user ID from the given keyblock.
+ * We use the primary uid flag which has been set by the merge_selfsigs
+ * function. The returned value is only valid as long as then given
+ * keyblock is not changed
+ */
+static const char *
+get_primary_uid ( KBNODE keyblock, size_t *uidlen )
+{
+ KBNODE k;
+ const char *s;
+
+ for (k=keyblock; k; k=k->next ) {
+ if ( k->pkt->pkttype == PKT_USER_ID
+ && !k->pkt->pkt.user_id->attrib_data
+ && k->pkt->pkt.user_id->is_primary ) {
+ *uidlen = k->pkt->pkt.user_id->len;
+ return k->pkt->pkt.user_id->name;
+ }
+ }
+ s = user_id_not_found_utf8 ();
+ *uidlen = strlen (s);
+ return s;
+}
+
+
+static void
+release_keyid_list ( keyid_list_t k )
+{
+ while ( k ) {
+ keyid_list_t k2 = k->next;
+ xfree (k);
+ k = k2;
+ }
+}
+
+/****************
+ * Store the association of keyid and userid
+ * Feed only public keys to this function.
+ */
+static void
+cache_user_id( KBNODE keyblock )
+{
+ user_id_db_t r;
+ const char *uid;
+ size_t uidlen;
+ keyid_list_t keyids = NULL;
+ KBNODE k;
+
+ for (k=keyblock; k; k = k->next ) {
+ if ( k->pkt->pkttype == PKT_PUBLIC_KEY
+ || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+ keyid_list_t a = xmalloc_clear ( sizeof *a );
+ /* Hmmm: For a long list of keyids it might be an advantage
+ * to append the keys */
+ keyid_from_pk( k->pkt->pkt.public_key, a->keyid );
+ /* first check for duplicates */
+ for(r=user_id_db; r; r = r->next ) {
+ keyid_list_t b = r->keyids;
+ for ( b = r->keyids; b; b = b->next ) {
+ if( b->keyid[0] == a->keyid[0]
+ && b->keyid[1] == a->keyid[1] ) {
+ if( DBG_CACHE )
+ log_debug("cache_user_id: already in cache\n");
+ release_keyid_list ( keyids );
+ xfree ( a );
+ return;
+ }
+ }
+ }
+ /* now put it into the cache */
+ a->next = keyids;
+ keyids = a;
+ }
+ }
+ if ( !keyids )
+ BUG (); /* No key no fun */
+
+
+ uid = get_primary_uid ( keyblock, &uidlen );
+
+ if( uid_cache_entries >= MAX_UID_CACHE_ENTRIES ) {
+ /* fixme: use another algorithm to free some cache slots */
+ r = user_id_db;
+ user_id_db = r->next;
+ release_keyid_list ( r->keyids );
+ xfree(r);
+ uid_cache_entries--;
+ }
+ r = xmalloc( sizeof *r + uidlen-1 );
+ r->keyids = keyids;
+ r->len = uidlen;
+ memcpy(r->name, uid, r->len);
+ r->next = user_id_db;
+ user_id_db = r;
+ uid_cache_entries++;
+}
+
+
+void
+getkey_disable_caches()
+{
+#if MAX_PK_CACHE_ENTRIES
+ {
+ pk_cache_entry_t ce, ce2;
+
+ for( ce = pk_cache; ce; ce = ce2 ) {
+ ce2 = ce->next;
+ free_public_key( ce->pk );
+ xfree( ce );
+ }
+ pk_cache_disabled=1;
+ pk_cache_entries = 0;
+ pk_cache = NULL;
+ }
+#endif
+ /* fixme: disable user id cache ? */
+}
+
+
+static void
+pk_from_block ( GETKEY_CTX ctx, PKT_public_key *pk, KBNODE keyblock )
+{
+ KBNODE a = ctx->found_key ? ctx->found_key : keyblock;
+
+ assert ( a->pkt->pkttype == PKT_PUBLIC_KEY
+ || a->pkt->pkttype == PKT_PUBLIC_SUBKEY );
+
+ copy_public_key ( pk, a->pkt->pkt.public_key );
+}
+
+static void
+sk_from_block ( GETKEY_CTX ctx,
+ PKT_secret_key *sk, KBNODE keyblock )
+{
+ KBNODE a = ctx->found_key ? ctx->found_key : keyblock;
+
+ assert ( a->pkt->pkttype == PKT_SECRET_KEY
+ || a->pkt->pkttype == PKT_SECRET_SUBKEY );
+
+ copy_secret_key( sk, a->pkt->pkt.secret_key);
+}
+
+
+/****************
+ * Get a public key and store it into the allocated pk
+ * can be called with PK set to NULL to just read it into some
+ * internal structures.
+ */
+int
+get_pubkey( PKT_public_key *pk, u32 *keyid )
+{
+ int internal = 0;
+ int rc = 0;
+
+#if MAX_PK_CACHE_ENTRIES
+ if(pk)
+ {
+ /* Try to get it from the cache. We don't do this when pk is
+ NULL as it does not guarantee that the user IDs are
+ cached. */
+ pk_cache_entry_t ce;
+ for( ce = pk_cache; ce; ce = ce->next )
+ {
+ if( ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1] )
+ {
+ copy_public_key( pk, ce->pk );
+ return 0;
+ }
+ }
+ }
+#endif
+ /* more init stuff */
+ if( !pk ) {
+ pk = xmalloc_clear( sizeof *pk );
+ internal++;
+ }
+
+
+ /* do a lookup */
+ { struct getkey_ctx_s ctx;
+ KBNODE kb = NULL;
+ memset( &ctx, 0, sizeof ctx );
+ ctx.exact = 1; /* use the key ID exactly as given */
+ ctx.not_allocated = 1;
+ ctx.kr_handle = keydb_new (0);
+ ctx.nitems = 1;
+ ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID;
+ ctx.items[0].u.kid[0] = keyid[0];
+ ctx.items[0].u.kid[1] = keyid[1];
+ ctx.req_algo = pk->req_algo;
+ ctx.req_usage = pk->req_usage;
+ rc = lookup( &ctx, &kb, 0 );
+ if ( !rc ) {
+ pk_from_block ( &ctx, pk, kb );
+ }
+ get_pubkey_end( &ctx );
+ release_kbnode ( kb );
+ }
+ if( !rc )
+ goto leave;
+
+ rc = G10ERR_NO_PUBKEY;
+
+ leave:
+ if( !rc )
+ cache_public_key( pk );
+ if( internal )
+ free_public_key(pk);
+ return rc;
+}
+
+
+/* Get a public key and store it into the allocated pk. This function
+ differs from get_pubkey() in that it does not do a check of the key
+ to avoid recursion. It should be used only in very certain cases.
+ It will only retrieve primary keys. */
+int
+get_pubkey_fast (PKT_public_key *pk, u32 *keyid)
+{
+ int rc = 0;
+ KEYDB_HANDLE hd;
+ KBNODE keyblock;
+ u32 pkid[2];
+
+ assert (pk);
+#if MAX_PK_CACHE_ENTRIES
+ { /* Try to get it from the cache */
+ pk_cache_entry_t ce;
+
+ for (ce = pk_cache; ce; ce = ce->next)
+ {
+ if (ce->keyid[0] == keyid[0] && ce->keyid[1] == keyid[1])
+ {
+ if (pk)
+ copy_public_key (pk, ce->pk);
+ return 0;
+ }
+ }
+ }
+#endif
+
+ hd = keydb_new (0);
+ rc = keydb_search_kid (hd, keyid);
+ if (rc == -1)
+ {
+ keydb_release (hd);
+ return G10ERR_NO_PUBKEY;
+ }
+ rc = keydb_get_keyblock (hd, &keyblock);
+ keydb_release (hd);
+ if (rc)
+ {
+ log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc));
+ return G10ERR_NO_PUBKEY;
+ }
+
+ assert ( keyblock->pkt->pkttype == PKT_PUBLIC_KEY
+ || keyblock->pkt->pkttype == PKT_PUBLIC_SUBKEY );
+
+ keyid_from_pk(keyblock->pkt->pkt.public_key,pkid);
+ if(keyid[0]==pkid[0] && keyid[1]==pkid[1])
+ copy_public_key (pk, keyblock->pkt->pkt.public_key );
+ else
+ rc=G10ERR_NO_PUBKEY;
+
+ release_kbnode (keyblock);
+
+ /* Not caching key here since it won't have all of the fields
+ properly set. */
+
+ return rc;
+}
+
+
+KBNODE
+get_pubkeyblock( u32 *keyid )
+{
+ struct getkey_ctx_s ctx;
+ int rc = 0;
+ KBNODE keyblock = NULL;
+
+ memset( &ctx, 0, sizeof ctx );
+ /* no need to set exact here because we want the entire block */
+ ctx.not_allocated = 1;
+ ctx.kr_handle = keydb_new (0);
+ ctx.nitems = 1;
+ ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID;
+ ctx.items[0].u.kid[0] = keyid[0];
+ ctx.items[0].u.kid[1] = keyid[1];
+ rc = lookup( &ctx, &keyblock, 0 );
+ get_pubkey_end( &ctx );
+
+ return rc ? NULL : keyblock;
+}
+
+
+
+
+/****************
+ * Get a secret key and store it into sk
+ */
+int
+get_seckey( PKT_secret_key *sk, u32 *keyid )
+{
+ int rc;
+ struct getkey_ctx_s ctx;
+ KBNODE kb = NULL;
+
+ memset( &ctx, 0, sizeof ctx );
+ ctx.exact = 1; /* use the key ID exactly as given */
+ ctx.not_allocated = 1;
+ ctx.kr_handle = keydb_new (1);
+ ctx.nitems = 1;
+ ctx.items[0].mode = KEYDB_SEARCH_MODE_LONG_KID;
+ ctx.items[0].u.kid[0] = keyid[0];
+ ctx.items[0].u.kid[1] = keyid[1];
+ ctx.req_algo = sk->req_algo;
+ ctx.req_usage = sk->req_usage;
+ rc = lookup( &ctx, &kb, 1 );
+ if ( !rc ) {
+ sk_from_block ( &ctx, sk, kb );
+ }
+ get_seckey_end( &ctx );
+ release_kbnode ( kb );
+
+ if( !rc ) {
+ /* check the secret key (this may prompt for a passprase to
+ * unlock the secret key
+ */
+ rc = check_secret_key( sk, 0 );
+ }
+
+ return rc;
+}
+
+
+/****************
+ * Check whether the secret key is available. This is just a fast
+ * check and does not tell us whether the secret key is valid. It
+ * merely tells other whether there is some secret key.
+ * Returns: 0 := key is available
+ * G10ERR_NO_SECKEY := not availabe
+ */
+int
+seckey_available( u32 *keyid )
+{
+ int rc;
+ KEYDB_HANDLE hd = keydb_new (1);
+
+ rc = keydb_search_kid (hd, keyid);
+ if ( rc == -1 )
+ rc = G10ERR_NO_SECKEY;
+ keydb_release (hd);
+ return rc;
+}
+
+
+/****************
+ * Return the type of the user id:
+ *
+ * Please use the constants KEYDB_SERCH_MODE_xxx
+ * 0 = Invalid user ID
+ * 1 = exact match
+ * 2 = match a substring
+ * 3 = match an email address
+ * 4 = match a substring of an email address
+ * 5 = match an email address, but compare from end
+ * 6 = word match mode
+ * 10 = it is a short KEYID (don't care about keyid[0])
+ * 11 = it is a long KEYID
+ * 12 = it is a trustdb index (keyid is looked up)
+ * 16 = it is a 16 byte fingerprint
+ * 20 = it is a 20 byte fingerprint
+ * 21 = Unified fingerprint :fpr:pk_algo:
+ * (We don't use pk_algo yet)
+ *
+ * Rules used:
+ * - If the username starts with 8,9,16 or 17 hex-digits (the first one
+ * must be in the range 0..9), this is considered a keyid; depending
+ * on the length a short or complete one.
+ * - If the username starts with 32,33,40 or 41 hex-digits (the first one
+ * must be in the range 0..9), this is considered a fingerprint.
+ * - If the username starts with a left angle, we assume it is a complete
+ * email address and look only at this part.
+ * - If the username starts with a colon we assume it is a unified
+ * key specfification.
+ * - If the username starts with a '.', we assume it is the ending
+ * part of an email address
+ * - If the username starts with an '@', we assume it is a part of an
+ * email address
+ * - If the userid start with an '=' an exact compare is done.
+ * - If the userid starts with a '*' a case insensitive substring search is
+ * done (This is the default).
+ * - If the userid starts with a '+' we will compare individual words
+ * and a match requires that all the words are in the userid.
+ * Words are delimited by white space or "()<>[]{}.@-+_,;/&!"
+ * (note that you can't search for these characters). Compare
+ * is not case sensitive.
+ */
+
+int
+classify_user_id( const char *name, KEYDB_SEARCH_DESC *desc )
+{
+ const char *s;
+ int hexprefix = 0;
+ int hexlength;
+ int mode = 0;
+ KEYDB_SEARCH_DESC dummy_desc;
+
+ if (!desc)
+ desc = &dummy_desc;
+
+ /* clear the structure so that the mode field is set to zero unless
+ * we set it to the correct value right at the end of this function */
+ memset (desc, 0, sizeof *desc);
+
+ /* skip leading spaces. Fixme: what is with trailing spaces? */
+ for(s = name; *s && spacep (s); s++ )
+ ;
+
+ switch (*s) {
+ case 0: /* empty string is an error */
+ return 0;
+
+#if 0
+ case '.': /* an email address, compare from end */
+ mode = KEYDB_SEARCH_MODE_MAILEND;
+ s++;
+ desc->u.name = s;
+ break;
+#endif
+
+ case '<': /* an email address */
+ mode = KEYDB_SEARCH_MODE_MAIL;
+ desc->u.name = s;
+ break;
+
+ case '@': /* part of an email address */
+ mode = KEYDB_SEARCH_MODE_MAILSUB;
+ s++;
+ desc->u.name = s;
+ break;
+
+ case '=': /* exact compare */
+ mode = KEYDB_SEARCH_MODE_EXACT;
+ s++;
+ desc->u.name = s;
+ break;
+
+ case '*': /* case insensitive substring search */
+ mode = KEYDB_SEARCH_MODE_SUBSTR;
+ s++;
+ desc->u.name = s;
+ break;
+
+#if 0
+ case '+': /* compare individual words */
+ mode = KEYDB_SEARCH_MODE_WORDS;
+ s++;
+ desc->u.name = s;
+ break;
+#endif
+
+ case '#': /* local user id */
+ return 0; /* This is now obsolete and van't not be used anymore*/
+
+ case ':': /*Unified fingerprint */
+ {
+ const char *se, *si;
+ int i;
+
+ se = strchr( ++s,':');
+ if ( !se )
+ return 0;
+ for (i=0,si=s; si < se; si++, i++ ) {
+ if ( !strchr("01234567890abcdefABCDEF", *si ) )
+ return 0; /* invalid digit */
+ }
+ if (i != 32 && i != 40)
+ return 0; /* invalid length of fpr*/
+ for (i=0,si=s; si < se; i++, si +=2)
+ desc->u.fpr[i] = hextobyte(si);
+ for ( ; i < 20; i++)
+ desc->u.fpr[i]= 0;
+ s = se + 1;
+ mode = KEYDB_SEARCH_MODE_FPR;
+ }
+ break;
+
+ default:
+ if (s[0] == '0' && s[1] == 'x') {
+ hexprefix = 1;
+ s += 2;
+ }
+
+ hexlength = strspn(s, "0123456789abcdefABCDEF");
+ if (hexlength >= 8 && s[hexlength] =='!') {
+ desc->exact = 1;
+ hexlength++; /* just for the following check */
+ }
+
+ /* check if a hexadecimal number is terminated by EOS or blank */
+ if (hexlength && s[hexlength] && !spacep(s+hexlength)) {
+ if (hexprefix) /* a "0x" prefix without correct */
+ return 0; /* termination is an error */
+ else /* The first chars looked like */
+ hexlength = 0; /* a hex number, but really were not. */
+ }
+
+ if (desc->exact)
+ hexlength--;
+
+ if (hexlength == 8
+ || (!hexprefix && hexlength == 9 && *s == '0')){
+ /* short keyid */
+ if (hexlength == 9)
+ s++;
+ desc->u.kid[0] = 0;
+ desc->u.kid[1] = strtoul( s, NULL, 16 );
+ mode = KEYDB_SEARCH_MODE_SHORT_KID;
+ }
+ else if (hexlength == 16
+ || (!hexprefix && hexlength == 17 && *s == '0')) {
+ /* complete keyid */
+ char buf[9];
+ if (hexlength == 17)
+ s++;
+ mem2str(buf, s, 9 );
+ desc->u.kid[0] = strtoul( buf, NULL, 16 );
+ desc->u.kid[1] = strtoul( s+8, NULL, 16 );
+ mode = KEYDB_SEARCH_MODE_LONG_KID;
+ }
+ else if (hexlength == 32 || (!hexprefix && hexlength == 33
+ && *s == '0')) {
+ /* md5 fingerprint */
+ int i;
+ if (hexlength == 33)
+ s++;
+ memset(desc->u.fpr+16, 0, 4);
+ for (i=0; i < 16; i++, s+=2) {
+ int c = hextobyte(s);
+ if (c == -1)
+ return 0;
+ desc->u.fpr[i] = c;
+ }
+ mode = KEYDB_SEARCH_MODE_FPR16;
+ }
+ else if (hexlength == 40 || (!hexprefix && hexlength == 41
+ && *s == '0')) {
+ /* sha1/rmd160 fingerprint */
+ int i;
+ if (hexlength == 41)
+ s++;
+ for (i=0; i < 20; i++, s+=2) {
+ int c = hextobyte(s);
+ if (c == -1)
+ return 0;
+ desc->u.fpr[i] = c;
+ }
+ mode = KEYDB_SEARCH_MODE_FPR20;
+ }
+ else {
+ if (hexprefix) /* This was a hex number with a prefix */
+ return 0; /* and a wrong length */
+
+ desc->exact = 0;
+ desc->u.name = s;
+ mode = KEYDB_SEARCH_MODE_SUBSTR; /* default mode */
+ }
+ }
+
+ desc->mode = mode;
+ return mode;
+}
+
+
+static int
+skip_unusable(void *dummy,u32 *keyid,PKT_user_id *uid)
+{
+ int unusable=0;
+ KBNODE keyblock;
+
+ keyblock=get_pubkeyblock(keyid);
+ if(!keyblock)
+ {
+ log_error("error checking usability status of %s\n",keystr(keyid));
+ goto leave;
+ }
+
+ /* Is the user ID in question revoked/expired? */
+ if(uid)
+ {
+ KBNODE node;
+
+ for(node=keyblock;node;node=node->next)
+ {
+ if(node->pkt->pkttype==PKT_USER_ID)
+ {
+ if(cmp_user_ids(uid,node->pkt->pkt.user_id)==0
+ && (node->pkt->pkt.user_id->is_revoked
+ || node->pkt->pkt.user_id->is_expired))
+ {
+ unusable=1;
+ break;
+ }
+ }
+ }
+ }
+
+ if(!unusable)
+ unusable=pk_is_disabled(keyblock->pkt->pkt.public_key);
+
+ leave:
+ release_kbnode(keyblock);
+ return unusable;
+}
+
+/****************
+ * Try to get the pubkey by the userid. This function looks for the
+ * first pubkey certificate which has the given name in a user_id. if
+ * pk/sk has the pubkey algo set, the function will only return a
+ * pubkey with that algo. If namelist is NULL, the first key is
+ * returned. The caller should provide storage for either the pk or
+ * the sk. If ret_kb is not NULL the function will return the
+ * keyblock there.
+ */
+
+static int
+key_byname( GETKEY_CTX *retctx, STRLIST namelist,
+ PKT_public_key *pk, PKT_secret_key *sk,
+ int secmode, int include_unusable,
+ KBNODE *ret_kb, KEYDB_HANDLE *ret_kdbhd )
+{
+ int rc = 0;
+ int n;
+ STRLIST r;
+ GETKEY_CTX ctx;
+ KBNODE help_kb = NULL;
+
+ if( retctx ) {/* reset the returned context in case of error */
+ assert (!ret_kdbhd); /* not allowed because the handle is
+ stored in the context */
+ *retctx = NULL;
+ }
+ if (ret_kdbhd)
+ *ret_kdbhd = NULL;
+
+ if(!namelist)
+ {
+ ctx = xmalloc_clear (sizeof *ctx);
+ ctx->nitems = 1;
+ ctx->items[0].mode=KEYDB_SEARCH_MODE_FIRST;
+ if(!include_unusable)
+ ctx->items[0].skipfnc=skip_unusable;
+ }
+ else
+ {
+ /* build the search context */
+ for(n=0, r=namelist; r; r = r->next )
+ n++;
+
+ ctx = xmalloc_clear (sizeof *ctx + (n-1)*sizeof ctx->items );
+ ctx->nitems = n;
+
+ for(n=0, r=namelist; r; r = r->next, n++ )
+ {
+ classify_user_id (r->d, &ctx->items[n]);
+
+ if (ctx->items[n].exact)
+ ctx->exact = 1;
+ if (!ctx->items[n].mode)
+ {
+ xfree (ctx);
+ return G10ERR_INV_USER_ID;
+ }
+ if(!include_unusable
+ && ctx->items[n].mode!=KEYDB_SEARCH_MODE_SHORT_KID
+ && ctx->items[n].mode!=KEYDB_SEARCH_MODE_LONG_KID
+ && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR16
+ && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR20
+ && ctx->items[n].mode!=KEYDB_SEARCH_MODE_FPR)
+ ctx->items[n].skipfnc=skip_unusable;
+ }
+ }
+
+ ctx->kr_handle = keydb_new (secmode);
+ if ( !ret_kb )
+ ret_kb = &help_kb;
+
+ if( secmode ) {
+ if (sk) {
+ ctx->req_algo = sk->req_algo;
+ ctx->req_usage = sk->req_usage;
+ }
+ rc = lookup( ctx, ret_kb, 1 );
+ if ( !rc && sk ) {
+ sk_from_block ( ctx, sk, *ret_kb );
+ }
+ }
+ else {
+ if (pk) {
+ ctx->req_algo = pk->req_algo;
+ ctx->req_usage = pk->req_usage;
+ }
+ rc = lookup( ctx, ret_kb, 0 );
+ if ( !rc && pk ) {
+ pk_from_block ( ctx, pk, *ret_kb );
+ }
+ }
+
+ release_kbnode ( help_kb );
+
+ if (retctx) /* caller wants the context */
+ *retctx = ctx;
+ else {
+ if (ret_kdbhd) {
+ *ret_kdbhd = ctx->kr_handle;
+ ctx->kr_handle = NULL;
+ }
+ get_pubkey_end (ctx);
+ }
+
+ return rc;
+}
+
+
+
+/* Find a public key from NAME and return the keyblock or the key. If
+ ret_kdb is not NULL, the KEYDB handle used to locate this keyblock
+ is returned and the caller is responsible for closing it. If a key
+ was not found and NAME is a valid RFC822 mailbox and PKA retrieval
+ has been enabled, we try to import the pkea via the PKA
+ mechanism. */
+int
+get_pubkey_byname (PKT_public_key *pk,
+ const char *name, KBNODE *ret_keyblock,
+ KEYDB_HANDLE *ret_kdbhd, int include_unusable )
+{
+ int rc;
+ STRLIST namelist = NULL;
+
+ add_to_strlist( &namelist, name );
+
+ rc = key_byname( NULL, namelist, pk, NULL, 0,
+ include_unusable, ret_keyblock, ret_kdbhd);
+
+ /* If the requested name resembles a valid mailbox and automatic
+ retrieval has been enabled, we try to import the key. */
+
+ if (rc == G10ERR_NO_PUBKEY && is_valid_mailbox(name))
+ {
+ struct akl *akl;
+
+ for(akl=opt.auto_key_locate;akl;akl=akl->next)
+ {
+ unsigned char *fpr=NULL;
+ size_t fpr_len;
+
+ switch(akl->type)
+ {
+ case AKL_CERT:
+ glo_ctrl.in_auto_key_retrieve++;
+ rc=keyserver_import_cert(name,&fpr,&fpr_len);
+ glo_ctrl.in_auto_key_retrieve--;
+
+ if(rc==0)
+ log_info(_("automatically retrieved `%s' via %s\n"),
+ name,"DNS CERT");
+ break;
+
+ case AKL_PKA:
+ glo_ctrl.in_auto_key_retrieve++;
+ rc=keyserver_import_pka(name,&fpr,&fpr_len);
+ glo_ctrl.in_auto_key_retrieve--;
+
+ if(rc==0)
+ log_info(_("automatically retrieved `%s' via %s\n"),
+ name,"PKA");
+ break;
+
+ case AKL_LDAP:
+ glo_ctrl.in_auto_key_retrieve++;
+ rc=keyserver_import_ldap(name,&fpr,&fpr_len);
+ glo_ctrl.in_auto_key_retrieve--;
+
+ if(rc==0)
+ log_info(_("automatically retrieved `%s' via %s\n"),
+ name,"LDAP");
+ break;
+
+ case AKL_KEYSERVER:
+ /* Strictly speaking, we don't need to only use a valid
+ mailbox for the getname search, but it helps cut down
+ on the problem of searching for something like "john"
+ and getting a whole lot of keys back. */
+ if(opt.keyserver)
+ {
+ glo_ctrl.in_auto_key_retrieve++;
+ rc=keyserver_import_name(name,&fpr,&fpr_len,opt.keyserver);
+ glo_ctrl.in_auto_key_retrieve--;
+
+ if(rc==0)
+ log_info(_("automatically retrieved `%s' via %s\n"),
+ name,opt.keyserver->uri);
+ }
+ break;
+
+ case AKL_SPEC:
+ {
+ struct keyserver_spec *keyserver;
+
+ keyserver=keyserver_match(akl->spec);
+ glo_ctrl.in_auto_key_retrieve++;
+ rc=keyserver_import_name(name,&fpr,&fpr_len,keyserver);
+ glo_ctrl.in_auto_key_retrieve--;
+
+ if(rc==0)
+ log_info(_("automatically retrieved `%s' via %s\n"),
+ name,akl->spec->uri);
+ }
+ break;
+ }
+
+ /* Use the fingerprint of the key that we actually fetched.
+ This helps prevent problems where the key that we fetched
+ doesn't have the same name that we used to fetch it. In
+ the case of CERT and PKA, this is an actual security
+ requirement as the URL might point to a key put in by an
+ attacker. By forcing the use of the fingerprint, we
+ won't use the attacker's key here. */
+ if(rc==0 && fpr)
+ {
+ int i;
+ char fpr_string[MAX_FINGERPRINT_LEN*2+1];
+
+ assert(fpr_len<=MAX_FINGERPRINT_LEN);
+
+ free_strlist(namelist);
+ namelist=NULL;
+
+ for(i=0;i<fpr_len;i++)
+ sprintf(fpr_string+2*i,"%02X",fpr[i]);
+
+ if(opt.verbose)
+ log_info("auto-key-locate found fingerprint %s\n",fpr_string);
+
+ add_to_strlist( &namelist, fpr_string );
+
+ xfree(fpr);
+ }
+
+ rc = key_byname( NULL, namelist, pk, NULL, 0,
+ include_unusable, ret_keyblock, ret_kdbhd);
+ if(rc!=G10ERR_NO_PUBKEY)
+ break;
+ }
+ }
+
+ free_strlist( namelist );
+ return rc;
+}
+
+int
+get_pubkey_bynames( GETKEY_CTX *retctx, PKT_public_key *pk,
+ STRLIST names, KBNODE *ret_keyblock )
+{
+ return key_byname( retctx, names, pk, NULL, 0, 1, ret_keyblock, NULL);
+}
+
+int
+get_pubkey_next( GETKEY_CTX ctx, PKT_public_key *pk, KBNODE *ret_keyblock )
+{
+ int rc;
+
+ rc = lookup( ctx, ret_keyblock, 0 );
+ if ( !rc && pk && ret_keyblock )
+ pk_from_block ( ctx, pk, *ret_keyblock );
+
+ return rc;
+}
+
+void
+get_pubkey_end( GETKEY_CTX ctx )
+{
+ if( ctx ) {
+ memset (&ctx->kbpos, 0, sizeof ctx->kbpos);
+ keydb_release (ctx->kr_handle);
+ if( !ctx->not_allocated )
+ xfree( ctx );
+ }
+}
+
+
+/****************
+ * Search for a key with the given fingerprint.
+ * FIXME:
+ * We should replace this with the _byname function. Thiscsan be done
+ * by creating a userID conforming to the unified fingerprint style.
+ */
+int
+get_pubkey_byfprint( PKT_public_key *pk,
+ const byte *fprint, size_t fprint_len)
+{
+ int rc;
+
+ if( fprint_len == 20 || fprint_len == 16 ) {
+ struct getkey_ctx_s ctx;
+ KBNODE kb = NULL;
+
+ memset( &ctx, 0, sizeof ctx );
+ ctx.exact = 1 ;
+ ctx.not_allocated = 1;
+ ctx.kr_handle = keydb_new (0);
+ ctx.nitems = 1;
+ ctx.items[0].mode = fprint_len==16? KEYDB_SEARCH_MODE_FPR16
+ : KEYDB_SEARCH_MODE_FPR20;
+ memcpy( ctx.items[0].u.fpr, fprint, fprint_len );
+ rc = lookup( &ctx, &kb, 0 );
+ if (!rc && pk )
+ pk_from_block ( &ctx, pk, kb );
+ release_kbnode ( kb );
+ get_pubkey_end( &ctx );
+ }
+ else
+ rc = G10ERR_GENERAL; /* Oops */
+ return rc;
+}
+
+
+/* Get a public key and store it into the allocated pk. This function
+ differs from get_pubkey_byfprint() in that it does not do a check
+ of the key to avoid recursion. It should be used only in very
+ certain cases. PK may be NULL to check just for the existance of
+ the key. */
+int
+get_pubkey_byfprint_fast (PKT_public_key *pk,
+ const byte *fprint, size_t fprint_len)
+{
+ int rc = 0;
+ KEYDB_HANDLE hd;
+ KBNODE keyblock;
+ byte fprbuf[MAX_FINGERPRINT_LEN];
+ int i;
+
+ for (i=0; i < MAX_FINGERPRINT_LEN && i < fprint_len; i++)
+ fprbuf[i] = fprint[i];
+ while (i < MAX_FINGERPRINT_LEN)
+ fprbuf[i++] = 0;
+
+ hd = keydb_new (0);
+ rc = keydb_search_fpr (hd, fprbuf);
+ if (rc == -1)
+ {
+ keydb_release (hd);
+ return G10ERR_NO_PUBKEY;
+ }
+ rc = keydb_get_keyblock (hd, &keyblock);
+ keydb_release (hd);
+ if (rc)
+ {
+ log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc));
+ return G10ERR_NO_PUBKEY;
+ }
+
+ assert ( keyblock->pkt->pkttype == PKT_PUBLIC_KEY
+ || keyblock->pkt->pkttype == PKT_PUBLIC_SUBKEY );
+ if (pk)
+ copy_public_key (pk, keyblock->pkt->pkt.public_key );
+ release_kbnode (keyblock);
+
+ /* Not caching key here since it won't have all of the fields
+ properly set. */
+
+ return 0;
+}
+
+/****************
+ * Search for a key with the given fingerprint and return the
+ * complete keyblock which may have more than only this key.
+ */
+int
+get_keyblock_byfprint( KBNODE *ret_keyblock, const byte *fprint,
+ size_t fprint_len )
+{
+ int rc;
+
+ if( fprint_len == 20 || fprint_len == 16 ) {
+ struct getkey_ctx_s ctx;
+
+ memset( &ctx, 0, sizeof ctx );
+ ctx.not_allocated = 1;
+ ctx.kr_handle = keydb_new (0);
+ ctx.nitems = 1;
+ ctx.items[0].mode = fprint_len==16? KEYDB_SEARCH_MODE_FPR16
+ : KEYDB_SEARCH_MODE_FPR20;
+ memcpy( ctx.items[0].u.fpr, fprint, fprint_len );
+ rc = lookup( &ctx, ret_keyblock, 0 );
+ get_pubkey_end( &ctx );
+ }
+ else
+ rc = G10ERR_GENERAL; /* Oops */
+
+ return rc;
+}
+
+
+/****************
+ * Get a secret key by name and store it into sk
+ * If NAME is NULL use the default key
+ */
+static int
+get_seckey_byname2( GETKEY_CTX *retctx,
+ PKT_secret_key *sk, const char *name, int unprotect,
+ KBNODE *retblock )
+{
+ STRLIST namelist = NULL;
+ int rc,include_unusable=1;
+
+ /* If we have no name, try to use the default secret key. If we
+ have no default, we'll use the first usable one. */
+
+ if( !name && opt.def_secret_key && *opt.def_secret_key )
+ add_to_strlist( &namelist, opt.def_secret_key );
+ else if(name)
+ add_to_strlist( &namelist, name );
+ else
+ include_unusable=0;
+
+ rc = key_byname( retctx, namelist, NULL, sk, 1, include_unusable,
+ retblock, NULL );
+
+ free_strlist( namelist );
+
+ if( !rc && unprotect )
+ rc = check_secret_key( sk, 0 );
+
+ return rc;
+}
+
+int
+get_seckey_byname( PKT_secret_key *sk, const char *name, int unlock )
+{
+ return get_seckey_byname2 ( NULL, sk, name, unlock, NULL );
+}
+
+
+int
+get_seckey_bynames( GETKEY_CTX *retctx, PKT_secret_key *sk,
+ STRLIST names, KBNODE *ret_keyblock )
+{
+ return key_byname( retctx, names, NULL, sk, 1, 1, ret_keyblock, NULL );
+}
+
+
+int
+get_seckey_next( GETKEY_CTX ctx, PKT_secret_key *sk, KBNODE *ret_keyblock )
+{
+ int rc;
+
+ rc = lookup( ctx, ret_keyblock, 1 );
+ if ( !rc && sk && ret_keyblock )
+ sk_from_block ( ctx, sk, *ret_keyblock );
+
+ return rc;
+}
+
+
+void
+get_seckey_end( GETKEY_CTX ctx )
+{
+ get_pubkey_end( ctx );
+}
+
+
+/****************
+ * Search for a key with the given fingerprint.
+ * FIXME:
+ * We should replace this with the _byname function. Thiscsan be done
+ * by creating a userID conforming to the unified fingerprint style.
+ */
+int
+get_seckey_byfprint( PKT_secret_key *sk,
+ const byte *fprint, size_t fprint_len)
+{
+ int rc;
+
+ if( fprint_len == 20 || fprint_len == 16 ) {
+ struct getkey_ctx_s ctx;
+ KBNODE kb = NULL;
+
+ memset( &ctx, 0, sizeof ctx );
+ ctx.exact = 1 ;
+ ctx.not_allocated = 1;
+ ctx.kr_handle = keydb_new (1);
+ ctx.nitems = 1;
+ ctx.items[0].mode = fprint_len==16? KEYDB_SEARCH_MODE_FPR16
+ : KEYDB_SEARCH_MODE_FPR20;
+ memcpy( ctx.items[0].u.fpr, fprint, fprint_len );
+ rc = lookup( &ctx, &kb, 1 );
+ if (!rc && sk )
+ sk_from_block ( &ctx, sk, kb );
+ release_kbnode ( kb );
+ get_seckey_end( &ctx );
+ }
+ else
+ rc = G10ERR_GENERAL; /* Oops */
+ return rc;
+}
+
+
+/* Search for a secret key with the given fingerprint and return the
+ complete keyblock which may have more than only this key. */
+int
+get_seckeyblock_byfprint (KBNODE *ret_keyblock, const byte *fprint,
+ size_t fprint_len )
+{
+ int rc;
+ struct getkey_ctx_s ctx;
+
+ if (fprint_len != 20 && fprint_len == 16)
+ return G10ERR_GENERAL; /* Oops */
+
+ memset (&ctx, 0, sizeof ctx);
+ ctx.not_allocated = 1;
+ ctx.kr_handle = keydb_new (1);
+ ctx.nitems = 1;
+ ctx.items[0].mode = (fprint_len==16
+ ? KEYDB_SEARCH_MODE_FPR16
+ : KEYDB_SEARCH_MODE_FPR20);
+ memcpy (ctx.items[0].u.fpr, fprint, fprint_len);
+ rc = lookup (&ctx, ret_keyblock, 1);
+ get_seckey_end (&ctx);
+
+ return rc;
+}
+
+
+
+/************************************************
+ ************* Merging stuff ********************
+ ************************************************/
+
+/****************
+ * merge all selfsignatures with the keys.
+ * FIXME: replace this at least for the public key parts
+ * by merge_selfsigs.
+ * It is still used in keyedit.c and
+ * at 2 or 3 other places - check whether it is really needed.
+ * It might be needed by the key edit and import stuff because
+ * the keylock is changed.
+ */
+void
+merge_keys_and_selfsig( KBNODE keyblock )
+{
+ PKT_public_key *pk = NULL;
+ PKT_secret_key *sk = NULL;
+ PKT_signature *sig;
+ KBNODE k;
+ u32 kid[2] = { 0, 0 };
+ u32 sigdate = 0;
+
+ if (keyblock && keyblock->pkt->pkttype == PKT_PUBLIC_KEY ) {
+ /* divert to our new function */
+ merge_selfsigs (keyblock);
+ return;
+ }
+ /* still need the old one because the new one can't handle secret keys */
+
+ for(k=keyblock; k; k = k->next ) {
+ if( k->pkt->pkttype == PKT_PUBLIC_KEY
+ || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+ pk = k->pkt->pkt.public_key; sk = NULL;
+ if( pk->version < 4 )
+ pk = NULL; /* not needed for old keys */
+ else if( k->pkt->pkttype == PKT_PUBLIC_KEY )
+ keyid_from_pk( pk, kid );
+ else if( !pk->expiredate ) { /* and subkey */
+ /* insert the expiration date here */
+ /*FIXME!!! pk->expiredate = subkeys_expiretime( k, kid );*/
+ }
+ sigdate = 0;
+ }
+ else if( k->pkt->pkttype == PKT_SECRET_KEY
+ || k->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+ pk = NULL; sk = k->pkt->pkt.secret_key;
+ if( sk->version < 4 )
+ sk = NULL;
+ else if( k->pkt->pkttype == PKT_SECRET_KEY )
+ keyid_from_sk( sk, kid );
+ sigdate = 0;
+ }
+ else if( (pk || sk ) && k->pkt->pkttype == PKT_SIGNATURE
+ && (sig=k->pkt->pkt.signature)->sig_class >= 0x10
+ && sig->sig_class <= 0x30 && sig->version > 3
+ && !(sig->sig_class == 0x18 || sig->sig_class == 0x28)
+ && sig->keyid[0] == kid[0] && sig->keyid[1] == kid[1] ) {
+ /* okay this is a self-signature which can be used.
+ * This is not used for subkey binding signature, becuase this
+ * is done above.
+ * FIXME: We should only use this if the signature is valid
+ * but this is time consuming - we must provide another
+ * way to handle this
+ */
+ const byte *p;
+ u32 ed;
+
+ p = parse_sig_subpkt( sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL );
+ if( pk ) {
+ ed = p? pk->timestamp + buffer_to_u32(p):0;
+ if( sig->timestamp > sigdate ) {
+ pk->expiredate = ed;
+ sigdate = sig->timestamp;
+ }
+ }
+ else {
+ ed = p? sk->timestamp + buffer_to_u32(p):0;
+ if( sig->timestamp > sigdate ) {
+ sk->expiredate = ed;
+ sigdate = sig->timestamp;
+ }
+ }
+ }
+
+ if(pk && (pk->expiredate==0 ||
+ (pk->max_expiredate && pk->expiredate>pk->max_expiredate)))
+ pk->expiredate=pk->max_expiredate;
+
+ if(sk && (sk->expiredate==0 ||
+ (sk->max_expiredate && sk->expiredate>sk->max_expiredate)))
+ sk->expiredate=sk->max_expiredate;
+ }
+}
+
+static int
+parse_key_usage(PKT_signature *sig)
+{
+ int key_usage=0;
+ const byte *p;
+ size_t n;
+ byte flags;
+
+ p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_KEY_FLAGS,&n);
+ if(p && n)
+ {
+ /* first octet of the keyflags */
+ flags=*p;
+
+ if(flags & 1)
+ {
+ key_usage |= PUBKEY_USAGE_CERT;
+ flags&=~1;
+ }
+
+ if(flags & 2)
+ {
+ key_usage |= PUBKEY_USAGE_SIG;
+ flags&=~2;
+ }
+
+ /* We do not distinguish between encrypting communications and
+ encrypting storage. */
+ if(flags & (0x04|0x08))
+ {
+ key_usage |= PUBKEY_USAGE_ENC;
+ flags&=~(0x04|0x08);
+ }
+
+ if(flags & 0x20)
+ {
+ key_usage |= PUBKEY_USAGE_AUTH;
+ flags&=~0x20;
+ }
+
+ if(flags)
+ key_usage |= PUBKEY_USAGE_UNKNOWN;
+ }
+
+ /* We set PUBKEY_USAGE_UNKNOWN to indicate that this key has a
+ capability that we do not handle. This serves to distinguish
+ between a zero key usage which we handle as the default
+ capabilities for that algorithm, and a usage that we do not
+ handle. */
+
+ return key_usage;
+}
+
+/*
+ * Apply information from SIGNODE (which is the valid self-signature
+ * associated with that UID) to the UIDNODE:
+ * - wether the UID has been revoked
+ * - assumed creation date of the UID
+ * - temporary store the keyflags here
+ * - temporary store the key expiration time here
+ * - mark whether the primary user ID flag hat been set.
+ * - store the preferences
+ */
+static void
+fixup_uidnode ( KBNODE uidnode, KBNODE signode, u32 keycreated )
+{
+ PKT_user_id *uid = uidnode->pkt->pkt.user_id;
+ PKT_signature *sig = signode->pkt->pkt.signature;
+ const byte *p, *sym, *hash, *zip;
+ size_t n, nsym, nhash, nzip;
+
+ sig->flags.chosen_selfsig = 1; /* we chose this one */
+ uid->created = 0; /* not created == invalid */
+ if ( IS_UID_REV ( sig ) ) {
+ uid->is_revoked = 1;
+ return; /* has been revoked */
+ }
+
+ uid->expiredate = sig->expiredate;
+
+ if(sig->flags.expired)
+ {
+ uid->is_expired = 1;
+ return; /* has expired */
+ }
+
+ uid->created = sig->timestamp; /* this one is okay */
+ uid->selfsigversion = sig->version;
+ /* If we got this far, it's not expired :) */
+ uid->is_expired = 0;
+
+ /* store the key flags in the helper variable for later processing */
+ uid->help_key_usage=parse_key_usage(sig);
+
+ /* ditto for the key expiration */
+ p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL);
+ if( p && buffer_to_u32(p) )
+ uid->help_key_expire = keycreated + buffer_to_u32(p);
+ else
+ uid->help_key_expire = 0;
+
+ /* Set the primary user ID flag - we will later wipe out some
+ * of them to only have one in our keyblock */
+ uid->is_primary = 0;
+ p = parse_sig_subpkt ( sig->hashed, SIGSUBPKT_PRIMARY_UID, NULL );
+ if ( p && *p )
+ uid->is_primary = 2;
+ /* We could also query this from the unhashed area if it is not in
+ * the hased area and then later try to decide which is the better
+ * there should be no security problem with this.
+ * For now we only look at the hashed one.
+ */
+
+ /* Now build the preferences list. These must come from the
+ hashed section so nobody can modify the ciphers a key is
+ willing to accept. */
+ p = parse_sig_subpkt ( sig->hashed, SIGSUBPKT_PREF_SYM, &n );
+ sym = p; nsym = p?n:0;
+ p = parse_sig_subpkt ( sig->hashed, SIGSUBPKT_PREF_HASH, &n );
+ hash = p; nhash = p?n:0;
+ p = parse_sig_subpkt ( sig->hashed, SIGSUBPKT_PREF_COMPR, &n );
+ zip = p; nzip = p?n:0;
+ if (uid->prefs)
+ xfree (uid->prefs);
+ n = nsym + nhash + nzip;
+ if (!n)
+ uid->prefs = NULL;
+ else {
+ uid->prefs = xmalloc (sizeof (*uid->prefs) * (n+1));
+ n = 0;
+ for (; nsym; nsym--, n++) {
+ uid->prefs[n].type = PREFTYPE_SYM;
+ uid->prefs[n].value = *sym++;
+ }
+ for (; nhash; nhash--, n++) {
+ uid->prefs[n].type = PREFTYPE_HASH;
+ uid->prefs[n].value = *hash++;
+ }
+ for (; nzip; nzip--, n++) {
+ uid->prefs[n].type = PREFTYPE_ZIP;
+ uid->prefs[n].value = *zip++;
+ }
+ uid->prefs[n].type = PREFTYPE_NONE; /* end of list marker */
+ uid->prefs[n].value = 0;
+ }
+
+ /* see whether we have the MDC feature */
+ uid->flags.mdc = 0;
+ p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n);
+ if (p && n && (p[0] & 0x01))
+ uid->flags.mdc = 1;
+
+ /* and the keyserver modify flag */
+ uid->flags.ks_modify = 1;
+ p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KS_FLAGS, &n);
+ if (p && n && (p[0] & 0x80))
+ uid->flags.ks_modify = 0;
+}
+
+static void
+sig_to_revoke_info(PKT_signature *sig,struct revoke_info *rinfo)
+{
+ rinfo->date = sig->timestamp;
+ rinfo->algo = sig->pubkey_algo;
+ rinfo->keyid[0] = sig->keyid[0];
+ rinfo->keyid[1] = sig->keyid[1];
+}
+
+static void
+merge_selfsigs_main(KBNODE keyblock, int *r_revoked, struct revoke_info *rinfo)
+{
+ PKT_public_key *pk = NULL;
+ KBNODE k;
+ u32 kid[2];
+ u32 sigdate, uiddate, uiddate2;
+ KBNODE signode, uidnode, uidnode2;
+ u32 curtime = make_timestamp ();
+ unsigned int key_usage = 0;
+ u32 keytimestamp = 0;
+ u32 key_expire = 0;
+ int key_expire_seen = 0;
+ byte sigversion = 0;
+
+ *r_revoked = 0;
+ memset(rinfo,0,sizeof(*rinfo));
+
+ if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY )
+ BUG ();
+ pk = keyblock->pkt->pkt.public_key;
+ keytimestamp = pk->timestamp;
+
+ keyid_from_pk( pk, kid );
+ pk->main_keyid[0] = kid[0];
+ pk->main_keyid[1] = kid[1];
+
+ if ( pk->version < 4 ) {
+ /* before v4 the key packet itself contains the expiration
+ * date and there was no way to change it, so we start with
+ * the one from the key packet */
+ key_expire = pk->max_expiredate;
+ key_expire_seen = 1;
+ }
+
+ /* first pass: find the latest direct key self-signature.
+ * We assume that the newest one overrides all others
+ */
+
+ /* In case this key was already merged */
+ xfree(pk->revkey);
+ pk->revkey=NULL;
+ pk->numrevkeys=0;
+
+ signode = NULL;
+ sigdate = 0; /* helper to find the latest signature */
+ for(k=keyblock; k && k->pkt->pkttype != PKT_USER_ID; k = k->next ) {
+ if ( k->pkt->pkttype == PKT_SIGNATURE ) {
+ PKT_signature *sig = k->pkt->pkt.signature;
+ if ( sig->keyid[0] == kid[0] && sig->keyid[1]==kid[1] ) {
+ if ( check_key_signature( keyblock, k, NULL ) )
+ ; /* signature did not verify */
+ else if ( IS_KEY_REV (sig) ){
+ /* key has been revoked - there is no way to override
+ * such a revocation, so we theoretically can stop now.
+ * We should not cope with expiration times for revocations
+ * here because we have to assume that an attacker can
+ * generate all kinds of signatures. However due to the
+ * fact that the key has been revoked it does not harm
+ * either and by continuing we gather some more info on
+ * that key.
+ */
+ *r_revoked = 1;
+ sig_to_revoke_info(sig,rinfo);
+ }
+ else if ( IS_KEY_SIG (sig) ) {
+ /* Add any revocation keys onto the pk. This is
+ particularly interesting since we normally only
+ get data from the most recent 1F signature, but
+ you need multiple 1F sigs to properly handle
+ revocation keys (PGP does it this way, and a
+ revocation key could be sensitive and hence in a
+ different signature). */
+ if(sig->revkey) {
+ int i;
+
+ pk->revkey=
+ xrealloc(pk->revkey,sizeof(struct revocation_key)*
+ (pk->numrevkeys+sig->numrevkeys));
+
+ for(i=0;i<sig->numrevkeys;i++)
+ memcpy(&pk->revkey[pk->numrevkeys++],
+ sig->revkey[i],
+ sizeof(struct revocation_key));
+ }
+
+ if( sig->timestamp >= sigdate ) {
+ if(sig->flags.expired)
+ ; /* signature has expired - ignore it */
+ else {
+ sigdate = sig->timestamp;
+ signode = k;
+ if( sig->version > sigversion )
+ sigversion = sig->version;
+
+ }
+ }
+ }
+ }
+ }
+ }
+
+ /* Remove dupes from the revocation keys */
+
+ if(pk->revkey)
+ {
+ int i,j,x,changed=0;
+
+ for(i=0;i<pk->numrevkeys;i++)
+ {
+ for(j=i+1;j<pk->numrevkeys;j++)
+ {
+ if(memcmp(&pk->revkey[i],&pk->revkey[j],
+ sizeof(struct revocation_key))==0)
+ {
+ /* remove j */
+
+ for(x=j;x<pk->numrevkeys-1;x++)
+ pk->revkey[x]=pk->revkey[x+1];
+
+ pk->numrevkeys--;
+ j--;
+ changed=1;
+ }
+ }
+ }
+
+ if(changed)
+ pk->revkey=xrealloc(pk->revkey,
+ pk->numrevkeys*sizeof(struct revocation_key));
+ }
+
+ if ( signode )
+ {
+ /* some information from a direct key signature take precedence
+ * over the same information given in UID sigs.
+ */
+ PKT_signature *sig = signode->pkt->pkt.signature;
+ const byte *p;
+
+ key_usage=parse_key_usage(sig);
+
+ p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL);
+ if( p && buffer_to_u32(p) )
+ {
+ key_expire = keytimestamp + buffer_to_u32(p);
+ key_expire_seen = 1;
+ }
+
+ /* mark that key as valid: one direct key signature should
+ * render a key as valid */
+ pk->is_valid = 1;
+ }
+
+ /* pass 1.5: look for key revocation signatures that were not made
+ by the key (i.e. did a revocation key issue a revocation for
+ us?). Only bother to do this if there is a revocation key in
+ the first place and we're not revoked already. */
+
+ if(!*r_revoked && pk->revkey)
+ for(k=keyblock; k && k->pkt->pkttype != PKT_USER_ID; k = k->next )
+ {
+ if ( k->pkt->pkttype == PKT_SIGNATURE )
+ {
+ PKT_signature *sig = k->pkt->pkt.signature;
+
+ if(IS_KEY_REV(sig) &&
+ (sig->keyid[0]!=kid[0] || sig->keyid[1]!=kid[1]))
+ {
+ int rc=check_revocation_keys(pk,sig);
+ if(rc==0)
+ {
+ *r_revoked=2;
+ sig_to_revoke_info(sig,rinfo);
+ /* don't continue checking since we can't be any
+ more revoked than this */
+ break;
+ }
+ else if(rc==G10ERR_NO_PUBKEY)
+ pk->maybe_revoked=1;
+
+ /* A failure here means the sig did not verify, was
+ not issued by a revocation key, or a revocation
+ key loop was broken. If a revocation key isn't
+ findable, however, the key might be revoked and
+ we don't know it. */
+
+ /* TODO: In the future handle subkey and cert
+ revocations? PGP doesn't, but it's in 2440. */
+ }
+ }
+ }
+
+ /* second pass: look at the self-signature of all user IDs */
+ signode = uidnode = NULL;
+ sigdate = 0; /* helper to find the latest signature in one user ID */
+ for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next ) {
+ if ( k->pkt->pkttype == PKT_USER_ID ) {
+ if ( uidnode && signode )
+ {
+ fixup_uidnode ( uidnode, signode, keytimestamp );
+ pk->is_valid=1;
+ }
+ uidnode = k;
+ signode = NULL;
+ sigdate = 0;
+ }
+ else if ( k->pkt->pkttype == PKT_SIGNATURE && uidnode ) {
+ PKT_signature *sig = k->pkt->pkt.signature;
+ if ( sig->keyid[0] == kid[0] && sig->keyid[1]==kid[1] ) {
+ if ( check_key_signature( keyblock, k, NULL ) )
+ ; /* signature did not verify */
+ else if ( (IS_UID_SIG (sig) || IS_UID_REV (sig))
+ && sig->timestamp >= sigdate )
+ {
+ /* Note: we allow to invalidate cert revocations
+ * by a newer signature. An attacker can't use this
+ * because a key should be revoced with a key revocation.
+ * The reason why we have to allow for that is that at
+ * one time an email address may become invalid but later
+ * the same email address may become valid again (hired,
+ * fired, hired again).
+ */
+
+ sigdate = sig->timestamp;
+ signode = k;
+ signode->pkt->pkt.signature->flags.chosen_selfsig=0;
+ if( sig->version > sigversion )
+ sigversion = sig->version;
+ }
+ }
+ }
+ }
+ if ( uidnode && signode ) {
+ fixup_uidnode ( uidnode, signode, keytimestamp );
+ pk->is_valid = 1;
+ }
+
+ /* If the key isn't valid yet, and we have
+ --allow-non-selfsigned-uid set, then force it valid. */
+ if(!pk->is_valid && opt.allow_non_selfsigned_uid)
+ {
+ if(opt.verbose)
+ log_info(_("Invalid key %s made valid by"
+ " --allow-non-selfsigned-uid\n"),keystr_from_pk(pk));
+ pk->is_valid = 1;
+ }
+
+ /* The key STILL isn't valid, so try and find an ultimately
+ trusted signature. */
+ if(!pk->is_valid)
+ {
+ uidnode=NULL;
+
+ for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k=k->next)
+ {
+ if ( k->pkt->pkttype == PKT_USER_ID )
+ uidnode = k;
+ else if ( k->pkt->pkttype == PKT_SIGNATURE && uidnode )
+ {
+ PKT_signature *sig = k->pkt->pkt.signature;
+
+ if(sig->keyid[0] != kid[0] || sig->keyid[1]!=kid[1])
+ {
+ PKT_public_key *ultimate_pk;
+
+ ultimate_pk=xmalloc_clear(sizeof(*ultimate_pk));
+
+ /* We don't want to use the full get_pubkey to
+ avoid infinite recursion in certain cases.
+ There is no reason to check that an ultimately
+ trusted key is still valid - if it has been
+ revoked or the user should also renmove the
+ ultimate trust flag. */
+ if(get_pubkey_fast(ultimate_pk,sig->keyid)==0
+ && check_key_signature2(keyblock,k,ultimate_pk,
+ NULL,NULL,NULL,NULL)==0
+ && get_ownertrust(ultimate_pk)==TRUST_ULTIMATE)
+ {
+ free_public_key(ultimate_pk);
+ pk->is_valid=1;
+ break;
+ }
+
+ free_public_key(ultimate_pk);
+ }
+ }
+ }
+ }
+
+ /* Record the highest selfsig version so we know if this is a v3
+ key through and through, or a v3 key with a v4 selfsig
+ somewhere. This is useful in a few places to know if the key
+ must be treated as PGP2-style or OpenPGP-style. Note that a
+ selfsig revocation with a higher version number will also raise
+ this value. This is okay since such a revocation must be
+ issued by the user (i.e. it cannot be issued by someone else to
+ modify the key behavior.) */
+
+ pk->selfsigversion=sigversion;
+
+ /* Now that we had a look at all user IDs we can now get some information
+ * from those user IDs.
+ */
+
+ if ( !key_usage ) {
+ /* find the latest user ID with key flags set */
+ uiddate = 0; /* helper to find the latest user ID */
+ for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY;
+ k = k->next ) {
+ if ( k->pkt->pkttype == PKT_USER_ID ) {
+ PKT_user_id *uid = k->pkt->pkt.user_id;
+ if ( uid->help_key_usage && uid->created > uiddate ) {
+ key_usage = uid->help_key_usage;
+ uiddate = uid->created;
+ }
+ }
+ }
+ }
+ if ( !key_usage ) { /* no key flags at all: get it from the algo */
+ key_usage = openpgp_pk_algo_usage ( pk->pubkey_algo );
+ }
+ else { /* check that the usage matches the usage as given by the algo */
+ int x = openpgp_pk_algo_usage ( pk->pubkey_algo );
+ if ( x ) /* mask it down to the actual allowed usage */
+ key_usage &= x;
+ }
+
+ /* Whatever happens, it's a primary key, so it can certify. */
+ pk->pubkey_usage = key_usage|PUBKEY_USAGE_CERT;
+
+ if ( !key_expire_seen ) {
+ /* find the latest valid user ID with a key expiration set
+ * Note, that this may be a different one from the above because
+ * some user IDs may have no expiration date set */
+ uiddate = 0;
+ for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY;
+ k = k->next ) {
+ if ( k->pkt->pkttype == PKT_USER_ID ) {
+ PKT_user_id *uid = k->pkt->pkt.user_id;
+ if ( uid->help_key_expire && uid->created > uiddate ) {
+ key_expire = uid->help_key_expire;
+ uiddate = uid->created;
+ }
+ }
+ }
+ }
+
+ /* Currently only v3 keys have a maximum expiration date, but I'll
+ bet v5 keys get this feature again. */
+ if(key_expire==0 || (pk->max_expiredate && key_expire>pk->max_expiredate))
+ key_expire=pk->max_expiredate;
+
+ pk->has_expired = key_expire >= curtime? 0 : key_expire;
+ pk->expiredate = key_expire;
+
+ /* Fixme: we should see how to get rid of the expiretime fields but
+ * this needs changes at other places too. */
+
+ /* and now find the real primary user ID and delete all others */
+ uiddate = uiddate2 = 0;
+ uidnode = uidnode2 = NULL;
+ for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next ) {
+ if ( k->pkt->pkttype == PKT_USER_ID &&
+ !k->pkt->pkt.user_id->attrib_data) {
+ PKT_user_id *uid = k->pkt->pkt.user_id;
+ if (uid->is_primary)
+ {
+ if(uid->created > uiddate)
+ {
+ uiddate = uid->created;
+ uidnode = k;
+ }
+ else if(uid->created==uiddate && uidnode)
+ {
+ /* The dates are equal, so we need to do a
+ different (and arbitrary) comparison. This
+ should rarely, if ever, happen. It's good to
+ try and guarantee that two different GnuPG
+ users with two different keyrings at least pick
+ the same primary. */
+ if(cmp_user_ids(uid,uidnode->pkt->pkt.user_id)>0)
+ uidnode=k;
+ }
+ }
+ else
+ {
+ if(uid->created > uiddate2)
+ {
+ uiddate2 = uid->created;
+ uidnode2 = k;
+ }
+ else if(uid->created==uiddate2 && uidnode2)
+ {
+ if(cmp_user_ids(uid,uidnode2->pkt->pkt.user_id)>0)
+ uidnode2=k;
+ }
+ }
+ }
+ }
+ if ( uidnode ) {
+ for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY;
+ k = k->next ) {
+ if ( k->pkt->pkttype == PKT_USER_ID &&
+ !k->pkt->pkt.user_id->attrib_data) {
+ PKT_user_id *uid = k->pkt->pkt.user_id;
+ if ( k != uidnode )
+ uid->is_primary = 0;
+ }
+ }
+ }
+ else if( uidnode2 ) {
+ /* none is flagged primary - use the latest user ID we have,
+ and disambiguate with the arbitrary packet comparison. */
+ uidnode2->pkt->pkt.user_id->is_primary = 1;
+ }
+ else
+ {
+ /* None of our uids were self-signed, so pick the one that
+ sorts first to be the primary. This is the best we can do
+ here since there are no self sigs to date the uids. */
+
+ uidnode = NULL;
+
+ for(k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY;
+ k = k->next )
+ {
+ if(k->pkt->pkttype==PKT_USER_ID
+ && !k->pkt->pkt.user_id->attrib_data)
+ {
+ if(!uidnode)
+ {
+ uidnode=k;
+ uidnode->pkt->pkt.user_id->is_primary=1;
+ continue;
+ }
+ else
+ {
+ if(cmp_user_ids(k->pkt->pkt.user_id,
+ uidnode->pkt->pkt.user_id)>0)
+ {
+ uidnode->pkt->pkt.user_id->is_primary=0;
+ uidnode=k;
+ uidnode->pkt->pkt.user_id->is_primary=1;
+ }
+ else
+ k->pkt->pkt.user_id->is_primary=0; /* just to be
+ safe */
+ }
+ }
+ }
+ }
+}
+
+
+static void
+merge_selfsigs_subkey( KBNODE keyblock, KBNODE subnode )
+{
+ PKT_public_key *mainpk = NULL, *subpk = NULL;
+ PKT_signature *sig;
+ KBNODE k;
+ u32 mainkid[2];
+ u32 sigdate = 0;
+ KBNODE signode;
+ u32 curtime = make_timestamp ();
+ unsigned int key_usage = 0;
+ u32 keytimestamp = 0;
+ u32 key_expire = 0;
+ const byte *p;
+
+ if ( subnode->pkt->pkttype != PKT_PUBLIC_SUBKEY )
+ BUG ();
+ mainpk = keyblock->pkt->pkt.public_key;
+ if ( mainpk->version < 4 )
+ return; /* (actually this should never happen) */
+ keyid_from_pk( mainpk, mainkid );
+ subpk = subnode->pkt->pkt.public_key;
+ keytimestamp = subpk->timestamp;
+
+ subpk->is_valid = 0;
+ subpk->main_keyid[0] = mainpk->main_keyid[0];
+ subpk->main_keyid[1] = mainpk->main_keyid[1];
+
+ /* find the latest key binding self-signature. */
+ signode = NULL;
+ sigdate = 0; /* helper to find the latest signature */
+ for(k=subnode->next; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY;
+ k = k->next ) {
+ if ( k->pkt->pkttype == PKT_SIGNATURE ) {
+ sig = k->pkt->pkt.signature;
+ if ( sig->keyid[0] == mainkid[0] && sig->keyid[1]==mainkid[1] ) {
+ if ( check_key_signature( keyblock, k, NULL ) )
+ ; /* signature did not verify */
+ else if ( IS_SUBKEY_REV (sig) ) {
+ /* Note that this means that the date on a
+ revocation sig does not matter - even if the
+ binding sig is dated after the revocation sig,
+ the subkey is still marked as revoked. This
+ seems ok, as it is just as easy to make new
+ subkeys rather than re-sign old ones as the
+ problem is in the distribution. Plus, PGP (7)
+ does this the same way. */
+ subpk->is_revoked = 1;
+ sig_to_revoke_info(sig,&subpk->revoked);
+ /* although we could stop now, we continue to
+ * figure out other information like the old expiration
+ * time */
+ }
+ else if ( IS_SUBKEY_SIG (sig) && sig->timestamp >= sigdate )
+ {
+ if(sig->flags.expired)
+ ; /* signature has expired - ignore it */
+ else
+ {
+ sigdate = sig->timestamp;
+ signode = k;
+ signode->pkt->pkt.signature->flags.chosen_selfsig=0;
+ }
+ }
+ }
+ }
+ }
+
+ /* no valid key binding */
+ if ( !signode )
+ return;
+
+ sig = signode->pkt->pkt.signature;
+ sig->flags.chosen_selfsig=1; /* so we know which selfsig we chose later */
+
+ key_usage=parse_key_usage(sig);
+ if ( !key_usage )
+ {
+ /* no key flags at all: get it from the algo */
+ key_usage = openpgp_pk_algo_usage ( subpk->pubkey_algo );
+ }
+ else
+ {
+ /* check that the usage matches the usage as given by the algo */
+ int x = openpgp_pk_algo_usage ( subpk->pubkey_algo );
+ if ( x ) /* mask it down to the actual allowed usage */
+ key_usage &= x;
+ }
+
+ subpk->pubkey_usage = key_usage;
+
+ p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE, NULL);
+ if ( p && buffer_to_u32(p) )
+ key_expire = keytimestamp + buffer_to_u32(p);
+ else
+ key_expire = 0;
+ subpk->has_expired = key_expire >= curtime? 0 : key_expire;
+ subpk->expiredate = key_expire;
+
+ /* algo doesn't exist */
+ if(check_pubkey_algo(subpk->pubkey_algo))
+ return;
+
+ subpk->is_valid = 1;
+
+ /* Find the first 0x19 embedded signature on our self-sig. */
+ if(subpk->backsig==0)
+ {
+ int seq=0;
+ size_t n;
+
+ /* We do this while() since there may be other embedded
+ signatures in the future. We only want 0x19 here. */
+ while((p=enum_sig_subpkt(sig->hashed,
+ SIGSUBPKT_SIGNATURE,&n,&seq,NULL)))
+ if(n>3 && ((p[0]==3 && p[2]==0x19) || (p[0]==4 && p[1]==0x19)))
+ break;
+
+ if(p==NULL)
+ {
+ seq=0;
+ /* It is safe to have this in the unhashed area since the
+ 0x19 is located on the selfsig for convenience, not
+ security. */
+ while((p=enum_sig_subpkt(sig->unhashed,SIGSUBPKT_SIGNATURE,
+ &n,&seq,NULL)))
+ if(n>3 && ((p[0]==3 && p[2]==0x19) || (p[0]==4 && p[1]==0x19)))
+ break;
+ }
+
+ if(p)
+ {
+ PKT_signature *backsig=xmalloc_clear(sizeof(PKT_signature));
+ IOBUF backsig_buf=iobuf_temp_with_content(p,n);
+
+ if(parse_signature(backsig_buf,PKT_SIGNATURE,n,backsig)==0)
+ {
+ if(check_backsig(mainpk,subpk,backsig)==0)
+ subpk->backsig=2;
+ else
+ subpk->backsig=1;
+ }
+
+ iobuf_close(backsig_buf);
+ free_seckey_enc(backsig);
+ }
+ }
+}
+
+
+/*
+ * Merge information from the self-signatures with the key, so that
+ * we can later use them more easy.
+ * The function works by first applying the self signatures to the
+ * primary key and the to each subkey.
+ * Here are the rules we use to decide which inormation from which
+ * self-signature is used:
+ * We check all self signatures or validity and ignore all invalid signatures.
+ * All signatures are then ordered by their creation date ....
+ * For the primary key:
+ * FIXME the docs
+ */
+static void
+merge_selfsigs( KBNODE keyblock )
+{
+ KBNODE k;
+ int revoked;
+ struct revoke_info rinfo;
+ PKT_public_key *main_pk;
+ prefitem_t *prefs;
+ int mdc_feature;
+
+ if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY ) {
+ if (keyblock->pkt->pkttype == PKT_SECRET_KEY ) {
+ log_error ("expected public key but found secret key "
+ "- must stop\n");
+ /* we better exit here becuase a public key is expected at
+ other places too. FIXME: Figure this out earlier and
+ don't get to here at all */
+ g10_exit (1);
+ }
+ BUG ();
+ }
+
+ merge_selfsigs_main ( keyblock, &revoked, &rinfo );
+
+ /* now merge in the data from each of the subkeys */
+ for(k=keyblock; k; k = k->next ) {
+ if ( k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+ merge_selfsigs_subkey ( keyblock, k );
+ }
+ }
+
+ main_pk = keyblock->pkt->pkt.public_key;
+ if ( revoked || main_pk->has_expired || !main_pk->is_valid ) {
+ /* if the primary key is revoked, expired, or invalid we
+ * better set the appropriate flags on that key and all
+ * subkeys */
+ for(k=keyblock; k; k = k->next ) {
+ if ( k->pkt->pkttype == PKT_PUBLIC_KEY
+ || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+ PKT_public_key *pk = k->pkt->pkt.public_key;
+ if(!main_pk->is_valid)
+ pk->is_valid = 0;
+ if(revoked && !pk->is_revoked)
+ {
+ pk->is_revoked = revoked;
+ memcpy(&pk->revoked,&rinfo,sizeof(rinfo));
+ }
+ if(main_pk->has_expired)
+ pk->has_expired = main_pk->has_expired;
+ }
+ }
+ return;
+ }
+
+ /* set the preference list of all keys to those of the primary real
+ * user ID. Note: we use these preferences when we don't know by
+ * which user ID the key has been selected.
+ * fixme: we should keep atoms of commonly used preferences or
+ * use reference counting to optimize the preference lists storage.
+ * FIXME: it might be better to use the intersection of
+ * all preferences.
+ * Do a similar thing for the MDC feature flag.
+ */
+ prefs = NULL;
+ mdc_feature = 0;
+ for (k=keyblock; k && k->pkt->pkttype != PKT_PUBLIC_SUBKEY; k = k->next) {
+ if (k->pkt->pkttype == PKT_USER_ID
+ && !k->pkt->pkt.user_id->attrib_data
+ && k->pkt->pkt.user_id->is_primary) {
+ prefs = k->pkt->pkt.user_id->prefs;
+ mdc_feature = k->pkt->pkt.user_id->flags.mdc;
+ break;
+ }
+ }
+ for(k=keyblock; k; k = k->next ) {
+ if ( k->pkt->pkttype == PKT_PUBLIC_KEY
+ || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+ PKT_public_key *pk = k->pkt->pkt.public_key;
+ if (pk->prefs)
+ xfree (pk->prefs);
+ pk->prefs = copy_prefs (prefs);
+ pk->mdc_feature = mdc_feature;
+ }
+ }
+}
+
+
+/*
+ * Merge the secret keys from secblock into the pubblock thereby
+ * replacing the public (sub)keys with their secret counterparts Hmmm:
+ * It might be better to get away from the concept of entire secret
+ * keys at all and have a way to store just the real secret parts
+ * from the key.
+ */
+static void
+merge_public_with_secret ( KBNODE pubblock, KBNODE secblock )
+{
+ KBNODE pub;
+
+ assert ( pubblock->pkt->pkttype == PKT_PUBLIC_KEY );
+ assert ( secblock->pkt->pkttype == PKT_SECRET_KEY );
+
+ for (pub=pubblock; pub; pub = pub->next ) {
+ if ( pub->pkt->pkttype == PKT_PUBLIC_KEY ) {
+ PKT_public_key *pk = pub->pkt->pkt.public_key;
+ PKT_secret_key *sk = secblock->pkt->pkt.secret_key;
+ assert ( pub == pubblock ); /* only in the first node */
+ /* there is nothing to compare in this case, so just replace
+ * some information */
+ copy_public_parts_to_secret_key ( pk, sk );
+ free_public_key ( pk );
+ pub->pkt->pkttype = PKT_SECRET_KEY;
+ pub->pkt->pkt.secret_key = copy_secret_key (NULL, sk);
+ }
+ else if ( pub->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+ KBNODE sec;
+ PKT_public_key *pk = pub->pkt->pkt.public_key;
+
+ /* this is more complicated: it may happen that the sequence
+ * of the subkeys dosn't match, so we have to find the
+ * appropriate secret key */
+ for (sec=secblock->next; sec; sec = sec->next ) {
+ if ( sec->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+ PKT_secret_key *sk = sec->pkt->pkt.secret_key;
+ if ( !cmp_public_secret_key ( pk, sk ) ) {
+ copy_public_parts_to_secret_key ( pk, sk );
+ free_public_key ( pk );
+ pub->pkt->pkttype = PKT_SECRET_SUBKEY;
+ pub->pkt->pkt.secret_key = copy_secret_key (NULL, sk);
+ break;
+ }
+ }
+ }
+ if ( !sec )
+ BUG(); /* already checked in premerge */
+ }
+ }
+}
+
+/* This function checks that for every public subkey a corresponding
+ * secret subkey is available and deletes the public subkey otherwise.
+ * We need this function because we can't delete it later when we
+ * actually merge the secret parts into the pubring.
+ * The function also plays some games with the node flags.
+ */
+static void
+premerge_public_with_secret ( KBNODE pubblock, KBNODE secblock )
+{
+ KBNODE last, pub;
+
+ assert ( pubblock->pkt->pkttype == PKT_PUBLIC_KEY );
+ assert ( secblock->pkt->pkttype == PKT_SECRET_KEY );
+
+ for (pub=pubblock,last=NULL; pub; last = pub, pub = pub->next ) {
+ pub->flag &= ~3; /* reset bits 0 and 1 */
+ if ( pub->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+ KBNODE sec;
+ PKT_public_key *pk = pub->pkt->pkt.public_key;
+
+ for (sec=secblock->next; sec; sec = sec->next ) {
+ if ( sec->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+ PKT_secret_key *sk = sec->pkt->pkt.secret_key;
+ if ( !cmp_public_secret_key ( pk, sk ) ) {
+ if ( sk->protect.s2k.mode == 1001 ) {
+ /* The secret parts are not available so
+ we can't use that key for signing etc.
+ Fix the pubkey usage */
+ pk->pubkey_usage &= ~(PUBKEY_USAGE_SIG
+ |PUBKEY_USAGE_AUTH);
+ }
+ /* transfer flag bits 0 and 1 to the pubblock */
+ pub->flag |= (sec->flag &3);
+ break;
+ }
+ }
+ }
+ if ( !sec ) {
+ KBNODE next, ll;
+
+ if (opt.verbose)
+ log_info (_("no secret subkey"
+ " for public subkey %s - ignoring\n"),
+ keystr_from_pk (pk));
+ /* we have to remove the subkey in this case */
+ assert ( last );
+ /* find the next subkey */
+ for (next=pub->next,ll=pub;
+ next && next->pkt->pkttype != PKT_PUBLIC_SUBKEY;
+ ll = next, next = next->next )
+ ;
+ /* make new link */
+ last->next = next;
+ /* release this public subkey with all sigs */
+ ll->next = NULL;
+ release_kbnode( pub );
+ /* let the loop continue */
+ pub = last;
+ }
+ }
+ }
+ /* We need to copy the found bits (0 and 1) from the secret key to
+ the public key. This has already been done for the subkeys but
+ got lost on the primary key - fix it here *. */
+ pubblock->flag |= (secblock->flag & 3);
+}
+
+
+
+
+/* See see whether the key fits
+ * our requirements and in case we do not
+ * request the primary key, we should select
+ * a suitable subkey.
+ * FIXME: Check against PGP 7 whether we still need a kludge
+ * to favor type 16 keys over type 20 keys when type 20
+ * has not been explitely requested.
+ * Returns: True when a suitable key has been found.
+ *
+ * We have to distinguish four cases: FIXME!
+ * 1. No usage and no primary key requested
+ * Examples for this case are that we have a keyID to be used
+ * for decrytion or verification.
+ * 2. No usage but primary key requested
+ * This is the case for all functions which work on an
+ * entire keyblock, e.g. for editing or listing
+ * 3. Usage and primary key requested
+ * FXME
+ * 4. Usage but no primary key requested
+ * FIXME
+ * FIXME: Tell what is going to happen here and something about the rationale
+ * Note: We don't use this function if no specific usage is requested;
+ * This way the getkey functions can be used for plain key listings.
+ *
+ * CTX ist the keyblock we are investigating, if FOUNDK is not NULL this
+ * is the key we actually found by looking at the keyid or a fingerprint and
+ * may eitehr point to the primary or one of the subkeys.
+ */
+
+static int
+finish_lookup (GETKEY_CTX ctx)
+{
+ KBNODE keyblock = ctx->keyblock;
+ KBNODE k;
+ KBNODE foundk = NULL;
+ PKT_user_id *foundu = NULL;
+#define USAGE_MASK (PUBKEY_USAGE_SIG|PUBKEY_USAGE_ENC|PUBKEY_USAGE_CERT)
+ unsigned int req_usage = ( ctx->req_usage & USAGE_MASK );
+ /* Request the primary if we're certifying another key, and also
+ if signing data while --pgp6 or --pgp7 is on since pgp 6 and 7
+ do not understand signatures made by a signing subkey. PGP 8
+ does. */
+ int req_prim = (ctx->req_usage & PUBKEY_USAGE_CERT) ||
+ ((PGP6 || PGP7) && (ctx->req_usage & PUBKEY_USAGE_SIG));
+ u32 latest_date;
+ KBNODE latest_key;
+ u32 curtime = make_timestamp ();
+
+ assert( keyblock->pkt->pkttype == PKT_PUBLIC_KEY );
+
+ ctx->found_key = NULL;
+
+ if (ctx->exact) {
+ for (k=keyblock; k; k = k->next) {
+ if ( (k->flag & 1) ) {
+ assert ( k->pkt->pkttype == PKT_PUBLIC_KEY
+ || k->pkt->pkttype == PKT_PUBLIC_SUBKEY );
+ foundk = k;
+ break;
+ }
+ }
+ }
+
+ for (k=keyblock; k; k = k->next) {
+ if ( (k->flag & 2) ) {
+ assert (k->pkt->pkttype == PKT_USER_ID);
+ foundu = k->pkt->pkt.user_id;
+ break;
+ }
+ }
+
+ if ( DBG_CACHE )
+ log_debug( "finish_lookup: checking key %08lX (%s)(req_usage=%x)\n",
+ (ulong)keyid_from_pk( keyblock->pkt->pkt.public_key, NULL),
+ foundk? "one":"all", req_usage);
+
+ if (!req_usage) {
+ latest_key = foundk? foundk:keyblock;
+ goto found;
+ }
+
+ if (!req_usage) {
+ PKT_public_key *pk = foundk->pkt->pkt.public_key;
+ if (pk->user_id)
+ free_user_id (pk->user_id);
+ pk->user_id = scopy_user_id (foundu);
+ ctx->found_key = foundk;
+ cache_user_id( keyblock );
+ return 1; /* found */
+ }
+
+ latest_date = 0;
+ latest_key = NULL;
+ /* do not look at subkeys if a certification key is requested */
+ if ((!foundk || foundk->pkt->pkttype == PKT_PUBLIC_SUBKEY) && !req_prim) {
+ KBNODE nextk;
+ /* either start a loop or check just this one subkey */
+ for (k=foundk?foundk:keyblock; k; k = nextk ) {
+ PKT_public_key *pk;
+ nextk = k->next;
+ if ( k->pkt->pkttype != PKT_PUBLIC_SUBKEY )
+ continue;
+ if ( foundk )
+ nextk = NULL; /* what a hack */
+ pk = k->pkt->pkt.public_key;
+ if (DBG_CACHE)
+ log_debug( "\tchecking subkey %08lX\n",
+ (ulong)keyid_from_pk( pk, NULL));
+ if ( !pk->is_valid ) {
+ if (DBG_CACHE)
+ log_debug( "\tsubkey not valid\n");
+ continue;
+ }
+ if ( pk->is_revoked ) {
+ if (DBG_CACHE)
+ log_debug( "\tsubkey has been revoked\n");
+ continue;
+ }
+ if ( pk->has_expired ) {
+ if (DBG_CACHE)
+ log_debug( "\tsubkey has expired\n");
+ continue;
+ }
+ if ( pk->timestamp > curtime && !opt.ignore_valid_from ) {
+ if (DBG_CACHE)
+ log_debug( "\tsubkey not yet valid\n");
+ continue;
+ }
+
+ if ( !((pk->pubkey_usage&USAGE_MASK) & req_usage) ) {
+ if (DBG_CACHE)
+ log_debug( "\tusage does not match: want=%x have=%x\n",
+ req_usage, pk->pubkey_usage );
+ continue;
+ }
+
+ if (DBG_CACHE)
+ log_debug( "\tsubkey looks fine\n");
+ if ( pk->timestamp > latest_date ) {
+ latest_date = pk->timestamp;
+ latest_key = k;
+ }
+ }
+ }
+
+ /* Okay now try the primary key unless we want an exact
+ * key ID match on a subkey */
+ if ((!latest_key && !(ctx->exact && foundk != keyblock)) || req_prim) {
+ PKT_public_key *pk;
+ if (DBG_CACHE && !foundk && !req_prim )
+ log_debug( "\tno suitable subkeys found - trying primary\n");
+ pk = keyblock->pkt->pkt.public_key;
+ if ( !pk->is_valid ) {
+ if (DBG_CACHE)
+ log_debug( "\tprimary key not valid\n");
+ }
+ else if ( pk->is_revoked ) {
+ if (DBG_CACHE)
+ log_debug( "\tprimary key has been revoked\n");
+ }
+ else if ( pk->has_expired ) {
+ if (DBG_CACHE)
+ log_debug( "\tprimary key has expired\n");
+ }
+ else if ( !((pk->pubkey_usage&USAGE_MASK) & req_usage) ) {
+ if (DBG_CACHE)
+ log_debug( "\tprimary key usage does not match: "
+ "want=%x have=%x\n",
+ req_usage, pk->pubkey_usage );
+ }
+ else { /* okay */
+ if (DBG_CACHE)
+ log_debug( "\tprimary key may be used\n");
+ latest_key = keyblock;
+ latest_date = pk->timestamp;
+ }
+ }
+
+ if ( !latest_key ) {
+ if (DBG_CACHE)
+ log_debug("\tno suitable key found - giving up\n");
+ return 0;
+ }
+
+ found:
+ if (DBG_CACHE)
+ log_debug( "\tusing key %08lX\n",
+ (ulong)keyid_from_pk( latest_key->pkt->pkt.public_key, NULL) );
+
+ if (latest_key) {
+ PKT_public_key *pk = latest_key->pkt->pkt.public_key;
+ if (pk->user_id)
+ free_user_id (pk->user_id);
+ pk->user_id = scopy_user_id (foundu);
+ }
+
+ ctx->found_key = latest_key;
+
+ if (latest_key != keyblock && opt.verbose)
+ {
+ char *tempkeystr=
+ xstrdup(keystr_from_pk(latest_key->pkt->pkt.public_key));
+ log_info(_("using subkey %s instead of primary key %s\n"),
+ tempkeystr, keystr_from_pk(keyblock->pkt->pkt.public_key));
+ xfree(tempkeystr);
+ }
+
+ cache_user_id( keyblock );
+
+ return 1; /* found */
+}
+
+
+static int
+lookup( GETKEY_CTX ctx, KBNODE *ret_keyblock, int secmode )
+{
+ int rc;
+ KBNODE secblock = NULL; /* helper */
+ int no_suitable_key = 0;
+
+ rc = 0;
+ while (!(rc = keydb_search (ctx->kr_handle, ctx->items, ctx->nitems))) {
+ /* If we are searching for the first key we have to make sure
+ that the next interation does not no an implicit reset.
+ This can be triggered by an empty key ring. */
+ if (ctx->nitems && ctx->items->mode == KEYDB_SEARCH_MODE_FIRST)
+ ctx->items->mode = KEYDB_SEARCH_MODE_NEXT;
+
+ rc = keydb_get_keyblock (ctx->kr_handle, &ctx->keyblock);
+ if (rc) {
+ log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc));
+ rc = 0;
+ goto skip;
+ }
+
+ if ( secmode ) {
+ /* find the correspondig public key and use this
+ * this one for the selection process */
+ u32 aki[2];
+ KBNODE k = ctx->keyblock;
+
+ if (k->pkt->pkttype != PKT_SECRET_KEY)
+ BUG();
+
+ keyid_from_sk (k->pkt->pkt.secret_key, aki);
+ k = get_pubkeyblock (aki);
+ if( !k )
+ {
+ if (!opt.quiet)
+ log_info(_("key %s: secret key without public key"
+ " - skipped\n"), keystr(aki));
+ goto skip;
+ }
+ secblock = ctx->keyblock;
+ ctx->keyblock = k;
+
+ premerge_public_with_secret ( ctx->keyblock, secblock );
+ }
+
+ /* warning: node flag bits 0 and 1 should be preserved by
+ * merge_selfsigs. For secret keys, premerge did tranfer the
+ * keys to the keyblock */
+ merge_selfsigs ( ctx->keyblock );
+ if ( finish_lookup (ctx) ) {
+ no_suitable_key = 0;
+ if ( secmode ) {
+ merge_public_with_secret ( ctx->keyblock,
+ secblock);
+ release_kbnode (secblock);
+ secblock = NULL;
+ }
+ goto found;
+ }
+ else
+ no_suitable_key = 1;
+
+ skip:
+ /* release resources and continue search */
+ if ( secmode ) {
+ release_kbnode( secblock );
+ secblock = NULL;
+ }
+ release_kbnode( ctx->keyblock );
+ ctx->keyblock = NULL;
+ }
+
+ found:
+ if( rc && rc != -1 )
+ log_error("keydb_search failed: %s\n", g10_errstr(rc));
+
+ if( !rc ) {
+ *ret_keyblock = ctx->keyblock; /* return the keyblock */
+ ctx->keyblock = NULL;
+ }
+ else if (rc == -1 && no_suitable_key)
+ rc = secmode ? G10ERR_UNU_SECKEY : G10ERR_UNU_PUBKEY;
+ else if( rc == -1 )
+ rc = secmode ? G10ERR_NO_SECKEY : G10ERR_NO_PUBKEY;
+
+ if ( secmode ) {
+ release_kbnode( secblock );
+ secblock = NULL;
+ }
+ release_kbnode( ctx->keyblock );
+ ctx->keyblock = NULL;
+
+ ctx->last_rc = rc;
+ return rc;
+}
+
+
+
+
+/****************
+ * FIXME: Replace by the generic function
+ * It does not work as it is right now - it is used at
+ * 2 places: a) to get the key for an anonyous recipient
+ * b) to get the ultimately trusted keys.
+ * The a) usage might have some problems.
+ *
+ * set with_subkeys true to include subkeys
+ * set with_spm true to include secret-parts-missing keys
+ *
+ * Enumerate all primary secret keys. Caller must use these procedure:
+ * 1) create a void pointer and initialize it to NULL
+ * 2) pass this void pointer by reference to this function
+ * and provide space for the secret key (pass a buffer for sk)
+ * 3) call this function as long as it does not return -1
+ * to indicate EOF.
+ * 4) Always call this function a last time with SK set to NULL,
+ * so that can free it's context.
+ */
+int
+enum_secret_keys( void **context, PKT_secret_key *sk,
+ int with_subkeys, int with_spm )
+{
+ int rc=0;
+ struct {
+ int eof;
+ int first;
+ KEYDB_HANDLE hd;
+ KBNODE keyblock;
+ KBNODE node;
+ } *c = *context;
+
+
+ if( !c ) { /* make a new context */
+ c = xmalloc_clear( sizeof *c );
+ *context = c;
+ c->hd = keydb_new (1);
+ c->first = 1;
+ c->keyblock = NULL;
+ c->node = NULL;
+ }
+
+ if( !sk ) { /* free the context */
+ keydb_release (c->hd);
+ release_kbnode (c->keyblock);
+ xfree( c );
+ *context = NULL;
+ return 0;
+ }
+
+ if( c->eof )
+ return -1;
+
+ do {
+ /* get the next secret key from the current keyblock */
+ for (; c->node; c->node = c->node->next) {
+ if ((c->node->pkt->pkttype == PKT_SECRET_KEY
+ || (with_subkeys
+ && c->node->pkt->pkttype == PKT_SECRET_SUBKEY) )
+ && !(c->node->pkt->pkt.secret_key->protect.s2k.mode==1001
+ && !with_spm)) {
+ copy_secret_key (sk, c->node->pkt->pkt.secret_key );
+ c->node = c->node->next;
+ return 0; /* found */
+ }
+ }
+ release_kbnode (c->keyblock);
+ c->keyblock = c->node = NULL;
+
+ rc = c->first? keydb_search_first (c->hd) : keydb_search_next (c->hd);
+ c->first = 0;
+ if (rc) {
+ keydb_release (c->hd); c->hd = NULL;
+ c->eof = 1;
+ return -1; /* eof */
+ }
+
+ rc = keydb_get_keyblock (c->hd, &c->keyblock);
+ c->node = c->keyblock;
+ } while (!rc);
+
+ return rc; /* error */
+}
+
+
+
+/*********************************************
+ *********** user ID printing helpers *******
+ *********************************************/
+
+/****************
+ * Return a string with a printable representation of the user_id.
+ * this string must be freed by xfree.
+ */
+char*
+get_user_id_string( u32 *keyid )
+{
+ user_id_db_t r;
+ char *p;
+ int pass=0;
+ /* try it two times; second pass reads from key resources */
+ do
+ {
+ for(r=user_id_db; r; r = r->next )
+ {
+ keyid_list_t a;
+ for (a=r->keyids; a; a= a->next )
+ {
+ if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] )
+ {
+ p = xmalloc( keystrlen() + 1 + r->len + 1 );
+ sprintf(p, "%s %.*s", keystr(keyid), r->len, r->name );
+ return p;
+ }
+ }
+ }
+ } while( ++pass < 2 && !get_pubkey( NULL, keyid ) );
+ p = xmalloc( keystrlen() + 5 );
+ sprintf(p, "%s [?]", keystr(keyid));
+ return p;
+}
+
+
+char*
+get_user_id_string_native ( u32 *keyid )
+{
+ char *p = get_user_id_string( keyid );
+ char *p2 = utf8_to_native( p, strlen(p), 0 );
+ xfree(p);
+ return p2;
+}
+
+
+char*
+get_long_user_id_string( u32 *keyid )
+{
+ user_id_db_t r;
+ char *p;
+ int pass=0;
+ /* try it two times; second pass reads from key resources */
+ do {
+ for(r=user_id_db; r; r = r->next ) {
+ keyid_list_t a;
+ for (a=r->keyids; a; a= a->next ) {
+ if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) {
+ p = xmalloc( r->len + 20 );
+ sprintf(p, "%08lX%08lX %.*s",
+ (ulong)keyid[0], (ulong)keyid[1],
+ r->len, r->name );
+ return p;
+ }
+ }
+ }
+ } while( ++pass < 2 && !get_pubkey( NULL, keyid ) );
+ p = xmalloc( 25 );
+ sprintf(p, "%08lX%08lX [?]", (ulong)keyid[0], (ulong)keyid[1] );
+ return p;
+}
+
+char*
+get_user_id( u32 *keyid, size_t *rn )
+{
+ user_id_db_t r;
+ char *p;
+ int pass=0;
+
+ /* try it two times; second pass reads from key resources */
+ do {
+ for(r=user_id_db; r; r = r->next ) {
+ keyid_list_t a;
+ for (a=r->keyids; a; a= a->next ) {
+ if( a->keyid[0] == keyid[0] && a->keyid[1] == keyid[1] ) {
+ p = xmalloc( r->len );
+ memcpy(p, r->name, r->len );
+ *rn = r->len;
+ return p;
+ }
+ }
+ }
+ } while( ++pass < 2 && !get_pubkey( NULL, keyid ) );
+ p = xstrdup( user_id_not_found_utf8 () );
+ *rn = strlen(p);
+ return p;
+}
+
+char*
+get_user_id_native( u32 *keyid )
+{
+ size_t rn;
+ char *p = get_user_id( keyid, &rn );
+ char *p2 = utf8_to_native( p, rn, 0 );
+ xfree(p);
+ return p2;
+}
+
+KEYDB_HANDLE
+get_ctx_handle(GETKEY_CTX ctx)
+{
+ return ctx->kr_handle;
+}
+
+static void
+free_akl(struct akl *akl)
+{
+ if(akl->spec)
+ free_keyserver_spec(akl->spec);
+
+ xfree(akl);
+}
+
+void
+release_akl(void)
+{
+ while(opt.auto_key_locate)
+ {
+ struct akl *akl2=opt.auto_key_locate;
+ opt.auto_key_locate=opt.auto_key_locate->next;
+ free_akl(akl2);
+ }
+}
+
+int
+parse_auto_key_locate(char *options)
+{
+ char *tok;
+
+ while((tok=optsep(&options)))
+ {
+ struct akl *akl,*check,*last=NULL;
+ int dupe=0;
+
+ if(tok[0]=='\0')
+ continue;
+
+ akl=xmalloc_clear(sizeof(*akl));
+
+ if(ascii_strcasecmp(tok,"ldap")==0)
+ akl->type=AKL_LDAP;
+ else if(ascii_strcasecmp(tok,"keyserver")==0)
+ akl->type=AKL_KEYSERVER;
+#ifdef USE_DNS_CERT
+ else if(ascii_strcasecmp(tok,"cert")==0)
+ akl->type=AKL_CERT;
+#endif
+#ifdef USE_DNS_PKA
+ else if(ascii_strcasecmp(tok,"pka")==0)
+ akl->type=AKL_PKA;
+#endif
+ else if((akl->spec=parse_keyserver_uri(tok,1,NULL,0)))
+ akl->type=AKL_SPEC;
+ else
+ {
+ free_akl(akl);
+ return 0;
+ }
+
+ /* We must maintain the order the user gave us */
+ for(check=opt.auto_key_locate;check;last=check,check=check->next)
+ {
+ /* Check for duplicates */
+ if(check->type==akl->type
+ && (akl->type!=AKL_SPEC
+ || (akl->type==AKL_SPEC
+ && strcmp(check->spec->uri,akl->spec->uri)==0)))
+ {
+ dupe=1;
+ free_akl(akl);
+ break;
+ }
+ }
+
+ if(!dupe)
+ {
+ if(last)
+ last->next=akl;
+ else
+ opt.auto_key_locate=akl;
+ }
+ }
+
+ return 1;
+}
diff --git a/g10/global.h b/g10/global.h
new file mode 100644
index 0000000..1b2f872
--- /dev/null
+++ b/g10/global.h
@@ -0,0 +1,30 @@
+/* global.h - Local typedefs and constants
+ * Copyright (C) 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef GPG_GLOBAL_H
+#define GPG_GLOBAL_H
+
+#define MAX_FINGERPRINT_LEN 20
+
+typedef struct kbnode_struct *KBNODE;
+typedef struct keydb_search_desc KEYDB_SEARCH_DESC;
+
+#endif /*GPG_GLOBAL_H*/
diff --git a/g10/gpg.c b/g10/gpg.c
new file mode 100644
index 0000000..691cfff
--- /dev/null
+++ b/g10/gpg.c
@@ -0,0 +1,4207 @@
+/* gpg.c - The GnuPG utility (main for gpg)
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#include <assert.h>
+#ifdef HAVE_DOSISH_SYSTEM
+#include <fcntl.h> /* for setmode() */
+#endif
+#ifdef HAVE_STAT
+#include <sys/stat.h> /* for stat() */
+#endif
+#include <fcntl.h>
+#ifdef HAVE_W32_SYSTEM
+#include <windows.h>
+#endif
+
+#define INCLUDED_BY_MAIN_MODULE 1
+#include "packet.h"
+#include "iobuf.h"
+#include "memory.h"
+#include "util.h"
+#include "main.h"
+#include "options.h"
+#include "keydb.h"
+#include "trustdb.h"
+#include "mpi.h"
+#include "cipher.h"
+#include "filter.h"
+#include "ttyio.h"
+#include "i18n.h"
+#include "status.h"
+#include "g10defs.h"
+#include "keyserver-internal.h"
+#include "exec.h"
+#include "cardglue.h"
+#ifdef ENABLE_CARD_SUPPORT
+#include "ccid-driver.h"
+#endif
+
+#if defined(HAVE_DOSISH_SYSTEM) || defined(__CYGWIN__)
+#define MY_O_BINARY O_BINARY
+#ifndef S_IRGRP
+# define S_IRGRP 0
+# define S_IWGRP 0
+#endif
+#else
+#define MY_O_BINARY 0
+#endif
+
+
+enum cmd_and_opt_values
+ {
+ aNull = 0,
+ oArmor = 'a',
+ aDetachedSign = 'b',
+ aSym = 'c',
+ aDecrypt = 'd',
+ aEncr = 'e',
+ oInteractive = 'i',
+ oKOption = 'k',
+ oDryRun = 'n',
+ oOutput = 'o',
+ oQuiet = 'q',
+ oRecipient = 'r',
+ oHiddenRecipient = 'R',
+ aSign = 's',
+ oTextmodeShort= 't',
+ oLocalUser = 'u',
+ oVerbose = 'v',
+ oCompress = 'z',
+ oSetNotation = 'N',
+ aListSecretKeys = 'K',
+ oBatch = 500,
+ oMaxOutput,
+ oSigNotation,
+ oCertNotation,
+ oShowNotation,
+ oNoShowNotation,
+ aEncrFiles,
+ aEncrSym,
+ aDecryptFiles,
+ aClearsign,
+ aStore,
+ aKeygen,
+ aSignEncr,
+ aSignEncrSym,
+ aSignSym,
+ aSignKey,
+ aLSignKey,
+ aListConfig,
+ aGPGConfList,
+ aGPGConfTest,
+ aListPackets,
+ aEditKey,
+ aDeleteKeys,
+ aDeleteSecretKeys,
+ aDeleteSecretAndPublicKeys,
+ aKMode,
+ aKModeC,
+ aImport,
+ aFastImport,
+ aVerify,
+ aVerifyFiles,
+ aListKeys,
+ aListSigs,
+ aSendKeys,
+ aRecvKeys,
+ aSearchKeys,
+ aRefreshKeys,
+ aFetchKeys,
+ aExport,
+ aExportSecret,
+ aExportSecretSub,
+ aCheckKeys,
+ aGenRevoke,
+ aDesigRevoke,
+ aPrimegen,
+ aPrintMD,
+ aPrintMDs,
+ aCheckTrustDB,
+ aUpdateTrustDB,
+ aFixTrustDB,
+ aListTrustDB,
+ aListTrustPath,
+ aExportOwnerTrust,
+ aListOwnerTrust,
+ aImportOwnerTrust,
+ aDeArmor,
+ aEnArmor,
+ aGenRandom,
+ aPipeMode,
+ aRebuildKeydbCaches,
+ aCardStatus,
+ aCardEdit,
+ aChangePIN,
+
+ oTextmode,
+ oNoTextmode,
+ oExpert,
+ oNoExpert,
+ oDefSigExpire,
+ oAskSigExpire,
+ oNoAskSigExpire,
+ oDefCertExpire,
+ oAskCertExpire,
+ oNoAskCertExpire,
+ oDefCertLevel,
+ oMinCertLevel,
+ oAskCertLevel,
+ oNoAskCertLevel,
+ oFingerprint,
+ oWithFingerprint,
+ oAnswerYes,
+ oAnswerNo,
+ oKeyring,
+ oPrimaryKeyring,
+ oSecretKeyring,
+ oShowKeyring,
+ oDefaultKey,
+ oDefRecipient,
+ oDefRecipientSelf,
+ oNoDefRecipient,
+ oOptions,
+ oDebug,
+ oDebugAll,
+ oDebugCCIDDriver,
+ oStatusFD,
+ oStatusFile,
+ oAttributeFD,
+ oAttributeFile,
+ oEmitVersion,
+ oNoEmitVersion,
+ oCompletesNeeded,
+ oMarginalsNeeded,
+ oMaxCertDepth,
+ oLoadExtension,
+ oGnuPG,
+ oRFC1991,
+ oRFC2440,
+ oOpenPGP,
+ oPGP2,
+ oPGP6,
+ oPGP7,
+ oPGP8,
+ oRFC2440Text,
+ oNoRFC2440Text,
+ oCipherAlgo,
+ oDigestAlgo,
+ oCertDigestAlgo,
+ oCompressAlgo,
+ oCompressLevel,
+ oBZ2CompressLevel,
+ oBZ2DecompressLowmem,
+ oPasswd,
+ oPasswdFD,
+ oPasswdFile,
+ oPasswdRepeat,
+ oCommandFD,
+ oCommandFile,
+ oQuickRandom,
+ oNoVerbose,
+ oTrustDBName,
+ oNoSecmemWarn,
+ oRequireSecmem,
+ oNoRequireSecmem,
+ oNoPermissionWarn,
+ oNoMDCWarn,
+ oNoArmor,
+ oNoDefKeyring,
+ oNoGreeting,
+ oNoTTY,
+ oNoOptions,
+ oNoBatch,
+ oHomedir,
+ oWithColons,
+ oWithKeyData,
+ oSkipVerify,
+ oCompressKeys,
+ oCompressSigs,
+ oAlwaysTrust,
+ oTrustModel,
+ oForceOwnertrust,
+ oRunAsShmCP,
+ oSetFilename,
+ oForYourEyesOnly,
+ oNoForYourEyesOnly,
+ oSetPolicyURL,
+ oSigPolicyURL,
+ oCertPolicyURL,
+ oShowPolicyURL,
+ oNoShowPolicyURL,
+ oSigKeyserverURL,
+ oUseEmbeddedFilename,
+ oNoUseEmbeddedFilename,
+ oComment,
+ oDefaultComment,
+ oNoComments,
+ oThrowKeyids,
+ oNoThrowKeyids,
+ oShowPhotos,
+ oNoShowPhotos,
+ oPhotoViewer,
+ oForceV3Sigs,
+ oNoForceV3Sigs,
+ oForceV4Certs,
+ oNoForceV4Certs,
+ oForceMDC,
+ oNoForceMDC,
+ oDisableMDC,
+ oNoDisableMDC,
+ oS2KMode,
+ oS2KDigest,
+ oS2KCipher,
+ oS2KCount,
+ oSimpleSKChecksum,
+ oDisplayCharset,
+ oNotDashEscaped,
+ oEscapeFrom,
+ oNoEscapeFrom,
+ oLockOnce,
+ oLockMultiple,
+ oLockNever,
+ oKeyServer,
+ oKeyServerOptions,
+ oImportOptions,
+ oExportOptions,
+ oListOptions,
+ oVerifyOptions,
+ oTempDir,
+ oExecPath,
+ oEncryptTo,
+ oHiddenEncryptTo,
+ oNoEncryptTo,
+ oLoggerFD,
+ oLoggerFile,
+ oUtf8Strings,
+ oNoUtf8Strings,
+ oDisableCipherAlgo,
+ oDisablePubkeyAlgo,
+ oAllowNonSelfsignedUID,
+ oNoAllowNonSelfsignedUID,
+ oAllowFreeformUID,
+ oNoAllowFreeformUID,
+ oAllowSecretKeyImport,
+ oEnableSpecialFilenames,
+ oNoLiteral,
+ oSetFilesize,
+ oHonorHttpProxy,
+ oFastListMode,
+ oListOnly,
+ oIgnoreTimeConflict,
+ oIgnoreValidFrom,
+ oIgnoreCrcError,
+ oIgnoreMDCError,
+ oShowSessionKey,
+ oOverrideSessionKey,
+ oNoRandomSeedFile,
+ oAutoKeyRetrieve,
+ oNoAutoKeyRetrieve,
+ oUseAgent,
+ oNoUseAgent,
+ oGpgAgentInfo,
+ oMergeOnly,
+ oTryAllSecrets,
+ oTrustedKey,
+ oNoExpensiveTrustChecks,
+ oFixedListMode,
+ oNoSigCache,
+ oNoSigCreateCheck,
+ oAutoCheckTrustDB,
+ oNoAutoCheckTrustDB,
+ oPreservePermissions,
+ oDefaultPreferenceList,
+ oDefaultKeyserverURL,
+ oPersonalCipherPreferences,
+ oPersonalDigestPreferences,
+ oPersonalCompressPreferences,
+ oDisplay,
+ oTTYname,
+ oTTYtype,
+ oLCctype,
+ oLCmessages,
+ oGroup,
+ oUnGroup,
+ oNoGroups,
+ oStrict,
+ oNoStrict,
+ oMangleDosFilenames,
+ oNoMangleDosFilenames,
+ oEnableProgressFilter,
+ oMultifile,
+ oKeyidFormat,
+ oExitOnStatusWriteError,
+ oLimitCardInsertTries,
+ oReaderPort,
+ octapiDriver,
+ opcscDriver,
+ oDisableCCID,
+ oRequireCrossCert,
+ oNoRequireCrossCert,
+ oAutoKeyLocate,
+ oNoAutoKeyLocate,
+ oAllowMultisigVerification,
+ oEnableDSA2,
+ oDisableDSA2,
+
+ oNoop
+ };
+
+
+static ARGPARSE_OPTS opts[] = {
+
+ { 300, NULL, 0, N_("@Commands:\n ") },
+
+ { aSign, "sign", 256, N_("|[file]|make a signature")},
+ { aClearsign, "clearsign", 256, N_("|[file]|make a clear text signature")},
+ { aDetachedSign, "detach-sign", 256, N_("make a detached signature")},
+ { aEncr, "encrypt", 256, N_("encrypt data")},
+ { aEncrFiles, "encrypt-files", 256, "@"},
+ { aSym, "symmetric", 256, N_("encryption only with symmetric cipher")},
+ { aStore, "store", 256, "@"},
+ { aDecrypt, "decrypt", 256, N_("decrypt data (default)")},
+ { aDecryptFiles, "decrypt-files", 256, "@"},
+ { aVerify, "verify" , 256, N_("verify a signature")},
+ { aVerifyFiles, "verify-files" , 256, "@" },
+ { aListKeys, "list-keys", 256, N_("list keys")},
+ { aListKeys, "list-public-keys", 256, "@" },
+ { aListSigs, "list-sigs", 256, N_("list keys and signatures")},
+ { aCheckKeys, "check-sigs",256, N_("list and check key signatures")},
+ { oFingerprint, "fingerprint", 256, N_("list keys and fingerprints")},
+ { aListSecretKeys, "list-secret-keys", 256, N_("list secret keys")},
+ { aKeygen, "gen-key", 256, N_("generate a new key pair")},
+ { aDeleteKeys,"delete-keys",256,N_("remove keys from the public keyring")},
+ { aDeleteSecretKeys, "delete-secret-keys",256,
+ N_("remove keys from the secret keyring")},
+ { aSignKey, "sign-key" ,256, N_("sign a key")},
+ { aLSignKey, "lsign-key" ,256, N_("sign a key locally")},
+ { aEditKey, "edit-key" ,256, N_("sign or edit a key")},
+ { aGenRevoke, "gen-revoke",256, N_("generate a revocation certificate")},
+ { aDesigRevoke, "desig-revoke",256, "@" },
+ { aExport, "export" , 256, N_("export keys") },
+ { aSendKeys, "send-keys" , 256, N_("export keys to a key server") },
+ { aRecvKeys, "recv-keys" , 256, N_("import keys from a key server") },
+ { aSearchKeys, "search-keys" , 256,
+ N_("search for keys on a key server") },
+ { aRefreshKeys, "refresh-keys", 256,
+ N_("update all keys from a keyserver")},
+ { aFetchKeys, "fetch-keys" , 256, "@" },
+ { aExportSecret, "export-secret-keys" , 256, "@" },
+ { aExportSecretSub, "export-secret-subkeys" , 256, "@" },
+ { aImport, "import", 256 , N_("import/merge keys")},
+ { aFastImport, "fast-import", 256 , "@"},
+#ifdef ENABLE_CARD_SUPPORT
+ { aCardStatus, "card-status", 256, N_("print the card status")},
+ { aCardEdit, "card-edit", 256, N_("change data on a card")},
+ { aChangePIN, "change-pin", 256, N_("change a card's PIN")},
+#endif
+ { aListConfig, "list-config", 256, "@"},
+ { aGPGConfList, "gpgconf-list", 256, "@" },
+ { aGPGConfTest, "gpgconf-test", 256, "@" },
+ { aListPackets, "list-packets",256, "@"},
+ { aExportOwnerTrust, "export-ownertrust", 256, "@"},
+ { aImportOwnerTrust, "import-ownertrust", 256, "@"},
+ { aUpdateTrustDB,
+ "update-trustdb",0 , N_("update the trust database")},
+ { aCheckTrustDB, "check-trustdb", 0, "@"},
+ { aFixTrustDB, "fix-trustdb", 0, "@"},
+ { aDeArmor, "dearmor", 256, "@"},
+ { aDeArmor, "dearmour", 256, "@"},
+ { aEnArmor, "enarmor", 256, "@"},
+ { aEnArmor, "enarmour", 256, "@"},
+ { aPrintMD, "print-md" , 256, N_("|algo [files]|print message digests")},
+ { aPrimegen, "gen-prime" , 256, "@" },
+ { aGenRandom, "gen-random" , 256, "@" },
+
+ { 301, NULL, 0, N_("@\nOptions:\n ") },
+
+ { oArmor, "armor", 0, N_("create ascii armored output")},
+ { oArmor, "armour", 0, "@" },
+ { oRecipient, "recipient", 2, N_("|NAME|encrypt for NAME")},
+ { oHiddenRecipient, "hidden-recipient", 2, "@" },
+ { oRecipient, "remote-user", 2, "@"}, /* old option name */
+ { oDefRecipient, "default-recipient", 2, "@"},
+ { oDefRecipientSelf, "default-recipient-self", 0, "@"},
+ { oNoDefRecipient, "no-default-recipient", 0, "@" },
+ { oTempDir, "temp-directory", 2, "@" },
+ { oExecPath, "exec-path", 2, "@" },
+ { oEncryptTo, "encrypt-to", 2, "@" },
+ { oHiddenEncryptTo, "hidden-encrypt-to", 2, "@" },
+ { oNoEncryptTo, "no-encrypt-to", 0, "@" },
+ { oLocalUser, "local-user",2, N_("use this user-id to sign or decrypt")},
+ { oCompress, NULL, 1, N_("|N|set compress level N (0 disables)") },
+ { oCompressLevel, "compress-level", 1, "@" },
+ { oBZ2CompressLevel, "bzip2-compress-level", 1, "@" },
+ { oBZ2DecompressLowmem, "bzip2-decompress-lowmem", 0, "@" },
+ { oTextmodeShort, NULL, 0, "@"},
+ { oTextmode, "textmode", 0, N_("use canonical text mode")},
+ { oNoTextmode, "no-textmode", 0, "@"},
+ { oExpert, "expert", 0, "@"},
+ { oNoExpert, "no-expert", 0, "@"},
+ { oDefSigExpire, "default-sig-expire", 2, "@"},
+ { oAskSigExpire, "ask-sig-expire", 0, "@"},
+ { oNoAskSigExpire, "no-ask-sig-expire", 0, "@"},
+ { oDefCertExpire, "default-cert-expire", 2, "@"},
+ { oAskCertExpire, "ask-cert-expire", 0, "@"},
+ { oNoAskCertExpire, "no-ask-cert-expire", 0, "@"},
+ { oDefCertLevel, "default-cert-level", 1, "@"},
+ { oMinCertLevel, "min-cert-level", 1, "@"},
+ { oAskCertLevel, "ask-cert-level", 0, "@"},
+ { oNoAskCertLevel, "no-ask-cert-level", 0, "@"},
+ { oOutput, "output", 2, N_("use as output file")},
+ { oMaxOutput, "max-output", 16|4, "@" },
+ { oVerbose, "verbose", 0, N_("verbose") },
+ { oQuiet, "quiet", 0, "@"},
+ { oNoTTY, "no-tty", 0, "@"},
+ { oForceV3Sigs, "force-v3-sigs", 0, "@"},
+ { oNoForceV3Sigs, "no-force-v3-sigs", 0, "@"},
+ { oForceV4Certs, "force-v4-certs", 0, "@"},
+ { oNoForceV4Certs, "no-force-v4-certs", 0, "@"},
+ { oForceMDC, "force-mdc", 0, "@"},
+ { oNoForceMDC, "no-force-mdc", 0, "@" },
+ { oDisableMDC, "disable-mdc", 0, "@"},
+ { oNoDisableMDC, "no-disable-mdc", 0, "@" },
+ { oDryRun, "dry-run", 0, N_("do not make any changes") },
+ { oInteractive, "interactive", 0, N_("prompt before overwriting") },
+ { oUseAgent, "use-agent",0, "@"},
+ { oNoUseAgent, "no-use-agent",0, "@"},
+ { oGpgAgentInfo, "gpg-agent-info",2, "@"},
+ { oBatch, "batch", 0, "@"},
+ { oAnswerYes, "yes", 0, "@"},
+ { oAnswerNo, "no", 0, "@"},
+ { oKeyring, "keyring", 2, "@"},
+ { oPrimaryKeyring, "primary-keyring",2, "@" },
+ { oSecretKeyring, "secret-keyring", 2, "@"},
+ { oShowKeyring, "show-keyring", 0, "@"},
+ { oDefaultKey, "default-key", 2, "@"},
+ { oKeyServer, "keyserver", 2, "@"},
+ { oKeyServerOptions, "keyserver-options",2,"@"},
+ { oImportOptions, "import-options",2,"@"},
+ { oExportOptions, "export-options",2,"@"},
+ { oListOptions, "list-options",2,"@"},
+ { oVerifyOptions, "verify-options",2,"@"},
+ { oDisplayCharset, "display-charset", 2, "@"},
+ { oDisplayCharset, "charset", 2, "@"},
+ { oOptions, "options", 2, "@"},
+ { oDebug, "debug" ,4|16, "@"},
+ { oDebugAll, "debug-all" ,0, "@"},
+ { oStatusFD, "status-fd" ,1, "@"},
+ { oStatusFile, "status-file" ,2, "@"},
+ { oAttributeFD, "attribute-fd" ,1, "@" },
+ { oAttributeFile, "attribute-file" ,2, "@" },
+ { oNoop, "sk-comments", 0, "@"},
+ { oNoop, "no-sk-comments", 0, "@"},
+ { oCompletesNeeded, "completes-needed", 1, "@"},
+ { oMarginalsNeeded, "marginals-needed", 1, "@"},
+ { oMaxCertDepth, "max-cert-depth", 1, "@" },
+ { oTrustedKey, "trusted-key", 2, "@"},
+ { oLoadExtension, "load-extension", 2, "@"},
+ { oGnuPG, "gnupg", 0, "@"},
+ { oGnuPG, "no-pgp2", 0, "@"},
+ { oGnuPG, "no-pgp6", 0, "@"},
+ { oGnuPG, "no-pgp7", 0, "@"},
+ { oGnuPG, "no-pgp8", 0, "@"},
+ { oRFC1991, "rfc1991", 0, "@"},
+ { oRFC2440, "rfc2440", 0, "@" },
+ { oOpenPGP, "openpgp", 0, N_("use strict OpenPGP behavior")},
+ { oPGP2, "pgp2", 0, N_("generate PGP 2.x compatible messages")},
+ { oPGP6, "pgp6", 0, "@"},
+ { oPGP7, "pgp7", 0, "@"},
+ { oPGP8, "pgp8", 0, "@"},
+ { oRFC2440Text, "rfc2440-text", 0, "@"},
+ { oNoRFC2440Text, "no-rfc2440-text", 0, "@"},
+ { oS2KMode, "s2k-mode", 1, "@"},
+ { oS2KDigest, "s2k-digest-algo", 2, "@"},
+ { oS2KCipher, "s2k-cipher-algo", 2, "@"},
+ { oS2KCount, "s2k-count", 1, "@"},
+ { oSimpleSKChecksum, "simple-sk-checksum", 0, "@"},
+ { oCipherAlgo, "cipher-algo", 2, "@"},
+ { oDigestAlgo, "digest-algo", 2, "@"},
+ { oCertDigestAlgo, "cert-digest-algo", 2 , "@" },
+ { oCompressAlgo,"compress-algo", 2, "@"},
+ { oCompressAlgo, "compression-algo", 2, "@"}, /* Alias */
+ { oThrowKeyids, "throw-keyid", 0, "@"},
+ { oThrowKeyids, "throw-keyids", 0, "@"},
+ { oNoThrowKeyids, "no-throw-keyid", 0, "@" },
+ { oNoThrowKeyids, "no-throw-keyids", 0, "@" },
+ { oShowPhotos, "show-photos", 0, "@" },
+ { oNoShowPhotos, "no-show-photos", 0, "@" },
+ { oPhotoViewer, "photo-viewer", 2, "@" },
+ { oSetNotation, "set-notation", 2, "@" },
+ { oSetNotation, "notation-data", 2, "@" }, /* Alias */
+ { oSigNotation, "sig-notation", 2, "@" },
+ { oCertNotation, "cert-notation", 2, "@" },
+
+ { 302, NULL, 0, N_(
+ "@\n(See the man page for a complete listing of all commands and options)\n"
+ )},
+
+ { 303, NULL, 0, N_("@\nExamples:\n\n"
+ " -se -r Bob [file] sign and encrypt for user Bob\n"
+ " --clearsign [file] make a clear text signature\n"
+ " --detach-sign [file] make a detached signature\n"
+ " --list-keys [names] show keys\n"
+ " --fingerprint [names] show fingerprints\n" ) },
+
+ /* hidden options */
+ { aListOwnerTrust, "list-ownertrust", 256, "@"}, /* deprecated */
+ { aPrintMDs, "print-mds" , 256, "@"}, /* old */
+ { aListTrustDB, "list-trustdb",0 , "@"},
+ /* Not yet used */
+ /* { aListTrustPath, "list-trust-path",0, "@"}, */
+ { aPipeMode, "pipemode", 0, "@" },
+ { oKOption, NULL, 0, "@"},
+ { oPasswd, "passphrase",2, "@" },
+ { oPasswdFD, "passphrase-fd",1, "@" },
+ { oPasswdFile, "passphrase-file",2, "@" },
+ { oPasswdRepeat, "passphrase-repeat", 1, "@"},
+ { oCommandFD, "command-fd",1, "@" },
+ { oCommandFile, "command-file",2, "@" },
+ { oQuickRandom, "quick-random", 0, "@"},
+ { oNoVerbose, "no-verbose", 0, "@"},
+ { oTrustDBName, "trustdb-name", 2, "@" },
+ { oNoSecmemWarn, "no-secmem-warning", 0, "@" },
+ { oRequireSecmem,"require-secmem", 0, "@" },
+ { oNoRequireSecmem,"no-require-secmem", 0, "@" },
+ { oNoPermissionWarn, "no-permission-warning", 0, "@" },
+ { oNoMDCWarn, "no-mdc-warning", 0, "@" },
+ { oNoArmor, "no-armor", 0, "@"},
+ { oNoArmor, "no-armour", 0, "@"},
+ { oNoDefKeyring, "no-default-keyring", 0, "@" },
+ { oNoGreeting, "no-greeting", 0, "@" },
+ { oNoOptions, "no-options", 0, "@" }, /* shortcut for --options /dev/null */
+ { oHomedir, "homedir", 2, "@" }, /* defaults to "~/.gnupg" */
+ { oNoBatch, "no-batch", 0, "@" },
+ { oWithColons, "with-colons", 0, "@"},
+ { oWithKeyData,"with-key-data", 0, "@"},
+ { aListKeys, "list-key", 0, "@" }, /* alias */
+ { aListSigs, "list-sig", 0, "@" }, /* alias */
+ { aCheckKeys, "check-sig",0, "@" }, /* alias */
+ { oSkipVerify, "skip-verify",0, "@" },
+ { oCompressKeys, "compress-keys",0, "@"},
+ { oCompressSigs, "compress-sigs",0, "@"},
+ { oDefCertLevel, "default-cert-check-level", 1, "@"}, /* Old option */
+ { oAlwaysTrust, "always-trust", 0, "@"},
+ { oTrustModel, "trust-model", 2, "@"},
+ { oForceOwnertrust, "force-ownertrust", 2, "@"},
+ { oRunAsShmCP, "run-as-shm-coprocess", 4, "@" },
+ { oSetFilename, "set-filename", 2, "@" },
+ { oForYourEyesOnly, "for-your-eyes-only", 0, "@" },
+ { oNoForYourEyesOnly, "no-for-your-eyes-only", 0, "@" },
+ { oSetPolicyURL, "set-policy-url", 2, "@" },
+ { oSigPolicyURL, "sig-policy-url", 2, "@" },
+ { oCertPolicyURL, "cert-policy-url", 2, "@" },
+ { oShowPolicyURL, "show-policy-url", 0, "@" },
+ { oNoShowPolicyURL, "no-show-policy-url", 0, "@" },
+ { oSigKeyserverURL, "sig-keyserver-url", 2, "@" },
+ { oShowNotation, "show-notation", 0, "@" },
+ { oNoShowNotation, "no-show-notation", 0, "@" },
+ { oComment, "comment", 2, "@" },
+ { oDefaultComment, "default-comment", 0, "@" },
+ { oNoComments, "no-comments", 0, "@" },
+ { oEmitVersion, "emit-version", 0, "@"},
+ { oNoEmitVersion, "no-emit-version", 0, "@"},
+ { oNoEmitVersion, "no-version", 0, "@"}, /* alias */
+ { oNotDashEscaped, "not-dash-escaped", 0, "@" },
+ { oEscapeFrom, "escape-from-lines", 0, "@" },
+ { oNoEscapeFrom, "no-escape-from-lines", 0, "@" },
+ { oLockOnce, "lock-once", 0, "@" },
+ { oLockMultiple, "lock-multiple", 0, "@" },
+ { oLockNever, "lock-never", 0, "@" },
+ { oLoggerFD, "logger-fd",1, "@" },
+ { oLoggerFile, "logger-file",2, "@" },
+ { oUseEmbeddedFilename, "use-embedded-filename", 0, "@" },
+ { oNoUseEmbeddedFilename, "no-use-embedded-filename", 0, "@" },
+ { oUtf8Strings, "utf8-strings", 0, "@" },
+ { oNoUtf8Strings, "no-utf8-strings", 0, "@" },
+ { oWithFingerprint, "with-fingerprint", 0, "@" },
+ { oDisableCipherAlgo, "disable-cipher-algo", 2, "@" },
+ { oDisablePubkeyAlgo, "disable-pubkey-algo", 2, "@" },
+ { oAllowNonSelfsignedUID, "allow-non-selfsigned-uid", 0, "@" },
+ { oNoAllowNonSelfsignedUID, "no-allow-non-selfsigned-uid", 0, "@" },
+ { oAllowFreeformUID, "allow-freeform-uid", 0, "@" },
+ { oNoAllowFreeformUID, "no-allow-freeform-uid", 0, "@" },
+ { oNoLiteral, "no-literal", 0, "@" },
+ { oSetFilesize, "set-filesize", 20, "@" },
+ { oHonorHttpProxy,"honor-http-proxy", 0, "@" },
+ { oFastListMode,"fast-list-mode", 0, "@" },
+ { oFixedListMode,"fixed-list-mode", 0, "@" },
+ { oListOnly, "list-only", 0, "@"},
+ { oIgnoreTimeConflict, "ignore-time-conflict", 0, "@" },
+ { oIgnoreValidFrom, "ignore-valid-from", 0, "@" },
+ { oIgnoreCrcError, "ignore-crc-error", 0,"@" },
+ { oIgnoreMDCError, "ignore-mdc-error", 0,"@" },
+ { oShowSessionKey, "show-session-key", 0, "@" },
+ { oOverrideSessionKey, "override-session-key", 2, "@" },
+ { oNoRandomSeedFile, "no-random-seed-file", 0, "@" },
+ { oAutoKeyRetrieve, "auto-key-retrieve", 0, "@" },
+ { oNoAutoKeyRetrieve, "no-auto-key-retrieve", 0, "@" },
+ { oNoSigCache, "no-sig-cache", 0, "@" },
+ { oNoSigCreateCheck, "no-sig-create-check", 0, "@" },
+ { oAutoCheckTrustDB, "auto-check-trustdb", 0, "@"},
+ { oNoAutoCheckTrustDB, "no-auto-check-trustdb", 0, "@"},
+ { oMergeOnly, "merge-only", 0, "@" },
+ { oAllowSecretKeyImport, "allow-secret-key-import", 0, "@" },
+ { oTryAllSecrets, "try-all-secrets", 0, "@" },
+ { oEnableSpecialFilenames, "enable-special-filenames", 0, "@" },
+ { oNoExpensiveTrustChecks, "no-expensive-trust-checks", 0, "@" },
+ { aDeleteSecretAndPublicKeys, "delete-secret-and-public-keys",256, "@" },
+ { aRebuildKeydbCaches, "rebuild-keydb-caches", 256, "@"},
+ { oPreservePermissions, "preserve-permissions", 0, "@"},
+ { oDefaultPreferenceList, "default-preference-list", 2, "@"},
+ { oDefaultKeyserverURL, "default-keyserver-url", 2, "@"},
+ { oPersonalCipherPreferences, "personal-cipher-preferences", 2, "@"},
+ { oPersonalDigestPreferences, "personal-digest-preferences", 2, "@"},
+ { oPersonalCompressPreferences, "personal-compress-preferences", 2, "@"},
+ /* Aliases. I constantly mistype these, and assume other people
+ do as well. */
+ { oPersonalCipherPreferences, "personal-cipher-prefs", 2, "@"},
+ { oPersonalDigestPreferences, "personal-digest-prefs", 2, "@"},
+ { oPersonalCompressPreferences, "personal-compress-prefs", 2, "@"},
+ { oDisplay, "display", 2, "@" },
+ { oTTYname, "ttyname", 2, "@" },
+ { oTTYtype, "ttytype", 2, "@" },
+ { oLCctype, "lc-ctype", 2, "@" },
+ { oLCmessages, "lc-messages", 2, "@" },
+ { oGroup, "group", 2, "@" },
+ { oUnGroup, "ungroup", 2, "@" },
+ { oNoGroups, "no-groups", 0, "@" },
+ { oStrict, "strict", 0, "@" },
+ { oNoStrict, "no-strict", 0, "@" },
+ { oMangleDosFilenames, "mangle-dos-filenames", 0, "@" },
+ { oNoMangleDosFilenames, "no-mangle-dos-filenames", 0, "@" },
+ { oEnableProgressFilter, "enable-progress-filter", 0, "@" },
+ { oMultifile, "multifile", 0, "@" },
+ { oKeyidFormat, "keyid-format", 2, "@" },
+ { oExitOnStatusWriteError, "exit-on-status-write-error", 0, "@" },
+ { oLimitCardInsertTries, "limit-card-insert-tries", 1, "@"},
+
+ { oReaderPort, "reader-port", 2, "@"},
+ { octapiDriver, "ctapi-driver", 2, "@"},
+ { opcscDriver, "pcsc-driver", 2, "@"},
+ { oDisableCCID, "disable-ccid", 0, "@"},
+#if defined(ENABLE_CARD_SUPPORT) && defined(HAVE_LIBUSB)
+ { oDebugCCIDDriver, "debug-ccid-driver", 0, "@"},
+#endif
+ { oAllowMultisigVerification, "allow-multisig-verification", 0, "@"},
+ { oEnableDSA2, "enable-dsa2", 0, "@"},
+ { oDisableDSA2, "disable-dsa2", 0, "@"},
+
+ /* These two are aliases to help users of the PGP command line
+ product use gpg with minimal pain. Many commands are common
+ already as they seem to have borrowed commands from us. Now
+ I'm returning the favor. */
+ { oLocalUser, "sign-with", 2, "@" },
+ { oRecipient, "user", 2, "@" },
+ { oRequireCrossCert, "require-backsigs", 0, "@"},
+ { oRequireCrossCert, "require-cross-certification", 0, "@"},
+ { oNoRequireCrossCert, "no-require-backsigs", 0, "@"},
+ { oNoRequireCrossCert, "no-require-cross-certification", 0, "@"},
+ { oAutoKeyLocate, "auto-key-locate", 2, "@"},
+ { oNoAutoKeyLocate, "no-auto-key-locate", 0, "@"},
+ {0,NULL,0,NULL}
+};
+
+
+#ifdef ENABLE_SELINUX_HACKS
+#define ALWAYS_ADD_KEYRINGS 1
+#else
+#define ALWAYS_ADD_KEYRINGS 0
+#endif
+
+
+int g10_errors_seen = 0;
+
+static int utf8_strings = 0;
+static int maybe_setuid = 1;
+
+static char *build_list( const char *text, char letter,
+ const char *(*mapf)(int), int (*chkf)(int) );
+static void set_cmd( enum cmd_and_opt_values *ret_cmd,
+ enum cmd_and_opt_values new_cmd );
+static void print_mds( const char *fname, int algo );
+static void add_notation_data( const char *string, int which );
+static void add_policy_url( const char *string, int which );
+static void add_keyserver_url( const char *string, int which );
+
+const char *
+strusage( int level )
+{
+ static char *digests, *pubkeys, *ciphers, *zips;
+ const char *p;
+ switch( level ) {
+ case 11: p = "gpg (GnuPG)";
+ break;
+ case 13: p = VERSION; break;
+ case 17: p = PRINTABLE_OS_NAME; break;
+ case 19: p =
+ _("Please report bugs to <gnupg-bugs@gnu.org>.\n");
+ break;
+
+#ifdef IS_DEVELOPMENT_VERSION
+ case 20:
+ p="NOTE: THIS IS A DEVELOPMENT VERSION!";
+ break;
+ case 21:
+ p="It is only intended for test purposes and should NOT be";
+ break;
+ case 22:
+ p="used in a production environment or with production keys!";
+ break;
+#endif
+
+ case 1:
+ case 40: p =
+ _("Usage: gpg [options] [files] (-h for help)");
+ break;
+ case 41: p =
+ _("Syntax: gpg [options] [files]\n"
+ "sign, check, encrypt or decrypt\n"
+ "default operation depends on the input data\n");
+ break;
+
+ case 31: p = "\nHome: "; break;
+#ifndef __riscos__
+ case 32: p = opt.homedir; break;
+#else /* __riscos__ */
+ case 32: p = make_filename(opt.homedir, NULL); break;
+#endif /* __riscos__ */
+ case 33: p = _("\nSupported algorithms:\n"); break;
+ case 34:
+ if( !pubkeys )
+ pubkeys = build_list(_("Pubkey: "), 0, pubkey_algo_to_string,
+ check_pubkey_algo );
+ p = pubkeys;
+ break;
+ case 35:
+ if( !ciphers )
+ ciphers = build_list(_("Cipher: "), 'S', cipher_algo_to_string,
+ check_cipher_algo );
+ p = ciphers;
+ break;
+ case 36:
+ if( !digests )
+ digests = build_list(_("Hash: "), 'H', digest_algo_to_string,
+ check_digest_algo );
+ p = digests;
+ break;
+ case 37:
+ if( !zips )
+ zips = build_list(_("Compression: "),'Z',compress_algo_to_string,
+ check_compress_algo);
+ p = zips;
+ break;
+
+ default: p = default_strusage(level);
+ }
+ return p;
+}
+
+
+static char *
+build_list( const char *text, char letter,
+ const char * (*mapf)(int), int (*chkf)(int) )
+{
+ int i;
+ const char *s;
+ size_t n=strlen(text)+2;
+ char *list, *p, *line=NULL;
+
+ if( maybe_setuid )
+ secmem_init( 0 ); /* drop setuid */
+
+ for(i=0; i <= 110; i++ )
+ if( !chkf(i) && (s=mapf(i)) )
+ n += strlen(s) + 7 + 2;
+ list = xmalloc( 21 + n ); *list = 0;
+ for(p=NULL, i=0; i <= 110; i++ ) {
+ if( !chkf(i) && (s=mapf(i)) ) {
+ if( !p ) {
+ p = stpcpy( list, text );
+ line=p;
+ }
+ else
+ p = stpcpy( p, ", ");
+
+ if(strlen(line)>60) {
+ int spaces=strlen(text);
+
+ list=xrealloc(list,n+spaces+1);
+ /* realloc could move the block, so find the end again */
+ p=list;
+ while(*p)
+ p++;
+
+ p=stpcpy(p, "\n");
+ line=p;
+ for(;spaces;spaces--)
+ p=stpcpy(p, " ");
+ }
+
+ p = stpcpy(p, s );
+ if(opt.verbose && letter)
+ {
+ char num[8];
+ sprintf(num," (%c%d)",letter,i);
+ p = stpcpy(p,num);
+ }
+ }
+ }
+ if( p )
+ p = stpcpy(p, "\n" );
+ return list;
+}
+
+
+static void
+i18n_init(void)
+{
+#ifdef USE_SIMPLE_GETTEXT
+ set_gettext_file (PACKAGE, "Software\\GNU\\GnuPG");
+#else
+#ifdef ENABLE_NLS
+ setlocale( LC_ALL, "" );
+ bindtextdomain( PACKAGE, G10_LOCALEDIR );
+ textdomain( PACKAGE );
+#endif
+#endif
+}
+
+static void
+wrong_args( const char *text)
+{
+ fputs(_("usage: gpg [options] "),stderr);
+ fputs(text,stderr);
+ putc('\n',stderr);
+ g10_exit(2);
+}
+
+
+static char *
+make_username( const char *string )
+{
+ char *p;
+ if( utf8_strings )
+ p = xstrdup(string);
+ else
+ p = native_to_utf8( string );
+ return p;
+}
+
+
+static void
+set_debug(void)
+{
+ if( opt.debug & DBG_MEMORY_VALUE )
+ memory_debug_mode = 1;
+ if( opt.debug & DBG_MEMSTAT_VALUE )
+ memory_stat_debug_mode = 1;
+ if( opt.debug & DBG_MPI_VALUE )
+ mpi_debug_mode = 1;
+ if( opt.debug & DBG_CIPHER_VALUE )
+ g10c_debug_mode = 1;
+ if( opt.debug & DBG_IOBUF_VALUE )
+ iobuf_debug_mode = 1;
+
+}
+
+
+/* We need the home directory also in some other directories, so make
+ sure that both variables are always in sync. */
+static void
+set_homedir (char *dir)
+{
+ if (!dir)
+ dir = "";
+ g10_opt_homedir = opt.homedir = dir;
+}
+
+
+/* We set the screen dimensions for UI purposes. Do not allow screens
+ smaller than 80x24 for the sake of simplicity. */
+static void
+set_screen_dimensions(void)
+{
+#ifndef _WIN32
+ char *str;
+
+ str=getenv("COLUMNS");
+ if(str)
+ opt.screen_columns=atoi(str);
+
+ str=getenv("LINES");
+ if(str)
+ opt.screen_lines=atoi(str);
+#endif
+
+ if(opt.screen_columns<80 || opt.screen_columns>255)
+ opt.screen_columns=80;
+
+ if(opt.screen_lines<24 || opt.screen_lines>255)
+ opt.screen_lines=24;
+}
+
+
+/* Helper to open a file FNAME either for reading or writing to be
+ used with --status-file etc functions. Not generally useful but it
+ avoids the riscos specific functions and well some Windows people
+ might like it too. Prints an error message and returns -1 on
+ error. On success the file descriptor is returned. */
+static int
+open_info_file (const char *fname, int for_write)
+{
+#ifdef __riscos__
+ return riscos_fdopenfile (fname, for_write);
+#elif defined (ENABLE_SELINUX_HACKS)
+ /* We can't allow these even when testing for a secured filename
+ because files to be secured might not yet been secured. This is
+ similar to the option file but in that case it is unlikely that
+ sensitive information may be retrieved by means of error
+ messages. */
+ return -1;
+#else
+ int fd;
+
+/* if (is_secured_filename (fname)) */
+/* { */
+/* fd = -1; */
+/* errno = EPERM; */
+/* } */
+/* else */
+/* { */
+ do
+ {
+ if (for_write)
+ fd = open (fname, O_CREAT | O_TRUNC | O_WRONLY,
+ S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP);
+ else
+ fd = open (fname, O_RDONLY | MY_O_BINARY);
+ }
+ while (fd == -1 && errno == EINTR);
+/* } */
+ if ( fd == -1)
+ log_error ( for_write? _("can't create `%s': %s\n")
+ : _("can't open `%s': %s\n"), fname, strerror(errno));
+
+ return fd;
+#endif
+}
+
+static void
+set_cmd( enum cmd_and_opt_values *ret_cmd, enum cmd_and_opt_values new_cmd )
+{
+ enum cmd_and_opt_values cmd = *ret_cmd;
+
+ if( !cmd || cmd == new_cmd )
+ cmd = new_cmd;
+ else if( cmd == aSign && new_cmd == aEncr )
+ cmd = aSignEncr;
+ else if( cmd == aEncr && new_cmd == aSign )
+ cmd = aSignEncr;
+ else if( cmd == aSign && new_cmd == aSym )
+ cmd = aSignSym;
+ else if( cmd == aSym && new_cmd == aSign )
+ cmd = aSignSym;
+ else if( cmd == aSym && new_cmd == aEncr )
+ cmd = aEncrSym;
+ else if( cmd == aEncr && new_cmd == aSym )
+ cmd = aEncrSym;
+ else if( cmd == aKMode && new_cmd == aSym )
+ cmd = aKModeC;
+ else if (cmd == aSignEncr && new_cmd == aSym)
+ cmd = aSignEncrSym;
+ else if (cmd == aSignSym && new_cmd == aEncr)
+ cmd = aSignEncrSym;
+ else if (cmd == aEncrSym && new_cmd == aSign)
+ cmd = aSignEncrSym;
+ else if( ( cmd == aSign && new_cmd == aClearsign )
+ || ( cmd == aClearsign && new_cmd == aSign ) )
+ cmd = aClearsign;
+ else {
+ log_error(_("conflicting commands\n"));
+ g10_exit(2);
+ }
+
+ *ret_cmd = cmd;
+}
+
+
+static void
+add_group(char *string)
+{
+ char *name,*value;
+ struct groupitem *item;
+
+ /* Break off the group name */
+ name=strsep(&string,"=");
+ if(string==NULL)
+ {
+ log_error(_("no = sign found in group definition `%s'\n"),name);
+ return;
+ }
+
+ trim_trailing_ws(name,strlen(name));
+
+ /* Does this group already exist? */
+ for(item=opt.grouplist;item;item=item->next)
+ if(strcasecmp(item->name,name)==0)
+ break;
+
+ if(!item)
+ {
+ item=xmalloc(sizeof(struct groupitem));
+ item->name=name;
+ item->next=opt.grouplist;
+ item->values=NULL;
+ opt.grouplist=item;
+ }
+
+ /* Break apart the values */
+ while ((value= strsep(&string," \t")))
+ {
+ if (*value)
+ add_to_strlist2(&item->values,value,utf8_strings);
+ }
+}
+
+
+static void
+rm_group(char *name)
+{
+ struct groupitem *item,*last=NULL;
+
+ trim_trailing_ws(name,strlen(name));
+
+ for(item=opt.grouplist;item;last=item,item=item->next)
+ {
+ if(strcasecmp(item->name,name)==0)
+ {
+ if(last)
+ last->next=item->next;
+ else
+ opt.grouplist=item->next;
+
+ free_strlist(item->values);
+ xfree(item);
+ break;
+ }
+ }
+}
+
+
+/* We need to check three things.
+
+ 0) The homedir. It must be x00, a directory, and owned by the
+ user.
+
+ 1) The options/gpg.conf file. Okay unless it or its containing
+ directory is group or other writable or not owned by us. Disable
+ exec in this case.
+
+ 2) Extensions. Same as #1.
+
+ Returns true if the item is unsafe. */
+static int
+check_permissions(const char *path,int item)
+{
+#if defined(HAVE_STAT) && !defined(HAVE_DOSISH_SYSTEM)
+ static int homedir_cache=-1;
+ char *tmppath,*dir;
+ struct stat statbuf,dirbuf;
+ int homedir=0,ret=0,checkonly=0;
+ int perm=0,own=0,enc_dir_perm=0,enc_dir_own=0;
+
+ if(opt.no_perm_warn)
+ return 0;
+
+ assert(item==0 || item==1 || item==2);
+
+ /* extensions may attach a path */
+ if(item==2 && path[0]!=DIRSEP_C)
+ {
+ if(strchr(path,DIRSEP_C))
+ tmppath=make_filename(path,NULL);
+ else
+ tmppath=make_filename(GNUPG_LIBDIR,path,NULL);
+ }
+ else
+ tmppath=xstrdup(path);
+
+ /* If the item is located in the homedir, but isn't the homedir,
+ don't continue if we already checked the homedir itself. This is
+ to avoid user confusion with an extra options file warning which
+ could be rectified if the homedir itself had proper
+ permissions. */
+ if(item!=0 && homedir_cache>-1
+ && ascii_strncasecmp(opt.homedir,tmppath,strlen(opt.homedir))==0)
+ {
+ ret=homedir_cache;
+ goto end;
+ }
+
+ /* It's okay if the file or directory doesn't exist */
+ if(stat(tmppath,&statbuf)!=0)
+ {
+ ret=0;
+ goto end;
+ }
+
+ /* Now check the enclosing directory. Theoretically, we could walk
+ this test up to the root directory /, but for the sake of sanity,
+ I'm stopping at one level down. */
+ dir=make_dirname(tmppath);
+
+ if(stat(dir,&dirbuf)!=0 || !S_ISDIR(dirbuf.st_mode))
+ {
+ /* Weird error */
+ ret=1;
+ goto end;
+ }
+
+ xfree(dir);
+
+ /* Assume failure */
+ ret=1;
+
+ if(item==0)
+ {
+ /* The homedir must be x00, a directory, and owned by the user. */
+
+ if(S_ISDIR(statbuf.st_mode))
+ {
+ if(statbuf.st_uid==getuid())
+ {
+ if((statbuf.st_mode & (S_IRWXG|S_IRWXO))==0)
+ ret=0;
+ else
+ perm=1;
+ }
+ else
+ own=1;
+
+ homedir_cache=ret;
+ }
+ }
+ else if(item==1 || item==2)
+ {
+ /* The options or extension file. Okay unless it or its
+ containing directory is group or other writable or not owned
+ by us or root. */
+
+ if(S_ISREG(statbuf.st_mode))
+ {
+ if(statbuf.st_uid==getuid() || statbuf.st_uid==0)
+ {
+ if((statbuf.st_mode & (S_IWGRP|S_IWOTH))==0)
+ {
+ /* it's not writable, so make sure the enclosing
+ directory is also not writable */
+ if(dirbuf.st_uid==getuid() || dirbuf.st_uid==0)
+ {
+ if((dirbuf.st_mode & (S_IWGRP|S_IWOTH))==0)
+ ret=0;
+ else
+ enc_dir_perm=1;
+ }
+ else
+ enc_dir_own=1;
+ }
+ else
+ {
+ /* it's writable, so the enclosing directory had
+ better not let people get to it. */
+ if(dirbuf.st_uid==getuid() || dirbuf.st_uid==0)
+ {
+ if((dirbuf.st_mode & (S_IRWXG|S_IRWXO))==0)
+ ret=0;
+ else
+ perm=enc_dir_perm=1; /* unclear which one to fix! */
+ }
+ else
+ enc_dir_own=1;
+ }
+ }
+ else
+ own=1;
+ }
+ }
+ else
+ BUG();
+
+ if(!checkonly)
+ {
+ if(own)
+ {
+ if(item==0)
+ log_info(_("WARNING: unsafe ownership on"
+ " homedir `%s'\n"),tmppath);
+ else if(item==1)
+ log_info(_("WARNING: unsafe ownership on"
+ " configuration file `%s'\n"),tmppath);
+ else
+ log_info(_("WARNING: unsafe ownership on"
+ " extension `%s'\n"),tmppath);
+ }
+ if(perm)
+ {
+ if(item==0)
+ log_info(_("WARNING: unsafe permissions on"
+ " homedir `%s'\n"),tmppath);
+ else if(item==1)
+ log_info(_("WARNING: unsafe permissions on"
+ " configuration file `%s'\n"),tmppath);
+ else
+ log_info(_("WARNING: unsafe permissions on"
+ " extension `%s'\n"),tmppath);
+ }
+ if(enc_dir_own)
+ {
+ if(item==0)
+ log_info(_("WARNING: unsafe enclosing directory ownership on"
+ " homedir `%s'\n"),tmppath);
+ else if(item==1)
+ log_info(_("WARNING: unsafe enclosing directory ownership on"
+ " configuration file `%s'\n"),tmppath);
+ else
+ log_info(_("WARNING: unsafe enclosing directory ownership on"
+ " extension `%s'\n"),tmppath);
+ }
+ if(enc_dir_perm)
+ {
+ if(item==0)
+ log_info(_("WARNING: unsafe enclosing directory permissions on"
+ " homedir `%s'\n"),tmppath);
+ else if(item==1)
+ log_info(_("WARNING: unsafe enclosing directory permissions on"
+ " configuration file `%s'\n"),tmppath);
+ else
+ log_info(_("WARNING: unsafe enclosing directory permissions on"
+ " extension `%s'\n"),tmppath);
+ }
+ }
+
+ end:
+ xfree(tmppath);
+
+ if(homedir)
+ homedir_cache=ret;
+
+ return ret;
+
+#endif /* HAVE_STAT && !HAVE_DOSISH_SYSTEM */
+
+ return 0;
+}
+
+
+static void
+print_algo_numbers(int (*checker)(int))
+{
+ int i,first=1;
+
+ for(i=0;i<=110;i++)
+ {
+ if(!checker(i))
+ {
+ if(first)
+ first=0;
+ else
+ printf(";");
+ printf("%d",i);
+ }
+ }
+}
+
+
+/* In the future, we can do all sorts of interesting configuration
+ output here. For now, just give "group" as the Enigmail folks need
+ it, and pubkey, cipher, hash, and compress as they may be useful
+ for frontends. */
+static void
+list_config(char *items)
+{
+ int show_all=(items==NULL);
+ char *name=NULL;
+
+ if(!opt.with_colons)
+ return;
+
+ while(show_all || (name=strsep(&items," ")))
+ {
+ int any=0;
+
+ if(show_all || ascii_strcasecmp(name,"group")==0)
+ {
+ struct groupitem *iter;
+
+ for(iter=opt.grouplist;iter;iter=iter->next)
+ {
+ STRLIST sl;
+
+ printf("cfg:group:");
+ print_string(stdout,iter->name,strlen(iter->name),':');
+ printf(":");
+
+ for(sl=iter->values;sl;sl=sl->next)
+ {
+ print_string2(stdout,sl->d,strlen(sl->d),':',';');
+ if(sl->next)
+ printf(";");
+ }
+
+ printf("\n");
+ }
+
+ any=1;
+ }
+
+ if(show_all || ascii_strcasecmp(name,"version")==0)
+ {
+ printf("cfg:version:");
+ print_string(stdout,VERSION,strlen(VERSION),':');
+ printf("\n");
+ any=1;
+ }
+
+ if(show_all || ascii_strcasecmp(name,"pubkey")==0)
+ {
+ printf("cfg:pubkey:");
+ print_algo_numbers(check_pubkey_algo);
+ printf("\n");
+ any=1;
+ }
+
+ if(show_all || ascii_strcasecmp(name,"cipher")==0)
+ {
+ printf("cfg:cipher:");
+ print_algo_numbers(check_cipher_algo);
+ printf("\n");
+ any=1;
+ }
+
+ if(show_all
+ || ascii_strcasecmp(name,"digest")==0
+ || ascii_strcasecmp(name,"hash")==0)
+ {
+ printf("cfg:digest:");
+ print_algo_numbers(check_digest_algo);
+ printf("\n");
+ any=1;
+ }
+
+ if(show_all || ascii_strcasecmp(name,"compress")==0)
+ {
+ printf("cfg:compress:");
+ print_algo_numbers(check_compress_algo);
+ printf("\n");
+ any=1;
+ }
+
+ if(show_all || ascii_strcasecmp(name,"ccid-reader-id")==0)
+ {
+#if defined(ENABLE_CARD_SUPPORT) && defined(HAVE_LIBUSB)
+ char *p, *p2, *list = ccid_get_reader_list ();
+
+ for (p=list; p && (p2 = strchr (p, '\n')); p = p2+1)
+ {
+ *p2 = 0;
+ printf("cfg:ccid-reader-id:%s\n", p);
+ }
+ free (list);
+#endif
+ any=1;
+ }
+
+ if(show_all)
+ break;
+
+ if(!any)
+ log_error(_("unknown configuration item `%s'\n"),name);
+ }
+}
+
+
+/* List options and default values in the GPG Conf format. This is a
+ new tool distributed with gnupg 1.9.x but we also want some limited
+ support in older gpg versions. The output is the name of the
+ configuration file and a list of options available for editing by
+ gpgconf. */
+static void
+gpgconf_list (const char *configfile)
+{
+ /* The following definitions are taken from gnupg/tools/gpgconf-comp.c. */
+#define GC_OPT_FLAG_NONE 0UL
+#define GC_OPT_FLAG_DEFAULT (1UL << 4)
+
+ printf ("gpgconf-gpg.conf:%lu:\"%s\n",
+ GC_OPT_FLAG_DEFAULT,configfile?configfile:"/dev/null");
+ printf ("verbose:%lu:\n", GC_OPT_FLAG_NONE);
+ printf ("quiet:%lu:\n", GC_OPT_FLAG_NONE);
+ printf ("keyserver:%lu:\n", GC_OPT_FLAG_NONE);
+ printf ("reader-port:%lu:\n", GC_OPT_FLAG_NONE);
+}
+
+
+static int
+parse_subpacket_list(char *list)
+{
+ char *tok;
+ byte subpackets[128],i;
+ int count=0;
+
+ if(!list)
+ {
+ /* No arguments means all subpackets */
+ memset(subpackets+1,1,sizeof(subpackets)-1);
+ count=127;
+ }
+ else
+ {
+ memset(subpackets,0,sizeof(subpackets));
+
+ /* Merge with earlier copy */
+ if(opt.show_subpackets)
+ {
+ byte *in;
+
+ for(in=opt.show_subpackets;*in;in++)
+ {
+ if(*in>127 || *in<1)
+ BUG();
+
+ if(!subpackets[*in])
+ count++;
+ subpackets[*in]=1;
+ }
+ }
+
+ while((tok=strsep(&list," ,")))
+ {
+ if(!*tok)
+ continue;
+
+ i=atoi(tok);
+ if(i>127 || i<1)
+ return 0;
+
+ if(!subpackets[i])
+ count++;
+ subpackets[i]=1;
+ }
+ }
+
+ xfree(opt.show_subpackets);
+ opt.show_subpackets=xmalloc(count+1);
+ opt.show_subpackets[count--]=0;
+
+ for(i=1;i<128 && count>=0;i++)
+ if(subpackets[i])
+ opt.show_subpackets[count--]=i;
+
+ return 1;
+}
+
+
+static int
+parse_list_options(char *str)
+{
+ char *subpackets=""; /* something that isn't NULL */
+ struct parse_options lopts[]=
+ {
+ {"show-photos",LIST_SHOW_PHOTOS,NULL,
+ N_("display photo IDs during key listings")},
+ {"show-policy-urls",LIST_SHOW_POLICY_URLS,NULL,
+ N_("show policy URLs during signature listings")},
+ {"show-notations",LIST_SHOW_NOTATIONS,NULL,
+ N_("show all notations during signature listings")},
+ {"show-std-notations",LIST_SHOW_STD_NOTATIONS,NULL,
+ N_("show IETF standard notations during signature listings")},
+ {"show-standard-notations",LIST_SHOW_STD_NOTATIONS,NULL,
+ NULL},
+ {"show-user-notations",LIST_SHOW_USER_NOTATIONS,NULL,
+ N_("show user-supplied notations during signature listings")},
+ {"show-keyserver-urls",LIST_SHOW_KEYSERVER_URLS,NULL,
+ N_("show preferred keyserver URLs during signature listings")},
+ {"show-uid-validity",LIST_SHOW_UID_VALIDITY,NULL,
+ N_("show user ID validity during key listings")},
+ {"show-unusable-uids",LIST_SHOW_UNUSABLE_UIDS,NULL,
+ N_("show revoked and expired user IDs in key listings")},
+ {"show-unusable-subkeys",LIST_SHOW_UNUSABLE_SUBKEYS,NULL,
+ N_("show revoked and expired subkeys in key listings")},
+ {"show-keyring",LIST_SHOW_KEYRING,NULL,
+ N_("show the keyring name in key listings")},
+ {"show-sig-expire",LIST_SHOW_SIG_EXPIRE,NULL,
+ N_("show expiration dates during signature listings")},
+ {"show-sig-subpackets",LIST_SHOW_SIG_SUBPACKETS,NULL,
+ NULL},
+ {NULL,0,NULL,NULL}
+ };
+
+ /* C99 allows for non-constant initializers, but we'd like to
+ compile everywhere, so fill in the show-sig-subpackets argument
+ here. Note that if the parse_options array changes, we'll have
+ to change the subscript here. */
+ lopts[12].value=&subpackets;
+
+ if(parse_options(str,&opt.list_options,lopts,1))
+ {
+ if(opt.list_options&LIST_SHOW_SIG_SUBPACKETS)
+ {
+ /* Unset so users can pass multiple lists in. */
+ opt.list_options&=~LIST_SHOW_SIG_SUBPACKETS;
+ if(!parse_subpacket_list(subpackets))
+ return 0;
+ }
+ else if(subpackets==NULL && opt.show_subpackets)
+ {
+ /* User did 'no-show-subpackets' */
+ xfree(opt.show_subpackets);
+ opt.show_subpackets=NULL;
+ }
+
+ return 1;
+ }
+ else
+ return 0;
+}
+
+
+/* Collapses argc/argv into a single string that must be freed */
+static char *
+collapse_args(int argc,char *argv[])
+{
+ char *str=NULL;
+ int i,first=1,len=0;
+
+ for(i=0;i<argc;i++)
+ {
+ len+=strlen(argv[i])+2;
+ str=xrealloc(str,len);
+ if(first)
+ {
+ str[0]='\0';
+ first=0;
+ }
+ else
+ strcat(str," ");
+
+ strcat(str,argv[i]);
+ }
+
+ return str;
+}
+
+static void
+parse_trust_model(const char *model)
+{
+ if(ascii_strcasecmp(model,"pgp")==0)
+ opt.trust_model=TM_PGP;
+ else if(ascii_strcasecmp(model,"classic")==0)
+ opt.trust_model=TM_CLASSIC;
+ else if(ascii_strcasecmp(model,"always")==0)
+ opt.trust_model=TM_ALWAYS;
+ else if(ascii_strcasecmp(model,"direct")==0)
+ opt.trust_model=TM_DIRECT;
+ else if(ascii_strcasecmp(model,"auto")==0)
+ opt.trust_model=TM_AUTO;
+ else
+ log_error("unknown trust model `%s'\n",model);
+}
+
+/* Must be called before we open any files. */
+static void
+reopen_std(void)
+{
+#if defined(HAVE_STAT) && !defined(HAVE_W32_SYSTEM)
+ struct stat statbuf;
+ int did_stdin=0,did_stdout=0,did_stderr=0;
+ FILE *complain;
+
+ if(fstat(STDIN_FILENO,&statbuf)==-1 && errno==EBADF)
+ {
+ if(open("/dev/null",O_RDONLY)==STDIN_FILENO)
+ did_stdin=1;
+ else
+ did_stdin=2;
+ }
+
+ if(fstat(STDOUT_FILENO,&statbuf)==-1 && errno==EBADF)
+ {
+ if(open("/dev/null",O_WRONLY)==STDOUT_FILENO)
+ did_stdout=1;
+ else
+ did_stdout=2;
+ }
+
+ if(fstat(STDERR_FILENO,&statbuf)==-1 && errno==EBADF)
+ {
+ if(open("/dev/null",O_WRONLY)==STDERR_FILENO)
+ did_stderr=1;
+ else
+ did_stderr=2;
+ }
+
+ /* It's hard to log this sort of thing since the filehandle we would
+ complain to may be closed... */
+ if(did_stderr==0)
+ complain=stderr;
+ else if(did_stdout==0)
+ complain=stdout;
+ else
+ complain=NULL;
+
+ if(complain)
+ {
+ if(did_stdin==1)
+ fprintf(complain,"gpg: WARNING: standard input reopened\n");
+ if(did_stdout==1)
+ fprintf(complain,"gpg: WARNING: standard output reopened\n");
+ if(did_stderr==1)
+ fprintf(complain,"gpg: WARNING: standard error reopened\n");
+
+ if(did_stdin==2 || did_stdout==2 || did_stderr==2)
+ fprintf(complain,"gpg: fatal: unable to reopen standard input,"
+ " output, or error\n");
+ }
+
+ if(did_stdin==2 || did_stdout==2 || did_stderr==2)
+ exit(3);
+#endif /* HAVE_STAT && !HAVE_W32_SYSTEM */
+}
+
+/* Pack an s2k iteration count into the form specified in 2440. If
+ we're in between valid values, round up. */
+static unsigned char
+encode_s2k_iterations(int iterations)
+{
+ unsigned char c=0,result;
+ unsigned int count;
+
+ if(iterations<=1024)
+ return 0;
+
+ if(iterations>=65011712)
+ return 255;
+
+ /* Need count to be in the range 16-31 */
+ for(count=iterations>>6;count>=32;count>>=1)
+ c++;
+
+ result=(c<<4)|(count-16);
+
+ if(S2K_DECODE_COUNT(result)<iterations)
+ result++;
+
+ return result;
+}
+
+int
+main (int argc, char **argv )
+{
+ ARGPARSE_ARGS pargs;
+ IOBUF a;
+ int rc=0;
+ int orig_argc;
+ char **orig_argv;
+ const char *fname;
+ char *username;
+ int may_coredump;
+ STRLIST sl, remusr= NULL, locusr=NULL;
+ STRLIST nrings=NULL, sec_nrings=NULL;
+ armor_filter_context_t afx;
+ int detached_sig = 0;
+ FILE *configfp = NULL;
+ char *configname = NULL;
+ char *save_configname = NULL;
+ unsigned configlineno;
+ int parse_debug = 0;
+ int default_config = 1;
+ int default_keyring = 1;
+ int greeting = 0;
+ int nogreeting = 0;
+ int use_random_seed = 1;
+ enum cmd_and_opt_values cmd = 0;
+ const char *trustdb_name = NULL;
+ char *def_cipher_string = NULL;
+ char *def_digest_string = NULL;
+ char *compress_algo_string = NULL;
+ char *cert_digest_string = NULL;
+ char *s2k_cipher_string = NULL;
+ char *s2k_digest_string = NULL;
+ char *pers_cipher_list = NULL;
+ char *pers_digest_list = NULL;
+ char *pers_compress_list = NULL;
+ int eyes_only=0;
+ int multifile=0;
+ int pwfd = -1;
+ int with_fpr = 0; /* make an option out of --fingerprint */
+ int any_explicit_recipient = 0;
+ int require_secmem=0,got_secmem=0;
+#ifdef USE_SHM_COPROCESSING
+ ulong requested_shm_size=0;
+#endif
+
+#ifdef __riscos__
+ opt.lock_once = 1;
+#endif /* __riscos__ */
+
+ reopen_std();
+ trap_unaligned();
+ secmem_set_flags( secmem_get_flags() | 2 ); /* suspend warnings */
+ /* Please note that we may running SUID(ROOT), so be very CAREFUL
+ * when adding any stuff between here and the call to
+ * secmem_init() somewhere after the option parsing
+ */
+ log_set_name("gpg");
+ secure_randoxmalloc(); /* put random number into secure memory */
+ may_coredump = disable_core_dumps();
+ init_signals();
+ create_dotlock(NULL); /* register locking cleanup */
+ i18n_init();
+ opt.command_fd = -1; /* no command fd */
+ opt.compress_level = -1; /* defaults to standard compress level */
+ opt.bz2_compress_level = -1; /* defaults to standard compress level */
+ /* note: if you change these lines, look at oOpenPGP */
+ opt.def_cipher_algo = 0;
+ opt.def_digest_algo = 0;
+ opt.cert_digest_algo = 0;
+ opt.compress_algo = -1; /* defaults to DEFAULT_COMPRESS_ALGO */
+ opt.s2k_mode = 3; /* iterated+salted */
+ opt.s2k_count = 96; /* 65536 iterations */
+#ifdef USE_CAST5
+ opt.s2k_cipher_algo = CIPHER_ALGO_CAST5;
+#else
+ opt.s2k_cipher_algo = CIPHER_ALGO_3DES;
+#endif
+ opt.completes_needed = 1;
+ opt.marginals_needed = 3;
+ opt.max_cert_depth = 5;
+ opt.pgp2_workarounds = 1;
+ opt.force_v3_sigs = 1;
+ opt.escape_from = 1;
+ opt.import_options=IMPORT_SK2PK;
+ opt.export_options=EXPORT_ATTRIBUTES;
+ opt.keyserver_options.import_options=IMPORT_REPAIR_PKS_SUBKEY_BUG;
+ opt.keyserver_options.export_options=EXPORT_ATTRIBUTES;
+ opt.keyserver_options.options=
+ KEYSERVER_HONOR_KEYSERVER_URL|KEYSERVER_HONOR_PKA_RECORD;
+ opt.verify_options=
+ VERIFY_SHOW_POLICY_URLS|VERIFY_SHOW_STD_NOTATIONS|VERIFY_SHOW_KEYSERVER_URLS;
+ opt.trust_model=TM_AUTO;
+ opt.mangle_dos_filenames=0;
+ opt.min_cert_level=2;
+ set_screen_dimensions();
+ opt.keyid_format=KF_SHORT;
+ opt.rfc2440_text=1;
+ opt.def_sig_expire="0";
+ opt.def_cert_expire="0";
+ set_homedir ( default_homedir () );
+ opt.passwd_repeat=1;
+
+#ifdef ENABLE_CARD_SUPPORT
+#if defined(_WIN32) || defined(__CYGWIN__)
+ opt.pcsc_driver = "winscard.dll";
+#elif defined(__APPLE__)
+ opt.pcsc_driver = "/System/Library/Frameworks/PCSC.framework/PCSC";
+#elif defined(__GLIBC__)
+ opt.pcsc_driver = "libpcsclite.so.1";
+#else
+ opt.pcsc_driver = "libpcsclite.so";
+#endif
+#endif /*ENABLE_CARD_SUPPORT*/
+
+ /* check whether we have a config file on the commandline */
+ orig_argc = argc;
+ orig_argv = argv;
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags= 1|(1<<6); /* do not remove the args, ignore version */
+ while( arg_parse( &pargs, opts) ) {
+ if( pargs.r_opt == oDebug || pargs.r_opt == oDebugAll )
+ parse_debug++;
+ else if( pargs.r_opt == oOptions ) {
+ /* yes there is one, so we do not try the default one, but
+ * read the option file when it is encountered at the commandline
+ */
+ default_config = 0;
+ }
+ else if( pargs.r_opt == oNoOptions )
+ default_config = 0; /* --no-options */
+ else if( pargs.r_opt == oHomedir )
+ set_homedir ( pargs.r.ret_str );
+ else if( pargs.r_opt == oNoPermissionWarn )
+ opt.no_perm_warn=1;
+ else if (pargs.r_opt == oStrict )
+ {
+ opt.strict=1;
+ log_set_strict(1);
+ }
+ else if (pargs.r_opt == oNoStrict )
+ {
+ opt.strict=0;
+ log_set_strict(0);
+ }
+#ifdef USE_SHM_COPROCESSING
+ else if( pargs.r_opt == oRunAsShmCP ) {
+ /* does not make sense in a options file, we do it here,
+ * so that we are the able to drop setuid as soon as possible */
+ opt.shm_coprocess = 1;
+ requested_shm_size = pargs.r.ret_ulong;
+ }
+ else if ( pargs.r_opt == oStatusFD ) {
+ /* this is needed to ensure that the status-fd filedescriptor is
+ * initialized when init_shm_coprocessing() is called */
+ set_status_fd( iobuf_translate_file_handle (pargs.r.ret_int, 1) );
+ }
+#endif
+ }
+
+#ifdef HAVE_DOSISH_SYSTEM
+ if ( strchr (opt.homedir,'\\') ) {
+ char *d, *buf = xmalloc (strlen (opt.homedir)+1);
+ const char *s = opt.homedir;
+ for (d=buf,s=opt.homedir; *s; s++)
+ {
+ *d++ = *s == '\\'? '/': *s;
+#ifdef HAVE_W32_SYSTEM
+ if (s[1] && IsDBCSLeadByte (*s))
+ *d++ = *++s;
+#endif
+ }
+ *d = 0;
+ set_homedir (buf);
+ }
+#endif
+#ifdef USE_SHM_COPROCESSING
+ if( opt.shm_coprocess ) {
+ init_shm_coprocessing(requested_shm_size, 1 );
+ }
+#endif
+ /* initialize the secure memory. */
+ got_secmem=secmem_init( 32768 );
+ maybe_setuid = 0;
+ /* Okay, we are now working under our real uid */
+
+#if defined(HAVE_GETUID) && defined(HAVE_GETEUID)
+ /* There should be no way to get to this spot while still carrying
+ setuid privs. Just in case, bomb out if we are. */
+ if(getuid()!=geteuid())
+ BUG();
+#endif
+
+ set_native_charset (NULL); /* Try to auto set the character set */
+
+ /* Try for a version specific config file first */
+ if( default_config )
+ {
+ char *name=xstrdup("gpg" EXTSEP_S "conf-" SAFE_VERSION);
+ char *ver=&name[strlen("gpg" EXTSEP_S "conf-")];
+
+ do
+ {
+ if(configname)
+ {
+ char *tok;
+
+ xfree(configname);
+ configname=NULL;
+
+ if((tok=strrchr(ver,SAFE_VERSION_DASH)))
+ *tok='\0';
+ else if((tok=strrchr(ver,SAFE_VERSION_DOT)))
+ *tok='\0';
+ else
+ break;
+ }
+
+ configname = make_filename(opt.homedir,name,NULL);
+ }
+ while(access(configname,R_OK));
+
+ xfree(name);
+
+ if(!configname)
+ configname=make_filename(opt.homedir, "gpg" EXTSEP_S "conf", NULL );
+ if (!access (configname, R_OK))
+ { /* Print a warning when both config files are present. */
+ char *p = make_filename(opt.homedir, "options", NULL );
+ if (!access (p, R_OK))
+ log_info (_("NOTE: old default options file `%s' ignored\n"), p);
+ xfree (p);
+ }
+ else
+ { /* Keep on using the old default one. */
+ xfree (configname);
+ configname = make_filename(opt.homedir, "options", NULL );
+ }
+ }
+ argc = orig_argc;
+ argv = orig_argv;
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags= 1; /* do not remove the args */
+
+ /* By this point we have a homedir, and cannot change it. */
+ check_permissions(opt.homedir,0);
+
+ next_pass:
+ if( configname ) {
+ if(check_permissions(configname,1))
+ {
+ /* If any options file is unsafe, then disable any external
+ programs for keyserver calls or photo IDs. Since the
+ external program to call is set in the options file, a
+ unsafe options file can lead to an arbitrary program
+ being run. */
+
+ opt.exec_disable=1;
+ }
+
+ configlineno = 0;
+ configfp = fopen( configname, "r" );
+ if (configfp && is_secured_file (fileno (configfp)))
+ {
+ fclose (configfp);
+ configfp = NULL;
+ errno = EPERM;
+ }
+ if( !configfp ) {
+ if( default_config ) {
+ if( parse_debug )
+ log_info(_("NOTE: no default option file `%s'\n"),
+ configname );
+ }
+ else {
+ log_error(_("option file `%s': %s\n"),
+ configname, strerror(errno) );
+ g10_exit(2);
+ }
+ xfree(configname); configname = NULL;
+ }
+ if( parse_debug && configname )
+ log_info(_("reading options from `%s'\n"), configname );
+ default_config = 0;
+ }
+
+ while( optfile_parse( configfp, configname, &configlineno,
+ &pargs, opts) )
+ {
+ switch( pargs.r_opt )
+ {
+ case aCheckKeys:
+ case aListConfig:
+ case aGPGConfList:
+ case aGPGConfTest:
+ case aListPackets:
+ case aImport:
+ case aFastImport:
+ case aSendKeys:
+ case aRecvKeys:
+ case aSearchKeys:
+ case aRefreshKeys:
+ case aFetchKeys:
+ case aExport:
+ set_cmd (&cmd, pargs.r_opt);
+ break;
+ case aListKeys: set_cmd( &cmd, aListKeys); break;
+ case aListSigs: set_cmd( &cmd, aListSigs); break;
+ case aExportSecret: set_cmd( &cmd, aExportSecret); break;
+ case aExportSecretSub: set_cmd( &cmd, aExportSecretSub); break;
+ case aDeleteSecretKeys:
+ set_cmd( &cmd, aDeleteSecretKeys);
+ greeting=1;
+ break;
+ case aDeleteSecretAndPublicKeys:
+ set_cmd( &cmd, aDeleteSecretAndPublicKeys);
+ greeting=1;
+ break;
+ case aDeleteKeys: set_cmd( &cmd, aDeleteKeys); greeting=1; break;
+
+ case aDetachedSign: detached_sig = 1; set_cmd( &cmd, aSign ); break;
+ case aSym: set_cmd( &cmd, aSym); break;
+
+ case aDecryptFiles: multifile=1; /* fall through */
+ case aDecrypt: set_cmd( &cmd, aDecrypt); break;
+
+ case aEncrFiles: multifile=1; /* fall through */
+ case aEncr: set_cmd( &cmd, aEncr); break;
+
+ case aVerifyFiles: multifile=1; /* fall through */
+ case aVerify: set_cmd( &cmd, aVerify); break;
+
+ case aSign: set_cmd( &cmd, aSign ); break;
+ case aKeygen: set_cmd( &cmd, aKeygen); greeting=1; break;
+ case aSignKey: set_cmd( &cmd, aSignKey); break;
+ case aLSignKey: set_cmd( &cmd, aLSignKey); break;
+ case aStore: set_cmd( &cmd, aStore); break;
+ case aEditKey: set_cmd( &cmd, aEditKey); greeting=1; break;
+ case aClearsign: set_cmd( &cmd, aClearsign); break;
+ case aGenRevoke: set_cmd( &cmd, aGenRevoke); break;
+ case aDesigRevoke: set_cmd( &cmd, aDesigRevoke); break;
+ case aPrimegen: set_cmd( &cmd, aPrimegen); break;
+ case aGenRandom: set_cmd( &cmd, aGenRandom); break;
+ case aPrintMD: set_cmd( &cmd, aPrintMD); break;
+ case aPrintMDs: set_cmd( &cmd, aPrintMDs); break;
+ case aListTrustDB: set_cmd( &cmd, aListTrustDB); break;
+ case aCheckTrustDB: set_cmd( &cmd, aCheckTrustDB); break;
+ case aUpdateTrustDB: set_cmd( &cmd, aUpdateTrustDB); break;
+ case aFixTrustDB: set_cmd( &cmd, aFixTrustDB); break;
+ case aListTrustPath: set_cmd( &cmd, aListTrustPath); break;
+ case aDeArmor: set_cmd( &cmd, aDeArmor); break;
+ case aEnArmor: set_cmd( &cmd, aEnArmor); break;
+ case aListOwnerTrust:
+ deprecated_warning(configname,configlineno,
+ "--list-ownertrust","--export-ownertrust","");
+ case aExportOwnerTrust: set_cmd( &cmd, aExportOwnerTrust); break;
+ case aImportOwnerTrust: set_cmd( &cmd, aImportOwnerTrust); break;
+ case aPipeMode:
+ deprecated_command ("--pipemode");
+ set_cmd( &cmd, aPipeMode);
+ break;
+
+ case aRebuildKeydbCaches: set_cmd( &cmd, aRebuildKeydbCaches); break;
+
+#ifdef ENABLE_CARD_SUPPORT
+ case aCardStatus: set_cmd (&cmd, aCardStatus); break;
+ case aCardEdit: set_cmd (&cmd, aCardEdit); break;
+ case aChangePIN: set_cmd (&cmd, aChangePIN); break;
+ case oReaderPort:
+ card_set_reader_port (pargs.r.ret_str);
+ break;
+ case octapiDriver: opt.ctapi_driver = pargs.r.ret_str; break;
+ case opcscDriver: opt.pcsc_driver = pargs.r.ret_str; break;
+ case oDisableCCID: opt.disable_ccid = 1; break;
+#endif /* ENABLE_CARD_SUPPORT*/
+
+ case oArmor: opt.armor = 1; opt.no_armor=0; break;
+ case oOutput: opt.outfile = pargs.r.ret_str; break;
+ case oMaxOutput: opt.max_output = pargs.r.ret_ulong; break;
+ case oQuiet: opt.quiet = 1; break;
+ case oNoTTY: tty_no_terminal(1); break;
+ case oDryRun: opt.dry_run = 1; break;
+ case oInteractive: opt.interactive = 1; break;
+ case oVerbose:
+ g10_opt_verbose++;
+ opt.verbose++;
+ opt.list_options|=LIST_SHOW_UNUSABLE_UIDS;
+ opt.list_options|=LIST_SHOW_UNUSABLE_SUBKEYS;
+ break;
+ case oKOption: set_cmd( &cmd, aKMode ); break;
+
+ case oBatch: opt.batch = 1; nogreeting = 1; break;
+ case oUseAgent:
+#ifndef __riscos__
+ opt.use_agent = 1;
+#else /* __riscos__ */
+ opt.use_agent = 0;
+ riscos_not_implemented("use-agent");
+#endif /* __riscos__ */
+ break;
+ case oNoUseAgent: opt.use_agent = 0; break;
+ case oGpgAgentInfo: opt.gpg_agent_info = pargs.r.ret_str; break;
+ case oAnswerYes: opt.answer_yes = 1; break;
+ case oAnswerNo: opt.answer_no = 1; break;
+ case oKeyring: append_to_strlist( &nrings, pargs.r.ret_str); break;
+ case oPrimaryKeyring:
+ sl=append_to_strlist( &nrings, pargs.r.ret_str);
+ sl->flags=2;
+ break;
+ case oShowKeyring:
+ deprecated_warning(configname,configlineno,"--show-keyring",
+ "--list-options ","show-keyring");
+ opt.list_options|=LIST_SHOW_KEYRING;
+ break;
+ case oDebug: opt.debug |= pargs.r.ret_ulong; break;
+ case oDebugAll: opt.debug = ~0; break;
+ case oDebugCCIDDriver:
+#if defined(ENABLE_CARD_SUPPORT) && defined(HAVE_LIBUSB)
+ ccid_set_debug_level (ccid_set_debug_level (1)+1);
+#endif
+ break;
+ case oStatusFD:
+ set_status_fd( iobuf_translate_file_handle (pargs.r.ret_int, 1) );
+ break;
+ case oStatusFile:
+ set_status_fd ( open_info_file (pargs.r.ret_str, 1) );
+ break;
+ case oAttributeFD:
+ set_attrib_fd(iobuf_translate_file_handle (pargs.r.ret_int, 1));
+ break;
+ case oAttributeFile:
+ set_attrib_fd ( open_info_file (pargs.r.ret_str, 1) );
+ break;
+ case oLoggerFD:
+ log_set_logfile( NULL,
+ iobuf_translate_file_handle (pargs.r.ret_int, 1));
+ break;
+ case oLoggerFile:
+ log_set_logfile( NULL, open_info_file (pargs.r.ret_str, 1) );
+ break;
+
+ case oWithFingerprint:
+ opt.with_fingerprint = 1;
+ with_fpr=1; /*fall thru*/
+ case oFingerprint: opt.fingerprint++; break;
+ case oSecretKeyring:
+ append_to_strlist( &sec_nrings, pargs.r.ret_str);
+ break;
+ case oOptions:
+ /* config files may not be nested (silently ignore them) */
+ if( !configfp ) {
+ xfree(configname);
+ configname = xstrdup(pargs.r.ret_str);
+ goto next_pass;
+ }
+ break;
+ case oNoArmor: opt.no_armor=1; opt.armor=0; break;
+ case oNoDefKeyring: default_keyring = 0; break;
+ case oNoGreeting: nogreeting = 1; break;
+ case oNoVerbose: g10_opt_verbose = 0;
+ opt.verbose = 0; opt.list_sigs=0; break;
+ case oQuickRandom: quick_random_gen(1); break;
+ case oEmitVersion: opt.no_version=0; break;
+ case oNoEmitVersion: opt.no_version=1; break;
+ case oCompletesNeeded: opt.completes_needed = pargs.r.ret_int; break;
+ case oMarginalsNeeded: opt.marginals_needed = pargs.r.ret_int; break;
+ case oMaxCertDepth: opt.max_cert_depth = pargs.r.ret_int; break;
+ case oTrustDBName: trustdb_name = pargs.r.ret_str; break;
+ case oDefaultKey: opt.def_secret_key = pargs.r.ret_str; break;
+ case oDefRecipient:
+ if( *pargs.r.ret_str )
+ opt.def_recipient = make_username(pargs.r.ret_str);
+ break;
+ case oDefRecipientSelf:
+ xfree(opt.def_recipient); opt.def_recipient = NULL;
+ opt.def_recipient_self = 1;
+ break;
+ case oNoDefRecipient:
+ xfree(opt.def_recipient); opt.def_recipient = NULL;
+ opt.def_recipient_self = 0;
+ break;
+ case oNoOptions: opt.no_homedir_creation = 1; break; /* no-options */
+ case oHomedir: break;
+ case oNoBatch: opt.batch = 0; break;
+ case oWithKeyData: opt.with_key_data=1; /* fall thru */
+ case oWithColons: opt.with_colons=':'; break;
+
+ case oSkipVerify: opt.skip_verify=1; break;
+ case oCompressKeys: opt.compress_keys = 1; break;
+ case aListSecretKeys: set_cmd( &cmd, aListSecretKeys); break;
+ /* There are many programs (like mutt) that call gpg with
+ --always-trust so keep this option around for a long
+ time. */
+ case oAlwaysTrust: opt.trust_model=TM_ALWAYS; break;
+ case oTrustModel:
+ parse_trust_model(pargs.r.ret_str);
+ break;
+ case oForceOwnertrust:
+ log_info(_("NOTE: %s is not for normal use!\n"),
+ "--force-ownertrust");
+ opt.force_ownertrust=string_to_trust_value(pargs.r.ret_str);
+ if(opt.force_ownertrust==-1)
+ {
+ log_error("invalid ownertrust `%s'\n",pargs.r.ret_str);
+ opt.force_ownertrust=0;
+ }
+ break;
+ case oLoadExtension:
+#ifndef __riscos__
+#if defined(USE_DYNAMIC_LINKING) || defined(_WIN32)
+ if(check_permissions(pargs.r.ret_str,2))
+ log_info(_("cipher extension `%s' not loaded due to"
+ " unsafe permissions\n"),pargs.r.ret_str);
+ else
+ register_cipher_extension(orig_argc? *orig_argv:NULL,
+ pargs.r.ret_str);
+#endif
+#else /* __riscos__ */
+ riscos_not_implemented("load-extension");
+#endif /* __riscos__ */
+ break;
+ case oRFC1991:
+ opt.compliance = CO_RFC1991;
+ opt.force_v4_certs = 0;
+ opt.escape_from = 1;
+ break;
+ case oOpenPGP:
+ case oRFC2440:
+ /* TODO: When 2440bis becomes a RFC, set new values for
+ oOpenPGP. */
+ opt.rfc2440_text=1;
+ opt.compliance = CO_RFC2440;
+ opt.allow_non_selfsigned_uid = 1;
+ opt.allow_freeform_uid = 1;
+ opt.pgp2_workarounds = 0;
+ opt.escape_from = 0;
+ opt.force_v3_sigs = 0;
+ opt.compress_keys = 0; /* not mandated, but we do it */
+ opt.compress_sigs = 0; /* ditto. */
+ opt.not_dash_escaped = 0;
+ opt.def_cipher_algo = 0;
+ opt.def_digest_algo = 0;
+ opt.cert_digest_algo = 0;
+ opt.compress_algo = -1;
+ opt.s2k_mode = 3; /* iterated+salted */
+ opt.s2k_digest_algo = DIGEST_ALGO_SHA1;
+ opt.s2k_cipher_algo = CIPHER_ALGO_3DES;
+ break;
+ case oPGP2: opt.compliance = CO_PGP2; break;
+ case oPGP6: opt.compliance = CO_PGP6; break;
+ case oPGP7: opt.compliance = CO_PGP7; break;
+ case oPGP8: opt.compliance = CO_PGP8; break;
+ case oGnuPG: opt.compliance = CO_GNUPG; break;
+ case oCompressSigs: opt.compress_sigs = 1; break;
+ case oRFC2440Text: opt.rfc2440_text=1; break;
+ case oNoRFC2440Text: opt.rfc2440_text=0; break;
+ case oRunAsShmCP:
+#ifndef __riscos__
+# ifndef USE_SHM_COPROCESSING
+ /* not possible in the option file,
+ * but we print the warning here anyway */
+ log_error("shared memory coprocessing is not available\n");
+# endif
+#else /* __riscos__ */
+ riscos_not_implemented("run-as-shm-coprocess");
+#endif /* __riscos__ */
+ break;
+ case oSetFilename:
+ if(utf8_strings)
+ opt.set_filename = pargs.r.ret_str;
+ else
+ opt.set_filename = native_to_utf8(pargs.r.ret_str);
+ break;
+ case oForYourEyesOnly: eyes_only = 1; break;
+ case oNoForYourEyesOnly: eyes_only = 0; break;
+ case oSetPolicyURL:
+ add_policy_url(pargs.r.ret_str,0);
+ add_policy_url(pargs.r.ret_str,1);
+ break;
+ case oSigPolicyURL: add_policy_url(pargs.r.ret_str,0); break;
+ case oCertPolicyURL: add_policy_url(pargs.r.ret_str,1); break;
+ case oShowPolicyURL:
+ deprecated_warning(configname,configlineno,"--show-policy-url",
+ "--list-options ","show-policy-urls");
+ deprecated_warning(configname,configlineno,"--show-policy-url",
+ "--verify-options ","show-policy-urls");
+ opt.list_options|=LIST_SHOW_POLICY_URLS;
+ opt.verify_options|=VERIFY_SHOW_POLICY_URLS;
+ break;
+ case oNoShowPolicyURL:
+ deprecated_warning(configname,configlineno,"--no-show-policy-url",
+ "--list-options ","no-show-policy-urls");
+ deprecated_warning(configname,configlineno,"--no-show-policy-url",
+ "--verify-options ","no-show-policy-urls");
+ opt.list_options&=~LIST_SHOW_POLICY_URLS;
+ opt.verify_options&=~VERIFY_SHOW_POLICY_URLS;
+ break;
+ case oSigKeyserverURL: add_keyserver_url(pargs.r.ret_str,0); break;
+ case oUseEmbeddedFilename:
+ opt.flags.use_embedded_filename=1;
+ break;
+ case oNoUseEmbeddedFilename:
+ opt.flags.use_embedded_filename=0;
+ break;
+ case oComment:
+ if(pargs.r.ret_str[0])
+ append_to_strlist(&opt.comments,pargs.r.ret_str);
+ break;
+ case oDefaultComment:
+ deprecated_warning(configname,configlineno,
+ "--default-comment","--no-comments","");
+ /* fall through */
+ case oNoComments:
+ free_strlist(opt.comments);
+ opt.comments=NULL;
+ break;
+ case oThrowKeyids: opt.throw_keyid = 1; break;
+ case oNoThrowKeyids: opt.throw_keyid = 0; break;
+ case oShowPhotos:
+ deprecated_warning(configname,configlineno,"--show-photos",
+ "--list-options ","show-photos");
+ deprecated_warning(configname,configlineno,"--show-photos",
+ "--verify-options ","show-photos");
+ opt.list_options|=LIST_SHOW_PHOTOS;
+ opt.verify_options|=VERIFY_SHOW_PHOTOS;
+ break;
+ case oNoShowPhotos:
+ deprecated_warning(configname,configlineno,"--no-show-photos",
+ "--list-options ","no-show-photos");
+ deprecated_warning(configname,configlineno,"--no-show-photos",
+ "--verify-options ","no-show-photos");
+ opt.list_options&=~LIST_SHOW_PHOTOS;
+ opt.verify_options&=~VERIFY_SHOW_PHOTOS;
+ break;
+ case oPhotoViewer: opt.photo_viewer = pargs.r.ret_str; break;
+ case oForceV3Sigs: opt.force_v3_sigs = 1; break;
+ case oNoForceV3Sigs: opt.force_v3_sigs = 0; break;
+ case oForceV4Certs: opt.force_v4_certs = 1; break;
+ case oNoForceV4Certs: opt.force_v4_certs = 0; break;
+ case oForceMDC: opt.force_mdc = 1; break;
+ case oNoForceMDC: opt.force_mdc = 0; break;
+ case oDisableMDC: opt.disable_mdc = 1; break;
+ case oNoDisableMDC: opt.disable_mdc = 0; break;
+ case oS2KMode: opt.s2k_mode = pargs.r.ret_int; break;
+ case oS2KDigest: s2k_digest_string = xstrdup(pargs.r.ret_str); break;
+ case oS2KCipher: s2k_cipher_string = xstrdup(pargs.r.ret_str); break;
+ case oS2KCount:
+ opt.s2k_count=encode_s2k_iterations(pargs.r.ret_int);
+ break;
+ case oSimpleSKChecksum: opt.simple_sk_checksum = 1; break;
+ case oNoEncryptTo: opt.no_encrypt_to = 1; break;
+ case oEncryptTo: /* store the recipient in the second list */
+ sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
+ sl->flags = 1;
+ break;
+ case oHiddenEncryptTo: /* store the recipient in the second list */
+ sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
+ sl->flags = 1|2;
+ break;
+ case oRecipient: /* store the recipient */
+ add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
+ any_explicit_recipient = 1;
+ break;
+ case oHiddenRecipient: /* store the recipient with a flag */
+ sl = add_to_strlist2( &remusr, pargs.r.ret_str, utf8_strings );
+ sl->flags = 2;
+ any_explicit_recipient = 1;
+ break;
+ case oTextmodeShort: opt.textmode = 2; break;
+ case oTextmode: opt.textmode=1; break;
+ case oNoTextmode: opt.textmode=0; break;
+ case oExpert: opt.expert = 1; break;
+ case oNoExpert: opt.expert = 0; break;
+ case oDefSigExpire:
+ if(*pargs.r.ret_str!='\0')
+ {
+ if(parse_expire_string(pargs.r.ret_str)==(u32)-1)
+ log_error(_("`%s' is not a valid signature expiration\n"),
+ pargs.r.ret_str);
+ else
+ opt.def_sig_expire=pargs.r.ret_str;
+ }
+ break;
+ case oAskSigExpire: opt.ask_sig_expire = 1; break;
+ case oNoAskSigExpire: opt.ask_sig_expire = 0; break;
+ case oDefCertExpire:
+ if(*pargs.r.ret_str!='\0')
+ {
+ if(parse_expire_string(pargs.r.ret_str)==(u32)-1)
+ log_error(_("`%s' is not a valid signature expiration\n"),
+ pargs.r.ret_str);
+ else
+ opt.def_cert_expire=pargs.r.ret_str;
+ }
+ break;
+ case oAskCertExpire: opt.ask_cert_expire = 1; break;
+ case oNoAskCertExpire: opt.ask_cert_expire = 0; break;
+ case oDefCertLevel: opt.def_cert_level=pargs.r.ret_int; break;
+ case oMinCertLevel: opt.min_cert_level=pargs.r.ret_int; break;
+ case oAskCertLevel: opt.ask_cert_level = 1; break;
+ case oNoAskCertLevel: opt.ask_cert_level = 0; break;
+ case oLocalUser: /* store the local users */
+ add_to_strlist2( &locusr, pargs.r.ret_str, utf8_strings );
+ break;
+ case oCompress:
+ /* this is the -z command line option */
+ opt.compress_level = opt.bz2_compress_level = pargs.r.ret_int;
+ break;
+ case oCompressLevel: opt.compress_level = pargs.r.ret_int; break;
+ case oBZ2CompressLevel: opt.bz2_compress_level = pargs.r.ret_int; break;
+ case oBZ2DecompressLowmem: opt.bz2_decompress_lowmem=1; break;
+ case oPasswd:
+ set_passphrase_from_string(pargs.r.ret_str);
+ break;
+ case oPasswdFD:
+ pwfd = iobuf_translate_file_handle (pargs.r.ret_int, 0);
+ opt.use_agent = 0;
+ break;
+ case oPasswdFile:
+ pwfd = open_info_file (pargs.r.ret_str, 0);
+ break;
+ case oPasswdRepeat: opt.passwd_repeat=pargs.r.ret_int; break;
+ case oCommandFD:
+ opt.command_fd = iobuf_translate_file_handle (pargs.r.ret_int, 0);
+ break;
+ case oCommandFile:
+ opt.command_fd = open_info_file (pargs.r.ret_str, 0);
+ break;
+ case oCipherAlgo:
+ def_cipher_string = xstrdup(pargs.r.ret_str);
+ break;
+ case oDigestAlgo:
+ def_digest_string = xstrdup(pargs.r.ret_str);
+ break;
+ case oCompressAlgo:
+ /* If it is all digits, stick a Z in front of it for
+ later. This is for backwards compatibility with
+ versions that took the compress algorithm number. */
+ {
+ char *pt=pargs.r.ret_str;
+ while(*pt)
+ {
+ if (!isascii (*pt) || !isdigit (*pt))
+ break;
+
+ pt++;
+ }
+
+ if(*pt=='\0')
+ {
+ compress_algo_string=xmalloc(strlen(pargs.r.ret_str)+2);
+ strcpy(compress_algo_string,"Z");
+ strcat(compress_algo_string,pargs.r.ret_str);
+ }
+ else
+ compress_algo_string = xstrdup(pargs.r.ret_str);
+ }
+ break;
+ case oCertDigestAlgo: cert_digest_string = xstrdup(pargs.r.ret_str); break;
+ case oNoSecmemWarn: secmem_set_flags( secmem_get_flags() | 1 ); break;
+ case oRequireSecmem: require_secmem=1; break;
+ case oNoRequireSecmem: require_secmem=0; break;
+ case oNoPermissionWarn: opt.no_perm_warn=1; break;
+ case oNoMDCWarn: opt.no_mdc_warn=1; break;
+ case oDisplayCharset:
+ if( set_native_charset( pargs.r.ret_str ) )
+ log_error(_("`%s' is not a valid character set\n"),
+ pargs.r.ret_str);
+ break;
+ case oNotDashEscaped: opt.not_dash_escaped = 1; break;
+ case oEscapeFrom: opt.escape_from = 1; break;
+ case oNoEscapeFrom: opt.escape_from = 0; break;
+ case oLockOnce: opt.lock_once = 1; break;
+ case oLockNever:
+ disable_dotlock ();
+ random_disable_locking ();
+ break;
+ case oLockMultiple:
+#ifndef __riscos__
+ opt.lock_once = 0;
+#else /* __riscos__ */
+ riscos_not_implemented("lock-multiple");
+#endif /* __riscos__ */
+ break;
+ case oKeyServer:
+ {
+ struct keyserver_spec *keyserver;
+ keyserver=parse_keyserver_uri(pargs.r.ret_str,0,
+ configname,configlineno);
+ if(!keyserver)
+ log_error(_("could not parse keyserver URL\n"));
+ else
+ {
+ keyserver->next=opt.keyserver;
+ opt.keyserver=keyserver;
+ }
+ }
+ break;
+ case oKeyServerOptions:
+ if(!parse_keyserver_options(pargs.r.ret_str))
+ {
+ if(configname)
+ log_error(_("%s:%d: invalid keyserver options\n"),
+ configname,configlineno);
+ else
+ log_error(_("invalid keyserver options\n"));
+ }
+ break;
+ case oImportOptions:
+ if(!parse_import_options(pargs.r.ret_str,&opt.import_options,1))
+ {
+ if(configname)
+ log_error(_("%s:%d: invalid import options\n"),
+ configname,configlineno);
+ else
+ log_error(_("invalid import options\n"));
+ }
+ break;
+ case oExportOptions:
+ if(!parse_export_options(pargs.r.ret_str,&opt.export_options,1))
+ {
+ if(configname)
+ log_error(_("%s:%d: invalid export options\n"),
+ configname,configlineno);
+ else
+ log_error(_("invalid export options\n"));
+ }
+ break;
+ case oListOptions:
+ if(!parse_list_options(pargs.r.ret_str))
+ {
+ if(configname)
+ log_error(_("%s:%d: invalid list options\n"),
+ configname,configlineno);
+ else
+ log_error(_("invalid list options\n"));
+ }
+ break;
+ case oVerifyOptions:
+ {
+ struct parse_options vopts[]=
+ {
+ {"show-photos",VERIFY_SHOW_PHOTOS,NULL,
+ N_("display photo IDs during signature verification")},
+ {"show-policy-urls",VERIFY_SHOW_POLICY_URLS,NULL,
+ N_("show policy URLs during signature verification")},
+ {"show-notations",VERIFY_SHOW_NOTATIONS,NULL,
+ N_("show all notations during signature verification")},
+ {"show-std-notations",VERIFY_SHOW_STD_NOTATIONS,NULL,
+ N_("show IETF standard notations during signature verification")},
+ {"show-standard-notations",VERIFY_SHOW_STD_NOTATIONS,NULL,
+ NULL},
+ {"show-user-notations",VERIFY_SHOW_USER_NOTATIONS,NULL,
+ N_("show user-supplied notations during signature verification")},
+ {"show-keyserver-urls",VERIFY_SHOW_KEYSERVER_URLS,NULL,
+ N_("show preferred keyserver URLs during signature verification")},
+ {"show-uid-validity",VERIFY_SHOW_UID_VALIDITY,NULL,
+ N_("show user ID validity during signature verification")},
+ {"show-unusable-uids",VERIFY_SHOW_UNUSABLE_UIDS,NULL,
+ N_("show revoked and expired user IDs in signature verification")},
+ {"pka-lookups",VERIFY_PKA_LOOKUPS,NULL,
+ N_("validate signatures with PKA data")},
+ {"pka-trust-increase",VERIFY_PKA_TRUST_INCREASE,NULL,
+ N_("elevate the trust of signatures with valid PKA data")},
+ {NULL,0,NULL,NULL}
+ };
+
+ if(!parse_options(pargs.r.ret_str,&opt.verify_options,vopts,1))
+ {
+ if(configname)
+ log_error(_("%s:%d: invalid verify options\n"),
+ configname,configlineno);
+ else
+ log_error(_("invalid verify options\n"));
+ }
+ }
+ break;
+ case oTempDir: opt.temp_dir=pargs.r.ret_str; break;
+ case oExecPath:
+ if(set_exec_path(pargs.r.ret_str))
+ log_error(_("unable to set exec-path to %s\n"),pargs.r.ret_str);
+ else
+ opt.exec_path_set=1;
+ break;
+ case oSetNotation:
+ add_notation_data( pargs.r.ret_str, 0 );
+ add_notation_data( pargs.r.ret_str, 1 );
+ break;
+ case oSigNotation: add_notation_data( pargs.r.ret_str, 0 ); break;
+ case oCertNotation: add_notation_data( pargs.r.ret_str, 1 ); break;
+ case oShowNotation:
+ deprecated_warning(configname,configlineno,"--show-notation",
+ "--list-options ","show-notations");
+ deprecated_warning(configname,configlineno,"--show-notation",
+ "--verify-options ","show-notations");
+ opt.list_options|=LIST_SHOW_NOTATIONS;
+ opt.verify_options|=VERIFY_SHOW_NOTATIONS;
+ break;
+ case oNoShowNotation:
+ deprecated_warning(configname,configlineno,"--no-show-notation",
+ "--list-options ","no-show-notations");
+ deprecated_warning(configname,configlineno,"--no-show-notation",
+ "--verify-options ","no-show-notations");
+ opt.list_options&=~LIST_SHOW_NOTATIONS;
+ opt.verify_options&=~VERIFY_SHOW_NOTATIONS;
+ break;
+ case oUtf8Strings: utf8_strings = 1; break;
+ case oNoUtf8Strings: utf8_strings = 0; break;
+ case oDisableCipherAlgo:
+ disable_cipher_algo( string_to_cipher_algo(pargs.r.ret_str) );
+ break;
+ case oDisablePubkeyAlgo:
+ disable_pubkey_algo( string_to_pubkey_algo(pargs.r.ret_str) );
+ break;
+ case oNoSigCache: opt.no_sig_cache = 1; break;
+ case oNoSigCreateCheck: opt.no_sig_create_check = 1; break;
+ case oAllowNonSelfsignedUID: opt.allow_non_selfsigned_uid = 1; break;
+ case oNoAllowNonSelfsignedUID: opt.allow_non_selfsigned_uid=0; break;
+ case oAllowFreeformUID: opt.allow_freeform_uid = 1; break;
+ case oNoAllowFreeformUID: opt.allow_freeform_uid = 0; break;
+ case oNoLiteral: opt.no_literal = 1; break;
+ case oSetFilesize: opt.set_filesize = pargs.r.ret_ulong; break;
+ case oHonorHttpProxy:
+ add_to_strlist(&opt.keyserver_options.other,"http-proxy");
+ deprecated_warning(configname,configlineno,
+ "--honor-http-proxy",
+ "--keyserver-options ","http-proxy");
+ break;
+ case oFastListMode: opt.fast_list_mode = 1; break;
+ case oFixedListMode: opt.fixed_list_mode = 1; break;
+ case oListOnly: opt.list_only=1; break;
+ case oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break;
+ case oIgnoreValidFrom: opt.ignore_valid_from = 1; break;
+ case oIgnoreCrcError: opt.ignore_crc_error = 1; break;
+ case oIgnoreMDCError: opt.ignore_mdc_error = 1; break;
+ case oNoRandomSeedFile: use_random_seed = 0; break;
+ case oAutoKeyRetrieve:
+ case oNoAutoKeyRetrieve:
+ if(pargs.r_opt==oAutoKeyRetrieve)
+ opt.keyserver_options.options|=KEYSERVER_AUTO_KEY_RETRIEVE;
+ else
+ opt.keyserver_options.options&=~KEYSERVER_AUTO_KEY_RETRIEVE;
+
+ deprecated_warning(configname,configlineno,
+ pargs.r_opt==oAutoKeyRetrieve?"--auto-key-retrieve":
+ "--no-auto-key-retrieve","--keyserver-options ",
+ pargs.r_opt==oAutoKeyRetrieve?"auto-key-retrieve":
+ "no-auto-key-retrieve");
+ break;
+ case oShowSessionKey: opt.show_session_key = 1; break;
+ case oOverrideSessionKey:
+ opt.override_session_key = pargs.r.ret_str;
+ break;
+ case oMergeOnly:
+ deprecated_warning(configname,configlineno,"--merge-only",
+ "--import-options ","merge-only");
+ opt.import_options|=IMPORT_MERGE_ONLY;
+ break;
+ case oAllowSecretKeyImport: /* obsolete */ break;
+ case oTryAllSecrets: opt.try_all_secrets = 1; break;
+ case oTrustedKey: register_trusted_key( pargs.r.ret_str ); break;
+ case oEnableSpecialFilenames:
+ iobuf_enable_special_filenames (1);
+ break;
+ case oNoExpensiveTrustChecks: opt.no_expensive_trust_checks=1; break;
+ case oAutoCheckTrustDB: opt.no_auto_check_trustdb=0; break;
+ case oNoAutoCheckTrustDB: opt.no_auto_check_trustdb=1; break;
+ case oPreservePermissions: opt.preserve_permissions=1; break;
+ case oDefaultPreferenceList:
+ opt.def_preference_list = pargs.r.ret_str;
+ break;
+ case oDefaultKeyserverURL:
+ {
+ struct keyserver_spec *keyserver;
+ keyserver=parse_keyserver_uri(pargs.r.ret_str,1,
+ configname,configlineno);
+ if(!keyserver)
+ log_error(_("could not parse keyserver URL\n"));
+ else
+ free_keyserver_spec(keyserver);
+
+ opt.def_keyserver_url = pargs.r.ret_str;
+ }
+ break;
+ case oPersonalCipherPreferences:
+ pers_cipher_list=pargs.r.ret_str;
+ break;
+ case oPersonalDigestPreferences:
+ pers_digest_list=pargs.r.ret_str;
+ break;
+ case oPersonalCompressPreferences:
+ pers_compress_list=pargs.r.ret_str;
+ break;
+ case oDisplay: opt.display = pargs.r.ret_str; break;
+ case oTTYname: opt.ttyname = pargs.r.ret_str; break;
+ case oTTYtype: opt.ttytype = pargs.r.ret_str; break;
+ case oLCctype: opt.lc_ctype = pargs.r.ret_str; break;
+ case oLCmessages: opt.lc_messages = pargs.r.ret_str; break;
+ case oGroup: add_group(pargs.r.ret_str); break;
+ case oUnGroup: rm_group(pargs.r.ret_str); break;
+ case oNoGroups:
+ while(opt.grouplist)
+ {
+ struct groupitem *iter=opt.grouplist;
+ free_strlist(iter->values);
+ opt.grouplist=opt.grouplist->next;
+ xfree(iter);
+ }
+ break;
+ case oStrict: opt.strict=1; log_set_strict(1); break;
+ case oNoStrict: opt.strict=0; log_set_strict(0); break;
+ case oMangleDosFilenames: opt.mangle_dos_filenames = 1; break;
+ case oNoMangleDosFilenames: opt.mangle_dos_filenames = 0; break;
+ case oEnableProgressFilter: opt.enable_progress_filter = 1; break;
+ case oMultifile: multifile=1; break;
+ case oKeyidFormat:
+ if(ascii_strcasecmp(pargs.r.ret_str,"short")==0)
+ opt.keyid_format=KF_SHORT;
+ else if(ascii_strcasecmp(pargs.r.ret_str,"long")==0)
+ opt.keyid_format=KF_LONG;
+ else if(ascii_strcasecmp(pargs.r.ret_str,"0xshort")==0)
+ opt.keyid_format=KF_0xSHORT;
+ else if(ascii_strcasecmp(pargs.r.ret_str,"0xlong")==0)
+ opt.keyid_format=KF_0xLONG;
+ else
+ log_error("unknown keyid-format `%s'\n",pargs.r.ret_str);
+ break;
+
+ case oExitOnStatusWriteError:
+ opt.exit_on_status_write_error = 1;
+ break;
+
+ case oLimitCardInsertTries:
+ opt.limit_card_insert_tries = pargs.r.ret_int;
+ break;
+
+ case oRequireCrossCert: opt.flags.require_cross_cert=1; break;
+ case oNoRequireCrossCert: opt.flags.require_cross_cert=0; break;
+
+ case oAutoKeyLocate:
+ if(!parse_auto_key_locate(pargs.r.ret_str))
+ {
+ if(configname)
+ log_error(_("%s:%d: invalid auto-key-locate list\n"),
+ configname,configlineno);
+ else
+ log_error(_("invalid auto-key-locate list\n"));
+ }
+ break;
+ case oNoAutoKeyLocate:
+ release_akl();
+ break;
+
+ case oAllowMultisigVerification:
+ opt.allow_multisig_verification = 1;
+ break;
+
+ case oEnableDSA2: opt.flags.dsa2=1; break;
+ case oDisableDSA2: opt.flags.dsa2=0; break;
+
+ case oNoop: break;
+
+ default : pargs.err = configfp? 1:2; break;
+ }
+ }
+
+
+ if( configfp ) {
+ fclose( configfp );
+ configfp = NULL;
+ /* Remember the first config file name. */
+ if (!save_configname)
+ save_configname = configname;
+ else
+ xfree(configname);
+ configname = NULL;
+ goto next_pass;
+ }
+ xfree( configname ); configname = NULL;
+ if( log_get_errorcount(0) )
+ g10_exit(2);
+
+ /* The command --gpgconf-list is pretty simple and may be called
+ directly after the option parsing. */
+ if (cmd == aGPGConfList)
+ {
+ gpgconf_list (save_configname);
+ g10_exit (0);
+ }
+ xfree (save_configname);
+
+ if( nogreeting )
+ greeting = 0;
+
+ if( greeting ) {
+ fprintf(stderr, "%s %s; %s\n",
+ strusage(11), strusage(13), strusage(14) );
+ fprintf(stderr, "%s\n", strusage(15) );
+ }
+#ifdef IS_DEVELOPMENT_VERSION
+ if( !opt.batch )
+ {
+ const char *s;
+
+ if((s=strusage(20)))
+ log_info("%s\n",s);
+ if((s=strusage(21)))
+ log_info("%s\n",s);
+ if((s=strusage(22)))
+ log_info("%s\n",s);
+ }
+#endif
+
+ if (opt.verbose > 2)
+ log_info ("using character set `%s'\n", get_native_charset ());
+
+ if( may_coredump && !opt.quiet )
+ log_info(_("WARNING: program may create a core file!\n"));
+
+ if (eyes_only) {
+ if (opt.set_filename)
+ log_info(_("WARNING: %s overrides %s\n"),
+ "--for-your-eyes-only","--set-filename");
+
+ opt.set_filename="_CONSOLE";
+ }
+
+ if (opt.no_literal) {
+ log_info(_("NOTE: %s is not for normal use!\n"), "--no-literal");
+ if (opt.textmode)
+ log_error(_("%s not allowed with %s!\n"),
+ "--textmode", "--no-literal" );
+ if (opt.set_filename)
+ log_error(_("%s makes no sense with %s!\n"),
+ eyes_only?"--for-your-eyes-only":"--set-filename",
+ "--no-literal" );
+ }
+
+#ifndef ENABLE_AGENT_SUPPORT
+ if (opt.use_agent) {
+ log_info(_("NOTE: %s is not available in this version\n"),
+ "--use-agent");
+ opt.use_agent = 0;
+ }
+#endif /*!ENABLE_AGENT_SUPPORT*/
+
+ if (opt.set_filesize)
+ log_info(_("NOTE: %s is not for normal use!\n"), "--set-filesize");
+ if( opt.batch )
+ tty_batchmode( 1 );
+
+ secmem_set_flags( secmem_get_flags() & ~2 ); /* resume warnings */
+
+ if(require_secmem && !got_secmem)
+ {
+ log_info(_("will not run with insecure memory due to %s\n"),
+ "--require-secmem");
+ g10_exit(2);
+ }
+
+ set_debug();
+
+ /* Do these after the switch(), so they can override settings. */
+ if(PGP2)
+ {
+ int unusable=0;
+
+ if(cmd==aSign && !detached_sig)
+ {
+ log_info(_("you can only make detached or clear signatures "
+ "while in --pgp2 mode\n"));
+ unusable=1;
+ }
+ else if(cmd==aSignEncr || cmd==aSignSym)
+ {
+ log_info(_("you can't sign and encrypt at the "
+ "same time while in --pgp2 mode\n"));
+ unusable=1;
+ }
+ else if(argc==0 && (cmd==aSign || cmd==aEncr || cmd==aSym))
+ {
+ log_info(_("you must use files (and not a pipe) when "
+ "working with --pgp2 enabled.\n"));
+ unusable=1;
+ }
+ else if(cmd==aEncr || cmd==aSym)
+ {
+ /* Everything else should work without IDEA (except using
+ a secret key encrypted with IDEA and setting an IDEA
+ preference, but those have their own error
+ messages). */
+
+ if(check_cipher_algo(CIPHER_ALGO_IDEA))
+ {
+ log_info(_("encrypting a message in --pgp2 mode requires "
+ "the IDEA cipher\n"));
+ idea_cipher_warn(1);
+ unusable=1;
+ }
+ else if(cmd==aSym)
+ {
+ /* This only sets IDEA for symmetric encryption
+ since it is set via select_algo_from_prefs for
+ pk encryption. */
+ xfree(def_cipher_string);
+ def_cipher_string = xstrdup("idea");
+ }
+
+ /* PGP2 can't handle the output from the textmode
+ filter, so we disable it for anything that could
+ create a literal packet (only encryption and
+ symmetric encryption, since we disable signing
+ above). */
+ if(!unusable)
+ opt.textmode=0;
+ }
+
+ if(unusable)
+ compliance_failure();
+ else
+ {
+ opt.force_v4_certs = 0;
+ opt.escape_from = 1;
+ opt.force_v3_sigs = 1;
+ opt.pgp2_workarounds = 1;
+ opt.ask_sig_expire = 0;
+ opt.ask_cert_expire = 0;
+ xfree(def_digest_string);
+ def_digest_string = xstrdup("md5");
+ xfree(s2k_digest_string);
+ s2k_digest_string = xstrdup("md5");
+ opt.compress_algo = COMPRESS_ALGO_ZIP;
+ }
+ }
+ else if(PGP6)
+ {
+ opt.escape_from=1;
+ opt.force_v3_sigs=1;
+ opt.ask_sig_expire=0;
+ }
+ else if(PGP7)
+ {
+ opt.escape_from=1;
+ opt.force_v3_sigs=1;
+ opt.ask_sig_expire=0;
+ }
+ else if(PGP8)
+ {
+ opt.escape_from=1;
+ }
+
+ /* must do this after dropping setuid, because string_to...
+ * may try to load an module */
+ if( def_cipher_string ) {
+ opt.def_cipher_algo = string_to_cipher_algo(def_cipher_string);
+ if(opt.def_cipher_algo==0 &&
+ (ascii_strcasecmp(def_cipher_string,"idea")==0
+ || ascii_strcasecmp(def_cipher_string,"s1")==0))
+ idea_cipher_warn(1);
+ xfree(def_cipher_string); def_cipher_string = NULL;
+ if( check_cipher_algo(opt.def_cipher_algo) )
+ log_error(_("selected cipher algorithm is invalid\n"));
+ }
+ if( def_digest_string ) {
+ opt.def_digest_algo = string_to_digest_algo(def_digest_string);
+ xfree(def_digest_string); def_digest_string = NULL;
+ if( check_digest_algo(opt.def_digest_algo) )
+ log_error(_("selected digest algorithm is invalid\n"));
+ }
+ if( compress_algo_string ) {
+ opt.compress_algo = string_to_compress_algo(compress_algo_string);
+ xfree(compress_algo_string); compress_algo_string = NULL;
+ if( check_compress_algo(opt.compress_algo) )
+ log_error(_("selected compression algorithm is invalid\n"));
+ }
+ if( cert_digest_string ) {
+ opt.cert_digest_algo = string_to_digest_algo(cert_digest_string);
+ xfree(cert_digest_string); cert_digest_string = NULL;
+ if( check_digest_algo(opt.cert_digest_algo) )
+ log_error(_("selected certification digest algorithm is invalid\n"));
+ }
+ if( s2k_cipher_string ) {
+ opt.s2k_cipher_algo = string_to_cipher_algo(s2k_cipher_string);
+ xfree(s2k_cipher_string); s2k_cipher_string = NULL;
+ if( check_cipher_algo(opt.s2k_cipher_algo) )
+ log_error(_("selected cipher algorithm is invalid\n"));
+ }
+ if( s2k_digest_string ) {
+ opt.s2k_digest_algo = string_to_digest_algo(s2k_digest_string);
+ xfree(s2k_digest_string); s2k_digest_string = NULL;
+ if( check_digest_algo(opt.s2k_digest_algo) )
+ log_error(_("selected digest algorithm is invalid\n"));
+ }
+ if( opt.completes_needed < 1 )
+ log_error(_("completes-needed must be greater than 0\n"));
+ if( opt.marginals_needed < 2 )
+ log_error(_("marginals-needed must be greater than 1\n"));
+ if( opt.max_cert_depth < 1 || opt.max_cert_depth > 255 )
+ log_error(_("max-cert-depth must be in the range from 1 to 255\n"));
+ if(opt.def_cert_level<0 || opt.def_cert_level>3)
+ log_error(_("invalid default-cert-level; must be 0, 1, 2, or 3\n"));
+ if( opt.min_cert_level < 1 || opt.min_cert_level > 3 )
+ log_error(_("invalid min-cert-level; must be 1, 2, or 3\n"));
+ switch( opt.s2k_mode ) {
+ case 0:
+ log_info(_("NOTE: simple S2K mode (0) is strongly discouraged\n"));
+ break;
+ case 1: case 3: break;
+ default:
+ log_error(_("invalid S2K mode; must be 0, 1 or 3\n"));
+ }
+
+ /* This isn't actually needed, but does serve to error out if the
+ string is invalid. */
+ if(opt.def_preference_list &&
+ keygen_set_std_prefs(opt.def_preference_list,0))
+ log_error(_("invalid default preferences\n"));
+
+ /* We provide defaults for the personal digest list. This is
+ SHA-1. */
+ if(!pers_digest_list)
+ pers_digest_list="h2";
+
+ if(pers_cipher_list &&
+ keygen_set_std_prefs(pers_cipher_list,PREFTYPE_SYM))
+ log_error(_("invalid personal cipher preferences\n"));
+
+ if(pers_digest_list &&
+ keygen_set_std_prefs(pers_digest_list,PREFTYPE_HASH))
+ log_error(_("invalid personal digest preferences\n"));
+
+ if(pers_compress_list &&
+ keygen_set_std_prefs(pers_compress_list,PREFTYPE_ZIP))
+ log_error(_("invalid personal compress preferences\n"));
+
+ /* We don't support all possible commands with multifile yet */
+ if(multifile)
+ {
+ char *cmdname;
+
+ switch(cmd)
+ {
+ case aSign:
+ cmdname="--sign";
+ break;
+ case aClearsign:
+ cmdname="--clearsign";
+ break;
+ case aDetachedSign:
+ cmdname="--detach-sign";
+ break;
+ case aSym:
+ cmdname="--symmetric";
+ break;
+ case aEncrSym:
+ cmdname="--symmetric --encrypt";
+ break;
+ case aStore:
+ cmdname="--store";
+ break;
+ default:
+ cmdname=NULL;
+ break;
+ }
+
+ if(cmdname)
+ log_error(_("%s does not yet work with %s\n"),cmdname,"--multifile");
+ }
+
+ if( log_get_errorcount(0) )
+ g10_exit(2);
+
+ if(opt.compress_level==0)
+ opt.compress_algo=COMPRESS_ALGO_NONE;
+
+ /* Check our chosen algorithms against the list of legal
+ algorithms. */
+
+ if(!GNUPG)
+ {
+ const char *badalg=NULL;
+ preftype_t badtype=PREFTYPE_NONE;
+
+ if(opt.def_cipher_algo
+ && !algo_available(PREFTYPE_SYM,opt.def_cipher_algo,NULL))
+ {
+ badalg=cipher_algo_to_string(opt.def_cipher_algo);
+ badtype=PREFTYPE_SYM;
+ }
+ else if(opt.def_digest_algo
+ && !algo_available(PREFTYPE_HASH,opt.def_digest_algo,NULL))
+ {
+ badalg=digest_algo_to_string(opt.def_digest_algo);
+ badtype=PREFTYPE_HASH;
+ }
+ else if(opt.cert_digest_algo
+ && !algo_available(PREFTYPE_HASH,opt.cert_digest_algo,NULL))
+ {
+ badalg=digest_algo_to_string(opt.cert_digest_algo);
+ badtype=PREFTYPE_HASH;
+ }
+ else if(opt.compress_algo!=-1
+ && !algo_available(PREFTYPE_ZIP,opt.compress_algo,NULL))
+ {
+ badalg=compress_algo_to_string(opt.compress_algo);
+ badtype=PREFTYPE_ZIP;
+ }
+
+ if(badalg)
+ {
+ switch(badtype)
+ {
+ case PREFTYPE_SYM:
+ log_info(_("you may not use cipher algorithm `%s'"
+ " while in %s mode\n"),
+ badalg,compliance_option_string());
+ break;
+ case PREFTYPE_HASH:
+ log_info(_("you may not use digest algorithm `%s'"
+ " while in %s mode\n"),
+ badalg,compliance_option_string());
+ break;
+ case PREFTYPE_ZIP:
+ log_info(_("you may not use compression algorithm `%s'"
+ " while in %s mode\n"),
+ badalg,compliance_option_string());
+ break;
+ default:
+ BUG();
+ }
+
+ compliance_failure();
+ }
+ }
+
+ /* set the random seed file */
+ if( use_random_seed ) {
+ char *p = make_filename(opt.homedir, "random_seed", NULL );
+ set_random_seed_file(p);
+ if (!access (p, F_OK))
+ register_secured_file (p);
+ xfree(p);
+ }
+
+ if( !cmd && opt.fingerprint && !with_fpr ) {
+ set_cmd( &cmd, aListKeys);
+ }
+
+ if( cmd == aKMode || cmd == aKModeC ) { /* kludge to be compatible to pgp */
+ if( cmd == aKModeC ) {
+ opt.fingerprint = 1;
+ cmd = aKMode;
+ }
+ opt.list_sigs = 0;
+ if( opt.verbose > 2 )
+ opt.check_sigs++;
+ if( opt.verbose > 1 )
+ opt.list_sigs++;
+
+ opt.verbose = opt.verbose > 1;
+ g10_opt_verbose = opt.verbose;
+ }
+
+ /* kludge to let -sat generate a clear text signature */
+ if( opt.textmode == 2 && !detached_sig && opt.armor && cmd == aSign )
+ cmd = aClearsign;
+
+ if( opt.verbose > 1 )
+ set_packet_list_mode(1);
+
+ if (cmd == aGPGConfTest)
+ g10_exit(0);
+
+ /* Add the keyrings, but not for some special commands and not in
+ case of "-kvv userid keyring". Also avoid adding the secret
+ keyring for a couple of commands to avoid unneeded access in
+ case the secrings are stored on a floppy.
+
+ We always need to add the keyrings if we are running under
+ SELinux, this is so that the rings are added to the list of
+ secured files. */
+ if( ALWAYS_ADD_KEYRINGS
+ || (cmd != aDeArmor && cmd != aEnArmor
+ && !(cmd == aKMode && argc == 2 )) )
+ {
+ if (ALWAYS_ADD_KEYRINGS
+ || (cmd != aCheckKeys && cmd != aListSigs && cmd != aListKeys
+ && cmd != aVerify && cmd != aSym))
+ {
+ if (!sec_nrings || default_keyring) /* add default secret rings */
+ keydb_add_resource ("secring" EXTSEP_S "gpg", 4, 1);
+ for (sl = sec_nrings; sl; sl = sl->next)
+ keydb_add_resource ( sl->d, 0, 1 );
+ }
+ if( !nrings || default_keyring ) /* add default ring */
+ keydb_add_resource ("pubring" EXTSEP_S "gpg", 4, 0);
+ for(sl = nrings; sl; sl = sl->next )
+ keydb_add_resource ( sl->d, sl->flags, 0 );
+ }
+ FREE_STRLIST(nrings);
+ FREE_STRLIST(sec_nrings);
+
+
+ if( pwfd != -1 ) /* read the passphrase now. */
+ read_passphrase_from_fd( pwfd );
+
+ fname = argc? *argv : NULL;
+
+ if(fname && utf8_strings)
+ opt.flags.utf8_filename=1;
+
+ switch( cmd ) {
+ case aPrimegen:
+ case aPrintMD:
+ case aPrintMDs:
+ case aGenRandom:
+ case aDeArmor:
+ case aEnArmor:
+ case aFixTrustDB:
+ break;
+ case aExportOwnerTrust: rc = setup_trustdb( 0, trustdb_name ); break;
+ case aListTrustDB: rc = setup_trustdb( argc? 1:0, trustdb_name ); break;
+ default: rc = setup_trustdb(1, trustdb_name ); break;
+ }
+ if( rc )
+ log_error(_("failed to initialize the TrustDB: %s\n"), g10_errstr(rc));
+
+
+ switch (cmd)
+ {
+ case aStore:
+ case aSym:
+ case aSign:
+ case aSignSym:
+ case aClearsign:
+ if (!opt.quiet && any_explicit_recipient)
+ log_info (_("WARNING: recipients (-r) given "
+ "without using public key encryption\n"));
+ break;
+ default:
+ break;
+ }
+
+ switch( cmd )
+ {
+ case aStore: /* only store the file */
+ if( argc > 1 )
+ wrong_args(_("--store [filename]"));
+ if( (rc = encode_store(fname)) )
+ log_error ("storing `%s' failed: %s\n",
+ print_fname_stdin(fname),g10_errstr(rc) );
+ break;
+ case aSym: /* encrypt the given file only with the symmetric cipher */
+ if( argc > 1 )
+ wrong_args(_("--symmetric [filename]"));
+ if( (rc = encode_symmetric(fname)) )
+ log_error (_("symmetric encryption of `%s' failed: %s\n"),
+ print_fname_stdin(fname),g10_errstr(rc) );
+ break;
+
+ case aEncr: /* encrypt the given file */
+ if(multifile)
+ encode_crypt_files(argc, argv, remusr);
+ else
+ {
+ if( argc > 1 )
+ wrong_args(_("--encrypt [filename]"));
+ if( (rc = encode_crypt(fname,remusr,0)) )
+ log_error("%s: encryption failed: %s\n",
+ print_fname_stdin(fname), g10_errstr(rc) );
+ }
+ break;
+
+ case aEncrSym:
+ /* This works with PGP 8 in the sense that it acts just like a
+ symmetric message. It doesn't work at all with 2 or 6. It
+ might work with 7, but alas, I don't have a copy to test
+ with right now. */
+ if( argc > 1 )
+ wrong_args(_("--symmetric --encrypt [filename]"));
+ else if(opt.s2k_mode==0)
+ log_error(_("you cannot use --symmetric --encrypt"
+ " with --s2k-mode 0\n"));
+ else if(PGP2 || PGP6 || PGP7 || RFC1991)
+ log_error(_("you cannot use --symmetric --encrypt"
+ " while in %s mode\n"),compliance_option_string());
+ else
+ {
+ if( (rc = encode_crypt(fname,remusr,1)) )
+ log_error("%s: encryption failed: %s\n",
+ print_fname_stdin(fname), g10_errstr(rc) );
+ }
+ break;
+
+ case aSign: /* sign the given file */
+ sl = NULL;
+ if( detached_sig ) { /* sign all files */
+ for( ; argc; argc--, argv++ )
+ add_to_strlist( &sl, *argv );
+ }
+ else {
+ if( argc > 1 )
+ wrong_args(_("--sign [filename]"));
+ if( argc ) {
+ sl = xmalloc_clear( sizeof *sl + strlen(fname));
+ strcpy(sl->d, fname);
+ }
+ }
+ if( (rc = sign_file( sl, detached_sig, locusr, 0, NULL, NULL)) )
+ log_error("signing failed: %s\n", g10_errstr(rc) );
+ free_strlist(sl);
+ break;
+
+ case aSignEncr: /* sign and encrypt the given file */
+ if( argc > 1 )
+ wrong_args(_("--sign --encrypt [filename]"));
+ if( argc ) {
+ sl = xmalloc_clear( sizeof *sl + strlen(fname));
+ strcpy(sl->d, fname);
+ }
+ else
+ sl = NULL;
+ if( (rc = sign_file(sl, detached_sig, locusr, 1, remusr, NULL)) )
+ log_error("%s: sign+encrypt failed: %s\n",
+ print_fname_stdin(fname), g10_errstr(rc) );
+ free_strlist(sl);
+ break;
+
+ case aSignEncrSym: /* sign and encrypt the given file */
+ if( argc > 1 )
+ wrong_args(_("--symmetric --sign --encrypt [filename]"));
+ else if(opt.s2k_mode==0)
+ log_error(_("you cannot use --symmetric --sign --encrypt"
+ " with --s2k-mode 0\n"));
+ else if(PGP2 || PGP6 || PGP7 || RFC1991)
+ log_error(_("you cannot use --symmetric --sign --encrypt"
+ " while in %s mode\n"),compliance_option_string());
+ else
+ {
+ if( argc )
+ {
+ sl = xmalloc_clear( sizeof *sl + strlen(fname));
+ strcpy(sl->d, fname);
+ }
+ else
+ sl = NULL;
+ if( (rc = sign_file(sl, detached_sig, locusr, 2, remusr, NULL)) )
+ log_error("%s: symmetric+sign+encrypt failed: %s\n",
+ print_fname_stdin(fname), g10_errstr(rc) );
+ free_strlist(sl);
+ }
+ break;
+
+ case aSignSym: /* sign and conventionally encrypt the given file */
+ if (argc > 1)
+ wrong_args(_("--sign --symmetric [filename]"));
+ rc = sign_symencrypt_file (fname, locusr);
+ if (rc)
+ log_error("%s: sign+symmetric failed: %s\n",
+ print_fname_stdin(fname), g10_errstr(rc) );
+ break;
+
+ case aClearsign: /* make a clearsig */
+ if( argc > 1 )
+ wrong_args(_("--clearsign [filename]"));
+ if( (rc = clearsign_file(fname, locusr, NULL)) )
+ log_error("%s: clearsign failed: %s\n",
+ print_fname_stdin(fname), g10_errstr(rc) );
+ break;
+
+ case aVerify:
+ if(multifile)
+ {
+ if( (rc = verify_files( argc, argv ) ))
+ log_error("verify files failed: %s\n", g10_errstr(rc) );
+ }
+ else
+ {
+ if( (rc = verify_signatures( argc, argv ) ))
+ log_error("verify signatures failed: %s\n", g10_errstr(rc) );
+ }
+ break;
+
+ case aDecrypt:
+ if(multifile)
+ decrypt_messages(argc, argv);
+ else
+ {
+ if( argc > 1 )
+ wrong_args(_("--decrypt [filename]"));
+ if( (rc = decrypt_message( fname ) ))
+ log_error("decrypt_message failed: %s\n", g10_errstr(rc) );
+ }
+ break;
+
+ case aSignKey:
+ if( argc != 1 )
+ wrong_args(_("--sign-key user-id"));
+ /* fall through */
+ case aLSignKey:
+ if( argc != 1 )
+ wrong_args(_("--lsign-key user-id"));
+ /* fall through */
+
+ sl=NULL;
+
+ if(cmd==aSignKey)
+ append_to_strlist(&sl,"sign");
+ else if(cmd==aLSignKey)
+ append_to_strlist(&sl,"lsign");
+ else
+ BUG();
+
+ append_to_strlist( &sl, "save" );
+ username = make_username( fname );
+ keyedit_menu(fname, locusr, sl, 0, 0 );
+ xfree(username);
+ free_strlist(sl);
+ break;
+
+ case aEditKey: /* Edit a key signature */
+ if( !argc )
+ wrong_args(_("--edit-key user-id [commands]"));
+ username = make_username( fname );
+ if( argc > 1 ) {
+ sl = NULL;
+ for( argc--, argv++ ; argc; argc--, argv++ )
+ append_to_strlist( &sl, *argv );
+ keyedit_menu( username, locusr, sl, 0, 1 );
+ free_strlist(sl);
+ }
+ else
+ keyedit_menu(username, locusr, NULL, 0, 1 );
+ xfree(username);
+ break;
+
+ case aDeleteKeys:
+ case aDeleteSecretKeys:
+ case aDeleteSecretAndPublicKeys:
+ sl = NULL;
+ /* I'm adding these in reverse order as add_to_strlist2
+ reverses them again, and it's easier to understand in the
+ proper order :) */
+ for( ; argc; argc-- )
+ add_to_strlist2( &sl, argv[argc-1], utf8_strings );
+ delete_keys(sl,cmd==aDeleteSecretKeys,cmd==aDeleteSecretAndPublicKeys);
+ free_strlist(sl);
+ break;
+
+ case aCheckKeys:
+ opt.check_sigs = 1;
+ case aListSigs:
+ opt.list_sigs = 1;
+ case aListKeys:
+ sl = NULL;
+ for( ; argc; argc--, argv++ )
+ add_to_strlist2( &sl, *argv, utf8_strings );
+ public_key_list( sl );
+ free_strlist(sl);
+ break;
+ case aListSecretKeys:
+ sl = NULL;
+ for( ; argc; argc--, argv++ )
+ add_to_strlist2( &sl, *argv, utf8_strings );
+ secret_key_list( sl );
+ free_strlist(sl);
+ break;
+
+ case aKMode: /* list keyring -- NOTE: This will be removed soon */
+ if( argc < 2 ) { /* -kv [userid] */
+ sl = NULL;
+ if (argc && **argv)
+ add_to_strlist2( &sl, *argv, utf8_strings );
+ public_key_list( sl );
+ free_strlist(sl);
+ }
+ else if( argc == 2 ) { /* -kv userid keyring */
+ if( access( argv[1], R_OK ) ) {
+ log_error(_("can't open `%s': %s\n"),
+ print_fname_stdin(argv[1]), strerror(errno));
+ }
+ else {
+ /* add keyring (default keyrings are not registered in this
+ * special case */
+ keydb_add_resource( argv[1], 0, 0 );
+ sl = NULL;
+ if (**argv)
+ add_to_strlist2( &sl, *argv, utf8_strings );
+ public_key_list( sl );
+ free_strlist(sl);
+ }
+ }
+ else
+ wrong_args(_("-k[v][v][v][c] [user-id] [keyring]") );
+ break;
+
+ case aKeygen: /* generate a key */
+ if( opt.batch ) {
+ if( argc > 1 )
+ wrong_args("--gen-key [parameterfile]");
+ generate_keypair( argc? *argv : NULL, NULL, NULL );
+ }
+ else {
+ if( argc )
+ wrong_args("--gen-key");
+ generate_keypair(NULL, NULL, NULL);
+ }
+ break;
+
+ case aFastImport:
+ opt.import_options |= IMPORT_FAST;
+ case aImport:
+ import_keys( argc? argv:NULL, argc, NULL, opt.import_options );
+ break;
+
+ /* TODO: There are a number of command that use this same
+ "make strlist, call function, report error, free strlist"
+ pattern. Join them together here and avoid all that
+ duplicated code. */
+
+ case aExport:
+ case aSendKeys:
+ case aRecvKeys:
+ sl = NULL;
+ for( ; argc; argc--, argv++ )
+ append_to_strlist2( &sl, *argv, utf8_strings );
+ if( cmd == aSendKeys )
+ rc=keyserver_export( sl );
+ else if( cmd == aRecvKeys )
+ rc=keyserver_import( sl );
+ else
+ rc=export_pubkeys( sl, opt.export_options );
+ if(rc)
+ {
+ if(cmd==aSendKeys)
+ log_error(_("keyserver send failed: %s\n"),g10_errstr(rc));
+ else if(cmd==aRecvKeys)
+ log_error(_("keyserver receive failed: %s\n"),g10_errstr(rc));
+ else
+ log_error(_("key export failed: %s\n"),g10_errstr(rc));
+ }
+ free_strlist(sl);
+ break;
+
+ case aSearchKeys:
+ sl = NULL;
+ for( ; argc; argc--, argv++ )
+ append_to_strlist2( &sl, *argv, utf8_strings );
+ rc=keyserver_search( sl );
+ if(rc)
+ log_error(_("keyserver search failed: %s\n"),g10_errstr(rc));
+ free_strlist(sl);
+ break;
+
+ case aRefreshKeys:
+ sl = NULL;
+ for( ; argc; argc--, argv++ )
+ append_to_strlist2( &sl, *argv, utf8_strings );
+ rc=keyserver_refresh(sl);
+ if(rc)
+ log_error(_("keyserver refresh failed: %s\n"),g10_errstr(rc));
+ free_strlist(sl);
+ break;
+
+ case aFetchKeys:
+ sl = NULL;
+ for( ; argc; argc--, argv++ )
+ append_to_strlist2( &sl, *argv, utf8_strings );
+ rc=keyserver_fetch(sl);
+ if(rc)
+ log_error("key fetch failed: %s\n",g10_errstr(rc));
+ free_strlist(sl);
+ break;
+
+ case aExportSecret:
+ sl = NULL;
+ for( ; argc; argc--, argv++ )
+ add_to_strlist2( &sl, *argv, utf8_strings );
+ export_seckeys( sl );
+ free_strlist(sl);
+ break;
+
+ case aExportSecretSub:
+ sl = NULL;
+ for( ; argc; argc--, argv++ )
+ add_to_strlist2( &sl, *argv, utf8_strings );
+ export_secsubkeys( sl );
+ free_strlist(sl);
+ break;
+
+ case aGenRevoke:
+ if( argc != 1 )
+ wrong_args("--gen-revoke user-id");
+ username = make_username(*argv);
+ gen_revoke( username );
+ xfree( username );
+ break;
+
+ case aDesigRevoke:
+ if( argc != 1 )
+ wrong_args("--desig-revoke user-id");
+ username = make_username(*argv);
+ gen_desig_revoke( username, locusr );
+ xfree( username );
+ break;
+
+ case aDeArmor:
+ if( argc > 1 )
+ wrong_args("--dearmor [file]");
+ rc = dearmor_file( argc? *argv: NULL );
+ if( rc )
+ log_error(_("dearmoring failed: %s\n"), g10_errstr(rc));
+ break;
+
+ case aEnArmor:
+ if( argc > 1 )
+ wrong_args("--enarmor [file]");
+ rc = enarmor_file( argc? *argv: NULL );
+ if( rc )
+ log_error(_("enarmoring failed: %s\n"), g10_errstr(rc));
+ break;
+
+
+ case aPrimegen:
+ { int mode = argc < 2 ? 0 : atoi(*argv);
+
+ if( mode == 1 && argc == 2 ) {
+ mpi_print( stdout, generate_public_prime( atoi(argv[1]) ), 1);
+ }
+ else if( mode == 2 && argc == 3 ) {
+ mpi_print( stdout, generate_elg_prime(
+ 0, atoi(argv[1]),
+ atoi(argv[2]), NULL,NULL ), 1);
+ }
+ else if( mode == 3 && argc == 3 ) {
+ MPI *factors;
+ mpi_print( stdout, generate_elg_prime(
+ 1, atoi(argv[1]),
+ atoi(argv[2]), NULL,&factors ), 1);
+ putchar('\n');
+ mpi_print( stdout, factors[0], 1 ); /* print q */
+ }
+ else if( mode == 4 && argc == 3 ) {
+ MPI g = mpi_alloc(1);
+ mpi_print( stdout, generate_elg_prime(
+ 0, atoi(argv[1]),
+ atoi(argv[2]), g, NULL ), 1);
+ putchar('\n');
+ mpi_print( stdout, g, 1 );
+ mpi_free(g);
+ }
+ else
+ wrong_args("--gen-prime mode bits [qbits] ");
+ putchar('\n');
+ }
+ break;
+
+ case aGenRandom:
+ {
+ int level = argc ? atoi(*argv):0;
+ int count = argc > 1 ? atoi(argv[1]): 0;
+ int endless = !count;
+
+ if( argc < 1 || argc > 2 || level < 0 || level > 2 || count < 0 )
+ wrong_args("--gen-random 0|1|2 [count]");
+
+ while( endless || count ) {
+ byte *p;
+ /* Wee need a multiple of 3, so that in case of
+ armored output we get a correct string. No
+ linefolding is done, as it is best to levae this to
+ other tools */
+ size_t n = !endless && count < 99? count : 99;
+
+ p = get_random_bits( n*8, level, 0);
+#ifdef HAVE_DOSISH_SYSTEM
+ setmode ( fileno(stdout), O_BINARY );
+#endif
+ if (opt.armor) {
+ char *tmp = make_radix64_string (p, n);
+ fputs (tmp, stdout);
+ xfree (tmp);
+ if (n%3 == 1)
+ putchar ('=');
+ if (n%3)
+ putchar ('=');
+ } else {
+ fwrite( p, n, 1, stdout );
+ }
+ xfree(p);
+ if( !endless )
+ count -= n;
+ }
+ if (opt.armor)
+ putchar ('\n');
+ }
+ break;
+
+ case aPrintMD:
+ if( argc < 1)
+ wrong_args("--print-md algo [files]");
+ {
+ int all_algos = (**argv=='*' && !(*argv)[1]);
+ int algo = all_algos? 0 : string_to_digest_algo(*argv);
+
+ if( !algo && !all_algos )
+ log_error(_("invalid hash algorithm `%s'\n"), *argv );
+ else {
+ argc--; argv++;
+ if( !argc )
+ print_mds(NULL, algo);
+ else {
+ for(; argc; argc--, argv++ )
+ print_mds(*argv, algo);
+ }
+ }
+ }
+ break;
+
+ case aPrintMDs: /* old option */
+ if( !argc )
+ print_mds(NULL,0);
+ else {
+ for(; argc; argc--, argv++ )
+ print_mds(*argv,0);
+ }
+ break;
+
+ case aListTrustDB:
+ if( !argc )
+ list_trustdb(NULL);
+ else {
+ for( ; argc; argc--, argv++ )
+ list_trustdb( *argv );
+ }
+ break;
+
+ case aUpdateTrustDB:
+ if( argc )
+ wrong_args("--update-trustdb");
+ update_trustdb();
+ break;
+
+ case aCheckTrustDB:
+ /* Old versions allowed for arguments - ignore them */
+ check_trustdb();
+ break;
+
+ case aFixTrustDB:
+ log_error("this command is not yet implemented.\n");
+ log_error("A workaround is to use \"--export-ownertrust\", remove\n");
+ log_error("the trustdb file and do an \"--import-ownertrust\".\n" );
+ break;
+
+ case aListTrustPath:
+ if( !argc )
+ wrong_args("--list-trust-path <user-ids>");
+ for( ; argc; argc--, argv++ ) {
+ username = make_username( *argv );
+ list_trust_path( username );
+ xfree(username);
+ }
+ break;
+
+ case aExportOwnerTrust:
+ if( argc )
+ wrong_args("--export-ownertrust");
+ export_ownertrust();
+ break;
+
+ case aImportOwnerTrust:
+ if( argc > 1 )
+ wrong_args("--import-ownertrust [file]");
+ import_ownertrust( argc? *argv:NULL );
+ break;
+
+ case aPipeMode:
+ if ( argc )
+ wrong_args ("--pipemode");
+ run_in_pipemode ();
+ break;
+
+ case aRebuildKeydbCaches:
+ if (argc)
+ wrong_args ("--rebuild-keydb-caches");
+ keydb_rebuild_caches (1);
+ break;
+
+#ifdef ENABLE_CARD_SUPPORT
+ case aCardStatus:
+ if (argc)
+ wrong_args ("--card-status");
+ card_status (stdout, NULL, 0);
+ break;
+
+ case aCardEdit:
+ if (argc) {
+ sl = NULL;
+ for (argc--, argv++ ; argc; argc--, argv++)
+ append_to_strlist (&sl, *argv);
+ card_edit (sl);
+ free_strlist (sl);
+ }
+ else
+ card_edit (NULL);
+ break;
+
+ case aChangePIN:
+ if (!argc)
+ change_pin (0,1);
+ else if (argc == 1)
+ change_pin (atoi (*argv),1);
+ else
+ wrong_args ("--change-pin [no]");
+ break;
+#endif /* ENABLE_CARD_SUPPORT*/
+
+ case aListConfig:
+ {
+ char *str=collapse_args(argc,argv);
+ list_config(str);
+ xfree(str);
+ }
+ break;
+
+ case aListPackets:
+ opt.list_packets=2;
+ default:
+ if( argc > 1 )
+ wrong_args(_("[filename]"));
+ /* Issue some output for the unix newbie */
+ if( !fname && !opt.outfile && isatty( fileno(stdin) )
+ && isatty( fileno(stdout) ) && isatty( fileno(stderr) ) )
+ log_info(_("Go ahead and type your message ...\n"));
+
+ a = iobuf_open(fname);
+ if (a && is_secured_file (iobuf_get_fd (a)))
+ {
+ iobuf_close (a);
+ a = NULL;
+ errno = EPERM;
+ }
+ if( !a )
+ log_error(_("can't open `%s'\n"), print_fname_stdin(fname));
+ else {
+
+ if( !opt.no_armor ) {
+ if( use_armor_filter( a ) ) {
+ memset( &afx, 0, sizeof afx);
+ iobuf_push_filter( a, armor_filter, &afx );
+ }
+ }
+ if( cmd == aListPackets ) {
+ set_packet_list_mode(1);
+ opt.list_packets=1;
+ }
+ rc = proc_packets(NULL, a );
+ if( rc )
+ log_error("processing message failed: %s\n", g10_errstr(rc) );
+ iobuf_close(a);
+ }
+ break;
+ }
+
+ /* cleanup */
+ FREE_STRLIST(remusr);
+ FREE_STRLIST(locusr);
+ g10_exit(0);
+ return 8; /*NEVER REACHED*/
+}
+
+
+void
+g10_exit( int rc )
+{
+#ifdef ENABLE_CARD_SUPPORT
+ card_close ();
+#endif
+ update_random_seed_file();
+ if( opt.debug & DBG_MEMSTAT_VALUE ) {
+ m_print_stats("on exit");
+ random_dump_stats();
+ }
+ if( opt.debug )
+ secmem_dump_stats();
+ secmem_term();
+ rc = rc? rc : log_get_errorcount(0)? 2 :
+ g10_errors_seen? 1 : 0;
+ exit(rc );
+}
+
+
+/* Pretty-print hex hashes. This assumes at least an 80-character
+ display, but there are a few other similar assumptions in the
+ display code. */
+static void
+print_hex( MD_HANDLE md, int algo, const char *fname )
+{
+ int i,n,count,indent=0;
+ const byte *p;
+
+ if(fname)
+ indent=printf("%s: ",fname);
+
+ if(indent>40)
+ {
+ printf("\n");
+ indent=0;
+ }
+
+ if(algo==DIGEST_ALGO_RMD160)
+ indent+=printf("RMD160 = ");
+ else if(algo>0)
+ indent+=printf("%6s = ",digest_algo_to_string(algo));
+ else
+ algo=abs(algo);
+
+ count=indent;
+
+ p = md_read( md, algo );
+ n = md_digest_length(algo);
+
+ count+=printf("%02X",*p++);
+
+ for(i=1;i<n;i++,p++)
+ {
+ if(n==16)
+ {
+ if(count+2>79)
+ {
+ printf("\n%*s",indent," ");
+ count=indent;
+ }
+ else
+ count+=printf(" ");
+
+ if(!(i%8))
+ count+=printf(" ");
+ }
+ else if (n==20)
+ {
+ if(!(i%2))
+ {
+ if(count+4>79)
+ {
+ printf("\n%*s",indent," ");
+ count=indent;
+ }
+ else
+ count+=printf(" ");
+ }
+
+ if(!(i%10))
+ count+=printf(" ");
+ }
+ else
+ {
+ if(!(i%4))
+ {
+ if(count+8>79)
+ {
+ printf("\n%*s",indent," ");
+ count=indent;
+ }
+ else
+ count+=printf(" ");
+ }
+ }
+
+ count+=printf("%02X",*p);
+ }
+
+ printf("\n");
+}
+
+static void
+print_hashline( MD_HANDLE md, int algo, const char *fname )
+{
+ int i, n;
+ const byte *p;
+
+ if ( fname ) {
+ for (p = fname; *p; p++ ) {
+ if ( *p <= 32 || *p > 127 || *p == ':' || *p == '%' )
+ printf("%%%02X", *p );
+ else
+ putchar( *p );
+ }
+ }
+ putchar(':');
+ printf("%d:", algo );
+ p = md_read( md, algo );
+ n = md_digest_length(algo);
+ for(i=0; i < n ; i++, p++ )
+ printf("%02X", *p );
+ putchar(':');
+ putchar('\n');
+}
+
+static void
+print_mds( const char *fname, int algo )
+{
+ FILE *fp;
+ char buf[1024];
+ size_t n;
+ MD_HANDLE md;
+
+ if( !fname ) {
+ fp = stdin;
+#ifdef HAVE_DOSISH_SYSTEM
+ setmode ( fileno(fp) , O_BINARY );
+#endif
+ }
+ else {
+ fp = fopen( fname, "rb" );
+ if (fp && is_secured_file (fileno (fp)))
+ {
+ fclose (fp);
+ fp = NULL;
+ errno = EPERM;
+ }
+ }
+ if( !fp ) {
+ log_error("%s: %s\n", fname?fname:"[stdin]", strerror(errno) );
+ return;
+ }
+
+ md = md_open( 0, 0 );
+ if( algo )
+ md_enable( md, algo );
+ else {
+ md_enable( md, DIGEST_ALGO_MD5 );
+ md_enable( md, DIGEST_ALGO_SHA1 );
+ md_enable( md, DIGEST_ALGO_RMD160 );
+#ifdef USE_SHA256
+ md_enable( md, DIGEST_ALGO_SHA224 );
+ md_enable( md, DIGEST_ALGO_SHA256 );
+#endif
+#ifdef USE_SHA512
+ md_enable( md, DIGEST_ALGO_SHA384 );
+ md_enable( md, DIGEST_ALGO_SHA512 );
+#endif
+ }
+
+ while( (n=fread( buf, 1, DIM(buf), fp )) )
+ md_write( md, buf, n );
+ if( ferror(fp) )
+ log_error("%s: %s\n", fname?fname:"[stdin]", strerror(errno) );
+ else {
+ md_final(md);
+ if ( opt.with_colons ) {
+ if ( algo )
+ print_hashline( md, algo, fname );
+ else {
+ print_hashline( md, DIGEST_ALGO_MD5, fname );
+ print_hashline( md, DIGEST_ALGO_SHA1, fname );
+ print_hashline( md, DIGEST_ALGO_RMD160, fname );
+#ifdef USE_SHA256
+ print_hashline( md, DIGEST_ALGO_SHA224, fname );
+ print_hashline( md, DIGEST_ALGO_SHA256, fname );
+#endif
+#ifdef USE_SHA512
+ print_hashline( md, DIGEST_ALGO_SHA384, fname );
+ print_hashline( md, DIGEST_ALGO_SHA512, fname );
+#endif
+ }
+ }
+ else {
+ if( algo )
+ print_hex(md,-algo,fname);
+ else {
+ print_hex( md, DIGEST_ALGO_MD5, fname );
+ print_hex( md, DIGEST_ALGO_SHA1, fname );
+ print_hex( md, DIGEST_ALGO_RMD160, fname );
+#ifdef USE_SHA256
+ print_hex( md, DIGEST_ALGO_SHA224, fname );
+ print_hex( md, DIGEST_ALGO_SHA256, fname );
+#endif
+#ifdef USE_SHA512
+ print_hex( md, DIGEST_ALGO_SHA384, fname );
+ print_hex( md, DIGEST_ALGO_SHA512, fname );
+#endif
+ }
+ }
+ }
+ md_close(md);
+
+ if( fp != stdin )
+ fclose(fp);
+}
+
+
+/****************
+ * Check the supplied name,value string and add it to the notation
+ * data to be used for signatures. which==0 for sig notations, and 1
+ * for cert notations.
+*/
+static void
+add_notation_data( const char *string, int which )
+{
+ struct notation *notation;
+
+ notation=string_to_notation(string,utf8_strings);
+ if(notation)
+ {
+ if(which)
+ {
+ notation->next=opt.cert_notations;
+ opt.cert_notations=notation;
+ }
+ else
+ {
+ notation->next=opt.sig_notations;
+ opt.sig_notations=notation;
+ }
+ }
+}
+
+static void
+add_policy_url( const char *string, int which )
+{
+ unsigned int i,critical=0;
+ STRLIST sl;
+
+ if(*string=='!')
+ {
+ string++;
+ critical=1;
+ }
+
+ for(i=0;i<strlen(string);i++)
+ if( !isascii (string[i]) || iscntrl(string[i]))
+ break;
+
+ if(i==0 || i<strlen(string))
+ {
+ if(which)
+ log_error(_("the given certification policy URL is invalid\n"));
+ else
+ log_error(_("the given signature policy URL is invalid\n"));
+ }
+
+ if(which)
+ sl=add_to_strlist( &opt.cert_policy_url, string );
+ else
+ sl=add_to_strlist( &opt.sig_policy_url, string );
+
+ if(critical)
+ sl->flags |= 1;
+}
+
+static void
+add_keyserver_url( const char *string, int which )
+{
+ unsigned int i,critical=0;
+ STRLIST sl;
+
+ if(*string=='!')
+ {
+ string++;
+ critical=1;
+ }
+
+ for(i=0;i<strlen(string);i++)
+ if( !isascii (string[i]) || iscntrl(string[i]))
+ break;
+
+ if(i==0 || i<strlen(string))
+ {
+ if(which)
+ BUG();
+ else
+ log_error(_("the given preferred keyserver URL is invalid\n"));
+ }
+
+ if(which)
+ BUG();
+ else
+ sl=add_to_strlist( &opt.sig_keyserver_url, string );
+
+ if(critical)
+ sl->flags |= 1;
+}
diff --git a/g10/gpgv.c b/g10/gpgv.c
new file mode 100644
index 0000000..5192c26
--- /dev/null
+++ b/g10/gpgv.c
@@ -0,0 +1,434 @@
+/* gpgv.c - The GnuPG signature verify utility
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2005,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <unistd.h>
+#ifdef HAVE_DOSISH_SYSTEM
+#include <fcntl.h> /* for setmode() */
+#endif
+#ifdef HAVE_LIBREADLINE
+#include <stdio.h>
+#include <readline/readline.h>
+#endif
+
+#define INCLUDED_BY_MAIN_MODULE 1
+#include "packet.h"
+#include "iobuf.h"
+#include "memory.h"
+#include "util.h"
+#include "main.h"
+#include "options.h"
+#include "keydb.h"
+#include "trustdb.h"
+#include "mpi.h"
+#include "cipher.h"
+#include "filter.h"
+#include "ttyio.h"
+#include "i18n.h"
+#include "status.h"
+#include "g10defs.h"
+#include "cardglue.h"
+
+
+enum cmd_and_opt_values { aNull = 0,
+ oQuiet = 'q',
+ oVerbose = 'v',
+ oBatch = 500,
+ oKeyring,
+ oIgnoreTimeConflict,
+ oStatusFD,
+ oLoggerFD,
+ oHomedir,
+aTest };
+
+
+static ARGPARSE_OPTS opts[] = {
+
+ { 301, NULL, 0, N_("@\nOptions:\n ") },
+
+ { oVerbose, "verbose", 0, N_("verbose") },
+ { oQuiet, "quiet", 0, N_("be somewhat more quiet") },
+ { oKeyring, "keyring" ,2, N_("take the keys from this keyring")},
+ { oIgnoreTimeConflict, "ignore-time-conflict", 0,
+ N_("make timestamp conflicts only a warning") },
+ { oStatusFD, "status-fd" ,1, N_("|FD|write status info to this FD") },
+ { oLoggerFD, "logger-fd",1, "@" },
+ { oHomedir, "homedir", 2, "@" }, /* defaults to "~/.gnupg" */
+
+{0} };
+
+
+
+int g10_errors_seen = 0;
+
+const char *
+strusage( int level )
+{
+ const char *p;
+ switch( level ) {
+ case 11: p = "gpgv (GnuPG)";
+ break;
+ case 13: p = VERSION; break;
+ case 17: p = PRINTABLE_OS_NAME; break;
+ case 19: p =
+ _("Please report bugs to <gnupg-bugs@gnu.org>.\n");
+ break;
+ case 1:
+ case 40: p =
+ _("Usage: gpgv [options] [files] (-h for help)");
+ break;
+ case 41: p =
+ _("Syntax: gpg [options] [files]\n"
+ "Check signatures against known trusted keys\n");
+ break;
+
+ default: p = default_strusage(level);
+ }
+ return p;
+}
+
+
+
+
+static void
+i18n_init(void)
+{
+#ifdef USE_SIMPLE_GETTEXT
+ set_gettext_file (PACKAGE, "Software\\GNU\\GnuPG");
+#else
+#ifdef ENABLE_NLS
+ setlocale( LC_ALL, "" );
+ bindtextdomain( PACKAGE, G10_LOCALEDIR );
+ textdomain( PACKAGE );
+#endif
+#endif
+}
+
+
+int
+main( int argc, char **argv )
+{
+ ARGPARSE_ARGS pargs;
+ int rc=0;
+ STRLIST sl;
+ STRLIST nrings=NULL;
+ unsigned configlineno;
+
+ log_set_name("gpgv");
+ init_signals();
+ i18n_init();
+ opt.command_fd = -1; /* no command fd */
+ opt.pgp2_workarounds = 1;
+ opt.keyserver_options.options|=KEYSERVER_AUTO_KEY_RETRIEVE;
+ opt.trust_model = TM_ALWAYS;
+ opt.batch = 1;
+
+ opt.homedir = default_homedir ();
+
+ tty_no_terminal(1);
+ tty_batchmode(1);
+ disable_dotlock();
+
+ set_native_charset (NULL); /* Try to auto set the character set */
+
+ pargs.argc = &argc;
+ pargs.argv = &argv;
+ pargs.flags= 1; /* do not remove the args */
+ while( optfile_parse( NULL, NULL, &configlineno, &pargs, opts) ) {
+ switch( pargs.r_opt ) {
+ case oQuiet: opt.quiet = 1; break;
+ case oVerbose: g10_opt_verbose++;
+ opt.verbose++; opt.list_sigs=1; break;
+ case oKeyring: append_to_strlist( &nrings, pargs.r.ret_str); break;
+ case oStatusFD: set_status_fd( pargs.r.ret_int ); break;
+ case oLoggerFD: log_set_logfile( NULL, pargs.r.ret_int ); break;
+ case oHomedir: opt.homedir = pargs.r.ret_str; break;
+ case oIgnoreTimeConflict: opt.ignore_time_conflict = 1; break;
+ default : pargs.err = 2; break;
+ }
+ }
+
+ if( log_get_errorcount(0) )
+ g10_exit(2);
+
+ g10_opt_homedir = opt.homedir;
+
+ if( opt.verbose > 1 )
+ set_packet_list_mode(1);
+
+ if( !nrings ) /* no keyring given: use default one */
+ keydb_add_resource ("trustedkeys" EXTSEP_S "gpg", 0, 0);
+ for(sl = nrings; sl; sl = sl->next )
+ keydb_add_resource (sl->d, 0, 0 );
+
+ FREE_STRLIST(nrings);
+
+ if( (rc = verify_signatures( argc, argv ) ))
+ log_error("verify signatures failed: %s\n", g10_errstr(rc) );
+
+ /* cleanup */
+ g10_exit(0);
+ return 8; /*NEVER REACHED*/
+}
+
+
+void
+g10_exit( int rc )
+{
+ rc = rc? rc : log_get_errorcount(0)? 2 :
+ g10_errors_seen? 1 : 0;
+ exit(rc );
+}
+
+
+/* Stub:
+ * We have to override the trustcheck from pkclist.c becuase
+ * this utility assumes that all keys in the keyring are trustworthy
+ */
+int
+check_signatures_trust( PKT_signature *sig )
+{
+ return 0;
+}
+
+void
+read_trust_options(byte *trust_model,ulong *created,ulong *nextcheck,
+ byte *marginals,byte *completes,byte *cert_depth) {}
+
+/* Stub:
+ * We don't have the trustdb , so we have to provide some stub functions
+ * instead
+ */
+
+int
+cache_disabled_value(PKT_public_key *pk)
+{
+ return 0;
+}
+
+void
+check_trustdb_stale(void) {}
+
+int
+get_validity_info (PKT_public_key *pk, PKT_user_id *uid)
+{
+ return '?';
+}
+
+unsigned int
+get_validity (PKT_public_key *pk, PKT_user_id *uid)
+{
+ return 0;
+}
+
+const char *
+trust_value_to_string (unsigned int value)
+{
+ return "err";
+}
+
+const char *
+uid_trust_string_fixed(PKT_public_key *key,PKT_user_id *uid)
+{
+ return "err";
+}
+
+int
+get_ownertrust_info (PKT_public_key *pk)
+{
+ return '?';
+}
+
+unsigned int
+get_ownertrust (PKT_public_key *pk)
+{
+ return TRUST_UNKNOWN;
+}
+
+
+/* Stubs:
+ * Because we only work with trusted keys, it does not make sense to
+ * get them from a keyserver
+ */
+
+struct keyserver_spec *
+keyserver_match(struct keyserver_spec *spec) { return NULL; }
+
+int
+keyserver_import_keyid( u32 *keyid, void *dummy )
+{
+ return -1;
+}
+
+int
+keyserver_import_cert(const char *name) { return -1; }
+
+int
+keyserver_import_pka(const char *name,unsigned char *fpr) { return -1; }
+
+int
+keyserver_import_name(const char *name,struct keyserver_spec *spec)
+{
+ return -1;
+}
+
+int
+keyserver_import_ldap(const char *name) { return -1; }
+
+/* Stub:
+ * No encryption here but mainproc links to these functions.
+ */
+int
+get_session_key( PKT_pubkey_enc *k, DEK *dek )
+{
+ return G10ERR_GENERAL;
+}
+/* Stub: */
+int
+get_override_session_key( DEK *dek, const char *string )
+{
+ return G10ERR_GENERAL;
+}
+/* Stub: */
+int
+decrypt_data( void *procctx, PKT_encrypted *ed, DEK *dek )
+{
+ return G10ERR_GENERAL;
+}
+
+
+/* Stub:
+ * No interactive commnds, so we don't need the helptexts
+ */
+void
+display_online_help( const char *keyword )
+{
+}
+
+/* Stub:
+ * We don't use secret keys, but getkey.c links to this
+ */
+int
+check_secret_key( PKT_secret_key *sk, int n )
+{
+ return G10ERR_GENERAL;
+}
+
+/* Stub:
+ * No secret key, so no passphrase needed
+ */
+DEK *
+passphrase_to_dek( u32 *keyid, int pubkey_algo,
+ int cipher_algo, STRING2KEY *s2k, int mode,
+ const char *tmp, int *canceled)
+{
+ if (canceled)
+ *canceled = 0;
+ return NULL;
+}
+
+struct keyserver_spec *parse_preferred_keyserver(PKT_signature *sig) {return NULL;}
+struct keyserver_spec *parse_keyserver_uri(const char *uri,int require_scheme,
+ const char *configname,
+ unsigned int configlineno)
+{
+ return NULL;
+}
+
+void free_keyserver_spec(struct keyserver_spec *keyserver) {}
+
+/* Stubs to avoid linking to photoid.c */
+void show_photos(const struct user_attribute *attrs,int count,PKT_public_key *pk) {}
+int parse_image_header(const struct user_attribute *attr,byte *type,u32 *len) {return 0;}
+char *image_type_to_string(byte type,int string) {return NULL;}
+
+#ifdef ENABLE_CARD_SUPPORT
+int agent_scd_getattr (const char *name, struct agent_card_info_s *info) {return 0;}
+#endif /* ENABLE_CARD_SUPPORT */
+
+/* Stubs to void linking to ../cipher/cipher.c */
+int string_to_cipher_algo( const char *string ) { return 0; }
+const char *cipher_algo_to_string( int algo ) { return "?";}
+void disable_cipher_algo( int algo ) {}
+int check_cipher_algo( int algo ) { return -1;}
+unsigned int cipher_get_keylen( int algo ) { return 0; }
+unsigned int cipher_get_blocksize( int algo ) {return 0;}
+CIPHER_HANDLE cipher_open( int algo, int mode, int secure ) { return NULL;}
+void cipher_close( CIPHER_HANDLE c ) {}
+int cipher_setkey( CIPHER_HANDLE c, byte *key, unsigned keylen ) { return -1;}
+void cipher_setiv( CIPHER_HANDLE c, const byte *iv, unsigned ivlen ){}
+void cipher_encrypt( CIPHER_HANDLE c, byte *outbuf,
+ byte *inbuf, unsigned nbytes ) {}
+void cipher_decrypt( CIPHER_HANDLE c, byte *outbuf,
+ byte *inbuf, unsigned nbytes ) {}
+void cipher_sync( CIPHER_HANDLE c ) {}
+
+/* Stubs to avoid linking to ../cipher/random.c */
+void random_dump_stats(void) {}
+int quick_random_gen( int onoff ) { return -1;}
+void randomize_buffer( byte *buffer, size_t length, int level ) {}
+int random_is_faked() { return -1;}
+byte *get_random_bits( size_t nbits, int level, int secure ) { return NULL;}
+void set_random_seed_file( const char *name ) {}
+void update_random_seed_file() {}
+void fast_random_poll() {}
+
+/* Stubs to avoid linking of ../cipher/primegen.c */
+void register_primegen_progress ( void (*cb)( void *, int), void *cb_data ) {}
+MPI generate_secret_prime( unsigned nbits ) { return NULL;}
+MPI generate_public_prime( unsigned nbits ) { return NULL;}
+MPI generate_elg_prime( int mode, unsigned pbits, unsigned qbits,
+ MPI g, MPI **ret_factors ) { return NULL;}
+
+/* Do not link to ../cipher/rndlinux.c */
+void rndlinux_constructor(void) {}
+
+
+/* Stubs to avoid linking to ../util/ttyio.c */
+int tty_batchmode( int onoff ) { return 0; }
+void tty_printf( const char *fmt, ... ) { }
+void tty_fprintf (FILE *fp, const char *fmt, ... ) { }
+void tty_print_string( const byte *p, size_t n ) { }
+void tty_print_utf8_string( const byte *p, size_t n ) {}
+void tty_print_utf8_string2( const byte *p, size_t n, size_t max_n ) {}
+char *tty_get( const char *prompt ) { return NULL;}
+char *tty_get_hidden( const char *prompt ) {return NULL; }
+void tty_kill_prompt(void) {}
+int tty_get_answer_is_yes( const char *prompt ) {return 0;}
+int tty_no_terminal(int onoff) {return 0;}
+#ifdef HAVE_LIBREADLINE
+void tty_enable_completion(rl_completion_func_t *completer) {}
+void tty_disable_completion(void) {}
+#endif
+
+/* We do not do any locking, so use these stubs here */
+void disable_dotlock(void) {}
+DOTLOCK create_dotlock( const char *file_to_lock ) { return NULL; }
+void destroy_dotlock (DOTLOCK h) {}
+int make_dotlock( DOTLOCK h, long timeout ) { return 0;}
+int release_dotlock( DOTLOCK h ) {return 0;}
+void remove_lockfiles(void) {}
diff --git a/g10/helptext.c b/g10/helptext.c
new file mode 100644
index 0000000..e1f7851
--- /dev/null
+++ b/g10/helptext.c
@@ -0,0 +1,300 @@
+/* helptext.c - English help texts
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002,
+ * 2004 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "util.h"
+#include "ttyio.h"
+#include "main.h"
+#include "i18n.h"
+
+
+/****************
+ * These helptexts are used for the "online" help feature. We use
+ * a key consisting of words and dots. Because the lookup is only
+ * done in an interactive mode on a user request (when she enters a "?"
+ * as response to a prompt) we can use a simple search through the list.
+ *
+ * Mini glossary:
+ *
+ * "user ID", "trustdb", "NOTE" and "WARNING".
+ */
+
+static struct helptexts { const char *key; const char *help; } helptexts[] = {
+
+/* begin of list */
+
+{ "edit_ownertrust.value", N_(
+"It's up to you to assign a value here; this value will never be exported\n"
+"to any 3rd party. We need it to implement the web-of-trust; it has nothing\n"
+"to do with the (implicitly created) web-of-certificates."
+)},
+
+{ "edit_ownertrust.set_ultimate.okay", N_(
+ "To build the Web-of-Trust, GnuPG needs to know which keys are\n"
+ "ultimately trusted - those are usually the keys for which you have\n"
+ "access to the secret key. Answer \"yes\" to set this key to\n"
+ "ultimately trusted\n"
+)},
+
+{ "untrusted_key.override", N_(
+"If you want to use this untrusted key anyway, answer \"yes\"."
+)},
+
+{ "pklist.user_id.enter", N_(
+"Enter the user ID of the addressee to whom you want to send the message."
+)},
+
+{ "keygen.algo", N_(
+"Select the algorithm to use.\n"
+"\n"
+"DSA (aka DSS) is the Digital Signature Algorithm and can only be used\n"
+"for signatures.\n"
+"\n"
+"Elgamal is an encrypt-only algorithm.\n"
+"\n"
+"RSA may be used for signatures or encryption.\n"
+"\n"
+"The first (primary) key must always be a key which is capable of signing."
+)},
+
+
+{ "keygen.algo.rsa_se", N_(
+"In general it is not a good idea to use the same key for signing and\n"
+"encryption. This algorithm should only be used in certain domains.\n"
+"Please consult your security expert first."
+)},
+
+
+{ "keygen.size", N_(
+ "Enter the size of the key"
+)},
+
+{ "keygen.size.huge.okay", N_(
+ "Answer \"yes\" or \"no\""
+)},
+
+
+{ "keygen.size.large.okay", N_(
+ "Answer \"yes\" or \"no\""
+)},
+
+
+{ "keygen.valid", N_(
+ "Enter the required value as shown in the prompt.\n"
+ "It is possible to enter a ISO date (YYYY-MM-DD) but you won't\n"
+ "get a good error response - instead the system tries to interpret\n"
+ "the given value as an interval."
+)},
+
+{ "keygen.valid.okay", N_(
+ "Answer \"yes\" or \"no\""
+)},
+
+
+{ "keygen.name", N_(
+ "Enter the name of the key holder"
+)},
+
+
+{ "keygen.email", N_(
+ "please enter an optional but highly suggested email address"
+)},
+
+{ "keygen.comment", N_(
+ "Please enter an optional comment"
+)},
+
+
+{ "keygen.userid.cmd", N_(
+ ""
+"N to change the name.\n"
+"C to change the comment.\n"
+"E to change the email address.\n"
+"O to continue with key generation.\n"
+"Q to to quit the key generation."
+)},
+
+{ "keygen.sub.okay", N_(
+ "Answer \"yes\" (or just \"y\") if it is okay to generate the sub key."
+)},
+
+{ "sign_uid.okay", N_(
+ "Answer \"yes\" or \"no\""
+)},
+
+{ "sign_uid.class", N_(
+"When you sign a user ID on a key, you should first verify that the key\n"
+"belongs to the person named in the user ID. It is useful for others to\n"
+"know how carefully you verified this.\n\n"
+"\"0\" means you make no particular claim as to how carefully you verified the\n"
+" key.\n\n"
+"\"1\" means you believe the key is owned by the person who claims to own it\n"
+" but you could not, or did not verify the key at all. This is useful for\n"
+" a \"persona\" verification, where you sign the key of a pseudonymous user.\n\n"
+"\"2\" means you did casual verification of the key. For example, this could\n"
+" mean that you verified the key fingerprint and checked the user ID on the\n"
+" key against a photo ID.\n\n"
+"\"3\" means you did extensive verification of the key. For example, this could\n"
+" mean that you verified the key fingerprint with the owner of the key in\n"
+" person, and that you checked, by means of a hard to forge document with a\n"
+" photo ID (such as a passport) that the name of the key owner matches the\n"
+" name in the user ID on the key, and finally that you verified (by exchange\n"
+" of email) that the email address on the key belongs to the key owner.\n\n"
+"Note that the examples given above for levels 2 and 3 are *only* examples.\n"
+"In the end, it is up to you to decide just what \"casual\" and \"extensive\"\n"
+"mean to you when you sign other keys.\n\n"
+"If you don't know what the right answer is, answer \"0\"."
+)},
+
+{ "change_passwd.empty.okay", N_(
+ "Answer \"yes\" or \"no\""
+)},
+
+
+{ "keyedit.save.okay", N_(
+ "Answer \"yes\" or \"no\""
+)},
+
+
+{ "keyedit.cancel.okay", N_(
+ "Answer \"yes\" or \"no\""
+)},
+
+{ "keyedit.sign_all.okay", N_(
+ "Answer \"yes\" if you want to sign ALL the user IDs"
+)},
+
+{ "keyedit.remove.uid.okay", N_(
+ "Answer \"yes\" if you really want to delete this user ID.\n"
+ "All certificates are then also lost!"
+)},
+
+{ "keyedit.remove.subkey.okay", N_(
+ "Answer \"yes\" if it is okay to delete the subkey"
+)},
+
+
+{ "keyedit.delsig.valid", N_(
+ "This is a valid signature on the key; you normally don't want\n"
+ "to delete this signature because it may be important to establish a\n"
+ "trust connection to the key or another key certified by this key."
+)},
+{ "keyedit.delsig.unknown", N_(
+ "This signature can't be checked because you don't have the\n"
+ "corresponding key. You should postpone its deletion until you\n"
+ "know which key was used because this signing key might establish\n"
+ "a trust connection through another already certified key."
+)},
+{ "keyedit.delsig.invalid", N_(
+ "The signature is not valid. It does make sense to remove it from\n"
+ "your keyring."
+)},
+{ "keyedit.delsig.selfsig", N_(
+ "This is a signature which binds the user ID to the key. It is\n"
+ "usually not a good idea to remove such a signature. Actually\n"
+ "GnuPG might not be able to use this key anymore. So do this\n"
+ "only if this self-signature is for some reason not valid and\n"
+ "a second one is available."
+)},
+
+{ "keyedit.updpref.okay", N_(
+ "Change the preferences of all user IDs (or just of the selected ones)\n"
+ "to the current list of preferences. The timestamp of all affected\n"
+ "self-signatures will be advanced by one second.\n"
+)},
+
+
+{ "passphrase.enter", N_(
+ ""
+"Please enter the passhrase; this is a secret sentence \n"
+)},
+
+
+{ "passphrase.repeat", N_(
+ "Please repeat the last passphrase, so you are sure what you typed in."
+)},
+
+{ "detached_signature.filename", N_(
+ "Give the name of the file to which the signature applies"
+)},
+
+/* openfile.c (overwrite_filep) */
+{ "openfile.overwrite.okay", N_(
+ "Answer \"yes\" if it is okay to overwrite the file"
+)},
+
+/* openfile.c (ask_outfile_name) */
+{ "openfile.askoutname", N_(
+ "Please enter a new filename. If you just hit RETURN the default\n"
+ "file (which is shown in brackets) will be used."
+)},
+
+/* revoke.c (ask_revocation_reason) */
+{ "ask_revocation_reason.code", N_(
+ "You should specify a reason for the certification. Depending on the\n"
+ "context you have the ability to choose from this list:\n"
+ " \"Key has been compromised\"\n"
+ " Use this if you have a reason to believe that unauthorized persons\n"
+ " got access to your secret key.\n"
+ " \"Key is superseded\"\n"
+ " Use this if you have replaced this key with a newer one.\n"
+ " \"Key is no longer used\"\n"
+ " Use this if you have retired this key.\n"
+ " \"User ID is no longer valid\"\n"
+ " Use this to state that the user ID should not longer be used;\n"
+ " this is normally used to mark an email address invalid.\n"
+)},
+
+/* revoke.c (ask_revocation_reason) */
+{ "ask_revocation_reason.text", N_(
+ "If you like, you can enter a text describing why you issue this\n"
+ "revocation certificate. Please keep this text concise.\n"
+ "An empty line ends the text.\n"
+)},
+
+/* end of list */
+{ NULL, NULL } };
+
+
+void
+display_online_help( const char *keyword )
+{
+
+ tty_kill_prompt();
+ if( !keyword )
+ tty_printf(_("No help available") );
+ else {
+ const char *p;
+ int i;
+
+ for(i=0; (p=helptexts[i].key) && strcmp( p, keyword ); i++ )
+ ;
+ if( !p || !*helptexts[i].help )
+ tty_printf(_("No help available for `%s'"), keyword );
+ else
+ tty_printf("%s", _(helptexts[i].help) );
+ }
+ tty_printf("\n");
+}
diff --git a/g10/import.c b/g10/import.c
new file mode 100644
index 0000000..7260301
--- /dev/null
+++ b/g10/import.c
@@ -0,0 +1,2396 @@
+/* import.c - import a key into our key storage.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "options.h"
+#include "packet.h"
+#include "errors.h"
+#include "keydb.h"
+#include "memory.h"
+#include "util.h"
+#include "trustdb.h"
+#include "main.h"
+#include "i18n.h"
+#include "ttyio.h"
+#include "status.h"
+#include "keyserver-internal.h"
+
+struct stats_s {
+ ulong count;
+ ulong no_user_id;
+ ulong imported;
+ ulong imported_rsa;
+ ulong n_uids;
+ ulong n_sigs;
+ ulong n_subk;
+ ulong unchanged;
+ ulong n_revoc;
+ ulong secret_read;
+ ulong secret_imported;
+ ulong secret_dups;
+ ulong skipped_new_keys;
+ ulong not_imported;
+ ulong n_sigs_cleaned;
+ ulong n_uids_cleaned;
+};
+
+
+static int import( IOBUF inp, const char* fname,struct stats_s *stats,
+ unsigned char **fpr,size_t *fpr_len,unsigned int options );
+static int read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root );
+static void revocation_present(KBNODE keyblock);
+static int import_one(const char *fname, KBNODE keyblock,struct stats_s *stats,
+ unsigned char **fpr,size_t *fpr_len,
+ unsigned int options,int from_sk);
+static int import_secret_one( const char *fname, KBNODE keyblock,
+ struct stats_s *stats, unsigned int options);
+static int import_revoke_cert( const char *fname, KBNODE node,
+ struct stats_s *stats);
+static int chk_self_sigs( const char *fname, KBNODE keyblock,
+ PKT_public_key *pk, u32 *keyid, int *non_self );
+static int delete_inv_parts( const char *fname, KBNODE keyblock,
+ u32 *keyid, unsigned int options );
+static int merge_blocks( const char *fname, KBNODE keyblock_orig,
+ KBNODE keyblock, u32 *keyid,
+ int *n_uids, int *n_sigs, int *n_subk );
+static int append_uid( KBNODE keyblock, KBNODE node, int *n_sigs,
+ const char *fname, u32 *keyid );
+static int append_key( KBNODE keyblock, KBNODE node, int *n_sigs,
+ const char *fname, u32 *keyid );
+static int merge_sigs( KBNODE dst, KBNODE src, int *n_sigs,
+ const char *fname, u32 *keyid );
+static int merge_keysigs( KBNODE dst, KBNODE src, int *n_sigs,
+ const char *fname, u32 *keyid );
+
+int
+parse_import_options(char *str,unsigned int *options,int noisy)
+{
+ struct parse_options import_opts[]=
+ {
+ {"import-local-sigs",IMPORT_LOCAL_SIGS,NULL,
+ N_("import signatures that are marked as local-only")},
+ {"repair-pks-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL,
+ N_("repair damage from the pks keyserver during import")},
+ {"fast-import",IMPORT_FAST,NULL,
+ N_("do not update the trustdb after import")},
+ {"convert-sk-to-pk",IMPORT_SK2PK,NULL,
+ N_("create a public key when importing a secret key")},
+ {"merge-only",IMPORT_MERGE_ONLY,NULL,
+ N_("only accept updates to existing keys")},
+ {"import-clean",IMPORT_CLEAN,NULL,
+ N_("remove unusable parts from key after import")},
+ {"import-minimal",IMPORT_MINIMAL|IMPORT_CLEAN,NULL,
+ N_("remove as much as possible from key after import")},
+ /* Aliases for backward compatibility */
+ {"allow-local-sigs",IMPORT_LOCAL_SIGS,NULL,NULL},
+ {"repair-hkp-subkey-bug",IMPORT_REPAIR_PKS_SUBKEY_BUG,NULL,NULL},
+ /* dummy */
+ {"import-unusable-sigs",0,NULL,NULL},
+ {"import-clean-sigs",0,NULL,NULL},
+ {"import-clean-uids",0,NULL,NULL},
+ {NULL,0,NULL,NULL}
+ };
+
+ return parse_options(str,options,import_opts,noisy);
+}
+
+void *
+import_new_stats_handle (void)
+{
+ return xmalloc_clear ( sizeof (struct stats_s) );
+}
+
+void
+import_release_stats_handle (void *p)
+{
+ xfree (p);
+}
+
+/****************
+ * Import the public keys from the given filename. Input may be armored.
+ * This function rejects all keys which are not validly self signed on at
+ * least one userid. Only user ids which are self signed will be imported.
+ * Other signatures are not checked.
+ *
+ * Actually this function does a merge. It works like this:
+ *
+ * - get the keyblock
+ * - check self-signatures and remove all userids and their signatures
+ * without/invalid self-signatures.
+ * - reject the keyblock, if we have no valid userid.
+ * - See whether we have this key already in one of our pubrings.
+ * If not, simply add it to the default keyring.
+ * - Compare the key and the self-signatures of the new and the one in
+ * our keyring. If they are different something weird is going on;
+ * ask what to do.
+ * - See whether we have only non-self-signature on one user id; if not
+ * ask the user what to do.
+ * - compare the signatures: If we already have this signature, check
+ * that they compare okay; if not, issue a warning and ask the user.
+ * (consider looking at the timestamp and use the newest?)
+ * - Simply add the signature. Can't verify here because we may not have
+ * the signature's public key yet; verification is done when putting it
+ * into the trustdb, which is done automagically as soon as this pubkey
+ * is used.
+ * - Proceed with next signature.
+ *
+ * Key revocation certificates have special handling.
+ *
+ */
+static int
+import_keys_internal( IOBUF inp, char **fnames, int nnames,
+ void *stats_handle, unsigned char **fpr, size_t *fpr_len,
+ unsigned int options )
+{
+ int i, rc = 0;
+ struct stats_s *stats = stats_handle;
+
+ if (!stats)
+ stats = import_new_stats_handle ();
+
+ if (inp) {
+ rc = import( inp, "[stream]", stats, fpr, fpr_len, options);
+ }
+ else {
+ if( !fnames && !nnames )
+ nnames = 1; /* Ohh what a ugly hack to jump into the loop */
+
+ for(i=0; i < nnames; i++ ) {
+ const char *fname = fnames? fnames[i] : NULL;
+ IOBUF inp2 = iobuf_open(fname);
+ if( !fname )
+ fname = "[stdin]";
+ if (inp2 && is_secured_file (iobuf_get_fd (inp2)))
+ {
+ iobuf_close (inp2);
+ inp2 = NULL;
+ errno = EPERM;
+ }
+ if( !inp2 )
+ log_error(_("can't open `%s': %s\n"), fname, strerror(errno) );
+ else
+ {
+ rc = import( inp2, fname, stats, fpr, fpr_len, options );
+ iobuf_close(inp2);
+ /* Must invalidate that ugly cache to actually close it. */
+ iobuf_ioctl (NULL, 2, 0, (char*)fname);
+ if( rc )
+ log_error("import from `%s' failed: %s\n", fname,
+ g10_errstr(rc) );
+ }
+ if( !fname )
+ break;
+ }
+ }
+ if (!stats_handle) {
+ import_print_stats (stats);
+ import_release_stats_handle (stats);
+ }
+
+ /* If no fast import and the trustdb is dirty (i.e. we added a key
+ or userID that had something other than a selfsig, a signature
+ that was other than a selfsig, or any revocation), then
+ update/check the trustdb if the user specified by setting
+ interactive or by not setting no-auto-check-trustdb */
+
+ if(!(options&IMPORT_FAST))
+ trustdb_check_or_update();
+
+ return rc;
+}
+
+void
+import_keys( char **fnames, int nnames,
+ void *stats_handle, unsigned int options )
+{
+ import_keys_internal(NULL,fnames,nnames,stats_handle,NULL,NULL,options);
+}
+
+int
+import_keys_stream( IOBUF inp, void *stats_handle,
+ unsigned char **fpr, size_t *fpr_len,unsigned int options )
+{
+ return import_keys_internal(inp,NULL,0,stats_handle,fpr,fpr_len,options);
+}
+
+static int
+import( IOBUF inp, const char* fname,struct stats_s *stats,
+ unsigned char **fpr,size_t *fpr_len,unsigned int options )
+{
+ PACKET *pending_pkt = NULL;
+ KBNODE keyblock = NULL;
+ int rc = 0;
+
+ getkey_disable_caches();
+
+ if( !opt.no_armor ) { /* armored reading is not disabled */
+ armor_filter_context_t *afx = new_armor_context ();
+ afx->only_keyblocks = 1;
+ push_armor_filter (afx, inp);
+ release_armor_context (afx);
+ }
+
+ while( !(rc = read_block( inp, &pending_pkt, &keyblock) )) {
+ if( keyblock->pkt->pkttype == PKT_PUBLIC_KEY )
+ rc = import_one( fname, keyblock, stats, fpr, fpr_len, options, 0);
+ else if( keyblock->pkt->pkttype == PKT_SECRET_KEY )
+ rc = import_secret_one( fname, keyblock, stats, options );
+ else if( keyblock->pkt->pkttype == PKT_SIGNATURE
+ && keyblock->pkt->pkt.signature->sig_class == 0x20 )
+ rc = import_revoke_cert( fname, keyblock, stats );
+ else {
+ log_info( _("skipping block of type %d\n"),
+ keyblock->pkt->pkttype );
+ }
+ release_kbnode(keyblock);
+ /* fixme: we should increment the not imported counter but this
+ does only make sense if we keep on going despite of errors. */
+ if( rc )
+ break;
+ if( !(++stats->count % 100) && !opt.quiet )
+ log_info(_("%lu keys processed so far\n"), stats->count );
+ }
+ if( rc == -1 )
+ rc = 0;
+ else if( rc && rc != G10ERR_INV_KEYRING )
+ log_error( _("error reading `%s': %s\n"), fname, g10_errstr(rc));
+
+ return rc;
+}
+
+
+void
+import_print_stats (void *hd)
+{
+ struct stats_s *stats = hd;
+
+ if( !opt.quiet ) {
+ log_info(_("Total number processed: %lu\n"), stats->count );
+ if( stats->skipped_new_keys )
+ log_info(_(" skipped new keys: %lu\n"),
+ stats->skipped_new_keys );
+ if( stats->no_user_id )
+ log_info(_(" w/o user IDs: %lu\n"), stats->no_user_id );
+ if( stats->imported || stats->imported_rsa ) {
+ log_info(_(" imported: %lu"), stats->imported );
+ if( stats->imported_rsa )
+ fprintf(stderr, " (RSA: %lu)", stats->imported_rsa );
+ putc('\n', stderr);
+ }
+ if( stats->unchanged )
+ log_info(_(" unchanged: %lu\n"), stats->unchanged );
+ if( stats->n_uids )
+ log_info(_(" new user IDs: %lu\n"), stats->n_uids );
+ if( stats->n_subk )
+ log_info(_(" new subkeys: %lu\n"), stats->n_subk );
+ if( stats->n_sigs )
+ log_info(_(" new signatures: %lu\n"), stats->n_sigs );
+ if( stats->n_revoc )
+ log_info(_(" new key revocations: %lu\n"), stats->n_revoc );
+ if( stats->secret_read )
+ log_info(_(" secret keys read: %lu\n"), stats->secret_read );
+ if( stats->secret_imported )
+ log_info(_(" secret keys imported: %lu\n"), stats->secret_imported );
+ if( stats->secret_dups )
+ log_info(_(" secret keys unchanged: %lu\n"), stats->secret_dups );
+ if( stats->not_imported )
+ log_info(_(" not imported: %lu\n"), stats->not_imported );
+ if( stats->n_sigs_cleaned)
+ log_info(_(" signatures cleaned: %lu\n"),stats->n_sigs_cleaned);
+ if( stats->n_uids_cleaned)
+ log_info(_(" user IDs cleaned: %lu\n"),stats->n_uids_cleaned);
+ }
+
+ if( is_status_enabled() ) {
+ char buf[14*20];
+ sprintf(buf, "%lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu %lu",
+ stats->count,
+ stats->no_user_id,
+ stats->imported,
+ stats->imported_rsa,
+ stats->unchanged,
+ stats->n_uids,
+ stats->n_subk,
+ stats->n_sigs,
+ stats->n_revoc,
+ stats->secret_read,
+ stats->secret_imported,
+ stats->secret_dups,
+ stats->skipped_new_keys,
+ stats->not_imported );
+ write_status_text( STATUS_IMPORT_RES, buf );
+ }
+}
+
+
+/****************
+ * Read the next keyblock from stream A.
+ * PENDING_PKT should be initialzed to NULL
+ * and not chnaged form the caller.
+ * Retunr: 0 = okay, -1 no more blocks or another errorcode.
+ */
+static int
+read_block( IOBUF a, PACKET **pending_pkt, KBNODE *ret_root )
+{
+ int rc;
+ PACKET *pkt;
+ KBNODE root = NULL;
+ int in_cert;
+
+ if( *pending_pkt ) {
+ root = new_kbnode( *pending_pkt );
+ *pending_pkt = NULL;
+ in_cert = 1;
+ }
+ else
+ in_cert = 0;
+ pkt = xmalloc( sizeof *pkt );
+ init_packet(pkt);
+ while( (rc=parse_packet(a, pkt)) != -1 ) {
+ if( rc ) { /* ignore errors */
+ if( rc != G10ERR_UNKNOWN_PACKET ) {
+ log_error("read_block: read error: %s\n", g10_errstr(rc) );
+ rc = G10ERR_INV_KEYRING;
+ goto ready;
+ }
+ free_packet( pkt );
+ init_packet(pkt);
+ continue;
+ }
+
+ if( !root && pkt->pkttype == PKT_SIGNATURE
+ && pkt->pkt.signature->sig_class == 0x20 ) {
+ /* this is a revocation certificate which is handled
+ * in a special way */
+ root = new_kbnode( pkt );
+ pkt = NULL;
+ goto ready;
+ }
+
+ /* make a linked list of all packets */
+ switch( pkt->pkttype ) {
+ case PKT_COMPRESSED:
+ if(check_compress_algo(pkt->pkt.compressed->algorithm))
+ {
+ rc = G10ERR_COMPR_ALGO;
+ goto ready;
+ }
+ else
+ {
+ compress_filter_context_t *cfx = xmalloc_clear( sizeof *cfx );
+ pkt->pkt.compressed->buf = NULL;
+ push_compress_filter2(a,cfx,pkt->pkt.compressed->algorithm,1);
+ }
+ free_packet( pkt );
+ init_packet(pkt);
+ break;
+
+ case PKT_RING_TRUST:
+ /* skip those packets */
+ free_packet( pkt );
+ init_packet(pkt);
+ break;
+
+ case PKT_PUBLIC_KEY:
+ case PKT_SECRET_KEY:
+ if( in_cert ) { /* store this packet */
+ *pending_pkt = pkt;
+ pkt = NULL;
+ goto ready;
+ }
+ in_cert = 1;
+ default:
+ if( in_cert ) {
+ if( !root )
+ root = new_kbnode( pkt );
+ else
+ add_kbnode( root, new_kbnode( pkt ) );
+ pkt = xmalloc( sizeof *pkt );
+ }
+ init_packet(pkt);
+ break;
+ }
+ }
+ ready:
+ if( rc == -1 && root )
+ rc = 0;
+
+ if( rc )
+ release_kbnode( root );
+ else
+ *ret_root = root;
+ free_packet( pkt );
+ xfree( pkt );
+ return rc;
+}
+
+/* Walk through the subkeys on a pk to find if we have the PKS
+ disease: multiple subkeys with their binding sigs stripped, and the
+ sig for the first subkey placed after the last subkey. That is,
+ instead of "pk uid sig sub1 bind1 sub2 bind2 sub3 bind3" we have
+ "pk uid sig sub1 sub2 sub3 bind1". We can't do anything about sub2
+ and sub3, as they are already lost, but we can try and rescue sub1
+ by reordering the keyblock so that it reads "pk uid sig sub1 bind1
+ sub2 sub3". Returns TRUE if the keyblock was modified. */
+
+static int
+fix_pks_corruption(KBNODE keyblock)
+{
+ int changed=0,keycount=0;
+ KBNODE node,last=NULL,sknode=NULL;
+
+ /* First determine if we have the problem at all. Look for 2 or
+ more subkeys in a row, followed by a single binding sig. */
+ for(node=keyblock;node;last=node,node=node->next)
+ {
+ if(node->pkt->pkttype==PKT_PUBLIC_SUBKEY)
+ {
+ keycount++;
+ if(!sknode)
+ sknode=node;
+ }
+ else if(node->pkt->pkttype==PKT_SIGNATURE &&
+ node->pkt->pkt.signature->sig_class==0x18 &&
+ keycount>=2 && node->next==NULL)
+ {
+ /* We might have the problem, as this key has two subkeys in
+ a row without any intervening packets. */
+
+ /* Sanity check */
+ if(last==NULL)
+ break;
+
+ /* Temporarily attach node to sknode. */
+ node->next=sknode->next;
+ sknode->next=node;
+ last->next=NULL;
+
+ /* Note we aren't checking whether this binding sig is a
+ selfsig. This is not necessary here as the subkey and
+ binding sig will be rejected later if that is the
+ case. */
+ if(check_key_signature(keyblock,node,NULL))
+ {
+ /* Not a match, so undo the changes. */
+ sknode->next=node->next;
+ last->next=node;
+ node->next=NULL;
+ break;
+ }
+ else
+ {
+ sknode->flag |= 1; /* Mark it good so we don't need to
+ check it again */
+ changed=1;
+ break;
+ }
+ }
+ else
+ keycount=0;
+ }
+
+ return changed;
+}
+
+
+static void
+print_import_ok (PKT_public_key *pk, PKT_secret_key *sk, unsigned int reason)
+{
+ byte array[MAX_FINGERPRINT_LEN], *s;
+ char buf[MAX_FINGERPRINT_LEN*2+30], *p;
+ size_t i, n;
+
+ sprintf (buf, "%u ", reason);
+ p = buf + strlen (buf);
+
+ if (pk)
+ fingerprint_from_pk (pk, array, &n);
+ else
+ fingerprint_from_sk (sk, array, &n);
+ s = array;
+ for (i=0; i < n ; i++, s++, p += 2)
+ sprintf (p, "%02X", *s);
+
+ write_status_text (STATUS_IMPORT_OK, buf);
+}
+
+static void
+print_import_check (PKT_public_key * pk, PKT_user_id * id)
+{
+ char * buf;
+ byte fpr[24];
+ u32 keyid[2];
+ size_t i, pos = 0, n;
+
+ buf = xmalloc (17+41+id->len+32);
+ keyid_from_pk (pk, keyid);
+ sprintf (buf, "%08X%08X ", keyid[0], keyid[1]);
+ pos = 17;
+ fingerprint_from_pk (pk, fpr, &n);
+ for (i = 0; i < n; i++, pos += 2)
+ sprintf (buf+pos, "%02X", fpr[i]);
+ strcat (buf, " ");
+ pos += 1;
+ strcat (buf, id->name);
+ write_status_text (STATUS_IMPORT_CHECK, buf);
+ xfree (buf);
+}
+
+static void
+check_prefs_warning(PKT_public_key *pk)
+{
+ log_info(_("WARNING: key %s contains preferences for unavailable\n"),
+ keystr_from_pk(pk));
+ /* TRANSLATORS: This string is belongs to the previous one. They are
+ only split up to allow printing of a common prefix. */
+ log_info(_(" algorithms on these user IDs:\n"));
+}
+
+static void
+check_prefs(KBNODE keyblock)
+{
+ KBNODE node;
+ PKT_public_key *pk;
+ int problem=0;
+
+ merge_keys_and_selfsig(keyblock);
+ pk=keyblock->pkt->pkt.public_key;
+
+ for(node=keyblock;node;node=node->next)
+ {
+ if(node->pkt->pkttype==PKT_USER_ID
+ && node->pkt->pkt.user_id->created
+ && node->pkt->pkt.user_id->prefs)
+ {
+ PKT_user_id *uid=node->pkt->pkt.user_id;
+ prefitem_t *prefs=uid->prefs;
+ char *user=utf8_to_native(uid->name,strlen(uid->name),0);
+
+ for(;prefs->type;prefs++)
+ {
+ char num[10]; /* prefs->value is a byte, so we're over
+ safe here */
+
+ sprintf(num,"%u",prefs->value);
+
+ if(prefs->type==PREFTYPE_SYM)
+ {
+ if(check_cipher_algo(prefs->value))
+ {
+ const char *algo=cipher_algo_to_string(prefs->value);
+ if(!problem)
+ check_prefs_warning(pk);
+ log_info(_(" \"%s\": preference for cipher"
+ " algorithm %s\n"),user,algo?algo:num);
+ problem=1;
+ }
+ }
+ else if(prefs->type==PREFTYPE_HASH)
+ {
+ if(check_digest_algo(prefs->value))
+ {
+ const char *algo=digest_algo_to_string(prefs->value);
+ if(!problem)
+ check_prefs_warning(pk);
+ log_info(_(" \"%s\": preference for digest"
+ " algorithm %s\n"),user,algo?algo:num);
+ problem=1;
+ }
+ }
+ else if(prefs->type==PREFTYPE_ZIP)
+ {
+ if(check_compress_algo(prefs->value))
+ {
+ const char *algo=compress_algo_to_string(prefs->value);
+ if(!problem)
+ check_prefs_warning(pk);
+ log_info(_(" \"%s\": preference for compression"
+ " algorithm %s\n"),user,algo?algo:num);
+ problem=1;
+ }
+ }
+ }
+
+ xfree(user);
+ }
+ }
+
+ if(problem)
+ {
+ log_info(_("it is strongly suggested that you update"
+ " your preferences and\n"));
+ log_info(_("re-distribute this key to avoid potential algorithm"
+ " mismatch problems\n"));
+
+ if(!opt.batch)
+ {
+ STRLIST sl=NULL,locusr=NULL;
+ size_t fprlen=0;
+ byte fpr[MAX_FINGERPRINT_LEN],*p;
+ char username[(MAX_FINGERPRINT_LEN*2)+1];
+ unsigned int i;
+
+ p=fingerprint_from_pk(pk,fpr,&fprlen);
+ for(i=0;i<fprlen;i++,p++)
+ sprintf(username+2*i,"%02X",*p);
+ add_to_strlist(&locusr,username);
+
+ append_to_strlist(&sl,"updpref");
+ append_to_strlist(&sl,"save");
+
+ keyedit_menu( username, locusr, sl, 1, 1 );
+ free_strlist(sl);
+ free_strlist(locusr);
+ }
+ else if(!opt.quiet)
+ log_info(_("you can update your preferences with:"
+ " gpg --edit-key %s updpref save\n"),keystr_from_pk(pk));
+ }
+}
+
+/****************
+ * Try to import one keyblock. Return an error only in serious cases, but
+ * never for an invalid keyblock. It uses log_error to increase the
+ * internal errorcount, so that invalid input can be detected by programs
+ * which called g10.
+ */
+static int
+import_one( const char *fname, KBNODE keyblock, struct stats_s *stats,
+ unsigned char **fpr,size_t *fpr_len,unsigned int options,
+ int from_sk )
+{
+ PKT_public_key *pk;
+ PKT_public_key *pk_orig;
+ KBNODE node, uidnode;
+ KBNODE keyblock_orig = NULL;
+ u32 keyid[2];
+ int rc = 0;
+ int new_key = 0;
+ int mod_key = 0;
+ int non_self = 0;
+
+ /* get the key and print some info about it */
+ node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
+ if( !node )
+ BUG();
+
+ pk = node->pkt->pkt.public_key;
+
+ keyid_from_pk( pk, keyid );
+ uidnode = find_next_kbnode( keyblock, PKT_USER_ID );
+
+ if( opt.verbose && !opt.interactive )
+ {
+ log_info( "pub %4u%c/%s %s ",
+ nbits_from_pk( pk ),
+ pubkey_letter( pk->pubkey_algo ),
+ keystr_from_pk(pk), datestr_from_pk(pk) );
+ if( uidnode )
+ print_utf8_string( stderr, uidnode->pkt->pkt.user_id->name,
+ uidnode->pkt->pkt.user_id->len );
+ putc('\n', stderr);
+ }
+
+ if( !uidnode )
+ {
+ log_error( _("key %s: no user ID\n"), keystr_from_pk(pk));
+ return 0;
+ }
+
+ if (opt.interactive) {
+ if(is_status_enabled())
+ print_import_check (pk, uidnode->pkt->pkt.user_id);
+ merge_keys_and_selfsig (keyblock);
+ tty_printf ("\n");
+ show_basic_key_info (keyblock);
+ tty_printf ("\n");
+ if (!cpr_get_answer_is_yes ("import.okay",
+ "Do you want to import this key? (y/N) "))
+ return 0;
+ }
+
+ collapse_uids(&keyblock);
+
+ /* Clean the key that we're about to import, to cut down on things
+ that we have to clean later. This has no practical impact on
+ the end result, but does result in less logging which might
+ confuse the user. */
+ if(options&IMPORT_CLEAN)
+ clean_key(keyblock,opt.verbose,options&IMPORT_MINIMAL,NULL,NULL);
+
+ clear_kbnode_flags( keyblock );
+
+ if((options&IMPORT_REPAIR_PKS_SUBKEY_BUG) && fix_pks_corruption(keyblock)
+ && opt.verbose)
+ log_info(_("key %s: PKS subkey corruption repaired\n"),
+ keystr_from_pk(pk));
+
+ rc = chk_self_sigs( fname, keyblock , pk, keyid, &non_self );
+ if( rc )
+ return rc== -1? 0:rc;
+
+ /* If we allow such a thing, mark unsigned uids as valid */
+ if( opt.allow_non_selfsigned_uid )
+ for( node=keyblock; node; node = node->next )
+ if( node->pkt->pkttype == PKT_USER_ID && !(node->flag & 1) )
+ {
+ char *user=utf8_to_native(node->pkt->pkt.user_id->name,
+ node->pkt->pkt.user_id->len,0);
+ node->flag |= 1;
+ log_info( _("key %s: accepted non self-signed user ID \"%s\"\n"),
+ keystr_from_pk(pk),user);
+ xfree(user);
+ }
+
+ if( !delete_inv_parts( fname, keyblock, keyid, options ) ) {
+ log_error( _("key %s: no valid user IDs\n"), keystr_from_pk(pk));
+ if( !opt.quiet )
+ log_info(_("this may be caused by a missing self-signature\n"));
+ stats->no_user_id++;
+ return 0;
+ }
+
+ /* do we have this key already in one of our pubrings ? */
+ pk_orig = xmalloc_clear( sizeof *pk_orig );
+ rc = get_pubkey_fast ( pk_orig, keyid );
+ if( rc && rc != G10ERR_NO_PUBKEY && rc != G10ERR_UNU_PUBKEY )
+ {
+ log_error( _("key %s: public key not found: %s\n"),
+ keystr(keyid), g10_errstr(rc));
+ }
+ else if ( rc && (opt.import_options&IMPORT_MERGE_ONLY) )
+ {
+ if( opt.verbose )
+ log_info( _("key %s: new key - skipped\n"), keystr(keyid));
+ rc = 0;
+ stats->skipped_new_keys++;
+ }
+ else if( rc ) { /* insert this key */
+ KEYDB_HANDLE hd = keydb_new (0);
+
+ rc = keydb_locate_writable (hd, NULL);
+ if (rc) {
+ log_error (_("no writable keyring found: %s\n"), g10_errstr (rc));
+ keydb_release (hd);
+ return G10ERR_GENERAL;
+ }
+ if( opt.verbose > 1 )
+ log_info (_("writing to `%s'\n"), keydb_get_resource_name (hd) );
+
+ rc = keydb_insert_keyblock (hd, keyblock );
+ if (rc)
+ log_error (_("error writing keyring `%s': %s\n"),
+ keydb_get_resource_name (hd), g10_errstr(rc));
+ else
+ {
+ /* This should not be possible since we delete the
+ ownertrust when a key is deleted, but it can happen if
+ the keyring and trustdb are out of sync. It can also
+ be made to happen with the trusted-key command. */
+
+ clear_ownertrusts (pk);
+ if(non_self)
+ revalidation_mark ();
+ }
+ keydb_release (hd);
+
+ /* we are ready */
+ if( !opt.quiet )
+ {
+ char *p=get_user_id_native (keyid);
+ log_info( _("key %s: public key \"%s\" imported\n"),
+ keystr(keyid),p);
+ xfree(p);
+ }
+ if( is_status_enabled() )
+ {
+ char *us = get_long_user_id_string( keyid );
+ write_status_text( STATUS_IMPORTED, us );
+ xfree(us);
+ print_import_ok (pk,NULL, 1);
+ }
+ stats->imported++;
+ if( is_RSA( pk->pubkey_algo ) )
+ stats->imported_rsa++;
+ new_key = 1;
+ }
+ else { /* merge */
+ KEYDB_HANDLE hd;
+ int n_uids, n_sigs, n_subk, n_sigs_cleaned, n_uids_cleaned;
+
+ /* Compare the original against the new key; just to be sure nothing
+ * weird is going on */
+ if( cmp_public_keys( pk_orig, pk ) )
+ {
+ log_error( _("key %s: doesn't match our copy\n"),keystr(keyid));
+ goto leave;
+ }
+
+ /* now read the original keyblock */
+ hd = keydb_new (0);
+ {
+ byte afp[MAX_FINGERPRINT_LEN];
+ size_t an;
+
+ fingerprint_from_pk (pk_orig, afp, &an);
+ while (an < MAX_FINGERPRINT_LEN)
+ afp[an++] = 0;
+ rc = keydb_search_fpr (hd, afp);
+ }
+ if( rc )
+ {
+ log_error (_("key %s: can't locate original keyblock: %s\n"),
+ keystr(keyid), g10_errstr(rc));
+ keydb_release (hd);
+ goto leave;
+ }
+ rc = keydb_get_keyblock (hd, &keyblock_orig );
+ if (rc)
+ {
+ log_error (_("key %s: can't read original keyblock: %s\n"),
+ keystr(keyid), g10_errstr(rc));
+ keydb_release (hd);
+ goto leave;
+ }
+
+ /* and try to merge the block */
+ clear_kbnode_flags( keyblock_orig );
+ clear_kbnode_flags( keyblock );
+ n_uids = n_sigs = n_subk = n_sigs_cleaned = n_uids_cleaned = 0;
+ rc = merge_blocks( fname, keyblock_orig, keyblock,
+ keyid, &n_uids, &n_sigs, &n_subk );
+ if( rc )
+ {
+ keydb_release (hd);
+ goto leave;
+ }
+
+ if(options&IMPORT_CLEAN)
+ clean_key(keyblock_orig,opt.verbose,options&IMPORT_MINIMAL,
+ &n_uids_cleaned,&n_sigs_cleaned);
+
+ if( n_uids || n_sigs || n_subk || n_sigs_cleaned || n_uids_cleaned) {
+ mod_key = 1;
+ /* keyblock_orig has been updated; write */
+ rc = keydb_update_keyblock (hd, keyblock_orig);
+ if (rc)
+ log_error (_("error writing keyring `%s': %s\n"),
+ keydb_get_resource_name (hd), g10_errstr(rc) );
+ else if(non_self)
+ revalidation_mark ();
+
+ /* we are ready */
+ if( !opt.quiet )
+ {
+ char *p=get_user_id_native(keyid);
+ if( n_uids == 1 )
+ log_info( _("key %s: \"%s\" 1 new user ID\n"),
+ keystr(keyid),p);
+ else if( n_uids )
+ log_info( _("key %s: \"%s\" %d new user IDs\n"),
+ keystr(keyid),p,n_uids);
+ if( n_sigs == 1 )
+ log_info( _("key %s: \"%s\" 1 new signature\n"),
+ keystr(keyid), p);
+ else if( n_sigs )
+ log_info( _("key %s: \"%s\" %d new signatures\n"),
+ keystr(keyid), p, n_sigs );
+ if( n_subk == 1 )
+ log_info( _("key %s: \"%s\" 1 new subkey\n"),
+ keystr(keyid), p);
+ else if( n_subk )
+ log_info( _("key %s: \"%s\" %d new subkeys\n"),
+ keystr(keyid), p, n_subk );
+ if(n_sigs_cleaned==1)
+ log_info(_("key %s: \"%s\" %d signature cleaned\n"),
+ keystr(keyid),p,n_sigs_cleaned);
+ else if(n_sigs_cleaned)
+ log_info(_("key %s: \"%s\" %d signatures cleaned\n"),
+ keystr(keyid),p,n_sigs_cleaned);
+ if(n_uids_cleaned==1)
+ log_info(_("key %s: \"%s\" %d user ID cleaned\n"),
+ keystr(keyid),p,n_uids_cleaned);
+ else if(n_uids_cleaned)
+ log_info(_("key %s: \"%s\" %d user IDs cleaned\n"),
+ keystr(keyid),p,n_uids_cleaned);
+ xfree(p);
+ }
+
+ stats->n_uids +=n_uids;
+ stats->n_sigs +=n_sigs;
+ stats->n_subk +=n_subk;
+ stats->n_sigs_cleaned +=n_sigs_cleaned;
+ stats->n_uids_cleaned +=n_uids_cleaned;
+
+ if (is_status_enabled ())
+ print_import_ok (pk, NULL,
+ ((n_uids?2:0)|(n_sigs?4:0)|(n_subk?8:0)));
+ }
+ else
+ {
+ if (is_status_enabled ())
+ print_import_ok (pk, NULL, 0);
+
+ if( !opt.quiet )
+ {
+ char *p=get_user_id_native(keyid);
+ log_info( _("key %s: \"%s\" not changed\n"),keystr(keyid),p);
+ xfree(p);
+ }
+
+ stats->unchanged++;
+ }
+
+ keydb_release (hd); hd = NULL;
+ }
+
+ leave:
+
+ /* Now that the key is definitely incorporated into the keydb, we
+ need to check if a designated revocation is present or if the
+ prefs are not rational so we can warn the user. */
+
+ if(mod_key)
+ {
+ revocation_present(keyblock_orig);
+ if(!from_sk && seckey_available(keyid)==0)
+ check_prefs(keyblock_orig);
+ }
+ else if(new_key)
+ {
+ /* A little explanation for this: we fill in the fingerprint
+ when importing keys as it can be useful to know the
+ fingerprint in certain keyserver-related cases (a keyserver
+ asked for a particular name, but the key doesn't have that
+ name). However, in cases where we're importing more than
+ one key at a time, we cannot know which key to fingerprint.
+ In these cases, rather than guessing, we do not fingerpring
+ at all, and we must hope the user ID on the keys are
+ useful. */
+ if(fpr)
+ {
+ xfree(*fpr);
+ if(stats->imported==1)
+ *fpr=fingerprint_from_pk(pk,NULL,fpr_len);
+ else
+ *fpr=NULL;
+ }
+
+ revocation_present(keyblock);
+ if(!from_sk && seckey_available(keyid)==0)
+ check_prefs(keyblock);
+ }
+
+ release_kbnode( keyblock_orig );
+ free_public_key( pk_orig );
+
+ return rc;
+}
+
+/* Walk a secret keyblock and produce a public keyblock out of it. */
+static KBNODE
+sec_to_pub_keyblock(KBNODE sec_keyblock)
+{
+ KBNODE secnode,pub_keyblock=NULL,ctx=NULL;
+
+ while((secnode=walk_kbnode(sec_keyblock,&ctx,0)))
+ {
+ KBNODE pubnode;
+
+ if(secnode->pkt->pkttype==PKT_SECRET_KEY ||
+ secnode->pkt->pkttype==PKT_SECRET_SUBKEY)
+ {
+ /* Make a public key. We only need to convert enough to
+ write the keyblock out. */
+
+ PKT_secret_key *sk=secnode->pkt->pkt.secret_key;
+ PACKET *pkt=xmalloc_clear(sizeof(PACKET));
+ PKT_public_key *pk=xmalloc_clear(sizeof(PKT_public_key));
+ int n;
+
+ if(secnode->pkt->pkttype==PKT_SECRET_KEY)
+ pkt->pkttype=PKT_PUBLIC_KEY;
+ else
+ pkt->pkttype=PKT_PUBLIC_SUBKEY;
+
+ pkt->pkt.public_key=pk;
+
+ pk->version=sk->version;
+ pk->timestamp=sk->timestamp;
+ pk->expiredate=sk->expiredate;
+ pk->pubkey_algo=sk->pubkey_algo;
+
+ n=pubkey_get_npkey(pk->pubkey_algo);
+ if(n==0)
+ {
+ /* we can't properly extract the pubkey without knowing
+ the number of MPIs */
+ release_kbnode(pub_keyblock);
+ return NULL;
+ }
+ else
+ {
+ int i;
+
+ for(i=0;i<n;i++)
+ pk->pkey[i]=mpi_copy(sk->skey[i]);
+ }
+
+ pubnode=new_kbnode(pkt);
+ }
+ else
+ {
+ pubnode=clone_kbnode(secnode);
+ }
+
+ if(pub_keyblock==NULL)
+ pub_keyblock=pubnode;
+ else
+ add_kbnode(pub_keyblock,pubnode);
+ }
+
+ return pub_keyblock;
+}
+
+/****************
+ * Ditto for secret keys. Handling is simpler than for public keys.
+ * We allow secret key importing only when allow is true, this is so
+ * that a secret key can not be imported accidently and thereby tampering
+ * with the trust calculation.
+ */
+static int
+import_secret_one( const char *fname, KBNODE keyblock,
+ struct stats_s *stats, unsigned int options)
+{
+ PKT_secret_key *sk;
+ KBNODE node, uidnode;
+ u32 keyid[2];
+ int rc = 0;
+
+ /* get the key and print some info about it */
+ node = find_kbnode( keyblock, PKT_SECRET_KEY );
+ if( !node )
+ BUG();
+
+ sk = node->pkt->pkt.secret_key;
+ keyid_from_sk( sk, keyid );
+ uidnode = find_next_kbnode( keyblock, PKT_USER_ID );
+
+ if( opt.verbose )
+ {
+ log_info( "sec %4u%c/%s %s ",
+ nbits_from_sk( sk ),
+ pubkey_letter( sk->pubkey_algo ),
+ keystr_from_sk(sk), datestr_from_sk(sk) );
+ if( uidnode )
+ print_utf8_string( stderr, uidnode->pkt->pkt.user_id->name,
+ uidnode->pkt->pkt.user_id->len );
+ putc('\n', stderr);
+ }
+ stats->secret_read++;
+
+ if( !uidnode )
+ {
+ log_error( _("key %s: no user ID\n"), keystr_from_sk(sk));
+ return 0;
+ }
+
+ if(sk->protect.algo>110)
+ {
+ log_error(_("key %s: secret key with invalid cipher %d"
+ " - skipped\n"),keystr_from_sk(sk),sk->protect.algo);
+ return 0;
+ }
+
+#ifdef ENABLE_SELINUX_HACKS
+ if (1)
+ {
+ /* We don't allow to import secret keys because that may be used
+ to put a secret key into the keyring and the user might later
+ be tricked into signing stuff with that key. */
+ log_error (_("importing secret keys not allowed\n"));
+ return 0;
+ }
+#endif
+
+ clear_kbnode_flags( keyblock );
+
+ /* do we have this key already in one of our secrings ? */
+ rc = seckey_available( keyid );
+ if( rc == G10ERR_NO_SECKEY && !(opt.import_options&IMPORT_MERGE_ONLY) )
+ {
+ /* simply insert this key */
+ KEYDB_HANDLE hd = keydb_new (1);
+
+ /* get default resource */
+ rc = keydb_locate_writable (hd, NULL);
+ if (rc) {
+ log_error (_("no default secret keyring: %s\n"), g10_errstr (rc));
+ keydb_release (hd);
+ return G10ERR_GENERAL;
+ }
+ rc = keydb_insert_keyblock (hd, keyblock );
+ if (rc)
+ log_error (_("error writing keyring `%s': %s\n"),
+ keydb_get_resource_name (hd), g10_errstr(rc) );
+ keydb_release (hd);
+ /* we are ready */
+ if( !opt.quiet )
+ log_info( _("key %s: secret key imported\n"), keystr_from_sk(sk));
+ stats->secret_imported++;
+ if (is_status_enabled ())
+ print_import_ok (NULL, sk, 1|16);
+
+ if(options&IMPORT_SK2PK)
+ {
+ /* Try and make a public key out of this. */
+
+ KBNODE pub_keyblock=sec_to_pub_keyblock(keyblock);
+ if(pub_keyblock)
+ {
+ import_one(fname,pub_keyblock,stats,
+ NULL,NULL,opt.import_options,1);
+ release_kbnode(pub_keyblock);
+ }
+ }
+
+ /* Now that the key is definitely incorporated into the keydb,
+ if we have the public part of this key, we need to check if
+ the prefs are rational. */
+ node=get_pubkeyblock(keyid);
+ if(node)
+ {
+ check_prefs(node);
+ release_kbnode(node);
+ }
+ }
+ else if( !rc )
+ { /* we can't merge secret keys */
+ log_error( _("key %s: already in secret keyring\n"),
+ keystr_from_sk(sk));
+ stats->secret_dups++;
+ if (is_status_enabled ())
+ print_import_ok (NULL, sk, 16);
+
+ /* TODO: if we ever do merge secret keys, make sure to handle
+ the sec_to_pub_keyblock feature as well. */
+ }
+ else
+ log_error( _("key %s: secret key not found: %s\n"),
+ keystr_from_sk(sk), g10_errstr(rc));
+
+ return rc;
+}
+
+
+/****************
+ * Import a revocation certificate; this is a single signature packet.
+ */
+static int
+import_revoke_cert( const char *fname, KBNODE node, struct stats_s *stats )
+{
+ PKT_public_key *pk=NULL;
+ KBNODE onode, keyblock = NULL;
+ KEYDB_HANDLE hd = NULL;
+ u32 keyid[2];
+ int rc = 0;
+
+ assert( !node->next );
+ assert( node->pkt->pkttype == PKT_SIGNATURE );
+ assert( node->pkt->pkt.signature->sig_class == 0x20 );
+
+ keyid[0] = node->pkt->pkt.signature->keyid[0];
+ keyid[1] = node->pkt->pkt.signature->keyid[1];
+
+ pk = xmalloc_clear( sizeof *pk );
+ rc = get_pubkey( pk, keyid );
+ if( rc == G10ERR_NO_PUBKEY )
+ {
+ log_error(_("key %s: no public key -"
+ " can't apply revocation certificate\n"), keystr(keyid));
+ rc = 0;
+ goto leave;
+ }
+ else if( rc )
+ {
+ log_error(_("key %s: public key not found: %s\n"),
+ keystr(keyid), g10_errstr(rc));
+ goto leave;
+ }
+
+ /* read the original keyblock */
+ hd = keydb_new (0);
+ {
+ byte afp[MAX_FINGERPRINT_LEN];
+ size_t an;
+
+ fingerprint_from_pk (pk, afp, &an);
+ while (an < MAX_FINGERPRINT_LEN)
+ afp[an++] = 0;
+ rc = keydb_search_fpr (hd, afp);
+ }
+ if (rc)
+ {
+ log_error (_("key %s: can't locate original keyblock: %s\n"),
+ keystr(keyid), g10_errstr(rc));
+ goto leave;
+ }
+ rc = keydb_get_keyblock (hd, &keyblock );
+ if (rc)
+ {
+ log_error (_("key %s: can't read original keyblock: %s\n"),
+ keystr(keyid), g10_errstr(rc));
+ goto leave;
+ }
+
+ /* it is okay, that node is not in keyblock because
+ * check_key_signature works fine for sig_class 0x20 in this
+ * special case. */
+ rc = check_key_signature( keyblock, node, NULL);
+ if( rc )
+ {
+ log_error( _("key %s: invalid revocation certificate"
+ ": %s - rejected\n"), keystr(keyid), g10_errstr(rc));
+ goto leave;
+ }
+
+ /* check whether we already have this */
+ for(onode=keyblock->next; onode; onode=onode->next ) {
+ if( onode->pkt->pkttype == PKT_USER_ID )
+ break;
+ else if( onode->pkt->pkttype == PKT_SIGNATURE
+ && !cmp_signatures(node->pkt->pkt.signature,
+ onode->pkt->pkt.signature))
+ {
+ rc = 0;
+ goto leave; /* yes, we already know about it */
+ }
+ }
+
+
+ /* insert it */
+ insert_kbnode( keyblock, clone_kbnode(node), 0 );
+
+ /* and write the keyblock back */
+ rc = keydb_update_keyblock (hd, keyblock );
+ if (rc)
+ log_error (_("error writing keyring `%s': %s\n"),
+ keydb_get_resource_name (hd), g10_errstr(rc) );
+ keydb_release (hd); hd = NULL;
+ /* we are ready */
+ if( !opt.quiet )
+ {
+ char *p=get_user_id_native (keyid);
+ log_info( _("key %s: \"%s\" revocation certificate imported\n"),
+ keystr(keyid),p);
+ xfree(p);
+ }
+ stats->n_revoc++;
+
+ /* If the key we just revoked was ultimately trusted, remove its
+ ultimate trust. This doesn't stop the user from putting the
+ ultimate trust back, but is a reasonable solution for now. */
+ if(get_ownertrust(pk)==TRUST_ULTIMATE)
+ clear_ownertrusts(pk);
+
+ revalidation_mark ();
+
+ leave:
+ keydb_release (hd);
+ release_kbnode( keyblock );
+ free_public_key( pk );
+ return rc;
+}
+
+
+/****************
+ * loop over the keyblock and check all self signatures.
+ * Mark all user-ids with a self-signature by setting flag bit 0.
+ * Mark all user-ids with an invalid self-signature by setting bit 1.
+ * This works also for subkeys, here the subkey is marked. Invalid or
+ * extra subkey sigs (binding or revocation) are marked for deletion.
+ * non_self is set to true if there are any sigs other than self-sigs
+ * in this keyblock.
+ */
+static int
+chk_self_sigs( const char *fname, KBNODE keyblock,
+ PKT_public_key *pk, u32 *keyid, int *non_self )
+{
+ KBNODE n,knode=NULL;
+ PKT_signature *sig;
+ int rc;
+ u32 bsdate=0,rsdate=0;
+ KBNODE bsnode=NULL,rsnode=NULL;
+
+ for( n=keyblock; (n = find_next_kbnode(n, 0)); ) {
+ if(n->pkt->pkttype==PKT_PUBLIC_SUBKEY)
+ {
+ knode=n;
+ bsdate=0;
+ rsdate=0;
+ bsnode=NULL;
+ rsnode=NULL;
+ continue;
+ }
+ else if( n->pkt->pkttype != PKT_SIGNATURE )
+ continue;
+ sig = n->pkt->pkt.signature;
+ if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] ) {
+
+ /* This just caches the sigs for later use. That way we
+ import a fully-cached key which speeds things up. */
+ if(!opt.no_sig_cache)
+ check_key_signature(keyblock,n,NULL);
+
+ if( IS_UID_SIG(sig) || IS_UID_REV(sig) )
+ {
+ KBNODE unode = find_prev_kbnode( keyblock, n, PKT_USER_ID );
+ if( !unode )
+ {
+ log_error( _("key %s: no user ID for signature\n"),
+ keystr(keyid));
+ return -1; /* the complete keyblock is invalid */
+ }
+
+ /* If it hasn't been marked valid yet, keep trying */
+ if(!(unode->flag&1)) {
+ rc = check_key_signature( keyblock, n, NULL);
+ if( rc )
+ {
+ if( opt.verbose )
+ {
+ char *p=utf8_to_native(unode->pkt->pkt.user_id->name,
+ strlen(unode->pkt->pkt.user_id->name),0);
+ log_info( rc == G10ERR_PUBKEY_ALGO ?
+ _("key %s: unsupported public key "
+ "algorithm on user ID \"%s\"\n"):
+ _("key %s: invalid self-signature "
+ "on user ID \"%s\"\n"),
+ keystr(keyid),p);
+ xfree(p);
+ }
+ }
+ else
+ unode->flag |= 1; /* mark that signature checked */
+ }
+ }
+ else if( sig->sig_class == 0x18 ) {
+ /* Note that this works based solely on the timestamps
+ like the rest of gpg. If the standard gets
+ revocation targets, this may need to be revised. */
+
+ if( !knode )
+ {
+ if(opt.verbose)
+ log_info( _("key %s: no subkey for key binding\n"),
+ keystr(keyid));
+ n->flag |= 4; /* delete this */
+ }
+ else
+ {
+ rc = check_key_signature( keyblock, n, NULL);
+ if( rc )
+ {
+ if(opt.verbose)
+ log_info(rc == G10ERR_PUBKEY_ALGO ?
+ _("key %s: unsupported public key"
+ " algorithm\n"):
+ _("key %s: invalid subkey binding\n"),
+ keystr(keyid));
+ n->flag|=4;
+ }
+ else
+ {
+ /* It's valid, so is it newer? */
+ if(sig->timestamp>=bsdate) {
+ knode->flag |= 1; /* the subkey is valid */
+ if(bsnode)
+ {
+ bsnode->flag|=4; /* Delete the last binding
+ sig since this one is
+ newer */
+ if(opt.verbose)
+ log_info(_("key %s: removed multiple subkey"
+ " binding\n"),keystr(keyid));
+ }
+
+ bsnode=n;
+ bsdate=sig->timestamp;
+ }
+ else
+ n->flag|=4; /* older */
+ }
+ }
+ }
+ else if( sig->sig_class == 0x28 ) {
+ /* We don't actually mark the subkey as revoked right
+ now, so just check that the revocation sig is the
+ most recent valid one. Note that we don't care if
+ the binding sig is newer than the revocation sig.
+ See the comment in getkey.c:merge_selfsigs_subkey for
+ more */
+ if( !knode )
+ {
+ if(opt.verbose)
+ log_info( _("key %s: no subkey for key revocation\n"),
+ keystr(keyid));
+ n->flag |= 4; /* delete this */
+ }
+ else
+ {
+ rc = check_key_signature( keyblock, n, NULL);
+ if( rc )
+ {
+ if(opt.verbose)
+ log_info(rc == G10ERR_PUBKEY_ALGO ?
+ _("key %s: unsupported public"
+ " key algorithm\n"):
+ _("key %s: invalid subkey revocation\n"),
+ keystr(keyid));
+ n->flag|=4;
+ }
+ else
+ {
+ /* It's valid, so is it newer? */
+ if(sig->timestamp>=rsdate)
+ {
+ if(rsnode)
+ {
+ rsnode->flag|=4; /* Delete the last revocation
+ sig since this one is
+ newer */
+ if(opt.verbose)
+ log_info(_("key %s: removed multiple subkey"
+ " revocation\n"),keystr(keyid));
+ }
+
+ rsnode=n;
+ rsdate=sig->timestamp;
+ }
+ else
+ n->flag|=4; /* older */
+ }
+ }
+ }
+ }
+ else
+ *non_self=1;
+ }
+
+ return 0;
+}
+
+/****************
+ * delete all parts which are invalid and those signatures whose
+ * public key algorithm is not available in this implemenation;
+ * but consider RSA as valid, because parse/build_packets knows
+ * about it.
+ * returns: true if at least one valid user-id is left over.
+ */
+static int
+delete_inv_parts( const char *fname, KBNODE keyblock,
+ u32 *keyid, unsigned int options)
+{
+ KBNODE node;
+ int nvalid=0, uid_seen=0, subkey_seen=0;
+
+ for(node=keyblock->next; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_USER_ID ) {
+ uid_seen = 1;
+ if( (node->flag & 2) || !(node->flag & 1) ) {
+ if( opt.verbose )
+ {
+ char *p=utf8_to_native(node->pkt->pkt.user_id->name,
+ node->pkt->pkt.user_id->len,0);
+ log_info( _("key %s: skipped user ID \"%s\"\n"),
+ keystr(keyid),p);
+ xfree(p);
+ }
+ delete_kbnode( node ); /* the user-id */
+ /* and all following packets up to the next user-id */
+ while( node->next
+ && node->next->pkt->pkttype != PKT_USER_ID
+ && node->next->pkt->pkttype != PKT_PUBLIC_SUBKEY
+ && node->next->pkt->pkttype != PKT_SECRET_SUBKEY ){
+ delete_kbnode( node->next );
+ node = node->next;
+ }
+ }
+ else
+ nvalid++;
+ }
+ else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+ if( (node->flag & 2) || !(node->flag & 1) ) {
+ if( opt.verbose )
+ log_info( _("key %s: skipped subkey\n"),keystr(keyid));
+
+ delete_kbnode( node ); /* the subkey */
+ /* and all following signature packets */
+ while( node->next
+ && node->next->pkt->pkttype == PKT_SIGNATURE ) {
+ delete_kbnode( node->next );
+ node = node->next;
+ }
+ }
+ else
+ subkey_seen = 1;
+ }
+ else if( node->pkt->pkttype == PKT_SIGNATURE
+ && check_pubkey_algo( node->pkt->pkt.signature->pubkey_algo)
+ && node->pkt->pkt.signature->pubkey_algo != PUBKEY_ALGO_RSA )
+ delete_kbnode( node ); /* build_packet() can't handle this */
+ else if( node->pkt->pkttype == PKT_SIGNATURE &&
+ !node->pkt->pkt.signature->flags.exportable &&
+ !(options&IMPORT_LOCAL_SIGS) &&
+ seckey_available( node->pkt->pkt.signature->keyid ) )
+ {
+ /* here we violate the rfc a bit by still allowing
+ * to import non-exportable signature when we have the
+ * the secret key used to create this signature - it
+ * seems that this makes sense */
+ if(opt.verbose)
+ log_info( _("key %s: non exportable signature"
+ " (class 0x%02X) - skipped\n"),
+ keystr(keyid), node->pkt->pkt.signature->sig_class );
+ delete_kbnode( node );
+ }
+ else if( node->pkt->pkttype == PKT_SIGNATURE
+ && node->pkt->pkt.signature->sig_class == 0x20 ) {
+ if( uid_seen )
+ {
+ if(opt.verbose)
+ log_info( _("key %s: revocation certificate"
+ " at wrong place - skipped\n"),keystr(keyid));
+ delete_kbnode( node );
+ }
+ else {
+ /* If the revocation cert is from a different key than
+ the one we're working on don't check it - it's
+ probably from a revocation key and won't be
+ verifiable with this key anyway. */
+
+ if(node->pkt->pkt.signature->keyid[0]==keyid[0] &&
+ node->pkt->pkt.signature->keyid[1]==keyid[1])
+ {
+ int rc = check_key_signature( keyblock, node, NULL);
+ if( rc )
+ {
+ if(opt.verbose)
+ log_info( _("key %s: invalid revocation"
+ " certificate: %s - skipped\n"),
+ keystr(keyid), g10_errstr(rc));
+ delete_kbnode( node );
+ }
+ }
+ }
+ }
+ else if( node->pkt->pkttype == PKT_SIGNATURE &&
+ (node->pkt->pkt.signature->sig_class == 0x18 ||
+ node->pkt->pkt.signature->sig_class == 0x28) &&
+ !subkey_seen )
+ {
+ if(opt.verbose)
+ log_info( _("key %s: subkey signature"
+ " in wrong place - skipped\n"), keystr(keyid));
+ delete_kbnode( node );
+ }
+ else if( node->pkt->pkttype == PKT_SIGNATURE
+ && !IS_CERT(node->pkt->pkt.signature))
+ {
+ if(opt.verbose)
+ log_info(_("key %s: unexpected signature class (0x%02X) -"
+ " skipped\n"),keystr(keyid),
+ node->pkt->pkt.signature->sig_class);
+ delete_kbnode(node);
+ }
+ else if( (node->flag & 4) ) /* marked for deletion */
+ delete_kbnode( node );
+ }
+
+ /* note: because keyblock is the public key, it is never marked
+ * for deletion and so keyblock cannot change */
+ commit_kbnode( &keyblock );
+ return nvalid;
+}
+
+
+/****************
+ * It may happen that the imported keyblock has duplicated user IDs.
+ * We check this here and collapse those user IDs together with their
+ * sigs into one.
+ * Returns: True if the keyblock hash changed.
+ */
+int
+collapse_uids( KBNODE *keyblock )
+{
+ KBNODE n, n2;
+ int in_uid;
+ int any=0;
+
+ restart:
+ for( n = *keyblock; n; n = n->next ) {
+ if( n->pkt->pkttype != PKT_USER_ID )
+ continue;
+ for( n2 = n->next; n2; n2 = n2->next ) {
+ if( n2->pkt->pkttype == PKT_USER_ID
+ && !cmp_user_ids( n->pkt->pkt.user_id,
+ n2->pkt->pkt.user_id ) ) {
+ /* found a duplicate */
+ any = 1;
+ if( !n2->next
+ || n2->next->pkt->pkttype == PKT_USER_ID
+ || n2->next->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || n2->next->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+ /* no more signatures: delete the user ID
+ * and start over */
+ remove_kbnode( keyblock, n2 );
+ }
+ else {
+ /* The simple approach: Move one signature and
+ * then start over to delete the next one :-( */
+ move_kbnode( keyblock, n2->next, n->next );
+ }
+ goto restart;
+ }
+ }
+ }
+ if( !any )
+ return 0;
+
+ restart_sig:
+ /* now we may have duplicate signatures on one user ID: fix this */
+ for( in_uid = 0, n = *keyblock; n; n = n->next ) {
+ if( n->pkt->pkttype == PKT_USER_ID )
+ in_uid = 1;
+ else if( n->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || n->pkt->pkttype == PKT_SECRET_SUBKEY )
+ in_uid = 0;
+ else if( in_uid ) {
+ n2 = n;
+ do {
+ KBNODE ncmp = NULL;
+ for( ; n2; n2 = n2->next ) {
+ if( n2->pkt->pkttype == PKT_USER_ID
+ || n2->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || n2->pkt->pkttype == PKT_SECRET_SUBKEY )
+ break;
+ if( n2->pkt->pkttype != PKT_SIGNATURE )
+ ;
+ else if( !ncmp )
+ ncmp = n2;
+ else if( !cmp_signatures( ncmp->pkt->pkt.signature,
+ n2->pkt->pkt.signature )) {
+ remove_kbnode( keyblock, n2 );
+ goto restart_sig;
+ }
+ }
+ n2 = ncmp? ncmp->next : NULL;
+ } while( n2 );
+ }
+ }
+
+ if(!opt.quiet)
+ {
+ const char *key="???";
+
+ if( (n = find_kbnode( *keyblock, PKT_PUBLIC_KEY )) )
+ key=keystr_from_pk(n->pkt->pkt.public_key);
+ else if( (n = find_kbnode( *keyblock, PKT_SECRET_KEY )) )
+ key=keystr_from_sk(n->pkt->pkt.secret_key);
+
+ log_info(_("key %s: duplicated user ID detected - merged\n"),key);
+ }
+
+ return 1;
+}
+
+/* Check for a 0x20 revocation from a revocation key that is not
+ present. This may be called without the benefit of merge_xxxx so
+ you can't rely on pk->revkey and friends. */
+static void
+revocation_present(KBNODE keyblock)
+{
+ KBNODE onode,inode;
+ PKT_public_key *pk=keyblock->pkt->pkt.public_key;
+
+ for(onode=keyblock->next;onode;onode=onode->next)
+ {
+ /* If we reach user IDs, we're done. */
+ if(onode->pkt->pkttype==PKT_USER_ID)
+ break;
+
+ if(onode->pkt->pkttype==PKT_SIGNATURE &&
+ onode->pkt->pkt.signature->sig_class==0x1F &&
+ onode->pkt->pkt.signature->revkey)
+ {
+ int idx;
+ PKT_signature *sig=onode->pkt->pkt.signature;
+
+ for(idx=0;idx<sig->numrevkeys;idx++)
+ {
+ u32 keyid[2];
+
+ keyid_from_fingerprint(sig->revkey[idx]->fpr,
+ MAX_FINGERPRINT_LEN,keyid);
+
+ for(inode=keyblock->next;inode;inode=inode->next)
+ {
+ /* If we reach user IDs, we're done. */
+ if(inode->pkt->pkttype==PKT_USER_ID)
+ break;
+
+ if(inode->pkt->pkttype==PKT_SIGNATURE &&
+ inode->pkt->pkt.signature->sig_class==0x20 &&
+ inode->pkt->pkt.signature->keyid[0]==keyid[0] &&
+ inode->pkt->pkt.signature->keyid[1]==keyid[1])
+ {
+ /* Okay, we have a revocation key, and a
+ revocation issued by it. Do we have the key
+ itself? */
+ int rc;
+
+ rc=get_pubkey_byfprint_fast (NULL,sig->revkey[idx]->fpr,
+ MAX_FINGERPRINT_LEN);
+ if(rc==G10ERR_NO_PUBKEY || rc==G10ERR_UNU_PUBKEY)
+ {
+ char *tempkeystr=xstrdup(keystr_from_pk(pk));
+
+ /* No, so try and get it */
+ if(opt.keyserver
+ && (opt.keyserver_options.options
+ & KEYSERVER_AUTO_KEY_RETRIEVE))
+ {
+ log_info(_("WARNING: key %s may be revoked:"
+ " fetching revocation key %s\n"),
+ tempkeystr,keystr(keyid));
+ keyserver_import_fprint(sig->revkey[idx]->fpr,
+ MAX_FINGERPRINT_LEN,
+ opt.keyserver);
+
+ /* Do we have it now? */
+ rc=get_pubkey_byfprint_fast (NULL,
+ sig->revkey[idx]->fpr,
+ MAX_FINGERPRINT_LEN);
+ }
+
+ if(rc==G10ERR_NO_PUBKEY || rc==G10ERR_UNU_PUBKEY)
+ log_info(_("WARNING: key %s may be revoked:"
+ " revocation key %s not present.\n"),
+ tempkeystr,keystr(keyid));
+
+ xfree(tempkeystr);
+ }
+ }
+ }
+ }
+ }
+ }
+}
+
+/****************
+ * compare and merge the blocks
+ *
+ * o compare the signatures: If we already have this signature, check
+ * that they compare okay; if not, issue a warning and ask the user.
+ * o Simply add the signature. Can't verify here because we may not have
+ * the signature's public key yet; verification is done when putting it
+ * into the trustdb, which is done automagically as soon as this pubkey
+ * is used.
+ * Note: We indicate newly inserted packets with flag bit 0
+ */
+static int
+merge_blocks( const char *fname, KBNODE keyblock_orig, KBNODE keyblock,
+ u32 *keyid, int *n_uids, int *n_sigs, int *n_subk )
+{
+ KBNODE onode, node;
+ int rc, found;
+
+ /* 1st: handle revocation certificates */
+ for(node=keyblock->next; node; node=node->next ) {
+ if( node->pkt->pkttype == PKT_USER_ID )
+ break;
+ else if( node->pkt->pkttype == PKT_SIGNATURE
+ && node->pkt->pkt.signature->sig_class == 0x20 ) {
+ /* check whether we already have this */
+ found = 0;
+ for(onode=keyblock_orig->next; onode; onode=onode->next ) {
+ if( onode->pkt->pkttype == PKT_USER_ID )
+ break;
+ else if( onode->pkt->pkttype == PKT_SIGNATURE
+ && onode->pkt->pkt.signature->sig_class == 0x20
+ && !cmp_signatures(onode->pkt->pkt.signature,
+ node->pkt->pkt.signature))
+ {
+ found = 1;
+ break;
+ }
+ }
+ if( !found ) {
+ KBNODE n2 = clone_kbnode(node);
+ insert_kbnode( keyblock_orig, n2, 0 );
+ n2->flag |= 1;
+ ++*n_sigs;
+ if(!opt.quiet)
+ {
+ char *p=get_user_id_native (keyid);
+ log_info(_("key %s: \"%s\" revocation"
+ " certificate added\n"), keystr(keyid),p);
+ xfree(p);
+ }
+ }
+ }
+ }
+
+ /* 2nd: merge in any direct key (0x1F) sigs */
+ for(node=keyblock->next; node; node=node->next ) {
+ if( node->pkt->pkttype == PKT_USER_ID )
+ break;
+ else if( node->pkt->pkttype == PKT_SIGNATURE
+ && node->pkt->pkt.signature->sig_class == 0x1F ) {
+ /* check whether we already have this */
+ found = 0;
+ for(onode=keyblock_orig->next; onode; onode=onode->next ) {
+ if( onode->pkt->pkttype == PKT_USER_ID )
+ break;
+ else if( onode->pkt->pkttype == PKT_SIGNATURE
+ && onode->pkt->pkt.signature->sig_class == 0x1F
+ && !cmp_signatures(onode->pkt->pkt.signature,
+ node->pkt->pkt.signature)) {
+ found = 1;
+ break;
+ }
+ }
+ if( !found )
+ {
+ KBNODE n2 = clone_kbnode(node);
+ insert_kbnode( keyblock_orig, n2, 0 );
+ n2->flag |= 1;
+ ++*n_sigs;
+ if(!opt.quiet)
+ log_info( _("key %s: direct key signature added\n"),
+ keystr(keyid));
+ }
+ }
+ }
+
+ /* 3rd: try to merge new certificates in */
+ for(onode=keyblock_orig->next; onode; onode=onode->next ) {
+ if( !(onode->flag & 1) && onode->pkt->pkttype == PKT_USER_ID) {
+ /* find the user id in the imported keyblock */
+ for(node=keyblock->next; node; node=node->next )
+ if( node->pkt->pkttype == PKT_USER_ID
+ && !cmp_user_ids( onode->pkt->pkt.user_id,
+ node->pkt->pkt.user_id ) )
+ break;
+ if( node ) { /* found: merge */
+ rc = merge_sigs( onode, node, n_sigs, fname, keyid );
+ if( rc )
+ return rc;
+ }
+ }
+ }
+
+ /* 4th: add new user-ids */
+ for(node=keyblock->next; node; node=node->next ) {
+ if( node->pkt->pkttype == PKT_USER_ID) {
+ /* do we have this in the original keyblock */
+ for(onode=keyblock_orig->next; onode; onode=onode->next )
+ if( onode->pkt->pkttype == PKT_USER_ID
+ && !cmp_user_ids( onode->pkt->pkt.user_id,
+ node->pkt->pkt.user_id ) )
+ break;
+ if( !onode ) { /* this is a new user id: append */
+ rc = append_uid( keyblock_orig, node, n_sigs, fname, keyid);
+ if( rc )
+ return rc;
+ ++*n_uids;
+ }
+ }
+ }
+
+ /* 5th: add new subkeys */
+ for(node=keyblock->next; node; node=node->next ) {
+ onode = NULL;
+ if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+ /* do we have this in the original keyblock? */
+ for(onode=keyblock_orig->next; onode; onode=onode->next )
+ if( onode->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ && !cmp_public_keys( onode->pkt->pkt.public_key,
+ node->pkt->pkt.public_key ) )
+ break;
+ if( !onode ) { /* this is a new subkey: append */
+ rc = append_key( keyblock_orig, node, n_sigs, fname, keyid);
+ if( rc )
+ return rc;
+ ++*n_subk;
+ }
+ }
+ else if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+ /* do we have this in the original keyblock? */
+ for(onode=keyblock_orig->next; onode; onode=onode->next )
+ if( onode->pkt->pkttype == PKT_SECRET_SUBKEY
+ && !cmp_secret_keys( onode->pkt->pkt.secret_key,
+ node->pkt->pkt.secret_key ) )
+ break;
+ if( !onode ) { /* this is a new subkey: append */
+ rc = append_key( keyblock_orig, node, n_sigs, fname, keyid);
+ if( rc )
+ return rc;
+ ++*n_subk;
+ }
+ }
+ }
+
+ /* 6th: merge subkey certificates */
+ for(onode=keyblock_orig->next; onode; onode=onode->next ) {
+ if( !(onode->flag & 1)
+ && ( onode->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || onode->pkt->pkttype == PKT_SECRET_SUBKEY) ) {
+ /* find the subkey in the imported keyblock */
+ for(node=keyblock->next; node; node=node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ && !cmp_public_keys( onode->pkt->pkt.public_key,
+ node->pkt->pkt.public_key ) )
+ break;
+ else if( node->pkt->pkttype == PKT_SECRET_SUBKEY
+ && !cmp_secret_keys( onode->pkt->pkt.secret_key,
+ node->pkt->pkt.secret_key ) )
+ break;
+ }
+ if( node ) { /* found: merge */
+ rc = merge_keysigs( onode, node, n_sigs, fname, keyid );
+ if( rc )
+ return rc;
+ }
+ }
+ }
+
+
+ return 0;
+}
+
+
+/****************
+ * append the userid starting with NODE and all signatures to KEYBLOCK.
+ */
+static int
+append_uid( KBNODE keyblock, KBNODE node, int *n_sigs,
+ const char *fname, u32 *keyid )
+{
+ KBNODE n, n_where=NULL;
+
+ assert(node->pkt->pkttype == PKT_USER_ID );
+
+ /* find the position */
+ for( n = keyblock; n; n_where = n, n = n->next ) {
+ if( n->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || n->pkt->pkttype == PKT_SECRET_SUBKEY )
+ break;
+ }
+ if( !n )
+ n_where = NULL;
+
+ /* and append/insert */
+ while( node ) {
+ /* we add a clone to the original keyblock, because this
+ * one is released first */
+ n = clone_kbnode(node);
+ if( n_where ) {
+ insert_kbnode( n_where, n, 0 );
+ n_where = n;
+ }
+ else
+ add_kbnode( keyblock, n );
+ n->flag |= 1;
+ node->flag |= 1;
+ if( n->pkt->pkttype == PKT_SIGNATURE )
+ ++*n_sigs;
+
+ node = node->next;
+ if( node && node->pkt->pkttype != PKT_SIGNATURE )
+ break;
+ }
+
+ return 0;
+}
+
+
+/****************
+ * Merge the sigs from SRC onto DST. SRC and DST are both a PKT_USER_ID.
+ * (how should we handle comment packets here?)
+ */
+static int
+merge_sigs( KBNODE dst, KBNODE src, int *n_sigs,
+ const char *fname, u32 *keyid )
+{
+ KBNODE n, n2;
+ int found=0;
+
+ assert(dst->pkt->pkttype == PKT_USER_ID );
+ assert(src->pkt->pkttype == PKT_USER_ID );
+
+ for(n=src->next; n && n->pkt->pkttype != PKT_USER_ID; n = n->next ) {
+ if( n->pkt->pkttype != PKT_SIGNATURE )
+ continue;
+ if( n->pkt->pkt.signature->sig_class == 0x18
+ || n->pkt->pkt.signature->sig_class == 0x28 )
+ continue; /* skip signatures which are only valid on subkeys */
+ found = 0;
+ for(n2=dst->next; n2 && n2->pkt->pkttype != PKT_USER_ID; n2 = n2->next)
+ if(!cmp_signatures(n->pkt->pkt.signature,n2->pkt->pkt.signature))
+ {
+ found++;
+ break;
+ }
+ if( !found ) {
+ /* This signature is new or newer, append N to DST.
+ * We add a clone to the original keyblock, because this
+ * one is released first */
+ n2 = clone_kbnode(n);
+ insert_kbnode( dst, n2, PKT_SIGNATURE );
+ n2->flag |= 1;
+ n->flag |= 1;
+ ++*n_sigs;
+ }
+ }
+
+ return 0;
+}
+
+/****************
+ * Merge the sigs from SRC onto DST. SRC and DST are both a PKT_xxx_SUBKEY.
+ */
+static int
+merge_keysigs( KBNODE dst, KBNODE src, int *n_sigs,
+ const char *fname, u32 *keyid )
+{
+ KBNODE n, n2;
+ int found=0;
+
+ assert( dst->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || dst->pkt->pkttype == PKT_SECRET_SUBKEY );
+
+ for(n=src->next; n ; n = n->next ) {
+ if( n->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || n->pkt->pkttype == PKT_PUBLIC_KEY )
+ break;
+ if( n->pkt->pkttype != PKT_SIGNATURE )
+ continue;
+ found = 0;
+ for(n2=dst->next; n2; n2 = n2->next){
+ if( n2->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || n2->pkt->pkttype == PKT_PUBLIC_KEY )
+ break;
+ if( n2->pkt->pkttype == PKT_SIGNATURE
+ && n->pkt->pkt.signature->keyid[0]
+ == n2->pkt->pkt.signature->keyid[0]
+ && n->pkt->pkt.signature->keyid[1]
+ == n2->pkt->pkt.signature->keyid[1]
+ && n->pkt->pkt.signature->timestamp
+ <= n2->pkt->pkt.signature->timestamp
+ && n->pkt->pkt.signature->sig_class
+ == n2->pkt->pkt.signature->sig_class ) {
+ found++;
+ break;
+ }
+ }
+ if( !found ) {
+ /* This signature is new or newer, append N to DST.
+ * We add a clone to the original keyblock, because this
+ * one is released first */
+ n2 = clone_kbnode(n);
+ insert_kbnode( dst, n2, PKT_SIGNATURE );
+ n2->flag |= 1;
+ n->flag |= 1;
+ ++*n_sigs;
+ }
+ }
+
+ return 0;
+}
+
+/****************
+ * append the subkey starting with NODE and all signatures to KEYBLOCK.
+ * Mark all new and copied packets by setting flag bit 0.
+ */
+static int
+append_key( KBNODE keyblock, KBNODE node, int *n_sigs,
+ const char *fname, u32 *keyid )
+{
+ KBNODE n;
+
+ assert( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY );
+
+ while( node ) {
+ /* we add a clone to the original keyblock, because this
+ * one is released first */
+ n = clone_kbnode(node);
+ add_kbnode( keyblock, n );
+ n->flag |= 1;
+ node->flag |= 1;
+ if( n->pkt->pkttype == PKT_SIGNATURE )
+ ++*n_sigs;
+
+ node = node->next;
+ if( node && node->pkt->pkttype != PKT_SIGNATURE )
+ break;
+ }
+
+ return 0;
+}
+
+
+
+/* Walk a public keyblock and produce a secret keyblock out of it.
+ Instead of inserting the secret key parameters (which we don't
+ have), we insert a stub. */
+static KBNODE
+pub_to_sec_keyblock (KBNODE pub_keyblock)
+{
+ KBNODE pubnode, secnode;
+ KBNODE sec_keyblock = NULL;
+ KBNODE walkctx = NULL;
+
+ while((pubnode = walk_kbnode (pub_keyblock,&walkctx,0)))
+ {
+ if (pubnode->pkt->pkttype == PKT_PUBLIC_KEY
+ || pubnode->pkt->pkttype == PKT_PUBLIC_SUBKEY)
+ {
+ /* Make a secret key. We only need to convert enough to
+ write the keyblock out. */
+ PKT_public_key *pk = pubnode->pkt->pkt.public_key;
+ PACKET *pkt = xmalloc_clear (sizeof *pkt);
+ PKT_secret_key *sk = xmalloc_clear (sizeof *sk);
+ int i, n;
+
+ if (pubnode->pkt->pkttype == PKT_PUBLIC_KEY)
+ pkt->pkttype = PKT_SECRET_KEY;
+ else
+ pkt->pkttype = PKT_SECRET_SUBKEY;
+
+ pkt->pkt.secret_key = sk;
+
+ copy_public_parts_to_secret_key ( pk, sk );
+ sk->version = pk->version;
+ sk->timestamp = pk->timestamp;
+
+ n = pubkey_get_npkey (pk->pubkey_algo);
+ if (!n)
+ n = 1; /* Unknown number of parameters, however the data
+ is stored in the first mpi. */
+ for (i=0; i < n; i++ )
+ sk->skey[i] = mpi_copy (pk->pkey[i]);
+
+ sk->is_protected = 1;
+ sk->protect.s2k.mode = 1001;
+
+ secnode = new_kbnode (pkt);
+ }
+ else
+ {
+ secnode = clone_kbnode (pubnode);
+ }
+
+ if(!sec_keyblock)
+ sec_keyblock = secnode;
+ else
+ add_kbnode (sec_keyblock, secnode);
+ }
+
+ return sec_keyblock;
+}
+
+
+/* Walk over the secret keyring SEC_KEYBLOCK and update any simple
+ stub keys with the serial number SNNUM of the card if one of the
+ fingerprints FPR1, FPR2 or FPR3 match. Print a note if the key is
+ a duplicate (may happen in case of backed uped keys).
+
+ Returns: True if anything changed.
+*/
+static int
+update_sec_keyblock_with_cardinfo (KBNODE sec_keyblock,
+ const unsigned char *fpr1,
+ const unsigned char *fpr2,
+ const unsigned char *fpr3,
+ const char *serialnostr)
+{
+ KBNODE node;
+ KBNODE walkctx = NULL;
+ PKT_secret_key *sk;
+ byte array[MAX_FINGERPRINT_LEN];
+ size_t n;
+ int result = 0;
+ const char *s;
+
+ while((node = walk_kbnode (sec_keyblock, &walkctx, 0)))
+ {
+ if (node->pkt->pkttype != PKT_SECRET_KEY
+ && node->pkt->pkttype != PKT_SECRET_SUBKEY)
+ continue;
+ sk = node->pkt->pkt.secret_key;
+
+ fingerprint_from_sk (sk, array, &n);
+ if (n != 20)
+ continue; /* Can't be a card key. */
+ if ( !((fpr1 && !memcmp (array, fpr1, 20))
+ || (fpr2 && !memcmp (array, fpr2, 20))
+ || (fpr3 && !memcmp (array, fpr3, 20))) )
+ continue; /* No match. */
+
+ if (sk->is_protected == 1 && sk->protect.s2k.mode == 1001)
+ {
+ /* Standard case: migrate that stub to a key stub. */
+ sk->protect.s2k.mode = 1002;
+ s = serialnostr;
+ for (sk->protect.ivlen=0; sk->protect.ivlen < 16 && *s && s[1];
+ sk->protect.ivlen++, s += 2)
+ sk->protect.iv[sk->protect.ivlen] = xtoi_2 (s);
+ result = 1;
+ }
+ else if (sk->is_protected == 1 && sk->protect.s2k.mode == 1002)
+ {
+ s = serialnostr;
+ for (sk->protect.ivlen=0; sk->protect.ivlen < 16 && *s && s[1];
+ sk->protect.ivlen++, s += 2)
+ if (sk->protect.iv[sk->protect.ivlen] != xtoi_2 (s))
+ {
+ log_info (_("NOTE: a key's S/N does not "
+ "match the card's one\n"));
+ break;
+ }
+ }
+ else
+ {
+ if (node->pkt->pkttype != PKT_SECRET_KEY)
+ log_info (_("NOTE: primary key is online and stored on card\n"));
+ else
+ log_info (_("NOTE: secondary key is online and stored on card\n"));
+ }
+ }
+
+ return result;
+}
+
+
+
+/* Check whether a secret key stub exists for the public key PK. If
+ not create such a stub key and store it into the secring. If it
+ exists, add appropriate subkey stubs and update the secring.
+ Return 0 if the key could be created. */
+int
+auto_create_card_key_stub ( const char *serialnostr,
+ const unsigned char *fpr1,
+ const unsigned char *fpr2,
+ const unsigned char *fpr3)
+{
+ KBNODE pub_keyblock;
+ KBNODE sec_keyblock;
+ KEYDB_HANDLE hd;
+ int rc;
+
+ /* We only want to do this for an OpenPGP card. */
+ if (!serialnostr || strncmp (serialnostr, "D27600012401", 12)
+ || strlen (serialnostr) != 32 )
+ return G10ERR_GENERAL;
+
+ /* First get the public keyring from any of the provided fingerprints. */
+ if ( (fpr1 && !get_keyblock_byfprint (&pub_keyblock, fpr1, 20))
+ || (fpr2 && !get_keyblock_byfprint (&pub_keyblock, fpr2, 20))
+ || (fpr3 && !get_keyblock_byfprint (&pub_keyblock, fpr3, 20)))
+ ;
+ else
+ return G10ERR_GENERAL;
+
+ hd = keydb_new (1);
+
+ /* Now check whether there is a secret keyring. */
+ {
+ PKT_public_key *pk = pub_keyblock->pkt->pkt.public_key;
+ byte afp[MAX_FINGERPRINT_LEN];
+ size_t an;
+
+ fingerprint_from_pk (pk, afp, &an);
+ memset (afp, 0, MAX_FINGERPRINT_LEN);
+ rc = keydb_search_fpr (hd, afp);
+ }
+
+ if (!rc)
+ {
+ rc = keydb_get_keyblock (hd, &sec_keyblock);
+ if (rc)
+ {
+ log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) );
+ rc = G10ERR_GENERAL;
+ }
+ else
+ {
+ merge_keys_and_selfsig (sec_keyblock);
+
+ /* FIXME: We need to add new subkeys first. */
+ if (update_sec_keyblock_with_cardinfo (sec_keyblock,
+ fpr1, fpr2, fpr3,
+ serialnostr))
+ {
+ rc = keydb_update_keyblock (hd, sec_keyblock );
+ if (rc)
+ log_error (_("error writing keyring `%s': %s\n"),
+ keydb_get_resource_name (hd), g10_errstr(rc) );
+ }
+ }
+ }
+ else /* A secret key does not exists - create it. */
+ {
+ sec_keyblock = pub_to_sec_keyblock (pub_keyblock);
+ update_sec_keyblock_with_cardinfo (sec_keyblock,
+ fpr1, fpr2, fpr3,
+ serialnostr);
+
+ rc = keydb_locate_writable (hd, NULL);
+ if (rc)
+ {
+ log_error (_("no default secret keyring: %s\n"), g10_errstr (rc));
+ rc = G10ERR_GENERAL;
+ }
+ else
+ {
+ rc = keydb_insert_keyblock (hd, sec_keyblock );
+ if (rc)
+ log_error (_("error writing keyring `%s': %s\n"),
+ keydb_get_resource_name (hd), g10_errstr(rc) );
+ }
+ }
+
+ release_kbnode (sec_keyblock);
+ release_kbnode (pub_keyblock);
+ keydb_release (hd);
+ return rc;
+}
+
diff --git a/g10/iso7816.c b/g10/iso7816.c
new file mode 100644
index 0000000..35d0aa9
--- /dev/null
+++ b/g10/iso7816.c
@@ -0,0 +1,685 @@
+/* iso7816.c - ISO 7816 commands
+ * Copyright (C) 2003, 2004 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ * $Id: iso7816.c 3886 2005-09-07 17:05:42Z wk $
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#if defined(GNUPG_SCD_MAIN_HEADER)
+#include GNUPG_SCD_MAIN_HEADER
+#elif GNUPG_MAJOR_VERSION == 1
+/* This is used with GnuPG version < 1.9. The code has been source
+ copied from the current GnuPG >= 1.9 and is maintained over
+ there. */
+#include "options.h"
+#include "errors.h"
+#include "memory.h"
+#include "util.h"
+#include "i18n.h"
+#else /* GNUPG_MAJOR_VERSION != 1 */
+#include "scdaemon.h"
+#endif /* GNUPG_MAJOR_VERSION != 1 */
+
+#include "iso7816.h"
+#include "apdu.h"
+
+
+#define CMD_SELECT_FILE 0xA4
+#define CMD_VERIFY 0x20
+#define CMD_CHANGE_REFERENCE_DATA 0x24
+#define CMD_RESET_RETRY_COUNTER 0x2C
+#define CMD_GET_DATA 0xCA
+#define CMD_PUT_DATA 0xDA
+#define CMD_MSE 0x22
+#define CMD_PSO 0x2A
+#define CMD_INTERNAL_AUTHENTICATE 0x88
+#define CMD_GENERATE_KEYPAIR 0x47
+#define CMD_GET_CHALLENGE 0x84
+#define CMD_READ_BINARY 0xB0
+#define CMD_READ_RECORD 0xB2
+
+static gpg_error_t
+map_sw (int sw)
+{
+ gpg_err_code_t ec;
+
+ switch (sw)
+ {
+ case SW_EEPROM_FAILURE: ec = GPG_ERR_HARDWARE; break;
+ case SW_WRONG_LENGTH: ec = GPG_ERR_INV_VALUE; break;
+ case SW_CHV_WRONG: ec = GPG_ERR_BAD_PIN; break;
+ case SW_CHV_BLOCKED: ec = GPG_ERR_PIN_BLOCKED; break;
+ case SW_USE_CONDITIONS: ec = GPG_ERR_USE_CONDITIONS; break;
+ case SW_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break;
+ case SW_BAD_PARAMETER: ec = GPG_ERR_INV_VALUE; break;
+ case SW_FILE_NOT_FOUND: ec = GPG_ERR_ENOENT; break;
+ case SW_RECORD_NOT_FOUND:ec= GPG_ERR_NOT_FOUND; break;
+ case SW_REF_NOT_FOUND: ec = GPG_ERR_NO_OBJ; break;
+ case SW_BAD_P0_P1: ec = GPG_ERR_INV_VALUE; break;
+ case SW_EXACT_LENGTH: ec = GPG_ERR_INV_VALUE; break;
+ case SW_INS_NOT_SUP: ec = GPG_ERR_CARD; break;
+ case SW_CLA_NOT_SUP: ec = GPG_ERR_CARD; break;
+ case SW_SUCCESS: ec = 0; break;
+
+ case SW_HOST_OUT_OF_CORE: ec = GPG_ERR_ENOMEM; break;
+ case SW_HOST_INV_VALUE: ec = GPG_ERR_INV_VALUE; break;
+ case SW_HOST_INCOMPLETE_CARD_RESPONSE: ec = GPG_ERR_CARD; break;
+ case SW_HOST_NOT_SUPPORTED: ec = GPG_ERR_NOT_SUPPORTED; break;
+ case SW_HOST_LOCKING_FAILED: ec = GPG_ERR_BUG; break;
+ case SW_HOST_BUSY: ec = GPG_ERR_EBUSY; break;
+ case SW_HOST_NO_CARD: ec = GPG_ERR_CARD_NOT_PRESENT; break;
+ case SW_HOST_CARD_INACTIVE: ec = GPG_ERR_CARD_RESET; break;
+ case SW_HOST_CARD_IO_ERROR: ec = GPG_ERR_EIO; break;
+ case SW_HOST_GENERAL_ERROR: ec = GPG_ERR_GENERAL; break;
+ case SW_HOST_NO_READER: ec = GPG_ERR_ENODEV; break;
+ case SW_HOST_ABORTED: ec = GPG_ERR_CANCELED; break;
+
+ default:
+ if ((sw & 0x010000))
+ ec = GPG_ERR_GENERAL; /* Should not happen. */
+ else if ((sw & 0xff00) == SW_MORE_DATA)
+ ec = 0; /* This should actually never been seen here. */
+ else
+ ec = GPG_ERR_CARD;
+ }
+ return gpg_error (ec);
+}
+
+/* Map a status word from the APDU layer to a gpg-error code. */
+gpg_error_t
+iso7816_map_sw (int sw)
+{
+ /* All APDU functions should return 0x9000 on success but for
+ historical reasons of the implementation some return 0 to
+ indicate success. We allow for that here. */
+ return sw? map_sw (sw) : 0;
+}
+
+
+/* This function is specialized version of the SELECT FILE command.
+ SLOT is the card and reader as created for example by
+ apdu_open_reader (), AID is a buffer of size AIDLEN holding the
+ requested application ID. The function can't be used to enumerate
+ AIDs and won't return the AID on success. The return value is 0
+ for okay or a GPG error code. Note that ISO error codes are
+ internally mapped. */
+gpg_error_t
+iso7816_select_application (int slot, const char *aid, size_t aidlen)
+{
+ int sw;
+ sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE, 4, 0, aidlen, aid);
+ return map_sw (sw);
+}
+
+
+gpg_error_t
+iso7816_select_file (int slot, int tag, int is_dir,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw, p0, p1;
+ unsigned char tagbuf[2];
+
+ tagbuf[0] = (tag >> 8) & 0xff;
+ tagbuf[1] = tag & 0xff;
+
+ if (result || resultlen)
+ {
+ *result = NULL;
+ *resultlen = 0;
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ }
+ else
+ {
+ p0 = (tag == 0x3F00)? 0: is_dir? 1:2;
+ p1 = 0x0c; /* No FC return. */
+ sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE,
+ p0, p1, 2, (char*)tagbuf );
+ return map_sw (sw);
+ }
+
+ return 0;
+}
+
+
+/* Do a select file command with a direct path. */
+gpg_error_t
+iso7816_select_path (int slot, const unsigned short *path, size_t pathlen,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw, p0, p1;
+ unsigned char buffer[100];
+ int buflen;
+
+ if (result || resultlen)
+ {
+ *result = NULL;
+ *resultlen = 0;
+ return gpg_error (GPG_ERR_NOT_IMPLEMENTED);
+ }
+
+ if (pathlen/2 >= sizeof buffer)
+ return gpg_error (GPG_ERR_TOO_LARGE);
+
+ for (buflen = 0; pathlen; pathlen--, path++)
+ {
+ buffer[buflen++] = (*path >> 8);
+ buffer[buflen++] = *path;
+ }
+
+ p0 = 0x08;
+ p1 = 0x0c; /* No FC return. */
+ sw = apdu_send_simple (slot, 0x00, CMD_SELECT_FILE,
+ p0, p1, buflen, (char*)buffer );
+ return map_sw (sw);
+}
+
+
+/* This is a private command currently only working for TCOS cards. */
+gpg_error_t
+iso7816_list_directory (int slot, int list_dirs,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+
+ if (!result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ sw = apdu_send (slot, 0x80, 0xAA, list_dirs? 1:2, 0, -1, NULL,
+ result, resultlen);
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ }
+ return map_sw (sw);
+}
+
+
+
+/* Perform a VERIFY command on SLOT using the card holder verification
+ vector CHVNO with a CHV of lenght CHVLEN. Returns 0 on success. */
+gpg_error_t
+iso7816_verify (int slot, int chvno, const char *chv, size_t chvlen)
+{
+ int sw;
+
+ sw = apdu_send_simple (slot, 0x00, CMD_VERIFY, 0, chvno, chvlen, chv);
+ return map_sw (sw);
+}
+
+/* Perform a CHANGE_REFERENCE_DATA command on SLOT for the card holder
+ verification vector CHVNO. If the OLDCHV is NULL (and OLDCHVLEN
+ 0), a "change reference data" is done, otherwise an "exchange
+ reference data". The new reference data is expected in NEWCHV of
+ length NEWCHVLEN. */
+gpg_error_t
+iso7816_change_reference_data (int slot, int chvno,
+ const char *oldchv, size_t oldchvlen,
+ const char *newchv, size_t newchvlen)
+{
+ int sw;
+ char *buf;
+
+ if ((!oldchv && oldchvlen)
+ || (oldchv && !oldchvlen)
+ || !newchv || !newchvlen )
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ buf = xtrymalloc (oldchvlen + newchvlen);
+ if (!buf)
+ return gpg_error (gpg_err_code_from_errno (errno));
+ if (oldchvlen)
+ memcpy (buf, oldchv, oldchvlen);
+ memcpy (buf+oldchvlen, newchv, newchvlen);
+
+ sw = apdu_send_simple (slot, 0x00, CMD_CHANGE_REFERENCE_DATA,
+ oldchvlen? 0 : 1, chvno, oldchvlen+newchvlen, buf);
+ xfree (buf);
+ return map_sw (sw);
+
+}
+
+gpg_error_t
+iso7816_reset_retry_counter (int slot, int chvno,
+ const char *newchv, size_t newchvlen)
+{
+ int sw;
+
+ if (!newchv || !newchvlen )
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ sw = apdu_send_simple (slot, 0x00, CMD_RESET_RETRY_COUNTER,
+ 2, chvno, newchvlen, newchv);
+ return map_sw (sw);
+}
+
+
+/* Perform a GET DATA command requesting TAG and storing the result in
+ a newly allocated buffer at the address passed by RESULT. Return
+ the length of this data at the address of RESULTLEN. */
+gpg_error_t
+iso7816_get_data (int slot, int tag,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+
+ if (!result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ sw = apdu_send (slot, 0x00, CMD_GET_DATA,
+ ((tag >> 8) & 0xff), (tag & 0xff), -1, NULL,
+ result, resultlen);
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+
+ return 0;
+}
+
+
+/* Perform a PUT DATA command on card in SLOT. Write DATA of length
+ DATALEN to TAG. */
+gpg_error_t
+iso7816_put_data (int slot, int tag,
+ const unsigned char *data, size_t datalen)
+{
+ int sw;
+
+ sw = apdu_send_simple (slot, 0x00, CMD_PUT_DATA,
+ ((tag >> 8) & 0xff), (tag & 0xff),
+ datalen, (const char*)data);
+ return map_sw (sw);
+}
+
+/* Manage Security Environment. This is a weird operation and there
+ is no easy abstraction for it. Furthermore, some card seem to have
+ a different interpreation of 7816-8 and thus we resort to let the
+ caller decide what to do. */
+gpg_error_t
+iso7816_manage_security_env (int slot, int p1, int p2,
+ const unsigned char *data, size_t datalen)
+{
+ int sw;
+
+ if (p1 < 0 || p1 > 255 || p2 < 0 || p2 > 255 )
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ sw = apdu_send_simple (slot, 0x00, CMD_MSE, p1, p2,
+ data? datalen : -1, (const char*)data);
+ return map_sw (sw);
+}
+
+
+/* Perform the security operation COMPUTE DIGITAL SIGANTURE. On
+ success 0 is returned and the data is availavle in a newly
+ allocated buffer stored at RESULT with its length stored at
+ RESULTLEN. */
+gpg_error_t
+iso7816_compute_ds (int slot, const unsigned char *data, size_t datalen,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+
+ if (!data || !datalen || !result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ sw = apdu_send (slot, 0x00, CMD_PSO, 0x9E, 0x9A, datalen, (const char*)data,
+ result, resultlen);
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+
+ return 0;
+}
+
+
+/* Perform the security operation DECIPHER. PADIND is the padding
+ indicator to be used. It should be 0 if no padding is required, a
+ value of -1 suppresses the padding byte. On success 0 is returned
+ and the plaintext is available in a newly allocated buffer stored
+ at RESULT with its length stored at RESULTLEN. */
+gpg_error_t
+iso7816_decipher (int slot, const unsigned char *data, size_t datalen,
+ int padind, unsigned char **result, size_t *resultlen)
+{
+ int sw;
+ unsigned char *buf;
+
+ if (!data || !datalen || !result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ if (padind >= 0)
+ {
+ /* We need to prepend the padding indicator. */
+ buf = xtrymalloc (datalen + 1);
+ if (!buf)
+ return gpg_error (gpg_err_code_from_errno (errno));
+
+ *buf = padind; /* Padding indicator. */
+ memcpy (buf+1, data, datalen);
+ sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86,
+ datalen+1, (char*)buf,
+ result, resultlen);
+ xfree (buf);
+ }
+ else
+ {
+ sw = apdu_send (slot, 0x00, CMD_PSO, 0x80, 0x86,
+ datalen, (const char *)data,
+ result, resultlen);
+ }
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+
+ return 0;
+}
+
+
+gpg_error_t
+iso7816_internal_authenticate (int slot,
+ const unsigned char *data, size_t datalen,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+
+ if (!data || !datalen || !result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ sw = apdu_send (slot, 0x00, CMD_INTERNAL_AUTHENTICATE, 0, 0,
+ datalen, (const char*)data, result, resultlen);
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+
+ return 0;
+}
+
+
+static gpg_error_t
+do_generate_keypair (int slot, int readonly,
+ const unsigned char *data, size_t datalen,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+
+ if (!data || !datalen || !result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ sw = apdu_send (slot, 0x00, CMD_GENERATE_KEYPAIR, readonly? 0x81:0x80, 0,
+ datalen, (const char*)data, result, resultlen);
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+
+ return 0;
+}
+
+
+gpg_error_t
+iso7816_generate_keypair (int slot,
+ const unsigned char *data, size_t datalen,
+ unsigned char **result, size_t *resultlen)
+{
+ return do_generate_keypair (slot, 0, data, datalen, result, resultlen);
+}
+
+
+gpg_error_t
+iso7816_read_public_key (int slot,
+ const unsigned char *data, size_t datalen,
+ unsigned char **result, size_t *resultlen)
+{
+ return do_generate_keypair (slot, 1, data, datalen, result, resultlen);
+}
+
+
+
+gpg_error_t
+iso7816_get_challenge (int slot, int length, unsigned char *buffer)
+{
+ int sw;
+ unsigned char *result;
+ size_t resultlen, n;
+
+ if (!buffer || length < 1)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ do
+ {
+ result = NULL;
+ n = length > 254? 254 : length;
+ sw = apdu_send_le (slot, 0x00, CMD_GET_CHALLENGE, 0, 0, -1, NULL,
+ n,
+ &result, &resultlen);
+ if (sw != SW_SUCCESS)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (result);
+ return map_sw (sw);
+ }
+ if (resultlen > n)
+ resultlen = n;
+ memcpy (buffer, result, resultlen);
+ buffer += resultlen;
+ length -= resultlen;
+ xfree (result);
+ }
+ while (length > 0);
+
+ return 0;
+}
+
+/* Perform a READ BINARY command requesting a maximum of NMAX bytes
+ from OFFSET. With NMAX = 0 the entire file is read. The result is
+ stored in a newly allocated buffer at the address passed by RESULT.
+ Returns the length of this data at the address of RESULTLEN. */
+gpg_error_t
+iso7816_read_binary (int slot, size_t offset, size_t nmax,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+ unsigned char *buffer;
+ size_t bufferlen;
+ int read_all = !nmax;
+ size_t n;
+
+ if (!result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ /* We can only encode 15 bits in p0,p1 to indicate an offset. Thus
+ we check for this limit. */
+ if (offset > 32767)
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ do
+ {
+ buffer = NULL;
+ bufferlen = 0;
+ /* Note, that we to set N to 254 due to problems either with the
+ ccid driver or some TCOS cards. It actually should be 0
+ which is the official ISO value to read a variable length
+ object. */
+ if (read_all || nmax > 254)
+ n = 254;
+ else
+ n = nmax;
+ sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY,
+ ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
+ n, &buffer, &bufferlen);
+ if ( SW_EXACT_LENGTH_P(sw) )
+ {
+ n = (sw & 0x00ff);
+ sw = apdu_send_le (slot, 0x00, CMD_READ_BINARY,
+ ((offset>>8) & 0xff), (offset & 0xff) , -1, NULL,
+ n, &buffer, &bufferlen);
+ }
+
+ if (*result && sw == SW_BAD_P0_P1)
+ {
+ /* Bad Parameter means that the offset is outside of the
+ EF. When reading all data we take this as an indication
+ for EOF. */
+ break;
+ }
+
+ if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (buffer);
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+ if (*result) /* Need to extend the buffer. */
+ {
+ unsigned char *p = xtryrealloc (*result, *resultlen + bufferlen);
+ if (!p)
+ {
+ gpg_error_t err = gpg_error_from_errno (errno);
+ xfree (buffer);
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return err;
+ }
+ *result = p;
+ memcpy (*result + *resultlen, buffer, bufferlen);
+ *resultlen += bufferlen;
+ xfree (buffer);
+ buffer = NULL;
+ }
+ else /* Transfer the buffer into our result. */
+ {
+ *result = buffer;
+ *resultlen = bufferlen;
+ }
+ offset += bufferlen;
+ if (offset > 32767)
+ break; /* We simply truncate the result for too large
+ files. */
+ if (nmax > bufferlen)
+ nmax -= bufferlen;
+ else
+ nmax = 0;
+ }
+ while ((read_all && sw != SW_EOF_REACHED) || (!read_all && nmax));
+
+ return 0;
+}
+
+/* Perform a READ RECORD command. RECNO gives the record number to
+ read with 0 indicating the current record. RECCOUNT must be 1 (not
+ all cards support reading of more than one record). SHORT_EF
+ should be 0 to read the current EF or contain a short EF. The
+ result is stored in a newly allocated buffer at the address passed
+ by RESULT. Returns the length of this data at the address of
+ RESULTLEN. */
+gpg_error_t
+iso7816_read_record (int slot, int recno, int reccount, int short_ef,
+ unsigned char **result, size_t *resultlen)
+{
+ int sw;
+ unsigned char *buffer;
+ size_t bufferlen;
+
+ if (!result || !resultlen)
+ return gpg_error (GPG_ERR_INV_VALUE);
+ *result = NULL;
+ *resultlen = 0;
+
+ /* We can only encode 15 bits in p0,p1 to indicate an offset. Thus
+ we check for this limit. */
+ if (recno < 0 || recno > 255 || reccount != 1
+ || short_ef < 0 || short_ef > 254 )
+ return gpg_error (GPG_ERR_INV_VALUE);
+
+ buffer = NULL;
+ bufferlen = 0;
+ /* Fixme: Either the ccid driver or the TCOS cards have problems
+ with an Le of 0. */
+ sw = apdu_send_le (slot, 0x00, CMD_READ_RECORD,
+ recno,
+ short_ef? short_ef : 0x04,
+ -1, NULL,
+ 254, &buffer, &bufferlen);
+
+ if (sw != SW_SUCCESS && sw != SW_EOF_REACHED)
+ {
+ /* Make sure that pending buffers are released. */
+ xfree (buffer);
+ xfree (*result);
+ *result = NULL;
+ *resultlen = 0;
+ return map_sw (sw);
+ }
+ *result = buffer;
+ *resultlen = bufferlen;
+
+ return 0;
+}
+
diff --git a/g10/iso7816.h b/g10/iso7816.h
new file mode 100644
index 0000000..c2e742a
--- /dev/null
+++ b/g10/iso7816.h
@@ -0,0 +1,81 @@
+/* iso7816.h - ISO 7816 commands
+ * Copyright (C) 2003 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ *
+ * $Id: iso7816.h 3886 2005-09-07 17:05:42Z wk $
+ */
+
+#ifndef ISO7816_H
+#define ISO7816_H
+
+#if GNUPG_MAJOR_VERSION == 1
+#include "cardglue.h"
+#endif
+
+gpg_error_t iso7816_map_sw (int sw);
+
+gpg_error_t iso7816_select_application (int slot,
+ const char *aid, size_t aidlen);
+gpg_error_t iso7816_select_file (int slot, int tag, int is_dir,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_select_path (int slot,
+ const unsigned short *path, size_t pathlen,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_list_directory (int slot, int list_dirs,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_verify (int slot,
+ int chvno, const char *chv, size_t chvlen);
+gpg_error_t iso7816_change_reference_data (int slot, int chvno,
+ const char *oldchv, size_t oldchvlen,
+ const char *newchv, size_t newchvlen);
+gpg_error_t iso7816_reset_retry_counter (int slot, int chvno,
+ const char *newchv, size_t newchvlen);
+gpg_error_t iso7816_get_data (int slot, int tag,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_put_data (int slot, int tag,
+ const unsigned char *data, size_t datalen);
+gpg_error_t iso7816_manage_security_env (int slot, int p1, int p2,
+ const unsigned char *data,
+ size_t datalen);
+gpg_error_t iso7816_compute_ds (int slot,
+ const unsigned char *data, size_t datalen,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_decipher (int slot,
+ const unsigned char *data, size_t datalen,
+ int padind,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_internal_authenticate (int slot,
+ const unsigned char *data, size_t datalen,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_generate_keypair (int slot,
+ const unsigned char *data, size_t datalen,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_read_public_key (int slot,
+ const unsigned char *data, size_t datalen,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_get_challenge (int slot,
+ int length, unsigned char *buffer);
+
+gpg_error_t iso7816_read_binary (int slot, size_t offset, size_t nmax,
+ unsigned char **result, size_t *resultlen);
+gpg_error_t iso7816_read_record (int slot, int recno, int reccount,
+ int short_ef,
+ unsigned char **result, size_t *resultlen);
+
+#endif /*ISO7816_H*/
diff --git a/g10/kbnode.c b/g10/kbnode.c
new file mode 100644
index 0000000..9038f5b
--- /dev/null
+++ b/g10/kbnode.c
@@ -0,0 +1,399 @@
+/* kbnode.c - keyblock node utility functions
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002,
+ * 2005 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "util.h"
+#include "memory.h"
+#include "packet.h"
+#include "keydb.h"
+
+#define USE_UNUSED_NODES 1
+
+static KBNODE unused_nodes;
+
+static KBNODE
+alloc_node(void)
+{
+ KBNODE n;
+
+ n = unused_nodes;
+ if( n )
+ unused_nodes = n->next;
+ else
+ n = xmalloc( sizeof *n );
+ n->next = NULL;
+ n->pkt = NULL;
+ n->flag = 0;
+ n->private_flag=0;
+ n->recno = 0;
+ return n;
+}
+
+static void
+free_node( KBNODE n )
+{
+ if( n ) {
+#if USE_UNUSED_NODES
+ n->next = unused_nodes;
+ unused_nodes = n;
+#else
+ xfree( n );
+#endif
+ }
+}
+
+
+
+KBNODE
+new_kbnode( PACKET *pkt )
+{
+ KBNODE n = alloc_node();
+ n->pkt = pkt;
+ return n;
+}
+
+
+KBNODE
+clone_kbnode( KBNODE node )
+{
+ KBNODE n = alloc_node();
+
+ n->pkt = node->pkt;
+ n->private_flag = node->private_flag | 2; /* mark cloned */
+ return n;
+}
+
+
+void
+release_kbnode( KBNODE n )
+{
+ KBNODE n2;
+
+ while( n ) {
+ n2 = n->next;
+ if( !is_cloned_kbnode(n) ) {
+ free_packet( n->pkt );
+ xfree( n->pkt );
+ }
+ free_node( n );
+ n = n2;
+ }
+}
+
+
+/****************
+ * Delete NODE.
+ * Note: This only works with walk_kbnode!!
+ */
+void
+delete_kbnode( KBNODE node )
+{
+ node->private_flag |= 1;
+}
+
+/****************
+ * Append NODE to ROOT. ROOT must exist!
+ */
+void
+add_kbnode( KBNODE root, KBNODE node )
+{
+ KBNODE n1;
+
+ for(n1=root; n1->next; n1 = n1->next)
+ ;
+ n1->next = node;
+}
+
+/****************
+ * Insert NODE into the list after root but before a packet which is not of
+ * type PKTTYPE
+ * (only if PKTTYPE != 0)
+ */
+void
+insert_kbnode( KBNODE root, KBNODE node, int pkttype )
+{
+ if( !pkttype ) {
+ node->next = root->next;
+ root->next = node;
+ }
+ else {
+ KBNODE n1;
+
+ for(n1=root; n1->next; n1 = n1->next)
+ if( pkttype != n1->next->pkt->pkttype ) {
+ node->next = n1->next;
+ n1->next = node;
+ return;
+ }
+ /* no such packet, append */
+ node->next = NULL;
+ n1->next = node;
+ }
+}
+
+
+/****************
+ * Find the previous node (if PKTTYPE = 0) or the previous node
+ * with pkttype PKTTYPE in the list starting with ROOT of NODE.
+ */
+KBNODE
+find_prev_kbnode( KBNODE root, KBNODE node, int pkttype )
+{
+ KBNODE n1;
+
+ for (n1=NULL; root && root != node; root = root->next ) {
+ if (!pkttype ||root->pkt->pkttype == pkttype)
+ n1 = root;
+ }
+ return n1;
+}
+
+/****************
+ * Ditto, but find the next packet. The behaviour is trivial if
+ * PKTTYPE is 0 but if it is specified, the next node with a packet
+ * of this type is returned. The function has some knowledge about
+ * the valid ordering of packets: e.g. if the next signature packet
+ * is requested, the function will not return one if it encounters
+ * a user-id.
+ */
+KBNODE
+find_next_kbnode( KBNODE node, int pkttype )
+{
+ for( node=node->next ; node; node = node->next ) {
+ if( !pkttype )
+ return node;
+ else if( pkttype == PKT_USER_ID
+ && ( node->pkt->pkttype == PKT_PUBLIC_KEY
+ || node->pkt->pkttype == PKT_SECRET_KEY ) )
+ return NULL;
+ else if( pkttype == PKT_SIGNATURE
+ && ( node->pkt->pkttype == PKT_USER_ID
+ || node->pkt->pkttype == PKT_PUBLIC_KEY
+ || node->pkt->pkttype == PKT_SECRET_KEY ) )
+ return NULL;
+ else if( node->pkt->pkttype == pkttype )
+ return node;
+ }
+ return NULL;
+}
+
+
+KBNODE
+find_kbnode( KBNODE node, int pkttype )
+{
+ for( ; node; node = node->next ) {
+ if( node->pkt->pkttype == pkttype )
+ return node;
+ }
+ return NULL;
+}
+
+
+
+/****************
+ * Walk through a list of kbnodes. This function returns
+ * the next kbnode for each call; before using the function the first
+ * time, the caller must set CONTEXT to NULL (This has simply the effect
+ * to start with ROOT).
+ */
+KBNODE
+walk_kbnode( KBNODE root, KBNODE *context, int all )
+{
+ KBNODE n;
+
+ do {
+ if( !*context ) {
+ *context = root;
+ n = root;
+ }
+ else {
+ n = (*context)->next;
+ *context = n;
+ }
+ } while( !all && n && is_deleted_kbnode(n) );
+
+ return n;
+}
+
+void
+clear_kbnode_flags( KBNODE n )
+{
+ for( ; n; n = n->next ) {
+ n->flag = 0;
+ }
+}
+
+
+/****************
+ * Commit changes made to the kblist at ROOT. Note that ROOT my change,
+ * and it is therefore passed by reference.
+ * The function has the effect of removing all nodes marked as deleted.
+ * returns true if any node has been changed
+ */
+int
+commit_kbnode( KBNODE *root )
+{
+ KBNODE n, nl;
+ int changed = 0;
+
+ for( n = *root, nl=NULL; n; n = nl->next ) {
+ if( is_deleted_kbnode(n) ) {
+ if( n == *root )
+ *root = nl = n->next;
+ else
+ nl->next = n->next;
+ if( !is_cloned_kbnode(n) ) {
+ free_packet( n->pkt );
+ xfree( n->pkt );
+ }
+ free_node( n );
+ changed = 1;
+ }
+ else
+ nl = n;
+ }
+ return changed;
+}
+
+void
+remove_kbnode( KBNODE *root, KBNODE node )
+{
+ KBNODE n, nl;
+
+ for( n = *root, nl=NULL; n; n = nl->next ) {
+ if( n == node ) {
+ if( n == *root )
+ *root = nl = n->next;
+ else
+ nl->next = n->next;
+ if( !is_cloned_kbnode(n) ) {
+ free_packet( n->pkt );
+ xfree( n->pkt );
+ }
+ free_node( n );
+ }
+ else
+ nl = n;
+ }
+}
+
+
+/****************
+ * Move NODE behind right after WHERE or to the beginning if WHERE is NULL.
+ */
+void
+move_kbnode( KBNODE *root, KBNODE node, KBNODE where )
+{
+ KBNODE tmp, prev;
+
+ if( !root || !*root || !node )
+ return; /* sanity check */
+ for( prev = *root; prev && prev->next != node; prev = prev->next )
+ ;
+ if( !prev )
+ return; /* node is not in the list */
+
+ if( !where ) { /* move node before root */
+ if( node == *root ) /* move to itself */
+ return;
+ prev->next = node->next;
+ node->next = *root;
+ *root = node;
+ return;
+ }
+ /* move it after where */
+ if( node == where )
+ return;
+ tmp = node->next;
+ node->next = where->next;
+ where->next = node;
+ prev->next = tmp;
+}
+
+
+
+
+void
+dump_kbnode( KBNODE node )
+{
+ for(; node; node = node->next ) {
+ const char *s;
+ switch( node->pkt->pkttype ) {
+ case 0: s="empty"; break;
+ case PKT_PUBLIC_KEY: s="public-key"; break;
+ case PKT_SECRET_KEY: s="secret-key"; break;
+ case PKT_SECRET_SUBKEY: s= "secret-subkey"; break;
+ case PKT_PUBKEY_ENC: s="public-enc"; break;
+ case PKT_SIGNATURE: s="signature"; break;
+ case PKT_ONEPASS_SIG: s="onepass-sig"; break;
+ case PKT_USER_ID: s="user-id"; break;
+ case PKT_PUBLIC_SUBKEY: s="public-subkey"; break;
+ case PKT_COMMENT: s="comment"; break;
+ case PKT_RING_TRUST: s="trust"; break;
+ case PKT_PLAINTEXT: s="plaintext"; break;
+ case PKT_COMPRESSED: s="compressed"; break;
+ case PKT_ENCRYPTED: s="encrypted"; break;
+ case PKT_GPG_CONTROL: s="gpg-control"; break;
+ default: s="unknown"; break;
+ }
+ fprintf(stderr, "node %p %02x/%02x type=%s",
+ node, node->flag, node->private_flag, s);
+ if( node->pkt->pkttype == PKT_USER_ID ) {
+ PKT_user_id *uid = node->pkt->pkt.user_id;
+ fputs(" \"", stderr);
+ print_string( stderr, uid->name, uid->len, 0 );
+ fprintf (stderr, "\" %c%c%c%c\n",
+ uid->is_expired? 'e':'.',
+ uid->is_revoked? 'r':'.',
+ uid->created? 'v':'.',
+ uid->is_primary? 'p':'.' );
+ }
+ else if( node->pkt->pkttype == PKT_SIGNATURE ) {
+ fprintf(stderr, " class=%02x keyid=%08lX ts=%lu\n",
+ node->pkt->pkt.signature->sig_class,
+ (ulong)node->pkt->pkt.signature->keyid[1],
+ (ulong)node->pkt->pkt.signature->timestamp);
+ }
+ else if( node->pkt->pkttype == PKT_GPG_CONTROL ) {
+ fprintf(stderr, " ctrl=%d len=%u\n",
+ node->pkt->pkt.gpg_control->control,
+ (unsigned int)node->pkt->pkt.gpg_control->datalen);
+ }
+ else if( node->pkt->pkttype == PKT_PUBLIC_KEY
+ || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+ PKT_public_key *pk = node->pkt->pkt.public_key;
+ fprintf(stderr, " keyid=%08lX a=%d u=%d %c%c%c%c\n",
+ (ulong)keyid_from_pk( pk, NULL ),
+ pk->pubkey_algo, pk->pubkey_usage,
+ pk->has_expired? 'e':'.',
+ pk->is_revoked? 'r':'.',
+ pk->is_valid? 'v':'.',
+ pk->mdc_feature? 'm':'.');
+ }
+ else
+ fputs("\n", stderr);
+ }
+}
diff --git a/g10/keydb.c b/g10/keydb.c
new file mode 100644
index 0000000..b1a7268
--- /dev/null
+++ b/g10/keydb.c
@@ -0,0 +1,807 @@
+/* keydb.c - key database dispatcher
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+
+#include "util.h"
+#include "options.h"
+#include "main.h" /*try_make_homedir ()*/
+#include "packet.h"
+#include "keyring.h"
+#include "keydb.h"
+#include "i18n.h"
+
+static int active_handles;
+
+typedef enum {
+ KEYDB_RESOURCE_TYPE_NONE = 0,
+ KEYDB_RESOURCE_TYPE_KEYRING
+} KeydbResourceType;
+#define MAX_KEYDB_RESOURCES 40
+
+struct resource_item {
+ KeydbResourceType type;
+ union {
+ KEYRING_HANDLE kr;
+ } u;
+ void *token;
+ int secret;
+};
+
+static struct resource_item all_resources[MAX_KEYDB_RESOURCES];
+static int used_resources;
+static void *primary_keyring=NULL;
+
+struct keydb_handle {
+ int locked;
+ int found;
+ int current;
+ int used; /* items in active */
+ struct resource_item active[MAX_KEYDB_RESOURCES];
+};
+
+
+static int lock_all (KEYDB_HANDLE hd);
+static void unlock_all (KEYDB_HANDLE hd);
+
+
+/* Handle the creation of a keyring if it does not yet exist. Take
+ into acount that other processes might have the keyring already
+ locked. This lock check does not work if the directory itself is
+ not yet available. */
+static int
+maybe_create_keyring (char *filename, int force)
+{
+ DOTLOCK lockhd = NULL;
+ IOBUF iobuf;
+ int rc;
+ mode_t oldmask;
+ char *last_slash_in_filename;
+
+ /* A quick test whether the filename already exists. */
+ if (!access (filename, F_OK))
+ return 0;
+
+ /* If we don't want to create a new file at all, there is no need to
+ go any further - bail out right here. */
+ if (!force)
+ return G10ERR_OPEN_FILE;
+
+ /* First of all we try to create the home directory. Note, that we
+ don't do any locking here because any sane application of gpg
+ would create the home directory by itself and not rely on gpg's
+ tricky auto-creation which is anyway only done for some home
+ directory name patterns. */
+ last_slash_in_filename = strrchr (filename, DIRSEP_C);
+ *last_slash_in_filename = 0;
+ if (access(filename, F_OK))
+ {
+ static int tried;
+
+ if (!tried)
+ {
+ tried = 1;
+ try_make_homedir (filename);
+ }
+ if (access (filename, F_OK))
+ {
+ rc = G10ERR_OPEN_FILE;
+ *last_slash_in_filename = DIRSEP_C;
+ goto leave;
+ }
+ }
+ *last_slash_in_filename = DIRSEP_C;
+
+
+ /* To avoid races with other instances of gpg trying to create or
+ update the keyring (it is removed during an update for a short
+ time), we do the next stuff in a locked state. */
+ lockhd = create_dotlock (filename);
+ if (!lockhd)
+ {
+ /* A reason for this to fail is that the directory is not
+ writable. However, this whole locking stuff does not make
+ sense if this is the case. An empty non-writable directory
+ with no keyring is not really useful at all. */
+ if (opt.verbose)
+ log_info ("can't allocate lock for `%s'\n", filename );
+
+ if (!force)
+ return G10ERR_OPEN_FILE;
+ else
+ return G10ERR_GENERAL;
+ }
+
+ if ( make_dotlock (lockhd, -1) )
+ {
+ /* This is something bad. Probably a stale lockfile. */
+ log_info ("can't lock `%s'\n", filename );
+ rc = G10ERR_GENERAL;
+ goto leave;
+ }
+
+ /* Now the real test while we are locked. */
+ if (!access(filename, F_OK))
+ {
+ rc = 0; /* Okay, we may access the file now. */
+ goto leave;
+ }
+
+ /* The file does not yet exist, create it now. */
+ oldmask = umask (077);
+ if (is_secured_filename (filename))
+ {
+ iobuf = NULL;
+ errno = EPERM;
+ }
+ else
+ iobuf = iobuf_create (filename);
+ umask (oldmask);
+ if (!iobuf)
+ {
+ log_error ( _("error creating keyring `%s': %s\n"),
+ filename, strerror(errno));
+ rc = G10ERR_OPEN_FILE;
+ goto leave;
+ }
+
+ if (!opt.quiet)
+ log_info (_("keyring `%s' created\n"), filename);
+
+ iobuf_close (iobuf);
+ /* Must invalidate that ugly cache */
+ iobuf_ioctl (NULL, 2, 0, filename);
+ rc = 0;
+
+ leave:
+ if (lockhd)
+ {
+ release_dotlock (lockhd);
+ destroy_dotlock (lockhd);
+ }
+ return rc;
+}
+
+
+/*
+ * Register a resource (which currently may only be a keyring file).
+ * The first keyring which is added by this function is
+ * created if it does not exist.
+ * Note: this function may be called before secure memory is
+ * available.
+ * Flag 1 == force
+ * Flag 2 == mark resource as primary
+ * Flag 4 == This is a default resources
+ */
+int
+keydb_add_resource (const char *url, int flags, int secret)
+{
+ static int any_secret, any_public;
+ const char *resname = url;
+ char *filename = NULL;
+ int force=(flags&1);
+ int rc = 0;
+ KeydbResourceType rt = KEYDB_RESOURCE_TYPE_NONE;
+ void *token;
+
+ /* Do we have an URL?
+ * gnupg-ring:filename := this is a plain keyring
+ * filename := See what is is, but create as plain keyring.
+ */
+ if (strlen (resname) > 11) {
+ if (!strncmp( resname, "gnupg-ring:", 11) ) {
+ rt = KEYDB_RESOURCE_TYPE_KEYRING;
+ resname += 11;
+ }
+#if !defined(HAVE_DRIVE_LETTERS) && !defined(__riscos__)
+ else if (strchr (resname, ':')) {
+ log_error ("invalid key resource URL `%s'\n", url );
+ rc = G10ERR_GENERAL;
+ goto leave;
+ }
+#endif /* !HAVE_DRIVE_LETTERS && !__riscos__ */
+ }
+
+ if (*resname != DIRSEP_C ) { /* do tilde expansion etc */
+ if (strchr(resname, DIRSEP_C) )
+ filename = make_filename (resname, NULL);
+ else
+ filename = make_filename (opt.homedir, resname, NULL);
+ }
+ else
+ filename = xstrdup (resname);
+
+ if (!force)
+ force = secret? !any_secret : !any_public;
+
+ /* see whether we can determine the filetype */
+ if (rt == KEYDB_RESOURCE_TYPE_NONE) {
+ FILE *fp = fopen( filename, "rb" );
+
+ if (fp) {
+ u32 magic;
+
+ if (fread( &magic, 4, 1, fp) == 1 ) {
+ if (magic == 0x13579ace || magic == 0xce9a5713)
+ ; /* GDBM magic - no more support */
+ else
+ rt = KEYDB_RESOURCE_TYPE_KEYRING;
+ }
+ else /* maybe empty: assume ring */
+ rt = KEYDB_RESOURCE_TYPE_KEYRING;
+ fclose( fp );
+ }
+ else /* no file yet: create ring */
+ rt = KEYDB_RESOURCE_TYPE_KEYRING;
+ }
+
+ switch (rt) {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ log_error ("unknown type of key resource `%s'\n", url );
+ rc = G10ERR_GENERAL;
+ goto leave;
+
+ case KEYDB_RESOURCE_TYPE_KEYRING:
+ rc = maybe_create_keyring (filename, force);
+ if (rc)
+ goto leave;
+
+ if(keyring_register_filename (filename, secret, &token))
+ {
+ if (used_resources >= MAX_KEYDB_RESOURCES)
+ rc = G10ERR_RESOURCE_LIMIT;
+ else
+ {
+ if(flags&2)
+ primary_keyring=token;
+ all_resources[used_resources].type = rt;
+ all_resources[used_resources].u.kr = NULL; /* Not used here */
+ all_resources[used_resources].token = token;
+ all_resources[used_resources].secret = secret;
+ used_resources++;
+ }
+ }
+ else
+ {
+ /* This keyring was already registered, so ignore it.
+ However, we can still mark it as primary even if it was
+ already registered. */
+ if(flags&2)
+ primary_keyring=token;
+ }
+ break;
+
+ default:
+ log_error ("resource type of `%s' not supported\n", url);
+ rc = G10ERR_GENERAL;
+ goto leave;
+ }
+
+ /* fixme: check directory permissions and print a warning */
+
+ leave:
+ if (rc)
+ {
+ /* Secret keyrings are not required in all cases. To avoid
+ having gpg return failure we use log_info here if the
+ rewsource is a secret one and marked as default
+ resource. */
+ if ((flags&4) && secret)
+ log_info (_("keyblock resource `%s': %s\n"),
+ filename, g10_errstr(rc));
+ else
+ log_error (_("keyblock resource `%s': %s\n"),
+ filename, g10_errstr(rc));
+ }
+ else if (secret)
+ any_secret = 1;
+ else
+ any_public = 1;
+ xfree (filename);
+ return rc;
+}
+
+
+
+
+KEYDB_HANDLE
+keydb_new (int secret)
+{
+ KEYDB_HANDLE hd;
+ int i, j;
+
+ hd = xmalloc_clear (sizeof *hd);
+ hd->found = -1;
+
+ assert (used_resources <= MAX_KEYDB_RESOURCES);
+ for (i=j=0; i < used_resources; i++)
+ {
+ if (!all_resources[i].secret != !secret)
+ continue;
+ switch (all_resources[i].type)
+ {
+ case KEYDB_RESOURCE_TYPE_NONE: /* ignore */
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYRING:
+ hd->active[j].type = all_resources[i].type;
+ hd->active[j].token = all_resources[i].token;
+ hd->active[j].secret = all_resources[i].secret;
+ hd->active[j].u.kr = keyring_new (all_resources[i].token, secret);
+ if (!hd->active[j].u.kr) {
+ xfree (hd);
+ return NULL; /* fixme: release all previously allocated handles*/
+ }
+ j++;
+ break;
+ }
+ }
+ hd->used = j;
+
+ active_handles++;
+ return hd;
+}
+
+void
+keydb_release (KEYDB_HANDLE hd)
+{
+ int i;
+
+ if (!hd)
+ return;
+ assert (active_handles > 0);
+ active_handles--;
+
+ unlock_all (hd);
+ for (i=0; i < hd->used; i++) {
+ switch (hd->active[i].type) {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYRING:
+ keyring_release (hd->active[i].u.kr);
+ break;
+ }
+ }
+
+ xfree (hd);
+}
+
+
+/*
+ * Return the name of the current resource. This is function first
+ * looks for the last found found, then for the current search
+ * position, and last returns the first available resource. The
+ * returned string is only valid as long as the handle exists. This
+ * function does only return NULL if no handle is specified, in all
+ * other error cases an empty string is returned.
+ */
+const char *
+keydb_get_resource_name (KEYDB_HANDLE hd)
+{
+ int idx;
+ const char *s = NULL;
+
+ if (!hd)
+ return NULL;
+
+ if ( hd->found >= 0 && hd->found < hd->used)
+ idx = hd->found;
+ else if ( hd->current >= 0 && hd->current < hd->used)
+ idx = hd->current;
+ else
+ idx = 0;
+
+ switch (hd->active[idx].type) {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ s = NULL;
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYRING:
+ s = keyring_get_resource_name (hd->active[idx].u.kr);
+ break;
+ }
+
+ return s? s: "";
+}
+
+
+
+static int
+lock_all (KEYDB_HANDLE hd)
+{
+ int i, rc = 0;
+
+ for (i=0; !rc && i < hd->used; i++) {
+ switch (hd->active[i].type) {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYRING:
+ rc = keyring_lock (hd->active[i].u.kr, 1);
+ break;
+ }
+ }
+
+ if (rc) {
+ /* revert the already set locks */
+ for (i--; i >= 0; i--) {
+ switch (hd->active[i].type) {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYRING:
+ keyring_lock (hd->active[i].u.kr, 0);
+ break;
+ }
+ }
+ }
+ else
+ hd->locked = 1;
+
+ return rc;
+}
+
+static void
+unlock_all (KEYDB_HANDLE hd)
+{
+ int i;
+
+ if (!hd->locked)
+ return;
+
+ for (i=hd->used-1; i >= 0; i--) {
+ switch (hd->active[i].type) {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYRING:
+ keyring_lock (hd->active[i].u.kr, 0);
+ break;
+ }
+ }
+ hd->locked = 0;
+}
+
+
+/*
+ * Return the last found keyring. Caller must free it.
+ * The returned keyblock has the kbode flag bit 0 set for the node with
+ * the public key used to locate the keyblock or flag bit 1 set for
+ * the user ID node.
+ */
+int
+keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb)
+{
+ int rc = 0;
+
+ if (!hd)
+ return G10ERR_INV_ARG;
+
+ if ( hd->found < 0 || hd->found >= hd->used)
+ return -1; /* nothing found */
+
+ switch (hd->active[hd->found].type) {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ rc = G10ERR_GENERAL; /* oops */
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYRING:
+ rc = keyring_get_keyblock (hd->active[hd->found].u.kr, ret_kb);
+ break;
+ }
+
+ return rc;
+}
+
+/*
+ * update the current keyblock with KB
+ */
+int
+keydb_update_keyblock (KEYDB_HANDLE hd, KBNODE kb)
+{
+ int rc = 0;
+
+ if (!hd)
+ return G10ERR_INV_ARG;
+
+ if ( hd->found < 0 || hd->found >= hd->used)
+ return -1; /* nothing found */
+
+ if( opt.dry_run )
+ return 0;
+
+ rc = lock_all (hd);
+ if (rc)
+ return rc;
+
+ switch (hd->active[hd->found].type) {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ rc = G10ERR_GENERAL; /* oops */
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYRING:
+ rc = keyring_update_keyblock (hd->active[hd->found].u.kr, kb);
+ break;
+ }
+
+ unlock_all (hd);
+ return rc;
+}
+
+
+/*
+ * Insert a new KB into one of the resources.
+ */
+int
+keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb)
+{
+ int rc = -1;
+ int idx;
+
+ if (!hd)
+ return G10ERR_INV_ARG;
+
+ if( opt.dry_run )
+ return 0;
+
+ if ( hd->found >= 0 && hd->found < hd->used)
+ idx = hd->found;
+ else if ( hd->current >= 0 && hd->current < hd->used)
+ idx = hd->current;
+ else
+ return G10ERR_GENERAL;
+
+ rc = lock_all (hd);
+ if (rc)
+ return rc;
+
+ switch (hd->active[idx].type) {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ rc = G10ERR_GENERAL; /* oops */
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYRING:
+ rc = keyring_insert_keyblock (hd->active[idx].u.kr, kb);
+ break;
+ }
+
+ unlock_all (hd);
+ return rc;
+}
+
+
+/*
+ * The current keyblock will be deleted.
+ */
+int
+keydb_delete_keyblock (KEYDB_HANDLE hd)
+{
+ int rc = -1;
+
+ if (!hd)
+ return G10ERR_INV_ARG;
+
+ if ( hd->found < 0 || hd->found >= hd->used)
+ return -1; /* nothing found */
+
+ if( opt.dry_run )
+ return 0;
+
+ rc = lock_all (hd);
+ if (rc)
+ return rc;
+
+ switch (hd->active[hd->found].type) {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ rc = G10ERR_GENERAL; /* oops */
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYRING:
+ rc = keyring_delete_keyblock (hd->active[hd->found].u.kr);
+ break;
+ }
+
+ unlock_all (hd);
+ return rc;
+}
+
+
+/*
+ * Locate the default writable key resource, so that the next
+ * operation (which is only relevant for inserts) will be done on this
+ * resource.
+ */
+int
+keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved)
+{
+ int rc;
+
+ if (!hd)
+ return G10ERR_INV_ARG;
+
+ rc = keydb_search_reset (hd); /* this does reset hd->current */
+ if (rc)
+ return rc;
+
+ /* If we have a primary set, try that one first */
+ if(primary_keyring)
+ {
+ for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++)
+ {
+ if(hd->active[hd->current].token==primary_keyring)
+ {
+ if(keyring_is_writable (hd->active[hd->current].token))
+ return 0;
+ else
+ break;
+ }
+ }
+
+ rc = keydb_search_reset (hd); /* this does reset hd->current */
+ if (rc)
+ return rc;
+ }
+
+ for ( ; hd->current >= 0 && hd->current < hd->used; hd->current++)
+ {
+ switch (hd->active[hd->current].type)
+ {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ BUG();
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYRING:
+ if (keyring_is_writable (hd->active[hd->current].token))
+ return 0; /* found (hd->current is set to it) */
+ break;
+ }
+ }
+
+ return -1;
+}
+
+/*
+ * Rebuild the caches of all key resources.
+ */
+void
+keydb_rebuild_caches (int noisy)
+{
+ int i, rc;
+
+ for (i=0; i < used_resources; i++)
+ {
+ if (all_resources[i].secret)
+ continue;
+ switch (all_resources[i].type)
+ {
+ case KEYDB_RESOURCE_TYPE_NONE: /* ignore */
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYRING:
+ rc = keyring_rebuild_cache (all_resources[i].token,noisy);
+ if (rc)
+ log_error (_("failed to rebuild keyring cache: %s\n"),
+ g10_errstr (rc));
+ break;
+ }
+ }
+}
+
+
+
+/*
+ * Start the next search on this handle right at the beginning
+ */
+int
+keydb_search_reset (KEYDB_HANDLE hd)
+{
+ int i, rc = 0;
+
+ if (!hd)
+ return G10ERR_INV_ARG;
+
+ hd->current = 0;
+ hd->found = -1;
+ /* and reset all resources */
+ for (i=0; !rc && i < hd->used; i++) {
+ switch (hd->active[i].type) {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYRING:
+ rc = keyring_search_reset (hd->active[i].u.kr);
+ break;
+ }
+ }
+ return rc;
+}
+
+
+/*
+ * Search through all keydb resources, starting at the current position,
+ * for a keyblock which contains one of the keys described in the DESC array.
+ */
+int
+keydb_search2 (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
+ size_t ndesc, size_t *descindex)
+{
+ int rc = -1;
+
+ if (!hd)
+ return G10ERR_INV_ARG;
+
+ while (rc == -1 && hd->current >= 0 && hd->current < hd->used) {
+ switch (hd->active[hd->current].type) {
+ case KEYDB_RESOURCE_TYPE_NONE:
+ BUG(); /* we should never see it here */
+ break;
+ case KEYDB_RESOURCE_TYPE_KEYRING:
+ rc = keyring_search (hd->active[hd->current].u.kr, desc,
+ ndesc, descindex);
+ break;
+ }
+ if (rc == -1) /* EOF -> switch to next resource */
+ hd->current++;
+ else if (!rc)
+ hd->found = hd->current;
+ }
+
+ return rc;
+}
+
+int
+keydb_search_first (KEYDB_HANDLE hd)
+{
+ KEYDB_SEARCH_DESC desc;
+
+ memset (&desc, 0, sizeof desc);
+ desc.mode = KEYDB_SEARCH_MODE_FIRST;
+ return keydb_search (hd, &desc, 1);
+}
+
+int
+keydb_search_next (KEYDB_HANDLE hd)
+{
+ KEYDB_SEARCH_DESC desc;
+
+ memset (&desc, 0, sizeof desc);
+ desc.mode = KEYDB_SEARCH_MODE_NEXT;
+ return keydb_search (hd, &desc, 1);
+}
+
+int
+keydb_search_kid (KEYDB_HANDLE hd, u32 *kid)
+{
+ KEYDB_SEARCH_DESC desc;
+
+ memset (&desc, 0, sizeof desc);
+ desc.mode = KEYDB_SEARCH_MODE_LONG_KID;
+ desc.u.kid[0] = kid[0];
+ desc.u.kid[1] = kid[1];
+ return keydb_search (hd, &desc, 1);
+}
+
+int
+keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr)
+{
+ KEYDB_SEARCH_DESC desc;
+
+ memset (&desc, 0, sizeof desc);
+ desc.mode = KEYDB_SEARCH_MODE_FPR;
+ memcpy (desc.u.fpr, fpr, MAX_FINGERPRINT_LEN);
+ return keydb_search (hd, &desc, 1);
+}
diff --git a/g10/keydb.h b/g10/keydb.h
new file mode 100644
index 0000000..b588381
--- /dev/null
+++ b/g10/keydb.h
@@ -0,0 +1,317 @@
+/* keydb.h - Key database
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef G10_KEYDB_H
+#define G10_KEYDB_H
+
+#include "types.h"
+#include "global.h"
+#include "packet.h"
+#include "cipher.h"
+#ifdef ENABLE_AGENT_SUPPORT
+#include "assuan.h"
+#endif
+
+/* What qualifies as a certification (rather than a signature?) */
+#define IS_CERT(s) (IS_KEY_SIG(s) || IS_UID_SIG(s) || IS_SUBKEY_SIG(s) \
+ || IS_KEY_REV(s) || IS_UID_REV(s) || IS_SUBKEY_REV(s))
+#define IS_SIG(s) (!IS_CERT(s))
+#define IS_KEY_SIG(s) ((s)->sig_class == 0x1f)
+#define IS_UID_SIG(s) (((s)->sig_class & ~3) == 0x10)
+#define IS_SUBKEY_SIG(s) ((s)->sig_class == 0x18)
+#define IS_KEY_REV(s) ((s)->sig_class == 0x20)
+#define IS_UID_REV(s) ((s)->sig_class == 0x30)
+#define IS_SUBKEY_REV(s) ((s)->sig_class == 0x28)
+
+struct getkey_ctx_s;
+typedef struct getkey_ctx_s *GETKEY_CTX;
+
+/****************
+ * A Keyblock is all packets which form an entire certificate;
+ * i.e. the public key, certificate, trust packets, user ids,
+ * signatures, and subkey.
+ *
+ * This structure is also used to bind arbitrary packets together.
+ */
+
+struct kbnode_struct {
+ KBNODE next;
+ PACKET *pkt;
+ int flag;
+ int private_flag;
+ ulong recno; /* used while updating the trustdb */
+};
+
+#define is_deleted_kbnode(a) ((a)->private_flag & 1)
+#define is_cloned_kbnode(a) ((a)->private_flag & 2)
+
+
+enum resource_type {
+ rt_UNKNOWN = 0,
+ rt_RING = 1
+};
+
+
+/****************
+ * A data structre to hold information about the external position
+ * of a keyblock.
+ */
+struct keyblock_pos_struct {
+ int resno; /* resource number */
+ enum resource_type rt;
+ off_t offset; /* position information */
+ unsigned count; /* length of the keyblock in packets */
+ IOBUF fp; /* used by enum_keyblocks */
+ int secret; /* working on a secret keyring */
+ PACKET *pkt; /* ditto */
+ int valid;
+};
+typedef struct keyblock_pos_struct KBPOS;
+
+/* structure to hold a couple of public key certificates */
+typedef struct pk_list *PK_LIST;
+struct pk_list {
+ PK_LIST next;
+ PKT_public_key *pk;
+ int flags; /* flag bit 1==throw_keyid */
+};
+
+/* structure to hold a couple of secret key certificates */
+typedef struct sk_list *SK_LIST;
+struct sk_list {
+ SK_LIST next;
+ PKT_secret_key *sk;
+ int mark; /* not used */
+};
+
+/* structure to collect all information which can be used to
+ * identify a public key */
+typedef struct pubkey_find_info *PUBKEY_FIND_INFO;
+struct pubkey_find_info {
+ u32 keyid[2];
+ unsigned nbits;
+ byte pubkey_algo;
+ byte fingerprint[MAX_FINGERPRINT_LEN];
+ char userid[1];
+};
+
+
+typedef struct keydb_handle *KEYDB_HANDLE;
+
+typedef enum {
+ KEYDB_SEARCH_MODE_NONE,
+ KEYDB_SEARCH_MODE_EXACT,
+ KEYDB_SEARCH_MODE_SUBSTR,
+ KEYDB_SEARCH_MODE_MAIL,
+ KEYDB_SEARCH_MODE_MAILSUB,
+ KEYDB_SEARCH_MODE_MAILEND,
+ KEYDB_SEARCH_MODE_WORDS,
+ KEYDB_SEARCH_MODE_SHORT_KID,
+ KEYDB_SEARCH_MODE_LONG_KID,
+ KEYDB_SEARCH_MODE_FPR16,
+ KEYDB_SEARCH_MODE_FPR20,
+ KEYDB_SEARCH_MODE_FPR,
+ KEYDB_SEARCH_MODE_FIRST,
+ KEYDB_SEARCH_MODE_NEXT
+} KeydbSearchMode;
+
+struct keydb_search_desc {
+ KeydbSearchMode mode;
+ int (*skipfnc)(void *,u32*,PKT_user_id*);
+ void *skipfncvalue;
+ union {
+ const char *name;
+ byte fpr[MAX_FINGERPRINT_LEN];
+ u32 kid[2];
+ } u;
+ int exact;
+};
+
+/*-- keydb.c --*/
+
+/*
+ Flag 1 == force
+ Flag 2 == default
+*/
+int keydb_add_resource (const char *url, int flags, int secret);
+KEYDB_HANDLE keydb_new (int secret);
+void keydb_release (KEYDB_HANDLE hd);
+const char *keydb_get_resource_name (KEYDB_HANDLE hd);
+int keydb_get_keyblock (KEYDB_HANDLE hd, KBNODE *ret_kb);
+int keydb_update_keyblock (KEYDB_HANDLE hd, KBNODE kb);
+int keydb_insert_keyblock (KEYDB_HANDLE hd, KBNODE kb);
+int keydb_delete_keyblock (KEYDB_HANDLE hd);
+int keydb_locate_writable (KEYDB_HANDLE hd, const char *reserved);
+void keydb_rebuild_caches (int noisy);
+int keydb_search_reset (KEYDB_HANDLE hd);
+#define keydb_search(a,b,c) keydb_search2((a),(b),(c),NULL)
+int keydb_search2 (KEYDB_HANDLE hd, KEYDB_SEARCH_DESC *desc,
+ size_t ndesc, size_t *descindex);
+int keydb_search_first (KEYDB_HANDLE hd);
+int keydb_search_next (KEYDB_HANDLE hd);
+int keydb_search_kid (KEYDB_HANDLE hd, u32 *kid);
+int keydb_search_fpr (KEYDB_HANDLE hd, const byte *fpr);
+
+
+/*-- pkclist.c --*/
+void show_revocation_reason( PKT_public_key *pk, int mode );
+int check_signatures_trust( PKT_signature *sig );
+void release_pk_list( PK_LIST pk_list );
+int build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned use );
+union pref_hint
+{
+ int digest_length;
+};
+int algo_available( preftype_t preftype, int algo,
+ const union pref_hint *hint );
+int select_algo_from_prefs( PK_LIST pk_list, int preftype,
+ int request, const union pref_hint *hint );
+int select_mdc_from_pklist (PK_LIST pk_list);
+
+/*-- skclist.c --*/
+void release_sk_list( SK_LIST sk_list );
+int build_sk_list( STRLIST locusr, SK_LIST *ret_sk_list,
+ int unlock, unsigned use );
+
+/*-- passphrase.h --*/
+#ifdef ENABLE_AGENT_SUPPORT
+assuan_context_t agent_open (int try, const char *orig_codeset);
+void agent_close (assuan_context_t ctx);
+#else
+/* If we build w/o agent support, assuan.h won't get included and thus
+ we need to define a replacement for some Assuan types. */
+typedef int assuan_error_t;
+typedef void *assuan_context_t;
+#endif
+int have_static_passphrase(void);
+void set_passphrase_from_string(const char *pass);
+void read_passphrase_from_fd( int fd );
+void passphrase_clear_cache ( u32 *keyid, const char *cacheid, int algo );
+char *ask_passphrase (const char *description,
+ const char *tryagain_text,
+ const char *promptid,
+ const char *prompt,
+ const char *cacheid, int *canceled);
+DEK *passphrase_to_dek( u32 *keyid, int pubkey_algo,
+ int cipher_algo, STRING2KEY *s2k, int mode,
+ const char *tryagain_text, int *canceled);
+void set_next_passphrase( const char *s );
+char *get_last_passphrase(void);
+void next_to_last_passphrase(void);
+
+/*-- getkey.c --*/
+int classify_user_id( const char *name, KEYDB_SEARCH_DESC *desc);
+void cache_public_key( PKT_public_key *pk );
+void getkey_disable_caches(void);
+int get_pubkey( PKT_public_key *pk, u32 *keyid );
+int get_pubkey_fast ( PKT_public_key *pk, u32 *keyid );
+KBNODE get_pubkeyblock( u32 *keyid );
+int get_pubkey_byname( PKT_public_key *pk, const char *name,
+ KBNODE *ret_keyblock, KEYDB_HANDLE *ret_kdbhd,
+ int include_unusable );
+int get_pubkey_bynames( GETKEY_CTX *rx, PKT_public_key *pk,
+ STRLIST names, KBNODE *ret_keyblock );
+int get_pubkey_next( GETKEY_CTX ctx, PKT_public_key *pk, KBNODE *ret_keyblock );
+void get_pubkey_end( GETKEY_CTX ctx );
+int get_seckey( PKT_secret_key *sk, u32 *keyid );
+int get_primary_seckey( PKT_secret_key *sk, u32 *keyid );
+int get_pubkey_byfprint( PKT_public_key *pk, const byte *fprint,
+ size_t fprint_len );
+int get_pubkey_byfprint_fast (PKT_public_key *pk,
+ const byte *fprint, size_t fprint_len);
+int get_keyblock_byfprint( KBNODE *ret_keyblock, const byte *fprint,
+ size_t fprint_len );
+int get_keyblock_bylid( KBNODE *ret_keyblock, ulong lid );
+int seckey_available( u32 *keyid );
+int get_seckey_byname( PKT_secret_key *sk, const char *name, int unlock );
+int get_seckey_bynames( GETKEY_CTX *rx, PKT_secret_key *sk,
+ STRLIST names, KBNODE *ret_keyblock );
+int get_seckey_next (GETKEY_CTX ctx, PKT_secret_key *sk, KBNODE *ret_keyblock);
+void get_seckey_end( GETKEY_CTX ctx );
+
+int get_seckey_byfprint( PKT_secret_key *sk,
+ const byte *fprint, size_t fprint_len);
+int get_seckeyblock_byfprint (KBNODE *ret_keyblock, const byte *fprint,
+ size_t fprint_len );
+
+
+int enum_secret_keys( void **context, PKT_secret_key *sk,
+ int with_subkeys, int with_spm );
+void merge_keys_and_selfsig( KBNODE keyblock );
+char*get_user_id_string( u32 *keyid );
+char*get_user_id_string_native( u32 *keyid );
+char*get_long_user_id_string( u32 *keyid );
+char*get_user_id( u32 *keyid, size_t *rn );
+char*get_user_id_native( u32 *keyid );
+KEYDB_HANDLE get_ctx_handle(GETKEY_CTX ctx);
+void release_akl(void);
+int parse_auto_key_locate(char *options);
+
+/*-- keyid.c --*/
+int pubkey_letter( int algo );
+void hash_public_key( MD_HANDLE md, PKT_public_key *pk );
+size_t keystrlen(void);
+const char *keystr(u32 *keyid);
+const char *keystr_from_pk(PKT_public_key *pk);
+const char *keystr_from_sk(PKT_secret_key *sk);
+const char *keystr_from_desc(KEYDB_SEARCH_DESC *desc);
+u32 keyid_from_sk( PKT_secret_key *sk, u32 *keyid );
+u32 keyid_from_pk( PKT_public_key *pk, u32 *keyid );
+u32 keyid_from_sig( PKT_signature *sig, u32 *keyid );
+u32 keyid_from_fingerprint(const byte *fprint, size_t fprint_len, u32 *keyid);
+byte *namehash_from_uid(PKT_user_id *uid);
+unsigned nbits_from_pk( PKT_public_key *pk );
+unsigned nbits_from_sk( PKT_secret_key *sk );
+const char *datestr_from_pk( PKT_public_key *pk );
+const char *datestr_from_sk( PKT_secret_key *sk );
+const char *datestr_from_sig( PKT_signature *sig );
+const char *expirestr_from_pk( PKT_public_key *pk );
+const char *expirestr_from_sk( PKT_secret_key *sk );
+const char *expirestr_from_sig( PKT_signature *sig );
+const char *revokestr_from_pk( PKT_public_key *pk );
+const char *usagestr_from_pk( PKT_public_key *pk );
+const char *colon_strtime (u32 t);
+const char *colon_datestr_from_pk (PKT_public_key *pk);
+const char *colon_datestr_from_sk (PKT_secret_key *sk);
+const char *colon_datestr_from_sig (PKT_signature *sig);
+const char *colon_expirestr_from_sig (PKT_signature *sig);
+byte *fingerprint_from_sk( PKT_secret_key *sk, byte *buf, size_t *ret_len );
+byte *fingerprint_from_pk( PKT_public_key *pk, byte *buf, size_t *ret_len );
+
+/*-- kbnode.c --*/
+KBNODE new_kbnode( PACKET *pkt );
+KBNODE clone_kbnode( KBNODE node );
+void release_kbnode( KBNODE n );
+void delete_kbnode( KBNODE node );
+void add_kbnode( KBNODE root, KBNODE node );
+void insert_kbnode( KBNODE root, KBNODE node, int pkttype );
+void move_kbnode( KBNODE *root, KBNODE node, KBNODE where );
+void remove_kbnode( KBNODE *root, KBNODE node );
+KBNODE find_prev_kbnode( KBNODE root, KBNODE node, int pkttype );
+KBNODE find_next_kbnode( KBNODE node, int pkttype );
+KBNODE find_kbnode( KBNODE node, int pkttype );
+KBNODE walk_kbnode( KBNODE root, KBNODE *context, int all );
+void clear_kbnode_flags( KBNODE n );
+int commit_kbnode( KBNODE *root );
+void dump_kbnode( KBNODE node );
+
+#endif /*G10_KEYDB_H*/
diff --git a/g10/keyedit.c b/g10/keyedit.c
new file mode 100644
index 0000000..c1e0ec3
--- /dev/null
+++ b/g10/keyedit.c
@@ -0,0 +1,5094 @@
+/* keyedit.c - keyedit stuff
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <ctype.h>
+#ifdef HAVE_LIBREADLINE
+#include <stdio.h>
+#include <readline/readline.h>
+#endif
+#include "options.h"
+#include "packet.h"
+#include "errors.h"
+#include "iobuf.h"
+#include "keydb.h"
+#include "memory.h"
+#include "photoid.h"
+#include "util.h"
+#include "main.h"
+#include "trustdb.h"
+#include "filter.h"
+#include "ttyio.h"
+#include "status.h"
+#include "i18n.h"
+#include "keyserver-internal.h"
+
+static void show_prefs( PKT_user_id *uid, PKT_signature *selfsig, int verbose);
+static void show_names(KBNODE keyblock,PKT_public_key *pk,
+ unsigned int flag,int with_prefs);
+static void show_key_with_all_names( KBNODE keyblock, int only_marked,
+ int with_revoker, int with_fpr, int with_subkeys, int with_prefs );
+static void show_key_and_fingerprint( KBNODE keyblock );
+static int menu_adduid( KBNODE keyblock, KBNODE sec_keyblock,
+ int photo, const char *photo_name );
+static void menu_deluid( KBNODE pub_keyblock, KBNODE sec_keyblock );
+static int menu_delsig( KBNODE pub_keyblock );
+static int menu_clean(KBNODE keyblock,int self_only);
+static void menu_delkey( KBNODE pub_keyblock, KBNODE sec_keyblock );
+static int menu_addrevoker( KBNODE pub_keyblock,
+ KBNODE sec_keyblock, int sensitive );
+static int menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock );
+static int menu_backsign(KBNODE pub_keyblock,KBNODE sec_keyblock);
+static int menu_set_primary_uid( KBNODE pub_keyblock, KBNODE sec_keyblock );
+static int menu_set_preferences( KBNODE pub_keyblock, KBNODE sec_keyblock );
+static int menu_set_keyserver_url (const char *url,
+ KBNODE pub_keyblock, KBNODE sec_keyblock );
+static int menu_set_notation(const char *string,
+ KBNODE pub_keyblock,KBNODE sec_keyblock);
+static int menu_select_uid( KBNODE keyblock, int idx );
+static int menu_select_uid_namehash( KBNODE keyblock, const char *namehash );
+static int menu_select_key( KBNODE keyblock, int idx );
+static int count_uids( KBNODE keyblock );
+static int count_uids_with_flag( KBNODE keyblock, unsigned flag );
+static int count_keys_with_flag( KBNODE keyblock, unsigned flag );
+static int count_selected_uids( KBNODE keyblock );
+static int real_uids_left( KBNODE keyblock );
+static int count_selected_keys( KBNODE keyblock );
+static int menu_revsig( KBNODE keyblock );
+static int menu_revuid( KBNODE keyblock, KBNODE sec_keyblock );
+static int menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock );
+static int menu_revsubkey( KBNODE pub_keyblock, KBNODE sec_keyblock );
+static int enable_disable_key( KBNODE keyblock, int disable );
+static void menu_showphoto( KBNODE keyblock );
+
+static int update_trust=0;
+
+#define CONTROL_D ('D' - 'A' + 1)
+
+#define NODFLG_BADSIG (1<<0) /* bad signature */
+#define NODFLG_NOKEY (1<<1) /* no public key */
+#define NODFLG_SIGERR (1<<2) /* other sig error */
+
+#define NODFLG_MARK_A (1<<4) /* temporary mark */
+#define NODFLG_DELSIG (1<<5) /* to be deleted */
+
+#define NODFLG_SELUID (1<<8) /* indicate the selected userid */
+#define NODFLG_SELKEY (1<<9) /* indicate the selected key */
+#define NODFLG_SELSIG (1<<10) /* indicate a selected signature */
+
+struct sign_attrib {
+ int non_exportable,non_revocable;
+ struct revocation_reason_info *reason;
+ byte trust_depth,trust_value;
+ char *trust_regexp;
+};
+
+
+#ifdef ENABLE_CARD_SUPPORT
+/* Given a node SEC_NODE with a secret key or subkey, locate the
+ corresponding public key from pub_keyblock. */
+static PKT_public_key *
+find_pk_from_sknode (KBNODE pub_keyblock, KBNODE sec_node)
+{
+ KBNODE node = pub_keyblock;
+ PKT_secret_key *sk;
+ PKT_public_key *pk;
+
+ if (sec_node->pkt->pkttype == PKT_SECRET_KEY
+ && node->pkt->pkttype == PKT_PUBLIC_KEY)
+ return node->pkt->pkt.public_key;
+ if (sec_node->pkt->pkttype != PKT_SECRET_SUBKEY)
+ return NULL;
+ sk = sec_node->pkt->pkt.secret_key;
+ for (; node; node = node->next)
+ if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
+ {
+ pk = node->pkt->pkt.public_key;
+ if (pk->keyid[0] == sk->keyid[0] && pk->keyid[1] == sk->keyid[1])
+ return pk;
+ }
+
+ return NULL;
+}
+#endif /* ENABLE_CARD_SUPPORT */
+
+
+/* TODO: Fix duplicated code between here and the check-sigs/list-sigs
+ code in keylist.c. */
+static int
+print_and_check_one_sig_colon( KBNODE keyblock, KBNODE node,
+ int *inv_sigs, int *no_key, int *oth_err,
+ int *is_selfsig, int print_without_key )
+{
+ PKT_signature *sig = node->pkt->pkt.signature;
+ int rc, sigrc;
+
+ /* TODO: Make sure a cached sig record here still has the pk that
+ issued it. See also keylist.c:list_keyblock_print */
+
+ switch((rc=check_key_signature(keyblock,node,is_selfsig)))
+ {
+ case 0:
+ node->flag &= ~(NODFLG_BADSIG|NODFLG_NOKEY|NODFLG_SIGERR);
+ sigrc = '!';
+ break;
+ case G10ERR_BAD_SIGN:
+ node->flag = NODFLG_BADSIG;
+ sigrc = '-';
+ if( inv_sigs )
+ ++*inv_sigs;
+ break;
+ case G10ERR_NO_PUBKEY:
+ case G10ERR_UNU_PUBKEY:
+ node->flag = NODFLG_NOKEY;
+ sigrc = '?';
+ if( no_key )
+ ++*no_key;
+ break;
+ default:
+ node->flag = NODFLG_SIGERR;
+ sigrc = '%';
+ if( oth_err )
+ ++*oth_err;
+ break;
+ }
+
+ if( sigrc != '?' || print_without_key )
+ {
+ printf("sig:%c::%d:%08lX%08lX:%lu:%lu:",
+ sigrc,sig->pubkey_algo,(ulong)sig->keyid[0],(ulong)sig->keyid[1],
+ (ulong)sig->timestamp,(ulong)sig->expiredate);
+
+ if(sig->trust_depth || sig->trust_value)
+ printf("%d %d",sig->trust_depth,sig->trust_value);
+
+ printf(":");
+
+ if(sig->trust_regexp)
+ print_string(stdout,sig->trust_regexp,strlen(sig->trust_regexp),':');
+
+ printf("::%02x%c\n",sig->sig_class,sig->flags.exportable?'x':'l');
+
+ if(opt.show_subpackets)
+ print_subpackets_colon(sig);
+ }
+
+ return (sigrc == '!');
+}
+
+
+/****************
+ * Print information about a signature, check it and return true
+ * if the signature is okay. NODE must be a signature packet.
+ */
+static int
+print_and_check_one_sig( KBNODE keyblock, KBNODE node,
+ int *inv_sigs, int *no_key, int *oth_err,
+ int *is_selfsig, int print_without_key )
+{
+ PKT_signature *sig = node->pkt->pkt.signature;
+ int rc, sigrc;
+ int is_rev = sig->sig_class == 0x30;
+
+ /* TODO: Make sure a cached sig record here still has the pk that
+ issued it. See also keylist.c:list_keyblock_print */
+
+ switch( (rc = check_key_signature( keyblock, node, is_selfsig)) ) {
+ case 0:
+ node->flag &= ~(NODFLG_BADSIG|NODFLG_NOKEY|NODFLG_SIGERR);
+ sigrc = '!';
+ break;
+ case G10ERR_BAD_SIGN:
+ node->flag = NODFLG_BADSIG;
+ sigrc = '-';
+ if( inv_sigs )
+ ++*inv_sigs;
+ break;
+ case G10ERR_NO_PUBKEY:
+ case G10ERR_UNU_PUBKEY:
+ node->flag = NODFLG_NOKEY;
+ sigrc = '?';
+ if( no_key )
+ ++*no_key;
+ break;
+ default:
+ node->flag = NODFLG_SIGERR;
+ sigrc = '%';
+ if( oth_err )
+ ++*oth_err;
+ break;
+ }
+ if( sigrc != '?' || print_without_key ) {
+ tty_printf("%s%c%c %c%c%c%c%c%c %s %s",
+ is_rev? "rev":"sig",sigrc,
+ (sig->sig_class-0x10>0 &&
+ sig->sig_class-0x10<4)?'0'+sig->sig_class-0x10:' ',
+ sig->flags.exportable?' ':'L',
+ sig->flags.revocable?' ':'R',
+ sig->flags.policy_url?'P':' ',
+ sig->flags.notation?'N':' ',
+ sig->flags.expired?'X':' ',
+ (sig->trust_depth>9)?'T':
+ (sig->trust_depth>0)?'0'+sig->trust_depth:' ',
+ keystr(sig->keyid),datestr_from_sig(sig));
+ if(opt.list_options&LIST_SHOW_SIG_EXPIRE)
+ tty_printf(" %s",expirestr_from_sig(sig));
+ tty_printf(" ");
+ if( sigrc == '%' )
+ tty_printf("[%s] ", g10_errstr(rc) );
+ else if( sigrc == '?' )
+ ;
+ else if( *is_selfsig ) {
+ tty_printf( is_rev? _("[revocation]")
+ : _("[self-signature]") );
+ }
+ else
+ {
+ size_t n;
+ char *p = get_user_id( sig->keyid, &n );
+ tty_print_utf8_string2(p, n, opt.screen_columns-keystrlen()-26-
+ ((opt.list_options&LIST_SHOW_SIG_EXPIRE)?11:0));
+ xfree(p);
+ }
+ tty_printf("\n");
+
+ if(sig->flags.policy_url && (opt.list_options&LIST_SHOW_POLICY_URLS))
+ show_policy_url(sig,3,0);
+
+ if(sig->flags.notation && (opt.list_options&LIST_SHOW_NOTATIONS))
+ show_notation(sig,3,0,
+ ((opt.list_options&LIST_SHOW_STD_NOTATIONS)?1:0)+
+ ((opt.list_options&LIST_SHOW_USER_NOTATIONS)?2:0));
+
+ if(sig->flags.pref_ks && (opt.list_options&LIST_SHOW_KEYSERVER_URLS))
+ show_keyserver_url(sig,3,0);
+ }
+
+ return (sigrc == '!');
+}
+
+
+
+/****************
+ * Check the keysigs and set the flags to indicate errors.
+ * Returns true if error found.
+ */
+static int
+check_all_keysigs( KBNODE keyblock, int only_selected )
+{
+ KBNODE kbctx;
+ KBNODE node;
+ int inv_sigs = 0;
+ int no_key = 0;
+ int oth_err = 0;
+ int has_selfsig = 0;
+ int mis_selfsig = 0;
+ int selected = !only_selected;
+ int anyuid = 0;
+
+ for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
+ if( node->pkt->pkttype == PKT_USER_ID ) {
+ PKT_user_id *uid = node->pkt->pkt.user_id;
+
+ if( only_selected )
+ selected = (node->flag & NODFLG_SELUID);
+ if( selected ) {
+ tty_printf("uid ");
+ tty_print_utf8_string( uid->name, uid->len );
+ tty_printf("\n");
+ if( anyuid && !has_selfsig )
+ mis_selfsig++;
+ has_selfsig = 0;
+ anyuid = 1;
+ }
+ }
+ else if( selected && node->pkt->pkttype == PKT_SIGNATURE
+ && ( (node->pkt->pkt.signature->sig_class&~3) == 0x10
+ || node->pkt->pkt.signature->sig_class == 0x30 ) ) {
+ int selfsig;
+
+ if( print_and_check_one_sig( keyblock, node, &inv_sigs,
+ &no_key, &oth_err, &selfsig, 0 ) ) {
+ if( selfsig )
+ has_selfsig = 1;
+ }
+ /* Hmmm: should we update the trustdb here? */
+ }
+ }
+ if( !has_selfsig )
+ mis_selfsig++;
+ if( inv_sigs == 1 )
+ tty_printf(_("1 bad signature\n") );
+ else if( inv_sigs )
+ tty_printf(_("%d bad signatures\n"), inv_sigs );
+ if( no_key == 1 )
+ tty_printf(_("1 signature not checked due to a missing key\n") );
+ else if( no_key )
+ tty_printf(_("%d signatures not checked due to missing keys\n"), no_key );
+ if( oth_err == 1 )
+ tty_printf(_("1 signature not checked due to an error\n") );
+ else if( oth_err )
+ tty_printf(_("%d signatures not checked due to errors\n"), oth_err );
+ if( mis_selfsig == 1 )
+ tty_printf(_("1 user ID without valid self-signature detected\n"));
+ else if( mis_selfsig )
+ tty_printf(_("%d user IDs without valid self-signatures detected\n"),
+ mis_selfsig);
+
+ return inv_sigs || no_key || oth_err || mis_selfsig;
+}
+
+
+static int
+sign_mk_attrib( PKT_signature *sig, void *opaque )
+{
+ struct sign_attrib *attrib = opaque;
+ byte buf[8];
+
+ if( attrib->non_exportable ) {
+ buf[0] = 0; /* not exportable */
+ build_sig_subpkt( sig, SIGSUBPKT_EXPORTABLE, buf, 1 );
+ }
+
+ if( attrib->non_revocable ) {
+ buf[0] = 0; /* not revocable */
+ build_sig_subpkt( sig, SIGSUBPKT_REVOCABLE, buf, 1 );
+ }
+
+ if( attrib->reason )
+ revocation_reason_build_cb( sig, attrib->reason );
+
+ if(attrib->trust_depth)
+ {
+ /* Not critical. If someone doesn't understand trust sigs,
+ this can still be a valid regular signature. */
+ buf[0] = attrib->trust_depth;
+ buf[1] = attrib->trust_value;
+ build_sig_subpkt(sig,SIGSUBPKT_TRUST,buf,2);
+
+ /* Critical. If someone doesn't understands regexps, this
+ whole sig should be invalid. Note the +1 for the length -
+ regexps are null terminated. */
+ if(attrib->trust_regexp)
+ build_sig_subpkt(sig,SIGSUBPKT_FLAG_CRITICAL|SIGSUBPKT_REGEXP,
+ attrib->trust_regexp,
+ strlen(attrib->trust_regexp)+1);
+ }
+
+ return 0;
+}
+
+static void
+trustsig_prompt(byte *trust_value,byte *trust_depth,char **regexp)
+{
+ char *p;
+
+ *trust_value=0;
+ *trust_depth=0;
+ *regexp=NULL;
+
+ /* Same string as pkclist.c:do_edit_ownertrust */
+ tty_printf(_("Please decide how far you trust this user to correctly verify"
+ " other users' keys\n(by looking at passports, checking"
+ " fingerprints from different sources, etc.)\n"));
+ tty_printf("\n");
+ tty_printf (_(" %d = I trust marginally\n"), 1);
+ tty_printf (_(" %d = I trust fully\n"), 2);
+ tty_printf("\n");
+
+ while(*trust_value==0)
+ {
+ p = cpr_get("trustsig_prompt.trust_value",_("Your selection? "));
+ trim_spaces(p);
+ cpr_kill_prompt();
+ /* 60 and 120 are as per RFC2440 */
+ if(p[0]=='1' && !p[1])
+ *trust_value=60;
+ else if(p[0]=='2' && !p[1])
+ *trust_value=120;
+ xfree(p);
+ }
+
+ tty_printf("\n");
+
+ tty_printf(_(
+ "Please enter the depth of this trust signature.\n"
+ "A depth greater than 1 allows the key you are signing to make\n"
+ "trust signatures on your behalf.\n"));
+ tty_printf("\n");
+
+ while(*trust_depth==0)
+ {
+ p = cpr_get("trustsig_prompt.trust_depth",_("Your selection? "));
+ trim_spaces(p);
+ cpr_kill_prompt();
+ *trust_depth=atoi(p);
+ xfree(p);
+ }
+
+ tty_printf("\n");
+
+ tty_printf(_("Please enter a domain to restrict this signature, "
+ "or enter for none.\n"));
+
+ tty_printf("\n");
+
+ p=cpr_get("trustsig_prompt.trust_regexp",_("Your selection? "));
+ trim_spaces(p);
+ cpr_kill_prompt();
+
+ if(strlen(p)>0)
+ {
+ char *q=p;
+ int regexplen=100,ind;
+
+ *regexp=xmalloc(regexplen);
+
+ /* Now mangle the domain the user entered into a regexp. To do
+ this, \-escape everything that isn't alphanumeric, and attach
+ "<[^>]+[@.]" to the front, and ">$" to the end. */
+
+ strcpy(*regexp,"<[^>]+[@.]");
+ ind=strlen(*regexp);
+
+ while(*q)
+ {
+ if(!((*q>='A' && *q<='Z')
+ || (*q>='a' && *q<='z') || (*q>='0' && *q<='9')))
+ (*regexp)[ind++]='\\';
+
+ (*regexp)[ind++]=*q;
+
+ if((regexplen-ind)<3)
+ {
+ regexplen+=100;
+ *regexp=xrealloc(*regexp,regexplen);
+ }
+
+ q++;
+ }
+
+ (*regexp)[ind]='\0';
+ strcat(*regexp,">$");
+ }
+
+ xfree(p);
+ tty_printf("\n");
+}
+
+/****************
+ * Loop over all locusr and and sign the uids after asking.
+ * If no user id is marked, all user ids will be signed;
+ * if some user_ids are marked those will be signed.
+ */
+static int
+sign_uids( KBNODE keyblock, STRLIST locusr, int *ret_modified,
+ int local, int nonrevocable, int trust, int interactive )
+{
+ int rc = 0;
+ SK_LIST sk_list = NULL;
+ SK_LIST sk_rover = NULL;
+ PKT_secret_key *sk = NULL;
+ KBNODE node, uidnode;
+ PKT_public_key *primary_pk=NULL;
+ int select_all = !count_selected_uids(keyblock) || interactive;
+ int all_v3=1;
+
+ /* Are there any non-v3 sigs on this key already? */
+ if(PGP2)
+ for(node=keyblock;node;node=node->next)
+ if(node->pkt->pkttype==PKT_SIGNATURE &&
+ node->pkt->pkt.signature->version>3)
+ {
+ all_v3=0;
+ break;
+ }
+
+ /* build a list of all signators.
+ *
+ * We use the CERT flag to request the primary which must always
+ * be one which is capable of signing keys. I can't see a reason
+ * why to sign keys using a subkey. Implementation of USAGE_CERT
+ * is just a hack in getkey.c and does not mean that a subkey
+ * marked as certification capable will be used. */
+ rc=build_sk_list( locusr, &sk_list, 0, PUBKEY_USAGE_CERT);
+ if( rc )
+ goto leave;
+
+ /* loop over all signators */
+ for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
+ u32 sk_keyid[2],pk_keyid[2];
+ char *p,*trust_regexp=NULL;
+ int force_v4=0,class=0,selfsig=0;
+ u32 duration=0,timestamp=0;
+ byte trust_depth=0,trust_value=0;
+
+ if(local || nonrevocable || trust ||
+ opt.cert_policy_url || opt.cert_notations)
+ force_v4=1;
+
+ /* we have to use a copy of the sk, because make_keysig_packet
+ * may remove the protection from sk and if we did other
+ * changes to the secret key, we would save the unprotected
+ * version */
+ if( sk )
+ free_secret_key(sk);
+ sk = copy_secret_key( NULL, sk_rover->sk );
+ keyid_from_sk( sk, sk_keyid );
+ /* set mark A for all selected user ids */
+ for( node=keyblock; node; node = node->next ) {
+ if( select_all || (node->flag & NODFLG_SELUID) )
+ node->flag |= NODFLG_MARK_A;
+ else
+ node->flag &= ~NODFLG_MARK_A;
+ }
+ /* reset mark for uids which are already signed */
+ uidnode = NULL;
+ for( node=keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_KEY ) {
+ primary_pk=node->pkt->pkt.public_key;
+ keyid_from_pk( primary_pk, pk_keyid );
+
+ /* Is this a self-sig? */
+ if(pk_keyid[0]==sk_keyid[0] && pk_keyid[1]==sk_keyid[1])
+ {
+ selfsig=1;
+ /* Do not force a v4 sig here, otherwise it would
+ be difficult to remake a v3 selfsig. If this
+ is a v3->v4 promotion case, then we set
+ force_v4 later anyway. */
+ force_v4=0;
+ }
+ }
+ else if( node->pkt->pkttype == PKT_USER_ID )
+ {
+ uidnode = (node->flag & NODFLG_MARK_A)? node : NULL;
+ if(uidnode)
+ {
+ int yesreally=0;
+ char *user=utf8_to_native(uidnode->pkt->pkt.user_id->name,
+ uidnode->pkt->pkt.user_id->len,
+ 0);
+
+ if(uidnode->pkt->pkt.user_id->is_revoked)
+ {
+ tty_printf(_("User ID \"%s\" is revoked."),user);
+
+ if(selfsig)
+ tty_printf("\n");
+ else if(opt.expert)
+ {
+ tty_printf("\n");
+ /* No, so remove the mark and continue */
+ if(!cpr_get_answer_is_yes("sign_uid.revoke_okay",
+ _("Are you sure you "
+ "still want to sign "
+ "it? (y/N) ")))
+ {
+ uidnode->flag &= ~NODFLG_MARK_A;
+ uidnode=NULL;
+ }
+ else if(interactive)
+ yesreally=1;
+ }
+ else
+ {
+ uidnode->flag &= ~NODFLG_MARK_A;
+ uidnode=NULL;
+ tty_printf(_(" Unable to sign.\n"));
+ }
+ }
+ else if(uidnode->pkt->pkt.user_id->is_expired)
+ {
+ tty_printf(_("User ID \"%s\" is expired."),user);
+
+ if(selfsig)
+ tty_printf("\n");
+ else if(opt.expert)
+ {
+ tty_printf("\n");
+ /* No, so remove the mark and continue */
+ if(!cpr_get_answer_is_yes("sign_uid.expire_okay",
+ _("Are you sure you "
+ "still want to sign "
+ "it? (y/N) ")))
+ {
+ uidnode->flag &= ~NODFLG_MARK_A;
+ uidnode=NULL;
+ }
+ else if(interactive)
+ yesreally=1;
+ }
+ else
+ {
+ uidnode->flag &= ~NODFLG_MARK_A;
+ uidnode=NULL;
+ tty_printf(_(" Unable to sign.\n"));
+ }
+ }
+ else if(!uidnode->pkt->pkt.user_id->created && !selfsig)
+ {
+ tty_printf(_("User ID \"%s\" is not self-signed."),
+ user);
+
+ if(opt.expert)
+ {
+ tty_printf("\n");
+ /* No, so remove the mark and continue */
+ if(!cpr_get_answer_is_yes("sign_uid.nosig_okay",
+ _("Are you sure you "
+ "still want to sign "
+ "it? (y/N) ")))
+ {
+ uidnode->flag &= ~NODFLG_MARK_A;
+ uidnode=NULL;
+ }
+ else if(interactive)
+ yesreally=1;
+ }
+ else
+ {
+ uidnode->flag &= ~NODFLG_MARK_A;
+ uidnode=NULL;
+ tty_printf(_(" Unable to sign.\n"));
+ }
+ }
+
+ if(uidnode && interactive && !yesreally)
+ {
+ tty_printf(_("User ID \"%s\" is signable. "),user);
+ if(!cpr_get_answer_is_yes("sign_uid.sign_okay",
+ _("Sign it? (y/N) ")))
+ {
+ uidnode->flag &= ~NODFLG_MARK_A;
+ uidnode=NULL;
+ }
+ }
+
+ xfree(user);
+ }
+ }
+ else if( uidnode && node->pkt->pkttype == PKT_SIGNATURE
+ && (node->pkt->pkt.signature->sig_class&~3) == 0x10 ) {
+ if( sk_keyid[0] == node->pkt->pkt.signature->keyid[0]
+ && sk_keyid[1] == node->pkt->pkt.signature->keyid[1] ) {
+ char buf[50];
+ char *user=utf8_to_native(uidnode->pkt->pkt.user_id->name,
+ uidnode->pkt->pkt.user_id->len,
+ 0);
+
+ /* It's a v3 self-sig. Make it into a v4 self-sig? */
+ if(node->pkt->pkt.signature->version<4 && selfsig)
+ {
+ tty_printf(_("The self-signature on \"%s\"\n"
+ "is a PGP 2.x-style signature.\n"),user);
+
+ /* Note that the regular PGP2 warning below
+ still applies if there are no v4 sigs on
+ this key at all. */
+
+ if(opt.expert)
+ if(cpr_get_answer_is_yes("sign_uid.v4_promote_okay",
+ _("Do you want to promote "
+ "it to an OpenPGP self-"
+ "signature? (y/N) ")))
+ {
+ force_v4=1;
+ node->flag|=NODFLG_DELSIG;
+ xfree(user);
+ continue;
+ }
+ }
+
+ /* Is the current signature expired? */
+ if(node->pkt->pkt.signature->flags.expired)
+ {
+ tty_printf(_("Your current signature on \"%s\"\n"
+ "has expired.\n"),user);
+
+ if(cpr_get_answer_is_yes("sign_uid.replace_expired_okay",
+ _("Do you want to issue a "
+ "new signature to replace "
+ "the expired one? (y/N) ")))
+ {
+ /* Mark these for later deletion. We
+ don't want to delete them here, just in
+ case the replacement signature doesn't
+ happen for some reason. We only delete
+ these after the replacement is already
+ in place. */
+
+ node->flag|=NODFLG_DELSIG;
+ xfree(user);
+ continue;
+ }
+ }
+
+ if(!node->pkt->pkt.signature->flags.exportable && !local)
+ {
+ /* It's a local sig, and we want to make a
+ exportable sig. */
+ tty_printf(_("Your current signature on \"%s\"\n"
+ "is a local signature.\n"),user);
+
+ if(cpr_get_answer_is_yes("sign_uid.local_promote_okay",
+ _("Do you want to promote "
+ "it to a full exportable "
+ "signature? (y/N) ")))
+ {
+ /* Mark these for later deletion. We
+ don't want to delete them here, just in
+ case the replacement signature doesn't
+ happen for some reason. We only delete
+ these after the replacement is already
+ in place. */
+
+ node->flag|=NODFLG_DELSIG;
+ xfree(user);
+ continue;
+ }
+ }
+
+ /* Fixme: see whether there is a revocation in which
+ * case we should allow to sign it again. */
+ if (!node->pkt->pkt.signature->flags.exportable && local)
+ tty_printf(_(
+ "\"%s\" was already locally signed by key %s\n"),
+ user,keystr_from_sk(sk));
+ else
+ tty_printf(_("\"%s\" was already signed by key %s\n"),
+ user,keystr_from_sk(sk));
+
+ if(opt.expert
+ && cpr_get_answer_is_yes("sign_uid.dupe_okay",
+ _("Do you want to sign it "
+ "again anyway? (y/N) ")))
+ {
+ /* Don't delete the old sig here since this is
+ an --expert thing. */
+ xfree(user);
+ continue;
+ }
+
+ sprintf (buf, "%08lX%08lX",
+ (ulong)sk->keyid[0], (ulong)sk->keyid[1] );
+ write_status_text (STATUS_ALREADY_SIGNED, buf);
+ uidnode->flag &= ~NODFLG_MARK_A; /* remove mark */
+
+ xfree(user);
+ }
+ }
+ }
+
+ /* check whether any uids are left for signing */
+ if( !count_uids_with_flag(keyblock, NODFLG_MARK_A) )
+ {
+ tty_printf(_("Nothing to sign with key %s\n"),keystr_from_sk(sk));
+ continue;
+ }
+
+ /* Ask whether we really should sign these user id(s) */
+ tty_printf("\n");
+ show_key_with_all_names( keyblock, 1, 0, 1, 0, 0 );
+ tty_printf("\n");
+
+ if(primary_pk->expiredate && !selfsig)
+ {
+ u32 now=make_timestamp();
+
+ if(primary_pk->expiredate<=now)
+ {
+ tty_printf(_("This key has expired!"));
+
+ if(opt.expert)
+ {
+ tty_printf(" ");
+ if(!cpr_get_answer_is_yes("sign_uid.expired_okay",
+ _("Are you sure you still "
+ "want to sign it? (y/N) ")))
+ continue;
+ }
+ else
+ {
+ tty_printf(_(" Unable to sign.\n"));
+ continue;
+ }
+ }
+ else
+ {
+ tty_printf(_("This key is due to expire on %s.\n"),
+ expirestr_from_pk(primary_pk));
+
+ if(opt.ask_cert_expire)
+ {
+ char *answer=cpr_get("sign_uid.expire",
+ _("Do you want your signature to "
+ "expire at the same time? (Y/n) "));
+ if(answer_is_yes_no_default(answer,1))
+ {
+ /* This fixes the signature timestamp we're
+ going to make as now. This is so the
+ expiration date is exactly correct, and not
+ a few seconds off (due to the time it takes
+ to answer the questions, enter the
+ passphrase, etc). */
+ timestamp=now;
+ duration=primary_pk->expiredate-now;
+ force_v4=1;
+ }
+
+ cpr_kill_prompt();
+ xfree(answer);
+ }
+ }
+ }
+
+ /* Only ask for duration if we haven't already set it to match
+ the expiration of the pk */
+ if(!duration && !selfsig)
+ {
+ if(opt.ask_cert_expire)
+ duration=ask_expire_interval(1,opt.def_cert_expire);
+ else
+ duration=parse_expire_string(opt.def_cert_expire);
+ }
+
+ if(duration)
+ force_v4=1;
+
+ /* Is --pgp2 on, it's a v3 key, all the sigs on the key are
+ currently v3 and we're about to sign it with a v4 sig? If
+ so, danger! */
+ if(PGP2 && all_v3 &&
+ (sk->version>3 || force_v4) && primary_pk->version<=3)
+ {
+ tty_printf(_("You may not make an OpenPGP signature on a "
+ "PGP 2.x key while in --pgp2 mode.\n"));
+ tty_printf(_("This would make the key unusable in PGP 2.x.\n"));
+
+ if(opt.expert)
+ {
+ if(!cpr_get_answer_is_yes("sign_uid.v4_on_v3_okay",
+ _("Are you sure you still "
+ "want to sign it? (y/N) ")))
+ continue;
+
+ all_v3=0;
+ }
+ else
+ continue;
+ }
+
+ if(selfsig)
+ ;
+ else
+ {
+ if(opt.batch || !opt.ask_cert_level)
+ class=0x10+opt.def_cert_level;
+ else
+ {
+ char *answer;
+
+ tty_printf(_("How carefully have you verified the key you are "
+ "about to sign actually belongs\nto the person "
+ "named above? If you don't know what to "
+ "answer, enter \"0\".\n"));
+ tty_printf("\n");
+ tty_printf(_(" (0) I will not answer.%s\n"),
+ opt.def_cert_level==0?" (default)":"");
+ tty_printf(_(" (1) I have not checked at all.%s\n"),
+ opt.def_cert_level==1?" (default)":"");
+ tty_printf(_(" (2) I have done casual checking.%s\n"),
+ opt.def_cert_level==2?" (default)":"");
+ tty_printf(_(" (3) I have done very careful checking.%s\n"),
+ opt.def_cert_level==3?" (default)":"");
+ tty_printf("\n");
+
+ while(class==0)
+ {
+ answer = cpr_get("sign_uid.class",_("Your selection? "
+ "(enter `?' for more information): "));
+ if(answer[0]=='\0')
+ class=0x10+opt.def_cert_level; /* Default */
+ else if(ascii_strcasecmp(answer,"0")==0)
+ class=0x10; /* Generic */
+ else if(ascii_strcasecmp(answer,"1")==0)
+ class=0x11; /* Persona */
+ else if(ascii_strcasecmp(answer,"2")==0)
+ class=0x12; /* Casual */
+ else if(ascii_strcasecmp(answer,"3")==0)
+ class=0x13; /* Positive */
+ else
+ tty_printf(_("Invalid selection.\n"));
+
+ xfree(answer);
+ }
+ }
+
+ if(trust)
+ trustsig_prompt(&trust_value,&trust_depth,&trust_regexp);
+ }
+
+ p=get_user_id_native(sk_keyid);
+ tty_printf(_("Are you sure that you want to sign this key with your\n"
+ "key \"%s\" (%s)\n"),p,keystr_from_sk(sk));
+ xfree(p);
+
+ if(selfsig)
+ {
+ tty_printf("\n");
+ tty_printf(_("This will be a self-signature.\n"));
+
+ if( local )
+ {
+ tty_printf("\n");
+ tty_printf(
+ _("WARNING: the signature will not be marked "
+ "as non-exportable.\n"));
+ }
+
+ if( nonrevocable )
+ {
+ tty_printf("\n");
+ tty_printf(
+ _("WARNING: the signature will not be marked "
+ "as non-revocable.\n"));
+ }
+ }
+ else
+ {
+ if( local )
+ {
+ tty_printf("\n");
+ tty_printf(
+ _("The signature will be marked as non-exportable.\n"));
+ }
+
+ if( nonrevocable )
+ {
+ tty_printf("\n");
+ tty_printf(
+ _("The signature will be marked as non-revocable.\n"));
+ }
+
+ switch(class)
+ {
+ case 0x11:
+ tty_printf("\n");
+ tty_printf(_("I have not checked this key at all.\n"));
+ break;
+
+ case 0x12:
+ tty_printf("\n");
+ tty_printf(_("I have checked this key casually.\n"));
+ break;
+
+ case 0x13:
+ tty_printf("\n");
+ tty_printf(_("I have checked this key very carefully.\n"));
+ break;
+ }
+ }
+
+ tty_printf("\n");
+
+ if( opt.batch && opt.answer_yes )
+ ;
+ else if( !cpr_get_answer_is_yes("sign_uid.okay",
+ _("Really sign? (y/N) ")) )
+ continue;
+
+ /* now we can sign the user ids */
+ reloop: /* (must use this, because we are modifing the list) */
+ primary_pk = NULL;
+ for( node=keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_KEY )
+ primary_pk = node->pkt->pkt.public_key;
+ else if( node->pkt->pkttype == PKT_USER_ID
+ && (node->flag & NODFLG_MARK_A) ) {
+ PACKET *pkt;
+ PKT_signature *sig;
+ struct sign_attrib attrib;
+
+ assert( primary_pk );
+ memset( &attrib, 0, sizeof attrib );
+ attrib.non_exportable = local;
+ attrib.non_revocable = nonrevocable;
+ attrib.trust_depth = trust_depth;
+ attrib.trust_value = trust_value;
+ attrib.trust_regexp = trust_regexp;
+ node->flag &= ~NODFLG_MARK_A;
+
+ /* we force creation of a v4 signature for local
+ * signatures, otherwise we would not generate the
+ * subpacket with v3 keys and the signature becomes
+ * exportable */
+
+ if(selfsig)
+ rc = make_keysig_packet( &sig, primary_pk,
+ node->pkt->pkt.user_id,
+ NULL,
+ sk,
+ 0x13, 0, force_v4?4:0, 0, 0,
+ keygen_add_std_prefs, primary_pk);
+ else
+ rc = make_keysig_packet( &sig, primary_pk,
+ node->pkt->pkt.user_id,
+ NULL,
+ sk,
+ class, 0, force_v4?4:0,
+ timestamp, duration,
+ sign_mk_attrib, &attrib );
+ if( rc ) {
+ log_error(_("signing failed: %s\n"), g10_errstr(rc));
+ goto leave;
+ }
+
+ *ret_modified = 1; /* we changed the keyblock */
+ update_trust = 1;
+
+ pkt = xmalloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_SIGNATURE;
+ pkt->pkt.signature = sig;
+ insert_kbnode( node, new_kbnode(pkt), PKT_SIGNATURE );
+ goto reloop;
+ }
+ }
+
+ /* Delete any sigs that got promoted */
+ for( node=keyblock; node; node = node->next )
+ if( node->flag & NODFLG_DELSIG)
+ delete_kbnode(node);
+ } /* end loop over signators */
+
+ leave:
+ release_sk_list( sk_list );
+ if( sk )
+ free_secret_key(sk);
+ return rc;
+}
+
+
+
+/****************
+ * Change the passphrase of the primary and all secondary keys.
+ * We use only one passphrase for all keys.
+ */
+static int
+change_passphrase( KBNODE keyblock )
+{
+ int rc = 0;
+ int changed=0;
+ KBNODE node;
+ PKT_secret_key *sk;
+ char *passphrase = NULL;
+ int no_primary_secrets = 0;
+ int any;
+
+ node = find_kbnode( keyblock, PKT_SECRET_KEY );
+ if( !node ) {
+ log_error("Oops; secret key not found anymore!\n");
+ goto leave;
+ }
+ sk = node->pkt->pkt.secret_key;
+
+ for (any = 0, node=keyblock; node; node = node->next) {
+ if (node->pkt->pkttype == PKT_SECRET_KEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY) {
+ PKT_secret_key *tmpsk = node->pkt->pkt.secret_key;
+ if (!(tmpsk->is_protected
+ && (tmpsk->protect.s2k.mode == 1001
+ || tmpsk->protect.s2k.mode == 1002))) {
+ any = 1;
+ break;
+ }
+ }
+ }
+ if (!any) {
+ tty_printf (_("Key has only stub or on-card key items - "
+ "no passphrase to change.\n"));
+ goto leave;
+ }
+
+ /* See how to handle this key. */
+ switch( is_secret_key_protected( sk ) ) {
+ case -1:
+ rc = G10ERR_PUBKEY_ALGO;
+ break;
+ case 0:
+ tty_printf(_("This key is not protected.\n"));
+ break;
+ default:
+ if( sk->protect.s2k.mode == 1001 ) {
+ tty_printf(_("Secret parts of primary key are not available.\n"));
+ no_primary_secrets = 1;
+ }
+ else if( sk->protect.s2k.mode == 1002 ) {
+ tty_printf(_("Secret parts of primary key are stored on-card.\n"));
+ no_primary_secrets = 1;
+ }
+ else {
+ tty_printf(_("Key is protected.\n"));
+ rc = check_secret_key( sk, 0 );
+ if( !rc )
+ passphrase = get_last_passphrase();
+ }
+ break;
+ }
+
+ /* Unprotect all subkeys (use the supplied passphrase or ask)*/
+ for(node=keyblock; !rc && node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+ PKT_secret_key *subsk = node->pkt->pkt.secret_key;
+ if ( !(subsk->is_protected
+ && (subsk->protect.s2k.mode == 1001
+ || subsk->protect.s2k.mode == 1002))) {
+ set_next_passphrase( passphrase );
+ rc = check_secret_key( subsk, 0 );
+ if( !rc && !passphrase )
+ passphrase = get_last_passphrase();
+ }
+ }
+ }
+
+ if( rc )
+ tty_printf(_("Can't edit this key: %s\n"), g10_errstr(rc));
+ else {
+ DEK *dek = NULL;
+ STRING2KEY *s2k = xmalloc_secure( sizeof *s2k );
+ const char *errtext = NULL;
+
+ tty_printf(_("Enter the new passphrase for this secret key.\n\n") );
+
+ set_next_passphrase( NULL );
+ for(;;) {
+ s2k->mode = opt.s2k_mode;
+ s2k->hash_algo = S2K_DIGEST_ALGO;
+ dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo,
+ s2k, 2, errtext, NULL);
+ if( !dek ) {
+ errtext = N_("passphrase not correctly repeated; try again");
+ tty_printf ("%s.\n", _(errtext));
+ }
+ else if( !dek->keylen ) {
+ rc = 0;
+ tty_printf(_( "You don't want a passphrase -"
+ " this is probably a *bad* idea!\n\n"));
+ if( cpr_get_answer_is_yes("change_passwd.empty.okay",
+ _("Do you really want to do this? (y/N) ")))
+ {
+ changed++;
+ break;
+ }
+ }
+ else { /* okay */
+ rc = 0;
+ if( !no_primary_secrets ) {
+ sk->protect.algo = dek->algo;
+ sk->protect.s2k = *s2k;
+ rc = protect_secret_key( sk, dek );
+ }
+ for(node=keyblock; !rc && node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+ PKT_secret_key *subsk = node->pkt->pkt.secret_key;
+ if ( !(subsk->is_protected
+ && (subsk->protect.s2k.mode == 1001
+ || subsk->protect.s2k.mode == 1002))) {
+ subsk->protect.algo = dek->algo;
+ subsk->protect.s2k = *s2k;
+ rc = protect_secret_key( subsk, dek );
+ }
+ }
+ }
+ if( rc )
+ log_error("protect_secret_key failed: %s\n",
+ g10_errstr(rc) );
+ else
+ changed++;
+ break;
+ }
+ }
+ xfree(s2k);
+ xfree(dek);
+ }
+
+ leave:
+ xfree( passphrase );
+ set_next_passphrase( NULL );
+ return changed && !rc;
+}
+
+
+/****************
+ * There are some keys out (due to a bug in gnupg), where the sequence
+ * of the packets is wrong. This function fixes that.
+ * Returns: true if the keyblock has been fixed.
+ *
+ * Note: This function does not work if there is more than one user ID.
+ */
+static int
+fix_keyblock( KBNODE keyblock )
+{
+ KBNODE node, last, subkey;
+ int fixed=0;
+
+ /* locate key signatures of class 0x10..0x13 behind sub key packets */
+ for( subkey=last=NULL, node = keyblock; node;
+ last=node, node = node->next ) {
+ switch( node->pkt->pkttype ) {
+ case PKT_PUBLIC_SUBKEY:
+ case PKT_SECRET_SUBKEY:
+ if( !subkey )
+ subkey = last; /* actually it is the one before the subkey */
+ break;
+ case PKT_SIGNATURE:
+ if( subkey ) {
+ PKT_signature *sig = node->pkt->pkt.signature;
+ if( sig->sig_class >= 0x10 && sig->sig_class <= 0x13 ) {
+ log_info(_(
+ "moving a key signature to the correct place\n"));
+ last->next = node->next;
+ node->next = subkey->next;
+ subkey->next = node;
+ node = last;
+ fixed=1;
+ }
+ }
+ break;
+ default: break;
+ }
+ }
+
+ return fixed;
+}
+
+static int
+parse_sign_type(const char *str,int *localsig,int *nonrevokesig,int *trustsig)
+{
+ const char *p=str;
+
+ while(*p)
+ {
+ if(ascii_strncasecmp(p,"l",1)==0)
+ {
+ *localsig=1;
+ p++;
+ }
+ else if(ascii_strncasecmp(p,"nr",2)==0)
+ {
+ *nonrevokesig=1;
+ p+=2;
+ }
+ else if(ascii_strncasecmp(p,"t",1)==0)
+ {
+ *trustsig=1;
+ p++;
+ }
+ else
+ return 0;
+ }
+
+ return 1;
+}
+
+
+/****************
+ * Menu driven key editor. If seckey_check is true, then a secret key
+ * that matches username will be looked for. If it is false, not all
+ * commands will be available.
+ *
+ * Note: to keep track of some selection we use node->mark MARKBIT_xxxx.
+ */
+
+/* Need an SK for this command */
+#define KEYEDIT_NEED_SK 1
+/* Cannot be viewing the SK for this command */
+#define KEYEDIT_NOT_SK 2
+/* Must be viewing the SK for this command */
+#define KEYEDIT_ONLY_SK 4
+/* Match the tail of the string */
+#define KEYEDIT_TAIL_MATCH 8
+
+enum cmdids
+ {
+ cmdNONE = 0,
+ cmdQUIT, cmdHELP, cmdFPR, cmdLIST, cmdSELUID, cmdCHECK, cmdSIGN,
+ cmdREVSIG, cmdREVKEY, cmdREVUID, cmdDELSIG, cmdPRIMARY, cmdDEBUG,
+ cmdSAVE, cmdADDUID, cmdADDPHOTO, cmdDELUID, cmdADDKEY, cmdDELKEY,
+ cmdADDREVOKER, cmdTOGGLE, cmdSELKEY, cmdPASSWD, cmdTRUST, cmdPREF,
+ cmdEXPIRE, cmdBACKSIGN, cmdENABLEKEY, cmdDISABLEKEY, cmdSHOWPREF,
+ cmdSETPREF, cmdPREFKS, cmdNOTATION, cmdINVCMD, cmdSHOWPHOTO, cmdUPDTRUST,
+ cmdCHKTRUST, cmdADDCARDKEY, cmdKEYTOCARD, cmdBKUPTOCARD, cmdCLEAN,
+ cmdMINIMIZE, cmdNOP
+ };
+
+static struct
+{
+ const char *name;
+ enum cmdids id;
+ int flags;
+ const char *desc;
+} cmds[] =
+ {
+ { "quit" , cmdQUIT , 0, N_("quit this menu") },
+ { "q" , cmdQUIT , 0, NULL },
+ { "save" , cmdSAVE , 0, N_("save and quit") },
+ { "help" , cmdHELP , 0, N_("show this help") },
+ { "?" , cmdHELP , 0, NULL },
+ { "fpr" , cmdFPR , 0, N_("show key fingerprint") },
+ { "list" , cmdLIST , 0, N_("list key and user IDs") },
+ { "l" , cmdLIST , 0, NULL },
+ { "uid" , cmdSELUID , 0, N_("select user ID N") },
+ { "key" , cmdSELKEY , 0, N_("select subkey N") },
+ { "check" , cmdCHECK , 0, N_("check signatures") },
+ { "c" , cmdCHECK , 0, NULL },
+ { "cross-certify", cmdBACKSIGN , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL },
+ { "backsign", cmdBACKSIGN , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL },
+ { "sign" , cmdSIGN , KEYEDIT_NOT_SK|KEYEDIT_TAIL_MATCH,
+ N_("sign selected user IDs [* see below for related commands]") },
+ { "s" , cmdSIGN , KEYEDIT_NOT_SK, NULL },
+ /* "lsign" and friends will never match since "sign" comes first
+ and it is a tail match. They are just here so they show up in
+ the help menu. */
+ { "lsign" , cmdNOP , 0, N_("sign selected user IDs locally") },
+ { "tsign" , cmdNOP , 0,
+ N_("sign selected user IDs with a trust signature") },
+ { "nrsign" , cmdNOP , 0,
+ N_("sign selected user IDs with a non-revocable signature") },
+
+ { "debug" , cmdDEBUG , 0, NULL },
+ { "adduid" , cmdADDUID , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+ N_("add a user ID") },
+ { "addphoto", cmdADDPHOTO , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+ N_("add a photo ID") },
+ { "deluid" , cmdDELUID , KEYEDIT_NOT_SK,
+ N_("delete selected user IDs") },
+ /* delphoto is really deluid in disguise */
+ { "delphoto", cmdDELUID , KEYEDIT_NOT_SK, NULL },
+
+ { "addkey" , cmdADDKEY , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+ N_("add a subkey") },
+
+#ifdef ENABLE_CARD_SUPPORT
+ { "addcardkey", cmdADDCARDKEY , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+ N_("add a key to a smartcard") },
+ { "keytocard", cmdKEYTOCARD , KEYEDIT_NEED_SK|KEYEDIT_ONLY_SK,
+ N_("move a key to a smartcard")},
+ { "bkuptocard", cmdBKUPTOCARD , KEYEDIT_NEED_SK|KEYEDIT_ONLY_SK,
+ N_("move a backup key to a smartcard")},
+#endif /*ENABLE_CARD_SUPPORT*/
+
+ { "delkey" , cmdDELKEY , KEYEDIT_NOT_SK,
+ N_("delete selected subkeys") },
+ { "addrevoker",cmdADDREVOKER,KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+ N_("add a revocation key") },
+ { "delsig" , cmdDELSIG , KEYEDIT_NOT_SK,
+ N_("delete signatures from the selected user IDs") },
+ { "expire" , cmdEXPIRE , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+ N_("change the expiration date for the key or selected subkeys") },
+ { "primary" , cmdPRIMARY , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+ N_("flag the selected user ID as primary")},
+ { "toggle" , cmdTOGGLE , KEYEDIT_NEED_SK,
+ N_("toggle between the secret and public key listings") },
+ { "t" , cmdTOGGLE , KEYEDIT_NEED_SK, NULL },
+ { "pref" , cmdPREF , KEYEDIT_NOT_SK,
+ N_("list preferences (expert)")},
+ { "showpref", cmdSHOWPREF , KEYEDIT_NOT_SK,
+ N_("list preferences (verbose)") },
+ { "setpref" , cmdSETPREF , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+ N_("set preference list for the selected user IDs") },
+ /* Alias */
+ { "updpref" , cmdSETPREF , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL },
+
+ { "keyserver",cmdPREFKS , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+ N_("set the preferred keyserver URL for the selected user IDs")},
+ { "notation", cmdNOTATION , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+ N_("set a notation for the selected user IDs")},
+ { "passwd" , cmdPASSWD , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+ N_("change the passphrase") },
+ /* Alias */
+ { "password", cmdPASSWD , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL },
+
+ { "trust" , cmdTRUST , KEYEDIT_NOT_SK, N_("change the ownertrust") },
+ { "revsig" , cmdREVSIG , KEYEDIT_NOT_SK,
+ N_("revoke signatures on the selected user IDs") },
+ { "revuid" , cmdREVUID , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+ N_("revoke selected user IDs") },
+ /* Alias */
+ { "revphoto", cmdREVUID , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK, NULL },
+
+ { "revkey" , cmdREVKEY , KEYEDIT_NOT_SK|KEYEDIT_NEED_SK,
+ N_("revoke key or selected subkeys") },
+ { "enable" , cmdENABLEKEY , KEYEDIT_NOT_SK, N_("enable key") },
+ { "disable" , cmdDISABLEKEY, KEYEDIT_NOT_SK, N_("disable key") },
+ { "showphoto",cmdSHOWPHOTO , 0, N_("show selected photo IDs") },
+ { "clean", cmdCLEAN , KEYEDIT_NOT_SK,
+ 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") },
+ { NULL, cmdNONE, 0, NULL }
+ };
+
+#ifdef HAVE_LIBREADLINE
+
+/* These two functions are used by readline for command completion. */
+
+static char *
+command_generator(const char *text,int state)
+{
+ static int list_index,len;
+ const char *name;
+
+ /* If this is a new word to complete, initialize now. This includes
+ saving the length of TEXT for efficiency, and initializing the
+ index variable to 0. */
+ if(!state)
+ {
+ list_index=0;
+ len=strlen(text);
+ }
+
+ /* Return the next partial match */
+ while((name=cmds[list_index].name))
+ {
+ /* Only complete commands that have help text */
+ if(cmds[list_index++].desc && strncmp(name,text,len)==0)
+ return strdup(name);
+ }
+
+ return NULL;
+}
+
+static char **
+keyedit_completion(const char *text, int start, int end)
+{
+ /* If we are at the start of a line, we try and command-complete.
+ If not, just do nothing for now. */
+
+ if(start==0)
+ return rl_completion_matches(text,command_generator);
+
+ rl_attempted_completion_over=1;
+
+ return NULL;
+}
+#endif /* HAVE_LIBREADLINE */
+
+
+void
+keyedit_menu( const char *username, STRLIST locusr,
+ STRLIST commands, int quiet, int seckey_check )
+{
+ enum cmdids cmd = 0;
+ int rc = 0;
+ KBNODE keyblock = NULL;
+ KEYDB_HANDLE kdbhd = NULL;
+ KBNODE sec_keyblock = NULL;
+ KEYDB_HANDLE sec_kdbhd = NULL;
+ KBNODE cur_keyblock;
+ char *answer = NULL;
+ int redisplay = 1;
+ int modified = 0;
+ int sec_modified = 0;
+ int toggle;
+ int have_commands = !!commands;
+
+ if ( opt.command_fd != -1 )
+ ;
+ else if( opt.batch && !have_commands )
+ {
+ log_error(_("can't do this in batch mode\n"));
+ goto leave;
+ }
+
+#ifdef HAVE_W32_SYSTEM
+ /* Due to Windows peculiarities we need to make sure that the
+ trustdb stale check is done before we open another file
+ (i.e. by searching for a key). In theory we could make sure
+ that the files are closed after use but the open/close caches
+ inhibits that and flushing the cache right before the stale
+ check is not easy to implement. Thus we take the easy way out
+ and run the stale check as early as possible. Note, that for
+ non- W32 platforms it is run indirectly trough a call to
+ get_validity (). */
+ check_trustdb_stale ();
+#endif
+
+ /* Get the public key */
+ rc = get_pubkey_byname (NULL, username, &keyblock, &kdbhd, 1);
+ if( rc )
+ goto leave;
+ if( fix_keyblock( keyblock ) )
+ modified++;
+ if( collapse_uids( &keyblock ) )
+ modified++;
+ reorder_keyblock(keyblock);
+
+ if(seckey_check)
+ {/* see whether we have a matching secret key */
+ PKT_public_key *pk = keyblock->pkt->pkt.public_key;
+
+ sec_kdbhd = keydb_new (1);
+ {
+ byte afp[MAX_FINGERPRINT_LEN];
+ size_t an;
+
+ fingerprint_from_pk (pk, afp, &an);
+ while (an < MAX_FINGERPRINT_LEN)
+ afp[an++] = 0;
+ rc = keydb_search_fpr (sec_kdbhd, afp);
+ }
+ if (!rc)
+ {
+ rc = keydb_get_keyblock (sec_kdbhd, &sec_keyblock);
+ if (rc)
+ {
+ log_error (_("error reading secret keyblock \"%s\": %s\n"),
+ username, g10_errstr(rc));
+ }
+ else
+ {
+ merge_keys_and_selfsig( sec_keyblock );
+ if( fix_keyblock( sec_keyblock ) )
+ sec_modified++;
+ }
+ }
+
+ if (rc) {
+ sec_keyblock = NULL;
+ keydb_release (sec_kdbhd); sec_kdbhd = NULL;
+ rc = 0;
+ }
+
+ if( sec_keyblock && !quiet )
+ tty_printf(_("Secret key is available.\n"));
+ }
+
+ toggle = 0;
+ cur_keyblock = keyblock;
+ for(;;) { /* main loop */
+ int i, arg_number, photo;
+ const char *arg_string = "";
+ char *p;
+ PKT_public_key *pk=keyblock->pkt->pkt.public_key;
+
+ tty_printf("\n");
+
+ if( redisplay && !quiet )
+ {
+ show_key_with_all_names( cur_keyblock, 0, 1, 0, 1, 0 );
+ tty_printf("\n");
+ redisplay = 0;
+ }
+ do {
+ xfree(answer);
+ if( have_commands ) {
+ if( commands ) {
+ answer = xstrdup( commands->d );
+ commands = commands->next;
+ }
+ else if( opt.batch ) {
+ answer = xstrdup("quit");
+ }
+ else
+ have_commands = 0;
+ }
+ if( !have_commands )
+ {
+ tty_enable_completion(keyedit_completion);
+ answer = cpr_get_no_help("keyedit.prompt", _("Command> "));
+ cpr_kill_prompt();
+ tty_disable_completion();
+ }
+ trim_spaces(answer);
+ } while( *answer == '#' );
+
+ arg_number = 0; /* Yes, here is the init which egcc complains about */
+ photo = 0; /* This too */
+ if( !*answer )
+ cmd = cmdLIST;
+ else if( *answer == CONTROL_D )
+ cmd = cmdQUIT;
+ else if( digitp(answer ) ) {
+ cmd = cmdSELUID;
+ arg_number = atoi(answer);
+ }
+ else {
+ if( (p=strchr(answer,' ')) ) {
+ *p++ = 0;
+ trim_spaces(answer);
+ trim_spaces(p);
+ arg_number = atoi(p);
+ arg_string = p;
+ }
+
+ for(i=0; cmds[i].name; i++ )
+ {
+ if(cmds[i].flags & KEYEDIT_TAIL_MATCH)
+ {
+ size_t l=strlen(cmds[i].name);
+ size_t a=strlen(answer);
+ if(a>=l)
+ {
+ if(ascii_strcasecmp(&answer[a-l],cmds[i].name)==0)
+ {
+ answer[a-l]='\0';
+ break;
+ }
+ }
+ }
+ else if( !ascii_strcasecmp( answer, cmds[i].name ) )
+ break;
+ }
+ if((cmds[i].flags & KEYEDIT_NEED_SK) && !sec_keyblock )
+ {
+ tty_printf(_("Need the secret key to do this.\n"));
+ cmd = cmdNOP;
+ }
+ else if(((cmds[i].flags & KEYEDIT_NOT_SK) && sec_keyblock
+ && toggle)
+ ||((cmds[i].flags & KEYEDIT_ONLY_SK) && sec_keyblock
+ && !toggle))
+ {
+ tty_printf(_("Please use the command \"toggle\" first.\n"));
+ cmd = cmdNOP;
+ }
+ else
+ cmd = cmds[i].id;
+ }
+ switch( cmd )
+ {
+ case cmdHELP:
+ for(i=0; cmds[i].name; i++ )
+ {
+ if((cmds[i].flags & KEYEDIT_NEED_SK) && !sec_keyblock )
+ ; /* skip if we do not have the secret key */
+ else if( cmds[i].desc )
+ tty_printf("%-11s %s\n", cmds[i].name, _(cmds[i].desc) );
+ }
+
+ tty_printf("\n");
+ tty_printf(_(
+"* The `sign' command may be prefixed with an `l' for local "
+"signatures (lsign),\n"
+" a `t' for trust signatures (tsign), an `nr' for non-revocable signatures\n"
+" (nrsign), or any combination thereof (ltsign, tnrsign, etc.).\n"));
+
+ break;
+
+ case cmdLIST:
+ redisplay = 1;
+ break;
+
+ case cmdFPR:
+ show_key_and_fingerprint( keyblock );
+ break;
+
+ case cmdSELUID:
+ if(strlen(arg_string)==NAMEHASH_LEN*2)
+ redisplay=menu_select_uid_namehash(cur_keyblock,arg_string);
+ else
+ redisplay=menu_select_uid(cur_keyblock,arg_number);
+ break;
+
+ case cmdSELKEY:
+ if( menu_select_key( cur_keyblock, arg_number ) )
+ redisplay = 1;
+ break;
+
+ case cmdCHECK:
+ /* we can only do this with the public key becuase the
+ * check functions can't cope with secret keys and it
+ * is questionable whether this would make sense at all */
+ check_all_keysigs( keyblock, count_selected_uids(keyblock) );
+ break;
+
+ case cmdSIGN: /* sign (only the public key) */
+ {
+ int localsig=0,nonrevokesig=0,trustsig=0,interactive=0;
+
+ if( pk->is_revoked )
+ {
+ tty_printf(_("Key is revoked."));
+
+ if(opt.expert)
+ {
+ tty_printf(" ");
+ if(!cpr_get_answer_is_yes("keyedit.sign_revoked.okay",
+ _("Are you sure you still want"
+ " to sign it? (y/N) ")))
+ break;
+ }
+ else
+ {
+ tty_printf(_(" Unable to sign.\n"));
+ break;
+ }
+ }
+
+ if(count_uids(keyblock) > 1 && !count_selected_uids(keyblock)
+ && !cpr_get_answer_is_yes("keyedit.sign_all.okay",
+ _("Really sign all user IDs?"
+ " (y/N) ")))
+ {
+ if(opt.interactive)
+ interactive=1;
+ else
+ {
+ tty_printf(_("Hint: Select the user IDs to sign\n"));
+ have_commands = 0;
+ break;
+ }
+
+ }
+ /* What sort of signing are we doing? */
+ if(!parse_sign_type(answer,&localsig,&nonrevokesig,&trustsig))
+ {
+ tty_printf(_("Unknown signature type `%s'\n"),answer);
+ break;
+ }
+
+ sign_uids(keyblock, locusr, &modified,
+ localsig, nonrevokesig, trustsig, interactive);
+ }
+ break;
+
+ case cmdDEBUG:
+ dump_kbnode( cur_keyblock );
+ break;
+
+ case cmdTOGGLE:
+ toggle = !toggle;
+ cur_keyblock = toggle? sec_keyblock : keyblock;
+ redisplay = 1;
+ break;
+
+ case cmdADDPHOTO:
+ if (RFC2440 || RFC1991 || PGP2)
+ {
+ tty_printf(
+ _("This command is not allowed while in %s mode.\n"),
+ RFC2440?"OpenPGP":PGP2?"PGP2":"RFC-1991");
+ break;
+ }
+ photo=1;
+ /* fall through */
+
+ case cmdADDUID:
+ if( menu_adduid( keyblock, sec_keyblock, photo, arg_string ) )
+ {
+ update_trust = 1;
+ redisplay = 1;
+ sec_modified = modified = 1;
+ merge_keys_and_selfsig( sec_keyblock );
+ merge_keys_and_selfsig( keyblock );
+ }
+ break;
+
+ case cmdDELUID: {
+ int n1;
+
+ if( !(n1=count_selected_uids(keyblock)) )
+ tty_printf(_("You must select at least one user ID.\n"));
+ else if( real_uids_left(keyblock) < 1 )
+ tty_printf(_("You can't delete the last user ID!\n"));
+ else if( cpr_get_answer_is_yes("keyedit.remove.uid.okay",
+ n1 > 1? _("Really remove all selected user IDs? (y/N) ")
+ : _("Really remove this user ID? (y/N) ")
+ ) ) {
+ menu_deluid( keyblock, sec_keyblock );
+ redisplay = 1;
+ modified = 1;
+ if( sec_keyblock )
+ sec_modified = 1;
+ }
+ }
+ break;
+
+ case cmdDELSIG: {
+ int n1;
+
+ if( !(n1=count_selected_uids(keyblock)) )
+ tty_printf(_("You must select at least one user ID.\n"));
+ else if( menu_delsig( keyblock ) ) {
+ /* no redisplay here, because it may scroll away some
+ * status output of delsig */
+ modified = 1;
+ }
+ }
+ break;
+
+ case cmdADDKEY:
+ if( generate_subkeypair( keyblock, sec_keyblock ) ) {
+ redisplay = 1;
+ sec_modified = modified = 1;
+ merge_keys_and_selfsig( sec_keyblock );
+ merge_keys_and_selfsig( keyblock );
+ }
+ break;
+
+#ifdef ENABLE_CARD_SUPPORT
+ case cmdADDCARDKEY:
+ if (card_generate_subkey (keyblock, sec_keyblock)) {
+ redisplay = 1;
+ sec_modified = modified = 1;
+ merge_keys_and_selfsig( sec_keyblock );
+ merge_keys_and_selfsig( keyblock );
+ }
+ break;
+
+ case cmdKEYTOCARD:
+ {
+ KBNODE node=NULL;
+ switch ( count_selected_keys (sec_keyblock) )
+ {
+ case 0:
+ if (cpr_get_answer_is_yes("keyedit.keytocard.use_primary",
+ _("Really move the primary key? (y/N) ")))
+ node = sec_keyblock;
+ break;
+ case 1:
+ for (node = sec_keyblock; node; node = node->next )
+ {
+ if (node->pkt->pkttype == PKT_SECRET_SUBKEY
+ && node->flag & NODFLG_SELKEY)
+ break;
+ }
+ break;
+ default:
+ tty_printf(_("You must select exactly one key.\n"));
+ break;
+ }
+ if (node)
+ {
+ PKT_public_key *xxpk = find_pk_from_sknode (keyblock, node);
+ if (card_store_subkey (node, xxpk?xxpk->pubkey_usage:0))
+ {
+ redisplay = 1;
+ sec_modified = 1;
+ }
+ }
+ }
+ break;
+
+ case cmdBKUPTOCARD:
+ {
+ /* Ask for a filename, check whether this is really a
+ backup key as generated by the card generation, parse
+ that key and store it on card. */
+ KBNODE node;
+ const char *fname;
+ PACKET *pkt;
+ IOBUF a;
+
+ fname = arg_string;
+ if (!*fname)
+ {
+ tty_printf (_("Command expects a filename argument\n"));
+ break;
+ }
+
+ /* Open that file. */
+ a = iobuf_open (fname);
+ if (a && is_secured_file (iobuf_get_fd (a)))
+ {
+ iobuf_close (a);
+ a = NULL;
+ errno = EPERM;
+ }
+ if (!a)
+ {
+ tty_printf (_("Can't open `%s': %s\n"),
+ fname, strerror(errno));
+ break;
+ }
+
+ /* Parse and check that file. */
+ pkt = xmalloc (sizeof *pkt);
+ init_packet (pkt);
+ rc = parse_packet (a, pkt);
+ iobuf_close (a);
+ iobuf_ioctl (NULL, 2, 0, (char*)fname); /* (invalidate cache). */
+ if (!rc
+ && pkt->pkttype != PKT_SECRET_KEY
+ && pkt->pkttype != PKT_SECRET_SUBKEY)
+ rc = G10ERR_NO_SECKEY;
+ if (rc)
+ {
+ tty_printf(_("Error reading backup key from `%s': %s\n"),
+ fname, g10_errstr (rc));
+ free_packet (pkt);
+ xfree (pkt);
+ break;
+ }
+ node = new_kbnode (pkt);
+
+ /* Store it. */
+ if (card_store_subkey (node, 0))
+ {
+ redisplay = 1;
+ sec_modified = 1;
+ }
+ release_kbnode (node);
+ }
+ break;
+
+#endif /* ENABLE_CARD_SUPPORT */
+
+ case cmdDELKEY: {
+ int n1;
+
+ if( !(n1=count_selected_keys( keyblock )) )
+ tty_printf(_("You must select at least one key.\n"));
+ else if( !cpr_get_answer_is_yes( "keyedit.remove.subkey.okay",
+ n1 > 1?
+ _("Do you really want to delete the selected keys? (y/N) "):
+ _("Do you really want to delete this key? (y/N) ")
+ ))
+ ;
+ else {
+ menu_delkey( keyblock, sec_keyblock );
+ redisplay = 1;
+ modified = 1;
+ if( sec_keyblock )
+ sec_modified = 1;
+ }
+ }
+ break;
+
+ case cmdADDREVOKER:
+ {
+ int sensitive=0;
+
+ if(ascii_strcasecmp(arg_string,"sensitive")==0)
+ sensitive=1;
+ if( menu_addrevoker( keyblock, sec_keyblock, sensitive ) ) {
+ redisplay = 1;
+ sec_modified = modified = 1;
+ merge_keys_and_selfsig( sec_keyblock );
+ merge_keys_and_selfsig( keyblock );
+ }
+ }
+ break;
+
+ case cmdREVUID: {
+ int n1;
+
+ if( !(n1=count_selected_uids(keyblock)) )
+ tty_printf(_("You must select at least one user ID.\n"));
+ else if( cpr_get_answer_is_yes(
+ "keyedit.revoke.uid.okay",
+ n1 > 1? _("Really revoke all selected user IDs? (y/N) ")
+ : _("Really revoke this user ID? (y/N) ")
+ ) ) {
+ if(menu_revuid(keyblock,sec_keyblock))
+ {
+ modified=1;
+ redisplay=1;
+ }
+ }
+ }
+ break;
+
+ case cmdREVKEY:
+ {
+ int n1;
+
+ if( !(n1=count_selected_keys( keyblock )) )
+ {
+ if(cpr_get_answer_is_yes("keyedit.revoke.subkey.okay",
+ _("Do you really want to revoke"
+ " the entire key? (y/N) ")))
+ {
+ if(menu_revkey(keyblock,sec_keyblock))
+ modified=1;
+
+ redisplay=1;
+ }
+ }
+ else if(cpr_get_answer_is_yes("keyedit.revoke.subkey.okay",
+ n1 > 1?
+ _("Do you really want to revoke"
+ " the selected subkeys? (y/N) "):
+ _("Do you really want to revoke"
+ " this subkey? (y/N) ")))
+ {
+ if( menu_revsubkey( keyblock, sec_keyblock ) )
+ modified = 1;
+
+ redisplay = 1;
+ }
+
+ if(modified)
+ merge_keys_and_selfsig( keyblock );
+ }
+ break;
+
+ case cmdEXPIRE:
+ if( menu_expire( keyblock, sec_keyblock ) )
+ {
+ merge_keys_and_selfsig( sec_keyblock );
+ merge_keys_and_selfsig( keyblock );
+ sec_modified = 1;
+ modified = 1;
+ redisplay = 1;
+ }
+ break;
+
+ case cmdBACKSIGN:
+ if(menu_backsign(keyblock,sec_keyblock))
+ {
+ sec_modified = 1;
+ modified = 1;
+ redisplay = 1;
+ }
+ break;
+
+ case cmdPRIMARY:
+ if( menu_set_primary_uid ( keyblock, sec_keyblock ) ) {
+ merge_keys_and_selfsig( keyblock );
+ modified = 1;
+ redisplay = 1;
+ }
+ break;
+
+ case cmdPASSWD:
+ if( change_passphrase( sec_keyblock ) )
+ sec_modified = 1;
+ break;
+
+ case cmdTRUST:
+ if(opt.trust_model==TM_EXTERNAL)
+ {
+ tty_printf(_("Owner trust may not be set while "
+ "using an user provided trust database\n"));
+ break;
+ }
+
+ show_key_with_all_names( keyblock, 0, 0, 0, 1, 0 );
+ tty_printf("\n");
+ if( edit_ownertrust( find_kbnode( keyblock,
+ PKT_PUBLIC_KEY )->pkt->pkt.public_key, 1 ) ) {
+ redisplay = 1;
+ /* No real need to set update_trust here as
+ edit_ownertrust() calls revalidation_mark()
+ anyway. */
+ update_trust=1;
+ }
+ break;
+
+ case cmdPREF:
+ {
+ int count=count_selected_uids(keyblock);
+ assert(keyblock->pkt->pkttype==PKT_PUBLIC_KEY);
+ show_names(keyblock,keyblock->pkt->pkt.public_key,
+ count?NODFLG_SELUID:0,1);
+ }
+ break;
+
+ case cmdSHOWPREF:
+ {
+ int count=count_selected_uids(keyblock);
+ assert(keyblock->pkt->pkttype==PKT_PUBLIC_KEY);
+ show_names(keyblock,keyblock->pkt->pkt.public_key,
+ count?NODFLG_SELUID:0,2);
+ }
+ break;
+
+ case cmdSETPREF:
+ {
+ PKT_user_id *tempuid;
+
+ keygen_set_std_prefs(!*arg_string?"default" : arg_string, 0);
+
+ tempuid=keygen_get_std_prefs();
+ tty_printf(_("Set preference list to:\n"));
+ show_prefs(tempuid,NULL,1);
+ free_user_id(tempuid);
+
+ if(cpr_get_answer_is_yes("keyedit.setpref.okay",
+ count_selected_uids (keyblock)?
+ _("Really update the preferences"
+ " for the selected user IDs? (y/N) "):
+ _("Really update the preferences? (y/N) ")))
+ {
+ if ( menu_set_preferences (keyblock, sec_keyblock) )
+ {
+ merge_keys_and_selfsig (keyblock);
+ modified = 1;
+ redisplay = 1;
+ }
+ }
+ }
+ break;
+
+ case cmdPREFKS:
+ if( menu_set_keyserver_url ( *arg_string?arg_string:NULL,
+ keyblock, sec_keyblock ) )
+ {
+ merge_keys_and_selfsig( keyblock );
+ modified = 1;
+ redisplay = 1;
+ }
+ break;
+
+ case cmdNOTATION:
+ if( menu_set_notation ( *arg_string?arg_string:NULL,
+ keyblock, sec_keyblock ) )
+ {
+ merge_keys_and_selfsig( keyblock );
+ modified = 1;
+ redisplay = 1;
+ }
+ break;
+
+ case cmdNOP:
+ break;
+
+ case cmdREVSIG:
+ if( menu_revsig( keyblock ) ) {
+ redisplay = 1;
+ modified = 1;
+ }
+ break;
+
+ case cmdENABLEKEY:
+ case cmdDISABLEKEY:
+ if( enable_disable_key( keyblock, cmd == cmdDISABLEKEY ) ) {
+ redisplay = 1;
+ modified = 1;
+ }
+ break;
+
+ case cmdSHOWPHOTO:
+ menu_showphoto(keyblock);
+ break;
+
+ case cmdCLEAN:
+ redisplay=modified=menu_clean(keyblock,0);
+ break;
+
+ case cmdMINIMIZE:
+ redisplay=modified=menu_clean(keyblock,1);
+ break;
+
+ case cmdQUIT:
+ if( have_commands )
+ goto leave;
+ if( !modified && !sec_modified )
+ goto leave;
+ if( !cpr_get_answer_is_yes("keyedit.save.okay",
+ _("Save changes? (y/N) ")) ) {
+ if( cpr_enabled()
+ || cpr_get_answer_is_yes("keyedit.cancel.okay",
+ _("Quit without saving? (y/N) ")))
+ goto leave;
+ break;
+ }
+ /* fall thru */
+ case cmdSAVE:
+ if( modified || sec_modified ) {
+ if( modified ) {
+ rc = keydb_update_keyblock (kdbhd, keyblock);
+ if( rc ) {
+ log_error(_("update failed: %s\n"), g10_errstr(rc) );
+ break;
+ }
+ }
+ if( sec_modified ) {
+ rc = keydb_update_keyblock (sec_kdbhd, sec_keyblock );
+ if( rc ) {
+ log_error( _("update secret failed: %s\n"),
+ g10_errstr(rc) );
+ break;
+ }
+ }
+ }
+ else
+ tty_printf(_("Key not changed so no update needed.\n"));
+
+ if( update_trust )
+ {
+ revalidation_mark ();
+ update_trust=0;
+ }
+ goto leave;
+
+ case cmdINVCMD:
+ default:
+ tty_printf("\n");
+ tty_printf(_("Invalid command (try \"help\")\n"));
+ break;
+ }
+ } /* end main loop */
+
+ leave:
+ release_kbnode( keyblock );
+ release_kbnode( sec_keyblock );
+ keydb_release (kdbhd);
+ xfree(answer);
+}
+
+static void
+tty_print_notations(int indent,PKT_signature *sig)
+{
+ int first=1;
+ struct notation *notation,*nd;
+
+ if(indent<0)
+ {
+ first=0;
+ indent=-indent;
+ }
+
+ notation=sig_to_notation(sig);
+
+ for(nd=notation;nd;nd=nd->next)
+ {
+ if(!first)
+ tty_printf("%*s",indent,"");
+ else
+ first=0;
+
+ tty_print_utf8_string(nd->name,strlen(nd->name));
+ tty_printf("=");
+ tty_print_utf8_string(nd->value,strlen(nd->value));
+ tty_printf("\n");
+ }
+
+ free_notation(notation);
+}
+
+/****************
+ * show preferences of a public keyblock.
+ */
+static void
+show_prefs (PKT_user_id *uid, PKT_signature *selfsig, int verbose)
+{
+ const prefitem_t fake={0,0};
+ const prefitem_t *prefs;
+ int i;
+
+ if( !uid )
+ return;
+
+ if( uid->prefs )
+ prefs=uid->prefs;
+ else if(verbose)
+ prefs=&fake;
+ else
+ return;
+
+ if (verbose) {
+ int any, des_seen=0, sha1_seen=0, uncomp_seen=0;
+
+ tty_printf (" ");
+ tty_printf (_("Cipher: "));
+ for(i=any=0; prefs[i].type; i++ ) {
+ if( prefs[i].type == PREFTYPE_SYM ) {
+ const char *s = cipher_algo_to_string (prefs[i].value);
+
+ if (any)
+ tty_printf (", ");
+ any = 1;
+ /* We don't want to display strings for experimental algos */
+ if (s && prefs[i].value < 100 )
+ tty_printf ("%s", s );
+ else
+ tty_printf ("[%d]", prefs[i].value);
+ if (prefs[i].value == CIPHER_ALGO_3DES )
+ des_seen = 1;
+ }
+ }
+ if (!des_seen) {
+ if (any)
+ tty_printf (", ");
+ tty_printf ("%s",cipher_algo_to_string(CIPHER_ALGO_3DES));
+ }
+ tty_printf ("\n ");
+ tty_printf (_("Digest: "));
+ for(i=any=0; prefs[i].type; i++ ) {
+ if( prefs[i].type == PREFTYPE_HASH ) {
+ const char *s = digest_algo_to_string (prefs[i].value);
+
+ if (any)
+ tty_printf (", ");
+ any = 1;
+ /* We don't want to display strings for experimental algos */
+ if (s && prefs[i].value < 100 )
+ tty_printf ("%s", s );
+ else
+ tty_printf ("[%d]", prefs[i].value);
+ if (prefs[i].value == DIGEST_ALGO_SHA1 )
+ sha1_seen = 1;
+ }
+ }
+ if (!sha1_seen) {
+ if (any)
+ tty_printf (", ");
+ tty_printf ("%s",digest_algo_to_string(DIGEST_ALGO_SHA1));
+ }
+ tty_printf ("\n ");
+ tty_printf (_("Compression: "));
+ for(i=any=0; prefs[i].type; i++ ) {
+ if( prefs[i].type == PREFTYPE_ZIP ) {
+ const char *s=compress_algo_to_string(prefs[i].value);
+
+ if (any)
+ tty_printf (", ");
+ any = 1;
+ /* We don't want to display strings for experimental algos */
+ if (s && prefs[i].value < 100 )
+ tty_printf ("%s", s );
+ else
+ tty_printf ("[%d]", prefs[i].value);
+ if (prefs[i].value == COMPRESS_ALGO_NONE )
+ uncomp_seen = 1;
+ }
+ }
+ if (!uncomp_seen) {
+ if (any)
+ tty_printf (", ");
+ else {
+ tty_printf ("%s",compress_algo_to_string(COMPRESS_ALGO_ZIP));
+ tty_printf (", ");
+ }
+ tty_printf ("%s",compress_algo_to_string(COMPRESS_ALGO_NONE));
+ }
+ if(uid->flags.mdc || !uid->flags.ks_modify)
+ {
+ tty_printf ("\n ");
+ tty_printf (_("Features: "));
+ any=0;
+ if(uid->flags.mdc)
+ {
+ tty_printf ("MDC");
+ any=1;
+ }
+ if(!uid->flags.ks_modify)
+ {
+ if(any)
+ tty_printf (", ");
+ tty_printf (_("Keyserver no-modify"));
+ }
+ }
+ tty_printf("\n");
+
+ if(selfsig)
+ {
+ const byte *pref_ks;
+ size_t pref_ks_len;
+
+ pref_ks=parse_sig_subpkt(selfsig->hashed,
+ SIGSUBPKT_PREF_KS,&pref_ks_len);
+ if(pref_ks && pref_ks_len)
+ {
+ tty_printf (" ");
+ tty_printf(_("Preferred keyserver: "));
+ tty_print_utf8_string(pref_ks,pref_ks_len);
+ tty_printf("\n");
+ }
+
+ if(selfsig->flags.notation)
+ {
+ tty_printf (" ");
+ tty_printf(_("Notations: "));
+ tty_print_notations(5+strlen(_("Notations: ")),selfsig);
+ }
+ }
+ }
+ else {
+ tty_printf(" ");
+ for(i=0; prefs[i].type; i++ ) {
+ tty_printf( " %c%d", prefs[i].type == PREFTYPE_SYM ? 'S' :
+ prefs[i].type == PREFTYPE_HASH ? 'H' :
+ prefs[i].type == PREFTYPE_ZIP ? 'Z':'?',
+ prefs[i].value);
+ }
+ if (uid->flags.mdc)
+ tty_printf (" [mdc]");
+ if (!uid->flags.ks_modify)
+ tty_printf (" [no-ks-modify]");
+ tty_printf("\n");
+ }
+}
+
+/* This is the version of show_key_with_all_names used when
+ opt.with_colons is used. It prints all available data in a easy to
+ parse format and does not translate utf8 */
+static void
+show_key_with_all_names_colon (KBNODE keyblock)
+{
+ KBNODE node;
+ int i, j, ulti_hack=0;
+ byte pk_version=0;
+ PKT_public_key *primary=NULL;
+
+ /* the keys */
+ for ( node = keyblock; node; node = node->next )
+ {
+ if (node->pkt->pkttype == PKT_PUBLIC_KEY
+ || (node->pkt->pkttype == PKT_PUBLIC_SUBKEY) )
+ {
+ PKT_public_key *pk = node->pkt->pkt.public_key;
+ u32 keyid[2];
+
+ if (node->pkt->pkttype == PKT_PUBLIC_KEY)
+ {
+ pk_version = pk->version;
+ primary=pk;
+ }
+
+ keyid_from_pk (pk, keyid);
+
+ fputs (node->pkt->pkttype == PKT_PUBLIC_KEY?"pub:":"sub:", stdout);
+ if (!pk->is_valid)
+ putchar ('i');
+ else if (pk->is_revoked)
+ putchar ('r');
+ else if (pk->has_expired)
+ putchar ('e');
+ else if (!(opt.fast_list_mode || opt.no_expensive_trust_checks ))
+ {
+ int trust = get_validity_info (pk, NULL);
+ if(trust=='u')
+ ulti_hack=1;
+ putchar (trust);
+ }
+
+ printf (":%u:%d:%08lX%08lX:%lu:%lu::",
+ nbits_from_pk (pk),
+ pk->pubkey_algo,
+ (ulong)keyid[0], (ulong)keyid[1],
+ (ulong)pk->timestamp,
+ (ulong)pk->expiredate );
+ if (node->pkt->pkttype==PKT_PUBLIC_KEY
+ && !(opt.fast_list_mode || opt.no_expensive_trust_checks ))
+ putchar(get_ownertrust_info (pk));
+ putchar(':');
+ putchar('\n');
+
+ print_fingerprint (pk, NULL, 0);
+ print_revokers(pk);
+ }
+ }
+
+ /* the user ids */
+ i = 0;
+ for (node = keyblock; node; node = node->next)
+ {
+ if ( node->pkt->pkttype == PKT_USER_ID )
+ {
+ PKT_user_id *uid = node->pkt->pkt.user_id;
+
+ ++i;
+
+ if(uid->attrib_data)
+ printf("uat:");
+ else
+ printf("uid:");
+
+ if ( uid->is_revoked )
+ printf("r::::::::");
+ else if ( uid->is_expired )
+ printf("e::::::::");
+ else if ( opt.fast_list_mode || opt.no_expensive_trust_checks )
+ printf("::::::::");
+ else
+ {
+ int uid_validity;
+
+ if( primary && !ulti_hack )
+ uid_validity = get_validity_info( primary, uid );
+ else
+ uid_validity = 'u';
+ printf("%c::::::::",uid_validity);
+ }
+
+ if(uid->attrib_data)
+ printf ("%u %lu",uid->numattribs,uid->attrib_len);
+ else
+ print_string (stdout, uid->name, uid->len, ':');
+
+ putchar (':');
+ /* signature class */
+ putchar (':');
+ /* capabilities */
+ putchar (':');
+ /* preferences */
+ if (pk_version>3 || uid->selfsigversion>3)
+ {
+ const prefitem_t *prefs = uid->prefs;
+
+ for (j=0; prefs && prefs[j].type; j++)
+ {
+ if (j)
+ putchar (' ');
+ printf ("%c%d", prefs[j].type == PREFTYPE_SYM ? 'S' :
+ prefs[j].type == PREFTYPE_HASH ? 'H' :
+ prefs[j].type == PREFTYPE_ZIP ? 'Z':'?',
+ prefs[j].value);
+ }
+ if (uid->flags.mdc)
+ printf (",mdc");
+ if (!uid->flags.ks_modify)
+ printf (",no-ks-modify");
+ }
+ putchar (':');
+ /* flags */
+ printf ("%d,", i);
+ if (uid->is_primary)
+ putchar ('p');
+ if (uid->is_revoked)
+ putchar ('r');
+ if (uid->is_expired)
+ putchar ('e');
+ if ((node->flag & NODFLG_SELUID))
+ putchar ('s');
+ if ((node->flag & NODFLG_MARK_A))
+ putchar ('m');
+ putchar (':');
+ putchar('\n');
+ }
+ }
+}
+
+static void
+show_names(KBNODE keyblock,PKT_public_key *pk,unsigned int flag,int with_prefs)
+{
+ KBNODE node;
+ int i=0;
+
+ for( node = keyblock; node; node = node->next )
+ {
+ if( node->pkt->pkttype == PKT_USER_ID
+ && !is_deleted_kbnode(node))
+ {
+ PKT_user_id *uid = node->pkt->pkt.user_id;
+ ++i;
+ if(!flag || (flag && (node->flag & flag)))
+ {
+ if(!(flag&NODFLG_MARK_A) && pk)
+ tty_printf("%s ",uid_trust_string_fixed(pk,uid));
+
+ if( flag & NODFLG_MARK_A )
+ tty_printf(" ");
+ else if( node->flag & NODFLG_SELUID )
+ tty_printf("(%d)* ", i);
+ else if( uid->is_primary )
+ tty_printf("(%d). ", i);
+ else
+ tty_printf("(%d) ", i);
+ tty_print_utf8_string( uid->name, uid->len );
+ tty_printf("\n");
+ if(with_prefs && pk)
+ {
+ if(pk->version>3 || uid->selfsigversion>3)
+ {
+ PKT_signature *selfsig=NULL;
+ KBNODE signode;
+
+ for(signode=node->next;
+ signode && signode->pkt->pkttype==PKT_SIGNATURE;
+ signode=signode->next)
+ {
+ if(signode->pkt->pkt.signature->
+ flags.chosen_selfsig)
+ {
+ selfsig=signode->pkt->pkt.signature;
+ break;
+ }
+ }
+
+ show_prefs (uid, selfsig, with_prefs == 2);
+ }
+ else
+ tty_printf(_("There are no preferences on a"
+ " PGP 2.x-style user ID.\n"));
+ }
+ }
+ }
+ }
+}
+
+/****************
+ * Display the key a the user ids, if only_marked is true, do only
+ * so for user ids with mark A flag set and dont display the index number
+ */
+static void
+show_key_with_all_names( KBNODE keyblock, int only_marked, int with_revoker,
+ int with_fpr, int with_subkeys, int with_prefs )
+{
+ KBNODE node;
+ int i;
+ int do_warn = 0;
+ byte pk_version=0;
+ PKT_public_key *primary=NULL;
+
+ if (opt.with_colons)
+ {
+ show_key_with_all_names_colon (keyblock);
+ return;
+ }
+
+ /* the keys */
+ for( node = keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_KEY
+ || (with_subkeys && node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ && !is_deleted_kbnode(node)) ) {
+ PKT_public_key *pk = node->pkt->pkt.public_key;
+ const char *otrust="err",*trust="err";
+
+ if( node->pkt->pkttype == PKT_PUBLIC_KEY ) {
+ /* do it here, so that debug messages don't clutter the
+ * output */
+ static int did_warn = 0;
+
+ trust = get_validity_string (pk, NULL);
+ otrust = get_ownertrust_string (pk);
+
+ /* Show a warning once */
+ if (!did_warn
+ && (get_validity (pk, NULL) & TRUST_FLAG_PENDING_CHECK)) {
+ did_warn = 1;
+ do_warn = 1;
+ }
+
+ pk_version=pk->version;
+ primary=pk;
+ }
+
+ if(pk->is_revoked)
+ {
+ char *user=get_user_id_string_native(pk->revoked.keyid);
+ const char *algo=pubkey_algo_to_string(pk->revoked.algo);
+ tty_printf(_("This key was revoked on %s by %s key %s\n"),
+ revokestr_from_pk(pk),algo?algo:"?",user);
+ xfree(user);
+ }
+
+ if(with_revoker)
+ {
+ if( !pk->revkey && pk->numrevkeys )
+ BUG();
+ else
+ for(i=0;i<pk->numrevkeys;i++)
+ {
+ u32 r_keyid[2];
+ char *user;
+ const char *algo=
+ pubkey_algo_to_string(pk->revkey[i].algid);
+
+ keyid_from_fingerprint(pk->revkey[i].fpr,
+ MAX_FINGERPRINT_LEN,r_keyid);
+
+ user=get_user_id_string_native(r_keyid);
+ tty_printf(_("This key may be revoked by %s key %s"),
+ algo?algo:"?",user);
+
+ if(pk->revkey[i].class&0x40)
+ {
+ tty_printf(" ");
+ tty_printf(_("(sensitive)"));
+ }
+
+ tty_printf ("\n");
+ xfree(user);
+ }
+ }
+
+ keyid_from_pk(pk,NULL);
+ tty_printf("%s%c %4u%c/%s ",
+ node->pkt->pkttype == PKT_PUBLIC_KEY? "pub":"sub",
+ (node->flag & NODFLG_SELKEY)? '*':' ',
+ nbits_from_pk( pk ),
+ pubkey_letter( pk->pubkey_algo ),
+ keystr(pk->keyid));
+
+ tty_printf(_("created: %s"),datestr_from_pk(pk));
+ tty_printf(" ");
+ if(pk->is_revoked)
+ tty_printf(_("revoked: %s"),revokestr_from_pk(pk));
+ else if(pk->has_expired)
+ tty_printf(_("expired: %s"),expirestr_from_pk(pk));
+ else
+ tty_printf(_("expires: %s"),expirestr_from_pk(pk));
+ tty_printf(" ");
+ tty_printf(_("usage: %s"),usagestr_from_pk(pk));
+ tty_printf("\n");
+
+ if( node->pkt->pkttype == PKT_PUBLIC_KEY )
+ {
+ if(opt.trust_model!=TM_ALWAYS)
+ {
+ tty_printf("%*s", (int)keystrlen()+13,"");
+ /* Ownertrust is only meaningful for the PGP or
+ classic trust models */
+ if(opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC)
+ {
+ int width=14-strlen(otrust);
+ if(width<=0)
+ width=1;
+ tty_printf(_("trust: %s"), otrust);
+ tty_printf("%*s",width,"");
+ }
+
+ tty_printf(_("validity: %s"), trust );
+ tty_printf("\n");
+ }
+ if( node->pkt->pkttype == PKT_PUBLIC_KEY
+ && (get_ownertrust (pk)&TRUST_FLAG_DISABLED))
+ {
+ tty_printf("*** ");
+ tty_printf(_("This key has been disabled"));
+ tty_printf("\n");
+ }
+ }
+
+ if( node->pkt->pkttype == PKT_PUBLIC_KEY && with_fpr )
+ {
+ print_fingerprint ( pk, NULL, 2 );
+ tty_printf("\n");
+ }
+ }
+ else if( node->pkt->pkttype == PKT_SECRET_KEY
+ || (with_subkeys && node->pkt->pkttype == PKT_SECRET_SUBKEY) )
+ {
+ PKT_secret_key *sk = node->pkt->pkt.secret_key;
+ tty_printf("%s%c %4u%c/%s ",
+ node->pkt->pkttype == PKT_SECRET_KEY? "sec":"ssb",
+ (node->flag & NODFLG_SELKEY)? '*':' ',
+ nbits_from_sk( sk ),
+ pubkey_letter( sk->pubkey_algo ),
+ keystr_from_sk(sk));
+ tty_printf(_("created: %s"),datestr_from_sk(sk));
+ tty_printf(" ");
+ tty_printf(_("expires: %s"),expirestr_from_sk(sk));
+ tty_printf("\n");
+ if (sk->is_protected && sk->protect.s2k.mode == 1002)
+ {
+ tty_printf(" ");
+ tty_printf(_("card-no: "));
+ if (sk->protect.ivlen == 16
+ && !memcmp (sk->protect.iv, "\xD2\x76\x00\x01\x24\x01", 6))
+ { /* This is an OpenPGP card. */
+ for (i=8; i < 14; i++)
+ {
+ if (i == 10)
+ tty_printf (" ");
+ tty_printf ("%02X", sk->protect.iv[i]);
+ }
+ }
+ else
+ { /* Something is wrong: Print all. */
+ for (i=0; i < sk->protect.ivlen; i++)
+ tty_printf ("%02X", sk->protect.iv[i]);
+ }
+ tty_printf ("\n");
+ }
+ }
+ }
+
+ show_names(keyblock,primary,only_marked?NODFLG_MARK_A:0,with_prefs);
+
+ if (do_warn)
+ tty_printf (_("Please note that the shown key validity"
+ " is not necessarily correct\n"
+ "unless you restart the program.\n"));
+}
+
+
+/* Display basic key information. This function is suitable to show
+ information on the key without any dependencies on the trustdb or
+ any other internal GnuPG stuff. KEYBLOCK may either be a public or
+ a secret key.*/
+void
+show_basic_key_info ( KBNODE keyblock )
+{
+ KBNODE node;
+ int i;
+
+ /* The primary key */
+ for (node = keyblock; node; node = node->next)
+ {
+ if (node->pkt->pkttype == PKT_PUBLIC_KEY)
+ {
+ PKT_public_key *pk = node->pkt->pkt.public_key;
+
+ /* Note, we use the same format string as in other show
+ functions to make the translation job easier. */
+ tty_printf ("%s %4u%c/%s ",
+ node->pkt->pkttype == PKT_PUBLIC_KEY? "pub":"sub",
+ nbits_from_pk( pk ),
+ pubkey_letter( pk->pubkey_algo ),
+ keystr_from_pk(pk));
+ tty_printf(_("created: %s"),datestr_from_pk(pk));
+ tty_printf(" ");
+ tty_printf(_("expires: %s"),expirestr_from_pk(pk));
+ tty_printf("\n");
+ print_fingerprint ( pk, NULL, 3 );
+ tty_printf("\n");
+ }
+ else if (node->pkt->pkttype == PKT_SECRET_KEY)
+ {
+ PKT_secret_key *sk = node->pkt->pkt.secret_key;
+ tty_printf("%s %4u%c/%s",
+ node->pkt->pkttype == PKT_SECRET_KEY? "sec":"ssb",
+ nbits_from_sk( sk ),
+ pubkey_letter( sk->pubkey_algo ),
+ keystr_from_sk(sk));
+ tty_printf(_("created: %s"),datestr_from_sk(sk));
+ tty_printf(" ");
+ tty_printf(_("expires: %s"),expirestr_from_sk(sk));
+ tty_printf("\n");
+ print_fingerprint (NULL, sk, 3 );
+ tty_printf("\n");
+ }
+ }
+
+ /* The user IDs. */
+ for (i=0, node = keyblock; node; node = node->next)
+ {
+ if (node->pkt->pkttype == PKT_USER_ID)
+ {
+ PKT_user_id *uid = node->pkt->pkt.user_id;
+ ++i;
+
+ tty_printf (" ");
+ if (uid->is_revoked)
+ tty_printf("[%s] ",_("revoked"));
+ else if ( uid->is_expired )
+ tty_printf("[%s] ",_("expired"));
+ tty_print_utf8_string (uid->name, uid->len);
+ tty_printf ("\n");
+ }
+ }
+}
+
+static void
+show_key_and_fingerprint( KBNODE keyblock )
+{
+ KBNODE node;
+ PKT_public_key *pk = NULL;
+
+ for( node = keyblock; node; node = node->next )
+ {
+ if( node->pkt->pkttype == PKT_PUBLIC_KEY )
+ {
+ pk = node->pkt->pkt.public_key;
+ tty_printf("pub %4u%c/%s %s ",
+ nbits_from_pk( pk ),
+ pubkey_letter( pk->pubkey_algo ),
+ keystr_from_pk(pk),
+ datestr_from_pk(pk) );
+ }
+ else if( node->pkt->pkttype == PKT_USER_ID )
+ {
+ PKT_user_id *uid = node->pkt->pkt.user_id;
+ tty_print_utf8_string( uid->name, uid->len );
+ break;
+ }
+ }
+ tty_printf("\n");
+ if( pk )
+ print_fingerprint( pk, NULL, 2 );
+}
+
+
+/* Show a warning if no uids on the key have the primary uid flag
+ set. */
+static void
+no_primary_warning(KBNODE keyblock)
+{
+ KBNODE node;
+ int have_primary=0,uid_count=0;
+
+ /* TODO: if we ever start behaving differently with a primary or
+ non-primary attribute ID, we will need to check for attributes
+ here as well. */
+
+ for(node=keyblock; node; node = node->next)
+ {
+ if(node->pkt->pkttype==PKT_USER_ID
+ && node->pkt->pkt.user_id->attrib_data==NULL)
+ {
+ uid_count++;
+
+ if(node->pkt->pkt.user_id->is_primary==2)
+ {
+ have_primary=1;
+ break;
+ }
+ }
+ }
+
+ if(uid_count>1 && !have_primary)
+ log_info(_("WARNING: no user ID has been marked as primary. This command"
+ " may\n cause a different user ID to become"
+ " the assumed primary.\n"));
+}
+
+/****************
+ * Ask for a new user id, do the selfsignature and put it into
+ * both keyblocks.
+ * Return true if there is a new user id
+ */
+static int
+menu_adduid( KBNODE pub_keyblock, KBNODE sec_keyblock,
+ int photo, const char *photo_name)
+{
+ PKT_user_id *uid;
+ PKT_public_key *pk=NULL;
+ PKT_secret_key *sk=NULL;
+ PKT_signature *sig=NULL;
+ PACKET *pkt;
+ KBNODE node;
+ KBNODE pub_where=NULL, sec_where=NULL;
+ int rc;
+
+ for( node = pub_keyblock; node; pub_where = node, node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_KEY )
+ pk = node->pkt->pkt.public_key;
+ else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+ break;
+ }
+ if( !node ) /* no subkey */
+ pub_where = NULL;
+ for( node = sec_keyblock; node; sec_where = node, node = node->next ) {
+ if( node->pkt->pkttype == PKT_SECRET_KEY )
+ sk = copy_secret_key( NULL, node->pkt->pkt.secret_key);
+ else if( node->pkt->pkttype == PKT_SECRET_SUBKEY )
+ break;
+ }
+ if( !node ) /* no subkey */
+ sec_where = NULL;
+ assert(pk && sk);
+
+ if(photo) {
+ int hasattrib=0;
+
+ for( node = pub_keyblock; node; node = node->next )
+ if( node->pkt->pkttype == PKT_USER_ID &&
+ node->pkt->pkt.user_id->attrib_data!=NULL)
+ {
+ hasattrib=1;
+ break;
+ }
+
+ /* It is legal but bad for compatibility to add a photo ID to a
+ v3 key as it means that PGP2 will not be able to use that key
+ anymore. Also, PGP may not expect a photo on a v3 key.
+ Don't bother to ask this if the key already has a photo - any
+ damage has already been done at that point. -dms */
+ if(pk->version==3 && !hasattrib)
+ {
+ if(opt.expert)
+ {
+ tty_printf(_("WARNING: This is a PGP2-style key. "
+ "Adding a photo ID may cause some versions\n"
+ " of PGP to reject this key.\n"));
+
+ if(!cpr_get_answer_is_yes("keyedit.v3_photo.okay",
+ _("Are you sure you still want "
+ "to add it? (y/N) ")))
+ return 0;
+ }
+ else
+ {
+ tty_printf(_("You may not add a photo ID to "
+ "a PGP2-style key.\n"));
+ return 0;
+ }
+ }
+
+ uid = generate_photo_id(pk,photo_name);
+ } else
+ uid = generate_user_id();
+ if( !uid )
+ return 0;
+
+ rc = make_keysig_packet( &sig, pk, uid, NULL, sk, 0x13, 0, 0, 0, 0,
+ keygen_add_std_prefs, pk );
+ free_secret_key( sk );
+ if( rc ) {
+ log_error("signing failed: %s\n", g10_errstr(rc) );
+ free_user_id(uid);
+ return 0;
+ }
+
+ /* insert/append to secret keyblock */
+ pkt = xmalloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_USER_ID;
+ pkt->pkt.user_id = scopy_user_id(uid);
+ node = new_kbnode(pkt);
+ if( sec_where )
+ insert_kbnode( sec_where, node, 0 );
+ else
+ add_kbnode( sec_keyblock, node );
+ pkt = xmalloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_SIGNATURE;
+ pkt->pkt.signature = copy_signature(NULL, sig);
+ if( sec_where )
+ insert_kbnode( node, new_kbnode(pkt), 0 );
+ else
+ add_kbnode( sec_keyblock, new_kbnode(pkt) );
+ /* insert/append to public keyblock */
+ pkt = xmalloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_USER_ID;
+ pkt->pkt.user_id = uid;
+ node = new_kbnode(pkt);
+ if( pub_where )
+ insert_kbnode( pub_where, node, 0 );
+ else
+ add_kbnode( pub_keyblock, node );
+ pkt = xmalloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_SIGNATURE;
+ pkt->pkt.signature = copy_signature(NULL, sig);
+ if( pub_where )
+ insert_kbnode( node, new_kbnode(pkt), 0 );
+ else
+ add_kbnode( pub_keyblock, new_kbnode(pkt) );
+ return 1;
+}
+
+
+/****************
+ * Remove all selected userids from the keyrings
+ */
+static void
+menu_deluid( KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
+ KBNODE node;
+ int selected=0;
+
+ for( node = pub_keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_USER_ID ) {
+ selected = node->flag & NODFLG_SELUID;
+ if( selected ) {
+ /* Only cause a trust update if we delete a
+ non-revoked user id */
+ if(!node->pkt->pkt.user_id->is_revoked)
+ update_trust=1;
+ delete_kbnode( node );
+ if( sec_keyblock ) {
+ KBNODE snode;
+ int s_selected = 0;
+ PKT_user_id *uid = node->pkt->pkt.user_id;
+ for( snode = sec_keyblock; snode; snode = snode->next ) {
+ if( snode->pkt->pkttype == PKT_USER_ID ) {
+ PKT_user_id *suid = snode->pkt->pkt.user_id;
+
+ s_selected =
+ (uid->len == suid->len
+ && !memcmp( uid->name, suid->name, uid->len));
+ if( s_selected )
+ delete_kbnode( snode );
+ }
+ else if( s_selected
+ && snode->pkt->pkttype == PKT_SIGNATURE )
+ delete_kbnode( snode );
+ else if( snode->pkt->pkttype == PKT_SECRET_SUBKEY )
+ s_selected = 0;
+ }
+ }
+ }
+ }
+ else if( selected && node->pkt->pkttype == PKT_SIGNATURE )
+ delete_kbnode( node );
+ else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+ selected = 0;
+ }
+ commit_kbnode( &pub_keyblock );
+ if( sec_keyblock )
+ commit_kbnode( &sec_keyblock );
+}
+
+
+static int
+menu_delsig( KBNODE pub_keyblock )
+{
+ KBNODE node;
+ PKT_user_id *uid = NULL;
+ int changed=0;
+
+ for( node = pub_keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_USER_ID ) {
+ uid = (node->flag & NODFLG_SELUID)? node->pkt->pkt.user_id : NULL;
+ }
+ else if( uid && node->pkt->pkttype == PKT_SIGNATURE ) {
+ int okay, valid, selfsig, inv_sig, no_key, other_err;
+
+ tty_printf("uid ");
+ tty_print_utf8_string( uid->name, uid->len );
+ tty_printf("\n");
+
+ okay = inv_sig = no_key = other_err = 0;
+ if(opt.with_colons)
+ valid = print_and_check_one_sig_colon( pub_keyblock, node,
+ &inv_sig, &no_key, &other_err,
+ &selfsig, 1 );
+ else
+ valid = print_and_check_one_sig( pub_keyblock, node,
+ &inv_sig, &no_key, &other_err,
+ &selfsig, 1 );
+
+ if( valid ) {
+ okay = cpr_get_answer_yes_no_quit(
+ "keyedit.delsig.valid",
+ _("Delete this good signature? (y/N/q)"));
+
+ /* Only update trust if we delete a good signature.
+ The other two cases do not affect trust. */
+ if(okay)
+ update_trust=1;
+ }
+ else if( inv_sig || other_err )
+ okay = cpr_get_answer_yes_no_quit(
+ "keyedit.delsig.invalid",
+ _("Delete this invalid signature? (y/N/q)"));
+ else if( no_key )
+ okay = cpr_get_answer_yes_no_quit(
+ "keyedit.delsig.unknown",
+ _("Delete this unknown signature? (y/N/q)"));
+
+ if( okay == -1 )
+ break;
+ if( okay && selfsig && !cpr_get_answer_is_yes(
+ "keyedit.delsig.selfsig",
+ _("Really delete this self-signature? (y/N)") ))
+ okay = 0;
+ if( okay ) {
+ delete_kbnode( node );
+ changed++;
+ }
+
+ }
+ else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+ uid = NULL;
+ }
+
+ if( changed ) {
+ commit_kbnode( &pub_keyblock );
+ tty_printf( changed == 1? _("Deleted %d signature.\n")
+ : _("Deleted %d signatures.\n"), changed );
+ }
+ else
+ tty_printf( _("Nothing deleted.\n") );
+
+ return changed;
+}
+
+static int
+menu_clean(KBNODE keyblock,int self_only)
+{
+ KBNODE uidnode;
+ int modified=0,select_all=!count_selected_uids(keyblock);
+
+ for(uidnode=keyblock->next;
+ uidnode && uidnode->pkt->pkttype!=PKT_PUBLIC_SUBKEY;
+ uidnode=uidnode->next)
+ {
+ if(uidnode->pkt->pkttype==PKT_USER_ID
+ && (uidnode->flag&NODFLG_SELUID || select_all))
+ {
+ int uids=0,sigs=0;
+ char *user=utf8_to_native(uidnode->pkt->pkt.user_id->name,
+ uidnode->pkt->pkt.user_id->len,
+ 0);
+
+ clean_one_uid(keyblock,uidnode,opt.verbose,self_only,&uids,&sigs);
+ if(uids)
+ {
+ const char *reason;
+
+ if(uidnode->pkt->pkt.user_id->is_revoked)
+ reason=_("revoked");
+ else if(uidnode->pkt->pkt.user_id->is_expired)
+ reason=_("expired");
+ else
+ reason=_("invalid");
+
+ tty_printf (_("User ID \"%s\" compacted: %s\n"), user, reason);
+
+ modified=1;
+ }
+ else if(sigs)
+ {
+ tty_printf (sigs==1?
+ _("User ID \"%s\": %d signature removed\n"):
+ _("User ID \"%s\": %d signatures removed\n"),
+ user,sigs);
+
+ modified=1;
+ }
+ else
+ {
+ tty_printf (self_only==1?
+ _("User ID \"%s\": already minimized\n"):
+ _("User ID \"%s\": already clean\n"),
+ user);
+ }
+
+ xfree(user);
+ }
+ }
+
+ return modified;
+}
+
+/****************
+ * Remove some of the secondary keys
+ */
+static void
+menu_delkey( KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
+ KBNODE node;
+ int selected=0;
+
+ for( node = pub_keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+ selected = node->flag & NODFLG_SELKEY;
+ if( selected ) {
+ delete_kbnode( node );
+ if( sec_keyblock ) {
+ KBNODE snode;
+ int s_selected = 0;
+ u32 ki[2];
+
+ keyid_from_pk( node->pkt->pkt.public_key, ki );
+ for( snode = sec_keyblock; snode; snode = snode->next ) {
+ if( snode->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+ u32 ki2[2];
+
+ keyid_from_sk( snode->pkt->pkt.secret_key, ki2 );
+ s_selected = (ki[0] == ki2[0] && ki[1] == ki2[1]);
+ if( s_selected )
+ delete_kbnode( snode );
+ }
+ else if( s_selected
+ && snode->pkt->pkttype == PKT_SIGNATURE )
+ delete_kbnode( snode );
+ else
+ s_selected = 0;
+ }
+ }
+ }
+ }
+ else if( selected && node->pkt->pkttype == PKT_SIGNATURE )
+ delete_kbnode( node );
+ else
+ selected = 0;
+ }
+ commit_kbnode( &pub_keyblock );
+ if( sec_keyblock )
+ commit_kbnode( &sec_keyblock );
+
+ /* No need to set update_trust here since signing keys are no
+ longer used to certify other keys, so there is no change in
+ trust when revoking/removing them */
+}
+
+
+/****************
+ * Ask for a new revoker, do the selfsignature and put it into
+ * both keyblocks.
+ * Return true if there is a new revoker
+ */
+static int
+menu_addrevoker( KBNODE pub_keyblock, KBNODE sec_keyblock, int sensitive )
+{
+ PKT_public_key *pk=NULL,*revoker_pk=NULL;
+ PKT_secret_key *sk=NULL;
+ PKT_signature *sig=NULL;
+ PACKET *pkt;
+ struct revocation_key revkey;
+ size_t fprlen;
+ int rc;
+
+ assert(pub_keyblock->pkt->pkttype==PKT_PUBLIC_KEY);
+ assert(sec_keyblock->pkt->pkttype==PKT_SECRET_KEY);
+
+ pk=pub_keyblock->pkt->pkt.public_key;
+
+ if(pk->numrevkeys==0 && pk->version==3)
+ {
+ /* It is legal but bad for compatibility to add a revoker to a
+ v3 key as it means that PGP2 will not be able to use that key
+ anymore. Also, PGP may not expect a revoker on a v3 key.
+ Don't bother to ask this if the key already has a revoker -
+ any damage has already been done at that point. -dms */
+ if(opt.expert)
+ {
+ tty_printf(_("WARNING: This is a PGP 2.x-style key. "
+ "Adding a designated revoker may cause\n"
+ " some versions of PGP to reject this key.\n"));
+
+ if(!cpr_get_answer_is_yes("keyedit.v3_revoker.okay",
+ _("Are you sure you still want "
+ "to add it? (y/N) ")))
+ return 0;
+ }
+ else
+ {
+ tty_printf(_("You may not add a designated revoker to "
+ "a PGP 2.x-style key.\n"));
+ return 0;
+ }
+ }
+
+ sk=copy_secret_key(NULL,sec_keyblock->pkt->pkt.secret_key);
+
+ for(;;)
+ {
+ char *answer;
+
+ if(revoker_pk)
+ free_public_key(revoker_pk);
+
+ revoker_pk=xmalloc_clear(sizeof(*revoker_pk));
+
+ tty_printf("\n");
+
+ answer=cpr_get_utf8("keyedit.add_revoker",
+ _("Enter the user ID of the designated revoker: "));
+ if(answer[0]=='\0' || answer[0]=='\004')
+ {
+ xfree(answer);
+ goto fail;
+ }
+
+ /* Note that I'm requesting CERT here, which usually implies
+ primary keys only, but some casual testing shows that PGP and
+ GnuPG both can handle a designated revokation from a
+ subkey. */
+ revoker_pk->req_usage=PUBKEY_USAGE_CERT;
+ rc=get_pubkey_byname(revoker_pk,answer,NULL,NULL,1);
+ if(rc)
+ {
+ log_error (_("key \"%s\" not found: %s\n"),answer,g10_errstr(rc));
+ xfree(answer);
+ continue;
+ }
+
+ xfree(answer);
+
+ fingerprint_from_pk(revoker_pk,revkey.fpr,&fprlen);
+ if(fprlen!=20)
+ {
+ log_error(_("cannot appoint a PGP 2.x style key as a "
+ "designated revoker\n"));
+ continue;
+ }
+
+ revkey.class=0x80;
+ if(sensitive)
+ revkey.class|=0x40;
+ revkey.algid=revoker_pk->pubkey_algo;
+
+ if(cmp_public_keys(revoker_pk,pk)==0)
+ {
+ /* This actually causes no harm (after all, a key that
+ designates itself as a revoker is the same as a
+ regular key), but it's easy enough to check. */
+ log_error(_("you cannot appoint a key as its own "
+ "designated revoker\n"));
+
+ continue;
+ }
+
+ keyid_from_pk(pk,NULL);
+
+ /* Does this revkey already exist? */
+ if(!pk->revkey && pk->numrevkeys)
+ BUG();
+ else
+ {
+ int i;
+
+ for(i=0;i<pk->numrevkeys;i++)
+ {
+ if(memcmp(&pk->revkey[i],&revkey,
+ sizeof(struct revocation_key))==0)
+ {
+ char buf[50];
+
+ log_error(_("this key has already been designated "
+ "as a revoker\n"));
+
+ sprintf(buf,"%08lX%08lX",
+ (ulong)pk->keyid[0],(ulong)pk->keyid[1]);
+ write_status_text(STATUS_ALREADY_SIGNED,buf);
+
+ break;
+ }
+ }
+
+ if(i<pk->numrevkeys)
+ continue;
+ }
+
+ print_pubkey_info(NULL,revoker_pk);
+ print_fingerprint(revoker_pk,NULL,2);
+ tty_printf("\n");
+
+ tty_printf(_("WARNING: appointing a key as a designated revoker "
+ "cannot be undone!\n"));
+
+ tty_printf("\n");
+
+ if(!cpr_get_answer_is_yes("keyedit.add_revoker.okay",
+ _("Are you sure you want to appoint this "
+ "key as a designated revoker? (y/N) ")))
+ continue;
+
+ free_public_key(revoker_pk);
+ revoker_pk=NULL;
+ break;
+ }
+
+ /* The 1F signature must be at least v4 to carry the revocation key
+ subpacket. */
+ rc = make_keysig_packet( &sig, pk, NULL, NULL, sk, 0x1F, 0, 4, 0, 0,
+ keygen_add_revkey,&revkey );
+ if( rc )
+ {
+ log_error("signing failed: %s\n", g10_errstr(rc) );
+ goto fail;
+ }
+
+ free_secret_key(sk);
+ sk=NULL;
+
+ /* insert into secret keyblock */
+ pkt = xmalloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_SIGNATURE;
+ pkt->pkt.signature = copy_signature(NULL, sig);
+ insert_kbnode( sec_keyblock, new_kbnode(pkt), PKT_SIGNATURE );
+
+ /* insert into public keyblock */
+ pkt = xmalloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_SIGNATURE;
+ pkt->pkt.signature = sig;
+ insert_kbnode( pub_keyblock, new_kbnode(pkt), PKT_SIGNATURE );
+
+ return 1;
+
+ fail:
+ if(sk)
+ free_secret_key(sk);
+ if(sig)
+ free_seckey_enc(sig);
+ if(revoker_pk)
+ free_public_key(revoker_pk);
+
+ return 0;
+}
+
+
+static int
+menu_expire( KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
+ int n1, signumber, rc;
+ u32 expiredate;
+ int mainkey=0;
+ PKT_secret_key *sk; /* copy of the main sk */
+ PKT_public_key *main_pk, *sub_pk;
+ PKT_user_id *uid;
+ KBNODE node;
+ u32 keyid[2];
+
+ if( count_selected_keys( sec_keyblock ) ) {
+ tty_printf(_("Please remove selections from the secret keys.\n"));
+ return 0;
+ }
+
+ n1 = count_selected_keys( pub_keyblock );
+ if( n1 > 1 ) {
+ tty_printf(_("Please select at most one subkey.\n"));
+ return 0;
+ }
+ else if( n1 )
+ tty_printf(_("Changing expiration time for a subkey.\n"));
+ else
+ {
+ tty_printf(_("Changing expiration time for the primary key.\n"));
+ mainkey=1;
+ no_primary_warning(pub_keyblock);
+ }
+
+ expiredate = ask_expiredate();
+ node = find_kbnode( sec_keyblock, PKT_SECRET_KEY );
+ sk = copy_secret_key( NULL, node->pkt->pkt.secret_key);
+
+ /* Now we can actually change the self signature(s) */
+ main_pk = sub_pk = NULL;
+ uid = NULL;
+ signumber = 0;
+ for( node=pub_keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_KEY ) {
+ main_pk = node->pkt->pkt.public_key;
+ keyid_from_pk( main_pk, keyid );
+ main_pk->expiredate = expiredate;
+ }
+ else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ && (node->flag & NODFLG_SELKEY ) ) {
+ sub_pk = node->pkt->pkt.public_key;
+ sub_pk->expiredate = expiredate;
+ }
+ else if( node->pkt->pkttype == PKT_USER_ID )
+ uid = node->pkt->pkt.user_id;
+ else if( main_pk && node->pkt->pkttype == PKT_SIGNATURE
+ && ( mainkey || sub_pk ) ) {
+ PKT_signature *sig = node->pkt->pkt.signature;
+ if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
+ && ( (mainkey && uid
+ && uid->created && (sig->sig_class&~3) == 0x10)
+ || (!mainkey && sig->sig_class == 0x18) )
+ && sig->flags.chosen_selfsig )
+ {
+ /* this is a selfsignature which is to be replaced */
+ PKT_signature *newsig;
+ PACKET *newpkt;
+ KBNODE sn;
+ int signumber2 = 0;
+
+ signumber++;
+
+ if( (mainkey && main_pk->version < 4)
+ || (!mainkey && sub_pk->version < 4 ) ) {
+ log_info(_(
+ "You can't change the expiration date of a v3 key\n"));
+ free_secret_key( sk );
+ return 0;
+ }
+
+ /* find the corresponding secret self-signature */
+ for( sn=sec_keyblock; sn; sn = sn->next ) {
+ if( sn->pkt->pkttype == PKT_SIGNATURE ) {
+ PKT_signature *b = sn->pkt->pkt.signature;
+ if( keyid[0] == b->keyid[0] && keyid[1] == b->keyid[1]
+ && sig->sig_class == b->sig_class
+ && ++signumber2 == signumber )
+ break;
+ }
+ }
+ if( !sn )
+ log_info(_("No corresponding signature in secret ring\n"));
+
+ if( mainkey )
+ rc = update_keysig_packet(&newsig, sig, main_pk, uid, NULL,
+ sk, keygen_add_key_expire, main_pk);
+ else
+ rc = update_keysig_packet(&newsig, sig, main_pk, NULL, sub_pk,
+ sk, keygen_add_key_expire, sub_pk );
+ if( rc ) {
+ log_error("make_keysig_packet failed: %s\n",
+ g10_errstr(rc));
+ free_secret_key( sk );
+ return 0;
+ }
+ /* replace the packet */
+ newpkt = xmalloc_clear( sizeof *newpkt );
+ newpkt->pkttype = PKT_SIGNATURE;
+ newpkt->pkt.signature = newsig;
+ free_packet( node->pkt );
+ xfree( node->pkt );
+ node->pkt = newpkt;
+ if( sn ) {
+ newpkt = xmalloc_clear( sizeof *newpkt );
+ newpkt->pkttype = PKT_SIGNATURE;
+ newpkt->pkt.signature = copy_signature( NULL, newsig );
+ free_packet( sn->pkt );
+ xfree( sn->pkt );
+ sn->pkt = newpkt;
+ }
+ sub_pk = NULL;
+ }
+ }
+ }
+
+ free_secret_key( sk );
+ update_trust=1;
+ return 1;
+}
+
+static int
+menu_backsign(KBNODE pub_keyblock,KBNODE sec_keyblock)
+{
+ int rc,modified=0;
+ PKT_public_key *main_pk;
+ PKT_secret_key *main_sk,*sub_sk=NULL;
+ KBNODE node;
+
+ assert(pub_keyblock->pkt->pkttype==PKT_PUBLIC_KEY);
+ assert(sec_keyblock->pkt->pkttype==PKT_SECRET_KEY);
+
+ merge_keys_and_selfsig(pub_keyblock);
+ main_pk=pub_keyblock->pkt->pkt.public_key;
+ main_sk=copy_secret_key(NULL,sec_keyblock->pkt->pkt.secret_key);
+ keyid_from_pk(main_pk,NULL);
+
+ for(node=pub_keyblock;node;node=node->next)
+ {
+ PKT_public_key *sub_pk=NULL;
+ KBNODE node2,sig_pk=NULL,sig_sk=NULL;
+ char *passphrase;
+
+ if(sub_sk)
+ {
+ free_secret_key(sub_sk);
+ sub_sk=NULL;
+ }
+
+ /* Find a signing subkey with no backsig */
+ if(node->pkt->pkttype==PKT_PUBLIC_SUBKEY)
+ {
+ if(node->pkt->pkt.public_key->pubkey_usage&PUBKEY_USAGE_SIG)
+ {
+ if(node->pkt->pkt.public_key->backsig)
+ tty_printf(_("signing subkey %s is already cross-certified\n"),
+ keystr_from_pk(node->pkt->pkt.public_key));
+ else
+ sub_pk=node->pkt->pkt.public_key;
+ }
+ else
+ tty_printf(_("subkey %s does not sign and so does"
+ " not need to be cross-certified\n"),
+ keystr_from_pk(node->pkt->pkt.public_key));
+ }
+
+ if(!sub_pk)
+ continue;
+
+ /* Find the selected selfsig on this subkey */
+ for(node2=node->next;
+ node2 && node2->pkt->pkttype==PKT_SIGNATURE;
+ node2=node2->next)
+ if(node2->pkt->pkt.signature->version>=4
+ && node2->pkt->pkt.signature->flags.chosen_selfsig)
+ {
+ sig_pk=node2;
+ break;
+ }
+
+ if(!sig_pk)
+ continue;
+
+ /* Find the secret subkey that matches the public subkey */
+ for(node2=sec_keyblock;node2;node2=node2->next)
+ if(node2->pkt->pkttype==PKT_SECRET_SUBKEY
+ && !cmp_public_secret_key(sub_pk,node2->pkt->pkt.secret_key))
+ {
+ sub_sk=copy_secret_key(NULL,node2->pkt->pkt.secret_key);
+ break;
+ }
+
+ if(!sub_sk)
+ {
+ tty_printf(_("no secret subkey for public subkey %s - ignoring\n"),
+ keystr_from_pk(sub_pk));
+ continue;
+ }
+
+ /* Now finally find the matching selfsig on the secret subkey.
+ We can't use chosen_selfsig here (it's not set for secret
+ keys), so we just pick the selfsig with the right class.
+ This is what menu_expire does as well. */
+ for(node2=node2->next;
+ node2 && node2->pkt->pkttype!=PKT_SECRET_SUBKEY;
+ node2=node2->next)
+ if(node2->pkt->pkttype==PKT_SIGNATURE
+ && node2->pkt->pkt.signature->version>=4
+ && node2->pkt->pkt.signature->keyid[0]==sig_pk->pkt->pkt.signature->keyid[0]
+ && node2->pkt->pkt.signature->keyid[1]==sig_pk->pkt->pkt.signature->keyid[1]
+ && node2->pkt->pkt.signature->sig_class==sig_pk->pkt->pkt.signature->sig_class)
+ {
+ sig_sk=node2;
+ break;
+ }
+
+ /* Now we can get to work. We have a main key and secret part,
+ a signing subkey with signature and secret part possibly with
+ signature. */
+
+ passphrase=get_last_passphrase();
+ set_next_passphrase(passphrase);
+ xfree(passphrase);
+
+ rc=make_backsig(sig_pk->pkt->pkt.signature,main_pk,sub_pk,sub_sk);
+ if(rc==0)
+ {
+ PKT_signature *newsig;
+ PACKET *newpkt;
+
+ passphrase=get_last_passphrase();
+ set_next_passphrase(passphrase);
+ xfree(passphrase);
+
+ rc=update_keysig_packet(&newsig,sig_pk->pkt->pkt.signature,main_pk,
+ NULL,sub_pk,main_sk,NULL,NULL);
+ if(rc==0)
+ {
+ /* Put the new sig into place on the pubkey */
+ newpkt=xmalloc_clear(sizeof(*newpkt));
+ newpkt->pkttype=PKT_SIGNATURE;
+ newpkt->pkt.signature=newsig;
+ free_packet(sig_pk->pkt);
+ xfree(sig_pk->pkt);
+ sig_pk->pkt=newpkt;
+
+ if(sig_sk)
+ {
+ /* Put the new sig into place on the seckey */
+ newpkt=xmalloc_clear(sizeof(*newpkt));
+ newpkt->pkttype=PKT_SIGNATURE;
+ newpkt->pkt.signature=copy_signature(NULL,newsig);
+ free_packet(sig_sk->pkt);
+ xfree(sig_sk->pkt);
+ sig_sk->pkt=newpkt;
+ }
+
+ modified=1;
+ }
+ else
+ {
+ log_error("update_keysig_packet failed: %s\n",g10_errstr(rc));
+ break;
+ }
+ }
+ else
+ {
+ log_error("make_backsig failed: %s\n",g10_errstr(rc));
+ break;
+ }
+ }
+
+ set_next_passphrase(NULL);
+
+ free_secret_key(main_sk);
+ if(sub_sk)
+ free_secret_key(sub_sk);
+
+ return modified;
+}
+
+
+static int
+change_primary_uid_cb ( PKT_signature *sig, void *opaque )
+{
+ byte buf[1];
+
+ /* first clear all primary uid flags so that we are sure none are
+ * lingering around */
+ delete_sig_subpkt (sig->hashed, SIGSUBPKT_PRIMARY_UID);
+ delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PRIMARY_UID);
+
+ /* if opaque is set,we want to set the primary id */
+ if (opaque) {
+ buf[0] = 1;
+ build_sig_subpkt (sig, SIGSUBPKT_PRIMARY_UID, buf, 1 );
+ }
+
+ return 0;
+}
+
+
+/*
+ * Set the primary uid flag for the selected UID. We will also reset
+ * all other primary uid flags. For this to work with have to update
+ * all the signature timestamps. If we would do this with the current
+ * time, we lose quite a lot of information, so we use a a kludge to
+ * do this: Just increment the timestamp by one second which is
+ * sufficient to updated a signature during import.
+ */
+static int
+menu_set_primary_uid ( KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
+ PKT_secret_key *sk; /* copy of the main sk */
+ PKT_public_key *main_pk;
+ PKT_user_id *uid;
+ KBNODE node;
+ u32 keyid[2];
+ int selected;
+ int attribute = 0;
+ int modified = 0;
+
+ if ( count_selected_uids (pub_keyblock) != 1 ) {
+ tty_printf(_("Please select exactly one user ID.\n"));
+ return 0;
+ }
+
+ node = find_kbnode( sec_keyblock, PKT_SECRET_KEY );
+ sk = copy_secret_key( NULL, node->pkt->pkt.secret_key);
+
+ /* Now we can actually change the self signature(s) */
+ main_pk = NULL;
+ uid = NULL;
+ selected = 0;
+
+ /* Is our selected uid an attribute packet? */
+ for ( node=pub_keyblock; node; node = node->next )
+ if (node->pkt->pkttype == PKT_USER_ID && node->flag & NODFLG_SELUID)
+ attribute = (node->pkt->pkt.user_id->attrib_data!=NULL);
+
+ for ( node=pub_keyblock; node; node = node->next ) {
+ if ( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+ break; /* ready */
+
+ if ( node->pkt->pkttype == PKT_PUBLIC_KEY ) {
+ main_pk = node->pkt->pkt.public_key;
+ keyid_from_pk( main_pk, keyid );
+ }
+ else if ( node->pkt->pkttype == PKT_USER_ID ) {
+ uid = node->pkt->pkt.user_id;
+ selected = node->flag & NODFLG_SELUID;
+ }
+ else if ( main_pk && uid && node->pkt->pkttype == PKT_SIGNATURE ) {
+ PKT_signature *sig = node->pkt->pkt.signature;
+ if ( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
+ && (uid && (sig->sig_class&~3) == 0x10)
+ && attribute == (uid->attrib_data!=NULL)
+ && sig->flags.chosen_selfsig )
+ {
+ if(sig->version < 4) {
+ char *user=utf8_to_native(uid->name,strlen(uid->name),0);
+
+ log_info(_("skipping v3 self-signature on user ID \"%s\"\n"),
+ user);
+ xfree(user);
+ }
+ else {
+ /* This is a selfsignature which is to be replaced.
+ We can just ignore v3 signatures because they are
+ not able to carry the primary ID flag. We also
+ ignore self-sigs on user IDs that are not of the
+ same type that we are making primary. That is, if
+ we are making a user ID primary, we alter user IDs.
+ If we are making an attribute packet primary, we
+ alter attribute packets. */
+
+ /* FIXME: We must make sure that we only have one
+ self-signature per user ID here (not counting
+ revocations) */
+ PKT_signature *newsig;
+ PACKET *newpkt;
+ const byte *p;
+ int action;
+
+ /* see whether this signature has the primary UID flag */
+ p = parse_sig_subpkt (sig->hashed,
+ SIGSUBPKT_PRIMARY_UID, NULL );
+ if ( !p )
+ p = parse_sig_subpkt (sig->unhashed,
+ SIGSUBPKT_PRIMARY_UID, NULL );
+ if ( p && *p ) /* yes */
+ action = selected? 0 : -1;
+ else /* no */
+ action = selected? 1 : 0;
+
+ if (action) {
+ int rc = update_keysig_packet (&newsig, sig,
+ main_pk, uid, NULL,
+ sk,
+ change_primary_uid_cb,
+ action > 0? "x":NULL );
+ if( rc ) {
+ log_error ("update_keysig_packet failed: %s\n",
+ g10_errstr(rc));
+ free_secret_key( sk );
+ return 0;
+ }
+ /* replace the packet */
+ newpkt = xmalloc_clear( sizeof *newpkt );
+ newpkt->pkttype = PKT_SIGNATURE;
+ newpkt->pkt.signature = newsig;
+ free_packet( node->pkt );
+ xfree( node->pkt );
+ node->pkt = newpkt;
+ modified = 1;
+ }
+ }
+ }
+ }
+ }
+
+ free_secret_key( sk );
+ return modified;
+}
+
+
+/*
+ * Set preferences to new values for the selected user IDs
+ */
+static int
+menu_set_preferences (KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
+ PKT_secret_key *sk; /* copy of the main sk */
+ PKT_public_key *main_pk;
+ PKT_user_id *uid;
+ KBNODE node;
+ u32 keyid[2];
+ int selected, select_all;
+ int modified = 0;
+
+ no_primary_warning(pub_keyblock);
+
+ select_all = !count_selected_uids (pub_keyblock);
+
+ node = find_kbnode( sec_keyblock, PKT_SECRET_KEY );
+ sk = copy_secret_key( NULL, node->pkt->pkt.secret_key);
+
+ /* Now we can actually change the self signature(s) */
+ main_pk = NULL;
+ uid = NULL;
+ selected = 0;
+ for ( node=pub_keyblock; node; node = node->next ) {
+ if ( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+ break; /* ready */
+
+ if ( node->pkt->pkttype == PKT_PUBLIC_KEY ) {
+ main_pk = node->pkt->pkt.public_key;
+ keyid_from_pk( main_pk, keyid );
+ }
+ else if ( node->pkt->pkttype == PKT_USER_ID ) {
+ uid = node->pkt->pkt.user_id;
+ selected = select_all || (node->flag & NODFLG_SELUID);
+ }
+ else if ( main_pk && uid && selected
+ && node->pkt->pkttype == PKT_SIGNATURE ) {
+ PKT_signature *sig = node->pkt->pkt.signature;
+ if ( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
+ && (uid && (sig->sig_class&~3) == 0x10)
+ && sig->flags.chosen_selfsig ) {
+ if( sig->version < 4 ) {
+ char *user=utf8_to_native(uid->name,strlen(uid->name),0);
+
+ log_info(_("skipping v3 self-signature on user ID \"%s\"\n"),
+ user);
+ xfree(user);
+ }
+ else {
+ /* This is a selfsignature which is to be replaced
+ * We have to ignore v3 signatures because they are
+ * not able to carry the preferences */
+ PKT_signature *newsig;
+ PACKET *newpkt;
+ int rc;
+
+ rc = update_keysig_packet (&newsig, sig,
+ main_pk, uid, NULL,
+ sk,
+ keygen_upd_std_prefs,
+ NULL );
+ if( rc ) {
+ log_error ("update_keysig_packet failed: %s\n",
+ g10_errstr(rc));
+ free_secret_key( sk );
+ return 0;
+ }
+ /* replace the packet */
+ newpkt = xmalloc_clear( sizeof *newpkt );
+ newpkt->pkttype = PKT_SIGNATURE;
+ newpkt->pkt.signature = newsig;
+ free_packet( node->pkt );
+ xfree( node->pkt );
+ node->pkt = newpkt;
+ modified = 1;
+ }
+ }
+ }
+ }
+
+ free_secret_key( sk );
+ return modified;
+}
+
+
+static int
+menu_set_keyserver_url (const char *url,
+ KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
+ PKT_secret_key *sk; /* copy of the main sk */
+ PKT_public_key *main_pk;
+ PKT_user_id *uid;
+ KBNODE node;
+ u32 keyid[2];
+ int selected, select_all;
+ int modified = 0;
+ char *answer,*uri;
+
+ no_primary_warning(pub_keyblock);
+
+ if(url)
+ answer=xstrdup(url);
+ else
+ {
+ answer=cpr_get_utf8("keyedit.add_keyserver",
+ _("Enter your preferred keyserver URL: "));
+ if(answer[0]=='\0' || answer[0]=='\004')
+ {
+ xfree(answer);
+ return 0;
+ }
+ }
+
+ if(ascii_strcasecmp(answer,"none")==0)
+ uri=NULL;
+ else
+ {
+ struct keyserver_spec *keyserver=NULL;
+ /* Sanity check the format */
+ keyserver=parse_keyserver_uri(answer,1,NULL,0);
+ xfree(answer);
+ if(!keyserver)
+ {
+ log_info(_("could not parse keyserver URL\n"));
+ return 0;
+ }
+ uri=xstrdup(keyserver->uri);
+ free_keyserver_spec(keyserver);
+ }
+
+ select_all = !count_selected_uids (pub_keyblock);
+
+ node = find_kbnode( sec_keyblock, PKT_SECRET_KEY );
+ sk = copy_secret_key( NULL, node->pkt->pkt.secret_key);
+
+ /* Now we can actually change the self signature(s) */
+ main_pk = NULL;
+ uid = NULL;
+ selected = 0;
+ for ( node=pub_keyblock; node; node = node->next )
+ {
+ if ( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+ break; /* ready */
+
+ if ( node->pkt->pkttype == PKT_PUBLIC_KEY )
+ {
+ main_pk = node->pkt->pkt.public_key;
+ keyid_from_pk( main_pk, keyid );
+ }
+ else if ( node->pkt->pkttype == PKT_USER_ID )
+ {
+ uid = node->pkt->pkt.user_id;
+ selected = select_all || (node->flag & NODFLG_SELUID);
+ }
+ else if ( main_pk && uid && selected
+ && node->pkt->pkttype == PKT_SIGNATURE )
+ {
+ PKT_signature *sig = node->pkt->pkt.signature;
+ if ( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
+ && (uid && (sig->sig_class&~3) == 0x10)
+ && sig->flags.chosen_selfsig)
+ {
+ char *user=utf8_to_native(uid->name,strlen(uid->name),0);
+ if( sig->version < 4 )
+ log_info(_("skipping v3 self-signature on user ID \"%s\"\n"),
+ user);
+ else
+ {
+ /* This is a selfsignature which is to be replaced
+ * We have to ignore v3 signatures because they are
+ * not able to carry the subpacket. */
+ PKT_signature *newsig;
+ PACKET *newpkt;
+ int rc;
+ const byte *p;
+ size_t plen;
+
+ p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_PREF_KS,&plen);
+ if(p && plen)
+ {
+ tty_printf("Current preferred keyserver for user"
+ " ID \"%s\": ",user);
+ tty_print_utf8_string(p,plen);
+ tty_printf("\n");
+ if(!cpr_get_answer_is_yes("keyedit.confirm_keyserver",
+ uri?_("Are you sure you want to replace it? (y/N) "):
+ _("Are you sure you want to delete it? (y/N) ")))
+ continue;
+ }
+ else if(uri==NULL)
+ {
+ /* There is no current keyserver URL, so there
+ is no point in trying to un-set it. */
+ continue;
+ }
+
+ rc = update_keysig_packet (&newsig, sig,
+ main_pk, uid, NULL,
+ sk,
+ keygen_add_keyserver_url, uri );
+ if( rc )
+ {
+ log_error ("update_keysig_packet failed: %s\n",
+ g10_errstr(rc));
+ free_secret_key( sk );
+ xfree(uri);
+ return 0;
+ }
+ /* replace the packet */
+ newpkt = xmalloc_clear( sizeof *newpkt );
+ newpkt->pkttype = PKT_SIGNATURE;
+ newpkt->pkt.signature = newsig;
+ free_packet( node->pkt );
+ xfree( node->pkt );
+ node->pkt = newpkt;
+ modified = 1;
+ }
+
+ xfree(user);
+ }
+ }
+ }
+
+ xfree(uri);
+ free_secret_key( sk );
+ return modified;
+}
+
+static int
+menu_set_notation(const char *string,KBNODE pub_keyblock,KBNODE sec_keyblock)
+{
+ PKT_secret_key *sk; /* copy of the main sk */
+ PKT_public_key *main_pk;
+ PKT_user_id *uid;
+ KBNODE node;
+ u32 keyid[2];
+ int selected, select_all;
+ int modified = 0;
+ char *answer;
+ struct notation *notation;
+
+ no_primary_warning(pub_keyblock);
+
+ if(string)
+ answer=xstrdup(string);
+ else
+ {
+ answer=cpr_get_utf8("keyedit.add_notation",
+ _("Enter the notation: "));
+ if(answer[0]=='\0' || answer[0]=='\004')
+ {
+ xfree(answer);
+ return 0;
+ }
+ }
+
+ if(ascii_strcasecmp(answer,"none")==0
+ || ascii_strcasecmp(answer,"-")==0)
+ notation=NULL; /* delete them all */
+ else
+ {
+ notation=string_to_notation(answer,0);
+ if(!notation)
+ {
+ xfree(answer);
+ return 0;
+ }
+ }
+
+ xfree(answer);
+
+ select_all = !count_selected_uids (pub_keyblock);
+
+ node = find_kbnode( sec_keyblock, PKT_SECRET_KEY );
+ sk = copy_secret_key( NULL, node->pkt->pkt.secret_key);
+
+ /* Now we can actually change the self signature(s) */
+ main_pk = NULL;
+ uid = NULL;
+ selected = 0;
+ for ( node=pub_keyblock; node; node = node->next )
+ {
+ if ( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+ break; /* ready */
+
+ if ( node->pkt->pkttype == PKT_PUBLIC_KEY )
+ {
+ main_pk = node->pkt->pkt.public_key;
+ keyid_from_pk( main_pk, keyid );
+ }
+ else if ( node->pkt->pkttype == PKT_USER_ID )
+ {
+ uid = node->pkt->pkt.user_id;
+ selected = select_all || (node->flag & NODFLG_SELUID);
+ }
+ else if ( main_pk && uid && selected
+ && node->pkt->pkttype == PKT_SIGNATURE )
+ {
+ PKT_signature *sig = node->pkt->pkt.signature;
+ if ( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1]
+ && (uid && (sig->sig_class&~3) == 0x10)
+ && sig->flags.chosen_selfsig)
+ {
+ char *user=utf8_to_native(uid->name,strlen(uid->name),0);
+ if( sig->version < 4 )
+ log_info(_("skipping v3 self-signature on user ID \"%s\"\n"),
+ user);
+ else
+ {
+ PKT_signature *newsig;
+ PACKET *newpkt;
+ int rc,skip=0,addonly=1;
+
+ if(sig->flags.notation)
+ {
+ tty_printf("Current notations for user ID \"%s\":\n",
+ user);
+ tty_print_notations(-9,sig);
+ }
+ else
+ {
+ tty_printf("No notations on user ID \"%s\"\n",user);
+ if(notation==NULL)
+ {
+ /* There are no current notations, so there
+ is no point in trying to un-set them. */
+ continue;
+ }
+ }
+
+ if(notation)
+ {
+ struct notation *n;
+ int deleting=0;
+
+ notation->next=sig_to_notation(sig);
+
+ for(n=notation->next;n;n=n->next)
+ if(strcmp(n->name,notation->name)==0)
+ {
+ if(notation->value)
+ {
+ if(strcmp(n->value,notation->value)==0)
+ {
+ if(notation->flags.ignore)
+ {
+ /* Value match with a delete
+ flag. */
+ n->flags.ignore=1;
+ deleting=1;
+ }
+ else
+ {
+ /* Adding the same notation
+ twice, so don't add it at
+ all. */
+ skip=1;
+ tty_printf("Skipping notation:"
+ " %s=%s\n",
+ notation->name,
+ notation->value);
+ break;
+ }
+ }
+ }
+ else
+ {
+ /* No value, so it means delete. */
+ n->flags.ignore=1;
+ deleting=1;
+ }
+
+ if(n->flags.ignore)
+ {
+ tty_printf("Removing notation: %s=%s\n",
+ n->name,n->value);
+ addonly=0;
+ }
+ }
+
+ if(!notation->flags.ignore && !skip)
+ tty_printf("Adding notation: %s=%s\n",
+ notation->name,notation->value);
+
+ /* We tried to delete, but had no matches */
+ if(notation->flags.ignore && !deleting)
+ continue;
+ }
+ else
+ {
+ tty_printf("Removing all notations\n");
+ addonly=0;
+ }
+
+ if(skip
+ || (!addonly
+ && !cpr_get_answer_is_yes("keyedit.confirm_notation",
+ _("Proceed? (y/N) "))))
+ continue;
+
+ rc = update_keysig_packet (&newsig, sig,
+ main_pk, uid, NULL,
+ sk,
+ keygen_add_notations, notation );
+ if( rc )
+ {
+ log_error ("update_keysig_packet failed: %s\n",
+ g10_errstr(rc));
+ free_secret_key( sk );
+ free_notation(notation);
+ xfree(user);
+ return 0;
+ }
+
+ /* replace the packet */
+ newpkt = xmalloc_clear( sizeof *newpkt );
+ newpkt->pkttype = PKT_SIGNATURE;
+ newpkt->pkt.signature = newsig;
+ free_packet( node->pkt );
+ xfree( node->pkt );
+ node->pkt = newpkt;
+ modified = 1;
+
+ if(notation)
+ {
+ /* Snip off the notation list from the sig */
+ free_notation(notation->next);
+ notation->next=NULL;
+ }
+
+ xfree(user);
+ }
+ }
+ }
+ }
+
+ free_notation(notation);
+ free_secret_key( sk );
+ return modified;
+}
+
+
+/****************
+ * Select one user id or remove all selection if index is 0.
+ * Returns: True if the selection changed;
+ */
+static int
+menu_select_uid( KBNODE keyblock, int idx )
+{
+ KBNODE node;
+ int i;
+
+ /* first check that the index is valid */
+ if( idx ) {
+ for( i=0, node = keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_USER_ID ) {
+ if( ++i == idx )
+ break;
+ }
+ }
+ if( !node ) {
+ tty_printf(_("No user ID with index %d\n"), idx );
+ return 0;
+ }
+ }
+ else { /* reset all */
+ for( i=0, node = keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_USER_ID )
+ node->flag &= ~NODFLG_SELUID;
+ }
+ return 1;
+ }
+ /* and toggle the new index */
+ for( i=0, node = keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_USER_ID ) {
+ if( ++i == idx ) {
+ if( (node->flag & NODFLG_SELUID) )
+ node->flag &= ~NODFLG_SELUID;
+ else
+ node->flag |= NODFLG_SELUID;
+ }
+ }
+ }
+
+ return 1;
+}
+
+/* Search in the keyblock for a uid that matches namehash */
+static int
+menu_select_uid_namehash( KBNODE keyblock, const char *namehash )
+{
+ byte hash[NAMEHASH_LEN];
+ KBNODE node;
+ int i;
+
+ assert(strlen(namehash)==NAMEHASH_LEN*2);
+
+ for(i=0;i<NAMEHASH_LEN;i++)
+ hash[i]=hextobyte(&namehash[i*2]);
+
+ for(node=keyblock->next;node;node=node->next)
+ {
+ if(node->pkt->pkttype==PKT_USER_ID)
+ {
+ namehash_from_uid(node->pkt->pkt.user_id);
+ if(memcmp(node->pkt->pkt.user_id->namehash,hash,NAMEHASH_LEN)==0)
+ {
+ if(node->flag&NODFLG_SELUID)
+ node->flag &= ~NODFLG_SELUID;
+ else
+ node->flag |= NODFLG_SELUID;
+
+ break;
+ }
+ }
+ }
+
+ if(!node)
+ {
+ tty_printf(_("No user ID with hash %s\n"),namehash);
+ return 0;
+ }
+
+ return 1;
+}
+
+/****************
+ * Select secondary keys
+ * Returns: True if the selection changed;
+ */
+static int
+menu_select_key( KBNODE keyblock, int idx )
+{
+ KBNODE node;
+ int i;
+
+ /* first check that the index is valid */
+ if( idx ) {
+ for( i=0, node = keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+ if( ++i == idx )
+ break;
+ }
+ }
+ if( !node ) {
+ tty_printf(_("No subkey with index %d\n"), idx );
+ return 0;
+ }
+ }
+ else { /* reset all */
+ for( i=0, node = keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY )
+ node->flag &= ~NODFLG_SELKEY;
+ }
+ return 1;
+ }
+ /* and set the new index */
+ for( i=0, node = keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+ if( ++i == idx ) {
+ if( (node->flag & NODFLG_SELKEY) )
+ node->flag &= ~NODFLG_SELKEY;
+ else
+ node->flag |= NODFLG_SELKEY;
+ }
+ }
+ }
+
+ return 1;
+}
+
+
+static int
+count_uids_with_flag( KBNODE keyblock, unsigned flag )
+{
+ KBNODE node;
+ int i=0;
+
+ for( node = keyblock; node; node = node->next )
+ if( node->pkt->pkttype == PKT_USER_ID && (node->flag & flag) )
+ i++;
+ return i;
+}
+
+static int
+count_keys_with_flag( KBNODE keyblock, unsigned flag )
+{
+ KBNODE node;
+ int i=0;
+
+ for( node = keyblock; node; node = node->next )
+ if( ( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY)
+ && (node->flag & flag) )
+ i++;
+ return i;
+}
+
+static int
+count_uids( KBNODE keyblock )
+{
+ KBNODE node;
+ int i=0;
+
+ for( node = keyblock; node; node = node->next )
+ if( node->pkt->pkttype == PKT_USER_ID )
+ i++;
+ return i;
+}
+
+
+/****************
+ * Returns true if there is at least one selected user id
+ */
+static int
+count_selected_uids( KBNODE keyblock )
+{
+ return count_uids_with_flag( keyblock, NODFLG_SELUID);
+}
+
+static int
+count_selected_keys( KBNODE keyblock )
+{
+ return count_keys_with_flag( keyblock, NODFLG_SELKEY);
+}
+
+/* returns how many real (i.e. not attribute) uids are unmarked */
+static int
+real_uids_left( KBNODE keyblock )
+{
+ KBNODE node;
+ int real=0;
+
+ for(node=keyblock;node;node=node->next)
+ if(node->pkt->pkttype==PKT_USER_ID && !(node->flag&NODFLG_SELUID) &&
+ !node->pkt->pkt.user_id->attrib_data)
+ real++;
+
+ return real;
+}
+
+/*
+ * Ask whether the signature should be revoked. If the user commits this,
+ * flag bit MARK_A is set on the signature and the user ID.
+ */
+static void
+ask_revoke_sig( KBNODE keyblock, KBNODE node )
+{
+ int doit=0;
+ PKT_user_id *uid;
+ PKT_signature *sig = node->pkt->pkt.signature;
+ KBNODE unode = find_prev_kbnode( keyblock, node, PKT_USER_ID );
+
+ if( !unode ) {
+ log_error("Oops: no user ID for signature\n");
+ return;
+ }
+
+ uid=unode->pkt->pkt.user_id;
+
+ if(opt.with_colons)
+ {
+ if(uid->attrib_data)
+ printf("uat:::::::::%u %lu",uid->numattribs,uid->attrib_len);
+ else
+ {
+ printf("uid:::::::::");
+ print_string (stdout, uid->name, uid->len, ':');
+ }
+
+ printf("\n");
+
+ print_and_check_one_sig_colon(keyblock,node,NULL,NULL,NULL,NULL,1);
+ }
+ else
+ {
+ char *p=utf8_to_native(unode->pkt->pkt.user_id->name,
+ unode->pkt->pkt.user_id->len,0);
+ tty_printf(_("user ID: \"%s\"\n"),p);
+ xfree(p);
+
+ tty_printf(_("signed by your key %s on %s%s%s\n"),
+ keystr(sig->keyid),datestr_from_sig(sig),
+ sig->flags.exportable?"":_(" (non-exportable)"),"");
+ }
+ if(sig->flags.expired)
+ {
+ tty_printf(_("This signature expired on %s.\n"),
+ expirestr_from_sig(sig));
+ /* Use a different question so we can have different help text */
+ doit=cpr_get_answer_is_yes("ask_revoke_sig.expired",
+ _("Are you sure you still want to revoke it? (y/N) "));
+ }
+ else
+ doit=cpr_get_answer_is_yes("ask_revoke_sig.one",
+ _("Create a revocation certificate for this signature? (y/N) "));
+
+ if(doit) {
+ node->flag |= NODFLG_MARK_A;
+ unode->flag |= NODFLG_MARK_A;
+ }
+}
+
+/****************
+ * Display all user ids of the current public key together with signatures
+ * done by one of our keys. Then walk over all this sigs and ask the user
+ * whether he wants to revoke this signature.
+ * Return: True when the keyblock has changed.
+ */
+static int
+menu_revsig( KBNODE keyblock )
+{
+ PKT_signature *sig;
+ PKT_public_key *primary_pk;
+ KBNODE node;
+ int changed = 0;
+ int rc, any, skip=1, all=!count_selected_uids(keyblock);
+ struct revocation_reason_info *reason = NULL;
+
+ assert(keyblock->pkt->pkttype==PKT_PUBLIC_KEY);
+
+ /* FIXME: detect duplicates here */
+ tty_printf(_("You have signed these user IDs on key %s:\n"),
+ keystr_from_pk(keyblock->pkt->pkt.public_key));
+ for( node = keyblock; node; node = node->next ) {
+ node->flag &= ~(NODFLG_SELSIG | NODFLG_MARK_A);
+ if( node->pkt->pkttype == PKT_USER_ID ) {
+ if( node->flag&NODFLG_SELUID || all ) {
+ PKT_user_id *uid = node->pkt->pkt.user_id;
+ /* Hmmm: Should we show only UIDs with a signature? */
+ tty_printf(" ");
+ tty_print_utf8_string( uid->name, uid->len );
+ tty_printf("\n");
+ skip=0;
+ }
+ else
+ skip=1;
+ }
+ else if( !skip && node->pkt->pkttype == PKT_SIGNATURE
+ && ((sig = node->pkt->pkt.signature),
+ !seckey_available(sig->keyid) ) )
+ {
+ if( (sig->sig_class&~3) == 0x10 )
+ {
+ tty_printf(" ");
+ tty_printf(_("signed by your key %s on %s%s%s\n"),
+ keystr(sig->keyid), datestr_from_sig(sig),
+ sig->flags.exportable?"":_(" (non-exportable)"),
+ sig->flags.revocable?"":_(" (non-revocable)"));
+ if(sig->flags.revocable)
+ node->flag |= NODFLG_SELSIG;
+ }
+ else if( sig->sig_class == 0x30 )
+ {
+ tty_printf(" ");
+ tty_printf(_("revoked by your key %s on %s\n"),
+ keystr(sig->keyid),datestr_from_sig(sig));
+ }
+ }
+ }
+
+ tty_printf("\n");
+
+ /* ask */
+ for( node = keyblock; node; node = node->next ) {
+ if( !(node->flag & NODFLG_SELSIG) )
+ continue;
+ ask_revoke_sig( keyblock, node );
+ }
+
+ /* present selected */
+ any = 0;
+ for( node = keyblock; node; node = node->next ) {
+ if( !(node->flag & NODFLG_MARK_A) )
+ continue;
+ if( !any ) {
+ any = 1;
+ tty_printf(_("You are about to revoke these signatures:\n"));
+ }
+ if( node->pkt->pkttype == PKT_USER_ID ) {
+ PKT_user_id *uid = node->pkt->pkt.user_id;
+ tty_printf(" ");
+ tty_print_utf8_string( uid->name, uid->len );
+ tty_printf("\n");
+ }
+ else if( node->pkt->pkttype == PKT_SIGNATURE ) {
+ sig = node->pkt->pkt.signature;
+ tty_printf(" ");
+ tty_printf(_("signed by your key %s on %s%s%s\n"),
+ keystr(sig->keyid), datestr_from_sig(sig),"",
+ sig->flags.exportable?"":_(" (non-exportable)") );
+ }
+ }
+ if( !any )
+ return 0; /* none selected */
+
+ if( !cpr_get_answer_is_yes("ask_revoke_sig.okay",
+ _("Really create the revocation certificates? (y/N) ")) )
+ return 0; /* forget it */
+
+ reason = ask_revocation_reason( 0, 1, 0 );
+ if( !reason ) { /* user decided to cancel */
+ return 0;
+ }
+
+ /* now we can sign the user ids */
+ reloop: /* (must use this, because we are modifing the list) */
+ primary_pk = keyblock->pkt->pkt.public_key;
+ for( node=keyblock; node; node = node->next ) {
+ KBNODE unode;
+ PACKET *pkt;
+ struct sign_attrib attrib;
+ PKT_secret_key *sk;
+
+ if( !(node->flag & NODFLG_MARK_A)
+ || node->pkt->pkttype != PKT_SIGNATURE )
+ continue;
+ unode = find_prev_kbnode( keyblock, node, PKT_USER_ID );
+ assert( unode ); /* we already checked this */
+
+ memset( &attrib, 0, sizeof attrib );
+ attrib.reason = reason;
+ attrib.non_exportable=!node->pkt->pkt.signature->flags.exportable;
+
+ node->flag &= ~NODFLG_MARK_A;
+ sk = xmalloc_secure_clear( sizeof *sk );
+ if( get_seckey( sk, node->pkt->pkt.signature->keyid ) ) {
+ log_info(_("no secret key\n"));
+ continue;
+ }
+ rc = make_keysig_packet( &sig, primary_pk,
+ unode->pkt->pkt.user_id,
+ NULL,
+ sk,
+ 0x30, 0, 0, 0, 0,
+ sign_mk_attrib,
+ &attrib );
+ free_secret_key(sk);
+ if( rc ) {
+ log_error(_("signing failed: %s\n"), g10_errstr(rc));
+ release_revocation_reason_info( reason );
+ return changed;
+ }
+ changed = 1; /* we changed the keyblock */
+ update_trust = 1;
+ /* Are we revoking our own uid? */
+ if(primary_pk->keyid[0]==sig->keyid[0] &&
+ primary_pk->keyid[1]==sig->keyid[1])
+ unode->pkt->pkt.user_id->is_revoked=1;
+ pkt = xmalloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_SIGNATURE;
+ pkt->pkt.signature = sig;
+ insert_kbnode( unode, new_kbnode(pkt), 0 );
+ goto reloop;
+ }
+
+ release_revocation_reason_info( reason );
+ return changed;
+}
+
+/* Revoke a user ID (i.e. revoke a user ID selfsig). Return true if
+ keyblock changed. */
+static int
+menu_revuid( KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
+ PKT_public_key *pk = pub_keyblock->pkt->pkt.public_key;
+ PKT_secret_key *sk = copy_secret_key( NULL,
+ sec_keyblock->pkt->pkt.secret_key );
+ KBNODE node;
+ int changed = 0;
+ int rc;
+ struct revocation_reason_info *reason = NULL;
+
+ /* Note that this is correct as per the RFCs, but nevertheless
+ somewhat meaningless in the real world. 1991 did define the 0x30
+ sig class, but PGP 2.x did not actually implement it, so it would
+ probably be safe to use v4 revocations everywhere. -ds */
+
+ for( node = pub_keyblock; node; node = node->next )
+ if(pk->version>3 || (node->pkt->pkttype==PKT_USER_ID &&
+ node->pkt->pkt.user_id->selfsigversion>3))
+ {
+ if((reason = ask_revocation_reason( 0, 1, 4 )))
+ break;
+ else
+ goto leave;
+ }
+
+ reloop: /* (better this way because we are modifing the keyring) */
+ for( node = pub_keyblock; node; node = node->next )
+ if(node->pkt->pkttype == PKT_USER_ID && (node->flag & NODFLG_SELUID))
+ {
+ PKT_user_id *uid=node->pkt->pkt.user_id;
+
+ if(uid->is_revoked)
+ {
+ char *user=utf8_to_native(uid->name,uid->len,0);
+ log_info(_("user ID \"%s\" is already revoked\n"),user);
+ xfree(user);
+ }
+ else
+ {
+ PACKET *pkt;
+ PKT_signature *sig;
+ struct sign_attrib attrib;
+ u32 timestamp=make_timestamp();
+
+ if(uid->created>=timestamp)
+ {
+ /* Okay, this is a problem. The user ID selfsig was
+ created in the future, so we need to warn the user and
+ set our revocation timestamp one second after that so
+ everything comes out clean. */
+
+ log_info(_("WARNING: a user ID signature is dated %d"
+ " seconds in the future\n"),uid->created-timestamp);
+
+ timestamp=uid->created+1;
+ }
+
+ memset( &attrib, 0, sizeof attrib );
+ attrib.reason = reason;
+
+ node->flag &= ~NODFLG_SELUID;
+
+ rc = make_keysig_packet( &sig, pk, uid, NULL, sk, 0x30, 0,
+ (reason==NULL)?3:0, timestamp, 0,
+ sign_mk_attrib, &attrib );
+ if( rc )
+ {
+ log_error(_("signing failed: %s\n"), g10_errstr(rc));
+ goto leave;
+ }
+ else
+ {
+ pkt = xmalloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_SIGNATURE;
+ pkt->pkt.signature = sig;
+ insert_kbnode( node, new_kbnode(pkt), 0 );
+
+ /* If the trustdb has an entry for this key+uid then the
+ trustdb needs an update. */
+ if(!update_trust
+ && (get_validity(pk,uid)&TRUST_MASK)>=TRUST_UNDEFINED)
+ update_trust=1;
+
+ changed = 1;
+ node->pkt->pkt.user_id->is_revoked=1;
+
+ goto reloop;
+ }
+ }
+ }
+
+ if(changed)
+ commit_kbnode( &pub_keyblock );
+
+ leave:
+ free_secret_key(sk);
+ release_revocation_reason_info( reason );
+ return changed;
+}
+
+/****************
+ * Revoke the whole key.
+ */
+static int
+menu_revkey( KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
+ PKT_public_key *pk=pub_keyblock->pkt->pkt.public_key;
+ PKT_secret_key *sk;
+ int rc,changed = 0;
+ struct revocation_reason_info *reason;
+ PACKET *pkt;
+ PKT_signature *sig;
+
+ if(pk->is_revoked)
+ {
+ tty_printf(_("Key %s is already revoked.\n"),keystr_from_pk(pk));
+ return 0;
+ }
+
+ reason = ask_revocation_reason( 1, 0, 0 );
+ /* user decided to cancel */
+ if( !reason )
+ return 0;
+
+ sk = copy_secret_key( NULL, sec_keyblock->pkt->pkt.secret_key );
+ rc = make_keysig_packet( &sig, pk, NULL, NULL, sk,
+ 0x20, 0, opt.force_v4_certs?4:0, 0, 0,
+ revocation_reason_build_cb, reason );
+ free_secret_key(sk);
+ if( rc )
+ {
+ log_error(_("signing failed: %s\n"), g10_errstr(rc));
+ goto scram;
+ }
+
+ changed = 1; /* we changed the keyblock */
+
+ pkt = xmalloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_SIGNATURE;
+ pkt->pkt.signature = sig;
+ insert_kbnode( pub_keyblock, new_kbnode(pkt), 0 );
+ commit_kbnode( &pub_keyblock );
+
+ update_trust=1;
+
+ scram:
+ release_revocation_reason_info( reason );
+ return changed;
+}
+
+static int
+menu_revsubkey( KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
+ PKT_public_key *mainpk;
+ KBNODE node;
+ int changed = 0;
+ int rc;
+ struct revocation_reason_info *reason = NULL;
+
+ reason = ask_revocation_reason( 1, 0, 0 );
+ if( !reason ) { /* user decided to cancel */
+ return 0;
+ }
+
+ reloop: /* (better this way because we are modifing the keyring) */
+ mainpk = pub_keyblock->pkt->pkt.public_key;
+ for( node = pub_keyblock; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ && (node->flag & NODFLG_SELKEY) ) {
+ PACKET *pkt;
+ PKT_signature *sig;
+ PKT_secret_key *sk;
+ PKT_public_key *subpk = node->pkt->pkt.public_key;
+ struct sign_attrib attrib;
+
+ if(subpk->is_revoked)
+ {
+ tty_printf(_("Subkey %s is already revoked.\n"),
+ keystr_from_pk(subpk));
+ continue;
+ }
+
+ memset( &attrib, 0, sizeof attrib );
+ attrib.reason = reason;
+
+ node->flag &= ~NODFLG_SELKEY;
+ sk = copy_secret_key( NULL, sec_keyblock->pkt->pkt.secret_key );
+ rc = make_keysig_packet( &sig, mainpk, NULL, subpk, sk,
+ 0x28, 0, 0, 0, 0,
+ sign_mk_attrib, &attrib );
+ free_secret_key(sk);
+ if( rc ) {
+ log_error(_("signing failed: %s\n"), g10_errstr(rc));
+ release_revocation_reason_info( reason );
+ return changed;
+ }
+ changed = 1; /* we changed the keyblock */
+
+ pkt = xmalloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_SIGNATURE;
+ pkt->pkt.signature = sig;
+ insert_kbnode( node, new_kbnode(pkt), 0 );
+ goto reloop;
+ }
+ }
+ commit_kbnode( &pub_keyblock );
+ /*commit_kbnode( &sec_keyblock );*/
+
+ /* No need to set update_trust here since signing keys no longer
+ are used to certify other keys, so there is no change in trust
+ when revoking/removing them */
+
+ release_revocation_reason_info( reason );
+ return changed;
+}
+
+/* Note that update_ownertrust is going to mark the trustdb dirty when
+ enabling or disabling a key. This is arguably sub-optimal as
+ disabled keys are still counted in the web of trust, but perhaps
+ not worth adding extra complexity to change. -ds */
+static int
+enable_disable_key( KBNODE keyblock, int disable )
+{
+ PKT_public_key *pk = find_kbnode( keyblock, PKT_PUBLIC_KEY )
+ ->pkt->pkt.public_key;
+ unsigned int trust, newtrust;
+
+ trust = newtrust = get_ownertrust (pk);
+ newtrust &= ~TRUST_FLAG_DISABLED;
+ if( disable )
+ newtrust |= TRUST_FLAG_DISABLED;
+ if( trust == newtrust )
+ return 0; /* already in that state */
+ update_ownertrust(pk, newtrust );
+ return 0;
+}
+
+
+static void
+menu_showphoto( KBNODE keyblock )
+{
+ KBNODE node;
+ int select_all = !count_selected_uids(keyblock);
+ int count=0;
+ PKT_public_key *pk=NULL;
+
+ /* Look for the public key first. We have to be really, really,
+ explicit as to which photo this is, and what key it is a UID on
+ since people may want to sign it. */
+
+ for( node = keyblock; node; node = node->next )
+ {
+ if( node->pkt->pkttype == PKT_PUBLIC_KEY )
+ pk = node->pkt->pkt.public_key;
+ else if( node->pkt->pkttype == PKT_USER_ID )
+ {
+ PKT_user_id *uid = node->pkt->pkt.user_id;
+ count++;
+
+ if((select_all || (node->flag & NODFLG_SELUID)) &&
+ uid->attribs!=NULL)
+ {
+ int i;
+
+ for(i=0;i<uid->numattribs;i++)
+ {
+ byte type;
+ u32 size;
+
+ if(uid->attribs[i].type==ATTRIB_IMAGE &&
+ parse_image_header(&uid->attribs[i],&type,&size))
+ {
+ tty_printf(_("Displaying %s photo ID of size %ld for "
+ "key %s (uid %d)\n"),
+ image_type_to_string(type,1),
+ (ulong)size,keystr_from_pk(pk),count);
+ show_photos(&uid->attribs[i],1,pk,NULL);
+ }
+ }
+ }
+ }
+ }
+}
diff --git a/g10/keygen.c b/g10/keygen.c
new file mode 100644
index 0000000..620274a
--- /dev/null
+++ b/g10/keygen.c
@@ -0,0 +1,3707 @@
+/* keygen.c - generate a key pair
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <ctype.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "util.h"
+#include "main.h"
+#include "packet.h"
+#include "cipher.h"
+#include "ttyio.h"
+#include "options.h"
+#include "keydb.h"
+#include "trustdb.h"
+#include "status.h"
+#include "i18n.h"
+#include "cardglue.h"
+#include "keyserver-internal.h"
+
+#define MAX_PREFS 30
+
+enum para_name {
+ pKEYTYPE,
+ pKEYLENGTH,
+ pKEYUSAGE,
+ pSUBKEYTYPE,
+ pSUBKEYLENGTH,
+ pSUBKEYUSAGE,
+ pAUTHKEYTYPE,
+ pNAMEREAL,
+ pNAMEEMAIL,
+ pNAMECOMMENT,
+ pPREFERENCES,
+ pREVOKER,
+ pUSERID,
+ pEXPIREDATE,
+ pKEYEXPIRE, /* in n seconds */
+ pSUBKEYEXPIRE, /* in n seconds */
+ pPASSPHRASE,
+ pPASSPHRASE_DEK,
+ pPASSPHRASE_S2K,
+ pSERIALNO,
+ pBACKUPENCDIR,
+ pHANDLE,
+ pKEYSERVER
+};
+
+struct para_data_s {
+ struct para_data_s *next;
+ int lnr;
+ enum para_name key;
+ union {
+ DEK *dek;
+ STRING2KEY *s2k;
+ u32 expire;
+ unsigned int usage;
+ struct revocation_key revkey;
+ char value[1];
+ } u;
+};
+
+struct output_control_s {
+ int lnr;
+ int dryrun;
+ int use_files;
+ struct {
+ char *fname;
+ char *newfname;
+ IOBUF stream;
+ armor_filter_context_t afx;
+ } pub;
+ struct {
+ char *fname;
+ char *newfname;
+ IOBUF stream;
+ armor_filter_context_t afx;
+ } sec;
+};
+
+
+struct opaque_data_usage_and_pk {
+ unsigned int usage;
+ PKT_public_key *pk;
+};
+
+
+static int prefs_initialized = 0;
+static byte sym_prefs[MAX_PREFS];
+static int nsym_prefs;
+static byte hash_prefs[MAX_PREFS];
+static int nhash_prefs;
+static byte zip_prefs[MAX_PREFS];
+static int nzip_prefs;
+static int mdc_available,ks_modify;
+
+static void do_generate_keypair( struct para_data_s *para,
+ struct output_control_s *outctrl, int card );
+static int write_keyblock( IOBUF out, KBNODE node );
+static int gen_card_key (int algo, int keyno, int is_primary,
+ KBNODE pub_root, KBNODE sec_root,
+ PKT_secret_key **ret_sk,
+ u32 expireval, struct para_data_s *para);
+static int gen_card_key_with_backup (int algo, int keyno, int is_primary,
+ KBNODE pub_root, KBNODE sec_root,
+ u32 expireval, struct para_data_s *para,
+ const char *backup_dir);
+
+
+static void
+print_status_key_created (int letter, PKT_public_key *pk, const char *handle)
+{
+ byte array[MAX_FINGERPRINT_LEN], *s;
+ char *buf, *p;
+ size_t i, n;
+
+ if (!handle)
+ handle = "";
+
+ buf = xmalloc (MAX_FINGERPRINT_LEN*2+31 + strlen (handle) + 1);
+
+ p = buf;
+ if (letter || pk)
+ {
+ *p++ = letter;
+ *p++ = ' ';
+ fingerprint_from_pk (pk, array, &n);
+ s = array;
+ for (i=0; i < n ; i++, s++, p += 2)
+ sprintf (p, "%02X", *s);
+ }
+ if (*handle)
+ {
+ *p++ = ' ';
+ for (i=0; handle[i] && i < 100; i++)
+ *p++ = isspace ((unsigned int)handle[i])? '_':handle[i];
+ }
+ *p = 0;
+ write_status_text ((letter || pk)?STATUS_KEY_CREATED:STATUS_KEY_NOT_CREATED,
+ buf);
+ xfree (buf);
+}
+
+static void
+print_status_key_not_created (const char *handle)
+{
+ print_status_key_created (0, NULL, handle);
+}
+
+
+
+static void
+write_uid( KBNODE root, const char *s )
+{
+ PACKET *pkt = xmalloc_clear(sizeof *pkt );
+ size_t n = strlen(s);
+
+ pkt->pkttype = PKT_USER_ID;
+ pkt->pkt.user_id = xmalloc_clear( sizeof *pkt->pkt.user_id + n - 1 );
+ pkt->pkt.user_id->len = n;
+ pkt->pkt.user_id->ref = 1;
+ strcpy(pkt->pkt.user_id->name, s);
+ add_kbnode( root, new_kbnode( pkt ) );
+}
+
+static void
+do_add_key_flags (PKT_signature *sig, unsigned int use)
+{
+ byte buf[1];
+
+ buf[0] = 0;
+
+ /* The spec says that all primary keys MUST be able to certify. */
+ if(sig->sig_class!=0x18)
+ buf[0] |= 0x01;
+
+ if (use & PUBKEY_USAGE_SIG)
+ buf[0] |= 0x02;
+ if (use & PUBKEY_USAGE_ENC)
+ buf[0] |= 0x04 | 0x08;
+ if (use & PUBKEY_USAGE_AUTH)
+ buf[0] |= 0x20;
+
+ if (!buf[0])
+ return;
+
+ build_sig_subpkt (sig, SIGSUBPKT_KEY_FLAGS, buf, 1);
+}
+
+
+int
+keygen_add_key_expire( PKT_signature *sig, void *opaque )
+{
+ PKT_public_key *pk = opaque;
+ byte buf[8];
+ u32 u;
+
+ if( pk->expiredate ) {
+ if(pk->expiredate > pk->timestamp)
+ u= pk->expiredate - pk->timestamp;
+ else
+ u= 1;
+
+ buf[0] = (u >> 24) & 0xff;
+ buf[1] = (u >> 16) & 0xff;
+ buf[2] = (u >> 8) & 0xff;
+ buf[3] = u & 0xff;
+ build_sig_subpkt( sig, SIGSUBPKT_KEY_EXPIRE, buf, 4 );
+ }
+ else
+ {
+ /* Make sure we don't leave a key expiration subpacket lying
+ around */
+ delete_sig_subpkt (sig->hashed, SIGSUBPKT_KEY_EXPIRE);
+ }
+
+ return 0;
+}
+
+static int
+keygen_add_key_flags_and_expire (PKT_signature *sig, void *opaque)
+{
+ struct opaque_data_usage_and_pk *oduap = opaque;
+
+ do_add_key_flags (sig, oduap->usage);
+ return keygen_add_key_expire (sig, oduap->pk);
+}
+
+static int
+set_one_pref (int val, int type, const char *item, byte *buf, int *nbuf)
+{
+ int i;
+
+ for (i=0; i < *nbuf; i++ )
+ if (buf[i] == val)
+ {
+ log_info (_("preference `%s' duplicated\n"), item);
+ return -1;
+ }
+
+ if (*nbuf >= MAX_PREFS)
+ {
+ if(type==1)
+ log_info(_("too many cipher preferences\n"));
+ else if(type==2)
+ log_info(_("too many digest preferences\n"));
+ else if(type==3)
+ log_info(_("too many compression preferences\n"));
+ else
+ BUG();
+
+ return -1;
+ }
+
+ buf[(*nbuf)++] = val;
+ return 0;
+}
+
+/*
+ * Parse the supplied string and use it to set the standard
+ * preferences. The string may be in a form like the one printed by
+ * "pref" (something like: "S10 S3 H3 H2 Z2 Z1") or the actual
+ * cipher/hash/compress names. Use NULL to set the default
+ * preferences. Returns: 0 = okay
+ */
+int
+keygen_set_std_prefs (const char *string,int personal)
+{
+ byte sym[MAX_PREFS], hash[MAX_PREFS], zip[MAX_PREFS];
+ int nsym=0, nhash=0, nzip=0, val, rc=0;
+ int mdc=1, modify=0; /* mdc defaults on, modify defaults off. */
+ char dummy_string[45+1]; /* Enough for 15 items. */
+
+ if (!string || !ascii_strcasecmp (string, "default"))
+ {
+ if (opt.def_preference_list)
+ string=opt.def_preference_list;
+ else
+ {
+ dummy_string[0]='\0';
+
+ /* The rationale why we use the order AES256,192,128 is
+ for compatibility reasons with PGP. If gpg would
+ define AES128 first, we would get the somewhat
+ confusing situation:
+
+ gpg -r pgpkey -r gpgkey ---gives--> AES256
+ gpg -r gpgkey -r pgpkey ---gives--> AES
+
+ Note that by using --personal-cipher-preferences it is
+ possible to prefer AES128.
+ */
+
+ /* Make sure we do not add more than 15 items here, as we
+ could overflow the size of dummy_string. We currently
+ have at most 12. */
+ if(!check_cipher_algo(CIPHER_ALGO_AES256))
+ strcat(dummy_string,"S9 ");
+ if(!check_cipher_algo(CIPHER_ALGO_AES192))
+ strcat(dummy_string,"S8 ");
+ if(!check_cipher_algo(CIPHER_ALGO_AES))
+ strcat(dummy_string,"S7 ");
+ if(!check_cipher_algo(CIPHER_ALGO_CAST5))
+ strcat(dummy_string,"S3 ");
+ strcat(dummy_string,"S2 "); /* 3DES */
+ /* If we have it, IDEA goes *after* 3DES so it won't be
+ used unless we're encrypting along with a V3 key.
+ Ideally, we would only put the S1 preference in if the
+ key was RSA and <=2048 bits, as that is what won't
+ break PGP2, but that is difficult with the current
+ code, and not really worth checking as a non-RSA <=2048
+ bit key wouldn't be usable by PGP2 anyway. -dms */
+ if(!check_cipher_algo(CIPHER_ALGO_IDEA))
+ strcat(dummy_string,"S1 ");
+
+ /* SHA-1 */
+ strcat(dummy_string,"H2 ");
+
+ if(!check_digest_algo(DIGEST_ALGO_SHA256))
+ strcat(dummy_string,"H8 ");
+
+ /* RIPEMD160 */
+ strcat(dummy_string,"H3 ");
+
+ /* ZLIB */
+ strcat(dummy_string,"Z2 ");
+
+ if(!check_compress_algo(COMPRESS_ALGO_BZIP2))
+ strcat(dummy_string,"Z3 ");
+
+ /* ZIP */
+ strcat(dummy_string,"Z1");
+
+ string=dummy_string;
+ }
+ }
+ else if (!ascii_strcasecmp (string, "none"))
+ string = "";
+
+ if(strlen(string))
+ {
+ char *tok,*prefstring;
+
+ prefstring=xstrdup(string); /* need a writable string! */
+
+ while((tok=strsep(&prefstring," ,")))
+ {
+ if((val=string_to_cipher_algo(tok)))
+ {
+ if(set_one_pref(val,1,tok,sym,&nsym))
+ rc=-1;
+ }
+ else if((val=string_to_digest_algo(tok)))
+ {
+ if(set_one_pref(val,2,tok,hash,&nhash))
+ rc=-1;
+ }
+ else if((val=string_to_compress_algo(tok))>-1)
+ {
+ if(set_one_pref(val,3,tok,zip,&nzip))
+ rc=-1;
+ }
+ else if (ascii_strcasecmp(tok,"mdc")==0)
+ mdc=1;
+ else if (ascii_strcasecmp(tok,"no-mdc")==0)
+ mdc=0;
+ else if (ascii_strcasecmp(tok,"ks-modify")==0)
+ modify=1;
+ else if (ascii_strcasecmp(tok,"no-ks-modify")==0)
+ modify=0;
+ else
+ {
+ log_info (_("invalid item `%s' in preference string\n"),tok);
+
+ /* Complain if IDEA is not available. */
+ if(ascii_strcasecmp(tok,"s1")==0
+ || ascii_strcasecmp(tok,"idea")==0)
+ idea_cipher_warn(1);
+
+ rc=-1;
+ }
+ }
+
+ xfree(prefstring);
+ }
+
+ if(!rc)
+ {
+ if(personal)
+ {
+ if(personal==PREFTYPE_SYM)
+ {
+ xfree(opt.personal_cipher_prefs);
+
+ if(nsym==0)
+ opt.personal_cipher_prefs=NULL;
+ else
+ {
+ int i;
+
+ opt.personal_cipher_prefs=
+ xmalloc(sizeof(prefitem_t *)*(nsym+1));
+
+ for (i=0; i<nsym; i++)
+ {
+ opt.personal_cipher_prefs[i].type = PREFTYPE_SYM;
+ opt.personal_cipher_prefs[i].value = sym[i];
+ }
+
+ opt.personal_cipher_prefs[i].type = PREFTYPE_NONE;
+ opt.personal_cipher_prefs[i].value = 0;
+ }
+ }
+ else if(personal==PREFTYPE_HASH)
+ {
+ xfree(opt.personal_digest_prefs);
+
+ if(nhash==0)
+ opt.personal_digest_prefs=NULL;
+ else
+ {
+ int i;
+
+ opt.personal_digest_prefs=
+ xmalloc(sizeof(prefitem_t *)*(nhash+1));
+
+ for (i=0; i<nhash; i++)
+ {
+ opt.personal_digest_prefs[i].type = PREFTYPE_HASH;
+ opt.personal_digest_prefs[i].value = hash[i];
+ }
+
+ opt.personal_digest_prefs[i].type = PREFTYPE_NONE;
+ opt.personal_digest_prefs[i].value = 0;
+ }
+ }
+ else if(personal==PREFTYPE_ZIP)
+ {
+ xfree(opt.personal_compress_prefs);
+
+ if(nzip==0)
+ opt.personal_compress_prefs=NULL;
+ else
+ {
+ int i;
+
+ opt.personal_compress_prefs=
+ xmalloc(sizeof(prefitem_t *)*(nzip+1));
+
+ for (i=0; i<nzip; i++)
+ {
+ opt.personal_compress_prefs[i].type = PREFTYPE_ZIP;
+ opt.personal_compress_prefs[i].value = zip[i];
+ }
+
+ opt.personal_compress_prefs[i].type = PREFTYPE_NONE;
+ opt.personal_compress_prefs[i].value = 0;
+ }
+ }
+ }
+ else
+ {
+ memcpy (sym_prefs, sym, (nsym_prefs=nsym));
+ memcpy (hash_prefs, hash, (nhash_prefs=nhash));
+ memcpy (zip_prefs, zip, (nzip_prefs=nzip));
+ mdc_available = mdc;
+ ks_modify = modify;
+ prefs_initialized = 1;
+ }
+ }
+
+ return rc;
+}
+
+/* Return a fake user ID containing the preferences. Caller must
+ free. */
+PKT_user_id *keygen_get_std_prefs(void)
+{
+ int i,j=0;
+ PKT_user_id *uid=xmalloc_clear(sizeof(PKT_user_id));
+
+ if(!prefs_initialized)
+ keygen_set_std_prefs(NULL,0);
+
+ uid->ref=1;
+
+ uid->prefs=xmalloc((sizeof(prefitem_t *)*
+ (nsym_prefs+nhash_prefs+nzip_prefs+1)));
+
+ for(i=0;i<nsym_prefs;i++,j++)
+ {
+ uid->prefs[j].type=PREFTYPE_SYM;
+ uid->prefs[j].value=sym_prefs[i];
+ }
+
+ for(i=0;i<nhash_prefs;i++,j++)
+ {
+ uid->prefs[j].type=PREFTYPE_HASH;
+ uid->prefs[j].value=hash_prefs[i];
+ }
+
+ for(i=0;i<nzip_prefs;i++,j++)
+ {
+ uid->prefs[j].type=PREFTYPE_ZIP;
+ uid->prefs[j].value=zip_prefs[i];
+ }
+
+ uid->prefs[j].type=PREFTYPE_NONE;
+ uid->prefs[j].value=0;
+
+ uid->flags.mdc=mdc_available;
+ uid->flags.ks_modify=ks_modify;
+
+ return uid;
+}
+
+static void
+add_feature_mdc (PKT_signature *sig,int enabled)
+{
+ const byte *s;
+ size_t n;
+ int i;
+ char *buf;
+
+ s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES, &n );
+ /* Already set or cleared */
+ if (s && n &&
+ ((enabled && (s[0] & 0x01)) || (!enabled && !(s[0] & 0x01))))
+ return;
+
+ if (!s || !n) { /* create a new one */
+ n = 1;
+ buf = xmalloc_clear (n);
+ }
+ else {
+ buf = xmalloc (n);
+ memcpy (buf, s, n);
+ }
+
+ if(enabled)
+ buf[0] |= 0x01; /* MDC feature */
+ else
+ buf[0] &= ~0x01;
+
+ /* Are there any bits set? */
+ for(i=0;i<n;i++)
+ if(buf[i]!=0)
+ break;
+
+ if(i==n)
+ delete_sig_subpkt (sig->hashed, SIGSUBPKT_FEATURES);
+ else
+ build_sig_subpkt (sig, SIGSUBPKT_FEATURES, buf, n);
+
+ xfree (buf);
+}
+
+static void
+add_keyserver_modify (PKT_signature *sig,int enabled)
+{
+ const byte *s;
+ size_t n;
+ int i;
+ char *buf;
+
+ /* The keyserver modify flag is a negative flag (i.e. no-modify) */
+ enabled=!enabled;
+
+ s = parse_sig_subpkt (sig->hashed, SIGSUBPKT_KS_FLAGS, &n );
+ /* Already set or cleared */
+ if (s && n &&
+ ((enabled && (s[0] & 0x80)) || (!enabled && !(s[0] & 0x80))))
+ return;
+
+ if (!s || !n) { /* create a new one */
+ n = 1;
+ buf = xmalloc_clear (n);
+ }
+ else {
+ buf = xmalloc (n);
+ memcpy (buf, s, n);
+ }
+
+ if(enabled)
+ buf[0] |= 0x80; /* no-modify flag */
+ else
+ buf[0] &= ~0x80;
+
+ /* Are there any bits set? */
+ for(i=0;i<n;i++)
+ if(buf[i]!=0)
+ break;
+
+ if(i==n)
+ delete_sig_subpkt (sig->hashed, SIGSUBPKT_KS_FLAGS);
+ else
+ build_sig_subpkt (sig, SIGSUBPKT_KS_FLAGS, buf, n);
+
+ xfree (buf);
+}
+
+int
+keygen_upd_std_prefs( PKT_signature *sig, void *opaque )
+{
+ if (!prefs_initialized)
+ keygen_set_std_prefs (NULL, 0);
+
+ if (nsym_prefs)
+ build_sig_subpkt (sig, SIGSUBPKT_PREF_SYM, sym_prefs, nsym_prefs);
+ else
+ {
+ delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_SYM);
+ delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_SYM);
+ }
+
+ if (nhash_prefs)
+ build_sig_subpkt (sig, SIGSUBPKT_PREF_HASH, hash_prefs, nhash_prefs);
+ else
+ {
+ delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_HASH);
+ delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_HASH);
+ }
+
+ if (nzip_prefs)
+ build_sig_subpkt (sig, SIGSUBPKT_PREF_COMPR, zip_prefs, nzip_prefs);
+ else
+ {
+ delete_sig_subpkt (sig->hashed, SIGSUBPKT_PREF_COMPR);
+ delete_sig_subpkt (sig->unhashed, SIGSUBPKT_PREF_COMPR);
+ }
+
+ /* Make sure that the MDC feature flag is set if needed */
+ add_feature_mdc (sig,mdc_available);
+ add_keyserver_modify (sig,ks_modify);
+ keygen_add_keyserver_url(sig,NULL);
+
+ return 0;
+}
+
+
+/****************
+ * Add preference to the self signature packet.
+ * This is only called for packets with version > 3.
+
+ */
+int
+keygen_add_std_prefs( PKT_signature *sig, void *opaque )
+{
+ PKT_public_key *pk = opaque;
+
+ do_add_key_flags (sig, pk->pubkey_usage);
+ keygen_add_key_expire( sig, opaque );
+ keygen_upd_std_prefs (sig, opaque);
+ keygen_add_keyserver_url(sig,NULL);
+
+ return 0;
+}
+
+int
+keygen_add_keyserver_url(PKT_signature *sig, void *opaque)
+{
+ const char *url=opaque;
+
+ if(!url)
+ url=opt.def_keyserver_url;
+
+ if(url)
+ build_sig_subpkt(sig,SIGSUBPKT_PREF_KS,url,strlen(url));
+ else
+ delete_sig_subpkt (sig->hashed,SIGSUBPKT_PREF_KS);
+
+ return 0;
+}
+
+int
+keygen_add_notations(PKT_signature *sig,void *opaque)
+{
+ struct notation *notation;
+
+ /* We always start clean */
+ delete_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION);
+ delete_sig_subpkt(sig->unhashed,SIGSUBPKT_NOTATION);
+ sig->flags.notation=0;
+
+ for(notation=opaque;notation;notation=notation->next)
+ if(!notation->flags.ignore)
+ {
+ unsigned char *buf;
+ unsigned int n1,n2;
+
+ n1=strlen(notation->name);
+ if(notation->altvalue)
+ n2=strlen(notation->altvalue);
+ else if(notation->bdat)
+ n2=notation->blen;
+ else
+ n2=strlen(notation->value);
+
+ buf = xmalloc( 8 + n1 + n2 );
+
+ /* human readable or not */
+ buf[0] = notation->bdat?0:0x80;
+ buf[1] = buf[2] = buf[3] = 0;
+ buf[4] = n1 >> 8;
+ buf[5] = n1;
+ buf[6] = n2 >> 8;
+ buf[7] = n2;
+ memcpy(buf+8, notation->name, n1 );
+ if(notation->altvalue)
+ memcpy(buf+8+n1, notation->altvalue, n2 );
+ else if(notation->bdat)
+ memcpy(buf+8+n1, notation->bdat, n2 );
+ else
+ memcpy(buf+8+n1, notation->value, n2 );
+ build_sig_subpkt( sig, SIGSUBPKT_NOTATION |
+ (notation->flags.critical?SIGSUBPKT_FLAG_CRITICAL:0),
+ buf, 8+n1+n2 );
+ xfree(buf);
+ }
+
+ return 0;
+}
+
+int
+keygen_add_revkey(PKT_signature *sig, void *opaque)
+{
+ struct revocation_key *revkey=opaque;
+ byte buf[2+MAX_FINGERPRINT_LEN];
+
+ buf[0]=revkey->class;
+ buf[1]=revkey->algid;
+ memcpy(&buf[2],revkey->fpr,MAX_FINGERPRINT_LEN);
+
+ build_sig_subpkt(sig,SIGSUBPKT_REV_KEY,buf,2+MAX_FINGERPRINT_LEN);
+
+ /* All sigs with revocation keys set are nonrevocable */
+ sig->flags.revocable=0;
+ buf[0] = 0;
+ build_sig_subpkt( sig, SIGSUBPKT_REVOCABLE, buf, 1 );
+
+ parse_revkeys(sig);
+
+ return 0;
+}
+
+int
+make_backsig(PKT_signature *sig,PKT_public_key *pk,
+ PKT_public_key *sub_pk,PKT_secret_key *sub_sk)
+{
+ PKT_signature *backsig;
+ int rc;
+
+ cache_public_key(sub_pk);
+
+ rc=make_keysig_packet(&backsig,pk,NULL,sub_pk,sub_sk,0x19,0,0,0,0,NULL,NULL);
+ if(rc)
+ log_error("make_keysig_packet failed for backsig: %s\n",g10_errstr(rc));
+ else
+ {
+ /* get it into a binary packed form. */
+ IOBUF backsig_out=iobuf_temp();
+ PACKET backsig_pkt;
+
+ init_packet(&backsig_pkt);
+ backsig_pkt.pkttype=PKT_SIGNATURE;
+ backsig_pkt.pkt.signature=backsig;
+ rc=build_packet(backsig_out,&backsig_pkt);
+ free_packet(&backsig_pkt);
+ if(rc)
+ log_error("build_packet failed for backsig: %s\n",g10_errstr(rc));
+ else
+ {
+ size_t pktlen=0;
+ byte *buf=iobuf_get_temp_buffer(backsig_out);
+
+ /* Remove the packet header */
+ if(buf[0]&0x40)
+ {
+ if(buf[1]<192)
+ {
+ pktlen=buf[1];
+ buf+=2;
+ }
+ else if(buf[1]<224)
+ {
+ pktlen=(buf[1]-192)*256;
+ pktlen+=buf[2]+192;
+ buf+=3;
+ }
+ else if(buf[1]==255)
+ {
+ pktlen =buf[2] << 24;
+ pktlen|=buf[3] << 16;
+ pktlen|=buf[4] << 8;
+ pktlen|=buf[5];
+ buf+=6;
+ }
+ else
+ BUG();
+ }
+ else
+ {
+ int mark=1;
+
+ switch(buf[0]&3)
+ {
+ case 3:
+ BUG();
+ break;
+
+ case 2:
+ pktlen =buf[mark++] << 24;
+ pktlen|=buf[mark++] << 16;
+
+ case 1:
+ pktlen|=buf[mark++] << 8;
+
+ case 0:
+ pktlen|=buf[mark++];
+ }
+
+ buf+=mark;
+ }
+
+ /* now make the binary blob into a subpacket */
+ build_sig_subpkt(sig,SIGSUBPKT_SIGNATURE,buf,pktlen);
+
+ iobuf_close(backsig_out);
+ }
+ }
+
+ return rc;
+}
+
+
+static int
+write_direct_sig( KBNODE root, KBNODE pub_root, PKT_secret_key *sk,
+ struct revocation_key *revkey )
+{
+ PACKET *pkt;
+ PKT_signature *sig;
+ int rc=0;
+ KBNODE node;
+ PKT_public_key *pk;
+
+ if( opt.verbose )
+ log_info(_("writing direct signature\n"));
+
+ /* get the pk packet from the pub_tree */
+ node = find_kbnode( pub_root, PKT_PUBLIC_KEY );
+ if( !node )
+ BUG();
+ pk = node->pkt->pkt.public_key;
+
+ /* we have to cache the key, so that the verification of the signature
+ * creation is able to retrieve the public key */
+ cache_public_key (pk);
+
+ /* and make the signature */
+ rc = make_keysig_packet(&sig,pk,NULL,NULL,sk,0x1F,0,0,0,0,
+ keygen_add_revkey,revkey);
+ if( rc ) {
+ log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) );
+ return rc;
+ }
+
+ pkt = xmalloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_SIGNATURE;
+ pkt->pkt.signature = sig;
+ add_kbnode( root, new_kbnode( pkt ) );
+ return rc;
+}
+
+static int
+write_selfsigs( KBNODE sec_root, KBNODE pub_root, PKT_secret_key *sk,
+ unsigned int use )
+{
+ PACKET *pkt;
+ PKT_signature *sig;
+ PKT_user_id *uid;
+ int rc=0;
+ KBNODE node;
+ PKT_public_key *pk;
+
+ if( opt.verbose )
+ log_info(_("writing self signature\n"));
+
+ /* get the uid packet from the list */
+ node = find_kbnode( pub_root, PKT_USER_ID );
+ if( !node )
+ BUG(); /* no user id packet in tree */
+ uid = node->pkt->pkt.user_id;
+ /* get the pk packet from the pub_tree */
+ node = find_kbnode( pub_root, PKT_PUBLIC_KEY );
+ if( !node )
+ BUG();
+ pk = node->pkt->pkt.public_key;
+ pk->pubkey_usage = use;
+ /* we have to cache the key, so that the verification of the signature
+ * creation is able to retrieve the public key */
+ cache_public_key (pk);
+
+ /* and make the signature */
+ rc = make_keysig_packet( &sig, pk, uid, NULL, sk, 0x13, 0, 0, 0, 0,
+ keygen_add_std_prefs, pk );
+ if( rc ) {
+ log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) );
+ return rc;
+ }
+
+ pkt = xmalloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_SIGNATURE;
+ pkt->pkt.signature = sig;
+ add_kbnode( sec_root, new_kbnode( pkt ) );
+
+ pkt = xmalloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_SIGNATURE;
+ pkt->pkt.signature = copy_signature(NULL,sig);
+ add_kbnode( pub_root, new_kbnode( pkt ) );
+ return rc;
+}
+
+static int
+write_keybinding( KBNODE root, KBNODE pub_root,
+ PKT_secret_key *pri_sk, PKT_secret_key *sub_sk,
+ unsigned int use )
+{
+ PACKET *pkt;
+ PKT_signature *sig;
+ int rc=0;
+ KBNODE node;
+ PKT_public_key *pri_pk, *sub_pk;
+ struct opaque_data_usage_and_pk oduap;
+
+ if( opt.verbose )
+ log_info(_("writing key binding signature\n"));
+
+ /* get the pk packet from the pub_tree */
+ node = find_kbnode( pub_root, PKT_PUBLIC_KEY );
+ if( !node )
+ BUG();
+ pri_pk = node->pkt->pkt.public_key;
+ /* we have to cache the key, so that the verification of the signature
+ * creation is able to retrieve the public key */
+ cache_public_key (pri_pk);
+
+ /* find the last subkey */
+ sub_pk = NULL;
+ for(node=pub_root; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+ sub_pk = node->pkt->pkt.public_key;
+ }
+ if( !sub_pk )
+ BUG();
+
+ /* and make the signature */
+ oduap.usage = use;
+ oduap.pk = sub_pk;
+ rc=make_keysig_packet(&sig, pri_pk, NULL, sub_pk, pri_sk, 0x18, 0, 0, 0, 0,
+ keygen_add_key_flags_and_expire, &oduap );
+ if( rc ) {
+ log_error("make_keysig_packet failed: %s\n", g10_errstr(rc) );
+ return rc;
+ }
+
+ /* make a backsig */
+ if(use&PUBKEY_USAGE_SIG)
+ {
+ rc=make_backsig(sig,pri_pk,sub_pk,sub_sk);
+ if(rc)
+ return rc;
+ }
+
+ pkt = xmalloc_clear( sizeof *pkt );
+ pkt->pkttype = PKT_SIGNATURE;
+ pkt->pkt.signature = sig;
+ add_kbnode( root, new_kbnode( pkt ) );
+ return rc;
+}
+
+
+static int
+gen_elg(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
+ STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 expireval, int is_subkey)
+{
+ int rc;
+ PACKET *pkt;
+ PKT_secret_key *sk;
+ PKT_public_key *pk;
+ MPI skey[4];
+ MPI *factors;
+
+ assert( is_ELGAMAL(algo) );
+
+ if( nbits < 512 ) {
+ nbits = 1024;
+ log_info(_("keysize invalid; using %u bits\n"), nbits );
+ }
+
+ if( (nbits % 32) ) {
+ nbits = ((nbits + 31) / 32) * 32;
+ log_info(_("keysize rounded up to %u bits\n"), nbits );
+ }
+
+ rc = pubkey_generate( algo, nbits, skey, &factors );
+ if( rc ) {
+ log_error("pubkey_generate failed: %s\n", g10_errstr(rc) );
+ return rc;
+ }
+
+ sk = xmalloc_clear( sizeof *sk );
+ pk = xmalloc_clear( sizeof *pk );
+ sk->timestamp = pk->timestamp = make_timestamp();
+ sk->version = pk->version = 4;
+ if( expireval ) {
+ sk->expiredate = pk->expiredate = sk->timestamp + expireval;
+ }
+ sk->pubkey_algo = pk->pubkey_algo = algo;
+ pk->pkey[0] = mpi_copy( skey[0] );
+ pk->pkey[1] = mpi_copy( skey[1] );
+ pk->pkey[2] = mpi_copy( skey[2] );
+ sk->skey[0] = skey[0];
+ sk->skey[1] = skey[1];
+ sk->skey[2] = skey[2];
+ sk->skey[3] = skey[3];
+ sk->is_protected = 0;
+ sk->protect.algo = 0;
+
+ sk->csum = checksum_mpi( sk->skey[3] );
+ if( ret_sk ) /* return an unprotected version of the sk */
+ *ret_sk = copy_secret_key( NULL, sk );
+
+ if( dek ) {
+ sk->protect.algo = dek->algo;
+ sk->protect.s2k = *s2k;
+ rc = protect_secret_key( sk, dek );
+ if( rc ) {
+ log_error("protect_secret_key failed: %s\n", g10_errstr(rc) );
+ free_public_key(pk);
+ free_secret_key(sk);
+ return rc;
+ }
+ }
+
+ pkt = xmalloc_clear(sizeof *pkt);
+ pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY;
+ pkt->pkt.public_key = pk;
+ add_kbnode(pub_root, new_kbnode( pkt ));
+
+ /* don't know whether it makes sense to have the factors, so for now
+ * we store them in the secret keyring (but they are not secret) */
+ pkt = xmalloc_clear(sizeof *pkt);
+ pkt->pkttype = is_subkey ? PKT_SECRET_SUBKEY : PKT_SECRET_KEY;
+ pkt->pkt.secret_key = sk;
+ add_kbnode(sec_root, new_kbnode( pkt ));
+
+ return 0;
+}
+
+
+/****************
+ * Generate a DSA key
+ */
+static int
+gen_dsa(unsigned int nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
+ STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 expireval, int is_subkey)
+{
+ int rc;
+ PACKET *pkt;
+ PKT_secret_key *sk;
+ PKT_public_key *pk;
+ MPI skey[5];
+ MPI *factors;
+ unsigned int qbits;
+
+ if( nbits < 512 || (!opt.flags.dsa2 && nbits > 1024))
+ {
+ nbits = 1024;
+ log_info(_("keysize invalid; using %u bits\n"), nbits );
+ }
+ else if(nbits>3072)
+ {
+ nbits = 3072;
+ log_info(_("keysize invalid; using %u bits\n"), nbits );
+ }
+
+ if(nbits % 64)
+ {
+ nbits = ((nbits + 63) / 64) * 64;
+ log_info(_("keysize rounded up to %u bits\n"), nbits );
+ }
+
+ /*
+ Figure out a q size based on the key size. FIPS 180-3 says:
+
+ L = 1024, N = 160
+ L = 2048, N = 224
+ L = 2048, N = 256
+ L = 3072, N = 256
+
+ 2048/256 is an odd pair since there is also a 2048/224 and
+ 3072/256. Matching sizes is not a very exact science.
+
+ We'll do 256 qbits for nbits over 2048, 224 for nbits over 1024
+ but less than 2048, and 160 for 1024 (DSA1).
+ */
+
+ if(nbits>2048)
+ qbits=256;
+ else if(nbits>1024)
+ qbits=224;
+ else
+ qbits=160;
+
+ if(qbits!=160)
+ log_info("WARNING: some OpenPGP programs can't"
+ " handle a DSA key with this digest size\n");
+
+ rc = dsa2_generate( PUBKEY_ALGO_DSA, nbits, qbits, skey, &factors );
+ if( rc )
+ {
+ log_error("dsa2_generate failed: %s\n", g10_errstr(rc) );
+ return rc;
+ }
+
+ sk = xmalloc_clear( sizeof *sk );
+ pk = xmalloc_clear( sizeof *pk );
+ sk->timestamp = pk->timestamp = make_timestamp();
+ sk->version = pk->version = 4;
+ if( expireval )
+ sk->expiredate = pk->expiredate = sk->timestamp + expireval;
+
+ sk->pubkey_algo = pk->pubkey_algo = PUBKEY_ALGO_DSA;
+ pk->pkey[0] = mpi_copy( skey[0] );
+ pk->pkey[1] = mpi_copy( skey[1] );
+ pk->pkey[2] = mpi_copy( skey[2] );
+ pk->pkey[3] = mpi_copy( skey[3] );
+ sk->skey[0] = skey[0];
+ sk->skey[1] = skey[1];
+ sk->skey[2] = skey[2];
+ sk->skey[3] = skey[3];
+ sk->skey[4] = skey[4];
+ sk->is_protected = 0;
+ sk->protect.algo = 0;
+
+ sk->csum = checksum_mpi ( sk->skey[4] );
+ if( ret_sk ) /* return an unprotected version of the sk */
+ *ret_sk = copy_secret_key( NULL, sk );
+
+ if( dek ) {
+ sk->protect.algo = dek->algo;
+ sk->protect.s2k = *s2k;
+ rc = protect_secret_key( sk, dek );
+ if( rc ) {
+ log_error("protect_secret_key failed: %s\n", g10_errstr(rc) );
+ free_public_key(pk);
+ free_secret_key(sk);
+ return rc;
+ }
+ }
+
+ pkt = xmalloc_clear(sizeof *pkt);
+ pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY;
+ pkt->pkt.public_key = pk;
+ add_kbnode(pub_root, new_kbnode( pkt ));
+
+ /* don't know whether it makes sense to have the factors, so for now
+ * we store them in the secret keyring (but they are not secret)
+ * p = 2 * q * f1 * f2 * ... * fn
+ * We store only f1 to f_n-1; fn can be calculated because p and q
+ * are known.
+ */
+ pkt = xmalloc_clear(sizeof *pkt);
+ pkt->pkttype = is_subkey ? PKT_SECRET_SUBKEY : PKT_SECRET_KEY;
+ pkt->pkt.secret_key = sk;
+ add_kbnode(sec_root, new_kbnode( pkt ));
+
+ return 0;
+}
+
+
+/*
+ * Generate an RSA key.
+ */
+static int
+gen_rsa(int algo, unsigned nbits, KBNODE pub_root, KBNODE sec_root, DEK *dek,
+ STRING2KEY *s2k, PKT_secret_key **ret_sk, u32 expireval, int is_subkey)
+{
+ int rc;
+ PACKET *pkt;
+ PKT_secret_key *sk;
+ PKT_public_key *pk;
+ MPI skey[6];
+ MPI *factors;
+
+ assert( is_RSA(algo) );
+
+ if( nbits < 1024 ) {
+ nbits = 1024;
+ log_info(_("keysize invalid; using %u bits\n"), nbits );
+ }
+
+ if( (nbits % 32) ) {
+ nbits = ((nbits + 31) / 32) * 32;
+ log_info(_("keysize rounded up to %u bits\n"), nbits );
+ }
+
+ rc = pubkey_generate( algo, nbits, skey, &factors );
+ if( rc ) {
+ log_error("pubkey_generate failed: %s\n", g10_errstr(rc) );
+ return rc;
+ }
+
+ sk = xmalloc_clear( sizeof *sk );
+ pk = xmalloc_clear( sizeof *pk );
+ sk->timestamp = pk->timestamp = make_timestamp();
+ sk->version = pk->version = 4;
+ if( expireval ) {
+ sk->expiredate = pk->expiredate = sk->timestamp + expireval;
+ }
+ sk->pubkey_algo = pk->pubkey_algo = algo;
+ pk->pkey[0] = mpi_copy( skey[0] );
+ pk->pkey[1] = mpi_copy( skey[1] );
+ sk->skey[0] = skey[0];
+ sk->skey[1] = skey[1];
+ sk->skey[2] = skey[2];
+ sk->skey[3] = skey[3];
+ sk->skey[4] = skey[4];
+ sk->skey[5] = skey[5];
+ sk->is_protected = 0;
+ sk->protect.algo = 0;
+
+ sk->csum = checksum_mpi (sk->skey[2] );
+ sk->csum += checksum_mpi (sk->skey[3] );
+ sk->csum += checksum_mpi (sk->skey[4] );
+ sk->csum += checksum_mpi (sk->skey[5] );
+ if( ret_sk ) /* return an unprotected version of the sk */
+ *ret_sk = copy_secret_key( NULL, sk );
+
+ if( dek ) {
+ sk->protect.algo = dek->algo;
+ sk->protect.s2k = *s2k;
+ rc = protect_secret_key( sk, dek );
+ if( rc ) {
+ log_error("protect_secret_key failed: %s\n", g10_errstr(rc) );
+ free_public_key(pk);
+ free_secret_key(sk);
+ return rc;
+ }
+ }
+
+ pkt = xmalloc_clear(sizeof *pkt);
+ pkt->pkttype = is_subkey ? PKT_PUBLIC_SUBKEY : PKT_PUBLIC_KEY;
+ pkt->pkt.public_key = pk;
+ add_kbnode(pub_root, new_kbnode( pkt ));
+
+ pkt = xmalloc_clear(sizeof *pkt);
+ pkt->pkttype = is_subkey ? PKT_SECRET_SUBKEY : PKT_SECRET_KEY;
+ pkt->pkt.secret_key = sk;
+ add_kbnode(sec_root, new_kbnode( pkt ));
+
+ return 0;
+}
+
+
+/****************
+ * check valid days:
+ * return 0 on error or the multiplier
+ */
+static int
+check_valid_days( const char *s )
+{
+ if( !digitp(s) )
+ return 0;
+ for( s++; *s; s++)
+ if( !digitp(s) )
+ break;
+ if( !*s )
+ return 1;
+ if( s[1] )
+ return 0; /* e.g. "2323wc" */
+ if( *s == 'd' || *s == 'D' )
+ return 1;
+ if( *s == 'w' || *s == 'W' )
+ return 7;
+ if( *s == 'm' || *s == 'M' )
+ return 30;
+ if( *s == 'y' || *s == 'Y' )
+ return 365;
+ return 0;
+}
+
+
+static void
+print_key_flags(int flags)
+{
+ if(flags&PUBKEY_USAGE_SIG)
+ tty_printf("%s ",_("Sign"));
+
+ if(flags&PUBKEY_USAGE_CERT)
+ tty_printf("%s ",_("Certify"));
+
+ if(flags&PUBKEY_USAGE_ENC)
+ tty_printf("%s ",_("Encrypt"));
+
+ if(flags&PUBKEY_USAGE_AUTH)
+ tty_printf("%s ",_("Authenticate"));
+}
+
+
+/* Returns the key flags */
+static unsigned int
+ask_key_flags(int algo,int subkey)
+{
+ const char *togglers=_("SsEeAaQq");
+ char *answer=NULL;
+ unsigned int current=0;
+ unsigned int possible=openpgp_pk_algo_usage(algo);
+
+ if(strlen(togglers)!=8)
+ BUG();
+
+ /* Only primary keys may certify. */
+ if(subkey)
+ possible&=~PUBKEY_USAGE_CERT;
+
+ /* Preload the current set with the possible set, minus
+ authentication, since nobody really uses auth yet. */
+ current=possible&~PUBKEY_USAGE_AUTH;
+
+ for(;;)
+ {
+ tty_printf("\n");
+ tty_printf(_("Possible actions for a %s key: "),
+ pubkey_algo_to_string(algo));
+ print_key_flags(possible);
+ tty_printf("\n");
+ tty_printf(_("Current allowed actions: "));
+ print_key_flags(current);
+ tty_printf("\n\n");
+
+ if(possible&PUBKEY_USAGE_SIG)
+ tty_printf(_(" (%c) Toggle the sign capability\n"),
+ togglers[0]);
+ if(possible&PUBKEY_USAGE_ENC)
+ tty_printf(_(" (%c) Toggle the encrypt capability\n"),
+ togglers[2]);
+ if(possible&PUBKEY_USAGE_AUTH)
+ tty_printf(_(" (%c) Toggle the authenticate capability\n"),
+ togglers[4]);
+
+ tty_printf(_(" (%c) Finished\n"),togglers[6]);
+ tty_printf("\n");
+
+ xfree(answer);
+ answer = cpr_get("keygen.flags",_("Your selection? "));
+ cpr_kill_prompt();
+
+ if(strlen(answer)>1)
+ tty_printf(_("Invalid selection.\n"));
+ else if(*answer=='\0' || *answer==togglers[6] || *answer==togglers[7])
+ break;
+ else if((*answer==togglers[0] || *answer==togglers[1])
+ && possible&PUBKEY_USAGE_SIG)
+ {
+ if(current&PUBKEY_USAGE_SIG)
+ current&=~PUBKEY_USAGE_SIG;
+ else
+ current|=PUBKEY_USAGE_SIG;
+ }
+ else if((*answer==togglers[2] || *answer==togglers[3])
+ && possible&PUBKEY_USAGE_ENC)
+ {
+ if(current&PUBKEY_USAGE_ENC)
+ current&=~PUBKEY_USAGE_ENC;
+ else
+ current|=PUBKEY_USAGE_ENC;
+ }
+ else if((*answer==togglers[4] || *answer==togglers[5])
+ && possible&PUBKEY_USAGE_AUTH)
+ {
+ if(current&PUBKEY_USAGE_AUTH)
+ current&=~PUBKEY_USAGE_AUTH;
+ else
+ current|=PUBKEY_USAGE_AUTH;
+ }
+ else
+ tty_printf(_("Invalid selection.\n"));
+ }
+
+ xfree(answer);
+
+ return current;
+}
+
+
+/****************
+ * Returns: 0 to create both a DSA and a Elgamal key.
+ * and only if key flags are to be written the desired usage.
+ */
+static int
+ask_algo (int addmode, unsigned int *r_usage)
+{
+ char *answer;
+ int algo;
+
+ *r_usage = 0;
+ tty_printf(_("Please select what kind of key you want:\n"));
+ if( !addmode )
+ tty_printf(_(" (%d) DSA and Elgamal (default)\n"), 1 );
+ tty_printf( _(" (%d) DSA (sign only)\n"), 2 );
+ if (opt.expert)
+ tty_printf( _(" (%d) DSA (set your own capabilities)\n"), 3 );
+ if( addmode )
+ tty_printf(_(" (%d) Elgamal (encrypt only)\n"), 4 );
+ tty_printf( _(" (%d) RSA (sign only)\n"), 5 );
+ if (addmode)
+ tty_printf(_(" (%d) RSA (encrypt only)\n"), 6 );
+ if (opt.expert)
+ tty_printf( _(" (%d) RSA (set your own capabilities)\n"), 7 );
+
+ for(;;) {
+ answer = cpr_get("keygen.algo",_("Your selection? "));
+ cpr_kill_prompt();
+ algo = *answer? atoi(answer): 1;
+ xfree(answer);
+ if( algo == 1 && !addmode ) {
+ algo = 0; /* create both keys */
+ break;
+ }
+ else if( algo == 7 && opt.expert ) {
+ algo = PUBKEY_ALGO_RSA;
+ *r_usage=ask_key_flags(algo,addmode);
+ break;
+ }
+ else if( algo == 6 && addmode ) {
+ algo = PUBKEY_ALGO_RSA;
+ *r_usage = PUBKEY_USAGE_ENC;
+ break;
+ }
+ else if( algo == 5 ) {
+ algo = PUBKEY_ALGO_RSA;
+ *r_usage = PUBKEY_USAGE_SIG;
+ break;
+ }
+ else if( algo == 4 && addmode ) {
+ algo = PUBKEY_ALGO_ELGAMAL_E;
+ *r_usage = PUBKEY_USAGE_ENC;
+ break;
+ }
+ else if( algo == 3 && opt.expert ) {
+ algo = PUBKEY_ALGO_DSA;
+ *r_usage=ask_key_flags(algo,addmode);
+ break;
+ }
+ else if( algo == 2 ) {
+ algo = PUBKEY_ALGO_DSA;
+ *r_usage = PUBKEY_USAGE_SIG;
+ break;
+ }
+ else
+ tty_printf(_("Invalid selection.\n"));
+ }
+
+ return algo;
+}
+
+
+static unsigned
+ask_keysize( int algo )
+{
+ unsigned nbits,min,def=2048,max=4096;
+
+ if(opt.expert)
+ min=512;
+ else
+ min=1024;
+
+ switch(algo)
+ {
+ case PUBKEY_ALGO_DSA:
+ if(opt.flags.dsa2)
+ {
+ def=1024;
+ max=3072;
+ }
+ else
+ {
+ tty_printf(_("DSA keypair will have %u bits.\n"),1024);
+ return 1024;
+ }
+ break;
+
+ case PUBKEY_ALGO_RSA:
+ min=1024;
+ break;
+ }
+
+ tty_printf(_("%s keys may be between %u and %u bits long.\n"),
+ pubkey_algo_to_string(algo),min,max);
+
+ for(;;)
+ {
+ char *prompt,*answer;
+
+#define PROMPTSTRING _("What keysize do you want? (%u) ")
+
+ prompt=xmalloc(strlen(PROMPTSTRING)+20);
+ sprintf(prompt,PROMPTSTRING,def);
+
+#undef PROMPTSTRING
+
+ answer = cpr_get("keygen.size",prompt);
+ cpr_kill_prompt();
+ nbits = *answer? atoi(answer): def;
+ xfree(prompt);
+ xfree(answer);
+
+ if(nbits<min || nbits>max)
+ tty_printf(_("%s keysizes must be in the range %u-%u\n"),
+ pubkey_algo_to_string(algo),min,max);
+ else
+ break;
+ }
+
+ tty_printf(_("Requested keysize is %u bits\n"), nbits );
+
+ if( algo == PUBKEY_ALGO_DSA && (nbits % 64) )
+ {
+ nbits = ((nbits + 63) / 64) * 64;
+ tty_printf(_("rounded up to %u bits\n"), nbits );
+ }
+ else if( (nbits % 32) )
+ {
+ nbits = ((nbits + 31) / 32) * 32;
+ tty_printf(_("rounded up to %u bits\n"), nbits );
+ }
+
+ return nbits;
+}
+
+
+/****************
+ * Parse an expire string and return its value in seconds.
+ * Returns (u32)-1 on error.
+ * This isn't perfect since scan_isodatestr returns unix time, and
+ * OpenPGP actually allows a 32-bit time *plus* a 32-bit offset.
+ * Because of this, we only permit setting expirations up to 2106, but
+ * OpenPGP could theoretically allow up to 2242. I think we'll all
+ * just cope for the next few years until we get a 64-bit time_t or
+ * similar.
+ */
+u32
+parse_expire_string( const char *string )
+{
+ int mult;
+ u32 seconds,abs_date=0,curtime = make_timestamp();
+
+ if( !*string )
+ seconds = 0;
+ else if ( !strncmp (string, "seconds=", 8) )
+ seconds = atoi (string+8);
+ else if( (abs_date = scan_isodatestr(string)) && abs_date > curtime )
+ seconds = abs_date - curtime;
+ else if( (mult=check_valid_days(string)) )
+ seconds = atoi(string) * 86400L * mult;
+ else
+ seconds=(u32)-1;
+
+ return seconds;
+}
+
+/* object == 0 for a key, and 1 for a sig */
+u32
+ask_expire_interval(int object,const char *def_expire)
+{
+ u32 interval;
+ char *answer;
+
+ switch(object)
+ {
+ case 0:
+ if(def_expire)
+ BUG();
+ tty_printf(_("Please specify how long the key should be valid.\n"
+ " 0 = key does not expire\n"
+ " <n> = key expires in n days\n"
+ " <n>w = key expires in n weeks\n"
+ " <n>m = key expires in n months\n"
+ " <n>y = key expires in n years\n"));
+ break;
+
+ case 1:
+ if(!def_expire)
+ BUG();
+ tty_printf(_("Please specify how long the signature should be valid.\n"
+ " 0 = signature does not expire\n"
+ " <n> = signature expires in n days\n"
+ " <n>w = signature expires in n weeks\n"
+ " <n>m = signature expires in n months\n"
+ " <n>y = signature expires in n years\n"));
+ break;
+
+ default:
+ BUG();
+ }
+
+ /* Note: The elgamal subkey for DSA has no expiration date because
+ * it must be signed with the DSA key and this one has the expiration
+ * date */
+
+ answer = NULL;
+ for(;;)
+ {
+ u32 curtime=make_timestamp();
+
+ xfree(answer);
+ if(object==0)
+ answer = cpr_get("keygen.valid",_("Key is valid for? (0) "));
+ else
+ {
+ char *prompt;
+
+#define PROMPTSTRING _("Signature is valid for? (%s) ")
+ /* This will actually end up larger than necessary because
+ of the 2 bytes for '%s' */
+ prompt=xmalloc(strlen(PROMPTSTRING)+strlen(def_expire)+1);
+ sprintf(prompt,PROMPTSTRING,def_expire);
+#undef PROMPTSTRING
+
+ answer = cpr_get("siggen.valid",prompt);
+ xfree(prompt);
+
+ if(*answer=='\0')
+ answer=xstrdup(def_expire);
+ }
+ cpr_kill_prompt();
+ trim_spaces(answer);
+ interval = parse_expire_string( answer );
+ if( interval == (u32)-1 )
+ {
+ tty_printf(_("invalid value\n"));
+ continue;
+ }
+
+ if( !interval )
+ {
+ tty_printf((object==0)
+ ? _("Key does not expire at all\n")
+ : _("Signature does not expire at all\n"));
+ }
+ else
+ {
+ tty_printf(object==0
+ ? _("Key expires at %s\n")
+ : _("Signature expires at %s\n"),
+ asctimestamp((ulong)(curtime + interval) ) );
+ /* FIXME: This check yields warning on alhas: Write a
+ configure check and to this check here only for 32 bit
+ machines */
+ if( (time_t)((ulong)(curtime+interval)) < 0 )
+ tty_printf(_("Your system can't display dates beyond 2038.\n"
+ "However, it will be correctly handled up to 2106.\n"));
+ }
+
+ if( cpr_enabled() || cpr_get_answer_is_yes("keygen.valid.okay",
+ _("Is this correct? (y/N) ")) )
+ break;
+ }
+
+ xfree(answer);
+ return interval;
+}
+
+u32
+ask_expiredate()
+{
+ u32 x = ask_expire_interval(0,NULL);
+ return x? make_timestamp() + x : 0;
+}
+
+
+static char *
+ask_user_id( int mode )
+{
+ char *answer;
+ char *aname, *acomment, *amail, *uid;
+
+ if( !mode )
+ tty_printf( _("\n"
+"You need a user ID to identify your key; "
+ "the software constructs the user ID\n"
+"from the Real Name, Comment and Email Address in this form:\n"
+" \"Heinrich Heine (Der Dichter) <heinrichh@duesseldorf.de>\"\n\n") );
+ uid = aname = acomment = amail = NULL;
+ for(;;) {
+ char *p;
+ int fail=0;
+
+ if( !aname ) {
+ for(;;) {
+ xfree(aname);
+ aname = cpr_get("keygen.name",_("Real name: "));
+ trim_spaces(aname);
+ cpr_kill_prompt();
+
+ if( opt.allow_freeform_uid )
+ break;
+
+ if( strpbrk( aname, "<>" ) )
+ tty_printf(_("Invalid character in name\n"));
+ else if( digitp(aname) )
+ tty_printf(_("Name may not start with a digit\n"));
+ else if( strlen(aname) < 5 )
+ tty_printf(_("Name must be at least 5 characters long\n"));
+ else
+ break;
+ }
+ }
+ if( !amail ) {
+ for(;;) {
+ xfree(amail);
+ amail = cpr_get("keygen.email",_("Email address: "));
+ trim_spaces(amail);
+ cpr_kill_prompt();
+ if( !*amail || opt.allow_freeform_uid )
+ break; /* no email address is okay */
+ else if ( !is_valid_mailbox (amail) )
+ tty_printf(_("Not a valid email address\n"));
+ else
+ break;
+ }
+ }
+ if( !acomment ) {
+ for(;;) {
+ xfree(acomment);
+ acomment = cpr_get("keygen.comment",_("Comment: "));
+ trim_spaces(acomment);
+ cpr_kill_prompt();
+ if( !*acomment )
+ break; /* no comment is okay */
+ else if( strpbrk( acomment, "()" ) )
+ tty_printf(_("Invalid character in comment\n"));
+ else
+ break;
+ }
+ }
+
+
+ xfree(uid);
+ uid = p = xmalloc(strlen(aname)+strlen(amail)+strlen(acomment)+12+10);
+ p = stpcpy(p, aname );
+ if( *acomment )
+ p = stpcpy(stpcpy(stpcpy(p," ("), acomment),")");
+ if( *amail )
+ p = stpcpy(stpcpy(stpcpy(p," <"), amail),">");
+
+ /* append a warning if we do not have dev/random
+ * or it is switched into quick testmode */
+ if( quick_random_gen(-1) )
+ strcpy(p, " (INSECURE!)" );
+
+ /* print a note in case that UTF8 mapping has to be done */
+ for(p=uid; *p; p++ ) {
+ if( *p & 0x80 ) {
+ tty_printf(_("You are using the `%s' character set.\n"),
+ get_native_charset() );
+ break;
+ }
+ }
+
+ tty_printf(_("You selected this USER-ID:\n \"%s\"\n\n"), uid);
+ /* fixme: add a warning if this user-id already exists */
+ if( !*amail && !opt.allow_freeform_uid
+ && (strchr( aname, '@' ) || strchr( acomment, '@'))) {
+ fail = 1;
+ tty_printf(_("Please don't put the email address "
+ "into the real name or the comment\n") );
+ }
+
+ for(;;) {
+ /* TRANSLATORS: These are the allowed answers in
+ lower and uppercase. Below you will find the matching
+ string which should be translated accordingly and the
+ letter changed to match the one in the answer string.
+
+ n = Change name
+ c = Change comment
+ e = Change email
+ o = Okay (ready, continue)
+ q = Quit
+ */
+ const char *ansstr = _("NnCcEeOoQq");
+
+ if( strlen(ansstr) != 10 )
+ BUG();
+ if( cpr_enabled() ) {
+ answer = xstrdup(ansstr+6);
+ answer[1] = 0;
+ }
+ else {
+ answer = cpr_get("keygen.userid.cmd", fail?
+ _("Change (N)ame, (C)omment, (E)mail or (Q)uit? ") :
+ _("Change (N)ame, (C)omment, (E)mail or (O)kay/(Q)uit? "));
+ cpr_kill_prompt();
+ }
+ if( strlen(answer) > 1 )
+ ;
+ else if( *answer == ansstr[0] || *answer == ansstr[1] ) {
+ xfree(aname); aname = NULL;
+ break;
+ }
+ else if( *answer == ansstr[2] || *answer == ansstr[3] ) {
+ xfree(acomment); acomment = NULL;
+ break;
+ }
+ else if( *answer == ansstr[4] || *answer == ansstr[5] ) {
+ xfree(amail); amail = NULL;
+ break;
+ }
+ else if( *answer == ansstr[6] || *answer == ansstr[7] ) {
+ if( fail ) {
+ tty_printf(_("Please correct the error first\n"));
+ }
+ else {
+ xfree(aname); aname = NULL;
+ xfree(acomment); acomment = NULL;
+ xfree(amail); amail = NULL;
+ break;
+ }
+ }
+ else if( *answer == ansstr[8] || *answer == ansstr[9] ) {
+ xfree(aname); aname = NULL;
+ xfree(acomment); acomment = NULL;
+ xfree(amail); amail = NULL;
+ xfree(uid); uid = NULL;
+ break;
+ }
+ xfree(answer);
+ }
+ xfree(answer);
+ if( !amail && !acomment && !amail )
+ break;
+ xfree(uid); uid = NULL;
+ }
+ if( uid ) {
+ char *p = native_to_utf8( uid );
+ xfree( uid );
+ uid = p;
+ }
+ return uid;
+}
+
+
+/* FIXME: We need a way to cancel this prompt. */
+static DEK *
+do_ask_passphrase( STRING2KEY **ret_s2k )
+{
+ DEK *dek = NULL;
+ STRING2KEY *s2k;
+ const char *errtext = NULL;
+
+ tty_printf(_("You need a Passphrase to protect your secret key.\n\n") );
+
+ s2k = xmalloc_secure( sizeof *s2k );
+ for(;;) {
+ s2k->mode = opt.s2k_mode;
+ s2k->hash_algo = S2K_DIGEST_ALGO;
+ dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k,2,
+ errtext, NULL);
+ if( !dek ) {
+ errtext = N_("passphrase not correctly repeated; try again");
+ tty_printf(_("%s.\n"), _(errtext));
+ }
+ else if( !dek->keylen ) {
+ xfree(dek); dek = NULL;
+ xfree(s2k); s2k = NULL;
+ tty_printf(_(
+ "You don't want a passphrase - this is probably a *bad* idea!\n"
+ "I will do it anyway. You can change your passphrase at any time,\n"
+ "using this program with the option \"--edit-key\".\n\n"));
+ break;
+ }
+ else
+ break; /* okay */
+ }
+ *ret_s2k = s2k;
+ return dek;
+}
+
+
+static int
+do_create( int algo, unsigned int nbits, KBNODE pub_root, KBNODE sec_root,
+ DEK *dek, STRING2KEY *s2k, PKT_secret_key **sk, u32 expiredate,
+ int is_subkey )
+{
+ int rc=0;
+
+ if( !opt.batch )
+ tty_printf(_(
+"We need to generate a lot of random bytes. It is a good idea to perform\n"
+"some other action (type on the keyboard, move the mouse, utilize the\n"
+"disks) during the prime generation; this gives the random number\n"
+"generator a better chance to gain enough entropy.\n") );
+
+ if( algo == PUBKEY_ALGO_ELGAMAL_E )
+ rc = gen_elg(algo, nbits, pub_root, sec_root, dek, s2k, sk, expiredate,
+ is_subkey);
+ else if( algo == PUBKEY_ALGO_DSA )
+ rc = gen_dsa(nbits, pub_root, sec_root, dek, s2k, sk, expiredate,
+ is_subkey);
+ else if( algo == PUBKEY_ALGO_RSA )
+ rc = gen_rsa(algo, nbits, pub_root, sec_root, dek, s2k, sk, expiredate,
+ is_subkey);
+ else
+ BUG();
+
+ return rc;
+}
+
+
+/****************
+ * Generate a new user id packet, or return NULL if canceled
+ */
+PKT_user_id *
+generate_user_id()
+{
+ PKT_user_id *uid;
+ char *p;
+ size_t n;
+
+ p = ask_user_id( 1 );
+ if( !p )
+ return NULL;
+ n = strlen(p);
+ uid = xmalloc_clear( sizeof *uid + n - 1 );
+ uid->len = n;
+ strcpy(uid->name, p);
+ uid->ref = 1;
+ return uid;
+}
+
+
+static void
+release_parameter_list( struct para_data_s *r )
+{
+ struct para_data_s *r2;
+
+ for( ; r ; r = r2 ) {
+ r2 = r->next;
+ if( r->key == pPASSPHRASE_DEK )
+ xfree( r->u.dek );
+ else if( r->key == pPASSPHRASE_S2K )
+ xfree( r->u.s2k );
+
+ xfree(r);
+ }
+}
+
+static struct para_data_s *
+get_parameter( struct para_data_s *para, enum para_name key )
+{
+ struct para_data_s *r;
+
+ for( r = para; r && r->key != key; r = r->next )
+ ;
+ return r;
+}
+
+static const char *
+get_parameter_value( struct para_data_s *para, enum para_name key )
+{
+ struct para_data_s *r = get_parameter( para, key );
+ return (r && *r->u.value)? r->u.value : NULL;
+}
+
+static int
+get_parameter_algo( struct para_data_s *para, enum para_name key )
+{
+ int i;
+ struct para_data_s *r = get_parameter( para, key );
+ if( !r )
+ return -1;
+ if( digitp( r->u.value ) )
+ i = atoi( r->u.value );
+ else
+ i = string_to_pubkey_algo( r->u.value );
+ if (i == PUBKEY_ALGO_RSA_E || i == PUBKEY_ALGO_RSA_S)
+ i = 0; /* we don't want to allow generation of these algorithms */
+ return i;
+}
+
+/*
+ * parse the usage parameter and set the keyflags. Return true on error.
+ */
+static int
+parse_parameter_usage (const char *fname,
+ struct para_data_s *para, enum para_name key)
+{
+ struct para_data_s *r = get_parameter( para, key );
+ char *p, *pn;
+ unsigned int use;
+
+ if( !r )
+ return 0; /* none (this is an optional parameter)*/
+
+ use = 0;
+ pn = r->u.value;
+ while ( (p = strsep (&pn, " \t,")) ) {
+ if ( !*p)
+ ;
+ else if ( !ascii_strcasecmp (p, "sign") )
+ use |= PUBKEY_USAGE_SIG;
+ else if ( !ascii_strcasecmp (p, "encrypt") )
+ use |= PUBKEY_USAGE_ENC;
+ else if ( !ascii_strcasecmp (p, "auth") )
+ use |= PUBKEY_USAGE_AUTH;
+ else {
+ log_error("%s:%d: invalid usage list\n", fname, r->lnr );
+ return -1; /* error */
+ }
+ }
+ r->u.usage = use;
+ return 1;
+}
+
+static int
+parse_revocation_key (const char *fname,
+ struct para_data_s *para, enum para_name key)
+{
+ struct para_data_s *r = get_parameter( para, key );
+ struct revocation_key revkey;
+ char *pn;
+ int i;
+
+ if( !r )
+ return 0; /* none (this is an optional parameter) */
+
+ pn = r->u.value;
+
+ revkey.class=0x80;
+ revkey.algid=atoi(pn);
+ if(!revkey.algid)
+ goto fail;
+
+ /* Skip to the fpr */
+ while(*pn && *pn!=':')
+ pn++;
+
+ if(*pn!=':')
+ goto fail;
+
+ pn++;
+
+ for(i=0;i<MAX_FINGERPRINT_LEN && *pn;i++,pn+=2)
+ {
+ int c=hextobyte(pn);
+ if(c==-1)
+ goto fail;
+
+ revkey.fpr[i]=c;
+ }
+
+ /* skip to the tag */
+ while(*pn && *pn!='s' && *pn!='S')
+ pn++;
+
+ if(ascii_strcasecmp(pn,"sensitive")==0)
+ revkey.class|=0x40;
+
+ memcpy(&r->u.revkey,&revkey,sizeof(struct revocation_key));
+
+ return 0;
+
+ fail:
+ log_error("%s:%d: invalid revocation key\n", fname, r->lnr );
+ return -1; /* error */
+}
+
+
+static u32
+get_parameter_u32( struct para_data_s *para, enum para_name key )
+{
+ struct para_data_s *r = get_parameter( para, key );
+
+ if( !r )
+ return 0;
+ if( r->key == pKEYEXPIRE || r->key == pSUBKEYEXPIRE )
+ return r->u.expire;
+ if( r->key == pKEYUSAGE || r->key == pSUBKEYUSAGE )
+ return r->u.usage;
+
+ return (unsigned int)strtoul( r->u.value, NULL, 10 );
+}
+
+static unsigned int
+get_parameter_uint( struct para_data_s *para, enum para_name key )
+{
+ return get_parameter_u32( para, key );
+}
+
+static DEK *
+get_parameter_dek( struct para_data_s *para, enum para_name key )
+{
+ struct para_data_s *r = get_parameter( para, key );
+ return r? r->u.dek : NULL;
+}
+
+static STRING2KEY *
+get_parameter_s2k( struct para_data_s *para, enum para_name key )
+{
+ struct para_data_s *r = get_parameter( para, key );
+ return r? r->u.s2k : NULL;
+}
+
+static struct revocation_key *
+get_parameter_revkey( struct para_data_s *para, enum para_name key )
+{
+ struct para_data_s *r = get_parameter( para, key );
+ return r? &r->u.revkey : NULL;
+}
+
+static int
+proc_parameter_file( struct para_data_s *para, const char *fname,
+ struct output_control_s *outctrl, int card )
+{
+ struct para_data_s *r;
+ const char *s1, *s2, *s3;
+ size_t n;
+ char *p;
+ int have_user_id=0,err,algo;
+
+ /* Check that we have all required parameters. */
+ r = get_parameter( para, pKEYTYPE );
+ if(r)
+ {
+ algo=get_parameter_algo(para,pKEYTYPE);
+ if(check_pubkey_algo2(algo,PUBKEY_USAGE_SIG))
+ {
+ log_error("%s:%d: invalid algorithm\n", fname, r->lnr );
+ return -1;
+ }
+ }
+ else
+ {
+ log_error("%s: no Key-Type specified\n",fname);
+ return -1;
+ }
+
+ err=parse_parameter_usage (fname, para, pKEYUSAGE);
+ if(err==0)
+ {
+ /* Default to algo capabilities if key-usage is not provided */
+ r=xmalloc_clear(sizeof(*r));
+ r->key=pKEYUSAGE;
+ r->u.usage=openpgp_pk_algo_usage(algo);
+ r->next=para;
+ para=r;
+ }
+ else if(err==-1)
+ return -1;
+
+ r = get_parameter( para, pSUBKEYTYPE );
+ if(r)
+ {
+ algo=get_parameter_algo( para, pSUBKEYTYPE);
+ if(check_pubkey_algo(algo))
+ {
+ log_error("%s:%d: invalid algorithm\n", fname, r->lnr );
+ return -1;
+ }
+
+ err=parse_parameter_usage (fname, para, pSUBKEYUSAGE);
+ if(err==0)
+ {
+ /* Default to algo capabilities if subkey-usage is not
+ provided */
+ r=xmalloc_clear(sizeof(*r));
+ r->key=pSUBKEYUSAGE;
+ r->u.usage=openpgp_pk_algo_usage(algo);
+ r->next=para;
+ para=r;
+ }
+ else if(err==-1)
+ return -1;
+ }
+
+ if( get_parameter_value( para, pUSERID ) )
+ have_user_id=1;
+ else
+ {
+ /* create the formatted user ID */
+ s1 = get_parameter_value( para, pNAMEREAL );
+ s2 = get_parameter_value( para, pNAMECOMMENT );
+ s3 = get_parameter_value( para, pNAMEEMAIL );
+ if( s1 || s2 || s3 )
+ {
+ n = (s1?strlen(s1):0) + (s2?strlen(s2):0) + (s3?strlen(s3):0);
+ r = xmalloc_clear( sizeof *r + n + 20 );
+ r->key = pUSERID;
+ p = r->u.value;
+ if( s1 )
+ p = stpcpy(p, s1 );
+ if( s2 )
+ p = stpcpy(stpcpy(stpcpy(p," ("), s2 ),")");
+ if( s3 )
+ p = stpcpy(stpcpy(stpcpy(p," <"), s3 ),">");
+ r->next = para;
+ para = r;
+ have_user_id=1;
+ }
+ }
+
+ if(!have_user_id)
+ {
+ log_error("%s: no User-ID specified\n",fname);
+ return -1;
+ }
+
+ /* Set preferences, if any. */
+ keygen_set_std_prefs(get_parameter_value( para, pPREFERENCES ), 0);
+
+ /* Set keyserver, if any. */
+ s1=get_parameter_value( para, pKEYSERVER );
+ if(s1)
+ {
+ struct keyserver_spec *spec;
+
+ spec=parse_keyserver_uri(s1,1,NULL,0);
+ if(spec)
+ {
+ free_keyserver_spec(spec);
+ opt.def_keyserver_url=s1;
+ }
+ else
+ {
+ log_error("%s:%d: invalid keyserver url\n", fname, r->lnr );
+ return -1;
+ }
+ }
+
+ /* Set revoker, if any. */
+ if (parse_revocation_key (fname, para, pREVOKER))
+ return -1;
+
+ /* make DEK and S2K from the Passphrase */
+ r = get_parameter( para, pPASSPHRASE );
+ if( r && *r->u.value ) {
+ /* we have a plain text passphrase - create a DEK from it.
+ * It is a little bit ridiculous to keep it ih secure memory
+ * but becuase we do this alwasy, why not here */
+ STRING2KEY *s2k;
+ DEK *dek;
+
+ s2k = xmalloc_secure( sizeof *s2k );
+ s2k->mode = opt.s2k_mode;
+ s2k->hash_algo = S2K_DIGEST_ALGO;
+ set_next_passphrase( r->u.value );
+ dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k, 2,
+ NULL, NULL);
+ set_next_passphrase( NULL );
+ assert( dek );
+ memset( r->u.value, 0, strlen(r->u.value) );
+
+ r = xmalloc_clear( sizeof *r );
+ r->key = pPASSPHRASE_S2K;
+ r->u.s2k = s2k;
+ r->next = para;
+ para = r;
+ r = xmalloc_clear( sizeof *r );
+ r->key = pPASSPHRASE_DEK;
+ r->u.dek = dek;
+ r->next = para;
+ para = r;
+ }
+
+ /* make KEYEXPIRE from Expire-Date */
+ r = get_parameter( para, pEXPIREDATE );
+ if( r && *r->u.value )
+ {
+ u32 seconds;
+
+ seconds = parse_expire_string( r->u.value );
+ if( seconds == (u32)-1 )
+ {
+ log_error("%s:%d: invalid expire date\n", fname, r->lnr );
+ return -1;
+ }
+ r->u.expire = seconds;
+ r->key = pKEYEXPIRE; /* change hat entry */
+ /* also set it for the subkey */
+ r = xmalloc_clear( sizeof *r + 20 );
+ r->key = pSUBKEYEXPIRE;
+ r->u.expire = seconds;
+ r->next = para;
+ para = r;
+ }
+
+ if( !!outctrl->pub.newfname ^ !!outctrl->sec.newfname ) {
+ log_error("%s:%d: only one ring name is set\n", fname, outctrl->lnr );
+ return -1;
+ }
+
+ do_generate_keypair( para, outctrl, card );
+ return 0;
+}
+
+
+/****************
+ * Kludge to allow non interactive key generation controlled
+ * by a parameter file.
+ * Note, that string parameters are expected to be in UTF-8
+ */
+static void
+read_parameter_file( const char *fname )
+{
+ static struct { const char *name;
+ enum para_name key;
+ } keywords[] = {
+ { "Key-Type", pKEYTYPE},
+ { "Key-Length", pKEYLENGTH },
+ { "Key-Usage", pKEYUSAGE },
+ { "Subkey-Type", pSUBKEYTYPE },
+ { "Subkey-Length", pSUBKEYLENGTH },
+ { "Subkey-Usage", pSUBKEYUSAGE },
+ { "Name-Real", pNAMEREAL },
+ { "Name-Email", pNAMEEMAIL },
+ { "Name-Comment", pNAMECOMMENT },
+ { "Expire-Date", pEXPIREDATE },
+ { "Passphrase", pPASSPHRASE },
+ { "Preferences", pPREFERENCES },
+ { "Revoker", pREVOKER },
+ { "Handle", pHANDLE },
+ { "Keyserver", pKEYSERVER },
+ { NULL, 0 }
+ };
+ IOBUF fp;
+ byte *line;
+ unsigned int maxlen, nline;
+ char *p;
+ int lnr;
+ const char *err = NULL;
+ struct para_data_s *para, *r;
+ int i;
+ struct output_control_s outctrl;
+
+ memset( &outctrl, 0, sizeof( outctrl ) );
+
+ if( !fname || !*fname)
+ fname = "-";
+
+ fp = iobuf_open (fname);
+ if (fp && is_secured_file (iobuf_get_fd (fp)))
+ {
+ iobuf_close (fp);
+ fp = NULL;
+ errno = EPERM;
+ }
+ if (!fp) {
+ log_error (_("can't open `%s': %s\n"), fname, strerror(errno) );
+ return;
+ }
+ iobuf_ioctl (fp, 3, 1, NULL); /* No file caching. */
+
+ lnr = 0;
+ err = NULL;
+ para = NULL;
+ maxlen = 1024;
+ line = NULL;
+ while ( iobuf_read_line (fp, &line, &nline, &maxlen) ) {
+ char *keyword, *value;
+
+ lnr++;
+ if( !maxlen ) {
+ err = "line too long";
+ break;
+ }
+ for( p = line; isspace(*(byte*)p); p++ )
+ ;
+ if( !*p || *p == '#' )
+ continue;
+ keyword = p;
+ if( *keyword == '%' ) {
+ for( ; !isspace(*(byte*)p); p++ )
+ ;
+ if( *p )
+ *p++ = 0;
+ for( ; isspace(*(byte*)p); p++ )
+ ;
+ value = p;
+ trim_trailing_ws( value, strlen(value) );
+ if( !ascii_strcasecmp( keyword, "%echo" ) )
+ log_info("%s\n", value );
+ else if( !ascii_strcasecmp( keyword, "%dry-run" ) )
+ outctrl.dryrun = 1;
+ else if( !ascii_strcasecmp( keyword, "%commit" ) ) {
+ outctrl.lnr = lnr;
+ if (proc_parameter_file( para, fname, &outctrl, 0 ))
+ print_status_key_not_created
+ (get_parameter_value (para, pHANDLE));
+ release_parameter_list( para );
+ para = NULL;
+ }
+ else if( !ascii_strcasecmp( keyword, "%pubring" ) ) {
+ if( outctrl.pub.fname && !strcmp( outctrl.pub.fname, value ) )
+ ; /* still the same file - ignore it */
+ else {
+ xfree( outctrl.pub.newfname );
+ outctrl.pub.newfname = xstrdup( value );
+ outctrl.use_files = 1;
+ }
+ }
+ else if( !ascii_strcasecmp( keyword, "%secring" ) ) {
+ if( outctrl.sec.fname && !strcmp( outctrl.sec.fname, value ) )
+ ; /* still the same file - ignore it */
+ else {
+ xfree( outctrl.sec.newfname );
+ outctrl.sec.newfname = xstrdup( value );
+ outctrl.use_files = 1;
+ }
+ }
+ else
+ log_info("skipping control `%s' (%s)\n", keyword, value );
+
+
+ continue;
+ }
+
+
+ if( !(p = strchr( p, ':' )) || p == keyword ) {
+ err = "missing colon";
+ break;
+ }
+ if( *p )
+ *p++ = 0;
+ for( ; isspace(*(byte*)p); p++ )
+ ;
+ if( !*p ) {
+ err = "missing argument";
+ break;
+ }
+ value = p;
+ trim_trailing_ws( value, strlen(value) );
+
+ for(i=0; keywords[i].name; i++ ) {
+ if( !ascii_strcasecmp( keywords[i].name, keyword ) )
+ break;
+ }
+ if( !keywords[i].name ) {
+ err = "unknown keyword";
+ break;
+ }
+ if( keywords[i].key != pKEYTYPE && !para ) {
+ err = "parameter block does not start with \"Key-Type\"";
+ break;
+ }
+
+ if( keywords[i].key == pKEYTYPE && para ) {
+ outctrl.lnr = lnr;
+ if (proc_parameter_file( para, fname, &outctrl, 0 ))
+ print_status_key_not_created
+ (get_parameter_value (para, pHANDLE));
+ release_parameter_list( para );
+ para = NULL;
+ }
+ else {
+ for( r = para; r; r = r->next ) {
+ if( r->key == keywords[i].key )
+ break;
+ }
+ if( r ) {
+ err = "duplicate keyword";
+ break;
+ }
+ }
+ r = xmalloc_clear( sizeof *r + strlen( value ) );
+ r->lnr = lnr;
+ r->key = keywords[i].key;
+ strcpy( r->u.value, value );
+ r->next = para;
+ para = r;
+ }
+ if( err )
+ log_error("%s:%d: %s\n", fname, lnr, err );
+ else if( iobuf_error (fp) ) {
+ log_error("%s:%d: read error\n", fname, lnr);
+ }
+ else if( para ) {
+ outctrl.lnr = lnr;
+ if (proc_parameter_file( para, fname, &outctrl, 0 ))
+ print_status_key_not_created (get_parameter_value (para, pHANDLE));
+ }
+
+ if( outctrl.use_files ) { /* close open streams */
+ iobuf_close( outctrl.pub.stream );
+ iobuf_close( outctrl.sec.stream );
+
+ /* Must invalidate that ugly cache to actually close it. */
+ if (outctrl.pub.fname)
+ iobuf_ioctl (NULL, 2, 0, (char*)outctrl.pub.fname);
+ if (outctrl.sec.fname)
+ iobuf_ioctl (NULL, 2, 0, (char*)outctrl.sec.fname);
+
+ xfree( outctrl.pub.fname );
+ xfree( outctrl.pub.newfname );
+ xfree( outctrl.sec.fname );
+ xfree( outctrl.sec.newfname );
+ }
+
+ release_parameter_list( para );
+ iobuf_close (fp);
+}
+
+
+/*
+ * Generate a keypair (fname is only used in batch mode) If
+ * CARD_SERIALNO is not NULL the fucntion will create the keys on an
+ * OpenPGP Card. If BACKUP_ENCRYPTION_DIR has been set and
+ * CARD_SERIALNO is NOT NULL, the encryption key for the card gets
+ * generate in software, imported to the card and a backup file
+ * written to directory given by this argument .
+ */
+void
+generate_keypair (const char *fname, const char *card_serialno,
+ const char *backup_encryption_dir)
+{
+ unsigned int nbits;
+ char *uid = NULL;
+ DEK *dek;
+ STRING2KEY *s2k;
+ int algo;
+ unsigned int use;
+ int both = 0;
+ u32 expire;
+ struct para_data_s *para = NULL;
+ struct para_data_s *r;
+ struct output_control_s outctrl;
+
+ memset( &outctrl, 0, sizeof( outctrl ) );
+
+ if (opt.batch && card_serialno)
+ {
+ /* We don't yet support unattended key generation. */
+ log_error (_("can't do this in batch mode\n"));
+ return;
+ }
+
+ if (opt.batch)
+ {
+ read_parameter_file( fname );
+ return;
+ }
+
+ if (card_serialno)
+ {
+#ifdef ENABLE_CARD_SUPPORT
+ r = xcalloc (1, sizeof *r + strlen (card_serialno) );
+ r->key = pSERIALNO;
+ strcpy( r->u.value, card_serialno);
+ r->next = para;
+ para = r;
+
+ algo = PUBKEY_ALGO_RSA;
+
+ r = xcalloc (1, sizeof *r + 20 );
+ r->key = pKEYTYPE;
+ sprintf( r->u.value, "%d", algo );
+ r->next = para;
+ para = r;
+ r = xcalloc (1, sizeof *r + 20 );
+ r->key = pKEYUSAGE;
+ strcpy (r->u.value, "sign");
+ r->next = para;
+ para = r;
+
+ r = xcalloc (1, sizeof *r + 20 );
+ r->key = pSUBKEYTYPE;
+ sprintf( r->u.value, "%d", algo );
+ r->next = para;
+ para = r;
+ r = xcalloc (1, sizeof *r + 20 );
+ r->key = pSUBKEYUSAGE;
+ strcpy (r->u.value, "encrypt");
+ r->next = para;
+ para = r;
+
+ r = xcalloc (1, sizeof *r + 20 );
+ r->key = pAUTHKEYTYPE;
+ sprintf( r->u.value, "%d", algo );
+ r->next = para;
+ para = r;
+
+ if (backup_encryption_dir)
+ {
+ r = xcalloc (1, sizeof *r + strlen (backup_encryption_dir) );
+ r->key = pBACKUPENCDIR;
+ strcpy (r->u.value, backup_encryption_dir);
+ r->next = para;
+ para = r;
+ }
+#endif /*ENABLE_CARD_SUPPORT*/
+ }
+ else
+ {
+ algo = ask_algo( 0, &use );
+ if( !algo )
+ { /* default: DSA with ElG subkey of the specified size */
+ both = 1;
+ r = xmalloc_clear( sizeof *r + 20 );
+ r->key = pKEYTYPE;
+ sprintf( r->u.value, "%d", PUBKEY_ALGO_DSA );
+ r->next = para;
+ para = r;
+ nbits = ask_keysize( PUBKEY_ALGO_DSA );
+ r = xmalloc_clear( sizeof *r + 20 );
+ r->key = pKEYLENGTH;
+ sprintf( r->u.value, "%u", nbits);
+ r->next = para;
+ para = r;
+ r = xmalloc_clear( sizeof *r + 20 );
+ r->key = pKEYUSAGE;
+ strcpy( r->u.value, "sign" );
+ r->next = para;
+ para = r;
+
+ algo = PUBKEY_ALGO_ELGAMAL_E;
+ r = xmalloc_clear( sizeof *r + 20 );
+ r->key = pSUBKEYTYPE;
+ sprintf( r->u.value, "%d", algo );
+ r->next = para;
+ para = r;
+ r = xmalloc_clear( sizeof *r + 20 );
+ r->key = pSUBKEYUSAGE;
+ strcpy( r->u.value, "encrypt" );
+ r->next = para;
+ para = r;
+ }
+ else
+ {
+ r = xmalloc_clear( sizeof *r + 20 );
+ r->key = pKEYTYPE;
+ sprintf( r->u.value, "%d", algo );
+ r->next = para;
+ para = r;
+
+ if (use)
+ {
+ r = xmalloc_clear( sizeof *r + 25 );
+ r->key = pKEYUSAGE;
+ sprintf( r->u.value, "%s%s%s",
+ (use & PUBKEY_USAGE_SIG)? "sign ":"",
+ (use & PUBKEY_USAGE_ENC)? "encrypt ":"",
+ (use & PUBKEY_USAGE_AUTH)? "auth":"" );
+ r->next = para;
+ para = r;
+ }
+
+ }
+
+ nbits = ask_keysize( algo );
+ r = xmalloc_clear( sizeof *r + 20 );
+ r->key = both? pSUBKEYLENGTH : pKEYLENGTH;
+ sprintf( r->u.value, "%u", nbits);
+ r->next = para;
+ para = r;
+ }
+
+ expire = ask_expire_interval(0,NULL);
+ r = xmalloc_clear( sizeof *r + 20 );
+ r->key = pKEYEXPIRE;
+ r->u.expire = expire;
+ r->next = para;
+ para = r;
+ r = xmalloc_clear( sizeof *r + 20 );
+ r->key = pSUBKEYEXPIRE;
+ r->u.expire = expire;
+ r->next = para;
+ para = r;
+
+ uid = ask_user_id(0);
+ if( !uid )
+ {
+ log_error(_("Key generation canceled.\n"));
+ release_parameter_list( para );
+ return;
+ }
+ r = xmalloc_clear( sizeof *r + strlen(uid) );
+ r->key = pUSERID;
+ strcpy( r->u.value, uid );
+ r->next = para;
+ para = r;
+
+ dek = card_serialno? NULL : do_ask_passphrase( &s2k );
+ if( dek )
+ {
+ r = xmalloc_clear( sizeof *r );
+ r->key = pPASSPHRASE_DEK;
+ r->u.dek = dek;
+ r->next = para;
+ para = r;
+ r = xmalloc_clear( sizeof *r );
+ r->key = pPASSPHRASE_S2K;
+ r->u.s2k = s2k;
+ r->next = para;
+ para = r;
+ }
+
+ proc_parameter_file( para, "[internal]", &outctrl, !!card_serialno);
+ release_parameter_list( para );
+}
+
+
+#ifdef ENABLE_CARD_SUPPORT
+/* Generate a raw key and return it as a secret key packet. The
+ function will ask for the passphrase and return a protected as well
+ as an unprotected copy of a new secret key packet. 0 is returned
+ on success and the caller must then free the returned values. */
+static int
+generate_raw_key (int algo, unsigned int nbits, u32 created_at,
+ PKT_secret_key **r_sk_unprotected,
+ PKT_secret_key **r_sk_protected)
+{
+ int rc;
+ DEK *dek = NULL;
+ STRING2KEY *s2k = NULL;
+ PKT_secret_key *sk = NULL;
+ int i;
+ size_t nskey, npkey;
+
+ npkey = pubkey_get_npkey (algo);
+ nskey = pubkey_get_nskey (algo);
+ assert (nskey <= PUBKEY_MAX_NSKEY && npkey < nskey);
+
+ if (nbits < 512)
+ {
+ nbits = 512;
+ log_info (_("keysize invalid; using %u bits\n"), nbits );
+ }
+
+ if ((nbits % 32))
+ {
+ nbits = ((nbits + 31) / 32) * 32;
+ log_info(_("keysize rounded up to %u bits\n"), nbits );
+ }
+
+ dek = do_ask_passphrase (&s2k);
+
+ sk = xmalloc_clear (sizeof *sk);
+ sk->timestamp = created_at;
+ sk->version = 4;
+ sk->pubkey_algo = algo;
+
+ rc = pubkey_generate (algo, nbits, sk->skey, NULL);
+ if (rc)
+ {
+ log_error("pubkey_generate failed: %s\n", g10_errstr(rc) );
+ goto leave;
+ }
+
+ for (i=npkey; i < nskey; i++)
+ sk->csum += checksum_mpi (sk->skey[i]);
+
+ if (r_sk_unprotected)
+ *r_sk_unprotected = copy_secret_key (NULL, sk);
+
+ if (dek)
+ {
+ sk->protect.algo = dek->algo;
+ sk->protect.s2k = *s2k;
+ rc = protect_secret_key (sk, dek);
+ if (rc)
+ {
+ log_error ("protect_secret_key failed: %s\n", g10_errstr(rc));
+ goto leave;
+ }
+ }
+ if (r_sk_protected)
+ {
+ *r_sk_protected = sk;
+ sk = NULL;
+ }
+
+ leave:
+ if (sk)
+ free_secret_key (sk);
+ xfree (dek);
+ xfree (s2k);
+ return rc;
+}
+#endif /* ENABLE_CARD_SUPPORT */
+
+/* Create and delete a dummy packet to start off a list of kbnodes. */
+static void
+start_tree(KBNODE *tree)
+{
+ PACKET *pkt;
+
+ pkt=xmalloc_clear(sizeof(*pkt));
+ pkt->pkttype=PKT_NONE;
+ *tree=new_kbnode(pkt);
+ delete_kbnode(*tree);
+}
+
+static void
+do_generate_keypair( struct para_data_s *para,
+ struct output_control_s *outctrl, int card )
+{
+ KBNODE pub_root = NULL;
+ KBNODE sec_root = NULL;
+ PKT_secret_key *pri_sk = NULL, *sub_sk = NULL;
+ const char *s;
+ struct revocation_key *revkey;
+ int rc;
+ int did_sub = 0;
+
+ if( outctrl->dryrun )
+ {
+ log_info("dry-run mode - key generation skipped\n");
+ return;
+ }
+
+ if( outctrl->use_files ) {
+ if( outctrl->pub.newfname ) {
+ iobuf_close(outctrl->pub.stream);
+ outctrl->pub.stream = NULL;
+ if (outctrl->pub.fname)
+ iobuf_ioctl (NULL, 2, 0, (char*)outctrl->pub.fname);
+ xfree( outctrl->pub.fname );
+ outctrl->pub.fname = outctrl->pub.newfname;
+ outctrl->pub.newfname = NULL;
+
+ if (is_secured_filename (outctrl->pub.fname) ) {
+ outctrl->pub.stream = NULL;
+ errno = EPERM;
+ }
+ else
+ outctrl->pub.stream = iobuf_create( outctrl->pub.fname );
+ if( !outctrl->pub.stream ) {
+ log_error(_("can't create `%s': %s\n"), outctrl->pub.newfname,
+ strerror(errno) );
+ return;
+ }
+ if( opt.armor ) {
+ outctrl->pub.afx.what = 1;
+ iobuf_push_filter( outctrl->pub.stream, armor_filter,
+ &outctrl->pub.afx );
+ }
+ }
+ if( outctrl->sec.newfname ) {
+ mode_t oldmask;
+
+ iobuf_close(outctrl->sec.stream);
+ outctrl->sec.stream = NULL;
+ if (outctrl->sec.fname)
+ iobuf_ioctl (NULL, 2, 0, (char*)outctrl->sec.fname);
+ xfree( outctrl->sec.fname );
+ outctrl->sec.fname = outctrl->sec.newfname;
+ outctrl->sec.newfname = NULL;
+
+ oldmask = umask (077);
+ if (is_secured_filename (outctrl->sec.fname) ) {
+ outctrl->sec.stream = NULL;
+ errno = EPERM;
+ }
+ else
+ outctrl->sec.stream = iobuf_create( outctrl->sec.fname );
+ umask (oldmask);
+ if( !outctrl->sec.stream ) {
+ log_error(_("can't create `%s': %s\n"), outctrl->sec.newfname,
+ strerror(errno) );
+ return;
+ }
+ if( opt.armor ) {
+ outctrl->sec.afx.what = 5;
+ iobuf_push_filter( outctrl->sec.stream, armor_filter,
+ &outctrl->sec.afx );
+ }
+ }
+ assert( outctrl->pub.stream );
+ assert( outctrl->sec.stream );
+ if( opt.verbose ) {
+ log_info(_("writing public key to `%s'\n"), outctrl->pub.fname );
+ if (card)
+ log_info (_("writing secret key stub to `%s'\n"),
+ outctrl->sec.fname);
+ else
+ log_info(_("writing secret key to `%s'\n"), outctrl->sec.fname );
+ }
+ }
+
+
+ /* we create the packets as a tree of kbnodes. Because the
+ * structure we create is known in advance we simply generate a
+ * linked list. The first packet is a dummy packet which we flag
+ * as deleted. The very first packet must always be a KEY packet.
+ */
+
+ start_tree(&pub_root);
+ start_tree(&sec_root);
+
+ if (!card)
+ {
+ rc = do_create( get_parameter_algo( para, pKEYTYPE ),
+ get_parameter_uint( para, pKEYLENGTH ),
+ pub_root, sec_root,
+ get_parameter_dek( para, pPASSPHRASE_DEK ),
+ get_parameter_s2k( para, pPASSPHRASE_S2K ),
+ &pri_sk,
+ get_parameter_u32( para, pKEYEXPIRE ), 0 );
+ }
+ else
+ {
+ rc = gen_card_key (PUBKEY_ALGO_RSA, 1, 1, pub_root, sec_root, NULL,
+ get_parameter_u32 (para, pKEYEXPIRE), para);
+ if (!rc)
+ {
+ pri_sk = sec_root->next->pkt->pkt.secret_key;
+ assert (pri_sk);
+ }
+ }
+
+ if(!rc && (revkey=get_parameter_revkey(para,pREVOKER)))
+ {
+ rc=write_direct_sig(pub_root,pub_root,pri_sk,revkey);
+ if(!rc)
+ write_direct_sig(sec_root,pub_root,pri_sk,revkey);
+ }
+
+ if( !rc && (s=get_parameter_value(para, pUSERID)) )
+ {
+ write_uid(pub_root, s );
+ if( !rc )
+ write_uid(sec_root, s );
+
+ if( !rc )
+ rc = write_selfsigs(sec_root, pub_root, pri_sk,
+ get_parameter_uint (para, pKEYUSAGE));
+ }
+
+ /* Write the auth key to the card before the encryption key. This
+ is a partial workaround for a PGP bug (as of this writing, all
+ versions including 8.1), that causes it to try and encrypt to
+ the most recent subkey regardless of whether that subkey is
+ actually an encryption type. In this case, the auth key is an
+ RSA key so it succeeds. */
+
+ if (!rc && card && get_parameter (para, pAUTHKEYTYPE))
+ {
+ rc = gen_card_key (PUBKEY_ALGO_RSA, 3, 0, pub_root, sec_root, NULL,
+ get_parameter_u32 (para, pKEYEXPIRE), para);
+
+ if (!rc)
+ rc = write_keybinding (pub_root, pub_root, pri_sk, sub_sk, PUBKEY_USAGE_AUTH);
+ if (!rc)
+ rc = write_keybinding (sec_root, pub_root, pri_sk, sub_sk, PUBKEY_USAGE_AUTH);
+ }
+
+ if( !rc && get_parameter( para, pSUBKEYTYPE ) )
+ {
+ if (!card)
+ {
+ rc = do_create( get_parameter_algo( para, pSUBKEYTYPE ),
+ get_parameter_uint( para, pSUBKEYLENGTH ),
+ pub_root, sec_root,
+ get_parameter_dek( para, pPASSPHRASE_DEK ),
+ get_parameter_s2k( para, pPASSPHRASE_S2K ),
+ &sub_sk,
+ get_parameter_u32( para, pSUBKEYEXPIRE ), 1 );
+ }
+ else
+ {
+ if ((s = get_parameter_value (para, pBACKUPENCDIR)))
+ {
+ /* A backup of the encryption key has been requested.
+ Generate the key i software and import it then to
+ the card. Write a backup file. */
+ rc = gen_card_key_with_backup (PUBKEY_ALGO_RSA, 2, 0,
+ pub_root, sec_root,
+ get_parameter_u32 (para,
+ pKEYEXPIRE),
+ para, s);
+ }
+ else
+ rc = gen_card_key (PUBKEY_ALGO_RSA, 2, 0, pub_root, sec_root,
+ NULL,
+ get_parameter_u32 (para, pKEYEXPIRE), para);
+ }
+
+ if( !rc )
+ rc = write_keybinding(pub_root, pub_root, pri_sk, sub_sk,
+ get_parameter_uint (para, pSUBKEYUSAGE));
+ if( !rc )
+ rc = write_keybinding(sec_root, pub_root, pri_sk, sub_sk,
+ get_parameter_uint (para, pSUBKEYUSAGE));
+ did_sub = 1;
+ }
+
+ if( !rc && outctrl->use_files ) { /* direct write to specified files */
+ rc = write_keyblock( outctrl->pub.stream, pub_root );
+ if( rc )
+ log_error("can't write public key: %s\n", g10_errstr(rc) );
+ if( !rc ) {
+ rc = write_keyblock( outctrl->sec.stream, sec_root );
+ if( rc )
+ log_error("can't write secret key: %s\n", g10_errstr(rc) );
+ }
+
+ }
+ else if( !rc ) { /* write to the standard keyrings */
+ KEYDB_HANDLE pub_hd = keydb_new (0);
+ KEYDB_HANDLE sec_hd = keydb_new (1);
+
+ /* FIXME: we may have to create the keyring first */
+ rc = keydb_locate_writable (pub_hd, NULL);
+ if (rc)
+ log_error (_("no writable public keyring found: %s\n"),
+ g10_errstr (rc));
+
+ if (!rc) {
+ rc = keydb_locate_writable (sec_hd, NULL);
+ if (rc)
+ log_error (_("no writable secret keyring found: %s\n"),
+ g10_errstr (rc));
+ }
+
+ if (!rc && opt.verbose) {
+ log_info(_("writing public key to `%s'\n"),
+ keydb_get_resource_name (pub_hd));
+ if (card)
+ log_info (_("writing secret key stub to `%s'\n"),
+ keydb_get_resource_name (sec_hd));
+ else
+ log_info(_("writing secret key to `%s'\n"),
+ keydb_get_resource_name (sec_hd));
+ }
+
+ if (!rc) {
+ rc = keydb_insert_keyblock (pub_hd, pub_root);
+ if (rc)
+ log_error (_("error writing public keyring `%s': %s\n"),
+ keydb_get_resource_name (pub_hd), g10_errstr(rc));
+ }
+
+ if (!rc) {
+ rc = keydb_insert_keyblock (sec_hd, sec_root);
+ if (rc)
+ log_error (_("error writing secret keyring `%s': %s\n"),
+ keydb_get_resource_name (pub_hd), g10_errstr(rc));
+ }
+
+ keydb_release (pub_hd);
+ keydb_release (sec_hd);
+
+ if (!rc) {
+ int no_enc_rsa =
+ get_parameter_algo(para, pKEYTYPE) == PUBKEY_ALGO_RSA
+ && get_parameter_uint( para, pKEYUSAGE )
+ && !(get_parameter_uint( para,pKEYUSAGE) & PUBKEY_USAGE_ENC);
+ PKT_public_key *pk = find_kbnode (pub_root,
+ PKT_PUBLIC_KEY)->pkt->pkt.public_key;
+
+ keyid_from_pk(pk,pk->main_keyid);
+ register_trusted_keyid(pk->main_keyid);
+
+ update_ownertrust (pk,
+ ((get_ownertrust (pk) & ~TRUST_MASK)
+ | TRUST_ULTIMATE ));
+
+ if (!opt.batch) {
+ tty_printf(_("public and secret key created and signed.\n") );
+ tty_printf("\n");
+ list_keyblock(pub_root,0,1,NULL);
+ }
+
+
+ if( !opt.batch
+ && ( get_parameter_algo( para, pKEYTYPE ) == PUBKEY_ALGO_DSA
+ || no_enc_rsa )
+ && !get_parameter( para, pSUBKEYTYPE ) )
+ {
+ tty_printf(_("Note that this key cannot be used for "
+ "encryption. You may want to use\n"
+ "the command \"--edit-key\" to generate a "
+ "subkey for this purpose.\n") );
+ }
+ }
+ }
+
+ if( rc ) {
+ if( opt.batch )
+ log_error("key generation failed: %s\n", g10_errstr(rc) );
+ else
+ tty_printf(_("Key generation failed: %s\n"), g10_errstr(rc) );
+ print_status_key_not_created ( get_parameter_value (para, pHANDLE) );
+ }
+ else {
+ PKT_public_key *pk = find_kbnode (pub_root,
+ PKT_PUBLIC_KEY)->pkt->pkt.public_key;
+ print_status_key_created (did_sub? 'B':'P', pk,
+ get_parameter_value (para, pHANDLE));
+ }
+ release_kbnode( pub_root );
+ release_kbnode( sec_root );
+
+ if( pri_sk && !card) /* the unprotected secret key unless we have a */
+ free_secret_key(pri_sk); /* shallow copy in card mode. */
+ if( sub_sk )
+ free_secret_key(sub_sk);
+}
+
+
+/****************
+ * add a new subkey to an existing key.
+ * Returns true if a new key has been generated and put into the keyblocks.
+ */
+int
+generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock )
+{
+ int okay=0, rc=0;
+ KBNODE node;
+ PKT_secret_key *pri_sk = NULL, *sub_sk = NULL;
+ int algo;
+ unsigned int use;
+ u32 expire;
+ unsigned nbits;
+ char *passphrase = NULL;
+ DEK *dek = NULL;
+ STRING2KEY *s2k = NULL;
+ u32 cur_time;
+ int ask_pass = 0;
+
+ /* break out the primary secret key */
+ node = find_kbnode( sec_keyblock, PKT_SECRET_KEY );
+ if( !node ) {
+ log_error("Oops; secret key not found anymore!\n");
+ goto leave;
+ }
+
+ /* make a copy of the sk to keep the protected one in the keyblock */
+ pri_sk = copy_secret_key( NULL, node->pkt->pkt.secret_key );
+
+ cur_time = make_timestamp();
+ if( pri_sk->timestamp > cur_time ) {
+ ulong d = pri_sk->timestamp - cur_time;
+ log_info( d==1 ? _("key has been created %lu second "
+ "in future (time warp or clock problem)\n")
+ : _("key has been created %lu seconds "
+ "in future (time warp or clock problem)\n"), d );
+ if( !opt.ignore_time_conflict ) {
+ rc = G10ERR_TIME_CONFLICT;
+ goto leave;
+ }
+ }
+
+ if (pri_sk->version < 4) {
+ log_info (_("NOTE: creating subkeys for v3 keys "
+ "is not OpenPGP compliant\n"));
+ goto leave;
+ }
+
+ if (pri_sk->is_protected && pri_sk->protect.s2k.mode == 1001) {
+ tty_printf(_("Secret parts of primary key are not available.\n"));
+ rc = G10ERR_NO_SECKEY;
+ goto leave;
+ }
+
+
+ /* Unprotect to get the passphrase. */
+ switch( is_secret_key_protected( pri_sk ) ) {
+ case -1:
+ rc = G10ERR_PUBKEY_ALGO;
+ break;
+ case 0:
+ tty_printf(_("This key is not protected.\n"));
+ break;
+ case -2:
+ tty_printf(_("Secret parts of primary key are stored on-card.\n"));
+ ask_pass = 1;
+ break;
+ default:
+ tty_printf(_("Key is protected.\n"));
+ rc = check_secret_key( pri_sk, 0 );
+ if( !rc )
+ passphrase = get_last_passphrase();
+ break;
+ }
+ if( rc )
+ goto leave;
+
+ algo = ask_algo( 1, &use );
+ assert(algo);
+ nbits = ask_keysize( algo );
+ expire = ask_expire_interval(0,NULL);
+ if( !cpr_enabled() && !cpr_get_answer_is_yes("keygen.sub.okay",
+ _("Really create? (y/N) ")))
+ goto leave;
+
+ if (ask_pass)
+ dek = do_ask_passphrase (&s2k);
+ else if (passphrase) {
+ s2k = xmalloc_secure( sizeof *s2k );
+ s2k->mode = opt.s2k_mode;
+ s2k->hash_algo = S2K_DIGEST_ALGO;
+ set_next_passphrase( passphrase );
+ dek = passphrase_to_dek( NULL, 0, opt.s2k_cipher_algo, s2k, 2,
+ NULL, NULL );
+ }
+
+ rc = do_create( algo, nbits, pub_keyblock, sec_keyblock,
+ dek, s2k, &sub_sk, expire, 1 );
+ if( !rc )
+ rc = write_keybinding(pub_keyblock, pub_keyblock, pri_sk, sub_sk, use);
+ if( !rc )
+ rc = write_keybinding(sec_keyblock, pub_keyblock, pri_sk, sub_sk, use);
+ if( !rc ) {
+ okay = 1;
+ write_status_text (STATUS_KEY_CREATED, "S");
+ }
+
+ leave:
+ if( rc )
+ log_error(_("Key generation failed: %s\n"), g10_errstr(rc) );
+ xfree( passphrase );
+ xfree( dek );
+ xfree( s2k );
+ /* release the copy of the (now unprotected) secret keys */
+ if( pri_sk )
+ free_secret_key(pri_sk);
+ if( sub_sk )
+ free_secret_key(sub_sk);
+ set_next_passphrase( NULL );
+ return okay;
+}
+
+
+#ifdef ENABLE_CARD_SUPPORT
+/* Generate a subkey on a card. */
+int
+generate_card_subkeypair (KBNODE pub_keyblock, KBNODE sec_keyblock,
+ int keyno, const char *serialno)
+{
+ int okay=0, rc=0;
+ KBNODE node;
+ PKT_secret_key *pri_sk = NULL, *sub_sk;
+ int algo;
+ unsigned int use;
+ u32 expire;
+ char *passphrase = NULL;
+ u32 cur_time;
+ struct para_data_s *para = NULL;
+
+ assert (keyno >= 1 && keyno <= 3);
+
+ para = xcalloc (1, sizeof *para + strlen (serialno) );
+ para->key = pSERIALNO;
+ strcpy (para->u.value, serialno);
+
+ /* Break out the primary secret key */
+ node = find_kbnode( sec_keyblock, PKT_SECRET_KEY );
+ if(!node)
+ {
+ log_error("Oops; secret key not found anymore!\n");
+ goto leave;
+ }
+
+ /* Make a copy of the sk to keep the protected one in the keyblock */
+ pri_sk = copy_secret_key (NULL, node->pkt->pkt.secret_key);
+
+ cur_time = make_timestamp();
+ if (pri_sk->timestamp > cur_time)
+ {
+ ulong d = pri_sk->timestamp - cur_time;
+ log_info (d==1 ? _("key has been created %lu second "
+ "in future (time warp or clock problem)\n")
+ : _("key has been created %lu seconds "
+ "in future (time warp or clock problem)\n"), d );
+ if (!opt.ignore_time_conflict)
+ {
+ rc = G10ERR_TIME_CONFLICT;
+ goto leave;
+ }
+ }
+
+ if (pri_sk->version < 4)
+ {
+ log_info (_("NOTE: creating subkeys for v3 keys "
+ "is not OpenPGP compliant\n"));
+ goto leave;
+ }
+
+ /* Unprotect to get the passphrase. */
+ switch( is_secret_key_protected (pri_sk) )
+ {
+ case -1:
+ rc = G10ERR_PUBKEY_ALGO;
+ break;
+ case 0:
+ tty_printf("This key is not protected.\n");
+ break;
+ default:
+ tty_printf("Key is protected.\n");
+ rc = check_secret_key( pri_sk, 0 );
+ if (!rc)
+ passphrase = get_last_passphrase();
+ break;
+ }
+ if (rc)
+ goto leave;
+
+ algo = PUBKEY_ALGO_RSA;
+ expire = ask_expire_interval (0,NULL);
+ if (keyno == 1)
+ use = PUBKEY_USAGE_SIG;
+ else if (keyno == 2)
+ use = PUBKEY_USAGE_ENC;
+ else
+ use = PUBKEY_USAGE_AUTH;
+ if (!cpr_enabled() && !cpr_get_answer_is_yes("keygen.cardsub.okay",
+ _("Really create? (y/N) ")))
+ goto leave;
+
+ if (passphrase)
+ set_next_passphrase (passphrase);
+ rc = gen_card_key (algo, keyno, 0, pub_keyblock, sec_keyblock,
+ &sub_sk, expire, para);
+ if (!rc)
+ rc = write_keybinding (pub_keyblock, pub_keyblock, pri_sk, sub_sk, use);
+ if (!rc)
+ rc = write_keybinding (sec_keyblock, pub_keyblock, pri_sk, sub_sk, use);
+ if (!rc)
+ {
+ okay = 1;
+ write_status_text (STATUS_KEY_CREATED, "S");
+ }
+
+ leave:
+ if (rc)
+ log_error (_("Key generation failed: %s\n"), g10_errstr(rc) );
+ xfree (passphrase);
+ /* Release the copy of the (now unprotected) secret keys. */
+ if (pri_sk)
+ free_secret_key (pri_sk);
+ set_next_passphrase( NULL );
+ release_parameter_list (para);
+ return okay;
+}
+#endif /* !ENABLE_CARD_SUPPORT */
+
+
+/****************
+ * Write a keyblock to an output stream
+ */
+static int
+write_keyblock( IOBUF out, KBNODE node )
+{
+ for( ; node ; node = node->next )
+ {
+ if(!is_deleted_kbnode(node))
+ {
+ int rc = build_packet( out, node->pkt );
+ if( rc )
+ {
+ log_error("build_packet(%d) failed: %s\n",
+ node->pkt->pkttype, g10_errstr(rc) );
+ return G10ERR_WRITE_FILE;
+ }
+ }
+ }
+
+ return 0;
+}
+
+
+static int
+gen_card_key (int algo, int keyno, int is_primary,
+ KBNODE pub_root, KBNODE sec_root, PKT_secret_key **ret_sk,
+ u32 expireval, struct para_data_s *para)
+{
+#ifdef ENABLE_CARD_SUPPORT
+ int rc;
+ const char *s;
+ struct agent_card_genkey_s info;
+ PACKET *pkt;
+ PKT_secret_key *sk;
+ PKT_public_key *pk;
+
+ assert (algo == PUBKEY_ALGO_RSA);
+
+ /* Fixme: We don't have the serialnumber available, thus passing NULL. */
+ rc = agent_scd_genkey (&info, keyno, 1, NULL);
+/* if (gpg_err_code (rc) == GPG_ERR_EEXIST) */
+/* { */
+/* tty_printf ("\n"); */
+/* log_error ("WARNING: key does already exists!\n"); */
+/* tty_printf ("\n"); */
+/* if ( cpr_get_answer_is_yes( "keygen.card.replace_key", */
+/* _("Replace existing key? "))) */
+/* rc = agent_scd_genkey (&info, keyno, 1); */
+/* } */
+
+ if (rc)
+ {
+ log_error ("key generation failed: %s\n", gpg_strerror (rc));
+ return rc;
+ }
+ if ( !info.n || !info.e )
+ {
+ log_error ("communication error with SCD\n");
+ mpi_free (info.n);
+ mpi_free (info.e);
+ return gpg_error (GPG_ERR_GENERAL);
+ }
+
+
+ pk = xcalloc (1, sizeof *pk );
+ sk = xcalloc (1, sizeof *sk );
+ sk->timestamp = pk->timestamp = info.created_at;
+ sk->version = pk->version = 4;
+ if (expireval)
+ sk->expiredate = pk->expiredate = pk->timestamp + expireval;
+ sk->pubkey_algo = pk->pubkey_algo = algo;
+ pk->pkey[0] = info.n;
+ pk->pkey[1] = info.e;
+ sk->skey[0] = mpi_copy (pk->pkey[0]);
+ sk->skey[1] = mpi_copy (pk->pkey[1]);
+ sk->skey[2] = mpi_set_opaque (NULL, xstrdup ("dummydata"), 10);
+ sk->is_protected = 1;
+ sk->protect.s2k.mode = 1002;
+ s = get_parameter_value (para, pSERIALNO);
+ if (s)
+ {
+ for (sk->protect.ivlen=0; sk->protect.ivlen < 16 && *s && s[1];
+ sk->protect.ivlen++, s += 2)
+ sk->protect.iv[sk->protect.ivlen] = xtoi_2 (s);
+ }
+
+ if( ret_sk )
+ *ret_sk = sk;
+
+ pkt = xcalloc (1,sizeof *pkt);
+ pkt->pkttype = is_primary ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY;
+ pkt->pkt.public_key = pk;
+ add_kbnode(pub_root, new_kbnode( pkt ));
+
+ pkt = xcalloc (1,sizeof *pkt);
+ pkt->pkttype = is_primary ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY;
+ pkt->pkt.secret_key = sk;
+ add_kbnode(sec_root, new_kbnode( pkt ));
+
+ return 0;
+#else
+ return -1;
+#endif /*!ENABLE_CARD_SUPPORT*/
+}
+
+
+
+static int
+gen_card_key_with_backup (int algo, int keyno, int is_primary,
+ KBNODE pub_root, KBNODE sec_root,
+ u32 expireval, struct para_data_s *para,
+ const char *backup_dir)
+{
+#ifdef ENABLE_CARD_SUPPORT
+ int rc;
+ const char *s;
+ PACKET *pkt;
+ PKT_secret_key *sk, *sk_unprotected, *sk_protected;
+ PKT_public_key *pk;
+ size_t n;
+ int i;
+
+ sk_unprotected = NULL;
+ sk_protected = NULL;
+ rc = generate_raw_key (algo, 1024, make_timestamp (),
+ &sk_unprotected, &sk_protected);
+ if (rc)
+ return rc;
+
+ /* First, store the key to the card. */
+ rc = save_unprotected_key_to_card (sk_unprotected, keyno);
+ if (rc)
+ {
+ log_error (_("storing key onto card failed: %s\n"), g10_errstr (rc));
+ free_secret_key (sk_unprotected);
+ free_secret_key (sk_protected);
+ return rc;
+ }
+
+ /* Get rid of the secret key parameters and store the serial numer. */
+ sk = sk_unprotected;
+ n = pubkey_get_nskey (sk->pubkey_algo);
+ for (i=pubkey_get_npkey (sk->pubkey_algo); i < n; i++)
+ {
+ mpi_free (sk->skey[i]);
+ sk->skey[i] = NULL;
+ }
+ i = pubkey_get_npkey (sk->pubkey_algo);
+ sk->skey[i] = mpi_set_opaque (NULL, xstrdup ("dummydata"), 10);
+ sk->is_protected = 1;
+ sk->protect.s2k.mode = 1002;
+ s = get_parameter_value (para, pSERIALNO);
+ assert (s);
+ for (sk->protect.ivlen=0; sk->protect.ivlen < 16 && *s && s[1];
+ sk->protect.ivlen++, s += 2)
+ sk->protect.iv[sk->protect.ivlen] = xtoi_2 (s);
+
+ /* Now write the *protected* secret key to the file. */
+ {
+ char name_buffer[50];
+ char *fname;
+ IOBUF fp;
+ mode_t oldmask;
+
+ keyid_from_sk (sk, NULL);
+ sprintf (name_buffer,"sk_%08lX%08lX.gpg",
+ (ulong)sk->keyid[0], (ulong)sk->keyid[1]);
+
+ fname = make_filename (backup_dir, name_buffer, NULL);
+ oldmask = umask (077);
+ if (is_secured_filename (fname))
+ {
+ fp = NULL;
+ errno = EPERM;
+ }
+ else
+ fp = iobuf_create (fname);
+ umask (oldmask);
+ if (!fp)
+ {
+ log_error (_("can't create backup file `%s': %s\n"),
+ fname, strerror(errno) );
+ xfree (fname);
+ free_secret_key (sk_unprotected);
+ free_secret_key (sk_protected);
+ return G10ERR_OPEN_FILE;
+ }
+
+ pkt = xcalloc (1, sizeof *pkt);
+ pkt->pkttype = PKT_SECRET_KEY;
+ pkt->pkt.secret_key = sk_protected;
+ sk_protected = NULL;
+
+ rc = build_packet (fp, pkt);
+ if (rc)
+ {
+ log_error("build packet failed: %s\n", g10_errstr(rc) );
+ iobuf_cancel (fp);
+ }
+ else
+ {
+ byte array[MAX_FINGERPRINT_LEN];
+ char *fprbuf, *p;
+
+ iobuf_close (fp);
+ iobuf_ioctl (NULL, 2, 0, (char*)fname);
+ log_info (_("NOTE: backup of card key saved to `%s'\n"), fname);
+
+ fingerprint_from_sk (sk, array, &n);
+ p = fprbuf = xmalloc (MAX_FINGERPRINT_LEN*2 + 1 + 1);
+ for (i=0; i < n ; i++, p += 2)
+ sprintf (p, "%02X", array[i]);
+ *p++ = ' ';
+ *p = 0;
+
+ write_status_text_and_buffer (STATUS_BACKUP_KEY_CREATED,
+ fprbuf,
+ fname, strlen (fname),
+ 0);
+ xfree (fprbuf);
+ }
+ free_packet (pkt);
+ xfree (pkt);
+ xfree (fname);
+ if (rc)
+ {
+ free_secret_key (sk_unprotected);
+ return rc;
+ }
+ }
+
+ /* Create the public key from the secret key. */
+ pk = xcalloc (1, sizeof *pk );
+ pk->timestamp = sk->timestamp;
+ pk->version = sk->version;
+ if (expireval)
+ pk->expiredate = sk->expiredate = sk->timestamp + expireval;
+ pk->pubkey_algo = sk->pubkey_algo;
+ n = pubkey_get_npkey (sk->pubkey_algo);
+ for (i=0; i < n; i++)
+ pk->pkey[i] = mpi_copy (sk->skey[i]);
+
+ /* Build packets and add them to the node lists. */
+ pkt = xcalloc (1,sizeof *pkt);
+ pkt->pkttype = is_primary ? PKT_PUBLIC_KEY : PKT_PUBLIC_SUBKEY;
+ pkt->pkt.public_key = pk;
+ add_kbnode(pub_root, new_kbnode( pkt ));
+
+ pkt = xcalloc (1,sizeof *pkt);
+ pkt->pkttype = is_primary ? PKT_SECRET_KEY : PKT_SECRET_SUBKEY;
+ pkt->pkt.secret_key = sk;
+ add_kbnode(sec_root, new_kbnode( pkt ));
+
+ return 0;
+#else
+ return -1;
+#endif /*!ENABLE_CARD_SUPPORT*/
+}
+
+
+#ifdef ENABLE_CARD_SUPPORT
+int
+save_unprotected_key_to_card (PKT_secret_key *sk, int keyno)
+{
+ int rc;
+ unsigned char *rsa_n = NULL;
+ unsigned char *rsa_e = NULL;
+ unsigned char *rsa_p = NULL;
+ unsigned char *rsa_q = NULL;
+ unsigned int rsa_n_len, rsa_e_len, rsa_p_len, rsa_q_len;
+ unsigned char *sexp = NULL;
+ unsigned char *p;
+ char numbuf[55], numbuf2[50];
+
+ assert (is_RSA (sk->pubkey_algo));
+ assert (!sk->is_protected);
+
+ /* Copy the parameters into straight buffers. */
+ rsa_n = mpi_get_secure_buffer (sk->skey[0], &rsa_n_len, NULL);
+ rsa_e = mpi_get_secure_buffer (sk->skey[1], &rsa_e_len, NULL);
+ rsa_p = mpi_get_secure_buffer (sk->skey[3], &rsa_p_len, NULL);
+ rsa_q = mpi_get_secure_buffer (sk->skey[4], &rsa_q_len, NULL);
+ if (!rsa_n || !rsa_e || !rsa_p || !rsa_q)
+ {
+ rc = G10ERR_INV_ARG;
+ goto leave;
+ }
+
+ /* Put the key into an S-expression. */
+ sexp = p = xmalloc_secure (30
+ + rsa_n_len + rsa_e_len + rsa_p_len + rsa_q_len
+ + 4*sizeof (numbuf) + 25 + sizeof(numbuf) + 20);
+
+ p = stpcpy (p,"(11:private-key(3:rsa(1:n");
+ sprintf (numbuf, "%u:", rsa_n_len);
+ p = stpcpy (p, numbuf);
+ memcpy (p, rsa_n, rsa_n_len);
+ p += rsa_n_len;
+
+ sprintf (numbuf, ")(1:e%u:", rsa_e_len);
+ p = stpcpy (p, numbuf);
+ memcpy (p, rsa_e, rsa_e_len);
+ p += rsa_e_len;
+
+ sprintf (numbuf, ")(1:p%u:", rsa_p_len);
+ p = stpcpy (p, numbuf);
+ memcpy (p, rsa_p, rsa_p_len);
+ p += rsa_p_len;
+
+ sprintf (numbuf, ")(1:q%u:", rsa_q_len);
+ p = stpcpy (p, numbuf);
+ memcpy (p, rsa_q, rsa_q_len);
+ p += rsa_q_len;
+
+ p = stpcpy (p,"))(10:created-at");
+ sprintf (numbuf2, "%lu", (unsigned long)sk->timestamp);
+ sprintf (numbuf, "%lu:", (unsigned long)strlen (numbuf2));
+ p = stpcpy (stpcpy (stpcpy (p, numbuf), numbuf2), "))");
+
+ /* Fixme: Unfortunately we don't have the serialnumber available -
+ thus we can't pass it down to the agent. */
+ rc = agent_scd_writekey (keyno, NULL, sexp, p - sexp);
+
+ leave:
+ xfree (sexp);
+ xfree (rsa_n);
+ xfree (rsa_e);
+ xfree (rsa_p);
+ xfree (rsa_q);
+ return rc;
+}
+#endif /*ENABLE_CARD_SUPPORT*/
diff --git a/g10/keyid.c b/g10/keyid.c
new file mode 100644
index 0000000..86a204f
--- /dev/null
+++ b/g10/keyid.c
@@ -0,0 +1,753 @@
+/* keyid.c - key ID and fingerprint handling
+ * Copyright (C) 1998, 1999, 2000, 2001, 2003,
+ * 2004 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <time.h>
+#include <assert.h>
+#include "util.h"
+#include "main.h"
+#include "packet.h"
+#include "options.h"
+#include "mpi.h"
+#include "keydb.h"
+#include "i18n.h"
+
+int
+pubkey_letter( int algo )
+{
+ switch( algo ) {
+ case PUBKEY_ALGO_RSA: return 'R' ;
+ case PUBKEY_ALGO_RSA_E: return 'r' ;
+ case PUBKEY_ALGO_RSA_S: return 's' ;
+ case PUBKEY_ALGO_ELGAMAL_E: return 'g';
+ case PUBKEY_ALGO_ELGAMAL: return 'G' ;
+ case PUBKEY_ALGO_DSA: return 'D' ;
+ default: return '?';
+ }
+}
+
+/* This function is useful for v4 fingerprints and v3 or v4 key
+ signing. */
+void
+hash_public_key( MD_HANDLE md, PKT_public_key *pk )
+{
+ unsigned n=6;
+ unsigned nb[PUBKEY_MAX_NPKEY];
+ unsigned nn[PUBKEY_MAX_NPKEY];
+ byte *pp[PUBKEY_MAX_NPKEY];
+ int i;
+ int npkey = pubkey_get_npkey( pk->pubkey_algo );
+
+ /* Two extra bytes for the expiration date in v3 */
+ if(pk->version<4)
+ n+=2;
+
+ if(npkey==0 && pk->pkey[0] && mpi_is_opaque(pk->pkey[0]))
+ {
+ pp[0]=mpi_get_opaque(pk->pkey[0],&nn[0]);
+ n+=nn[0];
+ }
+ else
+ for(i=0; i < npkey; i++ )
+ {
+ nb[i] = mpi_get_nbits(pk->pkey[i]);
+ pp[i] = mpi_get_buffer( pk->pkey[i], nn+i, NULL );
+ n += 2 + nn[i];
+ }
+
+ md_putc( md, 0x99 ); /* ctb */
+ /* What does it mean if n is greater than than 0xFFFF ? */
+ md_putc( md, n >> 8 ); /* 2 byte length header */
+ md_putc( md, n );
+ md_putc( md, pk->version );
+
+ md_putc( md, pk->timestamp >> 24 );
+ md_putc( md, pk->timestamp >> 16 );
+ md_putc( md, pk->timestamp >> 8 );
+ md_putc( md, pk->timestamp );
+
+ if(pk->version<4)
+ {
+ u16 days=0;
+ if(pk->expiredate)
+ days=(u16)((pk->expiredate - pk->timestamp) / 86400L);
+
+ md_putc( md, days >> 8 );
+ md_putc( md, days );
+ }
+
+ md_putc( md, pk->pubkey_algo );
+
+ if(npkey==0 && pk->pkey[0] && mpi_is_opaque(pk->pkey[0]))
+ md_write(md,pp[0],nn[0]);
+ else
+ for(i=0; i < npkey; i++ )
+ {
+ md_putc( md, nb[i]>>8);
+ md_putc( md, nb[i] );
+ md_write( md, pp[i], nn[i] );
+ xfree(pp[i]);
+ }
+}
+
+static MD_HANDLE
+do_fingerprint_md( PKT_public_key *pk )
+{
+ MD_HANDLE md;
+
+ md = md_open( DIGEST_ALGO_SHA1, 0);
+ hash_public_key(md,pk);
+ md_final( md );
+
+ return md;
+}
+
+static MD_HANDLE
+do_fingerprint_md_sk( PKT_secret_key *sk )
+{
+ PKT_public_key pk;
+ int npkey = pubkey_get_npkey( sk->pubkey_algo ); /* npkey is correct! */
+ int i;
+
+ if(npkey==0)
+ return NULL;
+
+ pk.pubkey_algo = sk->pubkey_algo;
+ pk.version = sk->version;
+ pk.timestamp = sk->timestamp;
+ pk.expiredate = sk->expiredate;
+ pk.pubkey_algo = sk->pubkey_algo;
+ for( i=0; i < npkey; i++ )
+ pk.pkey[i] = sk->skey[i];
+ return do_fingerprint_md( &pk );
+}
+
+size_t
+keystrlen(void)
+{
+ switch(opt.keyid_format)
+ {
+ case KF_SHORT:
+ return 8;
+
+ case KF_LONG:
+ return 16;
+
+ case KF_0xSHORT:
+ return 10;
+
+ case KF_0xLONG:
+ return 18;
+
+ default:
+ BUG();
+ }
+}
+
+const char *
+keystr(u32 *keyid)
+{
+ static char keyid_str[19];
+
+ switch(opt.keyid_format)
+ {
+ case KF_SHORT:
+ sprintf(keyid_str,"%08lX",(ulong)keyid[1]);
+ break;
+
+ case KF_LONG:
+ if(keyid[0])
+ sprintf(keyid_str,"%08lX%08lX",(ulong)keyid[0],(ulong)keyid[1]);
+ else
+ sprintf(keyid_str,"%08lX",(ulong)keyid[1]);
+ break;
+
+ case KF_0xSHORT:
+ sprintf(keyid_str,"0x%08lX",(ulong)keyid[1]);
+ break;
+
+ case KF_0xLONG:
+ if(keyid[0])
+ sprintf(keyid_str,"0x%08lX%08lX",(ulong)keyid[0],(ulong)keyid[1]);
+ else
+ sprintf(keyid_str,"0x%08lX",(ulong)keyid[1]);
+ break;
+
+ default:
+ BUG();
+ }
+
+ return keyid_str;
+}
+
+const char *
+keystr_from_pk(PKT_public_key *pk)
+{
+ keyid_from_pk(pk,NULL);
+
+ return keystr(pk->keyid);
+}
+
+const char *
+keystr_from_sk(PKT_secret_key *sk)
+{
+ keyid_from_sk(sk,NULL);
+
+ return keystr(sk->keyid);
+}
+
+const char *
+keystr_from_desc(KEYDB_SEARCH_DESC *desc)
+{
+ switch(desc->mode)
+ {
+ case KEYDB_SEARCH_MODE_LONG_KID:
+ case KEYDB_SEARCH_MODE_SHORT_KID:
+ return keystr(desc->u.kid);
+
+ case KEYDB_SEARCH_MODE_FPR20:
+ {
+ u32 keyid[2];
+
+ keyid[0] = (unsigned char)desc->u.fpr[12] << 24
+ | (unsigned char)desc->u.fpr[13] << 16
+ | (unsigned char)desc->u.fpr[14] << 8
+ | (unsigned char)desc->u.fpr[15] ;
+ keyid[1] = (unsigned char)desc->u.fpr[16] << 24
+ | (unsigned char)desc->u.fpr[17] << 16
+ | (unsigned char)desc->u.fpr[18] << 8
+ | (unsigned char)desc->u.fpr[19] ;
+
+ return keystr(keyid);
+ }
+
+ case KEYDB_SEARCH_MODE_FPR16:
+ return "?v3 fpr?";
+
+ default:
+ BUG();
+ }
+}
+
+/****************
+ * Get the keyid from the secret key and put it into keyid
+ * if this is not NULL. Return the 32 low bits of the keyid.
+ */
+u32
+keyid_from_sk( PKT_secret_key *sk, u32 *keyid )
+{
+ u32 lowbits;
+ u32 dummy_keyid[2];
+
+ if( !keyid )
+ keyid = dummy_keyid;
+
+ if( sk->keyid[0] || sk->keyid[1] )
+ {
+ keyid[0] = sk->keyid[0];
+ keyid[1] = sk->keyid[1];
+ lowbits = keyid[1];
+ }
+ else if( sk->version < 4 )
+ {
+ if( is_RSA(sk->pubkey_algo) )
+ {
+ lowbits = pubkey_get_npkey(sk->pubkey_algo) ?
+ mpi_get_keyid( sk->skey[0], keyid ) : 0; /* take n */
+ sk->keyid[0]=keyid[0];
+ sk->keyid[1]=keyid[1];
+ }
+ else
+ sk->keyid[0]=sk->keyid[1]=keyid[0]=keyid[1]=lowbits=0xFFFFFFFF;
+ }
+ else
+ {
+ const byte *dp;
+ MD_HANDLE md;
+ md = do_fingerprint_md_sk(sk);
+ if(md)
+ {
+ dp = md_read( md, 0 );
+ keyid[0] = dp[12] << 24 | dp[13] << 16 | dp[14] << 8 | dp[15] ;
+ keyid[1] = dp[16] << 24 | dp[17] << 16 | dp[18] << 8 | dp[19] ;
+ lowbits = keyid[1];
+ md_close(md);
+ sk->keyid[0] = keyid[0];
+ sk->keyid[1] = keyid[1];
+ }
+ else
+ sk->keyid[0]=sk->keyid[1]=keyid[0]=keyid[1]=lowbits=0xFFFFFFFF;
+ }
+
+ return lowbits;
+}
+
+
+/****************
+ * Get the keyid from the public key and put it into keyid
+ * if this is not NULL. Return the 32 low bits of the keyid.
+ */
+u32
+keyid_from_pk( PKT_public_key *pk, u32 *keyid )
+{
+ u32 lowbits;
+ u32 dummy_keyid[2];
+
+ if( !keyid )
+ keyid = dummy_keyid;
+
+ if( pk->keyid[0] || pk->keyid[1] )
+ {
+ keyid[0] = pk->keyid[0];
+ keyid[1] = pk->keyid[1];
+ lowbits = keyid[1];
+ }
+ else if( pk->version < 4 )
+ {
+ if( is_RSA(pk->pubkey_algo) )
+ {
+ lowbits = pubkey_get_npkey(pk->pubkey_algo) ?
+ mpi_get_keyid( pk->pkey[0], keyid ) : 0 ; /* from n */
+ pk->keyid[0] = keyid[0];
+ pk->keyid[1] = keyid[1];
+ }
+ else
+ pk->keyid[0]=pk->keyid[1]=keyid[0]=keyid[1]=lowbits=0xFFFFFFFF;
+ }
+ else
+ {
+ const byte *dp;
+ MD_HANDLE md;
+ md = do_fingerprint_md(pk);
+ if(md)
+ {
+ dp = md_read( md, 0 );
+ keyid[0] = dp[12] << 24 | dp[13] << 16 | dp[14] << 8 | dp[15] ;
+ keyid[1] = dp[16] << 24 | dp[17] << 16 | dp[18] << 8 | dp[19] ;
+ lowbits = keyid[1];
+ md_close(md);
+ pk->keyid[0] = keyid[0];
+ pk->keyid[1] = keyid[1];
+ }
+ else
+ pk->keyid[0]=pk->keyid[1]=keyid[0]=keyid[1]=lowbits=0xFFFFFFFF;
+ }
+
+ return lowbits;
+}
+
+
+/****************
+ * Get the keyid from the fingerprint. This function is simple for most
+ * keys, but has to do a keylookup for old stayle keys.
+ */
+u32
+keyid_from_fingerprint( const byte *fprint, size_t fprint_len, u32 *keyid )
+{
+ u32 dummy_keyid[2];
+
+ if( !keyid )
+ keyid = dummy_keyid;
+
+ if( fprint_len != 20 ) {
+ /* This is special as we have to lookup the key first */
+ PKT_public_key pk;
+ int rc;
+
+ memset( &pk, 0, sizeof pk );
+ rc = get_pubkey_byfprint( &pk, fprint, fprint_len );
+ if( rc ) {
+ log_error("Oops: keyid_from_fingerprint: no pubkey\n");
+ keyid[0] = 0;
+ keyid[1] = 0;
+ }
+ else
+ keyid_from_pk( &pk, keyid );
+ }
+ else {
+ const byte *dp = fprint;
+ keyid[0] = dp[12] << 24 | dp[13] << 16 | dp[14] << 8 | dp[15] ;
+ keyid[1] = dp[16] << 24 | dp[17] << 16 | dp[18] << 8 | dp[19] ;
+ }
+
+ return keyid[1];
+}
+
+
+u32
+keyid_from_sig( PKT_signature *sig, u32 *keyid )
+{
+ if( keyid ) {
+ keyid[0] = sig->keyid[0];
+ keyid[1] = sig->keyid[1];
+ }
+ return sig->keyid[1];
+}
+
+byte *
+namehash_from_uid(PKT_user_id *uid)
+{
+ if(uid->namehash==NULL)
+ {
+ uid->namehash=xmalloc(20);
+
+ if(uid->attrib_data)
+ rmd160_hash_buffer(uid->namehash,uid->attrib_data,uid->attrib_len);
+ else
+ rmd160_hash_buffer(uid->namehash,uid->name,uid->len);
+ }
+
+ return uid->namehash;
+}
+
+/****************
+ * return the number of bits used in the pk
+ */
+unsigned
+nbits_from_pk( PKT_public_key *pk )
+{
+ return pubkey_nbits( pk->pubkey_algo, pk->pkey );
+}
+
+/****************
+ * return the number of bits used in the sk
+ */
+unsigned
+nbits_from_sk( PKT_secret_key *sk )
+{
+ return pubkey_nbits( sk->pubkey_algo, sk->skey );
+}
+
+static const char *
+mk_datestr (char *buffer, time_t atime)
+{
+ struct tm *tp;
+
+ if ( atime < 0 ) /* 32 bit time_t and after 2038-01-19 */
+ strcpy (buffer, "????" "-??" "-??"); /* mark this as invalid */
+ else {
+ tp = gmtime (&atime);
+ sprintf (buffer,"%04d-%02d-%02d",
+ 1900+tp->tm_year, tp->tm_mon+1, tp->tm_mday );
+ }
+ return buffer;
+}
+
+/****************
+ * return a string with the creation date of the pk
+ * Note: this is alloced in a static buffer.
+ * Format is: yyyy-mm-dd
+ */
+const char *
+datestr_from_pk( PKT_public_key *pk )
+{
+ static char buffer[11+5];
+ time_t atime = pk->timestamp;
+
+ return mk_datestr (buffer, atime);
+}
+
+const char *
+datestr_from_sk( PKT_secret_key *sk )
+{
+ static char buffer[11+5];
+ time_t atime = sk->timestamp;
+
+ return mk_datestr (buffer, atime);
+}
+
+const char *
+datestr_from_sig( PKT_signature *sig )
+{
+ static char buffer[11+5];
+ time_t atime = sig->timestamp;
+
+ return mk_datestr (buffer, atime);
+}
+
+const char *
+expirestr_from_pk( PKT_public_key *pk )
+{
+ static char buffer[11+5];
+ time_t atime;
+
+ if( !pk->expiredate )
+ return _("never ");
+ atime = pk->expiredate;
+ return mk_datestr (buffer, atime);
+}
+
+const char *
+expirestr_from_sk( PKT_secret_key *sk )
+{
+ static char buffer[11+5];
+ time_t atime;
+
+ if( !sk->expiredate )
+ return _("never ");
+ atime = sk->expiredate;
+ return mk_datestr (buffer, atime);
+}
+
+const char *
+expirestr_from_sig( PKT_signature *sig )
+{
+ static char buffer[11+5];
+ time_t atime;
+
+ if(!sig->expiredate)
+ return _("never ");
+ atime=sig->expiredate;
+ return mk_datestr (buffer, atime);
+}
+
+const char *
+revokestr_from_pk( PKT_public_key *pk )
+{
+ static char buffer[11+5];
+ time_t atime;
+
+ if(!pk->revoked.date)
+ return _("never ");
+ atime=pk->revoked.date;
+ return mk_datestr (buffer, atime);
+}
+
+
+const char *
+usagestr_from_pk( PKT_public_key *pk )
+{
+ static char buffer[10];
+ int i = 0;
+ unsigned int use = pk->pubkey_usage;
+
+ if ( use & PUBKEY_USAGE_SIG )
+ buffer[i++] = 'S';
+
+ if ( use & PUBKEY_USAGE_CERT )
+ buffer[i++] = 'C';
+
+ if ( use & PUBKEY_USAGE_ENC )
+ buffer[i++] = 'E';
+
+ if ( (use & PUBKEY_USAGE_AUTH) )
+ buffer[i++] = 'A';
+
+ while (i < 4)
+ buffer[i++] = ' ';
+
+ buffer[i] = 0;
+ return buffer;
+}
+
+
+const char *
+colon_strtime (u32 t)
+{
+ if (!t)
+ return "";
+ if (opt.fixed_list_mode) {
+ static char buf[15];
+ sprintf (buf, "%lu", (ulong)t);
+ return buf;
+ }
+ return strtimestamp(t);
+}
+
+const char *
+colon_datestr_from_pk (PKT_public_key *pk)
+{
+ if (opt.fixed_list_mode) {
+ static char buf[15];
+ sprintf (buf, "%lu", (ulong)pk->timestamp);
+ return buf;
+ }
+ return datestr_from_pk (pk);
+}
+
+const char *
+colon_datestr_from_sk (PKT_secret_key *sk)
+{
+ if (opt.fixed_list_mode) {
+ static char buf[15];
+ sprintf (buf, "%lu", (ulong)sk->timestamp);
+ return buf;
+ }
+ return datestr_from_sk (sk);
+}
+
+const char *
+colon_datestr_from_sig (PKT_signature *sig)
+{
+ if (opt.fixed_list_mode) {
+ static char buf[15];
+ sprintf (buf, "%lu", (ulong)sig->timestamp);
+ return buf;
+ }
+ return datestr_from_sig (sig);
+}
+
+const char *
+colon_expirestr_from_sig (PKT_signature *sig)
+{
+ if(!sig->expiredate)
+ return "";
+ if (opt.fixed_list_mode) {
+ static char buf[15];
+ sprintf (buf, "%lu", (ulong)sig->expiredate);
+ return buf;
+ }
+ return expirestr_from_sig (sig);
+}
+
+
+/**************** .
+ * Return a byte array with the fingerprint for the given PK/SK
+ * The length of the array is returned in ret_len. Caller must free
+ * the array or provide an array of length MAX_FINGERPRINT_LEN.
+ */
+
+byte *
+fingerprint_from_pk( PKT_public_key *pk, byte *array, size_t *ret_len )
+{
+ byte *p, *buf;
+ const byte *dp;
+ size_t len;
+ unsigned int n;
+
+ if( pk->version < 4 )
+ {
+ if( is_RSA(pk->pubkey_algo) )
+ {
+ /* RSA in version 3 packets is special */
+ MD_HANDLE md;
+
+ md = md_open( DIGEST_ALGO_MD5, 0);
+ if( pubkey_get_npkey( pk->pubkey_algo ) > 1 ) {
+ p = buf = mpi_get_buffer( pk->pkey[0], &n, NULL );
+ md_write( md, p, n );
+ xfree(buf);
+ p = buf = mpi_get_buffer( pk->pkey[1], &n, NULL );
+ md_write( md, p, n );
+ xfree(buf);
+ }
+ md_final(md);
+ if( !array )
+ array = xmalloc( 16 );
+ len = 16;
+ memcpy(array, md_read(md, DIGEST_ALGO_MD5), 16 );
+ md_close(md);
+ }
+ else
+ {
+ if(!array)
+ array=xmalloc(16);
+ len=16;
+ memset(array,0,16);
+ }
+ }
+ else {
+ MD_HANDLE md;
+ md = do_fingerprint_md(pk);
+ dp = md_read( md, 0 );
+ len = md_digest_length( md_get_algo( md ) );
+ assert( len <= MAX_FINGERPRINT_LEN );
+ if( !array )
+ array = xmalloc( len );
+ memcpy(array, dp, len );
+ pk->keyid[0] = dp[12] << 24 | dp[13] << 16 | dp[14] << 8 | dp[15] ;
+ pk->keyid[1] = dp[16] << 24 | dp[17] << 16 | dp[18] << 8 | dp[19] ;
+ md_close(md);
+ }
+
+ *ret_len = len;
+ return array;
+}
+
+byte *
+fingerprint_from_sk( PKT_secret_key *sk, byte *array, size_t *ret_len )
+{
+ byte *p, *buf;
+ const char *dp;
+ size_t len;
+ unsigned n;
+
+ if( sk->version < 4 )
+ {
+ if( is_RSA(sk->pubkey_algo) )
+ {
+ /* RSA in version 3 packets is special */
+ MD_HANDLE md;
+
+ md = md_open( DIGEST_ALGO_MD5, 0);
+ if( pubkey_get_npkey( sk->pubkey_algo ) > 1 ) {
+ p = buf = mpi_get_buffer( sk->skey[0], &n, NULL );
+ md_write( md, p, n );
+ xfree(buf);
+ p = buf = mpi_get_buffer( sk->skey[1], &n, NULL );
+ md_write( md, p, n );
+ xfree(buf);
+ }
+ md_final(md);
+ if( !array )
+ array = xmalloc( 16 );
+ len = 16;
+ memcpy(array, md_read(md, DIGEST_ALGO_MD5), 16 );
+ md_close(md);
+ }
+ else
+ {
+ if(!array)
+ array=xmalloc(16);
+ len=16;
+ memset(array,0,16);
+ }
+ }
+ else {
+ MD_HANDLE md;
+ md = do_fingerprint_md_sk(sk);
+ if(md)
+ {
+ dp = md_read( md, 0 );
+ len = md_digest_length( md_get_algo( md ) );
+ assert( len <= MAX_FINGERPRINT_LEN );
+ if( !array )
+ array = xmalloc( len );
+ memcpy(array, dp, len );
+ md_close(md);
+ }
+ else
+ {
+ len=MAX_FINGERPRINT_LEN;
+ if(!array)
+ array=xmalloc(len);
+ memset(array,0,len);
+ }
+ }
+
+ *ret_len = len;
+ return array;
+}
diff --git a/g10/keylist.c b/g10/keylist.c
new file mode 100644
index 0000000..19f3d3f
--- /dev/null
+++ b/g10/keylist.c
@@ -0,0 +1,1638 @@
+/* keylist.c
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003,
+ * 2004, 2005 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "options.h"
+#include "packet.h"
+#include "errors.h"
+#include "keydb.h"
+#include "memory.h"
+#include "photoid.h"
+#include "util.h"
+#include "ttyio.h"
+#include "trustdb.h"
+#include "main.h"
+#include "i18n.h"
+#include "status.h"
+
+static void list_all(int);
+static void list_one( STRLIST names, int secret);
+static void print_card_serialno (PKT_secret_key *sk);
+
+struct sig_stats
+{
+ int inv_sigs;
+ int no_key;
+ int oth_err;
+};
+
+static FILE *attrib_fp=NULL;
+
+/****************
+ * List the keys
+ * If list is NULL, all available keys are listed
+ */
+void
+public_key_list( STRLIST list )
+{
+ if(opt.with_colons)
+ {
+ byte trust_model,marginals,completes,cert_depth;
+ ulong created,nextcheck;
+
+ read_trust_options(&trust_model,&created,&nextcheck,
+ &marginals,&completes,&cert_depth);
+
+ printf("tru:");
+
+ if(nextcheck && nextcheck <= make_timestamp())
+ printf("o");
+ if(trust_model!=opt.trust_model)
+ printf("t");
+ if(opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC)
+ {
+ if(marginals!=opt.marginals_needed)
+ printf("m");
+ if(completes!=opt.completes_needed)
+ printf("c");
+ if(cert_depth!=opt.max_cert_depth)
+ printf("d");
+ }
+
+ printf(":%d:%lu:%lu",trust_model,created,nextcheck);
+
+ /* Only show marginals, completes, and cert_depth in the classic
+ or PGP trust models since they are not meaningful
+ otherwise. */
+
+ if(trust_model==TM_PGP || trust_model==TM_CLASSIC)
+ printf(":%d:%d:%d",marginals,completes,cert_depth);
+
+ printf("\n");
+ }
+
+ /* We need to do the stale check right here because it might need to
+ update the keyring while we already have the keyring open. This
+ is very bad for W32 because of a sharing violation. For real OSes
+ it might lead to false results if we are later listing a keyring
+ which is associated with the inode of a deleted file. */
+ check_trustdb_stale ();
+
+ if( !list )
+ list_all(0);
+ else
+ list_one( list, 0 );
+}
+
+void
+secret_key_list( STRLIST list )
+{
+ check_trustdb_stale ();
+
+ if( !list )
+ list_all(1);
+ else /* List by user id */
+ list_one( list, 1 );
+}
+
+void
+print_seckey_info (PKT_secret_key *sk)
+{
+ u32 keyid[2];
+ char *p;
+
+ keyid_from_sk (sk, keyid);
+ p=get_user_id_native(keyid);
+
+ tty_printf ("\nsec %4u%c/%s %s %s\n",
+ nbits_from_sk (sk),
+ pubkey_letter (sk->pubkey_algo),
+ keystr(keyid), datestr_from_sk (sk), p);
+
+ xfree (p);
+}
+
+/* Print information about the public key. With FP passed as NULL,
+ the tty output interface is used, otherwise output is directted to
+ the given stream. */
+void
+print_pubkey_info (FILE *fp, PKT_public_key *pk)
+{
+ u32 keyid[2];
+ char *p;
+
+ keyid_from_pk (pk, keyid);
+
+ /* If the pk was chosen by a particular user ID, that is the one to
+ print. */
+ if(pk->user_id)
+ p=utf8_to_native(pk->user_id->name,pk->user_id->len,0);
+ else
+ p=get_user_id_native(keyid);
+
+ if (fp)
+ fprintf (fp, "pub %4u%c/%s %s %s\n",
+ nbits_from_pk (pk),
+ pubkey_letter (pk->pubkey_algo),
+ keystr(keyid), datestr_from_pk (pk), p);
+ else
+ tty_printf ("\npub %4u%c/%s %s %s\n",
+ nbits_from_pk (pk), pubkey_letter (pk->pubkey_algo),
+ keystr(keyid), datestr_from_pk (pk), p);
+
+ xfree (p);
+}
+
+
+/* Print basic information of a secret key including the card serial
+ number information. */
+void
+print_card_key_info (FILE *fp, KBNODE keyblock)
+{
+ KBNODE node;
+ int i;
+
+ for (node = keyblock; node; node = node->next )
+ {
+ if (node->pkt->pkttype == PKT_SECRET_KEY
+ || (node->pkt->pkttype == PKT_SECRET_SUBKEY) )
+ {
+ PKT_secret_key *sk = node->pkt->pkt.secret_key;
+
+ tty_fprintf (fp, "%s%c %4u%c/%s ",
+ node->pkt->pkttype == PKT_SECRET_KEY? "sec":"ssb",
+ (sk->protect.s2k.mode==1001)?'#':
+ (sk->protect.s2k.mode==1002)?'>':' ',
+ nbits_from_sk (sk),
+ pubkey_letter (sk->pubkey_algo),
+ keystr_from_sk(sk));
+ tty_fprintf (fp, _("created: %s"), datestr_from_sk (sk));
+ tty_fprintf (fp, " ");
+ tty_fprintf (fp, _("expires: %s"), expirestr_from_sk (sk));
+ if (sk->is_protected && sk->protect.s2k.mode == 1002)
+ {
+ tty_fprintf (fp, "\n ");
+ tty_fprintf (fp, _("card-no: "));
+ if (sk->protect.ivlen == 16
+ && !memcmp (sk->protect.iv, "\xD2\x76\x00\x01\x24\x01", 6))
+ {
+ /* This is an OpenPGP card. */
+ for (i=8; i < 14; i++)
+ {
+ if (i == 10)
+ tty_fprintf (fp, " ");
+ tty_fprintf (fp, "%02X", sk->protect.iv[i]);
+ }
+ }
+ else
+ { /* Something is wrong: Print all. */
+ for (i=0; i < sk->protect.ivlen; i++)
+ tty_fprintf (fp, "%02X", sk->protect.iv[i]);
+ }
+ }
+ tty_fprintf (fp, "\n");
+ }
+ }
+}
+
+
+
+/* Flags = 0x01 hashed 0x02 critical */
+static void
+status_one_subpacket(sigsubpkttype_t type,size_t len,int flags,const byte *buf)
+{
+ char status[40];
+
+ /* Don't print these. */
+ if(len>256)
+ return;
+
+ sprintf(status,"%d %u %u ",type,flags,(unsigned int)len);
+
+ write_status_text_and_buffer(STATUS_SIG_SUBPACKET,status,buf,len,0);
+}
+
+/*
+ mode=0 for stdout.
+ mode=1 for log_info + status messages
+ mode=2 for status messages only
+*/
+
+void
+show_policy_url(PKT_signature *sig,int indent,int mode)
+{
+ const byte *p;
+ size_t len;
+ int seq=0,crit;
+ FILE *fp=mode?log_stream():stdout;
+
+ while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_POLICY,&len,&seq,&crit)))
+ {
+ if(mode!=2)
+ {
+ int i;
+ const char *str;
+
+ for(i=0;i<indent;i++)
+ putchar(' ');
+
+ if(crit)
+ str=_("Critical signature policy: ");
+ else
+ str=_("Signature policy: ");
+ if(mode)
+ log_info("%s",str);
+ else
+ printf("%s",str);
+ print_utf8_string(fp,p,len);
+ fprintf(fp,"\n");
+ }
+
+ if(mode)
+ write_status_buffer ( STATUS_POLICY_URL, p, len, 0 );
+ }
+}
+
+/*
+ mode=0 for stdout.
+ mode=1 for log_info + status messages
+ mode=2 for status messages only
+*/
+/* TODO: use this */
+void
+show_keyserver_url(PKT_signature *sig,int indent,int mode)
+{
+ const byte *p;
+ size_t len;
+ int seq=0,crit;
+ FILE *fp=mode?log_stream():stdout;
+
+ while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_PREF_KS,&len,&seq,&crit)))
+ {
+ if(mode!=2)
+ {
+ int i;
+ const char *str;
+
+ for(i=0;i<indent;i++)
+ putchar(' ');
+
+ if(crit)
+ str=_("Critical preferred keyserver: ");
+ else
+ str=_("Preferred keyserver: ");
+ if(mode)
+ log_info("%s",str);
+ else
+ printf("%s",str);
+ print_utf8_string(fp,p,len);
+ fprintf(fp,"\n");
+ }
+
+ if(mode)
+ status_one_subpacket(SIGSUBPKT_PREF_KS,len,(crit?0x02:0)|0x01,p);
+ }
+}
+
+/*
+ mode=0 for stdout.
+ mode=1 for log_info + status messages
+ mode=2 for status messages only
+
+ which bits:
+ 1 == standard notations
+ 2 == user notations
+*/
+
+void
+show_notation(PKT_signature *sig,int indent,int mode,int which)
+{
+ FILE *fp=mode?log_stream():stdout;
+ struct notation *nd,*notations;
+
+ if(which==0)
+ which=3;
+
+ notations=sig_to_notation(sig);
+
+ /* There may be multiple notations in the same sig. */
+ for(nd=notations;nd;nd=nd->next)
+ {
+ if(mode!=2)
+ {
+ int has_at=!!strchr(nd->name,'@');
+
+ if((which&1 && !has_at) || (which&2 && has_at))
+ {
+ int i;
+ const char *str;
+
+ for(i=0;i<indent;i++)
+ putchar(' ');
+
+ if(nd->flags.critical)
+ str=_("Critical signature notation: ");
+ else
+ str=_("Signature notation: ");
+ if(mode)
+ log_info("%s",str);
+ else
+ printf("%s",str);
+ /* This is all UTF8 */
+ print_utf8_string(fp,nd->name,strlen(nd->name));
+ fprintf(fp,"=");
+ print_utf8_string(fp,nd->value,strlen(nd->value));
+ fprintf(fp,"\n");
+ }
+ }
+
+ if(mode)
+ {
+ write_status_buffer(STATUS_NOTATION_NAME,
+ nd->name,strlen(nd->name),0);
+ write_status_buffer(STATUS_NOTATION_DATA,
+ nd->value,strlen(nd->value),50);
+ }
+ }
+
+ free_notation(notations);
+}
+
+static void
+print_signature_stats(struct sig_stats *s)
+{
+ if( s->inv_sigs == 1 )
+ tty_printf(_("1 bad signature\n") );
+ else if( s->inv_sigs )
+ tty_printf(_("%d bad signatures\n"), s->inv_sigs );
+ if( s->no_key == 1 )
+ tty_printf(_("1 signature not checked due to a missing key\n") );
+ else if( s->no_key )
+ tty_printf(_("%d signatures not checked due to missing keys\n"),s->no_key);
+ if( s->oth_err == 1 )
+ tty_printf(_("1 signature not checked due to an error\n") );
+ else if( s->oth_err )
+ tty_printf(_("%d signatures not checked due to errors\n"), s->oth_err );
+}
+
+static void
+list_all( int secret )
+{
+ KEYDB_HANDLE hd;
+ KBNODE keyblock = NULL;
+ int rc=0;
+ const char *lastresname, *resname;
+ struct sig_stats stats;
+
+ memset(&stats,0,sizeof(stats));
+
+ hd = keydb_new (secret);
+ if (!hd)
+ rc = G10ERR_GENERAL;
+ else
+ rc = keydb_search_first (hd);
+ if( rc ) {
+ if( rc != -1 )
+ log_error("keydb_search_first failed: %s\n", g10_errstr(rc) );
+ goto leave;
+ }
+
+ lastresname = NULL;
+ do {
+ rc = keydb_get_keyblock (hd, &keyblock);
+ if (rc) {
+ log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc));
+ goto leave;
+ }
+ if(!opt.with_colons)
+ {
+ resname = keydb_get_resource_name (hd);
+ if (lastresname != resname )
+ {
+ int i;
+
+ printf("%s\n", resname );
+ for(i=strlen(resname); i; i-- )
+ putchar('-');
+ putchar('\n');
+ lastresname = resname;
+ }
+ }
+ merge_keys_and_selfsig( keyblock );
+ list_keyblock( keyblock, secret, opt.fingerprint,
+ opt.check_sigs?&stats:NULL);
+ release_kbnode( keyblock );
+ keyblock = NULL;
+ } while (!(rc = keydb_search_next (hd)));
+ if( rc && rc != -1 )
+ log_error ("keydb_search_next failed: %s\n", g10_errstr(rc));
+
+ if(opt.check_sigs && !opt.with_colons)
+ print_signature_stats(&stats);
+
+ leave:
+ release_kbnode (keyblock);
+ keydb_release (hd);
+}
+
+
+static void
+list_one( STRLIST names, int secret )
+{
+ int rc = 0;
+ KBNODE keyblock = NULL;
+ GETKEY_CTX ctx;
+ const char *resname;
+ const char *keyring_str = _("Keyring");
+ int i;
+ struct sig_stats stats;
+
+ memset(&stats,0,sizeof(stats));
+
+ /* fixme: using the bynames function has the disadvantage that we
+ * don't know wether one of the names given was not found. OTOH,
+ * this function has the advantage to list the names in the
+ * sequence as defined by the keyDB and does not duplicate
+ * outputs. A solution could be do test whether all given have
+ * been listed (this needs a way to use the keyDB search
+ * functions) or to have the search function return indicators for
+ * found names. Yet another way is to use the keydb search
+ * facilities directly. */
+ if( secret ) {
+ rc = get_seckey_bynames( &ctx, NULL, names, &keyblock );
+ if( rc ) {
+ log_error("error reading key: %s\n", g10_errstr(rc) );
+ get_seckey_end( ctx );
+ return;
+ }
+ do {
+ if ((opt.list_options&LIST_SHOW_KEYRING) && !opt.with_colons) {
+ resname = keydb_get_resource_name (get_ctx_handle(ctx));
+ printf("%s: %s\n", keyring_str, resname);
+ for(i = strlen(resname) + strlen(keyring_str) + 2; i; i-- )
+ putchar('-');
+ putchar('\n');
+ }
+ list_keyblock( keyblock, 1, opt.fingerprint, NULL );
+ release_kbnode( keyblock );
+ } while( !get_seckey_next( ctx, NULL, &keyblock ) );
+ get_seckey_end( ctx );
+ }
+ else {
+ rc = get_pubkey_bynames( &ctx, NULL, names, &keyblock );
+ if( rc ) {
+ log_error("error reading key: %s\n", g10_errstr(rc) );
+ get_pubkey_end( ctx );
+ return;
+ }
+ do {
+ if ((opt.list_options&LIST_SHOW_KEYRING) && !opt.with_colons) {
+ resname = keydb_get_resource_name (get_ctx_handle(ctx));
+ printf("%s: %s\n", keyring_str, resname);
+ for(i = strlen(resname) + strlen(keyring_str) + 2; i; i-- )
+ putchar('-');
+ putchar('\n');
+ }
+ list_keyblock( keyblock, 0, opt.fingerprint,
+ opt.check_sigs?&stats:NULL );
+ release_kbnode( keyblock );
+ } while( !get_pubkey_next( ctx, NULL, &keyblock ) );
+ get_pubkey_end( ctx );
+ }
+
+ if(opt.check_sigs && !opt.with_colons)
+ print_signature_stats(&stats);
+}
+
+static void
+print_key_data( PKT_public_key *pk )
+{
+ int n = pk ? pubkey_get_npkey( pk->pubkey_algo ) : 0;
+ int i;
+
+ for(i=0; i < n; i++ ) {
+ printf("pkd:%d:%u:", i, mpi_get_nbits( pk->pkey[i] ) );
+ mpi_print(stdout, pk->pkey[i], 1 );
+ putchar(':');
+ putchar('\n');
+ }
+}
+
+static void
+print_capabilities (PKT_public_key *pk, PKT_secret_key *sk, KBNODE keyblock)
+{
+ if(pk || (sk && sk->protect.s2k.mode!=1001))
+ {
+ unsigned int use = pk? pk->pubkey_usage : sk->pubkey_usage;
+
+ if ( use & PUBKEY_USAGE_ENC )
+ putchar ('e');
+
+ if ( use & PUBKEY_USAGE_SIG )
+ {
+ putchar ('s');
+ if( pk? pk->is_primary : sk->is_primary )
+ putchar ('c');
+ }
+
+ if ( (use & PUBKEY_USAGE_AUTH) )
+ putchar ('a');
+ }
+
+ if ( keyblock ) { /* figure out the usable capabilities */
+ KBNODE k;
+ int enc=0, sign=0, cert=0, auth=0, disabled=0;
+
+ for (k=keyblock; k; k = k->next ) {
+ if ( k->pkt->pkttype == PKT_PUBLIC_KEY
+ || k->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+ pk = k->pkt->pkt.public_key;
+
+ if(pk->is_primary)
+ disabled=pk_is_disabled(pk);
+
+ if ( pk->is_valid && !pk->is_revoked && !pk->has_expired ) {
+ if ( pk->pubkey_usage & PUBKEY_USAGE_ENC )
+ enc = 1;
+ if ( pk->pubkey_usage & PUBKEY_USAGE_SIG )
+ {
+ sign = 1;
+ if(pk->is_primary)
+ cert = 1;
+ }
+ if ( (pk->pubkey_usage & PUBKEY_USAGE_AUTH) )
+ auth = 1;
+ }
+ }
+ else if ( k->pkt->pkttype == PKT_SECRET_KEY
+ || k->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+ sk = k->pkt->pkt.secret_key;
+ if ( sk->is_valid && !sk->is_revoked && !sk->has_expired
+ && sk->protect.s2k.mode!=1001 ) {
+ if ( sk->pubkey_usage & PUBKEY_USAGE_ENC )
+ enc = 1;
+ if ( sk->pubkey_usage & PUBKEY_USAGE_SIG )
+ {
+ sign = 1;
+ if(sk->is_primary)
+ cert = 1;
+ }
+ if ( (sk->pubkey_usage & PUBKEY_USAGE_AUTH) )
+ auth = 1;
+ }
+ }
+ }
+ if (enc)
+ putchar ('E');
+ if (sign)
+ putchar ('S');
+ if (cert)
+ putchar ('C');
+ if (auth)
+ putchar ('A');
+ if (disabled)
+ putchar ('D');
+ }
+
+ putchar(':');
+}
+
+/* Flags = 0x01 hashed 0x02 critical */
+static void
+print_one_subpacket(sigsubpkttype_t type,size_t len,int flags,const byte *buf)
+{
+ size_t i;
+
+ printf("spk:%d:%u:%u:",type,flags,(unsigned int)len);
+
+ for(i=0;i<len;i++)
+ {
+ /* printable ascii other than : and % */
+ if(buf[i]>=32 && buf[i]<=126 && buf[i]!=':' && buf[i]!='%')
+ printf("%c",buf[i]);
+ else
+ printf("%%%02X",buf[i]);
+ }
+
+ printf("\n");
+}
+
+void
+print_subpackets_colon(PKT_signature *sig)
+{
+ byte *i;
+
+ assert(opt.show_subpackets);
+
+ for(i=opt.show_subpackets;*i;i++)
+ {
+ const byte *p;
+ size_t len;
+ int seq,crit;
+
+ seq=0;
+
+ while((p=enum_sig_subpkt(sig->hashed,*i,&len,&seq,&crit)))
+ print_one_subpacket(*i,len,0x01|(crit?0x02:0),p);
+
+ seq=0;
+
+ while((p=enum_sig_subpkt(sig->unhashed,*i,&len,&seq,&crit)))
+ print_one_subpacket(*i,len,0x00|(crit?0x02:0),p);
+ }
+}
+
+void
+dump_attribs(const PKT_user_id *uid,PKT_public_key *pk,PKT_secret_key *sk)
+{
+ int i;
+
+ if(!attrib_fp)
+ return;
+
+ for(i=0;i<uid->numattribs;i++)
+ {
+ if(is_status_enabled())
+ {
+ byte array[MAX_FINGERPRINT_LEN], *p;
+ char buf[(MAX_FINGERPRINT_LEN*2)+90];
+ size_t j,n;
+
+ if(pk)
+ fingerprint_from_pk( pk, array, &n );
+ else if(sk)
+ fingerprint_from_sk( sk, array, &n );
+ else
+ BUG();
+
+ p = array;
+ for(j=0; j < n ; j++, p++ )
+ sprintf(buf+2*j, "%02X", *p );
+
+ sprintf(buf+strlen(buf)," %lu %u %u %u %lu %lu %u",
+ (ulong)uid->attribs[i].len,uid->attribs[i].type,i+1,
+ uid->numattribs,(ulong)uid->created,(ulong)uid->expiredate,
+ ((uid->is_primary?0x01:0)|
+ (uid->is_revoked?0x02:0)|
+ (uid->is_expired?0x04:0)));
+ write_status_text(STATUS_ATTRIBUTE,buf);
+ }
+
+ fwrite(uid->attribs[i].data,uid->attribs[i].len,1,attrib_fp);
+ }
+}
+
+static void
+list_keyblock_print ( KBNODE keyblock, int secret, int fpr, void *opaque )
+{
+ int rc = 0;
+ KBNODE kbctx;
+ KBNODE node;
+ PKT_public_key *pk;
+ PKT_secret_key *sk;
+ struct sig_stats *stats=opaque;
+ int skip_sigs=0;
+
+ /* get the keyid from the keyblock */
+ node = find_kbnode( keyblock, secret? PKT_SECRET_KEY : PKT_PUBLIC_KEY );
+ if( !node ) {
+ log_error("Oops; key lost!\n");
+ dump_kbnode( keyblock );
+ return;
+ }
+
+ if( secret )
+ {
+ pk = NULL;
+ sk = node->pkt->pkt.secret_key;
+
+ printf("sec%c %4u%c/%s %s",(sk->protect.s2k.mode==1001)?'#':
+ (sk->protect.s2k.mode==1002)?'>':' ',
+ nbits_from_sk( sk ),pubkey_letter( sk->pubkey_algo ),
+ keystr_from_sk(sk),datestr_from_sk( sk ));
+
+ if(sk->has_expired)
+ {
+ printf(" [");
+ printf(_("expired: %s"),expirestr_from_sk(sk));
+ printf("]");
+ }
+ else if(sk->expiredate )
+ {
+ printf(" [");
+ printf(_("expires: %s"),expirestr_from_sk(sk));
+ printf("]");
+ }
+
+ printf("\n");
+ }
+ else
+ {
+ pk = node->pkt->pkt.public_key;
+ sk = NULL;
+
+ check_trustdb_stale();
+
+ printf("pub %4u%c/%s %s",
+ nbits_from_pk(pk),pubkey_letter(pk->pubkey_algo),
+ keystr_from_pk(pk),datestr_from_pk( pk ));
+
+ /* We didn't include this before in the key listing, but there
+ is room in the new format, so why not? */
+
+ if(pk->is_revoked)
+ {
+ printf(" [");
+ printf(_("revoked: %s"),revokestr_from_pk(pk));
+ printf("]");
+ }
+ else if(pk->has_expired)
+ {
+ printf(" [");
+ printf(_("expired: %s"),expirestr_from_pk(pk));
+ printf("]");
+ }
+ else if(pk->expiredate)
+ {
+ printf(" [");
+ printf(_("expires: %s"),expirestr_from_pk(pk));
+ printf("]");
+ }
+
+#if 0
+ /* I need to think about this some more. It's easy enough to
+ include, but it looks sort of confusing in the
+ listing... */
+ if(opt.list_options&LIST_SHOW_VALIDITY)
+ {
+ int validity=get_validity(pk,NULL);
+ printf(" [%s]",trust_value_to_string(validity));
+ }
+#endif
+
+ printf("\n");
+ }
+
+ if( fpr )
+ print_fingerprint( pk, sk, 0 );
+ print_card_serialno (sk);
+ if( opt.with_key_data )
+ print_key_data( pk );
+
+ for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
+ if( node->pkt->pkttype == PKT_USER_ID && !opt.fast_list_mode ) {
+ PKT_user_id *uid=node->pkt->pkt.user_id;
+
+ if(pk && (uid->is_expired || uid->is_revoked)
+ && !(opt.list_options&LIST_SHOW_UNUSABLE_UIDS))
+ {
+ skip_sigs=1;
+ continue;
+ }
+ else
+ skip_sigs=0;
+
+ if(attrib_fp && uid->attrib_data!=NULL)
+ dump_attribs(uid,pk,sk);
+
+ if((uid->is_revoked || uid->is_expired)
+ || ((opt.list_options&LIST_SHOW_UID_VALIDITY) && pk))
+ {
+ const char *validity;
+ int indent;
+
+ validity=uid_trust_string_fixed(pk,uid);
+ indent=(keystrlen()+9)-atoi(uid_trust_string_fixed(NULL,NULL));
+
+ if(indent<0 || indent>40)
+ indent=0;
+
+ printf("uid%*s%s ",indent,"",validity);
+ }
+ else
+ printf("uid%*s", (int)keystrlen()+10,"");
+
+ print_utf8_string( stdout, uid->name, uid->len );
+ putchar('\n');
+
+ if((opt.list_options&LIST_SHOW_PHOTOS) && uid->attribs!=NULL)
+ show_photos(uid->attribs,uid->numattribs,pk,sk);
+ }
+ else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+ {
+ PKT_public_key *pk2 = node->pkt->pkt.public_key;
+
+ if((pk2->is_revoked || pk2->has_expired)
+ && !(opt.list_options&LIST_SHOW_UNUSABLE_SUBKEYS))
+ {
+ skip_sigs=1;
+ continue;
+ }
+ else
+ skip_sigs=0;
+
+ printf("sub %4u%c/%s %s",
+ nbits_from_pk( pk2 ),pubkey_letter( pk2->pubkey_algo ),
+ keystr_from_pk(pk2),datestr_from_pk(pk2));
+ if( pk2->is_revoked )
+ {
+ printf(" [");
+ printf(_("revoked: %s"),revokestr_from_pk(pk2));
+ printf("]");
+ }
+ else if( pk2->has_expired )
+ {
+ printf(" [");
+ printf(_("expired: %s"),expirestr_from_pk(pk2));
+ printf("]");
+ }
+ else if( pk2->expiredate )
+ {
+ printf(" [");
+ printf(_("expires: %s"),expirestr_from_pk(pk2));
+ printf("]");
+ }
+ putchar('\n');
+ if( fpr > 1 )
+ print_fingerprint( pk2, NULL, 0 );
+ if( opt.with_key_data )
+ print_key_data( pk2 );
+ }
+ else if( node->pkt->pkttype == PKT_SECRET_SUBKEY )
+ {
+ PKT_secret_key *sk2 = node->pkt->pkt.secret_key;
+
+ printf("ssb%c %4u%c/%s %s",
+ (sk2->protect.s2k.mode==1001)?'#':
+ (sk2->protect.s2k.mode==1002)?'>':' ',
+ nbits_from_sk( sk2 ),pubkey_letter( sk2->pubkey_algo ),
+ keystr_from_sk(sk2),datestr_from_sk( sk2 ) );
+ if( sk2->expiredate )
+ {
+ printf(" [");
+ printf(_("expires: %s"),expirestr_from_sk(sk2));
+ printf("]");
+ }
+ putchar('\n');
+ if( fpr > 1 )
+ {
+ print_fingerprint( NULL, sk2, 0 );
+ print_card_serialno (sk2);
+ }
+ }
+ else if( opt.list_sigs
+ && node->pkt->pkttype == PKT_SIGNATURE
+ && !skip_sigs ) {
+ PKT_signature *sig = node->pkt->pkt.signature;
+ int sigrc;
+ char *sigstr;
+
+ if( stats ) {
+ /*fflush(stdout);*/
+ rc = check_key_signature( keyblock, node, NULL );
+ switch( rc ) {
+ case 0: sigrc = '!'; break;
+ case G10ERR_BAD_SIGN: stats->inv_sigs++; sigrc = '-'; break;
+ case G10ERR_NO_PUBKEY:
+ case G10ERR_UNU_PUBKEY: stats->no_key++; continue;
+ default: stats->oth_err++; sigrc = '%'; break;
+ }
+
+ /* TODO: Make sure a cached sig record here still has
+ the pk that issued it. See also
+ keyedit.c:print_and_check_one_sig */
+ }
+ else {
+ rc = 0;
+ sigrc = ' ';
+ }
+
+ if( sig->sig_class == 0x20 || sig->sig_class == 0x28
+ || sig->sig_class == 0x30 )
+ sigstr = "rev";
+ else if( (sig->sig_class&~3) == 0x10 )
+ sigstr = "sig";
+ else if( sig->sig_class == 0x18 )
+ sigstr = "sig";
+ else if( sig->sig_class == 0x1F )
+ sigstr = "sig";
+ else {
+ printf("sig "
+ "[unexpected signature class 0x%02x]\n",sig->sig_class );
+ continue;
+ }
+
+ fputs( sigstr, stdout );
+ printf("%c%c %c%c%c%c%c%c %s %s",
+ sigrc,(sig->sig_class-0x10>0 &&
+ sig->sig_class-0x10<4)?'0'+sig->sig_class-0x10:' ',
+ sig->flags.exportable?' ':'L',
+ sig->flags.revocable?' ':'R',
+ sig->flags.policy_url?'P':' ',
+ sig->flags.notation?'N':' ',
+ sig->flags.expired?'X':' ',
+ (sig->trust_depth>9)?'T':
+ (sig->trust_depth>0)?'0'+sig->trust_depth:' ',
+ keystr(sig->keyid),datestr_from_sig(sig));
+ if(opt.list_options&LIST_SHOW_SIG_EXPIRE)
+ printf(" %s", expirestr_from_sig(sig));
+ printf(" ");
+ if( sigrc == '%' )
+ printf("[%s] ", g10_errstr(rc) );
+ else if( sigrc == '?' )
+ ;
+ else if ( !opt.fast_list_mode ) {
+ size_t n;
+ char *p = get_user_id( sig->keyid, &n );
+ print_utf8_string( stdout, p, n );
+ xfree(p);
+ }
+ putchar('\n');
+
+ if(sig->flags.policy_url
+ && (opt.list_options&LIST_SHOW_POLICY_URLS))
+ show_policy_url(sig,3,0);
+
+ if(sig->flags.notation && (opt.list_options&LIST_SHOW_NOTATIONS))
+ show_notation(sig,3,0,
+ ((opt.list_options&LIST_SHOW_STD_NOTATIONS)?1:0)+
+ ((opt.list_options&LIST_SHOW_USER_NOTATIONS)?2:0));
+
+ if(sig->flags.pref_ks
+ && (opt.list_options&LIST_SHOW_KEYSERVER_URLS))
+ show_keyserver_url(sig,3,0);
+
+ /* fixme: check or list other sigs here */
+ }
+ }
+ putchar('\n');
+}
+
+void
+print_revokers(PKT_public_key *pk)
+{
+ /* print the revoker record */
+ if( !pk->revkey && pk->numrevkeys )
+ BUG();
+ else
+ {
+ int i,j;
+
+ for (i=0; i < pk->numrevkeys; i++)
+ {
+ byte *p;
+
+ printf ("rvk:::%d::::::", pk->revkey[i].algid);
+ p = pk->revkey[i].fpr;
+ for (j=0; j < 20; j++, p++ )
+ printf ("%02X", *p);
+ printf (":%02x%s:\n", pk->revkey[i].class,
+ (pk->revkey[i].class&0x40)?"s":"");
+ }
+ }
+}
+
+static void
+list_keyblock_colon( KBNODE keyblock, int secret, int fpr )
+{
+ int rc = 0;
+ KBNODE kbctx;
+ KBNODE node;
+ PKT_public_key *pk;
+ PKT_secret_key *sk;
+ u32 keyid[2];
+ int any=0;
+ int trustletter = 0;
+ int ulti_hack = 0;
+ int i;
+
+ /* get the keyid from the keyblock */
+ node = find_kbnode( keyblock, secret? PKT_SECRET_KEY : PKT_PUBLIC_KEY );
+ if( !node ) {
+ log_error("Oops; key lost!\n");
+ dump_kbnode( keyblock );
+ return;
+ }
+
+ if( secret ) {
+ pk = NULL;
+ sk = node->pkt->pkt.secret_key;
+ keyid_from_sk( sk, keyid );
+ printf("sec::%u:%d:%08lX%08lX:%s:%s:::",
+ nbits_from_sk( sk ),
+ sk->pubkey_algo,
+ (ulong)keyid[0],(ulong)keyid[1],
+ colon_datestr_from_sk( sk ),
+ colon_strtime (sk->expiredate)
+ /* fixme: add LID here */ );
+ }
+ else {
+ pk = node->pkt->pkt.public_key;
+ sk = NULL;
+ keyid_from_pk( pk, keyid );
+ fputs( "pub:", stdout );
+ if ( !pk->is_valid )
+ putchar ('i');
+ else if ( pk->is_revoked )
+ putchar ('r');
+ else if ( pk->has_expired )
+ putchar ('e');
+ else if ( opt.fast_list_mode || opt.no_expensive_trust_checks )
+ ;
+ else {
+ trustletter = get_validity_info ( pk, NULL );
+ if( trustletter == 'u' )
+ ulti_hack = 1;
+ putchar(trustletter);
+ }
+ printf(":%u:%d:%08lX%08lX:%s:%s::",
+ nbits_from_pk( pk ),
+ pk->pubkey_algo,
+ (ulong)keyid[0],(ulong)keyid[1],
+ colon_datestr_from_pk( pk ),
+ colon_strtime (pk->expiredate) );
+ if( !opt.fast_list_mode && !opt.no_expensive_trust_checks )
+ putchar( get_ownertrust_info(pk) );
+ putchar(':');
+ }
+
+ if (opt.fixed_list_mode) {
+ /* do not merge the first uid with the primary key */
+ putchar(':');
+ putchar(':');
+ print_capabilities (pk, sk, keyblock);
+ if (secret) {
+ putchar(':'); /* End of field 13. */
+ putchar(':'); /* End of field 14. */
+ if (sk->protect.s2k.mode == 1001)
+ putchar('#'); /* Key is just a stub. */
+ else if (sk->protect.s2k.mode == 1002) {
+ /* Key is stored on an external token (card) or handled by
+ the gpg-agent. Print the serial number of that token
+ here. */
+ for (i=0; i < sk->protect.ivlen; i++)
+ printf ("%02X", sk->protect.iv[i]);
+ }
+ putchar(':'); /* End of field 15. */
+ }
+ putchar('\n');
+ if(pk)
+ print_revokers(pk);
+ if( fpr )
+ print_fingerprint( pk, sk, 0 );
+ if( opt.with_key_data )
+ print_key_data( pk );
+ any = 1;
+ }
+
+ for( kbctx=NULL; (node=walk_kbnode( keyblock, &kbctx, 0)) ; ) {
+ if( node->pkt->pkttype == PKT_USER_ID && !opt.fast_list_mode ) {
+ PKT_user_id *uid=node->pkt->pkt.user_id;
+ if(attrib_fp && node->pkt->pkt.user_id->attrib_data!=NULL)
+ dump_attribs(node->pkt->pkt.user_id,pk,sk);
+ /*
+ * Fixme: We need a is_valid flag here too
+ */
+ if( any ) {
+ char *str=uid->attrib_data?"uat":"uid";
+ /* If we're listing a secret key, leave out the
+ validity values for now. This is handled better in
+ 1.9. */
+ if ( sk )
+ printf("%s:::::",str);
+ else if ( uid->is_revoked )
+ printf("%s:r::::",str);
+ else if ( uid->is_expired )
+ printf("%s:e::::",str);
+ else if ( opt.no_expensive_trust_checks )
+ printf("%s:::::",str);
+ else {
+ int uid_validity;
+
+ if( pk && !ulti_hack )
+ uid_validity=get_validity_info (pk, uid);
+ else
+ uid_validity = 'u';
+ printf("%s:%c::::",str,uid_validity);
+ }
+
+ printf("%s:",colon_strtime(uid->created));
+ printf("%s:",colon_strtime(uid->expiredate));
+
+ namehash_from_uid(uid);
+
+ for(i=0; i < 20; i++ )
+ printf("%02X",uid->namehash[i]);
+
+ printf("::");
+ }
+ if(uid->attrib_data)
+ printf("%u %lu",uid->numattribs,uid->attrib_len);
+ else
+ print_string(stdout,uid->name,uid->len, ':' );
+ putchar(':');
+ if (any)
+ putchar('\n');
+ else {
+ putchar(':');
+ print_capabilities (pk, sk, keyblock);
+ putchar('\n');
+ if( fpr )
+ print_fingerprint( pk, sk, 0 );
+ if( opt.with_key_data )
+ print_key_data( pk );
+ any = 1;
+ }
+ }
+ else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+ u32 keyid2[2];
+ PKT_public_key *pk2 = node->pkt->pkt.public_key;
+
+ if( !any ) {
+ putchar(':');
+ putchar(':');
+ print_capabilities (pk, sk, keyblock);
+ putchar('\n');
+ if( fpr )
+ print_fingerprint( pk, sk, 0 ); /* of the main key */
+ any = 1;
+ }
+
+ keyid_from_pk( pk2, keyid2 );
+ fputs ("sub:", stdout );
+ if ( !pk2->is_valid )
+ putchar ('i');
+ else if ( pk2->is_revoked )
+ putchar ('r');
+ else if ( pk2->has_expired )
+ putchar ('e');
+ else if ( opt.fast_list_mode || opt.no_expensive_trust_checks )
+ ;
+ else {
+ /* trustletter should always be defined here */
+ if(trustletter)
+ printf("%c", trustletter );
+ }
+ printf(":%u:%d:%08lX%08lX:%s:%s:::::",
+ nbits_from_pk( pk2 ),
+ pk2->pubkey_algo,
+ (ulong)keyid2[0],(ulong)keyid2[1],
+ colon_datestr_from_pk( pk2 ),
+ colon_strtime (pk2->expiredate)
+ /* fixme: add LID and ownertrust here */
+ );
+ print_capabilities (pk2, NULL, NULL);
+ putchar('\n');
+ if( fpr > 1 )
+ print_fingerprint( pk2, NULL, 0 );
+ if( opt.with_key_data )
+ print_key_data( pk2 );
+ }
+ else if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+ u32 keyid2[2];
+ PKT_secret_key *sk2 = node->pkt->pkt.secret_key;
+
+ if( !any ) {
+ putchar(':');
+ putchar(':');
+ print_capabilities (pk, sk, keyblock);
+ putchar('\n');
+ if( fpr )
+ print_fingerprint( pk, sk, 0 ); /* of the main key */
+ any = 1;
+ }
+
+ keyid_from_sk( sk2, keyid2 );
+ printf("ssb::%u:%d:%08lX%08lX:%s:%s:::::",
+ nbits_from_sk( sk2 ),
+ sk2->pubkey_algo,
+ (ulong)keyid2[0],(ulong)keyid2[1],
+ colon_datestr_from_sk( sk2 ),
+ colon_strtime (sk2->expiredate)
+ /* fixme: add LID */ );
+ print_capabilities (NULL, sk2, NULL);
+ if (opt.fixed_list_mode) {
+ /* We print the serial number only in fixed list mode
+ for the primary key so, so avoid questions we print
+ it for subkeys also only in this mode. There is no
+ technical reason, though. */
+ putchar(':'); /* End of field 13. */
+ putchar(':'); /* End of field 14. */
+ if (sk2->protect.s2k.mode == 1001)
+ putchar('#'); /* Key is just a stub. */
+ else if (sk2->protect.s2k.mode == 1002) {
+ /* Key is stored on an external token (card) or handled by
+ the gpg-agent. Print the serial number of that token
+ here. */
+ for (i=0; i < sk2->protect.ivlen; i++)
+ printf ("%02X", sk2->protect.iv[i]);
+ }
+ putchar(':'); /* End of field 15. */
+ }
+ putchar ('\n');
+ if( fpr > 1 )
+ print_fingerprint( NULL, sk2, 0 );
+ }
+ else if( opt.list_sigs && node->pkt->pkttype == PKT_SIGNATURE ) {
+ PKT_signature *sig = node->pkt->pkt.signature;
+ int sigrc,fprokay=0;
+ char *sigstr;
+ size_t fplen;
+ byte fparray[MAX_FINGERPRINT_LEN];
+
+ if( !any ) { /* no user id, (maybe a revocation follows)*/
+ if( sig->sig_class == 0x20 )
+ fputs("[revoked]:", stdout);
+ else if( sig->sig_class == 0x18 )
+ fputs("[key binding]:", stdout);
+ else if( sig->sig_class == 0x28 )
+ fputs("[subkey revoked]:", stdout);
+ else
+ putchar (':');
+ putchar(':');
+ print_capabilities (pk, sk, keyblock);
+ putchar('\n');
+ if( fpr )
+ print_fingerprint( pk, sk, 0 );
+ any=1;
+ }
+
+ if( sig->sig_class == 0x20 || sig->sig_class == 0x28
+ || sig->sig_class == 0x30 )
+ sigstr = "rev";
+ else if( (sig->sig_class&~3) == 0x10 )
+ sigstr = "sig";
+ else if( sig->sig_class == 0x18 )
+ sigstr = "sig";
+ else if( sig->sig_class == 0x1F )
+ sigstr = "sig";
+ else {
+ printf ("sig::::::::::%02x%c:\n",
+ sig->sig_class, sig->flags.exportable?'x':'l');
+ continue;
+ }
+ if( opt.check_sigs ) {
+ PKT_public_key *signer_pk=NULL;
+
+ fflush(stdout);
+ if(opt.no_sig_cache)
+ signer_pk=xmalloc_clear(sizeof(PKT_public_key));
+
+ rc = check_key_signature2( keyblock, node, NULL, signer_pk,
+ NULL, NULL, NULL );
+ switch( rc ) {
+ case 0: sigrc = '!'; break;
+ case G10ERR_BAD_SIGN: sigrc = '-'; break;
+ case G10ERR_NO_PUBKEY:
+ case G10ERR_UNU_PUBKEY: sigrc = '?'; break;
+ default: sigrc = '%'; break;
+ }
+
+ if(opt.no_sig_cache)
+ {
+ if(rc==0)
+ {
+ fingerprint_from_pk (signer_pk, fparray, &fplen);
+ fprokay=1;
+ }
+ free_public_key(signer_pk);
+ }
+ }
+ else {
+ rc = 0;
+ sigrc = ' ';
+ }
+ fputs( sigstr, stdout );
+ putchar(':');
+ if( sigrc != ' ' )
+ putchar(sigrc);
+ printf("::%d:%08lX%08lX:%s:%s:", sig->pubkey_algo,
+ (ulong)sig->keyid[0], (ulong)sig->keyid[1],
+ colon_datestr_from_sig(sig),
+ colon_expirestr_from_sig(sig));
+
+ if(sig->trust_depth || sig->trust_value)
+ printf("%d %d",sig->trust_depth,sig->trust_value);
+ printf(":");
+
+ if(sig->trust_regexp)
+ print_string(stdout,sig->trust_regexp,
+ strlen(sig->trust_regexp),':');
+ printf(":");
+
+ if( sigrc == '%' )
+ printf("[%s] ", g10_errstr(rc) );
+ else if( sigrc == '?' )
+ ;
+ else if ( !opt.fast_list_mode ) {
+ size_t n;
+ char *p = get_user_id( sig->keyid, &n );
+ print_string( stdout, p, n, ':' );
+ xfree(p);
+ }
+ printf(":%02x%c:", sig->sig_class,sig->flags.exportable?'x':'l');
+
+ if(opt.no_sig_cache && opt.check_sigs && fprokay)
+ {
+ printf(":");
+
+ for (i=0; i < fplen ; i++ )
+ printf ("%02X", fparray[i] );
+
+ printf(":");
+ }
+
+ printf("\n");
+
+ if(opt.show_subpackets)
+ print_subpackets_colon(sig);
+
+ /* fixme: check or list other sigs here */
+ }
+ }
+ if( !any ) {/* oops, no user id */
+ putchar(':');
+ putchar(':');
+ print_capabilities (pk, sk, keyblock);
+ putchar('\n');
+ }
+}
+
+/*
+ * Reorder the keyblock so that the primary user ID (and not attribute
+ * packet) comes first. Fixme: Replace this by a generic sort
+ * function. */
+static void
+do_reorder_keyblock (KBNODE keyblock,int attr)
+{
+ KBNODE primary = NULL, primary0 = NULL, primary2 = NULL;
+ KBNODE last, node;
+
+ for (node=keyblock; node; primary0=node, node = node->next) {
+ if( node->pkt->pkttype == PKT_USER_ID &&
+ ((attr && node->pkt->pkt.user_id->attrib_data) ||
+ (!attr && !node->pkt->pkt.user_id->attrib_data)) &&
+ node->pkt->pkt.user_id->is_primary ) {
+ primary = primary2 = node;
+ for (node=node->next; node; primary2=node, node = node->next ) {
+ if( node->pkt->pkttype == PKT_USER_ID
+ || node->pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+ break;
+ }
+ }
+ break;
+ }
+ }
+ if ( !primary )
+ return; /* no primary key flag found (should not happen) */
+
+ for (last=NULL, node=keyblock; node; last = node, node = node->next) {
+ if( node->pkt->pkttype == PKT_USER_ID )
+ break;
+ }
+ assert (node);
+ assert (last); /* the user ID is never the first packet */
+ assert (primary0); /* ditto (this is the node before primary) */
+ if ( node == primary )
+ return; /* already the first one */
+
+ last->next = primary;
+ primary0->next = primary2->next;
+ primary2->next = node;
+}
+
+void
+reorder_keyblock (KBNODE keyblock)
+{
+ do_reorder_keyblock(keyblock,1);
+ do_reorder_keyblock(keyblock,0);
+}
+
+void
+list_keyblock( KBNODE keyblock, int secret, int fpr, void *opaque )
+{
+ reorder_keyblock (keyblock);
+ if (opt.with_colons)
+ list_keyblock_colon (keyblock, secret, fpr );
+ else
+ list_keyblock_print (keyblock, secret, fpr, opaque );
+}
+
+/*
+ * standard function to print the finperprint.
+ * mode 0: as used in key listings, opt.with_colons is honored
+ * 1: print using log_info ()
+ * 2: direct use of tty
+ * 3: direct use of tty but only primary key.
+ * modes 1 and 2 will try and print both subkey and primary key fingerprints
+ */
+void
+print_fingerprint (PKT_public_key *pk, PKT_secret_key *sk, int mode )
+{
+ byte array[MAX_FINGERPRINT_LEN], *p;
+ size_t i, n;
+ FILE *fp;
+ const char *text;
+ int primary=0;
+
+ if(sk)
+ {
+ if(sk->main_keyid[0]==sk->keyid[0] && sk->main_keyid[1]==sk->keyid[1])
+ primary=1;
+ }
+ else
+ {
+ if(pk->main_keyid[0]==pk->keyid[0] && pk->main_keyid[1]==pk->keyid[1])
+ primary=1;
+ }
+
+ /* Just to be safe */
+ if(mode&0x80 && !primary)
+ {
+ log_error("primary key is not really primary!\n");
+ return;
+ }
+
+ mode&=~0x80;
+
+ if(!primary && (mode==1 || mode==2))
+ {
+ if(sk)
+ {
+ PKT_secret_key *primary_sk=xmalloc_clear(sizeof(*primary_sk));
+ get_seckey(primary_sk,sk->main_keyid);
+ print_fingerprint(NULL,primary_sk,mode|0x80);
+ free_secret_key(primary_sk);
+ }
+ else
+ {
+ PKT_public_key *primary_pk=xmalloc_clear(sizeof(*primary_pk));
+ get_pubkey(primary_pk,pk->main_keyid);
+ print_fingerprint(primary_pk,NULL,mode|0x80);
+ free_public_key(primary_pk);
+ }
+ }
+
+ if (mode == 1) {
+ fp = log_stream ();
+ if(primary)
+ text = _("Primary key fingerprint:");
+ else
+ text = _(" Subkey fingerprint:");
+ }
+ else if (mode == 2) {
+ fp = NULL; /* use tty */
+ if(primary)
+ /* TRANSLATORS: this should fit into 24 bytes to that the
+ * fingerprint data is properly aligned with the user ID */
+ text = _(" Primary key fingerprint:");
+ else
+ text = _(" Subkey fingerprint:");
+ }
+ else if (mode == 3) {
+ fp = NULL; /* use tty */
+ text = _(" Key fingerprint =");
+ }
+ else {
+ fp = stdout;
+ text = _(" Key fingerprint =");
+ }
+
+ if (sk)
+ fingerprint_from_sk (sk, array, &n);
+ else
+ fingerprint_from_pk (pk, array, &n);
+ p = array;
+ if (opt.with_colons && !mode) {
+ fprintf (fp, "fpr:::::::::");
+ for (i=0; i < n ; i++, p++ )
+ fprintf (fp, "%02X", *p );
+ putc(':', fp);
+ }
+ else {
+ if (fp)
+ fputs (text, fp);
+ else
+ tty_printf ("%s", text);
+ if (n == 20) {
+ for (i=0; i < n ; i++, i++, p += 2 ) {
+ if (fp) {
+ if (i == 10 )
+ putc(' ', fp);
+ fprintf (fp, " %02X%02X", *p, p[1] );
+ }
+ else {
+ if (i == 10 )
+ tty_printf (" ");
+ tty_printf (" %02X%02X", *p, p[1]);
+ }
+ }
+ }
+ else {
+ for (i=0; i < n ; i++, p++ ) {
+ if (fp) {
+ if (i && !(i%8) )
+ putc (' ', fp);
+ fprintf (fp, " %02X", *p );
+ }
+ else {
+ if (i && !(i%8) )
+ tty_printf (" ");
+ tty_printf (" %02X", *p );
+ }
+ }
+ }
+ }
+ if (fp)
+ putc ('\n', fp);
+ else
+ tty_printf ("\n");
+}
+
+/* Print the serial number of an OpenPGP card if available. */
+static void
+print_card_serialno (PKT_secret_key *sk)
+{
+ int i;
+
+ if (!sk)
+ return;
+ if (!sk->is_protected || sk->protect.s2k.mode != 1002)
+ return; /* Not a card. */
+ if (opt.with_colons)
+ return; /* Handled elsewhere. */
+
+ fputs (_(" Card serial no. ="), stdout);
+ putchar (' ');
+ if (sk->protect.ivlen == 16
+ && !memcmp (sk->protect.iv, "\xD2\x76\x00\x01\x24\x01", 6) )
+ { /* This is an OpenPGP card. Just print the relevant part. */
+ for (i=8; i < 14; i++)
+ {
+ if (i == 10)
+ putchar (' ');
+ printf ("%02X", sk->protect.iv[i]);
+ }
+ }
+ else
+ { /* Something is wrong: Print all. */
+ for (i=0; i < sk->protect.ivlen; i++)
+ printf ("%02X", sk->protect.iv[i]);
+ }
+ putchar ('\n');
+}
+
+
+
+void set_attrib_fd(int fd)
+{
+ static int last_fd=-1;
+
+ if ( fd != -1 && last_fd == fd )
+ return;
+
+ if ( attrib_fp && attrib_fp != stdout && attrib_fp != stderr )
+ fclose (attrib_fp);
+ attrib_fp = NULL;
+ if ( fd == -1 )
+ return;
+
+ if( fd == 1 )
+ attrib_fp = stdout;
+ else if( fd == 2 )
+ attrib_fp = stderr;
+ else
+ attrib_fp = fdopen( fd, "wb" );
+ if( !attrib_fp ) {
+ log_fatal("can't open fd %d for attribute output: %s\n",
+ fd, strerror(errno));
+ }
+
+ last_fd = fd;
+}
diff --git a/g10/keyring.c b/g10/keyring.c
new file mode 100644
index 0000000..9ef5b1b
--- /dev/null
+++ b/g10/keyring.c
@@ -0,0 +1,1624 @@
+/* keyring.c - keyring file handling
+ * Copyright (C) 2001, 2004 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "util.h"
+#include "keyring.h"
+#include "packet.h"
+#include "keydb.h"
+#include "options.h"
+#include "main.h" /*for check_key_signature()*/
+#include "i18n.h"
+
+/* off_item is a funny named for an object used to keep track of known
+ * keys. The idea was to use the offset to seek to the known keyblock, but
+ * this is not possible if more than one process is using the keyring.
+ */
+struct off_item {
+ struct off_item *next;
+ u32 kid[2];
+ /*off_t off;*/
+};
+
+typedef struct off_item **OffsetHashTable;
+
+
+typedef struct keyring_name *KR_NAME;
+struct keyring_name {
+ struct keyring_name *next;
+ int secret;
+ DOTLOCK lockhd;
+ int is_locked;
+ int did_full_scan;
+ char fname[1];
+};
+typedef struct keyring_name const * CONST_KR_NAME;
+
+static KR_NAME kr_names;
+static int active_handles;
+
+static OffsetHashTable kr_offtbl;
+static int kr_offtbl_ready;
+
+
+struct keyring_handle {
+ CONST_KR_NAME resource;
+ int secret; /* this is for a secret keyring */
+ struct {
+ CONST_KR_NAME kr;
+ IOBUF iobuf;
+ int eof;
+ int error;
+ } current;
+ struct {
+ CONST_KR_NAME kr;
+ off_t offset;
+ size_t pk_no;
+ size_t uid_no;
+ unsigned int n_packets; /*used for delete and update*/
+ } found;
+ struct {
+ char *name;
+ char *pattern;
+ } word_match;
+};
+
+
+
+static int do_copy (int mode, const char *fname, KBNODE root, int secret,
+ off_t start_offset, unsigned int n_packets );
+
+
+
+static struct off_item *
+new_offset_item (void)
+{
+ struct off_item *k;
+
+ k = xmalloc_clear (sizeof *k);
+ return k;
+}
+
+#if 0
+static void
+release_offset_items (struct off_item *k)
+{
+ struct off_item *k2;
+
+ for (; k; k = k2)
+ {
+ k2 = k->next;
+ xfree (k);
+ }
+}
+#endif
+
+static OffsetHashTable
+new_offset_hash_table (void)
+{
+ struct off_item **tbl;
+
+ tbl = xmalloc_clear (2048 * sizeof *tbl);
+ return tbl;
+}
+
+#if 0
+static void
+release_offset_hash_table (OffsetHashTable tbl)
+{
+ int i;
+
+ if (!tbl)
+ return;
+ for (i=0; i < 2048; i++)
+ release_offset_items (tbl[i]);
+ xfree (tbl);
+}
+#endif
+
+static struct off_item *
+lookup_offset_hash_table (OffsetHashTable tbl, u32 *kid)
+{
+ struct off_item *k;
+
+ for (k = tbl[(kid[1] & 0x07ff)]; k; k = k->next)
+ if (k->kid[0] == kid[0] && k->kid[1] == kid[1])
+ return k;
+ return NULL;
+}
+
+static void
+update_offset_hash_table (OffsetHashTable tbl, u32 *kid, off_t off)
+{
+ struct off_item *k;
+
+ for (k = tbl[(kid[1] & 0x07ff)]; k; k = k->next)
+ {
+ if (k->kid[0] == kid[0] && k->kid[1] == kid[1])
+ {
+ /*k->off = off;*/
+ return;
+ }
+ }
+
+ k = new_offset_item ();
+ k->kid[0] = kid[0];
+ k->kid[1] = kid[1];
+ /*k->off = off;*/
+ k->next = tbl[(kid[1] & 0x07ff)];
+ tbl[(kid[1] & 0x07ff)] = k;
+}
+
+static void
+update_offset_hash_table_from_kb (OffsetHashTable tbl, KBNODE node, off_t off)
+{
+ for (; node; node = node->next)
+ {
+ if (node->pkt->pkttype == PKT_PUBLIC_KEY
+ || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
+ {
+ u32 aki[2];
+ keyid_from_pk (node->pkt->pkt.public_key, aki);
+ update_offset_hash_table (tbl, aki, off);
+ }
+ }
+}
+
+/*
+ * Register a filename for plain keyring files. ptr is set to a
+ * pointer to be used to create a handles etc, or the already-issued
+ * pointer if it has already been registered. The function returns 1
+ * if a new keyring was registered.
+*/
+int
+keyring_register_filename (const char *fname, int secret, void **ptr)
+{
+ KR_NAME kr;
+
+ if (active_handles)
+ BUG (); /* We don't allow that */
+
+ for (kr=kr_names; kr; kr = kr->next)
+ {
+ if ( !compare_filenames (kr->fname, fname) )
+ {
+ *ptr=kr;
+ return 0; /* already registered */
+ }
+ }
+
+ if (secret)
+ register_secured_file (fname);
+
+ kr = xmalloc (sizeof *kr + strlen (fname));
+ strcpy (kr->fname, fname);
+ kr->secret = !!secret;
+ kr->lockhd = NULL;
+ kr->is_locked = 0;
+ kr->did_full_scan = 0;
+ /* keep a list of all issued pointers */
+ kr->next = kr_names;
+ kr_names = kr;
+
+ /* create the offset table the first time a function here is used */
+ if (!kr_offtbl)
+ kr_offtbl = new_offset_hash_table ();
+
+ *ptr=kr;
+
+ return 1;
+}
+
+int
+keyring_is_writable (void *token)
+{
+ KR_NAME r = token;
+
+ return r? !access (r->fname, W_OK) : 0;
+}
+
+
+
+/* Create a new handle for the resource associated with TOKEN. SECRET
+ is just just as a cross-check.
+
+ The returned handle must be released using keyring_release (). */
+KEYRING_HANDLE
+keyring_new (void *token, int secret)
+{
+ KEYRING_HANDLE hd;
+ KR_NAME resource = token;
+
+ assert (resource && !resource->secret == !secret);
+
+ hd = xmalloc_clear (sizeof *hd);
+ hd->resource = resource;
+ hd->secret = !!secret;
+ active_handles++;
+ return hd;
+}
+
+void
+keyring_release (KEYRING_HANDLE hd)
+{
+ if (!hd)
+ return;
+ assert (active_handles > 0);
+ active_handles--;
+ xfree (hd->word_match.name);
+ xfree (hd->word_match.pattern);
+ iobuf_close (hd->current.iobuf);
+ xfree (hd);
+}
+
+
+const char *
+keyring_get_resource_name (KEYRING_HANDLE hd)
+{
+ if (!hd || !hd->resource)
+ return NULL;
+ return hd->resource->fname;
+}
+
+
+/*
+ * Lock the keyring with the given handle, or unlok if yes is false.
+ * We ignore the handle and lock all registered files.
+ */
+int
+keyring_lock (KEYRING_HANDLE hd, int yes)
+{
+ KR_NAME kr;
+ int rc = 0;
+
+ if (yes) {
+ /* first make sure the lock handles are created */
+ for (kr=kr_names; kr; kr = kr->next) {
+ if (!keyring_is_writable(kr))
+ continue;
+ if (!kr->lockhd) {
+ kr->lockhd = create_dotlock( kr->fname );
+ if (!kr->lockhd) {
+ log_info ("can't allocate lock for `%s'\n", kr->fname );
+ rc = G10ERR_GENERAL;
+ }
+ }
+ }
+ if (rc)
+ return rc;
+
+ /* and now set the locks */
+ for (kr=kr_names; kr; kr = kr->next) {
+ if (!keyring_is_writable(kr))
+ continue;
+ if (kr->is_locked)
+ ;
+ else if (make_dotlock (kr->lockhd, -1) ) {
+ log_info ("can't lock `%s'\n", kr->fname );
+ rc = G10ERR_GENERAL;
+ }
+ else
+ kr->is_locked = 1;
+ }
+ }
+
+ if (rc || !yes) {
+ for (kr=kr_names; kr; kr = kr->next) {
+ if (!keyring_is_writable(kr))
+ continue;
+ if (!kr->is_locked)
+ ;
+ else if (release_dotlock (kr->lockhd))
+ log_info ("can't unlock `%s'\n", kr->fname );
+ else
+ kr->is_locked = 0;
+ }
+ }
+
+ return rc;
+}
+
+
+
+/*
+ * Return the last found keyring. Caller must free it.
+ * The returned keyblock has the kbode flag bit 0 set for the node with
+ * the public key used to locate the keyblock or flag bit 1 set for
+ * the user ID node.
+ */
+int
+keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb)
+{
+ PACKET *pkt;
+ int rc;
+ KBNODE keyblock = NULL, node, lastnode;
+ IOBUF a;
+ int in_cert = 0;
+ int pk_no = 0;
+ int uid_no = 0;
+ int save_mode;
+
+ if (ret_kb)
+ *ret_kb = NULL;
+
+ if (!hd->found.kr)
+ return -1; /* no successful search */
+
+ a = iobuf_open (hd->found.kr->fname);
+ if (!a)
+ {
+ log_error(_("can't open `%s'\n"), hd->found.kr->fname);
+ return G10ERR_KEYRING_OPEN;
+ }
+
+ if (iobuf_seek (a, hd->found.offset) ) {
+ log_error ("can't seek `%s'\n", hd->found.kr->fname);
+ iobuf_close(a);
+ return G10ERR_KEYRING_OPEN;
+ }
+
+ pkt = xmalloc (sizeof *pkt);
+ init_packet (pkt);
+ hd->found.n_packets = 0;;
+ lastnode = NULL;
+ save_mode = set_packet_list_mode(0);
+ while ((rc=parse_packet (a, pkt)) != -1) {
+ hd->found.n_packets++;
+ if (rc == G10ERR_UNKNOWN_PACKET) {
+ free_packet (pkt);
+ init_packet (pkt);
+ continue;
+ }
+ if (rc) {
+ log_error ("keyring_get_keyblock: read error: %s\n",
+ g10_errstr(rc) );
+ rc = G10ERR_INV_KEYRING;
+ break;
+ }
+ if (pkt->pkttype == PKT_COMPRESSED) {
+ log_error ("skipped compressed packet in keyring\n");
+ free_packet(pkt);
+ init_packet(pkt);
+ continue;
+ }
+
+ if (in_cert && (pkt->pkttype == PKT_PUBLIC_KEY
+ || pkt->pkttype == PKT_SECRET_KEY)) {
+ hd->found.n_packets--; /* fix counter */
+ break; /* ready */
+ }
+
+ in_cert = 1;
+ if (pkt->pkttype == PKT_RING_TRUST) {
+ /*(this code is duplicated after the loop)*/
+ if ( lastnode
+ && lastnode->pkt->pkttype == PKT_SIGNATURE
+ && (pkt->pkt.ring_trust->sigcache & 1) ) {
+ /* this is a ring trust packet with a checked signature
+ * status cache following directly a signature paket.
+ * Set the cache status into that signature packet */
+ PKT_signature *sig = lastnode->pkt->pkt.signature;
+
+ sig->flags.checked = 1;
+ sig->flags.valid = !!(pkt->pkt.ring_trust->sigcache & 2);
+ }
+ /* reset lastnode, so that we set the cache status only from
+ * the ring trust packet immediately folling a signature */
+ lastnode = NULL;
+ }
+ else {
+ node = lastnode = new_kbnode (pkt);
+ if (!keyblock)
+ keyblock = node;
+ else
+ add_kbnode (keyblock, node);
+
+ if ( pkt->pkttype == PKT_PUBLIC_KEY
+ || pkt->pkttype == PKT_PUBLIC_SUBKEY
+ || pkt->pkttype == PKT_SECRET_KEY
+ || pkt->pkttype == PKT_SECRET_SUBKEY) {
+ if (++pk_no == hd->found.pk_no)
+ node->flag |= 1;
+ }
+ else if ( pkt->pkttype == PKT_USER_ID) {
+ if (++uid_no == hd->found.uid_no)
+ node->flag |= 2;
+ }
+ }
+
+ pkt = xmalloc (sizeof *pkt);
+ init_packet(pkt);
+ }
+ set_packet_list_mode(save_mode);
+
+ if (rc == -1 && keyblock)
+ rc = 0; /* got the entire keyblock */
+
+ if (rc || !ret_kb)
+ release_kbnode (keyblock);
+ else {
+ /*(duplicated form the loop body)*/
+ if ( pkt && pkt->pkttype == PKT_RING_TRUST
+ && lastnode
+ && lastnode->pkt->pkttype == PKT_SIGNATURE
+ && (pkt->pkt.ring_trust->sigcache & 1) ) {
+ PKT_signature *sig = lastnode->pkt->pkt.signature;
+ sig->flags.checked = 1;
+ sig->flags.valid = !!(pkt->pkt.ring_trust->sigcache & 2);
+ }
+ *ret_kb = keyblock;
+ }
+ free_packet (pkt);
+ xfree (pkt);
+ iobuf_close(a);
+
+ /* Make sure that future search operations fail immediately when
+ * we know that we are working on a invalid keyring
+ */
+ if (rc == G10ERR_INV_KEYRING)
+ hd->current.error = rc;
+
+ return rc;
+}
+
+int
+keyring_update_keyblock (KEYRING_HANDLE hd, KBNODE kb)
+{
+ int rc;
+
+ if (!hd->found.kr)
+ return -1; /* no successful prior search */
+
+ if (!hd->found.n_packets) {
+ /* need to know the number of packets - do a dummy get_keyblock*/
+ rc = keyring_get_keyblock (hd, NULL);
+ if (rc) {
+ log_error ("re-reading keyblock failed: %s\n", g10_errstr (rc));
+ return rc;
+ }
+ if (!hd->found.n_packets)
+ BUG ();
+ }
+
+ /* The open iobuf isn't needed anymore and in fact is a problem when
+ it comes to renaming the keyring files on some operating systems,
+ so close it here */
+ iobuf_close(hd->current.iobuf);
+ hd->current.iobuf = NULL;
+
+ /* do the update */
+ rc = do_copy (3, hd->found.kr->fname, kb, hd->secret,
+ hd->found.offset, hd->found.n_packets );
+ if (!rc) {
+ if (!hd->secret && kr_offtbl)
+ {
+ update_offset_hash_table_from_kb (kr_offtbl, kb, 0);
+ }
+ /* better reset the found info */
+ hd->found.kr = NULL;
+ hd->found.offset = 0;
+ }
+ return rc;
+}
+
+int
+keyring_insert_keyblock (KEYRING_HANDLE hd, KBNODE kb)
+{
+ int rc;
+ const char *fname;
+
+ if (!hd)
+ fname = NULL;
+ else if (hd->found.kr)
+ fname = hd->found.kr->fname;
+ else if (hd->current.kr)
+ fname = hd->current.kr->fname;
+ else
+ fname = hd->resource? hd->resource->fname:NULL;
+
+ if (!fname)
+ return G10ERR_GENERAL;
+
+ /* close this one otherwise we will lose the position for
+ * a next search. Fixme: it would be better to adjust the position
+ * after the write opertions.
+ */
+ iobuf_close (hd->current.iobuf);
+ hd->current.iobuf = NULL;
+
+ /* do the insert */
+ rc = do_copy (1, fname, kb, hd->secret, 0, 0 );
+ if (!rc && !hd->secret && kr_offtbl)
+ {
+ update_offset_hash_table_from_kb (kr_offtbl, kb, 0);
+ }
+
+ return rc;
+}
+
+
+int
+keyring_delete_keyblock (KEYRING_HANDLE hd)
+{
+ int rc;
+
+ if (!hd->found.kr)
+ return -1; /* no successful prior search */
+
+ if (!hd->found.n_packets) {
+ /* need to know the number of packets - do a dummy get_keyblock*/
+ rc = keyring_get_keyblock (hd, NULL);
+ if (rc) {
+ log_error ("re-reading keyblock failed: %s\n", g10_errstr (rc));
+ return rc;
+ }
+ if (!hd->found.n_packets)
+ BUG ();
+ }
+
+ /* close this one otherwise we will lose the position for
+ * a next search. Fixme: it would be better to adjust the position
+ * after the write opertions.
+ */
+ iobuf_close (hd->current.iobuf);
+ hd->current.iobuf = NULL;
+
+ /* do the delete */
+ rc = do_copy (2, hd->found.kr->fname, NULL, hd->secret,
+ hd->found.offset, hd->found.n_packets );
+ if (!rc) {
+ /* better reset the found info */
+ hd->found.kr = NULL;
+ hd->found.offset = 0;
+ /* Delete is a rare operations, so we don't remove the keys
+ * from the offset table */
+ }
+ return rc;
+}
+
+
+
+/*
+ * Start the next search on this handle right at the beginning
+ */
+int
+keyring_search_reset (KEYRING_HANDLE hd)
+{
+ assert (hd);
+
+ hd->current.kr = NULL;
+ iobuf_close (hd->current.iobuf);
+ hd->current.iobuf = NULL;
+ hd->current.eof = 0;
+ hd->current.error = 0;
+
+ hd->found.kr = NULL;
+ hd->found.offset = 0;
+ return 0;
+}
+
+
+static int
+prepare_search (KEYRING_HANDLE hd)
+{
+ if (hd->current.error)
+ return hd->current.error; /* still in error state */
+
+ if (hd->current.kr && !hd->current.eof) {
+ if ( !hd->current.iobuf )
+ return G10ERR_GENERAL; /* position invalid after a modify */
+ return 0; /* okay */
+ }
+
+ if (!hd->current.kr && hd->current.eof)
+ return -1; /* still EOF */
+
+ if (!hd->current.kr) { /* start search with first keyring */
+ hd->current.kr = hd->resource;
+ if (!hd->current.kr) {
+ hd->current.eof = 1;
+ return -1; /* keyring not available */
+ }
+ assert (!hd->current.iobuf);
+ }
+ else { /* EOF */
+ iobuf_close (hd->current.iobuf);
+ hd->current.iobuf = NULL;
+ hd->current.kr = NULL;
+ hd->current.eof = 1;
+ return -1;
+ }
+
+ hd->current.eof = 0;
+ hd->current.iobuf = iobuf_open (hd->current.kr->fname);
+ if (!hd->current.iobuf)
+ {
+ log_error(_("can't open `%s'\n"), hd->current.kr->fname );
+ return (hd->current.error = G10ERR_OPEN_FILE);
+ }
+
+ return 0;
+}
+
+
+/* A map of the all characters valid used for word_match()
+ * Valid characters are in in this table converted to uppercase.
+ * because the upper 128 bytes have special meaning, we assume
+ * that they are all valid.
+ * Note: We must use numerical values here in case that this program
+ * will be converted to those little blue HAL9000s with their strange
+ * EBCDIC character set (user ids are UTF-8).
+ * wk 2000-04-13: Hmmm, does this really make sense, given the fact that
+ * we can run gpg now on a S/390 running GNU/Linux, where the code
+ * translation is done by the device drivers?
+ */
+static const byte word_match_chars[256] = {
+ /* 00 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 08 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 10 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 18 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 20 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 28 */ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 30 */ 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,
+ /* 38 */ 0x38, 0x39, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 40 */ 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ /* 48 */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ /* 50 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ /* 58 */ 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 60 */ 0x00, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,
+ /* 68 */ 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,
+ /* 70 */ 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,
+ /* 78 */ 0x58, 0x59, 0x5a, 0x00, 0x00, 0x00, 0x00, 0x00,
+ /* 80 */ 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
+ /* 88 */ 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,
+ /* 90 */ 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,
+ /* 98 */ 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,
+ /* a0 */ 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,
+ /* a8 */ 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,
+ /* b0 */ 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,
+ /* b8 */ 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,
+ /* c0 */ 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,
+ /* c8 */ 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,
+ /* d0 */ 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,
+ /* d8 */ 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,
+ /* e0 */ 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,
+ /* e8 */ 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,
+ /* f0 */ 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,
+ /* f8 */ 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff
+};
+
+/****************
+ * Do a word match (original user id starts with a '+').
+ * The pattern is already tokenized to a more suitable format:
+ * There are only the real words in it delimited by one space
+ * and all converted to uppercase.
+ *
+ * Returns: 0 if all words match.
+ *
+ * Note: This algorithm is a straightforward one and not very
+ * fast. It works for UTF-8 strings. The uidlen should
+ * be removed but due to the fact that old versions of
+ * pgp don't use UTF-8 we still use the length; this should
+ * be fixed in parse-packet (and replace \0 by some special
+ * UTF-8 encoding)
+ */
+static int
+word_match( const byte *uid, size_t uidlen, const byte *pattern )
+{
+ size_t wlen, n;
+ const byte *p;
+ const byte *s;
+
+ for( s=pattern; *s; ) {
+ do {
+ /* skip leading delimiters */
+ while( uidlen && !word_match_chars[*uid] )
+ uid++, uidlen--;
+ /* get length of the word */
+ n = uidlen; p = uid;
+ while( n && word_match_chars[*p] )
+ p++, n--;
+ wlen = p - uid;
+ /* and compare against the current word from pattern */
+ for(n=0, p=uid; n < wlen && s[n] != ' ' && s[n] ; n++, p++ ) {
+ if( word_match_chars[*p] != s[n] )
+ break;
+ }
+ if( n == wlen && (s[n] == ' ' || !s[n]) )
+ break; /* found */
+ uid += wlen;
+ uidlen -= wlen;
+ } while( uidlen );
+ if( !uidlen )
+ return -1; /* not found */
+
+ /* advance to next word in pattern */
+ for(; *s != ' ' && *s ; s++ )
+ ;
+ if( *s )
+ s++ ;
+ }
+ return 0; /* found */
+}
+
+/****************
+ * prepare word word_match; that is parse the name and
+ * build the pattern.
+ * caller has to free the returned pattern
+ */
+static char*
+prepare_word_match (const byte *name)
+{
+ byte *pattern, *p;
+ int c;
+
+ /* the original length is always enough for the pattern */
+ p = pattern = xmalloc(strlen(name)+1);
+ do {
+ /* skip leading delimiters */
+ while( *name && !word_match_chars[*name] )
+ name++;
+ /* copy as long as we don't have a delimiter and convert
+ * to uppercase.
+ * fixme: how can we handle utf8 uppercasing */
+ for( ; *name && (c=word_match_chars[*name]); name++ )
+ *p++ = c;
+ *p++ = ' '; /* append pattern delimiter */
+ } while( *name );
+ p[-1] = 0; /* replace last pattern delimiter by EOS */
+
+ return pattern;
+}
+
+
+
+
+static int
+compare_name (int mode, const char *name, const char *uid, size_t uidlen)
+{
+ int i;
+ const char *s, *se;
+
+ if (mode == KEYDB_SEARCH_MODE_EXACT) {
+ for (i=0; name[i] && uidlen; i++, uidlen--)
+ if (uid[i] != name[i])
+ break;
+ if (!uidlen && !name[i])
+ return 0; /* found */
+ }
+ else if (mode == KEYDB_SEARCH_MODE_SUBSTR) {
+ if (ascii_memistr( uid, uidlen, name ))
+ return 0;
+ }
+ else if ( mode == KEYDB_SEARCH_MODE_MAIL
+ || mode == KEYDB_SEARCH_MODE_MAILSUB
+ || mode == KEYDB_SEARCH_MODE_MAILEND) {
+ for (i=0, s= uid; i < uidlen && *s != '<'; s++, i++)
+ ;
+ if (i < uidlen) {
+ /* skip opening delim and one char and look for the closing one*/
+ s++; i++;
+ for (se=s+1, i++; i < uidlen && *se != '>'; se++, i++)
+ ;
+ if (i < uidlen) {
+ i = se - s;
+ if (mode == KEYDB_SEARCH_MODE_MAIL) {
+ if( strlen(name)-2 == i
+ && !ascii_memcasecmp( s, name+1, i) )
+ return 0;
+ }
+ else if (mode == KEYDB_SEARCH_MODE_MAILSUB) {
+ if( ascii_memistr( s, i, name ) )
+ return 0;
+ }
+ else { /* email from end */
+ /* nyi */
+ }
+ }
+ }
+ }
+ else if (mode == KEYDB_SEARCH_MODE_WORDS)
+ return word_match (uid, uidlen, name);
+ else
+ BUG();
+
+ return -1; /* not found */
+}
+
+
+/*
+ * Search through the keyring(s), starting at the current position,
+ * for a keyblock which contains one of the keys described in the DESC array.
+ */
+int
+keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc,
+ size_t ndesc, size_t *descindex)
+{
+ int rc;
+ PACKET pkt;
+ int save_mode;
+ off_t offset, main_offset;
+ size_t n;
+ int need_uid, need_words, need_keyid, need_fpr, any_skip;
+ int pk_no, uid_no;
+ int initial_skip;
+ int use_offtbl;
+ PKT_user_id *uid = NULL;
+ PKT_public_key *pk = NULL;
+ PKT_secret_key *sk = NULL;
+ u32 aki[2];
+
+ /* figure out what information we need */
+ need_uid = need_words = need_keyid = need_fpr = any_skip = 0;
+ for (n=0; n < ndesc; n++)
+ {
+ switch (desc[n].mode)
+ {
+ case KEYDB_SEARCH_MODE_EXACT:
+ case KEYDB_SEARCH_MODE_SUBSTR:
+ case KEYDB_SEARCH_MODE_MAIL:
+ case KEYDB_SEARCH_MODE_MAILSUB:
+ case KEYDB_SEARCH_MODE_MAILEND:
+ need_uid = 1;
+ break;
+ case KEYDB_SEARCH_MODE_WORDS:
+ need_uid = 1;
+ need_words = 1;
+ break;
+ case KEYDB_SEARCH_MODE_SHORT_KID:
+ case KEYDB_SEARCH_MODE_LONG_KID:
+ need_keyid = 1;
+ break;
+ case KEYDB_SEARCH_MODE_FPR16:
+ case KEYDB_SEARCH_MODE_FPR20:
+ case KEYDB_SEARCH_MODE_FPR:
+ need_fpr = 1;
+ break;
+ case KEYDB_SEARCH_MODE_FIRST:
+ /* always restart the search in this mode */
+ keyring_search_reset (hd);
+ break;
+ default: break;
+ }
+ if (desc[n].skipfnc)
+ {
+ any_skip = 1;
+ need_keyid = 1;
+ }
+ }
+
+ rc = prepare_search (hd);
+ if (rc)
+ return rc;
+
+ use_offtbl = !hd->secret && kr_offtbl;
+ if (!use_offtbl)
+ ;
+ else if (!kr_offtbl_ready)
+ need_keyid = 1;
+ else if (ndesc == 1 && desc[0].mode == KEYDB_SEARCH_MODE_LONG_KID)
+ {
+ struct off_item *oi;
+
+ oi = lookup_offset_hash_table (kr_offtbl, desc[0].u.kid);
+ if (!oi)
+ { /* We know that we don't have this key */
+ hd->found.kr = NULL;
+ hd->current.eof = 1;
+ return -1;
+ }
+ /* We could now create a positive search status and return.
+ * However the problem is that another instance of gpg may
+ * have changed the keyring so that the offsets are not valid
+ * anymore - therefore we don't do it
+ */
+ }
+
+ if (need_words)
+ {
+ const char *name = NULL;
+
+ log_debug ("word search mode does not yet work\n");
+ /* FIXME: here is a long standing bug in our function and in addition we
+ just use the first search description */
+ for (n=0; n < ndesc && !name; n++)
+ {
+ if (desc[n].mode == KEYDB_SEARCH_MODE_WORDS)
+ name = desc[n].u.name;
+ }
+ assert (name);
+ if ( !hd->word_match.name || strcmp (hd->word_match.name, name) )
+ {
+ /* name changed */
+ xfree (hd->word_match.name);
+ xfree (hd->word_match.pattern);
+ hd->word_match.name = xstrdup (name);
+ hd->word_match.pattern = prepare_word_match (name);
+ }
+ name = hd->word_match.pattern;
+ }
+
+ init_packet(&pkt);
+ save_mode = set_packet_list_mode(0);
+
+ hd->found.kr = NULL;
+ main_offset = 0;
+ pk_no = uid_no = 0;
+ initial_skip = 1; /* skip until we see the start of a keyblock */
+ while (!(rc=search_packet (hd->current.iobuf, &pkt, &offset, need_uid)))
+ {
+ byte afp[MAX_FINGERPRINT_LEN];
+ size_t an;
+
+ if (pkt.pkttype == PKT_PUBLIC_KEY || pkt.pkttype == PKT_SECRET_KEY)
+ {
+ main_offset = offset;
+ pk_no = uid_no = 0;
+ initial_skip = 0;
+ }
+ if (initial_skip)
+ {
+ free_packet (&pkt);
+ continue;
+ }
+
+ pk = NULL;
+ sk = NULL;
+ uid = NULL;
+ if ( pkt.pkttype == PKT_PUBLIC_KEY
+ || pkt.pkttype == PKT_PUBLIC_SUBKEY)
+ {
+ pk = pkt.pkt.public_key;
+ ++pk_no;
+
+ if (need_fpr) {
+ fingerprint_from_pk (pk, afp, &an);
+ while (an < 20) /* fill up to 20 bytes */
+ afp[an++] = 0;
+ }
+ if (need_keyid)
+ keyid_from_pk (pk, aki);
+
+ if (use_offtbl && !kr_offtbl_ready)
+ update_offset_hash_table (kr_offtbl, aki, main_offset);
+ }
+ else if (pkt.pkttype == PKT_USER_ID)
+ {
+ uid = pkt.pkt.user_id;
+ ++uid_no;
+ }
+ else if ( pkt.pkttype == PKT_SECRET_KEY
+ || pkt.pkttype == PKT_SECRET_SUBKEY)
+ {
+ sk = pkt.pkt.secret_key;
+ ++pk_no;
+
+ if (need_fpr) {
+ fingerprint_from_sk (sk, afp, &an);
+ while (an < 20) /* fill up to 20 bytes */
+ afp[an++] = 0;
+ }
+ if (need_keyid)
+ keyid_from_sk (sk, aki);
+
+ }
+
+ for (n=0; n < ndesc; n++)
+ {
+ switch (desc[n].mode) {
+ case KEYDB_SEARCH_MODE_NONE:
+ BUG ();
+ break;
+ case KEYDB_SEARCH_MODE_EXACT:
+ case KEYDB_SEARCH_MODE_SUBSTR:
+ case KEYDB_SEARCH_MODE_MAIL:
+ case KEYDB_SEARCH_MODE_MAILSUB:
+ case KEYDB_SEARCH_MODE_MAILEND:
+ case KEYDB_SEARCH_MODE_WORDS:
+ if ( uid && !compare_name (desc[n].mode,
+ desc[n].u.name,
+ uid->name, uid->len))
+ goto found;
+ break;
+
+ case KEYDB_SEARCH_MODE_SHORT_KID:
+ if ((pk||sk) && desc[n].u.kid[1] == aki[1])
+ goto found;
+ break;
+ case KEYDB_SEARCH_MODE_LONG_KID:
+ if ((pk||sk) && desc[n].u.kid[0] == aki[0]
+ && desc[n].u.kid[1] == aki[1])
+ goto found;
+ break;
+ case KEYDB_SEARCH_MODE_FPR16:
+ if ((pk||sk) && !memcmp (desc[n].u.fpr, afp, 16))
+ goto found;
+ break;
+ case KEYDB_SEARCH_MODE_FPR20:
+ case KEYDB_SEARCH_MODE_FPR:
+ if ((pk||sk) && !memcmp (desc[n].u.fpr, afp, 20))
+ goto found;
+ break;
+ case KEYDB_SEARCH_MODE_FIRST:
+ if (pk||sk)
+ goto found;
+ break;
+ case KEYDB_SEARCH_MODE_NEXT:
+ if (pk||sk)
+ goto found;
+ break;
+ default:
+ rc = G10ERR_INV_ARG;
+ goto found;
+ }
+ }
+ free_packet (&pkt);
+ continue;
+ found:
+ /* Record which desc we matched on. Note this value is only
+ meaningful if this function returns with no errors. */
+ if(descindex)
+ *descindex=n;
+ for (n=any_skip?0:ndesc; n < ndesc; n++)
+ {
+ if (desc[n].skipfnc
+ && desc[n].skipfnc (desc[n].skipfncvalue, aki, uid))
+ break;
+ }
+ if (n == ndesc)
+ goto real_found;
+ free_packet (&pkt);
+ }
+ real_found:
+ if (!rc)
+ {
+ hd->found.offset = main_offset;
+ hd->found.kr = hd->current.kr;
+ hd->found.pk_no = (pk||sk)? pk_no : 0;
+ hd->found.uid_no = uid? uid_no : 0;
+ }
+ else if (rc == -1)
+ {
+ hd->current.eof = 1;
+ /* if we scanned all keyrings, we are sure that
+ * all known key IDs are in our offtbl, mark that. */
+ if (use_offtbl && !kr_offtbl_ready)
+ {
+ KR_NAME kr;
+
+ /* First set the did_full_scan flag for this keyring (ignore
+ secret keyrings) */
+ for (kr=kr_names; kr; kr = kr->next)
+ {
+ if (!kr->secret && hd->resource == kr)
+ {
+ kr->did_full_scan = 1;
+ break;
+ }
+ }
+ /* Then check whether all flags are set and if so, mark the
+ offtbl ready */
+ for (kr=kr_names; kr; kr = kr->next)
+ {
+ if (!kr->secret && !kr->did_full_scan)
+ break;
+ }
+ if (!kr)
+ kr_offtbl_ready = 1;
+ }
+ }
+ else
+ hd->current.error = rc;
+
+ free_packet(&pkt);
+ set_packet_list_mode(save_mode);
+ return rc;
+}
+
+
+static int
+create_tmp_file (const char *template,
+ char **r_bakfname, char **r_tmpfname, IOBUF *r_fp)
+{
+ char *bakfname, *tmpfname;
+ mode_t oldmask;
+
+ *r_bakfname = NULL;
+ *r_tmpfname = NULL;
+
+# ifdef USE_ONLY_8DOT3
+ /* Here is another Windoze bug?:
+ * you cant rename("pubring.gpg.tmp", "pubring.gpg");
+ * but rename("pubring.gpg.tmp", "pubring.aaa");
+ * works. So we replace .gpg by .bak or .tmp
+ */
+ if (strlen (template) > 4
+ && !strcmp (template+strlen(template)-4, EXTSEP_S "gpg") )
+ {
+ bakfname = xmalloc (strlen (template) + 1);
+ strcpy (bakfname, template);
+ strcpy (bakfname+strlen(template)-4, EXTSEP_S "bak");
+
+ tmpfname = xmalloc (strlen( template ) + 1 );
+ strcpy (tmpfname,template);
+ strcpy (tmpfname+strlen(template)-4, EXTSEP_S "tmp");
+ }
+ else
+ { /* file does not end with gpg; hmmm */
+ bakfname = xmalloc (strlen( template ) + 5);
+ strcpy (stpcpy(bakfname, template), EXTSEP_S "bak");
+
+ tmpfname = xmalloc (strlen( template ) + 5);
+ strcpy (stpcpy(tmpfname, template), EXTSEP_S "tmp");
+ }
+# else /* Posix file names */
+ bakfname = xmalloc (strlen( template ) + 2);
+ strcpy (stpcpy (bakfname,template),"~");
+
+ tmpfname = xmalloc (strlen( template ) + 5);
+ strcpy (stpcpy(tmpfname,template), EXTSEP_S "tmp");
+# endif /* Posix filename */
+
+ /* Create the temp file with limited access */
+ oldmask=umask(077);
+ if (is_secured_filename (tmpfname))
+ {
+ *r_fp = NULL;
+ errno = EPERM;
+ }
+ else
+ *r_fp = iobuf_create (tmpfname);
+ umask(oldmask);
+ if (!*r_fp)
+ {
+ log_error(_("can't create `%s': %s\n"), tmpfname, strerror(errno) );
+ xfree (tmpfname);
+ xfree (bakfname);
+ return G10ERR_OPEN_FILE;
+ }
+
+ *r_bakfname = bakfname;
+ *r_tmpfname = tmpfname;
+ return 0;
+}
+
+
+static int
+rename_tmp_file (const char *bakfname, const char *tmpfname,
+ const char *fname, int secret )
+{
+ int rc=0;
+
+ /* invalidate close caches*/
+ iobuf_ioctl (NULL, 2, 0, (char*)tmpfname );
+ iobuf_ioctl (NULL, 2, 0, (char*)bakfname );
+ iobuf_ioctl (NULL, 2, 0, (char*)fname );
+
+ /* first make a backup file except for secret keyrings */
+ if (!secret)
+ {
+#if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
+ remove (bakfname);
+#endif
+ if (rename (fname, bakfname) )
+ {
+ log_error ("renaming `%s' to `%s' failed: %s\n",
+ fname, bakfname, strerror(errno) );
+ return G10ERR_RENAME_FILE;
+ }
+ }
+
+ /* then rename the file */
+#if defined(HAVE_DOSISH_SYSTEM) || defined(__riscos__)
+ remove( fname );
+#endif
+ if (secret)
+ unregister_secured_file (fname);
+ if (rename (tmpfname, fname) )
+ {
+ log_error (_("renaming `%s' to `%s' failed: %s\n"),
+ tmpfname, fname, strerror(errno) );
+ register_secured_file (fname);
+ rc = G10ERR_RENAME_FILE;
+ if (secret)
+ {
+ log_info(_("WARNING: 2 files with confidential"
+ " information exists.\n"));
+ log_info(_("%s is the unchanged one\n"), fname );
+ log_info(_("%s is the new one\n"), tmpfname );
+ log_info(_("Please fix this possible security flaw\n"));
+ }
+ return rc;
+ }
+
+ /* Now make sure the file has the same permissions as the original */
+
+#ifndef HAVE_DOSISH_SYSTEM
+ {
+ struct stat statbuf;
+
+ statbuf.st_mode=S_IRUSR | S_IWUSR;
+
+ if(((secret && !opt.preserve_permissions) ||
+ (stat(bakfname,&statbuf)==0)) &&
+ (chmod(fname,statbuf.st_mode)==0))
+ ;
+ else
+ log_error("WARNING: unable to restore permissions to `%s': %s",
+ fname,strerror(errno));
+ }
+#endif
+
+ return 0;
+}
+
+
+static int
+write_keyblock (IOBUF fp, KBNODE keyblock)
+{
+ KBNODE kbctx = NULL, node;
+ int rc;
+
+ while ( (node = walk_kbnode (keyblock, &kbctx, 0)) )
+ {
+ if (node->pkt->pkttype == PKT_RING_TRUST)
+ continue; /* we write it later on our own */
+
+ if ( (rc = build_packet (fp, node->pkt) ))
+ {
+ log_error ("build_packet(%d) failed: %s\n",
+ node->pkt->pkttype, g10_errstr(rc) );
+ return rc;
+ }
+ if (node->pkt->pkttype == PKT_SIGNATURE)
+ { /* always write a signature cache packet */
+ PKT_signature *sig = node->pkt->pkt.signature;
+ unsigned int cacheval = 0;
+
+ if (sig->flags.checked)
+ {
+ cacheval |= 1;
+ if (sig->flags.valid)
+ cacheval |= 2;
+ }
+ iobuf_put (fp, 0xb0); /* old style packet 12, 1 byte len*/
+ iobuf_put (fp, 2); /* 2 bytes */
+ iobuf_put (fp, 0); /* unused */
+ if (iobuf_put (fp, cacheval)) {
+ log_error ("writing sigcache packet failed\n");
+ return G10ERR_WRITE_FILE;
+ }
+ }
+ }
+ return 0;
+}
+
+/*
+ * Walk over all public keyrings, check the signatures and replace the
+ * keyring with a new one where the signature cache is then updated.
+ * This is only done for the public keyrings.
+ */
+int
+keyring_rebuild_cache (void *token,int noisy)
+{
+ KEYRING_HANDLE hd;
+ KEYDB_SEARCH_DESC desc;
+ KBNODE keyblock = NULL, node;
+ const char *lastresname = NULL, *resname;
+ IOBUF tmpfp = NULL;
+ char *tmpfilename = NULL;
+ char *bakfilename = NULL;
+ int rc;
+ ulong count = 0, sigcount = 0;
+
+ hd = keyring_new (token, 0);
+ memset (&desc, 0, sizeof desc);
+ desc.mode = KEYDB_SEARCH_MODE_FIRST;
+
+ rc=keyring_lock (hd, 1);
+ if(rc)
+ goto leave;
+
+ while ( !(rc = keyring_search (hd, &desc, 1, NULL)) )
+ {
+ desc.mode = KEYDB_SEARCH_MODE_NEXT;
+ resname = keyring_get_resource_name (hd);
+ if (lastresname != resname )
+ { /* we have switched to a new keyring - commit changes */
+ if (tmpfp)
+ {
+ if (iobuf_close (tmpfp))
+ {
+ log_error ("error closing `%s': %s\n",
+ tmpfilename, strerror (errno));
+ rc = G10ERR_CLOSE_FILE;
+ goto leave;
+ }
+ /* because we have switched resources, we can be sure that
+ * the original file is closed */
+ tmpfp = NULL;
+ }
+ rc = lastresname? rename_tmp_file (bakfilename, tmpfilename,
+ lastresname, 0) : 0;
+ xfree (tmpfilename); tmpfilename = NULL;
+ xfree (bakfilename); bakfilename = NULL;
+ if (rc)
+ goto leave;
+ lastresname = resname;
+ if (noisy && !opt.quiet)
+ log_info (_("caching keyring `%s'\n"), resname);
+ rc = create_tmp_file (resname, &bakfilename, &tmpfilename, &tmpfp);
+ if (rc)
+ goto leave;
+ }
+
+ release_kbnode (keyblock);
+ rc = keyring_get_keyblock (hd, &keyblock);
+ if (rc)
+ {
+ log_error ("keyring_get_keyblock failed: %s\n", g10_errstr(rc));
+ goto leave;
+ }
+ assert (keyblock->pkt->pkttype == PKT_PUBLIC_KEY);
+
+ /* check all signature to set the signature's cache flags */
+ for (node=keyblock; node; node=node->next)
+ {
+ /* Note that this doesn't cache the result of a revocation
+ issued by a designated revoker. This is because the pk
+ in question does not carry the revkeys as we haven't
+ merged the key and selfsigs. It is questionable whether
+ this matters very much since there are very very few
+ designated revoker revocation packets out there. */
+
+ if (node->pkt->pkttype == PKT_SIGNATURE)
+ {
+ PKT_signature *sig=node->pkt->pkt.signature;
+
+ if(!opt.no_sig_cache && sig->flags.checked && sig->flags.valid
+ && (check_digest_algo(sig->digest_algo)
+ || check_pubkey_algo(sig->pubkey_algo)))
+ sig->flags.checked=sig->flags.valid=0;
+ else
+ check_key_signature (keyblock, node, NULL);
+
+ sigcount++;
+ }
+ }
+
+ /* write the keyblock to the temporary file */
+ rc = write_keyblock (tmpfp, keyblock);
+ if (rc)
+ goto leave;
+
+ if ( !(++count % 50) && noisy && !opt.quiet)
+ log_info(_("%lu keys cached so far (%lu signatures)\n"),
+ count, sigcount );
+
+ } /* end main loop */
+ if (rc == -1)
+ rc = 0;
+ if (rc)
+ {
+ log_error ("keyring_search failed: %s\n", g10_errstr(rc));
+ goto leave;
+ }
+ if(noisy || opt.verbose)
+ log_info(_("%lu keys cached (%lu signatures)\n"), count, sigcount );
+ if (tmpfp)
+ {
+ if (iobuf_close (tmpfp))
+ {
+ log_error ("error closing `%s': %s\n",
+ tmpfilename, strerror (errno));
+ rc = G10ERR_CLOSE_FILE;
+ goto leave;
+ }
+ /* because we have switched resources, we can be sure that
+ * the original file is closed */
+ tmpfp = NULL;
+ }
+ rc = lastresname? rename_tmp_file (bakfilename, tmpfilename,
+ lastresname, 0) : 0;
+ xfree (tmpfilename); tmpfilename = NULL;
+ xfree (bakfilename); bakfilename = NULL;
+
+ leave:
+ if (tmpfp)
+ iobuf_cancel (tmpfp);
+ xfree (tmpfilename);
+ xfree (bakfilename);
+ release_kbnode (keyblock);
+ keyring_lock (hd, 0);
+ keyring_release (hd);
+ return rc;
+}
+
+
+/****************
+ * Perform insert/delete/update operation.
+ * mode 1 = insert
+ * 2 = delete
+ * 3 = update
+ */
+static int
+do_copy (int mode, const char *fname, KBNODE root, int secret,
+ off_t start_offset, unsigned int n_packets )
+{
+ IOBUF fp, newfp;
+ int rc=0;
+ char *bakfname = NULL;
+ char *tmpfname = NULL;
+
+ /* Open the source file. Because we do a rename, we have to check the
+ permissions of the file */
+ if (access (fname, W_OK))
+ return G10ERR_WRITE_FILE;
+
+ fp = iobuf_open (fname);
+ if (mode == 1 && !fp && errno == ENOENT) {
+ /* insert mode but file does not exist: create a new file */
+ KBNODE kbctx, node;
+ mode_t oldmask;
+
+ oldmask=umask(077);
+ if (!secret && is_secured_filename (fname)) {
+ newfp = NULL;
+ errno = EPERM;
+ }
+ else
+ newfp = iobuf_create (fname);
+ umask(oldmask);
+ if( !newfp )
+ {
+ log_error (_("can't create `%s': %s\n"), fname, strerror(errno));
+ return G10ERR_OPEN_FILE;
+ }
+ if( !opt.quiet )
+ log_info(_("%s: keyring created\n"), fname );
+
+ kbctx=NULL;
+ while ( (node = walk_kbnode( root, &kbctx, 0 )) ) {
+ if( (rc = build_packet( newfp, node->pkt )) ) {
+ log_error("build_packet(%d) failed: %s\n",
+ node->pkt->pkttype, g10_errstr(rc) );
+ iobuf_cancel(newfp);
+ return G10ERR_WRITE_FILE;
+ }
+ }
+ if( iobuf_close(newfp) ) {
+ log_error ("%s: close failed: %s\n", fname, strerror(errno));
+ return G10ERR_CLOSE_FILE;
+ }
+ return 0; /* ready */
+ }
+
+ if( !fp )
+ {
+ log_error(_("can't open `%s': %s\n"), fname, strerror(errno) );
+ rc = G10ERR_OPEN_FILE;
+ goto leave;
+ }
+
+ /* Create the new file. */
+ rc = create_tmp_file (fname, &bakfname, &tmpfname, &newfp);
+ if (rc) {
+ iobuf_close(fp);
+ goto leave;
+ }
+ if (secret)
+ register_secured_file (tmpfname);
+
+ if( mode == 1 ) { /* insert */
+ /* copy everything to the new file */
+ rc = copy_all_packets (fp, newfp);
+ if( rc != -1 ) {
+ log_error("%s: copy to `%s' failed: %s\n",
+ fname, tmpfname, g10_errstr(rc) );
+ iobuf_close(fp);
+ if (secret)
+ unregister_secured_file (tmpfname);
+ iobuf_cancel(newfp);
+ goto leave;
+ }
+ rc = 0;
+ }
+
+ if( mode == 2 || mode == 3 ) { /* delete or update */
+ /* copy first part to the new file */
+ rc = copy_some_packets( fp, newfp, start_offset );
+ if( rc ) { /* should never get EOF here */
+ log_error ("%s: copy to `%s' failed: %s\n",
+ fname, tmpfname, g10_errstr(rc) );
+ iobuf_close(fp);
+ if (secret)
+ unregister_secured_file (tmpfname);
+ iobuf_cancel(newfp);
+ goto leave;
+ }
+ /* skip this keyblock */
+ assert( n_packets );
+ rc = skip_some_packets( fp, n_packets );
+ if( rc ) {
+ log_error("%s: skipping %u packets failed: %s\n",
+ fname, n_packets, g10_errstr(rc));
+ iobuf_close(fp);
+ if (secret)
+ unregister_secured_file (tmpfname);
+ iobuf_cancel(newfp);
+ goto leave;
+ }
+ }
+
+ if( mode == 1 || mode == 3 ) { /* insert or update */
+ rc = write_keyblock (newfp, root);
+ if (rc) {
+ iobuf_close(fp);
+ if (secret)
+ unregister_secured_file (tmpfname);
+ iobuf_cancel(newfp);
+ goto leave;
+ }
+ }
+
+ if( mode == 2 || mode == 3 ) { /* delete or update */
+ /* copy the rest */
+ rc = copy_all_packets( fp, newfp );
+ if( rc != -1 ) {
+ log_error("%s: copy to `%s' failed: %s\n",
+ fname, tmpfname, g10_errstr(rc) );
+ iobuf_close(fp);
+ if (secret)
+ unregister_secured_file (tmpfname);
+ iobuf_cancel(newfp);
+ goto leave;
+ }
+ rc = 0;
+ }
+
+ /* close both files */
+ if( iobuf_close(fp) ) {
+ log_error("%s: close failed: %s\n", fname, strerror(errno) );
+ rc = G10ERR_CLOSE_FILE;
+ goto leave;
+ }
+ if( iobuf_close(newfp) ) {
+ log_error("%s: close failed: %s\n", tmpfname, strerror(errno) );
+ rc = G10ERR_CLOSE_FILE;
+ goto leave;
+ }
+
+ rc = rename_tmp_file (bakfname, tmpfname, fname, secret);
+
+ leave:
+ xfree(bakfname);
+ xfree(tmpfname);
+ return rc;
+}
diff --git a/g10/keyring.h b/g10/keyring.h
new file mode 100644
index 0000000..8c4ea73
--- /dev/null
+++ b/g10/keyring.h
@@ -0,0 +1,46 @@
+/* keyring.h - Keyring operations
+ * Copyright (C) 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef GPG_KEYRING_H
+#define GPG_KEYRING_H 1
+
+#include "global.h"
+
+typedef struct keyring_handle *KEYRING_HANDLE;
+
+int keyring_register_filename (const char *fname, int secret, void **ptr);
+int keyring_is_writable (void *token);
+
+KEYRING_HANDLE keyring_new (void *token, int secret);
+void keyring_release (KEYRING_HANDLE hd);
+const char *keyring_get_resource_name (KEYRING_HANDLE hd);
+int keyring_lock (KEYRING_HANDLE hd, int yes);
+int keyring_get_keyblock (KEYRING_HANDLE hd, KBNODE *ret_kb);
+int keyring_update_keyblock (KEYRING_HANDLE hd, KBNODE kb);
+int keyring_insert_keyblock (KEYRING_HANDLE hd, KBNODE kb);
+int keyring_locate_writable (KEYRING_HANDLE hd);
+int keyring_delete_keyblock (KEYRING_HANDLE hd);
+int keyring_search_reset (KEYRING_HANDLE hd);
+int keyring_search (KEYRING_HANDLE hd, KEYDB_SEARCH_DESC *desc,
+ size_t ndesc, size_t *descindex);
+int keyring_rebuild_cache (void *token,int noisy);
+
+#endif /*GPG_KEYRING_H*/
diff --git a/g10/keyserver-internal.h b/g10/keyserver-internal.h
new file mode 100644
index 0000000..a365986
--- /dev/null
+++ b/g10/keyserver-internal.h
@@ -0,0 +1,54 @@
+/* keyserver-internal.h - Keyserver internals
+ * Copyright (C) 2001, 2002, 2004, 2005, 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef _KEYSERVER_INTERNAL_H_
+#define _KEYSERVER_INTERNAL_H_
+
+#include <time.h>
+#include "keyserver.h"
+#include "iobuf.h"
+#include "types.h"
+
+int parse_keyserver_options(char *options);
+void free_keyserver_spec(struct keyserver_spec *keyserver);
+struct keyserver_spec *keyserver_match(struct keyserver_spec *spec);
+struct keyserver_spec *parse_keyserver_uri(const char *string,
+ int require_scheme,
+ const char *configname,
+ unsigned int configlineno);
+struct keyserver_spec *parse_preferred_keyserver(PKT_signature *sig);
+int keyserver_export(STRLIST users);
+int keyserver_import(STRLIST users);
+int keyserver_import_fprint(const byte *fprint,size_t fprint_len,
+ struct keyserver_spec *keyserver);
+int keyserver_import_keyid(u32 *keyid,struct keyserver_spec *keyserver);
+int keyserver_refresh(STRLIST users);
+int keyserver_search(STRLIST tokens);
+int keyserver_fetch(STRLIST urilist);
+int keyserver_import_cert(const char *name,
+ unsigned char **fpr,size_t *fpr_len);
+int keyserver_import_pka(const char *name,unsigned char **fpr,size_t *fpr_len);
+int keyserver_import_name(const char *name,unsigned char **fpr,size_t *fpr_len,
+ struct keyserver_spec *keyserver);
+int keyserver_import_ldap(const char *name,
+ unsigned char **fpr,size_t *fpr_len);
+
+#endif /* !_KEYSERVER_INTERNAL_H_ */
diff --git a/g10/keyserver.c b/g10/keyserver.c
new file mode 100644
index 0000000..4f69245
--- /dev/null
+++ b/g10/keyserver.c
@@ -0,0 +1,2132 @@
+/* keyserver.c - generic keyserver code
+ * Copyright (C) 2001, 2002, 2003, 2004, 2005,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <ctype.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "filter.h"
+#include "keydb.h"
+#include "status.h"
+#include "exec.h"
+#include "main.h"
+#include "i18n.h"
+#include "iobuf.h"
+#include "memory.h"
+#include "ttyio.h"
+#include "options.h"
+#include "packet.h"
+#include "trustdb.h"
+#include "keyserver-internal.h"
+#include "util.h"
+
+struct keyrec
+{
+ KEYDB_SEARCH_DESC desc;
+ u32 createtime,expiretime;
+ int size,flags;
+ byte type;
+ IOBUF uidbuf;
+ unsigned int lines;
+};
+
+enum ks_action {KS_UNKNOWN=0,KS_GET,KS_GETNAME,KS_SEND,KS_SEARCH};
+
+static struct parse_options keyserver_opts[]=
+ {
+ /* some of these options are not real - just for the help
+ message */
+ {"max-cert-size",0,NULL,NULL},
+ {"include-revoked",0,NULL,N_("include revoked keys in search results")},
+ {"include-subkeys",0,NULL,N_("include subkeys when searching by key ID")},
+ {"use-temp-files",0,NULL,
+ N_("use temporary files to pass data to keyserver helpers")},
+ {"keep-temp-files",KEYSERVER_KEEP_TEMP_FILES,NULL,
+ N_("do not delete temporary files after using them")},
+ {"refresh-add-fake-v3-keyids",KEYSERVER_ADD_FAKE_V3,NULL,
+ NULL},
+ {"auto-key-retrieve",KEYSERVER_AUTO_KEY_RETRIEVE,NULL,
+ N_("automatically retrieve keys when verifying signatures")},
+ {"honor-keyserver-url",KEYSERVER_HONOR_KEYSERVER_URL,NULL,
+ N_("honor the preferred keyserver URL set on the key")},
+ {"honor-pka-record",KEYSERVER_HONOR_PKA_RECORD,NULL,
+ N_("honor the PKA record set on a key when retrieving keys")},
+ {NULL,0,NULL,NULL}
+ };
+
+static int keyserver_work(enum ks_action action,STRLIST list,
+ KEYDB_SEARCH_DESC *desc,int count,
+ unsigned char **fpr,size_t *fpr_len,
+ struct keyserver_spec *keyserver);
+
+/* Reasonable guess */
+#define DEFAULT_MAX_CERT_SIZE 16384
+
+static size_t max_cert_size=DEFAULT_MAX_CERT_SIZE;
+
+static void
+add_canonical_option(char *option,STRLIST *list)
+{
+ char *arg=argsplit(option);
+
+ if(arg)
+ {
+ char *joined;
+
+ joined=xmalloc(strlen(option)+1+strlen(arg)+1);
+ /* Make a canonical name=value form with no spaces */
+ strcpy(joined,option);
+ strcat(joined,"=");
+ strcat(joined,arg);
+ append_to_strlist(list,joined);
+ xfree(joined);
+ }
+ else
+ append_to_strlist(list,option);
+}
+
+int
+parse_keyserver_options(char *options)
+{
+ int ret=1;
+ char *tok;
+ char *max_cert=NULL;
+
+ keyserver_opts[0].value=&max_cert;
+
+ while((tok=optsep(&options)))
+ {
+ if(tok[0]=='\0')
+ continue;
+
+ /* For backwards compatibility. 1.2.x used honor-http-proxy and
+ there are a good number of documents published that recommend
+ it. */
+ if(ascii_strcasecmp(tok,"honor-http-proxy")==0)
+ tok="http-proxy";
+ else if(ascii_strcasecmp(tok,"no-honor-http-proxy")==0)
+ tok="no-http-proxy";
+
+ /* We accept quite a few possible options here - some options to
+ handle specially, the keyserver_options list, and import and
+ export options that pertain to keyserver operations. Note
+ that you must use strncasecmp here as there might be an
+ =argument attached which will foil the use of strcasecmp. */
+
+#ifdef EXEC_TEMPFILE_ONLY
+ if(ascii_strncasecmp(tok,"use-temp-files",14)==0 ||
+ ascii_strncasecmp(tok,"no-use-temp-files",17)==0)
+ log_info(_("WARNING: keyserver option `%s' is not used"
+ " on this platform\n"),tok);
+#else
+ if(ascii_strncasecmp(tok,"use-temp-files",14)==0)
+ opt.keyserver_options.options|=KEYSERVER_USE_TEMP_FILES;
+ else if(ascii_strncasecmp(tok,"no-use-temp-files",17)==0)
+ opt.keyserver_options.options&=~KEYSERVER_USE_TEMP_FILES;
+#endif
+ else if(!parse_options(tok,&opt.keyserver_options.options,
+ keyserver_opts,0)
+ && !parse_import_options(tok,
+ &opt.keyserver_options.import_options,0)
+ && !parse_export_options(tok,
+ &opt.keyserver_options.export_options,0))
+ {
+ /* All of the standard options have failed, so the option is
+ destined for a keyserver plugin. */
+ add_canonical_option(tok,&opt.keyserver_options.other);
+ }
+ }
+
+ if(max_cert)
+ {
+ max_cert_size=strtoul(max_cert,(char **)NULL,10);
+
+ if(max_cert_size==0)
+ max_cert_size=DEFAULT_MAX_CERT_SIZE;
+ }
+
+ return ret;
+}
+
+void
+free_keyserver_spec(struct keyserver_spec *keyserver)
+{
+ xfree(keyserver->uri);
+ xfree(keyserver->scheme);
+ xfree(keyserver->auth);
+ xfree(keyserver->host);
+ xfree(keyserver->port);
+ xfree(keyserver->path);
+ xfree(keyserver->opaque);
+ free_strlist(keyserver->options);
+ xfree(keyserver);
+}
+
+/* Return 0 for match */
+static int
+cmp_keyserver_spec(struct keyserver_spec *one,struct keyserver_spec *two)
+{
+ if(ascii_strcasecmp(one->scheme,two->scheme)==0)
+ {
+ if(one->host && two->host && ascii_strcasecmp(one->host,two->host)==0)
+ {
+ if((one->port && two->port
+ && ascii_strcasecmp(one->port,two->port)==0)
+ || (!one->port && !two->port))
+ return 0;
+ }
+ else if(one->opaque && two->opaque
+ && ascii_strcasecmp(one->opaque,two->opaque)==0)
+ return 0;
+ }
+
+ return 1;
+}
+
+/* Try and match one of our keyservers. If we can, return that. If
+ we can't, return our input. */
+struct keyserver_spec *
+keyserver_match(struct keyserver_spec *spec)
+{
+ struct keyserver_spec *ks;
+
+ for(ks=opt.keyserver;ks;ks=ks->next)
+ if(cmp_keyserver_spec(spec,ks)==0)
+ return ks;
+
+ return spec;
+}
+
+/* TODO: once we cut over to an all-curl world, we don't need this
+ parser any longer so it can be removed, or at least moved to
+ keyserver/ksutil.c for limited use in gpgkeys_ldap or the like. */
+
+struct keyserver_spec *
+parse_keyserver_uri(const char *string,int require_scheme,
+ const char *configname,unsigned int configlineno)
+{
+ int assume_hkp=0;
+ struct keyserver_spec *keyserver;
+ const char *idx;
+ int count;
+ char *uri,*options;
+
+ assert(string!=NULL);
+
+ keyserver=xmalloc_clear(sizeof(struct keyserver_spec));
+
+ uri=xstrdup(string);
+
+ options=strchr(uri,' ');
+ if(options)
+ {
+ char *tok;
+
+ *options='\0';
+ options++;
+
+ while((tok=optsep(&options)))
+ add_canonical_option(tok,&keyserver->options);
+ }
+
+ /* Get the scheme */
+
+ for(idx=uri,count=0;*idx && *idx!=':';idx++)
+ {
+ count++;
+
+ /* Do we see the start of an RFC-2732 ipv6 address here? If so,
+ there clearly isn't a scheme so get out early. */
+ if(*idx=='[')
+ {
+ /* Was the '[' the first thing in the string? If not, we
+ have a mangled scheme with a [ in it so fail. */
+ if(count==1)
+ break;
+ else
+ goto fail;
+ }
+ }
+
+ if(count==0)
+ goto fail;
+
+ if(*idx=='\0' || *idx=='[')
+ {
+ if(require_scheme)
+ return NULL;
+
+ /* Assume HKP if there is no scheme */
+ assume_hkp=1;
+ keyserver->scheme=xstrdup("hkp");
+
+ keyserver->uri=xmalloc(strlen(keyserver->scheme)+3+strlen(uri)+1);
+ strcpy(keyserver->uri,keyserver->scheme);
+ strcat(keyserver->uri,"://");
+ strcat(keyserver->uri,uri);
+ }
+ else
+ {
+ int i;
+
+ keyserver->uri=xstrdup(uri);
+
+ keyserver->scheme=xmalloc(count+1);
+
+ /* Force to lowercase */
+ for(i=0;i<count;i++)
+ keyserver->scheme[i]=ascii_tolower(uri[i]);
+
+ keyserver->scheme[i]='\0';
+
+ /* Skip past the scheme and colon */
+ uri+=count+1;
+ }
+
+ if(ascii_strcasecmp(keyserver->scheme,"x-broken-hkp")==0)
+ {
+ deprecated_warning(configname,configlineno,"x-broken-hkp",
+ "--keyserver-options ","broken-http-proxy");
+ xfree(keyserver->scheme);
+ keyserver->scheme=xstrdup("hkp");
+ append_to_strlist(&opt.keyserver_options.other,"broken-http-proxy");
+ }
+ else if(ascii_strcasecmp(keyserver->scheme,"x-hkp")==0)
+ {
+ /* Canonicalize this to "hkp" so it works with both the internal
+ and external keyserver interface. */
+ xfree(keyserver->scheme);
+ keyserver->scheme=xstrdup("hkp");
+ }
+
+ if(assume_hkp || (uri[0]=='/' && uri[1]=='/'))
+ {
+ /* Two slashes means network path. */
+
+ /* Skip over the "//", if any */
+ if(!assume_hkp)
+ uri+=2;
+
+ /* Do we have userinfo auth data present? */
+ for(idx=uri,count=0;*idx && *idx!='@' && *idx!='/';idx++)
+ count++;
+
+ /* We found a @ before the slash, so that means everything
+ before the @ is auth data. */
+ if(*idx=='@')
+ {
+ if(count==0)
+ goto fail;
+
+ keyserver->auth=xmalloc(count+1);
+ strncpy(keyserver->auth,uri,count);
+ keyserver->auth[count]='\0';
+ uri+=count+1;
+ }
+
+ /* Is it an RFC-2732 ipv6 [literal address] ? */
+ if(*uri=='[')
+ {
+ for(idx=uri+1,count=1;*idx
+ && ((isascii (*idx) && isxdigit(*idx))
+ || *idx==':' || *idx=='.');idx++)
+ count++;
+
+ /* Is the ipv6 literal address terminated? */
+ if(*idx==']')
+ count++;
+ else
+ goto fail;
+ }
+ else
+ for(idx=uri,count=0;*idx && *idx!=':' && *idx!='/';idx++)
+ count++;
+
+ if(count==0)
+ goto fail;
+
+ keyserver->host=xmalloc(count+1);
+ strncpy(keyserver->host,uri,count);
+ keyserver->host[count]='\0';
+
+ /* Skip past the host */
+ uri+=count;
+
+ if(*uri==':')
+ {
+ /* It would seem to be reasonable to limit the range of the
+ ports to values between 1-65535, but RFC 1738 and 1808
+ imply there is no limit. Of course, the real world has
+ limits. */
+
+ for(idx=uri+1,count=0;*idx && *idx!='/';idx++)
+ {
+ count++;
+
+ /* Ports are digits only */
+ if(!digitp(idx))
+ goto fail;
+ }
+
+ keyserver->port=xmalloc(count+1);
+ strncpy(keyserver->port,uri+1,count);
+ keyserver->port[count]='\0';
+
+ /* Skip past the colon and port number */
+ uri+=1+count;
+ }
+
+ /* Everything else is the path */
+ if(*uri)
+ keyserver->path=xstrdup(uri);
+ else
+ keyserver->path=xstrdup("/");
+
+ if(keyserver->path[1])
+ keyserver->flags.direct_uri=1;
+ }
+ else if(uri[0]!='/')
+ {
+ /* No slash means opaque. Just record the opaque blob and get
+ out. */
+ keyserver->opaque=xstrdup(uri);
+ }
+ else
+ {
+ /* One slash means absolute path. We don't need to support that
+ yet. */
+ goto fail;
+ }
+
+ return keyserver;
+
+ fail:
+ free_keyserver_spec(keyserver);
+
+ return NULL;
+}
+
+struct keyserver_spec *
+parse_preferred_keyserver(PKT_signature *sig)
+{
+ struct keyserver_spec *spec=NULL;
+ const byte *p;
+ size_t plen;
+
+ p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_PREF_KS,&plen);
+ if(p && plen)
+ {
+ byte *dupe=xmalloc(plen+1);
+
+ memcpy(dupe,p,plen);
+ dupe[plen]='\0';
+ spec=parse_keyserver_uri(dupe,1,NULL,0);
+ xfree(dupe);
+ }
+
+ return spec;
+}
+
+static void
+print_keyrec(int number,struct keyrec *keyrec)
+{
+ int i;
+
+ iobuf_writebyte(keyrec->uidbuf,0);
+ iobuf_flush_temp(keyrec->uidbuf);
+ printf("(%d)\t%s ",number,iobuf_get_temp_buffer(keyrec->uidbuf));
+
+ if(keyrec->size>0)
+ printf("%d bit ",keyrec->size);
+
+ if(keyrec->type)
+ {
+ const char *str=pubkey_algo_to_string(keyrec->type);
+
+ if(str)
+ printf("%s ",str);
+ else
+ printf("unknown ");
+ }
+
+ switch(keyrec->desc.mode)
+ {
+ /* If the keyserver helper gave us a short keyid, we have no
+ choice but to use it. Do check --keyid-format to add a 0x if
+ needed. */
+ case KEYDB_SEARCH_MODE_SHORT_KID:
+ printf("key %s%08lX",
+ (opt.keyid_format==KF_0xSHORT
+ || opt.keyid_format==KF_0xLONG)?"0x":"",
+ (ulong)keyrec->desc.u.kid[1]);
+ break;
+
+ /* However, if it gave us a long keyid, we can honor
+ --keyid-format */
+ case KEYDB_SEARCH_MODE_LONG_KID:
+ printf("key %s",keystr(keyrec->desc.u.kid));
+ break;
+
+ case KEYDB_SEARCH_MODE_FPR16:
+ printf("key ");
+ for(i=0;i<16;i++)
+ printf("%02X",keyrec->desc.u.fpr[i]);
+ break;
+
+ case KEYDB_SEARCH_MODE_FPR20:
+ printf("key ");
+ for(i=0;i<20;i++)
+ printf("%02X",keyrec->desc.u.fpr[i]);
+ break;
+
+ default:
+ BUG();
+ break;
+ }
+
+ if(keyrec->createtime>0)
+ {
+ printf(", ");
+ printf(_("created: %s"),strtimestamp(keyrec->createtime));
+ }
+
+ if(keyrec->expiretime>0)
+ {
+ printf(", ");
+ printf(_("expires: %s"),strtimestamp(keyrec->expiretime));
+ }
+
+ if(keyrec->flags&1)
+ printf(" (%s)",_("revoked"));
+ if(keyrec->flags&2)
+ printf(" (%s)",_("disabled"));
+ if(keyrec->flags&4)
+ printf(" (%s)",_("expired"));
+
+ printf("\n");
+}
+
+/* Returns a keyrec (which must be freed) once a key is complete, and
+ NULL otherwise. Call with a NULL keystring once key parsing is
+ complete to return any unfinished keys. */
+static struct keyrec *
+parse_keyrec(char *keystring)
+{
+ static struct keyrec *work=NULL;
+ struct keyrec *ret=NULL;
+ char *record;
+ int i;
+
+ if(keystring==NULL)
+ {
+ if(work==NULL)
+ return NULL;
+ else if(work->desc.mode==KEYDB_SEARCH_MODE_NONE)
+ {
+ xfree(work);
+ return NULL;
+ }
+ else
+ {
+ ret=work;
+ work=NULL;
+ return ret;
+ }
+ }
+
+ if(work==NULL)
+ {
+ work=xmalloc_clear(sizeof(struct keyrec));
+ work->uidbuf=iobuf_temp();
+ }
+
+ /* Remove trailing whitespace */
+ for(i=strlen(keystring);i>0;i--)
+ if(ascii_isspace(keystring[i-1]))
+ keystring[i-1]='\0';
+ else
+ break;
+
+ if((record=strsep(&keystring,":"))==NULL)
+ return ret;
+
+ if(ascii_strcasecmp("pub",record)==0)
+ {
+ char *tok;
+
+ if(work->desc.mode)
+ {
+ ret=work;
+ work=xmalloc_clear(sizeof(struct keyrec));
+ work->uidbuf=iobuf_temp();
+ }
+
+ if((tok=strsep(&keystring,":"))==NULL)
+ return ret;
+
+ classify_user_id(tok,&work->desc);
+ if(work->desc.mode!=KEYDB_SEARCH_MODE_SHORT_KID
+ && work->desc.mode!=KEYDB_SEARCH_MODE_LONG_KID
+ && work->desc.mode!=KEYDB_SEARCH_MODE_FPR16
+ && work->desc.mode!=KEYDB_SEARCH_MODE_FPR20)
+ {
+ work->desc.mode=KEYDB_SEARCH_MODE_NONE;
+ return ret;
+ }
+
+ /* Note all items after this are optional. This allows us to
+ have a pub line as simple as pub:keyid and nothing else. */
+
+ work->lines++;
+
+ if((tok=strsep(&keystring,":"))==NULL)
+ return ret;
+
+ work->type=atoi(tok);
+
+ if((tok=strsep(&keystring,":"))==NULL)
+ return ret;
+
+ work->size=atoi(tok);
+
+ if((tok=strsep(&keystring,":"))==NULL)
+ return ret;
+
+ if(atoi(tok)<=0)
+ work->createtime=0;
+ else
+ work->createtime=atoi(tok);
+
+ if((tok=strsep(&keystring,":"))==NULL)
+ return ret;
+
+ if(atoi(tok)<=0)
+ work->expiretime=0;
+ else
+ {
+ work->expiretime=atoi(tok);
+ /* Force the 'e' flag on if this key is expired. */
+ if(work->expiretime<=make_timestamp())
+ work->flags|=4;
+ }
+
+ if((tok=strsep(&keystring,":"))==NULL)
+ return ret;
+
+ while(*tok)
+ switch(*tok++)
+ {
+ case 'r':
+ case 'R':
+ work->flags|=1;
+ break;
+
+ case 'd':
+ case 'D':
+ work->flags|=2;
+ break;
+
+ case 'e':
+ case 'E':
+ work->flags|=4;
+ break;
+ }
+ }
+ else if(ascii_strcasecmp("uid",record)==0 && work->desc.mode)
+ {
+ char *userid,*tok,*decoded;
+
+ if((tok=strsep(&keystring,":"))==NULL)
+ return ret;
+
+ if(strlen(tok)==0)
+ return ret;
+
+ userid=tok;
+
+ /* By definition, de-%-encoding is always smaller than the
+ original string so we can decode in place. */
+
+ i=0;
+
+ while(*tok)
+ if(tok[0]=='%' && tok[1] && tok[2])
+ {
+ if((userid[i]=hextobyte(&tok[1]))==-1)
+ userid[i]='?';
+
+ i++;
+ tok+=3;
+ }
+ else
+ userid[i++]=*tok++;
+
+ /* We don't care about the other info provided in the uid: line
+ since no keyserver supports marking userids with timestamps
+ or revoked/expired/disabled yet. */
+
+ /* No need to check for control characters, as utf8_to_native
+ does this for us. */
+
+ decoded=utf8_to_native(userid,i,0);
+ if(strlen(decoded)>opt.screen_columns-10)
+ decoded[opt.screen_columns-10]='\0';
+ iobuf_writestr(work->uidbuf,decoded);
+ xfree(decoded);
+ iobuf_writestr(work->uidbuf,"\n\t");
+ work->lines++;
+ }
+
+ /* Ignore any records other than "pri" and "uid" for easy future
+ growth. */
+
+ return ret;
+}
+
+/* TODO: do this as a list sent to keyserver_work rather than calling
+ it once for each key to get the correct counts after the import
+ (cosmetics, really) and to better take advantage of the keyservers
+ that can do multiple fetches in one go (LDAP). */
+static int
+show_prompt(KEYDB_SEARCH_DESC *desc,int numdesc,int count,const char *search)
+{
+ char *answer;
+
+ if(count && opt.command_fd==-1)
+ {
+ static int from=1;
+ tty_printf("Keys %d-%d of %d for \"%s\". ",from,numdesc,count,search);
+ from=numdesc+1;
+ }
+
+ answer=cpr_get_no_help("keysearch.prompt",
+ _("Enter number(s), N)ext, or Q)uit > "));
+ /* control-d */
+ if(answer[0]=='\x04')
+ {
+ printf("Q\n");
+ answer[0]='q';
+ }
+
+ if(answer[0]=='q' || answer[0]=='Q')
+ {
+ xfree(answer);
+ return 1;
+ }
+ else if(atoi(answer)>=1 && atoi(answer)<=numdesc)
+ {
+ char *split=answer,*num;
+
+ while((num=strsep(&split," ,"))!=NULL)
+ if(atoi(num)>=1 && atoi(num)<=numdesc)
+ keyserver_work(KS_GET,NULL,&desc[atoi(num)-1],1,
+ NULL,NULL,opt.keyserver);
+
+ xfree(answer);
+ return 1;
+ }
+
+ return 0;
+}
+
+/* Count and searchstr are just for cosmetics. If the count is too
+ small, it will grow safely. If negative it disables the "Key x-y
+ of z" messages. searchstr should be UTF-8 (rather than native). */
+static void
+keyserver_search_prompt(IOBUF buffer,const char *searchstr)
+{
+ int i=0,validcount=0,started=0,header=0,count=1;
+ unsigned int maxlen,buflen,numlines=0;
+ KEYDB_SEARCH_DESC *desc;
+ byte *line=NULL;
+ char *localstr=NULL;
+
+ if(searchstr)
+ localstr=utf8_to_native(searchstr,strlen(searchstr),0);
+
+ desc=xmalloc(count*sizeof(KEYDB_SEARCH_DESC));
+
+ for(;;)
+ {
+ struct keyrec *keyrec;
+ int rl;
+
+ maxlen=1024;
+ rl=iobuf_read_line(buffer,&line,&buflen,&maxlen);
+
+ if(opt.with_colons)
+ {
+ if(!header && ascii_strncasecmp("SEARCH ",line,7)==0
+ && ascii_strncasecmp(" BEGIN",&line[strlen(line)-7],6)==0)
+ {
+ header=1;
+ continue;
+ }
+ else if(ascii_strncasecmp("SEARCH ",line,7)==0
+ && ascii_strncasecmp(" END",&line[strlen(line)-5],4)==0)
+ continue;
+
+ printf("%s",line);
+ }
+
+ /* Look for an info: line. The only current info: values
+ defined are the version and key count. */
+ if(!started && rl>0 && ascii_strncasecmp("info:",line,5)==0)
+ {
+ char *tok,*str=&line[5];
+
+ if((tok=strsep(&str,":"))!=NULL)
+ {
+ int version;
+
+ if(sscanf(tok,"%d",&version)!=1)
+ version=1;
+
+ if(version!=1)
+ {
+ log_error(_("invalid keyserver protocol "
+ "(us %d!=handler %d)\n"),1,version);
+ break;
+ }
+ }
+
+ if((tok=strsep(&str,":"))!=NULL && sscanf(tok,"%d",&count)==1)
+ {
+ if(count==0)
+ goto notfound;
+ else if(count<0)
+ count=10;
+ else
+ validcount=1;
+
+ desc=xrealloc(desc,count*sizeof(KEYDB_SEARCH_DESC));
+ }
+
+ started=1;
+ continue;
+ }
+
+ if(rl==0)
+ {
+ keyrec=parse_keyrec(NULL);
+
+ if(keyrec==NULL)
+ {
+ if(i==0)
+ {
+ count=0;
+ break;
+ }
+
+ if(i!=count)
+ validcount=0;
+
+ for(;;)
+ {
+ if(show_prompt(desc,i,validcount?count:0,localstr))
+ break;
+ validcount=0;
+ }
+
+ break;
+ }
+ }
+ else
+ keyrec=parse_keyrec(line);
+
+ if(i==count)
+ {
+ /* keyserver helper sent more keys than they claimed in the
+ info: line. */
+ count+=10;
+ desc=xrealloc(desc,count*sizeof(KEYDB_SEARCH_DESC));
+ validcount=0;
+ }
+
+ if(keyrec)
+ {
+ desc[i]=keyrec->desc;
+
+ if(!opt.with_colons)
+ {
+ /* screen_lines - 1 for the prompt. */
+ if(numlines+keyrec->lines>opt.screen_lines-1)
+ {
+ if(show_prompt(desc,i,validcount?count:0,localstr))
+ break;
+ else
+ numlines=0;
+ }
+
+ print_keyrec(i+1,keyrec);
+ }
+
+ numlines+=keyrec->lines;
+ iobuf_close(keyrec->uidbuf);
+ xfree(keyrec);
+
+ started=1;
+ i++;
+ }
+ }
+
+ notfound:
+ /* Leave this commented out or now, and perhaps for a very long
+ time. All HKPish servers return HTML error messages for
+ no-key-found. */
+ /*
+ if(!started)
+ log_info(_("keyserver does not support searching\n"));
+ else
+ */
+ if(count==0)
+ {
+ if(localstr)
+ log_info(_("key \"%s\" not found on keyserver\n"),localstr);
+ else
+ log_info(_("key not found on keyserver\n"));
+ }
+
+ xfree(localstr);
+ xfree(desc);
+ xfree(line);
+}
+
+/* We sometimes want to use a different gpgkeys_xxx for a given
+ protocol (for example, ldaps is handled by gpgkeys_ldap). Map
+ these here. */
+static const char *
+keyserver_typemap(const char *type)
+{
+ if(strcmp(type,"ldaps")==0)
+ return "ldap";
+ else
+ return type;
+}
+
+/* The PGP LDAP and the curl fetch-a-LDAP-object methodologies are
+ sufficiently different that we can't use curl to do LDAP. */
+static int
+direct_uri_map(const char *scheme,unsigned int is_direct)
+{
+ if(is_direct && strcmp(scheme,"ldap")==0)
+ return 1;
+
+ return 0;
+}
+
+#define GPGKEYS_PREFIX "gpgkeys_"
+#define GPGKEYS_CURL GPGKEYS_PREFIX "curl" EXEEXT
+#define GPGKEYS_PREFIX_LEN (strlen(GPGKEYS_CURL))
+#define KEYSERVER_ARGS_KEEP " -o \"%O\" \"%I\""
+#define KEYSERVER_ARGS_NOKEEP " -o \"%o\" \"%i\""
+
+static int
+keyserver_spawn(enum ks_action action,STRLIST list,KEYDB_SEARCH_DESC *desc,
+ int count,int *prog,unsigned char **fpr,size_t *fpr_len,
+ struct keyserver_spec *keyserver)
+{
+ int ret=0,i,gotversion=0,outofband=0;
+ STRLIST temp;
+ unsigned int maxlen,buflen;
+ char *command,*end,*searchstr=NULL;
+ byte *line=NULL;
+ struct exec_info *spawn;
+ const char *scheme;
+ const char *libexecdir = get_libexecdir ();
+
+ assert(keyserver);
+
+#ifdef EXEC_TEMPFILE_ONLY
+ opt.keyserver_options.options|=KEYSERVER_USE_TEMP_FILES;
+#endif
+
+ /* Build the filename for the helper to execute */
+ scheme=keyserver_typemap(keyserver->scheme);
+
+#ifdef DISABLE_KEYSERVER_PATH
+ /* Destroy any path we might have. This is a little tricky,
+ portability-wise. It's not correct to delete the PATH
+ environment variable, as that may fall back to a system built-in
+ PATH. Similarly, it is not correct to set PATH to the null
+ string (PATH="") since this actually deletes the PATH environment
+ variable under MinGW. The safest thing to do here is to force
+ PATH to be GNUPG_LIBEXECDIR. All this is not that meaningful on
+ Unix-like systems (since we're going to give a full path to
+ gpgkeys_foo), but on W32 it prevents loading any DLLs from
+ directories in %PATH%.
+
+ After some more thinking about this we came to the conclusion
+ that it is better to load the helpers from the directory where
+ the program of this process lives. Fortunately Windows provides
+ a way to retrieve this and our get_libexecdir function has been
+ modified to return just this. Setting the exec-path is not
+ anymore required.
+ set_exec_path(libexecdir);
+ */
+#else
+ if(opt.exec_path_set)
+ {
+ /* If exec-path was set, and DISABLE_KEYSERVER_PATH is
+ undefined, then don't specify a full path to gpgkeys_foo, so
+ that the PATH can work. */
+ command=xmalloc(GPGKEYS_PREFIX_LEN+strlen(scheme)+3+strlen(EXEEXT)+1);
+ command[0]='\0';
+ }
+ else
+#endif
+ {
+ /* Specify a full path to gpgkeys_foo. */
+ command=xmalloc(strlen(libexecdir)+strlen(DIRSEP_S)+
+ GPGKEYS_PREFIX_LEN+strlen(scheme)+3+strlen(EXEEXT)+1);
+ strcpy(command,libexecdir);
+ strcat(command,DIRSEP_S);
+ }
+
+ end=command+strlen(command);
+
+ /* Build a path for the keyserver helper. If it is direct_uri
+ (i.e. an object fetch and not a keyserver), then add "_uri" to
+ the end to distinguish the keyserver helper from an object
+ fetcher that can speak that protocol (this is a problem for
+ LDAP). */
+
+ strcat(command,GPGKEYS_PREFIX);
+ strcat(command,scheme);
+
+ /* This "_uri" thing is in case we need to call a direct handler
+ instead of the keyserver handler. This lets us use gpgkeys_curl
+ or gpgkeys_ldap_uri (we don't provide it, but a user might)
+ instead of gpgkeys_ldap to fetch things like
+ ldap://keyserver.pgp.com/o=PGP%20keys?pgpkey?sub?pgpkeyid=99242560 */
+
+ if(direct_uri_map(scheme,keyserver->flags.direct_uri))
+ strcat(command,"_uri");
+
+ strcat(command,EXEEXT);
+
+ /* Can we execute it? If not, try curl as our catchall. */
+ if(path_access(command,X_OK)!=0)
+ strcpy(end,GPGKEYS_CURL);
+
+ if(opt.keyserver_options.options&KEYSERVER_USE_TEMP_FILES)
+ {
+ if(opt.keyserver_options.options&KEYSERVER_KEEP_TEMP_FILES)
+ {
+ command=xrealloc(command,strlen(command)+
+ strlen(KEYSERVER_ARGS_KEEP)+1);
+ strcat(command,KEYSERVER_ARGS_KEEP);
+ }
+ else
+ {
+ command=xrealloc(command,strlen(command)+
+ strlen(KEYSERVER_ARGS_NOKEEP)+1);
+ strcat(command,KEYSERVER_ARGS_NOKEEP);
+ }
+
+ ret=exec_write(&spawn,NULL,command,NULL,0,0);
+ }
+ else
+ ret=exec_write(&spawn,command,NULL,NULL,0,0);
+
+ xfree(command);
+
+ if(ret)
+ return ret;
+
+ fprintf(spawn->tochild,
+ "# This is a GnuPG %s keyserver communications file\n",VERSION);
+ fprintf(spawn->tochild,"VERSION %d\n",KEYSERVER_PROTO_VERSION);
+ fprintf(spawn->tochild,"PROGRAM %s\n",VERSION);
+ fprintf(spawn->tochild,"SCHEME %s\n",keyserver->scheme);
+
+ if(keyserver->opaque)
+ fprintf(spawn->tochild,"OPAQUE %s\n",keyserver->opaque);
+ else
+ {
+ if(keyserver->auth)
+ fprintf(spawn->tochild,"AUTH %s\n",keyserver->auth);
+
+ if(keyserver->host)
+ fprintf(spawn->tochild,"HOST %s\n",keyserver->host);
+
+ if(keyserver->port)
+ fprintf(spawn->tochild,"PORT %s\n",keyserver->port);
+
+ if(keyserver->path)
+ fprintf(spawn->tochild,"PATH %s\n",keyserver->path);
+ }
+
+ /* Write global options */
+
+ for(temp=opt.keyserver_options.other;temp;temp=temp->next)
+ fprintf(spawn->tochild,"OPTION %s\n",temp->d);
+
+ /* Write per-keyserver options */
+
+ for(temp=keyserver->options;temp;temp=temp->next)
+ fprintf(spawn->tochild,"OPTION %s\n",temp->d);
+
+ switch(action)
+ {
+ case KS_GET:
+ {
+ fprintf(spawn->tochild,"COMMAND GET\n\n");
+
+ /* Which keys do we want? */
+
+ for(i=0;i<count;i++)
+ {
+ int quiet=0;
+
+ if(desc[i].mode==KEYDB_SEARCH_MODE_FPR20)
+ {
+ int f;
+
+ fprintf(spawn->tochild,"0x");
+
+ for(f=0;f<MAX_FINGERPRINT_LEN;f++)
+ fprintf(spawn->tochild,"%02X",desc[i].u.fpr[f]);
+
+ fprintf(spawn->tochild,"\n");
+ }
+ else if(desc[i].mode==KEYDB_SEARCH_MODE_FPR16)
+ {
+ int f;
+
+ fprintf(spawn->tochild,"0x");
+
+ for(f=0;f<16;f++)
+ fprintf(spawn->tochild,"%02X",desc[i].u.fpr[f]);
+
+ fprintf(spawn->tochild,"\n");
+ }
+ else if(desc[i].mode==KEYDB_SEARCH_MODE_LONG_KID)
+ fprintf(spawn->tochild,"0x%08lX%08lX\n",
+ (ulong)desc[i].u.kid[0],
+ (ulong)desc[i].u.kid[1]);
+ else if(desc[i].mode==KEYDB_SEARCH_MODE_SHORT_KID)
+ fprintf(spawn->tochild,"0x%08lX\n",
+ (ulong)desc[i].u.kid[1]);
+ else if(desc[i].mode==KEYDB_SEARCH_MODE_EXACT)
+ {
+ fprintf(spawn->tochild,"0x0000000000000000\n");
+ quiet=1;
+ }
+ else if(desc[i].mode==KEYDB_SEARCH_MODE_NONE)
+ continue;
+ else
+ BUG();
+
+ if(!quiet)
+ {
+ if(keyserver->host)
+ log_info(_("requesting key %s from %s server %s\n"),
+ keystr_from_desc(&desc[i]),
+ keyserver->scheme,keyserver->host);
+ else
+ log_info(_("requesting key %s from %s\n"),
+ keystr_from_desc(&desc[i]),keyserver->uri);
+ }
+ }
+
+ fprintf(spawn->tochild,"\n");
+
+ break;
+ }
+
+ case KS_GETNAME:
+ {
+ STRLIST key;
+
+ fprintf(spawn->tochild,"COMMAND GETNAME\n\n");
+
+ /* Which names do we want? */
+
+ for(key=list;key!=NULL;key=key->next)
+ fprintf(spawn->tochild,"%s\n",key->d);
+
+ fprintf(spawn->tochild,"\n");
+
+ if(keyserver->host)
+ log_info(_("searching for names from %s server %s\n"),
+ keyserver->scheme,keyserver->host);
+ else
+ log_info(_("searching for names from %s\n"),keyserver->uri);
+
+ break;
+ }
+
+ case KS_SEND:
+ {
+ STRLIST key;
+
+ /* Note the extra \n here to send an empty keylist block */
+ fprintf(spawn->tochild,"COMMAND SEND\n\n\n");
+
+ for(key=list;key!=NULL;key=key->next)
+ {
+ armor_filter_context_t *afx;
+ IOBUF buffer=iobuf_temp();
+ KBNODE block;
+
+ temp=NULL;
+ add_to_strlist(&temp,key->d);
+
+ afx = new_armor_context ();
+ afx->what = 1;
+ /* Tell the armor filter to use Unix-style \n line
+ endings, since we're going to fprintf this to a file
+ that (on Win32) is open in text mode. The win32 stdio
+ will transform the \n to \r\n and we'll end up with the
+ proper line endings on win32. This is a no-op on
+ Unix. */
+ afx->eol[0]='\n';
+ push_armor_filter (afx, buffer);
+ release_armor_context (afx);
+
+ /* TODO: Remove Comment: lines from keys exported this
+ way? */
+
+ if(export_pubkeys_stream(buffer,temp,&block,
+ opt.keyserver_options.export_options)==-1)
+ iobuf_close(buffer);
+ else
+ {
+ KBNODE node;
+
+ iobuf_flush_temp(buffer);
+
+ merge_keys_and_selfsig(block);
+
+ fprintf(spawn->tochild,"INFO %08lX%08lX BEGIN\n",
+ (ulong)block->pkt->pkt.public_key->keyid[0],
+ (ulong)block->pkt->pkt.public_key->keyid[1]);
+
+ for(node=block;node;node=node->next)
+ {
+ switch(node->pkt->pkttype)
+ {
+ default:
+ continue;
+
+ case PKT_PUBLIC_KEY:
+ case PKT_PUBLIC_SUBKEY:
+ {
+ PKT_public_key *pk=node->pkt->pkt.public_key;
+
+ keyid_from_pk(pk,NULL);
+
+ fprintf(spawn->tochild,"%sb:%08lX%08lX:%u:%u:%u:%u:",
+ node->pkt->pkttype==PKT_PUBLIC_KEY?"pu":"su",
+ (ulong)pk->keyid[0],(ulong)pk->keyid[1],
+ pk->pubkey_algo,
+ nbits_from_pk(pk),
+ pk->timestamp,
+ pk->expiredate);
+
+ if(pk->is_revoked)
+ fprintf(spawn->tochild,"r");
+ if(pk->has_expired)
+ fprintf(spawn->tochild,"e");
+
+ fprintf(spawn->tochild,"\n");
+ }
+ break;
+
+ case PKT_USER_ID:
+ {
+ PKT_user_id *uid=node->pkt->pkt.user_id;
+ int r;
+
+ if(uid->attrib_data)
+ continue;
+
+ fprintf(spawn->tochild,"uid:");
+
+ /* Quote ':', '%', and any 8-bit
+ characters */
+ for(r=0;r<uid->len;r++)
+ {
+ if(uid->name[r]==':' || uid->name[r]=='%'
+ || uid->name[r]&0x80)
+ fprintf(spawn->tochild,"%%%02X",
+ (byte)uid->name[r]);
+ else
+ fprintf(spawn->tochild,"%c",uid->name[r]);
+ }
+
+ fprintf(spawn->tochild,":%u:%u:",
+ uid->created,uid->expiredate);
+
+ if(uid->is_revoked)
+ fprintf(spawn->tochild,"r");
+ if(uid->is_expired)
+ fprintf(spawn->tochild,"e");
+
+ fprintf(spawn->tochild,"\n");
+ }
+ break;
+
+ /* This bit is really for the benefit of
+ people who store their keys in LDAP
+ servers. It makes it easy to do queries
+ for things like "all keys signed by
+ Isabella". */
+ case PKT_SIGNATURE:
+ {
+ PKT_signature *sig=node->pkt->pkt.signature;
+
+ if(!IS_UID_SIG(sig))
+ continue;
+
+ fprintf(spawn->tochild,"sig:%08lX%08lX:%X:%u:%u\n",
+ (ulong)sig->keyid[0],(ulong)sig->keyid[1],
+ sig->sig_class,sig->timestamp,
+ sig->expiredate);
+ }
+ break;
+ }
+ }
+
+ fprintf(spawn->tochild,"INFO %08lX%08lX END\n",
+ (ulong)block->pkt->pkt.public_key->keyid[0],
+ (ulong)block->pkt->pkt.public_key->keyid[1]);
+
+ fprintf(spawn->tochild,"KEY %08lX%08lX BEGIN\n",
+ (ulong)block->pkt->pkt.public_key->keyid[0],
+ (ulong)block->pkt->pkt.public_key->keyid[1]);
+ fwrite(iobuf_get_temp_buffer(buffer),
+ iobuf_get_temp_length(buffer),1,spawn->tochild);
+ fprintf(spawn->tochild,"KEY %08lX%08lX END\n",
+ (ulong)block->pkt->pkt.public_key->keyid[0],
+ (ulong)block->pkt->pkt.public_key->keyid[1]);
+
+ iobuf_close(buffer);
+
+ if(keyserver->host)
+ log_info(_("sending key %s to %s server %s\n"),
+ keystr(block->pkt->pkt.public_key->keyid),
+ keyserver->scheme,keyserver->host);
+ else
+ log_info(_("sending key %s to %s\n"),
+ keystr(block->pkt->pkt.public_key->keyid),
+ keyserver->uri);
+
+ release_kbnode(block);
+ }
+
+ free_strlist(temp);
+ }
+
+ break;
+ }
+
+ case KS_SEARCH:
+ {
+ STRLIST key;
+
+ fprintf(spawn->tochild,"COMMAND SEARCH\n\n");
+
+ /* Which keys do we want? Remember that the gpgkeys_ program
+ is going to lump these together into a search string. */
+
+ for(key=list;key!=NULL;key=key->next)
+ {
+ fprintf(spawn->tochild,"%s\n",key->d);
+ if(key!=list)
+ {
+ searchstr=xrealloc(searchstr,
+ strlen(searchstr)+strlen(key->d)+2);
+ strcat(searchstr," ");
+ }
+ else
+ {
+ searchstr=xmalloc(strlen(key->d)+1);
+ searchstr[0]='\0';
+ }
+
+ strcat(searchstr,key->d);
+ }
+
+ fprintf(spawn->tochild,"\n");
+
+ if(keyserver->host)
+ log_info(_("searching for \"%s\" from %s server %s\n"),
+ searchstr,keyserver->scheme,keyserver->host);
+ else
+ log_info(_("searching for \"%s\" from %s\n"),
+ searchstr,keyserver->uri);
+
+ break;
+ }
+
+ default:
+ log_fatal(_("no keyserver action!\n"));
+ break;
+ }
+
+ /* Done sending, so start reading. */
+ ret=exec_read(spawn);
+ if(ret)
+ goto fail;
+
+ /* Now handle the response */
+
+ for(;;)
+ {
+ int plen;
+ char *ptr;
+
+ maxlen=1024;
+ if(iobuf_read_line(spawn->fromchild,&line,&buflen,&maxlen)==0)
+ {
+ ret=G10ERR_READ_FILE;
+ goto fail; /* i.e. EOF */
+ }
+
+ ptr=line;
+
+ /* remove trailing whitespace */
+ plen=strlen(ptr);
+ while(plen>0 && ascii_isspace(ptr[plen-1]))
+ plen--;
+ plen[ptr]='\0';
+
+ if(*ptr=='\0')
+ break;
+
+ if(ascii_strncasecmp(ptr,"VERSION ",8)==0)
+ {
+ gotversion=1;
+
+ if(atoi(&ptr[8])!=KEYSERVER_PROTO_VERSION)
+ {
+ log_error(_("invalid keyserver protocol (us %d!=handler %d)\n"),
+ KEYSERVER_PROTO_VERSION,atoi(&ptr[8]));
+ goto fail;
+ }
+ }
+ else if(ascii_strncasecmp(ptr,"PROGRAM ",8)==0)
+ {
+ if(ascii_strncasecmp(&ptr[8],VERSION,strlen(VERSION))!=0)
+ log_info(_("WARNING: keyserver handler from a different"
+ " version of GnuPG (%s)\n"),&ptr[8]);
+ }
+ else if(ascii_strncasecmp(ptr,"OPTION OUTOFBAND",16)==0)
+ outofband=1; /* Currently the only OPTION */
+ }
+
+ if(!gotversion)
+ {
+ log_error(_("keyserver did not send VERSION\n"));
+ goto fail;
+ }
+
+ if(!outofband)
+ switch(action)
+ {
+ case KS_GET:
+ case KS_GETNAME:
+ {
+ void *stats_handle;
+
+ stats_handle=import_new_stats_handle();
+
+ /* Slurp up all the key data. In the future, it might be
+ nice to look for KEY foo OUTOFBAND and FAILED indicators.
+ It's harmless to ignore them, but ignoring them does make
+ gpg complain about "no valid OpenPGP data found". One
+ way to do this could be to continue parsing this
+ line-by-line and make a temp iobuf for each key. */
+
+ import_keys_stream(spawn->fromchild,stats_handle,fpr,fpr_len,
+ opt.keyserver_options.import_options);
+
+ import_print_stats(stats_handle);
+ import_release_stats_handle(stats_handle);
+
+ break;
+ }
+
+ /* Nothing to do here */
+ case KS_SEND:
+ break;
+
+ case KS_SEARCH:
+ keyserver_search_prompt(spawn->fromchild,searchstr);
+ break;
+
+ default:
+ log_fatal(_("no keyserver action!\n"));
+ break;
+ }
+
+ fail:
+ xfree(line);
+ xfree(searchstr);
+
+
+ *prog=exec_finish(spawn);
+
+ return ret;
+}
+
+static int
+keyserver_work(enum ks_action action,STRLIST list,KEYDB_SEARCH_DESC *desc,
+ int count,unsigned char **fpr,size_t *fpr_len,
+ struct keyserver_spec *keyserver)
+{
+ int rc=0,ret=0;
+
+ if(!keyserver)
+ {
+ log_error(_("no keyserver known (use option --keyserver)\n"));
+ return G10ERR_BAD_URI;
+ }
+
+#ifdef DISABLE_KEYSERVER_HELPERS
+
+ log_error(_("external keyserver calls are not supported in this build\n"));
+ return G10ERR_KEYSERVER;
+
+#else
+ /* Spawn a handler */
+
+ rc=keyserver_spawn(action,list,desc,count,&ret,fpr,fpr_len,keyserver);
+ if(ret)
+ {
+ switch(ret)
+ {
+ case KEYSERVER_SCHEME_NOT_FOUND:
+ log_error(_("no handler for keyserver scheme `%s'\n"),
+ keyserver->scheme);
+ break;
+
+ case KEYSERVER_NOT_SUPPORTED:
+ log_error(_("action `%s' not supported with keyserver "
+ "scheme `%s'\n"),
+ action==KS_GET?"get":action==KS_SEND?"send":
+ action==KS_SEARCH?"search":"unknown",
+ keyserver->scheme);
+ break;
+
+ case KEYSERVER_VERSION_ERROR:
+ log_error(_(GPGKEYS_PREFIX "%s does not support"
+ " handler version %d\n"),
+ keyserver_typemap(keyserver->scheme),
+ KEYSERVER_PROTO_VERSION);
+ break;
+
+ case KEYSERVER_TIMEOUT:
+ log_error(_("keyserver timed out\n"));
+ break;
+
+ case KEYSERVER_INTERNAL_ERROR:
+ default:
+ log_error(_("keyserver internal error\n"));
+ break;
+ }
+
+ return G10ERR_KEYSERVER;
+ }
+
+ if(rc)
+ {
+ log_error(_("keyserver communications error: %s\n"),g10_errstr(rc));
+
+ return rc;
+ }
+
+ return 0;
+#endif /* ! DISABLE_KEYSERVER_HELPERS*/
+}
+
+int
+keyserver_export(STRLIST users)
+{
+ STRLIST sl=NULL;
+ KEYDB_SEARCH_DESC desc;
+ int rc=0;
+
+ /* Weed out descriptors that we don't support sending */
+ for(;users;users=users->next)
+ {
+ classify_user_id (users->d, &desc);
+ if(desc.mode!=KEYDB_SEARCH_MODE_SHORT_KID &&
+ desc.mode!=KEYDB_SEARCH_MODE_LONG_KID &&
+ desc.mode!=KEYDB_SEARCH_MODE_FPR16 &&
+ desc.mode!=KEYDB_SEARCH_MODE_FPR20)
+ {
+ log_error(_("\"%s\" not a key ID: skipping\n"),users->d);
+ continue;
+ }
+ else
+ append_to_strlist(&sl,users->d);
+ }
+
+ if(sl)
+ {
+ rc=keyserver_work(KS_SEND,sl,NULL,0,NULL,NULL,opt.keyserver);
+ free_strlist(sl);
+ }
+
+ return rc;
+}
+
+int
+keyserver_import(STRLIST users)
+{
+ KEYDB_SEARCH_DESC *desc;
+ int num=100,count=0;
+ int rc=0;
+
+ /* Build a list of key ids */
+ desc=xmalloc(sizeof(KEYDB_SEARCH_DESC)*num);
+
+ for(;users;users=users->next)
+ {
+ classify_user_id (users->d, &desc[count]);
+ if(desc[count].mode!=KEYDB_SEARCH_MODE_SHORT_KID &&
+ desc[count].mode!=KEYDB_SEARCH_MODE_LONG_KID &&
+ desc[count].mode!=KEYDB_SEARCH_MODE_FPR16 &&
+ desc[count].mode!=KEYDB_SEARCH_MODE_FPR20)
+ {
+ log_error(_("\"%s\" not a key ID: skipping\n"),users->d);
+ continue;
+ }
+
+ count++;
+ if(count==num)
+ {
+ num+=100;
+ desc=xrealloc(desc,sizeof(KEYDB_SEARCH_DESC)*num);
+ }
+ }
+
+ if(count>0)
+ rc=keyserver_work(KS_GET,NULL,desc,count,NULL,NULL,opt.keyserver);
+
+ xfree(desc);
+
+ return rc;
+}
+
+int
+keyserver_import_fprint(const byte *fprint,size_t fprint_len,
+ struct keyserver_spec *keyserver)
+{
+ KEYDB_SEARCH_DESC desc;
+
+ memset(&desc,0,sizeof(desc));
+
+ if(fprint_len==16)
+ desc.mode=KEYDB_SEARCH_MODE_FPR16;
+ else if(fprint_len==20)
+ desc.mode=KEYDB_SEARCH_MODE_FPR20;
+ else
+ return -1;
+
+ memcpy(desc.u.fpr,fprint,fprint_len);
+
+ /* TODO: Warn here if the fingerprint we got doesn't match the one
+ we asked for? */
+ return keyserver_work(KS_GET,NULL,&desc,1,NULL,NULL,keyserver);
+}
+
+int
+keyserver_import_keyid(u32 *keyid,struct keyserver_spec *keyserver)
+{
+ KEYDB_SEARCH_DESC desc;
+
+ memset(&desc,0,sizeof(desc));
+
+ desc.mode=KEYDB_SEARCH_MODE_LONG_KID;
+ desc.u.kid[0]=keyid[0];
+ desc.u.kid[1]=keyid[1];
+
+ return keyserver_work(KS_GET,NULL,&desc,1,NULL,NULL,keyserver);
+}
+
+/* code mostly stolen from do_export_stream */
+static int
+keyidlist(STRLIST users,KEYDB_SEARCH_DESC **klist,int *count,int fakev3)
+{
+ int rc=0,ndesc,num=100;
+ KBNODE keyblock=NULL,node;
+ KEYDB_HANDLE kdbhd;
+ KEYDB_SEARCH_DESC *desc;
+ STRLIST sl;
+
+ *count=0;
+
+ *klist=xmalloc(sizeof(KEYDB_SEARCH_DESC)*num);
+
+ kdbhd=keydb_new(0);
+
+ if(!users)
+ {
+ ndesc = 1;
+ desc = xmalloc_clear ( ndesc * sizeof *desc);
+ desc[0].mode = KEYDB_SEARCH_MODE_FIRST;
+ }
+ else
+ {
+ for (ndesc=0, sl=users; sl; sl = sl->next, ndesc++)
+ ;
+ desc = xmalloc ( ndesc * sizeof *desc);
+
+ for (ndesc=0, sl=users; sl; sl = sl->next)
+ {
+ if(classify_user_id (sl->d, desc+ndesc))
+ ndesc++;
+ else
+ log_error (_("key \"%s\" not found: %s\n"),
+ sl->d, g10_errstr (G10ERR_INV_USER_ID));
+ }
+ }
+
+ while (!(rc = keydb_search (kdbhd, desc, ndesc)))
+ {
+ if (!users)
+ desc[0].mode = KEYDB_SEARCH_MODE_NEXT;
+
+ /* read the keyblock */
+ rc = keydb_get_keyblock (kdbhd, &keyblock );
+ if( rc )
+ {
+ log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) );
+ goto leave;
+ }
+
+ if((node=find_kbnode(keyblock,PKT_PUBLIC_KEY)))
+ {
+ /* This is to work around a bug in some keyservers (pksd and
+ OKS) that calculate v4 RSA keyids as if they were v3 RSA.
+ The answer is to refresh both the correct v4 keyid
+ (e.g. 99242560) and the fake v3 keyid (e.g. 68FDDBC7).
+ This only happens for key refresh using the HKP scheme
+ and if the refresh-add-fake-v3-keyids keyserver option is
+ set. */
+ if(fakev3 && is_RSA(node->pkt->pkt.public_key->pubkey_algo) &&
+ node->pkt->pkt.public_key->version>=4)
+ {
+ (*klist)[*count].mode=KEYDB_SEARCH_MODE_LONG_KID;
+ mpi_get_keyid(node->pkt->pkt.public_key->pkey[0],
+ (*klist)[*count].u.kid);
+ (*count)++;
+
+ if(*count==num)
+ {
+ num+=100;
+ *klist=xrealloc(*klist,sizeof(KEYDB_SEARCH_DESC)*num);
+ }
+ }
+
+ /* v4 keys get full fingerprints. v3 keys get long keyids.
+ This is because it's easy to calculate any sort of keyid
+ from a v4 fingerprint, but not a v3 fingerprint. */
+
+ if(node->pkt->pkt.public_key->version<4)
+ {
+ (*klist)[*count].mode=KEYDB_SEARCH_MODE_LONG_KID;
+ keyid_from_pk(node->pkt->pkt.public_key,
+ (*klist)[*count].u.kid);
+ }
+ else
+ {
+ size_t dummy;
+
+ (*klist)[*count].mode=KEYDB_SEARCH_MODE_FPR20;
+ fingerprint_from_pk(node->pkt->pkt.public_key,
+ (*klist)[*count].u.fpr,&dummy);
+ }
+
+ /* This is a little hackish, using the skipfncvalue as a
+ void* pointer to the keyserver spec, but we don't need
+ the skipfnc here, and it saves having an additional field
+ for this (which would be wasted space most of the
+ time). */
+
+ (*klist)[*count].skipfncvalue=NULL;
+
+ /* Are we honoring preferred keyservers? */
+ if(opt.keyserver_options.options&KEYSERVER_HONOR_KEYSERVER_URL)
+ {
+ PKT_user_id *uid=NULL;
+ PKT_signature *sig=NULL;
+
+ merge_keys_and_selfsig(keyblock);
+
+ for(node=node->next;node;node=node->next)
+ {
+ if(node->pkt->pkttype==PKT_USER_ID
+ && node->pkt->pkt.user_id->is_primary)
+ uid=node->pkt->pkt.user_id;
+ else if(node->pkt->pkttype==PKT_SIGNATURE
+ && node->pkt->pkt.signature->
+ flags.chosen_selfsig && uid)
+ {
+ sig=node->pkt->pkt.signature;
+ break;
+ }
+ }
+
+ /* Try and parse the keyserver URL. If it doesn't work,
+ then we end up writing NULL which indicates we are
+ the same as any other key. */
+ if(sig)
+ (*klist)[*count].skipfncvalue=parse_preferred_keyserver(sig);
+ }
+
+ (*count)++;
+
+ if(*count==num)
+ {
+ num+=100;
+ *klist=xrealloc(*klist,sizeof(KEYDB_SEARCH_DESC)*num);
+ }
+ }
+ }
+
+ if(rc==-1)
+ rc=0;
+
+ leave:
+ if(rc)
+ xfree(*klist);
+ xfree(desc);
+ keydb_release(kdbhd);
+ release_kbnode(keyblock);
+
+ return rc;
+}
+
+/* Note this is different than the original HKP refresh. It allows
+ usernames to refresh only part of the keyring. */
+
+int
+keyserver_refresh(STRLIST users)
+{
+ int rc,count,numdesc,fakev3=0;
+ KEYDB_SEARCH_DESC *desc;
+ unsigned int options=opt.keyserver_options.import_options;
+
+ /* We switch merge-only on during a refresh, as 'refresh' should
+ never import new keys, even if their keyids match. */
+ opt.keyserver_options.import_options|=IMPORT_MERGE_ONLY;
+
+ /* Similarly, we switch on fast-import, since refresh may make
+ multiple import sets (due to preferred keyserver URLs). We don't
+ want each set to rebuild the trustdb. Instead we do it once at
+ the end here. */
+ opt.keyserver_options.import_options|=IMPORT_FAST;
+
+ /* If refresh_add_fake_v3_keyids is on and it's a HKP or MAILTO
+ scheme, then enable fake v3 keyid generation. */
+ if((opt.keyserver_options.options&KEYSERVER_ADD_FAKE_V3) && opt.keyserver
+ && (ascii_strcasecmp(opt.keyserver->scheme,"hkp")==0 ||
+ ascii_strcasecmp(opt.keyserver->scheme,"mailto")==0))
+ fakev3=1;
+
+ rc=keyidlist(users,&desc,&numdesc,fakev3);
+ if(rc)
+ return rc;
+
+ count=numdesc;
+ if(count>0)
+ {
+ int i;
+
+ /* Try to handle preferred keyserver keys first */
+ for(i=0;i<numdesc;i++)
+ {
+ if(desc[i].skipfncvalue)
+ {
+ struct keyserver_spec *keyserver=desc[i].skipfncvalue;
+
+ /* We use the keyserver structure we parsed out before.
+ Note that a preferred keyserver without a scheme://
+ will be interpreted as hkp:// */
+
+ rc=keyserver_work(KS_GET,NULL,&desc[i],1,NULL,NULL,keyserver);
+ if(rc)
+ log_info(_("WARNING: unable to refresh key %s"
+ " via %s: %s\n"),keystr_from_desc(&desc[i]),
+ keyserver->uri,g10_errstr(rc));
+ else
+ {
+ /* We got it, so mark it as NONE so we don't try and
+ get it again from the regular keyserver. */
+
+ desc[i].mode=KEYDB_SEARCH_MODE_NONE;
+ count--;
+ }
+
+ free_keyserver_spec(keyserver);
+ }
+ }
+ }
+
+ if(count>0)
+ {
+ if(opt.keyserver)
+ {
+ if(count==1)
+ log_info(_("refreshing 1 key from %s\n"),opt.keyserver->uri);
+ else
+ log_info(_("refreshing %d keys from %s\n"),
+ count,opt.keyserver->uri);
+ }
+
+ rc=keyserver_work(KS_GET,NULL,desc,numdesc,NULL,NULL,opt.keyserver);
+ }
+
+ xfree(desc);
+
+ opt.keyserver_options.import_options=options;
+
+ /* If the original options didn't have fast import, and the trustdb
+ is dirty, rebuild. */
+ if(!(opt.keyserver_options.import_options&IMPORT_FAST))
+ trustdb_check_or_update();
+
+ return rc;
+}
+
+int
+keyserver_search(STRLIST tokens)
+{
+ if(tokens)
+ return keyserver_work(KS_SEARCH,tokens,NULL,0,NULL,NULL,opt.keyserver);
+ else
+ return 0;
+}
+
+int
+keyserver_fetch(STRLIST urilist)
+{
+ KEYDB_SEARCH_DESC desc;
+ STRLIST sl;
+ unsigned int options=opt.keyserver_options.import_options;
+
+ /* Switch on fast-import, since fetch can handle more than one
+ import and we don't want each set to rebuild the trustdb.
+ Instead we do it once at the end. */
+ opt.keyserver_options.import_options|=IMPORT_FAST;
+
+ /* A dummy desc since we're not actually fetching a particular key
+ ID */
+ memset(&desc,0,sizeof(desc));
+ desc.mode=KEYDB_SEARCH_MODE_EXACT;
+
+ for(sl=urilist;sl;sl=sl->next)
+ {
+ struct keyserver_spec *spec;
+
+ spec=parse_keyserver_uri(sl->d,1,NULL,0);
+ if(spec)
+ {
+ int rc;
+
+ rc=keyserver_work(KS_GET,NULL,&desc,1,NULL,NULL,spec);
+ if(rc)
+ log_info (_("WARNING: unable to fetch URI %s: %s\n"),
+ sl->d,g10_errstr(rc));
+
+ free_keyserver_spec(spec);
+ }
+ else
+ log_info (_("WARNING: unable to parse URI %s\n"),sl->d);
+ }
+
+ opt.keyserver_options.import_options=options;
+
+ /* If the original options didn't have fast import, and the trustdb
+ is dirty, rebuild. */
+ if(!(opt.keyserver_options.import_options&IMPORT_FAST))
+ trustdb_check_or_update();
+
+ return 0;
+}
+
+/* Import key in a CERT or pointed to by a CERT */
+int
+keyserver_import_cert(const char *name,unsigned char **fpr,size_t *fpr_len)
+{
+ char *domain,*look,*url;
+ IOBUF key;
+ int type,rc=G10ERR_GENERAL;
+
+ look=xstrdup(name);
+
+ domain=strrchr(look,'@');
+ if(domain)
+ *domain='.';
+
+ type=get_cert(look,max_cert_size,&key,fpr,fpr_len,&url);
+ if(type==1)
+ {
+ int armor_status=opt.no_armor;
+
+ /* CERTs are always in binary format */
+ opt.no_armor=1;
+
+ rc=import_keys_stream(key,NULL,fpr,fpr_len,
+ opt.keyserver_options.import_options);
+
+ opt.no_armor=armor_status;
+
+ iobuf_close(key);
+ }
+ else if(type==2 && *fpr)
+ {
+ /* We only consider the IPGP type if a fingerprint was provided.
+ This lets us select the right key regardless of what a URL
+ points to, or get the key from a keyserver. */
+ if(url)
+ {
+ struct keyserver_spec *spec;
+
+ spec=parse_keyserver_uri(url,1,NULL,0);
+ if(spec)
+ {
+ STRLIST list=NULL;
+
+ add_to_strlist(&list,url);
+
+ rc=keyserver_fetch(list);
+
+ free_strlist(list);
+ free_keyserver_spec(spec);
+ }
+ }
+ else if(opt.keyserver)
+ {
+ /* If only a fingerprint is provided, try and fetch it from
+ our --keyserver */
+
+ rc=keyserver_import_fprint(*fpr,*fpr_len,opt.keyserver);
+ }
+ else
+ log_info(_("no keyserver known (use option --keyserver)\n"));
+
+ /* Give a better string here? "CERT fingerprint for \"%s\"
+ found, but no keyserver" " known (use option
+ --keyserver)\n" ? */
+
+ xfree(url);
+ }
+
+ xfree(look);
+
+ return rc;
+}
+
+/* Import key pointed to by a PKA record. Return the requested
+ fingerprint in fpr. */
+int
+keyserver_import_pka(const char *name,unsigned char **fpr,size_t *fpr_len)
+{
+ char *uri;
+ int rc=-1;
+
+ *fpr=xmalloc(20);
+ *fpr_len=20;
+
+ uri = get_pka_info (name, *fpr);
+ if (uri)
+ {
+ struct keyserver_spec *spec;
+ spec = parse_keyserver_uri (uri, 1, NULL, 0);
+ if (spec)
+ {
+ rc=keyserver_import_fprint (*fpr, 20, spec);
+ free_keyserver_spec (spec);
+ }
+ xfree (uri);
+ }
+
+ if(rc!=0)
+ xfree(*fpr);
+
+ return rc;
+}
+
+/* Import all keys that match name */
+int
+keyserver_import_name(const char *name,unsigned char **fpr,size_t *fpr_len,
+ struct keyserver_spec *keyserver)
+{
+ STRLIST list=NULL;
+ int rc;
+
+ append_to_strlist(&list,name);
+
+ rc=keyserver_work(KS_GETNAME,list,NULL,0,fpr,fpr_len,keyserver);
+
+ free_strlist(list);
+
+ return rc;
+}
+
+/* Use the PGP Universal trick of asking ldap://keys.(maildomain) for
+ the key. */
+int
+keyserver_import_ldap(const char *name,unsigned char **fpr,size_t *fpr_len)
+{
+ char *domain;
+ struct keyserver_spec *keyserver;
+ STRLIST list=NULL;
+ int rc;
+
+ append_to_strlist(&list,name);
+
+ /* Parse out the domain */
+ domain=strrchr(name,'@');
+ if(!domain)
+ return G10ERR_GENERAL;
+
+ domain++;
+
+ keyserver=xmalloc_clear(sizeof(struct keyserver_spec));
+
+ keyserver->scheme=xstrdup("ldap");
+ keyserver->host=xmalloc(5+strlen(domain)+1);
+ strcpy(keyserver->host,"keys.");
+ strcat(keyserver->host,domain);
+ keyserver->uri=xmalloc(strlen(keyserver->scheme)+
+ 3+strlen(keyserver->host)+1);
+ strcpy(keyserver->uri,keyserver->scheme);
+ strcat(keyserver->uri,"://");
+ strcat(keyserver->uri,keyserver->host);
+
+ rc=keyserver_work(KS_GETNAME,list,NULL,0,fpr,fpr_len,keyserver);
+
+ free_strlist(list);
+
+ free_keyserver_spec(keyserver);
+
+ return rc;
+}
diff --git a/g10/main.h b/g10/main.h
new file mode 100644
index 0000000..7abb2b3
--- /dev/null
+++ b/g10/main.h
@@ -0,0 +1,300 @@
+/* main.h
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+#ifndef G10_MAIN_H
+#define G10_MAIN_H
+#include "types.h"
+#include "iobuf.h"
+#include "mpi.h"
+#include "cipher.h"
+#include "keydb.h"
+
+/* It could be argued that the default cipher should be 3DES rather
+ than CAST5, and the default compression should be 0
+ (i.e. uncompressed) rather than 1 (zip). However, the real world
+ issues of speed and size come into play here. */
+
+#define DEFAULT_CIPHER_ALGO CIPHER_ALGO_CAST5
+#define DEFAULT_DIGEST_ALGO DIGEST_ALGO_SHA1
+#define DEFAULT_COMPRESS_ALGO COMPRESS_ALGO_ZIP
+#define DEFAULT_S2K_DIGEST_ALGO DIGEST_ALGO_SHA1
+
+#define S2K_DIGEST_ALGO (opt.s2k_digest_algo?opt.s2k_digest_algo:DEFAULT_S2K_DIGEST_ALGO)
+
+typedef struct
+{
+ int header_okay;
+ PK_LIST pk_list;
+ DEK *symkey_dek;
+ STRING2KEY *symkey_s2k;
+ cipher_filter_context_t cfx;
+} encrypt_filter_context_t;
+
+struct groupitem
+{
+ char *name;
+ STRLIST values;
+ struct groupitem *next;
+};
+
+/*-- gpg.c --*/
+extern int g10_errors_seen;
+
+#if __GNUC__ > 2 || (__GNUC__ == 2 && __GNUC_MINOR__ >= 5 )
+ void g10_exit(int rc) __attribute__ ((noreturn));
+#else
+ void g10_exit(int rc);
+#endif
+void print_pubkey_algo_note( int algo );
+void print_cipher_algo_note( int algo );
+void print_digest_algo_note( int algo );
+
+/*-- armor.c --*/
+char *make_radix64_string( const byte *data, size_t len );
+
+/*-- misc.c --*/
+void trap_unaligned(void);
+int disable_core_dumps(void);
+void register_secured_file (const char *fname);
+void unregister_secured_file (const char *fname);
+int is_secured_file (int fd);
+int is_secured_filename (const char *fname);
+u16 checksum_u16( unsigned n );
+u16 checksum( byte *p, unsigned n );
+u16 checksum_mpi( MPI a );
+u32 buffer_to_u32( const byte *buffer );
+const byte *get_session_marker( size_t *rlen );
+int openpgp_cipher_test_algo( int algo );
+int openpgp_pk_test_algo( int algo, unsigned int usage_flags );
+int openpgp_pk_algo_usage ( int algo );
+int openpgp_md_test_algo( int algo );
+
+#ifdef USE_IDEA
+void idea_cipher_warn( int show );
+#else
+#define idea_cipher_warn(a)
+#endif
+
+struct expando_args
+{
+ PKT_public_key *pk;
+ PKT_secret_key *sk;
+ byte imagetype;
+};
+
+char *pct_expando(const char *string,struct expando_args *args);
+void deprecated_warning(const char *configname,unsigned int configlineno,
+ const char *option,const char *repl1,const char *repl2);
+void deprecated_command (const char *name);
+
+const char *compress_algo_to_string(int algo);
+int string_to_compress_algo(const char *string);
+int check_compress_algo(int algo);
+int default_cipher_algo(void);
+int default_compress_algo(void);
+const char *compliance_option_string(void);
+void compliance_failure(void);
+
+struct parse_options
+{
+ char *name;
+ unsigned int bit;
+ char **value;
+ char *help;
+};
+
+char *optsep(char **stringp);
+char *argsplit(char *string);
+int parse_options(char *str,unsigned int *options,
+ struct parse_options *opts,int noisy);
+char *unescape_percent_string (const unsigned char *s);
+int has_invalid_email_chars (const char *s);
+int is_valid_mailbox (const char *name);
+char *default_homedir (void);
+const char *get_libexecdir (void);
+int path_access(const char *file,int mode);
+
+/*-- helptext.c --*/
+void display_online_help( const char *keyword );
+
+/*-- encode.c --*/
+int setup_symkey(STRING2KEY **symkey_s2k,DEK **symkey_dek);
+int encode_symmetric( const char *filename );
+int encode_store( const char *filename );
+int encode_crypt( const char *filename, STRLIST remusr, int use_symkey );
+void encode_crypt_files(int nfiles, char **files, STRLIST remusr);
+int encrypt_filter( void *opaque, int control,
+ IOBUF a, byte *buf, size_t *ret_len);
+
+
+/*-- sign.c --*/
+int complete_sig( PKT_signature *sig, PKT_secret_key *sk, MD_HANDLE md );
+int sign_file( STRLIST filenames, int detached, STRLIST locusr,
+ int do_encrypt, STRLIST remusr, const char *outfile );
+int clearsign_file( const char *fname, STRLIST locusr, const char *outfile );
+int sign_symencrypt_file (const char *fname, STRLIST locusr);
+
+/*-- sig-check.c --*/
+int check_revocation_keys (PKT_public_key *pk, PKT_signature *sig);
+int check_backsig(PKT_public_key *main_pk,PKT_public_key *sub_pk,
+ PKT_signature *backsig);
+int check_key_signature( KBNODE root, KBNODE node, int *is_selfsig );
+int check_key_signature2( KBNODE root, KBNODE node, PKT_public_key *check_pk,
+ PKT_public_key *ret_pk, int *is_selfsig,
+ u32 *r_expiredate, int *r_expired );
+
+/*-- delkey.c --*/
+int delete_keys( STRLIST names, int secret, int allow_both );
+
+/*-- keyedit.c --*/
+void keyedit_menu( const char *username, STRLIST locusr,
+ STRLIST commands, int quiet, int seckey_check );
+void show_basic_key_info (KBNODE keyblock);
+
+/*-- keygen.c --*/
+u32 parse_expire_string(const char *string);
+u32 ask_expire_interval(int object,const char *def_expire);
+u32 ask_expiredate(void);
+void generate_keypair( const char *fname, const char *card_serialno,
+ const char *backup_encryption_dir );
+int keygen_set_std_prefs (const char *string,int personal);
+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 );
+int keygen_upd_std_prefs( PKT_signature *sig, void *opaque );
+int keygen_add_keyserver_url(PKT_signature *sig, void *opaque);
+int keygen_add_notations(PKT_signature *sig,void *opaque);
+int keygen_add_revkey(PKT_signature *sig, void *opaque);
+int make_backsig(PKT_signature *sig,PKT_public_key *pk,
+ PKT_public_key *sub_pk,PKT_secret_key *sub_sk);
+int generate_subkeypair( KBNODE pub_keyblock, KBNODE sec_keyblock );
+#ifdef ENABLE_CARD_SUPPORT
+int generate_card_subkeypair (KBNODE pub_keyblock, KBNODE sec_keyblock,
+ int keyno, const char *serialno);
+int save_unprotected_key_to_card (PKT_secret_key *sk, int keyno);
+#endif
+
+/*-- openfile.c --*/
+int overwrite_filep( const char *fname );
+char *make_outfile_name( const char *iname );
+char *ask_outfile_name( const char *name, size_t namelen );
+int open_outfile( const char *iname, int mode, IOBUF *a );
+IOBUF open_sigfile( const char *iname, progress_filter_context_t *pfx );
+void try_make_homedir( const char *fname );
+
+/*-- seskey.c --*/
+void make_session_key( DEK *dek );
+MPI encode_session_key( DEK *dek, unsigned nbits );
+MPI encode_md_value( PKT_public_key *pk, PKT_secret_key *sk,
+ MD_HANDLE md, int hash_algo );
+
+/*-- import.c --*/
+int parse_import_options(char *str,unsigned int *options,int noisy);
+void import_keys( char **fnames, int nnames,
+ void *stats_hd, unsigned int options );
+int import_keys_stream( IOBUF inp,void *stats_hd,unsigned char **fpr,
+ size_t *fpr_len,unsigned int options );
+void *import_new_stats_handle (void);
+void import_release_stats_handle (void *p);
+void import_print_stats (void *hd);
+
+int collapse_uids( KBNODE *keyblock );
+
+int auto_create_card_key_stub ( const char *serialnostr,
+ const unsigned char *fpr1,
+ const unsigned char *fpr2,
+ const unsigned char *fpr3);
+
+/*-- export.c --*/
+int parse_export_options(char *str,unsigned int *options,int noisy);
+int export_pubkeys( STRLIST users, unsigned int options );
+int export_pubkeys_stream( IOBUF out, STRLIST users,
+ KBNODE *keyblock_out, unsigned int options );
+int export_seckeys( STRLIST users );
+int export_secsubkeys( STRLIST users );
+
+/* dearmor.c --*/
+int dearmor_file( const char *fname );
+int enarmor_file( const char *fname );
+
+/*-- revoke.c --*/
+struct revocation_reason_info;
+int gen_revoke( const char *uname );
+int gen_desig_revoke( const char *uname, STRLIST locusr);
+int revocation_reason_build_cb( PKT_signature *sig, void *opaque );
+struct revocation_reason_info *
+ ask_revocation_reason( int key_rev, int cert_rev, int hint );
+void release_revocation_reason_info( struct revocation_reason_info *reason );
+
+/*-- keylist.c --*/
+void public_key_list( STRLIST list );
+void secret_key_list( STRLIST list );
+void print_subpackets_colon(PKT_signature *sig);
+void reorder_keyblock (KBNODE keyblock);
+void list_keyblock( KBNODE keyblock, int secret, int fpr, void *opaque );
+void print_fingerprint (PKT_public_key *pk, PKT_secret_key *sk, int mode);
+void print_revokers(PKT_public_key *pk);
+void show_policy_url(PKT_signature *sig,int indent,int mode);
+void show_keyserver_url(PKT_signature *sig,int indent,int mode);
+void show_notation(PKT_signature *sig,int indent,int mode,int which);
+void dump_attribs(const PKT_user_id *uid,
+ PKT_public_key *pk,PKT_secret_key *sk);
+void set_attrib_fd(int fd);
+void print_seckey_info (PKT_secret_key *sk);
+void print_pubkey_info (FILE *fp, PKT_public_key *pk);
+void print_card_key_info (FILE *fp, KBNODE keyblock);
+
+/*-- verify.c --*/
+void print_file_status( int status, const char *name, int what );
+int verify_signatures( int nfiles, char **files );
+int verify_files( int nfiles, char **files );
+
+/*-- decrypt.c --*/
+int decrypt_message( const char *filename );
+void decrypt_messages(int nfiles, char *files[]);
+
+/*-- plaintext.c --*/
+int hash_datafiles( MD_HANDLE md, MD_HANDLE md2,
+ STRLIST files, const char *sigfilename, int textmode );
+PKT_plaintext *setup_plaintext_name(const char *filename,IOBUF iobuf);
+
+/*-- pipemode.c --*/
+void run_in_pipemode (void);
+
+/*-- signal.c --*/
+void init_signals(void);
+void pause_on_sigusr( int which );
+void block_all_signals(void);
+void unblock_all_signals(void);
+
+
+#ifdef ENABLE_CARD_SUPPORT
+/*-- card-util.c --*/
+void change_pin (int no, int allow_admin);
+void card_status (FILE *fp, char *serialno, size_t serialnobuflen);
+void card_edit (STRLIST commands);
+int card_generate_subkey (KBNODE pub_keyblock, KBNODE sec_keyblock);
+int card_store_subkey (KBNODE node, int use);
+#endif
+
+#define S2K_DECODE_COUNT(_val) ((16ul + ((_val) & 15)) << (((_val) >> 4) + 6))
+
+#endif /*G10_MAIN_H*/
diff --git a/g10/mainproc.c b/g10/mainproc.c
new file mode 100644
index 0000000..cb4432a
--- /dev/null
+++ b/g10/mainproc.c
@@ -0,0 +1,2083 @@
+/* mainproc.c - handle packets
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ * 2005, 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <time.h>
+
+#include "packet.h"
+#include "iobuf.h"
+#include "memory.h"
+#include "options.h"
+#include "util.h"
+#include "cipher.h"
+#include "keydb.h"
+#include "filter.h"
+#include "main.h"
+#include "status.h"
+#include "i18n.h"
+#include "trustdb.h"
+#include "keyserver-internal.h"
+#include "photoid.h"
+
+
+struct kidlist_item {
+ struct kidlist_item *next;
+ u32 kid[2];
+ int pubkey_algo;
+ int reason;
+};
+
+
+/****************
+ * Structure to hold the context
+ */
+typedef struct mainproc_context *CTX;
+struct mainproc_context
+{
+ struct mainproc_context *anchor; /* May be useful in the future. */
+ PKT_public_key *last_pubkey;
+ PKT_secret_key *last_seckey;
+ PKT_user_id *last_user_id;
+ md_filter_context_t mfx;
+ int sigs_only; /* Process only signatures and reject all other stuff. */
+ int encrypt_only; /* Process only encryption messages. */
+ STRLIST signed_data;
+ const char *sigfilename;
+ DEK *dek;
+ int last_was_session_key;
+ KBNODE list; /* The current list of packets. */
+ int have_data;
+ IOBUF iobuf; /* Used to get the filename etc. */
+ int trustletter; /* Temporary usage in list_node. */
+ ulong symkeys;
+ struct kidlist_item *pkenc_list; /* List of encryption packets. */
+ struct
+ {
+ int op;
+ int stop_now;
+ } pipemode;
+ int any_sig_seen; /* Set to true if a signature packet has been seen. */
+};
+
+
+static int do_proc_packets( CTX c, IOBUF a );
+
+static void list_node( CTX c, KBNODE node );
+static void proc_tree( CTX c, KBNODE node );
+
+
+static void
+release_list( CTX c )
+{
+ if( !c->list )
+ return;
+ proc_tree(c, c->list );
+ release_kbnode( c->list );
+ while( c->pkenc_list ) {
+ struct kidlist_item *tmp = c->pkenc_list->next;
+ xfree( c->pkenc_list );
+ c->pkenc_list = tmp;
+ }
+ c->pkenc_list = NULL;
+ c->list = NULL;
+ c->have_data = 0;
+ c->last_was_session_key = 0;
+ c->pipemode.op = 0;
+ c->pipemode.stop_now = 0;
+ xfree(c->dek); c->dek = NULL;
+}
+
+
+static int
+add_onepass_sig( CTX c, PACKET *pkt )
+{
+ KBNODE node;
+
+ if ( c->list ) /* add another packet */
+ add_kbnode( c->list, new_kbnode( pkt ));
+ else /* insert the first one */
+ c->list = node = new_kbnode( pkt );
+
+ return 1;
+}
+
+
+static int
+add_gpg_control( CTX c, PACKET *pkt )
+{
+ if ( pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START ) {
+ /* New clear text signature.
+ * Process the last one and reset everything */
+ release_list(c);
+ }
+ else if ( pkt->pkt.gpg_control->control == CTRLPKT_PIPEMODE ) {
+ /* Pipemode control packet */
+ if ( pkt->pkt.gpg_control->datalen < 2 )
+ log_fatal ("invalid pipemode control packet length\n");
+ if (pkt->pkt.gpg_control->data[0] == 1) {
+ /* start the whole thing */
+ assert ( !c->list ); /* we should be in a pretty virgin state */
+ assert ( !c->pipemode.op );
+ c->pipemode.op = pkt->pkt.gpg_control->data[1];
+ }
+ else if (pkt->pkt.gpg_control->data[0] == 2) {
+ /* the signed material follows in a plaintext packet */
+ assert ( c->pipemode.op == 'B' );
+ }
+ else if (pkt->pkt.gpg_control->data[0] == 3) {
+ assert ( c->pipemode.op == 'B' );
+ release_list (c);
+ /* and tell the outer loop to terminate */
+ c->pipemode.stop_now = 1;
+ }
+ else
+ log_fatal ("invalid pipemode control packet code\n");
+ return 0; /* no need to store the packet */
+ }
+
+ if( c->list ) /* add another packet */
+ add_kbnode( c->list, new_kbnode( pkt ));
+ else /* insert the first one */
+ c->list = new_kbnode( pkt );
+
+ return 1;
+}
+
+
+
+static int
+add_user_id( CTX c, PACKET *pkt )
+{
+ if( !c->list ) {
+ log_error("orphaned user ID\n" );
+ return 0;
+ }
+ add_kbnode( c->list, new_kbnode( pkt ) );
+ return 1;
+}
+
+static int
+add_subkey( CTX c, PACKET *pkt )
+{
+ if( !c->list ) {
+ log_error("subkey w/o mainkey\n" );
+ return 0;
+ }
+ add_kbnode( c->list, new_kbnode( pkt ) );
+ return 1;
+}
+
+static int
+add_ring_trust( CTX c, PACKET *pkt )
+{
+ if( !c->list ) {
+ log_error("ring trust w/o key\n" );
+ return 0;
+ }
+ add_kbnode( c->list, new_kbnode( pkt ) );
+ return 1;
+}
+
+
+static int
+add_signature( CTX c, PACKET *pkt )
+{
+ KBNODE node;
+
+ c->any_sig_seen = 1;
+ if( pkt->pkttype == PKT_SIGNATURE && !c->list ) {
+ /* This is the first signature for the following datafile.
+ * GPG does not write such packets; instead it always uses
+ * onepass-sig packets. The drawback of PGP's method
+ * of prepending the signature to the data is
+ * that it is not possible to make a signature from data read
+ * from stdin. (GPG is able to read PGP stuff anyway.) */
+ node = new_kbnode( pkt );
+ c->list = node;
+ return 1;
+ }
+ else if( !c->list )
+ return 0; /* oops (invalid packet sequence)*/
+ else if( !c->list->pkt )
+ BUG(); /* so nicht */
+
+ /* add a new signature node id at the end */
+ node = new_kbnode( pkt );
+ add_kbnode( c->list, node );
+ return 1;
+}
+
+static int
+symkey_decrypt_seskey( DEK *dek, byte *seskey, size_t slen )
+{
+ CIPHER_HANDLE hd;
+
+ if(slen < 17 || slen > 33)
+ {
+ log_error ( _("weird size for an encrypted session key (%d)\n"),
+ (int)slen);
+ return G10ERR_BAD_KEY;
+ }
+
+ hd = cipher_open( dek->algo, CIPHER_MODE_CFB, 1 );
+ cipher_setkey( hd, dek->key, dek->keylen );
+ cipher_setiv( hd, NULL, 0 );
+ cipher_decrypt( hd, seskey, seskey, slen );
+ cipher_close( hd );
+
+ /* now we replace the dek components with the real session key to
+ decrypt the contents of the sequencing packet. */
+
+ dek->keylen=slen-1;
+ dek->algo=seskey[0];
+
+ if(dek->keylen > DIM(dek->key))
+ BUG ();
+
+ /* This is not completely accurate, since a bad passphrase may have
+ resulted in a garbage algorithm byte, but it's close enough since
+ a bogus byte here will fail later. */
+ if(dek->algo==CIPHER_ALGO_IDEA)
+ idea_cipher_warn(0);
+
+ memcpy(dek->key, seskey + 1, dek->keylen);
+
+ /*log_hexdump( "thekey", dek->key, dek->keylen );*/
+
+ return 0;
+}
+
+static void
+proc_symkey_enc( CTX c, PACKET *pkt )
+{
+ PKT_symkey_enc *enc;
+
+ enc = pkt->pkt.symkey_enc;
+ if (!enc)
+ log_error ("invalid symkey encrypted packet\n");
+ else if(!c->dek)
+ {
+ int algo = enc->cipher_algo;
+ const char *s = cipher_algo_to_string (algo);
+
+ if(s)
+ {
+ if(!opt.quiet)
+ {
+ if(enc->seskeylen)
+ log_info(_("%s encrypted session key\n"), s );
+ else
+ log_info(_("%s encrypted data\n"), s );
+ }
+ }
+ else
+ log_error(_("encrypted with unknown algorithm %d\n"), algo );
+
+ if(check_digest_algo(enc->s2k.hash_algo))
+ {
+ log_error(_("passphrase generated with unknown digest"
+ " algorithm %d\n"),enc->s2k.hash_algo);
+ s=NULL;
+ }
+
+ c->last_was_session_key = 2;
+ if(!s || opt.list_only)
+ goto leave;
+
+ if(opt.override_session_key)
+ {
+ c->dek = xmalloc_clear( sizeof *c->dek );
+ if(get_override_session_key(c->dek, opt.override_session_key))
+ {
+ xfree(c->dek);
+ c->dek = NULL;
+ }
+ }
+ else
+ {
+ int canceled;
+
+ c->dek = passphrase_to_dek (NULL, 0, algo, &enc->s2k, 0,
+ NULL, &canceled);
+ if (canceled)
+ {
+ /* For unknown reasons passphrase_to_dek does only
+ return NULL if a new passphrase has been requested
+ and has not been repeated correctly. Thus even
+ with a cancel requested (by means of the gpg-agent)
+ it won't return NULL but an empty passphrase. We
+ take the most conservative approach for now and
+ work around it right here. */
+ xfree (c->dek);
+ c->dek = NULL;
+ }
+
+ if(c->dek)
+ {
+ c->dek->symmetric=1;
+
+ /* FIXME: This doesn't work perfectly if a symmetric
+ key comes before a public key in the message - if
+ the user doesn't know the passphrase, then there is
+ a chance that the "decrypted" algorithm will happen
+ to be a valid one, which will make the returned dek
+ appear valid, so we won't try any public keys that
+ come later. */
+ if(enc->seskeylen)
+ {
+ if(symkey_decrypt_seskey(c->dek, enc->seskey,
+ enc->seskeylen))
+ {
+ xfree(c->dek);
+ c->dek=NULL;
+ }
+ }
+ else
+ c->dek->algo_info_printed = 1;
+ }
+ }
+ }
+
+ leave:
+ c->symkeys++;
+ free_packet(pkt);
+}
+
+static void
+proc_pubkey_enc( CTX c, PACKET *pkt )
+{
+ PKT_pubkey_enc *enc;
+ int result = 0;
+
+ /* check whether the secret key is available and store in this case */
+ c->last_was_session_key = 1;
+ enc = pkt->pkt.pubkey_enc;
+ /*printf("enc: encrypted by a pubkey with keyid %08lX\n", enc->keyid[1] );*/
+ /* Hmmm: why do I have this algo check here - anyway there is
+ * function to check it. */
+ if( opt.verbose )
+ log_info(_("public key is %s\n"), keystr(enc->keyid) );
+
+ if( is_status_enabled() ) {
+ char buf[50];
+ sprintf(buf, "%08lX%08lX %d 0",
+ (ulong)enc->keyid[0], (ulong)enc->keyid[1], enc->pubkey_algo );
+ write_status_text( STATUS_ENC_TO, buf );
+ }
+
+ if( !opt.list_only && opt.override_session_key ) {
+ /* It does not make much sense to store the session key in
+ * secure memory because it has already been passed on the
+ * command line and the GCHQ knows about it. */
+ c->dek = xmalloc_clear( sizeof *c->dek );
+ result = get_override_session_key ( c->dek, opt.override_session_key );
+ if ( result ) {
+ xfree(c->dek); c->dek = NULL;
+ }
+ }
+ else if( is_ELGAMAL(enc->pubkey_algo)
+ || enc->pubkey_algo == PUBKEY_ALGO_DSA
+ || is_RSA(enc->pubkey_algo) ) {
+ /* FIXME: strore this all in a list and process it later */
+
+ if ( !c->dek && ((!enc->keyid[0] && !enc->keyid[1])
+ || opt.try_all_secrets
+ || !seckey_available( enc->keyid )) ) {
+ if( opt.list_only )
+ result = -1;
+ else {
+ c->dek = xmalloc_secure_clear( sizeof *c->dek );
+ if( (result = get_session_key( enc, c->dek )) ) {
+ /* error: delete the DEK */
+ xfree(c->dek); c->dek = NULL;
+ }
+ }
+ }
+ else
+ result = G10ERR_NO_SECKEY;
+ }
+ else
+ result = G10ERR_PUBKEY_ALGO;
+
+ if( result == -1 )
+ ;
+ else
+ {
+ /* store it for later display */
+ struct kidlist_item *x = xmalloc( sizeof *x );
+ x->kid[0] = enc->keyid[0];
+ x->kid[1] = enc->keyid[1];
+ x->pubkey_algo = enc->pubkey_algo;
+ x->reason = result;
+ x->next = c->pkenc_list;
+ c->pkenc_list = x;
+
+ if( !result && opt.verbose > 1 )
+ log_info( _("public key encrypted data: good DEK\n") );
+ }
+
+ free_packet(pkt);
+}
+
+
+
+/****************
+ * Print the list of public key encrypted packets which we could
+ * not decrypt.
+ */
+static void
+print_pkenc_list( struct kidlist_item *list, int failed )
+{
+ for( ; list; list = list->next ) {
+ PKT_public_key *pk;
+ const char *algstr;
+
+ if ( failed && !list->reason )
+ continue;
+ if ( !failed && list->reason )
+ continue;
+
+ algstr = pubkey_algo_to_string( list->pubkey_algo );
+ pk = xmalloc_clear( sizeof *pk );
+
+ if( !algstr )
+ algstr = "[?]";
+ pk->pubkey_algo = list->pubkey_algo;
+ if( !get_pubkey( pk, list->kid ) )
+ {
+ char *p;
+ log_info( _("encrypted with %u-bit %s key, ID %s, created %s\n"),
+ nbits_from_pk( pk ), algstr, keystr_from_pk(pk),
+ strtimestamp(pk->timestamp) );
+ p=get_user_id_native(list->kid);
+ fprintf(log_stream(),_(" \"%s\"\n"),p);
+ xfree(p);
+ }
+ else
+ log_info(_("encrypted with %s key, ID %s\n"),
+ algstr,keystr(list->kid));
+
+ free_public_key( pk );
+
+ if( list->reason == G10ERR_NO_SECKEY ) {
+ if( is_status_enabled() ) {
+ char buf[20];
+ sprintf(buf,"%08lX%08lX", (ulong)list->kid[0],
+ (ulong)list->kid[1] );
+ write_status_text( STATUS_NO_SECKEY, buf );
+ }
+ }
+ else if (list->reason)
+ log_info(_("public key decryption failed: %s\n"),
+ g10_errstr(list->reason));
+ }
+}
+
+
+static void
+proc_encrypted( CTX c, PACKET *pkt )
+{
+ int result = 0;
+
+ if (!opt.quiet)
+ {
+ if(c->symkeys>1)
+ log_info(_("encrypted with %lu passphrases\n"),c->symkeys);
+ else if(c->symkeys==1)
+ log_info(_("encrypted with 1 passphrase\n"));
+ print_pkenc_list ( c->pkenc_list, 1 );
+ print_pkenc_list ( c->pkenc_list, 0 );
+ }
+
+ /* FIXME: Figure out the session key by looking at all pkenc packets. */
+
+
+ write_status( STATUS_BEGIN_DECRYPTION );
+
+ /*log_debug("dat: %sencrypted data\n", c->dek?"":"conventional ");*/
+ if( opt.list_only )
+ result = -1;
+ else if( !c->dek && !c->last_was_session_key ) {
+ int algo;
+ STRING2KEY s2kbuf, *s2k = NULL;
+
+ if(opt.override_session_key)
+ {
+ c->dek = xmalloc_clear( sizeof *c->dek );
+ result=get_override_session_key(c->dek, opt.override_session_key);
+ if(result)
+ {
+ xfree(c->dek);
+ c->dek = NULL;
+ }
+ }
+ else
+ {
+ /* assume this is old style conventional encrypted data */
+ if ( (algo = opt.def_cipher_algo))
+ log_info (_("assuming %s encrypted data\n"),
+ cipher_algo_to_string(algo));
+ else if ( check_cipher_algo(CIPHER_ALGO_IDEA) )
+ {
+ algo = opt.def_cipher_algo;
+ if (!algo)
+ algo = opt.s2k_cipher_algo;
+ idea_cipher_warn(1);
+ log_info (_("IDEA cipher unavailable, "
+ "optimistically attempting to use %s instead\n"),
+ cipher_algo_to_string(algo));
+ }
+ else
+ {
+ algo = CIPHER_ALGO_IDEA;
+ if (!opt.s2k_digest_algo)
+ {
+ /* If no digest is given we assume MD5 */
+ s2kbuf.mode = 0;
+ s2kbuf.hash_algo = DIGEST_ALGO_MD5;
+ s2k = &s2kbuf;
+ }
+ log_info (_("assuming %s encrypted data\n"), "IDEA");
+ }
+
+ c->dek = passphrase_to_dek ( NULL, 0, algo, s2k, 0, NULL, NULL );
+ if (c->dek)
+ c->dek->algo_info_printed = 1;
+ }
+ }
+ else if( !c->dek )
+ result = G10ERR_NO_SECKEY;
+ if( !result )
+ result = decrypt_data( c, pkt->pkt.encrypted, c->dek );
+
+ if( result == -1 )
+ ;
+ else if( !result || (result==G10ERR_BAD_SIGN && opt.ignore_mdc_error)) {
+ write_status( STATUS_DECRYPTION_OKAY );
+ if( opt.verbose > 1 )
+ log_info(_("decryption okay\n"));
+ if( pkt->pkt.encrypted->mdc_method && !result )
+ write_status( STATUS_GOODMDC );
+ else if(!opt.no_mdc_warn)
+ log_info (_("WARNING: message was not integrity protected\n"));
+ if(opt.show_session_key)
+ {
+ int i;
+ char *buf = xmalloc ( c->dek->keylen*2 + 20 );
+ sprintf ( buf, "%d:", c->dek->algo );
+ for(i=0; i < c->dek->keylen; i++ )
+ sprintf(buf+strlen(buf), "%02X", c->dek->key[i] );
+ log_info( "session key: `%s'\n", buf );
+ write_status_text ( STATUS_SESSION_KEY, buf );
+ }
+ }
+ else if( result == G10ERR_BAD_SIGN ) {
+ log_error(_("WARNING: encrypted message has been manipulated!\n"));
+ write_status( STATUS_BADMDC );
+ write_status( STATUS_DECRYPTION_FAILED );
+ }
+ else {
+ write_status( STATUS_DECRYPTION_FAILED );
+ log_error(_("decryption failed: %s\n"), g10_errstr(result));
+ /* Hmmm: does this work when we have encrypted using multiple
+ * ways to specify the session key (symmmetric and PK)*/
+ }
+ xfree(c->dek); c->dek = NULL;
+ free_packet(pkt);
+ c->last_was_session_key = 0;
+ write_status( STATUS_END_DECRYPTION );
+}
+
+
+static void
+proc_plaintext( CTX c, PACKET *pkt )
+{
+ PKT_plaintext *pt = pkt->pkt.plaintext;
+ int any, clearsig, only_md5, rc;
+ KBNODE n;
+
+ if( pt->namelen == 8 && !memcmp( pt->name, "_CONSOLE", 8 ) )
+ log_info(_("NOTE: sender requested \"for-your-eyes-only\"\n"));
+ else if( opt.verbose )
+ log_info(_("original file name='%.*s'\n"), pt->namelen, pt->name);
+ free_md_filter_context( &c->mfx );
+ c->mfx.md = md_open( 0, 0);
+ /* fixme: we may need to push the textfilter if we have sigclass 1
+ * and no armoring - Not yet tested
+ * Hmmm, why don't we need it at all if we have sigclass 1
+ * Should we assume that plaintext in mode 't' has always sigclass 1??
+ * See: Russ Allbery's mail 1999-02-09
+ */
+ any = clearsig = only_md5 = 0;
+ for(n=c->list; n; n = n->next )
+ {
+ if( n->pkt->pkttype == PKT_ONEPASS_SIG )
+ {
+ /* For the onepass signature case */
+ if( n->pkt->pkt.onepass_sig->digest_algo )
+ {
+ md_enable( c->mfx.md, n->pkt->pkt.onepass_sig->digest_algo );
+ if( !any && n->pkt->pkt.onepass_sig->digest_algo
+ == DIGEST_ALGO_MD5 )
+ only_md5 = 1;
+ else
+ only_md5 = 0;
+ any = 1;
+ }
+ if( n->pkt->pkt.onepass_sig->sig_class != 0x01 )
+ only_md5 = 0;
+ }
+ else if( n->pkt->pkttype == PKT_GPG_CONTROL
+ && n->pkt->pkt.gpg_control->control
+ == CTRLPKT_CLEARSIGN_START )
+ {
+ /* For the clearsigned message case */
+ size_t datalen = n->pkt->pkt.gpg_control->datalen;
+ const byte *data = n->pkt->pkt.gpg_control->data;
+
+ /* check that we have at least the sigclass and one hash */
+ if ( datalen < 2 )
+ log_fatal("invalid control packet CTRLPKT_CLEARSIGN_START\n");
+ /* Note that we don't set the clearsig flag for not-dash-escaped
+ * documents */
+ clearsig = (*data == 0x01);
+ for( data++, datalen--; datalen; datalen--, data++ )
+ md_enable( c->mfx.md, *data );
+ any = 1;
+ break; /* Stop here as one-pass signature packets are not
+ expected. */
+ }
+ else if(n->pkt->pkttype==PKT_SIGNATURE)
+ {
+ /* For the SIG+LITERAL case that PGP used to use. */
+ md_enable( c->mfx.md, n->pkt->pkt.signature->digest_algo );
+ any=1;
+ }
+ }
+
+ if( !any && !opt.skip_verify )
+ {
+ /* This is for the old GPG LITERAL+SIG case. It's not legal
+ according to 2440, so hopefully it won't come up that
+ often. There is no good way to specify what algorithms to
+ use in that case, so these three are the historical
+ answer. */
+ md_enable( c->mfx.md, DIGEST_ALGO_RMD160 );
+ md_enable( c->mfx.md, DIGEST_ALGO_SHA1 );
+ md_enable( c->mfx.md, DIGEST_ALGO_MD5 );
+ }
+ if( opt.pgp2_workarounds && only_md5 && !opt.skip_verify ) {
+ /* This is a kludge to work around a bug in pgp2. It does only
+ * catch those mails which are armored. To catch the non-armored
+ * pgp mails we could see whether there is the signature packet
+ * in front of the plaintext. If someone needs this, send me a patch.
+ */
+ c->mfx.md2 = md_open( DIGEST_ALGO_MD5, 0);
+ }
+ if ( DBG_HASHING ) {
+ md_start_debug( c->mfx.md, "verify" );
+ if ( c->mfx.md2 )
+ md_start_debug( c->mfx.md2, "verify2" );
+ }
+ if ( c->pipemode.op == 'B' )
+ rc = handle_plaintext( pt, &c->mfx, 1, 0 );
+ else {
+ rc = handle_plaintext( pt, &c->mfx, c->sigs_only, clearsig );
+ if( rc == G10ERR_CREATE_FILE && !c->sigs_only) {
+ /* can't write output but we hash it anyway to
+ * check the signature */
+ rc = handle_plaintext( pt, &c->mfx, 1, clearsig );
+ }
+ }
+ if( rc )
+ log_error( "handle plaintext failed: %s\n", g10_errstr(rc));
+ free_packet(pkt);
+ c->last_was_session_key = 0;
+
+ /* We add a marker control packet instead of the plaintext packet.
+ * This is so that we can later detect invalid packet sequences.
+ */
+ n = new_kbnode (create_gpg_control (CTRLPKT_PLAINTEXT_MARK, NULL, 0));
+ if (c->list)
+ add_kbnode (c->list, n);
+ else
+ c->list = n;
+}
+
+
+static int
+proc_compressed_cb( IOBUF a, void *info )
+{
+ return proc_signature_packets( info, a, ((CTX)info)->signed_data,
+ ((CTX)info)->sigfilename );
+}
+
+static int
+proc_encrypt_cb( IOBUF a, void *info )
+{
+ return proc_encryption_packets( info, a );
+}
+
+static void
+proc_compressed( CTX c, PACKET *pkt )
+{
+ PKT_compressed *zd = pkt->pkt.compressed;
+ int rc;
+
+ /*printf("zip: compressed data packet\n");*/
+ if( !zd->algorithm )
+ rc=G10ERR_COMPR_ALGO;
+ else if( c->sigs_only )
+ rc = handle_compressed( c, zd, proc_compressed_cb, c );
+ else if( c->encrypt_only )
+ rc = handle_compressed( c, zd, proc_encrypt_cb, c );
+ else
+ rc = handle_compressed( c, zd, NULL, NULL );
+ if( rc )
+ log_error("uncompressing failed: %s\n", g10_errstr(rc));
+ free_packet(pkt);
+ c->last_was_session_key = 0;
+}
+
+/****************
+ * check the signature
+ * Returns: 0 = valid signature or an error code
+ */
+static int
+do_check_sig( CTX c, KBNODE node, int *is_selfsig,
+ int *is_expkey, int *is_revkey )
+{
+ PKT_signature *sig;
+ MD_HANDLE md = NULL, md2 = NULL;
+ int algo, rc;
+
+ assert( node->pkt->pkttype == PKT_SIGNATURE );
+ if( is_selfsig )
+ *is_selfsig = 0;
+ sig = node->pkt->pkt.signature;
+
+ algo = sig->digest_algo;
+ if( (rc=check_digest_algo(algo)) )
+ return rc;
+
+ if( sig->sig_class == 0x00 ) {
+ if( c->mfx.md )
+ md = md_copy( c->mfx.md );
+ else /* detached signature */
+ md = md_open( 0, 0 ); /* signature_check() will enable the md*/
+ }
+ else if( sig->sig_class == 0x01 ) {
+ /* how do we know that we have to hash the (already hashed) text
+ * in canonical mode ??? (calculating both modes???) */
+ if( c->mfx.md ) {
+ md = md_copy( c->mfx.md );
+ if( c->mfx.md2 )
+ md2 = md_copy( c->mfx.md2 );
+ }
+ else { /* detached signature */
+ log_debug("Do we really need this here?");
+ md = md_open( 0, 0 ); /* signature_check() will enable the md*/
+ md2 = md_open( 0, 0 );
+ }
+ }
+ else if( (sig->sig_class&~3) == 0x10
+ || sig->sig_class == 0x18
+ || sig->sig_class == 0x1f
+ || sig->sig_class == 0x20
+ || sig->sig_class == 0x28
+ || sig->sig_class == 0x30 ) {
+ if( c->list->pkt->pkttype == PKT_PUBLIC_KEY
+ || c->list->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+ return check_key_signature( c->list, node, is_selfsig );
+ }
+ else if( sig->sig_class == 0x20 ) {
+ log_error (_("standalone revocation - "
+ "use \"gpg --import\" to apply\n"));
+ return G10ERR_NOT_PROCESSED;
+ }
+ else {
+ log_error("invalid root packet for sigclass %02x\n",
+ sig->sig_class);
+ return G10ERR_SIG_CLASS;
+ }
+ }
+ else
+ return G10ERR_SIG_CLASS;
+ rc = signature_check2( sig, md, NULL, is_expkey, is_revkey, NULL );
+ if( rc == G10ERR_BAD_SIGN && md2 )
+ rc = signature_check2( sig, md2, NULL, is_expkey, is_revkey, NULL );
+ md_close(md);
+ md_close(md2);
+
+ return rc;
+}
+
+
+static void
+print_userid( PACKET *pkt )
+{
+ if( !pkt )
+ BUG();
+ if( pkt->pkttype != PKT_USER_ID ) {
+ printf("ERROR: unexpected packet type %d", pkt->pkttype );
+ return;
+ }
+ if( opt.with_colons )
+ {
+ if(pkt->pkt.user_id->attrib_data)
+ printf("%u %lu",
+ pkt->pkt.user_id->numattribs,
+ pkt->pkt.user_id->attrib_len);
+ else
+ print_string( stdout, pkt->pkt.user_id->name,
+ pkt->pkt.user_id->len, ':');
+ }
+ else
+ print_utf8_string( stdout, pkt->pkt.user_id->name,
+ pkt->pkt.user_id->len );
+}
+
+
+/****************
+ * List the certificate in a user friendly way
+ */
+
+static void
+list_node( CTX c, KBNODE node )
+{
+ int any=0;
+ int mainkey;
+
+ if( !node )
+ ;
+ else if( (mainkey = (node->pkt->pkttype == PKT_PUBLIC_KEY) )
+ || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+ PKT_public_key *pk = node->pkt->pkt.public_key;
+
+ if( opt.with_colons )
+ {
+ u32 keyid[2];
+ keyid_from_pk( pk, keyid );
+ if( mainkey )
+ c->trustletter = opt.fast_list_mode?
+ 0 : get_validity_info( pk, NULL );
+ printf("%s:", mainkey? "pub":"sub" );
+ if( c->trustletter )
+ putchar( c->trustletter );
+ printf(":%u:%d:%08lX%08lX:%s:%s::",
+ nbits_from_pk( pk ),
+ pk->pubkey_algo,
+ (ulong)keyid[0],(ulong)keyid[1],
+ colon_datestr_from_pk( pk ),
+ colon_strtime (pk->expiredate) );
+ if( mainkey && !opt.fast_list_mode )
+ putchar( get_ownertrust_info (pk) );
+ putchar(':');
+ if( node->next && node->next->pkt->pkttype == PKT_RING_TRUST) {
+ putchar('\n'); any=1;
+ if( opt.fingerprint )
+ print_fingerprint( pk, NULL, 0 );
+ printf("rtv:1:%u:\n",
+ node->next->pkt->pkt.ring_trust->trustval );
+ }
+ }
+ else
+ printf("%s %4u%c/%s %s%s",
+ mainkey? "pub":"sub", nbits_from_pk( pk ),
+ pubkey_letter( pk->pubkey_algo ), keystr_from_pk( pk ),
+ datestr_from_pk( pk ), mainkey?" ":"");
+
+ if( mainkey ) {
+ /* and now list all userids with their signatures */
+ for( node = node->next; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_SIGNATURE ) {
+ if( !any ) {
+ if( node->pkt->pkt.signature->sig_class == 0x20 )
+ puts("[revoked]");
+ else
+ putchar('\n');
+ any = 1;
+ }
+ list_node(c, node );
+ }
+ else if( node->pkt->pkttype == PKT_USER_ID ) {
+ if( any ) {
+ if( opt.with_colons )
+ printf("%s:::::::::",
+ node->pkt->pkt.user_id->attrib_data?"uat":"uid");
+ else
+ printf( "uid%*s", 28, "" );
+ }
+ print_userid( node->pkt );
+ if( opt.with_colons )
+ putchar(':');
+ putchar('\n');
+ if( opt.fingerprint && !any )
+ print_fingerprint( pk, NULL, 0 );
+ if( opt.with_colons
+ && node->next
+ && node->next->pkt->pkttype == PKT_RING_TRUST ) {
+ printf("rtv:2:%u:\n",
+ node->next->pkt->pkt.ring_trust?
+ node->next->pkt->pkt.ring_trust->trustval : 0);
+ }
+ any=1;
+ }
+ else if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+ if( !any ) {
+ putchar('\n');
+ any = 1;
+ }
+ list_node(c, node );
+ }
+ }
+ }
+ else
+ {
+ /* of subkey */
+ if( pk->is_revoked )
+ {
+ printf(" [");
+ printf(_("revoked: %s"),revokestr_from_pk(pk));
+ printf("]");
+ }
+ else if( pk->expiredate )
+ {
+ printf(" [");
+ printf(_("expires: %s"),expirestr_from_pk(pk));
+ printf("]");
+ }
+ }
+
+ if( !any )
+ putchar('\n');
+ if( !mainkey && opt.fingerprint > 1 )
+ print_fingerprint( pk, NULL, 0 );
+ }
+ else if( (mainkey = (node->pkt->pkttype == PKT_SECRET_KEY) )
+ || node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+ PKT_secret_key *sk = node->pkt->pkt.secret_key;
+
+ if( opt.with_colons )
+ {
+ u32 keyid[2];
+ keyid_from_sk( sk, keyid );
+ printf("%s::%u:%d:%08lX%08lX:%s:%s:::",
+ mainkey? "sec":"ssb",
+ nbits_from_sk( sk ),
+ sk->pubkey_algo,
+ (ulong)keyid[0],(ulong)keyid[1],
+ colon_datestr_from_sk( sk ),
+ colon_strtime (sk->expiredate)
+ /* fixme: add LID */ );
+ }
+ else
+ printf("%s %4u%c/%s %s ", mainkey? "sec":"ssb",
+ nbits_from_sk( sk ), pubkey_letter( sk->pubkey_algo ),
+ keystr_from_sk( sk ), datestr_from_sk( sk ));
+ if( mainkey ) {
+ /* and now list all userids with their signatures */
+ for( node = node->next; node; node = node->next ) {
+ if( node->pkt->pkttype == PKT_SIGNATURE ) {
+ if( !any ) {
+ if( node->pkt->pkt.signature->sig_class == 0x20 )
+ puts("[revoked]");
+ else
+ putchar('\n');
+ any = 1;
+ }
+ list_node(c, node );
+ }
+ else if( node->pkt->pkttype == PKT_USER_ID ) {
+ if( any ) {
+ if( opt.with_colons )
+ printf("%s:::::::::",
+ node->pkt->pkt.user_id->attrib_data?"uat":"uid");
+ else
+ printf( "uid%*s", 28, "" );
+ }
+ print_userid( node->pkt );
+ if( opt.with_colons )
+ putchar(':');
+ putchar('\n');
+ if( opt.fingerprint && !any )
+ print_fingerprint( NULL, sk, 0 );
+ any=1;
+ }
+ else if( node->pkt->pkttype == PKT_SECRET_SUBKEY ) {
+ if( !any ) {
+ putchar('\n');
+ any = 1;
+ }
+ list_node(c, node );
+ }
+ }
+ }
+ if( !any )
+ putchar('\n');
+ if( !mainkey && opt.fingerprint > 1 )
+ print_fingerprint( NULL, sk, 0 );
+ }
+ else if( node->pkt->pkttype == PKT_SIGNATURE ) {
+ PKT_signature *sig = node->pkt->pkt.signature;
+ int is_selfsig = 0;
+ int rc2=0;
+ size_t n;
+ char *p;
+ int sigrc = ' ';
+
+ if( !opt.verbose )
+ return;
+
+ if( sig->sig_class == 0x20 || sig->sig_class == 0x30 )
+ fputs("rev", stdout);
+ else
+ fputs("sig", stdout);
+ if( opt.check_sigs ) {
+ fflush(stdout);
+ switch( (rc2=do_check_sig( c, node, &is_selfsig, NULL, NULL )) ) {
+ case 0: sigrc = '!'; break;
+ case G10ERR_BAD_SIGN: sigrc = '-'; break;
+ case G10ERR_NO_PUBKEY:
+ case G10ERR_UNU_PUBKEY: sigrc = '?'; break;
+ default: sigrc = '%'; break;
+ }
+ }
+ else { /* check whether this is a self signature */
+ u32 keyid[2];
+
+ if( c->list->pkt->pkttype == PKT_PUBLIC_KEY
+ || c->list->pkt->pkttype == PKT_SECRET_KEY ) {
+ if( c->list->pkt->pkttype == PKT_PUBLIC_KEY )
+ keyid_from_pk( c->list->pkt->pkt.public_key, keyid );
+ else
+ keyid_from_sk( c->list->pkt->pkt.secret_key, keyid );
+
+ if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] )
+ is_selfsig = 1;
+ }
+ }
+ if( opt.with_colons ) {
+ putchar(':');
+ if( sigrc != ' ' )
+ putchar(sigrc);
+ printf("::%d:%08lX%08lX:%s:%s:", sig->pubkey_algo,
+ (ulong)sig->keyid[0], (ulong)sig->keyid[1],
+ colon_datestr_from_sig(sig),
+ colon_expirestr_from_sig(sig));
+
+ if(sig->trust_depth || sig->trust_value)
+ printf("%d %d",sig->trust_depth,sig->trust_value);
+ printf(":");
+
+ if(sig->trust_regexp)
+ print_string(stdout,sig->trust_regexp,
+ strlen(sig->trust_regexp),':');
+ printf(":");
+ }
+ else
+ printf("%c %s %s ",
+ sigrc, keystr(sig->keyid), datestr_from_sig(sig));
+ if( sigrc == '%' )
+ printf("[%s] ", g10_errstr(rc2) );
+ else if( sigrc == '?' )
+ ;
+ else if( is_selfsig ) {
+ if( opt.with_colons )
+ putchar(':');
+ fputs( sig->sig_class == 0x18? "[keybind]":"[selfsig]", stdout);
+ if( opt.with_colons )
+ putchar(':');
+ }
+ else if( !opt.fast_list_mode ) {
+ p = get_user_id( sig->keyid, &n );
+ print_string( stdout, p, n, opt.with_colons );
+ xfree(p);
+ }
+ if( opt.with_colons )
+ printf(":%02x%c:", sig->sig_class, sig->flags.exportable?'x':'l');
+ putchar('\n');
+ }
+ else
+ log_error("invalid node with packet of type %d\n", node->pkt->pkttype);
+}
+
+
+
+int
+proc_packets( void *anchor, IOBUF a )
+{
+ int rc;
+ CTX c = xmalloc_clear( sizeof *c );
+
+ c->anchor = anchor;
+ rc = do_proc_packets( c, a );
+ xfree( c );
+ return rc;
+}
+
+
+
+int
+proc_signature_packets( void *anchor, IOBUF a,
+ STRLIST signedfiles, const char *sigfilename )
+{
+ CTX c = xmalloc_clear( sizeof *c );
+ int rc;
+
+ c->anchor = anchor;
+ c->sigs_only = 1;
+ c->signed_data = signedfiles;
+ c->sigfilename = sigfilename;
+ rc = do_proc_packets( c, a );
+
+ /* If we have not encountered any signature we print an error
+ messages, send a NODATA status back and return an error code.
+ Using log_error is required because verify_files does not check
+ error codes for each file but we want to terminate the process
+ with an error. */
+ if (!rc && !c->any_sig_seen)
+ {
+ write_status_text (STATUS_NODATA, "4");
+ log_error (_("no signature found\n"));
+ rc = G10ERR_NO_DATA;
+ }
+
+ /* Propagate the signature seen flag upward. Do this only on
+ success so that we won't issue the nodata status several
+ times. */
+ if (!rc && c->anchor && c->any_sig_seen)
+ c->anchor->any_sig_seen = 1;
+
+ xfree( c );
+ return rc;
+}
+
+int
+proc_encryption_packets( void *anchor, IOBUF a )
+{
+ CTX c = xmalloc_clear( sizeof *c );
+ int rc;
+
+ c->anchor = anchor;
+ c->encrypt_only = 1;
+ rc = do_proc_packets( c, a );
+ xfree( c );
+ return rc;
+}
+
+
+int
+do_proc_packets( CTX c, IOBUF a )
+{
+ PACKET *pkt = xmalloc( sizeof *pkt );
+ int rc=0;
+ int any_data=0;
+ int newpkt;
+
+ c->iobuf = a;
+ init_packet(pkt);
+ while( (rc=parse_packet(a, pkt)) != -1 ) {
+ any_data = 1;
+ if( rc ) {
+ free_packet(pkt);
+ /* stop processing when an invalid packet has been encountered
+ * but don't do so when we are doing a --list-packets. */
+ if( rc == G10ERR_INVALID_PACKET && opt.list_packets != 2 )
+ break;
+ continue;
+ }
+ newpkt = -1;
+ if( opt.list_packets ) {
+ switch( pkt->pkttype ) {
+ case PKT_PUBKEY_ENC: proc_pubkey_enc( c, pkt ); break;
+ case PKT_SYMKEY_ENC: proc_symkey_enc( c, pkt ); break;
+ case PKT_ENCRYPTED:
+ case PKT_ENCRYPTED_MDC: proc_encrypted( c, pkt ); break;
+ case PKT_COMPRESSED: proc_compressed( c, pkt ); break;
+ default: newpkt = 0; break;
+ }
+ }
+ else if( c->sigs_only ) {
+ switch( pkt->pkttype ) {
+ case PKT_PUBLIC_KEY:
+ case PKT_SECRET_KEY:
+ case PKT_USER_ID:
+ case PKT_SYMKEY_ENC:
+ case PKT_PUBKEY_ENC:
+ case PKT_ENCRYPTED:
+ case PKT_ENCRYPTED_MDC:
+ write_status_text( STATUS_UNEXPECTED, "0" );
+ rc = G10ERR_UNEXPECTED;
+ goto leave;
+ case PKT_SIGNATURE: newpkt = add_signature( c, pkt ); break;
+ case PKT_PLAINTEXT: proc_plaintext( c, pkt ); break;
+ case PKT_COMPRESSED: proc_compressed( c, pkt ); break;
+ case PKT_ONEPASS_SIG: newpkt = add_onepass_sig( c, pkt ); break;
+ case PKT_GPG_CONTROL: newpkt = add_gpg_control(c, pkt); break;
+ default: newpkt = 0; break;
+ }
+ }
+ else if( c->encrypt_only ) {
+ switch( pkt->pkttype ) {
+ case PKT_PUBLIC_KEY:
+ case PKT_SECRET_KEY:
+ case PKT_USER_ID:
+ write_status_text( STATUS_UNEXPECTED, "0" );
+ rc = G10ERR_UNEXPECTED;
+ goto leave;
+ case PKT_SIGNATURE: newpkt = add_signature( c, pkt ); break;
+ case PKT_SYMKEY_ENC: proc_symkey_enc( c, pkt ); break;
+ case PKT_PUBKEY_ENC: proc_pubkey_enc( c, pkt ); break;
+ case PKT_ENCRYPTED:
+ case PKT_ENCRYPTED_MDC: proc_encrypted( c, pkt ); break;
+ case PKT_PLAINTEXT: proc_plaintext( c, pkt ); break;
+ case PKT_COMPRESSED: proc_compressed( c, pkt ); break;
+ case PKT_ONEPASS_SIG: newpkt = add_onepass_sig( c, pkt ); break;
+ case PKT_GPG_CONTROL: newpkt = add_gpg_control(c, pkt); break;
+ default: newpkt = 0; break;
+ }
+ }
+ else {
+ switch( pkt->pkttype ) {
+ case PKT_PUBLIC_KEY:
+ case PKT_SECRET_KEY:
+ release_list( c );
+ c->list = new_kbnode( pkt );
+ newpkt = 1;
+ break;
+ case PKT_PUBLIC_SUBKEY:
+ case PKT_SECRET_SUBKEY:
+ newpkt = add_subkey( c, pkt );
+ break;
+ case PKT_USER_ID: newpkt = add_user_id( c, pkt ); break;
+ case PKT_SIGNATURE: newpkt = add_signature( c, pkt ); break;
+ case PKT_PUBKEY_ENC: proc_pubkey_enc( c, pkt ); break;
+ case PKT_SYMKEY_ENC: proc_symkey_enc( c, pkt ); break;
+ case PKT_ENCRYPTED:
+ case PKT_ENCRYPTED_MDC: proc_encrypted( c, pkt ); break;
+ case PKT_PLAINTEXT: proc_plaintext( c, pkt ); break;
+ case PKT_COMPRESSED: proc_compressed( c, pkt ); break;
+ case PKT_ONEPASS_SIG: newpkt = add_onepass_sig( c, pkt ); break;
+ case PKT_GPG_CONTROL: newpkt = add_gpg_control(c, pkt); break;
+ case PKT_RING_TRUST: newpkt = add_ring_trust( c, pkt ); break;
+ default: newpkt = 0; break;
+ }
+ }
+ /* This is a very ugly construct and frankly, I don't remember why
+ * I used it. Adding the MDC check here is a hack.
+ * The right solution is to initiate another context for encrypted
+ * packet and not to reuse the current one ... It works right
+ * when there is a compression packet inbetween which adds just
+ * an extra layer.
+ * Hmmm: Rewrite this whole module here??
+ */
+ if( pkt->pkttype != PKT_SIGNATURE && pkt->pkttype != PKT_MDC )
+ c->have_data = pkt->pkttype == PKT_PLAINTEXT;
+
+ if( newpkt == -1 )
+ ;
+ else if( newpkt ) {
+ pkt = xmalloc( sizeof *pkt );
+ init_packet(pkt);
+ }
+ else
+ free_packet(pkt);
+ if ( c->pipemode.stop_now ) {
+ /* we won't get an EOF in pipemode, so we have to
+ * break the loop here */
+ rc = -1;
+ break;
+ }
+ }
+ if( rc == G10ERR_INVALID_PACKET )
+ write_status_text( STATUS_NODATA, "3" );
+ if( any_data )
+ rc = 0;
+ else if( rc == -1 )
+ write_status_text( STATUS_NODATA, "2" );
+
+
+ leave:
+ release_list( c );
+ xfree(c->dek);
+ free_packet( pkt );
+ xfree( pkt );
+ free_md_filter_context( &c->mfx );
+ return rc;
+}
+
+
+/* Helper for pka_uri_from_sig to parse the to-be-verified address out
+ of the notation data. */
+static pka_info_t *
+get_pka_address (PKT_signature *sig)
+{
+ pka_info_t *pka = NULL;
+ struct notation *nd,*notation;
+
+ notation=sig_to_notation(sig);
+
+ for(nd=notation;nd;nd=nd->next)
+ {
+ if(strcmp(nd->name,"pka-address@gnupg.org")!=0)
+ continue; /* Not the notation we want. */
+
+ /* For now we only use the first valid PKA notation. In future
+ we might want to keep additional PKA notations in a linked
+ list. */
+ if (is_valid_mailbox (nd->value))
+ {
+ pka = xmalloc (sizeof *pka + strlen(nd->value));
+ pka->valid = 0;
+ pka->checked = 0;
+ pka->uri = NULL;
+ strcpy (pka->email, nd->value);
+ break;
+ }
+ }
+
+ free_notation(notation);
+
+ return pka;
+}
+
+
+/* Return the URI from a DNS PKA record. If this record has already
+ be retrieved for the signature we merely return it; if not we go
+ out and try to get that DNS record. */
+static const char *
+pka_uri_from_sig (PKT_signature *sig)
+{
+ if (!sig->flags.pka_tried)
+ {
+ assert (!sig->pka_info);
+ sig->flags.pka_tried = 1;
+ sig->pka_info = get_pka_address (sig);
+ if (sig->pka_info)
+ {
+ char *uri;
+
+ uri = get_pka_info (sig->pka_info->email, sig->pka_info->fpr);
+ if (uri)
+ {
+ sig->pka_info->valid = 1;
+ if (!*uri)
+ xfree (uri);
+ else
+ sig->pka_info->uri = uri;
+ }
+ }
+ }
+ return sig->pka_info? sig->pka_info->uri : NULL;
+}
+
+
+static int
+check_sig_and_print( CTX c, KBNODE node )
+{
+ PKT_signature *sig = node->pkt->pkt.signature;
+ const char *astr;
+ int rc, is_expkey=0, is_revkey=0;
+
+ if (opt.skip_verify)
+ {
+ log_info(_("signature verification suppressed\n"));
+ return 0;
+ }
+
+ /* Check that the message composition is valid.
+
+ Per RFC-2440bis (-15) allowed:
+
+ S{1,n} -- detached signature.
+ S{1,n} P -- old style PGP2 signature
+ O{1,n} P S{1,n} -- standard OpenPGP signature.
+ C P S{1,n} -- cleartext signature.
+
+
+ O = One-Pass Signature packet.
+ S = Signature packet.
+ P = OpenPGP Message packet (Encrypted | Compressed | Literal)
+ (Note that the current rfc2440bis draft also allows
+ for a signed message but that does not work as it
+ introduces ambiguities.)
+ We keep track of these packages using the marker packet
+ CTRLPKT_PLAINTEXT_MARK.
+ C = Marker packet for cleartext signatures.
+
+ We reject all other messages.
+
+ Actually we are calling this too often, i.e. for verification of
+ each message but better have some duplicate work than to silently
+ introduce a bug here.
+ */
+ {
+ KBNODE n;
+ int n_onepass, n_sig;
+
+/* log_debug ("checking signature packet composition\n"); */
+/* dump_kbnode (c->list); */
+
+ n = c->list;
+ assert (n);
+ if ( n->pkt->pkttype == PKT_SIGNATURE )
+ {
+ /* This is either "S{1,n}" case (detached signature) or
+ "S{1,n} P" (old style PGP2 signature). */
+ for (n = n->next; n; n = n->next)
+ if (n->pkt->pkttype != PKT_SIGNATURE)
+ break;
+ if (!n)
+ ; /* Okay, this is a detached signature. */
+ else if (n->pkt->pkttype == PKT_GPG_CONTROL
+ && (n->pkt->pkt.gpg_control->control
+ == CTRLPKT_PLAINTEXT_MARK) )
+ {
+ if (n->next)
+ goto ambiguous; /* We only allow one P packet. */
+ }
+ else
+ goto ambiguous;
+ }
+ else if (n->pkt->pkttype == PKT_ONEPASS_SIG)
+ {
+ /* This is the "O{1,n} P S{1,n}" case (standard signature). */
+ for (n_onepass=1, n = n->next;
+ n && n->pkt->pkttype == PKT_ONEPASS_SIG; n = n->next)
+ n_onepass++;
+ if (!n || !(n->pkt->pkttype == PKT_GPG_CONTROL
+ && (n->pkt->pkt.gpg_control->control
+ == CTRLPKT_PLAINTEXT_MARK)))
+ goto ambiguous;
+ for (n_sig=0, n = n->next;
+ n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next)
+ n_sig++;
+ if (!n_sig)
+ goto ambiguous;
+ if (n && !opt.allow_multisig_verification)
+ goto ambiguous;
+ if (n_onepass != n_sig)
+ {
+ log_info ("number of one-pass packets does not match "
+ "number of signature packets\n");
+ goto ambiguous;
+ }
+ }
+ else if (n->pkt->pkttype == PKT_GPG_CONTROL
+ && n->pkt->pkt.gpg_control->control == CTRLPKT_CLEARSIGN_START )
+ {
+ /* This is the "C P S{1,n}" case (clear text signature). */
+ n = n->next;
+ if (!n || !(n->pkt->pkttype == PKT_GPG_CONTROL
+ && (n->pkt->pkt.gpg_control->control
+ == CTRLPKT_PLAINTEXT_MARK)))
+ goto ambiguous;
+ for (n_sig=0, n = n->next;
+ n && n->pkt->pkttype == PKT_SIGNATURE; n = n->next)
+ n_sig++;
+ if (n || !n_sig)
+ goto ambiguous;
+ }
+ else
+ {
+ ambiguous:
+ log_error(_("can't handle this ambiguous signature data\n"));
+ return 0;
+ }
+
+ }
+
+ /* (Indendation below not yet changed to GNU style.) */
+
+ astr = pubkey_algo_to_string( sig->pubkey_algo );
+ if(keystrlen()>8)
+ {
+ log_info(_("Signature made %s\n"),asctimestamp(sig->timestamp));
+ log_info(_(" using %s key %s\n"),
+ astr? astr: "?",keystr(sig->keyid));
+ }
+ else
+ log_info(_("Signature made %s using %s key ID %s\n"),
+ asctimestamp(sig->timestamp), astr? astr: "?",
+ keystr(sig->keyid));
+
+ rc = do_check_sig(c, node, NULL, &is_expkey, &is_revkey );
+
+ /* If the key isn't found, check for a preferred keyserver */
+
+ if(rc==G10ERR_NO_PUBKEY && sig->flags.pref_ks)
+ {
+ const byte *p;
+ int seq=0;
+ size_t n;
+
+ while((p=enum_sig_subpkt(sig->hashed,SIGSUBPKT_PREF_KS,&n,&seq,NULL)))
+ {
+ /* According to my favorite copy editor, in English
+ grammar, you say "at" if the key is located on a web
+ page, but "from" if it is located on a keyserver. I'm
+ not going to even try to make two strings here :) */
+ log_info(_("Key available at: ") );
+ print_utf8_string( log_stream(), p, n );
+ putc( '\n', log_stream() );
+
+ if(opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE
+ && opt.keyserver_options.options&KEYSERVER_HONOR_KEYSERVER_URL)
+ {
+ struct keyserver_spec *spec;
+
+ spec=parse_preferred_keyserver(sig);
+ if(spec)
+ {
+ int res;
+
+ glo_ctrl.in_auto_key_retrieve++;
+ res=keyserver_import_keyid(sig->keyid,spec);
+ glo_ctrl.in_auto_key_retrieve--;
+ if(!res)
+ rc=do_check_sig(c, node, NULL, &is_expkey, &is_revkey );
+ free_keyserver_spec(spec);
+
+ if(!rc)
+ break;
+ }
+ }
+ }
+ }
+
+ /* If the preferred keyserver thing above didn't work, our second
+ try is to use the URI from a DNS PKA record. */
+ if ( rc == G10ERR_NO_PUBKEY
+ && opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE
+ && opt.keyserver_options.options&KEYSERVER_HONOR_PKA_RECORD)
+ {
+ const char *uri = pka_uri_from_sig (sig);
+
+ if (uri)
+ {
+ /* FIXME: We might want to locate the key using the
+ fingerprint instead of the keyid. */
+ int res;
+ struct keyserver_spec *spec;
+
+ spec = parse_keyserver_uri (uri, 1, NULL, 0);
+ if (spec)
+ {
+ glo_ctrl.in_auto_key_retrieve++;
+ res = keyserver_import_keyid (sig->keyid, spec);
+ glo_ctrl.in_auto_key_retrieve--;
+ free_keyserver_spec (spec);
+ if (!res)
+ rc = do_check_sig(c, node, NULL, &is_expkey, &is_revkey );
+ }
+ }
+ }
+
+ /* If the preferred keyserver thing above didn't work and we got
+ no information from the DNS PKA, this is a third try. */
+
+ if( rc == G10ERR_NO_PUBKEY && opt.keyserver
+ && opt.keyserver_options.options&KEYSERVER_AUTO_KEY_RETRIEVE)
+ {
+ int res;
+
+ glo_ctrl.in_auto_key_retrieve++;
+ res=keyserver_import_keyid ( sig->keyid, opt.keyserver );
+ glo_ctrl.in_auto_key_retrieve--;
+ if(!res)
+ rc = do_check_sig(c, node, NULL, &is_expkey, &is_revkey );
+ }
+
+ if( !rc || rc == G10ERR_BAD_SIGN ) {
+ KBNODE un, keyblock;
+ int count=0, statno;
+ char keyid_str[50];
+ PKT_public_key *pk=NULL;
+
+ if(rc)
+ statno=STATUS_BADSIG;
+ else if(sig->flags.expired)
+ statno=STATUS_EXPSIG;
+ else if(is_expkey)
+ statno=STATUS_EXPKEYSIG;
+ else if(is_revkey)
+ statno=STATUS_REVKEYSIG;
+ else
+ statno=STATUS_GOODSIG;
+
+ keyblock = get_pubkeyblock( sig->keyid );
+
+ sprintf (keyid_str, "%08lX%08lX [uncertain] ",
+ (ulong)sig->keyid[0], (ulong)sig->keyid[1]);
+
+ /* find and print the primary user ID */
+ for( un=keyblock; un; un = un->next ) {
+ char *p;
+ int valid;
+ if(un->pkt->pkttype==PKT_PUBLIC_KEY)
+ {
+ pk=un->pkt->pkt.public_key;
+ continue;
+ }
+ if( un->pkt->pkttype != PKT_USER_ID )
+ continue;
+ if ( !un->pkt->pkt.user_id->created )
+ continue;
+ if ( un->pkt->pkt.user_id->is_revoked )
+ continue;
+ if ( un->pkt->pkt.user_id->is_expired )
+ continue;
+ if ( !un->pkt->pkt.user_id->is_primary )
+ continue;
+ /* We want the textual primary user ID here */
+ if ( un->pkt->pkt.user_id->attrib_data )
+ continue;
+
+ assert(pk);
+
+ /* Get it before we print anything to avoid interrupting
+ the output with the "please do a --check-trustdb"
+ line. */
+ valid=get_validity(pk,un->pkt->pkt.user_id);
+
+ keyid_str[17] = 0; /* cut off the "[uncertain]" part */
+ write_status_text_and_buffer (statno, keyid_str,
+ un->pkt->pkt.user_id->name,
+ un->pkt->pkt.user_id->len,
+ -1 );
+
+ p=utf8_to_native(un->pkt->pkt.user_id->name,
+ un->pkt->pkt.user_id->len,0);
+
+ if(rc)
+ log_info(_("BAD signature from \"%s\""),p);
+ else if(sig->flags.expired)
+ log_info(_("Expired signature from \"%s\""),p);
+ else
+ log_info(_("Good signature from \"%s\""),p);
+
+ xfree(p);
+
+ if(opt.verify_options&VERIFY_SHOW_UID_VALIDITY)
+ fprintf(log_stream()," [%s]\n",trust_value_to_string(valid));
+ else
+ fputs("\n", log_stream() );
+ count++;
+ }
+ if( !count ) { /* just in case that we have no valid textual
+ userid */
+ char *p;
+
+ /* Try for an invalid textual userid */
+ for( un=keyblock; un; un = un->next ) {
+ if( un->pkt->pkttype == PKT_USER_ID &&
+ !un->pkt->pkt.user_id->attrib_data )
+ break;
+ }
+
+ /* Try for any userid at all */
+ if(!un) {
+ for( un=keyblock; un; un = un->next ) {
+ if( un->pkt->pkttype == PKT_USER_ID )
+ break;
+ }
+ }
+
+ if (opt.trust_model==TM_ALWAYS || !un)
+ keyid_str[17] = 0; /* cut off the "[uncertain]" part */
+
+ write_status_text_and_buffer (statno, keyid_str,
+ un? un->pkt->pkt.user_id->name:"[?]",
+ un? un->pkt->pkt.user_id->len:3,
+ -1 );
+
+ if(un)
+ p=utf8_to_native(un->pkt->pkt.user_id->name,
+ un->pkt->pkt.user_id->len,0);
+ else
+ p=xstrdup("[?]");
+
+ if(rc)
+ log_info(_("BAD signature from \"%s\""),p);
+ else if(sig->flags.expired)
+ log_info(_("Expired signature from \"%s\""),p);
+ else
+ log_info(_("Good signature from \"%s\""),p);
+ if (opt.trust_model!=TM_ALWAYS && un)
+ {
+ putc(' ', log_stream() );
+ fputs(_("[uncertain]"), log_stream() );
+ }
+ fputs("\n", log_stream() );
+ }
+
+ /* If we have a good signature and already printed
+ * the primary user ID, print all the other user IDs */
+ if ( count && !rc ) {
+ char *p;
+ for( un=keyblock; un; un = un->next ) {
+ if( un->pkt->pkttype != PKT_USER_ID )
+ continue;
+ if((un->pkt->pkt.user_id->is_revoked
+ || un->pkt->pkt.user_id->is_expired)
+ && !(opt.verify_options&VERIFY_SHOW_UNUSABLE_UIDS))
+ continue;
+ /* Only skip textual primaries */
+ if ( un->pkt->pkt.user_id->is_primary &&
+ !un->pkt->pkt.user_id->attrib_data )
+ continue;
+
+ if(un->pkt->pkt.user_id->attrib_data)
+ {
+ dump_attribs(un->pkt->pkt.user_id,pk,NULL);
+
+ if(opt.verify_options&VERIFY_SHOW_PHOTOS)
+ show_photos(un->pkt->pkt.user_id->attribs,
+ un->pkt->pkt.user_id->numattribs,pk,NULL);
+ }
+
+ p=utf8_to_native(un->pkt->pkt.user_id->name,
+ un->pkt->pkt.user_id->len,0);
+ log_info(_(" aka \"%s\""),p);
+ xfree(p);
+
+ if(opt.verify_options&VERIFY_SHOW_UID_VALIDITY)
+ {
+ const char *valid;
+ if(un->pkt->pkt.user_id->is_revoked)
+ valid=_("revoked");
+ else if(un->pkt->pkt.user_id->is_expired)
+ valid=_("expired");
+ else
+ valid=trust_value_to_string(get_validity(pk,
+ un->pkt->
+ pkt.user_id));
+ fprintf(log_stream()," [%s]\n",valid);
+ }
+ else
+ fputs("\n", log_stream() );
+ }
+ }
+ release_kbnode( keyblock );
+
+ if( !rc )
+ {
+ if(opt.verify_options&VERIFY_SHOW_POLICY_URLS)
+ show_policy_url(sig,0,1);
+ else
+ show_policy_url(sig,0,2);
+
+ if(opt.verify_options&VERIFY_SHOW_KEYSERVER_URLS)
+ show_keyserver_url(sig,0,1);
+ else
+ show_keyserver_url(sig,0,2);
+
+ if(opt.verify_options&VERIFY_SHOW_NOTATIONS)
+ show_notation(sig,0,1,
+ ((opt.verify_options&VERIFY_SHOW_STD_NOTATIONS)?1:0)+
+ ((opt.verify_options&VERIFY_SHOW_USER_NOTATIONS)?2:0));
+ else
+ show_notation(sig,0,2,0);
+ }
+
+ if( !rc && is_status_enabled() ) {
+ /* print a status response with the fingerprint */
+ PKT_public_key *vpk = xmalloc_clear( sizeof *vpk );
+
+ if( !get_pubkey( vpk, sig->keyid ) ) {
+ byte array[MAX_FINGERPRINT_LEN], *p;
+ char buf[MAX_FINGERPRINT_LEN*4+90], *bufp;
+ size_t i, n;
+
+ bufp = buf;
+ fingerprint_from_pk( vpk, array, &n );
+ p = array;
+ for(i=0; i < n ; i++, p++, bufp += 2)
+ sprintf(bufp, "%02X", *p );
+ /* TODO: Replace the reserved '0' in the field below
+ with bits for status flags (policy url, notation,
+ etc.). Remember to make the buffer larger to
+ match! */
+ sprintf(bufp, " %s %lu %lu %d 0 %d %d %02X ",
+ strtimestamp( sig->timestamp ),
+ (ulong)sig->timestamp,(ulong)sig->expiredate,
+ sig->version,sig->pubkey_algo,sig->digest_algo,
+ sig->sig_class);
+ bufp = bufp + strlen (bufp);
+ if (!vpk->is_primary) {
+ u32 akid[2];
+
+ akid[0] = vpk->main_keyid[0];
+ akid[1] = vpk->main_keyid[1];
+ free_public_key (vpk);
+ vpk = xmalloc_clear( sizeof *vpk );
+ if (get_pubkey (vpk, akid)) {
+ /* impossible error, we simply return a zeroed out fpr */
+ n = MAX_FINGERPRINT_LEN < 20? MAX_FINGERPRINT_LEN : 20;
+ memset (array, 0, n);
+ }
+ else
+ fingerprint_from_pk( vpk, array, &n );
+ }
+ p = array;
+ for(i=0; i < n ; i++, p++, bufp += 2)
+ sprintf(bufp, "%02X", *p );
+ write_status_text( STATUS_VALIDSIG, buf );
+ }
+ free_public_key( vpk );
+ }
+
+ if (!rc)
+ {
+ if(opt.verify_options&VERIFY_PKA_LOOKUPS)
+ pka_uri_from_sig (sig); /* Make sure PKA info is available. */
+ rc = check_signatures_trust( sig );
+ }
+
+ if(sig->flags.expired)
+ {
+ log_info(_("Signature expired %s\n"),
+ asctimestamp(sig->expiredate));
+ rc=G10ERR_GENERAL; /* need a better error here? */
+ }
+ else if(sig->expiredate)
+ log_info(_("Signature expires %s\n"),asctimestamp(sig->expiredate));
+
+ if(opt.verbose)
+ log_info(_("%s signature, digest algorithm %s\n"),
+ sig->sig_class==0x00?_("binary"):
+ sig->sig_class==0x01?_("textmode"):_("unknown"),
+ digest_algo_to_string(sig->digest_algo));
+
+ if( rc )
+ g10_errors_seen = 1;
+ if( opt.batch && rc )
+ g10_exit(1);
+ }
+ else {
+ char buf[50];
+ sprintf(buf, "%08lX%08lX %d %d %02x %lu %d",
+ (ulong)sig->keyid[0], (ulong)sig->keyid[1],
+ sig->pubkey_algo, sig->digest_algo,
+ sig->sig_class, (ulong)sig->timestamp, rc );
+ write_status_text( STATUS_ERRSIG, buf );
+ if( rc == G10ERR_NO_PUBKEY ) {
+ buf[16] = 0;
+ write_status_text( STATUS_NO_PUBKEY, buf );
+ }
+ if( rc != G10ERR_NOT_PROCESSED )
+ log_error(_("Can't check signature: %s\n"), g10_errstr(rc) );
+ }
+ return rc;
+}
+
+
+/****************
+ * Process the tree which starts at node
+ */
+static void
+proc_tree( CTX c, KBNODE node )
+{
+ KBNODE n1;
+ int rc;
+
+ if( opt.list_packets || opt.list_only )
+ return;
+
+ /* we must skip our special plaintext marker packets here becuase
+ they may be the root packet. These packets are only used in
+ addionla checks and skipping them here doesn't matter */
+ while ( node
+ && node->pkt->pkttype == PKT_GPG_CONTROL
+ && node->pkt->pkt.gpg_control->control
+ == CTRLPKT_PLAINTEXT_MARK ) {
+ node = node->next;
+ }
+ if (!node)
+ return;
+
+ c->trustletter = ' ';
+ if( node->pkt->pkttype == PKT_PUBLIC_KEY
+ || node->pkt->pkttype == PKT_PUBLIC_SUBKEY ) {
+ merge_keys_and_selfsig( node );
+ list_node( c, node );
+ }
+ else if( node->pkt->pkttype == PKT_SECRET_KEY ) {
+ merge_keys_and_selfsig( node );
+ list_node( c, node );
+ }
+ else if( node->pkt->pkttype == PKT_ONEPASS_SIG ) {
+ /* check all signatures */
+ if( !c->have_data ) {
+ free_md_filter_context( &c->mfx );
+ /* prepare to create all requested message digests */
+ c->mfx.md = md_open(0, 0);
+
+ /* fixme: why looking for the signature packet and not the
+ one-pass packet? */
+ for( n1 = node; (n1 = find_next_kbnode(n1, PKT_SIGNATURE )); ) {
+ md_enable( c->mfx.md, n1->pkt->pkt.signature->digest_algo);
+ }
+ /* ask for file and hash it */
+ if( c->sigs_only ) {
+ rc = hash_datafiles( c->mfx.md, NULL,
+ c->signed_data, c->sigfilename,
+ n1? (n1->pkt->pkt.onepass_sig->sig_class == 0x01):0 );
+ }
+ else {
+ rc = ask_for_detached_datafile( c->mfx.md, c->mfx.md2,
+ iobuf_get_real_fname(c->iobuf),
+ n1? (n1->pkt->pkt.onepass_sig->sig_class == 0x01):0 );
+ }
+ if( rc ) {
+ log_error("can't hash datafile: %s\n", g10_errstr(rc));
+ return;
+ }
+ }
+ else if ( c->signed_data ) {
+ log_error (_("not a detached signature\n") );
+ return;
+ }
+
+ for( n1 = node; (n1 = find_next_kbnode(n1, PKT_SIGNATURE )); )
+ check_sig_and_print( c, n1 );
+ }
+ else if( node->pkt->pkttype == PKT_GPG_CONTROL
+ && node->pkt->pkt.gpg_control->control
+ == CTRLPKT_CLEARSIGN_START ) {
+ /* clear text signed message */
+ if( !c->have_data ) {
+ log_error("cleartext signature without data\n" );
+ return;
+ }
+ else if ( c->signed_data ) {
+ log_error (_("not a detached signature\n") );
+ return;
+ }
+
+ for( n1 = node; (n1 = find_next_kbnode(n1, PKT_SIGNATURE )); )
+ check_sig_and_print( c, n1 );
+ }
+ else if( node->pkt->pkttype == PKT_SIGNATURE ) {
+ PKT_signature *sig = node->pkt->pkt.signature;
+ int multiple_ok=1;
+
+ n1=find_next_kbnode(node, PKT_SIGNATURE);
+ if(n1)
+ {
+ byte class=sig->sig_class;
+ byte hash=sig->digest_algo;
+
+ for(; n1; (n1 = find_next_kbnode(n1, PKT_SIGNATURE)))
+ {
+ /* We can't currently handle multiple signatures of
+ different classes or digests (we'd pretty much have
+ to run a different hash context for each), but if
+ they are all the same, make an exception. */
+ if(n1->pkt->pkt.signature->sig_class!=class
+ || n1->pkt->pkt.signature->digest_algo!=hash)
+ {
+ multiple_ok=0;
+ log_info(_("WARNING: multiple signatures detected. "
+ "Only the first will be checked.\n"));
+ break;
+ }
+ }
+ }
+
+ if( sig->sig_class != 0x00 && sig->sig_class != 0x01 )
+ log_info(_("standalone signature of class 0x%02x\n"),
+ sig->sig_class);
+ else if( !c->have_data ) {
+ /* detached signature */
+ free_md_filter_context( &c->mfx );
+ c->mfx.md = md_open(sig->digest_algo, 0);
+ if( !opt.pgp2_workarounds )
+ ;
+ else if( sig->digest_algo == DIGEST_ALGO_MD5
+ && is_RSA( sig->pubkey_algo ) ) {
+ /* enable a workaround for a pgp2 bug */
+ c->mfx.md2 = md_open( DIGEST_ALGO_MD5, 0 );
+ }
+ else if( sig->digest_algo == DIGEST_ALGO_SHA1
+ && sig->pubkey_algo == PUBKEY_ALGO_DSA
+ && sig->sig_class == 0x01 ) {
+ /* enable the workaround also for pgp5 when the detached
+ * signature has been created in textmode */
+ c->mfx.md2 = md_open( sig->digest_algo, 0 );
+ }
+#if 0 /* workaround disabled */
+ /* Here we have another hack to work around a pgp 2 bug
+ * It works by not using the textmode for detached signatures;
+ * this will let the first signature check (on md) fail
+ * but the second one (on md2) which adds an extra CR should
+ * then produce the "correct" hash. This is very, very ugly
+ * hack but it may help in some cases (and break others)
+ */
+ /* c->mfx.md2? 0 :(sig->sig_class == 0x01) */
+#endif
+ if ( DBG_HASHING ) {
+ md_start_debug( c->mfx.md, "verify" );
+ if ( c->mfx.md2 )
+ md_start_debug( c->mfx.md2, "verify2" );
+ }
+ if( c->sigs_only ) {
+ rc = hash_datafiles( c->mfx.md, c->mfx.md2,
+ c->signed_data, c->sigfilename,
+ (sig->sig_class == 0x01) );
+ }
+ else {
+ rc = ask_for_detached_datafile( c->mfx.md, c->mfx.md2,
+ iobuf_get_real_fname(c->iobuf),
+ (sig->sig_class == 0x01) );
+ }
+ if( rc ) {
+ log_error("can't hash datafile: %s\n", g10_errstr(rc));
+ return;
+ }
+ }
+ else if ( c->signed_data ) {
+ log_error (_("not a detached signature\n") );
+ return;
+ }
+ else if ( c->pipemode.op == 'B' )
+ ; /* this is a detached signature trough the pipemode handler */
+ else if (!opt.quiet)
+ log_info(_("old style (PGP 2.x) signature\n"));
+
+ if(multiple_ok)
+ for( n1 = node; n1; (n1 = find_next_kbnode(n1, PKT_SIGNATURE )) )
+ check_sig_and_print( c, n1 );
+ else
+ check_sig_and_print( c, node );
+ }
+ else {
+ dump_kbnode (c->list);
+ log_error(_("invalid root packet detected in proc_tree()\n"));
+ dump_kbnode (node);
+ }
+}
diff --git a/g10/mdfilter.c b/g10/mdfilter.c
new file mode 100644
index 0000000..3605db8
--- /dev/null
+++ b/g10/mdfilter.c
@@ -0,0 +1,77 @@
+/* mdfilter.c - filter data and calculate a message digest
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "errors.h"
+#include "iobuf.h"
+#include "memory.h"
+#include "util.h"
+#include "filter.h"
+
+
+
+/****************
+ * This filter is used to collect a message digest
+ */
+int
+md_filter( void *opaque, int control,
+ IOBUF a, byte *buf, size_t *ret_len)
+{
+ size_t size = *ret_len;
+ md_filter_context_t *mfx = opaque;
+ int i, rc=0;
+
+ if( control == IOBUFCTRL_UNDERFLOW ) {
+ if( mfx->maxbuf_size && size > mfx->maxbuf_size )
+ size = mfx->maxbuf_size;
+ i = iobuf_read( a, buf, size );
+ if( i == -1 ) i = 0;
+ if( i ) {
+ md_write(mfx->md, buf, i );
+ if( mfx->md2 )
+ md_write(mfx->md2, buf, i );
+ }
+ else
+ rc = -1; /* eof */
+ *ret_len = i;
+ }
+ else if( control == IOBUFCTRL_DESC )
+ *(char**)buf = "md_filter";
+ return rc;
+}
+
+
+void
+free_md_filter_context( md_filter_context_t *mfx )
+{
+ md_close(mfx->md);
+ md_close(mfx->md2);
+ mfx->md = NULL;
+ mfx->md2 = NULL;
+ mfx->maxbuf_size = 0;
+}
+
diff --git a/g10/misc.c b/g10/misc.c
new file mode 100644
index 0000000..fbe4492
--- /dev/null
+++ b/g10/misc.c
@@ -0,0 +1,1284 @@
+/* misc.c - miscellaneous functions
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ * 2005 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2
+#include <asm/sysinfo.h>
+#include <asm/unistd.h>
+#endif
+#ifdef HAVE_SETRLIMIT
+#include <time.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif
+#ifdef ENABLE_SELINUX_HACKS
+#include <sys/stat.h>
+#endif
+#ifdef _WIN32
+#include <time.h>
+#include <process.h>
+#include <windows.h>
+#include <shlobj.h>
+#ifndef CSIDL_APPDATA
+#define CSIDL_APPDATA 0x001a
+#endif
+#ifndef CSIDL_LOCAL_APPDATA
+#define CSIDL_LOCAL_APPDATA 0x001c
+#endif
+#ifndef CSIDL_FLAG_CREATE
+#define CSIDL_FLAG_CREATE 0x8000
+#endif
+#include "errors.h"
+#include "dynload.h"
+#endif /*_WIN32*/
+
+#include "util.h"
+#include "main.h"
+#include "photoid.h"
+#include "options.h"
+#include "i18n.h"
+#include "cardglue.h"
+
+
+
+
+#ifdef ENABLE_SELINUX_HACKS
+/* A object and a global variable to keep track of files marked as
+ secured. */
+struct secured_file_item
+{
+ struct secured_file_item *next;
+ ino_t ino;
+ dev_t dev;
+};
+static struct secured_file_item *secured_files;
+#endif /*ENABLE_SELINUX_HACKS*/
+
+
+
+#if defined(__linux__) && defined(__alpha__) && __GLIBC__ < 2
+static int
+setsysinfo(unsigned long op, void *buffer, unsigned long size,
+ int *start, void *arg, unsigned long flag)
+{
+ return syscall(__NR_osf_setsysinfo, op, buffer, size, start, arg, flag);
+}
+
+void
+trap_unaligned(void)
+{
+ unsigned int buf[2];
+
+ buf[0] = SSIN_UACPROC;
+ buf[1] = UAC_SIGBUS | UAC_NOPRINT;
+ setsysinfo(SSI_NVPAIRS, buf, 1, 0, 0, 0);
+}
+#else
+void
+trap_unaligned(void)
+{ /* dummy */
+}
+#endif
+
+
+int
+disable_core_dumps()
+{
+#ifdef HAVE_DOSISH_SYSTEM
+ return 0;
+#else
+#ifdef HAVE_SETRLIMIT
+ struct rlimit limit;
+
+ limit.rlim_cur = 0;
+ limit.rlim_max = 0;
+ if( !setrlimit( RLIMIT_CORE, &limit ) )
+ return 0;
+ if( errno != EINVAL && errno != ENOSYS )
+ log_fatal(_("can't disable core dumps: %s\n"), strerror(errno) );
+#endif
+ return 1;
+#endif
+}
+
+
+/* For the sake of SELinux we want to restrict access through gpg to
+ certain files we keep under our own control. This function
+ registers such a file and is_secured_file may then be used to
+ check whether a file has ben registered as secured. */
+void
+register_secured_file (const char *fname)
+{
+#ifdef ENABLE_SELINUX_HACKS
+ struct stat buf;
+ struct secured_file_item *sf;
+
+ /* Note that we stop immediatley if something goes wrong here. */
+ if (stat (fname, &buf))
+ log_fatal (_("fstat of `%s' failed in %s: %s\n"), fname,
+ "register_secured_file", strerror (errno));
+/* log_debug ("registering `%s' i=%lu.%lu\n", fname, */
+/* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */
+ for (sf=secured_files; sf; sf = sf->next)
+ {
+ if (sf->ino == buf.st_ino && sf->dev == buf.st_dev)
+ return; /* Already registered. */
+ }
+
+ sf = xmalloc (sizeof *sf);
+ sf->ino = buf.st_ino;
+ sf->dev = buf.st_dev;
+ sf->next = secured_files;
+ secured_files = sf;
+#endif /*ENABLE_SELINUX_HACKS*/
+}
+
+/* Remove a file registerd as secure. */
+void
+unregister_secured_file (const char *fname)
+{
+#ifdef ENABLE_SELINUX_HACKS
+ struct stat buf;
+ struct secured_file_item *sf, *sfprev;
+
+ if (stat (fname, &buf))
+ {
+ log_error (_("fstat of `%s' failed in %s: %s\n"), fname,
+ "unregister_secured_file", strerror (errno));
+ return;
+ }
+/* log_debug ("unregistering `%s' i=%lu.%lu\n", fname, */
+/* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */
+ for (sfprev=NULL,sf=secured_files; sf; sfprev=sf, sf = sf->next)
+ {
+ if (sf->ino == buf.st_ino && sf->dev == buf.st_dev)
+ {
+ if (sfprev)
+ sfprev->next = sf->next;
+ else
+ secured_files = sf->next;
+ xfree (sf);
+ return;
+ }
+ }
+#endif /*ENABLE_SELINUX_HACKS*/
+}
+
+/* Return true if FD is corresponds to a secured file. Using -1 for
+ FS is allowed and will return false. */
+int
+is_secured_file (int fd)
+{
+#ifdef ENABLE_SELINUX_HACKS
+ struct stat buf;
+ struct secured_file_item *sf;
+
+ if (fd == -1)
+ return 0; /* No file descriptor so it can't be secured either. */
+
+ /* Note that we print out a error here and claim that a file is
+ secure if something went wrong. */
+ if (fstat (fd, &buf))
+ {
+ log_error (_("fstat(%d) failed in %s: %s\n"), fd,
+ "is_secured_file", strerror (errno));
+ return 1;
+ }
+/* log_debug ("is_secured_file (%d) i=%lu.%lu\n", fd, */
+/* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */
+ for (sf=secured_files; sf; sf = sf->next)
+ {
+ if (sf->ino == buf.st_ino && sf->dev == buf.st_dev)
+ return 1; /* Yes. */
+ }
+#endif /*ENABLE_SELINUX_HACKS*/
+ return 0; /* No. */
+}
+
+/* Return true if FNAME is corresponds to a secured file. Using NULL,
+ "" or "-" for FS is allowed and will return false. This function is
+ used before creating a file, thus it won't fail if the file does
+ not exist. */
+int
+is_secured_filename (const char *fname)
+{
+#ifdef ENABLE_SELINUX_HACKS
+ struct stat buf;
+ struct secured_file_item *sf;
+
+ if (iobuf_is_pipe_filename (fname) || !*fname)
+ return 0;
+
+ /* Note that we print out a error here and claim that a file is
+ secure if something went wrong. */
+ if (stat (fname, &buf))
+ {
+ if (errno == ENOENT || errno == EPERM || errno == EACCES)
+ return 0;
+ log_error (_("fstat of `%s' failed in %s: %s\n"), fname,
+ "is_secured_filename", strerror (errno));
+ return 1;
+ }
+/* log_debug ("is_secured_filename (%s) i=%lu.%lu\n", fname, */
+/* (unsigned long)buf.st_dev, (unsigned long)buf.st_ino); */
+ for (sf=secured_files; sf; sf = sf->next)
+ {
+ if (sf->ino == buf.st_ino && sf->dev == buf.st_dev)
+ return 1; /* Yes. */
+ }
+#endif /*ENABLE_SELINUX_HACKS*/
+ return 0; /* No. */
+}
+
+
+
+u16
+checksum_u16( unsigned n )
+{
+ u16 a;
+
+ a = (n >> 8) & 0xff;
+ a += n & 0xff;
+ return a;
+}
+
+
+u16
+checksum( byte *p, unsigned n )
+{
+ u16 a;
+
+ for(a=0; n; n-- )
+ a += *p++;
+ return a;
+}
+
+u16
+checksum_mpi( MPI a )
+{
+ u16 csum;
+ byte *buffer;
+ unsigned nbytes;
+ unsigned nbits;
+
+ buffer = mpi_get_buffer( a, &nbytes, NULL );
+ nbits = mpi_get_nbits(a);
+ csum = checksum_u16( nbits );
+ csum += checksum( buffer, nbytes );
+ xfree( buffer );
+ return csum;
+}
+
+u32
+buffer_to_u32( const byte *buffer )
+{
+ unsigned long a;
+ a = *buffer << 24;
+ a |= buffer[1] << 16;
+ a |= buffer[2] << 8;
+ a |= buffer[3];
+ return a;
+}
+
+void
+print_pubkey_algo_note( int algo )
+{
+ if(algo >= 100 && algo <= 110)
+ {
+ static int warn=0;
+ if(!warn)
+ {
+ warn=1;
+ log_info(_("WARNING: using experimental public key algorithm %s\n"),
+ pubkey_algo_to_string(algo));
+ }
+ }
+}
+
+void
+print_cipher_algo_note( int algo )
+{
+ if(algo >= 100 && algo <= 110)
+ {
+ static int warn=0;
+ if(!warn)
+ {
+ warn=1;
+ log_info(_("WARNING: using experimental cipher algorithm %s\n"),
+ cipher_algo_to_string(algo));
+ }
+ }
+}
+
+void
+print_digest_algo_note( int algo )
+{
+ if(algo >= 100 && algo <= 110)
+ {
+ static int warn=0;
+ if(!warn)
+ {
+ warn=1;
+ log_info(_("WARNING: using experimental digest algorithm %s\n"),
+ digest_algo_to_string(algo));
+ }
+ }
+ else if(algo==DIGEST_ALGO_MD5)
+ log_info(_("WARNING: digest algorithm %s is deprecated\n"),
+ digest_algo_to_string(algo));
+}
+
+/* Return a string which is used as a kind of process ID */
+const byte *
+get_session_marker( size_t *rlen )
+{
+ static byte marker[SIZEOF_UNSIGNED_LONG*2];
+ static int initialized;
+
+ if ( !initialized ) {
+ volatile ulong aa, bb; /* we really want the uninitialized value */
+ ulong a, b;
+
+ initialized = 1;
+ /* also this marker is guessable it is not easy to use this
+ * for a faked control packet because an attacker does not
+ * have enough control about the time the verification does
+ * take place. Of course, we can add just more random but
+ * than we need the random generator even for verification
+ * tasks - which does not make sense. */
+ a = aa ^ (ulong)getpid();
+ b = bb ^ (ulong)time(NULL);
+ memcpy( marker, &a, SIZEOF_UNSIGNED_LONG );
+ memcpy( marker+SIZEOF_UNSIGNED_LONG, &b, SIZEOF_UNSIGNED_LONG );
+ }
+ *rlen = sizeof(marker);
+ return marker;
+}
+
+/****************
+ * Wrapper around the libgcrypt function with addional checks on
+ * openPGP contraints for the algo ID.
+ */
+int
+openpgp_cipher_test_algo( int algo )
+{
+ if( algo < 0 || algo > 110 )
+ return G10ERR_CIPHER_ALGO;
+ return check_cipher_algo(algo);
+}
+
+int
+openpgp_pk_test_algo( int algo, unsigned int usage_flags )
+{
+ if( algo < 0 || algo > 110 )
+ return G10ERR_PUBKEY_ALGO;
+ return check_pubkey_algo2( algo, usage_flags );
+}
+
+int
+openpgp_pk_algo_usage ( int algo )
+{
+ int use = 0;
+
+ /* they are hardwired in gpg 1.0 */
+ switch ( algo ) {
+ case PUBKEY_ALGO_RSA:
+ use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_ENC | PUBKEY_USAGE_AUTH;
+ break;
+ case PUBKEY_ALGO_RSA_E:
+ use = PUBKEY_USAGE_ENC;
+ break;
+ case PUBKEY_ALGO_RSA_S:
+ use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG;
+ break;
+ case PUBKEY_ALGO_ELGAMAL_E:
+ use = PUBKEY_USAGE_ENC;
+ break;
+ case PUBKEY_ALGO_DSA:
+ use = PUBKEY_USAGE_CERT | PUBKEY_USAGE_SIG | PUBKEY_USAGE_AUTH;
+ break;
+ default:
+ break;
+ }
+ return use;
+}
+
+int
+openpgp_md_test_algo( int algo )
+{
+ if( algo < 0 || algo > 110 )
+ return G10ERR_DIGEST_ALGO;
+ return check_digest_algo(algo);
+}
+
+#ifdef USE_IDEA
+/* Special warning for the IDEA cipher */
+void
+idea_cipher_warn(int show)
+{
+ static int warned=0;
+
+ if(!warned || show)
+ {
+ log_info(_("the IDEA cipher plugin is not present\n"));
+ log_info(_("please see %s for more information\n"),
+ "http://www.gnupg.org/faq/why-not-idea.html");
+ warned=1;
+ }
+}
+#endif
+
+static unsigned long get_signature_count(PKT_secret_key *sk)
+{
+#ifdef ENABLE_CARD_SUPPORT
+ if(sk && sk->is_protected && sk->protect.s2k.mode==1002)
+ {
+ struct agent_card_info_s info;
+ if(agent_scd_getattr("SIG-COUNTER",&info)==0)
+ return info.sig_counter;
+ }
+#endif
+
+ /* How to do this without a card? */
+
+ return 0;
+}
+
+/* Expand %-strings. Returns a string which must be xfreed. Returns
+ NULL if the string cannot be expanded (too large). */
+char *
+pct_expando(const char *string,struct expando_args *args)
+{
+ const char *ch=string;
+ int idx=0,maxlen=0,done=0;
+ u32 pk_keyid[2]={0,0},sk_keyid[2]={0,0};
+ char *ret=NULL;
+
+ if(args->pk)
+ keyid_from_pk(args->pk,pk_keyid);
+
+ if(args->sk)
+ keyid_from_sk(args->sk,sk_keyid);
+
+ /* This is used so that %k works in photoid command strings in
+ --list-secret-keys (which of course has a sk, but no pk). */
+ if(!args->pk && args->sk)
+ keyid_from_sk(args->sk,pk_keyid);
+
+ while(*ch!='\0')
+ {
+ char *str=NULL;
+
+ if(!done)
+ {
+ /* 8192 is way bigger than we'll need here */
+ if(maxlen>=8192)
+ goto fail;
+
+ maxlen+=1024;
+ ret=xrealloc(ret,maxlen);
+ }
+
+ done=0;
+
+ if(*ch=='%')
+ {
+ switch(*(ch+1))
+ {
+ case 's': /* short key id */
+ if(idx+8<maxlen)
+ {
+ sprintf(&ret[idx],"%08lX",(ulong)sk_keyid[1]);
+ idx+=8;
+ done=1;
+ }
+ break;
+
+ case 'S': /* long key id */
+ if(idx+16<maxlen)
+ {
+ sprintf(&ret[idx],"%08lX%08lX",
+ (ulong)sk_keyid[0],(ulong)sk_keyid[1]);
+ idx+=16;
+ done=1;
+ }
+ break;
+
+ case 'k': /* short key id */
+ if(idx+8<maxlen)
+ {
+ sprintf(&ret[idx],"%08lX",(ulong)pk_keyid[1]);
+ idx+=8;
+ done=1;
+ }
+ break;
+
+ case 'K': /* long key id */
+ if(idx+16<maxlen)
+ {
+ sprintf(&ret[idx],"%08lX%08lX",
+ (ulong)pk_keyid[0],(ulong)pk_keyid[1]);
+ idx+=16;
+ done=1;
+ }
+ break;
+
+ case 'c': /* signature count from card, if any. */
+ if(idx+10<maxlen)
+ {
+ sprintf(&ret[idx],"%lu",get_signature_count(args->sk));
+ idx+=strlen(&ret[idx]);
+ done=1;
+ }
+ break;
+
+ case 'p': /* primary pk fingerprint of a sk */
+ case 'f': /* pk fingerprint */
+ case 'g': /* sk fingerprint */
+ {
+ byte array[MAX_FINGERPRINT_LEN];
+ size_t len;
+ int i;
+
+ if((*(ch+1))=='p' && args->sk)
+ {
+ if(args->sk->is_primary)
+ fingerprint_from_sk(args->sk,array,&len);
+ else if(args->sk->main_keyid[0] || args->sk->main_keyid[1])
+ {
+ PKT_public_key *pk=
+ xmalloc_clear(sizeof(PKT_public_key));
+
+ if(get_pubkey_fast(pk,args->sk->main_keyid)==0)
+ fingerprint_from_pk(pk,array,&len);
+ else
+ memset(array,0,(len=MAX_FINGERPRINT_LEN));
+ free_public_key(pk);
+ }
+ else
+ memset(array,0,(len=MAX_FINGERPRINT_LEN));
+ }
+ else if((*(ch+1))=='f' && args->pk)
+ fingerprint_from_pk(args->pk,array,&len);
+ else if((*(ch+1))=='g' && args->sk)
+ fingerprint_from_sk(args->sk,array,&len);
+ else
+ memset(array,0,(len=MAX_FINGERPRINT_LEN));
+
+ if(idx+(len*2)<maxlen)
+ {
+ for(i=0;i<len;i++)
+ {
+ sprintf(&ret[idx],"%02X",array[i]);
+ idx+=2;
+ }
+ done=1;
+ }
+ }
+ break;
+
+ case 't': /* e.g. "jpg" */
+ str=image_type_to_string(args->imagetype,0);
+ /* fall through */
+
+ case 'T': /* e.g. "image/jpeg" */
+ if(str==NULL)
+ str=image_type_to_string(args->imagetype,2);
+
+ if(idx+strlen(str)<maxlen)
+ {
+ strcpy(&ret[idx],str);
+ idx+=strlen(str);
+ done=1;
+ }
+ break;
+
+ case '%':
+ if(idx+1<maxlen)
+ {
+ ret[idx++]='%';
+ ret[idx]='\0';
+ done=1;
+ }
+ break;
+
+ /* Any unknown %-keys (like %i, %o, %I, and %O) are
+ passed through for later expansion. Note this also
+ handles the case where the last character in the
+ string is a '%' - the terminating \0 will end up here
+ and properly terminate the string. */
+ default:
+ if(idx+2<maxlen)
+ {
+ ret[idx++]='%';
+ ret[idx++]=*(ch+1);
+ ret[idx]='\0';
+ done=1;
+ }
+ break;
+ }
+
+ if(done)
+ ch++;
+ }
+ else
+ {
+ if(idx+1<maxlen)
+ {
+ ret[idx++]=*ch;
+ ret[idx]='\0';
+ done=1;
+ }
+ }
+
+ if(done)
+ ch++;
+ }
+
+ return ret;
+
+ fail:
+ xfree(ret);
+ return NULL;
+}
+
+void
+deprecated_warning(const char *configname,unsigned int configlineno,
+ const char *option,const char *repl1,const char *repl2)
+{
+ if(configname)
+ {
+ if(strncmp("--",option,2)==0)
+ option+=2;
+
+ if(strncmp("--",repl1,2)==0)
+ repl1+=2;
+
+ log_info(_("%s:%d: deprecated option \"%s\"\n"),
+ configname,configlineno,option);
+ }
+ else
+ log_info(_("WARNING: \"%s\" is a deprecated option\n"),option);
+
+ log_info(_("please use \"%s%s\" instead\n"),repl1,repl2);
+}
+
+
+void
+deprecated_command (const char *name)
+{
+ log_info(_("WARNING: \"%s\" is a deprecated command - do not use it\n"),
+ name);
+}
+
+
+const char *
+compress_algo_to_string(int algo)
+{
+ const char *s=NULL;
+
+ switch(algo)
+ {
+ case COMPRESS_ALGO_NONE:
+ s=_("Uncompressed");
+ break;
+
+ case COMPRESS_ALGO_ZIP:
+ s="ZIP";
+ break;
+
+ case COMPRESS_ALGO_ZLIB:
+ s="ZLIB";
+ break;
+
+#ifdef HAVE_BZIP2
+ case COMPRESS_ALGO_BZIP2:
+ s="BZIP2";
+ break;
+#endif
+ }
+
+ return s;
+}
+
+int
+string_to_compress_algo(const char *string)
+{
+ /* NOTE TO TRANSLATOR: See doc/TRANSLATE about this string. */
+ if(match_multistr(_("uncompressed|none"),string))
+ return 0;
+ else if(ascii_strcasecmp(string,"uncompressed")==0)
+ return 0;
+ else if(ascii_strcasecmp(string,"none")==0)
+ return 0;
+ else if(ascii_strcasecmp(string,"zip")==0)
+ return 1;
+ else if(ascii_strcasecmp(string,"zlib")==0)
+ return 2;
+#ifdef HAVE_BZIP2
+ else if(ascii_strcasecmp(string,"bzip2")==0)
+ return 3;
+#endif
+ else if(ascii_strcasecmp(string,"z0")==0)
+ return 0;
+ else if(ascii_strcasecmp(string,"z1")==0)
+ return 1;
+ else if(ascii_strcasecmp(string,"z2")==0)
+ return 2;
+#ifdef HAVE_BZIP2
+ else if(ascii_strcasecmp(string,"z3")==0)
+ return 3;
+#endif
+ else
+ return -1;
+}
+
+int
+check_compress_algo(int algo)
+{
+#ifdef HAVE_BZIP2
+ if(algo>=0 && algo<=3)
+ return 0;
+#else
+ if(algo>=0 && algo<=2)
+ return 0;
+#endif
+
+ return G10ERR_COMPR_ALGO;
+}
+
+int
+default_cipher_algo(void)
+{
+ if(opt.def_cipher_algo)
+ return opt.def_cipher_algo;
+ else if(opt.personal_cipher_prefs)
+ return opt.personal_cipher_prefs[0].value;
+ else
+ return opt.s2k_cipher_algo;
+}
+
+/* There is no default_digest_algo function, but see
+ sign.c:hash_for() */
+
+int
+default_compress_algo(void)
+{
+ if(opt.compress_algo!=-1)
+ return opt.compress_algo;
+ else if(opt.personal_compress_prefs)
+ return opt.personal_compress_prefs[0].value;
+ else
+ return DEFAULT_COMPRESS_ALGO;
+}
+
+const char *
+compliance_option_string(void)
+{
+ switch(opt.compliance)
+ {
+ case CO_RFC2440:
+ return "--openpgp";
+ case CO_PGP2:
+ return "--pgp2";
+ case CO_PGP6:
+ return "--pgp6";
+ case CO_PGP7:
+ return "--pgp7";
+ case CO_PGP8:
+ return "--pgp8";
+ default:
+ return "???";
+ }
+}
+
+static const char *
+compliance_string(void)
+{
+ switch(opt.compliance)
+ {
+ case CO_RFC2440:
+ return "OpenPGP";
+ case CO_PGP2:
+ return "PGP 2.x";
+ case CO_PGP6:
+ return "PGP 6.x";
+ case CO_PGP7:
+ return "PGP 7.x";
+ case CO_PGP8:
+ return "PGP 8.x";
+ default:
+ return "???";
+ }
+}
+
+void
+compliance_failure(void)
+{
+ log_info(_("this message may not be usable by %s\n"),compliance_string());
+ opt.compliance=CO_GNUPG;
+}
+
+/* Break a string into successive option pieces. Accepts single word
+ options and key=value argument options. */
+char *
+optsep(char **stringp)
+{
+ char *tok,*end;
+
+ tok=*stringp;
+ if(tok)
+ {
+ end=strpbrk(tok," ,=");
+ if(end)
+ {
+ int sawequals=0;
+ char *ptr=end;
+
+ /* what we need to do now is scan along starting with *end,
+ If the next character we see (ignoring spaces) is an =
+ sign, then there is an argument. */
+
+ while(*ptr)
+ {
+ if(*ptr=='=')
+ sawequals=1;
+ else if(*ptr!=' ')
+ break;
+ ptr++;
+ }
+
+ /* There is an argument, so grab that too. At this point,
+ ptr points to the first character of the argument. */
+ if(sawequals)
+ {
+ /* Is it a quoted argument? */
+ if(*ptr=='"')
+ {
+ ptr++;
+ end=strchr(ptr,'"');
+ if(end)
+ end++;
+ }
+ else
+ end=strpbrk(ptr," ,");
+ }
+
+ if(end && *end)
+ {
+ *end='\0';
+ *stringp=end+1;
+ }
+ else
+ *stringp=NULL;
+ }
+ else
+ *stringp=NULL;
+ }
+
+ return tok;
+}
+
+/* Breaks an option value into key and value. Returns NULL if there
+ is no value. Note that "string" is modified to remove the =value
+ part. */
+char *
+argsplit(char *string)
+{
+ char *equals,*arg=NULL;
+
+ equals=strchr(string,'=');
+ if(equals)
+ {
+ char *quote,*space;
+
+ *equals='\0';
+ arg=equals+1;
+
+ /* Quoted arg? */
+ quote=strchr(arg,'"');
+ if(quote)
+ {
+ arg=quote+1;
+
+ quote=strchr(arg,'"');
+ if(quote)
+ *quote='\0';
+ }
+ else
+ {
+ size_t spaces;
+
+ /* Trim leading spaces off of the arg */
+ spaces=strspn(arg," ");
+ arg+=spaces;
+ }
+
+ /* Trim tailing spaces off of the tag */
+ space=strchr(string,' ');
+ if(space)
+ *space='\0';
+ }
+
+ return arg;
+}
+
+/* Return the length of the initial token, leaving off any
+ argument. */
+static size_t
+optlen(const char *s)
+{
+ char *end=strpbrk(s," =");
+
+ if(end)
+ return end-s;
+ else
+ return strlen(s);
+}
+
+int
+parse_options(char *str,unsigned int *options,
+ struct parse_options *opts,int noisy)
+{
+ char *tok;
+
+ if (str && !strcmp (str, "help"))
+ {
+ int i,maxlen=0;
+
+ /* Figure out the longest option name so we can line these up
+ neatly. */
+ for(i=0;opts[i].name;i++)
+ if(opts[i].help && maxlen<strlen(opts[i].name))
+ maxlen=strlen(opts[i].name);
+
+ for(i=0;opts[i].name;i++)
+ if(opts[i].help)
+ printf("%s%*s%s\n",opts[i].name,
+ maxlen+2-(int)strlen(opts[i].name),"",_(opts[i].help));
+
+ g10_exit(0);
+ }
+
+ while((tok=optsep(&str)))
+ {
+ int i,rev=0;
+ char *otok=tok;
+
+ if(tok[0]=='\0')
+ continue;
+
+ if(ascii_strncasecmp("no-",tok,3)==0)
+ {
+ rev=1;
+ tok+=3;
+ }
+
+ for(i=0;opts[i].name;i++)
+ {
+ size_t toklen=optlen(tok);
+
+ if(ascii_strncasecmp(opts[i].name,tok,toklen)==0)
+ {
+ /* We have a match, but it might be incomplete */
+ if(toklen!=strlen(opts[i].name))
+ {
+ int j;
+
+ for(j=i+1;opts[j].name;j++)
+ {
+ if(ascii_strncasecmp(opts[j].name,tok,toklen)==0)
+ {
+ if(noisy)
+ log_info(_("ambiguous option `%s'\n"),otok);
+ return 0;
+ }
+ }
+ }
+
+ if(rev)
+ {
+ *options&=~opts[i].bit;
+ if(opts[i].value)
+ *opts[i].value=NULL;
+ }
+ else
+ {
+ *options|=opts[i].bit;
+ if(opts[i].value)
+ *opts[i].value=argsplit(tok);
+ }
+ break;
+ }
+ }
+
+ if(!opts[i].name)
+ {
+ if(noisy)
+ log_info(_("unknown option `%s'\n"),otok);
+ return 0;
+ }
+ }
+
+ return 1;
+}
+
+
+/* Return a new malloced string by unescaping the string S. Escaping
+ is percent escaping and '+'/space mapping. A binary nul will
+ silently be replaced by a 0xFF. */
+char *
+unescape_percent_string (const unsigned char *s)
+{
+ char *buffer, *d;
+
+ buffer = d = xmalloc (strlen (s)+1);
+ while (*s)
+ {
+ if (*s == '%' && s[1] && s[2])
+ {
+ s++;
+ *d = xtoi_2 (s);
+ if (!*d)
+ *d = '\xff';
+ d++;
+ s += 2;
+ }
+ else if (*s == '+')
+ {
+ *d++ = ' ';
+ s++;
+ }
+ else
+ *d++ = *s++;
+ }
+ *d = 0;
+ return buffer;
+}
+
+
+int
+has_invalid_email_chars (const char *s)
+{
+ int at_seen=0;
+ const char *valid_chars=
+ "01234567890_-.abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ";
+
+ for ( ; *s; s++ )
+ {
+ if ( *s & 0x80 )
+ return 1;
+ if ( *s == '@' )
+ at_seen=1;
+ else if ( !at_seen && !( !!strchr( valid_chars, *s ) || *s == '+' ) )
+ return 1;
+ else if ( at_seen && !strchr( valid_chars, *s ) )
+ return 1;
+ }
+ return 0;
+}
+
+
+/* Check whether NAME represents a valid mailbox according to
+ RFC822. Returns true if so. */
+int
+is_valid_mailbox (const char *name)
+{
+ return !( !name
+ || !*name
+ || has_invalid_email_chars (name)
+ || string_count_chr (name,'@') != 1
+ || *name == '@'
+ || name[strlen(name)-1] == '@'
+ || name[strlen(name)-1] == '.'
+ || strstr (name, "..") );
+}
+
+
+/* This is a helper function to load a Windows function from either of
+ one DLLs. */
+#ifdef HAVE_W32_SYSTEM
+static HRESULT
+w32_shgetfolderpath (HWND a, int b, HANDLE c, DWORD d, LPSTR e)
+{
+ static int initialized;
+ static HRESULT (WINAPI * func)(HWND,int,HANDLE,DWORD,LPSTR);
+
+ if (!initialized)
+ {
+ static char *dllnames[] = { "shell32.dll", "shfolder.dll", NULL };
+ void *handle;
+ int i;
+
+ initialized = 1;
+
+ for (i=0, handle = NULL; !handle && dllnames[i]; i++)
+ {
+ handle = dlopen (dllnames[i], RTLD_LAZY);
+ if (handle)
+ {
+ func = dlsym (handle, "SHGetFolderPathA");
+ if (!func)
+ {
+ dlclose (handle);
+ handle = NULL;
+ }
+ }
+ }
+ }
+
+ if (func)
+ return func (a,b,c,d,e);
+ else
+ return -1;
+}
+#endif /*HAVE_W32_SYSTEM*/
+
+
+/* Set up the default home directory. The usual --homedir option
+ should be parsed later. */
+char *
+default_homedir (void)
+{
+ char *dir;
+
+ dir = getenv("GNUPGHOME");
+#ifdef HAVE_W32_SYSTEM
+ if (!dir || !*dir)
+ dir = read_w32_registry_string (NULL, "Software\\GNU\\GnuPG", "HomeDir");
+ if (!dir || !*dir)
+ {
+ char path[MAX_PATH];
+
+ /* It might be better to use LOCAL_APPDATA because this is
+ defined as "non roaming" and thus more likely to be kept
+ locally. For private keys this is desired. However, given
+ that many users copy private keys anyway forth and back,
+ using a system roaming serives might be better than to let
+ them do it manually. A security conscious user will anyway
+ use the registry entry to have better control. */
+ if (w32_shgetfolderpath (NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE,
+ NULL, 0, path) >= 0)
+ {
+ char *tmp = xmalloc (strlen (path) + 6 +1);
+ strcpy (stpcpy (tmp, path), "\\gnupg");
+ dir = tmp;
+
+ /* Try to create the directory if it does not yet
+ exists. */
+ if (access (dir, F_OK))
+ CreateDirectory (dir, NULL);
+ }
+ }
+#endif /*HAVE_W32_SYSTEM*/
+ if (!dir || !*dir)
+ dir = GNUPG_HOMEDIR;
+
+ return dir;
+}
+
+
+/* Return the name of the libexec directory. The name is allocated in
+ a static area on the first use. This function won't fail. */
+const char *
+get_libexecdir (void)
+{
+#ifdef HAVE_W32_SYSTEM
+ static int got_dir;
+ static char dir[MAX_PATH+5];
+
+ if (!got_dir)
+ {
+ char *p;
+
+ if ( !GetModuleFileName ( NULL, dir, MAX_PATH) )
+ {
+ log_debug ("GetModuleFileName failed: %s\n", w32_strerror (0));
+ *dir = 0;
+ }
+ got_dir = 1;
+ p = strrchr (dir, DIRSEP_C);
+ if (p)
+ *p = 0;
+ else
+ {
+ log_debug ("bad filename `%s' returned for this process\n", dir);
+ *dir = 0;
+ }
+ }
+
+ if (*dir)
+ return dir;
+ /* Fallback to the hardwired value. */
+#endif /*HAVE_W32_SYSTEM*/
+
+ return GNUPG_LIBEXECDIR;
+}
+
+/* Similar to access(2), but uses PATH to find the file. */
+int
+path_access(const char *file,int mode)
+{
+ char *envpath;
+ int ret=-1;
+
+ envpath=getenv("PATH");
+
+ if(!envpath
+#ifdef HAVE_DRIVE_LETTERS
+ || (((file[0]>='A' && file[0]<='Z')
+ || (file[0]>='a' && file[0]<='z'))
+ && file[1]==':')
+#else
+ || file[0]=='/'
+#endif
+ )
+ return access(file,mode);
+ else
+ {
+ /* At least as large as, but most often larger than we need. */
+ char *buffer=xmalloc(strlen(envpath)+1+strlen(file)+1);
+ char *split,*item,*path=xstrdup(envpath);
+
+ split=path;
+
+ while((item=strsep(&split,PATHSEP_S)))
+ {
+ strcpy(buffer,item);
+ strcat(buffer,"/");
+ strcat(buffer,file);
+ ret=access(buffer,mode);
+ if(ret==0)
+ break;
+ }
+
+ xfree(path);
+ xfree(buffer);
+ }
+
+ return ret;
+}
diff --git a/g10/openfile.c b/g10/openfile.c
new file mode 100644
index 0000000..c106cdb
--- /dev/null
+++ b/g10/openfile.c
@@ -0,0 +1,430 @@
+/* openfile.c
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ * 2005 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include <errno.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include "util.h"
+#include "memory.h"
+#include "ttyio.h"
+#include "options.h"
+#include "main.h"
+#include "status.h"
+#include "i18n.h"
+
+#ifdef USE_ONLY_8DOT3
+#define SKELEXT ".skl"
+#else
+#define SKELEXT EXTSEP_S "skel"
+#endif
+
+#if defined (HAVE_DRIVE_LETTERS) || defined (__riscos__)
+#define CMP_FILENAME(a,b) ascii_strcasecmp( (a), (b) )
+#else
+#define CMP_FILENAME(a,b) strcmp( (a), (b) )
+#endif
+
+#ifdef MKDIR_TAKES_ONE_ARG
+#undef mkdir
+#define mkdir(a,b) mkdir(a)
+#endif
+
+/* FIXME: Implement opt.interactive. */
+
+/****************
+ * Check whether FNAME exists and ask if it's okay to overwrite an
+ * existing one.
+ * Returns: True: it's okay to overwrite or the file does not exist
+ * False: Do not overwrite
+ */
+int
+overwrite_filep( const char *fname )
+{
+ if( iobuf_is_pipe_filename (fname) )
+ return 1; /* Writing to stdout is always okay */
+
+ if( access( fname, F_OK ) )
+ return 1; /* does not exist */
+
+#ifndef HAVE_DOSISH_SYSTEM
+ if ( !strcmp ( fname, "/dev/null" ) )
+ return 1; /* does not do any harm */
+#endif
+
+ /* fixme: add some backup stuff in case of overwrite */
+ if( opt.answer_yes )
+ return 1;
+ if( opt.answer_no || opt.batch )
+ return 0; /* do not overwrite */
+
+ tty_printf(_("File `%s' exists. "), fname);
+ if( cpr_enabled () )
+ tty_printf ("\n");
+ if( cpr_get_answer_is_yes("openfile.overwrite.okay",
+ _("Overwrite? (y/N) ")) )
+ return 1;
+ return 0;
+}
+
+
+/****************
+ * Strip know extensions from iname and return a newly allocated
+ * filename. Return NULL if we can't do that.
+ */
+char *
+make_outfile_name( const char *iname )
+{
+ size_t n;
+
+ if ( iobuf_is_pipe_filename (iname) )
+ return xstrdup("-");
+
+ n = strlen(iname);
+ if( n > 4 && ( !CMP_FILENAME(iname+n-4, EXTSEP_S "gpg")
+ || !CMP_FILENAME(iname+n-4, EXTSEP_S "pgp")
+ || !CMP_FILENAME(iname+n-4, EXTSEP_S "sig")
+ || !CMP_FILENAME(iname+n-4, EXTSEP_S "asc") ) ) {
+ char *buf = xstrdup( iname );
+ buf[n-4] = 0;
+ return buf;
+ }
+ else if( n > 5 && !CMP_FILENAME(iname+n-5, EXTSEP_S "sign") ) {
+ char *buf = xstrdup( iname );
+ buf[n-5] = 0;
+ return buf;
+ }
+
+ log_info(_("%s: unknown suffix\n"), iname );
+ return NULL;
+}
+
+
+/****************
+ * Ask for a outputfilename and use the given one as default.
+ * Return NULL if no file has been given or it is not possible to
+ * ask the user.
+ */
+char *
+ask_outfile_name( const char *name, size_t namelen )
+{
+ size_t n;
+ const char *s;
+ char *prompt;
+ char *fname;
+ char *defname;
+
+ if( opt.batch )
+ return NULL;
+
+ s = _("Enter new filename");
+
+ defname = name && namelen? make_printable_string( name, namelen, 0): NULL;
+ n = strlen(s) + (defname?strlen (defname):0) + 10;
+ prompt = xmalloc(n);
+ if( defname )
+ sprintf(prompt, "%s [%s]: ", s, defname );
+ else
+ sprintf(prompt, "%s: ", s );
+ tty_enable_completion(NULL);
+ fname = cpr_get("openfile.askoutname", prompt );
+ cpr_kill_prompt();
+ tty_disable_completion();
+ xfree(prompt);
+ if( !*fname ) {
+ xfree( fname ); fname = NULL;
+ fname = defname; defname = NULL;
+ }
+ xfree(defname);
+ if (fname)
+ trim_spaces (fname);
+ return fname;
+}
+
+
+/****************
+ * Make an output filename for the inputfile INAME.
+ * Returns an IOBUF and an errorcode
+ * Mode 0 = use ".gpg"
+ * 1 = use ".asc"
+ * 2 = use ".sig"
+ */
+int
+open_outfile( const char *iname, int mode, IOBUF *a )
+{
+ int rc = 0;
+
+ *a = NULL;
+ if( iobuf_is_pipe_filename (iname) && !opt.outfile ) {
+ *a = iobuf_create(NULL);
+ if( !*a ) {
+ log_error(_("can't open `%s': %s\n"), "[stdout]", strerror(errno) );
+ rc = G10ERR_CREATE_FILE;
+ }
+ else if( opt.verbose )
+ log_info(_("writing to stdout\n"));
+ }
+ else {
+ char *buf = NULL;
+ const char *name;
+
+ if( opt.dry_run )
+ name = "/dev/null";
+ else if( opt.outfile )
+ name = opt.outfile;
+ else {
+#ifdef USE_ONLY_8DOT3
+ if (opt.mangle_dos_filenames)
+ {
+ /* It is quite common for DOS system to have only one dot in a
+ * a filename So if we have something like this, we simple
+ * replace the suffix except in cases where the suffix is
+ * larger than 3 characters and not identlically to the new one.
+ * We should really map the filenames to 8.3 but this tends to
+ * be more complicated and is probaly a duty of the filesystem
+ */
+ char *dot;
+ const char *newsfx = mode==1 ? ".asc" :
+ mode==2 ? ".sig" : ".gpg";
+
+ buf = xmalloc(strlen(iname)+4+1);
+ strcpy(buf,iname);
+ dot = strrchr(buf, '.' );
+ if ( dot && dot > buf && dot[1] && strlen(dot) <= 4
+ && CMP_FILENAME(newsfx, dot)
+ && !(strchr (dot, '/') || strchr (dot, '\\')))
+ {
+ /* There is a dot, the dot is not the first character,
+ the suffix is not longer than 3, the suffix is not
+ equal to the new suffix and tehre is no path delimter
+ after the dot (e.g. foo.1/bar): Replace the
+ suffix. */
+ strcpy (dot, newsfx );
+ }
+ else if ( dot && !dot[1] ) /* Don't duplicate a trailing dot. */
+ strcpy ( dot, newsfx+1 );
+ else
+ strcat ( buf, newsfx ); /* Just append the new suffix. */
+ }
+ if (!buf)
+#endif /* USE_ONLY_8DOT3 */
+ {
+ buf = xmalloc(strlen(iname)+4+1);
+ strcpy(stpcpy(buf,iname), mode==1 ? EXTSEP_S "asc" :
+ mode==2 ? EXTSEP_S "sig" : EXTSEP_S "gpg");
+ }
+ name = buf;
+ }
+
+ rc = 0;
+ while( !overwrite_filep (name) )
+ {
+ char *tmp = ask_outfile_name (NULL, 0);
+ if ( !tmp || !*tmp )
+ {
+ xfree (tmp);
+ rc = G10ERR_FILE_EXISTS;
+ break;
+ }
+ xfree (buf);
+ name = buf = tmp;
+ }
+
+ if( !rc )
+ {
+ if (is_secured_filename (name) )
+ {
+ *a = NULL;
+ errno = EPERM;
+ }
+ else
+ *a = iobuf_create( name );
+ if( !*a )
+ {
+ log_error(_("can't create `%s': %s\n"), name, strerror(errno) );
+ rc = G10ERR_CREATE_FILE;
+ }
+ else if( opt.verbose )
+ log_info(_("writing to `%s'\n"), name );
+ }
+ xfree(buf);
+ }
+
+ if (*a)
+ iobuf_ioctl (*a,3,1,NULL); /* disable fd caching */
+
+ return rc;
+}
+
+
+/****************
+ * Try to open a file without the extension ".sig" or ".asc"
+ * Return NULL if such a file is not available.
+ */
+IOBUF
+open_sigfile( const char *iname, progress_filter_context_t *pfx )
+{
+ IOBUF a = NULL;
+ size_t len;
+
+ if( !iobuf_is_pipe_filename (iname) ) {
+ len = strlen(iname);
+ if( len > 4 && ( !strcmp(iname + len - 4, EXTSEP_S "sig")
+ || ( len > 5 && !strcmp(iname + len - 5, EXTSEP_S "sign") )
+ || !strcmp(iname + len - 4, EXTSEP_S "asc")) ) {
+ char *buf;
+ buf = xstrdup(iname);
+ buf[len-(buf[len-1]=='n'?5:4)] = 0 ;
+ a = iobuf_open( buf );
+ if (a && is_secured_file (iobuf_get_fd (a)))
+ {
+ iobuf_close (a);
+ a = NULL;
+ errno = EPERM;
+ }
+ if( a && opt.verbose )
+ log_info(_("assuming signed data in `%s'\n"), buf );
+ if (a && pfx)
+ handle_progress (pfx, a, buf);
+ xfree(buf);
+ }
+ }
+ return a;
+}
+
+/****************
+ * Copy the option file skeleton to the given directory.
+ */
+static void
+copy_options_file( const char *destdir )
+{
+ const char *datadir = GNUPG_DATADIR;
+ char *fname;
+ FILE *src, *dst;
+ int linefeeds=0;
+ int c;
+ mode_t oldmask;
+ int esc = 0;
+ int any_option = 0;
+
+ if( opt.dry_run )
+ return;
+
+ fname = xmalloc( strlen(datadir) + strlen(destdir) + 15 );
+ strcpy(stpcpy(fname, datadir), DIRSEP_S "options" SKELEXT );
+ src = fopen( fname, "r" );
+ if (src && is_secured_file (fileno (src)))
+ {
+ fclose (src);
+ src = NULL;
+ errno = EPERM;
+ }
+ if( !src ) {
+ log_error(_("can't open `%s': %s\n"), fname, strerror(errno) );
+ xfree(fname);
+ return;
+ }
+ strcpy(stpcpy(fname, destdir), DIRSEP_S "gpg" EXTSEP_S "conf" );
+ oldmask=umask(077);
+ if ( is_secured_filename (fname) )
+ {
+ dst = NULL;
+ errno = EPERM;
+ }
+ else
+ dst = fopen( fname, "w" );
+ umask(oldmask);
+ if( !dst ) {
+ log_error(_("can't create `%s': %s\n"), fname, strerror(errno) );
+ fclose( src );
+ xfree(fname);
+ return;
+ }
+
+ while( (c=getc(src)) != EOF ) {
+ if( linefeeds < 3 ) {
+ if( c == '\n' )
+ linefeeds++;
+ }
+ else {
+ putc( c, dst );
+ if (c== '\n')
+ esc = 1;
+ else if (esc == 1) {
+ if (c == ' ' || c == '\t')
+ ;
+ else if (c == '#')
+ esc = 2;
+ else
+ any_option = 1;
+ }
+ }
+ }
+ fclose( dst );
+ fclose( src );
+ log_info(_("new configuration file `%s' created\n"), fname );
+ if (any_option)
+ log_info (_("WARNING: options in `%s'"
+ " are not yet active during this run\n"),
+ fname);
+ xfree(fname);
+}
+
+
+void
+try_make_homedir( const char *fname )
+{
+ const char *defhome = GNUPG_HOMEDIR;
+
+ /* Create the directory only if the supplied directory name
+ * is the same as the default one. This way we avoid to create
+ * arbitrary directories when a non-default homedirectory is used.
+ * To cope with HOME, we do compare only the suffix if we see that
+ * the default homedir does start with a tilde.
+ */
+ if( opt.dry_run || opt.no_homedir_creation )
+ return;
+
+ if ( ( *defhome == '~'
+ && ( strlen(fname) >= strlen (defhome+1)
+ && !strcmp(fname+strlen(fname)-strlen(defhome+1),
+ defhome+1 ) ))
+ || ( *defhome != '~'
+ && !compare_filenames( fname, defhome ) )
+ ) {
+ if( mkdir( fname, S_IRUSR|S_IWUSR|S_IXUSR ) )
+ log_fatal( _("can't create directory `%s': %s\n"),
+ fname, strerror(errno) );
+ else if( !opt.quiet )
+ log_info( _("directory `%s' created\n"), fname );
+ copy_options_file( fname );
+/* log_info(_("you have to start GnuPG again, " */
+/* "so it can read the new configuration file\n") ); */
+/* g10_exit(1); */
+ }
+}
diff --git a/g10/options.h b/g10/options.h
new file mode 100644
index 0000000..8f866e2
--- /dev/null
+++ b/g10/options.h
@@ -0,0 +1,335 @@
+/* options.h
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+#ifndef G10_OPTIONS_H
+#define G10_OPTIONS_H
+
+#include <sys/types.h>
+#include <types.h>
+#include "main.h"
+#include "packet.h"
+
+#ifndef EXTERN_UNLESS_MAIN_MODULE
+/* Norcraft can't cope with common symbols */
+#if defined (__riscos__) && !defined (INCLUDED_BY_MAIN_MODULE)
+#define EXTERN_UNLESS_MAIN_MODULE extern
+#else
+#define EXTERN_UNLESS_MAIN_MODULE
+#endif
+#endif
+
+EXTERN_UNLESS_MAIN_MODULE
+struct
+{
+ int verbose;
+ int quiet;
+ unsigned debug;
+ int armor;
+ char *outfile;
+ off_t max_output;
+ int dry_run;
+ int list_only;
+ int textmode;
+ int expert;
+ const char *def_sig_expire;
+ int ask_sig_expire;
+ const char *def_cert_expire;
+ int ask_cert_expire;
+ int batch; /* run in batch mode */
+ int answer_yes; /* answer yes on most questions */
+ int answer_no; /* answer no on most questions */
+ int check_sigs; /* check key signatures */
+ int with_colons;
+ int with_key_data;
+ int with_fingerprint; /* opt --with-fingerprint active */
+ int fingerprint; /* list fingerprints */
+ int list_sigs; /* list signatures */
+ int no_armor;
+ int list_packets; /* list-packets mode: 1=normal, 2=invoked by command*/
+ int def_cipher_algo;
+ int force_v3_sigs;
+ int force_v4_certs;
+ int force_mdc;
+ int disable_mdc;
+ int def_digest_algo;
+ int cert_digest_algo;
+ int compress_algo;
+ int compress_level;
+ int bz2_compress_level;
+ int bz2_decompress_lowmem;
+ const char *def_secret_key;
+ char *def_recipient;
+ int def_recipient_self;
+ int def_cert_level;
+ int min_cert_level;
+ int ask_cert_level;
+ int no_version;
+ int marginals_needed;
+ int completes_needed;
+ int max_cert_depth;
+ const char *homedir;
+
+ char *display; /* 5 options to be passed to the gpg-agent */
+ char *ttyname;
+ char *ttytype;
+ char *lc_ctype;
+ char *lc_messages;
+
+ int skip_verify;
+ int compress_keys;
+ int compress_sigs;
+ /* TM_CLASSIC must be zero to accomodate trustdbs generated before
+ we started storing the trust model inside the trustdb. */
+ enum
+ {
+ TM_CLASSIC=0, TM_PGP=1, TM_EXTERNAL=2, TM_ALWAYS, TM_DIRECT, TM_AUTO
+ } trust_model;
+ int force_ownertrust;
+ enum
+ {
+ CO_GNUPG=0, CO_RFC2440, CO_RFC1991, CO_PGP2, CO_PGP6, CO_PGP7, CO_PGP8
+ } compliance;
+ enum
+ {
+ KF_SHORT, KF_LONG, KF_0xSHORT, KF_0xLONG
+ } keyid_format;
+ int pgp2_workarounds;
+ int shm_coprocess;
+ const char *set_filename;
+ STRLIST comments;
+ int throw_keyid;
+ const char *photo_viewer;
+ int s2k_mode;
+ int s2k_digest_algo;
+ int s2k_cipher_algo;
+ unsigned char s2k_count; /* This is the encoded form, not the raw
+ count */
+ int simple_sk_checksum; /* create the deprecated rfc2440 secret key
+ protection */
+ int not_dash_escaped;
+ int escape_from;
+ int lock_once;
+ struct keyserver_spec
+ {
+ char *uri;
+ char *scheme;
+ char *auth;
+ char *host;
+ char *port;
+ char *path;
+ char *opaque;
+ STRLIST options;
+ struct
+ {
+ unsigned int direct_uri:1;
+ } flags;
+ struct keyserver_spec *next;
+ } *keyserver;
+ struct
+ {
+ unsigned int options;
+ unsigned int import_options;
+ unsigned int export_options;
+ STRLIST other;
+ } keyserver_options;
+ int exec_disable;
+ int exec_path_set;
+ unsigned int import_options;
+ unsigned int export_options;
+ unsigned int list_options;
+ unsigned int verify_options;
+ const char *def_preference_list;
+ const char *def_keyserver_url;
+ prefitem_t *personal_cipher_prefs;
+ prefitem_t *personal_digest_prefs;
+ prefitem_t *personal_compress_prefs;
+ int no_perm_warn;
+ int no_mdc_warn;
+ char *temp_dir;
+ int no_encrypt_to;
+ int interactive;
+ struct notation *sig_notations;
+ struct notation *cert_notations;
+ STRLIST sig_policy_url;
+ STRLIST cert_policy_url;
+ STRLIST sig_keyserver_url;
+ STRLIST cert_subpackets;
+ STRLIST sig_subpackets;
+ int allow_non_selfsigned_uid;
+ int allow_freeform_uid;
+ int no_literal;
+ ulong set_filesize;
+ int fast_list_mode;
+ int fixed_list_mode;
+ int ignore_time_conflict;
+ int ignore_valid_from;
+ int ignore_crc_error;
+ int ignore_mdc_error;
+ int command_fd;
+ const char *override_session_key;
+ int show_session_key;
+ int use_agent;
+ const char *gpg_agent_info;
+ int try_all_secrets;
+ int no_expensive_trust_checks;
+ int no_sig_cache;
+ int no_sig_create_check;
+ int no_auto_check_trustdb;
+ int preserve_permissions;
+ int no_homedir_creation;
+ struct groupitem *grouplist;
+ int strict;
+ int mangle_dos_filenames;
+ int enable_progress_filter;
+ unsigned int screen_columns;
+ unsigned int screen_lines;
+ byte *show_subpackets;
+ int rfc2440_text;
+
+ /* If true, let write failures on the status-fd exit the process. */
+ int exit_on_status_write_error;
+
+ /* If > 0, limit the number of card insertion prompts to this
+ value. */
+ int limit_card_insert_tries;
+
+#ifdef ENABLE_CARD_SUPPORT
+ const char *ctapi_driver; /* Library to access the ctAPI. */
+ const char *pcsc_driver; /* Library to access the PC/SC system. */
+ int disable_ccid; /* Disable the use of the internal CCID driver. */
+#endif /*ENABLE_CARD_SUPPORT*/
+
+ struct
+ {
+ /* If set, require an 0x19 backsig to be present on signatures
+ made by signing subkeys. If not set, a missing backsig is not
+ an error (but an invalid backsig still is). */
+ unsigned int require_cross_cert:1;
+ unsigned int use_embedded_filename:1;
+ unsigned int utf8_filename:1;
+ unsigned int dsa2:1;
+ } flags;
+
+ /* Linked list of ways to find a key if the key isn't on the local
+ keyring. */
+ struct akl
+ {
+ enum {AKL_CERT, AKL_PKA, AKL_LDAP, AKL_KEYSERVER, AKL_SPEC} type;
+ struct keyserver_spec *spec;
+ struct akl *next;
+ } *auto_key_locate;
+
+ /* True if multiple concatenated signatures may be verified. */
+ int allow_multisig_verification;
+ int passwd_repeat;
+} opt;
+
+/* CTRL is used to keep some global variables we currently can't
+ avoid. Future concurrent versions of gpg will put it into a per
+ request structure CTRL. */
+EXTERN_UNLESS_MAIN_MODULE
+struct {
+ int in_auto_key_retrieve; /* True if we are doing an
+ auto_key_retrieve. */
+} glo_ctrl;
+
+#define DBG_PACKET_VALUE 1 /* debug packet reading/writing */
+#define DBG_MPI_VALUE 2 /* debug mpi details */
+#define DBG_CIPHER_VALUE 4 /* debug cipher handling */
+ /* (may reveal sensitive data) */
+#define DBG_FILTER_VALUE 8 /* debug internal filter handling */
+#define DBG_IOBUF_VALUE 16 /* debug iobuf stuff */
+#define DBG_MEMORY_VALUE 32 /* debug memory allocation stuff */
+#define DBG_CACHE_VALUE 64 /* debug the cacheing */
+#define DBG_MEMSTAT_VALUE 128 /* show memory statistics */
+#define DBG_TRUST_VALUE 256 /* debug the trustdb */
+#define DBG_HASHING_VALUE 512 /* debug hashing operations */
+#define DBG_EXTPROG_VALUE 1024 /* debug external program calls */
+#define DBG_CARD_IO_VALUE 2048 /* debug smart card I/O. */
+
+#define DBG_PACKET (opt.debug & DBG_PACKET_VALUE)
+#define DBG_FILTER (opt.debug & DBG_FILTER_VALUE)
+#define DBG_CACHE (opt.debug & DBG_CACHE_VALUE)
+#define DBG_TRUST (opt.debug & DBG_TRUST_VALUE)
+#define DBG_HASHING (opt.debug & DBG_HASHING_VALUE)
+#define DBG_EXTPROG (opt.debug & DBG_EXTPROG_VALUE)
+#define DBG_CARD_IO (opt.debug & DBG_CARD_IO_VALUE)
+
+
+#define GNUPG (opt.compliance==CO_GNUPG)
+#define RFC1991 (opt.compliance==CO_RFC1991 || opt.compliance==CO_PGP2)
+#define RFC2440 (opt.compliance==CO_RFC2440)
+#define PGP2 (opt.compliance==CO_PGP2)
+#define PGP6 (opt.compliance==CO_PGP6)
+#define PGP7 (opt.compliance==CO_PGP7)
+#define PGP8 (opt.compliance==CO_PGP8)
+
+/* Various option flags. Note that there should be no common string
+ names between the IMPORT_ and EXPORT_ flags as they can be mixed in
+ the keyserver-options option. */
+
+#define IMPORT_LOCAL_SIGS (1<<0)
+#define IMPORT_REPAIR_PKS_SUBKEY_BUG (1<<1)
+#define IMPORT_FAST (1<<2)
+#define IMPORT_SK2PK (1<<3)
+#define IMPORT_MERGE_ONLY (1<<4)
+#define IMPORT_MINIMAL (1<<5)
+#define IMPORT_CLEAN (1<<6)
+
+#define EXPORT_LOCAL_SIGS (1<<0)
+#define EXPORT_ATTRIBUTES (1<<1)
+#define EXPORT_SENSITIVE_REVKEYS (1<<2)
+#define EXPORT_RESET_SUBKEY_PASSWD (1<<3)
+#define EXPORT_MINIMAL (1<<4)
+#define EXPORT_CLEAN (1<<5)
+
+#define LIST_SHOW_PHOTOS (1<<0)
+#define LIST_SHOW_POLICY_URLS (1<<1)
+#define LIST_SHOW_STD_NOTATIONS (1<<2)
+#define LIST_SHOW_USER_NOTATIONS (1<<3)
+#define LIST_SHOW_NOTATIONS (LIST_SHOW_STD_NOTATIONS|LIST_SHOW_USER_NOTATIONS)
+#define LIST_SHOW_KEYSERVER_URLS (1<<4)
+#define LIST_SHOW_UID_VALIDITY (1<<5)
+#define LIST_SHOW_UNUSABLE_UIDS (1<<6)
+#define LIST_SHOW_UNUSABLE_SUBKEYS (1<<7)
+#define LIST_SHOW_KEYRING (1<<8)
+#define LIST_SHOW_SIG_EXPIRE (1<<9)
+#define LIST_SHOW_SIG_SUBPACKETS (1<<10)
+
+#define VERIFY_SHOW_PHOTOS (1<<0)
+#define VERIFY_SHOW_POLICY_URLS (1<<1)
+#define VERIFY_SHOW_STD_NOTATIONS (1<<2)
+#define VERIFY_SHOW_USER_NOTATIONS (1<<3)
+#define VERIFY_SHOW_NOTATIONS (VERIFY_SHOW_STD_NOTATIONS|VERIFY_SHOW_USER_NOTATIONS)
+#define VERIFY_SHOW_KEYSERVER_URLS (1<<4)
+#define VERIFY_SHOW_UID_VALIDITY (1<<5)
+#define VERIFY_SHOW_UNUSABLE_UIDS (1<<6)
+#define VERIFY_PKA_LOOKUPS (1<<7)
+#define VERIFY_PKA_TRUST_INCREASE (1<<8)
+
+#define KEYSERVER_USE_TEMP_FILES (1<<0)
+#define KEYSERVER_KEEP_TEMP_FILES (1<<1)
+#define KEYSERVER_ADD_FAKE_V3 (1<<2)
+#define KEYSERVER_AUTO_KEY_RETRIEVE (1<<3)
+#define KEYSERVER_HONOR_KEYSERVER_URL (1<<4)
+#define KEYSERVER_HONOR_PKA_RECORD (1<<5)
+
+#endif /*G10_OPTIONS_H*/
diff --git a/g10/options.skel b/g10/options.skel
new file mode 100644
index 0000000..83c0949
--- /dev/null
+++ b/g10/options.skel
@@ -0,0 +1,241 @@
+# These first three lines are not copied to the gpg.conf file in
+# the users home directory.
+# $Id: options.skel 4106 2006-04-11 19:20:08Z dshaw $
+# Options for GnuPG
+# Copyright 1998, 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
+#
+# This file is free software; as a special exception the author gives
+# unlimited permission to copy and/or distribute it, with or without
+# modifications, as long as this notice is preserved.
+#
+# This file is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY, to the extent permitted by law; without even the
+# implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
+#
+# Unless you specify which option file to use (with the command line
+# option "--options filename"), GnuPG uses the file ~/.gnupg/gpg.conf
+# by default.
+#
+# An options file can contain any long options which are available in
+# GnuPG. If the first non white space character of a line is a '#',
+# this line is ignored. Empty lines are also ignored.
+#
+# See the man page for a list of options.
+
+# Uncomment the following option to get rid of the copyright notice
+
+#no-greeting
+
+# If you have more than 1 secret key in your keyring, you may want to
+# uncomment the following option and set your preferred keyid.
+
+#default-key 621CC013
+
+# If you do not pass a recipient to gpg, it will ask for one. Using
+# this option you can encrypt to a default key. Key validation will
+# not be done in this case. The second form uses the default key as
+# default recipient.
+
+#default-recipient some-user-id
+#default-recipient-self
+
+# Use --encrypt-to to add the specified key as a recipient to all
+# messages. This is useful, for example, when sending mail through a
+# mail client that does not automatically encrypt mail to your key.
+# In the example, this option allows you to read your local copy of
+# encrypted mail that you've sent to others.
+
+#encrypt-to some-key-id
+
+# By default GnuPG creates version 3 signatures for data files. This
+# is not strictly OpenPGP compliant but PGP 6 and most versions of PGP
+# 7 require them. To disable this behavior, you may use this option
+# or --openpgp.
+
+#no-force-v3-sigs
+
+# Because some mailers change lines starting with "From " to ">From "
+# it is good to handle such lines in a special way when creating
+# cleartext signatures; all other PGP versions do it this way too.
+# To enable full OpenPGP compliance you may want to use this option.
+
+#no-escape-from-lines
+
+# If you do not use the Latin-1 (ISO-8859-1) charset, you should tell
+# GnuPG which is the native character set. Please check the man page
+# for supported character sets. This character set is only used for
+# metadata and not for the actual message which does not undergo any
+# translation. Note that future version of GnuPG will change to UTF-8
+# as default character set. In most cases this option is not required
+# GnuPG is able to figure out the correct charset and use that.
+
+#charset utf-8
+
+# Group names may be defined like this:
+# group mynames = paige 0x12345678 joe patti
+#
+# Any time "mynames" is a recipient (-r or --recipient), it will be
+# expanded to the names "paige", "joe", and "patti", and the key ID
+# "0x12345678". Note there is only one level of expansion - you
+# cannot make an group that points to another group. Note also that
+# if there are spaces in the recipient name, this will appear as two
+# recipients. In these cases it is better to use the key ID.
+
+#group mynames = paige 0x12345678 joe patti
+
+# Lock the file only once for the lifetime of a process. If you do
+# not define this, the lock will be obtained and released every time
+# it is needed, which is usually preferable.
+
+#lock-once
+
+# GnuPG can send and receive keys to and from a keyserver. These
+# servers can be HKP, email, or LDAP (if GnuPG is built with LDAP
+# support).
+#
+# Example HKP keyserver:
+# hkp://subkeys.pgp.net
+#
+# Example email keyserver:
+# mailto:pgp-public-keys@keys.pgp.net
+#
+# Example LDAP keyservers:
+# ldap://keyserver.pgp.com
+#
+# Regular URL syntax applies, and you can set an alternate port
+# through the usual method:
+# hkp://keyserver.example.net:22742
+#
+# If you have problems connecting to a HKP server through a buggy http
+# proxy, you can use keyserver option broken-http-proxy (see below),
+# but first you should make sure that you have read the man page
+# regarding proxies (keyserver option honor-http-proxy)
+#
+# Most users just set the name and type of their preferred keyserver.
+# Note that most servers (with the notable exception of
+# ldap://keyserver.pgp.com) synchronize changes with each other. Note
+# also that a single server name may actually point to multiple
+# servers via DNS round-robin. hkp://subkeys.pgp.net is an example of
+# such a "server", which spreads the load over a number of physical
+# servers.
+
+keyserver hkp://subkeys.pgp.net
+#keyserver mailto:pgp-public-keys@keys.nl.pgp.net
+#keyserver ldap://keyserver.pgp.com
+
+# Common options for keyserver functions:
+#
+# include-disabled = when searching, include keys marked as "disabled"
+# on the keyserver (not all keyservers support this).
+#
+# no-include-revoked = when searching, do not include keys marked as
+# "revoked" on the keyserver.
+#
+# verbose = show more information as the keys are fetched.
+# Can be used more than once to increase the amount
+# of information shown.
+#
+# use-temp-files = use temporary files instead of a pipe to talk to the
+# keyserver. Some platforms (Win32 for one) always
+# have this on.
+#
+# keep-temp-files = do not delete temporary files after using them
+# (really only useful for debugging)
+#
+# honor-http-proxy = if the keyserver uses HTTP, honor the http_proxy
+# environment variable
+#
+# broken-http-proxy = try to work around a buggy HTTP proxy
+#
+# auto-key-retrieve = automatically fetch keys as needed from the keyserver
+# when verifying signatures or when importing keys that
+# have been revoked by a revocation key that is not
+# present on the keyring.
+#
+# no-include-attributes = do not include attribute IDs (aka "photo IDs")
+# when sending keys to the keyserver.
+
+#keyserver-options auto-key-retrieve
+
+# Display photo user IDs in key listings
+
+# list-options show-photos
+
+# Display photo user IDs when a signature from a key with a photo is
+# verified
+
+# verify-options show-photos
+
+# Use this program to display photo user IDs
+#
+# %i is expanded to a temporary file that contains the photo.
+# %I is the same as %i, but the file isn't deleted afterwards by GnuPG.
+# %k is expanded to the key ID of the key.
+# %K is expanded to the long OpenPGP key ID of the key.
+# %t is expanded to the extension of the image (e.g. "jpg").
+# %T is expanded to the MIME type of the image (e.g. "image/jpeg").
+# %f is expanded to the fingerprint of the key.
+# %% is %, of course.
+#
+# If %i or %I are not present, then the photo is supplied to the
+# viewer on standard input. If your platform supports it, standard
+# input is the best way to do this as it avoids the time and effort in
+# generating and then cleaning up a secure temp file.
+#
+# If no photo-viewer is provided, GnuPG will look for xloadimage, eog,
+# or display (ImageMagick). On Mac OS X and Windows, the default is
+# to use your regular JPEG image viewer.
+#
+# Some other viewers:
+# photo-viewer "qiv %i"
+# photo-viewer "ee %i"
+#
+# This one saves a copy of the photo ID in your home directory:
+# photo-viewer "cat > ~/photoid-for-key-%k.%t"
+#
+# Use your MIME handler to view photos:
+# photo-viewer "metamail -q -d -b -c %T -s 'KeyID 0x%k' -f GnuPG"
+
+# Passphrase agent
+#
+# We support the old experimental passphrase agent protocol as well as
+# the new Assuan based one (currently available in the "newpg" package
+# at ftp.gnupg.org/gcrypt/alpha/aegypten/). To make use of the agent,
+# you have to run an agent as daemon and use the option
+#
+# use-agent
+#
+# which tries to use the agent but will fallback to the regular mode
+# if there is a problem connecting to the agent. The normal way to
+# locate the agent is by looking at the environment variable
+# GPG_AGENT_INFO which should have been set during gpg-agent startup.
+# In certain situations the use of this variable is not possible, thus
+# the option
+#
+# --gpg-agent-info=<path>:<pid>:1
+#
+# may be used to override it.
+
+# Automatic key location
+#
+# GnuPG can automatically locate and retrieve keys as needed using the
+# auto-key-locate option. This happens when encrypting to an email
+# address (in the "user@example.com" form), and there are no
+# user@example.com keys on the local keyring. This option takes the
+# following arguments, in the order they are to be tried:
+#
+# cert = locate a key using DNS CERT, as specified in 2538bis
+# (currently in draft): http://www.josefsson.org/rfc2538bis/
+#
+# pka = locate a key using DNS PKA.
+#
+# ldap = locate a key using the PGP Universal method of checking
+# "ldap://keys.(thedomain)".
+#
+# keyserver = locate a key using whatever keyserver is defined using
+# the keyserver option.
+#
+# You may also list arbitrary keyservers here by URL.
+#
+# Try CERT, then PKA, then LDAP, then hkp://subkeys.net:
+#auto-key-locate cert pka ldap hkp://subkeys.pgp.net
diff --git a/g10/packet.h b/g10/packet.h
new file mode 100644
index 0000000..f318133
--- /dev/null
+++ b/g10/packet.h
@@ -0,0 +1,569 @@
+/* packet.h - packet definitions
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef G10_PACKET_H
+#define G10_PACKET_H
+
+#include "types.h"
+#include "iobuf.h"
+#include "mpi.h"
+#include "cipher.h"
+#include "filter.h"
+#include "global.h"
+
+#define DEBUG_PARSE_PACKET 1
+
+typedef enum {
+ PKT_NONE =0,
+ PKT_PUBKEY_ENC =1, /* public key encrypted packet */
+ PKT_SIGNATURE =2, /* secret key encrypted packet */
+ PKT_SYMKEY_ENC =3, /* session key packet (OpenPGP)*/
+ PKT_ONEPASS_SIG =4, /* one pass sig packet (OpenPGP)*/
+ PKT_SECRET_KEY =5, /* secret key */
+ PKT_PUBLIC_KEY =6, /* public key */
+ PKT_SECRET_SUBKEY =7, /* secret subkey (OpenPGP) */
+ PKT_COMPRESSED =8, /* compressed data packet */
+ PKT_ENCRYPTED =9, /* conventional encrypted data */
+ PKT_MARKER =10, /* marker packet (OpenPGP) */
+ PKT_PLAINTEXT =11, /* plaintext data with filename and mode */
+ PKT_RING_TRUST =12, /* keyring trust packet */
+ PKT_USER_ID =13, /* user id packet */
+ PKT_PUBLIC_SUBKEY =14, /* public subkey (OpenPGP) */
+ PKT_OLD_COMMENT =16, /* comment packet from an OpenPGP draft */
+ PKT_ATTRIBUTE =17, /* PGP's attribute packet */
+ PKT_ENCRYPTED_MDC =18, /* integrity protected encrypted data */
+ PKT_MDC =19, /* manipulation detection code packet */
+ PKT_COMMENT =61, /* new comment packet (private) */
+ PKT_GPG_CONTROL =63 /* internal control packet */
+} pkttype_t;
+
+typedef struct packet_struct PACKET;
+
+/* PKT_GPG_CONTROL types */
+typedef enum {
+ CTRLPKT_CLEARSIGN_START = 1,
+ CTRLPKT_PIPEMODE = 2,
+ CTRLPKT_PLAINTEXT_MARK =3
+} ctrlpkttype_t;
+
+typedef enum {
+ PREFTYPE_NONE = 0,
+ PREFTYPE_SYM = 1,
+ PREFTYPE_HASH = 2,
+ PREFTYPE_ZIP = 3
+} preftype_t;
+
+typedef struct {
+ byte type;
+ byte value;
+} prefitem_t;
+
+typedef struct {
+ int mode;
+ byte hash_algo;
+ byte salt[8];
+ u32 count;
+} STRING2KEY;
+
+typedef struct {
+ byte version;
+ byte cipher_algo; /* cipher algorithm used */
+ STRING2KEY s2k;
+ byte seskeylen; /* keylength in byte or 0 for no seskey */
+ byte seskey[1];
+} PKT_symkey_enc;
+
+typedef struct {
+ u32 keyid[2]; /* 64 bit keyid */
+ byte version;
+ byte pubkey_algo; /* algorithm used for public key scheme */
+ byte throw_keyid;
+ MPI data[PUBKEY_MAX_NENC];
+} PKT_pubkey_enc;
+
+
+typedef struct {
+ u32 keyid[2]; /* 64 bit keyid */
+ byte sig_class; /* sig classification */
+ byte digest_algo; /* algorithm used for digest */
+ byte pubkey_algo; /* algorithm used for public key scheme */
+ byte last; /* a stupid flag */
+} PKT_onepass_sig;
+
+
+typedef struct {
+ size_t size; /* allocated */
+ size_t len; /* used */
+ byte data[1];
+} subpktarea_t;
+
+struct revocation_key {
+ byte class;
+ byte algid;
+ byte fpr[MAX_FINGERPRINT_LEN];
+};
+
+
+/* Object to keep information about a PKA DNS record. */
+typedef struct
+{
+ int valid; /* An actual PKA record exists for EMAIL. */
+ int checked; /* Set to true if the FPR has been checked against the
+ actual key. */
+ char *uri; /* Malloced string with the URI. NULL if the URI is
+ not available.*/
+ unsigned char fpr[20]; /* The fingerprint as stored in the PKA RR. */
+ char email[1];/* The email address from the notation data. */
+} pka_info_t;
+
+
+/* Object to keep information pertaining to a signature. */
+typedef struct
+{
+ struct
+ {
+ unsigned checked:1; /* Signature has been checked. */
+ unsigned valid:1; /* Signature is good (if checked is set). */
+ unsigned chosen_selfsig:1; /* A selfsig that is the chosen one. */
+ unsigned unknown_critical:1;
+ unsigned exportable:1;
+ unsigned revocable:1;
+ unsigned policy_url:1; /* At least one policy URL is present */
+ unsigned notation:1; /* At least one notation is present */
+ unsigned pref_ks:1; /* At least one preferred keyserver is present */
+ unsigned expired:1;
+ unsigned pka_tried:1; /* Set if we tried to retrieve the PKA record. */
+ } flags;
+ u32 keyid[2]; /* 64 bit keyid */
+ u32 timestamp; /* Signature made (seconds since Epoch). */
+ u32 expiredate; /* Expires at this date or 0 if not at all. */
+ byte version;
+ byte sig_class; /* Sig classification, append for MD calculation. */
+ byte pubkey_algo; /* Algorithm used for public key scheme */
+ /* (PUBKEY_ALGO_xxx) */
+ byte digest_algo; /* Algorithm used for digest (DIGEST_ALGO_xxxx). */
+ byte trust_depth;
+ byte trust_value;
+ const byte *trust_regexp;
+ struct revocation_key **revkey;
+ int numrevkeys;
+ pka_info_t *pka_info; /* Malloced PKA data or NULL if not
+ available. See also flags.pka_tried. */
+ subpktarea_t *hashed; /* All subpackets with hashed data (v4 only). */
+ subpktarea_t *unhashed; /* Ditto for unhashed data. */
+ byte digest_start[2]; /* First 2 bytes of the digest. */
+ MPI data[PUBKEY_MAX_NSIG];
+} PKT_signature;
+
+#define ATTRIB_IMAGE 1
+
+/* This is the cooked form of attributes */
+struct user_attribute {
+ byte type;
+ const byte *data;
+ u32 len;
+};
+
+typedef struct
+{
+ int ref; /* reference counter */
+ int len; /* length of the name */
+ struct user_attribute *attribs;
+ int numattribs;
+ byte *attrib_data; /* if this is not NULL, the packet is an attribute */
+ unsigned long attrib_len;
+ byte *namehash;
+ int help_key_usage;
+ u32 help_key_expire;
+ int help_full_count;
+ int help_marginal_count;
+ int is_primary; /* 2 if set via the primary flag, 1 if calculated */
+ int is_revoked;
+ int is_expired;
+ u32 expiredate; /* expires at this date or 0 if not at all */
+ prefitem_t *prefs; /* list of preferences (may be NULL)*/
+ u32 created; /* according to the self-signature */
+ byte selfsigversion;
+ struct
+ {
+ /* TODO: Move more flags here */
+ unsigned mdc:1;
+ unsigned ks_modify:1;
+ unsigned compacted:1;
+ } flags;
+ char name[1];
+} PKT_user_id;
+
+struct revoke_info
+{
+ /* revoked at this date */
+ u32 date;
+ /* the keyid of the revoking key (selfsig or designated revoker) */
+ u32 keyid[2];
+ /* the algo of the revoking key */
+ byte algo;
+};
+
+/****************
+ * Note about the pkey/skey elements: We assume that the secret keys
+ * has the same elemts as the public key at the begin of the array, so
+ * that npkey < nskey and it is possible to compare the secret and
+ * public keys by comparing the first npkey elements of pkey againts skey.
+ */
+typedef struct {
+ u32 timestamp; /* key made */
+ u32 expiredate; /* expires at this date or 0 if not at all */
+ u32 max_expiredate; /* must not expire past this date */
+ struct revoke_info revoked;
+ byte hdrbytes; /* number of header bytes */
+ byte version;
+ byte selfsigversion; /* highest version of all of the self-sigs */
+ byte pubkey_algo; /* algorithm used for public key scheme */
+ byte pubkey_usage; /* for now only used to pass it to getkey() */
+ byte req_usage; /* hack to pass a request to getkey() */
+ byte req_algo; /* Ditto */
+ u32 has_expired; /* set to the expiration date if expired */
+ int is_revoked; /* key has been revoked, 1 if by the
+ owner, 2 if by a designated revoker */
+ int maybe_revoked; /* a designated revocation is present, but
+ without the key to check it */
+ int is_valid; /* key (especially subkey) is valid */
+ int dont_cache; /* do not cache this */
+ byte backsig; /* 0=none, 1=bad, 2=good */
+ u32 main_keyid[2]; /* keyid of the primary key */
+ u32 keyid[2]; /* calculated by keyid_from_pk() */
+ byte is_primary;
+ byte is_disabled; /* 0 for unset, 1 for enabled, 2 for disabled. */
+ prefitem_t *prefs; /* list of preferences (may be NULL) */
+ int mdc_feature; /* mdc feature set */
+ PKT_user_id *user_id; /* if != NULL: found by that uid */
+ struct revocation_key *revkey;
+ int numrevkeys;
+ u32 trust_timestamp;
+ byte trust_depth;
+ byte trust_value;
+ const byte *trust_regexp;
+ MPI pkey[PUBKEY_MAX_NPKEY];
+} PKT_public_key;
+
+/* Evaluates as true if the pk is disabled, and false if it isn't. If
+ there is no disable value cached, fill one in. */
+#define pk_is_disabled(a) (((a)->is_disabled)?((a)->is_disabled==2):(cache_disabled_value((a))))
+
+typedef struct {
+ u32 timestamp; /* key made */
+ u32 expiredate; /* expires at this date or 0 if not at all */
+ u32 max_expiredate; /* must not expire past this date */
+ byte hdrbytes; /* number of header bytes */
+ byte version;
+ byte pubkey_algo; /* algorithm used for public key scheme */
+ byte pubkey_usage;
+ byte req_usage;
+ byte req_algo;
+ u32 has_expired; /* set to the expiration date if expired */
+ int is_revoked; /* key has been revoked */
+ int is_valid; /* key (especially subkey) is valid */
+ u32 main_keyid[2]; /* keyid of the primary key */
+ u32 keyid[2];
+ byte is_primary;
+ byte is_protected; /* The secret info is protected and must */
+ /* be decrypted before use, the protected */
+ /* MPIs are simply (void*) pointers to memory */
+ /* and should never be passed to a mpi_xxx() */
+ struct {
+ byte algo; /* cipher used to protect the secret information*/
+ byte sha1chk; /* SHA1 is used instead of a 16 bit checksum */
+ STRING2KEY s2k;
+ byte ivlen; /* used length of the iv */
+ byte iv[16]; /* initialization vector for CFB mode */
+ } protect;
+ MPI skey[PUBKEY_MAX_NSKEY];
+ u16 csum; /* checksum */
+} PKT_secret_key;
+
+
+typedef struct {
+ int len; /* length of data */
+ char data[1];
+} PKT_comment;
+
+typedef struct {
+ u32 len; /* reserved */
+ byte new_ctb;
+ byte algorithm;
+ IOBUF buf; /* IOBUF reference */
+} PKT_compressed;
+
+typedef struct {
+ u32 len; /* length of encrypted data */
+ int extralen; /* this is (blocksize+2) */
+ byte new_ctb; /* uses a new CTB */
+ byte is_partial; /* partial length encoded */
+ byte mdc_method; /* > 0: integrity protected encrypted data packet */
+ IOBUF buf; /* IOBUF reference */
+} PKT_encrypted;
+
+typedef struct {
+ byte hash[20];
+} PKT_mdc;
+
+typedef struct {
+ unsigned int trustval;
+ unsigned int sigcache;
+} PKT_ring_trust;
+
+typedef struct {
+ u32 len; /* length of encrypted data */
+ IOBUF buf; /* IOBUF reference */
+ byte new_ctb;
+ byte is_partial; /* partial length encoded */
+ int mode;
+ u32 timestamp;
+ int namelen;
+ char name[1];
+} PKT_plaintext;
+
+typedef struct {
+ int control;
+ size_t datalen;
+ char data[1];
+} PKT_gpg_control;
+
+/* combine all packets into a union */
+struct packet_struct {
+ pkttype_t pkttype;
+ union {
+ void *generic;
+ PKT_symkey_enc *symkey_enc; /* PKT_SYMKEY_ENC */
+ PKT_pubkey_enc *pubkey_enc; /* PKT_PUBKEY_ENC */
+ PKT_onepass_sig *onepass_sig; /* PKT_ONEPASS_SIG */
+ PKT_signature *signature; /* PKT_SIGNATURE */
+ PKT_public_key *public_key; /* PKT_PUBLIC_[SUB)KEY */
+ PKT_secret_key *secret_key; /* PKT_SECRET_[SUB]KEY */
+ PKT_comment *comment; /* PKT_COMMENT */
+ PKT_user_id *user_id; /* PKT_USER_ID */
+ PKT_compressed *compressed; /* PKT_COMPRESSED */
+ PKT_encrypted *encrypted; /* PKT_ENCRYPTED[_MDC] */
+ PKT_mdc *mdc; /* PKT_MDC */
+ PKT_ring_trust *ring_trust; /* PKT_RING_TRUST */
+ PKT_plaintext *plaintext; /* PKT_PLAINTEXT */
+ PKT_gpg_control *gpg_control; /* PKT_GPG_CONTROL */
+ } pkt;
+};
+
+#define init_packet(a) do { (a)->pkttype = 0; \
+ (a)->pkt.generic = NULL; \
+ } while(0)
+
+typedef enum {
+ SIGSUBPKT_TEST_CRITICAL=-3,
+ SIGSUBPKT_LIST_UNHASHED=-2,
+ SIGSUBPKT_LIST_HASHED =-1,
+ SIGSUBPKT_NONE = 0,
+ SIGSUBPKT_SIG_CREATED = 2, /* signature creation time */
+ SIGSUBPKT_SIG_EXPIRE = 3, /* signature expiration time */
+ SIGSUBPKT_EXPORTABLE = 4, /* exportable */
+ SIGSUBPKT_TRUST = 5, /* trust signature */
+ SIGSUBPKT_REGEXP = 6, /* regular expression */
+ SIGSUBPKT_REVOCABLE = 7, /* revocable */
+ SIGSUBPKT_KEY_EXPIRE = 9, /* key expiration time */
+ SIGSUBPKT_ARR =10, /* additional recipient request */
+ SIGSUBPKT_PREF_SYM =11, /* preferred symmetric algorithms */
+ SIGSUBPKT_REV_KEY =12, /* revocation key */
+ SIGSUBPKT_ISSUER =16, /* issuer key ID */
+ SIGSUBPKT_NOTATION =20, /* notation data */
+ SIGSUBPKT_PREF_HASH =21, /* preferred hash algorithms */
+ SIGSUBPKT_PREF_COMPR =22, /* preferred compression algorithms */
+ SIGSUBPKT_KS_FLAGS =23, /* key server preferences */
+ SIGSUBPKT_PREF_KS =24, /* preferred key server */
+ SIGSUBPKT_PRIMARY_UID =25, /* primary user id */
+ SIGSUBPKT_POLICY =26, /* policy URL */
+ SIGSUBPKT_KEY_FLAGS =27, /* key flags */
+ SIGSUBPKT_SIGNERS_UID =28, /* signer's user id */
+ SIGSUBPKT_REVOC_REASON =29, /* reason for revocation */
+ SIGSUBPKT_FEATURES =30, /* feature flags */
+
+ SIGSUBPKT_SIGNATURE =32, /* embedded signature */
+
+ SIGSUBPKT_FLAG_CRITICAL=128
+} sigsubpkttype_t;
+
+struct notation
+{
+ char *name;
+ char *value;
+ char *altvalue;
+ unsigned char *bdat;
+ size_t blen;
+ struct
+ {
+ unsigned int critical:1;
+ unsigned int ignore:1;
+ } flags;
+ struct notation *next;
+};
+
+/*-- mainproc.c --*/
+int proc_packets( void *ctx, IOBUF a );
+int proc_signature_packets( void *ctx, IOBUF a,
+ STRLIST signedfiles, const char *sigfile );
+int proc_encryption_packets( void *ctx, IOBUF a );
+int list_packets( IOBUF a );
+
+/*-- parse-packet.c --*/
+int set_packet_list_mode( int mode );
+
+#if DEBUG_PARSE_PACKET
+int dbg_search_packet( IOBUF inp, PACKET *pkt, off_t *retpos, int with_uid,
+ const char* file, int lineno );
+int dbg_parse_packet( IOBUF inp, PACKET *ret_pkt,
+ const char* file, int lineno );
+int dbg_copy_all_packets( IOBUF inp, IOBUF out,
+ const char* file, int lineno );
+int dbg_copy_some_packets( IOBUF inp, IOBUF out, off_t stopoff,
+ const char* file, int lineno );
+int dbg_skip_some_packets( IOBUF inp, unsigned n,
+ const char* file, int lineno );
+#define search_packet( a,b,c,d ) \
+ dbg_search_packet( (a), (b), (c), (d), __FILE__, __LINE__ )
+#define parse_packet( a, b ) \
+ dbg_parse_packet( (a), (b), __FILE__, __LINE__ )
+#define copy_all_packets( a,b ) \
+ dbg_copy_all_packets((a),(b), __FILE__, __LINE__ )
+#define copy_some_packets( a,b,c ) \
+ dbg_copy_some_packets((a),(b),(c), __FILE__, __LINE__ )
+#define skip_some_packets( a,b ) \
+ dbg_skip_some_packets((a),(b), __FILE__, __LINE__ )
+#else
+int search_packet( IOBUF inp, PACKET *pkt, off_t *retpos, int with_uid );
+int parse_packet( IOBUF inp, PACKET *ret_pkt);
+int copy_all_packets( IOBUF inp, IOBUF out );
+int copy_some_packets( IOBUF inp, IOBUF out, off_t stopoff );
+int skip_some_packets( IOBUF inp, unsigned n );
+#endif
+
+int parse_signature( IOBUF inp, int pkttype, unsigned long pktlen,
+ PKT_signature *sig );
+const byte *enum_sig_subpkt ( const subpktarea_t *subpkts,
+ sigsubpkttype_t reqtype,
+ size_t *ret_n, int *start, int *critical );
+const byte *parse_sig_subpkt ( const subpktarea_t *buffer,
+ sigsubpkttype_t reqtype,
+ size_t *ret_n );
+const byte *parse_sig_subpkt2 ( PKT_signature *sig,
+ sigsubpkttype_t reqtype,
+ size_t *ret_n );
+int parse_one_sig_subpkt( const byte *buffer, size_t n, int type );
+void parse_revkeys(PKT_signature *sig);
+int parse_attribute_subpkts(PKT_user_id *uid);
+void make_attribute_uidname(PKT_user_id *uid, size_t max_namelen);
+PACKET *create_gpg_control ( ctrlpkttype_t type,
+ const byte *data,
+ size_t datalen );
+
+/*-- build-packet.c --*/
+int build_packet( IOBUF inp, PACKET *pkt );
+u32 calc_packet_length( PACKET *pkt );
+void build_sig_subpkt( PKT_signature *sig, sigsubpkttype_t type,
+ const byte *buffer, size_t buflen );
+void build_sig_subpkt_from_sig( PKT_signature *sig );
+int delete_sig_subpkt(subpktarea_t *buffer, sigsubpkttype_t type );
+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 *sig_to_notation(PKT_signature *sig);
+void free_notation(struct notation *notation);
+
+/*-- free-packet.c --*/
+void free_symkey_enc( PKT_symkey_enc *enc );
+void free_pubkey_enc( PKT_pubkey_enc *enc );
+void free_seckey_enc( PKT_signature *enc );
+int digest_algo_from_sig( PKT_signature *sig );
+void release_public_key_parts( PKT_public_key *pk );
+void free_public_key( PKT_public_key *key );
+void release_secret_key_parts( PKT_secret_key *sk );
+void free_secret_key( PKT_secret_key *sk );
+void free_attributes(PKT_user_id *uid);
+void free_user_id( PKT_user_id *uid );
+void free_comment( PKT_comment *rem );
+void free_packet( PACKET *pkt );
+prefitem_t *copy_prefs (const prefitem_t *prefs);
+PKT_public_key *copy_public_key( PKT_public_key *d, PKT_public_key *s );
+void copy_public_parts_to_secret_key( PKT_public_key *pk, PKT_secret_key *sk );
+PKT_secret_key *copy_secret_key( PKT_secret_key *d, PKT_secret_key *s );
+PKT_signature *copy_signature( PKT_signature *d, PKT_signature *s );
+PKT_user_id *scopy_user_id (PKT_user_id *sd );
+int cmp_public_keys( PKT_public_key *a, PKT_public_key *b );
+int cmp_secret_keys( PKT_secret_key *a, PKT_secret_key *b );
+int cmp_signatures( PKT_signature *a, PKT_signature *b );
+int cmp_public_secret_key( PKT_public_key *pk, PKT_secret_key *sk );
+int cmp_user_ids( PKT_user_id *a, PKT_user_id *b );
+
+
+/*-- sig-check.c --*/
+int signature_check( PKT_signature *sig, MD_HANDLE digest );
+int signature_check2( PKT_signature *sig, MD_HANDLE digest, u32 *r_expiredate,
+ int *r_expired, int *r_revoked, PKT_public_key *ret_pk );
+
+/*-- seckey-cert.c --*/
+int is_secret_key_protected( PKT_secret_key *sk );
+int check_secret_key( PKT_secret_key *sk, int retries );
+int protect_secret_key( PKT_secret_key *sk, DEK *dek );
+
+/*-- pubkey-enc.c --*/
+int get_session_key( PKT_pubkey_enc *k, DEK *dek );
+int get_override_session_key( DEK *dek, const char *string );
+
+/*-- compress.c --*/
+int handle_compressed( void *ctx, PKT_compressed *cd,
+ int (*callback)(IOBUF, void *), void *passthru );
+
+/*-- encr-data.c --*/
+int decrypt_data( void *ctx, PKT_encrypted *ed, DEK *dek );
+
+/*-- plaintext.c --*/
+int handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx,
+ int nooutput, int clearsig );
+int ask_for_detached_datafile( MD_HANDLE md, MD_HANDLE md2,
+ const char *inname, int textmode );
+
+/*-- sign.c --*/
+int make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk,
+ PKT_user_id *uid, PKT_public_key *subpk,
+ PKT_secret_key *sk, int sigclass, int digest_algo,
+ int sigversion, u32 timestamp, u32 duration,
+ int (*mksubpkt)(PKT_signature *, void *),
+ void *opaque );
+int update_keysig_packet( PKT_signature **ret_sig,
+ PKT_signature *orig_sig,
+ PKT_public_key *pk,
+ PKT_user_id *uid,
+ PKT_public_key *subpk,
+ PKT_secret_key *sk,
+ int (*mksubpkt)(PKT_signature *, void *),
+ void *opaque );
+
+/*-- keygen.c --*/
+PKT_user_id *generate_user_id(void);
+
+#endif /*G10_PACKET_H*/
diff --git a/g10/parse-packet.c b/g10/parse-packet.c
new file mode 100644
index 0000000..31f21bc
--- /dev/null
+++ b/g10/parse-packet.c
@@ -0,0 +1,2411 @@
+/* parse-packet.c - read packets
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#include "packet.h"
+#include "iobuf.h"
+#include "mpi.h"
+#include "util.h"
+#include "cipher.h"
+#include "memory.h"
+#include "filter.h"
+#include "photoid.h"
+#include "options.h"
+#include "main.h"
+#include "i18n.h"
+
+static int mpi_print_mode;
+static int list_mode;
+static FILE *listfp;
+
+static int parse( IOBUF inp, PACKET *pkt, int onlykeypkts,
+ off_t *retpos, int *skip, IOBUF out, int do_skip
+#ifdef DEBUG_PARSE_PACKET
+ ,const char *dbg_w, const char *dbg_f, int dbg_l
+#endif
+ );
+static int copy_packet( IOBUF inp, IOBUF out, int pkttype,
+ unsigned long pktlen, int partial );
+static void skip_packet( IOBUF inp, int pkttype,
+ unsigned long pktlen, int partial );
+static void *read_rest( IOBUF inp, size_t pktlen, int partial );
+static int parse_symkeyenc( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *packet );
+static int parse_pubkeyenc( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *packet );
+static int parse_onepass_sig( IOBUF inp, int pkttype, unsigned long pktlen,
+ PKT_onepass_sig *ops );
+static int parse_key( IOBUF inp, int pkttype, unsigned long pktlen,
+ byte *hdr, int hdrlen, PACKET *packet );
+static int parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *packet );
+static int parse_attribute( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *packet );
+static int parse_comment( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *packet );
+static void parse_trust( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *packet );
+static int parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *packet, int new_ctb, int partial);
+static int parse_compressed( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *packet, int new_ctb );
+static int parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *packet, int new_ctb, int partial);
+static int parse_mdc( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *packet, int new_ctb);
+static int parse_gpg_control( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *packet, int partial );
+
+static unsigned short
+read_16(IOBUF inp)
+{
+ unsigned short a;
+ a = iobuf_get_noeof(inp) << 8;
+ a |= iobuf_get_noeof(inp);
+ return a;
+}
+
+static unsigned long
+read_32(IOBUF inp)
+{
+ unsigned long a;
+ a = iobuf_get_noeof(inp) << 24;
+ a |= iobuf_get_noeof(inp) << 16;
+ a |= iobuf_get_noeof(inp) << 8;
+ a |= iobuf_get_noeof(inp);
+ return a;
+}
+
+
+int
+set_packet_list_mode( int mode )
+{
+ int old = list_mode;
+ list_mode = mode;
+ mpi_print_mode = DBG_MPI;
+ /* We use stdout print only if invoked by the --list-packets
+ command but switch to stderr in all otehr cases. This breaks
+ the previous behaviour but that seems to be more of a bug than
+ intentional. I don't believe that any application makes use of
+ this long standing annoying way of printing to stdout except
+ when doing a --list-packets. If this assumption fails, it will
+ be easy to add an option for the listing stream. Note that we
+ initialize it only once; mainly because some code may switch
+ the option value later back to 1 and we want to have all output
+ to the same stream.
+
+ Using stderr is not actually very clean because it bypasses the
+ logging code but it is a special thing anyay. I am not sure
+ whether using log_stream() would be better. Perhaps we should
+ enable the list mdoe only with a special option. */
+ if (!listfp)
+ listfp = opt.list_packets == 2 ? stdout : stderr;
+ return old;
+}
+
+static void
+unknown_pubkey_warning( int algo )
+{
+ static byte unknown_pubkey_algos[256];
+
+ algo &= 0xff;
+ if( !unknown_pubkey_algos[algo] ) {
+ if( opt.verbose )
+ log_info(_("can't handle public key algorithm %d\n"), algo );
+ unknown_pubkey_algos[algo] = 1;
+ }
+}
+
+/****************
+ * Parse a Packet and return it in packet
+ * Returns: 0 := valid packet in pkt
+ * -1 := no more packets
+ * >0 := error
+ * Note: The function may return an error and a partly valid packet;
+ * caller must free this packet.
+ */
+#ifdef DEBUG_PARSE_PACKET
+int
+dbg_parse_packet( IOBUF inp, PACKET *pkt, const char *dbg_f, int dbg_l )
+{
+ int skip, rc;
+
+ do {
+ rc = parse( inp, pkt, 0, NULL, &skip, NULL, 0, "parse", dbg_f, dbg_l );
+ } while( skip );
+ return rc;
+}
+#else
+int
+parse_packet( IOBUF inp, PACKET *pkt )
+{
+ int skip, rc;
+
+ do {
+ rc = parse( inp, pkt, 0, NULL, &skip, NULL, 0 );
+ } while( skip );
+ return rc;
+}
+#endif
+
+/****************
+ * Like parse packet, but only return secret or public (sub)key packets.
+ */
+#ifdef DEBUG_PARSE_PACKET
+int
+dbg_search_packet( IOBUF inp, PACKET *pkt, off_t *retpos, int with_uid,
+ const char *dbg_f, int dbg_l )
+{
+ int skip, rc;
+
+ do {
+ rc = parse( inp, pkt, with_uid?2:1, retpos, &skip, NULL, 0, "search", dbg_f, dbg_l );
+ } while( skip );
+ return rc;
+}
+#else
+int
+search_packet( IOBUF inp, PACKET *pkt, off_t *retpos, int with_uid )
+{
+ int skip, rc;
+
+ do {
+ rc = parse( inp, pkt, with_uid?2:1, retpos, &skip, NULL, 0 );
+ } while( skip );
+ return rc;
+}
+#endif
+
+/****************
+ * Copy all packets from INP to OUT, thereby removing unused spaces.
+ */
+#ifdef DEBUG_PARSE_PACKET
+int
+dbg_copy_all_packets( IOBUF inp, IOBUF out,
+ const char *dbg_f, int dbg_l )
+{
+ PACKET pkt;
+ int skip, rc=0;
+ do {
+ init_packet(&pkt);
+ } while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0, "copy", dbg_f, dbg_l )));
+ return rc;
+}
+#else
+int
+copy_all_packets( IOBUF inp, IOBUF out )
+{
+ PACKET pkt;
+ int skip, rc=0;
+ do {
+ init_packet(&pkt);
+ } while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0 )));
+ return rc;
+}
+#endif
+
+/****************
+ * Copy some packets from INP to OUT, thereby removing unused spaces.
+ * Stop at offset STOPoff (i.e. don't copy packets at this or later offsets)
+ */
+#ifdef DEBUG_PARSE_PACKET
+int
+dbg_copy_some_packets( IOBUF inp, IOBUF out, off_t stopoff,
+ const char *dbg_f, int dbg_l )
+{
+ PACKET pkt;
+ int skip, rc=0;
+ do {
+ if( iobuf_tell(inp) >= stopoff )
+ return 0;
+ init_packet(&pkt);
+ } while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0,
+ "some", dbg_f, dbg_l )) );
+ return rc;
+}
+#else
+int
+copy_some_packets( IOBUF inp, IOBUF out, off_t stopoff )
+{
+ PACKET pkt;
+ int skip, rc=0;
+ do {
+ if( iobuf_tell(inp) >= stopoff )
+ return 0;
+ init_packet(&pkt);
+ } while( !(rc = parse( inp, &pkt, 0, NULL, &skip, out, 0 )) );
+ return rc;
+}
+#endif
+
+/****************
+ * Skip over N packets
+ */
+#ifdef DEBUG_PARSE_PACKET
+int
+dbg_skip_some_packets( IOBUF inp, unsigned n,
+ const char *dbg_f, int dbg_l )
+{
+ int skip, rc=0;
+ PACKET pkt;
+
+ for( ;n && !rc; n--) {
+ init_packet(&pkt);
+ rc = parse( inp, &pkt, 0, NULL, &skip, NULL, 1, "skip", dbg_f, dbg_l );
+ }
+ return rc;
+}
+#else
+int
+skip_some_packets( IOBUF inp, unsigned n )
+{
+ int skip, rc=0;
+ PACKET pkt;
+
+ for( ;n && !rc; n--) {
+ init_packet(&pkt);
+ rc = parse( inp, &pkt, 0, NULL, &skip, NULL, 1 );
+ }
+ return rc;
+}
+#endif
+
+
+/****************
+ * Parse packet. Set the variable skip points to 1 if the packet
+ * should be skipped; this is the case if either ONLYKEYPKTS is set
+ * and the parsed packet isn't one or the
+ * packet-type is 0, indicating deleted stuff.
+ * if OUT is not NULL, a special copymode is used.
+ */
+static int
+parse( IOBUF inp, PACKET *pkt, int onlykeypkts, off_t *retpos,
+ int *skip, IOBUF out, int do_skip
+#ifdef DEBUG_PARSE_PACKET
+ ,const char *dbg_w, const char *dbg_f, int dbg_l
+#endif
+ )
+{
+ int rc=0, c, ctb, pkttype, lenbytes;
+ unsigned long pktlen;
+ byte hdr[8];
+ int hdrlen;
+ int new_ctb = 0, partial=0;
+ int with_uid = (onlykeypkts == 2);
+
+ *skip = 0;
+ assert( !pkt->pkt.generic );
+ if( retpos )
+ *retpos = iobuf_tell(inp);
+
+ if( (ctb = iobuf_get(inp)) == -1 ) {
+ rc = -1;
+ goto leave;
+ }
+ hdrlen=0;
+ hdr[hdrlen++] = ctb;
+ if( !(ctb & 0x80) ) {
+ log_error("%s: invalid packet (ctb=%02x)\n", iobuf_where(inp), ctb );
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ pktlen = 0;
+ new_ctb = !!(ctb & 0x40);
+ if( new_ctb ) {
+ pkttype = ctb & 0x3f;
+ if( (c = iobuf_get(inp)) == -1 ) {
+ log_error("%s: 1st length byte missing\n", iobuf_where(inp) );
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ if (pkttype == PKT_COMPRESSED) {
+ iobuf_set_partial_block_mode(inp, c & 0xff);
+ pktlen = 0;/* to indicate partial length */
+ partial=1;
+ }
+ else {
+ hdr[hdrlen++] = c;
+ if( c < 192 )
+ pktlen = c;
+ else if( c < 224 )
+ {
+ pktlen = (c - 192) * 256;
+ if( (c = iobuf_get(inp)) == -1 )
+ {
+ log_error("%s: 2nd length byte missing\n",
+ iobuf_where(inp) );
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ hdr[hdrlen++] = c;
+ pktlen += c + 192;
+ }
+ else if( c == 255 )
+ {
+ pktlen = (hdr[hdrlen++] = iobuf_get_noeof(inp)) << 24;
+ pktlen |= (hdr[hdrlen++] = iobuf_get_noeof(inp)) << 16;
+ pktlen |= (hdr[hdrlen++] = iobuf_get_noeof(inp)) << 8;
+ if( (c = iobuf_get(inp)) == -1 )
+ {
+ log_error("%s: 4 byte length invalid\n",
+ iobuf_where(inp) );
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ pktlen |= (hdr[hdrlen++] = c );
+ }
+ else
+ {
+ /* Partial body length. Note that we handled
+ PKT_COMPRESSED earlier. */
+ if(pkttype==PKT_PLAINTEXT || pkttype==PKT_ENCRYPTED
+ || pkttype==PKT_ENCRYPTED_MDC)
+ {
+ iobuf_set_partial_block_mode(inp, c & 0xff);
+ pktlen = 0;/* to indicate partial length */
+ partial=1;
+ }
+ else
+ {
+ log_error("%s: partial length for invalid"
+ " packet type %d\n",iobuf_where(inp),pkttype);
+ rc=G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ }
+ }
+ }
+ else
+ {
+ pkttype = (ctb>>2)&0xf;
+ lenbytes = ((ctb&3)==3)? 0 : (1<<(ctb & 3));
+ if( !lenbytes )
+ {
+ pktlen = 0; /* don't know the value */
+ /* This isn't really partial, but we can treat it the same
+ in a "read until the end" sort of way. */
+ partial=1;
+ if(pkttype!=PKT_ENCRYPTED && pkttype!=PKT_PLAINTEXT
+ && pkttype!=PKT_COMPRESSED)
+ {
+ log_error ("%s: indeterminate length for invalid"
+ " packet type %d\n", iobuf_where(inp), pkttype );
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ }
+ else
+ {
+ for( ; lenbytes; lenbytes-- )
+ {
+ pktlen <<= 8;
+ pktlen |= hdr[hdrlen++] = iobuf_get_noeof(inp);
+ }
+ }
+ }
+
+ if (pktlen == 0xffffffff) {
+ /* with a some probability this is caused by a problem in the
+ * the uncompressing layer - in some error cases it just loops
+ * and spits out 0xff bytes. */
+ log_error ("%s: garbled packet detected\n", iobuf_where(inp) );
+ g10_exit (2);
+ }
+
+ if( out && pkttype ) {
+ if( iobuf_write( out, hdr, hdrlen ) == -1 )
+ rc = G10ERR_WRITE_FILE;
+ else
+ rc = copy_packet(inp, out, pkttype, pktlen, partial );
+ goto leave;
+ }
+
+ if (with_uid && pkttype == PKT_USER_ID)
+ ;
+ else if( do_skip
+ || !pkttype
+ || (onlykeypkts && pkttype != PKT_PUBLIC_SUBKEY
+ && pkttype != PKT_PUBLIC_KEY
+ && pkttype != PKT_SECRET_SUBKEY
+ && pkttype != PKT_SECRET_KEY ) ) {
+ iobuf_skip_rest(inp, pktlen, partial);
+ *skip = 1;
+ rc = 0;
+ goto leave;
+ }
+
+ if( DBG_PACKET ) {
+#ifdef DEBUG_PARSE_PACKET
+ log_debug("parse_packet(iob=%d): type=%d length=%lu%s (%s.%s.%d)\n",
+ iobuf_id(inp), pkttype, pktlen, new_ctb?" (new_ctb)":"",
+ dbg_w, dbg_f, dbg_l );
+#else
+ log_debug("parse_packet(iob=%d): type=%d length=%lu%s\n",
+ iobuf_id(inp), pkttype, pktlen, new_ctb?" (new_ctb)":"" );
+#endif
+ }
+ pkt->pkttype = pkttype;
+ rc = G10ERR_UNKNOWN_PACKET; /* default error */
+ switch( pkttype ) {
+ case PKT_PUBLIC_KEY:
+ case PKT_PUBLIC_SUBKEY:
+ pkt->pkt.public_key = xmalloc_clear(sizeof *pkt->pkt.public_key );
+ rc = parse_key(inp, pkttype, pktlen, hdr, hdrlen, pkt );
+ break;
+ case PKT_SECRET_KEY:
+ case PKT_SECRET_SUBKEY:
+ pkt->pkt.secret_key = xmalloc_clear(sizeof *pkt->pkt.secret_key );
+ rc = parse_key(inp, pkttype, pktlen, hdr, hdrlen, pkt );
+ break;
+ case PKT_SYMKEY_ENC:
+ rc = parse_symkeyenc( inp, pkttype, pktlen, pkt );
+ break;
+ case PKT_PUBKEY_ENC:
+ rc = parse_pubkeyenc(inp, pkttype, pktlen, pkt );
+ break;
+ case PKT_SIGNATURE:
+ pkt->pkt.signature = xmalloc_clear(sizeof *pkt->pkt.signature );
+ rc = parse_signature(inp, pkttype, pktlen, pkt->pkt.signature );
+ break;
+ case PKT_ONEPASS_SIG:
+ pkt->pkt.onepass_sig = xmalloc_clear(sizeof *pkt->pkt.onepass_sig );
+ rc = parse_onepass_sig(inp, pkttype, pktlen, pkt->pkt.onepass_sig );
+ break;
+ case PKT_USER_ID:
+ rc = parse_user_id(inp, pkttype, pktlen, pkt );
+ break;
+ case PKT_ATTRIBUTE:
+ pkt->pkttype = pkttype = PKT_USER_ID; /* we store it in the userID */
+ rc = parse_attribute(inp, pkttype, pktlen, pkt);
+ break;
+ case PKT_OLD_COMMENT:
+ case PKT_COMMENT:
+ rc = parse_comment(inp, pkttype, pktlen, pkt);
+ break;
+ case PKT_RING_TRUST:
+ parse_trust(inp, pkttype, pktlen, pkt);
+ rc = 0;
+ break;
+ case PKT_PLAINTEXT:
+ rc = parse_plaintext(inp, pkttype, pktlen, pkt, new_ctb, partial );
+ break;
+ case PKT_COMPRESSED:
+ rc = parse_compressed(inp, pkttype, pktlen, pkt, new_ctb );
+ break;
+ case PKT_ENCRYPTED:
+ case PKT_ENCRYPTED_MDC:
+ rc = parse_encrypted(inp, pkttype, pktlen, pkt, new_ctb, partial );
+ break;
+ case PKT_MDC:
+ rc = parse_mdc(inp, pkttype, pktlen, pkt, new_ctb );
+ break;
+ case PKT_GPG_CONTROL:
+ rc = parse_gpg_control(inp, pkttype, pktlen, pkt, partial );
+ break;
+ default:
+ skip_packet(inp, pkttype, pktlen, partial);
+ break;
+ }
+
+ leave:
+ if( !rc && iobuf_error(inp) )
+ rc = G10ERR_INV_KEYRING;
+ return rc;
+}
+
+static void
+dump_hex_line( int c, int *i )
+{
+ if( *i && !(*i%8) ) {
+ if( *i && !(*i%24) )
+ fprintf (listfp, "\n%4d:", *i );
+ else
+ putc (' ', listfp);
+ }
+ if( c == -1 )
+ fprintf (listfp, " EOF" );
+ else
+ fprintf (listfp, " %02x", c );
+ ++*i;
+}
+
+
+static int
+copy_packet( IOBUF inp, IOBUF out, int pkttype,
+ unsigned long pktlen, int partial )
+{
+ int n;
+ char buf[100];
+
+ if( partial ) {
+ while( (n = iobuf_read( inp, buf, 100 )) != -1 )
+ if( iobuf_write(out, buf, n ) )
+ return G10ERR_WRITE_FILE; /* write error */
+ }
+ else if( !pktlen && pkttype == PKT_COMPRESSED ) {
+ log_debug("copy_packet: compressed!\n");
+ /* compressed packet, copy till EOF */
+ while( (n = iobuf_read( inp, buf, 100 )) != -1 )
+ if( iobuf_write(out, buf, n ) )
+ return G10ERR_WRITE_FILE; /* write error */
+ }
+ else {
+ for( ; pktlen; pktlen -= n ) {
+ n = pktlen > 100 ? 100 : pktlen;
+ n = iobuf_read( inp, buf, n );
+ if( n == -1 )
+ return G10ERR_READ_FILE;
+ if( iobuf_write(out, buf, n ) )
+ return G10ERR_WRITE_FILE; /* write error */
+ }
+ }
+ return 0;
+}
+
+
+static void
+skip_packet( IOBUF inp, int pkttype, unsigned long pktlen, int partial )
+{
+ if( list_mode ) {
+ if( pkttype == PKT_MARKER )
+ fputs(":marker packet:\n", listfp );
+ else
+ fprintf (listfp, ":unknown packet: type %2d, length %lu\n",
+ pkttype, pktlen);
+ if( pkttype ) {
+ int c, i=0 ;
+ if( pkttype != PKT_MARKER )
+ fputs("dump:", listfp );
+ if( partial ) {
+ while( (c=iobuf_get(inp)) != -1 )
+ dump_hex_line(c, &i);
+ }
+ else {
+ for( ; pktlen; pktlen-- )
+ dump_hex_line(iobuf_get(inp), &i);
+ }
+ putc ('\n', listfp);
+ return;
+ }
+ }
+ iobuf_skip_rest(inp,pktlen,partial);
+}
+
+
+static void *
+read_rest( IOBUF inp, size_t pktlen, int partial )
+{
+ byte *p;
+ int i;
+
+ if( partial ) {
+ log_error("read_rest: can't store stream data\n");
+ p = NULL;
+ }
+ else {
+ p = xmalloc( pktlen );
+ for(i=0; pktlen; pktlen--, i++ )
+ p[i] = iobuf_get(inp);
+ }
+ return p;
+}
+
+
+
+static int
+parse_symkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
+{
+ PKT_symkey_enc *k;
+ int rc = 0;
+ int i, version, s2kmode, cipher_algo, hash_algo, seskeylen, minlen;
+
+ if( pktlen < 4 ) {
+ log_error("packet(%d) too short\n", pkttype);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ version = iobuf_get_noeof(inp); pktlen--;
+ if( version != 4 ) {
+ log_error("packet(%d) with unknown version %d\n", pkttype, version);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ if( pktlen > 200 ) { /* (we encode the seskeylen in a byte) */
+ log_error("packet(%d) too large\n", pkttype);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ cipher_algo = iobuf_get_noeof(inp); pktlen--;
+ s2kmode = iobuf_get_noeof(inp); pktlen--;
+ hash_algo = iobuf_get_noeof(inp); pktlen--;
+ switch( s2kmode ) {
+ case 0: /* simple s2k */
+ minlen = 0;
+ break;
+ case 1: /* salted s2k */
+ minlen = 8;
+ break;
+ case 3: /* iterated+salted s2k */
+ minlen = 9;
+ break;
+ default:
+ log_error("unknown S2K %d\n", s2kmode );
+ goto leave;
+ }
+ if( minlen > pktlen ) {
+ log_error("packet with S2K %d too short\n", s2kmode );
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ seskeylen = pktlen - minlen;
+ k = packet->pkt.symkey_enc = xmalloc_clear( sizeof *packet->pkt.symkey_enc
+ + seskeylen - 1 );
+ k->version = version;
+ k->cipher_algo = cipher_algo;
+ k->s2k.mode = s2kmode;
+ k->s2k.hash_algo = hash_algo;
+ if( s2kmode == 1 || s2kmode == 3 ) {
+ for(i=0; i < 8 && pktlen; i++, pktlen-- )
+ k->s2k.salt[i] = iobuf_get_noeof(inp);
+ }
+ if( s2kmode == 3 ) {
+ k->s2k.count = iobuf_get(inp); pktlen--;
+ }
+ k->seskeylen = seskeylen;
+ if(k->seskeylen)
+ {
+ for(i=0; i < seskeylen && pktlen; i++, pktlen-- )
+ k->seskey[i] = iobuf_get_noeof(inp);
+
+ /* What we're watching out for here is a session key decryptor
+ with no salt. The RFC says that using salt for this is a
+ MUST. */
+ if(s2kmode!=1 && s2kmode!=3)
+ log_info(_("WARNING: potentially insecure symmetrically"
+ " encrypted session key\n"));
+ }
+ assert( !pktlen );
+
+ if( list_mode ) {
+ fprintf (listfp, ":symkey enc packet: version %d, cipher %d, s2k %d, hash %d",
+ version, cipher_algo, s2kmode, hash_algo);
+ if(seskeylen)
+ fprintf (listfp, ", seskey %d bits",(seskeylen-1)*8);
+ fprintf (listfp, "\n");
+ if( s2kmode == 1 || s2kmode == 3 ) {
+ fprintf (listfp, "\tsalt ");
+ for(i=0; i < 8; i++ )
+ fprintf (listfp, "%02x", k->s2k.salt[i]);
+ if( s2kmode == 3 )
+ fprintf (listfp, ", count %lu (%lu)",
+ S2K_DECODE_COUNT((ulong)k->s2k.count),
+ (ulong)k->s2k.count );
+ fprintf (listfp, "\n");
+ }
+ }
+
+ leave:
+ iobuf_skip_rest(inp, pktlen, 0);
+ return rc;
+}
+
+static int
+parse_pubkeyenc( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
+{
+ unsigned int n;
+ int rc = 0;
+ int i, ndata;
+ PKT_pubkey_enc *k;
+
+ k = packet->pkt.pubkey_enc = xmalloc_clear(sizeof *packet->pkt.pubkey_enc);
+ if( pktlen < 12 ) {
+ log_error("packet(%d) too short\n", pkttype);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ k->version = iobuf_get_noeof(inp); pktlen--;
+ if( k->version != 2 && k->version != 3 ) {
+ log_error("packet(%d) with unknown version %d\n", pkttype, k->version);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ k->keyid[0] = read_32(inp); pktlen -= 4;
+ k->keyid[1] = read_32(inp); pktlen -= 4;
+ k->pubkey_algo = iobuf_get_noeof(inp); pktlen--;
+ k->throw_keyid = 0; /* only used as flag for build_packet */
+ if( list_mode )
+ fprintf (listfp, ":pubkey enc packet: version %d, algo %d, keyid %08lX%08lX\n",
+ k->version, k->pubkey_algo, (ulong)k->keyid[0], (ulong)k->keyid[1]);
+
+ ndata = pubkey_get_nenc(k->pubkey_algo);
+ if( !ndata ) {
+ if( list_mode )
+ fprintf (listfp, "\tunsupported algorithm %d\n", k->pubkey_algo );
+ unknown_pubkey_warning( k->pubkey_algo );
+ k->data[0] = NULL; /* no need to store the encrypted data */
+ }
+ else {
+ for( i=0; i < ndata; i++ ) {
+ n = pktlen;
+ k->data[i] = mpi_read(inp, &n, 0); pktlen -=n;
+ if( list_mode ) {
+ fprintf (listfp, "\tdata: ");
+ mpi_print(listfp, k->data[i], mpi_print_mode );
+ putc ('\n', listfp);
+ }
+ if (!k->data[i])
+ rc = G10ERR_INVALID_PACKET;
+ }
+ }
+
+ leave:
+ iobuf_skip_rest(inp, pktlen, 0);
+ return rc;
+}
+
+
+static void
+dump_sig_subpkt( int hashed, int type, int critical,
+ const byte *buffer, size_t buflen, size_t length )
+{
+ const char *p=NULL;
+ int i;
+
+ /* The CERT has warning out with explains how to use GNUPG to
+ * detect the ARRs - we print our old message here when it is a faked
+ * ARR and add an additional notice */
+ if ( type == SIGSUBPKT_ARR && !hashed ) {
+ fprintf (listfp,
+ "\tsubpkt %d len %u (additional recipient request)\n"
+ "WARNING: PGP versions > 5.0 and < 6.5.8 will automagically "
+ "encrypt to this key and thereby reveal the plaintext to "
+ "the owner of this ARR key. Detailed info follows:\n",
+ type, (unsigned)length );
+ }
+
+ buffer++;
+ length--;
+
+ fprintf (listfp, "\t%s%ssubpkt %d len %u (", /*)*/
+ critical ? "critical ":"",
+ hashed ? "hashed ":"", type, (unsigned)length );
+ if( length > buflen ) {
+ fprintf (listfp, "too short: buffer is only %u)\n", (unsigned)buflen );
+ return;
+ }
+ switch( type ) {
+ case SIGSUBPKT_SIG_CREATED:
+ if( length >= 4 )
+ fprintf (listfp, "sig created %s", strtimestamp( buffer_to_u32(buffer) ) );
+ break;
+ case SIGSUBPKT_SIG_EXPIRE:
+ if( length >= 4 )
+ {
+ if(buffer_to_u32(buffer))
+ fprintf (listfp, "sig expires after %s",
+ strtimevalue( buffer_to_u32(buffer) ) );
+ else
+ fprintf (listfp, "sig does not expire");
+ }
+ break;
+ case SIGSUBPKT_EXPORTABLE:
+ if( length )
+ fprintf (listfp, "%sexportable", *buffer? "":"not ");
+ break;
+ case SIGSUBPKT_TRUST:
+ if(length!=2)
+ p="[invalid trust subpacket]";
+ else
+ fprintf (listfp, "trust signature of depth %d, value %d",buffer[0],buffer[1]);
+ break;
+ case SIGSUBPKT_REGEXP:
+ if(!length)
+ p="[invalid regexp subpacket]";
+ else
+ fprintf (listfp, "regular expression: \"%s\"",buffer);
+ break;
+ case SIGSUBPKT_REVOCABLE:
+ if( length )
+ fprintf (listfp, "%srevocable", *buffer? "":"not ");
+ break;
+ case SIGSUBPKT_KEY_EXPIRE:
+ if( length >= 4 )
+ {
+ if(buffer_to_u32(buffer))
+ fprintf (listfp, "key expires after %s",
+ strtimevalue( buffer_to_u32(buffer) ) );
+ else
+ fprintf (listfp, "key does not expire");
+ }
+ break;
+ case SIGSUBPKT_PREF_SYM:
+ fputs("pref-sym-algos:", listfp );
+ for( i=0; i < length; i++ )
+ fprintf (listfp, " %d", buffer[i] );
+ break;
+ case SIGSUBPKT_REV_KEY:
+ fputs("revocation key: ", listfp );
+ if( length < 22 )
+ p = "[too short]";
+ else {
+ fprintf (listfp, "c=%02x a=%d f=", buffer[0], buffer[1] );
+ for( i=2; i < length; i++ )
+ fprintf (listfp, "%02X", buffer[i] );
+ }
+ break;
+ case SIGSUBPKT_ISSUER:
+ if( length >= 8 )
+ fprintf (listfp, "issuer key ID %08lX%08lX",
+ (ulong)buffer_to_u32(buffer),
+ (ulong)buffer_to_u32(buffer+4) );
+ break;
+ case SIGSUBPKT_NOTATION:
+ {
+ fputs("notation: ", listfp );
+ if( length < 8 )
+ p = "[too short]";
+ else {
+ const byte *s = buffer;
+ size_t n1, n2;
+
+ n1 = (s[4] << 8) | s[5];
+ n2 = (s[6] << 8) | s[7];
+ s += 8;
+ if( 8+n1+n2 != length )
+ p = "[error]";
+ else {
+ print_string( listfp, s, n1, ')' );
+ putc( '=', listfp );
+
+ if( *buffer & 0x80 )
+ print_string( listfp, s+n1, n2, ')' );
+ else
+ p = "[not human readable]";
+ }
+ }
+ }
+ break;
+ case SIGSUBPKT_PREF_HASH:
+ fputs("pref-hash-algos:", listfp );
+ for( i=0; i < length; i++ )
+ fprintf (listfp, " %d", buffer[i] );
+ break;
+ case SIGSUBPKT_PREF_COMPR:
+ fputs("pref-zip-algos:", listfp );
+ for( i=0; i < length; i++ )
+ fprintf (listfp, " %d", buffer[i] );
+ break;
+ case SIGSUBPKT_KS_FLAGS:
+ fputs("key server preferences:",listfp);
+ for(i=0;i<length;i++)
+ fprintf (listfp, " %02X", buffer[i]);
+ break;
+ case SIGSUBPKT_PREF_KS:
+ fputs("preferred key server: ", listfp );
+ print_string( listfp, buffer, length, ')' );
+ break;
+ case SIGSUBPKT_PRIMARY_UID:
+ p = "primary user ID";
+ break;
+ case SIGSUBPKT_POLICY:
+ fputs("policy: ", listfp );
+ print_string( listfp, buffer, length, ')' );
+ break;
+ case SIGSUBPKT_KEY_FLAGS:
+ fputs ( "key flags:", listfp );
+ for( i=0; i < length; i++ )
+ fprintf (listfp, " %02X", buffer[i] );
+ break;
+ case SIGSUBPKT_SIGNERS_UID:
+ p = "signer's user ID";
+ break;
+ case SIGSUBPKT_REVOC_REASON:
+ if( length ) {
+ fprintf (listfp, "revocation reason 0x%02x (", *buffer );
+ print_string( listfp, buffer+1, length-1, ')' );
+ p = ")";
+ }
+ break;
+ case SIGSUBPKT_ARR:
+ fputs("Big Brother's key (ignored): ", listfp );
+ if( length < 22 )
+ p = "[too short]";
+ else {
+ fprintf (listfp, "c=%02x a=%d f=", buffer[0], buffer[1] );
+ for( i=2; i < length; i++ )
+ fprintf (listfp, "%02X", buffer[i] );
+ }
+ break;
+ case SIGSUBPKT_FEATURES:
+ fputs ( "features:", listfp );
+ for( i=0; i < length; i++ )
+ fprintf (listfp, " %02x", buffer[i] );
+ break;
+ case SIGSUBPKT_SIGNATURE:
+ fputs("signature: ",listfp);
+ if(length<17)
+ p="[too short]";
+ else
+ fprintf (listfp, "v%d, class 0x%02X, algo %d, digest algo %d",
+ buffer[0],
+ buffer[0]==3?buffer[2]:buffer[1],
+ buffer[0]==3?buffer[15]:buffer[2],
+ buffer[0]==3?buffer[16]:buffer[3]);
+ break;
+ default:
+ if(type>=100 && type<=110)
+ p="experimental / private subpacket";
+ else
+ p = "?";
+ break;
+ }
+
+ fprintf (listfp, "%s)\n", p? p: "");
+}
+
+/****************
+ * Returns: >= 0 use this offset into buffer
+ * -1 explicitly reject returning this type
+ * -2 subpacket too short
+ */
+int
+parse_one_sig_subpkt( const byte *buffer, size_t n, int type )
+{
+ switch( type )
+ {
+ case SIGSUBPKT_REV_KEY:
+ if(n < 22)
+ break;
+ return 0;
+ case SIGSUBPKT_SIG_CREATED:
+ case SIGSUBPKT_SIG_EXPIRE:
+ case SIGSUBPKT_KEY_EXPIRE:
+ if( n < 4 )
+ break;
+ return 0;
+ case SIGSUBPKT_KEY_FLAGS:
+ case SIGSUBPKT_KS_FLAGS:
+ case SIGSUBPKT_PREF_SYM:
+ case SIGSUBPKT_PREF_HASH:
+ case SIGSUBPKT_PREF_COMPR:
+ case SIGSUBPKT_POLICY:
+ case SIGSUBPKT_PREF_KS:
+ case SIGSUBPKT_FEATURES:
+ case SIGSUBPKT_REGEXP:
+ return 0;
+ case SIGSUBPKT_SIGNATURE:
+ case SIGSUBPKT_EXPORTABLE:
+ case SIGSUBPKT_REVOCABLE:
+ case SIGSUBPKT_REVOC_REASON:
+ if( !n )
+ break;
+ return 0;
+ case SIGSUBPKT_ISSUER: /* issuer key ID */
+ if( n < 8 )
+ break;
+ return 0;
+ case SIGSUBPKT_NOTATION:
+ /* minimum length needed, and the subpacket must be well-formed
+ where the name length and value length all fit inside the
+ packet. */
+ if(n<8 || 8+((buffer[4]<<8)|buffer[5])+((buffer[6]<<8)|buffer[7]) != n)
+ break;
+ return 0;
+ case SIGSUBPKT_PRIMARY_UID:
+ if ( n != 1 )
+ break;
+ return 0;
+ case SIGSUBPKT_TRUST:
+ if ( n != 2 )
+ break;
+ return 0;
+ default: return 0;
+ }
+ return -2;
+}
+
+/* Not many critical notations we understand yet... */
+static int
+can_handle_critical_notation(const byte *name,size_t len)
+{
+ if(len==32 && memcmp(name,"preferred-email-encoding@pgp.com",32)==0)
+ return 1;
+ if(len==21 && memcmp(name,"pka-address@gnupg.org",21)==0)
+ return 1;
+
+ return 0;
+}
+
+static int
+can_handle_critical( const byte *buffer, size_t n, int type )
+{
+ switch( type )
+ {
+ case SIGSUBPKT_NOTATION:
+ if(n>=8)
+ return can_handle_critical_notation(buffer+8,(buffer[4]<<8)|buffer[5]);
+ else
+ return 0;
+ case SIGSUBPKT_SIGNATURE:
+ case SIGSUBPKT_SIG_CREATED:
+ case SIGSUBPKT_SIG_EXPIRE:
+ case SIGSUBPKT_KEY_EXPIRE:
+ case SIGSUBPKT_EXPORTABLE:
+ case SIGSUBPKT_REVOCABLE:
+ case SIGSUBPKT_REV_KEY:
+ case SIGSUBPKT_ISSUER:/* issuer key ID */
+ case SIGSUBPKT_PREF_SYM:
+ case SIGSUBPKT_PREF_HASH:
+ case SIGSUBPKT_PREF_COMPR:
+ case SIGSUBPKT_KEY_FLAGS:
+ case SIGSUBPKT_PRIMARY_UID:
+ case SIGSUBPKT_FEATURES:
+ case SIGSUBPKT_TRUST:
+ case SIGSUBPKT_REGEXP:
+ /* Is it enough to show the policy or keyserver? */
+ case SIGSUBPKT_POLICY:
+ case SIGSUBPKT_PREF_KS:
+ return 1;
+
+ default:
+ return 0;
+ }
+}
+
+
+const byte *
+enum_sig_subpkt( const subpktarea_t *pktbuf, sigsubpkttype_t reqtype,
+ size_t *ret_n, int *start, int *critical )
+{
+ const byte *buffer;
+ int buflen;
+ int type;
+ int critical_dummy;
+ int offset;
+ size_t n;
+ int seq = 0;
+ int reqseq = start? *start: 0;
+
+ if(!critical)
+ critical=&critical_dummy;
+
+ if( !pktbuf || reqseq == -1 ) {
+ /* return some value different from NULL to indicate that
+ * there is no critical bit we do not understand. The caller
+ * will never use the value. Yes I know, it is an ugly hack */
+ return reqtype == SIGSUBPKT_TEST_CRITICAL? (const byte*)&pktbuf : NULL;
+ }
+ buffer = pktbuf->data;
+ buflen = pktbuf->len;
+ while( buflen ) {
+ n = *buffer++; buflen--;
+ if( n == 255 ) { /* 4 byte length header */
+ if( buflen < 4 )
+ goto too_short;
+ n = (buffer[0] << 24) | (buffer[1] << 16)
+ | (buffer[2] << 8) | buffer[3];
+ buffer += 4;
+ buflen -= 4;
+ }
+ else if( n >= 192 ) { /* 2 byte special encoded length header */
+ if( buflen < 2 )
+ goto too_short;
+ n = (( n - 192 ) << 8) + *buffer + 192;
+ buffer++;
+ buflen--;
+ }
+ if( buflen < n )
+ goto too_short;
+ type = *buffer;
+ if( type & 0x80 ) {
+ type &= 0x7f;
+ *critical = 1;
+ }
+ else
+ *critical = 0;
+ if( !(++seq > reqseq) )
+ ;
+ else if( reqtype == SIGSUBPKT_TEST_CRITICAL ) {
+ if( *critical ) {
+ if( n-1 > buflen+1 )
+ goto too_short;
+ if( !can_handle_critical(buffer+1, n-1, type ) )
+ {
+ if(opt.verbose)
+ log_info(_("subpacket of type %d has "
+ "critical bit set\n"),type);
+ if( start )
+ *start = seq;
+ return NULL; /* this is an error */
+ }
+ }
+ }
+ else if( reqtype < 0 ) /* list packets */
+ dump_sig_subpkt( reqtype == SIGSUBPKT_LIST_HASHED,
+ type, *critical, buffer, buflen, n );
+ else if( type == reqtype ) { /* found */
+ buffer++;
+ n--;
+ if( n > buflen )
+ goto too_short;
+ if( ret_n )
+ *ret_n = n;
+ offset = parse_one_sig_subpkt(buffer, n, type );
+ switch( offset ) {
+ case -2:
+ log_error("subpacket of type %d too short\n", type);
+ return NULL;
+ case -1:
+ return NULL;
+ default:
+ break;
+ }
+ if( start )
+ *start = seq;
+ return buffer+offset;
+ }
+ buffer += n; buflen -=n;
+ }
+ if( reqtype == SIGSUBPKT_TEST_CRITICAL )
+ return buffer; /* as value true to indicate that there is no */
+ /* critical bit we don't understand */
+ if( start )
+ *start = -1;
+ return NULL; /* end of packets; not found */
+
+ too_short:
+ if(opt.verbose)
+ log_info("buffer shorter than subpacket\n");
+ if( start )
+ *start = -1;
+ return NULL;
+}
+
+
+const byte *
+parse_sig_subpkt (const subpktarea_t *buffer, sigsubpkttype_t reqtype,
+ size_t *ret_n)
+{
+ return enum_sig_subpkt( buffer, reqtype, ret_n, NULL, NULL );
+}
+
+const byte *
+parse_sig_subpkt2 (PKT_signature *sig, sigsubpkttype_t reqtype,
+ size_t *ret_n )
+{
+ const byte *p;
+
+ p = parse_sig_subpkt (sig->hashed, reqtype, ret_n );
+ if( !p )
+ p = parse_sig_subpkt (sig->unhashed, reqtype, ret_n );
+ return p;
+}
+
+/* Find all revocation keys. Look in hashed area only. */
+void parse_revkeys(PKT_signature *sig)
+{
+ struct revocation_key *revkey;
+ int seq=0;
+ size_t len;
+
+ if(sig->sig_class!=0x1F)
+ return;
+
+ while((revkey=
+ (struct revocation_key *)enum_sig_subpkt(sig->hashed,
+ SIGSUBPKT_REV_KEY,
+ &len,&seq,NULL)))
+ {
+ if(len==sizeof(struct revocation_key) &&
+ (revkey->class&0x80)) /* 0x80 bit must be set */
+ {
+ sig->revkey=xrealloc(sig->revkey,
+ sizeof(struct revocation_key *)*(sig->numrevkeys+1));
+ sig->revkey[sig->numrevkeys]=revkey;
+ sig->numrevkeys++;
+ }
+ }
+}
+
+int
+parse_signature( IOBUF inp, int pkttype, unsigned long pktlen,
+ PKT_signature *sig )
+{
+ int md5_len=0;
+ unsigned n;
+ int is_v4=0;
+ int rc=0;
+ int i, ndata;
+
+ if( pktlen < 16 ) {
+ log_error("packet(%d) too short\n", pkttype);
+ goto leave;
+ }
+ sig->version = iobuf_get_noeof(inp); pktlen--;
+ if( sig->version == 4 )
+ is_v4=1;
+ else if( sig->version != 2 && sig->version != 3 ) {
+ log_error("packet(%d) with unknown version %d\n", pkttype, sig->version);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+
+ if( !is_v4 ) {
+ md5_len = iobuf_get_noeof(inp); pktlen--;
+ }
+ sig->sig_class = iobuf_get_noeof(inp); pktlen--;
+ if( !is_v4 ) {
+ sig->timestamp = read_32(inp); pktlen -= 4;
+ sig->keyid[0] = read_32(inp); pktlen -= 4;
+ sig->keyid[1] = read_32(inp); pktlen -= 4;
+ }
+ sig->pubkey_algo = iobuf_get_noeof(inp); pktlen--;
+ sig->digest_algo = iobuf_get_noeof(inp); pktlen--;
+ sig->flags.exportable=1;
+ sig->flags.revocable=1;
+ if( is_v4 ) { /* read subpackets */
+ n = read_16(inp); pktlen -= 2; /* length of hashed data */
+ if( n > 10000 ) {
+ log_error("signature packet: hashed data too long\n");
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ if( n ) {
+ sig->hashed = xmalloc (sizeof (*sig->hashed) + n - 1 );
+ sig->hashed->size = n;
+ sig->hashed->len = n;
+ if( iobuf_read (inp, sig->hashed->data, n ) != n ) {
+ log_error ("premature eof while reading "
+ "hashed signature data\n");
+ rc = -1;
+ goto leave;
+ }
+ pktlen -= n;
+ }
+ n = read_16(inp); pktlen -= 2; /* length of unhashed data */
+ if( n > 10000 ) {
+ log_error("signature packet: unhashed data too long\n");
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ if( n ) {
+ sig->unhashed = xmalloc (sizeof(*sig->unhashed) + n - 1 );
+ sig->unhashed->size = n;
+ sig->unhashed->len = n;
+ if( iobuf_read(inp, sig->unhashed->data, n ) != n ) {
+ log_error("premature eof while reading "
+ "unhashed signature data\n");
+ rc = -1;
+ goto leave;
+ }
+ pktlen -= n;
+ }
+ }
+
+ if( pktlen < 5 ) { /* sanity check */
+ log_error("packet(%d) too short\n", pkttype);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+
+ sig->digest_start[0] = iobuf_get_noeof(inp); pktlen--;
+ sig->digest_start[1] = iobuf_get_noeof(inp); pktlen--;
+
+ if( is_v4 && sig->pubkey_algo )
+ { /*extract required information */
+ const byte *p;
+ size_t len;
+
+ /* set sig->flags.unknown_critical if there is a
+ * critical bit set for packets which we do not understand */
+ if( !parse_sig_subpkt (sig->hashed, SIGSUBPKT_TEST_CRITICAL, NULL)
+ || !parse_sig_subpkt (sig->unhashed, SIGSUBPKT_TEST_CRITICAL,
+ NULL) )
+ sig->flags.unknown_critical = 1;
+
+ p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_CREATED, NULL );
+ if(p)
+ sig->timestamp = buffer_to_u32(p);
+ else if(!(sig->pubkey_algo>=100 && sig->pubkey_algo<=110)
+ && opt.verbose)
+ log_info ("signature packet without timestamp\n");
+
+ p = parse_sig_subpkt2( sig, SIGSUBPKT_ISSUER, NULL );
+ if(p)
+ {
+ sig->keyid[0] = buffer_to_u32(p);
+ sig->keyid[1] = buffer_to_u32(p+4);
+ }
+ else if(!(sig->pubkey_algo>=100 && sig->pubkey_algo<=110)
+ && opt.verbose)
+ log_info ("signature packet without keyid\n");
+
+ p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_SIG_EXPIRE,NULL);
+ if(p && buffer_to_u32(p))
+ sig->expiredate=sig->timestamp+buffer_to_u32(p);
+ if(sig->expiredate && sig->expiredate<=make_timestamp())
+ sig->flags.expired=1;
+
+ p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_POLICY,NULL);
+ if(p)
+ sig->flags.policy_url=1;
+
+ p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_PREF_KS,NULL);
+ if(p)
+ sig->flags.pref_ks=1;
+
+ p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_NOTATION,NULL);
+ if(p)
+ sig->flags.notation=1;
+
+ p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_REVOCABLE,NULL);
+ if(p && *p==0)
+ sig->flags.revocable=0;
+
+ p=parse_sig_subpkt(sig->hashed,SIGSUBPKT_TRUST,&len);
+ if(p && len==2)
+ {
+ sig->trust_depth=p[0];
+ sig->trust_value=p[1];
+
+ /* Only look for a regexp if there is also a trust
+ subpacket. */
+ sig->trust_regexp=
+ parse_sig_subpkt(sig->hashed,SIGSUBPKT_REGEXP,&len);
+
+ /* If the regular expression is of 0 length, there is no
+ regular expression. */
+ if(len==0)
+ sig->trust_regexp=NULL;
+ }
+
+ /* We accept the exportable subpacket from either the hashed
+ or unhashed areas as older versions of gpg put it in the
+ unhashed area. In theory, anyway, we should never see this
+ packet off of a local keyring. */
+
+ p=parse_sig_subpkt2(sig,SIGSUBPKT_EXPORTABLE,NULL);
+ if(p && *p==0)
+ sig->flags.exportable=0;
+
+ /* Find all revocation keys. */
+ if(sig->sig_class==0x1F)
+ parse_revkeys(sig);
+ }
+
+ if( list_mode ) {
+ fprintf (listfp, ":signature packet: algo %d, keyid %08lX%08lX\n"
+ "\tversion %d, created %lu, md5len %d, sigclass %02x\n"
+ "\tdigest algo %d, begin of digest %02x %02x\n",
+ sig->pubkey_algo,
+ (ulong)sig->keyid[0], (ulong)sig->keyid[1],
+ sig->version, (ulong)sig->timestamp, md5_len, sig->sig_class,
+ sig->digest_algo,
+ sig->digest_start[0], sig->digest_start[1] );
+ if( is_v4 ) {
+ parse_sig_subpkt (sig->hashed, SIGSUBPKT_LIST_HASHED, NULL );
+ parse_sig_subpkt (sig->unhashed, SIGSUBPKT_LIST_UNHASHED, NULL);
+ }
+ }
+
+ ndata = pubkey_get_nsig(sig->pubkey_algo);
+ if( !ndata ) {
+ if( list_mode )
+ fprintf (listfp, "\tunknown algorithm %d\n", sig->pubkey_algo );
+ unknown_pubkey_warning( sig->pubkey_algo );
+ /* we store the plain material in data[0], so that we are able
+ * to write it back with build_packet() */
+ sig->data[0]= mpi_set_opaque(NULL, read_rest(inp, pktlen, 0), pktlen );
+ pktlen = 0;
+ }
+ else {
+ for( i=0; i < ndata; i++ ) {
+ n = pktlen;
+ sig->data[i] = mpi_read(inp, &n, 0 );
+ pktlen -=n;
+ if( list_mode ) {
+ fprintf (listfp, "\tdata: ");
+ mpi_print(listfp, sig->data[i], mpi_print_mode );
+ putc ('\n', listfp);
+ }
+ if (!sig->data[i])
+ rc = G10ERR_INVALID_PACKET;
+ }
+ }
+
+ leave:
+ iobuf_skip_rest(inp, pktlen, 0);
+ return rc;
+}
+
+
+static int
+parse_onepass_sig( IOBUF inp, int pkttype, unsigned long pktlen,
+ PKT_onepass_sig *ops )
+{
+ int version;
+ int rc = 0;
+
+ if( pktlen < 13 ) {
+ log_error("packet(%d) too short\n", pkttype);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ version = iobuf_get_noeof(inp); pktlen--;
+ if( version != 3 ) {
+ log_error("onepass_sig with unknown version %d\n", version);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ ops->sig_class = iobuf_get_noeof(inp); pktlen--;
+ ops->digest_algo = iobuf_get_noeof(inp); pktlen--;
+ ops->pubkey_algo = iobuf_get_noeof(inp); pktlen--;
+ ops->keyid[0] = read_32(inp); pktlen -= 4;
+ ops->keyid[1] = read_32(inp); pktlen -= 4;
+ ops->last = iobuf_get_noeof(inp); pktlen--;
+ if( list_mode )
+ fprintf (listfp, ":onepass_sig packet: keyid %08lX%08lX\n"
+ "\tversion %d, sigclass %02x, digest %d, pubkey %d, last=%d\n",
+ (ulong)ops->keyid[0], (ulong)ops->keyid[1],
+ version, ops->sig_class,
+ ops->digest_algo, ops->pubkey_algo, ops->last );
+
+
+ leave:
+ iobuf_skip_rest(inp, pktlen, 0);
+ return rc;
+}
+
+
+static MPI
+read_protected_v3_mpi (IOBUF inp, unsigned long *length)
+{
+ int c;
+ unsigned int nbits, nbytes;
+ unsigned char *buf, *p;
+ MPI val;
+
+ if (*length < 2)
+ {
+ log_error ("mpi too small\n");
+ return NULL;
+ }
+
+ if ((c=iobuf_get (inp)) == -1)
+ return NULL;
+ --*length;
+ nbits = c << 8;
+ if ((c=iobuf_get(inp)) == -1)
+ return NULL;
+ --*length;
+ nbits |= c;
+
+ if (nbits > 16384)
+ {
+ log_error ("mpi too large (%u bits)\n", nbits);
+ return NULL;
+ }
+ nbytes = (nbits+7) / 8;
+ buf = p = xmalloc (2 + nbytes);
+ *p++ = nbits >> 8;
+ *p++ = nbits;
+ for (; nbytes && length; nbytes--, --*length)
+ *p++ = iobuf_get (inp);
+ if (nbytes)
+ {
+ log_error ("packet shorter tham mpi\n");
+ xfree (buf);
+ return NULL;
+ }
+
+ /* convert buffer into an opaque MPI */
+ val = mpi_set_opaque (NULL, buf, p-buf);
+ return val;
+}
+
+
+static int
+parse_key( IOBUF inp, int pkttype, unsigned long pktlen,
+ byte *hdr, int hdrlen, PACKET *pkt )
+{
+ int i, version, algorithm;
+ unsigned n;
+ unsigned long timestamp, expiredate, max_expiredate;
+ int npkey, nskey;
+ int is_v4=0;
+ int rc=0;
+
+ version = iobuf_get_noeof(inp); pktlen--;
+ if( pkttype == PKT_PUBLIC_SUBKEY && version == '#' ) {
+ /* early versions of G10 use old PGP comments packets;
+ * luckily all those comments are started by a hash */
+ if( list_mode ) {
+ fprintf (listfp, ":rfc1991 comment packet: \"" );
+ for( ; pktlen; pktlen-- ) {
+ int c;
+ c = iobuf_get_noeof(inp);
+ if( c >= ' ' && c <= 'z' )
+ putc (c, listfp);
+ else
+ fprintf (listfp, "\\x%02x", c );
+ }
+ fprintf (listfp, "\"\n");
+ }
+ iobuf_skip_rest(inp, pktlen, 0);
+ return 0;
+ }
+ else if( version == 4 )
+ is_v4=1;
+ else if( version != 2 && version != 3 ) {
+ log_error("packet(%d) with unknown version %d\n", pkttype, version);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+
+ if( pktlen < 11 ) {
+ log_error("packet(%d) too short\n", pkttype);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+
+ timestamp = read_32(inp); pktlen -= 4;
+ if( is_v4 ) {
+ expiredate = 0; /* have to get it from the selfsignature */
+ max_expiredate = 0;
+ }
+ else {
+ unsigned short ndays;
+ ndays = read_16(inp); pktlen -= 2;
+ if( ndays )
+ expiredate = timestamp + ndays * 86400L;
+ else
+ expiredate = 0;
+
+ max_expiredate=expiredate;
+ }
+ algorithm = iobuf_get_noeof(inp); pktlen--;
+ if( list_mode )
+ fprintf (listfp, ":%s key packet:\n"
+ "\tversion %d, algo %d, created %lu, expires %lu\n",
+ pkttype == PKT_PUBLIC_KEY? "public" :
+ pkttype == PKT_SECRET_KEY? "secret" :
+ pkttype == PKT_PUBLIC_SUBKEY? "public sub" :
+ pkttype == PKT_SECRET_SUBKEY? "secret sub" : "??",
+ version, algorithm, timestamp, expiredate );
+
+ if( pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY ) {
+ PKT_secret_key *sk = pkt->pkt.secret_key;
+
+ sk->timestamp = timestamp;
+ sk->expiredate = expiredate;
+ sk->max_expiredate = max_expiredate;
+ sk->hdrbytes = hdrlen;
+ sk->version = version;
+ sk->is_primary = pkttype == PKT_SECRET_KEY;
+ sk->pubkey_algo = algorithm;
+ sk->req_usage = 0;
+ sk->pubkey_usage = 0; /* not yet used */
+ }
+ else {
+ PKT_public_key *pk = pkt->pkt.public_key;
+
+ pk->timestamp = timestamp;
+ pk->expiredate = expiredate;
+ pk->max_expiredate = max_expiredate;
+ pk->hdrbytes = hdrlen;
+ pk->version = version;
+ pk->is_primary = pkttype == PKT_PUBLIC_KEY;
+ pk->pubkey_algo = algorithm;
+ pk->req_usage = 0;
+ pk->pubkey_usage = 0; /* not yet used */
+ pk->is_revoked = 0;
+ pk->is_disabled = 0;
+ pk->keyid[0] = 0;
+ pk->keyid[1] = 0;
+ }
+ nskey = pubkey_get_nskey( algorithm );
+ npkey = pubkey_get_npkey( algorithm );
+ if( !npkey ) {
+ if( list_mode )
+ fprintf (listfp, "\tunknown algorithm %d\n", algorithm );
+ unknown_pubkey_warning( algorithm );
+ }
+
+
+ if( pkttype == PKT_SECRET_KEY || pkttype == PKT_SECRET_SUBKEY ) {
+ PKT_secret_key *sk = pkt->pkt.secret_key;
+ byte temp[16];
+ size_t snlen = 0;
+
+ if( !npkey ) {
+ sk->skey[0] = mpi_set_opaque( NULL,
+ read_rest(inp, pktlen, 0), pktlen );
+ pktlen = 0;
+ goto leave;
+ }
+
+ for(i=0; i < npkey; i++ ) {
+ n = pktlen; sk->skey[i] = mpi_read(inp, &n, 0 ); pktlen -=n;
+ if( list_mode ) {
+ fprintf (listfp, "\tskey[%d]: ", i);
+ mpi_print(listfp, sk->skey[i], mpi_print_mode );
+ putc ('\n', listfp);
+ }
+ if (!sk->skey[i])
+ rc = G10ERR_INVALID_PACKET;
+ }
+ if (rc) /* one of the MPIs were bad */
+ goto leave;
+ sk->protect.algo = iobuf_get_noeof(inp); pktlen--;
+ sk->protect.sha1chk = 0;
+ if( sk->protect.algo ) {
+ sk->is_protected = 1;
+ sk->protect.s2k.count = 0;
+ if( sk->protect.algo == 254 || sk->protect.algo == 255 ) {
+ if( pktlen < 3 ) {
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ sk->protect.sha1chk = (sk->protect.algo == 254);
+ sk->protect.algo = iobuf_get_noeof(inp); pktlen--;
+ /* Note that a sk->protect.algo > 110 is illegal, but
+ I'm not erroring on it here as otherwise there
+ would be no way to delete such a key. */
+ sk->protect.s2k.mode = iobuf_get_noeof(inp); pktlen--;
+ sk->protect.s2k.hash_algo = iobuf_get_noeof(inp); pktlen--;
+ /* check for the special GNU extension */
+ if( is_v4 && sk->protect.s2k.mode == 101 ) {
+ for(i=0; i < 4 && pktlen; i++, pktlen-- )
+ temp[i] = iobuf_get_noeof(inp);
+ if( i < 4 || memcmp( temp, "GNU", 3 ) ) {
+ if( list_mode )
+ fprintf (listfp, "\tunknown S2K %d\n",
+ sk->protect.s2k.mode );
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ /* here we know that it is a gnu extension
+ * What follows is the GNU protection mode:
+ * All values have special meanings
+ * and they are mapped in the mode with a base of 1000.
+ */
+ sk->protect.s2k.mode = 1000 + temp[3];
+ }
+ switch( sk->protect.s2k.mode ) {
+ case 1:
+ case 3:
+ for(i=0; i < 8 && pktlen; i++, pktlen-- )
+ temp[i] = iobuf_get_noeof(inp);
+ memcpy(sk->protect.s2k.salt, temp, 8 );
+ break;
+ }
+ switch( sk->protect.s2k.mode ) {
+ case 0: if( list_mode ) fprintf (listfp, "\tsimple S2K" );
+ break;
+ case 1: if( list_mode ) fprintf (listfp, "\tsalted S2K" );
+ break;
+ case 3: if( list_mode ) fprintf (listfp, "\titer+salt S2K" );
+ break;
+ case 1001: if( list_mode ) fprintf (listfp,
+ "\tgnu-dummy S2K" );
+ break;
+ case 1002: if (list_mode) fprintf (listfp,
+ "\tgnu-divert-to-card S2K");
+ break;
+ default:
+ if( list_mode )
+ fprintf (listfp, "\tunknown %sS2K %d\n",
+ sk->protect.s2k.mode < 1000? "":"GNU ",
+ sk->protect.s2k.mode );
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+
+ if( list_mode ) {
+ fprintf (listfp, ", algo: %d,%s hash: %d",
+ sk->protect.algo,
+ sk->protect.sha1chk?" SHA1 protection,"
+ :" simple checksum,",
+ sk->protect.s2k.hash_algo );
+ if( sk->protect.s2k.mode == 1
+ || sk->protect.s2k.mode == 3 ) {
+ fprintf (listfp, ", salt: ");
+ for(i=0; i < 8; i++ )
+ fprintf (listfp, "%02x", sk->protect.s2k.salt[i]);
+ }
+ putc ('\n', listfp);
+ }
+
+ if( sk->protect.s2k.mode == 3 ) {
+ if( pktlen < 1 ) {
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ sk->protect.s2k.count = iobuf_get(inp);
+ pktlen--;
+ if( list_mode )
+ fprintf (listfp, "\tprotect count: %lu\n",
+ (ulong)sk->protect.s2k.count);
+ }
+ else if( sk->protect.s2k.mode == 1002 ) {
+ /* Read the serial number. */
+ if (pktlen < 1) {
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ snlen = iobuf_get (inp);
+ pktlen--;
+ if (pktlen < snlen || snlen == -1) {
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ }
+ }
+ /* Note that a sk->protect.algo > 110 is illegal, but I'm
+ not erroring on it here as otherwise there would be no
+ way to delete such a key. */
+ else { /* old version; no S2K, so we set mode to 0, hash MD5 */
+ sk->protect.s2k.mode = 0;
+ sk->protect.s2k.hash_algo = DIGEST_ALGO_MD5;
+ if( list_mode )
+ fprintf (listfp, "\tprotect algo: %d (hash algo: %d)\n",
+ sk->protect.algo, sk->protect.s2k.hash_algo );
+ }
+ /* It is really ugly that we don't know the size
+ * of the IV here in cases we are not aware of the algorithm.
+ * so a
+ * sk->protect.ivlen = cipher_get_blocksize(sk->protect.algo);
+ * won't work. The only solution I see is to hardwire it here.
+ * NOTE: if you change the ivlen above 16, don't forget to
+ * enlarge temp.
+ */
+ switch( sk->protect.algo ) {
+ case 7: case 8: case 9: /* reserved for AES */
+ case 10: /* Twofish */
+ sk->protect.ivlen = 16;
+ break;
+ default:
+ sk->protect.ivlen = 8;
+ }
+ if( sk->protect.s2k.mode == 1001 )
+ sk->protect.ivlen = 0;
+ else if( sk->protect.s2k.mode == 1002 )
+ sk->protect.ivlen = snlen < 16? snlen : 16;
+
+ if( pktlen < sk->protect.ivlen ) {
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ for(i=0; i < sk->protect.ivlen && pktlen; i++, pktlen-- )
+ temp[i] = iobuf_get_noeof(inp);
+ if( list_mode ) {
+ fprintf (listfp,
+ sk->protect.s2k.mode == 1002? "\tserial-number: "
+ : "\tprotect IV: ");
+ for(i=0; i < sk->protect.ivlen; i++ )
+ fprintf (listfp, " %02x", temp[i] );
+ putc ('\n', listfp);
+ }
+ memcpy(sk->protect.iv, temp, sk->protect.ivlen );
+ }
+ else
+ sk->is_protected = 0;
+ /* It does not make sense to read it into secure memory.
+ * If the user is so careless, not to protect his secret key,
+ * we can assume, that he operates an open system :=(.
+ * So we put the key into secure memory when we unprotect it. */
+ if( sk->protect.s2k.mode == 1001
+ || sk->protect.s2k.mode == 1002 ) {
+ /* better set some dummy stuff here */
+ sk->skey[npkey] = mpi_set_opaque(NULL, xstrdup("dummydata"), 10);
+ pktlen = 0;
+ }
+ else if( is_v4 && sk->is_protected ) {
+ /* ugly; the length is encrypted too, so we read all
+ * stuff up to the end of the packet into the first
+ * skey element */
+ sk->skey[npkey] = mpi_set_opaque(NULL,
+ read_rest(inp, pktlen, 0),pktlen);
+ pktlen = 0;
+ if( list_mode ) {
+ fprintf (listfp, "\tencrypted stuff follows\n");
+ }
+ }
+ else { /* v3 method: the mpi length is not encrypted */
+ for(i=npkey; i < nskey; i++ ) {
+ if ( sk->is_protected ) {
+ sk->skey[i] = read_protected_v3_mpi (inp, &pktlen);
+ if( list_mode )
+ fprintf (listfp, "\tskey[%d]: [encrypted]\n", i);
+ }
+ else {
+ n = pktlen;
+ sk->skey[i] = mpi_read(inp, &n, 0 );
+ pktlen -=n;
+ if( list_mode ) {
+ fprintf (listfp, "\tskey[%d]: ", i);
+ mpi_print(listfp, sk->skey[i], mpi_print_mode );
+ putc ('\n', listfp);
+ }
+ }
+
+ if (!sk->skey[i])
+ rc = G10ERR_INVALID_PACKET;
+ }
+ if (rc)
+ goto leave;
+
+ sk->csum = read_16(inp); pktlen -= 2;
+ if( list_mode ) {
+ fprintf (listfp, "\tchecksum: %04hx\n", sk->csum);
+ }
+ }
+ }
+ else {
+ PKT_public_key *pk = pkt->pkt.public_key;
+
+ if( !npkey ) {
+ pk->pkey[0] = mpi_set_opaque( NULL,
+ read_rest(inp, pktlen, 0), pktlen );
+ pktlen = 0;
+ goto leave;
+ }
+
+ for(i=0; i < npkey; i++ ) {
+ n = pktlen; pk->pkey[i] = mpi_read(inp, &n, 0 ); pktlen -=n;
+ if( list_mode ) {
+ fprintf (listfp, "\tpkey[%d]: ", i);
+ mpi_print(listfp, pk->pkey[i], mpi_print_mode );
+ putc ('\n', listfp);
+ }
+ if (!pk->pkey[i])
+ rc = G10ERR_INVALID_PACKET;
+ }
+ if (rc)
+ goto leave;
+ }
+
+ leave:
+ iobuf_skip_rest(inp, pktlen, 0);
+ return rc;
+}
+
+/* Attribute subpackets have the same format as v4 signature
+ subpackets. This is not part of OpenPGP, but is done in several
+ versions of PGP nevertheless. */
+int
+parse_attribute_subpkts(PKT_user_id *uid)
+{
+ size_t n;
+ int count=0;
+ struct user_attribute *attribs=NULL;
+ const byte *buffer=uid->attrib_data;
+ int buflen=uid->attrib_len;
+ byte type;
+
+ xfree(uid->attribs);
+
+ while(buflen)
+ {
+ n = *buffer++; buflen--;
+ if( n == 255 ) { /* 4 byte length header */
+ if( buflen < 4 )
+ goto too_short;
+ n = (buffer[0] << 24) | (buffer[1] << 16)
+ | (buffer[2] << 8) | buffer[3];
+ buffer += 4;
+ buflen -= 4;
+ }
+ else if( n >= 192 ) { /* 2 byte special encoded length header */
+ if( buflen < 2 )
+ goto too_short;
+ n = (( n - 192 ) << 8) + *buffer + 192;
+ buffer++;
+ buflen--;
+ }
+ if( buflen < n )
+ goto too_short;
+
+ attribs=xrealloc(attribs,(count+1)*sizeof(struct user_attribute));
+ memset(&attribs[count],0,sizeof(struct user_attribute));
+
+ type=*buffer;
+ buffer++;
+ buflen--;
+ n--;
+
+ attribs[count].type=type;
+ attribs[count].data=buffer;
+ attribs[count].len=n;
+ buffer+=n;
+ buflen-=n;
+ count++;
+ }
+
+ uid->attribs=attribs;
+ uid->numattribs=count;
+ return count;
+
+ too_short:
+ if(opt.verbose)
+ log_info("buffer shorter than attribute subpacket\n");
+ uid->attribs=attribs;
+ uid->numattribs=count;
+ return count;
+}
+
+
+static int
+parse_user_id( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
+{
+ byte *p;
+
+ /* Cap the size of a user ID at 2k: a value absurdly large enough
+ that there is no sane user ID string (which is printable text
+ as of RFC2440bis) that won't fit in it, but yet small enough to
+ avoid allocation problems. A large pktlen may not be
+ allocatable, and a very large pktlen could actually cause our
+ allocation to wrap around in xmalloc to a small number. */
+
+ if(pktlen>2048)
+ {
+ log_error("packet(%d) too large\n", pkttype);
+ iobuf_skip_rest(inp, pktlen, 0);
+ return G10ERR_INVALID_PACKET;
+ }
+
+ packet->pkt.user_id = xmalloc_clear(sizeof *packet->pkt.user_id + pktlen);
+ packet->pkt.user_id->len = pktlen;
+ packet->pkt.user_id->ref=1;
+
+ p = packet->pkt.user_id->name;
+ for( ; pktlen; pktlen--, p++ )
+ *p = iobuf_get_noeof(inp);
+ *p = 0;
+
+ if( list_mode ) {
+ int n = packet->pkt.user_id->len;
+ fprintf (listfp, ":user ID packet: \"");
+ /* fixme: Hey why don't we replace this with print_string?? */
+ for(p=packet->pkt.user_id->name; n; p++, n-- ) {
+ if( *p >= ' ' && *p <= 'z' )
+ putc (*p, listfp);
+ else
+ fprintf (listfp, "\\x%02x", *p );
+ }
+ fprintf (listfp, "\"\n");
+ }
+ return 0;
+}
+
+
+void
+make_attribute_uidname(PKT_user_id *uid, size_t max_namelen)
+{
+ assert ( max_namelen > 70 );
+ if(uid->numattribs<=0)
+ sprintf(uid->name,"[bad attribute packet of size %lu]",uid->attrib_len);
+ else if(uid->numattribs>1)
+ sprintf(uid->name,"[%d attributes of size %lu]",
+ uid->numattribs,uid->attrib_len);
+ else
+ {
+ /* Only one attribute, so list it as the "user id" */
+
+ if(uid->attribs->type==ATTRIB_IMAGE)
+ {
+ u32 len;
+ byte type;
+
+ if(parse_image_header(uid->attribs,&type,&len))
+ sprintf(uid->name,"[%.20s image of size %lu]",
+ image_type_to_string(type,1),(ulong)len);
+ else
+ sprintf(uid->name,"[invalid image]");
+ }
+ else
+ sprintf(uid->name,"[unknown attribute of size %lu]",
+ (ulong)uid->attribs->len);
+ }
+
+ uid->len = strlen(uid->name);
+}
+
+static int
+parse_attribute( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
+{
+ byte *p;
+
+#define EXTRA_UID_NAME_SPACE 71
+ packet->pkt.user_id = xmalloc_clear(sizeof *packet->pkt.user_id
+ + EXTRA_UID_NAME_SPACE);
+ packet->pkt.user_id->ref=1;
+ packet->pkt.user_id->attrib_data = xmalloc(pktlen);
+ packet->pkt.user_id->attrib_len = pktlen;
+
+ p = packet->pkt.user_id->attrib_data;
+ for( ; pktlen; pktlen--, p++ )
+ *p = iobuf_get_noeof(inp);
+
+ /* Now parse out the individual attribute subpackets. This is
+ somewhat pointless since there is only one currently defined
+ attribute type (jpeg), but it is correct by the spec. */
+ parse_attribute_subpkts(packet->pkt.user_id);
+
+ make_attribute_uidname(packet->pkt.user_id, EXTRA_UID_NAME_SPACE);
+
+ if( list_mode ) {
+ fprintf (listfp, ":attribute packet: %s\n", packet->pkt.user_id->name );
+ }
+ return 0;
+}
+
+
+static int
+parse_comment( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *packet )
+{
+ byte *p;
+
+ /* Cap comment packet at a reasonable value to avoid an integer
+ overflow in the malloc below. Comment packets are actually not
+ anymore define my OpenPGP and we even stopped to use our
+ private comment packet. */
+ if (pktlen>65536)
+ {
+ log_error ("packet(%d) too large\n", pkttype);
+ iobuf_skip_rest (inp, pktlen, 0);
+ return G10ERR_INVALID_PACKET;
+ }
+ packet->pkt.comment = xmalloc(sizeof *packet->pkt.comment + pktlen - 1);
+ packet->pkt.comment->len = pktlen;
+ p = packet->pkt.comment->data;
+ for( ; pktlen; pktlen--, p++ )
+ *p = iobuf_get_noeof(inp);
+
+ if( list_mode ) {
+ int n = packet->pkt.comment->len;
+ fprintf (listfp, ":%scomment packet: \"", pkttype == PKT_OLD_COMMENT?
+ "OpenPGP draft " : "GnuPG " );
+ for(p=packet->pkt.comment->data; n; p++, n-- ) {
+ if( *p >= ' ' && *p <= 'z' )
+ putc (*p, listfp);
+ else
+ fprintf (listfp, "\\x%02x", *p );
+ }
+ fprintf (listfp, "\"\n");
+ }
+ return 0;
+}
+
+
+static void
+parse_trust( IOBUF inp, int pkttype, unsigned long pktlen, PACKET *pkt )
+{
+ int c;
+
+ if (pktlen)
+ {
+ c = iobuf_get_noeof(inp);
+ pktlen--;
+ pkt->pkt.ring_trust = xmalloc( sizeof *pkt->pkt.ring_trust );
+ pkt->pkt.ring_trust->trustval = c;
+ pkt->pkt.ring_trust->sigcache = 0;
+ if (!c && pktlen==1)
+ {
+ c = iobuf_get_noeof (inp);
+ pktlen--;
+ /* we require that bit 7 of the sigcache is 0 (easier eof handling)*/
+ if ( !(c & 0x80) )
+ pkt->pkt.ring_trust->sigcache = c;
+ }
+ if( list_mode )
+ fprintf (listfp, ":trust packet: flag=%02x sigcache=%02x\n",
+ pkt->pkt.ring_trust->trustval,
+ pkt->pkt.ring_trust->sigcache);
+ }
+ else
+ {
+ if( list_mode )
+ fprintf (listfp, ":trust packet: empty\n");
+ }
+ iobuf_skip_rest (inp, pktlen, 0);
+}
+
+
+static int
+parse_plaintext( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *pkt, int new_ctb, int partial )
+{
+ int rc = 0;
+ int mode, namelen;
+ PKT_plaintext *pt;
+ byte *p;
+ int c, i;
+
+ if( !partial && pktlen < 6 ) {
+ log_error("packet(%d) too short (%lu)\n", pkttype, (ulong)pktlen);
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ mode = iobuf_get_noeof(inp); if( pktlen ) pktlen--;
+ namelen = iobuf_get_noeof(inp); if( pktlen ) pktlen--;
+ /* Note that namelen will never exceeds 255 byte. */
+ pt = pkt->pkt.plaintext = xmalloc(sizeof *pkt->pkt.plaintext + namelen -1);
+ pt->new_ctb = new_ctb;
+ pt->mode = mode;
+ pt->namelen = namelen;
+ pt->is_partial = partial;
+ if( pktlen ) {
+ for( i=0; pktlen > 4 && i < namelen; pktlen--, i++ )
+ pt->name[i] = iobuf_get_noeof(inp);
+ }
+ else {
+ for( i=0; i < namelen; i++ )
+ if( (c=iobuf_get(inp)) == -1 )
+ break;
+ else
+ pt->name[i] = c;
+ }
+ pt->timestamp = read_32(inp); if( pktlen) pktlen -= 4;
+ pt->len = pktlen;
+ pt->buf = inp;
+ pktlen = 0;
+
+ if( list_mode ) {
+ fprintf (listfp, ":literal data packet:\n"
+ "\tmode %c (%X), created %lu, name=\"",
+ mode >= ' ' && mode <'z'? mode : '?', mode,
+ (ulong)pt->timestamp );
+ for(p=pt->name,i=0; i < namelen; p++, i++ ) {
+ if( *p >= ' ' && *p <= 'z' )
+ putc (*p, listfp);
+ else
+ fprintf (listfp, "\\x%02x", *p );
+ }
+ fprintf (listfp, "\",\n\traw data: ");
+ if(partial)
+ fprintf (listfp, "unknown length\n");
+ else
+ fprintf (listfp, "%lu bytes\n", (ulong)pt->len );
+ }
+
+ leave:
+ return rc;
+}
+
+
+static int
+parse_compressed( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *pkt, int new_ctb )
+{
+ PKT_compressed *zd;
+
+ /* pktlen is here 0, but data follows
+ * (this should be the last object in a file or
+ * the compress algorithm should know the length)
+ */
+ zd = pkt->pkt.compressed = xmalloc(sizeof *pkt->pkt.compressed );
+ zd->algorithm = iobuf_get_noeof(inp);
+ zd->len = 0; /* not used */
+ zd->new_ctb = new_ctb;
+ zd->buf = inp;
+ if( list_mode )
+ fprintf (listfp, ":compressed packet: algo=%d\n", zd->algorithm);
+ return 0;
+}
+
+
+static int
+parse_encrypted( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *pkt, int new_ctb, int partial )
+{
+ int rc = 0;
+ PKT_encrypted *ed;
+ unsigned long orig_pktlen = pktlen;
+
+ ed = pkt->pkt.encrypted = xmalloc(sizeof *pkt->pkt.encrypted );
+ ed->len = pktlen;
+ /* we don't know the extralen which is (cipher_blocksize+2)
+ because the algorithm ist not specified in this packet.
+ However, it is only important to know this for some sanity
+ checks on the packet length - it doesn't matter that we can't
+ do it */
+ ed->extralen = 0;
+ ed->buf = NULL;
+ ed->new_ctb = new_ctb;
+ ed->is_partial = partial;
+ ed->mdc_method = 0;
+ if( pkttype == PKT_ENCRYPTED_MDC ) {
+ /* fixme: add some pktlen sanity checks */
+ int version;
+
+ version = iobuf_get_noeof(inp);
+ if (orig_pktlen)
+ pktlen--;
+ if( version != 1 ) {
+ log_error("encrypted_mdc packet with unknown version %d\n",
+ version);
+ /*skip_rest(inp, pktlen); should we really do this? */
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ ed->mdc_method = DIGEST_ALGO_SHA1;
+ }
+ if( orig_pktlen && pktlen < 10 ) { /* actually this is blocksize+2 */
+ log_error("packet(%d) too short\n", pkttype);
+ rc = G10ERR_INVALID_PACKET;
+ iobuf_skip_rest(inp, pktlen, partial);
+ goto leave;
+ }
+ if( list_mode ) {
+ if( orig_pktlen )
+ fprintf (listfp, ":encrypted data packet:\n\tlength: %lu\n",
+ orig_pktlen);
+ else
+ fprintf (listfp, ":encrypted data packet:\n\tlength: unknown\n");
+ if( ed->mdc_method )
+ fprintf (listfp, "\tmdc_method: %d\n", ed->mdc_method );
+ }
+
+ ed->buf = inp;
+
+ leave:
+ return rc;
+}
+
+
+static int
+parse_mdc( IOBUF inp, int pkttype, unsigned long pktlen,
+ PACKET *pkt, int new_ctb )
+{
+ int rc = 0;
+ PKT_mdc *mdc;
+ byte *p;
+
+ mdc = pkt->pkt.mdc= xmalloc(sizeof *pkt->pkt.mdc );
+ if( list_mode )
+ fprintf (listfp, ":mdc packet: length=%lu\n", pktlen);
+ if( !new_ctb || pktlen != 20 ) {
+ log_error("mdc_packet with invalid encoding\n");
+ rc = G10ERR_INVALID_PACKET;
+ goto leave;
+ }
+ p = mdc->hash;
+ for( ; pktlen; pktlen--, p++ )
+ *p = iobuf_get_noeof(inp);
+
+ leave:
+ return rc;
+}
+
+
+/*
+ * This packet is internally generated by GPG (by armor.c) to
+ * transfer some information to the lower layer. To make sure that
+ * this packet is really a GPG faked one and not one comming from outside,
+ * we first check that there is a unique tag in it.
+ * The format of such a control packet is:
+ * n byte session marker
+ * 1 byte control type CTRLPKT_xxxxx
+ * m byte control data
+ */
+
+static int
+parse_gpg_control( IOBUF inp, int pkttype,
+ unsigned long pktlen, PACKET *packet, int partial )
+{
+ byte *p;
+ const byte *sesmark;
+ size_t sesmarklen;
+ int i;
+
+ if ( list_mode )
+ fprintf (listfp, ":packet 63: length %lu ", pktlen);
+
+ sesmark = get_session_marker ( &sesmarklen );
+ if ( pktlen < sesmarklen+1 ) /* 1 is for the control bytes */
+ goto skipit;
+ for( i=0; i < sesmarklen; i++, pktlen-- ) {
+ if ( sesmark[i] != iobuf_get_noeof(inp) )
+ goto skipit;
+ }
+ if (pktlen > 4096)
+ goto skipit; /* Definitely too large. We skip it to avoid an
+ overflow in the malloc. */
+ if ( list_mode )
+ puts ("- gpg control packet");
+
+ packet->pkt.gpg_control = xmalloc(sizeof *packet->pkt.gpg_control
+ + pktlen - 1);
+ packet->pkt.gpg_control->control = iobuf_get_noeof(inp); pktlen--;
+ packet->pkt.gpg_control->datalen = pktlen;
+ p = packet->pkt.gpg_control->data;
+ for( ; pktlen; pktlen--, p++ )
+ *p = iobuf_get_noeof(inp);
+
+ return 0;
+
+ skipit:
+ if ( list_mode ) {
+ int c;
+
+ i=0;
+ fprintf (listfp, "- private (rest length %lu)\n", pktlen);
+ if( partial ) {
+ while( (c=iobuf_get(inp)) != -1 )
+ dump_hex_line(c, &i);
+ }
+ else {
+ for( ; pktlen; pktlen-- )
+ dump_hex_line(iobuf_get(inp), &i);
+ }
+ putc ('\n', listfp);
+ }
+ iobuf_skip_rest(inp,pktlen, 0);
+ return G10ERR_INVALID_PACKET;
+}
+
+/* create a gpg control packet to be used internally as a placeholder */
+PACKET *
+create_gpg_control( ctrlpkttype_t type, const byte *data, size_t datalen )
+{
+ PACKET *packet;
+ byte *p;
+
+ packet = xmalloc( sizeof *packet );
+ init_packet(packet);
+ packet->pkttype = PKT_GPG_CONTROL;
+ packet->pkt.gpg_control = xmalloc(sizeof *packet->pkt.gpg_control
+ + datalen - 1);
+ packet->pkt.gpg_control->control = type;
+ packet->pkt.gpg_control->datalen = datalen;
+ p = packet->pkt.gpg_control->data;
+ for( ; datalen; datalen--, p++ )
+ *p = *data++;
+
+ return packet;
+}
diff --git a/g10/passphrase.c b/g10/passphrase.c
new file mode 100644
index 0000000..4dd2f83
--- /dev/null
+++ b/g10/passphrase.c
@@ -0,0 +1,1084 @@
+/* passphrase.c - Get a passphrase
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <assert.h>
+#if !defined(HAVE_DOSISH_SYSTEM) && !defined(__riscos__)
+#include <sys/socket.h>
+#include <sys/un.h>
+#endif
+#if defined (_WIN32)
+#include <windows.h>
+#endif
+#include <errno.h>
+#ifdef HAVE_LOCALE_H
+#include <locale.h>
+#endif
+#ifdef HAVE_LANGINFO_CODESET
+#include <langinfo.h>
+#endif
+
+#include "util.h"
+#include "memory.h"
+#include "options.h"
+#include "ttyio.h"
+#include "cipher.h"
+#include "keydb.h"
+#include "main.h"
+#include "i18n.h"
+#include "status.h"
+#ifdef ENABLE_AGENT_SUPPORT
+#include "assuan.h"
+#endif /*ENABLE_AGENT_SUPPORT*/
+
+static char *fd_passwd = NULL;
+static char *next_pw = NULL;
+static char *last_pw = NULL;
+
+static void hash_passphrase( DEK *dek, char *pw, STRING2KEY *s2k, int create );
+
+int
+have_static_passphrase()
+{
+ if ( opt.use_agent )
+ return 0;
+ return !!fd_passwd;
+}
+
+/****************
+ * Set the passphrase to be used for the next query and only for the next
+ * one.
+ */
+void
+set_next_passphrase( const char *s )
+{
+ xfree(next_pw);
+ next_pw = NULL;
+ if( s ) {
+ next_pw = xmalloc_secure( strlen(s)+1 );
+ strcpy(next_pw, s );
+ }
+}
+
+/****************
+ * Get the last passphrase used in passphrase_to_dek.
+ * Note: This removes the passphrase from this modules and
+ * the caller must free the result. May return NULL:
+ */
+char *
+get_last_passphrase()
+{
+ char *p = last_pw;
+ last_pw = NULL;
+ return p;
+}
+
+/* As if we had used the passphrase - make it the last_pw. */
+void
+next_to_last_passphrase(void)
+{
+ if(next_pw)
+ {
+ last_pw=next_pw;
+ next_pw=NULL;
+ }
+}
+
+/* Here's an interesting question: since this passphrase was passed in
+ on the command line, is there really any point in using secure
+ memory for it? I'm going with 'yes', since it doesn't hurt, and
+ might help in some small way (swapping). */
+
+void
+set_passphrase_from_string(const char *pass)
+{
+ xfree( fd_passwd );
+ fd_passwd = xmalloc_secure(strlen(pass)+1);
+ strcpy(fd_passwd,pass);
+}
+
+
+void
+read_passphrase_from_fd( int fd )
+{
+ int i, len;
+ char *pw;
+
+ if ( opt.use_agent )
+ { /* Not used but we have to do a dummy read, so that it won't end
+ up at the begin of the message if the quite usual trick to
+ prepend the passphtrase to the message is used. */
+ char buf[1];
+
+ while (!(read (fd, buf, 1) != 1 || *buf == '\n' ))
+ ;
+ *buf = 0;
+ return;
+ }
+
+ if (!opt.batch )
+ tty_printf("Reading passphrase from file descriptor %d ...", fd );
+ for (pw = NULL, i = len = 100; ; i++ )
+ {
+ if (i >= len-1 )
+ {
+ char *pw2 = pw;
+ len += 100;
+ pw = xmalloc_secure( len );
+ if( pw2 )
+ {
+ memcpy(pw, pw2, i );
+ xfree (pw2);
+ }
+ else
+ i=0;
+ }
+ if (read( fd, pw+i, 1) != 1 || pw[i] == '\n' )
+ break;
+ }
+ pw[i] = 0;
+ if (!opt.batch)
+ tty_printf("\b\b\b \n" );
+
+ xfree( fd_passwd );
+ fd_passwd = pw;
+}
+
+
+
+#ifdef ENABLE_AGENT_SUPPORT
+/* Send one option to the gpg-agent. */
+static int
+agent_send_option (assuan_context_t ctx, const char *name, const char *value)
+{
+ char *line;
+ int rc;
+
+ if (!value || !*value)
+ return 0; /* Avoid sending empty option values. */
+
+ line = xmalloc (7 + strlen (name) + 1 + strlen (value) + 1);
+ strcpy (stpcpy (stpcpy (stpcpy (line, "OPTION "), name), "="), value);
+ rc = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ xfree (line);
+ return rc? -1 : 0;
+}
+
+/* Send all required options to the gpg-agent. */
+static int
+agent_send_all_options (assuan_context_t ctx)
+{
+ char *dft_display = NULL;
+ const char *dft_ttyname = NULL;
+ char *dft_ttytype = NULL;
+ char *old_lc = NULL;
+ char *dft_lc = NULL;
+ int rc = 0;
+
+ dft_display = getenv ("DISPLAY");
+ if (opt.display || dft_display)
+ {
+ if (agent_send_option (ctx, "display",
+ opt.display ? opt.display : dft_display))
+ return -1;
+ }
+
+ if (!opt.ttyname)
+ {
+ const char *tmp;
+
+ dft_ttyname = getenv ("GPG_TTY");
+ if ((!dft_ttyname || !*dft_ttyname) && (tmp=ttyname (0)))
+ dft_ttyname = tmp;
+ if ((!dft_ttyname || !*dft_ttyname) && (tmp=tty_get_ttyname ()))
+ dft_ttyname = tmp;
+ }
+ if (opt.ttyname || dft_ttyname)
+ {
+ if (agent_send_option (ctx, "ttyname",
+ opt.ttyname ? opt.ttyname : dft_ttyname))
+ return -1;
+ }
+
+ dft_ttytype = getenv ("TERM");
+ if (opt.ttytype || (dft_ttyname && dft_ttytype))
+ {
+ if (agent_send_option (ctx, "ttytype",
+ opt.ttyname ? opt.ttytype : dft_ttytype))
+ return -1;
+ }
+
+#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
+ old_lc = setlocale (LC_CTYPE, NULL);
+ if (old_lc)
+ old_lc = xstrdup (old_lc);
+ dft_lc = setlocale (LC_CTYPE, "");
+#endif
+ if (opt.lc_ctype || (dft_ttyname && dft_lc))
+ {
+ rc = agent_send_option (ctx, "lc-ctype",
+ opt.lc_ctype ? opt.lc_ctype : dft_lc);
+ }
+#if defined(HAVE_SETLOCALE) && defined(LC_CTYPE)
+ if (old_lc)
+ {
+ setlocale (LC_CTYPE, old_lc);
+ xfree (old_lc);
+ }
+#endif
+ if (rc)
+ return rc;
+
+#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES)
+ old_lc = setlocale (LC_MESSAGES, NULL);
+ if (old_lc)
+ old_lc = xstrdup (old_lc);
+ dft_lc = setlocale (LC_MESSAGES, "");
+#endif
+ if (opt.lc_messages || (dft_ttyname && dft_lc))
+ {
+ rc = agent_send_option (ctx, "lc-messages",
+ opt.lc_messages ? opt.lc_messages : dft_lc);
+ }
+#if defined(HAVE_SETLOCALE) && defined(LC_MESSAGES)
+ if (old_lc)
+ {
+ setlocale (LC_MESSAGES, old_lc);
+ xfree (old_lc);
+ }
+#endif
+ return rc;
+}
+#endif /*ENABLE_AGENT_SUPPORT*/
+
+
+/*
+ * Open a connection to the agent and initializes the connection.
+ * Returns: -1 on error; on success an Assuan context for that
+ * connection is returned. With TRY set to true, no error messages
+ * are printed and the use of the agent won't get disabled on failure.
+ * If ORIG_CODESET is not NULL, the function will swithc the codeset
+ * back to that one before printing error messages.
+ */
+#ifdef ENABLE_AGENT_SUPPORT
+assuan_context_t
+agent_open (int try, const char *orig_codeset)
+{
+ int rc;
+ assuan_context_t ctx;
+ char *infostr, *p;
+ int prot;
+ int pid;
+
+ if (opt.gpg_agent_info)
+ infostr = xstrdup (opt.gpg_agent_info);
+ else
+ {
+ infostr = getenv ( "GPG_AGENT_INFO" );
+ if (!infostr || !*infostr)
+ {
+ if (!try)
+ {
+#ifdef ENABLE_NLS
+ if (orig_codeset)
+ bind_textdomain_codeset (PACKAGE, orig_codeset);
+#endif /*ENABLE_NLS*/
+ log_info (_("gpg-agent is not available in this session\n"));
+ opt.use_agent = 0;
+ }
+ return NULL;
+ }
+ infostr = xstrdup ( infostr );
+ }
+
+ if ( !(p = strchr (infostr, PATHSEP_C)) || p == infostr)
+ {
+ if (!try)
+ {
+#ifdef ENABLE_NLS
+ if (orig_codeset)
+ bind_textdomain_codeset (PACKAGE, orig_codeset);
+#endif /*ENABLE_NLS*/
+ log_error ( _("malformed GPG_AGENT_INFO environment variable\n"));
+ opt.use_agent = 0;
+ }
+ xfree (infostr);
+ return NULL;
+ }
+ *p++ = 0;
+ pid = atoi (p);
+ while (*p && *p != PATHSEP_C)
+ p++;
+ prot = *p? atoi (p+1) : 0;
+ if (prot != 1)
+ {
+ if (!try)
+ {
+#ifdef ENABLE_NLS
+ if (orig_codeset)
+ bind_textdomain_codeset (PACKAGE, orig_codeset);
+#endif /*ENABLE_NLS*/
+ log_error (_("gpg-agent protocol version %d is not supported\n"),
+ prot);
+ opt.use_agent = 0;
+ }
+ xfree (infostr);
+ return NULL;
+ }
+
+ rc = assuan_socket_connect (&ctx, infostr, pid);
+ if (rc)
+ {
+ if (!try)
+ {
+#ifdef ENABLE_NLS
+ if (orig_codeset)
+ bind_textdomain_codeset (PACKAGE, orig_codeset);
+#endif /*ENABLE_NLS*/
+ log_info ( _("can't connect to `%s': %s\n"),
+ infostr, assuan_strerror (rc));
+ opt.use_agent = 0;
+ }
+ xfree (infostr );
+ return NULL;
+ }
+ xfree (infostr);
+
+ if (agent_send_all_options (ctx))
+ {
+ if (!try)
+ {
+#ifdef ENABLE_NLS
+ if (orig_codeset)
+ bind_textdomain_codeset (PACKAGE, orig_codeset);
+#endif /*ENABLE_NLS*/
+ log_error (_("problem with the agent - disabling agent use\n"));
+ opt.use_agent = 0;
+ }
+ assuan_disconnect (ctx);
+ return NULL;
+ }
+
+ return ctx;
+}
+#endif/*ENABLE_AGENT_SUPPORT*/
+
+
+#ifdef ENABLE_AGENT_SUPPORT
+void
+agent_close (assuan_context_t ctx)
+{
+ assuan_disconnect (ctx);
+}
+#endif /*ENABLE_AGENT_SUPPORT*/
+
+
+/* Copy the text ATEXT into the buffer P and do plus '+' and percent
+ escaping. Note that the provided buffer needs to be 3 times the
+ size of ATEXT plus 1. Returns a pointer to the leading Nul in P. */
+#ifdef ENABLE_AGENT_SUPPORT
+static char *
+percent_plus_escape (char *p, const char *atext)
+{
+ const unsigned char *s;
+
+ for (s=atext; *s; s++)
+ {
+ if (*s < ' ' || *s == '+')
+ {
+ sprintf (p, "%%%02X", *s);
+ p += 3;
+ }
+ else if (*s == ' ')
+ *p++ = '+';
+ else
+ *p++ = *s;
+ }
+ *p = 0;
+ return p;
+}
+#endif /*ENABLE_AGENT_SUPPORT*/
+
+
+#ifdef ENABLE_AGENT_SUPPORT
+
+/* Object for the agent_okay_cb function. */
+struct agent_okay_cb_s {
+ char *pw;
+};
+
+/* A callback used to get the passphrase from the okay line. See
+ agent-get_passphrase for details. LINE is the rest of the OK
+ status line without leading white spaces. */
+static assuan_error_t
+agent_okay_cb (void *opaque, const char *line)
+{
+ struct agent_okay_cb_s *parm = opaque;
+ int i;
+
+ /* Note: If the malloc below fails we won't be able to wipe the
+ memory at LINE given the current implementation of the Assuan
+ code. There is no easy ay around this w/o adding a lot of more
+ memory function code to allow wiping arbitrary stuff on memory
+ failure. */
+ parm->pw = xmalloc_secure (strlen (line)/2+2);
+
+ for (i=0; hexdigitp (line) && hexdigitp (line+1); line += 2)
+ parm->pw[i++] = xtoi_2 (line);
+ parm->pw[i] = 0;
+ return 0;
+}
+#endif /*ENABLE_AGENT_SUPPORT*/
+
+
+
+/*
+ * Ask the GPG Agent for the passphrase.
+ * Mode 0: Allow cached passphrase
+ * 1: No cached passphrase FIXME: Not really implemented
+ * 2: Ditto, but change the text to "repeat entry"
+ *
+ * Note that TRYAGAIN_TEXT must not be translated. If canceled is not
+ * NULL, the function does set it to 1 if the user canceled the
+ * operation. If CACHEID is not NULL, it will be used as the cacheID
+ * for the gpg-agent; if is NULL and a key fingerprint can be
+ * computed, this will be used as the cacheid.
+ */
+static char *
+agent_get_passphrase ( u32 *keyid, int mode, const char *cacheid,
+ const char *tryagain_text,
+ const char *custom_description,
+ const char *custom_prompt, int *canceled)
+{
+#ifdef ENABLE_AGENT_SUPPORT
+ char *atext = NULL;
+ assuan_context_t ctx = NULL;
+ char *pw = NULL;
+ PKT_public_key *pk = xmalloc_clear( sizeof *pk );
+ byte fpr[MAX_FINGERPRINT_LEN];
+ int have_fpr = 0;
+ char *orig_codeset = NULL;
+
+ if (canceled)
+ *canceled = 0;
+
+#if MAX_FINGERPRINT_LEN < 20
+#error agent needs a 20 byte fingerprint
+#endif
+
+ memset (fpr, 0, MAX_FINGERPRINT_LEN );
+ if( keyid && get_pubkey( pk, keyid ) )
+ {
+ if (pk)
+ free_public_key( pk );
+ pk = NULL; /* oops: no key for some reason */
+ }
+
+#ifdef ENABLE_NLS
+ /* The Assuan agent protocol requires us to transmit utf-8 strings */
+ orig_codeset = bind_textdomain_codeset (PACKAGE, NULL);
+#ifdef HAVE_LANGINFO_CODESET
+ if (!orig_codeset)
+ orig_codeset = nl_langinfo (CODESET);
+#endif
+ if (orig_codeset)
+ { /* We only switch when we are able to restore the codeset later. */
+ orig_codeset = xstrdup (orig_codeset);
+ if (!bind_textdomain_codeset (PACKAGE, "utf-8"))
+ orig_codeset = NULL;
+ }
+#endif
+
+ if ( !(ctx = agent_open (0, orig_codeset)) )
+ goto failure;
+
+ if (custom_description)
+ atext = native_to_utf8 (custom_description);
+ else if ( !mode && pk && keyid )
+ {
+ char *uid;
+ size_t uidlen;
+ const char *algo_name = pubkey_algo_to_string ( pk->pubkey_algo );
+ const char *timestr;
+ char *maink;
+
+ if ( !algo_name )
+ algo_name = "?";
+
+#define KEYIDSTRING _(" (main key ID %s)")
+
+ maink = xmalloc ( strlen (KEYIDSTRING) + keystrlen() + 20 );
+ if( keyid[2] && keyid[3] && keyid[0] != keyid[2]
+ && keyid[1] != keyid[3] )
+ sprintf( maink, KEYIDSTRING, keystr(&keyid[2]) );
+ else
+ *maink = 0;
+
+ uid = get_user_id ( keyid, &uidlen );
+ timestr = strtimestamp (pk->timestamp);
+
+#undef KEYIDSTRING
+
+#define PROMPTSTRING _("You need a passphrase to unlock the secret" \
+ " key for user:\n" \
+ "\"%.*s\"\n" \
+ "%u-bit %s key, ID %s, created %s%s\n" )
+
+ atext = xmalloc ( 100 + strlen (PROMPTSTRING)
+ + uidlen + 15 + strlen(algo_name) + keystrlen()
+ + strlen (timestr) + strlen (maink) );
+ sprintf (atext, PROMPTSTRING,
+ (int)uidlen, uid,
+ nbits_from_pk (pk), algo_name, keystr(&keyid[0]), timestr,
+ maink );
+ xfree (uid);
+ xfree (maink);
+
+#undef PROMPTSTRING
+
+ {
+ size_t dummy;
+ fingerprint_from_pk( pk, fpr, &dummy );
+ have_fpr = 1;
+ }
+
+ }
+ else if (mode == 2 )
+ atext = xstrdup ( _("Repeat passphrase\n") );
+ else
+ atext = xstrdup ( _("Enter passphrase\n") );
+
+ {
+ char *line, *p;
+ int i, rc;
+ struct agent_okay_cb_s okay_cb_parm;
+
+ if (!tryagain_text)
+ tryagain_text = "X";
+ else
+ tryagain_text = _(tryagain_text);
+
+ /* We allocate 23 times the needed space for thye texts so that
+ there is enough space for escaping. */
+ line = xmalloc (15 + 46
+ + 3*strlen (atext)
+ + 3*strlen (custom_prompt? custom_prompt:"")
+ + (cacheid? (3*strlen (cacheid)): 0)
+ + 3*strlen (tryagain_text)
+ + 1);
+ strcpy (line, "GET_PASSPHRASE ");
+ p = line+15;
+ if (!mode && cacheid)
+ {
+ p = percent_plus_escape (p, cacheid);
+ }
+ else if (!mode && have_fpr)
+ {
+ for (i=0; i < 20; i++, p +=2 )
+ sprintf (p, "%02X", fpr[i]);
+ }
+ else
+ *p++ = 'X'; /* No caching. */
+ *p++ = ' ';
+
+ p = percent_plus_escape (p, tryagain_text);
+ *p++ = ' ';
+
+ /* The prompt. */
+ if (custom_prompt)
+ {
+ char *tmp = native_to_utf8 (custom_prompt);
+ p = percent_plus_escape (p, tmp);
+ xfree (tmp);
+ }
+ else
+ *p++ = 'X'; /* Use the standard prompt. */
+ *p++ = ' ';
+
+ /* Copy description. */
+ percent_plus_escape (p, atext);
+
+ /* Call gpg-agent. */
+ memset (&okay_cb_parm, 0, sizeof okay_cb_parm);
+ rc = assuan_transact2 (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL,
+ agent_okay_cb, &okay_cb_parm);
+
+ xfree (line);
+ xfree (atext); atext = NULL;
+ if (!rc)
+ {
+ assert (okay_cb_parm.pw);
+ pw = okay_cb_parm.pw;
+ agent_close (ctx);
+ if (pk)
+ free_public_key( pk );
+#ifdef ENABLE_NLS
+ if (orig_codeset)
+ bind_textdomain_codeset (PACKAGE, orig_codeset);
+#endif
+ xfree (orig_codeset);
+ return pw;
+ }
+ else if (rc && (rc & 0xffff) == 99)
+ {
+ /* 99 is GPG_ERR_CANCELED. */
+ log_info (_("cancelled by user\n") );
+ if (canceled)
+ *canceled = 1;
+ }
+ else
+ {
+ log_error (_("problem with the agent - disabling agent use\n"));
+ opt.use_agent = 0;
+ }
+ }
+
+
+ failure:
+#ifdef ENABLE_NLS
+ if (orig_codeset)
+ {
+ bind_textdomain_codeset (PACKAGE, orig_codeset);
+ xfree (orig_codeset);
+ }
+#endif
+ xfree (atext);
+ agent_close (ctx);
+ xfree (pw );
+ if (pk)
+ free_public_key( pk );
+
+#endif /*ENABLE_AGENT_SUPPORT*/
+
+ return NULL;
+}
+
+
+/*
+ * Clear the cached passphrase. If CACHEID is not NULL, it will be
+ * used instead of a cache ID derived from KEYID.
+ */
+void
+passphrase_clear_cache ( u32 *keyid, const char *cacheid, int algo )
+{
+#ifdef ENABLE_AGENT_SUPPORT
+ assuan_context_t ctx = NULL;
+ PKT_public_key *pk;
+ byte fpr[MAX_FINGERPRINT_LEN];
+
+#if MAX_FINGERPRINT_LEN < 20
+#error agent needs a 20 byte fingerprint
+#endif
+
+ if (!opt.use_agent)
+ return;
+
+ if (!cacheid)
+ {
+ pk = xcalloc (1, sizeof *pk);
+ memset (fpr, 0, MAX_FINGERPRINT_LEN );
+ if( !keyid || get_pubkey( pk, keyid ) )
+ {
+ goto failure; /* oops: no key for some reason */
+ }
+
+ {
+ size_t dummy;
+ fingerprint_from_pk( pk, fpr, &dummy );
+ }
+ }
+ else
+ pk = NULL;
+
+ if ( !(ctx = agent_open (0, NULL)) )
+ goto failure;
+
+ {
+ char *line, *p;
+ int i, rc;
+
+ if (cacheid)
+ {
+ line = xmalloc (17 + 3*strlen (cacheid) + 2);
+ strcpy (line, "CLEAR_PASSPHRASE ");
+ p = line+17;
+ p = percent_plus_escape (p, cacheid);
+ }
+ else
+ {
+ line = xmalloc (17 + 40 + 2);
+ strcpy (line, "CLEAR_PASSPHRASE ");
+ p = line+17;
+ for (i=0; i < 20; i++, p +=2 )
+ sprintf (p, "%02X", fpr[i]);
+ }
+ *p = 0;
+
+ rc = assuan_transact (ctx, line, NULL, NULL, NULL, NULL, NULL, NULL);
+ xfree (line);
+ if (rc)
+ {
+ log_error (_("problem with the agent - disabling agent use\n"));
+ opt.use_agent = 0;
+ }
+ }
+
+ failure:
+ agent_close (ctx);
+ if (pk)
+ free_public_key( pk );
+#endif /*ENABLE_AGENT_SUPPORT*/
+}
+
+
+/****************
+ * Ask for a passphrase and return that string.
+ */
+char *
+ask_passphrase (const char *description,
+ const char *tryagain_text,
+ const char *promptid,
+ const char *prompt,
+ const char *cacheid, int *canceled)
+{
+ char *pw = NULL;
+
+ if (canceled)
+ *canceled = 0;
+
+ if (!opt.batch && description)
+ {
+ if (strchr (description, '%'))
+ {
+ char *tmp = unescape_percent_string (description);
+ tty_printf ("\n%s\n", tmp);
+ xfree (tmp);
+ }
+ else
+ tty_printf ("\n%s\n",description);
+ }
+
+ agent_died:
+ if ( opt.use_agent )
+ {
+ pw = agent_get_passphrase (NULL, 0, cacheid,
+ tryagain_text, description, prompt,
+ canceled );
+ if (!pw)
+ {
+ if (!opt.use_agent)
+ goto agent_died;
+ pw = NULL;
+ }
+ }
+ else if (fd_passwd)
+ {
+ pw = xmalloc_secure (strlen(fd_passwd)+1);
+ strcpy (pw, fd_passwd);
+ }
+ else if (opt.batch)
+ {
+ log_error(_("can't query passphrase in batch mode\n"));
+ pw = NULL;
+ }
+ else {
+ if (tryagain_text)
+ tty_printf(_("%s.\n"), tryagain_text);
+ pw = cpr_get_hidden(promptid? promptid : "passphrase.ask",
+ prompt?prompt : _("Enter passphrase: ") );
+ tty_kill_prompt();
+ }
+
+ if (!pw || !*pw)
+ write_status( STATUS_MISSING_PASSPHRASE );
+
+ return pw;
+}
+
+
+/* Return a new DEK object Using the string-to-key sepcifier S2K. Use
+ * KEYID and PUBKEY_ALGO to prompt the user.
+
+ MODE 0: Allow cached passphrase
+ 1: Ignore cached passphrase
+ 2: Ditto, but change the text to "repeat entry"
+*/
+DEK *
+passphrase_to_dek( u32 *keyid, int pubkey_algo,
+ int cipher_algo, STRING2KEY *s2k, int mode,
+ const char *tryagain_text, int *canceled)
+{
+ char *pw = NULL;
+ DEK *dek;
+ STRING2KEY help_s2k;
+
+ if (canceled)
+ *canceled = 0;
+
+ if( !s2k ) {
+ /* This is used for the old rfc1991 mode
+ * Note: This must match the code in encode.c with opt.rfc1991 set */
+ s2k = &help_s2k;
+ s2k->mode = 0;
+ s2k->hash_algo = S2K_DIGEST_ALGO;
+ }
+
+ /* If we do not have a passphrase available in NEXT_PW and status
+ information are request, we print them now. */
+ if( !next_pw && is_status_enabled() ) {
+ char buf[50];
+
+ if( keyid ) {
+ u32 used_kid[2];
+ char *us;
+
+ if( keyid[2] && keyid[3] ) {
+ used_kid[0] = keyid[2];
+ used_kid[1] = keyid[3];
+ }
+ else {
+ used_kid[0] = keyid[0];
+ used_kid[1] = keyid[1];
+ }
+
+ us = get_long_user_id_string( keyid );
+ write_status_text( STATUS_USERID_HINT, us );
+ xfree(us);
+
+ sprintf( buf, "%08lX%08lX %08lX%08lX %d 0",
+ (ulong)keyid[0], (ulong)keyid[1],
+ (ulong)used_kid[0], (ulong)used_kid[1],
+ pubkey_algo );
+
+ write_status_text( STATUS_NEED_PASSPHRASE, buf );
+ }
+ else {
+ sprintf( buf, "%d %d %d", cipher_algo, s2k->mode, s2k->hash_algo );
+ write_status_text( STATUS_NEED_PASSPHRASE_SYM, buf );
+ }
+ }
+
+ /* If we do have a keyID, we do not have a passphrase available in
+ NEXT_PW, we are not running in batch mode and we do not want to
+ ignore the passphrase cache (mode!=1), print a prompt with
+ information on that key. */
+ if( keyid && !opt.batch && !next_pw && mode!=1 ) {
+ PKT_public_key *pk = xmalloc_clear( sizeof *pk );
+ char *p;
+
+ p=get_user_id_native(keyid);
+ tty_printf("\n");
+ tty_printf(_("You need a passphrase to unlock the secret key for\n"
+ "user: \"%s\"\n"),p);
+ xfree(p);
+
+ if( !get_pubkey( pk, keyid ) ) {
+ const char *s = pubkey_algo_to_string( pk->pubkey_algo );
+ tty_printf( _("%u-bit %s key, ID %s, created %s"),
+ nbits_from_pk( pk ), s?s:"?", keystr(keyid),
+ strtimestamp(pk->timestamp) );
+ if( keyid[2] && keyid[3] && keyid[0] != keyid[2]
+ && keyid[1] != keyid[3] )
+ {
+ if(keystrlen()>10)
+ {
+ tty_printf("\n");
+ tty_printf(_(" (subkey on main key ID %s)"),
+ keystr(&keyid[2]) );
+ }
+ else
+ tty_printf( _(" (main key ID %s)"), keystr(&keyid[2]) );
+ }
+ tty_printf("\n");
+ }
+
+ tty_printf("\n");
+ if (pk)
+ free_public_key( pk );
+ }
+
+ agent_died:
+ if( next_pw ) {
+ /* Simply return the passphrase we already have in NEXT_PW. */
+ pw = next_pw;
+ next_pw = NULL;
+ }
+ else if ( opt.use_agent ) {
+ /* Divert to the gpg-agent. */
+ pw = agent_get_passphrase ( keyid, mode == 2? 1: 0, NULL,
+ tryagain_text, NULL, NULL, canceled );
+ if (!pw)
+ {
+ if (!opt.use_agent)
+ goto agent_died;
+ pw = xstrdup ("");
+ }
+ if( *pw && mode == 2 )
+ {
+ int i;
+ for(i=0;i<opt.passwd_repeat;i++)
+ {
+ char *pw2 = agent_get_passphrase ( keyid, 2, NULL, NULL, NULL,
+ NULL, canceled );
+ if (!pw2)
+ {
+ if (!opt.use_agent)
+ {
+ xfree (pw);
+ pw = NULL;
+ goto agent_died;
+ }
+ pw2 = xstrdup ("");
+ }
+ if( strcmp(pw, pw2) )
+ {
+ xfree(pw2);
+ xfree(pw);
+ return NULL;
+ }
+ xfree(pw2);
+ }
+ }
+ }
+ else if( fd_passwd ) {
+ /* Return the passphrase we have store in FD_PASSWD. */
+ pw = xmalloc_secure( strlen(fd_passwd)+1 );
+ strcpy( pw, fd_passwd );
+ }
+ else if( opt.batch )
+ {
+ log_error(_("can't query passphrase in batch mode\n"));
+ pw = xstrdup( "" ); /* return an empty passphrase */
+ }
+ else {
+ /* Read the passphrase from the tty or the command-fd. */
+ pw = cpr_get_hidden("passphrase.enter", _("Enter passphrase: ") );
+ tty_kill_prompt();
+ if( mode == 2 && !cpr_enabled() )
+ {
+ int i;
+ for(i=0;i<opt.passwd_repeat;i++)
+ {
+ char *pw2 = cpr_get_hidden("passphrase.repeat",
+ _("Repeat passphrase: ") );
+ tty_kill_prompt();
+ if( strcmp(pw, pw2) )
+ {
+ xfree(pw2);
+ xfree(pw);
+ return NULL;
+ }
+ xfree(pw2);
+ }
+ }
+ }
+
+ if( !pw || !*pw )
+ write_status( STATUS_MISSING_PASSPHRASE );
+
+ /* Hash the passphrase and store it in a newly allocated DEK
+ object. Keep a copy of the passphrase in LAST_PW for use by
+ get_last_passphrase(). */
+ dek = xmalloc_secure_clear ( sizeof *dek );
+ dek->algo = cipher_algo;
+ if( !*pw && mode == 2 )
+ dek->keylen = 0;
+ else
+ hash_passphrase( dek, pw, s2k, mode==2 );
+ xfree(last_pw);
+ last_pw = pw;
+ return dek;
+}
+
+
+/****************
+ * Hash a passphrase using the supplied s2k. If create is true, create
+ * a new salt or what else must be filled into the s2k for a new key.
+ * always needs: dek->algo, s2k->mode, s2k->hash_algo.
+ */
+static void
+hash_passphrase( DEK *dek, char *pw, STRING2KEY *s2k, int create )
+{
+ MD_HANDLE md;
+ int pass, i;
+ int used = 0;
+ int pwlen = strlen(pw);
+
+ assert( s2k->hash_algo );
+ dek->keylen = cipher_get_keylen( dek->algo ) / 8;
+ if( !(dek->keylen > 0 && dek->keylen <= DIM(dek->key)) )
+ BUG();
+
+ md = md_open( s2k->hash_algo, 1);
+ for(pass=0; used < dek->keylen ; pass++ ) {
+ if( pass ) {
+ md_reset(md);
+ for(i=0; i < pass; i++ ) /* preset the hash context */
+ md_putc(md, 0 );
+ }
+
+ if( s2k->mode == 1 || s2k->mode == 3 ) {
+ int len2 = pwlen + 8;
+ ulong count = len2;
+
+ if( create && !pass ) {
+ randomize_buffer(s2k->salt, 8, 1);
+ if( s2k->mode == 3 )
+ s2k->count = opt.s2k_count;
+ }
+
+ if( s2k->mode == 3 ) {
+ count = S2K_DECODE_COUNT(s2k->count);
+ if( count < len2 )
+ count = len2;
+ }
+ /* a little bit complicated because we need a ulong for count */
+ while( count > len2 ) { /* maybe iterated+salted */
+ md_write( md, s2k->salt, 8 );
+ md_write( md, pw, pwlen );
+ count -= len2;
+ }
+ if( count < 8 )
+ md_write( md, s2k->salt, count );
+ else {
+ md_write( md, s2k->salt, 8 );
+ count -= 8;
+ md_write( md, pw, count );
+ }
+ }
+ else
+ md_write( md, pw, pwlen );
+ md_final( md );
+ i = md_digest_length( s2k->hash_algo );
+ if( i > dek->keylen - used )
+ i = dek->keylen - used;
+ memcpy( dek->key+used, md_read(md, s2k->hash_algo), i );
+ used += i;
+ }
+ md_close(md);
+}
+
diff --git a/g10/photoid.c b/g10/photoid.c
new file mode 100644
index 0000000..3676d38
--- /dev/null
+++ b/g10/photoid.c
@@ -0,0 +1,385 @@
+/* photoid.c - photo ID handling code
+ * Copyright (C) 2001, 2002, 2005 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <errno.h>
+#include <stdio.h>
+#include <string.h>
+#ifdef _WIN32
+# include <windows.h>
+# ifndef VER_PLATFORM_WIN32_WINDOWS
+# define VER_PLATFORM_WIN32_WINDOWS 1
+# endif
+#endif
+#include "packet.h"
+#include "status.h"
+#include "exec.h"
+#include "keydb.h"
+#include "util.h"
+#include "i18n.h"
+#include "iobuf.h"
+#include "memory.h"
+#include "options.h"
+#include "main.h"
+#include "photoid.h"
+#include "ttyio.h"
+
+/* Generate a new photo id packet, or return NULL if canceled */
+PKT_user_id *
+generate_photo_id(PKT_public_key *pk,const char *photo_name)
+{
+ PKT_user_id *uid;
+ int error=1,i;
+ unsigned int len;
+ char *filename;
+ byte *photo=NULL;
+ byte header[16];
+ IOBUF file;
+ int overflow;
+
+ header[0]=0x10; /* little side of photo header length */
+ header[1]=0; /* big side of photo header length */
+ header[2]=1; /* 1 == version of photo header */
+ header[3]=1; /* 1 == JPEG */
+
+ for(i=4;i<16;i++) /* The reserved bytes */
+ header[i]=0;
+
+#define EXTRA_UID_NAME_SPACE 71
+ uid=xmalloc_clear(sizeof(*uid)+71);
+
+ if(photo_name && *photo_name)
+ filename=make_filename(photo_name,(void *)NULL);
+ else
+ {
+ tty_printf(_("\nPick an image to use for your photo ID."
+ " The image must be a JPEG file.\n"
+ "Remember that the image is stored within your public key."
+ " If you use a\n"
+ "very large picture, your key will become very large"
+ " as well!\n"
+ "Keeping the image close to 240x288 is a good size"
+ " to use.\n"));
+ filename=NULL;
+ }
+
+ while(photo==NULL)
+ {
+ if(filename==NULL)
+ {
+ char *tempname;
+
+ tty_printf("\n");
+
+ tty_enable_completion(NULL);
+
+ tempname=cpr_get("photoid.jpeg.add",
+ _("Enter JPEG filename for photo ID: "));
+
+ tty_disable_completion();
+
+ filename=make_filename(tempname,(void *)NULL);
+
+ xfree(tempname);
+
+ if(strlen(filename)==0)
+ goto scram;
+ }
+
+ file=iobuf_open(filename);
+ if (file && is_secured_file (iobuf_get_fd (file)))
+ {
+ iobuf_close (file);
+ file = NULL;
+ errno = EPERM;
+ }
+ if(!file)
+ {
+ log_error(_("unable to open JPEG file `%s': %s\n"),
+ filename,strerror(errno));
+ xfree(filename);
+ filename=NULL;
+ continue;
+ }
+
+
+ len=iobuf_get_filelength(file, &overflow);
+ if(len>6144 || overflow)
+ {
+ tty_printf( _("This JPEG is really large (%d bytes) !\n"),len);
+ if(!cpr_get_answer_is_yes("photoid.jpeg.size",
+ _("Are you sure you want to use it? (y/N) ")))
+ {
+ iobuf_close(file);
+ xfree(filename);
+ filename=NULL;
+ continue;
+ }
+ }
+
+ photo=xmalloc(len);
+ iobuf_read(file,photo,len);
+ iobuf_close(file);
+
+ /* Is it a JPEG? */
+ if(photo[0]!=0xFF || photo[1]!=0xD8 ||
+ photo[6]!='J' || photo[7]!='F' || photo[8]!='I' || photo[9]!='F')
+ {
+ log_error(_("`%s' is not a JPEG file\n"),filename);
+ xfree(photo);
+ photo=NULL;
+ xfree(filename);
+ filename=NULL;
+ continue;
+ }
+
+ /* Build the packet */
+ build_attribute_subpkt(uid,1,photo,len,header,16);
+ parse_attribute_subpkts(uid);
+ make_attribute_uidname(uid, EXTRA_UID_NAME_SPACE);
+
+ /* Showing the photo is not safe when noninteractive since the
+ "user" may not be able to dismiss a viewer window! */
+ if(opt.command_fd==-1)
+ {
+ show_photos(uid->attribs,uid->numattribs,pk,NULL);
+ switch(cpr_get_answer_yes_no_quit("photoid.jpeg.okay",
+ _("Is this photo correct (y/N/q)? ")))
+ {
+ case -1:
+ goto scram;
+ case 0:
+ free_attributes(uid);
+ xfree(photo);
+ photo=NULL;
+ xfree(filename);
+ filename=NULL;
+ continue;
+ }
+ }
+ }
+
+ error=0;
+ uid->ref=1;
+
+ scram:
+ xfree(filename);
+ xfree(photo);
+
+ if(error)
+ {
+ free_attributes(uid);
+ xfree(uid);
+ return NULL;
+ }
+
+ return uid;
+}
+
+/* Returns 0 for error, 1 for valid */
+int parse_image_header(const struct user_attribute *attr,byte *type,u32 *len)
+{
+ u16 headerlen;
+
+ if(attr->len<3)
+ return 0;
+
+ /* For historical reasons (i.e. "oops!"), the header length is
+ little endian. */
+ headerlen=(attr->data[1]<<8) | attr->data[0];
+
+ if(headerlen>attr->len)
+ return 0;
+
+ if(type && attr->len>=4)
+ {
+ if(attr->data[2]==1) /* header version 1 */
+ *type=attr->data[3];
+ else
+ *type=0;
+ }
+
+ *len=attr->len-headerlen;
+
+ if(*len==0)
+ return 0;
+
+ return 1;
+}
+
+/* style==0 for extension, 1 for name, 2 for MIME type. Remember that
+ the "name" style string could be used in a user ID name field, so
+ make sure it is not too big (see parse-packet.c:parse_attribute).
+ Extensions should be 3 characters long for the best cross-platform
+ compatibility. */
+char *image_type_to_string(byte type,int style)
+{
+ char *string;
+
+ switch(type)
+ {
+ case 1: /* jpeg */
+ if(style==0)
+ string="jpg";
+ else if(style==1)
+ string="jpeg";
+ else
+ string="image/jpeg";
+ break;
+
+ default:
+ if(style==0)
+ string="bin";
+ else if(style==1)
+ string="unknown";
+ else
+ string="image/x-unknown";
+ break;
+ }
+
+ return string;
+}
+
+#if !defined(FIXED_PHOTO_VIEWER) && !defined(DISABLE_PHOTO_VIEWER)
+static const char *
+get_default_photo_command(void)
+{
+#if defined(_WIN32)
+ OSVERSIONINFO osvi;
+
+ memset(&osvi,0,sizeof(osvi));
+ osvi.dwOSVersionInfoSize=sizeof(osvi);
+ GetVersionEx(&osvi);
+
+ if(osvi.dwPlatformId==VER_PLATFORM_WIN32_WINDOWS)
+ return "start /w %i";
+ else
+ return "cmd /c start /w %i";
+#elif defined(__APPLE__)
+ /* OS X. This really needs more than just __APPLE__. */
+ return "open %I";
+#elif defined(__riscos__)
+ return "Filer_Run %I";
+#else
+ if(path_access("xloadimage",X_OK)==0)
+ return "xloadimage -fork -quiet -title 'KeyID 0x%k' stdin";
+ else if(path_access("eog",X_OK)==0)
+ return "eog %i";
+ else if(path_access("display",X_OK)==0)
+ return "display -title 'KeyID 0x%k' %i";
+ else
+ return "";
+#endif
+}
+#endif
+
+void show_photos(const struct user_attribute *attrs,
+ int count,PKT_public_key *pk,PKT_secret_key *sk)
+{
+#ifndef DISABLE_PHOTO_VIEWER
+ int i;
+ struct expando_args args;
+ u32 len;
+ u32 kid[2]={0,0};
+
+ memset(&args,0,sizeof(args));
+ args.pk=pk;
+ args.sk=sk;
+
+ if(pk)
+ keyid_from_pk(pk,kid);
+ else if(sk)
+ keyid_from_sk(sk,kid);
+
+ for(i=0;i<count;i++)
+ if(attrs[i].type==ATTRIB_IMAGE &&
+ parse_image_header(&attrs[i],&args.imagetype,&len))
+ {
+ char *command,*name;
+ struct exec_info *spawn;
+ int offset=attrs[i].len-len;
+
+#ifdef FIXED_PHOTO_VIEWER
+ opt.photo_viewer=FIXED_PHOTO_VIEWER;
+#else
+ if(!opt.photo_viewer)
+ opt.photo_viewer=get_default_photo_command();
+#endif
+
+ if(!*opt.photo_viewer)
+ {
+ log_info(_("no photo viewer set\n"));
+ goto fail;
+ }
+
+ /* make command grow */
+ command=pct_expando(opt.photo_viewer,&args);
+ if(!command)
+ goto fail;
+
+ name=xmalloc(16+strlen(EXTSEP_S)+
+ strlen(image_type_to_string(args.imagetype,0))+1);
+
+ /* Make the filename. Notice we are not using the image
+ encoding type for more than cosmetics. Most external image
+ viewers can handle a multitude of types, and even if one
+ cannot understand a particular type, we have no way to know
+ which. The spec permits this, by the way. -dms */
+
+#ifdef USE_ONLY_8DOT3
+ sprintf(name,"%08lX" EXTSEP_S "%s",(ulong)kid[1],
+ image_type_to_string(args.imagetype,0));
+#else
+ sprintf(name,"%08lX%08lX" EXTSEP_S "%s",(ulong)kid[0],(ulong)kid[1],
+ image_type_to_string(args.imagetype,0));
+#endif
+
+ if(exec_write(&spawn,NULL,command,name,1,1)!=0)
+ {
+ xfree(name);
+ goto fail;
+ }
+
+#ifdef __riscos__
+ riscos_set_filetype_by_mimetype(spawn->tempfile_in,
+ image_type_to_string(args.imagetype,2));
+#endif
+
+ xfree(name);
+
+ fwrite(&attrs[i].data[offset],attrs[i].len-offset,1,spawn->tochild);
+
+ if(exec_read(spawn)!=0)
+ {
+ exec_finish(spawn);
+ goto fail;
+ }
+
+ if(exec_finish(spawn)!=0)
+ goto fail;
+ }
+
+ return;
+
+ fail:
+ log_error(_("unable to display photo ID!\n"));
+#endif
+}
diff --git a/g10/photoid.h b/g10/photoid.h
new file mode 100644
index 0000000..d13669c
--- /dev/null
+++ b/g10/photoid.h
@@ -0,0 +1,35 @@
+/* photoid.h
+ * Copyright (C) 2001, 2002 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+/* Photo ID functions */
+
+#ifndef _PHOTOID_H_
+#define _PHOTOID_H_
+
+#include "packet.h"
+
+PKT_user_id *generate_photo_id(PKT_public_key *pk,const char *filename);
+int parse_image_header(const struct user_attribute *attr,byte *type,u32 *len);
+char *image_type_to_string(byte type,int style);
+void show_photos(const struct user_attribute *attrs,
+ int count,PKT_public_key *pk,PKT_secret_key *sk);
+
+#endif /* !_PHOTOID_H_ */
diff --git a/g10/pipemode.c b/g10/pipemode.c
new file mode 100644
index 0000000..750aa3b
--- /dev/null
+++ b/g10/pipemode.c
@@ -0,0 +1,318 @@
+/* pipemode.c - pipemode handler
+ * Copyright (C) 1998, 1990, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "options.h"
+#include "packet.h"
+#include "errors.h"
+#include "iobuf.h"
+#include "keydb.h"
+#include "memory.h"
+#include "util.h"
+#include "main.h"
+#include "status.h"
+#include "filter.h"
+
+
+#define CONTROL_PACKET_SPACE 30
+#define FAKED_LITERAL_PACKET_SPACE (9+2+2)
+
+
+enum pipemode_state_e {
+ STX_init = 0,
+ STX_wait_operation,
+ STX_begin,
+ STX_text,
+ STX_detached_signature,
+ STX_detached_signature_wait_text,
+ STX_signed_data,
+ STX_wait_init
+};
+
+struct pipemode_context_s {
+ enum pipemode_state_e state;
+ int operation;
+ int stop;
+ int block_mode;
+ UnarmorPump unarmor_ctx;
+};
+
+
+static size_t
+make_control ( byte *buf, int code, int operation )
+{
+ const byte *sesmark;
+ size_t sesmarklen, n=0;;
+
+ sesmark = get_session_marker( &sesmarklen );
+ if ( sesmarklen > 20 )
+ BUG();
+
+ buf[n++] = 0xff; /* new format, type 63, 1 length byte */
+ n++; /* length will fixed below */
+ memcpy(buf+n, sesmark, sesmarklen ); n+= sesmarklen;
+ buf[n++] = CTRLPKT_PIPEMODE;
+ buf[n++] = code;
+ buf[n++] = operation;
+ buf[1] = n-2;
+ return n;
+}
+
+
+
+static int
+pipemode_filter( void *opaque, int control,
+ IOBUF a, byte *buf, size_t *ret_len)
+{
+ size_t size = *ret_len;
+ struct pipemode_context_s *stx = opaque;
+ int rc=0;
+ size_t n = 0;
+ int esc = 0;
+
+ if( control == IOBUFCTRL_UNDERFLOW ) {
+ *ret_len = 0;
+ /* reserve some space for one control packet */
+ if ( size <= CONTROL_PACKET_SPACE+FAKED_LITERAL_PACKET_SPACE )
+ BUG();
+ size -= CONTROL_PACKET_SPACE+FAKED_LITERAL_PACKET_SPACE;
+
+ if ( stx->block_mode ) {
+ /* reserve 2 bytes for the block length */
+ buf[n++] = 0;
+ buf[n++] = 0;
+ }
+
+
+ while ( n < size ) {
+ /* FIXME: we have to make sure that we have a large enough
+ * buffer for a control packet even after we already read
+ * something. The easest way to do this is probably by ungetting
+ * the control sequence and returning the buffer we have
+ * already assembled */
+ int c = iobuf_get (a);
+ if (c == -1) {
+ if ( stx->state != STX_init ) {
+ log_error ("EOF encountered at wrong state\n");
+ stx->stop = 1;
+ return -1;
+ }
+ break;
+ }
+ if ( esc ) {
+ switch (c) {
+ case '@':
+ if ( stx->state == STX_text ) {
+ buf[n++] = c;
+ break;
+ }
+ else if ( stx->state == STX_detached_signature ) {
+ esc = 0;
+ goto do_unarmor; /* not a very elegant solution */
+ }
+ else if ( stx->state == STX_detached_signature_wait_text) {
+ esc = 0;
+ break; /* just ignore it in this state */
+ }
+ log_error ("@@ not allowed in current state\n");
+ return -1;
+ case '<': /* begin of stream part */
+ if ( stx->state != STX_init ) {
+ log_error ("nested begin of stream\n");
+ stx->stop = 1;
+ return -1;
+ }
+ stx->state = STX_wait_operation;
+ stx->block_mode = 0;
+ unarmor_pump_release (stx->unarmor_ctx);
+ stx->unarmor_ctx = NULL;
+ break;
+ case '>': /* end of stream part */
+ if ( stx->state != STX_wait_init ) {
+ log_error ("invalid state for @>\n");
+ stx->stop = 1;
+ return -1;
+ }
+ stx->state = STX_init;
+ break;
+ case 'V': /* operation = verify */
+ case 'E': /* operation = encrypt */
+ case 'S': /* operation = sign */
+ case 'B': /* operation = detach sign */
+ case 'C': /* operation = clearsign */
+ case 'D': /* operation = decrypt */
+ if ( stx->state != STX_wait_operation ) {
+ log_error ("invalid state for operation code\n");
+ stx->stop = 1;
+ return -1;
+ }
+ stx->operation = c;
+ if ( stx->operation == 'B') {
+ stx->state = STX_detached_signature;
+ if ( !opt.no_armor )
+ stx->unarmor_ctx = unarmor_pump_new ();
+ }
+ else
+ stx->state = STX_begin;
+ n += make_control ( buf+n, 1, stx->operation );
+ /* must leave after a control packet */
+ goto leave;
+
+ case 't': /* plaintext text follows */
+ if ( stx->state == STX_detached_signature_wait_text )
+ stx->state = STX_detached_signature;
+ if ( stx->state == STX_detached_signature ) {
+ if ( stx->operation != 'B' ) {
+ log_error ("invalid operation for this state\n");
+ stx->stop = 1;
+ return -1;
+ }
+ stx->state = STX_signed_data;
+ n += make_control ( buf+n, 2, 'B' );
+ /* and now we fake a literal data packet much the same
+ * as in armor.c */
+ buf[n++] = 0xaf; /* old packet format, type 11,
+ var length */
+ buf[n++] = 0; /* set the length header */
+ buf[n++] = 6;
+ buf[n++] = 'b'; /* we ignore it anyway */
+ buf[n++] = 0; /* namelength */
+ memset(buf+n, 0, 4); /* timestamp */
+ n += 4;
+ /* and return now so that we are sure to have
+ * more space in the bufer for the next control
+ * packet */
+ stx->block_mode = 1;
+ goto leave2;
+ }
+ else {
+ log_error ("invalid state for @t\n");
+ stx->stop = 1;
+ return -1;
+ }
+ break;
+
+ case '.': /* ready */
+ if ( stx->state == STX_signed_data ) {
+ if (stx->block_mode) {
+ buf[0] = (n-2) >> 8;
+ buf[1] = (n-2);
+ if ( buf[0] || buf[1] ) {
+ /* end of blocks marker */
+ buf[n++] = 0;
+ buf[n++] = 0;
+ }
+ stx->block_mode = 0;
+ }
+ n += make_control ( buf+n, 3, 'B' );
+ }
+ else {
+ log_error ("invalid state for @.\n");
+ stx->stop = 1;
+ return -1;
+ }
+ stx->state = STX_wait_init;
+ goto leave;
+
+ default:
+ log_error ("invalid escape sequence 0x%02x in stream\n",
+ c);
+ stx->stop = 1;
+ return -1;
+ }
+ esc = 0;
+ }
+ else if (c == '@')
+ esc = 1;
+ else if (stx->unarmor_ctx) {
+ do_unarmor: /* used to handle a @@ */
+ c = unarmor_pump (stx->unarmor_ctx, c);
+ if ( !(c & ~255) )
+ buf[n++] = c;
+ else if ( c < 0 ) {
+ /* end of armor or error - we don't care becuase
+ the armor can be modified anyway. The unarmored
+ stuff should stand for itself. */
+ unarmor_pump_release (stx->unarmor_ctx);
+ stx->unarmor_ctx = NULL;
+ stx->state = STX_detached_signature_wait_text;
+ }
+ }
+ else if (stx->state == STX_detached_signature_wait_text)
+ ; /* just wait */
+ else
+ buf[n++] = c;
+ }
+
+ leave:
+ if ( !n ) {
+ stx->stop = 1;
+ rc = -1; /* eof */
+ }
+ if ( stx->block_mode ) {
+ /* fixup the block length */
+ buf[0] = (n-2) >> 8;
+ buf[1] = (n-2);
+ }
+ leave2:
+ /*log_hexdump ("pipemode:", buf, n );*/
+ *ret_len = n;
+ }
+ else if( control == IOBUFCTRL_DESC )
+ *(char**)buf = "pipemode_filter";
+ return rc;
+}
+
+
+
+void
+run_in_pipemode(void)
+{
+ IOBUF fp;
+ armor_filter_context_t afx;
+ struct pipemode_context_s stx;
+ int rc;
+
+ memset( &afx, 0, sizeof afx);
+ memset( &stx, 0, sizeof stx);
+
+ fp = iobuf_open("-");
+ iobuf_push_filter (fp, pipemode_filter, &stx );
+
+ do {
+ write_status (STATUS_BEGIN_STREAM);
+ rc = proc_packets( NULL, fp );
+ write_status (STATUS_END_STREAM);
+ } while ( !stx.stop );
+
+}
+
+
+
+
+
+
diff --git a/g10/pkclist.c b/g10/pkclist.c
new file mode 100644
index 0000000..4c0ffd7
--- /dev/null
+++ b/g10/pkclist.c
@@ -0,0 +1,1448 @@
+/* pkclist.c
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "options.h"
+#include "packet.h"
+#include "errors.h"
+#include "keydb.h"
+#include "memory.h"
+#include "util.h"
+#include "main.h"
+#include "trustdb.h"
+#include "ttyio.h"
+#include "status.h"
+#include "photoid.h"
+#include "i18n.h"
+
+#define CONTROL_D ('D' - 'A' + 1)
+
+/****************
+ * Show the revocation reason as it is stored with the given signature
+ */
+static void
+do_show_revocation_reason( PKT_signature *sig )
+{
+ size_t n, nn;
+ const byte *p, *pp;
+ int seq = 0;
+ const char *text;
+
+ while( (p = enum_sig_subpkt (sig->hashed, SIGSUBPKT_REVOC_REASON,
+ &n, &seq, NULL )) ) {
+ if( !n )
+ continue; /* invalid - just skip it */
+
+ if( *p == 0 )
+ text = _("No reason specified");
+ else if( *p == 0x01 )
+ text = _("Key is superseded");
+ else if( *p == 0x02 )
+ text = _("Key has been compromised");
+ else if( *p == 0x03 )
+ text = _("Key is no longer used");
+ else if( *p == 0x20 )
+ text = _("User ID is no longer valid");
+ else
+ text = NULL;
+
+ log_info( _("reason for revocation: ") );
+ if( text )
+ fputs( text, log_stream() );
+ else
+ fprintf( log_stream(), "code=%02x", *p );
+ putc( '\n', log_stream() );
+ n--; p++;
+ pp = NULL;
+ do {
+ /* We don't want any empty lines, so skip them */
+ while( n && *p == '\n' ) {
+ p++;
+ n--;
+ }
+ if( n ) {
+ pp = memchr( p, '\n', n );
+ nn = pp? pp - p : n;
+ log_info( _("revocation comment: ") );
+ print_string( log_stream(), p, nn, 0 );
+ putc( '\n', log_stream() );
+ p += nn; n -= nn;
+ }
+ } while( pp );
+ }
+}
+
+/* Mode 0: try and find the revocation based on the pk (i.e. check
+ subkeys, etc.) Mode 1: use only the revocation on the main pk */
+
+void
+show_revocation_reason( PKT_public_key *pk, int mode )
+{
+ /* Hmmm, this is not so easy becuase we have to duplicate the code
+ * used in the trustbd to calculate the keyflags. We need to find
+ * a clean way to check revocation certificates on keys and
+ * signatures. And there should be no duplicate code. Because we
+ * enter this function only when the trustdb told us that we have
+ * a revoked key, we could simply look for a revocation cert and
+ * display this one, when there is only one. Let's try to do this
+ * until we have a better solution. */
+ KBNODE node, keyblock = NULL;
+ byte fingerprint[MAX_FINGERPRINT_LEN];
+ size_t fingerlen;
+ int rc;
+
+ /* get the keyblock */
+ fingerprint_from_pk( pk, fingerprint, &fingerlen );
+ rc = get_keyblock_byfprint( &keyblock, fingerprint, fingerlen );
+ if( rc ) { /* that should never happen */
+ log_debug( "failed to get the keyblock\n");
+ return;
+ }
+
+ for( node=keyblock; node; node = node->next ) {
+ if( (mode && node->pkt->pkttype == PKT_PUBLIC_KEY) ||
+ ( ( node->pkt->pkttype == PKT_PUBLIC_KEY
+ || node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+ && !cmp_public_keys( node->pkt->pkt.public_key, pk ) ) )
+ break;
+ }
+ if( !node ) {
+ log_debug("Oops, PK not in keyblock\n");
+ release_kbnode( keyblock );
+ return;
+ }
+ /* now find the revocation certificate */
+ for( node = node->next; node ; node = node->next ) {
+ if( node->pkt->pkttype == PKT_PUBLIC_SUBKEY )
+ break;
+ if( node->pkt->pkttype == PKT_SIGNATURE
+ && (node->pkt->pkt.signature->sig_class == 0x20
+ || node->pkt->pkt.signature->sig_class == 0x28 ) ) {
+ /* FIXME: we should check the signature here */
+ do_show_revocation_reason ( node->pkt->pkt.signature );
+ break;
+ }
+ }
+
+ /* We didn't find it, so check if the whole key is revoked */
+ if(!node && !mode)
+ show_revocation_reason(pk,1);
+
+ release_kbnode( keyblock );
+}
+
+
+/****************
+ * mode: 0 = standard
+ * 1 = Without key info and additional menu option 'm'
+ * this does also add an option to set the key to ultimately trusted.
+ * Returns:
+ * -2 = nothing changed - caller should show some additional info
+ * -1 = quit operation
+ * 0 = nothing changed
+ * 1 = new ownertrust now in new_trust
+ */
+static int
+do_edit_ownertrust (PKT_public_key *pk, int mode,
+ unsigned *new_trust, int defer_help )
+{
+ char *p;
+ u32 keyid[2];
+ int changed=0;
+ int quit=0;
+ int show=0;
+ int min_num;
+ int did_help=defer_help;
+ unsigned int minimum=get_min_ownertrust(pk);
+
+ switch(minimum)
+ {
+ default:
+ case TRUST_UNDEFINED: min_num=1; break;
+ case TRUST_NEVER: min_num=2; break;
+ case TRUST_MARGINAL: min_num=3; break;
+ case TRUST_FULLY: min_num=4; break;
+ }
+
+ keyid_from_pk (pk, keyid);
+ for(;;) {
+ /* A string with valid answers.
+
+ Note to translators: These are the allowed answers in lower and
+ uppercase. Below you will find the matching strings which
+ should be translated accordingly and the letter changed to
+ match the one in the answer string.
+
+ i = please show me more information
+ m = back to the main menu
+ s = skip this key
+ q = quit
+ */
+ const char *ans = _("iImMqQsS");
+
+ if( !did_help )
+ {
+ if( !mode )
+ {
+ KBNODE keyblock, un;
+
+ tty_printf(_("No trust value assigned to:\n"));
+ tty_printf("%4u%c/%s %s\n",nbits_from_pk( pk ),
+ pubkey_letter( pk->pubkey_algo ),
+ keystr(keyid), datestr_from_pk( pk ) );
+ p=get_user_id_native(keyid);
+ tty_printf(_(" \"%s\"\n"),p);
+ xfree(p);
+
+ keyblock = get_pubkeyblock (keyid);
+ if (!keyblock)
+ BUG ();
+ for (un=keyblock; un; un = un->next)
+ {
+ if (un->pkt->pkttype != PKT_USER_ID )
+ continue;
+ if (un->pkt->pkt.user_id->is_revoked )
+ continue;
+ if (un->pkt->pkt.user_id->is_expired )
+ continue;
+ /* Only skip textual primaries */
+ if (un->pkt->pkt.user_id->is_primary
+ && !un->pkt->pkt.user_id->attrib_data )
+ continue;
+
+ if((opt.verify_options&VERIFY_SHOW_PHOTOS)
+ && un->pkt->pkt.user_id->attrib_data)
+ show_photos(un->pkt->pkt.user_id->attribs,
+ un->pkt->pkt.user_id->numattribs,pk,NULL);
+
+ p=utf8_to_native(un->pkt->pkt.user_id->name,
+ un->pkt->pkt.user_id->len,0);
+
+ tty_printf(_(" aka \"%s\"\n"),p);
+ }
+
+ print_fingerprint (pk, NULL, 2);
+ tty_printf("\n");
+ release_kbnode (keyblock);
+ }
+
+ if(opt.trust_model==TM_DIRECT)
+ {
+ tty_printf(_("How much do you trust that this key actually "
+ "belongs to the named user?\n"));
+ tty_printf("\n");
+ }
+ else
+ {
+ /* This string also used in keyedit.c:trustsig_prompt */
+ tty_printf(_("Please decide how far you trust this user to"
+ " correctly verify other users' keys\n"
+ "(by looking at passports, checking fingerprints from"
+ " different sources, etc.)\n"));
+ tty_printf("\n");
+ }
+
+ if(min_num<=1)
+ tty_printf (_(" %d = I don't know or won't say\n"), 1);
+ if(min_num<=2)
+ tty_printf (_(" %d = I do NOT trust\n"), 2);
+ if(min_num<=3)
+ tty_printf (_(" %d = I trust marginally\n"), 3);
+ if(min_num<=4)
+ tty_printf (_(" %d = I trust fully\n"), 4);
+ if (mode)
+ tty_printf (_(" %d = I trust ultimately\n"), 5);
+#if 0
+ /* not yet implemented */
+ tty_printf (" i = please show me more information\n");
+#endif
+ if( mode )
+ tty_printf(_(" m = back to the main menu\n"));
+ else
+ {
+ tty_printf(_(" s = skip this key\n"));
+ tty_printf(_(" q = quit\n"));
+ }
+ tty_printf("\n");
+ if(minimum)
+ tty_printf(_("The minimum trust level for this key is: %s\n\n"),
+ trust_value_to_string(minimum));
+ did_help = 1;
+ }
+ if( strlen(ans) != 8 )
+ BUG();
+ p = cpr_get("edit_ownertrust.value",_("Your decision? "));
+ trim_spaces(p);
+ cpr_kill_prompt();
+ if( !*p )
+ did_help = 0;
+ else if( *p && p[1] )
+ ;
+ else if( !p[1] && ((*p >= '0'+min_num) && *p <= (mode?'5':'4')) )
+ {
+ unsigned int trust;
+ switch( *p )
+ {
+ case '1': trust = TRUST_UNDEFINED; break;
+ case '2': trust = TRUST_NEVER ; break;
+ case '3': trust = TRUST_MARGINAL ; break;
+ case '4': trust = TRUST_FULLY ; break;
+ case '5': trust = TRUST_ULTIMATE ; break;
+ default: BUG();
+ }
+ if (trust == TRUST_ULTIMATE
+ && !cpr_get_answer_is_yes ("edit_ownertrust.set_ultimate.okay",
+ _("Do you really want to set this key"
+ " to ultimate trust? (y/N) ")))
+ ; /* no */
+ else
+ {
+ *new_trust = trust;
+ changed = 1;
+ break;
+ }
+ }
+#if 0
+ /* not yet implemented */
+ else if( *p == ans[0] || *p == ans[1] )
+ {
+ tty_printf(_("Certificates leading to an ultimately trusted key:\n"));
+ show = 1;
+ break;
+ }
+#endif
+ else if( mode && (*p == ans[2] || *p == ans[3] || *p == CONTROL_D ) )
+ {
+ break ; /* back to the menu */
+ }
+ else if( !mode && (*p == ans[6] || *p == ans[7] ) )
+ {
+ break; /* skip */
+ }
+ else if( !mode && (*p == ans[4] || *p == ans[5] ) )
+ {
+ quit = 1;
+ break ; /* back to the menu */
+ }
+ xfree(p); p = NULL;
+ }
+ xfree(p);
+ return show? -2: quit? -1 : changed;
+}
+
+/*
+ * Display a menu to change the ownertrust of the key PK (which should
+ * be a primary key).
+ * For mode values see do_edit_ownertrust ()
+ */
+int
+edit_ownertrust (PKT_public_key *pk, int mode )
+{
+ unsigned int trust = 0; /* Needs to be initialized to avoid gcc warning. */
+ int no_help = 0;
+
+ for(;;)
+ {
+ switch ( do_edit_ownertrust (pk, mode, &trust, no_help ) )
+ {
+ case -1: /* quit */
+ return -1;
+ case -2: /* show info */
+ no_help = 1;
+ break;
+ case 1: /* trust value set */
+ trust &= ~TRUST_FLAG_DISABLED;
+ trust |= get_ownertrust (pk) & TRUST_FLAG_DISABLED;
+ update_ownertrust (pk, trust );
+ return 1;
+ default:
+ return 0;
+ }
+ }
+}
+
+
+/****************
+ * Check whether we can trust this pk which has a trustlevel of TRUSTLEVEL
+ * Returns: true if we trust.
+ */
+static int
+do_we_trust( PKT_public_key *pk, unsigned int trustlevel )
+{
+ /* We should not be able to get here with a revoked or expired
+ key */
+ if(trustlevel & TRUST_FLAG_REVOKED
+ || trustlevel & TRUST_FLAG_SUB_REVOKED
+ || (trustlevel & TRUST_MASK) == TRUST_EXPIRED)
+ BUG();
+
+ if( opt.trust_model==TM_ALWAYS )
+ {
+ if( opt.verbose )
+ log_info("No trust check due to `--trust-model always' option\n");
+ return 1;
+ }
+
+ switch(trustlevel & TRUST_MASK)
+ {
+ default:
+ log_error ("invalid trustlevel %u returned from validation layer\n",
+ trustlevel);
+ /* fall thru */
+ case TRUST_UNKNOWN:
+ case TRUST_UNDEFINED:
+ log_info(_("%s: There is no assurance this key belongs"
+ " to the named user\n"),keystr_from_pk(pk));
+ return 0; /* no */
+
+ case TRUST_MARGINAL:
+ log_info(_("%s: There is limited assurance this key belongs"
+ " to the named user\n"),keystr_from_pk(pk));
+ return 1; /* yes */
+
+ case TRUST_FULLY:
+ if( opt.verbose )
+ log_info(_("This key probably belongs to the named user\n"));
+ return 1; /* yes */
+
+ case TRUST_ULTIMATE:
+ if( opt.verbose )
+ log_info(_("This key belongs to us\n"));
+ return 1; /* yes */
+ }
+
+ return 1; /*NOTREACHED*/
+}
+
+
+/****************
+ * wrapper around do_we_trust, so we can ask whether to use the
+ * key anyway.
+ */
+static int
+do_we_trust_pre( PKT_public_key *pk, unsigned int trustlevel )
+{
+ int rc;
+
+ rc = do_we_trust( pk, trustlevel );
+
+ if( !opt.batch && !rc )
+ {
+ print_pubkey_info(NULL,pk);
+ print_fingerprint (pk, NULL, 2);
+ tty_printf("\n");
+
+ tty_printf(
+ _("It is NOT certain that the key belongs to the person named\n"
+ "in the user ID. If you *really* know what you are doing,\n"
+ "you may answer the next question with yes.\n"));
+
+ tty_printf("\n");
+
+ if( cpr_get_answer_is_yes("untrusted_key.override",
+ _("Use this key anyway? (y/N) ")) )
+ rc = 1;
+
+ /* Hmmm: Should we set a flag to tell the user about
+ * his decision the next time he encrypts for this recipient?
+ */
+ }
+
+ return rc;
+}
+
+
+/****************
+ * Check whether we can trust this signature.
+ * Returns: Error if we shall not trust this signatures.
+ */
+int
+check_signatures_trust( PKT_signature *sig )
+{
+ PKT_public_key *pk = xmalloc_clear( sizeof *pk );
+ unsigned int trustlevel;
+ int rc=0;
+
+ rc = get_pubkey( pk, sig->keyid );
+ if (rc)
+ { /* this should not happen */
+ log_error("Ooops; the key vanished - can't check the trust\n");
+ rc = G10ERR_NO_PUBKEY;
+ goto leave;
+ }
+
+ if ( opt.trust_model==TM_ALWAYS )
+ {
+ if( !opt.quiet )
+ log_info(_("WARNING: Using untrusted key!\n"));
+ if (opt.with_fingerprint)
+ print_fingerprint (pk, NULL, 1);
+ goto leave;
+ }
+
+ if(pk->maybe_revoked && !pk->is_revoked)
+ log_info(_("WARNING: this key might be revoked (revocation key"
+ " not present)\n"));
+
+ trustlevel = get_validity (pk, NULL);
+
+ if ( (trustlevel & TRUST_FLAG_REVOKED) )
+ {
+ write_status( STATUS_KEYREVOKED );
+ if(pk->is_revoked==2)
+ log_info(_("WARNING: This key has been revoked by its"
+ " designated revoker!\n"));
+ else
+ log_info(_("WARNING: This key has been revoked by its owner!\n"));
+ log_info(_(" This could mean that the signature is forged.\n"));
+ show_revocation_reason( pk, 0 );
+ }
+ else if ((trustlevel & TRUST_FLAG_SUB_REVOKED) )
+ {
+ write_status( STATUS_KEYREVOKED );
+ log_info(_("WARNING: This subkey has been revoked by its owner!\n"));
+ show_revocation_reason( pk, 0 );
+ }
+
+ if ((trustlevel & TRUST_FLAG_DISABLED))
+ log_info (_("Note: This key has been disabled.\n"));
+
+ /* If we have PKA information adjust the trustlevel. */
+ if (sig->pka_info && sig->pka_info->valid)
+ {
+ unsigned char fpr[MAX_FINGERPRINT_LEN];
+ PKT_public_key *primary_pk;
+ size_t fprlen;
+ int okay;
+
+
+ primary_pk = xmalloc_clear (sizeof *primary_pk);
+ get_pubkey (primary_pk, pk->main_keyid);
+ fingerprint_from_pk (primary_pk, fpr, &fprlen);
+ free_public_key (primary_pk);
+
+ if ( fprlen == 20 && !memcmp (sig->pka_info->fpr, fpr, 20) )
+ {
+ okay = 1;
+ write_status_text (STATUS_PKA_TRUST_GOOD, sig->pka_info->email);
+ log_info (_("Note: Verified signer's address is `%s'\n"),
+ sig->pka_info->email);
+ }
+ else
+ {
+ okay = 0;
+ write_status_text (STATUS_PKA_TRUST_BAD, sig->pka_info->email);
+ log_info (_("Note: Signer's address `%s' "
+ "does not match DNS entry\n"), sig->pka_info->email);
+ }
+
+ switch ( (trustlevel & TRUST_MASK) )
+ {
+ case TRUST_UNKNOWN:
+ case TRUST_UNDEFINED:
+ case TRUST_MARGINAL:
+ if (okay && opt.verify_options&VERIFY_PKA_TRUST_INCREASE)
+ {
+ trustlevel = ((trustlevel & ~TRUST_MASK) | TRUST_FULLY);
+ log_info (_("trustlevel adjusted to FULL"
+ " due to valid PKA info\n"));
+ }
+ /* (fall through) */
+ case TRUST_FULLY:
+ if (!okay)
+ {
+ trustlevel = ((trustlevel & ~TRUST_MASK) | TRUST_NEVER);
+ log_info (_("trustlevel adjusted to NEVER"
+ " due to bad PKA info\n"));
+ }
+ break;
+ }
+ }
+
+ /* Now let the user know what up with the trustlevel. */
+ switch ( (trustlevel & TRUST_MASK) )
+ {
+ case TRUST_EXPIRED:
+ log_info(_("Note: This key has expired!\n"));
+ print_fingerprint (pk, NULL, 1);
+ break;
+
+ default:
+ log_error ("invalid trustlevel %u returned from validation layer\n",
+ trustlevel);
+ /* fall thru */
+ case TRUST_UNKNOWN:
+ case TRUST_UNDEFINED:
+ write_status( STATUS_TRUST_UNDEFINED );
+ log_info(_("WARNING: This key is not certified with"
+ " a trusted signature!\n"));
+ log_info(_(" There is no indication that the "
+ "signature belongs to the owner.\n" ));
+ print_fingerprint (pk, NULL, 1);
+ break;
+
+ case TRUST_NEVER:
+ /* currently we won't get that status */
+ write_status( STATUS_TRUST_NEVER );
+ log_info(_("WARNING: We do NOT trust this key!\n"));
+ log_info(_(" The signature is probably a FORGERY.\n"));
+ if (opt.with_fingerprint)
+ print_fingerprint (pk, NULL, 1);
+ rc = G10ERR_BAD_SIGN;
+ break;
+
+ case TRUST_MARGINAL:
+ write_status( STATUS_TRUST_MARGINAL );
+ log_info(_("WARNING: This key is not certified with"
+ " sufficiently trusted signatures!\n"));
+ log_info(_(" It is not certain that the"
+ " signature belongs to the owner.\n" ));
+ print_fingerprint (pk, NULL, 1);
+ break;
+
+ case TRUST_FULLY:
+ write_status( STATUS_TRUST_FULLY );
+ if (opt.with_fingerprint)
+ print_fingerprint (pk, NULL, 1);
+ break;
+
+ case TRUST_ULTIMATE:
+ write_status( STATUS_TRUST_ULTIMATE );
+ if (opt.with_fingerprint)
+ print_fingerprint (pk, NULL, 1);
+ break;
+ }
+
+ leave:
+ free_public_key( pk );
+ return rc;
+}
+
+
+void
+release_pk_list( PK_LIST pk_list )
+{
+ PK_LIST pk_rover;
+
+ for( ; pk_list; pk_list = pk_rover ) {
+ pk_rover = pk_list->next;
+ free_public_key( pk_list->pk );
+ xfree( pk_list );
+ }
+}
+
+
+static int
+key_present_in_pk_list(PK_LIST pk_list, PKT_public_key *pk)
+{
+ for( ; pk_list; pk_list = pk_list->next)
+ if (cmp_public_keys(pk_list->pk, pk) == 0)
+ return 0;
+
+ return -1;
+}
+
+
+/****************
+ * Return a malloced string with a default reciepient if there is any
+ */
+static char *
+default_recipient(void)
+{
+ PKT_secret_key *sk;
+ byte fpr[MAX_FINGERPRINT_LEN+1];
+ size_t n;
+ char *p;
+ int i;
+
+ if( opt.def_recipient )
+ return xstrdup( opt.def_recipient );
+ if( !opt.def_recipient_self )
+ return NULL;
+ sk = xmalloc_clear( sizeof *sk );
+ i = get_seckey_byname( sk, NULL, 0 );
+ if( i ) {
+ free_secret_key( sk );
+ return NULL;
+ }
+ n = MAX_FINGERPRINT_LEN;
+ fingerprint_from_sk( sk, fpr, &n );
+ free_secret_key( sk );
+ p = xmalloc( 2*n+3 );
+ *p++ = '0';
+ *p++ = 'x';
+ for(i=0; i < n; i++ )
+ sprintf( p+2*i, "%02X", fpr[i] );
+ p -= 2;
+ return p;
+}
+
+static int
+expand_id(const char *id,STRLIST *into,unsigned int flags)
+{
+ struct groupitem *groups;
+ int count=0;
+
+ for(groups=opt.grouplist;groups;groups=groups->next)
+ {
+ /* need strcasecmp() here, as this should be localized */
+ if(strcasecmp(groups->name,id)==0)
+ {
+ STRLIST each,sl;
+
+ /* this maintains the current utf8-ness */
+ for(each=groups->values;each;each=each->next)
+ {
+ sl=add_to_strlist(into,each->d);
+ sl->flags=flags;
+ count++;
+ }
+
+ break;
+ }
+ }
+
+ return count;
+}
+
+/* For simplicity, and to avoid potential loops, we only expand once -
+ you can't make an alias that points to an alias. */
+static STRLIST
+expand_group(STRLIST input)
+{
+ STRLIST sl,output=NULL,rover;
+
+ for(rover=input;rover;rover=rover->next)
+ if(expand_id(rover->d,&output,rover->flags)==0)
+ {
+ /* Didn't find any groups, so use the existing string */
+ sl=add_to_strlist(&output,rover->d);
+ sl->flags=rover->flags;
+ }
+
+ return output;
+}
+
+
+/* This is the central function to collect the keys for recipients.
+ It is thus used to prepare a public key encryption. encrypt-to
+ keys, default keys and the keys for the actual recipients are all
+ collected here. When not in batch mode and no recipient has been
+ passed on the commandline, the function will also ask for
+ recipients.
+
+ RCPTS is a string list with the recipients; NULL is an allowed
+ value but not very useful. Group expansion is done on these names;
+ they may be in any of the user Id formats we can handle. The flags
+ bits for each string in the string list are used for:
+ Bit 0: This is an encrypt-to recipient.
+ Bit 1: This is a hidden recipient.
+
+ USE is the desired use for the key - usually PUBKEY_USAGE_ENC.
+ RET_PK_LIST.
+
+ On success a list of keys is stored at the address RET_PK_LIST; the
+ caller must free this list. On error the value at this address is
+ not changed.
+ */
+int
+build_pk_list( STRLIST rcpts, PK_LIST *ret_pk_list, unsigned int use )
+{
+ PK_LIST pk_list = NULL;
+ PKT_public_key *pk=NULL;
+ int rc=0;
+ int any_recipients=0;
+ STRLIST rov,remusr;
+ char *def_rec = NULL;
+
+ /* Try to expand groups if any have been defined. */
+ if (opt.grouplist)
+ remusr = expand_group (rcpts);
+ else
+ remusr = rcpts;
+
+ /* Check whether there are any recipients in the list and build the
+ * list of the encrypt-to ones (we always trust them). */
+ for ( rov = remusr; rov; rov = rov->next )
+ {
+ if ( !(rov->flags & 1) )
+ {
+ /* This is a regular recipient; i.e. not an encrypt-to
+ one. */
+ any_recipients = 1;
+
+ /* Hidden recipients are not allowed while in PGP mode,
+ issue a warning and switch into GnuPG mode. */
+ if ((rov->flags&2) && (PGP2 || PGP6 || PGP7 || PGP8))
+ {
+ log_info(_("you may not use %s while in %s mode\n"),
+ "--hidden-recipient",
+ compliance_option_string());
+
+ compliance_failure();
+ }
+ }
+ else if ( (use & PUBKEY_USAGE_ENC) && !opt.no_encrypt_to )
+ {
+ /* Encryption has been requested and --encrypt-to has not
+ been disabled. Check this encrypt-to key. */
+ pk = xmalloc_clear( sizeof *pk );
+ pk->req_usage = use;
+
+ /* We explicitly allow encrypt-to to an disabled key; thus
+ we pass 1 as last argument. */
+ if ( (rc = get_pubkey_byname ( pk, rov->d, NULL, NULL, 1 )) )
+ {
+ free_public_key ( pk ); pk = NULL;
+ log_error (_("%s: skipped: %s\n"), rov->d, g10_errstr(rc) );
+ write_status_text_and_buffer (STATUS_INV_RECP, "0 ",
+ rov->d, strlen (rov->d), -1);
+ goto fail;
+ }
+ else if ( !(rc=check_pubkey_algo2 (pk->pubkey_algo, use )) )
+ {
+ /* Skip the actual key if the key is already present
+ * in the list. Add it to our list if not. */
+ if (key_present_in_pk_list(pk_list, pk) == 0)
+ {
+ free_public_key (pk); pk = NULL;
+ log_info (_("%s: skipped: public key already present\n"),
+ rov->d);
+ }
+ else
+ {
+ PK_LIST r;
+ r = xmalloc( sizeof *r );
+ r->pk = pk; pk = NULL;
+ r->next = pk_list;
+ r->flags = (rov->flags&2)?1:0;
+ pk_list = r;
+
+ /* Hidden encrypt-to recipients are not allowed while
+ in PGP mode, issue a warning and switch into
+ GnuPG mode. */
+ if ((r->flags&1) && (PGP2 || PGP6 || PGP7 || PGP8))
+ {
+ log_info(_("you may not use %s while in %s mode\n"),
+ "--hidden-encrypt-to",
+ compliance_option_string());
+
+ compliance_failure();
+ }
+ }
+ }
+ else
+ {
+ /* The public key is not usable for encryption or not
+ available. */
+ free_public_key( pk ); pk = NULL;
+ log_error(_("%s: skipped: %s\n"), rov->d, g10_errstr(rc) );
+ write_status_text_and_buffer (STATUS_INV_RECP, "0 ",
+ rov->d, strlen (rov->d), -1);
+ goto fail;
+ }
+ }
+ }
+
+ /* If we don't have any recipients yet and we are not in batch mode
+ drop into interactive selection mode. */
+ if ( !any_recipients && !opt.batch )
+ {
+ int have_def_rec;
+ char *answer = NULL;
+ STRLIST backlog = NULL;
+
+ if (pk_list)
+ any_recipients = 1;
+ def_rec = default_recipient();
+ have_def_rec = !!def_rec;
+ if ( !have_def_rec )
+ tty_printf(_("You did not specify a user ID. (you may use \"-r\")\n"));
+
+ for (;;)
+ {
+ rc = 0;
+ xfree(answer);
+ if ( have_def_rec )
+ {
+ /* A default recipient is taken as the first entry. */
+ answer = def_rec;
+ def_rec = NULL;
+ }
+ else if (backlog)
+ {
+ /* This is part of our trick to expand and display groups. */
+ answer = pop_strlist (&backlog);
+ }
+ else
+ {
+ /* Show the list of already collected recipients and ask
+ for more. */
+ PK_LIST iter;
+
+ tty_printf("\n");
+ tty_printf(_("Current recipients:\n"));
+ for (iter=pk_list;iter;iter=iter->next)
+ {
+ u32 keyid[2];
+
+ keyid_from_pk(iter->pk,keyid);
+ tty_printf("%4u%c/%s %s \"",
+ nbits_from_pk(iter->pk),
+ pubkey_letter(iter->pk->pubkey_algo),
+ keystr(keyid),
+ datestr_from_pk(iter->pk));
+
+ if (iter->pk->user_id)
+ tty_print_utf8_string(iter->pk->user_id->name,
+ iter->pk->user_id->len);
+ else
+ {
+ size_t n;
+ char *p = get_user_id( keyid, &n );
+ tty_print_utf8_string( p, n );
+ xfree(p);
+ }
+ tty_printf("\"\n");
+ }
+
+ answer = cpr_get_utf8("pklist.user_id.enter",
+ _("\nEnter the user ID. "
+ "End with an empty line: "));
+ trim_spaces(answer);
+ cpr_kill_prompt();
+ }
+
+ if ( !answer || !*answer )
+ {
+ xfree(answer);
+ break; /* No more recipients entered - get out of loop. */
+ }
+
+ /* Do group expand here too. The trick here is to continue
+ the loop if any expansion occured. The code above will
+ then list all expanded keys. */
+ if (expand_id(answer,&backlog,0))
+ continue;
+
+ /* Get and check key for the current name. */
+ if (pk)
+ free_public_key (pk);
+ pk = xmalloc_clear( sizeof *pk );
+ pk->req_usage = use;
+ rc = get_pubkey_byname( pk, answer, NULL, NULL, 0 );
+ if (rc)
+ tty_printf(_("No such user ID.\n"));
+ else if ( !(rc=check_pubkey_algo2(pk->pubkey_algo, use)) )
+ {
+ if ( have_def_rec )
+ {
+ /* No validation for a default recipient. */
+ if (!key_present_in_pk_list(pk_list, pk))
+ {
+ free_public_key (pk); pk = NULL;
+ log_info (_("skipped: public key "
+ "already set as default recipient\n") );
+ }
+ else
+ {
+ PK_LIST r = xmalloc (sizeof *r);
+ r->pk = pk; pk = NULL;
+ r->next = pk_list;
+ r->flags = 0; /* No throwing default ids. */
+ pk_list = r;
+ }
+ any_recipients = 1;
+ continue;
+ }
+ else
+ { /* Check validity of this key. */
+ int trustlevel;
+
+ trustlevel = get_validity (pk, pk->user_id);
+ if ( (trustlevel & TRUST_FLAG_DISABLED) )
+ {
+ tty_printf (_("Public key is disabled.\n") );
+ }
+ else if ( do_we_trust_pre (pk, trustlevel) )
+ {
+ /* Skip the actual key if the key is already
+ * present in the list */
+ if (!key_present_in_pk_list(pk_list, pk))
+ {
+ free_public_key(pk); pk = NULL;
+ log_info(_("skipped: public key already set\n") );
+ }
+ else
+ {
+ PK_LIST r;
+ r = xmalloc( sizeof *r );
+ r->pk = pk; pk = NULL;
+ r->next = pk_list;
+ r->flags = 0; /* No throwing interactive ids. */
+ pk_list = r;
+ }
+ any_recipients = 1;
+ continue;
+ }
+ }
+ }
+ xfree(def_rec); def_rec = NULL;
+ have_def_rec = 0;
+ }
+ if ( pk )
+ {
+ free_public_key( pk );
+ pk = NULL;
+ }
+ }
+ else if ( !any_recipients && (def_rec = default_recipient()) )
+ {
+ /* We are in batch mode and have only a default recipient. */
+ pk = xmalloc_clear( sizeof *pk );
+ pk->req_usage = use;
+
+ /* The default recipient is allowed to be disabled; thus pass 1
+ as last argument. */
+ rc = get_pubkey_byname (pk, def_rec, NULL, NULL, 1);
+ if (rc)
+ log_error(_("unknown default recipient \"%s\"\n"), def_rec );
+ else if ( !(rc=check_pubkey_algo2(pk->pubkey_algo, use)) )
+ {
+ /* Mark any_recipients here since the default recipient
+ would have been used if it wasn't already there. It
+ doesn't really matter if we got this key from the default
+ recipient or an encrypt-to. */
+ any_recipients = 1;
+ if (!key_present_in_pk_list(pk_list, pk))
+ log_info (_("skipped: public key already set "
+ "as default recipient\n"));
+ else
+ {
+ PK_LIST r = xmalloc( sizeof *r );
+ r->pk = pk; pk = NULL;
+ r->next = pk_list;
+ r->flags = 0; /* No throwing default ids. */
+ pk_list = r;
+ }
+ }
+ if ( pk )
+ {
+ free_public_key( pk );
+ pk = NULL;
+ }
+ xfree(def_rec); def_rec = NULL;
+ }
+ else
+ {
+ /* General case: Check all keys. */
+ any_recipients = 0;
+ for (; remusr; remusr = remusr->next )
+ {
+ if ( (remusr->flags & 1) )
+ continue; /* encrypt-to keys are already handled. */
+
+ pk = xmalloc_clear( sizeof *pk );
+ pk->req_usage = use;
+ if ( (rc = get_pubkey_byname( pk, remusr->d, NULL, NULL, 0 )) )
+ {
+ /* Key not found or other error. */
+ free_public_key( pk ); pk = NULL;
+ log_error(_("%s: skipped: %s\n"), remusr->d, g10_errstr(rc) );
+ write_status_text_and_buffer (STATUS_INV_RECP, "0 ",
+ remusr->d, strlen (remusr->d),
+ -1);
+ goto fail;
+ }
+ else if ( !(rc=check_pubkey_algo2(pk->pubkey_algo, use )) )
+ {
+ /* Key found and usable. Check validity. */
+ int trustlevel;
+
+ trustlevel = get_validity (pk, pk->user_id);
+ if ( (trustlevel & TRUST_FLAG_DISABLED) )
+ {
+ /*Key has been disabled. */
+ free_public_key(pk); pk = NULL;
+ log_info(_("%s: skipped: public key is disabled\n"),
+ remusr->d);
+ write_status_text_and_buffer (STATUS_INV_RECP, "0 ",
+ remusr->d,
+ strlen (remusr->d),
+ -1);
+ rc=G10ERR_UNU_PUBKEY;
+ goto fail;
+ }
+ else if ( do_we_trust_pre( pk, trustlevel ) )
+ {
+ /* Note: do_we_trust may have changed the trustlevel */
+
+ /* We have at least one valid recipient. It doesn't
+ * matters if this recipient is already present. */
+ any_recipients = 1;
+
+ /* Skip the actual key if the key is already present
+ * in the list */
+ if (!key_present_in_pk_list(pk_list, pk))
+ {
+ free_public_key(pk); pk = NULL;
+ log_info(_("%s: skipped: public key already present\n"),
+ remusr->d);
+ }
+ else
+ {
+ PK_LIST r;
+ r = xmalloc( sizeof *r );
+ r->pk = pk; pk = NULL;
+ r->next = pk_list;
+ r->flags = (remusr->flags&2)?1:0;
+ pk_list = r;
+ }
+ }
+ else
+ { /* We don't trust this key. */
+ free_public_key( pk ); pk = NULL;
+ write_status_text_and_buffer (STATUS_INV_RECP, "10 ",
+ remusr->d,
+ strlen (remusr->d),
+ -1);
+ rc=G10ERR_UNU_PUBKEY;
+ goto fail;
+ }
+ }
+ else
+ {
+ /* Key found but not usable for us (e.g. sign-only key). */
+ free_public_key( pk ); pk = NULL;
+ write_status_text_and_buffer (STATUS_INV_RECP, "0 ",
+ remusr->d,
+ strlen (remusr->d),
+ -1);
+ log_error(_("%s: skipped: %s\n"), remusr->d, g10_errstr(rc) );
+ goto fail;
+ }
+ }
+ }
+
+ if ( !rc && !any_recipients )
+ {
+ log_error(_("no valid addressees\n"));
+ write_status_text (STATUS_NO_RECP, "0");
+ rc = G10ERR_NO_USER_ID;
+ }
+
+ fail:
+
+ if ( rc )
+ release_pk_list( pk_list );
+ else
+ *ret_pk_list = pk_list;
+ if (opt.grouplist)
+ free_strlist(remusr);
+ return rc;
+}
+
+
+/* In pgp6 mode, disallow all ciphers except IDEA (1), 3DES (2), and
+ CAST5 (3), all hashes except MD5 (1), SHA1 (2), and RIPEMD160 (3),
+ and all compressions except none (0) and ZIP (1). pgp7 and pgp8
+ mode expands the cipher list to include AES128 (7), AES192 (8),
+ AES256 (9), and TWOFISH (10). pgp8 adds the SHA-256 hash (8). For
+ a true PGP key all of this is unneeded as they are the only items
+ present in the preferences subpacket, but checking here covers the
+ weird case of encrypting to a key that had preferences from a
+ different implementation which was then used with PGP. I am not
+ completely comfortable with this as the right thing to do, as it
+ slightly alters the list of what the user is supposedly requesting.
+ It is not against the RFC however, as the preference chosen will
+ never be one that the user didn't specify somewhere ("The
+ implementation may use any mechanism to pick an algorithm in the
+ intersection"), and PGP has no mechanism to fix such a broken
+ preference list, so I'm including it. -dms */
+
+int
+algo_available( preftype_t preftype, int algo, const union pref_hint *hint )
+{
+ if( preftype == PREFTYPE_SYM )
+ {
+ if(PGP6 && (algo != CIPHER_ALGO_IDEA
+ && algo != CIPHER_ALGO_3DES
+ && algo != CIPHER_ALGO_CAST5))
+ return 0;
+
+ if(PGP7 && (algo != CIPHER_ALGO_IDEA
+ && algo != CIPHER_ALGO_3DES
+ && algo != CIPHER_ALGO_CAST5
+ && algo != CIPHER_ALGO_AES
+ && algo != CIPHER_ALGO_AES192
+ && algo != CIPHER_ALGO_AES256
+ && algo != CIPHER_ALGO_TWOFISH))
+ return 0;
+
+ /* PGP8 supports all the ciphers we do.. */
+
+ return algo && !check_cipher_algo( algo );
+ }
+ else if( preftype == PREFTYPE_HASH )
+ {
+ if(hint && hint->digest_length)
+ {
+ if(hint->digest_length!=20 || opt.flags.dsa2)
+ {
+ /* If --enable-dsa2 is set or the hash isn't 160 bits
+ (which implies DSA2), then we'll accept a hash that
+ is larger than we need. Otherwise we won't accept
+ any hash that isn't exactly the right size. */
+ if(hint->digest_length > md_digest_length(algo))
+ return 0;
+ }
+ else if(hint->digest_length != md_digest_length(algo))
+ return 0;
+ }
+
+ if((PGP6 || PGP7) && (algo != DIGEST_ALGO_MD5
+ && algo != DIGEST_ALGO_SHA1
+ && algo != DIGEST_ALGO_RMD160))
+ return 0;
+
+
+ if(PGP8 && (algo != DIGEST_ALGO_MD5
+ && algo != DIGEST_ALGO_SHA1
+ && algo != DIGEST_ALGO_RMD160
+ && algo != DIGEST_ALGO_SHA256))
+ return 0;
+
+ return algo && !check_digest_algo( algo );
+ }
+ else if( preftype == PREFTYPE_ZIP )
+ {
+ if((PGP6 || PGP7) && (algo != COMPRESS_ALGO_NONE
+ && algo != COMPRESS_ALGO_ZIP))
+ return 0;
+
+ /* PGP8 supports all the compression algos we do */
+
+ return !check_compress_algo( algo );
+ }
+ else
+ return 0;
+}
+
+
+
+/****************
+ * Return -1 if we could not find an algorithm.
+ */
+int
+select_algo_from_prefs(PK_LIST pk_list, int preftype,
+ int request, const union pref_hint *hint)
+{
+ PK_LIST pkr;
+ u32 bits[8];
+ const prefitem_t *prefs;
+ int i, j;
+ int compr_hack=0;
+ int any;
+
+ if( !pk_list )
+ return -1;
+
+ memset( bits, ~0, 8 * sizeof *bits );
+ for( pkr = pk_list; pkr; pkr = pkr->next ) {
+ u32 mask[8];
+
+ memset( mask, 0, 8 * sizeof *mask );
+ if( preftype == PREFTYPE_SYM ) {
+ if( PGP2 &&
+ pkr->pk->version < 4 &&
+ pkr->pk->selfsigversion < 4 )
+ mask[0] |= (1<<1); /* IDEA is implicitly there for v3 keys
+ with v3 selfsigs (rfc2440:12.1) if
+ --pgp2 mode is on. This doesn't
+ mean it's actually available, of
+ course. */
+ else
+ mask[0] |= (1<<2); /* 3DES is implicitly there for everyone else */
+ }
+ else if( preftype == PREFTYPE_HASH ) {
+ /* While I am including this code for completeness, note
+ that currently --pgp2 mode locks the hash at MD5, so this
+ function will never even be called. Even if the hash
+ wasn't locked at MD5, we don't support sign+encrypt in
+ --pgp2 mode, and that's the only time PREFTYPE_HASH is
+ used anyway. -dms */
+ if( PGP2 &&
+ pkr->pk->version < 4 &&
+ pkr->pk->selfsigversion < 4 )
+ mask[0] |= (1<<1); /* MD5 is there for v3 keys with v3
+ selfsigs when --pgp2 is on. */
+ else
+ mask[0] |= (1<<2); /* SHA1 is there for everyone else */
+ }
+ else if( preftype == PREFTYPE_ZIP )
+ mask[0] |= (1<<0); /* Uncompressed is implicit */
+
+ if (pkr->pk->user_id) /* selected by user ID */
+ prefs = pkr->pk->user_id->prefs;
+ else
+ prefs = pkr->pk->prefs;
+
+ any = 0;
+ if( prefs ) {
+ for (i=0; prefs[i].type; i++ ) {
+ if( prefs[i].type == preftype ) {
+ mask[prefs[i].value/32] |= 1 << (prefs[i].value%32);
+ any = 1;
+ }
+ }
+ }
+
+ if( (!prefs || !any) && preftype == PREFTYPE_ZIP ) {
+ mask[0] |= 3; /* asume no_compression and old pgp */
+ compr_hack = 1;
+ }
+
+#if 0
+ log_debug("pref mask=%08lX%08lX%08lX%08lX%08lX%08lX%08lX%08lX\n",
+ (ulong)mask[7], (ulong)mask[6], (ulong)mask[5], (ulong)mask[4],
+ (ulong)mask[3], (ulong)mask[2], (ulong)mask[1], (ulong)mask[0]);
+#endif
+ for(i=0; i < 8; i++ )
+ bits[i] &= mask[i];
+#if 0
+ log_debug("pref bits=%08lX%08lX%08lX%08lX%08lX%08lX%08lX%08lX\n",
+ (ulong)bits[7], (ulong)bits[6], (ulong)bits[5], (ulong)bits[4],
+ (ulong)bits[3], (ulong)bits[2], (ulong)bits[1], (ulong)bits[0]);
+#endif
+ }
+ /* usable algorithms are now in bits
+ * We now use the last key from pk_list to select
+ * the algorithm we want to use. there are no
+ * preferences for the last key, we select the one
+ * corresponding to first set bit.
+ */
+ i = -1;
+ any = 0;
+
+ /* Can we use the requested algorithm? */
+ if(request>-1 && (bits[request/32] & (1<<(request%32))) &&
+ algo_available(preftype,request,hint))
+ return request;
+
+ /* If we have personal prefs set, use them instead of the last key */
+ if(preftype==PREFTYPE_SYM && opt.personal_cipher_prefs)
+ prefs=opt.personal_cipher_prefs;
+ else if(preftype==PREFTYPE_HASH && opt.personal_digest_prefs)
+ prefs=opt.personal_digest_prefs;
+ else if(preftype==PREFTYPE_ZIP && opt.personal_compress_prefs)
+ prefs=opt.personal_compress_prefs;
+
+ if( prefs ) {
+ for(j=0; prefs[j].type; j++ ) {
+ if( prefs[j].type == preftype ) {
+ if( (bits[prefs[j].value/32] & (1<<(prefs[j].value%32))) ) {
+ if( algo_available( preftype, prefs[j].value, hint ) ) {
+ any = 1;
+ i = prefs[j].value;
+ break;
+ }
+ }
+ }
+ }
+ }
+ if( !prefs || !any ) {
+ for(j=0; j < 256; j++ )
+ if( (bits[j/32] & (1<<(j%32))) ) {
+ if( algo_available( preftype, j, hint ) ) {
+ i = j;
+ break;
+ }
+ }
+ }
+
+#if 0
+ log_debug("prefs of type %d: selected %d\n", preftype, i );
+#endif
+ if( compr_hack && !i ) {
+ /* selected no compression, but we should check whether
+ * algorithm 1 is also available (the ordering is not relevant
+ * in this case). */
+ if( bits[0] & (1<<1) )
+ i = 1; /* yep; we can use compression algo 1 */
+ }
+
+ /* "If you are building an authentication system, the recipient
+ may specify a preferred signing algorithm. However, the signer
+ would be foolish to use a weak algorithm simply because the
+ recipient requests it." RFC2440:13. If we settle on MD5, and
+ SHA1 is also available, use SHA1 instead. Of course, if the
+ user intentionally chose MD5 (by putting it in their personal
+ prefs), then we should do what they say. */
+
+ if(preftype==PREFTYPE_HASH &&
+ i==DIGEST_ALGO_MD5 && (bits[0] & (1<<DIGEST_ALGO_SHA1)))
+ {
+ i=DIGEST_ALGO_SHA1;
+
+ if(opt.personal_digest_prefs)
+ for(j=0; prefs[j].type; j++ )
+ if(opt.personal_digest_prefs[j].type==PREFTYPE_HASH &&
+ opt.personal_digest_prefs[j].value==DIGEST_ALGO_MD5)
+ {
+ i=DIGEST_ALGO_MD5;
+ break;
+ }
+ }
+
+ return i;
+}
+
+/*
+ * Select the MDC flag from the pk_list. We can only use MDC if all recipients
+ * support this feature
+ */
+int
+select_mdc_from_pklist (PK_LIST pk_list)
+{
+ PK_LIST pkr;
+
+ if( !pk_list )
+ return 0;
+
+ for (pkr = pk_list; pkr; pkr = pkr->next) {
+ int mdc;
+
+ if (pkr->pk->user_id) /* selected by user ID */
+ mdc = pkr->pk->user_id->flags.mdc;
+ else
+ mdc = pkr->pk->mdc_feature;
+ if (!mdc)
+ return 0; /* at least one recipient does not support it */
+ }
+ return 1; /* can be used */
+}
diff --git a/g10/plaintext.c b/g10/plaintext.c
new file mode 100644
index 0000000..243296b
--- /dev/null
+++ b/g10/plaintext.c
@@ -0,0 +1,589 @@
+/* plaintext.c - process plaintext packets
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#ifdef HAVE_DOSISH_SYSTEM
+#include <fcntl.h> /* for setmode() */
+#endif
+
+#include "util.h"
+#include "memory.h"
+#include "options.h"
+#include "packet.h"
+#include "ttyio.h"
+#include "filter.h"
+#include "main.h"
+#include "status.h"
+#include "i18n.h"
+
+
+/****************
+ * Handle a plaintext packet. If MFX is not NULL, update the MDs
+ * Note: we should use the filter stuff here, but we have to add some
+ * easy mimic to set a read limit, so we calculate only the
+ * bytes from the plaintext.
+ */
+int
+handle_plaintext( PKT_plaintext *pt, md_filter_context_t *mfx,
+ int nooutput, int clearsig )
+{
+ char *fname = NULL;
+ FILE *fp = NULL;
+ static off_t count=0;
+ int rc = 0;
+ int c;
+ int convert = (pt->mode == 't' || pt->mode == 'u');
+#ifdef __riscos__
+ int filetype = 0xfff;
+#endif
+
+ /* Let people know what the plaintext info is. This allows the
+ receiving program to try and do something different based on
+ the format code (say, recode UTF-8 to local). */
+ if(!nooutput && is_status_enabled())
+ {
+ char status[50];
+
+ sprintf(status,"%X %lu ",(byte)pt->mode,(ulong)pt->timestamp);
+ write_status_text_and_buffer(STATUS_PLAINTEXT,
+ status,pt->name,pt->namelen,0);
+
+ if(!pt->is_partial)
+ {
+ sprintf(status,"%lu",(ulong)pt->len);
+ write_status_text(STATUS_PLAINTEXT_LENGTH,status);
+ }
+ }
+
+ /* create the filename as C string */
+ if( nooutput )
+ ;
+ else if( opt.outfile ) {
+ fname = xmalloc( strlen( opt.outfile ) + 1);
+ strcpy(fname, opt.outfile );
+ }
+ else if( pt->namelen == 8 && !memcmp( pt->name, "_CONSOLE", 8 ) ) {
+ log_info(_("data not saved; use option \"--output\" to save it\n"));
+ nooutput = 1;
+ }
+ else if( !opt.flags.use_embedded_filename ) {
+ fname = make_outfile_name( iobuf_get_real_fname(pt->buf) );
+ if( !fname )
+ fname = ask_outfile_name( pt->name, pt->namelen );
+ if( !fname ) {
+ rc = G10ERR_CREATE_FILE;
+ goto leave;
+ }
+ }
+ else
+ fname=utf8_to_native(pt->name,pt->namelen,0);
+
+ if( nooutput )
+ ;
+ else if ( iobuf_is_pipe_filename (fname) || !*fname)
+ {
+ /* No filename or "-" given; write to stdout. */
+ fp = stdout;
+#ifdef HAVE_DOSISH_SYSTEM
+ setmode ( fileno(fp) , O_BINARY );
+#endif
+ }
+ else {
+ while( !overwrite_filep (fname) ) {
+ char *tmp = ask_outfile_name (NULL, 0);
+ if ( !tmp || !*tmp ) {
+ xfree (tmp);
+ rc = G10ERR_CREATE_FILE;
+ goto leave;
+ }
+ xfree (fname);
+ fname = tmp;
+ }
+ }
+
+#ifndef __riscos__
+ if( fp || nooutput )
+ ;
+ else if (is_secured_filename (fname))
+ {
+ errno = EPERM;
+ log_error(_("error creating `%s': %s\n"), fname, strerror(errno) );
+ rc = G10ERR_CREATE_FILE;
+ goto leave;
+ }
+ else if( !(fp = fopen(fname,"wb")) ) {
+ log_error(_("error creating `%s': %s\n"), fname, strerror(errno) );
+ rc = G10ERR_CREATE_FILE;
+ goto leave;
+ }
+#else /* __riscos__ */
+ /* If no output filename was given, i.e. we constructed it,
+ convert all '.' in fname to '/' but not vice versa as
+ we don't create directories! */
+ if( !opt.outfile )
+ for( c=0; fname[c]; ++c )
+ if( fname[c] == '.' )
+ fname[c] = '/';
+
+ if( fp || nooutput )
+ ;
+ else {
+ fp = fopen(fname,"wb");
+ if( !fp ) {
+ log_error(_("error creating `%s': %s\n"), fname, strerror(errno) );
+ rc = G10ERR_CREATE_FILE;
+ if (errno == 106)
+ log_info("Do output file and input file have the same name?\n");
+ goto leave;
+ }
+
+ /* If there's a ,xxx extension in the embedded filename,
+ use that, else check whether the user input (in fname)
+ has a ,xxx appended, then use that in preference */
+ if( (c = riscos_get_filetype_from_string( pt->name,
+ pt->namelen )) != -1 )
+ filetype = c;
+ if( (c = riscos_get_filetype_from_string( fname,
+ strlen(fname) )) != -1 )
+ filetype = c;
+ riscos_set_filetype_by_number(fname, filetype);
+ }
+#endif /* __riscos__ */
+
+ if( !pt->is_partial ) {
+ /* We have an actual length (which might be zero). */
+
+ if (clearsig) {
+ log_error ("clearsig encountered while not expected\n");
+ rc = G10ERR_UNEXPECTED;
+ goto leave;
+ }
+
+ if( convert ) { /* text mode */
+ for( ; pt->len; pt->len-- ) {
+ if( (c = iobuf_get(pt->buf)) == -1 ) {
+ log_error("Problem reading source (%u bytes remaining)\n",
+ (unsigned)pt->len);
+ rc = G10ERR_READ_FILE;
+ goto leave;
+ }
+ if( mfx->md )
+ md_putc(mfx->md, c );
+#ifndef HAVE_DOSISH_SYSTEM
+ if( c == '\r' ) /* convert to native line ending */
+ continue; /* fixme: this hack might be too simple */
+#endif
+ if( fp )
+ {
+ if(opt.max_output && (++count)>opt.max_output)
+ {
+ log_error("Error writing to `%s': %s\n",
+ fname,"exceeded --max-output limit\n");
+ rc = G10ERR_WRITE_FILE;
+ goto leave;
+ }
+ else if( putc( c, fp ) == EOF )
+ {
+ log_error("Error writing to `%s': %s\n",
+ fname, strerror(errno) );
+ rc = G10ERR_WRITE_FILE;
+ goto leave;
+ }
+ }
+ }
+ }
+ else { /* binary mode */
+ byte *buffer = xmalloc( 32768 );
+ while( pt->len ) {
+ int len = pt->len > 32768 ? 32768 : pt->len;
+ len = iobuf_read( pt->buf, buffer, len );
+ if( len == -1 ) {
+ log_error("Problem reading source (%u bytes remaining)\n",
+ (unsigned)pt->len);
+ rc = G10ERR_READ_FILE;
+ xfree( buffer );
+ goto leave;
+ }
+ if( mfx->md )
+ md_write( mfx->md, buffer, len );
+ if( fp )
+ {
+ if(opt.max_output && (count+=len)>opt.max_output)
+ {
+ log_error("Error writing to `%s': %s\n",
+ fname,"exceeded --max-output limit\n");
+ rc = G10ERR_WRITE_FILE;
+ xfree( buffer );
+ goto leave;
+ }
+ else if( fwrite( buffer, 1, len, fp ) != len )
+ {
+ log_error("Error writing to `%s': %s\n",
+ fname, strerror(errno) );
+ rc = G10ERR_WRITE_FILE;
+ xfree( buffer );
+ goto leave;
+ }
+ }
+ pt->len -= len;
+ }
+ xfree( buffer );
+ }
+ }
+ else if( !clearsig ) {
+ if( convert ) { /* text mode */
+ while( (c = iobuf_get(pt->buf)) != -1 ) {
+ if( mfx->md )
+ md_putc(mfx->md, c );
+#ifndef HAVE_DOSISH_SYSTEM
+ if( convert && c == '\r' )
+ continue; /* fixme: this hack might be too simple */
+#endif
+ if( fp )
+ {
+ if(opt.max_output && (++count)>opt.max_output)
+ {
+ log_error("Error writing to `%s': %s\n",
+ fname,"exceeded --max-output limit\n");
+ rc = G10ERR_WRITE_FILE;
+ goto leave;
+ }
+ else if( putc( c, fp ) == EOF )
+ {
+ log_error("Error writing to `%s': %s\n",
+ fname, strerror(errno) );
+ rc = G10ERR_WRITE_FILE;
+ goto leave;
+ }
+ }
+ }
+ }
+ else { /* binary mode */
+ byte *buffer = xmalloc( 32768 );
+ int eof;
+ for( eof=0; !eof; ) {
+ /* Why do we check for len < 32768:
+ * If we won't, we would practically read 2 EOFs but
+ * the first one has already popped the block_filter
+ * off and therefore we don't catch the boundary.
+ * So, always assume EOF if iobuf_read returns less bytes
+ * then requested */
+ int len = iobuf_read( pt->buf, buffer, 32768 );
+ if( len == -1 )
+ break;
+ if( len < 32768 )
+ eof = 1;
+ if( mfx->md )
+ md_write( mfx->md, buffer, len );
+ if( fp )
+ {
+ if(opt.max_output && (count+=len)>opt.max_output)
+ {
+ log_error("Error writing to `%s': %s\n",
+ fname,"exceeded --max-output limit\n");
+ rc = G10ERR_WRITE_FILE;
+ xfree( buffer );
+ goto leave;
+ }
+ else if( fwrite( buffer, 1, len, fp ) != len ) {
+ log_error("Error writing to `%s': %s\n",
+ fname, strerror(errno) );
+ rc = G10ERR_WRITE_FILE;
+ xfree( buffer );
+ goto leave;
+ }
+ }
+ }
+ xfree( buffer );
+ }
+ pt->buf = NULL;
+ }
+ else { /* clear text signature - don't hash the last cr,lf */
+ int state = 0;
+
+ while( (c = iobuf_get(pt->buf)) != -1 ) {
+ if( fp )
+ {
+ if(opt.max_output && (++count)>opt.max_output)
+ {
+ log_error("Error writing to `%s': %s\n",
+ fname,"exceeded --max-output limit\n");
+ rc = G10ERR_WRITE_FILE;
+ goto leave;
+ }
+ else if( putc( c, fp ) == EOF )
+ {
+ log_error("Error writing to `%s': %s\n",
+ fname, strerror(errno) );
+ rc = G10ERR_WRITE_FILE;
+ goto leave;
+ }
+ }
+ if( !mfx->md )
+ continue;
+ if( state == 2 ) {
+ md_putc(mfx->md, '\r' );
+ md_putc(mfx->md, '\n' );
+ state = 0;
+ }
+ if( !state ) {
+ if( c == '\r' )
+ state = 1;
+ else if( c == '\n' )
+ state = 2;
+ else
+ md_putc(mfx->md, c );
+ }
+ else if( state == 1 ) {
+ if( c == '\n' )
+ state = 2;
+ else {
+ md_putc(mfx->md, '\r' );
+ if( c == '\r' )
+ state = 1;
+ else {
+ state = 0;
+ md_putc(mfx->md, c );
+ }
+ }
+ }
+ }
+ pt->buf = NULL;
+ }
+
+ if( fp && fp != stdout && fclose(fp) ) {
+ log_error("Error closing `%s': %s\n", fname, strerror(errno) );
+ fp = NULL;
+ rc = G10ERR_WRITE_FILE;
+ goto leave;
+ }
+ fp = NULL;
+
+ leave:
+ if( fp && fp != stdout )
+ fclose(fp);
+ xfree(fname);
+ return rc;
+}
+
+static void
+do_hash( MD_HANDLE md, MD_HANDLE md2, IOBUF fp, int textmode )
+{
+ text_filter_context_t tfx;
+ int c;
+
+ if( textmode ) {
+ memset( &tfx, 0, sizeof tfx);
+ iobuf_push_filter( fp, text_filter, &tfx );
+ }
+ if( md2 ) { /* work around a strange behaviour in pgp2 */
+ /* It seems that at least PGP5 converts a single CR to a CR,LF too */
+ int lc = -1;
+ while( (c = iobuf_get(fp)) != -1 ) {
+ if( c == '\n' && lc == '\r' )
+ md_putc(md2, c);
+ else if( c == '\n' ) {
+ md_putc(md2, '\r');
+ md_putc(md2, c);
+ }
+ else if( c != '\n' && lc == '\r' ) {
+ md_putc(md2, '\n');
+ md_putc(md2, c);
+ }
+ else
+ md_putc(md2, c);
+
+ if( md )
+ md_putc(md, c );
+ lc = c;
+ }
+ }
+ else {
+ while( (c = iobuf_get(fp)) != -1 ) {
+ if( md )
+ md_putc(md, c );
+ }
+ }
+}
+
+
+/****************
+ * Ask for the detached datafile and calculate the digest from it.
+ * INFILE is the name of the input file.
+ */
+int
+ask_for_detached_datafile( MD_HANDLE md, MD_HANDLE md2,
+ const char *inname, int textmode )
+{
+ progress_filter_context_t pfx;
+ char *answer = NULL;
+ IOBUF fp;
+ int rc = 0;
+
+ fp = open_sigfile( inname, &pfx ); /* open default file */
+
+ if( !fp && !opt.batch ) {
+ int any=0;
+ tty_printf(_("Detached signature.\n"));
+ do {
+ char *name;
+ xfree(answer);
+ tty_enable_completion(NULL);
+ name = cpr_get("detached_signature.filename",
+ _("Please enter name of data file: "));
+ tty_disable_completion();
+ cpr_kill_prompt();
+ answer=make_filename(name,(void *)NULL);
+ xfree(name);
+
+ if( any && !*answer ) {
+ rc = G10ERR_READ_FILE;
+ goto leave;
+ }
+ fp = iobuf_open(answer);
+ if (fp && is_secured_file (iobuf_get_fd (fp)))
+ {
+ iobuf_close (fp);
+ fp = NULL;
+ errno = EPERM;
+ }
+ if( !fp && errno == ENOENT ) {
+ tty_printf("No such file, try again or hit enter to quit.\n");
+ any++;
+ }
+ else if( !fp )
+ {
+ log_error(_("can't open `%s': %s\n"), answer, strerror(errno));
+ rc = G10ERR_READ_FILE;
+ goto leave;
+ }
+ } while( !fp );
+ }
+
+ if( !fp ) {
+ if( opt.verbose )
+ log_info(_("reading stdin ...\n"));
+ fp = iobuf_open( NULL );
+ assert(fp);
+ }
+ do_hash( md, md2, fp, textmode );
+ iobuf_close(fp);
+
+ leave:
+ xfree(answer);
+ return rc;
+}
+
+
+
+/****************
+ * Hash the given files and append the hash to hash context md.
+ * If FILES is NULL, hash stdin.
+ */
+int
+hash_datafiles( MD_HANDLE md, MD_HANDLE md2, STRLIST files,
+ const char *sigfilename, int textmode )
+{
+ progress_filter_context_t pfx;
+ IOBUF fp;
+ STRLIST sl;
+
+ if( !files ) {
+ /* check whether we can open the signed material */
+ fp = open_sigfile( sigfilename, &pfx );
+ if( fp ) {
+ do_hash( md, md2, fp, textmode );
+ iobuf_close(fp);
+ return 0;
+ }
+ log_error (_("no signed data\n"));
+ return G10ERR_OPEN_FILE;
+ }
+
+
+ for (sl=files; sl; sl = sl->next ) {
+ fp = iobuf_open( sl->d );
+ if (fp && is_secured_file (iobuf_get_fd (fp)))
+ {
+ iobuf_close (fp);
+ fp = NULL;
+ errno = EPERM;
+ }
+ if( !fp ) {
+ log_error(_("can't open signed data `%s'\n"),
+ print_fname_stdin(sl->d));
+ return G10ERR_OPEN_FILE;
+ }
+ handle_progress (&pfx, fp, sl->d);
+ do_hash( md, md2, fp, textmode );
+ iobuf_close(fp);
+ }
+
+ return 0;
+}
+
+
+/* Set up a plaintext packet with the appropriate filename. If there
+ is a --set-filename, use it (it's already UTF8). If there is a
+ regular filename, UTF8-ize it if necessary. If there is no
+ filenames at all, set the field empty. */
+
+PKT_plaintext *
+setup_plaintext_name(const char *filename,IOBUF iobuf)
+{
+ PKT_plaintext *pt;
+
+ if(filename || opt.set_filename)
+ {
+ char *s;
+
+ if(opt.set_filename)
+ s=make_basename(opt.set_filename,iobuf_get_real_fname(iobuf));
+ else if(filename && !opt.flags.utf8_filename)
+ {
+ char *tmp=native_to_utf8(filename);
+ s=make_basename(tmp,iobuf_get_real_fname(iobuf));
+ xfree(tmp);
+ }
+ else
+ s=make_basename(filename,iobuf_get_real_fname(iobuf));
+
+ pt = xmalloc (sizeof *pt + strlen(s) - 1);
+ pt->namelen = strlen (s);
+ memcpy (pt->name, s, pt->namelen);
+ xfree (s);
+ }
+ else
+ {
+ /* no filename */
+ pt = xmalloc (sizeof *pt - 1);
+ pt->namelen = 0;
+ }
+
+ return pt;
+}
diff --git a/g10/progress.c b/g10/progress.c
new file mode 100644
index 0000000..8c8265f
--- /dev/null
+++ b/g10/progress.c
@@ -0,0 +1,118 @@
+/* progress.c
+ * Copyright (C) 2003 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+
+#include "iobuf.h"
+#include "filter.h"
+#include "status.h"
+#include "util.h"
+#include "options.h"
+
+/****************
+ * The filter is used to report progress to the user.
+ */
+int
+progress_filter (void *opaque, int control,
+ IOBUF a, byte *buf, size_t *ret_len)
+{
+ int rc = 0;
+ progress_filter_context_t *pfx = opaque;
+
+ if (control == IOBUFCTRL_INIT)
+ {
+ char buffer[50];
+
+ pfx->last = 0;
+ pfx->offset = 0;
+ pfx->last_time = make_timestamp ();
+
+ sprintf (buffer, "%.20s ? %lu %lu",
+ pfx->what? pfx->what : "?",
+ pfx->offset,
+ pfx->total);
+ write_status_text (STATUS_PROGRESS, buffer);
+ }
+ else if (control == IOBUFCTRL_UNDERFLOW)
+ {
+ u32 timestamp = make_timestamp ();
+ int len = iobuf_read (a, buf, *ret_len);
+
+ if (len >= 0)
+ {
+ pfx->offset += len;
+ *ret_len = len;
+ }
+ else
+ {
+ *ret_len = 0;
+ rc = -1;
+ }
+ if ((len == -1 && pfx->offset != pfx->last)
+ || timestamp - pfx->last_time > 0)
+ {
+ char buffer[50];
+
+ sprintf (buffer, "%.20s ? %lu %lu",
+ pfx->what? pfx->what : "?",
+ pfx->offset,
+ pfx->total);
+ write_status_text (STATUS_PROGRESS, buffer);
+
+ pfx->last = pfx->offset;
+ pfx->last_time = timestamp;
+ }
+ }
+ else if (control == IOBUFCTRL_FREE)
+ {
+ /* Note, that we must always dealloc resources of a filter
+ within the filter handler and not anywhere else. (We set it
+ to NULL and check all uses just in case.) */
+ xfree (pfx->what);
+ pfx->what = NULL;
+ }
+ else if (control == IOBUFCTRL_DESC)
+ *(char**)buf = "progress_filter";
+ return rc;
+}
+
+void
+handle_progress (progress_filter_context_t *pfx, IOBUF inp, const char *name)
+{
+ off_t filesize = 0;
+
+ if (!opt.enable_progress_filter)
+ return;
+
+ if (!is_status_enabled ())
+ return;
+
+ if ( !iobuf_is_pipe_filename (name) && *name )
+ filesize = iobuf_get_filelength (inp, NULL);
+ else if (opt.set_filesize)
+ filesize = opt.set_filesize;
+
+ /* register the progress filter */
+ pfx->what = xstrdup (name ? name : "stdin");
+ pfx->total = filesize;
+ iobuf_push_filter (inp, progress_filter, pfx);
+}
diff --git a/g10/pubkey-enc.c b/g10/pubkey-enc.c
new file mode 100644
index 0000000..cffa79c
--- /dev/null
+++ b/g10/pubkey-enc.c
@@ -0,0 +1,355 @@
+/* pubkey-enc.c - public key encoded packet handling
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "util.h"
+#include "memory.h"
+#include "packet.h"
+#include "mpi.h"
+#include "keydb.h"
+#include "trustdb.h"
+#include "cipher.h"
+#include "status.h"
+#include "options.h"
+#include "main.h"
+#include "i18n.h"
+#include "cardglue.h"
+
+static int get_it( PKT_pubkey_enc *k,
+ DEK *dek, PKT_secret_key *sk, u32 *keyid );
+
+
+/* check that the given algo is mentioned in one of the valid user IDs */
+static int
+is_algo_in_prefs ( KBNODE keyblock, preftype_t type, int algo )
+{
+ KBNODE k;
+
+ for (k=keyblock; k; k=k->next) {
+ if (k->pkt->pkttype == PKT_USER_ID) {
+ PKT_user_id *uid = k->pkt->pkt.user_id;
+ prefitem_t *prefs = uid->prefs;
+
+ if (uid->created && prefs &&
+ !uid->is_revoked && !uid->is_expired ) {
+ for (; prefs->type; prefs++ )
+ if (prefs->type == type && prefs->value == algo)
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+
+/****************
+ * Get the session key from a pubkey enc packet and return
+ * it in DEK, which should have been allocated in secure memory.
+ */
+int
+get_session_key( PKT_pubkey_enc *k, DEK *dek )
+{
+ PKT_secret_key *sk = NULL;
+ int rc;
+
+ rc = check_pubkey_algo2 (k->pubkey_algo, PUBKEY_USAGE_ENC);
+ if( rc )
+ goto leave;
+
+ if( (k->keyid[0] || k->keyid[1]) && !opt.try_all_secrets ) {
+ sk = xmalloc_clear( sizeof *sk );
+ sk->pubkey_algo = k->pubkey_algo; /* we want a pubkey with this algo*/
+ if( !(rc = get_seckey( sk, k->keyid )) )
+ rc = get_it( k, dek, sk, k->keyid );
+ }
+ else { /* anonymous receiver: Try all available secret keys */
+ void *enum_context = NULL;
+ u32 keyid[2];
+ char *p;
+
+ for(;;) {
+ if( sk )
+ free_secret_key( sk );
+ sk = xmalloc_clear( sizeof *sk );
+ rc=enum_secret_keys( &enum_context, sk, 1, 0);
+ if( rc ) {
+ rc = G10ERR_NO_SECKEY;
+ break;
+ }
+ if( sk->pubkey_algo != k->pubkey_algo )
+ continue;
+ keyid_from_sk( sk, keyid );
+ log_info(_("anonymous recipient; trying secret key %s ...\n"),
+ keystr(keyid));
+
+ if(!opt.try_all_secrets && !is_status_enabled())
+ {
+ p=get_last_passphrase();
+ set_next_passphrase(p);
+ xfree(p);
+ }
+
+ rc = check_secret_key( sk, opt.try_all_secrets?1:-1 ); /* ask
+ only
+ once */
+ if( !rc )
+ {
+ rc = get_it( k, dek, sk, keyid );
+ /* Successfully checked the secret key (either it was
+ a card, had no passphrase, or had the right
+ passphrase) but couldn't decrypt the session key,
+ so thus that key is not the anonymous recipient.
+ Move the next passphrase into last for the next
+ round. We only do this if the secret key was
+ successfully checked as in the normal case,
+ check_secret_key handles this for us via
+ passphrase_to_dek */
+ if(rc)
+ next_to_last_passphrase();
+ }
+
+ if( !rc )
+ {
+ log_info(_("okay, we are the anonymous recipient.\n") );
+ break;
+ }
+ }
+ enum_secret_keys( &enum_context, NULL, 0, 0 ); /* free context */
+ }
+
+ leave:
+ if( sk )
+ free_secret_key( sk );
+ return rc;
+}
+
+
+static int
+get_it( PKT_pubkey_enc *enc, DEK *dek, PKT_secret_key *sk, u32 *keyid )
+{
+ int rc;
+ MPI plain_dek = NULL;
+ byte *frame = NULL;
+ unsigned n, nframe;
+ u16 csum, csum2;
+
+ int card = 0;
+
+ if (sk->is_protected && sk->protect.s2k.mode == 1002)
+ { /* Note, that we only support RSA for now. */
+#ifdef ENABLE_CARD_SUPPORT
+ unsigned char *rbuf;
+ size_t rbuflen;
+ char *snbuf;
+ unsigned char *indata = NULL;
+ unsigned int indatalen;
+
+ snbuf = serialno_and_fpr_from_sk (sk->protect.iv, sk->protect.ivlen, sk);
+
+ indata = mpi_get_buffer (enc->data[0], &indatalen, NULL);
+ if (!indata)
+ BUG ();
+
+ rc = agent_scd_pkdecrypt (snbuf, indata, indatalen, &rbuf, &rbuflen);
+ xfree (snbuf);
+ xfree (indata);
+ if (rc)
+ goto leave;
+
+ frame = rbuf;
+ nframe = rbuflen;
+ card = 1;
+#else
+ rc = G10ERR_UNSUPPORTED;
+ goto leave;
+#endif /*!ENABLE_CARD_SUPPORT*/
+ }
+ else
+ {
+ rc = pubkey_decrypt(sk->pubkey_algo, &plain_dek, enc->data, sk->skey );
+ if( rc )
+ goto leave;
+ frame = mpi_get_buffer( plain_dek, &nframe, NULL );
+ mpi_free( plain_dek ); plain_dek = NULL;
+ }
+
+ /* Now get the DEK (data encryption key) from the frame
+ *
+ * Old versions encode the DEK in in this format (msb is left):
+ *
+ * 0 1 DEK(16 bytes) CSUM(2 bytes) 0 RND(n bytes) 2
+ *
+ * Later versions encode the DEK like this:
+ *
+ * 0 2 RND(n bytes) 0 A DEK(k bytes) CSUM(2 bytes)
+ *
+ * (mpi_get_buffer already removed the leading zero).
+ *
+ * RND are non-zero randow bytes.
+ * A is the cipher algorithm
+ * DEK is the encryption key (session key) with length k
+ * CSUM
+ */
+ if( DBG_CIPHER )
+ log_hexdump("DEK frame:", frame, nframe );
+ n=0;
+ if (!card)
+ {
+ if( n + 7 > nframe )
+ { rc = G10ERR_WRONG_SECKEY; goto leave; }
+ if( frame[n] == 1 && frame[nframe-1] == 2 ) {
+ log_info(_("old encoding of the DEK is not supported\n"));
+ rc = G10ERR_CIPHER_ALGO;
+ goto leave;
+ }
+ if( frame[n] != 2 ) /* somethink is wrong */
+ { rc = G10ERR_WRONG_SECKEY; goto leave; }
+ for(n++; n < nframe && frame[n]; n++ ) /* skip the random bytes */
+ ;
+ n++; /* and the zero byte */
+ }
+
+ if( n + 4 > nframe )
+ { rc = G10ERR_WRONG_SECKEY; goto leave; }
+
+ dek->keylen = nframe - (n+1) - 2;
+ dek->algo = frame[n++];
+ if( dek->algo == CIPHER_ALGO_IDEA )
+ write_status(STATUS_RSA_OR_IDEA);
+ rc = check_cipher_algo( dek->algo );
+ if( rc ) {
+ if( !opt.quiet && rc == G10ERR_CIPHER_ALGO ) {
+ log_info(_("cipher algorithm %d%s is unknown or disabled\n"),
+ dek->algo, dek->algo == CIPHER_ALGO_IDEA? " (IDEA)":"");
+ if(dek->algo==CIPHER_ALGO_IDEA)
+ idea_cipher_warn(0);
+ }
+ dek->algo = 0;
+ goto leave;
+ }
+ if( (dek->keylen*8) != cipher_get_keylen( dek->algo ) ) {
+ rc = G10ERR_WRONG_SECKEY;
+ goto leave;
+ }
+
+ /* copy the key to DEK and compare the checksum */
+ csum = frame[nframe-2] << 8;
+ csum |= frame[nframe-1];
+ memcpy( dek->key, frame+n, dek->keylen );
+ for( csum2=0, n=0; n < dek->keylen; n++ )
+ csum2 += dek->key[n];
+ if( csum != csum2 ) {
+ rc = G10ERR_WRONG_SECKEY;
+ goto leave;
+ }
+ if( DBG_CIPHER )
+ log_hexdump("DEK is:", dek->key, dek->keylen );
+ /* check that the algo is in the preferences and whether it has expired */
+ {
+ PKT_public_key *pk = NULL;
+ KBNODE pkb = get_pubkeyblock (keyid);
+
+ if( !pkb ) {
+ rc = -1;
+ log_error("oops: public key not found for preference check\n");
+ }
+ else if(pkb->pkt->pkt.public_key->selfsigversion > 3
+ && dek->algo != CIPHER_ALGO_3DES
+ && !opt.quiet
+ && !is_algo_in_prefs( pkb, PREFTYPE_SYM, dek->algo ))
+ log_info(_("WARNING: cipher algorithm %s not found in recipient"
+ " preferences\n"),cipher_algo_to_string(dek->algo));
+ if (!rc) {
+ KBNODE k;
+
+ for (k=pkb; k; k = k->next) {
+ if (k->pkt->pkttype == PKT_PUBLIC_KEY
+ || k->pkt->pkttype == PKT_PUBLIC_SUBKEY){
+ u32 aki[2];
+ keyid_from_pk(k->pkt->pkt.public_key, aki);
+
+ if (aki[0]==keyid[0] && aki[1]==keyid[1]) {
+ pk = k->pkt->pkt.public_key;
+ break;
+ }
+ }
+ }
+ if (!pk)
+ BUG ();
+ if ( pk->expiredate && pk->expiredate <= make_timestamp() ) {
+ log_info(_("NOTE: secret key %s expired at %s\n"),
+ keystr(keyid), asctimestamp( pk->expiredate) );
+ }
+ }
+
+ if ( pk && pk->is_revoked ) {
+ log_info( _("NOTE: key has been revoked") );
+ putc( '\n', log_stream() );
+ show_revocation_reason( pk, 1 );
+ }
+
+ release_kbnode (pkb);
+ rc = 0;
+ }
+
+
+ leave:
+ mpi_free(plain_dek);
+ xfree(frame);
+ return rc;
+}
+
+
+/****************
+ * Get the session key from the given string.
+ * String is supposed to be formatted as this:
+ * <algo-id>:<even-number-of-hex-digits>
+ */
+int
+get_override_session_key( DEK *dek, const char *string )
+{
+ const char *s;
+ int i;
+
+ if ( !string )
+ return G10ERR_BAD_KEY;
+ dek->algo = atoi(string);
+ if ( dek->algo < 1 )
+ return G10ERR_BAD_KEY;
+ if ( !(s = strchr ( string, ':' )) )
+ return G10ERR_BAD_KEY;
+ s++;
+ for(i=0; i < DIM(dek->key) && *s; i++, s +=2 ) {
+ int c = hextobyte ( s );
+ if (c == -1)
+ return G10ERR_BAD_KEY;
+ dek->key[i] = c;
+ }
+ if ( *s )
+ return G10ERR_BAD_KEY;
+ dek->keylen = i;
+ return 0;
+}
+
diff --git a/g10/revoke.c b/g10/revoke.c
new file mode 100644
index 0000000..c2deefa
--- /dev/null
+++ b/g10/revoke.c
@@ -0,0 +1,738 @@
+/* revoke.c
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003,
+ * 2004 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <ctype.h>
+
+#include "options.h"
+#include "packet.h"
+#include "errors.h"
+#include "keydb.h"
+#include "memory.h"
+#include "util.h"
+#include "main.h"
+#include "ttyio.h"
+#include "status.h"
+#include "i18n.h"
+
+
+struct revocation_reason_info {
+ int code;
+ char *desc;
+};
+
+
+int
+revocation_reason_build_cb( PKT_signature *sig, void *opaque )
+{
+ struct revocation_reason_info *reason = opaque;
+ char *ud = NULL;
+ byte *buffer;
+ size_t buflen = 1;
+
+ if(!reason)
+ return 0;
+
+ if( reason->desc ) {
+ ud = native_to_utf8( reason->desc );
+ buflen += strlen(ud);
+ }
+ buffer = xmalloc( buflen );
+ *buffer = reason->code;
+ if( ud ) {
+ memcpy(buffer+1, ud, strlen(ud) );
+ xfree( ud );
+ }
+
+ build_sig_subpkt( sig, SIGSUBPKT_REVOC_REASON, buffer, buflen );
+ xfree( buffer );
+ return 0;
+}
+
+/* Outputs a minimal pk (as defined by 2440) from a keyblock. A
+ minimal pk consists of the public key packet and a user ID. We try
+ and pick a user ID that has a uid signature, and include it if
+ possible. */
+static int
+export_minimal_pk(IOBUF out,KBNODE keyblock,
+ PKT_signature *revsig,PKT_signature *revkey)
+{
+ KBNODE node;
+ PACKET pkt;
+ PKT_user_id *uid=NULL;
+ PKT_signature *selfsig=NULL;
+ u32 keyid[2];
+ int rc;
+
+ node=find_kbnode(keyblock,PKT_PUBLIC_KEY);
+ if(!node)
+ {
+ log_error("key incomplete\n");
+ return G10ERR_GENERAL;
+ }
+
+ keyid_from_pk(node->pkt->pkt.public_key,keyid);
+
+ pkt=*node->pkt;
+ rc=build_packet(out,&pkt);
+ if(rc)
+ {
+ log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
+ return rc;
+ }
+
+ init_packet(&pkt);
+ pkt.pkttype=PKT_SIGNATURE;
+
+ /* the revocation itself, if any. 2440 likes this to come first. */
+ if(revsig)
+ {
+ pkt.pkt.signature=revsig;
+ rc=build_packet(out,&pkt);
+ if(rc)
+ {
+ log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
+ return rc;
+ }
+ }
+
+ /* If a revkey in a 1F sig is present, include it too */
+ if(revkey)
+ {
+ pkt.pkt.signature=revkey;
+ rc=build_packet(out,&pkt);
+ if(rc)
+ {
+ log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
+ return rc;
+ }
+ }
+
+ while(!selfsig)
+ {
+ KBNODE signode;
+
+ node=find_next_kbnode(node,PKT_USER_ID);
+ if(!node)
+ {
+ /* We're out of user IDs - none were self-signed. */
+ if(uid)
+ break;
+ else
+ {
+ log_error(_("key %s has no user IDs\n"),keystr(keyid));
+ return G10ERR_GENERAL;
+ }
+ }
+
+ if(node->pkt->pkt.user_id->attrib_data)
+ continue;
+
+ uid=node->pkt->pkt.user_id;
+ signode=node;
+
+ while((signode=find_next_kbnode(signode,PKT_SIGNATURE)))
+ {
+ if(keyid[0]==signode->pkt->pkt.signature->keyid[0] &&
+ keyid[1]==signode->pkt->pkt.signature->keyid[1] &&
+ IS_UID_SIG(signode->pkt->pkt.signature))
+ {
+ selfsig=signode->pkt->pkt.signature;
+ break;
+ }
+ }
+ }
+
+ pkt.pkttype=PKT_USER_ID;
+ pkt.pkt.user_id=uid;
+
+ rc=build_packet(out,&pkt);
+ if(rc)
+ {
+ log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
+ return rc;
+ }
+
+ if(selfsig)
+ {
+ pkt.pkttype=PKT_SIGNATURE;
+ pkt.pkt.signature=selfsig;
+
+ rc=build_packet(out,&pkt);
+ if(rc)
+ {
+ log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/****************
+ * Generate a revocation certificate for UNAME via a designated revoker
+ */
+int
+gen_desig_revoke( const char *uname, STRLIST locusr )
+{
+ int rc = 0;
+ armor_filter_context_t afx;
+ PKT_public_key *pk = NULL;
+ PKT_secret_key *sk = NULL;
+ PKT_signature *sig = NULL;
+ IOBUF out = NULL;
+ struct revocation_reason_info *reason = NULL;
+ KEYDB_HANDLE kdbhd;
+ KEYDB_SEARCH_DESC desc;
+ KBNODE keyblock=NULL,node;
+ u32 keyid[2];
+ int i,any=0;
+ SK_LIST sk_list=NULL;
+
+ if( opt.batch )
+ {
+ log_error(_("can't do this in batch mode\n"));
+ return G10ERR_GENERAL;
+ }
+
+ memset( &afx, 0, sizeof afx);
+
+ kdbhd = keydb_new (0);
+ classify_user_id (uname, &desc);
+ rc = desc.mode? keydb_search (kdbhd, &desc, 1) : G10ERR_INV_USER_ID;
+ if (rc) {
+ log_error (_("key \"%s\" not found: %s\n"),uname, g10_errstr (rc));
+ goto leave;
+ }
+
+ rc = keydb_get_keyblock (kdbhd, &keyblock );
+ if( rc ) {
+ log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) );
+ goto leave;
+ }
+
+ /* To parse the revkeys */
+ merge_keys_and_selfsig(keyblock);
+
+ /* get the key from the keyblock */
+ node = find_kbnode( keyblock, PKT_PUBLIC_KEY );
+ if( !node )
+ BUG ();
+
+ pk=node->pkt->pkt.public_key;
+
+ keyid_from_pk(pk,keyid);
+
+ if(locusr)
+ {
+ rc=build_sk_list(locusr,&sk_list,0,PUBKEY_USAGE_CERT);
+ if(rc)
+ goto leave;
+ }
+
+ /* Are we a designated revoker for this key? */
+
+ if(!pk->revkey && pk->numrevkeys)
+ BUG();
+
+ for(i=0;i<pk->numrevkeys;i++)
+ {
+ SK_LIST list;
+
+ if(sk)
+ free_secret_key(sk);
+
+ if(sk_list)
+ {
+ for(list=sk_list;list;list=list->next)
+ {
+ byte fpr[MAX_FINGERPRINT_LEN];
+ size_t fprlen;
+
+ fingerprint_from_sk(list->sk,fpr,&fprlen);
+
+ /* Don't get involved with keys that don't have 160
+ bit fingerprints */
+ if(fprlen!=20)
+ continue;
+
+ if(memcmp(fpr,pk->revkey[i].fpr,20)==0)
+ break;
+ }
+
+ if(list)
+ sk=copy_secret_key(NULL,list->sk);
+ else
+ continue;
+ }
+ else
+ {
+ sk=xmalloc_secure_clear(sizeof(*sk));
+ rc=get_seckey_byfprint(sk,pk->revkey[i].fpr,MAX_FINGERPRINT_LEN);
+ }
+
+ /* We have the revocation key */
+ if(!rc)
+ {
+ PKT_signature *revkey = NULL;
+
+ any = 1;
+
+ print_pubkey_info (NULL, pk);
+ tty_printf ("\n");
+
+ tty_printf (_("To be revoked by:\n"));
+ print_seckey_info (sk);
+
+ if(pk->revkey[i].class&0x40)
+ tty_printf(_("(This is a sensitive revocation key)\n"));
+ tty_printf("\n");
+
+ if( !cpr_get_answer_is_yes("gen_desig_revoke.okay",
+ _("Create a designated revocation certificate for this key? (y/N) ")))
+ continue;
+
+ /* get the reason for the revocation (this is always v4) */
+ reason = ask_revocation_reason( 1, 0, 1 );
+ if( !reason )
+ continue;
+
+ rc = check_secret_key( sk, 0 );
+ if( rc )
+ continue;
+
+ if( !opt.armor )
+ tty_printf(_("ASCII armored output forced.\n"));
+
+ if( (rc = open_outfile( NULL, 0, &out )) )
+ goto leave;
+
+ afx.what = 1;
+ afx.hdrlines = "Comment: A designated revocation certificate"
+ " should follow\n";
+ iobuf_push_filter( out, armor_filter, &afx );
+
+ /* create it */
+ rc = make_keysig_packet( &sig, pk, NULL, NULL, sk, 0x20, 0,
+ 0, 0, 0,
+ revocation_reason_build_cb, reason );
+ if( rc ) {
+ log_error(_("make_keysig_packet failed: %s\n"), g10_errstr(rc));
+ goto leave;
+ }
+
+ /* Spit out a minimal pk as well, since otherwise there is
+ no way to know which key to attach this revocation to.
+ Also include the direct key signature that contains
+ this revocation key. We're allowed to include
+ sensitive revocation keys along with a revocation, as
+ this may be the only time the recipient has seen it.
+ Note that this means that if we have multiple different
+ sensitive revocation keys in a given direct key
+ signature, we're going to include them all here. This
+ is annoying, but the good outweighs the bad, since
+ without including this a sensitive revoker can't really
+ do their job. People should not include multiple
+ sensitive revocation keys in one signature: 2440 says
+ "Note that it may be appropriate to isolate this
+ subpacket within a separate signature so that it is not
+ combined with other subpackets that need to be
+ exported." -dms */
+
+ while(!revkey)
+ {
+ KBNODE signode;
+
+ signode=find_next_kbnode(node,PKT_SIGNATURE);
+ if(!signode)
+ break;
+
+ node=signode;
+
+ if(keyid[0]==signode->pkt->pkt.signature->keyid[0] &&
+ keyid[1]==signode->pkt->pkt.signature->keyid[1] &&
+ IS_KEY_SIG(signode->pkt->pkt.signature))
+ {
+ int j;
+
+ for(j=0;j<signode->pkt->pkt.signature->numrevkeys;j++)
+ {
+ if(pk->revkey[i].class==
+ signode->pkt->pkt.signature->revkey[j]->class &&
+ pk->revkey[i].algid==
+ signode->pkt->pkt.signature->revkey[j]->algid &&
+ memcmp(pk->revkey[i].fpr,
+ signode->pkt->pkt.signature->revkey[j]->fpr,
+ MAX_FINGERPRINT_LEN)==0)
+ {
+ revkey=signode->pkt->pkt.signature;
+ break;
+ }
+ }
+ }
+ }
+
+ if(!revkey)
+ BUG();
+
+ rc=export_minimal_pk(out,keyblock,sig,revkey);
+ if(rc)
+ goto leave;
+
+ /* and issue a usage notice */
+ tty_printf(_("Revocation certificate created.\n"));
+ break;
+ }
+ }
+
+ if(!any)
+ log_error(_("no revocation keys found for \"%s\"\n"),uname);
+
+ leave:
+ if( pk )
+ free_public_key( pk );
+ if( sk )
+ free_secret_key( sk );
+ if( sig )
+ free_seckey_enc( sig );
+
+ release_sk_list(sk_list);
+
+ if( rc )
+ iobuf_cancel(out);
+ else
+ iobuf_close(out);
+ release_revocation_reason_info( reason );
+ return rc;
+}
+
+
+/****************
+ * Generate a revocation certificate for UNAME
+ */
+int
+gen_revoke( const char *uname )
+{
+ int rc = 0;
+ armor_filter_context_t afx;
+ PACKET pkt;
+ PKT_secret_key *sk; /* used as pointer into a kbnode */
+ PKT_public_key *pk = NULL;
+ PKT_signature *sig = NULL;
+ u32 sk_keyid[2];
+ IOBUF out = NULL;
+ KBNODE keyblock = NULL, pub_keyblock = NULL;
+ KBNODE node;
+ KEYDB_HANDLE kdbhd;
+ struct revocation_reason_info *reason = NULL;
+ KEYDB_SEARCH_DESC desc;
+
+ if( opt.batch )
+ {
+ log_error(_("can't do this in batch mode\n"));
+ return G10ERR_GENERAL;
+ }
+
+ memset( &afx, 0, sizeof afx);
+ init_packet( &pkt );
+
+ /* search the userid:
+ * We don't want the whole getkey stuff here but the entire keyblock
+ */
+ kdbhd = keydb_new (1);
+ classify_user_id (uname, &desc);
+ rc = desc.mode? keydb_search (kdbhd, &desc, 1) : G10ERR_INV_USER_ID;
+ if (rc)
+ {
+ log_error (_("secret key \"%s\" not found: %s\n"),
+ uname, g10_errstr (rc));
+ goto leave;
+ }
+
+ rc = keydb_get_keyblock (kdbhd, &keyblock );
+ if( rc ) {
+ log_error (_("error reading keyblock: %s\n"), g10_errstr(rc) );
+ goto leave;
+ }
+
+ /* get the keyid from the keyblock */
+ node = find_kbnode( keyblock, PKT_SECRET_KEY );
+ if( !node )
+ BUG ();
+
+ /* fixme: should make a function out of this stuff,
+ * it's used all over the source */
+ sk = node->pkt->pkt.secret_key;
+ keyid_from_sk( sk, sk_keyid );
+ print_seckey_info (sk);
+
+ pk = xmalloc_clear( sizeof *pk );
+
+ /* FIXME: We should get the public key direct from the secret one */
+
+ pub_keyblock=get_pubkeyblock(sk_keyid);
+ if(!pub_keyblock)
+ {
+ log_error(_("no corresponding public key: %s\n"), g10_errstr(rc) );
+ goto leave;
+ }
+
+ node=find_kbnode(pub_keyblock,PKT_PUBLIC_KEY);
+ if(!node)
+ BUG();
+
+ pk=node->pkt->pkt.public_key;
+
+ if( cmp_public_secret_key( pk, sk ) ) {
+ log_error(_("public key does not match secret key!\n") );
+ rc = G10ERR_GENERAL;
+ goto leave;
+ }
+
+ tty_printf("\n");
+ if( !cpr_get_answer_is_yes("gen_revoke.okay",
+ _("Create a revocation certificate for this key? (y/N) ")) )
+ {
+ rc = 0;
+ goto leave;
+ }
+
+ if(sk->version>=4 || opt.force_v4_certs) {
+ /* get the reason for the revocation */
+ reason = ask_revocation_reason( 1, 0, 1 );
+ if( !reason ) { /* user decided to cancel */
+ rc = 0;
+ goto leave;
+ }
+ }
+
+ switch( is_secret_key_protected( sk ) ) {
+ case -1:
+ log_error(_("unknown protection algorithm\n"));
+ rc = G10ERR_PUBKEY_ALGO;
+ break;
+ case -3:
+ tty_printf (_("Secret parts of primary key are not available.\n"));
+ rc = G10ERR_NO_SECKEY;
+ break;
+ case 0:
+ tty_printf(_("NOTE: This key is not protected!\n"));
+ break;
+ default:
+ rc = check_secret_key( sk, 0 );
+ break;
+ }
+ if( rc )
+ goto leave;
+
+
+ if( !opt.armor )
+ tty_printf(_("ASCII armored output forced.\n"));
+
+ if( (rc = open_outfile( NULL, 0, &out )) )
+ goto leave;
+
+ afx.what = 1;
+ afx.hdrlines = "Comment: A revocation certificate should follow\n";
+ iobuf_push_filter( out, armor_filter, &afx );
+
+ /* create it */
+ rc = make_keysig_packet( &sig, pk, NULL, NULL, sk, 0x20, 0,
+ opt.force_v4_certs?4:0, 0, 0,
+ revocation_reason_build_cb, reason );
+ if( rc ) {
+ log_error(_("make_keysig_packet failed: %s\n"), g10_errstr(rc));
+ goto leave;
+ }
+
+ if(PGP2 || PGP6 || PGP7 || PGP8)
+ {
+ /* Use a minimal pk for PGPx mode, since PGP can't import bare
+ revocation certificates. */
+ rc=export_minimal_pk(out,pub_keyblock,sig,NULL);
+ if(rc)
+ goto leave;
+ }
+ else
+ {
+ init_packet( &pkt );
+ pkt.pkttype = PKT_SIGNATURE;
+ pkt.pkt.signature = sig;
+
+ rc = build_packet( out, &pkt );
+ if( rc ) {
+ log_error(_("build_packet failed: %s\n"), g10_errstr(rc) );
+ goto leave;
+ }
+ }
+
+ /* and issue a usage notice */
+ tty_printf(_("Revocation certificate created.\n\n"
+"Please move it to a medium which you can hide away; if Mallory gets\n"
+"access to this certificate he can use it to make your key unusable.\n"
+"It is smart to print this certificate and store it away, just in case\n"
+"your media become unreadable. But have some caution: The print system of\n"
+"your machine might store the data and make it available to others!\n"));
+
+ leave:
+ if( sig )
+ free_seckey_enc( sig );
+ release_kbnode( keyblock );
+ release_kbnode( pub_keyblock );
+ keydb_release (kdbhd);
+ if( rc )
+ iobuf_cancel(out);
+ else
+ iobuf_close(out);
+ release_revocation_reason_info( reason );
+ return rc;
+}
+
+
+
+struct revocation_reason_info *
+ask_revocation_reason( int key_rev, int cert_rev, int hint )
+{
+ int code=-1;
+ char *description = NULL;
+ struct revocation_reason_info *reason;
+ const char *text_0 = _("No reason specified");
+ const char *text_1 = _("Key has been compromised");
+ const char *text_2 = _("Key is superseded");
+ const char *text_3 = _("Key is no longer used");
+ const char *text_4 = _("User ID is no longer valid");
+ const char *code_text = NULL;
+
+ do {
+ code=-1;
+ xfree(description);
+ description = NULL;
+
+ tty_printf(_("Please select the reason for the revocation:\n"));
+ tty_printf( " 0 = %s\n", text_0 );
+ if( key_rev )
+ tty_printf(" 1 = %s\n", text_1 );
+ if( key_rev )
+ tty_printf(" 2 = %s\n", text_2 );
+ if( key_rev )
+ tty_printf(" 3 = %s\n", text_3 );
+ if( cert_rev )
+ tty_printf(" 4 = %s\n", text_4 );
+ tty_printf( " Q = %s\n", _("Cancel") );
+ if( hint )
+ tty_printf(_("(Probably you want to select %d here)\n"), hint );
+
+ while(code==-1) {
+ int n;
+ char *answer = cpr_get("ask_revocation_reason.code",
+ _("Your decision? "));
+ trim_spaces( answer );
+ cpr_kill_prompt();
+ if( *answer == 'q' || *answer == 'Q')
+ return NULL; /* cancel */
+ if( hint && !*answer )
+ n = hint;
+ else if(!digitp( answer ) )
+ n = -1;
+ else
+ n = atoi(answer);
+ xfree(answer);
+ if( n == 0 ) {
+ code = 0x00; /* no particular reason */
+ code_text = text_0;
+ }
+ else if( key_rev && n == 1 ) {
+ code = 0x02; /* key has been compromised */
+ code_text = text_1;
+ }
+ else if( key_rev && n == 2 ) {
+ code = 0x01; /* key is superseded */
+ code_text = text_2;
+ }
+ else if( key_rev && n == 3 ) {
+ code = 0x03; /* key is no longer used */
+ code_text = text_3;
+ }
+ else if( cert_rev && n == 4 ) {
+ code = 0x20; /* uid is no longer valid */
+ code_text = text_4;
+ }
+ else
+ tty_printf(_("Invalid selection.\n"));
+ }
+
+ tty_printf(_("Enter an optional description; "
+ "end it with an empty line:\n") );
+ for(;;) {
+ char *answer = cpr_get("ask_revocation_reason.text", "> " );
+ trim_trailing_ws( answer, strlen(answer) );
+ cpr_kill_prompt();
+ if( !*answer ) {
+ xfree(answer);
+ break;
+ }
+
+ {
+ char *p = make_printable_string( answer, strlen(answer), 0 );
+ xfree(answer);
+ answer = p;
+ }
+
+ if( !description )
+ description = xstrdup(answer);
+ else {
+ char *p = xmalloc( strlen(description) + strlen(answer) + 2 );
+ strcpy(stpcpy(stpcpy( p, description),"\n"),answer);
+ xfree(description);
+ description = p;
+ }
+ xfree(answer);
+ }
+
+ tty_printf(_("Reason for revocation: %s\n"), code_text );
+ if( !description )
+ tty_printf(_("(No description given)\n") );
+ else
+ tty_printf("%s\n", description );
+
+ } while( !cpr_get_answer_is_yes("ask_revocation_reason.okay",
+ _("Is this okay? (y/N) ")) );
+
+ reason = xmalloc( sizeof *reason );
+ reason->code = code;
+ reason->desc = description;
+ return reason;
+}
+
+void
+release_revocation_reason_info( struct revocation_reason_info *reason )
+{
+ if( reason ) {
+ xfree( reason->desc );
+ xfree( reason );
+ }
+}
diff --git a/g10/seckey-cert.c b/g10/seckey-cert.c
new file mode 100644
index 0000000..79cf22a
--- /dev/null
+++ b/g10/seckey-cert.c
@@ -0,0 +1,427 @@
+/* seckey-cert.c - secret key certificate packet handling
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "util.h"
+#include "memory.h"
+#include "packet.h"
+#include "mpi.h"
+#include "keydb.h"
+#include "cipher.h"
+#include "main.h"
+#include "options.h"
+#include "i18n.h"
+#include "status.h"
+
+
+static int
+do_check( PKT_secret_key *sk, const char *tryagain_text, int mode,
+ int *canceled )
+{
+ byte *buffer;
+ u16 csum=0;
+ int i, res;
+ unsigned nbytes;
+
+ if( sk->is_protected ) { /* remove the protection */
+ DEK *dek = NULL;
+ u32 keyid[4]; /* 4! because we need two of them */
+ CIPHER_HANDLE cipher_hd=NULL;
+ PKT_secret_key *save_sk;
+
+ if( sk->protect.s2k.mode == 1001 ) {
+ log_info(_("secret key parts are not available\n"));
+ return G10ERR_GENERAL;
+ }
+ if( sk->protect.algo == CIPHER_ALGO_NONE )
+ BUG();
+ if( check_cipher_algo( sk->protect.algo ) ) {
+ log_info(_("protection algorithm %d%s is not supported\n"),
+ sk->protect.algo,sk->protect.algo==1?" (IDEA)":"" );
+ if (sk->protect.algo==CIPHER_ALGO_IDEA)
+ {
+ write_status (STATUS_RSA_OR_IDEA);
+ idea_cipher_warn (0);
+ }
+ return G10ERR_CIPHER_ALGO;
+ }
+ if(check_digest_algo(sk->protect.s2k.hash_algo))
+ {
+ log_info(_("protection digest %d is not supported\n"),
+ sk->protect.s2k.hash_algo);
+ return G10ERR_DIGEST_ALGO;
+ }
+ keyid_from_sk( sk, keyid );
+ keyid[2] = keyid[3] = 0;
+ if( !sk->is_primary ) {
+ keyid[2] = sk->main_keyid[0];
+ keyid[3] = sk->main_keyid[1];
+ }
+ dek = passphrase_to_dek( keyid, sk->pubkey_algo, sk->protect.algo,
+ &sk->protect.s2k, mode,
+ tryagain_text, canceled );
+ if (!dek && canceled && *canceled)
+ return G10ERR_GENERAL;
+
+ cipher_hd = cipher_open( sk->protect.algo,
+ CIPHER_MODE_AUTO_CFB, 1);
+ cipher_setkey( cipher_hd, dek->key, dek->keylen );
+ xfree(dek);
+ save_sk = copy_secret_key( NULL, sk );
+ cipher_setiv( cipher_hd, sk->protect.iv, sk->protect.ivlen );
+ csum = 0;
+ if( sk->version >= 4 ) {
+ unsigned int ndata;
+ byte *p, *data;
+ u16 csumc = 0;
+
+ i = pubkey_get_npkey(sk->pubkey_algo);
+ assert( mpi_is_opaque( sk->skey[i] ) );
+ p = mpi_get_opaque( sk->skey[i], &ndata );
+ if ( ndata > 1 )
+ csumc = p[ndata-2] << 8 | p[ndata-1];
+ data = xmalloc_secure( ndata );
+ cipher_decrypt( cipher_hd, data, p, ndata );
+ mpi_free( sk->skey[i] ); sk->skey[i] = NULL ;
+ p = data;
+ if (sk->protect.sha1chk) {
+ /* This is the new SHA1 checksum method to detect
+ tampering with the key as used by the Klima/Rosa
+ attack */
+ sk->csum = 0;
+ csum = 1;
+ if( ndata < 20 )
+ log_error("not enough bytes for SHA-1 checksum\n");
+ else {
+ MD_HANDLE h = md_open (DIGEST_ALGO_SHA1, 1);
+ if (!h)
+ BUG(); /* algo not available */
+ md_write (h, data, ndata - 20);
+ md_final (h);
+ if (!memcmp (md_read (h, DIGEST_ALGO_SHA1),
+ data + ndata - 20, 20) ) {
+ /* digest does match. We have to keep the old
+ style checksum in sk->csum, so that the
+ test used for unprotected keys does work.
+ This test gets used when we are adding new
+ keys. */
+ sk->csum = csum = checksum (data, ndata-20);
+ }
+ md_close (h);
+ }
+ }
+ else {
+ if( ndata < 2 ) {
+ log_error("not enough bytes for checksum\n");
+ sk->csum = 0;
+ csum = 1;
+ }
+ else {
+ csum = checksum( data, ndata-2);
+ sk->csum = data[ndata-2] << 8 | data[ndata-1];
+ if ( sk->csum != csum ) {
+ /* This is a PGP 7.0.0 workaround */
+ sk->csum = csumc; /* take the encrypted one */
+ }
+ }
+ }
+
+ /* Must check it here otherwise the mpi_read_xx would fail
+ because the length may have an arbitrary value */
+ if( sk->csum == csum ) {
+ for( ; i < pubkey_get_nskey(sk->pubkey_algo); i++ ) {
+ nbytes = ndata;
+ sk->skey[i] = mpi_read_from_buffer(p, &nbytes, 1 );
+ if (!sk->skey[i])
+ {
+ /* Checksum was okay, but not correctly
+ decrypted. */
+ sk->csum = 0;
+ csum = 1;
+ break;
+ }
+ ndata -= nbytes;
+ p += nbytes;
+ }
+ /* Note: at this point ndata should be 2 for a simple
+ checksum or 20 for the sha1 digest */
+ }
+ xfree(data);
+ }
+ else {
+ for(i=pubkey_get_npkey(sk->pubkey_algo);
+ i < pubkey_get_nskey(sk->pubkey_algo); i++ ) {
+ byte *p;
+ unsigned int ndata;
+
+ assert (mpi_is_opaque (sk->skey[i]));
+ p = mpi_get_opaque (sk->skey[i], &ndata);
+ assert (ndata >= 2);
+ assert (ndata == ((p[0] << 8 | p[1]) + 7)/8 + 2);
+ buffer = xmalloc_secure (ndata);
+ cipher_sync (cipher_hd);
+ buffer[0] = p[0];
+ buffer[1] = p[1];
+ cipher_decrypt (cipher_hd, buffer+2, p+2, ndata-2);
+ csum += checksum (buffer, ndata);
+ mpi_free (sk->skey[i]);
+ sk->skey[i] = mpi_read_from_buffer (buffer, &ndata, 1);
+ xfree (buffer);
+ if (!sk->skey[i])
+ {
+ /* Checksum was okay, but not correctly
+ decrypted. */
+ sk->csum = 0;
+ csum = 1;
+ break;
+ }
+/* csum += checksum_mpi (sk->skey[i]); */
+ }
+ }
+ cipher_close( cipher_hd );
+ /* now let's see whether we have used the right passphrase */
+ if( csum != sk->csum ) {
+ copy_secret_key( sk, save_sk );
+ passphrase_clear_cache ( keyid, NULL, sk->pubkey_algo );
+ free_secret_key( save_sk );
+ return G10ERR_BAD_PASS;
+ }
+ /* the checksum may fail, so we also check the key itself */
+ res = pubkey_check_secret_key( sk->pubkey_algo, sk->skey );
+ if( res ) {
+ copy_secret_key( sk, save_sk );
+ passphrase_clear_cache ( keyid, NULL, sk->pubkey_algo );
+ free_secret_key( save_sk );
+ return G10ERR_BAD_PASS;
+ }
+ free_secret_key( save_sk );
+ sk->is_protected = 0;
+ }
+ else { /* not protected, assume it is okay if the checksum is okay */
+ csum = 0;
+ for(i=pubkey_get_npkey(sk->pubkey_algo);
+ i < pubkey_get_nskey(sk->pubkey_algo); i++ ) {
+ csum += checksum_mpi( sk->skey[i] );
+ }
+ if( csum != sk->csum )
+ return G10ERR_CHECKSUM;
+ }
+
+ return 0;
+}
+
+
+
+/****************
+ * Check the secret key
+ * Ask up to 3 (or n) times for a correct passphrase
+ * If n is negative, disable the key info prompt and make n=abs(n)
+ */
+int
+check_secret_key( PKT_secret_key *sk, int n )
+{
+ int rc = G10ERR_BAD_PASS;
+ int i,mode;
+
+ if (sk && sk->is_protected && sk->protect.s2k.mode == 1002)
+ return 0; /* Let the card support stuff handle this. */
+
+ if(n<0)
+ {
+ n=abs(n);
+ mode=1;
+ }
+ else
+ mode=0;
+
+ if( n < 1 )
+ n = (opt.batch && !opt.use_agent)? 1 : 3; /* use the default value */
+
+ for(i=0; i < n && rc == G10ERR_BAD_PASS; i++ ) {
+ int canceled = 0;
+ const char *tryagain = NULL;
+ if (i) {
+ tryagain = N_("Invalid passphrase; please try again");
+ log_info (_("%s ...\n"), _(tryagain));
+ }
+ rc = do_check( sk, tryagain, mode, &canceled );
+ if( rc == G10ERR_BAD_PASS && is_status_enabled() ) {
+ u32 kid[2];
+ char buf[50];
+
+ keyid_from_sk( sk, kid );
+ sprintf(buf, "%08lX%08lX", (ulong)kid[0], (ulong)kid[1]);
+ write_status_text( STATUS_BAD_PASSPHRASE, buf );
+ }
+ if( have_static_passphrase() || canceled)
+ break;
+ }
+
+ if( !rc )
+ write_status( STATUS_GOOD_PASSPHRASE );
+
+ return rc;
+}
+
+/****************
+ * check whether the secret key is protected.
+ * Returns: 0 not protected, -1 on error or the protection algorithm
+ * -2 indicates a card stub.
+ * -3 indicates a not-online stub.
+ */
+int
+is_secret_key_protected( PKT_secret_key *sk )
+{
+ return sk->is_protected?
+ sk->protect.s2k.mode == 1002? -2 :
+ sk->protect.s2k.mode == 1001? -3 : sk->protect.algo : 0;
+}
+
+
+
+/****************
+ * Protect the secret key with the passphrase from DEK
+ */
+int
+protect_secret_key( PKT_secret_key *sk, DEK *dek )
+{
+ int i,j, rc = 0;
+ byte *buffer;
+ unsigned nbytes;
+ u16 csum;
+
+ if( !dek )
+ return 0;
+
+ if( !sk->is_protected ) { /* okay, apply the protection */
+ CIPHER_HANDLE cipher_hd=NULL;
+
+ if( check_cipher_algo( sk->protect.algo ) )
+ rc = G10ERR_CIPHER_ALGO; /* unsupport protection algorithm */
+ else {
+ print_cipher_algo_note( sk->protect.algo );
+ cipher_hd = cipher_open( sk->protect.algo,
+ CIPHER_MODE_AUTO_CFB, 1 );
+ if( cipher_setkey( cipher_hd, dek->key, dek->keylen ) )
+ log_info(_("WARNING: Weak key detected"
+ " - please change passphrase again.\n"));
+ sk->protect.ivlen = cipher_get_blocksize( sk->protect.algo );
+ assert( sk->protect.ivlen <= DIM(sk->protect.iv) );
+ if( sk->protect.ivlen != 8 && sk->protect.ivlen != 16 )
+ BUG(); /* yes, we are very careful */
+ randomize_buffer(sk->protect.iv, sk->protect.ivlen, 1);
+ cipher_setiv( cipher_hd, sk->protect.iv, sk->protect.ivlen );
+ if( sk->version >= 4 ) {
+ byte *bufarr[PUBKEY_MAX_NSKEY];
+ unsigned narr[PUBKEY_MAX_NSKEY];
+ unsigned nbits[PUBKEY_MAX_NSKEY];
+ int ndata=0;
+ byte *p, *data;
+
+ for(j=0, i = pubkey_get_npkey(sk->pubkey_algo);
+ i < pubkey_get_nskey(sk->pubkey_algo); i++, j++ ) {
+ assert( !mpi_is_opaque( sk->skey[i] ) );
+ bufarr[j] = mpi_get_buffer( sk->skey[i], &narr[j], NULL );
+ nbits[j] = mpi_get_nbits( sk->skey[i] );
+ ndata += narr[j] + 2;
+ }
+ for( ; j < PUBKEY_MAX_NSKEY; j++ )
+ bufarr[j] = NULL;
+ ndata += opt.simple_sk_checksum? 2 : 20; /* for checksum */
+
+ data = xmalloc_secure( ndata );
+ p = data;
+ for(j=0; j < PUBKEY_MAX_NSKEY && bufarr[j]; j++ ) {
+ p[0] = nbits[j] >> 8 ;
+ p[1] = nbits[j];
+ p += 2;
+ memcpy(p, bufarr[j], narr[j] );
+ p += narr[j];
+ xfree(bufarr[j]);
+ }
+
+ if (opt.simple_sk_checksum) {
+ log_info (_("generating the deprecated 16-bit checksum"
+ " for secret key protection\n"));
+ csum = checksum( data, ndata-2);
+ sk->csum = csum;
+ *p++ = csum >> 8;
+ *p++ = csum;
+ sk->protect.sha1chk = 0;
+ }
+ else {
+ MD_HANDLE h = md_open (DIGEST_ALGO_SHA1, 1);
+ if (!h)
+ BUG(); /* algo not available */
+ md_write (h, data, ndata - 20);
+ md_final (h);
+ memcpy (p, md_read (h, DIGEST_ALGO_SHA1), 20);
+ p += 20;
+ md_close (h);
+ sk->csum = csum = 0;
+ sk->protect.sha1chk = 1;
+ }
+ assert( p == data+ndata );
+
+ cipher_encrypt( cipher_hd, data, data, ndata );
+ for(i = pubkey_get_npkey(sk->pubkey_algo);
+ i < pubkey_get_nskey(sk->pubkey_algo); i++ ) {
+ mpi_free( sk->skey[i] );
+ sk->skey[i] = NULL;
+ }
+ i = pubkey_get_npkey(sk->pubkey_algo);
+ sk->skey[i] = mpi_set_opaque(NULL, data, ndata );
+ }
+ else {
+ csum = 0;
+ for(i=pubkey_get_npkey(sk->pubkey_algo);
+ i < pubkey_get_nskey(sk->pubkey_algo); i++ ) {
+ byte *data;
+ unsigned int nbits;
+
+ csum += checksum_mpi (sk->skey[i]);
+ buffer = mpi_get_buffer( sk->skey[i], &nbytes, NULL );
+ cipher_sync (cipher_hd);
+ assert ( !mpi_is_opaque (sk->skey[i]) );
+ data = xmalloc (nbytes+2);
+ nbits = mpi_get_nbits (sk->skey[i]);
+ assert (nbytes == (nbits + 7)/8);
+ data[0] = nbits >> 8;
+ data[1] = nbits;
+ cipher_encrypt (cipher_hd, data+2, buffer, nbytes);
+ xfree( buffer );
+
+ mpi_free (sk->skey[i]);
+ sk->skey[i] = mpi_set_opaque (NULL, data, nbytes+2);
+ }
+ sk->csum = csum;
+ }
+ sk->is_protected = 1;
+ cipher_close( cipher_hd );
+ }
+ }
+ return rc;
+}
+
diff --git a/g10/seskey.c b/g10/seskey.c
new file mode 100644
index 0000000..dd4dc0b
--- /dev/null
+++ b/g10/seskey.c
@@ -0,0 +1,271 @@
+/* seskey.c - make sesssion keys etc.
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "util.h"
+#include "cipher.h"
+#include "mpi.h"
+#include "main.h"
+#include "i18n.h"
+
+
+/****************
+ * Make a session key and put it into DEK
+ */
+void
+make_session_key( DEK *dek )
+{
+ CIPHER_HANDLE chd;
+ int i, rc;
+
+ dek->keylen = cipher_get_keylen( dek->algo ) / 8;
+
+ chd = cipher_open( dek->algo, CIPHER_MODE_AUTO_CFB, 1 );
+ randomize_buffer( dek->key, dek->keylen, 1 );
+ for(i=0; i < 16; i++ ) {
+ rc = cipher_setkey( chd, dek->key, dek->keylen );
+ if( !rc ) {
+ cipher_close( chd );
+ return;
+ }
+ log_info(_("weak key created - retrying\n") );
+ /* Renew the session key until we get a non-weak key. */
+ randomize_buffer( dek->key, dek->keylen, 1 );
+ }
+ log_fatal(_(
+ "cannot avoid weak key for symmetric cipher; tried %d times!\n"),
+ i);
+}
+
+
+/****************
+ * Encode the session key. NBITS is the number of bits which should be used
+ * for packing the session key.
+ * returns: A mpi with the session key (caller must free)
+ */
+MPI
+encode_session_key( DEK *dek, unsigned nbits )
+{
+ int nframe = (nbits+7) / 8;
+ byte *p;
+ byte *frame;
+ int i,n;
+ u16 csum;
+ MPI a;
+
+ /* the current limitation is that we can only use a session key
+ * whose length is a multiple of BITS_PER_MPI_LIMB
+ * I think we can live with that.
+ */
+ if( dek->keylen + 7 > nframe || !nframe )
+ log_bug("can't encode a %d bit key in a %d bits frame\n",
+ dek->keylen*8, nbits );
+
+ /* We encode the session key in this way:
+ *
+ * 0 2 RND(n bytes) 0 A DEK(k bytes) CSUM(2 bytes)
+ *
+ * (But how can we store the leading 0 - the external representaion
+ * of MPIs doesn't allow leading zeroes =:-)
+ *
+ * RND are non-zero random bytes.
+ * A is the cipher algorithm
+ * DEK is the encryption key (session key) length k depends on the
+ * cipher algorithm (20 is used with blowfish160).
+ * CSUM is the 16 bit checksum over the DEK
+ */
+ csum = 0;
+ for( p = dek->key, i=0; i < dek->keylen; i++ )
+ csum += *p++;
+
+ frame = xmalloc_secure( nframe );
+ n = 0;
+ frame[n++] = 0;
+ frame[n++] = 2;
+ i = nframe - 6 - dek->keylen;
+ assert( i > 0 );
+ p = get_random_bits( i*8, 1, 1 );
+ /* replace zero bytes by new values */
+ for(;;) {
+ int j, k;
+ byte *pp;
+
+ /* count the zero bytes */
+ for(j=k=0; j < i; j++ )
+ if( !p[j] )
+ k++;
+ if( !k )
+ break; /* okay: no zero bytes */
+ k += k/128 + 3; /* better get some more */
+ pp = get_random_bits( k*8, 1, 1);
+ for(j=0; j < i && k ;) {
+ if( !p[j] )
+ p[j] = pp[--k];
+ if (p[j])
+ j++;
+ }
+ xfree(pp);
+ }
+ memcpy( frame+n, p, i );
+ xfree(p);
+ n += i;
+ frame[n++] = 0;
+ frame[n++] = dek->algo;
+ memcpy( frame+n, dek->key, dek->keylen ); n += dek->keylen;
+ frame[n++] = csum >>8;
+ frame[n++] = csum;
+ assert( n == nframe );
+ a = mpi_alloc_secure( (nframe+BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB );
+ mpi_set_buffer( a, frame, nframe, 0 );
+ xfree(frame);
+ return a;
+}
+
+
+static MPI
+do_encode_md( MD_HANDLE md, int algo, size_t len, unsigned nbits,
+ const byte *asn, size_t asnlen )
+{
+ int nframe = (nbits+7) / 8;
+ byte *frame;
+ int i,n;
+ MPI a;
+
+ if( len + asnlen + 4 > nframe )
+ log_bug("can't encode a %d bit MD into a %d bits frame\n",
+ (int)(len*8), (int)nbits);
+
+ /* We encode the MD in this way:
+ *
+ * 0 1 PAD(n bytes) 0 ASN(asnlen bytes) MD(len bytes)
+ *
+ * PAD consists of FF bytes.
+ */
+ frame = md_is_secure(md)? xmalloc_secure( nframe ) : xmalloc( nframe );
+ n = 0;
+ frame[n++] = 0;
+ frame[n++] = 1; /* block type */
+ i = nframe - len - asnlen -3 ;
+ assert( i > 1 );
+ memset( frame+n, 0xff, i ); n += i;
+ frame[n++] = 0;
+ memcpy( frame+n, asn, asnlen ); n += asnlen;
+ memcpy( frame+n, md_read(md, algo), len ); n += len;
+ assert( n == nframe );
+ a = md_is_secure(md)?
+ mpi_alloc_secure( (nframe+BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB )
+ : mpi_alloc( (nframe+BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB );
+ mpi_set_buffer( a, frame, nframe, 0 );
+ xfree(frame);
+
+ /* Note that PGP before version 2.3 encoded the MD as:
+ *
+ * 0 1 MD(16 bytes) 0 PAD(n bytes) 1
+ *
+ * The MD is always 16 bytes here because it's always MD5. We do
+ * not support pre-v2.3 signatures, but I'm including this comment
+ * so the information is easily found in the future.
+ */
+
+ return a;
+}
+
+
+/****************
+ * Encode a message digest into an MPI.
+ * If it's for a DSA signature, make sure that the hash is large
+ * enough to fill up q. If the hash is too big, take the leftmost
+ * bits.
+ */
+MPI
+encode_md_value( PKT_public_key *pk, PKT_secret_key *sk,
+ MD_HANDLE md, int hash_algo )
+{
+ MPI frame;
+
+ assert(hash_algo);
+ assert(pk || sk);
+
+ if((pk?pk->pubkey_algo:sk->pubkey_algo) == PUBKEY_ALGO_DSA)
+ {
+ /* It's a DSA signature, so find out the size of q. */
+
+ unsigned int qbytes=mpi_get_nbits(pk?pk->pkey[1]:sk->skey[1]);
+
+ /* Make sure it is a multiple of 8 bits. */
+
+ if(qbytes%8)
+ {
+ log_error(_("DSA requires the hash length to be a"
+ " multiple of 8 bits\n"));
+ return NULL;
+ }
+
+ /* Don't allow any q smaller than 160 bits. This might need a
+ revisit as the DSA2 design firms up, but for now, we don't
+ want someone to issue signatures from a key with a 16-bit q
+ or something like that, which would look correct but allow
+ trivial forgeries. Yes, I know this rules out using MD5 with
+ DSA. ;) */
+
+ if(qbytes<160)
+ {
+ log_error(_("DSA key %s uses an unsafe (%u bit) hash\n"),
+ pk?keystr_from_pk(pk):keystr_from_sk(sk),qbytes);
+ return NULL;
+ }
+
+ qbytes/=8;
+
+ /* Check if we're too short. Too long is safe as we'll
+ automatically left-truncate. */
+
+ if(md_digest_length(hash_algo) < qbytes)
+ {
+ log_error(_("DSA key %s requires a %u bit or larger hash\n"),
+ pk?keystr_from_pk(pk):keystr_from_sk(sk),qbytes*8);
+ return NULL;
+ }
+
+ frame = md_is_secure(md)? mpi_alloc_secure((qbytes+BYTES_PER_MPI_LIMB-1)
+ / BYTES_PER_MPI_LIMB )
+ : mpi_alloc((qbytes+BYTES_PER_MPI_LIMB-1) / BYTES_PER_MPI_LIMB );
+
+ mpi_set_buffer( frame, md_read(md, hash_algo), qbytes, 0 );
+ }
+ else
+ {
+ const byte *asn;
+ size_t asnlen,mdlen;
+
+ asn = md_asn_oid( hash_algo, &asnlen, &mdlen );
+ frame = do_encode_md( md, hash_algo, mdlen,
+ mpi_get_nbits(pk?pk->pkey[0]:sk->skey[0]),
+ asn, asnlen );
+ }
+
+ return frame;
+}
diff --git a/g10/sig-check.c b/g10/sig-check.c
new file mode 100644
index 0000000..1570334
--- /dev/null
+++ b/g10/sig-check.c
@@ -0,0 +1,616 @@
+/* sig-check.c - Check a signature
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003,
+ * 2004 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+#include "util.h"
+#include "packet.h"
+#include "memory.h"
+#include "mpi.h"
+#include "keydb.h"
+#include "cipher.h"
+#include "main.h"
+#include "status.h"
+#include "i18n.h"
+#include "options.h"
+
+struct cmp_help_context_s {
+ PKT_signature *sig;
+ MD_HANDLE md;
+};
+
+static int do_check( PKT_public_key *pk, PKT_signature *sig, MD_HANDLE digest,
+ int *r_expired, int *r_revoked, PKT_public_key *ret_pk);
+
+/****************
+ * Check the signature which is contained in SIG.
+ * The MD_HANDLE should be currently open, so that this function
+ * is able to append some data, before finalizing the digest.
+ */
+int
+signature_check( PKT_signature *sig, MD_HANDLE digest )
+{
+ return signature_check2( sig, digest, NULL, NULL, NULL, NULL );
+}
+
+int
+signature_check2( PKT_signature *sig, MD_HANDLE digest, u32 *r_expiredate,
+ int *r_expired, int *r_revoked, PKT_public_key *ret_pk )
+{
+ PKT_public_key *pk = xmalloc_clear( sizeof *pk );
+ int rc=0;
+
+ if( (rc=check_digest_algo(sig->digest_algo)) )
+ ; /* we don't have this digest */
+ else if((rc=check_pubkey_algo(sig->pubkey_algo)))
+ ; /* we don't have this pubkey algo */
+ else if(!md_algo_present(digest,sig->digest_algo))
+ {
+ /* Sanity check that the md has a context for the hash that the
+ sig is expecting. This can happen if a onepass sig header does
+ not match the actual sig, and also if the clearsign "Hash:"
+ header is missing or does not match the actual sig. */
+
+ log_info(_("WARNING: signature digest conflict in message\n"));
+ rc=G10ERR_GENERAL;
+ }
+ else if( get_pubkey( pk, sig->keyid ) )
+ rc = G10ERR_NO_PUBKEY;
+ else if(!pk->is_valid && !pk->is_primary)
+ rc=G10ERR_BAD_PUBKEY; /* you cannot have a good sig from an
+ invalid subkey */
+ else
+ {
+ if(r_expiredate)
+ *r_expiredate = pk->expiredate;
+
+ rc = do_check( pk, sig, digest, r_expired, r_revoked, ret_pk );
+
+ /* Check the backsig. This is a 0x19 signature from the
+ subkey on the primary key. The idea here is that it should
+ not be possible for someone to "steal" subkeys and claim
+ them as their own. The attacker couldn't actually use the
+ subkey, but they could try and claim ownership of any
+ signaures issued by it. */
+ if(rc==0 && !pk->is_primary && pk->backsig<2)
+ {
+ if(pk->backsig==0)
+ {
+ log_info(_("WARNING: signing subkey %s is not"
+ " cross-certified\n"),keystr_from_pk(pk));
+ log_info(_("please see %s for more information\n"),
+ "http://www.gnupg.org/faq/subkey-cross-certify.html");
+ /* --require-cross-certification makes this warning an
+ error. TODO: change the default to require this
+ after more keys have backsigs. */
+ if(opt.flags.require_cross_cert)
+ rc=G10ERR_GENERAL;
+ }
+ else if(pk->backsig==1)
+ {
+ log_info(_("WARNING: signing subkey %s has an invalid"
+ " cross-certification\n"),keystr_from_pk(pk));
+ rc=G10ERR_GENERAL;
+ }
+ }
+ }
+
+ free_public_key( pk );
+
+ if( !rc && sig->sig_class < 2 && is_status_enabled() ) {
+ /* This signature id works best with DLP algorithms because
+ * they use a random parameter for every signature. Instead of
+ * this sig-id we could have also used the hash of the document
+ * and the timestamp, but the drawback of this is, that it is
+ * not possible to sign more than one identical document within
+ * one second. Some remote batch processing applications might
+ * like this feature here */
+ MD_HANDLE md;
+ u32 a = sig->timestamp;
+ int i, nsig = pubkey_get_nsig( sig->pubkey_algo );
+ byte *p, *buffer;
+
+ md = md_open( DIGEST_ALGO_RMD160, 0);
+ md_putc( digest, sig->pubkey_algo );
+ md_putc( digest, sig->digest_algo );
+ md_putc( digest, (a >> 24) & 0xff );
+ md_putc( digest, (a >> 16) & 0xff );
+ md_putc( digest, (a >> 8) & 0xff );
+ md_putc( digest, a & 0xff );
+ for(i=0; i < nsig; i++ ) {
+ unsigned n = mpi_get_nbits( sig->data[i]);
+
+ md_putc( md, n>>8);
+ md_putc( md, n );
+ p = mpi_get_buffer( sig->data[i], &n, NULL );
+ md_write( md, p, n );
+ xfree(p);
+ }
+ md_final( md );
+ p = make_radix64_string( md_read( md, 0 ), 20 );
+ buffer = xmalloc( strlen(p) + 60 );
+ sprintf( buffer, "%s %s %lu",
+ p, strtimestamp( sig->timestamp ), (ulong)sig->timestamp );
+ write_status_text( STATUS_SIG_ID, buffer );
+ xfree(buffer);
+ xfree(p);
+ md_close(md);
+ }
+
+ return rc;
+}
+
+
+static int
+do_check_messages( PKT_public_key *pk, PKT_signature *sig,
+ int *r_expired, int *r_revoked )
+{
+ u32 cur_time;
+
+ if(r_expired)
+ *r_expired = 0;
+ if(r_revoked)
+ *r_revoked = 0;
+
+ if( pk->timestamp > sig->timestamp )
+ {
+ ulong d = pk->timestamp - sig->timestamp;
+ log_info(d==1
+ ?_("public key %s is %lu second newer than the signature\n")
+ :_("public key %s is %lu seconds newer than the signature\n"),
+ keystr_from_pk(pk),d );
+ if( !opt.ignore_time_conflict )
+ return G10ERR_TIME_CONFLICT; /* pubkey newer than signature */
+ }
+
+ cur_time = make_timestamp();
+ if( pk->timestamp > cur_time )
+ {
+ ulong d = pk->timestamp - cur_time;
+ log_info( d==1
+ ? _("key %s was created %lu second"
+ " in the future (time warp or clock problem)\n")
+ : _("key %s was created %lu seconds"
+ " in the future (time warp or clock problem)\n"),
+ keystr_from_pk(pk),d );
+ if( !opt.ignore_time_conflict )
+ return G10ERR_TIME_CONFLICT;
+ }
+
+ if( pk->expiredate && pk->expiredate < cur_time ) {
+ char buf[11];
+ if (opt.verbose)
+ log_info(_("NOTE: signature key %s expired %s\n"),
+ keystr_from_pk(pk), asctimestamp( pk->expiredate ) );
+ /* SIGEXPIRED is deprecated. Use KEYEXPIRED. */
+ sprintf(buf,"%lu",(ulong)pk->expiredate);
+ write_status_text(STATUS_KEYEXPIRED,buf);
+ write_status(STATUS_SIGEXPIRED);
+ if(r_expired)
+ *r_expired = 1;
+ }
+
+ if(pk->is_revoked && r_revoked)
+ *r_revoked=1;
+
+ return 0;
+}
+
+
+static int
+do_check( PKT_public_key *pk, PKT_signature *sig, MD_HANDLE digest,
+ int *r_expired, int *r_revoked, PKT_public_key *ret_pk )
+{
+ MPI result = NULL;
+ int rc=0;
+ struct cmp_help_context_s ctx;
+
+ if( (rc=do_check_messages(pk,sig,r_expired,r_revoked)) )
+ return rc;
+
+ /* make sure the digest algo is enabled (in case of a detached signature)*/
+ md_enable( digest, sig->digest_algo );
+
+ /* complete the digest */
+ if( sig->version >= 4 )
+ md_putc( digest, sig->version );
+ md_putc( digest, sig->sig_class );
+ if( sig->version < 4 ) {
+ u32 a = sig->timestamp;
+ md_putc( digest, (a >> 24) & 0xff );
+ md_putc( digest, (a >> 16) & 0xff );
+ md_putc( digest, (a >> 8) & 0xff );
+ md_putc( digest, a & 0xff );
+ }
+ else {
+ byte buf[6];
+ size_t n;
+ md_putc( digest, sig->pubkey_algo );
+ md_putc( digest, sig->digest_algo );
+ if( sig->hashed ) {
+ n = sig->hashed->len;
+ md_putc (digest, (n >> 8) );
+ md_putc (digest, n );
+ md_write (digest, sig->hashed->data, n);
+ n += 6;
+ }
+ else {
+ /* Two octets for the (empty) length of the hashed
+ section. */
+ md_putc (digest, 0);
+ md_putc (digest, 0);
+ n = 6;
+ }
+ /* add some magic */
+ buf[0] = sig->version;
+ buf[1] = 0xff;
+ buf[2] = n >> 24;
+ buf[3] = n >> 16;
+ buf[4] = n >> 8;
+ buf[5] = n;
+ md_write( digest, buf, 6 );
+ }
+ md_final( digest );
+
+ result = encode_md_value( pk, NULL, digest, sig->digest_algo );
+ if (!result)
+ return G10ERR_GENERAL;
+ ctx.sig = sig;
+ ctx.md = digest;
+ rc = pubkey_verify( pk->pubkey_algo, result, sig->data, pk->pkey );
+ mpi_free( result );
+
+ if( !rc && sig->flags.unknown_critical )
+ {
+ log_info(_("assuming bad signature from key %s"
+ " due to an unknown critical bit\n"),keystr_from_pk(pk));
+ rc = G10ERR_BAD_SIGN;
+ }
+
+ if(!rc && ret_pk)
+ copy_public_key(ret_pk,pk);
+
+ return rc;
+}
+
+
+static void
+hash_uid_node( KBNODE unode, MD_HANDLE md, PKT_signature *sig )
+{
+ PKT_user_id *uid = unode->pkt->pkt.user_id;
+
+ assert( unode->pkt->pkttype == PKT_USER_ID );
+ if( uid->attrib_data ) {
+ if( sig->version >=4 ) {
+ byte buf[5];
+ buf[0] = 0xd1; /* packet of type 17 */
+ buf[1] = uid->attrib_len >> 24; /* always use 4 length bytes */
+ buf[2] = uid->attrib_len >> 16;
+ buf[3] = uid->attrib_len >> 8;
+ buf[4] = uid->attrib_len;
+ md_write( md, buf, 5 );
+ }
+ md_write( md, uid->attrib_data, uid->attrib_len );
+ }
+ else {
+ if( sig->version >=4 ) {
+ byte buf[5];
+ buf[0] = 0xb4; /* indicates a userid packet */
+ buf[1] = uid->len >> 24; /* always use 4 length bytes */
+ buf[2] = uid->len >> 16;
+ buf[3] = uid->len >> 8;
+ buf[4] = uid->len;
+ md_write( md, buf, 5 );
+ }
+ md_write( md, uid->name, uid->len );
+ }
+}
+
+static void
+cache_sig_result ( PKT_signature *sig, int result )
+{
+ if ( !result ) {
+ sig->flags.checked = 1;
+ sig->flags.valid = 1;
+ }
+ else if ( result == G10ERR_BAD_SIGN ) {
+ sig->flags.checked = 1;
+ sig->flags.valid = 0;
+ }
+ else {
+ sig->flags.checked = 0;
+ sig->flags.valid = 0;
+ }
+}
+
+/* Check the revocation keys to see if any of them have revoked our
+ pk. sig is the revocation sig. pk is the key it is on. This code
+ will need to be modified if gpg ever becomes multi-threaded. Note
+ that this guarantees that a designated revocation sig will never be
+ considered valid unless it is actually valid, as well as being
+ issued by a revocation key in a valid direct signature. Note also
+ that this is written so that a revoked revoker can still issue
+ revocations: i.e. If A revokes B, but A is revoked, B is still
+ revoked. I'm not completely convinced this is the proper behavior,
+ but it matches how PGP does it. -dms */
+
+/* Returns 0 if sig is valid (i.e. pk is revoked), non-0 if not
+ revoked. It is important that G10ERR_NO_PUBKEY is only returned
+ when a revocation signature is from a valid revocation key
+ designated in a revkey subpacket, but the revocation key itself
+ isn't present. */
+int
+check_revocation_keys(PKT_public_key *pk,PKT_signature *sig)
+{
+ static int busy=0;
+ int i,rc=G10ERR_GENERAL;
+
+ assert(IS_KEY_REV(sig));
+ assert((sig->keyid[0]!=pk->keyid[0]) || (sig->keyid[0]!=pk->keyid[1]));
+
+ if(busy)
+ {
+ /* return an error (i.e. not revoked), but mark the pk as
+ uncacheable as we don't really know its revocation status
+ until it is checked directly. */
+
+ pk->dont_cache=1;
+ return rc;
+ }
+
+ busy=1;
+
+ /* printf("looking at %08lX with a sig from %08lX\n",(ulong)pk->keyid[1],
+ (ulong)sig->keyid[1]); */
+
+ /* is the issuer of the sig one of our revokers? */
+ if( !pk->revkey && pk->numrevkeys )
+ BUG();
+ else
+ for(i=0;i<pk->numrevkeys;i++)
+ {
+ u32 keyid[2];
+
+ keyid_from_fingerprint(pk->revkey[i].fpr,MAX_FINGERPRINT_LEN,keyid);
+
+ if(keyid[0]==sig->keyid[0] && keyid[1]==sig->keyid[1])
+ {
+ MD_HANDLE md;
+
+ md=md_open(sig->digest_algo,0);
+ hash_public_key(md,pk);
+ rc=signature_check(sig,md);
+ cache_sig_result(sig,rc);
+ break;
+ }
+ }
+
+ busy=0;
+
+ return rc;
+}
+
+/* Backsigs (0x19) have the same format as binding sigs (0x18), but
+ this function is simpler than check_key_signature in a few ways.
+ For example, there is no support for expiring backsigs since it is
+ questionable what such a thing actually means. Note also that the
+ sig cache check here, unlike other sig caches in GnuPG, is not
+ persistent. */
+int
+check_backsig(PKT_public_key *main_pk,PKT_public_key *sub_pk,
+ PKT_signature *backsig)
+{
+ MD_HANDLE md;
+ int rc;
+
+ if(!opt.no_sig_cache && backsig->flags.checked)
+ {
+ if((rc=check_digest_algo(backsig->digest_algo)))
+ return rc;
+
+ return backsig->flags.valid? 0 : G10ERR_BAD_SIGN;
+ }
+
+ md=md_open(backsig->digest_algo,0);
+ hash_public_key(md,main_pk);
+ hash_public_key(md,sub_pk);
+ rc=do_check(sub_pk,backsig,md,NULL,NULL,NULL);
+ cache_sig_result(backsig,rc);
+ md_close(md);
+
+ return rc;
+}
+
+
+/****************
+ * check the signature pointed to by NODE. This is a key signature.
+ * If the function detects a self-signature, it uses the PK from
+ * ROOT and does not read any public key.
+ */
+int
+check_key_signature( KBNODE root, KBNODE node, int *is_selfsig )
+{
+ return check_key_signature2(root, node, NULL, NULL, is_selfsig, NULL, NULL );
+}
+
+/* If check_pk is set, then use it to check the signature in node
+ rather than getting it from root or the keydb. If ret_pk is set,
+ fill in the public key that was used to verify the signature.
+ ret_pk is only meaningful when the verification was successful. */
+/* TODO: add r_revoked here as well. It has the same problems as
+ r_expiredate and r_expired and the cache. */
+int
+check_key_signature2( KBNODE root, KBNODE node, PKT_public_key *check_pk,
+ PKT_public_key *ret_pk, int *is_selfsig,
+ u32 *r_expiredate, int *r_expired )
+{
+ MD_HANDLE md;
+ PKT_public_key *pk;
+ PKT_signature *sig;
+ int algo;
+ int rc;
+
+ if( is_selfsig )
+ *is_selfsig = 0;
+ if( r_expiredate )
+ *r_expiredate = 0;
+ if( r_expired )
+ *r_expired = 0;
+ assert( node->pkt->pkttype == PKT_SIGNATURE );
+ assert( root->pkt->pkttype == PKT_PUBLIC_KEY );
+
+ pk = root->pkt->pkt.public_key;
+ sig = node->pkt->pkt.signature;
+ algo = sig->digest_algo;
+
+ /* Check whether we have cached the result of a previous signature
+ check. Note that we may no longer have the pubkey or hash
+ needed to verify a sig, but can still use the cached value. A
+ cache refresh detects and clears these cases. */
+ if ( !opt.no_sig_cache ) {
+ if (sig->flags.checked) { /*cached status available*/
+ if( is_selfsig ) {
+ u32 keyid[2];
+
+ keyid_from_pk( pk, keyid );
+ if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] )
+ *is_selfsig = 1;
+ }
+ /* BUG: This is wrong for non-self-sigs.. needs to be the
+ actual pk */
+ if((rc=do_check_messages(pk,sig,r_expired,NULL)))
+ return rc;
+ return sig->flags.valid? 0 : G10ERR_BAD_SIGN;
+ }
+ }
+
+ if( (rc=check_pubkey_algo(sig->pubkey_algo)) )
+ return rc;
+ if( (rc=check_digest_algo(algo)) )
+ return rc;
+
+ if( sig->sig_class == 0x20 ) { /* key revocation */
+ u32 keyid[2];
+ keyid_from_pk( pk, keyid );
+
+ /* is it a designated revoker? */
+ if(keyid[0]!=sig->keyid[0] || keyid[1]!=sig->keyid[1])
+ rc=check_revocation_keys(pk,sig);
+ else
+ {
+ md = md_open( algo, 0 );
+ hash_public_key( md, pk );
+ rc = do_check( pk, sig, md, r_expired, NULL, ret_pk );
+ cache_sig_result ( sig, rc );
+ md_close(md);
+ }
+ }
+ else if( sig->sig_class == 0x28 ) { /* subkey revocation */
+ KBNODE snode = find_prev_kbnode( root, node, PKT_PUBLIC_SUBKEY );
+
+ if( snode ) {
+ md = md_open( algo, 0 );
+ hash_public_key( md, pk );
+ hash_public_key( md, snode->pkt->pkt.public_key );
+ rc = do_check( pk, sig, md, r_expired, NULL, ret_pk );
+ cache_sig_result ( sig, rc );
+ md_close(md);
+ }
+ else
+ {
+ if (opt.verbose)
+ log_info (_("key %s: no subkey for subkey"
+ " revocation signature\n"),keystr_from_pk(pk));
+ rc = G10ERR_SIG_CLASS;
+ }
+ }
+ else if( sig->sig_class == 0x18 ) { /* key binding */
+ KBNODE snode = find_prev_kbnode( root, node, PKT_PUBLIC_SUBKEY );
+
+ if( snode ) {
+ if( is_selfsig ) { /* does this make sense????? */
+ u32 keyid[2]; /* it should always be a selfsig */
+
+ keyid_from_pk( pk, keyid );
+ if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] )
+ *is_selfsig = 1;
+ }
+ md = md_open( algo, 0 );
+ hash_public_key( md, pk );
+ hash_public_key( md, snode->pkt->pkt.public_key );
+ rc = do_check( pk, sig, md, r_expired, NULL, ret_pk );
+ cache_sig_result ( sig, rc );
+ md_close(md);
+ }
+ else
+ {
+ if (opt.verbose)
+ log_info(_("key %s: no subkey for subkey"
+ " binding signature\n"),keystr_from_pk(pk));
+ rc = G10ERR_SIG_CLASS;
+ }
+ }
+ else if( sig->sig_class == 0x1f ) { /* direct key signature */
+ md = md_open( algo, 0 );
+ hash_public_key( md, pk );
+ rc = do_check( pk, sig, md, r_expired, NULL, ret_pk );
+ cache_sig_result ( sig, rc );
+ md_close(md);
+ }
+ else { /* all other classes */
+ KBNODE unode = find_prev_kbnode( root, node, PKT_USER_ID );
+
+ if( unode ) {
+ u32 keyid[2];
+
+ keyid_from_pk( pk, keyid );
+ md = md_open( algo, 0 );
+ hash_public_key( md, pk );
+ hash_uid_node( unode, md, sig );
+ if( keyid[0] == sig->keyid[0] && keyid[1] == sig->keyid[1] )
+ {
+ if( is_selfsig )
+ *is_selfsig = 1;
+ rc = do_check( pk, sig, md, r_expired, NULL, ret_pk );
+ }
+ else if (check_pk)
+ rc=do_check(check_pk,sig,md,r_expired,NULL,ret_pk);
+ else
+ rc=signature_check2(sig,md,r_expiredate,r_expired,NULL,ret_pk);
+
+ cache_sig_result ( sig, rc );
+ md_close(md);
+ }
+ else
+ {
+ if (!opt.quiet)
+ log_info ("key %s: no user ID for key signature packet"
+ " of class %02x\n",keystr_from_pk(pk),sig->sig_class);
+ rc = G10ERR_SIG_CLASS;
+ }
+ }
+
+ return rc;
+}
diff --git a/g10/sign.c b/g10/sign.c
new file mode 100644
index 0000000..82076d7
--- /dev/null
+++ b/g10/sign.c
@@ -0,0 +1,1578 @@
+/* sign.c - sign data
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004, 2005,
+ * 2006 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h> /* need sleep() */
+
+#include "options.h"
+#include "packet.h"
+#include "errors.h"
+#include "iobuf.h"
+#include "keydb.h"
+#include "memory.h"
+#include "util.h"
+#include "main.h"
+#include "filter.h"
+#include "ttyio.h"
+#include "trustdb.h"
+#include "status.h"
+#include "i18n.h"
+#include "cardglue.h"
+
+
+#ifdef HAVE_DOSISH_SYSTEM
+#define LF "\r\n"
+void __stdcall Sleep(ulong);
+#define sleep(a) Sleep((a)*1000)
+#else
+#define LF "\n"
+#endif
+
+static int recipient_digest_algo=0;
+
+/****************
+ * Create notations and other stuff. It is assumed that the stings in
+ * STRLIST are already checked to contain only printable data and have
+ * a valid NAME=VALUE format.
+ */
+static void
+mk_notation_policy_etc( PKT_signature *sig,
+ PKT_public_key *pk, PKT_secret_key *sk )
+{
+ const char *string;
+ char *s=NULL;
+ STRLIST pu=NULL;
+ struct notation *nd=NULL;
+ struct expando_args args;
+
+ memset(&args,0,sizeof(args));
+ args.pk=pk;
+ args.sk=sk;
+
+ /* It is actually impossible to get here when making a v3 key
+ signature since keyedit.c:sign_uids will automatically bump a
+ signature with a notation or policy url up to v4, but it is
+ good to do these checks anyway. */
+
+ /* notation data */
+ if(IS_SIG(sig) && opt.sig_notations)
+ {
+ if(sig->version<4)
+ log_error(_("can't put notation data into v3 (PGP 2.x style) "
+ "signatures\n"));
+ else
+ nd=opt.sig_notations;
+ }
+ else if( IS_CERT(sig) && opt.cert_notations )
+ {
+ if(sig->version<4)
+ log_error(_("can't put notation data into v3 (PGP 2.x style) "
+ "key signatures\n"));
+ else
+ nd=opt.cert_notations;
+ }
+
+ if(nd)
+ {
+ struct notation *i;
+
+ for(i=nd;i;i=i->next)
+ {
+ i->altvalue=pct_expando(i->value,&args);
+ if(!i->altvalue)
+ log_error(_("WARNING: unable to %%-expand notation "
+ "(too large). Using unexpanded.\n"));
+ }
+
+ keygen_add_notations(sig,nd);
+
+ for(i=nd;i;i=i->next)
+ {
+ xfree(i->altvalue);
+ i->altvalue=NULL;
+ }
+ }
+
+ /* set policy URL */
+ if( IS_SIG(sig) && opt.sig_policy_url )
+ {
+ if(sig->version<4)
+ log_error(_("can't put a policy URL into v3 (PGP 2.x style) "
+ "signatures\n"));
+ else
+ pu=opt.sig_policy_url;
+ }
+ else if( IS_CERT(sig) && opt.cert_policy_url )
+ {
+ if(sig->version<4)
+ log_error(_("can't put a policy URL into v3 key (PGP 2.x style) "
+ "signatures\n"));
+ else
+ pu=opt.cert_policy_url;
+ }
+
+ for(;pu;pu=pu->next)
+ {
+ string = pu->d;
+
+ s=pct_expando(string,&args);
+ if(!s)
+ {
+ log_error(_("WARNING: unable to %%-expand policy URL "
+ "(too large). Using unexpanded.\n"));
+ s=xstrdup(string);
+ }
+
+ build_sig_subpkt(sig,SIGSUBPKT_POLICY|
+ ((pu->flags & 1)?SIGSUBPKT_FLAG_CRITICAL:0),
+ s,strlen(s));
+
+ xfree(s);
+ }
+
+ /* preferred keyserver URL */
+ if( IS_SIG(sig) && opt.sig_keyserver_url )
+ {
+ if(sig->version<4)
+ log_info("can't put a preferred keyserver URL into v3 signatures\n");
+ else
+ pu=opt.sig_keyserver_url;
+ }
+
+ for(;pu;pu=pu->next)
+ {
+ string = pu->d;
+
+ s=pct_expando(string,&args);
+ if(!s)
+ {
+ log_error(_("WARNING: unable to %%-expand preferred keyserver URL"
+ " (too large). Using unexpanded.\n"));
+ s=xstrdup(string);
+ }
+
+ build_sig_subpkt(sig,SIGSUBPKT_PREF_KS|
+ ((pu->flags & 1)?SIGSUBPKT_FLAG_CRITICAL:0),
+ s,strlen(s));
+
+ xfree(s);
+ }
+}
+
+
+/*
+ * Helper to hash a user ID packet.
+ */
+static void
+hash_uid (MD_HANDLE md, int sigversion, const PKT_user_id *uid)
+{
+ if ( sigversion >= 4 ) {
+ byte buf[5];
+
+ if(uid->attrib_data) {
+ buf[0] = 0xd1; /* indicates an attribute packet */
+ buf[1] = uid->attrib_len >> 24; /* always use 4 length bytes */
+ buf[2] = uid->attrib_len >> 16;
+ buf[3] = uid->attrib_len >> 8;
+ buf[4] = uid->attrib_len;
+ }
+ else {
+ buf[0] = 0xb4; /* indicates a userid packet */
+ buf[1] = uid->len >> 24; /* always use 4 length bytes */
+ buf[2] = uid->len >> 16;
+ buf[3] = uid->len >> 8;
+ buf[4] = uid->len;
+ }
+ md_write( md, buf, 5 );
+ }
+
+ if(uid->attrib_data)
+ md_write (md, uid->attrib_data, uid->attrib_len );
+ else
+ md_write (md, uid->name, uid->len );
+}
+
+
+/*
+ * Helper to hash some parts from the signature
+ */
+static void
+hash_sigversion_to_magic (MD_HANDLE md, const PKT_signature *sig)
+{
+ if (sig->version >= 4)
+ md_putc (md, sig->version);
+ md_putc (md, sig->sig_class);
+ if (sig->version < 4) {
+ u32 a = sig->timestamp;
+ md_putc (md, (a >> 24) & 0xff );
+ md_putc (md, (a >> 16) & 0xff );
+ md_putc (md, (a >> 8) & 0xff );
+ md_putc (md, a & 0xff );
+ }
+ else {
+ byte buf[6];
+ size_t n;
+
+ md_putc (md, sig->pubkey_algo);
+ md_putc (md, sig->digest_algo);
+ if (sig->hashed) {
+ n = sig->hashed->len;
+ md_putc (md, (n >> 8) );
+ md_putc (md, n );
+ md_write (md, sig->hashed->data, n );
+ n += 6;
+ }
+ else {
+ md_putc (md, 0); /* always hash the length of the subpacket*/
+ md_putc (md, 0);
+ n = 6;
+ }
+ /* add some magic */
+ buf[0] = sig->version;
+ buf[1] = 0xff;
+ buf[2] = n >> 24; /* hmmm, n is only 16 bit, so this is always 0 */
+ buf[3] = n >> 16;
+ buf[4] = n >> 8;
+ buf[5] = n;
+ md_write (md, buf, 6);
+ }
+}
+
+
+static int
+do_sign( PKT_secret_key *sk, PKT_signature *sig,
+ MD_HANDLE md, int digest_algo )
+{
+ MPI frame;
+ byte *dp;
+ int rc;
+
+ if( sk->timestamp > sig->timestamp ) {
+ ulong d = sk->timestamp - sig->timestamp;
+ log_info( d==1 ? _("key has been created %lu second "
+ "in future (time warp or clock problem)\n")
+ : _("key has been created %lu seconds "
+ "in future (time warp or clock problem)\n"), d );
+ if( !opt.ignore_time_conflict )
+ return G10ERR_TIME_CONFLICT;
+ }
+
+
+ print_pubkey_algo_note(sk->pubkey_algo);
+
+ if( !digest_algo )
+ digest_algo = md_get_algo(md);
+
+ print_digest_algo_note( digest_algo );
+ dp = md_read( md, digest_algo );
+ sig->digest_algo = digest_algo;
+ sig->digest_start[0] = dp[0];
+ sig->digest_start[1] = dp[1];
+ if (sk->is_protected && sk->protect.s2k.mode == 1002)
+ {
+#ifdef ENABLE_CARD_SUPPORT
+ unsigned char *rbuf;
+ size_t rbuflen;
+ char *snbuf;
+
+ snbuf = serialno_and_fpr_from_sk (sk->protect.iv,
+ sk->protect.ivlen, sk);
+ rc = agent_scd_pksign (snbuf, digest_algo,
+ md_read (md, digest_algo),
+ md_digest_length (digest_algo),
+ &rbuf, &rbuflen);
+ xfree (snbuf);
+ if (!rc)
+ {
+ sig->data[0] = mpi_alloc ( (rbuflen+BYTES_PER_MPI_LIMB-1)
+ / BYTES_PER_MPI_LIMB );
+ mpi_set_buffer (sig->data[0], rbuf, rbuflen, 0);
+ xfree (rbuf);
+ }
+#else
+ return G10ERR_UNSUPPORTED;
+#endif /* ENABLE_CARD_SUPPORT */
+ }
+ else
+ {
+ frame = encode_md_value( NULL, sk, md, digest_algo );
+ if (!frame)
+ return G10ERR_GENERAL;
+ rc = pubkey_sign( sk->pubkey_algo, sig->data, frame, sk->skey );
+ mpi_free(frame);
+ }
+
+ if (!rc && !opt.no_sig_create_check) {
+ /* check that the signature verification worked and nothing is
+ * fooling us e.g. by a bug in the signature create
+ * code or by deliberately introduced faults. */
+ PKT_public_key *pk = xmalloc_clear (sizeof *pk);
+
+ if( get_pubkey( pk, sig->keyid ) )
+ rc = G10ERR_NO_PUBKEY;
+ else {
+ frame = encode_md_value (pk, NULL, md, sig->digest_algo );
+ if (!frame)
+ rc = G10ERR_GENERAL;
+ else
+ rc = pubkey_verify (pk->pubkey_algo, frame,
+ sig->data, pk->pkey );
+ mpi_free (frame);
+ }
+ if (rc)
+ log_error (_("checking created signature failed: %s\n"),
+ g10_errstr (rc));
+ free_public_key (pk);
+ }
+ if( rc )
+ log_error(_("signing failed: %s\n"), g10_errstr(rc) );
+ else {
+ if( opt.verbose ) {
+ char *ustr = get_user_id_string_native (sig->keyid);
+ log_info(_("%s/%s signature from: \"%s\"\n"),
+ pubkey_algo_to_string(sk->pubkey_algo),
+ digest_algo_to_string(sig->digest_algo),
+ ustr );
+ xfree(ustr);
+ }
+ }
+ return rc;
+}
+
+
+int
+complete_sig( PKT_signature *sig, PKT_secret_key *sk, MD_HANDLE md )
+{
+ int rc=0;
+
+ if( !(rc=check_secret_key( sk, 0 )) )
+ rc = do_sign( sk, sig, md, 0 );
+ return rc;
+}
+
+static int
+match_dsa_hash(unsigned int qbytes)
+{
+ if(qbytes<=20)
+ return DIGEST_ALGO_SHA1;
+#ifdef USE_SHA256
+ if(qbytes<=28)
+ return DIGEST_ALGO_SHA224;
+ if(qbytes<=32)
+ return DIGEST_ALGO_SHA256;
+#endif
+#ifdef USE_SHA512
+ if(qbytes<=48)
+ return DIGEST_ALGO_SHA384;
+ if(qbytes<=64)
+ return DIGEST_ALGO_SHA512;
+#endif
+ return DEFAULT_DIGEST_ALGO;
+ /* DEFAULT_DIGEST_ALGO will certainly fail, but it's the best wrong
+ answer we have if the larger SHAs aren't there. */
+}
+
+
+/*
+ First try --digest-algo. If that isn't set, see if the recipient
+ has a preferred algorithm (which is also filtered through
+ --preferred-digest-prefs). If we're making a signature without a
+ particular recipient (i.e. signing, rather than signing+encrypting)
+ then take the first algorithm in --preferred-digest-prefs that is
+ usable for the pubkey algorithm. If --preferred-digest-prefs isn't
+ set, then take the OpenPGP default (i.e. SHA-1).
+
+ Possible improvement: Use the highest-ranked usable algorithm from
+ the signing key prefs either before or after using the personal
+ list?
+*/
+
+static int
+hash_for(PKT_secret_key *sk)
+{
+ if( opt.def_digest_algo )
+ return opt.def_digest_algo;
+ else if( recipient_digest_algo )
+ return recipient_digest_algo;
+ else if(sk->pubkey_algo==PUBKEY_ALGO_DSA)
+ {
+ unsigned int qbytes=mpi_get_nbits(sk->skey[1])/8;
+
+ /* It's a DSA key, so find a hash that is the same size as q or
+ larger. If q is 160, assume it is an old DSA key and use a
+ 160-bit hash unless --enable-dsa2 is set, in which case act
+ like a new DSA key that just happens to have a 160-bit q
+ (i.e. allow truncation). If q is not 160, by definition it
+ must be a new DSA key. */
+
+ if(opt.personal_digest_prefs)
+ {
+ prefitem_t *prefs;
+
+ if(qbytes!=20 || opt.flags.dsa2)
+ {
+ for(prefs=opt.personal_digest_prefs;prefs->type;prefs++)
+ if(md_digest_length(prefs->value)>=qbytes)
+ return prefs->value;
+ }
+ else
+ {
+ for(prefs=opt.personal_digest_prefs;prefs->type;prefs++)
+ if(md_digest_length(prefs->value)==qbytes)
+ return prefs->value;
+ }
+ }
+
+ return match_dsa_hash(qbytes);
+ }
+ else if(sk->is_protected && sk->protect.s2k.mode==1002)
+ {
+ /* The sk lives on a smartcard, and current smartcards only
+ handle SHA-1 and RIPEMD/160. This is correct now, but may
+ need revision as the cards add algorithms. */
+
+ if(opt.personal_digest_prefs)
+ {
+ prefitem_t *prefs;
+
+ for(prefs=opt.personal_digest_prefs;prefs->type;prefs++)
+ if(prefs->value==DIGEST_ALGO_SHA1
+ || prefs->value==DIGEST_ALGO_RMD160)
+ return prefs->value;
+ }
+
+ return DIGEST_ALGO_SHA1;
+ }
+ else if(PGP2 && sk->pubkey_algo == PUBKEY_ALGO_RSA && sk->version < 4 )
+ {
+ /* Old-style PGP only understands MD5 */
+ return DIGEST_ALGO_MD5;
+ }
+ else if( opt.personal_digest_prefs )
+ {
+ /* It's not DSA, so we can use whatever the first hash algorithm
+ is in the pref list */
+ return opt.personal_digest_prefs[0].value;
+ }
+ else
+ return DEFAULT_DIGEST_ALGO;
+}
+
+static int
+only_old_style( SK_LIST sk_list )
+{
+ SK_LIST sk_rover = NULL;
+ int old_style = 0;
+
+ /* if there are only old style capable key we use the old sytle */
+ for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
+ PKT_secret_key *sk = sk_rover->sk;
+ if( sk->pubkey_algo == PUBKEY_ALGO_RSA && sk->version < 4 )
+ old_style = 1;
+ else
+ return 0;
+ }
+ return old_style;
+}
+
+
+static void
+print_status_sig_created ( PKT_secret_key *sk, PKT_signature *sig, int what )
+{
+ byte array[MAX_FINGERPRINT_LEN], *p;
+ char buf[100+MAX_FINGERPRINT_LEN*2];
+ size_t i, n;
+
+ sprintf(buf, "%c %d %d %02x %lu ",
+ what, sig->pubkey_algo, sig->digest_algo, sig->sig_class,
+ (ulong)sig->timestamp );
+
+ fingerprint_from_sk( sk, array, &n );
+ p = buf + strlen(buf);
+ for(i=0; i < n ; i++ )
+ sprintf(p+2*i, "%02X", array[i] );
+
+ write_status_text( STATUS_SIG_CREATED, buf );
+}
+
+
+/*
+ * Loop over the secret certificates in SK_LIST and build the one pass
+ * signature packets. OpenPGP says that the data should be bracket by
+ * the onepass-sig and signature-packet; so we build these onepass
+ * packet here in reverse order
+ */
+static int
+write_onepass_sig_packets (SK_LIST sk_list, IOBUF out, int sigclass )
+{
+ int skcount;
+ SK_LIST sk_rover;
+
+ for (skcount=0, sk_rover=sk_list; sk_rover; sk_rover = sk_rover->next)
+ skcount++;
+
+ for (; skcount; skcount--) {
+ PKT_secret_key *sk;
+ PKT_onepass_sig *ops;
+ PACKET pkt;
+ int i, rc;
+
+ for (i=0, sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
+ if (++i == skcount)
+ break;
+ }
+
+ sk = sk_rover->sk;
+ ops = xmalloc_clear (sizeof *ops);
+ ops->sig_class = sigclass;
+ ops->digest_algo = hash_for (sk);
+ ops->pubkey_algo = sk->pubkey_algo;
+ keyid_from_sk (sk, ops->keyid);
+ ops->last = (skcount == 1);
+
+ init_packet(&pkt);
+ pkt.pkttype = PKT_ONEPASS_SIG;
+ pkt.pkt.onepass_sig = ops;
+ rc = build_packet (out, &pkt);
+ free_packet (&pkt);
+ if (rc) {
+ log_error ("build onepass_sig packet failed: %s\n",
+ g10_errstr(rc));
+ return rc;
+ }
+ }
+
+ return 0;
+}
+
+/*
+ * Helper to write the plaintext (literal data) packet
+ */
+static int
+write_plaintext_packet (IOBUF out, IOBUF inp, const char *fname, int ptmode)
+{
+ PKT_plaintext *pt = NULL;
+ u32 filesize;
+ int rc = 0;
+
+ if (!opt.no_literal)
+ pt=setup_plaintext_name(fname,inp);
+
+ /* try to calculate the length of the data */
+ if ( !iobuf_is_pipe_filename (fname) && *fname )
+ {
+ off_t tmpsize;
+ int overflow;
+
+ if( !(tmpsize = iobuf_get_filelength(inp, &overflow))
+ && !overflow )
+ log_info (_("WARNING: `%s' is an empty file\n"), fname);
+
+ /* We can't encode the length of very large files because
+ OpenPGP uses only 32 bit for file sizes. So if the size of
+ a file is larger than 2^32 minus some bytes for packet
+ headers, we switch to partial length encoding. */
+ if ( tmpsize < (IOBUF_FILELENGTH_LIMIT - 65536) )
+ filesize = tmpsize;
+ else
+ filesize = 0;
+
+ /* Because the text_filter modifies the length of the
+ * data, it is not possible to know the used length
+ * without a double read of the file - to avoid that
+ * we simple use partial length packets. */
+ if ( ptmode == 't' )
+ filesize = 0;
+ }
+ else
+ filesize = opt.set_filesize? opt.set_filesize : 0; /* stdin */
+
+ if (!opt.no_literal) {
+ PACKET pkt;
+
+ pt->timestamp = make_timestamp ();
+ pt->mode = ptmode;
+ pt->len = filesize;
+ pt->new_ctb = !pt->len && !RFC1991;
+ pt->buf = inp;
+ init_packet(&pkt);
+ pkt.pkttype = PKT_PLAINTEXT;
+ pkt.pkt.plaintext = pt;
+ /*cfx.datalen = filesize? calc_packet_length( &pkt ) : 0;*/
+ if( (rc = build_packet (out, &pkt)) )
+ log_error ("build_packet(PLAINTEXT) failed: %s\n",
+ g10_errstr(rc) );
+ pt->buf = NULL;
+ }
+ else {
+ byte copy_buffer[4096];
+ int bytes_copied;
+
+ while ((bytes_copied = iobuf_read(inp, copy_buffer, 4096)) != -1)
+ if (iobuf_write(out, copy_buffer, bytes_copied) == -1) {
+ rc = G10ERR_WRITE_FILE;
+ log_error ("copying input to output failed: %s\n",
+ g10_errstr(rc));
+ break;
+ }
+ wipememory(copy_buffer,4096); /* burn buffer */
+ }
+ /* fixme: it seems that we never freed pt/pkt */
+
+ return rc;
+}
+
+/*
+ * Write the signatures from the SK_LIST to OUT. HASH must be a non-finalized
+ * hash which will not be changes here.
+ */
+static int
+write_signature_packets (SK_LIST sk_list, IOBUF out, MD_HANDLE hash,
+ int sigclass, u32 timestamp, u32 duration,
+ int status_letter)
+{
+ SK_LIST sk_rover;
+
+ /* loop over the secret certificates */
+ for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) {
+ PKT_secret_key *sk;
+ PKT_signature *sig;
+ MD_HANDLE md;
+ int rc;
+
+ sk = sk_rover->sk;
+
+ /* build the signature packet */
+ sig = xmalloc_clear (sizeof *sig);
+ if(opt.force_v3_sigs || RFC1991)
+ sig->version=3;
+ else if(duration || opt.sig_policy_url
+ || opt.sig_notations || opt.sig_keyserver_url)
+ sig->version=4;
+ else
+ sig->version=sk->version;
+ keyid_from_sk (sk, sig->keyid);
+ sig->digest_algo = hash_for(sk);
+ sig->pubkey_algo = sk->pubkey_algo;
+ if(timestamp)
+ sig->timestamp = timestamp;
+ else
+ sig->timestamp = make_timestamp();
+ if(duration)
+ sig->expiredate = sig->timestamp+duration;
+ sig->sig_class = sigclass;
+
+ md = md_copy (hash);
+
+ if (sig->version >= 4)
+ build_sig_subpkt_from_sig (sig);
+ mk_notation_policy_etc (sig, NULL, sk);
+
+ hash_sigversion_to_magic (md, sig);
+ md_final (md);
+
+ rc = do_sign( sk, sig, md, hash_for (sk) );
+ md_close (md);
+
+ if( !rc ) { /* and write it */
+ PACKET pkt;
+
+ init_packet(&pkt);
+ pkt.pkttype = PKT_SIGNATURE;
+ pkt.pkt.signature = sig;
+ rc = build_packet (out, &pkt);
+ if (!rc && is_status_enabled()) {
+ print_status_sig_created ( sk, sig, status_letter);
+ }
+ free_packet (&pkt);
+ if (rc)
+ log_error ("build signature packet failed: %s\n",
+ g10_errstr(rc) );
+ }
+ if( rc )
+ return rc;;
+ }
+
+ return 0;
+}
+
+/****************
+ * Sign the files whose names are in FILENAME.
+ * If DETACHED has the value true,
+ * make a detached signature. If FILENAMES->d is NULL read from stdin
+ * and ignore the detached mode. Sign the file with all secret keys
+ * which can be taken from LOCUSR, if this is NULL, use the default one
+ * If ENCRYPTFLAG is true, use REMUSER (or ask if it is NULL) to encrypt the
+ * signed data for these users.
+ * If OUTFILE is not NULL; this file is used for output and the function
+ * does not ask for overwrite permission; output is then always
+ * uncompressed, non-armored and in binary mode.
+ */
+int
+sign_file( STRLIST filenames, int detached, STRLIST locusr,
+ int encryptflag, STRLIST remusr, const char *outfile )
+{
+ const char *fname;
+ armor_filter_context_t afx;
+ compress_filter_context_t zfx;
+ md_filter_context_t mfx;
+ text_filter_context_t tfx;
+ progress_filter_context_t pfx;
+ encrypt_filter_context_t efx;
+ IOBUF inp = NULL, out = NULL;
+ PACKET pkt;
+ int rc = 0;
+ PK_LIST pk_list = NULL;
+ SK_LIST sk_list = NULL;
+ SK_LIST sk_rover = NULL;
+ int multifile = 0;
+ u32 duration=0;
+
+ memset( &afx, 0, sizeof afx);
+ memset( &zfx, 0, sizeof zfx);
+ memset( &mfx, 0, sizeof mfx);
+ memset( &efx, 0, sizeof efx);
+ init_packet( &pkt );
+
+ if( filenames ) {
+ fname = filenames->d;
+ multifile = !!filenames->next;
+ }
+ else
+ fname = NULL;
+
+ if( fname && filenames->next && (!detached || encryptflag) )
+ log_bug("multiple files can only be detached signed");
+
+ if(encryptflag==2
+ && (rc=setup_symkey(&efx.symkey_s2k,&efx.symkey_dek)))
+ goto leave;
+
+ if(!opt.force_v3_sigs && !RFC1991)
+ {
+ if(opt.ask_sig_expire && !opt.batch)
+ duration=ask_expire_interval(1,opt.def_sig_expire);
+ else
+ duration=parse_expire_string(opt.def_sig_expire);
+ }
+
+ if( (rc=build_sk_list( locusr, &sk_list, 1, PUBKEY_USAGE_SIG )) )
+ goto leave;
+
+ if(PGP2 && !only_old_style(sk_list))
+ {
+ log_info(_("you can only detach-sign with PGP 2.x style keys "
+ "while in --pgp2 mode\n"));
+ compliance_failure();
+ }
+
+ if(encryptflag && (rc=build_pk_list( remusr, &pk_list, PUBKEY_USAGE_ENC )))
+ goto leave;
+
+ /* prepare iobufs */
+ if( multifile ) /* have list of filenames */
+ inp = NULL; /* we do it later */
+ else {
+ inp = iobuf_open(fname);
+ if (inp && is_secured_file (iobuf_get_fd (inp)))
+ {
+ iobuf_close (inp);
+ inp = NULL;
+ errno = EPERM;
+ }
+ if( !inp ) {
+ log_error(_("can't open `%s': %s\n"), fname? fname: "[stdin]",
+ strerror(errno) );
+ rc = G10ERR_OPEN_FILE;
+ goto leave;
+ }
+
+ handle_progress (&pfx, inp, fname);
+ }
+
+ if( outfile ) {
+ if (is_secured_filename ( outfile )) {
+ out = NULL;
+ errno = EPERM;
+ }
+ else
+ out = iobuf_create( outfile );
+ if( !out )
+ {
+ log_error(_("can't create `%s': %s\n"), outfile, strerror(errno) );
+ rc = G10ERR_CREATE_FILE;
+ goto leave;
+ }
+ else if( opt.verbose )
+ log_info(_("writing to `%s'\n"), outfile );
+ }
+ else if( (rc = open_outfile( fname, opt.armor? 1: detached? 2:0, &out )))
+ goto leave;
+
+ /* prepare to calculate the MD over the input */
+ if( opt.textmode && !outfile && !multifile )
+ {
+ memset( &tfx, 0, sizeof tfx);
+ iobuf_push_filter( inp, text_filter, &tfx );
+ }
+
+ mfx.md = md_open(0, 0);
+ if (DBG_HASHING)
+ md_start_debug (mfx.md, "sign");
+
+ /* If we're encrypting and signing, it is reasonable to pick the
+ hash algorithm to use out of the recepient key prefs. This is
+ best effort only, as in a DSA2 and smartcard world there are
+ cases where we cannot please everyone with a single hash (DSA2
+ wants >160 and smartcards want =160). In the future this could
+ be more complex with different hashes for each sk, but the
+ current design requires a single hash for all SKs. */
+ if(pk_list)
+ {
+ if(opt.def_digest_algo)
+ {
+ if(!opt.expert &&
+ select_algo_from_prefs(pk_list,PREFTYPE_HASH,
+ opt.def_digest_algo,
+ NULL)!=opt.def_digest_algo)
+ log_info(_("WARNING: forcing digest algorithm %s (%d)"
+ " violates recipient preferences\n"),
+ digest_algo_to_string(opt.def_digest_algo),
+ opt.def_digest_algo);
+ }
+ else
+ {
+ union pref_hint hint;
+ int algo,smartcard=0;
+
+ hint.digest_length=0;
+
+ /* Of course, if the recipient asks for something
+ unreasonable (like the wrong hash for a DSA key) then
+ don't do it. Check all sk's - if any are DSA or live
+ on a smartcard, then the hash has restrictions and we
+ may not be able to give the recipient what they want.
+ For DSA, pass a hint for the largest q we have. Note
+ that this means that a q>160 key will override a q=160
+ key and force the use of truncation for the q=160 key.
+ The alternative would be to ignore the recipient prefs
+ completely and get a different hash for each DSA key in
+ hash_for(). The override behavior here is more or less
+ reasonable as it is under the control of the user which
+ keys they sign with for a given message and the fact
+ that the message with multiple signatures won't be
+ usable on an implementation that doesn't understand
+ DSA2 anyway. */
+
+ for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next )
+ {
+ if(sk_rover->sk->pubkey_algo==PUBKEY_ALGO_DSA)
+ {
+ int temp_hashlen=mpi_get_nbits(sk_rover->sk->skey[1])/8;
+
+ /* Pick a hash that is large enough for our
+ largest q */
+
+ if(hint.digest_length<temp_hashlen)
+ hint.digest_length=temp_hashlen;
+ }
+ else if(sk_rover->sk->is_protected
+ && sk_rover->sk->protect.s2k.mode==1002)
+ smartcard=1;
+ }
+
+ /* Current smartcards only do 160-bit hashes. If we have
+ to have a >160-bit hash, then we can't use the
+ recipient prefs as we'd need both =160 and >160 at the
+ same time and recipient prefs currently require a
+ single hash for all signatures. All this may well have
+ to change as the cards add algorithms. */
+
+ if(!smartcard || (smartcard && hint.digest_length==20))
+ if((algo=
+ select_algo_from_prefs(pk_list,PREFTYPE_HASH,-1,&hint))>0)
+ recipient_digest_algo=algo;
+ }
+ }
+
+ for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
+ PKT_secret_key *sk = sk_rover->sk;
+ md_enable(mfx.md, hash_for(sk));
+ }
+
+ if( !multifile )
+ iobuf_push_filter( inp, md_filter, &mfx );
+
+ if( detached && !encryptflag && !RFC1991 )
+ afx.what = 2;
+
+ if( opt.armor && !outfile )
+ iobuf_push_filter( out, armor_filter, &afx );
+
+ if( encryptflag ) {
+ efx.pk_list = pk_list;
+ /* fixme: set efx.cfx.datalen if known */
+ iobuf_push_filter( out, encrypt_filter, &efx );
+ }
+
+ if( opt.compress_algo && !outfile && ( !detached || opt.compress_sigs) )
+ {
+ int compr_algo=opt.compress_algo;
+
+ /* If not forced by user */
+ if(compr_algo==-1)
+ {
+ /* If we're not encrypting, then select_algo_from_prefs
+ will fail and we'll end up with the default. If we are
+ encrypting, select_algo_from_prefs cannot fail since
+ there is an assumed preference for uncompressed data.
+ Still, if it did fail, we'll also end up with the
+ default. */
+
+ if((compr_algo=
+ select_algo_from_prefs(pk_list,PREFTYPE_ZIP,-1,NULL))==-1)
+ compr_algo=default_compress_algo();
+ }
+ else if(!opt.expert && pk_list
+ && select_algo_from_prefs(pk_list,PREFTYPE_ZIP,
+ compr_algo,NULL)!=compr_algo)
+ log_info(_("WARNING: forcing compression algorithm %s (%d)"
+ " violates recipient preferences\n"),
+ compress_algo_to_string(compr_algo),compr_algo);
+
+ /* algo 0 means no compression */
+ if( compr_algo )
+ push_compress_filter(out,&zfx,compr_algo);
+ }
+
+ /* Write the one-pass signature packets if needed */
+ if (!detached && !RFC1991) {
+ rc = write_onepass_sig_packets (sk_list, out,
+ opt.textmode && !outfile ? 0x01:0x00);
+ if (rc)
+ goto leave;
+ }
+
+ write_status (STATUS_BEGIN_SIGNING);
+
+ /* Setup the inner packet. */
+ if( detached ) {
+ if( multifile ) {
+ STRLIST sl;
+
+ if( opt.verbose )
+ log_info(_("signing:") );
+ /* must walk reverse trough this list */
+ for( sl = strlist_last(filenames); sl;
+ sl = strlist_prev( filenames, sl ) ) {
+ inp = iobuf_open(sl->d);
+ if (inp && is_secured_file (iobuf_get_fd (inp)))
+ {
+ iobuf_close (inp);
+ inp = NULL;
+ errno = EPERM;
+ }
+ if( !inp )
+ {
+ log_error(_("can't open `%s': %s\n"),
+ sl->d,strerror(errno));
+ rc = G10ERR_OPEN_FILE;
+ goto leave;
+ }
+ handle_progress (&pfx, inp, sl->d);
+ if( opt.verbose )
+ fprintf(stderr, " `%s'", sl->d );
+ if(opt.textmode)
+ {
+ memset( &tfx, 0, sizeof tfx);
+ iobuf_push_filter( inp, text_filter, &tfx );
+ }
+ iobuf_push_filter( inp, md_filter, &mfx );
+ while( iobuf_get(inp) != -1 )
+ ;
+ iobuf_close(inp); inp = NULL;
+ }
+ if( opt.verbose )
+ putc( '\n', stderr );
+ }
+ else {
+ /* read, so that the filter can calculate the digest */
+ while( iobuf_get(inp) != -1 )
+ ;
+ }
+ }
+ else {
+ rc = write_plaintext_packet (out, inp, fname,
+ opt.textmode && !outfile ? 't':'b');
+ }
+
+ /* catch errors from above */
+ if (rc)
+ goto leave;
+
+ /* write the signatures */
+ rc = write_signature_packets (sk_list, out, mfx.md,
+ opt.textmode && !outfile? 0x01 : 0x00,
+ 0, duration, detached ? 'D':'S');
+ if( rc )
+ goto leave;
+
+
+ leave:
+ if( rc )
+ iobuf_cancel(out);
+ else {
+ iobuf_close(out);
+ if (encryptflag)
+ write_status( STATUS_END_ENCRYPTION );
+ }
+ iobuf_close(inp);
+ md_close( mfx.md );
+ release_sk_list( sk_list );
+ release_pk_list( pk_list );
+ recipient_digest_algo=0;
+ return rc;
+}
+
+
+
+/****************
+ * make a clear signature. note that opt.armor is not needed
+ */
+int
+clearsign_file( const char *fname, STRLIST locusr, const char *outfile )
+{
+ armor_filter_context_t afx;
+ progress_filter_context_t pfx;
+ MD_HANDLE textmd = NULL;
+ IOBUF inp = NULL, out = NULL;
+ PACKET pkt;
+ int rc = 0;
+ SK_LIST sk_list = NULL;
+ SK_LIST sk_rover = NULL;
+ int old_style = RFC1991;
+ int only_md5 = 0;
+ u32 duration=0;
+
+ memset( &afx, 0, sizeof afx);
+ init_packet( &pkt );
+
+ if(!opt.force_v3_sigs && !RFC1991)
+ {
+ if(opt.ask_sig_expire && !opt.batch)
+ duration=ask_expire_interval(1,opt.def_sig_expire);
+ else
+ duration=parse_expire_string(opt.def_sig_expire);
+ }
+
+ if( (rc=build_sk_list( locusr, &sk_list, 1, PUBKEY_USAGE_SIG )) )
+ goto leave;
+
+ if( !old_style && !duration )
+ old_style = only_old_style( sk_list );
+
+ if(PGP2 && !only_old_style(sk_list))
+ {
+ log_info(_("you can only clearsign with PGP 2.x style keys "
+ "while in --pgp2 mode\n"));
+ compliance_failure();
+ }
+
+ /* prepare iobufs */
+ inp = iobuf_open(fname);
+ if (inp && is_secured_file (iobuf_get_fd (inp)))
+ {
+ iobuf_close (inp);
+ inp = NULL;
+ errno = EPERM;
+ }
+ if( !inp ) {
+ log_error(_("can't open `%s': %s\n"), fname? fname: "[stdin]",
+ strerror(errno) );
+ rc = G10ERR_OPEN_FILE;
+ goto leave;
+ }
+ handle_progress (&pfx, inp, fname);
+
+ if( outfile ) {
+ if (is_secured_filename (outfile) ) {
+ outfile = NULL;
+ errno = EPERM;
+ }
+ else
+ out = iobuf_create( outfile );
+ if( !out )
+ {
+ log_error(_("can't create `%s': %s\n"), outfile, strerror(errno) );
+ rc = G10ERR_CREATE_FILE;
+ goto leave;
+ }
+ else if( opt.verbose )
+ log_info(_("writing to `%s'\n"), outfile );
+ }
+ else if( (rc = open_outfile( fname, 1, &out )) )
+ goto leave;
+
+ iobuf_writestr(out, "-----BEGIN PGP SIGNED MESSAGE-----" LF );
+
+ for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
+ PKT_secret_key *sk = sk_rover->sk;
+ if( hash_for(sk) == DIGEST_ALGO_MD5 )
+ only_md5 = 1;
+ else {
+ only_md5 = 0;
+ break;
+ }
+ }
+
+ if( !(old_style && only_md5) ) {
+ const char *s;
+ int any = 0;
+ byte hashs_seen[256];
+
+ memset( hashs_seen, 0, sizeof hashs_seen );
+ iobuf_writestr(out, "Hash: " );
+ for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
+ PKT_secret_key *sk = sk_rover->sk;
+ int i = hash_for(sk);
+
+ if( !hashs_seen[ i & 0xff ] ) {
+ s = digest_algo_to_string( i );
+ if( s ) {
+ hashs_seen[ i & 0xff ] = 1;
+ if( any )
+ iobuf_put(out, ',' );
+ iobuf_writestr(out, s );
+ any = 1;
+ }
+ }
+ }
+ assert(any);
+ iobuf_writestr(out, LF );
+ }
+
+ if( opt.not_dash_escaped )
+ iobuf_writestr( out,
+ "NotDashEscaped: You need GnuPG to verify this message" LF );
+ iobuf_writestr(out, LF );
+
+ textmd = md_open(0, 0);
+ for( sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next ) {
+ PKT_secret_key *sk = sk_rover->sk;
+ md_enable(textmd, hash_for(sk));
+ }
+ if ( DBG_HASHING )
+ md_start_debug( textmd, "clearsign" );
+ copy_clearsig_text( out, inp, textmd, !opt.not_dash_escaped,
+ opt.escape_from, (old_style && only_md5) );
+ /* fixme: check for read errors */
+
+ /* now write the armor */
+ afx.what = 2;
+ iobuf_push_filter( out, armor_filter, &afx );
+
+ /* write the signatures */
+ rc=write_signature_packets (sk_list, out, textmd, 0x01, 0, duration, 'C');
+ if( rc )
+ goto leave;
+
+ leave:
+ if( rc )
+ iobuf_cancel(out);
+ else
+ iobuf_close(out);
+ iobuf_close(inp);
+ md_close( textmd );
+ release_sk_list( sk_list );
+ return rc;
+}
+
+/*
+ * Sign and conventionally encrypt the given file.
+ * FIXME: Far too much code is duplicated - revamp the whole file.
+ */
+int
+sign_symencrypt_file (const char *fname, STRLIST locusr)
+{
+ armor_filter_context_t afx;
+ progress_filter_context_t pfx;
+ compress_filter_context_t zfx;
+ md_filter_context_t mfx;
+ text_filter_context_t tfx;
+ cipher_filter_context_t cfx;
+ IOBUF inp = NULL, out = NULL;
+ PACKET pkt;
+ STRING2KEY *s2k = NULL;
+ int rc = 0;
+ SK_LIST sk_list = NULL;
+ SK_LIST sk_rover = NULL;
+ int algo;
+ u32 duration=0;
+
+ memset( &afx, 0, sizeof afx);
+ memset( &zfx, 0, sizeof zfx);
+ memset( &mfx, 0, sizeof mfx);
+ memset( &tfx, 0, sizeof tfx);
+ memset( &cfx, 0, sizeof cfx);
+ init_packet( &pkt );
+
+ if(!opt.force_v3_sigs && !RFC1991)
+ {
+ if(opt.ask_sig_expire && !opt.batch)
+ duration=ask_expire_interval(1,opt.def_sig_expire);
+ else
+ duration=parse_expire_string(opt.def_sig_expire);
+ }
+
+ rc = build_sk_list (locusr, &sk_list, 1, PUBKEY_USAGE_SIG);
+ if (rc)
+ goto leave;
+
+ /* prepare iobufs */
+ inp = iobuf_open(fname);
+ if (inp && is_secured_file (iobuf_get_fd (inp)))
+ {
+ iobuf_close (inp);
+ inp = NULL;
+ errno = EPERM;
+ }
+ if( !inp ) {
+ log_error(_("can't open `%s': %s\n"),
+ fname? fname: "[stdin]", strerror(errno) );
+ rc = G10ERR_OPEN_FILE;
+ goto leave;
+ }
+ handle_progress (&pfx, inp, fname);
+
+ /* prepare key */
+ s2k = xmalloc_clear( sizeof *s2k );
+ s2k->mode = RFC1991? 0:opt.s2k_mode;
+ s2k->hash_algo = S2K_DIGEST_ALGO;
+
+ algo = default_cipher_algo();
+ if (!opt.quiet || !opt.batch)
+ log_info (_("%s encryption will be used\n"),
+ cipher_algo_to_string(algo) );
+ cfx.dek = passphrase_to_dek( NULL, 0, algo, s2k, 2, NULL, NULL);
+
+ if (!cfx.dek || !cfx.dek->keylen) {
+ rc = G10ERR_PASSPHRASE;
+ log_error(_("error creating passphrase: %s\n"), g10_errstr(rc) );
+ goto leave;
+ }
+
+ /* We have no way to tell if the recipient can handle messages
+ with an MDC, so this defaults to no. Perhaps in a few years,
+ this can be defaulted to yes. Note that like regular
+ encrypting, --force-mdc overrides --disable-mdc. */
+ if(opt.force_mdc)
+ cfx.dek->use_mdc=1;
+
+ /* now create the outfile */
+ rc = open_outfile (fname, opt.armor? 1:0, &out);
+ if (rc)
+ goto leave;
+
+ /* prepare to calculate the MD over the input */
+ if (opt.textmode)
+ iobuf_push_filter (inp, text_filter, &tfx);
+ mfx.md = md_open(0, 0);
+ if ( DBG_HASHING )
+ md_start_debug (mfx.md, "symc-sign");
+
+ for (sk_rover = sk_list; sk_rover; sk_rover = sk_rover->next) {
+ PKT_secret_key *sk = sk_rover->sk;
+ md_enable (mfx.md, hash_for (sk));
+ }
+
+ iobuf_push_filter (inp, md_filter, &mfx);
+
+ /* Push armor output filter */
+ if (opt.armor)
+ iobuf_push_filter (out, armor_filter, &afx);
+
+ /* Write the symmetric key packet */
+ /*(current filters: armor)*/
+ if (!RFC1991) {
+ PKT_symkey_enc *enc = xmalloc_clear( sizeof *enc );
+ enc->version = 4;
+ enc->cipher_algo = cfx.dek->algo;
+ enc->s2k = *s2k;
+ pkt.pkttype = PKT_SYMKEY_ENC;
+ pkt.pkt.symkey_enc = enc;
+ if( (rc = build_packet( out, &pkt )) )
+ log_error("build symkey packet failed: %s\n", g10_errstr(rc) );
+ xfree(enc);
+ }
+
+ /* Push the encryption filter */
+ iobuf_push_filter( out, cipher_filter, &cfx );
+
+ /* Push the compress filter */
+ if (default_compress_algo())
+ push_compress_filter(out,&zfx,default_compress_algo());
+
+ /* Write the one-pass signature packets */
+ /*(current filters: zip - encrypt - armor)*/
+ if (!RFC1991) {
+ rc = write_onepass_sig_packets (sk_list, out,
+ opt.textmode? 0x01:0x00);
+ if (rc)
+ goto leave;
+ }
+
+ write_status (STATUS_BEGIN_SIGNING);
+
+ /* Pipe data through all filters; i.e. write the signed stuff */
+ /*(current filters: zip - encrypt - armor)*/
+ rc = write_plaintext_packet (out, inp, fname, opt.textmode ? 't':'b');
+ if (rc)
+ goto leave;
+
+ /* Write the signatures */
+ /*(current filters: zip - encrypt - armor)*/
+ rc = write_signature_packets (sk_list, out, mfx.md,
+ opt.textmode? 0x01 : 0x00,
+ 0, duration, 'S');
+ if( rc )
+ goto leave;
+
+
+ leave:
+ if( rc )
+ iobuf_cancel(out);
+ else {
+ iobuf_close(out);
+ write_status( STATUS_END_ENCRYPTION );
+ }
+ iobuf_close(inp);
+ release_sk_list( sk_list );
+ md_close( mfx.md );
+ xfree(cfx.dek);
+ xfree(s2k);
+ return rc;
+}
+
+
+/****************
+ * Create a signature packet for the given public key certificate and
+ * the user id and return it in ret_sig. User signature class SIGCLASS
+ * user-id is not used (and may be NULL if sigclass is 0x20) If
+ * DIGEST_ALGO is 0 the function selects an appropriate one.
+ * SIGVERSION gives the minimal required signature packet version;
+ * this is needed so that special properties like local sign are not
+ * applied (actually: dropped) when a v3 key is used. TIMESTAMP is
+ * the timestamp to use for the signature. 0 means "now" */
+int
+make_keysig_packet( PKT_signature **ret_sig, PKT_public_key *pk,
+ PKT_user_id *uid, PKT_public_key *subpk,
+ PKT_secret_key *sk,
+ int sigclass, int digest_algo,
+ int sigversion, u32 timestamp, u32 duration,
+ int (*mksubpkt)(PKT_signature *, void *), void *opaque
+ )
+{
+ PKT_signature *sig;
+ int rc=0;
+ MD_HANDLE md;
+
+ assert( (sigclass >= 0x10 && sigclass <= 0x13) || sigclass == 0x1F
+ || sigclass == 0x20 || sigclass == 0x18 || sigclass == 0x19
+ || sigclass == 0x30 || sigclass == 0x28 );
+
+ if (opt.force_v4_certs)
+ sigversion = 4;
+
+ if (sigversion < sk->version)
+ sigversion = sk->version;
+
+ /* If you are making a signature on a v4 key using your v3 key, it
+ doesn't make sense to generate a v3 sig. After all, no v3-only
+ PGP implementation could understand the v4 key in the first
+ place. Note that this implies that a signature on an attribute
+ uid is usually going to be v4 as well, since they are not
+ generally found on v3 keys. */
+ if (sigversion < pk->version)
+ sigversion = pk->version;
+
+ if( !digest_algo )
+ {
+ /* Basically, this means use SHA1 always unless it's a v3 RSA
+ key making a v3 cert (use MD5), or the user specified
+ something (use whatever they said), or it's DSA (use the
+ best match). They still can't pick an inappropriate hash
+ for DSA or the signature will fail. Note that this still
+ allows the caller of make_keysig_packet to override the
+ user setting if it must. */
+
+ if(opt.cert_digest_algo)
+ digest_algo=opt.cert_digest_algo;
+ else if(sk->pubkey_algo==PUBKEY_ALGO_RSA
+ && pk->version<4 && sigversion<4)
+ digest_algo = DIGEST_ALGO_MD5;
+ else if(sk->pubkey_algo==PUBKEY_ALGO_DSA)
+ digest_algo = match_dsa_hash(mpi_get_nbits(sk->skey[1])/8);
+ else
+ digest_algo = DIGEST_ALGO_SHA1;
+ }
+
+ md = md_open( digest_algo, 0 );
+
+ /* hash the public key certificate */
+ hash_public_key( md, pk );
+
+ if( sigclass == 0x18 || sigclass == 0x19 || sigclass == 0x28 )
+ {
+ /* hash the subkey binding/backsig/revocation */
+ hash_public_key( md, subpk );
+ }
+ else if( sigclass != 0x1F && sigclass != 0x20 )
+ {
+ /* hash the user id */
+ hash_uid (md, sigversion, uid);
+ }
+ /* and make the signature packet */
+ sig = xmalloc_clear( sizeof *sig );
+ sig->version = sigversion;
+ sig->flags.exportable=1;
+ sig->flags.revocable=1;
+ keyid_from_sk( sk, sig->keyid );
+ sig->pubkey_algo = sk->pubkey_algo;
+ sig->digest_algo = digest_algo;
+ if(timestamp)
+ sig->timestamp=timestamp;
+ else
+ sig->timestamp=make_timestamp();
+ if(duration)
+ sig->expiredate=sig->timestamp+duration;
+ sig->sig_class = sigclass;
+ if( sig->version >= 4 )
+ build_sig_subpkt_from_sig( sig );
+ mk_notation_policy_etc( sig, pk, sk );
+
+ /* Crucial that the call to mksubpkt comes LAST before the calls
+ to finalize the sig as that makes it possible for the mksubpkt
+ function to get a reliable pointer to the subpacket area. */
+ if( sig->version >= 4 && mksubpkt )
+ rc = (*mksubpkt)( sig, opaque );
+
+ if( !rc ) {
+ hash_sigversion_to_magic (md, sig);
+ md_final(md);
+
+ rc = complete_sig( sig, sk, md );
+ }
+
+ md_close( md );
+ if( rc )
+ free_seckey_enc( sig );
+ else
+ *ret_sig = sig;
+ return rc;
+}
+
+
+
+/****************
+ * Create a new signature packet based on an existing one.
+ * Only user ID signatures are supported for now.
+ * TODO: Merge this with make_keysig_packet.
+ */
+int
+update_keysig_packet( PKT_signature **ret_sig,
+ PKT_signature *orig_sig,
+ PKT_public_key *pk,
+ PKT_user_id *uid,
+ PKT_public_key *subpk,
+ PKT_secret_key *sk,
+ int (*mksubpkt)(PKT_signature *, void *),
+ void *opaque )
+{
+ PKT_signature *sig;
+ int rc=0;
+ MD_HANDLE md;
+
+ if ((!orig_sig || !pk || !sk)
+ || (orig_sig->sig_class >= 0x10 && orig_sig->sig_class <= 0x13 && !uid)
+ || (orig_sig->sig_class == 0x18 && !subpk))
+ return G10ERR_GENERAL;
+
+ md = md_open( orig_sig->digest_algo, 0 );
+
+ /* hash the public key certificate and the user id */
+ hash_public_key( md, pk );
+
+ if( orig_sig->sig_class == 0x18 )
+ hash_public_key( md, subpk );
+ else
+ hash_uid (md, orig_sig->version, uid);
+
+ /* create a new signature packet */
+ sig = copy_signature (NULL, orig_sig);
+
+ /* We need to create a new timestamp so that new sig expiration
+ calculations are done correctly... */
+ sig->timestamp=make_timestamp();
+
+ /* ... but we won't make a timestamp earlier than the existing
+ one. */
+ while(sig->timestamp<=orig_sig->timestamp)
+ {
+ sleep(1);
+ sig->timestamp=make_timestamp();
+ }
+
+ /* Note that already expired sigs will remain expired (with a
+ duration of 1) since build-packet.c:build_sig_subpkt_from_sig
+ detects this case. */
+
+ if( sig->version >= 4 )
+ {
+ /* Put the updated timestamp into the sig. Note that this
+ will automagically lower any sig expiration dates to
+ correctly correspond to the differences in the timestamps
+ (i.e. the duration will shrink). */
+ build_sig_subpkt_from_sig( sig );
+
+ if (mksubpkt)
+ rc = (*mksubpkt)(sig, opaque);
+ }
+
+ if (!rc) {
+ hash_sigversion_to_magic (md, sig);
+ md_final(md);
+
+ rc = complete_sig( sig, sk, md );
+ }
+
+ md_close (md);
+ if( rc )
+ free_seckey_enc (sig);
+ else
+ *ret_sig = sig;
+ return rc;
+}
diff --git a/g10/signal.c b/g10/signal.c
new file mode 100644
index 0000000..2e40af1
--- /dev/null
+++ b/g10/signal.c
@@ -0,0 +1,238 @@
+/* signal.c - signal handling
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ * 2005 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <unistd.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#ifdef HAVE_LIBREADLINE
+#include <readline/readline.h>
+#include <readline/history.h>
+#endif
+
+#include "options.h"
+#include "errors.h"
+#include "memory.h"
+#include "util.h"
+#include "main.h"
+#include "ttyio.h"
+
+#ifdef HAVE_DOSISH_SYSTEM
+void init_signals(void) {}
+void pause_on_sigusr(int which) {}
+#else
+static volatile int caught_fatal_sig = 0;
+static volatile int caught_sigusr1 = 0;
+
+static void
+init_one_signal (int sig, RETSIGTYPE (*handler)(int), int check_ign )
+{
+#if defined(HAVE_SIGACTION) && defined(HAVE_STRUCT_SIGACTION)
+ struct sigaction oact, nact;
+
+ if (check_ign) {
+ /* we don't want to change an IGN handler */
+ sigaction (sig, NULL, &oact );
+ if (oact.sa_handler == SIG_IGN )
+ return;
+ }
+
+ nact.sa_handler = handler;
+ sigemptyset (&nact.sa_mask);
+ nact.sa_flags = 0;
+ sigaction ( sig, &nact, NULL);
+#else
+ RETSIGTYPE (*ohandler)(int);
+
+ ohandler = signal (sig, handler);
+ if (check_ign && ohandler == SIG_IGN) {
+ /* Change it back if it was already set to IGN */
+ signal (sig, SIG_IGN);
+ }
+#endif
+}
+
+static RETSIGTYPE
+got_fatal_signal( int sig )
+{
+ const char *s;
+
+ if( caught_fatal_sig )
+ raise( sig );
+ caught_fatal_sig = 1;
+
+ secmem_term();
+
+#ifdef HAVE_LIBREADLINE
+ rl_free_line_state ();
+ rl_cleanup_after_signal ();
+#endif
+
+ /* Better don't translate these messages. */
+ write(2, "\n", 1 );
+ s = log_get_name(); if( s ) write(2, s, strlen(s) );
+ write(2, ": ", 2 );
+
+#if HAVE_DECL_SYS_SIGLIST && defined(NSIG)
+ s = (sig >= 0 && sig < NSIG) ? sys_siglist[sig] : "?";
+ write (2, s, strlen(s) );
+#else
+ write (2, "signal ", 7 );
+ if (sig < 0 || sig >=100)
+ write (2, "?", 1);
+ else {
+ if (sig >= 10)
+ write (2, "0123456789"+(sig/10), 1 );
+ write (2, "0123456789"+(sig%10), 1 );
+ }
+#endif
+ write(2, " caught ... exiting\n", 20 );
+
+ /* Reset action to default action and raise signal again. */
+ init_one_signal (sig, SIG_DFL, 0);
+ remove_lockfiles ();
+#ifdef __riscos__
+ riscos_close_fds ();
+#endif /* __riscos__ */
+ raise( sig );
+}
+
+
+static RETSIGTYPE
+got_usr_signal( int sig )
+{
+ caught_sigusr1 = 1;
+}
+
+
+void
+init_signals()
+{
+ init_one_signal (SIGINT, got_fatal_signal, 1 );
+ init_one_signal (SIGHUP, got_fatal_signal, 1 );
+ init_one_signal (SIGTERM, got_fatal_signal, 1 );
+ init_one_signal (SIGQUIT, got_fatal_signal, 1 );
+ init_one_signal (SIGSEGV, got_fatal_signal, 1 );
+ init_one_signal (SIGUSR1, got_usr_signal, 0 );
+ init_one_signal (SIGPIPE, SIG_IGN, 0 );
+}
+
+
+void
+pause_on_sigusr( int which )
+{
+#if defined(HAVE_SIGPROCMASK) && defined(HAVE_SIGSET_T)
+ sigset_t mask, oldmask;
+
+ assert( which == 1 );
+ sigemptyset( &mask );
+ sigaddset( &mask, SIGUSR1 );
+
+ sigprocmask( SIG_BLOCK, &mask, &oldmask );
+ while( !caught_sigusr1 )
+ sigsuspend( &oldmask );
+ caught_sigusr1 = 0;
+ sigprocmask( SIG_UNBLOCK, &mask, NULL );
+#else
+ assert (which == 1);
+ sighold (SIGUSR1);
+ while (!caught_sigusr1)
+ sigpause(SIGUSR1);
+ caught_sigusr1 = 0;
+ sigrelse(SIGUSR1);
+#endif /*! HAVE_SIGPROCMASK && HAVE_SIGSET_T */
+}
+
+/* Disabled - see comment in tdbio.c:tdbio_begin_transaction() */
+#if 0
+static void
+do_block( int block )
+{
+ static int is_blocked;
+#if defined(HAVE_SIGPROCMASK) && defined(HAVE_SIGSET_T)
+ static sigset_t oldmask;
+
+ if( block ) {
+ sigset_t newmask;
+
+ if( is_blocked )
+ log_bug("signals are already blocked\n");
+ sigfillset( &newmask );
+ sigprocmask( SIG_BLOCK, &newmask, &oldmask );
+ is_blocked = 1;
+ }
+ else {
+ if( !is_blocked )
+ log_bug("signals are not blocked\n");
+ sigprocmask( SIG_SETMASK, &oldmask, NULL );
+ is_blocked = 0;
+ }
+#else /*! HAVE_SIGPROCMASK && HAVE_SIGSET_T */
+
+#if defined(NSIG)
+#define SIGSMAX (NSIG)
+#elif defined(MAXSIG)
+#define SIGSMAX (MAXSIG+1)
+#else
+#error "define SIGSMAX to the number of signals on your platform plus one"
+#endif
+
+ static void (*disposition[SIGSMAX])(int);
+ int sig;
+
+ if( block ) {
+ if( is_blocked )
+ log_bug("signals are already blocked\n");
+ for (sig=1; sig < SIGSMAX; sig++) {
+ disposition[sig] = sigset (sig, SIG_HOLD);
+ }
+ is_blocked = 1;
+ }
+ else {
+ if( !is_blocked )
+ log_bug("signals are not blocked\n");
+ for (sig=1; sig < SIGSMAX; sig++) {
+ sigset (sig, disposition[sig]);
+ }
+ is_blocked = 0;
+ }
+#endif /*! HAVE_SIGPROCMASK && HAVE_SIGSET_T */
+}
+
+void
+block_all_signals()
+{
+ do_block(1);
+}
+
+void
+unblock_all_signals()
+{
+ do_block(0);
+}
+#endif
+
+#endif /* !HAVE_DOSISH_SYSTEM */
diff --git a/g10/skclist.c b/g10/skclist.c
new file mode 100644
index 0000000..5e55e24
--- /dev/null
+++ b/g10/skclist.c
@@ -0,0 +1,223 @@
+/* skclist.c
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "options.h"
+#include "packet.h"
+#include "errors.h"
+#include "keydb.h"
+#include "memory.h"
+#include "util.h"
+#include "i18n.h"
+#include "cipher.h"
+
+
+void
+release_sk_list( SK_LIST sk_list )
+{
+ SK_LIST sk_rover;
+
+ for( ; sk_list; sk_list = sk_rover ) {
+ sk_rover = sk_list->next;
+ free_secret_key( sk_list->sk );
+ xfree( sk_list );
+ }
+}
+
+
+/* Check that we are only using keys which don't have
+ * the string "(insecure!)" or "not secure" or "do not use"
+ * in one of the user ids
+ */
+static int
+is_insecure( PKT_secret_key *sk )
+{
+ u32 keyid[2];
+ KBNODE node = NULL, u;
+ int insecure = 0;
+
+ keyid_from_sk( sk, keyid );
+ node = get_pubkeyblock( keyid );
+ for ( u = node; u; u = u->next ) {
+ if ( u->pkt->pkttype == PKT_USER_ID ) {
+ PKT_user_id *id = u->pkt->pkt.user_id;
+ if ( id->attrib_data )
+ continue; /* skip attribute packets */
+ if ( strstr( id->name, "(insecure!)" )
+ || strstr( id->name, "not secure" )
+ || strstr( id->name, "do not use" )
+ || strstr( id->name, "(INSECURE!)" ) ) {
+ insecure = 1;
+ break;
+ }
+ }
+ }
+ release_kbnode( node );
+
+ return insecure;
+}
+
+static int
+key_present_in_sk_list(SK_LIST sk_list, PKT_secret_key *sk)
+{
+ for (; sk_list; sk_list = sk_list->next) {
+ if ( !cmp_secret_keys(sk_list->sk, sk) )
+ return 0;
+ }
+ return -1;
+}
+
+static int
+is_duplicated_entry (STRLIST list, STRLIST item)
+{
+ for(; list && list != item; list = list->next) {
+ if ( !strcmp (list->d, item->d) )
+ return 1;
+ }
+ return 0;
+}
+
+
+int
+build_sk_list( STRLIST locusr, SK_LIST *ret_sk_list,
+ int unlock, unsigned int use )
+{
+ SK_LIST sk_list = NULL;
+ int rc;
+
+ if( !locusr )
+ { /* use the default one */
+ PKT_secret_key *sk;
+
+ sk = xmalloc_clear( sizeof *sk );
+ sk->req_usage = use;
+ if( (rc = get_seckey_byname( sk, NULL, unlock )) ) {
+ free_secret_key( sk ); sk = NULL;
+ log_error("no default secret key: %s\n", g10_errstr(rc) );
+ }
+ else if( !(rc=check_pubkey_algo2(sk->pubkey_algo, use)) )
+ {
+ SK_LIST r;
+
+ if( random_is_faked() && !is_insecure( sk ) )
+ {
+ log_info(_("key is not flagged as insecure - "
+ "can't use it with the faked RNG!\n"));
+ free_secret_key( sk ); sk = NULL;
+ }
+ else
+ {
+ r = xmalloc( sizeof *r );
+ r->sk = sk; sk = NULL;
+ r->next = sk_list;
+ r->mark = 0;
+ sk_list = r;
+ }
+ }
+ else
+ {
+ free_secret_key( sk ); sk = NULL;
+ log_error("invalid default secret key: %s\n", g10_errstr(rc) );
+ }
+ }
+ else {
+ STRLIST locusr_orig = locusr;
+ for(; locusr; locusr = locusr->next ) {
+ PKT_secret_key *sk;
+
+ rc = 0;
+ /* Do an early check agains duplicated entries. However this
+ * won't catch all duplicates because the user IDs may be
+ * specified in different ways.
+ */
+ if ( is_duplicated_entry ( locusr_orig, locusr ) )
+ {
+ log_error(_("skipped \"%s\": duplicated\n"), locusr->d );
+ continue;
+ }
+ sk = xmalloc_clear( sizeof *sk );
+ sk->req_usage = use;
+ if( (rc = get_seckey_byname( sk, locusr->d, 0 )) )
+ {
+ free_secret_key( sk ); sk = NULL;
+ log_error(_("skipped \"%s\": %s\n"),
+ locusr->d, g10_errstr(rc) );
+ }
+ else if ( key_present_in_sk_list(sk_list, sk) == 0) {
+ free_secret_key(sk); sk = NULL;
+ log_info(_("skipped: secret key already present\n"));
+ }
+ else if ( unlock && (rc = check_secret_key( sk, 0 )) )
+ {
+ free_secret_key( sk ); sk = NULL;
+ log_error(_("skipped \"%s\": %s\n"),
+ locusr->d, g10_errstr(rc) );
+ }
+ else if( !(rc=check_pubkey_algo2(sk->pubkey_algo, use)) ) {
+ SK_LIST r;
+
+ if( sk->version == 4 && (use & PUBKEY_USAGE_SIG)
+ && sk->pubkey_algo == PUBKEY_ALGO_ELGAMAL_E )
+ {
+ log_info(_("skipped \"%s\": %s\n"),locusr->d,
+ _("this is a PGP generated Elgamal key which"
+ " is not secure for signatures!"));
+ free_secret_key( sk ); sk = NULL;
+ }
+ else if( random_is_faked() && !is_insecure( sk ) ) {
+ log_info(_("key is not flagged as insecure - "
+ "can't use it with the faked RNG!\n"));
+ free_secret_key( sk ); sk = NULL;
+ }
+ else {
+ r = xmalloc( sizeof *r );
+ r->sk = sk; sk = NULL;
+ r->next = sk_list;
+ r->mark = 0;
+ sk_list = r;
+ }
+ }
+ else {
+ free_secret_key( sk ); sk = NULL;
+ log_error("skipped \"%s\": %s\n", locusr->d, g10_errstr(rc) );
+ }
+ }
+ }
+
+
+ if( !rc && !sk_list ) {
+ log_error("no valid signators\n");
+ rc = G10ERR_NO_USER_ID;
+ }
+
+ if( rc )
+ release_sk_list( sk_list );
+ else
+ *ret_sk_list = sk_list;
+ return rc;
+}
+
diff --git a/g10/status.c b/g10/status.c
new file mode 100644
index 0000000..2044820
--- /dev/null
+++ b/g10/status.c
@@ -0,0 +1,790 @@
+/* status.c
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003,
+ * 2004, 2005 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <unistd.h>
+#include <signal.h>
+#ifdef USE_SHM_COPROCESSING
+#ifdef USE_CAPABILITIES
+#include <sys/capability.h>
+#endif
+#ifdef HAVE_SYS_IPC_H
+#include <sys/types.h>
+#include <sys/ipc.h>
+#endif
+#ifdef HAVE_SYS_SHM_H
+#include <sys/shm.h>
+#endif
+#if defined(HAVE_MLOCK)
+#include <sys/mman.h>
+#endif
+#endif
+#include "util.h"
+#include "status.h"
+#include "ttyio.h"
+#include "options.h"
+#include "main.h"
+#include "i18n.h"
+#include "cipher.h" /* for progress functions */
+
+#define CONTROL_D ('D' - 'A' + 1)
+
+
+
+static FILE *statusfp;
+
+#ifdef USE_SHM_COPROCESSING
+ static int shm_id = -1;
+ static volatile char *shm_area;
+ static size_t shm_size;
+ static int shm_is_locked;
+#endif /*USE_SHM_COPROCESSING*/
+
+
+static void
+progress_cb ( void *ctx, int c )
+{
+ char buf[50];
+
+ if ( c == '\n' )
+ sprintf ( buf, "%.20s X 100 100", (char*)ctx );
+ else
+ sprintf ( buf, "%.20s %c 0 0", (char*)ctx, c );
+ write_status_text ( STATUS_PROGRESS, buf );
+}
+
+static const char *
+get_status_string ( int no )
+{
+ const char *s;
+
+ switch( no )
+ {
+ case STATUS_ENTER : s = "ENTER"; break;
+ case STATUS_LEAVE : s = "LEAVE"; break;
+ case STATUS_ABORT : s = "ABORT"; break;
+ case STATUS_NEWSIG : s = "NEWSIG"; break;
+ case STATUS_GOODSIG: s = "GOODSIG"; break;
+ case STATUS_KEYEXPIRED: s = "KEYEXPIRED"; break;
+ case STATUS_KEYREVOKED: s = "KEYREVOKED"; break;
+ case STATUS_BADSIG : s = "BADSIG"; break;
+ case STATUS_ERRSIG : s = "ERRSIG"; break;
+ case STATUS_BADARMOR : s = "BADARMOR"; break;
+ case STATUS_RSA_OR_IDEA : s= "RSA_OR_IDEA"; break;
+ case STATUS_TRUST_UNDEFINED: s = "TRUST_UNDEFINED"; break;
+ case STATUS_TRUST_NEVER : s = "TRUST_NEVER"; break;
+ case STATUS_TRUST_MARGINAL : s = "TRUST_MARGINAL"; break;
+ case STATUS_TRUST_FULLY : s = "TRUST_FULLY"; break;
+ case STATUS_TRUST_ULTIMATE : s = "TRUST_ULTIMATE"; break;
+ case STATUS_GET_BOOL : s = "GET_BOOL"; break;
+ case STATUS_GET_LINE : s = "GET_LINE"; break;
+ case STATUS_GET_HIDDEN : s = "GET_HIDDEN"; break;
+ case STATUS_GOT_IT : s = "GOT_IT"; break;
+ case STATUS_SHM_INFO : s = "SHM_INFO"; break;
+ case STATUS_SHM_GET : s = "SHM_GET"; break;
+ case STATUS_SHM_GET_BOOL : s = "SHM_GET_BOOL"; break;
+ case STATUS_SHM_GET_HIDDEN : s = "SHM_GET_HIDDEN"; break;
+ case STATUS_NEED_PASSPHRASE: s = "NEED_PASSPHRASE"; break;
+ case STATUS_VALIDSIG : s = "VALIDSIG"; break;
+ case STATUS_SIG_ID : s = "SIG_ID"; break;
+ case STATUS_ENC_TO : s = "ENC_TO"; break;
+ case STATUS_NODATA : s = "NODATA"; break;
+ case STATUS_BAD_PASSPHRASE : s = "BAD_PASSPHRASE"; break;
+ case STATUS_NO_PUBKEY : s = "NO_PUBKEY"; break;
+ case STATUS_NO_SECKEY : s = "NO_SECKEY"; break;
+ case STATUS_NEED_PASSPHRASE_SYM: s = "NEED_PASSPHRASE_SYM"; break;
+ case STATUS_NEED_PASSPHRASE_PIN: s = "NEED_PASSPHRASE_PIN"; break;
+ case STATUS_DECRYPTION_FAILED: s = "DECRYPTION_FAILED"; break;
+ case STATUS_DECRYPTION_OKAY: s = "DECRYPTION_OKAY"; break;
+ case STATUS_MISSING_PASSPHRASE: s = "MISSING_PASSPHRASE"; break;
+ case STATUS_GOOD_PASSPHRASE : s = "GOOD_PASSPHRASE"; break;
+ case STATUS_GOODMDC : s = "GOODMDC"; break;
+ case STATUS_BADMDC : s = "BADMDC"; break;
+ case STATUS_ERRMDC : s = "ERRMDC"; break;
+ case STATUS_IMPORTED : s = "IMPORTED"; break;
+ case STATUS_IMPORT_OK : s = "IMPORT_OK"; break;
+ case STATUS_IMPORT_CHECK : s = "IMPORT_CHECK"; break;
+ case STATUS_IMPORT_RES : s = "IMPORT_RES"; break;
+ case STATUS_FILE_START : s = "FILE_START"; break;
+ case STATUS_FILE_DONE : s = "FILE_DONE"; break;
+ case STATUS_FILE_ERROR : s = "FILE_ERROR"; break;
+ case STATUS_BEGIN_DECRYPTION:s = "BEGIN_DECRYPTION"; break;
+ case STATUS_END_DECRYPTION : s = "END_DECRYPTION"; break;
+ case STATUS_BEGIN_ENCRYPTION:s = "BEGIN_ENCRYPTION"; break;
+ case STATUS_END_ENCRYPTION : s = "END_ENCRYPTION"; break;
+ case STATUS_DELETE_PROBLEM : s = "DELETE_PROBLEM"; break;
+ case STATUS_PROGRESS : s = "PROGRESS"; break;
+ case STATUS_SIG_CREATED : s = "SIG_CREATED"; break;
+ case STATUS_SESSION_KEY : s = "SESSION_KEY"; break;
+ case STATUS_NOTATION_NAME : s = "NOTATION_NAME" ; break;
+ case STATUS_NOTATION_DATA : s = "NOTATION_DATA" ; break;
+ case STATUS_POLICY_URL : s = "POLICY_URL" ; break;
+ case STATUS_BEGIN_STREAM : s = "BEGIN_STREAM"; break;
+ case STATUS_END_STREAM : s = "END_STREAM"; break;
+ case STATUS_KEY_CREATED : s = "KEY_CREATED"; break;
+ case STATUS_KEY_NOT_CREATED: s = "KEY_NOT_CREATED"; break;
+ case STATUS_USERID_HINT : s = "USERID_HINT"; break;
+ case STATUS_UNEXPECTED : s = "UNEXPECTED"; break;
+ case STATUS_INV_RECP : s = "INV_RECP"; break;
+ case STATUS_NO_RECP : s = "NO_RECP"; break;
+ case STATUS_ALREADY_SIGNED : s = "ALREADY_SIGNED"; break;
+ case STATUS_SIGEXPIRED : s = "SIGEXPIRED deprecated-use-keyexpired-instead"; break;
+ case STATUS_EXPSIG : s = "EXPSIG"; break;
+ case STATUS_EXPKEYSIG : s = "EXPKEYSIG"; break;
+ case STATUS_REVKEYSIG : s = "REVKEYSIG"; break;
+ case STATUS_ATTRIBUTE : s = "ATTRIBUTE"; break;
+ case STATUS_CARDCTRL : s = "CARDCTRL"; break;
+ case STATUS_PLAINTEXT : s = "PLAINTEXT"; break;
+ case STATUS_PLAINTEXT_LENGTH:s = "PLAINTEXT_LENGTH"; break;
+ case STATUS_SIG_SUBPACKET : s = "SIG_SUBPACKET"; break;
+ case STATUS_SC_OP_SUCCESS : s = "SC_OP_SUCCESS"; break;
+ case STATUS_SC_OP_FAILURE : s = "SC_OP_FAILURE"; break;
+ case STATUS_BACKUP_KEY_CREATED:s="BACKUP_KEY_CREATED"; break;
+ case STATUS_PKA_TRUST_BAD : s = "PKA_TRUST_BAD"; break;
+ case STATUS_PKA_TRUST_GOOD : s = "PKA_TRUST_GOOD"; break;
+ case STATUS_BEGIN_SIGNING : s = "BEGIN_SIGNING"; break;
+ default: s = "?"; break;
+ }
+ return s;
+}
+
+
+/* Return true if the status message NO may currently be issued. We
+ need this to avoid syncronisation problem while auto retrieving a
+ key. There it may happen that a status NODATA is issued for a non
+ available key and the user may falsely interpret this has a missing
+ signature. */
+static int
+status_currently_allowed (int no)
+{
+ if (!glo_ctrl.in_auto_key_retrieve)
+ return 1; /* Yes. */
+
+ /* We allow some statis anyway, so that import statistics are
+ correct and to avoid problems if the retriebval subsystem will
+ prompt the user. */
+ switch (no)
+ {
+ case STATUS_GET_BOOL:
+ case STATUS_GET_LINE:
+ case STATUS_GET_HIDDEN:
+ case STATUS_GOT_IT:
+ case STATUS_IMPORTED:
+ case STATUS_IMPORT_OK:
+ case STATUS_IMPORT_CHECK:
+ case STATUS_IMPORT_RES:
+ return 1; /* Yes. */
+ default:
+ break;
+ }
+ return 0; /* No. */
+}
+
+
+void
+set_status_fd ( int fd )
+{
+ static int last_fd = -1;
+
+ if ( fd != -1 && last_fd == fd )
+ return;
+
+ if ( statusfp && statusfp != stdout && statusfp != stderr )
+ fclose (statusfp);
+ statusfp = NULL;
+ if ( fd == -1 )
+ return;
+
+ if( fd == 1 )
+ statusfp = stdout;
+ else if( fd == 2 )
+ statusfp = stderr;
+ else
+ statusfp = fdopen( fd, "w" );
+ if( !statusfp ) {
+ log_fatal("can't open fd %d for status output: %s\n",
+ fd, strerror(errno));
+ }
+ last_fd = fd;
+ register_primegen_progress ( progress_cb, "primegen" );
+ register_pk_dsa_progress ( progress_cb, "pk_dsa" );
+ register_pk_elg_progress ( progress_cb, "pk_elg" );
+}
+
+int
+is_status_enabled()
+{
+ return !!statusfp;
+}
+
+void
+write_status ( int no )
+{
+ write_status_text( no, NULL );
+}
+
+void
+write_status_text ( int no, const char *text)
+{
+ if( !statusfp || !status_currently_allowed (no) )
+ return; /* Not enabled or allowed. */
+
+ fputs ( "[GNUPG:] ", statusfp );
+ fputs ( get_status_string (no), statusfp );
+ if( text ) {
+ putc ( ' ', statusfp );
+ for (; *text; text++) {
+ if (*text == '\n')
+ fputs ( "\\n", statusfp );
+ else if (*text == '\r')
+ fputs ( "\\r", statusfp );
+ else
+ putc ( *(const byte *)text, statusfp );
+ }
+ }
+ putc ('\n',statusfp);
+ if ( fflush (statusfp) && opt.exit_on_status_write_error )
+ g10_exit (0);
+}
+
+
+/*
+ * Write a status line with a buffer using %XX escapes. If WRAP is >
+ * 0 wrap the line after this length. If STRING is not NULL it will
+ * be prepended to the buffer, no escaping is done for string.
+ * A wrap of -1 forces spaces not to be encoded as %20.
+ */
+void
+write_status_text_and_buffer ( int no, const char *string,
+ const char *buffer, size_t len, int wrap )
+{
+ const char *s, *text;
+ int esc, first;
+ int lower_limit = ' ';
+ size_t n, count, dowrap;
+
+ if( !statusfp || !status_currently_allowed (no) )
+ return; /* Not enabled or allowed. */
+
+ if (wrap == -1) {
+ lower_limit--;
+ wrap = 0;
+ }
+
+ text = get_status_string (no);
+ count = dowrap = first = 1;
+ do {
+ if (dowrap) {
+ fprintf (statusfp, "[GNUPG:] %s ", text );
+ count = dowrap = 0;
+ if (first && string) {
+ fputs (string, statusfp);
+ count += strlen (string);
+ }
+ first = 0;
+ }
+ for (esc=0, s=buffer, n=len; n && !esc; s++, n-- ) {
+ if ( *s == '%' || *(const byte*)s <= lower_limit
+ || *(const byte*)s == 127 )
+ esc = 1;
+ if ( wrap && ++count > wrap ) {
+ dowrap=1;
+ break;
+ }
+ }
+ if (esc) {
+ s--; n++;
+ }
+ if (s != buffer)
+ fwrite (buffer, s-buffer, 1, statusfp );
+ if ( esc ) {
+ fprintf (statusfp, "%%%02X", *(const byte*)s );
+ s++; n--;
+ }
+ buffer = s;
+ len = n;
+ if ( dowrap && len )
+ putc ( '\n', statusfp );
+ } while ( len );
+
+ putc ('\n',statusfp);
+ if ( fflush (statusfp) && opt.exit_on_status_write_error )
+ g10_exit (0);
+}
+
+void
+write_status_buffer ( int no, const char *buffer, size_t len, int wrap )
+{
+ write_status_text_and_buffer (no, NULL, buffer, len, wrap);
+}
+
+
+#ifdef USE_SHM_COPROCESSING
+
+#ifndef IPC_RMID_DEFERRED_RELEASE
+static void
+remove_shmid( void )
+{
+ if( shm_id != -1 ) {
+ shmctl ( shm_id, IPC_RMID, 0);
+ shm_id = -1;
+ }
+}
+#endif
+
+void
+init_shm_coprocessing ( ulong requested_shm_size, int lock_mem )
+{
+ char buf[100];
+ struct shmid_ds shmds;
+
+#ifndef IPC_RMID_DEFERRED_RELEASE
+ atexit( remove_shmid );
+#endif
+ requested_shm_size = (requested_shm_size + 4095) & ~4095;
+ if ( requested_shm_size > 2 * 4096 )
+ log_fatal("too much shared memory requested; only 8k are allowed\n");
+ shm_size = 4096 /* one page for us */ + requested_shm_size;
+
+ shm_id = shmget( IPC_PRIVATE, shm_size, IPC_CREAT | 0700 );
+ if ( shm_id == -1 )
+ log_fatal("can't get %uk of shared memory: %s\n",
+ (unsigned)shm_size/1024, strerror(errno));
+
+#if !defined(IPC_HAVE_SHM_LOCK) \
+ && defined(HAVE_MLOCK) && !defined(HAVE_BROKEN_MLOCK)
+ /* part of the old code which uses mlock */
+ shm_area = shmat( shm_id, 0, 0 );
+ if ( shm_area == (char*)-1 )
+ log_fatal("can't attach %uk shared memory: %s\n",
+ (unsigned)shm_size/1024, strerror(errno));
+ log_debug("mapped %uk shared memory at %p, id=%d\n",
+ (unsigned)shm_size/1024, shm_area, shm_id );
+ if( lock_mem ) {
+#ifdef USE_CAPABILITIES
+ cap_set_proc( cap_from_text("cap_ipc_lock+ep") );
+#endif
+ /* (need the cast for Solaris with Sun's workshop compilers) */
+ if ( mlock ( (char*)shm_area, shm_size) )
+ log_info("locking shared memory %d failed: %s\n",
+ shm_id, strerror(errno));
+ else
+ shm_is_locked = 1;
+#ifdef USE_CAPABILITIES
+ cap_set_proc( cap_from_text("cap_ipc_lock+p") );
+#endif
+ }
+
+#ifdef IPC_RMID_DEFERRED_RELEASE
+ if( shmctl( shm_id, IPC_RMID, 0) )
+ log_fatal("shmctl IPC_RMDID of %d failed: %s\n",
+ shm_id, strerror(errno));
+#endif
+
+ if( shmctl( shm_id, IPC_STAT, &shmds ) )
+ log_fatal("shmctl IPC_STAT of %d failed: %s\n",
+ shm_id, strerror(errno));
+ if( shmds.shm_perm.uid != getuid() ) {
+ shmds.shm_perm.uid = getuid();
+ if( shmctl( shm_id, IPC_SET, &shmds ) )
+ log_fatal("shmctl IPC_SET of %d failed: %s\n",
+ shm_id, strerror(errno));
+ }
+
+#else /* this is the new code which handles the changes in the SHM
+ * semantics introduced with Linux 2.4. The changes is that we
+ * now change the permissions and then attach to the memory.
+ */
+
+ if( lock_mem ) {
+#ifdef USE_CAPABILITIES
+ cap_set_proc( cap_from_text("cap_ipc_lock+ep") );
+#endif
+#ifdef IPC_HAVE_SHM_LOCK
+ if ( shmctl (shm_id, SHM_LOCK, 0) )
+ log_info("locking shared memory %d failed: %s\n",
+ shm_id, strerror(errno));
+ else
+ shm_is_locked = 1;
+#else
+ log_info("Locking shared memory %d failed: No way to do it\n", shm_id );
+#endif
+#ifdef USE_CAPABILITIES
+ cap_set_proc( cap_from_text("cap_ipc_lock+p") );
+#endif
+ }
+
+ if( shmctl( shm_id, IPC_STAT, &shmds ) )
+ log_fatal("shmctl IPC_STAT of %d failed: %s\n",
+ shm_id, strerror(errno));
+ if( shmds.shm_perm.uid != getuid() ) {
+ shmds.shm_perm.uid = getuid();
+ if( shmctl( shm_id, IPC_SET, &shmds ) )
+ log_fatal("shmctl IPC_SET of %d failed: %s\n",
+ shm_id, strerror(errno));
+ }
+
+ shm_area = shmat( shm_id, 0, 0 );
+ if ( shm_area == (char*)-1 )
+ log_fatal("can't attach %uk shared memory: %s\n",
+ (unsigned)shm_size/1024, strerror(errno));
+ log_debug("mapped %uk shared memory at %p, id=%d\n",
+ (unsigned)shm_size/1024, shm_area, shm_id );
+
+#ifdef IPC_RMID_DEFERRED_RELEASE
+ if( shmctl( shm_id, IPC_RMID, 0) )
+ log_fatal("shmctl IPC_RMDID of %d failed: %s\n",
+ shm_id, strerror(errno));
+#endif
+
+#endif
+ /* write info; Protocol version, id, size, locked size */
+ sprintf( buf, "pv=1 pid=%d shmid=%d sz=%u lz=%u", (int)getpid(),
+ shm_id, (unsigned)shm_size, shm_is_locked? (unsigned)shm_size:0 );
+ write_status_text( STATUS_SHM_INFO, buf );
+}
+
+/****************
+ * Request a string from client
+ * If bool, returns static string on true (do not free) or NULL for false
+ */
+static char *
+do_shm_get( const char *keyword, int hidden, int bool )
+{
+ size_t n;
+ byte *p;
+ char *string;
+
+ if( !shm_area )
+ BUG();
+
+ shm_area[0] = 0; /* msb of length of control block */
+ shm_area[1] = 32; /* and lsb */
+ shm_area[2] = 1; /* indicate that we are waiting on a reply */
+ shm_area[3] = 0; /* clear data available flag */
+
+ write_status_text( bool? STATUS_SHM_GET_BOOL :
+ hidden? STATUS_SHM_GET_HIDDEN : STATUS_SHM_GET, keyword );
+
+ do {
+ pause_on_sigusr(1);
+ if( shm_area[0] || shm_area[1] != 32 || shm_area[2] != 1 )
+ log_fatal("client modified shm control block - abort\n");
+ } while( !shm_area[3] );
+ shm_area[2] = 0; /* reset request flag */
+ p = (byte*)shm_area+32;
+ n = p[0] << 8 | p[1];
+ p += 2;
+ if( n+32+2+1 > 4095 )
+ log_fatal("client returns too large data (%u bytes)\n", (unsigned)n );
+
+ if( bool )
+ return p[0]? "" : NULL;
+
+ string = hidden? xmalloc_secure( n+1 ) : xmalloc( n+1 );
+ memcpy(string, p, n );
+ string[n] = 0; /* make sure it is a string */
+ if( hidden ) /* invalidate the memory */
+ memset( p, 0, n );
+
+ return string;
+}
+
+#endif /* USE_SHM_COPROCESSING */
+
+static int
+myread(int fd, void *buf, size_t count)
+{
+ int rc;
+ do {
+ rc = read( fd, buf, count );
+ } while ( rc == -1 && errno == EINTR );
+ if ( !rc && count ) {
+ static int eof_emmited=0;
+ if ( eof_emmited < 3 ) {
+ *(char*)buf = CONTROL_D;
+ rc = 1;
+ eof_emmited++;
+ }
+ else { /* Ctrl-D not caught - do something reasonable */
+#ifdef HAVE_DOSISH_SYSTEM
+ raise (SIGINT); /* nothing to hangup under DOS */
+#else
+ raise (SIGHUP); /* no more input data */
+#endif
+ }
+ }
+ return rc;
+}
+
+
+
+/****************
+ * Request a string from the client over the command-fd
+ * If bool, returns static string on true (do not free) or NULL for false
+ */
+static char *
+do_get_from_fd( const char *keyword, int hidden, int bool )
+{
+ int i, len;
+ char *string;
+
+ if(statusfp!=stdout)
+ fflush(stdout);
+
+ write_status_text( bool? STATUS_GET_BOOL :
+ hidden? STATUS_GET_HIDDEN : STATUS_GET_LINE, keyword );
+
+ for( string = NULL, i = len = 200; ; i++ ) {
+ if( i >= len-1 ) {
+ char *save = string;
+ len += 100;
+ string = hidden? xmalloc_secure ( len ) : xmalloc ( len );
+ if( save )
+ memcpy(string, save, i );
+ else
+ i=0;
+ }
+ /* Hmmm: why not use our read_line function here */
+ if( myread( opt.command_fd, string+i, 1) != 1 || string[i] == '\n' )
+ break;
+ else if ( string[i] == CONTROL_D ) {
+ /* found ETX - cancel the line and return a sole ETX */
+ string[0] = CONTROL_D;
+ i=1;
+ break;
+ }
+ }
+ string[i] = 0;
+
+ write_status( STATUS_GOT_IT );
+
+ if( bool ) /* Fixme: is this correct??? */
+ return (string[0] == 'Y' || string[0] == 'y') ? "" : NULL;
+
+ return string;
+}
+
+
+
+int
+cpr_enabled()
+{
+ if( opt.command_fd != -1 )
+ return 1;
+#ifdef USE_SHM_COPROCESSING
+ if( opt.shm_coprocess )
+ return 1;
+#endif
+ return 0;
+}
+
+char *
+cpr_get_no_help( const char *keyword, const char *prompt )
+{
+ char *p;
+
+ if( opt.command_fd != -1 )
+ return do_get_from_fd ( keyword, 0, 0 );
+#ifdef USE_SHM_COPROCESSING
+ if( opt.shm_coprocess )
+ return do_shm_get( keyword, 0, 0 );
+#endif
+ for(;;) {
+ p = tty_get( prompt );
+ return p;
+ }
+}
+
+char *
+cpr_get( const char *keyword, const char *prompt )
+{
+ char *p;
+
+ if( opt.command_fd != -1 )
+ return do_get_from_fd ( keyword, 0, 0 );
+#ifdef USE_SHM_COPROCESSING
+ if( opt.shm_coprocess )
+ return do_shm_get( keyword, 0, 0 );
+#endif
+ for(;;) {
+ p = tty_get( prompt );
+ if( *p=='?' && !p[1] && !(keyword && !*keyword)) {
+ xfree(p);
+ display_online_help( keyword );
+ }
+ else
+ return p;
+ }
+}
+
+
+char *
+cpr_get_utf8( const char *keyword, const char *prompt )
+{
+ char *p;
+ p = cpr_get( keyword, prompt );
+ if( p ) {
+ char *utf8 = native_to_utf8( p );
+ xfree( p );
+ p = utf8;
+ }
+ return p;
+}
+
+char *
+cpr_get_hidden( const char *keyword, const char *prompt )
+{
+ char *p;
+
+ if( opt.command_fd != -1 )
+ return do_get_from_fd ( keyword, 1, 0 );
+#ifdef USE_SHM_COPROCESSING
+ if( opt.shm_coprocess )
+ return do_shm_get( keyword, 1, 0 );
+#endif
+ for(;;) {
+ p = tty_get_hidden( prompt );
+ if( *p == '?' && !p[1] ) {
+ xfree(p);
+ display_online_help( keyword );
+ }
+ else
+ return p;
+ }
+}
+
+void
+cpr_kill_prompt(void)
+{
+ if( opt.command_fd != -1 )
+ return;
+#ifdef USE_SHM_COPROCESSING
+ if( opt.shm_coprocess )
+ return;
+#endif
+ tty_kill_prompt();
+ return;
+}
+
+int
+cpr_get_answer_is_yes( const char *keyword, const char *prompt )
+{
+ int yes;
+ char *p;
+
+ if( opt.command_fd != -1 )
+ return !!do_get_from_fd ( keyword, 0, 1 );
+#ifdef USE_SHM_COPROCESSING
+ if( opt.shm_coprocess )
+ return !!do_shm_get( keyword, 0, 1 );
+#endif
+ for(;;) {
+ p = tty_get( prompt );
+ trim_spaces(p); /* it is okay to do this here */
+ if( *p == '?' && !p[1] ) {
+ xfree(p);
+ display_online_help( keyword );
+ }
+ else {
+ tty_kill_prompt();
+ yes = answer_is_yes(p);
+ xfree(p);
+ return yes;
+ }
+ }
+}
+
+int
+cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt )
+{
+ int yes;
+ char *p;
+
+ if( opt.command_fd != -1 )
+ return !!do_get_from_fd ( keyword, 0, 1 );
+#ifdef USE_SHM_COPROCESSING
+ if( opt.shm_coprocess )
+ return !!do_shm_get( keyword, 0, 1 );
+#endif
+ for(;;) {
+ p = tty_get( prompt );
+ trim_spaces(p); /* it is okay to do this here */
+ if( *p == '?' && !p[1] ) {
+ xfree(p);
+ display_online_help( keyword );
+ }
+ else {
+ tty_kill_prompt();
+ yes = answer_is_yes_no_quit(p);
+ xfree(p);
+ return yes;
+ }
+ }
+}
+
+
+int
+cpr_get_answer_okay_cancel (const char *keyword,
+ const char *prompt,
+ int def_answer)
+{
+ int yes;
+ char *answer = NULL;
+ char *p;
+
+ if( opt.command_fd != -1 )
+ answer = do_get_from_fd ( keyword, 0, 0 );
+#ifdef USE_SHM_COPROCESSING
+ else if( opt.shm_coprocess )
+ answer = do_shm_get( keyword, 0, 0 );
+#endif
+
+ if (answer)
+ {
+ yes = answer_is_okay_cancel (answer, def_answer);
+ xfree (answer);
+ return yes;
+ }
+
+ for(;;)
+ {
+ p = tty_get( prompt );
+ trim_spaces(p); /* it is okay to do this here */
+ if (*p == '?' && !p[1])
+ {
+ xfree(p);
+ display_online_help (keyword);
+ }
+ else
+ {
+ tty_kill_prompt();
+ yes = answer_is_okay_cancel (p, def_answer);
+ xfree(p);
+ return yes;
+ }
+ }
+}
diff --git a/g10/status.h b/g10/status.h
new file mode 100644
index 0000000..bc7271d
--- /dev/null
+++ b/g10/status.h
@@ -0,0 +1,150 @@
+/* status.h
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003,
+ * 2004 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+#ifndef G10_STATUS_H
+#define G10_STATUS_H
+
+#define STATUS_ENTER 1
+#define STATUS_LEAVE 2
+#define STATUS_ABORT 3
+
+#define STATUS_GOODSIG 4
+#define STATUS_BADSIG 5
+#define STATUS_ERRSIG 6
+
+#define STATUS_BADARMOR 7
+
+#define STATUS_RSA_OR_IDEA 8
+#define STATUS_KEYEXPIRED 9
+#define STATUS_KEYREVOKED 10
+
+#define STATUS_TRUST_UNDEFINED 11
+#define STATUS_TRUST_NEVER 12
+#define STATUS_TRUST_MARGINAL 13
+#define STATUS_TRUST_FULLY 14
+#define STATUS_TRUST_ULTIMATE 15
+
+#define STATUS_SHM_INFO 16
+#define STATUS_SHM_GET 17
+#define STATUS_SHM_GET_BOOL 18
+#define STATUS_SHM_GET_HIDDEN 19
+
+#define STATUS_NEED_PASSPHRASE 20
+#define STATUS_VALIDSIG 21
+#define STATUS_SIG_ID 22
+#define STATUS_ENC_TO 23
+#define STATUS_NODATA 24
+#define STATUS_BAD_PASSPHRASE 25
+#define STATUS_NO_PUBKEY 26
+#define STATUS_NO_SECKEY 27
+#define STATUS_NEED_PASSPHRASE_SYM 28
+#define STATUS_DECRYPTION_FAILED 29
+#define STATUS_DECRYPTION_OKAY 30
+#define STATUS_MISSING_PASSPHRASE 31
+#define STATUS_GOOD_PASSPHRASE 32
+#define STATUS_GOODMDC 33
+#define STATUS_BADMDC 34
+#define STATUS_ERRMDC 35
+#define STATUS_IMPORTED 36
+#define STATUS_IMPORT_RES 37
+#define STATUS_FILE_START 38
+#define STATUS_FILE_DONE 39
+#define STATUS_FILE_ERROR 40
+
+#define STATUS_BEGIN_DECRYPTION 41
+#define STATUS_END_DECRYPTION 42
+#define STATUS_BEGIN_ENCRYPTION 43
+#define STATUS_END_ENCRYPTION 44
+
+#define STATUS_DELETE_PROBLEM 45
+#define STATUS_GET_BOOL 46
+#define STATUS_GET_LINE 47
+#define STATUS_GET_HIDDEN 48
+#define STATUS_GOT_IT 49
+#define STATUS_PROGRESS 50
+#define STATUS_SIG_CREATED 51
+#define STATUS_SESSION_KEY 52
+#define STATUS_NOTATION_NAME 53
+#define STATUS_NOTATION_DATA 54
+#define STATUS_POLICY_URL 55
+#define STATUS_BEGIN_STREAM 56
+#define STATUS_END_STREAM 57
+#define STATUS_KEY_CREATED 58
+#define STATUS_USERID_HINT 59
+#define STATUS_UNEXPECTED 60
+#define STATUS_INV_RECP 61
+#define STATUS_NO_RECP 62
+#define STATUS_ALREADY_SIGNED 63
+#define STATUS_SIGEXPIRED 64
+#define STATUS_EXPSIG 65
+#define STATUS_EXPKEYSIG 66
+#define STATUS_ATTRIBUTE 67
+#define STATUS_IMPORT_OK 68
+#define STATUS_IMPORT_CHECK 69
+#define STATUS_REVKEYSIG 70
+#define STATUS_CARDCTRL 71
+#define STATUS_NEWSIG 72
+#define STATUS_PLAINTEXT 73
+#define STATUS_PLAINTEXT_LENGTH 74
+#define STATUS_KEY_NOT_CREATED 75
+#define STATUS_NEED_PASSPHRASE_PIN 76
+#define STATUS_SIG_SUBPACKET 77
+
+/* Extra status codes for certain smartcard operations. Primary
+ useful to double check that change PIN worked as expected. */
+#define STATUS_SC_OP_FAILURE 79
+#define STATUS_SC_OP_SUCCESS 80
+
+#define STATUS_BACKUP_KEY_CREATED 81
+
+#define STATUS_PKA_TRUST_BAD 82
+#define STATUS_PKA_TRUST_GOOD 83
+
+#define STATUS_BEGIN_SIGNING 84
+
+
+/*-- status.c --*/
+void set_status_fd ( int fd );
+int is_status_enabled ( void );
+void write_status ( int no );
+void write_status_text ( int no, const char *text );
+void write_status_buffer ( int no,
+ const char *buffer, size_t len, int wrap );
+void write_status_text_and_buffer ( int no, const char *text,
+ const char *buffer, size_t len, int wrap );
+
+#ifdef USE_SHM_COPROCESSING
+ void init_shm_coprocessing ( ulong requested_shm_size, int lock_mem );
+#endif /*USE_SHM_COPROCESSING*/
+
+int cpr_enabled(void);
+char *cpr_get( const char *keyword, const char *prompt );
+char *cpr_get_no_help( const char *keyword, const char *prompt );
+char *cpr_get_utf8( const char *keyword, const char *prompt );
+char *cpr_get_hidden( const char *keyword, const char *prompt );
+void cpr_kill_prompt(void);
+int cpr_get_answer_is_yes( const char *keyword, const char *prompt );
+int cpr_get_answer_yes_no_quit( const char *keyword, const char *prompt );
+int cpr_get_answer_okay_cancel (const char *keyword,
+ const char *prompt,
+ int def_answer);
+
+#endif /*G10_STATUS_H*/
diff --git a/g10/tdbdump.c b/g10/tdbdump.c
new file mode 100644
index 0000000..09ce119
--- /dev/null
+++ b/g10/tdbdump.c
@@ -0,0 +1,233 @@
+/* tdbdump.c
+ * Copyright (C) 1998, 1999, 2000, 2001 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <ctype.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "errors.h"
+#include "iobuf.h"
+#include "keydb.h"
+#include "memory.h"
+#include "util.h"
+#include "trustdb.h"
+#include "options.h"
+#include "packet.h"
+#include "main.h"
+#include "i18n.h"
+#include "tdbio.h"
+
+
+#define HEXTOBIN(x) ( (x) >= '0' && (x) <= '9' ? ((x)-'0') : \
+ (x) >= 'A' && (x) <= 'F' ? ((x)-'A'+10) : ((x)-'a'+10))
+
+
+/****************
+ * Wirte a record but die on error
+ */
+static void
+write_record( TRUSTREC *rec )
+{
+ int rc = tdbio_write_record( rec );
+ if( !rc )
+ return;
+ log_error(_("trust record %lu, type %d: write failed: %s\n"),
+ rec->recnum, rec->rectype, g10_errstr(rc) );
+ tdbio_invalid();
+}
+
+
+/****************
+ * Dump the entire trustdb or only the entries of one key.
+ */
+void
+list_trustdb( const char *username )
+{
+ TRUSTREC rec;
+
+ init_trustdb();
+ /* for now we ignore the user ID */
+ if (1) {
+ ulong recnum;
+ int i;
+
+ printf("TrustDB: %s\n", tdbio_get_dbname() );
+ for(i=9+strlen(tdbio_get_dbname()); i > 0; i-- )
+ putchar('-');
+ putchar('\n');
+ for(recnum=0; !tdbio_read_record( recnum, &rec, 0); recnum++ )
+ tdbio_dump_record( &rec, stdout );
+ }
+}
+
+
+
+
+
+/****************
+ * Print a list of all defined owner trust value.
+ */
+void
+export_ownertrust()
+{
+ TRUSTREC rec;
+ ulong recnum;
+ int i;
+ byte *p;
+
+ init_trustdb();
+ printf(_("# List of assigned trustvalues, created %s\n"
+ "# (Use \"gpg --import-ownertrust\" to restore them)\n"),
+ asctimestamp( make_timestamp() ) );
+ for(recnum=0; !tdbio_read_record( recnum, &rec, 0); recnum++ ) {
+ if( rec.rectype == RECTYPE_TRUST ) {
+ if( !rec.r.trust.ownertrust )
+ continue;
+ p = rec.r.trust.fingerprint;
+ for(i=0; i < 20; i++, p++ )
+ printf("%02X", *p );
+ printf(":%u:\n", (unsigned int)rec.r.trust.ownertrust );
+ }
+ }
+}
+
+
+void
+import_ownertrust( const char *fname )
+{
+ FILE *fp;
+ int is_stdin=0;
+ char line[256];
+ char *p;
+ size_t n, fprlen;
+ unsigned int otrust;
+ byte fpr[20];
+ int any = 0;
+ int rc;
+
+ init_trustdb();
+ if( iobuf_is_pipe_filename (fname) ) {
+ fp = stdin;
+ fname = "[stdin]";
+ is_stdin = 1;
+ }
+ else if( !(fp = fopen( fname, "r" )) ) {
+ log_error ( _("can't open `%s': %s\n"), fname, strerror(errno) );
+ return;
+ }
+
+ if (is_secured_file (fileno (fp)))
+ {
+ fclose (fp);
+ errno = EPERM;
+ log_error (_("can't open `%s': %s\n"), fname, strerror(errno) );
+ return;
+ }
+
+ while( fgets( line, DIM(line)-1, fp ) ) {
+ TRUSTREC rec;
+
+ if( !*line || *line == '#' )
+ continue;
+ n = strlen(line);
+ if( line[n-1] != '\n' ) {
+ log_error (_("error in `%s': %s\n"), fname, _("line too long") );
+ /* ... or last line does not have a LF */
+ break; /* can't continue */
+ }
+ for(p = line; *p && *p != ':' ; p++ )
+ if( !hexdigitp(p) )
+ break;
+ if( *p != ':' ) {
+ log_error (_("error in `%s': %s\n"), fname, _("colon missing") );
+ continue;
+ }
+ fprlen = p - line;
+ if( fprlen != 32 && fprlen != 40 ) {
+ log_error (_("error in `%s': %s\n"),
+ fname, _("invalid fingerprint") );
+ continue;
+ }
+ if( sscanf(p, ":%u:", &otrust ) != 1 ) {
+ log_error (_("error in `%s': %s\n"),
+ fname, _("ownertrust value missing"));
+ continue;
+ }
+ if( !otrust )
+ continue; /* no otrust defined - no need to update or insert */
+ /* convert the ascii fingerprint to binary */
+ for(p=line, fprlen=0; fprlen < 20 && *p != ':'; p += 2 )
+ fpr[fprlen++] = HEXTOBIN(p[0]) * 16 + HEXTOBIN(p[1]);
+ while (fprlen < 20)
+ fpr[fprlen++] = 0;
+
+ rc = tdbio_search_trust_byfpr (fpr, &rec);
+ if( !rc ) { /* found: update */
+ if (rec.r.trust.ownertrust != otrust)
+ {
+ if( rec.r.trust.ownertrust )
+ log_info("changing ownertrust from %u to %u\n",
+ rec.r.trust.ownertrust, otrust );
+ else
+ log_info("setting ownertrust to %u\n", otrust );
+ rec.r.trust.ownertrust = otrust;
+ write_record (&rec );
+ any = 1;
+ }
+ }
+ else if( rc == -1 ) { /* not found: insert */
+ log_info("inserting ownertrust of %u\n", otrust );
+ memset (&rec, 0, sizeof rec);
+ rec.recnum = tdbio_new_recnum ();
+ rec.rectype = RECTYPE_TRUST;
+ memcpy (rec.r.trust.fingerprint, fpr, 20);
+ rec.r.trust.ownertrust = otrust;
+ write_record (&rec );
+ any = 1;
+ }
+ else /* error */
+ log_error (_("error finding trust record in `%s': %s\n"),
+ fname, g10_errstr(rc));
+ }
+ if( ferror(fp) )
+ log_error ( _("read error in `%s': %s\n"), fname, strerror(errno) );
+ if( !is_stdin )
+ fclose(fp);
+
+ if (any)
+ {
+ revalidation_mark ();
+ rc = tdbio_sync ();
+ if (rc)
+ log_error (_("trustdb: sync failed: %s\n"), g10_errstr(rc) );
+ }
+
+}
+
+
diff --git a/g10/tdbio.c b/g10/tdbio.c
new file mode 100644
index 0000000..fbd569d
--- /dev/null
+++ b/g10/tdbio.c
@@ -0,0 +1,1633 @@
+/* tdbio.c
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <unistd.h>
+
+#include "errors.h"
+#include "iobuf.h"
+#include "memory.h"
+#include "util.h"
+#include "options.h"
+#include "main.h"
+#include "i18n.h"
+#include "trustdb.h"
+#include "tdbio.h"
+
+#if defined(HAVE_DOSISH_SYSTEM)
+#define ftruncate chsize
+#endif
+
+#if defined(HAVE_DOSISH_SYSTEM) || defined(__CYGWIN__)
+#define MY_O_BINARY O_BINARY
+#else
+#define MY_O_BINARY 0
+#endif
+
+
+/****************
+ * Yes, this is a very simple implementation. We should really
+ * use a page aligned buffer and read complete pages.
+ * To implement a simple trannsaction system, this is sufficient.
+ */
+typedef struct cache_ctrl_struct *CACHE_CTRL;
+struct cache_ctrl_struct {
+ CACHE_CTRL next;
+ struct {
+ unsigned used:1;
+ unsigned dirty:1;
+ } flags;
+ ulong recno;
+ char data[TRUST_RECORD_LEN];
+};
+
+#define MAX_CACHE_ENTRIES_SOFT 200 /* may be increased while in a */
+#define MAX_CACHE_ENTRIES_HARD 10000 /* transaction to this one */
+static CACHE_CTRL cache_list;
+static int cache_entries;
+static int cache_is_dirty;
+
+/* a type used to pass infomation to cmp_krec_fpr */
+struct cmp_krec_fpr_struct {
+ int pubkey_algo;
+ const char *fpr;
+ int fprlen;
+};
+
+/* a type used to pass infomation to cmp_[s]dir */
+struct cmp_xdir_struct {
+ int pubkey_algo;
+ u32 keyid[2];
+};
+
+
+static char *db_name;
+static DOTLOCK lockhandle;
+static int is_locked;
+static int db_fd = -1;
+static int in_transaction;
+
+static void open_db(void);
+static void migrate_from_v2 (void);
+
+
+
+/*************************************
+ ************* record cache **********
+ *************************************/
+
+/****************
+ * Get the data from therecord cache and return a
+ * pointer into that cache. Caller should copy
+ * the return data. NULL is returned on a cache miss.
+ */
+static const char *
+get_record_from_cache( ulong recno )
+{
+ CACHE_CTRL r;
+
+ for( r = cache_list; r; r = r->next ) {
+ if( r->flags.used && r->recno == recno )
+ return r->data;
+ }
+ return NULL;
+}
+
+
+static int
+write_cache_item( CACHE_CTRL r )
+{
+ int n;
+
+ if( lseek( db_fd, r->recno * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
+ log_error(_("trustdb rec %lu: lseek failed: %s\n"),
+ r->recno, strerror(errno) );
+ return G10ERR_WRITE_FILE;
+ }
+ n = write( db_fd, r->data, TRUST_RECORD_LEN);
+ if( n != TRUST_RECORD_LEN ) {
+ log_error(_("trustdb rec %lu: write failed (n=%d): %s\n"),
+ r->recno, n, strerror(errno) );
+ return G10ERR_WRITE_FILE;
+ }
+ r->flags.dirty = 0;
+ return 0;
+}
+
+/****************
+ * Put data into the cache. This function may flush the
+ * some cache entries if there is not enough space available.
+ */
+int
+put_record_into_cache( ulong recno, const char *data )
+{
+ CACHE_CTRL r, unused;
+ int dirty_count = 0;
+ int clean_count = 0;
+
+ /* see whether we already cached this one */
+ for( unused = NULL, r = cache_list; r; r = r->next ) {
+ if( !r->flags.used ) {
+ if( !unused )
+ unused = r;
+ }
+ else if( r->recno == recno ) {
+ if( !r->flags.dirty ) {
+ /* Hmmm: should we use a a copy and compare? */
+ if( memcmp(r->data, data, TRUST_RECORD_LEN ) ) {
+ r->flags.dirty = 1;
+ cache_is_dirty = 1;
+ }
+ }
+ memcpy( r->data, data, TRUST_RECORD_LEN );
+ return 0;
+ }
+ if( r->flags.used ) {
+ if( r->flags.dirty )
+ dirty_count++;
+ else
+ clean_count++;
+ }
+ }
+ /* not in the cache: add a new entry */
+ if( unused ) { /* reuse this entry */
+ r = unused;
+ r->flags.used = 1;
+ r->recno = recno;
+ memcpy( r->data, data, TRUST_RECORD_LEN );
+ r->flags.dirty = 1;
+ cache_is_dirty = 1;
+ cache_entries++;
+ return 0;
+ }
+ /* see whether we reached the limit */
+ if( cache_entries < MAX_CACHE_ENTRIES_SOFT ) { /* no */
+ r = xmalloc( sizeof *r );
+ r->flags.used = 1;
+ r->recno = recno;
+ memcpy( r->data, data, TRUST_RECORD_LEN );
+ r->flags.dirty = 1;
+ r->next = cache_list;
+ cache_list = r;
+ cache_is_dirty = 1;
+ cache_entries++;
+ return 0;
+ }
+ /* cache is full: discard some clean entries */
+ if( clean_count ) {
+ int n = clean_count / 3; /* discard a third of the clean entries */
+ if( !n )
+ n = 1;
+ for( unused = NULL, r = cache_list; r; r = r->next ) {
+ if( r->flags.used && !r->flags.dirty ) {
+ if( !unused )
+ unused = r;
+ r->flags.used = 0;
+ cache_entries--;
+ if( !--n )
+ break;
+ }
+ }
+ assert( unused );
+ r = unused;
+ r->flags.used = 1;
+ r->recno = recno;
+ memcpy( r->data, data, TRUST_RECORD_LEN );
+ r->flags.dirty = 1;
+ cache_is_dirty = 1;
+ cache_entries++;
+ return 0;
+ }
+ /* no clean entries: have to flush some dirty entries */
+ if( in_transaction ) {
+ /* but we can't do this while in a transaction
+ * we increase the cache size instead */
+ if( cache_entries < MAX_CACHE_ENTRIES_HARD ) { /* no */
+ if( opt.debug && !(cache_entries % 100) )
+ log_debug("increasing tdbio cache size\n");
+ r = xmalloc( sizeof *r );
+ r->flags.used = 1;
+ r->recno = recno;
+ memcpy( r->data, data, TRUST_RECORD_LEN );
+ r->flags.dirty = 1;
+ r->next = cache_list;
+ cache_list = r;
+ cache_is_dirty = 1;
+ cache_entries++;
+ return 0;
+ }
+ log_info(_("trustdb transaction too large\n"));
+ return G10ERR_RESOURCE_LIMIT;
+ }
+ if( dirty_count ) {
+ int n = dirty_count / 5; /* discard some dirty entries */
+ if( !n )
+ n = 1;
+ if( !is_locked ) {
+ if( make_dotlock( lockhandle, -1 ) )
+ log_fatal("can't acquire lock - giving up\n");
+ else
+ is_locked = 1;
+ }
+ for( unused = NULL, r = cache_list; r; r = r->next ) {
+ if( r->flags.used && r->flags.dirty ) {
+ int rc = write_cache_item( r );
+ if( rc )
+ return rc;
+ if( !unused )
+ unused = r;
+ r->flags.used = 0;
+ cache_entries--;
+ if( !--n )
+ break;
+ }
+ }
+ if( !opt.lock_once ) {
+ if( !release_dotlock( lockhandle ) )
+ is_locked = 0;
+ }
+ assert( unused );
+ r = unused;
+ r->flags.used = 1;
+ r->recno = recno;
+ memcpy( r->data, data, TRUST_RECORD_LEN );
+ r->flags.dirty = 1;
+ cache_is_dirty = 1;
+ cache_entries++;
+ return 0;
+ }
+ BUG();
+}
+
+
+int
+tdbio_is_dirty()
+{
+ return cache_is_dirty;
+}
+
+
+/****************
+ * Flush the cache. This cannot be used while in a transaction.
+ */
+int
+tdbio_sync()
+{
+ CACHE_CTRL r;
+ int did_lock = 0;
+
+ if( db_fd == -1 )
+ open_db();
+ if( in_transaction )
+ log_bug("tdbio: syncing while in transaction\n");
+
+ if( !cache_is_dirty )
+ return 0;
+
+ if( !is_locked ) {
+ if( make_dotlock( lockhandle, -1 ) )
+ log_fatal("can't acquire lock - giving up\n");
+ else
+ is_locked = 1;
+ did_lock = 1;
+ }
+ for( r = cache_list; r; r = r->next ) {
+ if( r->flags.used && r->flags.dirty ) {
+ int rc = write_cache_item( r );
+ if( rc )
+ return rc;
+ }
+ }
+ cache_is_dirty = 0;
+ if( did_lock && !opt.lock_once ) {
+ if( !release_dotlock( lockhandle ) )
+ is_locked = 0;
+ }
+
+ return 0;
+}
+
+#if 0
+/* The transaction code is disabled in the 1.2.x branch, as it is not
+ yet used. It will be enabled in 1.3.x. */
+
+/****************
+ * Simple transactions system:
+ * Everything between begin_transaction and end/cancel_transaction
+ * is not immediatly written but at the time of end_transaction.
+ *
+ */
+int
+tdbio_begin_transaction()
+{
+ int rc;
+
+ if( in_transaction )
+ log_bug("tdbio: nested transactions\n");
+ /* flush everything out */
+ rc = tdbio_sync();
+ if( rc )
+ return rc;
+ in_transaction = 1;
+ return 0;
+}
+
+int
+tdbio_end_transaction()
+{
+ int rc;
+
+ if( !in_transaction )
+ log_bug("tdbio: no active transaction\n");
+ if( !is_locked ) {
+ if( make_dotlock( lockhandle, -1 ) )
+ log_fatal("can't acquire lock - giving up\n");
+ else
+ is_locked = 1;
+ }
+ block_all_signals();
+ in_transaction = 0;
+ rc = tdbio_sync();
+ unblock_all_signals();
+ if( !opt.lock_once ) {
+ if( !release_dotlock( lockhandle ) )
+ is_locked = 0;
+ }
+ return rc;
+}
+
+int
+tdbio_cancel_transaction()
+{
+ CACHE_CTRL r;
+
+ if( !in_transaction )
+ log_bug("tdbio: no active transaction\n");
+
+ /* remove all dirty marked entries, so that the original ones
+ * are read back the next time */
+ if( cache_is_dirty ) {
+ for( r = cache_list; r; r = r->next ) {
+ if( r->flags.used && r->flags.dirty ) {
+ r->flags.used = 0;
+ cache_entries--;
+ }
+ }
+ cache_is_dirty = 0;
+ }
+
+ in_transaction = 0;
+ return 0;
+}
+#endif
+
+
+/********************************************************
+ **************** cached I/O functions ******************
+ ********************************************************/
+
+static void
+cleanup(void)
+{
+ if( is_locked ) {
+ if( !release_dotlock(lockhandle) )
+ is_locked = 0;
+ }
+}
+
+/* Caller must sync */
+int
+tdbio_update_version_record (void)
+{
+ TRUSTREC rec;
+ int rc;
+
+ memset( &rec, 0, sizeof rec );
+
+ rc=tdbio_read_record( 0, &rec, RECTYPE_VER);
+ if(rc==0)
+ {
+ rec.r.ver.created = make_timestamp();
+ rec.r.ver.marginals = opt.marginals_needed;
+ rec.r.ver.completes = opt.completes_needed;
+ rec.r.ver.cert_depth = opt.max_cert_depth;
+ rec.r.ver.trust_model = opt.trust_model;
+ rc=tdbio_write_record(&rec);
+ }
+
+ return rc;
+}
+
+static int
+create_version_record (void)
+{
+ TRUSTREC rec;
+ int rc;
+
+ memset( &rec, 0, sizeof rec );
+ rec.r.ver.version = 3;
+ rec.r.ver.created = make_timestamp();
+ rec.r.ver.marginals = opt.marginals_needed;
+ rec.r.ver.completes = opt.completes_needed;
+ rec.r.ver.cert_depth = opt.max_cert_depth;
+ if(opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC)
+ rec.r.ver.trust_model = opt.trust_model;
+ else
+ rec.r.ver.trust_model = TM_PGP;
+ rec.rectype = RECTYPE_VER;
+ rec.recnum = 0;
+ rc = tdbio_write_record( &rec );
+ if( !rc )
+ tdbio_sync();
+ return rc;
+}
+
+
+
+int
+tdbio_set_dbname( const char *new_dbname, int create )
+{
+ char *fname;
+ static int initialized = 0;
+
+ if( !initialized ) {
+ atexit( cleanup );
+ initialized = 1;
+ }
+
+ if(new_dbname==NULL)
+ fname=make_filename(opt.homedir,"trustdb" EXTSEP_S "gpg", NULL);
+ else if (*new_dbname != DIRSEP_C )
+ {
+ if (strchr(new_dbname, DIRSEP_C) )
+ fname = make_filename (new_dbname, NULL);
+ else
+ fname = make_filename (opt.homedir, new_dbname, NULL);
+ }
+ else
+ fname = xstrdup (new_dbname);
+
+ if( access( fname, R_OK ) ) {
+ if( errno != ENOENT ) {
+ log_error( _("can't access `%s': %s\n"), fname, strerror(errno) );
+ xfree(fname);
+ return G10ERR_TRUSTDB;
+ }
+ if( create ) {
+ FILE *fp;
+ TRUSTREC rec;
+ int rc;
+ char *p = strrchr( fname, DIRSEP_C );
+ mode_t oldmask;
+
+ assert(p);
+ *p = 0;
+ if( access( fname, F_OK ) ) {
+ try_make_homedir( fname );
+ log_fatal( _("%s: directory does not exist!\n"), fname );
+ }
+ *p = DIRSEP_C;
+
+ xfree(db_name);
+ db_name = fname;
+#ifdef __riscos__
+ if( !lockhandle )
+ lockhandle = create_dotlock( db_name );
+ if( !lockhandle )
+ log_fatal( _("can't create lock for `%s'\n"), db_name );
+ if( make_dotlock( lockhandle, -1 ) )
+ log_fatal( _("can't lock `%s'\n"), db_name );
+#endif /* __riscos__ */
+ oldmask=umask(077);
+ if (is_secured_filename (fname)) {
+ fp = NULL;
+ errno = EPERM;
+ }
+ else
+ fp =fopen( fname, "wb" );
+ umask(oldmask);
+ if( !fp )
+ log_fatal( _("can't create `%s': %s\n"), fname, strerror(errno) );
+ fclose(fp);
+ db_fd = open( db_name, O_RDWR | MY_O_BINARY );
+ if( db_fd == -1 )
+ log_fatal( _("can't open `%s': %s\n"), db_name, strerror(errno) );
+
+#ifndef __riscos__
+ if( !lockhandle )
+ lockhandle = create_dotlock( db_name );
+ if( !lockhandle )
+ log_fatal( _("can't create lock for `%s'\n"), db_name );
+#endif /* !__riscos__ */
+
+ rc = create_version_record ();
+ if( rc )
+ log_fatal( _("%s: failed to create version record: %s"),
+ fname, g10_errstr(rc));
+ /* and read again to check that we are okay */
+ if( tdbio_read_record( 0, &rec, RECTYPE_VER ) )
+ log_fatal( _("%s: invalid trustdb created\n"), db_name );
+
+ if( !opt.quiet )
+ log_info(_("%s: trustdb created\n"), db_name);
+
+ return 0;
+ }
+ }
+ xfree(db_name);
+ db_name = fname;
+ return 0;
+}
+
+
+const char *
+tdbio_get_dbname()
+{
+ return db_name;
+}
+
+
+
+static void
+open_db()
+{
+ byte buf[10];
+ int n;
+ TRUSTREC rec;
+
+ assert( db_fd == -1 );
+
+ if (!lockhandle )
+ lockhandle = create_dotlock( db_name );
+ if (!lockhandle )
+ log_fatal( _("can't create lock for `%s'\n"), db_name );
+#ifdef __riscos__
+ if (make_dotlock( lockhandle, -1 ) )
+ log_fatal( _("can't lock `%s'\n"), db_name );
+#endif /* __riscos__ */
+ db_fd = open (db_name, O_RDWR | MY_O_BINARY );
+ if (db_fd == -1 && (errno == EACCES
+#ifdef EROFS
+ || errno == EROFS)
+#endif
+ ) {
+ db_fd = open (db_name, O_RDONLY | MY_O_BINARY );
+ if (db_fd != -1)
+ log_info (_("NOTE: trustdb not writable\n"));
+ }
+ if ( db_fd == -1 )
+ log_fatal( _("can't open `%s': %s\n"), db_name, strerror(errno) );
+ register_secured_file (db_name);
+
+ /* check whether we need to do a version migration */
+ do
+ n = read (db_fd, buf, 5);
+ while (n==-1 && errno == EINTR);
+ if (n == 5 && !memcmp (buf, "\x01gpg\x02", 5))
+ {
+ migrate_from_v2 ();
+ }
+
+ /* read the version record */
+ if (tdbio_read_record (0, &rec, RECTYPE_VER ) )
+ log_fatal( _("%s: invalid trustdb\n"), db_name );
+}
+
+
+/****************
+ * Make a hashtable: type 0 = trust hash
+ */
+static void
+create_hashtable( TRUSTREC *vr, int type )
+{
+ TRUSTREC rec;
+ off_t offset;
+ ulong recnum;
+ int i, n, rc;
+
+ offset = lseek( db_fd, 0, SEEK_END );
+ if( offset == -1 )
+ log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) );
+ recnum = offset / TRUST_RECORD_LEN;
+ assert(recnum); /* this is will never be the first record */
+
+ if( !type )
+ vr->r.ver.trusthashtbl = recnum;
+
+ /* Now write the records */
+ n = (256+ITEMS_PER_HTBL_RECORD-1) / ITEMS_PER_HTBL_RECORD;
+ for(i=0; i < n; i++, recnum++ ) {
+ memset( &rec, 0, sizeof rec );
+ rec.rectype = RECTYPE_HTBL;
+ rec.recnum = recnum;
+ rc = tdbio_write_record( &rec );
+ if( rc )
+ log_fatal( _("%s: failed to create hashtable: %s\n"),
+ db_name, g10_errstr(rc));
+ }
+ /* update the version record */
+ rc = tdbio_write_record( vr );
+ if( !rc )
+ rc = tdbio_sync();
+ if( rc )
+ log_fatal( _("%s: error updating version record: %s\n"),
+ db_name, g10_errstr(rc));
+}
+
+
+int
+tdbio_db_matches_options()
+{
+ static int yes_no = -1;
+
+ if( yes_no == -1 )
+ {
+ TRUSTREC vr;
+ int rc;
+
+ rc = tdbio_read_record( 0, &vr, RECTYPE_VER );
+ if( rc )
+ log_fatal( _("%s: error reading version record: %s\n"),
+ db_name, g10_errstr(rc) );
+
+ yes_no = vr.r.ver.marginals == opt.marginals_needed
+ && vr.r.ver.completes == opt.completes_needed
+ && vr.r.ver.cert_depth == opt.max_cert_depth
+ && vr.r.ver.trust_model == opt.trust_model;
+ }
+
+ return yes_no;
+}
+
+byte
+tdbio_read_model(void)
+{
+ TRUSTREC vr;
+ int rc;
+
+ rc = tdbio_read_record( 0, &vr, RECTYPE_VER );
+ if( rc )
+ log_fatal( _("%s: error reading version record: %s\n"),
+ db_name, g10_errstr(rc) );
+ return vr.r.ver.trust_model;
+}
+
+/****************
+ * Return the nextstamp value.
+ */
+ulong
+tdbio_read_nextcheck ()
+{
+ TRUSTREC vr;
+ int rc;
+
+ rc = tdbio_read_record( 0, &vr, RECTYPE_VER );
+ if( rc )
+ log_fatal( _("%s: error reading version record: %s\n"),
+ db_name, g10_errstr(rc) );
+ return vr.r.ver.nextcheck;
+}
+
+/* Return true when the stamp was actually changed. */
+int
+tdbio_write_nextcheck (ulong stamp)
+{
+ TRUSTREC vr;
+ int rc;
+
+ rc = tdbio_read_record( 0, &vr, RECTYPE_VER );
+ if( rc )
+ log_fatal( _("%s: error reading version record: %s\n"),
+ db_name, g10_errstr(rc) );
+
+ if (vr.r.ver.nextcheck == stamp)
+ return 0;
+
+ vr.r.ver.nextcheck = stamp;
+ rc = tdbio_write_record( &vr );
+ if( rc )
+ log_fatal( _("%s: error writing version record: %s\n"),
+ db_name, g10_errstr(rc) );
+ return 1;
+}
+
+
+
+/****************
+ * Return the record number of the trusthash tbl or create a new one.
+ */
+static ulong
+get_trusthashrec(void)
+{
+ static ulong trusthashtbl; /* record number of the trust hashtable */
+
+ if( !trusthashtbl ) {
+ TRUSTREC vr;
+ int rc;
+
+ rc = tdbio_read_record( 0, &vr, RECTYPE_VER );
+ if( rc )
+ log_fatal( _("%s: error reading version record: %s\n"),
+ db_name, g10_errstr(rc) );
+ if( !vr.r.ver.trusthashtbl )
+ create_hashtable( &vr, 0 );
+
+ trusthashtbl = vr.r.ver.trusthashtbl;
+ }
+ return trusthashtbl;
+}
+
+
+
+/****************
+ * Update a hashtable.
+ * table gives the start of the table, key and keylen is the key,
+ * newrecnum is the record number to insert.
+ */
+static int
+upd_hashtable( ulong table, byte *key, int keylen, ulong newrecnum )
+{
+ TRUSTREC lastrec, rec;
+ ulong hashrec, item;
+ int msb;
+ int level=0;
+ int rc, i;
+
+ hashrec = table;
+ next_level:
+ msb = key[level];
+ hashrec += msb / ITEMS_PER_HTBL_RECORD;
+ rc = tdbio_read_record( hashrec, &rec, RECTYPE_HTBL );
+ if( rc ) {
+ log_error("upd_hashtable: read failed: %s\n", g10_errstr(rc) );
+ return rc;
+ }
+
+ item = rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD];
+ if( !item ) { /* insert a new item into the hash table */
+ rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = newrecnum;
+ rc = tdbio_write_record( &rec );
+ if( rc ) {
+ log_error("upd_hashtable: write htbl failed: %s\n",
+ g10_errstr(rc) );
+ return rc;
+ }
+ }
+ else if( item != newrecnum ) { /* must do an update */
+ lastrec = rec;
+ rc = tdbio_read_record( item, &rec, 0 );
+ if( rc ) {
+ log_error( "upd_hashtable: read item failed: %s\n",
+ g10_errstr(rc) );
+ return rc;
+ }
+
+ if( rec.rectype == RECTYPE_HTBL ) {
+ hashrec = item;
+ level++;
+ if( level >= keylen ) {
+ log_error( "hashtable has invalid indirections.\n");
+ return G10ERR_TRUSTDB;
+ }
+ goto next_level;
+ }
+ else if( rec.rectype == RECTYPE_HLST ) { /* extend list */
+ /* see whether the key is already in this list */
+ for(;;) {
+ for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
+ if( rec.r.hlst.rnum[i] == newrecnum ) {
+ return 0; /* okay, already in the list */
+ }
+ }
+ if( rec.r.hlst.next ) {
+ rc = tdbio_read_record( rec.r.hlst.next,
+ &rec, RECTYPE_HLST);
+ if( rc ) {
+ log_error( "upd_hashtable: read hlst failed: %s\n",
+ g10_errstr(rc) );
+ return rc;
+ }
+ }
+ else
+ break; /* not there */
+ }
+ /* find the next free entry and put it in */
+ for(;;) {
+ for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
+ if( !rec.r.hlst.rnum[i] ) {
+ rec.r.hlst.rnum[i] = newrecnum;
+ rc = tdbio_write_record( &rec );
+ if( rc )
+ log_error( "upd_hashtable: write hlst failed: %s\n",
+ g10_errstr(rc) );
+ return rc; /* done */
+ }
+ }
+ if( rec.r.hlst.next ) {
+ rc = tdbio_read_record( rec.r.hlst.next,
+ &rec, RECTYPE_HLST );
+ if( rc ) {
+ log_error( "upd_hashtable: read hlst failed: %s\n",
+ g10_errstr(rc) );
+ return rc;
+ }
+ }
+ else { /* add a new list record */
+ rec.r.hlst.next = item = tdbio_new_recnum();
+ rc = tdbio_write_record( &rec );
+ if( rc ) {
+ log_error( "upd_hashtable: write hlst failed: %s\n",
+ g10_errstr(rc) );
+ return rc;
+ }
+ memset( &rec, 0, sizeof rec );
+ rec.rectype = RECTYPE_HLST;
+ rec.recnum = item;
+ rec.r.hlst.rnum[0] = newrecnum;
+ rc = tdbio_write_record( &rec );
+ if( rc )
+ log_error( "upd_hashtable: write ext hlst failed: %s\n",
+ g10_errstr(rc) );
+ return rc; /* done */
+ }
+ } /* end loop over hlst slots */
+ }
+ else if( rec.rectype == RECTYPE_TRUST ) { /* insert a list record */
+ if( rec.recnum == newrecnum ) {
+ return 0;
+ }
+ item = rec.recnum; /* save number of key record */
+ memset( &rec, 0, sizeof rec );
+ rec.rectype = RECTYPE_HLST;
+ rec.recnum = tdbio_new_recnum();
+ rec.r.hlst.rnum[0] = item; /* old keyrecord */
+ rec.r.hlst.rnum[1] = newrecnum; /* and new one */
+ rc = tdbio_write_record( &rec );
+ if( rc ) {
+ log_error( "upd_hashtable: write new hlst failed: %s\n",
+ g10_errstr(rc) );
+ return rc;
+ }
+ /* update the hashtable record */
+ lastrec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = rec.recnum;
+ rc = tdbio_write_record( &lastrec );
+ if( rc )
+ log_error( "upd_hashtable: update htbl failed: %s\n",
+ g10_errstr(rc) );
+ return rc; /* ready */
+ }
+ else {
+ log_error( "hashtbl %lu: %lu/%d points to an invalid record %lu\n",
+ table, hashrec, (msb % ITEMS_PER_HTBL_RECORD), item);
+ list_trustdb(NULL);
+ return G10ERR_TRUSTDB;
+ }
+ }
+
+ return 0;
+}
+
+
+/****************
+ * Drop an entry from a hashtable
+ * table gives the start of the table, key and keylen is the key,
+ */
+static int
+drop_from_hashtable( ulong table, byte *key, int keylen, ulong recnum )
+{
+ TRUSTREC rec;
+ ulong hashrec, item;
+ int msb;
+ int level=0;
+ int rc, i;
+
+ hashrec = table;
+ next_level:
+ msb = key[level];
+ hashrec += msb / ITEMS_PER_HTBL_RECORD;
+ rc = tdbio_read_record( hashrec, &rec, RECTYPE_HTBL );
+ if( rc ) {
+ log_error("drop_from_hashtable: read failed: %s\n",
+ g10_errstr(rc) );
+ return rc;
+ }
+
+ item = rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD];
+ if( !item ) /* not found - forget about it */
+ return 0;
+
+ if( item == recnum ) { /* tables points direct to the record */
+ rec.r.htbl.item[msb % ITEMS_PER_HTBL_RECORD] = 0;
+ rc = tdbio_write_record( &rec );
+ if( rc )
+ log_error("drop_from_hashtable: write htbl failed: %s\n",
+ g10_errstr(rc) );
+ return rc;
+ }
+
+ rc = tdbio_read_record( item, &rec, 0 );
+ if( rc ) {
+ log_error( "drop_from_hashtable: read item failed: %s\n",
+ g10_errstr(rc) );
+ return rc;
+ }
+
+ if( rec.rectype == RECTYPE_HTBL ) {
+ hashrec = item;
+ level++;
+ if( level >= keylen ) {
+ log_error( "hashtable has invalid indirections.\n");
+ return G10ERR_TRUSTDB;
+ }
+ goto next_level;
+ }
+
+ if( rec.rectype == RECTYPE_HLST ) {
+ for(;;) {
+ for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
+ if( rec.r.hlst.rnum[i] == recnum ) {
+ rec.r.hlst.rnum[i] = 0; /* drop */
+ rc = tdbio_write_record( &rec );
+ if( rc )
+ log_error("drop_from_hashtable: write htbl failed: %s\n",
+ g10_errstr(rc) );
+ return rc;
+ }
+ }
+ if( rec.r.hlst.next ) {
+ rc = tdbio_read_record( rec.r.hlst.next,
+ &rec, RECTYPE_HLST);
+ if( rc ) {
+ log_error( "drop_from_hashtable: read hlst failed: %s\n",
+ g10_errstr(rc) );
+ return rc;
+ }
+ }
+ else
+ return 0; /* key not in table */
+ }
+ }
+
+ log_error( "hashtbl %lu: %lu/%d points to wrong record %lu\n",
+ table, hashrec, (msb % ITEMS_PER_HTBL_RECORD), item);
+ return G10ERR_TRUSTDB;
+}
+
+
+
+/****************
+ * Lookup a record via the hashtable tablewith key/keylen and return the
+ * result in rec. cmp() should return if the record is the desired one.
+ * Returns -1 if not found, 0 if found or another errocode
+ */
+static int
+lookup_hashtable( ulong table, const byte *key, size_t keylen,
+ int (*cmpfnc)(void*, const TRUSTREC *), void *cmpdata,
+ TRUSTREC *rec )
+{
+ int rc;
+ ulong hashrec, item;
+ int msb;
+ int level=0;
+
+ hashrec = table;
+ next_level:
+ msb = key[level];
+ hashrec += msb / ITEMS_PER_HTBL_RECORD;
+ rc = tdbio_read_record( hashrec, rec, RECTYPE_HTBL );
+ if( rc ) {
+ log_error("lookup_hashtable failed: %s\n", g10_errstr(rc) );
+ return rc;
+ }
+
+ item = rec->r.htbl.item[msb % ITEMS_PER_HTBL_RECORD];
+ if( !item )
+ return -1; /* not found */
+
+ rc = tdbio_read_record( item, rec, 0 );
+ if( rc ) {
+ log_error( "hashtable read failed: %s\n", g10_errstr(rc) );
+ return rc;
+ }
+ if( rec->rectype == RECTYPE_HTBL ) {
+ hashrec = item;
+ level++;
+ if( level >= keylen ) {
+ log_error("hashtable has invalid indirections\n");
+ return G10ERR_TRUSTDB;
+ }
+ goto next_level;
+ }
+ else if( rec->rectype == RECTYPE_HLST ) {
+ for(;;) {
+ int i;
+
+ for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
+ if( rec->r.hlst.rnum[i] ) {
+ TRUSTREC tmp;
+
+ rc = tdbio_read_record( rec->r.hlst.rnum[i], &tmp, 0 );
+ if( rc ) {
+ log_error( "lookup_hashtable: read item failed: %s\n",
+ g10_errstr(rc) );
+ return rc;
+ }
+ if( (*cmpfnc)( cmpdata, &tmp ) ) {
+ *rec = tmp;
+ return 0;
+ }
+ }
+ }
+ if( rec->r.hlst.next ) {
+ rc = tdbio_read_record( rec->r.hlst.next, rec, RECTYPE_HLST );
+ if( rc ) {
+ log_error( "lookup_hashtable: read hlst failed: %s\n",
+ g10_errstr(rc) );
+ return rc;
+ }
+ }
+ else
+ return -1; /* not found */
+ }
+ }
+
+
+ if( (*cmpfnc)( cmpdata, rec ) )
+ return 0; /* really found */
+
+ return -1; /* no: not found */
+}
+
+
+/****************
+ * Update the trust hashtbl or create the table if it does not exist
+ */
+static int
+update_trusthashtbl( TRUSTREC *tr )
+{
+ return upd_hashtable( get_trusthashrec(),
+ tr->r.trust.fingerprint, 20, tr->recnum );
+}
+
+
+
+void
+tdbio_dump_record( TRUSTREC *rec, FILE *fp )
+{
+ int i;
+ ulong rnum = rec->recnum;
+
+ fprintf(fp, "rec %5lu, ", rnum );
+
+ switch( rec->rectype ) {
+ case 0: fprintf(fp, "blank\n");
+ break;
+ case RECTYPE_VER: fprintf(fp,
+ "version, td=%lu, f=%lu, m/c/d=%d/%d/%d tm=%d nc=%lu (%s)\n",
+ rec->r.ver.trusthashtbl,
+ rec->r.ver.firstfree,
+ rec->r.ver.marginals,
+ rec->r.ver.completes,
+ rec->r.ver.cert_depth,
+ rec->r.ver.trust_model,
+ rec->r.ver.nextcheck,
+ strtimestamp(rec->r.ver.nextcheck)
+ );
+ break;
+ case RECTYPE_FREE: fprintf(fp, "free, next=%lu\n", rec->r.free.next );
+ break;
+ case RECTYPE_HTBL:
+ fprintf(fp, "htbl,");
+ for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ )
+ fprintf(fp, " %lu", rec->r.htbl.item[i] );
+ putc('\n', fp);
+ break;
+ case RECTYPE_HLST:
+ fprintf(fp, "hlst, next=%lu,", rec->r.hlst.next );
+ for(i=0; i < ITEMS_PER_HLST_RECORD; i++ )
+ fprintf(fp, " %lu", rec->r.hlst.rnum[i] );
+ putc('\n', fp);
+ break;
+ case RECTYPE_TRUST:
+ fprintf(fp, "trust ");
+ for(i=0; i < 20; i++ )
+ fprintf(fp, "%02X", rec->r.trust.fingerprint[i] );
+ fprintf (fp, ", ot=%d, d=%d, vl=%lu\n", rec->r.trust.ownertrust,
+ rec->r.trust.depth, rec->r.trust.validlist);
+ break;
+ case RECTYPE_VALID:
+ fprintf(fp, "valid ");
+ for(i=0; i < 20; i++ )
+ fprintf(fp, "%02X", rec->r.valid.namehash[i] );
+ fprintf (fp, ", v=%d, next=%lu\n", rec->r.valid.validity,
+ rec->r.valid.next);
+ break;
+ default:
+ fprintf(fp, "unknown type %d\n", rec->rectype );
+ break;
+ }
+}
+
+/****************
+ * read the record with number recnum
+ * returns: -1 on error, 0 on success
+ */
+int
+tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected )
+{
+ byte readbuf[TRUST_RECORD_LEN];
+ const byte *buf, *p;
+ int rc = 0;
+ int n, i;
+
+ if( db_fd == -1 )
+ open_db();
+ buf = get_record_from_cache( recnum );
+ if( !buf ) {
+ if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
+ log_error(_("trustdb: lseek failed: %s\n"), strerror(errno) );
+ return G10ERR_READ_FILE;
+ }
+ n = read( db_fd, readbuf, TRUST_RECORD_LEN);
+ if( !n ) {
+ return -1; /* eof */
+ }
+ else if( n != TRUST_RECORD_LEN ) {
+ log_error(_("trustdb: read failed (n=%d): %s\n"), n,
+ strerror(errno) );
+ return G10ERR_READ_FILE;
+ }
+ buf = readbuf;
+ }
+ rec->recnum = recnum;
+ rec->dirty = 0;
+ p = buf;
+ rec->rectype = *p++;
+ if( expected && rec->rectype != expected ) {
+ log_error("%lu: read expected rec type %d, got %d\n",
+ recnum, expected, rec->rectype );
+ return G10ERR_TRUSTDB;
+ }
+ p++; /* skip reserved byte */
+ switch( rec->rectype ) {
+ case 0: /* unused (free) record */
+ break;
+ case RECTYPE_VER: /* version record */
+ if( memcmp(buf+1, "gpg", 3 ) ) {
+ log_error( _("%s: not a trustdb file\n"), db_name );
+ rc = G10ERR_TRUSTDB;
+ }
+ p += 2; /* skip "gpg" */
+ rec->r.ver.version = *p++;
+ rec->r.ver.marginals = *p++;
+ rec->r.ver.completes = *p++;
+ rec->r.ver.cert_depth = *p++;
+ rec->r.ver.trust_model = *p++;
+ p += 3;
+ rec->r.ver.created = buftoulong(p); p += 4;
+ rec->r.ver.nextcheck = buftoulong(p); p += 4;
+ p += 4;
+ p += 4;
+ rec->r.ver.firstfree =buftoulong(p); p += 4;
+ p += 4;
+ rec->r.ver.trusthashtbl =buftoulong(p); p += 4;
+ if( recnum ) {
+ log_error( _("%s: version record with recnum %lu\n"), db_name,
+ (ulong)recnum );
+ rc = G10ERR_TRUSTDB;
+ }
+ else if( rec->r.ver.version != 3 ) {
+ log_error( _("%s: invalid file version %d\n"), db_name,
+ rec->r.ver.version );
+ rc = G10ERR_TRUSTDB;
+ }
+ break;
+ case RECTYPE_FREE:
+ rec->r.free.next = buftoulong(p); p += 4;
+ break;
+ case RECTYPE_HTBL:
+ for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) {
+ rec->r.htbl.item[i] = buftoulong(p); p += 4;
+ }
+ break;
+ case RECTYPE_HLST:
+ rec->r.hlst.next = buftoulong(p); p += 4;
+ for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
+ rec->r.hlst.rnum[i] = buftoulong(p); p += 4;
+ }
+ break;
+ case RECTYPE_TRUST:
+ memcpy( rec->r.trust.fingerprint, p, 20); p+=20;
+ rec->r.trust.ownertrust = *p++;
+ rec->r.trust.depth = *p++;
+ rec->r.trust.min_ownertrust = *p++;
+ p++;
+ rec->r.trust.validlist = buftoulong(p); p += 4;
+ break;
+ case RECTYPE_VALID:
+ memcpy( rec->r.valid.namehash, p, 20); p+=20;
+ rec->r.valid.validity = *p++;
+ rec->r.valid.next = buftoulong(p); p += 4;
+ rec->r.valid.full_count = *p++;
+ rec->r.valid.marginal_count = *p++;
+ break;
+ default:
+ log_error( "%s: invalid record type %d at recnum %lu\n",
+ db_name, rec->rectype, (ulong)recnum );
+ rc = G10ERR_TRUSTDB;
+ break;
+ }
+
+ return rc;
+}
+
+/****************
+ * Write the record at RECNUM
+ */
+int
+tdbio_write_record( TRUSTREC *rec )
+{
+ byte buf[TRUST_RECORD_LEN], *p;
+ int rc = 0;
+ int i;
+ ulong recnum = rec->recnum;
+
+ if( db_fd == -1 )
+ open_db();
+
+ memset(buf, 0, TRUST_RECORD_LEN);
+ p = buf;
+ *p++ = rec->rectype; p++;
+ switch( rec->rectype ) {
+ case 0: /* unused record */
+ break;
+ case RECTYPE_VER: /* version record */
+ if( recnum )
+ BUG();
+ memcpy(p-1, "gpg", 3 ); p += 2;
+ *p++ = rec->r.ver.version;
+ *p++ = rec->r.ver.marginals;
+ *p++ = rec->r.ver.completes;
+ *p++ = rec->r.ver.cert_depth;
+ *p++ = rec->r.ver.trust_model;
+ p += 3;
+ ulongtobuf(p, rec->r.ver.created); p += 4;
+ ulongtobuf(p, rec->r.ver.nextcheck); p += 4;
+ p += 4;
+ p += 4;
+ ulongtobuf(p, rec->r.ver.firstfree ); p += 4;
+ p += 4;
+ ulongtobuf(p, rec->r.ver.trusthashtbl ); p += 4;
+ break;
+
+ case RECTYPE_FREE:
+ ulongtobuf(p, rec->r.free.next); p += 4;
+ break;
+
+
+ case RECTYPE_HTBL:
+ for(i=0; i < ITEMS_PER_HTBL_RECORD; i++ ) {
+ ulongtobuf( p, rec->r.htbl.item[i]); p += 4;
+ }
+ break;
+
+ case RECTYPE_HLST:
+ ulongtobuf( p, rec->r.hlst.next); p += 4;
+ for(i=0; i < ITEMS_PER_HLST_RECORD; i++ ) {
+ ulongtobuf( p, rec->r.hlst.rnum[i]); p += 4;
+ }
+ break;
+
+ case RECTYPE_TRUST:
+ memcpy( p, rec->r.trust.fingerprint, 20); p += 20;
+ *p++ = rec->r.trust.ownertrust;
+ *p++ = rec->r.trust.depth;
+ *p++ = rec->r.trust.min_ownertrust;
+ p++;
+ ulongtobuf( p, rec->r.trust.validlist); p += 4;
+ break;
+
+ case RECTYPE_VALID:
+ memcpy( p, rec->r.valid.namehash, 20); p += 20;
+ *p++ = rec->r.valid.validity;
+ ulongtobuf( p, rec->r.valid.next); p += 4;
+ *p++ = rec->r.valid.full_count;
+ *p++ = rec->r.valid.marginal_count;
+ break;
+
+ default:
+ BUG();
+ }
+
+ rc = put_record_into_cache( recnum, buf );
+ if( rc )
+ ;
+ else if( rec->rectype == RECTYPE_TRUST )
+ rc = update_trusthashtbl( rec );
+
+ return rc;
+}
+
+int
+tdbio_delete_record( ulong recnum )
+{
+ TRUSTREC vr, rec;
+ int rc;
+
+ /* Must read the record fist, so we can drop it from the hash tables */
+ rc = tdbio_read_record( recnum, &rec, 0 );
+ if( rc )
+ ;
+ else if( rec.rectype == RECTYPE_TRUST ) {
+ rc = drop_from_hashtable( get_trusthashrec(),
+ rec.r.trust.fingerprint, 20, rec.recnum );
+ }
+
+ if( rc )
+ return rc;
+
+ /* now we can chnage it to a free record */
+ rc = tdbio_read_record( 0, &vr, RECTYPE_VER );
+ if( rc )
+ log_fatal( _("%s: error reading version record: %s\n"),
+ db_name, g10_errstr(rc) );
+
+ rec.recnum = recnum;
+ rec.rectype = RECTYPE_FREE;
+ rec.r.free.next = vr.r.ver.firstfree;
+ vr.r.ver.firstfree = recnum;
+ rc = tdbio_write_record( &rec );
+ if( !rc )
+ rc = tdbio_write_record( &vr );
+ return rc;
+}
+
+/****************
+ * create a new record and return its record number
+ */
+ulong
+tdbio_new_recnum()
+{
+ off_t offset;
+ ulong recnum;
+ TRUSTREC vr, rec;
+ int rc;
+
+ /* look for unused records */
+ rc = tdbio_read_record( 0, &vr, RECTYPE_VER );
+ if( rc )
+ log_fatal( _("%s: error reading version record: %s\n"),
+ db_name, g10_errstr(rc) );
+ if( vr.r.ver.firstfree ) {
+ recnum = vr.r.ver.firstfree;
+ rc = tdbio_read_record( recnum, &rec, RECTYPE_FREE );
+ if( rc ) {
+ log_error( _("%s: error reading free record: %s\n"),
+ db_name, g10_errstr(rc) );
+ return rc;
+ }
+ /* update dir record */
+ vr.r.ver.firstfree = rec.r.free.next;
+ rc = tdbio_write_record( &vr );
+ if( rc ) {
+ log_error( _("%s: error writing dir record: %s\n"),
+ db_name, g10_errstr(rc) );
+ return rc;
+ }
+ /*zero out the new record */
+ memset( &rec, 0, sizeof rec );
+ rec.rectype = 0; /* unused record */
+ rec.recnum = recnum;
+ rc = tdbio_write_record( &rec );
+ if( rc )
+ log_fatal(_("%s: failed to zero a record: %s\n"),
+ db_name, g10_errstr(rc));
+ }
+ else { /* not found, append a new record */
+ offset = lseek( db_fd, 0, SEEK_END );
+ if( offset == -1 )
+ log_fatal("trustdb: lseek to end failed: %s\n", strerror(errno) );
+ recnum = offset / TRUST_RECORD_LEN;
+ assert(recnum); /* this is will never be the first record */
+ /* we must write a record, so that the next call to this function
+ * returns another recnum */
+ memset( &rec, 0, sizeof rec );
+ rec.rectype = 0; /* unused record */
+ rec.recnum = recnum;
+ rc = 0;
+ if( lseek( db_fd, recnum * TRUST_RECORD_LEN, SEEK_SET ) == -1 ) {
+ log_error(_("trustdb rec %lu: lseek failed: %s\n"),
+ recnum, strerror(errno) );
+ rc = G10ERR_WRITE_FILE;
+ }
+ else {
+ int n = write( db_fd, &rec, TRUST_RECORD_LEN);
+ if( n != TRUST_RECORD_LEN ) {
+ log_error(_("trustdb rec %lu: write failed (n=%d): %s\n"),
+ recnum, n, strerror(errno) );
+ rc = G10ERR_WRITE_FILE;
+ }
+ }
+
+ if( rc )
+ log_fatal(_("%s: failed to append a record: %s\n"),
+ db_name, g10_errstr(rc));
+ }
+ return recnum ;
+}
+
+
+
+static int
+cmp_trec_fpr ( void *fpr, const TRUSTREC *rec )
+{
+ return rec->rectype == RECTYPE_TRUST
+ && !memcmp( rec->r.trust.fingerprint, fpr, 20);
+}
+
+
+int
+tdbio_search_trust_byfpr( const byte *fingerprint, TRUSTREC *rec )
+{
+ int rc;
+
+ /* locate the trust record using the hash table */
+ rc = lookup_hashtable( get_trusthashrec(), fingerprint, 20,
+ cmp_trec_fpr, (void*)fingerprint, rec );
+ return rc;
+}
+
+int
+tdbio_search_trust_bypk (PKT_public_key *pk, TRUSTREC *rec)
+{
+ byte fingerprint[MAX_FINGERPRINT_LEN];
+ size_t fingerlen;
+
+ fingerprint_from_pk( pk, fingerprint, &fingerlen );
+ for (; fingerlen < 20; fingerlen++ )
+ fingerprint[fingerlen] = 0;
+ return tdbio_search_trust_byfpr (fingerprint, rec);
+}
+
+
+
+void
+tdbio_invalid(void)
+{
+ log_error(_(
+ "the trustdb is corrupted; please run \"gpg --fix-trustdb\".\n") );
+ g10_exit(2);
+}
+
+/*
+ * Migrate the trustdb as just up to gpg 1.0.6 (trustdb version 2)
+ * to the 2.1 version as used with 1.0.6b - This is pretty trivial as needs
+ * only to scan the tdb and insert new the new trust records. The old ones are
+ * obsolte from now on
+ */
+static void
+migrate_from_v2 ()
+{
+ TRUSTREC rec;
+ int i, n;
+ struct {
+ ulong keyrecno;
+ byte ot;
+ byte okay;
+ byte fpr[20];
+ } *ottable;
+ int ottable_size, ottable_used;
+ byte oldbuf[40];
+ ulong recno;
+ int rc, count;
+
+ ottable_size = 5;
+ ottable = xmalloc (ottable_size * sizeof *ottable);
+ ottable_used = 0;
+
+ /* We have some restrictions here. We can't use the version record
+ * and we can't use any of the old hashtables because we dropped the
+ * code. So we first collect all ownertrusts and then use a second
+ * pass fo find the associated keys. We have to do this all without using
+ * the regular record read functions.
+ */
+
+ /* get all the ownertrusts */
+ if (lseek (db_fd, 0, SEEK_SET ) == -1 )
+ log_fatal ("migrate_from_v2: lseek failed: %s\n", strerror (errno));
+ for (recno=0;;recno++)
+ {
+ do
+ n = read (db_fd, oldbuf, 40);
+ while (n==-1 && errno == EINTR);
+ if (!n)
+ break; /* eof */
+ if (n != 40)
+ log_fatal ("migrate_vfrom_v2: read error or short read\n");
+
+ if (*oldbuf != 2)
+ continue;
+
+ /* v2 dir record */
+ if (ottable_used == ottable_size)
+ {
+ ottable_size += 1000;
+ ottable = xrealloc (ottable, ottable_size * sizeof *ottable);
+ }
+ ottable[ottable_used].keyrecno = buftoulong (oldbuf+6);
+ ottable[ottable_used].ot = oldbuf[18];
+ ottable[ottable_used].okay = 0;
+ memset (ottable[ottable_used].fpr,0, 20);
+ if (ottable[ottable_used].keyrecno && ottable[ottable_used].ot)
+ ottable_used++;
+ }
+ log_info ("found %d ownertrust records\n", ottable_used);
+
+ /* Read again and find the fingerprints */
+ if (lseek (db_fd, 0, SEEK_SET ) == -1 )
+ log_fatal ("migrate_from_v2: lseek failed: %s\n", strerror (errno));
+ for (recno=0;;recno++)
+ {
+ do
+ n = read (db_fd, oldbuf, 40);
+ while (n==-1 && errno == EINTR);
+ if (!n)
+ break; /* eof */
+ if (n != 40)
+ log_fatal ("migrate_from_v2: read error or short read\n");
+
+ if (*oldbuf != 3)
+ continue;
+
+ /* v2 key record */
+ for (i=0; i < ottable_used; i++)
+ {
+ if (ottable[i].keyrecno == recno)
+ {
+ memcpy (ottable[i].fpr, oldbuf+20, 20);
+ ottable[i].okay = 1;
+ break;
+ }
+ }
+ }
+
+ /* got everything - create the v3 trustdb */
+ if (ftruncate (db_fd, 0))
+ log_fatal ("can't truncate `%s': %s\n", db_name, strerror (errno) );
+ if (create_version_record ())
+ log_fatal ("failed to recreate version record of `%s'\n", db_name);
+
+ /* access the hash table, so it is store just after the version record,
+ * this is not needed put a dump is more pretty */
+ get_trusthashrec ();
+
+ /* And insert the old ownertrust values */
+ count = 0;
+ for (i=0; i < ottable_used; i++)
+ {
+ if (!ottable[i].okay)
+ continue;
+
+ memset (&rec, 0, sizeof rec);
+ rec.recnum = tdbio_new_recnum ();
+ rec.rectype = RECTYPE_TRUST;
+ memcpy(rec.r.trust.fingerprint, ottable[i].fpr, 20);
+ rec.r.trust.ownertrust = ottable[i].ot;
+ if (tdbio_write_record (&rec))
+ log_fatal ("failed to write trust record of `%s'\n", db_name);
+ count++;
+ }
+
+ revalidation_mark ();
+ rc = tdbio_sync ();
+ if (rc)
+ log_fatal ("failed to sync `%s'\n", db_name);
+ log_info ("migrated %d version 2 ownertrusts\n", count);
+ xfree (ottable);
+}
diff --git a/g10/tdbio.h b/g10/tdbio.h
new file mode 100644
index 0000000..80a70d9
--- /dev/null
+++ b/g10/tdbio.h
@@ -0,0 +1,118 @@
+/* tdbio.h - Trust database I/O functions
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef G10_TDBIO_H
+#define G10_TDBIO_H
+
+#include "host2net.h"
+
+#define TRUST_RECORD_LEN 40
+#define SIGS_PER_RECORD ((TRUST_RECORD_LEN-10)/5)
+#define ITEMS_PER_HTBL_RECORD ((TRUST_RECORD_LEN-2)/4)
+#define ITEMS_PER_HLST_RECORD ((TRUST_RECORD_LEN-6)/5)
+#define ITEMS_PER_PREF_RECORD (TRUST_RECORD_LEN-10)
+#if ITEMS_PER_PREF_RECORD % 2
+#error ITEMS_PER_PREF_RECORD must be even
+#endif
+#define MAX_LIST_SIGS_DEPTH 20
+
+
+#define RECTYPE_VER 1
+#define RECTYPE_HTBL 10
+#define RECTYPE_HLST 11
+#define RECTYPE_TRUST 12
+#define RECTYPE_VALID 13
+#define RECTYPE_FREE 254
+
+
+struct trust_record {
+ int rectype;
+ int mark;
+ int dirty; /* for now only used internal by functions */
+ struct trust_record *next; /* help pointer to build lists in memory */
+ ulong recnum;
+ union {
+ struct { /* version record: */
+ byte version; /* should be 3 */
+ byte marginals;
+ byte completes;
+ byte cert_depth;
+ byte trust_model;
+ ulong created; /* timestamp of trustdb creation */
+ ulong nextcheck; /* timestamp of next scheduled check */
+ ulong reserved;
+ ulong reserved2;
+ ulong firstfree;
+ ulong reserved3;
+ ulong trusthashtbl;
+ } ver;
+ struct { /* free record */
+ ulong next;
+ } free;
+ struct {
+ ulong item[ITEMS_PER_HTBL_RECORD];
+ } htbl;
+ struct {
+ ulong next;
+ ulong rnum[ITEMS_PER_HLST_RECORD]; /* of another record */
+ } hlst;
+ struct {
+ byte fingerprint[20];
+ byte ownertrust;
+ byte depth;
+ ulong validlist;
+ byte min_ownertrust;
+ } trust;
+ struct {
+ byte namehash[20];
+ ulong next;
+ byte validity;
+ byte full_count;
+ byte marginal_count;
+ } valid;
+ } r;
+};
+typedef struct trust_record TRUSTREC;
+
+/*-- tdbio.c --*/
+int tdbio_update_version_record(void);
+int tdbio_set_dbname( const char *new_dbname, int create );
+const char *tdbio_get_dbname(void);
+void tdbio_dump_record( TRUSTREC *rec, FILE *fp );
+int tdbio_read_record( ulong recnum, TRUSTREC *rec, int expected );
+int tdbio_write_record( TRUSTREC *rec );
+int tdbio_db_matches_options(void);
+byte tdbio_read_model(void);
+ulong tdbio_read_nextcheck (void);
+int tdbio_write_nextcheck (ulong stamp);
+int tdbio_is_dirty(void);
+int tdbio_sync(void);
+int tdbio_begin_transaction(void);
+int tdbio_end_transaction(void);
+int tdbio_cancel_transaction(void);
+int tdbio_delete_record( ulong recnum );
+ulong tdbio_new_recnum(void);
+int tdbio_search_trust_byfpr(const byte *fingerprint, TRUSTREC *rec );
+int tdbio_search_trust_bypk(PKT_public_key *pk, TRUSTREC *rec );
+
+void tdbio_invalid(void);
+
+#endif /*G10_TDBIO_H*/
diff --git a/g10/textfilter.c b/g10/textfilter.c
new file mode 100644
index 0000000..8665a72
--- /dev/null
+++ b/g10/textfilter.c
@@ -0,0 +1,251 @@
+/* textfilter.c
+ * Copyright (C) 1998, 1999, 2000, 2001, 2004 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+
+#include "errors.h"
+#include "iobuf.h"
+#include "memory.h"
+#include "util.h"
+#include "filter.h"
+#include "i18n.h"
+#include "options.h"
+#include "status.h"
+
+#ifdef HAVE_DOSISH_SYSTEM
+#define LF "\r\n"
+#else
+#define LF "\n"
+#endif
+
+#define MAX_LINELEN 19995 /* a little bit smaller than in armor.c */
+ /* to make sure that a warning is displayed while */
+ /* creating a message */
+
+static unsigned
+len_without_trailing_chars( byte *line, unsigned len, const char *trimchars )
+{
+ byte *p, *mark;
+ unsigned n;
+
+ for(mark=NULL, p=line, n=0; n < len; n++, p++ ) {
+ if( strchr( trimchars, *p ) ) {
+ if( !mark )
+ mark = p;
+ }
+ else
+ mark = NULL;
+ }
+
+ return mark? (mark - line) : len;
+}
+
+
+static int
+standard( text_filter_context_t *tfx, IOBUF a,
+ byte *buf, size_t size, size_t *ret_len)
+{
+ int rc=0;
+ size_t len = 0;
+ unsigned maxlen;
+
+ assert( size > 10 );
+ size -= 2; /* reserve 2 bytes to append CR,LF */
+ while( !rc && len < size ) {
+ int lf_seen;
+
+ while( len < size && tfx->buffer_pos < tfx->buffer_len )
+ buf[len++] = tfx->buffer[tfx->buffer_pos++];
+ if( len >= size )
+ continue;
+
+ /* read the next line */
+ maxlen = MAX_LINELEN;
+ tfx->buffer_pos = 0;
+ tfx->buffer_len = iobuf_read_line( a, &tfx->buffer,
+ &tfx->buffer_size, &maxlen );
+ if( !maxlen )
+ tfx->truncated++;
+ if( !tfx->buffer_len ) {
+ if( !len )
+ rc = -1; /* eof */
+ break;
+ }
+ lf_seen = tfx->buffer[tfx->buffer_len-1] == '\n';
+
+ /* The story behind this is that 2440 says that textmode
+ hashes should canonicalize line endings to CRLF and remove
+ spaces and tabs. 2440bis-12 says to just canonicalize to
+ CRLF. 1.4.0 was released using the bis-12 behavior, but it
+ was discovered that many mail clients do not canonicalize
+ PGP/MIME signature text appropriately (and were relying on
+ GnuPG to handle trailing spaces). So, we default to the
+ 2440 behavior, but use the 2440bis-12 behavior if the user
+ specifies --no-rfc2440-text. The default will be changed
+ at some point in the future when the mail clients have been
+ upgraded. Aside from PGP/MIME and broken mail clients,
+ this makes no difference to any signatures in the real
+ world except for a textmode detached signature. PGP always
+ used the 2440bis-12 behavior (ignoring 2440 itself), so
+ this actually makes us compatible with PGP textmode
+ detached signatures for the first time. */
+ if(opt.rfc2440_text)
+ tfx->buffer_len=trim_trailing_chars(tfx->buffer,tfx->buffer_len,
+ " \t\r\n");
+ else
+ tfx->buffer_len=trim_trailing_chars(tfx->buffer,tfx->buffer_len,
+ "\r\n");
+
+ if( lf_seen ) {
+ tfx->buffer[tfx->buffer_len++] = '\r';
+ tfx->buffer[tfx->buffer_len++] = '\n';
+ }
+ }
+ *ret_len = len;
+ return rc;
+}
+
+
+/****************
+ * The filter is used to make canonical text: Lines are terminated by
+ * CR, LF, trailing white spaces are removed.
+ */
+int
+text_filter( void *opaque, int control,
+ IOBUF a, byte *buf, size_t *ret_len)
+{
+ size_t size = *ret_len;
+ text_filter_context_t *tfx = opaque;
+ int rc=0;
+
+ if( control == IOBUFCTRL_UNDERFLOW ) {
+ rc = standard( tfx, a, buf, size, ret_len );
+ }
+ else if( control == IOBUFCTRL_FREE ) {
+ if( tfx->truncated )
+ log_error(_("can't handle text lines longer than %d characters\n"),
+ MAX_LINELEN );
+ xfree( tfx->buffer );
+ tfx->buffer = NULL;
+ }
+ else if( control == IOBUFCTRL_DESC )
+ *(char**)buf = "text_filter";
+ return rc;
+}
+
+
+/****************
+ * Copy data from INP to OUT and do some escaping if requested.
+ * md is updated as required by rfc2440
+ */
+int
+copy_clearsig_text( IOBUF out, IOBUF inp, MD_HANDLE md,
+ int escape_dash, int escape_from, int pgp2mode )
+{
+ unsigned maxlen;
+ byte *buffer = NULL; /* malloced buffer */
+ unsigned bufsize; /* and size of this buffer */
+ unsigned n;
+ int truncated = 0;
+ int pending_lf = 0;
+
+ if( !opt.pgp2_workarounds )
+ pgp2mode = 0;
+
+ if( !escape_dash )
+ escape_from = 0;
+
+ write_status (STATUS_BEGIN_SIGNING);
+
+ for(;;) {
+ maxlen = MAX_LINELEN;
+ n = iobuf_read_line( inp, &buffer, &bufsize, &maxlen );
+ if( !maxlen )
+ truncated++;
+
+ if( !n )
+ break; /* read_line has returned eof */
+
+ /* update the message digest */
+ if( escape_dash ) {
+ if( pending_lf ) {
+ md_putc( md, '\r' );
+ md_putc( md, '\n' );
+ }
+ md_write( md, buffer,
+ len_without_trailing_chars( buffer, n,
+ pgp2mode? " \r\n":" \t\r\n"));
+ }
+ else
+ md_write( md, buffer, n );
+ pending_lf = buffer[n-1] == '\n';
+
+ /* write the output */
+ if( ( escape_dash && *buffer == '-')
+ || ( escape_from && n > 4 && !memcmp(buffer, "From ", 5 ) ) ) {
+ iobuf_put( out, '-' );
+ iobuf_put( out, ' ' );
+ }
+
+#if 0 /*defined(HAVE_DOSISH_SYSTEM)*/
+ /* We don't use this anymore because my interpretation of rfc2440 7.1
+ * is that there is no conversion needed. If one decides to
+ * clearsign a unix file on a DOS box he will get a mixed line endings.
+ * If at some point it turns out, that a conversion is a nice feature
+ * we can make an option out of it.
+ */
+ /* make sure the lines do end in CR,LF */
+ if( n > 1 && ( (buffer[n-2] == '\r' && buffer[n-1] == '\n' )
+ || (buffer[n-2] == '\n' && buffer[n-1] == '\r'))) {
+ iobuf_write( out, buffer, n-2 );
+ iobuf_put( out, '\r');
+ iobuf_put( out, '\n');
+ }
+ else if( n && buffer[n-1] == '\n' ) {
+ iobuf_write( out, buffer, n-1 );
+ iobuf_put( out, '\r');
+ iobuf_put( out, '\n');
+ }
+ else
+ iobuf_write( out, buffer, n );
+
+#else
+ iobuf_write( out, buffer, n );
+#endif
+ }
+
+ /* at eof */
+ if( !pending_lf ) { /* make sure that the file ends with a LF */
+ iobuf_writestr( out, LF );
+ if( !escape_dash )
+ md_putc( md, '\n' );
+ }
+
+ if( truncated )
+ log_info(_("input line longer than %d characters\n"), MAX_LINELEN );
+
+ return 0; /* okay */
+}
diff --git a/g10/tlv.c b/g10/tlv.c
new file mode 100644
index 0000000..c7233e0
--- /dev/null
+++ b/g10/tlv.c
@@ -0,0 +1,306 @@
+/* tlv.c - Tag-Length-Value Utilities
+ * Copyright (C) 2003, 2004, 2005 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#if GNUPG_MAJOR_VERSION == 1
+#define GPG_ERR_EOF (-1)
+#define GPG_ERR_BAD_BER (1) /*G10ERR_GENERAL*/
+#define GPG_ERR_INV_SEXP (45) /*G10ERR_INV_ARG*/
+typedef int gpg_error_t;
+#define gpg_error(n) (n)
+#else
+#include <gpg-error.h>
+#endif
+
+
+#include "tlv.h"
+
+static const unsigned char *
+do_find_tlv (const unsigned char *buffer, size_t length,
+ int tag, size_t *nbytes, int nestlevel)
+{
+ const unsigned char *s = buffer;
+ size_t n = length;
+ size_t len;
+ int this_tag;
+ int composite;
+
+ for (;;)
+ {
+ buffer = s;
+ if (n < 2)
+ return NULL; /* Buffer definitely too short for tag and length. */
+ if (!*s || *s == 0xff)
+ { /* Skip optional filler between TLV objects. */
+ s++;
+ n--;
+ continue;
+ }
+ composite = !!(*s & 0x20);
+ if ((*s & 0x1f) == 0x1f)
+ { /* more tag bytes to follow */
+ s++;
+ n--;
+ if (n < 2)
+ return NULL; /* buffer definitely too short for tag and length. */
+ if ((*s & 0x1f) == 0x1f)
+ return NULL; /* We support only up to 2 bytes. */
+ this_tag = (s[-1] << 8) | (s[0] & 0x7f);
+ }
+ else
+ this_tag = s[0];
+ len = s[1];
+ s += 2; n -= 2;
+ if (len < 0x80)
+ ;
+ else if (len == 0x81)
+ { /* One byte length follows. */
+ if (!n)
+ return NULL; /* we expected 1 more bytes with the length. */
+ len = s[0];
+ s++; n--;
+ }
+ else if (len == 0x82)
+ { /* Two byte length follows. */
+ if (n < 2)
+ return NULL; /* We expected 2 more bytes with the length. */
+ len = (s[0] << 8) | s[1];
+ s += 2; n -= 2;
+ }
+ else
+ return NULL; /* APDU limit is 65535, thus it does not make
+ sense to assume longer length fields. */
+
+ if (composite && nestlevel < 100)
+ { /* Dive into this composite DO after checking for a too deep
+ nesting. */
+ const unsigned char *tmp_s;
+ size_t tmp_len;
+
+ tmp_s = do_find_tlv (s, len, tag, &tmp_len, nestlevel+1);
+ if (tmp_s)
+ {
+ *nbytes = tmp_len;
+ return tmp_s;
+ }
+ }
+
+ if (this_tag == tag)
+ {
+ *nbytes = len;
+ return s;
+ }
+ if (len > n)
+ return NULL; /* Buffer too short to skip to the next tag. */
+ s += len; n -= len;
+ }
+}
+
+
+/* Locate a TLV encoded data object in BUFFER of LENGTH and
+ return a pointer to value as well as its length in NBYTES. Return
+ NULL if it was not found or if the object does not fit into the buffer. */
+const unsigned char *
+find_tlv (const unsigned char *buffer, size_t length,
+ int tag, size_t *nbytes)
+{
+ const unsigned char *p;
+
+ p = do_find_tlv (buffer, length, tag, nbytes, 0);
+ if (p && *nbytes > (length - (p-buffer)))
+ p = NULL; /* Object longer than buffer. */
+ return p;
+}
+
+
+
+/* Locate a TLV encoded data object in BUFFER of LENGTH and
+ return a pointer to value as well as its length in NBYTES. Return
+ NULL if it was not found. Note, that the function does not check
+ whether the value fits into the provided buffer. */
+const unsigned char *
+find_tlv_unchecked (const unsigned char *buffer, size_t length,
+ int tag, size_t *nbytes)
+{
+ return do_find_tlv (buffer, length, tag, nbytes, 0);
+}
+
+
+/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
+ and the length part from the TLV triplet. Update BUFFER and SIZE
+ on success. */
+gpg_error_t
+parse_ber_header (unsigned char const **buffer, size_t *size,
+ int *r_class, int *r_tag,
+ int *r_constructed, int *r_ndef,
+ size_t *r_length, size_t *r_nhdr)
+{
+ int c;
+ unsigned long tag;
+ const unsigned char *buf = *buffer;
+ size_t length = *size;
+
+ *r_ndef = 0;
+ *r_length = 0;
+ *r_nhdr = 0;
+
+ /* Get the tag. */
+ if (!length)
+ return gpg_error (GPG_ERR_EOF);
+ c = *buf++; length--; ++*r_nhdr;
+
+ *r_class = (c & 0xc0) >> 6;
+ *r_constructed = !!(c & 0x20);
+ tag = c & 0x1f;
+
+ if (tag == 0x1f)
+ {
+ tag = 0;
+ do
+ {
+ tag <<= 7;
+ if (!length)
+ return gpg_error (GPG_ERR_EOF);
+ c = *buf++; length--; ++*r_nhdr;
+ tag |= c & 0x7f;
+
+ }
+ while (c & 0x80);
+ }
+ *r_tag = tag;
+
+ /* Get the length. */
+ if (!length)
+ return gpg_error (GPG_ERR_EOF);
+ c = *buf++; length--; ++*r_nhdr;
+
+ if ( !(c & 0x80) )
+ *r_length = c;
+ else if (c == 0x80)
+ *r_ndef = 1;
+ else if (c == 0xff)
+ return gpg_error (GPG_ERR_BAD_BER);
+ else
+ {
+ unsigned long len = 0;
+ int count = c & 0x7f;
+
+ if (count > sizeof (len) || count > sizeof (size_t))
+ return gpg_error (GPG_ERR_BAD_BER);
+
+ for (; count; count--)
+ {
+ len <<= 8;
+ if (!length)
+ return gpg_error (GPG_ERR_EOF);
+ c = *buf++; length--; ++*r_nhdr;
+ len |= c & 0xff;
+ }
+ *r_length = len;
+ }
+
+ /* Without this kludge some example certs can't be parsed. */
+ if (*r_class == CLASS_UNIVERSAL && !*r_tag)
+ *r_length = 0;
+
+ *buffer = buf;
+ *size = length;
+ return 0;
+}
+
+
+/* FIXME: The following function should not go into this file but for
+ now it is easier to keep it here. */
+
+/* Return the next token of an canconical encoded S-expression. BUF
+ is the pointer to the S-expression and BUFLEN is a pointer to the
+ length of this S-expression (used to validate the syntax). Both
+ are updated to reflect the new position. The token itself is
+ returned as a pointer into the orginal buffer at TOK and TOKLEN.
+ If a parentheses is the next token, TOK will be set to NULL.
+ TOKLEN is checked to be within the bounds. On error a error code
+ is returned and all pointers should are not guaranteed to point to
+ a meanigful value. DEPTH should be initialized to 0 and will
+ reflect on return the actual depth of the tree. To detect the end
+ of the S-expression it is advisable to check DEPTH after a
+ successful return:
+
+ depth = 0;
+ while (!(err = parse_sexp (&buf, &buflen, &depth, &tok, &toklen))
+ && depth)
+ process_token (tok, toklen);
+ if (err)
+ handle_error ();
+ */
+gpg_error_t
+parse_sexp (unsigned char const **buf, size_t *buflen,
+ int *depth, unsigned char const **tok, size_t *toklen)
+{
+ const unsigned char *s;
+ size_t n, vlen;
+
+ s = *buf;
+ n = *buflen;
+ *tok = NULL;
+ *toklen = 0;
+ if (!n)
+ return *depth ? gpg_error (GPG_ERR_INV_SEXP) : 0;
+ if (*s == '(')
+ {
+ s++; n--;
+ (*depth)++;
+ *buf = s;
+ *buflen = n;
+ return 0;
+ }
+ if (*s == ')')
+ {
+ if (!*depth)
+ return gpg_error (GPG_ERR_INV_SEXP);
+ *toklen = 1;
+ s++; n--;
+ (*depth)--;
+ *buf = s;
+ *buflen = n;
+ return 0;
+ }
+ for (vlen=0; n && *s && *s != ':' && (*s >= '0' && *s <= '9'); s++, n--)
+ vlen = vlen*10 + (*s - '0');
+ if (!n || *s != ':')
+ return gpg_error (GPG_ERR_INV_SEXP);
+ s++; n--;
+ if (vlen > n)
+ return gpg_error (GPG_ERR_INV_SEXP);
+ *tok = s;
+ *toklen = vlen;
+ s += vlen;
+ n -= vlen;
+ *buf = s;
+ *buflen = n;
+ return 0;
+}
+
diff --git a/g10/tlv.h b/g10/tlv.h
new file mode 100644
index 0000000..877573d
--- /dev/null
+++ b/g10/tlv.h
@@ -0,0 +1,109 @@
+/* tlv.h - Tag-Length-Value Utilities
+ * Copyright (C) 2004 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef SCD_TLV_H
+#define SCD_TLV_H 1
+
+
+enum tlv_tag_class {
+ CLASS_UNIVERSAL = 0,
+ CLASS_APPLICATION = 1,
+ CLASS_CONTEXT = 2,
+ CLASS_PRIVATE =3
+};
+
+enum tlv_tag_type {
+ TAG_NONE = 0,
+ TAG_BOOLEAN = 1,
+ TAG_INTEGER = 2,
+ TAG_BIT_STRING = 3,
+ TAG_OCTET_STRING = 4,
+ TAG_NULL = 5,
+ TAG_OBJECT_ID = 6,
+ TAG_OBJECT_DESCRIPTOR = 7,
+ TAG_EXTERNAL = 8,
+ TAG_REAL = 9,
+ TAG_ENUMERATED = 10,
+ TAG_EMBEDDED_PDV = 11,
+ TAG_UTF8_STRING = 12,
+ TAG_REALTIVE_OID = 13,
+ TAG_SEQUENCE = 16,
+ TAG_SET = 17,
+ TAG_NUMERIC_STRING = 18,
+ TAG_PRINTABLE_STRING = 19,
+ TAG_TELETEX_STRING = 20,
+ TAG_VIDEOTEX_STRING = 21,
+ TAG_IA5_STRING = 22,
+ TAG_UTC_TIME = 23,
+ TAG_GENERALIZED_TIME = 24,
+ TAG_GRAPHIC_STRING = 25,
+ TAG_VISIBLE_STRING = 26,
+ TAG_GENERAL_STRING = 27,
+ TAG_UNIVERSAL_STRING = 28,
+ TAG_CHARACTER_STRING = 29,
+ TAG_BMP_STRING = 30
+};
+
+
+/* Locate a TLV encoded data object in BUFFER of LENGTH and return a
+ pointer to value as well as its length in NBYTES. Return NULL if
+ it was not found or if the object does not fit into the buffer. */
+const unsigned char *find_tlv (const unsigned char *buffer, size_t length,
+ int tag, size_t *nbytes);
+
+
+/* Locate a TLV encoded data object in BUFFER of LENGTH and return a
+ pointer to value as well as its length in NBYTES. Return NULL if
+ it was not found. Note, that the function does not check whether
+ the value fits into the provided buffer.*/
+const unsigned char *find_tlv_unchecked (const unsigned char *buffer,
+ size_t length,
+ int tag, size_t *nbytes);
+
+
+/* ASN.1 BER parser: Parse BUFFER of length SIZE and return the tag
+ and the length part from the TLV triplet. Update BUFFER and SIZE
+ on success. */
+gpg_error_t parse_ber_header (unsigned char const **buffer, size_t *size,
+ int *r_class, int *r_tag,
+ int *r_constructed,
+ int *r_ndef, size_t *r_length, size_t *r_nhdr);
+
+
+
+/* Return the next token of an canconical encoded S-expression. BUF
+ is the pointer to the S-expression and BUFLEN is a pointer to the
+ length of this S-expression (used to validate the syntax). Both
+ are updated to reflect the new position. The token itself is
+ returned as a pointer into the orginal buffer at TOK and TOKLEN.
+ If a parentheses is the next token, TOK will be set to NULL.
+ TOKLEN is checked to be within the bounds. On error a error code
+ is returned and all pointers should are not guaranteed to point to
+ a meanigful value. DEPTH should be initialized to 0 and will
+ reflect on return the actual depth of the tree. To detect the end
+ of the S-expression it is advisable to check DEPTH after a
+ successful return. */
+gpg_error_t parse_sexp (unsigned char const **buf, size_t *buflen,
+ int *depth, unsigned char const **tok, size_t *toklen);
+
+
+
+#endif /* SCD_TLV_H */
diff --git a/g10/trustdb.c b/g10/trustdb.c
new file mode 100644
index 0000000..e372bf8
--- /dev/null
+++ b/g10/trustdb.c
@@ -0,0 +1,2368 @@
+/* trustdb.c
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ * 2005 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <assert.h>
+
+#ifndef DISABLE_REGEX
+#include <sys/types.h>
+#ifdef USE_INTERNAL_REGEX
+#include "_regex.h"
+#else
+#include <regex.h>
+#endif
+#endif /* !DISABLE_REGEX */
+
+#include "errors.h"
+#include "iobuf.h"
+#include "keydb.h"
+#include "memory.h"
+#include "util.h"
+#include "options.h"
+#include "packet.h"
+#include "main.h"
+#include "i18n.h"
+#include "tdbio.h"
+#include "trustdb.h"
+
+
+/*
+ * A structure to store key identification as well as some stuff needed
+ * for validation
+ */
+struct key_item {
+ struct key_item *next;
+ unsigned int ownertrust,min_ownertrust;
+ byte trust_depth;
+ byte trust_value;
+ char *trust_regexp;
+ u32 kid[2];
+};
+
+
+typedef struct key_item **KeyHashTable; /* see new_key_hash_table() */
+
+/*
+ * Structure to keep track of keys, this is used as an array wherre
+ * the item right after the last one has a keyblock set to NULL.
+ * Maybe we can drop this thing and replace it by key_item
+ */
+struct key_array {
+ KBNODE keyblock;
+};
+
+
+/* control information for the trust DB */
+static struct {
+ int init;
+ int level;
+ char *dbname;
+} trustdb_args;
+
+/* some globals */
+static struct key_item *user_utk_list; /* temp. used to store --trusted-keys */
+static struct key_item *utk_list; /* all ultimately trusted keys */
+
+static int pending_check_trustdb;
+
+static int validate_keys (int interactive);
+
+
+/**********************************************
+ ************* some helpers *******************
+ **********************************************/
+
+static struct key_item *
+new_key_item (void)
+{
+ struct key_item *k;
+
+ k = xmalloc_clear (sizeof *k);
+ return k;
+}
+
+static void
+release_key_items (struct key_item *k)
+{
+ struct key_item *k2;
+
+ for (; k; k = k2)
+ {
+ k2 = k->next;
+ xfree (k->trust_regexp);
+ xfree (k);
+ }
+}
+
+/*
+ * For fast keylook up we need a hash table. Each byte of a KeyIDs
+ * should be distributed equally over the 256 possible values (except
+ * for v3 keyIDs but we consider them as not important here). So we
+ * can just use 10 bits to index a table of 1024 key items.
+ * Possible optimization: Don not use key_items but other hash_table when the
+ * duplicates lists gets too large.
+ */
+static KeyHashTable
+new_key_hash_table (void)
+{
+ struct key_item **tbl;
+
+ tbl = xmalloc_clear (1024 * sizeof *tbl);
+ return tbl;
+}
+
+static void
+release_key_hash_table (KeyHashTable tbl)
+{
+ int i;
+
+ if (!tbl)
+ return;
+ for (i=0; i < 1024; i++)
+ release_key_items (tbl[i]);
+ xfree (tbl);
+}
+
+/*
+ * Returns: True if the keyID is in the given hash table
+ */
+static int
+test_key_hash_table (KeyHashTable tbl, u32 *kid)
+{
+ struct key_item *k;
+
+ for (k = tbl[(kid[1] & 0x03ff)]; k; k = k->next)
+ if (k->kid[0] == kid[0] && k->kid[1] == kid[1])
+ return 1;
+ return 0;
+}
+
+/*
+ * Add a new key to the hash table. The key is identified by its key ID.
+ */
+static void
+add_key_hash_table (KeyHashTable tbl, u32 *kid)
+{
+ struct key_item *k, *kk;
+
+ for (k = tbl[(kid[1] & 0x03ff)]; k; k = k->next)
+ if (k->kid[0] == kid[0] && k->kid[1] == kid[1])
+ return; /* already in table */
+
+ kk = new_key_item ();
+ kk->kid[0] = kid[0];
+ kk->kid[1] = kid[1];
+ kk->next = tbl[(kid[1] & 0x03ff)];
+ tbl[(kid[1] & 0x03ff)] = kk;
+}
+
+/*
+ * Release a key_array
+ */
+static void
+release_key_array ( struct key_array *keys )
+{
+ struct key_array *k;
+
+ if (keys) {
+ for (k=keys; k->keyblock; k++)
+ release_kbnode (k->keyblock);
+ xfree (keys);
+ }
+}
+
+
+/*********************************************
+ ********** Initialization *****************
+ *********************************************/
+
+
+
+/*
+ * Used to register extra ultimately trusted keys - this has to be done
+ * before initializing the validation module.
+ * FIXME: Should be replaced by a function to add those keys to the trustdb.
+ */
+void
+register_trusted_keyid(u32 *keyid)
+{
+ struct key_item *k;
+
+ k = new_key_item ();
+ k->kid[0] = keyid[0];
+ k->kid[1] = keyid[1];
+ k->next = user_utk_list;
+ user_utk_list = k;
+}
+
+void
+register_trusted_key( const char *string )
+{
+ KEYDB_SEARCH_DESC desc;
+
+ if (classify_user_id (string, &desc) != KEYDB_SEARCH_MODE_LONG_KID )
+ {
+ log_error(_("`%s' is not a valid long keyID\n"), string );
+ return;
+ }
+
+ register_trusted_keyid(desc.u.kid);
+}
+
+/*
+ * Helper to add a key to the global list of ultimately trusted keys.
+ * Retruns: true = inserted, false = already in in list.
+ */
+static int
+add_utk (u32 *kid)
+{
+ struct key_item *k;
+
+ for (k = utk_list; k; k = k->next)
+ {
+ if (k->kid[0] == kid[0] && k->kid[1] == kid[1])
+ {
+ return 0;
+ }
+ }
+
+ k = new_key_item ();
+ k->kid[0] = kid[0];
+ k->kid[1] = kid[1];
+ k->ownertrust = TRUST_ULTIMATE;
+ k->next = utk_list;
+ utk_list = k;
+ if( opt.verbose > 1 )
+ log_info(_("key %s: accepted as trusted key\n"), keystr(kid));
+ return 1;
+}
+
+
+/****************
+ * Verify that all our secret keys are usable and put them into the utk_list.
+ */
+static void
+verify_own_keys(void)
+{
+ TRUSTREC rec;
+ ulong recnum;
+ int rc;
+ struct key_item *k;
+
+ if (utk_list)
+ return;
+
+ /* scan the trustdb to find all ultimately trusted keys */
+ for (recnum=1; !tdbio_read_record (recnum, &rec, 0); recnum++ )
+ {
+ if ( rec.rectype == RECTYPE_TRUST
+ && (rec.r.trust.ownertrust & TRUST_MASK) == TRUST_ULTIMATE)
+ {
+ byte *fpr = rec.r.trust.fingerprint;
+ int fprlen;
+ u32 kid[2];
+
+ /* Problem: We do only use fingerprints in the trustdb but
+ * we need the keyID here to indetify the key; we can only
+ * use that ugly hack to distinguish between 16 and 20
+ * butes fpr - it does not work always so we better change
+ * the whole validation code to only work with
+ * fingerprints */
+ fprlen = (!fpr[16] && !fpr[17] && !fpr[18] && !fpr[19])? 16:20;
+ keyid_from_fingerprint (fpr, fprlen, kid);
+ if (!add_utk (kid))
+ log_info(_("key %s occurs more than once in the trustdb\n"),
+ keystr(kid));
+ }
+ }
+
+ /* Put any --trusted-key keys into the trustdb */
+ for (k = user_utk_list; k; k = k->next)
+ {
+ if ( add_utk (k->kid) )
+ { /* not yet in trustDB as ultimately trusted */
+ PKT_public_key pk;
+
+ memset (&pk, 0, sizeof pk);
+ rc = get_pubkey (&pk, k->kid);
+ if (rc)
+ log_info(_("key %s: no public key for trusted key - skipped\n"),
+ keystr(k->kid));
+ else
+ {
+ update_ownertrust (&pk,
+ ((get_ownertrust (&pk) & ~TRUST_MASK)
+ | TRUST_ULTIMATE ));
+ release_public_key_parts (&pk);
+ }
+
+ log_info (_("key %s marked as ultimately trusted\n"),keystr(k->kid));
+ }
+ }
+
+ /* release the helper table table */
+ release_key_items (user_utk_list);
+ user_utk_list = NULL;
+ return;
+}
+
+
+/*********************************************
+ *********** TrustDB stuff *******************
+ *********************************************/
+
+/*
+ * Read a record but die if it does not exist
+ */
+static void
+read_record (ulong recno, TRUSTREC *rec, int rectype )
+{
+ int rc = tdbio_read_record (recno, rec, rectype);
+ if (rc)
+ {
+ log_error(_("trust record %lu, req type %d: read failed: %s\n"),
+ recno, rec->rectype, g10_errstr(rc) );
+ tdbio_invalid();
+ }
+ if (rectype != rec->rectype)
+ {
+ log_error(_("trust record %lu is not of requested type %d\n"),
+ rec->recnum, rectype);
+ tdbio_invalid();
+ }
+}
+
+/*
+ * Write a record and die on error
+ */
+static void
+write_record (TRUSTREC *rec)
+{
+ int rc = tdbio_write_record (rec);
+ if (rc)
+ {
+ log_error(_("trust record %lu, type %d: write failed: %s\n"),
+ rec->recnum, rec->rectype, g10_errstr(rc) );
+ tdbio_invalid();
+ }
+}
+
+/*
+ * sync the TrustDb and die on error
+ */
+static void
+do_sync(void)
+{
+ int rc = tdbio_sync ();
+ if(rc)
+ {
+ log_error (_("trustdb: sync failed: %s\n"), g10_errstr(rc) );
+ g10_exit(2);
+ }
+}
+
+static const char *
+trust_model_string(void)
+{
+ switch(opt.trust_model)
+ {
+ case TM_CLASSIC: return "classic";
+ case TM_PGP: return "PGP";
+ case TM_EXTERNAL: return "external";
+ case TM_ALWAYS: return "always";
+ case TM_DIRECT: return "direct";
+ default: return "unknown";
+ }
+}
+
+/****************
+ * Perform some checks over the trustdb
+ * level 0: only open the db
+ * 1: used for initial program startup
+ */
+int
+setup_trustdb( int level, const char *dbname )
+{
+ /* just store the args */
+ if( trustdb_args.init )
+ return 0;
+ trustdb_args.level = level;
+ trustdb_args.dbname = dbname? xstrdup(dbname): NULL;
+ return 0;
+}
+
+void
+init_trustdb()
+{
+ int level = trustdb_args.level;
+ const char* dbname = trustdb_args.dbname;
+
+ if( trustdb_args.init )
+ return;
+
+ trustdb_args.init = 1;
+
+ if(level==0 || level==1)
+ {
+ int rc = tdbio_set_dbname( dbname, !!level );
+ if( rc )
+ log_fatal("can't init trustdb: %s\n", g10_errstr(rc) );
+ }
+ else
+ BUG();
+
+ if(opt.trust_model==TM_AUTO)
+ {
+ /* Try and set the trust model off of whatever the trustdb says
+ it is. */
+ opt.trust_model=tdbio_read_model();
+
+ /* Sanity check this ;) */
+ if(opt.trust_model!=TM_CLASSIC
+ && opt.trust_model!=TM_PGP
+ && opt.trust_model!=TM_EXTERNAL)
+ {
+ log_info(_("unable to use unknown trust model (%d) - "
+ "assuming %s trust model\n"),opt.trust_model,"PGP");
+ opt.trust_model=TM_PGP;
+ }
+
+ if(opt.verbose)
+ log_info(_("using %s trust model\n"),trust_model_string());
+ }
+
+ if(opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC)
+ {
+ /* Verify the list of ultimately trusted keys and move the
+ --trusted-keys list there as well. */
+ if(level==1)
+ verify_own_keys();
+
+ if(!tdbio_db_matches_options())
+ pending_check_trustdb=1;
+ }
+}
+
+
+/***********************************************
+ ************* Print helpers ****************
+ ***********************************************/
+
+/****************
+ * This function returns a letter for a trustvalue Trust flags
+ * are ignore.
+ */
+static int
+trust_letter (unsigned int value)
+{
+ switch( (value & TRUST_MASK) )
+ {
+ case TRUST_UNKNOWN: return '-';
+ case TRUST_EXPIRED: return 'e';
+ case TRUST_UNDEFINED: return 'q';
+ case TRUST_NEVER: return 'n';
+ case TRUST_MARGINAL: return 'm';
+ case TRUST_FULLY: return 'f';
+ case TRUST_ULTIMATE: return 'u';
+ default: return '?';
+ }
+}
+
+/* NOTE TO TRANSLATOR: these strings are similar to those in
+ trust_value_to_string(), but are a fixed length. This is needed to
+ make attractive information listings where columns line up
+ properly. The value "10" should be the length of the strings you
+ choose to translate to. This is the length in printable columns.
+ It gets passed to atoi() so everything after the number is
+ essentially a comment and need not be translated. Either key and
+ uid are both NULL, or neither are NULL. */
+const char *
+uid_trust_string_fixed(PKT_public_key *key,PKT_user_id *uid)
+{
+ if(!key && !uid)
+ return _("10 translator see trustdb.c:uid_trust_string_fixed");
+ else if(uid->is_revoked || (key && key->is_revoked))
+ return _("[ revoked]");
+ else if(uid->is_expired)
+ return _("[ expired]");
+ else if(key)
+ switch(get_validity(key,uid)&TRUST_MASK)
+ {
+ case TRUST_UNKNOWN: return _("[ unknown]");
+ case TRUST_EXPIRED: return _("[ expired]");
+ case TRUST_UNDEFINED: return _("[ undef ]");
+ case TRUST_MARGINAL: return _("[marginal]");
+ case TRUST_FULLY: return _("[ full ]");
+ case TRUST_ULTIMATE: return _("[ultimate]");
+ }
+
+ return "err";
+}
+
+/* The strings here are similar to those in
+ pkclist.c:do_edit_ownertrust() */
+const char *
+trust_value_to_string (unsigned int value)
+{
+ switch( (value & TRUST_MASK) )
+ {
+ case TRUST_UNKNOWN: return _("unknown");
+ case TRUST_EXPIRED: return _("expired");
+ case TRUST_UNDEFINED: return _("undefined");
+ case TRUST_NEVER: return _("never");
+ case TRUST_MARGINAL: return _("marginal");
+ case TRUST_FULLY: return _("full");
+ case TRUST_ULTIMATE: return _("ultimate");
+ default: return "err";
+ }
+}
+
+int
+string_to_trust_value (const char *str)
+{
+ if(ascii_strcasecmp(str,"undefined")==0)
+ return TRUST_UNDEFINED;
+ else if(ascii_strcasecmp(str,"never")==0)
+ return TRUST_NEVER;
+ else if(ascii_strcasecmp(str,"marginal")==0)
+ return TRUST_MARGINAL;
+ else if(ascii_strcasecmp(str,"full")==0)
+ return TRUST_FULLY;
+ else if(ascii_strcasecmp(str,"ultimate")==0)
+ return TRUST_ULTIMATE;
+ else
+ return -1;
+}
+
+/****************
+ * Recreate the WoT but do not ask for new ownertrusts. Special
+ * feature: In batch mode and without a forced yes, this is only done
+ * when a check is due. This can be used to run the check from a crontab
+ */
+void
+check_trustdb ()
+{
+ init_trustdb();
+ if(opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC)
+ {
+ if (opt.batch && !opt.answer_yes)
+ {
+ ulong scheduled;
+
+ scheduled = tdbio_read_nextcheck ();
+ if (!scheduled)
+ {
+ log_info (_("no need for a trustdb check\n"));
+ return;
+ }
+
+ if (scheduled > make_timestamp ())
+ {
+ log_info (_("next trustdb check due at %s\n"),
+ strtimestamp (scheduled));
+ return;
+ }
+ }
+
+ validate_keys (0);
+ }
+ else
+ log_info (_("no need for a trustdb check with `%s' trust model\n"),
+ trust_model_string());
+}
+
+
+/*
+ * Recreate the WoT.
+ */
+void
+update_trustdb()
+{
+ init_trustdb();
+ if(opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC)
+ validate_keys (1);
+ else
+ log_info (_("no need for a trustdb update with `%s' trust model\n"),
+ trust_model_string());
+}
+
+void
+revalidation_mark (void)
+{
+ init_trustdb();
+ /* we simply set the time for the next check to 1 (far back in 1970)
+ * so that a --update-trustdb will be scheduled */
+ if (tdbio_write_nextcheck (1))
+ do_sync ();
+ pending_check_trustdb = 1;
+}
+
+int
+trustdb_pending_check(void)
+{
+ return pending_check_trustdb;
+}
+
+/* If the trustdb is dirty, and we're interactive, update it.
+ Otherwise, check it unless no-auto-check-trustdb is set. */
+void
+trustdb_check_or_update(void)
+{
+ if(trustdb_pending_check())
+ {
+ if(opt.interactive)
+ update_trustdb();
+ else if(!opt.no_auto_check_trustdb)
+ check_trustdb();
+ }
+}
+
+void
+read_trust_options(byte *trust_model,ulong *created,ulong *nextcheck,
+ byte *marginals,byte *completes,byte *cert_depth)
+{
+ TRUSTREC opts;
+
+ init_trustdb();
+
+ read_record(0,&opts,RECTYPE_VER);
+
+ if(trust_model)
+ *trust_model=opts.r.ver.trust_model;
+ if(created)
+ *created=opts.r.ver.created;
+ if(nextcheck)
+ *nextcheck=opts.r.ver.nextcheck;
+ if(marginals)
+ *marginals=opts.r.ver.marginals;
+ if(completes)
+ *completes=opts.r.ver.completes;
+ if(cert_depth)
+ *cert_depth=opts.r.ver.cert_depth;
+}
+
+/***********************************************
+ *********** Ownertrust et al. ****************
+ ***********************************************/
+
+static int
+read_trust_record (PKT_public_key *pk, TRUSTREC *rec)
+{
+ int rc;
+
+ init_trustdb();
+ rc = tdbio_search_trust_bypk (pk, rec);
+ if (rc == -1)
+ return -1; /* no record yet */
+ if (rc)
+ {
+ log_error ("trustdb: searching trust record failed: %s\n",
+ g10_errstr (rc));
+ return rc;
+ }
+
+ if (rec->rectype != RECTYPE_TRUST)
+ {
+ log_error ("trustdb: record %lu is not a trust record\n",
+ rec->recnum);
+ return G10ERR_TRUSTDB;
+ }
+
+ return 0;
+}
+
+/****************
+ * Return the assigned ownertrust value for the given public key.
+ * The key should be the primary key.
+ */
+unsigned int
+get_ownertrust ( PKT_public_key *pk)
+{
+ TRUSTREC rec;
+ int rc;
+
+ rc = read_trust_record (pk, &rec);
+ if (rc == -1)
+ return TRUST_UNKNOWN; /* no record yet */
+ if (rc)
+ {
+ tdbio_invalid ();
+ return rc; /* actually never reached */
+ }
+
+ return rec.r.trust.ownertrust;
+}
+
+unsigned int
+get_min_ownertrust (PKT_public_key *pk)
+{
+ TRUSTREC rec;
+ int rc;
+
+ rc = read_trust_record (pk, &rec);
+ if (rc == -1)
+ return TRUST_UNKNOWN; /* no record yet */
+ if (rc)
+ {
+ tdbio_invalid ();
+ return rc; /* actually never reached */
+ }
+
+ return rec.r.trust.min_ownertrust;
+}
+
+/*
+ * Same as get_ownertrust but this takes the minimum ownertrust value
+ * into into account, and will bump up the value as needed.
+ */
+static int
+get_ownertrust_with_min (PKT_public_key *pk)
+{
+ unsigned int otrust,otrust_min;
+
+ otrust = (get_ownertrust (pk) & TRUST_MASK);
+ otrust_min = get_min_ownertrust (pk);
+ if(otrust<otrust_min)
+ {
+ /* If the trust that the user has set is less than the trust
+ that was calculated from a trust signature chain, use the
+ higher of the two. We do this here and not in
+ get_ownertrust since the underlying ownertrust should not
+ really be set - just the appearance of the ownertrust. */
+
+ otrust=otrust_min;
+ }
+
+ return otrust;
+}
+
+/*
+ * Same as get_ownertrust but return a trust letter instead of an
+ * value. This takes the minimum ownertrust value into account.
+ */
+int
+get_ownertrust_info (PKT_public_key *pk)
+{
+ return trust_letter(get_ownertrust_with_min(pk));
+}
+
+/*
+ * Same as get_ownertrust but return a trust string instead of an
+ * value. This takes the minimum ownertrust value into account.
+ */
+const char *
+get_ownertrust_string (PKT_public_key *pk)
+{
+ return trust_value_to_string(get_ownertrust_with_min(pk));
+}
+
+/*
+ * Set the trust value of the given public key to the new value.
+ * The key should be a primary one.
+ */
+void
+update_ownertrust (PKT_public_key *pk, unsigned int new_trust )
+{
+ TRUSTREC rec;
+ int rc;
+
+ rc = read_trust_record (pk, &rec);
+ if (!rc)
+ {
+ if (DBG_TRUST)
+ log_debug ("update ownertrust from %u to %u\n",
+ (unsigned int)rec.r.trust.ownertrust, new_trust );
+ if (rec.r.trust.ownertrust != new_trust)
+ {
+ rec.r.trust.ownertrust = new_trust;
+ write_record( &rec );
+ revalidation_mark ();
+ do_sync ();
+ }
+ }
+ else if (rc == -1)
+ { /* no record yet - create a new one */
+ size_t dummy;
+
+ if (DBG_TRUST)
+ log_debug ("insert ownertrust %u\n", new_trust );
+
+ memset (&rec, 0, sizeof rec);
+ rec.recnum = tdbio_new_recnum ();
+ rec.rectype = RECTYPE_TRUST;
+ fingerprint_from_pk (pk, rec.r.trust.fingerprint, &dummy);
+ rec.r.trust.ownertrust = new_trust;
+ write_record (&rec);
+ revalidation_mark ();
+ do_sync ();
+ rc = 0;
+ }
+ else
+ {
+ tdbio_invalid ();
+ }
+}
+
+static void
+update_min_ownertrust (u32 *kid, unsigned int new_trust )
+{
+ PKT_public_key *pk;
+ TRUSTREC rec;
+ int rc;
+
+ pk = xmalloc_clear (sizeof *pk);
+ rc = get_pubkey (pk, kid);
+ if (rc)
+ {
+ log_error(_("public key %s not found: %s\n"),keystr(kid),g10_errstr(rc));
+ return;
+ }
+
+ rc = read_trust_record (pk, &rec);
+ if (!rc)
+ {
+ if (DBG_TRUST)
+ log_debug ("key %08lX%08lX: update min_ownertrust from %u to %u\n",
+ (ulong)kid[0],(ulong)kid[1],
+ (unsigned int)rec.r.trust.min_ownertrust,
+ new_trust );
+ if (rec.r.trust.min_ownertrust != new_trust)
+ {
+ rec.r.trust.min_ownertrust = new_trust;
+ write_record( &rec );
+ revalidation_mark ();
+ do_sync ();
+ }
+ }
+ else if (rc == -1)
+ { /* no record yet - create a new one */
+ size_t dummy;
+
+ if (DBG_TRUST)
+ log_debug ("insert min_ownertrust %u\n", new_trust );
+
+ memset (&rec, 0, sizeof rec);
+ rec.recnum = tdbio_new_recnum ();
+ rec.rectype = RECTYPE_TRUST;
+ fingerprint_from_pk (pk, rec.r.trust.fingerprint, &dummy);
+ rec.r.trust.min_ownertrust = new_trust;
+ write_record (&rec);
+ revalidation_mark ();
+ do_sync ();
+ rc = 0;
+ }
+ else
+ {
+ tdbio_invalid ();
+ }
+}
+
+/* Clear the ownertrust and min_ownertrust values. Return true if a
+ change actually happened. */
+int
+clear_ownertrusts (PKT_public_key *pk)
+{
+ TRUSTREC rec;
+ int rc;
+
+ rc = read_trust_record (pk, &rec);
+ if (!rc)
+ {
+ if (DBG_TRUST)
+ {
+ log_debug ("clearing ownertrust (old value %u)\n",
+ (unsigned int)rec.r.trust.ownertrust);
+ log_debug ("clearing min_ownertrust (old value %u)\n",
+ (unsigned int)rec.r.trust.min_ownertrust);
+ }
+ if (rec.r.trust.ownertrust || rec.r.trust.min_ownertrust)
+ {
+ rec.r.trust.ownertrust = 0;
+ rec.r.trust.min_ownertrust = 0;
+ write_record( &rec );
+ revalidation_mark ();
+ do_sync ();
+ return 1;
+ }
+ }
+ else if (rc != -1)
+ {
+ tdbio_invalid ();
+ }
+ return 0;
+}
+
+/*
+ * Note: Caller has to do a sync
+ */
+static void
+update_validity (PKT_public_key *pk, PKT_user_id *uid,
+ int depth, int validity)
+{
+ TRUSTREC trec, vrec;
+ int rc;
+ ulong recno;
+
+ namehash_from_uid(uid);
+
+ rc = read_trust_record (pk, &trec);
+ if (rc && rc != -1)
+ {
+ tdbio_invalid ();
+ return;
+ }
+ if (rc == -1) /* no record yet - create a new one */
+ {
+ size_t dummy;
+
+ rc = 0;
+ memset (&trec, 0, sizeof trec);
+ trec.recnum = tdbio_new_recnum ();
+ trec.rectype = RECTYPE_TRUST;
+ fingerprint_from_pk (pk, trec.r.trust.fingerprint, &dummy);
+ trec.r.trust.ownertrust = 0;
+ }
+
+ /* locate an existing one */
+ recno = trec.r.trust.validlist;
+ while (recno)
+ {
+ read_record (recno, &vrec, RECTYPE_VALID);
+ if ( !memcmp (vrec.r.valid.namehash, uid->namehash, 20) )
+ break;
+ recno = vrec.r.valid.next;
+ }
+
+ if (!recno) /* insert a new validity record */
+ {
+ memset (&vrec, 0, sizeof vrec);
+ vrec.recnum = tdbio_new_recnum ();
+ vrec.rectype = RECTYPE_VALID;
+ memcpy (vrec.r.valid.namehash, uid->namehash, 20);
+ vrec.r.valid.next = trec.r.trust.validlist;
+ trec.r.trust.validlist = vrec.recnum;
+ }
+ vrec.r.valid.validity = validity;
+ vrec.r.valid.full_count = uid->help_full_count;
+ vrec.r.valid.marginal_count = uid->help_marginal_count;
+ write_record (&vrec);
+ trec.r.trust.depth = depth;
+ write_record (&trec);
+}
+
+
+/***********************************************
+ ********* Query trustdb values **************
+ ***********************************************/
+
+/* Return true if key is disabled */
+int
+cache_disabled_value(PKT_public_key *pk)
+{
+ int rc;
+ TRUSTREC trec;
+ int disabled=0;
+
+ if(pk->is_disabled)
+ return (pk->is_disabled==2);
+
+ init_trustdb();
+
+ rc = read_trust_record (pk, &trec);
+ if (rc && rc != -1)
+ {
+ tdbio_invalid ();
+ goto leave;
+ }
+ if (rc == -1) /* no record found, so assume not disabled */
+ goto leave;
+
+ if(trec.r.trust.ownertrust & TRUST_FLAG_DISABLED)
+ disabled=1;
+
+ /* Cache it for later so we don't need to look at the trustdb every
+ time */
+ if(disabled)
+ pk->is_disabled=2;
+ else
+ pk->is_disabled=1;
+
+ leave:
+ return disabled;
+}
+
+void
+check_trustdb_stale(void)
+{
+ static int did_nextcheck=0;
+
+ init_trustdb ();
+ if (!did_nextcheck
+ && (opt.trust_model==TM_PGP || opt.trust_model==TM_CLASSIC))
+ {
+ ulong scheduled;
+
+ did_nextcheck = 1;
+ scheduled = tdbio_read_nextcheck ();
+ if (scheduled && scheduled <= make_timestamp ())
+ {
+ if (opt.no_auto_check_trustdb)
+ {
+ pending_check_trustdb = 1;
+ log_info (_("please do a --check-trustdb\n"));
+ }
+ else
+ {
+ log_info (_("checking the trustdb\n"));
+ validate_keys (0);
+ }
+ }
+ }
+}
+
+/*
+ * Return the validity information for PK. If the namehash is not
+ * NULL, the validity of the corresponsing user ID is returned,
+ * otherwise, a reasonable value for the entire key is returned.
+ */
+unsigned int
+get_validity (PKT_public_key *pk, PKT_user_id *uid)
+{
+ TRUSTREC trec, vrec;
+ int rc;
+ ulong recno;
+ unsigned int validity;
+ u32 kid[2];
+ PKT_public_key *main_pk;
+
+ if(uid)
+ namehash_from_uid(uid);
+
+ init_trustdb ();
+ check_trustdb_stale();
+
+ keyid_from_pk (pk, kid);
+ if (pk->main_keyid[0] != kid[0] || pk->main_keyid[1] != kid[1])
+ { /* this is a subkey - get the mainkey */
+ main_pk = xmalloc_clear (sizeof *main_pk);
+ rc = get_pubkey (main_pk, pk->main_keyid);
+ if (rc)
+ {
+ char *tempkeystr=xstrdup(keystr(pk->main_keyid));
+ log_error ("error getting main key %s of subkey %s: %s\n",
+ tempkeystr, keystr(kid), g10_errstr(rc));
+ xfree(tempkeystr);
+ validity = TRUST_UNKNOWN;
+ goto leave;
+ }
+ }
+ else
+ main_pk = pk;
+
+ if(opt.trust_model==TM_DIRECT)
+ {
+ /* Note that this happens BEFORE any user ID stuff is checked.
+ The direct trust model applies to keys as a whole. */
+ validity=get_ownertrust(main_pk);
+ goto leave;
+ }
+
+ rc = read_trust_record (main_pk, &trec);
+ if (rc && rc != -1)
+ {
+ tdbio_invalid ();
+ return 0;
+ }
+ if (rc == -1) /* no record found */
+ {
+ validity = TRUST_UNKNOWN;
+ goto leave;
+ }
+
+ /* loop over all user IDs */
+ recno = trec.r.trust.validlist;
+ validity = 0;
+ while (recno)
+ {
+ read_record (recno, &vrec, RECTYPE_VALID);
+
+ if(uid)
+ {
+ /* If a user ID is given we return the validity for that
+ user ID ONLY. If the namehash is not found, then there
+ is no validity at all (i.e. the user ID wasn't
+ signed). */
+ if(memcmp(vrec.r.valid.namehash,uid->namehash,20)==0)
+ {
+ validity=(vrec.r.valid.validity & TRUST_MASK);
+ break;
+ }
+ }
+ else
+ {
+ /* If no namehash is given, we take the maximum validity
+ over all user IDs */
+ if ( validity < (vrec.r.valid.validity & TRUST_MASK) )
+ validity = (vrec.r.valid.validity & TRUST_MASK);
+ }
+
+ recno = vrec.r.valid.next;
+ }
+
+ if ( (trec.r.trust.ownertrust & TRUST_FLAG_DISABLED) )
+ {
+ validity |= TRUST_FLAG_DISABLED;
+ pk->is_disabled=2;
+ }
+ else
+ pk->is_disabled=1;
+
+ leave:
+ /* set some flags direct from the key */
+ if (main_pk->is_revoked)
+ validity |= TRUST_FLAG_REVOKED;
+ if (main_pk != pk && pk->is_revoked)
+ validity |= TRUST_FLAG_SUB_REVOKED;
+ /* Note: expiration is a trust value and not a flag - don't know why
+ * I initially designed it that way */
+ if (main_pk->has_expired || pk->has_expired)
+ validity = (validity & ~TRUST_MASK) | TRUST_EXPIRED;
+
+ if (pending_check_trustdb)
+ validity |= TRUST_FLAG_PENDING_CHECK;
+
+ if (main_pk != pk)
+ free_public_key (main_pk);
+ return validity;
+}
+
+int
+get_validity_info (PKT_public_key *pk, PKT_user_id *uid)
+{
+ int trustlevel;
+
+ trustlevel = get_validity (pk, uid);
+ if( trustlevel & TRUST_FLAG_REVOKED )
+ return 'r';
+ return trust_letter ( trustlevel );
+}
+
+const char *
+get_validity_string (PKT_public_key *pk, PKT_user_id *uid)
+{
+ int trustlevel;
+
+ trustlevel = get_validity (pk, uid);
+ if( trustlevel & TRUST_FLAG_REVOKED )
+ return _("revoked");
+ return trust_value_to_string(trustlevel);
+}
+
+static void
+get_validity_counts (PKT_public_key *pk, PKT_user_id *uid)
+{
+ TRUSTREC trec, vrec;
+ ulong recno;
+
+ if(pk==NULL || uid==NULL)
+ BUG();
+
+ namehash_from_uid(uid);
+
+ uid->help_marginal_count=uid->help_full_count=0;
+
+ init_trustdb ();
+
+ if(read_trust_record (pk, &trec)!=0)
+ return;
+
+ /* loop over all user IDs */
+ recno = trec.r.trust.validlist;
+ while (recno)
+ {
+ read_record (recno, &vrec, RECTYPE_VALID);
+
+ if(memcmp(vrec.r.valid.namehash,uid->namehash,20)==0)
+ {
+ uid->help_marginal_count=vrec.r.valid.marginal_count;
+ uid->help_full_count=vrec.r.valid.full_count;
+ /* printf("Fetched marginal %d, full %d\n",uid->help_marginal_count,uid->help_full_count); */
+ break;
+ }
+
+ recno = vrec.r.valid.next;
+ }
+}
+
+void
+list_trust_path( const char *username )
+{
+}
+
+/****************
+ * Enumerate all keys, which are needed to build all trust paths for
+ * the given key. This function does not return the key itself or
+ * the ultimate key (the last point in cerificate chain). Only
+ * certificate chains which ends up at an ultimately trusted key
+ * are listed. If ownertrust or validity is not NULL, the corresponding
+ * value for the returned LID is also returned in these variable(s).
+ *
+ * 1) create a void pointer and initialize it to NULL
+ * 2) pass this void pointer by reference to this function.
+ * Set lid to the key you want to enumerate and pass it by reference.
+ * 3) call this function as long as it does not return -1
+ * to indicate EOF. LID does contain the next key used to build the web
+ * 4) Always call this function a last time with LID set to NULL,
+ * so that it can free its context.
+ *
+ * Returns: -1 on EOF or the level of the returned LID
+ */
+int
+enum_cert_paths( void **context, ulong *lid,
+ unsigned *ownertrust, unsigned *validity )
+{
+ return -1;
+}
+
+
+/****************
+ * Print the current path
+ */
+void
+enum_cert_paths_print( void **context, FILE *fp,
+ int refresh, ulong selected_lid )
+{
+ return;
+}
+
+
+
+/****************************************
+ *********** NEW NEW NEW ****************
+ ****************************************/
+
+static int
+ask_ownertrust (u32 *kid,int minimum)
+{
+ PKT_public_key *pk;
+ int rc;
+ int ot;
+
+ pk = xmalloc_clear (sizeof *pk);
+ rc = get_pubkey (pk, kid);
+ if (rc)
+ {
+ log_error (_("public key %s not found: %s\n"),
+ keystr(kid), g10_errstr(rc) );
+ return TRUST_UNKNOWN;
+ }
+
+ if(opt.force_ownertrust)
+ {
+ log_info("force trust for key %s to %s\n",
+ keystr(kid),trust_value_to_string(opt.force_ownertrust));
+ update_ownertrust(pk,opt.force_ownertrust);
+ ot=opt.force_ownertrust;
+ }
+ else
+ {
+ ot=edit_ownertrust(pk,0);
+ if(ot>0)
+ ot = get_ownertrust (pk);
+ else if(ot==0)
+ ot = minimum?minimum:TRUST_UNDEFINED;
+ else
+ ot = -1; /* quit */
+ }
+
+ free_public_key( pk );
+
+ return ot;
+}
+
+
+static void
+mark_keyblock_seen (KeyHashTable tbl, KBNODE node)
+{
+ for ( ;node; node = node->next )
+ if (node->pkt->pkttype == PKT_PUBLIC_KEY
+ || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
+ {
+ u32 aki[2];
+
+ keyid_from_pk (node->pkt->pkt.public_key, aki);
+ add_key_hash_table (tbl, aki);
+ }
+}
+
+
+static void
+dump_key_array (int depth, struct key_array *keys)
+{
+ struct key_array *kar;
+
+ for (kar=keys; kar->keyblock; kar++)
+ {
+ KBNODE node = kar->keyblock;
+ u32 kid[2];
+
+ keyid_from_pk(node->pkt->pkt.public_key, kid);
+ printf ("%d:%08lX%08lX:K::%c::::\n",
+ depth, (ulong)kid[0], (ulong)kid[1], '?');
+
+ for (; node; node = node->next)
+ {
+ if (node->pkt->pkttype == PKT_USER_ID)
+ {
+ int len = node->pkt->pkt.user_id->len;
+
+ if (len > 30)
+ len = 30;
+ printf ("%d:%08lX%08lX:U:::%c:::",
+ depth, (ulong)kid[0], (ulong)kid[1],
+ (node->flag & 4)? 'f':
+ (node->flag & 2)? 'm':
+ (node->flag & 1)? 'q':'-');
+ print_string (stdout, node->pkt->pkt.user_id->name, len, ':');
+ putchar (':');
+ putchar ('\n');
+ }
+ }
+ }
+}
+
+
+static void
+store_validation_status (int depth, KBNODE keyblock, KeyHashTable stored)
+{
+ KBNODE node;
+ int status;
+ int any = 0;
+
+ for (node=keyblock; node; node = node->next)
+ {
+ if (node->pkt->pkttype == PKT_USER_ID)
+ {
+ PKT_user_id *uid = node->pkt->pkt.user_id;
+ if (node->flag & 4)
+ status = TRUST_FULLY;
+ else if (node->flag & 2)
+ status = TRUST_MARGINAL;
+ else if (node->flag & 1)
+ status = TRUST_UNDEFINED;
+ else
+ status = 0;
+
+ if (status)
+ {
+ update_validity (keyblock->pkt->pkt.public_key,
+ uid, depth, status);
+
+ mark_keyblock_seen(stored,keyblock);
+
+ any = 1;
+ }
+ }
+ }
+
+ if (any)
+ do_sync ();
+}
+
+/*
+ * check whether the signature sig is in the klist k
+ */
+static struct key_item *
+is_in_klist (struct key_item *k, PKT_signature *sig)
+{
+ for (; k; k = k->next)
+ {
+ if (k->kid[0] == sig->keyid[0] && k->kid[1] == sig->keyid[1])
+ return k;
+ }
+ return NULL;
+}
+
+/*
+ * Mark the signature of the given UID which are used to certify it.
+ * To do this, we first revmove all signatures which are not valid and
+ * from the remain ones we look for the latest one. If this is not a
+ * certification revocation signature we mark the signature by setting
+ * node flag bit 8. Revocations are marked with flag 11, and sigs
+ * from unavailable keys are marked with flag 12. Note that flag bits
+ * 9 and 10 are used for internal purposes.
+ */
+static void
+mark_usable_uid_certs (KBNODE keyblock, KBNODE uidnode,
+ u32 *main_kid, struct key_item *klist,
+ u32 curtime, u32 *next_expire)
+{
+ KBNODE node;
+ PKT_signature *sig;
+
+ /* first check all signatures */
+ for (node=uidnode->next; node; node = node->next)
+ {
+ int rc;
+
+ node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12);
+ if (node->pkt->pkttype == PKT_USER_ID
+ || node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
+ break; /* ready */
+ if (node->pkt->pkttype != PKT_SIGNATURE)
+ continue;
+ sig = node->pkt->pkt.signature;
+ if (main_kid
+ && sig->keyid[0] == main_kid[0] && sig->keyid[1] == main_kid[1])
+ continue; /* ignore self-signatures if we pass in a main_kid */
+ if (!IS_UID_SIG(sig) && !IS_UID_REV(sig))
+ continue; /* we only look at these signature classes */
+ if(sig->sig_class>=0x11 && sig->sig_class<=0x13 &&
+ sig->sig_class-0x10<opt.min_cert_level)
+ continue; /* treat anything under our min_cert_level as an
+ invalid signature */
+ if (klist && !is_in_klist (klist, sig))
+ continue; /* no need to check it then */
+ if ((rc=check_key_signature (keyblock, node, NULL)))
+ {
+ /* we ignore anything that won't verify, but tag the
+ no_pubkey case */
+ if(rc==G10ERR_NO_PUBKEY)
+ node->flag |= 1<<12;
+ continue;
+ }
+ node->flag |= 1<<9;
+ }
+ /* reset the remaining flags */
+ for (; node; node = node->next)
+ node->flag &= ~(1<<8 | 1<<9 | 1<<10 | 1<<11 | 1<<12);
+
+ /* kbnode flag usage: bit 9 is here set for signatures to consider,
+ * bit 10 will be set by the loop to keep track of keyIDs already
+ * processed, bit 8 will be set for the usable signatures, and bit
+ * 11 will be set for usable revocations. */
+
+ /* for each cert figure out the latest valid one */
+ for (node=uidnode->next; node; node = node->next)
+ {
+ KBNODE n, signode;
+ u32 kid[2];
+ u32 sigdate;
+
+ if (node->pkt->pkttype == PKT_PUBLIC_SUBKEY)
+ break;
+ if ( !(node->flag & (1<<9)) )
+ continue; /* not a node to look at */
+ if ( (node->flag & (1<<10)) )
+ continue; /* signature with a keyID already processed */
+ node->flag |= (1<<10); /* mark this node as processed */
+ sig = node->pkt->pkt.signature;
+ signode = node;
+ sigdate = sig->timestamp;
+ kid[0] = sig->keyid[0]; kid[1] = sig->keyid[1];
+
+ /* Now find the latest and greatest signature */
+ for (n=uidnode->next; n; n = n->next)
+ {
+ if (n->pkt->pkttype == PKT_PUBLIC_SUBKEY)
+ break;
+ if ( !(n->flag & (1<<9)) )
+ continue;
+ if ( (n->flag & (1<<10)) )
+ continue; /* shortcut already processed signatures */
+ sig = n->pkt->pkt.signature;
+ if (kid[0] != sig->keyid[0] || kid[1] != sig->keyid[1])
+ continue;
+ n->flag |= (1<<10); /* mark this node as processed */
+
+ /* If signode is nonrevocable and unexpired and n isn't,
+ then take signode (skip). It doesn't matter which is
+ older: if signode was older then we don't want to take n
+ as signode is nonrevocable. If n was older then we're
+ automatically fine. */
+
+ if(((IS_UID_SIG(signode->pkt->pkt.signature) &&
+ !signode->pkt->pkt.signature->flags.revocable &&
+ (signode->pkt->pkt.signature->expiredate==0 ||
+ signode->pkt->pkt.signature->expiredate>curtime))) &&
+ (!(IS_UID_SIG(n->pkt->pkt.signature) &&
+ !n->pkt->pkt.signature->flags.revocable &&
+ (n->pkt->pkt.signature->expiredate==0 ||
+ n->pkt->pkt.signature->expiredate>curtime))))
+ continue;
+
+ /* If n is nonrevocable and unexpired and signode isn't,
+ then take n. Again, it doesn't matter which is older: if
+ n was older then we don't want to take signode as n is
+ nonrevocable. If signode was older then we're
+ automatically fine. */
+
+ if((!(IS_UID_SIG(signode->pkt->pkt.signature) &&
+ !signode->pkt->pkt.signature->flags.revocable &&
+ (signode->pkt->pkt.signature->expiredate==0 ||
+ signode->pkt->pkt.signature->expiredate>curtime))) &&
+ ((IS_UID_SIG(n->pkt->pkt.signature) &&
+ !n->pkt->pkt.signature->flags.revocable &&
+ (n->pkt->pkt.signature->expiredate==0 ||
+ n->pkt->pkt.signature->expiredate>curtime))))
+ {
+ signode = n;
+ sigdate = sig->timestamp;
+ continue;
+ }
+
+ /* At this point, if it's newer, it goes in as the only
+ remaining possibilities are signode and n are both either
+ revocable or expired or both nonrevocable and unexpired.
+ If the timestamps are equal take the later ordered
+ packet, presuming that the key packets are hopefully in
+ their original order. */
+
+ if (sig->timestamp >= sigdate)
+ {
+ signode = n;
+ sigdate = sig->timestamp;
+ }
+ }
+
+ sig = signode->pkt->pkt.signature;
+ if (IS_UID_SIG (sig))
+ { /* this seems to be a usable one which is not revoked.
+ * Just need to check whether there is an expiration time,
+ * We do the expired certification after finding a suitable
+ * certification, the assumption is that a signator does not
+ * want that after the expiration of his certificate the
+ * system falls back to an older certification which has a
+ * different expiration time */
+ const byte *p;
+ u32 expire;
+
+ p = parse_sig_subpkt (sig->hashed, SIGSUBPKT_SIG_EXPIRE, NULL );
+ expire = p? sig->timestamp + buffer_to_u32(p) : 0;
+
+ if (expire==0 || expire > curtime )
+ {
+ signode->flag |= (1<<8); /* yeah, found a good cert */
+ if (next_expire && expire && expire < *next_expire)
+ *next_expire = expire;
+ }
+ }
+ else
+ signode->flag |= (1<<11);
+ }
+}
+
+static int
+clean_sigs_from_uid(KBNODE keyblock,KBNODE uidnode,int noisy,int self_only)
+{
+ int deleted=0;
+ KBNODE node;
+ u32 keyid[2];
+
+ assert(keyblock->pkt->pkttype==PKT_PUBLIC_KEY);
+
+ keyid_from_pk(keyblock->pkt->pkt.public_key,keyid);
+
+ /* Passing in a 0 for current time here means that we'll never weed
+ out an expired sig. This is correct behavior since we want to
+ keep the most recent expired sig in a series. */
+ mark_usable_uid_certs(keyblock,uidnode,NULL,NULL,0,NULL);
+
+ /* What we want to do here is remove signatures that are not
+ considered as part of the trust calculations. Thus, all invalid
+ signatures are out, as are any signatures that aren't the last of
+ a series of uid sigs or revocations It breaks down like this:
+ coming out of mark_usable_uid_certs, if a sig is unflagged, it is
+ not even a candidate. If a sig has flag 9 or 10, that means it
+ was selected as a candidate and vetted. If a sig has flag 8 it
+ is a usable signature. If a sig has flag 11 it is a usable
+ revocation. If a sig has flag 12 it was issued by an unavailable
+ key. "Usable" here means the most recent valid
+ signature/revocation in a series from a particular signer.
+
+ Delete everything that isn't a usable uid sig (which might be
+ expired), a usable revocation, or a sig from an unavailable
+ key. */
+
+ for(node=uidnode->next;
+ node && node->pkt->pkttype==PKT_SIGNATURE;
+ node=node->next)
+ {
+ int keep=self_only?(node->pkt->pkt.signature->keyid[0]==keyid[0]
+ && node->pkt->pkt.signature->keyid[1]==keyid[1]):1;
+
+ /* Keep usable uid sigs ... */
+ if((node->flag & (1<<8)) && keep)
+ continue;
+
+ /* ... and usable revocations... */
+ if((node->flag & (1<<11)) && keep)
+ continue;
+
+ /* ... and sigs from unavailable keys. */
+ /* disabled for now since more people seem to want sigs from
+ unavailable keys removed altogether. */
+ /*
+ if(node->flag & (1<<12))
+ continue;
+ */
+
+ /* Everything else we delete */
+
+ /* At this point, if 12 is set, the signing key was unavailable.
+ If 9 or 10 is set, it's superceded. Otherwise, it's
+ invalid. */
+
+ if(noisy)
+ log_info("removing signature from key %s on user ID \"%s\": %s\n",
+ keystr(node->pkt->pkt.signature->keyid),
+ uidnode->pkt->pkt.user_id->name,
+ node->flag&(1<<12)?"key unavailable":
+ node->flag&(1<<9)?"signature superceded":"invalid signature");
+
+ delete_kbnode(node);
+ deleted++;
+ }
+
+ return deleted;
+}
+
+/* This is substantially easier than clean_sigs_from_uid since we just
+ have to establish if the uid has a valid self-sig, is not revoked,
+ and is not expired. Note that this does not take into account
+ whether the uid has a trust path to it - just whether the keyholder
+ themselves has certified the uid. Returns true if the uid was
+ compacted. To "compact" a user ID, we simply remove ALL signatures
+ except the self-sig that caused the user ID to be remove-worthy.
+ We don't actually remove the user ID packet itself since it might
+ be ressurected in a later merge. Note that this function requires
+ that the caller has already done a merge_keys_and_selfsig().
+
+ TODO: change the import code to allow importing a uid with only a
+ revocation if the uid already exists on the keyring. */
+
+static int
+clean_uid_from_key(KBNODE keyblock,KBNODE uidnode,int noisy)
+{
+ KBNODE node;
+ PKT_user_id *uid=uidnode->pkt->pkt.user_id;
+ int deleted=0;
+
+ assert(keyblock->pkt->pkttype==PKT_PUBLIC_KEY);
+ assert(uidnode->pkt->pkttype==PKT_USER_ID);
+
+ /* Skip valid user IDs, compacted user IDs, and non-self-signed user
+ IDs if --allow-non-selfsigned-uid is set. */
+ if(uid->created || uid->flags.compacted
+ || (!uid->is_expired && !uid->is_revoked
+ && opt.allow_non_selfsigned_uid))
+ return 0;
+
+ for(node=uidnode->next;
+ node && node->pkt->pkttype==PKT_SIGNATURE;
+ node=node->next)
+ if(!node->pkt->pkt.signature->flags.chosen_selfsig)
+ {
+ delete_kbnode(node);
+ deleted=1;
+ uidnode->pkt->pkt.user_id->flags.compacted=1;
+ }
+
+ if(noisy)
+ {
+ const char *reason;
+ char *user=utf8_to_native(uid->name,uid->len,0);
+
+ if(uid->is_revoked)
+ reason=_("revoked");
+ else if(uid->is_expired)
+ reason=_("expired");
+ else
+ reason=_("invalid");
+
+ log_info("compacting user ID \"%s\" on key %s: %s\n",
+ user,keystr_from_pk(keyblock->pkt->pkt.public_key),
+ reason);
+
+ xfree(user);
+ }
+
+ return deleted;
+}
+
+/* Needs to be called after a merge_keys_and_selfsig() */
+void
+clean_one_uid(KBNODE keyblock,KBNODE uidnode,int noisy,int self_only,
+ int *uids_cleaned,int *sigs_cleaned)
+{
+ int dummy;
+
+ assert(keyblock->pkt->pkttype==PKT_PUBLIC_KEY);
+ assert(uidnode->pkt->pkttype==PKT_USER_ID);
+
+ if(!uids_cleaned)
+ uids_cleaned=&dummy;
+
+ if(!sigs_cleaned)
+ sigs_cleaned=&dummy;
+
+ /* Do clean_uid_from_key first since if it fires off, we don't
+ have to bother with the other */
+ *uids_cleaned+=clean_uid_from_key(keyblock,uidnode,noisy);
+ if(!uidnode->pkt->pkt.user_id->flags.compacted)
+ *sigs_cleaned+=clean_sigs_from_uid(keyblock,uidnode,noisy,self_only);
+}
+
+void
+clean_key(KBNODE keyblock,int noisy,int self_only,
+ int *uids_cleaned,int *sigs_cleaned)
+{
+ KBNODE uidnode;
+
+ merge_keys_and_selfsig(keyblock);
+
+ for(uidnode=keyblock->next;
+ uidnode && uidnode->pkt->pkttype!=PKT_PUBLIC_SUBKEY;
+ uidnode=uidnode->next)
+ if(uidnode->pkt->pkttype==PKT_USER_ID)
+ clean_one_uid(keyblock,uidnode,noisy,self_only,
+ uids_cleaned,sigs_cleaned);
+}
+
+/* Used by validate_one_keyblock to confirm a regexp within a trust
+ signature. Returns 1 for match, and 0 for no match or regex
+ error. */
+static int
+check_regexp(const char *expr,const char *string)
+{
+#ifdef DISABLE_REGEX
+ /* When DISABLE_REGEX is defined, assume all regexps do not
+ match. */
+ return 0;
+#elif defined(__riscos__)
+ return riscos_check_regexp(expr, string, DBG_TRUST);
+#else
+ int ret;
+ regex_t pat;
+
+ if(regcomp(&pat,expr,REG_ICASE|REG_NOSUB|REG_EXTENDED)!=0)
+ return 0;
+
+ ret=regexec(&pat,string,0,NULL,0);
+
+ regfree(&pat);
+
+ if(DBG_TRUST)
+ log_debug("regexp `%s' on `%s': %s\n",expr,string,ret==0?"YES":"NO");
+
+ return (ret==0);
+#endif
+}
+
+/*
+ * Return true if the key is signed by one of the keys in the given
+ * key ID list. User IDs with a valid signature are marked by node
+ * flags as follows:
+ * flag bit 0: There is at least one signature
+ * 1: There is marginal confidence that this is a legitimate uid
+ * 2: There is full confidence that this is a legitimate uid.
+ * 8: Used for internal purposes.
+ * 9: Ditto (in mark_usable_uid_certs())
+ * 10: Ditto (ditto)
+ * This function assumes that all kbnode flags are cleared on entry.
+ */
+static int
+validate_one_keyblock (KBNODE kb, struct key_item *klist,
+ u32 curtime, u32 *next_expire)
+{
+ struct key_item *kr;
+ KBNODE node, uidnode=NULL;
+ PKT_user_id *uid=NULL;
+ PKT_public_key *pk = kb->pkt->pkt.public_key;
+ u32 main_kid[2];
+ int issigned=0, any_signed = 0;
+
+ keyid_from_pk(pk, main_kid);
+ for (node=kb; node; node = node->next)
+ {
+ /* A bit of discussion here: is it better for the web of trust
+ to be built among only self-signed uids? On the one hand, a
+ self-signed uid is a statement that the key owner definitely
+ intended that uid to be there, but on the other hand, a
+ signed (but not self-signed) uid does carry trust, of a sort,
+ even if it is a statement being made by people other than the
+ key owner "through" the uids on the key owner's key. I'm
+ going with the latter. However, if the user ID was
+ explicitly revoked, or passively allowed to expire, that
+ should stop validity through the user ID until it is
+ resigned. -dshaw */
+
+ if (node->pkt->pkttype == PKT_USER_ID
+ && !node->pkt->pkt.user_id->is_revoked
+ && !node->pkt->pkt.user_id->is_expired)
+ {
+ if (uidnode && issigned)
+ {
+ if (uid->help_full_count >= opt.completes_needed
+ || uid->help_marginal_count >= opt.marginals_needed )
+ uidnode->flag |= 4;
+ else if (uid->help_full_count || uid->help_marginal_count)
+ uidnode->flag |= 2;
+ uidnode->flag |= 1;
+ any_signed = 1;
+ }
+ uidnode = node;
+ uid=uidnode->pkt->pkt.user_id;
+
+ /* If the selfsig is going to expire... */
+ if(uid->expiredate && uid->expiredate<*next_expire)
+ *next_expire = uid->expiredate;
+
+ issigned = 0;
+ get_validity_counts(pk,uid);
+ mark_usable_uid_certs (kb, uidnode, main_kid, klist,
+ curtime, next_expire);
+ }
+ else if (node->pkt->pkttype == PKT_SIGNATURE
+ && (node->flag & (1<<8)) && uid)
+ {
+ /* Note that we are only seeing unrevoked sigs here */
+ PKT_signature *sig = node->pkt->pkt.signature;
+
+ kr = is_in_klist (klist, sig);
+ /* If the trust_regexp does not match, it's as if the sig
+ did not exist. This is safe for non-trust sigs as well
+ since we don't accept a regexp on the sig unless it's a
+ trust sig. */
+ if (kr && (kr->trust_regexp==NULL || opt.trust_model!=TM_PGP ||
+ (uidnode && check_regexp(kr->trust_regexp,
+ uidnode->pkt->pkt.user_id->name))))
+ {
+ if(DBG_TRUST && opt.trust_model==TM_PGP && sig->trust_depth)
+ log_debug("trust sig on %s, sig depth is %d, kr depth is %d\n",
+ uidnode->pkt->pkt.user_id->name,sig->trust_depth,
+ kr->trust_depth);
+
+ /* Are we part of a trust sig chain? We always favor
+ the latest trust sig, rather than the greater or
+ lesser trust sig or value. I could make a decent
+ argument for any of these cases, but this seems to be
+ what PGP does, and I'd like to be compatible. -dms */
+ if(opt.trust_model==TM_PGP && sig->trust_depth
+ && pk->trust_timestamp<=sig->timestamp
+ && (sig->trust_depth<=kr->trust_depth
+ || kr->ownertrust==TRUST_ULTIMATE))
+ {
+ /* If we got here, we know that:
+
+ this is a trust sig.
+
+ it's a newer trust sig than any previous trust
+ sig on this key (not uid).
+
+ it is legal in that it was either generated by an
+ ultimate key, or a key that was part of a trust
+ chain, and the depth does not violate the
+ original trust sig.
+
+ if there is a regexp attached, it matched
+ successfully.
+ */
+
+ if(DBG_TRUST)
+ log_debug("replacing trust value %d with %d and "
+ "depth %d with %d\n",
+ pk->trust_value,sig->trust_value,
+ pk->trust_depth,sig->trust_depth);
+
+ pk->trust_value=sig->trust_value;
+ pk->trust_depth=sig->trust_depth-1;
+
+ /* If the trust sig contains a regexp, record it
+ on the pk for the next round. */
+ if(sig->trust_regexp)
+ pk->trust_regexp=sig->trust_regexp;
+ }
+
+ if (kr->ownertrust == TRUST_ULTIMATE)
+ uid->help_full_count = opt.completes_needed;
+ else if (kr->ownertrust == TRUST_FULLY)
+ uid->help_full_count++;
+ else if (kr->ownertrust == TRUST_MARGINAL)
+ uid->help_marginal_count++;
+ issigned = 1;
+ }
+ }
+ }
+
+ if (uidnode && issigned)
+ {
+ if (uid->help_full_count >= opt.completes_needed
+ || uid->help_marginal_count >= opt.marginals_needed )
+ uidnode->flag |= 4;
+ else if (uid->help_full_count || uid->help_marginal_count)
+ uidnode->flag |= 2;
+ uidnode->flag |= 1;
+ any_signed = 1;
+ }
+
+ return any_signed;
+}
+
+
+static int
+search_skipfnc (void *opaque, u32 *kid, PKT_user_id *dummy)
+{
+ return test_key_hash_table ((KeyHashTable)opaque, kid);
+}
+
+
+/*
+ * Scan all keys and return a key_array of all suitable keys from
+ * kllist. The caller has to pass keydb handle so that we don't use
+ * to create our own. Returns either a key_array or NULL in case of
+ * an error. No results found are indicated by an empty array.
+ * Caller hast to release the returned array.
+ */
+static struct key_array *
+validate_key_list (KEYDB_HANDLE hd, KeyHashTable full_trust,
+ struct key_item *klist, u32 curtime, u32 *next_expire)
+{
+ KBNODE keyblock = NULL;
+ struct key_array *keys = NULL;
+ size_t nkeys, maxkeys;
+ int rc;
+ KEYDB_SEARCH_DESC desc;
+
+ maxkeys = 1000;
+ keys = xmalloc ((maxkeys+1) * sizeof *keys);
+ nkeys = 0;
+
+ rc = keydb_search_reset (hd);
+ if (rc)
+ {
+ log_error ("keydb_search_reset failed: %s\n", g10_errstr(rc));
+ xfree (keys);
+ return NULL;
+ }
+
+ memset (&desc, 0, sizeof desc);
+ desc.mode = KEYDB_SEARCH_MODE_FIRST;
+ desc.skipfnc = search_skipfnc;
+ desc.skipfncvalue = full_trust;
+ rc = keydb_search (hd, &desc, 1);
+ if (rc == -1)
+ {
+ keys[nkeys].keyblock = NULL;
+ return keys;
+ }
+ if (rc)
+ {
+ log_error ("keydb_search_first failed: %s\n", g10_errstr(rc));
+ xfree (keys);
+ return NULL;
+ }
+
+ desc.mode = KEYDB_SEARCH_MODE_NEXT; /* change mode */
+ do
+ {
+ PKT_public_key *pk;
+
+ rc = keydb_get_keyblock (hd, &keyblock);
+ if (rc)
+ {
+ log_error ("keydb_get_keyblock failed: %s\n", g10_errstr(rc));
+ xfree (keys);
+ return NULL;
+ }
+
+ if ( keyblock->pkt->pkttype != PKT_PUBLIC_KEY)
+ {
+ log_debug ("ooops: invalid pkttype %d encountered\n",
+ keyblock->pkt->pkttype);
+ dump_kbnode (keyblock);
+ release_kbnode(keyblock);
+ continue;
+ }
+
+ /* prepare the keyblock for further processing */
+ merge_keys_and_selfsig (keyblock);
+ clear_kbnode_flags (keyblock);
+ pk = keyblock->pkt->pkt.public_key;
+ if (pk->has_expired || pk->is_revoked)
+ {
+ /* it does not make sense to look further at those keys */
+ mark_keyblock_seen (full_trust, keyblock);
+ }
+ else if (validate_one_keyblock (keyblock, klist, curtime, next_expire))
+ {
+ KBNODE node;
+
+ if (pk->expiredate && pk->expiredate >= curtime
+ && pk->expiredate < *next_expire)
+ *next_expire = pk->expiredate;
+
+ if (nkeys == maxkeys) {
+ maxkeys += 1000;
+ keys = xrealloc (keys, (maxkeys+1) * sizeof *keys);
+ }
+ keys[nkeys++].keyblock = keyblock;
+
+ /* Optimization - if all uids are fully trusted, then we
+ never need to consider this key as a candidate again. */
+
+ for (node=keyblock; node; node = node->next)
+ if (node->pkt->pkttype == PKT_USER_ID && !(node->flag & 4))
+ break;
+
+ if(node==NULL)
+ mark_keyblock_seen (full_trust, keyblock);
+
+ keyblock = NULL;
+ }
+
+ release_kbnode (keyblock);
+ keyblock = NULL;
+ }
+ while ( !(rc = keydb_search (hd, &desc, 1)) );
+ if (rc && rc != -1)
+ {
+ log_error ("keydb_search_next failed: %s\n", g10_errstr(rc));
+ xfree (keys);
+ return NULL;
+ }
+
+ keys[nkeys].keyblock = NULL;
+ return keys;
+}
+
+/* Caller must sync */
+static void
+reset_trust_records(void)
+{
+ TRUSTREC rec;
+ ulong recnum;
+ int count = 0, nreset = 0;
+
+ for (recnum=1; !tdbio_read_record (recnum, &rec, 0); recnum++ )
+ {
+ if(rec.rectype==RECTYPE_TRUST)
+ {
+ count++;
+ if(rec.r.trust.min_ownertrust)
+ {
+ rec.r.trust.min_ownertrust=0;
+ write_record(&rec);
+ }
+
+ }
+ else if(rec.rectype==RECTYPE_VALID
+ && ((rec.r.valid.validity&TRUST_MASK)
+ || rec.r.valid.marginal_count
+ || rec.r.valid.full_count))
+ {
+ rec.r.valid.validity &= ~TRUST_MASK;
+ rec.r.valid.marginal_count=rec.r.valid.full_count=0;
+ nreset++;
+ write_record(&rec);
+ }
+
+ }
+
+ if (opt.verbose)
+ log_info (_("%d keys processed (%d validity counts cleared)\n"),
+ count, nreset);
+}
+
+/*
+ * Run the key validation procedure.
+ *
+ * This works this way:
+ * Step 1: Find all ultimately trusted keys (UTK).
+ * mark them all as seen and put them into klist.
+ * Step 2: loop max_cert_times
+ * Step 3: if OWNERTRUST of any key in klist is undefined
+ * ask user to assign ownertrust
+ * Step 4: Loop over all keys in the keyDB which are not marked seen
+ * Step 5: if key is revoked or expired
+ * mark key as seen
+ * continue loop at Step 4
+ * Step 6: For each user ID of that key signed by a key in klist
+ * Calculate validity by counting trusted signatures.
+ * Set validity of user ID
+ * Step 7: If any signed user ID was found
+ * mark key as seen
+ * End Loop
+ * Step 8: Build a new klist from all fully trusted keys from step 6
+ * End Loop
+ * Ready
+ *
+ */
+static int
+validate_keys (int interactive)
+{
+ int rc = 0;
+ int quit=0;
+ struct key_item *klist = NULL;
+ struct key_item *k;
+ struct key_array *keys = NULL;
+ struct key_array *kar;
+ KEYDB_HANDLE kdb = NULL;
+ KBNODE node;
+ int depth;
+ int ot_unknown, ot_undefined, ot_never, ot_marginal, ot_full, ot_ultimate;
+ KeyHashTable stored,used,full_trust;
+ u32 start_time, next_expire;
+
+ /* Make sure we have all sigs cached. TODO: This is going to
+ require some architectual re-thinking, as it is agonizingly slow.
+ Perhaps combine this with reset_trust_records(), or only check
+ the caches on keys that are actually involved in the web of
+ trust. */
+ keydb_rebuild_caches(0);
+
+ start_time = make_timestamp ();
+ next_expire = 0xffffffff; /* set next expire to the year 2106 */
+ stored = new_key_hash_table ();
+ used = new_key_hash_table ();
+ full_trust = new_key_hash_table ();
+
+ kdb = keydb_new (0);
+ reset_trust_records();
+
+ /* Fixme: Instead of always building a UTK list, we could just build it
+ * here when needed */
+ if (!utk_list)
+ {
+ if (!opt.quiet)
+ log_info (_("no ultimately trusted keys found\n"));
+ goto leave;
+ }
+
+ /* mark all UTKs as used and fully_trusted and set validity to
+ ultimate */
+ for (k=utk_list; k; k = k->next)
+ {
+ KBNODE keyblock;
+ PKT_public_key *pk;
+
+ keyblock = get_pubkeyblock (k->kid);
+ if (!keyblock)
+ {
+ log_error (_("public key of ultimately"
+ " trusted key %s not found\n"), keystr(k->kid));
+ continue;
+ }
+ mark_keyblock_seen (used, keyblock);
+ mark_keyblock_seen (stored, keyblock);
+ mark_keyblock_seen (full_trust, keyblock);
+ pk = keyblock->pkt->pkt.public_key;
+ for (node=keyblock; node; node = node->next)
+ {
+ if (node->pkt->pkttype == PKT_USER_ID)
+ update_validity (pk, node->pkt->pkt.user_id, 0, TRUST_ULTIMATE);
+ }
+ if ( pk->expiredate && pk->expiredate >= start_time
+ && pk->expiredate < next_expire)
+ next_expire = pk->expiredate;
+
+ release_kbnode (keyblock);
+ do_sync ();
+ }
+
+ klist = utk_list;
+
+ log_info(_("%d marginal(s) needed, %d complete(s) needed, %s trust model\n"),
+ opt.marginals_needed,opt.completes_needed,trust_model_string());
+
+ for (depth=0; depth < opt.max_cert_depth; depth++)
+ {
+ int valids=0,key_count;
+ /* See whether we should assign ownertrust values to the keys in
+ klist. */
+ ot_unknown = ot_undefined = ot_never = 0;
+ ot_marginal = ot_full = ot_ultimate = 0;
+ for (k=klist; k; k = k->next)
+ {
+ int min=0;
+
+ /* 120 and 60 are as per RFC2440 */
+ if(k->trust_value>=120)
+ min=TRUST_FULLY;
+ else if(k->trust_value>=60)
+ min=TRUST_MARGINAL;
+
+ if(min!=k->min_ownertrust)
+ update_min_ownertrust(k->kid,min);
+
+ if (interactive && k->ownertrust == TRUST_UNKNOWN)
+ {
+ k->ownertrust = ask_ownertrust (k->kid,min);
+
+ if (k->ownertrust == -1)
+ {
+ quit=1;
+ goto leave;
+ }
+ }
+
+ /* This can happen during transition from an old trustdb
+ before trust sigs. It can also happen if a user uses two
+ different versions of GnuPG or changes the --trust-model
+ setting. */
+ if(k->ownertrust<min)
+ {
+ if(DBG_TRUST)
+ log_debug("key %08lX%08lX:"
+ " overriding ownertrust `%s' with `%s'\n",
+ (ulong)k->kid[0],(ulong)k->kid[1],
+ trust_value_to_string(k->ownertrust),
+ trust_value_to_string(min));
+
+ k->ownertrust=min;
+ }
+
+ if (k->ownertrust == TRUST_UNKNOWN)
+ ot_unknown++;
+ else if (k->ownertrust == TRUST_UNDEFINED)
+ ot_undefined++;
+ else if (k->ownertrust == TRUST_NEVER)
+ ot_never++;
+ else if (k->ownertrust == TRUST_MARGINAL)
+ ot_marginal++;
+ else if (k->ownertrust == TRUST_FULLY)
+ ot_full++;
+ else if (k->ownertrust == TRUST_ULTIMATE)
+ ot_ultimate++;
+
+ valids++;
+ }
+
+ /* Find all keys which are signed by a key in kdlist */
+ keys = validate_key_list (kdb, full_trust, klist,
+ start_time, &next_expire);
+ if (!keys)
+ {
+ log_error ("validate_key_list failed\n");
+ rc = G10ERR_GENERAL;
+ goto leave;
+ }
+
+ for (key_count=0, kar=keys; kar->keyblock; kar++, key_count++)
+ ;
+
+ /* Store the calculated valididation status somewhere */
+ if (opt.verbose > 1)
+ dump_key_array (depth, keys);
+
+ for (kar=keys; kar->keyblock; kar++)
+ store_validation_status (depth, kar->keyblock, stored);
+
+ log_info (_("depth: %d valid: %3d signed: %3d"
+ " trust: %d-, %dq, %dn, %dm, %df, %du\n"),
+ depth, valids, key_count, ot_unknown, ot_undefined,
+ ot_never, ot_marginal, ot_full, ot_ultimate );
+
+ /* Build a new kdlist from all fully valid keys in KEYS */
+ if (klist != utk_list)
+ release_key_items (klist);
+ klist = NULL;
+ for (kar=keys; kar->keyblock; kar++)
+ {
+ for (node=kar->keyblock; node; node = node->next)
+ {
+ if (node->pkt->pkttype == PKT_USER_ID && (node->flag & 4))
+ {
+ u32 kid[2];
+
+ /* have we used this key already? */
+ keyid_from_pk (kar->keyblock->pkt->pkt.public_key, kid);
+ if(test_key_hash_table(used,kid)==0)
+ {
+ /* Normally we add both the primary and subkey
+ ids to the hash via mark_keyblock_seen, but
+ since we aren't using this hash as a skipfnc,
+ that doesn't matter here. */
+ add_key_hash_table (used,kid);
+ k = new_key_item ();
+ k->kid[0]=kid[0];
+ k->kid[1]=kid[1];
+ k->ownertrust =
+ (get_ownertrust (kar->keyblock->pkt->pkt.public_key)
+ & TRUST_MASK);
+ k->min_ownertrust =
+ get_min_ownertrust(kar->keyblock->pkt->pkt.public_key);
+ k->trust_depth=
+ kar->keyblock->pkt->pkt.public_key->trust_depth;
+ k->trust_value=
+ kar->keyblock->pkt->pkt.public_key->trust_value;
+ if(kar->keyblock->pkt->pkt.public_key->trust_regexp)
+ k->trust_regexp=
+ xstrdup(kar->keyblock->pkt->
+ pkt.public_key->trust_regexp);
+ k->next = klist;
+ klist = k;
+ break;
+ }
+ }
+ }
+ }
+ release_key_array (keys);
+ keys = NULL;
+ if (!klist)
+ break; /* no need to dive in deeper */
+ }
+
+ leave:
+ keydb_release (kdb);
+ release_key_array (keys);
+ release_key_items (klist);
+ release_key_hash_table (full_trust);
+ release_key_hash_table (used);
+ release_key_hash_table (stored);
+ if (!rc && !quit) /* mark trustDB as checked */
+ {
+ if (next_expire == 0xffffffff || next_expire < start_time )
+ tdbio_write_nextcheck (0);
+ else
+ {
+ tdbio_write_nextcheck (next_expire);
+ log_info (_("next trustdb check due at %s\n"),
+ strtimestamp (next_expire));
+ }
+
+ if(tdbio_update_version_record()!=0)
+ {
+ log_error(_("unable to update trustdb version record: "
+ "write failed: %s\n"), g10_errstr(rc));
+ tdbio_invalid();
+ }
+
+ do_sync ();
+ pending_check_trustdb = 0;
+ }
+
+ return rc;
+}
diff --git a/g10/trustdb.h b/g10/trustdb.h
new file mode 100644
index 0000000..2d0581f
--- /dev/null
+++ b/g10/trustdb.h
@@ -0,0 +1,98 @@
+/* trustdb.h - Trust database
+ * Copyright (C) 1998, 1999, 2000, 2001, 2002, 2003, 2004,
+ * 2005 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#ifndef G10_TRUSTDB_H
+#define G10_TRUSTDB_H
+
+/* Trust values must be sorted in ascending order */
+#define TRUST_MASK 15
+#define TRUST_UNKNOWN 0 /* o: not yet calculated/assigned */
+#define TRUST_EXPIRED 1 /* e: calculation may be invalid */
+#define TRUST_UNDEFINED 2 /* q: not enough information for calculation */
+#define TRUST_NEVER 3 /* n: never trust this pubkey */
+#define TRUST_MARGINAL 4 /* m: marginally trusted */
+#define TRUST_FULLY 5 /* f: fully trusted */
+#define TRUST_ULTIMATE 6 /* u: ultimately trusted */
+/* trust values not covered by the mask */
+#define TRUST_FLAG_REVOKED 32 /* r: revoked */
+#define TRUST_FLAG_SUB_REVOKED 64 /* r: revoked but for subkeys */
+#define TRUST_FLAG_DISABLED 128 /* d: key/uid disabled */
+#define TRUST_FLAG_PENDING_CHECK 256 /* a check-trustdb is pending */
+
+#define NAMEHASH_HASH DIGEST_ALGO_RMD160
+#define NAMEHASH_LEN 20
+
+/*-- trustdb.c --*/
+void register_trusted_keyid(u32 *keyid);
+void register_trusted_key( const char *string );
+void check_trustdb (void);
+void update_trustdb (void);
+int setup_trustdb( int level, const char *dbname );
+void init_trustdb( void );
+void check_trustdb_stale(void);
+void sync_trustdb( void );
+
+const char *uid_trust_string_fixed(PKT_public_key *key,PKT_user_id *uid);
+const char *trust_value_to_string (unsigned int value);
+int string_to_trust_value (const char *str);
+
+void revalidation_mark (void);
+int trustdb_pending_check(void);
+void trustdb_check_or_update(void);
+
+int cache_disabled_value(PKT_public_key *pk);
+
+unsigned int get_validity (PKT_public_key *pk, PKT_user_id *uid);
+int get_validity_info (PKT_public_key *pk, PKT_user_id *uid);
+const char *get_validity_string (PKT_public_key *pk, PKT_user_id *uid);
+
+void list_trust_path( const char *username );
+int enum_cert_paths( void **context, ulong *lid,
+ unsigned *ownertrust, unsigned *validity );
+void enum_cert_paths_print( void **context, FILE *fp,
+ int refresh, ulong selected_lid );
+
+void read_trust_options(byte *trust_model,ulong *created,ulong *nextcheck,
+ byte *marginals,byte *completes,byte *cert_depth);
+
+unsigned int get_ownertrust (PKT_public_key *pk);
+unsigned int get_min_ownertrust (PKT_public_key *pk);
+int get_ownertrust_info (PKT_public_key *pk);
+const char *get_ownertrust_string (PKT_public_key *pk);
+
+void update_ownertrust (PKT_public_key *pk, unsigned int new_trust );
+int clear_ownertrusts (PKT_public_key *pk);
+
+void clean_one_uid(KBNODE keyblock,KBNODE uidnode,int noisy,int self_only,
+ int *uids_cleaned,int *sigs_cleaned);
+void clean_key(KBNODE keyblock,int noisy,int self_only,
+ int *uids_cleaned,int *sigs_cleaned);
+
+/*-- tdbdump.c --*/
+void list_trustdb(const char *username);
+void export_ownertrust(void);
+void import_ownertrust(const char *fname);
+
+/*-- pkclist.c --*/
+int edit_ownertrust (PKT_public_key *pk, int mode );
+
+#endif /*G10_TRUSTDB_H*/
diff --git a/g10/verify.c b/g10/verify.c
new file mode 100644
index 0000000..082db89
--- /dev/null
+++ b/g10/verify.c
@@ -0,0 +1,211 @@
+/* verify.c - verify signed data
+ * Copyright (C) 1998, 1999, 2000, 2001, 2004 Free Software Foundation, Inc.
+ *
+ * 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 2 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, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301,
+ * USA.
+ */
+
+#include <config.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <assert.h>
+#include <unistd.h> /* for isatty() */
+
+#include "options.h"
+#include "packet.h"
+#include "errors.h"
+#include "iobuf.h"
+#include "keydb.h"
+#include "memory.h"
+#include "util.h"
+#include "main.h"
+#include "status.h"
+#include "filter.h"
+#include "ttyio.h"
+#include "i18n.h"
+
+
+
+/****************
+ * Assume that the input is a signature and verify it without
+ * generating any output. With no arguments, the signature packet
+ * is read from stdin (it may be a detached signature when not
+ * used in batch mode). If only a sigfile is given, it may be a complete
+ * signature or a detached signature in which case the signed stuff
+ * is expected from stdin. With more than 1 argument, the first should
+ * be a detached signature and the remaining files are the signed stuff.
+ */
+
+int
+verify_signatures( int nfiles, char **files )
+{
+ IOBUF fp;
+ armor_filter_context_t afx;
+ progress_filter_context_t pfx;
+ const char *sigfile;
+ int i, rc;
+ STRLIST sl;
+
+ memset( &afx, 0, sizeof afx);
+ /* decide whether we should handle a detached or a normal signature,
+ * which is needed so that the code later can hash the correct data and
+ * not have a normal signature act as detached signature and ignoring the
+ * indended signed material from the 2nd file or stdin.
+ * 1. gpg <file - normal
+ * 2. gpg file - normal (or detached)
+ * 3. gpg file <file2 - detached
+ * 4. gpg file file2 - detached
+ * The question is how decide between case 2 and 3? The only way
+ * we can do it is by reading one byte from stdin and the unget
+ * it; the problem here is that we may be reading from the
+ * terminal (which could be detected using isatty() but won't work
+ * when under contol of a pty using program (e.g. expect)) and
+ * might get us in trouble when stdin is used for another purpose
+ * (--passphrase-fd 0). So we have to break with the behaviour
+ * prior to gpg 1.0.4 by assuming that case 3 is a normal
+ * signature (where file2 is ignored and require for a detached
+ * signature to indicate signed material comes from stdin by using
+ * case 4 with a file2 of "-".
+ *
+ * Actually we don't have to change anything here but can handle
+ * that all quite easily in mainproc.c
+ */
+
+
+ sigfile = nfiles? *files : NULL;
+
+ /* open the signature file */
+ fp = iobuf_open(sigfile);
+ if (fp && is_secured_file (iobuf_get_fd (fp)))
+ {
+ iobuf_close (fp);
+ fp = NULL;
+ errno = EPERM;
+ }
+ if( !fp ) {
+ log_error(_("can't open `%s'\n"), print_fname_stdin(sigfile));
+ return G10ERR_OPEN_FILE;
+ }
+ handle_progress (&pfx, fp, sigfile);
+
+ if( !opt.no_armor && use_armor_filter( fp ) )
+ iobuf_push_filter( fp, armor_filter, &afx );
+
+ sl = NULL;
+ for(i=nfiles-1 ; i > 0 ; i-- )
+ add_to_strlist( &sl, files[i] );
+ rc = proc_signature_packets( NULL, fp, sl, sigfile );
+ free_strlist(sl);
+ iobuf_close(fp);
+ if( (afx.no_openpgp_data && rc == -1) || rc == G10ERR_NO_DATA ) {
+ log_error(_("the signature could not be verified.\n"
+ "Please remember that the signature file (.sig or .asc)\n"
+ "should be the first file given on the command line.\n") );
+ rc = 0;
+ }
+
+ return rc;
+}
+
+
+void
+print_file_status( int status, const char *name, int what )
+{
+ char *p = xmalloc(strlen(name)+10);
+ sprintf(p, "%d %s", what, name );
+ write_status_text( status, p );
+ xfree(p);
+}
+
+
+static int
+verify_one_file( const char *name )
+{
+ IOBUF fp;
+ armor_filter_context_t afx;
+ progress_filter_context_t pfx;
+ int rc;
+
+ print_file_status( STATUS_FILE_START, name, 1 );
+ fp = iobuf_open(name);
+ if (fp)
+ iobuf_ioctl (fp,3,1,NULL); /* disable fd caching */
+ if (fp && is_secured_file (iobuf_get_fd (fp)))
+ {
+ iobuf_close (fp);
+ fp = NULL;
+ errno = EPERM;
+ }
+ if( !fp ) {
+ print_file_status( STATUS_FILE_ERROR, name, 1 );
+ log_error(_("can't open `%s'\n"), print_fname_stdin(name));
+ return G10ERR_OPEN_FILE;
+ }
+ handle_progress (&pfx, fp, name);
+
+ if( !opt.no_armor ) {
+ if( use_armor_filter( fp ) ) {
+ memset( &afx, 0, sizeof afx);
+ iobuf_push_filter( fp, armor_filter, &afx );
+ }
+ }
+
+ rc = proc_signature_packets( NULL, fp, NULL, name );
+ iobuf_close(fp);
+ write_status( STATUS_FILE_DONE );
+ return rc;
+}
+
+/****************
+ * Verify each file given in the files array or read the names of the
+ * files from stdin.
+ * Note: This function can not handle detached signatures.
+ */
+int
+verify_files( int nfiles, char **files )
+{
+ int i;
+
+ if( !nfiles ) { /* read the filenames from stdin */
+ char line[2048];
+ unsigned int lno = 0;
+
+ while( fgets(line, DIM(line), stdin) ) {
+ lno++;
+ if( !*line || line[strlen(line)-1] != '\n' ) {
+ log_error(_("input line %u too long or missing LF\n"), lno );
+ return G10ERR_GENERAL;
+ }
+ /* This code does not work on MSDOS but how cares there are
+ * also no script languages available. We don't strip any
+ * spaces, so that we can process nearly all filenames */
+ line[strlen(line)-1] = 0;
+ verify_one_file( line );
+ iobuf_ioctl( NULL, 2, 0, NULL); /* Invalidate entire cache. */
+ }
+
+ }
+ else { /* take filenames from the array */
+ for(i=0; i < nfiles; i++ ) {
+ verify_one_file( files[i] );
+ iobuf_ioctl( NULL, 2, 0, NULL); /* Invalidate entire cache. */
+ }
+ }
+ return 0;
+}